elfinder.full.js 989 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641346423464334644346453464634647346483464934650346513465234653346543465534656346573465834659346603466134662346633466434665346663466734668346693467034671346723467334674346753467634677346783467934680346813468234683346843468534686346873468834689346903469134692346933469434695346963469734698
  1. /*!
  2. * elFinder - file manager for web
  3. * Version 2.1.49 (2019-04-17)
  4. * http://elfinder.org
  5. *
  6. * Copyright 2009-2019, 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/>').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. tmb : false // old API
  154. },
  155. /**
  156. * Current working directory options
  157. *
  158. * @type Object
  159. **/
  160. cwdOptions = {},
  161. /**
  162. * Files/dirs cache
  163. *
  164. * @type Object
  165. **/
  166. files = {},
  167. /**
  168. * Hidden Files/dirs cache
  169. *
  170. * @type Object
  171. **/
  172. hiddenFiles = {},
  173. /**
  174. * Files/dirs hash cache of each dirs
  175. *
  176. * @type Object
  177. **/
  178. ownFiles = {},
  179. /**
  180. * Selected files hashes
  181. *
  182. * @type Array
  183. **/
  184. selected = [],
  185. /**
  186. * Events listeners
  187. *
  188. * @type Object
  189. **/
  190. listeners = {},
  191. /**
  192. * Shortcuts
  193. *
  194. * @type Object
  195. **/
  196. shortcuts = {},
  197. /**
  198. * Buffer for copied files
  199. *
  200. * @type Array
  201. **/
  202. clipboard = [],
  203. /**
  204. * Copied/cuted files hashes
  205. * Prevent from remove its from cache.
  206. * Required for dispaly correct files names in error messages
  207. *
  208. * @type Object
  209. **/
  210. remember = {},
  211. /**
  212. * Queue for 'open' requests
  213. *
  214. * @type Array
  215. **/
  216. queue = [],
  217. /**
  218. * Queue for only cwd requests e.g. `tmb`
  219. *
  220. * @type Array
  221. **/
  222. cwdQueue = [],
  223. /**
  224. * Commands prototype
  225. *
  226. * @type Object
  227. **/
  228. base = new self.command(self),
  229. /**
  230. * elFinder node width
  231. *
  232. * @type String
  233. * @default "auto"
  234. **/
  235. width = 'auto',
  236. /**
  237. * elFinder node height
  238. * Number: pixcel or String: Number + "%"
  239. *
  240. * @type Number | String
  241. * @default 400
  242. **/
  243. height = 400,
  244. /**
  245. * Base node object or selector
  246. * Element which is the reference of the height percentage
  247. *
  248. * @type Object|String
  249. * @default null | $(window) (if height is percentage)
  250. **/
  251. heightBase = null,
  252. /**
  253. * MIME type list(Associative array) handled as a text file
  254. *
  255. * @type Object|null
  256. */
  257. textMimes = null,
  258. /**
  259. * elfinder path for sound played on remove
  260. * @type String
  261. * @default ./sounds/
  262. **/
  263. soundPath = 'sounds/',
  264. /**
  265. * JSON.stringify of previous fm.sorters
  266. * @type String
  267. */
  268. prevSorterStr = '',
  269. /**
  270. * Map table of file extention to MIME-Type
  271. * @type Object
  272. */
  273. extToMimeTable,
  274. /**
  275. * Disabled page unload function
  276. * @type Boolean
  277. */
  278. diableUnloadCheck = false,
  279. beeper = $(document.createElement('audio')).hide().appendTo('body')[0],
  280. syncInterval,
  281. autoSyncStop = 0,
  282. uiCmdMapPrev = '',
  283. gcJobRes = null,
  284. open = function(data) {
  285. // NOTES: Do not touch data object
  286. var volumeid, contextmenu, emptyDirs = {}, stayDirs = {},
  287. rmClass, hashes, calc, gc, collapsed, prevcwd, sorterStr;
  288. if (self.api >= 2.1) {
  289. // support volume driver option `uiCmdMap`
  290. self.commandMap = (data.options.uiCmdMap && Object.keys(data.options.uiCmdMap).length)? data.options.uiCmdMap : {};
  291. if (uiCmdMapPrev !== JSON.stringify(self.commandMap)) {
  292. uiCmdMapPrev = JSON.stringify(self.commandMap);
  293. }
  294. } else {
  295. self.options.sync = 0;
  296. }
  297. if (data.init) {
  298. // init - reset cache
  299. files = {};
  300. ownFiles = {};
  301. } else {
  302. // remove only files from prev cwd
  303. // and collapsed directory (included 100+ directories) to empty for perfomance tune in DnD
  304. prevcwd = cwd;
  305. rmClass = 'elfinder-subtree-loaded ' + self.res('class', 'navexpand');
  306. collapsed = self.res('class', 'navcollapse');
  307. hashes = Object.keys(files);
  308. calc = function(i) {
  309. if (!files[i]) {
  310. return true;
  311. }
  312. var isDir = (files[i].mime === 'directory'),
  313. phash = files[i].phash,
  314. pnav;
  315. if (
  316. (!isDir
  317. || emptyDirs[phash]
  318. || (!stayDirs[phash]
  319. && self.navHash2Elm(files[i].hash).is(':hidden')
  320. && self.navHash2Elm(phash).next('.elfinder-navbar-subtree').children().length > 100
  321. )
  322. )
  323. && (isDir || phash !== cwd)
  324. && ! remember[i]
  325. ) {
  326. if (isDir && !emptyDirs[phash]) {
  327. emptyDirs[phash] = true;
  328. self.navHash2Elm(phash)
  329. .removeClass(rmClass)
  330. .next('.elfinder-navbar-subtree').empty();
  331. }
  332. deleteCache(files[i]);
  333. } else if (isDir) {
  334. stayDirs[phash] = true;
  335. }
  336. };
  337. gc = function() {
  338. if (hashes.length) {
  339. gcJobRes && gcJobRes._abort();
  340. gcJobRes = self.asyncJob(calc, hashes, {
  341. interval : 20,
  342. numPerOnce : 100
  343. }).done(function() {
  344. var hd = self.storage('hide') || {items: {}};
  345. if (Object.keys(hiddenFiles).length) {
  346. $.each(hiddenFiles, function(h) {
  347. if (!hd.items[h]) {
  348. delete hiddenFiles[h];
  349. }
  350. });
  351. }
  352. });
  353. }
  354. };
  355. self.trigger('filesgc').one('filesgc', function() {
  356. hashes = [];
  357. });
  358. self.one('opendone', function() {
  359. if (prevcwd !== cwd) {
  360. if (! node.data('lazycnt')) {
  361. gc();
  362. } else {
  363. self.one('lazydone', gc);
  364. }
  365. }
  366. });
  367. }
  368. self.sorters = {};
  369. cwd = data.cwd.hash;
  370. cache(data.files);
  371. if (!files[cwd]) {
  372. cache([data.cwd]);
  373. }
  374. // trigger event 'sorterupdate'
  375. sorterStr = JSON.stringify(self.sorters);
  376. if (prevSorterStr !== sorterStr) {
  377. self.trigger('sorterupdate');
  378. prevSorterStr = sorterStr;
  379. }
  380. self.lastDir(cwd);
  381. self.autoSync();
  382. },
  383. /**
  384. * Store info about files/dirs in "files" object.
  385. *
  386. * @param Array files
  387. * @param String data type
  388. * @return void
  389. **/
  390. cache = function(data, type) {
  391. var defsorter = { name: true, perm: true, date: true, size: true, kind: true },
  392. sorterChk = !self.sorters._checked,
  393. l = data.length,
  394. setSorter = function(file) {
  395. var f = file || {},
  396. sorters = [];
  397. $.each(self.sortRules, function(key) {
  398. if (defsorter[key] || typeof f[key] !== 'undefined' || (key === 'mode' && typeof f.perm !== 'undefined')) {
  399. sorters.push(key);
  400. }
  401. });
  402. self.sorters = self.arrayFlip(sorters, true);
  403. self.sorters._checked = true;
  404. },
  405. keeps = ['sizeInfo'],
  406. changedParents = {},
  407. hideData = self.storage('hide') || {},
  408. hides = hideData.items || {},
  409. f, i, keepProp, parents, hidden;
  410. for (i = 0; i < l; i++) {
  411. f = Object.assign({}, data[i]);
  412. hidden = (!hideData.show && hides[f.hash])? true : false;
  413. if (f.name && f.hash && f.mime) {
  414. if (!hidden) {
  415. if (sorterChk && f.phash === cwd) {
  416. setSorter(f);
  417. sorterChk = false;
  418. }
  419. if (f.phash && (type === 'add' || type === 'change')) {
  420. if (parents = self.parents(f.phash)) {
  421. $.each(parents, function() {
  422. changedParents[this] = true;
  423. });
  424. }
  425. }
  426. }
  427. if (files[f.hash]) {
  428. $.each(keeps, function() {
  429. if(files[f.hash][this] && ! f[this]) {
  430. f[this] = files[f.hash][this];
  431. }
  432. });
  433. if (f.sizeInfo && !f.size) {
  434. f.size = f.sizeInfo.size;
  435. }
  436. deleteCache(files[f.hash], true);
  437. }
  438. if (hides[f.hash]) {
  439. hiddenFiles[f.hash] = f;
  440. }
  441. if (hidden) {
  442. l--;
  443. data.splice(i--, 1);
  444. } else {
  445. files[f.hash] = f;
  446. if (f.mime === 'directory' && !ownFiles[f.hash]) {
  447. ownFiles[f.hash] = {};
  448. }
  449. if (f.phash) {
  450. if (!ownFiles[f.phash]) {
  451. ownFiles[f.phash] = {};
  452. }
  453. ownFiles[f.phash][f.hash] = true;
  454. }
  455. }
  456. }
  457. }
  458. // delete sizeInfo cache
  459. $.each(Object.keys(changedParents), function() {
  460. var target = files[this];
  461. if (target && target.sizeInfo) {
  462. delete target.sizeInfo;
  463. }
  464. });
  465. // for empty folder
  466. sorterChk && setSorter();
  467. },
  468. /**
  469. * Delete file object from files caches
  470. *
  471. * @param Array removed hashes
  472. * @return void
  473. */
  474. remove = function(removed) {
  475. var l = removed.length,
  476. roots = {},
  477. rm = function(hash) {
  478. var file = files[hash], i;
  479. if (file) {
  480. if (file.mime === 'directory') {
  481. if (roots[hash]) {
  482. delete self.roots[roots[hash]];
  483. }
  484. // restore stats of deleted root parent directory
  485. $.each(self.leafRoots, function(phash, roots) {
  486. var idx, pdir;
  487. if ((idx = $.inArray(hash, roots))!== -1) {
  488. if (roots.length === 1) {
  489. if ((pdir = Object.assign({}, files[phash])) && pdir._realStats) {
  490. $.each(pdir._realStats, function(k, v) {
  491. pdir[k] = v;
  492. });
  493. remove(files[phash]._realStats);
  494. self.change({ changed: [pdir] });
  495. }
  496. delete self.leafRoots[phash];
  497. } else {
  498. self.leafRoots[phash].splice(idx, 1);
  499. }
  500. }
  501. });
  502. if (self.searchStatus.state < 2) {
  503. $.each(files, function(h, f) {
  504. f.phash == hash && rm(h);
  505. });
  506. }
  507. }
  508. if (file.phash) {
  509. if (parents = self.parents(file.phash)) {
  510. $.each(parents, function() {
  511. changedParents[this] = true;
  512. });
  513. }
  514. }
  515. deleteCache(files[hash]);
  516. }
  517. },
  518. changedParents = {},
  519. parents;
  520. $.each(self.roots, function(k, v) {
  521. roots[v] = k;
  522. });
  523. while (l--) {
  524. rm(removed[l]);
  525. }
  526. // delete sizeInfo cache
  527. $.each(Object.keys(changedParents), function() {
  528. var target = files[this];
  529. if (target && target.sizeInfo) {
  530. delete target.sizeInfo;
  531. }
  532. });
  533. },
  534. /**
  535. * Update file object in files caches
  536. *
  537. * @param Array changed file objects
  538. * @return void
  539. */
  540. change = function(changed) {
  541. $.each(changed, function(i, file) {
  542. var hash = file.hash;
  543. if (files[hash]) {
  544. $.each(Object.keys(files[hash]), function(i, v){
  545. if (typeof file[v] === 'undefined') {
  546. delete files[hash][v];
  547. }
  548. });
  549. }
  550. files[hash] = files[hash] ? Object.assign(files[hash], file) : file;
  551. });
  552. },
  553. /**
  554. * Delete cache data of files, ownFiles and self.optionsByHashes
  555. *
  556. * @param Object file
  557. * @param Boolean update
  558. * @return void
  559. */
  560. deleteCache = function(file, update) {
  561. var hash = file.hash,
  562. phash = file.phash;
  563. if (phash && ownFiles[phash]) {
  564. delete ownFiles[phash][hash];
  565. }
  566. if (!update) {
  567. ownFiles[hash] && delete ownFiles[hash];
  568. self.optionsByHashes[hash] && delete self.optionsByHashes[hash];
  569. }
  570. delete files[hash];
  571. },
  572. /**
  573. * Maximum number of concurrent connections on request
  574. *
  575. * @type Number
  576. */
  577. requestMaxConn,
  578. /**
  579. * Current number of connections
  580. *
  581. * @type Number
  582. */
  583. requestCnt = 0,
  584. /**
  585. * Queue waiting for connection
  586. *
  587. * @type Array
  588. */
  589. requestQueue = [],
  590. /**
  591. * Flag to cancel the `open` command waiting for connection
  592. *
  593. * @type Boolean
  594. */
  595. requestQueueSkipOpen = false,
  596. /**
  597. * Exec shortcut
  598. *
  599. * @param jQuery.Event keydown/keypress event
  600. * @return void
  601. */
  602. execShortcut = function(e) {
  603. var code = e.keyCode,
  604. ctrlKey = !!(e.ctrlKey || e.metaKey),
  605. isMousedown = e.type === 'mousedown',
  606. ddm;
  607. !isMousedown && (self.keyState.keyCode = code);
  608. self.keyState.ctrlKey = ctrlKey;
  609. self.keyState.shiftKey = e.shiftKey;
  610. self.keyState.metaKey = e.metaKey;
  611. self.keyState.altKey = e.altKey;
  612. if (isMousedown) {
  613. return;
  614. } else if (e.type === 'keyup') {
  615. self.keyState.keyCode = null;
  616. return;
  617. }
  618. if (enabled) {
  619. $.each(shortcuts, function(i, shortcut) {
  620. if (shortcut.type == e.type
  621. && shortcut.keyCode == code
  622. && shortcut.shiftKey == e.shiftKey
  623. && shortcut.ctrlKey == ctrlKey
  624. && shortcut.altKey == e.altKey) {
  625. e.preventDefault();
  626. e.stopPropagation();
  627. shortcut.callback(e, self);
  628. self.debug('shortcut-exec', i+' : '+shortcut.description);
  629. }
  630. });
  631. // prevent tab out of elfinder
  632. if (code == $.ui.keyCode.TAB && !$(e.target).is(':input')) {
  633. e.preventDefault();
  634. }
  635. // cancel any actions by [Esc] key
  636. if (e.type === 'keydown' && code == $.ui.keyCode.ESCAPE) {
  637. // copy or cut
  638. if (! node.find('.ui-widget:visible').length) {
  639. self.clipboard().length && self.clipboard([]);
  640. }
  641. // dragging
  642. if ($.ui.ddmanager) {
  643. ddm = $.ui.ddmanager.current;
  644. ddm && ddm.helper && ddm.cancel();
  645. }
  646. // button menus
  647. self.toHide(node.find('.ui-widget.elfinder-button-menu.elfinder-frontmost:visible'));
  648. // trigger keydownEsc
  649. self.trigger('keydownEsc', e);
  650. }
  651. }
  652. },
  653. date = new Date(),
  654. utc,
  655. i18n,
  656. inFrame = (window.parent !== window),
  657. parentIframe = (function() {
  658. var pifm, ifms;
  659. if (inFrame) {
  660. try {
  661. ifms = $('iframe', window.parent.document);
  662. if (ifms.length) {
  663. $.each(ifms, function(i, ifm) {
  664. if (ifm.contentWindow === window) {
  665. pifm = $(ifm);
  666. return false;
  667. }
  668. });
  669. }
  670. } catch(e) {}
  671. }
  672. return pifm;
  673. })(),
  674. /**
  675. * elFinder boot up function
  676. *
  677. * @type Function
  678. */
  679. bootUp,
  680. /**
  681. * Original function of XMLHttpRequest.prototype.send
  682. *
  683. * @type Function
  684. */
  685. savedXhrSend;
  686. // opts must be an object
  687. if (!opts) {
  688. opts = {};
  689. }
  690. // set UA.Angle, UA.Rotated for mobile devices
  691. if (self.UA.Mobile) {
  692. $(window).on('orientationchange.'+namespace, function() {
  693. var a = ((screen && screen.orientation && screen.orientation.angle) || window.orientation || 0) + 0;
  694. if (a === -90) {
  695. a = 270;
  696. }
  697. self.UA.Angle = a;
  698. self.UA.Rotated = a % 180 === 0? false : true;
  699. }).trigger('orientationchange.'+namespace);
  700. }
  701. // check opt.bootCallback
  702. if (opts.bootCallback && typeof opts.bootCallback === 'function') {
  703. (function() {
  704. var func = bootCallback,
  705. opFunc = opts.bootCallback;
  706. bootCallback = function(fm, extraObj) {
  707. func && typeof func === 'function' && func.call(this, fm, extraObj);
  708. opFunc.call(this, fm, extraObj);
  709. };
  710. })();
  711. }
  712. delete opts.bootCallback;
  713. /**
  714. * Protocol version
  715. *
  716. * @type String
  717. **/
  718. this.api = null;
  719. /**
  720. * elFinder use new api
  721. *
  722. * @type Boolean
  723. **/
  724. this.newAPI = false;
  725. /**
  726. * elFinder use old api
  727. *
  728. * @type Boolean
  729. **/
  730. this.oldAPI = false;
  731. /**
  732. * Net drivers names
  733. *
  734. * @type Array
  735. **/
  736. this.netDrivers = [];
  737. /**
  738. * Base URL of elfFinder library starting from Manager HTML
  739. *
  740. * @type String
  741. */
  742. this.baseUrl = '';
  743. /**
  744. * Base URL of i18n js files
  745. * baseUrl + "js/i18n/" when empty value
  746. *
  747. * @type String
  748. */
  749. this.i18nBaseUrl = '';
  750. /**
  751. * Is elFinder CSS loaded
  752. *
  753. * @type Boolean
  754. */
  755. this.cssloaded = false;
  756. /**
  757. * Current theme object
  758. *
  759. * @type Object|Null
  760. */
  761. this.theme = null;
  762. this.mimesCanMakeEmpty = {};
  763. /**
  764. * Callback function at boot up that option specified at elFinder starting
  765. *
  766. * @type Function
  767. */
  768. this.bootCallback;
  769. /**
  770. * Callback function at reload(restart) elFinder
  771. *
  772. * @type Function
  773. */
  774. this.reloadCallback;
  775. /**
  776. * ID. Required to create unique cookie name
  777. *
  778. * @type String
  779. **/
  780. this.id = id;
  781. /**
  782. * Method to store/fetch data
  783. *
  784. * @type Function
  785. **/
  786. this.storage = (function() {
  787. try {
  788. if ('localStorage' in window && window.localStorage !== null) {
  789. if (self.UA.Safari) {
  790. // check for Mac/iOS safari private browsing mode
  791. window.localStorage.setItem('elfstoragecheck', 1);
  792. window.localStorage.removeItem('elfstoragecheck');
  793. }
  794. return self.localStorage;
  795. } else {
  796. return self.cookie;
  797. }
  798. } catch (e) {
  799. return self.cookie;
  800. }
  801. })();
  802. /**
  803. * Set pause page unload check function or Get state
  804. *
  805. * @param Boolean state To set state
  806. * @param Boolean keep Keep disabled
  807. * @return Boolean|void
  808. */
  809. this.pauseUnloadCheck = function(state, keep) {
  810. if (typeof state === 'undefined') {
  811. return diableUnloadCheck;
  812. } else {
  813. diableUnloadCheck = !!state;
  814. if (state && !keep) {
  815. requestAnimationFrame(function() {
  816. diableUnloadCheck = false;
  817. });
  818. }
  819. }
  820. };
  821. /**
  822. * Configuration options
  823. *
  824. * @type Object
  825. **/
  826. //this.options = $.extend(true, {}, this._options, opts);
  827. this.options = Object.assign({}, this._options);
  828. // for old type configuration
  829. if (opts.uiOptions) {
  830. if (opts.uiOptions.toolbar && Array.isArray(opts.uiOptions.toolbar)) {
  831. if ($.isPlainObject(opts.uiOptions.toolbar[opts.uiOptions.toolbar.length - 1])) {
  832. self.options.uiOptions.toolbarExtra = Object.assign(self.options.uiOptions.toolbarExtra || {}, opts.uiOptions.toolbar.pop());
  833. }
  834. }
  835. }
  836. // Overwrite if opts value is an array
  837. (function() {
  838. var arrOv = function(obj, base) {
  839. if ($.isPlainObject(obj)) {
  840. $.each(obj, function(k, v) {
  841. if ($.isPlainObject(v)) {
  842. if (!base[k]) {
  843. base[k] = {};
  844. }
  845. arrOv(v, base[k]);
  846. } else {
  847. base[k] = v;
  848. }
  849. });
  850. }
  851. };
  852. arrOv(opts, self.options);
  853. })();
  854. // join toolbarExtra to toolbar
  855. this.options.uiOptions.toolbar.push(this.options.uiOptions.toolbarExtra);
  856. delete this.options.uiOptions.toolbarExtra;
  857. /**
  858. * Arrays that has to unbind events
  859. *
  860. * @type Object
  861. */
  862. this.toUnbindEvents = {};
  863. /**
  864. * Attach listener to events
  865. * To bind to multiply events at once, separate events names by space
  866. *
  867. * @param String event(s) name(s)
  868. * @param Object event handler or {done: handler}
  869. * @param Boolean priority first
  870. * @return elFinder
  871. */
  872. this.bind = function(event, callback, priorityFirst) {
  873. var i, len;
  874. if (callback && (typeof callback === 'function' || typeof callback.done === 'function')) {
  875. event = ('' + event).toLowerCase().replace(/^\s+|\s+$/g, '').split(/\s+/);
  876. len = event.length;
  877. for (i = 0; i < len; i++) {
  878. if (listeners[event[i]] === void(0)) {
  879. listeners[event[i]] = [];
  880. }
  881. listeners[event[i]][priorityFirst? 'unshift' : 'push'](callback);
  882. }
  883. }
  884. return this;
  885. };
  886. /**
  887. * Remove event listener if exists
  888. * To un-bind to multiply events at once, separate events names by space
  889. *
  890. * @param String event(s) name(s)
  891. * @param Function callback
  892. * @return elFinder
  893. */
  894. this.unbind = function(event, callback) {
  895. var i, len, l, ci;
  896. event = ('' + event).toLowerCase().split(/\s+/);
  897. len = event.length;
  898. for (i = 0; i < len; i++) {
  899. if (l = listeners[event[i]]) {
  900. ci = $.inArray(callback, l);
  901. ci > -1 && l.splice(ci, 1);
  902. }
  903. }
  904. callback = null;
  905. return this;
  906. };
  907. /**
  908. * Fire event - send notification to all event listeners
  909. * In the callback `this` becames an event object
  910. *
  911. * @param String event type
  912. * @param Object data to send across event
  913. * @param Boolean allow modify data (call by reference of data) default: true
  914. * @return elFinder
  915. */
  916. this.trigger = function(evType, data, allowModify) {
  917. var type = evType.toLowerCase(),
  918. isopen = (type === 'open'),
  919. dataIsObj = (typeof data === 'object'),
  920. handlers = listeners[type] || [],
  921. dones = [],
  922. i, l, jst, event;
  923. this.debug('event-'+type, data);
  924. if (! dataIsObj || typeof allowModify === 'undefined') {
  925. allowModify = true;
  926. }
  927. if (l = handlers.length) {
  928. event = $.Event(type);
  929. if (data) {
  930. data._event = event;
  931. }
  932. if (allowModify) {
  933. event.data = data;
  934. }
  935. for (i = 0; i < l; i++) {
  936. if (! handlers[i]) {
  937. // probably un-binded this handler
  938. continue;
  939. }
  940. // handler is $.Deferred(), call all functions upon completion
  941. if (handlers[i].done) {
  942. dones.push(handlers[i].done);
  943. continue;
  944. }
  945. // set `event.data` only callback has argument
  946. if (handlers[i].length) {
  947. if (!allowModify) {
  948. // to avoid data modifications. remember about "sharing" passing arguments in js :)
  949. if (typeof jst === 'undefined') {
  950. try {
  951. jst = JSON.stringify(data);
  952. } catch(e) {
  953. jst = false;
  954. }
  955. }
  956. event.data = jst? JSON.parse(jst) : data;
  957. }
  958. }
  959. try {
  960. if (handlers[i].call(event, event, this) === false || event.isDefaultPrevented()) {
  961. this.debug('event-stoped', event.type);
  962. break;
  963. }
  964. } catch (ex) {
  965. window.console && window.console.log && window.console.log(ex);
  966. }
  967. }
  968. // call done functions
  969. if (l = dones.length) {
  970. for (i = 0; i < l; i++) {
  971. try {
  972. if (dones[i].call(event, event, this) === false || event.isDefaultPrevented()) {
  973. this.debug('event-stoped', event.type + '(done)');
  974. break;
  975. }
  976. } catch (ex) {
  977. window.console && window.console.log && window.console.log(ex);
  978. }
  979. }
  980. }
  981. if (this.toUnbindEvents[type] && this.toUnbindEvents[type].length) {
  982. $.each(this.toUnbindEvents[type], function(i, v) {
  983. self.unbind(v.type, v.callback);
  984. });
  985. delete this.toUnbindEvents[type];
  986. }
  987. }
  988. return this;
  989. };
  990. /**
  991. * Get event listeners
  992. *
  993. * @param String event type
  994. * @return Array listed event functions
  995. */
  996. this.getListeners = function(event) {
  997. return event? listeners[event.toLowerCase()] : listeners;
  998. };
  999. // set fm.baseUrl
  1000. this.baseUrl = (function() {
  1001. var myTag, base, baseUrl;
  1002. if (self.options.baseUrl) {
  1003. return self.options.baseUrl;
  1004. } else {
  1005. baseUrl = '';
  1006. myTag = null;
  1007. $('head > script').each(function() {
  1008. if (this.src && this.src.match(/js\/elfinder(?:-[a-z0-9_-]+)?\.(?:min|full)\.js$/i)) {
  1009. myTag = $(this);
  1010. return false;
  1011. }
  1012. });
  1013. if (myTag) {
  1014. baseUrl = myTag.attr('src').replace(/js\/[^\/]+$/, '');
  1015. if (! baseUrl.match(/^(https?\/\/|\/)/)) {
  1016. // check <base> tag
  1017. if (base = $('head > base[href]').attr('href')) {
  1018. baseUrl = base.replace(/\/$/, '') + '/' + baseUrl;
  1019. }
  1020. }
  1021. }
  1022. if (baseUrl !== '') {
  1023. self.options.baseUrl = baseUrl;
  1024. } else {
  1025. if (! self.options.baseUrl) {
  1026. self.options.baseUrl = './';
  1027. }
  1028. baseUrl = self.options.baseUrl;
  1029. }
  1030. return baseUrl;
  1031. }
  1032. })();
  1033. this.i18nBaseUrl = (this.options.i18nBaseUrl || this.baseUrl + 'js/i18n').replace(/\/$/, '') + '/';
  1034. this.options.maxErrorDialogs = Math.max(1, parseInt(this.options.maxErrorDialogs || 5));
  1035. // set dispInlineRegex
  1036. cwdOptionsDefault.dispInlineRegex = this.options.dispInlineRegex;
  1037. // auto load required CSS
  1038. if (this.options.cssAutoLoad) {
  1039. (function() {
  1040. var baseUrl = self.baseUrl,
  1041. myCss = $('head > link[href$="css/elfinder.min.css"],link[href$="css/elfinder.full.css"]:first').length,
  1042. rmTag = function() {
  1043. if (node.data('cssautoloadHide')) {
  1044. node.data('cssautoloadHide').remove();
  1045. node.removeData('cssautoloadHide');
  1046. }
  1047. },
  1048. loaded = function() {
  1049. if (!self.cssloaded) {
  1050. rmTag();
  1051. self.cssloaded = true;
  1052. self.trigger('cssloaded');
  1053. }
  1054. };
  1055. if (! myCss) {
  1056. // to request CSS auto loading
  1057. self.cssloaded = null;
  1058. }
  1059. // additional CSS files
  1060. if (Array.isArray(self.options.cssAutoLoad)) {
  1061. if (self.cssloaded === true) {
  1062. self.loadCss(self.options.cssAutoLoad);
  1063. } else {
  1064. self.bind('cssloaded', function() {
  1065. self.loadCss(self.options.cssAutoLoad);
  1066. });
  1067. }
  1068. }
  1069. // try to load main css
  1070. if (self.cssloaded === null) {
  1071. // hide elFinder node while css loading
  1072. node.addClass('elfinder')
  1073. .data('cssautoloadHide', $('<style>.elfinder{visibility:hidden;overflow:hidden}</style>'));
  1074. $('head').append(node.data('cssautoloadHide'));
  1075. // set default theme
  1076. if (!self.options.themes.default) {
  1077. self.options.themes = Object.assign({
  1078. 'default' : {
  1079. 'name': 'default',
  1080. 'cssurls': 'css/theme.css',
  1081. 'author': 'elFinder Project',
  1082. 'license': '3-clauses BSD'
  1083. }
  1084. }, self.options.themes);
  1085. if (!self.options.theme) {
  1086. self.options.theme = 'default';
  1087. }
  1088. }
  1089. // Delay 'visibility' check it required for browsers such as Safari
  1090. requestAnimationFrame(function() {
  1091. if (node.css('visibility') === 'hidden') {
  1092. // load CSS
  1093. self.loadCss([baseUrl+'css/elfinder.min.css'], {
  1094. dfd: $.Deferred().done(function() {
  1095. loaded();
  1096. }).fail(function() {
  1097. rmTag();
  1098. if (!self.cssloaded) {
  1099. self.cssloaded = false;
  1100. self.bind('init', function() {
  1101. if (!self.cssloaded) {
  1102. self.error(['errRead', 'CSS (elfinder.min)']);
  1103. }
  1104. });
  1105. }
  1106. })
  1107. });
  1108. } else {
  1109. loaded();
  1110. }
  1111. });
  1112. }
  1113. })();
  1114. }
  1115. // load theme if exists
  1116. (function() {
  1117. var theme,
  1118. themes = self.options.themes,
  1119. ids = Object.keys(themes || {});
  1120. if (ids.length) {
  1121. theme = self.storage('theme') || self.options.theme;
  1122. if (!themes[theme]) {
  1123. theme = ids[0];
  1124. }
  1125. if (self.cssloaded) {
  1126. self.changeTheme(theme);
  1127. } else {
  1128. self.bind('cssloaded', function() {
  1129. self.changeTheme(theme);
  1130. });
  1131. }
  1132. }
  1133. })();
  1134. /**
  1135. * Volume option to set the properties of the root Stat
  1136. *
  1137. * @type Object
  1138. */
  1139. this.optionProperties = {
  1140. icon: void(0),
  1141. csscls: void(0),
  1142. tmbUrl: void(0),
  1143. uiCmdMap: {},
  1144. netkey: void(0),
  1145. disabled: []
  1146. };
  1147. if (! inFrame && ! this.options.enableAlways && $('body').children().length === 2) { // only node and beeper
  1148. this.options.enableAlways = true;
  1149. }
  1150. // make options.debug
  1151. if (this.options.debug === true) {
  1152. this.options.debug = 'all';
  1153. } else if (Array.isArray(this.options.debug)) {
  1154. (function() {
  1155. var d = {};
  1156. $.each(self.options.debug, function() {
  1157. d[this] = true;
  1158. });
  1159. self.options.debug = d;
  1160. })();
  1161. } else {
  1162. this.options.debug = false;
  1163. }
  1164. /**
  1165. * Original functions evacuated by conflict check
  1166. *
  1167. * @type Object
  1168. */
  1169. this.noConflicts = {};
  1170. /**
  1171. * Check and save conflicts with bootstrap etc
  1172. *
  1173. * @type Function
  1174. */
  1175. this.noConflict = function() {
  1176. $.each(conflictChecks, function(i, p) {
  1177. if ($.fn[p] && typeof $.fn[p].noConflict === 'function') {
  1178. self.noConflicts[p] = $.fn[p].noConflict();
  1179. }
  1180. });
  1181. };
  1182. // do check conflict
  1183. this.noConflict();
  1184. /**
  1185. * Is elFinder over CORS
  1186. *
  1187. * @type Boolean
  1188. **/
  1189. this.isCORS = false;
  1190. // configure for CORS
  1191. (function(){
  1192. if (typeof self.options.cors !== 'undefined' && self.options.cors !== null) {
  1193. self.isCORS = self.options.cors? true : false;
  1194. } else {
  1195. var parseUrl = document.createElement('a'),
  1196. parseUploadUrl,
  1197. selfProtocol = window.location.protocol,
  1198. portReg = function(protocol) {
  1199. protocol = (!protocol || protocol === ':')? selfProtocol : protocol;
  1200. return protocol === 'https:'? /\:443$/ : /\:80$/;
  1201. },
  1202. selfHost = window.location.host.replace(portReg(selfProtocol), '');
  1203. parseUrl.href = opts.url;
  1204. if (opts.urlUpload && (opts.urlUpload !== opts.url)) {
  1205. parseUploadUrl = document.createElement('a');
  1206. parseUploadUrl.href = opts.urlUpload;
  1207. }
  1208. if (selfHost !== parseUrl.host.replace(portReg(parseUrl.protocol), '')
  1209. || (parseUrl.protocol !== ':'&& parseUrl.protocol !== '' && (selfProtocol !== parseUrl.protocol))
  1210. || (parseUploadUrl &&
  1211. (selfHost !== parseUploadUrl.host.replace(portReg(parseUploadUrl.protocol), '')
  1212. || (parseUploadUrl.protocol !== ':' && parseUploadUrl.protocol !== '' && (selfProtocol !== parseUploadUrl.protocol))
  1213. )
  1214. )
  1215. ) {
  1216. self.isCORS = true;
  1217. }
  1218. }
  1219. if (self.isCORS) {
  1220. if (!$.isPlainObject(self.options.customHeaders)) {
  1221. self.options.customHeaders = {};
  1222. }
  1223. if (!$.isPlainObject(self.options.xhrFields)) {
  1224. self.options.xhrFields = {};
  1225. }
  1226. self.options.requestType = 'post';
  1227. self.options.customHeaders['X-Requested-With'] = 'XMLHttpRequest';
  1228. self.options.xhrFields['withCredentials'] = true;
  1229. }
  1230. })();
  1231. /**
  1232. * Ajax request type
  1233. *
  1234. * @type String
  1235. * @default "get"
  1236. **/
  1237. this.requestType = /^(get|post)$/i.test(this.options.requestType) ? this.options.requestType.toLowerCase() : 'get';
  1238. // set `requestMaxConn` by option
  1239. requestMaxConn = Math.max(parseInt(this.options.requestMaxConn), 1);
  1240. /**
  1241. * Custom data that given as options
  1242. *
  1243. * @type Object
  1244. * @default {}
  1245. */
  1246. this.optsCustomData = $.isPlainObject(this.options.customData) ? this.options.customData : {};
  1247. /**
  1248. * Any data to send across every ajax request
  1249. *
  1250. * @type Object
  1251. * @default {}
  1252. **/
  1253. this.customData = Object.assign({}, this.optsCustomData);
  1254. /**
  1255. * Previous custom data from connector
  1256. *
  1257. * @type Object|null
  1258. */
  1259. this.prevCustomData = null;
  1260. /**
  1261. * Any custom headers to send across every ajax request
  1262. *
  1263. * @type Object
  1264. * @default {}
  1265. */
  1266. this.customHeaders = $.isPlainObject(this.options.customHeaders) ? this.options.customHeaders : {};
  1267. /**
  1268. * Any custom xhrFields to send across every ajax request
  1269. *
  1270. * @type Object
  1271. * @default {}
  1272. */
  1273. this.xhrFields = $.isPlainObject(this.options.xhrFields) ? this.options.xhrFields : {};
  1274. /**
  1275. * Replace XMLHttpRequest.prototype.send to extended function for 3rd party libs XHR request etc.
  1276. *
  1277. * @type Function
  1278. */
  1279. this.replaceXhrSend = function() {
  1280. if (! savedXhrSend) {
  1281. savedXhrSend = XMLHttpRequest.prototype.send;
  1282. }
  1283. XMLHttpRequest.prototype.send = function() {
  1284. var xhr = this;
  1285. // set request headers
  1286. if (self.customHeaders) {
  1287. $.each(self.customHeaders, function(key) {
  1288. xhr.setRequestHeader(key, this);
  1289. });
  1290. }
  1291. // set xhrFields
  1292. if (self.xhrFields) {
  1293. $.each(self.xhrFields, function(key) {
  1294. if (key in xhr) {
  1295. xhr[key] = this;
  1296. }
  1297. });
  1298. }
  1299. return savedXhrSend.apply(this, arguments);
  1300. };
  1301. };
  1302. /**
  1303. * Restore saved original XMLHttpRequest.prototype.send
  1304. *
  1305. * @type Function
  1306. */
  1307. this.restoreXhrSend = function() {
  1308. savedXhrSend && (XMLHttpRequest.prototype.send = savedXhrSend);
  1309. };
  1310. /**
  1311. * command names for into queue for only cwd requests
  1312. * these commands aborts before `open` request
  1313. *
  1314. * @type Array
  1315. * @default ['tmb', 'parents']
  1316. */
  1317. this.abortCmdsOnOpen = this.options.abortCmdsOnOpen || ['tmb', 'parents'];
  1318. /**
  1319. * ui.nav id prefix
  1320. *
  1321. * @type String
  1322. */
  1323. this.navPrefix = 'nav' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-';
  1324. /**
  1325. * ui.cwd id prefix
  1326. *
  1327. * @type String
  1328. */
  1329. this.cwdPrefix = elFinder.prototype.uniqueid? ('cwd' + elFinder.prototype.uniqueid + '-') : '';
  1330. // Increment elFinder.prototype.uniqueid
  1331. ++elFinder.prototype.uniqueid;
  1332. /**
  1333. * URL to upload files
  1334. *
  1335. * @type String
  1336. **/
  1337. this.uploadURL = opts.urlUpload || opts.url;
  1338. /**
  1339. * Events namespace
  1340. *
  1341. * @type String
  1342. **/
  1343. this.namespace = namespace;
  1344. /**
  1345. * Today timestamp
  1346. *
  1347. * @type Number
  1348. **/
  1349. this.today = (new Date(date.getFullYear(), date.getMonth(), date.getDate())).getTime()/1000;
  1350. /**
  1351. * Yesterday timestamp
  1352. *
  1353. * @type Number
  1354. **/
  1355. this.yesterday = this.today - 86400;
  1356. utc = this.options.UTCDate ? 'UTC' : '';
  1357. this.getHours = 'get'+utc+'Hours';
  1358. this.getMinutes = 'get'+utc+'Minutes';
  1359. this.getSeconds = 'get'+utc+'Seconds';
  1360. this.getDate = 'get'+utc+'Date';
  1361. this.getDay = 'get'+utc+'Day';
  1362. this.getMonth = 'get'+utc+'Month';
  1363. this.getFullYear = 'get'+utc+'FullYear';
  1364. /**
  1365. * elFinder node z-index (auto detect on elFinder load)
  1366. *
  1367. * @type null | Number
  1368. **/
  1369. this.zIndex;
  1370. /**
  1371. * Current search status
  1372. *
  1373. * @type Object
  1374. */
  1375. this.searchStatus = {
  1376. state : 0, // 0: search ended, 1: search started, 2: in search result
  1377. query : '',
  1378. target : '',
  1379. mime : '',
  1380. mixed : false, // in multi volumes search: false or Array that target volume ids
  1381. ininc : false // in incremental search
  1382. };
  1383. /**
  1384. * Interface language
  1385. *
  1386. * @type String
  1387. * @default "en"
  1388. **/
  1389. this.lang = this.storage('lang') || this.options.lang;
  1390. if (this.lang === 'jp') {
  1391. this.lang = this.options.lang = 'ja';
  1392. }
  1393. this.viewType = this.storage('view') || this.options.defaultView || 'icons';
  1394. this.sortType = this.storage('sortType') || this.options.sortType || 'name';
  1395. this.sortOrder = this.storage('sortOrder') || this.options.sortOrder || 'asc';
  1396. this.sortStickFolders = this.storage('sortStickFolders');
  1397. if (this.sortStickFolders === null) {
  1398. this.sortStickFolders = !!this.options.sortStickFolders;
  1399. } else {
  1400. this.sortStickFolders = !!this.sortStickFolders;
  1401. }
  1402. this.sortAlsoTreeview = this.storage('sortAlsoTreeview');
  1403. if (this.sortAlsoTreeview === null || this.options.sortAlsoTreeview === null) {
  1404. this.sortAlsoTreeview = !!this.options.sortAlsoTreeview;
  1405. } else {
  1406. this.sortAlsoTreeview = !!this.sortAlsoTreeview;
  1407. }
  1408. this.sortRules = $.extend(true, {}, this._sortRules, this.options.sortRules);
  1409. $.each(this.sortRules, function(name, method) {
  1410. if (typeof method != 'function') {
  1411. delete self.sortRules[name];
  1412. }
  1413. });
  1414. this.compare = $.proxy(this.compare, this);
  1415. /**
  1416. * Delay in ms before open notification dialog
  1417. *
  1418. * @type Number
  1419. * @default 500
  1420. **/
  1421. this.notifyDelay = this.options.notifyDelay > 0 ? parseInt(this.options.notifyDelay) : 500;
  1422. /**
  1423. * Dragging UI Helper object
  1424. *
  1425. * @type jQuery | null
  1426. **/
  1427. this.draggingUiHelper = null;
  1428. /**
  1429. * Base droppable options
  1430. *
  1431. * @type Object
  1432. **/
  1433. this.droppable = {
  1434. greedy : true,
  1435. tolerance : 'pointer',
  1436. accept : '.elfinder-cwd-file-wrapper,.elfinder-navbar-dir,.elfinder-cwd-file,.elfinder-cwd-filename',
  1437. hoverClass : this.res('class', 'adroppable'),
  1438. classes : { // Deprecated hoverClass jQueryUI>=1.12.0
  1439. 'ui-droppable-hover': this.res('class', 'adroppable')
  1440. },
  1441. autoDisable: true, // elFinder original, see jquery.elfinder.js
  1442. drop : function(e, ui) {
  1443. var dst = $(this),
  1444. targets = $.grep(ui.helper.data('files')||[], function(h) { return h? true : false; }),
  1445. result = [],
  1446. dups = [],
  1447. faults = [],
  1448. isCopy = ui.helper.hasClass('elfinder-drag-helper-plus'),
  1449. c = 'class',
  1450. cnt, hash, i, h;
  1451. if (typeof e.button === 'undefined' || ui.helper.data('namespace') !== namespace || ! self.insideWorkzone(e.pageX, e.pageY)) {
  1452. return false;
  1453. }
  1454. if (dst.hasClass(self.res(c, 'cwdfile'))) {
  1455. hash = self.cwdId2Hash(dst.attr('id'));
  1456. } else if (dst.hasClass(self.res(c, 'navdir'))) {
  1457. hash = self.navId2Hash(dst.attr('id'));
  1458. } else {
  1459. hash = cwd;
  1460. }
  1461. cnt = targets.length;
  1462. while (cnt--) {
  1463. h = targets[cnt];
  1464. // ignore drop into itself or in own location
  1465. if (h != hash && files[h].phash != hash) {
  1466. result.push(h);
  1467. } else {
  1468. ((isCopy && h !== hash && files[hash].write)? dups : faults).push(h);
  1469. }
  1470. }
  1471. if (faults.length) {
  1472. return false;
  1473. }
  1474. ui.helper.data('droped', true);
  1475. if (dups.length) {
  1476. ui.helper.hide();
  1477. self.exec('duplicate', dups, {_userAction: true});
  1478. }
  1479. if (result.length) {
  1480. ui.helper.hide();
  1481. self.clipboard(result, !isCopy);
  1482. self.exec('paste', hash, {_userAction: true}, hash).always(function(){
  1483. self.clipboard([]);
  1484. self.trigger('unlockfiles', {files : targets});
  1485. });
  1486. self.trigger('drop', {files : targets});
  1487. }
  1488. }
  1489. };
  1490. /**
  1491. * Return true if filemanager is active
  1492. *
  1493. * @return Boolean
  1494. **/
  1495. this.enabled = function() {
  1496. return enabled && this.visible();
  1497. };
  1498. /**
  1499. * Return true if filemanager is visible
  1500. *
  1501. * @return Boolean
  1502. **/
  1503. this.visible = function() {
  1504. return node[0].elfinder && node.is(':visible');
  1505. };
  1506. /**
  1507. * Return file is root?
  1508. *
  1509. * @param Object target file object
  1510. * @return Boolean
  1511. */
  1512. this.isRoot = function(file) {
  1513. return (file.isroot || ! file.phash)? true : false;
  1514. };
  1515. /**
  1516. * Return root dir hash for current working directory
  1517. *
  1518. * @param String target hash
  1519. * @param Boolean include fake parent (optional)
  1520. * @return String
  1521. */
  1522. this.root = function(hash, fake) {
  1523. hash = hash || cwd;
  1524. var dir, i;
  1525. if (! fake) {
  1526. $.each(self.roots, function(id, rhash) {
  1527. if (hash.indexOf(id) === 0) {
  1528. dir = rhash;
  1529. return false;
  1530. }
  1531. });
  1532. if (dir) {
  1533. return dir;
  1534. }
  1535. }
  1536. dir = files[hash];
  1537. while (dir && dir.phash && (fake || ! dir.isroot)) {
  1538. dir = files[dir.phash];
  1539. }
  1540. if (dir) {
  1541. return dir.hash;
  1542. }
  1543. while (i in files && files.hasOwnProperty(i)) {
  1544. dir = files[i];
  1545. if (dir.mime === 'directory' && !dir.phash && dir.read) {
  1546. return dir.hash;
  1547. }
  1548. }
  1549. return '';
  1550. };
  1551. /**
  1552. * Return current working directory info
  1553. *
  1554. * @return Object
  1555. */
  1556. this.cwd = function() {
  1557. return files[cwd] || {};
  1558. };
  1559. /**
  1560. * Return required cwd option
  1561. *
  1562. * @param String option name
  1563. * @param String target hash (optional)
  1564. * @return mixed
  1565. */
  1566. this.option = function(name, target) {
  1567. var res, item;
  1568. target = target || cwd;
  1569. if (self.optionsByHashes[target] && typeof self.optionsByHashes[target][name] !== 'undefined') {
  1570. return self.optionsByHashes[target][name];
  1571. }
  1572. if (self.hasVolOptions && cwd !== target && (!(item = self.file(target)) || item.phash !== cwd)) {
  1573. res = '';
  1574. $.each(self.volOptions, function(id, opt) {
  1575. if (target.indexOf(id) === 0) {
  1576. res = opt[name] || '';
  1577. return false;
  1578. }
  1579. });
  1580. return res;
  1581. } else {
  1582. return cwdOptions[name] || '';
  1583. }
  1584. };
  1585. /**
  1586. * Return disabled commands by each folder
  1587. *
  1588. * @param Array target hashes
  1589. * @return Array
  1590. */
  1591. this.getDisabledCmds = function(targets, flip) {
  1592. var disabled = {'hidden': true};
  1593. if (! Array.isArray(targets)) {
  1594. targets = [ targets ];
  1595. }
  1596. $.each(targets, function(i, h) {
  1597. var disCmds = self.option('disabledFlip', h);
  1598. if (disCmds) {
  1599. Object.assign(disabled, disCmds);
  1600. }
  1601. });
  1602. return flip? disabled : Object.keys(disabled);
  1603. };
  1604. /**
  1605. * Return file data from current dir or tree by it's hash
  1606. *
  1607. * @param String file hash
  1608. * @return Object
  1609. */
  1610. this.file = function(hash, alsoHidden) {
  1611. return hash? (files[hash] || (alsoHidden? hiddenFiles[hash] : void(0))) : void(0);
  1612. };
  1613. /**
  1614. * Return all cached files
  1615. *
  1616. * @param String parent hash
  1617. * @return Object
  1618. */
  1619. this.files = function(phash) {
  1620. var items = {};
  1621. if (phash) {
  1622. if (!ownFiles[phash]) {
  1623. return {};
  1624. }
  1625. $.each(ownFiles[phash], function(h) {
  1626. if (files[h]) {
  1627. items[h] = files[h];
  1628. } else {
  1629. delete ownFiles[phash][h];
  1630. }
  1631. });
  1632. return Object.assign({}, items);
  1633. }
  1634. return Object.assign({}, files);
  1635. };
  1636. /**
  1637. * Return list of file parents hashes include file hash
  1638. *
  1639. * @param String file hash
  1640. * @return Array
  1641. */
  1642. this.parents = function(hash) {
  1643. var parents = [],
  1644. dir;
  1645. while (hash && (dir = this.file(hash))) {
  1646. parents.unshift(dir.hash);
  1647. hash = dir.phash;
  1648. }
  1649. return parents;
  1650. };
  1651. this.path2array = function(hash, i18) {
  1652. var file,
  1653. path = [];
  1654. while (hash) {
  1655. if ((file = files[hash]) && file.hash) {
  1656. path.unshift(i18 && file.i18 ? file.i18 : file.name);
  1657. hash = file.isroot? null : file.phash;
  1658. } else {
  1659. path = [];
  1660. break;
  1661. }
  1662. }
  1663. return path;
  1664. };
  1665. /**
  1666. * Return file path or Get path async with jQuery.Deferred
  1667. *
  1668. * @param Object file
  1669. * @param Boolean i18
  1670. * @param Object asyncOpt
  1671. * @return String|jQuery.Deferred
  1672. */
  1673. this.path = function(hash, i18, asyncOpt) {
  1674. var path = files[hash] && files[hash].path
  1675. ? files[hash].path
  1676. : this.path2array(hash, i18).join(cwdOptions.separator);
  1677. if (! asyncOpt || ! files[hash]) {
  1678. return path;
  1679. } else {
  1680. asyncOpt = Object.assign({notify: {type : 'parents', cnt : 1, hideCnt : true}}, asyncOpt);
  1681. var dfd = $.Deferred(),
  1682. notify = asyncOpt.notify,
  1683. noreq = false,
  1684. req = function() {
  1685. self.request({
  1686. data : {cmd : 'parents', target : files[hash].phash},
  1687. notify : notify,
  1688. preventFail : true
  1689. })
  1690. .done(done)
  1691. .fail(function() {
  1692. dfd.reject();
  1693. });
  1694. },
  1695. done = function() {
  1696. self.one('parentsdone', function() {
  1697. path = self.path(hash, i18);
  1698. if (path === '' && noreq) {
  1699. //retry with request
  1700. noreq = false;
  1701. req();
  1702. } else {
  1703. if (notify) {
  1704. clearTimeout(ntftm);
  1705. notify.cnt = -(parseInt(notify.cnt || 0));
  1706. self.notify(notify);
  1707. }
  1708. dfd.resolve(path);
  1709. }
  1710. });
  1711. },
  1712. ntftm;
  1713. if (path) {
  1714. return dfd.resolve(path);
  1715. } else {
  1716. if (self.ui['tree']) {
  1717. // try as no request
  1718. if (notify) {
  1719. ntftm = setTimeout(function() {
  1720. self.notify(notify);
  1721. }, self.notifyDelay);
  1722. }
  1723. noreq = true;
  1724. done(true);
  1725. } else {
  1726. req();
  1727. }
  1728. return dfd;
  1729. }
  1730. }
  1731. };
  1732. /**
  1733. * Return file url if set
  1734. *
  1735. * @param String file hash
  1736. * @param Object Options
  1737. * @return String|Object of jQuery Deferred
  1738. */
  1739. this.url = function(hash, o) {
  1740. var file = files[hash],
  1741. opts = o || {},
  1742. async = opts.async || false,
  1743. temp = opts.temporary || false,
  1744. onetm = (opts.onetime && self.option('onetimeUrl', hash)) || false,
  1745. absurl = opts.absurl || false,
  1746. dfrd = (async || onetm)? $.Deferred() : null,
  1747. filter = function(url) {
  1748. if (url && absurl) {
  1749. url = self.convAbsUrl(url);
  1750. }
  1751. return url;
  1752. },
  1753. getUrl = function(url) {
  1754. if (url) {
  1755. return filter(url);
  1756. }
  1757. if (file.url) {
  1758. return filter(file.url);
  1759. }
  1760. if (typeof baseUrl === 'undefined') {
  1761. baseUrl = self.option('url', (!self.isRoot(file) && file.phash) || file.hash);
  1762. }
  1763. if (baseUrl) {
  1764. return filter(baseUrl + $.map(self.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/'));
  1765. }
  1766. var params = Object.assign({}, self.customData, {
  1767. cmd: 'file',
  1768. target: file.hash
  1769. });
  1770. if (self.oldAPI) {
  1771. params.cmd = 'open';
  1772. params.current = file.phash;
  1773. }
  1774. return filter(self.options.url + (self.options.url.indexOf('?') === -1 ? '?' : '&') + $.param(params, true));
  1775. },
  1776. baseUrl, res;
  1777. if (!file || !file.read) {
  1778. return async? dfrd.resolve('') : '';
  1779. }
  1780. if (onetm) {
  1781. async = true;
  1782. this.request({
  1783. data : { cmd : 'url', target : hash, options : { onetime: 1 } },
  1784. preventDefault : true,
  1785. options: {async: async},
  1786. notify: {type : 'file', cnt : 1, hideCnt : true}
  1787. }).done(function(data) {
  1788. dfrd.resolve(filter(data.url || ''));
  1789. }).fail(function() {
  1790. dfrd.resolve('');
  1791. });
  1792. } else {
  1793. if (file.url == '1' || (temp && !file.url && !(baseUrl = self.option('url', (!self.isRoot(file) && file.phash) || file.hash)))) {
  1794. this.request({
  1795. data : { cmd : 'url', target : hash, options : { temporary: temp? 1 : 0 } },
  1796. preventDefault : true,
  1797. options: {async: async},
  1798. notify: async? {type : temp? 'file' : 'url', cnt : 1, hideCnt : true} : {}
  1799. })
  1800. .done(function(data) {
  1801. file.url = data.url || '';
  1802. })
  1803. .fail(function() {
  1804. file.url = '';
  1805. })
  1806. .always(function() {
  1807. var url;
  1808. if (file.url && temp) {
  1809. url = file.url;
  1810. file.url = '1'; // restore
  1811. }
  1812. if (async) {
  1813. dfrd.resolve(getUrl(url));
  1814. } else {
  1815. return getUrl(url);
  1816. }
  1817. });
  1818. } else {
  1819. if (async) {
  1820. dfrd.resolve(getUrl());
  1821. } else {
  1822. return getUrl();
  1823. }
  1824. }
  1825. }
  1826. if (async) {
  1827. return dfrd;
  1828. }
  1829. };
  1830. /**
  1831. * Return file url for the extarnal service
  1832. *
  1833. * @param String hash The hash
  1834. * @param Object options The options
  1835. * @return Object jQuery Deferred
  1836. */
  1837. this.forExternalUrl = function(hash, options) {
  1838. var onetime = self.option('onetimeUrl', hash),
  1839. opts = {
  1840. async: true,
  1841. absurl: true
  1842. };
  1843. opts[onetime? 'onetime' : 'temporary'] = true;
  1844. return self.url(hash, Object.assign({}, options, opts));
  1845. };
  1846. /**
  1847. * Return file url for open in elFinder
  1848. *
  1849. * @param String file hash
  1850. * @param Boolean for download link
  1851. * @return String
  1852. */
  1853. this.openUrl = function(hash, download) {
  1854. var file = files[hash],
  1855. url = '';
  1856. if (!file || !file.read) {
  1857. return '';
  1858. }
  1859. if (!download) {
  1860. if (file.url) {
  1861. if (file.url != 1) {
  1862. url = file.url;
  1863. }
  1864. } else if (cwdOptions.url && file.hash.indexOf(self.cwd().volumeid) === 0) {
  1865. url = cwdOptions.url + $.map(this.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/');
  1866. }
  1867. if (url) {
  1868. url += (url.match(/\?/)? '&' : '?') + '_'.repeat((url.match(/[\?&](_+)t=/g) || ['&t=']).sort().shift().match(/[\?&](_*)t=/)[1].length + 1) + 't=' + (file.ts || parseInt(+new Date()/1000));
  1869. return url;
  1870. }
  1871. }
  1872. url = this.options.url;
  1873. url = url + (url.indexOf('?') === -1 ? '?' : '&')
  1874. + (this.oldAPI ? 'cmd=open&current='+file.phash : 'cmd=file')
  1875. + '&target=' + file.hash
  1876. + '&_t=' + (file.ts || parseInt(+new Date()/1000));
  1877. if (download) {
  1878. url += '&download=1';
  1879. }
  1880. $.each(this.customData, function(key, val) {
  1881. url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val);
  1882. });
  1883. return url;
  1884. };
  1885. /**
  1886. * Return thumbnail url
  1887. *
  1888. * @param Object file object
  1889. * @return String
  1890. */
  1891. this.tmb = function(file) {
  1892. var tmbUrl, tmbCrop,
  1893. cls = 'elfinder-cwd-bgurl',
  1894. url = '';
  1895. if ($.isPlainObject(file)) {
  1896. if (self.searchStatus.state && file.hash.indexOf(self.cwd().volumeid) !== 0) {
  1897. tmbUrl = self.option('tmbUrl', file.hash);
  1898. tmbCrop = self.option('tmbCrop', file.hash);
  1899. } else {
  1900. tmbUrl = cwdOptions['tmbUrl'];
  1901. tmbCrop = cwdOptions['tmbCrop'];
  1902. }
  1903. if (tmbCrop) {
  1904. cls += ' elfinder-cwd-bgurl-crop';
  1905. }
  1906. if (tmbUrl === 'self' && file.mime.indexOf('image/') === 0) {
  1907. url = self.openUrl(file.hash);
  1908. cls += ' elfinder-cwd-bgself';
  1909. } else if ((self.oldAPI || tmbUrl) && file && file.tmb && file.tmb != 1) {
  1910. url = tmbUrl + file.tmb;
  1911. } else if (self.newAPI && file && file.tmb && file.tmb != 1) {
  1912. url = file.tmb;
  1913. }
  1914. if (url) {
  1915. if (file.ts && tmbUrl !== 'self') {
  1916. url += (url.match(/\?/)? '&' : '?') + '_t=' + file.ts;
  1917. }
  1918. return { url: url, className: cls };
  1919. }
  1920. }
  1921. return false;
  1922. };
  1923. /**
  1924. * Return selected files hashes
  1925. *
  1926. * @return Array
  1927. **/
  1928. this.selected = function() {
  1929. return selected.slice(0);
  1930. };
  1931. /**
  1932. * Return selected files info
  1933. *
  1934. * @return Array
  1935. */
  1936. this.selectedFiles = function() {
  1937. return $.map(selected, function(hash) { return files[hash] ? Object.assign({}, files[hash]) : null; });
  1938. };
  1939. /**
  1940. * Return true if file with required name existsin required folder
  1941. *
  1942. * @param String file name
  1943. * @param String parent folder hash
  1944. * @return Boolean
  1945. */
  1946. this.fileByName = function(name, phash) {
  1947. var hash;
  1948. for (hash in files) {
  1949. if (files.hasOwnProperty(hash) && files[hash].phash == phash && files[hash].name == name) {
  1950. return files[hash];
  1951. }
  1952. }
  1953. };
  1954. /**
  1955. * Valid data for required command based on rules
  1956. *
  1957. * @param String command name
  1958. * @param Object cammand's data
  1959. * @return Boolean
  1960. */
  1961. this.validResponse = function(cmd, data) {
  1962. return data.error || this.rules[this.rules[cmd] ? cmd : 'defaults'](data);
  1963. };
  1964. /**
  1965. * Return bytes from ini formated size
  1966. *
  1967. * @param String ini formated size
  1968. * @return Integer
  1969. */
  1970. this.returnBytes = function(val) {
  1971. var last;
  1972. if (isNaN(val)) {
  1973. if (! val) {
  1974. val = '';
  1975. }
  1976. // for ex. 1mb, 1KB
  1977. val = val.replace(/b$/i, '');
  1978. last = val.charAt(val.length - 1).toLowerCase();
  1979. val = val.replace(/[tgmk]$/i, '');
  1980. if (last == 't') {
  1981. val = val * 1024 * 1024 * 1024 * 1024;
  1982. } else if (last == 'g') {
  1983. val = val * 1024 * 1024 * 1024;
  1984. } else if (last == 'm') {
  1985. val = val * 1024 * 1024;
  1986. } else if (last == 'k') {
  1987. val = val * 1024;
  1988. }
  1989. val = isNaN(val)? 0 : parseInt(val);
  1990. } else {
  1991. val = parseInt(val);
  1992. if (val < 1) val = 0;
  1993. }
  1994. return val;
  1995. };
  1996. /**
  1997. * Process ajax request.
  1998. * Fired events :
  1999. * @todo
  2000. * @example
  2001. * @todo
  2002. * @return $.Deferred
  2003. */
  2004. this.request = function(opts) {
  2005. var self = this,
  2006. o = this.options,
  2007. dfrd = $.Deferred(),
  2008. // request ID
  2009. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16),
  2010. // request data
  2011. data = Object.assign({}, self.customData, {mimes : o.onlyMimes}, opts.data || opts),
  2012. // command name
  2013. cmd = data.cmd,
  2014. // request type is binary
  2015. isBinary = (opts.options || {}).dataType === 'binary',
  2016. // current cmd is "open"
  2017. isOpen = (!opts.asNotOpen && cmd === 'open'),
  2018. // call default fail callback (display error dialog) ?
  2019. deffail = !(isBinary || opts.preventDefault || opts.preventFail),
  2020. // call default success callback ?
  2021. defdone = !(isBinary || opts.preventDefault || opts.preventDone),
  2022. // options for notify dialog
  2023. notify = Object.assign({}, opts.notify),
  2024. // make cancel button
  2025. cancel = !!opts.cancel,
  2026. // do not normalize data - return as is
  2027. raw = isBinary || !!opts.raw,
  2028. // sync files on request fail
  2029. syncOnFail = opts.syncOnFail,
  2030. // use lazy()
  2031. lazy = !!opts.lazy,
  2032. // prepare function before done()
  2033. prepare = opts.prepare,
  2034. // navigate option object when cmd done
  2035. navigate = opts.navigate,
  2036. // open notify dialog timeout
  2037. timeout,
  2038. // use browser cache
  2039. useCache = (opts.options || {}).cache,
  2040. // request options
  2041. options = Object.assign({
  2042. url : o.url,
  2043. async : true,
  2044. type : this.requestType,
  2045. dataType : 'json',
  2046. cache : (self.api >= 2.1029), // api >= 2.1029 has unique request ID
  2047. data : data,
  2048. headers : this.customHeaders,
  2049. xhrFields: this.xhrFields
  2050. }, opts.options || {}),
  2051. /**
  2052. * Default success handler.
  2053. * Call default data handlers and fire event with command name.
  2054. *
  2055. * @param Object normalized response data
  2056. * @return void
  2057. **/
  2058. done = function(data) {
  2059. data.warning && self.error(data.warning);
  2060. if (isOpen) {
  2061. open(data);
  2062. } else {
  2063. self.updateCache(data);
  2064. }
  2065. data.changed && data.changed.length && change(data.changed);
  2066. self.lazy(function() {
  2067. // fire some event to update cache/ui
  2068. data.removed && data.removed.length && self.remove(data);
  2069. data.added && data.added.length && self.add(data);
  2070. data.changed && data.changed.length && self.change(data);
  2071. }).then(function() {
  2072. // fire event with command name
  2073. return self.lazy(function() {
  2074. self.trigger(cmd, data, false);
  2075. });
  2076. }).then(function() {
  2077. // fire event with command name + 'done'
  2078. return self.lazy(function() {
  2079. self.trigger(cmd + 'done');
  2080. });
  2081. }).then(function() {
  2082. // make toast message
  2083. if (data.toasts && Array.isArray(data.toasts)) {
  2084. $.each(data.toasts, function() {
  2085. this.msg && self.toast(this);
  2086. });
  2087. }
  2088. // force update content
  2089. data.sync && self.sync();
  2090. });
  2091. },
  2092. /**
  2093. * Request error handler. Reject dfrd with correct error message.
  2094. *
  2095. * @param jqxhr request object
  2096. * @param String request status
  2097. * @return void
  2098. **/
  2099. error = function(xhr, status) {
  2100. var error, data,
  2101. d = self.options.debug;
  2102. switch (status) {
  2103. case 'abort':
  2104. error = xhr.quiet ? '' : ['errConnect', 'errAbort'];
  2105. break;
  2106. case 'timeout':
  2107. error = ['errConnect', 'errTimeout'];
  2108. break;
  2109. case 'parsererror':
  2110. error = ['errResponse', 'errDataNotJSON'];
  2111. if (xhr.responseText) {
  2112. if (! cwd || (d && (d === 'all' || d['backend-error']))) {
  2113. error.push(xhr.responseText);
  2114. }
  2115. }
  2116. break;
  2117. default:
  2118. if (xhr.responseText) {
  2119. // check responseText, Is that JSON?
  2120. try {
  2121. data = JSON.parse(xhr.responseText);
  2122. if (data && data.error) {
  2123. error = data.error;
  2124. }
  2125. } catch(e) {}
  2126. }
  2127. if (! error) {
  2128. if (xhr.status == 403) {
  2129. error = ['errConnect', 'errAccess', 'HTTP error ' + xhr.status];
  2130. } else if (xhr.status == 404) {
  2131. error = ['errConnect', 'errNotFound', 'HTTP error ' + xhr.status];
  2132. } else if (xhr.status >= 500) {
  2133. error = ['errResponse', 'errServerError', 'HTTP error ' + xhr.status];
  2134. } else {
  2135. if (xhr.status == 414 && options.type === 'get') {
  2136. // retry by POST method
  2137. options.type = 'post';
  2138. self.abortXHR(xhr);
  2139. dfrd.xhr = xhr = self.transport.send(options).fail(error).done(success);
  2140. return;
  2141. }
  2142. error = xhr.quiet ? '' : ['errConnect', 'HTTP error ' + xhr.status];
  2143. }
  2144. }
  2145. }
  2146. self.trigger(cmd + 'done');
  2147. dfrd.reject({error: error}, xhr, status);
  2148. },
  2149. /**
  2150. * Request success handler. Valid response data and reject/resolve dfrd.
  2151. *
  2152. * @param Object response data
  2153. * @param String request status
  2154. * @return void
  2155. **/
  2156. success = function(response) {
  2157. var d = self.options.debug;
  2158. // Set currrent request command name
  2159. self.currentReqCmd = cmd;
  2160. if (response.debug && (!d || d !== 'all')) {
  2161. if (!d) {
  2162. d = self.options.debug = {};
  2163. }
  2164. d['backend-error'] = true;
  2165. d['warning'] = true;
  2166. }
  2167. if (raw) {
  2168. self.abortXHR(xhr);
  2169. response && response.debug && self.debug('backend-debug', response);
  2170. return dfrd.resolve(response);
  2171. }
  2172. if (!response) {
  2173. return dfrd.reject({error :['errResponse', 'errDataEmpty']}, xhr, response);
  2174. } else if (!$.isPlainObject(response)) {
  2175. return dfrd.reject({error :['errResponse', 'errDataNotJSON']}, xhr, response);
  2176. } else if (response.error) {
  2177. if (isOpen) {
  2178. // check leafRoots
  2179. $.each(self.leafRoots, function(phash, roots) {
  2180. self.leafRoots[phash] = $.grep(roots, function(h) { return h !== data.target; });
  2181. });
  2182. }
  2183. return dfrd.reject({error :response.error}, xhr, response);
  2184. }
  2185. var resolve = function() {
  2186. var pushLeafRoots = function(name) {
  2187. if (self.leafRoots[data.target] && response[name]) {
  2188. $.each(self.leafRoots[data.target], function(i, h) {
  2189. var root;
  2190. if (root = self.file(h)) {
  2191. response[name].push(root);
  2192. }
  2193. });
  2194. }
  2195. },
  2196. setTextMimes = function() {
  2197. self.textMimes = {};
  2198. $.each(self.res('mimes', 'text'), function() {
  2199. self.textMimes[this.toLowerCase()] = true;
  2200. });
  2201. },
  2202. actionTarget;
  2203. if (isOpen) {
  2204. pushLeafRoots('files');
  2205. } else if (cmd === 'tree') {
  2206. pushLeafRoots('tree');
  2207. }
  2208. response = self.normalize(response);
  2209. if (!self.validResponse(cmd, response)) {
  2210. return dfrd.reject({error :(response.norError || 'errResponse')}, xhr, response);
  2211. }
  2212. if (isOpen) {
  2213. if (!self.api) {
  2214. self.api = response.api || 1;
  2215. if (self.api == '2.0' && typeof response.options.uploadMaxSize !== 'undefined') {
  2216. self.api = '2.1';
  2217. }
  2218. self.newAPI = self.api >= 2;
  2219. self.oldAPI = !self.newAPI;
  2220. }
  2221. if (response.textMimes && Array.isArray(response.textMimes)) {
  2222. self.resources.mimes.text = response.textMimes;
  2223. setTextMimes();
  2224. }
  2225. !self.textMimes && setTextMimes();
  2226. if (response.options) {
  2227. cwdOptions = Object.assign({}, cwdOptionsDefault, response.options);
  2228. }
  2229. if (response.netDrivers) {
  2230. self.netDrivers = response.netDrivers;
  2231. }
  2232. if (response.maxTargets) {
  2233. self.maxTargets = response.maxTargets;
  2234. }
  2235. if (!!data.init) {
  2236. self.uplMaxSize = self.returnBytes(response.uplMaxSize);
  2237. self.uplMaxFile = !!response.uplMaxFile? Math.min(parseInt(response.uplMaxFile), 50) : 20;
  2238. }
  2239. }
  2240. if (typeof prepare === 'function') {
  2241. prepare(response);
  2242. }
  2243. if (navigate) {
  2244. actionTarget = navigate.target || 'added';
  2245. if (response[actionTarget] && response[actionTarget].length) {
  2246. self.one(cmd + 'done', function() {
  2247. var targets = response[actionTarget],
  2248. newItems = self.findCwdNodes(targets),
  2249. inCwdHashes = function() {
  2250. var cwdHash = self.cwd().hash;
  2251. return $.map(targets, function(f) { return (f.phash && cwdHash === f.phash)? f.hash : null; });
  2252. },
  2253. hashes = inCwdHashes(),
  2254. makeToast = function(t) {
  2255. var node = void(0),
  2256. data = t.action? t.action.data : void(0),
  2257. cmd, msg, done;
  2258. if ((data || hashes.length) && t.action && (msg = t.action.msg) && (cmd = t.action.cmd) && (!t.action.cwdNot || t.action.cwdNot !== self.cwd().hash)) {
  2259. done = t.action.done;
  2260. data = t.action.data;
  2261. node = $('<div/>')
  2262. .append(
  2263. $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all elfinder-tabstop"><span class="ui-button-text">'
  2264. +self.i18n(msg)
  2265. +'</span></button>')
  2266. .on('mouseenter mouseleave', function(e) {
  2267. $(this).toggleClass('ui-state-hover', e.type == 'mouseenter');
  2268. })
  2269. .on('click', function() {
  2270. self.exec(cmd, data || hashes, {_userAction: true, _currentType: 'toast', _currentNode: $(this) });
  2271. if (done) {
  2272. self.one(cmd+'done', function() {
  2273. if (typeof done === 'function') {
  2274. done();
  2275. } else if (done === 'select') {
  2276. self.trigger('selectfiles', {files : inCwdHashes()});
  2277. }
  2278. });
  2279. }
  2280. })
  2281. );
  2282. }
  2283. delete t.action;
  2284. t.extNode = node;
  2285. return t;
  2286. };
  2287. if (! navigate.toast) {
  2288. navigate.toast = {};
  2289. }
  2290. !navigate.noselect && self.trigger('selectfiles', {files : self.searchStatus.state > 1 ? $.map(targets, function(f) { return f.hash; }) : hashes});
  2291. if (newItems.length) {
  2292. if (!navigate.noscroll) {
  2293. newItems.first().trigger('scrolltoview', {blink : false});
  2294. self.resources.blink(newItems, 'lookme');
  2295. }
  2296. if ($.isPlainObject(navigate.toast.incwd)) {
  2297. self.toast(makeToast(navigate.toast.incwd));
  2298. }
  2299. } else {
  2300. if ($.isPlainObject(navigate.toast.inbuffer)) {
  2301. self.toast(makeToast(navigate.toast.inbuffer));
  2302. }
  2303. }
  2304. });
  2305. }
  2306. }
  2307. dfrd.resolve(response);
  2308. response.debug && self.debug('backend-debug', response);
  2309. };
  2310. self.abortXHR(xhr);
  2311. lazy? self.lazy(resolve) : resolve();
  2312. },
  2313. xhr, _xhr,
  2314. xhrAbort = function(e) {
  2315. if (xhr && xhr.state() === 'pending') {
  2316. self.abortXHR(xhr, { quiet: true , abort: true });
  2317. if (!e || (e.type !== 'unload' && e.type !== 'destroy')) {
  2318. self.autoSync();
  2319. }
  2320. }
  2321. },
  2322. abort = function(e){
  2323. self.trigger(cmd + 'done');
  2324. if (e.type == 'autosync') {
  2325. if (e.data.action != 'stop') return;
  2326. } else if (e.type != 'unload' && e.type != 'destroy' && e.type != 'openxhrabort') {
  2327. if (!e.data.added || !e.data.added.length) {
  2328. return;
  2329. }
  2330. }
  2331. xhrAbort(e);
  2332. },
  2333. request = function(mode) {
  2334. var queueAbort = function() {
  2335. syncOnFail = false;
  2336. dfrd.reject();
  2337. };
  2338. if (mode) {
  2339. if (mode === 'cmd') {
  2340. return cmd;
  2341. }
  2342. }
  2343. if (isOpen) {
  2344. if (requestQueueSkipOpen) {
  2345. return dfrd.reject();
  2346. }
  2347. requestQueueSkipOpen = true;
  2348. }
  2349. dfrd.always(function() {
  2350. delete options.headers['X-elFinderReqid'];
  2351. }).fail(function(error, xhr, response) {
  2352. var errData = {
  2353. cmd: cmd,
  2354. err: error,
  2355. xhr: xhr,
  2356. rc: response
  2357. };
  2358. // unset this cmd queue when user canceling
  2359. // see notify : function - `cancel.reject(0);`
  2360. if (error === 0) {
  2361. if (requestQueue.length) {
  2362. requestQueue = $.grep(requestQueue, function(req) {
  2363. return (req('cmd') === cmd) ? false : true;
  2364. });
  2365. }
  2366. }
  2367. // trigger "requestError" event
  2368. self.trigger('requestError', errData);
  2369. if (errData._event && errData._event.isDefaultPrevented()) {
  2370. deffail = false;
  2371. syncOnFail = false;
  2372. if (error) {
  2373. error.error = '';
  2374. }
  2375. }
  2376. // abort xhr
  2377. xhrAbort();
  2378. if (isOpen) {
  2379. openDir = self.file(data.target);
  2380. openDir && openDir.volumeid && self.isRoot(openDir) && delete self.volumeExpires[openDir.volumeid];
  2381. }
  2382. self.trigger(cmd + 'fail', response);
  2383. if (error) {
  2384. deffail ? self.error(error) : self.debug('error', self.i18n(error));
  2385. }
  2386. syncOnFail && self.sync();
  2387. });
  2388. if (!cmd) {
  2389. syncOnFail = false;
  2390. return dfrd.reject({error :'errCmdReq'});
  2391. }
  2392. if (self.maxTargets && data.targets && data.targets.length > self.maxTargets) {
  2393. syncOnFail = false;
  2394. return dfrd.reject({error :['errMaxTargets', self.maxTargets]});
  2395. }
  2396. defdone && dfrd.done(done);
  2397. // quiet abort not completed "open" requests
  2398. if (isOpen) {
  2399. while ((_xhr = queue.pop())) {
  2400. _xhr.queueAbort();
  2401. }
  2402. if (cwd !== data.target) {
  2403. while ((_xhr = cwdQueue.pop())) {
  2404. _xhr.queueAbort();
  2405. }
  2406. }
  2407. }
  2408. // trigger abort autoSync for commands to add the item
  2409. if ($.inArray(cmd, (self.cmdsToAdd + ' autosync').split(' ')) !== -1) {
  2410. if (cmd !== 'autosync') {
  2411. self.autoSync('stop');
  2412. dfrd.always(function() {
  2413. self.autoSync();
  2414. });
  2415. }
  2416. self.trigger('openxhrabort');
  2417. }
  2418. delete options.preventFail;
  2419. if (self.api >= 2.1029) {
  2420. if (useCache) {
  2421. options.headers['X-elFinderReqid'] = reqId;
  2422. } else {
  2423. Object.assign(options.data, { reqid : reqId });
  2424. }
  2425. }
  2426. // function for set value of this syncOnFail
  2427. dfrd.syncOnFail = function(state) {
  2428. syncOnFail = !!state;
  2429. };
  2430. requestCnt++;
  2431. dfrd.xhr = xhr = self.transport.send(options).always(function() {
  2432. // set responseURL from native xhr object
  2433. if (options._xhr && typeof options._xhr.responseURL !== 'undefined') {
  2434. xhr.responseURL = options._xhr.responseURL || '';
  2435. }
  2436. --requestCnt;
  2437. if (requestQueue.length) {
  2438. requestQueue.shift()();
  2439. } else {
  2440. requestQueueSkipOpen = false;
  2441. }
  2442. }).fail(error).done(success);
  2443. if (self.api >= 2.1029) {
  2444. xhr._requestId = reqId;
  2445. }
  2446. if (isOpen || (data.compare && cmd === 'info')) {
  2447. // regist function queueAbort
  2448. xhr.queueAbort = queueAbort;
  2449. // add autoSync xhr into queue
  2450. queue.unshift(xhr);
  2451. // bind abort()
  2452. data.compare && self.bind(self.cmdsToAdd + ' autosync openxhrabort', abort);
  2453. dfrd.always(function() {
  2454. var ndx = $.inArray(xhr, queue);
  2455. data.compare && self.unbind(self.cmdsToAdd + ' autosync openxhrabort', abort);
  2456. ndx !== -1 && queue.splice(ndx, 1);
  2457. });
  2458. } else if ($.inArray(cmd, self.abortCmdsOnOpen) !== -1) {
  2459. // regist function queueAbort
  2460. xhr.queueAbort = queueAbort;
  2461. // add "open" xhr, only cwd xhr into queue
  2462. cwdQueue.unshift(xhr);
  2463. dfrd.always(function() {
  2464. var ndx = $.inArray(xhr, cwdQueue);
  2465. ndx !== -1 && cwdQueue.splice(ndx, 1);
  2466. });
  2467. }
  2468. // abort pending xhr on window unload or elFinder destroy
  2469. self.bind('unload destroy', abort);
  2470. dfrd.always(function() {
  2471. self.unbind('unload destroy', abort);
  2472. });
  2473. return dfrd;
  2474. },
  2475. queueingRequest = function() {
  2476. // show notify
  2477. if (notify.type && notify.cnt) {
  2478. if (cancel) {
  2479. notify.cancel = dfrd;
  2480. opts.eachCancel && (notify.id = +new Date());
  2481. }
  2482. timeout = setTimeout(function() {
  2483. self.notify(notify);
  2484. dfrd.always(function() {
  2485. notify.cnt = -(parseInt(notify.cnt)||0);
  2486. self.notify(notify);
  2487. });
  2488. }, self.notifyDelay);
  2489. dfrd.always(function() {
  2490. clearTimeout(timeout);
  2491. });
  2492. }
  2493. // queueing
  2494. if (isOpen) {
  2495. requestQueueSkipOpen = false;
  2496. }
  2497. if (requestCnt < requestMaxConn) {
  2498. // do request
  2499. return request();
  2500. } else {
  2501. if (isOpen) {
  2502. requestQueue.unshift(request);
  2503. } else {
  2504. requestQueue.push(request);
  2505. }
  2506. return dfrd;
  2507. }
  2508. },
  2509. bindData = {opts: opts, result: true},
  2510. openDir;
  2511. // prevent request initial request is completed
  2512. if (!self.api && !data.init) {
  2513. syncOnFail = false;
  2514. return dfrd.reject();
  2515. }
  2516. // trigger "request.cmd" that callback be able to cancel request by substituting "false" for "event.data.result"
  2517. self.trigger('request.' + cmd, bindData, true);
  2518. if (! bindData.result) {
  2519. self.trigger(cmd + 'done');
  2520. return dfrd.reject();
  2521. } else if (typeof bindData.result === 'object' && bindData.result.promise) {
  2522. bindData.result
  2523. .done(queueingRequest)
  2524. .fail(function() {
  2525. self.trigger(cmd + 'done');
  2526. dfrd.reject();
  2527. });
  2528. return dfrd;
  2529. }
  2530. return queueingRequest();
  2531. };
  2532. /**
  2533. * Call cache()
  2534. * Store info about files/dirs in "files" object.
  2535. *
  2536. * @param Array files
  2537. * @return void
  2538. */
  2539. this.cache = function(dataArray) {
  2540. if (! Array.isArray(dataArray)) {
  2541. dataArray = [ dataArray ];
  2542. }
  2543. cache(dataArray);
  2544. };
  2545. /**
  2546. * Update file object caches by respose data object
  2547. *
  2548. * @param Object respose data object
  2549. * @return void
  2550. */
  2551. this.updateCache = function(data) {
  2552. if ($.isPlainObject(data)) {
  2553. data.files && data.files.length && cache(data.files, 'files');
  2554. data.tree && data.tree.length && cache(data.tree, 'tree');
  2555. data.removed && data.removed.length && remove(data.removed);
  2556. data.added && data.added.length && cache(data.added, 'add');
  2557. data.changed && data.changed.length && change(data.changed, 'change');
  2558. }
  2559. };
  2560. /**
  2561. * Compare current files cache with new files and return diff
  2562. *
  2563. * @param Array new files
  2564. * @param String target folder hash
  2565. * @param Array exclude properties to compare
  2566. * @return Object
  2567. */
  2568. this.diff = function(incoming, onlydir, excludeProps) {
  2569. var raw = {},
  2570. added = [],
  2571. removed = [],
  2572. changed = [],
  2573. excludes = null,
  2574. isChanged = function(hash) {
  2575. var l = changed.length;
  2576. while (l--) {
  2577. if (changed[l].hash == hash) {
  2578. return true;
  2579. }
  2580. }
  2581. };
  2582. $.each(incoming, function(i, f) {
  2583. raw[f.hash] = f;
  2584. });
  2585. // make excludes object
  2586. if (excludeProps && excludeProps.length) {
  2587. excludes = {};
  2588. $.each(excludeProps, function() {
  2589. excludes[this] = true;
  2590. });
  2591. }
  2592. // find removed
  2593. $.each(files, function(hash, f) {
  2594. if (! raw[hash] && (! onlydir || f.phash === onlydir)) {
  2595. removed.push(hash);
  2596. }
  2597. });
  2598. // compare files
  2599. $.each(raw, function(hash, file) {
  2600. var origin = files[hash],
  2601. orgKeys = {},
  2602. chkKeyLen;
  2603. if (!origin) {
  2604. added.push(file);
  2605. } else {
  2606. // make orgKeys object
  2607. $.each(Object.keys(origin), function() {
  2608. orgKeys[this] = true;
  2609. });
  2610. $.each(file, function(prop) {
  2611. delete orgKeys[prop];
  2612. if (! excludes || ! excludes[prop]) {
  2613. if (file[prop] !== origin[prop]) {
  2614. changed.push(file);
  2615. orgKeys = {};
  2616. return false;
  2617. }
  2618. }
  2619. });
  2620. chkKeyLen = Object.keys(orgKeys).length;
  2621. if (chkKeyLen !== 0) {
  2622. if (excludes) {
  2623. $.each(orgKeys, function(prop) {
  2624. if (excludes[prop]) {
  2625. --chkKeyLen;
  2626. }
  2627. });
  2628. }
  2629. (chkKeyLen !== 0) && changed.push(file);
  2630. }
  2631. }
  2632. });
  2633. // parents of removed dirs mark as changed (required for tree correct work)
  2634. $.each(removed, function(i, hash) {
  2635. var file = files[hash],
  2636. phash = file.phash;
  2637. if (phash
  2638. && file.mime == 'directory'
  2639. && $.inArray(phash, removed) === -1
  2640. && raw[phash]
  2641. && !isChanged(phash)) {
  2642. changed.push(raw[phash]);
  2643. }
  2644. });
  2645. return {
  2646. added : added,
  2647. removed : removed,
  2648. changed : changed
  2649. };
  2650. };
  2651. /**
  2652. * Sync content
  2653. *
  2654. * @return jQuery.Deferred
  2655. */
  2656. this.sync = function(onlydir, polling) {
  2657. this.autoSync('stop');
  2658. var self = this,
  2659. compare = function(){
  2660. var c = '', cnt = 0, mtime = 0;
  2661. if (onlydir && polling) {
  2662. $.each(files, function(h, f) {
  2663. if (f.phash && f.phash === onlydir) {
  2664. ++cnt;
  2665. mtime = Math.max(mtime, f.ts);
  2666. }
  2667. c = cnt+':'+mtime;
  2668. });
  2669. }
  2670. return c;
  2671. },
  2672. comp = compare(),
  2673. dfrd = $.Deferred().done(function() { self.trigger('sync'); }),
  2674. opts = [this.request({
  2675. data : {cmd : 'open', reload : 1, target : cwd, tree : (! onlydir && this.ui.tree) ? 1 : 0, compare : comp},
  2676. preventDefault : true
  2677. })],
  2678. exParents = function() {
  2679. var parents = [],
  2680. curRoot = self.file(self.root(cwd)),
  2681. curId = curRoot? curRoot.volumeid : null,
  2682. phash = self.cwd().phash,
  2683. isroot,pdir;
  2684. while(phash) {
  2685. if (pdir = self.file(phash)) {
  2686. if (phash.indexOf(curId) !== 0) {
  2687. parents.push( {target: phash, cmd: 'tree'} );
  2688. if (! self.isRoot(pdir)) {
  2689. parents.push( {target: phash, cmd: 'parents'} );
  2690. }
  2691. curRoot = self.file(self.root(phash));
  2692. curId = curRoot? curRoot.volumeid : null;
  2693. }
  2694. phash = pdir.phash;
  2695. } else {
  2696. phash = null;
  2697. }
  2698. }
  2699. return parents;
  2700. };
  2701. if (! onlydir && self.api >= 2) {
  2702. (cwd !== this.root()) && opts.push(this.request({
  2703. data : {cmd : 'parents', target : cwd},
  2704. preventDefault : true
  2705. }));
  2706. $.each(exParents(), function(i, data) {
  2707. opts.push(self.request({
  2708. data : {cmd : data.cmd, target : data.target},
  2709. preventDefault : true
  2710. }));
  2711. });
  2712. }
  2713. $.when.apply($, opts)
  2714. .fail(function(error, xhr) {
  2715. if (! polling || $.inArray('errOpen', error) !== -1) {
  2716. dfrd.reject(error);
  2717. self.parseError(error) && self.request({
  2718. data : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1},
  2719. notify : {type : 'open', cnt : 1, hideCnt : true}
  2720. });
  2721. } else {
  2722. dfrd.reject((error && xhr.status != 0)? error : void 0);
  2723. }
  2724. })
  2725. .done(function(odata) {
  2726. var pdata, argLen, i;
  2727. if (odata.cwd.compare) {
  2728. if (comp === odata.cwd.compare) {
  2729. return dfrd.reject();
  2730. }
  2731. }
  2732. // for 2nd and more requests
  2733. pdata = {tree : []};
  2734. // results marge of 2nd and more requests
  2735. argLen = arguments.length;
  2736. if (argLen > 1) {
  2737. for(i = 1; i < argLen; i++) {
  2738. if (arguments[i].tree && arguments[i].tree.length) {
  2739. pdata.tree.push.apply(pdata.tree, arguments[i].tree);
  2740. }
  2741. }
  2742. }
  2743. if (self.api < 2.1) {
  2744. if (! pdata.tree) {
  2745. pdata.tree = [];
  2746. }
  2747. pdata.tree.push(odata.cwd);
  2748. }
  2749. // data normalize
  2750. odata = self.normalize(odata);
  2751. if (!self.validResponse('open', odata)) {
  2752. return dfrd.reject((odata.norError || 'errResponse'));
  2753. }
  2754. pdata = self.normalize(pdata);
  2755. if (!self.validResponse('tree', pdata)) {
  2756. return dfrd.reject((pdata.norError || 'errResponse'));
  2757. }
  2758. var diff = self.diff(odata.files.concat(pdata && pdata.tree ? pdata.tree : []), onlydir);
  2759. diff.added.push(odata.cwd);
  2760. self.updateCache(diff);
  2761. // trigger events
  2762. diff.removed.length && self.remove(diff);
  2763. diff.added.length && self.add(diff);
  2764. diff.changed.length && self.change(diff);
  2765. return dfrd.resolve(diff);
  2766. })
  2767. .always(function() {
  2768. self.autoSync();
  2769. });
  2770. return dfrd;
  2771. };
  2772. this.upload = function(files) {
  2773. return this.transport.upload(files, this);
  2774. };
  2775. /**
  2776. * Bind keybord shortcut to keydown event
  2777. *
  2778. * @example
  2779. * elfinder.shortcut({
  2780. * pattern : 'ctrl+a',
  2781. * description : 'Select all files',
  2782. * callback : function(e) { ... },
  2783. * keypress : true|false (bind to keypress instead of keydown)
  2784. * })
  2785. *
  2786. * @param Object shortcut config
  2787. * @return elFinder
  2788. */
  2789. this.shortcut = function(s) {
  2790. var patterns, pattern, code, i, parts;
  2791. if (this.options.allowShortcuts && s.pattern && $.isFunction(s.callback)) {
  2792. patterns = s.pattern.toUpperCase().split(/\s+/);
  2793. for (i= 0; i < patterns.length; i++) {
  2794. pattern = patterns[i];
  2795. parts = pattern.split('+');
  2796. code = (code = parts.pop()).length == 1
  2797. ? (code > 0 ? code : code.charCodeAt(0))
  2798. : (code > 0 ? code : $.ui.keyCode[code]);
  2799. if (code && !shortcuts[pattern]) {
  2800. shortcuts[pattern] = {
  2801. keyCode : code,
  2802. altKey : $.inArray('ALT', parts) != -1,
  2803. ctrlKey : $.inArray('CTRL', parts) != -1,
  2804. shiftKey : $.inArray('SHIFT', parts) != -1,
  2805. type : s.type || 'keydown',
  2806. callback : s.callback,
  2807. description : s.description,
  2808. pattern : pattern
  2809. };
  2810. }
  2811. }
  2812. }
  2813. return this;
  2814. };
  2815. /**
  2816. * Registered shortcuts
  2817. *
  2818. * @type Object
  2819. **/
  2820. this.shortcuts = function() {
  2821. var ret = [];
  2822. $.each(shortcuts, function(i, s) {
  2823. ret.push([s.pattern, self.i18n(s.description)]);
  2824. });
  2825. return ret;
  2826. };
  2827. /**
  2828. * Get/set clipboard content.
  2829. * Return new clipboard content.
  2830. *
  2831. * @example
  2832. * this.clipboard([]) - clean clipboard
  2833. * this.clipboard([{...}, {...}], true) - put 2 files in clipboard and mark it as cutted
  2834. *
  2835. * @param Array new files hashes
  2836. * @param Boolean cut files?
  2837. * @return Array
  2838. */
  2839. this.clipboard = function(hashes, cut) {
  2840. var map = function() { return $.map(clipboard, function(f) { return f.hash; }); };
  2841. if (hashes !== void(0)) {
  2842. clipboard.length && this.trigger('unlockfiles', {files : map()});
  2843. remember = {};
  2844. clipboard = $.map(hashes||[], function(hash) {
  2845. var file = files[hash];
  2846. if (file) {
  2847. remember[hash] = true;
  2848. return {
  2849. hash : hash,
  2850. phash : file.phash,
  2851. name : file.name,
  2852. mime : file.mime,
  2853. read : file.read,
  2854. locked : file.locked,
  2855. cut : !!cut
  2856. };
  2857. }
  2858. return null;
  2859. });
  2860. this.trigger('changeclipboard', {clipboard : clipboard.slice(0, clipboard.length)});
  2861. cut && this.trigger('lockfiles', {files : map()});
  2862. }
  2863. // return copy of clipboard instead of refrence
  2864. return clipboard.slice(0, clipboard.length);
  2865. };
  2866. /**
  2867. * Return true if command enabled
  2868. *
  2869. * @param String command name
  2870. * @param String|void hash for check of own volume's disabled cmds
  2871. * @return Boolean
  2872. */
  2873. this.isCommandEnabled = function(name, dstHash) {
  2874. var disabled, cmd,
  2875. cvid = self.cwd().volumeid || '';
  2876. // In serach results use selected item hash to check
  2877. if (!dstHash && self.searchStatus.state > 1 && self.selected().length) {
  2878. dstHash = self.selected()[0];
  2879. }
  2880. if (dstHash && (! cvid || dstHash.indexOf(cvid) !== 0)) {
  2881. disabled = self.option('disabledFlip', dstHash);
  2882. //if (! disabled) {
  2883. // disabled = {};
  2884. //}
  2885. } else {
  2886. disabled = cwdOptions.disabledFlip/* || {}*/;
  2887. }
  2888. cmd = this._commands[name];
  2889. return cmd ? (cmd.alwaysEnabled || !disabled[name]) : false;
  2890. };
  2891. /**
  2892. * Exec command and return result;
  2893. *
  2894. * @param String command name
  2895. * @param String|Array usualy files hashes
  2896. * @param String|Array command options
  2897. * @param String|void hash for enabled check of own volume's disabled cmds
  2898. * @return $.Deferred
  2899. */
  2900. this.exec = function(cmd, files, opts, dstHash) {
  2901. var dfrd, resType;
  2902. // apply commandMap for keyboard shortcut
  2903. if (!dstHash && this.commandMap[cmd] && this.commandMap[cmd] !== 'hidden') {
  2904. cmd = this.commandMap[cmd];
  2905. }
  2906. if (cmd === 'open') {
  2907. if (this.searchStatus.state || this.searchStatus.ininc) {
  2908. this.trigger('searchend', { noupdate: true });
  2909. }
  2910. this.autoSync('stop');
  2911. }
  2912. if (!dstHash && files) {
  2913. if ($.isArray(files)) {
  2914. if (files.length) {
  2915. dstHash = files[0];
  2916. }
  2917. } else {
  2918. dstHash = files;
  2919. }
  2920. }
  2921. dfrd = this._commands[cmd] && this.isCommandEnabled(cmd, dstHash)
  2922. ? this._commands[cmd].exec(files, opts)
  2923. : $.Deferred().reject('errUnknownCmd');
  2924. resType = typeof dfrd;
  2925. if (!(resType === 'object' && dfrd.promise)) {
  2926. self.debug('warning', '"cmd.exec()" should be returned "$.Deferred" but cmd "' + cmd + '" returned "' + resType + '"');
  2927. dfrd = $.Deferred().resolve();
  2928. }
  2929. this.trigger('exec', { dfrd : dfrd, cmd : cmd, files : files, opts : opts, dstHash : dstHash });
  2930. return dfrd;
  2931. };
  2932. /**
  2933. * Create and return dialog.
  2934. *
  2935. * @param String|DOMElement dialog content
  2936. * @param Object dialog options
  2937. * @return jQuery
  2938. */
  2939. this.dialog = function(content, options) {
  2940. var dialog = $('<div/>').append(content).appendTo(node).elfinderdialog(options, self),
  2941. dnode = dialog.closest('.ui-dialog'),
  2942. resize = function(){
  2943. ! dialog.data('draged') && dialog.is(':visible') && dialog.elfinderdialog('posInit');
  2944. };
  2945. if (dnode.length) {
  2946. self.bind('resize', resize);
  2947. dnode.on('remove', function() {
  2948. self.unbind('resize', resize);
  2949. });
  2950. }
  2951. return dialog;
  2952. };
  2953. /**
  2954. * Create and return toast.
  2955. *
  2956. * @param Object toast options - see ui/toast.js
  2957. * @return jQuery
  2958. */
  2959. this.toast = function(options) {
  2960. return $('<div class="ui-front"/>').appendTo(this.ui.toast).elfindertoast(options || {}, this);
  2961. };
  2962. /**
  2963. * Return UI widget or node
  2964. *
  2965. * @param String ui name
  2966. * @return jQuery
  2967. */
  2968. this.getUI = function(ui) {
  2969. return ui? (this.ui[ui] || $()) : node;
  2970. };
  2971. /**
  2972. * Return elFinder.command instance or instances array
  2973. *
  2974. * @param String command name
  2975. * @return Object | Array
  2976. */
  2977. this.getCommand = function(name) {
  2978. return name === void(0) ? this._commands : this._commands[name];
  2979. };
  2980. /**
  2981. * Resize elfinder node
  2982. *
  2983. * @param String|Number width
  2984. * @param String|Number height
  2985. * @return void
  2986. */
  2987. this.resize = function(w, h) {
  2988. var getMargin = function() {
  2989. var m = node.outerHeight(true) - node.innerHeight(),
  2990. p = node;
  2991. while(p.get(0) !== heightBase.get(0)) {
  2992. p = p.parent();
  2993. m += p.outerHeight(true) - p.innerHeight();
  2994. if (! p.parent().length) {
  2995. // reached the document
  2996. break;
  2997. }
  2998. }
  2999. return m;
  3000. },
  3001. fit = ! node.hasClass('ui-resizable'),
  3002. prv = node.data('resizeSize') || {w: 0, h: 0},
  3003. mt, size = {};
  3004. if (heightBase && heightBase.data('resizeTm')) {
  3005. clearTimeout(heightBase.data('resizeTm'));
  3006. }
  3007. if (typeof h === 'string') {
  3008. if (mt = h.match(/^([0-9.]+)%$/)) {
  3009. // setup heightBase
  3010. if (! heightBase || ! heightBase.length) {
  3011. heightBase = $(window);
  3012. }
  3013. if (! heightBase.data('marginToMyNode')) {
  3014. heightBase.data('marginToMyNode', getMargin());
  3015. }
  3016. if (! heightBase.data('fitToBaseFunc')) {
  3017. heightBase.data('fitToBaseFunc', function(e) {
  3018. var tm = heightBase.data('resizeTm');
  3019. e.preventDefault();
  3020. e.stopPropagation();
  3021. tm && cancelAnimationFrame(tm);
  3022. if (! node.hasClass('elfinder-fullscreen') && (!self.UA.Mobile || heightBase.data('rotated') !== self.UA.Rotated)) {
  3023. heightBase.data('rotated', self.UA.Rotated);
  3024. heightBase.data('resizeTm', requestAnimationFrame(function() {
  3025. self.restoreSize();
  3026. }));
  3027. }
  3028. });
  3029. }
  3030. if (typeof heightBase.data('rotated') === 'undefined') {
  3031. heightBase.data('rotated', self.UA.Rotated);
  3032. }
  3033. h = heightBase.height() * (mt[1] / 100) - heightBase.data('marginToMyNode');
  3034. heightBase.off('resize.' + self.namespace, heightBase.data('fitToBaseFunc'));
  3035. fit && heightBase.on('resize.' + self.namespace, heightBase.data('fitToBaseFunc'));
  3036. }
  3037. }
  3038. node.css({ width : w, height : parseInt(h) });
  3039. size.w = Math.round(node.width());
  3040. size.h = Math.round(node.height());
  3041. node.data('resizeSize', size);
  3042. if (size.w !== prv.w || size.h !== prv.h) {
  3043. node.trigger('resize');
  3044. this.trigger('resize', {width : size.w, height : size.h});
  3045. }
  3046. };
  3047. /**
  3048. * Restore elfinder node size
  3049. *
  3050. * @return elFinder
  3051. */
  3052. this.restoreSize = function() {
  3053. this.resize(width, height);
  3054. };
  3055. this.show = function() {
  3056. node.show();
  3057. this.enable().trigger('show');
  3058. };
  3059. this.hide = function() {
  3060. if (this.options.enableAlways) {
  3061. prevEnabled = enabled;
  3062. enabled = false;
  3063. }
  3064. this.disable();
  3065. this.trigger('hide');
  3066. node.hide();
  3067. };
  3068. /**
  3069. * Lazy execution function
  3070. *
  3071. * @param Object function
  3072. * @param Number delay
  3073. * @param Object options
  3074. * @return Object jQuery.Deferred
  3075. */
  3076. this.lazy = function(func, delay, opts) {
  3077. var busy = function(state) {
  3078. var cnt = node.data('lazycnt'),
  3079. repaint;
  3080. if (state) {
  3081. repaint = node.data('lazyrepaint')? false : opts.repaint;
  3082. if (! cnt) {
  3083. node.data('lazycnt', 1)
  3084. .addClass('elfinder-processing');
  3085. } else {
  3086. node.data('lazycnt', ++cnt);
  3087. }
  3088. if (repaint) {
  3089. node.data('lazyrepaint', true).css('display'); // force repaint
  3090. }
  3091. } else {
  3092. if (cnt && cnt > 1) {
  3093. node.data('lazycnt', --cnt);
  3094. } else {
  3095. repaint = node.data('lazyrepaint');
  3096. node.data('lazycnt', 0)
  3097. .removeData('lazyrepaint')
  3098. .removeClass('elfinder-processing');
  3099. repaint && node.css('display'); // force repaint;
  3100. self.trigger('lazydone');
  3101. }
  3102. }
  3103. },
  3104. dfd = $.Deferred(),
  3105. callFunc = function() {
  3106. dfd.resolve(func.call(dfd));
  3107. busy(false);
  3108. };
  3109. delay = delay || 0;
  3110. opts = opts || {};
  3111. busy(true);
  3112. if (delay) {
  3113. setTimeout(callFunc, delay);
  3114. } else {
  3115. requestAnimationFrame(callFunc);
  3116. }
  3117. return dfd;
  3118. };
  3119. /**
  3120. * Destroy this elFinder instance
  3121. *
  3122. * @return void
  3123. **/
  3124. this.destroy = function() {
  3125. if (node && node[0].elfinder) {
  3126. node.hasClass('elfinder-fullscreen') && self.toggleFullscreen(node);
  3127. this.options.syncStart = false;
  3128. this.autoSync('forcestop');
  3129. this.trigger('destroy').disable();
  3130. clipboard = [];
  3131. selected = [];
  3132. listeners = {};
  3133. shortcuts = {};
  3134. $(window).off('.' + namespace);
  3135. $(document).off('.' + namespace);
  3136. self.trigger = function(){};
  3137. $(beeper).remove();
  3138. node.off()
  3139. .removeData()
  3140. .empty()
  3141. .append(prevContent.contents())
  3142. .attr('class', prevContent.attr('class'))
  3143. .attr('style', prevContent.attr('style'));
  3144. delete node[0].elfinder;
  3145. // restore kept events
  3146. $.each(prevEvents, function(n, arr) {
  3147. $.each(arr, function(i, o) {
  3148. node.on(o.type + (o.namespace? '.'+o.namespace : ''), o.selector, o.handler);
  3149. });
  3150. });
  3151. }
  3152. };
  3153. /**
  3154. * Start or stop auto sync
  3155. *
  3156. * @param String|Bool stop
  3157. * @return void
  3158. */
  3159. this.autoSync = function(mode) {
  3160. var sync;
  3161. if (self.options.sync >= 1000) {
  3162. if (syncInterval) {
  3163. clearTimeout(syncInterval);
  3164. syncInterval = null;
  3165. self.trigger('autosync', {action : 'stop'});
  3166. }
  3167. if (mode === 'stop') {
  3168. ++autoSyncStop;
  3169. } else {
  3170. autoSyncStop = Math.max(0, --autoSyncStop);
  3171. }
  3172. if (autoSyncStop || mode === 'forcestop' || ! self.options.syncStart) {
  3173. return;
  3174. }
  3175. // run interval sync
  3176. sync = function(start){
  3177. var timeout;
  3178. if (cwdOptions.syncMinMs && (start || syncInterval)) {
  3179. start && self.trigger('autosync', {action : 'start'});
  3180. timeout = Math.max(self.options.sync, cwdOptions.syncMinMs);
  3181. syncInterval && clearTimeout(syncInterval);
  3182. syncInterval = setTimeout(function() {
  3183. var dosync = true, hash = cwd, cts;
  3184. if (cwdOptions.syncChkAsTs && files[hash] && (cts = files[hash].ts)) {
  3185. self.request({
  3186. data : {cmd : 'info', targets : [hash], compare : cts, reload : 1},
  3187. preventDefault : true
  3188. })
  3189. .done(function(data){
  3190. var ts;
  3191. dosync = true;
  3192. if (data.compare) {
  3193. ts = data.compare;
  3194. if (ts == cts) {
  3195. dosync = false;
  3196. }
  3197. }
  3198. if (dosync) {
  3199. self.sync(hash).always(function(){
  3200. if (ts) {
  3201. // update ts for cache clear etc.
  3202. files[hash].ts = ts;
  3203. }
  3204. sync();
  3205. });
  3206. } else {
  3207. sync();
  3208. }
  3209. })
  3210. .fail(function(error, xhr){
  3211. var err = self.parseError(error);
  3212. if (err && xhr.status != 0) {
  3213. self.error(err);
  3214. if (Array.isArray(err) && $.inArray('errOpen', err) !== -1) {
  3215. self.request({
  3216. data : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1},
  3217. notify : {type : 'open', cnt : 1, hideCnt : true}
  3218. });
  3219. }
  3220. } else {
  3221. syncInterval = setTimeout(function() {
  3222. sync();
  3223. }, timeout);
  3224. }
  3225. });
  3226. } else {
  3227. self.sync(cwd, true).always(function(){
  3228. sync();
  3229. });
  3230. }
  3231. }, timeout);
  3232. }
  3233. };
  3234. sync(true);
  3235. }
  3236. };
  3237. /**
  3238. * Return bool is inside work zone of specific point
  3239. *
  3240. * @param Number event.pageX
  3241. * @param Number event.pageY
  3242. * @return Bool
  3243. */
  3244. this.insideWorkzone = function(x, y, margin) {
  3245. var rectangle = this.getUI('workzone').data('rectangle');
  3246. margin = margin || 1;
  3247. if (x < rectangle.left + margin
  3248. || x > rectangle.left + rectangle.width + margin
  3249. || y < rectangle.top + margin
  3250. || y > rectangle.top + rectangle.height + margin) {
  3251. return false;
  3252. }
  3253. return true;
  3254. };
  3255. /**
  3256. * Target ui node move to last of children of elFinder node fot to show front
  3257. *
  3258. * @param Object target Target jQuery node object
  3259. */
  3260. this.toFront = function(target) {
  3261. var nodes = node.children('.ui-front').removeClass('elfinder-frontmost'),
  3262. lastnode = nodes.last();
  3263. nodes.css('z-index', '');
  3264. $(target).addClass('ui-front elfinder-frontmost').css('z-index', lastnode.css('z-index') + 1);
  3265. };
  3266. /**
  3267. * Remove class 'elfinder-frontmost' and hide() to target ui node
  3268. *
  3269. * @param Object target Target jQuery node object
  3270. * @param Boolean nohide Do not hide
  3271. */
  3272. this.toHide =function(target, nohide) {
  3273. var tgt = $(target),
  3274. last;
  3275. !nohide && tgt.hide();
  3276. if (tgt.hasClass('elfinder-frontmost')) {
  3277. tgt.removeClass('elfinder-frontmost');
  3278. last = node.children('.ui-front:visible:not(.elfinder-frontmost)').last();
  3279. if (last.length) {
  3280. requestAnimationFrame(function() {
  3281. if (!node.children('.elfinder-frontmost:visible').length) {
  3282. self.toFront(last);
  3283. last.trigger('frontmost');
  3284. }
  3285. });
  3286. }
  3287. }
  3288. };
  3289. /**
  3290. * Return css object for maximize
  3291. *
  3292. * @return Object
  3293. */
  3294. this.getMaximizeCss = function() {
  3295. return {
  3296. width : '100%',
  3297. height : '100%',
  3298. margin : 0,
  3299. top : 0,
  3300. left : 0,
  3301. display : 'block',
  3302. position: 'fixed',
  3303. zIndex : Math.max(self.zIndex? (self.zIndex + 1) : 0 , 1000),
  3304. maxWidth : '',
  3305. maxHeight: ''
  3306. };
  3307. };
  3308. // Closure for togglefullscreen
  3309. (function() {
  3310. // check is in iframe
  3311. if (inFrame && self.UA.Fullscreen) {
  3312. self.UA.Fullscreen = false;
  3313. if (parentIframe && typeof parentIframe.attr('allowfullscreen') !== 'undefined') {
  3314. self.UA.Fullscreen = true;
  3315. }
  3316. }
  3317. var orgStyle, bodyOvf, resizeTm, fullElm, exitFull, toFull, funcObj,
  3318. cls = 'elfinder-fullscreen',
  3319. clsN = 'elfinder-fullscreen-native',
  3320. checkDialog = function() {
  3321. var t = 0,
  3322. l = 0;
  3323. $.each(node.children('.ui-dialog,.ui-draggable'), function(i, d) {
  3324. var $d = $(d),
  3325. pos = $d.position();
  3326. if (pos.top < 0) {
  3327. $d.css('top', t);
  3328. t += 20;
  3329. }
  3330. if (pos.left < 0) {
  3331. $d.css('left', l);
  3332. l += 20;
  3333. }
  3334. });
  3335. },
  3336. setFuncObj = function() {
  3337. var useFullscreen = self.storage('useFullscreen');
  3338. funcObj = self.UA.Fullscreen && (useFullscreen? useFullscreen > 0 : self.options.commandsOptions.fullscreen.mode === 'screen') ? {
  3339. // native full screen mode
  3340. fullElm: function() {
  3341. return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement || null;
  3342. },
  3343. exitFull: function() {
  3344. if (document.exitFullscreen) {
  3345. return document.exitFullscreen();
  3346. } else if (document.webkitExitFullscreen) {
  3347. return document.webkitExitFullscreen();
  3348. } else if (document.mozCancelFullScreen) {
  3349. return document.mozCancelFullScreen();
  3350. } else if (document.msExitFullscreen) {
  3351. return document.msExitFullscreen();
  3352. }
  3353. },
  3354. toFull: function(elem) {
  3355. if (elem.requestFullscreen) {
  3356. return elem.requestFullscreen();
  3357. } else if (elem.webkitRequestFullscreen) {
  3358. return elem.webkitRequestFullscreen();
  3359. } else if (elem.mozRequestFullScreen) {
  3360. return elem.mozRequestFullScreen();
  3361. } else if (elem.msRequestFullscreen) {
  3362. return elem.msRequestFullscreen();
  3363. }
  3364. return false;
  3365. }
  3366. } : {
  3367. // node element maximize mode
  3368. fullElm: function() {
  3369. var full;
  3370. if (node.hasClass(cls)) {
  3371. return node.get(0);
  3372. } else {
  3373. full = node.find('.' + cls);
  3374. if (full.length) {
  3375. return full.get(0);
  3376. }
  3377. }
  3378. return null;
  3379. },
  3380. exitFull: function() {
  3381. var elm;
  3382. $(window).off('resize.' + namespace, resize);
  3383. if (bodyOvf !== void(0)) {
  3384. $('body').css('overflow', bodyOvf);
  3385. }
  3386. bodyOvf = void(0);
  3387. if (orgStyle) {
  3388. elm = orgStyle.elm;
  3389. restoreStyle(elm);
  3390. $(elm).trigger('resize', {fullscreen: 'off'});
  3391. }
  3392. $(window).trigger('resize');
  3393. },
  3394. toFull: function(elem) {
  3395. bodyOvf = $('body').css('overflow') || '';
  3396. $('body').css('overflow', 'hidden');
  3397. $(elem).css(self.getMaximizeCss())
  3398. .addClass(cls)
  3399. .trigger('resize', {fullscreen: 'on'});
  3400. checkDialog();
  3401. $(window).on('resize.' + namespace, resize).trigger('resize');
  3402. return true;
  3403. }
  3404. };
  3405. },
  3406. restoreStyle = function(elem) {
  3407. if (orgStyle && orgStyle.elm == elem) {
  3408. $(elem).removeClass(cls + ' ' + clsN).attr('style', orgStyle.style);
  3409. orgStyle = null;
  3410. }
  3411. },
  3412. resize = function(e) {
  3413. var elm;
  3414. if (e.target === window) {
  3415. resizeTm && cancelAnimationFrame(resizeTm);
  3416. resizeTm = requestAnimationFrame(function() {
  3417. if (elm = funcObj.fullElm()) {
  3418. $(elm).trigger('resize', {fullscreen: 'on'});
  3419. }
  3420. });
  3421. }
  3422. };
  3423. setFuncObj();
  3424. $(document).on('fullscreenchange.' + namespace + ' webkitfullscreenchange.' + namespace + ' mozfullscreenchange.' + namespace + ' MSFullscreenChange.' + namespace, function(e){
  3425. if (self.UA.Fullscreen) {
  3426. var elm = funcObj.fullElm(),
  3427. win = $(window);
  3428. resizeTm && cancelAnimationFrame(resizeTm);
  3429. if (elm === null) {
  3430. win.off('resize.' + namespace, resize);
  3431. if (orgStyle) {
  3432. elm = orgStyle.elm;
  3433. restoreStyle(elm);
  3434. $(elm).trigger('resize', {fullscreen: 'off'});
  3435. }
  3436. } else {
  3437. $(elm).addClass(cls + ' ' + clsN)
  3438. .attr('style', 'width:100%; height:100%; margin:0; padding:0;')
  3439. .trigger('resize', {fullscreen: 'on'});
  3440. win.on('resize.' + namespace, resize);
  3441. checkDialog();
  3442. }
  3443. win.trigger('resize');
  3444. }
  3445. });
  3446. /**
  3447. * Toggle Full Scrren Mode
  3448. *
  3449. * @param Object target
  3450. * @param Bool full
  3451. * @return Object | Null DOM node object of current full scrren
  3452. */
  3453. self.toggleFullscreen = function(target, full) {
  3454. var elm = $(target).get(0),
  3455. curElm = null;
  3456. curElm = funcObj.fullElm();
  3457. if (curElm) {
  3458. if (curElm == elm) {
  3459. if (full === true) {
  3460. return curElm;
  3461. }
  3462. } else {
  3463. if (full === false) {
  3464. return curElm;
  3465. }
  3466. }
  3467. funcObj.exitFull();
  3468. return null;
  3469. } else {
  3470. if (full === false) {
  3471. return null;
  3472. }
  3473. }
  3474. setFuncObj();
  3475. orgStyle = {elm: elm, style: $(elm).attr('style')};
  3476. if (funcObj.toFull(elm) !== false) {
  3477. return elm;
  3478. } else {
  3479. orgStyle = null;
  3480. return null;
  3481. }
  3482. };
  3483. })();
  3484. // Closure for toggleMaximize
  3485. (function(){
  3486. var cls = 'elfinder-maximized',
  3487. resizeTm,
  3488. resize = function(e) {
  3489. if (e.target === window && e.data && e.data.elm) {
  3490. var elm = e.data.elm;
  3491. resizeTm && cancelAnimationFrame(resizeTm);
  3492. resizeTm = requestAnimationFrame(function() {
  3493. elm.trigger('resize', {maximize: 'on'});
  3494. });
  3495. }
  3496. },
  3497. exitMax = function(elm) {
  3498. $(window).off('resize.' + namespace, resize);
  3499. $('body').css('overflow', elm.data('bodyOvf'));
  3500. elm.removeClass(cls)
  3501. .attr('style', elm.data('orgStyle'))
  3502. .removeData('bodyOvf')
  3503. .removeData('orgStyle');
  3504. elm.trigger('resize', {maximize: 'off'});
  3505. },
  3506. toMax = function(elm) {
  3507. elm.data('bodyOvf', $('body').css('overflow') || '')
  3508. .data('orgStyle', elm.attr('style'))
  3509. .addClass(cls)
  3510. .css(self.getMaximizeCss());
  3511. $('body').css('overflow', 'hidden');
  3512. $(window).on('resize.' + namespace, {elm: elm}, resize);
  3513. elm.trigger('resize', {maximize: 'on'});
  3514. };
  3515. /**
  3516. * Toggle Maximize target node
  3517. *
  3518. * @param Object target
  3519. * @param Bool max
  3520. * @return void
  3521. */
  3522. self.toggleMaximize = function(target, max) {
  3523. var elm = $(target),
  3524. maximized = elm.hasClass(cls);
  3525. if (maximized) {
  3526. if (max === true) {
  3527. return;
  3528. }
  3529. exitMax(elm);
  3530. } else {
  3531. if (max === false) {
  3532. return;
  3533. }
  3534. toMax(elm);
  3535. }
  3536. };
  3537. })();
  3538. /************* init stuffs ****************/
  3539. Object.assign($.ui.keyCode, {
  3540. 'F1' : 112,
  3541. 'F2' : 113,
  3542. 'F3' : 114,
  3543. 'F4' : 115,
  3544. 'F5' : 116,
  3545. 'F6' : 117,
  3546. 'F7' : 118,
  3547. 'F8' : 119,
  3548. 'F9' : 120,
  3549. 'F10' : 121,
  3550. 'F11' : 122,
  3551. 'F12' : 123,
  3552. 'DIG0' : 48,
  3553. 'DIG1' : 49,
  3554. 'DIG2' : 50,
  3555. 'DIG3' : 51,
  3556. 'DIG4' : 52,
  3557. 'DIG5' : 53,
  3558. 'DIG6' : 54,
  3559. 'DIG7' : 55,
  3560. 'DIG8' : 56,
  3561. 'DIG9' : 57,
  3562. 'NUM0' : 96,
  3563. 'NUM1' : 97,
  3564. 'NUM2' : 98,
  3565. 'NUM3' : 99,
  3566. 'NUM4' : 100,
  3567. 'NUM5' : 101,
  3568. 'NUM6' : 102,
  3569. 'NUM7' : 103,
  3570. 'NUM8' : 104,
  3571. 'NUM9' : 105,
  3572. 'CONTEXTMENU' : 93,
  3573. 'DOT' : 190
  3574. });
  3575. this.dragUpload = false;
  3576. this.xhrUpload = (typeof XMLHttpRequestUpload != 'undefined' || typeof XMLHttpRequestEventTarget != 'undefined') && typeof File != 'undefined' && typeof FormData != 'undefined';
  3577. // configure transport object
  3578. this.transport = {};
  3579. if (typeof(this.options.transport) == 'object') {
  3580. this.transport = this.options.transport;
  3581. if (typeof(this.transport.init) == 'function') {
  3582. this.transport.init(this);
  3583. }
  3584. }
  3585. if (typeof(this.transport.send) != 'function') {
  3586. this.transport.send = function(opts) {
  3587. if (!self.UA.IE) {
  3588. // keep native xhr object for handling property responseURL
  3589. opts._xhr = new XMLHttpRequest();
  3590. opts.xhr = function() { return opts._xhr; };
  3591. }
  3592. return $.ajax(opts);
  3593. };
  3594. }
  3595. if (this.transport.upload == 'iframe') {
  3596. this.transport.upload = $.proxy(this.uploads.iframe, this);
  3597. } else if (typeof(this.transport.upload) == 'function') {
  3598. this.dragUpload = !!this.options.dragUploadAllow;
  3599. } else if (this.xhrUpload && !!this.options.dragUploadAllow) {
  3600. this.transport.upload = $.proxy(this.uploads.xhr, this);
  3601. this.dragUpload = true;
  3602. } else {
  3603. this.transport.upload = $.proxy(this.uploads.iframe, this);
  3604. }
  3605. /**
  3606. * Decoding 'raw' string converted to unicode
  3607. *
  3608. * @param String str
  3609. * @return String
  3610. */
  3611. this.decodeRawString = function(str) {
  3612. var charCodes = function(str) {
  3613. var i, len, arr;
  3614. for (i=0,len=str.length,arr=[]; i<len; i++) {
  3615. arr.push(str.charCodeAt(i));
  3616. }
  3617. return arr;
  3618. },
  3619. scalarValues = function(arr) {
  3620. var scalars = [], i, len, c;
  3621. if (typeof arr === 'string') {arr = charCodes(arr);}
  3622. for (i=0,len=arr.length; c=arr[i],i<len; i++) {
  3623. if (c >= 0xd800 && c <= 0xdbff) {
  3624. scalars.push((c & 1023) + 64 << 10 | arr[++i] & 1023);
  3625. } else {
  3626. scalars.push(c);
  3627. }
  3628. }
  3629. return scalars;
  3630. },
  3631. decodeUTF8 = function(arr) {
  3632. var i, len, c, str, char = String.fromCharCode;
  3633. for (i=0,len=arr.length,str=""; c=arr[i],i<len; i++) {
  3634. if (c <= 0x7f) {
  3635. str += char(c);
  3636. } else if (c <= 0xdf && c >= 0xc2) {
  3637. str += char((c&31)<<6 | arr[++i]&63);
  3638. } else if (c <= 0xef && c >= 0xe0) {
  3639. str += char((c&15)<<12 | (arr[++i]&63)<<6 | arr[++i]&63);
  3640. } else if (c <= 0xf7 && c >= 0xf0) {
  3641. str += char(
  3642. 0xd800 | ((c&7)<<8 | (arr[++i]&63)<<2 | arr[++i]>>>4&3) - 64,
  3643. 0xdc00 | (arr[i++]&15)<<6 | arr[i]&63
  3644. );
  3645. } else {
  3646. str += char(0xfffd);
  3647. }
  3648. }
  3649. return str;
  3650. };
  3651. return decodeUTF8(scalarValues(str));
  3652. };
  3653. /**
  3654. * Gets target file contents by file.hash
  3655. *
  3656. * @param String hash The hash
  3657. * @param String responseType 'blob' or 'arraybuffer' (default)
  3658. * @return arraybuffer|blob The contents.
  3659. */
  3660. this.getContents = function(hash, responseType) {
  3661. var self = this,
  3662. dfd = $.Deferred(),
  3663. type = responseType || 'arraybuffer',
  3664. url, req;
  3665. dfd.fail(function() {
  3666. req && req.state() === 'pending' && req.reject();
  3667. });
  3668. url = self.openUrl(hash);
  3669. if (!self.isSameOrigin(url)) {
  3670. url = self.openUrl(hash, true);
  3671. }
  3672. req = self.request({
  3673. data : {cmd : 'get'},
  3674. options : {
  3675. url: url,
  3676. type: 'get',
  3677. cache : true,
  3678. dataType : 'binary',
  3679. responseType : type,
  3680. processData: false
  3681. }
  3682. })
  3683. .fail(function() {
  3684. dfd.reject();
  3685. })
  3686. .done(function(data) {
  3687. dfd.resolve(data);
  3688. });
  3689. return dfd;
  3690. };
  3691. this.getMimetype = function(name, orgMime) {
  3692. var mime = orgMime,
  3693. ext, m;
  3694. m = (name + '').match(/\.([^.]+)$/);
  3695. if (m && (ext = m[1])) {
  3696. if (!extToMimeTable) {
  3697. extToMimeTable = self.arrayFlip(self.mimeTypes);
  3698. }
  3699. if (!(mime = extToMimeTable[ext.toLowerCase()])) {
  3700. mime = orgMime;
  3701. }
  3702. }
  3703. return mime;
  3704. };
  3705. /**
  3706. * Supported check hash algorisms
  3707. *
  3708. * @type Array
  3709. */
  3710. self.hashCheckers = [];
  3711. /**
  3712. * Closure of getContentsHashes()
  3713. */
  3714. (function(self) {
  3715. var hashLibs = {
  3716. check : true
  3717. },
  3718. md5Calc = function(arr) {
  3719. var spark = new hashLibs.SparkMD5.ArrayBuffer(),
  3720. job;
  3721. job = self.asyncJob(function(buf) {
  3722. spark.append(buf);
  3723. }, arr).done(function() {
  3724. job._md5 = spark.end();
  3725. });
  3726. return job;
  3727. },
  3728. shaCalc = function(arr, length) {
  3729. var sha, job;
  3730. try {
  3731. sha = new hashLibs.jsSHA('SHA' + (length.substr(0, 1) === '3'? length : ('-' + length)), 'ARRAYBUFFER');
  3732. job = self.asyncJob(function(buf) {
  3733. sha.update(buf);
  3734. }, arr).done(function() {
  3735. job._sha = sha.getHash('HEX');
  3736. });
  3737. } catch(e) {
  3738. job = $.Deferred.reject();
  3739. }
  3740. return job;
  3741. };
  3742. // make fm.hashCheckers
  3743. if (self.options.cdns.sparkmd5) {
  3744. self.hashCheckers.push('md5');
  3745. }
  3746. if (self.options.cdns.jssha) {
  3747. self.hashCheckers = self.hashCheckers.concat(['sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'shake128', 'shake256']);
  3748. }
  3749. /**
  3750. * Gets the contents hashes.
  3751. *
  3752. * @param String target target file.hash
  3753. * @param Object needHashes need hash lib names
  3754. * @return Object hashes with lib name as key
  3755. */
  3756. self.getContentsHashes = function(target, needHashes) {
  3757. var dfd = $.Deferred(),
  3758. needs = self.arrayFlip(needHashes || ['md5'], true),
  3759. libs = [],
  3760. jobs = [],
  3761. res = {},
  3762. req;
  3763. dfd.fail(function() {
  3764. req && req.reject();
  3765. });
  3766. if (hashLibs.check) {
  3767. delete hashLibs.check;
  3768. // load SparkMD5
  3769. var libsmd5 = $.Deferred();
  3770. if (window.ArrayBuffer && self.options.cdns.sparkmd5) {
  3771. libs.push(libsmd5);
  3772. self.loadScript([self.options.cdns.sparkmd5],
  3773. function(res) {
  3774. var SparkMD5 = res || window.SparkMD5;
  3775. window.SparkMD5 && delete window.SparkMD5;
  3776. libsmd5.resolve();
  3777. if (SparkMD5) {
  3778. hashLibs.SparkMD5 = SparkMD5;
  3779. }
  3780. },
  3781. {
  3782. tryRequire: true,
  3783. error: function() {
  3784. libsmd5.reject();
  3785. }
  3786. }
  3787. );
  3788. }
  3789. // load jsSha
  3790. var libssha = $.Deferred();
  3791. if (window.ArrayBuffer && self.options.cdns.jssha) {
  3792. libs.push(libssha);
  3793. self.loadScript([self.options.cdns.jssha],
  3794. function(res) {
  3795. var jsSHA = res || window.jsSHA;
  3796. window.jsSHA && delete window.jsSHA;
  3797. libssha.resolve();
  3798. if (jsSHA) {
  3799. hashLibs.jsSHA = jsSHA;
  3800. }
  3801. },
  3802. {
  3803. tryRequire: true,
  3804. error: function() {
  3805. libssha.reject();
  3806. }
  3807. }
  3808. );
  3809. }
  3810. }
  3811. $.when.apply(null, libs).always(function() {
  3812. if (Object.keys(hashLibs).length) {
  3813. req = self.getContents(target).done(function(arrayBuffer) {
  3814. var arr = (arrayBuffer instanceof ArrayBuffer && arrayBuffer.byteLength > 0)? self.sliceArrayBuffer(arrayBuffer, 1048576) : false,
  3815. i;
  3816. if (needs.md5 && hashLibs.SparkMD5) {
  3817. jobs.push(function() {
  3818. var job = md5Calc(arr).done(function() {
  3819. var f;
  3820. res.md5 = job._md5;
  3821. if (f = self.file(target)) {
  3822. f.md5 = job._md5;
  3823. }
  3824. dfd.notify(res);
  3825. });
  3826. dfd.fail(function() {
  3827. job.reject();
  3828. });
  3829. return job;
  3830. });
  3831. }
  3832. if (hashLibs.jsSHA) {
  3833. $.each(['1', '224', '256', '384', '512', '3-224', '3-256', '3-384', '3-512', 'ke128', 'ke256'], function(i, v) {
  3834. if (needs['sha' + v]) {
  3835. jobs.push(function() {
  3836. var job = shaCalc(arr, v).done(function() {
  3837. var f;
  3838. res['sha' + v] = job._sha;
  3839. if (f = self.file(target)) {
  3840. f['sha' + v] = job._sha;
  3841. }
  3842. dfd.notify(res);
  3843. });
  3844. return job;
  3845. });
  3846. }
  3847. });
  3848. }
  3849. if (jobs.length) {
  3850. self.sequence(jobs).always(function() {
  3851. dfd.resolve(res);
  3852. });
  3853. } else {
  3854. dfd.reject();
  3855. }
  3856. }).fail(function() {
  3857. dfd.reject();
  3858. });
  3859. } else {
  3860. dfd.reject();
  3861. }
  3862. });
  3863. return dfd;
  3864. };
  3865. })(this);
  3866. /**
  3867. * Parse error value to display
  3868. *
  3869. * @param Mixed error
  3870. * @return Mixed parsed error
  3871. */
  3872. this.parseError = function(error) {
  3873. var arg = error;
  3874. if ($.isPlainObject(arg)) {
  3875. arg = arg.error;
  3876. }
  3877. return arg;
  3878. };
  3879. /**
  3880. * Alias for this.trigger('error', {error : 'message'})
  3881. *
  3882. * @param String error message
  3883. * @return elFinder
  3884. **/
  3885. this.error = function() {
  3886. var arg = arguments[0],
  3887. opts = arguments[1] || null,
  3888. err;
  3889. if (arguments.length == 1 && typeof(arg) === 'function') {
  3890. return self.bind('error', arg);
  3891. } else {
  3892. err = this.parseError(arg);
  3893. return (err === true || !err)? this : self.trigger('error', {error: err, opts : opts});
  3894. }
  3895. };
  3896. // create bind/trigger aliases for build-in events
  3897. $.each(events, function(i, name) {
  3898. self[name] = function() {
  3899. var arg = arguments[0];
  3900. return arguments.length == 1 && typeof(arg) == 'function'
  3901. ? self.bind(name, arg)
  3902. : self.trigger(name, $.isPlainObject(arg) ? arg : {});
  3903. };
  3904. });
  3905. // bind core event handlers
  3906. this
  3907. .enable(function() {
  3908. if (!enabled && self.api && self.visible() && self.ui.overlay.is(':hidden') && ! node.children('.elfinder-dialog.' + self.res('class', 'editing') + ':visible').length) {
  3909. enabled = true;
  3910. document.activeElement && document.activeElement.blur();
  3911. node.removeClass('elfinder-disabled');
  3912. }
  3913. })
  3914. .disable(function() {
  3915. prevEnabled = enabled;
  3916. enabled = false;
  3917. node.addClass('elfinder-disabled');
  3918. })
  3919. .open(function() {
  3920. selected = [];
  3921. })
  3922. .select(function(e) {
  3923. var cnt = 0,
  3924. unselects = [];
  3925. selected = $.grep(e.data.selected || e.data.value|| [], function(hash) {
  3926. if (unselects.length || (self.maxTargets && ++cnt > self.maxTargets)) {
  3927. unselects.push(hash);
  3928. return false;
  3929. } else {
  3930. return files[hash] ? true : false;
  3931. }
  3932. });
  3933. if (unselects.length) {
  3934. self.trigger('unselectfiles', {files: unselects, inselect: true});
  3935. self.toast({mode: 'warning', msg: self.i18n(['errMaxTargets', self.maxTargets])});
  3936. }
  3937. })
  3938. .error(function(e) {
  3939. var opts = {
  3940. cssClass : 'elfinder-dialog-error',
  3941. title : self.i18n('error'),
  3942. resizable : false,
  3943. destroyOnClose : true,
  3944. buttons : {}
  3945. },
  3946. node = self.getUI(),
  3947. cnt = node.children('.elfinder-dialog-error').length,
  3948. last, counter;
  3949. if (cnt < self.options.maxErrorDialogs) {
  3950. opts.buttons[self.i18n(self.i18n('btnClose'))] = function() { $(this).elfinderdialog('close'); };
  3951. if (e.data.opts && $.isPlainObject(e.data.opts)) {
  3952. Object.assign(opts, e.data.opts);
  3953. }
  3954. self.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-error"/>'+self.i18n(e.data.error), opts);
  3955. } else {
  3956. last = node.children('.elfinder-dialog-error:last').children('.ui-dialog-content:first');
  3957. counter = last.children('.elfinder-error-counter');
  3958. if (counter.length) {
  3959. counter.data('cnt', parseInt(counter.data('cnt')) + 1).html(self.i18n(['moreErrors', counter.data('cnt')]));
  3960. } else {
  3961. counter = $('<span class="elfinder-error-counter">'+ self.i18n(['moreErrors', 1]) +'</span>').data('cnt', 1);
  3962. last.append('<br/>', counter);
  3963. }
  3964. }
  3965. })
  3966. .bind('tmb', function(e) {
  3967. $.each(e.data.images||[], function(hash, tmb) {
  3968. if (files[hash]) {
  3969. files[hash].tmb = tmb;
  3970. }
  3971. });
  3972. })
  3973. .bind('searchstart', function(e) {
  3974. Object.assign(self.searchStatus, e.data);
  3975. self.searchStatus.state = 1;
  3976. })
  3977. .bind('search', function(e) {
  3978. self.searchStatus.state = 2;
  3979. })
  3980. .bind('searchend', function() {
  3981. self.searchStatus.state = 0;
  3982. self.searchStatus.ininc = false;
  3983. self.searchStatus.mixed = false;
  3984. })
  3985. .bind('canMakeEmptyFile', function(e) {
  3986. var data = e.data,
  3987. obj = {};
  3988. if (data && Array.isArray(data.mimes)) {
  3989. if (!data.unshift) {
  3990. obj = self.mimesCanMakeEmpty;
  3991. }
  3992. $.each(data.mimes, function() {
  3993. if (!obj[this]) {
  3994. obj[this] = self.mimeTypes[this];
  3995. }
  3996. });
  3997. if (data.unshift) {
  3998. self.mimesCanMakeEmpty = Object.assign(obj, self.mimesCanMakeEmpty);
  3999. }
  4000. }
  4001. })
  4002. .bind('themechange', function() {
  4003. requestAnimationFrame(function() {
  4004. self.trigger('uiresize');
  4005. });
  4006. })
  4007. ;
  4008. // We listen and emit a sound on delete according to option
  4009. if (true === this.options.sound) {
  4010. this.bind('playsound', function(e) {
  4011. var play = beeper.canPlayType && beeper.canPlayType('audio/wav; codecs="1"'),
  4012. file = e.data && e.data.soundFile;
  4013. play && file && play != '' && play != 'no' && $(beeper).html('<source src="' + soundPath + file + '" type="audio/wav">')[0].play();
  4014. });
  4015. }
  4016. // bind external event handlers
  4017. $.each(this.options.handlers, function(event, callback) {
  4018. self.bind(event, callback);
  4019. });
  4020. /**
  4021. * History object. Store visited folders
  4022. *
  4023. * @type Object
  4024. **/
  4025. this.history = new this.history(this);
  4026. /**
  4027. * Root hashed
  4028. *
  4029. * @type Object
  4030. */
  4031. this.roots = {};
  4032. /**
  4033. * leaf roots
  4034. *
  4035. * @type Object
  4036. */
  4037. this.leafRoots = {};
  4038. this.volumeExpires = {};
  4039. /**
  4040. * Loaded commands
  4041. *
  4042. * @type Object
  4043. **/
  4044. this._commands = {};
  4045. if (!Array.isArray(this.options.commands)) {
  4046. this.options.commands = [];
  4047. }
  4048. if ($.inArray('*', this.options.commands) !== -1) {
  4049. this.options.commands = Object.keys(this.commands);
  4050. }
  4051. /**
  4052. * UI command map of cwd volume ( That volume driver option `uiCmdMap` )
  4053. *
  4054. * @type Object
  4055. **/
  4056. this.commandMap = {};
  4057. /**
  4058. * cwd options of each volume
  4059. * key: volumeid
  4060. * val: options object
  4061. *
  4062. * @type Object
  4063. */
  4064. this.volOptions = {};
  4065. /**
  4066. * Has volOptions data
  4067. *
  4068. * @type Boolean
  4069. */
  4070. this.hasVolOptions = false;
  4071. /**
  4072. * Hash of trash holders
  4073. * key: trash folder hash
  4074. * val: source volume hash
  4075. *
  4076. * @type Object
  4077. */
  4078. this.trashes = {};
  4079. /**
  4080. * cwd options of each folder/file
  4081. * key: hash
  4082. * val: options object
  4083. *
  4084. * @type Object
  4085. */
  4086. this.optionsByHashes = {};
  4087. /**
  4088. * UI Auto Hide Functions
  4089. * Each auto hide function mast be call to `fm.trigger('uiautohide')` at end of process
  4090. *
  4091. * @type Array
  4092. **/
  4093. this.uiAutoHide = [];
  4094. // trigger `uiautohide`
  4095. this.one('open', function() {
  4096. if (self.uiAutoHide.length) {
  4097. setTimeout(function() {
  4098. self.trigger('uiautohide');
  4099. }, 500);
  4100. }
  4101. });
  4102. // Auto Hide Functions sequential processing start
  4103. this.bind('uiautohide', function() {
  4104. if (self.uiAutoHide.length) {
  4105. self.uiAutoHide.shift()();
  4106. }
  4107. });
  4108. if (this.options.width) {
  4109. width = this.options.width;
  4110. }
  4111. if (this.options.height) {
  4112. height = this.options.height;
  4113. }
  4114. if (this.options.heightBase) {
  4115. heightBase = $(this.options.heightBase);
  4116. }
  4117. if (this.options.soundPath) {
  4118. soundPath = this.options.soundPath.replace(/\/+$/, '') + '/';
  4119. } else {
  4120. soundPath = this.baseUrl + soundPath;
  4121. }
  4122. self.one('opendone', function() {
  4123. var tm;
  4124. // attach events to document
  4125. $(document)
  4126. // disable elfinder on click outside elfinder
  4127. .on('click.'+namespace, function(e) { enabled && ! self.options.enableAlways && !$(e.target).closest(node).length && self.disable(); })
  4128. // exec shortcuts
  4129. .on(keydown+' '+keypress+' '+keyup+' '+mousedown, execShortcut);
  4130. // attach events to window
  4131. self.options.useBrowserHistory && $(window)
  4132. .on('popstate.' + namespace, function(ev) {
  4133. var state = ev.originalEvent.state || {},
  4134. hasThash = state.thash? true : false,
  4135. dialog = node.find('.elfinder-frontmost:visible'),
  4136. input = node.find('.elfinder-navbar-dir,.elfinder-cwd-filename').find('input,textarea'),
  4137. onOpen, toast;
  4138. if (!hasThash) {
  4139. state = { thash: self.cwd().hash };
  4140. // scroll to elFinder node
  4141. $('html,body').animate({ scrollTop: node.offset().top });
  4142. }
  4143. if (dialog.length || input.length) {
  4144. history.pushState(state, null, location.pathname + location.search + '#elf_' + state.thash);
  4145. if (dialog.length) {
  4146. if (!dialog.hasClass(self.res('class', 'preventback'))) {
  4147. if (dialog.hasClass('elfinder-contextmenu')) {
  4148. $(document).trigger($.Event('keydown', { keyCode: $.ui.keyCode.ESCAPE, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  4149. } else if (dialog.hasClass('elfinder-dialog')) {
  4150. dialog.elfinderdialog('close');
  4151. } else {
  4152. dialog.trigger('close');
  4153. }
  4154. }
  4155. } else {
  4156. input.trigger($.Event('keydown', { keyCode: $.ui.keyCode.ESCAPE, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  4157. }
  4158. } else {
  4159. if (hasThash) {
  4160. !$.isEmptyObject(self.files()) && self.request({
  4161. data : {cmd : 'open', target : state.thash, onhistory : 1},
  4162. notify : {type : 'open', cnt : 1, hideCnt : true},
  4163. syncOnFail : true
  4164. });
  4165. } else {
  4166. onOpen = function() {
  4167. toast.trigger('click');
  4168. };
  4169. self.one('open', onOpen, true);
  4170. toast = self.toast({
  4171. msg: self.i18n('pressAgainToExit'),
  4172. onHidden: function() {
  4173. self.unbind('open', onOpen);
  4174. history.pushState(state, null, location.pathname + location.search + '#elf_' + state.thash);
  4175. }
  4176. });
  4177. }
  4178. }
  4179. });
  4180. $(window).on('resize.' + namespace, function(e){
  4181. if (e.target === this) {
  4182. tm && cancelAnimationFrame(tm);
  4183. tm = requestAnimationFrame(function() {
  4184. var prv = node.data('resizeSize') || {w: 0, h: 0},
  4185. size = {w: Math.round(node.width()), h: Math.round(node.height())};
  4186. node.data('resizeSize', size);
  4187. if (size.w !== prv.w || size.h !== prv.h) {
  4188. node.trigger('resize');
  4189. self.trigger('resize', {width : size.w, height : size.h});
  4190. }
  4191. });
  4192. }
  4193. })
  4194. .on('beforeunload.' + namespace,function(e){
  4195. var msg, cnt;
  4196. if (!self.pauseUnloadCheck()) {
  4197. if (node.is(':visible')) {
  4198. if (self.ui.notify.children().length && $.inArray('hasNotifyDialog', self.options.windowCloseConfirm) !== -1) {
  4199. msg = self.i18n('ntfsmth');
  4200. } else if (node.find('.'+self.res('class', 'editing')).length && $.inArray('editingFile', self.options.windowCloseConfirm) !== -1) {
  4201. msg = self.i18n('editingFile');
  4202. } else if ((cnt = Object.keys(self.selected()).length) && $.inArray('hasSelectedItem', self.options.windowCloseConfirm) !== -1) {
  4203. msg = self.i18n('hasSelected', ''+cnt);
  4204. } else if ((cnt = Object.keys(self.clipboard()).length) && $.inArray('hasClipboardData', self.options.windowCloseConfirm) !== -1) {
  4205. msg = self.i18n('hasClipboard', ''+cnt);
  4206. }
  4207. if (msg) {
  4208. e.returnValue = msg;
  4209. return msg;
  4210. }
  4211. }
  4212. self.trigger('unload');
  4213. }
  4214. });
  4215. // bind window onmessage for CORS
  4216. $(window).on('message.' + namespace, function(e){
  4217. var res = e.originalEvent || null,
  4218. obj, data;
  4219. if (res && self.uploadURL.indexOf(res.origin) === 0) {
  4220. try {
  4221. obj = JSON.parse(res.data);
  4222. data = obj.data || null;
  4223. if (data) {
  4224. if (data.error) {
  4225. if (obj.bind) {
  4226. self.trigger(obj.bind+'fail', data);
  4227. }
  4228. self.error(data.error);
  4229. } else {
  4230. data.warning && self.error(data.warning);
  4231. self.updateCache(data);
  4232. data.removed && data.removed.length && self.remove(data);
  4233. data.added && data.added.length && self.add(data);
  4234. data.changed && data.changed.length && self.change(data);
  4235. if (obj.bind) {
  4236. self.trigger(obj.bind, data);
  4237. self.trigger(obj.bind+'done');
  4238. }
  4239. data.sync && self.sync();
  4240. }
  4241. }
  4242. } catch (e) {
  4243. self.sync();
  4244. }
  4245. }
  4246. });
  4247. // elFinder enable always
  4248. if (self.options.enableAlways) {
  4249. $(window).on('focus.' + namespace, function(e){
  4250. (e.target === this) && self.enable();
  4251. });
  4252. if (inFrame) {
  4253. $(window.top).on('focus.' + namespace, function() {
  4254. if (self.enable() && (! parentIframe || parentIframe.is(':visible'))) {
  4255. requestAnimationFrame(function() {
  4256. $(window).trigger('focus');
  4257. });
  4258. }
  4259. });
  4260. }
  4261. } else if (inFrame) {
  4262. $(window).on('blur.' + namespace, function(e){
  4263. enabled && e.target === this && self.disable();
  4264. });
  4265. }
  4266. // return focus to the window on click (elFInder in the frame)
  4267. if (inFrame) {
  4268. node.on('click', function(e) {
  4269. $(window).trigger('focus');
  4270. });
  4271. }
  4272. // elFinder to enable by mouse over
  4273. if (self.options.enableByMouseOver) {
  4274. node.on('mouseenter touchstart', function(e) {
  4275. (inFrame) && $(window).trigger('focus');
  4276. ! self.enabled() && self.enable();
  4277. });
  4278. }
  4279. // When the browser tab turn to foreground/background
  4280. $(window).on('visibilitychange.' + namespace, function(e) {
  4281. var background = document.hidden || document.webkitHidden || document.msHidden;
  4282. // AutoSync turn On/Off
  4283. if (self.options.syncStart) {
  4284. self.autoSync(background? 'stop' : void(0));
  4285. }
  4286. });
  4287. });
  4288. // store instance in node
  4289. node[0].elfinder = this;
  4290. // auto load language file
  4291. dfrdsBeforeBootup.push((function() {
  4292. var lang = self.lang,
  4293. langJs = self.i18nBaseUrl + 'elfinder.' + lang + '.js',
  4294. dfd = $.Deferred().done(function() {
  4295. if (self.i18[lang]) {
  4296. self.lang = lang;
  4297. }
  4298. self.trigger('i18load');
  4299. i18n = self.lang === 'en'
  4300. ? self.i18['en']
  4301. : $.extend(true, {}, self.i18['en'], self.i18[self.lang]);
  4302. });
  4303. if (!self.i18[lang]) {
  4304. self.lang = 'en';
  4305. if (self.hasRequire) {
  4306. require([langJs], function() {
  4307. dfd.resolve();
  4308. }, function() {
  4309. dfd.resolve();
  4310. });
  4311. } else {
  4312. self.loadScript([langJs], function() {
  4313. dfd.resolve();
  4314. }, {
  4315. loadType: 'tag',
  4316. error : function() {
  4317. dfd.resolve();
  4318. }
  4319. });
  4320. }
  4321. } else {
  4322. dfd.resolve();
  4323. }
  4324. return dfd;
  4325. })());
  4326. // elFinder boot up function
  4327. bootUp = function() {
  4328. var columnNames;
  4329. /**
  4330. * i18 messages
  4331. *
  4332. * @type Object
  4333. **/
  4334. self.messages = i18n.messages;
  4335. // check jquery ui
  4336. if (!($.fn.selectable && $.fn.draggable && $.fn.droppable && $.fn.resizable && $.fn.button && $.fn.slider)) {
  4337. return alert(self.i18n('errJqui'));
  4338. }
  4339. // check node
  4340. if (!node.length) {
  4341. return alert(self.i18n('errNode'));
  4342. }
  4343. // check connector url
  4344. if (!self.options.url) {
  4345. return alert(self.i18n('errURL'));
  4346. }
  4347. // column key/name map for fm.getColumnName()
  4348. columnNames = Object.assign({
  4349. name : self.i18n('name'),
  4350. perm : self.i18n('perms'),
  4351. date : self.i18n('modify'),
  4352. size : self.i18n('size'),
  4353. kind : self.i18n('kind'),
  4354. modestr : self.i18n('mode'),
  4355. modeoct : self.i18n('mode'),
  4356. modeboth : self.i18n('mode')
  4357. }, self.options.uiOptions.cwd.listView.columnsCustomName);
  4358. /**
  4359. * Gets the column name of cwd list view
  4360. *
  4361. * @param String key The key
  4362. * @return String The column name.
  4363. */
  4364. self.getColumnName = function(key) {
  4365. return columnNames[key] || self.i18n(key);
  4366. };
  4367. /**
  4368. * Interface direction
  4369. *
  4370. * @type String
  4371. * @default "ltr"
  4372. **/
  4373. self.direction = i18n.direction;
  4374. /**
  4375. * Date/time format
  4376. *
  4377. * @type String
  4378. * @default "m.d.Y"
  4379. **/
  4380. self.dateFormat = self.options.dateFormat || i18n.dateFormat;
  4381. /**
  4382. * Date format like "Yesterday 10:20:12"
  4383. *
  4384. * @type String
  4385. * @default "{day} {time}"
  4386. **/
  4387. self.fancyFormat = self.options.fancyDateFormat || i18n.fancyDateFormat;
  4388. /**
  4389. * Date format for if upload file has not original unique name
  4390. * e.g. Clipboard image data, Image data taken with iOS
  4391. *
  4392. * @type String
  4393. * @default "ymd-His"
  4394. **/
  4395. self.nonameDateFormat = (self.options.nonameDateFormat || i18n.nonameDateFormat).replace(/[\/\\]/g, '_');
  4396. /**
  4397. * Css classes
  4398. *
  4399. * @type String
  4400. **/
  4401. self.cssClass = 'ui-helper-reset ui-helper-clearfix ui-widget ui-widget-content ui-corner-all elfinder elfinder-'
  4402. +(self.direction == 'rtl' ? 'rtl' : 'ltr')
  4403. +(self.UA.Touch? (' elfinder-touch' + (self.options.resizable ? ' touch-punch' : '')) : '')
  4404. +(self.UA.Mobile? ' elfinder-mobile' : '')
  4405. +(self.UA.iOS? ' elfinder-ios' : '')
  4406. +' '+self.options.cssClass;
  4407. // prepare node
  4408. node.addClass(self.cssClass)
  4409. .on(mousedown, function() {
  4410. !enabled && self.enable();
  4411. });
  4412. // draggable closure
  4413. (function() {
  4414. var ltr, wzRect, wzBottom, wzBottom2, nodeStyle,
  4415. keyEvt = keydown + 'draggable' + ' keyup.' + namespace + 'draggable';
  4416. /**
  4417. * Base draggable options
  4418. *
  4419. * @type Object
  4420. **/
  4421. self.draggable = {
  4422. appendTo : node,
  4423. addClasses : false,
  4424. distance : 4,
  4425. revert : true,
  4426. refreshPositions : false,
  4427. cursor : 'crosshair',
  4428. cursorAt : {left : 50, top : 47},
  4429. scroll : false,
  4430. start : function(e, ui) {
  4431. var helper = ui.helper,
  4432. targets = $.grep(helper.data('files')||[], function(h) {
  4433. if (h) {
  4434. remember[h] = true;
  4435. return true;
  4436. }
  4437. return false;
  4438. }),
  4439. locked = false,
  4440. cnt, h;
  4441. // fix node size
  4442. nodeStyle = node.attr('style');
  4443. node.width(node.width()).height(node.height());
  4444. // set var for drag()
  4445. ltr = (self.direction === 'ltr');
  4446. wzRect = self.getUI('workzone').data('rectangle');
  4447. wzBottom = wzRect.top + wzRect.height;
  4448. wzBottom2 = wzBottom - self.getUI('navdock').outerHeight(true);
  4449. self.draggingUiHelper = helper;
  4450. cnt = targets.length;
  4451. while (cnt--) {
  4452. h = targets[cnt];
  4453. if (files[h].locked) {
  4454. locked = true;
  4455. helper.data('locked', true);
  4456. break;
  4457. }
  4458. }
  4459. !locked && self.trigger('lockfiles', {files : targets});
  4460. helper.data('autoScrTm', setInterval(function() {
  4461. if (helper.data('autoScr')) {
  4462. self.autoScroll[helper.data('autoScr')](helper.data('autoScrVal'));
  4463. }
  4464. }, 50));
  4465. },
  4466. drag : function(e, ui) {
  4467. var helper = ui.helper,
  4468. autoScr, autoUp, bottom;
  4469. if ((autoUp = wzRect.top > e.pageY) || wzBottom2 < e.pageY) {
  4470. if (wzRect.cwdEdge > e.pageX) {
  4471. autoScr = (ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down');
  4472. } else {
  4473. autoScr = (ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down');
  4474. }
  4475. if (!autoUp) {
  4476. if (autoScr.substr(0, 3) === 'cwd') {
  4477. if (wzBottom < e.pageY) {
  4478. bottom = wzBottom;
  4479. } else {
  4480. autoScr = null;
  4481. }
  4482. } else {
  4483. bottom = wzBottom2;
  4484. }
  4485. }
  4486. if (autoScr) {
  4487. helper.data('autoScr', autoScr);
  4488. helper.data('autoScrVal', Math.pow((autoUp? wzRect.top - e.pageY : e.pageY - bottom), 1.3));
  4489. }
  4490. }
  4491. if (! autoScr) {
  4492. if (helper.data('autoScr')) {
  4493. helper.data('refreshPositions', 1).data('autoScr', null);
  4494. }
  4495. }
  4496. if (helper.data('refreshPositions') && $(this).elfUiWidgetInstance('draggable')) {
  4497. if (helper.data('refreshPositions') > 0) {
  4498. $(this).draggable('option', { refreshPositions : true, elfRefresh : true });
  4499. helper.data('refreshPositions', -1);
  4500. } else {
  4501. $(this).draggable('option', { refreshPositions : false, elfRefresh : false });
  4502. helper.data('refreshPositions', null);
  4503. }
  4504. }
  4505. },
  4506. stop : function(e, ui) {
  4507. var helper = ui.helper,
  4508. files;
  4509. $(document).off(keyEvt);
  4510. $(this).elfUiWidgetInstance('draggable') && $(this).draggable('option', { refreshPositions : false });
  4511. self.draggingUiHelper = null;
  4512. self.trigger('focus').trigger('dragstop');
  4513. if (! helper.data('droped')) {
  4514. files = $.grep(helper.data('files')||[], function(h) { return h? true : false ;});
  4515. self.trigger('unlockfiles', {files : files});
  4516. self.trigger('selectfiles', {files : self.selected()});
  4517. }
  4518. self.enable();
  4519. // restore node style
  4520. node.attr('style', nodeStyle);
  4521. helper.data('autoScrTm') && clearInterval(helper.data('autoScrTm'));
  4522. },
  4523. helper : function(e, ui) {
  4524. var element = this.id ? $(this) : $(this).parents('[id]:first'),
  4525. helper = $('<div class="elfinder-drag-helper"><span class="elfinder-drag-helper-icon-status"/></div>'),
  4526. icon = function(f) {
  4527. var mime = f.mime, i, tmb = self.tmb(f);
  4528. i = '<div class="elfinder-cwd-icon elfinder-cwd-icon-drag '+self.mime2class(mime)+' ui-corner-all"/>';
  4529. if (tmb) {
  4530. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  4531. } else if (f.icon) {
  4532. i = $(i).css(self.getIconStyle(f, true)).get(0).outerHTML;
  4533. }
  4534. if (f.csscls) {
  4535. i = '<div class="'+f.csscls+'">' + i + '</div>';
  4536. }
  4537. return i;
  4538. },
  4539. hashes, l, ctr;
  4540. self.draggingUiHelper && self.draggingUiHelper.stop(true, true);
  4541. self.trigger('dragstart', {target : element[0], originalEvent : e}, true);
  4542. hashes = element.hasClass(self.res('class', 'cwdfile'))
  4543. ? self.selected()
  4544. : [self.navId2Hash(element.attr('id'))];
  4545. helper.append(icon(files[hashes[0]])).data('files', hashes).data('locked', false).data('droped', false).data('namespace', namespace).data('dropover', 0);
  4546. if ((l = hashes.length) > 1) {
  4547. helper.append(icon(files[hashes[l-1]]) + '<span class="elfinder-drag-num">'+l+'</span>');
  4548. }
  4549. $(document).on(keyEvt, function(e){
  4550. var chk = (e.shiftKey||e.ctrlKey||e.metaKey);
  4551. if (ctr !== chk) {
  4552. ctr = chk;
  4553. if (helper.is(':visible') && helper.data('dropover') && ! helper.data('droped')) {
  4554. helper.toggleClass('elfinder-drag-helper-plus', helper.data('locked')? true : ctr);
  4555. self.trigger(ctr? 'unlockfiles' : 'lockfiles', {files : hashes, helper: helper});
  4556. }
  4557. }
  4558. });
  4559. return helper;
  4560. }
  4561. };
  4562. })();
  4563. // in getFileCallback set - change default actions on double click/enter/ctrl+enter
  4564. if (self.commands.getfile) {
  4565. if (typeof(self.options.getFileCallback) == 'function') {
  4566. self.bind('dblclick', function(e) {
  4567. e.preventDefault();
  4568. self.exec('getfile').fail(function() {
  4569. self.exec('open', e.data && e.data.file? [ e.data.file ]: void(0));
  4570. });
  4571. });
  4572. self.shortcut({
  4573. pattern : 'enter',
  4574. description : self.i18n('cmdgetfile'),
  4575. callback : function() { self.exec('getfile').fail(function() { self.exec(self.OS == 'mac' ? 'rename' : 'open'); }); }
  4576. })
  4577. .shortcut({
  4578. pattern : 'ctrl+enter',
  4579. description : self.i18n(self.OS == 'mac' ? 'cmdrename' : 'cmdopen'),
  4580. callback : function() { self.exec(self.OS == 'mac' ? 'rename' : 'open'); }
  4581. });
  4582. } else {
  4583. self.options.getFileCallback = null;
  4584. }
  4585. }
  4586. // load commands
  4587. $.each(self.commands, function(name, cmd) {
  4588. var proto = Object.assign({}, cmd.prototype),
  4589. extendsCmd, opts;
  4590. if ($.isFunction(cmd) && !self._commands[name] && (cmd.prototype.forceLoad || $.inArray(name, self.options.commands) !== -1)) {
  4591. extendsCmd = cmd.prototype.extendsCmd || '';
  4592. if (extendsCmd) {
  4593. if ($.isFunction(self.commands[extendsCmd])) {
  4594. cmd.prototype = Object.assign({}, base, new self.commands[extendsCmd](), cmd.prototype);
  4595. } else {
  4596. return true;
  4597. }
  4598. } else {
  4599. cmd.prototype = Object.assign({}, base, cmd.prototype);
  4600. }
  4601. self._commands[name] = new cmd();
  4602. cmd.prototype = proto;
  4603. opts = self.options.commandsOptions[name] || {};
  4604. if (extendsCmd && self.options.commandsOptions[extendsCmd]) {
  4605. opts = $.extend(true, {}, self.options.commandsOptions[extendsCmd], opts);
  4606. }
  4607. self._commands[name].setup(name, opts);
  4608. // setup linked commands
  4609. if (self._commands[name].linkedCmds.length) {
  4610. $.each(self._commands[name].linkedCmds, function(i, n) {
  4611. var lcmd = self.commands[n];
  4612. if ($.isFunction(lcmd) && !self._commands[n]) {
  4613. lcmd.prototype = base;
  4614. self._commands[n] = new lcmd();
  4615. self._commands[n].setup(n, self.options.commandsOptions[n]||{});
  4616. }
  4617. });
  4618. }
  4619. }
  4620. });
  4621. /**
  4622. * UI nodes
  4623. *
  4624. * @type Object
  4625. **/
  4626. self.ui = {
  4627. // container for nav panel and current folder container
  4628. workzone : $('<div/>').appendTo(node).elfinderworkzone(self),
  4629. // container for folders tree / places
  4630. navbar : $('<div/>').appendTo(node).elfindernavbar(self, self.options.uiOptions.navbar || {}),
  4631. // container for for preview etc at below the navbar
  4632. navdock : $('<div/>').appendTo(node).elfindernavdock(self, self.options.uiOptions.navdock || {}),
  4633. // contextmenu
  4634. contextmenu : $('<div/>').appendTo(node).elfindercontextmenu(self),
  4635. // overlay
  4636. overlay : $('<div/>').appendTo(node).elfinderoverlay({
  4637. show : function() { self.disable(); },
  4638. hide : function() { prevEnabled && self.enable(); }
  4639. }),
  4640. // current folder container
  4641. cwd : $('<div/>').appendTo(node).elfindercwd(self, self.options.uiOptions.cwd || {}),
  4642. // notification dialog window
  4643. notify : self.dialog('', {
  4644. cssClass : 'elfinder-dialog-notify',
  4645. position : self.options.notifyDialog.position,
  4646. absolute : true,
  4647. resizable : false,
  4648. autoOpen : false,
  4649. closeOnEscape : false,
  4650. title : '&nbsp;',
  4651. width : self.options.notifyDialog.width? parseInt(self.options.notifyDialog.width) : null,
  4652. minHeight : null
  4653. }),
  4654. statusbar : $('<div class="ui-widget-header ui-helper-clearfix ui-corner-bottom elfinder-statusbar"/>').hide().appendTo(node),
  4655. toast : $('<div class="elfinder-toast"/>').appendTo(node),
  4656. bottomtray : $('<div class="elfinder-bottomtray">').appendTo(node)
  4657. };
  4658. self.trigger('uiready');
  4659. // load required ui
  4660. $.each(self.options.ui || [], function(i, ui) {
  4661. var name = 'elfinder'+ui,
  4662. opts = self.options.uiOptions[ui] || {};
  4663. if (!self.ui[ui] && $.fn[name]) {
  4664. // regist to self.ui before make instance
  4665. self.ui[ui] = $('<'+(opts.tag || 'div')+'/>').appendTo(node);
  4666. self.ui[ui][name](self, opts);
  4667. }
  4668. });
  4669. // update size
  4670. self.resize(width, height);
  4671. // make node resizable
  4672. if (self.options.resizable) {
  4673. node.resizable({
  4674. resize : function(e, ui) {
  4675. self.resize(ui.size.width, ui.size.height);
  4676. },
  4677. handles : 'se',
  4678. minWidth : 300,
  4679. minHeight : 200
  4680. });
  4681. if (self.UA.Touch) {
  4682. node.addClass('touch-punch');
  4683. }
  4684. }
  4685. (function() {
  4686. var navbar = self.getUI('navbar'),
  4687. cwd = self.getUI('cwd').parent();
  4688. self.autoScroll = {
  4689. navbarUp : function(v) {
  4690. navbar.scrollTop(Math.max(0, navbar.scrollTop() - v));
  4691. },
  4692. navbarDown : function(v) {
  4693. navbar.scrollTop(navbar.scrollTop() + v);
  4694. },
  4695. cwdUp : function(v) {
  4696. cwd.scrollTop(Math.max(0, cwd.scrollTop() - v));
  4697. },
  4698. cwdDown : function(v) {
  4699. cwd.scrollTop(cwd.scrollTop() + v);
  4700. }
  4701. };
  4702. })();
  4703. // Swipe on the touch devices to show/hide of toolbar or navbar
  4704. if (self.UA.Touch) {
  4705. (function() {
  4706. var lastX, lastY, nodeOffset, nodeWidth, nodeTop, navbarW, toolbarH,
  4707. navbar = self.getUI('navbar'),
  4708. toolbar = self.getUI('toolbar'),
  4709. moveEv = 'touchmove.stopscroll',
  4710. moveTm,
  4711. moveUpOn = function(e) {
  4712. var touches = e.originalEvent.touches || [{}],
  4713. y = touches[0].pageY || null;
  4714. if (!lastY || y < lastY) {
  4715. e.preventDefault();
  4716. moveTm && clearTimeout(moveTm);
  4717. }
  4718. },
  4719. moveDownOn = function(e) {
  4720. e.preventDefault();
  4721. moveTm && clearTimeout(moveTm);
  4722. },
  4723. moveOff = function() {
  4724. moveTm = setTimeout(function() {
  4725. node.off(moveEv);
  4726. }, 100);
  4727. },
  4728. handleW, handleH = 50;
  4729. navbar = navbar.children().length? navbar : null;
  4730. toolbar = toolbar.length? toolbar : null;
  4731. node.on('touchstart touchmove touchend', function(e) {
  4732. if (e.type === 'touchend') {
  4733. lastX = false;
  4734. lastY = false;
  4735. moveOff();
  4736. return;
  4737. }
  4738. var touches = e.originalEvent.touches || [{}],
  4739. x = touches[0].pageX || null,
  4740. y = touches[0].pageY || null,
  4741. ltr = (self.direction === 'ltr'),
  4742. navbarMode, treeWidth, swipeX, moveX, toolbarT, mode;
  4743. if (x === null || y === null || (e.type === 'touchstart' && touches.length > 1)) {
  4744. return;
  4745. }
  4746. if (e.type === 'touchstart') {
  4747. nodeOffset = node.offset();
  4748. nodeWidth = node.width();
  4749. if (navbar) {
  4750. lastX = false;
  4751. if (navbar.is(':hidden')) {
  4752. if (! handleW) {
  4753. handleW = Math.max(50, nodeWidth / 10);
  4754. }
  4755. if ((ltr? (x - nodeOffset.left) : (nodeWidth + nodeOffset.left - x)) < handleW) {
  4756. lastX = x;
  4757. }
  4758. } else if (! e.originalEvent._preventSwipeX) {
  4759. navbarW = navbar.width();
  4760. if (ltr) {
  4761. swipeX = (x < nodeOffset.left + navbarW);
  4762. } else {
  4763. swipeX = (x > nodeOffset.left + nodeWidth - navbarW);
  4764. }
  4765. if (swipeX) {
  4766. handleW = Math.max(50, nodeWidth / 10);
  4767. lastX = x;
  4768. } else {
  4769. lastX = false;
  4770. }
  4771. }
  4772. }
  4773. if (toolbar) {
  4774. lastY = false;
  4775. if (! e.originalEvent._preventSwipeY) {
  4776. toolbarH = toolbar.height();
  4777. nodeTop = nodeOffset.top;
  4778. if (y - nodeTop < (toolbar.is(':hidden')? handleH : (toolbarH + 30))) {
  4779. lastY = y;
  4780. node.on(moveEv, toolbar.is(':hidden')? moveDownOn: moveUpOn);
  4781. }
  4782. }
  4783. }
  4784. } else {
  4785. if (navbar && lastX !== false) {
  4786. navbarMode = (ltr? (lastX > x) : (lastX < x))? 'navhide' : 'navshow';
  4787. moveX = Math.abs(lastX - x);
  4788. if (navbarMode === 'navhide' && moveX > navbarW * 0.6
  4789. || (moveX > (navbarMode === 'navhide'? navbarW / 3 : 45)
  4790. && (navbarMode === 'navshow'
  4791. || (ltr? x < nodeOffset.left + 20 : x > nodeOffset.left + nodeWidth - 20)
  4792. ))
  4793. ) {
  4794. self.getUI('navbar').trigger(navbarMode, {handleW: handleW});
  4795. lastX = false;
  4796. }
  4797. }
  4798. if (toolbar && lastY !== false ) {
  4799. toolbarT = toolbar.offset().top;
  4800. if (Math.abs(lastY - y) > Math.min(45, toolbarH / 3)) {
  4801. mode = (lastY > y)? 'slideUp' : 'slideDown';
  4802. if (mode === 'slideDown' || toolbarT + 20 > y) {
  4803. if (toolbar.is(mode === 'slideDown' ? ':hidden' : ':visible')) {
  4804. toolbar.stop(true, true).trigger('toggle', {duration: 100, handleH: handleH});
  4805. }
  4806. lastY = false;
  4807. }
  4808. }
  4809. }
  4810. }
  4811. });
  4812. })();
  4813. }
  4814. if (self.dragUpload) {
  4815. // add event listener for HTML5 DnD upload
  4816. (function() {
  4817. var isin = function(e) {
  4818. return (e.target.nodeName !== 'TEXTAREA' && e.target.nodeName !== 'INPUT' && $(e.target).closest('div.ui-dialog-content').length === 0);
  4819. },
  4820. ent = 'native-drag-enter',
  4821. disable = 'native-drag-disable',
  4822. c = 'class',
  4823. navdir = self.res(c, 'navdir'),
  4824. droppable = self.res(c, 'droppable'),
  4825. dropover = self.res(c, 'adroppable'),
  4826. arrow = self.res(c, 'navarrow'),
  4827. clDropActive = self.res(c, 'adroppable'),
  4828. wz = self.getUI('workzone'),
  4829. ltr = (self.direction === 'ltr'),
  4830. clearTm = function() {
  4831. autoScrTm && cancelAnimationFrame(autoScrTm);
  4832. autoScrTm = null;
  4833. },
  4834. wzRect, autoScrFn, autoScrTm;
  4835. node.on('dragenter', function(e) {
  4836. clearTm();
  4837. if (isin(e)) {
  4838. e.preventDefault();
  4839. e.stopPropagation();
  4840. wzRect = wz.data('rectangle');
  4841. }
  4842. })
  4843. .on('dragleave', function(e) {
  4844. clearTm();
  4845. if (isin(e)) {
  4846. e.preventDefault();
  4847. e.stopPropagation();
  4848. }
  4849. })
  4850. .on('dragover', function(e) {
  4851. var autoUp;
  4852. if (isin(e)) {
  4853. e.preventDefault();
  4854. e.stopPropagation();
  4855. e.originalEvent.dataTransfer.dropEffect = 'none';
  4856. if (! autoScrTm) {
  4857. autoScrTm = requestAnimationFrame(function() {
  4858. var wzBottom = wzRect.top + wzRect.height,
  4859. wzBottom2 = wzBottom - self.getUI('navdock').outerHeight(true),
  4860. fn;
  4861. if ((autoUp = e.pageY < wzRect.top) || e.pageY > wzBottom2 ) {
  4862. if (wzRect.cwdEdge > e.pageX) {
  4863. fn = (ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down');
  4864. } else {
  4865. fn = (ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down');
  4866. }
  4867. if (!autoUp) {
  4868. if (fn.substr(0, 3) === 'cwd') {
  4869. if (wzBottom < e.pageY) {
  4870. wzBottom2 = wzBottom;
  4871. } else {
  4872. fn = '';
  4873. }
  4874. }
  4875. }
  4876. fn && self.autoScroll[fn](Math.pow((autoUp? wzRect.top - e.pageY : e.pageY - wzBottom2), 1.3));
  4877. }
  4878. autoScrTm = null;
  4879. });
  4880. }
  4881. } else {
  4882. clearTm();
  4883. }
  4884. })
  4885. .on('drop', function(e) {
  4886. clearTm();
  4887. if (isin(e)) {
  4888. e.stopPropagation();
  4889. e.preventDefault();
  4890. }
  4891. });
  4892. node.on('dragenter', '.native-droppable', function(e){
  4893. if (e.originalEvent.dataTransfer) {
  4894. var $elm = $(e.currentTarget),
  4895. id = e.currentTarget.id || null,
  4896. cwd = null,
  4897. elfFrom;
  4898. if (!id) { // target is cwd
  4899. cwd = self.cwd();
  4900. $elm.data(disable, false);
  4901. try {
  4902. $.each(e.originalEvent.dataTransfer.types, function(i, v){
  4903. if (v.substr(0, 13) === 'elfinderfrom:') {
  4904. elfFrom = v.substr(13).toLowerCase();
  4905. }
  4906. });
  4907. } catch(e) {}
  4908. }
  4909. if (!cwd || (cwd.write && (!elfFrom || elfFrom !== (window.location.href + cwd.hash).toLowerCase()))) {
  4910. e.preventDefault();
  4911. e.stopPropagation();
  4912. $elm.data(ent, true);
  4913. $elm.addClass(clDropActive);
  4914. } else {
  4915. $elm.data(disable, true);
  4916. }
  4917. }
  4918. })
  4919. .on('dragleave', '.native-droppable', function(e){
  4920. if (e.originalEvent.dataTransfer) {
  4921. var $elm = $(e.currentTarget);
  4922. e.preventDefault();
  4923. e.stopPropagation();
  4924. if ($elm.data(ent)) {
  4925. $elm.data(ent, false);
  4926. } else {
  4927. $elm.removeClass(clDropActive);
  4928. }
  4929. }
  4930. })
  4931. .on('dragover', '.native-droppable', function(e){
  4932. if (e.originalEvent.dataTransfer) {
  4933. var $elm = $(e.currentTarget);
  4934. e.preventDefault();
  4935. e.stopPropagation();
  4936. e.originalEvent.dataTransfer.dropEffect = $elm.data(disable)? 'none' : 'copy';
  4937. $elm.data(ent, false);
  4938. }
  4939. })
  4940. .on('drop', '.native-droppable', function(e){
  4941. if (e.originalEvent && e.originalEvent.dataTransfer) {
  4942. var $elm = $(e.currentTarget),
  4943. id;
  4944. e.preventDefault();
  4945. e.stopPropagation();
  4946. $elm.removeClass(clDropActive);
  4947. if (e.currentTarget.id) {
  4948. id = $elm.hasClass(navdir)? self.navId2Hash(e.currentTarget.id) : self.cwdId2Hash(e.currentTarget.id);
  4949. } else {
  4950. id = self.cwd().hash;
  4951. }
  4952. e.originalEvent._target = id;
  4953. self.exec('upload', {dropEvt: e.originalEvent, target: id}, void 0, id);
  4954. }
  4955. });
  4956. })();
  4957. }
  4958. // trigger event cssloaded if cssAutoLoad disabled
  4959. if (self.cssloaded === false) {
  4960. self.cssloaded = true;
  4961. self.trigger('cssloaded');
  4962. }
  4963. // calculate elFinder node z-index
  4964. self.zIndexCalc();
  4965. // send initial request and start to pray >_<
  4966. self.trigger('init')
  4967. .request({
  4968. data : {cmd : 'open', target : self.startDir(), init : 1, tree : 1},
  4969. preventDone : true,
  4970. notify : {type : 'open', cnt : 1, hideCnt : true},
  4971. freeze : true
  4972. })
  4973. .fail(function() {
  4974. self.trigger('fail').disable().lastDir('');
  4975. listeners = {};
  4976. shortcuts = {};
  4977. $(document).add(node).off('.'+namespace);
  4978. self.trigger = function() { };
  4979. })
  4980. .done(function(data) {
  4981. var trashDisable = function(th) {
  4982. var src = self.file(self.trashes[th]),
  4983. d = self.options.debug,
  4984. error;
  4985. if (src && src.volumeid) {
  4986. delete self.volOptions[src.volumeid].trashHash;
  4987. }
  4988. self.trashes[th] = false;
  4989. self.debug('backend-error', 'Trash hash "'+th+'" was not found or not writable.');
  4990. },
  4991. toChkTh = {};
  4992. // regist rawStringDecoder
  4993. if (self.options.rawStringDecoder) {
  4994. self.registRawStringDecoder(self.options.rawStringDecoder);
  4995. }
  4996. // re-calculate elFinder node z-index
  4997. self.zIndexCalc();
  4998. self.load().debug('api', self.api);
  4999. // update ui's size after init
  5000. node.trigger('resize');
  5001. // initial open
  5002. open(data);
  5003. self.trigger('open', data, false);
  5004. self.trigger('opendone');
  5005. if (inFrame && self.options.enableAlways) {
  5006. $(window).trigger('focus');
  5007. }
  5008. // check self.trashes
  5009. $.each(self.trashes, function(th) {
  5010. var dir = self.file(th),
  5011. src;
  5012. if (! dir) {
  5013. toChkTh[th] = true;
  5014. } else if (dir.mime !== 'directory' || ! dir.write) {
  5015. trashDisable(th);
  5016. }
  5017. });
  5018. if (Object.keys(toChkTh).length) {
  5019. self.request({
  5020. data : {cmd : 'info', targets : Object.keys(toChkTh)},
  5021. preventDefault : true
  5022. }).done(function(data) {
  5023. if (data && data.files) {
  5024. $.each(data.files, function(i, dir) {
  5025. if (dir.mime === 'directory' && dir.write) {
  5026. delete toChkTh[dir.hash];
  5027. }
  5028. });
  5029. }
  5030. }).always(function() {
  5031. $.each(toChkTh, trashDisable);
  5032. });
  5033. }
  5034. // to enable / disable
  5035. self[self.options.enableAlways? 'enable' : 'disable']();
  5036. });
  5037. // self.timeEnd('load');
  5038. // End of bootUp()
  5039. };
  5040. // call bootCallback function with elFinder instance, extraObject - { dfrdsBeforeBootup: dfrdsBeforeBootup }
  5041. if (bootCallback && typeof bootCallback === 'function') {
  5042. self.bootCallback = bootCallback;
  5043. bootCallback.call(node.get(0), self, { dfrdsBeforeBootup: dfrdsBeforeBootup });
  5044. }
  5045. // call dfrdsBeforeBootup functions then boot up elFinder
  5046. $.when.apply(null, dfrdsBeforeBootup).done(function() {
  5047. bootUp();
  5048. }).fail(function(error) {
  5049. self.error(error);
  5050. });
  5051. };
  5052. //register elFinder to global scope
  5053. if (typeof toGlobal === 'undefined' || toGlobal) {
  5054. window.elFinder = elFinder;
  5055. }
  5056. /**
  5057. * Prototype
  5058. *
  5059. * @type Object
  5060. */
  5061. elFinder.prototype = {
  5062. uniqueid : 0,
  5063. res : function(type, id) {
  5064. return this.resources[type] && this.resources[type][id];
  5065. },
  5066. /**
  5067. * User os. Required to bind native shortcuts for open/rename
  5068. *
  5069. * @type String
  5070. **/
  5071. OS : navigator.userAgent.indexOf('Mac') !== -1 ? 'mac' : navigator.userAgent.indexOf('Win') !== -1 ? 'win' : 'other',
  5072. /**
  5073. * User browser UA.
  5074. * jQuery.browser: version deprecated: 1.3, removed: 1.9
  5075. *
  5076. * @type Object
  5077. **/
  5078. UA : (function(){
  5079. var self = this,
  5080. webkit = !document.unqueID && !window.opera && !window.sidebar && window.localStorage && 'WebkitAppearance' in document.documentElement.style,
  5081. chrome = webkit && window.chrome,
  5082. /*setRotated = function() {
  5083. var a = ((screen && screen.orientation && screen.orientation.angle) || window.orientation || 0) + 0;
  5084. if (a === -90) {
  5085. a = 270;
  5086. }
  5087. UA.Angle = a;
  5088. UA.Rotated = a % 180 === 0? false : true;
  5089. },*/
  5090. UA = {
  5091. // Browser IE <= IE 6
  5092. ltIE6 : typeof window.addEventListener == "undefined" && typeof document.documentElement.style.maxHeight == "undefined",
  5093. // Browser IE <= IE 7
  5094. ltIE7 : typeof window.addEventListener == "undefined" && typeof document.querySelectorAll == "undefined",
  5095. // Browser IE <= IE 8
  5096. ltIE8 : typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined",
  5097. // Browser IE <= IE 9
  5098. ltIE9 : document.uniqueID && document.documentMode <= 9,
  5099. // Browser IE <= IE 10
  5100. ltIE10 : document.uniqueID && document.documentMode <= 10,
  5101. // Browser IE >= IE 11
  5102. gtIE11 : document.uniqueID && document.documentMode >= 11,
  5103. IE : document.uniqueID,
  5104. Firefox : window.sidebar,
  5105. Opera : window.opera,
  5106. Webkit : webkit,
  5107. Chrome : chrome,
  5108. Edge : (chrome && window.msCredentials)? true : false,
  5109. Safari : webkit && !window.chrome,
  5110. Mobile : typeof window.orientation != "undefined",
  5111. Touch : typeof window.ontouchstart != "undefined",
  5112. iOS : navigator.platform.match(/^iP(?:[ao]d|hone)/),
  5113. Fullscreen : (typeof (document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen || document.msExitFullscreen) !== 'undefined'),
  5114. Angle : 0,
  5115. Rotated : false,
  5116. CSS : (function() {
  5117. var aStyle = document.createElement('a').style,
  5118. pStyle = document.createElement('p').style,
  5119. css;
  5120. css = 'position:sticky;position:-webkit-sticky;';
  5121. css += 'width:-webkit-max-content;width:-moz-max-content;width:-ms-max-content;width:max-content;';
  5122. aStyle.cssText = css;
  5123. return {
  5124. positionSticky : aStyle.position.indexOf('sticky')!==-1,
  5125. widthMaxContent : aStyle.width.indexOf('max-content')!==-1,
  5126. flex : typeof pStyle.flex !== 'undefined'
  5127. };
  5128. })()
  5129. };
  5130. return UA;
  5131. })(),
  5132. /**
  5133. * Has RequireJS?
  5134. *
  5135. * @type Boolean
  5136. */
  5137. hasRequire : (typeof define === 'function' && define.amd),
  5138. /**
  5139. * Current request command
  5140. *
  5141. * @type String
  5142. */
  5143. currentReqCmd : '',
  5144. /**
  5145. * Current keyboard state
  5146. *
  5147. * @type Object
  5148. */
  5149. keyState : {},
  5150. /**
  5151. * Internationalization object
  5152. *
  5153. * @type Object
  5154. */
  5155. i18 : {
  5156. en : {
  5157. translator : '',
  5158. language : 'English',
  5159. direction : 'ltr',
  5160. dateFormat : 'd.m.Y H:i',
  5161. fancyDateFormat : '$1 H:i',
  5162. nonameDateFormat : 'ymd-His',
  5163. messages : {}
  5164. },
  5165. months : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
  5166. monthsShort : ['msJan', 'msFeb', 'msMar', 'msApr', 'msMay', 'msJun', 'msJul', 'msAug', 'msSep', 'msOct', 'msNov', 'msDec'],
  5167. days : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
  5168. daysShort : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
  5169. },
  5170. /**
  5171. * File mimetype to kind mapping
  5172. *
  5173. * @type Object
  5174. */
  5175. kinds : {
  5176. 'unknown' : 'Unknown',
  5177. 'directory' : 'Folder',
  5178. 'group' : 'Selects',
  5179. 'symlink' : 'Alias',
  5180. 'symlink-broken' : 'AliasBroken',
  5181. 'application/x-empty' : 'TextPlain',
  5182. 'application/postscript' : 'Postscript',
  5183. 'application/vnd.ms-office' : 'MsOffice',
  5184. 'application/msword' : 'MsWord',
  5185. 'application/vnd.ms-word' : 'MsWord',
  5186. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : 'MsWord',
  5187. 'application/vnd.ms-word.document.macroEnabled.12' : 'MsWord',
  5188. 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' : 'MsWord',
  5189. 'application/vnd.ms-word.template.macroEnabled.12' : 'MsWord',
  5190. 'application/vnd.ms-excel' : 'MsExcel',
  5191. 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : 'MsExcel',
  5192. 'application/vnd.ms-excel.sheet.macroEnabled.12' : 'MsExcel',
  5193. 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' : 'MsExcel',
  5194. 'application/vnd.ms-excel.template.macroEnabled.12' : 'MsExcel',
  5195. 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' : 'MsExcel',
  5196. 'application/vnd.ms-excel.addin.macroEnabled.12' : 'MsExcel',
  5197. 'application/vnd.ms-powerpoint' : 'MsPP',
  5198. 'application/vnd.openxmlformats-officedocument.presentationml.presentation' : 'MsPP',
  5199. 'application/vnd.ms-powerpoint.presentation.macroEnabled.12' : 'MsPP',
  5200. 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' : 'MsPP',
  5201. 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' : 'MsPP',
  5202. 'application/vnd.openxmlformats-officedocument.presentationml.template' : 'MsPP',
  5203. 'application/vnd.ms-powerpoint.template.macroEnabled.12' : 'MsPP',
  5204. 'application/vnd.ms-powerpoint.addin.macroEnabled.12' : 'MsPP',
  5205. 'application/vnd.openxmlformats-officedocument.presentationml.slide' : 'MsPP',
  5206. 'application/vnd.ms-powerpoint.slide.macroEnabled.12' : 'MsPP',
  5207. 'application/pdf' : 'PDF',
  5208. 'application/xml' : 'XML',
  5209. 'application/vnd.oasis.opendocument.text' : 'OO',
  5210. 'application/vnd.oasis.opendocument.text-template' : 'OO',
  5211. 'application/vnd.oasis.opendocument.text-web' : 'OO',
  5212. 'application/vnd.oasis.opendocument.text-master' : 'OO',
  5213. 'application/vnd.oasis.opendocument.graphics' : 'OO',
  5214. 'application/vnd.oasis.opendocument.graphics-template' : 'OO',
  5215. 'application/vnd.oasis.opendocument.presentation' : 'OO',
  5216. 'application/vnd.oasis.opendocument.presentation-template' : 'OO',
  5217. 'application/vnd.oasis.opendocument.spreadsheet' : 'OO',
  5218. 'application/vnd.oasis.opendocument.spreadsheet-template' : 'OO',
  5219. 'application/vnd.oasis.opendocument.chart' : 'OO',
  5220. 'application/vnd.oasis.opendocument.formula' : 'OO',
  5221. 'application/vnd.oasis.opendocument.database' : 'OO',
  5222. 'application/vnd.oasis.opendocument.image' : 'OO',
  5223. 'application/vnd.openofficeorg.extension' : 'OO',
  5224. 'application/x-shockwave-flash' : 'AppFlash',
  5225. 'application/flash-video' : 'Flash video',
  5226. 'application/x-bittorrent' : 'Torrent',
  5227. 'application/javascript' : 'JS',
  5228. 'application/rtf' : 'RTF',
  5229. 'application/rtfd' : 'RTF',
  5230. 'application/x-font-ttf' : 'TTF',
  5231. 'application/x-font-otf' : 'OTF',
  5232. 'application/x-rpm' : 'RPM',
  5233. 'application/x-web-config' : 'TextPlain',
  5234. 'application/xhtml+xml' : 'HTML',
  5235. 'application/docbook+xml' : 'DOCBOOK',
  5236. 'application/x-awk' : 'AWK',
  5237. 'application/x-gzip' : 'GZIP',
  5238. 'application/x-bzip2' : 'BZIP',
  5239. 'application/x-xz' : 'XZ',
  5240. 'application/zip' : 'ZIP',
  5241. 'application/x-zip' : 'ZIP',
  5242. 'application/x-rar' : 'RAR',
  5243. 'application/x-tar' : 'TAR',
  5244. 'application/x-7z-compressed' : '7z',
  5245. 'application/x-jar' : 'JAR',
  5246. 'text/plain' : 'TextPlain',
  5247. 'text/x-php' : 'PHP',
  5248. 'text/html' : 'HTML',
  5249. 'text/javascript' : 'JS',
  5250. 'text/css' : 'CSS',
  5251. 'text/rtf' : 'RTF',
  5252. 'text/rtfd' : 'RTF',
  5253. 'text/x-c' : 'C',
  5254. 'text/x-csrc' : 'C',
  5255. 'text/x-chdr' : 'CHeader',
  5256. 'text/x-c++' : 'CPP',
  5257. 'text/x-c++src' : 'CPP',
  5258. 'text/x-c++hdr' : 'CPPHeader',
  5259. 'text/x-shellscript' : 'Shell',
  5260. 'application/x-csh' : 'Shell',
  5261. 'text/x-python' : 'Python',
  5262. 'text/x-java' : 'Java',
  5263. 'text/x-java-source' : 'Java',
  5264. 'text/x-ruby' : 'Ruby',
  5265. 'text/x-perl' : 'Perl',
  5266. 'text/x-sql' : 'SQL',
  5267. 'text/xml' : 'XML',
  5268. 'text/x-comma-separated-values' : 'CSV',
  5269. 'text/x-markdown' : 'Markdown',
  5270. 'image/x-ms-bmp' : 'BMP',
  5271. 'image/jpeg' : 'JPEG',
  5272. 'image/gif' : 'GIF',
  5273. 'image/png' : 'PNG',
  5274. 'image/tiff' : 'TIFF',
  5275. 'image/x-targa' : 'TGA',
  5276. 'image/vnd.adobe.photoshop' : 'PSD',
  5277. 'image/xbm' : 'XBITMAP',
  5278. 'image/pxm' : 'PXM',
  5279. 'audio/mpeg' : 'AudioMPEG',
  5280. 'audio/midi' : 'AudioMIDI',
  5281. 'audio/ogg' : 'AudioOGG',
  5282. 'audio/mp4' : 'AudioMPEG4',
  5283. 'audio/x-m4a' : 'AudioMPEG4',
  5284. 'audio/wav' : 'AudioWAV',
  5285. 'audio/x-mp3-playlist' : 'AudioPlaylist',
  5286. 'video/x-dv' : 'VideoDV',
  5287. 'video/mp4' : 'VideoMPEG4',
  5288. 'video/mpeg' : 'VideoMPEG',
  5289. 'video/x-msvideo' : 'VideoAVI',
  5290. 'video/quicktime' : 'VideoMOV',
  5291. 'video/x-ms-wmv' : 'VideoWM',
  5292. 'video/x-flv' : 'VideoFlash',
  5293. 'video/x-matroska' : 'VideoMKV',
  5294. 'video/ogg' : 'VideoOGG'
  5295. },
  5296. /**
  5297. * File mimetype to file extention mapping
  5298. *
  5299. * @type Object
  5300. * @see elFinder.mimetypes.js
  5301. */
  5302. mimeTypes : {},
  5303. /**
  5304. * Ajax request data validation rules
  5305. *
  5306. * @type Object
  5307. */
  5308. rules : {
  5309. defaults : function(data) {
  5310. if (!data
  5311. || (data.added && !Array.isArray(data.added))
  5312. || (data.removed && !Array.isArray(data.removed))
  5313. || (data.changed && !Array.isArray(data.changed))) {
  5314. return false;
  5315. }
  5316. return true;
  5317. },
  5318. open : function(data) { return data && data.cwd && data.files && $.isPlainObject(data.cwd) && Array.isArray(data.files); },
  5319. tree : function(data) { return data && data.tree && Array.isArray(data.tree); },
  5320. parents : function(data) { return data && data.tree && Array.isArray(data.tree); },
  5321. tmb : function(data) { return data && data.images && ($.isPlainObject(data.images) || Array.isArray(data.images)); },
  5322. upload : function(data) { return data && ($.isPlainObject(data.added) || Array.isArray(data.added));},
  5323. search : function(data) { return data && data.files && Array.isArray(data.files); }
  5324. },
  5325. /**
  5326. * Commands costructors
  5327. *
  5328. * @type Object
  5329. */
  5330. commands : {},
  5331. /**
  5332. * Commands to add the item (space delimited)
  5333. *
  5334. * @type String
  5335. */
  5336. cmdsToAdd : 'archive duplicate extract mkdir mkfile paste rm upload',
  5337. parseUploadData : function(text) {
  5338. var self = this,
  5339. data;
  5340. if (!$.trim(text)) {
  5341. return {error : ['errResponse', 'errDataEmpty']};
  5342. }
  5343. try {
  5344. data = JSON.parse(text);
  5345. } catch (e) {
  5346. return {error : ['errResponse', 'errDataNotJSON']};
  5347. }
  5348. data = self.normalize(data);
  5349. if (!self.validResponse('upload', data)) {
  5350. return {error : (response.norError || ['errResponse'])};
  5351. }
  5352. data.removed = $.merge((data.removed || []), $.map(data.added || [], function(f) { return self.file(f.hash)? f.hash : null; }));
  5353. return data;
  5354. },
  5355. iframeCnt : 0,
  5356. uploads : {
  5357. // xhr muiti uploading flag
  5358. xhrUploading: false,
  5359. // Timer of request fail to sync
  5360. failSyncTm: null,
  5361. // current chunkfail requesting chunk
  5362. chunkfailReq: {},
  5363. // check file/dir exists
  5364. checkExists: function(files, target, fm, isDir) {
  5365. var dfrd = $.Deferred(),
  5366. names, renames = [], hashes = {}, chkFiles = [],
  5367. cancel = function() {
  5368. var i = files.length;
  5369. while (--i > -1) {
  5370. files[i]._remove = true;
  5371. }
  5372. },
  5373. resolve = function() {
  5374. dfrd.resolve(renames, hashes);
  5375. },
  5376. check = function() {
  5377. var existed = [], exists = [], i, c,
  5378. pathStr = target !== fm.cwd().hash? fm.path(target, true) + fm.option('separator', target) : '',
  5379. confirm = function(ndx) {
  5380. var last = ndx == exists.length-1,
  5381. opts = {
  5382. cssClass : 'elfinder-confirm-upload',
  5383. title : fm.i18n('cmdupload'),
  5384. text : ['errExists', pathStr + exists[ndx].name, 'confirmRepl'],
  5385. all : !last,
  5386. accept : {
  5387. label : 'btnYes',
  5388. callback : function(all) {
  5389. !last && !all
  5390. ? confirm(++ndx)
  5391. : resolve();
  5392. }
  5393. },
  5394. reject : {
  5395. label : 'btnNo',
  5396. callback : function(all) {
  5397. var i;
  5398. if (all) {
  5399. i = exists.length;
  5400. while (ndx < i--) {
  5401. files[exists[i].i]._remove = true;
  5402. }
  5403. } else {
  5404. files[exists[ndx].i]._remove = true;
  5405. }
  5406. !last && !all
  5407. ? confirm(++ndx)
  5408. : resolve();
  5409. }
  5410. },
  5411. cancel : {
  5412. label : 'btnCancel',
  5413. callback : function() {
  5414. cancel();
  5415. resolve();
  5416. }
  5417. },
  5418. buttons : [
  5419. {
  5420. label : 'btnBackup',
  5421. cssClass : 'elfinder-confirm-btn-backup',
  5422. callback : function(all) {
  5423. var i;
  5424. if (all) {
  5425. i = exists.length;
  5426. while (ndx < i--) {
  5427. renames.push(exists[i].name);
  5428. }
  5429. } else {
  5430. renames.push(exists[ndx].name);
  5431. }
  5432. !last && !all
  5433. ? confirm(++ndx)
  5434. : resolve();
  5435. }
  5436. }
  5437. ]
  5438. };
  5439. if (!isDir) {
  5440. opts.buttons.push({
  5441. label : 'btnRename' + (last? '' : 'All'),
  5442. cssClass : 'elfinder-confirm-btn-rename',
  5443. callback : function() {
  5444. renames = null;
  5445. resolve();
  5446. }
  5447. });
  5448. }
  5449. if (fm.iframeCnt > 0) {
  5450. delete opts.reject;
  5451. }
  5452. fm.confirm(opts);
  5453. };
  5454. if (! fm.file(target).read) {
  5455. // for dropbox type
  5456. resolve();
  5457. return;
  5458. }
  5459. names = $.map(files, function(file, i) { return file.name && (!fm.UA.iOS || file.name !== 'image.jpg')? {i: i, name: file.name} : null ;});
  5460. fm.request({
  5461. data : {cmd : 'ls', target : target, intersect : $.map(names, function(item) { return item.name;})},
  5462. notify : {type : 'preupload', cnt : 1, hideCnt : true},
  5463. preventDefault : true
  5464. })
  5465. .done(function(data) {
  5466. var existedArr, cwdItems;
  5467. if (data) {
  5468. if (data.error) {
  5469. cancel();
  5470. } else {
  5471. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  5472. if (data.list) {
  5473. if (Array.isArray(data.list)) {
  5474. existed = data.list || [];
  5475. } else {
  5476. existedArr = [];
  5477. existed = $.map(data.list, function(n) {
  5478. if (typeof n === 'string') {
  5479. return n;
  5480. } else {
  5481. // support to >=2.1.11 plugin Normalizer, Sanitizer
  5482. existedArr = existedArr.concat(n);
  5483. return false;
  5484. }
  5485. });
  5486. if (existedArr.length) {
  5487. existed = existed.concat(existedArr);
  5488. }
  5489. hashes = data.list;
  5490. }
  5491. exists = $.grep(names, function(name){
  5492. return $.inArray(name.name, existed) !== -1 ? true : false ;
  5493. });
  5494. if (exists.length && existed.length && target == fm.cwd().hash) {
  5495. cwdItems = $.map(fm.files(target), function(file) { return file.name; } );
  5496. if ($.grep(existed, function(n) {
  5497. return $.inArray(n, cwdItems) === -1? true : false;
  5498. }).length){
  5499. fm.sync();
  5500. }
  5501. }
  5502. }
  5503. }
  5504. }
  5505. }
  5506. if (exists.length > 0) {
  5507. confirm(0);
  5508. } else {
  5509. resolve();
  5510. }
  5511. })
  5512. .fail(function(error) {
  5513. cancel();
  5514. resolve();
  5515. error && fm.error(error);
  5516. });
  5517. };
  5518. if (fm.api >= 2.1 && typeof files[0] == 'object') {
  5519. check();
  5520. } else {
  5521. resolve();
  5522. }
  5523. return dfrd;
  5524. },
  5525. // check droped contents
  5526. checkFile : function(data, fm, target) {
  5527. if (!!data.checked || data.type == 'files') {
  5528. return data.files;
  5529. } else if (data.type == 'data') {
  5530. var dfrd = $.Deferred(),
  5531. scanDfd = $.Deferred(),
  5532. files = [],
  5533. paths = [],
  5534. dirctorys = [],
  5535. processing = 0,
  5536. items,
  5537. mkdirs = [],
  5538. cancel = false,
  5539. toArray = function(list) {
  5540. return Array.prototype.slice.call(list || [], 0);
  5541. },
  5542. doScan = function(items) {
  5543. var entry, readEntries,
  5544. excludes = fm.options.folderUploadExclude[fm.OS] || null,
  5545. length = items.length,
  5546. check = function() {
  5547. if (--processing < 1 && scanDfd.state() === 'pending') {
  5548. scanDfd.resolve();
  5549. }
  5550. },
  5551. pushItem = function(file) {
  5552. if (! excludes || ! file.name.match(excludes)) {
  5553. paths.push(entry.fullPath || '');
  5554. files.push(file);
  5555. }
  5556. check();
  5557. },
  5558. readEntries = function(dirReader) {
  5559. var entries = [],
  5560. read = function() {
  5561. dirReader.readEntries(function(results) {
  5562. if (cancel || !results.length) {
  5563. for (var i = 0; i < entries.length; i++) {
  5564. if (cancel) {
  5565. scanDfd.reject();
  5566. break;
  5567. }
  5568. doScan([entries[i]]);
  5569. }
  5570. check();
  5571. } else {
  5572. entries = entries.concat(toArray(results));
  5573. read();
  5574. }
  5575. }, check);
  5576. };
  5577. read();
  5578. };
  5579. processing++;
  5580. for (var i = 0; i < length; i++) {
  5581. if (cancel) {
  5582. scanDfd.reject();
  5583. break;
  5584. }
  5585. entry = items[i];
  5586. if (entry) {
  5587. if (entry.isFile) {
  5588. processing++;
  5589. entry.file(pushItem, check);
  5590. } else if (entry.isDirectory) {
  5591. if (fm.api >= 2.1) {
  5592. processing++;
  5593. mkdirs.push(entry.fullPath);
  5594. readEntries(entry.createReader()); // Start reading dirs.
  5595. }
  5596. }
  5597. }
  5598. }
  5599. check();
  5600. return scanDfd;
  5601. }, hasDirs;
  5602. items = $.map(data.files.items, function(item){
  5603. return item.getAsEntry? item.getAsEntry() : item.webkitGetAsEntry();
  5604. });
  5605. $.each(items, function(i, item) {
  5606. if (item.isDirectory) {
  5607. hasDirs = true;
  5608. return false;
  5609. }
  5610. });
  5611. if (items.length > 0) {
  5612. fm.uploads.checkExists(items, target, fm, hasDirs).done(function(renames, hashes){
  5613. var dfds = [];
  5614. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  5615. if (renames === null) {
  5616. data.overwrite = 0;
  5617. renames = [];
  5618. }
  5619. items = $.grep(items, function(item){
  5620. var i, bak, hash, dfd, hi;
  5621. if (item.isDirectory && renames.length) {
  5622. i = $.inArray(item.name, renames);
  5623. if (i !== -1) {
  5624. renames.splice(i, 1);
  5625. bak = fm.uniqueName(item.name + fm.options.backupSuffix , null, '');
  5626. $.each(hashes, function(h, name) {
  5627. if (item.name == name) {
  5628. hash = h;
  5629. return false;
  5630. }
  5631. });
  5632. if (! hash) {
  5633. hash = fm.fileByName(item.name, target).hash;
  5634. }
  5635. fm.lockfiles({files : [hash]});
  5636. dfd = fm.request({
  5637. data : {cmd : 'rename', target : hash, name : bak},
  5638. notify : {type : 'rename', cnt : 1}
  5639. })
  5640. .fail(function() {
  5641. item._remove = true;
  5642. fm.sync();
  5643. })
  5644. .always(function() {
  5645. fm.unlockfiles({files : [hash]});
  5646. });
  5647. dfds.push(dfd);
  5648. }
  5649. }
  5650. return !item._remove? true : false;
  5651. });
  5652. }
  5653. $.when.apply($, dfds).done(function(){
  5654. var notifyto, msg,
  5655. id = +new Date();
  5656. if (items.length > 0) {
  5657. msg = fm.escape(items[0].name);
  5658. if (items.length > 1) {
  5659. msg += ' ... ' + items.length + fm.i18n('items');
  5660. }
  5661. notifyto = setTimeout(function() {
  5662. fm.notify({
  5663. type : 'readdir',
  5664. id : id,
  5665. cnt : 1,
  5666. hideCnt: true,
  5667. msg : fm.i18n('ntfreaddir') + ' (' + msg + ')',
  5668. cancel: function() {
  5669. cancel = true;
  5670. }
  5671. });
  5672. }, fm.options.notifyDelay);
  5673. doScan(items).done(function() {
  5674. notifyto && clearTimeout(notifyto);
  5675. fm.notify({type : 'readdir', id: id, cnt : -1});
  5676. if (cancel) {
  5677. dfrd.reject();
  5678. } else {
  5679. dfrd.resolve([files, paths, renames, hashes, mkdirs]);
  5680. }
  5681. }).fail(function() {
  5682. dfrd.reject();
  5683. });
  5684. } else {
  5685. dfrd.reject();
  5686. }
  5687. });
  5688. });
  5689. return dfrd.promise();
  5690. } else {
  5691. return dfrd.reject();
  5692. }
  5693. } else {
  5694. var ret = [];
  5695. var check = [];
  5696. var str = data.files[0];
  5697. if (data.type == 'html') {
  5698. var tmp = $("<html/>").append($.parseHTML(str.replace(/ src=/ig, ' _elfsrc='))),
  5699. atag;
  5700. $('img[_elfsrc]', tmp).each(function(){
  5701. var url, purl,
  5702. self = $(this),
  5703. pa = self.closest('a');
  5704. if (pa && pa.attr('href') && pa.attr('href').match(/\.(?:jpe?g|gif|bmp|png)/i)) {
  5705. purl = pa.attr('href');
  5706. }
  5707. url = self.attr('_elfsrc');
  5708. if (url) {
  5709. if (purl) {
  5710. $.inArray(purl, ret) == -1 && ret.push(purl);
  5711. $.inArray(url, check) == -1 && check.push(url);
  5712. } else {
  5713. $.inArray(url, ret) == -1 && ret.push(url);
  5714. }
  5715. }
  5716. // Probably it's clipboard data
  5717. if (ret.length === 1 && ret[0].match(/^data:image\/png/)) {
  5718. data.clipdata = true;
  5719. }
  5720. });
  5721. atag = $('a[href]', tmp);
  5722. atag.each(function(){
  5723. var text, loc,
  5724. parseUrl = function(url) {
  5725. var a = document.createElement('a');
  5726. a.href = url;
  5727. return a;
  5728. };
  5729. if (text = $(this).text()) {
  5730. loc = parseUrl($(this).attr('href'));
  5731. 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))) {
  5732. if ($.inArray(loc.href, ret) == -1 && $.inArray(loc.href, check) == -1) ret.push(loc.href);
  5733. }
  5734. }
  5735. });
  5736. } else {
  5737. var regex, m, url;
  5738. regex = /((?:ht|f)tps?:\/\/[-_.!~*\'()a-z0-9;/?:\@&=+\$,%#\*\[\]]+)/ig;
  5739. while (m = regex.exec(str)) {
  5740. url = m[1].replace(/&amp;/g, '&');
  5741. if ($.inArray(url, ret) == -1) ret.push(url);
  5742. }
  5743. }
  5744. return ret;
  5745. }
  5746. },
  5747. // upload transport using XMLHttpRequest
  5748. xhr : function(data, fm) {
  5749. var self = fm ? fm : this,
  5750. node = self.getUI(),
  5751. xhr = new XMLHttpRequest(),
  5752. notifyto = null, notifyto2 = null,
  5753. dataChecked = data.checked,
  5754. isDataType = (data.isDataType || data.type == 'data'),
  5755. target = (data.target || self.cwd().hash),
  5756. dropEvt = (data.dropEvt || null),
  5757. extraData = data.extraData || null,
  5758. chunkEnable = (self.option('uploadMaxConn', target) != -1),
  5759. multiMax = Math.min(5, Math.max(1, self.option('uploadMaxConn', target))),
  5760. retryWait = 10000, // 10 sec
  5761. retryMax = 30, // 10 sec * 30 = 300 secs (Max 5 mins)
  5762. retry = 0,
  5763. getFile = function(files) {
  5764. var dfd = $.Deferred(),
  5765. file;
  5766. if (files.promise) {
  5767. files.always(function(f) {
  5768. dfd.resolve(Array.isArray(f) && f.length? (isDataType? f[0][0] : f[0]) : {});
  5769. });
  5770. } else {
  5771. dfd.resolve(files.length? (isDataType? files[0][0] : files[0]) : {});
  5772. }
  5773. return dfd;
  5774. },
  5775. dfrd = $.Deferred()
  5776. .fail(function(err) {
  5777. var error = self.parseError(err),
  5778. userAbort;
  5779. if (error === 'userabort') {
  5780. userAbort = true;
  5781. error = void 0;
  5782. }
  5783. if (files && (self.uploads.xhrUploading || userAbort)) {
  5784. // send request om fail
  5785. getFile(files).done(function(file) {
  5786. if (!userAbort) {
  5787. triggerError(error, file);
  5788. }
  5789. if (! file._cid) {
  5790. // send sync request
  5791. self.uploads.failSyncTm && clearTimeout(self.uploads.failSyncTm);
  5792. self.uploads.failSyncTm = setTimeout(function() {
  5793. self.sync(target);
  5794. }, 1000);
  5795. } else if (! self.uploads.chunkfailReq[file._cid]) {
  5796. // send chunkfail request
  5797. self.uploads.chunkfailReq[file._cid] = true;
  5798. setTimeout(function() {
  5799. fm.request({
  5800. data : {
  5801. cmd: 'upload',
  5802. target: target,
  5803. chunk: file._chunk,
  5804. cid: file._cid,
  5805. upload: ['chunkfail'],
  5806. mimes: 'chunkfail'
  5807. },
  5808. options : {
  5809. type: 'post',
  5810. url: self.uploadURL
  5811. },
  5812. preventDefault: true
  5813. }).always(function() {
  5814. delete self.uploads.chunkfailReq[file._chunk];
  5815. });
  5816. }, 1000);
  5817. }
  5818. });
  5819. } else {
  5820. triggerError(error);
  5821. }
  5822. !userAbort && self.sync();
  5823. self.uploads.xhrUploading = false;
  5824. files = null;
  5825. })
  5826. .done(function(data) {
  5827. self.uploads.xhrUploading = false;
  5828. files = null;
  5829. if (data) {
  5830. self.currentReqCmd = 'upload';
  5831. data.warning && triggerError(data.warning);
  5832. self.updateCache(data);
  5833. data.removed && data.removed.length && self.remove(data);
  5834. data.added && data.added.length && self.add(data);
  5835. data.changed && data.changed.length && self.change(data);
  5836. self.trigger('upload', data, false);
  5837. self.trigger('uploaddone');
  5838. if (data.toasts && Array.isArray(data.toasts)) {
  5839. $.each(data.toasts, function() {
  5840. this.msg && self.toast(this);
  5841. });
  5842. }
  5843. data.sync && self.sync();
  5844. data.debug && fm.debug('backend-debug', data);
  5845. }
  5846. })
  5847. .always(function() {
  5848. self.abortXHR(xhr);
  5849. // unregist fnAbort function
  5850. node.off('uploadabort', fnAbort);
  5851. $(window).off('unload', fnAbort);
  5852. notifyto && clearTimeout(notifyto);
  5853. notifyto2 && clearTimeout(notifyto2);
  5854. dataChecked && !data.multiupload && checkNotify() && self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  5855. chunkMerge && notifyElm.children('.elfinder-notify-chunkmerge').length && self.notify({type : 'chunkmerge', cnt : -1});
  5856. }),
  5857. formData = new FormData(),
  5858. files = data.input ? data.input.files : self.uploads.checkFile(data, self, target),
  5859. cnt = data.checked? (isDataType? files[0].length : files.length) : files.length,
  5860. loaded = 0,
  5861. prev = 0,
  5862. filesize = 0,
  5863. notify = false,
  5864. notifyElm = self.ui.notify,
  5865. cancelBtn = true,
  5866. abort = false,
  5867. checkNotify = function() {
  5868. if (!notify && (ntfUpload = notifyElm.children('.elfinder-notify-upload')).length) {
  5869. notify = true;
  5870. }
  5871. return notify;
  5872. },
  5873. fnAbort = function(e, error) {
  5874. abort = true;
  5875. self.abortXHR(xhr, { quiet: true, abort: true });
  5876. dfrd.reject(error);
  5877. if (checkNotify()) {
  5878. self.notify({type : 'upload', cnt : ntfUpload.data('cnt') * -1, progress : 0, size : 0});
  5879. }
  5880. },
  5881. cancelToggle = function(show) {
  5882. ntfUpload.children('.elfinder-notify-cancel')[show? 'show':'hide']();
  5883. },
  5884. startNotify = function(size) {
  5885. if (!size) size = filesize;
  5886. return setTimeout(function() {
  5887. notify = true;
  5888. self.notify({type : 'upload', cnt : cnt, progress : loaded - prev, size : size,
  5889. cancel: function() {
  5890. node.trigger('uploadabort', 'userabort');
  5891. }
  5892. });
  5893. ntfUpload = notifyElm.children('.elfinder-notify-upload');
  5894. prev = loaded;
  5895. if (data.multiupload) {
  5896. cancelBtn && cancelToggle(true);
  5897. } else {
  5898. cancelToggle(cancelBtn && loaded < size);
  5899. }
  5900. }, self.options.notifyDelay);
  5901. },
  5902. doRetry = function() {
  5903. if (retry++ <= retryMax) {
  5904. if (checkNotify() && prev) {
  5905. self.notify({type : 'upload', cnt : 0, progress : 0, size : prev});
  5906. }
  5907. self.abortXHR(xhr, { quiet: true });
  5908. prev = loaded = 0;
  5909. setTimeout(function() {
  5910. var reqId;
  5911. if (! abort) {
  5912. xhr.open('POST', self.uploadURL, true);
  5913. if (self.api >= 2.1029) {
  5914. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
  5915. (typeof formData['delete'] === 'function') && formData['delete']('reqid');
  5916. formData.append('reqid', reqId);
  5917. xhr._requestId = reqId;
  5918. }
  5919. xhr.send(formData);
  5920. }
  5921. }, retryWait);
  5922. } else {
  5923. node.trigger('uploadabort', ['errAbort', 'errTimeout']);
  5924. }
  5925. },
  5926. progress = function() {
  5927. var node;
  5928. if (notify) {
  5929. dfrd.notifyWith(ntfUpload, [{
  5930. cnt: ntfUpload.data('cnt'),
  5931. progress: ntfUpload.data('progress'),
  5932. total: ntfUpload.data('total')
  5933. }]);
  5934. }
  5935. },
  5936. triggerError = function(err, file, unite) {
  5937. err && self.trigger('xhruploadfail', { error: err, file: file });
  5938. if (unite) {
  5939. if (err) {
  5940. if (errCnt < self.options.maxErrorDialogs) {
  5941. if (Array.isArray(err)) {
  5942. errors = errors.concat(err);
  5943. } else {
  5944. errors.push(err);
  5945. }
  5946. }
  5947. errCnt++;
  5948. }
  5949. } else {
  5950. if (err) {
  5951. self.error(err);
  5952. } else {
  5953. if (errors.length) {
  5954. if (errCnt >= self.options.maxErrorDialogs) {
  5955. errors = errors.concat('moreErrors', errCnt - self.options.maxErrorDialogs);
  5956. }
  5957. self.error(errors);
  5958. }
  5959. errors = [];
  5960. errCnt = 0;
  5961. }
  5962. }
  5963. },
  5964. errors = [],
  5965. errCnt = 0,
  5966. renames = (data.renames || null),
  5967. hashes = (data.hashes || null),
  5968. chunkMerge = false,
  5969. ntfUpload = $();
  5970. // regist fnAbort function
  5971. node.one('uploadabort', fnAbort);
  5972. $(window).one('unload.' + fm.namespace, fnAbort);
  5973. !chunkMerge && (prev = loaded);
  5974. if (!isDataType && !cnt) {
  5975. return dfrd.reject(['errUploadNoFiles']);
  5976. }
  5977. xhr.addEventListener('error', function() {
  5978. if (xhr.status == 0) {
  5979. if (abort) {
  5980. dfrd.reject();
  5981. } else {
  5982. // ff bug while send zero sized file
  5983. // for safari - send directory
  5984. if (!isDataType && data.files && $.grep(data.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) {
  5985. dfrd.reject(['errAbort', 'errFolderUpload']);
  5986. } else if (data.input && $.grep(data.input.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) {
  5987. dfrd.reject(['errUploadNoFiles']);
  5988. } else {
  5989. doRetry();
  5990. }
  5991. }
  5992. } else {
  5993. node.trigger('uploadabort', 'errConnect');
  5994. }
  5995. }, false);
  5996. xhr.addEventListener('load', function(e) {
  5997. var status = xhr.status, res, curr = 0, error = '', errData, errObj;
  5998. if (status >= 400) {
  5999. if (status > 500) {
  6000. error = 'errResponse';
  6001. } else {
  6002. error = ['errResponse', 'errServerError'];
  6003. }
  6004. } else {
  6005. if (!xhr.responseText) {
  6006. error = ['errResponse', 'errDataEmpty'];
  6007. }
  6008. }
  6009. if (error) {
  6010. node.trigger('uploadabort');
  6011. getFile(files).done(function(file) {
  6012. return dfrd.reject(file._cid? null : error);
  6013. });
  6014. }
  6015. loaded = filesize;
  6016. if (checkNotify() && (curr = loaded - prev)) {
  6017. self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
  6018. progress();
  6019. }
  6020. res = self.parseUploadData(xhr.responseText);
  6021. // chunked upload commit
  6022. if (res._chunkmerged) {
  6023. formData = new FormData();
  6024. var _file = [{_chunkmerged: res._chunkmerged, _name: res._name, _mtime: res._mtime}];
  6025. chunkMerge = true;
  6026. node.off('uploadabort', fnAbort);
  6027. notifyto2 = setTimeout(function() {
  6028. self.notify({type : 'chunkmerge', cnt : 1});
  6029. }, self.options.notifyDelay);
  6030. isDataType? send(_file, files[1]) : send(_file);
  6031. return;
  6032. }
  6033. res._multiupload = data.multiupload? true : false;
  6034. if (res.error) {
  6035. errData = {
  6036. cmd: 'upload',
  6037. err: res,
  6038. xhr: xhr,
  6039. rc: xhr.status
  6040. };
  6041. self.trigger('uploadfail', res);
  6042. // trigger "requestError" event
  6043. self.trigger('requestError', errData);
  6044. if (errData._event && errData._event.isDefaultPrevented()) {
  6045. res.error = '';
  6046. }
  6047. if (res._chunkfailure || res._multiupload) {
  6048. abort = true;
  6049. self.uploads.xhrUploading = false;
  6050. notifyto && clearTimeout(notifyto);
  6051. if (ntfUpload.length) {
  6052. self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  6053. dfrd.reject(res);
  6054. } else {
  6055. // for multi connection
  6056. dfrd.reject();
  6057. }
  6058. } else {
  6059. dfrd.reject(res);
  6060. }
  6061. } else {
  6062. dfrd.resolve(res);
  6063. }
  6064. }, false);
  6065. xhr.upload.addEventListener('loadstart', function(e) {
  6066. if (!chunkMerge && e.lengthComputable) {
  6067. loaded = e.loaded;
  6068. retry && (loaded = 0);
  6069. filesize = e.total;
  6070. if (!loaded) {
  6071. loaded = parseInt(filesize * 0.05);
  6072. }
  6073. if (checkNotify()) {
  6074. self.notify({type : 'upload', cnt : 0, progress : loaded - prev, size : data.multiupload? 0 : filesize});
  6075. prev = loaded;
  6076. progress();
  6077. }
  6078. }
  6079. }, false);
  6080. xhr.upload.addEventListener('progress', function(e) {
  6081. var curr;
  6082. if (e.lengthComputable && !chunkMerge && xhr.readyState < 2) {
  6083. loaded = e.loaded;
  6084. // to avoid strange bug in safari (not in chrome) with drag&drop.
  6085. // bug: macos finder opened in any folder,
  6086. // reset safari cache (option+command+e), reload elfinder page,
  6087. // drop file from finder
  6088. // on first attempt request starts (progress callback called ones) but never ends.
  6089. // any next drop - successfull.
  6090. if (!data.checked && loaded > 0 && !notifyto) {
  6091. notifyto = startNotify(xhr._totalSize - loaded);
  6092. }
  6093. if (!filesize) {
  6094. filesize = e.total;
  6095. if (!loaded) {
  6096. loaded = parseInt(filesize * 0.05);
  6097. }
  6098. }
  6099. curr = loaded - prev;
  6100. if (checkNotify() && (curr/e.total) >= 0.05) {
  6101. self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
  6102. prev = loaded;
  6103. progress();
  6104. }
  6105. if (! data.multiupload && loaded >= filesize) {
  6106. cancelBtn = false;
  6107. cancelToggle(false);
  6108. }
  6109. }
  6110. }, false);
  6111. var send = function(files, paths){
  6112. var size = 0,
  6113. fcnt = 1,
  6114. sfiles = [],
  6115. c = 0,
  6116. total = cnt,
  6117. maxFileSize,
  6118. totalSize = 0,
  6119. chunked = [],
  6120. chunkID = new Date().getTime().toString().substr(-9), // for take care of the 32bit backend system
  6121. BYTES_PER_CHUNK = Math.min((fm.uplMaxSize? fm.uplMaxSize : 2097152) - 8190, fm.options.uploadMaxChunkSize), // uplMaxSize margin 8kb or options.uploadMaxChunkSize
  6122. blobSlice = chunkEnable? false : '',
  6123. blobSize, blobMtime, blobName, i, start, end, chunks, blob, chunk, added, done, last, failChunk,
  6124. multi = function(files, num){
  6125. var sfiles = [], cid, sfilesLen = 0, cancelChk;
  6126. if (!abort) {
  6127. while(files.length && sfiles.length < num) {
  6128. sfiles.push(files.shift());
  6129. }
  6130. sfilesLen = sfiles.length;
  6131. if (sfilesLen) {
  6132. cancelChk = sfilesLen;
  6133. for (var i=0; i < sfilesLen; i++) {
  6134. if (abort) {
  6135. break;
  6136. }
  6137. cid = isDataType? (sfiles[i][0][0]._cid || null) : (sfiles[i][0]._cid || null);
  6138. if (!!failChunk[cid]) {
  6139. last--;
  6140. continue;
  6141. }
  6142. fm.exec('upload', {
  6143. type: data.type,
  6144. isDataType: isDataType,
  6145. files: sfiles[i],
  6146. checked: true,
  6147. target: target,
  6148. dropEvt: dropEvt,
  6149. renames: renames,
  6150. hashes: hashes,
  6151. multiupload: true,
  6152. overwrite: data.overwrite === 0? 0 : void 0,
  6153. clipdata: data.clipdata
  6154. }, void 0, target)
  6155. .fail(function(error) {
  6156. if (error && error === 'No such command') {
  6157. abort = true;
  6158. fm.error(['errUpload', 'errPerm']);
  6159. }
  6160. if (cid) {
  6161. failChunk[cid] = true;
  6162. }
  6163. })
  6164. .always(function(e) {
  6165. if (e && e.added) added = $.merge(added, e.added);
  6166. if (last <= ++done) {
  6167. fm.trigger('multiupload', {added: added});
  6168. notifyto && clearTimeout(notifyto);
  6169. if (checkNotify()) {
  6170. self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  6171. }
  6172. }
  6173. if (files.length) {
  6174. multi(files, 1); // Next one
  6175. } else {
  6176. if (--cancelChk <= 1) {
  6177. cancelBtn = false;
  6178. cancelToggle(false);
  6179. }
  6180. }
  6181. });
  6182. }
  6183. }
  6184. }
  6185. if (sfiles.length < 1 || abort) {
  6186. if (abort) {
  6187. notifyto && clearTimeout(notifyto);
  6188. if (cid) {
  6189. failChunk[cid] = true;
  6190. }
  6191. dfrd.reject();
  6192. } else {
  6193. dfrd.resolve();
  6194. self.uploads.xhrUploading = false;
  6195. }
  6196. }
  6197. },
  6198. check = function(){
  6199. if (!self.uploads.xhrUploading) {
  6200. self.uploads.xhrUploading = true;
  6201. multi(sfiles, multiMax); // Max connection: 3
  6202. } else {
  6203. setTimeout(check, 100);
  6204. }
  6205. },
  6206. reqId, err;
  6207. if (! dataChecked && (isDataType || data.type == 'files')) {
  6208. if (! (maxFileSize = fm.option('uploadMaxSize', target))) {
  6209. maxFileSize = 0;
  6210. }
  6211. for (i=0; i < files.length; i++) {
  6212. try {
  6213. blob = files[i];
  6214. blobSize = blob.size;
  6215. if (blobSlice === false) {
  6216. blobSlice = '';
  6217. if (self.api >= 2.1) {
  6218. if ('slice' in blob) {
  6219. blobSlice = 'slice';
  6220. } else if ('mozSlice' in blob) {
  6221. blobSlice = 'mozSlice';
  6222. } else if ('webkitSlice' in blob) {
  6223. blobSlice = 'webkitSlice';
  6224. }
  6225. }
  6226. }
  6227. } catch(e) {
  6228. cnt--;
  6229. total--;
  6230. continue;
  6231. }
  6232. // file size check
  6233. if ((maxFileSize && blobSize > maxFileSize) || (!blobSlice && fm.uplMaxSize && blobSize > fm.uplMaxSize)) {
  6234. triggerError(['errUploadFile', blob.name, 'errUploadFileSize'], blob, true);
  6235. cnt--;
  6236. total--;
  6237. continue;
  6238. }
  6239. // file mime check
  6240. if (blob.type && ! self.uploadMimeCheck(blob.type, target)) {
  6241. triggerError(['errUploadFile', blob.name, 'errUploadMime', '(' + blob.type + ')'], blob, true);
  6242. cnt--;
  6243. total--;
  6244. continue;
  6245. }
  6246. if (blobSlice && blobSize > BYTES_PER_CHUNK) {
  6247. start = 0;
  6248. end = BYTES_PER_CHUNK;
  6249. chunks = -1;
  6250. total = Math.floor((blobSize - 1) / BYTES_PER_CHUNK);
  6251. blobMtime = blob.lastModified? Math.round(blob.lastModified/1000) : 0;
  6252. blobName = data.clipdata? fm.date(fm.nonameDateFormat) + '.png' : blob.name;
  6253. totalSize += blobSize;
  6254. chunked[chunkID] = 0;
  6255. while(start < blobSize) {
  6256. chunk = blob[blobSlice](start, end);
  6257. chunk._chunk = blobName + '.' + (++chunks) + '_' + total + '.part';
  6258. chunk._cid = chunkID;
  6259. chunk._range = start + ',' + chunk.size + ',' + blobSize;
  6260. chunk._mtime = blobMtime;
  6261. chunked[chunkID]++;
  6262. if (size) {
  6263. c++;
  6264. }
  6265. if (typeof sfiles[c] == 'undefined') {
  6266. sfiles[c] = [];
  6267. if (isDataType) {
  6268. sfiles[c][0] = [];
  6269. sfiles[c][1] = [];
  6270. }
  6271. }
  6272. size = BYTES_PER_CHUNK;
  6273. fcnt = 1;
  6274. if (isDataType) {
  6275. sfiles[c][0].push(chunk);
  6276. sfiles[c][1].push(paths[i]);
  6277. } else {
  6278. sfiles[c].push(chunk);
  6279. }
  6280. start = end;
  6281. end = start + BYTES_PER_CHUNK;
  6282. }
  6283. if (chunk == null) {
  6284. triggerError(['errUploadFile', blob.name, 'errUploadFileSize'], blob, true);
  6285. cnt--;
  6286. total--;
  6287. } else {
  6288. total += chunks;
  6289. size = 0;
  6290. fcnt = 1;
  6291. c++;
  6292. }
  6293. continue;
  6294. }
  6295. if ((fm.uplMaxSize && size + blobSize > fm.uplMaxSize) || fcnt > fm.uplMaxFile) {
  6296. size = 0;
  6297. fcnt = 1;
  6298. c++;
  6299. }
  6300. if (typeof sfiles[c] == 'undefined') {
  6301. sfiles[c] = [];
  6302. if (isDataType) {
  6303. sfiles[c][0] = [];
  6304. sfiles[c][1] = [];
  6305. }
  6306. }
  6307. if (isDataType) {
  6308. sfiles[c][0].push(blob);
  6309. sfiles[c][1].push(paths[i]);
  6310. } else {
  6311. sfiles[c].push(blob);
  6312. }
  6313. size += blobSize;
  6314. totalSize += blobSize;
  6315. fcnt++;
  6316. }
  6317. if (errors.length) {
  6318. triggerError();
  6319. }
  6320. if (sfiles.length == 0) {
  6321. // no data
  6322. data.checked = true;
  6323. return false;
  6324. }
  6325. if (sfiles.length > 1) {
  6326. // multi upload
  6327. notifyto = startNotify(totalSize);
  6328. added = [];
  6329. done = 0;
  6330. last = sfiles.length;
  6331. failChunk = [];
  6332. check();
  6333. return true;
  6334. }
  6335. // single upload
  6336. if (isDataType) {
  6337. files = sfiles[0][0];
  6338. paths = sfiles[0][1];
  6339. } else {
  6340. files = sfiles[0];
  6341. }
  6342. }
  6343. if (!dataChecked) {
  6344. if (!fm.UA.Safari || !data.files) {
  6345. notifyto = startNotify(totalSize);
  6346. } else {
  6347. xhr._totalSize = totalSize;
  6348. }
  6349. }
  6350. dataChecked = true;
  6351. if (! files.length) {
  6352. dfrd.reject(['errUploadNoFiles']);
  6353. }
  6354. xhr.open('POST', self.uploadURL, true);
  6355. // set request headers
  6356. if (fm.customHeaders) {
  6357. $.each(fm.customHeaders, function(key) {
  6358. xhr.setRequestHeader(key, this);
  6359. });
  6360. }
  6361. // set xhrFields
  6362. if (fm.xhrFields) {
  6363. $.each(fm.xhrFields, function(key) {
  6364. if (key in xhr) {
  6365. xhr[key] = this;
  6366. }
  6367. });
  6368. }
  6369. if (self.api >= 2.1029) {
  6370. // request ID
  6371. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
  6372. formData.append('reqid', reqId);
  6373. xhr._requestId = reqId;
  6374. }
  6375. formData.append('cmd', 'upload');
  6376. formData.append(self.newAPI ? 'target' : 'current', target);
  6377. if (renames && renames.length) {
  6378. $.each(renames, function(i, v) {
  6379. formData.append('renames[]', v);
  6380. });
  6381. formData.append('suffix', fm.options.backupSuffix);
  6382. }
  6383. if (hashes) {
  6384. $.each(hashes, function(i, v) {
  6385. formData.append('hashes['+ i +']', v);
  6386. });
  6387. }
  6388. $.each(self.customData, function(key, val) {
  6389. formData.append(key, val);
  6390. });
  6391. $.each(self.options.onlyMimes, function(i, mime) {
  6392. formData.append('mimes[]', mime);
  6393. });
  6394. $.each(files, function(i, file) {
  6395. var name;
  6396. if (file._chunkmerged) {
  6397. formData.append('chunk', file._chunkmerged);
  6398. formData.append('upload[]', file._name);
  6399. formData.append('mtime[]', file._mtime);
  6400. data.clipdata && formData.append('overwrite', 0);
  6401. } else {
  6402. if (file._chunkfail) {
  6403. formData.append('upload[]', 'chunkfail');
  6404. formData.append('mimes', 'chunkfail');
  6405. } else {
  6406. if (data.clipdata) {
  6407. if (!file._chunk) {
  6408. data.overwrite = 0;
  6409. name = fm.date(fm.nonameDateFormat) + '.png';
  6410. }
  6411. } else {
  6412. if (file.name) {
  6413. name = file.name;
  6414. if (fm.UA.iOS) {
  6415. if (name.match(/^image\.jpe?g$/i)) {
  6416. data.overwrite = 0;
  6417. name = fm.date(fm.nonameDateFormat) + '.jpg';
  6418. } else if (name.match(/^capturedvideo\.mov$/i)) {
  6419. data.overwrite = 0;
  6420. name = fm.date(fm.nonameDateFormat) + '.mov';
  6421. }
  6422. }
  6423. }
  6424. }
  6425. name? formData.append('upload[]', file, name) : formData.append('upload[]', file);
  6426. }
  6427. if (file._chunk) {
  6428. formData.append('chunk', file._chunk);
  6429. formData.append('cid' , file._cid);
  6430. formData.append('range', file._range);
  6431. formData.append('mtime[]', file._mtime);
  6432. } else {
  6433. formData.append('mtime[]', file.lastModified? Math.round(file.lastModified/1000) : 0);
  6434. }
  6435. }
  6436. });
  6437. if (isDataType) {
  6438. $.each(paths, function(i, path) {
  6439. formData.append('upload_path[]', path);
  6440. });
  6441. }
  6442. if (data.overwrite === 0) {
  6443. formData.append('overwrite', 0);
  6444. }
  6445. // send int value that which meta key was pressed when dropped as `dropWith`
  6446. if (dropEvt) {
  6447. formData.append('dropWith', parseInt(
  6448. (dropEvt.altKey ? '1' : '0')+
  6449. (dropEvt.ctrlKey ? '1' : '0')+
  6450. (dropEvt.metaKey ? '1' : '0')+
  6451. (dropEvt.shiftKey? '1' : '0'), 2));
  6452. }
  6453. // set extraData on current request
  6454. if (extraData) {
  6455. $.each(extraData, function(key, val) {
  6456. formData.append(key, val);
  6457. });
  6458. }
  6459. xhr.send(formData);
  6460. return true;
  6461. };
  6462. if (! isDataType) {
  6463. if (files.length > 0) {
  6464. if (! data.clipdata && renames == null) {
  6465. var mkdirs = [],
  6466. paths = [],
  6467. excludes = fm.options.folderUploadExclude[fm.OS] || null;
  6468. $.each(files, function(i, file) {
  6469. var relPath = file.webkitRelativePath || file.relativePath || '',
  6470. idx, rootDir;
  6471. if (! relPath) {
  6472. return false;
  6473. }
  6474. if (excludes && file.name.match(excludes)) {
  6475. file._remove = true;
  6476. relPath = void(0);
  6477. } else {
  6478. // add '/' as prefix to make same to folder uploading with DnD, see #2607
  6479. relPath = '/' + relPath.replace(/\/[^\/]*$/, '').replace(/^\//, '');
  6480. if (relPath && $.inArray(relPath, mkdirs) === -1) {
  6481. mkdirs.push(relPath);
  6482. // checking the root directory to supports <input type="file" webkitdirectory> see #2378
  6483. idx = relPath.substr(1).indexOf('/');
  6484. if (idx !== -1 && (rootDir = relPath.substr(0, idx + 1)) && $.inArray(rootDir, mkdirs) === -1) {
  6485. mkdirs.unshift(rootDir);
  6486. }
  6487. }
  6488. }
  6489. paths.push(relPath);
  6490. });
  6491. renames = [];
  6492. hashes = {};
  6493. if (mkdirs.length) {
  6494. (function() {
  6495. var checkDirs = $.map(mkdirs, function(name) { return name.substr(1).indexOf('/') === -1 ? {name: name.substr(1)} : null;}),
  6496. cancelDirs = [];
  6497. fm.uploads.checkExists(checkDirs, target, fm, true).done(
  6498. function(res, res2) {
  6499. var dfds = [], dfd, bak, hash;
  6500. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  6501. cancelDirs = $.map(checkDirs, function(dir) { return dir._remove? dir.name : null ;} );
  6502. checkDirs = $.grep(checkDirs, function(dir) { return !dir._remove? true : false ;} );
  6503. }
  6504. if (cancelDirs.length) {
  6505. $.each(paths.concat(), function(i, path) {
  6506. if ($.inArray(path, cancelDirs) === 0) {
  6507. files[i]._remove = true;
  6508. paths[i] = void(0);
  6509. }
  6510. });
  6511. }
  6512. files = $.grep(files, function(file) { return file._remove? false : true; });
  6513. paths = $.grep(paths, function(path) { return path === void 0 ? false : true; });
  6514. if (checkDirs.length) {
  6515. dfd = $.Deferred();
  6516. if (res.length) {
  6517. $.each(res, function(i, existName) {
  6518. // backup
  6519. bak = fm.uniqueName(existName + fm.options.backupSuffix , null, '');
  6520. $.each(res2, function(h, name) {
  6521. if (res[0] == name) {
  6522. hash = h;
  6523. return false;
  6524. }
  6525. });
  6526. if (! hash) {
  6527. hash = fm.fileByName(res[0], target).hash;
  6528. }
  6529. fm.lockfiles({files : [hash]});
  6530. dfds.push(
  6531. fm.request({
  6532. data : {cmd : 'rename', target : hash, name : bak},
  6533. notify : {type : 'rename', cnt : 1}
  6534. })
  6535. .fail(function(error) {
  6536. dfrd.reject(error);
  6537. fm.sync();
  6538. })
  6539. .always(function() {
  6540. fm.unlockfiles({files : [hash]});
  6541. })
  6542. );
  6543. });
  6544. } else {
  6545. dfds.push(null);
  6546. }
  6547. $.when.apply($, dfds).done(function() {
  6548. // ensure directories
  6549. fm.request({
  6550. data : {cmd : 'mkdir', target : target, dirs : mkdirs},
  6551. notify : {type : 'mkdir', cnt : mkdirs.length},
  6552. preventFail: true
  6553. })
  6554. .fail(function(error) {
  6555. error = error || ['errUnknown'];
  6556. if (error[0] === 'errCmdParams') {
  6557. multiMax = 1;
  6558. } else {
  6559. multiMax = 0;
  6560. dfrd.reject(error);
  6561. }
  6562. })
  6563. .done(function(data) {
  6564. var rm = false;
  6565. if (!data.hashes) {
  6566. data.hashes = {};
  6567. }
  6568. paths = $.map(paths.concat(), function(p, i) {
  6569. if (p === '/') {
  6570. return target;
  6571. } else {
  6572. if (data.hashes[p]) {
  6573. return data.hashes[p];
  6574. } else {
  6575. rm = true;
  6576. files[i]._remove = true;
  6577. return null;
  6578. }
  6579. }
  6580. });
  6581. if (rm) {
  6582. files = $.grep(files, function(file) { return file._remove? false : true; });
  6583. }
  6584. })
  6585. .always(function(data) {
  6586. if (multiMax) {
  6587. isDataType = true;
  6588. if (! send(files, paths)) {
  6589. dfrd.reject();
  6590. }
  6591. }
  6592. });
  6593. });
  6594. } else {
  6595. dfrd.reject();
  6596. }
  6597. }
  6598. );
  6599. })();
  6600. } else {
  6601. fm.uploads.checkExists(files, target, fm).done(
  6602. function(res, res2){
  6603. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  6604. hashes = res2;
  6605. if (res === null) {
  6606. data.overwrite = 0;
  6607. } else {
  6608. renames = res;
  6609. }
  6610. files = $.grep(files, function(file){return !file._remove? true : false ;});
  6611. }
  6612. cnt = files.length;
  6613. if (cnt > 0) {
  6614. if (! send(files)) {
  6615. dfrd.reject();
  6616. }
  6617. } else {
  6618. dfrd.reject();
  6619. }
  6620. }
  6621. );
  6622. }
  6623. } else {
  6624. if (! send(files)) {
  6625. dfrd.reject();
  6626. }
  6627. }
  6628. } else {
  6629. dfrd.reject();
  6630. }
  6631. } else {
  6632. if (dataChecked) {
  6633. send(files[0], files[1]);
  6634. } else {
  6635. files.done(function(result) { // result: [files, paths, renames, hashes, mkdirs]
  6636. renames = [];
  6637. cnt = result[0].length;
  6638. if (cnt) {
  6639. if (result[4] && result[4].length) {
  6640. // ensure directories
  6641. fm.request({
  6642. data : {cmd : 'mkdir', target : target, dirs : result[4]},
  6643. notify : {type : 'mkdir', cnt : result[4].length},
  6644. preventFail: true
  6645. })
  6646. .fail(function(error) {
  6647. error = error || ['errUnknown'];
  6648. if (error[0] === 'errCmdParams') {
  6649. multiMax = 1;
  6650. } else {
  6651. multiMax = 0;
  6652. dfrd.reject(error);
  6653. }
  6654. })
  6655. .done(function(data) {
  6656. var rm = false;
  6657. if (!data.hashes) {
  6658. data.hashes = {};
  6659. }
  6660. result[1] = $.map(result[1], function(p, i) {
  6661. p = p.replace(/\/[^\/]*$/, '');
  6662. if (p === '') {
  6663. return target;
  6664. } else {
  6665. if (data.hashes[p]) {
  6666. return data.hashes[p];
  6667. } else {
  6668. rm = true;
  6669. result[0][i]._remove = true;
  6670. return null;
  6671. }
  6672. }
  6673. });
  6674. if (rm) {
  6675. result[0] = $.grep(result[0], function(file) { return file._remove? false : true; });
  6676. }
  6677. })
  6678. .always(function(data) {
  6679. if (multiMax) {
  6680. renames = result[2];
  6681. hashes = result[3];
  6682. send(result[0], result[1]);
  6683. }
  6684. });
  6685. return;
  6686. } else {
  6687. result[1] = $.map(result[1], function() { return target; });
  6688. }
  6689. renames = result[2];
  6690. hashes = result[3];
  6691. send(result[0], result[1]);
  6692. } else {
  6693. dfrd.reject(['errUploadNoFiles']);
  6694. }
  6695. }).fail(function(){
  6696. dfrd.reject();
  6697. });
  6698. }
  6699. }
  6700. return dfrd;
  6701. },
  6702. // upload transport using iframe
  6703. iframe : function(data, fm) {
  6704. var self = fm ? fm : this,
  6705. input = data.input? data.input : false,
  6706. files = !input ? self.uploads.checkFile(data, self) : false,
  6707. dfrd = $.Deferred()
  6708. .fail(function(error) {
  6709. error && self.error(error);
  6710. }),
  6711. name = 'iframe-'+fm.namespace+(++self.iframeCnt),
  6712. 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>'),
  6713. msie = this.UA.IE,
  6714. // clear timeouts, close notification dialog, remove form/iframe
  6715. onload = function() {
  6716. abortto && clearTimeout(abortto);
  6717. notifyto && clearTimeout(notifyto);
  6718. notify && self.notify({type : 'upload', cnt : -cnt});
  6719. setTimeout(function() {
  6720. msie && $('<iframe src="javascript:false;"/>').appendTo(form);
  6721. form.remove();
  6722. iframe.remove();
  6723. }, 100);
  6724. },
  6725. iframe = $('<iframe src="'+(msie ? 'javascript:false;' : 'about:blank')+'" name="'+name+'" style="position:absolute;left:-1000px;top:-1000px" />')
  6726. .on('load', function() {
  6727. iframe.off('load')
  6728. .on('load', function() {
  6729. onload();
  6730. // data will be processed in callback response or window onmessage
  6731. dfrd.resolve();
  6732. });
  6733. // notify dialog
  6734. notifyto = setTimeout(function() {
  6735. notify = true;
  6736. self.notify({type : 'upload', cnt : cnt});
  6737. }, self.options.notifyDelay);
  6738. // emulate abort on timeout
  6739. if (self.options.iframeTimeout > 0) {
  6740. abortto = setTimeout(function() {
  6741. onload();
  6742. dfrd.reject(['errConnect', 'errTimeout']);
  6743. }, self.options.iframeTimeout);
  6744. }
  6745. form.submit();
  6746. }),
  6747. target = (data.target || self.cwd().hash),
  6748. names = [],
  6749. dfds = [],
  6750. renames = [],
  6751. hashes = {},
  6752. cnt, notify, notifyto, abortto;
  6753. if (files && files.length) {
  6754. $.each(files, function(i, val) {
  6755. form.append('<input type="hidden" name="upload[]" value="'+val+'"/>');
  6756. });
  6757. cnt = 1;
  6758. } else if (input && $(input).is(':file') && $(input).val()) {
  6759. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  6760. names = input.files? input.files : [{ name: $(input).val().replace(/^(?:.+[\\\/])?([^\\\/]+)$/, '$1') }];
  6761. //names = $.map(names, function(file){return file.name? { name: file.name } : null ;});
  6762. dfds.push(self.uploads.checkExists(names, target, self).done(
  6763. function(res, res2){
  6764. hashes = res2;
  6765. if (res === null) {
  6766. data.overwrite = 0;
  6767. } else{
  6768. renames = res;
  6769. cnt = $.grep(names, function(file){return !file._remove? true : false ;}).length;
  6770. if (cnt != names.length) {
  6771. cnt = 0;
  6772. }
  6773. }
  6774. }
  6775. ));
  6776. }
  6777. cnt = input.files ? input.files.length : 1;
  6778. form.append(input);
  6779. } else {
  6780. return dfrd.reject();
  6781. }
  6782. $.when.apply($, dfds).done(function() {
  6783. if (cnt < 1) {
  6784. return dfrd.reject();
  6785. }
  6786. form.append('<input type="hidden" name="'+(self.newAPI ? 'target' : 'current')+'" value="'+target+'"/>')
  6787. .append('<input type="hidden" name="html" value="1"/>')
  6788. .append('<input type="hidden" name="node" value="'+self.id+'"/>')
  6789. .append($(input).attr('name', 'upload[]'));
  6790. if (renames.length > 0) {
  6791. $.each(renames, function(i, rename) {
  6792. form.append('<input type="hidden" name="renames[]" value="'+self.escape(rename)+'"/>');
  6793. });
  6794. form.append('<input type="hidden" name="suffix" value="'+fm.options.backupSuffix+'"/>');
  6795. }
  6796. if (hashes) {
  6797. $.each(renames, function(i, v) {
  6798. form.append('<input type="hidden" name="['+i+']" value="'+self.escape(v)+'"/>');
  6799. });
  6800. }
  6801. if (data.overwrite === 0) {
  6802. form.append('<input type="hidden" name="overwrite" value="0"/>');
  6803. }
  6804. $.each(self.options.onlyMimes||[], function(i, mime) {
  6805. form.append('<input type="hidden" name="mimes[]" value="'+self.escape(mime)+'"/>');
  6806. });
  6807. $.each(self.customData, function(key, val) {
  6808. form.append('<input type="hidden" name="'+key+'" value="'+self.escape(val)+'"/>');
  6809. });
  6810. form.appendTo('body');
  6811. iframe.appendTo('body');
  6812. });
  6813. return dfrd;
  6814. }
  6815. },
  6816. /**
  6817. * Bind callback to event(s) The callback is executed at most once per event.
  6818. * To bind to multiply events at once, separate events names by space
  6819. *
  6820. * @param String event name
  6821. * @param Function callback
  6822. * @param Boolan priority first
  6823. * @return elFinder
  6824. */
  6825. one : function(ev, callback, priorityFirst) {
  6826. var self = this,
  6827. event = ev.toLowerCase(),
  6828. h = function(e, f) {
  6829. if (!self.toUnbindEvents[event]) {
  6830. self.toUnbindEvents[event] = [];
  6831. }
  6832. self.toUnbindEvents[event].push({
  6833. type: event,
  6834. callback: h
  6835. });
  6836. return (callback.done? callback.done : callback).apply(this, arguments);
  6837. };
  6838. if (callback.done) {
  6839. h = {done: h};
  6840. }
  6841. return this.bind(event, h, priorityFirst);
  6842. },
  6843. /**
  6844. * Set/get data into/from localStorage
  6845. *
  6846. * @param String key
  6847. * @param String|void value
  6848. * @return String|null
  6849. */
  6850. localStorage : function(key, val) {
  6851. var self = this,
  6852. s = window.localStorage,
  6853. oldkey = 'elfinder-'+(key || '')+this.id, // old key of elFinder < 2.1.6
  6854. prefix = window.location.pathname+'-elfinder-',
  6855. suffix = this.id,
  6856. clrs = [],
  6857. retval, oldval, t, precnt, sufcnt;
  6858. // reset this node data
  6859. if (typeof(key) === 'undefined') {
  6860. precnt = prefix.length;
  6861. sufcnt = suffix.length * -1;
  6862. $.each(s, function(key) {
  6863. if (key.substr(0, precnt) === prefix && key.substr(sufcnt) === suffix) {
  6864. clrs.push(key);
  6865. }
  6866. });
  6867. $.each(clrs, function(i, key) {
  6868. s.removeItem(key);
  6869. });
  6870. return true;
  6871. }
  6872. // new key of elFinder >= 2.1.6
  6873. key = prefix+key+suffix;
  6874. if (val === null) {
  6875. return s.removeItem(key);
  6876. }
  6877. if (val === void(0) && !(retval = s.getItem(key)) && (oldval = s.getItem(oldkey))) {
  6878. val = oldval;
  6879. s.removeItem(oldkey);
  6880. }
  6881. if (val !== void(0)) {
  6882. t = typeof val;
  6883. if (t !== 'string' && t !== 'number') {
  6884. val = JSON.stringify(val);
  6885. }
  6886. try {
  6887. s.setItem(key, val);
  6888. } catch (e) {
  6889. try {
  6890. s.clear();
  6891. s.setItem(key, val);
  6892. } catch (e) {
  6893. self.debug('error', e.toString());
  6894. }
  6895. }
  6896. retval = s.getItem(key);
  6897. }
  6898. if (retval && (retval.substr(0,1) === '{' || retval.substr(0,1) === '[')) {
  6899. try {
  6900. return JSON.parse(retval);
  6901. } catch(e) {}
  6902. }
  6903. return retval;
  6904. },
  6905. /**
  6906. * Get/set cookie
  6907. *
  6908. * @param String cookie name
  6909. * @param String|void cookie value
  6910. * @return String|null
  6911. */
  6912. cookie : function(name, value) {
  6913. var d, o, c, i, retval, t;
  6914. name = 'elfinder-'+name+this.id;
  6915. if (value === void(0)) {
  6916. if (document.cookie && document.cookie != '') {
  6917. c = document.cookie.split(';');
  6918. name += '=';
  6919. for (i=0; i<c.length; i++) {
  6920. c[i] = $.trim(c[i]);
  6921. if (c[i].substring(0, name.length) == name) {
  6922. retval = decodeURIComponent(c[i].substring(name.length));
  6923. if (retval.substr(0,1) === '{' || retval.substr(0,1) === '[') {
  6924. try {
  6925. return JSON.parse(retval);
  6926. } catch(e) {}
  6927. }
  6928. return retval;
  6929. }
  6930. }
  6931. }
  6932. return null;
  6933. }
  6934. o = Object.assign({}, this.options.cookie);
  6935. if (value === null) {
  6936. value = '';
  6937. o.expires = -1;
  6938. } else {
  6939. t = typeof value;
  6940. if (t !== 'string' && t !== 'number') {
  6941. value = JSON.stringify(value);
  6942. }
  6943. }
  6944. if (typeof(o.expires) == 'number') {
  6945. d = new Date();
  6946. d.setTime(d.getTime()+(o.expires * 86400000));
  6947. o.expires = d;
  6948. }
  6949. document.cookie = name+'='+encodeURIComponent(value)+'; expires='+o.expires.toUTCString()+(o.path ? '; path='+o.path : '')+(o.domain ? '; domain='+o.domain : '')+(o.secure ? '; secure' : '');
  6950. if (value && (value.substr(0,1) === '{' || value.substr(0,1) === '[')) {
  6951. try {
  6952. return JSON.parse(value);
  6953. } catch(e) {}
  6954. }
  6955. return value;
  6956. },
  6957. /**
  6958. * Get start directory (by location.hash or last opened directory)
  6959. *
  6960. * @return String
  6961. */
  6962. startDir : function() {
  6963. var locHash = window.location.hash;
  6964. if (locHash && locHash.match(/^#elf_/)) {
  6965. return locHash.replace(/^#elf_/, '');
  6966. } else if (this.options.startPathHash) {
  6967. return this.options.startPathHash;
  6968. } else {
  6969. return this.lastDir();
  6970. }
  6971. },
  6972. /**
  6973. * Get/set last opened directory
  6974. *
  6975. * @param String|undefined dir hash
  6976. * @return String
  6977. */
  6978. lastDir : function(hash) {
  6979. return this.options.rememberLastDir ? this.storage('lastdir', hash) : '';
  6980. },
  6981. /**
  6982. * Node for escape html entities in texts
  6983. *
  6984. * @type jQuery
  6985. */
  6986. _node : $('<span/>'),
  6987. /**
  6988. * Replace not html-safe symbols to html entities
  6989. *
  6990. * @param String text to escape
  6991. * @return String
  6992. */
  6993. escape : function(name) {
  6994. return this._node.text(name).html().replace(/"/g, '&quot;').replace(/'/g, '&#039;');
  6995. },
  6996. /**
  6997. * Cleanup ajax data.
  6998. * For old api convert data into new api format
  6999. *
  7000. * @param String command name
  7001. * @param Object data from backend
  7002. * @return Object
  7003. */
  7004. normalize : function(data) {
  7005. var self = this,
  7006. fileFilter = (function() {
  7007. var func, filter;
  7008. if (filter = self.options.fileFilter) {
  7009. if (typeof filter === 'function') {
  7010. func = function(file) {
  7011. return filter.call(self, file);
  7012. };
  7013. } else if (filter instanceof RegExp) {
  7014. func = function(file) {
  7015. return filter.test(file.name);
  7016. };
  7017. }
  7018. }
  7019. return func? func : null;
  7020. })(),
  7021. chkCmdMap = function(opts) {
  7022. // Disable command to replace with other command
  7023. var disabled;
  7024. if (opts.uiCmdMap) {
  7025. if ($.isPlainObject(opts.uiCmdMap) && Object.keys(opts.uiCmdMap).length) {
  7026. if (!opts.disabledFlip) {
  7027. opts.disabledFlip = {};
  7028. }
  7029. disabled = opts.disabledFlip;
  7030. $.each(opts.uiCmdMap, function(f, t) {
  7031. if (t === 'hidden' && !disabled[f]) {
  7032. opts.disabled.push(f);
  7033. opts.disabledFlip[f] = true;
  7034. }
  7035. });
  7036. } else {
  7037. delete opts.uiCmdMap;
  7038. }
  7039. }
  7040. },
  7041. normalizeOptions = function(opts) {
  7042. var getType = function(v) {
  7043. var type = typeof v;
  7044. if (type === 'object' && Array.isArray(v)) {
  7045. type = 'array';
  7046. }
  7047. return type;
  7048. };
  7049. $.each(self.optionProperties, function(k, empty) {
  7050. if (empty !== void(0)) {
  7051. if (opts[k] && getType(opts[k]) !== getType(empty)) {
  7052. opts[k] = empty;
  7053. }
  7054. }
  7055. });
  7056. if (opts['disabled']) {
  7057. opts['disabledFlip'] = self.arrayFlip(opts['disabled'], true);
  7058. } else {
  7059. opts['disabledFlip'] = {};
  7060. }
  7061. return opts;
  7062. },
  7063. filter = function(file, asMap, type) {
  7064. var res = asMap? file : true,
  7065. ign = asMap? null : false,
  7066. vid, targetOptions, isRoot, rootNames;
  7067. if (file && file.hash && file.name && file.mime) {
  7068. if (file.mime === 'application/x-empty') {
  7069. file.mime = 'text/plain';
  7070. }
  7071. isRoot = self.isRoot(file);
  7072. if (isRoot && ! file.volumeid) {
  7073. self.debug('warning', 'The volume root statuses requires `volumeid` property.');
  7074. }
  7075. if (isRoot || file.mime === 'directory') {
  7076. // Prevention of circular reference
  7077. if (file.phash) {
  7078. if (file.phash === file.hash) {
  7079. error = error.concat(['Parent folder of "$1" is itself.', file.name]);
  7080. return ign;
  7081. }
  7082. if (isRoot && file.volumeid && file.phash.indexOf(file.volumeid) === 0) {
  7083. error = error.concat(['Parent folder of "$1" is inner itself.', file.name]);
  7084. return ign;
  7085. }
  7086. }
  7087. // set options, tmbUrls for each volume
  7088. if (file.volumeid) {
  7089. vid = file.volumeid;
  7090. if (isRoot) {
  7091. // make or update of leaf roots cache
  7092. if (file.phash) {
  7093. if (! self.leafRoots[file.phash]) {
  7094. self.leafRoots[file.phash] = [ file.hash ];
  7095. } else {
  7096. if ($.inArray(file.hash, self.leafRoots[file.phash]) === -1) {
  7097. self.leafRoots[file.phash].push(file.hash);
  7098. }
  7099. }
  7100. }
  7101. self.hasVolOptions = true;
  7102. if (! self.volOptions[vid]) {
  7103. self.volOptions[vid] = {
  7104. // set dispInlineRegex
  7105. dispInlineRegex: self.options.dispInlineRegex
  7106. };
  7107. }
  7108. targetOptions = self.volOptions[vid];
  7109. if (file.options) {
  7110. // >= v.2.1.14 has file.options
  7111. Object.assign(targetOptions, file.options);
  7112. }
  7113. // for compat <= v2.1.13
  7114. if (file.disabled) {
  7115. targetOptions.disabled = file.disabled;
  7116. targetOptions.disabledFlip = self.arrayFlip(file.disabled, true);
  7117. }
  7118. if (file.tmbUrl) {
  7119. targetOptions.tmbUrl = file.tmbUrl;
  7120. }
  7121. // '/' required at the end of url
  7122. if (targetOptions.url && targetOptions.url.substr(-1) !== '/') {
  7123. targetOptions.url += '/';
  7124. }
  7125. // check uiCmdMap
  7126. chkCmdMap(targetOptions);
  7127. // check trash bin hash
  7128. if (targetOptions.trashHash) {
  7129. if (self.trashes[targetOptions.trashHash] === false) {
  7130. delete targetOptions.trashHash;
  7131. } else {
  7132. self.trashes[targetOptions.trashHash] = file.hash;
  7133. }
  7134. }
  7135. // set immediate properties
  7136. $.each(self.optionProperties, function(k) {
  7137. if (targetOptions[k]) {
  7138. file[k] = targetOptions[k];
  7139. }
  7140. });
  7141. // regist fm.roots
  7142. if (type !== 'cwd') {
  7143. self.roots[vid] = file.hash;
  7144. }
  7145. // regist fm.volumeExpires
  7146. if (file.expires) {
  7147. self.volumeExpires[vid] = file.expires;
  7148. }
  7149. }
  7150. if (prevId !== vid) {
  7151. prevId = vid;
  7152. i18nFolderName = self.option('i18nFolderName', vid);
  7153. }
  7154. }
  7155. // volume root i18n name
  7156. if (isRoot && ! file.i18) {
  7157. name = 'volume_' + file.name,
  7158. i18 = self.i18n(false, name);
  7159. if (name !== i18) {
  7160. file.i18 = i18;
  7161. }
  7162. }
  7163. // i18nFolderName
  7164. if (i18nFolderName && ! file.i18) {
  7165. name = 'folder_' + file.name,
  7166. i18 = self.i18n(false, name);
  7167. if (name !== i18) {
  7168. file.i18 = i18;
  7169. }
  7170. }
  7171. if (isRoot) {
  7172. if (rootNames = self.storage('rootNames')) {
  7173. if (rootNames[file.hash]) {
  7174. file._name = file.name;
  7175. file._i18 = file.i18;
  7176. file.name = rootNames[file.hash] = rootNames[file.hash];
  7177. delete file.i18;
  7178. }
  7179. self.storage('rootNames', rootNames);
  7180. }
  7181. }
  7182. // lock trash bins holder
  7183. if (self.trashes[file.hash]) {
  7184. file.locked = true;
  7185. }
  7186. } else {
  7187. if (fileFilter) {
  7188. try {
  7189. if (! fileFilter(file)) {
  7190. return ign;
  7191. }
  7192. } catch(e) {
  7193. self.debug(e);
  7194. }
  7195. }
  7196. if (file.size == 0) {
  7197. file.mime = self.getMimetype(file.name, file.mime);
  7198. }
  7199. }
  7200. if (file.options) {
  7201. self.optionsByHashes[file.hash] = normalizeOptions(file.options);
  7202. }
  7203. delete file.options;
  7204. return res;
  7205. }
  7206. return ign;
  7207. },
  7208. getDescendants = function(hashes) {
  7209. var res = [];
  7210. $.each(self.files(), function(h, f) {
  7211. $.each(self.parents(h), function(i, ph) {
  7212. if ($.inArray(ph, hashes) !== -1 && $.inArray(h, hashes) === -1) {
  7213. res.push(h);
  7214. return false;
  7215. }
  7216. });
  7217. });
  7218. return res;
  7219. },
  7220. applyLeafRootStats = function(dataArr, type) {
  7221. $.each(dataArr, function(i, f) {
  7222. var pfile, done;
  7223. if (self.leafRoots[f.hash]) {
  7224. self.applyLeafRootStats(f);
  7225. }
  7226. // update leaf root parent stat
  7227. if (type !== 'change' && f.phash && self.isRoot(f) && (pfile = self.file(f.phash))) {
  7228. self.applyLeafRootStats(pfile);
  7229. // add to data.changed
  7230. if (!data.changed) {
  7231. data.changed = [pfile];
  7232. } else {
  7233. $.each(data.changed, function(i, f) {
  7234. if (f.hash === pfile.hash) {
  7235. data.changed[i] = pfile;
  7236. done = true;
  7237. return false;
  7238. }
  7239. });
  7240. if (!done) {
  7241. data.changed.push(pfile);
  7242. }
  7243. }
  7244. }
  7245. });
  7246. },
  7247. error = [],
  7248. name, i18, i18nFolderName, prevId, cData;
  7249. // set cunstom data
  7250. if (data.customData && data.customData !== self.prevCustomData) {
  7251. self.prevCustomData = data.customData;
  7252. try {
  7253. cData = JSON.parse(data.customData);
  7254. if ($.isPlainObject(cData)) {
  7255. self.prevCustomData = cData;
  7256. $.each(Object.keys(cData), function(i, key) {
  7257. if (cData[key] === null) {
  7258. delete cData[key];
  7259. delete self.optsCustomData[key];
  7260. }
  7261. });
  7262. self.customData = Object.assign({}, self.optsCustomData, cData);
  7263. }
  7264. } catch(e) {}
  7265. }
  7266. if (data.options) {
  7267. normalizeOptions(data.options);
  7268. }
  7269. if (data.cwd) {
  7270. if (data.cwd.volumeid && data.options && Object.keys(data.options).length && self.isRoot(data.cwd)) {
  7271. self.hasVolOptions = true;
  7272. self.volOptions[data.cwd.volumeid] = data.options;
  7273. }
  7274. data.cwd = filter(data.cwd, true, 'cwd');
  7275. }
  7276. if (data.files) {
  7277. data.files = $.grep(data.files, filter);
  7278. }
  7279. if (data.tree) {
  7280. data.tree = $.grep(data.tree, filter);
  7281. }
  7282. if (data.added) {
  7283. data.added = $.grep(data.added, filter);
  7284. }
  7285. if (data.changed) {
  7286. data.changed = $.grep(data.changed, filter);
  7287. }
  7288. if (data.removed && data.removed.length && self.searchStatus.state === 2) {
  7289. data.removed = data.removed.concat(getDescendants(data.removed));
  7290. }
  7291. if (data.api) {
  7292. data.init = true;
  7293. }
  7294. if (Object.keys(self.leafRoots).length) {
  7295. data.files && applyLeafRootStats(data.files);
  7296. data.tree && applyLeafRootStats(data.tree);
  7297. data.added && applyLeafRootStats(data.added);
  7298. data.changed && applyLeafRootStats(data.changed, 'change');
  7299. }
  7300. // merge options that apply only to cwd
  7301. if (data.cwd && data.cwd.options && data.options) {
  7302. Object.assign(data.options, normalizeOptions(data.cwd.options));
  7303. }
  7304. // '/' required at the end of url
  7305. if (data.options && data.options.url && data.options.url.substr(-1) !== '/') {
  7306. data.options.url += '/';
  7307. }
  7308. // check error
  7309. if (error.length) {
  7310. data.norError = ['errResponse'].concat(error);
  7311. }
  7312. return data;
  7313. },
  7314. /**
  7315. * Update sort options
  7316. *
  7317. * @param {String} sort type
  7318. * @param {String} sort order
  7319. * @param {Boolean} show folder first
  7320. */
  7321. setSort : function(type, order, stickFolders, alsoTreeview) {
  7322. this.storage('sortType', (this.sortType = this.sortRules[type] ? type : 'name'));
  7323. this.storage('sortOrder', (this.sortOrder = /asc|desc/.test(order) ? order : 'asc'));
  7324. this.storage('sortStickFolders', (this.sortStickFolders = !!stickFolders) ? 1 : '');
  7325. this.storage('sortAlsoTreeview', (this.sortAlsoTreeview = !!alsoTreeview) ? 1 : '');
  7326. this.trigger('sortchange');
  7327. },
  7328. _sortRules : {
  7329. name : function(file1, file2) {
  7330. return elFinder.prototype.naturalCompare(file1.i18 || file1.name, file2.i18 || file2.name);
  7331. },
  7332. size : function(file1, file2) {
  7333. var size1 = parseInt(file1.size) || 0,
  7334. size2 = parseInt(file2.size) || 0;
  7335. return size1 === size2 ? 0 : size1 > size2 ? 1 : -1;
  7336. },
  7337. kind : function(file1, file2) {
  7338. return elFinder.prototype.naturalCompare(file1.mime, file2.mime);
  7339. },
  7340. date : function(file1, file2) {
  7341. var date1 = file1.ts || file1.date || 0,
  7342. date2 = file2.ts || file2.date || 0;
  7343. return date1 === date2 ? 0 : date1 > date2 ? 1 : -1;
  7344. },
  7345. perm : function(file1, file2) {
  7346. var val = function(file) { return (file.write? 2 : 0) + (file.read? 1 : 0); },
  7347. v1 = val(file1),
  7348. v2 = val(file2);
  7349. return v1 === v2 ? 0 : v1 > v2 ? 1 : -1;
  7350. },
  7351. mode : function(file1, file2) {
  7352. var v1 = file1.mode || (file1.perm || ''),
  7353. v2 = file2.mode || (file2.perm || '');
  7354. return elFinder.prototype.naturalCompare(v1, v2);
  7355. },
  7356. owner : function(file1, file2) {
  7357. var v1 = file1.owner || '',
  7358. v2 = file2.owner || '';
  7359. return elFinder.prototype.naturalCompare(v1, v2);
  7360. },
  7361. group : function(file1, file2) {
  7362. var v1 = file1.group || '',
  7363. v2 = file2.group || '';
  7364. return elFinder.prototype.naturalCompare(v1, v2);
  7365. }
  7366. },
  7367. /**
  7368. * Valid sort rule names
  7369. *
  7370. * @type Object
  7371. */
  7372. sorters : {},
  7373. /**
  7374. * Compare strings for natural sort
  7375. *
  7376. * @param String
  7377. * @param String
  7378. * @return Number
  7379. */
  7380. naturalCompare : function(a, b) {
  7381. var self = elFinder.prototype.naturalCompare;
  7382. if (typeof self.loc == 'undefined') {
  7383. self.loc = (navigator.userLanguage || navigator.browserLanguage || navigator.language || 'en-US');
  7384. }
  7385. if (typeof self.sort == 'undefined') {
  7386. if ('11'.localeCompare('2', self.loc, {numeric: true}) > 0) {
  7387. // Native support
  7388. if (window.Intl && window.Intl.Collator) {
  7389. self.sort = new Intl.Collator(self.loc, {numeric: true}).compare;
  7390. } else {
  7391. self.sort = function(a, b) {
  7392. return a.localeCompare(b, self.loc, {numeric: true});
  7393. };
  7394. }
  7395. } else {
  7396. /*
  7397. * Edited for elFinder (emulates localeCompare() by numeric) by Naoki Sawada aka nao-pon
  7398. */
  7399. /*
  7400. * Huddle/javascript-natural-sort (https://github.com/Huddle/javascript-natural-sort)
  7401. */
  7402. /*
  7403. * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
  7404. * Author: Jim Palmer (based on chunking idea from Dave Koelle)
  7405. * http://opensource.org/licenses/mit-license.php
  7406. */
  7407. self.sort = function(a, b) {
  7408. var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
  7409. sre = /(^[ ]*|[ ]*$)/g,
  7410. dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
  7411. hre = /^0x[0-9a-f]+$/i,
  7412. ore = /^0/,
  7413. syre = /^[\x01\x21-\x2f\x3a-\x40\x5b-\x60\x7b-\x7e]/, // symbol first - (Naoki Sawada)
  7414. i = function(s) { return self.sort.insensitive && (''+s).toLowerCase() || ''+s; },
  7415. // convert all to strings strip whitespace
  7416. // first character is "_", it's smallest - (Naoki Sawada)
  7417. x = i(a).replace(sre, '').replace(/^_/, "\x01") || '',
  7418. y = i(b).replace(sre, '').replace(/^_/, "\x01") || '',
  7419. // chunk/tokenize
  7420. xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  7421. yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  7422. // numeric, hex or date detection
  7423. xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
  7424. yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
  7425. oFxNcL, oFyNcL,
  7426. locRes = 0;
  7427. // first try and sort Hex codes or Dates
  7428. if (yD) {
  7429. if ( xD < yD ) return -1;
  7430. else if ( xD > yD ) return 1;
  7431. }
  7432. // natural sorting through split numeric strings and default strings
  7433. for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
  7434. // find floats not starting with '0', string or 0 if not defined (Clint Priest)
  7435. oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
  7436. oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
  7437. // handle numeric vs string comparison - number < string - (Kyle Adams)
  7438. // but symbol first < number - (Naoki Sawada)
  7439. if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
  7440. if (isNaN(oFxNcL) && (typeof oFxNcL !== 'string' || ! oFxNcL.match(syre))) {
  7441. return 1;
  7442. } else if (typeof oFyNcL !== 'string' || ! oFyNcL.match(syre)) {
  7443. return -1;
  7444. }
  7445. }
  7446. // use decimal number comparison if either value is string zero
  7447. if (parseInt(oFxNcL, 10) === 0) oFxNcL = 0;
  7448. if (parseInt(oFyNcL, 10) === 0) oFyNcL = 0;
  7449. // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
  7450. if (typeof oFxNcL !== typeof oFyNcL) {
  7451. oFxNcL += '';
  7452. oFyNcL += '';
  7453. }
  7454. // use locale sensitive sort for strings when case insensitive
  7455. // note: localeCompare interleaves uppercase with lowercase (e.g. A,a,B,b)
  7456. if (self.sort.insensitive && typeof oFxNcL === 'string' && typeof oFyNcL === 'string') {
  7457. locRes = oFxNcL.localeCompare(oFyNcL, self.loc);
  7458. if (locRes !== 0) return locRes;
  7459. }
  7460. if (oFxNcL < oFyNcL) return -1;
  7461. if (oFxNcL > oFyNcL) return 1;
  7462. }
  7463. return 0;
  7464. };
  7465. self.sort.insensitive = true;
  7466. }
  7467. }
  7468. return self.sort(a, b);
  7469. },
  7470. /**
  7471. * Compare files based on elFinder.sort
  7472. *
  7473. * @param Object file
  7474. * @param Object file
  7475. * @return Number
  7476. */
  7477. compare : function(file1, file2) {
  7478. var self = this,
  7479. type = self.sortType,
  7480. asc = self.sortOrder == 'asc',
  7481. stick = self.sortStickFolders,
  7482. rules = self.sortRules,
  7483. sort = rules[type],
  7484. d1 = file1.mime == 'directory',
  7485. d2 = file2.mime == 'directory',
  7486. res;
  7487. if (stick) {
  7488. if (d1 && !d2) {
  7489. return -1;
  7490. } else if (!d1 && d2) {
  7491. return 1;
  7492. }
  7493. }
  7494. res = asc ? sort(file1, file2) : sort(file2, file1);
  7495. return type !== 'name' && res === 0
  7496. ? res = asc ? rules.name(file1, file2) : rules.name(file2, file1)
  7497. : res;
  7498. },
  7499. /**
  7500. * Sort files based on config
  7501. *
  7502. * @param Array files
  7503. * @return Array
  7504. */
  7505. sortFiles : function(files) {
  7506. return files.sort(this.compare);
  7507. },
  7508. /**
  7509. * Open notification dialog
  7510. * and append/update message for required notification type.
  7511. *
  7512. * @param Object options
  7513. * @example
  7514. * this.notify({
  7515. * type : 'copy',
  7516. * msg : 'Copy files', // not required for known types @see this.notifyType
  7517. * cnt : 3,
  7518. * hideCnt : false, // true for not show count
  7519. * progress : 10, // progress bar percents (use cnt : 0 to update progress bar)
  7520. * cancel : callback // callback function for cancel button
  7521. * })
  7522. * @return elFinder
  7523. */
  7524. notify : function(opts) {
  7525. var type = opts.type,
  7526. id = opts.id? 'elfinder-notify-'+opts.id : '',
  7527. msg = this.i18n((typeof opts.msg !== 'undefined')? opts.msg : (this.messages['ntf'+type] ? 'ntf'+type : 'ntfsmth')),
  7528. ndialog = this.ui.notify,
  7529. notify = ndialog.children('.elfinder-notify-'+type+(id? ('.'+id) : '')),
  7530. button = notify.children('div.elfinder-notify-cancel').children('button'),
  7531. ntpl = '<div class="elfinder-notify elfinder-notify-{type}'+(id? (' '+id) : '')+'"><span class="elfinder-dialog-icon elfinder-dialog-icon-{type}"/><span class="elfinder-notify-msg">{msg}</span> <span class="elfinder-notify-cnt"/><div class="elfinder-notify-progressbar"><div class="elfinder-notify-progress"/></div><div class="elfinder-notify-cancel"/></div>',
  7532. delta = opts.cnt,
  7533. size = (typeof opts.size != 'undefined')? parseInt(opts.size) : null,
  7534. progress = (typeof opts.progress != 'undefined' && opts.progress >= 0) ? opts.progress : null,
  7535. cancel = opts.cancel,
  7536. clhover = 'ui-state-hover',
  7537. close = function() {
  7538. notify._esc && $(document).off('keydown', notify._esc);
  7539. notify.remove();
  7540. !ndialog.children().length && ndialog.elfinderdialog('close');
  7541. },
  7542. cnt, total, prc;
  7543. if (!type) {
  7544. return this;
  7545. }
  7546. if (!notify.length) {
  7547. notify = $(ntpl.replace(/\{type\}/g, type).replace(/\{msg\}/g, msg))
  7548. .appendTo(ndialog)
  7549. .data('cnt', 0);
  7550. if (progress != null) {
  7551. notify.data({progress : 0, total : 0});
  7552. }
  7553. if (cancel) {
  7554. button = $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">'+this.i18n('btnCancel')+'</span></button>')
  7555. .on('mouseenter mouseleave', function(e) {
  7556. $(this).toggleClass(clhover, e.type === 'mouseenter');
  7557. });
  7558. notify.children('div.elfinder-notify-cancel').append(button);
  7559. }
  7560. } else if (typeof opts.msg !== 'undefined') {
  7561. notify.children('span.elfinder-notify-msg').html(msg);
  7562. }
  7563. cnt = delta + parseInt(notify.data('cnt'));
  7564. if (cnt > 0) {
  7565. if (cancel && button.length) {
  7566. if ($.isFunction(cancel) || (typeof cancel === 'object' && cancel.promise)) {
  7567. notify._esc = function(e) {
  7568. if (e.type == 'keydown' && e.keyCode != $.ui.keyCode.ESCAPE) {
  7569. return;
  7570. }
  7571. e.preventDefault();
  7572. e.stopPropagation();
  7573. close();
  7574. if (cancel.promise) {
  7575. cancel.reject(0); // 0 is canceling flag
  7576. } else {
  7577. cancel(e);
  7578. }
  7579. };
  7580. button.on('click', function(e) {
  7581. notify._esc(e);
  7582. });
  7583. $(document).on('keydown.' + this.namespace, notify._esc);
  7584. }
  7585. }
  7586. !opts.hideCnt && notify.children('.elfinder-notify-cnt').text('('+cnt+')');
  7587. ndialog.is(':hidden') && ndialog.elfinderdialog('open', this).height('auto');
  7588. notify.data('cnt', cnt);
  7589. if ((progress != null)
  7590. && (total = notify.data('total')) >= 0
  7591. && (prc = notify.data('progress')) >= 0) {
  7592. total += size != null? size : delta;
  7593. prc += progress;
  7594. (size == null && delta < 0) && (prc += delta * 100);
  7595. notify.data({progress : prc, total : total});
  7596. if (size != null) {
  7597. prc *= 100;
  7598. total = Math.max(1, total);
  7599. }
  7600. progress = parseInt(prc/total);
  7601. notify.find('.elfinder-notify-progress')
  7602. .animate({
  7603. width : (progress < 100 ? progress : 100)+'%'
  7604. }, 20);
  7605. }
  7606. } else {
  7607. close();
  7608. }
  7609. return this;
  7610. },
  7611. /**
  7612. * Open confirmation dialog
  7613. *
  7614. * @param Object options
  7615. * @example
  7616. * this.confirm({
  7617. * cssClass : 'elfinder-confirm-mydialog',
  7618. * title : 'Remove files',
  7619. * text : 'Here is question text',
  7620. * accept : { // accept callback - required
  7621. * label : 'Continue',
  7622. * callback : function(applyToAll) { fm.log('Ok') }
  7623. * },
  7624. * cancel : { // cancel callback - required
  7625. * label : 'Cancel',
  7626. * callback : function() { fm.log('Cancel')}
  7627. * },
  7628. * reject : { // reject callback - optionally
  7629. * label : 'No',
  7630. * callback : function(applyToAll) { fm.log('No')}
  7631. * },
  7632. * buttons : [ // additional buttons callback - optionally
  7633. * {
  7634. * label : 'Btn1',
  7635. * callback : function(applyToAll) { fm.log('Btn1')}
  7636. * }
  7637. * ],
  7638. * all : true // display checkbox "Apply to all"
  7639. * })
  7640. * @return elFinder
  7641. */
  7642. confirm : function(opts) {
  7643. var self = this,
  7644. complete = false,
  7645. options = {
  7646. cssClass : 'elfinder-dialog-confirm',
  7647. modal : true,
  7648. resizable : false,
  7649. title : this.i18n(opts.title || 'confirmReq'),
  7650. buttons : {},
  7651. close : function() {
  7652. !complete && opts.cancel.callback();
  7653. $(this).elfinderdialog('destroy');
  7654. }
  7655. },
  7656. apply = this.i18n('apllyAll'),
  7657. label, checkbox, btnNum;
  7658. if (opts.cssClass) {
  7659. options.cssClass += ' ' + opts.cssClass;
  7660. }
  7661. options.buttons[this.i18n(opts.accept.label)] = function() {
  7662. opts.accept.callback(!!(checkbox && checkbox.prop('checked')));
  7663. complete = true;
  7664. $(this).elfinderdialog('close');
  7665. };
  7666. options.buttons[this.i18n(opts.accept.label)]._cssClass = 'elfinder-confirm-accept';
  7667. if (opts.reject) {
  7668. options.buttons[this.i18n(opts.reject.label)] = function() {
  7669. opts.reject.callback(!!(checkbox && checkbox.prop('checked')));
  7670. complete = true;
  7671. $(this).elfinderdialog('close');
  7672. };
  7673. options.buttons[this.i18n(opts.reject.label)]._cssClass = 'elfinder-confirm-reject';
  7674. }
  7675. if (opts.buttons && opts.buttons.length > 0) {
  7676. btnNum = 1;
  7677. $.each(opts.buttons, function(i, v){
  7678. options.buttons[self.i18n(v.label)] = function() {
  7679. v.callback(!!(checkbox && checkbox.prop('checked')));
  7680. complete = true;
  7681. $(this).elfinderdialog('close');
  7682. };
  7683. options.buttons[self.i18n(v.label)]._cssClass = 'elfinder-confirm-extbtn' + (btnNum++);
  7684. if (v.cssClass) {
  7685. options.buttons[self.i18n(v.label)]._cssClass += ' ' + v.cssClass;
  7686. }
  7687. });
  7688. }
  7689. options.buttons[this.i18n(opts.cancel.label)] = function() {
  7690. $(this).elfinderdialog('close');
  7691. };
  7692. options.buttons[this.i18n(opts.cancel.label)]._cssClass = 'elfinder-confirm-cancel';
  7693. if (opts.all) {
  7694. options.create = function() {
  7695. var base = $('<div class="elfinder-dialog-confirm-applyall"/>');
  7696. checkbox = $('<input type="checkbox" />');
  7697. $(this).next().find('.ui-dialog-buttonset')
  7698. .prepend(base.append($('<label>'+apply+'</label>').prepend(checkbox)));
  7699. };
  7700. }
  7701. if (opts.optionsCallback && $.isFunction(opts.optionsCallback)) {
  7702. opts.optionsCallback(options);
  7703. }
  7704. return this.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-confirm"/>' + this.i18n(opts.text), options);
  7705. },
  7706. /**
  7707. * Create unique file name in required dir
  7708. *
  7709. * @param String file name
  7710. * @param String parent dir hash
  7711. * @param String glue
  7712. * @return String
  7713. */
  7714. uniqueName : function(prefix, phash, glue) {
  7715. var i = 0, ext = '', p, name;
  7716. prefix = this.i18n(false, prefix);
  7717. phash = phash || this.cwd().hash;
  7718. glue = (typeof glue === 'undefined')? ' ' : glue;
  7719. if (p = prefix.match(/^(.+)(\.[^.]+)$/)) {
  7720. ext = p[2];
  7721. prefix = p[1];
  7722. }
  7723. name = prefix+ext;
  7724. if (!this.fileByName(name, phash)) {
  7725. return name;
  7726. }
  7727. while (i < 10000) {
  7728. name = prefix + glue + (++i) + ext;
  7729. if (!this.fileByName(name, phash)) {
  7730. return name;
  7731. }
  7732. }
  7733. return prefix + Math.random() + ext;
  7734. },
  7735. /**
  7736. * Return message translated onto current language
  7737. * Allowed accept HTML element that was wrapped in jQuery object
  7738. * To be careful to XSS vulnerability of HTML element Ex. You should use `fm.escape(file.name)`
  7739. *
  7740. * @param String|Array message[s]|Object jQuery
  7741. * @return String
  7742. **/
  7743. i18n : function() {
  7744. var self = this,
  7745. messages = this.messages,
  7746. input = [],
  7747. ignore = [],
  7748. message = function(m) {
  7749. var file;
  7750. if (m.indexOf('#') === 0) {
  7751. if ((file = self.file(m.substr(1)))) {
  7752. return file.name;
  7753. }
  7754. }
  7755. return m;
  7756. },
  7757. i, j, m, escFunc, start = 0, isErr;
  7758. if (arguments.length && arguments[0] === false) {
  7759. escFunc = function(m){ return m; };
  7760. start = 1;
  7761. }
  7762. for (i = start; i< arguments.length; i++) {
  7763. m = arguments[i];
  7764. if (Array.isArray(m)) {
  7765. for (j = 0; j < m.length; j++) {
  7766. if (m[j] instanceof jQuery) {
  7767. // jQuery object is HTML element
  7768. input.push(m[j]);
  7769. } else if (typeof m[j] !== 'undefined'){
  7770. input.push(message('' + m[j]));
  7771. }
  7772. }
  7773. } else if (m instanceof jQuery) {
  7774. // jQuery object is HTML element
  7775. input.push(m[j]);
  7776. } else if (typeof m !== 'undefined'){
  7777. input.push(message('' + m));
  7778. }
  7779. }
  7780. for (i = 0; i < input.length; i++) {
  7781. // dont translate placeholders
  7782. if ($.inArray(i, ignore) !== -1) {
  7783. continue;
  7784. }
  7785. m = input[i];
  7786. if (typeof m == 'string') {
  7787. isErr = !!(messages[m] && m.match(/^err/));
  7788. // translate message
  7789. m = messages[m] || (escFunc? escFunc(m) : self.escape(m));
  7790. // replace placeholders in message
  7791. m = m.replace(/\$(\d+)/g, function(match, placeholder) {
  7792. var res;
  7793. placeholder = i + parseInt(placeholder);
  7794. if (placeholder > 0 && input[placeholder]) {
  7795. ignore.push(placeholder);
  7796. }
  7797. res = escFunc? escFunc(input[placeholder]) : self.escape(input[placeholder]);
  7798. if (isErr) {
  7799. res = '<span class="elfinder-err-var elfinder-err-var' + placeholder + '">' + res + '</span>';
  7800. }
  7801. return res;
  7802. });
  7803. } else {
  7804. // get HTML from jQuery object
  7805. m = m.get(0).outerHTML;
  7806. }
  7807. input[i] = m;
  7808. }
  7809. return $.grep(input, function(m, i) { return $.inArray(i, ignore) === -1 ? true : false; }).join('<br>');
  7810. },
  7811. /**
  7812. * Get icon style from file.icon
  7813. *
  7814. * @param Object elFinder file object
  7815. * @return String|Object
  7816. */
  7817. getIconStyle : function(file, asObject) {
  7818. var self = this,
  7819. template = {
  7820. 'background' : 'url(\'{url}\') 0 0 no-repeat',
  7821. 'background-size' : 'contain'
  7822. },
  7823. style = '',
  7824. cssObj = {},
  7825. i = 0;
  7826. if (file.icon) {
  7827. style = 'style="';
  7828. $.each(template, function(k, v) {
  7829. if (i++ === 0) {
  7830. v = v.replace('{url}', self.escape(file.icon));
  7831. }
  7832. if (asObject) {
  7833. cssObj[k] = v;
  7834. } else {
  7835. style += k+':'+v+';';
  7836. }
  7837. });
  7838. style += '"';
  7839. }
  7840. return asObject? cssObj : style;
  7841. },
  7842. /**
  7843. * Convert mimetype into css classes
  7844. *
  7845. * @param String file mimetype
  7846. * @return String
  7847. */
  7848. mime2class : function(mimeType) {
  7849. var prefix = 'elfinder-cwd-icon-',
  7850. mime = mimeType.toLowerCase(),
  7851. isText = this.textMimes[mime];
  7852. mime = mime.split('/');
  7853. if (isText) {
  7854. mime[0] += ' ' + prefix + 'text';
  7855. } else if (mime[1] && mime[1].match(/\+xml$/)) {
  7856. mime[0] += ' ' + prefix + 'xml';
  7857. }
  7858. return prefix + mime[0] + (mime[1] ? ' ' + prefix + mime[1].replace(/(\.|\+)/g, '-') : '');
  7859. },
  7860. /**
  7861. * Return localized kind of file
  7862. *
  7863. * @param Object|String file or file mimetype
  7864. * @return String
  7865. */
  7866. mime2kind : function(f) {
  7867. var isObj = typeof(f) == 'object' ? true : false,
  7868. mime = isObj ? f.mime : f,
  7869. kind;
  7870. if (isObj && f.alias && mime != 'symlink-broken') {
  7871. kind = 'Alias';
  7872. } else if (this.kinds[mime]) {
  7873. if (isObj && mime === 'directory' && (! f.phash || f.isroot)) {
  7874. kind = 'Root';
  7875. } else {
  7876. kind = this.kinds[mime];
  7877. }
  7878. }
  7879. if (! kind) {
  7880. if (mime.indexOf('text') === 0) {
  7881. kind = 'Text';
  7882. } else if (mime.indexOf('image') === 0) {
  7883. kind = 'Image';
  7884. } else if (mime.indexOf('audio') === 0) {
  7885. kind = 'Audio';
  7886. } else if (mime.indexOf('video') === 0) {
  7887. kind = 'Video';
  7888. } else if (mime.indexOf('application') === 0) {
  7889. kind = 'App';
  7890. } else {
  7891. kind = mime;
  7892. }
  7893. }
  7894. return this.messages['kind'+kind] ? this.i18n('kind'+kind) : mime;
  7895. },
  7896. /**
  7897. * Return boolean Is mime-type text file
  7898. *
  7899. * @param String mime-type
  7900. * @return Boolean
  7901. */
  7902. mimeIsText : function(mime) {
  7903. return (this.textMimes[mime.toLowerCase()] || (mime.indexOf('text/') === 0 && mime.substr(5, 3) !== 'rtf') || mime.match(/^application\/.+\+xml$/))? true : false;
  7904. },
  7905. /**
  7906. * Returns a date string formatted according to the given format string
  7907. *
  7908. * @param String format string
  7909. * @param Object Date object
  7910. * @return String
  7911. */
  7912. date : function(format, date) {
  7913. var self = this,
  7914. output, d, dw, m, y, h, g, i, s;
  7915. if (! date) {
  7916. date = new Date();
  7917. }
  7918. h = date[self.getHours]();
  7919. g = h > 12 ? h - 12 : h;
  7920. i = date[self.getMinutes]();
  7921. s = date[self.getSeconds]();
  7922. d = date[self.getDate]();
  7923. dw = date[self.getDay]();
  7924. m = date[self.getMonth]() + 1;
  7925. y = date[self.getFullYear]();
  7926. output = format.replace(/[a-z]/gi, function(val) {
  7927. switch (val) {
  7928. case 'd': return d > 9 ? d : '0'+d;
  7929. case 'j': return d;
  7930. case 'D': return self.i18n(self.i18.daysShort[dw]);
  7931. case 'l': return self.i18n(self.i18.days[dw]);
  7932. case 'm': return m > 9 ? m : '0'+m;
  7933. case 'n': return m;
  7934. case 'M': return self.i18n(self.i18.monthsShort[m-1]);
  7935. case 'F': return self.i18n(self.i18.months[m-1]);
  7936. case 'Y': return y;
  7937. case 'y': return (''+y).substr(2);
  7938. case 'H': return h > 9 ? h : '0'+h;
  7939. case 'G': return h;
  7940. case 'g': return g;
  7941. case 'h': return g > 9 ? g : '0'+g;
  7942. case 'a': return h >= 12 ? 'pm' : 'am';
  7943. case 'A': return h >= 12 ? 'PM' : 'AM';
  7944. case 'i': return i > 9 ? i : '0'+i;
  7945. case 's': return s > 9 ? s : '0'+s;
  7946. }
  7947. return val;
  7948. });
  7949. return output;
  7950. },
  7951. /**
  7952. * Return localized date
  7953. *
  7954. * @param Object file object
  7955. * @return String
  7956. */
  7957. formatDate : function(file, t) {
  7958. var self = this,
  7959. ts = t || file.ts,
  7960. i18 = self.i18,
  7961. date, format, output, d, dw, m, y, h, g, i, s;
  7962. if (self.options.clientFormatDate && ts > 0) {
  7963. date = new Date(ts*1000);
  7964. format = ts >= this.yesterday
  7965. ? this.fancyFormat
  7966. : this.dateFormat;
  7967. output = self.date(format, date);
  7968. return ts >= this.yesterday
  7969. ? output.replace('$1', this.i18n(ts >= this.today ? 'Today' : 'Yesterday'))
  7970. : output;
  7971. } else if (file.date) {
  7972. return file.date.replace(/([a-z]+)\s/i, function(a1, a2) { return self.i18n(a2)+' '; });
  7973. }
  7974. return self.i18n('dateUnknown');
  7975. },
  7976. /**
  7977. * Return localized number string
  7978. *
  7979. * @param Number
  7980. * @return String
  7981. */
  7982. toLocaleString : function(num) {
  7983. var v = new Number(num);
  7984. if (v) {
  7985. if (v.toLocaleString) {
  7986. return v.toLocaleString();
  7987. } else {
  7988. return String(num).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
  7989. }
  7990. }
  7991. return num;
  7992. },
  7993. /**
  7994. * Return css class marks file permissions
  7995. *
  7996. * @param Object file
  7997. * @return String
  7998. */
  7999. perms2class : function(o) {
  8000. var c = '';
  8001. if (!o.read && !o.write) {
  8002. c = 'elfinder-na';
  8003. } else if (!o.read) {
  8004. c = 'elfinder-wo';
  8005. } else if (!o.write) {
  8006. c = 'elfinder-ro';
  8007. }
  8008. if (o.type) {
  8009. c += ' elfinder-' + this.escape(o.type);
  8010. }
  8011. return c;
  8012. },
  8013. /**
  8014. * Return localized string with file permissions
  8015. *
  8016. * @param Object file
  8017. * @return String
  8018. */
  8019. formatPermissions : function(f) {
  8020. var p = [];
  8021. f.read && p.push(this.i18n('read'));
  8022. f.write && p.push(this.i18n('write'));
  8023. return p.length ? p.join(' '+this.i18n('and')+' ') : this.i18n('noaccess');
  8024. },
  8025. /**
  8026. * Return formated file size
  8027. *
  8028. * @param Number file size
  8029. * @return String
  8030. */
  8031. formatSize : function(s) {
  8032. var n = 1, u = 'b';
  8033. if (s == 'unknown') {
  8034. return this.i18n('unknown');
  8035. }
  8036. if (s > 1073741824) {
  8037. n = 1073741824;
  8038. u = 'GB';
  8039. } else if (s > 1048576) {
  8040. n = 1048576;
  8041. u = 'MB';
  8042. } else if (s > 1024) {
  8043. n = 1024;
  8044. u = 'KB';
  8045. }
  8046. s = s/n;
  8047. return (s > 0 ? n >= 1048576 ? s.toFixed(2) : Math.round(s) : 0) +' '+u;
  8048. },
  8049. /**
  8050. * Return formated file mode by options.fileModeStyle
  8051. *
  8052. * @param String file mode
  8053. * @param String format style
  8054. * @return String
  8055. */
  8056. formatFileMode : function(p, style) {
  8057. var i, o, s, b, sticy, suid, sgid, str, oct;
  8058. if (!style) {
  8059. style = this.options.fileModeStyle.toLowerCase();
  8060. }
  8061. p = $.trim(p);
  8062. if (p.match(/[rwxs-]{9}$/i)) {
  8063. str = p = p.substr(-9);
  8064. if (style == 'string') {
  8065. return str;
  8066. }
  8067. oct = '';
  8068. s = 0;
  8069. for (i=0; i<7; i=i+3) {
  8070. o = p.substr(i, 3);
  8071. b = 0;
  8072. if (o.match(/[r]/i)) {
  8073. b += 4;
  8074. }
  8075. if (o.match(/[w]/i)) {
  8076. b += 2;
  8077. }
  8078. if (o.match(/[xs]/i)) {
  8079. if (o.match(/[xs]/)) {
  8080. b += 1;
  8081. }
  8082. if (o.match(/[s]/i)) {
  8083. if (i == 0) {
  8084. s += 4;
  8085. } else if (i == 3) {
  8086. s += 2;
  8087. }
  8088. }
  8089. }
  8090. oct += b.toString(8);
  8091. }
  8092. if (s) {
  8093. oct = s.toString(8) + oct;
  8094. }
  8095. } else {
  8096. p = parseInt(p, 8);
  8097. oct = p? p.toString(8) : '';
  8098. if (!p || style == 'octal') {
  8099. return oct;
  8100. }
  8101. o = p.toString(8);
  8102. s = 0;
  8103. if (o.length > 3) {
  8104. o = o.substr(-4);
  8105. s = parseInt(o.substr(0, 1), 8);
  8106. o = o.substr(1);
  8107. }
  8108. sticy = ((s & 1) == 1); // not support
  8109. sgid = ((s & 2) == 2);
  8110. suid = ((s & 4) == 4);
  8111. str = '';
  8112. for(i=0; i<3; i++) {
  8113. if ((parseInt(o.substr(i, 1), 8) & 4) == 4) {
  8114. str += 'r';
  8115. } else {
  8116. str += '-';
  8117. }
  8118. if ((parseInt(o.substr(i, 1), 8) & 2) == 2) {
  8119. str += 'w';
  8120. } else {
  8121. str += '-';
  8122. }
  8123. if ((parseInt(o.substr(i, 1), 8) & 1) == 1) {
  8124. str += ((i==0 && suid)||(i==1 && sgid))? 's' : 'x';
  8125. } else {
  8126. str += '-';
  8127. }
  8128. }
  8129. }
  8130. if (style == 'both') {
  8131. return str + ' (' + oct + ')';
  8132. } else if (style == 'string') {
  8133. return str;
  8134. } else {
  8135. return oct;
  8136. }
  8137. },
  8138. /**
  8139. * Regist this.decodeRawString function
  8140. *
  8141. * @return void
  8142. */
  8143. registRawStringDecoder : function(rawStringDecoder) {
  8144. if ($.isFunction(rawStringDecoder)) {
  8145. this.decodeRawString = this.options.rawStringDecoder = rawStringDecoder;
  8146. }
  8147. },
  8148. /**
  8149. * Return boolean that uploadable MIME type into target folder
  8150. *
  8151. * @param String mime MIME type
  8152. * @param String target target folder hash
  8153. * @return Bool
  8154. */
  8155. uploadMimeCheck : function(mime, target) {
  8156. target = target || this.cwd().hash;
  8157. var res = true, // default is allow
  8158. mimeChecker = this.option('uploadMime', target),
  8159. allow,
  8160. deny,
  8161. check = function(checker) {
  8162. var ret = false;
  8163. if (typeof checker === 'string' && checker.toLowerCase() === 'all') {
  8164. ret = true;
  8165. } else if (Array.isArray(checker) && checker.length) {
  8166. $.each(checker, function(i, v) {
  8167. v = v.toLowerCase();
  8168. if (v === 'all' || mime.indexOf(v) === 0) {
  8169. ret = true;
  8170. return false;
  8171. }
  8172. });
  8173. }
  8174. return ret;
  8175. };
  8176. if (mime && $.isPlainObject(mimeChecker)) {
  8177. mime = mime.toLowerCase();
  8178. allow = check(mimeChecker.allow);
  8179. deny = check(mimeChecker.deny);
  8180. if (mimeChecker.firstOrder === 'allow') {
  8181. res = false; // default is deny
  8182. if (! deny && allow === true) { // match only allow
  8183. res = true;
  8184. }
  8185. } else {
  8186. res = true; // default is allow
  8187. if (deny === true && ! allow) { // match only deny
  8188. res = false;
  8189. }
  8190. }
  8191. }
  8192. return res;
  8193. },
  8194. /**
  8195. * call chained sequence of async deferred functions
  8196. *
  8197. * @param Array tasks async functions
  8198. * @return Object jQuery.Deferred
  8199. */
  8200. sequence : function(tasks) {
  8201. var l = tasks.length,
  8202. chain = function(task, idx) {
  8203. ++idx;
  8204. if (tasks[idx]) {
  8205. return chain(task.then(tasks[idx]), idx);
  8206. } else {
  8207. return task;
  8208. }
  8209. };
  8210. if (l > 1) {
  8211. return chain(tasks[0](), 0);
  8212. } else {
  8213. return tasks[0]();
  8214. }
  8215. },
  8216. /**
  8217. * Reload contents of target URL for clear browser cache
  8218. *
  8219. * @param String url target URL
  8220. * @return Object jQuery.Deferred
  8221. */
  8222. reloadContents : function(url) {
  8223. var dfd = $.Deferred(),
  8224. ifm;
  8225. try {
  8226. ifm = $('<iframe width="1" height="1" scrolling="no" frameborder="no" style="position:absolute; top:-1px; left:-1px" crossorigin="use-credentials">')
  8227. .attr('src', url)
  8228. .one('load', function() {
  8229. var ifm = $(this);
  8230. try {
  8231. this.contentDocument.location.reload(true);
  8232. ifm.one('load', function() {
  8233. ifm.remove();
  8234. dfd.resolve();
  8235. });
  8236. } catch(e) {
  8237. ifm.attr('src', '').attr('src', url).one('load', function() {
  8238. ifm.remove();
  8239. dfd.resolve();
  8240. });
  8241. }
  8242. })
  8243. .appendTo('body');
  8244. } catch(e) {
  8245. ifm && ifm.remove();
  8246. dfd.reject();
  8247. }
  8248. return dfd;
  8249. },
  8250. /**
  8251. * Make netmount option for OAuth2
  8252. *
  8253. * @param String protocol
  8254. * @param String name
  8255. * @param String host
  8256. * @param Object opts Default {noOffline: false, root: 'root', pathI18n: 'folderId', folders: true}
  8257. }
  8258. *
  8259. * @return Object
  8260. */
  8261. makeNetmountOptionOauth : function(protocol, name, host, opt) {
  8262. var noOffline = typeof opt === 'boolean'? opt : null, // for backward compat
  8263. opts = Object.assign({
  8264. noOffline : false,
  8265. root : 'root',
  8266. pathI18n : 'folderId',
  8267. folders : true
  8268. }, (noOffline === null? (opt || {}) : {noOffline : noOffline})),
  8269. addFolders = function(fm, bro, folders) {
  8270. var self = this,
  8271. cnt = Object.keys($.isPlainObject(folders)? folders : {}).length,
  8272. select;
  8273. bro.next().remove();
  8274. if (cnt) {
  8275. select = $('<select class="ui-corner-all elfinder-tabstop" style="max-width:200px;">').append(
  8276. $($.map(folders, function(n,i){return '<option value="'+fm.escape((i+'').trim())+'">'+fm.escape(n)+'</option>';}).join(''))
  8277. ).on('change click', function(e){
  8278. var node = $(this),
  8279. path = node.val(),
  8280. spn;
  8281. self.inputs.path.val(path);
  8282. if (opts.folders && (e.type === 'change' || node.data('current') !== path)) {
  8283. node.next().remove();
  8284. node.data('current', path);
  8285. if (path != opts.root) {
  8286. spn = spinner();
  8287. if (xhr && xhr.state() === 'pending') {
  8288. fm.abortXHR(xhr, { quiet: true , abort: true });
  8289. }
  8290. node.after(spn);
  8291. xhr = fm.request({
  8292. data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', path: path, pass: 'folders'},
  8293. preventDefault : true
  8294. }).done(function(data){
  8295. addFolders.call(self, fm, node, data.folders);
  8296. }).always(function() {
  8297. fm.abortXHR(xhr, { quiet: true });
  8298. spn.remove();
  8299. }).xhr;
  8300. }
  8301. }
  8302. });
  8303. bro.after($('<div/>').append(select))
  8304. .closest('.ui-dialog').trigger('tabstopsInit');
  8305. select.trigger('focus');
  8306. }
  8307. },
  8308. spinner = function() {
  8309. return $('<div class="elfinder-netmount-spinner"/>').append('<span class="elfinder-spinner"/>');
  8310. },
  8311. xhr;
  8312. return {
  8313. vars : {},
  8314. name : name,
  8315. inputs: {
  8316. offline : $('<input type="checkbox"/>').on('change', function() {
  8317. $(this).parents('table.elfinder-netmount-tb').find('select:first').trigger('change', 'reset');
  8318. }),
  8319. host : $('<span><span class="elfinder-spinner"/></span><input type="hidden"/>'),
  8320. path : $('<input type="text" value="'+opts.root+'"/>'),
  8321. user : $('<input type="hidden"/>'),
  8322. pass : $('<input type="hidden"/>')
  8323. },
  8324. select: function(fm, ev, d){
  8325. var f = this.inputs,
  8326. oline = f.offline,
  8327. f0 = $(f.host[0]),
  8328. data = d || null;
  8329. this.vars.mbtn = f.host.closest('.ui-dialog').children('.ui-dialog-buttonpane:first').find('button.elfinder-btncnt-0');
  8330. if (! f0.data('inrequest')
  8331. && (f0.find('span.elfinder-spinner').length
  8332. || data === 'reset'
  8333. || (data === 'winfocus' && ! f0.siblings('span.elfinder-button-icon-reload').length))
  8334. )
  8335. {
  8336. if (oline.parent().children().length === 1) {
  8337. f.path.parent().prev().html(fm.i18n(opts.pathI18n));
  8338. oline.attr('title', fm.i18n('offlineAccess'));
  8339. oline.uniqueId().after($('<label/>').attr('for', oline.attr('id')).html(' '+fm.i18n('offlineAccess')));
  8340. }
  8341. f0.data('inrequest', true).empty().addClass('elfinder-spinner')
  8342. .parent().find('span.elfinder-button-icon').remove();
  8343. fm.request({
  8344. data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', options: {id: fm.id, offline: oline.prop('checked')? 1:0, pass: f.host[1].value}},
  8345. preventDefault : true
  8346. }).done(function(data){
  8347. f0.removeClass("elfinder-spinner").html(data.body.replace(/\{msg:([^}]+)\}/g, function(whole,s1){return fm.i18n(s1, host);}));
  8348. });
  8349. opts.noOffline && oline.closest('tr').hide();
  8350. } else {
  8351. oline.closest('tr')[(opts.noOffline || f.user.val())? 'hide':'show']();
  8352. f0.data('funcexpup') && f0.data('funcexpup')();
  8353. }
  8354. this.vars.mbtn[$(f.host[1]).val()? 'show':'hide']();
  8355. },
  8356. done: function(fm, data){
  8357. var f = this.inputs,
  8358. p = this.protocol,
  8359. f0 = $(f.host[0]),
  8360. f1 = $(f.host[1]),
  8361. expires = '&nbsp;';
  8362. opts.noOffline && f.offline.closest('tr').hide();
  8363. if (data.mode == 'makebtn') {
  8364. f0.removeClass('elfinder-spinner').removeData('expires').removeData('funcexpup');
  8365. f.host.find('input').on('mouseenter mouseleave', function(){$(this).toggleClass('ui-state-hover');});
  8366. f1.val('');
  8367. f.path.val(opts.root).next().remove();
  8368. f.user.val('');
  8369. f.pass.val('');
  8370. ! opts.noOffline && f.offline.closest('tr').show();
  8371. this.vars.mbtn.hide();
  8372. } else if (data.mode == 'folders') {
  8373. if (data.folders) {
  8374. addFolders.call(this, fm, f.path.nextAll(':last'), data.folders);
  8375. }
  8376. } else {
  8377. if (data.expires) {
  8378. expires = '()';
  8379. f0.data('expires', data.expires);
  8380. }
  8381. f0.html(host + expires).removeClass('elfinder-spinner');
  8382. if (data.expires) {
  8383. f0.data('funcexpup', function() {
  8384. var rem = Math.floor((f0.data('expires') - (+new Date()) / 1000) / 60);
  8385. if (rem < 3) {
  8386. f0.parent().children('.elfinder-button-icon-reload').click();
  8387. } else {
  8388. f0.text(f0.text().replace(/\(.*\)/, '('+fm.i18n(['minsLeft', rem])+')'));
  8389. setTimeout(function() {
  8390. if (f0.is(':visible')) {
  8391. f0.data('funcexpup')();
  8392. }
  8393. }, 60000);
  8394. }
  8395. });
  8396. f0.data('funcexpup')();
  8397. }
  8398. if (data.reset) {
  8399. p.trigger('change', 'reset');
  8400. return;
  8401. }
  8402. f0.parent().append($('<span class="elfinder-button-icon elfinder-button-icon-reload" title="'+fm.i18n('reAuth')+'">')
  8403. .on('click', function() {
  8404. f1.val('reauth');
  8405. p.trigger('change', 'reset');
  8406. }));
  8407. f1.val(protocol);
  8408. this.vars.mbtn.show();
  8409. if (data.folders) {
  8410. addFolders.call(this, fm, f.path, data.folders);
  8411. }
  8412. f.user.val('done');
  8413. f.pass.val('done');
  8414. f.offline.closest('tr').hide();
  8415. }
  8416. f0.removeData('inrequest');
  8417. },
  8418. fail: function(fm, err){
  8419. $(this.inputs.host[0]).removeData('inrequest');
  8420. this.protocol.trigger('change', 'reset');
  8421. },
  8422. integrateInfo: opts.integrate
  8423. };
  8424. },
  8425. /**
  8426. * Find cwd's nodes from files
  8427. *
  8428. * @param Array files
  8429. * @param Object opts {firstOnly: true|false}
  8430. */
  8431. findCwdNodes : function(files, opts) {
  8432. var self = this,
  8433. cwd = this.getUI('cwd'),
  8434. cwdHash = this.cwd().hash,
  8435. newItem = $();
  8436. opts = opts || {};
  8437. $.each(files, function(i, f) {
  8438. if (f.phash === cwdHash || self.searchStatus.state > 1) {
  8439. newItem = newItem.add(self.cwdHash2Elm(f.hash));
  8440. if (opts.firstOnly) {
  8441. return false;
  8442. }
  8443. }
  8444. });
  8445. return newItem;
  8446. },
  8447. /**
  8448. * Convert from relative URL to abstract URL based on current URL
  8449. *
  8450. * @param String URL
  8451. * @return String
  8452. */
  8453. convAbsUrl : function(url) {
  8454. if (url.match(/^http/i)) {
  8455. return url;
  8456. }
  8457. if (url.substr(0,2) === '//') {
  8458. return window.location.protocol + url;
  8459. }
  8460. var root = window.location.protocol + '//' + window.location.host,
  8461. reg = /[^\/]+\/\.\.\//,
  8462. ret;
  8463. if (url.substr(0, 1) === '/') {
  8464. ret = root + url;
  8465. } else {
  8466. ret = root + window.location.pathname.replace(/\/[^\/]+$/, '/') + url;
  8467. }
  8468. ret = ret.replace('/./', '/');
  8469. while(reg.test(ret)) {
  8470. ret = ret.replace(reg, '');
  8471. }
  8472. return ret;
  8473. },
  8474. /**
  8475. * Is same origin to current site
  8476. *
  8477. * @param String check url
  8478. * @return Boolean
  8479. */
  8480. isSameOrigin : function (checkUrl) {
  8481. var url;
  8482. checkUrl = this.convAbsUrl(checkUrl);
  8483. if (location.origin && window.URL) {
  8484. try {
  8485. url = new URL(checkUrl);
  8486. return location.origin === url.origin;
  8487. } catch(e) {}
  8488. }
  8489. url = document.createElement('a');
  8490. url.href = checkUrl;
  8491. return location.protocol === url.protocol && location.host === url.host && location.port && url.port;
  8492. },
  8493. navHash2Id : function(hash) {
  8494. return this.navPrefix + hash;
  8495. },
  8496. navId2Hash : function(id) {
  8497. return typeof(id) == 'string' ? id.substr(this.navPrefix.length) : false;
  8498. },
  8499. cwdHash2Id : function(hash) {
  8500. return this.cwdPrefix + hash;
  8501. },
  8502. cwdId2Hash : function(id) {
  8503. return typeof(id) == 'string' ? id.substr(this.cwdPrefix.length) : false;
  8504. },
  8505. /**
  8506. * navHash to jQuery element object
  8507. *
  8508. * @param String hash nav hash
  8509. * @return Object jQuery element object
  8510. */
  8511. navHash2Elm : function(hash) {
  8512. return $(document.getElementById(this.navHash2Id(hash)));
  8513. },
  8514. /**
  8515. * cwdHash to jQuery element object
  8516. *
  8517. * @param String hash cwd hash
  8518. * @return Object jQuery element object
  8519. */
  8520. cwdHash2Elm : function(hash) {
  8521. return $(document.getElementById(this.cwdHash2Id(hash)));
  8522. },
  8523. isInWindow : function(elem, nochkHide) {
  8524. var elm, rect;
  8525. if (! (elm = elem.get(0))) {
  8526. return false;
  8527. }
  8528. if (! nochkHide && elm.offsetParent === null) {
  8529. return false;
  8530. }
  8531. rect = elm.getBoundingClientRect();
  8532. return document.elementFromPoint(rect.left, rect.top)? true : false;
  8533. },
  8534. /**
  8535. * calculate elFinder node z-index
  8536. *
  8537. * @return void
  8538. */
  8539. zIndexCalc : function() {
  8540. var self = this,
  8541. node = this.getUI(),
  8542. ni = node.css('z-index');
  8543. if (ni && ni !== 'auto' && ni !== 'inherit') {
  8544. self.zIndex = ni;
  8545. } else {
  8546. node.parents().each(function(i, n) {
  8547. var z = $(n).css('z-index');
  8548. if (z !== 'auto' && z !== 'inherit' && (z = parseInt(z))) {
  8549. self.zIndex = z;
  8550. return false;
  8551. }
  8552. });
  8553. }
  8554. },
  8555. /**
  8556. * Load JavaScript files
  8557. *
  8558. * @param Array urls to load JavaScript file URLs
  8559. * @param Function callback call back function on script loaded
  8560. * @param Object opts Additional options to $.ajax OR {loadType: 'tag'} to load by script tag
  8561. * @param Object check { obj: (Object)ParentObject, name: (String)"Attribute name", timeout: (Integer)milliseconds }
  8562. * @return elFinder
  8563. */
  8564. loadScript : function(urls, callback, opts, check) {
  8565. var defOpts = {
  8566. dataType : 'script',
  8567. cache : true
  8568. },
  8569. success, cnt, scripts = {}, results = {};
  8570. opts = opts || {};
  8571. if (opts.tryRequire && this.hasRequire) {
  8572. require(urls, callback, opts.error);
  8573. } else {
  8574. success = function() {
  8575. var cnt, fi, hasError;
  8576. $.each(results, function(i, status) {
  8577. if (status !== 'success' && status !== 'notmodified') {
  8578. hasError = true;
  8579. return false;
  8580. }
  8581. });
  8582. if (!hasError) {
  8583. if ($.isFunction(callback)) {
  8584. if (check) {
  8585. if (typeof check.obj[check.name] === 'undefined') {
  8586. cnt = check.timeout? (check.timeout / 10) : 1;
  8587. fi = setInterval(function() {
  8588. if (--cnt < 0 || typeof check.obj[check.name] !== 'undefined') {
  8589. clearInterval(fi);
  8590. callback();
  8591. }
  8592. }, 10);
  8593. } else {
  8594. callback();
  8595. }
  8596. } else {
  8597. callback();
  8598. }
  8599. }
  8600. } else {
  8601. if (opts.error && $.isFunction(opts.error)) {
  8602. opts.error({ loadResults: results });
  8603. }
  8604. }
  8605. };
  8606. if (opts.loadType === 'tag') {
  8607. $('head > script').each(function() {
  8608. scripts[this.src] = this;
  8609. });
  8610. cnt = urls.length;
  8611. $.each(urls, function(i, url) {
  8612. var done = false,
  8613. script;
  8614. if (scripts[url]) {
  8615. results[i] = scripts[url]._error || 'success';
  8616. (--cnt < 1) && success();
  8617. } else {
  8618. script = document.createElement('script');
  8619. script.charset = opts.charset || 'UTF-8';
  8620. $('head').append(script);
  8621. script.onload = script.onreadystatechange = function() {
  8622. if ( !done && (!this.readyState ||
  8623. this.readyState === 'loaded' || this.readyState === 'complete') ) {
  8624. done = true;
  8625. results[i] = 'success';
  8626. (--cnt < 1) && success();
  8627. }
  8628. };
  8629. script.onerror = function(err) {
  8630. results[i] = script._error = (err && err.type)? err.type : 'error';
  8631. (--cnt < 1) && success();
  8632. };
  8633. script.src = url;
  8634. }
  8635. });
  8636. } else {
  8637. opts = $.isPlainObject(opts)? Object.assign(defOpts, opts) : defOpts;
  8638. cnt = 0;
  8639. (function appendScript(d, status) {
  8640. if (d !== void(0)) {
  8641. results[cnt++] = status;
  8642. }
  8643. if (urls.length) {
  8644. $.ajax(Object.assign({}, opts, {
  8645. url: urls.shift(),
  8646. success: appendScript,
  8647. error: appendScript
  8648. }));
  8649. } else {
  8650. success();
  8651. }
  8652. })();
  8653. }
  8654. }
  8655. return this;
  8656. },
  8657. /**
  8658. * Load CSS files
  8659. *
  8660. * @param Array to load CSS file URLs
  8661. * @param Object options
  8662. * @return elFinder
  8663. */
  8664. loadCss : function(urls, opts) {
  8665. var self = this,
  8666. clName, dfds;
  8667. if (typeof urls === 'string') {
  8668. urls = [ urls ];
  8669. }
  8670. if (opts) {
  8671. if (opts.className) {
  8672. clName = opts.className;
  8673. }
  8674. if (opts.dfd && opts.dfd.promise) {
  8675. dfds = [];
  8676. }
  8677. }
  8678. $.each(urls, function(i, url) {
  8679. var link, df;
  8680. url = self.convAbsUrl(url).replace(/^https?:/i, '');
  8681. if (dfds) {
  8682. dfds[i] = $.Deferred();
  8683. }
  8684. if (! $("head > link[href='+url+']").length) {
  8685. link = document.createElement('link');
  8686. link.type = 'text/css';
  8687. link.rel = 'stylesheet';
  8688. link.href = url;
  8689. if (clName) {
  8690. link.className = clName;
  8691. }
  8692. if (dfds) {
  8693. link.onload = function() {
  8694. dfds[i].resolve();
  8695. };
  8696. link.onerror = function() {
  8697. dfds[i].reject();
  8698. };
  8699. }
  8700. $('head').append(link);
  8701. } else {
  8702. dfds && dfds[i].resolve();
  8703. }
  8704. });
  8705. if (dfds) {
  8706. $.when.apply(null, dfds).done(function() {
  8707. opts.dfd.resolve();
  8708. }).fail(function() {
  8709. opts.dfd.reject();
  8710. });
  8711. }
  8712. return this;
  8713. },
  8714. /**
  8715. * Abortable async job performer
  8716. *
  8717. * @param func Function
  8718. * @param arr Array
  8719. * @param opts Object
  8720. *
  8721. * @return Object $.Deferred that has an extended method _abort()
  8722. */
  8723. asyncJob : function(func, arr, opts) {
  8724. var dfrd = $.Deferred(),
  8725. abortFlg = false,
  8726. parms = Object.assign({
  8727. interval : 0,
  8728. numPerOnce : 1
  8729. }, opts || {}),
  8730. resArr = [],
  8731. vars =[],
  8732. curVars = [],
  8733. exec,
  8734. tm;
  8735. dfrd._abort = function(resolve) {
  8736. tm && clearTimeout(tm);
  8737. vars = [];
  8738. abortFlg = true;
  8739. if (dfrd.state() === 'pending') {
  8740. dfrd[resolve? 'resolve' : 'reject'](resArr);
  8741. }
  8742. };
  8743. dfrd.fail(function() {
  8744. dfrd._abort();
  8745. }).always(function() {
  8746. dfrd._abort = function() {};
  8747. });
  8748. if (typeof func === 'function' && Array.isArray(arr)) {
  8749. vars = arr.concat();
  8750. exec = function() {
  8751. var i, len, res;
  8752. if (abortFlg) {
  8753. return;
  8754. }
  8755. curVars = vars.splice(0, parms.numPerOnce);
  8756. len = curVars.length;
  8757. for (i = 0; i < len; i++) {
  8758. if (abortFlg) {
  8759. break;
  8760. }
  8761. res = func(curVars[i]);
  8762. (res !== null) && resArr.push(res);
  8763. }
  8764. if (abortFlg) {
  8765. return;
  8766. }
  8767. if (vars.length) {
  8768. tm = setTimeout(exec, parms.interval);
  8769. } else {
  8770. dfrd.resolve(resArr);
  8771. }
  8772. };
  8773. if (vars.length) {
  8774. tm = setTimeout(exec, 0);
  8775. } else {
  8776. dfrd.resolve(resArr);
  8777. }
  8778. } else {
  8779. dfrd.reject();
  8780. }
  8781. return dfrd;
  8782. },
  8783. getSize : function(targets) {
  8784. var self = this,
  8785. reqs = [],
  8786. tgtlen = targets.length,
  8787. dfrd = $.Deferred().fail(function() {
  8788. $.each(reqs, function(i, req) {
  8789. if (req) {
  8790. req.syncOnFail && req.syncOnFail(false);
  8791. req.reject();
  8792. }
  8793. });
  8794. }),
  8795. getLeafRoots = function(file) {
  8796. var targets = [];
  8797. if (file.mime === 'directory') {
  8798. $.each(self.leafRoots, function(hash, roots) {
  8799. var phash;
  8800. if (hash === file.hash) {
  8801. targets.push.apply(targets, roots);
  8802. } else {
  8803. phash = (self.file(hash) || {}).phash;
  8804. while(phash) {
  8805. if (phash === file.hash) {
  8806. targets.push.apply(targets, roots);
  8807. }
  8808. phash = (self.file(phash) || {}).phash;
  8809. }
  8810. }
  8811. });
  8812. }
  8813. return targets;
  8814. },
  8815. checkPhash = function(hash) {
  8816. var dfd = $.Deferred(),
  8817. dir = self.file(hash),
  8818. target = dir? dir.phash : hash;
  8819. if (target && ! self.file(target)) {
  8820. self.request({
  8821. data : {
  8822. cmd : 'parents',
  8823. target : target
  8824. },
  8825. preventFail : true
  8826. }).done(function() {
  8827. self.one('parentsdone', function() {
  8828. dfd.resolve();
  8829. });
  8830. }).fail(function() {
  8831. dfd.resolve();
  8832. });
  8833. } else {
  8834. dfd.resolve();
  8835. }
  8836. return dfd;
  8837. },
  8838. cache = function() {
  8839. var dfd = $.Deferred(),
  8840. cnt = Object.keys(self.leafRoots).length;
  8841. if (cnt > 0) {
  8842. $.each(self.leafRoots, function(hash) {
  8843. checkPhash(hash).done(function() {
  8844. --cnt;
  8845. if (cnt < 1) {
  8846. dfd.resolve();
  8847. }
  8848. });
  8849. });
  8850. } else {
  8851. dfd.resolve();
  8852. }
  8853. return dfd;
  8854. };
  8855. self.autoSync('stop');
  8856. cache().done(function() {
  8857. var files = [], grps = {}, dfds = [], cache = [], singles = {};
  8858. $.each(targets, function() {
  8859. files.push.apply(files, getLeafRoots(self.file(this)));
  8860. });
  8861. targets.push.apply(targets, files);
  8862. $.each(targets, function() {
  8863. var root = self.root(this),
  8864. file = self.file(this);
  8865. if (file && (file.sizeInfo || file.mime !== 'directory')) {
  8866. cache.push($.Deferred().resolve(file.sizeInfo? file.sizeInfo : {size: file.size, dirCnt: 0, fileCnt : 1}));
  8867. } else {
  8868. if (! grps[root]) {
  8869. grps[root] = [ this.toString() ];
  8870. } else {
  8871. grps[root].push(this.toString());
  8872. }
  8873. }
  8874. });
  8875. $.each(grps, function() {
  8876. var idx = dfds.length;
  8877. if (this.length === 1) {
  8878. singles[idx] = this[0];
  8879. }
  8880. dfds.push(self.request({
  8881. data : {cmd : 'size', targets : this},
  8882. preventDefault : true
  8883. }));
  8884. });
  8885. reqs.push.apply(reqs, dfds);
  8886. dfds.push.apply(dfds, cache);
  8887. $.when.apply($, dfds).fail(function() {
  8888. dfrd.reject();
  8889. }).done(function() {
  8890. var cache = function(h, data) {
  8891. var file;
  8892. if (file = self.file(h)) {
  8893. file.sizeInfo = { isCache: true };
  8894. $.each(['size', 'dirCnt', 'fileCnt'], function() {
  8895. file.sizeInfo[this] = data[this] || 0;
  8896. });
  8897. file.size = parseInt(file.sizeInfo.size);
  8898. changed.push(file);
  8899. }
  8900. },
  8901. size = 0,
  8902. fileCnt = 0,
  8903. dirCnt = 0,
  8904. argLen = arguments.length,
  8905. cnts = [],
  8906. cntsTxt = '',
  8907. changed = [],
  8908. i, file, data;
  8909. for (i = 0; i < argLen; i++) {
  8910. data = arguments[i];
  8911. file = null;
  8912. if (!data.isCache) {
  8913. if (singles[i] && (file = self.file(singles[i]))) {
  8914. cache(singles[i], data);
  8915. } else if (data.sizes && $.isPlainObject(data.sizes)) {
  8916. $.each(data.sizes, function(h, sizeInfo) {
  8917. cache(h, sizeInfo);
  8918. });
  8919. }
  8920. }
  8921. size += parseInt(data.size);
  8922. if (fileCnt !== false) {
  8923. if (typeof data.fileCnt === 'undefined') {
  8924. fileCnt = false;
  8925. }
  8926. fileCnt += parseInt(data.fileCnt || 0);
  8927. }
  8928. if (dirCnt !== false) {
  8929. if (typeof data.dirCnt === 'undefined') {
  8930. dirCnt = false;
  8931. }
  8932. dirCnt += parseInt(data.dirCnt || 0);
  8933. }
  8934. }
  8935. changed.length && self.change({changed: changed});
  8936. if (dirCnt !== false){
  8937. cnts.push(self.i18n('folders') + ': ' + (dirCnt - (tgtlen > 1? 0 : 1)));
  8938. }
  8939. if (fileCnt !== false){
  8940. cnts.push(self.i18n('files') + ': ' + fileCnt);
  8941. }
  8942. if (cnts.length) {
  8943. cntsTxt = '<br>' + cnts.join(', ');
  8944. }
  8945. dfrd.resolve({
  8946. size: size,
  8947. fileCnt: fileCnt,
  8948. dirCnt: dirCnt,
  8949. formated: (size >= 0 ? self.formatSize(size) : self.i18n('unknown')) + cntsTxt
  8950. });
  8951. });
  8952. self.autoSync();
  8953. });
  8954. return dfrd;
  8955. },
  8956. /**
  8957. * Gets the theme object by settings of options.themes
  8958. *
  8959. * @param String themeid The themeid
  8960. * @return Object jQuery.Deferred
  8961. */
  8962. getTheme : function(themeid) {
  8963. var self = this,
  8964. dfd = $.Deferred(),
  8965. absUrl = function(url, base) {
  8966. if (!base) {
  8967. base = self.convAbsUrl(self.baseUrl);
  8968. }
  8969. if (Array.isArray(url)) {
  8970. return $.map(url, function(v) {
  8971. return absUrl(v, base);
  8972. });
  8973. } else {
  8974. return url.match(/^(?:http|\/\/)/i)? url : base + url.replace(/^(?:\.\/|\/)/, '');
  8975. }
  8976. },
  8977. themeObj, m;
  8978. if (themeid && (themeObj = self.options.themes[themeid])) {
  8979. if (typeof themeObj === 'string') {
  8980. url = absUrl(themeObj);
  8981. if (m = url.match(/^(.+\/)[^/]+\.json$/i)) {
  8982. $.getJSON(url).done(function(data) {
  8983. themeObj = data;
  8984. themeObj.id = themeid;
  8985. if (themeObj.cssurls) {
  8986. themeObj.cssurls = absUrl(themeObj.cssurls, m[1]);
  8987. }
  8988. dfd.resolve(themeObj);
  8989. }).fail(function() {
  8990. dfd.reject();
  8991. });
  8992. } else {
  8993. dfd.resolve({
  8994. id: themeid,
  8995. name: themeid,
  8996. cssurls: [url]
  8997. });
  8998. }
  8999. } else if ($.isPlainObject(themeObj) && themeObj.cssurls) {
  9000. themeObj.id = themeid;
  9001. themeObj.cssurls = absUrl(themeObj.cssurls);
  9002. if (!Array.isArray(themeObj.cssurls)) {
  9003. themeObj.cssurls = [themeObj.cssurls];
  9004. }
  9005. if (!themeObj.name) {
  9006. themeObj.name = themeid;
  9007. }
  9008. dfd.resolve(themeObj);
  9009. } else {
  9010. dfd.reject();
  9011. }
  9012. } else {
  9013. dfd.reject();
  9014. }
  9015. return dfd;
  9016. },
  9017. /**
  9018. * Change current theme
  9019. *
  9020. * @param String themeid The themeid
  9021. * @return Object this elFinder instance
  9022. */
  9023. changeTheme : function(themeid) {
  9024. var self = this;
  9025. if (themeid) {
  9026. if (self.options.themes[themeid] && (!self.theme || self.theme.id !== themeid)) {
  9027. self.getTheme(themeid).done(function(themeObj) {
  9028. if (themeObj.cssurls) {
  9029. $('head>link.elfinder-theme-ext').remove();
  9030. self.loadCss(themeObj.cssurls, {
  9031. className: 'elfinder-theme-ext',
  9032. dfd: $.Deferred().done(function() {
  9033. self.theme = themeObj;
  9034. self.trigger && self.trigger('themechange');
  9035. })
  9036. });
  9037. }
  9038. });
  9039. } else if (themeid === 'default' && self.theme && self.theme.id !== 'default') {
  9040. $('head>link.elfinder-theme-ext').remove();
  9041. self.theme = null;
  9042. self.trigger && self.trigger('themechange');
  9043. }
  9044. }
  9045. return this;
  9046. },
  9047. /**
  9048. * Apply leaf root stats to target directory
  9049. *
  9050. * @param object dir object of target directory
  9051. * @param boolean update is force update
  9052. *
  9053. * @return boolean dir object was chenged
  9054. */
  9055. applyLeafRootStats : function(dir, update) {
  9056. var self = this,
  9057. prev = update? dir : (self.file(dir.hash) || dir),
  9058. prevTs = prev.ts,
  9059. change = false;
  9060. // backup original stats
  9061. if (update || !dir._realStats) {
  9062. dir._realStats = {
  9063. locked: dir.locked || 0,
  9064. dirs: dir.dirs || 0,
  9065. ts: dir.ts
  9066. };
  9067. }
  9068. // set lock
  9069. dir.locked = 1;
  9070. if (!prev.locked) {
  9071. change = true;
  9072. }
  9073. // has leaf root to `dirs: 1`
  9074. dir.dirs = 1;
  9075. if (!prev.dirs) {
  9076. change = true;
  9077. }
  9078. // set ts
  9079. $.each(self.leafRoots[dir.hash], function() {
  9080. var f = self.file(this);
  9081. if (f && f.ts && (dir.ts || 0) < f.ts) {
  9082. dir.ts = f.ts;
  9083. }
  9084. });
  9085. if (prevTs !== dir.ts) {
  9086. change = true;
  9087. }
  9088. return change;
  9089. },
  9090. /**
  9091. * To aborted XHR object
  9092. *
  9093. * @param Object xhr
  9094. * @param Object opts
  9095. *
  9096. * @return void
  9097. */
  9098. abortXHR : function(xhr, o) {
  9099. var opts = o || {};
  9100. if (xhr) {
  9101. opts.quiet && (xhr.quiet = true);
  9102. if (opts.abort && xhr._requestId) {
  9103. this.request({
  9104. data: {
  9105. cmd: 'abort',
  9106. id: xhr._requestId
  9107. },
  9108. preventDefault: true
  9109. });
  9110. }
  9111. xhr.abort();
  9112. xhr = void 0;
  9113. }
  9114. },
  9115. /**
  9116. * Gets the request identifier
  9117. *
  9118. * @return String The request identifier.
  9119. */
  9120. getRequestId : function() {
  9121. return (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
  9122. },
  9123. /**
  9124. * Flip key and value of array or object
  9125. *
  9126. * @param Array | Object { a: 1, b: 1, c: 2 }
  9127. * @param Mixed Static value
  9128. * @return Object { 1: "b", 2: "c" }
  9129. */
  9130. arrayFlip : function (trans, val) {
  9131. var key,
  9132. tmpArr = {},
  9133. isArr = $.isArray(trans);
  9134. for (key in trans) {
  9135. if (isArr || trans.hasOwnProperty(key)) {
  9136. tmpArr[trans[key]] = val || key;
  9137. }
  9138. }
  9139. return tmpArr;
  9140. },
  9141. /**
  9142. * Return array ["name without extention", "extention"]
  9143. *
  9144. * @param String name
  9145. *
  9146. * @return Array
  9147. *
  9148. */
  9149. splitFileExtention : function(name) {
  9150. var m;
  9151. if (m = name.match(/^(.+?)?\.((?:tar\.(?:gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(?:gz|bz2)|[a-z0-9]{1,10})$/i)) {
  9152. if (typeof m[1] === 'undefined') {
  9153. m[1] = '';
  9154. }
  9155. return [m[1], m[2]];
  9156. } else {
  9157. return [name, ''];
  9158. }
  9159. },
  9160. /**
  9161. * Slice the ArrayBuffer by sliceSize
  9162. *
  9163. * @param arraybuffer arrayBuffer The array buffer
  9164. * @param Number sliceSize The slice size
  9165. * @return Array Array of sleced arraybuffer
  9166. */
  9167. sliceArrayBuffer : function(arrayBuffer, sliceSize) {
  9168. var segments= [],
  9169. fi = 0;
  9170. while(fi * sliceSize < arrayBuffer.byteLength){
  9171. segments.push(arrayBuffer.slice(fi * sliceSize, (fi + 1) * sliceSize));
  9172. fi++;
  9173. }
  9174. return segments;
  9175. },
  9176. arrayBufferToBase64 : function(ab) {
  9177. if (!window.btoa) {
  9178. return '';
  9179. }
  9180. var dView = new Uint8Array(ab), // Get a byte view
  9181. arr = Array.prototype.slice.call(dView), // Create a normal array
  9182. arr1 = arr.map(function(item) {
  9183. return String.fromCharCode(item); // Convert
  9184. });
  9185. return window.btoa(arr1.join('')); // Form a string
  9186. },
  9187. log : function(m) { window.console && window.console.log && window.console.log(m); return this; },
  9188. debug : function(type, m) {
  9189. var d = this.options.debug;
  9190. if (d && (d === 'all' || d[type])) {
  9191. window.console && window.console.log && window.console.log('elfinder debug: ['+type+'] ['+this.id+']', m);
  9192. }
  9193. if (type === 'backend-error') {
  9194. if (! this.cwd().hash || (d && (d === 'all' || d['backend-error']))) {
  9195. m = Array.isArray(m)? m : [ m ];
  9196. this.error(m);
  9197. }
  9198. } else if (type === 'backend-debug') {
  9199. this.trigger('backenddebug', m);
  9200. }
  9201. return this;
  9202. },
  9203. time : function(l) { window.console && window.console.time && window.console.time(l); },
  9204. timeEnd : function(l) { window.console && window.console.timeEnd && window.console.timeEnd(l); }
  9205. };
  9206. /**
  9207. * for conpat ex. ie8...
  9208. *
  9209. * Object.keys() - JavaScript | MDN
  9210. * https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
  9211. */
  9212. if (!Object.keys) {
  9213. Object.keys = (function () {
  9214. var hasOwnProperty = Object.prototype.hasOwnProperty,
  9215. hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
  9216. dontEnums = [
  9217. 'toString',
  9218. 'toLocaleString',
  9219. 'valueOf',
  9220. 'hasOwnProperty',
  9221. 'isPrototypeOf',
  9222. 'propertyIsEnumerable',
  9223. 'constructor'
  9224. ],
  9225. dontEnumsLength = dontEnums.length;
  9226. return function (obj) {
  9227. if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
  9228. var result = [];
  9229. for (var prop in obj) {
  9230. if (hasOwnProperty.call(obj, prop)) result.push(prop);
  9231. }
  9232. if (hasDontEnumBug) {
  9233. for (var i=0; i < dontEnumsLength; i++) {
  9234. if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
  9235. }
  9236. }
  9237. return result;
  9238. };
  9239. })();
  9240. }
  9241. // Array.isArray
  9242. if (!Array.isArray) {
  9243. Array.isArray = function(arr) {
  9244. return jQuery.isArray(arr);
  9245. };
  9246. }
  9247. // Object.assign
  9248. if (!Object.assign) {
  9249. Object.assign = function() {
  9250. return jQuery.extend.apply(null, arguments);
  9251. };
  9252. }
  9253. // String.repeat
  9254. if (!String.prototype.repeat) {
  9255. String.prototype.repeat = function(count) {
  9256. 'use strict';
  9257. if (this == null) {
  9258. throw new TypeError('can\'t convert ' + this + ' to object');
  9259. }
  9260. var str = '' + this;
  9261. count = +count;
  9262. if (count != count) {
  9263. count = 0;
  9264. }
  9265. if (count < 0) {
  9266. throw new RangeError('repeat count must be non-negative');
  9267. }
  9268. if (count == Infinity) {
  9269. throw new RangeError('repeat count must be less than infinity');
  9270. }
  9271. count = Math.floor(count);
  9272. if (str.length == 0 || count == 0) {
  9273. return '';
  9274. }
  9275. // Ensuring count is a 31-bit integer allows us to heavily optimize the
  9276. // main part. But anyway, most current (August 2014) browsers can't handle
  9277. // strings 1 << 28 chars or longer, so:
  9278. if (str.length * count >= 1 << 28) {
  9279. throw new RangeError('repeat count must not overflow maximum string size');
  9280. }
  9281. var rpt = '';
  9282. for (var i = 0; i < count; i++) {
  9283. rpt += str;
  9284. }
  9285. return rpt;
  9286. };
  9287. }
  9288. // String.trim
  9289. if (!String.prototype.trim) {
  9290. String.prototype.trim = function() {
  9291. return this.replace(/^\s+|\s+$/g, '');
  9292. };
  9293. }
  9294. // Array.apply
  9295. (function () {
  9296. try {
  9297. Array.apply(null, {});
  9298. return;
  9299. } catch (e) { }
  9300. var toString = Object.prototype.toString,
  9301. arrayType = '[object Array]',
  9302. _apply = Function.prototype.apply,
  9303. slice = /*@cc_on @if (@_jscript_version <= 5.8)
  9304. function () {
  9305. var a = [], i = this.length;
  9306. while (i-- > 0) a[i] = this[i];
  9307. return a;
  9308. }@else@*/Array.prototype.slice/*@end@*/;
  9309. Function.prototype.apply = function apply(thisArg, argArray) {
  9310. return _apply.call(this, thisArg,
  9311. toString.call(argArray) === arrayType ? argArray : slice.call(argArray));
  9312. };
  9313. })();
  9314. // Array.from
  9315. if (!Array.from) {
  9316. Array.from = function(obj) {
  9317. return obj.length === 1 ? [obj[0]] : Array.apply(null, obj);
  9318. };
  9319. }
  9320. // window.requestAnimationFrame and window.cancelAnimationFrame
  9321. if (!window.cancelAnimationFrame) {
  9322. // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
  9323. // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
  9324. // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
  9325. // MIT license
  9326. (function() {
  9327. var lastTime = 0;
  9328. var vendors = ['ms', 'moz', 'webkit', 'o'];
  9329. for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  9330. window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
  9331. window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
  9332. || window[vendors[x]+'CancelRequestAnimationFrame'];
  9333. }
  9334. if (!window.requestAnimationFrame)
  9335. window.requestAnimationFrame = function(callback, element) {
  9336. var currTime = new Date().getTime();
  9337. var timeToCall = Math.max(0, 16 - (currTime - lastTime));
  9338. var id = window.setTimeout(function() { callback(currTime + timeToCall); },
  9339. timeToCall);
  9340. lastTime = currTime + timeToCall;
  9341. return id;
  9342. };
  9343. if (!window.cancelAnimationFrame)
  9344. window.cancelAnimationFrame = function(id) {
  9345. clearTimeout(id);
  9346. };
  9347. }());
  9348. }
  9349. /*
  9350. * File: /js/elFinder.version.js
  9351. */
  9352. /**
  9353. * Application version
  9354. *
  9355. * @type String
  9356. **/
  9357. elFinder.prototype.version = '2.1.49';
  9358. /*
  9359. * File: /js/jquery.elfinder.js
  9360. */
  9361. /*** jQuery UI droppable performance tune for elFinder ***/
  9362. (function(){
  9363. if ($.ui) {
  9364. if ($.ui.ddmanager) {
  9365. var origin = $.ui.ddmanager.prepareOffsets;
  9366. $.ui.ddmanager.prepareOffsets = function( t, event ) {
  9367. var isOutView = function(elem) {
  9368. if (elem.is(':hidden')) {
  9369. return true;
  9370. }
  9371. var rect = elem[0].getBoundingClientRect();
  9372. return document.elementFromPoint(rect.left, rect.top) || document.elementFromPoint(rect.left + rect.width, rect.top + rect.height)? false : true;
  9373. };
  9374. if (event.type === 'mousedown' || t.options.elfRefresh) {
  9375. var i, d,
  9376. m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
  9377. l = m.length;
  9378. for ( i = 0; i < l; i++ ) {
  9379. d = m[ i ];
  9380. if (d.options.autoDisable && (!d.options.disabled || d.options.autoDisable > 1)) {
  9381. d.options.disabled = isOutView(d.element);
  9382. d.options.autoDisable = d.options.disabled? 2 : 1;
  9383. }
  9384. }
  9385. }
  9386. // call origin function
  9387. return origin( t, event );
  9388. };
  9389. }
  9390. }
  9391. })();
  9392. /**
  9393. *
  9394. * jquery.binarytransport.js
  9395. *
  9396. * @description. jQuery ajax transport for making binary data type requests.
  9397. * @version 1.0
  9398. * @author Henry Algus <henryalgus@gmail.com>
  9399. *
  9400. */
  9401. // use this transport for "binary" data type
  9402. $.ajaxTransport('+binary', function(options, originalOptions, jqXHR) {
  9403. // check for conditions and support for blob / arraybuffer response type
  9404. if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob)))))
  9405. {
  9406. var xhr;
  9407. return {
  9408. // create new XMLHttpRequest
  9409. send: function(headers, callback){
  9410. // setup all variables
  9411. xhr = new XMLHttpRequest();
  9412. var url = options.url,
  9413. type = options.type,
  9414. async = options.async || true,
  9415. // blob or arraybuffer. Default is blob
  9416. dataType = options.responseType || 'blob',
  9417. data = options.data || null,
  9418. username = options.username,
  9419. password = options.password;
  9420. xhr.addEventListener('load', function(){
  9421. var data = {};
  9422. data[options.dataType] = xhr.response;
  9423. // make callback and send data
  9424. callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders());
  9425. });
  9426. xhr.open(type, url, async, username, password);
  9427. // setup custom headers
  9428. for (var i in headers ) {
  9429. xhr.setRequestHeader(i, headers[i] );
  9430. }
  9431. // setuo xhrFields
  9432. if (options.xhrFields) {
  9433. for (var key in options.xhrFields) {
  9434. if (key in xhr) {
  9435. xhr[key] = options.xhrFields[key];
  9436. }
  9437. }
  9438. }
  9439. xhr.responseType = dataType;
  9440. xhr.send(data);
  9441. },
  9442. abort: function(){
  9443. xhr.abort();
  9444. }
  9445. };
  9446. }
  9447. });
  9448. /*!
  9449. * jQuery UI Touch Punch 0.2.3
  9450. *
  9451. * Copyright 2011–2014, Dave Furfero
  9452. * Dual licensed under the MIT or GPL Version 2 licenses.
  9453. *
  9454. * Depends:
  9455. * jquery.ui.widget.js
  9456. * jquery.ui.mouse.js
  9457. */
  9458. (function ($) {
  9459. // Detect touch support
  9460. $.support.touch = 'ontouchend' in document;
  9461. // Ignore browsers without touch support
  9462. if (!$.support.touch) {
  9463. return;
  9464. }
  9465. var mouseProto = $.ui.mouse.prototype,
  9466. _mouseInit = mouseProto._mouseInit,
  9467. _mouseDestroy = mouseProto._mouseDestroy,
  9468. touchHandled,
  9469. posX, posY;
  9470. /**
  9471. * Simulate a mouse event based on a corresponding touch event
  9472. * @param {Object} event A touch event
  9473. * @param {String} simulatedType The corresponding mouse event
  9474. */
  9475. function simulateMouseEvent (event, simulatedType) {
  9476. // Ignore multi-touch events
  9477. if (event.originalEvent.touches.length > 1) {
  9478. return;
  9479. }
  9480. if (! $(event.currentTarget).hasClass('touch-punch-keep-default')) {
  9481. event.preventDefault();
  9482. }
  9483. var touch = event.originalEvent.changedTouches[0],
  9484. simulatedEvent = document.createEvent('MouseEvents');
  9485. // Initialize the simulated mouse event using the touch event's coordinates
  9486. simulatedEvent.initMouseEvent(
  9487. simulatedType, // type
  9488. true, // bubbles
  9489. true, // cancelable
  9490. window, // view
  9491. 1, // detail
  9492. touch.screenX, // screenX
  9493. touch.screenY, // screenY
  9494. touch.clientX, // clientX
  9495. touch.clientY, // clientY
  9496. false, // ctrlKey
  9497. false, // altKey
  9498. false, // shiftKey
  9499. false, // metaKey
  9500. 0, // button
  9501. null // relatedTarget
  9502. );
  9503. // Dispatch the simulated event to the target element
  9504. event.target.dispatchEvent(simulatedEvent);
  9505. }
  9506. /**
  9507. * Handle the jQuery UI widget's touchstart events
  9508. * @param {Object} event The widget element's touchstart event
  9509. */
  9510. mouseProto._touchStart = function (event) {
  9511. var self = this;
  9512. // Ignore the event if another widget is already being handled
  9513. if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
  9514. return;
  9515. }
  9516. // Track element position to avoid "false" move
  9517. posX = event.originalEvent.changedTouches[0].screenX.toFixed(0);
  9518. posY = event.originalEvent.changedTouches[0].screenY.toFixed(0);
  9519. // Set the flag to prevent other widgets from inheriting the touch event
  9520. touchHandled = true;
  9521. // Track movement to determine if interaction was a click
  9522. self._touchMoved = false;
  9523. // Simulate the mouseover event
  9524. simulateMouseEvent(event, 'mouseover');
  9525. // Simulate the mousemove event
  9526. simulateMouseEvent(event, 'mousemove');
  9527. // Simulate the mousedown event
  9528. simulateMouseEvent(event, 'mousedown');
  9529. };
  9530. /**
  9531. * Handle the jQuery UI widget's touchmove events
  9532. * @param {Object} event The document's touchmove event
  9533. */
  9534. mouseProto._touchMove = function (event) {
  9535. // Ignore event if not handled
  9536. if (!touchHandled) {
  9537. return;
  9538. }
  9539. // Ignore if it's a "false" move (position not changed)
  9540. var x = event.originalEvent.changedTouches[0].screenX.toFixed(0);
  9541. var y = event.originalEvent.changedTouches[0].screenY.toFixed(0);
  9542. // Ignore if it's a "false" move (position not changed)
  9543. if (Math.abs(posX - x) <= 4 && Math.abs(posY - y) <= 4) {
  9544. return;
  9545. }
  9546. // Interaction was not a click
  9547. this._touchMoved = true;
  9548. // Simulate the mousemove event
  9549. simulateMouseEvent(event, 'mousemove');
  9550. };
  9551. /**
  9552. * Handle the jQuery UI widget's touchend events
  9553. * @param {Object} event The document's touchend event
  9554. */
  9555. mouseProto._touchEnd = function (event) {
  9556. // Ignore event if not handled
  9557. if (!touchHandled) {
  9558. return;
  9559. }
  9560. // Simulate the mouseup event
  9561. simulateMouseEvent(event, 'mouseup');
  9562. // Simulate the mouseout event
  9563. simulateMouseEvent(event, 'mouseout');
  9564. // If the touch interaction did not move, it should trigger a click
  9565. if (!this._touchMoved) {
  9566. // Simulate the click event
  9567. simulateMouseEvent(event, 'click');
  9568. }
  9569. // Unset the flag to allow other widgets to inherit the touch event
  9570. touchHandled = false;
  9571. this._touchMoved = false;
  9572. };
  9573. /**
  9574. * A duck punch of the $.ui.mouse _mouseInit method to support touch events.
  9575. * This method extends the widget with bound touch event handlers that
  9576. * translate touch events to mouse events and pass them to the widget's
  9577. * original mouse event handling methods.
  9578. */
  9579. mouseProto._mouseInit = function () {
  9580. var self = this;
  9581. if (self.element.hasClass('touch-punch')) {
  9582. // Delegate the touch handlers to the widget's element
  9583. self.element.on({
  9584. touchstart: $.proxy(self, '_touchStart'),
  9585. touchmove: $.proxy(self, '_touchMove'),
  9586. touchend: $.proxy(self, '_touchEnd')
  9587. });
  9588. }
  9589. // Call the original $.ui.mouse init method
  9590. _mouseInit.call(self);
  9591. };
  9592. /**
  9593. * Remove the touch event handlers
  9594. */
  9595. mouseProto._mouseDestroy = function () {
  9596. var self = this;
  9597. if (self.element.hasClass('touch-punch')) {
  9598. // Delegate the touch handlers to the widget's element
  9599. self.element.off({
  9600. touchstart: $.proxy(self, '_touchStart'),
  9601. touchmove: $.proxy(self, '_touchMove'),
  9602. touchend: $.proxy(self, '_touchEnd')
  9603. });
  9604. }
  9605. // Call the original $.ui.mouse destroy method
  9606. _mouseDestroy.call(self);
  9607. };
  9608. })(jQuery);
  9609. $.fn.elfinder = function(o, o2) {
  9610. if (o === 'instance') {
  9611. return this.getElFinder();
  9612. } else if (o === 'ondemand') {
  9613. }
  9614. return this.each(function() {
  9615. var cmd = typeof o === 'string' ? o : '',
  9616. bootCallback = typeof o2 === 'function'? o2 : void(0),
  9617. elfinder = this.elfinder,
  9618. opts, reloadCallback;
  9619. if (!elfinder) {
  9620. if ($.isPlainObject(o)) {
  9621. new elFinder(this, o, bootCallback);
  9622. }
  9623. } else {
  9624. switch(cmd) {
  9625. case 'close':
  9626. case 'hide':
  9627. elfinder.hide();
  9628. break;
  9629. case 'open':
  9630. case 'show':
  9631. elfinder.show();
  9632. break;
  9633. case 'destroy':
  9634. elfinder.destroy();
  9635. break;
  9636. case 'reload':
  9637. case 'restart':
  9638. if (elfinder) {
  9639. opts = $.extend(true, elfinder.options, $.isPlainObject(o2)? o2 : {});
  9640. bootCallback = elfinder.bootCallback;
  9641. if (elfinder.reloadCallback && $.isFunction(elfinder.reloadCallback)) {
  9642. elfinder.reloadCallback(opts, bootCallback);
  9643. } else {
  9644. elfinder.destroy();
  9645. new elFinder(this, opts, bootCallback);
  9646. }
  9647. }
  9648. break;
  9649. }
  9650. }
  9651. });
  9652. };
  9653. $.fn.getElFinder = function() {
  9654. var instance;
  9655. this.each(function() {
  9656. if (this.elfinder) {
  9657. instance = this.elfinder;
  9658. return false;
  9659. }
  9660. });
  9661. return instance;
  9662. };
  9663. $.fn.elfUiWidgetInstance = function(name) {
  9664. try {
  9665. return this[name]('instance');
  9666. } catch(e) {
  9667. // fallback for jQuery UI < 1.11
  9668. var data = this.data('ui-' + name);
  9669. if (data && typeof data === 'object' && data.widgetFullName === 'ui-' + name) {
  9670. return data;
  9671. }
  9672. return null;
  9673. }
  9674. };
  9675. // function scrollRight
  9676. if (! $.fn.scrollRight) {
  9677. $.fn.extend({
  9678. scrollRight: function (val) {
  9679. var node = this.get(0);
  9680. if (val === undefined) {
  9681. return Math.max(0, node.scrollWidth - (node.scrollLeft + node.clientWidth));
  9682. }
  9683. return this.scrollLeft(node.scrollWidth - node.clientWidth - val);
  9684. }
  9685. });
  9686. }
  9687. // function scrollBottom
  9688. if (! $.fn.scrollBottom) {
  9689. $.fn.extend({
  9690. scrollBottom: function(val) {
  9691. var node = this.get(0);
  9692. if (val === undefined) {
  9693. return Math.max(0, node.scrollHeight - (node.scrollTop + node.clientHeight));
  9694. }
  9695. return this.scrollTop(node.scrollHeight - node.clientHeight - val);
  9696. }
  9697. });
  9698. }
  9699. /*
  9700. * File: /js/elFinder.mimetypes.js
  9701. */
  9702. 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","application\/plt":"plt","application\/sat":"sat","application\/step":"step","text\/x-httpd-cgi":"cgi","text\/x-asap":"asp","text\/x-jsp":"jsp"};
  9703. /*
  9704. * File: /js/elFinder.options.js
  9705. */
  9706. /**
  9707. * Default elFinder config
  9708. *
  9709. * @type Object
  9710. * @autor Dmitry (dio) Levashov
  9711. */
  9712. elFinder.prototype._options = {
  9713. /**
  9714. * URLs of 3rd party libraries CDN
  9715. *
  9716. * @type Object
  9717. */
  9718. cdns : {
  9719. // for editor etc.
  9720. ace : 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.2',
  9721. codemirror : 'https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.43.0',
  9722. ckeditor : 'https://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.11.2',
  9723. ckeditor5 : 'https://cdn.ckeditor.com/ckeditor5/11.2.0',
  9724. tinymce : 'https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.0.0',
  9725. simplemde : 'https://cdnjs.cloudflare.com/ajax/libs/simplemde/1.11.2',
  9726. fabric16 : 'https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.7',
  9727. tui : 'https://uicdn.toast.com',
  9728. // for quicklook etc.
  9729. hls : 'https://cdnjs.cloudflare.com/ajax/libs/hls.js/0.10.1/hls.min.js',
  9730. dash : 'https://cdnjs.cloudflare.com/ajax/libs/dashjs/2.9.3/dash.all.min.js',
  9731. flv : 'https://cdnjs.cloudflare.com/ajax/libs/flv.js/1.5.0/flv.min.js',
  9732. prettify : 'https://cdn.jsdelivr.net/gh/google/code-prettify@453bd5f51e61245339b738b1bbdd42d7848722ba/loader/run_prettify.js',
  9733. psd : 'https://cdnjs.cloudflare.com/ajax/libs/psd.js/3.2.0/psd.min.js',
  9734. rar : 'https://cdn.jsdelivr.net/gh/nao-pon/rar.js@6cef13ec66dd67992fc7f3ea22f132d770ebaf8b/rar.min.js',
  9735. 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
  9736. zlibGunzip : 'https://cdn.jsdelivr.net/gh/imaya/zlib.js@0.3.1/bin/gunzip.min.js',
  9737. marked : 'https://cdnjs.cloudflare.com/ajax/libs/marked/0.6.1:/marked.min.js',
  9738. sparkmd5 : 'https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.min.js',
  9739. jssha : 'https://cdnjs.cloudflare.com/ajax/libs/jsSHA/2.3.1/sha.js',
  9740. amr : 'https://cdn.jsdelivr.net/gh/yxl/opencore-amr-js@dcf3d2b5f384a1d9ded2a54e4c137a81747b222b/js/amrnb.js'
  9741. },
  9742. /**
  9743. * Connector url. Required!
  9744. *
  9745. * @type String
  9746. */
  9747. url : '',
  9748. /**
  9749. * Ajax request type.
  9750. *
  9751. * @type String
  9752. * @default "get"
  9753. */
  9754. requestType : 'get',
  9755. /**
  9756. * Use CORS to connector url
  9757. *
  9758. * @type Boolean|null true|false|null(Auto detect)
  9759. */
  9760. cors : null,
  9761. /**
  9762. * Maximum number of concurrent connections on request
  9763. *
  9764. * @type Number
  9765. * @default 3
  9766. */
  9767. requestMaxConn : 3,
  9768. /**
  9769. * Transport to send request to backend.
  9770. * Required for future extensions using websockets/webdav etc.
  9771. * Must be an object with "send" method.
  9772. * transport.send must return $.Deferred() object
  9773. *
  9774. * @type Object
  9775. * @default null
  9776. * @example
  9777. * transport : {
  9778. * init : function(elfinderInstance) { },
  9779. * send : function(options) {
  9780. * var dfrd = $.Deferred();
  9781. * // connect to backend ...
  9782. * return dfrd;
  9783. * },
  9784. * upload : function(data) {
  9785. * var dfrd = $.Deferred();
  9786. * // upload ...
  9787. * return dfrd;
  9788. * }
  9789. *
  9790. * }
  9791. **/
  9792. transport : {},
  9793. /**
  9794. * URL to upload file to.
  9795. * If not set - connector URL will be used
  9796. *
  9797. * @type String
  9798. * @default ''
  9799. */
  9800. urlUpload : '',
  9801. /**
  9802. * Allow to drag and drop to upload files
  9803. *
  9804. * @type Boolean|String
  9805. * @default 'auto'
  9806. */
  9807. dragUploadAllow : 'auto',
  9808. /**
  9809. * Confirmation dialog displayed at the time of overwriting upload
  9810. *
  9811. * @type Boolean
  9812. * @default true
  9813. */
  9814. overwriteUploadConfirm : true,
  9815. /**
  9816. * Max size of chunked data of file upload
  9817. *
  9818. * @type Number
  9819. * @default 10485760(10MB)
  9820. */
  9821. uploadMaxChunkSize : 10485760,
  9822. /**
  9823. * Regular expression of file name to exclude when uploading folder
  9824. *
  9825. * @type Object
  9826. * @default { win: /^(?:desktop\.ini|thumbs\.db)$/i, mac: /^\.ds_store$/i }
  9827. */
  9828. folderUploadExclude : {
  9829. win: /^(?:desktop\.ini|thumbs\.db)$/i,
  9830. mac: /^\.ds_store$/i
  9831. },
  9832. /**
  9833. * Timeout for upload using iframe
  9834. *
  9835. * @type Number
  9836. * @default 0 - no timeout
  9837. */
  9838. iframeTimeout : 0,
  9839. /**
  9840. * Data to append to all requests and to upload files
  9841. *
  9842. * @type Object
  9843. * @default {}
  9844. */
  9845. customData : {},
  9846. /**
  9847. * Event listeners to bind on elFinder init
  9848. *
  9849. * @type Object
  9850. * @default {}
  9851. */
  9852. handlers : {},
  9853. /**
  9854. * Any custom headers to send across every ajax request
  9855. *
  9856. * @type Object
  9857. * @default {}
  9858. */
  9859. customHeaders : {},
  9860. /**
  9861. * Any custom xhrFields to send across every ajax request
  9862. *
  9863. * @type Object
  9864. * @default {}
  9865. */
  9866. xhrFields : {},
  9867. /**
  9868. * Interface language
  9869. *
  9870. * @type String
  9871. * @default "en"
  9872. */
  9873. lang : 'en',
  9874. /**
  9875. * Base URL of elfFinder library starting from Manager HTML
  9876. * Auto detect when empty value
  9877. *
  9878. * @type String
  9879. * @default ""
  9880. */
  9881. baseUrl : '',
  9882. /**
  9883. * Base URL of i18n js files
  9884. * baseUrl + "js/i18n/" when empty value
  9885. *
  9886. * @type String
  9887. * @default ""
  9888. */
  9889. i18nBaseUrl : '',
  9890. /**
  9891. * Auto load required CSS
  9892. * `false` to disable this function or
  9893. * CSS URL Array to load additional CSS files
  9894. *
  9895. * @type Boolean|Array
  9896. * @default true
  9897. */
  9898. cssAutoLoad : true,
  9899. /**
  9900. * Theme to load
  9901. * {"themeid" : "Theme CSS URL"} or
  9902. * {"themeid" : "Theme manifesto.json URL"} or
  9903. * Theme manifesto.json Object
  9904. * {
  9905. * "themeid" : {
  9906. * "name":"Theme Name",
  9907. * "cssurls":"Theme CSS URL",
  9908. * "author":"Author Name",
  9909. * "email":"Author Email",
  9910. * "license":"License",
  9911. * "link":"Web Site URL",
  9912. * "image":"Screen Shot URL",
  9913. * "description":"Description"
  9914. * }
  9915. * }
  9916. *
  9917. * @type Object
  9918. */
  9919. themes : {},
  9920. /**
  9921. * Theme id to initial theme
  9922. *
  9923. * @type String|Null
  9924. */
  9925. theme : null,
  9926. /**
  9927. * Maximum value of error dialog open at the same time
  9928. *
  9929. * @type Number
  9930. */
  9931. maxErrorDialogs : 5,
  9932. /**
  9933. * Additional css class for filemanager node.
  9934. *
  9935. * @type String
  9936. */
  9937. cssClass : '',
  9938. /**
  9939. * Active commands list. '*' means all of the commands that have been load.
  9940. * If some required commands will be missed here, elFinder will add its
  9941. *
  9942. * @type Array
  9943. */
  9944. commands : ['*'],
  9945. // Available commands list
  9946. //commands : [
  9947. // 'archive', 'back', 'chmod', 'colwidth', 'copy', 'cut', 'download', 'duplicate', 'edit', 'extract',
  9948. // 'forward', 'fullscreen', 'getfile', 'help', 'home', 'info', 'mkdir', 'mkfile', 'netmount', 'netunmount',
  9949. // 'open', 'opendir', 'paste', 'places', 'quicklook', 'reload', 'rename', 'resize', 'restore', 'rm',
  9950. // 'search', 'sort', 'up', 'upload', 'view', 'zipdl'
  9951. //],
  9952. /**
  9953. * Commands options.
  9954. *
  9955. * @type Object
  9956. **/
  9957. commandsOptions : {
  9958. // // configure shortcuts of any command
  9959. // // add `shortcuts` property into each command
  9960. // any_command_name : {
  9961. // shortcuts : [] // for disable this command's shortcuts
  9962. // },
  9963. // any_command_name : {
  9964. // shortcuts : function(fm, shortcuts) {
  9965. // // for add `CTRL + E` for this command action
  9966. // shortcuts[0]['pattern'] += ' ctrl+e';
  9967. // return shortcuts;
  9968. // }
  9969. // },
  9970. // any_command_name : {
  9971. // shortcuts : function(fm, shortcuts) {
  9972. // // for full customize of this command's shortcuts
  9973. // return [ { pattern: 'ctrl+e ctrl+down numpad_enter' + (fm.OS != 'mac' && ' enter') } ];
  9974. // }
  9975. // },
  9976. // "getfile" command options.
  9977. getfile : {
  9978. onlyURL : false,
  9979. // allow to return multiple files info
  9980. multiple : false,
  9981. // allow to return filers info
  9982. folders : false,
  9983. // action after callback (""/"close"/"destroy")
  9984. oncomplete : '',
  9985. // action when callback is fail (""/"close"/"destroy")
  9986. onerror : '',
  9987. // get path before callback call
  9988. getPath : true,
  9989. // get image sizes before callback call
  9990. getImgSize : false
  9991. },
  9992. open : {
  9993. // HTTP method that request to the connector when item URL is not valid URL.
  9994. // If you set to "get" will be displayed request parameter in the browser's location field
  9995. // so if you want to conceal its parameters should be given "post".
  9996. // Nevertheless, please specify "get" if you want to enable the partial request by HTTP Range header.
  9997. method : 'post',
  9998. // Where to open into : 'window'(default), 'tab' or 'tabs'
  9999. // 'tabs' opens in each tabs
  10000. into : 'window',
  10001. // Default command list of action when select file
  10002. // String value that is 'Command Name' or 'Command Name1/CommandName2...'
  10003. selectAction : 'open'
  10004. },
  10005. opennew : {
  10006. // URL of to open elFinder manager
  10007. // Default '' : Origin URL
  10008. url : '',
  10009. // Use search query of origin URL
  10010. useOriginQuery : true
  10011. },
  10012. // "upload" command options.
  10013. upload : {
  10014. // Open elFinder upload dialog: 'button' OR Open system OS upload dialog: 'uploadbutton'
  10015. ui : 'button'
  10016. },
  10017. // "download" command options.
  10018. download : {
  10019. // max request to download files when zipdl disabled
  10020. maxRequests : 10,
  10021. // minimum count of files to use zipdl
  10022. minFilesZipdl : 2
  10023. },
  10024. // "quicklook" command options.
  10025. quicklook : {
  10026. autoplay : true,
  10027. width : 450,
  10028. height : 300,
  10029. // ControlsList of HTML5 audio/video preview
  10030. // see https://googlechrome.github.io/samples/media/controlslist.html
  10031. mediaControlsList : '', // e.g. 'nodownload nofullscreen noremoteplayback'
  10032. // Show toolbar of PDF preview (with <embed> tag)
  10033. pdfToolbar : true,
  10034. // Maximum characters length to preview
  10035. textMaxlen : 2000,
  10036. // quicklook window must be contained in elFinder node on window open (true|false)
  10037. contain : false,
  10038. // preview window into NavDock (0 : undocked | 1 : docked(show) | 2 : docked(hide))
  10039. docked : 0,
  10040. // Docked preview height ('auto' or Number of pixel) 'auto' is setted to the Navbar width
  10041. dockHeight : 'auto',
  10042. // media auto play when docked
  10043. dockAutoplay : false,
  10044. // Google Maps API key (Require Maps JavaScript API)
  10045. googleMapsApiKey : '',
  10046. // Google Maps API Options
  10047. googleMapsOpts : {
  10048. maps : {},
  10049. kml : {
  10050. suppressInfoWindows : false,
  10051. preserveViewport : false
  10052. }
  10053. },
  10054. // ViewerJS (https://viewerjs.org/) Options
  10055. // To enable this you need to place ViewerJS on the same server as elFinder and specify that URL in `url`.
  10056. viewerjs : {
  10057. url: '', // Example '/ViewerJS/index.html'
  10058. mimes: ['application/pdf', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.presentation']
  10059. },
  10060. // MIME types to CAD-Files and 3D-Models online viewer on sharecad.org
  10061. // 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']
  10062. sharecadMimes : [],
  10063. // MIME types to use Google Docs online viewer
  10064. // 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']
  10065. googleDocsMimes : [],
  10066. // MIME types to use Microsoft Office Online viewer
  10067. // 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']
  10068. // These MIME types override "googleDocsMimes"
  10069. officeOnlineMimes : [],
  10070. // File size (byte) threshold when using the dim command for obtain the image size necessary to image preview
  10071. getDimThreshold : 200000,
  10072. // MIME-Type regular expression that does not check empty files
  10073. mimeRegexNotEmptyCheck : /^application\/vnd\.google-apps\./
  10074. },
  10075. // "quicklook" command options.
  10076. edit : {
  10077. // dialog width, integer(px) or integer+'%' (example: 650, '80%' ...)
  10078. dialogWidth : void(0),
  10079. // list of allowed mimetypes to edit of text files
  10080. // if empty - any text files can be edited
  10081. mimes : [],
  10082. // MIME-types of text file to make as empty files
  10083. makeTextMimes : ['text/plain', 'text/css', 'text/html'],
  10084. // Use the editor stored in the browser
  10085. // This value allowd overwrite with user preferences
  10086. useStoredEditor : false,
  10087. // Open the maximized editor window
  10088. // This value allowd overwrite with user preferences
  10089. editorMaximized : false,
  10090. // edit files in wysisyg's
  10091. editors : [
  10092. // {
  10093. // /**
  10094. // * editor info
  10095. // * @type Object
  10096. // */
  10097. // info : { name: 'Editor Name' },
  10098. // /**
  10099. // * files mimetypes allowed to edit in current wysisyg
  10100. // * @type Array
  10101. // */
  10102. // mimes : ['text/html'],
  10103. // /**
  10104. // * HTML element for editing area (optional for text editor)
  10105. // * @type String
  10106. // */
  10107. // html : '<textarea></textarea>',
  10108. // /**
  10109. // * Initialize editing area node (optional for text editor)
  10110. // *
  10111. // * @param String dialog DOM id
  10112. // * @param Object target file object
  10113. // * @param String target file content (text or Data URI Scheme(binary file))
  10114. // * @param Object elFinder instance
  10115. // * @type Function
  10116. // */
  10117. // init : function(id, file, content, fm) {
  10118. // $(this).attr('id', id + '-text').val(content);
  10119. // },
  10120. // /**
  10121. // * Get edited contents (optional for text editor)
  10122. // * @type Function
  10123. // */
  10124. // getContent : function() {
  10125. // return $(this).val();
  10126. // },
  10127. // /**
  10128. // * Called when "edit" dialog loaded.
  10129. // * Place to init wysisyg.
  10130. // * Can return wysisyg instance
  10131. // *
  10132. // * @param DOMElement textarea node
  10133. // * @return Object editor instance|jQuery.Deferred(return instance on resolve())
  10134. // */
  10135. // load : function(textarea) { },
  10136. // /**
  10137. // * Called before "edit" dialog closed.
  10138. // * Place to destroy wysisyg instance.
  10139. // *
  10140. // * @param DOMElement textarea node
  10141. // * @param Object wysisyg instance (if was returned by "load" callback)
  10142. // * @return void
  10143. // */
  10144. // close : function(textarea, instance) { },
  10145. // /**
  10146. // * Called before file content send to backend.
  10147. // * Place to update textarea content if needed.
  10148. // *
  10149. // * @param DOMElement textarea node
  10150. // * @param Object wysisyg instance (if was returned by "load" callback)
  10151. // * @return void
  10152. // */
  10153. // save : function(textarea, instance) {},
  10154. // /**
  10155. // * Called after load() or save().
  10156. // * Set focus to wysisyg editor.
  10157. // *
  10158. // * @param DOMElement textarea node
  10159. // * @param Object wysisyg instance (if was returned by "load" callback)
  10160. // * @return void
  10161. // */
  10162. // focus : function(textarea, instance) {}
  10163. // /**
  10164. // * Called after dialog resized..
  10165. // *
  10166. // * @param DOMElement textarea node
  10167. // * @param Object wysisyg instance (if was returned by "load" callback)
  10168. // * @param Object resize event object
  10169. // * @param Object data object
  10170. // * @return void
  10171. // */
  10172. // resize : function(textarea, instance, event, data) {}
  10173. //
  10174. // }
  10175. ],
  10176. // Character encodings of select box
  10177. encodings : ['Big5', 'Big5-HKSCS', 'Cp437', 'Cp737', 'Cp775', 'Cp850', 'Cp852', 'Cp855', 'Cp857', 'Cp858',
  10178. 'Cp862', 'Cp866', 'Cp874', 'EUC-CN', 'EUC-JP', 'EUC-KR', 'GB18030', 'ISO-2022-CN', 'ISO-2022-JP', 'ISO-2022-KR',
  10179. 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4', 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7',
  10180. 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-13', 'ISO-8859-15', 'KOI8-R', 'KOI8-U', 'Shift-JIS',
  10181. 'Windows-1250', 'Windows-1251', 'Windows-1252', 'Windows-1253', 'Windows-1254', 'Windows-1257'],
  10182. // options for extra editors
  10183. extraOptions : {
  10184. // upload command options
  10185. uploadOpts : {},
  10186. // TUI Image Editor's options
  10187. tuiImgEditOpts : {
  10188. // Path prefix of icon-a.svg, icon-b.svg, icon-c.svg and icon-d.svg in the Theme.
  10189. // `iconsPath` MUST follow the same origin policy.
  10190. iconsPath : void(0), // default is "./img/tui-"
  10191. // Theme object
  10192. theme : {}
  10193. },
  10194. // Pixo image editor constructor options - https://pixoeditor.com/
  10195. // Require 'apikey' to enable it
  10196. pixo: {
  10197. apikey: ''
  10198. },
  10199. // Specify the Creative Cloud API key when using Creative SDK image editor of Creative Cloud.
  10200. // You can get the API key at https://console.adobe.io/.
  10201. creativeCloudApiKey : '',
  10202. // Browsing manager URL for CKEditor, TinyMCE
  10203. // Uses self location with the empty value or not defined.
  10204. //managerUrl : 'elfinder.html'
  10205. managerUrl : null,
  10206. // CKEditor editor options
  10207. ckeditor: {},
  10208. // CKEditor 5 editor options
  10209. ckeditor5: {
  10210. // builds mode - 'classic', 'inline' or 'balloon'
  10211. mode: 'inline'
  10212. },
  10213. // TinyMCE editor options
  10214. tinymce : {},
  10215. // Setting for Online-Convert.com
  10216. onlineConvert : {
  10217. maxSize : 100, // (MB) Max 100MB on free account
  10218. showLink : true // It must be enabled with free account
  10219. }
  10220. }
  10221. },
  10222. fullscreen : {
  10223. // fullscreen mode 'screen'(When the browser supports it) or 'window'
  10224. mode: 'screen' // 'screen' or 'window'
  10225. },
  10226. search : {
  10227. // Incremental search from the current view
  10228. incsearch : {
  10229. enable : true, // is enable true or false
  10230. minlen : 1, // minimum number of characters
  10231. wait : 500 // wait milliseconds
  10232. },
  10233. // Additional search types
  10234. searchTypes : {
  10235. // "SearchMime" is implemented in default
  10236. SearchMime : { // The key is search type that send to the connector
  10237. name : 'btnMime', // Button text to be processed in i18n()
  10238. title : 'searchMime',// Button title to be processed in i18n()
  10239. incsearch : 'mime' // Incremental search target filed name of the file object
  10240. // Or Callable function
  10241. /* incsearch function example
  10242. function(queryObject, cwdHashes, elFinderInstance) {
  10243. var q = queryObject.val;
  10244. var regex = queryObject.regex;
  10245. var matchedHashes = $.grep(cwdHashes, function(hash) {
  10246. var file = elFinderInstance.file(hash);
  10247. return (file && file.mime && file.mime.match(regex))? true : false;
  10248. });
  10249. return matchedHashes;
  10250. }
  10251. */
  10252. }
  10253. }
  10254. },
  10255. // "info" command options.
  10256. info : {
  10257. // If the URL of the Directory is null,
  10258. // it is assumed that the link destination is a URL to open the folder in elFinder
  10259. nullUrlDirLinkSelf : true,
  10260. // Information items to be hidden by default
  10261. // These name are 'size', 'aliasfor', 'path', 'link', 'dim', 'modify', 'perms', 'locked', 'owner', 'group', 'perm' and your custom info items label
  10262. hideItems : [],
  10263. // Maximum file size (byte) to get file contents hash (md5, sha256 ...)
  10264. showHashMaxsize : 104857600, // 100 MB
  10265. // Array of hash algorisms to show on info dialog
  10266. // These name are 'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'shake128' and 'shake256'
  10267. showHashAlgorisms : ['md5', 'sha256'],
  10268. custom : {
  10269. // /**
  10270. // * Example of custom info `desc`
  10271. // */
  10272. // desc : {
  10273. // /**
  10274. // * Lable (require)
  10275. // * It is filtered by the `fm.i18n()`
  10276. // *
  10277. // * @type String
  10278. // */
  10279. // label : 'Description',
  10280. //
  10281. // /**
  10282. // * Template (require)
  10283. // * `{id}` is replaced in dialog.id
  10284. // *
  10285. // * @type String
  10286. // */
  10287. // tpl : '<div class="elfinder-info-desc"><span class="elfinder-spinner"></span></div>',
  10288. //
  10289. // /**
  10290. // * Restricts to mimetypes (optional)
  10291. // * Exact match or category match
  10292. // *
  10293. // * @type Array
  10294. // */
  10295. // mimes : ['text', 'image/jpeg', 'directory'],
  10296. //
  10297. // /**
  10298. // * Restricts to file.hash (optional)
  10299. // *
  10300. // * @ type Regex
  10301. // */
  10302. // hashRegex : /^l\d+_/,
  10303. //
  10304. // /**
  10305. // * Request that asks for the description and sets the field (optional)
  10306. // *
  10307. // * @type Function
  10308. // */
  10309. // action : function(file, fm, dialog) {
  10310. // fm.request({
  10311. // data : { cmd : 'desc', target: file.hash },
  10312. // preventDefault: true,
  10313. // })
  10314. // .fail(function() {
  10315. // dialog.find('div.elfinder-info-desc').html(fm.i18n('unknown'));
  10316. // })
  10317. // .done(function(data) {
  10318. // dialog.find('div.elfinder-info-desc').html(data.desc);
  10319. // });
  10320. // }
  10321. // }
  10322. }
  10323. },
  10324. mkdir: {
  10325. // Enable automatic switching function ["New Folder" / "Into New Folder"] of toolbar buttton
  10326. intoNewFolderToolbtn: false
  10327. },
  10328. resize: {
  10329. // defalt status of snap to 8px grid of the jpeg image ("enable" or "disable")
  10330. grid8px : 'disable',
  10331. // Preset size array [width, height]
  10332. presetSize : [[320, 240], [400, 400], [640, 480], [800,600]],
  10333. // File size (bytes) threshold when using the `dim` command for obtain the image size necessary to start editing
  10334. getDimThreshold : 204800,
  10335. // File size (bytes) to request to get substitute image (400px) with the `dim` command
  10336. dimSubImgSize : 307200
  10337. },
  10338. rm: {
  10339. // If trash is valid, items moves immediately to the trash holder without confirm.
  10340. quickTrash : true,
  10341. // Maximum wait seconds when checking the number of items to into the trash
  10342. infoCheckWait : 10,
  10343. // Maximum number of items that can be placed into the Trash at one time
  10344. toTrashMaxItems : 1000
  10345. },
  10346. help : {
  10347. // Tabs to show
  10348. view : ['about', 'shortcuts', 'help', 'integrations', 'debug'],
  10349. // HTML source URL of the heip tab
  10350. helpSource : ''
  10351. },
  10352. preference : {
  10353. // dialog width
  10354. width: 600,
  10355. // dialog height
  10356. height: 400,
  10357. // tabs setting see preference.js : build()
  10358. categories: null,
  10359. // preference setting see preference.js : build()
  10360. prefs: null,
  10361. // language setting see preference.js : build()
  10362. langs: null,
  10363. // Command list of action when select file
  10364. // Array value are 'Command Name' or 'Command Name1/CommandName2...'
  10365. selectActions : ['open', 'edit/download', 'resize/edit/download', 'download', 'quicklook']
  10366. }
  10367. },
  10368. /**
  10369. * Callback for prepare boot up
  10370. *
  10371. * - The this object in the function is an elFinder node
  10372. * - The first parameter is elFinder Instance
  10373. * - The second parameter is an object of other parameters
  10374. * For now it can use `dfrdsBeforeBootup` Array
  10375. *
  10376. * @type Function
  10377. * @default null
  10378. * @return void
  10379. */
  10380. bootCallback : null,
  10381. /**
  10382. * Callback for "getfile" commands.
  10383. * Required to use elFinder with WYSIWYG editors etc..
  10384. *
  10385. * @type Function
  10386. * @default null (command not active)
  10387. */
  10388. getFileCallback : null,
  10389. /**
  10390. * Default directory view. icons/list
  10391. *
  10392. * @type String
  10393. * @default "icons"
  10394. */
  10395. defaultView : 'icons',
  10396. /**
  10397. * Hash of default directory path to open
  10398. *
  10399. * NOTE: This setting will be disabled if the target folder is specified in location.hash.
  10400. *
  10401. * If you want to find the hash in Javascript
  10402. * can be obtained with the following code. (In the case of a standard hashing method)
  10403. *
  10404. * var volumeId = 'l1_'; // volume id
  10405. * var path = 'path/to/target'; // without root path
  10406. * //var path = 'path\\to\\target'; // use \ on windows server
  10407. * var hash = volumeId + btoa(path).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '.').replace(/\.+$/, '');
  10408. *
  10409. * @type String
  10410. * @default ""
  10411. */
  10412. startPathHash : '',
  10413. /**
  10414. * Emit a sound when a file is deleted
  10415. * Sounds are in sounds/ folder
  10416. *
  10417. * @type Boolean
  10418. * @default true
  10419. */
  10420. sound : true,
  10421. /**
  10422. * UI plugins to load.
  10423. * Current dir ui and dialogs loads always.
  10424. * Here set not required plugins as folders tree/toolbar/statusbar etc.
  10425. *
  10426. * @type Array
  10427. * @default ['toolbar', 'places', 'tree', 'path', 'stat']
  10428. * @full ['toolbar', 'places', 'tree', 'path', 'stat']
  10429. */
  10430. ui : ['toolbar', 'places', 'tree', 'path', 'stat'],
  10431. /**
  10432. * Some UI plugins options.
  10433. * @type Object
  10434. */
  10435. uiOptions : {
  10436. // toolbar configuration
  10437. toolbar : [
  10438. ['home', 'back', 'forward', 'up', 'reload'],
  10439. ['netmount'],
  10440. ['mkdir', 'mkfile', 'upload'],
  10441. ['open', 'download', 'getfile'],
  10442. ['undo', 'redo'],
  10443. ['copy', 'cut', 'paste', 'rm', 'empty', 'hide'],
  10444. ['duplicate', 'rename', 'edit', 'resize', 'chmod'],
  10445. ['selectall', 'selectnone', 'selectinvert'],
  10446. ['quicklook', 'info'],
  10447. ['extract', 'archive'],
  10448. ['search'],
  10449. ['view', 'sort'],
  10450. ['preference', 'help'],
  10451. ['fullscreen']
  10452. ],
  10453. // toolbar extra options
  10454. toolbarExtra : {
  10455. // also displays the text label on the button (true / false / 'none')
  10456. displayTextLabel: false,
  10457. // Exclude `displayTextLabel` setting UA type
  10458. labelExcludeUA: ['Mobile'],
  10459. // auto hide on initial open
  10460. autoHideUA: ['Mobile'],
  10461. // Initial setting value of hide button in toolbar setting
  10462. defaultHides: ['home', 'reload'],
  10463. // show Preference button ('none', 'auto', 'always')
  10464. // If you do not include 'preference' in the context menu you should specify 'auto' or 'always'
  10465. showPreferenceButton: 'none',
  10466. // show Preference button into contextmenu of the toolbar (true / false)
  10467. preferenceInContextmenu: true
  10468. },
  10469. // directories tree options
  10470. tree : {
  10471. // set path info to attr title
  10472. attrTitle : true,
  10473. // expand current root on init
  10474. openRootOnLoad : true,
  10475. // expand current work directory on open
  10476. openCwdOnOpen : true,
  10477. // auto loading current directory parents and do expand their node.
  10478. syncTree : true,
  10479. // Maximum number of display of each child trees
  10480. // The tree of directories with children exceeding this number will be split
  10481. subTreeMax : 100,
  10482. // Numbar of max connctions of subdirs request
  10483. subdirsMaxConn : 2,
  10484. // Number of max simultaneous processing directory of subdirs
  10485. subdirsAtOnce : 5,
  10486. // Durations of each animations
  10487. durations : {
  10488. slideUpDown : 'fast',
  10489. autoScroll : 'fast'
  10490. }
  10491. // ,
  10492. // /**
  10493. // * Add CSS class name to navbar directories (optional)
  10494. // * see: https://github.com/Studio-42/elFinder/pull/1061,
  10495. // * https://github.com/Studio-42/elFinder/issues/1231
  10496. // *
  10497. // * @type Function
  10498. // */
  10499. // getClass: function(dir) {
  10500. // // e.g. This adds the directory's name (lowercase) with prefix as a CSS class
  10501. // return 'elfinder-tree-' + dir.name.replace(/[ "]/g, '').toLowerCase();
  10502. // }
  10503. },
  10504. // navbar options
  10505. navbar : {
  10506. minWidth : 150,
  10507. maxWidth : 500,
  10508. // auto hide on initial open
  10509. autoHideUA: [] // e.g. ['Mobile']
  10510. },
  10511. navdock : {
  10512. // disabled navdock ui
  10513. disabled : false,
  10514. // percentage of initial maximum height to work zone
  10515. initMaxHeight : '50%',
  10516. // percentage of maximum height to work zone by user resize action
  10517. maxHeight : '90%'
  10518. },
  10519. cwd : {
  10520. // display parent folder with ".." name :)
  10521. oldSchool : false,
  10522. // fm.UA types array to show item select checkboxes e.g. ['All'] or ['Mobile'] etc. default: ['Touch']
  10523. showSelectCheckboxUA : ['Touch'],
  10524. // Enable dragout by dragstart with Alt key or Shift key
  10525. metakeyDragout : true,
  10526. // file info columns displayed
  10527. listView : {
  10528. // name is always displayed, cols are ordered
  10529. // e.g. ['perm', 'date', 'size', 'kind', 'owner', 'group', 'mode']
  10530. // mode: 'mode'(by `fileModeStyle` setting), 'modestr'(rwxr-xr-x) , 'modeoct'(755), 'modeboth'(rwxr-xr-x (755))
  10531. // 'owner', 'group' and 'mode', It's necessary set volume driver option "statOwner" to `true`
  10532. // for custom, characters that can be used in the name is `a-z0-9_`
  10533. columns : ['perm', 'date', 'size', 'kind'],
  10534. // override this if you want custom columns name
  10535. // example
  10536. // columnsCustomName : {
  10537. // date : 'Last modification',
  10538. // kind : 'Mime type'
  10539. // }
  10540. columnsCustomName : {},
  10541. // fixed list header colmun
  10542. fixedHeader : true
  10543. },
  10544. // icons view setting
  10545. iconsView : {
  10546. // default icon size (0-3 in default CSS (cwd.css - elfinder-cwd-size[number]))
  10547. size: 0,
  10548. // number of maximum size (3 in default CSS (cwd.css - elfinder-cwd-size[number]))
  10549. // uses in preference.js
  10550. sizeMax: 3,
  10551. // Name of each size
  10552. sizeNames: {
  10553. 0: 'viewSmall',
  10554. 1: 'viewMedium',
  10555. 2: 'viewLarge',
  10556. 3: 'viewExtraLarge'
  10557. }
  10558. },
  10559. // /**
  10560. // * Add CSS class name to cwd directories (optional)
  10561. // * see: https://github.com/Studio-42/elFinder/pull/1061,
  10562. // * https://github.com/Studio-42/elFinder/issues/1231
  10563. // *
  10564. // * @type Function
  10565. // */
  10566. // ,
  10567. // getClass: function(file) {
  10568. // // e.g. This adds the directory's name (lowercase) with prefix as a CSS class
  10569. // return 'elfinder-cwd-' + file.name.replace(/[ "]/g, '').toLowerCase();
  10570. //}
  10571. //,
  10572. //// Template placeholders replacement rules for overwrite. see ui/cwd.js replacement
  10573. //replacement : {
  10574. // tooltip : function(f, fm) {
  10575. // var list = fm.viewType == 'list', // current view type
  10576. // query = fm.searchStatus.state == 2, // is in search results
  10577. // title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''),
  10578. // info = '';
  10579. // if (query && f.path) {
  10580. // info = fm.escape(f.path.replace(/\/[^\/]*$/, ''));
  10581. // } else {
  10582. // info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, '&#13;') : '';
  10583. // }
  10584. // if (list) {
  10585. // info += (info? '&#13;' : '') + fm.escape(f.name);
  10586. // }
  10587. // return info? info + '&#13;' + title : title;
  10588. // }
  10589. //}
  10590. },
  10591. path : {
  10592. // Move to head of work zone without UI navbar
  10593. toWorkzoneWithoutNavbar : true
  10594. },
  10595. dialog : {
  10596. // Enable to auto focusing on mouse over in the target form element
  10597. focusOnMouseOver : true
  10598. },
  10599. toast : {
  10600. animate : {
  10601. // to show
  10602. showMethod: 'fadeIn', // fadeIn, slideDown, and show are built into jQuery
  10603. showDuration: 300, // milliseconds
  10604. showEasing: 'swing', // swing and linear are built into jQuery
  10605. // timeout to hide
  10606. timeOut: 3000,
  10607. // to hide
  10608. hideMethod: 'fadeOut',
  10609. hideDuration: 1500,
  10610. hideEasing: 'swing'
  10611. }
  10612. }
  10613. },
  10614. /**
  10615. * MIME regex of send HTTP header "Content-Disposition: inline" or allow preview in quicklook
  10616. * This option will overwrite by connector configuration
  10617. *
  10618. * @type String
  10619. * @default '^(?:(?:image|video|audio)|text/plain|application/pdf$)'
  10620. * @example
  10621. * dispInlineRegex : '.', // is allow inline of all of MIME types
  10622. * dispInlineRegex : '$^', // is not allow inline of all of MIME types
  10623. */
  10624. dispInlineRegex : '^(?:(?:image|video|audio)|application/(?:x-mpegURL|dash\+xml)|(?:text/plain|application/pdf)$)',
  10625. /**
  10626. * Display only required files by types
  10627. *
  10628. * @type Array
  10629. * @default []
  10630. * @example
  10631. * onlyMimes : ["image"] - display all images
  10632. * onlyMimes : ["image/png", "application/x-shockwave-flash"] - display png and flash
  10633. */
  10634. onlyMimes : [],
  10635. /**
  10636. * Custom files sort rules.
  10637. * All default rules (name/size/kind/date/perm/mode/owner/group) set in elFinder._sortRules
  10638. *
  10639. * @type {Object}
  10640. * @example
  10641. * sortRules : {
  10642. * name : function(file1, file2) { return file1.name.toLowerCase().localeCompare(file2.name.toLowerCase()); }
  10643. * }
  10644. */
  10645. sortRules : {},
  10646. /**
  10647. * Default sort type.
  10648. *
  10649. * @type {String}
  10650. */
  10651. sortType : 'name',
  10652. /**
  10653. * Default sort order.
  10654. *
  10655. * @type {String}
  10656. * @default "asc"
  10657. */
  10658. sortOrder : 'asc',
  10659. /**
  10660. * Display folders first?
  10661. *
  10662. * @type {Boolean}
  10663. * @default true
  10664. */
  10665. sortStickFolders : true,
  10666. /**
  10667. * Sort also applies to the treeview (null: disable this feature)
  10668. *
  10669. * @type Boolean|null
  10670. * @default false
  10671. */
  10672. sortAlsoTreeview : false,
  10673. /**
  10674. * If true - elFinder will formating dates itself,
  10675. * otherwise - backend date will be used.
  10676. *
  10677. * @type Boolean
  10678. */
  10679. clientFormatDate : true,
  10680. /**
  10681. * Show UTC dates.
  10682. * Required set clientFormatDate to true
  10683. *
  10684. * @type Boolean
  10685. */
  10686. UTCDate : false,
  10687. /**
  10688. * File modification datetime format.
  10689. * Value from selected language data is used by default.
  10690. * Set format here to overwrite it.
  10691. *
  10692. * @type String
  10693. * @default ""
  10694. */
  10695. dateFormat : '',
  10696. /**
  10697. * File modification datetime format in form "Yesterday 12:23:01".
  10698. * Value from selected language data is used by default.
  10699. * Set format here to overwrite it.
  10700. * Use $1 for "Today"/"Yesterday" placeholder
  10701. *
  10702. * @type String
  10703. * @default ""
  10704. * @example "$1 H:m:i"
  10705. */
  10706. fancyDateFormat : '',
  10707. /**
  10708. * Style of file mode at cwd-list, info dialog
  10709. * 'string' (ex. rwxr-xr-x) or 'octal' (ex. 755) or 'both' (ex. rwxr-xr-x (755))
  10710. *
  10711. * @type {String}
  10712. * @default 'both'
  10713. */
  10714. fileModeStyle : 'both',
  10715. /**
  10716. * elFinder width
  10717. *
  10718. * @type String|Number
  10719. * @default "auto"
  10720. */
  10721. width : 'auto',
  10722. /**
  10723. * elFinder node height
  10724. * Number: pixcel or String: Number + "%"
  10725. *
  10726. * @type Number | String
  10727. * @default 400
  10728. */
  10729. height : 400,
  10730. /**
  10731. * Base node object or selector
  10732. * Element which is the reference of the height percentage
  10733. *
  10734. * @type Object|String
  10735. * @default null | $(window) (if height is percentage)
  10736. **/
  10737. heightBase : null,
  10738. /**
  10739. * Make elFinder resizable if jquery ui resizable available
  10740. *
  10741. * @type Boolean
  10742. * @default true
  10743. */
  10744. resizable : true,
  10745. /**
  10746. * Timeout before open notifications dialogs
  10747. *
  10748. * @type Number
  10749. * @default 500 (.5 sec)
  10750. */
  10751. notifyDelay : 500,
  10752. /**
  10753. * Position CSS, Width of notifications dialogs
  10754. *
  10755. * @type Object
  10756. * @default {position: {}, width : null} - Apply CSS definition
  10757. * position: CSS object | null (null: position center & middle)
  10758. */
  10759. notifyDialog : {position: {}, width : null},
  10760. /**
  10761. * Dialog contained in the elFinder node
  10762. *
  10763. * @type Boolean
  10764. * @default false
  10765. */
  10766. dialogContained : false,
  10767. /**
  10768. * Allow shortcuts
  10769. *
  10770. * @type Boolean
  10771. * @default true
  10772. */
  10773. allowShortcuts : true,
  10774. /**
  10775. * Remeber last opened dir to open it after reload or in next session
  10776. *
  10777. * @type Boolean
  10778. * @default true
  10779. */
  10780. rememberLastDir : true,
  10781. /**
  10782. * Clear historys(elFinder) on reload(not browser) function
  10783. * Historys was cleared on Reload function on elFinder 2.0 (value is true)
  10784. *
  10785. * @type Boolean
  10786. * @default false
  10787. */
  10788. reloadClearHistory : false,
  10789. /**
  10790. * Use browser native history with supported browsers
  10791. *
  10792. * @type Boolean
  10793. * @default true
  10794. */
  10795. useBrowserHistory : true,
  10796. /**
  10797. * Lazy load config.
  10798. * How many files display at once?
  10799. *
  10800. * @type Number
  10801. * @default 50
  10802. */
  10803. showFiles : 50,
  10804. /**
  10805. * Lazy load config.
  10806. * Distance in px to cwd bottom edge to start display files
  10807. *
  10808. * @type Number
  10809. * @default 50
  10810. */
  10811. showThreshold : 50,
  10812. /**
  10813. * Additional rule to valid new file name.
  10814. * By default not allowed empty names or '..'
  10815. * This setting does not have a sense of security.
  10816. *
  10817. * @type false|RegExp|function
  10818. * @default false
  10819. * @example
  10820. * disable names with spaces:
  10821. * validName : /^[^\s]+$/,
  10822. */
  10823. validName : false,
  10824. /**
  10825. * Additional rule to filtering for browsing.
  10826. * This setting does not have a sense of security.
  10827. *
  10828. * The object `this` is elFinder instance object in this function
  10829. *
  10830. * @type false|RegExp|function
  10831. * @default false
  10832. * @example
  10833. * show only png and jpg files:
  10834. * fileFilter : /.*\.(png|jpg)$/i,
  10835. *
  10836. * show only image type files:
  10837. * fileFilter : function(file) { return file.mime && file.mime.match(/^image\//i); },
  10838. */
  10839. fileFilter : false,
  10840. /**
  10841. * Backup name suffix.
  10842. *
  10843. * @type String
  10844. * @default "~"
  10845. */
  10846. backupSuffix : '~',
  10847. /**
  10848. * Sync content interval
  10849. *
  10850. * @type Number
  10851. * @default 0 (do not sync)
  10852. */
  10853. sync : 0,
  10854. /**
  10855. * Sync start on load if sync value >= 1000
  10856. *
  10857. * @type Bool
  10858. * @default true
  10859. */
  10860. syncStart : true,
  10861. /**
  10862. * How many thumbnails create in one request
  10863. *
  10864. * @type Number
  10865. * @default 5
  10866. */
  10867. loadTmbs : 5,
  10868. /**
  10869. * Cookie option for browsersdoes not suppot localStorage
  10870. *
  10871. * @type Object
  10872. */
  10873. cookie : {
  10874. expires : 30,
  10875. domain : '',
  10876. path : '/',
  10877. secure : false
  10878. },
  10879. /**
  10880. * Contextmenu config
  10881. *
  10882. * @type Object
  10883. */
  10884. contextmenu : {
  10885. // navbarfolder menu
  10886. navbar : ['open', 'opennew', 'download', '|', 'upload', 'mkdir', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', 'empty', 'hide', '|', 'rename', '|', 'archive', '|', 'places', 'info', 'chmod', 'netunmount'],
  10887. // current directory menu
  10888. cwd : ['undo', 'redo', '|', 'back', 'up', 'reload', '|', 'upload', 'mkdir', 'mkfile', 'paste', '|', 'empty', 'hide', '|', 'view', 'sort', 'selectall', 'colwidth', '|', 'places', 'info', 'chmod', 'netunmount', '|', 'fullscreen', '|', 'preference'],
  10889. // current directory file menu
  10890. 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']
  10891. },
  10892. /**
  10893. * elFinder node enable always
  10894. * This value will set to `true` if <body> has elFinder node only
  10895. *
  10896. * @type Bool
  10897. * @default false
  10898. */
  10899. enableAlways : false,
  10900. /**
  10901. * elFinder node enable by mouse over
  10902. *
  10903. * @type Bool
  10904. * @default true
  10905. */
  10906. enableByMouseOver : true,
  10907. /**
  10908. * Show window close confirm dialog
  10909. * Value is which state to show
  10910. * 'hasNotifyDialog', 'editingFile', 'hasSelectedItem' and 'hasClipboardData'
  10911. *
  10912. * @type Array
  10913. * @default ['hasNotifyDialog', 'editingFile']
  10914. */
  10915. windowCloseConfirm : ['hasNotifyDialog', 'editingFile'],
  10916. /**
  10917. * Function decoding 'raw' string converted to unicode
  10918. * It is used instead of fm.decodeRawString(str)
  10919. *
  10920. * @type Null|Function
  10921. */
  10922. rawStringDecoder : typeof Encoding === 'object' && $.isFunction(Encoding.convert)? function(str) {
  10923. return Encoding.convert(str, {
  10924. to: 'UNICODE',
  10925. type: 'string'
  10926. });
  10927. } : null,
  10928. /**
  10929. * Debug config
  10930. *
  10931. * @type Array|String('auto')|Boolean(true|false)
  10932. */
  10933. // debug : true
  10934. debug : ['error', 'warning', 'event-destroy']
  10935. };
  10936. /*
  10937. * File: /js/elFinder.options.netmount.js
  10938. */
  10939. /**
  10940. * Default elFinder config of commandsOptions.netmount
  10941. *
  10942. * @type Object
  10943. */
  10944. elFinder.prototype._options.commandsOptions.netmount = {
  10945. ftp: {
  10946. name : 'FTP',
  10947. inputs: {
  10948. host : $('<input type="text"/>'),
  10949. port : $('<input type="number" placeholder="21" class="elfinder-input-optional"/>'),
  10950. path : $('<input type="text" value="/"/>'),
  10951. user : $('<input type="text"/>'),
  10952. pass : $('<input type="password" autocomplete="new-password"/>'),
  10953. FTPS : $('<input type="checkbox" value="1" title="File Transfer Protocol over SSL/TLS"/>'),
  10954. encoding : $('<input type="text" placeholder="Optional" class="elfinder-input-optional"/>'),
  10955. locale : $('<input type="text" placeholder="Optional" class="elfinder-input-optional"/>')
  10956. }
  10957. },
  10958. dropbox2: elFinder.prototype.makeNetmountOptionOauth('dropbox2', 'Dropbox', 'Dropbox', {noOffline : true,
  10959. root : '/',
  10960. pathI18n : 'path',
  10961. integrate : {
  10962. title: 'Dropbox.com',
  10963. link: 'https://www.dropbox.com'
  10964. }
  10965. }),
  10966. googledrive: elFinder.prototype.makeNetmountOptionOauth('googledrive', 'Google Drive', 'Google', {
  10967. integrate : {
  10968. title: 'Google Drive',
  10969. link: 'https://www.google.com/drive/'
  10970. }
  10971. }),
  10972. onedrive: elFinder.prototype.makeNetmountOptionOauth('onedrive', 'One Drive', 'OneDrive', {
  10973. integrate : {
  10974. title: 'Microsoft OneDrive',
  10975. link: 'https://onedrive.live.com'
  10976. }
  10977. }),
  10978. box: elFinder.prototype.makeNetmountOptionOauth('box', 'Box', 'Box', {
  10979. noOffline : true,
  10980. integrate : {
  10981. title: 'Box.com',
  10982. link: 'https://www.box.com'
  10983. }
  10984. })
  10985. };
  10986. /*
  10987. * File: /js/elFinder.history.js
  10988. */
  10989. /**
  10990. * @class elFinder.history
  10991. * Store visited folders
  10992. * and provide "back" and "forward" methods
  10993. *
  10994. * @author Dmitry (dio) Levashov
  10995. */
  10996. elFinder.prototype.history = function(fm) {
  10997. var self = this,
  10998. /**
  10999. * Update history on "open" event?
  11000. *
  11001. * @type Boolean
  11002. */
  11003. update = true,
  11004. /**
  11005. * Directories hashes storage
  11006. *
  11007. * @type Array
  11008. */
  11009. history = [],
  11010. /**
  11011. * Current directory index in history
  11012. *
  11013. * @type Number
  11014. */
  11015. current,
  11016. /**
  11017. * Clear history
  11018. *
  11019. * @return void
  11020. */
  11021. reset = function() {
  11022. history = [fm.cwd().hash];
  11023. current = 0;
  11024. update = true;
  11025. },
  11026. /**
  11027. * Browser native history object
  11028. */
  11029. nativeHistory = (fm.options.useBrowserHistory && window.history && window.history.pushState)? window.history : null,
  11030. /**
  11031. * Open prev/next folder
  11032. *
  11033. * @Boolen open next folder?
  11034. * @return jQuery.Deferred
  11035. */
  11036. go = function(fwd) {
  11037. if ((fwd && self.canForward()) || (!fwd && self.canBack())) {
  11038. update = false;
  11039. return fm.exec('open', history[fwd ? ++current : --current]).fail(reset);
  11040. }
  11041. return $.Deferred().reject();
  11042. },
  11043. /**
  11044. * Sets the native history.
  11045. *
  11046. * @param String thash target hash
  11047. */
  11048. setNativeHistory = function(thash) {
  11049. if (nativeHistory && (! nativeHistory.state || nativeHistory.state.thash !== thash)) {
  11050. nativeHistory.pushState({thash: thash}, null, location.pathname + location.search + (thash? '#elf_' + thash : ''));
  11051. }
  11052. };
  11053. /**
  11054. * Return true if there is previous visited directories
  11055. *
  11056. * @return Boolen
  11057. */
  11058. this.canBack = function() {
  11059. return current > 0;
  11060. };
  11061. /**
  11062. * Return true if can go forward
  11063. *
  11064. * @return Boolen
  11065. */
  11066. this.canForward = function() {
  11067. return current < history.length - 1;
  11068. };
  11069. /**
  11070. * Go back
  11071. *
  11072. * @return void
  11073. */
  11074. this.back = go;
  11075. /**
  11076. * Go forward
  11077. *
  11078. * @return void
  11079. */
  11080. this.forward = function() {
  11081. return go(true);
  11082. };
  11083. // bind to elfinder events
  11084. fm.bind('init', function() {
  11085. if (nativeHistory && !nativeHistory.state) {
  11086. setNativeHistory(fm.startDir());
  11087. }
  11088. })
  11089. .open(function() {
  11090. var l = history.length,
  11091. cwd = fm.cwd().hash;
  11092. if (update) {
  11093. current >= 0 && l > current + 1 && history.splice(current+1);
  11094. history[history.length-1] != cwd && history.push(cwd);
  11095. current = history.length - 1;
  11096. }
  11097. update = true;
  11098. setNativeHistory(cwd);
  11099. })
  11100. .reload(fm.options.reloadClearHistory && reset);
  11101. };
  11102. /*
  11103. * File: /js/elFinder.command.js
  11104. */
  11105. /**
  11106. * elFinder command prototype
  11107. *
  11108. * @type elFinder.command
  11109. * @author Dmitry (dio) Levashov
  11110. */
  11111. elFinder.prototype.command = function(fm) {
  11112. /**
  11113. * elFinder instance
  11114. *
  11115. * @type elFinder
  11116. */
  11117. this.fm = fm;
  11118. /**
  11119. * Command name, same as class name
  11120. *
  11121. * @type String
  11122. */
  11123. this.name = '';
  11124. /**
  11125. * Dialog class name
  11126. *
  11127. * @type String
  11128. */
  11129. this.dialogClass = '';
  11130. /**
  11131. * Command icon class name with out 'elfinder-button-icon-'
  11132. * Use this.name if it is empty
  11133. *
  11134. * @type String
  11135. */
  11136. this.className = '';
  11137. /**
  11138. * Short command description
  11139. *
  11140. * @type String
  11141. */
  11142. this.title = '';
  11143. /**
  11144. * Linked(Child) commands name
  11145. * They are loaded together when tthis command is loaded.
  11146. *
  11147. * @type Array
  11148. */
  11149. this.linkedCmds = [];
  11150. /**
  11151. * Current command state
  11152. *
  11153. * @example
  11154. * this.state = -1; // command disabled
  11155. * this.state = 0; // command enabled
  11156. * this.state = 1; // command active (for example "fullscreen" command while elfinder in fullscreen mode)
  11157. * @default -1
  11158. * @type Number
  11159. */
  11160. this.state = -1;
  11161. /**
  11162. * If true, command can not be disabled by connector.
  11163. * @see this.update()
  11164. *
  11165. * @type Boolen
  11166. */
  11167. this.alwaysEnabled = false;
  11168. /**
  11169. * Do not change dirctory on removed current work directory
  11170. *
  11171. * @type Boolen
  11172. */
  11173. this.noChangeDirOnRemovedCwd = false;
  11174. /**
  11175. * If true, this means command was disabled by connector.
  11176. * @see this.update()
  11177. *
  11178. * @type Boolen
  11179. */
  11180. this._disabled = false;
  11181. /**
  11182. * If true, this command is disabled on serach results
  11183. *
  11184. * @type Boolean
  11185. */
  11186. this.disableOnSearch = false;
  11187. /**
  11188. * Call update() when event select fired
  11189. *
  11190. * @type Boolean
  11191. */
  11192. this.updateOnSelect = true;
  11193. /**
  11194. * Sync toolbar button title on change
  11195. *
  11196. * @type Boolean
  11197. */
  11198. this.syncTitleOnChange = false;
  11199. /**
  11200. * Keep display of the context menu when command execution
  11201. *
  11202. * @type Boolean
  11203. */
  11204. this.keepContextmenu = false;
  11205. /**
  11206. * elFinder events defaults handlers.
  11207. * Inside handlers "this" is current command object
  11208. *
  11209. * @type Object
  11210. */
  11211. this._handlers = {
  11212. enable : function() { this.update(void(0), this.value); },
  11213. disable : function() { this.update(-1, this.value); },
  11214. 'open reload load sync' : function() {
  11215. this._disabled = !(this.alwaysEnabled || this.fm.isCommandEnabled(this.name));
  11216. this.update(void(0), this.value);
  11217. this.change();
  11218. }
  11219. };
  11220. /**
  11221. * elFinder events handlers.
  11222. * Inside handlers "this" is current command object
  11223. *
  11224. * @type Object
  11225. */
  11226. this.handlers = {};
  11227. /**
  11228. * Shortcuts
  11229. *
  11230. * @type Array
  11231. */
  11232. this.shortcuts = [];
  11233. /**
  11234. * Command options
  11235. *
  11236. * @type Object
  11237. */
  11238. this.options = {ui : 'button'};
  11239. /**
  11240. * Callback functions on `change` event
  11241. *
  11242. * @type Array
  11243. */
  11244. this.listeners = [];
  11245. /**
  11246. * Prepare object -
  11247. * bind events and shortcuts
  11248. *
  11249. * @return void
  11250. */
  11251. this.setup = function(name, opts) {
  11252. var self = this,
  11253. fm = this.fm,
  11254. setCallback = function(s) {
  11255. var cb = s.callback || function(e) {
  11256. fm.exec(self.name, void(0), {
  11257. _userAction: true,
  11258. _currentType: 'shortcut'
  11259. });
  11260. };
  11261. s.callback = function(e) {
  11262. var enabled, checks = {};
  11263. if (self.enabled()) {
  11264. if (fm.searchStatus.state < 2) {
  11265. enabled = fm.isCommandEnabled(self.name);
  11266. } else {
  11267. $.each(fm.selected(), function(i, h) {
  11268. if (fm.optionsByHashes[h]) {
  11269. checks[h] = true;
  11270. } else {
  11271. $.each(fm.volOptions, function(id) {
  11272. if (!checks[id] && h.indexOf(id) === 0) {
  11273. checks[id] = true;
  11274. return false;
  11275. }
  11276. });
  11277. }
  11278. });
  11279. $.each(checks, function(h) {
  11280. enabled = fm.isCommandEnabled(self.name, h);
  11281. if (! enabled) {
  11282. return false;
  11283. }
  11284. });
  11285. }
  11286. if (enabled) {
  11287. self.event = e;
  11288. cb.call(self);
  11289. delete self.event;
  11290. }
  11291. }
  11292. };
  11293. },
  11294. i, s, sc;
  11295. this.name = name;
  11296. this.title = fm.messages['cmd'+name] ? fm.i18n('cmd'+name)
  11297. : ((this.extendsCmd && fm.messages['cmd'+this.extendsCmd]) ? fm.i18n('cmd'+this.extendsCmd) : name);
  11298. this.options = Object.assign({}, this.options, opts);
  11299. this.listeners = [];
  11300. this.dialogClass = 'elfinder-dialog-' + name;
  11301. if (opts.shortcuts) {
  11302. if (typeof opts.shortcuts === 'function') {
  11303. sc = opts.shortcuts(this.fm, this.shortcuts);
  11304. } else if (Array.isArray(opts.shortcuts)) {
  11305. sc = opts.shortcuts;
  11306. }
  11307. this.shortcuts = sc || [];
  11308. }
  11309. if (this.updateOnSelect) {
  11310. this._handlers.select = function() { this.update(void(0), this.value); };
  11311. }
  11312. $.each(Object.assign({}, self._handlers, self.handlers), function(cmd, handler) {
  11313. fm.bind(cmd, $.proxy(handler, self));
  11314. });
  11315. for (i = 0; i < this.shortcuts.length; i++) {
  11316. s = this.shortcuts[i];
  11317. setCallback(s);
  11318. !s.description && (s.description = this.title);
  11319. fm.shortcut(s);
  11320. }
  11321. if (this.disableOnSearch) {
  11322. fm.bind('search searchend', function() {
  11323. self._disabled = this.type === 'search'? true : ! (this.alwaysEnabled || fm.isCommandEnabled(name));
  11324. self.update(void(0), self.value);
  11325. });
  11326. }
  11327. this.init();
  11328. };
  11329. /**
  11330. * Command specific init stuffs
  11331. *
  11332. * @return void
  11333. */
  11334. this.init = function() {};
  11335. /**
  11336. * Exec command
  11337. *
  11338. * @param Array target files hashes
  11339. * @param Array|Object command value
  11340. * @return $.Deferred
  11341. */
  11342. this.exec = function(files, opts) {
  11343. return $.Deferred().reject();
  11344. };
  11345. this.getUndo = function(opts, resData) {
  11346. return false;
  11347. };
  11348. /**
  11349. * Return true if command disabled.
  11350. *
  11351. * @return Boolen
  11352. */
  11353. this.disabled = function() {
  11354. return this.state < 0;
  11355. };
  11356. /**
  11357. * Return true if command enabled.
  11358. *
  11359. * @return Boolen
  11360. */
  11361. this.enabled = function() {
  11362. return this.state > -1;
  11363. };
  11364. /**
  11365. * Return true if command active.
  11366. *
  11367. * @return Boolen
  11368. */
  11369. this.active = function() {
  11370. return this.state > 0;
  11371. };
  11372. /**
  11373. * Return current command state.
  11374. * Must be overloaded in most commands
  11375. *
  11376. * @return Number
  11377. */
  11378. this.getstate = function() {
  11379. return -1;
  11380. };
  11381. /**
  11382. * Update command state/value
  11383. * and rize 'change' event if smth changed
  11384. *
  11385. * @param Number new state or undefined to auto update state
  11386. * @param mixed new value
  11387. * @return void
  11388. */
  11389. this.update = function(s, v) {
  11390. var state = this.state,
  11391. value = this.value;
  11392. if (this._disabled && this.fm.searchStatus === 0) {
  11393. this.state = -1;
  11394. } else {
  11395. this.state = s !== void(0) ? s : this.getstate();
  11396. }
  11397. this.value = v;
  11398. if (state != this.state || value != this.value) {
  11399. this.change();
  11400. }
  11401. };
  11402. /**
  11403. * Bind handler / fire 'change' event.
  11404. *
  11405. * @param Function|undefined event callback
  11406. * @return void
  11407. */
  11408. this.change = function(c) {
  11409. var cmd, i;
  11410. if (typeof(c) === 'function') {
  11411. this.listeners.push(c);
  11412. } else {
  11413. for (i = 0; i < this.listeners.length; i++) {
  11414. cmd = this.listeners[i];
  11415. try {
  11416. cmd(this.state, this.value);
  11417. } catch (e) {
  11418. this.fm.debug('error', e);
  11419. }
  11420. }
  11421. }
  11422. return this;
  11423. };
  11424. /**
  11425. * With argument check given files hashes and return list of existed files hashes.
  11426. * Without argument return selected files hashes.
  11427. *
  11428. * @param Array|String|void hashes
  11429. * @return Array
  11430. */
  11431. this.hashes = function(hashes) {
  11432. return hashes
  11433. ? $.grep(Array.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) ? true : false; })
  11434. : fm.selected();
  11435. };
  11436. /**
  11437. * Return only existed files from given fils hashes | selected files
  11438. *
  11439. * @param Array|String|void hashes
  11440. * @return Array
  11441. */
  11442. this.files = function(hashes) {
  11443. var fm = this.fm;
  11444. return hashes
  11445. ? $.map(Array.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) || null; })
  11446. : fm.selectedFiles();
  11447. };
  11448. /**
  11449. * Wrapper to fm.dialog()
  11450. *
  11451. * @param String|DOMElement content
  11452. * @param Object options
  11453. * @return Object jQuery element object
  11454. */
  11455. this.fmDialog = function(content, options) {
  11456. if (options.cssClass) {
  11457. options.cssClass += ' ' + this.dialogClass;
  11458. } else {
  11459. options.cssClass = this.dialogClass;
  11460. }
  11461. return this.fm.dialog(content, options);
  11462. };
  11463. };
  11464. /*
  11465. * File: /js/elFinder.resources.js
  11466. */
  11467. /**
  11468. * elFinder resources registry.
  11469. * Store shared data
  11470. *
  11471. * @type Object
  11472. * @author Dmitry (dio) Levashov
  11473. **/
  11474. elFinder.prototype.resources = {
  11475. 'class' : {
  11476. hover : 'ui-state-hover',
  11477. active : 'ui-state-active',
  11478. disabled : 'ui-state-disabled',
  11479. draggable : 'ui-draggable',
  11480. droppable : 'ui-droppable',
  11481. adroppable : 'elfinder-droppable-active',
  11482. cwdfile : 'elfinder-cwd-file',
  11483. cwd : 'elfinder-cwd',
  11484. tree : 'elfinder-tree',
  11485. treeroot : 'elfinder-navbar-root',
  11486. navdir : 'elfinder-navbar-dir',
  11487. navdirwrap : 'elfinder-navbar-dir-wrapper',
  11488. navarrow : 'elfinder-navbar-arrow',
  11489. navsubtree : 'elfinder-navbar-subtree',
  11490. navcollapse : 'elfinder-navbar-collapsed',
  11491. navexpand : 'elfinder-navbar-expanded',
  11492. treedir : 'elfinder-tree-dir',
  11493. placedir : 'elfinder-place-dir',
  11494. searchbtn : 'elfinder-button-search',
  11495. editing : 'elfinder-to-editing',
  11496. preventback : 'elfinder-prevent-back',
  11497. tabstab : 'ui-state-default ui-tabs-tab ui-corner-top ui-tab',
  11498. tabsactive : 'ui-tabs-active ui-state-active'
  11499. },
  11500. tpl : {
  11501. perms : '<span class="elfinder-perms"/>',
  11502. lock : '<span class="elfinder-lock"/>',
  11503. symlink : '<span class="elfinder-symlink"/>',
  11504. navicon : '<span class="elfinder-nav-icon"/>',
  11505. navspinner : '<span class="elfinder-spinner elfinder-navbar-spinner"/>',
  11506. 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 class="elfinder-navbar-icon" {style}/>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree" style="display:none"/></div>',
  11507. placedir : '<div class="elfinder-navbar-wrapper"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}"{title}><span class="elfinder-navbar-arrow"/><span class="elfinder-navbar-icon" {style}/>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree" style="display:none"/></div>'
  11508. },
  11509. // mimes.text will be overwritten with connector config if `textMimes` is included in initial response
  11510. // @see php/elFInder.class.php `public static $textMimes`
  11511. mimes : {
  11512. text : [
  11513. 'application/dash+xml',
  11514. 'application/docbook+xml',
  11515. 'application/javascript',
  11516. 'application/json',
  11517. 'application/plt',
  11518. 'application/sat',
  11519. 'application/sql',
  11520. 'application/step',
  11521. 'application/vnd.hp-hpgl',
  11522. 'application/x-awk',
  11523. 'application/x-config',
  11524. 'application/x-csh',
  11525. 'application/x-empty',
  11526. 'application/x-mpegurl',
  11527. 'application/x-perl',
  11528. 'application/x-php',
  11529. 'application/x-web-config',
  11530. 'application/xhtml+xml',
  11531. 'application/xml',
  11532. 'audio/x-mp3-playlist',
  11533. 'image/cgm',
  11534. 'image/svg+xml',
  11535. 'image/vnd.dxf',
  11536. 'model/iges'
  11537. ]
  11538. },
  11539. mixin : {
  11540. make : function() {
  11541. var self = this,
  11542. fm = this.fm,
  11543. cmd = this.name,
  11544. req = this.requestCmd || cmd,
  11545. wz = fm.getUI('workzone'),
  11546. org = (this.origin && this.origin === 'navbar')? 'tree' : 'cwd',
  11547. tree = (org === 'tree'),
  11548. find = tree? 'navHash2Elm' : 'cwdHash2Elm',
  11549. tarea= (! tree && fm.storage('view') != 'list'),
  11550. sel = fm.selected(),
  11551. move = this.move || false,
  11552. empty= wz.hasClass('elfinder-cwd-wrapper-empty'),
  11553. unselect = function() {
  11554. requestAnimationFrame(function() {
  11555. input && input.trigger('blur');
  11556. });
  11557. },
  11558. rest = function(){
  11559. if (!overlay.is(':hidden')) {
  11560. overlay.elfinderoverlay('hide').off('click close', cancel);
  11561. }
  11562. if (nnode) {
  11563. pnode.removeClass('ui-front')
  11564. .css('position', '')
  11565. .off('unselect.'+fm.namespace, unselect);
  11566. if (tarea) {
  11567. nnode && nnode.css('max-height', '');
  11568. } else if (!tree) {
  11569. pnode.css('width', '')
  11570. .parent('td').css('overflow', '');
  11571. }
  11572. }
  11573. }, colwidth,
  11574. dfrd = $.Deferred()
  11575. .fail(function(error) {
  11576. dstCls && dst.attr('class', dstCls);
  11577. empty && wz.addClass('elfinder-cwd-wrapper-empty');
  11578. if (sel) {
  11579. move && fm.trigger('unlockfiles', {files: sel});
  11580. fm.clipboard([]);
  11581. fm.trigger('selectfiles', { files: sel });
  11582. }
  11583. error && fm.error(error);
  11584. })
  11585. .always(function() {
  11586. rest();
  11587. cleanup();
  11588. fm.enable().unbind('open', openCallback).trigger('resMixinMake');
  11589. }),
  11590. id = 'tmp_'+parseInt(Math.random()*100000),
  11591. phash = this.data && this.data.target? this.data.target : (tree? fm.file(sel[0]).hash : fm.cwd().hash),
  11592. date = new Date(),
  11593. file = {
  11594. hash : id,
  11595. phash : phash,
  11596. name : fm.uniqueName(this.prefix, phash),
  11597. mime : this.mime,
  11598. read : true,
  11599. write : true,
  11600. date : 'Today '+date.getHours()+':'+date.getMinutes(),
  11601. move : move
  11602. },
  11603. dum = fm.getUI(org).trigger('create.'+fm.namespace, file),
  11604. data = this.data || {},
  11605. node = fm[find](id),
  11606. nnode, pnode,
  11607. overlay = fm.getUI('overlay'),
  11608. cleanup = function() {
  11609. if (node && node.length) {
  11610. input.off();
  11611. node.hide();
  11612. fm.unselectfiles({files : [id]}).unbind('resize', resize);
  11613. requestAnimationFrame(function() {
  11614. if (tree) {
  11615. node.closest('.elfinder-navbar-wrapper').remove();
  11616. } else {
  11617. node.remove();
  11618. }
  11619. });
  11620. }
  11621. },
  11622. cancel = function(e) {
  11623. if (!overlay.is(':hidden')) {
  11624. pnode.css('z-index', '');
  11625. }
  11626. if (! inError) {
  11627. cleanup();
  11628. dfrd.reject();
  11629. if (e) {
  11630. e.stopPropagation();
  11631. e.preventDefault();
  11632. }
  11633. }
  11634. },
  11635. input = $(tarea? '<textarea/>' : '<input type="text"/>')
  11636. .on('keyup text', function(){
  11637. if (tarea) {
  11638. this.style.height = '1px';
  11639. this.style.height = this.scrollHeight + 'px';
  11640. } else if (colwidth) {
  11641. this.style.width = colwidth + 'px';
  11642. if (this.scrollWidth > colwidth) {
  11643. this.style.width = this.scrollWidth + 10 + 'px';
  11644. }
  11645. }
  11646. })
  11647. .on('keydown', function(e) {
  11648. e.stopImmediatePropagation();
  11649. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  11650. dfrd.reject();
  11651. } else if (e.keyCode == $.ui.keyCode.ENTER) {
  11652. e.preventDefault();
  11653. input.trigger('blur');
  11654. }
  11655. })
  11656. .on('mousedown click dblclick', function(e) {
  11657. e.stopPropagation();
  11658. if (e.type === 'dblclick') {
  11659. e.preventDefault();
  11660. }
  11661. })
  11662. .on('blur', function() {
  11663. var name = $.trim(input.val()),
  11664. parent = input.parent(),
  11665. valid = true,
  11666. cut;
  11667. if (!overlay.is(':hidden')) {
  11668. pnode.css('z-index', '');
  11669. }
  11670. if (name === '') {
  11671. return cancel();
  11672. }
  11673. if (!inError && parent.length) {
  11674. if (fm.options.validName && fm.options.validName.test) {
  11675. try {
  11676. valid = fm.options.validName.test(name);
  11677. } catch(e) {
  11678. valid = false;
  11679. }
  11680. }
  11681. if (!name || name === '.' || name === '..' || !valid) {
  11682. inError = true;
  11683. fm.error(file.mime === 'directory'? 'errInvDirname' : 'errInvName', {modal: true, close: function(){setTimeout(select, 120);}});
  11684. return false;
  11685. }
  11686. if (fm.fileByName(name, phash)) {
  11687. inError = true;
  11688. fm.error(['errExists', name], {modal: true, close: function(){setTimeout(select, 120);}});
  11689. return false;
  11690. }
  11691. cut = (sel && move)? fm.exec('cut', sel) : null;
  11692. $.when(cut)
  11693. .done(function() {
  11694. var toast = {},
  11695. nextAct = {};
  11696. rest();
  11697. input.hide().before($('<span>').text(name));
  11698. fm.lockfiles({files : [id]});
  11699. fm.request({
  11700. data : Object.assign({cmd : req, name : name, target : phash}, data || {}),
  11701. notify : {type : req, cnt : 1},
  11702. preventFail : true,
  11703. syncOnFail : true,
  11704. navigate : {toast : toast},
  11705. })
  11706. .fail(function(error) {
  11707. fm.unlockfiles({files : [id]});
  11708. inError = true;
  11709. input.show().prev().remove();
  11710. fm.error(error, {
  11711. modal: true,
  11712. close: function() {
  11713. if (Array.isArray(error) && $.inArray('errUploadMime', error) !== -1) {
  11714. dfrd.notify('errUploadMime').reject();
  11715. } else {
  11716. setTimeout(select, 120);
  11717. }
  11718. }
  11719. });
  11720. })
  11721. .done(function(data) {
  11722. if (data && data.added && data.added[0]) {
  11723. var item = data.added[0],
  11724. dirhash = item.hash,
  11725. newItem = fm[find](dirhash),
  11726. acts = {
  11727. 'directory' : { cmd: 'open', msg: 'cmdopendir' },
  11728. 'text' : { cmd: 'edit', msg: 'cmdedit' },
  11729. 'default' : { cmd: 'open', msg: 'cmdopen' }
  11730. },
  11731. tmpMimes;
  11732. if (sel && move) {
  11733. fm.one(req+'done', function() {
  11734. fm.exec('paste', dirhash);
  11735. });
  11736. }
  11737. if (!move) {
  11738. if (fm.mimeIsText(item.mime) && !fm.mimesCanMakeEmpty[item.mime] && fm.mimeTypes[item.mime]) {
  11739. fm.trigger('canMakeEmptyFile', {mimes: [item.mime], unshift: true});
  11740. tmpMimes = {};
  11741. tmpMimes[item.mime] = fm.mimeTypes[item.mime];
  11742. fm.storage('mkfileTextMimes', Object.assign(tmpMimes, fm.storage('mkfileTextMimes') || {}));
  11743. }
  11744. 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']);
  11745. Object.assign(toast, nextAct.cmd ? {
  11746. incwd : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)]), action: nextAct},
  11747. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)]), action: nextAct}
  11748. } : {
  11749. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)])}
  11750. });
  11751. }
  11752. }
  11753. dfrd.resolve(data);
  11754. });
  11755. })
  11756. .fail(function() {
  11757. dfrd.reject();
  11758. });
  11759. }
  11760. })
  11761. .on('dragenter dragleave dragover drop', function(e) {
  11762. // stop bubbling to prevent upload with native drop event
  11763. e.stopPropagation();
  11764. }),
  11765. select = function() {
  11766. var name = fm.splitFileExtention(input.val())[0];
  11767. if (!inError && fm.UA.Mobile && !fm.UA.iOS) { // since iOS has a bug? (z-index not effect) so disable it
  11768. overlay.on('click close', cancel).elfinderoverlay('show');
  11769. pnode.css('z-index', overlay.css('z-index') + 1);
  11770. }
  11771. inError = false;
  11772. ! fm.enabled() && fm.enable();
  11773. input.trigger('focus').trigger('select');
  11774. input[0].setSelectionRange && input[0].setSelectionRange(0, name.length);
  11775. },
  11776. resize = function() {
  11777. node.trigger('scrolltoview', {blink : false});
  11778. },
  11779. openCallback = function() {
  11780. dfrd && (dfrd.state() === 'pending') && dfrd.reject();
  11781. },
  11782. inError = false,
  11783. nextAction,
  11784. // for tree
  11785. dst, dstCls, collapsed, expanded, arrow, subtree;
  11786. if (!fm.isCommandEnabled(req, phash) || !node.length) {
  11787. return dfrd.reject();
  11788. }
  11789. if ($.isPlainObject(self.nextAction)){
  11790. nextAction = Object.assign({}, self.nextAction);
  11791. }
  11792. if (tree) {
  11793. dst = fm[find](phash);
  11794. collapsed = fm.res('class', 'navcollapse');
  11795. expanded = fm.res('class', 'navexpand');
  11796. arrow = fm.res('class', 'navarrow');
  11797. subtree = fm.res('class', 'navsubtree');
  11798. node.closest('.'+subtree).show();
  11799. if (! dst.hasClass(collapsed)) {
  11800. dstCls = dst.attr('class');
  11801. dst.addClass(collapsed+' '+expanded+' elfinder-subtree-loaded');
  11802. }
  11803. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  11804. dst.children('.'+arrow).trigger('click').data('dfrd').done(function() {
  11805. if (input.val() === file.name) {
  11806. input.val(fm.uniqueName(self.prefix, phash)).trigger('select').trigger('focus');
  11807. }
  11808. });
  11809. }
  11810. nnode = node.contents().filter(function(){ return this.nodeType==3 && $(this).parent().attr('id') === fm.navHash2Id(file.hash); });
  11811. pnode = nnode.parent();
  11812. nnode.replaceWith(input.val(file.name));
  11813. } else {
  11814. empty && wz.removeClass('elfinder-cwd-wrapper-empty');
  11815. nnode = node.find('.elfinder-cwd-filename');
  11816. pnode = nnode.parent();
  11817. if (tarea) {
  11818. nnode.css('max-height', 'none');
  11819. } else {
  11820. colwidth = pnode.width();
  11821. pnode.width(colwidth - 15)
  11822. .parent('td').css('overflow', 'visible');
  11823. }
  11824. nnode.empty().append(input.val(file.name));
  11825. }
  11826. pnode.addClass('ui-front')
  11827. .css('position', 'relative')
  11828. .on('unselect.'+fm.namespace, unselect);
  11829. fm.bind('resize', resize).one('open', openCallback);
  11830. input.trigger('keyup');
  11831. select();
  11832. return dfrd;
  11833. }
  11834. },
  11835. blink: function(elm, mode) {
  11836. var acts = {
  11837. slowonce : function(){elm.hide().delay(250).fadeIn(750).delay(500).fadeOut(3500);},
  11838. lookme : function(){elm.show().fadeOut(500).fadeIn(750);}
  11839. }, func;
  11840. mode = mode || 'slowonce';
  11841. func = acts[mode] || acts['lookme'];
  11842. elm.stop(true, true);
  11843. func();
  11844. }
  11845. };
  11846. /*
  11847. * File: /js/jquery.dialogelfinder.js
  11848. */
  11849. /**
  11850. * @class dialogelfinder - open elFinder in dialog window
  11851. *
  11852. * @param Object elFinder options with dialog options
  11853. * @example
  11854. * $(selector).dialogelfinder({
  11855. * // some elfinder options
  11856. * title : 'My files', // dialog title, default = "Files"
  11857. * width : 850, // dialog width, default 840
  11858. * autoOpen : false, // if false - dialog will not be opened after init, default = true
  11859. * destroyOnClose : true // destroy elFinder on close dialog, default = false
  11860. * })
  11861. * @author Dmitry (dio) Levashov
  11862. **/
  11863. $.fn.dialogelfinder = function(opts, opts2) {
  11864. var position = 'elfinderPosition',
  11865. destroy = 'elfinderDestroyOnClose',
  11866. node, pos;
  11867. if ($.isPlainObject(opts)) {
  11868. this.not('.elfinder').each(function() {
  11869. opts.handlers = opts.handlers || {};
  11870. var node = $(this),
  11871. doc = $(document),
  11872. toolbar = $('<div class="ui-widget-header dialogelfinder-drag ui-corner-top">'+(opts.title || 'Files')+'</div>'),
  11873. button = $('<a href="#" class="dialogelfinder-drag-close ui-corner-all"><span class="ui-icon ui-icon-closethick"> </span></a>')
  11874. .appendTo(toolbar)
  11875. .on('click', function(e) {
  11876. e.preventDefault();
  11877. node.dialogelfinder('close');
  11878. }),
  11879. init = opts.handlers.init,
  11880. elfinder;
  11881. opts.handlers.init = function(e, fm) {
  11882. node.prepend(toolbar);
  11883. init && init(e, fm);
  11884. };
  11885. elfinder = node.addClass('elfinder dialogelfinder touch-punch')
  11886. .css('position', 'absolute')
  11887. .hide()
  11888. .appendTo('body')
  11889. .draggable({
  11890. handle : '.dialogelfinder-drag',
  11891. containment : 'window',
  11892. stop : function() {
  11893. node.trigger('resize');
  11894. elfinder.trigger('resize');
  11895. }
  11896. })
  11897. .elfinder(opts, opts2)
  11898. .elfinder('instance');
  11899. elfinder.reloadCallback = function(o, o2) {
  11900. elfinder.destroy();
  11901. o.handlers.init = init;
  11902. node.dialogelfinder(o, o2).dialogelfinder('open');
  11903. };
  11904. node.width(parseInt(node.width()) || 840) // fix width if set to "auto"
  11905. .data(destroy, !!opts.destroyOnClose)
  11906. .find('.elfinder-toolbar').removeClass('ui-corner-top');
  11907. opts.position && node.data(position, opts.position);
  11908. opts.autoOpen !== false && $(this).dialogelfinder('open');
  11909. });
  11910. } else {
  11911. if (opts === 'open') {
  11912. node = $(this);
  11913. pos = node.data(position) || {
  11914. top : parseInt($(document).scrollTop() + ($(window).height() < node.height() ? 2 : ($(window).height() - node.height())/2)),
  11915. left : parseInt($(document).scrollLeft() + ($(window).width() < node.width() ? 2 : ($(window).width() - node.width())/2))
  11916. };
  11917. if (node.is(':hidden')) {
  11918. node.addClass('ui-front').css(pos).show().trigger('resize');
  11919. setTimeout(function() {
  11920. // fix resize icon position and make elfinder active
  11921. node.trigger('resize').trigger('mousedown');
  11922. }, 200);
  11923. }
  11924. } else if (opts === 'close') {
  11925. node = $(this).removeClass('ui-front');
  11926. if (node.is(':visible')) {
  11927. !!node.data(destroy)
  11928. ? node.elfinder('destroy').remove()
  11929. : node.elfinder('close');
  11930. }
  11931. } else if (opts === 'instance') {
  11932. return $(this).getElFinder();
  11933. }
  11934. }
  11935. return this;
  11936. };
  11937. /*
  11938. * File: /js/i18n/elfinder.en.js
  11939. */
  11940. /**
  11941. * English translation
  11942. * @author Troex Nevelin <troex@fury.scancode.ru>
  11943. * @author Naoki Sawada <hypweb+elfinder@gmail.com>
  11944. * @version 2019-02-18
  11945. */
  11946. // elfinder.en.js is integrated into elfinder.(full|min).js by jake build
  11947. if (typeof elFinder === 'function' && elFinder.prototype.i18) {
  11948. elFinder.prototype.i18.en = {
  11949. translator : 'Troex Nevelin &lt;troex@fury.scancode.ru&gt;, Naoki Sawada &lt;hypweb+elfinder@gmail.com&gt;',
  11950. language : 'English',
  11951. direction : 'ltr',
  11952. dateFormat : 'M d, Y h:i A', // will show like: Aug 24, 2018 04:39 PM
  11953. fancyDateFormat : '$1 h:i A', // will show like: Today 04:39 PM
  11954. nonameDateFormat : 'ymd-His', // noname upload will show like: 180824-163916
  11955. messages : {
  11956. /********************************** errors **********************************/
  11957. 'error' : 'Error',
  11958. 'errUnknown' : 'Unknown error.',
  11959. 'errUnknownCmd' : 'Unknown command.',
  11960. 'errJqui' : 'Invalid jQuery UI configuration. Selectable, draggable and droppable components must be included.',
  11961. 'errNode' : 'elFinder requires DOM Element to be created.',
  11962. 'errURL' : 'Invalid elFinder configuration! URL option is not set.',
  11963. 'errAccess' : 'Access denied.',
  11964. 'errConnect' : 'Unable to connect to backend.',
  11965. 'errAbort' : 'Connection aborted.',
  11966. 'errTimeout' : 'Connection timeout.',
  11967. 'errNotFound' : 'Backend not found.',
  11968. 'errResponse' : 'Invalid backend response.',
  11969. 'errConf' : 'Invalid backend configuration.',
  11970. 'errJSON' : 'PHP JSON module not installed.',
  11971. 'errNoVolumes' : 'Readable volumes not available.',
  11972. 'errCmdParams' : 'Invalid parameters for command "$1".',
  11973. 'errDataNotJSON' : 'Data is not JSON.',
  11974. 'errDataEmpty' : 'Data is empty.',
  11975. 'errCmdReq' : 'Backend request requires command name.',
  11976. 'errOpen' : 'Unable to open "$1".',
  11977. 'errNotFolder' : 'Object is not a folder.',
  11978. 'errNotFile' : 'Object is not a file.',
  11979. 'errRead' : 'Unable to read "$1".',
  11980. 'errWrite' : 'Unable to write into "$1".',
  11981. 'errPerm' : 'Permission denied.',
  11982. 'errLocked' : '"$1" is locked and can not be renamed, moved or removed.',
  11983. 'errExists' : 'Item named "$1" already exists.',
  11984. 'errInvName' : 'Invalid file name.',
  11985. 'errInvDirname' : 'Invalid folder name.', // from v2.1.24 added 12.4.2017
  11986. 'errFolderNotFound' : 'Folder not found.',
  11987. 'errFileNotFound' : 'File not found.',
  11988. 'errTrgFolderNotFound' : 'Target folder "$1" not found.',
  11989. 'errPopup' : 'Browser prevented opening popup window. To open file enable it in browser options.',
  11990. 'errMkdir' : 'Unable to create folder "$1".',
  11991. 'errMkfile' : 'Unable to create file "$1".',
  11992. 'errRename' : 'Unable to rename "$1".',
  11993. 'errCopyFrom' : 'Copying files from volume "$1" not allowed.',
  11994. 'errCopyTo' : 'Copying files to volume "$1" not allowed.',
  11995. 'errMkOutLink' : 'Unable to create a link to outside the volume root.', // from v2.1 added 03.10.2015
  11996. 'errUpload' : 'Upload error.', // old name - errUploadCommon
  11997. 'errUploadFile' : 'Unable to upload "$1".', // old name - errUpload
  11998. 'errUploadNoFiles' : 'No files found for upload.',
  11999. 'errUploadTotalSize' : 'Data exceeds the maximum allowed size.', // old name - errMaxSize
  12000. 'errUploadFileSize' : 'File exceeds maximum allowed size.', // old name - errFileMaxSize
  12001. 'errUploadMime' : 'File type not allowed.',
  12002. 'errUploadTransfer' : '"$1" transfer error.',
  12003. 'errUploadTemp' : 'Unable to make temporary file for upload.', // from v2.1 added 26.09.2015
  12004. 'errNotReplace' : 'Object "$1" already exists at this location and can not be replaced by object with another type.', // new
  12005. 'errReplace' : 'Unable to replace "$1".',
  12006. 'errSave' : 'Unable to save "$1".',
  12007. 'errCopy' : 'Unable to copy "$1".',
  12008. 'errMove' : 'Unable to move "$1".',
  12009. 'errCopyInItself' : 'Unable to copy "$1" into itself.',
  12010. 'errRm' : 'Unable to remove "$1".',
  12011. 'errTrash' : 'Unable into trash.', // from v2.1.24 added 30.4.2017
  12012. 'errRmSrc' : 'Unable remove source file(s).',
  12013. 'errExtract' : 'Unable to extract files from "$1".',
  12014. 'errArchive' : 'Unable to create archive.',
  12015. 'errArcType' : 'Unsupported archive type.',
  12016. 'errNoArchive' : 'File is not archive or has unsupported archive type.',
  12017. 'errCmdNoSupport' : 'Backend does not support this command.',
  12018. 'errReplByChild' : 'The folder "$1" can\'t be replaced by an item it contains.',
  12019. 'errArcSymlinks' : 'For security reason denied to unpack archives contains symlinks or files with not allowed names.', // edited 24.06.2012
  12020. 'errArcMaxSize' : 'Archive files exceeds maximum allowed size.',
  12021. 'errResize' : 'Unable to resize "$1".',
  12022. 'errResizeDegree' : 'Invalid rotate degree.', // added 7.3.2013
  12023. 'errResizeRotate' : 'Unable to rotate image.', // added 7.3.2013
  12024. 'errResizeSize' : 'Invalid image size.', // added 7.3.2013
  12025. 'errResizeNoChange' : 'Image size not changed.', // added 7.3.2013
  12026. 'errUsupportType' : 'Unsupported file type.',
  12027. 'errNotUTF8Content' : 'File "$1" is not in UTF-8 and cannot be edited.', // added 9.11.2011
  12028. 'errNetMount' : 'Unable to mount "$1".', // added 17.04.2012
  12029. 'errNetMountNoDriver' : 'Unsupported protocol.', // added 17.04.2012
  12030. 'errNetMountFailed' : 'Mount failed.', // added 17.04.2012
  12031. 'errNetMountHostReq' : 'Host required.', // added 18.04.2012
  12032. 'errSessionExpires' : 'Your session has expired due to inactivity.',
  12033. 'errCreatingTempDir' : 'Unable to create temporary directory: "$1"',
  12034. 'errFtpDownloadFile' : 'Unable to download file from FTP: "$1"',
  12035. 'errFtpUploadFile' : 'Unable to upload file to FTP: "$1"',
  12036. 'errFtpMkdir' : 'Unable to create remote directory on FTP: "$1"',
  12037. 'errArchiveExec' : 'Error while archiving files: "$1"',
  12038. 'errExtractExec' : 'Error while extracting files: "$1"',
  12039. 'errNetUnMount' : 'Unable to unmount.', // from v2.1 added 30.04.2012
  12040. 'errConvUTF8' : 'Not convertible to UTF-8', // from v2.1 added 08.04.2014
  12041. 'errFolderUpload' : 'Try the modern browser, If you\'d like to upload the folder.', // from v2.1 added 26.6.2015
  12042. 'errSearchTimeout' : 'Timed out while searching "$1". Search result is partial.', // from v2.1 added 12.1.2016
  12043. 'errReauthRequire' : 'Re-authorization is required.', // from v2.1.10 added 24.3.2016
  12044. 'errMaxTargets' : 'Max number of selectable items is $1.', // from v2.1.17 added 17.10.2016
  12045. 'errRestore' : 'Unable to restore from the trash. Can\'t identify the restore destination.', // from v2.1.24 added 3.5.2017
  12046. 'errEditorNotFound' : 'Editor not found to this file type.', // from v2.1.25 added 23.5.2017
  12047. 'errServerError' : 'Error occurred on the server side.', // from v2.1.25 added 16.6.2017
  12048. 'errEmpty' : 'Unable to empty folder "$1".', // from v2.1.25 added 22.6.2017
  12049. 'moreErrors' : 'There are $1 more errors.', // from v2.1.44 added 9.12.2018
  12050. /******************************* commands names ********************************/
  12051. 'cmdarchive' : 'Create archive',
  12052. 'cmdback' : 'Back',
  12053. 'cmdcopy' : 'Copy',
  12054. 'cmdcut' : 'Cut',
  12055. 'cmddownload' : 'Download',
  12056. 'cmdduplicate' : 'Duplicate',
  12057. 'cmdedit' : 'Edit file',
  12058. 'cmdextract' : 'Extract files from archive',
  12059. 'cmdforward' : 'Forward',
  12060. 'cmdgetfile' : 'Select files',
  12061. 'cmdhelp' : 'About this software',
  12062. 'cmdhome' : 'Root',
  12063. 'cmdinfo' : 'Get info',
  12064. 'cmdmkdir' : 'New folder',
  12065. 'cmdmkdirin' : 'Into New Folder', // from v2.1.7 added 19.2.2016
  12066. 'cmdmkfile' : 'New file',
  12067. 'cmdopen' : 'Open',
  12068. 'cmdpaste' : 'Paste',
  12069. 'cmdquicklook' : 'Preview',
  12070. 'cmdreload' : 'Reload',
  12071. 'cmdrename' : 'Rename',
  12072. 'cmdrm' : 'Delete',
  12073. 'cmdtrash' : 'Into trash', //from v2.1.24 added 29.4.2017
  12074. 'cmdrestore' : 'Restore', //from v2.1.24 added 3.5.2017
  12075. 'cmdsearch' : 'Find files',
  12076. 'cmdup' : 'Go to parent folder',
  12077. 'cmdupload' : 'Upload files',
  12078. 'cmdview' : 'View',
  12079. 'cmdresize' : 'Resize & Rotate',
  12080. 'cmdsort' : 'Sort',
  12081. 'cmdnetmount' : 'Mount network volume', // added 18.04.2012
  12082. 'cmdnetunmount': 'Unmount', // from v2.1 added 30.04.2012
  12083. 'cmdplaces' : 'To Places', // added 28.12.2014
  12084. 'cmdchmod' : 'Change mode', // from v2.1 added 20.6.2015
  12085. 'cmdopendir' : 'Open a folder', // from v2.1 added 13.1.2016
  12086. 'cmdcolwidth' : 'Reset column width', // from v2.1.13 added 12.06.2016
  12087. 'cmdfullscreen': 'Full Screen', // from v2.1.15 added 03.08.2016
  12088. 'cmdmove' : 'Move', // from v2.1.15 added 21.08.2016
  12089. 'cmdempty' : 'Empty the folder', // from v2.1.25 added 22.06.2017
  12090. 'cmdundo' : 'Undo', // from v2.1.27 added 31.07.2017
  12091. 'cmdredo' : 'Redo', // from v2.1.27 added 31.07.2017
  12092. 'cmdpreference': 'Preferences', // from v2.1.27 added 03.08.2017
  12093. 'cmdselectall' : 'Select all', // from v2.1.28 added 15.08.2017
  12094. 'cmdselectnone': 'Select none', // from v2.1.28 added 15.08.2017
  12095. 'cmdselectinvert': 'Invert selection', // from v2.1.28 added 15.08.2017
  12096. 'cmdopennew' : 'Open in new window', // from v2.1.38 added 3.4.2018
  12097. 'cmdhide' : 'Hide (Preference)', // from v2.1.41 added 24.7.2018
  12098. /*********************************** buttons ***********************************/
  12099. 'btnClose' : 'Close',
  12100. 'btnSave' : 'Save',
  12101. 'btnRm' : 'Remove',
  12102. 'btnApply' : 'Apply',
  12103. 'btnCancel' : 'Cancel',
  12104. 'btnNo' : 'No',
  12105. 'btnYes' : 'Yes',
  12106. 'btnMount' : 'Mount', // added 18.04.2012
  12107. 'btnApprove': 'Goto $1 & approve', // from v2.1 added 26.04.2012
  12108. 'btnUnmount': 'Unmount', // from v2.1 added 30.04.2012
  12109. 'btnConv' : 'Convert', // from v2.1 added 08.04.2014
  12110. 'btnCwd' : 'Here', // from v2.1 added 22.5.2015
  12111. 'btnVolume' : 'Volume', // from v2.1 added 22.5.2015
  12112. 'btnAll' : 'All', // from v2.1 added 22.5.2015
  12113. 'btnMime' : 'MIME Type', // from v2.1 added 22.5.2015
  12114. 'btnFileName':'Filename', // from v2.1 added 22.5.2015
  12115. 'btnSaveClose': 'Save & Close', // from v2.1 added 12.6.2015
  12116. 'btnBackup' : 'Backup', // fromv2.1 added 28.11.2015
  12117. 'btnRename' : 'Rename', // from v2.1.24 added 6.4.2017
  12118. 'btnRenameAll' : 'Rename(All)', // from v2.1.24 added 6.4.2017
  12119. 'btnPrevious' : 'Prev ($1/$2)', // from v2.1.24 added 11.5.2017
  12120. 'btnNext' : 'Next ($1/$2)', // from v2.1.24 added 11.5.2017
  12121. 'btnSaveAs' : 'Save As', // from v2.1.25 added 24.5.2017
  12122. /******************************** notifications ********************************/
  12123. 'ntfopen' : 'Open folder',
  12124. 'ntffile' : 'Open file',
  12125. 'ntfreload' : 'Reload folder content',
  12126. 'ntfmkdir' : 'Creating folder',
  12127. 'ntfmkfile' : 'Creating files',
  12128. 'ntfrm' : 'Delete items',
  12129. 'ntfcopy' : 'Copy items',
  12130. 'ntfmove' : 'Move items',
  12131. 'ntfprepare' : 'Checking existing items',
  12132. 'ntfrename' : 'Rename files',
  12133. 'ntfupload' : 'Uploading files',
  12134. 'ntfdownload' : 'Downloading files',
  12135. 'ntfsave' : 'Save files',
  12136. 'ntfarchive' : 'Creating archive',
  12137. 'ntfextract' : 'Extracting files from archive',
  12138. 'ntfsearch' : 'Searching files',
  12139. 'ntfresize' : 'Resizing images',
  12140. 'ntfsmth' : 'Doing something',
  12141. 'ntfloadimg' : 'Loading image',
  12142. 'ntfnetmount' : 'Mounting network volume', // added 18.04.2012
  12143. 'ntfnetunmount': 'Unmounting network volume', // from v2.1 added 30.04.2012
  12144. 'ntfdim' : 'Acquiring image dimension', // added 20.05.2013
  12145. 'ntfreaddir' : 'Reading folder infomation', // from v2.1 added 01.07.2013
  12146. 'ntfurl' : 'Getting URL of link', // from v2.1 added 11.03.2014
  12147. 'ntfchmod' : 'Changing file mode', // from v2.1 added 20.6.2015
  12148. 'ntfpreupload': 'Verifying upload file name', // from v2.1 added 31.11.2015
  12149. 'ntfzipdl' : 'Creating a file for download', // from v2.1.7 added 23.1.2016
  12150. 'ntfparents' : 'Getting path infomation', // from v2.1.17 added 2.11.2016
  12151. 'ntfchunkmerge': 'Processing the uploaded file', // from v2.1.17 added 2.11.2016
  12152. 'ntftrash' : 'Doing throw in the trash', // from v2.1.24 added 2.5.2017
  12153. 'ntfrestore' : 'Doing restore from the trash', // from v2.1.24 added 3.5.2017
  12154. 'ntfchkdir' : 'Checking destination folder', // from v2.1.24 added 3.5.2017
  12155. 'ntfundo' : 'Undoing previous operation', // from v2.1.27 added 31.07.2017
  12156. 'ntfredo' : 'Redoing previous undone', // from v2.1.27 added 31.07.2017
  12157. 'ntfchkcontent' : 'Checking contents', // from v2.1.41 added 3.8.2018
  12158. /*********************************** volumes *********************************/
  12159. 'volume_Trash' : 'Trash', //from v2.1.24 added 29.4.2017
  12160. /************************************ dates **********************************/
  12161. 'dateUnknown' : 'unknown',
  12162. 'Today' : 'Today',
  12163. 'Yesterday' : 'Yesterday',
  12164. 'msJan' : 'Jan',
  12165. 'msFeb' : 'Feb',
  12166. 'msMar' : 'Mar',
  12167. 'msApr' : 'Apr',
  12168. 'msMay' : 'May',
  12169. 'msJun' : 'Jun',
  12170. 'msJul' : 'Jul',
  12171. 'msAug' : 'Aug',
  12172. 'msSep' : 'Sep',
  12173. 'msOct' : 'Oct',
  12174. 'msNov' : 'Nov',
  12175. 'msDec' : 'Dec',
  12176. 'January' : 'January',
  12177. 'February' : 'February',
  12178. 'March' : 'March',
  12179. 'April' : 'April',
  12180. 'May' : 'May',
  12181. 'June' : 'June',
  12182. 'July' : 'July',
  12183. 'August' : 'August',
  12184. 'September' : 'September',
  12185. 'October' : 'October',
  12186. 'November' : 'November',
  12187. 'December' : 'December',
  12188. 'Sunday' : 'Sunday',
  12189. 'Monday' : 'Monday',
  12190. 'Tuesday' : 'Tuesday',
  12191. 'Wednesday' : 'Wednesday',
  12192. 'Thursday' : 'Thursday',
  12193. 'Friday' : 'Friday',
  12194. 'Saturday' : 'Saturday',
  12195. 'Sun' : 'Sun',
  12196. 'Mon' : 'Mon',
  12197. 'Tue' : 'Tue',
  12198. 'Wed' : 'Wed',
  12199. 'Thu' : 'Thu',
  12200. 'Fri' : 'Fri',
  12201. 'Sat' : 'Sat',
  12202. /******************************** sort variants ********************************/
  12203. 'sortname' : 'by name',
  12204. 'sortkind' : 'by kind',
  12205. 'sortsize' : 'by size',
  12206. 'sortdate' : 'by date',
  12207. 'sortFoldersFirst' : 'Folders first',
  12208. 'sortperm' : 'by permission', // from v2.1.13 added 13.06.2016
  12209. 'sortmode' : 'by mode', // from v2.1.13 added 13.06.2016
  12210. 'sortowner' : 'by owner', // from v2.1.13 added 13.06.2016
  12211. 'sortgroup' : 'by group', // from v2.1.13 added 13.06.2016
  12212. 'sortAlsoTreeview' : 'Also Treeview', // from v2.1.15 added 01.08.2016
  12213. /********************************** new items **********************************/
  12214. 'untitled file.txt' : 'NewFile.txt', // added 10.11.2015
  12215. 'untitled folder' : 'NewFolder', // added 10.11.2015
  12216. 'Archive' : 'NewArchive', // from v2.1 added 10.11.2015
  12217. 'untitled file' : 'NewFile.$1', // from v2.1.41 added 6.8.2018
  12218. 'extentionfile' : '$1: File', // from v2.1.41 added 6.8.2018
  12219. 'extentiontype' : '$1: $2', // from v2.1.43 added 17.10.2018
  12220. /********************************** messages **********************************/
  12221. 'confirmReq' : 'Confirmation required',
  12222. 'confirmRm' : 'Are you sure you want to permanently remove items?<br/>This cannot be undone!',
  12223. 'confirmRepl' : 'Replace old file with new one? (If it contains folders, it will be merged. To backup and replace, select Backup.)',
  12224. 'confirmRest' : 'Replace existing item with the item in trash?', // fromv2.1.24 added 5.5.2017
  12225. '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
  12226. '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
  12227. 'confirmNotSave' : 'It has been modified.<br/>Losing work if you do not save changes.', // from v2.1 added 15.7.2015
  12228. 'confirmTrash' : 'Are you sure you want to move items to trash bin?', //from v2.1.24 added 29.4.2017
  12229. 'apllyAll' : 'Apply to all',
  12230. 'name' : 'Name',
  12231. 'size' : 'Size',
  12232. 'perms' : 'Permissions',
  12233. 'modify' : 'Modified',
  12234. 'kind' : 'Kind',
  12235. 'read' : 'read',
  12236. 'write' : 'write',
  12237. 'noaccess' : 'no access',
  12238. 'and' : 'and',
  12239. 'unknown' : 'unknown',
  12240. 'selectall' : 'Select all items',
  12241. 'selectfiles' : 'Select item(s)',
  12242. 'selectffile' : 'Select first item',
  12243. 'selectlfile' : 'Select last item',
  12244. 'viewlist' : 'List view',
  12245. 'viewicons' : 'Icons view',
  12246. 'viewSmall' : 'Small icons', // from v2.1.39 added 22.5.2018
  12247. 'viewMedium' : 'Medium icons', // from v2.1.39 added 22.5.2018
  12248. 'viewLarge' : 'Large icons', // from v2.1.39 added 22.5.2018
  12249. 'viewExtraLarge' : 'Extra large icons', // from v2.1.39 added 22.5.2018
  12250. 'places' : 'Places',
  12251. 'calc' : 'Calculate',
  12252. 'path' : 'Path',
  12253. 'aliasfor' : 'Alias for',
  12254. 'locked' : 'Locked',
  12255. 'dim' : 'Dimensions',
  12256. 'files' : 'Files',
  12257. 'folders' : 'Folders',
  12258. 'items' : 'Items',
  12259. 'yes' : 'yes',
  12260. 'no' : 'no',
  12261. 'link' : 'Link',
  12262. 'searcresult' : 'Search results',
  12263. 'selected' : 'selected items',
  12264. 'about' : 'About',
  12265. 'shortcuts' : 'Shortcuts',
  12266. 'help' : 'Help',
  12267. 'webfm' : 'Web file manager',
  12268. 'ver' : 'Version',
  12269. 'protocolver' : 'protocol version',
  12270. 'homepage' : 'Project home',
  12271. 'docs' : 'Documentation',
  12272. 'github' : 'Fork us on GitHub',
  12273. 'twitter' : 'Follow us on Twitter',
  12274. 'facebook' : 'Join us on Facebook',
  12275. 'team' : 'Team',
  12276. 'chiefdev' : 'chief developer',
  12277. 'developer' : 'developer',
  12278. 'contributor' : 'contributor',
  12279. 'maintainer' : 'maintainer',
  12280. 'translator' : 'translator',
  12281. 'icons' : 'Icons',
  12282. 'dontforget' : 'and don\'t forget to take your towel',
  12283. 'shortcutsof' : 'Shortcuts disabled',
  12284. 'dropFiles' : 'Drop files here',
  12285. 'or' : 'or',
  12286. 'selectForUpload' : 'Select files',
  12287. 'moveFiles' : 'Move items',
  12288. 'copyFiles' : 'Copy items',
  12289. 'restoreFiles' : 'Restore items', // from v2.1.24 added 5.5.2017
  12290. 'rmFromPlaces' : 'Remove from places',
  12291. 'aspectRatio' : 'Aspect ratio',
  12292. 'scale' : 'Scale',
  12293. 'width' : 'Width',
  12294. 'height' : 'Height',
  12295. 'resize' : 'Resize',
  12296. 'crop' : 'Crop',
  12297. 'rotate' : 'Rotate',
  12298. 'rotate-cw' : 'Rotate 90 degrees CW',
  12299. 'rotate-ccw' : 'Rotate 90 degrees CCW',
  12300. 'degree' : '°',
  12301. 'netMountDialogTitle' : 'Mount network volume', // added 18.04.2012
  12302. 'protocol' : 'Protocol', // added 18.04.2012
  12303. 'host' : 'Host', // added 18.04.2012
  12304. 'port' : 'Port', // added 18.04.2012
  12305. 'user' : 'User', // added 18.04.2012
  12306. 'pass' : 'Password', // added 18.04.2012
  12307. 'confirmUnmount' : 'Are you sure to unmount $1?', // from v2.1 added 30.04.2012
  12308. 'dropFilesBrowser': 'Drop or Paste files from browser', // from v2.1 added 30.05.2012
  12309. 'dropPasteFiles' : 'Drop files, Paste URLs or images(clipboard) here', // from v2.1 added 07.04.2014
  12310. 'encoding' : 'Encoding', // from v2.1 added 19.12.2014
  12311. 'locale' : 'Locale', // from v2.1 added 19.12.2014
  12312. 'searchTarget' : 'Target: $1', // from v2.1 added 22.5.2015
  12313. 'searchMime' : 'Search by input MIME Type', // from v2.1 added 22.5.2015
  12314. 'owner' : 'Owner', // from v2.1 added 20.6.2015
  12315. 'group' : 'Group', // from v2.1 added 20.6.2015
  12316. 'other' : 'Other', // from v2.1 added 20.6.2015
  12317. 'execute' : 'Execute', // from v2.1 added 20.6.2015
  12318. 'perm' : 'Permission', // from v2.1 added 20.6.2015
  12319. 'mode' : 'Mode', // from v2.1 added 20.6.2015
  12320. 'emptyFolder' : 'Folder is empty', // from v2.1.6 added 30.12.2015
  12321. 'emptyFolderDrop' : 'Folder is empty\\A Drop to add items', // from v2.1.6 added 30.12.2015
  12322. 'emptyFolderLTap' : 'Folder is empty\\A Long tap to add items', // from v2.1.6 added 30.12.2015
  12323. 'quality' : 'Quality', // from v2.1.6 added 5.1.2016
  12324. 'autoSync' : 'Auto sync', // from v2.1.6 added 10.1.2016
  12325. 'moveUp' : 'Move up', // from v2.1.6 added 18.1.2016
  12326. 'getLink' : 'Get URL link', // from v2.1.7 added 9.2.2016
  12327. 'selectedItems' : 'Selected items ($1)', // from v2.1.7 added 2.19.2016
  12328. 'folderId' : 'Folder ID', // from v2.1.10 added 3.25.2016
  12329. 'offlineAccess' : 'Allow offline access', // from v2.1.10 added 3.25.2016
  12330. 'reAuth' : 'To re-authenticate', // from v2.1.10 added 3.25.2016
  12331. 'nowLoading' : 'Now loading...', // from v2.1.12 added 4.26.2016
  12332. 'openMulti' : 'Open multiple files', // from v2.1.12 added 5.14.2016
  12333. '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
  12334. 'emptySearch' : 'Search results is empty in search target.', // from v2.1.12 added 5.16.2016
  12335. 'editingFile' : 'It is editing a file.', // from v2.1.13 added 6.3.2016
  12336. 'hasSelected' : 'You have selected $1 items.', // from v2.1.13 added 6.3.2016
  12337. 'hasClipboard' : 'You have $1 items in the clipboard.', // from v2.1.13 added 6.3.2016
  12338. 'incSearchOnly' : 'Incremental search is only from the current view.', // from v2.1.13 added 6.30.2016
  12339. 'reinstate' : 'Reinstate', // from v2.1.15 added 3.8.2016
  12340. 'complete' : '$1 complete', // from v2.1.15 added 21.8.2016
  12341. 'contextmenu' : 'Context menu', // from v2.1.15 added 9.9.2016
  12342. 'pageTurning' : 'Page turning', // from v2.1.15 added 10.9.2016
  12343. 'volumeRoots' : 'Volume roots', // from v2.1.16 added 16.9.2016
  12344. 'reset' : 'Reset', // from v2.1.16 added 1.10.2016
  12345. 'bgcolor' : 'Background color', // from v2.1.16 added 1.10.2016
  12346. 'colorPicker' : 'Color picker', // from v2.1.16 added 1.10.2016
  12347. '8pxgrid' : '8px Grid', // from v2.1.16 added 4.10.2016
  12348. 'enabled' : 'Enabled', // from v2.1.16 added 4.10.2016
  12349. 'disabled' : 'Disabled', // from v2.1.16 added 4.10.2016
  12350. 'emptyIncSearch' : 'Search results is empty in current view.\\A Press [Enter] to expand search target.', // from v2.1.16 added 5.10.2016
  12351. 'emptyLetSearch' : 'First letter search results is empty in current view.', // from v2.1.23 added 24.3.2017
  12352. 'textLabel' : 'Text label', // from v2.1.17 added 13.10.2016
  12353. 'minsLeft' : '$1 mins left', // from v2.1.17 added 13.11.2016
  12354. 'openAsEncoding' : 'Reopen with selected encoding', // from v2.1.19 added 2.12.2016
  12355. 'saveAsEncoding' : 'Save with the selected encoding', // from v2.1.19 added 2.12.2016
  12356. 'selectFolder' : 'Select folder', // from v2.1.20 added 13.12.2016
  12357. 'firstLetterSearch': 'First letter search', // from v2.1.23 added 24.3.2017
  12358. 'presets' : 'Presets', // from v2.1.25 added 26.5.2017
  12359. 'tooManyToTrash' : 'It\'s too many items so it can\'t into trash.', // from v2.1.25 added 9.6.2017
  12360. 'TextArea' : 'TextArea', // from v2.1.25 added 14.6.2017
  12361. 'folderToEmpty' : 'Empty the folder "$1".', // from v2.1.25 added 22.6.2017
  12362. 'filderIsEmpty' : 'There are no items in a folder "$1".', // from v2.1.25 added 22.6.2017
  12363. 'preference' : 'Preference', // from v2.1.26 added 28.6.2017
  12364. 'language' : 'Language', // from v2.1.26 added 28.6.2017
  12365. 'clearBrowserData': 'Initialize the settings saved in this browser', // from v2.1.26 added 28.6.2017
  12366. 'toolbarPref' : 'Toolbar settings', // from v2.1.27 added 2.8.2017
  12367. 'charsLeft' : '... $1 chars left.', // from v2.1.29 added 30.8.2017
  12368. 'sum' : 'Sum', // from v2.1.29 added 28.9.2017
  12369. 'roughFileSize' : 'Rough file size', // from v2.1.30 added 2.11.2017
  12370. 'autoFocusDialog' : 'Focus on the element of dialog with mouseover', // from v2.1.30 added 2.11.2017
  12371. 'select' : 'Select', // from v2.1.30 added 23.11.2017
  12372. 'selectAction' : 'Action when select file', // from v2.1.30 added 23.11.2017
  12373. 'useStoredEditor' : 'Open with the editor used last time', // from v2.1.30 added 23.11.2017
  12374. 'selectinvert' : 'Invert selection', // from v2.1.30 added 25.11.2017
  12375. '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
  12376. 'batchRename' : 'Batch rename', // from v2.1.31 added 8.12.2017
  12377. 'plusNumber' : '+ Number', // from v2.1.31 added 8.12.2017
  12378. 'asPrefix' : 'Add prefix', // from v2.1.31 added 8.12.2017
  12379. 'asSuffix' : 'Add suffix', // from v2.1.31 added 8.12.2017
  12380. 'changeExtention' : 'Change extention', // from v2.1.31 added 8.12.2017
  12381. 'columnPref' : 'Columns settings (List view)', // from v2.1.32 added 6.2.2018
  12382. 'reflectOnImmediate' : 'All changes will reflect immediately to the archive.', // from v2.1.33 added 2.3.2018
  12383. 'reflectOnUnmount' : 'Any changes will not reflect until un-mount this volume.', // from v2.1.33 added 2.3.2018
  12384. '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
  12385. 'selectionInfo' : 'Selection Info', // from v2.1.33 added 7.3.2018
  12386. 'hashChecker' : 'Algorithms to show the file hash', // from v2.1.33 added 10.3.2018
  12387. 'infoItems' : 'Info Items (Selection Info Panel)', // from v2.1.38 added 28.3.2018
  12388. 'pressAgainToExit': 'Press again to exit.', // from v2.1.38 added 1.4.2018
  12389. 'toolbar' : 'Toolbar', // from v2.1.38 added 4.4.2018
  12390. 'workspace' : 'Work Space', // from v2.1.38 added 4.4.2018
  12391. 'dialog' : 'Dialog', // from v2.1.38 added 4.4.2018
  12392. 'all' : 'All', // from v2.1.38 added 4.4.2018
  12393. 'iconSize' : 'Icon Size (Icons view)', // from v2.1.39 added 7.5.2018
  12394. 'editorMaximized' : 'Open the maximized editor window', // from v2.1.40 added 30.6.2018
  12395. 'editorConvNoApi' : 'Because conversion by API is not currently available, please convert on the website.', //from v2.1.40 added 8.7.2018
  12396. '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
  12397. 'convertOn' : 'Convert on the site of $1', // from v2.1.40 added 10.7.2018
  12398. 'integrations' : 'Integrations', // from v2.1.40 added 11.7.2018
  12399. '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
  12400. 'showHidden' : 'Show hidden items', // from v2.1.41 added 24.7.2018
  12401. 'hideHidden' : 'Hide hidden items', // from v2.1.41 added 24.7.2018
  12402. 'toggleHidden' : 'Show/Hide hidden items', // from v2.1.41 added 24.7.2018
  12403. 'makefileTypes' : 'File types to enable with "New file"', // from v2.1.41 added 7.8.2018
  12404. 'typeOfTextfile' : 'Type of the Text file', // from v2.1.41 added 7.8.2018
  12405. 'add' : 'Add', // from v2.1.41 added 7.8.2018
  12406. 'theme' : 'Theme', // from v2.1.43 added 19.10.2018
  12407. 'default' : 'Default', // from v2.1.43 added 19.10.2018
  12408. 'description' : 'Description', // from v2.1.43 added 19.10.2018
  12409. 'website' : 'Website', // from v2.1.43 added 19.10.2018
  12410. 'author' : 'Author', // from v2.1.43 added 19.10.2018
  12411. 'email' : 'Email', // from v2.1.43 added 19.10.2018
  12412. 'license' : 'License', // from v2.1.43 added 19.10.2018
  12413. '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
  12414. 'dblclickToSelect': 'Double click on the file to select it.', // from v2.1.47 added 22.1.2019
  12415. 'useFullscreen' : 'Use fullscreen mode', // from v2.1.47 added 19.2.2019
  12416. /********************************** mimetypes **********************************/
  12417. 'kindUnknown' : 'Unknown',
  12418. 'kindRoot' : 'Volume Root', // from v2.1.16 added 16.10.2016
  12419. 'kindFolder' : 'Folder',
  12420. 'kindSelects' : 'Selections', // from v2.1.29 added 29.8.2017
  12421. 'kindAlias' : 'Alias',
  12422. 'kindAliasBroken' : 'Broken alias',
  12423. // applications
  12424. 'kindApp' : 'Application',
  12425. 'kindPostscript' : 'Postscript document',
  12426. 'kindMsOffice' : 'Microsoft Office document',
  12427. 'kindMsWord' : 'Microsoft Word document',
  12428. 'kindMsExcel' : 'Microsoft Excel document',
  12429. 'kindMsPP' : 'Microsoft Powerpoint presentation',
  12430. 'kindOO' : 'Open Office document',
  12431. 'kindAppFlash' : 'Flash application',
  12432. 'kindPDF' : 'Portable Document Format (PDF)',
  12433. 'kindTorrent' : 'Bittorrent file',
  12434. 'kind7z' : '7z archive',
  12435. 'kindTAR' : 'TAR archive',
  12436. 'kindGZIP' : 'GZIP archive',
  12437. 'kindBZIP' : 'BZIP archive',
  12438. 'kindXZ' : 'XZ archive',
  12439. 'kindZIP' : 'ZIP archive',
  12440. 'kindRAR' : 'RAR archive',
  12441. 'kindJAR' : 'Java JAR file',
  12442. 'kindTTF' : 'True Type font',
  12443. 'kindOTF' : 'Open Type font',
  12444. 'kindRPM' : 'RPM package',
  12445. // texts
  12446. 'kindText' : 'Text document',
  12447. 'kindTextPlain' : 'Plain text',
  12448. 'kindPHP' : 'PHP source',
  12449. 'kindCSS' : 'Cascading style sheet',
  12450. 'kindHTML' : 'HTML document',
  12451. 'kindJS' : 'Javascript source',
  12452. 'kindRTF' : 'Rich Text Format',
  12453. 'kindC' : 'C source',
  12454. 'kindCHeader' : 'C header source',
  12455. 'kindCPP' : 'C++ source',
  12456. 'kindCPPHeader' : 'C++ header source',
  12457. 'kindShell' : 'Unix shell script',
  12458. 'kindPython' : 'Python source',
  12459. 'kindJava' : 'Java source',
  12460. 'kindRuby' : 'Ruby source',
  12461. 'kindPerl' : 'Perl script',
  12462. 'kindSQL' : 'SQL source',
  12463. 'kindXML' : 'XML document',
  12464. 'kindAWK' : 'AWK source',
  12465. 'kindCSV' : 'Comma separated values',
  12466. 'kindDOCBOOK' : 'Docbook XML document',
  12467. 'kindMarkdown' : 'Markdown text', // added 20.7.2015
  12468. // images
  12469. 'kindImage' : 'Image',
  12470. 'kindBMP' : 'BMP image',
  12471. 'kindJPEG' : 'JPEG image',
  12472. 'kindGIF' : 'GIF Image',
  12473. 'kindPNG' : 'PNG Image',
  12474. 'kindTIFF' : 'TIFF image',
  12475. 'kindTGA' : 'TGA image',
  12476. 'kindPSD' : 'Adobe Photoshop image',
  12477. 'kindXBITMAP' : 'X bitmap image',
  12478. 'kindPXM' : 'Pixelmator image',
  12479. // media
  12480. 'kindAudio' : 'Audio media',
  12481. 'kindAudioMPEG' : 'MPEG audio',
  12482. 'kindAudioMPEG4' : 'MPEG-4 audio',
  12483. 'kindAudioMIDI' : 'MIDI audio',
  12484. 'kindAudioOGG' : 'Ogg Vorbis audio',
  12485. 'kindAudioWAV' : 'WAV audio',
  12486. 'AudioPlaylist' : 'MP3 playlist',
  12487. 'kindVideo' : 'Video media',
  12488. 'kindVideoDV' : 'DV movie',
  12489. 'kindVideoMPEG' : 'MPEG movie',
  12490. 'kindVideoMPEG4' : 'MPEG-4 movie',
  12491. 'kindVideoAVI' : 'AVI movie',
  12492. 'kindVideoMOV' : 'Quick Time movie',
  12493. 'kindVideoWM' : 'Windows Media movie',
  12494. 'kindVideoFlash' : 'Flash movie',
  12495. 'kindVideoMKV' : 'Matroska movie',
  12496. 'kindVideoOGG' : 'Ogg movie'
  12497. }
  12498. };
  12499. }
  12500. /*
  12501. * File: /js/ui/button.js
  12502. */
  12503. /**
  12504. * @class elFinder toolbar button widget.
  12505. * If command has variants - create menu
  12506. *
  12507. * @author Dmitry (dio) Levashov
  12508. **/
  12509. $.fn.elfinderbutton = function(cmd) {
  12510. return this.each(function() {
  12511. var c = 'class',
  12512. fm = cmd.fm,
  12513. disabled = fm.res(c, 'disabled'),
  12514. active = fm.res(c, 'active'),
  12515. hover = fm.res(c, 'hover'),
  12516. item = 'elfinder-button-menu-item',
  12517. selected = 'elfinder-button-menu-item-selected',
  12518. menu,
  12519. text = $('<span class="elfinder-button-text">'+cmd.title+'</span>'),
  12520. prvCname = cmd.className? cmd.className : cmd.name,
  12521. button = $(this).addClass('ui-state-default elfinder-button')
  12522. .attr('title', cmd.title)
  12523. .append('<span class="elfinder-button-icon elfinder-button-icon-' + prvCname + '"/>', text)
  12524. .on('mouseenter mouseleave', function(e) { !button.hasClass(disabled) && button[e.type == 'mouseleave' ? 'removeClass' : 'addClass'](hover);})
  12525. .on('click', function(e) {
  12526. if (!button.hasClass(disabled)) {
  12527. if (menu && cmd.variants.length >= 1) {
  12528. // close other menus
  12529. menu.is(':hidden') && fm.getUI().click();
  12530. e.stopPropagation();
  12531. menu.css(getMenuOffset()).slideToggle({
  12532. duration: 100,
  12533. done: function(e) {
  12534. fm[menu.is(':visible')? 'toFront' : 'toHide'](menu);
  12535. }
  12536. });
  12537. } else {
  12538. fm.exec(cmd.name, getSelected(), {_userAction: true, _currentType: 'toolbar', _currentNode: button });
  12539. }
  12540. }
  12541. }),
  12542. hideMenu = function() {
  12543. fm.toHide(menu);
  12544. },
  12545. getMenuOffset = function() {
  12546. var fmNode = fm.getUI(),
  12547. baseOffset = fmNode.offset(),
  12548. buttonOffset = button.offset();
  12549. return {
  12550. top : buttonOffset.top - baseOffset.top,
  12551. left : buttonOffset.left - baseOffset.left,
  12552. maxHeight : fmNode.height() - 40
  12553. };
  12554. },
  12555. getSelected = function() {
  12556. var sel = fm.selected(),
  12557. cwd;
  12558. if (!sel.length) {
  12559. if (cwd = fm.cwd()) {
  12560. sel = [ fm.cwd().hash ];
  12561. } else {
  12562. sel = void(0);
  12563. }
  12564. }
  12565. return sel;
  12566. },
  12567. tm;
  12568. text.hide();
  12569. // set self button object to cmd object
  12570. cmd.button = button;
  12571. // if command has variants create menu
  12572. if (Array.isArray(cmd.variants)) {
  12573. button.addClass('elfinder-menubutton');
  12574. menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu elfinder-button-' + prvCname + '-menu ui-corner-all"/>')
  12575. .hide()
  12576. .appendTo(fm.getUI())
  12577. .on('mouseenter mouseleave', '.'+item, function() { $(this).toggleClass(hover); })
  12578. .on('click', '.'+item, function(e) {
  12579. var opts = $(this).data('value');
  12580. e.preventDefault();
  12581. e.stopPropagation();
  12582. button.removeClass(hover);
  12583. fm.toHide(menu);
  12584. if (typeof opts === 'undefined') {
  12585. opts = {};
  12586. }
  12587. if (typeof opts === 'object') {
  12588. opts._userAction = true;
  12589. }
  12590. fm.exec(cmd.name, getSelected(), opts);
  12591. })
  12592. .on('close', hideMenu);
  12593. fm.bind('disable select', hideMenu).getUI().on('click', hideMenu);
  12594. cmd.change(function() {
  12595. menu.html('');
  12596. $.each(cmd.variants, function(i, variant) {
  12597. menu.append($('<div class="'+item+'">'+variant[1]+'</div>').data('value', variant[0]).addClass(variant[0] == cmd.value ? selected : ''));
  12598. });
  12599. });
  12600. }
  12601. cmd.change(function() {
  12602. var cName;
  12603. tm && cancelAnimationFrame(tm);
  12604. tm = requestAnimationFrame(function() {
  12605. if (cmd.disabled()) {
  12606. button.removeClass(active+' '+hover).addClass(disabled);
  12607. } else {
  12608. button.removeClass(disabled);
  12609. button[cmd.active() ? 'addClass' : 'removeClass'](active);
  12610. }
  12611. if (cmd.syncTitleOnChange) {
  12612. cName = cmd.className? cmd.className : cmd.name;
  12613. if (prvCname !== cName) {
  12614. button.children('.elfinder-button-icon').removeClass('elfinder-button-icon-' + prvCname).addClass('elfinder-button-icon-' + cName);
  12615. if (menu) {
  12616. menu.removeClass('elfinder-button-' + prvCname + '-menu').addClass('elfinder-button-' + cName + '-menu');
  12617. }
  12618. prvCname = cName;
  12619. }
  12620. text.html(cmd.title);
  12621. button.attr('title', cmd.title);
  12622. }
  12623. });
  12624. })
  12625. .change();
  12626. });
  12627. };
  12628. /*
  12629. * File: /js/ui/contextmenu.js
  12630. */
  12631. /**
  12632. * @class elFinder contextmenu
  12633. *
  12634. * @author Dmitry (dio) Levashov
  12635. **/
  12636. $.fn.elfindercontextmenu = function(fm) {
  12637. return this.each(function() {
  12638. var self = $(this),
  12639. cmItem = 'elfinder-contextmenu-item',
  12640. smItem = 'elfinder-contextsubmenu-item',
  12641. exIcon = 'elfinder-contextmenu-extra-icon',
  12642. cHover = fm.res('class', 'hover'),
  12643. dragOpt = {
  12644. distance: 8,
  12645. start: function() {
  12646. menu.data('drag', true).data('touching') && menu.find('.'+cHover).removeClass(cHover);
  12647. },
  12648. stop: function() {
  12649. menu.data('draged', true).removeData('drag');
  12650. }
  12651. },
  12652. menu = $(this).addClass('touch-punch ui-helper-reset ui-front ui-widget ui-state-default ui-corner-all elfinder-contextmenu elfinder-contextmenu-'+fm.direction)
  12653. .hide()
  12654. .on('touchstart', function(e) {
  12655. menu.data('touching', true).children().removeClass(cHover);
  12656. })
  12657. .on('touchend', function(e) {
  12658. menu.removeData('touching');
  12659. })
  12660. .on('mouseenter mouseleave', '.'+cmItem, function(e) {
  12661. $(this).toggleClass(cHover, (e.type === 'mouseenter' || (! menu.data('draged') && menu.data('submenuKeep'))? true : false));
  12662. if (menu.data('draged') && menu.data('submenuKeep')) {
  12663. menu.find('.elfinder-contextmenu-sub:visible').parent().addClass(cHover);
  12664. }
  12665. })
  12666. .on('mouseenter mouseleave', '.'+exIcon, function(e) {
  12667. $(this).parent().toggleClass(cHover, e.type === 'mouseleave');
  12668. })
  12669. .on('mouseenter mouseleave', '.'+cmItem+',.'+smItem, function(e) {
  12670. var setIndex = function(target, sub) {
  12671. $.each(sub? subnodes : nodes, function(i, n) {
  12672. if (target[0] === n) {
  12673. (sub? subnodes : nodes)._cur = i;
  12674. if (sub) {
  12675. subselected = target;
  12676. } else {
  12677. selected = target;
  12678. }
  12679. return false;
  12680. }
  12681. });
  12682. };
  12683. if (e.originalEvent) {
  12684. var target = $(this),
  12685. unHover = function() {
  12686. if (selected && !selected.children('div.elfinder-contextmenu-sub:visible').length) {
  12687. selected.removeClass(cHover);
  12688. }
  12689. };
  12690. if (e.type === 'mouseenter') {
  12691. // mouseenter
  12692. if (target.hasClass(smItem)) {
  12693. // submenu
  12694. if (subselected) {
  12695. subselected.removeClass(cHover);
  12696. }
  12697. if (selected) {
  12698. subnodes = selected.find('div.'+smItem);
  12699. }
  12700. setIndex(target, true);
  12701. } else {
  12702. // menu
  12703. unHover();
  12704. setIndex(target);
  12705. }
  12706. } else {
  12707. // mouseleave
  12708. if (target.hasClass(smItem)) {
  12709. //submenu
  12710. subselected = null;
  12711. subnodes = null;
  12712. } else {
  12713. // menu
  12714. unHover();
  12715. (function(sel) {
  12716. setTimeout(function() {
  12717. if (sel === selected) {
  12718. selected = null;
  12719. }
  12720. }, 250);
  12721. })(selected);
  12722. }
  12723. }
  12724. }
  12725. })
  12726. .on('contextmenu', function(){return false;})
  12727. .on('mouseup', function() {
  12728. setTimeout(function() {
  12729. menu.removeData('draged');
  12730. }, 100);
  12731. })
  12732. .draggable(dragOpt),
  12733. ltr = fm.direction === 'ltr',
  12734. subpos = ltr? 'left' : 'right',
  12735. types = Object.assign({}, fm.options.contextmenu),
  12736. tpl = '<div class="'+cmItem+'{className}"><span class="elfinder-button-icon {icon} elfinder-contextmenu-icon"{style}/><span>{label}</span></div>',
  12737. item = function(label, icon, callback, opts) {
  12738. var className = '',
  12739. style = '',
  12740. iconClass = '',
  12741. v, pos;
  12742. if (opts) {
  12743. if (opts.className) {
  12744. className = ' ' + opts.className;
  12745. }
  12746. if (opts.iconClass) {
  12747. iconClass = opts.iconClass;
  12748. icon = '';
  12749. }
  12750. if (opts.iconImg) {
  12751. v = opts.iconImg.split(/ +/);
  12752. pos = v[1] && v[2]? fm.escape(v[1] + 'px ' + v[2] + 'px') : '';
  12753. style = ' style="background:url(\''+fm.escape(v[0])+'\') '+(pos? pos : '0 0')+' no-repeat;'+(pos? '' : 'posbackground-size:contain;')+'"';
  12754. }
  12755. }
  12756. return $(tpl.replace('{icon}', icon ? 'elfinder-button-icon-'+icon : (iconClass? iconClass : ''))
  12757. .replace('{label}', label)
  12758. .replace('{style}', style)
  12759. .replace('{className}', className))
  12760. .on('click', function(e) {
  12761. e.stopPropagation();
  12762. e.preventDefault();
  12763. callback();
  12764. });
  12765. },
  12766. urlIcon = function(iconUrl) {
  12767. var v = iconUrl.split(/ +/),
  12768. pos = v[1] && v[2]? (v[1] + 'px ' + v[2] + 'px') : '';
  12769. return {
  12770. backgroundImage: 'url("'+v[0]+'")',
  12771. backgroundRepeat: 'no-repeat',
  12772. backgroundPosition: pos? pos : '',
  12773. backgroundSize: pos? '' : 'contain'
  12774. };
  12775. },
  12776. base, cwd,
  12777. nodes, selected, subnodes, subselected, autoSyncStop, subHoverTm,
  12778. autoToggle = function() {
  12779. var evTouchStart = 'touchstart.contextmenuAutoToggle';
  12780. menu.data('hideTm') && clearTimeout(menu.data('hideTm'));
  12781. if (menu.is(':visible')) {
  12782. menu.on('touchstart', function(e) {
  12783. if (e.originalEvent.touches.length > 1) {
  12784. return;
  12785. }
  12786. menu.stop();
  12787. fm.toFront(menu);
  12788. menu.data('hideTm') && clearTimeout(menu.data('hideTm'));
  12789. })
  12790. .data('hideTm', setTimeout(function() {
  12791. if (menu.is(':visible')) {
  12792. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  12793. cwd.find('.elfinder-cwd-file.ui-selected')
  12794. .one(evTouchStart, function(e) {
  12795. if (e.originalEvent.touches.length > 1) {
  12796. return;
  12797. }
  12798. var tgt = $(e.target);
  12799. if (menu.first().length && !tgt.is('input:checkbox') && !tgt.hasClass('elfinder-cwd-select')) {
  12800. e.stopPropagation();
  12801. //e.preventDefault();
  12802. open(e.originalEvent.touches[0].pageX, e.originalEvent.touches[0].pageY);
  12803. cwd.data('longtap', true)
  12804. tgt.one('touchend', function() {
  12805. setTimeout(function() {
  12806. cwd.removeData('longtap');
  12807. }, 80);
  12808. });
  12809. return;
  12810. }
  12811. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  12812. })
  12813. .one('unselect.'+fm.namespace, function() {
  12814. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  12815. });
  12816. menu.fadeOut({
  12817. duration: 300,
  12818. fail: function() {
  12819. menu.css('opacity', '1').show();
  12820. },
  12821. done: function() {
  12822. fm.toHide(menu);
  12823. }
  12824. });
  12825. }
  12826. }, 4500));
  12827. }
  12828. },
  12829. keyEvts = function(e) {
  12830. var code = e.keyCode,
  12831. ESC = $.ui.keyCode.ESCAPE,
  12832. ENT = $.ui.keyCode.ENTER,
  12833. LEFT = $.ui.keyCode.LEFT,
  12834. RIGHT = $.ui.keyCode.RIGHT,
  12835. UP = $.ui.keyCode.UP,
  12836. DOWN = $.ui.keyCode.DOWN,
  12837. subent = fm.direction === 'ltr'? RIGHT : LEFT,
  12838. sublev = subent === RIGHT? LEFT : RIGHT;
  12839. if ($.inArray(code, [ESC, ENT, LEFT, RIGHT, UP, DOWN]) !== -1) {
  12840. e.preventDefault();
  12841. e.stopPropagation();
  12842. e.stopImmediatePropagation();
  12843. if (code == ESC || code === sublev) {
  12844. if (selected && subnodes && subselected) {
  12845. subselected.trigger('mouseleave').trigger('submenuclose');
  12846. selected.addClass(cHover);
  12847. subnodes = null;
  12848. subselected = null;
  12849. } else {
  12850. code == ESC && close();
  12851. }
  12852. } else if (code == UP || code == DOWN) {
  12853. if (subnodes) {
  12854. if (subselected) {
  12855. subselected.trigger('mouseleave');
  12856. }
  12857. if (code == DOWN && (! subselected || subnodes.length <= ++subnodes._cur)) {
  12858. subnodes._cur = 0;
  12859. } else if (code == UP && (! subselected || --subnodes._cur < 0)) {
  12860. subnodes._cur = subnodes.length - 1;
  12861. }
  12862. subselected = subnodes.eq(subnodes._cur).trigger('mouseenter');
  12863. } else {
  12864. subnodes = null;
  12865. if (selected) {
  12866. selected.trigger('mouseleave');
  12867. }
  12868. if (code == DOWN && (! selected || nodes.length <= ++nodes._cur)) {
  12869. nodes._cur = 0;
  12870. } else if (code == UP && (! selected || --nodes._cur < 0)) {
  12871. nodes._cur = nodes.length - 1;
  12872. }
  12873. selected = nodes.eq(nodes._cur).addClass(cHover);
  12874. }
  12875. } else if (selected && (code == ENT || code === subent)) {
  12876. if (selected.hasClass('elfinder-contextmenu-group')) {
  12877. if (subselected) {
  12878. code == ENT && subselected.click();
  12879. } else {
  12880. selected.trigger('mouseenter');
  12881. subnodes = selected.find('div.'+smItem);
  12882. subnodes._cur = 0;
  12883. subselected = subnodes.first().addClass(cHover);
  12884. }
  12885. } else {
  12886. code == ENT && selected.click();
  12887. }
  12888. }
  12889. }
  12890. },
  12891. open = function(x, y, css) {
  12892. var width = menu.outerWidth(),
  12893. height = menu.outerHeight(),
  12894. bstyle = base.attr('style'),
  12895. bpos = base.offset(),
  12896. bwidth = base.width(),
  12897. bheight = base.height(),
  12898. mw = fm.UA.Mobile? 40 : 2,
  12899. mh = fm.UA.Mobile? 20 : 2,
  12900. x = x - (bpos? bpos.left : 0),
  12901. y = y - (bpos? bpos.top : 0),
  12902. css = Object.assign(css || {}, {
  12903. top : Math.max(0, y + mh + height < bheight ? y + mh : y - (y + height - bheight)),
  12904. left : Math.max(0, (x < width + mw || x + mw + width < bwidth)? x + mw : x - mw - width),
  12905. opacity : '1'
  12906. }),
  12907. evts;
  12908. autoSyncStop = true;
  12909. fm.autoSync('stop');
  12910. base.width(bwidth);
  12911. menu.stop().removeAttr('style').css(css);
  12912. fm.toFront(menu);
  12913. menu.show();
  12914. base.attr('style', bstyle);
  12915. css[subpos] = parseInt(menu.width());
  12916. menu.find('.elfinder-contextmenu-sub').css(css);
  12917. if (fm.UA.iOS) {
  12918. $('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'auto');
  12919. }
  12920. selected = null;
  12921. subnodes = null;
  12922. subselected = null;
  12923. $(document).on('keydown.' + fm.namespace, keyEvts);
  12924. evts = $._data(document).events;
  12925. if (evts && evts.keydown) {
  12926. evts.keydown.unshift(evts.keydown.pop());
  12927. }
  12928. fm.UA.Mobile && autoToggle();
  12929. requestAnimationFrame(function() {
  12930. fm.getUI().one('click.' + fm.namespace, close);
  12931. });
  12932. },
  12933. close = function() {
  12934. fm.getUI().off('click.' + fm.namespace, close);
  12935. $(document).off('keydown.' + fm.namespace, keyEvts);
  12936. currentType = currentTargets = null;
  12937. if (menu.is(':visible') || menu.children().length) {
  12938. fm.toHide(menu.removeAttr('style').empty().removeData('submenuKeep'));
  12939. try {
  12940. if (! menu.draggable('instance')) {
  12941. menu.draggable(dragOpt);
  12942. }
  12943. } catch(e) {
  12944. if (! menu.hasClass('ui-draggable')) {
  12945. menu.draggable(dragOpt);
  12946. }
  12947. }
  12948. if (menu.data('prevNode')) {
  12949. menu.data('prevNode').after(menu);
  12950. menu.removeData('prevNode');
  12951. }
  12952. fm.trigger('closecontextmenu');
  12953. if (fm.UA.iOS) {
  12954. $('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'touch');
  12955. }
  12956. }
  12957. autoSyncStop && fm.searchStatus.state < 1 && ! fm.searchStatus.ininc && fm.autoSync();
  12958. autoSyncStop = false;
  12959. },
  12960. create = function(type, targets) {
  12961. var sep = false,
  12962. insSep = false,
  12963. disabled = [],
  12964. isCwd = type === 'cwd',
  12965. selcnt = 0,
  12966. cmdMap;
  12967. currentType = type;
  12968. currentTargets = targets;
  12969. // get current uiCmdMap option
  12970. if (!(cmdMap = fm.option('uiCmdMap', isCwd? void(0) : targets[0]))) {
  12971. cmdMap = {};
  12972. }
  12973. if (!isCwd) {
  12974. disabled = fm.getDisabledCmds(targets);
  12975. }
  12976. selcnt = fm.selected().length;
  12977. if (selcnt > 1) {
  12978. menu.append('<div class="ui-corner-top ui-widget-header elfinder-contextmenu-header"><span>'
  12979. + fm.i18n('selectedItems', ''+selcnt)
  12980. + '</span></div>');
  12981. }
  12982. nodes = $();
  12983. $.each(types[type]||[], function(i, name) {
  12984. var cmd, cmdName, useMap, node, submenu, hover;
  12985. if (name === '|') {
  12986. if (sep) {
  12987. insSep = true;
  12988. }
  12989. return;
  12990. }
  12991. if (cmdMap[name]) {
  12992. cmdName = cmdMap[name];
  12993. useMap = true;
  12994. } else {
  12995. cmdName = name;
  12996. }
  12997. cmd = fm.getCommand(cmdName);
  12998. if (cmd && !isCwd && (!fm.searchStatus.state || !cmd.disableOnSearch)) {
  12999. cmd.__disabled = cmd._disabled;
  13000. cmd._disabled = !(cmd.alwaysEnabled || (fm._commands[cmdName] ? $.inArray(name, disabled) === -1 && (!useMap || !disabled[cmdName]) : false));
  13001. $.each(cmd.linkedCmds, function(i, n) {
  13002. var c;
  13003. if (c = fm.getCommand(n)) {
  13004. c.__disabled = c._disabled;
  13005. c._disabled = !(c.alwaysEnabled || (fm._commands[n] ? !disabled[n] : false));
  13006. }
  13007. });
  13008. }
  13009. if (cmd && !cmd._disabled && cmd.getstate(targets) != -1) {
  13010. if (cmd.variants) {
  13011. if (!cmd.variants.length) {
  13012. return;
  13013. }
  13014. node = item(cmd.title, cmd.className? cmd.className : cmd.name, function(){}, cmd.contextmenuOpts);
  13015. submenu = $('<div class="ui-front ui-corner-all elfinder-contextmenu-sub"/>')
  13016. .hide()
  13017. .css('max-height', fm.getUI().height() - 30)
  13018. .appendTo(node.append('<span class="elfinder-contextmenu-arrow"/>'));
  13019. hover = function(show){
  13020. if (! show) {
  13021. submenu.hide();
  13022. } else {
  13023. var bstyle = base.attr('style');
  13024. base.width(base.width());
  13025. // top: '-1000px' to prevent visible scrollbar of window with the elFinder option `height: '100%'`
  13026. submenu.css({ top: '-1000px', left: 'auto', right: 'auto' });
  13027. var nodeOffset = node.offset(),
  13028. nodeleft = nodeOffset.left,
  13029. nodetop = nodeOffset.top,
  13030. nodewidth = node.outerWidth(),
  13031. width = submenu.outerWidth(true),
  13032. height = submenu.outerHeight(true),
  13033. baseOffset = base.offset(),
  13034. wwidth = baseOffset.left + base.width(),
  13035. wheight = baseOffset.top + base.height(),
  13036. cltr = ltr,
  13037. x = nodewidth,
  13038. y, over;
  13039. if (ltr) {
  13040. over = (nodeleft + nodewidth + width) - wwidth;
  13041. if (over > 10) {
  13042. if (nodeleft > width - 5) {
  13043. x = x - 5;
  13044. cltr = false;
  13045. } else {
  13046. if (!fm.UA.Mobile) {
  13047. x = nodewidth - over;
  13048. }
  13049. }
  13050. }
  13051. } else {
  13052. over = width - nodeleft;
  13053. if (over > 0) {
  13054. if ((nodeleft + nodewidth + width - 15) < wwidth) {
  13055. x = x - 5;
  13056. cltr = true;
  13057. } else {
  13058. if (!fm.UA.Mobile) {
  13059. x = nodewidth - over;
  13060. }
  13061. }
  13062. }
  13063. }
  13064. over = (nodetop + 5 + height) - wheight;
  13065. y = (over > 0 && nodetop < wheight)? 5 - over : (over > 0? 30 - height : 5);
  13066. menu.find('.elfinder-contextmenu-sub:visible').hide();
  13067. submenu.css({
  13068. top : y,
  13069. left : cltr? x : 'auto',
  13070. right: cltr? 'auto' : x,
  13071. overflowY: 'auto'
  13072. }).show();
  13073. base.attr('style', bstyle);
  13074. }
  13075. };
  13076. node.addClass('elfinder-contextmenu-group')
  13077. .on('mouseleave', '.elfinder-contextmenu-sub', function(e) {
  13078. if (! menu.data('draged')) {
  13079. menu.removeData('submenuKeep');
  13080. }
  13081. })
  13082. .on('submenuclose', '.elfinder-contextmenu-sub', function(e) {
  13083. hover(false);
  13084. })
  13085. .on('click', '.'+smItem, function(e){
  13086. var opts, $this;
  13087. e.stopPropagation();
  13088. if (! menu.data('draged')) {
  13089. $this = $(this);
  13090. if (!cmd.keepContextmenu) {
  13091. menu.hide();
  13092. } else {
  13093. $this.removeClass(cHover);
  13094. node.addClass(cHover);
  13095. }
  13096. opts = $this.data('exec');
  13097. if (typeof opts === 'undefined') {
  13098. opts = {};
  13099. }
  13100. if (typeof opts === 'object') {
  13101. opts._userAction = true;
  13102. opts._currentType = type;
  13103. opts._currentNode = $this;
  13104. }
  13105. !cmd.keepContextmenu && close();
  13106. fm.exec(cmd.name, targets, opts);
  13107. }
  13108. })
  13109. .on('touchend', function(e) {
  13110. if (! menu.data('drag')) {
  13111. hover(true);
  13112. menu.data('submenuKeep', true);
  13113. }
  13114. })
  13115. .on('mouseenter mouseleave', function(e){
  13116. if (! menu.data('touching')) {
  13117. if (node.data('timer')) {
  13118. clearTimeout(node.data('timer'));
  13119. node.removeData('timer');
  13120. }
  13121. if (!$(e.target).closest('.elfinder-contextmenu-sub', menu).length) {
  13122. if (e.type === 'mouseleave') {
  13123. if (! menu.data('submenuKeep')) {
  13124. node.data('timer', setTimeout(function() {
  13125. node.removeData('timer');
  13126. hover(false);
  13127. }, 250));
  13128. }
  13129. } else {
  13130. node.data('timer', setTimeout(function() {
  13131. node.removeData('timer');
  13132. hover(true);
  13133. }, nodes.find('div.elfinder-contextmenu-sub:visible').length? 250 : 0));
  13134. }
  13135. }
  13136. }
  13137. });
  13138. $.each(cmd.variants, function(i, variant) {
  13139. var item = variant === '|' ? '<div class="elfinder-contextmenu-separator"/>' :
  13140. $('<div class="'+cmItem+' '+smItem+'"><span>'+variant[1]+'</span></div>').data('exec', variant[0]),
  13141. iconClass, icon;
  13142. if (typeof variant[2] !== 'undefined') {
  13143. icon = $('<span/>').addClass('elfinder-button-icon elfinder-contextmenu-icon');
  13144. if (! /\//.test(variant[2])) {
  13145. icon.addClass('elfinder-button-icon-'+variant[2]);
  13146. } else {
  13147. icon.css(urlIcon(variant[2]));
  13148. }
  13149. item.prepend(icon).addClass(smItem+'-icon');
  13150. }
  13151. submenu.append(item);
  13152. });
  13153. } else {
  13154. node = item(cmd.title, cmd.className? cmd.className : cmd.name, function() {
  13155. if (! menu.data('draged')) {
  13156. !cmd.keepContextmenu && close();
  13157. fm.exec(cmd.name, targets, {_userAction: true, _currentType: type, _currentNode: node});
  13158. }
  13159. }, cmd.contextmenuOpts);
  13160. if (cmd.extra && cmd.extra.node) {
  13161. $('<span class="elfinder-button-icon elfinder-button-icon-'+(cmd.extra.icon || '')+' '+exIcon+'"/>')
  13162. .append(cmd.extra.node).appendTo(node);
  13163. $(cmd.extra.node).trigger('ready', {targets: targets});
  13164. } else {
  13165. node.remove('.'+exIcon);
  13166. }
  13167. }
  13168. if (cmd.extendsCmd) {
  13169. node.children('span.elfinder-button-icon').addClass('elfinder-button-icon-' + cmd.extendsCmd);
  13170. }
  13171. if (insSep) {
  13172. menu.append('<div class="elfinder-contextmenu-separator"/>');
  13173. }
  13174. menu.append(node);
  13175. sep = true;
  13176. insSep = false;
  13177. }
  13178. if (cmd && typeof cmd.__disabled !== 'undefined') {
  13179. cmd._disabled = cmd.__disabled;
  13180. delete cmd.__disabled;
  13181. $.each(cmd.linkedCmds, function(i, n) {
  13182. var c;
  13183. if (c = fm.getCommand(n)) {
  13184. c._disabled = c.__disabled;
  13185. delete c.__disabled;
  13186. }
  13187. });
  13188. }
  13189. });
  13190. nodes = menu.children('div.'+cmItem);
  13191. },
  13192. createFromRaw = function(raw) {
  13193. currentType = 'raw';
  13194. $.each(raw, function(i, data) {
  13195. var node;
  13196. if (data === '|') {
  13197. menu.append('<div class="elfinder-contextmenu-separator"/>');
  13198. } else if (data.label && typeof data.callback == 'function') {
  13199. node = item(data.label, data.icon, function() {
  13200. if (! menu.data('draged')) {
  13201. !data.remain && close();
  13202. data.callback();
  13203. }
  13204. }, data.options || null);
  13205. menu.append(node);
  13206. }
  13207. });
  13208. nodes = menu.children('div.'+cmItem);
  13209. },
  13210. currentType = null,
  13211. currentTargets = null;
  13212. fm.one('load', function() {
  13213. base = fm.getUI();
  13214. cwd = fm.getUI('cwd');
  13215. fm.bind('contextmenu', function(e) {
  13216. var data = e.data,
  13217. css = {},
  13218. prevNode;
  13219. if (data.type && data.type !== 'files') {
  13220. cwd.trigger('unselectall');
  13221. }
  13222. close();
  13223. if (data.type && data.targets) {
  13224. fm.trigger('contextmenucreate', data);
  13225. create(data.type, data.targets);
  13226. fm.trigger('contextmenucreatedone', data);
  13227. } else if (data.raw) {
  13228. createFromRaw(data.raw);
  13229. }
  13230. if (menu.children().length) {
  13231. prevNode = data.prevNode || null;
  13232. if (prevNode) {
  13233. menu.data('prevNode', menu.prev());
  13234. prevNode.after(menu);
  13235. }
  13236. if (data.fitHeight) {
  13237. css = {maxHeight: Math.min(fm.getUI().height(), $(window).height()), overflowY: 'auto'};
  13238. menu.draggable('destroy').removeClass('ui-draggable');
  13239. }
  13240. open(data.x, data.y, css);
  13241. // call opened callback function
  13242. if (data.opened && typeof data.opened === 'function') {
  13243. data.opened.call(menu);
  13244. }
  13245. }
  13246. })
  13247. .one('destroy', function() { menu.remove(); })
  13248. .bind('disable', close)
  13249. .bind('select', function(e){
  13250. (currentType === 'files' && (!e.data || e.data.selected.toString() !== currentTargets.toString())) && close();
  13251. });
  13252. })
  13253. .shortcut({
  13254. pattern : fm.OS === 'mac' ? 'ctrl+m' : 'contextmenu shift+f10',
  13255. description : 'contextmenu',
  13256. callback : function(e) {
  13257. e.stopPropagation();
  13258. e.preventDefault();
  13259. $(document).one('contextmenu.' + fm.namespace, function(e) {
  13260. e.preventDefault();
  13261. e.stopPropagation();
  13262. });
  13263. var sel = fm.selected(),
  13264. type, targets, pos, elm;
  13265. if (sel.length) {
  13266. type = 'files';
  13267. targets = sel;
  13268. elm = fm.cwdHash2Elm(sel[0]);
  13269. } else {
  13270. type = 'cwd';
  13271. targets = [ fm.cwd().hash ];
  13272. pos = fm.getUI('workzone').offset();
  13273. }
  13274. if (! elm || ! elm.length) {
  13275. elm = fm.getUI('workzone');
  13276. }
  13277. pos = elm.offset();
  13278. pos.top += (elm.height() / 2);
  13279. pos.left += (elm.width() / 2);
  13280. fm.trigger('contextmenu', {
  13281. 'type' : type,
  13282. 'targets' : targets,
  13283. 'x' : pos.left,
  13284. 'y' : pos.top
  13285. });
  13286. }
  13287. });
  13288. });
  13289. };
  13290. /*
  13291. * File: /js/ui/cwd.js
  13292. */
  13293. /**
  13294. * elFinder current working directory ui.
  13295. *
  13296. * @author Dmitry (dio) Levashov
  13297. **/
  13298. $.fn.elfindercwd = function(fm, options) {
  13299. this.not('.elfinder-cwd').each(function() {
  13300. // fm.time('cwdLoad');
  13301. var mobile = fm.UA.Mobile,
  13302. list = fm.viewType == 'list',
  13303. undef = 'undefined',
  13304. /**
  13305. * Select event full name
  13306. *
  13307. * @type String
  13308. **/
  13309. evtSelect = 'select.'+fm.namespace,
  13310. /**
  13311. * Unselect event full name
  13312. *
  13313. * @type String
  13314. **/
  13315. evtUnselect = 'unselect.'+fm.namespace,
  13316. /**
  13317. * Disable event full name
  13318. *
  13319. * @type String
  13320. **/
  13321. evtDisable = 'disable.'+fm.namespace,
  13322. /**
  13323. * Disable event full name
  13324. *
  13325. * @type String
  13326. **/
  13327. evtEnable = 'enable.'+fm.namespace,
  13328. c = 'class',
  13329. /**
  13330. * File css class
  13331. *
  13332. * @type String
  13333. **/
  13334. clFile = fm.res(c, 'cwdfile'),
  13335. /**
  13336. * Selected css class
  13337. *
  13338. * @type String
  13339. **/
  13340. fileSelector = '.'+clFile,
  13341. /**
  13342. * Selected css class
  13343. *
  13344. * @type String
  13345. **/
  13346. clSelected = 'ui-selected',
  13347. /**
  13348. * Disabled css class
  13349. *
  13350. * @type String
  13351. **/
  13352. clDisabled = fm.res(c, 'disabled'),
  13353. /**
  13354. * Draggable css class
  13355. *
  13356. * @type String
  13357. **/
  13358. clDraggable = fm.res(c, 'draggable'),
  13359. /**
  13360. * Droppable css class
  13361. *
  13362. * @type String
  13363. **/
  13364. clDroppable = fm.res(c, 'droppable'),
  13365. /**
  13366. * Hover css class
  13367. *
  13368. * @type String
  13369. **/
  13370. clHover = fm.res(c, 'hover'),
  13371. /**
  13372. * Active css class
  13373. *
  13374. * @type String
  13375. **/
  13376. clActive = fm.res(c, 'active'),
  13377. /**
  13378. * Hover css class
  13379. *
  13380. * @type String
  13381. **/
  13382. clDropActive = fm.res(c, 'adroppable'),
  13383. /**
  13384. * Css class for temporary nodes (for mkdir/mkfile) commands
  13385. *
  13386. * @type String
  13387. **/
  13388. clTmp = clFile+'-tmp',
  13389. /**
  13390. * Select checkbox css class
  13391. *
  13392. * @type String
  13393. */
  13394. clSelChk = 'elfinder-cwd-selectchk',
  13395. /**
  13396. * Number of thumbnails to load in one request (new api only)
  13397. *
  13398. * @type Number
  13399. **/
  13400. tmbNum = fm.options.loadTmbs > 0 ? fm.options.loadTmbs : 5,
  13401. /**
  13402. * Current search query.
  13403. *
  13404. * @type String
  13405. */
  13406. query = '',
  13407. /**
  13408. * Currect clipboard(cut) hashes as object key
  13409. *
  13410. * @type Object
  13411. */
  13412. clipCuts = {},
  13413. /**
  13414. * Parents hashes of cwd
  13415. *
  13416. * @type Array
  13417. */
  13418. cwdParents = [],
  13419. /**
  13420. * cwd current hashes
  13421. *
  13422. * @type Array
  13423. */
  13424. cwdHashes = [],
  13425. /**
  13426. * incsearch current hashes
  13427. *
  13428. * @type Array
  13429. */
  13430. incHashes = void 0,
  13431. /**
  13432. * Custom columns name and order
  13433. *
  13434. * @type Array
  13435. */
  13436. customCols = [],
  13437. /**
  13438. * Current clicked element id of first time for dblclick
  13439. *
  13440. * @type String
  13441. */
  13442. curClickId = '',
  13443. /**
  13444. * Custom columns builder
  13445. *
  13446. * @type Function
  13447. */
  13448. customColsBuild = function() {
  13449. var cols = '';
  13450. for (var i = 0; i < customCols.length; i++) {
  13451. cols += '<td class="elfinder-col-'+customCols[i]+'">{' + customCols[i] + '}</td>';
  13452. }
  13453. return cols;
  13454. },
  13455. /**
  13456. * Make template.row from customCols
  13457. *
  13458. * @type Function
  13459. */
  13460. makeTemplateRow = function() {
  13461. 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}/>{marker}<span class="elfinder-cwd-filename">{name}</span></div>'+selectCheckbox+'</td>'+customColsBuild()+'</tr>';
  13462. },
  13463. 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>' : '',
  13464. colResizing = false,
  13465. colWidth = null,
  13466. /**
  13467. * Table header height
  13468. */
  13469. thHeight,
  13470. /**
  13471. * File templates
  13472. *
  13473. * @type Object
  13474. **/
  13475. templates = {
  13476. 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}/>{marker}</div><div class="elfinder-cwd-filename" title="{nametitle}">{name}</div>'+selectCheckbox+'</div>',
  13477. row : ''
  13478. },
  13479. permsTpl = fm.res('tpl', 'perms'),
  13480. lockTpl = fm.res('tpl', 'lock'),
  13481. symlinkTpl = fm.res('tpl', 'symlink'),
  13482. /**
  13483. * Template placeholders replacement rules
  13484. *
  13485. * @type Object
  13486. **/
  13487. replacement = {
  13488. id : function(f) {
  13489. return fm.cwdHash2Id(f.hash);
  13490. },
  13491. name : function(f) {
  13492. var name = fm.escape(f.i18 || f.name);
  13493. !list && (name = name.replace(/([_.])/g, '&#8203;$1'));
  13494. return name;
  13495. },
  13496. nametitle : function(f) {
  13497. return fm.escape(f.i18 || f.name);
  13498. },
  13499. permsclass : function(f) {
  13500. return fm.perms2class(f);
  13501. },
  13502. perm : function(f) {
  13503. return fm.formatPermissions(f);
  13504. },
  13505. dirclass : function(f) {
  13506. var cName = f.mime == 'directory' ? 'directory' : '';
  13507. f.isroot && (cName += ' isroot');
  13508. f.csscls && (cName += ' ' + fm.escape(f.csscls));
  13509. options.getClass && (cName += ' ' + options.getClass(f));
  13510. return cName;
  13511. },
  13512. style : function(f) {
  13513. return f.icon? fm.getIconStyle(f) : '';
  13514. },
  13515. mime : function(f) {
  13516. var cName = fm.mime2class(f.mime);
  13517. f.icon && (cName += ' elfinder-cwd-bgurl');
  13518. return cName;
  13519. },
  13520. size : function(f) {
  13521. return (f.mime === 'directory' && !f.size)? '-' : fm.formatSize(f.size);
  13522. },
  13523. date : function(f) {
  13524. return fm.formatDate(f);
  13525. },
  13526. kind : function(f) {
  13527. return fm.mime2kind(f);
  13528. },
  13529. mode : function(f) {
  13530. return f.perm? fm.formatFileMode(f.perm) : '';
  13531. },
  13532. modestr : function(f) {
  13533. return f.perm? fm.formatFileMode(f.perm, 'string') : '';
  13534. },
  13535. modeoct : function(f) {
  13536. return f.perm? fm.formatFileMode(f.perm, 'octal') : '';
  13537. },
  13538. modeboth : function(f) {
  13539. return f.perm? fm.formatFileMode(f.perm, 'both') : '';
  13540. },
  13541. marker : function(f) {
  13542. return (f.alias || f.mime == 'symlink-broken' ? symlinkTpl : '')+(!f.read || !f.write ? permsTpl : '')+(f.locked ? lockTpl : '');
  13543. },
  13544. tooltip : function(f) {
  13545. var title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''),
  13546. info = '';
  13547. if (query && f.path) {
  13548. info = fm.escape(f.path.replace(/\/[^\/]*$/, ''));
  13549. } else {
  13550. info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, '&#13;') : '';
  13551. }
  13552. if (list) {
  13553. info += (info? '&#13;' : '') + fm.escape(f.i18 || f.name);
  13554. }
  13555. return info? info + '&#13;' + title : title;
  13556. }
  13557. },
  13558. /**
  13559. * Type badge CSS added flag
  13560. *
  13561. * @type Object
  13562. */
  13563. addedBadges = {},
  13564. /**
  13565. * Type badge style sheet element
  13566. *
  13567. * @type Object
  13568. */
  13569. addBadgeStyleSheet,
  13570. /**
  13571. * Add type badge CSS into 'head'
  13572. *
  13573. * @type Fundtion
  13574. */
  13575. addBadgeStyle = function(mime, name) {
  13576. var sel, ext, type;
  13577. if (mime && ! addedBadges[mime]) {
  13578. if (typeof addBadgeStyleSheet === 'undefined') {
  13579. if ($('#elfinderAddBadgeStyle'+fm.namespace).length) {
  13580. $('#elfinderAddBadgeStyle'+fm.namespace).remove();
  13581. }
  13582. addBadgeStyleSheet = $('<style id="addBadgeStyle'+fm.namespace+'"/>').insertBefore($('head').children(':first')).get(0).sheet || null;
  13583. }
  13584. if (addBadgeStyleSheet) {
  13585. mime = mime.toLowerCase();
  13586. type = mime.split('/');
  13587. ext = fm.escape(fm.mimeTypes[mime] || (name.replace(/.bac?k$/i, '').match(/\.([^.]+)$/) || ['',''])[1]);
  13588. if (ext) {
  13589. sel = '.elfinder-cwd-icon-' + type[0].replace(/(\.|\+)/g, '-');
  13590. if (typeof type[1] !== 'undefined') {
  13591. sel += '.elfinder-cwd-icon-' + type[1].replace(/(\.|\+)/g, '-');
  13592. }
  13593. try {
  13594. addBadgeStyleSheet.insertRule(sel + ':before{content:"' + ext.toLowerCase() + '"}', 0);
  13595. } catch(e) {}
  13596. }
  13597. addedBadges[mime] = true;
  13598. }
  13599. }
  13600. },
  13601. /**
  13602. * Return file html
  13603. *
  13604. * @param Object file info
  13605. * @return String
  13606. **/
  13607. itemhtml = function(f) {
  13608. f.mime && f.mime !== 'directory' && !addedBadges[f.mime] && addBadgeStyle(f.mime, f.name);
  13609. return templates[list ? 'row' : 'icon']
  13610. .replace(/\{([a-z0-9_]+)\}/g, function(s, e) {
  13611. return replacement[e] ? replacement[e](f, fm) : (f[e] ? f[e] : '');
  13612. });
  13613. },
  13614. /**
  13615. * jQueery node that will be selected next
  13616. *
  13617. * @type Object jQuery node
  13618. */
  13619. selectedNext = $(),
  13620. /**
  13621. * Flag. Required for msie to avoid unselect files on dragstart
  13622. *
  13623. * @type Boolean
  13624. **/
  13625. selectLock = false,
  13626. /**
  13627. * Move selection to prev/next file
  13628. *
  13629. * @param String move direction
  13630. * @param Boolean append to current selection
  13631. * @return void
  13632. * @rise select
  13633. */
  13634. select = function(keyCode, append) {
  13635. var code = $.ui.keyCode,
  13636. prev = keyCode == code.LEFT || keyCode == code.UP,
  13637. sel = cwd.find('[id].'+clSelected),
  13638. selector = prev ? 'first:' : 'last',
  13639. s, n, sib, top, left;
  13640. function sibling(n, direction) {
  13641. return n[direction+'All']('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):first');
  13642. }
  13643. if (sel.length) {
  13644. s = sel.filter(prev ? ':first' : ':last');
  13645. sib = sibling(s, prev ? 'prev' : 'next');
  13646. if (!sib.length) {
  13647. // there is no sibling on required side - do not move selection
  13648. n = s;
  13649. } else if (list || keyCode == code.LEFT || keyCode == code.RIGHT) {
  13650. // find real prevoius file
  13651. n = sib;
  13652. } else {
  13653. // find up/down side file in icons view
  13654. top = s.position().top;
  13655. left = s.position().left;
  13656. n = s;
  13657. if (prev) {
  13658. do {
  13659. n = n.prev('[id]');
  13660. } while (n.length && !(n.position().top < top && n.position().left <= left));
  13661. if (n.hasClass(clDisabled)) {
  13662. n = sibling(n, 'next');
  13663. }
  13664. } else {
  13665. do {
  13666. n = n.next('[id]');
  13667. } while (n.length && !(n.position().top > top && n.position().left >= left));
  13668. if (n.hasClass(clDisabled)) {
  13669. n = sibling(n, 'prev');
  13670. }
  13671. // there is row before last one - select last file
  13672. if (!n.length) {
  13673. sib = cwd.find('[id]:not(.'+clDisabled+'):last');
  13674. if (sib.position().top > top) {
  13675. n = sib;
  13676. }
  13677. }
  13678. }
  13679. }
  13680. // !append && unselectAll();
  13681. } else {
  13682. if (selectedNext.length) {
  13683. n = prev? selectedNext.prev() : selectedNext;
  13684. } else {
  13685. // there are no selected file - select first/last one
  13686. n = cwd.find('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):'+(prev ? 'last' : 'first'));
  13687. }
  13688. }
  13689. if (n && n.length && !n.hasClass('elfinder-cwd-parent')) {
  13690. if (s && append) {
  13691. // append new files to selected
  13692. n = s.add(s[prev ? 'prevUntil' : 'nextUntil']('#'+n.attr('id'))).add(n);
  13693. } else {
  13694. // unselect selected files
  13695. sel.trigger(evtUnselect);
  13696. }
  13697. // select file(s)
  13698. n.trigger(evtSelect);
  13699. // set its visible
  13700. scrollToView(n.filter(prev ? ':first' : ':last'));
  13701. // update cache/view
  13702. trigger();
  13703. }
  13704. },
  13705. selectedFiles = {},
  13706. selectFile = function(hash) {
  13707. fm.cwdHash2Elm(hash).trigger(evtSelect);
  13708. },
  13709. allSelected = false,
  13710. selectAll = function() {
  13711. var phash = fm.cwd().hash;
  13712. selectCheckbox && selectAllCheckbox.find('input').prop('checked', true);
  13713. fm.lazy(function() {
  13714. var files;
  13715. if (fm.maxTargets && (incHashes || cwdHashes).length > fm.maxTargets) {
  13716. unselectAll({ notrigger: true });
  13717. files = $.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null; });
  13718. files = files.slice(0, fm.maxTargets);
  13719. selectedFiles = {};
  13720. $.each(files, function(i, v) {
  13721. selectedFiles[v.hash] = true;
  13722. fm.cwdHash2Elm(v.hash).trigger(evtSelect);
  13723. });
  13724. fm.toast({mode: 'warning', msg: fm.i18n(['errMaxTargets', fm.maxTargets])});
  13725. } else {
  13726. cwd.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').trigger(evtSelect);
  13727. selectedFiles = fm.arrayFlip(incHashes || cwdHashes, true);
  13728. }
  13729. trigger();
  13730. selectCheckbox && selectAllCheckbox.data('pending', false);
  13731. }, 0, {repaint: true});
  13732. },
  13733. /**
  13734. * Unselect all files
  13735. *
  13736. * @param Object options
  13737. * @return void
  13738. */
  13739. unselectAll = function(opts) {
  13740. var o = opts || {};
  13741. selectCheckbox && selectAllCheckbox.find('input').prop('checked', false);
  13742. if (Object.keys(selectedFiles).length) {
  13743. selectLock = false;
  13744. selectedFiles = {};
  13745. cwd.find('[id].'+clSelected).trigger(evtUnselect);
  13746. selectCheckbox && cwd.find('input:checkbox.'+clSelChk).prop('checked', false);
  13747. }
  13748. !o.notrigger && trigger();
  13749. selectCheckbox && selectAllCheckbox.data('pending', false);
  13750. cwd.removeClass('elfinder-cwd-allselected');
  13751. },
  13752. selectInvert = function() {
  13753. var invHashes = {};
  13754. if (allSelected) {
  13755. unselectAll();
  13756. } else if (! Object.keys(selectedFiles).length) {
  13757. selectAll();
  13758. } else {
  13759. $.each((incHashes || cwdHashes), function(i, h) {
  13760. var itemNode = fm.cwdHash2Elm(h);
  13761. if (! selectedFiles[h]) {
  13762. invHashes[h] = true;
  13763. itemNode.length && itemNode.trigger(evtSelect);
  13764. } else {
  13765. itemNode.length && itemNode.trigger(evtUnselect);
  13766. }
  13767. });
  13768. selectedFiles = invHashes;
  13769. trigger();
  13770. }
  13771. },
  13772. /**
  13773. * Return selected files hashes list
  13774. *
  13775. * @return Array
  13776. */
  13777. selected = function() {
  13778. return Object.keys(selectedFiles);
  13779. },
  13780. /**
  13781. * Last selected node id
  13782. *
  13783. * @type String|Void
  13784. */
  13785. lastSelect = void 0,
  13786. /**
  13787. * Fire elfinder "select" event and pass selected files to it
  13788. *
  13789. * @return void
  13790. */
  13791. trigger = function() {
  13792. var selected = Object.keys(selectedFiles),
  13793. opts = {
  13794. selected : selected,
  13795. origin : 'cwd'
  13796. };
  13797. if (oldSchoolItem && (selected.length > 1 || selected[0] !== fm.cwdId2Hash(
  13798. oldSchoolItem.attr('id'))) && oldSchoolItem.hasClass(clSelected)) {
  13799. oldSchoolItem.trigger(evtUnselect);
  13800. }
  13801. allSelected = selected.length && (selected.length === (incHashes || cwdHashes).length) && (!fm.maxTargets || selected.length <= fm.maxTargets);
  13802. if (selectCheckbox) {
  13803. selectAllCheckbox.find('input').prop('checked', allSelected);
  13804. cwd[allSelected? 'addClass' : 'removeClass']('elfinder-cwd-allselected');
  13805. }
  13806. if (allSelected) {
  13807. opts.selectall = true;
  13808. } else if (! selected.length) {
  13809. opts.unselectall = true;
  13810. }
  13811. fm.trigger('select', opts);
  13812. },
  13813. /**
  13814. * Scroll file to set it visible
  13815. *
  13816. * @param DOMElement file/dir node
  13817. * @return void
  13818. */
  13819. scrollToView = function(o, blink) {
  13820. if (! o.length) {
  13821. return;
  13822. }
  13823. var ftop = o.position().top,
  13824. fheight = o.outerHeight(true),
  13825. wtop = wrapper.scrollTop(),
  13826. wheight = wrapper.get(0).clientHeight,
  13827. thheight = tableHeader? tableHeader.outerHeight(true) : 0;
  13828. if (ftop + thheight + fheight > wtop + wheight) {
  13829. wrapper.scrollTop(parseInt(ftop + thheight + fheight - wheight));
  13830. } else if (ftop < wtop) {
  13831. wrapper.scrollTop(ftop);
  13832. }
  13833. list && wrapper.scrollLeft(0);
  13834. !!blink && fm.resources.blink(o, 'lookme');
  13835. },
  13836. /**
  13837. * Files we get from server but not show yet
  13838. *
  13839. * @type Array
  13840. **/
  13841. buffer = [],
  13842. /**
  13843. * Extra data of buffer
  13844. *
  13845. * @type Object
  13846. **/
  13847. bufferExt = {},
  13848. /**
  13849. * Return index of elements with required hash in buffer
  13850. *
  13851. * @param String file hash
  13852. * @return Number
  13853. */
  13854. index = function(hash) {
  13855. var l = buffer.length;
  13856. while (l--) {
  13857. if (buffer[l].hash == hash) {
  13858. return l;
  13859. }
  13860. }
  13861. return -1;
  13862. },
  13863. /**
  13864. * Scroll start event name
  13865. *
  13866. * @type String
  13867. **/
  13868. scrollStartEvent = 'elfscrstart',
  13869. /**
  13870. * Scroll stop event name
  13871. *
  13872. * @type String
  13873. **/
  13874. scrollEvent = 'elfscrstop',
  13875. scrolling = false,
  13876. /**
  13877. * jQuery UI selectable option
  13878. *
  13879. * @type Object
  13880. */
  13881. selectableOption = {
  13882. disabled : true,
  13883. filter : '[id]:first',
  13884. stop : trigger,
  13885. delay : 250,
  13886. appendTo : 'body',
  13887. autoRefresh: false,
  13888. selected : function(e, ui) { $(ui.selected).trigger(evtSelect); },
  13889. unselected : function(e, ui) { $(ui.unselected).trigger(evtUnselect); }
  13890. },
  13891. /**
  13892. * hashes of items displayed in current view
  13893. *
  13894. * @type Object ItemHash => DomId
  13895. */
  13896. inViewHashes = {},
  13897. /**
  13898. * Processing when the current view is changed (On open, search, scroll, resize etc.)
  13899. *
  13900. * @return void
  13901. */
  13902. wrapperRepaint = function(init, recnt) {
  13903. if (!bufferExt.renderd) {
  13904. return;
  13905. }
  13906. var firstNode = (list? cwd.find('tbody:first') : cwd).children('[id]'+(options.oldSchool? ':not(.elfinder-cwd-parent)' : '')+':first');
  13907. if (!firstNode.length) {
  13908. return;
  13909. }
  13910. var selectable = cwd.data('selectable'),
  13911. rec = (function() {
  13912. var wos = wrapper.offset(),
  13913. ww = wrapper.width(),
  13914. w = $(window),
  13915. x = firstNode.width() / 2,
  13916. l = Math.min(wos.left - w.scrollLeft() + (fm.direction === 'ltr'? x : ww - x), wos.left + ww - 10),
  13917. t = wos.top - w.scrollTop() + 10 + (list? thHeight : 0);
  13918. return {left: Math.max(0, Math.round(l)), top: Math.max(0, Math.round(t))};
  13919. })(),
  13920. tgt = init? firstNode : $(document.elementFromPoint(rec.left , rec.top)),
  13921. ids = {},
  13922. tmbs = {},
  13923. multi = 5,
  13924. cnt = Math.ceil((bufferExt.hpi? Math.ceil((wz.data('rectangle').height / bufferExt.hpi) * 1.5) : showFiles) / multi),
  13925. chk = function() {
  13926. var id, hash, file, i;
  13927. for (i = 0; i < multi; i++) {
  13928. id = tgt.attr('id');
  13929. if (id) {
  13930. bufferExt.getTmbs = [];
  13931. hash = fm.cwdId2Hash(id);
  13932. inViewHashes[hash] = id;
  13933. // for tmbs
  13934. if (bufferExt.attachTmbs[hash]) {
  13935. tmbs[hash] = bufferExt.attachTmbs[hash];
  13936. }
  13937. // for selectable
  13938. selectable && (ids[id] = true);
  13939. }
  13940. // next node
  13941. tgt = tgt.next();
  13942. if (!tgt.length) {
  13943. break;
  13944. }
  13945. }
  13946. },
  13947. done = function() {
  13948. var idsArr;
  13949. if (cwd.data('selectable')) {
  13950. Object.assign(ids, selectedFiles);
  13951. idsArr = Object.keys(ids);
  13952. if (idsArr.length) {
  13953. selectableOption.filter = '#'+idsArr.join(', #');
  13954. cwd.selectable('enable').selectable('option', {filter : selectableOption.filter}).selectable('refresh');
  13955. }
  13956. }
  13957. if (Object.keys(tmbs).length) {
  13958. bufferExt.getTmbs = [];
  13959. attachThumbnails(tmbs);
  13960. }
  13961. },
  13962. setTarget = function() {
  13963. if (!tgt.hasClass(clFile)) {
  13964. tgt = tgt.closest(fileSelector);
  13965. }
  13966. },
  13967. arr, widget;
  13968. inViewHashes = {};
  13969. selectable && cwd.selectable('option', 'disabled');
  13970. if (tgt.length) {
  13971. if (!tgt.hasClass(clFile) && !tgt.closest(fileSelector).length) {
  13972. // dialog, serach button etc.
  13973. widget = fm.getUI().find('.ui-dialog:visible,.ui-widget:visible');
  13974. if (widget.length) {
  13975. widget.hide();
  13976. tgt = $(document.elementFromPoint(rec.left , rec.top));
  13977. widget.show();
  13978. } else {
  13979. widget = null;
  13980. }
  13981. }
  13982. setTarget();
  13983. if (!tgt.length) {
  13984. // try search 5px down
  13985. widget && widget.hide();
  13986. tgt = $(document.elementFromPoint(rec.left , rec.top + 5));
  13987. widget && widget.show();
  13988. setTarget();
  13989. }
  13990. }
  13991. if (tgt.length) {
  13992. if (tgt.attr('id')) {
  13993. if (init) {
  13994. for (var i = 0; i < cnt; i++) {
  13995. chk();
  13996. if (! tgt.length) {
  13997. break;
  13998. }
  13999. }
  14000. done();
  14001. } else {
  14002. bufferExt.repaintJob && bufferExt.repaintJob.state() === 'pending' && bufferExt.repaintJob.reject();
  14003. arr = new Array(cnt);
  14004. bufferExt.repaintJob = fm.asyncJob(function() {
  14005. chk();
  14006. if (! tgt.length) {
  14007. done();
  14008. bufferExt.repaintJob && bufferExt.repaintJob.state() === 'pending' && bufferExt.repaintJob.reject();
  14009. }
  14010. }, arr).done(done);
  14011. }
  14012. }
  14013. } else if (init && bufferExt.renderd) {
  14014. // In initial request, cwd DOM not renderd so doing lazy check
  14015. recnt = recnt || 0;
  14016. if (recnt < 10) { // Prevent infinite loop
  14017. requestAnimationFrame(function() {
  14018. wrapperRepaint(init, ++recnt);
  14019. });
  14020. }
  14021. }
  14022. },
  14023. /**
  14024. * Item node of oldScholl ".."
  14025. */
  14026. oldSchoolItem = null,
  14027. /**
  14028. * display parent folder with ".." name
  14029. *
  14030. * @param String phash
  14031. * @return void
  14032. */
  14033. oldSchool = function(p) {
  14034. var phash = fm.cwd().phash,
  14035. pdir = fm.file(phash) || null,
  14036. set = function(pdir) {
  14037. if (pdir) {
  14038. oldSchoolItem = $(itemhtml($.extend(true, {}, pdir, {name : '..', i18 : '..', mime : 'directory'})))
  14039. .addClass('elfinder-cwd-parent')
  14040. .on('dblclick', function() {
  14041. var hash = fm.cwdId2Hash(this.id);
  14042. fm.trigger('select', {selected : [hash]}).exec('open', hash);
  14043. });
  14044. (list ? oldSchoolItem.children('td:first') : oldSchoolItem).children('.elfinder-cwd-select').remove();
  14045. (list ? cwd.find('tbody') : cwd).prepend(oldSchoolItem);
  14046. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  14047. }
  14048. };
  14049. if (pdir) {
  14050. set(pdir);
  14051. } else {
  14052. if (fm.getUI('tree').length) {
  14053. fm.one('parents', function() {
  14054. set(fm.file(phash) || null);
  14055. wrapper.trigger(scrollEvent);
  14056. });
  14057. } else {
  14058. fm.request({
  14059. data : {cmd : 'parents', target : fm.cwd().hash},
  14060. preventFail : true
  14061. })
  14062. .done(function(data) {
  14063. set(fm.file(phash) || null);
  14064. wrapper.trigger(scrollEvent);
  14065. });
  14066. }
  14067. }
  14068. },
  14069. showFiles = fm.options.showFiles,
  14070. /**
  14071. * Cwd scroll event handler.
  14072. * Lazy load - append to cwd not shown files
  14073. *
  14074. * @return void
  14075. */
  14076. render = function() {
  14077. if (bufferExt.rendering || (bufferExt.renderd && ! buffer.length)) {
  14078. return;
  14079. }
  14080. var place = (list ? cwd.children('table').children('tbody') : cwd),
  14081. phash,
  14082. chk,
  14083. // created document fragment for jQuery >= 1.12, 2.2, 3.0
  14084. // see Studio-42/elFinder#1544 @ github
  14085. docFlag = $.htmlPrefilter? true : false,
  14086. tempDom = docFlag? $(document.createDocumentFragment()) : $('<div/>'),
  14087. go = function(o){
  14088. var over = o || null,
  14089. html = [],
  14090. dirs = false,
  14091. atmb = {},
  14092. stmb = (fm.option('tmbUrl') === 'self'),
  14093. init = bufferExt.renderd? false : true,
  14094. files, locks, selected;
  14095. files = buffer.splice(0, showFiles + (over || 0) / (bufferExt.hpi || 1));
  14096. bufferExt.renderd += files.length;
  14097. if (! buffer.length) {
  14098. bottomMarker.hide();
  14099. wrapper.off(scrollEvent, render);
  14100. }
  14101. locks = [];
  14102. html = $.map(files, function(f) {
  14103. if (f.hash && f.name) {
  14104. if (f.mime == 'directory') {
  14105. dirs = true;
  14106. }
  14107. if ((f.tmb && (f.tmb != 1 || f.size > 0)) || (stmb && f.mime.indexOf('image/') === 0)) {
  14108. atmb[f.hash] = f.tmb || 'self';
  14109. }
  14110. clipCuts[f.hash] && locks.push(f.hash);
  14111. return itemhtml(f);
  14112. }
  14113. return null;
  14114. });
  14115. // html into temp node
  14116. tempDom.empty().append(html.join(''));
  14117. // make directory droppable
  14118. dirs && !mobile && makeDroppable(tempDom);
  14119. // check selected items
  14120. selected = [];
  14121. if (Object.keys(selectedFiles).length) {
  14122. tempDom.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').each(function() {
  14123. selectedFiles[fm.cwdId2Hash(this.id)] && selected.push($(this));
  14124. });
  14125. }
  14126. // append to cwd
  14127. place.append(docFlag? tempDom : tempDom.children());
  14128. // trigger select
  14129. if (selected.length) {
  14130. $.each(selected, function(i, n) { n.trigger(evtSelect); });
  14131. trigger();
  14132. }
  14133. locks.length && fm.trigger('lockfiles', {files: locks});
  14134. !bufferExt.hpi && bottomMarkerShow(place, files.length);
  14135. if (list) {
  14136. // show thead
  14137. cwd.find('thead').show();
  14138. // fixed table header
  14139. fixTableHeader({fitWidth: ! colWidth});
  14140. }
  14141. if (Object.keys(atmb).length) {
  14142. Object.assign(bufferExt.attachTmbs, atmb);
  14143. }
  14144. if (init) {
  14145. if (! mobile && ! cwd.data('selectable')) {
  14146. // make files selectable
  14147. cwd.selectable(selectableOption).data('selectable', true);
  14148. }
  14149. }
  14150. ! scrolling && wrapper.trigger(scrollEvent);
  14151. };
  14152. if (! bufferExt.renderd) {
  14153. // first time to go()
  14154. bufferExt.rendering = true;
  14155. // scroll top on dir load to avoid scroll after page reload
  14156. wrapper.scrollTop(0);
  14157. phash = fm.cwd().phash;
  14158. go();
  14159. if (options.oldSchool) {
  14160. if (phash && !query) {
  14161. oldSchool(phash);
  14162. } else {
  14163. oldSchoolItem = $();
  14164. }
  14165. }
  14166. if (list) {
  14167. colWidth && setColwidth();
  14168. fixTableHeader({fitWidth: true});
  14169. }
  14170. bufferExt.itemH = (list? place.find('tr:first') : place.find('[id]:first')).outerHeight(true);
  14171. fm.trigger('cwdrender');
  14172. bufferExt.rendering = false;
  14173. wrapperRepaint(true);
  14174. }
  14175. if (! bufferExt.rendering && buffer.length) {
  14176. // next go()
  14177. if ((chk = (wrapper.height() + wrapper.scrollTop() + fm.options.showThreshold + bufferExt.row) - (bufferExt.renderd * bufferExt.hpi)) > 0) {
  14178. bufferExt.rendering = true;
  14179. fm.lazy(function() {
  14180. go(chk);
  14181. bufferExt.rendering = false;
  14182. });
  14183. } else {
  14184. !fm.enabled() && resize();
  14185. }
  14186. } else {
  14187. resize();
  14188. }
  14189. },
  14190. // fixed table header jQuery object
  14191. tableHeader = null,
  14192. // Is UA support CSS sticky
  14193. cssSticky = fm.UA.CSS.positionSticky && fm.UA.CSS.widthMaxContent,
  14194. // To fixed table header colmun
  14195. fixTableHeader = function(optsArg) {
  14196. thHeight = 0;
  14197. if (! options.listView.fixedHeader) {
  14198. return;
  14199. }
  14200. var setPos = function() {
  14201. var val, pos;
  14202. pos = (fm.direction === 'ltr')? 'left' : 'right';
  14203. val = ((fm.direction === 'ltr')? wrapper.scrollLeft() : table.outerWidth(true) - wrapper.width() - wrapper.scrollLeft()) * -1;
  14204. if (base.css(pos) !== val) {
  14205. base.css(pos, val);
  14206. }
  14207. },
  14208. opts = optsArg || {},
  14209. cnt, base, table, htable, thead, tbody, hheight, htr, btr, htd, btd, htw, btw, init;
  14210. tbody = cwd.find('tbody');
  14211. btr = tbody.children('tr:first');
  14212. if (btr.length && btr.is(':visible')) {
  14213. table = tbody.parent();
  14214. if (! tableHeader) {
  14215. init = true;
  14216. tbody.addClass('elfinder-cwd-fixheader');
  14217. thead = cwd.find('thead').attr('id', fm.namespace+'-cwd-thead');
  14218. htr = thead.children('tr:first');
  14219. hheight = htr.outerHeight(true);
  14220. cwd.css('margin-top', hheight - parseInt(table.css('padding-top')));
  14221. if (cssSticky) {
  14222. tableHeader = $('<div class="elfinder-table-header-sticky"/>').addClass(cwd.attr('class')).append($('<table/>').append(thead));
  14223. cwd.after(tableHeader);
  14224. wrapper.on('resize.fixheader', function(e) {
  14225. e.stopPropagation();
  14226. fixTableHeader({fitWidth: true});
  14227. });
  14228. } else {
  14229. base = $('<div/>').addClass(cwd.attr('class')).append($('<table/>').append(thead));
  14230. tableHeader = $('<div/>').addClass(wrapper.attr('class') + ' elfinder-cwd-fixheader')
  14231. .removeClass('ui-droppable native-droppable')
  14232. .css(wrapper.position())
  14233. .css({ height: hheight, width: cwd.outerWidth() })
  14234. .append(base);
  14235. if (fm.direction === 'rtl') {
  14236. tableHeader.css('left', (wrapper.data('width') - wrapper.width()) + 'px');
  14237. }
  14238. setPos();
  14239. wrapper.after(tableHeader)
  14240. .on('scroll.fixheader resize.fixheader', function(e) {
  14241. setPos();
  14242. if (e.type === 'resize') {
  14243. e.stopPropagation();
  14244. tableHeader.css(wrapper.position());
  14245. wrapper.data('width', wrapper.css('overflow', 'hidden').width());
  14246. wrapper.css('overflow', 'auto');
  14247. fixTableHeader();
  14248. }
  14249. });
  14250. }
  14251. } else {
  14252. thead = $('#'+fm.namespace+'-cwd-thead');
  14253. htr = thead.children('tr:first');
  14254. }
  14255. if (init || opts.fitWidth || Math.abs(btr.outerWidth() - htr.outerWidth()) > 2) {
  14256. cnt = customCols.length + 1;
  14257. for (var i = 0; i < cnt; i++) {
  14258. htd = htr.children('td:eq('+i+')');
  14259. btd = btr.children('td:eq('+i+')');
  14260. htw = htd.width();
  14261. btw = btd.width();
  14262. if (typeof htd.data('delta') === 'undefined') {
  14263. htd.data('delta', (htd.outerWidth() - htw) - (btd.outerWidth() - btw));
  14264. }
  14265. btw -= htd.data('delta');
  14266. if (! init && ! opts.fitWidth && htw === btw) {
  14267. break;
  14268. }
  14269. htd.css('width', btw + 'px');
  14270. }
  14271. }
  14272. if (!cssSticky) {
  14273. tableHeader.data('widthTimer') && cancelAnimationFrame(tableHeader.data('widthTimer'));
  14274. tableHeader.data('widthTimer', requestAnimationFrame(function() {
  14275. if (tableHeader) {
  14276. tableHeader.css('width', mBoard.width() + 'px');
  14277. if (fm.direction === 'rtl') {
  14278. tableHeader.css('left', (wrapper.data('width') - wrapper.width()) + 'px');
  14279. }
  14280. }
  14281. }));
  14282. }
  14283. thHeight = thead.height();
  14284. }
  14285. },
  14286. // Set colmun width
  14287. setColwidth = function() {
  14288. if (list && colWidth) {
  14289. var cl = 'elfinder-cwd-colwidth',
  14290. first = cwd.find('tr[id]:first'),
  14291. former;
  14292. if (! first.hasClass(cl)) {
  14293. former = cwd.find('tr.'+cl);
  14294. former.removeClass(cl).find('td').css('width', '');
  14295. first.addClass(cl);
  14296. cwd.find('table:first').css('table-layout', 'fixed');
  14297. $.each($.merge(['name'], customCols), function(i, k) {
  14298. var w = colWidth[k] || first.find('td.elfinder-col-'+k).width();
  14299. first.find('td.elfinder-col-'+k).width(w);
  14300. });
  14301. }
  14302. }
  14303. },
  14304. /**
  14305. * Droppable options for cwd.
  14306. * Drop target is `wrapper`
  14307. * Do not add class on childs file over
  14308. *
  14309. * @type Object
  14310. */
  14311. droppable = Object.assign({}, fm.droppable, {
  14312. over : function(e, ui) {
  14313. var dst = $(this),
  14314. helper = ui.helper,
  14315. ctr = (e.shiftKey || e.ctrlKey || e.metaKey),
  14316. hash, status, inParent;
  14317. e.stopPropagation();
  14318. helper.data('dropover', helper.data('dropover') + 1);
  14319. dst.data('dropover', true);
  14320. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  14321. if (helper.data('namespace') !== fm.namespace || ! fm.insideWorkzone(e.pageX, e.pageY)) {
  14322. dst.removeClass(clDropActive);
  14323. //helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  14324. return;
  14325. }
  14326. if (dst.hasClass(fm.res(c, 'cwdfile'))) {
  14327. hash = fm.cwdId2Hash(dst.attr('id'));
  14328. dst.data('dropover', hash);
  14329. } else {
  14330. hash = fm.cwd().hash;
  14331. fm.cwd().write && dst.data('dropover', hash);
  14332. }
  14333. inParent = (fm.file(helper.data('files')[0]).phash === hash);
  14334. if (dst.data('dropover') === hash) {
  14335. $.each(helper.data('files'), function(i, h) {
  14336. if (h === hash || (inParent && !ctr && !helper.hasClass('elfinder-drag-helper-plus'))) {
  14337. dst.removeClass(clDropActive);
  14338. return false; // break $.each
  14339. }
  14340. });
  14341. } else {
  14342. dst.removeClass(clDropActive);
  14343. }
  14344. if (helper.data('locked') || inParent) {
  14345. status = 'elfinder-drag-helper-plus';
  14346. } else {
  14347. status = 'elfinder-drag-helper-move';
  14348. if (ctr) {
  14349. status += ' elfinder-drag-helper-plus';
  14350. }
  14351. }
  14352. dst.hasClass(clDropActive) && helper.addClass(status);
  14353. requestAnimationFrame(function(){ dst.hasClass(clDropActive) && helper.addClass(status); });
  14354. },
  14355. out : function(e, ui) {
  14356. var helper = ui.helper;
  14357. e.stopPropagation();
  14358. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  14359. $(this).removeData('dropover')
  14360. .removeClass(clDropActive);
  14361. },
  14362. deactivate : function() {
  14363. $(this).removeData('dropover')
  14364. .removeClass(clDropActive);
  14365. },
  14366. drop : function(e, ui) {
  14367. unselectAll({ notrigger: true });
  14368. fm.droppable.drop.call(this, e, ui);
  14369. }
  14370. }),
  14371. /**
  14372. * Make directory droppable
  14373. *
  14374. * @return void
  14375. */
  14376. makeDroppable = function(place) {
  14377. place = place? place : (list ? cwd.find('tbody') : cwd);
  14378. var targets = place.children('.directory:not(.'+clDroppable+',.elfinder-na,.elfinder-ro)');
  14379. if (fm.isCommandEnabled('paste')) {
  14380. targets.droppable(droppable);
  14381. }
  14382. if (fm.isCommandEnabled('upload')) {
  14383. targets.addClass('native-droppable');
  14384. }
  14385. place.children('.isroot').each(function(i, n) {
  14386. var $n = $(n),
  14387. hash = fm.cwdId2Hash(n.id);
  14388. if (fm.isCommandEnabled('paste', hash)) {
  14389. if (! $n.hasClass(clDroppable+',elfinder-na,elfinder-ro')) {
  14390. $n.droppable(droppable);
  14391. }
  14392. } else {
  14393. if ($n.hasClass(clDroppable)) {
  14394. $n.droppable('destroy');
  14395. }
  14396. }
  14397. if (fm.isCommandEnabled('upload', hash)) {
  14398. if (! $n.hasClass('native-droppable,elfinder-na,elfinder-ro')) {
  14399. $n.addClass('native-droppable');
  14400. }
  14401. } else {
  14402. if ($n.hasClass('native-droppable')) {
  14403. $n.removeClass('native-droppable');
  14404. }
  14405. }
  14406. });
  14407. },
  14408. /**
  14409. * Preload required thumbnails and on load add css to files.
  14410. * Return false if required file is not visible yet (in buffer) -
  14411. * required for old api to stop loading thumbnails.
  14412. *
  14413. * @param Object file hash -> thumbnail map
  14414. * @param Bool reload
  14415. * @return void
  14416. */
  14417. attachThumbnails = function(tmbs, reload) {
  14418. var attach = function(node, tmb) {
  14419. $('<img/>')
  14420. .on('load', function() {
  14421. node.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')");
  14422. })
  14423. .attr('src', tmb.url);
  14424. },
  14425. chk = function(hash, tmb) {
  14426. var node = fm.cwdHash2Elm(hash),
  14427. file, tmbObj, reloads = [];
  14428. if (node.length) {
  14429. if (tmb != '1') {
  14430. file = fm.file(hash);
  14431. if (file.tmb !== tmb) {
  14432. file.tmb = tmb;
  14433. }
  14434. tmbObj = fm.tmb(file);
  14435. if (reload) {
  14436. node.find('.elfinder-cwd-icon').addClass(tmbObj.className).css('background-image', "url('"+tmbObj.url+"')");
  14437. } else {
  14438. attach(node, tmbObj);
  14439. }
  14440. delete bufferExt.attachTmbs[hash];
  14441. } else {
  14442. if (reload) {
  14443. loadThumbnails([hash]);
  14444. } else if (! bufferExt.tmbLoading[hash]) {
  14445. bufferExt.getTmbs.push(hash);
  14446. }
  14447. }
  14448. }
  14449. };
  14450. if ($.isPlainObject(tmbs) && Object.keys(tmbs).length) {
  14451. Object.assign(bufferExt.attachTmbs, tmbs);
  14452. $.each(tmbs, chk);
  14453. if (! reload && bufferExt.getTmbs.length && ! Object.keys(bufferExt.tmbLoading).length) {
  14454. loadThumbnails();
  14455. }
  14456. }
  14457. },
  14458. /**
  14459. * Load thumbnails from backend.
  14460. *
  14461. * @param Array|void reloads hashes list for reload thumbnail items
  14462. * @return void
  14463. */
  14464. loadThumbnails = function(reloads) {
  14465. var tmbs = [],
  14466. reload = false;
  14467. if (fm.oldAPI) {
  14468. fm.request({
  14469. data : {cmd : 'tmb', current : fm.cwd().hash},
  14470. preventFail : true
  14471. })
  14472. .done(function(data) {
  14473. if (data.images && Object.keys(data.images).length) {
  14474. attachThumbnails(data.images);
  14475. }
  14476. if (data.tmb) {
  14477. loadThumbnails();
  14478. }
  14479. });
  14480. return;
  14481. }
  14482. if (reloads) {
  14483. reload = true;
  14484. tmbs = reloads.splice(0, tmbNum);
  14485. } else {
  14486. tmbs = bufferExt.getTmbs.splice(0, tmbNum);
  14487. }
  14488. if (tmbs.length) {
  14489. if (reload || inViewHashes[tmbs[0]] || inViewHashes[tmbs[tmbs.length-1]]) {
  14490. $.each(tmbs, function(i, h) {
  14491. bufferExt.tmbLoading[h] = true;
  14492. });
  14493. fm.request({
  14494. data : {cmd : 'tmb', targets : tmbs},
  14495. preventFail : true
  14496. })
  14497. .done(function(data) {
  14498. var errs = [],
  14499. resLen;
  14500. if (data.images) {
  14501. if (resLen = Object.keys(data.images).length) {
  14502. if (resLen < tmbs.length) {
  14503. $.each(tmbs, function(i, h) {
  14504. if (! data.images[h]) {
  14505. errs.push(h);
  14506. }
  14507. });
  14508. }
  14509. attachThumbnails(data.images, reload);
  14510. } else {
  14511. errs = tmbs;
  14512. }
  14513. // unset error items from bufferExt.attachTmbs
  14514. if (errs.length) {
  14515. $.each(errs, function(i, h) {
  14516. delete bufferExt.attachTmbs[h];
  14517. });
  14518. }
  14519. }
  14520. if (reload) {
  14521. if (reloads.length) {
  14522. loadThumbnails(reloads);
  14523. }
  14524. }
  14525. })
  14526. .always(function() {
  14527. bufferExt.tmbLoading = {};
  14528. if (! reload && bufferExt.getTmbs.length) {
  14529. loadThumbnails();
  14530. }
  14531. });
  14532. }
  14533. }
  14534. },
  14535. /**
  14536. * Add new files to cwd/buffer
  14537. *
  14538. * @param Array new files
  14539. * @return void
  14540. */
  14541. add = function(files, mode) {
  14542. var place = list ? cwd.find('tbody') : cwd,
  14543. l = files.length,
  14544. atmb = {},
  14545. findNode = function(file) {
  14546. var pointer = cwd.find('[id]:first'), file2;
  14547. while (pointer.length) {
  14548. file2 = fm.file(fm.cwdId2Hash(pointer.attr('id')));
  14549. if (!pointer.hasClass('elfinder-cwd-parent') && file2 && fm.compare(file, file2) < 0) {
  14550. return pointer;
  14551. }
  14552. pointer = pointer.next('[id]');
  14553. }
  14554. },
  14555. findIndex = function(file) {
  14556. var l = buffer.length, i;
  14557. for (i =0; i < l; i++) {
  14558. if (fm.compare(file, buffer[i]) < 0) {
  14559. return i;
  14560. }
  14561. }
  14562. return l || -1;
  14563. },
  14564. // created document fragment for jQuery >= 1.12, 2.2, 3.0
  14565. // see Studio-42/elFinder#1544 @ github
  14566. docFlag = $.htmlPrefilter? true : false,
  14567. tempDom = docFlag? $(document.createDocumentFragment()) : $('<div/>'),
  14568. file, hash, node, nodes, ndx, stmb;
  14569. if (l > showFiles) {
  14570. // re-render for performance tune
  14571. content();
  14572. selectedFiles = fm.arrayFlip($.map(files, function(f) { return f.hash; }), true);
  14573. trigger();
  14574. } else {
  14575. // add the item immediately
  14576. l && wz.removeClass('elfinder-cwd-wrapper-empty');
  14577. // Self thumbnail
  14578. stmb = (fm.option('tmbUrl') === 'self');
  14579. while (l--) {
  14580. file = files[l];
  14581. hash = file.hash;
  14582. if (fm.cwdHash2Elm(hash).length) {
  14583. continue;
  14584. }
  14585. if ((node = findNode(file)) && ! node.length) {
  14586. node = null;
  14587. }
  14588. if (! node && (ndx = findIndex(file)) >= 0) {
  14589. buffer.splice(ndx, 0, file);
  14590. } else {
  14591. tempDom.empty().append(itemhtml(file));
  14592. (file.mime === 'directory') && !mobile && makeDroppable(tempDom);
  14593. nodes = docFlag? tempDom : tempDom.children();
  14594. if (node) {
  14595. node.before(nodes);
  14596. } else {
  14597. place.append(nodes);
  14598. }
  14599. ++bufferExt.renderd;
  14600. }
  14601. if (fm.cwdHash2Elm(hash).length) {
  14602. if ((file.tmb && (file.tmb != 1 || file.size > 0)) || (stmb && file.mime.indexOf('image/') === 0)) {
  14603. atmb[hash] = file.tmb || 'self';
  14604. }
  14605. }
  14606. }
  14607. if (list) {
  14608. setColwidth();
  14609. fixTableHeader({fitWidth: ! colWidth});
  14610. }
  14611. bottomMarkerShow(place);
  14612. if (Object.keys(atmb).length) {
  14613. Object.assign(bufferExt.attachTmbs, atmb);
  14614. }
  14615. }
  14616. },
  14617. /**
  14618. * Remove files from cwd/buffer
  14619. *
  14620. * @param Array files hashes
  14621. * @return void
  14622. */
  14623. remove = function(files) {
  14624. var l = files.length,
  14625. inSearch = fm.searchStatus.state > 1,
  14626. curCmd = fm.getCommand(fm.currentReqCmd) || {},
  14627. hash, n, ndx, found;
  14628. // removed cwd
  14629. if (!fm.cwd().hash && !curCmd.noChangeDirOnRemovedCwd) {
  14630. $.each(cwdParents.reverse(), function(i, h) {
  14631. if (fm.file(h)) {
  14632. found = true;
  14633. fm.one(fm.currentReqCmd + 'done', function() {
  14634. !fm.cwd().hash && fm.exec('open', h);
  14635. });
  14636. return false;
  14637. }
  14638. });
  14639. // fallback to fm.roots[0]
  14640. !found && !fm.cwd().hash && fm.exec('open', fm.roots[Object.keys(fm.roots)[0]]);
  14641. return;
  14642. }
  14643. while (l--) {
  14644. hash = files[l];
  14645. if ((n = fm.cwdHash2Elm(hash)).length) {
  14646. try {
  14647. n.remove();
  14648. --bufferExt.renderd;
  14649. } catch(e) {
  14650. fm.debug('error', e);
  14651. }
  14652. } else if ((ndx = index(hash)) !== -1) {
  14653. buffer.splice(ndx, 1);
  14654. }
  14655. selectedFiles[hash] && delete selectedFiles[hash];
  14656. if (inSearch) {
  14657. if ((ndx = $.inArray(hash, cwdHashes)) !== -1) {
  14658. cwdHashes.splice(ndx, 1);
  14659. }
  14660. }
  14661. }
  14662. inSearch && fm.trigger('cwdhasheschange', cwdHashes);
  14663. if (list) {
  14664. setColwidth();
  14665. fixTableHeader({fitWidth: ! colWidth});
  14666. }
  14667. },
  14668. customColsNameBuild = function() {
  14669. var name = '',
  14670. customColsName = '';
  14671. for (var i = 0; i < customCols.length; i++) {
  14672. name = fm.getColumnName(customCols[i]);
  14673. customColsName +='<td class="elfinder-cwd-view-th-'+customCols[i]+' sortable-item">'+name+'</td>';
  14674. }
  14675. return customColsName;
  14676. },
  14677. setItemBoxSize = function(boxSize) {
  14678. var place, elm;
  14679. if (!boxSize.height) {
  14680. place = (list ? cwd.find('tbody') : cwd);
  14681. elm = place.find(list? 'tr:first' : '[id]:first');
  14682. boxSize.height = elm.outerHeight(true);
  14683. if (!list) {
  14684. boxSize.width = elm.outerWidth(true);
  14685. }
  14686. }
  14687. },
  14688. bottomMarkerShow = function(cur, cnt) {
  14689. var place = cur || (list ? cwd.find('tbody') : cwd),
  14690. boxSize = itemBoxSize[fm.viewType],
  14691. col = 1,
  14692. row;
  14693. if (buffer.length > 0) {
  14694. if (!bufferExt.hpi) {
  14695. setItemBoxSize(boxSize);
  14696. if (! list) {
  14697. col = Math.floor(place.width() / boxSize.width);
  14698. bufferExt.row = boxSize.height;
  14699. bufferExt.hpi = bufferExt.row / col;
  14700. } else {
  14701. bufferExt.row = bufferExt.hpi = boxSize.height;
  14702. }
  14703. } else if (!list) {
  14704. col = Math.floor(place.width() / boxSize.width);
  14705. }
  14706. row = Math.ceil((buffer.length + (cnt || 0)) / col);
  14707. if (list && tableHeader) {
  14708. ++row;
  14709. }
  14710. bottomMarker.css({top: (bufferExt.row * row) + 'px'}).show();
  14711. }
  14712. },
  14713. wrapperContextMenu = {
  14714. contextmenu : function(e) {
  14715. e.preventDefault();
  14716. if (cwd.data('longtap') !== void(0)) {
  14717. e.stopPropagation();
  14718. return;
  14719. }
  14720. fm.trigger('contextmenu', {
  14721. 'type' : 'cwd',
  14722. 'targets' : [fm.cwd().hash],
  14723. 'x' : e.pageX,
  14724. 'y' : e.pageY
  14725. });
  14726. },
  14727. touchstart : function(e) {
  14728. if (e.originalEvent.touches.length > 1) {
  14729. return;
  14730. }
  14731. if (cwd.data('longtap') !== false) {
  14732. wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY});
  14733. cwd.data('tmlongtap', setTimeout(function(){
  14734. // long tap
  14735. cwd.data('longtap', true);
  14736. fm.trigger('contextmenu', {
  14737. 'type' : 'cwd',
  14738. 'targets' : [fm.cwd().hash],
  14739. 'x' : wrapper.data('touching').x,
  14740. 'y' : wrapper.data('touching').y
  14741. });
  14742. }, 500));
  14743. }
  14744. cwd.data('longtap', null);
  14745. },
  14746. touchend : function(e) {
  14747. if (e.type === 'touchmove') {
  14748. if (! wrapper.data('touching') ||
  14749. ( Math.abs(wrapper.data('touching').x - e.originalEvent.touches[0].pageX)
  14750. + Math.abs(wrapper.data('touching').y - e.originalEvent.touches[0].pageY)) > 4) {
  14751. wrapper.data('touching', null);
  14752. }
  14753. } else {
  14754. setTimeout(function() {
  14755. cwd.removeData('longtap');
  14756. }, 80);
  14757. }
  14758. clearTimeout(cwd.data('tmlongtap'));
  14759. },
  14760. click : function(e) {
  14761. if (cwd.data('longtap')) {
  14762. e.preventDefault();
  14763. e.stopPropagation();
  14764. }
  14765. }
  14766. },
  14767. /**
  14768. * Update directory content
  14769. *
  14770. * @return void
  14771. */
  14772. content = function() {
  14773. fm.lazy(function() {
  14774. var phash, emptyMethod, thtr;
  14775. wz.append(selectAllCheckbox).removeClass('elfinder-cwd-wrapper-empty elfinder-search-result elfinder-incsearch-result elfinder-letsearch-result');
  14776. if (fm.searchStatus.state > 1 || fm.searchStatus.ininc) {
  14777. wz.addClass('elfinder-search-result' + (fm.searchStatus.ininc? ' elfinder-'+(query.substr(0,1) === '/' ? 'let':'inc')+'search-result' : ''));
  14778. }
  14779. // abort attachThumbJob
  14780. bufferExt.attachThumbJob && bufferExt.attachThumbJob._abort();
  14781. // destroy selectable for GC
  14782. cwd.data('selectable') && cwd.selectable('disable').selectable('destroy').removeData('selectable');
  14783. // notify cwd init
  14784. fm.trigger('cwdinit');
  14785. selectedNext = $();
  14786. try {
  14787. // to avoid problem with draggable
  14788. cwd.empty();
  14789. } catch (e) {
  14790. cwd.html('');
  14791. }
  14792. if (tableHeader) {
  14793. wrapper.off('scroll.fixheader resize.fixheader');
  14794. tableHeader.remove();
  14795. tableHeader = null;
  14796. }
  14797. cwd.removeClass('elfinder-cwd-view-icons elfinder-cwd-view-list')
  14798. .addClass('elfinder-cwd-view-'+(list ? 'list' :'icons'))
  14799. .attr('style', '')
  14800. .css('height', 'auto');
  14801. bottomMarker.hide();
  14802. wrapper[list ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-list')._padding = parseInt(wrapper.css('padding-top')) + parseInt(wrapper.css('padding-bottom'));
  14803. if (fm.UA.iOS) {
  14804. wrapper.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch');
  14805. }
  14806. if (list) {
  14807. cwd.html('<table><thead/><tbody/></table>');
  14808. thtr = $('<tr class="ui-state-default"><td class="elfinder-cwd-view-th-name">'+fm.getColumnName('name')+'</td>'+customColsNameBuild()+'</tr>');
  14809. cwd.find('thead').hide().append(thtr).find('td:first').append(selectAllCheckbox);
  14810. if ($.fn.sortable) {
  14811. thtr.addClass('touch-punch touch-punch-keep-default')
  14812. .sortable({
  14813. axis: 'x',
  14814. distance: 8,
  14815. items: '> .sortable-item',
  14816. start: function(e, ui) {
  14817. $(ui.item[0]).data('dragging', true);
  14818. ui.placeholder
  14819. .width(ui.helper.removeClass('ui-state-hover').width())
  14820. .removeClass('ui-state-active')
  14821. .addClass('ui-state-hover')
  14822. .css('visibility', 'visible');
  14823. },
  14824. update: function(e, ui){
  14825. var target = $(ui.item[0]).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', ''),
  14826. prev, done;
  14827. customCols = $.map($(this).children(), function(n) {
  14828. var name = $(n).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', '');
  14829. if (! done) {
  14830. if (target === name) {
  14831. done = true;
  14832. } else {
  14833. prev = name;
  14834. }
  14835. }
  14836. return (name === 'name')? null : name;
  14837. });
  14838. templates.row = makeTemplateRow();
  14839. fm.storage('cwdCols', customCols);
  14840. prev = '.elfinder-col-'+prev+':first';
  14841. target = '.elfinder-col-'+target+':first';
  14842. fm.lazy(function() {
  14843. cwd.find('tbody tr').each(function() {
  14844. var $this = $(this);
  14845. $this.children(prev).after($this.children(target));
  14846. });
  14847. });
  14848. },
  14849. stop: function(e, ui) {
  14850. setTimeout(function() {
  14851. $(ui.item[0]).removeData('dragging');
  14852. }, 100);
  14853. }
  14854. });
  14855. }
  14856. thtr.find('td').addClass('touch-punch').resizable({
  14857. handles: fm.direction === 'ltr'? 'e' : 'w',
  14858. start: function(e, ui) {
  14859. var target = cwd.find('td.elfinder-col-'
  14860. + ui.element.attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', '')
  14861. + ':first');
  14862. ui.element
  14863. .data('dragging', true)
  14864. .data('resizeTarget', target)
  14865. .data('targetWidth', target.width());
  14866. colResizing = true;
  14867. if (cwd.find('table').css('table-layout') !== 'fixed') {
  14868. cwd.find('tbody tr:first td').each(function() {
  14869. $(this).width($(this).width());
  14870. });
  14871. cwd.find('table').css('table-layout', 'fixed');
  14872. }
  14873. },
  14874. resize: function(e, ui) {
  14875. ui.element.data('resizeTarget').width(ui.element.data('targetWidth') - (ui.originalSize.width - ui.size.width));
  14876. },
  14877. stop : function(e, ui) {
  14878. colResizing = false;
  14879. fixTableHeader({fitWidth: true});
  14880. colWidth = {};
  14881. cwd.find('tbody tr:first td').each(function() {
  14882. var name = $(this).attr('class').split(' ')[0].replace('elfinder-col-', '');
  14883. colWidth[name] = $(this).width();
  14884. });
  14885. fm.storage('cwdColWidth', colWidth);
  14886. setTimeout(function() {
  14887. ui.element.removeData('dragging');
  14888. }, 100);
  14889. }
  14890. })
  14891. .find('.ui-resizable-handle').addClass('ui-icon ui-icon-grip-dotted-vertical');
  14892. }
  14893. buffer = $.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null; });
  14894. buffer = fm.sortFiles(buffer);
  14895. if (incHashes) {
  14896. incHashes = $.map(buffer, function(f) { return f.hash; });
  14897. } else {
  14898. cwdHashes = $.map(buffer, function(f) { return f.hash; });
  14899. }
  14900. bufferExt = {
  14901. renderd: 0,
  14902. attachTmbs: {},
  14903. getTmbs: [],
  14904. tmbLoading: {},
  14905. lazyOpts: { tm : 0 }
  14906. };
  14907. wz[(buffer.length < 1) ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-empty');
  14908. wrapper.off(scrollEvent, render).on(scrollEvent, render).trigger(scrollEvent);
  14909. // set droppable
  14910. if (!fm.cwd().write) {
  14911. wrapper.removeClass('native-droppable')
  14912. .droppable('disable')
  14913. .removeClass('ui-state-disabled'); // for old jQueryUI see https://bugs.jqueryui.com/ticket/5974
  14914. } else {
  14915. wrapper[fm.isCommandEnabled('upload')? 'addClass' : 'removeClass']('native-droppable');
  14916. wrapper.droppable(fm.isCommandEnabled('paste')? 'enable' : 'disable');
  14917. }
  14918. });
  14919. },
  14920. /**
  14921. * CWD node itself
  14922. *
  14923. * @type JQuery
  14924. **/
  14925. cwd = $(this)
  14926. .addClass('ui-helper-clearfix elfinder-cwd')
  14927. .attr('unselectable', 'on')
  14928. // fix ui.selectable bugs and add shift+click support
  14929. .on('click.'+fm.namespace, fileSelector, function(e) {
  14930. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  14931. tgt = $(e.target),
  14932. prev,
  14933. next,
  14934. pl,
  14935. nl,
  14936. sib;
  14937. if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) {
  14938. e.stopPropagation();
  14939. e.preventDefault();
  14940. p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect);
  14941. trigger();
  14942. requestAnimationFrame(function() {
  14943. tgt.prop('checked', p.hasClass(clSelected));
  14944. });
  14945. return;
  14946. }
  14947. if (cwd.data('longtap') || tgt.hasClass('elfinder-cwd-nonselect')) {
  14948. e.stopPropagation();
  14949. return;
  14950. }
  14951. if (!curClickId) {
  14952. curClickId = p.attr('id');
  14953. setTimeout(function() {
  14954. curClickId = '';
  14955. }, 500);
  14956. }
  14957. if (e.shiftKey) {
  14958. prev = p.prevAll(lastSelect || '.'+clSelected+':first');
  14959. next = p.nextAll(lastSelect || '.'+clSelected+':first');
  14960. pl = prev.length;
  14961. nl = next.length;
  14962. }
  14963. if (e.shiftKey && (pl || nl)) {
  14964. sib = pl ? p.prevUntil('#'+prev.attr('id')) : p.nextUntil('#'+next.attr('id'));
  14965. sib.add(p).trigger(evtSelect);
  14966. } else if (e.ctrlKey || e.metaKey) {
  14967. p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect);
  14968. } else {
  14969. if (wrapper.data('touching') && p.hasClass(clSelected)) {
  14970. wrapper.data('touching', null);
  14971. fm.dblclick({file : fm.cwdId2Hash(this.id)});
  14972. return;
  14973. } else {
  14974. unselectAll({ notrigger: true });
  14975. p.trigger(evtSelect);
  14976. }
  14977. }
  14978. trigger();
  14979. })
  14980. // call fm.open()
  14981. .on('dblclick.'+fm.namespace, fileSelector, function(e) {
  14982. if (curClickId) {
  14983. var hash = fm.cwdId2Hash(curClickId);
  14984. e.stopPropagation();
  14985. if (this.id !== curClickId) {
  14986. $(this).trigger(evtUnselect);
  14987. $('#'+curClickId).trigger(evtSelect);
  14988. trigger();
  14989. }
  14990. fm.dblclick({file : hash});
  14991. }
  14992. })
  14993. // for touch device
  14994. .on('touchstart.'+fm.namespace, fileSelector, function(e) {
  14995. if (e.originalEvent.touches.length > 1) {
  14996. return;
  14997. }
  14998. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  14999. tgt = $(e.target),
  15000. nodeName = e.target.nodeName,
  15001. sel;
  15002. if ((nodeName === 'INPUT' && e.target.type === 'text') || nodeName === 'TEXTAREA' || tgt.hasClass('elfinder-cwd-nonselect')) {
  15003. e.stopPropagation();
  15004. return;
  15005. }
  15006. // now name editing
  15007. if (p.find('input:text,textarea').length) {
  15008. e.stopPropagation();
  15009. e.preventDefault();
  15010. return;
  15011. }
  15012. wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY});
  15013. if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) {
  15014. return;
  15015. }
  15016. sel = p.prevAll('.'+clSelected+':first').length +
  15017. p.nextAll('.'+clSelected+':first').length;
  15018. cwd.data('longtap', null);
  15019. if (Object.keys(selectedFiles).length
  15020. ||
  15021. (list && e.target.nodeName !== 'TD')
  15022. ||
  15023. (!list && this !== e.target)
  15024. ) {
  15025. cwd.data('longtap', false);
  15026. p.addClass(clHover);
  15027. p.data('tmlongtap', setTimeout(function(){
  15028. // long tap
  15029. cwd.data('longtap', true);
  15030. p.trigger(evtSelect);
  15031. trigger();
  15032. fm.trigger('contextmenu', {
  15033. 'type' : 'files',
  15034. 'targets' : fm.selected(),
  15035. 'x' : e.originalEvent.touches[0].pageX,
  15036. 'y' : e.originalEvent.touches[0].pageY
  15037. });
  15038. }, 500));
  15039. }
  15040. })
  15041. .on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, fileSelector, function(e) {
  15042. var tgt = $(e.target),
  15043. p;
  15044. if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) {
  15045. return;
  15046. }
  15047. if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA') {
  15048. e.stopPropagation();
  15049. return;
  15050. }
  15051. p = this.id ? $(this) : $(this).parents('[id]:first');
  15052. clearTimeout(p.data('tmlongtap'));
  15053. if (e.type === 'touchmove') {
  15054. wrapper.data('touching', null);
  15055. p.removeClass(clHover);
  15056. } else {
  15057. if (wrapper.data('touching') && !cwd.data('longtap') && p.hasClass(clSelected)) {
  15058. e.preventDefault();
  15059. wrapper.data('touching', null);
  15060. fm.dblclick({file : fm.cwdId2Hash(this.id)});
  15061. }
  15062. setTimeout(function() {
  15063. cwd.removeData('longtap');
  15064. }, 80);
  15065. }
  15066. })
  15067. // attach draggable
  15068. .on('mouseenter.'+fm.namespace, fileSelector, function(e) {
  15069. if (scrolling) { return; }
  15070. var $this = $(this), helper = null;
  15071. if (!mobile && !$this.data('dragRegisted') && !$this.hasClass(clTmp) && !$this.hasClass(clDraggable) && !$this.hasClass(clDisabled)) {
  15072. $this.data('dragRegisted', true);
  15073. if (!fm.isCommandEnabled('copy', fm.searchStatus.state > 1 || $this.hasClass('isroot')? fm.cwdId2Hash($this.attr('id')) : void 0)) {
  15074. return;
  15075. }
  15076. $this.on('mousedown', function(e) {
  15077. // shiftKey or altKey + drag start for HTML5 native drag function
  15078. // Note: can no use shiftKey with the Google Chrome
  15079. var metaKey = options.metakeyDragout && !fm.UA.IE && (e.shiftKey || e.altKey),
  15080. disable = false;
  15081. if (metaKey && cwd.data('selectable')) {
  15082. // destroy jQuery-ui selectable while trigger native drag
  15083. cwd.selectable('disable').selectable('destroy').removeData('selectable');
  15084. requestAnimationFrame(function(){
  15085. cwd.selectable(selectableOption).selectable('option', {disabled: false}).selectable('refresh').data('selectable', true);
  15086. });
  15087. }
  15088. $this.removeClass('ui-state-disabled');
  15089. if (metaKey) {
  15090. $this.draggable('option', 'disabled', true).attr('draggable', 'true');
  15091. } else {
  15092. if (!$this.hasClass(clSelected)) {
  15093. if (list) {
  15094. disable = $(e.target).closest('span,tr').is('tr');
  15095. } else {
  15096. disable = $(e.target).hasClass('elfinder-cwd-file');
  15097. }
  15098. }
  15099. if (disable) {
  15100. $this.draggable('option', 'disabled', true);
  15101. } else {
  15102. $this.draggable('option', 'disabled', false)
  15103. .removeAttr('draggable')
  15104. .draggable('option', 'cursorAt', {left: 50 - parseInt($(e.currentTarget).css('margin-left')), top: 47});
  15105. }
  15106. }
  15107. })
  15108. .on('dragstart', function(e) {
  15109. var dt = e.dataTransfer || e.originalEvent.dataTransfer || null;
  15110. helper = null;
  15111. if (dt && !fm.UA.IE) {
  15112. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  15113. elm = $('<span>'),
  15114. url = '',
  15115. durl = null,
  15116. murl = null,
  15117. files = [],
  15118. icon = function(f) {
  15119. var mime = f.mime, i, tmb = fm.tmb(f);
  15120. i = '<div class="elfinder-cwd-icon elfinder-cwd-icon-drag '+fm.mime2class(mime)+' ui-corner-all"/>';
  15121. if (tmb) {
  15122. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  15123. }
  15124. return i;
  15125. }, l, geturl = [];
  15126. p.trigger(evtSelect);
  15127. trigger();
  15128. $.each(selectedFiles, function(v){
  15129. var file = fm.file(v),
  15130. furl = file.url;
  15131. if (file && file.mime !== 'directory') {
  15132. if (!furl) {
  15133. furl = fm.url(file.hash);
  15134. } else if (furl == '1') {
  15135. geturl.push(v);
  15136. return true;
  15137. }
  15138. if (furl) {
  15139. furl = fm.convAbsUrl(furl);
  15140. files.push(v);
  15141. $('<a>').attr('href', furl).text(furl).appendTo(elm);
  15142. url += furl + "\n";
  15143. if (!durl) {
  15144. durl = file.mime + ':' + file.name + ':' + furl;
  15145. }
  15146. if (!murl) {
  15147. murl = furl + "\n" + file.name;
  15148. }
  15149. }
  15150. }
  15151. });
  15152. if (geturl.length) {
  15153. $.each(geturl, function(i, v){
  15154. var rfile = fm.file(v);
  15155. rfile.url = '';
  15156. fm.request({
  15157. data : {cmd : 'url', target : v},
  15158. notify : {type : 'url', cnt : 1},
  15159. preventDefault : true
  15160. })
  15161. .always(function(data) {
  15162. rfile.url = data.url? data.url : '1';
  15163. });
  15164. });
  15165. return false;
  15166. } else if (url) {
  15167. if (dt.setDragImage) {
  15168. helper = $('<div class="elfinder-drag-helper html5-native"></div>').append(icon(fm.file(files[0]))).appendTo($(document.body));
  15169. if ((l = files.length) > 1) {
  15170. helper.append(icon(fm.file(files[l-1])) + '<span class="elfinder-drag-num">'+l+'</span>');
  15171. }
  15172. dt.setDragImage(helper.get(0), 50, 47);
  15173. }
  15174. dt.effectAllowed = 'copyLink';
  15175. dt.setData('DownloadURL', durl);
  15176. dt.setData('text/x-moz-url', murl);
  15177. dt.setData('text/uri-list', url);
  15178. dt.setData('text/plain', url);
  15179. dt.setData('text/html', elm.html());
  15180. dt.setData('elfinderfrom', window.location.href + fm.cwd().hash);
  15181. dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), '');
  15182. } else {
  15183. return false;
  15184. }
  15185. }
  15186. })
  15187. .on('dragend', function(e){
  15188. unselectAll({ notrigger: true });
  15189. helper && helper.remove();
  15190. })
  15191. .draggable(fm.draggable);
  15192. }
  15193. })
  15194. // add hover class to selected file
  15195. .on(evtSelect, fileSelector, function(e) {
  15196. var $this = $(this),
  15197. id = fm.cwdId2Hash($this.attr('id'));
  15198. if (!selectLock && !$this.hasClass(clDisabled)) {
  15199. lastSelect = '#'+ this.id;
  15200. $this.addClass(clSelected).children().addClass(clHover).find('input:checkbox.'+clSelChk).prop('checked', true);
  15201. if (! selectedFiles[id]) {
  15202. selectedFiles[id] = true;
  15203. }
  15204. // will be selected next
  15205. selectedNext = cwd.find('[id].'+clSelected+':last').next();
  15206. }
  15207. })
  15208. // remove hover class from unselected file
  15209. .on(evtUnselect, fileSelector, function(e) {
  15210. var $this = $(this),
  15211. id = fm.cwdId2Hash($this.attr('id'));
  15212. if (!selectLock) {
  15213. $this.removeClass(clSelected).children().removeClass(clHover).find('input:checkbox.'+clSelChk).prop('checked', false);
  15214. if (cwd.hasClass('elfinder-cwd-allselected')) {
  15215. selectCheckbox && selectAllCheckbox.children('input').prop('checked', false);
  15216. cwd.removeClass('elfinder-cwd-allselected');
  15217. }
  15218. selectedFiles[id] && delete selectedFiles[id];
  15219. }
  15220. })
  15221. // disable files wich removing or moving
  15222. .on(evtDisable, fileSelector, function() {
  15223. var $this = $(this).removeClass(clHover+' '+clSelected).addClass(clDisabled),
  15224. child = $this.children(),
  15225. target = (list ? $this : child.find('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename'));
  15226. child.removeClass(clHover+' '+clSelected);
  15227. $this.hasClass(clDroppable) && $this.droppable('disable');
  15228. target.hasClass(clDraggable) && target.draggable('disable');
  15229. })
  15230. // if any files was not removed/moved - unlock its
  15231. .on(evtEnable, fileSelector, function() {
  15232. var $this = $(this).removeClass(clDisabled),
  15233. target = list ? $this : $this.children('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename');
  15234. $this.hasClass(clDroppable) && $this.droppable('enable');
  15235. target.hasClass(clDraggable) && target.draggable('enable');
  15236. })
  15237. .on('scrolltoview', fileSelector, function(e, data) {
  15238. scrollToView($(this), (data && typeof data.blink !== 'undefined')? data.blink : true);
  15239. })
  15240. .on('mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, fileSelector, function(e) {
  15241. var enter = (e.type === 'mouseenter');
  15242. if (enter && (scrolling || fm.UA.Mobile)) { return; }
  15243. fm.trigger('hover', {hash : fm.cwdId2Hash($(this).attr('id')), type : e.type});
  15244. $(this).toggleClass(clHover, (e.type == 'mouseenter'));
  15245. })
  15246. // for file contextmenu
  15247. .on('mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, '.elfinder-cwd-file-wrapper,.elfinder-cwd-filename', function(e) {
  15248. var enter = (e.type === 'mouseenter');
  15249. if (enter && scrolling) { return; }
  15250. $(this).closest(fileSelector).children('.elfinder-cwd-file-wrapper,.elfinder-cwd-filename').toggleClass(clActive, (e.type == 'mouseenter'));
  15251. })
  15252. .on('contextmenu.'+fm.namespace, function(e) {
  15253. var file = $(e.target).closest(fileSelector);
  15254. if (file.get(0) === e.target && !selectedFiles[fm.cwdId2Hash(file.get(0).id)]) {
  15255. return;
  15256. }
  15257. // now filename editing
  15258. if (file.find('input:text,textarea').length) {
  15259. e.stopPropagation();
  15260. return;
  15261. }
  15262. if (file.length && (e.target.nodeName != 'TD' || selectedFiles[fm.cwdId2Hash(file.get(0).id)])) {
  15263. e.stopPropagation();
  15264. e.preventDefault();
  15265. if (!file.hasClass(clDisabled) && !wrapper.data('touching')) {
  15266. if (!file.hasClass(clSelected)) {
  15267. unselectAll({ notrigger: true });
  15268. file.trigger(evtSelect);
  15269. trigger();
  15270. }
  15271. fm.trigger('contextmenu', {
  15272. 'type' : 'files',
  15273. 'targets' : fm.selected(),
  15274. 'x' : e.pageX,
  15275. 'y' : e.pageY
  15276. });
  15277. }
  15278. }
  15279. })
  15280. // unselect all on cwd click
  15281. .on('click.'+fm.namespace, function(e) {
  15282. if (e.target === this && ! cwd.data('longtap')) {
  15283. !e.shiftKey && !e.ctrlKey && !e.metaKey && unselectAll();
  15284. }
  15285. })
  15286. // prepend fake file/dir
  15287. .on('create.'+fm.namespace, function(e, f) {
  15288. var parent = list ? cwd.find('tbody') : cwd,
  15289. p = parent.find('.elfinder-cwd-parent'),
  15290. lock = f.move || false,
  15291. file = $(itemhtml(f)).addClass(clTmp),
  15292. selected = fm.selected();
  15293. if (selected.length) {
  15294. lock && fm.trigger('lockfiles', {files: selected});
  15295. } else {
  15296. unselectAll();
  15297. }
  15298. if (p.length) {
  15299. p.after(file);
  15300. } else {
  15301. parent.prepend(file);
  15302. }
  15303. setColwidth();
  15304. wrapper.scrollTop(0).scrollLeft(0);
  15305. })
  15306. // unselect all selected files
  15307. .on('unselectall', unselectAll)
  15308. .on('selectfile', function(e, id) {
  15309. fm.cwdHash2Elm(id).trigger(evtSelect);
  15310. trigger();
  15311. })
  15312. .on('colwidth', function() {
  15313. if (list) {
  15314. cwd.find('table').css('table-layout', '')
  15315. .find('td').css('width', '');
  15316. fixTableHeader({fitWidth: true});
  15317. fm.storage('cwdColWidth', colWidth = null);
  15318. }
  15319. })
  15320. .on('iconpref', function(e, data) {
  15321. cwd.removeClass(function(i, cName) {
  15322. return (cName.match(/\belfinder-cwd-size\S+/g) || []).join(' ');
  15323. });
  15324. iconSize = data? (parseInt(data.size) || 0) : 0;
  15325. if (!list) {
  15326. if (iconSize > 0) {
  15327. cwd.addClass('elfinder-cwd-size' + iconSize);
  15328. }
  15329. if (bufferExt.renderd) {
  15330. requestAnimationFrame(function() {
  15331. itemBoxSize.icons = {};
  15332. bufferExt.hpi = null;
  15333. bottomMarkerShow(cwd, bufferExt.renderd);
  15334. wrapperRepaint();
  15335. });
  15336. }
  15337. }
  15338. })
  15339. // Change icon size with mouse wheel event
  15340. .on('onwheel' in document ? 'wheel' : 'mousewheel', function(e) {
  15341. var tm, size, delta;
  15342. if (!list && ((e.ctrlKey && !e.metaKey) || (!e.ctrlKey && e.metaKey))) {
  15343. e.stopPropagation();
  15344. e.preventDefault();
  15345. tm = cwd.data('wheelTm');
  15346. if (typeof tm !== 'undefined') {
  15347. clearTimeout(tm);
  15348. cwd.data('wheelTm', setTimeout(function() {
  15349. cwd.removeData('wheelTm');
  15350. }, 200));
  15351. } else {
  15352. cwd.data('wheelTm', false);
  15353. size = iconSize || 0;
  15354. delta = e.originalEvent.deltaY ? e.originalEvent.deltaY : -(e.originalEvent.wheelDelta);
  15355. if (delta > 0) {
  15356. if (iconSize > 0) {
  15357. size = iconSize - 1;
  15358. }
  15359. } else {
  15360. if (iconSize < options.iconsView.sizeMax) {
  15361. size = iconSize + 1;
  15362. }
  15363. }
  15364. if (size !== iconSize) {
  15365. fm.storage('iconsize', size);
  15366. cwd.trigger('iconpref', {size: size});
  15367. }
  15368. }
  15369. }
  15370. }),
  15371. wrapper = $('<div class="elfinder-cwd-wrapper"/>')
  15372. // make cwd itself droppable for folders from nav panel
  15373. .droppable(Object.assign({}, droppable, {autoDisable: false}))
  15374. .on('contextmenu.'+fm.namespace, wrapperContextMenu.contextmenu)
  15375. .on('touchstart.'+fm.namespace, wrapperContextMenu.touchstart)
  15376. .on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, wrapperContextMenu.touchend)
  15377. .on('click.'+fm.namespace, wrapperContextMenu.click)
  15378. .on('scroll.'+fm.namespace, function() {
  15379. if (! scrolling) {
  15380. cwd.data('selectable') && cwd.selectable('disable');
  15381. wrapper.trigger(scrollStartEvent);
  15382. }
  15383. scrolling = true;
  15384. bufferExt.scrtm && cancelAnimationFrame(bufferExt.scrtm);
  15385. if (bufferExt.scrtm && Math.abs((bufferExt.scrolltop || 0) - (bufferExt.scrolltop = (this.scrollTop || $(this).scrollTop()))) < 5) {
  15386. bufferExt.scrtm = 0;
  15387. wrapper.trigger(scrollEvent);
  15388. }
  15389. bufferExt.scrtm = requestAnimationFrame(function() {
  15390. bufferExt.scrtm = 0;
  15391. wrapper.trigger(scrollEvent);
  15392. });
  15393. })
  15394. .on(scrollEvent, function() {
  15395. scrolling = false;
  15396. wrapperRepaint();
  15397. }),
  15398. bottomMarker = $('<div>&nbsp;</div>')
  15399. .css({position: 'absolute', width: '1px', height: '1px'})
  15400. .hide(),
  15401. selectAllCheckbox = selectCheckbox? $('<div class="elfinder-cwd-selectall"><input type="checkbox"/></div>')
  15402. .attr('title', fm.i18n('selectall'))
  15403. .on('touchstart mousedown click', function(e) {
  15404. e.stopPropagation();
  15405. e.preventDefault();
  15406. if ($(this).data('pending') || e.type === 'click') {
  15407. return false;
  15408. }
  15409. selectAllCheckbox.data('pending', true);
  15410. if (cwd.hasClass('elfinder-cwd-allselected')) {
  15411. selectAllCheckbox.find('input').prop('checked', false);
  15412. requestAnimationFrame(function() {
  15413. unselectAll();
  15414. });
  15415. } else {
  15416. selectAll();
  15417. }
  15418. }) : $(),
  15419. restm = null,
  15420. resize = function(init) {
  15421. var initHeight = function() {
  15422. if (typeof bufferExt.renderd !== 'undefined') {
  15423. var h = 0;
  15424. wrapper.siblings('div.elfinder-panel:visible').each(function() {
  15425. h += $(this).outerHeight(true);
  15426. });
  15427. wrapper.height(wz.height() - h - wrapper._padding);
  15428. }
  15429. };
  15430. init && initHeight();
  15431. restm && cancelAnimationFrame(restm);
  15432. restm = requestAnimationFrame(function(){
  15433. !init && initHeight();
  15434. var wph, cwdoh;
  15435. // fix cwd height if it less then wrapper
  15436. cwd.css('height', 'auto');
  15437. wph = wrapper[0].clientHeight - parseInt(wrapper.css('padding-top')) - parseInt(wrapper.css('padding-bottom')) - parseInt(cwd.css('margin-top')),
  15438. cwdoh = cwd.outerHeight(true);
  15439. if (cwdoh < wph) {
  15440. cwd.height(wph);
  15441. }
  15442. });
  15443. list && ! colResizing && (init? wrapper.trigger('resize.fixheader') : fixTableHeader());
  15444. wrapperRepaint();
  15445. },
  15446. // elfinder node
  15447. parent = $(this).parent().on('resize', resize),
  15448. // workzone node
  15449. wz = parent.children('.elfinder-workzone').append(wrapper.append(this).append(bottomMarker)),
  15450. // message board
  15451. mBoard = $('<div class="elfinder-cwd-message-board"/>').insertAfter(cwd),
  15452. // Volume expires
  15453. vExpires = $('<div class="elfinder-cwd-expires" />'),
  15454. vExpiresTm,
  15455. showVolumeExpires = function() {
  15456. var remain, sec, int;
  15457. vExpiresTm && clearTimeout(vExpiresTm);
  15458. if (curVolId && fm.volumeExpires[curVolId]) {
  15459. sec = fm.volumeExpires[curVolId] - ((+new Date()) / 1000);
  15460. int = (sec % 60) + 0.1;
  15461. remain = Math.floor(sec / 60);
  15462. vExpires.html(fm.i18n(['minsLeft', remain])).show();
  15463. if (remain) {
  15464. vExpiresTm = setTimeout(showVolumeExpires, int * 1000);
  15465. }
  15466. }
  15467. },
  15468. // each item box size
  15469. itemBoxSize = {
  15470. icons : {},
  15471. list : {}
  15472. },
  15473. // has UI tree
  15474. hasUiTree,
  15475. // Icon size of icons view
  15476. iconSize,
  15477. // Current volume id
  15478. curVolId,
  15479. winScrTm;
  15480. // IE < 11 not support CSS `pointer-events: none`
  15481. if (!fm.UA.ltIE10) {
  15482. mBoard.append($('<div class="elfinder-cwd-trash" />').html(fm.i18n('volume_Trash')))
  15483. .append(vExpires);
  15484. }
  15485. // setup by options
  15486. replacement = Object.assign(replacement, options.replacement || {});
  15487. try {
  15488. colWidth = fm.storage('cwdColWidth')? fm.storage('cwdColWidth') : null;
  15489. } catch(e) {
  15490. colWidth = null;
  15491. }
  15492. // setup costomCols
  15493. fm.bind('columnpref', function(e) {
  15494. var opts = e.data || {};
  15495. if (customCols = fm.storage('cwdCols')) {
  15496. customCols = $.grep(customCols, function(n) {
  15497. return (options.listView.columns.indexOf(n) !== -1)? true : false;
  15498. });
  15499. if (options.listView.columns.length > customCols.length) {
  15500. $.each(options.listView.columns, function(i, n) {
  15501. if (customCols.indexOf(n) === -1) {
  15502. customCols.push(n);
  15503. }
  15504. });
  15505. }
  15506. } else {
  15507. customCols = options.listView.columns;
  15508. }
  15509. // column names array that hidden
  15510. var columnhides = fm.storage('columnhides') || null;
  15511. if (columnhides && Object.keys(columnhides).length)
  15512. customCols = $.grep(customCols, function(n) {
  15513. return columnhides[n]? false : true;
  15514. });
  15515. // make template with customCols
  15516. templates.row = makeTemplateRow();
  15517. // repaint if need it
  15518. list && opts.repaint && content();
  15519. }).trigger('columnpref');
  15520. if (mobile) {
  15521. // for iOS5 bug
  15522. $('body').on('touchstart touchmove touchend', function(e){});
  15523. }
  15524. selectCheckbox && cwd.addClass('elfinder-has-checkbox');
  15525. $(window).on('scroll.'+fm.namespace, function() {
  15526. winScrTm && cancelAnimationFrame(winScrTm);
  15527. winScrTm = requestAnimationFrame(function() {
  15528. wrapper.trigger(scrollEvent);
  15529. });
  15530. });
  15531. $(document).on('keydown.'+fm.namespace, function(e) {
  15532. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  15533. if (! fm.getUI().find('.ui-widget:visible').length) {
  15534. unselectAll();
  15535. }
  15536. }
  15537. });
  15538. fm
  15539. .one('init', function(){
  15540. var style = document.createElement('style'),
  15541. sheet, node, base, resizeTm, iconSize, i = 0;
  15542. if (document.head) {
  15543. document.head.appendChild(style);
  15544. sheet = style.sheet;
  15545. sheet.insertRule('.elfinder-cwd-wrapper-empty .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder')+'" }', i++);
  15546. sheet.insertRule('.elfinder-cwd-wrapper-empty .native-droppable .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder'+(mobile? 'LTap' : 'Drop'))+'" }', i++);
  15547. sheet.insertRule('.elfinder-cwd-wrapper-empty .ui-droppable-disabled .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder')+'" }', i++);
  15548. sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptySearch')+'" }', i++);
  15549. 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++);
  15550. 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++);
  15551. }
  15552. if (iconSize = (fm.storage('iconsize') || options.iconsView.size || 0)) {
  15553. iconSize = Math.min(iconSize, options.iconsView.sizeMax);
  15554. cwd.trigger('iconpref', {size: iconSize});
  15555. }
  15556. if (! mobile) {
  15557. fm.one('open', function() {
  15558. sheet && fm.zIndex && sheet.insertRule('.ui-selectable-helper{z-index:'+fm.zIndex+';}', i++);
  15559. });
  15560. base = $('<div style="position:absolute"/>');
  15561. node = fm.getUI();
  15562. node.on('resize', function(e, data) {
  15563. var offset;
  15564. e.preventDefault();
  15565. e.stopPropagation();
  15566. if (data && data.fullscreen) {
  15567. offset = node.offset();
  15568. if (data.fullscreen === 'on') {
  15569. base.css({top:offset.top * -1 , left:offset.left * -1 }).appendTo(node);
  15570. selectableOption.appendTo = base;
  15571. } else {
  15572. base.detach();
  15573. selectableOption.appendTo = 'body';
  15574. }
  15575. cwd.data('selectable') && cwd.selectable('option', {appendTo : selectableOption.appendTo});
  15576. }
  15577. });
  15578. }
  15579. hasUiTree = fm.getUI('tree').length;
  15580. })
  15581. .bind('enable', function() {
  15582. resize();
  15583. })
  15584. .bind('request.open', function() {
  15585. bufferExt.getTmbs = [];
  15586. })
  15587. .one('open', function() {
  15588. if (fm.maxTargets) {
  15589. tmbNum = Math.min(fm.maxTargets, tmbNum);
  15590. }
  15591. })
  15592. .bind('open add remove searchend', function() {
  15593. var phash = fm.cwd().hash,
  15594. type = this.type;
  15595. if (type === 'open' || type === 'searchend' || fm.searchStatus.state < 2) {
  15596. cwdHashes = $.map(fm.files(phash), function(f) { return f.hash; });
  15597. fm.trigger('cwdhasheschange', cwdHashes);
  15598. }
  15599. if (type === 'open') {
  15600. var inTrash = function() {
  15601. var isIn = false;
  15602. $.each(cwdParents, function(i, h) {
  15603. if (fm.trashes[h]) {
  15604. isIn = true;
  15605. return false;
  15606. }
  15607. });
  15608. return isIn;
  15609. },
  15610. req = phash?
  15611. (! fm.file(phash) || hasUiTree?
  15612. (! hasUiTree?
  15613. fm.request({
  15614. data: {
  15615. cmd : 'parents',
  15616. target : fm.cwd().hash
  15617. },
  15618. preventFail : true
  15619. }) : (function() {
  15620. var dfd = $.Deferred();
  15621. fm.one('treesync', function(e) {
  15622. e.data.always(function() {
  15623. dfd.resolve();
  15624. });
  15625. });
  15626. return dfd;
  15627. })()
  15628. ) : null
  15629. ) : null,
  15630. cwdObj = fm.cwd();
  15631. // add/remove volume id class
  15632. if (cwdObj.volumeid !== curVolId) {
  15633. vExpires.empty().hide();
  15634. if (curVolId) {
  15635. wrapper.removeClass('elfinder-cwd-wrapper-' + curVolId);
  15636. }
  15637. curVolId = cwdObj.volumeid;
  15638. showVolumeExpires();
  15639. wrapper.addClass('elfinder-cwd-wrapper-' + curVolId);
  15640. }
  15641. // add/remove trash class
  15642. $.when(req).done(function() {
  15643. cwdParents = fm.parents(cwdObj.hash);
  15644. wrapper[inTrash()? 'addClass':'removeClass']('elfinder-cwd-wrapper-trash');
  15645. });
  15646. incHashes = void 0;
  15647. unselectAll({ notrigger: true });
  15648. content();
  15649. }
  15650. })
  15651. .bind('search', function(e) {
  15652. cwdHashes = $.map(e.data.files, function(f) { return f.hash; });
  15653. fm.trigger('cwdhasheschange', cwdHashes);
  15654. incHashes = void 0;
  15655. fm.searchStatus.ininc = false;
  15656. content();
  15657. fm.autoSync('stop');
  15658. })
  15659. .bind('searchend', function(e) {
  15660. if (query || incHashes) {
  15661. query = '';
  15662. if (incHashes) {
  15663. fm.trigger('incsearchend', e.data);
  15664. } else {
  15665. if (!e.data || !e.data.noupdate) {
  15666. content();
  15667. }
  15668. }
  15669. }
  15670. fm.autoSync();
  15671. })
  15672. .bind('searchstart', function(e) {
  15673. unselectAll();
  15674. query = e.data.query;
  15675. })
  15676. .bind('incsearchstart', function(e) {
  15677. var q = e.data.query || '',
  15678. type = e.data.type || 'SearchName',
  15679. searchTypes = fm.options.commandsOptions.search.searchTypes || {};
  15680. if ((searchTypes[type] && searchTypes[type].incsearch) || type === 'SearchName') {
  15681. selectedFiles = {};
  15682. fm.lazy(function() {
  15683. // incremental search
  15684. var regex, incSearch, fst = '';
  15685. query = q;
  15686. if (q) {
  15687. if (q.substr(0,1) === '/') {
  15688. q = q.substr(1);
  15689. fst = '^';
  15690. }
  15691. regex = new RegExp(fst + q.replace(/([\\*\;\.\?\[\]\{\}\(\)\^\$\-\|])/g, '\\$1'), 'i');
  15692. if (type === 'SearchName') {
  15693. incHashes = $.grep(cwdHashes, function(hash) {
  15694. var file = fm.file(hash);
  15695. return (file && (file.name.match(regex) || (file.i18 && file.i18.match(regex))))? true : false;
  15696. });
  15697. } else {
  15698. incSearch = searchTypes[type].incsearch;
  15699. if (typeof incSearch === 'string') {
  15700. incHashes = $.grep(cwdHashes, function(hash) {
  15701. var file = fm.file(hash);
  15702. return (file && file[incSearch] && (file[incSearch] + '').match(regex))? true : false;
  15703. });
  15704. } else if (typeof incSearch === 'function') {
  15705. try {
  15706. incHashes = $.grep(incSearch({val: q, regex: regex}, cwdHashes, fm), function(hash) {
  15707. return fm.file(hash)? true : false;
  15708. });
  15709. } catch(e) {
  15710. incHashes = [];
  15711. }
  15712. }
  15713. }
  15714. fm.trigger('incsearch', { hashes: incHashes, query: q })
  15715. .searchStatus.ininc = true;
  15716. content();
  15717. fm.autoSync('stop');
  15718. } else {
  15719. fm.trigger('incsearchend');
  15720. }
  15721. });
  15722. }
  15723. })
  15724. .bind('incsearchend', function(e) {
  15725. query = '';
  15726. fm.searchStatus.ininc = false;
  15727. incHashes = void 0;
  15728. if (!e.data || !e.data.noupdate) {
  15729. content();
  15730. }
  15731. fm.autoSync();
  15732. })
  15733. .bind('sortchange', function() {
  15734. var lastScrollLeft = wrapper.scrollLeft(),
  15735. allsel = cwd.hasClass('elfinder-cwd-allselected');
  15736. content();
  15737. fm.one('cwdrender', function() {
  15738. wrapper.scrollLeft(lastScrollLeft);
  15739. if (allsel) {
  15740. selectedFiles = fm.arrayFlip(incHashes || cwdHashes, true);
  15741. }
  15742. (allsel || Object.keys(selectedFiles).length) && trigger();
  15743. });
  15744. })
  15745. .bind('viewchange', function() {
  15746. var l = fm.storage('view') == 'list',
  15747. allsel = cwd.hasClass('elfinder-cwd-allselected');
  15748. if (l != list) {
  15749. list = l;
  15750. fm.viewType = list? 'list' : 'icons';
  15751. if (iconSize) {
  15752. fm.one('cwdinit', function() {
  15753. cwd.trigger('iconpref', {size: iconSize});
  15754. });
  15755. }
  15756. content();
  15757. resize();
  15758. if (allsel) {
  15759. cwd.addClass('elfinder-cwd-allselected');
  15760. selectAllCheckbox.find('input').prop('checked', true);
  15761. }
  15762. Object.keys(selectedFiles).length && trigger();
  15763. }
  15764. })
  15765. .bind('wzresize', function() {
  15766. var place = list ? cwd.find('tbody') : cwd,
  15767. cwdOffset;
  15768. resize(true);
  15769. if (bufferExt.hpi) {
  15770. bottomMarkerShow(place, place.find('[id]').length);
  15771. }
  15772. cwdOffset = cwd.offset();
  15773. wz.data('rectangle', Object.assign(
  15774. {
  15775. width: wz.width(),
  15776. height: wz.height(),
  15777. cwdEdge: (fm.direction === 'ltr')? cwdOffset.left : cwdOffset.left + cwd.width()
  15778. },
  15779. wz.offset())
  15780. );
  15781. bufferExt.itemH = (list? place.find('tr:first') : place.find('[id]:first')).outerHeight(true);
  15782. })
  15783. .bind('changeclipboard', function(e) {
  15784. clipCuts = {};
  15785. if (e.data && e.data.clipboard && e.data.clipboard.length) {
  15786. $.each(e.data.clipboard, function(i, f) {
  15787. if (f.cut) {
  15788. clipCuts[f.hash] = true;
  15789. }
  15790. });
  15791. }
  15792. })
  15793. .bind('resMixinMake', function() {
  15794. setColwidth();
  15795. })
  15796. .bind('tmbreload', function(e) {
  15797. var imgs = {},
  15798. files = (e.data && e.data.files)? e.data.files : null;
  15799. $.each(files, function(i, f) {
  15800. if (f.tmb && f.tmb != '1') {
  15801. imgs[f.hash] = f.tmb;
  15802. }
  15803. });
  15804. if (Object.keys(imgs).length) {
  15805. attachThumbnails(imgs, true);
  15806. }
  15807. })
  15808. .add(function(e) {
  15809. var regex = query? new RegExp(query.replace(/([\\*\;\.\?\[\]\{\}\(\)\^\$\-\|])/g, '\\$1'), 'i') : null,
  15810. mime = fm.searchStatus.mime,
  15811. inSearch = fm.searchStatus.state > 1,
  15812. phash = inSearch && fm.searchStatus.target? fm.searchStatus.target : fm.cwd().hash,
  15813. curPath = fm.path(phash),
  15814. inTarget = function(f) {
  15815. var res, parents, path;
  15816. res = (f.phash === phash);
  15817. if (!res && inSearch) {
  15818. path = f.path || fm.path(f.hash);
  15819. res = (curPath && path.indexOf(curPath) === 0);
  15820. if (! res && fm.searchStatus.mixed) {
  15821. res = $.grep(fm.searchStatus.mixed, function(vid) { return f.hash.indexOf(vid) === 0? true : false; }).length? true : false;
  15822. }
  15823. }
  15824. if (res && inSearch) {
  15825. if (mime) {
  15826. res = (f.mime.indexOf(mime) === 0);
  15827. } else {
  15828. res = (f.name.match(regex) || (f.i18 && f.i18.match(regex)))? true : false;
  15829. }
  15830. }
  15831. return res;
  15832. },
  15833. files = $.grep(e.data.added || [], function(f) { return inTarget(f)? true : false ;});
  15834. add(files);
  15835. if (fm.searchStatus.state === 2) {
  15836. $.each(files, function(i, f) {
  15837. if ($.inArray(f.hash, cwdHashes) === -1) {
  15838. cwdHashes.push(f.hash);
  15839. }
  15840. });
  15841. fm.trigger('cwdhasheschange', cwdHashes);
  15842. }
  15843. list && resize();
  15844. wrapper.trigger(scrollEvent);
  15845. })
  15846. .change(function(e) {
  15847. var phash = fm.cwd().hash,
  15848. sel = fm.selected(),
  15849. files, added;
  15850. if (query) {
  15851. $.each(e.data.changed || [], function(i, file) {
  15852. if (fm.cwdHash2Elm(file.hash).length) {
  15853. remove([file.hash]);
  15854. add([file], 'change');
  15855. $.inArray(file.hash, sel) !== -1 && selectFile(file.hash);
  15856. added = true;
  15857. }
  15858. });
  15859. } else {
  15860. $.each($.grep(e.data.changed || [], function(f) { return f.phash == phash ? true : false; }), function(i, file) {
  15861. if (fm.cwdHash2Elm(file.hash).length) {
  15862. remove([file.hash]);
  15863. add([file], 'change');
  15864. $.inArray(file.hash, sel) !== -1 && selectFile(file.hash);
  15865. added = true;
  15866. }
  15867. });
  15868. }
  15869. if (added) {
  15870. fm.trigger('cwdhasheschange', cwdHashes);
  15871. list && resize();
  15872. wrapper.trigger(scrollEvent);
  15873. }
  15874. trigger();
  15875. })
  15876. .remove(function(e) {
  15877. var place = list ? cwd.find('tbody') : cwd;
  15878. remove(e.data.removed || []);
  15879. trigger();
  15880. if (buffer.length < 1 && place.children(fileSelector + (options.oldSchool? ':not(.elfinder-cwd-parent)' : '')).length < 1) {
  15881. wz.addClass('elfinder-cwd-wrapper-empty');
  15882. selectCheckbox && selectAllCheckbox.find('input').prop('checked', false);
  15883. bottomMarker.hide();
  15884. wrapper.off(scrollEvent, render);
  15885. resize();
  15886. } else {
  15887. bottomMarkerShow(place);
  15888. wrapper.trigger(scrollEvent);
  15889. }
  15890. })
  15891. // select dragged file if no selected, disable selectable
  15892. .dragstart(function(e) {
  15893. var target = $(e.data.target),
  15894. oe = e.data.originalEvent;
  15895. if (target.hasClass(clFile)) {
  15896. if (!target.hasClass(clSelected)) {
  15897. !(oe.ctrlKey || oe.metaKey || oe.shiftKey) && unselectAll({ notrigger: true });
  15898. target.trigger(evtSelect);
  15899. trigger();
  15900. }
  15901. }
  15902. cwd.removeClass(clDisabled).data('selectable') && cwd.selectable('disable');
  15903. selectLock = true;
  15904. })
  15905. // enable selectable
  15906. .dragstop(function() {
  15907. cwd.data('selectable') && cwd.selectable('enable');
  15908. selectLock = false;
  15909. })
  15910. .bind('lockfiles unlockfiles selectfiles unselectfiles', function(e) {
  15911. var events = {
  15912. lockfiles : evtDisable ,
  15913. unlockfiles : evtEnable ,
  15914. selectfiles : evtSelect,
  15915. unselectfiles : evtUnselect },
  15916. event = events[e.type],
  15917. files = e.data.files || [],
  15918. l = files.length,
  15919. helper = e.data.helper || $(),
  15920. parents, ctr, add;
  15921. if (l > 0) {
  15922. parents = fm.parents(files[0]);
  15923. }
  15924. if (event === evtSelect || event === evtUnselect) {
  15925. add = (event === evtSelect),
  15926. $.each(files, function(i, hash) {
  15927. var all = cwd.hasClass('elfinder-cwd-allselected');
  15928. if (! selectedFiles[hash]) {
  15929. add && (selectedFiles[hash] = true);
  15930. } else {
  15931. if (all) {
  15932. selectCheckbox && selectAllCheckbox.children('input').prop('checked', false);
  15933. cwd.removeClass('elfinder-cwd-allselected');
  15934. all = false;
  15935. }
  15936. ! add && delete selectedFiles[hash];
  15937. }
  15938. });
  15939. }
  15940. if (!helper.data('locked')) {
  15941. while (l--) {
  15942. try {
  15943. fm.cwdHash2Elm(files[l]).trigger(event);
  15944. } catch(e) {}
  15945. }
  15946. ! e.data.inselect && trigger();
  15947. }
  15948. if (wrapper.data('dropover') && parents.indexOf(wrapper.data('dropover')) !== -1) {
  15949. ctr = e.type !== 'lockfiles';
  15950. helper.toggleClass('elfinder-drag-helper-plus', ctr);
  15951. wrapper.toggleClass(clDropActive, ctr);
  15952. }
  15953. })
  15954. // select new files after some actions
  15955. .bind('mkdir mkfile duplicate upload rename archive extract paste multiupload', function(e) {
  15956. if (e.type == 'upload' && e.data._multiupload) return;
  15957. var phash = fm.cwd().hash, files;
  15958. unselectAll({ notrigger: true });
  15959. $.each((e.data.added || []).concat(e.data.changed || []), function(i, file) {
  15960. file && file.phash == phash && selectFile(file.hash);
  15961. });
  15962. trigger();
  15963. })
  15964. .shortcut({
  15965. pattern :'ctrl+a',
  15966. description : 'selectall',
  15967. callback : selectAll
  15968. })
  15969. .shortcut({
  15970. pattern :'ctrl+shift+i',
  15971. description : 'selectinvert',
  15972. callback : selectInvert
  15973. })
  15974. .shortcut({
  15975. pattern : 'left right up down shift+left shift+right shift+up shift+down',
  15976. description : 'selectfiles',
  15977. type : 'keydown' , //fm.UA.Firefox || fm.UA.Opera ? 'keypress' : 'keydown',
  15978. callback : function(e) { select(e.keyCode, e.shiftKey); }
  15979. })
  15980. .shortcut({
  15981. pattern : 'home',
  15982. description : 'selectffile',
  15983. callback : function(e) {
  15984. unselectAll({ notrigger: true });
  15985. scrollToView(cwd.find('[id]:first').trigger(evtSelect));
  15986. trigger();
  15987. }
  15988. })
  15989. .shortcut({
  15990. pattern : 'end',
  15991. description : 'selectlfile',
  15992. callback : function(e) {
  15993. unselectAll({ notrigger: true });
  15994. scrollToView(cwd.find('[id]:last').trigger(evtSelect)) ;
  15995. trigger();
  15996. }
  15997. })
  15998. .shortcut({
  15999. pattern : 'page_up',
  16000. description : 'pageTurning',
  16001. callback : function(e) {
  16002. if (bufferExt.itemH) {
  16003. wrapper.scrollTop(
  16004. Math.round(
  16005. wrapper.scrollTop()
  16006. - (Math.floor((wrapper.height() + (list? bufferExt.itemH * -1 : 16)) / bufferExt.itemH)) * bufferExt.itemH
  16007. )
  16008. );
  16009. }
  16010. }
  16011. }).shortcut({
  16012. pattern : 'page_down',
  16013. description : 'pageTurning',
  16014. callback : function(e) {
  16015. if (bufferExt.itemH) {
  16016. wrapper.scrollTop(
  16017. Math.round(
  16018. wrapper.scrollTop()
  16019. + (Math.floor((wrapper.height() + (list? bufferExt.itemH * -1 : 16)) / bufferExt.itemH)) * bufferExt.itemH
  16020. )
  16021. );
  16022. }
  16023. }
  16024. });
  16025. });
  16026. // fm.timeEnd('cwdLoad')
  16027. return this;
  16028. };
  16029. /*
  16030. * File: /js/ui/dialog.js
  16031. */
  16032. /**
  16033. * @class elFinder dialog
  16034. *
  16035. * @author Dmitry (dio) Levashov
  16036. **/
  16037. $.fn.elfinderdialog = function(opts, fm) {
  16038. var platformWin = (window.navigator.platform.indexOf('Win') != -1),
  16039. delta = {},
  16040. syncSize = { enabled: false, width: false, height: false, defaultSize: null },
  16041. fitSize = function(dialog) {
  16042. var opts, node;
  16043. if (syncSize.enabled) {
  16044. node = fm.options.dialogContained? elfNode : $(window);
  16045. opts = {
  16046. maxWidth : syncSize.width? node.width() - delta.width : null,
  16047. maxHeight: syncSize.height? node.height() - delta.height : null
  16048. };
  16049. Object.assign(restoreStyle, opts);
  16050. dialog.css(opts).trigger('resize');
  16051. if (dialog.data('hasResizable') && (dialog.resizable('option', 'maxWidth') < opts.maxWidth || dialog.resizable('option', 'maxHeight') < opts.maxHeight)) {
  16052. dialog.resizable('option', opts);
  16053. }
  16054. }
  16055. },
  16056. syncFunc = function(e) {
  16057. var dialog = e.data;
  16058. syncTm && cancelAnimationFrame(syncTm);
  16059. syncTm = requestAnimationFrame(function() {
  16060. var opts, offset;
  16061. if (syncSize.enabled) {
  16062. fitSize(dialog);
  16063. }
  16064. });
  16065. },
  16066. checkEditing = function() {
  16067. var cldialog = 'elfinder-dialog',
  16068. dialogs = elfNode.children('.' + cldialog + '.' + fm.res('class', 'editing') + ':visible');
  16069. fm[dialogs.length? 'disable' : 'enable']();
  16070. },
  16071. propagationEvents = {},
  16072. syncTm, dialog, elfNode, restoreStyle;
  16073. if (fm && fm.ui) {
  16074. elfNode = fm.getUI();
  16075. } else {
  16076. elfNode = this.closest('.elfinder');
  16077. if (! fm) {
  16078. fm = elfNode.elfinder('instance');
  16079. }
  16080. }
  16081. if (typeof opts === 'string') {
  16082. if ((dialog = this.closest('.ui-dialog')).length) {
  16083. if (opts === 'open') {
  16084. if (dialog.css('display') === 'none') {
  16085. // Need dialog.show() and hide() to detect elements size in open() callbacks
  16086. dialog.trigger('posinit').show().trigger('open').hide();
  16087. dialog.fadeIn(120, function() {
  16088. fm.trigger('dialogopened', {dialog: dialog});
  16089. });
  16090. }
  16091. } else if (opts === 'close' || opts === 'destroy') {
  16092. dialog.stop(true);
  16093. if (dialog.is(':visible') || elfNode.is(':hidden')) {
  16094. dialog.trigger('close');
  16095. fm.trigger('dialogclosed', {dialog: dialog});
  16096. }
  16097. if (opts === 'destroy') {
  16098. dialog.remove();
  16099. fm.trigger('dialogremoved', {dialog: dialog});
  16100. }
  16101. } else if (opts === 'toTop') {
  16102. dialog.trigger('totop');
  16103. fm.trigger('dialogtotoped', {dialog: dialog});
  16104. } else if (opts === 'posInit') {
  16105. dialog.trigger('posinit');
  16106. fm.trigger('dialogposinited', {dialog: dialog});
  16107. } else if (opts === 'tabstopsInit') {
  16108. dialog.trigger('tabstopsInit');
  16109. fm.trigger('dialogtabstopsinited', {dialog: dialog});
  16110. } else if (opts === 'checkEditing') {
  16111. checkEditing();
  16112. }
  16113. }
  16114. return this;
  16115. }
  16116. opts = Object.assign({}, $.fn.elfinderdialog.defaults, opts);
  16117. if (opts.allowMinimize && opts.allowMinimize === 'auto') {
  16118. opts.allowMinimize = this.find('textarea,input').length? true : false;
  16119. }
  16120. opts.openMaximized = opts.allowMinimize && opts.openMaximized;
  16121. if (opts.headerBtnPos && opts.headerBtnPos === 'auto') {
  16122. opts.headerBtnPos = platformWin? 'right' : 'left';
  16123. }
  16124. if (opts.headerBtnOrder && opts.headerBtnOrder === 'auto') {
  16125. opts.headerBtnOrder = platformWin? 'close:maximize:minimize' : 'close:minimize:maximize';
  16126. }
  16127. if (opts.modal && opts.allowMinimize) {
  16128. opts.allowMinimize = false;
  16129. }
  16130. if (fm.options.dialogContained) {
  16131. syncSize.width = syncSize.height = syncSize.enabled = true;
  16132. } else {
  16133. syncSize.width = (opts.maxWidth === 'window');
  16134. syncSize.height = (opts.maxHeight === 'window');
  16135. if (syncSize.width || syncSize.height) {
  16136. syncSize.enabled = true;
  16137. }
  16138. }
  16139. propagationEvents = fm.arrayFlip(opts.propagationEvents, true);
  16140. this.filter(':not(.ui-dialog-content)').each(function() {
  16141. var self = $(this).addClass('ui-dialog-content ui-widget-content'),
  16142. clactive = 'elfinder-dialog-active',
  16143. cldialog = 'elfinder-dialog',
  16144. clnotify = 'elfinder-dialog-notify',
  16145. clhover = 'ui-state-hover',
  16146. cltabstop = 'elfinder-tabstop',
  16147. cl1stfocus = 'elfinder-focus',
  16148. clmodal = 'elfinder-dialog-modal',
  16149. id = parseInt(Math.random()*1000000),
  16150. titlebar = $('<div class="ui-dialog-titlebar ui-widget-header ui-corner-top ui-helper-clearfix"><span class="elfinder-dialog-title">'+opts.title+'</span></div>'),
  16151. buttonset = $('<div class="ui-dialog-buttonset"/>'),
  16152. buttonpane = $('<div class=" ui-helper-clearfix ui-dialog-buttonpane ui-widget-content"/>')
  16153. .append(buttonset),
  16154. btnWidth = 0,
  16155. btnCnt = 0,
  16156. tabstops = $(),
  16157. evCover = $('<div style="width:100%;height:100%;position:absolute;top:0px;left:0px;"/>').hide(),
  16158. numberToTel = function() {
  16159. if (opts.optimizeNumber) {
  16160. dialog.find('input[type=number]').each(function() {
  16161. $(this).attr('inputmode', 'numeric');
  16162. $(this).attr('pattern', '[0-9]*');
  16163. });
  16164. }
  16165. },
  16166. tabstopsInit = function() {
  16167. tabstops = dialog.find('.'+cltabstop);
  16168. if (tabstops.length) {
  16169. tabstops.attr('tabindex', '-1');
  16170. if (! tabstops.filter('.'+cl1stfocus).length) {
  16171. buttonset.children('.'+cltabstop+':'+(platformWin? 'first' : 'last')).addClass(cl1stfocus);
  16172. }
  16173. }
  16174. },
  16175. tabstopNext = function(cur) {
  16176. var elms = tabstops.filter(':visible:enabled'),
  16177. node = cur? null : elms.filter('.'+cl1stfocus+':first');
  16178. if (! node || ! node.length) {
  16179. node = elms.first();
  16180. }
  16181. if (cur) {
  16182. $.each(elms, function(i, elm) {
  16183. if (elm === cur && elms[i+1]) {
  16184. node = elms.eq(i+1);
  16185. return false;
  16186. }
  16187. });
  16188. }
  16189. return node;
  16190. },
  16191. tabstopPrev = function(cur) {
  16192. var elms = tabstops.filter(':visible:enabled'),
  16193. node = elms.last();
  16194. $.each(elms, function(i, elm) {
  16195. if (elm === cur && elms[i-1]) {
  16196. node = elms.eq(i-1);
  16197. return false;
  16198. }
  16199. });
  16200. return node;
  16201. },
  16202. makeHeaderBtn = function() {
  16203. $.each(opts.headerBtnOrder.split(':').reverse(), function(i, v) {
  16204. headerBtns[v] && headerBtns[v]();
  16205. });
  16206. if (platformWin) {
  16207. titlebar.children('.elfinder-titlebar-button').addClass('elfinder-titlebar-button-right');
  16208. }
  16209. },
  16210. headerBtns = {
  16211. close: function() {
  16212. 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>')
  16213. .on('mousedown', function(e) {
  16214. e.preventDefault();
  16215. e.stopPropagation();
  16216. self.elfinderdialog('close');
  16217. })
  16218. );
  16219. },
  16220. maximize: function() {
  16221. if (opts.allowMaximize) {
  16222. dialog.on('resize', function(e, data) {
  16223. var full, elm;
  16224. e.preventDefault();
  16225. e.stopPropagation();
  16226. if (data && data.maximize) {
  16227. elm = titlebar.find('.elfinder-titlebar-full');
  16228. full = (data.maximize === 'on');
  16229. elm.children('span.ui-icon')
  16230. .toggleClass('ui-icon-plusthick', ! full)
  16231. .toggleClass('ui-icon-arrowreturnthick-1-s', full);
  16232. if (full) {
  16233. try {
  16234. dialog.hasClass('ui-draggable') && dialog.draggable('disable');
  16235. dialog.hasClass('ui-resizable') && dialog.resizable('disable');
  16236. } catch(e) {}
  16237. self.css('width', '100%').css('height', dialog.height() - dialog.children('.ui-dialog-titlebar').outerHeight(true) - buttonpane.outerHeight(true));
  16238. } else {
  16239. self.attr('style', elm.data('style'));
  16240. elm.removeData('style');
  16241. posCheck();
  16242. try {
  16243. dialog.hasClass('ui-draggable') && dialog.draggable('enable');
  16244. dialog.hasClass('ui-resizable') && dialog.resizable('enable');
  16245. } catch(e) {}
  16246. }
  16247. dialog.trigger('resize', {init: true});
  16248. }
  16249. });
  16250. titlebar.prepend($('<span class="ui-widget-header ui-corner-all elfinder-titlebar-button elfinder-titlebar-full"><span class="ui-icon ui-icon-plusthick"/></span>')
  16251. .on('mousedown', function(e) {
  16252. var elm = $(this);
  16253. e.preventDefault();
  16254. e.stopPropagation();
  16255. if (!dialog.hasClass('elfinder-maximized') && typeof elm.data('style') === 'undefined') {
  16256. self.height(self.height());
  16257. elm.data('style', self.attr('style') || '');
  16258. }
  16259. fm.toggleMaximize(dialog);
  16260. typeof(opts.maximize) === 'function' && opts.maximize.call(self[0]);
  16261. })
  16262. );
  16263. }
  16264. },
  16265. minimize: function() {
  16266. var btn, mnode, doffset;
  16267. if (opts.allowMinimize) {
  16268. btn = $('<span class="ui-widget-header ui-corner-all elfinder-titlebar-button elfinder-titlebar-minimize"><span class="ui-icon ui-icon-minusthick"/></span>')
  16269. .on('mousedown', function(e) {
  16270. var $this = $(this),
  16271. tray = fm.getUI('bottomtray'),
  16272. dumStyle = { width: 70, height: 24 },
  16273. dum = $('<div/>').css(dumStyle).addClass(dialog.get(0).className + ' elfinder-dialog-minimized'),
  16274. pos = {};
  16275. e.preventDefault();
  16276. e.stopPropagation();
  16277. if (!dialog.data('minimized')) {
  16278. // minimize
  16279. doffset = dialog.data('minimized', true).position();
  16280. mnode = dialog.clone().on('mousedown', function() {
  16281. $this.trigger('mousedown');
  16282. }).removeClass('ui-draggable ui-resizable elfinder-frontmost');
  16283. tray.append(dum);
  16284. Object.assign(pos, dum.offset(), dumStyle);
  16285. dum.remove();
  16286. mnode.height(dialog.height()).children('.ui-dialog-content:first').empty();
  16287. fm.toHide(dialog.before(mnode));
  16288. mnode.children('.ui-dialog-content:first,.ui-dialog-buttonpane,.ui-resizable-handle').remove();
  16289. mnode.find('.elfinder-titlebar-minimize,.elfinder-titlebar-full').remove();
  16290. mnode.find('.ui-dialog-titlebar-close').on('mousedown', function(e) {
  16291. e.stopPropagation();
  16292. e.preventDefault();
  16293. mnode.remove();
  16294. dialog.show();
  16295. self.elfinderdialog('close');
  16296. });
  16297. mnode.animate(pos, function() {
  16298. mnode.attr('style', '')
  16299. .css({ maxWidth: dialog.width() })
  16300. .addClass('elfinder-dialog-minimized')
  16301. .appendTo(tray);
  16302. checkEditing();
  16303. typeof(opts.minimize) === 'function' && opts.minimize.call(self[0]);
  16304. });
  16305. } else {
  16306. //restore
  16307. dialog.removeData('minimized').before(mnode.css(Object.assign({'position': 'absolute'}, mnode.offset())));
  16308. fm.toFront(mnode);
  16309. mnode.animate(Object.assign({ width: dialog.width(), height: dialog.height() }, doffset), function() {
  16310. dialog.show();
  16311. fm.toFront(dialog);
  16312. mnode.remove();
  16313. posCheck();
  16314. checkEditing();
  16315. dialog.trigger('resize', {init: true});
  16316. typeof(opts.minimize) === 'function' && opts.minimize.call(self[0]);
  16317. });
  16318. }
  16319. });
  16320. titlebar.on('dblclick', function(e) {
  16321. $(this).children('.elfinder-titlebar-minimize').trigger('mousedown');
  16322. }).prepend(btn);
  16323. dialog.on('togleminimize', function() {
  16324. btn.trigger('mousedown');
  16325. });
  16326. }
  16327. }
  16328. },
  16329. dialog = $('<div class="ui-front ui-dialog ui-widget ui-widget-content ui-corner-all ui-draggable std42-dialog touch-punch '+cldialog+' '+opts.cssClass+'"/>')
  16330. .hide()
  16331. .append(self)
  16332. .appendTo(elfNode)
  16333. .draggable({
  16334. containment : fm.options.dialogContained? elfNode : null,
  16335. handle : '.ui-dialog-titlebar',
  16336. start : function() {
  16337. evCover.show();
  16338. },
  16339. drag : function(e, ui) {
  16340. var top = ui.offset.top,
  16341. left = ui.offset.left;
  16342. if (top < 0) {
  16343. ui.position.top = ui.position.top - top;
  16344. }
  16345. if (left < 0) {
  16346. ui.position.left = ui.position.left - left;
  16347. }
  16348. if (fm.options.dialogContained) {
  16349. ui.position.top < 0 && (ui.position.top = 0);
  16350. ui.position.left < 0 && (ui.position.left = 0);
  16351. }
  16352. },
  16353. stop : function(e, ui) {
  16354. evCover.hide();
  16355. dialog.css({height : opts.height});
  16356. self.data('draged', true);
  16357. }
  16358. })
  16359. .css({
  16360. width : opts.width,
  16361. height : opts.height,
  16362. minWidth : opts.minWidth,
  16363. minHeight : opts.minHeight,
  16364. maxWidth : opts.maxWidth,
  16365. maxHeight : opts.maxHeight
  16366. })
  16367. .on('touchstart touchmove touchend click dblclick mouseup mouseenter mouseleave mouseout mouseover mousemove', function(e) {
  16368. // stopPropagation of user action events
  16369. !propagationEvents[e.type] && e.stopPropagation();
  16370. })
  16371. .on('mousedown', function(e) {
  16372. !propagationEvents[e.type] && e.stopPropagation();
  16373. requestAnimationFrame(function() {
  16374. if (dialog.is(':visible') && !dialog.hasClass('elfinder-frontmost')) {
  16375. toFocusNode = $(':focus');
  16376. if (!toFocusNode.length) {
  16377. toFocusNode = void(0);
  16378. }
  16379. dialog.trigger('totop');
  16380. }
  16381. });
  16382. })
  16383. .on('open', function() {
  16384. dialog.data('margin-y', self.outerHeight(true) - self.height());
  16385. if (syncSize.enabled) {
  16386. if (opts.height && opts.height !== 'auto') {
  16387. dialog.trigger('resize', {init: true});
  16388. }
  16389. if (!syncSize.defaultSize) {
  16390. syncSize.defaultSize = { width: self.width(), height: self.height() };
  16391. }
  16392. fitSize(dialog);
  16393. dialog.trigger('resize').trigger('posinit');
  16394. elfNode.on('resize.'+fm.namespace, dialog, syncFunc);
  16395. }
  16396. if (!dialog.hasClass(clnotify)) {
  16397. elfNode.children('.'+cldialog+':visible:not(.'+clnotify+')').each(function() {
  16398. var d = $(this),
  16399. top = parseInt(d.css('top')),
  16400. left = parseInt(d.css('left')),
  16401. _top = parseInt(dialog.css('top')),
  16402. _left = parseInt(dialog.css('left')),
  16403. ct = Math.abs(top - _top) < 10,
  16404. cl = Math.abs(left - _left) < 10;
  16405. if (d[0] != dialog[0] && (ct || cl)) {
  16406. dialog.css({
  16407. top : ct ? (top + 10) : _top,
  16408. left : cl ? (left + 10) : _left
  16409. });
  16410. }
  16411. });
  16412. }
  16413. if (dialog.data('modal')) {
  16414. dialog.addClass(clmodal);
  16415. fm.getUI('overlay').elfinderoverlay('show');
  16416. }
  16417. dialog.trigger('totop');
  16418. opts.openMaximized && fm.toggleMaximize(dialog);
  16419. fm.trigger('dialogopen', {dialog: dialog});
  16420. typeof(opts.open) == 'function' && $.proxy(opts.open, self[0])();
  16421. if (opts.closeOnEscape) {
  16422. $(document).on('keydown.'+id, function(e) {
  16423. if (e.keyCode == $.ui.keyCode.ESCAPE && dialog.hasClass('elfinder-frontmost')) {
  16424. self.elfinderdialog('close');
  16425. }
  16426. });
  16427. }
  16428. dialog.hasClass(fm.res('class', 'editing')) && checkEditing();
  16429. })
  16430. .on('close', function(e) {
  16431. var dialogs, dfd;
  16432. if (opts.beforeclose && typeof opts.beforeclose === 'function') {
  16433. dfd = opts.beforeclose();
  16434. if (!dfd || !dfd.promise) {
  16435. dfd = !dfd? $.Deferred().reject() : $.Deferred().resolve();
  16436. }
  16437. } else {
  16438. dfd = $.Deferred().resolve();
  16439. }
  16440. dfd.done(function() {
  16441. syncSize.enabled && elfNode.off('resize.'+fm.namespace, syncFunc);
  16442. if (opts.closeOnEscape) {
  16443. $(document).off('keyup.'+id);
  16444. }
  16445. if (opts.allowMaximize) {
  16446. fm.toggleMaximize(dialog, false);
  16447. }
  16448. fm.toHide(dialog);
  16449. dialog.data('modal') && fm.getUI('overlay').elfinderoverlay('hide');
  16450. if (typeof(opts.close) == 'function') {
  16451. $.proxy(opts.close, self[0])();
  16452. }
  16453. if (opts.destroyOnClose && dialog.parent().length) {
  16454. dialog.hide().remove();
  16455. }
  16456. // get focus to next dialog
  16457. dialogs = elfNode.children('.'+cldialog+':visible');
  16458. dialog.hasClass(fm.res('class', 'editing')) && checkEditing();
  16459. });
  16460. })
  16461. .on('totop frontmost', function() {
  16462. var s = fm.storage('autoFocusDialog');
  16463. dialog.data('focusOnMouseOver', s? (s > 0) : fm.options.uiOptions.dialog.focusOnMouseOver);
  16464. if (dialog.data('minimized')) {
  16465. titlebar.children('.elfinder-titlebar-minimize').trigger('mousedown');
  16466. }
  16467. if (!dialog.data('modal') && fm.getUI('overlay').is(':visible')) {
  16468. fm.getUI('overlay').before(dialog);
  16469. } else {
  16470. fm.toFront(dialog);
  16471. }
  16472. elfNode.children('.'+cldialog+':not(.'+clmodal+')').removeClass(clactive);
  16473. dialog.addClass(clactive);
  16474. ! fm.UA.Mobile && (toFocusNode || tabstopNext()).trigger('focus');
  16475. toFocusNode = void(0);
  16476. })
  16477. .on('posinit', function() {
  16478. var css = opts.position,
  16479. nodeOffset, minTop, minLeft, outerSize, win, winSize, nodeFull;
  16480. if (dialog.hasClass('elfinder-maximized')) {
  16481. return;
  16482. }
  16483. if (! css && ! dialog.data('resizing')) {
  16484. nodeFull = elfNode.hasClass('elfinder-fullscreen') || fm.options.enableAlways;
  16485. dialog.css(nodeFull? {
  16486. maxWidth : '100%',
  16487. maxHeight : '100%',
  16488. overflow : 'auto'
  16489. } : restoreStyle);
  16490. if (fm.UA.Mobile && !nodeFull && dialog.data('rotated') === fm.UA.Rotated) {
  16491. return;
  16492. }
  16493. dialog.data('rotated', fm.UA.Rotated);
  16494. win = $(window);
  16495. nodeOffset = elfNode.offset();
  16496. outerSize = {
  16497. width : dialog.outerWidth(true),
  16498. height: dialog.outerHeight(true)
  16499. };
  16500. outerSize.right = nodeOffset.left + outerSize.width;
  16501. outerSize.bottom = nodeOffset.top + outerSize.height;
  16502. winSize = {
  16503. scrLeft: win.scrollLeft(),
  16504. scrTop : win.scrollTop(),
  16505. width : win.width(),
  16506. height : win.height()
  16507. };
  16508. winSize.right = winSize.scrLeft + winSize.width;
  16509. winSize.bottom = winSize.scrTop + winSize.height;
  16510. if (fm.options.dialogContained || nodeFull) {
  16511. minTop = 0;
  16512. minLeft = 0;
  16513. } else {
  16514. minTop = nodeOffset.top * -1 + winSize.scrTop;
  16515. minLeft = nodeOffset.left * -1 + winSize.scrLeft;
  16516. }
  16517. css = {
  16518. top : outerSize.height >= winSize.height? minTop : Math.max(minTop, parseInt((elfNode.height() - outerSize.height)/2 - 42)),
  16519. left : outerSize.width >= winSize.width ? minLeft : Math.max(minLeft, parseInt((elfNode.width() - outerSize.width)/2))
  16520. };
  16521. if (outerSize.right + css.left > winSize.right) {
  16522. css.left = Math.max(minLeft, winSize.right - outerSize.right);
  16523. }
  16524. if (outerSize.bottom + css.top > winSize.bottom) {
  16525. css.top = Math.max(minTop, winSize.bottom - outerSize.bottom);
  16526. }
  16527. }
  16528. if (opts.absolute) {
  16529. css.position = 'absolute';
  16530. }
  16531. css && dialog.css(css);
  16532. })
  16533. .on('resize', function(e, data) {
  16534. var oh = 0, init = data && data.init, h, minH, maxH;
  16535. if ((data && (data.minimize || data.maxmize)) || dialog.data('minimized')) {
  16536. return;
  16537. }
  16538. e.stopPropagation();
  16539. e.preventDefault();
  16540. dialog.children('.ui-widget-header,.ui-dialog-buttonpane').each(function() {
  16541. oh += $(this).outerHeight(true);
  16542. });
  16543. if (!init && syncSize.enabled && !e.originalEvent && !dialog.hasClass('elfinder-maximized')) {
  16544. h = dialog.height();
  16545. minH = dialog.css('min-height') || h;
  16546. maxH = dialog.css('max-height') || h;
  16547. if (minH.match(/%/)) {
  16548. minH = Math.floor((parseInt(minH) / 100) * dialog.parent().height());
  16549. } else {
  16550. minH = parseInt(minH);
  16551. }
  16552. if (maxH.match(/%/)) {
  16553. maxH = Math.floor((parseInt(maxH) / 100) * dialog.parent().height());
  16554. } else {
  16555. maxH = parseInt(maxH);
  16556. }
  16557. h = Math.min(syncSize.defaultSize.height, Math.max(maxH, minH) - oh - dialog.data('margin-y'));
  16558. } else {
  16559. h = dialog.height() - oh - dialog.data('margin-y');
  16560. }
  16561. self.height(h);
  16562. if (init) {
  16563. return;
  16564. }
  16565. posCheck();
  16566. minH = self.height();
  16567. minH = (h < minH)? (minH + oh + dialog.data('margin-y')) : opts.minHeight;
  16568. dialog.css('min-height', minH);
  16569. dialog.data('hasResizable') && dialog.resizable('option', { minHeight: minH });
  16570. if (typeof(opts.resize) === 'function') {
  16571. $.proxy(opts.resize, self[0])(e, data);
  16572. }
  16573. })
  16574. .on('tabstopsInit', tabstopsInit)
  16575. .on('focus', '.'+cltabstop, function() {
  16576. $(this).addClass(clhover).parent('label').addClass(clhover);
  16577. this.id && $(this).parent().find('label[for='+this.id+']').addClass(clhover);
  16578. })
  16579. .on('click', 'select.'+cltabstop, function() {
  16580. var node = $(this);
  16581. node.data('keepFocus')? node.removeData('keepFocus') : node.data('keepFocus', true);
  16582. })
  16583. .on('blur', '.'+cltabstop, function() {
  16584. $(this).removeClass(clhover).removeData('keepFocus').parent('label').removeClass(clhover);
  16585. this.id && $(this).parent().find('label[for='+this.id+']').removeClass(clhover);
  16586. })
  16587. .on('mouseenter mouseleave', '.'+cltabstop+',label', function(e) {
  16588. var $this = $(this), labelfor;
  16589. if (this.nodeName === 'LABEL') {
  16590. if (!$this.children('.'+cltabstop).length && (!(labelfor = $this.attr('for')) || !$('#'+labelfor).hasClass(cltabstop))) {
  16591. return;
  16592. }
  16593. }
  16594. if (opts.btnHoverFocus && dialog.data('focusOnMouseOver')) {
  16595. if (e.type === 'mouseenter' && ! $(':focus').data('keepFocus')) {
  16596. $this.trigger('focus');
  16597. }
  16598. } else {
  16599. $this.toggleClass(clhover, e.type == 'mouseenter');
  16600. }
  16601. })
  16602. .on('keydown', '.'+cltabstop, function(e) {
  16603. var $this = $(this),
  16604. esc, move, moveTo;
  16605. if ($this.is(':focus')) {
  16606. esc = e.keyCode === $.ui.keyCode.ESCAPE;
  16607. if (e.keyCode === $.ui.keyCode.ENTER) {
  16608. e.preventDefault();
  16609. $this.trigger('click');
  16610. } else if (((e.keyCode === $.ui.keyCode.TAB) && e.shiftKey) || e.keyCode === $.ui.keyCode.LEFT || e.keyCode == $.ui.keyCode.UP) {
  16611. move = 'prev';
  16612. } else if (e.keyCode === $.ui.keyCode.TAB || e.keyCode == $.ui.keyCode.RIGHT || e.keyCode == $.ui.keyCode.DOWN) {
  16613. move = 'next';
  16614. }
  16615. if (move
  16616. &&
  16617. (
  16618. ($this.is('textarea') && !(e.ctrlKey || e.metaKey))
  16619. ||
  16620. ($this.is('select,span.ui-slider-handle') && e.keyCode !== $.ui.keyCode.TAB)
  16621. ||
  16622. ($this.is('input:not(:checkbox,:radio)') && (!(e.ctrlKey || e.metaKey) && e.keyCode === $.ui.keyCode[move === 'prev'? 'LEFT':'RIGHT']))
  16623. )
  16624. ) {
  16625. e.stopPropagation();
  16626. return;
  16627. }
  16628. if (!esc) {
  16629. e.stopPropagation();
  16630. } else if ($this.is('input:not(:checkbox,:radio),textarea')) {
  16631. if ($this.val() !== '') {
  16632. $this.val('');
  16633. e.stopPropagation();
  16634. }
  16635. }
  16636. if (move) {
  16637. e.preventDefault();
  16638. (move === 'prev'? tabstopPrev : tabstopNext)(this).trigger('focus');
  16639. }
  16640. }
  16641. })
  16642. .data({modal: opts.modal}),
  16643. posCheck = function() {
  16644. var node = fm.getUI(),
  16645. pos;
  16646. if (node.hasClass('elfinder-fullscreen')) {
  16647. pos = dialog.position();
  16648. dialog.css('top', Math.max(Math.min(Math.max(pos.top, 0), node.height() - 100), 0));
  16649. dialog.css('left', Math.max(Math.min(Math.max(pos.left, 0), node.width() - 200), 0));
  16650. }
  16651. },
  16652. maxSize, toFocusNode;
  16653. dialog.prepend(titlebar);
  16654. makeHeaderBtn();
  16655. $.each(opts.buttons, function(name, cb) {
  16656. var button = $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only '
  16657. +'elfinder-btncnt-'+(btnCnt++)+' '
  16658. +cltabstop
  16659. +'"><span class="ui-button-text">'+name+'</span></button>')
  16660. .on('click', $.proxy(cb, self[0]));
  16661. if (cb._cssClass) {
  16662. button.addClass(cb._cssClass);
  16663. }
  16664. if (platformWin) {
  16665. buttonset.append(button);
  16666. } else {
  16667. buttonset.prepend(button);
  16668. }
  16669. });
  16670. if (buttonset.children().length) {
  16671. dialog.append(buttonpane);
  16672. dialog.show();
  16673. buttonpane.find('button').each(function(i, btn) {
  16674. btnWidth += $(btn).outerWidth(true);
  16675. });
  16676. dialog.hide();
  16677. btnWidth += 20;
  16678. if (dialog.width() < btnWidth) {
  16679. dialog.width(btnWidth);
  16680. }
  16681. }
  16682. dialog.append(evCover);
  16683. if (syncSize.enabled) {
  16684. delta.width = dialog.outerWidth(true) - dialog.width() + ((dialog.outerWidth() - dialog.width()) / 2);
  16685. delta.height = dialog.outerHeight(true) - dialog.height() + ((dialog.outerHeight() - dialog.height()) / 2);
  16686. }
  16687. if (fm.options.dialogContained) {
  16688. maxSize = {
  16689. maxWidth: elfNode.width() - delta.width,
  16690. maxHeight: elfNode.height() - delta.height
  16691. };
  16692. opts.maxWidth = opts.maxWidth? Math.min(maxSize.maxWidth, opts.maxWidth) : maxSize.maxWidth;
  16693. opts.maxHeight = opts.maxHeight? Math.min(maxSize.maxHeight, opts.maxHeight) : maxSize.maxHeight;
  16694. dialog.css(maxSize);
  16695. }
  16696. restoreStyle = {
  16697. maxWidth : dialog.css('max-width'),
  16698. maxHeight : dialog.css('max-height'),
  16699. overflow : dialog.css('overflow')
  16700. };
  16701. if (opts.resizable) {
  16702. dialog.resizable({
  16703. minWidth : opts.minWidth,
  16704. minHeight : opts.minHeight,
  16705. maxWidth : opts.maxWidth,
  16706. maxHeight : opts.maxHeight,
  16707. start : function() {
  16708. evCover.show();
  16709. if (dialog.data('resizing') !== true && dialog.data('resizing')) {
  16710. clearTimeout(dialog.data('resizing'));
  16711. }
  16712. dialog.data('resizing', true);
  16713. },
  16714. stop : function(e, ui) {
  16715. evCover.hide();
  16716. dialog.data('resizing', setTimeout(function() {
  16717. dialog.data('resizing', false);
  16718. }, 200));
  16719. if (syncSize.enabled) {
  16720. syncSize.defaultSize = { width: self.width(), height: self.height() };
  16721. }
  16722. }
  16723. }).data('hasResizable', true);
  16724. }
  16725. numberToTel();
  16726. tabstopsInit();
  16727. typeof(opts.create) == 'function' && $.proxy(opts.create, this)();
  16728. if (opts.autoOpen) {
  16729. if (opts.open) {
  16730. requestAnimationFrame(function() {
  16731. self.elfinderdialog('open');
  16732. });
  16733. } else {
  16734. self.elfinderdialog('open');
  16735. }
  16736. }
  16737. if (opts.resize) {
  16738. fm.bind('themechange', function() {
  16739. setTimeout(function() {
  16740. dialog.data('margin-y', self.outerHeight(true) - self.height());
  16741. dialog.trigger('resize', {init: true});
  16742. }, 300);
  16743. });
  16744. }
  16745. });
  16746. return this;
  16747. };
  16748. $.fn.elfinderdialog.defaults = {
  16749. cssClass : '',
  16750. title : '',
  16751. modal : false,
  16752. resizable : true,
  16753. autoOpen : true,
  16754. closeOnEscape : true,
  16755. destroyOnClose : false,
  16756. buttons : {},
  16757. btnHoverFocus : true,
  16758. position : null,
  16759. absolute : false,
  16760. width : 320,
  16761. height : 'auto',
  16762. minWidth : 200,
  16763. minHeight : 70,
  16764. maxWidth : null,
  16765. maxHeight : null,
  16766. allowMinimize : 'auto',
  16767. allowMaximize : false,
  16768. openMaximized : false,
  16769. headerBtnPos : 'auto',
  16770. headerBtnOrder : 'auto',
  16771. optimizeNumber : true,
  16772. propagationEvents : ['mousemove', 'mouseup']
  16773. };
  16774. /*
  16775. * File: /js/ui/fullscreenbutton.js
  16776. */
  16777. /**
  16778. * @class elFinder toolbar button to switch full scrren mode.
  16779. *
  16780. * @author Naoki Sawada
  16781. **/
  16782. $.fn.elfinderfullscreenbutton = function(cmd) {
  16783. return this.each(function() {
  16784. var button = $(this).elfinderbutton(cmd),
  16785. icon = button.children('.elfinder-button-icon'),
  16786. tm;
  16787. cmd.change(function() {
  16788. tm && cancelAnimationFrame(tm);
  16789. tm = requestAnimationFrame(function() {
  16790. var fullscreen = cmd.value;
  16791. icon.addClass('elfinder-button-icon-fullscreen').toggleClass('elfinder-button-icon-unfullscreen', fullscreen);
  16792. cmd.className = fullscreen? 'unfullscreen' : '';
  16793. });
  16794. });
  16795. });
  16796. };
  16797. /*
  16798. * File: /js/ui/navbar.js
  16799. */
  16800. /**
  16801. * @class elfindernav - elFinder container for diretories tree and places
  16802. *
  16803. * @author Dmitry (dio) Levashov
  16804. **/
  16805. $.fn.elfindernavbar = function(fm, opts) {
  16806. this.not('.elfinder-navbar').each(function() {
  16807. var nav = $(this).hide().addClass('ui-state-default elfinder-navbar'),
  16808. parent = nav.css('overflow', 'hidden').parent(),
  16809. wz = parent.children('.elfinder-workzone').append(nav),
  16810. ltr = fm.direction == 'ltr',
  16811. delta, deltaW, handle, swipeHandle, autoHide, setWidth, navdock,
  16812. setWzRect = function() {
  16813. var cwd = fm.getUI('cwd'),
  16814. wz = fm.getUI('workzone'),
  16815. wzRect = wz.data('rectangle'),
  16816. cwdOffset = cwd.offset();
  16817. wz.data('rectangle', Object.assign(wzRect, { cwdEdge: (fm.direction === 'ltr')? cwdOffset.left : cwdOffset.left + cwd.width() }));
  16818. },
  16819. setDelta = function() {
  16820. nav.css('overflow', 'hidden');
  16821. delta = Math.round(nav.outerHeight() - nav.height());
  16822. deltaW = Math.round(navdock.outerWidth() - navdock.innerWidth());
  16823. nav.css('overflow', 'auto');
  16824. };
  16825. fm.one('init', function() {
  16826. navdock = fm.getUI('navdock');
  16827. var set = function() {
  16828. setDelta();
  16829. fm.bind('wzresize', function() {
  16830. var navdockH = 0;
  16831. navdock.width(nav.outerWidth() - deltaW);
  16832. if (navdock.children().length > 1) {
  16833. navdockH = navdock.outerHeight(true);
  16834. }
  16835. nav.height(wz.height() - navdockH - delta);
  16836. }).trigger('wzresize');
  16837. };
  16838. if (fm.cssloaded) {
  16839. set();
  16840. } else {
  16841. fm.one('cssloaded', set);
  16842. }
  16843. })
  16844. .one('opendone',function() {
  16845. handle && handle.trigger('resize');
  16846. nav.css('overflow', 'auto');
  16847. }).bind('themechange', setDelta);
  16848. if (fm.UA.Touch) {
  16849. autoHide = fm.storage('autoHide') || {};
  16850. if (typeof autoHide.navbar === 'undefined') {
  16851. autoHide.navbar = (opts.autoHideUA && opts.autoHideUA.length > 0 && $.grep(opts.autoHideUA, function(v){ return fm.UA[v]? true : false; }).length);
  16852. fm.storage('autoHide', autoHide);
  16853. }
  16854. if (autoHide.navbar) {
  16855. fm.one('init', function() {
  16856. if (nav.children().length) {
  16857. fm.uiAutoHide.push(function(){ nav.stop(true, true).trigger('navhide', { duration: 'slow', init: true }); });
  16858. }
  16859. });
  16860. }
  16861. fm.bind('load', function() {
  16862. if (nav.children().length) {
  16863. swipeHandle = $('<div class="elfinder-navbar-swipe-handle"/>').hide().appendTo(wz);
  16864. if (swipeHandle.css('pointer-events') !== 'none') {
  16865. swipeHandle.remove();
  16866. swipeHandle = null;
  16867. }
  16868. }
  16869. });
  16870. nav.on('navshow navhide', function(e, data) {
  16871. var mode = (e.type === 'navshow')? 'show' : 'hide',
  16872. duration = (data && data.duration)? data.duration : 'fast',
  16873. handleW = (data && data.handleW)? data.handleW : Math.max(50, fm.getUI().width() / 10);
  16874. nav.stop(true, true)[mode]({
  16875. duration: duration,
  16876. step : function() {
  16877. fm.trigger('wzresize');
  16878. },
  16879. complete: function() {
  16880. if (swipeHandle) {
  16881. if (mode === 'show') {
  16882. swipeHandle.stop(true, true).hide();
  16883. } else {
  16884. swipeHandle.width(handleW? handleW : '');
  16885. fm.resources.blink(swipeHandle, 'slowonce');
  16886. }
  16887. }
  16888. fm.trigger('navbar'+ mode);
  16889. data.init && fm.trigger('uiautohide');
  16890. setWzRect();
  16891. }
  16892. });
  16893. autoHide.navbar = (mode !== 'show');
  16894. fm.storage('autoHide', Object.assign(fm.storage('autoHide'), {navbar: autoHide.navbar}));
  16895. }).on('touchstart', function(e) {
  16896. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  16897. e.originalEvent._preventSwipeX = true;
  16898. }
  16899. });
  16900. }
  16901. if (! fm.UA.Mobile) {
  16902. handle = nav.resizable({
  16903. handles : ltr ? 'e' : 'w',
  16904. minWidth : opts.minWidth || 150,
  16905. maxWidth : opts.maxWidth || 500,
  16906. resize : function() {
  16907. fm.trigger('wzresize');
  16908. },
  16909. stop : function(e, ui) {
  16910. fm.storage('navbarWidth', ui.size.width);
  16911. setWzRect();
  16912. }
  16913. })
  16914. .on('resize scroll', function(e) {
  16915. var $this = $(this),
  16916. tm = $this.data('posinit');
  16917. e.preventDefault();
  16918. e.stopPropagation();
  16919. if (! ltr && e.type === 'resize') {
  16920. nav.css('left', 0);
  16921. }
  16922. tm && cancelAnimationFrame(tm);
  16923. $this.data('posinit', requestAnimationFrame(function() {
  16924. var offset = (fm.UA.Opera && nav.scrollLeft())? 20 : 2;
  16925. handle.css('top', 0).css({
  16926. top : parseInt(nav.scrollTop())+'px',
  16927. left : ltr ? 'auto' : parseInt(nav.scrollRight() - offset) * -1,
  16928. right: ltr ? parseInt(nav.scrollLeft() - offset) * -1 : 'auto'
  16929. });
  16930. if (e.type === 'resize') {
  16931. fm.getUI('cwd').trigger('resize');
  16932. }
  16933. }));
  16934. })
  16935. .children('.ui-resizable-handle').addClass('ui-front');
  16936. }
  16937. if (setWidth = fm.storage('navbarWidth')) {
  16938. nav.width(setWidth);
  16939. } else {
  16940. if (fm.UA.Mobile) {
  16941. fm.one(fm.cssloaded? 'init' : 'cssloaded', function() {
  16942. var set = function() {
  16943. setWidth = nav.parent().width() / 2;
  16944. if (nav.data('defWidth') > setWidth) {
  16945. nav.width(setWidth);
  16946. } else {
  16947. nav.width(nav.data('defWidth'));
  16948. }
  16949. nav.data('width', nav.width());
  16950. fm.trigger('wzresize');
  16951. };
  16952. nav.data('defWidth', nav.width());
  16953. $(window).on('resize.' + fm.namespace, set);
  16954. set();
  16955. });
  16956. }
  16957. }
  16958. });
  16959. return this;
  16960. };
  16961. /*
  16962. * File: /js/ui/navdock.js
  16963. */
  16964. /**
  16965. * @class elfindernavdock - elFinder container for preview etc at below the navbar
  16966. *
  16967. * @author Naoki Sawada
  16968. **/
  16969. $.fn.elfindernavdock = function(fm, opts) {
  16970. this.not('.elfinder-navdock').each(function() {
  16971. var self = $(this).hide().addClass('ui-state-default elfinder-navdock touch-punch'),
  16972. node = self.parent(),
  16973. wz = node.children('.elfinder-workzone').append(self),
  16974. resize = function(to, h) {
  16975. var curH = h || self.height(),
  16976. diff = to - curH,
  16977. len = Object.keys(sizeSyncs).length,
  16978. calc = len? diff / len : 0,
  16979. ovf;
  16980. if (diff) {
  16981. ovf = self.css('overflow');
  16982. self.css('overflow', 'hidden');
  16983. self.height(to);
  16984. $.each(sizeSyncs, function(id, n) {
  16985. n.height(n.height() + calc).trigger('resize.' + fm.namespace);
  16986. });
  16987. fm.trigger('wzresize');
  16988. self.css('overflow', ovf);
  16989. }
  16990. },
  16991. handle = $('<div class="ui-front ui-resizable-handle ui-resizable-n"/>').appendTo(self),
  16992. sizeSyncs = {},
  16993. resizeFn = [],
  16994. initMaxHeight = (parseInt(opts.initMaxHeight) || 50) / 100,
  16995. maxHeight = (parseInt(opts.maxHeight) || 90) / 100,
  16996. basicHeight, hasNode;
  16997. self.data('addNode', function(cNode, opts) {
  16998. var wzH = fm.getUI('workzone').height(),
  16999. imaxH = wzH * initMaxHeight,
  17000. curH, tH, mH;
  17001. opts = Object.assign({
  17002. first: false,
  17003. sizeSync: true,
  17004. init: false
  17005. }, opts);
  17006. if (!cNode.attr('id')) {
  17007. cNode.attr('id', fm.namespace+'-navdock-' + (+new Date()));
  17008. }
  17009. opts.sizeSync && (sizeSyncs[cNode.attr('id')] = cNode);
  17010. curH = self.height();
  17011. tH = curH + cNode.outerHeight(true);
  17012. if (opts.first) {
  17013. handle.after(cNode);
  17014. } else {
  17015. self.append(cNode);
  17016. }
  17017. hasNode = true;
  17018. self.resizable('enable').height(tH).show();
  17019. fm.trigger('wzresize');
  17020. if (opts.init) {
  17021. mH = fm.storage('navdockHeight');
  17022. if (mH) {
  17023. tH = mH;
  17024. } else {
  17025. tH = tH > imaxH? imaxH : tH;
  17026. }
  17027. basicHeight = tH;
  17028. }
  17029. resize(Math.min(tH, wzH * maxHeight));
  17030. return self;
  17031. }).data('removeNode', function(nodeId, appendTo) {
  17032. var cNode = $('#'+nodeId);
  17033. delete sizeSyncs[nodeId];
  17034. self.height(self.height() - $('#'+nodeId).outerHeight(true));
  17035. if (appendTo) {
  17036. if (appendTo === 'detach') {
  17037. cNode = cNode.detach();
  17038. } else {
  17039. appendTo.append(cNode);
  17040. }
  17041. } else {
  17042. cNode.remove();
  17043. }
  17044. if (self.children().length <= 1) {
  17045. hasNode = false;
  17046. self.resizable('disable').height(0).hide();
  17047. }
  17048. fm.trigger('wzresize');
  17049. return cNode;
  17050. });
  17051. if (! opts.disabled) {
  17052. fm.one('init', function() {
  17053. var ovf;
  17054. if (fm.getUI('navbar').children().not('.ui-resizable-handle').length) {
  17055. self.data('dockEnabled', true);
  17056. self.resizable({
  17057. maxHeight: fm.getUI('workzone').height() * maxHeight,
  17058. handles: { n: handle },
  17059. start: function(e, ui) {
  17060. ovf = self.css('overflow');
  17061. self.css('overflow', 'hidden');
  17062. fm.trigger('navdockresizestart', {event: e, ui: ui}, true);
  17063. },
  17064. resize: function(e, ui) {
  17065. self.css('top', '');
  17066. fm.trigger('wzresize', { inNavdockResize : true });
  17067. },
  17068. stop: function(e, ui) {
  17069. fm.trigger('navdockresizestop', {event: e, ui: ui}, true);
  17070. self.css('top', '');
  17071. basicHeight = ui.size.height;
  17072. fm.storage('navdockHeight', basicHeight);
  17073. resize(basicHeight, ui.originalSize.height);
  17074. self.css('overflow', ovf);
  17075. }
  17076. });
  17077. fm.bind('wzresize', function(e) {
  17078. var minH, maxH, h;
  17079. if (self.is(':visible')) {
  17080. maxH = fm.getUI('workzone').height() * maxHeight;
  17081. if (! e.data || ! e.data.inNavdockResize) {
  17082. h = self.height();
  17083. if (maxH < basicHeight) {
  17084. if (Math.abs(h - maxH) > 1) {
  17085. resize(maxH);
  17086. }
  17087. } else {
  17088. if (Math.abs(h - basicHeight) > 1) {
  17089. resize(basicHeight);
  17090. }
  17091. }
  17092. }
  17093. self.resizable('option', 'maxHeight', maxH);
  17094. }
  17095. }).bind('themechange', function() {
  17096. var oldH = Math.round(self.height());
  17097. requestAnimationFrame(function() {
  17098. var curH = Math.round(self.height()),
  17099. diff = oldH - curH;
  17100. if (diff !== 0) {
  17101. resize(self.height(), curH - diff);
  17102. }
  17103. });
  17104. });
  17105. }
  17106. fm.bind('navbarshow navbarhide', function(e) {
  17107. self[hasNode && e.type === 'navbarshow'? 'show' : 'hide']();
  17108. });
  17109. });
  17110. }
  17111. });
  17112. return this;
  17113. };
  17114. /*
  17115. * File: /js/ui/overlay.js
  17116. */
  17117. $.fn.elfinderoverlay = function(opts) {
  17118. var fm = this.parent().elfinder('instance'),
  17119. o, cnt, show, hide;
  17120. this.filter(':not(.elfinder-overlay)').each(function() {
  17121. opts = Object.assign({}, opts);
  17122. $(this).addClass('ui-front ui-widget-overlay elfinder-overlay')
  17123. .hide()
  17124. .on('mousedown', function(e) {
  17125. e.preventDefault();
  17126. e.stopPropagation();
  17127. })
  17128. .data({
  17129. cnt : 0,
  17130. show : typeof(opts.show) == 'function' ? opts.show : function() { },
  17131. hide : typeof(opts.hide) == 'function' ? opts.hide : function() { }
  17132. });
  17133. });
  17134. if (opts == 'show') {
  17135. o = this.eq(0);
  17136. cnt = o.data('cnt') + 1;
  17137. show = o.data('show');
  17138. fm.toFront(o);
  17139. o.data('cnt', cnt);
  17140. if (o.is(':hidden')) {
  17141. o.show();
  17142. show();
  17143. }
  17144. }
  17145. if (opts == 'hide') {
  17146. o = this.eq(0);
  17147. cnt = o.data('cnt') - 1;
  17148. hide = o.data('hide');
  17149. o.data('cnt', cnt);
  17150. if (cnt <= 0) {
  17151. o.hide();
  17152. hide();
  17153. }
  17154. }
  17155. return this;
  17156. };
  17157. /*
  17158. * File: /js/ui/panel.js
  17159. */
  17160. $.fn.elfinderpanel = function(fm) {
  17161. return this.each(function() {
  17162. var panel = $(this).addClass('elfinder-panel ui-state-default ui-corner-all'),
  17163. margin = 'margin-'+(fm.direction == 'ltr' ? 'left' : 'right');
  17164. fm.one('load', function(e) {
  17165. var navbar = fm.getUI('navbar');
  17166. panel.css(margin, parseInt(navbar.outerWidth(true)));
  17167. navbar.on('resize', function(e) {
  17168. e.preventDefault();
  17169. e.stopPropagation();
  17170. panel.is(':visible') && panel.css(margin, parseInt(navbar.outerWidth(true)));
  17171. });
  17172. });
  17173. });
  17174. };
  17175. /*
  17176. * File: /js/ui/path.js
  17177. */
  17178. /**
  17179. * @class elFinder ui
  17180. * Display current folder path in statusbar.
  17181. * Click on folder name in path - open folder
  17182. *
  17183. * @author Dmitry (dio) Levashov
  17184. **/
  17185. $.fn.elfinderpath = function(fm, options) {
  17186. return this.each(function() {
  17187. var query = '',
  17188. target = '',
  17189. mimes = [],
  17190. place = 'statusbar',
  17191. clHover= fm.res('class', 'hover'),
  17192. prefix = 'path' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-',
  17193. wzbase = $('<div class="ui-widget-header ui-helper-clearfix elfinder-workzone-path"/>'),
  17194. path = $(this).addClass('elfinder-path').html('&nbsp;')
  17195. .on('mousedown', 'span.elfinder-path-dir', function(e) {
  17196. var hash = $(this).attr('id').substr(prefix.length);
  17197. e.preventDefault();
  17198. if (hash != fm.cwd().hash) {
  17199. $(this).addClass(clHover);
  17200. if (query) {
  17201. fm.exec('search', query, { target: hash, mime: mimes.join(' ') });
  17202. } else {
  17203. fm.trigger('select', {selected : [hash]}).exec('open', hash);
  17204. }
  17205. }
  17206. })
  17207. .prependTo(fm.getUI('statusbar').show()),
  17208. roots = $('<div class="elfinder-path-roots"/>').on('click', function(e) {
  17209. e.stopPropagation();
  17210. e.preventDefault();
  17211. var roots = $.map(fm.roots, function(h) { return fm.file(h); }),
  17212. raw = [];
  17213. $.each(roots, function(i, f) {
  17214. if (! f.phash && fm.root(fm.cwd().hash, true) !== f.hash) {
  17215. raw.push({
  17216. label : fm.escape(f.i18 || f.name),
  17217. icon : 'home',
  17218. callback : function() { fm.exec('open', f.hash); },
  17219. options : {
  17220. iconClass : f.csscls || '',
  17221. iconImg : f.icon || ''
  17222. }
  17223. });
  17224. }
  17225. });
  17226. fm.trigger('contextmenu', {
  17227. raw: raw,
  17228. x: e.pageX,
  17229. y: e.pageY
  17230. });
  17231. }).append('<span class="elfinder-button-icon elfinder-button-icon-menu" />').appendTo(wzbase),
  17232. render = function(cwd) {
  17233. var dirs = [],
  17234. names = [];
  17235. $.each(fm.parents(cwd), function(i, hash) {
  17236. var c = (cwd === hash)? 'elfinder-path-dir elfinder-path-cwd' : 'elfinder-path-dir',
  17237. f = fm.file(hash),
  17238. name = fm.escape(f.i18 || f.name);
  17239. names.push(name);
  17240. dirs.push('<span id="'+prefix+hash+'" class="'+c+'" title="'+names.join(fm.option('separator'))+'">'+name+'</span>');
  17241. });
  17242. return dirs.join('<span class="elfinder-path-other">'+fm.option('separator')+'</span>');
  17243. },
  17244. toWorkzone = function() {
  17245. var prev;
  17246. path.children('span.elfinder-path-dir').attr('style', '');
  17247. prev = fm.direction === 'ltr'? $('#'+prefix + fm.cwd().hash).prevAll('span.elfinder-path-dir:first') : $();
  17248. path.scrollLeft(prev.length? prev.position().left : 0);
  17249. },
  17250. fit = function() {
  17251. if (fm.UA.CSS.flex) {
  17252. return;
  17253. }
  17254. var dirs = path.children('span.elfinder-path-dir'),
  17255. cnt = dirs.length,
  17256. m, bg = 0, ids;
  17257. if (place === 'workzone' || cnt < 2) {
  17258. dirs.attr('style', '');
  17259. return;
  17260. }
  17261. path.width(path.css('max-width'));
  17262. dirs.css({maxWidth: (100/cnt)+'%', display: 'inline-block'});
  17263. m = path.width() - 9;
  17264. path.children('span.elfinder-path-other').each(function() {
  17265. m -= $(this).width();
  17266. });
  17267. ids = [];
  17268. dirs.each(function(i) {
  17269. var dir = $(this),
  17270. w = dir.width();
  17271. m -= w;
  17272. if (w < this.scrollWidth) {
  17273. ids.push(i);
  17274. }
  17275. });
  17276. path.width('');
  17277. if (ids.length) {
  17278. if (m > 0) {
  17279. m = m / ids.length;
  17280. $.each(ids, function(i, k) {
  17281. var d = $(dirs[k]);
  17282. d.css('max-width', d.width() + m);
  17283. });
  17284. }
  17285. dirs.last().attr('style', '');
  17286. } else {
  17287. dirs.attr('style', '');
  17288. }
  17289. },
  17290. hasUiTree, hasUiStat;
  17291. fm.one('init', function() {
  17292. hasUiTree = fm.getUI('tree').length;
  17293. hasUiStat = fm.getUI('stat').length;
  17294. if (! hasUiTree && options.toWorkzoneWithoutNavbar) {
  17295. wzbase.append(path).insertBefore(fm.getUI('workzone'));
  17296. place = 'workzone';
  17297. fm.bind('open', toWorkzone)
  17298. .one('opendone', function() {
  17299. fm.getUI().trigger('resize');
  17300. });
  17301. }
  17302. })
  17303. .bind('open searchend parents', function() {
  17304. var dirs = [];
  17305. query = '';
  17306. target = '';
  17307. mimes = [];
  17308. path.html(render(fm.cwd().hash));
  17309. if (Object.keys(fm.roots).length > 1) {
  17310. path.css('margin', '');
  17311. roots.show();
  17312. } else {
  17313. path.css('margin', 0);
  17314. roots.hide();
  17315. }
  17316. !hasUiStat && fit();
  17317. })
  17318. .bind('searchstart', function(e) {
  17319. if (e.data) {
  17320. query = e.data.query || '';
  17321. target = e.data.target || '';
  17322. mimes = e.data.mimes || [];
  17323. }
  17324. })
  17325. .bind('search', function(e) {
  17326. var dirs = [],
  17327. html = '';
  17328. if (target) {
  17329. html = render(target);
  17330. } else {
  17331. html = fm.i18n('btnAll');
  17332. }
  17333. path.html('<span class="elfinder-path-other">'+fm.i18n('searcresult') + ': </span>' + html);
  17334. fit();
  17335. })
  17336. // on swipe to navbar show/hide
  17337. .bind('navbarshow navbarhide', function() {
  17338. var wz = fm.getUI('workzone');
  17339. if (this.type === 'navbarshow') {
  17340. fm.unbind('open', toWorkzone);
  17341. path.prependTo(fm.getUI('statusbar'));
  17342. wzbase.detach();
  17343. place = 'statusbar';
  17344. } else {
  17345. wzbase.append(path).insertBefore(wz);
  17346. place = 'workzone';
  17347. toWorkzone();
  17348. fm.bind('open', toWorkzone);
  17349. }
  17350. fm.trigger('uiresize');
  17351. })
  17352. .bind('resize uistatchange', fit);
  17353. });
  17354. };
  17355. /*
  17356. * File: /js/ui/places.js
  17357. */
  17358. /**
  17359. * @class elFinder places/favorites ui
  17360. *
  17361. * @author Dmitry (dio) Levashov
  17362. * @author Naoki Sawada
  17363. **/
  17364. $.fn.elfinderplaces = function(fm, opts) {
  17365. return this.each(function() {
  17366. var dirs = {},
  17367. c = 'class',
  17368. navdir = fm.res(c, 'navdir'),
  17369. collapsed = fm.res(c, 'navcollapse'),
  17370. expanded = fm.res(c, 'navexpand'),
  17371. hover = fm.res(c, 'hover'),
  17372. clroot = fm.res(c, 'treeroot'),
  17373. dropover = fm.res(c, 'adroppable'),
  17374. tpl = fm.res('tpl', 'placedir'),
  17375. ptpl = fm.res('tpl', 'perms'),
  17376. spinner = $(fm.res('tpl', 'navspinner')),
  17377. suffix = opts.suffix? opts.suffix : '',
  17378. key = 'places' + suffix,
  17379. menuTimer = null,
  17380. /**
  17381. * Convert places dir node into dir hash
  17382. *
  17383. * @param String directory id
  17384. * @return String
  17385. **/
  17386. id2hash = function(id) { return id.substr(6); },
  17387. /**
  17388. * Convert places dir hash into dir node id
  17389. *
  17390. * @param String directory id
  17391. * @return String
  17392. **/
  17393. hash2id = function(hash) { return 'place-'+hash; },
  17394. /**
  17395. * Convert places dir hash into dir node elment (jQuery object)
  17396. *
  17397. * @param String directory id
  17398. * @return Object
  17399. **/
  17400. hash2elm = function(hash) { return $(document.getElementById(hash2id(hash))); },
  17401. /**
  17402. * Save current places state
  17403. *
  17404. * @return void
  17405. **/
  17406. save = function() {
  17407. var hashes = [], data = {};
  17408. hashes = $.map(subtree.children().find('[id]'), function(n) {
  17409. return id2hash(n.id);
  17410. });
  17411. if (hashes.length) {
  17412. $.each(hashes.reverse(), function(i, h) {
  17413. data[h] = dirs[h];
  17414. });
  17415. } else {
  17416. data = null;
  17417. }
  17418. fm.storage(key, data);
  17419. },
  17420. /**
  17421. * Init dir at places
  17422. *
  17423. * @return void
  17424. **/
  17425. init = function() {
  17426. var dat, hashes;
  17427. key = 'places'+(opts.suffix? opts.suffix : ''),
  17428. dirs = {};
  17429. dat = fm.storage(key);
  17430. if (typeof dat === 'string') {
  17431. // old data type elFinder <= 2.1.12
  17432. dat = $.grep(dat.split(','), function(hash) { return hash? true : false;});
  17433. $.each(dat, function(i, d) {
  17434. var dir = d.split('#');
  17435. dirs[dir[0]] = dir[1]? dir[1] : dir[0];
  17436. });
  17437. } else if ($.isPlainObject(dat)) {
  17438. dirs = dat;
  17439. }
  17440. // allow modify `dirs`
  17441. /**
  17442. * example for preset places
  17443. *
  17444. * elfinderInstance.bind('placesload', function(e, fm) {
  17445. * //if (fm.storage(e.data.storageKey) === null) { // for first time only
  17446. * if (!fm.storage(e.data.storageKey)) { // for empty places
  17447. * e.data.dirs[targetHash] = fallbackName; // preset folder
  17448. * }
  17449. * }
  17450. **/
  17451. fm.trigger('placesload', {dirs: dirs, storageKey: key}, true);
  17452. hashes = Object.keys(dirs);
  17453. if (hashes.length) {
  17454. root.prepend(spinner);
  17455. fm.request({
  17456. data : {cmd : 'info', targets : hashes},
  17457. preventDefault : true
  17458. })
  17459. .done(function(data) {
  17460. var exists = {};
  17461. data.files && data.files.length && fm.cache(data.files);
  17462. $.each(data.files, function(i, f) {
  17463. var hash = f.hash;
  17464. exists[hash] = f;
  17465. });
  17466. $.each(dirs, function(h, f) {
  17467. add(exists[h] || Object.assign({notfound: true}, f));
  17468. });
  17469. if (fm.storage('placesState') > 0) {
  17470. root.trigger('click');
  17471. }
  17472. })
  17473. .always(function() {
  17474. spinner.remove();
  17475. });
  17476. }
  17477. },
  17478. /**
  17479. * Return node for given dir object
  17480. *
  17481. * @param Object directory object
  17482. * @return jQuery
  17483. **/
  17484. create = function(dir, hash) {
  17485. return $(tpl.replace(/\{id\}/, hash2id(dir? dir.hash : hash))
  17486. .replace(/\{name\}/, fm.escape(dir? dir.i18 || dir.name : hash))
  17487. .replace(/\{cssclass\}/, dir? (fm.perms2class(dir) + (dir.notfound? ' elfinder-na' : '') + (dir.csscls? ' '+dir.csscls : '')) : '')
  17488. .replace(/\{permissions\}/, (dir && (!dir.read || !dir.write || dir.notfound))? ptpl : '')
  17489. .replace(/\{title\}/, dir? (' title="' + fm.escape(fm.path(dir.hash, true) || dir.i18 || dir.name) + '"') : '')
  17490. .replace(/\{symlink\}/, '')
  17491. .replace(/\{style\}/, (dir && dir.icon)? fm.getIconStyle(dir) : ''));
  17492. },
  17493. /**
  17494. * Add new node into places
  17495. *
  17496. * @param Object directory object
  17497. * @return void
  17498. **/
  17499. add = function(dir) {
  17500. var node, hash;
  17501. if (dir.mime !== 'directory') {
  17502. return false;
  17503. }
  17504. hash = dir.hash;
  17505. if (!fm.files().hasOwnProperty(hash)) {
  17506. // update cache
  17507. fm.trigger('tree', {tree: [dir]});
  17508. }
  17509. node = create(dir, hash);
  17510. dirs[hash] = dir;
  17511. subtree.prepend(node);
  17512. root.addClass(collapsed);
  17513. sortBtn.toggle(subtree.children().length > 1);
  17514. return true;
  17515. },
  17516. /**
  17517. * Remove dir from places
  17518. *
  17519. * @param String directory hash
  17520. * @return String removed name
  17521. **/
  17522. remove = function(hash) {
  17523. var name = null, tgt, cnt;
  17524. if (dirs[hash]) {
  17525. delete dirs[hash];
  17526. tgt = hash2elm(hash);
  17527. if (tgt.length) {
  17528. name = tgt.text();
  17529. tgt.parent().remove();
  17530. cnt = subtree.children().length;
  17531. sortBtn.toggle(cnt > 1);
  17532. if (! cnt) {
  17533. root.removeClass(collapsed);
  17534. places.removeClass(expanded);
  17535. subtree.slideToggle(false);
  17536. }
  17537. }
  17538. }
  17539. return name;
  17540. },
  17541. /**
  17542. * Move up dir on places
  17543. *
  17544. * @param String directory hash
  17545. * @return void
  17546. **/
  17547. moveup = function(hash) {
  17548. var self = hash2elm(hash),
  17549. tgt = self.parent(),
  17550. prev = tgt.prev('div'),
  17551. cls = 'ui-state-hover',
  17552. ctm = fm.getUI('contextmenu');
  17553. menuTimer && clearTimeout(menuTimer);
  17554. if (prev.length) {
  17555. ctm.find(':first').data('placesHash', hash);
  17556. self.addClass(cls);
  17557. tgt.insertBefore(prev);
  17558. prev = tgt.prev('div');
  17559. menuTimer = setTimeout(function() {
  17560. self.removeClass(cls);
  17561. if (ctm.find(':first').data('placesHash') === hash) {
  17562. ctm.hide().empty();
  17563. }
  17564. }, 1500);
  17565. }
  17566. if(!prev.length) {
  17567. self.removeClass(cls);
  17568. ctm.hide().empty();
  17569. }
  17570. },
  17571. /**
  17572. * Update dir at places
  17573. *
  17574. * @param Object directory
  17575. * @param String previous hash
  17576. * @return Boolean
  17577. **/
  17578. update = function(dir, preHash) {
  17579. var hash = dir.hash,
  17580. tgt = hash2elm(preHash || hash),
  17581. node = create(dir, hash);
  17582. if (tgt.length > 0) {
  17583. tgt.parent().replaceWith(node);
  17584. dirs[hash] = dir;
  17585. return true;
  17586. } else {
  17587. return false;
  17588. }
  17589. },
  17590. /**
  17591. * Remove all dir from places
  17592. *
  17593. * @return void
  17594. **/
  17595. clear = function() {
  17596. subtree.empty();
  17597. root.removeClass(collapsed);
  17598. places.removeClass(expanded);
  17599. subtree.slideToggle(false);
  17600. },
  17601. /**
  17602. * Sort places dirs A-Z
  17603. *
  17604. * @return void
  17605. **/
  17606. sort = function() {
  17607. $.each(dirs, function(h, f) {
  17608. var dir = fm.file(h) || f,
  17609. node = create(dir, h),
  17610. ret = null;
  17611. if (!dir) {
  17612. node.hide();
  17613. }
  17614. if (subtree.children().length) {
  17615. $.each(subtree.children(), function() {
  17616. var current = $(this);
  17617. if ((dir.i18 || dir.name).localeCompare(current.children('.'+navdir).text()) < 0) {
  17618. ret = !node.insertBefore(current);
  17619. return ret;
  17620. }
  17621. });
  17622. if (ret !== null) {
  17623. return true;
  17624. }
  17625. }
  17626. !hash2elm(h).length && subtree.append(node);
  17627. });
  17628. save();
  17629. },
  17630. // sort button
  17631. sortBtn = $('<span class="elfinder-button-icon elfinder-button-icon-sort elfinder-places-root-icon" title="'+fm.i18n('cmdsort')+'"/>')
  17632. .hide()
  17633. .on('click', function(e) {
  17634. e.stopPropagation();
  17635. subtree.empty();
  17636. sort();
  17637. }
  17638. ),
  17639. /**
  17640. * Node - wrapper for places root
  17641. *
  17642. * @type jQuery
  17643. **/
  17644. wrapper = create({
  17645. hash : 'root-'+fm.namespace,
  17646. name : fm.i18n(opts.name, 'places'),
  17647. read : true,
  17648. write : true
  17649. }),
  17650. /**
  17651. * Places root node
  17652. *
  17653. * @type jQuery
  17654. **/
  17655. root = wrapper.children('.'+navdir)
  17656. .addClass(clroot)
  17657. .on('click', function(e) {
  17658. e.stopPropagation();
  17659. if (root.hasClass(collapsed)) {
  17660. places.toggleClass(expanded);
  17661. subtree.slideToggle();
  17662. fm.storage('placesState', places.hasClass(expanded)? 1 : 0);
  17663. }
  17664. })
  17665. .append(sortBtn),
  17666. /**
  17667. * Container for dirs
  17668. *
  17669. * @type jQuery
  17670. **/
  17671. subtree = wrapper.children('.'+fm.res(c, 'navsubtree')),
  17672. /**
  17673. * Main places container
  17674. *
  17675. * @type jQuery
  17676. **/
  17677. places = $(this).addClass(fm.res(c, 'tree')+' elfinder-places ui-corner-all')
  17678. .hide()
  17679. .append(wrapper)
  17680. .appendTo(fm.getUI('navbar'))
  17681. .on('mouseenter mouseleave', '.'+navdir, function(e) {
  17682. $(this).toggleClass('ui-state-hover', (e.type == 'mouseenter'));
  17683. })
  17684. .on('click', '.'+navdir, function(e) {
  17685. var p = $(this);
  17686. if (p.data('longtap')) {
  17687. e.stopPropagation();
  17688. return;
  17689. }
  17690. ! p.hasClass('elfinder-na') && fm.exec('open', p.attr('id').substr(6));
  17691. })
  17692. .on('contextmenu', '.'+navdir+':not(.'+clroot+')', function(e) {
  17693. var self = $(this),
  17694. hash = self.attr('id').substr(6);
  17695. e.preventDefault();
  17696. fm.trigger('contextmenu', {
  17697. raw : [{
  17698. label : fm.i18n('moveUp'),
  17699. icon : 'up',
  17700. remain : true,
  17701. callback : function() { moveup(hash); save(); }
  17702. },'|',{
  17703. label : fm.i18n('rmFromPlaces'),
  17704. icon : 'rm',
  17705. callback : function() { remove(hash); save(); }
  17706. }],
  17707. 'x' : e.pageX,
  17708. 'y' : e.pageY
  17709. });
  17710. self.addClass('ui-state-hover');
  17711. fm.getUI('contextmenu').children().on('mouseenter', function() {
  17712. self.addClass('ui-state-hover');
  17713. });
  17714. fm.bind('closecontextmenu', function() {
  17715. self.removeClass('ui-state-hover');
  17716. });
  17717. })
  17718. .droppable({
  17719. tolerance : 'pointer',
  17720. accept : '.elfinder-cwd-file-wrapper,.elfinder-tree-dir,.elfinder-cwd-file',
  17721. hoverClass : fm.res('class', 'adroppable'),
  17722. classes : { // Deprecated hoverClass jQueryUI>=1.12.0
  17723. 'ui-droppable-hover': fm.res('class', 'adroppable')
  17724. },
  17725. over : function(e, ui) {
  17726. var helper = ui.helper,
  17727. dir = $.grep(helper.data('files'), function(h) { return (fm.file(h).mime === 'directory' && !dirs[h])? true : false; });
  17728. e.stopPropagation();
  17729. helper.data('dropover', helper.data('dropover') + 1);
  17730. if (fm.insideWorkzone(e.pageX, e.pageY)) {
  17731. if (dir.length > 0) {
  17732. helper.addClass('elfinder-drag-helper-plus');
  17733. fm.trigger('unlockfiles', {files : helper.data('files'), helper: helper});
  17734. } else {
  17735. $(this).removeClass(dropover);
  17736. }
  17737. }
  17738. },
  17739. out : function(e, ui) {
  17740. var helper = ui.helper;
  17741. e.stopPropagation();
  17742. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  17743. $(this).removeData('dropover')
  17744. .removeClass(dropover);
  17745. },
  17746. drop : function(e, ui) {
  17747. var helper = ui.helper,
  17748. resolve = true;
  17749. $.each(helper.data('files'), function(i, hash) {
  17750. var dir = fm.file(hash);
  17751. if (dir && dir.mime == 'directory' && !dirs[dir.hash]) {
  17752. add(dir);
  17753. } else {
  17754. resolve = false;
  17755. }
  17756. });
  17757. save();
  17758. resolve && helper.hide();
  17759. }
  17760. })
  17761. // for touch device
  17762. .on('touchstart', '.'+navdir+':not(.'+clroot+')', function(e) {
  17763. if (e.originalEvent.touches.length > 1) {
  17764. return;
  17765. }
  17766. var hash = $(this).attr('id').substr(6),
  17767. p = $(this)
  17768. .addClass(hover)
  17769. .data('longtap', null)
  17770. .data('tmlongtap', setTimeout(function(){
  17771. // long tap
  17772. p.data('longtap', true);
  17773. fm.trigger('contextmenu', {
  17774. raw : [{
  17775. label : fm.i18n('rmFromPlaces'),
  17776. icon : 'rm',
  17777. callback : function() { remove(hash); save(); }
  17778. }],
  17779. 'x' : e.originalEvent.touches[0].pageX,
  17780. 'y' : e.originalEvent.touches[0].pageY
  17781. });
  17782. }, 500));
  17783. })
  17784. .on('touchmove touchend', '.'+navdir+':not(.'+clroot+')', function(e) {
  17785. clearTimeout($(this).data('tmlongtap'));
  17786. if (e.type == 'touchmove') {
  17787. $(this).removeClass(hover);
  17788. }
  17789. });
  17790. if ($.fn.sortable) {
  17791. subtree.addClass('touch-punch')
  17792. .sortable({
  17793. appendTo : fm.getUI(),
  17794. revert : false,
  17795. helper : function(e) {
  17796. var dir = $(e.target).parent();
  17797. dir.children().removeClass('ui-state-hover');
  17798. return $('<div class="ui-widget elfinder-place-drag elfinder-'+fm.direction+'"/>')
  17799. .append($('<div class="elfinder-navbar"/>').show().append(dir.clone()));
  17800. },
  17801. stop : function(e, ui) {
  17802. var target = $(ui.item[0]),
  17803. top = places.offset().top,
  17804. left = places.offset().left,
  17805. width = places.width(),
  17806. height = places.height(),
  17807. x = e.pageX,
  17808. y = e.pageY;
  17809. if (!(x > left && x < left+width && y > top && y < y+height)) {
  17810. remove(id2hash(target.children(':first').attr('id')));
  17811. save();
  17812. }
  17813. },
  17814. update : function(e, ui) {
  17815. save();
  17816. }
  17817. });
  17818. }
  17819. // "on regist" for command exec
  17820. $(this).on('regist', function(e, files){
  17821. var added = false;
  17822. $.each(files, function(i, dir) {
  17823. if (dir && dir.mime == 'directory' && !dirs[dir.hash]) {
  17824. if (add(dir)) {
  17825. added = true;
  17826. }
  17827. }
  17828. });
  17829. added && save();
  17830. });
  17831. // on fm load - show places and load files from backend
  17832. fm.one('load', function() {
  17833. var dat, hashes;
  17834. if (fm.oldAPI) {
  17835. return;
  17836. }
  17837. places.show().parent().show();
  17838. init();
  17839. fm.change(function(e) {
  17840. var changed = false;
  17841. $.each(e.data.changed, function(i, file) {
  17842. if (dirs[file.hash]) {
  17843. if (file.mime !== 'directory') {
  17844. if (remove(file.hash)) {
  17845. changed = true;
  17846. }
  17847. } else {
  17848. if (update(file)) {
  17849. changed = true;
  17850. }
  17851. }
  17852. }
  17853. });
  17854. changed && save();
  17855. })
  17856. .bind('rename', function(e) {
  17857. var changed = false;
  17858. if (e.data.removed) {
  17859. $.each(e.data.removed, function(i, hash) {
  17860. if (e.data.added[i]) {
  17861. if (update(e.data.added[i], hash)) {
  17862. changed = true;
  17863. }
  17864. }
  17865. });
  17866. }
  17867. changed && save();
  17868. })
  17869. .bind('rm paste', function(e) {
  17870. var names = [],
  17871. changed = false;
  17872. if (e.data.removed) {
  17873. $.each(e.data.removed, function(i, hash) {
  17874. var name = remove(hash);
  17875. name && names.push(name);
  17876. });
  17877. }
  17878. if (names.length) {
  17879. changed = true;
  17880. }
  17881. if (e.data.added && names.length) {
  17882. $.each(e.data.added, function(i, file) {
  17883. if ($.inArray(file.name, names) !== 1) {
  17884. file.mime == 'directory' && add(file);
  17885. }
  17886. });
  17887. }
  17888. changed && save();
  17889. })
  17890. .bind('sync netmount', function() {
  17891. var ev = this,
  17892. opSuffix = opts.suffix? opts.suffix : '',
  17893. hashes;
  17894. if (ev.type === 'sync') {
  17895. // check is change of opts.suffix
  17896. if (suffix !== opSuffix) {
  17897. suffix = opSuffix;
  17898. clear();
  17899. init();
  17900. return;
  17901. }
  17902. }
  17903. hashes = Object.keys(dirs);
  17904. if (hashes.length) {
  17905. root.prepend(spinner);
  17906. fm.request({
  17907. data : {cmd : 'info', targets : hashes},
  17908. preventDefault : true
  17909. })
  17910. .done(function(data) {
  17911. var exists = {},
  17912. updated = false,
  17913. cwd = fm.cwd().hash;
  17914. $.each(data.files || [], function(i, file) {
  17915. var hash = file.hash;
  17916. exists[hash] = file;
  17917. if (!fm.files().hasOwnProperty(file.hash)) {
  17918. // update cache
  17919. fm.trigger('tree', {tree: [file]});
  17920. }
  17921. });
  17922. $.each(dirs, function(h, f) {
  17923. if (f.notfound === Boolean(exists[h])) {
  17924. if ((f.phash === cwd && ev.type !== 'netmount') || (exists[h] && exists[h].mime !== 'directory')) {
  17925. if (remove(h)) {
  17926. updated = true;
  17927. }
  17928. } else {
  17929. if (update(exists[h] || Object.assign({notfound: true}, f))) {
  17930. updated = true;
  17931. }
  17932. }
  17933. } else if (exists[h] && exists[h].phash != cwd) {
  17934. // update permission of except cwd
  17935. update(exists[h]);
  17936. }
  17937. });
  17938. updated && save();
  17939. })
  17940. .always(function() {
  17941. spinner.remove();
  17942. });
  17943. }
  17944. });
  17945. });
  17946. });
  17947. };
  17948. /*
  17949. * File: /js/ui/searchbutton.js
  17950. */
  17951. /**
  17952. * @class elFinder toolbar search button widget.
  17953. *
  17954. * @author Dmitry (dio) Levashov
  17955. **/
  17956. $.fn.elfindersearchbutton = function(cmd) {
  17957. return this.each(function() {
  17958. var result = false,
  17959. fm = cmd.fm,
  17960. disabled = fm.res('class', 'disabled'),
  17961. isopts = cmd.options.incsearch || { enable: false },
  17962. sTypes = cmd.options.searchTypes,
  17963. id = function(name){return fm.namespace + fm.escape(name);},
  17964. toolbar= fm.getUI('toolbar'),
  17965. btnCls = fm.res('class', 'searchbtn'),
  17966. button = $(this)
  17967. .hide()
  17968. .addClass('ui-widget-content elfinder-button '+btnCls)
  17969. .on('click', function(e) {
  17970. e.stopPropagation();
  17971. }),
  17972. getMenuOffset = function() {
  17973. var fmNode = fm.getUI(),
  17974. baseOffset = fmNode.offset(),
  17975. buttonOffset = button.offset();
  17976. return {
  17977. top : buttonOffset.top - baseOffset.top,
  17978. maxHeight : fmNode.height() - 40
  17979. };
  17980. },
  17981. search = function() {
  17982. input.data('inctm') && clearTimeout(input.data('inctm'));
  17983. var val = $.trim(input.val()),
  17984. from = !$('#' + id('SearchFromAll')).prop('checked'),
  17985. mime = $('#' + id('SearchMime')).prop('checked'),
  17986. type = '';
  17987. if (from) {
  17988. if ($('#' + id('SearchFromVol')).prop('checked')) {
  17989. from = fm.root(fm.cwd().hash);
  17990. } else {
  17991. from = fm.cwd().hash;
  17992. }
  17993. }
  17994. if (mime) {
  17995. mime = val;
  17996. val = '.';
  17997. }
  17998. if (typeSet) {
  17999. type = typeSet.children('input:checked').val();
  18000. }
  18001. if (val) {
  18002. input.trigger('focus');
  18003. cmd.exec(val, from, mime, type).done(function() {
  18004. result = true;
  18005. }).fail(function() {
  18006. abort();
  18007. });
  18008. } else {
  18009. fm.trigger('searchend');
  18010. }
  18011. },
  18012. abort = function() {
  18013. input.data('inctm') && clearTimeout(input.data('inctm'));
  18014. input.val('').trigger('blur');
  18015. if (result || incVal) {
  18016. result = false;
  18017. incVal = '';
  18018. fm.lazy(function() {
  18019. fm.trigger('searchend');
  18020. });
  18021. }
  18022. },
  18023. incVal = '',
  18024. input = $('<input type="text" size="42"/>')
  18025. .on('focus', function() {
  18026. // close other menus
  18027. !button.hasClass('ui-state-active') && fm.getUI().click();
  18028. inFocus = true;
  18029. incVal = '';
  18030. button.addClass('ui-state-active');
  18031. fm.trigger('uiresize');
  18032. opts && opts.css(getMenuOffset()).slideDown(function() {
  18033. // Care for on browser window re-active
  18034. button.addClass('ui-state-active');
  18035. fm.toFront(opts);
  18036. });
  18037. })
  18038. .on('blur', function() {
  18039. inFocus = false;
  18040. if (opts) {
  18041. if (!opts.data('infocus')) {
  18042. opts.slideUp(function() {
  18043. button.removeClass('ui-state-active');
  18044. fm.trigger('uiresize');
  18045. fm.toHide(opts);
  18046. });
  18047. } else {
  18048. opts.data('infocus', false);
  18049. }
  18050. } else {
  18051. button.removeClass('ui-state-active');
  18052. }
  18053. })
  18054. .appendTo(button)
  18055. // to avoid fm shortcuts on arrows
  18056. .on('keypress', function(e) {
  18057. e.stopPropagation();
  18058. })
  18059. .on('keydown', function(e) {
  18060. e.stopPropagation();
  18061. if (e.keyCode === $.ui.keyCode.ENTER) {
  18062. search();
  18063. } else if (e.keyCode === $.ui.keyCode.ESCAPE) {
  18064. e.preventDefault();
  18065. abort();
  18066. }
  18067. }),
  18068. opts, typeSet, cwdReady, inFocus;
  18069. if (isopts.enable) {
  18070. isopts.minlen = isopts.minlen || 2;
  18071. isopts.wait = isopts.wait || 500;
  18072. input
  18073. .attr('title', fm.i18n('incSearchOnly'))
  18074. .on('compositionstart', function() {
  18075. input.data('composing', true);
  18076. })
  18077. .on('compositionend', function() {
  18078. input.removeData('composing');
  18079. input.trigger('input'); // for IE, edge
  18080. })
  18081. .on('input', function() {
  18082. if (! input.data('composing')) {
  18083. input.data('inctm') && clearTimeout(input.data('inctm'));
  18084. input.data('inctm', setTimeout(function() {
  18085. var val = input.val();
  18086. if (val.length === 0 || val.length >= isopts.minlen) {
  18087. (incVal !== val) && fm.trigger('incsearchstart', {
  18088. query: val,
  18089. type: typeSet? typeSet.children('input:checked').val() : 'searchName'
  18090. });
  18091. incVal = val;
  18092. if (val === '' && fm.searchStatus.state > 1 && fm.searchStatus.query) {
  18093. input.val(fm.searchStatus.query).trigger('select');
  18094. }
  18095. }
  18096. }, isopts.wait));
  18097. }
  18098. });
  18099. if (fm.UA.ltIE8) {
  18100. input.on('keydown', function(e) {
  18101. if (e.keyCode === 229) {
  18102. input.data('imetm') && clearTimeout(input.data('imetm'));
  18103. input.data('composing', true);
  18104. input.data('imetm', setTimeout(function() {
  18105. input.removeData('composing');
  18106. }, 100));
  18107. }
  18108. })
  18109. .on('keyup', function(e) {
  18110. input.data('imetm') && clearTimeout(input.data('imetm'));
  18111. if (input.data('composing')) {
  18112. e.keyCode === $.ui.keyCode.ENTER && input.trigger('compositionend');
  18113. } else {
  18114. input.trigger('input');
  18115. }
  18116. });
  18117. }
  18118. }
  18119. $('<span class="ui-icon ui-icon-search" title="'+cmd.title+'"/>')
  18120. .appendTo(button)
  18121. .on('mousedown', function(e) {
  18122. e.stopPropagation();
  18123. e.preventDefault();
  18124. if (button.hasClass('ui-state-active')) {
  18125. search();
  18126. } else {
  18127. input.trigger('focus');
  18128. }
  18129. });
  18130. $('<span class="ui-icon ui-icon-close"/>')
  18131. .appendTo(button)
  18132. .on('mousedown', function(e) {
  18133. e.stopPropagation();
  18134. e.preventDefault();
  18135. if (input.val() === '' && !button.hasClass('ui-state-active')) {
  18136. input.trigger('focus');
  18137. } else {
  18138. abort();
  18139. }
  18140. });
  18141. // wait when button will be added to DOM
  18142. fm.bind('toolbarload', function(){
  18143. var parent = button.parent();
  18144. if (parent.length) {
  18145. toolbar.prepend(button.show());
  18146. parent.remove();
  18147. // position icons for ie7
  18148. if (fm.UA.ltIE7) {
  18149. var icon = button.children(fm.direction == 'ltr' ? '.ui-icon-close' : '.ui-icon-search');
  18150. icon.css({
  18151. right : '',
  18152. left : parseInt(button.width())-icon.outerWidth(true)
  18153. });
  18154. }
  18155. }
  18156. });
  18157. fm
  18158. .one('init', function() {
  18159. fm.getUI('cwd').on('touchstart click', function() {
  18160. inFocus && input.trigger('blur');
  18161. });
  18162. })
  18163. .one('open', function() {
  18164. 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"/>')
  18165. .append(
  18166. $('<div class="buttonset"/>')
  18167. .append(
  18168. $('<input id="'+id('SearchFromCwd')+'" name="serchfrom" type="radio" checked="checked"/><label for="'+id('SearchFromCwd')+'">'+fm.i18n('btnCwd')+'</label>'),
  18169. $('<input id="'+id('SearchFromVol')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromVol')+'">'+fm.i18n('btnVolume')+'</label>'),
  18170. $('<input id="'+id('SearchFromAll')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromAll')+'">'+fm.i18n('btnAll')+'</label>')
  18171. ),
  18172. $('<div class="buttonset elfinder-search-type"/>')
  18173. .append(
  18174. $('<input id="'+id('SearchName')+'" name="serchcol" type="radio" checked="checked" value="SearchName"/><label for="'+id('SearchName')+'">'+fm.i18n('btnFileName')+'</label>')
  18175. )
  18176. )
  18177. .hide()
  18178. .appendTo(fm.getUI());
  18179. if (opts) {
  18180. if (sTypes) {
  18181. typeSet = opts.find('.elfinder-search-type');
  18182. $.each(cmd.options.searchTypes, function(i, v) {
  18183. typeSet.append($('<input id="'+id(i)+'" name="serchcol" type="radio" value="'+fm.escape(i)+'"/><label for="'+id(i)+'">'+fm.i18n(v.name)+'</label>'));
  18184. });
  18185. }
  18186. opts.find('div.buttonset').buttonset();
  18187. $('#'+id('SearchFromAll')).next('label').attr('title', fm.i18n('searchTarget', fm.i18n('btnAll')));
  18188. if (sTypes) {
  18189. $.each(sTypes, function(i, v) {
  18190. if (v.title) {
  18191. $('#'+id(i)).next('label').attr('title', fm.i18n(v.title));
  18192. }
  18193. });
  18194. }
  18195. opts.on('mousedown', 'div.buttonset', function(e){
  18196. e.stopPropagation();
  18197. opts.data('infocus', true);
  18198. })
  18199. .on('click', 'input', function(e) {
  18200. e.stopPropagation();
  18201. $.trim(input.val())? search() : input.trigger('focus');
  18202. })
  18203. .on('close', function() {
  18204. input.trigger('blur');
  18205. });
  18206. }
  18207. })
  18208. .bind('searchend', function() {
  18209. input.val('');
  18210. })
  18211. .bind('open parents', function() {
  18212. var dirs = [],
  18213. volroot = fm.file(fm.root(fm.cwd().hash));
  18214. if (volroot) {
  18215. $.each(fm.parents(fm.cwd().hash), function(i, hash) {
  18216. dirs.push(fm.file(hash).name);
  18217. });
  18218. $('#'+id('SearchFromCwd')).next('label').attr('title', fm.i18n('searchTarget', dirs.join(fm.option('separator'))));
  18219. $('#'+id('SearchFromVol')).next('label').attr('title', fm.i18n('searchTarget', volroot.name));
  18220. }
  18221. })
  18222. .bind('open', function() {
  18223. incVal && abort();
  18224. })
  18225. .bind('cwdinit', function() {
  18226. cwdReady = false;
  18227. })
  18228. .bind('cwdrender',function() {
  18229. cwdReady = true;
  18230. })
  18231. .bind('keydownEsc', function() {
  18232. if (incVal && incVal.substr(0, 1) === '/') {
  18233. incVal = '';
  18234. input.val('');
  18235. fm.trigger('searchend');
  18236. }
  18237. })
  18238. .shortcut({
  18239. pattern : 'ctrl+f f3',
  18240. description : cmd.title,
  18241. callback : function() {
  18242. input.trigger('select').trigger('focus');
  18243. }
  18244. })
  18245. .shortcut({
  18246. 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',
  18247. description : fm.i18n('firstLetterSearch'),
  18248. callback : function(e) {
  18249. if (! cwdReady) { return; }
  18250. var code = e.originalEvent.keyCode,
  18251. next = function() {
  18252. var sel = fm.selected(),
  18253. key = $.ui.keyCode[(!sel.length || fm.cwdHash2Elm(sel[0]).next('[id]').length)? 'RIGHT' : 'HOME'];
  18254. $(document).trigger($.Event('keydown', { keyCode: key, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  18255. },
  18256. val;
  18257. if (code >= 96 && code <= 105) {
  18258. code -= 48;
  18259. }
  18260. val = '/' + String.fromCharCode(code);
  18261. if (incVal !== val) {
  18262. input.val(val);
  18263. incVal = val;
  18264. fm
  18265. .trigger('incsearchstart', { query: val })
  18266. .one('cwdrender', next);
  18267. } else{
  18268. next();
  18269. }
  18270. }
  18271. });
  18272. });
  18273. };
  18274. /*
  18275. * File: /js/ui/sortbutton.js
  18276. */
  18277. /**
  18278. * @class elFinder toolbar button menu with sort variants.
  18279. *
  18280. * @author Dmitry (dio) Levashov
  18281. **/
  18282. $.fn.elfindersortbutton = function(cmd) {
  18283. return this.each(function() {
  18284. var fm = cmd.fm,
  18285. name = cmd.name,
  18286. c = 'class',
  18287. disabled = fm.res(c, 'disabled'),
  18288. hover = fm.res(c, 'hover'),
  18289. item = 'elfinder-button-menu-item',
  18290. selected = item+'-selected',
  18291. asc = selected+'-asc',
  18292. desc = selected+'-desc',
  18293. text = $('<span class="elfinder-button-text">'+cmd.title+'</span>'),
  18294. button = $(this).addClass('ui-state-default elfinder-button elfinder-menubutton elfiner-button-'+name)
  18295. .attr('title', cmd.title)
  18296. .append('<span class="elfinder-button-icon elfinder-button-icon-'+name+'"/>', text)
  18297. .on('mouseenter mouseleave', function(e) { !button.hasClass(disabled) && button.toggleClass(hover, e.type === 'mouseenter'); })
  18298. .on('click', function(e) {
  18299. if (!button.hasClass(disabled)) {
  18300. e.stopPropagation();
  18301. menu.is(':hidden') && fm.getUI().click();
  18302. menu.css(getMenuOffset()).slideToggle({
  18303. duration: 100,
  18304. done: function(e) {
  18305. fm[menu.is(':visible')? 'toFront' : 'toHide'](menu);
  18306. }
  18307. });
  18308. }
  18309. }),
  18310. hide = function() { fm.toHide(menu); },
  18311. menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu elfinder-button-sort-menu ui-corner-all"/>')
  18312. .hide()
  18313. .appendTo(fm.getUI())
  18314. .on('mouseenter mouseleave', '.'+item, function(e) { $(this).toggleClass(hover, e.type === 'mouseenter'); })
  18315. .on('click', function(e) {
  18316. e.preventDefault();
  18317. e.stopPropagation();
  18318. })
  18319. .on('close', hide),
  18320. update = function() {
  18321. menu.children('[rel]').removeClass(selected+' '+asc+' '+desc)
  18322. .filter('[rel="'+fm.sortType+'"]')
  18323. .addClass(selected+' '+(fm.sortOrder == 'asc' ? asc : desc));
  18324. menu.children('.elfinder-sort-stick').toggleClass(selected, fm.sortStickFolders);
  18325. menu.children('.elfinder-sort-tree').toggleClass(selected, fm.sortAlsoTreeview);
  18326. },
  18327. getMenuOffset = function() {
  18328. var baseOffset = fm.getUI().offset(),
  18329. buttonOffset = button.offset();
  18330. return {
  18331. top : buttonOffset.top - baseOffset.top,
  18332. left : buttonOffset.left - baseOffset.left
  18333. };
  18334. },
  18335. tm;
  18336. text.hide();
  18337. $.each(fm.sortRules, function(name, value) {
  18338. menu.append($('<div class="'+item+'" rel="'+name+'"><span class="ui-icon ui-icon-arrowthick-1-n"/><span class="ui-icon ui-icon-arrowthick-1-s"/>'+fm.i18n('sort'+name)+'</div>').data('type', name));
  18339. });
  18340. menu.children().on('click', function(e) {
  18341. cmd.exec([], $(this).removeClass(hover).attr('rel'));
  18342. });
  18343. $('<div class="'+item+' '+item+'-separated elfinder-sort-ext elfinder-sort-stick"><span class="ui-icon ui-icon-check"/>'+fm.i18n('sortFoldersFirst')+'</div>')
  18344. .appendTo(menu)
  18345. .on('click', function() {
  18346. cmd.exec([], 'stick');
  18347. });
  18348. fm.one('init', function() {
  18349. if (fm.ui.tree && fm.options.sortAlsoTreeview !== null) {
  18350. $('<div class="'+item+' '+item+'-separated elfinder-sort-ext elfinder-sort-tree"><span class="ui-icon ui-icon-check"/>'+fm.i18n('sortAlsoTreeview')+'</div>')
  18351. .appendTo(menu)
  18352. .on('click', function() {
  18353. cmd.exec([], 'tree');
  18354. });
  18355. }
  18356. })
  18357. .bind('disable select', hide)
  18358. .bind('sortchange', update).getUI().on('click', hide);
  18359. if (menu.children().length > 1) {
  18360. cmd.change(function() {
  18361. tm && cancelAnimationFrame(tm);
  18362. tm = requestAnimationFrame(function() {
  18363. button.toggleClass(disabled, cmd.disabled());
  18364. update();
  18365. });
  18366. })
  18367. .change();
  18368. } else {
  18369. button.addClass(disabled);
  18370. }
  18371. });
  18372. };
  18373. /*
  18374. * File: /js/ui/stat.js
  18375. */
  18376. /**
  18377. * @class elFinder ui
  18378. * Display number of files/selected files and its size in statusbar
  18379. *
  18380. * @author Dmitry (dio) Levashov
  18381. **/
  18382. $.fn.elfinderstat = function(fm) {
  18383. return this.each(function() {
  18384. var size = $(this).addClass('elfinder-stat-size'),
  18385. sel = $('<div class="elfinder-stat-selected"/>')
  18386. .on('click', 'a', function(e) {
  18387. var hash = $(this).data('hash');
  18388. e.preventDefault();
  18389. fm.exec('opendir', [ hash ]);
  18390. }),
  18391. titleitems = fm.i18n('items'),
  18392. titlesel = fm.i18n('selected'),
  18393. titlesize = fm.i18n('size'),
  18394. setstat = function(files) {
  18395. var c = 0,
  18396. s = 0,
  18397. cwd = fm.cwd(),
  18398. calc = true,
  18399. hasSize = true;
  18400. if (cwd.sizeInfo || cwd.size) {
  18401. s = cwd.size;
  18402. calc = false;
  18403. }
  18404. $.each(files, function(i, file) {
  18405. c++;
  18406. if (calc) {
  18407. s += parseInt(file.size) || 0;
  18408. if (hasSize === true && file.mime === 'directory' && !file.sizeInfo) {
  18409. hasSize = false;
  18410. }
  18411. }
  18412. });
  18413. 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>')
  18414. .attr('title', size.text());
  18415. fm.trigger('uistatchange');
  18416. },
  18417. setIncsearchStat = function(data) {
  18418. size.find('span.elfinder-stat-incsearch').html(data? data.hashes.length + ' / ' : '');
  18419. size.attr('title', size.text());
  18420. fm.trigger('uistatchange');
  18421. },
  18422. setSelect = function(files) {
  18423. var s = 0,
  18424. c = 0,
  18425. dirs = [],
  18426. path, file;
  18427. if (files.length === 1) {
  18428. file = files[0];
  18429. s = file.size;
  18430. if (fm.searchStatus.state === 2) {
  18431. path = fm.escape(file.path? file.path.replace(/\/[^\/]*$/, '') : '..');
  18432. dirs.push('<a href="#elf_'+file.phash+'" data-hash="'+file.hash+'" title="'+path+'">'+path+'</a>');
  18433. }
  18434. dirs.push(fm.escape(file.i18 || file.name));
  18435. sel.html(dirs.join('/') + (s > 0 ? ', '+fm.formatSize(s) : ''));
  18436. } else if (files.length) {
  18437. $.each(files, function(i, file) {
  18438. c++;
  18439. s += parseInt(file.size)||0;
  18440. });
  18441. sel.html(c ? titlesel+': '+c+', '+titlesize+': '+fm.formatSize(s) : '&nbsp;');
  18442. } else {
  18443. sel.html('');
  18444. }
  18445. sel.attr('title', sel.text());
  18446. fm.trigger('uistatchange');
  18447. };
  18448. fm.getUI('statusbar').prepend(size).append(sel).show();
  18449. if (fm.UA.Mobile && $.fn.tooltip) {
  18450. fm.getUI('statusbar').tooltip({
  18451. classes: {
  18452. 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow'
  18453. },
  18454. tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow',
  18455. track: true
  18456. });
  18457. }
  18458. fm
  18459. .bind('cwdhasheschange', function(e) {
  18460. setstat($.map(e.data, function(h) { return fm.file(h); }));
  18461. })
  18462. .change(function(e) {
  18463. var files = e.data.changed || [],
  18464. cwdHash = fm.cwd().hash;
  18465. $.each(files, function() {
  18466. if (this.hash === cwdHash) {
  18467. if (this.size) {
  18468. size.children('.elfinder-stat-size').addClass('elfinder-stat-size-recursive').html(fm.i18n('sum')+': '+fm.formatSize(this.size));
  18469. size.attr('title', size.text());
  18470. }
  18471. return false;
  18472. }
  18473. });
  18474. })
  18475. .select(function() {
  18476. setSelect(fm.selectedFiles());
  18477. })
  18478. .bind('open', function() {
  18479. setSelect([]);
  18480. })
  18481. .bind('incsearch', function(e) {
  18482. setIncsearchStat(e.data);
  18483. })
  18484. .bind('incsearchend', function() {
  18485. setIncsearchStat();
  18486. })
  18487. ;
  18488. });
  18489. };
  18490. /*
  18491. * File: /js/ui/toast.js
  18492. */
  18493. /**
  18494. * @class elFinder toast
  18495. *
  18496. * This was created inspired by the toastr. Thanks to developers of toastr.
  18497. * CodeSeven/toastr: http://johnpapa.net <https://github.com/CodeSeven/toastr>
  18498. *
  18499. * @author Naoki Sawada
  18500. **/
  18501. $.fn.elfindertoast = function(opts, fm) {
  18502. var defOpts = Object.assign({
  18503. mode: 'success', // or 'info', 'warning' and 'error'
  18504. msg: '',
  18505. showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery
  18506. showDuration: 300,
  18507. showEasing: 'swing', //swing and linear are built into jQuery
  18508. onShown: undefined,
  18509. hideMethod: 'fadeOut',
  18510. hideDuration: 1500,
  18511. hideEasing: 'swing',
  18512. onHidden: undefined,
  18513. timeOut: 3000,
  18514. extNode: undefined,
  18515. button: undefined,
  18516. width: undefined
  18517. }, $.isPlainObject(fm.options.uiOptions.toast.defaults)? fm.options.uiOptions.toast.defaults : {});
  18518. return this.each(function() {
  18519. opts = Object.assign({}, defOpts, opts || {});
  18520. var self = $(this),
  18521. show = function(notm) {
  18522. self.stop();
  18523. fm.toFront(self);
  18524. self[opts.showMethod]({
  18525. duration: opts.showDuration,
  18526. easing: opts.showEasing,
  18527. complete: function() {
  18528. opts.onShown && opts.onShown();
  18529. if (!notm && opts.timeOut) {
  18530. rmTm = setTimeout(rm, opts.timeOut);
  18531. }
  18532. }
  18533. });
  18534. },
  18535. rm = function() {
  18536. self[opts.hideMethod]({
  18537. duration: opts.hideDuration,
  18538. easing: opts.hideEasing,
  18539. complete: function() {
  18540. opts.onHidden && opts.onHidden();
  18541. self.remove();
  18542. }
  18543. });
  18544. },
  18545. rmTm;
  18546. self.on('click', function(e) {
  18547. e.stopPropagation();
  18548. e.preventDefault();
  18549. rmTm && clearTimeout(rmTm);
  18550. opts.onHidden && opts.onHidden();
  18551. self.stop().remove();
  18552. }).on('mouseenter mouseleave', function(e) {
  18553. if (opts.timeOut) {
  18554. rmTm && clearTimeout(rmTm);
  18555. rmTm = null;
  18556. if (e.type === 'mouseenter') {
  18557. show(true);
  18558. } else {
  18559. rmTm = setTimeout(rm, opts.timeOut);
  18560. }
  18561. }
  18562. }).hide().addClass('toast-' + opts.mode).append($('<div class="elfinder-toast-msg"/>').html(opts.msg.replace(/%([a-zA-Z0-9]+)%/g, function(m, m1) {
  18563. return fm.i18n(m1);
  18564. })));
  18565. if (opts.extNode) {
  18566. self.append(opts.extNode);
  18567. }
  18568. if (opts.button) {
  18569. self.append(
  18570. $('<button class="ui-button ui-widget ui-state-default ui-corner-all elfinder-tabstop"/>')
  18571. .append($('<span class="ui-button-text"/>').text(fm.i18n(opts.button.text)))
  18572. .on('mouseenter mouseleave', function(e) {
  18573. $(this).toggleClass('ui-state-hover', e.type == 'mouseenter');
  18574. })
  18575. .on('click', opts.button.click || function(){})
  18576. );
  18577. }
  18578. if (opts.width) {
  18579. self.css('max-width', opts.width);
  18580. }
  18581. show();
  18582. });
  18583. };
  18584. /*
  18585. * File: /js/ui/toolbar.js
  18586. */
  18587. /**
  18588. * @class elFinder toolbar
  18589. *
  18590. * @author Dmitry (dio) Levashov
  18591. **/
  18592. $.fn.elfindertoolbar = function(fm, opts) {
  18593. this.not('.elfinder-toolbar').each(function() {
  18594. var commands = fm._commands,
  18595. self = $(this).addClass('ui-helper-clearfix ui-widget-header elfinder-toolbar'),
  18596. options = {
  18597. // default options
  18598. displayTextLabel: false,
  18599. labelExcludeUA: ['Mobile'],
  18600. autoHideUA: ['Mobile'],
  18601. showPreferenceButton: 'none'
  18602. },
  18603. filter = function(opts) {
  18604. return $.grep(opts, function(v) {
  18605. if ($.isPlainObject(v)) {
  18606. options = Object.assign(options, v);
  18607. return false;
  18608. }
  18609. return true;
  18610. });
  18611. },
  18612. render = function(disabled){
  18613. var name,cmdPref;
  18614. $.each(buttons, function(i, b) { b.detach(); });
  18615. self.empty();
  18616. l = panels.length;
  18617. while (l--) {
  18618. if (panels[l]) {
  18619. panel = $('<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>');
  18620. i = panels[l].length;
  18621. while (i--) {
  18622. name = panels[l][i];
  18623. if ((!disabled || !disabled[name]) && (cmd = commands[name])) {
  18624. button = 'elfinder'+cmd.options.ui;
  18625. if (! buttons[name] && $.fn[button]) {
  18626. buttons[name] = $('<div/>')[button](cmd);
  18627. }
  18628. if (buttons[name]) {
  18629. buttons[name].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  18630. panel.prepend(buttons[name]);
  18631. }
  18632. }
  18633. }
  18634. panel.children().length && self.prepend(panel);
  18635. panel.children(':gt(0)').before('<span class="ui-widget-content elfinder-toolbar-button-separator"/>');
  18636. }
  18637. }
  18638. if (cmdPref = commands['preference']) {
  18639. //cmdPref.state = !self.children().length? 0 : -1;
  18640. if (options.showPreferenceButton === 'always' || (!self.children().length && options.showPreferenceButton === 'auto')) {
  18641. //cmdPref.state = 0;
  18642. panel = $('<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>');
  18643. name = 'preference';
  18644. button = 'elfinder'+cmd.options.ui;
  18645. buttons[name] = $('<div/>')[button](cmdPref);
  18646. buttons[name].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  18647. panel.prepend(buttons[name]);
  18648. self.append(panel);
  18649. }
  18650. }
  18651. (! self.data('swipeClose') && self.children().length)? self.show() : self.hide();
  18652. prevHeight = self[0].clientHeight;
  18653. fm.trigger('toolbarload').trigger('uiresize');
  18654. },
  18655. buttons = {},
  18656. panels = filter(opts || []),
  18657. dispre = null,
  18658. uiCmdMapPrev = '',
  18659. prevHeight = 0,
  18660. contextRaw = [],
  18661. l, i, cmd, panel, button, swipeHandle, autoHide, textLabel, resizeTm;
  18662. // normalize options
  18663. options.showPreferenceButton = options.showPreferenceButton.toLowerCase();
  18664. if (options.displayTextLabel !== 'none') {
  18665. // correction of options.displayTextLabel
  18666. textLabel = fm.storage('toolbarTextLabel');
  18667. if (textLabel === null) {
  18668. textLabel = (options.displayTextLabel && (! options.labelExcludeUA || ! options.labelExcludeUA.length || ! $.grep(options.labelExcludeUA, function(v){ return fm.UA[v]? true : false; }).length));
  18669. } else {
  18670. textLabel = (textLabel == 1);
  18671. }
  18672. contextRaw.push({
  18673. label : fm.i18n('textLabel'),
  18674. icon : 'text',
  18675. callback : function() {
  18676. textLabel = ! textLabel;
  18677. self.css('height', '').find('.elfinder-button-text')[textLabel? 'show':'hide']();
  18678. fm.trigger('uiresize').storage('toolbarTextLabel', textLabel? '1' : '0');
  18679. },
  18680. });
  18681. }
  18682. if (options.preferenceInContextmenu && commands['preference']) {
  18683. contextRaw.push({
  18684. label : fm.i18n('toolbarPref'),
  18685. icon : 'preference',
  18686. callback : function() {
  18687. fm.exec('preference', void(0), {tab: 'toolbar'});
  18688. }
  18689. });
  18690. }
  18691. // add contextmenu
  18692. if (contextRaw.length) {
  18693. self.on('contextmenu', function(e) {
  18694. e.stopPropagation();
  18695. e.preventDefault();
  18696. fm.trigger('contextmenu', {
  18697. raw: contextRaw,
  18698. x: e.pageX,
  18699. y: e.pageY
  18700. });
  18701. }).on('touchstart', function(e) {
  18702. if (e.originalEvent.touches.length > 1) {
  18703. return;
  18704. }
  18705. self.data('tmlongtap') && clearTimeout(self.data('tmlongtap'));
  18706. self.removeData('longtap')
  18707. .data('longtap', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY})
  18708. .data('tmlongtap', setTimeout(function() {
  18709. self.removeData('longtapTm')
  18710. .trigger({
  18711. type: 'contextmenu',
  18712. pageX: self.data('longtap').x,
  18713. pageY: self.data('longtap').y
  18714. })
  18715. .data('longtap', {longtap: true});
  18716. }, 500));
  18717. }).on('touchmove touchend', function(e) {
  18718. if (self.data('tmlongtap')) {
  18719. if (e.type === 'touchend' ||
  18720. ( Math.abs(self.data('longtap').x - e.originalEvent.touches[0].pageX)
  18721. + Math.abs(self.data('longtap').y - e.originalEvent.touches[0].pageY)) > 4)
  18722. clearTimeout(self.data('tmlongtap'));
  18723. self.removeData('longtapTm');
  18724. }
  18725. }).on('click', function(e) {
  18726. if (self.data('longtap') && self.data('longtap').longtap) {
  18727. e.stopImmediatePropagation();
  18728. e.preventDefault();
  18729. }
  18730. }).on('touchend click', '.elfinder-button', function(e) {
  18731. if (self.data('longtap') && self.data('longtap').longtap) {
  18732. e.stopImmediatePropagation();
  18733. e.preventDefault();
  18734. }
  18735. }
  18736. );
  18737. }
  18738. self.prev().length && self.parent().prepend(this);
  18739. render();
  18740. fm.bind('open sync select toolbarpref', function() {
  18741. var disabled = Object.assign({}, fm.option('disabledFlip')),
  18742. userHides = fm.storage('toolbarhides'),
  18743. doRender, sel, disabledKeys;
  18744. if (! userHides && Array.isArray(options.defaultHides)) {
  18745. userHides = {};
  18746. $.each(options.defaultHides, function() {
  18747. userHides[this] = true;
  18748. });
  18749. fm.storage('toolbarhides', userHides);
  18750. }
  18751. if (this.type === 'select') {
  18752. if (fm.searchStatus.state < 2) {
  18753. return;
  18754. }
  18755. sel = fm.selected();
  18756. if (sel.length) {
  18757. disabled = fm.getDisabledCmds(sel, true);
  18758. }
  18759. }
  18760. $.each(userHides, function(n) {
  18761. if (!disabled[n]) {
  18762. disabled[n] = true;
  18763. }
  18764. });
  18765. if (Object.keys(fm.commandMap).length) {
  18766. $.each(fm.commandMap, function(from, to){
  18767. if (to === 'hidden') {
  18768. disabled[from] = true;
  18769. }
  18770. });
  18771. }
  18772. disabledKeys = Object.keys(disabled);
  18773. if (!dispre || dispre.toString() !== disabledKeys.sort().toString()) {
  18774. render(disabledKeys.length? disabled : null);
  18775. doRender = true;
  18776. }
  18777. dispre = disabledKeys.sort();
  18778. if (doRender || uiCmdMapPrev !== JSON.stringify(fm.commandMap)) {
  18779. uiCmdMapPrev = JSON.stringify(fm.commandMap);
  18780. if (! doRender) {
  18781. // reset toolbar
  18782. $.each($('div.elfinder-button'), function(){
  18783. var origin = $(this).data('origin');
  18784. if (origin) {
  18785. $(this).after(origin).detach();
  18786. }
  18787. });
  18788. }
  18789. if (Object.keys(fm.commandMap).length) {
  18790. $.each(fm.commandMap, function(from, to){
  18791. var cmd = fm._commands[to],
  18792. button = cmd? 'elfinder'+cmd.options.ui : null,
  18793. btn;
  18794. if (button && $.fn[button]) {
  18795. btn = buttons[from];
  18796. if (btn) {
  18797. if (! buttons[to] && $.fn[button]) {
  18798. buttons[to] = $('<div/>')[button](cmd);
  18799. if (buttons[to]) {
  18800. buttons[to].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  18801. if (cmd.extendsCmd) {
  18802. buttons[to].children('span.elfinder-button-icon').addClass('elfinder-button-icon-' + cmd.extendsCmd);
  18803. }
  18804. }
  18805. }
  18806. if (buttons[to]) {
  18807. btn.after(buttons[to]);
  18808. buttons[to].data('origin', btn.detach());
  18809. }
  18810. }
  18811. }
  18812. });
  18813. }
  18814. }
  18815. }).bind('resize', function(e) {
  18816. resizeTm && cancelAnimationFrame(resizeTm);
  18817. resizeTm = requestAnimationFrame(function() {
  18818. var h = self[0].clientHeight;
  18819. if (prevHeight !== h) {
  18820. prevHeight = h;
  18821. fm.trigger('uiresize');
  18822. }
  18823. });
  18824. });
  18825. if (fm.UA.Touch) {
  18826. autoHide = fm.storage('autoHide') || {};
  18827. if (typeof autoHide.toolbar === 'undefined') {
  18828. autoHide.toolbar = (options.autoHideUA && options.autoHideUA.length > 0 && $.grep(options.autoHideUA, function(v){ return fm.UA[v]? true : false; }).length);
  18829. fm.storage('autoHide', autoHide);
  18830. }
  18831. if (autoHide.toolbar) {
  18832. fm.one('init', function() {
  18833. fm.uiAutoHide.push(function(){ self.stop(true, true).trigger('toggle', { duration: 500, init: true }); });
  18834. });
  18835. }
  18836. fm.bind('load', function() {
  18837. swipeHandle = $('<div class="elfinder-toolbar-swipe-handle"/>').hide().appendTo(fm.getUI());
  18838. if (swipeHandle.css('pointer-events') !== 'none') {
  18839. swipeHandle.remove();
  18840. swipeHandle = null;
  18841. }
  18842. });
  18843. self.on('toggle', function(e, data) {
  18844. var wz = fm.getUI('workzone'),
  18845. toshow= self.is(':hidden'),
  18846. wzh = wz.height(),
  18847. h = self.height(),
  18848. tbh = self.outerHeight(true),
  18849. delta = tbh - h,
  18850. opt = Object.assign({
  18851. step: function(now) {
  18852. wz.height(wzh + (toshow? (now + delta) * -1 : h - now));
  18853. fm.trigger('resize');
  18854. },
  18855. always: function() {
  18856. requestAnimationFrame(function() {
  18857. self.css('height', '');
  18858. fm.trigger('uiresize');
  18859. if (swipeHandle) {
  18860. if (toshow) {
  18861. swipeHandle.stop(true, true).hide();
  18862. } else {
  18863. swipeHandle.height(data.handleH? data.handleH : '');
  18864. fm.resources.blink(swipeHandle, 'slowonce');
  18865. }
  18866. }
  18867. toshow && self.scrollTop('0px');
  18868. data.init && fm.trigger('uiautohide');
  18869. });
  18870. }
  18871. }, data);
  18872. self.data('swipeClose', ! toshow).stop(true, true).animate({height : 'toggle'}, opt);
  18873. autoHide.toolbar = !toshow;
  18874. fm.storage('autoHide', Object.assign(fm.storage('autoHide'), {toolbar: autoHide.toolbar}));
  18875. }).on('touchstart', function(e) {
  18876. if (self.scrollBottom() > 5) {
  18877. e.originalEvent._preventSwipeY = true;
  18878. }
  18879. });
  18880. }
  18881. });
  18882. return this;
  18883. };
  18884. /*
  18885. * File: /js/ui/tree.js
  18886. */
  18887. /**
  18888. * @class elFinder folders tree
  18889. *
  18890. * @author Dmitry (dio) Levashov
  18891. **/
  18892. $.fn.elfindertree = function(fm, opts) {
  18893. var treeclass = fm.res('class', 'tree');
  18894. this.not('.'+treeclass).each(function() {
  18895. var c = 'class', mobile = fm.UA.Mobile,
  18896. /**
  18897. * Root directory class name
  18898. *
  18899. * @type String
  18900. */
  18901. root = fm.res(c, 'treeroot'),
  18902. /**
  18903. * Open root dir if not opened yet
  18904. *
  18905. * @type Boolean
  18906. */
  18907. openRoot = opts.openRootOnLoad,
  18908. /**
  18909. * Open current work dir if not opened yet
  18910. *
  18911. * @type Boolean
  18912. */
  18913. openCwd = opts.openCwdOnOpen,
  18914. /**
  18915. * Auto loading current directory parents and do expand their node
  18916. *
  18917. * @type Boolean
  18918. */
  18919. syncTree = openCwd || opts.syncTree,
  18920. /**
  18921. * Subtree class name
  18922. *
  18923. * @type String
  18924. */
  18925. subtree = fm.res(c, 'navsubtree'),
  18926. /**
  18927. * Directory class name
  18928. *
  18929. * @type String
  18930. */
  18931. navdir = fm.res(c, 'treedir'),
  18932. /**
  18933. * Directory CSS selector
  18934. *
  18935. * @type String
  18936. */
  18937. selNavdir = 'span.' + navdir,
  18938. /**
  18939. * Collapsed arrow class name
  18940. *
  18941. * @type String
  18942. */
  18943. collapsed = fm.res(c, 'navcollapse'),
  18944. /**
  18945. * Expanded arrow class name
  18946. *
  18947. * @type String
  18948. */
  18949. expanded = fm.res(c, 'navexpand'),
  18950. /**
  18951. * Class name to mark arrow for directory with already loaded children
  18952. *
  18953. * @type String
  18954. */
  18955. loaded = 'elfinder-subtree-loaded',
  18956. /**
  18957. * Class name to mark need subdirs request
  18958. *
  18959. * @type String
  18960. */
  18961. chksubdir = 'elfinder-subtree-chksubdir',
  18962. /**
  18963. * Arraw class name
  18964. *
  18965. * @type String
  18966. */
  18967. arrow = fm.res(c, 'navarrow'),
  18968. /**
  18969. * Current directory class name
  18970. *
  18971. * @type String
  18972. */
  18973. active = fm.res(c, 'active'),
  18974. /**
  18975. * Droppable dirs dropover class
  18976. *
  18977. * @type String
  18978. */
  18979. dropover = fm.res(c, 'adroppable'),
  18980. /**
  18981. * Hover class name
  18982. *
  18983. * @type String
  18984. */
  18985. hover = fm.res(c, 'hover'),
  18986. /**
  18987. * Disabled dir class name
  18988. *
  18989. * @type String
  18990. */
  18991. disabled = fm.res(c, 'disabled'),
  18992. /**
  18993. * Draggable dir class name
  18994. *
  18995. * @type String
  18996. */
  18997. draggable = fm.res(c, 'draggable'),
  18998. /**
  18999. * Droppable dir class name
  19000. *
  19001. * @type String
  19002. */
  19003. droppable = fm.res(c, 'droppable'),
  19004. /**
  19005. * root wrapper class
  19006. *
  19007. * @type String
  19008. */
  19009. wrapperRoot = 'elfinder-navbar-wrapper-root',
  19010. /**
  19011. * Un-disabled cmd `paste` volume's root wrapper class
  19012. *
  19013. * @type String
  19014. */
  19015. pastable = 'elfinder-navbar-wrapper-pastable',
  19016. /**
  19017. * Un-disabled cmd `upload` volume's root wrapper class
  19018. *
  19019. * @type String
  19020. */
  19021. uploadable = 'elfinder-navbar-wrapper-uploadable',
  19022. /**
  19023. * Is position x inside Navbar
  19024. *
  19025. * @param x Numbar
  19026. *
  19027. * @return
  19028. */
  19029. insideNavbar = function(x) {
  19030. var left = navbar.offset().left;
  19031. return left <= x && x <= left + navbar.width();
  19032. },
  19033. /**
  19034. * To call subdirs elements queue
  19035. *
  19036. * @type Object
  19037. */
  19038. subdirsQue = {},
  19039. /**
  19040. * To exec subdirs elements ids
  19041. *
  19042. */
  19043. subdirsExecQue = [],
  19044. /**
  19045. * Request subdirs to backend
  19046. *
  19047. * @param id String
  19048. *
  19049. * @return Deferred
  19050. */
  19051. subdirs = function(ids) {
  19052. var targets = [];
  19053. $.each(ids, function(i, id) {
  19054. subdirsQue[id] && targets.push(fm.navId2Hash(id));
  19055. delete subdirsQue[id];
  19056. });
  19057. if (targets.length) {
  19058. return fm.request({
  19059. data: {
  19060. cmd: 'subdirs',
  19061. targets: targets,
  19062. preventDefault : true
  19063. }
  19064. }).done(function(res) {
  19065. if (res && res.subdirs) {
  19066. $.each(res.subdirs, function(hash, subdirs) {
  19067. var elm = fm.navHash2Elm(hash);
  19068. elm.removeClass(chksubdir);
  19069. elm[subdirs? 'addClass' : 'removeClass'](collapsed);
  19070. });
  19071. }
  19072. });
  19073. }
  19074. },
  19075. subdirsJobRes = null,
  19076. /**
  19077. * To check target element is in window of subdirs
  19078. *
  19079. * @return void
  19080. */
  19081. checkSubdirs = function() {
  19082. var ids = Object.keys(subdirsQue);
  19083. if (ids.length) {
  19084. subdirsJobRes && subdirsJobRes._abort();
  19085. execSubdirsTm && clearTimeout(execSubdirsTm);
  19086. subdirsExecQue = [];
  19087. subdirsJobRes = fm.asyncJob(function(id) {
  19088. return fm.isInWindow($('#'+id))? id : null;
  19089. }, ids, { numPerOnce: 200 })
  19090. .done(function(arr) {
  19091. if (arr.length) {
  19092. subdirsExecQue = arr;
  19093. execSubdirs();
  19094. }
  19095. });
  19096. }
  19097. },
  19098. subdirsPending = 0,
  19099. execSubdirsTm,
  19100. /**
  19101. * Exec subdirs as batch request
  19102. *
  19103. * @return void
  19104. */
  19105. execSubdirs = function() {
  19106. var cnt = opts.subdirsMaxConn - subdirsPending,
  19107. atOnce = fm.maxTargets? Math.min(fm.maxTargets, opts.subdirsAtOnce) : opts.subdirsAtOnce,
  19108. i, ids;
  19109. execSubdirsTm && cancelAnimationFrame(execSubdirsTm);
  19110. if (subdirsExecQue.length) {
  19111. if (cnt > 0) {
  19112. for (i = 0; i < cnt; i++) {
  19113. if (subdirsExecQue.length) {
  19114. subdirsPending++;
  19115. subdirs(subdirsExecQue.splice(0, atOnce)).always(function() {
  19116. subdirsPending--;
  19117. execSubdirs();
  19118. });
  19119. }
  19120. }
  19121. } else {
  19122. execSubdirsTm = requestAnimationFrame(function() {
  19123. subdirsExecQue.length && execSubdirs();
  19124. });
  19125. }
  19126. }
  19127. },
  19128. drop = fm.droppable.drop,
  19129. /**
  19130. * Droppable options
  19131. *
  19132. * @type Object
  19133. */
  19134. droppableopts = $.extend(true, {}, fm.droppable, {
  19135. // show subfolders on dropover
  19136. over : function(e, ui) {
  19137. var dst = $(this),
  19138. helper = ui.helper,
  19139. cl = hover+' '+dropover,
  19140. hash, status;
  19141. e.stopPropagation();
  19142. helper.data('dropover', helper.data('dropover') + 1);
  19143. dst.data('dropover', true);
  19144. if (ui.helper.data('namespace') !== fm.namespace || ! fm.insideWorkzone(e.pageX, e.pageY)) {
  19145. dst.removeClass(cl);
  19146. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  19147. return;
  19148. }
  19149. if (! insideNavbar(e.clientX)) {
  19150. dst.removeClass(cl);
  19151. return;
  19152. }
  19153. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  19154. dst.addClass(hover);
  19155. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  19156. dst.data('expandTimer', setTimeout(function() {
  19157. dst.is('.'+collapsed+'.'+hover) && dst.children('.'+arrow).trigger('click');
  19158. }, 500));
  19159. }
  19160. if (dst.is('.elfinder-ro,.elfinder-na')) {
  19161. dst.removeClass(dropover);
  19162. //helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  19163. return;
  19164. }
  19165. hash = fm.navId2Hash(dst.attr('id'));
  19166. dst.data('dropover', hash);
  19167. $.each(ui.helper.data('files'), function(i, h) {
  19168. if (h === hash || (fm.file(h).phash === hash && !ui.helper.hasClass('elfinder-drag-helper-plus'))) {
  19169. dst.removeClass(cl);
  19170. return false; // break $.each
  19171. }
  19172. });
  19173. if (helper.data('locked')) {
  19174. status = 'elfinder-drag-helper-plus';
  19175. } else {
  19176. status = 'elfinder-drag-helper-move';
  19177. if (e.shiftKey || e.ctrlKey || e.metaKey) {
  19178. status += ' elfinder-drag-helper-plus';
  19179. }
  19180. }
  19181. dst.hasClass(dropover) && helper.addClass(status);
  19182. requestAnimationFrame(function(){ dst.hasClass(dropover) && helper.addClass(status); });
  19183. },
  19184. out : function(e, ui) {
  19185. var dst = $(this),
  19186. helper = ui.helper;
  19187. e.stopPropagation();
  19188. if (insideNavbar(e.clientX)) {
  19189. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  19190. }
  19191. helper.data('dropover', Math.max(helper.data('dropover') - 1, 0));
  19192. dst.data('expandTimer') && clearTimeout(dst.data('expandTimer'));
  19193. dst.removeData('dropover')
  19194. .removeClass(hover+' '+dropover);
  19195. },
  19196. deactivate : function() {
  19197. $(this).removeData('dropover')
  19198. .removeClass(hover+' '+dropover);
  19199. },
  19200. drop : function(e, ui) {
  19201. insideNavbar(e.clientX) && drop.call(this, e, ui);
  19202. }
  19203. }),
  19204. spinner = $(fm.res('tpl', 'navspinner')),
  19205. /**
  19206. * Directory html template
  19207. *
  19208. * @type String
  19209. */
  19210. tpl = fm.res('tpl', 'navdir'),
  19211. /**
  19212. * Permissions marker html template
  19213. *
  19214. * @type String
  19215. */
  19216. ptpl = fm.res('tpl', 'perms'),
  19217. /**
  19218. * Lock marker html template
  19219. *
  19220. * @type String
  19221. */
  19222. ltpl = fm.res('tpl', 'lock'),
  19223. /**
  19224. * Symlink marker html template
  19225. *
  19226. * @type String
  19227. */
  19228. stpl = fm.res('tpl', 'symlink'),
  19229. /**
  19230. * Directory hashes that has more pages
  19231. *
  19232. * @type Object
  19233. */
  19234. hasMoreDirs = {},
  19235. /**
  19236. * Html template replacement methods
  19237. *
  19238. * @type Object
  19239. */
  19240. replace = {
  19241. id : function(dir) { return fm.navHash2Id(dir.hash); },
  19242. name : function(dir) { return fm.escape(dir.i18 || dir.name); },
  19243. cssclass : function(dir) {
  19244. var cname = (dir.phash && ! dir.isroot ? '' : root)+' '+navdir+' '+fm.perms2class(dir);
  19245. dir.dirs && !dir.link && (cname += ' ' + collapsed) && dir.dirs == -1 && (cname += ' ' + chksubdir);
  19246. opts.getClass && (cname += ' ' + opts.getClass(dir));
  19247. dir.csscls && (cname += ' ' + fm.escape(dir.csscls));
  19248. return cname;
  19249. },
  19250. title : function(dir) { return opts.attrTitle? (' title="' + fm.escape(fm.path(dir.hash, true) || dir.i18 || dir.name) + '"') : ''; },
  19251. root : function(dir) {
  19252. var cls = '';
  19253. if (!dir.phash || dir.isroot) {
  19254. cls += ' '+wrapperRoot;
  19255. if (!dir.disabled || dir.disabled.length < 1) {
  19256. cls += ' '+pastable+' '+uploadable;
  19257. } else {
  19258. if ($.inArray('paste', dir.disabled) === -1) {
  19259. cls += ' '+pastable;
  19260. }
  19261. if ($.inArray('upload', dir.disabled) === -1) {
  19262. cls += ' '+uploadable;
  19263. }
  19264. }
  19265. return cls;
  19266. } else {
  19267. return '';
  19268. }
  19269. },
  19270. permissions : function(dir) { return !dir.read || !dir.write ? ptpl : ''; },
  19271. symlink : function(dir) { return dir.alias ? stpl : ''; },
  19272. style : function(dir) { return dir.icon ? fm.getIconStyle(dir) : ''; }
  19273. },
  19274. /**
  19275. * Return html for given dir
  19276. *
  19277. * @param Object directory
  19278. * @return String
  19279. */
  19280. itemhtml = function(dir) {
  19281. return tpl.replace(/(?:\{([a-z]+)\})/ig, function(m, key) {
  19282. var res = replace[key] ? replace[key](dir) : (dir[key] || '');
  19283. if (key === 'id' && dir.dirs == -1) {
  19284. subdirsQue[res] = res;
  19285. }
  19286. return res;
  19287. });
  19288. },
  19289. /**
  19290. * Return only dirs from files list
  19291. *
  19292. * @param Array files list
  19293. * @param Boolean do check exists
  19294. * @return Array
  19295. */
  19296. filter = function(files, checkExists) {
  19297. return $.map(files || [], function(f) {
  19298. return (f.mime === 'directory' && (!checkExists || fm.navHash2Elm(f.hash).length)) ? f : null;
  19299. });
  19300. },
  19301. /**
  19302. * Find parent subtree for required directory
  19303. *
  19304. * @param String dir hash
  19305. * @return jQuery
  19306. */
  19307. findSubtree = function(hash) {
  19308. return hash ? fm.navHash2Elm(hash).next('.'+subtree) : tree;
  19309. },
  19310. /**
  19311. * Find directory (wrapper) in required node
  19312. * before which we can insert new directory
  19313. *
  19314. * @param jQuery parent directory
  19315. * @param Object new directory
  19316. * @return jQuery
  19317. */
  19318. findSibling = function(subtree, dir) {
  19319. var node = subtree.children(':first'),
  19320. info;
  19321. while (node.length) {
  19322. info = fm.file(fm.navId2Hash(node.children('[id]').attr('id')));
  19323. if ((info = fm.file(fm.navId2Hash(node.children('[id]').attr('id'))))
  19324. && compare(dir, info) < 0) {
  19325. return node;
  19326. }
  19327. node = node.next();
  19328. }
  19329. return subtree.children('button.elfinder-navbar-pager-next');
  19330. },
  19331. /**
  19332. * Add new dirs in tree
  19333. *
  19334. * @param Array dirs list
  19335. * @return void
  19336. */
  19337. updateTree = function(dirs) {
  19338. var length = dirs.length,
  19339. orphans = [],
  19340. i = length,
  19341. tgts = $(),
  19342. done = {},
  19343. cwd = fm.cwd(),
  19344. append = function(parent, dirs, start, direction) {
  19345. var hashes = {},
  19346. curStart = 0,
  19347. max = fm.newAPI? Math.min(10000, Math.max(10, opts.subTreeMax)) : 10000,
  19348. setHashes = function() {
  19349. hashes = {};
  19350. $.each(dirs, function(i, d) {
  19351. hashes[d.hash] = i;
  19352. });
  19353. },
  19354. change = function(mode) {
  19355. if (mode === 'prepare') {
  19356. $.each(dirs, function(i, d) {
  19357. d.node && parent.append(d.node.hide());
  19358. });
  19359. } else if (mode === 'done') {
  19360. $.each(dirs, function(i, d) {
  19361. d.node && d.node.detach().show();
  19362. });
  19363. }
  19364. },
  19365. update = function(e, data) {
  19366. var i, changed;
  19367. e.stopPropagation();
  19368. if (data.select) {
  19369. render(getStart(data.select));
  19370. return;
  19371. }
  19372. if (data.change) {
  19373. change(data.change);
  19374. return;
  19375. }
  19376. if (data.removed && data.removed.length) {
  19377. dirs = $.grep(dirs, function(d) {
  19378. if (data.removed.indexOf(d.hash) === -1) {
  19379. return true;
  19380. } else {
  19381. !changed && (changed = true);
  19382. return false;
  19383. }
  19384. });
  19385. }
  19386. if (data.added && data.added.length) {
  19387. dirs = dirs.concat($.grep(data.added, function(d) {
  19388. if (hashes[d.hash] === void(0)) {
  19389. !changed && (changed = true);
  19390. return true;
  19391. } else {
  19392. return false;
  19393. }
  19394. }));
  19395. }
  19396. if (changed) {
  19397. dirs.sort(compare);
  19398. setHashes();
  19399. render(curStart);
  19400. }
  19401. },
  19402. getStart = function(target) {
  19403. if (hashes[target] !== void(0)) {
  19404. return Math.floor(hashes[target] / max) * max;
  19405. }
  19406. return void(0);
  19407. },
  19408. target = fm.navId2Hash(parent.prev('[id]').attr('id')),
  19409. render = function(start, direction) {
  19410. var html = [],
  19411. nodes = {},
  19412. total, page, s, parts, prev, next, prevBtn, nextBtn;
  19413. delete hasMoreDirs[target];
  19414. curStart = start;
  19415. parent.off('update.'+fm.namespace, update);
  19416. if (dirs.length > max) {
  19417. parent.on('update.'+fm.namespace, update);
  19418. if (start === void(0)) {
  19419. s = 0;
  19420. setHashes();
  19421. start = getStart(cwd.hash);
  19422. if (start === void(0)) {
  19423. start = 0;
  19424. }
  19425. }
  19426. parts = dirs.slice(start, start + max);
  19427. hasMoreDirs[target] = parent;
  19428. prev = start? Math.max(-1, start - max) : -1;
  19429. next = (start + max >= dirs.length)? 0 : start + max;
  19430. total = Math.ceil(dirs.length/max);
  19431. page = Math.ceil(start/max);
  19432. }
  19433. $.each(parts || dirs, function(i, d) {
  19434. html.push(itemhtml(d));
  19435. if (d.node) {
  19436. nodes[d.hash] = d.node;
  19437. }
  19438. });
  19439. if (prev > -1) {
  19440. prevBtn = $('<button class="elfinder-navbar-pager elfinder-navbar-pager-prev"/>')
  19441. .text(fm.i18n('btnPrevious', page, total))
  19442. .button({
  19443. icons: {
  19444. primary: "ui-icon-caret-1-n"
  19445. }
  19446. })
  19447. .on('click', function(e) {
  19448. e.preventDefault();
  19449. e.stopPropagation();
  19450. render(prev, 'up');
  19451. });
  19452. } else {
  19453. prevBtn = $();
  19454. }
  19455. if (next) {
  19456. nextBtn = $('<button class="elfinder-navbar-pager elfinder-navbar-pager-next"/>')
  19457. .text(fm.i18n('btnNext', page + 2, total))
  19458. .button({
  19459. icons: {
  19460. primary: "ui-icon-caret-1-s"
  19461. }
  19462. })
  19463. .on('click', function(e) {
  19464. e.preventDefault();
  19465. e.stopPropagation();
  19466. render(next, 'down');
  19467. });
  19468. } else {
  19469. nextBtn = $();
  19470. }
  19471. detach();
  19472. parent.empty()[parts? 'addClass' : 'removeClass']('elfinder-navbar-hasmore').append(prevBtn, html.join(''), nextBtn);
  19473. $.each(nodes, function(h, n) {
  19474. fm.navHash2Elm(h).parent().replaceWith(n);
  19475. });
  19476. if (direction) {
  19477. autoScroll(fm.navHash2Id(parts[direction === 'up'? parts.length - 1 : 0].hash));
  19478. }
  19479. ! mobile && fm.lazy(function() { updateDroppable(null, parent); });
  19480. },
  19481. detach = function() {
  19482. $.each(parent.children('.elfinder-navbar-wrapper'), function(i, elm) {
  19483. var n = $(elm),
  19484. ch = n.children('[id]:first'),
  19485. h, c;
  19486. if (ch.hasClass(loaded)) {
  19487. h = fm.navId2Hash(ch.attr('id'));
  19488. if (h && (c = hashes[h]) !== void(0)) {
  19489. dirs[c].node = n.detach();
  19490. }
  19491. }
  19492. });
  19493. };
  19494. render();
  19495. },
  19496. dir, html, parent, sibling, init, atonce = {}, updates = [], base, node,
  19497. firstVol = true; // check for netmount volume
  19498. while (i--) {
  19499. dir = dirs[i];
  19500. if (done[dir.hash] || fm.navHash2Elm(dir.hash).length) {
  19501. continue;
  19502. }
  19503. done[dir.hash] = true;
  19504. if ((parent = findSubtree(dir.phash)).length) {
  19505. if (dir.phash && ((init = !parent.children().length) || parent.hasClass('elfinder-navbar-hasmore') || (sibling = findSibling(parent, dir)).length)) {
  19506. if (init) {
  19507. if (!atonce[dir.phash]) {
  19508. atonce[dir.phash] = [];
  19509. }
  19510. atonce[dir.phash].push(dir);
  19511. } else {
  19512. if (sibling) {
  19513. node = itemhtml(dir);
  19514. sibling.before(node);
  19515. ! mobile && (tgts = tgts.add(node));
  19516. } else {
  19517. updates.push(dir);
  19518. }
  19519. }
  19520. } else {
  19521. node = itemhtml(dir);
  19522. parent[firstVol || dir.phash ? 'append' : 'prepend'](node);
  19523. firstVol = false;
  19524. if (!dir.phash || dir.isroot) {
  19525. base = fm.navHash2Elm(dir.hash).parent();
  19526. }
  19527. ! mobile && updateDroppable(null, base);
  19528. }
  19529. } else {
  19530. orphans.push(dir);
  19531. }
  19532. }
  19533. // When init, html append at once
  19534. if (Object.keys(atonce).length){
  19535. $.each(atonce, function(p, dirs){
  19536. var parent = findSubtree(p),
  19537. html = [];
  19538. dirs.sort(compare);
  19539. append(parent, dirs);
  19540. });
  19541. }
  19542. if (updates.length) {
  19543. parent.trigger('update.' + fm.namespace, { added : updates });
  19544. }
  19545. if (orphans.length && orphans.length < length) {
  19546. updateTree(orphans);
  19547. return;
  19548. }
  19549. ! mobile && tgts.length && fm.lazy(function() { updateDroppable(tgts); });
  19550. },
  19551. /**
  19552. * sort function by dir.name
  19553. *
  19554. */
  19555. compare = function(dir1, dir2) {
  19556. if (! fm.sortAlsoTreeview) {
  19557. return fm.sortRules.name(dir1, dir2);
  19558. } else {
  19559. var asc = fm.sortOrder == 'asc',
  19560. type = fm.sortType,
  19561. rules = fm.sortRules,
  19562. res;
  19563. res = asc? rules[fm.sortType](dir1, dir2) : rules[fm.sortType](dir2, dir1);
  19564. return type !== 'name' && res === 0
  19565. ? res = asc ? rules.name(dir1, dir2) : rules.name(dir2, dir1)
  19566. : res;
  19567. }
  19568. },
  19569. /**
  19570. * Timer ID of autoScroll
  19571. *
  19572. * @type Integer
  19573. */
  19574. autoScrTm,
  19575. /**
  19576. * Auto scroll to cwd
  19577. *
  19578. * @return Object jQuery Deferred
  19579. */
  19580. autoScroll = function(target) {
  19581. var dfrd = $.Deferred(),
  19582. current, parent, top, treeH, bottom, tgtTop;
  19583. autoScrTm && clearTimeout(autoScrTm);
  19584. autoScrTm = setTimeout(function() {
  19585. current = $(document.getElementById((target || fm.navHash2Id(fm.cwd().hash))));
  19586. if (current.length) {
  19587. // expand parents directory
  19588. (openCwd? current : current.parent()).parents('.elfinder-navbar-wrapper').children('.'+loaded).addClass(expanded).next('.'+subtree).show();
  19589. parent = tree.parent().stop(false, true);
  19590. top = parent.offset().top;
  19591. treeH = parent.height();
  19592. bottom = top + treeH - current.outerHeight();
  19593. tgtTop = current.offset().top;
  19594. if (tgtTop < top || tgtTop > bottom) {
  19595. parent.animate({
  19596. scrollTop : parent.scrollTop() + tgtTop - top - treeH / 3
  19597. }, {
  19598. duration : opts.durations.autoScroll,
  19599. complete : function() { dfrd.resolve(); }
  19600. });
  19601. } else {
  19602. dfrd.resolve();
  19603. }
  19604. } else {
  19605. dfrd.reject();
  19606. }
  19607. }, 100);
  19608. return dfrd;
  19609. },
  19610. /**
  19611. * Get hashes array of items of the bottom of the leaf root back from the target
  19612. *
  19613. * @param Object elFinder item(directory) object
  19614. * @return Array hashes
  19615. */
  19616. getEnds = function(d) {
  19617. var cur = d || fm.cwd(),
  19618. res = cur.hash? [ cur.hash ] : [],
  19619. phash, root, dir;
  19620. root = fm.root(cur.hash);
  19621. dir = fm.file(root);
  19622. while (dir && (phash = dir.phash)) {
  19623. res.unshift(phash);
  19624. root = fm.root(phash);
  19625. dir = fm.file(root);
  19626. if (fm.navHash2Elm(dir.hash).hasClass(loaded)) {
  19627. break;
  19628. }
  19629. }
  19630. return res;
  19631. },
  19632. /**
  19633. * Select pages back in order to display the target
  19634. *
  19635. * @param Object elFinder item(directory) object
  19636. * @return Object jQuery node object of target node
  19637. */
  19638. selectPages = function(current) {
  19639. var cur = current || fm.cwd(),
  19640. curHash = cur.hash,
  19641. node = fm.navHash2Elm(curHash);
  19642. if (!node.length) {
  19643. while(cur && cur.phash) {
  19644. if (hasMoreDirs[cur.phash] && !fm.navHash2Elm(cur.hash).length) {
  19645. hasMoreDirs[cur.phash].trigger('update.'+fm.namespace, { select : cur.hash });
  19646. }
  19647. cur = fm.file(cur.phash);
  19648. }
  19649. node = fm.navHash2Elm(curHash);
  19650. }
  19651. return node;
  19652. },
  19653. /**
  19654. * Flag indicating that synchronization is currently in progress
  19655. *
  19656. * @type Boolean
  19657. */
  19658. syncing,
  19659. /**
  19660. * Mark current directory as active
  19661. * If current directory is not in tree - load it and its parents
  19662. *
  19663. * @param Array directory objects of cwd
  19664. * @param Boolean do auto scroll
  19665. * @return Object jQuery Deferred
  19666. */
  19667. sync = function(cwdDirs, aScr) {
  19668. var cwd = fm.cwd(),
  19669. cwdhash = cwd.hash,
  19670. autoScr = aScr === void(0)? syncTree : aScr,
  19671. loadParents = function(dir) {
  19672. var dfd = $.Deferred(),
  19673. reqs = [],
  19674. ends = getEnds(dir),
  19675. makeReq = function(cmd, h, until) {
  19676. var data = {
  19677. cmd : cmd,
  19678. target : h
  19679. };
  19680. if (until) {
  19681. data.until = until;
  19682. }
  19683. return fm.request({
  19684. data : data,
  19685. preventFail : true
  19686. });
  19687. },
  19688. baseHash, baseId;
  19689. reqs = $.map(ends, function(h) {
  19690. var d = fm.file(h),
  19691. isRoot = d? fm.isRoot(d) : false,
  19692. node = fm.navHash2Elm(h),
  19693. getPhash = function(h, dep) {
  19694. var d, ph,
  19695. depth = dep || 1;
  19696. ph = (d = fm.file(h))? d.phash : false;
  19697. if (ph && depth > 1) {
  19698. return getPhash(ph, --depth);
  19699. }
  19700. return ph;
  19701. },
  19702. until,
  19703. closest = (function() {
  19704. var phash = getPhash(h);
  19705. until = phash;
  19706. while (phash) {
  19707. if (fm.navHash2Elm(phash).hasClass(loaded)) {
  19708. break;
  19709. }
  19710. until = phash;
  19711. phash = getPhash(phash);
  19712. }
  19713. if (!phash) {
  19714. until = void(0);
  19715. phash = fm.root(h);
  19716. }
  19717. return phash;
  19718. })(),
  19719. cmd;
  19720. if (!node.hasClass(loaded) && (isRoot || !d || !fm.navHash2Elm(d.phash).hasClass(loaded))) {
  19721. if (isRoot || closest === getPhash(h) || closest === getPhash(h, 2)) {
  19722. until = void(0);
  19723. cmd = 'tree';
  19724. if (!isRoot) {
  19725. h = getPhash(h);
  19726. }
  19727. } else {
  19728. cmd = 'parents';
  19729. }
  19730. if (!baseHash) {
  19731. baseHash = (cmd === 'tree')? h : closest;
  19732. }
  19733. return makeReq(cmd, h, until);
  19734. }
  19735. return null;
  19736. });
  19737. if (reqs.length) {
  19738. selectPages(fm.file(baseHash));
  19739. baseId = fm.navHash2Id(baseHash);
  19740. autoScr && autoScroll(baseId);
  19741. baseNode = $('#'+baseId);
  19742. spinner = $(fm.res('tpl', 'navspinner')).insertBefore(baseNode.children('.'+arrow));
  19743. baseNode.removeClass(collapsed);
  19744. $.when.apply($, reqs)
  19745. .done(function() {
  19746. var res = {},data, treeDirs, dirs, argLen, i;
  19747. argLen = arguments.length;
  19748. if (argLen > 0) {
  19749. for (i = 0; i < argLen; i++) {
  19750. data = arguments[i].tree || [];
  19751. res[ends[i]] = Object.assign([], filter(data));
  19752. }
  19753. }
  19754. dfd.resolve(res);
  19755. })
  19756. .fail(function() {
  19757. dfd.reject();
  19758. });
  19759. return dfd;
  19760. } else {
  19761. return dfd.resolve();
  19762. }
  19763. },
  19764. done= function(res, dfrd) {
  19765. var open = function() {
  19766. if (openRoot && baseNode) {
  19767. findSubtree(baseNode.hash).show().prev(selNavdir).addClass(expanded);
  19768. openRoot = false;
  19769. }
  19770. if (autoScr) {
  19771. autoScroll().done(checkSubdirs);
  19772. } else {
  19773. checkSubdirs();
  19774. }
  19775. },
  19776. current;
  19777. if (res) {
  19778. $.each(res, function(endHash, dirs) {
  19779. dirs && updateTree(dirs);
  19780. selectPages(fm.file(endHash));
  19781. dirs && updateArrows(dirs, loaded);
  19782. });
  19783. }
  19784. if (cwdDirs) {
  19785. (fm.api < 2.1) && cwdDirs.push(cwd);
  19786. updateTree(cwdDirs);
  19787. }
  19788. // set current node
  19789. current = selectPages();
  19790. if (!current.hasClass(active)) {
  19791. tree.find(selNavdir+'.'+active).removeClass(active);
  19792. current.addClass(active);
  19793. }
  19794. // mark as loaded to cwd parents
  19795. current.parents('.elfinder-navbar-wrapper').children('.'+navdir).addClass(loaded);
  19796. if (res) {
  19797. fm.lazy(open).done(function() {
  19798. dfrd.resolve();
  19799. });
  19800. } else {
  19801. open();
  19802. dfrd.resolve();
  19803. }
  19804. },
  19805. rmSpinner = function(fail) {
  19806. if (baseNode) {
  19807. spinner.remove();
  19808. baseNode.addClass(collapsed + (fail? '' : (' ' + loaded)));
  19809. }
  19810. },
  19811. dfrd = $.Deferred(),
  19812. baseNode, spinner;
  19813. if (!fm.navHash2Elm(cwdhash).length) {
  19814. syncing = true;
  19815. loadParents()
  19816. .done(function(res) {
  19817. done(res, dfrd);
  19818. rmSpinner();
  19819. })
  19820. .fail(function() {
  19821. rmSpinner(true);
  19822. dfrd.reject();
  19823. })
  19824. .always(function() {
  19825. syncing = false;
  19826. });
  19827. } else {
  19828. done(void(0), dfrd);
  19829. }
  19830. // trigger 'treesync' with my $.Deferred
  19831. fm.trigger('treesync', dfrd);
  19832. return dfrd;
  19833. },
  19834. /**
  19835. * Make writable and not root dirs droppable
  19836. *
  19837. * @return void
  19838. */
  19839. updateDroppable = function(target, node) {
  19840. var limit = 100,
  19841. next;
  19842. if (!target) {
  19843. if (!node || node.closest('div.'+wrapperRoot).hasClass(uploadable)) {
  19844. (node || tree.find('div.'+uploadable)).find(selNavdir+':not(.elfinder-ro,.elfinder-na)').addClass('native-droppable');
  19845. }
  19846. if (!node || node.closest('div.'+wrapperRoot).hasClass(pastable)) {
  19847. target = (node || tree.find('div.'+pastable)).find(selNavdir+':not(.'+droppable+')');
  19848. } else {
  19849. target = $();
  19850. }
  19851. if (node) {
  19852. // check leaf roots
  19853. node.children('div.'+wrapperRoot).each(function() {
  19854. updateDroppable(null, $(this));
  19855. });
  19856. }
  19857. }
  19858. // make droppable on async
  19859. if (target.length) {
  19860. fm.asyncJob(function(elm) {
  19861. $(elm).droppable(droppableopts);
  19862. }, $.makeArray(target), {
  19863. interval : 20,
  19864. numPerOnce : 100
  19865. });
  19866. }
  19867. },
  19868. /**
  19869. * Check required folders for subfolders and update arrow classes
  19870. *
  19871. * @param Array folders to check
  19872. * @param String css class
  19873. * @return void
  19874. */
  19875. updateArrows = function(dirs, cls) {
  19876. var sel = cls == loaded
  19877. ? '.'+collapsed+':not(.'+loaded+')'
  19878. : ':not(.'+collapsed+')';
  19879. $.each(dirs, function(i, dir) {
  19880. fm.navHash2Elm(dir.phash).filter(sel)
  19881. .filter(function() { return $.grep($(this).next('.'+subtree).children(), function(n) {
  19882. return ($(n).children().hasClass(root))? false : true;
  19883. }).length > 0; })
  19884. .addClass(cls);
  19885. });
  19886. },
  19887. /**
  19888. * Navigation tree
  19889. *
  19890. * @type JQuery
  19891. */
  19892. tree = $(this).addClass(treeclass)
  19893. // make dirs draggable and toggle hover class
  19894. .on('mouseenter mouseleave', selNavdir, function(e) {
  19895. var enter = (e.type === 'mouseenter');
  19896. if (enter && scrolling) { return; }
  19897. var link = $(this),
  19898. hash, dir;
  19899. if (!link.hasClass(dropover+' '+disabled)) {
  19900. if (!mobile && enter && !link.data('dragRegisted') && !link.hasClass(root+' '+draggable+' elfinder-na elfinder-wo')) {
  19901. link.data('dragRegisted', true);
  19902. if (fm.isCommandEnabled('copy', (hash = fm.navId2Hash(link.attr('id'))))) {
  19903. link.draggable(fm.draggable);
  19904. }
  19905. }
  19906. link.toggleClass(hover, enter);
  19907. }
  19908. // update title attr if necessary
  19909. if (enter && opts.attrTitle) {
  19910. dir = fm.file(hash || fm.navId2Hash(link.attr('id')));
  19911. if (!dir.isroot && link.attr('title') === (dir.i18 || dir.name)) {
  19912. link.attr('title', fm.path(hash, true));
  19913. }
  19914. }
  19915. })
  19916. // native drag enter
  19917. .on('dragenter', selNavdir, function(e) {
  19918. if (e.originalEvent.dataTransfer) {
  19919. var dst = $(this);
  19920. dst.addClass(hover);
  19921. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  19922. dst.data('expandTimer', setTimeout(function() {
  19923. dst.is('.'+collapsed+'.'+hover) && dst.children('.'+arrow).trigger('click');
  19924. }, 500));
  19925. }
  19926. }
  19927. })
  19928. // native drag leave
  19929. .on('dragleave', selNavdir, function(e) {
  19930. if (e.originalEvent.dataTransfer) {
  19931. var dst = $(this);
  19932. dst.data('expandTimer') && clearTimeout(dst.data('expandTimer'));
  19933. dst.removeClass(hover);
  19934. }
  19935. })
  19936. // open dir or open subfolders in tree
  19937. .on('click', selNavdir, function(e) {
  19938. var link = $(this),
  19939. hash = fm.navId2Hash(link.attr('id')),
  19940. file = fm.file(hash);
  19941. if (link.data('longtap')) {
  19942. link.removeData('longtap');
  19943. e.stopPropagation();
  19944. return;
  19945. }
  19946. if (!link.hasClass(active)) {
  19947. tree.find(selNavdir+'.'+active).removeClass(active);
  19948. link.addClass(active);
  19949. }
  19950. if (hash != fm.cwd().hash && !link.hasClass(disabled)) {
  19951. fm.exec('open', hash).done(function() {
  19952. fm.one('opendone', function() {
  19953. fm.select({selected: [hash], origin: 'navbar'});
  19954. });
  19955. });
  19956. } else {
  19957. if (link.hasClass(collapsed)) {
  19958. link.children('.'+arrow).trigger('click');
  19959. }
  19960. fm.select({selected: [hash], origin: 'navbar'});
  19961. }
  19962. })
  19963. // for touch device
  19964. .on('touchstart', selNavdir, function(e) {
  19965. if (e.originalEvent.touches.length > 1) {
  19966. return;
  19967. }
  19968. var evt = e.originalEvent,
  19969. p;
  19970. if (e.target.nodeName === 'INPUT') {
  19971. e.stopPropagation();
  19972. return;
  19973. }
  19974. p = $(this).addClass(hover)
  19975. .removeData('longtap')
  19976. .data('tmlongtap', setTimeout(function(e){
  19977. // long tap
  19978. p.data('longtap', true);
  19979. fm.trigger('contextmenu', {
  19980. 'type' : 'navbar',
  19981. 'targets' : [fm.navId2Hash(p.attr('id'))],
  19982. 'x' : evt.touches[0].pageX,
  19983. 'y' : evt.touches[0].pageY
  19984. });
  19985. }, 500));
  19986. })
  19987. .on('touchmove touchend', selNavdir, function(e) {
  19988. if (e.target.nodeName === 'INPUT') {
  19989. e.stopPropagation();
  19990. return;
  19991. }
  19992. clearTimeout($(this).data('tmlongtap'));
  19993. $(this).removeData('tmlongtap');
  19994. if (e.type == 'touchmove') {
  19995. $(this).removeClass(hover);
  19996. }
  19997. })
  19998. // toggle subfolders in tree
  19999. .on('click', selNavdir+'.'+collapsed+' .'+arrow, function(e) {
  20000. var arrow = $(this),
  20001. link = arrow.parent(selNavdir),
  20002. stree = link.next('.'+subtree),
  20003. dfrd = $.Deferred(),
  20004. slideTH = 30, cnt;
  20005. e.stopPropagation();
  20006. if (link.hasClass(loaded)) {
  20007. link.toggleClass(expanded);
  20008. fm.lazy(function() {
  20009. cnt = link.hasClass(expanded)? stree.children().length + stree.find('div.elfinder-navbar-subtree[style*=block]').children().length : stree.find('div:visible').length;
  20010. if (cnt > slideTH) {
  20011. stree.toggle();
  20012. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  20013. checkSubdirs();
  20014. } else {
  20015. stree.stop(true, true)[link.hasClass(expanded)? 'slideDown' : 'slideUp'](opts.durations.slideUpDown, function(){
  20016. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  20017. checkSubdirs();
  20018. });
  20019. }
  20020. }).always(function() {
  20021. dfrd.resolve();
  20022. });
  20023. } else {
  20024. spinner.insertBefore(arrow);
  20025. link.removeClass(collapsed);
  20026. fm.request({cmd : 'tree', target : fm.navId2Hash(link.attr('id'))})
  20027. .done(function(data) {
  20028. updateTree(Object.assign([], filter(data.tree)));
  20029. if (stree.children().length) {
  20030. link.addClass(collapsed+' '+expanded);
  20031. if (stree.children().length > slideTH) {
  20032. stree.show();
  20033. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  20034. checkSubdirs();
  20035. } else {
  20036. stree.stop(true, true).slideDown(opts.durations.slideUpDown, function(){
  20037. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  20038. checkSubdirs();
  20039. });
  20040. }
  20041. }
  20042. })
  20043. .always(function(data) {
  20044. spinner.remove();
  20045. link.addClass(loaded);
  20046. fm.one('treedone', function() {
  20047. dfrd.resolve();
  20048. });
  20049. });
  20050. }
  20051. arrow.data('dfrd', dfrd);
  20052. })
  20053. .on('contextmenu', selNavdir, function(e) {
  20054. var self = $(this);
  20055. // now dirname editing
  20056. if (self.find('input:text').length) {
  20057. e.stopPropagation();
  20058. return;
  20059. }
  20060. e.preventDefault();
  20061. if (!self.data('tmlongtap')) {
  20062. fm.trigger('contextmenu', {
  20063. 'type' : 'navbar',
  20064. 'targets' : [fm.navId2Hash($(this).attr('id'))],
  20065. 'x' : e.pageX,
  20066. 'y' : e.pageY
  20067. });
  20068. }
  20069. self.addClass('ui-state-hover');
  20070. fm.getUI('contextmenu').children().on('mouseenter', function() {
  20071. self.addClass('ui-state-hover');
  20072. });
  20073. fm.bind('closecontextmenu', function() {
  20074. self.removeClass('ui-state-hover');
  20075. });
  20076. })
  20077. .on('scrolltoview', selNavdir, function(e, data) {
  20078. var self = $(this);
  20079. autoScroll(self.attr('id')).done(function() {
  20080. if (!data || data.blink === 'undefined' || data.blink) {
  20081. fm.resources.blink(self, 'lookme');
  20082. }
  20083. });
  20084. })
  20085. // prepend fake dir
  20086. .on('create.'+fm.namespace, function(e, item) {
  20087. var pdir = findSubtree(item.phash),
  20088. lock = item.move || false,
  20089. dir = $(itemhtml(item)).addClass('elfinder-navbar-wrapper-tmp'),
  20090. selected = fm.selected();
  20091. lock && selected.length && fm.trigger('lockfiles', {files: selected});
  20092. pdir.prepend(dir);
  20093. }),
  20094. scrolling = false,
  20095. navbarScrTm,
  20096. // move tree into navbar
  20097. navbar = fm.getUI('navbar').append(tree).show().on('scroll', function() {
  20098. scrolling = true;
  20099. navbarScrTm && cancelAnimationFrame(navbarScrTm);
  20100. navbarScrTm = requestAnimationFrame(function() {
  20101. scrolling = false;
  20102. checkSubdirs();
  20103. });
  20104. }),
  20105. prevSortTreeview = fm.sortAlsoTreeview;
  20106. fm.open(function(e) {
  20107. var data = e.data,
  20108. dirs = filter(data.files),
  20109. contextmenu = fm.getUI('contextmenu');
  20110. data.init && tree.empty();
  20111. if (fm.UA.iOS) {
  20112. navbar.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch');
  20113. }
  20114. if (dirs.length) {
  20115. fm.lazy(function() {
  20116. if (!contextmenu.data('cmdMaps')) {
  20117. contextmenu.data('cmdMaps', {});
  20118. }
  20119. updateTree(dirs);
  20120. updateArrows(dirs, loaded);
  20121. sync(dirs);
  20122. });
  20123. } else {
  20124. sync();
  20125. }
  20126. })
  20127. // add new dirs
  20128. .add(function(e) {
  20129. var dirs = filter(e.data.added);
  20130. if (dirs.length) {
  20131. updateTree(dirs);
  20132. updateArrows(dirs, collapsed);
  20133. }
  20134. })
  20135. // update changed dirs
  20136. .change(function(e) {
  20137. // do ot perfome while syncing
  20138. if (syncing) {
  20139. return;
  20140. }
  20141. var dirs = filter(e.data.changed, true),
  20142. length = dirs.length,
  20143. l = length,
  20144. tgts = $(),
  20145. changed = {},
  20146. dir, phash, node, tmp, realParent, reqParent, realSibling, reqSibling, isExpanded, isLoaded, parent, subdirs;
  20147. $.each(hasMoreDirs, function(h, node) {
  20148. node.trigger('update.'+fm.namespace, { change: 'prepare' });
  20149. });
  20150. while (l--) {
  20151. dir = dirs[l];
  20152. phash = dir.phash;
  20153. if ((node = fm.navHash2Elm(dir.hash)).length) {
  20154. parent = node.parent();
  20155. if (phash) {
  20156. realParent = node.closest('.'+subtree);
  20157. reqParent = findSubtree(phash);
  20158. realSibling = node.parent().next();
  20159. reqSibling = findSibling(reqParent, dir);
  20160. if (!reqParent.length) {
  20161. continue;
  20162. }
  20163. if (reqParent[0] !== realParent[0] || realSibling.get(0) !== reqSibling.get(0)) {
  20164. reqSibling.length ? reqSibling.before(parent) : reqParent.append(parent);
  20165. }
  20166. }
  20167. isExpanded = node.hasClass(expanded);
  20168. isLoaded = node.hasClass(loaded);
  20169. tmp = $(itemhtml(dir));
  20170. node.replaceWith(tmp.children(selNavdir));
  20171. ! mobile && updateDroppable(null, parent);
  20172. if (dir.dirs
  20173. && (isExpanded || isLoaded)
  20174. && (node = fm.navHash2Elm(dir.hash))
  20175. && node.next('.'+subtree).children().length) {
  20176. isExpanded && node.addClass(expanded);
  20177. isLoaded && node.addClass(loaded);
  20178. }
  20179. subdirs |= dir.dirs == -1;
  20180. }
  20181. }
  20182. // to check subdirs
  20183. if (subdirs) {
  20184. checkSubdirs();
  20185. }
  20186. $.each(hasMoreDirs, function(h, node) {
  20187. node.trigger('update.'+fm.namespace, { change: 'done' });
  20188. });
  20189. length && sync(void(0), false);
  20190. })
  20191. // remove dirs
  20192. .remove(function(e) {
  20193. var dirs = e.data.removed,
  20194. l = dirs.length,
  20195. node, stree, removed;
  20196. $.each(hasMoreDirs, function(h, node) {
  20197. node.trigger('update.'+fm.namespace, { removed : dirs });
  20198. node.trigger('update.'+fm.namespace, { change: 'prepare' });
  20199. });
  20200. while (l--) {
  20201. if ((node = fm.navHash2Elm(dirs[l])).length) {
  20202. removed = true;
  20203. stree = node.closest('.'+subtree);
  20204. node.parent().detach();
  20205. if (!stree.children().length) {
  20206. stree.hide().prev(selNavdir).removeClass(collapsed+' '+expanded+' '+loaded);
  20207. }
  20208. }
  20209. }
  20210. removed && fm.getUI('navbar').children('.ui-resizable-handle').trigger('resize');
  20211. $.each(hasMoreDirs, function(h, node) {
  20212. node.trigger('update.'+fm.namespace, { change: 'done' });
  20213. });
  20214. })
  20215. // lock/unlock dirs while moving
  20216. .bind('lockfiles unlockfiles', function(e) {
  20217. var lock = e.type == 'lockfiles',
  20218. helperLocked = e.data.helper? e.data.helper.data('locked') : false,
  20219. act = (lock && !helperLocked) ? 'disable' : 'enable',
  20220. dirs = $.grep(e.data.files||[], function(h) {
  20221. var dir = fm.file(h);
  20222. return dir && dir.mime == 'directory' ? true : false;
  20223. });
  20224. $.each(dirs, function(i, hash) {
  20225. var dir = fm.navHash2Elm(hash);
  20226. if (dir.length && !helperLocked) {
  20227. dir.hasClass(draggable) && dir.draggable(act);
  20228. dir.hasClass(droppable) && dir.droppable(act);
  20229. dir[lock ? 'addClass' : 'removeClass'](disabled);
  20230. }
  20231. });
  20232. })
  20233. .bind('sortchange', function() {
  20234. if (fm.sortAlsoTreeview || prevSortTreeview !== fm.sortAlsoTreeview) {
  20235. var dirs,
  20236. ends = [],
  20237. endsMap = {},
  20238. endsVid = {},
  20239. topVid = '',
  20240. single = false,
  20241. current;
  20242. fm.lazy(function() {
  20243. dirs = filter(fm.files());
  20244. prevSortTreeview = fm.sortAlsoTreeview;
  20245. tree.empty();
  20246. // append volume roots at first
  20247. updateTree($.map(fm.roots, function(h) {
  20248. var dir = fm.file(h);
  20249. return dir && !dir.phash? dir : null;
  20250. }));
  20251. if (!Object.keys(hasMoreDirs).length) {
  20252. updateTree(dirs);
  20253. current = selectPages();
  20254. updateArrows(dirs, loaded);
  20255. } else {
  20256. ends = getEnds();
  20257. if (ends.length > 1) {
  20258. $.each(ends, function(i, end) {
  20259. var vid = fm.file(fm.root(end)).volumeid;
  20260. if (i === 0) {
  20261. topVid = vid;
  20262. }
  20263. endsVid[vid] = end;
  20264. endsMap[end] = [];
  20265. });
  20266. $.each(dirs, function(i, d) {
  20267. if (!d.volumeid) {
  20268. single = true;
  20269. return false;
  20270. }
  20271. endsMap[endsVid[d.volumeid] || endsVid[topVid]].push(d);
  20272. });
  20273. } else {
  20274. single = true;
  20275. }
  20276. if (single) {
  20277. $.each(ends, function(i, endHash) {
  20278. updateTree(dirs);
  20279. current = selectPages(fm.file(endHash));
  20280. updateArrows(dirs, loaded);
  20281. });
  20282. } else {
  20283. $.each(endsMap, function(endHash, dirs) {
  20284. updateTree(dirs);
  20285. current = selectPages(fm.file(endHash));
  20286. updateArrows(dirs, loaded);
  20287. });
  20288. }
  20289. }
  20290. sync();
  20291. }, 100);
  20292. }
  20293. });
  20294. });
  20295. return this;
  20296. };
  20297. /*
  20298. * File: /js/ui/uploadButton.js
  20299. */
  20300. /**
  20301. * @class elFinder toolbar's button tor upload file
  20302. *
  20303. * @author Dmitry (dio) Levashov
  20304. **/
  20305. $.fn.elfinderuploadbutton = function(cmd) {
  20306. return this.each(function() {
  20307. var fm = cmd.fm,
  20308. button = $(this).elfinderbutton(cmd)
  20309. .off('click'),
  20310. form = $('<form/>').appendTo(button),
  20311. input = $('<input type="file" multiple="true" title="'+cmd.fm.i18n('selectForUpload')+'"/>')
  20312. .on('change', function() {
  20313. var _input = $(this);
  20314. if (_input.val()) {
  20315. fm.exec('upload', {input : _input.remove()[0]}, void(0), fm.cwd().hash);
  20316. input.clone(true).appendTo(form);
  20317. }
  20318. })
  20319. .on('dragover', function(e) {
  20320. e.originalEvent.dataTransfer.dropEffect = 'copy';
  20321. }),
  20322. tm;
  20323. form.append(input.clone(true));
  20324. cmd.change(function() {
  20325. tm && cancelAnimationFrame(tm);
  20326. tm = requestAnimationFrame(function() {
  20327. var toShow = cmd.disabled();
  20328. if (form.is('visible')) {
  20329. !toShow && form.hide();
  20330. } else {
  20331. toShow && form.show();
  20332. }
  20333. });
  20334. })
  20335. .change();
  20336. });
  20337. };
  20338. /*
  20339. * File: /js/ui/viewbutton.js
  20340. */
  20341. /**
  20342. * @class elFinder toolbar button to switch current directory view.
  20343. *
  20344. * @author Dmitry (dio) Levashov
  20345. **/
  20346. $.fn.elfinderviewbutton = function(cmd) {
  20347. return this.each(function() {
  20348. var button = $(this).elfinderbutton(cmd),
  20349. icon = button.children('.elfinder-button-icon'),
  20350. text = button.children('.elfinder-button-text'),
  20351. tm;
  20352. cmd.change(function() {
  20353. tm && cancelAnimationFrame(tm);
  20354. tm = requestAnimationFrame(function() {
  20355. var icons = cmd.value == 'icons';
  20356. icon.toggleClass('elfinder-button-icon-view-list', icons);
  20357. cmd.className = icons? 'view-list' : '';
  20358. cmd.title = cmd.fm.i18n(icons ? 'viewlist' : 'viewicons');
  20359. button.attr('title', cmd.title);
  20360. text.html(cmd.title);
  20361. });
  20362. });
  20363. });
  20364. };
  20365. /*
  20366. * File: /js/ui/workzone.js
  20367. */
  20368. /**
  20369. * @class elfinderworkzone - elFinder container for nav and current directory
  20370. * @author Dmitry (dio) Levashov
  20371. **/
  20372. $.fn.elfinderworkzone = function(fm) {
  20373. var cl = 'elfinder-workzone';
  20374. this.not('.'+cl).each(function() {
  20375. var wz = $(this).addClass(cl),
  20376. prevH = Math.round(wz.height()),
  20377. parent = wz.parent(),
  20378. setDelta = function() {
  20379. wdelta = wz.outerHeight(true) - wz.height();
  20380. },
  20381. fitsize = function(e) {
  20382. var height = parent.height() - wdelta,
  20383. style = parent.attr('style'),
  20384. curH = Math.round(wz.height());
  20385. if (e) {
  20386. e.preventDefault();
  20387. e.stopPropagation();
  20388. }
  20389. parent.css('overflow', 'hidden')
  20390. .children(':visible:not(.'+cl+')').each(function() {
  20391. var ch = $(this);
  20392. if (ch.css('position') != 'absolute' && ch.css('position') != 'fixed') {
  20393. height -= ch.outerHeight(true);
  20394. }
  20395. });
  20396. parent.attr('style', style || '');
  20397. height = Math.max(0, Math.round(height));
  20398. if (prevH !== height || curH !== height) {
  20399. prevH = Math.round(wz.height());
  20400. wz.height(height);
  20401. fm.trigger('wzresize');
  20402. }
  20403. },
  20404. cssloaded = function() {
  20405. wdelta = wz.outerHeight(true) - wz.height();
  20406. fitsize();
  20407. },
  20408. wdelta;
  20409. setDelta();
  20410. parent.on('resize.' + fm.namespace, fitsize);
  20411. fm.one('cssloaded', cssloaded)
  20412. .bind('uiresize', fitsize)
  20413. .bind('themechange', setDelta);
  20414. });
  20415. return this;
  20416. };
  20417. /*
  20418. * File: /js/commands/archive.js
  20419. */
  20420. /**
  20421. * @class elFinder command "archive"
  20422. * Archive selected files
  20423. *
  20424. * @author Dmitry (dio) Levashov
  20425. **/
  20426. elFinder.prototype.commands.archive = function() {
  20427. var self = this,
  20428. fm = self.fm,
  20429. mimes = [],
  20430. dfrd;
  20431. this.variants = [];
  20432. this.disableOnSearch = false;
  20433. this.nextAction = {};
  20434. /**
  20435. * Update mimes on open/reload
  20436. *
  20437. * @return void
  20438. **/
  20439. fm.bind('open reload', function() {
  20440. self.variants = [];
  20441. $.each((mimes = fm.option('archivers')['create'] || []), function(i, mime) {
  20442. self.variants.push([mime, fm.mime2kind(mime)]);
  20443. });
  20444. self.change();
  20445. });
  20446. this.getstate = function(select) {
  20447. var sel = this.files(select),
  20448. cnt = sel.length,
  20449. chk = (cnt && ! fm.isRoot(sel[0]) && (fm.file(sel[0].phash) || {}).write && ! $.grep(sel, function(f){ return f.read ? false : true; }).length),
  20450. cwdId;
  20451. if (chk && fm.searchStatus.state > 1) {
  20452. cwdId = fm.cwd().volumeid;
  20453. chk = (cnt === $.grep(sel, function(f) { return f.read && f.hash.indexOf(cwdId) === 0 ? true : false; }).length);
  20454. }
  20455. return chk && !this._disabled && mimes.length && (cnt || (dfrd && dfrd.state() == 'pending')) ? 0 : -1;
  20456. };
  20457. this.exec = function(hashes, type) {
  20458. var files = this.files(hashes),
  20459. cnt = files.length,
  20460. mime = type || mimes[0],
  20461. cwd = fm.file(files[0].phash) || null,
  20462. error = ['errArchive', 'errPerm', 'errCreatingTempDir', 'errFtpDownloadFile', 'errFtpUploadFile', 'errFtpMkdir', 'errArchiveExec', 'errExtractExec', 'errRm'],
  20463. i, open;
  20464. dfrd = $.Deferred().fail(function(error) {
  20465. error && fm.error(error);
  20466. });
  20467. if (! (cnt && mimes.length && $.inArray(mime, mimes) !== -1)) {
  20468. return dfrd.reject();
  20469. }
  20470. if (!cwd.write) {
  20471. return dfrd.reject(error);
  20472. }
  20473. for (i = 0; i < cnt; i++) {
  20474. if (!files[i].read) {
  20475. return dfrd.reject(error);
  20476. }
  20477. }
  20478. self.mime = mime;
  20479. self.prefix = ((cnt > 1)? 'Archive' : files[0].name) + (fm.option('archivers')['createext']? '.' + fm.option('archivers')['createext'][mime] : '');
  20480. self.data = {targets : self.hashes(hashes), type : mime};
  20481. if (fm.cwd().hash !== cwd.hash) {
  20482. open = fm.exec('open', cwd.hash).done(function() {
  20483. fm.one('cwdrender', function() {
  20484. fm.selectfiles({files : hashes});
  20485. dfrd = $.proxy(fm.res('mixin', 'make'), self)();
  20486. });
  20487. });
  20488. } else {
  20489. fm.selectfiles({files : hashes});
  20490. dfrd = $.proxy(fm.res('mixin', 'make'), self)();
  20491. }
  20492. return dfrd;
  20493. };
  20494. };
  20495. /*
  20496. * File: /js/commands/back.js
  20497. */
  20498. /**
  20499. * @class elFinder command "back"
  20500. * Open last visited folder
  20501. *
  20502. * @author Dmitry (dio) Levashov
  20503. **/
  20504. (elFinder.prototype.commands.back = function() {
  20505. this.alwaysEnabled = true;
  20506. this.updateOnSelect = false;
  20507. this.shortcuts = [{
  20508. pattern : 'ctrl+left backspace'
  20509. }];
  20510. this.getstate = function() {
  20511. return this.fm.history.canBack() ? 0 : -1;
  20512. };
  20513. this.exec = function() {
  20514. return this.fm.history.back();
  20515. };
  20516. }).prototype = { forceLoad : true }; // this is required command
  20517. /*
  20518. * File: /js/commands/chmod.js
  20519. */
  20520. /**
  20521. * @class elFinder command "chmod".
  20522. * Chmod files.
  20523. *
  20524. * @type elFinder.command
  20525. * @author Naoki Sawada
  20526. */
  20527. elFinder.prototype.commands.chmod = function() {
  20528. this.updateOnSelect = false;
  20529. var fm = this.fm,
  20530. level = {
  20531. 0 : 'owner',
  20532. 1 : 'group',
  20533. 2 : 'other'
  20534. },
  20535. msg = {
  20536. read : fm.i18n('read'),
  20537. write : fm.i18n('write'),
  20538. execute : fm.i18n('execute'),
  20539. perm : fm.i18n('perm'),
  20540. kind : fm.i18n('kind'),
  20541. files : fm.i18n('files')
  20542. },
  20543. isPerm = function(perm){
  20544. return (!isNaN(parseInt(perm, 8) && parseInt(perm, 8) <= 511) || perm.match(/^([r-][w-][x-]){3}$/i));
  20545. };
  20546. this.tpl = {
  20547. main : '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}</div>'
  20548. +'{dataTable}',
  20549. itemTitle : '<strong>{name}</strong><span id="elfinder-info-kind">{kind}</span>',
  20550. groupTitle : '<strong>{items}: {num}</strong>',
  20551. dataTable : '<table id="{id}-table-perm"><tr><td>{0}</td><td>{1}</td><td>{2}</td></tr></table>'
  20552. +'<div class="">'+msg.perm+': <input class="elfinder-tabstop elfinder-focus" id="{id}-perm" type="text" size="4" maxlength="3" value="{value}"></div>',
  20553. fieldset : '<fieldset id="{id}-fieldset-{level}"><legend>{f_title}{name}</legend>'
  20554. +'<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>'
  20555. +'<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>'
  20556. +'<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>'
  20557. };
  20558. this.shortcuts = [{
  20559. //pattern : 'ctrl+p'
  20560. }];
  20561. this.getstate = function(sel) {
  20562. var fm = this.fm;
  20563. sel = sel || fm.selected();
  20564. if (sel.length == 0) {
  20565. sel = [ fm.cwd().hash ];
  20566. }
  20567. return this.checkstate(this.files(sel)) ? 0 : -1;
  20568. };
  20569. this.checkstate = function(sel) {
  20570. var cnt = sel.length;
  20571. if (!cnt) return false;
  20572. var chk = $.grep(sel, function(f) {
  20573. return (f.isowner && f.perm && isPerm(f.perm) && (cnt == 1 || f.mime != 'directory')) ? true : false;
  20574. }).length;
  20575. return (cnt == chk)? true : false;
  20576. };
  20577. this.exec = function(select) {
  20578. var hashes = this.hashes(select),
  20579. files = this.files(hashes);
  20580. if (! files.length) {
  20581. hashes = [ this.fm.cwd().hash ];
  20582. files = this.files(hashes);
  20583. }
  20584. var fm = this.fm,
  20585. dfrd = $.Deferred().always(function() {
  20586. fm.enable();
  20587. }),
  20588. tpl = this.tpl,
  20589. cnt = files.length,
  20590. file = files[0],
  20591. id = fm.namespace + '-perm-' + file.hash,
  20592. view = tpl.main,
  20593. checked = ' checked="checked"',
  20594. buttons = function() {
  20595. var buttons = {};
  20596. buttons[fm.i18n('btnApply')] = save;
  20597. buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); };
  20598. return buttons;
  20599. },
  20600. save = function() {
  20601. var perm = $.trim($('#'+id+'-perm').val()),
  20602. reqData;
  20603. if (!isPerm(perm)) return false;
  20604. dialog.elfinderdialog('close');
  20605. reqData = {
  20606. cmd : 'chmod',
  20607. targets : hashes,
  20608. mode : perm
  20609. };
  20610. fm.request({
  20611. data : reqData,
  20612. notify : {type : 'chmod', cnt : cnt}
  20613. })
  20614. .fail(function(error) {
  20615. dfrd.reject(error);
  20616. })
  20617. .done(function(data) {
  20618. if (data.changed && data.changed.length) {
  20619. data.undo = {
  20620. cmd : 'chmod',
  20621. callback : function() {
  20622. var reqs = [];
  20623. $.each(prevVals, function(perm, hashes) {
  20624. reqs.push(fm.request({
  20625. data : {cmd : 'chmod', targets : hashes, mode : perm},
  20626. notify : {type : 'undo', cnt : hashes.length}
  20627. }));
  20628. });
  20629. return $.when.apply(null, reqs);
  20630. }
  20631. };
  20632. data.redo = {
  20633. cmd : 'chmod',
  20634. callback : function() {
  20635. return fm.request({
  20636. data : reqData,
  20637. notify : {type : 'redo', cnt : hashes.length}
  20638. });
  20639. }
  20640. };
  20641. }
  20642. dfrd.resolve(data);
  20643. });
  20644. },
  20645. setperm = function() {
  20646. var perm = '';
  20647. var _perm;
  20648. for (var i = 0; i < 3; i++){
  20649. _perm = 0;
  20650. if ($("#"+id+"-read-"+level[i]+'-perm').is(':checked')) {
  20651. _perm = (_perm | 4);
  20652. }
  20653. if ($("#"+id+"-write-"+level[i]+'-perm').is(':checked')) {
  20654. _perm = (_perm | 2);
  20655. }
  20656. if ($("#"+id+"-execute-"+level[i]+'-perm').is(':checked')) {
  20657. _perm = (_perm | 1);
  20658. }
  20659. perm += _perm.toString(8);
  20660. }
  20661. $('#'+id+'-perm').val(perm);
  20662. },
  20663. setcheck = function(perm) {
  20664. var _perm;
  20665. for (var i = 0; i < 3; i++){
  20666. _perm = parseInt(perm.slice(i, i+1), 8);
  20667. $("#"+id+"-read-"+level[i]+'-perm').prop("checked", false);
  20668. $("#"+id+"-write-"+level[i]+'-perm').prop("checked", false);
  20669. $("#"+id+"-execute-"+level[i]+'-perm').prop("checked", false);
  20670. if ((_perm & 4) == 4) {
  20671. $("#"+id+"-read-"+level[i]+'-perm').prop("checked", true);
  20672. }
  20673. if ((_perm & 2) == 2) {
  20674. $("#"+id+"-write-"+level[i]+'-perm').prop("checked", true);
  20675. }
  20676. if ((_perm & 1) == 1) {
  20677. $("#"+id+"-execute-"+level[i]+'-perm').prop("checked", true);
  20678. }
  20679. }
  20680. setperm();
  20681. },
  20682. makeperm = function(files) {
  20683. var perm = '777', ret = '', chk, _chk, _perm;
  20684. var len = files.length;
  20685. for (var i2 = 0; i2 < len; i2++) {
  20686. chk = getPerm(files[i2].perm);
  20687. if (! prevVals[chk]) {
  20688. prevVals[chk] = [];
  20689. }
  20690. prevVals[chk].push(files[i2].hash);
  20691. ret = '';
  20692. for (var i = 0; i < 3; i++){
  20693. _chk = parseInt(chk.slice(i, i+1), 8);
  20694. _perm = parseInt(perm.slice(i, i+1), 8);
  20695. if ((_chk & 4) != 4 && (_perm & 4) == 4) {
  20696. _perm -= 4;
  20697. }
  20698. if ((_chk & 2) != 2 && (_perm & 2) == 2) {
  20699. _perm -= 2;
  20700. }
  20701. if ((_chk & 1) != 1 && (_perm & 1) == 1) {
  20702. _perm -= 1;
  20703. }
  20704. ret += _perm.toString(8);
  20705. }
  20706. perm = ret;
  20707. }
  20708. return perm;
  20709. },
  20710. makeName = function(name) {
  20711. return name? ':'+name : '';
  20712. },
  20713. makeDataTable = function(perm, f) {
  20714. var _perm, fieldset;
  20715. var value = '';
  20716. var dataTable = tpl.dataTable;
  20717. for (var i = 0; i < 3; i++){
  20718. _perm = parseInt(perm.slice(i, i+1), 8);
  20719. value += _perm.toString(8);
  20720. fieldset = tpl.fieldset.replace('{f_title}', fm.i18n(level[i])).replace('{name}', makeName(f[level[i]])).replace(/\{level\}/g, level[i]);
  20721. dataTable = dataTable.replace('{'+i+'}', fieldset)
  20722. .replace('{checked-r}', ((_perm & 4) == 4)? checked : '')
  20723. .replace('{checked-w}', ((_perm & 2) == 2)? checked : '')
  20724. .replace('{checked-x}', ((_perm & 1) == 1)? checked : '');
  20725. }
  20726. dataTable = dataTable.replace('{value}', value).replace('{valueCaption}', msg['perm']);
  20727. return dataTable;
  20728. },
  20729. getPerm = function(perm){
  20730. if (isNaN(parseInt(perm, 8))) {
  20731. var mode_array = perm.split('');
  20732. var a = [];
  20733. for (var i = 0, l = mode_array.length; i < l; i++) {
  20734. if (i === 0 || i === 3 || i === 6) {
  20735. if (mode_array[i].match(/[r]/i)) {
  20736. a.push(1);
  20737. } else if (mode_array[i].match(/[-]/)) {
  20738. a.push(0);
  20739. }
  20740. } else if ( i === 1 || i === 4 || i === 7) {
  20741. if (mode_array[i].match(/[w]/i)) {
  20742. a.push(1);
  20743. } else if (mode_array[i].match(/[-]/)) {
  20744. a.push(0);
  20745. }
  20746. } else {
  20747. if (mode_array[i].match(/[x]/i)) {
  20748. a.push(1);
  20749. } else if (mode_array[i].match(/[-]/)) {
  20750. a.push(0);
  20751. }
  20752. }
  20753. }
  20754. a.splice(3, 0, ",");
  20755. a.splice(7, 0, ",");
  20756. var b = a.join("");
  20757. var b_array = b.split(",");
  20758. var c = [];
  20759. for (var j = 0, m = b_array.length; j < m; j++) {
  20760. var p = parseInt(b_array[j], 2).toString(8);
  20761. c.push(p);
  20762. }
  20763. perm = c.join('');
  20764. } else {
  20765. perm = parseInt(perm, 8).toString(8);
  20766. }
  20767. return perm;
  20768. },
  20769. opts = {
  20770. title : this.title,
  20771. width : 'auto',
  20772. buttons : buttons(),
  20773. close : function() { $(this).elfinderdialog('destroy'); }
  20774. },
  20775. dialog = fm.getUI().find('#'+id),
  20776. prevVals = {},
  20777. tmb = '', title, dataTable;
  20778. if (dialog.length) {
  20779. dialog.elfinderdialog('toTop');
  20780. return $.Deferred().resolve();
  20781. }
  20782. view = view.replace('{class}', cnt > 1 ? 'elfinder-cwd-icon-group' : fm.mime2class(file.mime));
  20783. if (cnt > 1) {
  20784. title = tpl.groupTitle.replace('{items}', fm.i18n('items')).replace('{num}', cnt);
  20785. } else {
  20786. title = tpl.itemTitle.replace('{name}', file.name).replace('{kind}', fm.mime2kind(file));
  20787. tmb = fm.tmb(file);
  20788. }
  20789. dataTable = makeDataTable(makeperm(files), files.length == 1? files[0] : {});
  20790. view = view.replace('{title}', title).replace('{dataTable}', dataTable).replace(/{id}/g, id);
  20791. dialog = this.fmDialog(view, opts);
  20792. dialog.attr('id', id);
  20793. // load thumbnail
  20794. if (tmb) {
  20795. $('<img/>')
  20796. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  20797. .attr('src', tmb.url);
  20798. }
  20799. $('#' + id + '-table-perm :checkbox').on('click', function(){setperm('perm');});
  20800. $('#' + id + '-perm').on('keydown', function(e) {
  20801. var c = e.keyCode;
  20802. if (c == $.ui.keyCode.ENTER) {
  20803. e.stopPropagation();
  20804. save();
  20805. return;
  20806. }
  20807. }).on('focus', function(e){
  20808. $(this).trigger('select');
  20809. }).on('keyup', function(e) {
  20810. if ($(this).val().length == 3) {
  20811. $(this).trigger('select');
  20812. setcheck($(this).val());
  20813. }
  20814. });
  20815. return dfrd;
  20816. };
  20817. };
  20818. /*
  20819. * File: /js/commands/colwidth.js
  20820. */
  20821. /**
  20822. * @class elFinder command "colwidth"
  20823. * CWD list table columns width to auto
  20824. *
  20825. * @author Naoki Sawada
  20826. **/
  20827. elFinder.prototype.commands.colwidth = function() {
  20828. this.alwaysEnabled = true;
  20829. this.updateOnSelect = false;
  20830. this.getstate = function() {
  20831. return this.fm.getUI('cwd').find('table').css('table-layout') === 'fixed' ? 0 : -1;
  20832. };
  20833. this.exec = function() {
  20834. this.fm.getUI('cwd').trigger('colwidth');
  20835. return $.Deferred().resolve();
  20836. };
  20837. };
  20838. /*
  20839. * File: /js/commands/copy.js
  20840. */
  20841. /**
  20842. * @class elFinder command "copy".
  20843. * Put files in filemanager clipboard.
  20844. *
  20845. * @type elFinder.command
  20846. * @author Dmitry (dio) Levashov
  20847. */
  20848. elFinder.prototype.commands.copy = function() {
  20849. this.shortcuts = [{
  20850. pattern : 'ctrl+c ctrl+insert'
  20851. }];
  20852. this.getstate = function(select) {
  20853. var sel = this.files(select),
  20854. cnt = sel.length;
  20855. return cnt && $.grep(sel, function(f) { return f.read ? true : false; }).length == cnt ? 0 : -1;
  20856. };
  20857. this.exec = function(hashes) {
  20858. var fm = this.fm,
  20859. dfrd = $.Deferred()
  20860. .fail(function(error) {
  20861. fm.error(error);
  20862. });
  20863. $.each(this.files(hashes), function(i, file) {
  20864. if (! file.read) {
  20865. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  20866. }
  20867. });
  20868. return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes)));
  20869. };
  20870. };
  20871. /*
  20872. * File: /js/commands/cut.js
  20873. */
  20874. /**
  20875. * @class elFinder command "copy".
  20876. * Put files in filemanager clipboard.
  20877. *
  20878. * @type elFinder.command
  20879. * @author Dmitry (dio) Levashov
  20880. */
  20881. elFinder.prototype.commands.cut = function() {
  20882. var fm = this.fm;
  20883. this.shortcuts = [{
  20884. pattern : 'ctrl+x shift+insert'
  20885. }];
  20886. this.getstate = function(select) {
  20887. var sel = this.files(select),
  20888. cnt = sel.length;
  20889. return cnt && $.grep(sel, function(f) { return f.read && ! f.locked && ! fm.isRoot(f) ? true : false; }).length == cnt ? 0 : -1;
  20890. };
  20891. this.exec = function(hashes) {
  20892. var dfrd = $.Deferred()
  20893. .fail(function(error) {
  20894. fm.error(error);
  20895. });
  20896. $.each(this.files(hashes), function(i, file) {
  20897. if (!(file.read && ! file.locked && ! fm.isRoot(file)) ) {
  20898. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  20899. }
  20900. if (file.locked) {
  20901. return !dfrd.reject(['errLocked', file.name]);
  20902. }
  20903. });
  20904. return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes), true));
  20905. };
  20906. };
  20907. /*
  20908. * File: /js/commands/download.js
  20909. */
  20910. /**
  20911. * @class elFinder command "download".
  20912. * Download selected files.
  20913. * Only for new api
  20914. *
  20915. * @author Dmitry (dio) Levashov, dio@std42.ru
  20916. **/
  20917. elFinder.prototype.commands.zipdl = function() {};
  20918. elFinder.prototype.commands.download = function() {
  20919. var self = this,
  20920. fm = this.fm,
  20921. czipdl = null,
  20922. zipOn = false,
  20923. mixed = false,
  20924. dlntf = false,
  20925. cpath = window.location.pathname || '/',
  20926. filter = function(hashes, inExec) {
  20927. var volumeid, mixedCmd;
  20928. if (czipdl !== null) {
  20929. if (fm.searchStatus.state > 1) {
  20930. mixed = fm.searchStatus.mixed;
  20931. } else if (fm.leafRoots[fm.cwd().hash]) {
  20932. volumeid = fm.cwd().volumeid;
  20933. $.each(hashes, function(i, h) {
  20934. if (h.indexOf(volumeid) !== 0) {
  20935. mixed = true;
  20936. return false;
  20937. }
  20938. });
  20939. }
  20940. zipOn = (fm.isCommandEnabled('zipdl', hashes[0]));
  20941. }
  20942. if (mixed) {
  20943. mixedCmd = czipdl? 'zipdl' : 'download';
  20944. hashes = $.grep(hashes, function(h) {
  20945. var f = fm.file(h),
  20946. res = (! f || (! czipdl && f.mime === 'directory') || ! fm.isCommandEnabled(mixedCmd, h))? false : true;
  20947. if (f && inExec && ! res) {
  20948. fm.cwdHash2Elm(f.hash).trigger('unselect');
  20949. }
  20950. return res;
  20951. });
  20952. if (! hashes.length) {
  20953. return [];
  20954. }
  20955. } else {
  20956. if (!fm.isCommandEnabled('download', hashes[0])) {
  20957. return [];
  20958. }
  20959. }
  20960. return $.grep(self.files(hashes), function(f) {
  20961. var res = (! f.read || (! zipOn && f.mime == 'directory')) ? false : true;
  20962. if (inExec && ! res) {
  20963. fm.cwdHash2Elm(f.hash).trigger('unselect');
  20964. }
  20965. return res;
  20966. });
  20967. };
  20968. this.linkedCmds = ['zipdl'];
  20969. this.shortcuts = [{
  20970. pattern : 'shift+enter'
  20971. }];
  20972. this.getstate = function(select) {
  20973. var sel = this.hashes(select),
  20974. cnt = sel.length,
  20975. maxReq = this.options.maxRequests || 10,
  20976. mixed = false,
  20977. croot = '';
  20978. if (cnt < 1) {
  20979. return -1;
  20980. }
  20981. cnt = filter(sel).length;
  20982. return (cnt && (zipOn || (cnt <= maxReq && ((!fm.UA.IE && !fm.UA.Mobile) || cnt == 1))) ? 0 : -1);
  20983. };
  20984. fm.bind('contextmenu', function(e){
  20985. var fm = self.fm,
  20986. helper = null,
  20987. targets, file, link,
  20988. getExtra = function(file) {
  20989. var link = file.url || fm.url(file.hash);
  20990. return {
  20991. icon: 'link',
  20992. node: $('<a/>')
  20993. .attr({href: link, target: '_blank', title: fm.i18n('link')})
  20994. .text(file.name)
  20995. .on('mousedown click touchstart touchmove touchend contextmenu', function(e){
  20996. e.stopPropagation();
  20997. })
  20998. .on('dragstart', function(e) {
  20999. var dt = e.dataTransfer || e.originalEvent.dataTransfer || null;
  21000. helper = null;
  21001. if (dt) {
  21002. var icon = function(f) {
  21003. var mime = f.mime, i, tmb = fm.tmb(f);
  21004. i = '<div class="elfinder-cwd-icon '+fm.mime2class(mime)+' ui-corner-all"/>';
  21005. if (tmb) {
  21006. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  21007. }
  21008. return i;
  21009. };
  21010. dt.effectAllowed = 'copyLink';
  21011. if (dt.setDragImage) {
  21012. helper = $('<div class="elfinder-drag-helper html5-native">').append(icon(file)).appendTo($(document.body));
  21013. dt.setDragImage(helper.get(0), 50, 47);
  21014. }
  21015. if (!fm.UA.IE) {
  21016. dt.setData('elfinderfrom', window.location.href + file.phash);
  21017. dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), '');
  21018. }
  21019. }
  21020. })
  21021. .on('dragend', function(e) {
  21022. helper && helper.remove();
  21023. })
  21024. };
  21025. };
  21026. self.extra = null;
  21027. if (e.data) {
  21028. targets = e.data.targets || [];
  21029. if (targets.length === 1 && (file = fm.file(targets[0])) && file.mime !== 'directory') {
  21030. if (file.url != '1') {
  21031. self.extra = getExtra(file);
  21032. } else {
  21033. // Get URL ondemand
  21034. var node;
  21035. self.extra = {
  21036. icon: 'link',
  21037. node: $('<a/>')
  21038. .attr({href: '#', title: fm.i18n('getLink'), draggable: 'false'})
  21039. .text(file.name)
  21040. .on('click touchstart', function(e){
  21041. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  21042. return;
  21043. }
  21044. var parent = node.parent();
  21045. e.stopPropagation();
  21046. e.preventDefault();
  21047. parent.removeClass('ui-state-disabled').addClass('elfinder-button-icon-spinner');
  21048. fm.request({
  21049. data : {cmd : 'url', target : file.hash},
  21050. preventDefault : true
  21051. })
  21052. .always(function(data) {
  21053. parent.removeClass('elfinder-button-icon-spinner');
  21054. if (data.url) {
  21055. var rfile = fm.file(file.hash);
  21056. rfile.url = data.url;
  21057. node.replaceWith(getExtra(file).node);
  21058. } else {
  21059. parent.addClass('ui-state-disabled');
  21060. }
  21061. });
  21062. })
  21063. };
  21064. node = self.extra.node;
  21065. node.ready(function(){
  21066. requestAnimationFrame(function(){
  21067. node.parent().addClass('ui-state-disabled').css('pointer-events', 'auto');
  21068. });
  21069. });
  21070. }
  21071. }
  21072. }
  21073. }).one('open', function() {
  21074. if (fm.api >= 2.1012) {
  21075. czipdl = fm.getCommand('zipdl');
  21076. }
  21077. dlntf = fm.api > 2.1038 && !fm.isCORS;
  21078. });
  21079. this.exec = function(select) {
  21080. var hashes = this.hashes(select),
  21081. fm = this.fm,
  21082. base = fm.options.url,
  21083. files = filter(hashes, true),
  21084. dfrd = $.Deferred(),
  21085. iframes = '',
  21086. cdata = '',
  21087. targets = {},
  21088. i, url,
  21089. linkdl = false,
  21090. getTask = function(hashes) {
  21091. return function() {
  21092. var dfd = $.Deferred(),
  21093. root = fm.file(fm.root(hashes[0])),
  21094. single = (hashes.length === 1),
  21095. volName = root? (root.i18 || root.name) : null,
  21096. dir, dlName, phash;
  21097. if (single) {
  21098. if (dir = fm.file(hashes[0])) {
  21099. dlName = (dir.i18 || dir.name);
  21100. }
  21101. } else {
  21102. $.each(hashes, function() {
  21103. var d = fm.file(this);
  21104. if (d && (!phash || phash === d.phash)) {
  21105. phash = d.phash;
  21106. } else {
  21107. phash = null;
  21108. return false;
  21109. }
  21110. });
  21111. if (phash && (dir = fm.file(phash))) {
  21112. dlName = (dir.i18 || dir.name) + '-' + hashes.length;
  21113. }
  21114. }
  21115. if (dlName) {
  21116. volName = dlName;
  21117. }
  21118. volName && (volName = ' (' + volName + ')');
  21119. fm.request({
  21120. data : {cmd : 'zipdl', targets : hashes},
  21121. notify : {type : 'zipdl', cnt : 1, hideCnt : true, msg : fm.i18n('ntfzipdl') + volName},
  21122. cancel : true,
  21123. eachCancel : true,
  21124. preventDefault : true
  21125. }).done(function(e) {
  21126. var zipdl, dialog, btn = {}, dllink, form, iframe, m,
  21127. uniq = 'dlw' + (+new Date());
  21128. if (e.error) {
  21129. fm.error(e.error);
  21130. dfd.resolve();
  21131. } else if (e.zipdl) {
  21132. zipdl = e.zipdl;
  21133. if (dlName) {
  21134. m = fm.splitFileExtention(zipdl.name || '');
  21135. dlName += m[1]? ('.' + m[1]) : '.zip';
  21136. } else {
  21137. dlName = zipdl.name;
  21138. }
  21139. if ((html5dl && (!fm.UA.Safari || fm.isSameOrigin(fm.options.url))) || linkdl) {
  21140. url = fm.options.url + (fm.options.url.indexOf('?') === -1 ? '?' : '&')
  21141. + 'cmd=zipdl&download=1';
  21142. $.each([hashes[0], zipdl.file, dlName, zipdl.mime], function(key, val) {
  21143. url += '&targets%5B%5D='+encodeURIComponent(val);
  21144. });
  21145. $.each(fm.customData, function(key, val) {
  21146. url += '&'+encodeURIComponent(key)+'='+encodeURIComponent(val);
  21147. });
  21148. url += '&'+encodeURIComponent(dlName);
  21149. dllink = $('<a/>')
  21150. .attr('href', url)
  21151. .attr('download', fm.escape(dlName))
  21152. .on('click', function() {
  21153. dfd.resolve();
  21154. dialog && dialog.elfinderdialog('destroy');
  21155. });
  21156. if (linkdl) {
  21157. dllink.attr('target', '_blank')
  21158. .append('<span class="elfinder-button-icon elfinder-button-icon-download"></span>'+fm.escape(dlName));
  21159. btn[fm.i18n('btnCancel')] = function() {
  21160. dialog.elfinderdialog('destroy');
  21161. };
  21162. dialog = self.fmDialog(dllink, {
  21163. title: fm.i18n('link'),
  21164. buttons: btn,
  21165. width: '200px',
  21166. destroyOnClose: true,
  21167. close: function() {
  21168. (dfd.state() !== 'resolved') && dfd.resolve();
  21169. }
  21170. });
  21171. } else {
  21172. click(dllink.hide().appendTo('body').get(0));
  21173. dllink.remove();
  21174. }
  21175. } else {
  21176. form = $('<form action="'+fm.options.url+'" method="post" target="'+uniq+'" style="display:none"/>')
  21177. .append('<input type="hidden" name="cmd" value="zipdl"/>')
  21178. .append('<input type="hidden" name="download" value="1"/>');
  21179. $.each([hashes[0], zipdl.file, dlName, zipdl.mime], function(key, val) {
  21180. form.append('<input type="hidden" name="targets[]" value="'+fm.escape(val)+'"/>');
  21181. });
  21182. $.each(fm.customData, function(key, val) {
  21183. form.append('<input type="hidden" name="'+key+'" value="'+fm.escape(val)+'"/>');
  21184. });
  21185. form.attr('target', uniq).appendTo('body');
  21186. iframe = $('<iframe style="display:none" name="'+uniq+'">')
  21187. .appendTo('body')
  21188. .ready(function() {
  21189. form.submit().remove();
  21190. dfd.resolve();
  21191. setTimeout(function() {
  21192. iframe.remove();
  21193. }, 20000); // give 20 sec file to be saved
  21194. });
  21195. }
  21196. }
  21197. }).fail(function(error) {
  21198. error && fm.error(error);
  21199. dfd.resolve();
  21200. });
  21201. return dfd.promise();
  21202. };
  21203. },
  21204. // use MouseEvent to click element for Safari etc
  21205. click = function(a) {
  21206. var clickEv;
  21207. if (typeof MouseEvent === 'function') {
  21208. clickEv = new MouseEvent('click');
  21209. } else {
  21210. clickEv = document.createEvent('MouseEvents');
  21211. clickEv.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  21212. }
  21213. fm.pauseUnloadCheck(true);
  21214. a.dispatchEvent(clickEv);
  21215. },
  21216. checkCookie = function(id) {
  21217. var name = 'elfdl' + id,
  21218. parts;
  21219. parts = document.cookie.split(name + "=");
  21220. if (parts.length === 2) {
  21221. ntftm && clearTimeout(ntftm);
  21222. document.cookie = name + '=; path=' + cpath + '; max-age=0';
  21223. closeNotify();
  21224. } else {
  21225. setTimeout(function() { checkCookie(id); }, 200);
  21226. }
  21227. },
  21228. closeNotify = function() {
  21229. if (fm.ui.notify.children('.elfinder-notify-download').length) {
  21230. fm.notify({
  21231. type : 'download',
  21232. cnt : -1
  21233. });
  21234. }
  21235. },
  21236. reqids = [],
  21237. link, html5dl, fileCnt, clickEv, cid, ntftm, reqid;
  21238. if (!files.length) {
  21239. return dfrd.reject();
  21240. }
  21241. fileCnt = $.grep(files, function(f) { return f.mime === 'directory'? false : true; }).length;
  21242. link = $('<a>').hide().appendTo('body');
  21243. html5dl = (typeof link.get(0).download === 'string');
  21244. if (zipOn && (fileCnt !== files.length || fileCnt >= (this.options.minFilesZipdl || 1))) {
  21245. link.remove();
  21246. linkdl = (!html5dl && fm.UA.Mobile);
  21247. if (mixed) {
  21248. targets = {};
  21249. $.each(files, function(i, f) {
  21250. var p = f.hash.split('_', 2);
  21251. if (! targets[p[0]]) {
  21252. targets[p[0]] = [ f.hash ];
  21253. } else {
  21254. targets[p[0]].push(f.hash);
  21255. }
  21256. });
  21257. if (!linkdl && fm.UA.Mobile && Object.keys(targets).length > 1) {
  21258. linkdl = true;
  21259. }
  21260. } else {
  21261. targets = [ $.map(files, function(f) { return f.hash; }) ];
  21262. }
  21263. dfrd = fm.sequence($.map(targets, function(t) { return getTask(t); })).always(
  21264. function() {
  21265. fm.trigger('download', {files : files});
  21266. }
  21267. );
  21268. return dfrd;
  21269. } else {
  21270. reqids = [];
  21271. for (i = 0; i < files.length; i++) {
  21272. url = fm.openUrl(files[i].hash, true);
  21273. if (dlntf && url.substr(0, fm.options.url.length) === fm.options.url) {
  21274. reqid = fm.getRequestId();
  21275. reqids.push(reqid);
  21276. url += '&cpath=' + cpath + '&reqid=' + reqid;
  21277. ntftm = setTimeout(function() {
  21278. fm.notify({
  21279. type : 'download',
  21280. cnt : 1,
  21281. cancel : (fm.UA.IE || fm.UA.Edge)? void(0) : function() {
  21282. if (reqids.length) {
  21283. $.each(reqids, function() {
  21284. fm.request({
  21285. data: {
  21286. cmd: 'abort',
  21287. id: this
  21288. },
  21289. preventDefault: true
  21290. });
  21291. });
  21292. }
  21293. reqids = [];
  21294. }
  21295. });
  21296. }, fm.notifyDelay);
  21297. checkCookie(reqid);
  21298. }
  21299. if (html5dl && (!fm.UA.Safari || fm.isSameOrigin(url))) {
  21300. click(link.attr('href', url)
  21301. .attr('download', fm.escape(files[i].name))
  21302. .get(0)
  21303. );
  21304. } else {
  21305. if (fm.UA.Mobile) {
  21306. setTimeout(function(){
  21307. if (! window.open(url)) {
  21308. fm.error('errPopup');
  21309. ntftm && cleaerTimeout(ntftm);
  21310. closeNotify();
  21311. }
  21312. }, 100);
  21313. } else {
  21314. iframes += '<iframe class="downloader" id="downloader-' + files[i].hash+'" style="display:none" src="'+url+'"/>';
  21315. }
  21316. }
  21317. }
  21318. link.remove();
  21319. $(iframes)
  21320. .appendTo('body')
  21321. .ready(function() {
  21322. setTimeout(function() {
  21323. $(iframes).each(function() {
  21324. $('#' + $(this).attr('id')).remove();
  21325. });
  21326. }, 20000 + (10000 * i)); // give 20 sec + 10 sec for each file to be saved
  21327. });
  21328. fm.trigger('download', {files : files});
  21329. return dfrd.resolve();
  21330. }
  21331. };
  21332. };
  21333. /*
  21334. * File: /js/commands/duplicate.js
  21335. */
  21336. /**
  21337. * @class elFinder command "duplicate"
  21338. * Create file/folder copy with suffix "copy Number"
  21339. *
  21340. * @type elFinder.command
  21341. * @author Dmitry (dio) Levashov
  21342. */
  21343. elFinder.prototype.commands.duplicate = function() {
  21344. var fm = this.fm;
  21345. this.getstate = function(select) {
  21346. var sel = this.files(select),
  21347. cnt = sel.length;
  21348. 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;
  21349. };
  21350. this.exec = function(hashes) {
  21351. var fm = this.fm,
  21352. files = this.files(hashes),
  21353. cnt = files.length,
  21354. dfrd = $.Deferred()
  21355. .fail(function(error) {
  21356. error && fm.error(error);
  21357. }),
  21358. args = [];
  21359. if (! cnt) {
  21360. return dfrd.reject();
  21361. }
  21362. $.each(files, function(i, file) {
  21363. if (!file.read || !fm.file(file.phash).write) {
  21364. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  21365. }
  21366. });
  21367. if (dfrd.state() == 'rejected') {
  21368. return dfrd;
  21369. }
  21370. return fm.request({
  21371. data : {cmd : 'duplicate', targets : this.hashes(hashes)},
  21372. notify : {type : 'copy', cnt : cnt},
  21373. navigate : {
  21374. toast : {
  21375. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdduplicate')])}
  21376. }
  21377. }
  21378. });
  21379. };
  21380. };
  21381. /*
  21382. * File: /js/commands/edit.js
  21383. */
  21384. /**
  21385. * @class elFinder command "edit".
  21386. * Edit text file in dialog window
  21387. *
  21388. * @author Dmitry (dio) Levashov, dio@std42.ru
  21389. **/
  21390. elFinder.prototype.commands.edit = function() {
  21391. var self = this,
  21392. fm = this.fm,
  21393. clsEditing = fm.res('class', 'editing'),
  21394. mimesSingle = [],
  21395. mimes = [],
  21396. allowAll = false,
  21397. rtrim = function(str){
  21398. return str.replace(/\s+$/, '');
  21399. },
  21400. getEncSelect = function(heads) {
  21401. var sel = $('<select class="ui-corner-all"/>'),
  21402. hval;
  21403. if (heads) {
  21404. $.each(heads, function(i, head) {
  21405. hval = fm.escape(head.value);
  21406. sel.append('<option value="'+hval+'">'+(head.caption? fm.escape(head.caption) : hval)+'</option>');
  21407. });
  21408. }
  21409. $.each(self.options.encodings, function(i, v) {
  21410. sel.append('<option value="'+v+'">'+v+'</option>');
  21411. });
  21412. return sel;
  21413. },
  21414. getDlgWidth = function() {
  21415. var m, width;
  21416. if (typeof self.options.dialogWidth === 'string' && (m = self.options.dialogWidth.match(/(\d+)%/))) {
  21417. width = parseInt(fm.getUI().width() * (m[1] / 100));
  21418. } else {
  21419. width = parseInt(self.options.dialogWidth || 650);
  21420. }
  21421. return Math.min(width, $(window).width());
  21422. },
  21423. /**
  21424. * Return files acceptable to edit
  21425. *
  21426. * @param Array files hashes
  21427. * @return Array
  21428. **/
  21429. filter = function(files) {
  21430. var cnt = files.length,
  21431. mime, ext, skip;
  21432. if (cnt > 1) {
  21433. mime = files[0].mime;
  21434. ext = files[0].name.replace(/^.*(\.[^.]+)$/, '$1');
  21435. }
  21436. return $.grep(files, function(file) {
  21437. var res;
  21438. if (skip || file.mime === 'directory') {
  21439. return false;
  21440. }
  21441. res = file.read
  21442. && (allowAll || fm.mimeIsText(file.mime) || $.inArray(file.mime, cnt === 1? mimesSingle : mimes) !== -1)
  21443. && (!self.onlyMimes.length || $.inArray(file.mime, self.onlyMimes) !== -1)
  21444. && (cnt === 1 || (file.mime === mime && file.name.substr(ext.length * -1) === ext))
  21445. && (fm.uploadMimeCheck(file.mime, file.phash)? true : false)
  21446. && setEditors(file, cnt)
  21447. && Object.keys(editors).length;
  21448. if (!res) {
  21449. skip = true;
  21450. }
  21451. return res;
  21452. });
  21453. },
  21454. fileSync = function(hash) {
  21455. var old = fm.file(hash),
  21456. f;
  21457. fm.request({
  21458. cmd: 'info',
  21459. targets: [hash],
  21460. preventDefault: true
  21461. }).done(function(data) {
  21462. var changed;
  21463. if (data && data.files && data.files.length) {
  21464. f = data.files[0];
  21465. if (old.ts != f.ts || old.size != f.size) {
  21466. changed = { changed: [ f ] };
  21467. fm.updateCache(changed);
  21468. fm.change(changed);
  21469. }
  21470. }
  21471. });
  21472. },
  21473. /**
  21474. * Open dialog with textarea to edit file
  21475. *
  21476. * @param String id dialog id
  21477. * @param Object file file object
  21478. * @param String content file content
  21479. * @return $.Deferred
  21480. **/
  21481. dialog = function(id, file, content, encoding, editor) {
  21482. var dfrd = $.Deferred(),
  21483. _loaded = false,
  21484. loaded = function() {
  21485. if (!_loaded) {
  21486. fm.toast({
  21487. mode: 'warning',
  21488. msg: fm.i18n('nowLoading')
  21489. });
  21490. return false;
  21491. }
  21492. return true;
  21493. },
  21494. save = function() {
  21495. var encord = selEncoding? selEncoding.val():void(0),
  21496. saveDfd = $.Deferred().fail(function(err) {
  21497. dialogNode.show().find('button.elfinder-btncnt-0,button.elfinder-btncnt-1').hide();
  21498. }),
  21499. conf, res;
  21500. if (!loaded()) {
  21501. return saveDfd.resolve();
  21502. }
  21503. if (ta.editor) {
  21504. ta.editor.save(ta[0], ta.editor.instance);
  21505. conf = ta.editor.confObj;
  21506. if (conf.info && (conf.info.schemeContent || conf.info.arrayBufferContent)) {
  21507. encord = 'scheme';
  21508. }
  21509. }
  21510. res = getContent();
  21511. setOld(res);
  21512. if (res.promise) {
  21513. res.done(function(data) {
  21514. dfrd.notifyWith(ta, [encord, ta.data('hash'), old, saveDfd]);
  21515. }).fail(function(err) {
  21516. saveDfd.reject(err);
  21517. });
  21518. } else {
  21519. dfrd.notifyWith(ta, [encord, ta.data('hash'), old, saveDfd]);
  21520. }
  21521. return saveDfd;
  21522. },
  21523. saveon = function() {
  21524. if (!loaded()) { return; }
  21525. save().fail(function(err) {
  21526. err && fm.error(err);
  21527. });
  21528. },
  21529. cancel = function() {
  21530. ta.elfinderdialog('close');
  21531. },
  21532. savecl = function() {
  21533. if (!loaded()) { return; }
  21534. save().done(function() {
  21535. _loaded = false;
  21536. dialogNode.show();
  21537. cancel();
  21538. }).fail(function(err) {
  21539. dialogNode.show();
  21540. err && fm.error(err);
  21541. });
  21542. dialogNode.hide();
  21543. },
  21544. saveAs = function() {
  21545. if (!loaded()) { return; }
  21546. var prevOld = old,
  21547. phash = fm.file(file.phash)? file.phash : fm.cwd().hash,
  21548. fail = function(err) {
  21549. dialogs.addClass(clsEditing).fadeIn(function() {
  21550. err && fm.error(err);
  21551. });
  21552. old = prevOld;
  21553. fm.disable();
  21554. },
  21555. make = function() {
  21556. self.mime = saveAsFile.mime || file.mime;
  21557. self.prefix = (saveAsFile.name || file.name).replace(/ \d+(\.[^.]+)?$/, '$1');
  21558. self.requestCmd = 'mkfile';
  21559. self.nextAction = {};
  21560. self.data = {target : phash};
  21561. $.proxy(fm.res('mixin', 'make'), self)()
  21562. .done(function(data) {
  21563. if (data.added && data.added.length) {
  21564. ta.data('hash', data.added[0].hash);
  21565. save().done(function() {
  21566. _loaded = false;
  21567. dialogNode.show();
  21568. cancel();
  21569. dialogs.fadeIn();
  21570. }).fail(fail);
  21571. } else {
  21572. fail();
  21573. }
  21574. })
  21575. .progress(function(err) {
  21576. if (err && err === 'errUploadMime') {
  21577. ta.trigger('saveAsFail');
  21578. }
  21579. })
  21580. .fail(fail)
  21581. .always(function() {
  21582. delete self.mime;
  21583. delete self.prefix;
  21584. delete self.nextAction;
  21585. delete self.data;
  21586. });
  21587. fm.trigger('unselectfiles', { files: [ file.hash ] });
  21588. },
  21589. reqOpen = null,
  21590. dialogs = fm.getUI().children('.' + self.dialogClass + ':visible');
  21591. if (dialogNode.is(':hidden')) {
  21592. dialogs = dialogs.add(dialogNode);
  21593. }
  21594. dialogs.removeClass(clsEditing).fadeOut();
  21595. fm.enable();
  21596. if (fm.searchStatus.state < 2 && phash !== fm.cwd().hash) {
  21597. reqOpen = fm.exec('open', [phash], {thash: phash});
  21598. }
  21599. $.when([reqOpen]).done(function() {
  21600. reqOpen? fm.one('cwdrender', make) : make();
  21601. }).fail(fail);
  21602. },
  21603. changed = function() {
  21604. var dfd = $.Deferred(),
  21605. res, tm;
  21606. if (!_loaded) {
  21607. return dfd.resolve(false);
  21608. }
  21609. ta.editor && ta.editor.save(ta[0], ta.editor.instance);
  21610. res = getContent();
  21611. if (res && res.promise) {
  21612. tm = setTimeout(function() {
  21613. fm.notify({
  21614. type : 'chkcontent',
  21615. cnt : 1,
  21616. hideCnt: true
  21617. });
  21618. }, 100);
  21619. res.always(function() {
  21620. tm && clearTimeout(tm);
  21621. fm.notify({ type : 'chkcontent', cnt: -1 });
  21622. }).done(function(d) {
  21623. dfd.resolve(old !== d);
  21624. }).fail(function(err) {
  21625. dfd.resolve(err || true);
  21626. });
  21627. } else {
  21628. dfd.resolve(old !== res);
  21629. }
  21630. return dfd;
  21631. },
  21632. opts = {
  21633. title : fm.escape(file.name),
  21634. width : getDlgWidth(),
  21635. buttons : {},
  21636. cssClass : clsEditing,
  21637. maxWidth : 'window',
  21638. maxHeight : 'window',
  21639. allowMinimize : true,
  21640. allowMaximize : true,
  21641. openMaximized : editorMaximized() || (editor && editor.info && editor.info.openMaximized),
  21642. btnHoverFocus : false,
  21643. closeOnEscape : false,
  21644. propagationEvents : ['mousemove', 'mouseup', 'click'],
  21645. minimize : function() {
  21646. var conf;
  21647. if (ta.editor && dialogNode.closest('.ui-dialog').is(':hidden')) {
  21648. conf = ta.editor.confObj;
  21649. if (conf.info && conf.info.syncInterval) {
  21650. fileSync(file.hash);
  21651. }
  21652. }
  21653. },
  21654. close : function() {
  21655. var close = function() {
  21656. var conf;
  21657. dfrd.resolve();
  21658. if (ta.editor) {
  21659. ta.editor.close(ta[0], ta.editor.instance);
  21660. conf = ta.editor.confObj;
  21661. if (conf.info && conf.info.syncInterval) {
  21662. fileSync(file.hash);
  21663. }
  21664. }
  21665. ta.elfinderdialog('destroy');
  21666. },
  21667. onlySaveAs = (typeof saveAsFile.name !== 'undefined'),
  21668. accept = onlySaveAs? {
  21669. label : 'btnSaveAs',
  21670. callback : function() {
  21671. requestAnimationFrame(saveAs);
  21672. }
  21673. } : {
  21674. label : 'btnSaveClose',
  21675. callback : function() {
  21676. save().done(function() {
  21677. close();
  21678. });
  21679. }
  21680. };
  21681. changed().done(function(change) {
  21682. var msgs = ['confirmNotSave'];
  21683. if (change) {
  21684. if (typeof change === 'string') {
  21685. msgs.unshift(change);
  21686. }
  21687. fm.confirm({
  21688. title : self.title,
  21689. text : msgs,
  21690. accept : accept,
  21691. cancel : {
  21692. label : 'btnClose',
  21693. callback : close
  21694. },
  21695. buttons : onlySaveAs? null : [{
  21696. label : 'btnSaveAs',
  21697. callback : function() {
  21698. requestAnimationFrame(saveAs);
  21699. }
  21700. }]
  21701. });
  21702. } else {
  21703. close();
  21704. }
  21705. });
  21706. },
  21707. open : function() {
  21708. var loadRes, conf, interval;
  21709. ta.initEditArea.call(ta, id, file, content, fm);
  21710. if (ta.editor) {
  21711. loadRes = ta.editor.load(ta[0]) || null;
  21712. if (loadRes && loadRes.done) {
  21713. loadRes.always(function() {
  21714. _loaded = true;
  21715. }).done(function(instance) {
  21716. ta.editor.instance = instance;
  21717. ta.editor.focus(ta[0], ta.editor.instance);
  21718. setOld(getContent());
  21719. requestAnimationFrame(function() {
  21720. dialogNode.trigger('resize');
  21721. });
  21722. }).fail(function(error) {
  21723. error && fm.error(error);
  21724. ta.elfinderdialog('destroy');
  21725. return;
  21726. });
  21727. } else {
  21728. _loaded = true;
  21729. if (loadRes && (typeof loadRes === 'string' || Array.isArray(loadRes))) {
  21730. fm.error(loadRes);
  21731. ta.elfinderdialog('destroy');
  21732. return;
  21733. }
  21734. ta.editor.instance = loadRes;
  21735. ta.editor.focus(ta[0], ta.editor.instance);
  21736. setOld(getContent());
  21737. requestAnimationFrame(function() {
  21738. dialogNode.trigger('resize');
  21739. });
  21740. }
  21741. conf = ta.editor.confObj;
  21742. if (conf.info && conf.info.syncInterval) {
  21743. if (interval = parseInt(conf.info.syncInterval)) {
  21744. setTimeout(function() {
  21745. autoSync(interval);
  21746. }, interval);
  21747. }
  21748. }
  21749. } else {
  21750. _loaded = true;
  21751. setOld(getContent());
  21752. }
  21753. },
  21754. resize : function(e, data) {
  21755. ta.editor && ta.editor.resize(ta[0], ta.editor.instance, e, data || {});
  21756. }
  21757. },
  21758. getContent = function() {
  21759. return ta.getContent.call(ta, ta[0]);
  21760. },
  21761. setOld = function(res) {
  21762. if (res && res.promise) {
  21763. res.done(function(d) {
  21764. old = d;
  21765. });
  21766. } else {
  21767. old = res;
  21768. }
  21769. },
  21770. autoSync = function(interval) {
  21771. if (dialogNode.is(':visible')) {
  21772. fileSync(file.hash);
  21773. setTimeout(function() {
  21774. autoSync(interval);
  21775. }, interval);
  21776. }
  21777. },
  21778. saveAsFile = {},
  21779. ta, old, dialogNode, selEncoding, extEditor, maxW, syncInterval;
  21780. if (editor) {
  21781. if (editor.html) {
  21782. ta = $(editor.html);
  21783. }
  21784. extEditor = {
  21785. init : editor.init || null,
  21786. load : editor.load,
  21787. getContent : editor.getContent || null,
  21788. save : editor.save,
  21789. beforeclose : typeof editor.beforeclose == 'function' ? editor.beforeclose : void 0,
  21790. close : typeof editor.close == 'function' ? editor.close : function() {},
  21791. focus : typeof editor.focus == 'function' ? editor.focus : function() {},
  21792. resize : typeof editor.resize == 'function' ? editor.resize : function() {},
  21793. instance : null,
  21794. doSave : saveon,
  21795. doCancel : cancel,
  21796. doClose : savecl,
  21797. file : file,
  21798. fm : fm,
  21799. confObj : editor,
  21800. trigger : function(evName, data) {
  21801. fm.trigger('editEditor' + evName, Object.assign({}, editor.info || {}, data));
  21802. }
  21803. };
  21804. }
  21805. if (!ta) {
  21806. if (!fm.mimeIsText(file.mime)) {
  21807. return dfrd.reject('errEditorNotFound');
  21808. }
  21809. (function() {
  21810. var stateChange = function() {
  21811. if (selEncoding) {
  21812. changed().done(function(change) {
  21813. if (change) {
  21814. selEncoding.attr('title', fm.i18n('saveAsEncoding')).addClass('elfinder-edit-changed');
  21815. } else {
  21816. selEncoding.attr('title', fm.i18n('openAsEncoding')).removeClass('elfinder-edit-changed');
  21817. }
  21818. });
  21819. }
  21820. };
  21821. ta = $('<textarea class="elfinder-file-edit" rows="20" id="'+id+'-ta"></textarea>')
  21822. .on('input propertychange', stateChange);
  21823. if (!ta.editor || !ta.editor.info || ta.editor.info.useTextAreaEvent) {
  21824. ta.on('keydown', function(e) {
  21825. var code = e.keyCode,
  21826. value, start;
  21827. e.stopPropagation();
  21828. if (code == $.ui.keyCode.TAB) {
  21829. e.preventDefault();
  21830. // insert tab on tab press
  21831. if (this.setSelectionRange) {
  21832. value = this.value;
  21833. start = this.selectionStart;
  21834. this.value = value.substr(0, start) + "\t" + value.substr(this.selectionEnd);
  21835. start += 1;
  21836. this.setSelectionRange(start, start);
  21837. }
  21838. }
  21839. if (e.ctrlKey || e.metaKey) {
  21840. // close on ctrl+w/q
  21841. if (code == 'Q'.charCodeAt(0) || code == 'W'.charCodeAt(0)) {
  21842. e.preventDefault();
  21843. cancel();
  21844. }
  21845. if (code == 'S'.charCodeAt(0)) {
  21846. e.preventDefault();
  21847. saveon();
  21848. }
  21849. }
  21850. })
  21851. .on('mouseenter', function(){this.focus();});
  21852. }
  21853. ta.initEditArea = function(id, file, content) {
  21854. var heads = (encoding && encoding !== 'unknown')? [{value: encoding}] : [],
  21855. wfake = $('<select/>').hide(),
  21856. setSelW = function(init) {
  21857. init && wfake.appendTo(selEncoding.parent());
  21858. wfake.empty().append($('<option/>').text(selEncoding.val()));
  21859. selEncoding.width(wfake.width());
  21860. };
  21861. // ta.hide() for performance tune. Need ta.show() in `load()` if use textarea node.
  21862. ta.hide().val(content);
  21863. if (content === '' || ! encoding || encoding !== 'UTF-8') {
  21864. heads.push({value: 'UTF-8'});
  21865. }
  21866. selEncoding = getEncSelect(heads).on('touchstart', function(e) {
  21867. // for touch punch event handler
  21868. e.stopPropagation();
  21869. }).on('change', function() {
  21870. // reload to change encoding if not edited
  21871. changed().done(function(change) {
  21872. if (! change && getContent() !== '') {
  21873. cancel();
  21874. edit(file, selEncoding.val(), editor).fail(function(err) { err && fm.error(err); });
  21875. }
  21876. });
  21877. setSelW();
  21878. }).on('mouseover', stateChange);
  21879. ta.parent().next().prepend($('<div class="ui-dialog-buttonset elfinder-edit-extras"/>').append(selEncoding));
  21880. setSelW(true);
  21881. };
  21882. })();
  21883. }
  21884. ta.data('hash', file.hash);
  21885. if (extEditor) {
  21886. ta.editor = extEditor;
  21887. if (typeof extEditor.beforeclose === 'function') {
  21888. opts.beforeclose = function() {
  21889. return extEditor.beforeclose(ta[0], extEditor.instance);
  21890. };
  21891. }
  21892. if (typeof extEditor.init === 'function') {
  21893. ta.initEditArea = extEditor.init;
  21894. }
  21895. if (typeof extEditor.getContent === 'function') {
  21896. ta.getContent = extEditor.getContent;
  21897. }
  21898. }
  21899. if (! ta.initEditArea) {
  21900. ta.initEditArea = function() {};
  21901. }
  21902. if (! ta.getContent) {
  21903. ta.getContent = function() {
  21904. return rtrim(ta.val());
  21905. };
  21906. }
  21907. if (!editor || !editor.info || !editor.info.preventGet) {
  21908. opts.buttons[fm.i18n('btnSave')] = saveon;
  21909. opts.buttons[fm.i18n('btnSaveClose')] = savecl;
  21910. opts.buttons[fm.i18n('btnSaveAs')] = saveAs;
  21911. opts.buttons[fm.i18n('btnCancel')] = cancel;
  21912. }
  21913. if (editor && typeof editor.prepare === 'function') {
  21914. editor.prepare(ta, opts, file);
  21915. }
  21916. dialogNode = self.fmDialog(ta, opts)
  21917. .attr('id', id)
  21918. .on('keydown keyup keypress', function(e) {
  21919. e.stopPropagation();
  21920. })
  21921. .css({ overflow: 'hidden', minHeight: '7em' })
  21922. .addClass('elfinder-edit-editor')
  21923. .closest('.ui-dialog')
  21924. .on('changeType', function(e, data) {
  21925. if (data.extention && data.mime) {
  21926. var ext = data.extention,
  21927. mime = data.mime,
  21928. btnSet = $(this).children('.ui-dialog-buttonpane').children('.ui-dialog-buttonset');
  21929. btnSet.children('.elfinder-btncnt-0,.elfinder-btncnt-1').hide();
  21930. saveAsFile.name = fm.splitFileExtention(file.name)[0] + '.' + data.extention;
  21931. saveAsFile.mime = data.mime;
  21932. if (!data.keepEditor) {
  21933. btnSet.children('.elfinder-btncnt-2').trigger('click');
  21934. }
  21935. }
  21936. });
  21937. // care to viewport scale change with mobile devices
  21938. maxW = (fm.options.dialogContained? elfNode : $(window)).width();
  21939. (dialogNode.width() > maxW) && dialogNode.width(maxW);
  21940. return dfrd.promise();
  21941. },
  21942. /**
  21943. * Get file content and
  21944. * open dialog with textarea to edit file content
  21945. *
  21946. * @param String file hash
  21947. * @return jQuery.Deferred
  21948. **/
  21949. edit = function(file, convert, editor) {
  21950. var hash = file.hash,
  21951. opts = fm.options,
  21952. dfrd = $.Deferred(),
  21953. id = 'edit-'+fm.namespace+'-'+file.hash,
  21954. d = fm.getUI().find('#'+id),
  21955. conv = !convert? 0 : convert,
  21956. noContent = false,
  21957. req, error, res;
  21958. if (d.length) {
  21959. d.elfinderdialog('toTop');
  21960. return dfrd.resolve();
  21961. }
  21962. if (!file.read || (!file.write && (!editor.info || !editor.info.converter))) {
  21963. error = ['errOpen', file.name, 'errPerm'];
  21964. return dfrd.reject(error);
  21965. }
  21966. if (editor && editor.info) {
  21967. if (typeof editor.info.edit === 'function') {
  21968. res = editor.info.edit.call(fm, file, editor);
  21969. if (res.promise) {
  21970. res.done(function() {
  21971. dfrd.resolve();
  21972. }).fail(function(error) {
  21973. dfrd.reject(error);
  21974. });
  21975. } else {
  21976. res? dfrd.resolve() : dfrd.reject();
  21977. }
  21978. return dfrd;
  21979. }
  21980. noContent = editor.info.preventGet || editor.info.noContent;
  21981. if (editor.info.urlAsContent || noContent) {
  21982. req = $.Deferred();
  21983. if (editor.info.urlAsContent) {
  21984. fm.url(hash, { async: true, onetime: true, temporary: true }).done(function(url) {
  21985. req.resolve({content: url});
  21986. });
  21987. } else {
  21988. req.resolve({});
  21989. }
  21990. } else {
  21991. req = fm.request({
  21992. data : {cmd : 'get', target : hash, conv : conv, _t : file.ts},
  21993. options : {type: 'get', cache : true},
  21994. notify : {type : 'file', cnt : 1},
  21995. preventDefault : true
  21996. });
  21997. }
  21998. req.done(function(data) {
  21999. var selEncoding, reg, m, res;
  22000. if (data.doconv) {
  22001. fm.confirm({
  22002. title : self.title,
  22003. text : data.doconv === 'unknown'? 'confirmNonUTF8' : 'confirmConvUTF8',
  22004. accept : {
  22005. label : 'btnConv',
  22006. callback : function() {
  22007. dfrd = edit(file, selEncoding.val(), editor);
  22008. }
  22009. },
  22010. cancel : {
  22011. label : 'btnCancel',
  22012. callback : function() { dfrd.reject(); }
  22013. },
  22014. optionsCallback : function(options) {
  22015. options.create = function() {
  22016. var base = $('<div class="elfinder-dialog-confirm-encoding"/>'),
  22017. head = {value: data.doconv},
  22018. detected;
  22019. if (data.doconv === 'unknown') {
  22020. head.caption = '-';
  22021. }
  22022. selEncoding = getEncSelect([head]);
  22023. $(this).next().find('.ui-dialog-buttonset')
  22024. .prepend(base.append($('<label>'+fm.i18n('encoding')+' </label>').append(selEncoding)));
  22025. };
  22026. }
  22027. });
  22028. } else {
  22029. if (!noContent && fm.mimeIsText(file.mime)) {
  22030. reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i');
  22031. if (!editor.info.dataScheme) {
  22032. if (window.atob && (m = data.content.match(reg))) {
  22033. data.content = atob(data.content.substr(m[1].length));
  22034. }
  22035. } else {
  22036. if (window.btoa && !data.content.match(reg)) {
  22037. data.content = 'data:'+file.mime+';base64,'+btoa(data.content);
  22038. }
  22039. }
  22040. }
  22041. dialog(id, file, data.content, data.encoding, editor)
  22042. .done(function(data) {
  22043. dfrd.resolve(data);
  22044. })
  22045. .progress(function(encoding, newHash, data, saveDfd) {
  22046. var ta = this;
  22047. if (newHash) {
  22048. hash = newHash;
  22049. }
  22050. fm.request({
  22051. options : {type : 'post'},
  22052. data : {
  22053. cmd : 'put',
  22054. target : hash,
  22055. encoding : encoding || data.encoding,
  22056. content : data
  22057. },
  22058. notify : {type : 'save', cnt : 1},
  22059. syncOnFail : true,
  22060. preventFail : true,
  22061. navigate : {
  22062. target : 'changed',
  22063. toast : {
  22064. inbuffer : {msg: fm.i18n(['complete', fm.i18n('btnSave')])}
  22065. }
  22066. }
  22067. })
  22068. .fail(function(error) {
  22069. dfrd.reject(error);
  22070. saveDfd.reject();
  22071. })
  22072. .done(function(data) {
  22073. requestAnimationFrame(function(){
  22074. ta.trigger('focus');
  22075. ta.editor && ta.editor.focus(ta[0], ta.editor.instance);
  22076. });
  22077. saveDfd.resolve();
  22078. });
  22079. })
  22080. .fail(function(error) {
  22081. dfrd.reject(error);
  22082. });
  22083. }
  22084. })
  22085. .fail(function(error) {
  22086. var err = fm.parseError(error);
  22087. err = Array.isArray(err)? err[0] : err;
  22088. (err !== 'errConvUTF8') && fm.sync();
  22089. dfrd.reject(error);
  22090. });
  22091. }
  22092. return dfrd.promise();
  22093. },
  22094. /**
  22095. * Current editors of selected files
  22096. *
  22097. * @type Object
  22098. */
  22099. editors = {},
  22100. /**
  22101. * Fallback editor (Simple text editor)
  22102. *
  22103. * @type Object
  22104. */
  22105. fallbackEditor = {
  22106. // Simple Text (basic textarea editor)
  22107. info : {
  22108. id : 'textarea',
  22109. name : 'TextArea',
  22110. useTextAreaEvent : true
  22111. },
  22112. load : function(textarea) {
  22113. // trigger event 'editEditorPrepare'
  22114. this.trigger('Prepare', {
  22115. node: textarea,
  22116. editorObj: void(0),
  22117. instance: void(0),
  22118. opts: {}
  22119. });
  22120. textarea.setSelectionRange && textarea.setSelectionRange(0, 0);
  22121. $(textarea).trigger('focus').show();
  22122. },
  22123. save : function(){}
  22124. },
  22125. /**
  22126. * Set current editors
  22127. *
  22128. * @param Object file object
  22129. * @param Number cnt count of selected items
  22130. * @return Void
  22131. */
  22132. setEditors = function(file, cnt) {
  22133. var mimeMatch = function(fileMime, editorMimes){
  22134. if (!editorMimes) {
  22135. return fm.mimeIsText(fileMime);
  22136. } else {
  22137. if (editorMimes[0] === '*' || $.inArray(fileMime, editorMimes) !== -1) {
  22138. return true;
  22139. }
  22140. var i, l;
  22141. l = editorMimes.length;
  22142. for (i = 0; i < l; i++) {
  22143. if (fileMime.indexOf(editorMimes[i]) === 0) {
  22144. return true;
  22145. }
  22146. }
  22147. return false;
  22148. }
  22149. },
  22150. extMatch = function(fileName, editorExts){
  22151. if (!editorExts || !editorExts.length) {
  22152. return true;
  22153. }
  22154. var ext = fileName.replace(/^.+\.([^.]+)|(.+)$/, '$1$2').toLowerCase(),
  22155. i, l;
  22156. l = editorExts.length;
  22157. for (i = 0; i < l; i++) {
  22158. if (ext === editorExts[i].toLowerCase()) {
  22159. return true;
  22160. }
  22161. }
  22162. return false;
  22163. },
  22164. optEditors = self.options.editors || [],
  22165. cwdWrite = fm.cwd().write;
  22166. stored = fm.storage('storedEditors') || {};
  22167. editors = {};
  22168. if (!optEditors.length) {
  22169. optEditors = [fallbackEditor];
  22170. }
  22171. $.each(optEditors, function(i, editor) {
  22172. var name;
  22173. if ((cnt === 1 || !editor.info.single)
  22174. && ((!editor.info || !editor.info.converter)? file.write : cwdWrite)
  22175. && (file.size > 0 || (!editor.info.converter && (editor.info.canMakeEmpty || (editor.info.canMakeEmpty !== false && fm.mimeIsText(file.mime)))))
  22176. && (!editor.info.maxSize || file.size <= editor.info.maxSize)
  22177. && mimeMatch(file.mime, editor.mimes || null)
  22178. && extMatch(file.name, editor.exts || null)
  22179. && typeof editor.load == 'function'
  22180. && typeof editor.save == 'function') {
  22181. name = editor.info.name? editor.info.name : ('Editor ' + i);
  22182. editor.id = editor.info.id? editor.info.id : ('editor' + i),
  22183. editor.name = name;
  22184. editor.i18n = fm.i18n(name);
  22185. editors[editor.id] = editor;
  22186. }
  22187. });
  22188. return Object.keys(editors).length? true : false;
  22189. },
  22190. store = function(mime, editor) {
  22191. if (mime && editor) {
  22192. if (!$.isPlainObject(stored)) {
  22193. stored = {};
  22194. }
  22195. stored[mime] = editor.id;
  22196. fm.storage('storedEditors', stored);
  22197. fm.trigger('selectfiles', {files : fm.selected()});
  22198. }
  22199. },
  22200. useStoredEditor = function() {
  22201. var d = fm.storage('useStoredEditor');
  22202. return d? (d > 0) : self.options.useStoredEditor;
  22203. },
  22204. editorMaximized = function() {
  22205. var d = fm.storage('editorMaximized');
  22206. return d? (d > 0) : self.options.editorMaximized;
  22207. },
  22208. getSubMenuRaw = function(files, callback) {
  22209. var subMenuRaw = [];
  22210. $.each(editors, function(id, ed) {
  22211. subMenuRaw.push(
  22212. {
  22213. label : fm.escape(ed.i18n),
  22214. icon : ed.info && ed.info.icon? ed.info.icon : 'edit',
  22215. options : { iconImg: ed.info && ed.info.iconImg? fm.baseUrl + ed.info.iconImg : void(0) },
  22216. callback : function() {
  22217. store(files[0].mime, ed);
  22218. callback && callback.call(ed);
  22219. }
  22220. }
  22221. );
  22222. });
  22223. return subMenuRaw;
  22224. },
  22225. getStoreId = function(name) {
  22226. // for compatibility to previous version
  22227. return name.toLowerCase().replace(/ +/g, '');
  22228. },
  22229. getStoredEditor = function(mime) {
  22230. var name = stored[mime];
  22231. return name && Object.keys(editors).length? editors[getStoreId(name)] : void(0);
  22232. },
  22233. infoRequest = function() {
  22234. },
  22235. stored;
  22236. this.shortcuts = [{
  22237. pattern : 'ctrl+e'
  22238. }];
  22239. this.init = function() {
  22240. var self = this,
  22241. fm = this.fm,
  22242. opts = this.options,
  22243. cmdChecks = [],
  22244. ccData, dfd;
  22245. this.onlyMimes = this.options.mimes || [];
  22246. fm.one('open', function() {
  22247. // editors setup
  22248. if (opts.editors && Array.isArray(opts.editors)) {
  22249. fm.trigger('canMakeEmptyFile', {mimes: Object.keys(fm.storage('mkfileTextMimes') || {}).concat(opts.makeTextMimes || ['text/plain'])});
  22250. $.each(opts.editors, function(i, editor) {
  22251. if (editor.info && editor.info.cmdCheck) {
  22252. cmdChecks.push(editor.info.cmdCheck);
  22253. }
  22254. });
  22255. if (cmdChecks.length) {
  22256. if (fm.api >= 2.1030) {
  22257. dfd = fm.request({
  22258. data : {
  22259. cmd: 'editor',
  22260. name: cmdChecks,
  22261. method: 'enabled'
  22262. },
  22263. preventDefault : true
  22264. }).done(function(d) {
  22265. ccData = d;
  22266. }).fail(function() {
  22267. ccData = {};
  22268. });
  22269. } else {
  22270. ccData = {};
  22271. dfd = $.Deferred().resolve();
  22272. }
  22273. } else {
  22274. dfd = $.Deferred().resolve();
  22275. }
  22276. dfd.always(function() {
  22277. if (ccData) {
  22278. opts.editors = $.grep(opts.editors, function(e) {
  22279. if (e.info && e.info.cmdCheck) {
  22280. return ccData[e.info.cmdCheck]? true : false;
  22281. } else {
  22282. return true;
  22283. }
  22284. });
  22285. }
  22286. $.each(opts.editors, function(i, editor) {
  22287. if (editor.setup && typeof editor.setup === 'function') {
  22288. editor.setup.call(editor, opts, fm);
  22289. }
  22290. if (!editor.disabled) {
  22291. if (editor.mimes && Array.isArray(editor.mimes)) {
  22292. mimesSingle = mimesSingle.concat(editor.mimes);
  22293. if (!editor.info || !editor.info.single) {
  22294. mimes = mimes.concat(editor.mimes);
  22295. }
  22296. }
  22297. if (!allowAll && editor.mimes && editor.mimes[0] === '*') {
  22298. allowAll = true;
  22299. }
  22300. if (!editor.info) {
  22301. editor.info = {};
  22302. }
  22303. if (editor.info.integrate) {
  22304. fm.trigger('helpIntegration', Object.assign({cmd: 'edit'}, editor.info.integrate));
  22305. }
  22306. if (editor.info.canMakeEmpty) {
  22307. fm.trigger('canMakeEmptyFile', {mimes: editor.mimes});
  22308. }
  22309. }
  22310. });
  22311. mimesSingle = ($.uniqueSort || $.unique)(mimesSingle);
  22312. mimes = ($.uniqueSort || $.unique)(mimes);
  22313. opts.editors = $.grep(opts.editors, function(e) {
  22314. return e.disabled? false : true;
  22315. });
  22316. });
  22317. }
  22318. })
  22319. .bind('select', function() {
  22320. editors = null;
  22321. })
  22322. .bind('contextmenucreate', function(e) {
  22323. var file, editor,
  22324. single = function(editor) {
  22325. var title = self.title;
  22326. fm.one('contextmenucreatedone', function() {
  22327. self.title = title;
  22328. });
  22329. self.title = fm.escape(editor.i18n);
  22330. if (editor.info && editor.info.iconImg) {
  22331. self.contextmenuOpts = {
  22332. iconImg: fm.baseUrl + editor.info.iconImg
  22333. };
  22334. }
  22335. delete self.variants;
  22336. };
  22337. self.contextmenuOpts = void(0);
  22338. if (e.data.type === 'files' && self.enabled()) {
  22339. file = fm.file(e.data.targets[0]);
  22340. if (setEditors(file, e.data.targets.length)) {
  22341. if (Object.keys(editors).length > 1) {
  22342. if (!useStoredEditor() || !(editor = getStoredEditor(file.mime))) {
  22343. delete self.extra;
  22344. self.variants = [];
  22345. $.each(editors, function(id, editor) {
  22346. self.variants.push([{ editor: editor }, editor.i18n, editor.info && editor.info.iconImg? fm.baseUrl + editor.info.iconImg : 'edit']);
  22347. });
  22348. } else {
  22349. single(editor);
  22350. self.extra = {
  22351. icon: 'menu',
  22352. node: $('<span/>')
  22353. .attr({title: fm.i18n('select')})
  22354. .on('click touchstart', function(e){
  22355. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  22356. return;
  22357. }
  22358. var node = $(this);
  22359. e.stopPropagation();
  22360. e.preventDefault();
  22361. fm.trigger('contextmenu', {
  22362. raw: getSubMenuRaw(fm.selectedFiles(), function() {
  22363. var hashes = fm.selected();
  22364. fm.exec('edit', hashes, {editor: this});
  22365. fm.trigger('selectfiles', {files : hashes});
  22366. }),
  22367. x: node.offset().left,
  22368. y: node.offset().top
  22369. });
  22370. })
  22371. };
  22372. }
  22373. } else {
  22374. single(editors[Object.keys(editors)[0]]);
  22375. delete self.extra;
  22376. }
  22377. }
  22378. }
  22379. })
  22380. .bind('canMakeEmptyFile', function(e) {
  22381. if (e.data && e.data.resetTexts) {
  22382. var defs = fm.arrayFlip(self.options.makeTextMimes || ['text/plain']),
  22383. hides = fm.storage('mkfileHides') || {};
  22384. $.each((fm.storage('mkfileTextMimes') || {}), function(mime, type) {
  22385. if (!defs[mime]) {
  22386. delete fm.mimesCanMakeEmpty[mime];
  22387. delete hides[mime];
  22388. }
  22389. });
  22390. fm.storage('mkfileTextMimes', null);
  22391. if (Object.keys(hides).length) {
  22392. fm.storage('mkfileHides', hides);
  22393. } else {
  22394. fm.storage('mkfileHides', null);
  22395. }
  22396. }
  22397. });
  22398. };
  22399. this.getstate = function(select) {
  22400. var sel = this.files(select),
  22401. cnt = sel.length;
  22402. return cnt && filter(sel).length == cnt ? 0 : -1;
  22403. };
  22404. this.exec = function(select, opts) {
  22405. var fm = this.fm,
  22406. files = filter(this.files(select)),
  22407. hashes = $.map(files, function(f) { return f.hash; }),
  22408. list = [],
  22409. editor = opts && opts.editor? opts.editor : null,
  22410. node = $(opts && opts._currentNode? opts._currentNode : fm.cwdHash2Elm(hashes[0])),
  22411. getEditor = function() {
  22412. var dfd = $.Deferred(),
  22413. storedId;
  22414. if (!editor && Object.keys(editors).length > 1) {
  22415. if (useStoredEditor() && (editor = getStoredEditor(files[0].mime))) {
  22416. return dfd.resolve(editor);
  22417. }
  22418. fm.trigger('contextmenu', {
  22419. raw: getSubMenuRaw(files, function() {
  22420. dfd.resolve(this);
  22421. }),
  22422. x: node.offset().left,
  22423. y: node.offset().top + 22,
  22424. opened: function() {
  22425. fm.one('closecontextmenu',function() {
  22426. requestAnimationFrame(function() {
  22427. if (dfd.state() === 'pending') {
  22428. dfd.reject();
  22429. }
  22430. });
  22431. });
  22432. }
  22433. });
  22434. fm.trigger('selectfiles', {files : hashes});
  22435. return dfd;
  22436. } else {
  22437. Object.keys(editors).length > 1 && editor && store(files[0].mime, editor);
  22438. return dfd.resolve(editor? editor : (Object.keys(editors).length? editors[Object.keys(editors)[0]] : null));
  22439. }
  22440. },
  22441. dfrd = $.Deferred(),
  22442. file;
  22443. if (editors === null) {
  22444. setEditors(files[0], hashes.length);
  22445. }
  22446. if (!node.length) {
  22447. node = fm.getUI('cwd');
  22448. }
  22449. getEditor().done(function(editor) {
  22450. while ((file = files.shift())) {
  22451. list.push(edit(file, void(0), editor).fail(function(error) {
  22452. error && fm.error(error);
  22453. }));
  22454. }
  22455. if (list.length) {
  22456. $.when.apply(null, list).done(function() {
  22457. dfrd.resolve();
  22458. }).fail(function() {
  22459. dfrd.reject();
  22460. });
  22461. } else {
  22462. dfrd.reject();
  22463. }
  22464. }).fail(function() {
  22465. dfrd.reject();
  22466. });
  22467. return dfrd;
  22468. };
  22469. };
  22470. /*
  22471. * File: /js/commands/empty.js
  22472. */
  22473. /**
  22474. * @class elFinder command "empty".
  22475. * Empty the folder
  22476. *
  22477. * @type elFinder.command
  22478. * @author Naoki Sawada
  22479. */
  22480. elFinder.prototype.commands.empty = function() {
  22481. var self, fm,
  22482. selFiles = function(select) {
  22483. var sel = self.files(select);
  22484. if (!sel.length) {
  22485. sel = [ fm.cwd() ];
  22486. }
  22487. return sel;
  22488. };
  22489. this.linkedCmds = ['rm'];
  22490. this.init = function() {
  22491. // lazy assign to make possible to become superclass
  22492. self = this;
  22493. fm = this.fm;
  22494. };
  22495. this.getstate = function(select) {
  22496. var sel = selFiles(select),
  22497. cnt;
  22498. cnt = sel.length;
  22499. return $.grep(sel, function(f) { return f.read && f.write && f.mime === 'directory' ? true : false; }).length == cnt ? 0 : -1;
  22500. };
  22501. this.exec = function(hashes) {
  22502. var dirs = selFiles(hashes),
  22503. cnt = dirs.length,
  22504. dfrd = $.Deferred()
  22505. .done(function() {
  22506. var data = {changed: {}};
  22507. fm.toast({msg: fm.i18n(['"'+success.join('", ')+'"', 'complete', fm.i18n('cmdempty')])});
  22508. $.each(dirs, function(i, dir) {
  22509. data.changed[dir.hash] = dir;
  22510. });
  22511. fm.change(data);
  22512. })
  22513. .always(function() {
  22514. var cwd = fm.cwd().hash;
  22515. fm.trigger('selectfiles', {files: $.map(dirs, function(d) { return cwd === d.phash? d.hash : null; })});
  22516. }),
  22517. success = [],
  22518. done = function(res) {
  22519. if (typeof res === 'number') {
  22520. success.push(dirs[res].name);
  22521. delete dirs[res].dirs;
  22522. } else {
  22523. res && fm.error(res);
  22524. }
  22525. (--cnt < 1) && dfrd[success.length? 'resolve' : 'reject']();
  22526. };
  22527. $.each(dirs, function(i, dir) {
  22528. var tm;
  22529. if (!(dir.write && dir.mime === 'directory')) {
  22530. done(['errEmpty', dir.name, 'errPerm']);
  22531. return null;
  22532. }
  22533. if (!fm.isCommandEnabled('rm', dir.hash)) {
  22534. done(['errCmdNoSupport', '"rm"']);
  22535. return null;
  22536. }
  22537. tm = setTimeout(function() {
  22538. fm.notify({type : 'search', cnt : 1, hideCnt : cnt > 1? false : true});
  22539. }, fm.notifyDelay);
  22540. fm.request({
  22541. data : {cmd : 'open', target : dir.hash},
  22542. preventDefault : true,
  22543. asNotOpen : true
  22544. }).done(function(data) {
  22545. var targets = [];
  22546. tm && clearTimeout(tm);
  22547. if (fm.ui.notify.children('.elfinder-notify-search').length) {
  22548. fm.notify({type : 'search', cnt : -1, hideCnt : cnt > 1? false : true});
  22549. }
  22550. if (data && data.files && data.files.length) {
  22551. if (data.files.length > fm.maxTargets) {
  22552. done(['errEmpty', dir.name, 'errMaxTargets', fm.maxTargets]);
  22553. } else {
  22554. fm.updateCache(data);
  22555. $.each(data.files, function(i, f) {
  22556. if (!f.write || f.locked) {
  22557. done(['errEmpty', dir.name, 'errRm', f.name, 'errPerm']);
  22558. targets = [];
  22559. return false;
  22560. }
  22561. targets.push(f.hash);
  22562. });
  22563. if (targets.length) {
  22564. fm.exec('rm', targets, { _userAction : true, addTexts : [ fm.i18n('folderToEmpty', dir.name) ] })
  22565. .fail(function(error) {
  22566. fm.trigger('unselectfiles', {files: fm.selected()});
  22567. done(fm.parseError(error) || '');
  22568. })
  22569. .done(function() { done(i); });
  22570. }
  22571. }
  22572. } else {
  22573. fm.toast({ mode: 'warning', msg: fm.i18n('filderIsEmpty', dir.name)});
  22574. done('');
  22575. }
  22576. }).fail(function(error) {
  22577. done(fm.parseError(error) || '');
  22578. });
  22579. });
  22580. return dfrd;
  22581. };
  22582. };
  22583. /*
  22584. * File: /js/commands/extract.js
  22585. */
  22586. /**
  22587. * @class elFinder command "extract"
  22588. * Extract files from archive
  22589. *
  22590. * @author Dmitry (dio) Levashov
  22591. **/
  22592. elFinder.prototype.commands.extract = function() {
  22593. var self = this,
  22594. fm = self.fm,
  22595. mimes = [],
  22596. filter = function(files) {
  22597. return $.grep(files, function(file) {
  22598. return file.read && $.inArray(file.mime, mimes) !== -1 ? true : false;
  22599. });
  22600. };
  22601. this.variants = [];
  22602. this.disableOnSearch = true;
  22603. // Update mimes list on open/reload
  22604. fm.bind('open reload', function() {
  22605. mimes = fm.option('archivers')['extract'] || [];
  22606. if (fm.api > 2) {
  22607. self.variants = [[{makedir: true}, fm.i18n('cmdmkdir')], [{}, fm.i18n('btnCwd')]];
  22608. } else {
  22609. self.variants = [[{}, fm.i18n('btnCwd')]];
  22610. }
  22611. self.change();
  22612. });
  22613. this.getstate = function(select) {
  22614. var sel = this.files(select),
  22615. cnt = sel.length;
  22616. return cnt && this.fm.cwd().write && filter(sel).length == cnt ? 0 : -1;
  22617. };
  22618. this.exec = function(hashes, opts) {
  22619. var files = this.files(hashes),
  22620. dfrd = $.Deferred(),
  22621. cnt = files.length,
  22622. makedir = opts && opts.makedir ? 1 : 0,
  22623. i, error,
  22624. decision;
  22625. var overwriteAll = false;
  22626. var omitAll = false;
  22627. var mkdirAll = 0;
  22628. var names = $.map(fm.files(hashes), function(file) { return file.name; });
  22629. var map = {};
  22630. $.grep(fm.files(hashes), function(file) {
  22631. map[file.name] = file;
  22632. return false;
  22633. });
  22634. var decide = function(decision) {
  22635. switch (decision) {
  22636. case 'overwrite_all' :
  22637. overwriteAll = true;
  22638. break;
  22639. case 'omit_all':
  22640. omitAll = true;
  22641. break;
  22642. }
  22643. };
  22644. var unpack = function(file) {
  22645. if (!(file.read && fm.file(file.phash).write)) {
  22646. error = ['errExtract', file.name, 'errPerm'];
  22647. fm.error(error);
  22648. dfrd.reject(error);
  22649. } else if ($.inArray(file.mime, mimes) === -1) {
  22650. error = ['errExtract', file.name, 'errNoArchive'];
  22651. fm.error(error);
  22652. dfrd.reject(error);
  22653. } else {
  22654. fm.request({
  22655. data:{cmd:'extract', target:file.hash, makedir:makedir},
  22656. notify:{type:'extract', cnt:1},
  22657. syncOnFail:true,
  22658. navigate:{
  22659. toast : makedir? {
  22660. incwd : {msg: fm.i18n(['complete', fm.i18n('cmdextract')]), action: {cmd: 'open', msg: 'cmdopen'}},
  22661. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdextract')]), action: {cmd: 'open', msg: 'cmdopen'}}
  22662. } : {
  22663. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdextract')])}
  22664. }
  22665. }
  22666. })
  22667. .fail(function (error) {
  22668. if (dfrd.state() != 'rejected') {
  22669. dfrd.reject(error);
  22670. }
  22671. })
  22672. .done(function () {
  22673. });
  22674. }
  22675. };
  22676. var confirm = function(files, index) {
  22677. var file = files[index],
  22678. name = fm.splitFileExtention(file.name)[0],
  22679. existed = ($.inArray(name, names) >= 0),
  22680. next = function(){
  22681. if((index+1) < cnt) {
  22682. confirm(files, index+1);
  22683. } else {
  22684. dfrd.resolve();
  22685. }
  22686. };
  22687. if (!makedir && existed && map[name].mime != 'directory') {
  22688. fm.confirm(
  22689. {
  22690. title : fm.i18n('ntfextract'),
  22691. text : ['errExists', name, 'confirmRepl'],
  22692. accept:{
  22693. label : 'btnYes',
  22694. callback:function (all) {
  22695. decision = all ? 'overwrite_all' : 'overwrite';
  22696. decide(decision);
  22697. if(!overwriteAll && !omitAll) {
  22698. if('overwrite' == decision) {
  22699. unpack(file);
  22700. }
  22701. if((index+1) < cnt) {
  22702. confirm(files, index+1);
  22703. } else {
  22704. dfrd.resolve();
  22705. }
  22706. } else if(overwriteAll) {
  22707. for (i = index; i < cnt; i++) {
  22708. unpack(files[i]);
  22709. }
  22710. dfrd.resolve();
  22711. }
  22712. }
  22713. },
  22714. reject : {
  22715. label : 'btnNo',
  22716. callback:function (all) {
  22717. decision = all ? 'omit_all' : 'omit';
  22718. decide(decision);
  22719. if(!overwriteAll && !omitAll && (index+1) < cnt) {
  22720. confirm(files, index+1);
  22721. } else if (omitAll) {
  22722. dfrd.resolve();
  22723. }
  22724. }
  22725. },
  22726. cancel : {
  22727. label : 'btnCancel',
  22728. callback:function () {
  22729. dfrd.resolve();
  22730. }
  22731. },
  22732. all : ((index+1) < cnt)
  22733. }
  22734. );
  22735. } else if (!makedir) {
  22736. if (mkdirAll == 0) {
  22737. fm.confirm({
  22738. title : fm.i18n('cmdextract'),
  22739. text : [fm.i18n('cmdextract')+' "'+file.name+'"', 'confirmRepl'],
  22740. accept:{
  22741. label : 'btnYes',
  22742. callback:function (all) {
  22743. all && (mkdirAll = 1);
  22744. unpack(file);
  22745. next();
  22746. }
  22747. },
  22748. reject : {
  22749. label : 'btnNo',
  22750. callback:function (all) {
  22751. all && (mkdirAll = -1);
  22752. next();
  22753. }
  22754. },
  22755. cancel : {
  22756. label : 'btnCancel',
  22757. callback:function () {
  22758. dfrd.resolve();
  22759. }
  22760. },
  22761. all : ((index+1) < cnt)
  22762. });
  22763. } else {
  22764. (mkdirAll > 0) && unpack(file);
  22765. next();
  22766. }
  22767. } else {
  22768. unpack(file);
  22769. next();
  22770. }
  22771. };
  22772. if (!(this.enabled() && cnt && mimes.length)) {
  22773. return dfrd.reject();
  22774. }
  22775. if(cnt > 0) {
  22776. confirm(files, 0);
  22777. }
  22778. return dfrd;
  22779. };
  22780. };
  22781. /*
  22782. * File: /js/commands/forward.js
  22783. */
  22784. /**
  22785. * @class elFinder command "forward"
  22786. * Open next visited folder
  22787. *
  22788. * @author Dmitry (dio) Levashov
  22789. **/
  22790. (elFinder.prototype.commands.forward = function() {
  22791. this.alwaysEnabled = true;
  22792. this.updateOnSelect = true;
  22793. this.shortcuts = [{
  22794. pattern : 'ctrl+right'
  22795. }];
  22796. this.getstate = function() {
  22797. return this.fm.history.canForward() ? 0 : -1;
  22798. };
  22799. this.exec = function() {
  22800. return this.fm.history.forward();
  22801. };
  22802. }).prototype = { forceLoad : true }; // this is required command
  22803. /*
  22804. * File: /js/commands/fullscreen.js
  22805. */
  22806. /**
  22807. * @class elFinder command "fullscreen"
  22808. * elFinder node to full scrren mode
  22809. *
  22810. * @author Naoki Sawada
  22811. **/
  22812. elFinder.prototype.commands.fullscreen = function() {
  22813. var self = this,
  22814. fm = this.fm,
  22815. update = function(e, data) {
  22816. e.preventDefault();
  22817. e.stopPropagation();
  22818. if (data && data.fullscreen) {
  22819. self.update(void(0), (data.fullscreen === 'on'));
  22820. }
  22821. };
  22822. this.alwaysEnabled = true;
  22823. this.updateOnSelect = false;
  22824. this.syncTitleOnChange = true;
  22825. this.value = false;
  22826. this.options = {
  22827. ui : 'fullscreenbutton'
  22828. };
  22829. this.getstate = function() {
  22830. return 0;
  22831. };
  22832. this.exec = function() {
  22833. var node = fm.getUI().get(0),
  22834. full = (node === fm.toggleFullscreen(node));
  22835. self.title = fm.i18n(full ? 'reinstate' : 'cmdfullscreen');
  22836. self.update(void(0), full);
  22837. return $.Deferred().resolve();
  22838. };
  22839. fm.bind('init', function() {
  22840. fm.getUI().off('resize.' + fm.namespace, update).on('resize.' + fm.namespace, update);
  22841. });
  22842. };
  22843. /*
  22844. * File: /js/commands/getfile.js
  22845. */
  22846. /**
  22847. * @class elFinder command "getfile".
  22848. * Return selected files info into outer callback.
  22849. * For use elFinder with wysiwyg editors etc.
  22850. *
  22851. * @author Dmitry (dio) Levashov, dio@std42.ru
  22852. **/
  22853. (elFinder.prototype.commands.getfile = function() {
  22854. var self = this,
  22855. fm = this.fm,
  22856. filter = function(files) {
  22857. var o = self.options;
  22858. files = $.grep(files, function(file) {
  22859. return (file.mime != 'directory' || o.folders) && file.read ? true : false;
  22860. });
  22861. return o.multiple || files.length == 1 ? files : [];
  22862. };
  22863. this.alwaysEnabled = true;
  22864. this.callback = fm.options.getFileCallback;
  22865. this._disabled = typeof(this.callback) == 'function';
  22866. this.getstate = function(select) {
  22867. var sel = this.files(select),
  22868. cnt = sel.length;
  22869. return this.callback && cnt && filter(sel).length == cnt ? 0 : -1;
  22870. };
  22871. this.exec = function(hashes) {
  22872. var fm = this.fm,
  22873. opts = this.options,
  22874. files = this.files(hashes),
  22875. cnt = files.length,
  22876. url = fm.option('url'),
  22877. tmb = fm.option('tmbUrl'),
  22878. dfrd = $.Deferred()
  22879. .done(function(data) {
  22880. var res,
  22881. done = function() {
  22882. if (opts.oncomplete == 'close') {
  22883. fm.hide();
  22884. } else if (opts.oncomplete == 'destroy') {
  22885. fm.destroy();
  22886. }
  22887. },
  22888. fail = function(error) {
  22889. if (opts.onerror == 'close') {
  22890. fm.hide();
  22891. } else if (opts.onerror == 'destroy') {
  22892. fm.destroy();
  22893. } else {
  22894. error && fm.error(error);
  22895. }
  22896. };
  22897. fm.trigger('getfile', {files : data});
  22898. try {
  22899. res = self.callback(data, fm);
  22900. } catch(e) {
  22901. fail(['Error in `getFileCallback`.', e.message]);
  22902. return;
  22903. }
  22904. if (typeof res === 'object' && typeof res.done === 'function') {
  22905. res.done(done).fail(fail);
  22906. } else {
  22907. done();
  22908. }
  22909. }),
  22910. result = function(file) {
  22911. return opts.onlyURL
  22912. ? opts.multiple ? $.map(files, function(f) { return f.url; }) : files[0].url
  22913. : opts.multiple ? files : files[0];
  22914. },
  22915. req = [],
  22916. i, file, dim;
  22917. for (i = 0; i < cnt; i++) {
  22918. file = files[i];
  22919. if (file.mime == 'directory' && !opts.folders) {
  22920. return dfrd.reject();
  22921. }
  22922. file.baseUrl = url;
  22923. if (file.url == '1') {
  22924. req.push(fm.request({
  22925. data : {cmd : 'url', target : file.hash},
  22926. notify : {type : 'url', cnt : 1, hideCnt : true},
  22927. preventDefault : true
  22928. })
  22929. .done(function(data) {
  22930. if (data.url) {
  22931. var rfile = fm.file(this.hash);
  22932. rfile.url = this.url = data.url;
  22933. }
  22934. }.bind(file)));
  22935. } else {
  22936. file.url = fm.url(file.hash);
  22937. }
  22938. if (! opts.onlyURL) {
  22939. if (opts.getPath) {
  22940. file.path = fm.path(file.hash);
  22941. if (file.path === '' && file.phash) {
  22942. // get parents
  22943. (function() {
  22944. var dfd = $.Deferred();
  22945. req.push(dfd);
  22946. fm.path(file.hash, false, {})
  22947. .done(function(path) {
  22948. file.path = path;
  22949. })
  22950. .fail(function() {
  22951. file.path = '';
  22952. })
  22953. .always(function() {
  22954. dfd.resolve();
  22955. });
  22956. })();
  22957. }
  22958. }
  22959. if (file.tmb && file.tmb != 1) {
  22960. file.tmb = tmb + file.tmb;
  22961. }
  22962. if (!file.width && !file.height) {
  22963. if (file.dim) {
  22964. dim = file.dim.split('x');
  22965. file.width = dim[0];
  22966. file.height = dim[1];
  22967. } else if (opts.getImgSize && file.mime.indexOf('image') !== -1) {
  22968. req.push(fm.request({
  22969. data : {cmd : 'dim', target : file.hash},
  22970. notify : {type : 'dim', cnt : 1, hideCnt : true},
  22971. preventDefault : true
  22972. })
  22973. .done(function(data) {
  22974. if (data.dim) {
  22975. var dim = data.dim.split('x');
  22976. var rfile = fm.file(this.hash);
  22977. rfile.width = this.width = dim[0];
  22978. rfile.height = this.height = dim[1];
  22979. }
  22980. }.bind(file)));
  22981. }
  22982. }
  22983. }
  22984. }
  22985. if (req.length) {
  22986. $.when.apply(null, req).always(function() {
  22987. dfrd.resolve(result(files));
  22988. });
  22989. return dfrd;
  22990. }
  22991. return dfrd.resolve(result(files));
  22992. };
  22993. }).prototype = { forceLoad : true }; // this is required command
  22994. /*
  22995. * File: /js/commands/help.js
  22996. */
  22997. /**
  22998. * @class elFinder command "help"
  22999. * "About" dialog
  23000. *
  23001. * @author Dmitry (dio) Levashov
  23002. **/
  23003. (elFinder.prototype.commands.help = function() {
  23004. var fm = this.fm,
  23005. self = this,
  23006. linktpl = '<div class="elfinder-help-link"> <a href="{url}">{link}</a></div>',
  23007. linktpltgt = '<div class="elfinder-help-link"> <a href="{url}" target="_blank">{link}</a></div>',
  23008. atpl = '<div class="elfinder-help-team"><div>{author}</div>{work}</div>',
  23009. url = /\{url\}/,
  23010. link = /\{link\}/,
  23011. author = /\{author\}/,
  23012. work = /\{work\}/,
  23013. r = 'replace',
  23014. prim = 'ui-priority-primary',
  23015. sec = 'ui-priority-secondary',
  23016. lic = 'elfinder-help-license',
  23017. tab = '<li class="' + fm.res('class', 'tabstab') + ' elfinder-help-tab-{id}"><a href="#'+fm.namespace+'-help-{id}" class="ui-tabs-anchor">{title}</a></li>',
  23018. html = ['<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-help">',
  23019. '<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-top">'],
  23020. stpl = '<div class="elfinder-help-shortcut"><div class="elfinder-help-shortcut-pattern">{pattern}</div> {descrip}</div>',
  23021. sep = '<div class="elfinder-help-separator"/>',
  23022. selfUrl = $('base').length? document.location.href.replace(/#.*$/, '') : '',
  23023. clTabActive = fm.res('class', 'tabsactive'),
  23024. getTheme = function() {
  23025. var src;
  23026. if (fm.theme && fm.theme.author) {
  23027. 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)+')');
  23028. } else {
  23029. src = '<div class="elfinder-help-team elfinder-help-term-theme" style="display:none"></div>';
  23030. }
  23031. return src;
  23032. },
  23033. about = function() {
  23034. html.push('<div id="'+fm.namespace+'-help-about" class="ui-tabs-panel ui-widget-content ui-corner-bottom"><div class="elfinder-help-logo"/>');
  23035. html.push('<h3>elFinder</h3>');
  23036. html.push('<div class="'+prim+'">'+fm.i18n('webfm')+'</div>');
  23037. html.push('<div class="'+sec+'">'+fm.i18n('ver')+': '+fm.version+'</div>');
  23038. html.push('<div class="'+sec+'">'+fm.i18n('protocolver')+': <span class="apiver"></span></div>');
  23039. html.push('<div class="'+sec+'">jQuery/jQuery UI: '+$().jquery+'/'+$.ui.version+'</div>');
  23040. html.push(sep);
  23041. html.push(linktpltgt[r](url, 'https://studio-42.github.io/elFinder/')[r](link, fm.i18n('homepage')));
  23042. html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder/wiki')[r](link, fm.i18n('docs')));
  23043. html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder')[r](link, fm.i18n('github')));
  23044. //html.push(linktpltgt[r](url, 'http://twitter.com/elrte_elfinder')[r](link, fm.i18n('twitter')));
  23045. html.push(sep);
  23046. html.push('<div class="'+prim+'">'+fm.i18n('team')+'</div>');
  23047. html.push(atpl[r](author, 'Dmitry "dio" Levashov &lt;dio@std42.ru&gt;')[r](work, fm.i18n('chiefdev')));
  23048. html.push(atpl[r](author, 'Naoki Sawada &lt;hypweb+elfinder@gmail.com&gt;')[r](work, fm.i18n('developer')));
  23049. html.push(atpl[r](author, 'Troex Nevelin &lt;troex@fury.scancode.ru&gt;')[r](work, fm.i18n('maintainer')));
  23050. html.push(atpl[r](author, 'Alexey Sukhotin &lt;strogg@yandex.ru&gt;')[r](work, fm.i18n('contributor')));
  23051. if (fm.i18[fm.lang].translator) {
  23052. $.each(fm.i18[fm.lang].translator.split(', '), function() {
  23053. html.push(atpl[r](author, $.trim(this))[r](work, fm.i18n('translator')+' ('+fm.i18[fm.lang].language+')'));
  23054. });
  23055. }
  23056. html.push(getTheme());
  23057. html.push(sep);
  23058. 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>');
  23059. html.push(sep);
  23060. html.push('<div class="'+lic+'">Licence: 3-clauses BSD Licence</div>');
  23061. html.push('<div class="'+lic+'">Copyright © 2009-2019, Studio 42</div>');
  23062. html.push('<div class="'+lic+'">„ …'+fm.i18n('dontforget')+' ”</div>');
  23063. html.push('</div>');
  23064. },
  23065. shortcuts = function() {
  23066. var sh = fm.shortcuts();
  23067. // shortcuts tab
  23068. html.push('<div id="'+fm.namespace+'-help-shortcuts" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  23069. if (sh.length) {
  23070. html.push('<div class="ui-widget-content elfinder-help-shortcuts">');
  23071. $.each(sh, function(i, s) {
  23072. html.push(stpl.replace(/\{pattern\}/, s[0]).replace(/\{descrip\}/, s[1]));
  23073. });
  23074. html.push('</div>');
  23075. } else {
  23076. html.push('<div class="elfinder-help-disabled">'+fm.i18n('shortcutsof')+'</div>');
  23077. }
  23078. html.push('</div>');
  23079. },
  23080. help = function() {
  23081. // help tab
  23082. html.push('<div id="'+fm.namespace+'-help-help" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  23083. html.push('<a href="https://github.com/Studio-42/elFinder/wiki" target="_blank" class="elfinder-dont-panic"><span>DON\'T PANIC</span></a>');
  23084. html.push('</div>');
  23085. // end help
  23086. },
  23087. useInteg = false,
  23088. integrations = function() {
  23089. useInteg = true;
  23090. html.push('<div id="'+fm.namespace+'-help-integrations" class="ui-tabs-panel ui-widget-content ui-corner-bottom"/>');
  23091. },
  23092. useDebug = false,
  23093. debug = function() {
  23094. useDebug = true;
  23095. // debug tab
  23096. html.push('<div id="'+fm.namespace+'-help-debug" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  23097. html.push('<div class="ui-widget-content elfinder-help-debug"><ul></ul></div>');
  23098. html.push('</div>');
  23099. // end debug
  23100. },
  23101. debugRender = function() {
  23102. var render = function(elm, obj) {
  23103. $.each(obj, function(k, v) {
  23104. elm.append($('<dt/>').text(k));
  23105. if (typeof v === 'undefined') {
  23106. elm.append($('<dd/>').append($('<span/>').text('undfined')));
  23107. } else if (typeof v === 'object' && !v) {
  23108. elm.append($('<dd/>').append($('<span/>').text('null')));
  23109. } else if (typeof v === 'object' && ($.isPlainObject(v) || v.length)) {
  23110. elm.append( $('<dd/>').append(render($('<dl/>'), v)));
  23111. } else {
  23112. elm.append($('<dd/>').append($('<span/>').text((v && typeof v === 'object')? '[]' : (v? v : '""'))));
  23113. }
  23114. });
  23115. return elm;
  23116. },
  23117. cnt = debugUL.children('li').length,
  23118. targetL, target, tabId,
  23119. info, lastUL, lastDIV;
  23120. if (self.debug.options || self.debug.debug) {
  23121. if (cnt >= 5) {
  23122. lastUL = debugUL.children('li:last');
  23123. lastDIV = debugDIV.children('div:last');
  23124. if (lastDIV.is(':hidden')) {
  23125. lastUL.remove();
  23126. lastDIV.remove();
  23127. } else {
  23128. lastUL.prev().remove();
  23129. lastDIV.prev().remove();
  23130. }
  23131. }
  23132. tabId = fm.namespace + '-help-debug-' + (+new Date());
  23133. targetL = $('<li/>').html('<a href="'+selfUrl+'#'+tabId+'">'+self.debug.debug.cmd+'</a>').prependTo(debugUL);
  23134. target = $('<div id="'+tabId+'"/>').data('debug', self.debug);
  23135. targetL.on('click.debugrender', function() {
  23136. var debug = target.data('debug');
  23137. target.removeData('debug');
  23138. if (debug) {
  23139. target.hide();
  23140. if (debug.debug) {
  23141. info = $('<fieldset>').append($('<legend/>').text('debug'), render($('<dl/>'), debug.debug));
  23142. target.append(info);
  23143. }
  23144. if (debug.options) {
  23145. info = $('<fieldset>').append($('<legend/>').text('options'), render($('<dl/>'), debug.options));
  23146. target.append(info);
  23147. }
  23148. target.show();
  23149. }
  23150. targetL.off('click.debugrender');
  23151. });
  23152. debugUL.after(target);
  23153. opened && debugDIV.tabs('refresh');
  23154. }
  23155. },
  23156. content = '',
  23157. opened, tabInteg, integDIV, tabDebug, debugDIV, debugUL;
  23158. this.alwaysEnabled = true;
  23159. this.updateOnSelect = false;
  23160. this.state = -1;
  23161. this.shortcuts = [{
  23162. pattern : 'f1',
  23163. description : this.title
  23164. }];
  23165. fm.bind('load', function() {
  23166. var parts = self.options.view || ['about', 'shortcuts', 'help', 'integrations', 'debug'],
  23167. i, helpSource, tabBase, tabNav, tabs, delta;
  23168. // remove 'preference' tab, it moved to command 'preference'
  23169. if ((i = $.inArray('preference', parts)) !== -1) {
  23170. parts.splice(i, 1);
  23171. }
  23172. // debug tab require jQueryUI Tabs Widget
  23173. if (! $.fn.tabs) {
  23174. if ((i = $.inArray(parts, 'debug')) !== -1) {
  23175. parts.splice(i, 1);
  23176. }
  23177. }
  23178. $.each(parts, function(i, title) {
  23179. html.push(tab[r](/\{id\}/g, title)[r](/\{title\}/, fm.i18n(title)));
  23180. });
  23181. html.push('</ul>');
  23182. $.inArray('about', parts) !== -1 && about();
  23183. $.inArray('shortcuts', parts) !== -1 && shortcuts();
  23184. if ($.inArray('help', parts) !== -1) {
  23185. helpSource = fm.i18nBaseUrl + 'help/%s.html.js';
  23186. help();
  23187. }
  23188. $.inArray('integrations', parts) !== -1 && integrations();
  23189. $.inArray('debug', parts) !== -1 && debug();
  23190. html.push('</div>');
  23191. content = $(html.join(''));
  23192. content.find('.ui-tabs-nav li')
  23193. .on('mouseenter mouseleave', function(e) {
  23194. $(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
  23195. })
  23196. .on('focus blur', 'a', function(e) {
  23197. $(e.delegateTarget).toggleClass('ui-state-focus', e.type === 'focusin');
  23198. })
  23199. .children()
  23200. .on('click', function(e) {
  23201. var link = $(this);
  23202. e.preventDefault();
  23203. e.stopPropagation();
  23204. link.parent().addClass(clTabActive).siblings().removeClass(clTabActive);
  23205. content.children('.ui-tabs-panel').hide().filter(link.attr('href')).show();
  23206. })
  23207. .filter(':first').trigger('click');
  23208. if (useInteg) {
  23209. tabInteg = content.find('.elfinder-help-tab-integrations').hide();
  23210. integDIV = content.find('#'+fm.namespace+'-help-integrations').hide().append($('<div class="elfinder-help-integrations-desc"/>').html(fm.i18n('integrationWith')));
  23211. fm.bind('helpIntegration', function(e) {
  23212. var ul = integDIV.children('ul:first'),
  23213. data, elm, cmdUL, cmdCls;
  23214. if (e.data) {
  23215. if ($.isPlainObject(e.data)) {
  23216. data = Object.assign({
  23217. link: '',
  23218. title: '',
  23219. banner: ''
  23220. }, e.data);
  23221. if (data.title || data.link) {
  23222. if (!data.title) {
  23223. data.title = data.link;
  23224. }
  23225. if (data.link) {
  23226. elm = $('<a/>').attr('href', data.link).attr('target', '_blank').text(data.title);
  23227. } else {
  23228. elm = $('<span/>').text(data.title);
  23229. }
  23230. if (data.banner) {
  23231. elm = $('<span/>').append($('<img/>').attr(data.banner), elm);
  23232. }
  23233. }
  23234. } else {
  23235. elm = $(e.data);
  23236. elm.filter('a').each(function() {
  23237. var tgt = $(this);
  23238. if (!tgt.attr('target')) {
  23239. tgt.attr('target', '_blank');;
  23240. }
  23241. });
  23242. }
  23243. if (elm) {
  23244. tabInteg.show();
  23245. if (!ul.length) {
  23246. ul = $('<ul class="elfinder-help-integrations"/>').appendTo(integDIV);
  23247. }
  23248. if (data && data.cmd) {
  23249. cmdCls = 'elfinder-help-integration-' + data.cmd;
  23250. cmdUL = ul.find('ul.' + cmdCls);
  23251. if (!cmdUL.length) {
  23252. cmdUL = $('<ul class="'+cmdCls+'"/>');
  23253. ul.append($('<li/>').append($('<span/>').html(fm.i18n('cmd'+data.cmd))).append(cmdUL));
  23254. }
  23255. elm = cmdUL.append($('<li/>').append(elm));
  23256. } else {
  23257. ul.append($('<li/>').append(elm));
  23258. }
  23259. }
  23260. }
  23261. }).bind('themechange', function() {
  23262. content.find('div.elfinder-help-term-theme').replaceWith(getTheme());
  23263. });
  23264. }
  23265. // debug
  23266. if (useDebug) {
  23267. tabDebug = content.find('.elfinder-help-tab-debug').hide();
  23268. debugDIV = content.find('#'+fm.namespace+'-help-debug').children('div:first');
  23269. debugUL = debugDIV.children('ul:first').on('click', function(e) {
  23270. e.preventDefault();
  23271. e.stopPropagation();
  23272. });
  23273. self.debug = {};
  23274. fm.bind('backenddebug', function(e) {
  23275. // CAUTION: DO NOT TOUCH `e.data`
  23276. if (useDebug && e.data && e.data.debug) {
  23277. self.debug = { options : e.data.options, debug : Object.assign({ cmd : fm.currentReqCmd }, e.data.debug) };
  23278. if (self.dialog) {
  23279. debugRender();
  23280. }
  23281. }
  23282. });
  23283. }
  23284. content.find('#'+fm.namespace+'-help-about').find('.apiver').text(fm.api);
  23285. self.dialog = self.fmDialog(content, {
  23286. title : self.title,
  23287. width : 530,
  23288. maxWidth: 'window',
  23289. maxHeight: 'window',
  23290. autoOpen : false,
  23291. destroyOnClose : false,
  23292. close : function() {
  23293. if (useDebug) {
  23294. tabDebug.hide();
  23295. debugDIV.tabs('destroy');
  23296. }
  23297. opened = false;
  23298. }
  23299. })
  23300. .on('click', function(e) {
  23301. e.stopPropagation();
  23302. })
  23303. .css({
  23304. overflow: 'hidden'
  23305. });
  23306. tabBase = self.dialog.children('.ui-tabs');
  23307. tabNav = tabBase.children('.ui-tabs-nav:first');
  23308. tabs = tabBase.children('.ui-tabs-panel');
  23309. delta = self.dialog.outerHeight(true) - self.dialog.height();
  23310. self.dialog.closest('.ui-dialog').on('resize', function() {
  23311. tabs.height(self.dialog.height() - delta - tabNav.outerHeight(true) - 20);
  23312. });
  23313. if (helpSource) {
  23314. self.dialog.one('initContents', function() {
  23315. $.ajax({
  23316. url: self.options.helpSource? self.options.helpSource : helpSource.replace('%s', fm.lang),
  23317. dataType: 'html'
  23318. }).done(function(source) {
  23319. $('#'+fm.namespace+'-help-help').html(source);
  23320. }).fail(function() {
  23321. $.ajax({
  23322. url: helpSource.replace('%s', 'en'),
  23323. dataType: 'html'
  23324. }).done(function(source) {
  23325. $('#'+fm.namespace+'-help-help').html(source);
  23326. });
  23327. });
  23328. });
  23329. }
  23330. self.state = 0;
  23331. fm.trigger('helpBuilded', self.dialog);
  23332. }).one('open', function() {
  23333. var debug = false;
  23334. fm.one('backenddebug', function() {
  23335. debug =true;
  23336. }).one('opendone', function() {
  23337. requestAnimationFrame(function() {
  23338. if (! debug && useDebug) {
  23339. useDebug = false;
  23340. tabDebug.hide();
  23341. debugDIV.hide();
  23342. debugUL.hide();
  23343. }
  23344. });
  23345. });
  23346. });
  23347. this.getstate = function() {
  23348. return 0;
  23349. };
  23350. this.exec = function(sel, opts) {
  23351. var tab = opts? opts.tab : void(0),
  23352. debugShow = function() {
  23353. if (useDebug) {
  23354. debugDIV.tabs();
  23355. debugUL.find('a:first').trigger('click');
  23356. tabDebug.show();
  23357. opened = true;
  23358. }
  23359. };
  23360. debugShow();
  23361. this.dialog.trigger('initContents').elfinderdialog('open').find((tab? '.elfinder-help-tab-'+tab : '.ui-tabs-nav li') + ' a:first').trigger('click');
  23362. return $.Deferred().resolve();
  23363. };
  23364. }).prototype = { forceLoad : true }; // this is required command
  23365. /*
  23366. * File: /js/commands/hidden.js
  23367. */
  23368. /**
  23369. * @class elFinder command "hidden"
  23370. * Always hidden command for uiCmdMap
  23371. *
  23372. * @author Naoki Sawada
  23373. **/
  23374. elFinder.prototype.commands.hidden = function() {
  23375. this.hidden = true;
  23376. this.updateOnSelect = false;
  23377. this.getstate = function() {
  23378. return -1;
  23379. };
  23380. };
  23381. /*
  23382. * File: /js/commands/hide.js
  23383. */
  23384. /**
  23385. * @class elFinder command "hide".
  23386. * folders/files to hide as personal setting.
  23387. *
  23388. * @type elFinder.command
  23389. * @author Naoki Sawada
  23390. */
  23391. elFinder.prototype.commands.hide = function() {
  23392. var self = this,
  23393. nameCache = {},
  23394. hideData, hideCnt, cMenuType, sOrigin;
  23395. this.syncTitleOnChange = true;
  23396. this.shortcuts = [{
  23397. pattern : 'ctrl+shift+dot',
  23398. description : this.fm.i18n('toggleHidden')
  23399. }];
  23400. this.init = function() {
  23401. var fm = this.fm;
  23402. hideData = fm.storage('hide') || {items: {}};
  23403. hideCnt = Object.keys(hideData.items).length;
  23404. this.title = fm.i18n(hideData.show? 'hideHidden' : 'showHidden');
  23405. self.update(void(0), self.title);
  23406. };
  23407. this.fm.bind('select contextmenucreate closecontextmenu', function(e, fm) {
  23408. var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected();
  23409. if (e.type === 'select' && e.data) {
  23410. sOrigin = e.data.origin;
  23411. } else if (e.type === 'contextmenucreate') {
  23412. cMenuType = e.data.type;
  23413. }
  23414. if (!sel.length || (((e.type !== 'contextmenucreate' && sOrigin !== 'navbar') || cMenuType === 'cwd') && sel[0] === fm.cwd().hash)) {
  23415. self.title = fm.i18n(hideData.show? 'hideHidden' : 'showHidden');
  23416. } else {
  23417. self.title = fm.i18n('cmdhide');
  23418. }
  23419. if (e.type !== 'closecontextmenu') {
  23420. self.update(cMenuType === 'cwd'? (hideCnt? 0 : -1) : void(0), self.title);
  23421. } else {
  23422. cMenuType = '';
  23423. requestAnimationFrame(function() {
  23424. self.update(void(0), self.title);
  23425. });
  23426. }
  23427. });
  23428. this.getstate = function(sel) {
  23429. return (cMenuType !== 'cwd' && (sel || this.fm.selected()).length) || hideCnt? 0 : -1;
  23430. };
  23431. this.exec = function(hashes, opts) {
  23432. var fm = this.fm,
  23433. dfrd = $.Deferred()
  23434. .done(function() {
  23435. fm.trigger('hide', {items: items, opts: opts});
  23436. })
  23437. .fail(function(error) {
  23438. fm.error(error);
  23439. }),
  23440. o = opts || {},
  23441. items = o.targets? o.targets : (hashes || fm.selected()),
  23442. added = [],
  23443. removed = [],
  23444. notifyto, files, res;
  23445. hideData = fm.storage('hide') || {};
  23446. if (!$.isPlainObject(hideData)) {
  23447. hideData = {};
  23448. }
  23449. if (!$.isPlainObject(hideData.items)) {
  23450. hideData.items = {};
  23451. }
  23452. if (opts._currentType === 'shortcut' || !items.length || (opts._currentType !== 'navbar' && sOrigin !=='navbar' && items[0] === fm.cwd().hash)) {
  23453. if (hideData.show) {
  23454. o.hide = true;
  23455. } else if (Object.keys(hideData.items).length) {
  23456. o.show = true;
  23457. }
  23458. }
  23459. if (o.reset) {
  23460. o.show = true;
  23461. hideCnt = 0;
  23462. }
  23463. if (o.show || o.hide) {
  23464. if (o.show) {
  23465. hideData.show = true;
  23466. } else {
  23467. delete hideData.show;
  23468. }
  23469. if (o.show) {
  23470. fm.storage('hide', o.reset? null : hideData);
  23471. self.title = fm.i18n('hideHidden');
  23472. self.update(o.reset? -1 : void(0), self.title);
  23473. $.each(hideData.items, function(h) {
  23474. var f = fm.file(h, true);
  23475. if (f && (fm.searchStatus.state || !f.phash || fm.file(f.phash))) {
  23476. added.push(f);
  23477. }
  23478. });
  23479. if (added.length) {
  23480. fm.updateCache({added: added});
  23481. fm.add({added: added});
  23482. }
  23483. if (o.reset) {
  23484. hideData = {items: {}};
  23485. }
  23486. return dfrd.resolve();
  23487. }
  23488. items = Object.keys(hideData.items);
  23489. }
  23490. if (items.length) {
  23491. $.each(items, function(i, h) {
  23492. var f;
  23493. if (!hideData.items[h]) {
  23494. f = fm.file(h);
  23495. if (f) {
  23496. nameCache[h] = f.i18 || f.name;
  23497. }
  23498. hideData.items[h] = nameCache[h]? nameCache[h] : h;
  23499. }
  23500. });
  23501. hideCnt = Object.keys(hideData.items).length;
  23502. files = this.files(items);
  23503. fm.storage('hide', hideData);
  23504. fm.remove({removed: items});
  23505. if (hideData.show) {
  23506. this.exec(void(0), {hide: true});
  23507. }
  23508. if (!o.hide) {
  23509. res = {};
  23510. res.undo = {
  23511. cmd : 'hide',
  23512. callback : function() {
  23513. var nData = fm.storage('hide');
  23514. if (nData) {
  23515. $.each(items, function(i, h) {
  23516. delete nData.items[h];
  23517. });
  23518. hideCnt = Object.keys(nData.items).length;
  23519. fm.storage('hide', nData);
  23520. fm.trigger('hide', {items: items, opts: {}});
  23521. self.update(hideCnt? 0 : -1);
  23522. }
  23523. fm.updateCache({added: files});
  23524. fm.add({added: files});
  23525. }
  23526. };
  23527. res.redo = {
  23528. cmd : 'hide',
  23529. callback : function() {
  23530. return fm.exec('hide', void(0), {targets: items});
  23531. }
  23532. };
  23533. }
  23534. }
  23535. return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(res);
  23536. };
  23537. };
  23538. /*
  23539. * File: /js/commands/home.js
  23540. */
  23541. (elFinder.prototype.commands.home = function() {
  23542. this.title = 'Home';
  23543. this.alwaysEnabled = true;
  23544. this.updateOnSelect = false;
  23545. this.shortcuts = [{
  23546. pattern : 'ctrl+home ctrl+shift+up',
  23547. description : 'Home'
  23548. }];
  23549. this.getstate = function() {
  23550. var root = this.fm.root(),
  23551. cwd = this.fm.cwd().hash;
  23552. return root && cwd && root != cwd ? 0: -1;
  23553. };
  23554. this.exec = function() {
  23555. return this.fm.exec('open', this.fm.root());
  23556. };
  23557. }).prototype = { forceLoad : true }; // this is required command
  23558. /*
  23559. * File: /js/commands/info.js
  23560. */
  23561. /**
  23562. * @class elFinder command "info".
  23563. * Display dialog with file properties.
  23564. *
  23565. * @author Dmitry (dio) Levashov, dio@std42.ru
  23566. **/
  23567. (elFinder.prototype.commands.info = function() {
  23568. var m = 'msg',
  23569. fm = this.fm,
  23570. spclass = 'elfinder-spinner',
  23571. btnclass = 'elfinder-info-button',
  23572. msg = {
  23573. calc : fm.i18n('calc'),
  23574. size : fm.i18n('size'),
  23575. unknown : fm.i18n('unknown'),
  23576. path : fm.i18n('path'),
  23577. aliasfor : fm.i18n('aliasfor'),
  23578. modify : fm.i18n('modify'),
  23579. perms : fm.i18n('perms'),
  23580. locked : fm.i18n('locked'),
  23581. dim : fm.i18n('dim'),
  23582. kind : fm.i18n('kind'),
  23583. files : fm.i18n('files'),
  23584. folders : fm.i18n('folders'),
  23585. roots : fm.i18n('volumeRoots'),
  23586. items : fm.i18n('items'),
  23587. yes : fm.i18n('yes'),
  23588. no : fm.i18n('no'),
  23589. link : fm.i18n('link'),
  23590. owner : fm.i18n('owner'),
  23591. group : fm.i18n('group'),
  23592. perm : fm.i18n('perm'),
  23593. getlink : fm.i18n('getLink')
  23594. },
  23595. applyZWSP = function(str, remove) {
  23596. if (remove) {
  23597. return str.replace(/\u200B/g, '');
  23598. } else {
  23599. return str.replace(/(\/|\\)/g, "$1\u200B");
  23600. }
  23601. };
  23602. this.items = ['size', 'aliasfor', 'path', 'link', 'dim', 'modify', 'perms', 'locked', 'owner', 'group', 'perm'];
  23603. if (this.options.custom && Object.keys(this.options.custom).length) {
  23604. $.each(this.options.custom, function(name, details) {
  23605. details.label && this.items.push(details.label);
  23606. });
  23607. }
  23608. this.tpl = {
  23609. main : '<div class="ui-helper-clearfix elfinder-info-title {dirclass}"><span class="elfinder-cwd-icon {class} ui-corner-all"{style}/>{title}</div><table class="elfinder-info-tb">{content}</table>',
  23610. itemTitle : '<strong>{name}</strong><span class="elfinder-info-kind">{kind}</span>',
  23611. groupTitle : '<strong>{items}: {num}</strong>',
  23612. row : '<tr><td class="elfinder-info-label">{label} : </td><td class="{class}">{value}</td></tr>',
  23613. spinner : '<span>{text}</span> <span class="'+spclass+' '+spclass+'-{name}"/>'
  23614. };
  23615. this.alwaysEnabled = true;
  23616. this.updateOnSelect = false;
  23617. this.shortcuts = [{
  23618. pattern : 'ctrl+i'
  23619. }];
  23620. this.init = function() {
  23621. $.each(msg, function(k, v) {
  23622. msg[k] = fm.i18n(v);
  23623. });
  23624. };
  23625. this.getstate = function() {
  23626. return 0;
  23627. };
  23628. this.exec = function(hashes) {
  23629. var files = this.files(hashes);
  23630. if (! files.length) {
  23631. files = this.files([ this.fm.cwd().hash ]);
  23632. }
  23633. var self = this,
  23634. fm = this.fm,
  23635. o = this.options,
  23636. tpl = this.tpl,
  23637. row = tpl.row,
  23638. cnt = files.length,
  23639. content = [],
  23640. view = tpl.main,
  23641. l = '{label}',
  23642. v = '{value}',
  23643. reqs = [],
  23644. reqDfrd = null,
  23645. opts = {
  23646. title : fm.i18n('selectionInfo'),
  23647. width : 'auto',
  23648. close : function() {
  23649. $(this).elfinderdialog('destroy');
  23650. if (reqDfrd && reqDfrd.state() === 'pending') {
  23651. reqDfrd.reject();
  23652. }
  23653. $.grep(reqs, function(r) {
  23654. r && r.state() === 'pending' && r.reject();
  23655. });
  23656. }
  23657. },
  23658. count = [],
  23659. replSpinner = function(msg, name, className) {
  23660. dialog.find('.'+spclass+'-'+name).parent().html(msg).addClass(className || '');
  23661. },
  23662. id = fm.namespace+'-info-'+$.map(files, function(f) { return f.hash; }).join('-'),
  23663. dialog = fm.getUI().find('#'+id),
  23664. customActions = [],
  23665. style = '',
  23666. hashClass = 'elfinder-font-mono elfinder-info-hash',
  23667. size, tmb, file, title, dcnt, rdcnt, path, getHashAlgorisms, hideItems;
  23668. if (!cnt) {
  23669. return $.Deferred().reject();
  23670. }
  23671. if (dialog.length) {
  23672. dialog.elfinderdialog('toTop');
  23673. return $.Deferred().resolve();
  23674. }
  23675. hideItems = fm.storage('infohides') || fm.arrayFlip(o.hideItems, true);
  23676. if (cnt === 1) {
  23677. file = files[0];
  23678. if (file.icon) {
  23679. style = ' '+fm.getIconStyle(file);
  23680. }
  23681. view = view.replace('{dirclass}', file.csscls? fm.escape(file.csscls) : '').replace('{class}', fm.mime2class(file.mime)).replace('{style}', style);
  23682. title = tpl.itemTitle.replace('{name}', fm.escape(file.i18 || file.name)).replace('{kind}', '<span title="'+fm.escape(file.mime)+'">'+fm.mime2kind(file)+'</span>');
  23683. tmb = fm.tmb(file);
  23684. if (!file.read) {
  23685. size = msg.unknown;
  23686. } else if (file.mime != 'directory' || file.alias) {
  23687. size = fm.formatSize(file.size);
  23688. } else {
  23689. size = tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size');
  23690. count.push(file.hash);
  23691. }
  23692. !hideItems.size && content.push(row.replace(l, msg.size).replace(v, size));
  23693. !hideItems.aleasfor && file.alias && content.push(row.replace(l, msg.aliasfor).replace(v, file.alias));
  23694. if (!hideItems.path) {
  23695. if (path = fm.path(file.hash, true)) {
  23696. content.push(row.replace(l, msg.path).replace(v, applyZWSP(fm.escape(path))).replace('{class}', 'elfinder-info-path'));
  23697. } else {
  23698. content.push(row.replace(l, msg.path).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'path')).replace('{class}', 'elfinder-info-path'));
  23699. reqs.push(fm.path(file.hash, true, {notify: null})
  23700. .fail(function() {
  23701. replSpinner(msg.unknown, 'path');
  23702. })
  23703. .done(function(path) {
  23704. replSpinner(applyZWSP(path), 'path');
  23705. }));
  23706. }
  23707. }
  23708. if (!hideItems.link && file.read) {
  23709. var href,
  23710. name_esc = fm.escape(file.name);
  23711. if (file.url == '1') {
  23712. content.push(row.replace(l, msg.link).replace(v, '<button class="'+btnclass+' '+spclass+'-url">'+msg.getlink+'</button>'));
  23713. } else {
  23714. if (file.url) {
  23715. href = file.url;
  23716. } else if (file.mime === 'directory') {
  23717. if (o.nullUrlDirLinkSelf && file.url === null) {
  23718. var loc = window.location;
  23719. href = loc.pathname + loc.search + '#elf_' + file.hash;
  23720. } else if (file.url !== '' && fm.option('url', (!fm.isRoot(file) && file.phash) || file.hash)) {
  23721. href = fm.url(file.hash);
  23722. }
  23723. } else {
  23724. href = fm.url(file.hash);
  23725. }
  23726. href && content.push(row.replace(l, msg.link).replace(v, '<a href="'+href+'" target="_blank">'+name_esc+'</a>'));
  23727. }
  23728. }
  23729. if (!hideItems.dim) {
  23730. if (file.dim) { // old api
  23731. content.push(row.replace(l, msg.dim).replace(v, file.dim));
  23732. } else if (file.mime.indexOf('image') !== -1) {
  23733. if (file.width && file.height) {
  23734. content.push(row.replace(l, msg.dim).replace(v, file.width+'x'+file.height));
  23735. } else {
  23736. content.push(row.replace(l, msg.dim).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'dim')));
  23737. reqs.push(fm.request({
  23738. data : {cmd : 'dim', target : file.hash},
  23739. preventDefault : true
  23740. })
  23741. .fail(function() {
  23742. replSpinner(msg.unknown, 'dim');
  23743. })
  23744. .done(function(data) {
  23745. replSpinner(data.dim || msg.unknown, 'dim');
  23746. if (data.dim) {
  23747. var dim = data.dim.split('x');
  23748. var rfile = fm.file(file.hash);
  23749. rfile.width = dim[0];
  23750. rfile.height = dim[1];
  23751. }
  23752. }));
  23753. }
  23754. }
  23755. }
  23756. !hideItems.modify && content.push(row.replace(l, msg.modify).replace(v, fm.formatDate(file)));
  23757. !hideItems.perms && content.push(row.replace(l, msg.perms).replace(v, fm.formatPermissions(file)));
  23758. !hideItems.locked && content.push(row.replace(l, msg.locked).replace(v, file.locked ? msg.yes : msg.no));
  23759. !hideItems.owner && file.owner && content.push(row.replace(l, msg.owner).replace(v, file.owner));
  23760. !hideItems.group && file.group && content.push(row.replace(l, msg.group).replace(v, file.group));
  23761. !hideItems.perm && file.perm && content.push(row.replace(l, msg.perm).replace(v, fm.formatFileMode(file.perm)));
  23762. // Get MD5 hash
  23763. if (window.ArrayBuffer && (fm.options.cdns.sparkmd5 || fm.options.cdns.jssha) && file.mime !== 'directory' && file.size > 0 && (!o.showHashMaxsize || file.size <= o.showHashMaxsize)) {
  23764. getHashAlgorisms = [];
  23765. $.each(fm.storage('hashchekcer') || o.showHashAlgorisms, function(i, n) {
  23766. if (!file[n]) {
  23767. content.push(row.replace(l, fm.i18n(n)).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', n)));
  23768. getHashAlgorisms.push(n);
  23769. } else {
  23770. content.push(row.replace(l, fm.i18n(n)).replace(v, file[n]).replace('{class}', hashClass));
  23771. }
  23772. });
  23773. reqs.push(
  23774. fm.getContentsHashes(file.hash, getHashAlgorisms).progress(function(hashes) {
  23775. $.each(getHashAlgorisms, function(i, n) {
  23776. if (hashes[n]) {
  23777. replSpinner(hashes[n], n, hashClass);
  23778. }
  23779. });
  23780. }).always(function() {
  23781. $.each(getHashAlgorisms, function(i, n) {
  23782. replSpinner(msg.unknown, n);
  23783. });
  23784. })
  23785. );
  23786. }
  23787. // Add custom info fields
  23788. if (o.custom) {
  23789. $.each(o.custom, function(name, details) {
  23790. if (
  23791. !hideItems[details.label]
  23792. &&
  23793. (!details.mimes || $.grep(details.mimes, function(m){return (file.mime === m || file.mime.indexOf(m+'/') === 0)? true : false;}).length)
  23794. &&
  23795. (!details.hashRegex || file.hash.match(details.hashRegex))
  23796. ) {
  23797. // Add to the content
  23798. content.push(row.replace(l, fm.i18n(details.label)).replace(v , details.tpl.replace('{id}', id)));
  23799. // Register the action
  23800. if (details.action && (typeof details.action == 'function')) {
  23801. customActions.push(details.action);
  23802. }
  23803. }
  23804. });
  23805. }
  23806. } else {
  23807. view = view.replace('{class}', 'elfinder-cwd-icon-group');
  23808. title = tpl.groupTitle.replace('{items}', msg.items).replace('{num}', cnt);
  23809. dcnt = $.grep(files, function(f) { return f.mime == 'directory' ? true : false ; }).length;
  23810. if (!dcnt) {
  23811. size = 0;
  23812. $.each(files, function(h, f) {
  23813. var s = parseInt(f.size);
  23814. if (s >= 0 && size >= 0) {
  23815. size += s;
  23816. } else {
  23817. size = 'unknown';
  23818. }
  23819. });
  23820. content.push(row.replace(l, msg.kind).replace(v, msg.files));
  23821. !hideItems.size && content.push(row.replace(l, msg.size).replace(v, fm.formatSize(size)));
  23822. } else {
  23823. rdcnt = $.grep(files, function(f) { return f.mime === 'directory' && (! f.phash || f.isroot)? true : false ; }).length;
  23824. dcnt -= rdcnt;
  23825. 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(', ')));
  23826. !hideItems.size && content.push(row.replace(l, msg.size).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size')));
  23827. count = $.map(files, function(f) { return f.hash; });
  23828. }
  23829. }
  23830. view = view.replace('{title}', title).replace('{content}', content.join('').replace(/{class}/g, ''));
  23831. dialog = self.fmDialog(view, opts);
  23832. dialog.attr('id', id).one('mousedown', '.elfinder-info-path', function() {
  23833. $(this).html(applyZWSP($(this).html(), true));
  23834. });
  23835. if (fm.UA.Mobile && $.fn.tooltip) {
  23836. dialog.children('.ui-dialog-content .elfinder-info-title').tooltip({
  23837. classes: {
  23838. 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow'
  23839. },
  23840. tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow',
  23841. track: true
  23842. });
  23843. }
  23844. if (file && file.url == '1') {
  23845. dialog.on('click', '.'+spclass+'-url', function(){
  23846. $(this).parent().html(tpl.spinner.replace('{text}', fm.i18n('ntfurl')).replace('{name}', 'url'));
  23847. fm.request({
  23848. data : {cmd : 'url', target : file.hash},
  23849. preventDefault : true
  23850. })
  23851. .fail(function() {
  23852. replSpinner(name_esc, 'url');
  23853. })
  23854. .done(function(data) {
  23855. if (data.url) {
  23856. replSpinner('<a href="'+data.url+'" target="_blank">'+name_esc+'</a>' || name_esc, 'url');
  23857. var rfile = fm.file(file.hash);
  23858. rfile.url = data.url;
  23859. } else {
  23860. replSpinner(name_esc, 'url');
  23861. }
  23862. });
  23863. });
  23864. }
  23865. // load thumbnail
  23866. if (tmb) {
  23867. $('<img/>')
  23868. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  23869. .attr('src', tmb.url);
  23870. }
  23871. // send request to count total size
  23872. if (count.length) {
  23873. reqDfrd = fm.getSize(count).done(function(data) {
  23874. replSpinner(data.formated, 'size');
  23875. }).fail(function() {
  23876. replSpinner(msg.unknown, 'size');
  23877. });
  23878. }
  23879. // call custom actions
  23880. if (customActions.length) {
  23881. $.each(customActions, function(i, action) {
  23882. try {
  23883. action(file, fm, dialog);
  23884. } catch(e) {
  23885. fm.debug('error', e);
  23886. }
  23887. });
  23888. }
  23889. return $.Deferred().resolve();
  23890. };
  23891. }).prototype = { forceLoad : true }; // this is required command
  23892. /*
  23893. * File: /js/commands/mkdir.js
  23894. */
  23895. /**
  23896. * @class elFinder command "mkdir"
  23897. * Create new folder
  23898. *
  23899. * @author Dmitry (dio) Levashov
  23900. **/
  23901. elFinder.prototype.commands.mkdir = function() {
  23902. var fm = this.fm,
  23903. self = this,
  23904. curOrg;
  23905. this.value = '';
  23906. this.disableOnSearch = true;
  23907. this.updateOnSelect = false;
  23908. this.syncTitleOnChange = true;
  23909. this.mime = 'directory';
  23910. this.prefix = 'untitled folder';
  23911. this.exec = function(select, cOpts) {
  23912. var onCwd;
  23913. if (select && select.length && cOpts && cOpts._currentType && cOpts._currentType === 'navbar') {
  23914. this.origin = cOpts._currentType;
  23915. this.data = {
  23916. target: select[0]
  23917. };
  23918. } else {
  23919. onCwd = fm.cwd().hash === select[0];
  23920. this.origin = curOrg && !onCwd? curOrg : 'cwd';
  23921. delete this.data;
  23922. }
  23923. if (! select && ! this.options.intoNewFolderToolbtn) {
  23924. fm.getUI('cwd').trigger('unselectall');
  23925. }
  23926. //this.move = (!onCwd && curOrg !== 'navbar' && fm.selected().length)? true : false;
  23927. this.move = this.value === fm.i18n('cmdmkdirin');
  23928. return $.proxy(fm.res('mixin', 'make'), self)();
  23929. };
  23930. this.shortcuts = [{
  23931. pattern : 'ctrl+shift+n'
  23932. }];
  23933. this.init = function() {
  23934. if (this.options.intoNewFolderToolbtn) {
  23935. this.syncTitleOnChange = true;
  23936. }
  23937. };
  23938. fm.bind('select contextmenucreate closecontextmenu', function(e) {
  23939. var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected();
  23940. self.className = 'mkdir';
  23941. curOrg = e.data && sel.length? (e.data.origin || e.data.type || '') : '';
  23942. if (!self.options.intoNewFolderToolbtn && curOrg === '') {
  23943. curOrg = 'cwd';
  23944. }
  23945. if (sel.length && curOrg !== 'navbar' && curOrg !== 'cwd' && fm.cwd().hash !== sel[0]) {
  23946. self.title = fm.i18n('cmdmkdirin');
  23947. self.className += ' elfinder-button-icon-mkdirin';
  23948. } else {
  23949. self.title = fm.i18n('cmdmkdir');
  23950. }
  23951. if (e.type !== 'closecontextmenu') {
  23952. self.update(void(0), self.title);
  23953. } else {
  23954. requestAnimationFrame(function() {
  23955. self.update(void(0), self.title);
  23956. });
  23957. }
  23958. });
  23959. this.getstate = function(select) {
  23960. var cwd = fm.cwd(),
  23961. sel = (curOrg === 'navbar' || (select && select[0] !== cwd.hash))? this.files(select || fm.selected()) : [],
  23962. cnt = sel.length;
  23963. if (curOrg === 'navbar') {
  23964. return cnt && sel[0].write && sel[0].read? 0 : -1;
  23965. } else {
  23966. return cwd.write && (!cnt || $.grep(sel, function(f) { return f.read && ! f.locked? true : false; }).length == cnt)? 0 : -1;
  23967. }
  23968. };
  23969. };
  23970. /*
  23971. * File: /js/commands/mkfile.js
  23972. */
  23973. /**
  23974. * @class elFinder command "mkfile"
  23975. * Create new empty file
  23976. *
  23977. * @author Dmitry (dio) Levashov
  23978. **/
  23979. elFinder.prototype.commands.mkfile = function() {
  23980. var self = this;
  23981. this.disableOnSearch = true;
  23982. this.updateOnSelect = false;
  23983. this.mime = 'text/plain';
  23984. this.prefix = 'untitled file.txt';
  23985. this.variants = [];
  23986. this.getTypeName = function(mime, type) {
  23987. var fm = self.fm,
  23988. name;
  23989. if (name = fm.messages['kind' + fm.kinds[mime]]) {
  23990. name = fm.i18n(['extentiontype', type.toUpperCase(), name]);
  23991. } else {
  23992. name = fm.i18n(['extentionfile', type.toUpperCase()]);
  23993. }
  23994. return name;
  23995. };
  23996. this.fm.bind('open reload canMakeEmptyFile', function() {
  23997. var fm = self.fm,
  23998. hides = fm.storage('mkfileHides') || {};
  23999. self.variants = [];
  24000. if (fm.mimesCanMakeEmpty) {
  24001. $.each(fm.mimesCanMakeEmpty, function(mime, type) {
  24002. type && !hides[mime] && fm.uploadMimeCheck(mime) && self.variants.push([mime, self.getTypeName(mime, type)]);
  24003. });
  24004. }
  24005. self.change();
  24006. });
  24007. this.getstate = function() {
  24008. return this.fm.cwd().write ? 0 : -1;
  24009. };
  24010. this.exec = function(_dum, mime) {
  24011. var fm = self.fm,
  24012. type, err;
  24013. if (type = fm.mimesCanMakeEmpty[mime]) {
  24014. if (fm.uploadMimeCheck(mime)) {
  24015. this.mime = mime;
  24016. this.prefix = fm.i18n(['untitled file', type]);
  24017. return $.proxy(fm.res('mixin', 'make'), self)();
  24018. }
  24019. err = ['errMkfile', self.getTypeName(mime, type)];
  24020. }
  24021. return $.Deferred().reject(err);
  24022. };
  24023. };
  24024. /*
  24025. * File: /js/commands/netmount.js
  24026. */
  24027. /**
  24028. * @class elFinder command "netmount"
  24029. * Mount network volume with user credentials.
  24030. *
  24031. * @author Dmitry (dio) Levashov
  24032. **/
  24033. elFinder.prototype.commands.netmount = function() {
  24034. var self = this,
  24035. hasMenus = false,
  24036. content;
  24037. this.alwaysEnabled = true;
  24038. this.updateOnSelect = false;
  24039. this.drivers = [];
  24040. this.handlers = {
  24041. load : function() {
  24042. var fm = self.fm;
  24043. self.drivers = fm.netDrivers;
  24044. if (self.drivers.length) {
  24045. requestAnimationFrame(function() {
  24046. $.each(self.drivers, function() {
  24047. var d = self.options[this];
  24048. if (d) {
  24049. hasMenus = true;
  24050. if (d.integrateInfo) {
  24051. fm.trigger('helpIntegration', Object.assign({cmd: 'netmount'}, d.integrateInfo));
  24052. }
  24053. }
  24054. });
  24055. });
  24056. }
  24057. }
  24058. };
  24059. this.getstate = function() {
  24060. return hasMenus ? 0 : -1;
  24061. };
  24062. this.exec = function() {
  24063. var fm = self.fm,
  24064. dfrd = $.Deferred(),
  24065. o = self.options,
  24066. create = function() {
  24067. var winFocus = function() {
  24068. inputs.protocol.trigger('change', 'winfocus');
  24069. },
  24070. inputs = {
  24071. protocol : $('<select/>')
  24072. .on('change', function(e, data){
  24073. var protocol = this.value;
  24074. content.find('.elfinder-netmount-tr').hide();
  24075. content.find('.elfinder-netmount-tr-'+protocol).show();
  24076. dialogNode && dialogNode.children('.ui-dialog-buttonpane:first').find('button').show();
  24077. if (typeof o[protocol].select == 'function') {
  24078. o[protocol].select(fm, e, data);
  24079. }
  24080. requestAnimationFrame(function() {
  24081. content.find('input:text.elfinder-tabstop:visible:first').trigger('focus');
  24082. });
  24083. })
  24084. .addClass('ui-corner-all')
  24085. },
  24086. opts = {
  24087. title : fm.i18n('netMountDialogTitle'),
  24088. resizable : false,
  24089. modal : true,
  24090. destroyOnClose : false,
  24091. open : function() {
  24092. $(window).on('focus.'+fm.namespace, winFocus);
  24093. inputs.protocol.trigger('change');
  24094. },
  24095. close : function() {
  24096. dfrd.state() == 'pending' && dfrd.reject();
  24097. $(window).off('focus.'+fm.namespace, winFocus);
  24098. },
  24099. buttons : {}
  24100. },
  24101. doMount = function() {
  24102. var protocol = inputs.protocol.val(),
  24103. data = {cmd : 'netmount', protocol: protocol},
  24104. cur = o[protocol];
  24105. $.each(content.find('input.elfinder-netmount-inputs-'+protocol), function(name, input) {
  24106. var val, elm;
  24107. elm = $(input);
  24108. if (elm.is(':radio,:checkbox')) {
  24109. if (elm.is(':checked')) {
  24110. val = $.trim(elm.val());
  24111. }
  24112. } else {
  24113. val = $.trim(elm.val());
  24114. }
  24115. if (val) {
  24116. data[input.name] = val;
  24117. }
  24118. });
  24119. if (!data.host) {
  24120. return fm.trigger('error', {error : 'errNetMountHostReq', opts : {modal: true}});
  24121. }
  24122. fm.request({data : data, notify : {type : 'netmount', cnt : 1, hideCnt : true}})
  24123. .done(function(data) {
  24124. var pdir;
  24125. if (data.added && data.added.length) {
  24126. if (data.added[0].phash) {
  24127. if (pdir = fm.file(data.added[0].phash)) {
  24128. if (! pdir.dirs) {
  24129. pdir.dirs = 1;
  24130. fm.change({ changed: [ pdir ] });
  24131. }
  24132. }
  24133. }
  24134. fm.one('netmountdone', function() {
  24135. fm.exec('open', data.added[0].hash);
  24136. });
  24137. }
  24138. dfrd.resolve();
  24139. })
  24140. .fail(function(error) {
  24141. if (cur.fail && typeof cur.fail == 'function') {
  24142. cur.fail(fm, fm.parseError(error));
  24143. }
  24144. dfrd.reject(error);
  24145. });
  24146. self.dialog.elfinderdialog('close');
  24147. },
  24148. form = $('<form autocomplete="off"/>').on('keydown', 'input', function(e) {
  24149. var comp = true,
  24150. next;
  24151. if (e.keyCode === $.ui.keyCode.ENTER) {
  24152. $.each(form.find('input:visible:not(.elfinder-input-optional)'), function() {
  24153. if ($(this).val() === '') {
  24154. comp = false;
  24155. next = $(this);
  24156. return false;
  24157. }
  24158. });
  24159. if (comp) {
  24160. doMount();
  24161. } else {
  24162. next.trigger('focus');
  24163. }
  24164. }
  24165. }),
  24166. hidden = $('<div/>'),
  24167. dialog;
  24168. content = $('<table class="elfinder-info-tb elfinder-netmount-tb"/>')
  24169. .append($('<tr/>').append($('<td>'+fm.i18n('protocol')+'</td>')).append($('<td/>').append(inputs.protocol)));
  24170. $.each(self.drivers, function(i, protocol) {
  24171. if (o[protocol]) {
  24172. inputs.protocol.append('<option value="'+protocol+'">'+fm.i18n(o[protocol].name || protocol)+'</option>');
  24173. $.each(o[protocol].inputs, function(name, input) {
  24174. input.attr('name', name);
  24175. if (input.attr('type') != 'hidden') {
  24176. input.addClass('ui-corner-all elfinder-netmount-inputs-'+protocol);
  24177. content.append($('<tr/>').addClass('elfinder-netmount-tr elfinder-netmount-tr-'+protocol).append($('<td>'+fm.i18n(name)+'</td>')).append($('<td/>').append(input)));
  24178. } else {
  24179. input.addClass('elfinder-netmount-inputs-'+protocol);
  24180. hidden.append(input);
  24181. }
  24182. });
  24183. o[protocol].protocol = inputs.protocol;
  24184. }
  24185. });
  24186. content.append(hidden);
  24187. content.find('.elfinder-netmount-tr').hide();
  24188. opts.buttons[fm.i18n('btnMount')] = doMount;
  24189. opts.buttons[fm.i18n('btnCancel')] = function() {
  24190. self.dialog.elfinderdialog('close');
  24191. };
  24192. content.find('select,input').addClass('elfinder-tabstop');
  24193. dialog = self.fmDialog(form.append(content), opts);
  24194. dialogNode = dialog.closest('.ui-dialog');
  24195. dialog.ready(function(){
  24196. inputs.protocol.trigger('change');
  24197. dialog.elfinderdialog('posInit');
  24198. });
  24199. return dialog;
  24200. },
  24201. dialogNode;
  24202. if (!self.dialog) {
  24203. self.dialog = create();
  24204. } else {
  24205. self.dialog.elfinderdialog('open');
  24206. }
  24207. return dfrd.promise();
  24208. };
  24209. self.fm.bind('netmount', function(e) {
  24210. var d = e.data || null,
  24211. o = self.options;
  24212. if (d && d.protocol) {
  24213. if (o[d.protocol] && typeof o[d.protocol].done == 'function') {
  24214. o[d.protocol].done(self.fm, d);
  24215. content.find('select,input').addClass('elfinder-tabstop');
  24216. self.dialog.elfinderdialog('tabstopsInit');
  24217. }
  24218. }
  24219. });
  24220. };
  24221. elFinder.prototype.commands.netunmount = function() {
  24222. var self = this;
  24223. this.alwaysEnabled = true;
  24224. this.updateOnSelect = false;
  24225. this.drivers = [];
  24226. this.handlers = {
  24227. load : function() {
  24228. this.drivers = this.fm.netDrivers;
  24229. }
  24230. };
  24231. this.getstate = function(sel) {
  24232. var fm = this.fm,
  24233. file;
  24234. return !!sel && this.drivers.length && !this._disabled && (file = fm.file(sel[0])) && file.netkey ? 0 : -1;
  24235. };
  24236. this.exec = function(hashes) {
  24237. var self = this,
  24238. fm = this.fm,
  24239. dfrd = $.Deferred()
  24240. .fail(function(error) {
  24241. error && fm.error(error);
  24242. }),
  24243. drive = fm.file(hashes[0]),
  24244. childrenRoots = function(hash) {
  24245. var roots = [],
  24246. work;
  24247. if (fm.leafRoots) {
  24248. work = [];
  24249. $.each(fm.leafRoots, function(phash, hashes) {
  24250. var parents = fm.parents(phash),
  24251. idx, deep;
  24252. if ((idx = $.inArray(hash, parents)) !== -1) {
  24253. idx = parents.length - idx;
  24254. $.each(hashes, function(i, h) {
  24255. work.push({i: idx, hash: h});
  24256. });
  24257. }
  24258. });
  24259. if (work.length) {
  24260. work.sort(function(a, b) { return a.i < b.i; });
  24261. $.each(work, function(i, o) {
  24262. roots.push(o.hash);
  24263. });
  24264. }
  24265. }
  24266. return roots;
  24267. };
  24268. if (this._disabled) {
  24269. return dfrd.reject();
  24270. }
  24271. if (dfrd.state() == 'pending') {
  24272. fm.confirm({
  24273. title : self.title,
  24274. text : fm.i18n('confirmUnmount', drive.name),
  24275. accept : {
  24276. label : 'btnUnmount',
  24277. callback : function() {
  24278. var target = drive.hash,
  24279. roots = childrenRoots(target),
  24280. requests = [],
  24281. removed = [],
  24282. doUmount = function() {
  24283. $.when(requests).done(function() {
  24284. fm.request({
  24285. data : {cmd : 'netmount', protocol : 'netunmount', host: drive.netkey, user : target, pass : 'dum'},
  24286. notify : {type : 'netunmount', cnt : 1, hideCnt : true},
  24287. preventFail : true
  24288. })
  24289. .fail(function(error) {
  24290. dfrd.reject(error);
  24291. })
  24292. .done(function(data) {
  24293. drive.volumeid && delete fm.volumeExpires[drive.volumeid];
  24294. dfrd.resolve();
  24295. });
  24296. }).fail(function(error) {
  24297. if (removed.length) {
  24298. fm.remove({ removed: removed });
  24299. }
  24300. dfrd.reject(error);
  24301. });
  24302. };
  24303. if (roots.length) {
  24304. fm.confirm({
  24305. title : self.title,
  24306. text : (function() {
  24307. var msgs = ['unmountChildren'];
  24308. $.each(roots, function(i, hash) {
  24309. msgs.push([fm.file(hash).name]);
  24310. });
  24311. return msgs;
  24312. })(),
  24313. accept : {
  24314. label : 'btnUnmount',
  24315. callback : function() {
  24316. $.each(roots, function(i, hash) {
  24317. var d = fm.file(hash);
  24318. if (d.netkey) {
  24319. requests.push(fm.request({
  24320. data : {cmd : 'netmount', protocol : 'netunmount', host: d.netkey, user : d.hash, pass : 'dum'},
  24321. notify : {type : 'netunmount', cnt : 1, hideCnt : true},
  24322. preventDefault : true
  24323. }).done(function(data) {
  24324. if (data.removed) {
  24325. d.volumeid && delete fm.volumeExpires[d.volumeid];
  24326. removed = removed.concat(data.removed);
  24327. }
  24328. }));
  24329. }
  24330. });
  24331. doUmount();
  24332. }
  24333. },
  24334. cancel : {
  24335. label : 'btnCancel',
  24336. callback : function() {
  24337. dfrd.reject();
  24338. }
  24339. }
  24340. });
  24341. } else {
  24342. requests = null;
  24343. doUmount();
  24344. }
  24345. }
  24346. },
  24347. cancel : {
  24348. label : 'btnCancel',
  24349. callback : function() { dfrd.reject(); }
  24350. }
  24351. });
  24352. }
  24353. return dfrd;
  24354. };
  24355. };
  24356. /*
  24357. * File: /js/commands/open.js
  24358. */
  24359. /**
  24360. * @class elFinder command "open"
  24361. * Enter folder or open files in new windows
  24362. *
  24363. * @author Dmitry (dio) Levashov
  24364. **/
  24365. (elFinder.prototype.commands.open = function() {
  24366. var fm = this.fm;
  24367. this.alwaysEnabled = true;
  24368. this.noChangeDirOnRemovedCwd = true;
  24369. this._handlers = {
  24370. dblclick : function(e) { e.preventDefault(); fm.exec('open', e.data && e.data.file? [ e.data.file ]: void(0)); },
  24371. 'select enable disable reload' : function(e) { this.update(e.type == 'disable' ? -1 : void(0)); }
  24372. };
  24373. this.shortcuts = [{
  24374. pattern : 'ctrl+down numpad_enter'+(fm.OS != 'mac' && ' enter')
  24375. }];
  24376. this.getstate = function(select) {
  24377. var sel = this.files(select),
  24378. cnt = sel.length;
  24379. return cnt == 1
  24380. ? (sel[0].read? 0 : -1)
  24381. : (cnt && !fm.UA.Mobile) ? ($.grep(sel, function(file) { return file.mime == 'directory' || ! file.read ? false : true;}).length == cnt ? 0 : -1) : -1;
  24382. };
  24383. this.exec = function(hashes, cOpts) {
  24384. var dfrd = $.Deferred().fail(function(error) { error && fm.error(error); }),
  24385. files = this.files(hashes),
  24386. cnt = files.length,
  24387. thash = (typeof cOpts == 'object')? cOpts.thash : false,
  24388. opts = this.options,
  24389. into = opts.into || 'window',
  24390. file, url, s, w, imgW, imgH, winW, winH, reg, link, html5dl, inline,
  24391. selAct, cmd;
  24392. if (!cnt && !thash) {
  24393. {
  24394. return dfrd.reject();
  24395. }
  24396. }
  24397. // open folder
  24398. if (thash || (cnt == 1 && (file = files[0]) && file.mime == 'directory')) {
  24399. if (!thash && file && !file.read) {
  24400. return dfrd.reject(['errOpen', file.name, 'errPerm']);
  24401. } else {
  24402. if (fm.keyState.ctrlKey && (fm.keyState.shiftKey || typeof fm.options.getFileCallback !== 'function')) {
  24403. if (fm.getCommand('opennew')) {
  24404. return fm.exec('opennew', [thash? thash : file.hash]);
  24405. }
  24406. }
  24407. return fm.request({
  24408. data : {cmd : 'open', target : thash || file.hash},
  24409. notify : {type : 'open', cnt : 1, hideCnt : true},
  24410. syncOnFail : true,
  24411. lazy : false
  24412. });
  24413. }
  24414. }
  24415. files = $.grep(files, function(file) { return file.mime != 'directory' ? true : false; });
  24416. // nothing to open or files and folders selected - do nothing
  24417. if (cnt != files.length) {
  24418. return dfrd.reject();
  24419. }
  24420. var doOpen = function() {
  24421. var wnd, target, getOnly;
  24422. try {
  24423. reg = new RegExp(fm.option('dispInlineRegex'), 'i');
  24424. } catch(e) {
  24425. reg = false;
  24426. }
  24427. // open files
  24428. link = $('<a>').hide().appendTo($('body')),
  24429. html5dl = (typeof link.get(0).download === 'string');
  24430. cnt = files.length;
  24431. while (cnt--) {
  24432. target = 'elf_open_window';
  24433. file = files[cnt];
  24434. if (!file.read) {
  24435. return dfrd.reject(['errOpen', file.name, 'errPerm']);
  24436. }
  24437. inline = (reg && file.mime.match(reg));
  24438. url = fm.openUrl(file.hash, !inline);
  24439. if (fm.UA.Mobile || !inline) {
  24440. if (html5dl) {
  24441. if (!inline) {
  24442. link.attr('download', file.name);
  24443. } else {
  24444. link.attr('target', '_blank');
  24445. }
  24446. link.attr('href', url).get(0).click();
  24447. } else {
  24448. wnd = window.open(url);
  24449. if (!wnd) {
  24450. return dfrd.reject('errPopup');
  24451. }
  24452. }
  24453. } else {
  24454. getOnly = (typeof opts.method === 'string' && opts.method.toLowerCase() === 'get');
  24455. if (!getOnly
  24456. && url.indexOf(fm.options.url) === 0
  24457. && fm.customData
  24458. && Object.keys(fm.customData).length
  24459. // Since playback by POST request can not be done in Chrome, media allows GET request
  24460. && !file.mime.match(/^(?:video|audio)/)
  24461. ) {
  24462. // Send request as 'POST' method to hide custom data at location bar
  24463. url = '';
  24464. }
  24465. if (into === 'window') {
  24466. // set window size for image if set
  24467. imgW = winW = Math.round(2 * screen.availWidth / 3);
  24468. imgH = winH = Math.round(2 * screen.availHeight / 3);
  24469. if (parseInt(file.width) && parseInt(file.height)) {
  24470. imgW = parseInt(file.width);
  24471. imgH = parseInt(file.height);
  24472. } else if (file.dim) {
  24473. s = file.dim.split('x');
  24474. imgW = parseInt(s[0]);
  24475. imgH = parseInt(s[1]);
  24476. }
  24477. if (winW >= imgW && winH >= imgH) {
  24478. winW = imgW;
  24479. winH = imgH;
  24480. } else {
  24481. if ((imgW - winW) > (imgH - winH)) {
  24482. winH = Math.round(imgH * (winW / imgW));
  24483. } else {
  24484. winW = Math.round(imgW * (winH / imgH));
  24485. }
  24486. }
  24487. w = 'width='+winW+',height='+winH;
  24488. wnd = window.open(url, target, w + ',top=50,left=50,scrollbars=yes,resizable=yes,titlebar=no');
  24489. } else {
  24490. if (into === 'tabs') {
  24491. target = file.hash;
  24492. }
  24493. wnd = window.open('about:blank', target);
  24494. }
  24495. if (!wnd) {
  24496. return dfrd.reject('errPopup');
  24497. }
  24498. if (url === '') {
  24499. var form = document.createElement("form");
  24500. form.action = fm.options.url;
  24501. form.method = 'POST';
  24502. form.target = target;
  24503. form.style.display = 'none';
  24504. var params = Object.assign({}, fm.customData, {
  24505. cmd: 'file',
  24506. target: file.hash,
  24507. _t: file.ts || parseInt(+new Date()/1000)
  24508. });
  24509. $.each(params, function(key, val)
  24510. {
  24511. var input = document.createElement("input");
  24512. input.name = key;
  24513. input.value = val;
  24514. form.appendChild(input);
  24515. });
  24516. document.body.appendChild(form);
  24517. form.submit();
  24518. } else if (into !== 'window') {
  24519. wnd.location = url;
  24520. }
  24521. $(wnd).trigger('focus');
  24522. }
  24523. }
  24524. link.remove();
  24525. return dfrd.resolve(hashes);
  24526. };
  24527. if (cnt > 1) {
  24528. fm.confirm({
  24529. title: 'openMulti',
  24530. text : ['openMultiConfirm', cnt + ''],
  24531. accept : {
  24532. label : 'cmdopen',
  24533. callback : function() { doOpen(); }
  24534. },
  24535. cancel : {
  24536. label : 'btnCancel',
  24537. callback : function() {
  24538. dfrd.reject();
  24539. }
  24540. },
  24541. buttons : (fm.getCommand('zipdl') && fm.isCommandEnabled('zipdl', fm.cwd().hash))? [
  24542. {
  24543. label : 'cmddownload',
  24544. callback : function() {
  24545. fm.exec('download', hashes);
  24546. dfrd.reject();
  24547. }
  24548. }
  24549. ] : []
  24550. });
  24551. } else {
  24552. selAct = fm.storage('selectAction') || opts.selectAction;
  24553. if (selAct) {
  24554. $.each(selAct.split('/'), function() {
  24555. var cmdName = this.valueOf();
  24556. if (cmdName !== 'open' && (cmd = fm.getCommand(cmdName)) && cmd.enabled()) {
  24557. return false;
  24558. }
  24559. cmd = null;
  24560. });
  24561. if (cmd) {
  24562. return fm.exec(cmd.name);
  24563. }
  24564. }
  24565. doOpen();
  24566. }
  24567. return dfrd;
  24568. };
  24569. }).prototype = { forceLoad : true }; // this is required command
  24570. /*
  24571. * File: /js/commands/opendir.js
  24572. */
  24573. /**
  24574. * @class elFinder command "opendir"
  24575. * Enter parent folder
  24576. *
  24577. * @author Naoki Sawada
  24578. **/
  24579. elFinder.prototype.commands.opendir = function() {
  24580. this.alwaysEnabled = true;
  24581. this.getstate = function() {
  24582. var sel = this.fm.selected(),
  24583. cnt = sel.length,
  24584. wz;
  24585. if (cnt !== 1) {
  24586. return -1;
  24587. }
  24588. wz = this.fm.getUI('workzone');
  24589. return wz.hasClass('elfinder-search-result')? 0 : -1;
  24590. };
  24591. this.exec = function(hashes) {
  24592. var fm = this.fm,
  24593. dfrd = $.Deferred(),
  24594. files = this.files(hashes),
  24595. cnt = files.length,
  24596. hash, pcheck = null;
  24597. if (!cnt || !files[0].phash) {
  24598. return dfrd.reject();
  24599. }
  24600. hash = files[0].phash;
  24601. fm.trigger('searchend', { noupdate: true });
  24602. fm.request({
  24603. data : {cmd : 'open', target : hash},
  24604. notify : {type : 'open', cnt : 1, hideCnt : true},
  24605. syncOnFail : false
  24606. });
  24607. return dfrd;
  24608. };
  24609. };
  24610. /*
  24611. * File: /js/commands/opennew.js
  24612. */
  24613. /**
  24614. * @class elFinder command "opennew"
  24615. * Open folder in new window
  24616. *
  24617. * @author Naoki Sawada
  24618. **/
  24619. elFinder.prototype.commands.opennew = function() {
  24620. var fm = this.fm;
  24621. this.shortcuts = [{
  24622. pattern : (typeof(fm.options.getFileCallback) === 'function'? 'shift+' : '') + 'ctrl+enter'
  24623. }];
  24624. this.getstate = function(select) {
  24625. var sel = this.files(select),
  24626. cnt = sel.length;
  24627. return cnt === 1
  24628. ? (sel[0].mime === 'directory' && sel[0].read? 0 : -1)
  24629. : -1;
  24630. };
  24631. this.exec = function(hashes) {
  24632. var dfrd = $.Deferred(),
  24633. files = this.files(hashes),
  24634. cnt = files.length,
  24635. opts = this.options,
  24636. file, loc, url, win;
  24637. // open folder to new tab (window)
  24638. if (cnt === 1 && (file = files[0]) && file.mime === 'directory') {
  24639. loc = window.location;
  24640. if (opts.url) {
  24641. url = opts.url;
  24642. } else {
  24643. url = loc.pathname;
  24644. }
  24645. if (opts.useOriginQuery) {
  24646. if (!url.match(/\?/)) {
  24647. url += loc.search;
  24648. } else if (loc.search) {
  24649. url += '&' + loc.search.substr(1);
  24650. }
  24651. }
  24652. url += '#elf_' + file.hash;
  24653. win = window.open(url, '_blank');
  24654. setTimeout(function() {
  24655. win.focus();
  24656. }, 1000);
  24657. return dfrd.resolve();
  24658. } else {
  24659. return dfrd.reject();
  24660. }
  24661. };
  24662. };
  24663. /*
  24664. * File: /js/commands/paste.js
  24665. */
  24666. /**
  24667. * @class elFinder command "paste"
  24668. * Paste filesfrom clipboard into directory.
  24669. * If files pasted in its parent directory - files duplicates will created
  24670. *
  24671. * @author Dmitry (dio) Levashov
  24672. **/
  24673. elFinder.prototype.commands.paste = function() {
  24674. this.updateOnSelect = false;
  24675. this.handlers = {
  24676. changeclipboard : function() { this.update(); }
  24677. };
  24678. this.shortcuts = [{
  24679. pattern : 'ctrl+v shift+insert'
  24680. }];
  24681. this.getstate = function(dst) {
  24682. if (this._disabled) {
  24683. return -1;
  24684. }
  24685. if (dst) {
  24686. if (Array.isArray(dst)) {
  24687. if (dst.length != 1) {
  24688. return -1;
  24689. }
  24690. dst = this.fm.file(dst[0]);
  24691. }
  24692. } else {
  24693. dst = this.fm.cwd();
  24694. }
  24695. return this.fm.clipboard().length && dst.mime == 'directory' && dst.write ? 0 : -1;
  24696. };
  24697. this.exec = function(select, cOpts) {
  24698. var self = this,
  24699. fm = self.fm,
  24700. opts = cOpts || {},
  24701. dst = select ? this.files(select)[0] : fm.cwd(),
  24702. files = fm.clipboard(),
  24703. cnt = files.length,
  24704. cut = cnt ? files[0].cut : false,
  24705. cmd = opts._cmd? opts._cmd : (cut? 'move' : 'copy'),
  24706. error = 'err' + cmd.charAt(0).toUpperCase() + cmd.substr(1),
  24707. fpaste = [],
  24708. fcopy = [],
  24709. dfrd = $.Deferred()
  24710. .fail(function(error) {
  24711. error && fm.error(error);
  24712. })
  24713. .always(function() {
  24714. fm.unlockfiles({files : $.map(files, function(f) { return f.hash; })});
  24715. }),
  24716. copy = function(files) {
  24717. return files.length && fm._commands.duplicate
  24718. ? fm.exec('duplicate', files)
  24719. : $.Deferred().resolve();
  24720. },
  24721. paste = function(files) {
  24722. var dfrd = $.Deferred(),
  24723. existed = [],
  24724. hashes = {},
  24725. intersect = function(files, names) {
  24726. var ret = [],
  24727. i = files.length;
  24728. while (i--) {
  24729. $.inArray(files[i].name, names) !== -1 && ret.unshift(i);
  24730. }
  24731. return ret;
  24732. },
  24733. confirm = function(ndx) {
  24734. var i = existed[ndx],
  24735. file = files[i],
  24736. last = ndx == existed.length-1;
  24737. if (!file) {
  24738. return;
  24739. }
  24740. fm.confirm({
  24741. title : fm.i18n(cmd + 'Files'),
  24742. text : ['errExists', file.name, cmd === 'restore'? 'confirmRest' : 'confirmRepl'],
  24743. all : !last,
  24744. accept : {
  24745. label : 'btnYes',
  24746. callback : function(all) {
  24747. !last && !all
  24748. ? confirm(++ndx)
  24749. : paste(files);
  24750. }
  24751. },
  24752. reject : {
  24753. label : 'btnNo',
  24754. callback : function(all) {
  24755. var i;
  24756. if (all) {
  24757. i = existed.length;
  24758. while (ndx < i--) {
  24759. files[existed[i]].remove = true;
  24760. }
  24761. } else {
  24762. files[existed[ndx]].remove = true;
  24763. }
  24764. !last && !all
  24765. ? confirm(++ndx)
  24766. : paste(files);
  24767. }
  24768. },
  24769. cancel : {
  24770. label : 'btnCancel',
  24771. callback : function() {
  24772. dfrd.resolve();
  24773. }
  24774. },
  24775. buttons : [
  24776. {
  24777. label : 'btnBackup',
  24778. callback : function(all) {
  24779. var i;
  24780. if (all) {
  24781. i = existed.length;
  24782. while (ndx < i--) {
  24783. files[existed[i]].rename = true;
  24784. }
  24785. } else {
  24786. files[existed[ndx]].rename = true;
  24787. }
  24788. !last && !all
  24789. ? confirm(++ndx)
  24790. : paste(files);
  24791. }
  24792. }
  24793. ]
  24794. });
  24795. },
  24796. valid = function(names) {
  24797. var exists = {}, existedArr;
  24798. if (names) {
  24799. if (Array.isArray(names)) {
  24800. if (names.length) {
  24801. if (typeof names[0] == 'string') {
  24802. // elFinder <= 2.1.6 command `is` results
  24803. existed = intersect(files, names);
  24804. } else {
  24805. $.each(names, function(i, v) {
  24806. exists[v.name] = v.hash;
  24807. });
  24808. existed = intersect(files, $.map(exists, function(h, n) { return n; }));
  24809. $.each(files, function(i, file) {
  24810. if (exists[file.name]) {
  24811. hashes[exists[file.name]] = file.name;
  24812. }
  24813. });
  24814. }
  24815. }
  24816. } else {
  24817. existedArr = [];
  24818. existed = $.map(names, function(n) {
  24819. if (typeof n === 'string') {
  24820. return n;
  24821. } else {
  24822. // support to >=2.1.11 plugin Normalizer, Sanitizer
  24823. existedArr = existedArr.concat(n);
  24824. return false;
  24825. }
  24826. });
  24827. if (existedArr.length) {
  24828. existed = existed.concat(existedArr);
  24829. }
  24830. existed = intersect(files, existed);
  24831. hashes = names;
  24832. }
  24833. }
  24834. existed.length ? confirm(0) : paste(files);
  24835. },
  24836. paste = function(selFiles) {
  24837. var renames = [],
  24838. files = $.grep(selFiles, function(file) {
  24839. if (file.rename) {
  24840. renames.push(file.name);
  24841. }
  24842. return !file.remove ? true : false;
  24843. }),
  24844. cnt = files.length,
  24845. groups = {},
  24846. args = [],
  24847. targets, reqData;
  24848. if (!cnt) {
  24849. return dfrd.resolve();
  24850. }
  24851. targets = $.map(files, function(f) { return f.hash; });
  24852. reqData = {cmd : 'paste', dst : dst.hash, targets : targets, cut : cut ? 1 : 0, renames : renames, hashes : hashes, suffix : fm.options.backupSuffix};
  24853. if (fm.api < 2.1) {
  24854. reqData.src = files[0].phash;
  24855. }
  24856. fm.request({
  24857. data : reqData,
  24858. notify : {type : cmd, cnt : cnt},
  24859. navigate : {
  24860. toast : opts.noToast? {} : {
  24861. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd' + cmd)]), action: {
  24862. cmd: 'open',
  24863. msg: 'cmdopendir',
  24864. data: [dst.hash],
  24865. done: 'select',
  24866. cwdNot: dst.hash
  24867. }}
  24868. }
  24869. }
  24870. })
  24871. .done(function(data) {
  24872. var dsts = {},
  24873. added = data.added && data.added.length? data.added : null;
  24874. if (cut && added) {
  24875. // undo/redo
  24876. $.each(files, function(i, f) {
  24877. var phash = f.phash,
  24878. srcHash = function(name) {
  24879. var hash;
  24880. $.each(added, function(i, f) {
  24881. if (f.name === name) {
  24882. hash = f.hash;
  24883. return false;
  24884. }
  24885. });
  24886. return hash;
  24887. },
  24888. shash = srcHash(f.name);
  24889. if (shash) {
  24890. if (dsts[phash]) {
  24891. dsts[phash].push(shash);
  24892. } else {
  24893. dsts[phash] = [ shash ];
  24894. }
  24895. }
  24896. });
  24897. if (Object.keys(dsts).length) {
  24898. data.undo = {
  24899. cmd : 'move',
  24900. callback : function() {
  24901. var reqs = [];
  24902. $.each(dsts, function(dst, targets) {
  24903. reqs.push(fm.request({
  24904. data : {cmd : 'paste', dst : dst, targets : targets, cut : 1},
  24905. notify : {type : 'undo', cnt : targets.length}
  24906. }));
  24907. });
  24908. return $.when.apply(null, reqs);
  24909. }
  24910. };
  24911. data.redo = {
  24912. cmd : 'move',
  24913. callback : function() {
  24914. return fm.request({
  24915. data : reqData,
  24916. notify : {type : 'redo', cnt : cnt}
  24917. });
  24918. }
  24919. };
  24920. }
  24921. }
  24922. dfrd.resolve(data);
  24923. })
  24924. .fail(function() {
  24925. dfrd.reject();
  24926. })
  24927. .always(function() {
  24928. fm.unlockfiles({files : files});
  24929. });
  24930. },
  24931. internames;
  24932. if (!fm.isCommandEnabled(self.name, dst.hash) || !files.length) {
  24933. return dfrd.resolve();
  24934. }
  24935. if (fm.oldAPI) {
  24936. paste(files);
  24937. } else {
  24938. if (!fm.option('copyOverwrite', dst.hash)) {
  24939. paste(files);
  24940. } else {
  24941. internames = $.map(files, function(f) { return f.name; });
  24942. dst.hash == fm.cwd().hash
  24943. ? valid($.map(fm.files(), function(file) { return file.phash == dst.hash ? {hash: file.hash, name: file.name} : null; }))
  24944. : fm.request({
  24945. data : {cmd : 'ls', target : dst.hash, intersect : internames},
  24946. notify : {type : 'prepare', cnt : 1, hideCnt : true},
  24947. preventFail : true
  24948. })
  24949. .always(function(data) {
  24950. valid(data.list);
  24951. });
  24952. }
  24953. }
  24954. return dfrd;
  24955. },
  24956. parents, fparents;
  24957. if (!cnt || !dst || dst.mime != 'directory') {
  24958. return dfrd.reject();
  24959. }
  24960. if (!dst.write) {
  24961. return dfrd.reject([error, files[0].name, 'errPerm']);
  24962. }
  24963. parents = fm.parents(dst.hash);
  24964. $.each(files, function(i, file) {
  24965. if (!file.read) {
  24966. return !dfrd.reject([error, file.name, 'errPerm']);
  24967. }
  24968. if (cut && file.locked) {
  24969. return !dfrd.reject(['errLocked', file.name]);
  24970. }
  24971. if ($.inArray(file.hash, parents) !== -1) {
  24972. return !dfrd.reject(['errCopyInItself', file.name]);
  24973. }
  24974. if (file.mime && file.mime !== 'directory' && ! fm.uploadMimeCheck(file.mime, dst.hash)) {
  24975. return !dfrd.reject([error, file.name, 'errUploadMime']);
  24976. }
  24977. fparents = fm.parents(file.hash);
  24978. fparents.pop();
  24979. if ($.inArray(dst.hash, fparents) !== -1) {
  24980. if ($.grep(fparents, function(h) { var d = fm.file(h); return d.phash == dst.hash && d.name == file.name ? true : false; }).length) {
  24981. return !dfrd.reject(['errReplByChild', file.name]);
  24982. }
  24983. }
  24984. if (file.phash == dst.hash) {
  24985. fcopy.push(file.hash);
  24986. } else {
  24987. fpaste.push({
  24988. hash : file.hash,
  24989. phash : file.phash,
  24990. name : file.name
  24991. });
  24992. }
  24993. });
  24994. if (dfrd.state() == 'rejected') {
  24995. return dfrd;
  24996. }
  24997. $.when(
  24998. copy(fcopy),
  24999. paste(fpaste)
  25000. )
  25001. .done(function(cr, pr) {
  25002. dfrd.resolve(pr && pr.undo? pr : void(0));
  25003. })
  25004. .fail(function() {
  25005. dfrd.reject();
  25006. })
  25007. .always(function() {
  25008. cut && fm.clipboard([]);
  25009. });
  25010. return dfrd;
  25011. };
  25012. };
  25013. /*
  25014. * File: /js/commands/places.js
  25015. */
  25016. /**
  25017. * @class elFinder command "places"
  25018. * Regist to Places
  25019. *
  25020. * @author Naoki Sawada
  25021. **/
  25022. elFinder.prototype.commands.places = function() {
  25023. var self = this,
  25024. fm = this.fm,
  25025. filter = function(hashes) {
  25026. return $.grep(self.files(hashes), function(f) { return f.mime == 'directory' ? true : false; });
  25027. },
  25028. places = null;
  25029. this.getstate = function(select) {
  25030. var sel = this.hashes(select),
  25031. cnt = sel.length;
  25032. return places && cnt && cnt == filter(sel).length ? 0 : -1;
  25033. };
  25034. this.exec = function(hashes) {
  25035. var files = this.files(hashes);
  25036. places.trigger('regist', [ files ]);
  25037. return $.Deferred().resolve();
  25038. };
  25039. fm.one('load', function(){
  25040. places = fm.ui.places;
  25041. });
  25042. };
  25043. /*
  25044. * File: /js/commands/preference.js
  25045. */
  25046. /**
  25047. * @class elFinder command "preference"
  25048. * "Preference" dialog
  25049. *
  25050. * @author Naoki Sawada
  25051. **/
  25052. elFinder.prototype.commands.preference = function() {
  25053. var self = this,
  25054. fm = this.fm,
  25055. r = 'replace',
  25056. 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>',
  25057. base = $('<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-preference">'),
  25058. ul = $('<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-top">'),
  25059. tabs = $('<div class="elfinder-preference-tabs ui-tabs-panel ui-widget-content ui-corner-bottom"/>'),
  25060. sep = '<div class="elfinder-preference-separator"/>',
  25061. selfUrl = $('base').length? document.location.href.replace(/#.*$/, '') : '',
  25062. selectTab = function(tab) {
  25063. $('#'+fm.namespace+'-preference-tab-'+tab).trigger('mouseover').trigger('click');
  25064. openTab = tab;
  25065. },
  25066. clTabActive = fm.res('class', 'tabsactive'),
  25067. build = function() {
  25068. var cats = self.options.categories || {
  25069. 'language' : ['language'],
  25070. 'theme' : ['theme'],
  25071. 'toolbar' : ['toolbarPref'],
  25072. 'workspace' : ['iconSize','columnPref', 'selectAction', 'makefileTypes', 'useStoredEditor', 'editorMaximized', 'useFullscreen', 'showHidden'],
  25073. 'dialog' : ['autoFocusDialog'],
  25074. 'selectionInfo' : ['infoItems', 'hashChecker'],
  25075. 'reset' : ['clearBrowserData'],
  25076. 'all' : true
  25077. },
  25078. forms = self.options.prefs || ['language', 'theme', 'toolbarPref', 'iconSize', 'columnPref', 'selectAction', 'makefileTypes', 'useStoredEditor', 'editorMaximized', 'useFullscreen', 'showHidden', 'infoItems', 'hashChecker', 'autoFocusDialog', 'clearBrowserData'];
  25079. forms = fm.arrayFlip(forms, true);
  25080. if (fm.options.getFileCallback) {
  25081. delete forms.selectAction;
  25082. }
  25083. if (!fm.UA.Fullscreen) {
  25084. delete forms.useFullscreen;
  25085. }
  25086. forms.language && (forms.language = (function() {
  25087. var langSel = $('<select/>').on('change', function() {
  25088. var lang = $(this).val();
  25089. fm.storage('lang', lang);
  25090. $('#'+fm.id).elfinder('reload');
  25091. }),
  25092. optTags = [],
  25093. langs = self.options.langs || {
  25094. ar: 'العربية',
  25095. bg: 'Български',
  25096. ca: 'Català',
  25097. cs: 'Čeština',
  25098. da: 'Dansk',
  25099. de: 'Deutsch',
  25100. el: 'Ελληνικά',
  25101. en: 'English',
  25102. es: 'Español',
  25103. fa: 'فارسی',
  25104. fo: 'Føroyskt',
  25105. fr: 'Français',
  25106. he: 'עברית',
  25107. hr: 'Hrvatski',
  25108. hu: 'Magyar',
  25109. id: 'Bahasa Indonesia',
  25110. it: 'Italiano',
  25111. ja: '日本語',
  25112. ko: '한국어',
  25113. nl: 'Nederlands',
  25114. no: 'Norsk',
  25115. pl: 'Polski',
  25116. pt_BR: 'Português',
  25117. ro: 'Română',
  25118. ru: 'Pусский',
  25119. si: 'සිංහල',
  25120. sk: 'Slovenčina',
  25121. sl: 'Slovenščina',
  25122. sr: 'Srpski',
  25123. sv: 'Svenska',
  25124. tr: 'Türkçe',
  25125. ug_CN: 'ئۇيغۇرچە',
  25126. uk: 'Український',
  25127. vi: 'Tiếng Việt',
  25128. zh_CN: '简体中文',
  25129. zh_TW: '正體中文'
  25130. };
  25131. $.each(langs, function(lang, name) {
  25132. optTags.push('<option value="'+lang+'">'+name+'</option>');
  25133. });
  25134. return langSel.append(optTags.join('')).val(fm.lang);
  25135. })());
  25136. forms.theme && (forms.theme = (function() {
  25137. var cnt = fm.options.themes? Object.keys(fm.options.themes).length : 0;
  25138. if (cnt === 0 || (cnt === 1 && fm.options.themes.default)) {
  25139. return null;
  25140. }
  25141. var themeSel = $('<select/>').on('change', function() {
  25142. var theme = $(this).val();
  25143. fm.changeTheme(theme).storage('theme', theme);
  25144. }),
  25145. optTags = [],
  25146. tpl = {
  25147. image: '<img class="elfinder-preference-theme elfinder-preference-theme-image" src="$2" />',
  25148. link: '<a href="$1" target="_blank" title="$3">$2</a>',
  25149. data: '<dt>$1</dt><dd><span class="elfinder-preference-theme elfinder-preference-theme-$0">$2</span></dd>'
  25150. },
  25151. items = ['image', 'description', 'author', 'email', 'license'],
  25152. render = function(key, data) {
  25153. },
  25154. defBtn = $('<button class="ui-button ui-corner-all ui-widget elfinder-preference-theme-default"/>').text(fm.i18n('default')).on('click', function(e) {
  25155. themeSel.val('default').trigger('change');
  25156. }),
  25157. list = $('<div class="elfinder-reference-hide-taball"/>').on('click', 'button', function() {
  25158. var val = $(this).data('themeid');
  25159. themeSel.val(val).trigger('change');
  25160. });
  25161. if (!fm.options.themes.default) {
  25162. themeSel.append('<option value="default">'+fm.i18n('default')+'</option>');
  25163. }
  25164. $.each(fm.options.themes, function(id, val) {
  25165. var opt = $('<option class="elfinder-theme-option-'+id+'" value="'+id+'">'+fm.i18n(id)+'</option>'),
  25166. 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"/></div></fieldset>'),
  25167. tm;
  25168. themeSel.append(opt);
  25169. list.append(dsc);
  25170. tm = setTimeout(function() {
  25171. dsc.find('span.elfinder-spinner').replaceWith(fm.i18n(['errRead', id]));
  25172. }, 10000);
  25173. fm.getTheme(id).always(function() {
  25174. tm && clearTimeout(tm);
  25175. }).done(function(data) {
  25176. var link, val = $(), dl = $('<dl/>');
  25177. link = data.link? tpl.link.replace(/\$1/g, data.link).replace(/\$3/g, fm.i18n('website')) : '$2';
  25178. if (data.name) {
  25179. opt.html(fm.i18n(data.name));
  25180. }
  25181. dsc.children('legend').html(link.replace(/\$2/g, fm.i18n(data.name) || id));
  25182. $.each(items, function(i, key) {
  25183. var t = tpl[key] || tpl.data,
  25184. elm;
  25185. if (data[key]) {
  25186. elm = t.replace(/\$0/g, fm.escape(key)).replace(/\$1/g, fm.i18n(key)).replace(/\$2/g, fm.i18n(data[key]));
  25187. if (key === 'image' && data.link) {
  25188. elm = $(elm).on('click', function() {
  25189. themeSel.val(id).trigger('change');
  25190. }).attr('title', fm.i18n('select'));
  25191. }
  25192. dl.append(elm);
  25193. }
  25194. });
  25195. val = val.add(dl);
  25196. val = val.add($('<div class="elfinder-preference-theme-btn"/>').append($('<button class="ui-button ui-corner-all ui-widget"/>').data('themeid', id).html(fm.i18n('select'))));
  25197. dsc.find('span.elfinder-spinner').replaceWith(val);
  25198. }).fail(function() {
  25199. dsc.find('span.elfinder-spinner').replaceWith(fm.i18n(['errRead', id]));
  25200. });
  25201. });
  25202. return $('<div/>').append(themeSel.val(fm.theme && fm.theme.id? fm.theme.id : 'default'), defBtn, list);
  25203. })());
  25204. forms.toolbarPref && (forms.toolbarPref = (function() {
  25205. var pnls = $.map(fm.options.uiOptions.toolbar, function(v) {
  25206. return $.isArray(v)? v : null;
  25207. }),
  25208. tags = [],
  25209. hides = fm.storage('toolbarhides') || {};
  25210. $.each(pnls, function() {
  25211. var cmd = this,
  25212. name = fm.i18n('cmd'+cmd);
  25213. if (name === 'cmd'+cmd) {
  25214. name = fm.i18n(cmd);
  25215. }
  25216. tags.push('<span class="elfinder-preference-toolbar-item"><label><input type="checkbox" value="'+cmd+'" '+(hides[cmd]? '' : 'checked')+'/>'+name+'</label></span>');
  25217. });
  25218. return $(tags.join(' ')).on('change', 'input', function() {
  25219. var v = $(this).val(),
  25220. o = $(this).is(':checked');
  25221. if (!o && !hides[v]) {
  25222. hides[v] = true;
  25223. } else if (o && hides[v]) {
  25224. delete hides[v];
  25225. }
  25226. fm.storage('toolbarhides', hides);
  25227. fm.trigger('toolbarpref');
  25228. });
  25229. })());
  25230. forms.iconSize && (forms.iconSize = (function() {
  25231. var max = fm.options.uiOptions.cwd.iconsView.sizeMax || 3,
  25232. size = fm.storage('iconsize') || 0,
  25233. sld = $('<div class="touch-punch"/>').slider({
  25234. classes: {
  25235. 'ui-slider-handle': 'elfinder-tabstop',
  25236. },
  25237. value: size,
  25238. max: max,
  25239. slide: function(e, ui) {
  25240. fm.getUI('cwd').trigger('iconpref', {size: ui.value});
  25241. },
  25242. change: function(e, ui) {
  25243. fm.storage('iconsize', ui.value);
  25244. }
  25245. });
  25246. fm.getUI('cwd').on('iconpref', function(e, data) {
  25247. sld.slider('option', 'value', data.size);
  25248. });
  25249. return sld;
  25250. })());
  25251. forms.columnPref && (forms.columnPref = (function() {
  25252. var cols = fm.options.uiOptions.cwd.listView.columns,
  25253. tags = [],
  25254. hides = fm.storage('columnhides') || {};
  25255. $.each(cols, function() {
  25256. var key = this,
  25257. name = fm.getColumnName(key);
  25258. tags.push('<span class="elfinder-preference-column-item"><label><input type="checkbox" value="'+key+'" '+(hides[key]? '' : 'checked')+'/>'+name+'</label></span>');
  25259. });
  25260. return $(tags.join(' ')).on('change', 'input', function() {
  25261. var v = $(this).val(),
  25262. o = $(this).is(':checked');
  25263. if (!o && !hides[v]) {
  25264. hides[v] = true;
  25265. } else if (o && hides[v]) {
  25266. delete hides[v];
  25267. }
  25268. fm.storage('columnhides', hides);
  25269. fm.trigger('columnpref', { repaint: true });
  25270. });
  25271. })());
  25272. forms.selectAction && (forms.selectAction = (function() {
  25273. var actSel = $('<select/>').on('change', function() {
  25274. var act = $(this).val();
  25275. fm.storage('selectAction', act === 'default'? null : act);
  25276. }),
  25277. optTags = [],
  25278. acts = self.options.selectActions,
  25279. defAct = fm.getCommand('open').options.selectAction || 'open';
  25280. if ($.inArray(defAct, acts) === -1) {
  25281. acts.unshift(defAct);
  25282. }
  25283. $.each(acts, function(i, act) {
  25284. var names = $.map(act.split('/'), function(cmd) {
  25285. var name = fm.i18n('cmd'+cmd);
  25286. if (name === 'cmd'+cmd) {
  25287. name = fm.i18n(cmd);
  25288. }
  25289. return name;
  25290. });
  25291. optTags.push('<option value="'+act+'">'+names.join('/')+'</option>');
  25292. });
  25293. return actSel.append(optTags.join('')).val(fm.storage('selectAction') || defAct);
  25294. })());
  25295. forms.makefileTypes && (forms.makefileTypes = (function() {
  25296. var hides = fm.storage('mkfileHides') || {},
  25297. getTag = function() {
  25298. var tags = [];
  25299. // re-assign hides
  25300. hides = fm.storage('mkfileHides') || {};
  25301. $.each(fm.mimesCanMakeEmpty, function(mime, type) {
  25302. var name = fm.getCommand('mkfile').getTypeName(mime, type);
  25303. tags.push('<span class="elfinder-preference-column-item" title="'+fm.escape(name)+'"><label><input type="checkbox" value="'+mime+'" '+(hides[mime]? '' : 'checked')+'/>'+type+'</label></span>');
  25304. });
  25305. return tags.join(' ');
  25306. },
  25307. elm = $('<div/>').on('change', 'input', function() {
  25308. var v = $(this).val(),
  25309. o = $(this).is(':checked');
  25310. if (!o && !hides[v]) {
  25311. hides[v] = true;
  25312. } else if (o && hides[v]) {
  25313. delete hides[v];
  25314. }
  25315. fm.storage('mkfileHides', hides);
  25316. fm.trigger('canMakeEmptyFile');
  25317. }).append(getTag()),
  25318. add = $('<div/>').append(
  25319. $('<input type="text" placeholder="'+fm.i18n('typeOfTextfile')+'"/>').on('keydown', function(e) {
  25320. (e.keyCode === $.ui.keyCode.ENTER) && $(this).next().trigger('click');
  25321. }),
  25322. $('<button class="ui-button"/>').html(fm.i18n('add')).on('click', function() {
  25323. var input = $(this).prev(),
  25324. val = input.val(),
  25325. uiToast = fm.getUI('toast'),
  25326. err = function() {
  25327. uiToast.appendTo(input.closest('.ui-dialog'));
  25328. fm.toast({
  25329. msg: fm.i18n('errUsupportType'),
  25330. mode: 'warning',
  25331. onHidden: function() {
  25332. uiToast.children().length === 1 && uiToast.appendTo(fm.getUI());
  25333. }
  25334. });
  25335. input.trigger('focus');
  25336. return false;
  25337. },
  25338. tmpMimes;
  25339. if (!val.match(/\//)) {
  25340. val = fm.arrayFlip(fm.mimeTypes)[val];
  25341. if (!val) {
  25342. return err();
  25343. }
  25344. input.val(val);
  25345. }
  25346. if (!fm.mimeIsText(val) || !fm.mimeTypes[val]) {
  25347. return err();
  25348. }
  25349. fm.trigger('canMakeEmptyFile', {mimes: [val], unshift: true});
  25350. tmpMimes = {};
  25351. tmpMimes[val] = fm.mimeTypes[val];
  25352. fm.storage('mkfileTextMimes', Object.assign(tmpMimes, fm.storage('mkfileTextMimes') || {}));
  25353. input.val('');
  25354. uiToast.appendTo(input.closest('.ui-dialog'));
  25355. fm.toast({
  25356. msg: fm.i18n(['complete', val + ' (' + tmpMimes[val] + ')']),
  25357. onHidden: function() {
  25358. uiToast.children().length === 1 && uiToast.appendTo(fm.getUI());
  25359. }
  25360. });
  25361. }),
  25362. $('<button class="ui-button"/>').html(fm.i18n('reset')).on('click', function() {
  25363. fm.one('canMakeEmptyFile', {done: function() {
  25364. elm.empty().append(getTag());
  25365. }});
  25366. fm.trigger('canMakeEmptyFile', {resetTexts: true});
  25367. })
  25368. ),
  25369. tm;
  25370. fm.bind('canMakeEmptyFile', {done: function(e) {
  25371. if (e.data && e.data.mimes && e.data.mimes.length) {
  25372. elm.empty().append(getTag());
  25373. }
  25374. }});
  25375. return $('<div/>').append(elm, add);
  25376. })());
  25377. forms.useStoredEditor && (forms.useStoredEditor = $('<input type="checkbox"/>').prop('checked', (function() {
  25378. var s = fm.storage('useStoredEditor');
  25379. return s? (s > 0) : fm.options.commandsOptions.edit.useStoredEditor;
  25380. })()).on('change', function(e) {
  25381. fm.storage('useStoredEditor', $(this).is(':checked')? 1 : -1);
  25382. }));
  25383. forms.editorMaximized && (forms.editorMaximized = $('<input type="checkbox"/>').prop('checked', (function() {
  25384. var s = fm.storage('editorMaximized');
  25385. return s? (s > 0) : fm.options.commandsOptions.edit.editorMaximized;
  25386. })()).on('change', function(e) {
  25387. fm.storage('editorMaximized', $(this).is(':checked')? 1 : -1);
  25388. }));
  25389. forms.useFullscreen && (forms.useFullscreen = $('<input type="checkbox"/>').prop('checked', (function() {
  25390. var s = fm.storage('useFullscreen');
  25391. return s? (s > 0) : fm.options.commandsOptions.fullscreen.mode === 'screen';
  25392. })()).on('change', function(e) {
  25393. fm.storage('useFullscreen', $(this).is(':checked')? 1 : -1);
  25394. }));
  25395. if (forms.showHidden) {
  25396. (function() {
  25397. var setTitle = function() {
  25398. var s = fm.storage('hide'),
  25399. t = [],
  25400. v;
  25401. if (s && s.items) {
  25402. $.each(s.items, function(h, n) {
  25403. t.push(fm.escape(n));
  25404. });
  25405. }
  25406. elms.prop('disabled', !t.length)[t.length? 'removeClass' : 'addClass']('ui-state-disabled');
  25407. v = t.length? t.join('\n') : '';
  25408. forms.showHidden.attr('title',v);
  25409. useTooltip && forms.showHidden.tooltip('option', 'content', v.replace(/\n/g, '<br>')).tooltip('close');
  25410. },
  25411. chk = $('<input type="checkbox"/>').prop('checked', (function() {
  25412. var s = fm.storage('hide');
  25413. return s && s.show;
  25414. })()).on('change', function(e) {
  25415. var o = {};
  25416. o[$(this).is(':checked')? 'show' : 'hide'] = true;
  25417. fm.exec('hide', void(0), o);
  25418. }),
  25419. btn = $('<button class="ui-button ui-corner-all ui-widget"/>').append(fm.i18n('reset')).on('click', function() {
  25420. fm.exec('hide', void(0), {reset: true});
  25421. $(this).parent().find('input:first').prop('checked', false);
  25422. setTitle();
  25423. }),
  25424. elms = $().add(chk).add(btn),
  25425. useTooltip;
  25426. forms.showHidden = $('<div/>').append(chk, btn);
  25427. fm.bind('hide', function(e) {
  25428. var d = e.data;
  25429. if (!d.opts || (!d.opts.show && !d.opts.hide)) {
  25430. setTitle();
  25431. }
  25432. });
  25433. if (fm.UA.Mobile && $.fn.tooltip) {
  25434. useTooltip = true;
  25435. forms.showHidden.tooltip({
  25436. classes: {
  25437. 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow'
  25438. },
  25439. tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow',
  25440. track: true
  25441. }).css('user-select', 'none');
  25442. btn.css('user-select', 'none');
  25443. }
  25444. setTitle();
  25445. })();
  25446. }
  25447. forms.infoItems && (forms.infoItems = (function() {
  25448. var items = fm.getCommand('info').items,
  25449. tags = [],
  25450. hides = fm.storage('infohides') || fm.arrayFlip(fm.options.commandsOptions.info.hideItems, true);
  25451. $.each(items, function() {
  25452. var key = this,
  25453. name = fm.i18n(key);
  25454. tags.push('<span class="elfinder-preference-info-item"><label><input type="checkbox" value="'+key+'" '+(hides[key]? '' : 'checked')+'/>'+name+'</label></span>');
  25455. });
  25456. return $(tags.join(' ')).on('change', 'input', function() {
  25457. var v = $(this).val(),
  25458. o = $(this).is(':checked');
  25459. if (!o && !hides[v]) {
  25460. hides[v] = true;
  25461. } else if (o && hides[v]) {
  25462. delete hides[v];
  25463. }
  25464. fm.storage('infohides', hides);
  25465. fm.trigger('infopref', { repaint: true });
  25466. });
  25467. })());
  25468. forms.hashChecker && fm.hashCheckers.length && (forms.hashChecker = (function() {
  25469. var tags = [],
  25470. enabled = fm.arrayFlip(fm.storage('hashchekcer') || fm.options.commandsOptions.info.showHashAlgorisms, true);
  25471. $.each(fm.hashCheckers, function() {
  25472. var cmd = this,
  25473. name = fm.i18n(cmd);
  25474. tags.push('<span class="elfinder-preference-hashchecker-item"><label><input type="checkbox" value="'+cmd+'" '+(enabled[cmd]? 'checked' : '')+'/>'+name+'</label></span>');
  25475. });
  25476. return $(tags.join(' ')).on('change', 'input', function() {
  25477. var v = $(this).val(),
  25478. o = $(this).is(':checked');
  25479. if (o) {
  25480. enabled[v] = true;
  25481. } else if (enabled[v]) {
  25482. delete enabled[v];
  25483. }
  25484. fm.storage('hashchekcer', $.grep(fm.hashCheckers, function(v) {
  25485. return enabled[v];
  25486. }));
  25487. });
  25488. })());
  25489. forms.autoFocusDialog && (forms.autoFocusDialog = $('<input type="checkbox"/>').prop('checked', (function() {
  25490. var s = fm.storage('autoFocusDialog');
  25491. return s? (s > 0) : fm.options.uiOptions.dialog.focusOnMouseOver;
  25492. })()).on('change', function(e) {
  25493. fm.storage('autoFocusDialog', $(this).is(':checked')? 1 : -1);
  25494. }));
  25495. forms.clearBrowserData && (forms.clearBrowserData = $('<button/>').text(fm.i18n('reset')).button().on('click', function(e) {
  25496. e.preventDefault();
  25497. fm.storage();
  25498. $('#'+fm.id).elfinder('reload');
  25499. }));
  25500. $.each(cats, function(id, prefs) {
  25501. var dls, found;
  25502. if (prefs === true) {
  25503. found = 1;
  25504. } else if (prefs) {
  25505. dls = $();
  25506. $.each(prefs, function(i, n) {
  25507. var f, title, chks = '', cbox;
  25508. if (f = forms[n]) {
  25509. found = 2;
  25510. title = fm.i18n(n);
  25511. cbox = $(f).filter('input[type="checkbox"]');
  25512. if (!cbox.length) {
  25513. cbox = $(f).find('input[type="checkbox"]');
  25514. }
  25515. if (cbox.length === 1) {
  25516. if (!cbox.attr('id')) {
  25517. cbox.attr('id', 'elfinder-preference-'+n+'-checkbox');
  25518. }
  25519. title = '<label for="'+cbox.attr('id')+'">'+title+'</label>';
  25520. } else if (cbox.length > 1) {
  25521. chks = ' elfinder-preference-checkboxes';
  25522. }
  25523. dls = dls.add($('<dt class="elfinder-preference-'+n+chks+'">'+title+'</dt>')).add($('<dd class="elfinder-preference-'+n+chks+'"/>').append(f));
  25524. }
  25525. });
  25526. }
  25527. if (found) {
  25528. ul.append(tab[r](/\{id\}/g, id)[r](/\{title\}/, fm.i18n(id))[r](/\{class\}/, openTab === id? 'elfinder-focus' : ''));
  25529. if (found === 2) {
  25530. tabs.append(
  25531. $('<div id="'+fm.namespace+'-preference-'+id+'" class="elfinder-preference-content"/>')
  25532. .hide()
  25533. .append($('<dl/>').append(dls))
  25534. );
  25535. }
  25536. }
  25537. });
  25538. ul.on('click', 'a', function(e) {
  25539. var t = $(e.target),
  25540. h = t.attr('href');
  25541. e.preventDefault();
  25542. e.stopPropagation();
  25543. ul.children().removeClass(clTabActive);
  25544. t.removeClass('ui-state-hover').parent().addClass(clTabActive);
  25545. if (h.match(/all$/)) {
  25546. tabs.addClass('elfinder-preference-taball').children().show();
  25547. } else {
  25548. tabs.removeClass('elfinder-preference-taball').children().hide();
  25549. $(h).show();
  25550. }
  25551. }).on('focus blur', 'a', function(e) {
  25552. $(this).parent().toggleClass('ui-state-focus', e.type === 'focusin');
  25553. }).on('mouseenter mouseleave', 'li', function(e) {
  25554. $(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
  25555. });
  25556. tabs.find('a,input,select,button').addClass('elfinder-tabstop');
  25557. base.append(ul, tabs);
  25558. dialog = self.fmDialog(base, {
  25559. title : self.title,
  25560. width : self.options.width || 600,
  25561. height: self.options.height || 400,
  25562. maxWidth: 'window',
  25563. maxHeight: 'window',
  25564. autoOpen : false,
  25565. destroyOnClose : false,
  25566. allowMinimize : false,
  25567. open : function() {
  25568. openTab && selectTab(openTab);
  25569. openTab = null;
  25570. },
  25571. resize : function() {
  25572. tabs.height(dialog.height() - ul.outerHeight(true) - (tabs.outerHeight(true) - tabs.height()) - 5);
  25573. }
  25574. })
  25575. .on('click', function(e) {
  25576. e.stopPropagation();
  25577. })
  25578. .css({
  25579. overflow: 'hidden'
  25580. });
  25581. dialog.closest('.ui-dialog')
  25582. .css({
  25583. overflow: 'hidden'
  25584. })
  25585. .addClass('elfinder-bg-translucent');
  25586. openTab = 'all';
  25587. },
  25588. dialog, openTab;
  25589. this.shortcuts = [{
  25590. pattern : 'ctrl+comma',
  25591. description : this.title
  25592. }];
  25593. this.alwaysEnabled = true;
  25594. this.getstate = function() {
  25595. return 0;
  25596. };
  25597. this.exec = function(sel, cOpts) {
  25598. !dialog && build();
  25599. if (cOpts) {
  25600. if (cOpts.tab) {
  25601. selectTab(cOpts.tab);
  25602. } else if (cOpts._currentType === 'cwd') {
  25603. selectTab('workspace');
  25604. }
  25605. }
  25606. dialog.elfinderdialog('open');
  25607. return $.Deferred().resolve();
  25608. };
  25609. };
  25610. /*
  25611. * File: /js/commands/quicklook.js
  25612. */
  25613. /**
  25614. * @class elFinder command "quicklook"
  25615. * Fast preview for some files types
  25616. *
  25617. * @author Dmitry (dio) Levashov
  25618. **/
  25619. (elFinder.prototype.commands.quicklook = function() {
  25620. var self = this,
  25621. fm = self.fm,
  25622. /**
  25623. * window closed state
  25624. *
  25625. * @type Number
  25626. **/
  25627. closed = 0,
  25628. /**
  25629. * window animated state
  25630. *
  25631. * @type Number
  25632. **/
  25633. animated = 1,
  25634. /**
  25635. * window opened state
  25636. *
  25637. * @type Number
  25638. **/
  25639. opened = 2,
  25640. /**
  25641. * window docked state
  25642. *
  25643. * @type Number
  25644. **/
  25645. docked = 3,
  25646. /**
  25647. * window docked and hidden state
  25648. *
  25649. * @type Number
  25650. **/
  25651. dockedhidden = 4,
  25652. /**
  25653. * window state
  25654. *
  25655. * @type Number
  25656. **/
  25657. state = closed,
  25658. /**
  25659. * Event name of update
  25660. * for fix conflicts with Prototype.JS
  25661. *
  25662. * `@see https://github.com/Studio-42/elFinder/pull/2346
  25663. * @type String
  25664. **/
  25665. evUpdate = Element.update? 'quicklookupdate' : 'update',
  25666. /**
  25667. * navbar icon class
  25668. *
  25669. * @type String
  25670. **/
  25671. navicon = 'elfinder-quicklook-navbar-icon',
  25672. /**
  25673. * navbar "fullscreen" icon class
  25674. *
  25675. * @type String
  25676. **/
  25677. fullscreen = 'elfinder-quicklook-fullscreen',
  25678. /**
  25679. * info wrapper class
  25680. *
  25681. * @type String
  25682. */
  25683. infocls = 'elfinder-quicklook-info-wrapper',
  25684. /**
  25685. * Triger keydown/keypress event with left/right arrow key code
  25686. *
  25687. * @param Number left/right arrow key code
  25688. * @return void
  25689. **/
  25690. navtrigger = function(code) {
  25691. $(document).trigger($.Event('keydown', { keyCode: code, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  25692. },
  25693. /**
  25694. * Return css for closed window
  25695. *
  25696. * @param jQuery file node in cwd
  25697. * @return void
  25698. **/
  25699. closedCss = function(node) {
  25700. var elf = fm.getUI().offset(),
  25701. base = (function() {
  25702. var target = node.find('.elfinder-cwd-file-wrapper');
  25703. return target.length? target : node;
  25704. })(),
  25705. baseOffset = base.offset() || { top: 0, left: 0 };
  25706. return {
  25707. opacity : 0,
  25708. width : base.width(),
  25709. height : base.height() - 30,
  25710. top : baseOffset.top - elf.top,
  25711. left : baseOffset.left - elf.left
  25712. };
  25713. },
  25714. /**
  25715. * Return css for opened window
  25716. *
  25717. * @return void
  25718. **/
  25719. openedCss = function() {
  25720. var contain = self.options.contain,
  25721. win = contain? fm.getUI() : $(window),
  25722. elf = fm.getUI().offset(),
  25723. w = Math.min(width, win.width()-10),
  25724. h = Math.min(height, win.height()-80);
  25725. return {
  25726. opacity : 1,
  25727. width : w,
  25728. height : h,
  25729. top : parseInt((win.height() - h - 60) / 2 + (contain? 0 : win.scrollTop() - elf.top)),
  25730. left : parseInt((win.width() - w) / 2 + (contain? 0 : win.scrollLeft() - elf.left))
  25731. };
  25732. },
  25733. mediaNode = {},
  25734. support = function(codec, name) {
  25735. var node = name || codec.substr(0, codec.indexOf('/')),
  25736. media = mediaNode[node]? mediaNode[node] : (mediaNode[node] = document.createElement(node)),
  25737. value = false;
  25738. try {
  25739. value = media.canPlayType && media.canPlayType(codec);
  25740. } catch(e) {}
  25741. return (value && value !== '' && value != 'no')? true : false;
  25742. },
  25743. platformWin = (window.navigator.platform.indexOf('Win') != -1),
  25744. /**
  25745. * Opened window width (from config)
  25746. *
  25747. * @type Number
  25748. **/
  25749. width,
  25750. /**
  25751. * Opened window height (from config)
  25752. *
  25753. * @type Number
  25754. **/
  25755. height,
  25756. /**
  25757. * Previous style before docked
  25758. *
  25759. * @type String
  25760. **/
  25761. prevStyle,
  25762. /**
  25763. * elFinder node
  25764. *
  25765. * @type jQuery
  25766. **/
  25767. parent,
  25768. /**
  25769. * elFinder current directory node
  25770. *
  25771. * @type jQuery
  25772. **/
  25773. cwd,
  25774. /**
  25775. * Current directory hash
  25776. *
  25777. * @type String
  25778. **/
  25779. cwdHash,
  25780. dockEnabled = false,
  25781. navdrag = false,
  25782. navmove = false,
  25783. navtm = null,
  25784. leftKey = $.ui.keyCode.LEFT,
  25785. rightKey = $.ui.keyCode.RIGHT,
  25786. coverEv = 'mousemove touchstart ' + ('onwheel' in document? 'wheel' : 'onmousewheel' in document? 'mousewheel' : 'DOMMouseScroll'),
  25787. title = $('<span class="elfinder-dialog-title elfinder-quicklook-title"/>'),
  25788. icon = $('<div/>'),
  25789. info = $('<div class="elfinder-quicklook-info"/>'),//.hide(),
  25790. cover = $('<div class="ui-front elfinder-quicklook-cover"/>'),
  25791. fsicon = $('<div class="'+navicon+' '+navicon+'-fullscreen"/>')
  25792. .on('click touchstart', function(e) {
  25793. if (navmove) {
  25794. return;
  25795. }
  25796. var win = self.window,
  25797. full = win.hasClass(fullscreen),
  25798. $window = $(window),
  25799. resize = function() { self.preview.trigger('changesize'); };
  25800. e.stopPropagation();
  25801. e.preventDefault();
  25802. if (full) {
  25803. navStyle = '';
  25804. navShow();
  25805. win.toggleClass(fullscreen)
  25806. .css(win.data('position'));
  25807. $window.trigger(self.resize).off(self.resize, resize);
  25808. navbar.off('mouseenter mouseleave');
  25809. cover.off(coverEv);
  25810. } else {
  25811. win.toggleClass(fullscreen)
  25812. .data('position', {
  25813. left : win.css('left'),
  25814. top : win.css('top'),
  25815. width : win.width(),
  25816. height : win.height(),
  25817. display: 'block'
  25818. })
  25819. .removeAttr('style');
  25820. $(window).on(self.resize, resize)
  25821. .trigger(self.resize);
  25822. cover.on(coverEv, function(e) {
  25823. if (! navdrag) {
  25824. if (e.type === 'mousemove' || e.type === 'touchstart') {
  25825. navShow();
  25826. navtm = setTimeout(function() {
  25827. if (fm.UA.Mobile || navbar.parent().find('.elfinder-quicklook-navbar:hover').length < 1) {
  25828. navbar.fadeOut('slow', function() {
  25829. cover.show();
  25830. });
  25831. }
  25832. }, 3000);
  25833. }
  25834. if (cover.is(':visible')) {
  25835. coverHide();
  25836. cover.data('tm', setTimeout(function() {
  25837. cover.show();
  25838. }, 3000));
  25839. }
  25840. }
  25841. }).show().trigger('mousemove');
  25842. navbar.on('mouseenter mouseleave', function(e) {
  25843. if (! navdrag) {
  25844. if (e.type === 'mouseenter') {
  25845. navShow();
  25846. } else {
  25847. cover.trigger('mousemove');
  25848. }
  25849. }
  25850. });
  25851. }
  25852. if (fm.zIndex) {
  25853. win.css('z-index', fm.zIndex + 1);
  25854. }
  25855. if (fm.UA.Mobile) {
  25856. navbar.attr('style', navStyle);
  25857. } else {
  25858. navbar.attr('style', navStyle).draggable(full ? 'destroy' : {
  25859. start: function() {
  25860. navdrag = true;
  25861. navmove = true;
  25862. cover.show();
  25863. navShow();
  25864. },
  25865. stop: function() {
  25866. navdrag = false;
  25867. navStyle = self.navbar.attr('style');
  25868. requestAnimationFrame(function() {
  25869. navmove = false;
  25870. });
  25871. }
  25872. });
  25873. }
  25874. $(this).toggleClass(navicon+'-fullscreen-off');
  25875. var collection = win;
  25876. if (parent.is('.ui-resizable')) {
  25877. collection = collection.add(parent);
  25878. }
  25879. collection.resizable(full ? 'enable' : 'disable').removeClass('ui-state-disabled');
  25880. win.trigger('viewchange');
  25881. }
  25882. ),
  25883. updateOnSel = function() {
  25884. self.update(void(0), (function() {
  25885. var fm = self.fm,
  25886. files = fm.selectedFiles(),
  25887. cnt = files.length,
  25888. inDock = self.docked(),
  25889. getInfo = function() {
  25890. var ts = 0;
  25891. $.each(files, function(i, f) {
  25892. var t = parseInt(f.ts);
  25893. if (ts >= 0) {
  25894. if (t > ts) {
  25895. ts = t;
  25896. }
  25897. } else {
  25898. ts = 'unknown';
  25899. }
  25900. });
  25901. return {
  25902. hash : files[0].hash + '/' + (+new Date()),
  25903. name : fm.i18n('items') + ': ' + cnt,
  25904. mime : 'group',
  25905. size : spinner,
  25906. ts : ts,
  25907. files : $.map(files, function(f) { return f.hash; }),
  25908. getSize : true
  25909. };
  25910. };
  25911. if (! cnt) {
  25912. cnt = 1;
  25913. files = [fm.cwd()];
  25914. }
  25915. return (cnt === 1)? files[0] : getInfo();
  25916. })());
  25917. },
  25918. navShow = function() {
  25919. if (self.window.hasClass(fullscreen)) {
  25920. navtm && clearTimeout(navtm);
  25921. navtm = null;
  25922. // if use `show()` it make infinite loop with old jQuery (jQuery/jQuery UI: 1.8.0/1.9.0)
  25923. // see #1478 https://github.com/Studio-42/elFinder/issues/1478
  25924. navbar.stop(true, true).css('display', 'block');
  25925. coverHide();
  25926. }
  25927. },
  25928. coverHide = function() {
  25929. cover.data('tm') && clearTimeout(cover.data('tm'));
  25930. cover.removeData('tm');
  25931. cover.hide();
  25932. },
  25933. prev = $('<div class="'+navicon+' '+navicon+'-prev"/>').on('click touchstart', function(e) { ! navmove && navtrigger(leftKey); return false; }),
  25934. next = $('<div class="'+navicon+' '+navicon+'-next"/>').on('click touchstart', function(e) { ! navmove && navtrigger(rightKey); return false; }),
  25935. navbar = $('<div class="elfinder-quicklook-navbar"/>')
  25936. .append(prev)
  25937. .append(fsicon)
  25938. .append(next)
  25939. .append('<div class="elfinder-quicklook-navbar-separator"/>')
  25940. .append($('<div class="'+navicon+' '+navicon+'-close"/>').on('click touchstart', function(e) { ! navmove && self.window.trigger('close'); return false; }))
  25941. ,
  25942. titleClose = $('<span class="ui-front ui-icon elfinder-icon-close ui-icon-closethick"/>').on('mousedown', function(e) {
  25943. e.stopPropagation();
  25944. self.window.trigger('close');
  25945. }),
  25946. titleDock = $('<span class="ui-front ui-icon elfinder-icon-minimize ui-icon-minusthick"/>').on('mousedown', function(e) {
  25947. e.stopPropagation();
  25948. if (! self.docked()) {
  25949. self.window.trigger('navdockin');
  25950. } else {
  25951. self.window.trigger('navdockout');
  25952. }
  25953. }),
  25954. spinner = '<span class="elfinder-spinner-text">' + fm.i18n('calc') + '</span>' + '<span class="elfinder-spinner"/>',
  25955. navStyle = '',
  25956. init = true,
  25957. dockHeight, getSize, tm4cwd, dockedNode, selectTm;
  25958. this.cover = cover;
  25959. this.evUpdate = evUpdate;
  25960. (this.navbar = navbar)._show = navShow;
  25961. this.resize = 'resize.'+fm.namespace;
  25962. this.info = $('<div/>').addClass(infocls)
  25963. .append(icon)
  25964. .append(info);
  25965. this.autoPlay = function() {
  25966. if (self.opened()) {
  25967. return !! self.options[self.docked()? 'dockAutoplay' : 'autoplay'];
  25968. }
  25969. return false;
  25970. };
  25971. this.preview = $('<div class="elfinder-quicklook-preview ui-helper-clearfix"/>')
  25972. // clean info/icon
  25973. .on('change', function() {
  25974. navShow();
  25975. navbar.attr('style', navStyle);
  25976. self.docked() && navbar.hide();
  25977. self.preview.attr('style', '').removeClass('elfinder-overflow-auto');
  25978. self.info.attr('style', '').hide();
  25979. self.cover.removeClass('elfinder-quicklook-coverbg');
  25980. icon.removeAttr('class').attr('style', '');
  25981. info.html('');
  25982. })
  25983. // update info/icon
  25984. .on(evUpdate, function(e) {
  25985. var preview = self.preview,
  25986. file = e.file,
  25987. tpl = '<div class="elfinder-quicklook-info-data">{value}</div>',
  25988. update = function() {
  25989. var win = self.window.css('overflow', 'hidden');
  25990. name = fm.escape(file.i18 || file.name);
  25991. !file.read && e.stopImmediatePropagation();
  25992. self.window.data('hash', file.hash);
  25993. self.preview.off('changesize').trigger('change').children().remove();
  25994. title.html(name);
  25995. prev.css('visibility', '');
  25996. next.css('visibility', '');
  25997. if (file.hash === fm.cwdId2Hash(cwd.find('[id]:not(.elfinder-cwd-parent):first').attr('id'))) {
  25998. prev.css('visibility', 'hidden');
  25999. }
  26000. if (file.hash === fm.cwdId2Hash(cwd.find('[id]:last').attr('id'))) {
  26001. next.css('visibility', 'hidden');
  26002. }
  26003. if (file.mime === 'directory') {
  26004. getSizeHashes = [ file.hash ];
  26005. } else if (file.mime === 'group' && file.getSize) {
  26006. getSizeHashes = file.files;
  26007. }
  26008. info.html(
  26009. tpl.replace(/\{value\}/, name)
  26010. + tpl.replace(/\{value\}/, fm.mime2kind(file))
  26011. + tpl.replace(/\{value\}/, getSizeHashes.length ? spinner : fm.formatSize(file.size))
  26012. + tpl.replace(/\{value\}/, fm.i18n('modify')+': '+ fm.formatDate(file))
  26013. );
  26014. if (getSizeHashes.length) {
  26015. getSize = fm.getSize(getSizeHashes).done(function(data) {
  26016. info.find('span.elfinder-spinner').parent().html(data.formated);
  26017. }).fail(function() {
  26018. info.find('span.elfinder-spinner').parent().html(fm.i18n('unknown'));
  26019. }).always(function() {
  26020. getSize = null;
  26021. });
  26022. getSize._hash = file.hash;
  26023. }
  26024. icon.addClass('elfinder-cwd-icon ui-corner-all '+fm.mime2class(file.mime));
  26025. if (file.icon) {
  26026. icon.css(fm.getIconStyle(file, true));
  26027. }
  26028. self.info.attr('class', infocls);
  26029. if (file.csscls) {
  26030. self.info.addClass(file.csscls);
  26031. }
  26032. if (file.read && (tmb = fm.tmb(file))) {
  26033. $('<img/>')
  26034. .hide()
  26035. .appendTo(self.preview)
  26036. .on('load', function() {
  26037. icon.addClass(tmb.className).css('background-image', "url('"+tmb.url+"')");
  26038. $(this).remove();
  26039. })
  26040. .attr('src', tmb.url);
  26041. }
  26042. self.info.delay(100).fadeIn(10);
  26043. if (self.window.hasClass(fullscreen)) {
  26044. cover.trigger('mousemove');
  26045. }
  26046. win.css('overflow', '');
  26047. },
  26048. tmb, name, getSizeHashes = [];
  26049. if (file && ! Object.keys(file).length) {
  26050. file = fm.cwd();
  26051. }
  26052. if (file && getSize && getSize.state() === 'pending' && getSize._hash !== file.hash) {
  26053. getSize.reject();
  26054. }
  26055. if (file && (e.forceUpdate || self.window.data('hash') !== file.hash)) {
  26056. update();
  26057. } else {
  26058. e.stopImmediatePropagation();
  26059. }
  26060. });
  26061. this.window = $('<div class="ui-front ui-helper-reset ui-widget elfinder-quicklook touch-punch" style="position:absolute"/>')
  26062. .hide()
  26063. .addClass(fm.UA.Touch? 'elfinder-touch' : '')
  26064. .on('click', function(e) {
  26065. var win = this;
  26066. e.stopPropagation();
  26067. if (state === opened) {
  26068. requestAnimationFrame(function() {
  26069. state === opened && fm.toFront(win);
  26070. });
  26071. }
  26072. })
  26073. .append(
  26074. $('<div class="ui-dialog-titlebar ui-widget-header ui-corner-top ui-helper-clearfix elfinder-quicklook-titlebar"/>')
  26075. .append(
  26076. $('<span class="ui-widget-header ui-dialog-titlebar-close ui-corner-all elfinder-titlebar-button elfinder-quicklook-titlebar-icon'+(platformWin? ' elfinder-titlebar-button-right' : '')+'"/>').append(
  26077. titleClose, titleDock
  26078. ),
  26079. title
  26080. ),
  26081. this.preview,
  26082. self.info.hide(),
  26083. cover.hide(),
  26084. navbar
  26085. )
  26086. .draggable({handle : 'div.elfinder-quicklook-titlebar'})
  26087. .on('open', function(e, clcss) {
  26088. var win = self.window,
  26089. file = self.value,
  26090. node = fm.getUI('cwd'),
  26091. open = function(status) {
  26092. state = status;
  26093. self.update(1, self.value);
  26094. self.change();
  26095. win.trigger('resize.' + fm.namespace);
  26096. };
  26097. if (!init && state === closed) {
  26098. if (file && file.hash !== cwdHash) {
  26099. node = fm.cwdHash2Elm(file.hash.split('/', 2)[0]);
  26100. }
  26101. navStyle = '';
  26102. navbar.attr('style', '');
  26103. state = animated;
  26104. node.trigger('scrolltoview');
  26105. coverHide();
  26106. win.css(clcss || closedCss(node))
  26107. .show()
  26108. .animate(openedCss(), 550, function() {
  26109. open(opened);
  26110. navShow();
  26111. });
  26112. fm.toFront(win);
  26113. } else if (state === dockedhidden) {
  26114. fm.getUI('navdock').data('addNode')(dockedNode);
  26115. open(docked);
  26116. self.preview.trigger('changesize');
  26117. fm.storage('previewDocked', '1');
  26118. if (fm.getUI('navdock').width() === 0) {
  26119. win.trigger('navdockout');
  26120. }
  26121. }
  26122. })
  26123. .on('close', function(e, dfd) {
  26124. var win = self.window,
  26125. preview = self.preview.trigger('change'),
  26126. file = self.value,
  26127. hash = (win.data('hash') || '').split('/', 2)[0],
  26128. close = function(status, winhide) {
  26129. state = status;
  26130. winhide && fm.toHide(win);
  26131. preview.children().remove();
  26132. self.update(0, self.value);
  26133. win.data('hash', '');
  26134. dfd && dfd.resolve();
  26135. },
  26136. node;
  26137. if (self.opened()) {
  26138. getSize && getSize.state() === 'pending' && getSize.reject();
  26139. if (! self.docked()) {
  26140. state = animated;
  26141. win.hasClass(fullscreen) && fsicon.click();
  26142. (hash && (node = cwd.find('#'+hash)).length)
  26143. ? win.animate(closedCss(node), 500, function() { close(closed, true); })
  26144. : close(closed, true);
  26145. } else {
  26146. dockedNode = fm.getUI('navdock').data('removeNode')(self.window.attr('id'), 'detach');
  26147. close(dockedhidden);
  26148. fm.storage('previewDocked', '2');
  26149. }
  26150. }
  26151. })
  26152. .on('navdockin', function(e, data) {
  26153. var w = self.window,
  26154. box = fm.getUI('navdock'),
  26155. height = dockHeight || box.width(),
  26156. opts = data || {};
  26157. if (init) {
  26158. opts.init = true;
  26159. }
  26160. state = docked;
  26161. prevStyle = w.attr('style');
  26162. w.toggleClass('ui-front').removeClass('ui-widget').draggable('disable').resizable('disable').removeAttr('style').css({
  26163. width: '100%',
  26164. height: height,
  26165. boxSizing: 'border-box',
  26166. paddingBottom: 0,
  26167. zIndex: 'unset'
  26168. });
  26169. navbar.hide();
  26170. titleDock.toggleClass('ui-icon-plusthick ui-icon-minusthick elfinder-icon-full elfinder-icon-minimize');
  26171. fm.toHide(w, true);
  26172. box.data('addNode')(w, opts);
  26173. self.preview.trigger('changesize');
  26174. fm.storage('previewDocked', '1');
  26175. })
  26176. .on('navdockout', function(e) {
  26177. var w = self.window,
  26178. box = fm.getUI('navdock'),
  26179. dfd = $.Deferred(),
  26180. clcss = closedCss(self.preview);
  26181. dockHeight = w.outerHeight();
  26182. box.data('removeNode')(w.attr('id'), fm.getUI());
  26183. w.toggleClass('ui-front').addClass('ui-widget').draggable('enable').resizable('enable').attr('style', prevStyle);
  26184. titleDock.toggleClass('ui-icon-plusthick ui-icon-minusthick elfinder-icon-full elfinder-icon-minimize');
  26185. state = closed;
  26186. w.trigger('open', clcss);
  26187. fm.storage('previewDocked', '0');
  26188. })
  26189. .on('resize.' + fm.namespace, function() {
  26190. self.preview.trigger('changesize');
  26191. });
  26192. /**
  26193. * This command cannot be disable by backend
  26194. *
  26195. * @type Boolean
  26196. **/
  26197. this.alwaysEnabled = true;
  26198. /**
  26199. * Selected file
  26200. *
  26201. * @type Object
  26202. **/
  26203. this.value = null;
  26204. this.handlers = {
  26205. // save selected file
  26206. select : function(e, d) {
  26207. selectTm && cancelAnimationFrame(selectTm);
  26208. if (! e.data || ! e.data.selected || ! e.data.selected.length) {
  26209. selectTm = requestAnimationFrame(function() {
  26210. self.opened() && updateOnSel();
  26211. });
  26212. } else {
  26213. self.opened() && updateOnSel();
  26214. }
  26215. },
  26216. error : function() { self.window.is(':visible') && self.window.trigger('close'); },
  26217. 'searchshow searchhide' : function() { this.opened() && this.window.trigger('close'); },
  26218. navbarshow : function() {
  26219. requestAnimationFrame(function() {
  26220. self.docked() && self.preview.trigger('changesize');
  26221. });
  26222. },
  26223. destroy : function() { self.window.remove(); }
  26224. };
  26225. this.shortcuts = [{
  26226. pattern : 'space'
  26227. }];
  26228. this.support = {
  26229. audio : {
  26230. ogg : support('audio/ogg;'),
  26231. webm: support('audio/webm;'),
  26232. mp3 : support('audio/mpeg;'),
  26233. wav : support('audio/wav;'),
  26234. m4a : support('audio/mp4;') || support('audio/x-m4a;') || support('audio/aac;'),
  26235. flac: support('audio/flac;'),
  26236. amr : support('audio/amr;')
  26237. },
  26238. video : {
  26239. ogg : support('video/ogg;'),
  26240. webm : support('video/webm;'),
  26241. mp4 : support('video/mp4;'),
  26242. mkv : support('video/x-matroska;') || support('video/webm;'),
  26243. '3gp': support('video/3gpp;') || support('video/mp4;'), // try as mp4
  26244. m3u8 : support('application/x-mpegURL', 'video') || support('application/vnd.apple.mpegURL', 'video'),
  26245. mpd : support('application/dash+xml', 'video')
  26246. }
  26247. };
  26248. // for GC
  26249. mediaNode = {};
  26250. /**
  26251. * Return true if quickLoock window is hiddenReturn true if quickLoock window is visible and not animated
  26252. *
  26253. * @return Boolean
  26254. **/
  26255. this.closed = function() {
  26256. return (state == closed || state == dockedhidden);
  26257. };
  26258. /**
  26259. * Return true if quickLoock window is visible and not animated
  26260. *
  26261. * @return Boolean
  26262. **/
  26263. this.opened = function() {
  26264. return state == opened || state == docked;
  26265. };
  26266. /**
  26267. * Return true if quickLoock window is in NavDock
  26268. *
  26269. * @return Boolean
  26270. **/
  26271. this.docked = function() {
  26272. return state == docked;
  26273. };
  26274. /**
  26275. * Adds an integration into help dialog.
  26276. *
  26277. * @param Object opts options
  26278. */
  26279. this.addIntegration = function(opts) {
  26280. requestAnimationFrame(function() {
  26281. fm.trigger('helpIntegration', Object.assign({cmd: 'quicklook'}, opts));
  26282. });
  26283. };
  26284. /**
  26285. * Init command.
  26286. * Add default plugins and init other plugins
  26287. *
  26288. * @return Object
  26289. **/
  26290. this.init = function() {
  26291. var o = this.options,
  26292. win = this.window,
  26293. preview = this.preview,
  26294. i, p, cwdDispInlineRegex;
  26295. width = o.width > 0 ? parseInt(o.width) : 450;
  26296. height = o.height > 0 ? parseInt(o.height) : 300;
  26297. if (o.dockHeight !== 'auto') {
  26298. dockHeight = parseInt(o.dockHeight);
  26299. if (! dockHeight) {
  26300. dockHeight = void(0);
  26301. }
  26302. }
  26303. fm.one('load', function() {
  26304. dockEnabled = fm.getUI('navdock').data('dockEnabled');
  26305. ! dockEnabled && titleDock.hide();
  26306. parent = fm.getUI();
  26307. cwd = fm.getUI('cwd');
  26308. if (fm.zIndex) {
  26309. win.css('z-index', fm.zIndex + 1);
  26310. }
  26311. win.appendTo(parent);
  26312. // close window on escape
  26313. $(document).on('keydown.'+fm.namespace, function(e) {
  26314. e.keyCode == $.ui.keyCode.ESCAPE && self.opened() && ! self.docked() && win.hasClass('elfinder-frontmost') && win.trigger('close');
  26315. });
  26316. win.resizable({
  26317. handles : 'se',
  26318. minWidth : 350,
  26319. minHeight : 120,
  26320. resize : function() {
  26321. // use another event to avoid recursion in fullscreen mode
  26322. // may be there is clever solution, but i cant find it :(
  26323. preview.trigger('changesize');
  26324. }
  26325. });
  26326. self.change(function() {
  26327. if (self.opened()) {
  26328. if (self.value) {
  26329. if (self.value.tmb && self.value.tmb == 1) {
  26330. // try re-get file object
  26331. self.value = Object.assign({}, fm.file(self.value.hash));
  26332. }
  26333. preview.trigger($.Event(evUpdate, {file : self.value}));
  26334. }
  26335. }
  26336. });
  26337. preview.on(evUpdate, function(e) {
  26338. var file, hash, serach;
  26339. if (file = e.file) {
  26340. hash = file.hash;
  26341. serach = (fm.searchStatus.mixed && fm.searchStatus.state > 1);
  26342. if (file.mime !== 'directory') {
  26343. if (parseInt(file.size) || file.mime.match(o.mimeRegexNotEmptyCheck)) {
  26344. // set current dispInlineRegex
  26345. self.dispInlineRegex = cwdDispInlineRegex;
  26346. if (serach || fm.optionsByHashes[hash]) {
  26347. try {
  26348. self.dispInlineRegex = new RegExp(fm.option('dispInlineRegex', hash), 'i');
  26349. } catch(e) {
  26350. try {
  26351. self.dispInlineRegex = new RegExp(!fm.isRoot(file)? fm.option('dispInlineRegex', file.phash) : fm.options.dispInlineRegex, 'i');
  26352. } catch(e) {
  26353. self.dispInlineRegex = /^$/;
  26354. }
  26355. }
  26356. }
  26357. } else {
  26358. // do not preview of file that size = 0
  26359. e.stopImmediatePropagation();
  26360. }
  26361. } else {
  26362. self.dispInlineRegex = /^$/;
  26363. }
  26364. self.info.show();
  26365. } else {
  26366. e.stopImmediatePropagation();
  26367. }
  26368. });
  26369. $.each(fm.commands.quicklook.plugins || [], function(i, plugin) {
  26370. if (typeof(plugin) == 'function') {
  26371. new plugin(self);
  26372. }
  26373. });
  26374. }).one('open', function() {
  26375. var dock = Number(fm.storage('previewDocked') || o.docked),
  26376. win;
  26377. if (dockEnabled && dock >= 1) {
  26378. win = self.window;
  26379. self.exec();
  26380. win.trigger('navdockin', { init : true });
  26381. if (dock === 2) {
  26382. win.trigger('close');
  26383. } else {
  26384. self.update(void(0), fm.cwd());
  26385. self.change();
  26386. }
  26387. }
  26388. init = false;
  26389. }).bind('open', function() {
  26390. cwdHash = fm.cwd().hash;
  26391. self.value = fm.cwd();
  26392. // set current volume dispInlineRegex
  26393. try {
  26394. cwdDispInlineRegex = new RegExp(fm.option('dispInlineRegex'), 'i');
  26395. } catch(e) {
  26396. cwdDispInlineRegex = /^$/;
  26397. }
  26398. }).bind('change', function(e) {
  26399. if (e.data && e.data.changed && self.opened()) {
  26400. $.each(e.data.changed, function() {
  26401. if (self.window.data('hash') === this.hash) {
  26402. self.window.data('hash', null);
  26403. self.preview.trigger(evUpdate);
  26404. return false;
  26405. }
  26406. });
  26407. }
  26408. }).bind('navdockresizestart navdockresizestop', function(e) {
  26409. cover[e.type === 'navdockresizestart'? 'show' : 'hide']();
  26410. });
  26411. };
  26412. this.getstate = function() {
  26413. return self.opened()? 1 : 0;
  26414. };
  26415. this.exec = function() {
  26416. self.closed() && updateOnSel();
  26417. self.enabled() && self.window.trigger(self.opened() ? 'close' : 'open');
  26418. return $.Deferred().resolve();
  26419. };
  26420. this.hideinfo = function() {
  26421. this.info.stop(true, true).hide();
  26422. };
  26423. }).prototype = { forceLoad : true }; // this is required command
  26424. /*
  26425. * File: /js/commands/quicklook.plugins.js
  26426. */
  26427. elFinder.prototype.commands.quicklook.plugins = [
  26428. /**
  26429. * Images preview plugin
  26430. *
  26431. * @param elFinder.commands.quicklook
  26432. **/
  26433. function(ql) {
  26434. var mimes = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/x-ms-bmp'],
  26435. preview = ql.preview,
  26436. WebP, flipMime;
  26437. // webp support
  26438. WebP = new Image();
  26439. WebP.onload = WebP.onerror = function() {
  26440. if (WebP.height == 2) {
  26441. mimes.push('image/webp');
  26442. }
  26443. };
  26444. WebP.src='data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
  26445. // what kind of images we can display
  26446. $.each(navigator.mimeTypes, function(i, o) {
  26447. var mime = o.type;
  26448. if (mime.indexOf('image/') === 0 && $.inArray(mime, mimes)) {
  26449. mimes.push(mime);
  26450. }
  26451. });
  26452. preview.on(ql.evUpdate, function(e) {
  26453. var fm = ql.fm,
  26454. file = e.file,
  26455. showed = false,
  26456. dimreq = null,
  26457. setdim = function(dim) {
  26458. var rfile = fm.file(file.hash);
  26459. rfile.width = dim[0];
  26460. rfile.height = dim[1];
  26461. },
  26462. show = function() {
  26463. var elm, varelm, memSize, width, height, prop;
  26464. dimreq && dimreq.state && dimreq.state() === 'pending' && dimreq.reject();
  26465. if (showed) {
  26466. return;
  26467. }
  26468. showed = true;
  26469. elm = img.get(0);
  26470. memSize = file.width && file.height? {w: file.width, h: file.height} : (elm.naturalWidth? null : {w: img.width(), h: img.height()});
  26471. memSize && img.removeAttr('width').removeAttr('height');
  26472. width = file.width || elm.naturalWidth || elm.width || img.width();
  26473. height = file.height || elm.naturalHeight || elm.height || img.height();
  26474. if (!file.width || !file.height) {
  26475. setdim([width, height]);
  26476. }
  26477. memSize && img.width(memSize.w).height(memSize.h);
  26478. prop = (width/height).toFixed(2);
  26479. preview.on('changesize', function() {
  26480. var pw = parseInt(preview.width()),
  26481. ph = parseInt(preview.height()),
  26482. w, h;
  26483. if (prop < (pw/ph).toFixed(2)) {
  26484. h = ph;
  26485. w = Math.floor(h * prop);
  26486. } else {
  26487. w = pw;
  26488. h = Math.floor(w/prop);
  26489. }
  26490. img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0);
  26491. })
  26492. .trigger('changesize');
  26493. //show image
  26494. img.fadeIn(100);
  26495. },
  26496. hideInfo = function() {
  26497. loading.remove();
  26498. // hide info/icon
  26499. ql.hideinfo();
  26500. },
  26501. url, img, loading, m;
  26502. if (!flipMime) {
  26503. flipMime = fm.arrayFlip(mimes);
  26504. }
  26505. if (flipMime[file.mime] && ql.dispInlineRegex.test(file.mime)) {
  26506. // this is our file - stop event propagation
  26507. e.stopImmediatePropagation();
  26508. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  26509. url = fm.openUrl(file.hash);
  26510. img = $('<img/>')
  26511. .hide()
  26512. .appendTo(preview)
  26513. .on('load', function() {
  26514. hideInfo();
  26515. show();
  26516. })
  26517. .on('error', function() {
  26518. loading.remove();
  26519. })
  26520. .attr('src', url);
  26521. if (file.width && file.height) {
  26522. show();
  26523. } else if (file.size > (ql.options.getDimThreshold || 0)) {
  26524. dimreq = fm.request({
  26525. data : {cmd : 'dim', target : file.hash},
  26526. preventDefault : true
  26527. })
  26528. .done(function(data) {
  26529. if (data.dim) {
  26530. var dim = data.dim.split('x');
  26531. file.width = dim[0];
  26532. file.height = dim[1];
  26533. setdim(dim);
  26534. show();
  26535. }
  26536. });
  26537. }
  26538. }
  26539. });
  26540. },
  26541. /**
  26542. * PSD(Adobe Photoshop data) preview plugin
  26543. *
  26544. * @param elFinder.commands.quicklook
  26545. **/
  26546. function(ql) {
  26547. var fm = ql.fm,
  26548. mimes = fm.arrayFlip(['image/vnd.adobe.photoshop', 'image/x-photoshop']),
  26549. preview = ql.preview,
  26550. load = function(url, img, loading) {
  26551. try {
  26552. fm.replaceXhrSend();
  26553. PSD.fromURL(url).then(function(psd) {
  26554. var prop;
  26555. img.attr('src', psd.image.toBase64());
  26556. requestAnimationFrame(function() {
  26557. prop = (img.width()/img.height()).toFixed(2);
  26558. preview.on('changesize', function() {
  26559. var pw = parseInt(preview.width()),
  26560. ph = parseInt(preview.height()),
  26561. w, h;
  26562. if (prop < (pw/ph).toFixed(2)) {
  26563. h = ph;
  26564. w = Math.floor(h * prop);
  26565. } else {
  26566. w = pw;
  26567. h = Math.floor(w/prop);
  26568. }
  26569. img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0);
  26570. }).trigger('changesize');
  26571. loading.remove();
  26572. // hide info/icon
  26573. ql.hideinfo();
  26574. //show image
  26575. img.fadeIn(100);
  26576. });
  26577. }, function() {
  26578. loading.remove();
  26579. img.remove();
  26580. });
  26581. fm.restoreXhrSend();
  26582. } catch(e) {
  26583. fm.restoreXhrSend();
  26584. loading.remove();
  26585. img.remove();
  26586. }
  26587. },
  26588. PSD;
  26589. preview.on(ql.evUpdate, function(e) {
  26590. var file = e.file,
  26591. url, img, loading, m,
  26592. _define, _require;
  26593. if (mimes[file.mime] && fm.options.cdns.psd && ! fm.UA.ltIE10 && ql.dispInlineRegex.test(file.mime)) {
  26594. // this is our file - stop event propagation
  26595. e.stopImmediatePropagation();
  26596. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  26597. url = fm.openUrl(file.hash);
  26598. if (!fm.isSameOrigin(url)) {
  26599. url = fm.openUrl(file.hash, true);
  26600. }
  26601. img = $('<img/>').hide().appendTo(preview);
  26602. if (PSD) {
  26603. load(url, img, loading);
  26604. } else {
  26605. _define = window.define;
  26606. _require = window.require;
  26607. window.require = null;
  26608. window.define = null;
  26609. fm.loadScript(
  26610. [ fm.options.cdns.psd ],
  26611. function() {
  26612. PSD = require('psd');
  26613. _define? (window.define = _define) : (delete window.define);
  26614. _require? (window.require = _require) : (delete window.require);
  26615. load(url, img, loading);
  26616. }
  26617. );
  26618. }
  26619. }
  26620. });
  26621. },
  26622. /**
  26623. * HTML preview plugin
  26624. *
  26625. * @param elFinder.commands.quicklook
  26626. **/
  26627. function(ql) {
  26628. var fm = ql.fm,
  26629. mimes = fm.arrayFlip(['text/html', 'application/xhtml+xml']),
  26630. preview = ql.preview;
  26631. preview.on(ql.evUpdate, function(e) {
  26632. var file = e.file, jqxhr, loading;
  26633. if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) {
  26634. e.stopImmediatePropagation();
  26635. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  26636. // stop loading on change file if not loaded yet
  26637. preview.one('change', function() {
  26638. jqxhr.state() == 'pending' && jqxhr.reject();
  26639. }).addClass('elfinder-overflow-auto');
  26640. jqxhr = fm.request({
  26641. data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts},
  26642. options : {type: 'get', cache : true},
  26643. preventDefault : true
  26644. })
  26645. .done(function(data) {
  26646. ql.hideinfo();
  26647. var doc = $('<iframe class="elfinder-quicklook-preview-html"/>').appendTo(preview)[0].contentWindow.document;
  26648. doc.open();
  26649. doc.write(data.content);
  26650. doc.close();
  26651. })
  26652. .always(function() {
  26653. loading.remove();
  26654. });
  26655. }
  26656. });
  26657. },
  26658. /**
  26659. * MarkDown preview plugin
  26660. *
  26661. * @param elFinder.commands.quicklook
  26662. **/
  26663. function(ql) {
  26664. var fm = ql.fm,
  26665. mimes = fm.arrayFlip(['text/x-markdown']),
  26666. preview = ql.preview,
  26667. marked = null,
  26668. show = function(data, loading) {
  26669. ql.hideinfo();
  26670. var doc = $('<iframe class="elfinder-quicklook-preview-html"/>').appendTo(preview)[0].contentWindow.document;
  26671. doc.open();
  26672. doc.write(marked(data.content));
  26673. doc.close();
  26674. loading.remove();
  26675. },
  26676. error = function(loading) {
  26677. marked = false;
  26678. loading.remove();
  26679. };
  26680. preview.on(ql.evUpdate, function(e) {
  26681. var file = e.file, jqxhr, loading;
  26682. if (mimes[file.mime] && fm.options.cdns.marked && marked !== false && ql.dispInlineRegex.test(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) {
  26683. e.stopImmediatePropagation();
  26684. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  26685. // stop loading on change file if not loaded yet
  26686. preview.one('change', function() {
  26687. jqxhr.state() == 'pending' && jqxhr.reject();
  26688. }).addClass('elfinder-overflow-auto');
  26689. jqxhr = fm.request({
  26690. data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts},
  26691. options : {type: 'get', cache : true},
  26692. preventDefault : true
  26693. })
  26694. .done(function(data) {
  26695. if (marked || window.marked) {
  26696. if (!marked) {
  26697. marked = window.marked;
  26698. }
  26699. show(data, loading);
  26700. } else {
  26701. fm.loadScript([fm.options.cdns.marked],
  26702. function(res) {
  26703. marked = res || window.marked || false;
  26704. delete window.marked;
  26705. if (marked) {
  26706. show(data, loading);
  26707. } else {
  26708. error(loading);
  26709. }
  26710. },
  26711. {
  26712. tryRequire: true,
  26713. error: function() {
  26714. error(loading);
  26715. }
  26716. }
  26717. );
  26718. }
  26719. })
  26720. .fail(function() {
  26721. error(loading);
  26722. });
  26723. }
  26724. });
  26725. },
  26726. /**
  26727. * PDF/ODT/ODS/ODP preview with ViewerJS
  26728. *
  26729. * @param elFinder.commands.quicklook
  26730. */
  26731. function(ql) {
  26732. if (ql.options.viewerjs) {
  26733. var fm = ql.fm,
  26734. preview = ql.preview,
  26735. opts = ql.options.viewerjs,
  26736. mimes = opts.url? fm.arrayFlip(opts.mimes || []) : [];
  26737. if (opts.url) {
  26738. preview.on('update', function(e) {
  26739. var win = ql.window,
  26740. file = e.file, node, loading;
  26741. if (mimes[file.mime]) {
  26742. var url = fm.openUrl(file.hash);
  26743. if (url && fm.isSameOrigin(url)) {
  26744. e.stopImmediatePropagation();
  26745. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  26746. node = $('<iframe class="elfinder-quicklook-preview-iframe"/>')
  26747. .css('background-color', 'transparent')
  26748. .on('load', function() {
  26749. ql.hideinfo();
  26750. loading.remove();
  26751. node.css('background-color', '#fff');
  26752. })
  26753. .on('error', function() {
  26754. loading.remove();
  26755. node.remove();
  26756. })
  26757. .appendTo(preview)
  26758. .attr('src', opts.url + '#' + url);
  26759. preview.one('change', function() {
  26760. loading.remove();
  26761. node.off('load').remove();
  26762. });
  26763. }
  26764. }
  26765. });
  26766. }
  26767. }
  26768. },
  26769. /**
  26770. * PDF preview plugin
  26771. *
  26772. * @param elFinder.commands.quicklook
  26773. **/
  26774. function(ql) {
  26775. var fm = ql.fm,
  26776. mime = 'application/pdf',
  26777. preview = ql.preview,
  26778. active = false,
  26779. urlhash = '',
  26780. firefox, toolbar;
  26781. if ((fm.UA.Safari && fm.OS === 'mac' && !fm.UA.iOS) || fm.UA.IE || fm.UA.Firefox) {
  26782. active = true;
  26783. } else {
  26784. $.each(navigator.plugins, function(i, plugins) {
  26785. $.each(plugins, function(i, plugin) {
  26786. if (plugin.type === mime) {
  26787. return !(active = true);
  26788. }
  26789. });
  26790. });
  26791. }
  26792. if (active) {
  26793. if (typeof ql.options.pdfToolbar !== 'undefined' && !ql.options.pdfToolbar) {
  26794. urlhash = '#toolbar=0';
  26795. }
  26796. preview.on(ql.evUpdate, function(e) {
  26797. var file = e.file;
  26798. if (active && file.mime === mime && ql.dispInlineRegex.test(file.mime)) {
  26799. e.stopImmediatePropagation();
  26800. ql.hideinfo();
  26801. ql.cover.addClass('elfinder-quicklook-coverbg');
  26802. $('<object class="elfinder-quicklook-preview-pdf" data="'+fm.openUrl(file.hash)+urlhash+'" type="application/pdf" />')
  26803. .on('error', function(e) {
  26804. active = false;
  26805. ql.update(void(0), fm.cwd());
  26806. ql.update(void(0), file);
  26807. })
  26808. .appendTo(preview);
  26809. }
  26810. });
  26811. }
  26812. },
  26813. /**
  26814. * Flash preview plugin
  26815. *
  26816. * @param elFinder.commands.quicklook
  26817. **/
  26818. function(ql) {
  26819. var fm = ql.fm,
  26820. mime = 'application/x-shockwave-flash',
  26821. preview = ql.preview,
  26822. active = false;
  26823. $.each(navigator.plugins, function(i, plugins) {
  26824. $.each(plugins, function(i, plugin) {
  26825. if (plugin.type === mime) {
  26826. return !(active = true);
  26827. }
  26828. });
  26829. });
  26830. active && preview.on(ql.evUpdate, function(e) {
  26831. var file = e.file,
  26832. node;
  26833. if (file.mime === mime && ql.dispInlineRegex.test(file.mime)) {
  26834. e.stopImmediatePropagation();
  26835. ql.hideinfo();
  26836. node = $('<embed class="elfinder-quicklook-preview-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" src="'+fm.openUrl(file.hash)+'" quality="high" type="application/x-shockwave-flash" wmode="transparent" />')
  26837. .appendTo(preview);
  26838. }
  26839. });
  26840. },
  26841. /**
  26842. * HTML5 audio preview plugin
  26843. *
  26844. * @param elFinder.commands.quicklook
  26845. **/
  26846. function(ql) {
  26847. var fm = ql.fm,
  26848. preview = ql.preview,
  26849. mimes = {
  26850. 'audio/mpeg' : 'mp3',
  26851. 'audio/mpeg3' : 'mp3',
  26852. 'audio/mp3' : 'mp3',
  26853. 'audio/x-mpeg3' : 'mp3',
  26854. 'audio/x-mp3' : 'mp3',
  26855. 'audio/x-wav' : 'wav',
  26856. 'audio/wav' : 'wav',
  26857. 'audio/x-m4a' : 'm4a',
  26858. 'audio/aac' : 'm4a',
  26859. 'audio/mp4' : 'm4a',
  26860. 'audio/x-mp4' : 'm4a',
  26861. 'audio/ogg' : 'ogg',
  26862. 'audio/webm' : 'webm',
  26863. 'audio/flac' : 'flac',
  26864. 'audio/x-flac' : 'flac',
  26865. 'audio/amr' : 'amr'
  26866. },
  26867. node, curHash,
  26868. win = ql.window,
  26869. navi = ql.navbar,
  26870. AMR, autoplay,
  26871. controlsList = typeof ql.options.mediaControlsList === 'string' && ql.options.mediaControlsList? ' controlsList="' + fm.escape(ql.options.mediaControlsList) + '"' : '',
  26872. setNavi = function() {
  26873. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  26874. },
  26875. getNode = function(src, hash) {
  26876. return $('<audio class="elfinder-quicklook-preview-audio ui-front" controls' + controlsList + ' preload="auto" autobuffer><source src="'+src+'" /></audio>')
  26877. .on('change', function(e) {
  26878. // Firefox fire change event on seek or volume change
  26879. e.stopPropagation();
  26880. })
  26881. .on('error', function(e) {
  26882. node && node.data('hash') === hash && reset();
  26883. })
  26884. .data('hash', hash)
  26885. .appendTo(preview);
  26886. },
  26887. amrToWavUrl = function(hash) {
  26888. var dfd = $.Deferred(),
  26889. loader = $.Deferred().done(function() {
  26890. fm.getContents(hash).done(function(data) {
  26891. try {
  26892. var buffer = AMR.toWAV(new Uint8Array(data));
  26893. if (buffer) {
  26894. dfd.resolve(URL.createObjectURL(new Blob([buffer], { type: 'audio/x-wav' })));
  26895. } else {
  26896. dfd.reject();
  26897. }
  26898. } catch(e) {
  26899. dfd.reject();
  26900. }
  26901. }).fail(function() {
  26902. dfd.reject();
  26903. });
  26904. }).fail(function() {
  26905. AMR = false;
  26906. dfd.reject();
  26907. }),
  26908. _AMR;
  26909. if (window.TextEncoder && window.URL && URL.createObjectURL && typeof AMR === 'undefined') {
  26910. // previous window.AMR
  26911. _AMR = window.AMR;
  26912. delete window.AMR;
  26913. fm.loadScript(
  26914. [ fm.options.cdns.amr ],
  26915. function() {
  26916. AMR = window.AMR? window.AMR : false;
  26917. // restore previous window.AMR
  26918. window.AMR = _AMR;
  26919. loader[AMR? 'resolve':'reject']();
  26920. },
  26921. {
  26922. error: function() {
  26923. loader.reject();
  26924. }
  26925. }
  26926. );
  26927. } else {
  26928. loader[AMR? 'resolve':'reject']();
  26929. }
  26930. return dfd;
  26931. },
  26932. play = function(player) {
  26933. var hash = node.data('hash'),
  26934. playPromise;
  26935. autoplay && (playPromise = player.play());
  26936. // uses "playPromise['catch']" instead "playPromise.catch" to support Old IE
  26937. if (playPromise && playPromise['catch']) {
  26938. playPromise['catch'](function(e) {
  26939. if (!player.paused) {
  26940. node && node.data('hash') === hash && reset();
  26941. }
  26942. });
  26943. }
  26944. },
  26945. reset = function() {
  26946. if (node && node.parent().length) {
  26947. var elm = node[0],
  26948. url = node.children('source').attr('src');
  26949. win.off('viewchange.audio');
  26950. try {
  26951. elm.pause();
  26952. node.empty();
  26953. if (url.match(/^blob:/)) {
  26954. URL.revokeObjectURL(url);
  26955. }
  26956. elm.src = '';
  26957. elm.load();
  26958. } catch(e) {}
  26959. node.remove();
  26960. node = null;
  26961. }
  26962. };
  26963. preview.on(ql.evUpdate, function(e) {
  26964. var file = e.file,
  26965. type = mimes[file.mime],
  26966. html5, srcUrl;
  26967. if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime) && ((html5 = ql.support.audio[type]) || (type === 'amr'))) {
  26968. autoplay = ql.autoPlay();
  26969. curHash = file.hash;
  26970. srcUrl = html5? fm.openUrl(curHash) : '';
  26971. if (!html5) {
  26972. if (fm.options.cdns.amr && type === 'amr' && AMR !== false) {
  26973. e.stopImmediatePropagation();
  26974. node = getNode(srcUrl, curHash);
  26975. amrToWavUrl(file.hash).done(function(url) {
  26976. if (curHash === file.hash) {
  26977. var elm = node[0];
  26978. try {
  26979. node.children('source').attr('src', url);
  26980. elm.pause();
  26981. elm.load();
  26982. play(elm);
  26983. win.on('viewchange.audio', setNavi);
  26984. setNavi();
  26985. } catch(e) {
  26986. URL.revokeObjectURL(url);
  26987. node.remove();
  26988. }
  26989. } else {
  26990. URL.revokeObjectURL(url);
  26991. }
  26992. }).fail(function() {
  26993. node.remove();
  26994. });
  26995. }
  26996. } else {
  26997. e.stopImmediatePropagation();
  26998. node = getNode(srcUrl, curHash);
  26999. play(node[0]);
  27000. win.on('viewchange.audio', setNavi);
  27001. setNavi();
  27002. }
  27003. }
  27004. }).on('change', reset);
  27005. },
  27006. /**
  27007. * HTML5 video preview plugin
  27008. *
  27009. * @param elFinder.commands.quicklook
  27010. **/
  27011. function(ql) {
  27012. var fm = ql.fm,
  27013. preview = ql.preview,
  27014. mimes = {
  27015. 'video/mp4' : 'mp4',
  27016. 'video/x-m4v' : 'mp4',
  27017. 'video/quicktime' : 'mp4',
  27018. 'video/mpeg' : 'mpeg',
  27019. 'video/ogg' : 'ogg',
  27020. 'application/ogg' : 'ogg',
  27021. 'video/webm' : 'webm',
  27022. 'video/x-matroska': 'mkv',
  27023. 'video/3gpp' : '3gp',
  27024. 'application/vnd.apple.mpegurl' : 'm3u8',
  27025. 'application/x-mpegurl' : 'm3u8',
  27026. 'application/dash+xml' : 'mpd',
  27027. 'video/x-flv' : 'flv',
  27028. 'video/x-msvideo' : 'avi'
  27029. },
  27030. node,
  27031. win = ql.window,
  27032. navi = ql.navbar,
  27033. cHls, cDash, pDash, cFlv, cVideojs, autoplay, tm,
  27034. controlsList = typeof ql.options.mediaControlsList === 'string' && ql.options.mediaControlsList? ' controlsList="' + fm.escape(ql.options.mediaControlsList) + '"' : '',
  27035. setNavi = function() {
  27036. if (fm.UA.iOS) {
  27037. if (win.hasClass('elfinder-quicklook-fullscreen')) {
  27038. preview.css('height', '-webkit-calc(100% - 50px)');
  27039. navi._show();
  27040. } else {
  27041. preview.css('height', '');
  27042. }
  27043. } else {
  27044. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  27045. }
  27046. },
  27047. render = function(file, opts) {
  27048. var errTm = function(e) {
  27049. if (err > 1) {
  27050. tm && clearTimeout(tm);
  27051. tm = setTimeout(function() {
  27052. !canPlay && reset(true);
  27053. }, 800);
  27054. }
  27055. },
  27056. err = 0,
  27057. canPlay;
  27058. //reset();
  27059. pDash = null;
  27060. opts = opts || {};
  27061. ql.hideinfo();
  27062. node = $('<video class="elfinder-quicklook-preview-video" controls' + controlsList + ' preload="auto" autobuffer playsinline>'
  27063. +'</video>')
  27064. .on('change', function(e) {
  27065. // Firefox fire change event on seek or volume change
  27066. e.stopPropagation();
  27067. })
  27068. .on('timeupdate progress', errTm)
  27069. .on('canplay', function() {
  27070. canPlay = true;
  27071. })
  27072. .data('hash', file.hash);
  27073. // can not handling error event with jQuery `on` event handler
  27074. node[0].addEventListener('error', function(e) {
  27075. if (opts.src && fm.convAbsUrl(opts.src) === fm.convAbsUrl(e.target.src)) {
  27076. ++err;
  27077. errTm();
  27078. }
  27079. }, true);
  27080. if (opts.src) {
  27081. node.append('<source src="'+opts.src+'" type="'+file.mime+'"/><source src="'+opts.src+'"/>');
  27082. }
  27083. node.appendTo(preview);
  27084. win.on('viewchange.video', setNavi);
  27085. setNavi();
  27086. },
  27087. loadHls = function(file) {
  27088. var hls;
  27089. render(file);
  27090. hls = new cHls();
  27091. hls.loadSource(fm.openUrl(file.hash));
  27092. hls.attachMedia(node[0]);
  27093. if (autoplay) {
  27094. hls.on(cHls.Events.MANIFEST_PARSED, function() {
  27095. play(node[0]);
  27096. });
  27097. }
  27098. },
  27099. loadDash = function(file) {
  27100. render(file);
  27101. pDash = window.dashjs.MediaPlayer().create();
  27102. pDash.getDebug().setLogToBrowserConsole(false);
  27103. pDash.initialize(node[0], fm.openUrl(file.hash), autoplay);
  27104. pDash.on('error', function(e) {
  27105. reset(true);
  27106. });
  27107. },
  27108. loadFlv = function(file) {
  27109. if (!cFlv.isSupported()) {
  27110. cFlv = false;
  27111. return;
  27112. }
  27113. var player = cFlv.createPlayer({
  27114. type: 'flv',
  27115. url: fm.openUrl(file.hash)
  27116. });
  27117. render(file);
  27118. player.on(cFlv.Events.ERROR, function() {
  27119. player.destroy();
  27120. reset(true);
  27121. });
  27122. player.attachMediaElement(node[0]);
  27123. player.load();
  27124. play(player);
  27125. },
  27126. loadVideojs = function(file) {
  27127. render(file);
  27128. node[0].src = fm.openUrl(file.hash);
  27129. cVideojs(node[0], {
  27130. src: fm.openUrl(file.hash)
  27131. });
  27132. },
  27133. play = function(player) {
  27134. var hash = node.data('hash'),
  27135. playPromise;
  27136. autoplay && (playPromise = player.play());
  27137. // uses "playPromise['catch']" instead "playPromise.catch" to support Old IE
  27138. if (playPromise && playPromise['catch']) {
  27139. playPromise['catch'](function(e) {
  27140. if (!player.paused) {
  27141. node && node.data('hash') === hash && reset(true);
  27142. }
  27143. });
  27144. }
  27145. },
  27146. reset = function(showInfo) {
  27147. tm && clearTimeout(tm);
  27148. if (node && node.parent().length) {
  27149. var elm = node[0];
  27150. win.off('viewchange.video');
  27151. pDash && pDash.reset();
  27152. try {
  27153. elm.pause();
  27154. node.empty();
  27155. elm.src = '';
  27156. elm.load();
  27157. } catch(e) {}
  27158. node.remove();
  27159. node = null;
  27160. }
  27161. showInfo && ql.info.show();
  27162. };
  27163. preview.on(ql.evUpdate, function(e) {
  27164. var file = e.file,
  27165. mime = file.mime.toLowerCase(),
  27166. type = mimes[mime],
  27167. stock, playPromise;
  27168. if (mimes[mime] && ql.dispInlineRegex.test(file.mime) /*&& (((type === 'm3u8' || (type === 'mpd' && !fm.UA.iOS) || type === 'flv') && !fm.UA.ltIE10) || ql.support.video[type])*/) {
  27169. autoplay = ql.autoPlay();
  27170. if (ql.support.video[type] && (type !== 'm3u8' || fm.UA.Safari)) {
  27171. e.stopImmediatePropagation();
  27172. render(file, { src: fm.openUrl(file.hash) });
  27173. play(node[0]);
  27174. } else {
  27175. if (cHls !== false && fm.options.cdns.hls && type === 'm3u8') {
  27176. e.stopImmediatePropagation();
  27177. if (cHls) {
  27178. loadHls(file);
  27179. } else {
  27180. stock = window.Hls;
  27181. delete window.Hls;
  27182. fm.loadScript(
  27183. [ fm.options.cdns.hls ],
  27184. function(res) {
  27185. cHls = res || window.Hls || false;
  27186. window.Hls = stock;
  27187. cHls && loadHls(file);
  27188. },
  27189. {
  27190. tryRequire: true,
  27191. error : function() {
  27192. cHls = false;
  27193. }
  27194. }
  27195. );
  27196. }
  27197. } else if (cDash !== false && fm.options.cdns.dash && type === 'mpd') {
  27198. e.stopImmediatePropagation();
  27199. if (cDash) {
  27200. loadDash(file);
  27201. } else {
  27202. fm.loadScript(
  27203. [ fm.options.cdns.dash ],
  27204. function() {
  27205. // dashjs require window.dashjs in global scope
  27206. cDash = window.dashjs? true : false;
  27207. cDash && loadDash(file);
  27208. },
  27209. {
  27210. tryRequire: true,
  27211. error : function() {
  27212. cDash = false;
  27213. }
  27214. }
  27215. );
  27216. }
  27217. } else if (cFlv !== false && fm.options.cdns.flv && type === 'flv') {
  27218. e.stopImmediatePropagation();
  27219. if (cFlv) {
  27220. loadFlv(file);
  27221. } else {
  27222. stock = window.flvjs;
  27223. delete window.flvjs;
  27224. fm.loadScript(
  27225. [ fm.options.cdns.flv ],
  27226. function(res) {
  27227. cFlv = res || window.flvjs || false;
  27228. window.flvjs = stock;
  27229. cFlv && loadFlv(file);
  27230. },
  27231. {
  27232. tryRequire: true,
  27233. error : function() {
  27234. cFlv = false;
  27235. }
  27236. }
  27237. );
  27238. }
  27239. } else {
  27240. if (cVideojs) {
  27241. loadVideojs(file);
  27242. } else {
  27243. fm.loadScript(
  27244. [ 'https://cdnjs.cloudflare.com/ajax/libs/video.js/7.5.0/video.min.js' ],
  27245. function(res) {
  27246. cVideojs = res || window.videojs || false;
  27247. //window.flvjs = stock;
  27248. cVideojs && loadVideojs(file);
  27249. },
  27250. {
  27251. tryRequire: true,
  27252. error : function() {
  27253. cVideojs = false;
  27254. }
  27255. }
  27256. ).loadCss(['https://cdnjs.cloudflare.com/ajax/libs/video.js/7.5.0/video-js.min.css']);
  27257. }
  27258. }
  27259. }
  27260. }
  27261. }).on('change', reset);
  27262. },
  27263. /**
  27264. * Audio/video preview plugin using browser plugins
  27265. *
  27266. * @param elFinder.commands.quicklook
  27267. **/
  27268. function(ql) {
  27269. var preview = ql.preview,
  27270. mimes = [],
  27271. node,
  27272. win = ql.window,
  27273. navi = ql.navbar;
  27274. $.each(navigator.plugins, function(i, plugins) {
  27275. $.each(plugins, function(i, plugin) {
  27276. (plugin.type.indexOf('audio/') === 0 || plugin.type.indexOf('video/') === 0) && mimes.push(plugin.type);
  27277. });
  27278. });
  27279. mimes = ql.fm.arrayFlip(mimes);
  27280. preview.on(ql.evUpdate, function(e) {
  27281. var file = e.file,
  27282. mime = file.mime,
  27283. video,
  27284. setNavi = function() {
  27285. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  27286. };
  27287. if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime)) {
  27288. e.stopImmediatePropagation();
  27289. (video = mime.indexOf('video/') === 0) && ql.hideinfo();
  27290. node = $('<embed src="'+ql.fm.openUrl(file.hash)+'" type="'+mime+'" class="elfinder-quicklook-preview-'+(video ? 'video' : 'audio')+'"/>')
  27291. .appendTo(preview);
  27292. win.on('viewchange.embed', setNavi);
  27293. setNavi();
  27294. }
  27295. }).on('change', function() {
  27296. if (node && node.parent().length) {
  27297. win.off('viewchange.embed');
  27298. node.remove();
  27299. node= null;
  27300. }
  27301. });
  27302. },
  27303. /**
  27304. * Archive(zip|gzip|tar) preview plugin using https://github.com/imaya/zlib.js
  27305. *
  27306. * @param elFinder.commands.quicklook
  27307. **/
  27308. function(ql) {
  27309. var fm = ql.fm,
  27310. mimes = fm.arrayFlip(['application/zip', 'application/x-gzip', 'application/x-tar']),
  27311. preview = ql.preview,
  27312. unzipFiles = function() {
  27313. /** @type {Array.<string>} */
  27314. var filenameList = [];
  27315. /** @type {number} */
  27316. var i;
  27317. /** @type {number} */
  27318. var il;
  27319. /** @type {Array.<Zlib.Unzip.FileHeader>} */
  27320. var fileHeaderList;
  27321. // need check this.Y when update cdns.zlibUnzip
  27322. this.Y();
  27323. fileHeaderList = this.i;
  27324. for (i = 0, il = fileHeaderList.length; i < il; ++i) {
  27325. // need check fileHeaderList[i].J when update cdns.zlibUnzip
  27326. filenameList[i] = fileHeaderList[i].filename + (fileHeaderList[i].J? ' (' + fm.formatSize(fileHeaderList[i].J) + ')' : '');
  27327. }
  27328. return filenameList;
  27329. },
  27330. tarFiles = function(tar) {
  27331. var filenames = [],
  27332. tarlen = tar.length,
  27333. offset = 0,
  27334. toStr = function(arr) {
  27335. return String.fromCharCode.apply(null, arr).replace(/\0+$/, '');
  27336. },
  27337. h, name, prefix, size, dbs;
  27338. while (offset < tarlen && tar[offset] !== 0) {
  27339. h = tar.subarray(offset, offset + 512);
  27340. name = toStr(h.subarray(0, 100));
  27341. if (prefix = toStr(h.subarray(345, 500))) {
  27342. name = prefix + name;
  27343. }
  27344. size = parseInt(toStr(h.subarray(124, 136)), 8);
  27345. dbs = Math.ceil(size / 512) * 512;
  27346. if (name === '././@LongLink') {
  27347. name = toStr(tar.subarray(offset + 512, offset + 512 + dbs));
  27348. }
  27349. (name !== 'pax_global_header') && filenames.push(name + (size? ' (' + fm.formatSize(size) + ')': ''));
  27350. offset = offset + 512 + dbs;
  27351. }
  27352. return filenames;
  27353. },
  27354. Zlib;
  27355. if (window.Uint8Array && window.DataView && fm.options.cdns.zlibUnzip && fm.options.cdns.zlibGunzip) {
  27356. preview.on(ql.evUpdate, function(e) {
  27357. var file = e.file,
  27358. isTar = (file.mime === 'application/x-tar');
  27359. if (mimes[file.mime] && (
  27360. isTar
  27361. || ((typeof Zlib === 'undefined' || Zlib) && (file.mime === 'application/zip' || file.mime === 'application/x-gzip'))
  27362. )) {
  27363. var jqxhr, loading, url,
  27364. req = function() {
  27365. url = fm.openUrl(file.hash);
  27366. if (!fm.isSameOrigin(url)) {
  27367. url = fm.openUrl(file.hash, true);
  27368. }
  27369. jqxhr = fm.request({
  27370. data : {cmd : 'get'},
  27371. options : {
  27372. url: url,
  27373. type: 'get',
  27374. cache : true,
  27375. dataType : 'binary',
  27376. responseType :'arraybuffer',
  27377. processData: false
  27378. }
  27379. })
  27380. .fail(function() {
  27381. loading.remove();
  27382. })
  27383. .done(function(data) {
  27384. var unzip, filenames;
  27385. try {
  27386. if (file.mime === 'application/zip') {
  27387. unzip = new Zlib.Unzip(new Uint8Array(data));
  27388. //filenames = unzip.getFilenames();
  27389. filenames = unzipFiles.call(unzip);
  27390. } else if (file.mime === 'application/x-gzip') {
  27391. unzip = new Zlib.Gunzip(new Uint8Array(data));
  27392. filenames = tarFiles(unzip.decompress());
  27393. } else if (file.mime === 'application/x-tar') {
  27394. filenames = tarFiles(new Uint8Array(data));
  27395. }
  27396. makeList(filenames);
  27397. } catch (e) {
  27398. loading.remove();
  27399. fm.debug('error', e);
  27400. }
  27401. });
  27402. },
  27403. makeList = function(filenames) {
  27404. var header, doc;
  27405. if (filenames && filenames.length) {
  27406. filenames = $.map(filenames, function(str) {
  27407. return fm.decodeRawString(str);
  27408. });
  27409. filenames.sort();
  27410. loading.remove();
  27411. header = '<strong>'+fm.escape(file.mime)+'</strong> ('+fm.formatSize(file.size)+')'+'<hr/>';
  27412. doc = $('<div class="elfinder-quicklook-preview-archive-wrapper">'+header+'<pre class="elfinder-quicklook-preview-text">'+fm.escape(filenames.join("\n"))+'</pre></div>')
  27413. .on('touchstart', function(e) {
  27414. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  27415. e.originalEvent._preventSwipeX = true;
  27416. }
  27417. })
  27418. .appendTo(preview);
  27419. ql.hideinfo();
  27420. }
  27421. },
  27422. _Zlib;
  27423. // this is our file - stop event propagation
  27424. e.stopImmediatePropagation();
  27425. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27426. // stop loading on change file if not loaded yet
  27427. preview.one('change', function() {
  27428. jqxhr.state() === 'pending' && jqxhr.reject();
  27429. loading.remove();
  27430. });
  27431. if (Zlib) {
  27432. req();
  27433. } else {
  27434. if (window.Zlib) {
  27435. _Zlib = window.Zlib;
  27436. delete window.Zlib;
  27437. }
  27438. fm.loadScript(
  27439. [ fm.options.cdns.zlibUnzip, fm.options.cdns.zlibGunzip ],
  27440. function() {
  27441. if (window.Zlib && (Zlib = window.Zlib)) {
  27442. if (_Zlib) {
  27443. window.Zlib = _Zlib;
  27444. } else {
  27445. delete window.Zlib;
  27446. }
  27447. req();
  27448. } else {
  27449. error();
  27450. }
  27451. }
  27452. );
  27453. }
  27454. }
  27455. });
  27456. }
  27457. },
  27458. /**
  27459. * RAR Archive preview plugin using https://github.com/43081j/rar.js
  27460. *
  27461. * @param elFinder.commands.quicklook
  27462. **/
  27463. function(ql) {
  27464. var fm = ql.fm,
  27465. mimes = fm.arrayFlip(['application/x-rar']),
  27466. preview = ql.preview,
  27467. RAR;
  27468. if (window.DataView) {
  27469. preview.on(ql.evUpdate, function(e) {
  27470. var file = e.file;
  27471. if (mimes[file.mime] && fm.options.cdns.rar && RAR !== false) {
  27472. var loading, url, archive, abort,
  27473. getList = function(url) {
  27474. if (abort) {
  27475. loading.remove();
  27476. return;
  27477. }
  27478. try {
  27479. archive = RAR({
  27480. file: url,
  27481. type: 2,
  27482. xhrHeaders: fm.customHeaders,
  27483. xhrFields: fm.xhrFields
  27484. }, function(err) {
  27485. loading.remove();
  27486. var filenames = [],
  27487. header, doc;
  27488. if (abort || err) {
  27489. // An error occurred (not a rar, read error, etc)
  27490. err && fm.debug('error', err);
  27491. return;
  27492. }
  27493. $.each(archive.entries, function() {
  27494. filenames.push(this.path + (this.size? ' (' + fm.formatSize(this.size) + ')' : ''));
  27495. });
  27496. if (filenames.length) {
  27497. filenames = $.map(filenames, function(str) {
  27498. return fm.decodeRawString(str);
  27499. });
  27500. filenames.sort();
  27501. header = '<strong>'+fm.escape(file.mime)+'</strong> ('+fm.formatSize(file.size)+')'+'<hr/>';
  27502. doc = $('<div class="elfinder-quicklook-preview-archive-wrapper">'+header+'<pre class="elfinder-quicklook-preview-text">'+fm.escape(filenames.join("\n"))+'</pre></div>')
  27503. .on('touchstart', function(e) {
  27504. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  27505. e.originalEvent._preventSwipeX = true;
  27506. }
  27507. })
  27508. .appendTo(preview);
  27509. ql.hideinfo();
  27510. }
  27511. });
  27512. } catch(e) {
  27513. loading.remove();
  27514. }
  27515. },
  27516. error = function() {
  27517. RAR = false;
  27518. loading.remove();
  27519. },
  27520. _RAR;
  27521. // this is our file - stop event propagation
  27522. e.stopImmediatePropagation();
  27523. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27524. // stop loading on change file if not loaded yet
  27525. preview.one('change', function() {
  27526. archive && (archive.abort = true);
  27527. loading.remove();
  27528. abort = true;
  27529. });
  27530. url = fm.openUrl(file.hash);
  27531. if (!fm.isSameOrigin(url)) {
  27532. url = fm.openUrl(file.hash, true);
  27533. }
  27534. if (RAR) {
  27535. getList(url);
  27536. } else {
  27537. if (window.RarArchive) {
  27538. _RAR = window.RarArchive;
  27539. delete window.RarArchive;
  27540. }
  27541. fm.loadScript(
  27542. [ fm.options.cdns.rar ],
  27543. function() {
  27544. if (fm.hasRequire) {
  27545. require(['rar'], function(RarArchive) {
  27546. RAR = RarArchive;
  27547. getList(url);
  27548. }, error);
  27549. } else {
  27550. if (RAR = window.RarArchive) {
  27551. if (_RAR) {
  27552. window.RarArchive = _RAR;
  27553. } else {
  27554. delete window.RarArchive;
  27555. }
  27556. getList(url);
  27557. } else {
  27558. error();
  27559. }
  27560. }
  27561. },
  27562. {
  27563. tryRequire: true,
  27564. error : error
  27565. }
  27566. );
  27567. }
  27568. }
  27569. });
  27570. }
  27571. },
  27572. /**
  27573. * CAD-Files and 3D-Models online viewer on sharecad.org
  27574. *
  27575. * @param elFinder.commands.quicklook
  27576. **/
  27577. function(ql) {
  27578. var fm = ql.fm,
  27579. mimes = fm.arrayFlip(ql.options.sharecadMimes || []),
  27580. preview = ql.preview,
  27581. win = ql.window,
  27582. node;
  27583. if (ql.options.sharecadMimes.length) {
  27584. ql.addIntegration({
  27585. title: 'ShareCAD.org CAD and 3D-Models viewer',
  27586. link: 'https://sharecad.org/DWGOnlinePlugin'
  27587. });
  27588. }
  27589. preview.on(ql.evUpdate, function(e) {
  27590. var file = e.file;
  27591. if (mimes[file.mime.toLowerCase()] && !fm.option('onetimeUrl', file.hash)) {
  27592. var win = ql.window,
  27593. loading, url;
  27594. e.stopImmediatePropagation();
  27595. if (file.url == '1') {
  27596. preview.hide();
  27597. $('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info'))
  27598. .on('click', function() {
  27599. var self = $(this);
  27600. self.html('<span class="elfinder-spinner">');
  27601. fm.request({
  27602. data : {cmd : 'url', target : file.hash},
  27603. preventDefault : true
  27604. })
  27605. .always(function() {
  27606. self.html('');
  27607. })
  27608. .done(function(data) {
  27609. var rfile = fm.file(file.hash);
  27610. file.url = rfile.url = data.url || '';
  27611. if (file.url) {
  27612. preview.trigger({
  27613. type: ql.evUpdate,
  27614. file: file,
  27615. forceUpdate: true
  27616. });
  27617. }
  27618. });
  27619. });
  27620. }
  27621. if (file.url !== '' && file.url != '1') {
  27622. preview.one('change', function() {
  27623. loading.remove();
  27624. node.off('load').remove();
  27625. node = null;
  27626. }).addClass('elfinder-overflow-auto');
  27627. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27628. url = fm.convAbsUrl(fm.url(file.hash));
  27629. node = $('<iframe class="elfinder-quicklook-preview-iframe" scrolling="no"/>')
  27630. .css('background-color', 'transparent')
  27631. .appendTo(preview)
  27632. .on('load', function() {
  27633. ql.hideinfo();
  27634. loading.remove();
  27635. ql.preview.after(ql.info);
  27636. $(this).css('background-color', '#fff').show();
  27637. })
  27638. .on('error', function() {
  27639. loading.remove();
  27640. ql.preview.after(ql.info);
  27641. })
  27642. .attr('src', '//sharecad.org/cadframe/load?url=' + encodeURIComponent(url));
  27643. ql.info.after(ql.preview);
  27644. }
  27645. }
  27646. });
  27647. },
  27648. /**
  27649. * KML preview with GoogleMaps API
  27650. *
  27651. * @param elFinder.commands.quicklook
  27652. */
  27653. function(ql) {
  27654. var fm = ql.fm,
  27655. mimes = {
  27656. 'application/vnd.google-earth.kml+xml' : true,
  27657. 'application/vnd.google-earth.kmz' : true
  27658. },
  27659. preview = ql.preview,
  27660. gMaps, loadMap, wGmfail, fail, mapScr;
  27661. if (ql.options.googleMapsApiKey) {
  27662. ql.addIntegration({
  27663. title: 'Google Maps',
  27664. link: 'https://www.google.com/intl/' + fm.lang.replace('_', '-') + '/help/terms_maps.html'
  27665. });
  27666. gMaps = (window.google && google.maps);
  27667. // start load maps
  27668. loadMap = function(file, node) {
  27669. var mapsOpts = ql.options.googleMapsOpts.maps;
  27670. fm.forExternalUrl(file.hash).done(function(url) {
  27671. if (url) {
  27672. try {
  27673. new gMaps.KmlLayer(url, Object.assign({
  27674. map: new gMaps.Map(node.get(0), mapsOpts)
  27675. }, ql.options.googleMapsOpts.kml));
  27676. ql.hideinfo();
  27677. } catch(e) {
  27678. fail();
  27679. }
  27680. } else {
  27681. fail();
  27682. }
  27683. });
  27684. };
  27685. // keep stored error handler if exists
  27686. wGmfail = window.gm_authFailure;
  27687. // on error function
  27688. fail = function() {
  27689. mapScr = null;
  27690. };
  27691. // API script url
  27692. mapScr = 'https://maps.googleapis.com/maps/api/js?key=' + ql.options.googleMapsApiKey;
  27693. // error handler
  27694. window.gm_authFailure = function() {
  27695. fail();
  27696. wGmfail && wGmfail();
  27697. };
  27698. preview.on(ql.evUpdate, function(e) {
  27699. var file = e.file;
  27700. if (mapScr && mimes[file.mime.toLowerCase()]) {
  27701. var win = ql.window,
  27702. getLink = (file.url == '1' && !fm.option('onetimeUrl', file.hash)),
  27703. loading, url, node;
  27704. e.stopImmediatePropagation();
  27705. if (getLink) {
  27706. preview.hide();
  27707. $('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info'))
  27708. .on('click', function() {
  27709. var self = $(this);
  27710. self.html('<span class="elfinder-spinner">');
  27711. fm.request({
  27712. data : {cmd : 'url', target : file.hash},
  27713. preventDefault : true
  27714. })
  27715. .always(function() {
  27716. self.html('');
  27717. })
  27718. .done(function(data) {
  27719. var rfile = fm.file(file.hash);
  27720. file.url = rfile.url = data.url || '';
  27721. if (file.url) {
  27722. preview.trigger({
  27723. type: ql.evUpdate,
  27724. file: file,
  27725. forceUpdate: true
  27726. });
  27727. }
  27728. });
  27729. });
  27730. }
  27731. if (file.url !== '' && !getLink) {
  27732. node = $('<div style="width:100%;height:100%;"/>').appendTo(preview);
  27733. preview.one('change', function() {
  27734. node.remove();
  27735. node = null;
  27736. });
  27737. if (!gMaps) {
  27738. fm.loadScript([mapScr], function() {
  27739. gMaps = window.google && google.maps;
  27740. gMaps && loadMap(file, node);
  27741. });
  27742. } else {
  27743. loadMap(file, node);
  27744. }
  27745. }
  27746. }
  27747. });
  27748. }
  27749. },
  27750. /**
  27751. * Any supported files preview plugin using (Google docs | MS Office) online viewer
  27752. *
  27753. * @param elFinder.commands.quicklook
  27754. **/
  27755. function(ql) {
  27756. var fm = ql.fm,
  27757. mimes = Object.assign(fm.arrayFlip(ql.options.googleDocsMimes || [], 'g'), fm.arrayFlip(ql.options.officeOnlineMimes || [], 'm')),
  27758. preview = ql.preview,
  27759. win = ql.window,
  27760. navi = ql.navbar,
  27761. urls = {
  27762. g: 'docs.google.com/gview?embedded=true&url=',
  27763. m: 'view.officeapps.live.com/op/embed.aspx?wdStartOn=0&src='
  27764. },
  27765. navBottom = {
  27766. g: '56px',
  27767. m: '24px'
  27768. },
  27769. mLimits = {
  27770. xls : 5242880, // 5MB
  27771. xlsb : 5242880,
  27772. xlsx : 5242880,
  27773. xlsm : 5242880,
  27774. other: 10485760 // 10MB
  27775. },
  27776. node, enable;
  27777. if (ql.options.googleDocsMimes.length) {
  27778. enable = true;
  27779. ql.addIntegration({
  27780. title: 'Google Docs Viewer',
  27781. link: 'https://docs.google.com/'
  27782. });
  27783. }
  27784. if (ql.options.officeOnlineMimes.length) {
  27785. enable = true;
  27786. ql.addIntegration({
  27787. title: 'MS Online Doc Viewer',
  27788. link: 'https://products.office.com/office-online/view-office-documents-online'
  27789. });
  27790. }
  27791. if (enable) {
  27792. preview.on(ql.evUpdate, function(e) {
  27793. var file = e.file,
  27794. type;
  27795. // 25MB is maximum filesize of Google Docs prevew
  27796. if (file.size <= 26214400 && (type = mimes[file.mime])) {
  27797. var win = ql.window,
  27798. setNavi = function() {
  27799. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? navBottom[type] : '');
  27800. },
  27801. ext = fm.mimeTypes[file.mime],
  27802. getLink = (file.url == '1' && !fm.option('onetimeUrl', file.hash)),
  27803. loading, url;
  27804. if (type === 'm') {
  27805. if ((mLimits[ext] && file.size > mLimits[ext]) || file.size > mLimits.other) {
  27806. type = 'g';
  27807. }
  27808. }
  27809. if (getLink) {
  27810. preview.hide();
  27811. $('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info'))
  27812. .on('click', function() {
  27813. var self = $(this);
  27814. self.html('<span class="elfinder-spinner">');
  27815. fm.request({
  27816. data : {cmd : 'url', target : file.hash},
  27817. preventDefault : true
  27818. })
  27819. .always(function() {
  27820. self.html('');
  27821. })
  27822. .done(function(data) {
  27823. var rfile = fm.file(file.hash);
  27824. file.url = rfile.url = data.url || '';
  27825. if (file.url) {
  27826. preview.trigger({
  27827. type: ql.evUpdate,
  27828. file: file,
  27829. forceUpdate: true
  27830. });
  27831. }
  27832. });
  27833. });
  27834. }
  27835. if (file.url !== '' && !getLink) {
  27836. e.stopImmediatePropagation();
  27837. preview.one('change', function() {
  27838. win.off('viewchange.googledocs');
  27839. loading.remove();
  27840. node.off('load').remove();
  27841. node = null;
  27842. }).addClass('elfinder-overflow-auto');
  27843. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27844. node = $('<iframe class="elfinder-quicklook-preview-iframe"/>')
  27845. .css('background-color', 'transparent')
  27846. .appendTo(preview);
  27847. fm.forExternalUrl(file.hash).done(function(url) {
  27848. if (url) {
  27849. if (file.ts) {
  27850. url += (url.match(/\?/)? '&' : '?') + '_t=' + file.ts;
  27851. }
  27852. node.on('load', function() {
  27853. ql.hideinfo();
  27854. loading.remove();
  27855. ql.preview.after(ql.info);
  27856. $(this).css('background-color', '#fff').show();
  27857. })
  27858. .on('error', function() {
  27859. loading.remove();
  27860. ql.preview.after(ql.info);
  27861. }).attr('src', 'https://' + urls[type] + encodeURIComponent(url));
  27862. } else {
  27863. loading.remove();
  27864. node.remove();
  27865. }
  27866. });
  27867. win.on('viewchange.googledocs', setNavi);
  27868. setNavi();
  27869. ql.info.after(ql.preview);
  27870. }
  27871. }
  27872. });
  27873. }
  27874. },
  27875. /**
  27876. * Texts preview plugin
  27877. *
  27878. * @param elFinder.commands.quicklook
  27879. **/
  27880. function(ql) {
  27881. var fm = ql.fm,
  27882. preview = ql.preview,
  27883. textMaxlen = parseInt(ql.options.textMaxlen) || 2000,
  27884. prettify = function() {
  27885. if (fm.options.cdns.prettify) {
  27886. fm.loadScript([fm.options.cdns.prettify + (fm.options.cdns.prettify.match(/\?/)? '&' : '?') + 'autorun=false']);
  27887. prettify = function() { return true; };
  27888. } else {
  27889. prettify = function() { return false; };
  27890. }
  27891. },
  27892. PRcheck = function(node, cnt) {
  27893. if (prettify()) {
  27894. if (typeof window.PR === 'undefined' && cnt--) {
  27895. setTimeout(function() { PRcheck(node, cnt); }, 100);
  27896. } else {
  27897. if (typeof window.PR === 'object') {
  27898. node.css('cursor', 'wait');
  27899. requestAnimationFrame(function() {
  27900. PR.prettyPrint && PR.prettyPrint(null, node.get(0));
  27901. node.css('cursor', '');
  27902. });
  27903. } else {
  27904. prettify = function() { return false; };
  27905. }
  27906. }
  27907. }
  27908. };
  27909. preview.on(ql.evUpdate, function(e) {
  27910. var file = e.file,
  27911. mime = file.mime,
  27912. jqxhr, loading;
  27913. if (fm.mimeIsText(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) {
  27914. e.stopImmediatePropagation();
  27915. (typeof window.PR === 'undefined') && prettify();
  27916. loading = $('<div class="elfinder-quicklook-info-data"><span class="elfinder-spinner-text">'+fm.i18n('nowLoading')+'</span><span class="elfinder-spinner"/></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  27917. // stop loading on change file if not loadin yet
  27918. preview.one('change', function() {
  27919. jqxhr.state() == 'pending' && jqxhr.reject();
  27920. });
  27921. jqxhr = fm.request({
  27922. data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts},
  27923. options : {type: 'get', cache : true},
  27924. preventDefault : true
  27925. })
  27926. .done(function(data) {
  27927. var reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i'),
  27928. text = data.content,
  27929. part, more, node, m;
  27930. ql.hideinfo();
  27931. if (window.atob && (m = text.match(reg))) {
  27932. text = atob(text.substr(m[1].length));
  27933. }
  27934. more = text.length - textMaxlen;
  27935. if (more > 100) {
  27936. part = text.substr(0, textMaxlen) + '...';
  27937. } else {
  27938. more = 0;
  27939. }
  27940. node = $('<div class="elfinder-quicklook-preview-text-wrapper"><pre class="elfinder-quicklook-preview-text prettyprint"></pre></div>');
  27941. if (more) {
  27942. node.append($('<div class="elfinder-quicklook-preview-charsleft"><hr/><span>' + fm.i18n('charsLeft', fm.toLocaleString(more)) + '</span></div>')
  27943. .on('click', function() {
  27944. var top = node.scrollTop();
  27945. $(this).remove();
  27946. node.children('pre').removeClass('prettyprinted').text(text).scrollTop(top);
  27947. PRcheck(node, 100);
  27948. })
  27949. );
  27950. }
  27951. node.children('pre').text(part || text);
  27952. node.on('touchstart', function(e) {
  27953. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  27954. e.originalEvent._preventSwipeX = true;
  27955. }
  27956. }).appendTo(preview);
  27957. PRcheck(node, 100);
  27958. })
  27959. .always(function() {
  27960. loading.remove();
  27961. });
  27962. }
  27963. });
  27964. }
  27965. ];
  27966. /*
  27967. * File: /js/commands/reload.js
  27968. */
  27969. /**
  27970. * @class elFinder command "reload"
  27971. * Sync files and folders
  27972. *
  27973. * @author Dmitry (dio) Levashov
  27974. **/
  27975. (elFinder.prototype.commands.reload = function() {
  27976. "use strict";
  27977. var self = this,
  27978. search = false;
  27979. this.alwaysEnabled = true;
  27980. this.updateOnSelect = true;
  27981. this.shortcuts = [{
  27982. pattern : 'ctrl+shift+r f5'
  27983. }];
  27984. this.getstate = function() {
  27985. return 0;
  27986. };
  27987. this.init = function() {
  27988. this.fm.bind('search searchend', function() {
  27989. search = this.type == 'search';
  27990. });
  27991. };
  27992. this.fm.bind('contextmenu', function(){
  27993. var fm = self.fm;
  27994. if (fm.options.sync >= 1000) {
  27995. self.extra = {
  27996. icon: 'accept',
  27997. node: $('<span/>')
  27998. .attr({title: fm.i18n('autoSync')})
  27999. .on('click touchstart', function(e){
  28000. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  28001. return;
  28002. }
  28003. e.stopPropagation();
  28004. e.preventDefault();
  28005. $(this).parent()
  28006. .toggleClass('ui-state-disabled', fm.options.syncStart)
  28007. .parent().removeClass('ui-state-hover');
  28008. fm.options.syncStart = !fm.options.syncStart;
  28009. fm.autoSync(fm.options.syncStart? null : 'stop');
  28010. }).on('ready', function(){
  28011. $(this).parent().toggleClass('ui-state-disabled', !fm.options.syncStart).css('pointer-events', 'auto');
  28012. })
  28013. };
  28014. }
  28015. });
  28016. this.exec = function() {
  28017. var fm = this.fm;
  28018. if (!search) {
  28019. var dfrd = fm.sync(),
  28020. timeout = setTimeout(function() {
  28021. fm.notify({type : 'reload', cnt : 1, hideCnt : true});
  28022. dfrd.always(function() { fm.notify({type : 'reload', cnt : -1}); });
  28023. }, fm.notifyDelay);
  28024. return dfrd.always(function() {
  28025. clearTimeout(timeout);
  28026. fm.trigger('reload');
  28027. });
  28028. } else {
  28029. $('div.elfinder-toolbar > div.'+fm.res('class', 'searchbtn') + ' > span.ui-icon-search').click();
  28030. }
  28031. };
  28032. }).prototype = { forceLoad : true }; // this is required command
  28033. /*
  28034. * File: /js/commands/rename.js
  28035. */
  28036. /**
  28037. * @class elFinder command "rename".
  28038. * Rename selected file.
  28039. *
  28040. * @author Dmitry (dio) Levashov, dio@std42.ru
  28041. * @author Naoki Sawada
  28042. **/
  28043. elFinder.prototype.commands.rename = function() {
  28044. "use strict";
  28045. // set alwaysEnabled to allow root rename on client size
  28046. this.alwaysEnabled = true;
  28047. this.syncTitleOnChange = true;
  28048. var self = this,
  28049. fm = self.fm,
  28050. request = function(dfrd, targtes, file, name) {
  28051. var sel = targtes? [file.hash].concat(targtes) : [file.hash],
  28052. cnt = sel.length,
  28053. data = {}, rootNames;
  28054. fm.lockfiles({files : sel});
  28055. if (fm.isRoot(file)) {
  28056. if (!(rootNames = fm.storage('rootNames'))) {
  28057. rootNames = {};
  28058. }
  28059. if (name === '') {
  28060. if (rootNames[file.hash]) {
  28061. file.name = file._name;
  28062. file.i18 = file._i18;
  28063. delete rootNames[file.hash];
  28064. delete file._name;
  28065. delete file._i18;
  28066. } else {
  28067. dfrd && dfrd.reject();
  28068. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  28069. return;
  28070. }
  28071. } else {
  28072. if (typeof file._name === 'undefined') {
  28073. file._name = file.name;
  28074. file._i18 = file.i18;
  28075. }
  28076. file.name = rootNames[file.hash] = name;
  28077. delete file.i18;
  28078. }
  28079. fm.storage('rootNames', rootNames);
  28080. data = { changed: [file] };
  28081. fm.updateCache(data);
  28082. fm.change(data);
  28083. dfrd && dfrd.resolve(data);
  28084. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  28085. return;
  28086. }
  28087. data = {
  28088. cmd : 'rename',
  28089. name : name,
  28090. target : file.hash
  28091. };
  28092. if (cnt > 1) {
  28093. data['targets'] = targtes;
  28094. if (name.match(/\*/)) {
  28095. data['q'] = name;
  28096. }
  28097. }
  28098. fm.request({
  28099. data : data,
  28100. notify : {type : 'rename', cnt : cnt},
  28101. navigate : {}
  28102. })
  28103. .fail(function(error) {
  28104. var err = fm.parseError(error);
  28105. dfrd && dfrd.reject();
  28106. if (! err || ! Array.isArray(err) || err[0] !== 'errRename') {
  28107. fm.sync();
  28108. }
  28109. })
  28110. .done(function(data) {
  28111. var cwdHash;
  28112. if (data.added && data.added.length && cnt === 1) {
  28113. data.undo = {
  28114. cmd : 'rename',
  28115. callback : function() {
  28116. return fm.request({
  28117. data : {cmd : 'rename', target : data.added[0].hash, name : file.name},
  28118. notify : {type : 'undo', cnt : 1}
  28119. });
  28120. }
  28121. };
  28122. data.redo = {
  28123. cmd : 'rename',
  28124. callback : function() {
  28125. return fm.request({
  28126. data : {cmd : 'rename', target : file.hash, name : name},
  28127. notify : {type : 'rename', cnt : 1}
  28128. });
  28129. }
  28130. };
  28131. }
  28132. dfrd && dfrd.resolve(data);
  28133. if (!(cwdHash = fm.cwd().hash) || cwdHash === file.hash) {
  28134. fm.exec('open', $.map(data.added, function(f) {
  28135. return (f.mime === 'directory')? f.hash : null;
  28136. })[0]);
  28137. }
  28138. })
  28139. .always(function() {
  28140. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  28141. }
  28142. );
  28143. },
  28144. getHint = function(name, target) {
  28145. var sel = target || fm.selected(),
  28146. splits = fm.splitFileExtention(name),
  28147. f1 = fm.file(sel[0]),
  28148. f2 = fm.file(sel[1]),
  28149. ext, hint, add;
  28150. ext = splits[1]? ('.' + splits[1]) : '';
  28151. if (splits[1] && splits[0] === '*') {
  28152. // change extention
  28153. hint = '"' + fm.splitFileExtention(f1.name)[0] + ext + '", ';
  28154. hint += '"' + fm.splitFileExtention(f2.name)[0] + ext + '"';
  28155. } else if (splits[0].length > 1) {
  28156. if (splits[0].substr(-1) === '*') {
  28157. // add prefix
  28158. add = splits[0].substr(0, splits[0].length - 1);
  28159. hint = '"' + add + f1.name+'", ';
  28160. hint += '"' + add + f2.name+'"';
  28161. } else if (splits[0].substr(0, 1) === '*') {
  28162. // add suffix
  28163. add = splits[0].substr(1);
  28164. hint = '"'+fm.splitFileExtention(f1.name)[0] + add + ext + '", ';
  28165. hint += '"'+fm.splitFileExtention(f2.name)[0] + add + ext + '"';
  28166. }
  28167. }
  28168. if (!hint) {
  28169. hint = '"'+splits[0] + '1' + ext + '", "' + splits[0] + '2' + ext + '"';
  28170. }
  28171. if (sel.length > 2) {
  28172. hint += ' ...';
  28173. }
  28174. return hint;
  28175. },
  28176. batchRename = function() {
  28177. var sel = fm.selected(),
  28178. tplr = '<input name="type" type="radio" class="elfinder-tabstop">',
  28179. mkChk = function(node, label) {
  28180. return $('<label class="elfinder-rename-batch-checks">' + fm.i18n(label) + '</label>').prepend(node);
  28181. },
  28182. name = $('<input type="text" class="ui-corner-all elfinder-tabstop">'),
  28183. num = $(tplr),
  28184. prefix = $(tplr),
  28185. suffix = $(tplr),
  28186. extention = $(tplr),
  28187. checks = $('<div/>').append(
  28188. mkChk(num, 'plusNumber'),
  28189. mkChk(prefix, 'asPrefix'),
  28190. mkChk(suffix, 'asSuffix'),
  28191. mkChk(extention, 'changeExtention')
  28192. ),
  28193. preview = $('<div class="elfinder-rename-batch-preview"/>'),
  28194. node = $('<div class="elfinder-rename-batch"/>').append(
  28195. $('<div class="elfinder-rename-batch-name"/>').append(name),
  28196. $('<div class="elfinder-rename-batch-type"/>').append(checks),
  28197. preview
  28198. ),
  28199. opts = {
  28200. title : fm.i18n('batchRename'),
  28201. modal : true,
  28202. destroyOnClose : true,
  28203. width: Math.min(380, fm.getUI().width() - 20),
  28204. buttons : {},
  28205. open : function() {
  28206. name.on('input', mkPrev).trigger('focus');
  28207. }
  28208. },
  28209. getName = function() {
  28210. var vName = name.val(),
  28211. ext = fm.splitFileExtention(fm.file(sel[0]).name)[1];
  28212. if (vName !== '' || num.is(':checked')) {
  28213. if (prefix.is(':checked')) {
  28214. vName += '*';
  28215. } else if (suffix.is(':checked')) {
  28216. vName = '*' + vName + '.' + ext;
  28217. } else if (extention.is(':checked')) {
  28218. vName = '*.' + vName;
  28219. } else if (ext) {
  28220. vName += '.' + ext;
  28221. }
  28222. }
  28223. return vName;
  28224. },
  28225. mkPrev = function() {
  28226. var vName = getName();
  28227. if (vName !== '') {
  28228. preview.html(fm.i18n(['renameMultiple', sel.length, getHint(vName)]));
  28229. } else {
  28230. preview.empty();
  28231. }
  28232. },
  28233. radios = checks.find('input:radio').on('change', mkPrev),
  28234. dialog;
  28235. opts.buttons[fm.i18n('btnApply')] = function() {
  28236. var vName = getName(),
  28237. file, targets;
  28238. if (vName !== '') {
  28239. dialog.elfinderdialog('close');
  28240. targets = sel;
  28241. file = fm.file(targets.shift());
  28242. request(void(0), targets, file, vName);
  28243. }
  28244. };
  28245. opts.buttons[fm.i18n('btnCancel')] = function() {
  28246. dialog.elfinderdialog('close');
  28247. };
  28248. if ($.fn.checkboxradio) {
  28249. radios.checkboxradio({
  28250. create: function(e, ui) {
  28251. if (this === num.get(0)) {
  28252. num.prop('checked', true).change();
  28253. }
  28254. }
  28255. });
  28256. } else {
  28257. checks.buttonset({
  28258. create: function(e, ui) {
  28259. num.prop('checked', true).change();
  28260. }
  28261. });
  28262. }
  28263. dialog = self.fmDialog(node, opts);
  28264. };
  28265. this.noChangeDirOnRemovedCwd = true;
  28266. this.shortcuts = [{
  28267. pattern : 'f2' + (fm.OS == 'mac' ? ' enter' : '')
  28268. }, {
  28269. pattern : 'shift+f2',
  28270. description : 'batchRename',
  28271. callback : function() {
  28272. fm.selected().length > 1 && batchRename();
  28273. }
  28274. }];
  28275. this.getstate = function(select) {
  28276. var sel = this.files(select),
  28277. cnt = sel.length,
  28278. phash, ext, mime, brk, state, isRoot;
  28279. if (!cnt) {
  28280. return -1;
  28281. }
  28282. if (cnt > 1 && sel[0].phash) {
  28283. phash = sel[0].phash;
  28284. ext = fm.splitFileExtention(sel[0].name)[1].toLowerCase();
  28285. mime = sel[0].mime;
  28286. }
  28287. if (cnt === 1) {
  28288. isRoot = fm.isRoot(sel[0]);
  28289. }
  28290. state = (cnt === 1 && (isRoot || !sel[0].locked) || (fm.api > 2.1030 && cnt === $.grep(sel, function(f) {
  28291. if (!brk && !f.locked && f.phash === phash && !fm.isRoot(f) && (mime === f.mime || ext === fm.splitFileExtention(f.name)[1].toLowerCase())) {
  28292. return true;
  28293. } else {
  28294. brk && (brk = true);
  28295. return false;
  28296. }
  28297. }).length)) ? 0 : -1;
  28298. // because alwaysEnabled = true, it need check disabled on connector
  28299. if (!isRoot && state === 0 && fm.option('disabledFlip', sel[0].hash)['rename']) {
  28300. state = -1;
  28301. }
  28302. if (state !== -1 && cnt > 1) {
  28303. self.extra = {
  28304. icon: 'preference',
  28305. node: $('<span/>')
  28306. .attr({title: fm.i18n('batchRename')})
  28307. .on('click touchstart', function(e){
  28308. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  28309. return;
  28310. }
  28311. e.stopPropagation();
  28312. e.preventDefault();
  28313. fm.getUI().trigger('click'); // to close the context menu immediately
  28314. batchRename();
  28315. })
  28316. };
  28317. } else {
  28318. delete self.extra;
  28319. }
  28320. return state;
  28321. };
  28322. this.exec = function(hashes, cOpts) {
  28323. var cwd = fm.getUI('cwd'),
  28324. sel = hashes || (fm.selected().length? fm.selected() : false) || [fm.cwd().hash],
  28325. cnt = sel.length,
  28326. file = fm.file(sel.shift()),
  28327. filename = '.elfinder-cwd-filename',
  28328. opts = cOpts || {},
  28329. incwd = (fm.cwd().hash == file.hash),
  28330. type = (opts._currentType === 'navbar' || opts._currentType === 'files')? opts._currentType : (incwd? 'navbar' : 'files'),
  28331. navbar = (type !== 'files'),
  28332. target = fm[navbar? 'navHash2Elm' : 'cwdHash2Elm'](file.hash),
  28333. tarea = (!navbar && fm.storage('view') != 'list'),
  28334. split = function(name) {
  28335. var ext = fm.splitFileExtention(name)[1];
  28336. return [name.substr(0, name.length - ext.length - 1), ext];
  28337. },
  28338. unselect = function() {
  28339. requestAnimationFrame(function() {
  28340. input && input.trigger('blur');
  28341. });
  28342. },
  28343. rest = function(){
  28344. if (!overlay.is(':hidden')) {
  28345. overlay.elfinderoverlay('hide').off('click close', cancel);
  28346. }
  28347. pnode.removeClass('ui-front')
  28348. .css('position', '')
  28349. .off('unselect.'+fm.namespace, unselect);
  28350. if (tarea) {
  28351. node && node.css('max-height', '');
  28352. } else if (!navbar) {
  28353. pnode.css('width', '')
  28354. .parent('td').css('overflow', '');
  28355. }
  28356. }, colwidth,
  28357. dfrd = $.Deferred()
  28358. .fail(function(error) {
  28359. var parent = input.parent(),
  28360. name = fm.escape(file.i18 || file.name);
  28361. input.off();
  28362. if (tarea) {
  28363. name = name.replace(/([_.])/g, '&#8203;$1');
  28364. }
  28365. requestAnimationFrame(function() {
  28366. if (navbar) {
  28367. input.replaceWith(name);
  28368. } else {
  28369. if (parent.length) {
  28370. input.remove();
  28371. parent.html(name);
  28372. } else {
  28373. target.find(filename).html(name);
  28374. }
  28375. }
  28376. });
  28377. error && fm.error(error);
  28378. })
  28379. .always(function() {
  28380. rest();
  28381. fm.unbind('resize', resize);
  28382. fm.enable();
  28383. }),
  28384. blur = function(e) {
  28385. var name = $.trim(input.val()),
  28386. splits = fm.splitFileExtention(name),
  28387. valid = true,
  28388. req = function() {
  28389. input.off();
  28390. rest();
  28391. if (navbar) {
  28392. input.replaceWith(fm.escape(name));
  28393. } else {
  28394. node.html(fm.escape(name));
  28395. }
  28396. request(dfrd, sel, file, name);
  28397. };
  28398. if (!overlay.is(':hidden')) {
  28399. pnode.css('z-index', '');
  28400. }
  28401. if (name === '') {
  28402. if (!fm.isRoot(file)) {
  28403. return cancel();
  28404. }
  28405. if (navbar) {
  28406. input.replaceWith(fm.escape(file.name));
  28407. } else {
  28408. node.html(fm.escape(file.name));
  28409. }
  28410. }
  28411. if (!inError && pnode.length) {
  28412. input.off('blur');
  28413. if (cnt === 1 && name === file.name) {
  28414. return dfrd.reject();
  28415. }
  28416. if (fm.options.validName && fm.options.validName.test) {
  28417. try {
  28418. valid = fm.options.validName.test(name);
  28419. } catch(e) {
  28420. valid = false;
  28421. }
  28422. }
  28423. if (name === '.' || name === '..' || !valid) {
  28424. inError = true;
  28425. fm.error(file.mime === 'directory'? 'errInvDirname' : 'errInvName', {modal: true, close: function(){setTimeout(select, 120);}});
  28426. return false;
  28427. }
  28428. if (cnt === 1 && fm.fileByName(name, file.phash)) {
  28429. inError = true;
  28430. fm.error(['errExists', name], {modal: true, close: function(){setTimeout(select, 120);}});
  28431. return false;
  28432. }
  28433. if (cnt === 1) {
  28434. req();
  28435. } else {
  28436. fm.confirm({
  28437. title : 'cmdrename',
  28438. text : ['renameMultiple', cnt, getHint(name, [file.hash].concat(sel))],
  28439. accept : {
  28440. label : 'btnYes',
  28441. callback : req
  28442. },
  28443. cancel : {
  28444. label : 'btnCancel',
  28445. callback : function() {
  28446. setTimeout(function() {
  28447. inError = true;
  28448. select();
  28449. }, 120);
  28450. }
  28451. }
  28452. });
  28453. setTimeout(function() {
  28454. fm.trigger('unselectfiles', {files: fm.selected()})
  28455. .trigger('selectfiles', {files : [file.hash].concat(sel)});
  28456. }, 120);
  28457. }
  28458. }
  28459. },
  28460. input = $(tarea? '<textarea/>' : '<input type="text"/>')
  28461. .on('keyup text', function(){
  28462. if (tarea) {
  28463. this.style.height = '1px';
  28464. this.style.height = this.scrollHeight + 'px';
  28465. } else if (colwidth) {
  28466. this.style.width = colwidth + 'px';
  28467. if (this.scrollWidth > colwidth) {
  28468. this.style.width = this.scrollWidth + 10 + 'px';
  28469. }
  28470. }
  28471. })
  28472. .on('keydown', function(e) {
  28473. e.stopImmediatePropagation();
  28474. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  28475. dfrd.reject();
  28476. } else if (e.keyCode == $.ui.keyCode.ENTER) {
  28477. e.preventDefault();
  28478. input.trigger('blur');
  28479. }
  28480. })
  28481. .on('mousedown click dblclick', function(e) {
  28482. e.stopPropagation();
  28483. if (e.type === 'dblclick') {
  28484. e.preventDefault();
  28485. }
  28486. })
  28487. .on('blur', blur)
  28488. .on('dragenter dragleave dragover drop', function(e) {
  28489. // stop bubbling to prevent upload with native drop event
  28490. e.stopPropagation();
  28491. }),
  28492. select = function() {
  28493. var name = fm.splitFileExtention(input.val())[0];
  28494. if (!inError && fm.UA.Mobile && !fm.UA.iOS) { // since iOS has a bug? (z-index not effect) so disable it
  28495. overlay.on('click close', cancel).elfinderoverlay('show');
  28496. pnode.css('z-index', overlay.css('z-index') + 1);
  28497. }
  28498. ! fm.enabled() && fm.enable();
  28499. if (inError) {
  28500. inError = false;
  28501. input.on('blur', blur);
  28502. }
  28503. input.trigger('focus').trigger('select');
  28504. input[0].setSelectionRange && input[0].setSelectionRange(0, name.length);
  28505. },
  28506. node = navbar? target.contents().filter(function(){ return this.nodeType==3 && $(this).parent().attr('id') === fm.navHash2Id(file.hash); })
  28507. : target.find(filename),
  28508. pnode = node.parent(),
  28509. overlay = fm.getUI('overlay'),
  28510. cancel = function(e) {
  28511. if (!overlay.is(':hidden')) {
  28512. pnode.css('z-index', '');
  28513. }
  28514. if (! inError) {
  28515. dfrd.reject();
  28516. if (e) {
  28517. e.stopPropagation();
  28518. e.preventDefault();
  28519. }
  28520. }
  28521. },
  28522. resize = function() {
  28523. target.trigger('scrolltoview', {blink : false});
  28524. },
  28525. inError = false;
  28526. pnode.addClass('ui-front')
  28527. .css('position', 'relative')
  28528. .on('unselect.'+fm.namespace, unselect);
  28529. fm.bind('resize', resize);
  28530. if (navbar) {
  28531. node.replaceWith(input.val(file.name));
  28532. } else {
  28533. if (tarea) {
  28534. node.css('max-height', 'none');
  28535. } else if (!navbar) {
  28536. colwidth = pnode.width();
  28537. pnode.width(colwidth - 15)
  28538. .parent('td').css('overflow', 'visible');
  28539. }
  28540. node.empty().append(input.val(file.name));
  28541. }
  28542. if (cnt > 1 && fm.api <= 2.1030) {
  28543. return dfrd.reject();
  28544. }
  28545. if (!file || !node.length) {
  28546. return dfrd.reject('errCmdParams', this.title);
  28547. }
  28548. if (file.locked && !fm.isRoot(file)) {
  28549. return dfrd.reject(['errLocked', file.name]);
  28550. }
  28551. fm.one('select', function() {
  28552. input.parent().length && file && $.inArray(file.hash, fm.selected()) === -1 && input.trigger('blur');
  28553. });
  28554. input.trigger('keyup');
  28555. select();
  28556. return dfrd;
  28557. };
  28558. fm.bind('select contextmenucreate closecontextmenu', function(e) {
  28559. var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected(),
  28560. file;
  28561. if (sel && sel.length === 1 && (file = fm.file(sel[0])) && fm.isRoot(file)) {
  28562. self.title = fm.i18n('kindAlias') + ' (' + fm.i18n('preference') + ')';
  28563. } else {
  28564. self.title = fm.i18n('cmdrename');
  28565. }
  28566. if (e.type !== 'closecontextmenu') {
  28567. self.update(void(0), self.title);
  28568. } else {
  28569. requestAnimationFrame(function() {
  28570. self.update(void(0), self.title);
  28571. });
  28572. }
  28573. }).remove(function(e) {
  28574. var rootNames;
  28575. if (e.data && e.data.removed && (rootNames = fm.storage('rootNames'))) {
  28576. $.each(e.data.removed, function(i, h) {
  28577. if (rootNames[h]) {
  28578. delete rootNames[h];
  28579. }
  28580. });
  28581. fm.storage('rootNames', rootNames);
  28582. }
  28583. });
  28584. };
  28585. /*
  28586. * File: /js/commands/resize.js
  28587. */
  28588. /**
  28589. * @class elFinder command "resize"
  28590. * Open dialog to resize image
  28591. *
  28592. * @author Dmitry (dio) Levashov
  28593. * @author Alexey Sukhotin
  28594. * @author Naoki Sawada
  28595. * @author Sergio Jovani
  28596. **/
  28597. elFinder.prototype.commands.resize = function() {
  28598. "use strict";
  28599. var losslessRotate = 0,
  28600. getBounceBox = function(w, h, theta) {
  28601. var srcPts = [
  28602. {x: w/2, y: h/2},
  28603. {x: -w/2, y: h/2},
  28604. {x: -w/2, y: -h/2},
  28605. {x: w/2, y: -h/2}
  28606. ],
  28607. dstPts = [],
  28608. min = {x: Number.MAX_VALUE, y: Number.MAX_VALUE},
  28609. max = {x: Number.MIN_VALUE, y: Number.MIN_VALUE};
  28610. $.each(srcPts, function(i, srcPt){
  28611. dstPts.push({
  28612. x: srcPt.x * Math.cos(theta) - srcPt.y * Math.sin(theta),
  28613. y: srcPt.x * Math.sin(theta) + srcPt.y * Math.cos(theta)
  28614. });
  28615. });
  28616. $.each(dstPts, function(i, pt) {
  28617. min.x = Math.min(min.x, pt.x);
  28618. min.y = Math.min(min.y, pt.y);
  28619. max.x = Math.max(max.x, pt.x);
  28620. max.y = Math.max(max.y, pt.y);
  28621. });
  28622. return {
  28623. width: max.x - min.x, height: max.y - min.y
  28624. };
  28625. };
  28626. this.updateOnSelect = false;
  28627. this.getstate = function() {
  28628. var sel = this.fm.selectedFiles();
  28629. return sel.length == 1 && sel[0].read && sel[0].write && sel[0].mime.indexOf('image/') !== -1 ? 0 : -1;
  28630. };
  28631. this.resizeRequest = function(data, f, dfrd) {
  28632. var fm = this.fm,
  28633. file = f || fm.file(data.target),
  28634. tmb = file? file.tmb : null,
  28635. enabled = fm.isCommandEnabled('resize', data.target);
  28636. if (enabled && (! file || (file && file.read && file.write && file.mime.indexOf('image/') !== -1 ))) {
  28637. return fm.request({
  28638. data : Object.assign(data, {
  28639. cmd : 'resize'
  28640. }),
  28641. notify : {type : 'resize', cnt : 1}
  28642. })
  28643. .fail(function(error) {
  28644. if (dfrd) {
  28645. dfrd.reject(error);
  28646. }
  28647. })
  28648. .done(function() {
  28649. if (data.quality) {
  28650. fm.storage('jpgQuality', data.quality === fm.option('jpgQuality')? null : data.quality);
  28651. }
  28652. dfrd && dfrd.resolve();
  28653. });
  28654. } else {
  28655. var error;
  28656. if (file) {
  28657. if (file.mime.indexOf('image/') === -1) {
  28658. error = ['errResize', file.name, 'errUsupportType'];
  28659. } else {
  28660. error = ['errResize', file.name, 'errPerm'];
  28661. }
  28662. } else {
  28663. error = ['errResize', data.target, 'errPerm'];
  28664. }
  28665. if (dfrd) {
  28666. dfrd.reject(error);
  28667. } else {
  28668. fm.error(error);
  28669. }
  28670. return $.Deferred().reject(error);
  28671. }
  28672. };
  28673. this.exec = function(hashes) {
  28674. var self = this,
  28675. fm = this.fm,
  28676. files = this.files(hashes),
  28677. dfrd = $.Deferred(),
  28678. api2 = (fm.api > 1),
  28679. options = this.options,
  28680. dialogWidth = 650,
  28681. fmnode = fm.getUI(),
  28682. ctrgrup = $().controlgroup? 'controlgroup' : 'buttonset',
  28683. grid8Def = typeof options.grid8px === 'undefined' || options.grid8px !== 'disable'? true : false,
  28684. presetSize = Array.isArray(options.presetSize)? options.presetSize : [],
  28685. clactive = 'elfinder-dialog-active',
  28686. clsediting = fm.res('class', 'editing'),
  28687. open = function(file, id) {
  28688. var isJpeg = (file.mime === 'image/jpeg'),
  28689. dialog = $('<div class="elfinder-resize-container"/>'),
  28690. input = '<input type="number" class="ui-corner-all"/>',
  28691. row = '<div class="elfinder-resize-row"/>',
  28692. label = '<div class="elfinder-resize-label"/>',
  28693. changeTm = null,
  28694. operate = false,
  28695. opStart = function() { operate = true; },
  28696. opStop = function() {
  28697. if (operate) {
  28698. operate = false;
  28699. control.trigger('change');
  28700. }
  28701. },
  28702. control = $('<div class="elfinder-resize-control"/>')
  28703. .on('focus', 'input[type=text],input[type=number]', function() {
  28704. $(this).trigger('select');
  28705. })
  28706. .on('change', function() {
  28707. changeTm && cancelAnimationFrame(changeTm);
  28708. changeTm = requestAnimationFrame(function() {
  28709. var panel, quty, canvas, ctx, img, sx, sy, sw, sh, deg, theta, bb;
  28710. if (sizeImg && ! operate && (canvas = sizeImg.data('canvas'))) {
  28711. panel = control.children('div.elfinder-resize-control-panel:visible');
  28712. quty = panel.find('input.elfinder-resize-quality');
  28713. if (quty.is(':visible')) {
  28714. ctx = sizeImg.data('ctx');
  28715. img = sizeImg.get(0);
  28716. if (panel.hasClass('elfinder-resize-uiresize')) {
  28717. // resize
  28718. sw = canvas.width = width.val();
  28719. sh = canvas.height = height.val();
  28720. ctx.drawImage(img, 0, 0, sw, sh);
  28721. } else if (panel.hasClass('elfinder-resize-uicrop')) {
  28722. // crop
  28723. sx = pointX.val();
  28724. sy = pointY.val();
  28725. sw = offsetX.val();
  28726. sh = offsetY.val();
  28727. canvas.width = sw;
  28728. canvas.height = sh;
  28729. ctx.drawImage(img, sx, sy, sw, sh, 0, 0, sw, sh);
  28730. } else {
  28731. // rotate
  28732. deg = degree.val();
  28733. theta = (degree.val() * Math.PI) / 180;
  28734. bb = getBounceBox(owidth, oheight, theta);
  28735. sw = canvas.width = bb.width;
  28736. sh = canvas.height = bb.height;
  28737. ctx.save();
  28738. if (deg % 90 !== 0) {
  28739. ctx.fillStyle = bg.val() || '#FFF';
  28740. ctx.fillRect(0, 0, sw, sh);
  28741. }
  28742. ctx.translate(sw / 2, sh / 2);
  28743. ctx.rotate(theta);
  28744. ctx.drawImage(img, -img.width/2, -img.height/2, owidth, oheight);
  28745. ctx.restore();
  28746. }
  28747. canvas.toBlob(function(blob) {
  28748. blob && quty.next('span').text(' (' + fm.formatSize(blob.size) + ')');
  28749. }, 'image/jpeg', Math.max(Math.min(quty.val(), 100), 1) / 100);
  28750. }
  28751. }
  28752. });
  28753. })
  28754. .on('mouseup', 'input', function(e) {
  28755. $(e.target).trigger('change');
  28756. }),
  28757. preview = $('<div class="elfinder-resize-preview"/>')
  28758. .on('touchmove', function(e) {
  28759. if ($(e.target).hasClass('touch-punch')) {
  28760. e.stopPropagation();
  28761. e.preventDefault();
  28762. }
  28763. }),
  28764. spinner = $('<div class="elfinder-resize-loading">'+fm.i18n('ntfloadimg')+'</div>'),
  28765. rhandle = $('<div class="elfinder-resize-handle touch-punch"/>'),
  28766. rhandlec = $('<div class="elfinder-resize-handle touch-punch"/>'),
  28767. uiresize = $('<div class="elfinder-resize-uiresize elfinder-resize-control-panel"/>'),
  28768. uicrop = $('<div class="elfinder-resize-uicrop elfinder-resize-control-panel"/>'),
  28769. uirotate = $('<div class="elfinder-resize-rotate elfinder-resize-control-panel"/>'),
  28770. uideg270 = $('<button/>').attr('title',fm.i18n('rotate-cw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-l"/>')),
  28771. uideg90 = $('<button/>').attr('title',fm.i18n('rotate-ccw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-r"/>')),
  28772. uiprop = $('<span />'),
  28773. reset = $('<button class="elfinder-resize-reset">').text(fm.i18n('reset'))
  28774. .on('click', function() {
  28775. resetView();
  28776. })
  28777. .button({
  28778. icons: {
  28779. primary: 'ui-icon-arrowrefresh-1-n'
  28780. },
  28781. text: false
  28782. }),
  28783. uitype = $('<div class="elfinder-resize-type"/>')
  28784. .append('<input type="radio" name="type" id="'+id+'-resize" value="resize" checked="checked" /><label for="'+id+'-resize">'+fm.i18n('resize')+'</label>',
  28785. '<input class="api2" type="radio" name="type" id="'+id+'-crop" value="crop" /><label class="api2" for="'+id+'-crop">'+fm.i18n('crop')+'</label>',
  28786. '<input class="api2" type="radio" name="type" id="'+id+'-rotate" value="rotate" /><label class="api2" for="'+id+'-rotate">'+fm.i18n('rotate')+'</label>'),
  28787. mode = 'resize',
  28788. type = uitype[ctrgrup]()[ctrgrup]('disable').find('input')
  28789. .on('change', function() {
  28790. mode = $(this).val();
  28791. resetView();
  28792. resizable(true);
  28793. croppable(true);
  28794. rotateable(true);
  28795. if (mode == 'resize') {
  28796. uiresize.show();
  28797. uirotate.hide();
  28798. uicrop.hide();
  28799. resizable();
  28800. isJpeg && grid8px.insertAfter(uiresize.find('.elfinder-resize-grid8'));
  28801. }
  28802. else if (mode == 'crop') {
  28803. uirotate.hide();
  28804. uiresize.hide();
  28805. uicrop.show();
  28806. croppable();
  28807. isJpeg && grid8px.insertAfter(uicrop.find('.elfinder-resize-grid8'));
  28808. } else if (mode == 'rotate') {
  28809. uiresize.hide();
  28810. uicrop.hide();
  28811. uirotate.show();
  28812. rotateable();
  28813. }
  28814. }),
  28815. width = $(input)
  28816. .on('change', function() {
  28817. var w = round(parseInt(width.val())),
  28818. h = round(cratio ? w/ratio : parseInt(height.val()));
  28819. if (w > 0 && h > 0) {
  28820. resize.updateView(w, h);
  28821. width.val(w);
  28822. height.val(h);
  28823. }
  28824. }).addClass('elfinder-focus'),
  28825. height = $(input)
  28826. .on('change', function() {
  28827. var h = round(parseInt(height.val())),
  28828. w = round(cratio ? h*ratio : parseInt(width.val()));
  28829. if (w > 0 && h > 0) {
  28830. resize.updateView(w, h);
  28831. width.val(w);
  28832. height.val(h);
  28833. }
  28834. }),
  28835. pointX = $(input).on('change', function(){crop.updateView();}),
  28836. pointY = $(input).on('change', function(){crop.updateView();}),
  28837. offsetX = $(input).on('change', function(){crop.updateView('w');}),
  28838. offsetY = $(input).on('change', function(){crop.updateView('h');}),
  28839. quality = isJpeg && api2?
  28840. $(input).val(fm.storage('jpgQuality') > 0? fm.storage('jpgQuality') : fm.option('jpgQuality'))
  28841. .addClass('elfinder-resize-quality')
  28842. .attr('min', '1').attr('max', '100').attr('title', '1 - 100')
  28843. .on('blur', function(){
  28844. var q = Math.min(100, Math.max(1, parseInt(this.value)));
  28845. control.find('input.elfinder-resize-quality').val(q);
  28846. })
  28847. : null,
  28848. degree = $('<input type="number" class="ui-corner-all" maxlength="3" value="0" />')
  28849. .on('change', function() {
  28850. rotate.update();
  28851. }),
  28852. uidegslider = $('<div class="elfinder-resize-rotate-slider touch-punch"/>')
  28853. .slider({
  28854. min: 0,
  28855. max: 360,
  28856. value: degree.val(),
  28857. animate: true,
  28858. start: opStart,
  28859. stop: opStop,
  28860. change: function(event, ui) {
  28861. if (ui.value != uidegslider.slider('value')) {
  28862. rotate.update(ui.value);
  28863. }
  28864. },
  28865. slide: function(event, ui) {
  28866. rotate.update(ui.value, false);
  28867. }
  28868. }).find('.ui-slider-handle')
  28869. .addClass('elfinder-tabstop')
  28870. .off('keydown')
  28871. .on('keydown', function(e) {
  28872. if (e.keyCode == $.ui.keyCode.LEFT || e.keyCode == $.ui.keyCode.RIGHT) {
  28873. e.stopPropagation();
  28874. e.preventDefault();
  28875. rotate.update(Number(degree.val()) + (e.keyCode == $.ui.keyCode.RIGHT? 1 : -1), false);
  28876. }
  28877. })
  28878. .end(),
  28879. pickimg,
  28880. pickcanv,
  28881. pickctx,
  28882. pickc = {},
  28883. pick = function(e) {
  28884. var color, r, g, b, h, s, l;
  28885. try {
  28886. color = pickc[Math.round(e.offsetX)][Math.round(e.offsetY)];
  28887. } catch(e) {}
  28888. if (!color) return;
  28889. r = color[0]; g = color[1]; b = color[2];
  28890. h = color[3]; s = color[4]; l = color[5];
  28891. setbg(r, g, b, (e.type === 'click'));
  28892. },
  28893. palpick = function(e) {
  28894. setbg($(this).css('backgroundColor'), '', '', (e.type === 'click'));
  28895. },
  28896. setbg = function(r, g, b, off) {
  28897. var s, m, cc;
  28898. if (typeof r === 'string') {
  28899. g = '';
  28900. if (r && (s = $('<span>').css('backgroundColor', r).css('backgroundColor')) && (m = s.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i))) {
  28901. r = Number(m[1]);
  28902. g = Number(m[2]);
  28903. b = Number(m[3]);
  28904. }
  28905. }
  28906. cc = (g === '')? r : '#' + getColorCode(r, g, b);
  28907. bg.val(cc).css({ backgroundColor: cc, backgroundImage: 'none', color: (r+g+b < 384? '#fff' : '#000') });
  28908. preview.css('backgroundColor', cc);
  28909. if (off) {
  28910. imgr.off('.picker').removeClass('elfinder-resize-picking');
  28911. pallet.off('.picker').removeClass('elfinder-resize-picking');
  28912. }
  28913. },
  28914. getColorCode = function(r, g, b) {
  28915. return $.map([r,g,b], function(c){return ('0'+parseInt(c).toString(16)).slice(-2);}).join('');
  28916. },
  28917. picker = $('<button>').text(fm.i18n('colorPicker'))
  28918. .on('click', function() {
  28919. imgr.on('mousemove.picker click.picker', pick).addClass('elfinder-resize-picking');
  28920. pallet.on('mousemove.picker click.picker', 'span', palpick).addClass('elfinder-resize-picking');
  28921. })
  28922. .button({
  28923. icons: {
  28924. primary: 'ui-icon-pin-s'
  28925. },
  28926. text: false
  28927. }),
  28928. reseter = $('<button>').text(fm.i18n('reset'))
  28929. .on('click', function() {
  28930. setbg('', '', '', true);
  28931. })
  28932. .button({
  28933. icons: {
  28934. primary: 'ui-icon-arrowrefresh-1-n'
  28935. },
  28936. text: false
  28937. }),
  28938. bg = $('<input class="ui-corner-all elfinder-resize-bg" type="text">')
  28939. .on('focus', function() {
  28940. $(this).attr('style', '');
  28941. })
  28942. .on('blur', function() {
  28943. setbg($(this).val());
  28944. }),
  28945. pallet = $('<div class="elfinder-resize-pallet">').on('click', 'span', function() {
  28946. setbg($(this).css('backgroundColor'));
  28947. }),
  28948. ratio = 1,
  28949. prop = 1,
  28950. owidth = 0,
  28951. oheight = 0,
  28952. cratio = true,
  28953. cratioc = false,
  28954. pwidth = 0,
  28955. pheight = 0,
  28956. rwidth = 0,
  28957. rheight = 0,
  28958. rdegree = 0,
  28959. grid8 = isJpeg? grid8Def : false,
  28960. constr = $('<button>').html(fm.i18n('aspectRatio'))
  28961. .on('click', function() {
  28962. cratio = ! cratio;
  28963. constr.button('option', {
  28964. icons : { primary: cratio? 'ui-icon-locked' : 'ui-icon-unlocked'}
  28965. });
  28966. resize.fixHeight();
  28967. rhandle.resizable('option', 'aspectRatio', cratio).data('uiResizable')._aspectRatio = cratio;
  28968. })
  28969. .button({
  28970. icons : {
  28971. primary: cratio? 'ui-icon-locked' : 'ui-icon-unlocked'
  28972. },
  28973. text: false
  28974. }),
  28975. constrc = $('<button>').html(fm.i18n('aspectRatio'))
  28976. .on('click', function() {
  28977. cratioc = ! cratioc;
  28978. constrc.button('option', {
  28979. icons : { primary: cratioc? 'ui-icon-locked' : 'ui-icon-unlocked'}
  28980. });
  28981. rhandlec.resizable('option', 'aspectRatio', cratioc).data('uiResizable')._aspectRatio = cratioc;
  28982. })
  28983. .button({
  28984. icons : {
  28985. primary: cratioc? 'ui-icon-locked' : 'ui-icon-unlocked'
  28986. },
  28987. text: false
  28988. }),
  28989. grid8px = $('<button>').html(fm.i18n(grid8? 'enabled' : 'disabled')).toggleClass('ui-state-active', grid8)
  28990. .on('click', function() {
  28991. grid8 = ! grid8;
  28992. grid8px.html(fm.i18n(grid8? 'enabled' : 'disabled')).toggleClass('ui-state-active', grid8);
  28993. setStep8();
  28994. })
  28995. .button(),
  28996. setStep8 = function() {
  28997. var step = grid8? 8 : 1;
  28998. $.each([width, height, offsetX, offsetY, pointX, pointY], function() {
  28999. this.attr('step', step);
  29000. });
  29001. if (grid8) {
  29002. width.val(round(width.val()));
  29003. height.val(round(height.val()));
  29004. offsetX.val(round(offsetX.val()));
  29005. offsetY.val(round(offsetY.val()));
  29006. pointX.val(round(pointX.val()));
  29007. pointY.val(round(pointY.val()));
  29008. if (uiresize.is(':visible')) {
  29009. resize.updateView(width.val(), height.val());
  29010. } else if (uicrop.is(':visible')) {
  29011. crop.updateView();
  29012. }
  29013. }
  29014. },
  29015. setuprimg = function() {
  29016. var r_scale,
  29017. fail = function() {
  29018. bg.parent().hide();
  29019. pallet.hide();
  29020. };
  29021. r_scale = Math.min(pwidth, pheight) / Math.sqrt(Math.pow(owidth, 2) + Math.pow(oheight, 2));
  29022. rwidth = Math.ceil(owidth * r_scale);
  29023. rheight = Math.ceil(oheight * r_scale);
  29024. imgr.width(rwidth)
  29025. .height(rheight)
  29026. .css('margin-top', (pheight-rheight)/2 + 'px')
  29027. .css('margin-left', (pwidth-rwidth)/2 + 'px');
  29028. if (imgr.is(':visible') && bg.is(':visible')) {
  29029. if (file.mime !== 'image/png') {
  29030. preview.css('backgroundColor', bg.val());
  29031. pickimg = $('<img>');
  29032. if (fm.isCORS) {
  29033. pickimg.attr('crossorigin', 'use-credentials');
  29034. }
  29035. pickimg.on('load', function() {
  29036. if (pickcanv && pickcanv.width !== rwidth) {
  29037. setColorData();
  29038. }
  29039. })
  29040. .on('error', fail)
  29041. .attr('src', canvSrc);
  29042. } else {
  29043. fail();
  29044. }
  29045. }
  29046. },
  29047. setupimg = function() {
  29048. resize.updateView(owidth, oheight);
  29049. setuprimg();
  29050. basec
  29051. .width(img.width())
  29052. .height(img.height());
  29053. imgc
  29054. .width(img.width())
  29055. .height(img.height());
  29056. crop.updateView();
  29057. jpgCalc();
  29058. },
  29059. setColorData = function() {
  29060. if (pickctx) {
  29061. var n, w, h, r, g, b, a, s, l, hsl, hue,
  29062. data, scale, tx1, tx2, ty1, ty2, rgb,
  29063. domi = {},
  29064. domic = [],
  29065. domiv, palc,
  29066. rgbToHsl = function (r, g, b) {
  29067. var h, s, l,
  29068. max = Math.max(Math.max(r, g), b),
  29069. min = Math.min(Math.min(r, g), b);
  29070. // Hue, 0 ~ 359
  29071. if (max === min) {
  29072. h = 0;
  29073. } else if (r === max) {
  29074. h = ((g - b) / (max - min) * 60 + 360) % 360;
  29075. } else if (g === max) {
  29076. h = (b - r) / (max - min) * 60 + 120;
  29077. } else if (b === max) {
  29078. h = (r - g) / (max - min) * 60 + 240;
  29079. }
  29080. // Saturation, 0 ~ 1
  29081. s = (max - min) / max;
  29082. // Lightness, 0 ~ 1
  29083. l = (r * 0.3 + g * 0.59 + b * 0.11) / 255;
  29084. return [h, s, l, 'hsl'];
  29085. },
  29086. rgbRound = function(c) {
  29087. return Math.round(c / 8) * 8;
  29088. };
  29089. calc:
  29090. try {
  29091. w = pickcanv.width = imgr.width();
  29092. h = pickcanv.height = imgr.height();
  29093. scale = w / owidth;
  29094. pickctx.scale(scale, scale);
  29095. pickctx.drawImage(pickimg.get(0), 0, 0);
  29096. data = pickctx.getImageData(0, 0, w, h).data;
  29097. // Range to detect the dominant color
  29098. tx1 = w * 0.1;
  29099. tx2 = w * 0.9;
  29100. ty1 = h * 0.1;
  29101. ty2 = h * 0.9;
  29102. for (var y = 0; y < h - 1; y++) {
  29103. for (var x = 0; x < w - 1; x++) {
  29104. n = x * 4 + y * w * 4;
  29105. // RGB
  29106. r = data[n]; g = data[n + 1]; b = data[n + 2]; a = data[n + 3];
  29107. // check alpha ch
  29108. if (a !== 255) {
  29109. bg.parent().hide();
  29110. pallet.hide();
  29111. break calc;
  29112. }
  29113. // HSL
  29114. hsl = rgbToHsl(r, g, b);
  29115. hue = Math.round(hsl[0]); s = Math.round(hsl[1] * 100); l = Math.round(hsl[2] * 100);
  29116. if (! pickc[x]) {
  29117. pickc[x] = {};
  29118. }
  29119. // set pickc
  29120. pickc[x][y] = [r, g, b, hue, s, l];
  29121. // detect the dominant color
  29122. if ((x < tx1 || x > tx2) && (y < ty1 || y > ty2)) {
  29123. rgb = rgbRound(r) + ',' + rgbRound(g) + ',' + rgbRound(b);
  29124. if (! domi[rgb]) {
  29125. domi[rgb] = 1;
  29126. } else {
  29127. ++domi[rgb];
  29128. }
  29129. }
  29130. }
  29131. }
  29132. if (! pallet.children(':first').length) {
  29133. palc = 1;
  29134. $.each(domi, function(c, v) {
  29135. domic.push({c: c, v: v});
  29136. });
  29137. $.each(domic.sort(function(a, b) {
  29138. return (a.v > b.v)? -1 : 1;
  29139. }), function() {
  29140. if (this.v < 2 || palc > 10) {
  29141. return false;
  29142. }
  29143. pallet.append($('<span style="width:20px;height:20px;display:inline-block;background-color:rgb('+this.c+');">'));
  29144. ++palc;
  29145. });
  29146. }
  29147. } catch(e) {
  29148. picker.hide();
  29149. pallet.hide();
  29150. }
  29151. }
  29152. },
  29153. setupPicker = function() {
  29154. try {
  29155. pickcanv = document.createElement('canvas');
  29156. pickctx = pickcanv.getContext('2d');
  29157. } catch(e) {
  29158. picker.hide();
  29159. pallet.hide();
  29160. }
  29161. },
  29162. setupPreset = function() {
  29163. preset.on('click', 'span.elfinder-resize-preset', function() {
  29164. var btn = $(this),
  29165. w = btn.data('s')[0],
  29166. h = btn.data('s')[1],
  29167. r = owidth / oheight;
  29168. btn.data('s', [h, w]).text(h + 'x' + w);
  29169. if (owidth > w || oheight > h) {
  29170. if (owidth <= w) {
  29171. w = round(h * r);
  29172. } else if (oheight <= h) {
  29173. h = round(w / r);
  29174. } else {
  29175. if (owidth - w > oheight - h) {
  29176. h = round(w / r);
  29177. } else {
  29178. w = round(h * r);
  29179. }
  29180. }
  29181. } else {
  29182. w = owidth;
  29183. h = oheight;
  29184. }
  29185. width.val(w);
  29186. height.val(h);
  29187. resize.updateView(w, h);
  29188. jpgCalc();
  29189. });
  29190. presetc.on('click', 'span.elfinder-resize-preset', function() {
  29191. var btn = $(this),
  29192. w = btn.data('s')[0],
  29193. h = btn.data('s')[1],
  29194. x = pointX.val(),
  29195. y = pointY.val();
  29196. btn.data('s', [h, w]).text(h + 'x' + w);
  29197. if (owidth >= w && oheight >= h) {
  29198. if (owidth - w - x < 0) {
  29199. x = owidth - w;
  29200. }
  29201. if (oheight - h - y < 0) {
  29202. y = oheight - h;
  29203. }
  29204. pointX.val(x);
  29205. pointY.val(y);
  29206. offsetX.val(w);
  29207. offsetY.val(h);
  29208. crop.updateView();
  29209. jpgCalc();
  29210. }
  29211. });
  29212. presetc.children('span.elfinder-resize-preset').each(function() {
  29213. var btn = $(this),
  29214. w = btn.data('s')[0],
  29215. h = btn.data('s')[1];
  29216. btn[(owidth >= w && oheight >= h)? 'show' : 'hide']();
  29217. });
  29218. },
  29219. dimreq = null,
  29220. inited = false,
  29221. setdim = function(dim) {
  29222. var rfile = fm.file(file.hash);
  29223. rfile.width = dim[0];
  29224. rfile.height = dim[1];
  29225. },
  29226. init = function() {
  29227. var elm, memSize, r_scale, imgRatio;
  29228. if (inited) {
  29229. return;
  29230. }
  29231. inited = true;
  29232. dimreq && dimreq.state && dimreq.state() === 'pending' && dimreq.reject();
  29233. // check lossless rotete
  29234. if (fm.api >= 2.1030) {
  29235. if (losslessRotate === 0) {
  29236. fm.request({
  29237. data: {
  29238. cmd : 'resize',
  29239. target : file.hash,
  29240. degree : 0,
  29241. mode : 'rotate'
  29242. },
  29243. preventDefault : true
  29244. }).done(function(data) {
  29245. losslessRotate = data.losslessRotate? 1 : -1;
  29246. if (losslessRotate === 1 && (degree.val() % 90 === 0)) {
  29247. uirotate.children('div.elfinder-resize-quality').hide();
  29248. }
  29249. }).fail(function() {
  29250. losslessRotate = -1;
  29251. });
  29252. }
  29253. } else {
  29254. losslessRotate = -1;
  29255. }
  29256. elm = img.get(0);
  29257. memSize = file.width && file.height? {w: file.width, h: file.height} : (elm.naturalWidth? null : {w: img.width(), h: img.height()});
  29258. memSize && img.removeAttr('width').removeAttr('height');
  29259. owidth = file.width || elm.naturalWidth || elm.width || img.width();
  29260. oheight = file.height || elm.naturalHeight || elm.height || img.height();
  29261. if (!file.width || !file.height) {
  29262. setdim([owidth, oheight]);
  29263. }
  29264. memSize && img.width(memSize.w).height(memSize.h);
  29265. dMinBtn.show();
  29266. imgRatio = oheight / owidth;
  29267. if (imgRatio < 1 && preview.height() > preview.width() * imgRatio) {
  29268. preview.height(preview.width() * imgRatio);
  29269. }
  29270. if (preview.height() > img.height() + 20) {
  29271. preview.height(img.height() + 20);
  29272. }
  29273. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  29274. spinner.remove();
  29275. ratio = owidth/oheight;
  29276. rhandle.append(img.show()).show();
  29277. width.val(owidth);
  29278. height.val(oheight);
  29279. setupPicker();
  29280. setupPreset();
  29281. setupimg();
  29282. uitype[ctrgrup]('enable');
  29283. control.find('input,select').prop('disabled', false)
  29284. .filter(':text').on('keydown', function(e) {
  29285. var cOpts;
  29286. if (e.keyCode == $.ui.keyCode.ENTER) {
  29287. e.stopPropagation();
  29288. e.preventDefault();
  29289. cOpts = {
  29290. title : $('input:checked', uitype).val(),
  29291. text : 'confirmReq',
  29292. accept : {
  29293. label : 'btnApply',
  29294. callback : function() {
  29295. save();
  29296. }
  29297. },
  29298. cancel : {
  29299. label : 'btnCancel',
  29300. callback : function(){
  29301. $(this).trigger('focus');
  29302. }
  29303. }
  29304. };
  29305. if (useSaveAs) {
  29306. cOpts['buttons'] = [{
  29307. label : 'btnSaveAs',
  29308. callback : function() {
  29309. requestAnimationFrame(saveAs);
  29310. }
  29311. }];
  29312. }
  29313. fm.confirm(cOpts);
  29314. return;
  29315. }
  29316. })
  29317. .on('keyup', function() {
  29318. var $this = $(this);
  29319. if (! $this.hasClass('elfinder-resize-bg')) {
  29320. requestAnimationFrame(function() {
  29321. $this.val($this.val().replace(/[^0-9]/g, ''));
  29322. });
  29323. }
  29324. })
  29325. .filter(':first');
  29326. setStep8();
  29327. !fm.UA.Mobile && width.trigger('focus');
  29328. resizable();
  29329. },
  29330. img = $('<img/>')
  29331. .on('load', init)
  29332. .on('error', function() {
  29333. spinner.text('Unable to load image').css('background', 'transparent');
  29334. }),
  29335. basec = $('<div/>'),
  29336. imgc = $('<img/>'),
  29337. coverc = $('<div/>'),
  29338. imgr = $('<img class="elfinder-resize-imgrotate" />'),
  29339. round = function(v, max) {
  29340. v = grid8? Math.round(v/8)*8 : Math.round(v);
  29341. v = Math.max(0, v);
  29342. if (max && v > max) {
  29343. v = grid8? Math.floor(max/8)*8 : max;
  29344. }
  29345. return v;
  29346. },
  29347. resetView = function() {
  29348. width.val(owidth);
  29349. height.val(oheight);
  29350. resize.updateView(owidth, oheight);
  29351. pointX.val(0);
  29352. pointY.val(0);
  29353. offsetX.val(owidth);
  29354. offsetY.val(oheight);
  29355. crop.updateView();
  29356. jpgCalc();
  29357. },
  29358. resize = {
  29359. update : function() {
  29360. width.val(round(img.width()/prop));
  29361. height.val(round(img.height()/prop));
  29362. jpgCalc();
  29363. },
  29364. updateView : function(w, h) {
  29365. if (w > pwidth || h > pheight) {
  29366. if (w / pwidth > h / pheight) {
  29367. prop = pwidth / w;
  29368. img.width(pwidth).height(round(h*prop));
  29369. } else {
  29370. prop = pheight / h;
  29371. img.height(pheight).width(round(w*prop));
  29372. }
  29373. } else {
  29374. img.width(round(w)).height(round(h));
  29375. }
  29376. prop = img.width()/w;
  29377. uiprop.text('1 : '+(1/prop).toFixed(2));
  29378. resize.updateHandle();
  29379. },
  29380. updateHandle : function() {
  29381. rhandle.width(img.width()).height(img.height());
  29382. },
  29383. fixHeight : function() {
  29384. var w, h;
  29385. if (cratio) {
  29386. w = width.val();
  29387. h = round(w/ratio);
  29388. resize.updateView(w, h);
  29389. height.val(h);
  29390. }
  29391. }
  29392. },
  29393. crop = {
  29394. update : function(change) {
  29395. pointX.val(round(((rhandlec.data('x')||rhandlec.position().left))/prop, owidth));
  29396. pointY.val(round(((rhandlec.data('y')||rhandlec.position().top))/prop, oheight));
  29397. if (change !== 'xy') {
  29398. offsetX.val(round((rhandlec.data('w')||rhandlec.width())/prop, owidth - pointX.val()));
  29399. offsetY.val(round((rhandlec.data('h')||rhandlec.height())/prop, oheight - pointY.val()));
  29400. }
  29401. jpgCalc();
  29402. },
  29403. updateView : function(change) {
  29404. var r, x, y, w, h;
  29405. pointX.val(round(pointX.val(), owidth - (grid8? 8 : 1)));
  29406. pointY.val(round(pointY.val(), oheight - (grid8? 8 : 1)));
  29407. offsetX.val(round(offsetX.val(), owidth - pointX.val()));
  29408. offsetY.val(round(offsetY.val(), oheight - pointY.val()));
  29409. if (cratioc) {
  29410. r = coverc.width() / coverc.height();
  29411. if (change === 'w') {
  29412. offsetY.val(round(parseInt(offsetX.val()) / r));
  29413. } else if (change === 'h') {
  29414. offsetX.val(round(parseInt(offsetY.val()) * r));
  29415. }
  29416. }
  29417. x = Math.round(parseInt(pointX.val()) * prop);
  29418. y = Math.round(parseInt(pointY.val()) * prop);
  29419. if (change !== 'xy') {
  29420. w = Math.round(parseInt(offsetX.val()) * prop);
  29421. h = Math.round(parseInt(offsetY.val()) * prop);
  29422. } else {
  29423. w = rhandlec.data('w');
  29424. h = rhandlec.data('h');
  29425. }
  29426. rhandlec.data({x: x, y: y, w: w, h: h})
  29427. .width(w)
  29428. .height(h)
  29429. .css({left: x, top: y});
  29430. coverc.width(w)
  29431. .height(h);
  29432. },
  29433. resize_update : function(e, ui) {
  29434. rhandlec.data({x: ui.position.left, y: ui.position.top, w: ui.size.width, h: ui.size.height});
  29435. crop.update();
  29436. crop.updateView();
  29437. },
  29438. drag_update : function(e, ui) {
  29439. rhandlec.data({x: ui.position.left, y: ui.position.top});
  29440. crop.update('xy');
  29441. }
  29442. },
  29443. rotate = {
  29444. mouseStartAngle : 0,
  29445. imageStartAngle : 0,
  29446. imageBeingRotated : false,
  29447. setQuality : function() {
  29448. uirotate.children('div.elfinder-resize-quality')[(losslessRotate > 0 && (degree.val() % 90) === 0)? 'hide' : 'show']();
  29449. },
  29450. update : function(value, animate) {
  29451. if (typeof value == 'undefined') {
  29452. rdegree = value = parseInt(degree.val());
  29453. }
  29454. if (typeof animate == 'undefined') {
  29455. animate = true;
  29456. }
  29457. if (! animate || fm.UA.Opera || fm.UA.ltIE8) {
  29458. imgr.rotate(value);
  29459. } else {
  29460. imgr.animate({rotate: value + 'deg'});
  29461. }
  29462. value = value % 360;
  29463. if (value < 0) {
  29464. value += 360;
  29465. }
  29466. degree.val(parseInt(value));
  29467. uidegslider.slider('value', degree.val());
  29468. rotate.setQuality();
  29469. },
  29470. execute : function ( e ) {
  29471. if ( !rotate.imageBeingRotated ) return;
  29472. var imageCentre = rotate.getCenter( imgr );
  29473. var ev = e.originalEvent.touches? e.originalEvent.touches[0] : e;
  29474. var mouseXFromCentre = ev.pageX - imageCentre[0];
  29475. var mouseYFromCentre = ev.pageY - imageCentre[1];
  29476. var mouseAngle = Math.atan2( mouseYFromCentre, mouseXFromCentre );
  29477. var rotateAngle = mouseAngle - rotate.mouseStartAngle + rotate.imageStartAngle;
  29478. rotateAngle = Math.round(parseFloat(rotateAngle) * 180 / Math.PI);
  29479. if ( e.shiftKey ) {
  29480. rotateAngle = Math.round((rotateAngle + 6)/15) * 15;
  29481. }
  29482. imgr.rotate(rotateAngle);
  29483. rotateAngle = rotateAngle % 360;
  29484. if (rotateAngle < 0) {
  29485. rotateAngle += 360;
  29486. }
  29487. degree.val(rotateAngle);
  29488. uidegslider.slider('value', degree.val());
  29489. rotate.setQuality();
  29490. return false;
  29491. },
  29492. start : function ( e ) {
  29493. if (imgr.hasClass('elfinder-resize-picking')) {
  29494. return;
  29495. }
  29496. opStart();
  29497. rotate.imageBeingRotated = true;
  29498. var imageCentre = rotate.getCenter( imgr );
  29499. var ev = e.originalEvent.touches? e.originalEvent.touches[0] : e;
  29500. var mouseStartXFromCentre = ev.pageX - imageCentre[0];
  29501. var mouseStartYFromCentre = ev.pageY - imageCentre[1];
  29502. rotate.mouseStartAngle = Math.atan2( mouseStartYFromCentre, mouseStartXFromCentre );
  29503. rotate.imageStartAngle = parseFloat(imgr.rotate()) * Math.PI / 180.0;
  29504. $(document).on('mousemove', rotate.execute);
  29505. imgr.on('touchmove', rotate.execute);
  29506. return false;
  29507. },
  29508. stop : function ( e ) {
  29509. if ( !rotate.imageBeingRotated ) return;
  29510. $(document).off('mousemove', rotate.execute);
  29511. imgr.off('touchmove', rotate.execute);
  29512. requestAnimationFrame(function() { rotate.imageBeingRotated = false; });
  29513. opStop();
  29514. return false;
  29515. },
  29516. getCenter : function ( image ) {
  29517. var currentRotation = imgr.rotate();
  29518. imgr.rotate(0);
  29519. var imageOffset = imgr.offset();
  29520. var imageCentreX = imageOffset.left + imgr.width() / 2;
  29521. var imageCentreY = imageOffset.top + imgr.height() / 2;
  29522. imgr.rotate(currentRotation);
  29523. return Array( imageCentreX, imageCentreY );
  29524. }
  29525. },
  29526. resizable = function(destroy) {
  29527. if (destroy) {
  29528. rhandle.filter(':ui-resizable').resizable('destroy');
  29529. rhandle.hide();
  29530. }
  29531. else {
  29532. rhandle.show();
  29533. rhandle.resizable({
  29534. alsoResize : img,
  29535. aspectRatio : cratio,
  29536. resize : resize.update,
  29537. start : opStart,
  29538. stop : function(e) {
  29539. resize.fixHeight;
  29540. resize.updateView(width.val(), height.val());
  29541. opStop();
  29542. }
  29543. });
  29544. dinit();
  29545. }
  29546. },
  29547. croppable = function(destroy) {
  29548. if (destroy) {
  29549. rhandlec.filter(':ui-resizable').resizable('destroy')
  29550. .filter(':ui-draggable').draggable('destroy');
  29551. basec.hide();
  29552. }
  29553. else {
  29554. basec.show();
  29555. rhandlec
  29556. .resizable({
  29557. containment : basec,
  29558. aspectRatio : cratioc,
  29559. resize : crop.resize_update,
  29560. start : opStart,
  29561. stop : opStop,
  29562. handles : 'all'
  29563. })
  29564. .draggable({
  29565. handle : coverc,
  29566. containment : imgc,
  29567. drag : crop.drag_update,
  29568. start : opStart,
  29569. stop : function() {
  29570. crop.updateView('xy');
  29571. opStop();
  29572. }
  29573. });
  29574. dinit();
  29575. crop.update();
  29576. }
  29577. },
  29578. rotateable = function(destroy) {
  29579. if (destroy) {
  29580. imgr.hide();
  29581. }
  29582. else {
  29583. imgr.show();
  29584. dinit();
  29585. }
  29586. },
  29587. checkVals = function() {
  29588. var w, h, x, y, d, q, b = '';
  29589. if (mode == 'resize') {
  29590. w = parseInt(width.val()) || 0;
  29591. h = parseInt(height.val()) || 0;
  29592. } else if (mode == 'crop') {
  29593. w = parseInt(offsetX.val()) || 0;
  29594. h = parseInt(offsetY.val()) || 0;
  29595. x = parseInt(pointX.val()) || 0;
  29596. y = parseInt(pointY.val()) || 0;
  29597. } else if (mode == 'rotate') {
  29598. w = owidth;
  29599. h = oheight;
  29600. d = parseInt(degree.val()) || 0;
  29601. if (d < 0 || d > 360) {
  29602. fm.error('Invalid rotate degree');
  29603. return false;
  29604. }
  29605. if (d == 0 || d == 360) {
  29606. fm.error('errResizeNoChange');
  29607. return false;
  29608. }
  29609. b = bg.val();
  29610. }
  29611. q = quality? parseInt(quality.val()) : 0;
  29612. if (mode != 'rotate') {
  29613. if (w <= 0 || h <= 0) {
  29614. fm.error('Invalid image size');
  29615. return false;
  29616. }
  29617. if (w == owidth && h == oheight) {
  29618. fm.error('errResizeNoChange');
  29619. return false;
  29620. }
  29621. }
  29622. return {w: w, h: h, x: x, y: y, d: d, q: q, b: b};
  29623. },
  29624. save = function() {
  29625. var vals;
  29626. if (vals = checkVals()) {
  29627. dialog.elfinderdialog('close');
  29628. self.resizeRequest({
  29629. target : file.hash,
  29630. width : vals.w,
  29631. height : vals.h,
  29632. x : vals.x,
  29633. y : vals.y,
  29634. degree : vals.d,
  29635. quality: vals.q,
  29636. bg : vals.b,
  29637. mode : mode
  29638. }, file, dfrd);
  29639. }
  29640. },
  29641. saveAs = function() {
  29642. var fail = function() {
  29643. dialogs.addClass(clsediting).fadeIn(function() {
  29644. base.addClass(clactive);
  29645. });
  29646. fm.disable();
  29647. },
  29648. make = function() {
  29649. self.mime = file.mime;
  29650. self.prefix = file.name.replace(/ \d+(\.[^.]+)?$/, '$1');
  29651. self.requestCmd = 'mkfile';
  29652. self.nextAction = {};
  29653. self.data = {target : file.phash};
  29654. $.proxy(fm.res('mixin', 'make'), self)()
  29655. .done(function(data) {
  29656. var hash, dfd;
  29657. if (data.added && data.added.length) {
  29658. hash = data.added[0].hash;
  29659. dfd = fm.api < 2.1032? fm.url(file.hash, { async: true, temporary: true }) : null;
  29660. $.when(dfd).done(function(url) {
  29661. fm.request({
  29662. options : {type : 'post'},
  29663. data : {
  29664. cmd : 'put',
  29665. target : hash,
  29666. encoding: dfd? 'scheme' : 'hash',
  29667. content : dfd? fm.convAbsUrl(url) : file.hash
  29668. },
  29669. notify : {type : 'copy', cnt : 1},
  29670. syncOnFail : true
  29671. })
  29672. .fail(fail)
  29673. .done(function(data) {
  29674. data = fm.normalize(data);
  29675. fm.updateCache(data);
  29676. file = fm.file(hash);
  29677. data.changed && data.changed.length && fm.change(data);
  29678. base.show().find('.elfinder-dialog-title').html(fm.escape(file.name));
  29679. save();
  29680. dialogs.fadeIn();
  29681. });
  29682. }).fail(fail);
  29683. } else {
  29684. fail();
  29685. }
  29686. })
  29687. .fail(fail)
  29688. .always(function() {
  29689. delete self.mime;
  29690. delete self.prefix;
  29691. delete self.nextAction;
  29692. delete self.data;
  29693. });
  29694. fm.trigger('unselectfiles', { files: [ file.hash ] });
  29695. },
  29696. reqOpen = null,
  29697. dialogs;
  29698. if (checkVals()) {
  29699. dialogs = fmnode.children('.' + self.dialogClass + ':visible').removeClass(clsediting).fadeOut();
  29700. base.removeClass(clactive);
  29701. fm.enable();
  29702. if (fm.searchStatus.state < 2 && file.phash !== fm.cwd().hash) {
  29703. reqOpen = fm.exec('open', [file.phash], {thash: file.phash});
  29704. }
  29705. $.when([reqOpen]).done(function() {
  29706. reqOpen? fm.one('cwdrender', make) : make();
  29707. }).fail(fail);
  29708. }
  29709. },
  29710. buttons = {},
  29711. hline = 'elfinder-resize-handle-hline',
  29712. vline = 'elfinder-resize-handle-vline',
  29713. rpoint = 'elfinder-resize-handle-point',
  29714. src = fm.openUrl(file.hash),
  29715. canvSrc = fm.openUrl(file.hash, !fm.isSameOrigin(src)),
  29716. sizeImg = quality? $('<img>').attr('crossorigin', fm.isCORS? 'use-credentials' : '').attr('src', canvSrc).on('load', function() {
  29717. try {
  29718. var canv = document.createElement('canvas');
  29719. sizeImg.data('canvas', canv).data('ctx', canv.getContext('2d'));
  29720. jpgCalc();
  29721. } catch(e) {
  29722. sizeImg.removeData('canvas').removeData('ctx');
  29723. }
  29724. }) : null,
  29725. jpgCalc = function() {
  29726. control.find('input.elfinder-resize-quality:visible').trigger('change');
  29727. },
  29728. dinit = function(e) {
  29729. if (base.hasClass('elfinder-dialog-minimized') || base.is(':hidden')) {
  29730. return;
  29731. }
  29732. preset.hide();
  29733. presetc.hide();
  29734. var win = fm.options.dialogContained? fmnode : $(window),
  29735. winH = win.height(),
  29736. winW = win.width(),
  29737. presW = 'auto',
  29738. presIn = true,
  29739. dw, ctrW, prvW;
  29740. base.width(Math.min(dialogWidth, winW - 30));
  29741. preview.attr('style', '');
  29742. if (owidth && oheight) {
  29743. pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width());
  29744. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  29745. resize.updateView(owidth, oheight);
  29746. }
  29747. ctrW = dialog.find('div.elfinder-resize-control').width();
  29748. prvW = preview.width();
  29749. dw = dialog.width() - 20;
  29750. if (prvW > dw) {
  29751. preview.width(dw);
  29752. presIn = false;
  29753. } else if ((dw - prvW) < ctrW) {
  29754. if (winW > winH) {
  29755. preview.width(dw - ctrW - 20);
  29756. } else {
  29757. preview.css({ float: 'none', marginLeft: 'auto', marginRight: 'auto'});
  29758. presIn = false;
  29759. }
  29760. }
  29761. if (presIn) {
  29762. presW = ctrW;
  29763. }
  29764. pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width());
  29765. if (fmnode.hasClass('elfinder-fullscreen')) {
  29766. if (base.height() > winH) {
  29767. winH -= 2;
  29768. preview.height(winH - base.height() + preview.height());
  29769. base.css('top', 0 - fmnode.offset().top);
  29770. }
  29771. } else {
  29772. winH -= 30;
  29773. (preview.height() > winH) && preview.height(winH);
  29774. }
  29775. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  29776. if (owidth && oheight) {
  29777. setupimg();
  29778. }
  29779. if (img.height() && preview.height() > img.height() + 20) {
  29780. preview.height(img.height() + 20);
  29781. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  29782. setuprimg();
  29783. }
  29784. preset.css('width', presW).show();
  29785. presetc.css('width', presW).show();
  29786. if (!presetc.children('span.elfinder-resize-preset:visible').length) {
  29787. presetc.hide();
  29788. }
  29789. },
  29790. preset = (function() {
  29791. var sets = $('<fieldset class="elfinder-resize-preset-container">').append($('<legend>').html(fm.i18n('presets'))).hide(),
  29792. hasC;
  29793. $.each(presetSize, function(i, s) {
  29794. if (s.length === 2) {
  29795. hasC = true;
  29796. sets.append($('<span class="elfinder-resize-preset"/>')
  29797. .data('s', s)
  29798. .text(s[0]+'x'+s[1])
  29799. .button()
  29800. );
  29801. }
  29802. });
  29803. if (!hasC) {
  29804. return $();
  29805. } else {
  29806. return sets;
  29807. }
  29808. })(),
  29809. presetc = preset.clone(true),
  29810. useSaveAs = fm.uploadMimeCheck(file.mime, file.phash),
  29811. dMinBtn, base;
  29812. uiresize.append(
  29813. $(row).append($(label).text(fm.i18n('width')), width),
  29814. $(row).append($(label).text(fm.i18n('height')), height, $('<div class="elfinder-resize-whctrls">').append(constr, reset)),
  29815. (quality? $(row).append($(label).text(fm.i18n('quality')), quality, $('<span/>')) : $()),
  29816. (isJpeg? $(row).append($(label).text(fm.i18n('8pxgrid')).addClass('elfinder-resize-grid8'), grid8px) : $()),
  29817. $(row).append($(label).text(fm.i18n('scale')), uiprop),
  29818. $(row).append(preset)
  29819. );
  29820. if (api2) {
  29821. uicrop.append(
  29822. $(row).append($(label).text('X'), pointX),
  29823. $(row).append($(label).text('Y')).append(pointY),
  29824. $(row).append($(label).text(fm.i18n('width')), offsetX),
  29825. $(row).append($(label).text(fm.i18n('height')), offsetY, $('<div class="elfinder-resize-whctrls">').append(constrc, reset.clone(true))),
  29826. (quality? $(row).append($(label).text(fm.i18n('quality')), quality.clone(true), $('<span/>')) : $()),
  29827. (isJpeg? $(row).append($(label).text(fm.i18n('8pxgrid')).addClass('elfinder-resize-grid8')) : $()),
  29828. $(row).append(presetc)
  29829. );
  29830. uirotate.append(
  29831. $(row).addClass('elfinder-resize-degree').append(
  29832. $(label).text(fm.i18n('rotate')),
  29833. degree,
  29834. $('<span/>').text(fm.i18n('degree')),
  29835. $('<div/>').append(uideg270, uideg90)[ctrgrup]()
  29836. ),
  29837. $(row).css('height', '20px').append(uidegslider),
  29838. ((quality)? $(row)[losslessRotate < 1? 'show' : 'hide']().addClass('elfinder-resize-quality').append(
  29839. $(label).text(fm.i18n('quality')),
  29840. quality.clone(true),
  29841. $('<span/>')) : $()
  29842. ),
  29843. $(row).append($(label).text(fm.i18n('bgcolor')), bg, picker, reseter),
  29844. $(row).css('height', '20px').append(pallet)
  29845. );
  29846. uideg270.on('click', function() {
  29847. rdegree = rdegree - 90;
  29848. rotate.update(rdegree);
  29849. });
  29850. uideg90.on('click', function(){
  29851. rdegree = rdegree + 90;
  29852. rotate.update(rdegree);
  29853. });
  29854. }
  29855. dialog.append(uitype).on('resize', function(e){
  29856. e.stopPropagation();
  29857. });
  29858. if (api2) {
  29859. control.append(/*$(row), */uiresize, uicrop.hide(), uirotate.hide());
  29860. } else {
  29861. control.append(/*$(row), */uiresize);
  29862. }
  29863. rhandle.append('<div class="'+hline+' '+hline+'-top"/>',
  29864. '<div class="'+hline+' '+hline+'-bottom"/>',
  29865. '<div class="'+vline+' '+vline+'-left"/>',
  29866. '<div class="'+vline+' '+vline+'-right"/>',
  29867. '<div class="'+rpoint+' '+rpoint+'-e"/>',
  29868. '<div class="'+rpoint+' '+rpoint+'-se"/>',
  29869. '<div class="'+rpoint+' '+rpoint+'-s"/>');
  29870. preview.append(spinner).append(rhandle.hide()).append(img.hide());
  29871. if (api2) {
  29872. rhandlec.css('position', 'absolute')
  29873. .append('<div class="'+hline+' '+hline+'-top"/>',
  29874. '<div class="'+hline+' '+hline+'-bottom"/>',
  29875. '<div class="'+vline+' '+vline+'-left"/>',
  29876. '<div class="'+vline+' '+vline+'-right"/>',
  29877. '<div class="'+rpoint+' '+rpoint+'-n"/>',
  29878. '<div class="'+rpoint+' '+rpoint+'-e"/>',
  29879. '<div class="'+rpoint+' '+rpoint+'-s"/>',
  29880. '<div class="'+rpoint+' '+rpoint+'-w"/>',
  29881. '<div class="'+rpoint+' '+rpoint+'-ne"/>',
  29882. '<div class="'+rpoint+' '+rpoint+'-se"/>',
  29883. '<div class="'+rpoint+' '+rpoint+'-sw"/>',
  29884. '<div class="'+rpoint+' '+rpoint+'-nw"/>');
  29885. preview.append(basec.css('position', 'absolute').hide().append(imgc, rhandlec.append(coverc)));
  29886. preview.append(imgr.hide());
  29887. }
  29888. preview.css('overflow', 'hidden');
  29889. dialog.append(preview, control);
  29890. buttons[fm.i18n('btnApply')] = save;
  29891. if (useSaveAs) {
  29892. buttons[fm.i18n('btnSaveAs')] = function() { requestAnimationFrame(saveAs); };
  29893. }
  29894. buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); };
  29895. dialog.find('input,button').addClass('elfinder-tabstop');
  29896. base = self.fmDialog(dialog, {
  29897. title : fm.escape(file.name),
  29898. width : dialogWidth,
  29899. resizable : false,
  29900. buttons : buttons,
  29901. open : function() {
  29902. var substituteImg = (fm.option('substituteImg', file.hash) && file.size > options.dimSubImgSize)? true : false,
  29903. hasSize = (file.width && file.height)? true : false;
  29904. dialog.parent().css('overflow', 'hidden');
  29905. dMinBtn = base.find('.ui-dialog-titlebar .elfinder-titlebar-minimize').hide();
  29906. fm.bind('resize', dinit);
  29907. img.attr('src', src);
  29908. imgc.attr('src', src);
  29909. imgr.attr('src', src);
  29910. if (api2) {
  29911. imgr.on('mousedown touchstart', rotate.start)
  29912. .on('touchend', rotate.stop);
  29913. base.on('mouseup', rotate.stop);
  29914. }
  29915. if (hasSize && !substituteImg) {
  29916. return init();
  29917. }
  29918. if (file.size > (options.getDimThreshold || 0)) {
  29919. dimreq = fm.request({
  29920. data : {cmd : 'dim', target : file.hash, substitute : (substituteImg? 400 : '')},
  29921. preventDefault : true
  29922. })
  29923. .done(function(data) {
  29924. if (data.dim) {
  29925. var dim = data.dim.split('x');
  29926. file.width = dim[0];
  29927. file.height = dim[1];
  29928. setdim(dim);
  29929. if (data.url) {
  29930. img.attr('src', data.url);
  29931. imgc.attr('src', data.url);
  29932. imgr.attr('src', data.url);
  29933. }
  29934. return init();
  29935. }
  29936. });
  29937. } else if (hasSize) {
  29938. return init();
  29939. }
  29940. },
  29941. close : function() {
  29942. if (api2) {
  29943. imgr.off('mousedown touchstart', rotate.start)
  29944. .off('touchend', rotate.stop);
  29945. $(document).off('mouseup', rotate.stop);
  29946. }
  29947. fm.unbind('resize', dinit);
  29948. $(this).elfinderdialog('destroy');
  29949. },
  29950. resize : function(e, data) {
  29951. if (data && data.minimize === 'off') {
  29952. dinit();
  29953. }
  29954. }
  29955. }).attr('id', id).closest('.ui-dialog').addClass(clsediting);
  29956. // for IE < 9 dialog mising at open second+ time.
  29957. if (fm.UA.ltIE8) {
  29958. $('.elfinder-dialog').css('filter', '');
  29959. }
  29960. coverc.css({ 'opacity': 0.2, 'background-color': '#fff', 'position': 'absolute'}),
  29961. rhandlec.css('cursor', 'move');
  29962. rhandlec.find('.elfinder-resize-handle-point').css({
  29963. 'background-color' : '#fff',
  29964. 'opacity': 0.5,
  29965. 'border-color':'#000'
  29966. });
  29967. if (! api2) {
  29968. uitype.find('.api2').remove();
  29969. }
  29970. control.find('input,select').prop('disabled', true);
  29971. control.find('input.elfinder-resize-quality')
  29972. .next('span').addClass('elfinder-resize-jpgsize').attr('title', fm.i18n('roughFileSize'));
  29973. },
  29974. id, dialog
  29975. ;
  29976. if (!files.length || files[0].mime.indexOf('image/') === -1) {
  29977. return dfrd.reject();
  29978. }
  29979. id = 'resize-'+fm.namespace+'-'+files[0].hash;
  29980. dialog = fmnode.find('#'+id);
  29981. if (dialog.length) {
  29982. dialog.elfinderdialog('toTop');
  29983. return dfrd.resolve();
  29984. }
  29985. open(files[0], id);
  29986. return dfrd;
  29987. };
  29988. };
  29989. (function ($) {
  29990. var findProperty = function (styleObject, styleArgs) {
  29991. var i = 0 ;
  29992. for( i in styleArgs) {
  29993. if (typeof styleObject[styleArgs[i]] != 'undefined')
  29994. return styleArgs[i];
  29995. }
  29996. styleObject[styleArgs[i]] = '';
  29997. return styleArgs[i];
  29998. };
  29999. $.cssHooks.rotate = {
  30000. get: function(elem, computed, extra) {
  30001. return $(elem).rotate();
  30002. },
  30003. set: function(elem, value) {
  30004. $(elem).rotate(value);
  30005. return value;
  30006. }
  30007. };
  30008. $.cssHooks.transform = {
  30009. get: function(elem, computed, extra) {
  30010. var name = findProperty( elem.style ,
  30011. ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] );
  30012. return elem.style[name];
  30013. },
  30014. set: function(elem, value) {
  30015. var name = findProperty( elem.style ,
  30016. ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] );
  30017. elem.style[name] = value;
  30018. return value;
  30019. }
  30020. };
  30021. $.fn.rotate = function(val) {
  30022. var r;
  30023. if (typeof val == 'undefined') {
  30024. if (!!window.opera) {
  30025. r = this.css('transform').match(/rotate\((.*?)\)/);
  30026. return ( r && r[1])?
  30027. Math.round(parseFloat(r[1]) * 180 / Math.PI) : 0;
  30028. } else {
  30029. r = this.css('transform').match(/rotate\((.*?)\)/);
  30030. return ( r && r[1])? parseInt(r[1]) : 0;
  30031. }
  30032. }
  30033. this.css('transform',
  30034. this.css('transform').replace(/none|rotate\(.*?\)/, '') + 'rotate(' + parseInt(val) + 'deg)');
  30035. return this;
  30036. };
  30037. $.fx.step.rotate = function(fx) {
  30038. if ( fx.state == 0 ) {
  30039. fx.start = $(fx.elem).rotate();
  30040. fx.now = fx.start;
  30041. }
  30042. $(fx.elem).rotate(fx.now);
  30043. };
  30044. if (typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined") { // IE & IE<9
  30045. var GetAbsoluteXY = function(element) {
  30046. var pnode = element;
  30047. var x = pnode.offsetLeft;
  30048. var y = pnode.offsetTop;
  30049. while ( pnode.offsetParent ) {
  30050. pnode = pnode.offsetParent;
  30051. if (pnode != document.body && pnode.currentStyle['position'] != 'static') {
  30052. break;
  30053. }
  30054. if (pnode != document.body && pnode != document.documentElement) {
  30055. x -= pnode.scrollLeft;
  30056. y -= pnode.scrollTop;
  30057. }
  30058. x += pnode.offsetLeft;
  30059. y += pnode.offsetTop;
  30060. }
  30061. return { x: x, y: y };
  30062. };
  30063. var StaticToAbsolute = function (element) {
  30064. if ( element.currentStyle['position'] != 'static') {
  30065. return ;
  30066. }
  30067. var xy = GetAbsoluteXY(element);
  30068. element.style.position = 'absolute' ;
  30069. element.style.left = xy.x + 'px';
  30070. element.style.top = xy.y + 'px';
  30071. };
  30072. var IETransform = function(element,transform){
  30073. var r;
  30074. var m11 = 1;
  30075. var m12 = 1;
  30076. var m21 = 1;
  30077. var m22 = 1;
  30078. if (typeof element.style['msTransform'] != 'undefined'){
  30079. return true;
  30080. }
  30081. StaticToAbsolute(element);
  30082. r = transform.match(/rotate\((.*?)\)/);
  30083. var rotate = ( r && r[1]) ? parseInt(r[1]) : 0;
  30084. rotate = rotate % 360;
  30085. if (rotate < 0) rotate = 360 + rotate;
  30086. var radian= rotate * Math.PI / 180;
  30087. var cosX =Math.cos(radian);
  30088. var sinY =Math.sin(radian);
  30089. m11 *= cosX;
  30090. m12 *= -sinY;
  30091. m21 *= sinY;
  30092. m22 *= cosX;
  30093. element.style.filter = (element.style.filter || '').replace(/progid:DXImageTransform\.Microsoft\.Matrix\([^)]*\)/, "" ) +
  30094. ("progid:DXImageTransform.Microsoft.Matrix(" +
  30095. "M11=" + m11 +
  30096. ",M12=" + m12 +
  30097. ",M21=" + m21 +
  30098. ",M22=" + m22 +
  30099. ",FilterType='bilinear',sizingMethod='auto expand')")
  30100. ;
  30101. var ow = parseInt(element.style.width || element.width || 0 );
  30102. var oh = parseInt(element.style.height || element.height || 0 );
  30103. radian = rotate * Math.PI / 180;
  30104. var absCosX =Math.abs(Math.cos(radian));
  30105. var absSinY =Math.abs(Math.sin(radian));
  30106. var dx = (ow - (ow * absCosX + oh * absSinY)) / 2;
  30107. var dy = (oh - (ow * absSinY + oh * absCosX)) / 2;
  30108. element.style.marginLeft = Math.floor(dx) + "px";
  30109. element.style.marginTop = Math.floor(dy) + "px";
  30110. return(true);
  30111. };
  30112. var transform_set = $.cssHooks.transform.set;
  30113. $.cssHooks.transform.set = function(elem, value) {
  30114. transform_set.apply(this, [elem, value] );
  30115. IETransform(elem,value);
  30116. return value;
  30117. };
  30118. }
  30119. })(jQuery);
  30120. /*
  30121. * File: /js/commands/restore.js
  30122. */
  30123. /**
  30124. * @class elFinder command "restore"
  30125. * Restore items from the trash
  30126. *
  30127. * @author Naoki Sawada
  30128. **/
  30129. (elFinder.prototype.commands.restore = function() {
  30130. "use strict";
  30131. var self = this,
  30132. fm = this.fm,
  30133. fakeCnt = 0,
  30134. getFilesRecursively = function(files) {
  30135. var dfd = $.Deferred(),
  30136. dirs = [],
  30137. results = [],
  30138. reqs = [],
  30139. phashes = [],
  30140. getFile;
  30141. dfd._xhrReject = function() {
  30142. $.each(reqs, function() {
  30143. this && this.reject && this.reject();
  30144. });
  30145. getFile && getFile._xhrReject();
  30146. };
  30147. $.each(files, function(i, f) {
  30148. f.mime === 'directory'? dirs.push(f) : results.push(f);
  30149. });
  30150. if (dirs.length) {
  30151. $.each(dirs, function(i, d) {
  30152. reqs.push(fm.request({
  30153. data : {cmd : 'open', target : d.hash},
  30154. preventDefault : true,
  30155. asNotOpen : true
  30156. }));
  30157. phashes[i] = d.hash;
  30158. });
  30159. $.when.apply($, reqs).fail(function() {
  30160. dfd.reject();
  30161. }).done(function() {
  30162. var items = [];
  30163. $.each(arguments, function(i, r) {
  30164. var files;
  30165. if (r.files) {
  30166. if (r.files.length) {
  30167. items = items.concat(r.files);
  30168. } else {
  30169. items.push({
  30170. hash: 'fakefile_' + (fakeCnt++),
  30171. phash: phashes[i],
  30172. mime: 'fakefile',
  30173. name: 'fakefile',
  30174. ts: 0
  30175. });
  30176. }
  30177. }
  30178. });
  30179. fm.cache(items);
  30180. getFile = getFilesRecursively(items).done(function(res) {
  30181. results = results.concat(res);
  30182. dfd.resolve(results);
  30183. });
  30184. });
  30185. } else {
  30186. dfd.resolve(results);
  30187. }
  30188. return dfd;
  30189. },
  30190. restore = function(dfrd, files, targets, ops) {
  30191. var rHashes = {},
  30192. others = [],
  30193. found = false,
  30194. dirs = [],
  30195. opts = ops || {},
  30196. id = +new Date(),
  30197. tm, getFile;
  30198. fm.lockfiles({files : targets});
  30199. dirs = $.map(files, function(f) {
  30200. return f.mime === 'directory'? f.hash : null;
  30201. });
  30202. dfrd.done(function() {
  30203. dirs && fm.exec('rm', dirs, {forceRm : true, quiet : true});
  30204. }).always(function() {
  30205. fm.unlockfiles({files : targets});
  30206. });
  30207. tm = setTimeout(function() {
  30208. fm.notify({type : 'search', id : id, cnt : 1, hideCnt : true, cancel : function() {
  30209. getFile && getFile._xhrReject();
  30210. dfrd.reject();
  30211. }});
  30212. }, fm.notifyDelay);
  30213. fakeCnt = 0;
  30214. getFile = getFilesRecursively(files).always(function() {
  30215. tm && clearTimeout(tm);
  30216. fm.notify({type : 'search', id: id, cnt : -1, hideCnt : true});
  30217. }).fail(function() {
  30218. dfrd.reject('errRestore', 'errFileNotFound');
  30219. }).done(function(res) {
  30220. var errFolderNotfound = ['errRestore', 'errFolderNotFound'],
  30221. dirTop = '';
  30222. if (res.length) {
  30223. $.each(res, function(i, f) {
  30224. var phash = f.phash,
  30225. pfile,
  30226. srcRoot, tPath;
  30227. while(phash) {
  30228. if (srcRoot = fm.trashes[phash]) {
  30229. if (! rHashes[srcRoot]) {
  30230. if (found) {
  30231. // Keep items of other trash
  30232. others.push(f.hash);
  30233. return null; // continue $.each
  30234. }
  30235. rHashes[srcRoot] = {};
  30236. found = true;
  30237. }
  30238. tPath = fm.path(f.hash).substr(fm.path(phash).length).replace(/\\/g, '/');
  30239. tPath = tPath.replace(/\/[^\/]+?$/, '');
  30240. if (tPath === '') {
  30241. tPath = '/';
  30242. }
  30243. if (!rHashes[srcRoot][tPath]) {
  30244. rHashes[srcRoot][tPath] = [];
  30245. }
  30246. if (f.mime === 'fakefile') {
  30247. fm.updateCache({removed:[f.hash]});
  30248. } else {
  30249. rHashes[srcRoot][tPath].push(f.hash);
  30250. }
  30251. if (!dirTop || dirTop.length > tPath.length) {
  30252. dirTop = tPath;
  30253. }
  30254. break;
  30255. }
  30256. // Go up one level for next check
  30257. pfile = fm.file(phash);
  30258. if (!pfile) {
  30259. phash = false;
  30260. // Detection method for search results
  30261. $.each(fm.trashes, function(ph) {
  30262. var file = fm.file(ph),
  30263. filePath = fm.path(ph);
  30264. if ((!file.volumeid || f.hash.indexOf(file.volumeid) === 0) && fm.path(f.hash).indexOf(filePath) === 0) {
  30265. phash = ph;
  30266. return false;
  30267. }
  30268. });
  30269. } else {
  30270. phash = pfile.phash;
  30271. }
  30272. }
  30273. });
  30274. if (found) {
  30275. $.each(rHashes, function(src, dsts) {
  30276. var dirs = Object.keys(dsts),
  30277. cnt = dirs.length;
  30278. fm.request({
  30279. data : {cmd : 'mkdir', target : src, dirs : dirs},
  30280. notify : {type : 'chkdir', cnt : cnt},
  30281. preventFail : true
  30282. }).fail(function(error) {
  30283. dfrd.reject(error);
  30284. fm.unlockfiles({files : targets});
  30285. }).done(function(data) {
  30286. var cmdPaste, hashes;
  30287. if (hashes = data.hashes) {
  30288. cmdPaste = fm.getCommand('paste');
  30289. if (cmdPaste) {
  30290. // wait until file cache made
  30291. fm.one('mkdirdone', function() {
  30292. var hasErr = false;
  30293. $.each(dsts, function(dir, files) {
  30294. if (hashes[dir]) {
  30295. if (files.length) {
  30296. if (fm.file(hashes[dir])) {
  30297. fm.clipboard(files, true);
  30298. fm.exec('paste', [ hashes[dir] ], {_cmd : 'restore', noToast : (opts.noToast || dir !== dirTop)})
  30299. .done(function(data) {
  30300. if (data && (data.error || data.warning)) {
  30301. hasErr = true;
  30302. }
  30303. })
  30304. .fail(function() {
  30305. hasErr = true;
  30306. })
  30307. .always(function() {
  30308. if (--cnt < 1) {
  30309. dfrd[hasErr? 'reject' : 'resolve']();
  30310. if (others.length) {
  30311. // Restore items of other trash
  30312. fm.exec('restore', others);
  30313. }
  30314. }
  30315. });
  30316. } else {
  30317. dfrd.reject(errFolderNotfound);
  30318. }
  30319. } else {
  30320. if (--cnt < 1) {
  30321. dfrd.resolve();
  30322. if (others.length) {
  30323. // Restore items of other trash
  30324. fm.exec('restore', others);
  30325. }
  30326. }
  30327. }
  30328. }
  30329. });
  30330. });
  30331. } else {
  30332. dfrd.reject(['errRestore', 'errCmdNoSupport', '(paste)']);
  30333. }
  30334. } else {
  30335. dfrd.reject(errFolderNotfound);
  30336. }
  30337. });
  30338. });
  30339. } else {
  30340. dfrd.reject(errFolderNotfound);
  30341. }
  30342. } else {
  30343. dfrd.reject('errFileNotFound');
  30344. dirs && fm.exec('rm', dirs, {forceRm : true, quiet : true});
  30345. }
  30346. });
  30347. };
  30348. // for to be able to overwrite
  30349. this.restore = restore;
  30350. this.linkedCmds = ['copy', 'paste', 'mkdir', 'rm'];
  30351. this.updateOnSelect = false;
  30352. this.init = function() {
  30353. // re-assign for extended command
  30354. self = this;
  30355. fm = this.fm;
  30356. };
  30357. this.getstate = function(sel, e) {
  30358. sel = sel || fm.selected();
  30359. return sel.length && $.grep(sel, function(h) {var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? true : false; }).length == sel.length
  30360. ? 0 : -1;
  30361. };
  30362. this.exec = function(hashes, opts) {
  30363. var dfrd = $.Deferred()
  30364. .fail(function(error) {
  30365. error && fm.error(error);
  30366. }),
  30367. files = self.files(hashes);
  30368. if (! files.length) {
  30369. return dfrd.reject();
  30370. }
  30371. $.each(files, function(i, file) {
  30372. if (fm.isRoot(file)) {
  30373. return !dfrd.reject(['errRestore', file.name]);
  30374. }
  30375. if (file.locked) {
  30376. return !dfrd.reject(['errLocked', file.name]);
  30377. }
  30378. });
  30379. if (dfrd.state() === 'pending') {
  30380. this.restore(dfrd, files, hashes, opts);
  30381. }
  30382. return dfrd;
  30383. };
  30384. }).prototype = { forceLoad : true }; // this is required command
  30385. /*
  30386. * File: /js/commands/rm.js
  30387. */
  30388. /**
  30389. * @class elFinder command "rm"
  30390. * Delete files
  30391. *
  30392. * @author Dmitry (dio) Levashov
  30393. * @author Naoki Sawada
  30394. **/
  30395. elFinder.prototype.commands.rm = function() {
  30396. "use strict";
  30397. var self = this,
  30398. fm = this.fm,
  30399. tpl = '<div class="ui-helper-clearfix elfinder-rm-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}<div class="elfinder-rm-desc">{desc}</div></div>',
  30400. confirm = function(dfrd, targets, files, tHash, addTexts) {
  30401. var cnt = targets.length,
  30402. cwd = fm.cwd().hash,
  30403. descs = [],
  30404. spinner = fm.i18n('calc') + '<span class="elfinder-spinner"/>',
  30405. dialog, text, tmb, size, f, fname;
  30406. if (cnt > 1) {
  30407. size = 0;
  30408. $.each(files, function(h, f) {
  30409. if (f.size && f.size != 'unknown' && f.mime !== 'directory') {
  30410. var s = parseInt(f.size);
  30411. if (s >= 0 && size >= 0) {
  30412. size += s;
  30413. }
  30414. } else {
  30415. size = 'unknown';
  30416. return false;
  30417. }
  30418. });
  30419. getSize = (size === 'unknown');
  30420. descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(size)));
  30421. text = [$(tpl.replace('{class}', 'elfinder-cwd-icon-group').replace('{title}', '<strong>' + fm.i18n('items')+ ': ' + cnt + '</strong>').replace('{desc}', descs.join('<br>')))];
  30422. } else {
  30423. f = files[0];
  30424. tmb = fm.tmb(f);
  30425. getSize = (f.mime === 'directory');
  30426. descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(f.size)));
  30427. descs.push(fm.i18n('modify')+': '+fm.formatDate(f));
  30428. fname = fm.escape(f.i18 || f.name).replace(/([_.])/g, '&#8203;$1');
  30429. text = [$(tpl.replace('{class}', fm.mime2class(f.mime)).replace('{title}', '<strong>' + fname + '</strong>').replace('{desc}', descs.join('<br>')))];
  30430. }
  30431. if (addTexts) {
  30432. text = text.concat(addTexts);
  30433. }
  30434. text.push(tHash? 'confirmTrash' : 'confirmRm');
  30435. dialog = fm.confirm({
  30436. title : self.title,
  30437. text : text,
  30438. accept : {
  30439. label : 'btnRm',
  30440. callback : function() {
  30441. if (tHash) {
  30442. self.toTrash(dfrd, targets, tHash);
  30443. } else {
  30444. remove(dfrd, targets);
  30445. }
  30446. }
  30447. },
  30448. cancel : {
  30449. label : 'btnCancel',
  30450. callback : function() {
  30451. fm.unlockfiles({files : targets});
  30452. if (targets.length === 1 && fm.file(targets[0]).phash !== cwd) {
  30453. fm.select({selected : targets});
  30454. } else {
  30455. fm.selectfiles({files : targets});
  30456. }
  30457. dfrd.reject();
  30458. }
  30459. }
  30460. });
  30461. // load thumbnail
  30462. if (tmb) {
  30463. $('<img/>')
  30464. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  30465. .attr('src', tmb.url);
  30466. }
  30467. if (getSize) {
  30468. getSize = fm.getSize($.map(files, function(f) { return f.mime === 'directory'? f.hash : null; })).done(function(data) {
  30469. dialog.find('span.elfinder-spinner').parent().html(fm.i18n('size')+': '+data.formated);
  30470. }).fail(function() {
  30471. dialog.find('span.elfinder-spinner').parent().html(fm.i18n('size')+': '+fm.i18n('unknown'));
  30472. }).always(function() {
  30473. getSize = false;
  30474. });
  30475. }
  30476. },
  30477. toTrash = function(dfrd, targets, tHash) {
  30478. var dsts = {},
  30479. itemCnt = targets.length,
  30480. maxCnt = self.options.toTrashMaxItems,
  30481. checkDirs = [],
  30482. reqDfd = $.Deferred(),
  30483. req, dirs, cnt;
  30484. if (itemCnt > maxCnt) {
  30485. self.confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]);
  30486. return;
  30487. }
  30488. // Directory preparation preparation and directory enumeration
  30489. $.each(targets, function(i, h) {
  30490. var file = fm.file(h),
  30491. path = fm.path(h).replace(/\\/g, '/'),
  30492. m = path.match(/^[^\/]+?(\/(?:[^\/]+?\/)*)[^\/]+?$/);
  30493. if (file) {
  30494. if (m) {
  30495. m[1] = m[1].replace(/(^\/.*?)\/?$/, '$1');
  30496. if (! dsts[m[1]]) {
  30497. dsts[m[1]] = [];
  30498. }
  30499. dsts[m[1]].push(h);
  30500. }
  30501. if (file.mime === 'directory') {
  30502. checkDirs.push(h);
  30503. }
  30504. }
  30505. });
  30506. // Check directory information
  30507. if (checkDirs.length) {
  30508. req = fm.request({
  30509. data : {cmd : 'size', targets : checkDirs},
  30510. notify : {type: 'readdir', cnt: 1, hideCnt: true},
  30511. preventDefault : true
  30512. }).done(function(data) {
  30513. var cnt = 0;
  30514. data.fileCnt && (cnt += parseInt(data.fileCnt));
  30515. data.dirCnt && (cnt += parseInt(data.dirCnt));
  30516. reqDfd[cnt > maxCnt ? 'reject' : 'resolve']();
  30517. }).fail(function() {
  30518. reqDfd.reject();
  30519. });
  30520. setTimeout(function() {
  30521. var xhr = (req && req.xhr)? req.xhr : null;
  30522. if (xhr && xhr.state() == 'pending') {
  30523. req.syncOnFail(false);
  30524. req.reject();
  30525. reqDfd.reject();
  30526. }
  30527. }, self.options.infoCheckWait * 1000);
  30528. } else {
  30529. reqDfd.resolve();
  30530. }
  30531. // Directory creation and paste command execution
  30532. reqDfd.done(function() {
  30533. dirs = Object.keys(dsts);
  30534. cnt = dirs.length;
  30535. if (cnt) {
  30536. fm.request({
  30537. data : {cmd : 'mkdir', target : tHash, dirs : dirs},
  30538. notify : {type : 'chkdir', cnt : cnt},
  30539. preventFail : true
  30540. })
  30541. .fail(function(error) {
  30542. dfrd.reject(error);
  30543. fm.unlockfiles({files : targets});
  30544. })
  30545. .done(function(data) {
  30546. var margeRes = function(data, phash, reqData) {
  30547. var undo, prevUndo, redo, prevRedo;
  30548. $.each(data, function(k, v) {
  30549. if (Array.isArray(v)) {
  30550. if (res[k]) {
  30551. res[k] = res[k].concat(v);
  30552. } else {
  30553. res[k] = v;
  30554. }
  30555. }
  30556. });
  30557. if (data.sync) {
  30558. res.sync = 1;
  30559. }
  30560. if (data.added && data.added.length) {
  30561. undo = function() {
  30562. var targets = [],
  30563. dirs = $.map(data.added, function(f) { return f.mime === 'directory'? f.hash : null; });
  30564. $.each(data.added, function(i, f) {
  30565. if ($.inArray(f.phash, dirs) === -1) {
  30566. targets.push(f.hash);
  30567. }
  30568. });
  30569. return fm.exec('restore', targets, {noToast: true});
  30570. };
  30571. redo = function() {
  30572. return fm.request({
  30573. data : reqData,
  30574. notify : {type : 'redo', cnt : targets.length}
  30575. });
  30576. };
  30577. if (res.undo) {
  30578. prevUndo = res.undo;
  30579. res.undo = function() {
  30580. undo();
  30581. prevUndo();
  30582. };
  30583. } else {
  30584. res.undo = undo;
  30585. }
  30586. if (res.redo) {
  30587. prevRedo = res.redo;
  30588. res.redo = function() {
  30589. redo();
  30590. prevRedo();
  30591. };
  30592. } else {
  30593. res.redo = redo;
  30594. }
  30595. }
  30596. },
  30597. err = ['errTrash'],
  30598. res = {},
  30599. hasNtf = function() {
  30600. return fm.ui.notify.children('.elfinder-notify-trash').length;
  30601. },
  30602. hashes, tm, prg, prgSt;
  30603. if (hashes = data.hashes) {
  30604. prg = 1 / cnt * 100;
  30605. prgSt = cnt === 1? 100 : 5;
  30606. tm = setTimeout(function() {
  30607. fm.notify({type : 'trash', cnt : 1, hideCnt : true, progress : prgSt});
  30608. }, fm.notifyDelay);
  30609. $.each(dsts, function(dir, files) {
  30610. var phash = fm.file(files[0]).phash,
  30611. reqData;
  30612. if (hashes[dir]) {
  30613. reqData = {cmd : 'paste', dst : hashes[dir], targets : files, cut : 1};
  30614. fm.request({
  30615. data : reqData,
  30616. preventDefault : true
  30617. })
  30618. .fail(function(error) {
  30619. if (error) {
  30620. err = err.concat(error);
  30621. }
  30622. })
  30623. .done(function(data) {
  30624. data = fm.normalize(data);
  30625. fm.updateCache(data);
  30626. margeRes(data, phash, reqData);
  30627. if (data.warning) {
  30628. err = err.concat(data.warning);
  30629. delete data.warning;
  30630. }
  30631. // fire some event to update cache/ui
  30632. data.removed && data.removed.length && fm.remove(data);
  30633. data.added && data.added.length && fm.add(data);
  30634. data.changed && data.changed.length && fm.change(data);
  30635. // fire event with command name
  30636. fm.trigger('paste', data);
  30637. // fire event with command name + 'done'
  30638. fm.trigger('pastedone');
  30639. // force update content
  30640. data.sync && fm.sync();
  30641. })
  30642. .always(function() {
  30643. var hashes = [], addTexts, end = 2;
  30644. if (hasNtf()) {
  30645. fm.notify({type : 'trash', cnt : 0, hideCnt : true, progress : prg});
  30646. } else {
  30647. prgSt+= prg;
  30648. }
  30649. if (--cnt < 1) {
  30650. tm && clearTimeout(tm);
  30651. hasNtf() && fm.notify({type : 'trash', cnt : -1});
  30652. fm.unlockfiles({files : targets});
  30653. if (Object.keys(res).length) {
  30654. if (err.length > 1) {
  30655. if (res.removed || res.removed.length) {
  30656. hashes = $.grep(targets, function(h) {
  30657. return $.inArray(h, res.removed) === -1? true : false;
  30658. });
  30659. }
  30660. if (hashes.length) {
  30661. if (err.length > end) {
  30662. end = (fm.messages[err[end-1]] || '').indexOf('$') === -1? end : end + 1;
  30663. }
  30664. dfrd.reject();
  30665. fm.exec('rm', hashes, { addTexts: err.slice(0, end), forceRm: true });
  30666. } else {
  30667. fm.error(err);
  30668. }
  30669. }
  30670. res._noSound = true;
  30671. if (res.undo && res.redo) {
  30672. res.undo = {
  30673. cmd : 'trash',
  30674. callback : res.undo,
  30675. };
  30676. res.redo = {
  30677. cmd : 'trash',
  30678. callback : res.redo
  30679. };
  30680. }
  30681. dfrd.resolve(res);
  30682. } else {
  30683. dfrd.reject(err);
  30684. }
  30685. }
  30686. });
  30687. }
  30688. });
  30689. } else {
  30690. dfrd.reject('errFolderNotFound');
  30691. fm.unlockfiles({files : targets});
  30692. }
  30693. });
  30694. } else {
  30695. dfrd.reject(['error', 'The folder hierarchy to be deleting can not be determined.']);
  30696. fm.unlockfiles({files : targets});
  30697. }
  30698. }).fail(function() {
  30699. self.confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]);
  30700. });
  30701. },
  30702. remove = function(dfrd, targets, quiet) {
  30703. var notify = quiet? {} : {type : 'rm', cnt : targets.length};
  30704. fm.request({
  30705. data : {cmd : 'rm', targets : targets},
  30706. notify : notify,
  30707. preventFail : true
  30708. })
  30709. .fail(function(error) {
  30710. dfrd.reject(error);
  30711. })
  30712. .done(function(data) {
  30713. if (data.error || data.warning) {
  30714. data.sync = true;
  30715. }
  30716. dfrd.resolve(data);
  30717. })
  30718. .always(function() {
  30719. fm.unlockfiles({files : targets});
  30720. });
  30721. },
  30722. getTHash = function(targets) {
  30723. var thash = null,
  30724. root1st;
  30725. if (targets && targets.length) {
  30726. if (targets.length > 1 && fm.searchStatus.state === 2) {
  30727. root1st = fm.file(fm.root(targets[0])).volumeid;
  30728. if (!$.grep(targets, function(h) { return h.indexOf(root1st) !== 0? true : false ; }).length) {
  30729. thash = fm.option('trashHash', targets[0]);
  30730. }
  30731. } else {
  30732. thash = fm.option('trashHash', targets[0]);
  30733. }
  30734. }
  30735. return thash;
  30736. },
  30737. getSize = false;
  30738. // for to be able to overwrite
  30739. this.confirm = confirm;
  30740. this.toTrash = toTrash;
  30741. this.remove = remove;
  30742. this.syncTitleOnChange = true;
  30743. this.updateOnSelect = false;
  30744. this.shortcuts = [{
  30745. pattern : 'delete ctrl+backspace shift+delete'
  30746. }];
  30747. this.value = 'rm';
  30748. this.init = function() {
  30749. // re-assign for extended command
  30750. self = this;
  30751. fm = this.fm;
  30752. // bind function of change
  30753. self.change(function() {
  30754. var targets;
  30755. delete self.extra;
  30756. self.title = fm.i18n('cmd' + self.value);
  30757. self.className = self.value;
  30758. self.button && self.button.children('span.elfinder-button-icon')[self.value === 'trash'? 'addClass' : 'removeClass']('elfinder-button-icon-trash');
  30759. if (self.value === 'trash') {
  30760. self.extra = {
  30761. icon: 'rm',
  30762. node: $('<span/>')
  30763. .attr({title: fm.i18n('cmdrm')})
  30764. .on('ready', function(e, data) {
  30765. targets = data.targets;
  30766. })
  30767. .on('click touchstart', function(e){
  30768. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  30769. return;
  30770. }
  30771. e.stopPropagation();
  30772. e.preventDefault();
  30773. fm.getUI().trigger('click'); // to close the context menu immediately
  30774. fm.exec('rm', targets, {_userAction: true, forceRm : true});
  30775. })
  30776. };
  30777. }
  30778. });
  30779. };
  30780. this.getstate = function(select) {
  30781. var sel = this.hashes(select);
  30782. return sel.length && $.grep(sel, function(h) { var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? true : false; }).length == sel.length
  30783. ? 0 : -1;
  30784. };
  30785. this.exec = function(hashes, cOpts) {
  30786. var opts = cOpts || {},
  30787. dfrd = $.Deferred()
  30788. .always(function() {
  30789. if (getSize && getSize.state && getSize.state() === 'pending') {
  30790. getSize.reject();
  30791. }
  30792. })
  30793. .fail(function(error) {
  30794. error && fm.error(error);
  30795. }).done(function(data) {
  30796. !opts.quiet && !data._noSound && data.removed && data.removed.length && fm.trigger('playsound', {soundFile : 'rm.wav'});
  30797. }),
  30798. files = self.files(hashes),
  30799. cnt = files.length,
  30800. tHash = null,
  30801. addTexts = opts.addTexts? opts.addTexts : null,
  30802. forceRm = opts.forceRm,
  30803. quiet = opts.quiet,
  30804. targets;
  30805. if (! cnt) {
  30806. return dfrd.reject();
  30807. }
  30808. $.each(files, function(i, file) {
  30809. if (fm.isRoot(file)) {
  30810. return !dfrd.reject(['errRm', file.name, 'errPerm']);
  30811. }
  30812. if (file.locked) {
  30813. return !dfrd.reject(['errLocked', file.name]);
  30814. }
  30815. });
  30816. if (dfrd.state() === 'pending') {
  30817. targets = self.hashes(hashes);
  30818. cnt = files.length;
  30819. if (forceRm || (self.event && self.event.originalEvent && self.event.originalEvent.shiftKey)) {
  30820. tHash = '';
  30821. self.title = fm.i18n('cmdrm');
  30822. }
  30823. if (tHash === null) {
  30824. tHash = getTHash(targets);
  30825. }
  30826. fm.lockfiles({files : targets});
  30827. if (tHash && self.options.quickTrash) {
  30828. self.toTrash(dfrd, targets, tHash);
  30829. } else {
  30830. if (quiet) {
  30831. remove(dfrd, targets, quiet);
  30832. } else {
  30833. self.confirm(dfrd, targets, files, tHash, addTexts);
  30834. }
  30835. }
  30836. }
  30837. return dfrd;
  30838. };
  30839. fm.bind('select contextmenucreate closecontextmenu', function(e) {
  30840. var targets = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected();
  30841. if (targets && targets.length) {
  30842. self.update(void(0), (targets? getTHash(targets) : fm.option('trashHash'))? 'trash' : 'rm');
  30843. }
  30844. });
  30845. };
  30846. /*
  30847. * File: /js/commands/search.js
  30848. */
  30849. /**
  30850. * @class elFinder command "search"
  30851. * Find files
  30852. *
  30853. * @author Dmitry (dio) Levashov
  30854. **/
  30855. elFinder.prototype.commands.search = function() {
  30856. "use strict";
  30857. this.title = 'Find files';
  30858. this.options = {ui : 'searchbutton'};
  30859. this.alwaysEnabled = true;
  30860. this.updateOnSelect = false;
  30861. /**
  30862. * Return command status.
  30863. * Search does not support old api.
  30864. *
  30865. * @return Number
  30866. **/
  30867. this.getstate = function() {
  30868. return 0;
  30869. };
  30870. /**
  30871. * Send search request to backend.
  30872. *
  30873. * @param String search string
  30874. * @return $.Deferred
  30875. **/
  30876. this.exec = function(q, target, mime, type) {
  30877. var fm = this.fm,
  30878. reqDef = [],
  30879. sType = type || '',
  30880. onlyMimes = fm.options.onlyMimes,
  30881. phash, targetVolids = [],
  30882. setType = function(data) {
  30883. if (sType && sType !== 'SearchName' && sType !== 'SearchMime') {
  30884. data.type = sType;
  30885. }
  30886. return data;
  30887. };
  30888. if (typeof q == 'string' && q) {
  30889. if (typeof target == 'object') {
  30890. mime = target.mime || '';
  30891. target = target.target || '';
  30892. }
  30893. target = target? target : '';
  30894. if (mime) {
  30895. mime = $.trim(mime).replace(',', ' ').split(' ');
  30896. if (onlyMimes.length) {
  30897. mime = $.map(mime, function(m){
  30898. m = $.trim(m);
  30899. return m && ($.inArray(m, onlyMimes) !== -1
  30900. || $.grep(onlyMimes, function(om) { return m.indexOf(om) === 0? true : false; }).length
  30901. )? m : null;
  30902. });
  30903. }
  30904. } else {
  30905. mime = [].concat(onlyMimes);
  30906. }
  30907. fm.trigger('searchstart', setType({query : q, target : target, mimes : mime}));
  30908. if (! onlyMimes.length || mime.length) {
  30909. if (target === '' && fm.api >= 2.1) {
  30910. $.each(fm.roots, function(id, hash) {
  30911. reqDef.push(fm.request({
  30912. data : setType({cmd : 'search', q : q, target : hash, mimes : mime}),
  30913. notify : {type : 'search', cnt : 1, hideCnt : (reqDef.length? false : true)},
  30914. cancel : true,
  30915. preventDone : true
  30916. }));
  30917. });
  30918. } else {
  30919. reqDef.push(fm.request({
  30920. data : setType({cmd : 'search', q : q, target : target, mimes : mime}),
  30921. notify : {type : 'search', cnt : 1, hideCnt : true},
  30922. cancel : true,
  30923. preventDone : true
  30924. }));
  30925. if (target !== '' && fm.api >= 2.1 && Object.keys(fm.leafRoots).length) {
  30926. $.each(fm.leafRoots, function(hash, roots) {
  30927. phash = hash;
  30928. while(phash) {
  30929. if (target === phash) {
  30930. $.each(roots, function() {
  30931. var f = fm.file(this);
  30932. f && f.volumeid && targetVolids.push(f.volumeid);
  30933. reqDef.push(fm.request({
  30934. data : setType({cmd : 'search', q : q, target : this, mimes : mime}),
  30935. notify : {type : 'search', cnt : 1, hideCnt : false},
  30936. cancel : true,
  30937. preventDone : true
  30938. }));
  30939. });
  30940. }
  30941. phash = (fm.file(phash) || {}).phash;
  30942. }
  30943. });
  30944. }
  30945. }
  30946. } else {
  30947. reqDef = [$.Deferred().resolve({files: []})];
  30948. }
  30949. fm.searchStatus.mixed = (reqDef.length > 1)? targetVolids : false;
  30950. return $.when.apply($, reqDef).done(function(data) {
  30951. var argLen = arguments.length,
  30952. i;
  30953. data.warning && fm.error(data.warning);
  30954. if (argLen > 1) {
  30955. data.files = (data.files || []);
  30956. for(i = 1; i < argLen; i++) {
  30957. arguments[i].warning && fm.error(arguments[i].warning);
  30958. if (arguments[i].files) {
  30959. data.files.push.apply(data.files, arguments[i].files);
  30960. }
  30961. }
  30962. }
  30963. // because "preventDone : true" so update files cache
  30964. data.files && data.files.length && fm.cache(data.files);
  30965. fm.lazy(function() {
  30966. fm.trigger('search', data);
  30967. }).then(function() {
  30968. // fire event with command name + 'done'
  30969. return fm.lazy(function() {
  30970. fm.trigger('searchdone');
  30971. });
  30972. }).then(function() {
  30973. // force update content
  30974. data.sync && fm.sync();
  30975. });
  30976. });
  30977. }
  30978. fm.getUI('toolbar').find('.'+fm.res('class', 'searchbtn')+' :text').trigger('focus');
  30979. return $.Deferred().reject();
  30980. };
  30981. };
  30982. /*
  30983. * File: /js/commands/selectall.js
  30984. */
  30985. /**
  30986. * @class elFinder command "selectall"
  30987. * Select ALL of cwd items
  30988. *
  30989. * @author Naoki Sawada
  30990. **/
  30991. elFinder.prototype.commands.selectall = function() {
  30992. "use strict";
  30993. var self = this,
  30994. state = 0;
  30995. this.fm.bind('select', function(e) {
  30996. state = (e.data && e.data.selectall)? -1 : 0;
  30997. });
  30998. this.state = 0;
  30999. this.updateOnSelect = false;
  31000. this.getstate = function() {
  31001. return state;
  31002. };
  31003. this.exec = function() {
  31004. $(document).trigger($.Event('keydown', { keyCode: 65, ctrlKey : true, shiftKey : false, altKey : false, metaKey : false }));
  31005. return $.Deferred().resolve();
  31006. };
  31007. };
  31008. /*
  31009. * File: /js/commands/selectinvert.js
  31010. */
  31011. /**
  31012. * @class elFinder command "selectinvert"
  31013. * Invert Selection of cwd items
  31014. *
  31015. * @author Naoki Sawada
  31016. **/
  31017. elFinder.prototype.commands.selectinvert = function() {
  31018. "use strict";
  31019. this.updateOnSelect = false;
  31020. this.getstate = function() {
  31021. return 0;
  31022. };
  31023. this.exec = function() {
  31024. $(document).trigger($.Event('keydown', { keyCode: 73, ctrlKey : true, shiftKey : true, altKey : false, metaKey : false }));
  31025. return $.Deferred().resolve();
  31026. };
  31027. };
  31028. /*
  31029. * File: /js/commands/selectnone.js
  31030. */
  31031. /**
  31032. * @class elFinder command "selectnone"
  31033. * Unselect ALL of cwd items
  31034. *
  31035. * @author Naoki Sawada
  31036. **/
  31037. elFinder.prototype.commands.selectnone = function() {
  31038. "use strict";
  31039. var self = this,
  31040. fm = this.fm,
  31041. state = -1;
  31042. fm.bind('select', function(e) {
  31043. state = (e.data && e.data.unselectall)? -1 : 0;
  31044. });
  31045. this.state = -1;
  31046. this.updateOnSelect = false;
  31047. this.getstate = function() {
  31048. return state;
  31049. };
  31050. this.exec = function() {
  31051. fm.getUI('cwd').trigger('unselectall');
  31052. return $.Deferred().resolve();
  31053. };
  31054. };
  31055. /*
  31056. * File: /js/commands/sort.js
  31057. */
  31058. /**
  31059. * @class elFinder command "sort"
  31060. * Change sort files rule
  31061. *
  31062. * @author Dmitry (dio) Levashov
  31063. **/
  31064. elFinder.prototype.commands.sort = function() {
  31065. "use strict";
  31066. var self = this,
  31067. fm = self.fm,
  31068. setVar = function() {
  31069. self.variants = [];
  31070. $.each(fm.sortRules, function(name, value) {
  31071. if (fm.sorters[name]) {
  31072. var arr = (name === fm.sortType)? (fm.sortOrder === 'asc'? 'n' : 's') : '';
  31073. self.variants.push([name, (arr? '<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>' : '') + '&nbsp;' + fm.i18n('sort'+name)]);
  31074. }
  31075. });
  31076. self.variants.push('|');
  31077. self.variants.push([
  31078. 'stick',
  31079. (fm.sortStickFolders? '<span class="ui-icon ui-icon-check"/>' : '') + '&nbsp;' + fm.i18n('sortFoldersFirst')
  31080. ]);
  31081. if (fm.ui.tree && fm.options.sortAlsoTreeview !== null) {
  31082. self.variants.push('|');
  31083. self.variants.push([
  31084. 'tree',
  31085. (fm.sortAlsoTreeview? '<span class="ui-icon ui-icon-check"/>' : '') + '&nbsp;' + fm.i18n('sortAlsoTreeview')
  31086. ]);
  31087. }
  31088. updateContextmenu();
  31089. },
  31090. updateContextmenu = function() {
  31091. var cm = fm.getUI('contextmenu'),
  31092. icon, sub;
  31093. if (cm.is(':visible')) {
  31094. icon = cm.find('span.elfinder-button-icon-sort');
  31095. sub = icon.siblings('div.elfinder-contextmenu-sub');
  31096. sub.find('span.ui-icon').remove();
  31097. sub.children('div.elfinder-contextsubmenu-item').each(function() {
  31098. var tgt = $(this).children('span'),
  31099. name = tgt.text().trim(),
  31100. arr;
  31101. if (name === (i18Name.stick || (i18Name.stick = fm.i18n('sortFoldersFirst')))) {
  31102. if (fm.sortStickFolders) {
  31103. tgt.prepend('<span class="ui-icon ui-icon-check"/>');
  31104. }
  31105. } else if (name === (i18Name.tree || (i18Name.tree = fm.i18n('sortAlsoTreeview')))) {
  31106. if (fm.sortAlsoTreeview) {
  31107. tgt.prepend('<span class="ui-icon ui-icon-check"/>');
  31108. }
  31109. } else if (name === (i18Name[fm.sortType] || (i18Name[fm.sortType] = fm.i18n('sort' + fm.sortType)))) {
  31110. arr = fm.sortOrder === 'asc'? 'n' : 's';
  31111. tgt.prepend('<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>');
  31112. }
  31113. });
  31114. }
  31115. },
  31116. i18Name = {};
  31117. /**
  31118. * Command options
  31119. *
  31120. * @type Object
  31121. */
  31122. this.options = {ui : 'sortbutton'};
  31123. this.keepContextmenu = true;
  31124. fm.bind('sortchange', setVar)
  31125. .bind('sorterupdate', function() {
  31126. setVar();
  31127. fm.getUI().children('.elfinder-button-sort-menu').children('.elfinder-button-menu-item').each(function() {
  31128. var tgt = $(this),
  31129. rel = tgt.attr('rel');
  31130. tgt.toggle(!!(! rel || fm.sorters[rel]));
  31131. });
  31132. })
  31133. .bind('cwdrender', function() {
  31134. var cols = $(fm.cwd).find('div.elfinder-cwd-wrapper-list table');
  31135. if (cols.length) {
  31136. $.each(fm.sortRules, function(name, value) {
  31137. var td = cols.find('thead tr td.elfinder-cwd-view-th-'+name);
  31138. if (td.length) {
  31139. var current = ( name == fm.sortType),
  31140. sort = {
  31141. type : name,
  31142. order : current ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder
  31143. },arr;
  31144. if (current) {
  31145. td.addClass('ui-state-active');
  31146. arr = fm.sortOrder == 'asc' ? 'n' : 's';
  31147. $('<span class="ui-icon ui-icon-triangle-1-'+arr+'"/>').appendTo(td);
  31148. }
  31149. $(td).on('click', function(e){
  31150. if (! $(this).data('dragging')) {
  31151. e.stopPropagation();
  31152. if (! fm.getUI('cwd').data('longtap')) {
  31153. fm.exec('sort', [], sort);
  31154. }
  31155. }
  31156. })
  31157. .on('mouseenter mouseleave', function(e) {
  31158. $(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
  31159. });
  31160. }
  31161. });
  31162. }
  31163. });
  31164. this.getstate = function() {
  31165. return 0;
  31166. };
  31167. this.exec = function(hashes, cOpt) {
  31168. var fm = this.fm,
  31169. sortopt = $.isPlainObject(cOpt)? cOpt : (function() {
  31170. cOpt += '';
  31171. var sOpts = {};
  31172. if (cOpt === 'stick') {
  31173. sOpts.stick = !fm.sortStickFolders;
  31174. } else if (cOpt === 'tree') {
  31175. sOpts.tree = !fm.sortAlsoTreeview;
  31176. } else if (fm.sorters[cOpt]) {
  31177. if (fm.sortType === cOpt) {
  31178. sOpts.order = fm.sortOrder === 'asc'? 'desc' : 'asc';
  31179. } else {
  31180. sOpts.type = cOpt;
  31181. }
  31182. }
  31183. return sOpts;
  31184. })(),
  31185. sort = Object.assign({
  31186. type : fm.sortType,
  31187. order : fm.sortOrder,
  31188. stick : fm.sortStickFolders,
  31189. tree : fm.sortAlsoTreeview
  31190. }, sortopt);
  31191. return fm.lazy(function() {
  31192. fm.setSort(sort.type, sort.order, sort.stick, sort.tree);
  31193. this.resolve();
  31194. });
  31195. };
  31196. };
  31197. /*
  31198. * File: /js/commands/undo.js
  31199. */
  31200. /**
  31201. * @class elFinder command "undo"
  31202. * Undo previous commands
  31203. *
  31204. * @author Naoki Sawada
  31205. **/
  31206. elFinder.prototype.commands.undo = function() {
  31207. "use strict";
  31208. var self = this,
  31209. fm = this.fm,
  31210. setTitle = function(undo) {
  31211. if (undo) {
  31212. self.title = fm.i18n('cmdundo') + ' ' + fm.i18n('cmd'+undo.cmd);
  31213. self.state = 0;
  31214. } else {
  31215. self.title = fm.i18n('cmdundo');
  31216. self.state = -1;
  31217. }
  31218. self.change();
  31219. },
  31220. cmds = [];
  31221. this.alwaysEnabled = true;
  31222. this.updateOnSelect = false;
  31223. this.shortcuts = [{
  31224. pattern : 'ctrl+z'
  31225. }];
  31226. this.syncTitleOnChange = true;
  31227. this.getstate = function() {
  31228. return cmds.length? 0 : -1;
  31229. };
  31230. this.setUndo = function(undo, redo) {
  31231. var _undo = {};
  31232. if (undo) {
  31233. if ($.isPlainObject(undo) && undo.cmd && undo.callback) {
  31234. Object.assign(_undo, undo);
  31235. if (redo) {
  31236. delete redo.undo;
  31237. _undo.redo = redo;
  31238. } else {
  31239. fm.getCommand('redo').setRedo(null);
  31240. }
  31241. cmds.push(_undo);
  31242. setTitle(_undo);
  31243. }
  31244. }
  31245. };
  31246. this.exec = function() {
  31247. var redo = fm.getCommand('redo'),
  31248. dfd = $.Deferred(),
  31249. undo, res, _redo = {};
  31250. if (cmds.length) {
  31251. undo = cmds.pop();
  31252. if (undo.redo) {
  31253. Object.assign(_redo, undo.redo);
  31254. delete undo.redo;
  31255. } else {
  31256. _redo = null;
  31257. }
  31258. dfd.done(function() {
  31259. if (_redo) {
  31260. redo.setRedo(_redo, undo);
  31261. }
  31262. });
  31263. setTitle(cmds.length? cmds[cmds.length-1] : void(0));
  31264. res = undo.callback();
  31265. if (res && res.done) {
  31266. res.done(function() {
  31267. dfd.resolve();
  31268. }).fail(function() {
  31269. dfd.reject();
  31270. });
  31271. } else {
  31272. dfd.resolve();
  31273. }
  31274. if (cmds.length) {
  31275. this.update(0, cmds[cmds.length - 1].name);
  31276. } else {
  31277. this.update(-1, '');
  31278. }
  31279. } else {
  31280. dfd.reject();
  31281. }
  31282. return dfd;
  31283. };
  31284. fm.bind('exec', function(e) {
  31285. var data = e.data || {};
  31286. if (data.opts && data.opts._userAction) {
  31287. if (data.dfrd && data.dfrd.done) {
  31288. data.dfrd.done(function(res) {
  31289. if (res && res.undo && res.redo) {
  31290. res.undo.redo = res.redo;
  31291. self.setUndo(res.undo);
  31292. }
  31293. });
  31294. }
  31295. }
  31296. });
  31297. };
  31298. /**
  31299. * @class elFinder command "redo"
  31300. * Redo previous commands
  31301. *
  31302. * @author Naoki Sawada
  31303. **/
  31304. elFinder.prototype.commands.redo = function() {
  31305. "use strict";
  31306. var self = this,
  31307. fm = this.fm,
  31308. setTitle = function(redo) {
  31309. if (redo && redo.callback) {
  31310. self.title = fm.i18n('cmdredo') + ' ' + fm.i18n('cmd'+redo.cmd);
  31311. self.state = 0;
  31312. } else {
  31313. self.title = fm.i18n('cmdredo');
  31314. self.state = -1;
  31315. }
  31316. self.change();
  31317. },
  31318. cmds = [];
  31319. this.alwaysEnabled = true;
  31320. this.updateOnSelect = false;
  31321. this.shortcuts = [{
  31322. pattern : 'shift+ctrl+z ctrl+y'
  31323. }];
  31324. this.syncTitleOnChange = true;
  31325. this.getstate = function() {
  31326. return cmds.length? 0 : -1;
  31327. };
  31328. this.setRedo = function(redo, undo) {
  31329. if (redo === null) {
  31330. cmds = [];
  31331. setTitle();
  31332. } else {
  31333. if (redo && redo.cmd && redo.callback) {
  31334. if (undo) {
  31335. redo.undo = undo;
  31336. }
  31337. cmds.push(redo);
  31338. setTitle(redo);
  31339. }
  31340. }
  31341. };
  31342. this.exec = function() {
  31343. var undo = fm.getCommand('undo'),
  31344. dfd = $.Deferred(),
  31345. redo, res, _undo = {}, _redo = {};
  31346. if (cmds.length) {
  31347. redo = cmds.pop();
  31348. if (redo.undo) {
  31349. Object.assign(_undo, redo.undo);
  31350. Object.assign(_redo, redo);
  31351. delete _redo.undo;
  31352. dfd.done(function() {
  31353. undo.setUndo(_undo, _redo);
  31354. });
  31355. }
  31356. setTitle(cmds.length? cmds[cmds.length-1] : void(0));
  31357. res = redo.callback();
  31358. if (res && res.done) {
  31359. res.done(function() {
  31360. dfd.resolve();
  31361. }).fail(function() {
  31362. dfd.reject();
  31363. });
  31364. } else {
  31365. dfd.resolve();
  31366. }
  31367. return dfd;
  31368. } else {
  31369. return dfd.reject();
  31370. }
  31371. };
  31372. };
  31373. /*
  31374. * File: /js/commands/up.js
  31375. */
  31376. /**
  31377. * @class elFinder command "up"
  31378. * Go into parent directory
  31379. *
  31380. * @author Dmitry (dio) Levashov
  31381. **/
  31382. (elFinder.prototype.commands.up = function() {
  31383. "use strict";
  31384. this.alwaysEnabled = true;
  31385. this.updateOnSelect = false;
  31386. this.shortcuts = [{
  31387. pattern : 'ctrl+up'
  31388. }];
  31389. this.getstate = function() {
  31390. return this.fm.cwd().phash ? 0 : -1;
  31391. };
  31392. this.exec = function() {
  31393. var fm = this.fm,
  31394. cwdhash = fm.cwd().hash;
  31395. return this.fm.cwd().phash ? this.fm.exec('open', this.fm.cwd().phash).done(function() {
  31396. fm.one('opendone', function() {
  31397. fm.selectfiles({files : [cwdhash]});
  31398. });
  31399. }) : $.Deferred().reject();
  31400. };
  31401. }).prototype = { forceLoad : true }; // this is required command
  31402. /*
  31403. * File: /js/commands/upload.js
  31404. */
  31405. /**
  31406. * @class elFinder command "upload"
  31407. * Upload files using iframe or XMLHttpRequest & FormData.
  31408. * Dialog allow to send files using drag and drop
  31409. *
  31410. * @type elFinder.command
  31411. * @author Dmitry (dio) Levashov
  31412. */
  31413. elFinder.prototype.commands.upload = function() {
  31414. "use strict";
  31415. var hover = this.fm.res('class', 'hover');
  31416. this.disableOnSearch = true;
  31417. this.updateOnSelect = false;
  31418. // Shortcut opens dialog
  31419. this.shortcuts = [{
  31420. pattern : 'ctrl+u'
  31421. }];
  31422. /**
  31423. * Return command state
  31424. *
  31425. * @return Number
  31426. **/
  31427. this.getstate = function(select) {
  31428. var fm = this.fm, f,
  31429. sel = (select || [fm.cwd().hash]);
  31430. if (!this._disabled && sel.length == 1) {
  31431. f = fm.file(sel[0]);
  31432. }
  31433. return (f && f.mime == 'directory' && f.write)? 0 : -1;
  31434. };
  31435. this.exec = function(data) {
  31436. var fm = this.fm,
  31437. cwdHash = fm.cwd().hash,
  31438. getTargets = function() {
  31439. var tgts = data && (data instanceof Array)? data : null,
  31440. sel;
  31441. if (! data || data instanceof Array) {
  31442. if (! tgts && (sel = fm.selected()).length === 1 && fm.file(sel[0]).mime === 'directory') {
  31443. tgts = sel;
  31444. } else if (!tgts || tgts.length !== 1 || fm.file(tgts[0]).mime !== 'directory') {
  31445. tgts = [ cwdHash ];
  31446. }
  31447. }
  31448. return tgts;
  31449. },
  31450. targets = getTargets(),
  31451. check = targets? targets[0] : (data && data.target? data.target : null),
  31452. targetDir = check? fm.file(check) : fm.cwd(),
  31453. fmUpload = function(data) {
  31454. fm.upload(data)
  31455. .fail(function(error) {
  31456. dfrd.reject(error);
  31457. })
  31458. .done(function(data) {
  31459. var cwd = fm.getUI('cwd'),
  31460. node;
  31461. dfrd.resolve(data);
  31462. if (data && data.added && data.added[0] && ! fm.ui.notify.children('.elfinder-notify-upload').length) {
  31463. var newItem = fm.findCwdNodes(data.added);
  31464. if (newItem.length) {
  31465. newItem.trigger('scrolltoview');
  31466. } else {
  31467. if (targetDir.hash !== cwdHash) {
  31468. node = $('<div/>').append(
  31469. $('<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>')
  31470. .on('mouseenter mouseleave', function(e) {
  31471. $(this).toggleClass('ui-state-hover', e.type == 'mouseenter');
  31472. }).on('click', function() {
  31473. fm.exec('open', check).done(function() {
  31474. fm.one('opendone', function() {
  31475. fm.trigger('selectfiles', {files : $.map(data.added, function(f) {return f.hash;})});
  31476. });
  31477. });
  31478. })
  31479. );
  31480. } else {
  31481. fm.trigger('selectfiles', {files : $.map(data.added, function(f) {return f.hash;})});
  31482. }
  31483. fm.toast({msg: fm.i18n(['complete', fm.i18n('cmdupload')]), extNode: node});
  31484. }
  31485. }
  31486. })
  31487. .progress(function() {
  31488. dfrd.notifyWith(this, Array.from(arguments));
  31489. });
  31490. },
  31491. upload = function(data) {
  31492. dialog.elfinderdialog('close');
  31493. if (targets) {
  31494. data.target = targets[0];
  31495. }
  31496. fmUpload(data);
  31497. },
  31498. getSelector = function() {
  31499. var hash = targetDir.hash,
  31500. dirs = $.map(fm.files(hash), function(f) {
  31501. return (f.mime === 'directory' && f.write)? f : null;
  31502. });
  31503. if (! dirs.length) {
  31504. return $();
  31505. }
  31506. return $('<div class="elfinder-upload-dirselect elfinder-tabstop" title="' + fm.i18n('folders') + '"/>')
  31507. .on('click', function(e) {
  31508. e.stopPropagation();
  31509. e.preventDefault();
  31510. dirs = fm.sortFiles(dirs);
  31511. var $this = $(this),
  31512. cwd = fm.cwd(),
  31513. base = dialog.closest('div.ui-dialog'),
  31514. getRaw = function(f, icon) {
  31515. return {
  31516. label : fm.escape(f.i18 || f.name),
  31517. icon : icon,
  31518. remain : false,
  31519. callback : function() {
  31520. var title = base.children('.ui-dialog-titlebar:first').find('span.elfinder-upload-target');
  31521. targets = [ f.hash ];
  31522. title.html(' - ' + fm.escape(f.i18 || f.name));
  31523. $this.trigger('focus');
  31524. },
  31525. options : {
  31526. className : (targets && targets.length && f.hash === targets[0])? 'ui-state-active' : '',
  31527. iconClass : f.csscls || '',
  31528. iconImg : f.icon || ''
  31529. }
  31530. };
  31531. },
  31532. raw = [ getRaw(targetDir, 'opendir'), '|' ];
  31533. $.each(dirs, function(i, f) {
  31534. raw.push(getRaw(f, 'dir'));
  31535. });
  31536. $this.trigger('blur');
  31537. fm.trigger('contextmenu', {
  31538. raw: raw,
  31539. x: e.pageX || $(this).offset().left,
  31540. y: e.pageY || $(this).offset().top,
  31541. prevNode: base,
  31542. fitHeight: true
  31543. });
  31544. }).append('<span class="elfinder-button-icon elfinder-button-icon-dir" />');
  31545. },
  31546. inputButton = function(type, caption) {
  31547. var button,
  31548. input = $('<input type="file" ' + type + '/>')
  31549. .on('click', function() {
  31550. // for IE's bug
  31551. if (fm.UA.IE) {
  31552. setTimeout(function() {
  31553. form.css('display', 'none').css('position', 'relative');
  31554. requestAnimationFrame(function() {
  31555. form.css('display', '').css('position', '');
  31556. });
  31557. }, 100);
  31558. }
  31559. })
  31560. .on('change', function() {
  31561. upload({input : input.get(0), type : 'files'});
  31562. })
  31563. .on('dragover', function(e) {
  31564. e.originalEvent.dataTransfer.dropEffect = 'copy';
  31565. }),
  31566. form = $('<form/>').append(input).on('click', function(e) {
  31567. e.stopPropagation();
  31568. });
  31569. 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>')
  31570. .append(form)
  31571. .on('click', function(e) {
  31572. e.stopPropagation();
  31573. e.preventDefault();
  31574. input.trigger('click');
  31575. })
  31576. .on('mouseenter mouseleave', function(e) {
  31577. $(this).toggleClass(hover, e.type === 'mouseenter');
  31578. });
  31579. },
  31580. dfrd = $.Deferred(),
  31581. dialog, dropbox, pastebox, dropUpload, paste, dirs, spinner, uidialog;
  31582. dropUpload = function(e) {
  31583. e.stopPropagation();
  31584. e.preventDefault();
  31585. var file = false,
  31586. type = '',
  31587. elfFrom = null,
  31588. mycwd = '',
  31589. data = null,
  31590. target = e._target || null,
  31591. trf = e.dataTransfer || null,
  31592. kind = (trf.items && trf.items.length && trf.items[0].kind)? trf.items[0].kind : '',
  31593. errors;
  31594. if (trf) {
  31595. try {
  31596. elfFrom = trf.getData('elfinderfrom');
  31597. if (elfFrom) {
  31598. mycwd = window.location.href + fm.cwd().hash;
  31599. if ((!target && elfFrom === mycwd) || target === mycwd) {
  31600. dfrd.reject();
  31601. return;
  31602. }
  31603. }
  31604. } catch(e) {}
  31605. if (kind === 'file' && (trf.items[0].getAsEntry || trf.items[0].webkitGetAsEntry)) {
  31606. file = trf;
  31607. type = 'data';
  31608. } else if (kind !== 'string' && trf.files && trf.files.length && $.inArray('Text', trf.types) === -1) {
  31609. file = trf.files;
  31610. type = 'files';
  31611. } else {
  31612. try {
  31613. if ((data = trf.getData('text/html')) && data.match(/<(?:img|a)/i)) {
  31614. file = [ data ];
  31615. type = 'html';
  31616. }
  31617. } catch(e) {}
  31618. if (! file) {
  31619. if (data = trf.getData('text')) {
  31620. file = [ data ];
  31621. type = 'text';
  31622. } else if (trf && trf.files) {
  31623. // maybe folder uploading but this UA dose not support it
  31624. kind = 'file';
  31625. }
  31626. }
  31627. }
  31628. }
  31629. if (file) {
  31630. fmUpload({files : file, type : type, target : target, dropEvt : e});
  31631. } else {
  31632. errors = ['errUploadNoFiles'];
  31633. if (kind === 'file') {
  31634. errors.push('errFolderUpload');
  31635. }
  31636. fm.error(errors);
  31637. dfrd.reject();
  31638. }
  31639. };
  31640. if (!targets && data) {
  31641. if (data.input || data.files) {
  31642. data.type = 'files';
  31643. fmUpload(data);
  31644. } else if (data.dropEvt) {
  31645. dropUpload(data.dropEvt);
  31646. }
  31647. return dfrd;
  31648. }
  31649. paste = function(ev) {
  31650. var e = ev.originalEvent || ev;
  31651. var files = [], items = [];
  31652. var file;
  31653. if (e.clipboardData) {
  31654. if (e.clipboardData.items && e.clipboardData.items.length){
  31655. items = e.clipboardData.items;
  31656. for (var i=0; i < items.length; i++) {
  31657. if (e.clipboardData.items[i].kind == 'file') {
  31658. file = e.clipboardData.items[i].getAsFile();
  31659. files.push(file);
  31660. }
  31661. }
  31662. } else if (e.clipboardData.files && e.clipboardData.files.length) {
  31663. files = e.clipboardData.files;
  31664. }
  31665. if (files.length) {
  31666. upload({files : files, type : 'files', clipdata : true});
  31667. return;
  31668. }
  31669. }
  31670. var my = e.target || e.srcElement;
  31671. requestAnimationFrame(function() {
  31672. var type = 'text',
  31673. src;
  31674. if (my.innerHTML) {
  31675. $(my).find('img').each(function(i, v){
  31676. if (v.src.match(/^webkit-fake-url:\/\//)) {
  31677. // For Safari's bug.
  31678. // ref. https://bugs.webkit.org/show_bug.cgi?id=49141
  31679. // https://dev.ckeditor.com/ticket/13029
  31680. $(v).remove();
  31681. }
  31682. });
  31683. if ($(my).find('a,img').length) {
  31684. type = 'html';
  31685. }
  31686. src = my.innerHTML;
  31687. my.innerHTML = '';
  31688. upload({files : [ src ], type : type});
  31689. }
  31690. });
  31691. };
  31692. dialog = $('<div class="elfinder-upload-dialog-wrapper"/>')
  31693. .append(inputButton('multiple', 'selectForUpload'));
  31694. if (! fm.UA.Mobile && (function(input) {
  31695. return (typeof input.webkitdirectory !== 'undefined' || typeof input.directory !== 'undefined');})(document.createElement('input'))) {
  31696. dialog.append(inputButton('multiple webkitdirectory directory', 'selectFolder'));
  31697. }
  31698. if (targetDir.dirs) {
  31699. if (targetDir.hash === cwdHash || fm.navHash2Elm(targetDir.hash).hasClass('elfinder-subtree-loaded')) {
  31700. getSelector().appendTo(dialog);
  31701. } else {
  31702. spinner = $('<div class="elfinder-upload-dirselect" title="' + fm.i18n('nowLoading') + '"/>')
  31703. .append('<span class="elfinder-button-icon elfinder-button-icon-spinner" />')
  31704. .appendTo(dialog);
  31705. fm.request({cmd : 'tree', target : targetDir.hash})
  31706. .done(function() {
  31707. fm.one('treedone', function() {
  31708. spinner.replaceWith(getSelector());
  31709. uidialog.elfinderdialog('tabstopsInit');
  31710. });
  31711. })
  31712. .fail(function() {
  31713. spinner.remove();
  31714. });
  31715. }
  31716. }
  31717. if (fm.dragUpload) {
  31718. dropbox = $('<div class="ui-corner-all elfinder-upload-dropbox elfinder-tabstop" contenteditable="true" data-ph="'+fm.i18n('dropPasteFiles')+'"></div>')
  31719. .on('paste', function(e){
  31720. paste(e);
  31721. })
  31722. .on('mousedown click', function(){
  31723. $(this).trigger('focus');
  31724. })
  31725. .on('focus', function(){
  31726. this.innerHTML = '';
  31727. })
  31728. .on('mouseover', function(){
  31729. $(this).addClass(hover);
  31730. })
  31731. .on('mouseout', function(){
  31732. $(this).removeClass(hover);
  31733. })
  31734. .on('dragenter', function(e) {
  31735. e.stopPropagation();
  31736. e.preventDefault();
  31737. $(this).addClass(hover);
  31738. })
  31739. .on('dragleave', function(e) {
  31740. e.stopPropagation();
  31741. e.preventDefault();
  31742. $(this).removeClass(hover);
  31743. })
  31744. .on('dragover', function(e) {
  31745. e.stopPropagation();
  31746. e.preventDefault();
  31747. e.originalEvent.dataTransfer.dropEffect = 'copy';
  31748. $(this).addClass(hover);
  31749. })
  31750. .on('drop', function(e) {
  31751. dialog.elfinderdialog('close');
  31752. targets && (e.originalEvent._target = targets[0]);
  31753. dropUpload(e.originalEvent);
  31754. })
  31755. .prependTo(dialog)
  31756. .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0];
  31757. } else {
  31758. pastebox = $('<div class="ui-corner-all elfinder-upload-dropbox" contenteditable="true">'+fm.i18n('dropFilesBrowser')+'</div>')
  31759. .on('paste drop', function(e){
  31760. paste(e);
  31761. })
  31762. .on('mousedown click', function(){
  31763. $(this).trigger('focus');
  31764. })
  31765. .on('focus', function(){
  31766. this.innerHTML = '';
  31767. })
  31768. .on('dragenter mouseover', function(){
  31769. $(this).addClass(hover);
  31770. })
  31771. .on('dragleave mouseout', function(){
  31772. $(this).removeClass(hover);
  31773. })
  31774. .prependTo(dialog)
  31775. .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0];
  31776. }
  31777. uidialog = this.fmDialog(dialog, {
  31778. title : this.title + '<span class="elfinder-upload-target">' + (targetDir? ' - ' + fm.escape(targetDir.i18 || targetDir.name) : '') + '</span>',
  31779. modal : true,
  31780. resizable : false,
  31781. destroyOnClose : true,
  31782. propagationEvents : ['mousemove', 'mouseup', 'click'],
  31783. close : function() {
  31784. var cm = fm.getUI('contextmenu');
  31785. if (cm.is(':visible')) {
  31786. cm.click();
  31787. }
  31788. }
  31789. });
  31790. return dfrd;
  31791. };
  31792. };
  31793. /*
  31794. * File: /js/commands/view.js
  31795. */
  31796. /**
  31797. * @class elFinder command "view"
  31798. * Change current directory view (icons/list)
  31799. *
  31800. * @author Dmitry (dio) Levashov
  31801. **/
  31802. elFinder.prototype.commands.view = function() {
  31803. "use strict";
  31804. var self = this,
  31805. fm = this.fm,
  31806. subMenuRaw;
  31807. this.value = fm.viewType;
  31808. this.alwaysEnabled = true;
  31809. this.updateOnSelect = false;
  31810. this.options = { ui : 'viewbutton'};
  31811. this.getstate = function() {
  31812. return 0;
  31813. };
  31814. this.extra = {
  31815. icon: 'menu',
  31816. node: $('<span/>')
  31817. .attr({title: fm.i18n('viewtype')})
  31818. .on('click touchstart', function(e){
  31819. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  31820. return;
  31821. }
  31822. var node = $(this);
  31823. e.stopPropagation();
  31824. e.preventDefault();
  31825. fm.trigger('contextmenu', {
  31826. raw: getSubMenuRaw(),
  31827. x: node.offset().left,
  31828. y: node.offset().top
  31829. });
  31830. })
  31831. };
  31832. this.exec = function() {
  31833. var self = this,
  31834. value = fm.storage('view', this.value == 'list' ? 'icons' : 'list');
  31835. return fm.lazy(function() {
  31836. fm.viewchange();
  31837. self.update(void(0), value);
  31838. this.resolve();
  31839. });
  31840. };
  31841. fm.bind('init', function() {
  31842. subMenuRaw = (function() {
  31843. var cwd = fm.getUI('cwd'),
  31844. raws = [],
  31845. sizeNames = fm.options.uiOptions.cwd.iconsView.sizeNames,
  31846. max = fm.options.uiOptions.cwd.iconsView.sizeMax,
  31847. i, size;
  31848. for (i = 0; i <= max; i++) {
  31849. raws.push(
  31850. {
  31851. label : fm.i18n(sizeNames[i] || ('Size-' + i + ' icons')),
  31852. icon : 'view',
  31853. callback : (function(s) {
  31854. return function() {
  31855. cwd.trigger('iconpref', {size: s});
  31856. fm.storage('iconsize', s);
  31857. if (self.value === 'list') {
  31858. self.exec();
  31859. }
  31860. };
  31861. })(i)
  31862. }
  31863. );
  31864. }
  31865. raws.push('|');
  31866. raws.push(
  31867. {
  31868. label : fm.i18n('viewlist'),
  31869. icon : 'view-list',
  31870. callback : function() {
  31871. if (self.value !== 'list') {
  31872. self.exec();
  31873. }
  31874. }
  31875. }
  31876. );
  31877. return raws;
  31878. })();
  31879. }).bind('contextmenucreate', function() {
  31880. self.extra = {
  31881. icon: 'menu',
  31882. node: $('<span/>')
  31883. .attr({title: fm.i18n('cmdview')})
  31884. .on('click touchstart', function(e){
  31885. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  31886. return;
  31887. }
  31888. var node = $(this),
  31889. raw = subMenuRaw.concat(),
  31890. idx, i;
  31891. if (self.value === 'list') {
  31892. idx = subMenuRaw.length - 1;
  31893. } else {
  31894. idx = parseInt(fm.storage('iconsize') || 0);
  31895. }
  31896. for (i = 0; i < subMenuRaw.length; i++) {
  31897. if (subMenuRaw[i] !== '|') {
  31898. subMenuRaw[i].options = (i === idx? {'className': 'ui-state-active'} : void(0))
  31899. ;
  31900. }
  31901. }
  31902. e.stopPropagation();
  31903. e.preventDefault();
  31904. fm.trigger('contextmenu', {
  31905. raw: subMenuRaw,
  31906. x: node.offset().left,
  31907. y: node.offset().top
  31908. });
  31909. })
  31910. };
  31911. });
  31912. };
  31913. return elFinder;
  31914. }));