elfinder.full.js 977 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623176241762517626176271762817629176301763117632176331763417635176361763717638176391764017641176421764317644176451764617647176481764917650176511765217653176541765517656176571765817659176601766117662176631766417665176661766717668176691767017671176721767317674176751767617677176781767917680176811768217683176841768517686176871768817689176901769117692176931769417695176961769717698176991770017701177021770317704177051770617707177081770917710177111771217713177141771517716177171771817719177201772117722177231772417725177261772717728177291773017731177321773317734177351773617737177381773917740177411774217743177441774517746177471774817749177501775117752177531775417755177561775717758177591776017761177621776317764177651776617767177681776917770177711777217773177741777517776177771777817779177801778117782177831778417785177861778717788177891779017791177921779317794177951779617797177981779917800178011780217803178041780517806178071780817809178101781117812178131781417815178161781717818178191782017821178221782317824178251782617827178281782917830178311783217833178341783517836178371783817839178401784117842178431784417845178461784717848178491785017851178521785317854178551785617857178581785917860178611786217863178641786517866178671786817869178701787117872178731787417875178761787717878178791788017881178821788317884178851788617887178881788917890178911789217893178941789517896178971789817899179001790117902179031790417905179061790717908179091791017911179121791317914179151791617917179181791917920179211792217923179241792517926179271792817929179301793117932179331793417935179361793717938179391794017941179421794317944179451794617947179481794917950179511795217953179541795517956179571795817959179601796117962179631796417965179661796717968179691797017971179721797317974179751797617977179781797917980179811798217983179841798517986179871798817989179901799117992179931799417995179961799717998179991800018001180021800318004180051800618007180081800918010180111801218013180141801518016180171801818019180201802118022180231802418025180261802718028180291803018031180321803318034180351803618037180381803918040180411804218043180441804518046180471804818049180501805118052180531805418055180561805718058180591806018061180621806318064180651806618067180681806918070180711807218073180741807518076180771807818079180801808118082180831808418085180861808718088180891809018091180921809318094180951809618097180981809918100181011810218103181041810518106181071810818109181101811118112181131811418115181161811718118181191812018121181221812318124181251812618127181281812918130181311813218133181341813518136181371813818139181401814118142181431814418145181461814718148181491815018151181521815318154181551815618157181581815918160181611816218163181641816518166181671816818169181701817118172181731817418175181761817718178181791818018181181821818318184181851818618187181881818918190181911819218193181941819518196181971819818199182001820118202182031820418205182061820718208182091821018211182121821318214182151821618217182181821918220182211822218223182241822518226182271822818229182301823118232182331823418235182361823718238182391824018241182421824318244182451824618247182481824918250182511825218253182541825518256182571825818259182601826118262182631826418265182661826718268182691827018271182721827318274182751827618277182781827918280182811828218283182841828518286182871828818289182901829118292182931829418295182961829718298182991830018301183021830318304183051830618307183081830918310183111831218313183141831518316183171831818319183201832118322183231832418325183261832718328183291833018331183321833318334183351833618337183381833918340183411834218343183441834518346183471834818349183501835118352183531835418355183561835718358183591836018361183621836318364183651836618367183681836918370183711837218373183741837518376183771837818379183801838118382183831838418385183861838718388183891839018391183921839318394183951839618397183981839918400184011840218403184041840518406184071840818409184101841118412184131841418415184161841718418184191842018421184221842318424184251842618427184281842918430184311843218433184341843518436184371843818439184401844118442184431844418445184461844718448184491845018451184521845318454184551845618457184581845918460184611846218463184641846518466184671846818469184701847118472184731847418475184761847718478184791848018481184821848318484184851848618487184881848918490184911849218493184941849518496184971849818499185001850118502185031850418505185061850718508185091851018511185121851318514185151851618517185181851918520185211852218523185241852518526185271852818529185301853118532185331853418535185361853718538185391854018541185421854318544185451854618547185481854918550185511855218553185541855518556185571855818559185601856118562185631856418565185661856718568185691857018571185721857318574185751857618577185781857918580185811858218583185841858518586185871858818589185901859118592185931859418595185961859718598185991860018601186021860318604186051860618607186081860918610186111861218613186141861518616186171861818619186201862118622186231862418625186261862718628186291863018631186321863318634186351863618637186381863918640186411864218643186441864518646186471864818649186501865118652186531865418655186561865718658186591866018661186621866318664186651866618667186681866918670186711867218673186741867518676186771867818679186801868118682186831868418685186861868718688186891869018691186921869318694186951869618697186981869918700187011870218703187041870518706187071870818709187101871118712187131871418715187161871718718187191872018721187221872318724187251872618727187281872918730187311873218733187341873518736187371873818739187401874118742187431874418745187461874718748187491875018751187521875318754187551875618757187581875918760187611876218763187641876518766187671876818769187701877118772187731877418775187761877718778187791878018781187821878318784187851878618787187881878918790187911879218793187941879518796187971879818799188001880118802188031880418805188061880718808188091881018811188121881318814188151881618817188181881918820188211882218823188241882518826188271882818829188301883118832188331883418835188361883718838188391884018841188421884318844188451884618847188481884918850188511885218853188541885518856188571885818859188601886118862188631886418865188661886718868188691887018871188721887318874188751887618877188781887918880188811888218883188841888518886188871888818889188901889118892188931889418895188961889718898188991890018901189021890318904189051890618907189081890918910189111891218913189141891518916189171891818919189201892118922189231892418925189261892718928189291893018931189321893318934189351893618937189381893918940189411894218943189441894518946189471894818949189501895118952189531895418955189561895718958189591896018961189621896318964189651896618967189681896918970189711897218973189741897518976189771897818979189801898118982189831898418985189861898718988189891899018991189921899318994189951899618997189981899919000190011900219003190041900519006190071900819009190101901119012190131901419015190161901719018190191902019021190221902319024190251902619027190281902919030190311903219033190341903519036190371903819039190401904119042190431904419045190461904719048190491905019051190521905319054190551905619057190581905919060190611906219063190641906519066190671906819069190701907119072190731907419075190761907719078190791908019081190821908319084190851908619087190881908919090190911909219093190941909519096190971909819099191001910119102191031910419105191061910719108191091911019111191121911319114191151911619117191181911919120191211912219123191241912519126191271912819129191301913119132191331913419135191361913719138191391914019141191421914319144191451914619147191481914919150191511915219153191541915519156191571915819159191601916119162191631916419165191661916719168191691917019171191721917319174191751917619177191781917919180191811918219183191841918519186191871918819189191901919119192191931919419195191961919719198191991920019201192021920319204192051920619207192081920919210192111921219213192141921519216192171921819219192201922119222192231922419225192261922719228192291923019231192321923319234192351923619237192381923919240192411924219243192441924519246192471924819249192501925119252192531925419255192561925719258192591926019261192621926319264192651926619267192681926919270192711927219273192741927519276192771927819279192801928119282192831928419285192861928719288192891929019291192921929319294192951929619297192981929919300193011930219303193041930519306193071930819309193101931119312193131931419315193161931719318193191932019321193221932319324193251932619327193281932919330193311933219333193341933519336193371933819339193401934119342193431934419345193461934719348193491935019351193521935319354193551935619357193581935919360193611936219363193641936519366193671936819369193701937119372193731937419375193761937719378193791938019381193821938319384193851938619387193881938919390193911939219393193941939519396193971939819399194001940119402194031940419405194061940719408194091941019411194121941319414194151941619417194181941919420194211942219423194241942519426194271942819429194301943119432194331943419435194361943719438194391944019441194421944319444194451944619447194481944919450194511945219453194541945519456194571945819459194601946119462194631946419465194661946719468194691947019471194721947319474194751947619477194781947919480194811948219483194841948519486194871948819489194901949119492194931949419495194961949719498194991950019501195021950319504195051950619507195081950919510195111951219513195141951519516195171951819519195201952119522195231952419525195261952719528195291953019531195321953319534195351953619537195381953919540195411954219543195441954519546195471954819549195501955119552195531955419555195561955719558195591956019561195621956319564195651956619567195681956919570195711957219573195741957519576195771957819579195801958119582195831958419585195861958719588195891959019591195921959319594195951959619597195981959919600196011960219603196041960519606196071960819609196101961119612196131961419615196161961719618196191962019621196221962319624196251962619627196281962919630196311963219633196341963519636196371963819639196401964119642196431964419645196461964719648196491965019651196521965319654196551965619657196581965919660196611966219663196641966519666196671966819669196701967119672196731967419675196761967719678196791968019681196821968319684196851968619687196881968919690196911969219693196941969519696196971969819699197001970119702197031970419705197061970719708197091971019711197121971319714197151971619717197181971919720197211972219723197241972519726197271972819729197301973119732197331973419735197361973719738197391974019741197421974319744197451974619747197481974919750197511975219753197541975519756197571975819759197601976119762197631976419765197661976719768197691977019771197721977319774197751977619777197781977919780197811978219783197841978519786197871978819789197901979119792197931979419795197961979719798197991980019801198021980319804198051980619807198081980919810198111981219813198141981519816198171981819819198201982119822198231982419825198261982719828198291983019831198321983319834198351983619837198381983919840198411984219843198441984519846198471984819849198501985119852198531985419855198561985719858198591986019861198621986319864198651986619867198681986919870198711987219873198741987519876198771987819879198801988119882198831988419885198861988719888198891989019891198921989319894198951989619897198981989919900199011990219903199041990519906199071990819909199101991119912199131991419915199161991719918199191992019921199221992319924199251992619927199281992919930199311993219933199341993519936199371993819939199401994119942199431994419945199461994719948199491995019951199521995319954199551995619957199581995919960199611996219963199641996519966199671996819969199701997119972199731997419975199761997719978199791998019981199821998319984199851998619987199881998919990199911999219993199941999519996199971999819999200002000120002200032000420005200062000720008200092001020011200122001320014200152001620017200182001920020200212002220023200242002520026200272002820029200302003120032200332003420035200362003720038200392004020041200422004320044200452004620047200482004920050200512005220053200542005520056200572005820059200602006120062200632006420065200662006720068200692007020071200722007320074200752007620077200782007920080200812008220083200842008520086200872008820089200902009120092200932009420095200962009720098200992010020101201022010320104201052010620107201082010920110201112011220113201142011520116201172011820119201202012120122201232012420125201262012720128201292013020131201322013320134201352013620137201382013920140201412014220143201442014520146201472014820149201502015120152201532015420155201562015720158201592016020161201622016320164201652016620167201682016920170201712017220173201742017520176201772017820179201802018120182201832018420185201862018720188201892019020191201922019320194201952019620197201982019920200202012020220203202042020520206202072020820209202102021120212202132021420215202162021720218202192022020221202222022320224202252022620227202282022920230202312023220233202342023520236202372023820239202402024120242202432024420245202462024720248202492025020251202522025320254202552025620257202582025920260202612026220263202642026520266202672026820269202702027120272202732027420275202762027720278202792028020281202822028320284202852028620287202882028920290202912029220293202942029520296202972029820299203002030120302203032030420305203062030720308203092031020311203122031320314203152031620317203182031920320203212032220323203242032520326203272032820329203302033120332203332033420335203362033720338203392034020341203422034320344203452034620347203482034920350203512035220353203542035520356203572035820359203602036120362203632036420365203662036720368203692037020371203722037320374203752037620377203782037920380203812038220383203842038520386203872038820389203902039120392203932039420395203962039720398203992040020401204022040320404204052040620407204082040920410204112041220413204142041520416204172041820419204202042120422204232042420425204262042720428204292043020431204322043320434204352043620437204382043920440204412044220443204442044520446204472044820449204502045120452204532045420455204562045720458204592046020461204622046320464204652046620467204682046920470204712047220473204742047520476204772047820479204802048120482204832048420485204862048720488204892049020491204922049320494204952049620497204982049920500205012050220503205042050520506205072050820509205102051120512205132051420515205162051720518205192052020521205222052320524205252052620527205282052920530205312053220533205342053520536205372053820539205402054120542205432054420545205462054720548205492055020551205522055320554205552055620557205582055920560205612056220563205642056520566205672056820569205702057120572205732057420575205762057720578205792058020581205822058320584205852058620587205882058920590205912059220593205942059520596205972059820599206002060120602206032060420605206062060720608206092061020611206122061320614206152061620617206182061920620206212062220623206242062520626206272062820629206302063120632206332063420635206362063720638206392064020641206422064320644206452064620647206482064920650206512065220653206542065520656206572065820659206602066120662206632066420665206662066720668206692067020671206722067320674206752067620677206782067920680206812068220683206842068520686206872068820689206902069120692206932069420695206962069720698206992070020701207022070320704207052070620707207082070920710207112071220713207142071520716207172071820719207202072120722207232072420725207262072720728207292073020731207322073320734207352073620737207382073920740207412074220743207442074520746207472074820749207502075120752207532075420755207562075720758207592076020761207622076320764207652076620767207682076920770207712077220773207742077520776207772077820779207802078120782207832078420785207862078720788207892079020791207922079320794207952079620797207982079920800208012080220803208042080520806208072080820809208102081120812208132081420815208162081720818208192082020821208222082320824208252082620827208282082920830208312083220833208342083520836208372083820839208402084120842208432084420845208462084720848208492085020851208522085320854208552085620857208582085920860208612086220863208642086520866208672086820869208702087120872208732087420875208762087720878208792088020881208822088320884208852088620887208882088920890208912089220893208942089520896208972089820899209002090120902209032090420905209062090720908209092091020911209122091320914209152091620917209182091920920209212092220923209242092520926209272092820929209302093120932209332093420935209362093720938209392094020941209422094320944209452094620947209482094920950209512095220953209542095520956209572095820959209602096120962209632096420965209662096720968209692097020971209722097320974209752097620977209782097920980209812098220983209842098520986209872098820989209902099120992209932099420995209962099720998209992100021001210022100321004210052100621007210082100921010210112101221013210142101521016210172101821019210202102121022210232102421025210262102721028210292103021031210322103321034210352103621037210382103921040210412104221043210442104521046210472104821049210502105121052210532105421055210562105721058210592106021061210622106321064210652106621067210682106921070210712107221073210742107521076210772107821079210802108121082210832108421085210862108721088210892109021091210922109321094210952109621097210982109921100211012110221103211042110521106211072110821109211102111121112211132111421115211162111721118211192112021121211222112321124211252112621127211282112921130211312113221133211342113521136211372113821139211402114121142211432114421145211462114721148211492115021151211522115321154211552115621157211582115921160211612116221163211642116521166211672116821169211702117121172211732117421175211762117721178211792118021181211822118321184211852118621187211882118921190211912119221193211942119521196211972119821199212002120121202212032120421205212062120721208212092121021211212122121321214212152121621217212182121921220212212122221223212242122521226212272122821229212302123121232212332123421235212362123721238212392124021241212422124321244212452124621247212482124921250212512125221253212542125521256212572125821259212602126121262212632126421265212662126721268212692127021271212722127321274212752127621277212782127921280212812128221283212842128521286212872128821289212902129121292212932129421295212962129721298212992130021301213022130321304213052130621307213082130921310213112131221313213142131521316213172131821319213202132121322213232132421325213262132721328213292133021331213322133321334213352133621337213382133921340213412134221343213442134521346213472134821349213502135121352213532135421355213562135721358213592136021361213622136321364213652136621367213682136921370213712137221373213742137521376213772137821379213802138121382213832138421385213862138721388213892139021391213922139321394213952139621397213982139921400214012140221403214042140521406214072140821409214102141121412214132141421415214162141721418214192142021421214222142321424214252142621427214282142921430214312143221433214342143521436214372143821439214402144121442214432144421445214462144721448214492145021451214522145321454214552145621457214582145921460214612146221463214642146521466214672146821469214702147121472214732147421475214762147721478214792148021481214822148321484214852148621487214882148921490214912149221493214942149521496214972149821499215002150121502215032150421505215062150721508215092151021511215122151321514215152151621517215182151921520215212152221523215242152521526215272152821529215302153121532215332153421535215362153721538215392154021541215422154321544215452154621547215482154921550215512155221553215542155521556215572155821559215602156121562215632156421565215662156721568215692157021571215722157321574215752157621577215782157921580215812158221583215842158521586215872158821589215902159121592215932159421595215962159721598215992160021601216022160321604216052160621607216082160921610216112161221613216142161521616216172161821619216202162121622216232162421625216262162721628216292163021631216322163321634216352163621637216382163921640216412164221643216442164521646216472164821649216502165121652216532165421655216562165721658216592166021661216622166321664216652166621667216682166921670216712167221673216742167521676216772167821679216802168121682216832168421685216862168721688216892169021691216922169321694216952169621697216982169921700217012170221703217042170521706217072170821709217102171121712217132171421715217162171721718217192172021721217222172321724217252172621727217282172921730217312173221733217342173521736217372173821739217402174121742217432174421745217462174721748217492175021751217522175321754217552175621757217582175921760217612176221763217642176521766217672176821769217702177121772217732177421775217762177721778217792178021781217822178321784217852178621787217882178921790217912179221793217942179521796217972179821799218002180121802218032180421805218062180721808218092181021811218122181321814218152181621817218182181921820218212182221823218242182521826218272182821829218302183121832218332183421835218362183721838218392184021841218422184321844218452184621847218482184921850218512185221853218542185521856218572185821859218602186121862218632186421865218662186721868218692187021871218722187321874218752187621877218782187921880218812188221883218842188521886218872188821889218902189121892218932189421895218962189721898218992190021901219022190321904219052190621907219082190921910219112191221913219142191521916219172191821919219202192121922219232192421925219262192721928219292193021931219322193321934219352193621937219382193921940219412194221943219442194521946219472194821949219502195121952219532195421955219562195721958219592196021961219622196321964219652196621967219682196921970219712197221973219742197521976219772197821979219802198121982219832198421985219862198721988219892199021991219922199321994219952199621997219982199922000220012200222003220042200522006220072200822009220102201122012220132201422015220162201722018220192202022021220222202322024220252202622027220282202922030220312203222033220342203522036220372203822039220402204122042220432204422045220462204722048220492205022051220522205322054220552205622057220582205922060220612206222063220642206522066220672206822069220702207122072220732207422075220762207722078220792208022081220822208322084220852208622087220882208922090220912209222093220942209522096220972209822099221002210122102221032210422105221062210722108221092211022111221122211322114221152211622117221182211922120221212212222123221242212522126221272212822129221302213122132221332213422135221362213722138221392214022141221422214322144221452214622147221482214922150221512215222153221542215522156221572215822159221602216122162221632216422165221662216722168221692217022171221722217322174221752217622177221782217922180221812218222183221842218522186221872218822189221902219122192221932219422195221962219722198221992220022201222022220322204222052220622207222082220922210222112221222213222142221522216222172221822219222202222122222222232222422225222262222722228222292223022231222322223322234222352223622237222382223922240222412224222243222442224522246222472224822249222502225122252222532225422255222562225722258222592226022261222622226322264222652226622267222682226922270222712227222273222742227522276222772227822279222802228122282222832228422285222862228722288222892229022291222922229322294222952229622297222982229922300223012230222303223042230522306223072230822309223102231122312223132231422315223162231722318223192232022321223222232322324223252232622327223282232922330223312233222333223342233522336223372233822339223402234122342223432234422345223462234722348223492235022351223522235322354223552235622357223582235922360223612236222363223642236522366223672236822369223702237122372223732237422375223762237722378223792238022381223822238322384223852238622387223882238922390223912239222393223942239522396223972239822399224002240122402224032240422405224062240722408224092241022411224122241322414224152241622417224182241922420224212242222423224242242522426224272242822429224302243122432224332243422435224362243722438224392244022441224422244322444224452244622447224482244922450224512245222453224542245522456224572245822459224602246122462224632246422465224662246722468224692247022471224722247322474224752247622477224782247922480224812248222483224842248522486224872248822489224902249122492224932249422495224962249722498224992250022501225022250322504225052250622507225082250922510225112251222513225142251522516225172251822519225202252122522225232252422525225262252722528225292253022531225322253322534225352253622537225382253922540225412254222543225442254522546225472254822549225502255122552225532255422555225562255722558225592256022561225622256322564225652256622567225682256922570225712257222573225742257522576225772257822579225802258122582225832258422585225862258722588225892259022591225922259322594225952259622597225982259922600226012260222603226042260522606226072260822609226102261122612226132261422615226162261722618226192262022621226222262322624226252262622627226282262922630226312263222633226342263522636226372263822639226402264122642226432264422645226462264722648226492265022651226522265322654226552265622657226582265922660226612266222663226642266522666226672266822669226702267122672226732267422675226762267722678226792268022681226822268322684226852268622687226882268922690226912269222693226942269522696226972269822699227002270122702227032270422705227062270722708227092271022711227122271322714227152271622717227182271922720227212272222723227242272522726227272272822729227302273122732227332273422735227362273722738227392274022741227422274322744227452274622747227482274922750227512275222753227542275522756227572275822759227602276122762227632276422765227662276722768227692277022771227722277322774227752277622777227782277922780227812278222783227842278522786227872278822789227902279122792227932279422795227962279722798227992280022801228022280322804228052280622807228082280922810228112281222813228142281522816228172281822819228202282122822228232282422825228262282722828228292283022831228322283322834228352283622837228382283922840228412284222843228442284522846228472284822849228502285122852228532285422855228562285722858228592286022861228622286322864228652286622867228682286922870228712287222873228742287522876228772287822879228802288122882228832288422885228862288722888228892289022891228922289322894228952289622897228982289922900229012290222903229042290522906229072290822909229102291122912229132291422915229162291722918229192292022921229222292322924229252292622927229282292922930229312293222933229342293522936229372293822939229402294122942229432294422945229462294722948229492295022951229522295322954229552295622957229582295922960229612296222963229642296522966229672296822969229702297122972229732297422975229762297722978229792298022981229822298322984229852298622987229882298922990229912299222993229942299522996229972299822999230002300123002230032300423005230062300723008230092301023011230122301323014230152301623017230182301923020230212302223023230242302523026230272302823029230302303123032230332303423035230362303723038230392304023041230422304323044230452304623047230482304923050230512305223053230542305523056230572305823059230602306123062230632306423065230662306723068230692307023071230722307323074230752307623077230782307923080230812308223083230842308523086230872308823089230902309123092230932309423095230962309723098230992310023101231022310323104231052310623107231082310923110231112311223113231142311523116231172311823119231202312123122231232312423125231262312723128231292313023131231322313323134231352313623137231382313923140231412314223143231442314523146231472314823149231502315123152231532315423155231562315723158231592316023161231622316323164231652316623167231682316923170231712317223173231742317523176231772317823179231802318123182231832318423185231862318723188231892319023191231922319323194231952319623197231982319923200232012320223203232042320523206232072320823209232102321123212232132321423215232162321723218232192322023221232222322323224232252322623227232282322923230232312323223233232342323523236232372323823239232402324123242232432324423245232462324723248232492325023251232522325323254232552325623257232582325923260232612326223263232642326523266232672326823269232702327123272232732327423275232762327723278232792328023281232822328323284232852328623287232882328923290232912329223293232942329523296232972329823299233002330123302233032330423305233062330723308233092331023311233122331323314233152331623317233182331923320233212332223323233242332523326233272332823329233302333123332233332333423335233362333723338233392334023341233422334323344233452334623347233482334923350233512335223353233542335523356233572335823359233602336123362233632336423365233662336723368233692337023371233722337323374233752337623377233782337923380233812338223383233842338523386233872338823389233902339123392233932339423395233962339723398233992340023401234022340323404234052340623407234082340923410234112341223413234142341523416234172341823419234202342123422234232342423425234262342723428234292343023431234322343323434234352343623437234382343923440234412344223443234442344523446234472344823449234502345123452234532345423455234562345723458234592346023461234622346323464234652346623467234682346923470234712347223473234742347523476234772347823479234802348123482234832348423485234862348723488234892349023491234922349323494234952349623497234982349923500235012350223503235042350523506235072350823509235102351123512235132351423515235162351723518235192352023521235222352323524235252352623527235282352923530235312353223533235342353523536235372353823539235402354123542235432354423545235462354723548235492355023551235522355323554235552355623557235582355923560235612356223563235642356523566235672356823569235702357123572235732357423575235762357723578235792358023581235822358323584235852358623587235882358923590235912359223593235942359523596235972359823599236002360123602236032360423605236062360723608236092361023611236122361323614236152361623617236182361923620236212362223623236242362523626236272362823629236302363123632236332363423635236362363723638236392364023641236422364323644236452364623647236482364923650236512365223653236542365523656236572365823659236602366123662236632366423665236662366723668236692367023671236722367323674236752367623677236782367923680236812368223683236842368523686236872368823689236902369123692236932369423695236962369723698236992370023701237022370323704237052370623707237082370923710237112371223713237142371523716237172371823719237202372123722237232372423725237262372723728237292373023731237322373323734237352373623737237382373923740237412374223743237442374523746237472374823749237502375123752237532375423755237562375723758237592376023761237622376323764237652376623767237682376923770237712377223773237742377523776237772377823779237802378123782237832378423785237862378723788237892379023791237922379323794237952379623797237982379923800238012380223803238042380523806238072380823809238102381123812238132381423815238162381723818238192382023821238222382323824238252382623827238282382923830238312383223833238342383523836238372383823839238402384123842238432384423845238462384723848238492385023851238522385323854238552385623857238582385923860238612386223863238642386523866238672386823869238702387123872238732387423875238762387723878238792388023881238822388323884238852388623887238882388923890238912389223893238942389523896238972389823899239002390123902239032390423905239062390723908239092391023911239122391323914239152391623917239182391923920239212392223923239242392523926239272392823929239302393123932239332393423935239362393723938239392394023941239422394323944239452394623947239482394923950239512395223953239542395523956239572395823959239602396123962239632396423965239662396723968239692397023971239722397323974239752397623977239782397923980239812398223983239842398523986239872398823989239902399123992239932399423995239962399723998239992400024001240022400324004240052400624007240082400924010240112401224013240142401524016240172401824019240202402124022240232402424025240262402724028240292403024031240322403324034240352403624037240382403924040240412404224043240442404524046240472404824049240502405124052240532405424055240562405724058240592406024061240622406324064240652406624067240682406924070240712407224073240742407524076240772407824079240802408124082240832408424085240862408724088240892409024091240922409324094240952409624097240982409924100241012410224103241042410524106241072410824109241102411124112241132411424115241162411724118241192412024121241222412324124241252412624127241282412924130241312413224133241342413524136241372413824139241402414124142241432414424145241462414724148241492415024151241522415324154241552415624157241582415924160241612416224163241642416524166241672416824169241702417124172241732417424175241762417724178241792418024181241822418324184241852418624187241882418924190241912419224193241942419524196241972419824199242002420124202242032420424205242062420724208242092421024211242122421324214242152421624217242182421924220242212422224223242242422524226242272422824229242302423124232242332423424235242362423724238242392424024241242422424324244242452424624247242482424924250242512425224253242542425524256242572425824259242602426124262242632426424265242662426724268242692427024271242722427324274242752427624277242782427924280242812428224283242842428524286242872428824289242902429124292242932429424295242962429724298242992430024301243022430324304243052430624307243082430924310243112431224313243142431524316243172431824319243202432124322243232432424325243262432724328243292433024331243322433324334243352433624337243382433924340243412434224343243442434524346243472434824349243502435124352243532435424355243562435724358243592436024361243622436324364243652436624367243682436924370243712437224373243742437524376243772437824379243802438124382243832438424385243862438724388243892439024391243922439324394243952439624397243982439924400244012440224403244042440524406244072440824409244102441124412244132441424415244162441724418244192442024421244222442324424244252442624427244282442924430244312443224433244342443524436244372443824439244402444124442244432444424445244462444724448244492445024451244522445324454244552445624457244582445924460244612446224463244642446524466244672446824469244702447124472244732447424475244762447724478244792448024481244822448324484244852448624487244882448924490244912449224493244942449524496244972449824499245002450124502245032450424505245062450724508245092451024511245122451324514245152451624517245182451924520245212452224523245242452524526245272452824529245302453124532245332453424535245362453724538245392454024541245422454324544245452454624547245482454924550245512455224553245542455524556245572455824559245602456124562245632456424565245662456724568245692457024571245722457324574245752457624577245782457924580245812458224583245842458524586245872458824589245902459124592245932459424595245962459724598245992460024601246022460324604246052460624607246082460924610246112461224613246142461524616246172461824619246202462124622246232462424625246262462724628246292463024631246322463324634246352463624637246382463924640246412464224643246442464524646246472464824649246502465124652246532465424655246562465724658246592466024661246622466324664246652466624667246682466924670246712467224673246742467524676246772467824679246802468124682246832468424685246862468724688246892469024691246922469324694246952469624697246982469924700247012470224703247042470524706247072470824709247102471124712247132471424715247162471724718247192472024721247222472324724247252472624727247282472924730247312473224733247342473524736247372473824739247402474124742247432474424745247462474724748247492475024751247522475324754247552475624757247582475924760247612476224763247642476524766247672476824769247702477124772247732477424775247762477724778247792478024781247822478324784247852478624787247882478924790247912479224793247942479524796247972479824799248002480124802248032480424805248062480724808248092481024811248122481324814248152481624817248182481924820248212482224823248242482524826248272482824829248302483124832248332483424835248362483724838248392484024841248422484324844248452484624847248482484924850248512485224853248542485524856248572485824859248602486124862248632486424865248662486724868248692487024871248722487324874248752487624877248782487924880248812488224883248842488524886248872488824889248902489124892248932489424895248962489724898248992490024901249022490324904249052490624907249082490924910249112491224913249142491524916249172491824919249202492124922249232492424925249262492724928249292493024931249322493324934249352493624937249382493924940249412494224943249442494524946249472494824949249502495124952249532495424955249562495724958249592496024961249622496324964249652496624967249682496924970249712497224973249742497524976249772497824979249802498124982249832498424985249862498724988249892499024991249922499324994249952499624997249982499925000250012500225003250042500525006250072500825009250102501125012250132501425015250162501725018250192502025021250222502325024250252502625027250282502925030250312503225033250342503525036250372503825039250402504125042250432504425045250462504725048250492505025051250522505325054250552505625057250582505925060250612506225063250642506525066250672506825069250702507125072250732507425075250762507725078250792508025081250822508325084250852508625087250882508925090250912509225093250942509525096250972509825099251002510125102251032510425105251062510725108251092511025111251122511325114251152511625117251182511925120251212512225123251242512525126251272512825129251302513125132251332513425135251362513725138251392514025141251422514325144251452514625147251482514925150251512515225153251542515525156251572515825159251602516125162251632516425165251662516725168251692517025171251722517325174251752517625177251782517925180251812518225183251842518525186251872518825189251902519125192251932519425195251962519725198251992520025201252022520325204252052520625207252082520925210252112521225213252142521525216252172521825219252202522125222252232522425225252262522725228252292523025231252322523325234252352523625237252382523925240252412524225243252442524525246252472524825249252502525125252252532525425255252562525725258252592526025261252622526325264252652526625267252682526925270252712527225273252742527525276252772527825279252802528125282252832528425285252862528725288252892529025291252922529325294252952529625297252982529925300253012530225303253042530525306253072530825309253102531125312253132531425315253162531725318253192532025321253222532325324253252532625327253282532925330253312533225333253342533525336253372533825339253402534125342253432534425345253462534725348253492535025351253522535325354253552535625357253582535925360253612536225363253642536525366253672536825369253702537125372253732537425375253762537725378253792538025381253822538325384253852538625387253882538925390253912539225393253942539525396253972539825399254002540125402254032540425405254062540725408254092541025411254122541325414254152541625417254182541925420254212542225423254242542525426254272542825429254302543125432254332543425435254362543725438254392544025441254422544325444254452544625447254482544925450254512545225453254542545525456254572545825459254602546125462254632546425465254662546725468254692547025471254722547325474254752547625477254782547925480254812548225483254842548525486254872548825489254902549125492254932549425495254962549725498254992550025501255022550325504255052550625507255082550925510255112551225513255142551525516255172551825519255202552125522255232552425525255262552725528255292553025531255322553325534255352553625537255382553925540255412554225543255442554525546255472554825549255502555125552255532555425555255562555725558255592556025561255622556325564255652556625567255682556925570255712557225573255742557525576255772557825579255802558125582255832558425585255862558725588255892559025591255922559325594255952559625597255982559925600256012560225603256042560525606256072560825609256102561125612256132561425615256162561725618256192562025621256222562325624256252562625627256282562925630256312563225633256342563525636256372563825639256402564125642256432564425645256462564725648256492565025651256522565325654256552565625657256582565925660256612566225663256642566525666256672566825669256702567125672256732567425675256762567725678256792568025681256822568325684256852568625687256882568925690256912569225693256942569525696256972569825699257002570125702257032570425705257062570725708257092571025711257122571325714257152571625717257182571925720257212572225723257242572525726257272572825729257302573125732257332573425735257362573725738257392574025741257422574325744257452574625747257482574925750257512575225753257542575525756257572575825759257602576125762257632576425765257662576725768257692577025771257722577325774257752577625777257782577925780257812578225783257842578525786257872578825789257902579125792257932579425795257962579725798257992580025801258022580325804258052580625807258082580925810258112581225813258142581525816258172581825819258202582125822258232582425825258262582725828258292583025831258322583325834258352583625837258382583925840258412584225843258442584525846258472584825849258502585125852258532585425855258562585725858258592586025861258622586325864258652586625867258682586925870258712587225873258742587525876258772587825879258802588125882258832588425885258862588725888258892589025891258922589325894258952589625897258982589925900259012590225903259042590525906259072590825909259102591125912259132591425915259162591725918259192592025921259222592325924259252592625927259282592925930259312593225933259342593525936259372593825939259402594125942259432594425945259462594725948259492595025951259522595325954259552595625957259582595925960259612596225963259642596525966259672596825969259702597125972259732597425975259762597725978259792598025981259822598325984259852598625987259882598925990259912599225993259942599525996259972599825999260002600126002260032600426005260062600726008260092601026011260122601326014260152601626017260182601926020260212602226023260242602526026260272602826029260302603126032260332603426035260362603726038260392604026041260422604326044260452604626047260482604926050260512605226053260542605526056260572605826059260602606126062260632606426065260662606726068260692607026071260722607326074260752607626077260782607926080260812608226083260842608526086260872608826089260902609126092260932609426095260962609726098260992610026101261022610326104261052610626107261082610926110261112611226113261142611526116261172611826119261202612126122261232612426125261262612726128261292613026131261322613326134261352613626137261382613926140261412614226143261442614526146261472614826149261502615126152261532615426155261562615726158261592616026161261622616326164261652616626167261682616926170261712617226173261742617526176261772617826179261802618126182261832618426185261862618726188261892619026191261922619326194261952619626197261982619926200262012620226203262042620526206262072620826209262102621126212262132621426215262162621726218262192622026221262222622326224262252622626227262282622926230262312623226233262342623526236262372623826239262402624126242262432624426245262462624726248262492625026251262522625326254262552625626257262582625926260262612626226263262642626526266262672626826269262702627126272262732627426275262762627726278262792628026281262822628326284262852628626287262882628926290262912629226293262942629526296262972629826299263002630126302263032630426305263062630726308263092631026311263122631326314263152631626317263182631926320263212632226323263242632526326263272632826329263302633126332263332633426335263362633726338263392634026341263422634326344263452634626347263482634926350263512635226353263542635526356263572635826359263602636126362263632636426365263662636726368263692637026371263722637326374263752637626377263782637926380263812638226383263842638526386263872638826389263902639126392263932639426395263962639726398263992640026401264022640326404264052640626407264082640926410264112641226413264142641526416264172641826419264202642126422264232642426425264262642726428264292643026431264322643326434264352643626437264382643926440264412644226443264442644526446264472644826449264502645126452264532645426455264562645726458264592646026461264622646326464264652646626467264682646926470264712647226473264742647526476264772647826479264802648126482264832648426485264862648726488264892649026491264922649326494264952649626497264982649926500265012650226503265042650526506265072650826509265102651126512265132651426515265162651726518265192652026521265222652326524265252652626527265282652926530265312653226533265342653526536265372653826539265402654126542265432654426545265462654726548265492655026551265522655326554265552655626557265582655926560265612656226563265642656526566265672656826569265702657126572265732657426575265762657726578265792658026581265822658326584265852658626587265882658926590265912659226593265942659526596265972659826599266002660126602266032660426605266062660726608266092661026611266122661326614266152661626617266182661926620266212662226623266242662526626266272662826629266302663126632266332663426635266362663726638266392664026641266422664326644266452664626647266482664926650266512665226653266542665526656266572665826659266602666126662266632666426665266662666726668266692667026671266722667326674266752667626677266782667926680266812668226683266842668526686266872668826689266902669126692266932669426695266962669726698266992670026701267022670326704267052670626707267082670926710267112671226713267142671526716267172671826719267202672126722267232672426725267262672726728267292673026731267322673326734267352673626737267382673926740267412674226743267442674526746267472674826749267502675126752267532675426755267562675726758267592676026761267622676326764267652676626767267682676926770267712677226773267742677526776267772677826779267802678126782267832678426785267862678726788267892679026791267922679326794267952679626797267982679926800268012680226803268042680526806268072680826809268102681126812268132681426815268162681726818268192682026821268222682326824268252682626827268282682926830268312683226833268342683526836268372683826839268402684126842268432684426845268462684726848268492685026851268522685326854268552685626857268582685926860268612686226863268642686526866268672686826869268702687126872268732687426875268762687726878268792688026881268822688326884268852688626887268882688926890268912689226893268942689526896268972689826899269002690126902269032690426905269062690726908269092691026911269122691326914269152691626917269182691926920269212692226923269242692526926269272692826929269302693126932269332693426935269362693726938269392694026941269422694326944269452694626947269482694926950269512695226953269542695526956269572695826959269602696126962269632696426965269662696726968269692697026971269722697326974269752697626977269782697926980269812698226983269842698526986269872698826989269902699126992269932699426995269962699726998269992700027001270022700327004270052700627007270082700927010270112701227013270142701527016270172701827019270202702127022270232702427025270262702727028270292703027031270322703327034270352703627037270382703927040270412704227043270442704527046270472704827049270502705127052270532705427055270562705727058270592706027061270622706327064270652706627067270682706927070270712707227073270742707527076270772707827079270802708127082270832708427085270862708727088270892709027091270922709327094270952709627097270982709927100271012710227103271042710527106271072710827109271102711127112271132711427115271162711727118271192712027121271222712327124271252712627127271282712927130271312713227133271342713527136271372713827139271402714127142271432714427145271462714727148271492715027151271522715327154271552715627157271582715927160271612716227163271642716527166271672716827169271702717127172271732717427175271762717727178271792718027181271822718327184271852718627187271882718927190271912719227193271942719527196271972719827199272002720127202272032720427205272062720727208272092721027211272122721327214272152721627217272182721927220272212722227223272242722527226272272722827229272302723127232272332723427235272362723727238272392724027241272422724327244272452724627247272482724927250272512725227253272542725527256272572725827259272602726127262272632726427265272662726727268272692727027271272722727327274272752727627277272782727927280272812728227283272842728527286272872728827289272902729127292272932729427295272962729727298272992730027301273022730327304273052730627307273082730927310273112731227313273142731527316273172731827319273202732127322273232732427325273262732727328273292733027331273322733327334273352733627337273382733927340273412734227343273442734527346273472734827349273502735127352273532735427355273562735727358273592736027361273622736327364273652736627367273682736927370273712737227373273742737527376273772737827379273802738127382273832738427385273862738727388273892739027391273922739327394273952739627397273982739927400274012740227403274042740527406274072740827409274102741127412274132741427415274162741727418274192742027421274222742327424274252742627427274282742927430274312743227433274342743527436274372743827439274402744127442274432744427445274462744727448274492745027451274522745327454274552745627457274582745927460274612746227463274642746527466274672746827469274702747127472274732747427475274762747727478274792748027481274822748327484274852748627487274882748927490274912749227493274942749527496274972749827499275002750127502275032750427505275062750727508275092751027511275122751327514275152751627517275182751927520275212752227523275242752527526275272752827529275302753127532275332753427535275362753727538275392754027541275422754327544275452754627547275482754927550275512755227553275542755527556275572755827559275602756127562275632756427565275662756727568275692757027571275722757327574275752757627577275782757927580275812758227583275842758527586275872758827589275902759127592275932759427595275962759727598275992760027601276022760327604276052760627607276082760927610276112761227613276142761527616276172761827619276202762127622276232762427625276262762727628276292763027631276322763327634276352763627637276382763927640276412764227643276442764527646276472764827649276502765127652276532765427655276562765727658276592766027661276622766327664276652766627667276682766927670276712767227673276742767527676276772767827679276802768127682276832768427685276862768727688276892769027691276922769327694276952769627697276982769927700277012770227703277042770527706277072770827709277102771127712277132771427715277162771727718277192772027721277222772327724277252772627727277282772927730277312773227733277342773527736277372773827739277402774127742277432774427745277462774727748277492775027751277522775327754277552775627757277582775927760277612776227763277642776527766277672776827769277702777127772277732777427775277762777727778277792778027781277822778327784277852778627787277882778927790277912779227793277942779527796277972779827799278002780127802278032780427805278062780727808278092781027811278122781327814278152781627817278182781927820278212782227823278242782527826278272782827829278302783127832278332783427835278362783727838278392784027841278422784327844278452784627847278482784927850278512785227853278542785527856278572785827859278602786127862278632786427865278662786727868278692787027871278722787327874278752787627877278782787927880278812788227883278842788527886278872788827889278902789127892278932789427895278962789727898278992790027901279022790327904279052790627907279082790927910279112791227913279142791527916279172791827919279202792127922279232792427925279262792727928279292793027931279322793327934279352793627937279382793927940279412794227943279442794527946279472794827949279502795127952279532795427955279562795727958279592796027961279622796327964279652796627967279682796927970279712797227973279742797527976279772797827979279802798127982279832798427985279862798727988279892799027991279922799327994279952799627997279982799928000280012800228003280042800528006280072800828009280102801128012280132801428015280162801728018280192802028021280222802328024280252802628027280282802928030280312803228033280342803528036280372803828039280402804128042280432804428045280462804728048280492805028051280522805328054280552805628057280582805928060280612806228063280642806528066280672806828069280702807128072280732807428075280762807728078280792808028081280822808328084280852808628087280882808928090280912809228093280942809528096280972809828099281002810128102281032810428105281062810728108281092811028111281122811328114281152811628117281182811928120281212812228123281242812528126281272812828129281302813128132281332813428135281362813728138281392814028141281422814328144281452814628147281482814928150281512815228153281542815528156281572815828159281602816128162281632816428165281662816728168281692817028171281722817328174281752817628177281782817928180281812818228183281842818528186281872818828189281902819128192281932819428195281962819728198281992820028201282022820328204282052820628207282082820928210282112821228213282142821528216282172821828219282202822128222282232822428225282262822728228282292823028231282322823328234282352823628237282382823928240282412824228243282442824528246282472824828249282502825128252282532825428255282562825728258282592826028261282622826328264282652826628267282682826928270282712827228273282742827528276282772827828279282802828128282282832828428285282862828728288282892829028291282922829328294282952829628297282982829928300283012830228303283042830528306283072830828309283102831128312283132831428315283162831728318283192832028321283222832328324283252832628327283282832928330283312833228333283342833528336283372833828339283402834128342283432834428345283462834728348283492835028351283522835328354283552835628357283582835928360283612836228363283642836528366283672836828369283702837128372283732837428375283762837728378283792838028381283822838328384283852838628387283882838928390283912839228393283942839528396283972839828399284002840128402284032840428405284062840728408284092841028411284122841328414284152841628417284182841928420284212842228423284242842528426284272842828429284302843128432284332843428435284362843728438284392844028441284422844328444284452844628447284482844928450284512845228453284542845528456284572845828459284602846128462284632846428465284662846728468284692847028471284722847328474284752847628477284782847928480284812848228483284842848528486284872848828489284902849128492284932849428495284962849728498284992850028501285022850328504285052850628507285082850928510285112851228513285142851528516285172851828519285202852128522285232852428525285262852728528285292853028531285322853328534285352853628537285382853928540285412854228543285442854528546285472854828549285502855128552285532855428555285562855728558285592856028561285622856328564285652856628567285682856928570285712857228573285742857528576285772857828579285802858128582285832858428585285862858728588285892859028591285922859328594285952859628597285982859928600286012860228603286042860528606286072860828609286102861128612286132861428615286162861728618286192862028621286222862328624286252862628627286282862928630286312863228633286342863528636286372863828639286402864128642286432864428645286462864728648286492865028651286522865328654286552865628657286582865928660286612866228663286642866528666286672866828669286702867128672286732867428675286762867728678286792868028681286822868328684286852868628687286882868928690286912869228693286942869528696286972869828699287002870128702287032870428705287062870728708287092871028711287122871328714287152871628717287182871928720287212872228723287242872528726287272872828729287302873128732287332873428735287362873728738287392874028741287422874328744287452874628747287482874928750287512875228753287542875528756287572875828759287602876128762287632876428765287662876728768287692877028771287722877328774287752877628777287782877928780287812878228783287842878528786287872878828789287902879128792287932879428795287962879728798287992880028801288022880328804288052880628807288082880928810288112881228813288142881528816288172881828819288202882128822288232882428825288262882728828288292883028831288322883328834288352883628837288382883928840288412884228843288442884528846288472884828849288502885128852288532885428855288562885728858288592886028861288622886328864288652886628867288682886928870288712887228873288742887528876288772887828879288802888128882288832888428885288862888728888288892889028891288922889328894288952889628897288982889928900289012890228903289042890528906289072890828909289102891128912289132891428915289162891728918289192892028921289222892328924289252892628927289282892928930289312893228933289342893528936289372893828939289402894128942289432894428945289462894728948289492895028951289522895328954289552895628957289582895928960289612896228963289642896528966289672896828969289702897128972289732897428975289762897728978289792898028981289822898328984289852898628987289882898928990289912899228993289942899528996289972899828999290002900129002290032900429005290062900729008290092901029011290122901329014290152901629017290182901929020290212902229023290242902529026290272902829029290302903129032290332903429035290362903729038290392904029041290422904329044290452904629047290482904929050290512905229053290542905529056290572905829059290602906129062290632906429065290662906729068290692907029071290722907329074290752907629077290782907929080290812908229083290842908529086290872908829089290902909129092290932909429095290962909729098290992910029101291022910329104291052910629107291082910929110291112911229113291142911529116291172911829119291202912129122291232912429125291262912729128291292913029131291322913329134291352913629137291382913929140291412914229143291442914529146291472914829149291502915129152291532915429155291562915729158291592916029161291622916329164291652916629167291682916929170291712917229173291742917529176291772917829179291802918129182291832918429185291862918729188291892919029191291922919329194291952919629197291982919929200292012920229203292042920529206292072920829209292102921129212292132921429215292162921729218292192922029221292222922329224292252922629227292282922929230292312923229233292342923529236292372923829239292402924129242292432924429245292462924729248292492925029251292522925329254292552925629257292582925929260292612926229263292642926529266292672926829269292702927129272292732927429275292762927729278292792928029281292822928329284292852928629287292882928929290292912929229293292942929529296292972929829299293002930129302293032930429305293062930729308293092931029311293122931329314293152931629317293182931929320293212932229323293242932529326293272932829329293302933129332293332933429335293362933729338293392934029341293422934329344293452934629347293482934929350293512935229353293542935529356293572935829359293602936129362293632936429365293662936729368293692937029371293722937329374293752937629377293782937929380293812938229383293842938529386293872938829389293902939129392293932939429395293962939729398293992940029401294022940329404294052940629407294082940929410294112941229413294142941529416294172941829419294202942129422294232942429425294262942729428294292943029431294322943329434294352943629437294382943929440294412944229443294442944529446294472944829449294502945129452294532945429455294562945729458294592946029461294622946329464294652946629467294682946929470294712947229473294742947529476294772947829479294802948129482294832948429485294862948729488294892949029491294922949329494294952949629497294982949929500295012950229503295042950529506295072950829509295102951129512295132951429515295162951729518295192952029521295222952329524295252952629527295282952929530295312953229533295342953529536295372953829539295402954129542295432954429545295462954729548295492955029551295522955329554295552955629557295582955929560295612956229563295642956529566295672956829569295702957129572295732957429575295762957729578295792958029581295822958329584295852958629587295882958929590295912959229593295942959529596295972959829599296002960129602296032960429605296062960729608296092961029611296122961329614296152961629617296182961929620296212962229623296242962529626296272962829629296302963129632296332963429635296362963729638296392964029641296422964329644296452964629647296482964929650296512965229653296542965529656296572965829659296602966129662296632966429665296662966729668296692967029671296722967329674296752967629677296782967929680296812968229683296842968529686296872968829689296902969129692296932969429695296962969729698296992970029701297022970329704297052970629707297082970929710297112971229713297142971529716297172971829719297202972129722297232972429725297262972729728297292973029731297322973329734297352973629737297382973929740297412974229743297442974529746297472974829749297502975129752297532975429755297562975729758297592976029761297622976329764297652976629767297682976929770297712977229773297742977529776297772977829779297802978129782297832978429785297862978729788297892979029791297922979329794297952979629797297982979929800298012980229803298042980529806298072980829809298102981129812298132981429815298162981729818298192982029821298222982329824298252982629827298282982929830298312983229833298342983529836298372983829839298402984129842298432984429845298462984729848298492985029851298522985329854298552985629857298582985929860298612986229863298642986529866298672986829869298702987129872298732987429875298762987729878298792988029881298822988329884298852988629887298882988929890298912989229893298942989529896298972989829899299002990129902299032990429905299062990729908299092991029911299122991329914299152991629917299182991929920299212992229923299242992529926299272992829929299302993129932299332993429935299362993729938299392994029941299422994329944299452994629947299482994929950299512995229953299542995529956299572995829959299602996129962299632996429965299662996729968299692997029971299722997329974299752997629977299782997929980299812998229983299842998529986299872998829989299902999129992299932999429995299962999729998299993000030001300023000330004300053000630007300083000930010300113001230013300143001530016300173001830019300203002130022300233002430025300263002730028300293003030031300323003330034300353003630037300383003930040300413004230043300443004530046300473004830049300503005130052300533005430055300563005730058300593006030061300623006330064300653006630067300683006930070300713007230073300743007530076300773007830079300803008130082300833008430085300863008730088300893009030091300923009330094300953009630097300983009930100301013010230103301043010530106301073010830109301103011130112301133011430115301163011730118301193012030121301223012330124301253012630127301283012930130301313013230133301343013530136301373013830139301403014130142301433014430145301463014730148301493015030151301523015330154301553015630157301583015930160301613016230163301643016530166301673016830169301703017130172301733017430175301763017730178301793018030181301823018330184301853018630187301883018930190301913019230193301943019530196301973019830199302003020130202302033020430205302063020730208302093021030211302123021330214302153021630217302183021930220302213022230223302243022530226302273022830229302303023130232302333023430235302363023730238302393024030241302423024330244302453024630247302483024930250302513025230253302543025530256302573025830259302603026130262302633026430265302663026730268302693027030271302723027330274302753027630277302783027930280302813028230283302843028530286302873028830289302903029130292302933029430295302963029730298302993030030301303023030330304303053030630307303083030930310303113031230313303143031530316303173031830319303203032130322303233032430325303263032730328303293033030331303323033330334303353033630337303383033930340303413034230343303443034530346303473034830349303503035130352303533035430355303563035730358303593036030361303623036330364303653036630367303683036930370303713037230373303743037530376303773037830379303803038130382303833038430385303863038730388303893039030391303923039330394303953039630397303983039930400304013040230403304043040530406304073040830409304103041130412304133041430415304163041730418304193042030421304223042330424304253042630427304283042930430304313043230433304343043530436304373043830439304403044130442304433044430445304463044730448304493045030451304523045330454304553045630457304583045930460304613046230463304643046530466304673046830469304703047130472304733047430475304763047730478304793048030481304823048330484304853048630487304883048930490304913049230493304943049530496304973049830499305003050130502305033050430505305063050730508305093051030511305123051330514305153051630517305183051930520305213052230523305243052530526305273052830529305303053130532305333053430535305363053730538305393054030541305423054330544305453054630547305483054930550305513055230553305543055530556305573055830559305603056130562305633056430565305663056730568305693057030571305723057330574305753057630577305783057930580305813058230583305843058530586305873058830589305903059130592305933059430595305963059730598305993060030601306023060330604306053060630607306083060930610306113061230613306143061530616306173061830619306203062130622306233062430625306263062730628306293063030631306323063330634306353063630637306383063930640306413064230643306443064530646306473064830649306503065130652306533065430655306563065730658306593066030661306623066330664306653066630667306683066930670306713067230673306743067530676306773067830679306803068130682306833068430685306863068730688306893069030691306923069330694306953069630697306983069930700307013070230703307043070530706307073070830709307103071130712307133071430715307163071730718307193072030721307223072330724307253072630727307283072930730307313073230733307343073530736307373073830739307403074130742307433074430745307463074730748307493075030751307523075330754307553075630757307583075930760307613076230763307643076530766307673076830769307703077130772307733077430775307763077730778307793078030781307823078330784307853078630787307883078930790307913079230793307943079530796307973079830799308003080130802308033080430805308063080730808308093081030811308123081330814308153081630817308183081930820308213082230823308243082530826308273082830829308303083130832308333083430835308363083730838308393084030841308423084330844308453084630847308483084930850308513085230853308543085530856308573085830859308603086130862308633086430865308663086730868308693087030871308723087330874308753087630877308783087930880308813088230883308843088530886308873088830889308903089130892308933089430895308963089730898308993090030901309023090330904309053090630907309083090930910309113091230913309143091530916309173091830919309203092130922309233092430925309263092730928309293093030931309323093330934309353093630937309383093930940309413094230943309443094530946309473094830949309503095130952309533095430955309563095730958309593096030961309623096330964309653096630967309683096930970309713097230973309743097530976309773097830979309803098130982309833098430985309863098730988309893099030991309923099330994309953099630997309983099931000310013100231003310043100531006310073100831009310103101131012310133101431015310163101731018310193102031021310223102331024310253102631027310283102931030310313103231033310343103531036310373103831039310403104131042310433104431045310463104731048310493105031051310523105331054310553105631057310583105931060310613106231063310643106531066310673106831069310703107131072310733107431075310763107731078310793108031081310823108331084310853108631087310883108931090310913109231093310943109531096310973109831099311003110131102311033110431105311063110731108311093111031111311123111331114311153111631117311183111931120311213112231123311243112531126311273112831129311303113131132311333113431135311363113731138311393114031141311423114331144311453114631147311483114931150311513115231153311543115531156311573115831159311603116131162311633116431165311663116731168311693117031171311723117331174311753117631177311783117931180311813118231183311843118531186311873118831189311903119131192311933119431195311963119731198311993120031201312023120331204312053120631207312083120931210312113121231213312143121531216312173121831219312203122131222312233122431225312263122731228312293123031231312323123331234312353123631237312383123931240312413124231243312443124531246312473124831249312503125131252312533125431255312563125731258312593126031261312623126331264312653126631267312683126931270312713127231273312743127531276312773127831279312803128131282312833128431285312863128731288312893129031291312923129331294312953129631297312983129931300313013130231303313043130531306313073130831309313103131131312313133131431315313163131731318313193132031321313223132331324313253132631327313283132931330313313133231333313343133531336313373133831339313403134131342313433134431345313463134731348313493135031351313523135331354313553135631357313583135931360313613136231363313643136531366313673136831369313703137131372313733137431375313763137731378313793138031381313823138331384313853138631387313883138931390313913139231393313943139531396313973139831399314003140131402314033140431405314063140731408314093141031411314123141331414314153141631417314183141931420314213142231423314243142531426314273142831429314303143131432314333143431435314363143731438314393144031441314423144331444314453144631447314483144931450314513145231453314543145531456314573145831459314603146131462314633146431465314663146731468314693147031471314723147331474314753147631477314783147931480314813148231483314843148531486314873148831489314903149131492314933149431495314963149731498314993150031501315023150331504315053150631507315083150931510315113151231513315143151531516315173151831519315203152131522315233152431525315263152731528315293153031531315323153331534315353153631537315383153931540315413154231543315443154531546315473154831549315503155131552315533155431555315563155731558315593156031561315623156331564315653156631567315683156931570315713157231573315743157531576315773157831579315803158131582315833158431585315863158731588315893159031591315923159331594315953159631597315983159931600316013160231603316043160531606316073160831609316103161131612316133161431615316163161731618316193162031621316223162331624316253162631627316283162931630316313163231633316343163531636316373163831639316403164131642316433164431645316463164731648316493165031651316523165331654316553165631657316583165931660316613166231663316643166531666316673166831669316703167131672316733167431675316763167731678316793168031681316823168331684316853168631687316883168931690316913169231693316943169531696316973169831699317003170131702317033170431705317063170731708317093171031711317123171331714317153171631717317183171931720317213172231723317243172531726317273172831729317303173131732317333173431735317363173731738317393174031741317423174331744317453174631747317483174931750317513175231753317543175531756317573175831759317603176131762317633176431765317663176731768317693177031771317723177331774317753177631777317783177931780317813178231783317843178531786317873178831789317903179131792317933179431795317963179731798317993180031801318023180331804318053180631807318083180931810318113181231813318143181531816318173181831819318203182131822318233182431825318263182731828318293183031831318323183331834318353183631837318383183931840318413184231843318443184531846318473184831849318503185131852318533185431855318563185731858318593186031861318623186331864318653186631867318683186931870318713187231873318743187531876318773187831879318803188131882318833188431885318863188731888318893189031891318923189331894318953189631897318983189931900319013190231903319043190531906319073190831909319103191131912319133191431915319163191731918319193192031921319223192331924319253192631927319283192931930319313193231933319343193531936319373193831939319403194131942319433194431945319463194731948319493195031951319523195331954319553195631957319583195931960319613196231963319643196531966319673196831969319703197131972319733197431975319763197731978319793198031981319823198331984319853198631987319883198931990319913199231993319943199531996319973199831999320003200132002320033200432005320063200732008320093201032011320123201332014320153201632017320183201932020320213202232023320243202532026320273202832029320303203132032320333203432035320363203732038320393204032041320423204332044320453204632047320483204932050320513205232053320543205532056320573205832059320603206132062320633206432065320663206732068320693207032071320723207332074320753207632077320783207932080320813208232083320843208532086320873208832089320903209132092320933209432095320963209732098320993210032101321023210332104321053210632107321083210932110321113211232113321143211532116321173211832119321203212132122321233212432125321263212732128321293213032131321323213332134321353213632137321383213932140321413214232143321443214532146321473214832149321503215132152321533215432155321563215732158321593216032161321623216332164321653216632167321683216932170321713217232173321743217532176321773217832179321803218132182321833218432185321863218732188321893219032191321923219332194321953219632197321983219932200322013220232203322043220532206322073220832209322103221132212322133221432215322163221732218322193222032221322223222332224322253222632227322283222932230322313223232233322343223532236322373223832239322403224132242322433224432245322463224732248322493225032251322523225332254322553225632257322583225932260322613226232263322643226532266322673226832269322703227132272322733227432275322763227732278322793228032281322823228332284322853228632287322883228932290322913229232293322943229532296322973229832299323003230132302323033230432305323063230732308323093231032311323123231332314323153231632317323183231932320323213232232323323243232532326323273232832329323303233132332323333233432335323363233732338323393234032341323423234332344323453234632347323483234932350323513235232353323543235532356323573235832359323603236132362323633236432365323663236732368323693237032371323723237332374323753237632377323783237932380323813238232383323843238532386323873238832389323903239132392323933239432395323963239732398323993240032401324023240332404324053240632407324083240932410324113241232413324143241532416324173241832419324203242132422324233242432425324263242732428324293243032431324323243332434324353243632437324383243932440324413244232443324443244532446324473244832449324503245132452324533245432455324563245732458324593246032461324623246332464324653246632467324683246932470324713247232473324743247532476324773247832479324803248132482324833248432485324863248732488324893249032491324923249332494324953249632497324983249932500325013250232503325043250532506325073250832509325103251132512325133251432515325163251732518325193252032521325223252332524325253252632527325283252932530325313253232533325343253532536325373253832539325403254132542325433254432545325463254732548325493255032551325523255332554325553255632557325583255932560325613256232563325643256532566325673256832569325703257132572325733257432575325763257732578325793258032581325823258332584325853258632587325883258932590325913259232593325943259532596325973259832599326003260132602326033260432605326063260732608326093261032611326123261332614326153261632617326183261932620326213262232623326243262532626326273262832629326303263132632326333263432635326363263732638326393264032641326423264332644326453264632647326483264932650326513265232653326543265532656326573265832659326603266132662326633266432665326663266732668326693267032671326723267332674326753267632677326783267932680326813268232683326843268532686326873268832689326903269132692326933269432695326963269732698326993270032701327023270332704327053270632707327083270932710327113271232713327143271532716327173271832719327203272132722327233272432725327263272732728327293273032731327323273332734327353273632737327383273932740327413274232743327443274532746327473274832749327503275132752327533275432755327563275732758327593276032761327623276332764327653276632767327683276932770327713277232773327743277532776327773277832779327803278132782327833278432785327863278732788327893279032791327923279332794327953279632797327983279932800328013280232803328043280532806328073280832809328103281132812328133281432815328163281732818328193282032821328223282332824328253282632827328283282932830328313283232833328343283532836328373283832839328403284132842328433284432845328463284732848328493285032851328523285332854328553285632857328583285932860328613286232863328643286532866328673286832869328703287132872328733287432875328763287732878328793288032881328823288332884328853288632887328883288932890328913289232893328943289532896328973289832899329003290132902329033290432905329063290732908329093291032911329123291332914329153291632917329183291932920329213292232923329243292532926329273292832929329303293132932329333293432935329363293732938329393294032941329423294332944329453294632947329483294932950329513295232953329543295532956329573295832959329603296132962329633296432965329663296732968329693297032971329723297332974329753297632977329783297932980329813298232983329843298532986329873298832989329903299132992329933299432995329963299732998329993300033001330023300333004330053300633007330083300933010330113301233013330143301533016330173301833019330203302133022330233302433025330263302733028330293303033031330323303333034330353303633037330383303933040330413304233043330443304533046330473304833049330503305133052330533305433055330563305733058330593306033061330623306333064330653306633067330683306933070330713307233073330743307533076330773307833079330803308133082330833308433085330863308733088330893309033091330923309333094330953309633097330983309933100331013310233103331043310533106331073310833109331103311133112331133311433115331163311733118331193312033121331223312333124331253312633127331283312933130331313313233133331343313533136331373313833139331403314133142331433314433145331463314733148331493315033151331523315333154331553315633157331583315933160331613316233163331643316533166331673316833169331703317133172331733317433175331763317733178331793318033181331823318333184331853318633187331883318933190331913319233193331943319533196331973319833199332003320133202332033320433205332063320733208332093321033211332123321333214332153321633217332183321933220332213322233223332243322533226332273322833229332303323133232332333323433235332363323733238332393324033241332423324333244332453324633247332483324933250332513325233253332543325533256332573325833259332603326133262332633326433265332663326733268332693327033271332723327333274332753327633277332783327933280332813328233283332843328533286332873328833289332903329133292332933329433295332963329733298332993330033301333023330333304333053330633307333083330933310333113331233313333143331533316333173331833319333203332133322333233332433325333263332733328333293333033331333323333333334333353333633337333383333933340333413334233343333443334533346333473334833349333503335133352333533335433355333563335733358333593336033361333623336333364333653336633367333683336933370333713337233373333743337533376333773337833379333803338133382333833338433385333863338733388333893339033391333923339333394333953339633397333983339933400334013340233403334043340533406334073340833409334103341133412334133341433415334163341733418334193342033421334223342333424334253342633427334283342933430334313343233433334343343533436334373343833439334403344133442334433344433445334463344733448334493345033451334523345333454334553345633457334583345933460334613346233463334643346533466334673346833469334703347133472334733347433475334763347733478334793348033481334823348333484334853348633487334883348933490334913349233493334943349533496334973349833499335003350133502335033350433505335063350733508335093351033511335123351333514335153351633517335183351933520335213352233523335243352533526335273352833529335303353133532335333353433535335363353733538335393354033541335423354333544335453354633547335483354933550335513355233553335543355533556335573355833559335603356133562335633356433565335663356733568335693357033571335723357333574335753357633577335783357933580335813358233583335843358533586335873358833589335903359133592335933359433595335963359733598335993360033601336023360333604336053360633607336083360933610336113361233613336143361533616336173361833619336203362133622336233362433625336263362733628336293363033631336323363333634336353363633637336383363933640336413364233643336443364533646336473364833649336503365133652336533365433655336563365733658336593366033661336623366333664336653366633667336683366933670336713367233673336743367533676336773367833679336803368133682336833368433685336863368733688336893369033691336923369333694336953369633697336983369933700337013370233703337043370533706337073370833709337103371133712337133371433715337163371733718337193372033721337223372333724337253372633727337283372933730337313373233733337343373533736337373373833739337403374133742337433374433745337463374733748337493375033751337523375333754337553375633757337583375933760337613376233763337643376533766337673376833769337703377133772337733377433775337763377733778337793378033781337823378333784337853378633787337883378933790337913379233793337943379533796337973379833799338003380133802338033380433805338063380733808338093381033811338123381333814338153381633817338183381933820338213382233823338243382533826338273382833829338303383133832338333383433835338363383733838338393384033841338423384333844338453384633847338483384933850338513385233853338543385533856338573385833859338603386133862338633386433865338663386733868338693387033871338723387333874338753387633877338783387933880338813388233883338843388533886338873388833889338903389133892338933389433895338963389733898338993390033901339023390333904339053390633907339083390933910339113391233913339143391533916339173391833919339203392133922339233392433925339263392733928339293393033931339323393333934339353393633937339383393933940339413394233943339443394533946339473394833949339503395133952339533395433955339563395733958339593396033961339623396333964339653396633967339683396933970339713397233973339743397533976339773397833979339803398133982339833398433985339863398733988339893399033991339923399333994339953399633997339983399934000340013400234003340043400534006340073400834009340103401134012340133401434015340163401734018340193402034021340223402334024340253402634027340283402934030340313403234033340343403534036340373403834039340403404134042340433404434045340463404734048340493405034051340523405334054340553405634057340583405934060340613406234063340643406534066340673406834069340703407134072340733407434075340763407734078340793408034081340823408334084340853408634087340883408934090340913409234093340943409534096340973409834099341003410134102341033410434105341063410734108341093411034111341123411334114341153411634117341183411934120341213412234123341243412534126341273412834129341303413134132341333413434135341363413734138341393414034141341423414334144341453414634147341483414934150341513415234153341543415534156341573415834159341603416134162341633416434165341663416734168341693417034171341723417334174341753417634177341783417934180341813418234183341843418534186341873418834189341903419134192341933419434195341963419734198341993420034201342023420334204342053420634207342083420934210342113421234213342143421534216342173421834219342203422134222342233422434225342263422734228342293423034231342323423334234342353423634237342383423934240342413424234243342443424534246342473424834249342503425134252342533425434255342563425734258342593426034261342623426334264342653426634267342683426934270
  1. /*!
  2. * elFinder - file manager for web
  3. * Version 2.1.43 (2018-11-24)
  4. * http://elfinder.org
  5. *
  6. * Copyright 2009-2018, 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') || '',
  75. /**
  76. * Events namespace
  77. *
  78. * @type String
  79. **/
  80. namespace = 'elfinder-' + (id ? id : Math.random().toString().substr(2, 7)),
  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. beeper = $(document.createElement('audio')).hide().appendTo('body')[0],
  275. syncInterval,
  276. autoSyncStop = 0,
  277. uiCmdMapPrev = '',
  278. gcJobRes = null,
  279. open = function(data) {
  280. // NOTES: Do not touch data object
  281. var volumeid, contextmenu, emptyDirs = {}, stayDirs = {},
  282. rmClass, hashes, calc, gc, collapsed, prevcwd, sorterStr;
  283. if (self.api >= 2.1) {
  284. // support volume driver option `uiCmdMap`
  285. self.commandMap = (data.options.uiCmdMap && Object.keys(data.options.uiCmdMap).length)? data.options.uiCmdMap : {};
  286. if (uiCmdMapPrev !== JSON.stringify(self.commandMap)) {
  287. uiCmdMapPrev = JSON.stringify(self.commandMap);
  288. }
  289. } else {
  290. self.options.sync = 0;
  291. }
  292. if (data.init) {
  293. // init - reset cache
  294. files = {};
  295. ownFiles = {};
  296. } else {
  297. // remove only files from prev cwd
  298. // and collapsed directory (included 100+ directories) to empty for perfomance tune in DnD
  299. prevcwd = cwd;
  300. rmClass = 'elfinder-subtree-loaded ' + self.res('class', 'navexpand');
  301. collapsed = self.res('class', 'navcollapse');
  302. hashes = Object.keys(files);
  303. calc = function(i) {
  304. if (!files[i]) {
  305. return true;
  306. }
  307. var isDir = (files[i].mime === 'directory'),
  308. phash = files[i].phash,
  309. pnav;
  310. if (
  311. (!isDir
  312. || emptyDirs[phash]
  313. || (!stayDirs[phash]
  314. && $('#'+self.navHash2Id(files[i].hash)).is(':hidden')
  315. && $('#'+self.navHash2Id(phash)).next('.elfinder-navbar-subtree').children().length > 100
  316. )
  317. )
  318. && (isDir || phash !== cwd)
  319. && ! remember[i]
  320. ) {
  321. if (isDir && !emptyDirs[phash]) {
  322. emptyDirs[phash] = true;
  323. $('#'+self.navHash2Id(phash))
  324. .removeClass(rmClass)
  325. .next('.elfinder-navbar-subtree').empty();
  326. }
  327. deleteCache(files[i]);
  328. } else if (isDir) {
  329. stayDirs[phash] = true;
  330. }
  331. };
  332. gc = function() {
  333. if (hashes.length) {
  334. gcJobRes && gcJobRes._abort();
  335. gcJobRes = self.asyncJob(calc, hashes, {
  336. interval : 20,
  337. numPerOnce : 100
  338. }).done(function() {
  339. var hd = self.storage('hide') || {items: {}};
  340. if (Object.keys(hiddenFiles).length) {
  341. $.each(hiddenFiles, function(h) {
  342. if (!hd.items[h]) {
  343. delete hiddenFiles[h];
  344. }
  345. });
  346. }
  347. });
  348. }
  349. };
  350. self.trigger('filesgc').one('filesgc', function() {
  351. hashes = [];
  352. });
  353. self.one('opendone', function() {
  354. if (prevcwd !== cwd) {
  355. if (! node.data('lazycnt')) {
  356. gc();
  357. } else {
  358. self.one('lazydone', gc);
  359. }
  360. }
  361. });
  362. }
  363. self.sorters = {};
  364. cwd = data.cwd.hash;
  365. cache(data.files);
  366. if (!files[cwd]) {
  367. cache([data.cwd]);
  368. }
  369. // trigger event 'sorterupdate'
  370. sorterStr = JSON.stringify(self.sorters);
  371. if (prevSorterStr !== sorterStr) {
  372. self.trigger('sorterupdate');
  373. prevSorterStr = sorterStr;
  374. }
  375. self.lastDir(cwd);
  376. self.autoSync();
  377. },
  378. /**
  379. * Store info about files/dirs in "files" object.
  380. *
  381. * @param Array files
  382. * @param String data type
  383. * @return void
  384. **/
  385. cache = function(data, type) {
  386. var defsorter = { name: true, perm: true, date: true, size: true, kind: true },
  387. sorterChk = !self.sorters._checked,
  388. l = data.length,
  389. setSorter = function(file) {
  390. var f = file || {},
  391. sorters = [];
  392. $.each(self.sortRules, function(key) {
  393. if (defsorter[key] || typeof f[key] !== 'undefined' || (key === 'mode' && typeof f.perm !== 'undefined')) {
  394. sorters.push(key);
  395. }
  396. });
  397. self.sorters = self.arrayFlip(sorters, true);
  398. self.sorters._checked = true;
  399. },
  400. keeps = ['sizeInfo'],
  401. changedParents = {},
  402. hideData = self.storage('hide') || {},
  403. hides = hideData.items || {},
  404. f, i, keepProp, parents, hidden;
  405. for (i = 0; i < l; i++) {
  406. f = Object.assign({}, data[i]);
  407. hidden = (!hideData.show && hides[f.hash])? true : false;
  408. if (f.name && f.hash && f.mime) {
  409. if (!hidden) {
  410. if (sorterChk && f.phash === cwd) {
  411. setSorter(f);
  412. sorterChk = false;
  413. }
  414. if (f.phash && (type === 'add' || type === 'change')) {
  415. if (parents = self.parents(f.phash)) {
  416. $.each(parents, function() {
  417. changedParents[this] = true;
  418. });
  419. }
  420. }
  421. }
  422. if (files[f.hash]) {
  423. $.each(keeps, function() {
  424. if(files[f.hash][this] && ! f[this]) {
  425. f[this] = files[f.hash][this];
  426. }
  427. });
  428. if (f.sizeInfo && !f.size) {
  429. f.size = f.sizeInfo.size;
  430. }
  431. deleteCache(files[f.hash], true);
  432. }
  433. if (hides[f.hash]) {
  434. hiddenFiles[f.hash] = f;
  435. }
  436. if (hidden) {
  437. l--;
  438. data.splice(i--, 1);
  439. } else {
  440. files[f.hash] = f;
  441. if (f.mime === 'directory' && !ownFiles[f.hash]) {
  442. ownFiles[f.hash] = {};
  443. }
  444. if (f.phash) {
  445. if (!ownFiles[f.phash]) {
  446. ownFiles[f.phash] = {};
  447. }
  448. ownFiles[f.phash][f.hash] = true;
  449. }
  450. }
  451. }
  452. }
  453. // delete sizeInfo cache
  454. $.each(Object.keys(changedParents), function() {
  455. var target = files[this];
  456. if (target && target.sizeInfo) {
  457. delete target.sizeInfo;
  458. }
  459. });
  460. // for empty folder
  461. sorterChk && setSorter();
  462. },
  463. /**
  464. * Delete file object from files caches
  465. *
  466. * @param Array removed hashes
  467. * @return void
  468. */
  469. remove = function(removed) {
  470. var l = removed.length,
  471. roots = {},
  472. rm = function(hash) {
  473. var file = files[hash], i;
  474. if (file) {
  475. if (file.mime === 'directory') {
  476. if (roots[hash]) {
  477. delete self.roots[roots[hash]];
  478. }
  479. // restore stats of deleted root parent directory
  480. $.each(self.leafRoots, function(phash, roots) {
  481. var idx, pdir;
  482. if ((idx = $.inArray(hash, roots))!== -1) {
  483. if (roots.length === 1) {
  484. if ((pdir = Object.assign({}, files[phash])) && pdir._realStats) {
  485. $.each(pdir._realStats, function(k, v) {
  486. pdir[k] = v;
  487. });
  488. remove(files[phash]._realStats);
  489. self.change({ changed: [pdir] });
  490. }
  491. delete self.leafRoots[phash];
  492. } else {
  493. self.leafRoots[phash].splice(idx, 1);
  494. }
  495. }
  496. });
  497. if (self.searchStatus.state < 2) {
  498. $.each(files, function(h, f) {
  499. f.phash == hash && rm(h);
  500. });
  501. }
  502. }
  503. if (file.phash) {
  504. if (parents = self.parents(file.phash)) {
  505. $.each(parents, function() {
  506. changedParents[this] = true;
  507. });
  508. }
  509. }
  510. deleteCache(files[hash]);
  511. }
  512. },
  513. changedParents = {},
  514. parents;
  515. $.each(self.roots, function(k, v) {
  516. roots[v] = k;
  517. });
  518. while (l--) {
  519. rm(removed[l]);
  520. }
  521. // delete sizeInfo cache
  522. $.each(Object.keys(changedParents), function() {
  523. var target = files[this];
  524. if (target && target.sizeInfo) {
  525. delete target.sizeInfo;
  526. }
  527. });
  528. },
  529. /**
  530. * Update file object in files caches
  531. *
  532. * @param Array changed file objects
  533. * @return void
  534. */
  535. change = function(changed) {
  536. $.each(changed, function(i, file) {
  537. var hash = file.hash;
  538. if (files[hash]) {
  539. $.each(Object.keys(files[hash]), function(i, v){
  540. if (typeof file[v] === 'undefined') {
  541. delete files[hash][v];
  542. }
  543. });
  544. }
  545. files[hash] = files[hash] ? Object.assign(files[hash], file) : file;
  546. });
  547. },
  548. /**
  549. * Delete cache data of files, ownFiles and self.optionsByHashes
  550. *
  551. * @param Object file
  552. * @param Boolean update
  553. * @return void
  554. */
  555. deleteCache = function(file, update) {
  556. var hash = file.hash,
  557. phash = file.phash;
  558. if (phash && ownFiles[phash]) {
  559. delete ownFiles[phash][hash];
  560. }
  561. if (!update) {
  562. ownFiles[hash] && delete ownFiles[hash];
  563. self.optionsByHashes[hash] && delete self.optionsByHashes[hash];
  564. }
  565. delete files[hash];
  566. },
  567. /**
  568. * Maximum number of concurrent connections on request
  569. *
  570. * @type Number
  571. */
  572. requestMaxConn,
  573. /**
  574. * Current number of connections
  575. *
  576. * @type Number
  577. */
  578. requestCnt = 0,
  579. /**
  580. * Queue waiting for connection
  581. *
  582. * @type Array
  583. */
  584. requestQueue = [],
  585. /**
  586. * Flag to cancel the `open` command waiting for connection
  587. *
  588. * @type Boolean
  589. */
  590. requestQueueSkipOpen = false,
  591. /**
  592. * Exec shortcut
  593. *
  594. * @param jQuery.Event keydown/keypress event
  595. * @return void
  596. */
  597. execShortcut = function(e) {
  598. var code = e.keyCode,
  599. ctrlKey = !!(e.ctrlKey || e.metaKey),
  600. isMousedown = e.type === 'mousedown',
  601. ddm;
  602. !isMousedown && (self.keyState.keyCode = code);
  603. self.keyState.ctrlKey = ctrlKey;
  604. self.keyState.shiftKey = e.shiftKey;
  605. self.keyState.metaKey = e.metaKey;
  606. self.keyState.altKey = e.altKey;
  607. if (isMousedown) {
  608. return;
  609. } else if (e.type === 'keyup') {
  610. self.keyState.keyCode = null;
  611. return;
  612. }
  613. if (enabled) {
  614. $.each(shortcuts, function(i, shortcut) {
  615. if (shortcut.type == e.type
  616. && shortcut.keyCode == code
  617. && shortcut.shiftKey == e.shiftKey
  618. && shortcut.ctrlKey == ctrlKey
  619. && shortcut.altKey == e.altKey) {
  620. e.preventDefault();
  621. e.stopPropagation();
  622. shortcut.callback(e, self);
  623. self.debug('shortcut-exec', i+' : '+shortcut.description);
  624. }
  625. });
  626. // prevent tab out of elfinder
  627. if (code == $.ui.keyCode.TAB && !$(e.target).is(':input')) {
  628. e.preventDefault();
  629. }
  630. // cancel any actions by [Esc] key
  631. if (e.type === 'keydown' && code == $.ui.keyCode.ESCAPE) {
  632. // copy or cut
  633. if (! node.find('.ui-widget:visible').length) {
  634. self.clipboard().length && self.clipboard([]);
  635. }
  636. // dragging
  637. if ($.ui.ddmanager) {
  638. ddm = $.ui.ddmanager.current;
  639. ddm && ddm.helper && ddm.cancel();
  640. }
  641. // button menus
  642. self.toHide(node.find('.ui-widget.elfinder-button-menu.elfinder-frontmost:visible'));
  643. // trigger keydownEsc
  644. self.trigger('keydownEsc', e);
  645. }
  646. }
  647. },
  648. date = new Date(),
  649. utc,
  650. i18n,
  651. inFrame = (window.parent !== window),
  652. parentIframe = (function() {
  653. var pifm, ifms;
  654. if (inFrame) {
  655. try {
  656. ifms = $('iframe', window.parent.document);
  657. if (ifms.length) {
  658. $.each(ifms, function(i, ifm) {
  659. if (ifm.contentWindow === window) {
  660. pifm = $(ifm);
  661. return false;
  662. }
  663. });
  664. }
  665. } catch(e) {}
  666. }
  667. return pifm;
  668. })(),
  669. /**
  670. * elFinder boot up function
  671. *
  672. * @type Function
  673. */
  674. bootUp,
  675. /**
  676. * Original function of XMLHttpRequest.prototype.send
  677. *
  678. * @type Function
  679. */
  680. savedXhrSend;
  681. // opts must be an object
  682. if (!opts) {
  683. opts = {};
  684. }
  685. // set UA.Angle, UA.Rotated for mobile devices
  686. if (self.UA.Mobile) {
  687. $(window).on('orientationchange.'+namespace, function() {
  688. var a = ((screen && screen.orientation && screen.orientation.angle) || window.orientation || 0) + 0;
  689. if (a === -90) {
  690. a = 270;
  691. }
  692. self.UA.Angle = a;
  693. self.UA.Rotated = a % 180 === 0? false : true;
  694. }).trigger('orientationchange.'+namespace);
  695. }
  696. // check opt.bootCallback
  697. if (opts.bootCallback && typeof opts.bootCallback === 'function') {
  698. (function() {
  699. var func = bootCallback,
  700. opFunc = opts.bootCallback;
  701. bootCallback = function(fm, extraObj) {
  702. func && typeof func === 'function' && func.call(this, fm, extraObj);
  703. opFunc.call(this, fm, extraObj);
  704. };
  705. })();
  706. }
  707. delete opts.bootCallback;
  708. /**
  709. * Protocol version
  710. *
  711. * @type String
  712. **/
  713. this.api = null;
  714. /**
  715. * elFinder use new api
  716. *
  717. * @type Boolean
  718. **/
  719. this.newAPI = false;
  720. /**
  721. * elFinder use old api
  722. *
  723. * @type Boolean
  724. **/
  725. this.oldAPI = false;
  726. /**
  727. * Net drivers names
  728. *
  729. * @type Array
  730. **/
  731. this.netDrivers = [];
  732. /**
  733. * Base URL of elfFinder library starting from Manager HTML
  734. *
  735. * @type String
  736. */
  737. this.baseUrl = '';
  738. /**
  739. * Base URL of i18n js files
  740. * baseUrl + "js/i18n/" when empty value
  741. *
  742. * @type String
  743. */
  744. this.i18nBaseUrl = '';
  745. /**
  746. * Is elFinder CSS loaded
  747. *
  748. * @type Boolean
  749. */
  750. this.cssloaded = false;
  751. /**
  752. * Current theme object
  753. *
  754. * @type Object|Null
  755. */
  756. this.theme = null;
  757. this.mimesCanMakeEmpty = {};
  758. /**
  759. * Callback function at boot up that option specified at elFinder starting
  760. *
  761. * @type Function
  762. */
  763. this.bootCallback;
  764. /**
  765. * ID. Required to create unique cookie name
  766. *
  767. * @type String
  768. **/
  769. this.id = id;
  770. /**
  771. * Method to store/fetch data
  772. *
  773. * @type Function
  774. **/
  775. this.storage = (function() {
  776. try {
  777. if ('localStorage' in window && window['localStorage'] !== null) {
  778. if (self.UA.Safari) {
  779. // check for Mac/iOS safari private browsing mode
  780. window.localStorage.setItem('elfstoragecheck', 1);
  781. window.localStorage.removeItem('elfstoragecheck');
  782. }
  783. return self.localStorage;
  784. } else {
  785. return self.cookie;
  786. }
  787. } catch (e) {
  788. return self.cookie;
  789. }
  790. })();
  791. /**
  792. * Configuration options
  793. *
  794. * @type Object
  795. **/
  796. //this.options = $.extend(true, {}, this._options, opts);
  797. this.options = Object.assign({}, this._options);
  798. // for old type configuration
  799. if (opts.uiOptions) {
  800. if (opts.uiOptions.toolbar && Array.isArray(opts.uiOptions.toolbar)) {
  801. if ($.isPlainObject(opts.uiOptions.toolbar[opts.uiOptions.toolbar.length - 1])) {
  802. self.options.uiOptions.toolbarExtra = Object.assign(self.options.uiOptions.toolbarExtra || {}, opts.uiOptions.toolbar.pop());
  803. }
  804. }
  805. }
  806. // Overwrite if opts value is an array
  807. (function() {
  808. var arrOv = function(obj, base) {
  809. if ($.isPlainObject(obj)) {
  810. $.each(obj, function(k, v) {
  811. if ($.isPlainObject(v)) {
  812. if (!base[k]) {
  813. base[k] = {};
  814. }
  815. arrOv(v, base[k]);
  816. } else {
  817. base[k] = v;
  818. }
  819. });
  820. }
  821. };
  822. arrOv(opts, self.options);
  823. })();
  824. // join toolbarExtra to toolbar
  825. this.options.uiOptions.toolbar.push(this.options.uiOptions.toolbarExtra);
  826. delete this.options.uiOptions.toolbarExtra;
  827. // set fm.baseUrl
  828. this.baseUrl = (function() {
  829. var myTag, myCss, base, baseUrl;
  830. if (self.options.baseUrl) {
  831. return self.options.baseUrl;
  832. } else {
  833. baseUrl = '';
  834. //myTag = $('head > script[src$="js/elfinder.min.js"],script[src$="js/elfinder.full.js"]:first');
  835. myTag = null;
  836. $('head > script').each(function() {
  837. if (this.src && this.src.match(/js\/elfinder(?:-[a-z0-9_-]+)?\.(?:min|full)\.js$/i)) {
  838. myTag = $(this);
  839. return false;
  840. }
  841. });
  842. if (myTag) {
  843. myCss = $('head > link[href$="css/elfinder.min.css"],link[href$="css/elfinder.full.css"]:first').length;
  844. if (! myCss) {
  845. // to request CSS auto loading
  846. self.cssloaded = null;
  847. }
  848. baseUrl = myTag.attr('src').replace(/js\/[^\/]+$/, '');
  849. if (! baseUrl.match(/^(https?\/\/|\/)/)) {
  850. // check <base> tag
  851. if (base = $('head > base[href]').attr('href')) {
  852. baseUrl = base.replace(/\/$/, '') + '/' + baseUrl;
  853. }
  854. }
  855. }
  856. if (baseUrl !== '') {
  857. self.options.baseUrl = baseUrl;
  858. } else {
  859. if (! self.options.baseUrl) {
  860. self.options.baseUrl = './';
  861. }
  862. baseUrl = self.options.baseUrl;
  863. }
  864. return baseUrl;
  865. }
  866. })();
  867. this.i18nBaseUrl = (this.options.i18nBaseUrl || this.baseUrl + 'js/i18n').replace(/\/$/, '') + '/';
  868. // set dispInlineRegex
  869. cwdOptionsDefault['dispInlineRegex'] = this.options.dispInlineRegex;
  870. // auto load required CSS
  871. if (this.options.cssAutoLoad) {
  872. (function() {
  873. var baseUrl = self.baseUrl;
  874. if (self.cssloaded === null) {
  875. // hide elFinder node while css loading
  876. node.data('cssautoloadHide', $('<style>.elfinder{visibility:hidden;overflow:hidden}</style>'));
  877. $('head').append(node.data('cssautoloadHide'));
  878. // load CSS
  879. self.loadCss([baseUrl+'css/elfinder.min.css', baseUrl+'css/theme.css'], {
  880. dfd: $.Deferred().always(function() {
  881. if (node.data('cssautoloadHide')) {
  882. node.data('cssautoloadHide').remove();
  883. node.removeData('cssautoloadHide');
  884. }
  885. }).done(function() {
  886. if (!self.cssloaded) {
  887. self.cssloaded = true;
  888. self.trigger('cssloaded');
  889. }
  890. }).fail(function() {
  891. self.cssloaded = false;
  892. self.error(['errRead', 'CSS (elfinder or theme)']);
  893. })
  894. });
  895. // additional CSS files
  896. if (Array.isArray(self.options.cssAutoLoad)) {
  897. self.loadCss(self.options.cssAutoLoad);
  898. }
  899. }
  900. self.options.cssAutoLoad = false;
  901. })();
  902. }
  903. // load theme if exists
  904. self.changeTheme(self.storage('theme') || self.options.theme);
  905. /**
  906. * Volume option to set the properties of the root Stat
  907. *
  908. * @type Object
  909. */
  910. this.optionProperties = {
  911. icon: void(0),
  912. csscls: void(0),
  913. tmbUrl: void(0),
  914. uiCmdMap: {},
  915. netkey: void(0),
  916. disabled: []
  917. };
  918. if (! inFrame && ! this.options.enableAlways && $('body').children().length === 2) { // only node and beeper
  919. this.options.enableAlways = true;
  920. }
  921. // make options.debug
  922. if (this.options.debug === true) {
  923. this.options.debug = 'all';
  924. } else if (Array.isArray(this.options.debug)) {
  925. (function() {
  926. var d = {};
  927. $.each(self.options.debug, function() {
  928. d[this] = true;
  929. });
  930. self.options.debug = d;
  931. })();
  932. } else {
  933. this.options.debug = false;
  934. }
  935. /**
  936. * Original functions evacuated by conflict check
  937. *
  938. * @type Object
  939. */
  940. this.noConflicts = {};
  941. /**
  942. * Check and save conflicts with bootstrap etc
  943. *
  944. * @type Function
  945. */
  946. this.noConflict = function() {
  947. $.each(conflictChecks, function(i, p) {
  948. if ($.fn[p] && typeof $.fn[p].noConflict === 'function') {
  949. self.noConflicts[p] = $.fn[p].noConflict();
  950. }
  951. });
  952. };
  953. // do check conflict
  954. this.noConflict();
  955. /**
  956. * Is elFinder over CORS
  957. *
  958. * @type Boolean
  959. **/
  960. this.isCORS = false;
  961. // configure for CORS
  962. (function(){
  963. if (typeof self.options.cors !== 'undefined' && self.options.cors !== null) {
  964. self.isCORS = self.options.cors? true : false;
  965. } else {
  966. var parseUrl = document.createElement('a'),
  967. parseUploadUrl,
  968. selfProtocol = window.location.protocol,
  969. portReg = function(protocol) {
  970. protocol = (!protocol || protocol === ':')? selfProtocol : protocol;
  971. return protocol === 'https:'? /\:443$/ : /\:80$/;
  972. },
  973. selfHost = window.location.host.replace(portReg(selfProtocol), '');
  974. parseUrl.href = opts.url;
  975. if (opts.urlUpload && (opts.urlUpload !== opts.url)) {
  976. parseUploadUrl = document.createElement('a');
  977. parseUploadUrl.href = opts.urlUpload;
  978. }
  979. if (selfHost !== parseUrl.host.replace(portReg(parseUrl.protocol), '')
  980. || (parseUrl.protocol !== ':'&& parseUrl.protocol !== '' && (selfProtocol !== parseUrl.protocol))
  981. || (parseUploadUrl &&
  982. (selfHost !== parseUploadUrl.host.replace(portReg(parseUploadUrl.protocol), '')
  983. || (parseUploadUrl.protocol !== ':' && parseUploadUrl.protocol !== '' && (selfProtocol !== parseUploadUrl.protocol))
  984. )
  985. )
  986. ) {
  987. self.isCORS = true;
  988. }
  989. }
  990. if (self.isCORS) {
  991. if (!$.isPlainObject(self.options.customHeaders)) {
  992. self.options.customHeaders = {};
  993. }
  994. if (!$.isPlainObject(self.options.xhrFields)) {
  995. self.options.xhrFields = {};
  996. }
  997. self.options.requestType = 'post';
  998. self.options.customHeaders['X-Requested-With'] = 'XMLHttpRequest';
  999. self.options.xhrFields['withCredentials'] = true;
  1000. }
  1001. })();
  1002. /**
  1003. * Ajax request type
  1004. *
  1005. * @type String
  1006. * @default "get"
  1007. **/
  1008. this.requestType = /^(get|post)$/i.test(this.options.requestType) ? this.options.requestType.toLowerCase() : 'get';
  1009. // set `requestMaxConn` by option
  1010. requestMaxConn = Math.max(parseInt(this.options.requestMaxConn), 1);
  1011. /**
  1012. * Custom data that given as options
  1013. *
  1014. * @type Object
  1015. * @default {}
  1016. */
  1017. this.optsCustomData = $.isPlainObject(this.options.customData) ? this.options.customData : {};
  1018. /**
  1019. * Any data to send across every ajax request
  1020. *
  1021. * @type Object
  1022. * @default {}
  1023. **/
  1024. this.customData = Object.assign({}, this.optsCustomData);
  1025. /**
  1026. * Previous custom data from connector
  1027. *
  1028. * @type Object|null
  1029. */
  1030. this.prevCustomData = null;
  1031. /**
  1032. * Any custom headers to send across every ajax request
  1033. *
  1034. * @type Object
  1035. * @default {}
  1036. */
  1037. this.customHeaders = $.isPlainObject(this.options.customHeaders) ? this.options.customHeaders : {};
  1038. /**
  1039. * Any custom xhrFields to send across every ajax request
  1040. *
  1041. * @type Object
  1042. * @default {}
  1043. */
  1044. this.xhrFields = $.isPlainObject(this.options.xhrFields) ? this.options.xhrFields : {};
  1045. /**
  1046. * Replace XMLHttpRequest.prototype.send to extended function for 3rd party libs XHR request etc.
  1047. *
  1048. * @type Function
  1049. */
  1050. this.replaceXhrSend = function() {
  1051. if (! savedXhrSend) {
  1052. savedXhrSend = XMLHttpRequest.prototype.send;
  1053. }
  1054. XMLHttpRequest.prototype.send = function() {
  1055. var xhr = this;
  1056. // set request headers
  1057. if (self.customHeaders) {
  1058. $.each(self.customHeaders, function(key) {
  1059. xhr.setRequestHeader(key, this);
  1060. });
  1061. }
  1062. // set xhrFields
  1063. if (self.xhrFields) {
  1064. $.each(self.xhrFields, function(key) {
  1065. if (key in xhr) {
  1066. xhr[key] = this;
  1067. }
  1068. });
  1069. }
  1070. return savedXhrSend.apply(this, arguments);
  1071. };
  1072. };
  1073. /**
  1074. * Restore saved original XMLHttpRequest.prototype.send
  1075. *
  1076. * @type Function
  1077. */
  1078. this.restoreXhrSend = function() {
  1079. savedXhrSend && (XMLHttpRequest.prototype.send = savedXhrSend);
  1080. };
  1081. /**
  1082. * command names for into queue for only cwd requests
  1083. * these commands aborts before `open` request
  1084. *
  1085. * @type Array
  1086. * @default ['tmb', 'parents']
  1087. */
  1088. this.abortCmdsOnOpen = this.options.abortCmdsOnOpen || ['tmb', 'parents'];
  1089. /**
  1090. * ui.nav id prefix
  1091. *
  1092. * @type String
  1093. */
  1094. this.navPrefix = 'nav' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-';
  1095. /**
  1096. * ui.cwd id prefix
  1097. *
  1098. * @type String
  1099. */
  1100. this.cwdPrefix = elFinder.prototype.uniqueid? ('cwd' + elFinder.prototype.uniqueid + '-') : '';
  1101. // Increment elFinder.prototype.uniqueid
  1102. ++elFinder.prototype.uniqueid;
  1103. /**
  1104. * URL to upload files
  1105. *
  1106. * @type String
  1107. **/
  1108. this.uploadURL = opts.urlUpload || opts.url;
  1109. /**
  1110. * Events namespace
  1111. *
  1112. * @type String
  1113. **/
  1114. this.namespace = namespace;
  1115. /**
  1116. * Today timestamp
  1117. *
  1118. * @type Number
  1119. **/
  1120. this.today = (new Date(date.getFullYear(), date.getMonth(), date.getDate())).getTime()/1000;
  1121. /**
  1122. * Yesterday timestamp
  1123. *
  1124. * @type Number
  1125. **/
  1126. this.yesterday = this.today - 86400;
  1127. utc = this.options.UTCDate ? 'UTC' : '';
  1128. this.getHours = 'get'+utc+'Hours';
  1129. this.getMinutes = 'get'+utc+'Minutes';
  1130. this.getSeconds = 'get'+utc+'Seconds';
  1131. this.getDate = 'get'+utc+'Date';
  1132. this.getDay = 'get'+utc+'Day';
  1133. this.getMonth = 'get'+utc+'Month';
  1134. this.getFullYear = 'get'+utc+'FullYear';
  1135. /**
  1136. * elFinder node z-index (auto detect on elFinder load)
  1137. *
  1138. * @type null | Number
  1139. **/
  1140. this.zIndex;
  1141. /**
  1142. * Current search status
  1143. *
  1144. * @type Object
  1145. */
  1146. this.searchStatus = {
  1147. state : 0, // 0: search ended, 1: search started, 2: in search result
  1148. query : '',
  1149. target : '',
  1150. mime : '',
  1151. mixed : false, // in multi volumes search: false or Array that target volume ids
  1152. ininc : false // in incremental search
  1153. };
  1154. /**
  1155. * Interface language
  1156. *
  1157. * @type String
  1158. * @default "en"
  1159. **/
  1160. this.lang = this.storage('lang') || this.options.lang;
  1161. if (this.lang === 'jp') {
  1162. this.lang = this.options.lang = 'ja';
  1163. }
  1164. this.viewType = this.storage('view') || this.options.defaultView || 'icons';
  1165. this.sortType = this.storage('sortType') || this.options.sortType || 'name';
  1166. this.sortOrder = this.storage('sortOrder') || this.options.sortOrder || 'asc';
  1167. this.sortStickFolders = this.storage('sortStickFolders');
  1168. if (this.sortStickFolders === null) {
  1169. this.sortStickFolders = !!this.options.sortStickFolders;
  1170. } else {
  1171. this.sortStickFolders = !!this.sortStickFolders;
  1172. }
  1173. this.sortAlsoTreeview = this.storage('sortAlsoTreeview');
  1174. if (this.sortAlsoTreeview === null) {
  1175. this.sortAlsoTreeview = !!this.options.sortAlsoTreeview;
  1176. } else {
  1177. this.sortAlsoTreeview = !!this.sortAlsoTreeview;
  1178. }
  1179. this.sortRules = $.extend(true, {}, this._sortRules, this.options.sortRules);
  1180. $.each(this.sortRules, function(name, method) {
  1181. if (typeof method != 'function') {
  1182. delete self.sortRules[name];
  1183. }
  1184. });
  1185. this.compare = $.proxy(this.compare, this);
  1186. /**
  1187. * Delay in ms before open notification dialog
  1188. *
  1189. * @type Number
  1190. * @default 500
  1191. **/
  1192. this.notifyDelay = this.options.notifyDelay > 0 ? parseInt(this.options.notifyDelay) : 500;
  1193. /**
  1194. * Dragging UI Helper object
  1195. *
  1196. * @type jQuery | null
  1197. **/
  1198. this.draggingUiHelper = null;
  1199. /**
  1200. * Base droppable options
  1201. *
  1202. * @type Object
  1203. **/
  1204. this.droppable = {
  1205. greedy : true,
  1206. tolerance : 'pointer',
  1207. accept : '.elfinder-cwd-file-wrapper,.elfinder-navbar-dir,.elfinder-cwd-file,.elfinder-cwd-filename',
  1208. hoverClass : this.res('class', 'adroppable'),
  1209. classes : { // Deprecated hoverClass jQueryUI>=1.12.0
  1210. 'ui-droppable-hover': this.res('class', 'adroppable')
  1211. },
  1212. autoDisable: true, // elFinder original, see jquery.elfinder.js
  1213. drop : function(e, ui) {
  1214. var dst = $(this),
  1215. targets = $.grep(ui.helper.data('files')||[], function(h) { return h? true : false; }),
  1216. result = [],
  1217. dups = [],
  1218. faults = [],
  1219. isCopy = ui.helper.hasClass('elfinder-drag-helper-plus'),
  1220. c = 'class',
  1221. cnt, hash, i, h;
  1222. if (typeof e.button === 'undefined' || ui.helper.data('namespace') !== namespace || ! self.insideWorkzone(e.pageX, e.pageY)) {
  1223. return false;
  1224. }
  1225. if (dst.hasClass(self.res(c, 'cwdfile'))) {
  1226. hash = self.cwdId2Hash(dst.attr('id'));
  1227. } else if (dst.hasClass(self.res(c, 'navdir'))) {
  1228. hash = self.navId2Hash(dst.attr('id'));
  1229. } else {
  1230. hash = cwd;
  1231. }
  1232. cnt = targets.length;
  1233. while (cnt--) {
  1234. h = targets[cnt];
  1235. // ignore drop into itself or in own location
  1236. if (h != hash && files[h].phash != hash) {
  1237. result.push(h);
  1238. } else {
  1239. ((isCopy && h !== hash && files[hash].write)? dups : faults).push(h);
  1240. }
  1241. }
  1242. if (faults.length) {
  1243. return false;
  1244. }
  1245. ui.helper.data('droped', true);
  1246. if (dups.length) {
  1247. ui.helper.hide();
  1248. self.exec('duplicate', dups, {_userAction: true});
  1249. }
  1250. if (result.length) {
  1251. ui.helper.hide();
  1252. self.clipboard(result, !isCopy);
  1253. self.exec('paste', hash, {_userAction: true}, hash).always(function(){
  1254. self.clipboard([]);
  1255. self.trigger('unlockfiles', {files : targets});
  1256. });
  1257. self.trigger('drop', {files : targets});
  1258. }
  1259. }
  1260. };
  1261. /**
  1262. * Return true if filemanager is active
  1263. *
  1264. * @return Boolean
  1265. **/
  1266. this.enabled = function() {
  1267. return enabled && this.visible();
  1268. };
  1269. /**
  1270. * Return true if filemanager is visible
  1271. *
  1272. * @return Boolean
  1273. **/
  1274. this.visible = function() {
  1275. return node[0].elfinder && node.is(':visible');
  1276. };
  1277. /**
  1278. * Return file is root?
  1279. *
  1280. * @param Object target file object
  1281. * @return Boolean
  1282. */
  1283. this.isRoot = function(file) {
  1284. return (file.isroot || ! file.phash)? true : false;
  1285. };
  1286. /**
  1287. * Return root dir hash for current working directory
  1288. *
  1289. * @param String target hash
  1290. * @param Boolean include fake parent (optional)
  1291. * @return String
  1292. */
  1293. this.root = function(hash, fake) {
  1294. hash = hash || cwd;
  1295. var dir, i;
  1296. if (! fake) {
  1297. $.each(self.roots, function(id, rhash) {
  1298. if (hash.indexOf(id) === 0) {
  1299. dir = rhash;
  1300. return false;
  1301. }
  1302. });
  1303. if (dir) {
  1304. return dir;
  1305. }
  1306. }
  1307. dir = files[hash];
  1308. while (dir && dir.phash && (fake || ! dir.isroot)) {
  1309. dir = files[dir.phash];
  1310. }
  1311. if (dir) {
  1312. return dir.hash;
  1313. }
  1314. while (i in files && files.hasOwnProperty(i)) {
  1315. dir = files[i];
  1316. if (dir.mime === 'directory' && !dir.phash && dir.read) {
  1317. return dir.hash;
  1318. }
  1319. }
  1320. return '';
  1321. };
  1322. /**
  1323. * Return current working directory info
  1324. *
  1325. * @return Object
  1326. */
  1327. this.cwd = function() {
  1328. return files[cwd] || {};
  1329. };
  1330. /**
  1331. * Return required cwd option
  1332. *
  1333. * @param String option name
  1334. * @param String target hash (optional)
  1335. * @return mixed
  1336. */
  1337. this.option = function(name, target) {
  1338. var res, item;
  1339. target = target || cwd;
  1340. if (self.optionsByHashes[target] && typeof self.optionsByHashes[target][name] !== 'undefined') {
  1341. return self.optionsByHashes[target][name];
  1342. }
  1343. if (self.hasVolOptions && cwd !== target && (!(item = self.file(target)) || item.phash !== cwd)) {
  1344. res = '';
  1345. $.each(self.volOptions, function(id, opt) {
  1346. if (target.indexOf(id) === 0) {
  1347. res = opt[name] || '';
  1348. return false;
  1349. }
  1350. });
  1351. return res;
  1352. } else {
  1353. return cwdOptions[name] || '';
  1354. }
  1355. };
  1356. /**
  1357. * Return disabled commands by each folder
  1358. *
  1359. * @param Array target hashes
  1360. * @return Array
  1361. */
  1362. this.getDisabledCmds = function(targets, flip) {
  1363. var disabled = {'hidden': true};
  1364. if (! Array.isArray(targets)) {
  1365. targets = [ targets ];
  1366. }
  1367. $.each(targets, function(i, h) {
  1368. var disCmds = self.option('disabledFlip', h);
  1369. if (disCmds) {
  1370. Object.assign(disabled, disCmds);
  1371. }
  1372. });
  1373. return flip? disabled : Object.keys(disabled);
  1374. };
  1375. /**
  1376. * Return file data from current dir or tree by it's hash
  1377. *
  1378. * @param String file hash
  1379. * @return Object
  1380. */
  1381. this.file = function(hash, alsoHidden) {
  1382. return hash? (files[hash] || (alsoHidden? hiddenFiles[hash] : void(0))) : void(0);
  1383. };
  1384. /**
  1385. * Return all cached files
  1386. *
  1387. * @param String parent hash
  1388. * @return Object
  1389. */
  1390. this.files = function(phash) {
  1391. var items = {};
  1392. if (phash) {
  1393. if (!ownFiles[phash]) {
  1394. return {};
  1395. }
  1396. $.each(ownFiles[phash], function(h) {
  1397. if (files[h]) {
  1398. items[h] = files[h];
  1399. } else {
  1400. delete ownFiles[phash][h];
  1401. }
  1402. });
  1403. return Object.assign({}, items);
  1404. }
  1405. return Object.assign({}, files);
  1406. };
  1407. /**
  1408. * Return list of file parents hashes include file hash
  1409. *
  1410. * @param String file hash
  1411. * @return Array
  1412. */
  1413. this.parents = function(hash) {
  1414. var parents = [],
  1415. dir;
  1416. while (hash && (dir = this.file(hash))) {
  1417. parents.unshift(dir.hash);
  1418. hash = dir.phash;
  1419. }
  1420. return parents;
  1421. };
  1422. this.path2array = function(hash, i18) {
  1423. var file,
  1424. path = [];
  1425. while (hash) {
  1426. if ((file = files[hash]) && file.hash) {
  1427. path.unshift(i18 && file.i18 ? file.i18 : file.name);
  1428. hash = file.isroot? null : file.phash;
  1429. } else {
  1430. path = [];
  1431. break;
  1432. }
  1433. }
  1434. return path;
  1435. };
  1436. /**
  1437. * Return file path or Get path async with jQuery.Deferred
  1438. *
  1439. * @param Object file
  1440. * @param Boolean i18
  1441. * @param Object asyncOpt
  1442. * @return String|jQuery.Deferred
  1443. */
  1444. this.path = function(hash, i18, asyncOpt) {
  1445. var path = files[hash] && files[hash].path
  1446. ? files[hash].path
  1447. : this.path2array(hash, i18).join(cwdOptions.separator);
  1448. if (! asyncOpt || ! files[hash]) {
  1449. return path;
  1450. } else {
  1451. asyncOpt = Object.assign({notify: {type : 'parents', cnt : 1, hideCnt : true}}, asyncOpt);
  1452. var dfd = $.Deferred(),
  1453. notify = asyncOpt.notify,
  1454. noreq = false,
  1455. req = function() {
  1456. self.request({
  1457. data : {cmd : 'parents', target : files[hash].phash},
  1458. notify : notify,
  1459. preventFail : true
  1460. })
  1461. .done(done)
  1462. .fail(function() {
  1463. dfd.reject();
  1464. });
  1465. },
  1466. done = function() {
  1467. self.one('parentsdone', function() {
  1468. path = self.path(hash, i18);
  1469. if (path === '' && noreq) {
  1470. //retry with request
  1471. noreq = false;
  1472. req();
  1473. } else {
  1474. if (notify) {
  1475. clearTimeout(ntftm);
  1476. notify.cnt = -(parseInt(notify.cnt || 0));
  1477. self.notify(notify);
  1478. }
  1479. dfd.resolve(path);
  1480. }
  1481. });
  1482. },
  1483. ntftm;
  1484. if (path) {
  1485. return dfd.resolve(path);
  1486. } else {
  1487. if (self.ui['tree']) {
  1488. // try as no request
  1489. if (notify) {
  1490. ntftm = setTimeout(function() {
  1491. self.notify(notify);
  1492. }, self.notifyDelay);
  1493. }
  1494. noreq = true;
  1495. done(true);
  1496. } else {
  1497. req();
  1498. }
  1499. return dfd;
  1500. }
  1501. }
  1502. };
  1503. /**
  1504. * Return file url if set
  1505. *
  1506. * @param String file hash
  1507. * @param Object Options
  1508. * @return String
  1509. */
  1510. this.url = function(hash, o) {
  1511. var file = files[hash],
  1512. opts = o || {},
  1513. async = opts.async || false,
  1514. temp = opts.temporary || false,
  1515. dfrd = async? $.Deferred() : null,
  1516. getUrl = function(url) {
  1517. if (url) {
  1518. return url;
  1519. }
  1520. if (file.url) {
  1521. return file.url;
  1522. }
  1523. if (typeof baseUrl === 'undefined') {
  1524. baseUrl = self.option('url', (!self.isRoot(file) && file.phash) || file.hash);
  1525. }
  1526. if (baseUrl) {
  1527. return baseUrl + $.map(self.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/');
  1528. }
  1529. var params = Object.assign({}, self.customData, {
  1530. cmd: 'file',
  1531. target: file.hash
  1532. });
  1533. if (self.oldAPI) {
  1534. params.cmd = 'open';
  1535. params.current = file.phash;
  1536. }
  1537. return self.options.url + (self.options.url.indexOf('?') === -1 ? '?' : '&') + $.param(params, true);
  1538. },
  1539. baseUrl, res;
  1540. if (!file || !file.read) {
  1541. return async? dfrd.resolve('') : '';
  1542. }
  1543. if (file.url == '1' || (temp && !file.url && !(baseUrl = self.option('url', (!self.isRoot(file) && file.phash) || file.hash)))) {
  1544. this.request({
  1545. data : { cmd : 'url', target : hash, options : { temporary: temp? 1 : 0 } },
  1546. preventDefault : true,
  1547. options: {async: async},
  1548. notify: async? {type : temp? 'file' : 'url', cnt : 1, hideCnt : true} : {}
  1549. })
  1550. .done(function(data) {
  1551. file.url = data.url || '';
  1552. })
  1553. .fail(function() {
  1554. file.url = '';
  1555. })
  1556. .always(function() {
  1557. var url;
  1558. if (file.url && temp) {
  1559. url = file.url;
  1560. file.url = '1'; // restore
  1561. }
  1562. if (async) {
  1563. dfrd.resolve(getUrl(url));
  1564. } else {
  1565. return getUrl(url);
  1566. }
  1567. });
  1568. } else {
  1569. if (async) {
  1570. dfrd.resolve(getUrl());
  1571. } else {
  1572. return getUrl();
  1573. }
  1574. }
  1575. if (async) {
  1576. return dfrd;
  1577. }
  1578. };
  1579. /**
  1580. * Return file url for open in elFinder
  1581. *
  1582. * @param String file hash
  1583. * @param Boolean for download link
  1584. * @return String
  1585. */
  1586. this.openUrl = function(hash, download) {
  1587. var file = files[hash],
  1588. url = '';
  1589. if (!file || !file.read) {
  1590. return '';
  1591. }
  1592. if (!download) {
  1593. if (file.url) {
  1594. if (file.url != 1) {
  1595. url = file.url;
  1596. }
  1597. } else if (cwdOptions.url && file.hash.indexOf(self.cwd().volumeid) === 0) {
  1598. url = cwdOptions.url + $.map(this.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/');
  1599. }
  1600. if (url) {
  1601. url += (url.match(/\?/)? '&' : '?') + '_'.repeat((url.match(/[\?&](_+)t=/g) || ['&t=']).sort().shift().match(/[\?&](_*)t=/)[1].length + 1) + 't=' + (file.ts || parseInt(+new Date()/1000));
  1602. return url;
  1603. }
  1604. }
  1605. url = this.options.url;
  1606. url = url + (url.indexOf('?') === -1 ? '?' : '&')
  1607. + (this.oldAPI ? 'cmd=open&current='+file.phash : 'cmd=file')
  1608. + '&target=' + file.hash
  1609. + '&_t=' + (file.ts || parseInt(+new Date()/1000));
  1610. if (download) {
  1611. url += '&download=1';
  1612. }
  1613. $.each(this.customData, function(key, val) {
  1614. url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val);
  1615. });
  1616. return url;
  1617. };
  1618. /**
  1619. * Return thumbnail url
  1620. *
  1621. * @param Object file object
  1622. * @return String
  1623. */
  1624. this.tmb = function(file) {
  1625. var tmbUrl, tmbCrop,
  1626. cls = 'elfinder-cwd-bgurl',
  1627. url = '';
  1628. if ($.isPlainObject(file)) {
  1629. if (self.searchStatus.state && file.hash.indexOf(self.cwd().volumeid) !== 0) {
  1630. tmbUrl = self.option('tmbUrl', file.hash);
  1631. tmbCrop = self.option('tmbCrop', file.hash);
  1632. } else {
  1633. tmbUrl = cwdOptions['tmbUrl'];
  1634. tmbCrop = cwdOptions['tmbCrop'];
  1635. }
  1636. if (tmbCrop) {
  1637. cls += ' elfinder-cwd-bgurl-crop';
  1638. }
  1639. if (tmbUrl === 'self' && file.mime.indexOf('image/') === 0) {
  1640. url = self.openUrl(file.hash);
  1641. cls += ' elfinder-cwd-bgself';
  1642. } else if ((self.oldAPI || tmbUrl) && file && file.tmb && file.tmb != 1) {
  1643. url = tmbUrl + file.tmb;
  1644. } else if (self.newAPI && file && file.tmb && file.tmb != 1) {
  1645. url = file.tmb;
  1646. }
  1647. if (url) {
  1648. if (file.ts && tmbUrl !== 'self') {
  1649. url += (url.match(/\?/)? '&' : '?') + '_t=' + file.ts;
  1650. }
  1651. return { url: url, className: cls };
  1652. }
  1653. }
  1654. return false;
  1655. };
  1656. /**
  1657. * Return selected files hashes
  1658. *
  1659. * @return Array
  1660. **/
  1661. this.selected = function() {
  1662. return selected.slice(0);
  1663. };
  1664. /**
  1665. * Return selected files info
  1666. *
  1667. * @return Array
  1668. */
  1669. this.selectedFiles = function() {
  1670. return $.map(selected, function(hash) { return files[hash] ? Object.assign({}, files[hash]) : null; });
  1671. };
  1672. /**
  1673. * Return true if file with required name existsin required folder
  1674. *
  1675. * @param String file name
  1676. * @param String parent folder hash
  1677. * @return Boolean
  1678. */
  1679. this.fileByName = function(name, phash) {
  1680. var hash;
  1681. for (hash in files) {
  1682. if (files.hasOwnProperty(hash) && files[hash].phash == phash && files[hash].name == name) {
  1683. return files[hash];
  1684. }
  1685. }
  1686. };
  1687. /**
  1688. * Valid data for required command based on rules
  1689. *
  1690. * @param String command name
  1691. * @param Object cammand's data
  1692. * @return Boolean
  1693. */
  1694. this.validResponse = function(cmd, data) {
  1695. return data.error || this.rules[this.rules[cmd] ? cmd : 'defaults'](data);
  1696. };
  1697. /**
  1698. * Return bytes from ini formated size
  1699. *
  1700. * @param String ini formated size
  1701. * @return Integer
  1702. */
  1703. this.returnBytes = function(val) {
  1704. var last;
  1705. if (isNaN(val)) {
  1706. if (! val) {
  1707. val = '';
  1708. }
  1709. // for ex. 1mb, 1KB
  1710. val = val.replace(/b$/i, '');
  1711. last = val.charAt(val.length - 1).toLowerCase();
  1712. val = val.replace(/[tgmk]$/i, '');
  1713. if (last == 't') {
  1714. val = val * 1024 * 1024 * 1024 * 1024;
  1715. } else if (last == 'g') {
  1716. val = val * 1024 * 1024 * 1024;
  1717. } else if (last == 'm') {
  1718. val = val * 1024 * 1024;
  1719. } else if (last == 'k') {
  1720. val = val * 1024;
  1721. }
  1722. val = isNaN(val)? 0 : parseInt(val);
  1723. } else {
  1724. val = parseInt(val);
  1725. if (val < 1) val = 0;
  1726. }
  1727. return val;
  1728. };
  1729. /**
  1730. * Process ajax request.
  1731. * Fired events :
  1732. * @todo
  1733. * @example
  1734. * @todo
  1735. * @return $.Deferred
  1736. */
  1737. this.request = function(opts) {
  1738. var self = this,
  1739. o = this.options,
  1740. dfrd = $.Deferred(),
  1741. // request ID
  1742. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16),
  1743. // request data
  1744. data = Object.assign({}, self.customData, {mimes : o.onlyMimes}, opts.data || opts),
  1745. // command name
  1746. cmd = data.cmd,
  1747. // request type is binary
  1748. isBinary = (opts.options || {}).dataType === 'binary',
  1749. // current cmd is "open"
  1750. isOpen = (!opts.asNotOpen && cmd === 'open'),
  1751. // call default fail callback (display error dialog) ?
  1752. deffail = !(isBinary || opts.preventDefault || opts.preventFail),
  1753. // call default success callback ?
  1754. defdone = !(isBinary || opts.preventDefault || opts.preventDone),
  1755. // options for notify dialog
  1756. notify = Object.assign({}, opts.notify),
  1757. // make cancel button
  1758. cancel = !!opts.cancel,
  1759. // do not normalize data - return as is
  1760. raw = isBinary || !!opts.raw,
  1761. // sync files on request fail
  1762. syncOnFail = opts.syncOnFail,
  1763. // use lazy()
  1764. lazy = !!opts.lazy,
  1765. // prepare function before done()
  1766. prepare = opts.prepare,
  1767. // navigate option object when cmd done
  1768. navigate = opts.navigate,
  1769. // open notify dialog timeout
  1770. timeout,
  1771. // use browser cache
  1772. useCache = (opts.options || {}).cache,
  1773. // request options
  1774. options = Object.assign({
  1775. url : o.url,
  1776. async : true,
  1777. type : this.requestType,
  1778. dataType : 'json',
  1779. cache : (self.api >= 2.1029), // api >= 2.1029 has unique request ID
  1780. data : data,
  1781. headers : this.customHeaders,
  1782. xhrFields: this.xhrFields
  1783. }, opts.options || {}),
  1784. /**
  1785. * Default success handler.
  1786. * Call default data handlers and fire event with command name.
  1787. *
  1788. * @param Object normalized response data
  1789. * @return void
  1790. **/
  1791. done = function(data) {
  1792. data.warning && self.error(data.warning);
  1793. if (isOpen) {
  1794. open(data);
  1795. } else {
  1796. self.updateCache(data);
  1797. }
  1798. data.changed && data.changed.length && change(data.changed);
  1799. self.lazy(function() {
  1800. // fire some event to update cache/ui
  1801. data.removed && data.removed.length && self.remove(data);
  1802. data.added && data.added.length && self.add(data);
  1803. data.changed && data.changed.length && self.change(data);
  1804. }).then(function() {
  1805. // fire event with command name
  1806. return self.lazy(function() {
  1807. self.trigger(cmd, data, false);
  1808. });
  1809. }).then(function() {
  1810. // fire event with command name + 'done'
  1811. return self.lazy(function() {
  1812. self.trigger(cmd + 'done');
  1813. });
  1814. }).then(function() {
  1815. // make toast message
  1816. if (data.toasts && Array.isArray(data.toasts)) {
  1817. $.each(data.toasts, function() {
  1818. this.msg && self.toast(this);
  1819. });
  1820. }
  1821. // force update content
  1822. data.sync && self.sync();
  1823. });
  1824. },
  1825. /**
  1826. * Request error handler. Reject dfrd with correct error message.
  1827. *
  1828. * @param jqxhr request object
  1829. * @param String request status
  1830. * @return void
  1831. **/
  1832. error = function(xhr, status) {
  1833. var error, data,
  1834. d = self.options.debug;
  1835. switch (status) {
  1836. case 'abort':
  1837. error = xhr.quiet ? '' : ['errConnect', 'errAbort'];
  1838. break;
  1839. case 'timeout':
  1840. error = ['errConnect', 'errTimeout'];
  1841. break;
  1842. case 'parsererror':
  1843. error = ['errResponse', 'errDataNotJSON'];
  1844. if (xhr.responseText) {
  1845. if (! cwd || (d && (d === 'all' || d['backend-error']))) {
  1846. error.push(xhr.responseText);
  1847. }
  1848. }
  1849. break;
  1850. default:
  1851. if (xhr.responseText) {
  1852. // check responseText, Is that JSON?
  1853. try {
  1854. data = JSON.parse(xhr.responseText);
  1855. if (data && data.error) {
  1856. error = data.error;
  1857. }
  1858. } catch(e) {}
  1859. }
  1860. if (! error) {
  1861. if (xhr.status == 403) {
  1862. error = ['errConnect', 'errAccess', 'HTTP error ' + xhr.status];
  1863. } else if (xhr.status == 404) {
  1864. error = ['errConnect', 'errNotFound', 'HTTP error ' + xhr.status];
  1865. } else if (xhr.status >= 500) {
  1866. error = ['errResponse', 'errServerError', 'HTTP error ' + xhr.status];
  1867. } else {
  1868. if (xhr.status == 414 && options.type === 'get') {
  1869. // retry by POST method
  1870. options.type = 'post';
  1871. self.abortXHR(xhr);
  1872. dfrd.xhr = xhr = self.transport.send(options).fail(error).done(success);
  1873. return;
  1874. }
  1875. error = xhr.quiet ? '' : ['errConnect', 'HTTP error ' + xhr.status];
  1876. }
  1877. }
  1878. }
  1879. self.trigger(cmd + 'done');
  1880. dfrd.reject({error: error}, xhr, status);
  1881. },
  1882. /**
  1883. * Request success handler. Valid response data and reject/resolve dfrd.
  1884. *
  1885. * @param Object response data
  1886. * @param String request status
  1887. * @return void
  1888. **/
  1889. success = function(response) {
  1890. var d = self.options.debug;
  1891. // Set currrent request command name
  1892. self.currentReqCmd = cmd;
  1893. if (response.debug && (!d || d !== 'all')) {
  1894. if (!d) {
  1895. d = self.options.debug = {};
  1896. }
  1897. d['backend-error'] = true;
  1898. d['warning'] = true;
  1899. }
  1900. if (raw) {
  1901. self.abortXHR(xhr);
  1902. response && response.debug && self.debug('backend-debug', response);
  1903. return dfrd.resolve(response);
  1904. }
  1905. if (!response) {
  1906. return dfrd.reject({error :['errResponse', 'errDataEmpty']}, xhr, response);
  1907. } else if (!$.isPlainObject(response)) {
  1908. return dfrd.reject({error :['errResponse', 'errDataNotJSON']}, xhr, response);
  1909. } else if (response.error) {
  1910. if (isOpen) {
  1911. // check leafRoots
  1912. $.each(self.leafRoots, function(phash, roots) {
  1913. self.leafRoots[phash] = $.grep(roots, function(h) { return h !== data.target; });
  1914. });
  1915. }
  1916. return dfrd.reject({error :response.error}, xhr, response);
  1917. }
  1918. var resolve = function() {
  1919. var pushLeafRoots = function(name) {
  1920. if (self.leafRoots[data.target] && response[name]) {
  1921. $.each(self.leafRoots[data.target], function(i, h) {
  1922. var root;
  1923. if (root = self.file(h)) {
  1924. response[name].push(root);
  1925. }
  1926. });
  1927. }
  1928. },
  1929. setTextMimes = function() {
  1930. self.textMimes = {};
  1931. $.each(self.res('mimes', 'text'), function() {
  1932. self.textMimes[this.toLowerCase()] = true;
  1933. });
  1934. },
  1935. actionTarget;
  1936. if (isOpen) {
  1937. pushLeafRoots('files');
  1938. } else if (cmd === 'tree') {
  1939. pushLeafRoots('tree');
  1940. }
  1941. response = self.normalize(response);
  1942. if (!self.validResponse(cmd, response)) {
  1943. return dfrd.reject({error :(response.norError || 'errResponse')}, xhr, response);
  1944. }
  1945. if (isOpen) {
  1946. if (!self.api) {
  1947. self.api = response.api || 1;
  1948. if (self.api == '2.0' && typeof response.options.uploadMaxSize !== 'undefined') {
  1949. self.api = '2.1';
  1950. }
  1951. self.newAPI = self.api >= 2;
  1952. self.oldAPI = !self.newAPI;
  1953. }
  1954. if (response.textMimes && Array.isArray(response.textMimes)) {
  1955. self.resources.mimes.text = response.textMimes;
  1956. setTextMimes();
  1957. }
  1958. !self.textMimes && setTextMimes();
  1959. if (response.options) {
  1960. cwdOptions = Object.assign({}, cwdOptionsDefault, response.options);
  1961. }
  1962. if (response.netDrivers) {
  1963. self.netDrivers = response.netDrivers;
  1964. }
  1965. if (response.maxTargets) {
  1966. self.maxTargets = response.maxTargets;
  1967. }
  1968. if (!!data.init) {
  1969. self.uplMaxSize = self.returnBytes(response.uplMaxSize);
  1970. self.uplMaxFile = !!response.uplMaxFile? Math.max(parseInt(response.uplMaxFile), 50) : 20;
  1971. }
  1972. }
  1973. if (typeof prepare === 'function') {
  1974. prepare(response);
  1975. }
  1976. if (navigate) {
  1977. actionTarget = navigate.target || 'added';
  1978. if (response[actionTarget] && response[actionTarget].length) {
  1979. self.one(cmd + 'done', function() {
  1980. var targets = response[actionTarget],
  1981. newItems = self.findCwdNodes(targets),
  1982. inCwdHashes = function() {
  1983. var cwdHash = self.cwd().hash;
  1984. return $.map(targets, function(f) { return (f.phash && cwdHash === f.phash)? f.hash : null; });
  1985. },
  1986. hashes = inCwdHashes(),
  1987. makeToast = function(t) {
  1988. var node = void(0),
  1989. data = t.action? t.action.data : void(0),
  1990. cmd, msg, done;
  1991. if ((data || hashes.length) && t.action && (msg = t.action.msg) && (cmd = t.action.cmd) && (!t.action.cwdNot || t.action.cwdNot !== self.cwd().hash)) {
  1992. done = t.action.done;
  1993. data = t.action.data;
  1994. node = $('<div/>')
  1995. .append(
  1996. $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all elfinder-tabstop"><span class="ui-button-text">'
  1997. +self.i18n(msg)
  1998. +'</span></button>')
  1999. .on('mouseenter mouseleave', function(e) {
  2000. $(this).toggleClass('ui-state-hover', e.type == 'mouseenter');
  2001. })
  2002. .on('click', function() {
  2003. self.exec(cmd, data || hashes, {_userAction: true, _currentType: 'toast', _currentNode: $(this) });
  2004. if (done) {
  2005. self.one(cmd+'done', function() {
  2006. if (typeof done === 'function') {
  2007. done();
  2008. } else if (done === 'select') {
  2009. self.trigger('selectfiles', {files : inCwdHashes()});
  2010. }
  2011. });
  2012. }
  2013. })
  2014. );
  2015. }
  2016. delete t.action;
  2017. t.extNode = node;
  2018. return t;
  2019. };
  2020. if (! navigate.toast) {
  2021. navigate.toast = {};
  2022. }
  2023. !navigate.noselect && self.trigger('selectfiles', {files : self.searchStatus.state > 1 ? $.map(targets, function(f) { return f.hash; }) : hashes});
  2024. if (newItems.length) {
  2025. if (!navigate.noscroll) {
  2026. newItems.first().trigger('scrolltoview', {blink : false});
  2027. self.resources.blink(newItems, 'lookme');
  2028. }
  2029. if ($.isPlainObject(navigate.toast.incwd)) {
  2030. self.toast(makeToast(navigate.toast.incwd));
  2031. }
  2032. } else {
  2033. if ($.isPlainObject(navigate.toast.inbuffer)) {
  2034. self.toast(makeToast(navigate.toast.inbuffer));
  2035. }
  2036. }
  2037. });
  2038. }
  2039. }
  2040. dfrd.resolve(response);
  2041. response.debug && self.debug('backend-debug', response);
  2042. };
  2043. self.abortXHR(xhr);
  2044. lazy? self.lazy(resolve) : resolve();
  2045. },
  2046. xhr, _xhr,
  2047. xhrAbort = function(e) {
  2048. if (xhr && xhr.state() === 'pending') {
  2049. self.abortXHR(xhr, { quiet: true , abort: true });
  2050. if (!e || (e.type !== 'unload' && e.type !== 'destroy')) {
  2051. self.autoSync();
  2052. }
  2053. }
  2054. },
  2055. abort = function(e){
  2056. self.trigger(cmd + 'done');
  2057. if (e.type == 'autosync') {
  2058. if (e.data.action != 'stop') return;
  2059. } else if (e.type != 'unload' && e.type != 'destroy' && e.type != 'openxhrabort') {
  2060. if (!e.data.added || !e.data.added.length) {
  2061. return;
  2062. }
  2063. }
  2064. xhrAbort(e);
  2065. },
  2066. request = function(mode) {
  2067. var queueAbort = function() {
  2068. syncOnFail = false;
  2069. dfrd.reject();
  2070. };
  2071. if (mode) {
  2072. if (mode === 'cmd') {
  2073. return cmd;
  2074. }
  2075. }
  2076. if (isOpen) {
  2077. if (requestQueueSkipOpen) {
  2078. return dfrd.reject();
  2079. }
  2080. requestQueueSkipOpen = true;
  2081. }
  2082. dfrd.always(function() {
  2083. delete options.headers['X-elFinderReqid'];
  2084. }).fail(function(error, xhr, response) {
  2085. var errData = {
  2086. cmd: cmd,
  2087. err: error,
  2088. xhr: xhr,
  2089. rc: response
  2090. };
  2091. // unset this cmd queue when user canceling
  2092. // see notify : function - `cancel.reject(0);`
  2093. if (error === 0) {
  2094. if (requestQueue.length) {
  2095. requestQueue = $.grep(requestQueue, function(req) {
  2096. return (req('cmd') === cmd) ? false : true;
  2097. });
  2098. }
  2099. }
  2100. // trigger "requestError" event
  2101. self.trigger('requestError', errData);
  2102. if (errData._event && errData._event.isDefaultPrevented()) {
  2103. deffail = false;
  2104. syncOnFail = false;
  2105. if (error) {
  2106. error.error = '';
  2107. }
  2108. }
  2109. // abort xhr
  2110. xhrAbort();
  2111. if (isOpen) {
  2112. openDir = self.file(data.target);
  2113. openDir && openDir.volumeid && self.isRoot(openDir) && delete self.volumeExpires[openDir.volumeid];
  2114. }
  2115. self.trigger(cmd + 'fail', response);
  2116. if (error) {
  2117. deffail ? self.error(error) : self.debug('error', self.i18n(error));
  2118. }
  2119. syncOnFail && self.sync();
  2120. });
  2121. if (!cmd) {
  2122. syncOnFail = false;
  2123. return dfrd.reject({error :'errCmdReq'});
  2124. }
  2125. if (self.maxTargets && data.targets && data.targets.length > self.maxTargets) {
  2126. syncOnFail = false;
  2127. return dfrd.reject({error :['errMaxTargets', self.maxTargets]});
  2128. }
  2129. defdone && dfrd.done(done);
  2130. // quiet abort not completed "open" requests
  2131. if (isOpen) {
  2132. while ((_xhr = queue.pop())) {
  2133. _xhr.queueAbort();
  2134. }
  2135. if (cwd !== data.target) {
  2136. while ((_xhr = cwdQueue.pop())) {
  2137. _xhr.queueAbort();
  2138. }
  2139. }
  2140. }
  2141. // trigger abort autoSync for commands to add the item
  2142. if ($.inArray(cmd, (self.cmdsToAdd + ' autosync').split(' ')) !== -1) {
  2143. if (cmd !== 'autosync') {
  2144. self.autoSync('stop');
  2145. dfrd.always(function() {
  2146. self.autoSync();
  2147. });
  2148. }
  2149. self.trigger('openxhrabort');
  2150. }
  2151. delete options.preventFail;
  2152. if (self.api >= 2.1029) {
  2153. if (useCache) {
  2154. options.headers['X-elFinderReqid'] = reqId;
  2155. } else {
  2156. Object.assign(options.data, { reqid : reqId });
  2157. }
  2158. }
  2159. // function for set value of this syncOnFail
  2160. dfrd.syncOnFail = function(state) {
  2161. syncOnFail = !!state;
  2162. };
  2163. requestCnt++;
  2164. dfrd.xhr = xhr = self.transport.send(options).always(function() {
  2165. // set responseURL from native xhr object
  2166. if (options._xhr && typeof options._xhr.responseURL !== 'undefined') {
  2167. xhr.responseURL = options._xhr.responseURL || '';
  2168. }
  2169. --requestCnt;
  2170. if (requestQueue.length) {
  2171. requestQueue.shift()();
  2172. } else {
  2173. requestQueueSkipOpen = false;
  2174. }
  2175. }).fail(error).done(success);
  2176. if (self.api >= 2.1029) {
  2177. xhr._requestId = reqId;
  2178. }
  2179. if (isOpen || (data.compare && cmd === 'info')) {
  2180. // regist function queueAbort
  2181. xhr.queueAbort = queueAbort;
  2182. // add autoSync xhr into queue
  2183. queue.unshift(xhr);
  2184. // bind abort()
  2185. data.compare && self.bind(self.cmdsToAdd + ' autosync openxhrabort', abort);
  2186. dfrd.always(function() {
  2187. var ndx = $.inArray(xhr, queue);
  2188. data.compare && self.unbind(self.cmdsToAdd + ' autosync openxhrabort', abort);
  2189. ndx !== -1 && queue.splice(ndx, 1);
  2190. });
  2191. } else if ($.inArray(cmd, self.abortCmdsOnOpen) !== -1) {
  2192. // regist function queueAbort
  2193. xhr.queueAbort = queueAbort;
  2194. // add "open" xhr, only cwd xhr into queue
  2195. cwdQueue.unshift(xhr);
  2196. dfrd.always(function() {
  2197. var ndx = $.inArray(xhr, cwdQueue);
  2198. ndx !== -1 && cwdQueue.splice(ndx, 1);
  2199. });
  2200. }
  2201. // abort pending xhr on window unload or elFinder destroy
  2202. self.bind('unload destroy', abort);
  2203. dfrd.always(function() {
  2204. self.unbind('unload destroy', abort);
  2205. });
  2206. return dfrd;
  2207. },
  2208. queueingRequest = function() {
  2209. // show notify
  2210. if (notify.type && notify.cnt) {
  2211. if (cancel) {
  2212. notify.cancel = dfrd;
  2213. opts.eachCancel && (notify.id = +new Date());
  2214. }
  2215. timeout = setTimeout(function() {
  2216. self.notify(notify);
  2217. dfrd.always(function() {
  2218. notify.cnt = -(parseInt(notify.cnt)||0);
  2219. self.notify(notify);
  2220. });
  2221. }, self.notifyDelay);
  2222. dfrd.always(function() {
  2223. clearTimeout(timeout);
  2224. });
  2225. }
  2226. // queueing
  2227. if (isOpen) {
  2228. requestQueueSkipOpen = false;
  2229. }
  2230. if (requestCnt < requestMaxConn) {
  2231. // do request
  2232. return request();
  2233. } else {
  2234. if (isOpen) {
  2235. requestQueue.unshift(request);
  2236. } else {
  2237. requestQueue.push(request);
  2238. }
  2239. return dfrd;
  2240. }
  2241. },
  2242. bindData = {opts: opts, result: true},
  2243. openDir;
  2244. // prevent request initial request is completed
  2245. if (!self.api && !data.init) {
  2246. syncOnFail = false;
  2247. return dfrd.reject();
  2248. }
  2249. // trigger "request.cmd" that callback be able to cancel request by substituting "false" for "event.data.result"
  2250. self.trigger('request.' + cmd, bindData, true);
  2251. if (! bindData.result) {
  2252. self.trigger(cmd + 'done');
  2253. return dfrd.reject();
  2254. } else if (typeof bindData.result === 'object' && bindData.result.promise) {
  2255. bindData.result
  2256. .done(queueingRequest)
  2257. .fail(function() {
  2258. self.trigger(cmd + 'done');
  2259. dfrd.reject();
  2260. });
  2261. return dfrd;
  2262. }
  2263. return queueingRequest();
  2264. };
  2265. /**
  2266. * Call cache()
  2267. * Store info about files/dirs in "files" object.
  2268. *
  2269. * @param Array files
  2270. * @return void
  2271. */
  2272. this.cache = function(dataArray) {
  2273. if (! Array.isArray(dataArray)) {
  2274. dataArray = [ dataArray ];
  2275. }
  2276. cache(dataArray);
  2277. };
  2278. /**
  2279. * Update file object caches by respose data object
  2280. *
  2281. * @param Object respose data object
  2282. * @return void
  2283. */
  2284. this.updateCache = function(data) {
  2285. if ($.isPlainObject(data)) {
  2286. data.files && data.files.length && cache(data.files, 'files');
  2287. data.tree && data.tree.length && cache(data.tree, 'tree');
  2288. data.removed && data.removed.length && remove(data.removed);
  2289. data.added && data.added.length && cache(data.added, 'add');
  2290. data.changed && data.changed.length && change(data.changed, 'change');
  2291. }
  2292. };
  2293. /**
  2294. * Compare current files cache with new files and return diff
  2295. *
  2296. * @param Array new files
  2297. * @param String target folder hash
  2298. * @param Array exclude properties to compare
  2299. * @return Object
  2300. */
  2301. this.diff = function(incoming, onlydir, excludeProps) {
  2302. var raw = {},
  2303. added = [],
  2304. removed = [],
  2305. changed = [],
  2306. excludes = null,
  2307. isChanged = function(hash) {
  2308. var l = changed.length;
  2309. while (l--) {
  2310. if (changed[l].hash == hash) {
  2311. return true;
  2312. }
  2313. }
  2314. };
  2315. $.each(incoming, function(i, f) {
  2316. raw[f.hash] = f;
  2317. });
  2318. // make excludes object
  2319. if (excludeProps && excludeProps.length) {
  2320. excludes = {};
  2321. $.each(excludeProps, function() {
  2322. excludes[this] = true;
  2323. });
  2324. }
  2325. // find removed
  2326. $.each(files, function(hash, f) {
  2327. if (! raw[hash] && (! onlydir || f.phash === onlydir)) {
  2328. removed.push(hash);
  2329. }
  2330. });
  2331. // compare files
  2332. $.each(raw, function(hash, file) {
  2333. var origin = files[hash],
  2334. orgKeys = {},
  2335. chkKeyLen;
  2336. if (!origin) {
  2337. added.push(file);
  2338. } else {
  2339. // make orgKeys object
  2340. $.each(Object.keys(origin), function() {
  2341. orgKeys[this] = true;
  2342. });
  2343. $.each(file, function(prop) {
  2344. delete orgKeys[prop];
  2345. if (! excludes || ! excludes[prop]) {
  2346. if (file[prop] !== origin[prop]) {
  2347. changed.push(file);
  2348. orgKeys = {};
  2349. return false;
  2350. }
  2351. }
  2352. });
  2353. chkKeyLen = Object.keys(orgKeys).length;
  2354. if (chkKeyLen !== 0) {
  2355. if (excludes) {
  2356. $.each(orgKeys, function(prop) {
  2357. if (excludes[prop]) {
  2358. --chkKeyLen;
  2359. }
  2360. });
  2361. }
  2362. (chkKeyLen !== 0) && changed.push(file);
  2363. }
  2364. }
  2365. });
  2366. // parents of removed dirs mark as changed (required for tree correct work)
  2367. $.each(removed, function(i, hash) {
  2368. var file = files[hash],
  2369. phash = file.phash;
  2370. if (phash
  2371. && file.mime == 'directory'
  2372. && $.inArray(phash, removed) === -1
  2373. && raw[phash]
  2374. && !isChanged(phash)) {
  2375. changed.push(raw[phash]);
  2376. }
  2377. });
  2378. return {
  2379. added : added,
  2380. removed : removed,
  2381. changed : changed
  2382. };
  2383. };
  2384. /**
  2385. * Sync content
  2386. *
  2387. * @return jQuery.Deferred
  2388. */
  2389. this.sync = function(onlydir, polling) {
  2390. this.autoSync('stop');
  2391. var self = this,
  2392. compare = function(){
  2393. var c = '', cnt = 0, mtime = 0;
  2394. if (onlydir && polling) {
  2395. $.each(files, function(h, f) {
  2396. if (f.phash && f.phash === onlydir) {
  2397. ++cnt;
  2398. mtime = Math.max(mtime, f.ts);
  2399. }
  2400. c = cnt+':'+mtime;
  2401. });
  2402. }
  2403. return c;
  2404. },
  2405. comp = compare(),
  2406. dfrd = $.Deferred().done(function() { self.trigger('sync'); }),
  2407. opts = [this.request({
  2408. data : {cmd : 'open', reload : 1, target : cwd, tree : (! onlydir && this.ui.tree) ? 1 : 0, compare : comp},
  2409. preventDefault : true
  2410. })],
  2411. exParents = function() {
  2412. var parents = [],
  2413. curRoot = self.file(self.root(cwd)),
  2414. curId = curRoot? curRoot.volumeid : null,
  2415. phash = self.cwd().phash,
  2416. isroot,pdir;
  2417. while(phash) {
  2418. if (pdir = self.file(phash)) {
  2419. if (phash.indexOf(curId) !== 0) {
  2420. parents.push( {target: phash, cmd: 'tree'} );
  2421. if (! self.isRoot(pdir)) {
  2422. parents.push( {target: phash, cmd: 'parents'} );
  2423. }
  2424. curRoot = self.file(self.root(phash));
  2425. curId = curRoot? curRoot.volumeid : null;
  2426. }
  2427. phash = pdir.phash;
  2428. } else {
  2429. phash = null;
  2430. }
  2431. }
  2432. return parents;
  2433. };
  2434. if (! onlydir && self.api >= 2) {
  2435. (cwd !== this.root()) && opts.push(this.request({
  2436. data : {cmd : 'parents', target : cwd},
  2437. preventDefault : true
  2438. }));
  2439. $.each(exParents(), function(i, data) {
  2440. opts.push(self.request({
  2441. data : {cmd : data.cmd, target : data.target},
  2442. preventDefault : true
  2443. }));
  2444. });
  2445. }
  2446. $.when.apply($, opts)
  2447. .fail(function(error, xhr) {
  2448. if (! polling || $.inArray('errOpen', error) !== -1) {
  2449. dfrd.reject(error);
  2450. self.parseError(error) && self.request({
  2451. data : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1},
  2452. notify : {type : 'open', cnt : 1, hideCnt : true}
  2453. });
  2454. } else {
  2455. dfrd.reject((error && xhr.status != 0)? error : void 0);
  2456. }
  2457. })
  2458. .done(function(odata) {
  2459. var pdata, argLen, i;
  2460. if (odata.cwd.compare) {
  2461. if (comp === odata.cwd.compare) {
  2462. return dfrd.reject();
  2463. }
  2464. }
  2465. // for 2nd and more requests
  2466. pdata = {tree : []};
  2467. // results marge of 2nd and more requests
  2468. argLen = arguments.length;
  2469. if (argLen > 1) {
  2470. for(i = 1; i < argLen; i++) {
  2471. if (arguments[i].tree && arguments[i].tree.length) {
  2472. pdata.tree.push.apply(pdata.tree, arguments[i].tree);
  2473. }
  2474. }
  2475. }
  2476. if (self.api < 2.1) {
  2477. if (! pdata.tree) {
  2478. pdata.tree = [];
  2479. }
  2480. pdata.tree.push(odata.cwd);
  2481. }
  2482. // data normalize
  2483. odata = self.normalize(odata);
  2484. if (!self.validResponse('open', odata)) {
  2485. return dfrd.reject((odata.norError || 'errResponse'));
  2486. }
  2487. pdata = self.normalize(pdata);
  2488. if (!self.validResponse('tree', pdata)) {
  2489. return dfrd.reject((pdata.norError || 'errResponse'));
  2490. }
  2491. var diff = self.diff(odata.files.concat(pdata && pdata.tree ? pdata.tree : []), onlydir);
  2492. diff.added.push(odata.cwd);
  2493. self.updateCache(diff);
  2494. // trigger events
  2495. diff.removed.length && self.remove(diff);
  2496. diff.added.length && self.add(diff);
  2497. diff.changed.length && self.change(diff);
  2498. return dfrd.resolve(diff);
  2499. })
  2500. .always(function() {
  2501. self.autoSync();
  2502. });
  2503. return dfrd;
  2504. };
  2505. this.upload = function(files) {
  2506. return this.transport.upload(files, this);
  2507. };
  2508. /**
  2509. * Arrays that has to unbind events
  2510. *
  2511. * @type Object
  2512. */
  2513. this.toUnbindEvents = {};
  2514. /**
  2515. * Attach listener to events
  2516. * To bind to multiply events at once, separate events names by space
  2517. *
  2518. * @param String event(s) name(s)
  2519. * @param Object event handler or {done: handler}
  2520. * @param Boolean priority first
  2521. * @return elFinder
  2522. */
  2523. this.bind = function(event, callback, priorityFirst) {
  2524. var i, len;
  2525. if (callback && (typeof callback === 'function' || typeof callback.done === 'function')) {
  2526. event = ('' + event).toLowerCase().replace(/^\s+|\s+$/g, '').split(/\s+/);
  2527. len = event.length;
  2528. for (i = 0; i < len; i++) {
  2529. if (listeners[event[i]] === void(0)) {
  2530. listeners[event[i]] = [];
  2531. }
  2532. listeners[event[i]][priorityFirst? 'unshift' : 'push'](callback);
  2533. }
  2534. }
  2535. return this;
  2536. };
  2537. /**
  2538. * Remove event listener if exists
  2539. * To un-bind to multiply events at once, separate events names by space
  2540. *
  2541. * @param String event(s) name(s)
  2542. * @param Function callback
  2543. * @return elFinder
  2544. */
  2545. this.unbind = function(event, callback) {
  2546. var i, len, l, ci;
  2547. event = ('' + event).toLowerCase().split(/\s+/);
  2548. len = event.length;
  2549. for (i = 0; i < len; i++) {
  2550. if (l = listeners[event[i]]) {
  2551. ci = $.inArray(callback, l);
  2552. ci > -1 && l.splice(ci, 1);
  2553. }
  2554. }
  2555. callback = null;
  2556. return this;
  2557. };
  2558. /**
  2559. * Fire event - send notification to all event listeners
  2560. * In the callback `this` becames an event object
  2561. *
  2562. * @param String event type
  2563. * @param Object data to send across event
  2564. * @param Boolean allow modify data (call by reference of data) default: true
  2565. * @return elFinder
  2566. */
  2567. this.trigger = function(evType, data, allowModify) {
  2568. var type = evType.toLowerCase(),
  2569. isopen = (type === 'open'),
  2570. dataIsObj = (typeof data === 'object'),
  2571. handlers = listeners[type] || [],
  2572. dones = [],
  2573. i, l, jst, event;
  2574. this.debug('event-'+type, data);
  2575. if (! dataIsObj || typeof allowModify === 'undefined') {
  2576. allowModify = true;
  2577. }
  2578. if (l = handlers.length) {
  2579. event = $.Event(type);
  2580. if (data) {
  2581. data._event = event;
  2582. }
  2583. if (allowModify) {
  2584. event.data = data;
  2585. }
  2586. for (i = 0; i < l; i++) {
  2587. if (! handlers[i]) {
  2588. // probably un-binded this handler
  2589. continue;
  2590. }
  2591. // handler is $.Deferred(), call all functions upon completion
  2592. if (handlers[i].done) {
  2593. dones.push(handlers[i].done);
  2594. continue;
  2595. }
  2596. // set `event.data` only callback has argument
  2597. if (handlers[i].length) {
  2598. if (!allowModify) {
  2599. // to avoid data modifications. remember about "sharing" passing arguments in js :)
  2600. if (typeof jst === 'undefined') {
  2601. try {
  2602. jst = JSON.stringify(data);
  2603. } catch(e) {
  2604. jst = false;
  2605. }
  2606. }
  2607. event.data = jst? JSON.parse(jst) : data;
  2608. }
  2609. }
  2610. try {
  2611. if (handlers[i].call(event, event, this) === false || event.isDefaultPrevented()) {
  2612. this.debug('event-stoped', event.type);
  2613. break;
  2614. }
  2615. } catch (ex) {
  2616. window.console && window.console.log && window.console.log(ex);
  2617. }
  2618. }
  2619. // call done functions
  2620. if (l = dones.length) {
  2621. for (i = 0; i < l; i++) {
  2622. try {
  2623. if (dones[i].call(event, event, this) === false || event.isDefaultPrevented()) {
  2624. this.debug('event-stoped', event.type + '(done)');
  2625. break;
  2626. }
  2627. } catch (ex) {
  2628. window.console && window.console.log && window.console.log(ex);
  2629. }
  2630. }
  2631. }
  2632. if (this.toUnbindEvents[type] && this.toUnbindEvents[type].length) {
  2633. $.each(this.toUnbindEvents[type], function(i, v) {
  2634. self.unbind(v.type, v.callback);
  2635. });
  2636. delete this.toUnbindEvents[type];
  2637. }
  2638. }
  2639. return this;
  2640. };
  2641. /**
  2642. * Get event listeners
  2643. *
  2644. * @param String event type
  2645. * @return Array listed event functions
  2646. */
  2647. this.getListeners = function(event) {
  2648. return event? listeners[event.toLowerCase()] : listeners;
  2649. };
  2650. /**
  2651. * Bind keybord shortcut to keydown event
  2652. *
  2653. * @example
  2654. * elfinder.shortcut({
  2655. * pattern : 'ctrl+a',
  2656. * description : 'Select all files',
  2657. * callback : function(e) { ... },
  2658. * keypress : true|false (bind to keypress instead of keydown)
  2659. * })
  2660. *
  2661. * @param Object shortcut config
  2662. * @return elFinder
  2663. */
  2664. this.shortcut = function(s) {
  2665. var patterns, pattern, code, i, parts;
  2666. if (this.options.allowShortcuts && s.pattern && $.isFunction(s.callback)) {
  2667. patterns = s.pattern.toUpperCase().split(/\s+/);
  2668. for (i= 0; i < patterns.length; i++) {
  2669. pattern = patterns[i];
  2670. parts = pattern.split('+');
  2671. code = (code = parts.pop()).length == 1
  2672. ? (code > 0 ? code : code.charCodeAt(0))
  2673. : (code > 0 ? code : $.ui.keyCode[code]);
  2674. if (code && !shortcuts[pattern]) {
  2675. shortcuts[pattern] = {
  2676. keyCode : code,
  2677. altKey : $.inArray('ALT', parts) != -1,
  2678. ctrlKey : $.inArray('CTRL', parts) != -1,
  2679. shiftKey : $.inArray('SHIFT', parts) != -1,
  2680. type : s.type || 'keydown',
  2681. callback : s.callback,
  2682. description : s.description,
  2683. pattern : pattern
  2684. };
  2685. }
  2686. }
  2687. }
  2688. return this;
  2689. };
  2690. /**
  2691. * Registered shortcuts
  2692. *
  2693. * @type Object
  2694. **/
  2695. this.shortcuts = function() {
  2696. var ret = [];
  2697. $.each(shortcuts, function(i, s) {
  2698. ret.push([s.pattern, self.i18n(s.description)]);
  2699. });
  2700. return ret;
  2701. };
  2702. /**
  2703. * Get/set clipboard content.
  2704. * Return new clipboard content.
  2705. *
  2706. * @example
  2707. * this.clipboard([]) - clean clipboard
  2708. * this.clipboard([{...}, {...}], true) - put 2 files in clipboard and mark it as cutted
  2709. *
  2710. * @param Array new files hashes
  2711. * @param Boolean cut files?
  2712. * @return Array
  2713. */
  2714. this.clipboard = function(hashes, cut) {
  2715. var map = function() { return $.map(clipboard, function(f) { return f.hash; }); };
  2716. if (hashes !== void(0)) {
  2717. clipboard.length && this.trigger('unlockfiles', {files : map()});
  2718. remember = {};
  2719. clipboard = $.map(hashes||[], function(hash) {
  2720. var file = files[hash];
  2721. if (file) {
  2722. remember[hash] = true;
  2723. return {
  2724. hash : hash,
  2725. phash : file.phash,
  2726. name : file.name,
  2727. mime : file.mime,
  2728. read : file.read,
  2729. locked : file.locked,
  2730. cut : !!cut
  2731. };
  2732. }
  2733. return null;
  2734. });
  2735. this.trigger('changeclipboard', {clipboard : clipboard.slice(0, clipboard.length)});
  2736. cut && this.trigger('lockfiles', {files : map()});
  2737. }
  2738. // return copy of clipboard instead of refrence
  2739. return clipboard.slice(0, clipboard.length);
  2740. };
  2741. /**
  2742. * Return true if command enabled
  2743. *
  2744. * @param String command name
  2745. * @param String|void hash for check of own volume's disabled cmds
  2746. * @return Boolean
  2747. */
  2748. this.isCommandEnabled = function(name, dstHash) {
  2749. var disabled, cmd,
  2750. cvid = self.cwd().volumeid || '';
  2751. // In serach results use selected item hash to check
  2752. if (!dstHash && self.searchStatus.state > 1 && self.selected().length) {
  2753. dstHash = self.selected()[0];
  2754. }
  2755. if (dstHash && (! cvid || dstHash.indexOf(cvid) !== 0)) {
  2756. disabled = self.option('disabledFlip', dstHash);
  2757. //if (! disabled) {
  2758. // disabled = {};
  2759. //}
  2760. } else {
  2761. disabled = cwdOptions.disabledFlip/* || {}*/;
  2762. }
  2763. cmd = this._commands[name];
  2764. return cmd ? (cmd.alwaysEnabled || !disabled[name]) : false;
  2765. };
  2766. /**
  2767. * Exec command and return result;
  2768. *
  2769. * @param String command name
  2770. * @param String|Array usualy files hashes
  2771. * @param String|Array command options
  2772. * @param String|void hash for enabled check of own volume's disabled cmds
  2773. * @return $.Deferred
  2774. */
  2775. this.exec = function(cmd, files, opts, dstHash) {
  2776. var dfrd, resType;
  2777. // apply commandMap for keyboard shortcut
  2778. if (!dstHash && this.commandMap[cmd] && this.commandMap[cmd] !== 'hidden') {
  2779. cmd = this.commandMap[cmd];
  2780. }
  2781. if (cmd === 'open') {
  2782. if (this.searchStatus.state || this.searchStatus.ininc) {
  2783. this.trigger('searchend', { noupdate: true });
  2784. }
  2785. this.autoSync('stop');
  2786. }
  2787. if (!dstHash && files) {
  2788. if ($.isArray(files)) {
  2789. if (files.length) {
  2790. dstHash = files[0];
  2791. }
  2792. } else {
  2793. dstHash = files;
  2794. }
  2795. }
  2796. dfrd = this._commands[cmd] && this.isCommandEnabled(cmd, dstHash)
  2797. ? this._commands[cmd].exec(files, opts)
  2798. : $.Deferred().reject('No such command');
  2799. resType = typeof dfrd;
  2800. if (!(resType === 'object' && dfrd.promise)) {
  2801. self.debug('warning', '"cmd.exec()" should be returned "$.Deferred" but cmd "' + cmd + '" returned "' + resType + '"');
  2802. dfrd = $.Deferred().resolve();
  2803. }
  2804. this.trigger('exec', { dfrd : dfrd, cmd : cmd, files : files, opts : opts, dstHash : dstHash });
  2805. return dfrd;
  2806. };
  2807. /**
  2808. * Create and return dialog.
  2809. *
  2810. * @param String|DOMElement dialog content
  2811. * @param Object dialog options
  2812. * @return jQuery
  2813. */
  2814. this.dialog = function(content, options) {
  2815. var dialog = $('<div/>').append(content).appendTo(node).elfinderdialog(options, self),
  2816. dnode = dialog.closest('.ui-dialog'),
  2817. resize = function(){
  2818. ! dialog.data('draged') && dialog.is(':visible') && dialog.elfinderdialog('posInit');
  2819. };
  2820. if (dnode.length) {
  2821. self.bind('resize', resize);
  2822. dnode.on('remove', function() {
  2823. self.unbind('resize', resize);
  2824. });
  2825. }
  2826. return dialog;
  2827. };
  2828. /**
  2829. * Create and return toast.
  2830. *
  2831. * @param Object toast options - see ui/toast.js
  2832. * @return jQuery
  2833. */
  2834. this.toast = function(options) {
  2835. return $('<div class="ui-front"/>').appendTo(this.ui.toast).elfindertoast(options || {}, this);
  2836. };
  2837. /**
  2838. * Return UI widget or node
  2839. *
  2840. * @param String ui name
  2841. * @return jQuery
  2842. */
  2843. this.getUI = function(ui) {
  2844. return this.ui[ui] || (ui? $() : node);
  2845. };
  2846. /**
  2847. * Return elFinder.command instance or instances array
  2848. *
  2849. * @param String command name
  2850. * @return Object | Array
  2851. */
  2852. this.getCommand = function(name) {
  2853. return name === void(0) ? this._commands : this._commands[name];
  2854. };
  2855. /**
  2856. * Resize elfinder node
  2857. *
  2858. * @param String|Number width
  2859. * @param String|Number height
  2860. * @return void
  2861. */
  2862. this.resize = function(w, h) {
  2863. var getMargin = function() {
  2864. var m = node.outerHeight(true) - node.innerHeight(),
  2865. p = node;
  2866. while(p.get(0) !== heightBase.get(0)) {
  2867. p = p.parent();
  2868. m += p.outerHeight(true) - p.innerHeight();
  2869. if (! p.parent().length) {
  2870. // reached the document
  2871. break;
  2872. }
  2873. }
  2874. return m;
  2875. },
  2876. fit = ! node.hasClass('ui-resizable'),
  2877. prv = node.data('resizeSize') || {w: 0, h: 0},
  2878. mt, size = {};
  2879. if (heightBase && heightBase.data('resizeTm')) {
  2880. clearTimeout(heightBase.data('resizeTm'));
  2881. }
  2882. if (typeof h === 'string') {
  2883. if (mt = h.match(/^([0-9.]+)%$/)) {
  2884. // setup heightBase
  2885. if (! heightBase || ! heightBase.length) {
  2886. heightBase = $(window);
  2887. }
  2888. if (! heightBase.data('marginToMyNode')) {
  2889. heightBase.data('marginToMyNode', getMargin());
  2890. }
  2891. if (! heightBase.data('fitToBaseFunc')) {
  2892. heightBase.data('fitToBaseFunc', function(e) {
  2893. var tm = heightBase.data('resizeTm');
  2894. e.preventDefault();
  2895. e.stopPropagation();
  2896. tm && cancelAnimationFrame(tm);
  2897. if (! node.hasClass('elfinder-fullscreen') && (!self.UA.Mobile || heightBase.data('rotated') !== self.UA.Rotated)) {
  2898. heightBase.data('rotated', self.UA.Rotated);
  2899. heightBase.data('resizeTm', requestAnimationFrame(function() {
  2900. self.restoreSize();
  2901. }));
  2902. }
  2903. });
  2904. }
  2905. if (typeof heightBase.data('rotated') === 'undefined') {
  2906. heightBase.data('rotated', self.UA.Rotated);
  2907. }
  2908. h = heightBase.height() * (mt[1] / 100) - heightBase.data('marginToMyNode');
  2909. heightBase.off('resize.' + self.namespace, heightBase.data('fitToBaseFunc'));
  2910. fit && heightBase.on('resize.' + self.namespace, heightBase.data('fitToBaseFunc'));
  2911. }
  2912. }
  2913. node.css({ width : w, height : parseInt(h) });
  2914. size.w = Math.round(node.width());
  2915. size.h = Math.round(node.height());
  2916. node.data('resizeSize', size);
  2917. if (size.w !== prv.w || size.h !== prv.h) {
  2918. node.trigger('resize');
  2919. this.trigger('resize', {width : size.w, height : size.h});
  2920. }
  2921. };
  2922. /**
  2923. * Restore elfinder node size
  2924. *
  2925. * @return elFinder
  2926. */
  2927. this.restoreSize = function() {
  2928. this.resize(width, height);
  2929. };
  2930. this.show = function() {
  2931. node.show();
  2932. this.enable().trigger('show');
  2933. };
  2934. this.hide = function() {
  2935. if (this.options.enableAlways) {
  2936. prevEnabled = enabled;
  2937. enabled = false;
  2938. }
  2939. this.disable();
  2940. this.trigger('hide');
  2941. node.hide();
  2942. };
  2943. /**
  2944. * Lazy execution function
  2945. *
  2946. * @param Object function
  2947. * @param Number delay
  2948. * @param Object options
  2949. * @return Object jQuery.Deferred
  2950. */
  2951. this.lazy = function(func, delay, opts) {
  2952. var busy = function(state) {
  2953. var cnt = node.data('lazycnt'),
  2954. repaint;
  2955. if (state) {
  2956. repaint = node.data('lazyrepaint')? false : opts.repaint;
  2957. if (! cnt) {
  2958. node.data('lazycnt', 1)
  2959. .addClass('elfinder-processing');
  2960. } else {
  2961. node.data('lazycnt', ++cnt);
  2962. }
  2963. if (repaint) {
  2964. node.data('lazyrepaint', true).css('display'); // force repaint
  2965. }
  2966. } else {
  2967. if (cnt && cnt > 1) {
  2968. node.data('lazycnt', --cnt);
  2969. } else {
  2970. repaint = node.data('lazyrepaint');
  2971. node.data('lazycnt', 0)
  2972. .removeData('lazyrepaint')
  2973. .removeClass('elfinder-processing');
  2974. repaint && node.css('display'); // force repaint;
  2975. self.trigger('lazydone');
  2976. }
  2977. }
  2978. },
  2979. dfd = $.Deferred(),
  2980. callFunc = function() {
  2981. dfd.resolve(func.call(dfd));
  2982. busy(false);
  2983. };
  2984. delay = delay || 0;
  2985. opts = opts || {};
  2986. busy(true);
  2987. if (delay) {
  2988. setTimeout(callFunc, delay);
  2989. } else {
  2990. requestAnimationFrame(callFunc);
  2991. }
  2992. return dfd;
  2993. };
  2994. /**
  2995. * Destroy this elFinder instance
  2996. *
  2997. * @return void
  2998. **/
  2999. this.destroy = function() {
  3000. if (node && node[0].elfinder) {
  3001. node.hasClass('elfinder-fullscreen') && self.toggleFullscreen(node);
  3002. this.options.syncStart = false;
  3003. this.autoSync('forcestop');
  3004. this.trigger('destroy').disable();
  3005. clipboard = [];
  3006. selected = [];
  3007. listeners = {};
  3008. shortcuts = {};
  3009. $(window).off('.' + namespace);
  3010. $(document).off('.' + namespace);
  3011. self.trigger = function(){};
  3012. $(beeper).remove();
  3013. node.off()
  3014. .removeData()
  3015. .empty()
  3016. .append(prevContent.contents())
  3017. .attr('class', prevContent.attr('class'))
  3018. .attr('style', prevContent.attr('style'));
  3019. delete node[0].elfinder;
  3020. // restore kept events
  3021. $.each(prevEvents, function(n, arr) {
  3022. $.each(arr, function(i, o) {
  3023. node.on(o.type + (o.namespace? '.'+o.namespace : ''), o.selector, o.handler);
  3024. });
  3025. });
  3026. }
  3027. };
  3028. /**
  3029. * Start or stop auto sync
  3030. *
  3031. * @param String|Bool stop
  3032. * @return void
  3033. */
  3034. this.autoSync = function(mode) {
  3035. var sync;
  3036. if (self.options.sync >= 1000) {
  3037. if (syncInterval) {
  3038. clearTimeout(syncInterval);
  3039. syncInterval = null;
  3040. self.trigger('autosync', {action : 'stop'});
  3041. }
  3042. if (mode === 'stop') {
  3043. ++autoSyncStop;
  3044. } else {
  3045. autoSyncStop = Math.max(0, --autoSyncStop);
  3046. }
  3047. if (autoSyncStop || mode === 'forcestop' || ! self.options.syncStart) {
  3048. return;
  3049. }
  3050. // run interval sync
  3051. sync = function(start){
  3052. var timeout;
  3053. if (cwdOptions.syncMinMs && (start || syncInterval)) {
  3054. start && self.trigger('autosync', {action : 'start'});
  3055. timeout = Math.max(self.options.sync, cwdOptions.syncMinMs);
  3056. syncInterval && clearTimeout(syncInterval);
  3057. syncInterval = setTimeout(function() {
  3058. var dosync = true, hash = cwd, cts;
  3059. if (cwdOptions.syncChkAsTs && files[hash] && (cts = files[hash].ts)) {
  3060. self.request({
  3061. data : {cmd : 'info', targets : [hash], compare : cts, reload : 1},
  3062. preventDefault : true
  3063. })
  3064. .done(function(data){
  3065. var ts;
  3066. dosync = true;
  3067. if (data.compare) {
  3068. ts = data.compare;
  3069. if (ts == cts) {
  3070. dosync = false;
  3071. }
  3072. }
  3073. if (dosync) {
  3074. self.sync(hash).always(function(){
  3075. if (ts) {
  3076. // update ts for cache clear etc.
  3077. files[hash].ts = ts;
  3078. }
  3079. sync();
  3080. });
  3081. } else {
  3082. sync();
  3083. }
  3084. })
  3085. .fail(function(error, xhr){
  3086. var err = self.parseError(error);
  3087. if (err && xhr.status != 0) {
  3088. self.error(err);
  3089. if (Array.isArray(err) && $.inArray('errOpen', err) !== -1) {
  3090. self.request({
  3091. data : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1},
  3092. notify : {type : 'open', cnt : 1, hideCnt : true}
  3093. });
  3094. }
  3095. } else {
  3096. syncInterval = setTimeout(function() {
  3097. sync();
  3098. }, timeout);
  3099. }
  3100. });
  3101. } else {
  3102. self.sync(cwd, true).always(function(){
  3103. sync();
  3104. });
  3105. }
  3106. }, timeout);
  3107. }
  3108. };
  3109. sync(true);
  3110. }
  3111. };
  3112. /**
  3113. * Return bool is inside work zone of specific point
  3114. *
  3115. * @param Number event.pageX
  3116. * @param Number event.pageY
  3117. * @return Bool
  3118. */
  3119. this.insideWorkzone = function(x, y, margin) {
  3120. var rectangle = this.getUI('workzone').data('rectangle');
  3121. margin = margin || 1;
  3122. if (x < rectangle.left + margin
  3123. || x > rectangle.left + rectangle.width + margin
  3124. || y < rectangle.top + margin
  3125. || y > rectangle.top + rectangle.height + margin) {
  3126. return false;
  3127. }
  3128. return true;
  3129. };
  3130. /**
  3131. * Target ui node move to last of children of elFinder node fot to show front
  3132. *
  3133. * @param Object target Target jQuery node object
  3134. */
  3135. this.toFront = function(target) {
  3136. var nodes = node.children('.ui-front').removeClass('elfinder-frontmost'),
  3137. lastnode = nodes.last();
  3138. nodes.css('z-index', '');
  3139. $(target).addClass('ui-front elfinder-frontmost').css('z-index', lastnode.css('z-index') + 1);
  3140. };
  3141. /**
  3142. * Remove class 'elfinder-frontmost' and hide() to target ui node
  3143. *
  3144. * @param Object target Target jQuery node object
  3145. * @param Boolean nohide Do not hide
  3146. */
  3147. this.toHide =function(target, nohide) {
  3148. var tgt = $(target),
  3149. last;
  3150. !nohide && tgt.hide();
  3151. if (tgt.hasClass('elfinder-frontmost')) {
  3152. tgt.removeClass('elfinder-frontmost');
  3153. last = node.children('.ui-front:visible:not(.elfinder-frontmost)').last();
  3154. if (last.length) {
  3155. requestAnimationFrame(function() {
  3156. if (!node.children('.elfinder-frontmost:visible').length) {
  3157. self.toFront(last);
  3158. last.trigger('frontmost');
  3159. }
  3160. });
  3161. }
  3162. }
  3163. };
  3164. /**
  3165. * Return css object for maximize
  3166. *
  3167. * @return Object
  3168. */
  3169. this.getMaximizeCss = function() {
  3170. return {
  3171. width : '100%',
  3172. height : '100%',
  3173. margin : 0,
  3174. top : 0,
  3175. left : 0,
  3176. display : 'block',
  3177. position: 'fixed',
  3178. zIndex : Math.max(self.zIndex? (self.zIndex + 1) : 0 , 1000),
  3179. maxWidth : '',
  3180. maxHeight: ''
  3181. };
  3182. };
  3183. // Closure for togglefullscreen
  3184. (function() {
  3185. // check is in iframe
  3186. if (inFrame && self.UA.Fullscreen) {
  3187. self.UA.Fullscreen = false;
  3188. if (parentIframe && typeof parentIframe.attr('allowfullscreen') !== 'undefined') {
  3189. self.UA.Fullscreen = true;
  3190. }
  3191. }
  3192. var orgStyle, bodyOvf, resizeTm, fullElm, exitFull, toFull,
  3193. cls = 'elfinder-fullscreen',
  3194. clsN = 'elfinder-fullscreen-native',
  3195. checkDialog = function() {
  3196. var t = 0,
  3197. l = 0;
  3198. $.each(node.children('.ui-dialog,.ui-draggable'), function(i, d) {
  3199. var $d = $(d),
  3200. pos = $d.position();
  3201. if (pos.top < 0) {
  3202. $d.css('top', t);
  3203. t += 20;
  3204. }
  3205. if (pos.left < 0) {
  3206. $d.css('left', l);
  3207. l += 20;
  3208. }
  3209. });
  3210. },
  3211. funcObj = self.UA.Fullscreen? {
  3212. // native full screen mode
  3213. fullElm: function() {
  3214. return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement || null;
  3215. },
  3216. exitFull: function() {
  3217. if (document.exitFullscreen) {
  3218. return document.exitFullscreen();
  3219. } else if (document.webkitExitFullscreen) {
  3220. return document.webkitExitFullscreen();
  3221. } else if (document.mozCancelFullScreen) {
  3222. return document.mozCancelFullScreen();
  3223. } else if (document.msExitFullscreen) {
  3224. return document.msExitFullscreen();
  3225. }
  3226. },
  3227. toFull: function(elem) {
  3228. if (elem.requestFullscreen) {
  3229. return elem.requestFullscreen();
  3230. } else if (elem.webkitRequestFullscreen) {
  3231. return elem.webkitRequestFullscreen();
  3232. } else if (elem.mozRequestFullScreen) {
  3233. return elem.mozRequestFullScreen();
  3234. } else if (elem.msRequestFullscreen) {
  3235. return elem.msRequestFullscreen();
  3236. }
  3237. return false;
  3238. }
  3239. } : {
  3240. // node element maximize mode
  3241. fullElm: function() {
  3242. var full;
  3243. if (node.hasClass(cls)) {
  3244. return node.get(0);
  3245. } else {
  3246. full = node.find('.' + cls);
  3247. if (full.length) {
  3248. return full.get(0);
  3249. }
  3250. }
  3251. return null;
  3252. },
  3253. exitFull: function() {
  3254. var elm;
  3255. $(window).off('resize.' + namespace, resize);
  3256. if (bodyOvf !== void(0)) {
  3257. $('body').css('overflow', bodyOvf);
  3258. }
  3259. bodyOvf = void(0);
  3260. if (orgStyle) {
  3261. elm = orgStyle.elm;
  3262. restoreStyle(elm);
  3263. $(elm).trigger('resize', {fullscreen: 'off'});
  3264. }
  3265. $(window).trigger('resize');
  3266. },
  3267. toFull: function(elem) {
  3268. bodyOvf = $('body').css('overflow') || '';
  3269. $('body').css('overflow', 'hidden');
  3270. $(elem).css(self.getMaximizeCss())
  3271. .addClass(cls)
  3272. .trigger('resize', {fullscreen: 'on'});
  3273. checkDialog();
  3274. $(window).on('resize.' + namespace, resize).trigger('resize');
  3275. return true;
  3276. }
  3277. },
  3278. restoreStyle = function(elem) {
  3279. if (orgStyle && orgStyle.elm == elem) {
  3280. $(elem).removeClass(cls + ' ' + clsN).attr('style', orgStyle.style);
  3281. orgStyle = null;
  3282. }
  3283. },
  3284. resize = function(e) {
  3285. var elm;
  3286. if (e.target === window) {
  3287. resizeTm && cancelAnimationFrame(resizeTm);
  3288. resizeTm = requestAnimationFrame(function() {
  3289. if (elm = funcObj.fullElm()) {
  3290. $(elm).trigger('resize', {fullscreen: 'on'});
  3291. }
  3292. });
  3293. }
  3294. };
  3295. $(document).on('fullscreenchange.' + namespace + ' webkitfullscreenchange.' + namespace + ' mozfullscreenchange.' + namespace + ' MSFullscreenChange.' + namespace, function(e){
  3296. if (self.UA.Fullscreen) {
  3297. var elm = funcObj.fullElm(),
  3298. win = $(window);
  3299. resizeTm && cancelAnimationFrame(resizeTm);
  3300. if (elm === null) {
  3301. win.off('resize.' + namespace, resize);
  3302. if (orgStyle) {
  3303. elm = orgStyle.elm;
  3304. restoreStyle(elm);
  3305. $(elm).trigger('resize', {fullscreen: 'off'});
  3306. }
  3307. } else {
  3308. $(elm).addClass(cls + ' ' + clsN)
  3309. .attr('style', 'width:100%; height:100%; margin:0; padding:0;')
  3310. .trigger('resize', {fullscreen: 'on'});
  3311. win.on('resize.' + namespace, resize);
  3312. checkDialog();
  3313. }
  3314. win.trigger('resize');
  3315. }
  3316. });
  3317. /**
  3318. * Toggle Full Scrren Mode
  3319. *
  3320. * @param Object target
  3321. * @param Bool full
  3322. * @return Object | Null DOM node object of current full scrren
  3323. */
  3324. self.toggleFullscreen = function(target, full) {
  3325. var elm = $(target).get(0),
  3326. curElm = null;
  3327. curElm = funcObj.fullElm();
  3328. if (curElm) {
  3329. if (curElm == elm) {
  3330. if (full === true) {
  3331. return curElm;
  3332. }
  3333. } else {
  3334. if (full === false) {
  3335. return curElm;
  3336. }
  3337. }
  3338. funcObj.exitFull();
  3339. return null;
  3340. } else {
  3341. if (full === false) {
  3342. return null;
  3343. }
  3344. }
  3345. orgStyle = {elm: elm, style: $(elm).attr('style')};
  3346. if (funcObj.toFull(elm) !== false) {
  3347. return elm;
  3348. } else {
  3349. orgStyle = null;
  3350. return null;
  3351. }
  3352. };
  3353. })();
  3354. // Closure for toggleMaximize
  3355. (function(){
  3356. var cls = 'elfinder-maximized',
  3357. resizeTm,
  3358. resize = function(e) {
  3359. if (e.target === window && e.data && e.data.elm) {
  3360. var elm = e.data.elm;
  3361. resizeTm && cancelAnimationFrame(resizeTm);
  3362. resizeTm = requestAnimationFrame(function() {
  3363. elm.trigger('resize', {maximize: 'on'});
  3364. });
  3365. }
  3366. },
  3367. exitMax = function(elm) {
  3368. $(window).off('resize.' + namespace, resize);
  3369. $('body').css('overflow', elm.data('bodyOvf'));
  3370. elm.removeClass(cls)
  3371. .attr('style', elm.data('orgStyle'))
  3372. .removeData('bodyOvf')
  3373. .removeData('orgStyle');
  3374. elm.trigger('resize', {maximize: 'off'});
  3375. },
  3376. toMax = function(elm) {
  3377. elm.data('bodyOvf', $('body').css('overflow') || '')
  3378. .data('orgStyle', elm.attr('style'))
  3379. .addClass(cls)
  3380. .css(self.getMaximizeCss());
  3381. $('body').css('overflow', 'hidden');
  3382. $(window).on('resize.' + namespace, {elm: elm}, resize);
  3383. elm.trigger('resize', {maximize: 'on'});
  3384. };
  3385. /**
  3386. * Toggle Maximize target node
  3387. *
  3388. * @param Object target
  3389. * @param Bool max
  3390. * @return void
  3391. */
  3392. self.toggleMaximize = function(target, max) {
  3393. var elm = $(target),
  3394. maximized = elm.hasClass(cls);
  3395. if (maximized) {
  3396. if (max === true) {
  3397. return;
  3398. }
  3399. exitMax(elm);
  3400. } else {
  3401. if (max === false) {
  3402. return;
  3403. }
  3404. toMax(elm);
  3405. }
  3406. };
  3407. })();
  3408. /************* init stuffs ****************/
  3409. Object.assign($.ui.keyCode, {
  3410. 'F1' : 112,
  3411. 'F2' : 113,
  3412. 'F3' : 114,
  3413. 'F4' : 115,
  3414. 'F5' : 116,
  3415. 'F6' : 117,
  3416. 'F7' : 118,
  3417. 'F8' : 119,
  3418. 'F9' : 120,
  3419. 'F10' : 121,
  3420. 'F11' : 122,
  3421. 'F12' : 123,
  3422. 'DIG0' : 48,
  3423. 'DIG1' : 49,
  3424. 'DIG2' : 50,
  3425. 'DIG3' : 51,
  3426. 'DIG4' : 52,
  3427. 'DIG5' : 53,
  3428. 'DIG6' : 54,
  3429. 'DIG7' : 55,
  3430. 'DIG8' : 56,
  3431. 'DIG9' : 57,
  3432. 'NUM0' : 96,
  3433. 'NUM1' : 97,
  3434. 'NUM2' : 98,
  3435. 'NUM3' : 99,
  3436. 'NUM4' : 100,
  3437. 'NUM5' : 101,
  3438. 'NUM6' : 102,
  3439. 'NUM7' : 103,
  3440. 'NUM8' : 104,
  3441. 'NUM9' : 105,
  3442. 'CONTEXTMENU' : 93,
  3443. 'DOT' : 190
  3444. });
  3445. this.dragUpload = false;
  3446. this.xhrUpload = (typeof XMLHttpRequestUpload != 'undefined' || typeof XMLHttpRequestEventTarget != 'undefined') && typeof File != 'undefined' && typeof FormData != 'undefined';
  3447. // configure transport object
  3448. this.transport = {};
  3449. if (typeof(this.options.transport) == 'object') {
  3450. this.transport = this.options.transport;
  3451. if (typeof(this.transport.init) == 'function') {
  3452. this.transport.init(this);
  3453. }
  3454. }
  3455. if (typeof(this.transport.send) != 'function') {
  3456. this.transport.send = function(opts) {
  3457. if (!self.UA.IE) {
  3458. // keep native xhr object for handling property responseURL
  3459. opts._xhr = new XMLHttpRequest();
  3460. opts.xhr = function() { return opts._xhr; };
  3461. }
  3462. return $.ajax(opts);
  3463. };
  3464. }
  3465. if (this.transport.upload == 'iframe') {
  3466. this.transport.upload = $.proxy(this.uploads.iframe, this);
  3467. } else if (typeof(this.transport.upload) == 'function') {
  3468. this.dragUpload = !!this.options.dragUploadAllow;
  3469. } else if (this.xhrUpload && !!this.options.dragUploadAllow) {
  3470. this.transport.upload = $.proxy(this.uploads.xhr, this);
  3471. this.dragUpload = true;
  3472. } else {
  3473. this.transport.upload = $.proxy(this.uploads.iframe, this);
  3474. }
  3475. /**
  3476. * Decoding 'raw' string converted to unicode
  3477. *
  3478. * @param String str
  3479. * @return String
  3480. */
  3481. this.decodeRawString = function(str) {
  3482. var charCodes = function(str) {
  3483. var i, len, arr;
  3484. for (i=0,len=str.length,arr=[]; i<len; i++) {
  3485. arr.push(str.charCodeAt(i));
  3486. }
  3487. return arr;
  3488. },
  3489. scalarValues = function(arr) {
  3490. var scalars = [], i, len, c;
  3491. if (typeof arr === 'string') {arr = charCodes(arr);}
  3492. for (i=0,len=arr.length; c=arr[i],i<len; i++) {
  3493. if (c >= 0xd800 && c <= 0xdbff) {
  3494. scalars.push((c & 1023) + 64 << 10 | arr[++i] & 1023);
  3495. } else {
  3496. scalars.push(c);
  3497. }
  3498. }
  3499. return scalars;
  3500. },
  3501. decodeUTF8 = function(arr) {
  3502. var i, len, c, str, char = String.fromCharCode;
  3503. for (i=0,len=arr.length,str=""; c=arr[i],i<len; i++) {
  3504. if (c <= 0x7f) {
  3505. str += char(c);
  3506. } else if (c <= 0xdf && c >= 0xc2) {
  3507. str += char((c&31)<<6 | arr[++i]&63);
  3508. } else if (c <= 0xef && c >= 0xe0) {
  3509. str += char((c&15)<<12 | (arr[++i]&63)<<6 | arr[++i]&63);
  3510. } else if (c <= 0xf7 && c >= 0xf0) {
  3511. str += char(
  3512. 0xd800 | ((c&7)<<8 | (arr[++i]&63)<<2 | arr[++i]>>>4&3) - 64,
  3513. 0xdc00 | (arr[i++]&15)<<6 | arr[i]&63
  3514. );
  3515. } else {
  3516. str += char(0xfffd);
  3517. }
  3518. }
  3519. return str;
  3520. };
  3521. return decodeUTF8(scalarValues(str));
  3522. };
  3523. /**
  3524. * Gets target file contents by file.hash
  3525. *
  3526. * @param String hash The hash
  3527. * @param String responseType 'blob' or 'arraybuffer' (default)
  3528. * @return arraybuffer|blob The contents.
  3529. */
  3530. this.getContents = function(hash, responseType) {
  3531. var self = this,
  3532. dfd = $.Deferred(),
  3533. type = responseType || 'arraybuffer',
  3534. url, req;
  3535. dfd.fail(function() {
  3536. req && req.state() === 'pending' && req.reject();
  3537. });
  3538. url = self.openUrl(hash);
  3539. if (!self.isSameOrigin(url)) {
  3540. url = self.openUrl(hash, true);
  3541. }
  3542. req = self.request({
  3543. data : {cmd : 'get'},
  3544. options : {
  3545. url: url,
  3546. type: 'get',
  3547. cache : true,
  3548. dataType : 'binary',
  3549. responseType : type,
  3550. processData: false
  3551. }
  3552. })
  3553. .fail(function() {
  3554. dfd.reject();
  3555. })
  3556. .done(function(data) {
  3557. dfd.resolve(data);
  3558. });
  3559. return dfd;
  3560. };
  3561. this.getMimetype = function(name, orgMime) {
  3562. var mime = orgMime,
  3563. ext, m;
  3564. m = (name + '').match(/\.([^.]+)$/);
  3565. if (m && (ext = m[1])) {
  3566. if (!extToMimeTable) {
  3567. extToMimeTable = self.arrayFlip(self.mimeTypes);
  3568. }
  3569. if (!(mime = extToMimeTable[ext.toLowerCase()])) {
  3570. mime = orgMime;
  3571. }
  3572. }
  3573. return mime;
  3574. };
  3575. /**
  3576. * Supported check hash algorisms
  3577. *
  3578. * @type Array
  3579. */
  3580. self.hashCheckers = [];
  3581. /**
  3582. * Closure of getContentsHashes()
  3583. */
  3584. (function(self) {
  3585. var hashLibs = {
  3586. check : true
  3587. },
  3588. md5Calc = function(arr) {
  3589. var spark = new hashLibs.SparkMD5.ArrayBuffer(),
  3590. job;
  3591. job = self.asyncJob(function(buf) {
  3592. spark.append(buf);
  3593. }, arr).done(function() {
  3594. job._md5 = spark.end();
  3595. });
  3596. return job;
  3597. },
  3598. shaCalc = function(arr, length) {
  3599. var sha, job;
  3600. try {
  3601. sha = new hashLibs.jsSHA('SHA' + (length.substr(0, 1) === '3'? length : ('-' + length)), 'ARRAYBUFFER');
  3602. job = self.asyncJob(function(buf) {
  3603. sha.update(buf);
  3604. }, arr).done(function() {
  3605. job._sha = sha.getHash('HEX');
  3606. });
  3607. } catch(e) {
  3608. job = $.Deferred.reject();
  3609. }
  3610. return job;
  3611. };
  3612. // make fm.hashCheckers
  3613. if (self.options.cdns.sparkmd5) {
  3614. self.hashCheckers.push('md5');
  3615. }
  3616. if (self.options.cdns.jssha) {
  3617. self.hashCheckers = self.hashCheckers.concat(['sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'shake128', 'shake256']);
  3618. }
  3619. /**
  3620. * Gets the contents hashes.
  3621. *
  3622. * @param String target target file.hash
  3623. * @param Object needHashes need hash lib names
  3624. * @return Object hashes with lib name as key
  3625. */
  3626. self.getContentsHashes = function(target, needHashes) {
  3627. var dfd = $.Deferred(),
  3628. needs = self.arrayFlip(needHashes || ['md5'], true),
  3629. libs = [],
  3630. jobs = [],
  3631. res = {},
  3632. req;
  3633. dfd.fail(function() {
  3634. req && req.reject();
  3635. });
  3636. if (hashLibs.check) {
  3637. delete hashLibs.check;
  3638. // load SparkMD5
  3639. var libsmd5 = $.Deferred();
  3640. if (window.ArrayBuffer && self.options.cdns.sparkmd5) {
  3641. libs.push(libsmd5);
  3642. self.loadScript([self.options.cdns.sparkmd5],
  3643. function(res) {
  3644. var SparkMD5 = res || window.SparkMD5;
  3645. window.SparkMD5 && delete window.SparkMD5;
  3646. libsmd5.resolve();
  3647. if (SparkMD5) {
  3648. hashLibs.SparkMD5 = SparkMD5;
  3649. }
  3650. },
  3651. {
  3652. tryRequire: true,
  3653. error: function() {
  3654. libsmd5.reject();
  3655. }
  3656. }
  3657. );
  3658. }
  3659. // load jsSha
  3660. var libssha = $.Deferred();
  3661. if (window.ArrayBuffer && self.options.cdns.jssha) {
  3662. libs.push(libssha);
  3663. self.loadScript([self.options.cdns.jssha],
  3664. function(res) {
  3665. var jsSHA = res || window.jsSHA;
  3666. window.jsSHA && delete window.jsSHA;
  3667. libssha.resolve();
  3668. if (jsSHA) {
  3669. hashLibs.jsSHA = jsSHA;
  3670. }
  3671. },
  3672. {
  3673. tryRequire: true,
  3674. error: function() {
  3675. libssha.reject();
  3676. }
  3677. }
  3678. );
  3679. }
  3680. }
  3681. $.when.apply(null, libs).always(function() {
  3682. if (Object.keys(hashLibs).length) {
  3683. req = self.getContents(target).done(function(arrayBuffer) {
  3684. var arr = (arrayBuffer instanceof ArrayBuffer && arrayBuffer.byteLength > 0)? self.sliceArrayBuffer(arrayBuffer, 1048576) : false,
  3685. i;
  3686. if (needs.md5 && hashLibs.SparkMD5) {
  3687. jobs.push(function() {
  3688. var job = md5Calc(arr).done(function() {
  3689. var f;
  3690. res.md5 = job._md5;
  3691. if (f = self.file(target)) {
  3692. f.md5 = job._md5;
  3693. }
  3694. dfd.notify(res);
  3695. });
  3696. dfd.fail(function() {
  3697. job.reject();
  3698. });
  3699. return job;
  3700. });
  3701. }
  3702. if (hashLibs.jsSHA) {
  3703. $.each(['1', '224', '256', '384', '512', '3-224', '3-256', '3-384', '3-512', 'ke128', 'ke256'], function(i, v) {
  3704. if (needs['sha' + v]) {
  3705. jobs.push(function() {
  3706. var job = shaCalc(arr, v).done(function() {
  3707. var f;
  3708. res['sha' + v] = job._sha;
  3709. if (f = self.file(target)) {
  3710. f['sha' + v] = job._sha;
  3711. }
  3712. dfd.notify(res);
  3713. });
  3714. return job;
  3715. });
  3716. }
  3717. });
  3718. }
  3719. if (jobs.length) {
  3720. self.sequence(jobs).always(function() {
  3721. dfd.resolve(res);
  3722. });
  3723. } else {
  3724. dfd.reject();
  3725. }
  3726. }).fail(function() {
  3727. dfd.reject();
  3728. });
  3729. } else {
  3730. dfd.reject();
  3731. }
  3732. });
  3733. return dfd;
  3734. };
  3735. })(this);
  3736. /**
  3737. * Parse error value to display
  3738. *
  3739. * @param Mixed error
  3740. * @return Mixed parsed error
  3741. */
  3742. this.parseError = function(error) {
  3743. var arg = error;
  3744. if ($.isPlainObject(arg)) {
  3745. arg = arg.error;
  3746. }
  3747. return arg;
  3748. };
  3749. /**
  3750. * Alias for this.trigger('error', {error : 'message'})
  3751. *
  3752. * @param String error message
  3753. * @return elFinder
  3754. **/
  3755. this.error = function() {
  3756. var arg = arguments[0],
  3757. opts = arguments[1] || null,
  3758. err;
  3759. if (arguments.length == 1 && typeof(arg) === 'function') {
  3760. return self.bind('error', arg);
  3761. } else {
  3762. err = this.parseError(arg);
  3763. return (err === true || !err)? this : self.trigger('error', {error: err, opts : opts});
  3764. }
  3765. };
  3766. // create bind/trigger aliases for build-in events
  3767. $.each(events, function(i, name) {
  3768. self[name] = function() {
  3769. var arg = arguments[0];
  3770. return arguments.length == 1 && typeof(arg) == 'function'
  3771. ? self.bind(name, arg)
  3772. : self.trigger(name, $.isPlainObject(arg) ? arg : {});
  3773. };
  3774. });
  3775. // bind core event handlers
  3776. this
  3777. .enable(function() {
  3778. if (!enabled && self.api && self.visible() && self.ui.overlay.is(':hidden') && ! node.children('.elfinder-dialog.' + self.res('class', 'editing') + ':visible').length) {
  3779. enabled = true;
  3780. document.activeElement && document.activeElement.blur();
  3781. node.removeClass('elfinder-disabled');
  3782. }
  3783. })
  3784. .disable(function() {
  3785. prevEnabled = enabled;
  3786. enabled = false;
  3787. node.addClass('elfinder-disabled');
  3788. })
  3789. .open(function() {
  3790. selected = [];
  3791. })
  3792. .select(function(e) {
  3793. var cnt = 0,
  3794. unselects = [];
  3795. selected = $.grep(e.data.selected || e.data.value|| [], function(hash) {
  3796. if (unselects.length || (self.maxTargets && ++cnt > self.maxTargets)) {
  3797. unselects.push(hash);
  3798. return false;
  3799. } else {
  3800. return files[hash] ? true : false;
  3801. }
  3802. });
  3803. if (unselects.length) {
  3804. self.trigger('unselectfiles', {files: unselects, inselect: true});
  3805. self.toast({mode: 'warning', msg: self.i18n(['errMaxTargets', self.maxTargets])});
  3806. }
  3807. })
  3808. .error(function(e) {
  3809. var opts = {
  3810. cssClass : 'elfinder-dialog-error',
  3811. title : self.i18n(self.i18n('error')),
  3812. resizable : false,
  3813. destroyOnClose : true,
  3814. buttons : {}
  3815. };
  3816. opts.buttons[self.i18n(self.i18n('btnClose'))] = function() { $(this).elfinderdialog('close'); };
  3817. if (e.data.opts && $.isPlainObject(e.data.opts)) {
  3818. Object.assign(opts, e.data.opts);
  3819. }
  3820. self.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-error"/>'+self.i18n(e.data.error), opts);
  3821. })
  3822. .bind('tmb', function(e) {
  3823. $.each(e.data.images||[], function(hash, tmb) {
  3824. if (files[hash]) {
  3825. files[hash].tmb = tmb;
  3826. }
  3827. });
  3828. })
  3829. .bind('searchstart', function(e) {
  3830. Object.assign(self.searchStatus, e.data);
  3831. self.searchStatus.state = 1;
  3832. })
  3833. .bind('search', function(e) {
  3834. self.searchStatus.state = 2;
  3835. })
  3836. .bind('searchend', function() {
  3837. self.searchStatus.state = 0;
  3838. self.searchStatus.ininc = false;
  3839. self.searchStatus.mixed = false;
  3840. })
  3841. .bind('canMakeEmptyFile', function(e) {
  3842. var data = e.data,
  3843. obj = {};
  3844. if (data && Array.isArray(data.mimes)) {
  3845. if (!data.unshift) {
  3846. obj = self.mimesCanMakeEmpty;
  3847. }
  3848. $.each(data.mimes, function() {
  3849. if (!obj[this]) {
  3850. obj[this] = self.mimeTypes[this];
  3851. }
  3852. });
  3853. if (data.unshift) {
  3854. self.mimesCanMakeEmpty = Object.assign(obj, self.mimesCanMakeEmpty);
  3855. }
  3856. }
  3857. })
  3858. .bind('themechange', function() {
  3859. requestAnimationFrame(function() {
  3860. self.trigger('uiresize');
  3861. });
  3862. })
  3863. ;
  3864. // We listen and emit a sound on delete according to option
  3865. if (true === this.options.sound) {
  3866. this.bind('playsound', function(e) {
  3867. var play = beeper.canPlayType && beeper.canPlayType('audio/wav; codecs="1"'),
  3868. file = e.data && e.data.soundFile;
  3869. play && file && play != '' && play != 'no' && $(beeper).html('<source src="' + soundPath + file + '" type="audio/wav">')[0].play();
  3870. });
  3871. }
  3872. // bind external event handlers
  3873. $.each(this.options.handlers, function(event, callback) {
  3874. self.bind(event, callback);
  3875. });
  3876. /**
  3877. * History object. Store visited folders
  3878. *
  3879. * @type Object
  3880. **/
  3881. this.history = new this.history(this);
  3882. /**
  3883. * Root hashed
  3884. *
  3885. * @type Object
  3886. */
  3887. this.roots = {};
  3888. /**
  3889. * leaf roots
  3890. *
  3891. * @type Object
  3892. */
  3893. this.leafRoots = {};
  3894. this.volumeExpires = {};
  3895. /**
  3896. * Loaded commands
  3897. *
  3898. * @type Object
  3899. **/
  3900. this._commands = {};
  3901. if (!Array.isArray(this.options.commands)) {
  3902. this.options.commands = [];
  3903. }
  3904. if ($.inArray('*', this.options.commands) !== -1) {
  3905. this.options.commands = Object.keys(this.commands);
  3906. }
  3907. /**
  3908. * UI command map of cwd volume ( That volume driver option `uiCmdMap` )
  3909. *
  3910. * @type Object
  3911. **/
  3912. this.commandMap = {};
  3913. /**
  3914. * cwd options of each volume
  3915. * key: volumeid
  3916. * val: options object
  3917. *
  3918. * @type Object
  3919. */
  3920. this.volOptions = {};
  3921. /**
  3922. * Has volOptions data
  3923. *
  3924. * @type Boolean
  3925. */
  3926. this.hasVolOptions = false;
  3927. /**
  3928. * Hash of trash holders
  3929. * key: trash folder hash
  3930. * val: source volume hash
  3931. *
  3932. * @type Object
  3933. */
  3934. this.trashes = {};
  3935. /**
  3936. * cwd options of each folder/file
  3937. * key: hash
  3938. * val: options object
  3939. *
  3940. * @type Object
  3941. */
  3942. this.optionsByHashes = {};
  3943. /**
  3944. * UI Auto Hide Functions
  3945. * Each auto hide function mast be call to `fm.trigger('uiautohide')` at end of process
  3946. *
  3947. * @type Array
  3948. **/
  3949. this.uiAutoHide = [];
  3950. // trigger `uiautohide`
  3951. this.one('open', function() {
  3952. if (self.uiAutoHide.length) {
  3953. setTimeout(function() {
  3954. self.trigger('uiautohide');
  3955. }, 500);
  3956. }
  3957. });
  3958. // Auto Hide Functions sequential processing start
  3959. this.bind('uiautohide', function() {
  3960. if (self.uiAutoHide.length) {
  3961. self.uiAutoHide.shift()();
  3962. }
  3963. });
  3964. if (this.options.width) {
  3965. width = this.options.width;
  3966. }
  3967. if (this.options.height) {
  3968. height = this.options.height;
  3969. }
  3970. if (this.options.heightBase) {
  3971. heightBase = $(this.options.heightBase);
  3972. }
  3973. if (this.options.soundPath) {
  3974. soundPath = this.options.soundPath.replace(/\/+$/, '') + '/';
  3975. } else {
  3976. soundPath = this.baseUrl + soundPath;
  3977. }
  3978. self.one('opendone', function() {
  3979. var tm;
  3980. // attach events to document
  3981. $(document)
  3982. // disable elfinder on click outside elfinder
  3983. .on('click.'+namespace, function(e) { enabled && ! self.options.enableAlways && !$(e.target).closest(node).length && self.disable(); })
  3984. // exec shortcuts
  3985. .on(keydown+' '+keypress+' '+keyup+' '+mousedown, execShortcut);
  3986. // attach events to window
  3987. self.options.useBrowserHistory && $(window)
  3988. .on('popstate.' + namespace, function(ev) {
  3989. var state = ev.originalEvent.state || {},
  3990. hasThash = state.thash? true : false,
  3991. dialog = node.find('.elfinder-frontmost:visible'),
  3992. input = node.find('.elfinder-navbar-dir,.elfinder-cwd-filename').find('input,textarea'),
  3993. onOpen, toast;
  3994. if (!hasThash) {
  3995. state = { thash: self.cwd().hash };
  3996. // scroll to elFinder node
  3997. $('html,body').animate({ scrollTop: node.offset().top });
  3998. }
  3999. if (dialog.length || input.length) {
  4000. history.pushState(state, null, location.pathname + location.search + '#elf_' + state.thash);
  4001. if (dialog.length) {
  4002. if (!dialog.hasClass(self.res('class', 'preventback'))) {
  4003. if (dialog.hasClass('elfinder-contextmenu')) {
  4004. $(document).trigger($.Event('keydown', { keyCode: $.ui.keyCode.ESCAPE, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  4005. } else if (dialog.hasClass('elfinder-dialog')) {
  4006. dialog.elfinderdialog('close');
  4007. } else {
  4008. dialog.trigger('close');
  4009. }
  4010. }
  4011. } else {
  4012. input.trigger($.Event('keydown', { keyCode: $.ui.keyCode.ESCAPE, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  4013. }
  4014. } else {
  4015. if (hasThash) {
  4016. !$.isEmptyObject(self.files()) && self.request({
  4017. data : {cmd : 'open', target : state.thash, onhistory : 1},
  4018. notify : {type : 'open', cnt : 1, hideCnt : true},
  4019. syncOnFail : true
  4020. });
  4021. } else {
  4022. onOpen = function() {
  4023. toast.trigger('click');
  4024. };
  4025. self.one('open', onOpen, true);
  4026. toast = self.toast({
  4027. msg: self.i18n('pressAgainToExit'),
  4028. onHidden: function() {
  4029. self.unbind('open', onOpen);
  4030. history.pushState(state, null, location.pathname + location.search + '#elf_' + state.thash);
  4031. }
  4032. });
  4033. }
  4034. }
  4035. });
  4036. $(window).on('resize.' + namespace, function(e){
  4037. if (e.target === this) {
  4038. tm && cancelAnimationFrame(tm);
  4039. tm = requestAnimationFrame(function() {
  4040. var prv = node.data('resizeSize') || {w: 0, h: 0},
  4041. size = {w: Math.round(node.width()), h: Math.round(node.height())};
  4042. node.data('resizeSize', size);
  4043. if (size.w !== prv.w || size.h !== prv.h) {
  4044. node.trigger('resize');
  4045. self.trigger('resize', {width : size.w, height : size.h});
  4046. }
  4047. });
  4048. }
  4049. })
  4050. .on('beforeunload.' + namespace,function(e){
  4051. var msg, cnt;
  4052. if (node.is(':visible')) {
  4053. if (self.ui.notify.children().length && $.inArray('hasNotifyDialog', self.options.windowCloseConfirm) !== -1) {
  4054. msg = self.i18n('ntfsmth');
  4055. } else if (node.find('.'+self.res('class', 'editing')).length && $.inArray('editingFile', self.options.windowCloseConfirm) !== -1) {
  4056. msg = self.i18n('editingFile');
  4057. } else if ((cnt = Object.keys(self.selected()).length) && $.inArray('hasSelectedItem', self.options.windowCloseConfirm) !== -1) {
  4058. msg = self.i18n('hasSelected', ''+cnt);
  4059. } else if ((cnt = Object.keys(self.clipboard()).length) && $.inArray('hasClipboardData', self.options.windowCloseConfirm) !== -1) {
  4060. msg = self.i18n('hasClipboard', ''+cnt);
  4061. }
  4062. if (msg) {
  4063. e.returnValue = msg;
  4064. return msg;
  4065. }
  4066. }
  4067. self.trigger('unload');
  4068. });
  4069. // bind window onmessage for CORS
  4070. $(window).on('message.' + namespace, function(e){
  4071. var res = e.originalEvent || null,
  4072. obj, data;
  4073. if (res && self.uploadURL.indexOf(res.origin) === 0) {
  4074. try {
  4075. obj = JSON.parse(res.data);
  4076. data = obj.data || null;
  4077. if (data) {
  4078. if (data.error) {
  4079. if (obj.bind) {
  4080. self.trigger(obj.bind+'fail', data);
  4081. }
  4082. self.error(data.error);
  4083. } else {
  4084. data.warning && self.error(data.warning);
  4085. self.updateCache(data);
  4086. data.removed && data.removed.length && self.remove(data);
  4087. data.added && data.added.length && self.add(data);
  4088. data.changed && data.changed.length && self.change(data);
  4089. if (obj.bind) {
  4090. self.trigger(obj.bind, data);
  4091. self.trigger(obj.bind+'done');
  4092. }
  4093. data.sync && self.sync();
  4094. }
  4095. }
  4096. } catch (e) {
  4097. self.sync();
  4098. }
  4099. }
  4100. });
  4101. // elFinder enable always
  4102. if (self.options.enableAlways) {
  4103. $(window).on('focus.' + namespace, function(e){
  4104. (e.target === this) && self.enable();
  4105. });
  4106. if (inFrame) {
  4107. $(window.top).on('focus.' + namespace, function() {
  4108. if (self.enable() && (! parentIframe || parentIframe.is(':visible'))) {
  4109. requestAnimationFrame(function() {
  4110. $(window).trigger('focus');
  4111. });
  4112. }
  4113. });
  4114. }
  4115. } else if (inFrame) {
  4116. $(window).on('blur.' + namespace, function(e){
  4117. enabled && e.target === this && self.disable();
  4118. });
  4119. }
  4120. // return focus to the window on click (elFInder in the frame)
  4121. if (inFrame) {
  4122. node.on('click', function(e) {
  4123. $(window).trigger('focus');
  4124. });
  4125. }
  4126. // elFinder to enable by mouse over
  4127. if (self.options.enableByMouseOver) {
  4128. node.on('mouseenter touchstart', function(e) {
  4129. (inFrame) && $(window).trigger('focus');
  4130. ! self.enabled() && self.enable();
  4131. });
  4132. }
  4133. });
  4134. // store instance in node
  4135. node[0].elfinder = this;
  4136. // auto load language file
  4137. dfrdsBeforeBootup.push((function() {
  4138. var lang = self.lang,
  4139. langJs = self.i18nBaseUrl + 'elfinder.' + lang + '.js',
  4140. dfd = $.Deferred().done(function() {
  4141. if (self.i18[lang]) {
  4142. self.lang = lang;
  4143. }
  4144. self.trigger('i18load');
  4145. i18n = self.lang === 'en'
  4146. ? self.i18['en']
  4147. : $.extend(true, {}, self.i18['en'], self.i18[self.lang]);
  4148. });
  4149. if (!self.i18[lang]) {
  4150. self.lang = 'en';
  4151. if (self.hasRequire) {
  4152. require([langJs], function() {
  4153. dfd.resolve();
  4154. }, function() {
  4155. dfd.resolve();
  4156. });
  4157. } else {
  4158. self.loadScript([langJs], function() {
  4159. dfd.resolve();
  4160. }, {
  4161. loadType: 'tag',
  4162. error : function() {
  4163. dfd.resolve();
  4164. }
  4165. });
  4166. }
  4167. } else {
  4168. dfd.resolve();
  4169. }
  4170. return dfd;
  4171. })());
  4172. // elFinder boot up function
  4173. bootUp = function() {
  4174. var columnNames;
  4175. /**
  4176. * i18 messages
  4177. *
  4178. * @type Object
  4179. **/
  4180. self.messages = i18n.messages;
  4181. // check jquery ui
  4182. if (!($.fn.selectable && $.fn.draggable && $.fn.droppable && $.fn.resizable && $.fn.slider)) {
  4183. return alert(self.i18n('errJqui'));
  4184. }
  4185. // check node
  4186. if (!node.length) {
  4187. return alert(self.i18n('errNode'));
  4188. }
  4189. // check connector url
  4190. if (!self.options.url) {
  4191. return alert(self.i18n('errURL'));
  4192. }
  4193. // column key/name map for fm.getColumnName()
  4194. columnNames = Object.assign({
  4195. name : self.i18n('name'),
  4196. perm : self.i18n('perms'),
  4197. date : self.i18n('modify'),
  4198. size : self.i18n('size'),
  4199. kind : self.i18n('kind'),
  4200. modestr : self.i18n('mode'),
  4201. modeoct : self.i18n('mode'),
  4202. modeboth : self.i18n('mode')
  4203. }, self.options.uiOptions.cwd.listView.columnsCustomName);
  4204. /**
  4205. * Gets the column name of cwd list view
  4206. *
  4207. * @param String key The key
  4208. * @return String The column name.
  4209. */
  4210. self.getColumnName = function(key) {
  4211. return columnNames[key] || self.i18n(key);
  4212. };
  4213. /**
  4214. * Interface direction
  4215. *
  4216. * @type String
  4217. * @default "ltr"
  4218. **/
  4219. self.direction = i18n.direction;
  4220. /**
  4221. * Date/time format
  4222. *
  4223. * @type String
  4224. * @default "m.d.Y"
  4225. **/
  4226. self.dateFormat = self.options.dateFormat || i18n.dateFormat;
  4227. /**
  4228. * Date format like "Yesterday 10:20:12"
  4229. *
  4230. * @type String
  4231. * @default "{day} {time}"
  4232. **/
  4233. self.fancyFormat = self.options.fancyDateFormat || i18n.fancyDateFormat;
  4234. /**
  4235. * Date format for if upload file has not original unique name
  4236. * e.g. Clipboard image data, Image data taken with iOS
  4237. *
  4238. * @type String
  4239. * @default "ymd-His"
  4240. **/
  4241. self.nonameDateFormat = (self.options.nonameDateFormat || i18n.nonameDateFormat).replace(/[\/\\]/g, '_');
  4242. /**
  4243. * Css classes
  4244. *
  4245. * @type String
  4246. **/
  4247. self.cssClass = 'ui-helper-reset ui-helper-clearfix ui-widget ui-widget-content ui-corner-all elfinder elfinder-'
  4248. +(self.direction == 'rtl' ? 'rtl' : 'ltr')
  4249. +(self.UA.Touch? (' elfinder-touch' + (self.options.resizable ? ' touch-punch' : '')) : '')
  4250. +(self.UA.Mobile? ' elfinder-mobile' : '')
  4251. +(self.UA.iOS? ' elfinder-ios' : '')
  4252. +' '+self.options.cssClass;
  4253. // prepare node
  4254. node.addClass(self.cssClass)
  4255. .on(mousedown, function() {
  4256. !enabled && self.enable();
  4257. });
  4258. // draggable closure
  4259. (function() {
  4260. var ltr, wzRect, wzBottom, wzBottom2, nodeStyle,
  4261. keyEvt = keydown + 'draggable' + ' keyup.' + namespace + 'draggable';
  4262. /**
  4263. * Base draggable options
  4264. *
  4265. * @type Object
  4266. **/
  4267. self.draggable = {
  4268. appendTo : node,
  4269. addClasses : false,
  4270. distance : 4,
  4271. revert : true,
  4272. refreshPositions : false,
  4273. cursor : 'crosshair',
  4274. cursorAt : {left : 50, top : 47},
  4275. scroll : false,
  4276. start : function(e, ui) {
  4277. var helper = ui.helper,
  4278. targets = $.grep(helper.data('files')||[], function(h) {
  4279. if (h) {
  4280. remember[h] = true;
  4281. return true;
  4282. }
  4283. return false;
  4284. }),
  4285. locked = false,
  4286. cnt, h;
  4287. // fix node size
  4288. nodeStyle = node.attr('style');
  4289. node.width(node.width()).height(node.height());
  4290. // set var for drag()
  4291. ltr = (self.direction === 'ltr');
  4292. wzRect = self.getUI('workzone').data('rectangle');
  4293. wzBottom = wzRect.top + wzRect.height;
  4294. wzBottom2 = wzBottom - self.getUI('navdock').outerHeight(true);
  4295. self.draggingUiHelper = helper;
  4296. cnt = targets.length;
  4297. while (cnt--) {
  4298. h = targets[cnt];
  4299. if (files[h].locked) {
  4300. locked = true;
  4301. helper.data('locked', true);
  4302. break;
  4303. }
  4304. }
  4305. !locked && self.trigger('lockfiles', {files : targets});
  4306. helper.data('autoScrTm', setInterval(function() {
  4307. if (helper.data('autoScr')) {
  4308. self.autoScroll[helper.data('autoScr')](helper.data('autoScrVal'));
  4309. }
  4310. }, 50));
  4311. },
  4312. drag : function(e, ui) {
  4313. var helper = ui.helper,
  4314. autoScr, autoUp, bottom;
  4315. if ((autoUp = wzRect.top > e.pageY) || wzBottom2 < e.pageY) {
  4316. if (wzRect.cwdEdge > e.pageX) {
  4317. autoScr = (ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down');
  4318. } else {
  4319. autoScr = (ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down');
  4320. }
  4321. if (!autoUp) {
  4322. if (autoScr.substr(0, 3) === 'cwd') {
  4323. if (wzBottom < e.pageY) {
  4324. bottom = wzBottom;
  4325. } else {
  4326. autoScr = null;
  4327. }
  4328. } else {
  4329. bottom = wzBottom2;
  4330. }
  4331. }
  4332. if (autoScr) {
  4333. helper.data('autoScr', autoScr);
  4334. helper.data('autoScrVal', Math.pow((autoUp? wzRect.top - e.pageY : e.pageY - bottom), 1.3));
  4335. }
  4336. }
  4337. if (! autoScr) {
  4338. if (helper.data('autoScr')) {
  4339. helper.data('refreshPositions', 1).data('autoScr', null);
  4340. }
  4341. }
  4342. if (helper.data('refreshPositions') && $(this).elfUiWidgetInstance('draggable')) {
  4343. if (helper.data('refreshPositions') > 0) {
  4344. $(this).draggable('option', { refreshPositions : true, elfRefresh : true });
  4345. helper.data('refreshPositions', -1);
  4346. } else {
  4347. $(this).draggable('option', { refreshPositions : false, elfRefresh : false });
  4348. helper.data('refreshPositions', null);
  4349. }
  4350. }
  4351. },
  4352. stop : function(e, ui) {
  4353. var helper = ui.helper,
  4354. files;
  4355. $(document).off(keyEvt);
  4356. $(this).elfUiWidgetInstance('draggable') && $(this).draggable('option', { refreshPositions : false });
  4357. self.draggingUiHelper = null;
  4358. self.trigger('focus').trigger('dragstop');
  4359. if (! helper.data('droped')) {
  4360. files = $.grep(helper.data('files')||[], function(h) { return h? true : false ;});
  4361. self.trigger('unlockfiles', {files : files});
  4362. self.trigger('selectfiles', {files : self.selected()});
  4363. }
  4364. self.enable();
  4365. // restore node style
  4366. node.attr('style', nodeStyle);
  4367. helper.data('autoScrTm') && clearInterval(helper.data('autoScrTm'));
  4368. },
  4369. helper : function(e, ui) {
  4370. var element = this.id ? $(this) : $(this).parents('[id]:first'),
  4371. helper = $('<div class="elfinder-drag-helper"><span class="elfinder-drag-helper-icon-status"/></div>'),
  4372. icon = function(f) {
  4373. var mime = f.mime, i, tmb = self.tmb(f);
  4374. i = '<div class="elfinder-cwd-icon elfinder-cwd-icon-drag '+self.mime2class(mime)+' ui-corner-all"/>';
  4375. if (tmb) {
  4376. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  4377. } else if (f.icon) {
  4378. i = $(i).css(self.getIconStyle(f, true)).get(0).outerHTML;
  4379. }
  4380. if (f.csscls) {
  4381. i = '<div class="'+f.csscls+'">' + i + '</div>';
  4382. }
  4383. return i;
  4384. },
  4385. hashes, l, ctr;
  4386. self.draggingUiHelper && self.draggingUiHelper.stop(true, true);
  4387. self.trigger('dragstart', {target : element[0], originalEvent : e}, true);
  4388. hashes = element.hasClass(self.res('class', 'cwdfile'))
  4389. ? self.selected()
  4390. : [self.navId2Hash(element.attr('id'))];
  4391. helper.append(icon(files[hashes[0]])).data('files', hashes).data('locked', false).data('droped', false).data('namespace', namespace).data('dropover', 0);
  4392. if ((l = hashes.length) > 1) {
  4393. helper.append(icon(files[hashes[l-1]]) + '<span class="elfinder-drag-num">'+l+'</span>');
  4394. }
  4395. $(document).on(keyEvt, function(e){
  4396. var chk = (e.shiftKey||e.ctrlKey||e.metaKey);
  4397. if (ctr !== chk) {
  4398. ctr = chk;
  4399. if (helper.is(':visible') && helper.data('dropover') && ! helper.data('droped')) {
  4400. helper.toggleClass('elfinder-drag-helper-plus', helper.data('locked')? true : ctr);
  4401. self.trigger(ctr? 'unlockfiles' : 'lockfiles', {files : hashes, helper: helper});
  4402. }
  4403. }
  4404. });
  4405. return helper;
  4406. }
  4407. };
  4408. })();
  4409. // in getFileCallback set - change default actions on double click/enter/ctrl+enter
  4410. if (self.commands.getfile) {
  4411. if (typeof(self.options.getFileCallback) == 'function') {
  4412. self.bind('dblclick', function(e) {
  4413. e.preventDefault();
  4414. self.exec('getfile').fail(function() {
  4415. self.exec('open', e.data && e.data.file? [ e.data.file ]: void(0));
  4416. });
  4417. });
  4418. self.shortcut({
  4419. pattern : 'enter',
  4420. description : self.i18n('cmdgetfile'),
  4421. callback : function() { self.exec('getfile').fail(function() { self.exec(self.OS == 'mac' ? 'rename' : 'open'); }); }
  4422. })
  4423. .shortcut({
  4424. pattern : 'ctrl+enter',
  4425. description : self.i18n(self.OS == 'mac' ? 'cmdrename' : 'cmdopen'),
  4426. callback : function() { self.exec(self.OS == 'mac' ? 'rename' : 'open'); }
  4427. });
  4428. } else {
  4429. self.options.getFileCallback = null;
  4430. }
  4431. }
  4432. // load commands
  4433. $.each(self.commands, function(name, cmd) {
  4434. var proto = Object.assign({}, cmd.prototype),
  4435. extendsCmd, opts;
  4436. if ($.isFunction(cmd) && !self._commands[name] && (cmd.prototype.forceLoad || $.inArray(name, self.options.commands) !== -1)) {
  4437. extendsCmd = cmd.prototype.extendsCmd || '';
  4438. if (extendsCmd) {
  4439. if ($.isFunction(self.commands[extendsCmd])) {
  4440. cmd.prototype = Object.assign({}, base, new self.commands[extendsCmd](), cmd.prototype);
  4441. } else {
  4442. return true;
  4443. }
  4444. } else {
  4445. cmd.prototype = Object.assign({}, base, cmd.prototype);
  4446. }
  4447. self._commands[name] = new cmd();
  4448. cmd.prototype = proto;
  4449. opts = self.options.commandsOptions[name] || {};
  4450. if (extendsCmd && self.options.commandsOptions[extendsCmd]) {
  4451. opts = $.extend(true, {}, self.options.commandsOptions[extendsCmd], opts);
  4452. }
  4453. self._commands[name].setup(name, opts);
  4454. // setup linked commands
  4455. if (self._commands[name].linkedCmds.length) {
  4456. $.each(self._commands[name].linkedCmds, function(i, n) {
  4457. var lcmd = self.commands[n];
  4458. if ($.isFunction(lcmd) && !self._commands[n]) {
  4459. lcmd.prototype = base;
  4460. self._commands[n] = new lcmd();
  4461. self._commands[n].setup(n, self.options.commandsOptions[n]||{});
  4462. }
  4463. });
  4464. }
  4465. }
  4466. });
  4467. /**
  4468. * UI nodes
  4469. *
  4470. * @type Object
  4471. **/
  4472. self.ui = {
  4473. // container for nav panel and current folder container
  4474. workzone : $('<div/>').appendTo(node).elfinderworkzone(self),
  4475. // container for folders tree / places
  4476. navbar : $('<div/>').appendTo(node).elfindernavbar(self, self.options.uiOptions.navbar || {}),
  4477. // container for for preview etc at below the navbar
  4478. navdock : $('<div/>').appendTo(node).elfindernavdock(self, self.options.uiOptions.navdock || {}),
  4479. // contextmenu
  4480. contextmenu : $('<div/>').appendTo(node).elfindercontextmenu(self),
  4481. // overlay
  4482. overlay : $('<div/>').appendTo(node).elfinderoverlay({
  4483. show : function() { self.disable(); },
  4484. hide : function() { prevEnabled && self.enable(); }
  4485. }),
  4486. // current folder container
  4487. cwd : $('<div/>').appendTo(node).elfindercwd(self, self.options.uiOptions.cwd || {}),
  4488. // notification dialog window
  4489. notify : self.dialog('', {
  4490. cssClass : 'elfinder-dialog-notify',
  4491. position : self.options.notifyDialog.position,
  4492. absolute : true,
  4493. resizable : false,
  4494. autoOpen : false,
  4495. closeOnEscape : false,
  4496. title : '&nbsp;',
  4497. width : self.options.notifyDialog.width? parseInt(self.options.notifyDialog.width) : null,
  4498. minHeight : null
  4499. }),
  4500. statusbar : $('<div class="ui-widget-header ui-helper-clearfix ui-corner-bottom elfinder-statusbar"/>').hide().appendTo(node),
  4501. toast : $('<div class="elfinder-toast"/>').appendTo(node),
  4502. bottomtray : $('<div class="elfinder-bottomtray">').appendTo(node)
  4503. };
  4504. self.trigger('uiready');
  4505. // load required ui
  4506. $.each(self.options.ui || [], function(i, ui) {
  4507. var name = 'elfinder'+ui,
  4508. opts = self.options.uiOptions[ui] || {};
  4509. if (!self.ui[ui] && $.fn[name]) {
  4510. // regist to self.ui before make instance
  4511. self.ui[ui] = $('<'+(opts.tag || 'div')+'/>').appendTo(node);
  4512. self.ui[ui][name](self, opts);
  4513. }
  4514. });
  4515. // update size
  4516. self.resize(width, height);
  4517. // make node resizable
  4518. if (self.options.resizable) {
  4519. node.resizable({
  4520. resize : function(e, ui) {
  4521. self.resize(ui.size.width, ui.size.height);
  4522. },
  4523. handles : 'se',
  4524. minWidth : 300,
  4525. minHeight : 200
  4526. });
  4527. if (self.UA.Touch) {
  4528. node.addClass('touch-punch');
  4529. }
  4530. }
  4531. (function() {
  4532. var navbar = self.getUI('navbar'),
  4533. cwd = self.getUI('cwd').parent();
  4534. self.autoScroll = {
  4535. navbarUp : function(v) {
  4536. navbar.scrollTop(Math.max(0, navbar.scrollTop() - v));
  4537. },
  4538. navbarDown : function(v) {
  4539. navbar.scrollTop(navbar.scrollTop() + v);
  4540. },
  4541. cwdUp : function(v) {
  4542. cwd.scrollTop(Math.max(0, cwd.scrollTop() - v));
  4543. },
  4544. cwdDown : function(v) {
  4545. cwd.scrollTop(cwd.scrollTop() + v);
  4546. }
  4547. };
  4548. })();
  4549. // Swipe on the touch devices to show/hide of toolbar or navbar
  4550. if (self.UA.Touch) {
  4551. (function() {
  4552. var lastX, lastY, nodeOffset, nodeWidth, nodeTop, navbarW, toolbarH,
  4553. navbar = self.getUI('navbar'),
  4554. toolbar = self.getUI('toolbar'),
  4555. moveEv = 'touchmove.stopscroll',
  4556. moveTm,
  4557. moveUpOn = function(e) {
  4558. var touches = e.originalEvent.touches || [{}],
  4559. y = touches[0].pageY || null;
  4560. if (!lastY || y < lastY) {
  4561. e.preventDefault();
  4562. moveTm && clearTimeout(moveTm);
  4563. }
  4564. },
  4565. moveDownOn = function(e) {
  4566. e.preventDefault();
  4567. moveTm && clearTimeout(moveTm);
  4568. },
  4569. moveOff = function() {
  4570. moveTm = setTimeout(function() {
  4571. node.off(moveEv);
  4572. }, 100);
  4573. },
  4574. handleW, handleH = 50;
  4575. navbar = navbar.children().length? navbar : null;
  4576. toolbar = toolbar.length? toolbar : null;
  4577. node.on('touchstart touchmove touchend', function(e) {
  4578. if (e.type === 'touchend') {
  4579. lastX = false;
  4580. lastY = false;
  4581. moveOff();
  4582. return;
  4583. }
  4584. var touches = e.originalEvent.touches || [{}],
  4585. x = touches[0].pageX || null,
  4586. y = touches[0].pageY || null,
  4587. ltr = (self.direction === 'ltr'),
  4588. navbarMode, treeWidth, swipeX, moveX, toolbarT, mode;
  4589. if (x === null || y === null || (e.type === 'touchstart' && touches.length > 1)) {
  4590. return;
  4591. }
  4592. if (e.type === 'touchstart') {
  4593. nodeOffset = node.offset();
  4594. nodeWidth = node.width();
  4595. if (navbar) {
  4596. lastX = false;
  4597. if (navbar.is(':hidden')) {
  4598. if (! handleW) {
  4599. handleW = Math.max(50, nodeWidth / 10);
  4600. }
  4601. if ((ltr? (x - nodeOffset.left) : (nodeWidth + nodeOffset.left - x)) < handleW) {
  4602. lastX = x;
  4603. }
  4604. } else if (! e.originalEvent._preventSwipeX) {
  4605. navbarW = navbar.width();
  4606. if (ltr) {
  4607. swipeX = (x < nodeOffset.left + navbarW);
  4608. } else {
  4609. swipeX = (x > nodeOffset.left + nodeWidth - navbarW);
  4610. }
  4611. if (swipeX) {
  4612. handleW = Math.max(50, nodeWidth / 10);
  4613. lastX = x;
  4614. } else {
  4615. lastX = false;
  4616. }
  4617. }
  4618. }
  4619. if (toolbar) {
  4620. lastY = false;
  4621. if (! e.originalEvent._preventSwipeY) {
  4622. toolbarH = toolbar.height();
  4623. nodeTop = nodeOffset.top;
  4624. if (y - nodeTop < (toolbar.is(':hidden')? handleH : (toolbarH + 30))) {
  4625. lastY = y;
  4626. node.on(moveEv, toolbar.is(':hidden')? moveDownOn: moveUpOn);
  4627. }
  4628. }
  4629. }
  4630. } else {
  4631. if (navbar && lastX !== false) {
  4632. navbarMode = (ltr? (lastX > x) : (lastX < x))? 'navhide' : 'navshow';
  4633. moveX = Math.abs(lastX - x);
  4634. if (navbarMode === 'navhide' && moveX > navbarW * 0.6
  4635. || (moveX > (navbarMode === 'navhide'? navbarW / 3 : 45)
  4636. && (navbarMode === 'navshow'
  4637. || (ltr? x < nodeOffset.left + 20 : x > nodeOffset.left + nodeWidth - 20)
  4638. ))
  4639. ) {
  4640. self.getUI('navbar').trigger(navbarMode, {handleW: handleW});
  4641. lastX = false;
  4642. }
  4643. }
  4644. if (toolbar && lastY !== false ) {
  4645. toolbarT = toolbar.offset().top;
  4646. if (Math.abs(lastY - y) > Math.min(45, toolbarH / 3)) {
  4647. mode = (lastY > y)? 'slideUp' : 'slideDown';
  4648. if (mode === 'slideDown' || toolbarT + 20 > y) {
  4649. if (toolbar.is(mode === 'slideDown' ? ':hidden' : ':visible')) {
  4650. toolbar.stop(true, true).trigger('toggle', {duration: 100, handleH: handleH});
  4651. }
  4652. lastY = false;
  4653. }
  4654. }
  4655. }
  4656. }
  4657. });
  4658. })();
  4659. }
  4660. if (self.dragUpload) {
  4661. // add event listener for HTML5 DnD upload
  4662. (function() {
  4663. var isin = function(e) {
  4664. return (e.target.nodeName !== 'TEXTAREA' && e.target.nodeName !== 'INPUT' && $(e.target).closest('div.ui-dialog-content').length === 0);
  4665. },
  4666. ent = 'native-drag-enter',
  4667. disable = 'native-drag-disable',
  4668. c = 'class',
  4669. navdir = self.res(c, 'navdir'),
  4670. droppable = self.res(c, 'droppable'),
  4671. dropover = self.res(c, 'adroppable'),
  4672. arrow = self.res(c, 'navarrow'),
  4673. clDropActive = self.res(c, 'adroppable'),
  4674. wz = self.getUI('workzone'),
  4675. ltr = (self.direction === 'ltr'),
  4676. clearTm = function() {
  4677. autoScrTm && cancelAnimationFrame(autoScrTm);
  4678. autoScrTm = null;
  4679. },
  4680. wzRect, autoScrFn, autoScrTm;
  4681. node.on('dragenter', function(e) {
  4682. clearTm();
  4683. if (isin(e)) {
  4684. e.preventDefault();
  4685. e.stopPropagation();
  4686. wzRect = wz.data('rectangle');
  4687. }
  4688. })
  4689. .on('dragleave', function(e) {
  4690. clearTm();
  4691. if (isin(e)) {
  4692. e.preventDefault();
  4693. e.stopPropagation();
  4694. }
  4695. })
  4696. .on('dragover', function(e) {
  4697. var autoUp;
  4698. if (isin(e)) {
  4699. e.preventDefault();
  4700. e.stopPropagation();
  4701. e.originalEvent.dataTransfer.dropEffect = 'none';
  4702. if (! autoScrTm) {
  4703. autoScrTm = requestAnimationFrame(function() {
  4704. var wzBottom = wzRect.top + wzRect.height,
  4705. wzBottom2 = wzBottom - self.getUI('navdock').outerHeight(true),
  4706. fn;
  4707. if ((autoUp = e.pageY < wzRect.top) || e.pageY > wzBottom2 ) {
  4708. if (wzRect.cwdEdge > e.pageX) {
  4709. fn = (ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down');
  4710. } else {
  4711. fn = (ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down');
  4712. }
  4713. if (!autoUp) {
  4714. if (fn.substr(0, 3) === 'cwd') {
  4715. if (wzBottom < e.pageY) {
  4716. wzBottom2 = wzBottom;
  4717. } else {
  4718. fn = '';
  4719. }
  4720. }
  4721. }
  4722. fn && self.autoScroll[fn](Math.pow((autoUp? wzRect.top - e.pageY : e.pageY - wzBottom2), 1.3));
  4723. }
  4724. autoScrTm = null;
  4725. });
  4726. }
  4727. } else {
  4728. clearTm();
  4729. }
  4730. })
  4731. .on('drop', function(e) {
  4732. clearTm();
  4733. if (isin(e)) {
  4734. e.stopPropagation();
  4735. e.preventDefault();
  4736. }
  4737. });
  4738. node.on('dragenter', '.native-droppable', function(e){
  4739. if (e.originalEvent.dataTransfer) {
  4740. var $elm = $(e.currentTarget),
  4741. id = e.currentTarget.id || null,
  4742. cwd = null,
  4743. elfFrom;
  4744. if (!id) { // target is cwd
  4745. cwd = self.cwd();
  4746. $elm.data(disable, false);
  4747. try {
  4748. $.each(e.originalEvent.dataTransfer.types, function(i, v){
  4749. if (v.substr(0, 13) === 'elfinderfrom:') {
  4750. elfFrom = v.substr(13).toLowerCase();
  4751. }
  4752. });
  4753. } catch(e) {}
  4754. }
  4755. if (!cwd || (cwd.write && (!elfFrom || elfFrom !== (window.location.href + cwd.hash).toLowerCase()))) {
  4756. e.preventDefault();
  4757. e.stopPropagation();
  4758. $elm.data(ent, true);
  4759. $elm.addClass(clDropActive);
  4760. } else {
  4761. $elm.data(disable, true);
  4762. }
  4763. }
  4764. })
  4765. .on('dragleave', '.native-droppable', function(e){
  4766. if (e.originalEvent.dataTransfer) {
  4767. var $elm = $(e.currentTarget);
  4768. e.preventDefault();
  4769. e.stopPropagation();
  4770. if ($elm.data(ent)) {
  4771. $elm.data(ent, false);
  4772. } else {
  4773. $elm.removeClass(clDropActive);
  4774. }
  4775. }
  4776. })
  4777. .on('dragover', '.native-droppable', function(e){
  4778. if (e.originalEvent.dataTransfer) {
  4779. var $elm = $(e.currentTarget);
  4780. e.preventDefault();
  4781. e.stopPropagation();
  4782. e.originalEvent.dataTransfer.dropEffect = $elm.data(disable)? 'none' : 'copy';
  4783. $elm.data(ent, false);
  4784. }
  4785. })
  4786. .on('drop', '.native-droppable', function(e){
  4787. if (e.originalEvent && e.originalEvent.dataTransfer) {
  4788. var $elm = $(e.currentTarget),
  4789. id;
  4790. e.preventDefault();
  4791. e.stopPropagation();
  4792. $elm.removeClass(clDropActive);
  4793. if (e.currentTarget.id) {
  4794. id = $elm.hasClass(navdir)? self.navId2Hash(e.currentTarget.id) : self.cwdId2Hash(e.currentTarget.id);
  4795. } else {
  4796. id = self.cwd().hash;
  4797. }
  4798. e.originalEvent._target = id;
  4799. self.exec('upload', {dropEvt: e.originalEvent, target: id}, void 0, id);
  4800. }
  4801. });
  4802. })();
  4803. }
  4804. // trigger event cssloaded if cddAutoLoad disabled
  4805. if (self.cssloaded === null) {
  4806. // check css loaded and remove hide
  4807. (function() {
  4808. var loaded = function() {
  4809. if (node.data('cssautoloadHide')) {
  4810. node.data('cssautoloadHide').remove();
  4811. node.removeData('cssautoloadHide');
  4812. }
  4813. self.cssloaded = true;
  4814. requestAnimationFrame(function() {
  4815. self.trigger('cssloaded');
  4816. });
  4817. },
  4818. cnt, fi;
  4819. if (node.css('visibility') === 'hidden') {
  4820. cnt = 1000; // timeout 10 secs
  4821. fi = setInterval(function() {
  4822. if (--cnt < 0 || node.css('visibility') !== 'hidden') {
  4823. clearInterval(fi);
  4824. loaded();
  4825. }
  4826. }, 10);
  4827. } else {
  4828. loaded();
  4829. }
  4830. })();
  4831. } else {
  4832. self.cssloaded = true;
  4833. self.trigger('cssloaded');
  4834. }
  4835. // calculate elFinder node z-index
  4836. self.zIndexCalc();
  4837. // send initial request and start to pray >_<
  4838. self.trigger('init')
  4839. .request({
  4840. data : {cmd : 'open', target : self.startDir(), init : 1, tree : 1},
  4841. preventDone : true,
  4842. notify : {type : 'open', cnt : 1, hideCnt : true},
  4843. freeze : true
  4844. })
  4845. .fail(function() {
  4846. self.trigger('fail').disable().lastDir('');
  4847. listeners = {};
  4848. shortcuts = {};
  4849. $(document).add(node).off('.'+namespace);
  4850. self.trigger = function() { };
  4851. })
  4852. .done(function(data) {
  4853. var trashDisable = function(th) {
  4854. var src = self.file(self.trashes[th]),
  4855. d = self.options.debug,
  4856. error;
  4857. if (src && src.volumeid) {
  4858. delete self.volOptions[src.volumeid].trashHash;
  4859. }
  4860. self.trashes[th] = false;
  4861. self.debug('backend-error', 'Trash hash "'+th+'" was not found or not writable.');
  4862. },
  4863. toChkTh = {};
  4864. // regist rawStringDecoder
  4865. if (self.options.rawStringDecoder) {
  4866. self.registRawStringDecoder(self.options.rawStringDecoder);
  4867. }
  4868. // re-calculate elFinder node z-index
  4869. self.zIndexCalc();
  4870. self.load().debug('api', self.api);
  4871. // update ui's size after init
  4872. node.trigger('resize');
  4873. // initial open
  4874. open(data);
  4875. self.trigger('open', data, false);
  4876. self.trigger('opendone');
  4877. if (inFrame && self.options.enableAlways) {
  4878. $(window).trigger('focus');
  4879. }
  4880. // check self.trashes
  4881. $.each(self.trashes, function(th) {
  4882. var dir = self.file(th),
  4883. src;
  4884. if (! dir) {
  4885. toChkTh[th] = true;
  4886. } else if (dir.mime !== 'directory' || ! dir.write) {
  4887. trashDisable(th);
  4888. }
  4889. });
  4890. if (Object.keys(toChkTh).length) {
  4891. self.request({
  4892. data : {cmd : 'info', targets : Object.keys(toChkTh)},
  4893. preventDefault : true
  4894. }).done(function(data) {
  4895. if (data && data.files) {
  4896. $.each(data.files, function(i, dir) {
  4897. if (dir.mime === 'directory' && dir.write) {
  4898. delete toChkTh[dir.hash];
  4899. }
  4900. });
  4901. }
  4902. }).always(function() {
  4903. $.each(toChkTh, trashDisable);
  4904. });
  4905. }
  4906. // to enable / disable
  4907. self[self.options.enableAlways? 'enable' : 'disable']();
  4908. });
  4909. // self.timeEnd('load');
  4910. // End of bootUp()
  4911. };
  4912. // call bootCallback function with elFinder instance, extraObject - { dfrdsBeforeBootup: dfrdsBeforeBootup }
  4913. if (bootCallback && typeof bootCallback === 'function') {
  4914. self.bootCallback = bootCallback;
  4915. bootCallback.call(node.get(0), self, { dfrdsBeforeBootup: dfrdsBeforeBootup });
  4916. }
  4917. // call dfrdsBeforeBootup functions then boot up elFinder
  4918. $.when.apply(null, dfrdsBeforeBootup).done(function() {
  4919. bootUp();
  4920. }).fail(function(error) {
  4921. self.error(error);
  4922. });
  4923. };
  4924. //register elFinder to global scope
  4925. if (typeof toGlobal === 'undefined' || toGlobal) {
  4926. window.elFinder = elFinder;
  4927. }
  4928. /**
  4929. * Prototype
  4930. *
  4931. * @type Object
  4932. */
  4933. elFinder.prototype = {
  4934. uniqueid : 0,
  4935. res : function(type, id) {
  4936. return this.resources[type] && this.resources[type][id];
  4937. },
  4938. /**
  4939. * User os. Required to bind native shortcuts for open/rename
  4940. *
  4941. * @type String
  4942. **/
  4943. OS : navigator.userAgent.indexOf('Mac') !== -1 ? 'mac' : navigator.userAgent.indexOf('Win') !== -1 ? 'win' : 'other',
  4944. /**
  4945. * User browser UA.
  4946. * jQuery.browser: version deprecated: 1.3, removed: 1.9
  4947. *
  4948. * @type Object
  4949. **/
  4950. UA : (function(){
  4951. var self = this,
  4952. webkit = !document.unqueID && !window.opera && !window.sidebar && window.localStorage && 'WebkitAppearance' in document.documentElement.style,
  4953. chrome = webkit && window.chrome,
  4954. /*setRotated = function() {
  4955. var a = ((screen && screen.orientation && screen.orientation.angle) || window.orientation || 0) + 0;
  4956. if (a === -90) {
  4957. a = 270;
  4958. }
  4959. UA.Angle = a;
  4960. UA.Rotated = a % 180 === 0? false : true;
  4961. },*/
  4962. UA = {
  4963. // Browser IE <= IE 6
  4964. ltIE6 : typeof window.addEventListener == "undefined" && typeof document.documentElement.style.maxHeight == "undefined",
  4965. // Browser IE <= IE 7
  4966. ltIE7 : typeof window.addEventListener == "undefined" && typeof document.querySelectorAll == "undefined",
  4967. // Browser IE <= IE 8
  4968. ltIE8 : typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined",
  4969. // Browser IE <= IE 9
  4970. ltIE9 : document.uniqueID && document.documentMode <= 9,
  4971. // Browser IE <= IE 10
  4972. ltIE10 : document.uniqueID && document.documentMode <= 10,
  4973. // Browser IE >= IE 11
  4974. gtIE11 : document.uniqueID && document.documentMode >= 11,
  4975. IE : document.uniqueID,
  4976. Firefox : window.sidebar,
  4977. Opera : window.opera,
  4978. Webkit : webkit,
  4979. Chrome : chrome,
  4980. Edge : (chrome && window.msCredentials)? true : false,
  4981. Safari : webkit && !window.chrome,
  4982. Mobile : typeof window.orientation != "undefined",
  4983. Touch : typeof window.ontouchstart != "undefined",
  4984. iOS : navigator.platform.match(/^iP(?:[ao]d|hone)/),
  4985. Fullscreen : (typeof (document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen || document.msExitFullscreen) !== 'undefined'),
  4986. Angle : 0,
  4987. Rotated : false,
  4988. CSS : (function() {
  4989. var aStyle = document.createElement('a').style,
  4990. pStyle = document.createElement('p').style,
  4991. css;
  4992. css = 'position:sticky;position:-webkit-sticky;';
  4993. css += 'width:-webkit-max-content;width:-moz-max-content;width:-ms-max-content;width:max-content;';
  4994. aStyle.cssText = css;
  4995. return {
  4996. positionSticky : aStyle.position.indexOf('sticky')!==-1,
  4997. widthMaxContent : aStyle.width.indexOf('max-content')!==-1,
  4998. flex : typeof pStyle.flex !== 'undefined'
  4999. };
  5000. })()
  5001. };
  5002. return UA;
  5003. })(),
  5004. /**
  5005. * Has RequireJS?
  5006. *
  5007. * @type Boolean
  5008. */
  5009. hasRequire : (typeof define === 'function' && define.amd),
  5010. /**
  5011. * Current request command
  5012. *
  5013. * @type String
  5014. */
  5015. currentReqCmd : '',
  5016. /**
  5017. * Current keyboard state
  5018. *
  5019. * @type Object
  5020. */
  5021. keyState : {},
  5022. /**
  5023. * Internationalization object
  5024. *
  5025. * @type Object
  5026. */
  5027. i18 : {
  5028. en : {
  5029. translator : '',
  5030. language : 'English',
  5031. direction : 'ltr',
  5032. dateFormat : 'd.m.Y H:i',
  5033. fancyDateFormat : '$1 H:i',
  5034. nonameDateFormat : 'ymd-His',
  5035. messages : {}
  5036. },
  5037. months : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
  5038. monthsShort : ['msJan', 'msFeb', 'msMar', 'msApr', 'msMay', 'msJun', 'msJul', 'msAug', 'msSep', 'msOct', 'msNov', 'msDec'],
  5039. days : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
  5040. daysShort : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
  5041. },
  5042. /**
  5043. * File mimetype to kind mapping
  5044. *
  5045. * @type Object
  5046. */
  5047. kinds : {
  5048. 'unknown' : 'Unknown',
  5049. 'directory' : 'Folder',
  5050. 'group' : 'Selects',
  5051. 'symlink' : 'Alias',
  5052. 'symlink-broken' : 'AliasBroken',
  5053. 'application/x-empty' : 'TextPlain',
  5054. 'application/postscript' : 'Postscript',
  5055. 'application/vnd.ms-office' : 'MsOffice',
  5056. 'application/msword' : 'MsWord',
  5057. 'application/vnd.ms-word' : 'MsWord',
  5058. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : 'MsWord',
  5059. 'application/vnd.ms-word.document.macroEnabled.12' : 'MsWord',
  5060. 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' : 'MsWord',
  5061. 'application/vnd.ms-word.template.macroEnabled.12' : 'MsWord',
  5062. 'application/vnd.ms-excel' : 'MsExcel',
  5063. 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : 'MsExcel',
  5064. 'application/vnd.ms-excel.sheet.macroEnabled.12' : 'MsExcel',
  5065. 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' : 'MsExcel',
  5066. 'application/vnd.ms-excel.template.macroEnabled.12' : 'MsExcel',
  5067. 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' : 'MsExcel',
  5068. 'application/vnd.ms-excel.addin.macroEnabled.12' : 'MsExcel',
  5069. 'application/vnd.ms-powerpoint' : 'MsPP',
  5070. 'application/vnd.openxmlformats-officedocument.presentationml.presentation' : 'MsPP',
  5071. 'application/vnd.ms-powerpoint.presentation.macroEnabled.12' : 'MsPP',
  5072. 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' : 'MsPP',
  5073. 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' : 'MsPP',
  5074. 'application/vnd.openxmlformats-officedocument.presentationml.template' : 'MsPP',
  5075. 'application/vnd.ms-powerpoint.template.macroEnabled.12' : 'MsPP',
  5076. 'application/vnd.ms-powerpoint.addin.macroEnabled.12' : 'MsPP',
  5077. 'application/vnd.openxmlformats-officedocument.presentationml.slide' : 'MsPP',
  5078. 'application/vnd.ms-powerpoint.slide.macroEnabled.12' : 'MsPP',
  5079. 'application/pdf' : 'PDF',
  5080. 'application/xml' : 'XML',
  5081. 'application/vnd.oasis.opendocument.text' : 'OO',
  5082. 'application/vnd.oasis.opendocument.text-template' : 'OO',
  5083. 'application/vnd.oasis.opendocument.text-web' : 'OO',
  5084. 'application/vnd.oasis.opendocument.text-master' : 'OO',
  5085. 'application/vnd.oasis.opendocument.graphics' : 'OO',
  5086. 'application/vnd.oasis.opendocument.graphics-template' : 'OO',
  5087. 'application/vnd.oasis.opendocument.presentation' : 'OO',
  5088. 'application/vnd.oasis.opendocument.presentation-template' : 'OO',
  5089. 'application/vnd.oasis.opendocument.spreadsheet' : 'OO',
  5090. 'application/vnd.oasis.opendocument.spreadsheet-template' : 'OO',
  5091. 'application/vnd.oasis.opendocument.chart' : 'OO',
  5092. 'application/vnd.oasis.opendocument.formula' : 'OO',
  5093. 'application/vnd.oasis.opendocument.database' : 'OO',
  5094. 'application/vnd.oasis.opendocument.image' : 'OO',
  5095. 'application/vnd.openofficeorg.extension' : 'OO',
  5096. 'application/x-shockwave-flash' : 'AppFlash',
  5097. 'application/flash-video' : 'Flash video',
  5098. 'application/x-bittorrent' : 'Torrent',
  5099. 'application/javascript' : 'JS',
  5100. 'application/rtf' : 'RTF',
  5101. 'application/rtfd' : 'RTF',
  5102. 'application/x-font-ttf' : 'TTF',
  5103. 'application/x-font-otf' : 'OTF',
  5104. 'application/x-rpm' : 'RPM',
  5105. 'application/x-web-config' : 'TextPlain',
  5106. 'application/xhtml+xml' : 'HTML',
  5107. 'application/docbook+xml' : 'DOCBOOK',
  5108. 'application/x-awk' : 'AWK',
  5109. 'application/x-gzip' : 'GZIP',
  5110. 'application/x-bzip2' : 'BZIP',
  5111. 'application/x-xz' : 'XZ',
  5112. 'application/zip' : 'ZIP',
  5113. 'application/x-zip' : 'ZIP',
  5114. 'application/x-rar' : 'RAR',
  5115. 'application/x-tar' : 'TAR',
  5116. 'application/x-7z-compressed' : '7z',
  5117. 'application/x-jar' : 'JAR',
  5118. 'text/plain' : 'TextPlain',
  5119. 'text/x-php' : 'PHP',
  5120. 'text/html' : 'HTML',
  5121. 'text/javascript' : 'JS',
  5122. 'text/css' : 'CSS',
  5123. 'text/rtf' : 'RTF',
  5124. 'text/rtfd' : 'RTF',
  5125. 'text/x-c' : 'C',
  5126. 'text/x-csrc' : 'C',
  5127. 'text/x-chdr' : 'CHeader',
  5128. 'text/x-c++' : 'CPP',
  5129. 'text/x-c++src' : 'CPP',
  5130. 'text/x-c++hdr' : 'CPPHeader',
  5131. 'text/x-shellscript' : 'Shell',
  5132. 'application/x-csh' : 'Shell',
  5133. 'text/x-python' : 'Python',
  5134. 'text/x-java' : 'Java',
  5135. 'text/x-java-source' : 'Java',
  5136. 'text/x-ruby' : 'Ruby',
  5137. 'text/x-perl' : 'Perl',
  5138. 'text/x-sql' : 'SQL',
  5139. 'text/xml' : 'XML',
  5140. 'text/x-comma-separated-values' : 'CSV',
  5141. 'text/x-markdown' : 'Markdown',
  5142. 'image/x-ms-bmp' : 'BMP',
  5143. 'image/jpeg' : 'JPEG',
  5144. 'image/gif' : 'GIF',
  5145. 'image/png' : 'PNG',
  5146. 'image/tiff' : 'TIFF',
  5147. 'image/x-targa' : 'TGA',
  5148. 'image/vnd.adobe.photoshop' : 'PSD',
  5149. 'image/xbm' : 'XBITMAP',
  5150. 'image/pxm' : 'PXM',
  5151. 'audio/mpeg' : 'AudioMPEG',
  5152. 'audio/midi' : 'AudioMIDI',
  5153. 'audio/ogg' : 'AudioOGG',
  5154. 'audio/mp4' : 'AudioMPEG4',
  5155. 'audio/x-m4a' : 'AudioMPEG4',
  5156. 'audio/wav' : 'AudioWAV',
  5157. 'audio/x-mp3-playlist' : 'AudioPlaylist',
  5158. 'video/x-dv' : 'VideoDV',
  5159. 'video/mp4' : 'VideoMPEG4',
  5160. 'video/mpeg' : 'VideoMPEG',
  5161. 'video/x-msvideo' : 'VideoAVI',
  5162. 'video/quicktime' : 'VideoMOV',
  5163. 'video/x-ms-wmv' : 'VideoWM',
  5164. 'video/x-flv' : 'VideoFlash',
  5165. 'video/x-matroska' : 'VideoMKV',
  5166. 'video/ogg' : 'VideoOGG'
  5167. },
  5168. /**
  5169. * File mimetype to file extention mapping
  5170. *
  5171. * @type Object
  5172. * @see elFinder.mimetypes.js
  5173. */
  5174. mimeTypes : {},
  5175. /**
  5176. * Ajax request data validation rules
  5177. *
  5178. * @type Object
  5179. */
  5180. rules : {
  5181. defaults : function(data) {
  5182. if (!data
  5183. || (data.added && !Array.isArray(data.added))
  5184. || (data.removed && !Array.isArray(data.removed))
  5185. || (data.changed && !Array.isArray(data.changed))) {
  5186. return false;
  5187. }
  5188. return true;
  5189. },
  5190. open : function(data) { return data && data.cwd && data.files && $.isPlainObject(data.cwd) && Array.isArray(data.files); },
  5191. tree : function(data) { return data && data.tree && Array.isArray(data.tree); },
  5192. parents : function(data) { return data && data.tree && Array.isArray(data.tree); },
  5193. tmb : function(data) { return data && data.images && ($.isPlainObject(data.images) || Array.isArray(data.images)); },
  5194. upload : function(data) { return data && ($.isPlainObject(data.added) || Array.isArray(data.added));},
  5195. search : function(data) { return data && data.files && Array.isArray(data.files); }
  5196. },
  5197. /**
  5198. * Commands costructors
  5199. *
  5200. * @type Object
  5201. */
  5202. commands : {},
  5203. /**
  5204. * Commands to add the item (space delimited)
  5205. *
  5206. * @type String
  5207. */
  5208. cmdsToAdd : 'archive duplicate extract mkdir mkfile paste rm upload',
  5209. parseUploadData : function(text) {
  5210. var self = this,
  5211. data;
  5212. if (!$.trim(text)) {
  5213. return {error : ['errResponse', 'errDataEmpty']};
  5214. }
  5215. try {
  5216. data = JSON.parse(text);
  5217. } catch (e) {
  5218. return {error : ['errResponse', 'errDataNotJSON']};
  5219. }
  5220. data = self.normalize(data);
  5221. if (!self.validResponse('upload', data)) {
  5222. return {error : (response.norError || ['errResponse'])};
  5223. }
  5224. data.removed = $.merge((data.removed || []), $.map(data.added || [], function(f) { return self.file(f.hash)? f.hash : null; }));
  5225. return data;
  5226. },
  5227. iframeCnt : 0,
  5228. uploads : {
  5229. // xhr muiti uploading flag
  5230. xhrUploading: false,
  5231. // Timer of request fail to sync
  5232. failSyncTm: null,
  5233. // current chunkfail requesting chunk
  5234. chunkfailReq: {},
  5235. // check file/dir exists
  5236. checkExists: function(files, target, fm, isDir) {
  5237. var dfrd = $.Deferred(),
  5238. names, renames = [], hashes = {}, chkFiles = [],
  5239. cancel = function() {
  5240. var i = files.length;
  5241. while (--i > -1) {
  5242. files[i]._remove = true;
  5243. }
  5244. },
  5245. resolve = function() {
  5246. dfrd.resolve(renames, hashes);
  5247. },
  5248. check = function() {
  5249. var existed = [], exists = [], i, c,
  5250. pathStr = target !== fm.cwd().hash? fm.path(target, true) + fm.option('separator', target) : '',
  5251. confirm = function(ndx) {
  5252. var last = ndx == exists.length-1,
  5253. opts = {
  5254. cssClass : 'elfinder-confirm-upload',
  5255. title : fm.i18n('cmdupload'),
  5256. text : ['errExists', pathStr + exists[ndx].name, 'confirmRepl'],
  5257. all : !last,
  5258. accept : {
  5259. label : 'btnYes',
  5260. callback : function(all) {
  5261. !last && !all
  5262. ? confirm(++ndx)
  5263. : resolve();
  5264. }
  5265. },
  5266. reject : {
  5267. label : 'btnNo',
  5268. callback : function(all) {
  5269. var i;
  5270. if (all) {
  5271. i = exists.length;
  5272. while (ndx < i--) {
  5273. files[exists[i].i]._remove = true;
  5274. }
  5275. } else {
  5276. files[exists[ndx].i]._remove = true;
  5277. }
  5278. !last && !all
  5279. ? confirm(++ndx)
  5280. : resolve();
  5281. }
  5282. },
  5283. cancel : {
  5284. label : 'btnCancel',
  5285. callback : function() {
  5286. cancel();
  5287. resolve();
  5288. }
  5289. },
  5290. buttons : [
  5291. {
  5292. label : 'btnBackup',
  5293. cssClass : 'elfinder-confirm-btn-backup',
  5294. callback : function(all) {
  5295. var i;
  5296. if (all) {
  5297. i = exists.length;
  5298. while (ndx < i--) {
  5299. renames.push(exists[i].name);
  5300. }
  5301. } else {
  5302. renames.push(exists[ndx].name);
  5303. }
  5304. !last && !all
  5305. ? confirm(++ndx)
  5306. : resolve();
  5307. }
  5308. }
  5309. ]
  5310. };
  5311. if (!isDir) {
  5312. opts.buttons.push({
  5313. label : 'btnRename' + (last? '' : 'All'),
  5314. cssClass : 'elfinder-confirm-btn-rename',
  5315. callback : function() {
  5316. renames = null;
  5317. resolve();
  5318. }
  5319. });
  5320. }
  5321. if (fm.iframeCnt > 0) {
  5322. delete opts.reject;
  5323. }
  5324. fm.confirm(opts);
  5325. };
  5326. if (! fm.file(target).read) {
  5327. // for dropbox type
  5328. resolve();
  5329. return;
  5330. }
  5331. names = $.map(files, function(file, i) { return file.name && (!fm.UA.iOS || file.name !== 'image.jpg')? {i: i, name: file.name} : null ;});
  5332. fm.request({
  5333. data : {cmd : 'ls', target : target, intersect : $.map(names, function(item) { return item.name;})},
  5334. notify : {type : 'preupload', cnt : 1, hideCnt : true},
  5335. preventDefault : true
  5336. })
  5337. .done(function(data) {
  5338. var existedArr, cwdItems;
  5339. if (data) {
  5340. if (data.error) {
  5341. cancel();
  5342. } else {
  5343. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  5344. if (data.list) {
  5345. if (Array.isArray(data.list)) {
  5346. existed = data.list || [];
  5347. } else {
  5348. existedArr = [];
  5349. existed = $.map(data.list, function(n) {
  5350. if (typeof n === 'string') {
  5351. return n;
  5352. } else {
  5353. // support to >=2.1.11 plugin Normalizer, Sanitizer
  5354. existedArr = existedArr.concat(n);
  5355. return false;
  5356. }
  5357. });
  5358. if (existedArr.length) {
  5359. existed = existed.concat(existedArr);
  5360. }
  5361. hashes = data.list;
  5362. }
  5363. exists = $.grep(names, function(name){
  5364. return $.inArray(name.name, existed) !== -1 ? true : false ;
  5365. });
  5366. if (exists.length && existed.length && target == fm.cwd().hash) {
  5367. cwdItems = $.map(fm.files(target), function(file) { return file.name; } );
  5368. if ($.grep(existed, function(n) {
  5369. return $.inArray(n, cwdItems) === -1? true : false;
  5370. }).length){
  5371. fm.sync();
  5372. }
  5373. }
  5374. }
  5375. }
  5376. }
  5377. }
  5378. if (exists.length > 0) {
  5379. confirm(0);
  5380. } else {
  5381. resolve();
  5382. }
  5383. })
  5384. .fail(function(error) {
  5385. cancel();
  5386. resolve();
  5387. error && fm.error(error);
  5388. });
  5389. };
  5390. if (fm.api >= 2.1 && typeof files[0] == 'object') {
  5391. check();
  5392. } else {
  5393. resolve();
  5394. }
  5395. return dfrd;
  5396. },
  5397. // check droped contents
  5398. checkFile : function(data, fm, target) {
  5399. if (!!data.checked || data.type == 'files') {
  5400. return data.files;
  5401. } else if (data.type == 'data') {
  5402. var dfrd = $.Deferred(),
  5403. scanDfd = $.Deferred(),
  5404. files = [],
  5405. paths = [],
  5406. dirctorys = [],
  5407. processing = 0,
  5408. items,
  5409. mkdirs = [],
  5410. cancel = false,
  5411. toArray = function(list) {
  5412. return Array.prototype.slice.call(list || [], 0);
  5413. },
  5414. doScan = function(items) {
  5415. var entry, readEntries,
  5416. excludes = fm.options.folderUploadExclude[fm.OS] || null,
  5417. length = items.length,
  5418. check = function() {
  5419. if (--processing < 1 && scanDfd.state() === 'pending') {
  5420. scanDfd.resolve();
  5421. }
  5422. },
  5423. pushItem = function(file) {
  5424. if (! excludes || ! file.name.match(excludes)) {
  5425. paths.push(entry.fullPath || '');
  5426. files.push(file);
  5427. }
  5428. check();
  5429. },
  5430. readEntries = function(dirReader) {
  5431. var entries = [],
  5432. read = function() {
  5433. dirReader.readEntries(function(results) {
  5434. if (cancel || !results.length) {
  5435. for (var i = 0; i < entries.length; i++) {
  5436. if (cancel) {
  5437. scanDfd.reject();
  5438. break;
  5439. }
  5440. doScan([entries[i]]);
  5441. }
  5442. check();
  5443. } else {
  5444. entries = entries.concat(toArray(results));
  5445. read();
  5446. }
  5447. }, check);
  5448. };
  5449. read();
  5450. };
  5451. processing++;
  5452. for (var i = 0; i < length; i++) {
  5453. if (cancel) {
  5454. scanDfd.reject();
  5455. break;
  5456. }
  5457. entry = items[i];
  5458. if (entry) {
  5459. if (entry.isFile) {
  5460. processing++;
  5461. entry.file(pushItem, check);
  5462. } else if (entry.isDirectory) {
  5463. if (fm.api >= 2.1) {
  5464. processing++;
  5465. mkdirs.push(entry.fullPath);
  5466. readEntries(entry.createReader()); // Start reading dirs.
  5467. }
  5468. }
  5469. }
  5470. }
  5471. check();
  5472. return scanDfd;
  5473. }, hasDirs;
  5474. items = $.map(data.files.items, function(item){
  5475. return item.getAsEntry? item.getAsEntry() : item.webkitGetAsEntry();
  5476. });
  5477. $.each(items, function(i, item) {
  5478. if (item.isDirectory) {
  5479. hasDirs = true;
  5480. return false;
  5481. }
  5482. });
  5483. if (items.length > 0) {
  5484. fm.uploads.checkExists(items, target, fm, hasDirs).done(function(renames, hashes){
  5485. var dfds = [];
  5486. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  5487. if (renames === null) {
  5488. data.overwrite = 0;
  5489. renames = [];
  5490. }
  5491. items = $.grep(items, function(item){
  5492. var i, bak, hash, dfd, hi;
  5493. if (item.isDirectory && renames.length) {
  5494. i = $.inArray(item.name, renames);
  5495. if (i !== -1) {
  5496. renames.splice(i, 1);
  5497. bak = fm.uniqueName(item.name + fm.options.backupSuffix , null, '');
  5498. $.each(hashes, function(h, name) {
  5499. if (item.name == name) {
  5500. hash = h;
  5501. return false;
  5502. }
  5503. });
  5504. if (! hash) {
  5505. hash = fm.fileByName(item.name, target).hash;
  5506. }
  5507. fm.lockfiles({files : [hash]});
  5508. dfd = fm.request({
  5509. data : {cmd : 'rename', target : hash, name : bak},
  5510. notify : {type : 'rename', cnt : 1}
  5511. })
  5512. .fail(function() {
  5513. item._remove = true;
  5514. fm.sync();
  5515. })
  5516. .always(function() {
  5517. fm.unlockfiles({files : [hash]});
  5518. });
  5519. dfds.push(dfd);
  5520. }
  5521. }
  5522. return !item._remove? true : false;
  5523. });
  5524. }
  5525. $.when.apply($, dfds).done(function(){
  5526. var notifyto, msg,
  5527. id = +new Date();
  5528. if (items.length > 0) {
  5529. msg = fm.escape(items[0].name);
  5530. if (items.length > 1) {
  5531. msg += ' ... ' + items.length + fm.i18n('items');
  5532. }
  5533. notifyto = setTimeout(function() {
  5534. fm.notify({
  5535. type : 'readdir',
  5536. id : id,
  5537. cnt : 1,
  5538. hideCnt: true,
  5539. msg : fm.i18n('ntfreaddir') + ' (' + msg + ')',
  5540. cancel: function() {
  5541. cancel = true;
  5542. }
  5543. });
  5544. }, fm.options.notifyDelay);
  5545. doScan(items).done(function() {
  5546. notifyto && clearTimeout(notifyto);
  5547. fm.notify({type : 'readdir', id: id, cnt : -1});
  5548. if (cancel) {
  5549. dfrd.reject();
  5550. } else {
  5551. dfrd.resolve([files, paths, renames, hashes, mkdirs]);
  5552. }
  5553. }).fail(function() {
  5554. dfrd.reject();
  5555. });
  5556. } else {
  5557. dfrd.reject();
  5558. }
  5559. });
  5560. });
  5561. return dfrd.promise();
  5562. } else {
  5563. return dfrd.reject();
  5564. }
  5565. } else {
  5566. var ret = [];
  5567. var check = [];
  5568. var str = data.files[0];
  5569. if (data.type == 'html') {
  5570. var tmp = $("<html/>").append($.parseHTML(str.replace(/ src=/ig, ' _elfsrc='))),
  5571. atag;
  5572. $('img[_elfsrc]', tmp).each(function(){
  5573. var url, purl,
  5574. self = $(this),
  5575. pa = self.closest('a');
  5576. if (pa && pa.attr('href') && pa.attr('href').match(/\.(?:jpe?g|gif|bmp|png)/i)) {
  5577. purl = pa.attr('href');
  5578. }
  5579. url = self.attr('_elfsrc');
  5580. if (url) {
  5581. if (purl) {
  5582. $.inArray(purl, ret) == -1 && ret.push(purl);
  5583. $.inArray(url, check) == -1 && check.push(url);
  5584. } else {
  5585. $.inArray(url, ret) == -1 && ret.push(url);
  5586. }
  5587. }
  5588. // Probably it's clipboard data
  5589. if (ret.length === 1 && ret[0].match(/^data:image\/png/)) {
  5590. data.clipdata = true;
  5591. }
  5592. });
  5593. atag = $('a[href]', tmp);
  5594. atag.each(function(){
  5595. var text, loc,
  5596. parseUrl = function(url) {
  5597. var a = document.createElement('a');
  5598. a.href = url;
  5599. return a;
  5600. };
  5601. if (text = $(this).text()) {
  5602. loc = parseUrl($(this).attr('href'));
  5603. 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))) {
  5604. if ($.inArray(loc.href, ret) == -1 && $.inArray(loc.href, check) == -1) ret.push(loc.href);
  5605. }
  5606. }
  5607. });
  5608. } else {
  5609. var regex, m, url;
  5610. regex = /(http[^<>"{}|\\^\[\]`\s]+)/ig;
  5611. while (m = regex.exec(str)) {
  5612. url = m[1].replace(/&amp;/g, '&');
  5613. if ($.inArray(url, ret) == -1) ret.push(url);
  5614. }
  5615. }
  5616. return ret;
  5617. }
  5618. },
  5619. // upload transport using XMLHttpRequest
  5620. xhr : function(data, fm) {
  5621. var self = fm ? fm : this,
  5622. node = self.getUI(),
  5623. xhr = new XMLHttpRequest(),
  5624. notifyto = null, notifyto2 = null,
  5625. dataChecked = data.checked,
  5626. isDataType = (data.isDataType || data.type == 'data'),
  5627. target = (data.target || self.cwd().hash),
  5628. dropEvt = (data.dropEvt || null),
  5629. extraData  = data.extraData || null,
  5630. chunkEnable = (self.option('uploadMaxConn', target) != -1),
  5631. multiMax = Math.min(5, Math.max(1, self.option('uploadMaxConn', target))),
  5632. retryWait = 10000, // 10 sec
  5633. retryMax = 30, // 10 sec * 30 = 300 secs (Max 5 mins)
  5634. retry = 0,
  5635. getFile = function(files) {
  5636. var dfd = $.Deferred(),
  5637. file;
  5638. if (files.promise) {
  5639. files.always(function(f) {
  5640. dfd.resolve(Array.isArray(f) && f.length? (isDataType? f[0][0] : f[0]) : {});
  5641. });
  5642. } else {
  5643. dfd.resolve(files.length? (isDataType? files[0][0] : files[0]) : {});
  5644. }
  5645. return dfd;
  5646. },
  5647. dfrd = $.Deferred()
  5648. .fail(function(err) {
  5649. var error = self.parseError(err),
  5650. userAbort;
  5651. if (error === 'userabort') {
  5652. userAbort = true;
  5653. error = void 0;
  5654. }
  5655. if (files && (self.uploads.xhrUploading || userAbort)) {
  5656. // send request om fail
  5657. getFile(files).done(function(file) {
  5658. if (! file._cid) {
  5659. // send sync request
  5660. self.uploads.failSyncTm && clearTimeout(self.uploads.failSyncTm);
  5661. self.uploads.failSyncTm = setTimeout(function() {
  5662. self.sync(target);
  5663. }, 1000);
  5664. } else if (! self.uploads.chunkfailReq[file._cid]) {
  5665. // send chunkfail request
  5666. self.uploads.chunkfailReq[file._cid] = true;
  5667. setTimeout(function() {
  5668. fm.request({
  5669. data : {
  5670. cmd: 'upload',
  5671. target: target,
  5672. chunk: file._chunk,
  5673. cid: file._cid,
  5674. upload: ['chunkfail'],
  5675. mimes: 'chunkfail'
  5676. },
  5677. options : {
  5678. type: 'post',
  5679. url: self.uploadURL
  5680. },
  5681. preventDefault: true
  5682. }).always(function() {
  5683. delete self.uploads.chunkfailReq[file._chunk];
  5684. });
  5685. }, 1000);
  5686. }
  5687. });
  5688. }
  5689. !userAbort && self.sync();
  5690. self.uploads.xhrUploading = false;
  5691. files = null;
  5692. error && self.error(error);
  5693. })
  5694. .done(function(data) {
  5695. self.uploads.xhrUploading = false;
  5696. files = null;
  5697. if (data) {
  5698. self.currentReqCmd = 'upload';
  5699. data.warning && self.error(data.warning);
  5700. self.updateCache(data);
  5701. data.removed && data.removed.length && self.remove(data);
  5702. data.added && data.added.length && self.add(data);
  5703. data.changed && data.changed.length && self.change(data);
  5704. self.trigger('upload', data, false);
  5705. self.trigger('uploaddone');
  5706. if (data.toasts && Array.isArray(data.toasts)) {
  5707. $.each(data.toasts, function() {
  5708. this.msg && self.toast(this);
  5709. });
  5710. }
  5711. data.sync && self.sync();
  5712. data.debug && fm.debug('backend-debug', data);
  5713. }
  5714. })
  5715. .always(function() {
  5716. self.abortXHR(xhr);
  5717. // unregist fnAbort function
  5718. node.off('uploadabort', fnAbort);
  5719. $(window).off('unload', fnAbort);
  5720. notifyto && clearTimeout(notifyto);
  5721. notifyto2 && clearTimeout(notifyto2);
  5722. dataChecked && !data.multiupload && checkNotify() && self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  5723. chunkMerge && notifyElm.children('.elfinder-notify-chunkmerge').length && self.notify({type : 'chunkmerge', cnt : -1});
  5724. }),
  5725. formData = new FormData(),
  5726. files = data.input ? data.input.files : self.uploads.checkFile(data, self, target),
  5727. cnt = data.checked? (isDataType? files[0].length : files.length) : files.length,
  5728. loaded = 0,
  5729. prev = 0,
  5730. filesize = 0,
  5731. notify = false,
  5732. notifyElm = self.ui.notify,
  5733. cancelBtn = true,
  5734. abort = false,
  5735. checkNotify = function() {
  5736. if (!notify && (ntfUpload = notifyElm.children('.elfinder-notify-upload')).length) {
  5737. notify = true;
  5738. }
  5739. return notify;
  5740. },
  5741. fnAbort = function(e, error) {
  5742. abort = true;
  5743. self.abortXHR(xhr, { quiet: true, abort: true });
  5744. dfrd.reject(error);
  5745. if (checkNotify()) {
  5746. self.notify({type : 'upload', cnt : ntfUpload.data('cnt') * -1, progress : 0, size : 0});
  5747. }
  5748. },
  5749. cancelToggle = function(show) {
  5750. ntfUpload.children('.elfinder-notify-cancel')[show? 'show':'hide']();
  5751. },
  5752. startNotify = function(size) {
  5753. if (!size) size = filesize;
  5754. return setTimeout(function() {
  5755. notify = true;
  5756. self.notify({type : 'upload', cnt : cnt, progress : loaded - prev, size : size,
  5757. cancel: function() {
  5758. node.trigger('uploadabort', 'userabort');
  5759. }
  5760. });
  5761. ntfUpload = notifyElm.children('.elfinder-notify-upload');
  5762. prev = loaded;
  5763. if (data.multiupload) {
  5764. cancelBtn && cancelToggle(true);
  5765. } else {
  5766. cancelToggle(cancelBtn && loaded < size);
  5767. }
  5768. }, self.options.notifyDelay);
  5769. },
  5770. doRetry = function() {
  5771. if (retry++ <= retryMax) {
  5772. if (checkNotify() && prev) {
  5773. self.notify({type : 'upload', cnt : 0, progress : 0, size : prev});
  5774. }
  5775. self.abortXHR(xhr, { quiet: true });
  5776. prev = loaded = 0;
  5777. setTimeout(function() {
  5778. var reqId;
  5779. if (! abort) {
  5780. xhr.open('POST', self.uploadURL, true);
  5781. if (self.api >= 2.1029) {
  5782. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
  5783. (typeof formData['delete'] === 'function') && formData['delete']('reqid');
  5784. formData.append('reqid', reqId);
  5785. xhr._requestId = reqId;
  5786. }
  5787. xhr.send(formData);
  5788. }
  5789. }, retryWait);
  5790. } else {
  5791. node.trigger('uploadabort', ['errAbort', 'errTimeout']);
  5792. }
  5793. },
  5794. progress = function() {
  5795. var node;
  5796. if (notify) {
  5797. dfrd.notifyWith(ntfUpload, [{
  5798. cnt: ntfUpload.data('cnt'),
  5799. progress: ntfUpload.data('progress'),
  5800. total: ntfUpload.data('total')
  5801. }]);
  5802. }
  5803. },
  5804. renames = (data.renames || null),
  5805. hashes = (data.hashes || null),
  5806. chunkMerge = false,
  5807. ntfUpload = $();
  5808. // regist fnAbort function
  5809. node.one('uploadabort', fnAbort);
  5810. $(window).one('unload.' + fm.namespace, fnAbort);
  5811. !chunkMerge && (prev = loaded);
  5812. if (!isDataType && !cnt) {
  5813. return dfrd.reject(['errUploadNoFiles']);
  5814. }
  5815. xhr.addEventListener('error', function() {
  5816. if (xhr.status == 0) {
  5817. if (abort) {
  5818. dfrd.reject();
  5819. } else {
  5820. // ff bug while send zero sized file
  5821. // for safari - send directory
  5822. if (!isDataType && data.files && $.grep(data.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) {
  5823. errors.push('errFolderUpload');
  5824. dfrd.reject(['errAbort', 'errFolderUpload']);
  5825. } else if (data.input && $.grep(data.input.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) {
  5826. dfrd.reject(['errUploadNoFiles']);
  5827. } else {
  5828. doRetry();
  5829. }
  5830. }
  5831. } else {
  5832. node.trigger('uploadabort', 'errConnect');
  5833. }
  5834. }, false);
  5835. xhr.addEventListener('load', function(e) {
  5836. var status = xhr.status, res, curr = 0, error = '', errData, errObj;
  5837. if (status >= 400) {
  5838. if (status > 500) {
  5839. error = 'errResponse';
  5840. } else {
  5841. error = ['errResponse', 'errServerError'];
  5842. }
  5843. } else {
  5844. if (!xhr.responseText) {
  5845. error = ['errResponse', 'errDataEmpty'];
  5846. }
  5847. }
  5848. if (error) {
  5849. node.trigger('uploadabort');
  5850. getFile(files).done(function(file) {
  5851. return dfrd.reject(file._cid? null : error);
  5852. });
  5853. }
  5854. loaded = filesize;
  5855. if (checkNotify() && (curr = loaded - prev)) {
  5856. self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
  5857. progress();
  5858. }
  5859. res = self.parseUploadData(xhr.responseText);
  5860. // chunked upload commit
  5861. if (res._chunkmerged) {
  5862. formData = new FormData();
  5863. var _file = [{_chunkmerged: res._chunkmerged, _name: res._name, _mtime: res._mtime}];
  5864. chunkMerge = true;
  5865. node.off('uploadabort', fnAbort);
  5866. notifyto2 = setTimeout(function() {
  5867. self.notify({type : 'chunkmerge', cnt : 1});
  5868. }, self.options.notifyDelay);
  5869. isDataType? send(_file, files[1]) : send(_file);
  5870. return;
  5871. }
  5872. res._multiupload = data.multiupload? true : false;
  5873. if (res.error) {
  5874. errData = {
  5875. cmd: 'upload',
  5876. err: res,
  5877. xhr: xhr,
  5878. rc: xhr.status
  5879. };
  5880. self.trigger('uploadfail', res);
  5881. // trigger "requestError" event
  5882. self.trigger('requestError', errData);
  5883. if (errData._event && errData._event.isDefaultPrevented()) {
  5884. res.error = '';
  5885. }
  5886. if (res._chunkfailure || res._multiupload) {
  5887. abort = true;
  5888. self.uploads.xhrUploading = false;
  5889. notifyto && clearTimeout(notifyto);
  5890. if (ntfUpload.length) {
  5891. self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  5892. dfrd.reject(res);
  5893. } else {
  5894. // for multi connection
  5895. dfrd.reject();
  5896. }
  5897. } else {
  5898. dfrd.reject(res);
  5899. }
  5900. } else {
  5901. dfrd.resolve(res);
  5902. }
  5903. }, false);
  5904. xhr.upload.addEventListener('loadstart', function(e) {
  5905. if (!chunkMerge && e.lengthComputable) {
  5906. loaded = e.loaded;
  5907. retry && (loaded = 0);
  5908. filesize = e.total;
  5909. if (!loaded) {
  5910. loaded = parseInt(filesize * 0.05);
  5911. }
  5912. if (checkNotify()) {
  5913. self.notify({type : 'upload', cnt : 0, progress : loaded - prev, size : data.multiupload? 0 : filesize});
  5914. prev = loaded;
  5915. progress();
  5916. }
  5917. }
  5918. }, false);
  5919. xhr.upload.addEventListener('progress', function(e) {
  5920. var curr;
  5921. if (e.lengthComputable && !chunkMerge && xhr.readyState < 2) {
  5922. loaded = e.loaded;
  5923. // to avoid strange bug in safari (not in chrome) with drag&drop.
  5924. // bug: macos finder opened in any folder,
  5925. // reset safari cache (option+command+e), reload elfinder page,
  5926. // drop file from finder
  5927. // on first attempt request starts (progress callback called ones) but never ends.
  5928. // any next drop - successfull.
  5929. if (!data.checked && loaded > 0 && !notifyto) {
  5930. notifyto = startNotify(xhr._totalSize - loaded);
  5931. }
  5932. if (!filesize) {
  5933. filesize = e.total;
  5934. if (!loaded) {
  5935. loaded = parseInt(filesize * 0.05);
  5936. }
  5937. }
  5938. curr = loaded - prev;
  5939. if (checkNotify() && (curr/e.total) >= 0.05) {
  5940. self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
  5941. prev = loaded;
  5942. progress();
  5943. }
  5944. if (! data.multiupload && loaded >= filesize) {
  5945. cancelBtn = false;
  5946. cancelToggle(false);
  5947. }
  5948. }
  5949. }, false);
  5950. var send = function(files, paths){
  5951. var size = 0,
  5952. fcnt = 1,
  5953. sfiles = [],
  5954. c = 0,
  5955. total = cnt,
  5956. maxFileSize,
  5957. totalSize = 0,
  5958. chunked = [],
  5959. chunkID = new Date().getTime().toString().substr(-9), // for take care of the 32bit backend system
  5960. BYTES_PER_CHUNK = Math.min((fm.uplMaxSize? fm.uplMaxSize : 2097152) - 8190, fm.options.uploadMaxChunkSize), // uplMaxSize margin 8kb or options.uploadMaxChunkSize
  5961. blobSlice = chunkEnable? false : '',
  5962. blobSize, blobMtime, i, start, end, chunks, blob, chunk, added, done, last, failChunk,
  5963. multi = function(files, num){
  5964. var sfiles = [], cid, sfilesLen = 0, cancelChk;
  5965. if (!abort) {
  5966. while(files.length && sfiles.length < num) {
  5967. sfiles.push(files.shift());
  5968. }
  5969. sfilesLen = sfiles.length;
  5970. if (sfilesLen) {
  5971. cancelChk = sfilesLen;
  5972. for (var i=0; i < sfilesLen; i++) {
  5973. if (abort) {
  5974. break;
  5975. }
  5976. cid = isDataType? (sfiles[i][0][0]._cid || null) : (sfiles[i][0]._cid || null);
  5977. if (!!failChunk[cid]) {
  5978. last--;
  5979. continue;
  5980. }
  5981. fm.exec('upload', {
  5982. type: data.type,
  5983. isDataType: isDataType,
  5984. files: sfiles[i],
  5985. checked: true,
  5986. target: target,
  5987. dropEvt: dropEvt,
  5988. renames: renames,
  5989. hashes: hashes,
  5990. multiupload: true,
  5991. overwrite: data.overwrite === 0? 0 : void 0
  5992. }, void 0, target)
  5993. .fail(function(error) {
  5994. if (error && error === 'No such command') {
  5995. abort = true;
  5996. fm.error(['errUpload', 'errPerm']);
  5997. }
  5998. if (cid) {
  5999. failChunk[cid] = true;
  6000. }
  6001. })
  6002. .always(function(e) {
  6003. if (e && e.added) added = $.merge(added, e.added);
  6004. if (last <= ++done) {
  6005. fm.trigger('multiupload', {added: added});
  6006. notifyto && clearTimeout(notifyto);
  6007. if (checkNotify()) {
  6008. self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  6009. }
  6010. }
  6011. if (files.length) {
  6012. multi(files, 1); // Next one
  6013. } else {
  6014. if (--cancelChk <= 1) {
  6015. cancelBtn = false;
  6016. cancelToggle(false);
  6017. }
  6018. }
  6019. });
  6020. }
  6021. }
  6022. }
  6023. if (sfiles.length < 1 || abort) {
  6024. if (abort) {
  6025. notifyto && clearTimeout(notifyto);
  6026. if (cid) {
  6027. failChunk[cid] = true;
  6028. }
  6029. dfrd.reject();
  6030. } else {
  6031. dfrd.resolve();
  6032. self.uploads.xhrUploading = false;
  6033. }
  6034. }
  6035. },
  6036. check = function(){
  6037. if (!self.uploads.xhrUploading) {
  6038. self.uploads.xhrUploading = true;
  6039. multi(sfiles, multiMax); // Max connection: 3
  6040. } else {
  6041. setTimeout(check, 100);
  6042. }
  6043. },
  6044. reqId;
  6045. if (! dataChecked && (isDataType || data.type == 'files')) {
  6046. if (! (maxFileSize = fm.option('uploadMaxSize', target))) {
  6047. maxFileSize = 0;
  6048. }
  6049. for (i=0; i < files.length; i++) {
  6050. try {
  6051. blob = files[i];
  6052. blobSize = blob.size;
  6053. if (blobSlice === false) {
  6054. blobSlice = '';
  6055. if (self.api >= 2.1) {
  6056. if ('slice' in blob) {
  6057. blobSlice = 'slice';
  6058. } else if ('mozSlice' in blob) {
  6059. blobSlice = 'mozSlice';
  6060. } else if ('webkitSlice' in blob) {
  6061. blobSlice = 'webkitSlice';
  6062. }
  6063. }
  6064. }
  6065. } catch(e) {
  6066. cnt--;
  6067. total--;
  6068. continue;
  6069. }
  6070. // file size check
  6071. if ((maxFileSize && blobSize > maxFileSize) || (!blobSlice && fm.uplMaxSize && blobSize > fm.uplMaxSize)) {
  6072. self.error(['errUploadFile', blob.name, 'errUploadFileSize']);
  6073. cnt--;
  6074. total--;
  6075. continue;
  6076. }
  6077. // file mime check
  6078. if (blob.type && ! self.uploadMimeCheck(blob.type, target)) {
  6079. self.error(['errUploadFile', blob.name, 'errUploadMime', '(' + blob.type + ')']);
  6080. cnt--;
  6081. total--;
  6082. continue;
  6083. }
  6084. if (blobSlice && blobSize > BYTES_PER_CHUNK) {
  6085. start = 0;
  6086. end = BYTES_PER_CHUNK;
  6087. chunks = -1;
  6088. total = Math.floor((blobSize - 1) / BYTES_PER_CHUNK);
  6089. blobMtime = blob.lastModified? Math.round(blob.lastModified/1000) : 0;
  6090. totalSize += blobSize;
  6091. chunked[chunkID] = 0;
  6092. while(start < blobSize) {
  6093. chunk = blob[blobSlice](start, end);
  6094. chunk._chunk = blob.name + '.' + (++chunks) + '_' + total + '.part';
  6095. chunk._cid = chunkID;
  6096. chunk._range = start + ',' + chunk.size + ',' + blobSize;
  6097. chunk._mtime = blobMtime;
  6098. chunked[chunkID]++;
  6099. if (size) {
  6100. c++;
  6101. }
  6102. if (typeof sfiles[c] == 'undefined') {
  6103. sfiles[c] = [];
  6104. if (isDataType) {
  6105. sfiles[c][0] = [];
  6106. sfiles[c][1] = [];
  6107. }
  6108. }
  6109. size = BYTES_PER_CHUNK;
  6110. fcnt = 1;
  6111. if (isDataType) {
  6112. sfiles[c][0].push(chunk);
  6113. sfiles[c][1].push(paths[i]);
  6114. } else {
  6115. sfiles[c].push(chunk);
  6116. }
  6117. start = end;
  6118. end = start + BYTES_PER_CHUNK;
  6119. }
  6120. if (chunk == null) {
  6121. self.error(['errUploadFile', blob.name, 'errUploadFileSize']);
  6122. cnt--;
  6123. total--;
  6124. } else {
  6125. total += chunks;
  6126. size = 0;
  6127. fcnt = 1;
  6128. c++;
  6129. }
  6130. continue;
  6131. }
  6132. if ((fm.uplMaxSize && size + blobSize > fm.uplMaxSize) || fcnt > fm.uplMaxFile) {
  6133. size = 0;
  6134. fcnt = 1;
  6135. c++;
  6136. }
  6137. if (typeof sfiles[c] == 'undefined') {
  6138. sfiles[c] = [];
  6139. if (isDataType) {
  6140. sfiles[c][0] = [];
  6141. sfiles[c][1] = [];
  6142. }
  6143. }
  6144. if (isDataType) {
  6145. sfiles[c][0].push(blob);
  6146. sfiles[c][1].push(paths[i]);
  6147. } else {
  6148. sfiles[c].push(blob);
  6149. }
  6150. size += blobSize;
  6151. totalSize += blobSize;
  6152. fcnt++;
  6153. }
  6154. if (sfiles.length == 0) {
  6155. // no data
  6156. data.checked = true;
  6157. return false;
  6158. }
  6159. if (sfiles.length > 1) {
  6160. // multi upload
  6161. notifyto = startNotify(totalSize);
  6162. added = [];
  6163. done = 0;
  6164. last = sfiles.length;
  6165. failChunk = [];
  6166. check();
  6167. return true;
  6168. }
  6169. // single upload
  6170. if (isDataType) {
  6171. files = sfiles[0][0];
  6172. paths = sfiles[0][1];
  6173. } else {
  6174. files = sfiles[0];
  6175. }
  6176. }
  6177. if (!dataChecked) {
  6178. if (!fm.UA.Safari || !data.files) {
  6179. notifyto = startNotify(totalSize);
  6180. } else {
  6181. xhr._totalSize = totalSize;
  6182. }
  6183. }
  6184. dataChecked = true;
  6185. if (! files.length) {
  6186. dfrd.reject(['errUploadNoFiles']);
  6187. }
  6188. xhr.open('POST', self.uploadURL, true);
  6189. // set request headers
  6190. if (fm.customHeaders) {
  6191. $.each(fm.customHeaders, function(key) {
  6192. xhr.setRequestHeader(key, this);
  6193. });
  6194. }
  6195. // set xhrFields
  6196. if (fm.xhrFields) {
  6197. $.each(fm.xhrFields, function(key) {
  6198. if (key in xhr) {
  6199. xhr[key] = this;
  6200. }
  6201. });
  6202. }
  6203. if (self.api >= 2.1029) {
  6204. // request ID
  6205. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
  6206. formData.append('reqid', reqId);
  6207. xhr._requestId = reqId;
  6208. }
  6209. formData.append('cmd', 'upload');
  6210. formData.append(self.newAPI ? 'target' : 'current', target);
  6211. if (renames && renames.length) {
  6212. $.each(renames, function(i, v) {
  6213. formData.append('renames[]', v);
  6214. });
  6215. formData.append('suffix', fm.options.backupSuffix);
  6216. }
  6217. if (hashes) {
  6218. $.each(hashes, function(i, v) {
  6219. formData.append('hashes['+ i +']', v);
  6220. });
  6221. }
  6222. $.each(self.customData, function(key, val) {
  6223. formData.append(key, val);
  6224. });
  6225. $.each(self.options.onlyMimes, function(i, mime) {
  6226. formData.append('mimes[]', mime);
  6227. });
  6228. $.each(files, function(i, file) {
  6229. if (file._chunkmerged) {
  6230. formData.append('chunk', file._chunkmerged);
  6231. formData.append('upload[]', file._name);
  6232. formData.append('mtime[]', file._mtime);
  6233. } else {
  6234. if (file._chunkfail) {
  6235. formData.append('upload[]', 'chunkfail');
  6236. formData.append('mimes', 'chunkfail');
  6237. } else {
  6238. formData.append('upload[]', file);
  6239. if (data.clipdata) {
  6240. data.overwrite = 0;
  6241. formData.append('name[]', fm.date(fm.nonameDateFormat) + '.png');
  6242. }
  6243. if (file.name && fm.UA.iOS) {
  6244. if (file.name.match(/^image\.jpe?g$/i)) {
  6245. data.overwrite = 0;
  6246. formData.append('name[]', fm.date(fm.nonameDateFormat) + '.jpg');
  6247. } else if (file.name.match(/^capturedvideo\.mov$/i)) {
  6248. data.overwrite = 0;
  6249. formData.append('name[]', fm.date(fm.nonameDateFormat) + '.mov');
  6250. }
  6251. }
  6252. }
  6253. if (file._chunk) {
  6254. formData.append('chunk', file._chunk);
  6255. formData.append('cid' , file._cid);
  6256. formData.append('range', file._range);
  6257. formData.append('mtime[]', file._mtime);
  6258. } else {
  6259. formData.append('mtime[]', file.lastModified? Math.round(file.lastModified/1000) : 0);
  6260. }
  6261. }
  6262. });
  6263. if (isDataType) {
  6264. $.each(paths, function(i, path) {
  6265. formData.append('upload_path[]', path);
  6266. });
  6267. }
  6268. if (data.overwrite === 0) {
  6269. formData.append('overwrite', 0);
  6270. }
  6271. // send int value that which meta key was pressed when dropped as `dropWith`
  6272. if (dropEvt) {
  6273. formData.append('dropWith', parseInt(
  6274. (dropEvt.altKey ? '1' : '0')+
  6275. (dropEvt.ctrlKey ? '1' : '0')+
  6276. (dropEvt.metaKey ? '1' : '0')+
  6277. (dropEvt.shiftKey? '1' : '0'), 2));
  6278. }
  6279. // set extraData on current request
  6280. if (extraData) {
  6281. $.each(extraData, function(key, val) {
  6282. formData.append(key, val);
  6283. });
  6284. }
  6285. xhr.send(formData);
  6286. return true;
  6287. };
  6288. if (! isDataType) {
  6289. if (files.length > 0) {
  6290. if (! data.clipdata && renames == null) {
  6291. var mkdirs = [],
  6292. paths = [],
  6293. excludes = fm.options.folderUploadExclude[fm.OS] || null;
  6294. $.each(files, function(i, file) {
  6295. var relPath = file.webkitRelativePath || file.relativePath || '',
  6296. idx, rootDir;
  6297. if (! relPath) {
  6298. return false;
  6299. }
  6300. if (excludes && file.name.match(excludes)) {
  6301. file._remove = true;
  6302. relPath = void(0);
  6303. } else {
  6304. // add '/' as prefix to make same to folder uploading with DnD, see #2607
  6305. relPath = '/' + relPath.replace(/\/[^\/]*$/, '').replace(/^\//, '');
  6306. if (relPath && $.inArray(relPath, mkdirs) === -1) {
  6307. mkdirs.push(relPath);
  6308. // checking the root directory to supports <input type="file" webkitdirectory> see #2378
  6309. idx = relPath.substr(1).indexOf('/');
  6310. if (idx !== -1 && (rootDir = relPath.substr(0, idx + 1)) && $.inArray(rootDir, mkdirs) === -1) {
  6311. mkdirs.unshift(rootDir);
  6312. }
  6313. }
  6314. }
  6315. paths.push(relPath);
  6316. });
  6317. renames = [];
  6318. hashes = {};
  6319. if (mkdirs.length) {
  6320. (function() {
  6321. var checkDirs = $.map(mkdirs, function(name) { return name.substr(1).indexOf('/') === -1 ? {name: name.substr(1)} : null;}),
  6322. cancelDirs = [];
  6323. fm.uploads.checkExists(checkDirs, target, fm, true).done(
  6324. function(res, res2) {
  6325. var dfds = [], dfd, bak, hash;
  6326. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  6327. cancelDirs = $.map(checkDirs, function(dir) { return dir._remove? dir.name : null ;} );
  6328. checkDirs = $.grep(checkDirs, function(dir) { return !dir._remove? true : false ;} );
  6329. }
  6330. if (cancelDirs.length) {
  6331. $.each(paths.concat(), function(i, path) {
  6332. if ($.inArray(path, cancelDirs) === 0) {
  6333. files[i]._remove = true;
  6334. paths[i] = void(0);
  6335. }
  6336. });
  6337. }
  6338. files = $.grep(files, function(file) { return file._remove? false : true; });
  6339. paths = $.grep(paths, function(path) { return path === void 0 ? false : true; });
  6340. if (checkDirs.length) {
  6341. dfd = $.Deferred();
  6342. if (res.length) {
  6343. $.each(res, function(i, existName) {
  6344. // backup
  6345. bak = fm.uniqueName(existName + fm.options.backupSuffix , null, '');
  6346. $.each(res2, function(h, name) {
  6347. if (res[0] == name) {
  6348. hash = h;
  6349. return false;
  6350. }
  6351. });
  6352. if (! hash) {
  6353. hash = fm.fileByName(res[0], target).hash;
  6354. }
  6355. fm.lockfiles({files : [hash]});
  6356. dfds.push(
  6357. fm.request({
  6358. data : {cmd : 'rename', target : hash, name : bak},
  6359. notify : {type : 'rename', cnt : 1}
  6360. })
  6361. .fail(function(error) {
  6362. dfrd.reject(error);
  6363. fm.sync();
  6364. })
  6365. .always(function() {
  6366. fm.unlockfiles({files : [hash]});
  6367. })
  6368. );
  6369. });
  6370. } else {
  6371. dfds.push(null);
  6372. }
  6373. $.when.apply($, dfds).done(function() {
  6374. // ensure directories
  6375. fm.request({
  6376. data : {cmd : 'mkdir', target : target, dirs : mkdirs},
  6377. notify : {type : 'mkdir', cnt : mkdirs.length},
  6378. preventFail: true
  6379. })
  6380. .fail(function(error) {
  6381. error = error || ['errUnknown'];
  6382. if (error[0] === 'errCmdParams') {
  6383. multiMax = 1;
  6384. } else {
  6385. multiMax = 0;
  6386. dfrd.reject(error);
  6387. }
  6388. })
  6389. .done(function(data) {
  6390. var rm = false;
  6391. if (!data.hashes) {
  6392. data.hashes = {};
  6393. }
  6394. paths = $.map(paths.concat(), function(p, i) {
  6395. if (p === '/') {
  6396. return target;
  6397. } else {
  6398. if (data.hashes[p]) {
  6399. return data.hashes[p];
  6400. } else {
  6401. rm = true;
  6402. files[i]._remove = true;
  6403. return null;
  6404. }
  6405. }
  6406. });
  6407. if (rm) {
  6408. files = $.grep(files, function(file) { return file._remove? false : true; });
  6409. }
  6410. })
  6411. .always(function(data) {
  6412. if (multiMax) {
  6413. isDataType = true;
  6414. if (! send(files, paths)) {
  6415. dfrd.reject();
  6416. }
  6417. }
  6418. });
  6419. });
  6420. } else {
  6421. dfrd.reject();
  6422. }
  6423. }
  6424. );
  6425. })();
  6426. } else {
  6427. fm.uploads.checkExists(files, target, fm).done(
  6428. function(res, res2){
  6429. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  6430. hashes = res2;
  6431. if (res === null) {
  6432. data.overwrite = 0;
  6433. } else {
  6434. renames = res;
  6435. }
  6436. files = $.grep(files, function(file){return !file._remove? true : false ;});
  6437. }
  6438. cnt = files.length;
  6439. if (cnt > 0) {
  6440. if (! send(files)) {
  6441. dfrd.reject();
  6442. }
  6443. } else {
  6444. dfrd.reject();
  6445. }
  6446. }
  6447. );
  6448. }
  6449. } else {
  6450. if (! send(files)) {
  6451. dfrd.reject();
  6452. }
  6453. }
  6454. } else {
  6455. dfrd.reject();
  6456. }
  6457. } else {
  6458. if (dataChecked) {
  6459. send(files[0], files[1]);
  6460. } else {
  6461. files.done(function(result) { // result: [files, paths, renames, hashes, mkdirs]
  6462. renames = [];
  6463. cnt = result[0].length;
  6464. if (cnt) {
  6465. if (result[4] && result[4].length) {
  6466. // ensure directories
  6467. fm.request({
  6468. data : {cmd : 'mkdir', target : target, dirs : result[4]},
  6469. notify : {type : 'mkdir', cnt : result[4].length},
  6470. preventFail: true
  6471. })
  6472. .fail(function(error) {
  6473. error = error || ['errUnknown'];
  6474. if (error[0] === 'errCmdParams') {
  6475. multiMax = 1;
  6476. } else {
  6477. multiMax = 0;
  6478. dfrd.reject(error);
  6479. }
  6480. })
  6481. .done(function(data) {
  6482. var rm = false;
  6483. if (!data.hashes) {
  6484. data.hashes = {};
  6485. }
  6486. result[1] = $.map(result[1], function(p, i) {
  6487. p = p.replace(/\/[^\/]*$/, '');
  6488. if (p === '') {
  6489. return target;
  6490. } else {
  6491. if (data.hashes[p]) {
  6492. return data.hashes[p];
  6493. } else {
  6494. rm = true;
  6495. result[0][i]._remove = true;
  6496. return null;
  6497. }
  6498. }
  6499. });
  6500. if (rm) {
  6501. result[0] = $.grep(result[0], function(file) { return file._remove? false : true; });
  6502. }
  6503. })
  6504. .always(function(data) {
  6505. if (multiMax) {
  6506. renames = result[2];
  6507. hashes = result[3];
  6508. send(result[0], result[1]);
  6509. }
  6510. });
  6511. return;
  6512. } else {
  6513. result[1] = $.map(result[1], function() { return target; });
  6514. }
  6515. renames = result[2];
  6516. hashes = result[3];
  6517. send(result[0], result[1]);
  6518. } else {
  6519. dfrd.reject(['errUploadNoFiles']);
  6520. }
  6521. }).fail(function(){
  6522. dfrd.reject();
  6523. });
  6524. }
  6525. }
  6526. return dfrd;
  6527. },
  6528. // upload transport using iframe
  6529. iframe : function(data, fm) {
  6530. var self = fm ? fm : this,
  6531. input = data.input? data.input : false,
  6532. files = !input ? self.uploads.checkFile(data, self) : false,
  6533. dfrd = $.Deferred()
  6534. .fail(function(error) {
  6535. error && self.error(error);
  6536. }),
  6537. name = 'iframe-'+fm.namespace+(++self.iframeCnt),
  6538. 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>'),
  6539. msie = this.UA.IE,
  6540. // clear timeouts, close notification dialog, remove form/iframe
  6541. onload = function() {
  6542. abortto && clearTimeout(abortto);
  6543. notifyto && clearTimeout(notifyto);
  6544. notify && self.notify({type : 'upload', cnt : -cnt});
  6545. setTimeout(function() {
  6546. msie && $('<iframe src="javascript:false;"/>').appendTo(form);
  6547. form.remove();
  6548. iframe.remove();
  6549. }, 100);
  6550. },
  6551. iframe = $('<iframe src="'+(msie ? 'javascript:false;' : 'about:blank')+'" name="'+name+'" style="position:absolute;left:-1000px;top:-1000px" />')
  6552. .on('load', function() {
  6553. iframe.off('load')
  6554. .on('load', function() {
  6555. onload();
  6556. // data will be processed in callback response or window onmessage
  6557. dfrd.resolve();
  6558. });
  6559. // notify dialog
  6560. notifyto = setTimeout(function() {
  6561. notify = true;
  6562. self.notify({type : 'upload', cnt : cnt});
  6563. }, self.options.notifyDelay);
  6564. // emulate abort on timeout
  6565. if (self.options.iframeTimeout > 0) {
  6566. abortto = setTimeout(function() {
  6567. onload();
  6568. dfrd.reject([errors.connect, errors.timeout]);
  6569. }, self.options.iframeTimeout);
  6570. }
  6571. form.submit();
  6572. }),
  6573. target = (data.target || self.cwd().hash),
  6574. names = [],
  6575. dfds = [],
  6576. renames = [],
  6577. hashes = {},
  6578. cnt, notify, notifyto, abortto;
  6579. if (files && files.length) {
  6580. $.each(files, function(i, val) {
  6581. form.append('<input type="hidden" name="upload[]" value="'+val+'"/>');
  6582. });
  6583. cnt = 1;
  6584. } else if (input && $(input).is(':file') && $(input).val()) {
  6585. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  6586. names = input.files? input.files : [{ name: $(input).val().replace(/^(?:.+[\\\/])?([^\\\/]+)$/, '$1') }];
  6587. //names = $.map(names, function(file){return file.name? { name: file.name } : null ;});
  6588. dfds.push(self.uploads.checkExists(names, target, self).done(
  6589. function(res, res2){
  6590. hashes = res2;
  6591. if (res === null) {
  6592. data.overwrite = 0;
  6593. } else{
  6594. renames = res;
  6595. cnt = $.grep(names, function(file){return !file._remove? true : false ;}).length;
  6596. if (cnt != names.length) {
  6597. cnt = 0;
  6598. }
  6599. }
  6600. }
  6601. ));
  6602. }
  6603. cnt = input.files ? input.files.length : 1;
  6604. form.append(input);
  6605. } else {
  6606. return dfrd.reject();
  6607. }
  6608. $.when.apply($, dfds).done(function() {
  6609. if (cnt < 1) {
  6610. return dfrd.reject();
  6611. }
  6612. form.append('<input type="hidden" name="'+(self.newAPI ? 'target' : 'current')+'" value="'+target+'"/>')
  6613. .append('<input type="hidden" name="html" value="1"/>')
  6614. .append('<input type="hidden" name="node" value="'+self.id+'"/>')
  6615. .append($(input).attr('name', 'upload[]'));
  6616. if (renames.length > 0) {
  6617. $.each(renames, function(i, rename) {
  6618. form.append('<input type="hidden" name="renames[]" value="'+self.escape(rename)+'"/>');
  6619. });
  6620. form.append('<input type="hidden" name="suffix" value="'+fm.options.backupSuffix+'"/>');
  6621. }
  6622. if (hashes) {
  6623. $.each(renames, function(i, v) {
  6624. form.append('<input type="hidden" name="['+i+']" value="'+self.escape(v)+'"/>');
  6625. });
  6626. }
  6627. if (data.overwrite === 0) {
  6628. form.append('<input type="hidden" name="overwrite" value="0"/>');
  6629. }
  6630. $.each(self.options.onlyMimes||[], function(i, mime) {
  6631. form.append('<input type="hidden" name="mimes[]" value="'+self.escape(mime)+'"/>');
  6632. });
  6633. $.each(self.customData, function(key, val) {
  6634. form.append('<input type="hidden" name="'+key+'" value="'+self.escape(val)+'"/>');
  6635. });
  6636. form.appendTo('body');
  6637. iframe.appendTo('body');
  6638. });
  6639. return dfrd;
  6640. }
  6641. },
  6642. /**
  6643. * Bind callback to event(s) The callback is executed at most once per event.
  6644. * To bind to multiply events at once, separate events names by space
  6645. *
  6646. * @param String event name
  6647. * @param Function callback
  6648. * @param Boolan priority first
  6649. * @return elFinder
  6650. */
  6651. one : function(ev, callback, priorityFirst) {
  6652. var self = this,
  6653. event = ev.toLowerCase(),
  6654. h = function(e, f) {
  6655. if (!self.toUnbindEvents[event]) {
  6656. self.toUnbindEvents[event] = [];
  6657. }
  6658. self.toUnbindEvents[event].push({
  6659. type: event,
  6660. callback: h
  6661. });
  6662. return (callback.done? callback.done : callback).apply(this, arguments);
  6663. };
  6664. if (callback.done) {
  6665. h = {done: h};
  6666. }
  6667. return this.bind(event, h, priorityFirst);
  6668. },
  6669. /**
  6670. * Set/get data into/from localStorage
  6671. *
  6672. * @param String key
  6673. * @param String|void value
  6674. * @return String|null
  6675. */
  6676. localStorage : function(key, val) {
  6677. var self = this,
  6678. s = window.localStorage,
  6679. oldkey = 'elfinder-'+(key || '')+this.id, // old key of elFinder < 2.1.6
  6680. prefix = window.location.pathname+'-elfinder-',
  6681. suffix = this.id,
  6682. clrs = [],
  6683. retval, oldval, t, precnt, sufcnt;
  6684. // reset this node data
  6685. if (typeof(key) === 'undefined') {
  6686. precnt = prefix.length;
  6687. sufcnt = suffix.length * -1;
  6688. $.each(s, function(key) {
  6689. if (key.substr(0, precnt) === prefix && key.substr(sufcnt) === suffix) {
  6690. clrs.push(key);
  6691. }
  6692. });
  6693. $.each(clrs, function(i, key) {
  6694. s.removeItem(key);
  6695. });
  6696. return true;
  6697. }
  6698. // new key of elFinder >= 2.1.6
  6699. key = prefix+key+suffix;
  6700. if (val === null) {
  6701. return s.removeItem(key);
  6702. }
  6703. if (val === void(0) && !(retval = s.getItem(key)) && (oldval = s.getItem(oldkey))) {
  6704. val = oldval;
  6705. s.removeItem(oldkey);
  6706. }
  6707. if (val !== void(0)) {
  6708. t = typeof val;
  6709. if (t !== 'string' && t !== 'number') {
  6710. val = JSON.stringify(val);
  6711. }
  6712. try {
  6713. s.setItem(key, val);
  6714. } catch (e) {
  6715. try {
  6716. s.clear();
  6717. s.setItem(key, val);
  6718. } catch (e) {
  6719. self.debug('error', e.toString());
  6720. }
  6721. }
  6722. retval = s.getItem(key);
  6723. }
  6724. if (retval && (retval.substr(0,1) === '{' || retval.substr(0,1) === '[')) {
  6725. try {
  6726. return JSON.parse(retval);
  6727. } catch(e) {}
  6728. }
  6729. return retval;
  6730. },
  6731. /**
  6732. * Get/set cookie
  6733. *
  6734. * @param String cookie name
  6735. * @param String|void cookie value
  6736. * @return String|null
  6737. */
  6738. cookie : function(name, value) {
  6739. var d, o, c, i, retval, t;
  6740. name = 'elfinder-'+name+this.id;
  6741. if (value === void(0)) {
  6742. if (document.cookie && document.cookie != '') {
  6743. c = document.cookie.split(';');
  6744. name += '=';
  6745. for (i=0; i<c.length; i++) {
  6746. c[i] = $.trim(c[i]);
  6747. if (c[i].substring(0, name.length) == name) {
  6748. retval = decodeURIComponent(c[i].substring(name.length));
  6749. if (retval.substr(0,1) === '{' || retval.substr(0,1) === '[') {
  6750. try {
  6751. return JSON.parse(retval);
  6752. } catch(e) {}
  6753. }
  6754. return retval;
  6755. }
  6756. }
  6757. }
  6758. return null;
  6759. }
  6760. o = Object.assign({}, this.options.cookie);
  6761. if (value === null) {
  6762. value = '';
  6763. o.expires = -1;
  6764. } else {
  6765. t = typeof value;
  6766. if (t !== 'string' && t !== 'number') {
  6767. value = JSON.stringify(value);
  6768. }
  6769. }
  6770. if (typeof(o.expires) == 'number') {
  6771. d = new Date();
  6772. d.setTime(d.getTime()+(o.expires * 86400000));
  6773. o.expires = d;
  6774. }
  6775. document.cookie = name+'='+encodeURIComponent(value)+'; expires='+o.expires.toUTCString()+(o.path ? '; path='+o.path : '')+(o.domain ? '; domain='+o.domain : '')+(o.secure ? '; secure' : '');
  6776. if (value && (value.substr(0,1) === '{' || value.substr(0,1) === '[')) {
  6777. try {
  6778. return JSON.parse(value);
  6779. } catch(e) {}
  6780. }
  6781. return value;
  6782. },
  6783. /**
  6784. * Get start directory (by location.hash or last opened directory)
  6785. *
  6786. * @return String
  6787. */
  6788. startDir : function() {
  6789. var locHash = window.location.hash;
  6790. if (locHash && locHash.match(/^#elf_/)) {
  6791. return locHash.replace(/^#elf_/, '');
  6792. } else if (this.options.startPathHash) {
  6793. return this.options.startPathHash;
  6794. } else {
  6795. return this.lastDir();
  6796. }
  6797. },
  6798. /**
  6799. * Get/set last opened directory
  6800. *
  6801. * @param String|undefined dir hash
  6802. * @return String
  6803. */
  6804. lastDir : function(hash) {
  6805. return this.options.rememberLastDir ? this.storage('lastdir', hash) : '';
  6806. },
  6807. /**
  6808. * Node for escape html entities in texts
  6809. *
  6810. * @type jQuery
  6811. */
  6812. _node : $('<span/>'),
  6813. /**
  6814. * Replace not html-safe symbols to html entities
  6815. *
  6816. * @param String text to escape
  6817. * @return String
  6818. */
  6819. escape : function(name) {
  6820. return this._node.text(name).html().replace(/"/g, '&quot;').replace(/'/g, '&#039;');
  6821. },
  6822. /**
  6823. * Cleanup ajax data.
  6824. * For old api convert data into new api format
  6825. *
  6826. * @param String command name
  6827. * @param Object data from backend
  6828. * @return Object
  6829. */
  6830. normalize : function(data) {
  6831. var self = this,
  6832. fileFilter = (function() {
  6833. var func, filter;
  6834. if (filter = self.options.fileFilter) {
  6835. if (typeof filter === 'function') {
  6836. func = function(file) {
  6837. return filter.call(self, file);
  6838. };
  6839. } else if (filter instanceof RegExp) {
  6840. func = function(file) {
  6841. return filter.test(file.name);
  6842. };
  6843. }
  6844. }
  6845. return func? func : null;
  6846. })(),
  6847. chkCmdMap = function(opts) {
  6848. // Disable command to replace with other command
  6849. var disabled;
  6850. if (opts.uiCmdMap) {
  6851. if ($.isPlainObject(opts.uiCmdMap) && Object.keys(opts.uiCmdMap).length) {
  6852. if (!opts.disabledFlip) {
  6853. opts.disabledFlip = {};
  6854. }
  6855. disabled = opts.disabledFlip;
  6856. $.each(opts.uiCmdMap, function(f, t) {
  6857. if (t === 'hidden' && !disabled[f]) {
  6858. opts.disabled.push(f);
  6859. opts.disabledFlip[f] = true;
  6860. }
  6861. });
  6862. } else {
  6863. delete opts.uiCmdMap;
  6864. }
  6865. }
  6866. },
  6867. normalizeOptions = function(opts) {
  6868. var getType = function(v) {
  6869. var type = typeof v;
  6870. if (type === 'object' && Array.isArray(v)) {
  6871. type = 'array';
  6872. }
  6873. return type;
  6874. };
  6875. $.each(self.optionProperties, function(k, empty) {
  6876. if (empty !== void(0)) {
  6877. if (opts[k] && getType(opts[k]) !== getType(empty)) {
  6878. opts[k] = empty;
  6879. }
  6880. }
  6881. });
  6882. if (opts['disabled']) {
  6883. opts['disabledFlip'] = self.arrayFlip(opts['disabled'], true);
  6884. } else {
  6885. opts['disabledFlip'] = {};
  6886. }
  6887. return opts;
  6888. },
  6889. filter = function(file, asMap, type) {
  6890. var res = asMap? file : true,
  6891. ign = asMap? null : false,
  6892. vid, targetOptions, isRoot, rootNames;
  6893. if (file && file.hash && file.name && file.mime) {
  6894. if (file.mime === 'application/x-empty') {
  6895. file.mime = 'text/plain';
  6896. }
  6897. isRoot = self.isRoot(file);
  6898. if (isRoot && ! file.volumeid) {
  6899. self.debug('warning', 'The volume root statuses requires `volumeid` property.');
  6900. }
  6901. if (isRoot || file.mime === 'directory') {
  6902. // Prevention of circular reference
  6903. if (file.phash) {
  6904. if (file.phash === file.hash) {
  6905. error = error.concat(['Parent folder of "$1" is itself.', file.name]);
  6906. return ign;
  6907. }
  6908. if (isRoot && file.volumeid && file.phash.indexOf(file.volumeid) === 0) {
  6909. error = error.concat(['Parent folder of "$1" is inner itself.', file.name]);
  6910. return ign;
  6911. }
  6912. }
  6913. // set options, tmbUrls for each volume
  6914. if (file.volumeid) {
  6915. vid = file.volumeid;
  6916. if (isRoot) {
  6917. // make or update of leaf roots cache
  6918. if (file.phash) {
  6919. if (! self.leafRoots[file.phash]) {
  6920. self.leafRoots[file.phash] = [ file.hash ];
  6921. } else {
  6922. if ($.inArray(file.hash, self.leafRoots[file.phash]) === -1) {
  6923. self.leafRoots[file.phash].push(file.hash);
  6924. }
  6925. }
  6926. }
  6927. self.hasVolOptions = true;
  6928. if (! self.volOptions[vid]) {
  6929. self.volOptions[vid] = {
  6930. // set dispInlineRegex
  6931. dispInlineRegex: self.options.dispInlineRegex
  6932. };
  6933. }
  6934. targetOptions = self.volOptions[vid];
  6935. if (file.options) {
  6936. // >= v.2.1.14 has file.options
  6937. Object.assign(targetOptions, file.options);
  6938. }
  6939. // for compat <= v2.1.13
  6940. if (file.disabled) {
  6941. targetOptions.disabled = file.disabled;
  6942. targetOptions.disabledFlip = self.arrayFlip(file.disabled, true);
  6943. }
  6944. if (file.tmbUrl) {
  6945. targetOptions.tmbUrl = file.tmbUrl;
  6946. }
  6947. // '/' required at the end of url
  6948. if (targetOptions.url && targetOptions.url.substr(-1) !== '/') {
  6949. targetOptions.url += '/';
  6950. }
  6951. // check uiCmdMap
  6952. chkCmdMap(targetOptions);
  6953. // check trash bin hash
  6954. if (targetOptions.trashHash) {
  6955. if (self.trashes[targetOptions.trashHash] === false) {
  6956. delete targetOptions.trashHash;
  6957. } else {
  6958. self.trashes[targetOptions.trashHash] = file.hash;
  6959. }
  6960. }
  6961. // set immediate properties
  6962. $.each(self.optionProperties, function(k) {
  6963. if (targetOptions[k]) {
  6964. file[k] = targetOptions[k];
  6965. }
  6966. });
  6967. // regist fm.roots
  6968. if (type !== 'cwd') {
  6969. self.roots[vid] = file.hash;
  6970. }
  6971. // regist fm.volumeExpires
  6972. if (file.expires) {
  6973. self.volumeExpires[vid] = file.expires;
  6974. }
  6975. }
  6976. if (prevId !== vid) {
  6977. prevId = vid;
  6978. i18nFolderName = self.option('i18nFolderName', vid);
  6979. }
  6980. }
  6981. // volume root i18n name
  6982. if (isRoot && ! file.i18) {
  6983. name = 'volume_' + file.name,
  6984. i18 = self.i18n(false, name);
  6985. if (name !== i18) {
  6986. file.i18 = i18;
  6987. }
  6988. }
  6989. // i18nFolderName
  6990. if (i18nFolderName && ! file.i18) {
  6991. name = 'folder_' + file.name,
  6992. i18 = self.i18n(false, name);
  6993. if (name !== i18) {
  6994. file.i18 = i18;
  6995. }
  6996. }
  6997. if (isRoot) {
  6998. if (rootNames = self.storage('rootNames')) {
  6999. if (rootNames[file.hash]) {
  7000. file._name = file.name;
  7001. file._i18 = file.i18;
  7002. file.name = rootNames[file.hash] = rootNames[file.hash];
  7003. delete file.i18;
  7004. }
  7005. self.storage('rootNames', rootNames);
  7006. }
  7007. }
  7008. // lock trash bins holder
  7009. if (self.trashes[file.hash]) {
  7010. file.locked = true;
  7011. }
  7012. } else {
  7013. if (fileFilter) {
  7014. try {
  7015. if (! fileFilter(file)) {
  7016. return ign;
  7017. }
  7018. } catch(e) {
  7019. self.debug(e);
  7020. }
  7021. }
  7022. if (file.size == 0) {
  7023. file.mime = self.getMimetype(file.name, file.mime);
  7024. }
  7025. }
  7026. if (file.options) {
  7027. self.optionsByHashes[file.hash] = normalizeOptions(file.options);
  7028. }
  7029. delete file.options;
  7030. return res;
  7031. }
  7032. return ign;
  7033. },
  7034. getDescendants = function(hashes) {
  7035. var res = [];
  7036. $.each(self.files(), function(h, f) {
  7037. $.each(self.parents(h), function(i, ph) {
  7038. if ($.inArray(ph, hashes) !== -1 && $.inArray(h, hashes) === -1) {
  7039. res.push(h);
  7040. return false;
  7041. }
  7042. });
  7043. });
  7044. return res;
  7045. },
  7046. applyLeafRootStats = function(dataArr, type) {
  7047. $.each(dataArr, function(i, f) {
  7048. var pfile, done;
  7049. if (self.leafRoots[f.hash]) {
  7050. self.applyLeafRootStats(f);
  7051. }
  7052. // update leaf root parent stat
  7053. if (type !== 'change' && f.phash && self.isRoot(f) && (pfile = self.file(f.phash))) {
  7054. self.applyLeafRootStats(pfile);
  7055. // add to data.changed
  7056. if (!data.changed) {
  7057. data.changed = [pfile];
  7058. } else {
  7059. $.each(data.changed, function(i, f) {
  7060. if (f.hash === pfile.hash) {
  7061. data.changed[i] = pfile;
  7062. done = true;
  7063. return false;
  7064. }
  7065. });
  7066. if (!done) {
  7067. data.changed.push(pfile);
  7068. }
  7069. }
  7070. }
  7071. });
  7072. },
  7073. error = [],
  7074. name, i18, i18nFolderName, prevId, cData;
  7075. // set cunstom data
  7076. if (data.customData && data.customData !== self.prevCustomData) {
  7077. self.prevCustomData = data.customData;
  7078. try {
  7079. cData = JSON.parse(data.customData);
  7080. if ($.isPlainObject(cData)) {
  7081. self.prevCustomData = cData;
  7082. $.each(Object.keys(cData), function(i, key) {
  7083. if (cData[key] === null) {
  7084. delete cData[key];
  7085. delete self.optsCustomData[key];
  7086. }
  7087. });
  7088. self.customData = Object.assign({}, self.optsCustomData, cData);
  7089. }
  7090. } catch(e) {}
  7091. }
  7092. if (data.options) {
  7093. normalizeOptions(data.options);
  7094. }
  7095. if (data.cwd) {
  7096. if (data.cwd.volumeid && data.options && Object.keys(data.options).length && self.isRoot(data.cwd)) {
  7097. self.hasVolOptions = true;
  7098. self.volOptions[data.cwd.volumeid] = data.options;
  7099. }
  7100. data.cwd = filter(data.cwd, true, 'cwd');
  7101. }
  7102. if (data.files) {
  7103. data.files = $.grep(data.files, filter);
  7104. }
  7105. if (data.tree) {
  7106. data.tree = $.grep(data.tree, filter);
  7107. }
  7108. if (data.added) {
  7109. data.added = $.grep(data.added, filter);
  7110. }
  7111. if (data.changed) {
  7112. data.changed = $.grep(data.changed, filter);
  7113. }
  7114. if (data.removed && data.removed.length && self.searchStatus.state === 2) {
  7115. data.removed = data.removed.concat(getDescendants(data.removed));
  7116. }
  7117. if (data.api) {
  7118. data.init = true;
  7119. }
  7120. if (Object.keys(self.leafRoots).length) {
  7121. data.files && applyLeafRootStats(data.files);
  7122. data.tree && applyLeafRootStats(data.tree);
  7123. data.added && applyLeafRootStats(data.added);
  7124. data.changed && applyLeafRootStats(data.changed, 'change');
  7125. }
  7126. // merge options that apply only to cwd
  7127. if (data.cwd && data.cwd.options && data.options) {
  7128. Object.assign(data.options, normalizeOptions(data.cwd.options));
  7129. }
  7130. // '/' required at the end of url
  7131. if (data.options && data.options.url && data.options.url.substr(-1) !== '/') {
  7132. data.options.url += '/';
  7133. }
  7134. // check error
  7135. if (error.length) {
  7136. data.norError = ['errResponse'].concat(error);
  7137. }
  7138. return data;
  7139. },
  7140. /**
  7141. * Update sort options
  7142. *
  7143. * @param {String} sort type
  7144. * @param {String} sort order
  7145. * @param {Boolean} show folder first
  7146. */
  7147. setSort : function(type, order, stickFolders, alsoTreeview) {
  7148. this.storage('sortType', (this.sortType = this.sortRules[type] ? type : 'name'));
  7149. this.storage('sortOrder', (this.sortOrder = /asc|desc/.test(order) ? order : 'asc'));
  7150. this.storage('sortStickFolders', (this.sortStickFolders = !!stickFolders) ? 1 : '');
  7151. this.storage('sortAlsoTreeview', (this.sortAlsoTreeview = !!alsoTreeview) ? 1 : '');
  7152. this.trigger('sortchange');
  7153. },
  7154. _sortRules : {
  7155. name : function(file1, file2) {
  7156. return elFinder.prototype.naturalCompare(file1.i18 || file1.name, file2.i18 || file2.name);
  7157. },
  7158. size : function(file1, file2) {
  7159. var size1 = parseInt(file1.size) || 0,
  7160. size2 = parseInt(file2.size) || 0;
  7161. return size1 === size2 ? 0 : size1 > size2 ? 1 : -1;
  7162. },
  7163. kind : function(file1, file2) {
  7164. return elFinder.prototype.naturalCompare(file1.mime, file2.mime);
  7165. },
  7166. date : function(file1, file2) {
  7167. var date1 = file1.ts || file1.date || 0,
  7168. date2 = file2.ts || file2.date || 0;
  7169. return date1 === date2 ? 0 : date1 > date2 ? 1 : -1;
  7170. },
  7171. perm : function(file1, file2) {
  7172. var val = function(file) { return (file.write? 2 : 0) + (file.read? 1 : 0); },
  7173. v1 = val(file1),
  7174. v2 = val(file2);
  7175. return v1 === v2 ? 0 : v1 > v2 ? 1 : -1;
  7176. },
  7177. mode : function(file1, file2) {
  7178. var v1 = file1.mode || (file1.perm || ''),
  7179. v2 = file2.mode || (file2.perm || '');
  7180. return elFinder.prototype.naturalCompare(v1, v2);
  7181. },
  7182. owner : function(file1, file2) {
  7183. var v1 = file1.owner || '',
  7184. v2 = file2.owner || '';
  7185. return elFinder.prototype.naturalCompare(v1, v2);
  7186. },
  7187. group : function(file1, file2) {
  7188. var v1 = file1.group || '',
  7189. v2 = file2.group || '';
  7190. return elFinder.prototype.naturalCompare(v1, v2);
  7191. }
  7192. },
  7193. /**
  7194. * Valid sort rule names
  7195. *
  7196. * @type Object
  7197. */
  7198. sorters : {},
  7199. /**
  7200. * Compare strings for natural sort
  7201. *
  7202. * @param String
  7203. * @param String
  7204. * @return Number
  7205. */
  7206. naturalCompare : function(a, b) {
  7207. var self = elFinder.prototype.naturalCompare;
  7208. if (typeof self.loc == 'undefined') {
  7209. self.loc = (navigator.userLanguage || navigator.browserLanguage || navigator.language || 'en-US');
  7210. }
  7211. if (typeof self.sort == 'undefined') {
  7212. if ('11'.localeCompare('2', self.loc, {numeric: true}) > 0) {
  7213. // Native support
  7214. if (window.Intl && window.Intl.Collator) {
  7215. self.sort = new Intl.Collator(self.loc, {numeric: true}).compare;
  7216. } else {
  7217. self.sort = function(a, b) {
  7218. return a.localeCompare(b, self.loc, {numeric: true});
  7219. };
  7220. }
  7221. } else {
  7222. /*
  7223. * Edited for elFinder (emulates localeCompare() by numeric) by Naoki Sawada aka nao-pon
  7224. */
  7225. /*
  7226. * Huddle/javascript-natural-sort (https://github.com/Huddle/javascript-natural-sort)
  7227. */
  7228. /*
  7229. * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
  7230. * Author: Jim Palmer (based on chunking idea from Dave Koelle)
  7231. * http://opensource.org/licenses/mit-license.php
  7232. */
  7233. self.sort = function(a, b) {
  7234. var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
  7235. sre = /(^[ ]*|[ ]*$)/g,
  7236. dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
  7237. hre = /^0x[0-9a-f]+$/i,
  7238. ore = /^0/,
  7239. syre = /^[\x01\x21-\x2f\x3a-\x40\x5b-\x60\x7b-\x7e]/, // symbol first - (Naoki Sawada)
  7240. i = function(s) { return self.sort.insensitive && (''+s).toLowerCase() || ''+s; },
  7241. // convert all to strings strip whitespace
  7242. // first character is "_", it's smallest - (Naoki Sawada)
  7243. x = i(a).replace(sre, '').replace(/^_/, "\x01") || '',
  7244. y = i(b).replace(sre, '').replace(/^_/, "\x01") || '',
  7245. // chunk/tokenize
  7246. xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  7247. yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  7248. // numeric, hex or date detection
  7249. xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
  7250. yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
  7251. oFxNcL, oFyNcL,
  7252. locRes = 0;
  7253. // first try and sort Hex codes or Dates
  7254. if (yD) {
  7255. if ( xD < yD ) return -1;
  7256. else if ( xD > yD ) return 1;
  7257. }
  7258. // natural sorting through split numeric strings and default strings
  7259. for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
  7260. // find floats not starting with '0', string or 0 if not defined (Clint Priest)
  7261. oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
  7262. oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
  7263. // handle numeric vs string comparison - number < string - (Kyle Adams)
  7264. // but symbol first < number - (Naoki Sawada)
  7265. if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
  7266. if (isNaN(oFxNcL) && (typeof oFxNcL !== 'string' || ! oFxNcL.match(syre))) {
  7267. return 1;
  7268. } else if (typeof oFyNcL !== 'string' || ! oFyNcL.match(syre)) {
  7269. return -1;
  7270. }
  7271. }
  7272. // use decimal number comparison if either value is string zero
  7273. if (parseInt(oFxNcL, 10) === 0) oFxNcL = 0;
  7274. if (parseInt(oFyNcL, 10) === 0) oFyNcL = 0;
  7275. // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
  7276. if (typeof oFxNcL !== typeof oFyNcL) {
  7277. oFxNcL += '';
  7278. oFyNcL += '';
  7279. }
  7280. // use locale sensitive sort for strings when case insensitive
  7281. // note: localeCompare interleaves uppercase with lowercase (e.g. A,a,B,b)
  7282. if (self.sort.insensitive && typeof oFxNcL === 'string' && typeof oFyNcL === 'string') {
  7283. locRes = oFxNcL.localeCompare(oFyNcL, self.loc);
  7284. if (locRes !== 0) return locRes;
  7285. }
  7286. if (oFxNcL < oFyNcL) return -1;
  7287. if (oFxNcL > oFyNcL) return 1;
  7288. }
  7289. return 0;
  7290. };
  7291. self.sort.insensitive = true;
  7292. }
  7293. }
  7294. return self.sort(a, b);
  7295. },
  7296. /**
  7297. * Compare files based on elFinder.sort
  7298. *
  7299. * @param Object file
  7300. * @param Object file
  7301. * @return Number
  7302. */
  7303. compare : function(file1, file2) {
  7304. var self = this,
  7305. type = self.sortType,
  7306. asc = self.sortOrder == 'asc',
  7307. stick = self.sortStickFolders,
  7308. rules = self.sortRules,
  7309. sort = rules[type],
  7310. d1 = file1.mime == 'directory',
  7311. d2 = file2.mime == 'directory',
  7312. res;
  7313. if (stick) {
  7314. if (d1 && !d2) {
  7315. return -1;
  7316. } else if (!d1 && d2) {
  7317. return 1;
  7318. }
  7319. }
  7320. res = asc ? sort(file1, file2) : sort(file2, file1);
  7321. return type !== 'name' && res === 0
  7322. ? res = asc ? rules.name(file1, file2) : rules.name(file2, file1)
  7323. : res;
  7324. },
  7325. /**
  7326. * Sort files based on config
  7327. *
  7328. * @param Array files
  7329. * @return Array
  7330. */
  7331. sortFiles : function(files) {
  7332. return files.sort(this.compare);
  7333. },
  7334. /**
  7335. * Open notification dialog
  7336. * and append/update message for required notification type.
  7337. *
  7338. * @param Object options
  7339. * @example
  7340. * this.notify({
  7341. * type : 'copy',
  7342. * msg : 'Copy files', // not required for known types @see this.notifyType
  7343. * cnt : 3,
  7344. * hideCnt : false, // true for not show count
  7345. * progress : 10, // progress bar percents (use cnt : 0 to update progress bar)
  7346. * cancel : callback // callback function for cancel button
  7347. * })
  7348. * @return elFinder
  7349. */
  7350. notify : function(opts) {
  7351. var type = opts.type,
  7352. id = opts.id? 'elfinder-notify-'+opts.id : '',
  7353. msg = this.i18n((typeof opts.msg !== 'undefined')? opts.msg : (this.messages['ntf'+type] ? 'ntf'+type : 'ntfsmth')),
  7354. ndialog = this.ui.notify,
  7355. notify = ndialog.children('.elfinder-notify-'+type+(id? ('.'+id) : '')),
  7356. button = notify.children('div.elfinder-notify-cancel').children('button'),
  7357. 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>',
  7358. delta = opts.cnt,
  7359. size = (typeof opts.size != 'undefined')? parseInt(opts.size) : null,
  7360. progress = (typeof opts.progress != 'undefined' && opts.progress >= 0) ? opts.progress : null,
  7361. cancel = opts.cancel,
  7362. clhover = 'ui-state-hover',
  7363. close = function() {
  7364. notify._esc && $(document).off('keydown', notify._esc);
  7365. notify.remove();
  7366. !ndialog.children().length && ndialog.elfinderdialog('close');
  7367. },
  7368. cnt, total, prc;
  7369. if (!type) {
  7370. return this;
  7371. }
  7372. if (!notify.length) {
  7373. notify = $(ntpl.replace(/\{type\}/g, type).replace(/\{msg\}/g, msg))
  7374. .appendTo(ndialog)
  7375. .data('cnt', 0);
  7376. if (progress != null) {
  7377. notify.data({progress : 0, total : 0});
  7378. }
  7379. if (cancel) {
  7380. 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>')
  7381. .on('mouseenter mouseleave', function(e) {
  7382. $(this).toggleClass(clhover, e.type === 'mouseenter');
  7383. });
  7384. notify.children('div.elfinder-notify-cancel').append(button);
  7385. }
  7386. } else if (typeof opts.msg !== 'undefined') {
  7387. notify.children('span.elfinder-notify-msg').html(msg);
  7388. }
  7389. cnt = delta + parseInt(notify.data('cnt'));
  7390. if (cnt > 0) {
  7391. if (cancel && button.length) {
  7392. if ($.isFunction(cancel) || (typeof cancel === 'object' && cancel.promise)) {
  7393. notify._esc = function(e) {
  7394. if (e.type == 'keydown' && e.keyCode != $.ui.keyCode.ESCAPE) {
  7395. return;
  7396. }
  7397. e.preventDefault();
  7398. e.stopPropagation();
  7399. close();
  7400. if (cancel.promise) {
  7401. cancel.reject(0); // 0 is canceling flag
  7402. } else {
  7403. cancel(e);
  7404. }
  7405. };
  7406. button.on('click', function(e) {
  7407. notify._esc(e);
  7408. });
  7409. $(document).on('keydown.' + this.namespace, notify._esc);
  7410. }
  7411. }
  7412. !opts.hideCnt && notify.children('.elfinder-notify-cnt').text('('+cnt+')');
  7413. ndialog.is(':hidden') && ndialog.elfinderdialog('open', this).height('auto');
  7414. notify.data('cnt', cnt);
  7415. if ((progress != null)
  7416. && (total = notify.data('total')) >= 0
  7417. && (prc = notify.data('progress')) >= 0) {
  7418. total += size != null? size : delta;
  7419. prc += progress;
  7420. (size == null && delta < 0) && (prc += delta * 100);
  7421. notify.data({progress : prc, total : total});
  7422. if (size != null) {
  7423. prc *= 100;
  7424. total = Math.max(1, total);
  7425. }
  7426. progress = parseInt(prc/total);
  7427. notify.find('.elfinder-notify-progress')
  7428. .animate({
  7429. width : (progress < 100 ? progress : 100)+'%'
  7430. }, 20);
  7431. }
  7432. } else {
  7433. close();
  7434. }
  7435. return this;
  7436. },
  7437. /**
  7438. * Open confirmation dialog
  7439. *
  7440. * @param Object options
  7441. * @example
  7442. * this.confirm({
  7443. * cssClass : 'elfinder-confirm-mydialog',
  7444. * title : 'Remove files',
  7445. * text : 'Here is question text',
  7446. * accept : { // accept callback - required
  7447. * label : 'Continue',
  7448. * callback : function(applyToAll) { fm.log('Ok') }
  7449. * },
  7450. * cancel : { // cancel callback - required
  7451. * label : 'Cancel',
  7452. * callback : function() { fm.log('Cancel')}
  7453. * },
  7454. * reject : { // reject callback - optionally
  7455. * label : 'No',
  7456. * callback : function(applyToAll) { fm.log('No')}
  7457. * },
  7458. * buttons : [ // additional buttons callback - optionally
  7459. * {
  7460. * label : 'Btn1',
  7461. * callback : function(applyToAll) { fm.log('Btn1')}
  7462. * }
  7463. * ],
  7464. * all : true // display checkbox "Apply to all"
  7465. * })
  7466. * @return elFinder
  7467. */
  7468. confirm : function(opts) {
  7469. var self = this,
  7470. complete = false,
  7471. options = {
  7472. cssClass : 'elfinder-dialog-confirm',
  7473. modal : true,
  7474. resizable : false,
  7475. title : this.i18n(opts.title || 'confirmReq'),
  7476. buttons : {},
  7477. close : function() {
  7478. !complete && opts.cancel.callback();
  7479. $(this).elfinderdialog('destroy');
  7480. }
  7481. },
  7482. apply = this.i18n('apllyAll'),
  7483. label, checkbox, btnNum;
  7484. if (opts.cssClass) {
  7485. options.cssClass += ' ' + opts.cssClass;
  7486. }
  7487. options.buttons[this.i18n(opts.accept.label)] = function() {
  7488. opts.accept.callback(!!(checkbox && checkbox.prop('checked')));
  7489. complete = true;
  7490. $(this).elfinderdialog('close');
  7491. };
  7492. options.buttons[this.i18n(opts.accept.label)]._cssClass = 'elfinder-confirm-accept';
  7493. if (opts.reject) {
  7494. options.buttons[this.i18n(opts.reject.label)] = function() {
  7495. opts.reject.callback(!!(checkbox && checkbox.prop('checked')));
  7496. complete = true;
  7497. $(this).elfinderdialog('close');
  7498. };
  7499. options.buttons[this.i18n(opts.reject.label)]._cssClass = 'elfinder-confirm-reject';
  7500. }
  7501. if (opts.buttons && opts.buttons.length > 0) {
  7502. btnNum = 1;
  7503. $.each(opts.buttons, function(i, v){
  7504. options.buttons[self.i18n(v.label)] = function() {
  7505. v.callback(!!(checkbox && checkbox.prop('checked')));
  7506. complete = true;
  7507. $(this).elfinderdialog('close');
  7508. };
  7509. options.buttons[self.i18n(v.label)]._cssClass = 'elfinder-confirm-extbtn' + (btnNum++);
  7510. if (v.cssClass) {
  7511. options.buttons[self.i18n(v.label)]._cssClass += ' ' + v.cssClass;
  7512. }
  7513. });
  7514. }
  7515. options.buttons[this.i18n(opts.cancel.label)] = function() {
  7516. $(this).elfinderdialog('close');
  7517. };
  7518. options.buttons[this.i18n(opts.cancel.label)]._cssClass = 'elfinder-confirm-cancel';
  7519. if (opts.all) {
  7520. options.create = function() {
  7521. var base = $('<div class="elfinder-dialog-confirm-applyall"/>');
  7522. checkbox = $('<input type="checkbox" />');
  7523. $(this).next().find('.ui-dialog-buttonset')
  7524. .prepend(base.append($('<label>'+apply+'</label>').prepend(checkbox)));
  7525. };
  7526. }
  7527. if (opts.optionsCallback && $.isFunction(opts.optionsCallback)) {
  7528. opts.optionsCallback(options);
  7529. }
  7530. return this.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-confirm"/>' + this.i18n(opts.text), options);
  7531. },
  7532. /**
  7533. * Create unique file name in required dir
  7534. *
  7535. * @param String file name
  7536. * @param String parent dir hash
  7537. * @param String glue
  7538. * @return String
  7539. */
  7540. uniqueName : function(prefix, phash, glue) {
  7541. var i = 0, ext = '', p, name;
  7542. prefix = this.i18n(false, prefix);
  7543. phash = phash || this.cwd().hash;
  7544. glue = (typeof glue === 'undefined')? ' ' : glue;
  7545. if (p = prefix.match(/^(.+)(\.[^.]+)$/)) {
  7546. ext = p[2];
  7547. prefix = p[1];
  7548. }
  7549. name = prefix+ext;
  7550. if (!this.fileByName(name, phash)) {
  7551. return name;
  7552. }
  7553. while (i < 10000) {
  7554. name = prefix + glue + (++i) + ext;
  7555. if (!this.fileByName(name, phash)) {
  7556. return name;
  7557. }
  7558. }
  7559. return prefix + Math.random() + ext;
  7560. },
  7561. /**
  7562. * Return message translated onto current language
  7563. * Allowed accept HTML element that was wrapped in jQuery object
  7564. * To be careful to XSS vulnerability of HTML element Ex. You should use `fm.escape(file.name)`
  7565. *
  7566. * @param String|Array message[s]|Object jQuery
  7567. * @return String
  7568. **/
  7569. i18n : function() {
  7570. var self = this,
  7571. messages = this.messages,
  7572. input = [],
  7573. ignore = [],
  7574. message = function(m) {
  7575. var file;
  7576. if (m.indexOf('#') === 0) {
  7577. if ((file = self.file(m.substr(1)))) {
  7578. return file.name;
  7579. }
  7580. }
  7581. return m;
  7582. },
  7583. i, j, m, escFunc, start = 0, isErr;
  7584. if (arguments.length && arguments[0] === false) {
  7585. escFunc = function(m){ return m; };
  7586. start = 1;
  7587. }
  7588. for (i = start; i< arguments.length; i++) {
  7589. m = arguments[i];
  7590. if (Array.isArray(m)) {
  7591. for (j = 0; j < m.length; j++) {
  7592. if (m[j] instanceof jQuery) {
  7593. // jQuery object is HTML element
  7594. input.push(m[j]);
  7595. } else if (typeof m[j] !== 'undefined'){
  7596. input.push(message('' + m[j]));
  7597. }
  7598. }
  7599. } else if (m instanceof jQuery) {
  7600. // jQuery object is HTML element
  7601. input.push(m[j]);
  7602. } else if (typeof m !== 'undefined'){
  7603. input.push(message('' + m));
  7604. }
  7605. }
  7606. for (i = 0; i < input.length; i++) {
  7607. // dont translate placeholders
  7608. if ($.inArray(i, ignore) !== -1) {
  7609. continue;
  7610. }
  7611. m = input[i];
  7612. if (typeof m == 'string') {
  7613. isErr = !!(messages[m] && m.match(/^err/));
  7614. // translate message
  7615. m = messages[m] || (escFunc? escFunc(m) : self.escape(m));
  7616. // replace placeholders in message
  7617. m = m.replace(/\$(\d+)/g, function(match, placeholder) {
  7618. var res;
  7619. placeholder = i + parseInt(placeholder);
  7620. if (placeholder > 0 && input[placeholder]) {
  7621. ignore.push(placeholder);
  7622. }
  7623. res = escFunc? escFunc(input[placeholder]) : self.escape(input[placeholder]);
  7624. if (isErr) {
  7625. res = '<span class="elfinder-err-var elfinder-err-var' + placeholder + '">' + res + '</span>';
  7626. }
  7627. return res;
  7628. });
  7629. } else {
  7630. // get HTML from jQuery object
  7631. m = m.get(0).outerHTML;
  7632. }
  7633. input[i] = m;
  7634. }
  7635. return $.grep(input, function(m, i) { return $.inArray(i, ignore) === -1 ? true : false; }).join('<br>');
  7636. },
  7637. /**
  7638. * Get icon style from file.icon
  7639. *
  7640. * @param Object elFinder file object
  7641. * @return String|Object
  7642. */
  7643. getIconStyle : function(file, asObject) {
  7644. var self = this,
  7645. template = {
  7646. 'background' : 'url(\'{url}\') 0 0 no-repeat',
  7647. 'background-size' : 'contain'
  7648. },
  7649. style = '',
  7650. cssObj = {},
  7651. i = 0;
  7652. if (file.icon) {
  7653. style = 'style="';
  7654. $.each(template, function(k, v) {
  7655. if (i++ === 0) {
  7656. v = v.replace('{url}', self.escape(file.icon));
  7657. }
  7658. if (asObject) {
  7659. cssObj[k] = v;
  7660. } else {
  7661. style += k+':'+v+';';
  7662. }
  7663. });
  7664. style += '"';
  7665. }
  7666. return asObject? cssObj : style;
  7667. },
  7668. /**
  7669. * Convert mimetype into css classes
  7670. *
  7671. * @param String file mimetype
  7672. * @return String
  7673. */
  7674. mime2class : function(mimeType) {
  7675. var prefix = 'elfinder-cwd-icon-',
  7676. mime = mimeType.toLowerCase(),
  7677. isText = this.textMimes[mime];
  7678. mime = mime.split('/');
  7679. if (isText) {
  7680. mime[0] += ' ' + prefix + 'text';
  7681. } else if (mime[1] && mime[1].match(/\+xml$/)) {
  7682. mime[0] += ' ' + prefix + 'xml';
  7683. }
  7684. return prefix + mime[0] + (mime[1] ? ' ' + prefix + mime[1].replace(/(\.|\+)/g, '-') : '');
  7685. },
  7686. /**
  7687. * Return localized kind of file
  7688. *
  7689. * @param Object|String file or file mimetype
  7690. * @return String
  7691. */
  7692. mime2kind : function(f) {
  7693. var isObj = typeof(f) == 'object' ? true : false,
  7694. mime = isObj ? f.mime : f,
  7695. kind;
  7696. if (isObj && f.alias && mime != 'symlink-broken') {
  7697. kind = 'Alias';
  7698. } else if (this.kinds[mime]) {
  7699. if (isObj && mime === 'directory' && (! f.phash || f.isroot)) {
  7700. kind = 'Root';
  7701. } else {
  7702. kind = this.kinds[mime];
  7703. }
  7704. }
  7705. if (! kind) {
  7706. if (mime.indexOf('text') === 0) {
  7707. kind = 'Text';
  7708. } else if (mime.indexOf('image') === 0) {
  7709. kind = 'Image';
  7710. } else if (mime.indexOf('audio') === 0) {
  7711. kind = 'Audio';
  7712. } else if (mime.indexOf('video') === 0) {
  7713. kind = 'Video';
  7714. } else if (mime.indexOf('application') === 0) {
  7715. kind = 'App';
  7716. } else {
  7717. kind = mime;
  7718. }
  7719. }
  7720. return this.messages['kind'+kind] ? this.i18n('kind'+kind) : mime;
  7721. },
  7722. /**
  7723. * Return boolean Is mime-type text file
  7724. *
  7725. * @param String mime-type
  7726. * @return Boolean
  7727. */
  7728. mimeIsText : function(mime) {
  7729. return (this.textMimes[mime.toLowerCase()] || (mime.indexOf('text/') === 0 && mime.substr(5, 3) !== 'rtf') || mime.match(/^application\/.+\+xml$/))? true : false;
  7730. },
  7731. /**
  7732. * Returns a date string formatted according to the given format string
  7733. *
  7734. * @param String format string
  7735. * @param Object Date object
  7736. * @return String
  7737. */
  7738. date : function(format, date) {
  7739. var self = this,
  7740. output, d, dw, m, y, h, g, i, s;
  7741. if (! date) {
  7742. date = new Date();
  7743. }
  7744. h = date[self.getHours]();
  7745. g = h > 12 ? h - 12 : h;
  7746. i = date[self.getMinutes]();
  7747. s = date[self.getSeconds]();
  7748. d = date[self.getDate]();
  7749. dw = date[self.getDay]();
  7750. m = date[self.getMonth]() + 1;
  7751. y = date[self.getFullYear]();
  7752. output = format.replace(/[a-z]/gi, function(val) {
  7753. switch (val) {
  7754. case 'd': return d > 9 ? d : '0'+d;
  7755. case 'j': return d;
  7756. case 'D': return self.i18n(self.i18.daysShort[dw]);
  7757. case 'l': return self.i18n(self.i18.days[dw]);
  7758. case 'm': return m > 9 ? m : '0'+m;
  7759. case 'n': return m;
  7760. case 'M': return self.i18n(self.i18.monthsShort[m-1]);
  7761. case 'F': return self.i18n(self.i18.months[m-1]);
  7762. case 'Y': return y;
  7763. case 'y': return (''+y).substr(2);
  7764. case 'H': return h > 9 ? h : '0'+h;
  7765. case 'G': return h;
  7766. case 'g': return g;
  7767. case 'h': return g > 9 ? g : '0'+g;
  7768. case 'a': return h >= 12 ? 'pm' : 'am';
  7769. case 'A': return h >= 12 ? 'PM' : 'AM';
  7770. case 'i': return i > 9 ? i : '0'+i;
  7771. case 's': return s > 9 ? s : '0'+s;
  7772. }
  7773. return val;
  7774. });
  7775. return output;
  7776. },
  7777. /**
  7778. * Return localized date
  7779. *
  7780. * @param Object file object
  7781. * @return String
  7782. */
  7783. formatDate : function(file, t) {
  7784. var self = this,
  7785. ts = t || file.ts,
  7786. i18 = self.i18,
  7787. date, format, output, d, dw, m, y, h, g, i, s;
  7788. if (self.options.clientFormatDate && ts > 0) {
  7789. date = new Date(ts*1000);
  7790. format = ts >= this.yesterday
  7791. ? this.fancyFormat
  7792. : this.dateFormat;
  7793. output = self.date(format, date);
  7794. return ts >= this.yesterday
  7795. ? output.replace('$1', this.i18n(ts >= this.today ? 'Today' : 'Yesterday'))
  7796. : output;
  7797. } else if (file.date) {
  7798. return file.date.replace(/([a-z]+)\s/i, function(a1, a2) { return self.i18n(a2)+' '; });
  7799. }
  7800. return self.i18n('dateUnknown');
  7801. },
  7802. /**
  7803. * Return localized number string
  7804. *
  7805. * @param Number
  7806. * @return String
  7807. */
  7808. toLocaleString : function(num) {
  7809. var v = new Number(num);
  7810. if (v) {
  7811. if (v.toLocaleString) {
  7812. return v.toLocaleString();
  7813. } else {
  7814. return String(num).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
  7815. }
  7816. }
  7817. return num;
  7818. },
  7819. /**
  7820. * Return css class marks file permissions
  7821. *
  7822. * @param Object file
  7823. * @return String
  7824. */
  7825. perms2class : function(o) {
  7826. var c = '';
  7827. if (!o.read && !o.write) {
  7828. c = 'elfinder-na';
  7829. } else if (!o.read) {
  7830. c = 'elfinder-wo';
  7831. } else if (!o.write) {
  7832. c = 'elfinder-ro';
  7833. }
  7834. if (o.type) {
  7835. c += ' elfinder-' + this.escape(o.type);
  7836. }
  7837. return c;
  7838. },
  7839. /**
  7840. * Return localized string with file permissions
  7841. *
  7842. * @param Object file
  7843. * @return String
  7844. */
  7845. formatPermissions : function(f) {
  7846. var p = [];
  7847. f.read && p.push(this.i18n('read'));
  7848. f.write && p.push(this.i18n('write'));
  7849. return p.length ? p.join(' '+this.i18n('and')+' ') : this.i18n('noaccess');
  7850. },
  7851. /**
  7852. * Return formated file size
  7853. *
  7854. * @param Number file size
  7855. * @return String
  7856. */
  7857. formatSize : function(s) {
  7858. var n = 1, u = 'b';
  7859. if (s == 'unknown') {
  7860. return this.i18n('unknown');
  7861. }
  7862. if (s > 1073741824) {
  7863. n = 1073741824;
  7864. u = 'GB';
  7865. } else if (s > 1048576) {
  7866. n = 1048576;
  7867. u = 'MB';
  7868. } else if (s > 1024) {
  7869. n = 1024;
  7870. u = 'KB';
  7871. }
  7872. s = s/n;
  7873. return (s > 0 ? n >= 1048576 ? s.toFixed(2) : Math.round(s) : 0) +' '+u;
  7874. },
  7875. /**
  7876. * Return formated file mode by options.fileModeStyle
  7877. *
  7878. * @param String file mode
  7879. * @param String format style
  7880. * @return String
  7881. */
  7882. formatFileMode : function(p, style) {
  7883. var i, o, s, b, sticy, suid, sgid, str, oct;
  7884. if (!style) {
  7885. style = this.options.fileModeStyle.toLowerCase();
  7886. }
  7887. p = $.trim(p);
  7888. if (p.match(/[rwxs-]{9}$/i)) {
  7889. str = p = p.substr(-9);
  7890. if (style == 'string') {
  7891. return str;
  7892. }
  7893. oct = '';
  7894. s = 0;
  7895. for (i=0; i<7; i=i+3) {
  7896. o = p.substr(i, 3);
  7897. b = 0;
  7898. if (o.match(/[r]/i)) {
  7899. b += 4;
  7900. }
  7901. if (o.match(/[w]/i)) {
  7902. b += 2;
  7903. }
  7904. if (o.match(/[xs]/i)) {
  7905. if (o.match(/[xs]/)) {
  7906. b += 1;
  7907. }
  7908. if (o.match(/[s]/i)) {
  7909. if (i == 0) {
  7910. s += 4;
  7911. } else if (i == 3) {
  7912. s += 2;
  7913. }
  7914. }
  7915. }
  7916. oct += b.toString(8);
  7917. }
  7918. if (s) {
  7919. oct = s.toString(8) + oct;
  7920. }
  7921. } else {
  7922. p = parseInt(p, 8);
  7923. oct = p? p.toString(8) : '';
  7924. if (!p || style == 'octal') {
  7925. return oct;
  7926. }
  7927. o = p.toString(8);
  7928. s = 0;
  7929. if (o.length > 3) {
  7930. o = o.substr(-4);
  7931. s = parseInt(o.substr(0, 1), 8);
  7932. o = o.substr(1);
  7933. }
  7934. sticy = ((s & 1) == 1); // not support
  7935. sgid = ((s & 2) == 2);
  7936. suid = ((s & 4) == 4);
  7937. str = '';
  7938. for(i=0; i<3; i++) {
  7939. if ((parseInt(o.substr(i, 1), 8) & 4) == 4) {
  7940. str += 'r';
  7941. } else {
  7942. str += '-';
  7943. }
  7944. if ((parseInt(o.substr(i, 1), 8) & 2) == 2) {
  7945. str += 'w';
  7946. } else {
  7947. str += '-';
  7948. }
  7949. if ((parseInt(o.substr(i, 1), 8) & 1) == 1) {
  7950. str += ((i==0 && suid)||(i==1 && sgid))? 's' : 'x';
  7951. } else {
  7952. str += '-';
  7953. }
  7954. }
  7955. }
  7956. if (style == 'both') {
  7957. return str + ' (' + oct + ')';
  7958. } else if (style == 'string') {
  7959. return str;
  7960. } else {
  7961. return oct;
  7962. }
  7963. },
  7964. /**
  7965. * Regist this.decodeRawString function
  7966. *
  7967. * @return void
  7968. */
  7969. registRawStringDecoder : function(rawStringDecoder) {
  7970. if ($.isFunction(rawStringDecoder)) {
  7971. this.decodeRawString = this.options.rawStringDecoder = rawStringDecoder;
  7972. }
  7973. },
  7974. /**
  7975. * Return boolean that uploadable MIME type into target folder
  7976. *
  7977. * @param String mime MIME type
  7978. * @param String target target folder hash
  7979. * @return Bool
  7980. */
  7981. uploadMimeCheck : function(mime, target) {
  7982. target = target || this.cwd().hash;
  7983. var res = true, // default is allow
  7984. mimeChecker = this.option('uploadMime', target),
  7985. allow,
  7986. deny,
  7987. check = function(checker) {
  7988. var ret = false;
  7989. if (typeof checker === 'string' && checker.toLowerCase() === 'all') {
  7990. ret = true;
  7991. } else if (Array.isArray(checker) && checker.length) {
  7992. $.each(checker, function(i, v) {
  7993. v = v.toLowerCase();
  7994. if (v === 'all' || mime.indexOf(v) === 0) {
  7995. ret = true;
  7996. return false;
  7997. }
  7998. });
  7999. }
  8000. return ret;
  8001. };
  8002. if (mime && $.isPlainObject(mimeChecker)) {
  8003. mime = mime.toLowerCase();
  8004. allow = check(mimeChecker.allow);
  8005. deny = check(mimeChecker.deny);
  8006. if (mimeChecker.firstOrder === 'allow') {
  8007. res = false; // default is deny
  8008. if (! deny && allow === true) { // match only allow
  8009. res = true;
  8010. }
  8011. } else {
  8012. res = true; // default is allow
  8013. if (deny === true && ! allow) { // match only deny
  8014. res = false;
  8015. }
  8016. }
  8017. }
  8018. return res;
  8019. },
  8020. /**
  8021. * call chained sequence of async deferred functions
  8022. *
  8023. * @param Array tasks async functions
  8024. * @return Object jQuery.Deferred
  8025. */
  8026. sequence : function(tasks) {
  8027. var l = tasks.length,
  8028. chain = function(task, idx) {
  8029. ++idx;
  8030. if (tasks[idx]) {
  8031. return chain(task.then(tasks[idx]), idx);
  8032. } else {
  8033. return task;
  8034. }
  8035. };
  8036. if (l > 1) {
  8037. return chain(tasks[0](), 0);
  8038. } else {
  8039. return tasks[0]();
  8040. }
  8041. },
  8042. /**
  8043. * Reload contents of target URL for clear browser cache
  8044. *
  8045. * @param String url target URL
  8046. * @return Object jQuery.Deferred
  8047. */
  8048. reloadContents : function(url) {
  8049. var dfd = $.Deferred(),
  8050. ifm;
  8051. try {
  8052. ifm = $('<iframe width="1" height="1" scrolling="no" frameborder="no" style="position:absolute; top:-1px; left:-1px" crossorigin="use-credentials">')
  8053. .attr('src', url)
  8054. .one('load', function() {
  8055. var ifm = $(this);
  8056. try {
  8057. this.contentDocument.location.reload(true);
  8058. ifm.one('load', function() {
  8059. ifm.remove();
  8060. dfd.resolve();
  8061. });
  8062. } catch(e) {
  8063. ifm.attr('src', '').attr('src', url).one('load', function() {
  8064. ifm.remove();
  8065. dfd.resolve();
  8066. });
  8067. }
  8068. })
  8069. .appendTo('body');
  8070. } catch(e) {
  8071. ifm && ifm.remove();
  8072. dfd.reject();
  8073. }
  8074. return dfd;
  8075. },
  8076. /**
  8077. * Make netmount option for OAuth2
  8078. *
  8079. * @param String protocol
  8080. * @param String name
  8081. * @param String host
  8082. * @param Object opts Default {noOffline: false, root: 'root', pathI18n: 'folderId', folders: true}
  8083. }
  8084. *
  8085. * @return Object
  8086. */
  8087. makeNetmountOptionOauth : function(protocol, name, host, opt) {
  8088. var noOffline = typeof opt === 'boolean'? opt : null, // for backward compat
  8089. opts = Object.assign({
  8090. noOffline : false,
  8091. root : 'root',
  8092. pathI18n : 'folderId',
  8093. folders : true
  8094. }, (noOffline === null? (opt || {}) : {noOffline : noOffline})),
  8095. addFolders = function(fm, bro, folders) {
  8096. var self = this,
  8097. cnt = Object.keys($.isPlainObject(folders)? folders : {}).length,
  8098. select;
  8099. bro.next().remove();
  8100. if (cnt) {
  8101. select = $('<select class="ui-corner-all elfinder-tabstop" style="max-width:200px;">').append(
  8102. $($.map(folders, function(n,i){return '<option value="'+fm.escape((i+'').trim())+'">'+fm.escape(n)+'</option>';}).join(''))
  8103. ).on('change click', function(e){
  8104. var node = $(this),
  8105. path = node.val(),
  8106. spn;
  8107. self.inputs.path.val(path);
  8108. if (opts.folders && (e.type === 'change' || node.data('current') !== path)) {
  8109. node.next().remove();
  8110. node.data('current', path);
  8111. if (path != opts.root) {
  8112. spn = spinner();
  8113. if (xhr && xhr.state() === 'pending') {
  8114. fm.abortXHR(xhr, { quiet: true , abort: true });
  8115. }
  8116. node.after(spn);
  8117. xhr = fm.request({
  8118. data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', path: path, pass: 'folders'},
  8119. preventDefault : true
  8120. }).done(function(data){
  8121. addFolders.call(self, fm, node, data.folders);
  8122. }).always(function() {
  8123. fm.abortXHR(xhr, { quiet: true });
  8124. spn.remove();
  8125. }).xhr;
  8126. }
  8127. }
  8128. });
  8129. bro.after($('<div/>').append(select))
  8130. .closest('.ui-dialog').trigger('tabstopsInit');
  8131. select.trigger('focus');
  8132. }
  8133. },
  8134. spinner = function() {
  8135. return $('<div class="elfinder-netmount-spinner"/>').append('<span class="elfinder-spinner"/>');
  8136. },
  8137. xhr;
  8138. return {
  8139. vars : {},
  8140. name : name,
  8141. inputs: {
  8142. offline : $('<input type="checkbox"/>').on('change', function() {
  8143. $(this).parents('table.elfinder-netmount-tb').find('select:first').trigger('change', 'reset');
  8144. }),
  8145. host : $('<span><span class="elfinder-spinner"/></span><input type="hidden"/>'),
  8146. path : $('<input type="text" value="'+opts.root+'"/>'),
  8147. user : $('<input type="hidden"/>'),
  8148. pass : $('<input type="hidden"/>')
  8149. },
  8150. select: function(fm, ev, d){
  8151. var f = this.inputs,
  8152. oline = f.offline,
  8153. f0 = $(f.host[0]),
  8154. data = d || null;
  8155. this.vars.mbtn = f.host.closest('.ui-dialog').children('.ui-dialog-buttonpane:first').find('button.elfinder-btncnt-0');
  8156. if (! f0.data('inrequest')
  8157. && (f0.find('span.elfinder-spinner').length
  8158. || data === 'reset'
  8159. || (data === 'winfocus' && ! f0.siblings('span.elfinder-button-icon-reload').length))
  8160. )
  8161. {
  8162. if (oline.parent().children().length === 1) {
  8163. f.path.parent().prev().html(fm.i18n(opts.pathI18n));
  8164. oline.attr('title', fm.i18n('offlineAccess'));
  8165. oline.uniqueId().after($('<label/>').attr('for', oline.attr('id')).html(' '+fm.i18n('offlineAccess')));
  8166. }
  8167. f0.data('inrequest', true).empty().addClass('elfinder-spinner')
  8168. .parent().find('span.elfinder-button-icon').remove();
  8169. fm.request({
  8170. data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', options: {id: fm.id, offline: oline.prop('checked')? 1:0, pass: f.host[1].value}},
  8171. preventDefault : true
  8172. }).done(function(data){
  8173. f0.removeClass("elfinder-spinner").html(data.body.replace(/\{msg:([^}]+)\}/g, function(whole,s1){return fm.i18n(s1, host);}));
  8174. });
  8175. opts.noOffline && oline.closest('tr').hide();
  8176. } else {
  8177. oline.closest('tr')[(opts.noOffline || f.user.val())? 'hide':'show']();
  8178. f0.data('funcexpup') && f0.data('funcexpup')();
  8179. }
  8180. this.vars.mbtn[$(f.host[1]).val()? 'show':'hide']();
  8181. },
  8182. done: function(fm, data){
  8183. var f = this.inputs,
  8184. p = this.protocol,
  8185. f0 = $(f.host[0]),
  8186. f1 = $(f.host[1]),
  8187. expires = '&nbsp;';
  8188. opts.noOffline && f.offline.closest('tr').hide();
  8189. if (data.mode == 'makebtn') {
  8190. f0.removeClass('elfinder-spinner').removeData('expires').removeData('funcexpup');
  8191. f.host.find('input').on('mouseenter mouseleave', function(){$(this).toggleClass('ui-state-hover');});
  8192. f1.val('');
  8193. f.path.val(opts.root).next().remove();
  8194. f.user.val('');
  8195. f.pass.val('');
  8196. ! opts.noOffline && f.offline.closest('tr').show();
  8197. this.vars.mbtn.hide();
  8198. } else if (data.mode == 'folders') {
  8199. if (data.folders) {
  8200. addFolders.call(this, fm, f.path.nextAll(':last'), data.folders);
  8201. }
  8202. } else {
  8203. if (data.expires) {
  8204. expires = '()';
  8205. f0.data('expires', data.expires);
  8206. }
  8207. f0.html(host + expires).removeClass('elfinder-spinner');
  8208. if (data.expires) {
  8209. f0.data('funcexpup', function() {
  8210. var rem = Math.floor((f0.data('expires') - (+new Date()) / 1000) / 60);
  8211. if (rem < 3) {
  8212. f0.parent().children('.elfinder-button-icon-reload').click();
  8213. } else {
  8214. f0.text(f0.text().replace(/\(.*\)/, '('+fm.i18n(['minsLeft', rem])+')'));
  8215. setTimeout(function() {
  8216. if (f0.is(':visible')) {
  8217. f0.data('funcexpup')();
  8218. }
  8219. }, 60000);
  8220. }
  8221. });
  8222. f0.data('funcexpup')();
  8223. }
  8224. if (data.reset) {
  8225. p.trigger('change', 'reset');
  8226. return;
  8227. }
  8228. f0.parent().append($('<span class="elfinder-button-icon elfinder-button-icon-reload" title="'+fm.i18n('reAuth')+'">')
  8229. .on('click', function() {
  8230. f1.val('reauth');
  8231. p.trigger('change', 'reset');
  8232. }));
  8233. f1.val(protocol);
  8234. this.vars.mbtn.show();
  8235. if (data.folders) {
  8236. addFolders.call(this, fm, f.path, data.folders);
  8237. }
  8238. f.user.val('done');
  8239. f.pass.val('done');
  8240. f.offline.closest('tr').hide();
  8241. }
  8242. f0.removeData('inrequest');
  8243. },
  8244. fail: function(fm, err){
  8245. $(this.inputs.host[0]).removeData('inrequest');
  8246. this.protocol.trigger('change', 'reset');
  8247. },
  8248. integrateInfo: opts.integrate
  8249. };
  8250. },
  8251. /**
  8252. * Find cwd's nodes from files
  8253. *
  8254. * @param Array files
  8255. * @param Object opts {firstOnly: true|false}
  8256. */
  8257. findCwdNodes : function(files, opts) {
  8258. var self = this,
  8259. cwd = this.getUI('cwd'),
  8260. cwdHash = this.cwd().hash,
  8261. newItem = $();
  8262. opts = opts || {};
  8263. $.each(files, function(i, f) {
  8264. if (f.phash === cwdHash || self.searchStatus.state > 1) {
  8265. newItem = newItem.add(cwd.find('#'+self.cwdHash2Id(f.hash)));
  8266. if (opts.firstOnly) {
  8267. return false;
  8268. }
  8269. }
  8270. });
  8271. return newItem;
  8272. },
  8273. /**
  8274. * Convert from relative URL to abstract URL based on current URL
  8275. *
  8276. * @param String URL
  8277. * @return String
  8278. */
  8279. convAbsUrl : function(url) {
  8280. if (url.match(/^http/i)) {
  8281. return url;
  8282. }
  8283. if (url.substr(0,2) === '//') {
  8284. return window.location.protocol + url;
  8285. }
  8286. var root = window.location.protocol + '//' + window.location.host,
  8287. reg = /[^\/]+\/\.\.\//,
  8288. ret;
  8289. if (url.substr(0, 1) === '/') {
  8290. ret = root + url;
  8291. } else {
  8292. ret = root + window.location.pathname.replace(/\/[^\/]+$/, '/') + url;
  8293. }
  8294. ret = ret.replace('/./', '/');
  8295. while(reg.test(ret)) {
  8296. ret = ret.replace(reg, '');
  8297. }
  8298. return ret;
  8299. },
  8300. /**
  8301. * Is same origin to current site
  8302. *
  8303. * @param String check url
  8304. * @return Boolean
  8305. */
  8306. isSameOrigin : function (checkUrl) {
  8307. var url;
  8308. checkUrl = this.convAbsUrl(checkUrl);
  8309. if (location.origin && window.URL) {
  8310. try {
  8311. url = new URL(checkUrl);
  8312. return location.origin === url.origin;
  8313. } catch(e) {}
  8314. }
  8315. url = document.createElement('a');
  8316. url.href = checkUrl;
  8317. return location.protocol === url.protocol && location.host === url.host && location.port && url.port;
  8318. },
  8319. navHash2Id : function(hash) {
  8320. return this.navPrefix + hash;
  8321. },
  8322. navId2Hash : function(id) {
  8323. return typeof(id) == 'string' ? id.substr(this.navPrefix.length) : false;
  8324. },
  8325. cwdHash2Id : function(hash) {
  8326. return this.cwdPrefix + hash;
  8327. },
  8328. cwdId2Hash : function(id) {
  8329. return typeof(id) == 'string' ? id.substr(this.cwdPrefix.length) : false;
  8330. },
  8331. isInWindow : function(elem, nochkHide) {
  8332. var elm, rect;
  8333. if (! (elm = elem.get(0))) {
  8334. return false;
  8335. }
  8336. if (! nochkHide && elm.offsetParent === null) {
  8337. return false;
  8338. }
  8339. rect = elm.getBoundingClientRect();
  8340. return document.elementFromPoint(rect.left, rect.top)? true : false;
  8341. },
  8342. /**
  8343. * calculate elFinder node z-index
  8344. *
  8345. * @return void
  8346. */
  8347. zIndexCalc : function() {
  8348. var self = this,
  8349. node = this.getUI(),
  8350. ni = node.css('z-index');
  8351. if (ni && ni !== 'auto' && ni !== 'inherit') {
  8352. self.zIndex = ni;
  8353. } else {
  8354. node.parents().each(function(i, n) {
  8355. var z = $(n).css('z-index');
  8356. if (z !== 'auto' && z !== 'inherit' && (z = parseInt(z))) {
  8357. self.zIndex = z;
  8358. return false;
  8359. }
  8360. });
  8361. }
  8362. },
  8363. /**
  8364. * Load JavaScript files
  8365. *
  8366. * @param Array urls to load JavaScript file URLs
  8367. * @param Function callback call back function on script loaded
  8368. * @param Object opts Additional options to $.ajax OR {loadType: 'tag'} to load by script tag
  8369. * @param Object check { obj: (Object)ParentObject, name: (String)"Attribute name", timeout: (Integer)milliseconds }
  8370. * @return elFinder
  8371. */
  8372. loadScript : function(urls, callback, opts, check) {
  8373. var defOpts = {
  8374. dataType : 'script',
  8375. cache : true
  8376. },
  8377. success, cnt, scripts = {}, results = {};
  8378. opts = opts || {};
  8379. if (opts.tryRequire && this.hasRequire) {
  8380. require(urls, callback, opts.error);
  8381. } else {
  8382. success = function() {
  8383. var cnt, fi, hasError;
  8384. $.each(results, function(i, status) {
  8385. if (status !== 'success' && status !== 'notmodified') {
  8386. hasError = true;
  8387. return false;
  8388. }
  8389. });
  8390. if (!hasError) {
  8391. if ($.isFunction(callback)) {
  8392. if (check) {
  8393. if (typeof check.obj[check.name] === 'undefined') {
  8394. cnt = check.timeout? (check.timeout / 10) : 1;
  8395. fi = setInterval(function() {
  8396. if (--cnt < 0 || typeof check.obj[check.name] !== 'undefined') {
  8397. clearInterval(fi);
  8398. callback();
  8399. }
  8400. }, 10);
  8401. } else {
  8402. callback();
  8403. }
  8404. } else {
  8405. callback();
  8406. }
  8407. }
  8408. } else {
  8409. if (opts.error && $.isFunction(opts.error)) {
  8410. opts.error({ loadResults: results });
  8411. }
  8412. }
  8413. };
  8414. if (opts.loadType === 'tag') {
  8415. $('head > script').each(function() {
  8416. scripts[this.src] = this;
  8417. });
  8418. cnt = urls.length;
  8419. $.each(urls, function(i, url) {
  8420. var done = false,
  8421. script;
  8422. if (scripts[url]) {
  8423. results[i] = scripts[url]._error || 'success';
  8424. (--cnt < 1) && success();
  8425. } else {
  8426. script = document.createElement('script');
  8427. script.charset = opts.charset || 'UTF-8';
  8428. $('head').append(script);
  8429. script.onload = script.onreadystatechange = function() {
  8430. if ( !done && (!this.readyState ||
  8431. this.readyState === 'loaded' || this.readyState === 'complete') ) {
  8432. done = true;
  8433. results[i] = 'success';
  8434. (--cnt < 1) && success();
  8435. }
  8436. };
  8437. script.onerror = function(err) {
  8438. results[i] = script._error = (err && err.type)? err.type : 'error';
  8439. (--cnt < 1) && success();
  8440. };
  8441. script.src = url;
  8442. }
  8443. });
  8444. } else {
  8445. opts = $.isPlainObject(opts)? Object.assign(defOpts, opts) : defOpts;
  8446. cnt = 0;
  8447. (function appendScript(d, status) {
  8448. if (d !== void(0)) {
  8449. results[cnt++] = status;
  8450. }
  8451. if (urls.length) {
  8452. $.ajax(Object.assign({}, opts, {
  8453. url: urls.shift(),
  8454. success: appendScript,
  8455. error: appendScript
  8456. }));
  8457. } else {
  8458. success();
  8459. }
  8460. })();
  8461. }
  8462. }
  8463. return this;
  8464. },
  8465. /**
  8466. * Load CSS files
  8467. *
  8468. * @param Array to load CSS file URLs
  8469. * @param Object options
  8470. * @return elFinder
  8471. */
  8472. loadCss : function(urls, opts) {
  8473. var self = this,
  8474. clName, dfds;
  8475. if (typeof urls === 'string') {
  8476. urls = [ urls ];
  8477. }
  8478. if (opts) {
  8479. if (opts.className) {
  8480. clName = opts.className;
  8481. }
  8482. if (opts.dfd && opts.dfd.promise) {
  8483. dfds = [];
  8484. }
  8485. }
  8486. $.each(urls, function(i, url) {
  8487. var link, df;
  8488. url = self.convAbsUrl(url).replace(/^https?:/i, '');
  8489. if (dfds) {
  8490. dfds[i] = $.Deferred();
  8491. }
  8492. if (! $("head > link[href='+url+']").length) {
  8493. link = document.createElement('link');
  8494. link.type = 'text/css';
  8495. link.rel = 'stylesheet';
  8496. link.href = url;
  8497. if (clName) {
  8498. link.className = clName;
  8499. }
  8500. if (dfds) {
  8501. link.onload = function() {
  8502. dfds[i].resolve();
  8503. };
  8504. link.onerror = function() {
  8505. dfds[i].reject();
  8506. };
  8507. }
  8508. $('head').append(link);
  8509. } else {
  8510. dfds && dfds[i].resolve();
  8511. }
  8512. });
  8513. if (dfds) {
  8514. $.when.apply(null, dfds).done(function() {
  8515. opts.dfd.resolve();
  8516. }).fail(function() {
  8517. opts.dfd.reject();
  8518. });
  8519. }
  8520. return this;
  8521. },
  8522. /**
  8523. * Abortable async job performer
  8524. *
  8525. * @param func Function
  8526. * @param arr Array
  8527. * @param opts Object
  8528. *
  8529. * @return Object $.Deferred that has an extended method _abort()
  8530. */
  8531. asyncJob : function(func, arr, opts) {
  8532. var dfrd = $.Deferred(),
  8533. abortFlg = false,
  8534. parms = Object.assign({
  8535. interval : 0,
  8536. numPerOnce : 1
  8537. }, opts || {}),
  8538. resArr = [],
  8539. vars =[],
  8540. curVars = [],
  8541. exec,
  8542. tm;
  8543. dfrd._abort = function(resolve) {
  8544. tm && clearTimeout(tm);
  8545. vars = [];
  8546. abortFlg = true;
  8547. if (dfrd.state() === 'pending') {
  8548. dfrd[resolve? 'resolve' : 'reject'](resArr);
  8549. }
  8550. };
  8551. dfrd.fail(function() {
  8552. dfrd._abort();
  8553. }).always(function() {
  8554. dfrd._abort = function() {};
  8555. });
  8556. if (typeof func === 'function' && Array.isArray(arr)) {
  8557. vars = arr.concat();
  8558. exec = function() {
  8559. var i, len, res;
  8560. if (abortFlg) {
  8561. return;
  8562. }
  8563. curVars = vars.splice(0, parms.numPerOnce);
  8564. len = curVars.length;
  8565. for (i = 0; i < len; i++) {
  8566. if (abortFlg) {
  8567. break;
  8568. }
  8569. res = func(curVars[i]);
  8570. (res !== null) && resArr.push(res);
  8571. }
  8572. if (abortFlg) {
  8573. return;
  8574. }
  8575. if (vars.length) {
  8576. tm = setTimeout(exec, parms.interval);
  8577. } else {
  8578. dfrd.resolve(resArr);
  8579. }
  8580. };
  8581. if (vars.length) {
  8582. tm = setTimeout(exec, 0);
  8583. } else {
  8584. dfrd.resolve(resArr);
  8585. }
  8586. } else {
  8587. dfrd.reject();
  8588. }
  8589. return dfrd;
  8590. },
  8591. getSize : function(targets) {
  8592. var self = this,
  8593. reqs = [],
  8594. tgtlen = targets.length,
  8595. dfrd = $.Deferred().fail(function() {
  8596. $.each(reqs, function(i, req) {
  8597. if (req) {
  8598. req.syncOnFail && req.syncOnFail(false);
  8599. req.reject();
  8600. }
  8601. });
  8602. }),
  8603. getLeafRoots = function(file) {
  8604. var targets = [];
  8605. if (file.mime === 'directory') {
  8606. $.each(self.leafRoots, function(hash, roots) {
  8607. var phash;
  8608. if (hash === file.hash) {
  8609. targets.push.apply(targets, roots);
  8610. } else {
  8611. phash = (self.file(hash) || {}).phash;
  8612. while(phash) {
  8613. if (phash === file.hash) {
  8614. targets.push.apply(targets, roots);
  8615. }
  8616. phash = (self.file(phash) || {}).phash;
  8617. }
  8618. }
  8619. });
  8620. }
  8621. return targets;
  8622. },
  8623. checkPhash = function(hash) {
  8624. var dfd = $.Deferred(),
  8625. dir = self.file(hash),
  8626. target = dir? dir.phash : hash;
  8627. if (target && ! self.file(target)) {
  8628. self.request({
  8629. data : {
  8630. cmd : 'parents',
  8631. target : target
  8632. },
  8633. preventFail : true
  8634. }).done(function() {
  8635. self.one('parentsdone', function() {
  8636. dfd.resolve();
  8637. });
  8638. }).fail(function() {
  8639. dfd.resolve();
  8640. });
  8641. } else {
  8642. dfd.resolve();
  8643. }
  8644. return dfd;
  8645. },
  8646. cache = function() {
  8647. var dfd = $.Deferred(),
  8648. cnt = Object.keys(self.leafRoots).length;
  8649. if (cnt > 0) {
  8650. $.each(self.leafRoots, function(hash) {
  8651. checkPhash(hash).done(function() {
  8652. --cnt;
  8653. if (cnt < 1) {
  8654. dfd.resolve();
  8655. }
  8656. });
  8657. });
  8658. } else {
  8659. dfd.resolve();
  8660. }
  8661. return dfd;
  8662. };
  8663. self.autoSync('stop');
  8664. cache().done(function() {
  8665. var files = [], grps = {}, dfds = [], cache = [], singles = {};
  8666. $.each(targets, function() {
  8667. files.push.apply(files, getLeafRoots(self.file(this)));
  8668. });
  8669. targets.push.apply(targets, files);
  8670. $.each(targets, function() {
  8671. var root = self.root(this),
  8672. file = self.file(this);
  8673. if (file && (file.sizeInfo || file.mime !== 'directory')) {
  8674. cache.push($.Deferred().resolve(file.sizeInfo? file.sizeInfo : {size: file.size, dirCnt: 0, fileCnt : 1}));
  8675. } else {
  8676. if (! grps[root]) {
  8677. grps[root] = [ this ];
  8678. } else {
  8679. grps[root].push(this);
  8680. }
  8681. }
  8682. });
  8683. $.each(grps, function() {
  8684. var idx = dfds.length;
  8685. if (this.length === 1) {
  8686. singles[idx] = this[0];
  8687. }
  8688. dfds.push(self.request({
  8689. data : {cmd : 'size', targets : this},
  8690. preventDefault : true
  8691. }));
  8692. });
  8693. reqs.push.apply(reqs, dfds);
  8694. dfds.push.apply(dfds, cache);
  8695. $.when.apply($, dfds).fail(function() {
  8696. dfrd.reject();
  8697. }).done(function() {
  8698. var cache = function(h, data) {
  8699. var file;
  8700. if (file = self.file(h)) {
  8701. file.sizeInfo = { isCache: true };
  8702. $.each(['size', 'dirCnt', 'fileCnt'], function() {
  8703. file.sizeInfo[this] = data[this] || 0;
  8704. });
  8705. file.size = parseInt(file.sizeInfo.size);
  8706. changed.push(file);
  8707. }
  8708. },
  8709. size = 0,
  8710. fileCnt = 0,
  8711. dirCnt = 0,
  8712. argLen = arguments.length,
  8713. cnts = [],
  8714. cntsTxt = '',
  8715. changed = [],
  8716. i, file, data;
  8717. for (i = 0; i < argLen; i++) {
  8718. data = arguments[i];
  8719. file = null;
  8720. if (!data.isCache) {
  8721. if (singles[i] && (file = self.file(singles[i]))) {
  8722. cache(singles[i], data);
  8723. } else if (data.sizes && $.isPlainObject(data.sizes)) {
  8724. $.each(data.sizes, function(h, sizeInfo) {
  8725. cache(h, sizeInfo);
  8726. });
  8727. }
  8728. }
  8729. size += parseInt(data.size);
  8730. if (fileCnt !== false) {
  8731. if (typeof data.fileCnt === 'undefined') {
  8732. fileCnt = false;
  8733. }
  8734. fileCnt += parseInt(data.fileCnt || 0);
  8735. }
  8736. if (dirCnt !== false) {
  8737. if (typeof data.dirCnt === 'undefined') {
  8738. dirCnt = false;
  8739. }
  8740. dirCnt += parseInt(data.dirCnt || 0);
  8741. }
  8742. }
  8743. changed.length && self.change({changed: changed});
  8744. if (dirCnt !== false){
  8745. cnts.push(self.i18n('folders') + ': ' + (dirCnt - (tgtlen > 1? 0 : 1)));
  8746. }
  8747. if (fileCnt !== false){
  8748. cnts.push(self.i18n('files') + ': ' + fileCnt);
  8749. }
  8750. if (cnts.length) {
  8751. cntsTxt = '<br>' + cnts.join(', ');
  8752. }
  8753. dfrd.resolve({
  8754. size: size,
  8755. fileCnt: fileCnt,
  8756. dirCnt: dirCnt,
  8757. formated: (size >= 0 ? self.formatSize(size) : self.i18n('unknown')) + cntsTxt
  8758. });
  8759. });
  8760. self.autoSync();
  8761. });
  8762. return dfrd;
  8763. },
  8764. /**
  8765. * Gets the theme object by settings of options.themes
  8766. *
  8767. * @param String themeid The themeid
  8768. * @return Object jQuery.Deferred
  8769. */
  8770. getTheme : function(themeid) {
  8771. var self = this,
  8772. dfd = $.Deferred(),
  8773. absUrl = function(url, base) {
  8774. if (Array.isArray(url)) {
  8775. return $.map(url, function(v) {
  8776. return absUrl(v, base);
  8777. });
  8778. } else {
  8779. return url.match(/^(?:http|\/\/)/i)? url : (base || self.baseUrl) + url.replace(/^(?:\.\/|\/)/, '');
  8780. }
  8781. },
  8782. themeObj, m;
  8783. if (themeid && (themeObj = self.options.themes[themeid])) {
  8784. if (typeof themeObj === 'string') {
  8785. url = absUrl(themeObj);
  8786. if (m = url.match(/^(.+\/)[^/]+\.json$/i)) {
  8787. $.getJSON(url).done(function(data) {
  8788. themeObj = data;
  8789. themeObj.id = themeid;
  8790. if (themeObj.cssurls) {
  8791. themeObj.cssurls = absUrl(themeObj.cssurls, m[1]);
  8792. }
  8793. dfd.resolve(themeObj);
  8794. }).fail(function() {
  8795. dfd.reject();
  8796. });
  8797. } else {
  8798. dfd.resolve({
  8799. id: themeid,
  8800. name: themeid,
  8801. cssurls: [url]
  8802. });
  8803. }
  8804. } else if ($.isPlainObject(themeObj) && themeObj.cssurls) {
  8805. themeObj.id = themeid;
  8806. themeObj.cssurls = absUrl(themeObj.cssurls);
  8807. if (!Array.isArray(themeObj.cssurls)) {
  8808. themeObj.cssurls = [themeObj.cssurls];
  8809. }
  8810. if (!themeObj.name) {
  8811. themeObj.name = themeid;
  8812. }
  8813. dfd.resolve(themeObj);
  8814. } else {
  8815. dfd.reject();
  8816. }
  8817. } else {
  8818. dfd.reject();
  8819. }
  8820. return dfd;
  8821. },
  8822. /**
  8823. * Change current theme
  8824. *
  8825. * @param String themeid The themeid
  8826. * @return Object this elFinder instance
  8827. */
  8828. changeTheme : function(themeid) {
  8829. var self = this;
  8830. if (themeid) {
  8831. if (self.options.themes[themeid] && (!self.theme || self.theme.id !== themeid)) {
  8832. self.getTheme(themeid).done(function(themeObj) {
  8833. if (themeObj.cssurls) {
  8834. $('head>link.elfinder-theme-ext').remove();
  8835. self.loadCss(themeObj.cssurls, {
  8836. className: 'elfinder-theme-ext',
  8837. dfd: $.Deferred().done(function() {
  8838. self.theme = themeObj;
  8839. self.trigger && self.trigger('themechange');
  8840. })
  8841. });
  8842. }
  8843. });
  8844. } else if (themeid === 'default' && self.theme) {
  8845. $('head>link.elfinder-theme-ext').remove();
  8846. self.theme = null;
  8847. self.trigger && self.trigger('themechange');
  8848. }
  8849. }
  8850. return this;
  8851. },
  8852. /**
  8853. * Apply leaf root stats to target directory
  8854. *
  8855. * @param object dir object of target directory
  8856. * @param boolean update is force update
  8857. *
  8858. * @return boolean dir object was chenged
  8859. */
  8860. applyLeafRootStats : function(dir, update) {
  8861. var self = this,
  8862. prev = update? dir : (self.file(dir.hash) || dir),
  8863. prevTs = prev.ts,
  8864. change = false;
  8865. // backup original stats
  8866. if (update || !dir._realStats) {
  8867. dir._realStats = {
  8868. locked: dir.locked || 0,
  8869. dirs: dir.dirs || 0,
  8870. ts: dir.ts
  8871. };
  8872. }
  8873. // set lock
  8874. dir.locked = 1;
  8875. if (!prev.locked) {
  8876. change = true;
  8877. }
  8878. // has leaf root to `dirs: 1`
  8879. dir.dirs = 1;
  8880. if (!prev.dirs) {
  8881. change = true;
  8882. }
  8883. // set ts
  8884. $.each(self.leafRoots[dir.hash], function() {
  8885. var f = self.file(this);
  8886. if (f && f.ts && (dir.ts || 0) < f.ts) {
  8887. dir.ts = f.ts;
  8888. }
  8889. });
  8890. if (prevTs !== dir.ts) {
  8891. change = true;
  8892. }
  8893. return change;
  8894. },
  8895. /**
  8896. * To aborted XHR object
  8897. *
  8898. * @param Object xhr
  8899. * @param Object opts
  8900. *
  8901. * @return void
  8902. */
  8903. abortXHR : function(xhr, o) {
  8904. var opts = o || {};
  8905. if (xhr) {
  8906. opts.quiet && (xhr.quiet = true);
  8907. if (opts.abort && xhr._requestId) {
  8908. this.request({
  8909. data: {
  8910. cmd: 'abort',
  8911. id: xhr._requestId
  8912. },
  8913. preventDefault: true
  8914. });
  8915. }
  8916. xhr.abort();
  8917. xhr = void 0;
  8918. }
  8919. },
  8920. /**
  8921. * Gets the request identifier
  8922. *
  8923. * @return String The request identifier.
  8924. */
  8925. getRequestId : function() {
  8926. return (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
  8927. },
  8928. /**
  8929. * Flip key and value of array or object
  8930. *
  8931. * @param Array | Object { a: 1, b: 1, c: 2 }
  8932. * @param Mixed Static value
  8933. * @return Object { 1: "b", 2: "c" }
  8934. */
  8935. arrayFlip : function (trans, val) {
  8936. var key,
  8937. tmpArr = {},
  8938. isArr = $.isArray(trans);
  8939. for (key in trans) {
  8940. if (isArr || trans.hasOwnProperty(key)) {
  8941. tmpArr[trans[key]] = val || key;
  8942. }
  8943. }
  8944. return tmpArr;
  8945. },
  8946. /**
  8947. * Return array ["name without extention", "extention"]
  8948. *
  8949. * @param String name
  8950. *
  8951. * @return Array
  8952. *
  8953. */
  8954. splitFileExtention : function(name) {
  8955. var m;
  8956. if (m = name.match(/^(.+?)?\.((?:tar\.(?:gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(?:gz|bz2)|[a-z0-9]{1,10})$/i)) {
  8957. if (typeof m[1] === 'undefined') {
  8958. m[1] = '';
  8959. }
  8960. return [m[1], m[2]];
  8961. } else {
  8962. return [name, ''];
  8963. }
  8964. },
  8965. /**
  8966. * Slice the ArrayBuffer by sliceSize
  8967. *
  8968. * @param arraybuffer arrayBuffer The array buffer
  8969. * @param Number sliceSize The slice size
  8970. * @return Array Array of sleced arraybuffer
  8971. */
  8972. sliceArrayBuffer : function(arrayBuffer, sliceSize) {
  8973. var segments= [],
  8974. fi = 0;
  8975. while(fi * sliceSize < arrayBuffer.byteLength){
  8976. segments.push(arrayBuffer.slice(fi * sliceSize, (fi + 1) * sliceSize));
  8977. fi++;
  8978. }
  8979. return segments;
  8980. },
  8981. arrayBufferToBase64 : function(ab) {
  8982. if (!window.btoa) {
  8983. return '';
  8984. }
  8985. var dView = new Uint8Array(ab), // Get a byte view
  8986. arr = Array.prototype.slice.call(dView), // Create a normal array
  8987. arr1 = arr.map(function(item) {
  8988. return String.fromCharCode(item); // Convert
  8989. });
  8990. return window.btoa(arr1.join('')); // Form a string
  8991. },
  8992. log : function(m) { window.console && window.console.log && window.console.log(m); return this; },
  8993. debug : function(type, m) {
  8994. var d = this.options.debug;
  8995. if (d && (d === 'all' || d[type])) {
  8996. window.console && window.console.log && window.console.log('elfinder debug: ['+type+'] ['+this.id+']', m);
  8997. }
  8998. if (type === 'backend-error') {
  8999. if (! this.cwd().hash || (d && (d === 'all' || d['backend-error']))) {
  9000. m = Array.isArray(m)? m : [ m ];
  9001. this.error(m);
  9002. }
  9003. } else if (type === 'backend-debug') {
  9004. this.trigger('backenddebug', m);
  9005. }
  9006. return this;
  9007. },
  9008. time : function(l) { window.console && window.console.time && window.console.time(l); },
  9009. timeEnd : function(l) { window.console && window.console.timeEnd && window.console.timeEnd(l); }
  9010. };
  9011. /**
  9012. * for conpat ex. ie8...
  9013. *
  9014. * Object.keys() - JavaScript | MDN
  9015. * https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
  9016. */
  9017. if (!Object.keys) {
  9018. Object.keys = (function () {
  9019. var hasOwnProperty = Object.prototype.hasOwnProperty,
  9020. hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
  9021. dontEnums = [
  9022. 'toString',
  9023. 'toLocaleString',
  9024. 'valueOf',
  9025. 'hasOwnProperty',
  9026. 'isPrototypeOf',
  9027. 'propertyIsEnumerable',
  9028. 'constructor'
  9029. ],
  9030. dontEnumsLength = dontEnums.length;
  9031. return function (obj) {
  9032. if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
  9033. var result = [];
  9034. for (var prop in obj) {
  9035. if (hasOwnProperty.call(obj, prop)) result.push(prop);
  9036. }
  9037. if (hasDontEnumBug) {
  9038. for (var i=0; i < dontEnumsLength; i++) {
  9039. if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
  9040. }
  9041. }
  9042. return result;
  9043. };
  9044. })();
  9045. }
  9046. // Array.isArray
  9047. if (!Array.isArray) {
  9048. Array.isArray = function(arr) {
  9049. return jQuery.isArray(arr);
  9050. };
  9051. }
  9052. // Object.assign
  9053. if (!Object.assign) {
  9054. Object.assign = function() {
  9055. return jQuery.extend.apply(null, arguments);
  9056. };
  9057. }
  9058. // String.repeat
  9059. if (!String.prototype.repeat) {
  9060. String.prototype.repeat = function(count) {
  9061. 'use strict';
  9062. if (this == null) {
  9063. throw new TypeError('can\'t convert ' + this + ' to object');
  9064. }
  9065. var str = '' + this;
  9066. count = +count;
  9067. if (count != count) {
  9068. count = 0;
  9069. }
  9070. if (count < 0) {
  9071. throw new RangeError('repeat count must be non-negative');
  9072. }
  9073. if (count == Infinity) {
  9074. throw new RangeError('repeat count must be less than infinity');
  9075. }
  9076. count = Math.floor(count);
  9077. if (str.length == 0 || count == 0) {
  9078. return '';
  9079. }
  9080. // Ensuring count is a 31-bit integer allows us to heavily optimize the
  9081. // main part. But anyway, most current (August 2014) browsers can't handle
  9082. // strings 1 << 28 chars or longer, so:
  9083. if (str.length * count >= 1 << 28) {
  9084. throw new RangeError('repeat count must not overflow maximum string size');
  9085. }
  9086. var rpt = '';
  9087. for (var i = 0; i < count; i++) {
  9088. rpt += str;
  9089. }
  9090. return rpt;
  9091. };
  9092. }
  9093. // String.trim
  9094. if (!String.prototype.trim) {
  9095. String.prototype.trim = function() {
  9096. return this.replace(/^\s+|\s+$/g, '');
  9097. };
  9098. }
  9099. // Array.apply
  9100. (function () {
  9101. try {
  9102. Array.apply(null, {});
  9103. return;
  9104. } catch (e) { }
  9105. var toString = Object.prototype.toString,
  9106. arrayType = '[object Array]',
  9107. _apply = Function.prototype.apply,
  9108. slice = /*@cc_on @if (@_jscript_version <= 5.8)
  9109. function () {
  9110. var a = [], i = this.length;
  9111. while (i-- > 0) a[i] = this[i];
  9112. return a;
  9113. }@else@*/Array.prototype.slice/*@end@*/;
  9114. Function.prototype.apply = function apply(thisArg, argArray) {
  9115. return _apply.call(this, thisArg,
  9116. toString.call(argArray) === arrayType ? argArray : slice.call(argArray));
  9117. };
  9118. })();
  9119. // Array.from
  9120. if (!Array.from) {
  9121. Array.from = function(obj) {
  9122. return obj.length === 1 ? [obj[0]] : Array.apply(null, obj);
  9123. };
  9124. }
  9125. // window.requestAnimationFrame and window.cancelAnimationFrame
  9126. if (!window.cancelAnimationFrame) {
  9127. // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
  9128. // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
  9129. // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
  9130. // MIT license
  9131. (function() {
  9132. var lastTime = 0;
  9133. var vendors = ['ms', 'moz', 'webkit', 'o'];
  9134. for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  9135. window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
  9136. window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
  9137. || window[vendors[x]+'CancelRequestAnimationFrame'];
  9138. }
  9139. if (!window.requestAnimationFrame)
  9140. window.requestAnimationFrame = function(callback, element) {
  9141. var currTime = new Date().getTime();
  9142. var timeToCall = Math.max(0, 16 - (currTime - lastTime));
  9143. var id = window.setTimeout(function() { callback(currTime + timeToCall); },
  9144. timeToCall);
  9145. lastTime = currTime + timeToCall;
  9146. return id;
  9147. };
  9148. if (!window.cancelAnimationFrame)
  9149. window.cancelAnimationFrame = function(id) {
  9150. clearTimeout(id);
  9151. };
  9152. }());
  9153. }
  9154. /*
  9155. * File: /js/elFinder.version.js
  9156. */
  9157. /**
  9158. * Application version
  9159. *
  9160. * @type String
  9161. **/
  9162. elFinder.prototype.version = '2.1.43';
  9163. /*
  9164. * File: /js/jquery.elfinder.js
  9165. */
  9166. /*** jQuery UI droppable performance tune for elFinder ***/
  9167. (function(){
  9168. if ($.ui) {
  9169. if ($.ui.ddmanager) {
  9170. var origin = $.ui.ddmanager.prepareOffsets;
  9171. $.ui.ddmanager.prepareOffsets = function( t, event ) {
  9172. var isOutView = function(elem) {
  9173. if (elem.is(':hidden')) {
  9174. return true;
  9175. }
  9176. var rect = elem[0].getBoundingClientRect();
  9177. return document.elementFromPoint(rect.left, rect.top)? false : true;
  9178. };
  9179. if (event.type === 'mousedown' || t.options.elfRefresh) {
  9180. var i, d,
  9181. m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
  9182. l = m.length;
  9183. for ( i = 0; i < l; i++ ) {
  9184. d = m[ i ];
  9185. if (d.options.autoDisable && (!d.options.disabled || d.options.autoDisable > 1)) {
  9186. d.options.disabled = isOutView(d.element);
  9187. d.options.autoDisable = d.options.disabled? 2 : 1;
  9188. }
  9189. }
  9190. }
  9191. // call origin function
  9192. return origin( t, event );
  9193. };
  9194. }
  9195. }
  9196. })();
  9197. /**
  9198. *
  9199. * jquery.binarytransport.js
  9200. *
  9201. * @description. jQuery ajax transport for making binary data type requests.
  9202. * @version 1.0
  9203. * @author Henry Algus <henryalgus@gmail.com>
  9204. *
  9205. */
  9206. // use this transport for "binary" data type
  9207. $.ajaxTransport('+binary', function(options, originalOptions, jqXHR) {
  9208. // check for conditions and support for blob / arraybuffer response type
  9209. if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob)))))
  9210. {
  9211. var xhr;
  9212. return {
  9213. // create new XMLHttpRequest
  9214. send: function(headers, callback){
  9215. // setup all variables
  9216. xhr = new XMLHttpRequest();
  9217. var url = options.url,
  9218. type = options.type,
  9219. async = options.async || true,
  9220. // blob or arraybuffer. Default is blob
  9221. dataType = options.responseType || 'blob',
  9222. data = options.data || null,
  9223. username = options.username,
  9224. password = options.password;
  9225. xhr.addEventListener('load', function(){
  9226. var data = {};
  9227. data[options.dataType] = xhr.response;
  9228. // make callback and send data
  9229. callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders());
  9230. });
  9231. xhr.open(type, url, async, username, password);
  9232. // setup custom headers
  9233. for (var i in headers ) {
  9234. xhr.setRequestHeader(i, headers[i] );
  9235. }
  9236. // setuo xhrFields
  9237. if (options.xhrFields) {
  9238. for (var key in options.xhrFields) {
  9239. if (key in xhr) {
  9240. xhr[key] = options.xhrFields[key];
  9241. }
  9242. }
  9243. }
  9244. xhr.responseType = dataType;
  9245. xhr.send(data);
  9246. },
  9247. abort: function(){
  9248. xhr.abort();
  9249. }
  9250. };
  9251. }
  9252. });
  9253. /*!
  9254. * jQuery UI Touch Punch 0.2.3
  9255. *
  9256. * Copyright 2011–2014, Dave Furfero
  9257. * Dual licensed under the MIT or GPL Version 2 licenses.
  9258. *
  9259. * Depends:
  9260. * jquery.ui.widget.js
  9261. * jquery.ui.mouse.js
  9262. */
  9263. (function ($) {
  9264. // Detect touch support
  9265. $.support.touch = 'ontouchend' in document;
  9266. // Ignore browsers without touch support
  9267. if (!$.support.touch) {
  9268. return;
  9269. }
  9270. var mouseProto = $.ui.mouse.prototype,
  9271. _mouseInit = mouseProto._mouseInit,
  9272. _mouseDestroy = mouseProto._mouseDestroy,
  9273. touchHandled,
  9274. posX, posY;
  9275. /**
  9276. * Simulate a mouse event based on a corresponding touch event
  9277. * @param {Object} event A touch event
  9278. * @param {String} simulatedType The corresponding mouse event
  9279. */
  9280. function simulateMouseEvent (event, simulatedType) {
  9281. // Ignore multi-touch events
  9282. if (event.originalEvent.touches.length > 1) {
  9283. return;
  9284. }
  9285. if (! $(event.currentTarget).hasClass('touch-punch-keep-default')) {
  9286. event.preventDefault();
  9287. }
  9288. var touch = event.originalEvent.changedTouches[0],
  9289. simulatedEvent = document.createEvent('MouseEvents');
  9290. // Initialize the simulated mouse event using the touch event's coordinates
  9291. simulatedEvent.initMouseEvent(
  9292. simulatedType, // type
  9293. true, // bubbles
  9294. true, // cancelable
  9295. window, // view
  9296. 1, // detail
  9297. touch.screenX, // screenX
  9298. touch.screenY, // screenY
  9299. touch.clientX, // clientX
  9300. touch.clientY, // clientY
  9301. false, // ctrlKey
  9302. false, // altKey
  9303. false, // shiftKey
  9304. false, // metaKey
  9305. 0, // button
  9306. null // relatedTarget
  9307. );
  9308. // Dispatch the simulated event to the target element
  9309. event.target.dispatchEvent(simulatedEvent);
  9310. }
  9311. /**
  9312. * Handle the jQuery UI widget's touchstart events
  9313. * @param {Object} event The widget element's touchstart event
  9314. */
  9315. mouseProto._touchStart = function (event) {
  9316. var self = this;
  9317. // Ignore the event if another widget is already being handled
  9318. if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
  9319. return;
  9320. }
  9321. // Track element position to avoid "false" move
  9322. posX = event.originalEvent.changedTouches[0].screenX.toFixed(0);
  9323. posY = event.originalEvent.changedTouches[0].screenY.toFixed(0);
  9324. // Set the flag to prevent other widgets from inheriting the touch event
  9325. touchHandled = true;
  9326. // Track movement to determine if interaction was a click
  9327. self._touchMoved = false;
  9328. // Simulate the mouseover event
  9329. simulateMouseEvent(event, 'mouseover');
  9330. // Simulate the mousemove event
  9331. simulateMouseEvent(event, 'mousemove');
  9332. // Simulate the mousedown event
  9333. simulateMouseEvent(event, 'mousedown');
  9334. };
  9335. /**
  9336. * Handle the jQuery UI widget's touchmove events
  9337. * @param {Object} event The document's touchmove event
  9338. */
  9339. mouseProto._touchMove = function (event) {
  9340. // Ignore event if not handled
  9341. if (!touchHandled) {
  9342. return;
  9343. }
  9344. // Ignore if it's a "false" move (position not changed)
  9345. var x = event.originalEvent.changedTouches[0].screenX.toFixed(0);
  9346. var y = event.originalEvent.changedTouches[0].screenY.toFixed(0);
  9347. // Ignore if it's a "false" move (position not changed)
  9348. if (Math.abs(posX - x) <= 4 && Math.abs(posY - y) <= 4) {
  9349. return;
  9350. }
  9351. // Interaction was not a click
  9352. this._touchMoved = true;
  9353. // Simulate the mousemove event
  9354. simulateMouseEvent(event, 'mousemove');
  9355. };
  9356. /**
  9357. * Handle the jQuery UI widget's touchend events
  9358. * @param {Object} event The document's touchend event
  9359. */
  9360. mouseProto._touchEnd = function (event) {
  9361. // Ignore event if not handled
  9362. if (!touchHandled) {
  9363. return;
  9364. }
  9365. // Simulate the mouseup event
  9366. simulateMouseEvent(event, 'mouseup');
  9367. // Simulate the mouseout event
  9368. simulateMouseEvent(event, 'mouseout');
  9369. // If the touch interaction did not move, it should trigger a click
  9370. if (!this._touchMoved) {
  9371. // Simulate the click event
  9372. simulateMouseEvent(event, 'click');
  9373. }
  9374. // Unset the flag to allow other widgets to inherit the touch event
  9375. touchHandled = false;
  9376. this._touchMoved = false;
  9377. };
  9378. /**
  9379. * A duck punch of the $.ui.mouse _mouseInit method to support touch events.
  9380. * This method extends the widget with bound touch event handlers that
  9381. * translate touch events to mouse events and pass them to the widget's
  9382. * original mouse event handling methods.
  9383. */
  9384. mouseProto._mouseInit = function () {
  9385. var self = this;
  9386. if (self.element.hasClass('touch-punch')) {
  9387. // Delegate the touch handlers to the widget's element
  9388. self.element.on({
  9389. touchstart: $.proxy(self, '_touchStart'),
  9390. touchmove: $.proxy(self, '_touchMove'),
  9391. touchend: $.proxy(self, '_touchEnd')
  9392. });
  9393. }
  9394. // Call the original $.ui.mouse init method
  9395. _mouseInit.call(self);
  9396. };
  9397. /**
  9398. * Remove the touch event handlers
  9399. */
  9400. mouseProto._mouseDestroy = function () {
  9401. var self = this;
  9402. if (self.element.hasClass('touch-punch')) {
  9403. // Delegate the touch handlers to the widget's element
  9404. self.element.off({
  9405. touchstart: $.proxy(self, '_touchStart'),
  9406. touchmove: $.proxy(self, '_touchMove'),
  9407. touchend: $.proxy(self, '_touchEnd')
  9408. });
  9409. }
  9410. // Call the original $.ui.mouse destroy method
  9411. _mouseDestroy.call(self);
  9412. };
  9413. })(jQuery);
  9414. $.fn.elfinder = function(o, o2) {
  9415. if (o === 'instance') {
  9416. return this.getElFinder();
  9417. }
  9418. return this.each(function() {
  9419. var cmd = typeof o === 'string' ? o : '',
  9420. bootCallback = typeof o2 === 'function'? o2 : void(0),
  9421. opts;
  9422. if (!this.elfinder) {
  9423. if ($.isPlainObject(o)) {
  9424. new elFinder(this, o, bootCallback);
  9425. }
  9426. } else {
  9427. switch(cmd) {
  9428. case 'close':
  9429. case 'hide':
  9430. this.elfinder.hide();
  9431. break;
  9432. case 'open':
  9433. case 'show':
  9434. this.elfinder.show();
  9435. break;
  9436. case 'destroy':
  9437. this.elfinder.destroy();
  9438. break;
  9439. case 'reload':
  9440. case 'restart':
  9441. if (this.elfinder) {
  9442. opts = this.elfinder.options;
  9443. bootCallback = this.elfinder.bootCallback;
  9444. this.elfinder.destroy();
  9445. new elFinder(this, $.extend(true, opts, $.isPlainObject(o2)? o2 : {}), bootCallback);
  9446. }
  9447. break;
  9448. }
  9449. }
  9450. });
  9451. };
  9452. $.fn.getElFinder = function() {
  9453. var instance;
  9454. this.each(function() {
  9455. if (this.elfinder) {
  9456. instance = this.elfinder;
  9457. return false;
  9458. }
  9459. });
  9460. return instance;
  9461. };
  9462. $.fn.elfUiWidgetInstance = function(name) {
  9463. try {
  9464. return this[name]('instance');
  9465. } catch(e) {
  9466. // fallback for jQuery UI < 1.11
  9467. var data = this.data('ui-' + name);
  9468. if (data && typeof data === 'object' && data.widgetFullName === 'ui-' + name) {
  9469. return data;
  9470. }
  9471. return null;
  9472. }
  9473. };
  9474. // function scrollRight
  9475. if (! $.fn.scrollRight) {
  9476. $.fn.extend({
  9477. scrollRight: function (val) {
  9478. var node = this.get(0);
  9479. if (val === undefined) {
  9480. return Math.max(0, node.scrollWidth - (node.scrollLeft + node.clientWidth));
  9481. }
  9482. return this.scrollLeft(node.scrollWidth - node.clientWidth - val);
  9483. }
  9484. });
  9485. }
  9486. // function scrollBottom
  9487. if (! $.fn.scrollBottom) {
  9488. $.fn.extend({
  9489. scrollBottom: function(val) {
  9490. var node = this.get(0);
  9491. if (val === undefined) {
  9492. return Math.max(0, node.scrollHeight - (node.scrollTop + node.clientHeight));
  9493. }
  9494. return this.scrollTop(node.scrollHeight - node.clientHeight - val);
  9495. }
  9496. });
  9497. }
  9498. /*
  9499. * File: /js/elFinder.mimetypes.js
  9500. */
  9501. 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"};
  9502. /*
  9503. * File: /js/elFinder.options.js
  9504. */
  9505. /**
  9506. * Default elFinder config
  9507. *
  9508. * @type Object
  9509. * @autor Dmitry (dio) Levashov
  9510. */
  9511. elFinder.prototype._options = {
  9512. /**
  9513. * URLs of 3rd party libraries CDN
  9514. *
  9515. * @type Object
  9516. */
  9517. cdns : {
  9518. // for editor etc.
  9519. ace : 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.1',
  9520. codemirror : 'https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.40.2',
  9521. ckeditor : 'https://cdnjs.cloudflare.com/ajax/libs/ckeditor/4.10.0',
  9522. ckeditor5 : 'https://cdn.ckeditor.com/ckeditor5/11.1.1',
  9523. tinymce : 'https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.8.3',
  9524. simplemde : 'https://cdnjs.cloudflare.com/ajax/libs/simplemde/1.11.2',
  9525. fabric16 : 'https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.7',
  9526. tui : 'https://uicdn.toast.com',
  9527. // for quicklook etc.
  9528. hls : 'https://cdnjs.cloudflare.com/ajax/libs/hls.js/0.10.1/hls.min.js',
  9529. dash : 'https://cdnjs.cloudflare.com/ajax/libs/dashjs/2.9.1/dash.all.min.js',
  9530. flv : 'https://cdnjs.cloudflare.com/ajax/libs/flv.js/1.4.2/flv.min.js',
  9531. prettify : 'https://cdn.jsdelivr.net/gh/google/code-prettify@453bd5f51e61245339b738b1bbdd42d7848722ba/loader/run_prettify.js',
  9532. psd : 'https://cdnjs.cloudflare.com/ajax/libs/psd.js/3.2.0/psd.min.js',
  9533. rar : 'https://cdn.jsdelivr.net/gh/nao-pon/rar.js@6cef13ec66dd67992fc7f3ea22f132d770ebaf8b/rar.min.js',
  9534. 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
  9535. zlibGunzip : 'https://cdn.jsdelivr.net/gh/imaya/zlib.js@0.3.1/bin/gunzip.min.js',
  9536. marked : 'https://cdnjs.cloudflare.com/ajax/libs/marked/0.5.1/marked.min.js',
  9537. sparkmd5 : 'https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.min.js',
  9538. jssha : 'https://cdnjs.cloudflare.com/ajax/libs/jsSHA/2.3.1/sha.js',
  9539. amr : 'https://cdn.jsdelivr.net/gh/yxl/opencore-amr-js@dcf3d2b5f384a1d9ded2a54e4c137a81747b222b/js/amrnb.js'
  9540. },
  9541. /**
  9542. * Connector url. Required!
  9543. *
  9544. * @type String
  9545. */
  9546. url : '',
  9547. /**
  9548. * Ajax request type.
  9549. *
  9550. * @type String
  9551. * @default "get"
  9552. */
  9553. requestType : 'get',
  9554. /**
  9555. * Use CORS to connector url
  9556. *
  9557. * @type Boolean|null true|false|null(Auto detect)
  9558. */
  9559. cors : null,
  9560. /**
  9561. * Maximum number of concurrent connections on request
  9562. *
  9563. * @type Number
  9564. * @default 3
  9565. */
  9566. requestMaxConn : 3,
  9567. /**
  9568. * Transport to send request to backend.
  9569. * Required for future extensions using websockets/webdav etc.
  9570. * Must be an object with "send" method.
  9571. * transport.send must return $.Deferred() object
  9572. *
  9573. * @type Object
  9574. * @default null
  9575. * @example
  9576. * transport : {
  9577. * init : function(elfinderInstance) { },
  9578. * send : function(options) {
  9579. * var dfrd = $.Deferred();
  9580. * // connect to backend ...
  9581. * return dfrd;
  9582. * },
  9583. * upload : function(data) {
  9584. * var dfrd = $.Deferred();
  9585. * // upload ...
  9586. * return dfrd;
  9587. * }
  9588. *
  9589. * }
  9590. **/
  9591. transport : {},
  9592. /**
  9593. * URL to upload file to.
  9594. * If not set - connector URL will be used
  9595. *
  9596. * @type String
  9597. * @default ''
  9598. */
  9599. urlUpload : '',
  9600. /**
  9601. * Allow to drag and drop to upload files
  9602. *
  9603. * @type Boolean|String
  9604. * @default 'auto'
  9605. */
  9606. dragUploadAllow : 'auto',
  9607. /**
  9608. * Confirmation dialog displayed at the time of overwriting upload
  9609. *
  9610. * @type Boolean
  9611. * @default true
  9612. */
  9613. overwriteUploadConfirm : true,
  9614. /**
  9615. * Max size of chunked data of file upload
  9616. *
  9617. * @type Number
  9618. * @default 10485760(10MB)
  9619. */
  9620. uploadMaxChunkSize : 10485760,
  9621. /**
  9622. * Regular expression of file name to exclude when uploading folder
  9623. *
  9624. * @type Object
  9625. * @default { win: /^(?:desktop\.ini|thumbs\.db)$/i, mac: /^\.ds_store$/i }
  9626. */
  9627. folderUploadExclude : {
  9628. win: /^(?:desktop\.ini|thumbs\.db)$/i,
  9629. mac: /^\.ds_store$/i
  9630. },
  9631. /**
  9632. * Timeout for upload using iframe
  9633. *
  9634. * @type Number
  9635. * @default 0 - no timeout
  9636. */
  9637. iframeTimeout : 0,
  9638. /**
  9639. * Data to append to all requests and to upload files
  9640. *
  9641. * @type Object
  9642. * @default {}
  9643. */
  9644. customData : {},
  9645. /**
  9646. * Event listeners to bind on elFinder init
  9647. *
  9648. * @type Object
  9649. * @default {}
  9650. */
  9651. handlers : {},
  9652. /**
  9653. * Any custom headers to send across every ajax request
  9654. *
  9655. * @type Object
  9656. * @default {}
  9657. */
  9658. customHeaders : {},
  9659. /**
  9660. * Any custom xhrFields to send across every ajax request
  9661. *
  9662. * @type Object
  9663. * @default {}
  9664. */
  9665. xhrFields : {},
  9666. /**
  9667. * Interface language
  9668. *
  9669. * @type String
  9670. * @default "en"
  9671. */
  9672. lang : 'en',
  9673. /**
  9674. * Base URL of elfFinder library starting from Manager HTML
  9675. * Auto detect when empty value
  9676. *
  9677. * @type String
  9678. * @default ""
  9679. */
  9680. baseUrl : '',
  9681. /**
  9682. * Base URL of i18n js files
  9683. * baseUrl + "js/i18n/" when empty value
  9684. *
  9685. * @type String
  9686. * @default ""
  9687. */
  9688. i18nBaseUrl : '',
  9689. /**
  9690. * Auto load required CSS
  9691. * `false` to disable this function or
  9692. * CSS URL Array to load additional CSS files
  9693. *
  9694. * @type Boolean|Array
  9695. * @default true
  9696. */
  9697. cssAutoLoad : true,
  9698. /**
  9699. * Theme to load
  9700. * {"themeid" : "Theme CSS URL"} or
  9701. * {"themeid" : "Theme manifesto.json URL"} or
  9702. * Theme manifesto.json Object
  9703. * {
  9704. * "themeid" : {
  9705. * "name":"Theme Name",
  9706. * "cssurls":"Theme CSS URL",
  9707. * "author":"Author Name",
  9708. * "email":"Author Email",
  9709. * "license":"License",
  9710. * "link":"Web Site URL",
  9711. * "image":"Screen Shot URL",
  9712. * "description":"Description"
  9713. * }
  9714. * }
  9715. *
  9716. * @type Object
  9717. */
  9718. themes : {},
  9719. /**
  9720. * Theme id to initial theme
  9721. *
  9722. * @type String|Null
  9723. */
  9724. theme : null,
  9725. /**
  9726. * Additional css class for filemanager node.
  9727. *
  9728. * @type String
  9729. */
  9730. cssClass : '',
  9731. /**
  9732. * Active commands list. '*' means all of the commands that have been load.
  9733. * If some required commands will be missed here, elFinder will add its
  9734. *
  9735. * @type Array
  9736. */
  9737. commands : ['*'],
  9738. // Available commands list
  9739. //commands : [
  9740. // 'archive', 'back', 'chmod', 'colwidth', 'copy', 'cut', 'download', 'duplicate', 'edit', 'extract',
  9741. // 'forward', 'fullscreen', 'getfile', 'help', 'home', 'info', 'mkdir', 'mkfile', 'netmount', 'netunmount',
  9742. // 'open', 'opendir', 'paste', 'places', 'quicklook', 'reload', 'rename', 'resize', 'restore', 'rm',
  9743. // 'search', 'sort', 'up', 'upload', 'view', 'zipdl'
  9744. //],
  9745. /**
  9746. * Commands options.
  9747. *
  9748. * @type Object
  9749. **/
  9750. commandsOptions : {
  9751. // // configure shortcuts of any command
  9752. // // add `shortcuts` property into each command
  9753. // any_command_name : {
  9754. // shortcuts : [] // for disable this command's shortcuts
  9755. // },
  9756. // any_command_name : {
  9757. // shortcuts : function(fm, shortcuts) {
  9758. // // for add `CTRL + E` for this command action
  9759. // shortcuts[0]['pattern'] += ' ctrl+e';
  9760. // return shortcuts;
  9761. // }
  9762. // },
  9763. // any_command_name : {
  9764. // shortcuts : function(fm, shortcuts) {
  9765. // // for full customize of this command's shortcuts
  9766. // return [ { pattern: 'ctrl+e ctrl+down numpad_enter' + (fm.OS != 'mac' && ' enter') } ];
  9767. // }
  9768. // },
  9769. // "getfile" command options.
  9770. getfile : {
  9771. onlyURL : false,
  9772. // allow to return multiple files info
  9773. multiple : false,
  9774. // allow to return filers info
  9775. folders : false,
  9776. // action after callback (""/"close"/"destroy")
  9777. oncomplete : '',
  9778. // action when callback is fail (""/"close"/"destroy")
  9779. onerror : '',
  9780. // get path before callback call
  9781. getPath : true,
  9782. // get image sizes before callback call
  9783. getImgSize : false
  9784. },
  9785. open : {
  9786. // HTTP method that request to the connector when item URL is not valid URL.
  9787. // If you set to "get" will be displayed request parameter in the browser's location field
  9788. // so if you want to conceal its parameters should be given "post".
  9789. // Nevertheless, please specify "get" if you want to enable the partial request by HTTP Range header.
  9790. method : 'post',
  9791. // Where to open into : 'window'(default), 'tab' or 'tabs'
  9792. // 'tabs' opens in each tabs
  9793. into : 'window',
  9794. // Default command list of action when select file
  9795. // String value that is 'Command Name' or 'Command Name1/CommandName2...'
  9796. selectAction : 'open'
  9797. },
  9798. opennew : {
  9799. // URL of to open elFinder manager
  9800. // Default '' : Origin URL
  9801. url : '',
  9802. // Use search query of origin URL
  9803. useOriginQuery : true
  9804. },
  9805. // "upload" command options.
  9806. upload : {
  9807. // Open elFinder upload dialog: 'button' OR Open system OS upload dialog: 'uploadbutton'
  9808. ui : 'button'
  9809. },
  9810. // "download" command options.
  9811. download : {
  9812. // max request to download files when zipdl disabled
  9813. maxRequests : 10,
  9814. // minimum count of files to use zipdl
  9815. minFilesZipdl : 2
  9816. },
  9817. // "quicklook" command options.
  9818. quicklook : {
  9819. autoplay : true,
  9820. width : 450,
  9821. height : 300,
  9822. // ControlsList of HTML5 audio/video preview
  9823. // see https://googlechrome.github.io/samples/media/controlslist.html
  9824. mediaControlsList : '', // e.g. 'nodownload nofullscreen noremoteplayback'
  9825. // Show toolbar of PDF preview (with <embed> tag)
  9826. pdfToolbar : true,
  9827. // Maximum characters length to preview
  9828. textMaxlen : 2000,
  9829. // quicklook window must be contained in elFinder node on window open (true|false)
  9830. contain : false,
  9831. // preview window into NavDock (0 : undocked | 1 : docked(show) | 2 : docked(hide))
  9832. docked : 0,
  9833. // Docked preview height ('auto' or Number of pixel) 'auto' is setted to the Navbar width
  9834. dockHeight : 'auto',
  9835. // media auto play when docked
  9836. dockAutoplay : false,
  9837. // Google Maps API key (Require Maps JavaScript API)
  9838. googleMapsApiKey : '',
  9839. // Google Maps API Options
  9840. googleMapsOpts : {
  9841. maps : {},
  9842. kml : {
  9843. suppressInfoWindows : false,
  9844. preserveViewport : false
  9845. }
  9846. },
  9847. // ViewerJS (https://viewerjs.org/) Options
  9848. // To enable this you need to place ViewerJS on the same server as elFinder and specify that URL in `url`.
  9849. viewerjs : {
  9850. url: '', // Example '/ViewerJS/index.html'
  9851. mimes: ['application/pdf', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.spreadsheet', 'application/vnd.oasis.opendocument.presentation']
  9852. },
  9853. // MIME types to CAD-Files and 3D-Models online viewer on sharecad.org
  9854. // 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']
  9855. sharecadMimes : [],
  9856. // MIME types to use Google Docs online viewer
  9857. // 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']
  9858. googleDocsMimes : [],
  9859. // MIME types to use Microsoft Office Online viewer
  9860. // 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']
  9861. // These MIME types override "googleDocsMimes"
  9862. officeOnlineMimes : [],
  9863. // File size (byte) threshold when using the dim command for obtain the image size necessary to image preview
  9864. getDimThreshold : 200000,
  9865. // MIME-Type regular expression that does not check empty files
  9866. mimeRegexNotEmptyCheck : /^application\/vnd\.google-apps\./
  9867. },
  9868. // "quicklook" command options.
  9869. edit : {
  9870. // dialog width, integer(px) or integer+'%' (example: 650, '80%' ...)
  9871. dialogWidth : void(0),
  9872. // list of allowed mimetypes to edit of text files
  9873. // if empty - any text files can be edited
  9874. mimes : [],
  9875. // MIME-types of text file to make as empty files
  9876. makeTextMimes : ['text/plain', 'text/css', 'text/html'],
  9877. // Use the editor stored in the browser
  9878. // This value allowd overwrite with user preferences
  9879. useStoredEditor : false,
  9880. // Open the maximized editor window
  9881. // This value allowd overwrite with user preferences
  9882. editorMaximized : false,
  9883. // edit files in wysisyg's
  9884. editors : [
  9885. // {
  9886. // /**
  9887. // * editor info
  9888. // * @type Object
  9889. // */
  9890. // info : { name: 'Editor Name' },
  9891. // /**
  9892. // * files mimetypes allowed to edit in current wysisyg
  9893. // * @type Array
  9894. // */
  9895. // mimes : ['text/html'],
  9896. // /**
  9897. // * HTML element for editing area (optional for text editor)
  9898. // * @type String
  9899. // */
  9900. // html : '<textarea></textarea>',
  9901. // /**
  9902. // * Initialize editing area node (optional for text editor)
  9903. // *
  9904. // * @param String dialog DOM id
  9905. // * @param Object target file object
  9906. // * @param String target file content (text or Data URI Scheme(binary file))
  9907. // * @param Object elFinder instance
  9908. // * @type Function
  9909. // */
  9910. // init : function(id, file, content, fm) {
  9911. // $(this).attr('id', id + '-text').val(content);
  9912. // },
  9913. // /**
  9914. // * Get edited contents (optional for text editor)
  9915. // * @type Function
  9916. // */
  9917. // getContent : function() {
  9918. // return $(this).val();
  9919. // },
  9920. // /**
  9921. // * Called when "edit" dialog loaded.
  9922. // * Place to init wysisyg.
  9923. // * Can return wysisyg instance
  9924. // *
  9925. // * @param DOMElement textarea node
  9926. // * @return Object editor instance|jQuery.Deferred(return instance on resolve())
  9927. // */
  9928. // load : function(textarea) { },
  9929. // /**
  9930. // * Called before "edit" dialog closed.
  9931. // * Place to destroy wysisyg instance.
  9932. // *
  9933. // * @param DOMElement textarea node
  9934. // * @param Object wysisyg instance (if was returned by "load" callback)
  9935. // * @return void
  9936. // */
  9937. // close : function(textarea, instance) { },
  9938. // /**
  9939. // * Called before file content send to backend.
  9940. // * Place to update textarea content if needed.
  9941. // *
  9942. // * @param DOMElement textarea node
  9943. // * @param Object wysisyg instance (if was returned by "load" callback)
  9944. // * @return void
  9945. // */
  9946. // save : function(textarea, instance) {},
  9947. // /**
  9948. // * Called after load() or save().
  9949. // * Set focus to wysisyg editor.
  9950. // *
  9951. // * @param DOMElement textarea node
  9952. // * @param Object wysisyg instance (if was returned by "load" callback)
  9953. // * @return void
  9954. // */
  9955. // focus : function(textarea, instance) {}
  9956. // /**
  9957. // * Called after dialog resized..
  9958. // *
  9959. // * @param DOMElement textarea node
  9960. // * @param Object wysisyg instance (if was returned by "load" callback)
  9961. // * @param Object resize event object
  9962. // * @param Object data object
  9963. // * @return void
  9964. // */
  9965. // resize : function(textarea, instance, event, data) {}
  9966. //
  9967. // }
  9968. ],
  9969. // Character encodings of select box
  9970. encodings : ['Big5', 'Big5-HKSCS', 'Cp437', 'Cp737', 'Cp775', 'Cp850', 'Cp852', 'Cp855', 'Cp857', 'Cp858',
  9971. 'Cp862', 'Cp866', 'Cp874', 'EUC-CN', 'EUC-JP', 'EUC-KR', 'GB18030', 'ISO-2022-CN', 'ISO-2022-JP', 'ISO-2022-KR',
  9972. 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4', 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7',
  9973. 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-13', 'ISO-8859-15', 'KOI8-R', 'KOI8-U', 'Shift-JIS',
  9974. 'Windows-1250', 'Windows-1251', 'Windows-1252', 'Windows-1253', 'Windows-1254', 'Windows-1257'],
  9975. // options for extra editors
  9976. extraOptions : {
  9977. // TUI Image Editor's options
  9978. tuiImgEditOpts : {
  9979. // Path prefix of icon-a.svg, icon-b.svg, icon-c.svg and icon-d.svg in the Theme.
  9980. // `iconsPath` MUST follow the same origin policy.
  9981. iconsPath : void(0), // default is "./img/tui-"
  9982. // Theme object
  9983. theme : {}
  9984. },
  9985. // Specify the Creative Cloud API key when using Creative SDK image editor of Creative Cloud.
  9986. // You can get the API key at https://console.adobe.io/.
  9987. creativeCloudApiKey : '',
  9988. // Browsing manager URL for CKEditor, TinyMCE
  9989. // Uses self location with the empty value or not defined.
  9990. //managerUrl : 'elfinder.html'
  9991. managerUrl : null,
  9992. // CKEditor5' builds mode - 'classic', 'inline' or 'balloon'
  9993. ckeditor5Mode : 'balloon',
  9994. // Setting for Online-Convert.com
  9995. onlineConvert : {
  9996. maxSize : 100, // (MB) Max 100MB on free account
  9997. showLink : true // It must be enabled with free account
  9998. }
  9999. }
  10000. },
  10001. search : {
  10002. // Incremental search from the current view
  10003. incsearch : {
  10004. enable : true, // is enable true or false
  10005. minlen : 1, // minimum number of characters
  10006. wait : 500 // wait milliseconds
  10007. },
  10008. // Additional search types
  10009. searchTypes : {
  10010. // "SearchMime" is implemented in default
  10011. SearchMime : { // The key is search type that send to the connector
  10012. name : 'btnMime', // Button text to be processed in i18n()
  10013. title : 'searchMime' // Button title to be processed in i18n()
  10014. }
  10015. }
  10016. },
  10017. // "info" command options.
  10018. info : {
  10019. // If the URL of the Directory is null,
  10020. // it is assumed that the link destination is a URL to open the folder in elFinder
  10021. nullUrlDirLinkSelf : true,
  10022. // Information items to be hidden by default
  10023. // These name are 'size', 'aliasfor', 'path', 'link', 'dim', 'modify', 'perms', 'locked', 'owner', 'group', 'perm' and your custom info items label
  10024. hideItems : [],
  10025. // Maximum file size (byte) to get file contents hash (md5, sha256 ...)
  10026. showHashMaxsize : 104857600, // 100 MB
  10027. // Array of hash algorisms to show on info dialog
  10028. // These name are 'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'shake128' and 'shake256'
  10029. showHashAlgorisms : ['md5', 'sha256'],
  10030. custom : {
  10031. // /**
  10032. // * Example of custom info `desc`
  10033. // */
  10034. // desc : {
  10035. // /**
  10036. // * Lable (require)
  10037. // * It is filtered by the `fm.i18n()`
  10038. // *
  10039. // * @type String
  10040. // */
  10041. // label : 'Description',
  10042. //
  10043. // /**
  10044. // * Template (require)
  10045. // * `{id}` is replaced in dialog.id
  10046. // *
  10047. // * @type String
  10048. // */
  10049. // tpl : '<div class="elfinder-info-desc"><span class="elfinder-spinner"></span></div>',
  10050. //
  10051. // /**
  10052. // * Restricts to mimetypes (optional)
  10053. // * Exact match or category match
  10054. // *
  10055. // * @type Array
  10056. // */
  10057. // mimes : ['text', 'image/jpeg', 'directory'],
  10058. //
  10059. // /**
  10060. // * Restricts to file.hash (optional)
  10061. // *
  10062. // * @ type Regex
  10063. // */
  10064. // hashRegex : /^l\d+_/,
  10065. //
  10066. // /**
  10067. // * Request that asks for the description and sets the field (optional)
  10068. // *
  10069. // * @type Function
  10070. // */
  10071. // action : function(file, fm, dialog) {
  10072. // fm.request({
  10073. // data : { cmd : 'desc', target: file.hash },
  10074. // preventDefault: true,
  10075. // })
  10076. // .fail(function() {
  10077. // dialog.find('div.elfinder-info-desc').html(fm.i18n('unknown'));
  10078. // })
  10079. // .done(function(data) {
  10080. // dialog.find('div.elfinder-info-desc').html(data.desc);
  10081. // });
  10082. // }
  10083. // }
  10084. }
  10085. },
  10086. mkdir: {
  10087. // Enable automatic switching function ["New Folder" / "Into New Folder"] of toolbar buttton
  10088. intoNewFolderToolbtn: false
  10089. },
  10090. resize: {
  10091. // defalt status of snap to 8px grid of the jpeg image ("enable" or "disable")
  10092. grid8px : 'disable',
  10093. // Preset size array [width, height]
  10094. presetSize : [[320, 240], [400, 400], [640, 480], [800,600]],
  10095. // File size (bytes) threshold when using the `dim` command for obtain the image size necessary to start editing
  10096. getDimThreshold : 204800,
  10097. // File size (bytes) to request to get substitute image (400px) with the `dim` command
  10098. dimSubImgSize : 307200
  10099. },
  10100. rm: {
  10101. // If trash is valid, items moves immediately to the trash holder without confirm.
  10102. quickTrash : true,
  10103. // Maximum wait seconds when checking the number of items to into the trash
  10104. infoCheckWait : 10,
  10105. // Maximum number of items that can be placed into the Trash at one time
  10106. toTrashMaxItems : 1000
  10107. },
  10108. help : {
  10109. // Tabs to show
  10110. view : ['about', 'shortcuts', 'help', 'integrations', 'debug'],
  10111. // HTML source URL of the heip tab
  10112. helpSource : ''
  10113. },
  10114. preference : {
  10115. // dialog width
  10116. width: 600,
  10117. // dialog height
  10118. height: 400,
  10119. // tabs setting see preference.js : build()
  10120. categories: null,
  10121. // preference setting see preference.js : build()
  10122. prefs: null,
  10123. // language setting see preference.js : build()
  10124. langs: null,
  10125. // Command list of action when select file
  10126. // Array value are 'Command Name' or 'Command Name1/CommandName2...'
  10127. selectActions : ['open', 'edit/download', 'resize/edit/download', 'download', 'quicklook']
  10128. }
  10129. },
  10130. /**
  10131. * Callback for prepare boot up
  10132. *
  10133. * - The this object in the function is an elFinder node
  10134. * - The first parameter is elFinder Instance
  10135. * - The second parameter is an object of other parameters
  10136. * For now it can use `dfrdsBeforeBootup` Array
  10137. *
  10138. * @type Function
  10139. * @default null
  10140. * @return void
  10141. */
  10142. bootCallback : null,
  10143. /**
  10144. * Callback for "getfile" commands.
  10145. * Required to use elFinder with WYSIWYG editors etc..
  10146. *
  10147. * @type Function
  10148. * @default null (command not active)
  10149. */
  10150. getFileCallback : null,
  10151. /**
  10152. * Default directory view. icons/list
  10153. *
  10154. * @type String
  10155. * @default "icons"
  10156. */
  10157. defaultView : 'icons',
  10158. /**
  10159. * Hash of default directory path to open
  10160. *
  10161. * NOTE: This setting will be disabled if the target folder is specified in location.hash.
  10162. *
  10163. * If you want to find the hash in Javascript
  10164. * can be obtained with the following code. (In the case of a standard hashing method)
  10165. *
  10166. * var volumeId = 'l1_'; // volume id
  10167. * var path = 'path/to/target'; // without root path
  10168. * //var path = 'path\\to\\target'; // use \ on windows server
  10169. * var hash = volumeId + btoa(path).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '.').replace(/\.+$/, '');
  10170. *
  10171. * @type String
  10172. * @default ""
  10173. */
  10174. startPathHash : '',
  10175. /**
  10176. * Emit a sound when a file is deleted
  10177. * Sounds are in sounds/ folder
  10178. *
  10179. * @type Boolean
  10180. * @default true
  10181. */
  10182. sound : true,
  10183. /**
  10184. * UI plugins to load.
  10185. * Current dir ui and dialogs loads always.
  10186. * Here set not required plugins as folders tree/toolbar/statusbar etc.
  10187. *
  10188. * @type Array
  10189. * @default ['toolbar', 'tree', 'path', 'stat']
  10190. * @full ['toolbar', 'places', 'tree', 'path', 'stat']
  10191. */
  10192. ui : ['toolbar', 'tree', 'path', 'stat'],
  10193. /**
  10194. * Some UI plugins options.
  10195. * @type Object
  10196. */
  10197. uiOptions : {
  10198. // toolbar configuration
  10199. toolbar : [
  10200. ['home', 'back', 'forward', 'up', 'reload'],
  10201. ['netmount'],
  10202. ['mkdir', 'mkfile', 'upload'],
  10203. ['open', 'download', 'getfile'],
  10204. ['undo', 'redo'],
  10205. ['copy', 'cut', 'paste', 'rm', 'empty', 'hide'],
  10206. ['duplicate', 'rename', 'edit', 'resize', 'chmod'],
  10207. ['selectall', 'selectnone', 'selectinvert'],
  10208. ['quicklook', 'info'],
  10209. ['extract', 'archive'],
  10210. ['search'],
  10211. ['view', 'sort'],
  10212. ['preference', 'help'],
  10213. ['fullscreen']
  10214. ],
  10215. // toolbar extra options
  10216. toolbarExtra : {
  10217. // also displays the text label on the button (true / false / 'none')
  10218. displayTextLabel: false,
  10219. // Exclude `displayTextLabel` setting UA type
  10220. labelExcludeUA: ['Mobile'],
  10221. // auto hide on initial open
  10222. autoHideUA: ['Mobile'],
  10223. // Initial setting value of hide button in toolbar setting
  10224. defaultHides: ['home', 'reload'],
  10225. // show Preference button ('none', 'auto', 'always')
  10226. // If you do not include 'preference' in the context menu you should specify 'auto' or 'always'
  10227. showPreferenceButton: 'none',
  10228. // show Preference button into contextmenu of the toolbar (true / false)
  10229. preferenceInContextmenu: true
  10230. },
  10231. // directories tree options
  10232. tree : {
  10233. // expand current root on init
  10234. openRootOnLoad : true,
  10235. // expand current work directory on open
  10236. openCwdOnOpen : true,
  10237. // auto loading current directory parents and do expand their node.
  10238. syncTree : true,
  10239. // Maximum number of display of each child trees
  10240. // The tree of directories with children exceeding this number will be split
  10241. subTreeMax : 100,
  10242. // Numbar of max connctions of subdirs request
  10243. subdirsMaxConn : 2,
  10244. // Number of max simultaneous processing directory of subdirs
  10245. subdirsAtOnce : 5,
  10246. // Durations of each animations
  10247. durations : {
  10248. slideUpDown : 'fast',
  10249. autoScroll : 'fast'
  10250. }
  10251. // ,
  10252. // /**
  10253. // * Add CSS class name to navbar directories (optional)
  10254. // * see: https://github.com/Studio-42/elFinder/pull/1061,
  10255. // * https://github.com/Studio-42/elFinder/issues/1231
  10256. // *
  10257. // * @type Function
  10258. // */
  10259. // getClass: function(dir) {
  10260. // // e.g. This adds the directory's name (lowercase) with prefix as a CSS class
  10261. // return 'elfinder-tree-' + dir.name.replace(/[ "]/g, '').toLowerCase();
  10262. // }
  10263. },
  10264. // navbar options
  10265. navbar : {
  10266. minWidth : 150,
  10267. maxWidth : 500,
  10268. // auto hide on initial open
  10269. autoHideUA: [] // e.g. ['Mobile']
  10270. },
  10271. navdock : {
  10272. // disabled navdock ui
  10273. disabled : false,
  10274. // percentage of initial maximum height to work zone
  10275. initMaxHeight : '50%',
  10276. // percentage of maximum height to work zone by user resize action
  10277. maxHeight : '90%'
  10278. },
  10279. cwd : {
  10280. // display parent folder with ".." name :)
  10281. oldSchool : false,
  10282. // fm.UA types array to show item select checkboxes e.g. ['All'] or ['Mobile'] etc. default: ['Touch']
  10283. showSelectCheckboxUA : ['Touch'],
  10284. // file info columns displayed
  10285. listView : {
  10286. // name is always displayed, cols are ordered
  10287. // e.g. ['perm', 'date', 'size', 'kind', 'owner', 'group', 'mode']
  10288. // mode: 'mode'(by `fileModeStyle` setting), 'modestr'(rwxr-xr-x) , 'modeoct'(755), 'modeboth'(rwxr-xr-x (755))
  10289. // 'owner', 'group' and 'mode', It's necessary set volume driver option "statOwner" to `true`
  10290. // for custom, characters that can be used in the name is `a-z0-9_`
  10291. columns : ['perm', 'date', 'size', 'kind'],
  10292. // override this if you want custom columns name
  10293. // example
  10294. // columnsCustomName : {
  10295. // date : 'Last modification',
  10296. // kind : 'Mime type'
  10297. // }
  10298. columnsCustomName : {},
  10299. // fixed list header colmun
  10300. fixedHeader : true
  10301. },
  10302. // icons view setting
  10303. iconsView : {
  10304. // default icon size (0-3 in default CSS (cwd.css - elfinder-cwd-size[number]))
  10305. size: 0,
  10306. // number of maximum size (3 in default CSS (cwd.css - elfinder-cwd-size[number]))
  10307. // uses in preference.js
  10308. sizeMax: 3,
  10309. // Name of each size
  10310. sizeNames: {
  10311. 0: 'viewSmall',
  10312. 1: 'viewMedium',
  10313. 2: 'viewLarge',
  10314. 3: 'viewExtraLarge'
  10315. }
  10316. },
  10317. // /**
  10318. // * Add CSS class name to cwd directories (optional)
  10319. // * see: https://github.com/Studio-42/elFinder/pull/1061,
  10320. // * https://github.com/Studio-42/elFinder/issues/1231
  10321. // *
  10322. // * @type Function
  10323. // */
  10324. // ,
  10325. // getClass: function(file) {
  10326. // // e.g. This adds the directory's name (lowercase) with prefix as a CSS class
  10327. // return 'elfinder-cwd-' + file.name.replace(/[ "]/g, '').toLowerCase();
  10328. //}
  10329. //,
  10330. //// Template placeholders replacement rules for overwrite. see ui/cwd.js replacement
  10331. //replacement : {
  10332. // tooltip : function(f, fm) {
  10333. // var list = fm.viewType == 'list', // current view type
  10334. // query = fm.searchStatus.state == 2, // is in search results
  10335. // title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''),
  10336. // info = '';
  10337. // if (query && f.path) {
  10338. // info = fm.escape(f.path.replace(/\/[^\/]*$/, ''));
  10339. // } else {
  10340. // info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, '&#13;') : '';
  10341. // }
  10342. // if (list) {
  10343. // info += (info? '&#13;' : '') + fm.escape(f.name);
  10344. // }
  10345. // return info? info + '&#13;' + title : title;
  10346. // }
  10347. //}
  10348. },
  10349. path : {
  10350. // Move to head of work zone without UI navbar
  10351. toWorkzoneWithoutNavbar : true
  10352. },
  10353. dialog : {
  10354. // Enable to auto focusing on mouse over in the target form element
  10355. focusOnMouseOver : true
  10356. },
  10357. toast : {
  10358. animate : {
  10359. // to show
  10360. showMethod: 'fadeIn', // fadeIn, slideDown, and show are built into jQuery
  10361. showDuration: 300, // milliseconds
  10362. showEasing: 'swing', // swing and linear are built into jQuery
  10363. // timeout to hide
  10364. timeOut: 3000,
  10365. // to hide
  10366. hideMethod: 'fadeOut',
  10367. hideDuration: 1500,
  10368. hideEasing: 'swing'
  10369. }
  10370. }
  10371. },
  10372. /**
  10373. * MIME regex of send HTTP header "Content-Disposition: inline" or allow preview in quicklook
  10374. * This option will overwrite by connector configuration
  10375. *
  10376. * @type String
  10377. * @default '^(?:(?:image|video|audio)|text/plain|application/pdf$)'
  10378. * @example
  10379. * dispInlineRegex : '.', // is allow inline of all of MIME types
  10380. * dispInlineRegex : '$^', // is not allow inline of all of MIME types
  10381. */
  10382. dispInlineRegex : '^(?:(?:image|video|audio)|application/(?:x-mpegURL|dash\+xml)|(?:text/plain|application/pdf)$)',
  10383. /**
  10384. * Display only required files by types
  10385. *
  10386. * @type Array
  10387. * @default []
  10388. * @example
  10389. * onlyMimes : ["image"] - display all images
  10390. * onlyMimes : ["image/png", "application/x-shockwave-flash"] - display png and flash
  10391. */
  10392. onlyMimes : [],
  10393. /**
  10394. * Custom files sort rules.
  10395. * All default rules (name/size/kind/date/perm/mode/owner/group) set in elFinder._sortRules
  10396. *
  10397. * @type {Object}
  10398. * @example
  10399. * sortRules : {
  10400. * name : function(file1, file2) { return file1.name.toLowerCase().localeCompare(file2.name.toLowerCase()); }
  10401. * }
  10402. */
  10403. sortRules : {},
  10404. /**
  10405. * Default sort type.
  10406. *
  10407. * @type {String}
  10408. */
  10409. sortType : 'name',
  10410. /**
  10411. * Default sort order.
  10412. *
  10413. * @type {String}
  10414. * @default "asc"
  10415. */
  10416. sortOrder : 'asc',
  10417. /**
  10418. * Display folders first?
  10419. *
  10420. * @type {Boolean}
  10421. * @default true
  10422. */
  10423. sortStickFolders : true,
  10424. /**
  10425. * Sort also applies to the treeview
  10426. *
  10427. * @type {Boolean}
  10428. * @default false
  10429. */
  10430. sortAlsoTreeview : false,
  10431. /**
  10432. * If true - elFinder will formating dates itself,
  10433. * otherwise - backend date will be used.
  10434. *
  10435. * @type Boolean
  10436. */
  10437. clientFormatDate : true,
  10438. /**
  10439. * Show UTC dates.
  10440. * Required set clientFormatDate to true
  10441. *
  10442. * @type Boolean
  10443. */
  10444. UTCDate : false,
  10445. /**
  10446. * File modification datetime format.
  10447. * Value from selected language data is used by default.
  10448. * Set format here to overwrite it.
  10449. *
  10450. * @type String
  10451. * @default ""
  10452. */
  10453. dateFormat : '',
  10454. /**
  10455. * File modification datetime format in form "Yesterday 12:23:01".
  10456. * Value from selected language data is used by default.
  10457. * Set format here to overwrite it.
  10458. * Use $1 for "Today"/"Yesterday" placeholder
  10459. *
  10460. * @type String
  10461. * @default ""
  10462. * @example "$1 H:m:i"
  10463. */
  10464. fancyDateFormat : '',
  10465. /**
  10466. * Style of file mode at cwd-list, info dialog
  10467. * 'string' (ex. rwxr-xr-x) or 'octal' (ex. 755) or 'both' (ex. rwxr-xr-x (755))
  10468. *
  10469. * @type {String}
  10470. * @default 'both'
  10471. */
  10472. fileModeStyle : 'both',
  10473. /**
  10474. * elFinder width
  10475. *
  10476. * @type String|Number
  10477. * @default "auto"
  10478. */
  10479. width : 'auto',
  10480. /**
  10481. * elFinder node height
  10482. * Number: pixcel or String: Number + "%"
  10483. *
  10484. * @type Number | String
  10485. * @default 400
  10486. */
  10487. height : 400,
  10488. /**
  10489. * Base node object or selector
  10490. * Element which is the reference of the height percentage
  10491. *
  10492. * @type Object|String
  10493. * @default null | $(window) (if height is percentage)
  10494. **/
  10495. heightBase : null,
  10496. /**
  10497. * Make elFinder resizable if jquery ui resizable available
  10498. *
  10499. * @type Boolean
  10500. * @default true
  10501. */
  10502. resizable : true,
  10503. /**
  10504. * Timeout before open notifications dialogs
  10505. *
  10506. * @type Number
  10507. * @default 500 (.5 sec)
  10508. */
  10509. notifyDelay : 500,
  10510. /**
  10511. * Position CSS, Width of notifications dialogs
  10512. *
  10513. * @type Object
  10514. * @default {position: {}, width : null} - Apply CSS definition
  10515. * position: CSS object | null (null: position center & middle)
  10516. */
  10517. notifyDialog : {position: {}, width : null},
  10518. /**
  10519. * Dialog contained in the elFinder node
  10520. *
  10521. * @type Boolean
  10522. * @default false
  10523. */
  10524. dialogContained : false,
  10525. /**
  10526. * Allow shortcuts
  10527. *
  10528. * @type Boolean
  10529. * @default true
  10530. */
  10531. allowShortcuts : true,
  10532. /**
  10533. * Remeber last opened dir to open it after reload or in next session
  10534. *
  10535. * @type Boolean
  10536. * @default true
  10537. */
  10538. rememberLastDir : true,
  10539. /**
  10540. * Clear historys(elFinder) on reload(not browser) function
  10541. * Historys was cleared on Reload function on elFinder 2.0 (value is true)
  10542. *
  10543. * @type Boolean
  10544. * @default false
  10545. */
  10546. reloadClearHistory : false,
  10547. /**
  10548. * Use browser native history with supported browsers
  10549. *
  10550. * @type Boolean
  10551. * @default true
  10552. */
  10553. useBrowserHistory : true,
  10554. /**
  10555. * Lazy load config.
  10556. * How many files display at once?
  10557. *
  10558. * @type Number
  10559. * @default 50
  10560. */
  10561. showFiles : 50,
  10562. /**
  10563. * Lazy load config.
  10564. * Distance in px to cwd bottom edge to start display files
  10565. *
  10566. * @type Number
  10567. * @default 50
  10568. */
  10569. showThreshold : 50,
  10570. /**
  10571. * Additional rule to valid new file name.
  10572. * By default not allowed empty names or '..'
  10573. * This setting does not have a sense of security.
  10574. *
  10575. * @type false|RegExp|function
  10576. * @default false
  10577. * @example
  10578. * disable names with spaces:
  10579. * validName : /^[^\s]+$/,
  10580. */
  10581. validName : false,
  10582. /**
  10583. * Additional rule to filtering for browsing.
  10584. * This setting does not have a sense of security.
  10585. *
  10586. * The object `this` is elFinder instance object in this function
  10587. *
  10588. * @type false|RegExp|function
  10589. * @default false
  10590. * @example
  10591. * show only png and jpg files:
  10592. * fileFilter : /.*\.(png|jpg)$/i,
  10593. *
  10594. * show only image type files:
  10595. * fileFilter : function(file) { return file.mime && file.mime.match(/^image\//i); },
  10596. */
  10597. fileFilter : false,
  10598. /**
  10599. * Backup name suffix.
  10600. *
  10601. * @type String
  10602. * @default "~"
  10603. */
  10604. backupSuffix : '~',
  10605. /**
  10606. * Sync content interval
  10607. *
  10608. * @type Number
  10609. * @default 0 (do not sync)
  10610. */
  10611. sync : 0,
  10612. /**
  10613. * Sync start on load if sync value >= 1000
  10614. *
  10615. * @type Bool
  10616. * @default true
  10617. */
  10618. syncStart : true,
  10619. /**
  10620. * How many thumbnails create in one request
  10621. *
  10622. * @type Number
  10623. * @default 5
  10624. */
  10625. loadTmbs : 5,
  10626. /**
  10627. * Cookie option for browsersdoes not suppot localStorage
  10628. *
  10629. * @type Object
  10630. */
  10631. cookie : {
  10632. expires : 30,
  10633. domain : '',
  10634. path : '/',
  10635. secure : false
  10636. },
  10637. /**
  10638. * Contextmenu config
  10639. *
  10640. * @type Object
  10641. */
  10642. contextmenu : {
  10643. // navbarfolder menu
  10644. navbar : ['open', 'opennew', 'download', '|', 'upload', 'mkdir', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', 'empty', 'hide', '|', 'rename', '|', 'archive', '|', 'places', 'info', 'chmod', 'netunmount'],
  10645. // current directory menu
  10646. cwd : ['undo', 'redo', '|', 'back', 'up', 'reload', '|', 'upload', 'mkdir', 'mkfile', 'paste', '|', 'empty', 'hide', '|', 'view', 'sort', 'selectall', 'colwidth', '|', 'places', 'info', 'chmod', 'netunmount', '|', 'fullscreen', '|', 'preference'],
  10647. // current directory file menu
  10648. 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']
  10649. },
  10650. /**
  10651. * elFinder node enable always
  10652. * This value will set to `true` if <body> has elFinder node only
  10653. *
  10654. * @type Bool
  10655. * @default false
  10656. */
  10657. enableAlways : false,
  10658. /**
  10659. * elFinder node enable by mouse over
  10660. *
  10661. * @type Bool
  10662. * @default true
  10663. */
  10664. enableByMouseOver : true,
  10665. /**
  10666. * Show window close confirm dialog
  10667. * Value is which state to show
  10668. * 'hasNotifyDialog', 'editingFile', 'hasSelectedItem' and 'hasClipboardData'
  10669. *
  10670. * @type Array
  10671. * @default ['hasNotifyDialog', 'editingFile']
  10672. */
  10673. windowCloseConfirm : ['hasNotifyDialog', 'editingFile'],
  10674. /**
  10675. * Function decoding 'raw' string converted to unicode
  10676. * It is used instead of fm.decodeRawString(str)
  10677. *
  10678. * @type Null|Function
  10679. */
  10680. rawStringDecoder : typeof Encoding === 'object' && $.isFunction(Encoding.convert)? function(str) {
  10681. return Encoding.convert(str, {
  10682. to: 'UNICODE',
  10683. type: 'string'
  10684. });
  10685. } : null,
  10686. /**
  10687. * Debug config
  10688. *
  10689. * @type Array|String('auto')|Boolean(true|false)
  10690. */
  10691. // debug : true
  10692. debug : ['error', 'warning', 'event-destroy']
  10693. };
  10694. /*
  10695. * File: /js/elFinder.options.netmount.js
  10696. */
  10697. /**
  10698. * Default elFinder config of commandsOptions.netmount
  10699. *
  10700. * @type Object
  10701. */
  10702. elFinder.prototype._options.commandsOptions.netmount = {
  10703. ftp: {
  10704. name : 'FTP',
  10705. inputs: {
  10706. host : $('<input type="text"/>'),
  10707. port : $('<input type="number" placeholder="21" class="elfinder-input-optional"/>'),
  10708. path : $('<input type="text" value="/"/>'),
  10709. user : $('<input type="text"/>'),
  10710. pass : $('<input type="password" autocomplete="new-password"/>'),
  10711. FTPS : $('<input type="checkbox" value="1" title="File Transfer Protocol over SSL/TLS"/>'),
  10712. encoding : $('<input type="text" placeholder="Optional" class="elfinder-input-optional"/>'),
  10713. locale : $('<input type="text" placeholder="Optional" class="elfinder-input-optional"/>')
  10714. }
  10715. },
  10716. dropbox2: elFinder.prototype.makeNetmountOptionOauth('dropbox2', 'Dropbox', 'Dropbox', {noOffline : true,
  10717. root : '/',
  10718. pathI18n : 'path',
  10719. integrate : {
  10720. title: 'Dropbox.com',
  10721. link: 'https://www.dropbox.com'
  10722. }
  10723. }),
  10724. googledrive: elFinder.prototype.makeNetmountOptionOauth('googledrive', 'Google Drive', 'Google', {
  10725. integrate : {
  10726. title: 'Google Drive',
  10727. link: 'https://www.google.com/drive/'
  10728. }
  10729. }),
  10730. onedrive: elFinder.prototype.makeNetmountOptionOauth('onedrive', 'One Drive', 'OneDrive', {
  10731. integrate : {
  10732. title: 'Microsoft OneDrive',
  10733. link: 'https://onedrive.live.com'
  10734. }
  10735. }),
  10736. box: elFinder.prototype.makeNetmountOptionOauth('box', 'Box', 'Box', {
  10737. noOffline : true,
  10738. integrate : {
  10739. title: 'Box.com',
  10740. link: 'https://www.box.com'
  10741. }
  10742. })
  10743. };
  10744. /*
  10745. * File: /js/elFinder.history.js
  10746. */
  10747. /**
  10748. * @class elFinder.history
  10749. * Store visited folders
  10750. * and provide "back" and "forward" methods
  10751. *
  10752. * @author Dmitry (dio) Levashov
  10753. */
  10754. elFinder.prototype.history = function(fm) {
  10755. var self = this,
  10756. /**
  10757. * Update history on "open" event?
  10758. *
  10759. * @type Boolean
  10760. */
  10761. update = true,
  10762. /**
  10763. * Directories hashes storage
  10764. *
  10765. * @type Array
  10766. */
  10767. history = [],
  10768. /**
  10769. * Current directory index in history
  10770. *
  10771. * @type Number
  10772. */
  10773. current,
  10774. /**
  10775. * Clear history
  10776. *
  10777. * @return void
  10778. */
  10779. reset = function() {
  10780. history = [fm.cwd().hash];
  10781. current = 0;
  10782. update = true;
  10783. },
  10784. /**
  10785. * Browser native history object
  10786. */
  10787. nativeHistory = (fm.options.useBrowserHistory && window.history && window.history.pushState)? window.history : null,
  10788. /**
  10789. * Open prev/next folder
  10790. *
  10791. * @Boolen open next folder?
  10792. * @return jQuery.Deferred
  10793. */
  10794. go = function(fwd) {
  10795. if ((fwd && self.canForward()) || (!fwd && self.canBack())) {
  10796. update = false;
  10797. return fm.exec('open', history[fwd ? ++current : --current]).fail(reset);
  10798. }
  10799. return $.Deferred().reject();
  10800. },
  10801. /**
  10802. * Sets the native history.
  10803. *
  10804. * @param String thash target hash
  10805. */
  10806. setNativeHistory = function(thash) {
  10807. if (nativeHistory && (! nativeHistory.state || nativeHistory.state.thash !== thash)) {
  10808. nativeHistory.pushState({thash: thash}, null, location.pathname + location.search + (thash? '#elf_' + thash : ''));
  10809. }
  10810. };
  10811. /**
  10812. * Return true if there is previous visited directories
  10813. *
  10814. * @return Boolen
  10815. */
  10816. this.canBack = function() {
  10817. return current > 0;
  10818. };
  10819. /**
  10820. * Return true if can go forward
  10821. *
  10822. * @return Boolen
  10823. */
  10824. this.canForward = function() {
  10825. return current < history.length - 1;
  10826. };
  10827. /**
  10828. * Go back
  10829. *
  10830. * @return void
  10831. */
  10832. this.back = go;
  10833. /**
  10834. * Go forward
  10835. *
  10836. * @return void
  10837. */
  10838. this.forward = function() {
  10839. return go(true);
  10840. };
  10841. // bind to elfinder events
  10842. fm.bind('init', function() {
  10843. if (nativeHistory && !nativeHistory.state) {
  10844. setNativeHistory(fm.startDir());
  10845. }
  10846. })
  10847. .open(function() {
  10848. var l = history.length,
  10849. cwd = fm.cwd().hash;
  10850. if (update) {
  10851. current >= 0 && l > current + 1 && history.splice(current+1);
  10852. history[history.length-1] != cwd && history.push(cwd);
  10853. current = history.length - 1;
  10854. }
  10855. update = true;
  10856. setNativeHistory(cwd);
  10857. })
  10858. .reload(fm.options.reloadClearHistory && reset);
  10859. };
  10860. /*
  10861. * File: /js/elFinder.command.js
  10862. */
  10863. /**
  10864. * elFinder command prototype
  10865. *
  10866. * @type elFinder.command
  10867. * @author Dmitry (dio) Levashov
  10868. */
  10869. elFinder.prototype.command = function(fm) {
  10870. /**
  10871. * elFinder instance
  10872. *
  10873. * @type elFinder
  10874. */
  10875. this.fm = fm;
  10876. /**
  10877. * Command name, same as class name
  10878. *
  10879. * @type String
  10880. */
  10881. this.name = '';
  10882. /**
  10883. * Dialog class name
  10884. *
  10885. * @type String
  10886. */
  10887. this.dialogClass = '';
  10888. /**
  10889. * Command icon class name with out 'elfinder-button-icon-'
  10890. * Use this.name if it is empty
  10891. *
  10892. * @type String
  10893. */
  10894. this.className = '';
  10895. /**
  10896. * Short command description
  10897. *
  10898. * @type String
  10899. */
  10900. this.title = '';
  10901. /**
  10902. * Linked(Child) commands name
  10903. * They are loaded together when tthis command is loaded.
  10904. *
  10905. * @type Array
  10906. */
  10907. this.linkedCmds = [];
  10908. /**
  10909. * Current command state
  10910. *
  10911. * @example
  10912. * this.state = -1; // command disabled
  10913. * this.state = 0; // command enabled
  10914. * this.state = 1; // command active (for example "fullscreen" command while elfinder in fullscreen mode)
  10915. * @default -1
  10916. * @type Number
  10917. */
  10918. this.state = -1;
  10919. /**
  10920. * If true, command can not be disabled by connector.
  10921. * @see this.update()
  10922. *
  10923. * @type Boolen
  10924. */
  10925. this.alwaysEnabled = false;
  10926. /**
  10927. * Do not change dirctory on removed current work directory
  10928. *
  10929. * @type Boolen
  10930. */
  10931. this.noChangeDirOnRemovedCwd = false;
  10932. /**
  10933. * If true, this means command was disabled by connector.
  10934. * @see this.update()
  10935. *
  10936. * @type Boolen
  10937. */
  10938. this._disabled = false;
  10939. /**
  10940. * If true, this command is disabled on serach results
  10941. *
  10942. * @type Boolean
  10943. */
  10944. this.disableOnSearch = false;
  10945. /**
  10946. * Call update() when event select fired
  10947. *
  10948. * @type Boolean
  10949. */
  10950. this.updateOnSelect = true;
  10951. /**
  10952. * Sync toolbar button title on change
  10953. *
  10954. * @type Boolean
  10955. */
  10956. this.syncTitleOnChange = false;
  10957. /**
  10958. * Keep display of the context menu when command execution
  10959. *
  10960. * @type Boolean
  10961. */
  10962. this.keepContextmenu = false;
  10963. /**
  10964. * elFinder events defaults handlers.
  10965. * Inside handlers "this" is current command object
  10966. *
  10967. * @type Object
  10968. */
  10969. this._handlers = {
  10970. enable : function() { this.update(void(0), this.value); },
  10971. disable : function() { this.update(-1, this.value); },
  10972. 'open reload load sync' : function() {
  10973. this._disabled = !(this.alwaysEnabled || this.fm.isCommandEnabled(this.name));
  10974. this.update(void(0), this.value);
  10975. this.change();
  10976. }
  10977. };
  10978. /**
  10979. * elFinder events handlers.
  10980. * Inside handlers "this" is current command object
  10981. *
  10982. * @type Object
  10983. */
  10984. this.handlers = {};
  10985. /**
  10986. * Shortcuts
  10987. *
  10988. * @type Array
  10989. */
  10990. this.shortcuts = [];
  10991. /**
  10992. * Command options
  10993. *
  10994. * @type Object
  10995. */
  10996. this.options = {ui : 'button'};
  10997. /**
  10998. * Callback functions on `change` event
  10999. *
  11000. * @type Array
  11001. */
  11002. this.listeners = [];
  11003. /**
  11004. * Prepare object -
  11005. * bind events and shortcuts
  11006. *
  11007. * @return void
  11008. */
  11009. this.setup = function(name, opts) {
  11010. var self = this,
  11011. fm = this.fm,
  11012. setCallback = function(s) {
  11013. var cb = s.callback || function(e) {
  11014. fm.exec(self.name, void(0), {
  11015. _userAction: true,
  11016. _currentType: 'shortcut'
  11017. });
  11018. };
  11019. s.callback = function(e) {
  11020. var enabled, checks = {};
  11021. if (self.enabled()) {
  11022. if (fm.searchStatus.state < 2) {
  11023. enabled = fm.isCommandEnabled(self.name);
  11024. } else {
  11025. $.each(fm.selected(), function(i, h) {
  11026. if (fm.optionsByHashes[h]) {
  11027. checks[h] = true;
  11028. } else {
  11029. $.each(fm.volOptions, function(id) {
  11030. if (!checks[id] && h.indexOf(id) === 0) {
  11031. checks[id] = true;
  11032. return false;
  11033. }
  11034. });
  11035. }
  11036. });
  11037. $.each(checks, function(h) {
  11038. enabled = fm.isCommandEnabled(self.name, h);
  11039. if (! enabled) {
  11040. return false;
  11041. }
  11042. });
  11043. }
  11044. if (enabled) {
  11045. self.event = e;
  11046. cb.call(self);
  11047. delete self.event;
  11048. }
  11049. }
  11050. };
  11051. },
  11052. i, s, sc;
  11053. this.name = name;
  11054. this.title = fm.messages['cmd'+name] ? fm.i18n('cmd'+name)
  11055. : ((this.extendsCmd && fm.messages['cmd'+this.extendsCmd]) ? fm.i18n('cmd'+this.extendsCmd) : name);
  11056. this.options = Object.assign({}, this.options, opts);
  11057. this.listeners = [];
  11058. this.dialogClass = 'elfinder-dialog-' + name;
  11059. if (opts.shortcuts) {
  11060. if (typeof opts.shortcuts === 'function') {
  11061. sc = opts.shortcuts(this.fm, this.shortcuts);
  11062. } else if (Array.isArray(opts.shortcuts)) {
  11063. sc = opts.shortcuts;
  11064. }
  11065. this.shortcuts = sc || [];
  11066. }
  11067. if (this.updateOnSelect) {
  11068. this._handlers.select = function() { this.update(void(0), this.value); };
  11069. }
  11070. $.each(Object.assign({}, self._handlers, self.handlers), function(cmd, handler) {
  11071. fm.bind(cmd, $.proxy(handler, self));
  11072. });
  11073. for (i = 0; i < this.shortcuts.length; i++) {
  11074. s = this.shortcuts[i];
  11075. setCallback(s);
  11076. !s.description && (s.description = this.title);
  11077. fm.shortcut(s);
  11078. }
  11079. if (this.disableOnSearch) {
  11080. fm.bind('search searchend', function() {
  11081. self._disabled = this.type === 'search'? true : ! (this.alwaysEnabled || fm.isCommandEnabled(name));
  11082. self.update(void(0), self.value);
  11083. });
  11084. }
  11085. this.init();
  11086. };
  11087. /**
  11088. * Command specific init stuffs
  11089. *
  11090. * @return void
  11091. */
  11092. this.init = function() {};
  11093. /**
  11094. * Exec command
  11095. *
  11096. * @param Array target files hashes
  11097. * @param Array|Object command value
  11098. * @return $.Deferred
  11099. */
  11100. this.exec = function(files, opts) {
  11101. return $.Deferred().reject();
  11102. };
  11103. this.getUndo = function(opts, resData) {
  11104. return false;
  11105. };
  11106. /**
  11107. * Return true if command disabled.
  11108. *
  11109. * @return Boolen
  11110. */
  11111. this.disabled = function() {
  11112. return this.state < 0;
  11113. };
  11114. /**
  11115. * Return true if command enabled.
  11116. *
  11117. * @return Boolen
  11118. */
  11119. this.enabled = function() {
  11120. return this.state > -1;
  11121. };
  11122. /**
  11123. * Return true if command active.
  11124. *
  11125. * @return Boolen
  11126. */
  11127. this.active = function() {
  11128. return this.state > 0;
  11129. };
  11130. /**
  11131. * Return current command state.
  11132. * Must be overloaded in most commands
  11133. *
  11134. * @return Number
  11135. */
  11136. this.getstate = function() {
  11137. return -1;
  11138. };
  11139. /**
  11140. * Update command state/value
  11141. * and rize 'change' event if smth changed
  11142. *
  11143. * @param Number new state or undefined to auto update state
  11144. * @param mixed new value
  11145. * @return void
  11146. */
  11147. this.update = function(s, v) {
  11148. var state = this.state,
  11149. value = this.value;
  11150. if (this._disabled && this.fm.searchStatus === 0) {
  11151. this.state = -1;
  11152. } else {
  11153. this.state = s !== void(0) ? s : this.getstate();
  11154. }
  11155. this.value = v;
  11156. if (state != this.state || value != this.value) {
  11157. this.change();
  11158. }
  11159. };
  11160. /**
  11161. * Bind handler / fire 'change' event.
  11162. *
  11163. * @param Function|undefined event callback
  11164. * @return void
  11165. */
  11166. this.change = function(c) {
  11167. var cmd, i;
  11168. if (typeof(c) === 'function') {
  11169. this.listeners.push(c);
  11170. } else {
  11171. for (i = 0; i < this.listeners.length; i++) {
  11172. cmd = this.listeners[i];
  11173. try {
  11174. cmd(this.state, this.value);
  11175. } catch (e) {
  11176. this.fm.debug('error', e);
  11177. }
  11178. }
  11179. }
  11180. return this;
  11181. };
  11182. /**
  11183. * With argument check given files hashes and return list of existed files hashes.
  11184. * Without argument return selected files hashes.
  11185. *
  11186. * @param Array|String|void hashes
  11187. * @return Array
  11188. */
  11189. this.hashes = function(hashes) {
  11190. return hashes
  11191. ? $.grep(Array.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) ? true : false; })
  11192. : fm.selected();
  11193. };
  11194. /**
  11195. * Return only existed files from given fils hashes | selected files
  11196. *
  11197. * @param Array|String|void hashes
  11198. * @return Array
  11199. */
  11200. this.files = function(hashes) {
  11201. var fm = this.fm;
  11202. return hashes
  11203. ? $.map(Array.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) || null; })
  11204. : fm.selectedFiles();
  11205. };
  11206. /**
  11207. * Wrapper to fm.dialog()
  11208. *
  11209. * @param String|DOMElement content
  11210. * @param Object options
  11211. * @return Object jQuery element object
  11212. */
  11213. this.fmDialog = function(content, options) {
  11214. if (options.cssClass) {
  11215. options.cssClass += ' ' + this.dialogClass;
  11216. } else {
  11217. options.cssClass = this.dialogClass;
  11218. }
  11219. return this.fm.dialog(content, options);
  11220. };
  11221. };
  11222. /*
  11223. * File: /js/elFinder.resources.js
  11224. */
  11225. /**
  11226. * elFinder resources registry.
  11227. * Store shared data
  11228. *
  11229. * @type Object
  11230. * @author Dmitry (dio) Levashov
  11231. **/
  11232. elFinder.prototype.resources = {
  11233. 'class' : {
  11234. hover : 'ui-state-hover',
  11235. active : 'ui-state-active',
  11236. disabled : 'ui-state-disabled',
  11237. draggable : 'ui-draggable',
  11238. droppable : 'ui-droppable',
  11239. adroppable : 'elfinder-droppable-active',
  11240. cwdfile : 'elfinder-cwd-file',
  11241. cwd : 'elfinder-cwd',
  11242. tree : 'elfinder-tree',
  11243. treeroot : 'elfinder-navbar-root',
  11244. navdir : 'elfinder-navbar-dir',
  11245. navdirwrap : 'elfinder-navbar-dir-wrapper',
  11246. navarrow : 'elfinder-navbar-arrow',
  11247. navsubtree : 'elfinder-navbar-subtree',
  11248. navcollapse : 'elfinder-navbar-collapsed',
  11249. navexpand : 'elfinder-navbar-expanded',
  11250. treedir : 'elfinder-tree-dir',
  11251. placedir : 'elfinder-place-dir',
  11252. searchbtn : 'elfinder-button-search',
  11253. editing : 'elfinder-to-editing',
  11254. preventback : 'elfinder-prevent-back',
  11255. tabstab : 'ui-state-default ui-tabs-tab ui-corner-top ui-tab',
  11256. tabsactive : 'ui-tabs-active ui-state-active'
  11257. },
  11258. tpl : {
  11259. perms : '<span class="elfinder-perms"/>',
  11260. lock : '<span class="elfinder-lock"/>',
  11261. symlink : '<span class="elfinder-symlink"/>',
  11262. navicon : '<span class="elfinder-nav-icon"/>',
  11263. navspinner : '<span class="elfinder-spinner elfinder-navbar-spinner"/>',
  11264. navdir : '<div class="elfinder-navbar-wrapper{root}"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}"><span class="elfinder-navbar-arrow"/><span class="elfinder-navbar-icon" {style}/>{symlink}{permissions}{name}</span><div class="elfinder-navbar-subtree" style="display:none"/></div>',
  11265. placedir : '<div class="elfinder-navbar-wrapper"><span id="{id}" class="ui-corner-all elfinder-navbar-dir {cssclass}" title="{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>'
  11266. },
  11267. // mimes.text will be overwritten with connector config if `textMimes` is included in initial response
  11268. // @see php/elFInder.class.php `public static $textMimes`
  11269. mimes : {
  11270. text : [
  11271. 'application/dash+xml',
  11272. 'application/docbook+xml',
  11273. 'application/javascript',
  11274. 'application/json',
  11275. 'application/plt',
  11276. 'application/sat',
  11277. 'application/sql',
  11278. 'application/step',
  11279. 'application/vnd.hp-hpgl',
  11280. 'application/x-awk',
  11281. 'application/x-config',
  11282. 'application/x-csh',
  11283. 'application/x-empty',
  11284. 'application/x-mpegurl',
  11285. 'application/x-perl',
  11286. 'application/x-php',
  11287. 'application/x-web-config',
  11288. 'application/xhtml+xml',
  11289. 'application/xml',
  11290. 'audio/x-mp3-playlist',
  11291. 'image/cgm',
  11292. 'image/svg+xml',
  11293. 'image/vnd.dxf',
  11294. 'model/iges'
  11295. ]
  11296. },
  11297. mixin : {
  11298. make : function() {
  11299. var self = this,
  11300. fm = this.fm,
  11301. cmd = this.name,
  11302. req = this.requestCmd || cmd,
  11303. wz = fm.getUI('workzone'),
  11304. org = (this.origin && this.origin === 'navbar')? 'tree' : 'cwd',
  11305. ui = fm.getUI(org),
  11306. tree = (org === 'tree'),
  11307. find = tree? 'navHash2Id' : 'cwdHash2Id',
  11308. tarea= (! tree && fm.storage('view') != 'list'),
  11309. sel = fm.selected(),
  11310. move = this.move || false,
  11311. empty= wz.hasClass('elfinder-cwd-wrapper-empty'),
  11312. unselect = function() {
  11313. requestAnimationFrame(function() {
  11314. input && input.trigger('blur');
  11315. });
  11316. },
  11317. rest = function(){
  11318. if (!overlay.is(':hidden')) {
  11319. overlay.elfinderoverlay('hide').off('click close', cancel);
  11320. }
  11321. pnode.removeClass('ui-front')
  11322. .css('position', '')
  11323. .off('unselect.'+fm.namespace, unselect);
  11324. if (tarea) {
  11325. nnode && nnode.css('max-height', '');
  11326. } else if (!tree) {
  11327. pnode.css('width', '')
  11328. .parent('td').css('overflow', '');
  11329. }
  11330. }, colwidth,
  11331. dfrd = $.Deferred()
  11332. .fail(function(error) {
  11333. dstCls && dst.attr('class', dstCls);
  11334. empty && wz.addClass('elfinder-cwd-wrapper-empty');
  11335. if (sel) {
  11336. move && fm.trigger('unlockfiles', {files: sel});
  11337. fm.clipboard([]);
  11338. fm.trigger('selectfiles', { files: sel });
  11339. }
  11340. error && fm.error(error);
  11341. })
  11342. .always(function() {
  11343. rest();
  11344. cleanup();
  11345. fm.enable().unbind('open', openCallback).trigger('resMixinMake');
  11346. }),
  11347. id = 'tmp_'+parseInt(Math.random()*100000),
  11348. phash = this.data && this.data.target? this.data.target : (tree? fm.file(sel[0]).hash : fm.cwd().hash),
  11349. date = new Date(),
  11350. file = {
  11351. hash : id,
  11352. phash : phash,
  11353. name : fm.uniqueName(this.prefix, phash),
  11354. mime : this.mime,
  11355. read : true,
  11356. write : true,
  11357. date : 'Today '+date.getHours()+':'+date.getMinutes(),
  11358. move : move
  11359. },
  11360. data = this.data || {},
  11361. node = ui.trigger('create.'+fm.namespace, file).find('#'+fm[find](id)),
  11362. nnode, pnode,
  11363. overlay = fm.getUI('overlay'),
  11364. cleanup = function() {
  11365. if (node && node.length) {
  11366. input.off();
  11367. node.hide();
  11368. fm.unselectfiles({files : [id]}).unbind('resize', resize);
  11369. requestAnimationFrame(function() {
  11370. if (tree) {
  11371. node.closest('.elfinder-navbar-wrapper').remove();
  11372. } else {
  11373. node.remove();
  11374. }
  11375. });
  11376. }
  11377. },
  11378. cancel = function(e) {
  11379. if (!overlay.is(':hidden')) {
  11380. pnode.css('z-index', '');
  11381. }
  11382. if (! inError) {
  11383. cleanup();
  11384. dfrd.reject();
  11385. if (e) {
  11386. e.stopPropagation();
  11387. e.preventDefault();
  11388. }
  11389. }
  11390. },
  11391. input = $(tarea? '<textarea/>' : '<input type="text"/>')
  11392. .on('keyup text', function(){
  11393. if (tarea) {
  11394. this.style.height = '1px';
  11395. this.style.height = this.scrollHeight + 'px';
  11396. } else if (colwidth) {
  11397. this.style.width = colwidth + 'px';
  11398. if (this.scrollWidth > colwidth) {
  11399. this.style.width = this.scrollWidth + 10 + 'px';
  11400. }
  11401. }
  11402. })
  11403. .on('keydown', function(e) {
  11404. e.stopImmediatePropagation();
  11405. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  11406. dfrd.reject();
  11407. } else if (e.keyCode == $.ui.keyCode.ENTER) {
  11408. input.trigger('blur');
  11409. }
  11410. })
  11411. .on('mousedown click dblclick', function(e) {
  11412. e.stopPropagation();
  11413. if (e.type === 'dblclick') {
  11414. e.preventDefault();
  11415. }
  11416. })
  11417. .on('blur', function() {
  11418. var name = $.trim(input.val()),
  11419. parent = input.parent(),
  11420. valid = true,
  11421. cut;
  11422. if (!overlay.is(':hidden')) {
  11423. pnode.css('z-index', '');
  11424. }
  11425. if (name === '') {
  11426. return cancel();
  11427. }
  11428. if (!inError && parent.length) {
  11429. if (fm.options.validName && fm.options.validName.test) {
  11430. try {
  11431. valid = fm.options.validName.test(name);
  11432. } catch(e) {
  11433. valid = false;
  11434. }
  11435. }
  11436. if (!name || name === '.' || name === '..' || !valid) {
  11437. inError = true;
  11438. fm.error(file.mime === 'directory'? 'errInvDirname' : 'errInvName', {modal: true, close: function(){setTimeout(select, 120);}});
  11439. return false;
  11440. }
  11441. if (fm.fileByName(name, phash)) {
  11442. inError = true;
  11443. fm.error(['errExists', name], {modal: true, close: function(){setTimeout(select, 120);}});
  11444. return false;
  11445. }
  11446. cut = (sel && move)? fm.exec('cut', sel) : null;
  11447. $.when(cut)
  11448. .done(function() {
  11449. var toast = {},
  11450. nextAct = {};
  11451. rest();
  11452. input.hide().before($('<span>').text(name));
  11453. fm.lockfiles({files : [id]});
  11454. fm.request({
  11455. data : Object.assign({cmd : req, name : name, target : phash}, data || {}),
  11456. notify : {type : req, cnt : 1},
  11457. preventFail : true,
  11458. syncOnFail : true,
  11459. navigate : {toast : toast},
  11460. })
  11461. .fail(function(error) {
  11462. fm.unlockfiles({files : [id]});
  11463. inError = true;
  11464. input.show().prev().remove();
  11465. fm.error(error, {
  11466. modal: true,
  11467. close: function() {
  11468. if (Array.isArray(error) && $.inArray('errUploadMime', error) !== -1) {
  11469. dfrd.notify('errUploadMime').reject();
  11470. } else {
  11471. setTimeout(select, 120);
  11472. }
  11473. }
  11474. });
  11475. })
  11476. .done(function(data) {
  11477. if (data && data.added && data.added[0]) {
  11478. var item = data.added[0],
  11479. dirhash = item.hash,
  11480. newItem = ui.find('#'+fm[find](dirhash)),
  11481. acts = {
  11482. 'directory' : { cmd: 'open', msg: 'cmdopendir' },
  11483. 'text' : { cmd: 'edit', msg: 'cmdedit' },
  11484. 'default' : { cmd: 'open', msg: 'cmdopen' }
  11485. },
  11486. tmpMimes;
  11487. if (sel && move) {
  11488. fm.one(req+'done', function() {
  11489. fm.exec('paste', dirhash);
  11490. });
  11491. }
  11492. if (!move) {
  11493. if (fm.mimeIsText(item.mime) && !fm.mimesCanMakeEmpty[item.mime] && fm.mimeTypes[item.mime]) {
  11494. fm.trigger('canMakeEmptyFile', {mimes: [item.mime], unshift: true});
  11495. tmpMimes = {};
  11496. tmpMimes[item.mime] = fm.mimeTypes[item.mime];
  11497. fm.storage('mkfileTextMimes', Object.assign(tmpMimes, fm.storage('mkfileTextMimes') || {}));
  11498. }
  11499. 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']);
  11500. Object.assign(toast, nextAct.cmd ? {
  11501. incwd : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)]), action: nextAct},
  11502. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)]), action: nextAct}
  11503. } : {
  11504. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)])}
  11505. });
  11506. }
  11507. }
  11508. dfrd.resolve(data);
  11509. });
  11510. })
  11511. .fail(function() {
  11512. dfrd.reject();
  11513. });
  11514. }
  11515. })
  11516. .on('dragenter dragleave dragover drop', function(e) {
  11517. // stop bubbling to prevent upload with native drop event
  11518. e.stopPropagation();
  11519. }),
  11520. select = function() {
  11521. var name = fm.splitFileExtention(input.val())[0];
  11522. if (!inError && fm.UA.Mobile && !fm.UA.iOS) { // since iOS has a bug? (z-index not effect) so disable it
  11523. overlay.on('click close', cancel).elfinderoverlay('show');
  11524. pnode.css('z-index', overlay.css('z-index') + 1);
  11525. }
  11526. inError = false;
  11527. ! fm.enabled() && fm.enable();
  11528. input.trigger('focus').trigger('select');
  11529. input[0].setSelectionRange && input[0].setSelectionRange(0, name.length);
  11530. },
  11531. resize = function() {
  11532. node.trigger('scrolltoview', {blink : false});
  11533. },
  11534. openCallback = function() {
  11535. dfrd && (dfrd.state() === 'pending') && dfrd.reject();
  11536. },
  11537. inError = false,
  11538. nextAction,
  11539. // for tree
  11540. dst, dstCls, collapsed, expanded, arrow, subtree;
  11541. if (!fm.isCommandEnabled(req, phash) || !node.length) {
  11542. return dfrd.reject();
  11543. }
  11544. if ($.isPlainObject(self.nextAction)){
  11545. nextAction = Object.assign({}, self.nextAction);
  11546. }
  11547. if (tree) {
  11548. dst = $('#'+fm[find](phash));
  11549. collapsed = fm.res('class', 'navcollapse');
  11550. expanded = fm.res('class', 'navexpand');
  11551. arrow = fm.res('class', 'navarrow');
  11552. subtree = fm.res('class', 'navsubtree');
  11553. node.closest('.'+subtree).show();
  11554. if (! dst.hasClass(collapsed)) {
  11555. dstCls = dst.attr('class');
  11556. dst.addClass(collapsed+' '+expanded+' elfinder-subtree-loaded');
  11557. }
  11558. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  11559. dst.children('.'+arrow).trigger('click').data('dfrd').done(function() {
  11560. if (input.val() === file.name) {
  11561. input.val(fm.uniqueName(self.prefix, phash)).trigger('select').trigger('focus');
  11562. }
  11563. });
  11564. }
  11565. nnode = node.contents().filter(function(){ return this.nodeType==3 && $(this).parent().attr('id') === fm.navHash2Id(file.hash); });
  11566. pnode = nnode.parent();
  11567. nnode.replaceWith(input.val(file.name));
  11568. } else {
  11569. empty && wz.removeClass('elfinder-cwd-wrapper-empty');
  11570. nnode = node.find('.elfinder-cwd-filename');
  11571. pnode = nnode.parent();
  11572. if (tarea) {
  11573. nnode.css('max-height', 'none');
  11574. } else {
  11575. colwidth = pnode.width();
  11576. pnode.width(colwidth - 15)
  11577. .parent('td').css('overflow', 'visible');
  11578. }
  11579. nnode.empty().append(input.val(file.name));
  11580. }
  11581. pnode.addClass('ui-front')
  11582. .css('position', 'relative')
  11583. .on('unselect.'+fm.namespace, unselect);
  11584. fm.bind('resize', resize).one('open', openCallback);
  11585. input.trigger('keyup');
  11586. select();
  11587. return dfrd;
  11588. }
  11589. },
  11590. blink: function(elm, mode) {
  11591. var acts = {
  11592. slowonce : function(){elm.hide().delay(250).fadeIn(750).delay(500).fadeOut(3500);},
  11593. lookme : function(){elm.show().fadeOut(500).fadeIn(750);}
  11594. }, func;
  11595. mode = mode || 'slowonce';
  11596. func = acts[mode] || acts['lookme'];
  11597. elm.stop(true, true);
  11598. func();
  11599. }
  11600. };
  11601. /*
  11602. * File: /js/jquery.dialogelfinder.js
  11603. */
  11604. /**
  11605. * @class dialogelfinder - open elFinder in dialog window
  11606. *
  11607. * @param Object elFinder options with dialog options
  11608. * @example
  11609. * $(selector).dialogelfinder({
  11610. * // some elfinder options
  11611. * title : 'My files', // dialog title, default = "Files"
  11612. * width : 850, // dialog width, default 840
  11613. * autoOpen : false, // if false - dialog will not be opened after init, default = true
  11614. * destroyOnClose : true // destroy elFinder on close dialog, default = false
  11615. * })
  11616. * @author Dmitry (dio) Levashov
  11617. **/
  11618. $.fn.dialogelfinder = function(opts) {
  11619. var position = 'elfinderPosition',
  11620. destroy = 'elfinderDestroyOnClose',
  11621. node;
  11622. this.not('.elfinder').each(function() {
  11623. var doc = $(document),
  11624. toolbar = $('<div class="ui-widget-header dialogelfinder-drag ui-corner-top">'+(opts.title || 'Files')+'</div>'),
  11625. button = $('<a href="#" class="dialogelfinder-drag-close ui-corner-all"><span class="ui-icon ui-icon-closethick"> </span></a>')
  11626. .appendTo(toolbar)
  11627. .on('click', function(e) {
  11628. e.preventDefault();
  11629. node.dialogelfinder('close');
  11630. }),
  11631. node = $(this).addClass('dialogelfinder')
  11632. .css('position', 'absolute')
  11633. .hide()
  11634. .appendTo('body')
  11635. .draggable({
  11636. handle : '.dialogelfinder-drag',
  11637. containment : 'window',
  11638. stop : function() {
  11639. node.trigger('resize');
  11640. elfinder.trigger('resize');
  11641. }
  11642. })
  11643. .elfinder(opts)
  11644. .prepend(toolbar),
  11645. elfinder = node.elfinder('instance');
  11646. node.width(parseInt(node.width()) || 840) // fix width if set to "auto"
  11647. .data(destroy, !!opts.destroyOnClose)
  11648. .find('.elfinder-toolbar').removeClass('ui-corner-top');
  11649. opts.position && node.data(position, opts.position);
  11650. opts.autoOpen !== false && $(this).dialogelfinder('open');
  11651. });
  11652. if (opts == 'open') {
  11653. var node = $(this),
  11654. pos = node.data(position) || {
  11655. top : parseInt($(document).scrollTop() + ($(window).height() < node.height() ? 2 : ($(window).height() - node.height())/2)),
  11656. left : parseInt($(document).scrollLeft() + ($(window).width() < node.width() ? 2 : ($(window).width() - node.width())/2))
  11657. };
  11658. if (node.is(':hidden')) {
  11659. node.addClass('ui-front').css(pos).show().trigger('resize');
  11660. setTimeout(function() {
  11661. // fix resize icon position and make elfinder active
  11662. node.trigger('resize').trigger('mousedown');
  11663. }, 200);
  11664. }
  11665. } else if (opts == 'close') {
  11666. node = $(this).removeClass('ui-front');
  11667. if (node.is(':visible')) {
  11668. !!node.data(destroy)
  11669. ? node.elfinder('destroy').remove()
  11670. : node.elfinder('close');
  11671. }
  11672. } else if (opts == 'instance') {
  11673. return $(this).getElFinder();
  11674. }
  11675. return this;
  11676. };
  11677. /*
  11678. * File: /js/i18n/elfinder.en.js
  11679. */
  11680. /**
  11681. * English translation
  11682. * @author Troex Nevelin <troex@fury.scancode.ru>
  11683. * @author Naoki Sawada <hypweb+elfinder@gmail.com>
  11684. * @version 2018-10-19
  11685. */
  11686. // elfinder.en.js is integrated into elfinder.(full|min).js by jake build
  11687. if (typeof elFinder === 'function' && elFinder.prototype.i18) {
  11688. elFinder.prototype.i18.en = {
  11689. translator : 'Troex Nevelin &lt;troex@fury.scancode.ru&gt;, Naoki Sawada &lt;hypweb+elfinder@gmail.com&gt;',
  11690. language : 'English',
  11691. direction : 'ltr',
  11692. dateFormat : 'M d, Y h:i A', // will show like: Aug 24, 2018 04:39 PM
  11693. fancyDateFormat : '$1 h:i A', // will show like: Today 04:39 PM
  11694. nonameDateFormat : 'ymd-His', // noname upload will show like: 180824-163916
  11695. messages : {
  11696. /********************************** errors **********************************/
  11697. 'error' : 'Error',
  11698. 'errUnknown' : 'Unknown error.',
  11699. 'errUnknownCmd' : 'Unknown command.',
  11700. 'errJqui' : 'Invalid jQuery UI configuration. Selectable, draggable and droppable components must be included.',
  11701. 'errNode' : 'elFinder requires DOM Element to be created.',
  11702. 'errURL' : 'Invalid elFinder configuration! URL option is not set.',
  11703. 'errAccess' : 'Access denied.',
  11704. 'errConnect' : 'Unable to connect to backend.',
  11705. 'errAbort' : 'Connection aborted.',
  11706. 'errTimeout' : 'Connection timeout.',
  11707. 'errNotFound' : 'Backend not found.',
  11708. 'errResponse' : 'Invalid backend response.',
  11709. 'errConf' : 'Invalid backend configuration.',
  11710. 'errJSON' : 'PHP JSON module not installed.',
  11711. 'errNoVolumes' : 'Readable volumes not available.',
  11712. 'errCmdParams' : 'Invalid parameters for command "$1".',
  11713. 'errDataNotJSON' : 'Data is not JSON.',
  11714. 'errDataEmpty' : 'Data is empty.',
  11715. 'errCmdReq' : 'Backend request requires command name.',
  11716. 'errOpen' : 'Unable to open "$1".',
  11717. 'errNotFolder' : 'Object is not a folder.',
  11718. 'errNotFile' : 'Object is not a file.',
  11719. 'errRead' : 'Unable to read "$1".',
  11720. 'errWrite' : 'Unable to write into "$1".',
  11721. 'errPerm' : 'Permission denied.',
  11722. 'errLocked' : '"$1" is locked and can not be renamed, moved or removed.',
  11723. 'errExists' : 'Item named "$1" already exists.',
  11724. 'errInvName' : 'Invalid file name.',
  11725. 'errInvDirname' : 'Invalid folder name.', // from v2.1.24 added 12.4.2017
  11726. 'errFolderNotFound' : 'Folder not found.',
  11727. 'errFileNotFound' : 'File not found.',
  11728. 'errTrgFolderNotFound' : 'Target folder "$1" not found.',
  11729. 'errPopup' : 'Browser prevented opening popup window. To open file enable it in browser options.',
  11730. 'errMkdir' : 'Unable to create folder "$1".',
  11731. 'errMkfile' : 'Unable to create file "$1".',
  11732. 'errRename' : 'Unable to rename "$1".',
  11733. 'errCopyFrom' : 'Copying files from volume "$1" not allowed.',
  11734. 'errCopyTo' : 'Copying files to volume "$1" not allowed.',
  11735. 'errMkOutLink' : 'Unable to create a link to outside the volume root.', // from v2.1 added 03.10.2015
  11736. 'errUpload' : 'Upload error.', // old name - errUploadCommon
  11737. 'errUploadFile' : 'Unable to upload "$1".', // old name - errUpload
  11738. 'errUploadNoFiles' : 'No files found for upload.',
  11739. 'errUploadTotalSize' : 'Data exceeds the maximum allowed size.', // old name - errMaxSize
  11740. 'errUploadFileSize' : 'File exceeds maximum allowed size.', // old name - errFileMaxSize
  11741. 'errUploadMime' : 'File type not allowed.',
  11742. 'errUploadTransfer' : '"$1" transfer error.',
  11743. 'errUploadTemp' : 'Unable to make temporary file for upload.', // from v2.1 added 26.09.2015
  11744. 'errNotReplace' : 'Object "$1" already exists at this location and can not be replaced by object with another type.', // new
  11745. 'errReplace' : 'Unable to replace "$1".',
  11746. 'errSave' : 'Unable to save "$1".',
  11747. 'errCopy' : 'Unable to copy "$1".',
  11748. 'errMove' : 'Unable to move "$1".',
  11749. 'errCopyInItself' : 'Unable to copy "$1" into itself.',
  11750. 'errRm' : 'Unable to remove "$1".',
  11751. 'errTrash' : 'Unable into trash.', // from v2.1.24 added 30.4.2017
  11752. 'errRmSrc' : 'Unable remove source file(s).',
  11753. 'errExtract' : 'Unable to extract files from "$1".',
  11754. 'errArchive' : 'Unable to create archive.',
  11755. 'errArcType' : 'Unsupported archive type.',
  11756. 'errNoArchive' : 'File is not archive or has unsupported archive type.',
  11757. 'errCmdNoSupport' : 'Backend does not support this command.',
  11758. 'errReplByChild' : 'The folder "$1" can\'t be replaced by an item it contains.',
  11759. 'errArcSymlinks' : 'For security reason denied to unpack archives contains symlinks or files with not allowed names.', // edited 24.06.2012
  11760. 'errArcMaxSize' : 'Archive files exceeds maximum allowed size.',
  11761. 'errResize' : 'Unable to resize "$1".',
  11762. 'errResizeDegree' : 'Invalid rotate degree.', // added 7.3.2013
  11763. 'errResizeRotate' : 'Unable to rotate image.', // added 7.3.2013
  11764. 'errResizeSize' : 'Invalid image size.', // added 7.3.2013
  11765. 'errResizeNoChange' : 'Image size not changed.', // added 7.3.2013
  11766. 'errUsupportType' : 'Unsupported file type.',
  11767. 'errNotUTF8Content' : 'File "$1" is not in UTF-8 and cannot be edited.', // added 9.11.2011
  11768. 'errNetMount' : 'Unable to mount "$1".', // added 17.04.2012
  11769. 'errNetMountNoDriver' : 'Unsupported protocol.', // added 17.04.2012
  11770. 'errNetMountFailed' : 'Mount failed.', // added 17.04.2012
  11771. 'errNetMountHostReq' : 'Host required.', // added 18.04.2012
  11772. 'errSessionExpires' : 'Your session has expired due to inactivity.',
  11773. 'errCreatingTempDir' : 'Unable to create temporary directory: "$1"',
  11774. 'errFtpDownloadFile' : 'Unable to download file from FTP: "$1"',
  11775. 'errFtpUploadFile' : 'Unable to upload file to FTP: "$1"',
  11776. 'errFtpMkdir' : 'Unable to create remote directory on FTP: "$1"',
  11777. 'errArchiveExec' : 'Error while archiving files: "$1"',
  11778. 'errExtractExec' : 'Error while extracting files: "$1"',
  11779. 'errNetUnMount' : 'Unable to unmount.', // from v2.1 added 30.04.2012
  11780. 'errConvUTF8' : 'Not convertible to UTF-8', // from v2.1 added 08.04.2014
  11781. 'errFolderUpload' : 'Try the modern browser, If you\'d like to upload the folder.', // from v2.1 added 26.6.2015
  11782. 'errSearchTimeout' : 'Timed out while searching "$1". Search result is partial.', // from v2.1 added 12.1.2016
  11783. 'errReauthRequire' : 'Re-authorization is required.', // from v2.1.10 added 24.3.2016
  11784. 'errMaxTargets' : 'Max number of selectable items is $1.', // from v2.1.17 added 17.10.2016
  11785. 'errRestore' : 'Unable to restore from the trash. Can\'t identify the restore destination.', // from v2.1.24 added 3.5.2017
  11786. 'errEditorNotFound' : 'Editor not found to this file type.', // from v2.1.25 added 23.5.2017
  11787. 'errServerError' : 'Error occurred on the server side.', // from v2.1.25 added 16.6.2017
  11788. 'errEmpty' : 'Unable to empty folder "$1".', // from v2.1.25 added 22.6.2017
  11789. /******************************* commands names ********************************/
  11790. 'cmdarchive' : 'Create archive',
  11791. 'cmdback' : 'Back',
  11792. 'cmdcopy' : 'Copy',
  11793. 'cmdcut' : 'Cut',
  11794. 'cmddownload' : 'Download',
  11795. 'cmdduplicate' : 'Duplicate',
  11796. 'cmdedit' : 'Edit file',
  11797. 'cmdextract' : 'Extract files from archive',
  11798. 'cmdforward' : 'Forward',
  11799. 'cmdgetfile' : 'Select files',
  11800. 'cmdhelp' : 'About this software',
  11801. 'cmdhome' : 'Root',
  11802. 'cmdinfo' : 'Get info',
  11803. 'cmdmkdir' : 'New folder',
  11804. 'cmdmkdirin' : 'Into New Folder', // from v2.1.7 added 19.2.2016
  11805. 'cmdmkfile' : 'New file',
  11806. 'cmdopen' : 'Open',
  11807. 'cmdpaste' : 'Paste',
  11808. 'cmdquicklook' : 'Preview',
  11809. 'cmdreload' : 'Reload',
  11810. 'cmdrename' : 'Rename',
  11811. 'cmdrm' : 'Delete',
  11812. 'cmdtrash' : 'Into trash', //from v2.1.24 added 29.4.2017
  11813. 'cmdrestore' : 'Restore', //from v2.1.24 added 3.5.2017
  11814. 'cmdsearch' : 'Find files',
  11815. 'cmdup' : 'Go to parent folder',
  11816. 'cmdupload' : 'Upload files',
  11817. 'cmdview' : 'View',
  11818. 'cmdresize' : 'Resize & Rotate',
  11819. 'cmdsort' : 'Sort',
  11820. 'cmdnetmount' : 'Mount network volume', // added 18.04.2012
  11821. 'cmdnetunmount': 'Unmount', // from v2.1 added 30.04.2012
  11822. 'cmdplaces' : 'To Places', // added 28.12.2014
  11823. 'cmdchmod' : 'Change mode', // from v2.1 added 20.6.2015
  11824. 'cmdopendir' : 'Open a folder', // from v2.1 added 13.1.2016
  11825. 'cmdcolwidth' : 'Reset column width', // from v2.1.13 added 12.06.2016
  11826. 'cmdfullscreen': 'Full Screen', // from v2.1.15 added 03.08.2016
  11827. 'cmdmove' : 'Move', // from v2.1.15 added 21.08.2016
  11828. 'cmdempty' : 'Empty the folder', // from v2.1.25 added 22.06.2017
  11829. 'cmdundo' : 'Undo', // from v2.1.27 added 31.07.2017
  11830. 'cmdredo' : 'Redo', // from v2.1.27 added 31.07.2017
  11831. 'cmdpreference': 'Preferences', // from v2.1.27 added 03.08.2017
  11832. 'cmdselectall' : 'Select all', // from v2.1.28 added 15.08.2017
  11833. 'cmdselectnone': 'Select none', // from v2.1.28 added 15.08.2017
  11834. 'cmdselectinvert': 'Invert selection', // from v2.1.28 added 15.08.2017
  11835. 'cmdopennew' : 'Open in new window', // from v2.1.38 added 3.4.2018
  11836. 'cmdhide' : 'Hide (Preference)', // from v2.1.41 added 24.7.2018
  11837. /*********************************** buttons ***********************************/
  11838. 'btnClose' : 'Close',
  11839. 'btnSave' : 'Save',
  11840. 'btnRm' : 'Remove',
  11841. 'btnApply' : 'Apply',
  11842. 'btnCancel' : 'Cancel',
  11843. 'btnNo' : 'No',
  11844. 'btnYes' : 'Yes',
  11845. 'btnMount' : 'Mount', // added 18.04.2012
  11846. 'btnApprove': 'Goto $1 & approve', // from v2.1 added 26.04.2012
  11847. 'btnUnmount': 'Unmount', // from v2.1 added 30.04.2012
  11848. 'btnConv' : 'Convert', // from v2.1 added 08.04.2014
  11849. 'btnCwd' : 'Here', // from v2.1 added 22.5.2015
  11850. 'btnVolume' : 'Volume', // from v2.1 added 22.5.2015
  11851. 'btnAll' : 'All', // from v2.1 added 22.5.2015
  11852. 'btnMime' : 'MIME Type', // from v2.1 added 22.5.2015
  11853. 'btnFileName':'Filename', // from v2.1 added 22.5.2015
  11854. 'btnSaveClose': 'Save & Close', // from v2.1 added 12.6.2015
  11855. 'btnBackup' : 'Backup', // fromv2.1 added 28.11.2015
  11856. 'btnRename' : 'Rename', // from v2.1.24 added 6.4.2017
  11857. 'btnRenameAll' : 'Rename(All)', // from v2.1.24 added 6.4.2017
  11858. 'btnPrevious' : 'Prev ($1/$2)', // from v2.1.24 added 11.5.2017
  11859. 'btnNext' : 'Next ($1/$2)', // from v2.1.24 added 11.5.2017
  11860. 'btnSaveAs' : 'Save As', // from v2.1.25 added 24.5.2017
  11861. /******************************** notifications ********************************/
  11862. 'ntfopen' : 'Open folder',
  11863. 'ntffile' : 'Open file',
  11864. 'ntfreload' : 'Reload folder content',
  11865. 'ntfmkdir' : 'Creating folder',
  11866. 'ntfmkfile' : 'Creating files',
  11867. 'ntfrm' : 'Delete items',
  11868. 'ntfcopy' : 'Copy items',
  11869. 'ntfmove' : 'Move items',
  11870. 'ntfprepare' : 'Checking existing items',
  11871. 'ntfrename' : 'Rename files',
  11872. 'ntfupload' : 'Uploading files',
  11873. 'ntfdownload' : 'Downloading files',
  11874. 'ntfsave' : 'Save files',
  11875. 'ntfarchive' : 'Creating archive',
  11876. 'ntfextract' : 'Extracting files from archive',
  11877. 'ntfsearch' : 'Searching files',
  11878. 'ntfresize' : 'Resizing images',
  11879. 'ntfsmth' : 'Doing something',
  11880. 'ntfloadimg' : 'Loading image',
  11881. 'ntfnetmount' : 'Mounting network volume', // added 18.04.2012
  11882. 'ntfnetunmount': 'Unmounting network volume', // from v2.1 added 30.04.2012
  11883. 'ntfdim' : 'Acquiring image dimension', // added 20.05.2013
  11884. 'ntfreaddir' : 'Reading folder infomation', // from v2.1 added 01.07.2013
  11885. 'ntfurl' : 'Getting URL of link', // from v2.1 added 11.03.2014
  11886. 'ntfchmod' : 'Changing file mode', // from v2.1 added 20.6.2015
  11887. 'ntfpreupload': 'Verifying upload file name', // from v2.1 added 31.11.2015
  11888. 'ntfzipdl' : 'Creating a file for download', // from v2.1.7 added 23.1.2016
  11889. 'ntfparents' : 'Getting path infomation', // from v2.1.17 added 2.11.2016
  11890. 'ntfchunkmerge': 'Processing the uploaded file', // from v2.1.17 added 2.11.2016
  11891. 'ntftrash' : 'Doing throw in the trash', // from v2.1.24 added 2.5.2017
  11892. 'ntfrestore' : 'Doing restore from the trash', // from v2.1.24 added 3.5.2017
  11893. 'ntfchkdir' : 'Checking destination folder', // from v2.1.24 added 3.5.2017
  11894. 'ntfundo' : 'Undoing previous operation', // from v2.1.27 added 31.07.2017
  11895. 'ntfredo' : 'Redoing previous undone', // from v2.1.27 added 31.07.2017
  11896. 'ntfchkcontent' : 'Checking contents', // from v2.1.41 added 3.8.2018
  11897. /*********************************** volumes *********************************/
  11898. 'volume_Trash' : 'Trash', //from v2.1.24 added 29.4.2017
  11899. /************************************ dates **********************************/
  11900. 'dateUnknown' : 'unknown',
  11901. 'Today' : 'Today',
  11902. 'Yesterday' : 'Yesterday',
  11903. 'msJan' : 'Jan',
  11904. 'msFeb' : 'Feb',
  11905. 'msMar' : 'Mar',
  11906. 'msApr' : 'Apr',
  11907. 'msMay' : 'May',
  11908. 'msJun' : 'Jun',
  11909. 'msJul' : 'Jul',
  11910. 'msAug' : 'Aug',
  11911. 'msSep' : 'Sep',
  11912. 'msOct' : 'Oct',
  11913. 'msNov' : 'Nov',
  11914. 'msDec' : 'Dec',
  11915. 'January' : 'January',
  11916. 'February' : 'February',
  11917. 'March' : 'March',
  11918. 'April' : 'April',
  11919. 'May' : 'May',
  11920. 'June' : 'June',
  11921. 'July' : 'July',
  11922. 'August' : 'August',
  11923. 'September' : 'September',
  11924. 'October' : 'October',
  11925. 'November' : 'November',
  11926. 'December' : 'December',
  11927. 'Sunday' : 'Sunday',
  11928. 'Monday' : 'Monday',
  11929. 'Tuesday' : 'Tuesday',
  11930. 'Wednesday' : 'Wednesday',
  11931. 'Thursday' : 'Thursday',
  11932. 'Friday' : 'Friday',
  11933. 'Saturday' : 'Saturday',
  11934. 'Sun' : 'Sun',
  11935. 'Mon' : 'Mon',
  11936. 'Tue' : 'Tue',
  11937. 'Wed' : 'Wed',
  11938. 'Thu' : 'Thu',
  11939. 'Fri' : 'Fri',
  11940. 'Sat' : 'Sat',
  11941. /******************************** sort variants ********************************/
  11942. 'sortname' : 'by name',
  11943. 'sortkind' : 'by kind',
  11944. 'sortsize' : 'by size',
  11945. 'sortdate' : 'by date',
  11946. 'sortFoldersFirst' : 'Folders first',
  11947. 'sortperm' : 'by permission', // from v2.1.13 added 13.06.2016
  11948. 'sortmode' : 'by mode', // from v2.1.13 added 13.06.2016
  11949. 'sortowner' : 'by owner', // from v2.1.13 added 13.06.2016
  11950. 'sortgroup' : 'by group', // from v2.1.13 added 13.06.2016
  11951. 'sortAlsoTreeview' : 'Also Treeview', // from v2.1.15 added 01.08.2016
  11952. /********************************** new items **********************************/
  11953. 'untitled file.txt' : 'NewFile.txt', // added 10.11.2015
  11954. 'untitled folder' : 'NewFolder', // added 10.11.2015
  11955. 'Archive' : 'NewArchive', // from v2.1 added 10.11.2015
  11956. 'untitled file' : 'NewFile.$1', // from v2.1.41 added 6.8.2018
  11957. 'extentionfile' : '$1: File', // from v2.1.41 added 6.8.2018
  11958. 'extentiontype' : '$1: $2', // from v2.1.43 added 17.10.2018
  11959. /********************************** messages **********************************/
  11960. 'confirmReq' : 'Confirmation required',
  11961. 'confirmRm' : 'Are you sure you want to permanently remove items?<br/>This cannot be undone!',
  11962. 'confirmRepl' : 'Replace old file with new one? (If it contains folders, it will be merged. To backup and replace, select Backup.)',
  11963. 'confirmRest' : 'Replace existing item with the item in trash?', // fromv2.1.24 added 5.5.2017
  11964. '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
  11965. '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
  11966. 'confirmNotSave' : 'It has been modified.<br/>Losing work if you do not save changes.', // from v2.1 added 15.7.2015
  11967. 'confirmTrash' : 'Are you sure you want to move items to trash bin?', //from v2.1.24 added 29.4.2017
  11968. 'apllyAll' : 'Apply to all',
  11969. 'name' : 'Name',
  11970. 'size' : 'Size',
  11971. 'perms' : 'Permissions',
  11972. 'modify' : 'Modified',
  11973. 'kind' : 'Kind',
  11974. 'read' : 'read',
  11975. 'write' : 'write',
  11976. 'noaccess' : 'no access',
  11977. 'and' : 'and',
  11978. 'unknown' : 'unknown',
  11979. 'selectall' : 'Select all items',
  11980. 'selectfiles' : 'Select item(s)',
  11981. 'selectffile' : 'Select first item',
  11982. 'selectlfile' : 'Select last item',
  11983. 'viewlist' : 'List view',
  11984. 'viewicons' : 'Icons view',
  11985. 'viewSmall' : 'Small icons', // from v2.1.39 added 22.5.2018
  11986. 'viewMedium' : 'Medium icons', // from v2.1.39 added 22.5.2018
  11987. 'viewLarge' : 'Large icons', // from v2.1.39 added 22.5.2018
  11988. 'viewExtraLarge' : 'Extra large icons', // from v2.1.39 added 22.5.2018
  11989. 'places' : 'Places',
  11990. 'calc' : 'Calculate',
  11991. 'path' : 'Path',
  11992. 'aliasfor' : 'Alias for',
  11993. 'locked' : 'Locked',
  11994. 'dim' : 'Dimensions',
  11995. 'files' : 'Files',
  11996. 'folders' : 'Folders',
  11997. 'items' : 'Items',
  11998. 'yes' : 'yes',
  11999. 'no' : 'no',
  12000. 'link' : 'Link',
  12001. 'searcresult' : 'Search results',
  12002. 'selected' : 'selected items',
  12003. 'about' : 'About',
  12004. 'shortcuts' : 'Shortcuts',
  12005. 'help' : 'Help',
  12006. 'webfm' : 'Web file manager',
  12007. 'ver' : 'Version',
  12008. 'protocolver' : 'protocol version',
  12009. 'homepage' : 'Project home',
  12010. 'docs' : 'Documentation',
  12011. 'github' : 'Fork us on GitHub',
  12012. 'twitter' : 'Follow us on Twitter',
  12013. 'facebook' : 'Join us on Facebook',
  12014. 'team' : 'Team',
  12015. 'chiefdev' : 'chief developer',
  12016. 'developer' : 'developer',
  12017. 'contributor' : 'contributor',
  12018. 'maintainer' : 'maintainer',
  12019. 'translator' : 'translator',
  12020. 'icons' : 'Icons',
  12021. 'dontforget' : 'and don\'t forget to take your towel',
  12022. 'shortcutsof' : 'Shortcuts disabled',
  12023. 'dropFiles' : 'Drop files here',
  12024. 'or' : 'or',
  12025. 'selectForUpload' : 'Select files',
  12026. 'moveFiles' : 'Move items',
  12027. 'copyFiles' : 'Copy items',
  12028. 'restoreFiles' : 'Restore items', // from v2.1.24 added 5.5.2017
  12029. 'rmFromPlaces' : 'Remove from places',
  12030. 'aspectRatio' : 'Aspect ratio',
  12031. 'scale' : 'Scale',
  12032. 'width' : 'Width',
  12033. 'height' : 'Height',
  12034. 'resize' : 'Resize',
  12035. 'crop' : 'Crop',
  12036. 'rotate' : 'Rotate',
  12037. 'rotate-cw' : 'Rotate 90 degrees CW',
  12038. 'rotate-ccw' : 'Rotate 90 degrees CCW',
  12039. 'degree' : '°',
  12040. 'netMountDialogTitle' : 'Mount network volume', // added 18.04.2012
  12041. 'protocol' : 'Protocol', // added 18.04.2012
  12042. 'host' : 'Host', // added 18.04.2012
  12043. 'port' : 'Port', // added 18.04.2012
  12044. 'user' : 'User', // added 18.04.2012
  12045. 'pass' : 'Password', // added 18.04.2012
  12046. 'confirmUnmount' : 'Are you sure to unmount $1?', // from v2.1 added 30.04.2012
  12047. 'dropFilesBrowser': 'Drop or Paste files from browser', // from v2.1 added 30.05.2012
  12048. 'dropPasteFiles' : 'Drop files, Paste URLs or images(clipboard) here', // from v2.1 added 07.04.2014
  12049. 'encoding' : 'Encoding', // from v2.1 added 19.12.2014
  12050. 'locale' : 'Locale', // from v2.1 added 19.12.2014
  12051. 'searchTarget' : 'Target: $1', // from v2.1 added 22.5.2015
  12052. 'searchMime' : 'Search by input MIME Type', // from v2.1 added 22.5.2015
  12053. 'owner' : 'Owner', // from v2.1 added 20.6.2015
  12054. 'group' : 'Group', // from v2.1 added 20.6.2015
  12055. 'other' : 'Other', // from v2.1 added 20.6.2015
  12056. 'execute' : 'Execute', // from v2.1 added 20.6.2015
  12057. 'perm' : 'Permission', // from v2.1 added 20.6.2015
  12058. 'mode' : 'Mode', // from v2.1 added 20.6.2015
  12059. 'emptyFolder' : 'Folder is empty', // from v2.1.6 added 30.12.2015
  12060. 'emptyFolderDrop' : 'Folder is empty\\A Drop to add items', // from v2.1.6 added 30.12.2015
  12061. 'emptyFolderLTap' : 'Folder is empty\\A Long tap to add items', // from v2.1.6 added 30.12.2015
  12062. 'quality' : 'Quality', // from v2.1.6 added 5.1.2016
  12063. 'autoSync' : 'Auto sync', // from v2.1.6 added 10.1.2016
  12064. 'moveUp' : 'Move up', // from v2.1.6 added 18.1.2016
  12065. 'getLink' : 'Get URL link', // from v2.1.7 added 9.2.2016
  12066. 'selectedItems' : 'Selected items ($1)', // from v2.1.7 added 2.19.2016
  12067. 'folderId' : 'Folder ID', // from v2.1.10 added 3.25.2016
  12068. 'offlineAccess' : 'Allow offline access', // from v2.1.10 added 3.25.2016
  12069. 'reAuth' : 'To re-authenticate', // from v2.1.10 added 3.25.2016
  12070. 'nowLoading' : 'Now loading...', // from v2.1.12 added 4.26.2016
  12071. 'openMulti' : 'Open multiple files', // from v2.1.12 added 5.14.2016
  12072. '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
  12073. 'emptySearch' : 'Search results is empty in search target.', // from v2.1.12 added 5.16.2016
  12074. 'editingFile' : 'It is editing a file.', // from v2.1.13 added 6.3.2016
  12075. 'hasSelected' : 'You have selected $1 items.', // from v2.1.13 added 6.3.2016
  12076. 'hasClipboard' : 'You have $1 items in the clipboard.', // from v2.1.13 added 6.3.2016
  12077. 'incSearchOnly' : 'Incremental search is only from the current view.', // from v2.1.13 added 6.30.2016
  12078. 'reinstate' : 'Reinstate', // from v2.1.15 added 3.8.2016
  12079. 'complete' : '$1 complete', // from v2.1.15 added 21.8.2016
  12080. 'contextmenu' : 'Context menu', // from v2.1.15 added 9.9.2016
  12081. 'pageTurning' : 'Page turning', // from v2.1.15 added 10.9.2016
  12082. 'volumeRoots' : 'Volume roots', // from v2.1.16 added 16.9.2016
  12083. 'reset' : 'Reset', // from v2.1.16 added 1.10.2016
  12084. 'bgcolor' : 'Background color', // from v2.1.16 added 1.10.2016
  12085. 'colorPicker' : 'Color picker', // from v2.1.16 added 1.10.2016
  12086. '8pxgrid' : '8px Grid', // from v2.1.16 added 4.10.2016
  12087. 'enabled' : 'Enabled', // from v2.1.16 added 4.10.2016
  12088. 'disabled' : 'Disabled', // from v2.1.16 added 4.10.2016
  12089. 'emptyIncSearch' : 'Search results is empty in current view.\\A Press [Enter] to expand search target.', // from v2.1.16 added 5.10.2016
  12090. 'emptyLetSearch' : 'First letter search results is empty in current view.', // from v2.1.23 added 24.3.2017
  12091. 'textLabel' : 'Text label', // from v2.1.17 added 13.10.2016
  12092. 'minsLeft' : '$1 mins left', // from v2.1.17 added 13.11.2016
  12093. 'openAsEncoding' : 'Reopen with selected encoding', // from v2.1.19 added 2.12.2016
  12094. 'saveAsEncoding' : 'Save with the selected encoding', // from v2.1.19 added 2.12.2016
  12095. 'selectFolder' : 'Select folder', // from v2.1.20 added 13.12.2016
  12096. 'firstLetterSearch': 'First letter search', // from v2.1.23 added 24.3.2017
  12097. 'presets' : 'Presets', // from v2.1.25 added 26.5.2017
  12098. 'tooManyToTrash' : 'It\'s too many items so it can\'t into trash.', // from v2.1.25 added 9.6.2017
  12099. 'TextArea' : 'TextArea', // from v2.1.25 added 14.6.2017
  12100. 'folderToEmpty' : 'Empty the folder "$1".', // from v2.1.25 added 22.6.2017
  12101. 'filderIsEmpty' : 'There are no items in a folder "$1".', // from v2.1.25 added 22.6.2017
  12102. 'preference' : 'Preference', // from v2.1.26 added 28.6.2017
  12103. 'language' : 'Language', // from v2.1.26 added 28.6.2017
  12104. 'clearBrowserData': 'Initialize the settings saved in this browser', // from v2.1.26 added 28.6.2017
  12105. 'toolbarPref' : 'Toolbar settings', // from v2.1.27 added 2.8.2017
  12106. 'charsLeft' : '... $1 chars left.', // from v2.1.29 added 30.8.2017
  12107. 'sum' : 'Sum', // from v2.1.29 added 28.9.2017
  12108. 'roughFileSize' : 'Rough file size', // from v2.1.30 added 2.11.2017
  12109. 'autoFocusDialog' : 'Focus on the element of dialog with mouseover', // from v2.1.30 added 2.11.2017
  12110. 'select' : 'Select', // from v2.1.30 added 23.11.2017
  12111. 'selectAction' : 'Action when select file', // from v2.1.30 added 23.11.2017
  12112. 'useStoredEditor' : 'Open with the editor used last time', // from v2.1.30 added 23.11.2017
  12113. 'selectinvert' : 'Invert selection', // from v2.1.30 added 25.11.2017
  12114. '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
  12115. 'batchRename' : 'Batch rename', // from v2.1.31 added 8.12.2017
  12116. 'plusNumber' : '+ Number', // from v2.1.31 added 8.12.2017
  12117. 'asPrefix' : 'Add prefix', // from v2.1.31 added 8.12.2017
  12118. 'asSuffix' : 'Add suffix', // from v2.1.31 added 8.12.2017
  12119. 'changeExtention' : 'Change extention', // from v2.1.31 added 8.12.2017
  12120. 'columnPref' : 'Columns settings (List view)', // from v2.1.32 added 6.2.2018
  12121. 'reflectOnImmediate' : 'All changes will reflect immediately to the archive.', // from v2.1.33 added 2.3.2018
  12122. 'reflectOnUnmount' : 'Any changes will not reflect until un-mount this volume.', // from v2.1.33 added 2.3.2018
  12123. '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
  12124. 'selectionInfo' : 'Selection Info', // from v2.1.33 added 7.3.2018
  12125. 'hashChecker' : 'Algorithms to show the file hash', // from v2.1.33 added 10.3.2018
  12126. 'infoItems' : 'Info Items (Selection Info Panel)', // from v2.1.38 added 28.3.2018
  12127. 'pressAgainToExit': 'Press again to exit.', // from v2.1.38 added 1.4.2018
  12128. 'toolbar' : 'Toolbar', // from v2.1.38 added 4.4.2018
  12129. 'workspace' : 'Work Space', // from v2.1.38 added 4.4.2018
  12130. 'dialog' : 'Dialog', // from v2.1.38 added 4.4.2018
  12131. 'all' : 'All', // from v2.1.38 added 4.4.2018
  12132. 'iconSize' : 'Icon Size (Icons view)', // from v2.1.39 added 7.5.2018
  12133. 'editorMaximized' : 'Open the maximized editor window', // from v2.1.40 added 30.6.2018
  12134. 'editorConvNoApi' : 'Because conversion by API is not currently available, please convert on the website.', //from v2.1.40 added 8.7.2018
  12135. '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
  12136. 'convertOn' : 'Convert on the site of $1', // from v2.1.40 added 10.7.2018
  12137. 'integrations' : 'Integrations', // from v2.1.40 added 11.7.2018
  12138. '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
  12139. 'showHidden' : 'Show hidden items', // from v2.1.41 added 24.7.2018
  12140. 'hideHidden' : 'Hide hidden items', // from v2.1.41 added 24.7.2018
  12141. 'toggleHidden' : 'Show/Hide hidden items', // from v2.1.41 added 24.7.2018
  12142. 'makefileTypes' : 'File types to enable with "New file"', // from v2.1.41 added 7.8.2018
  12143. 'typeOfTextfile' : 'Type of the Text file', // from v2.1.41 added 7.8.2018
  12144. 'add' : 'Add', // from v2.1.41 added 7.8.2018
  12145. 'theme' : 'Theme', // from v2.1.43 added 19.10.2018
  12146. 'default' : 'Default', // from v2.1.43 added 19.10.2018
  12147. 'description' : 'Description', // from v2.1.43 added 19.10.2018
  12148. 'website' : 'Website', // from v2.1.43 added 19.10.2018
  12149. 'author' : 'Author', // from v2.1.43 added 19.10.2018
  12150. 'email' : 'Email', // from v2.1.43 added 19.10.2018
  12151. 'license' : 'License', // from v2.1.43 added 19.10.2018
  12152. /********************************** mimetypes **********************************/
  12153. 'kindUnknown' : 'Unknown',
  12154. 'kindRoot' : 'Volume Root', // from v2.1.16 added 16.10.2016
  12155. 'kindFolder' : 'Folder',
  12156. 'kindSelects' : 'Selections', // from v2.1.29 added 29.8.2017
  12157. 'kindAlias' : 'Alias',
  12158. 'kindAliasBroken' : 'Broken alias',
  12159. // applications
  12160. 'kindApp' : 'Application',
  12161. 'kindPostscript' : 'Postscript document',
  12162. 'kindMsOffice' : 'Microsoft Office document',
  12163. 'kindMsWord' : 'Microsoft Word document',
  12164. 'kindMsExcel' : 'Microsoft Excel document',
  12165. 'kindMsPP' : 'Microsoft Powerpoint presentation',
  12166. 'kindOO' : 'Open Office document',
  12167. 'kindAppFlash' : 'Flash application',
  12168. 'kindPDF' : 'Portable Document Format (PDF)',
  12169. 'kindTorrent' : 'Bittorrent file',
  12170. 'kind7z' : '7z archive',
  12171. 'kindTAR' : 'TAR archive',
  12172. 'kindGZIP' : 'GZIP archive',
  12173. 'kindBZIP' : 'BZIP archive',
  12174. 'kindXZ' : 'XZ archive',
  12175. 'kindZIP' : 'ZIP archive',
  12176. 'kindRAR' : 'RAR archive',
  12177. 'kindJAR' : 'Java JAR file',
  12178. 'kindTTF' : 'True Type font',
  12179. 'kindOTF' : 'Open Type font',
  12180. 'kindRPM' : 'RPM package',
  12181. // texts
  12182. 'kindText' : 'Text document',
  12183. 'kindTextPlain' : 'Plain text',
  12184. 'kindPHP' : 'PHP source',
  12185. 'kindCSS' : 'Cascading style sheet',
  12186. 'kindHTML' : 'HTML document',
  12187. 'kindJS' : 'Javascript source',
  12188. 'kindRTF' : 'Rich Text Format',
  12189. 'kindC' : 'C source',
  12190. 'kindCHeader' : 'C header source',
  12191. 'kindCPP' : 'C++ source',
  12192. 'kindCPPHeader' : 'C++ header source',
  12193. 'kindShell' : 'Unix shell script',
  12194. 'kindPython' : 'Python source',
  12195. 'kindJava' : 'Java source',
  12196. 'kindRuby' : 'Ruby source',
  12197. 'kindPerl' : 'Perl script',
  12198. 'kindSQL' : 'SQL source',
  12199. 'kindXML' : 'XML document',
  12200. 'kindAWK' : 'AWK source',
  12201. 'kindCSV' : 'Comma separated values',
  12202. 'kindDOCBOOK' : 'Docbook XML document',
  12203. 'kindMarkdown' : 'Markdown text', // added 20.7.2015
  12204. // images
  12205. 'kindImage' : 'Image',
  12206. 'kindBMP' : 'BMP image',
  12207. 'kindJPEG' : 'JPEG image',
  12208. 'kindGIF' : 'GIF Image',
  12209. 'kindPNG' : 'PNG Image',
  12210. 'kindTIFF' : 'TIFF image',
  12211. 'kindTGA' : 'TGA image',
  12212. 'kindPSD' : 'Adobe Photoshop image',
  12213. 'kindXBITMAP' : 'X bitmap image',
  12214. 'kindPXM' : 'Pixelmator image',
  12215. // media
  12216. 'kindAudio' : 'Audio media',
  12217. 'kindAudioMPEG' : 'MPEG audio',
  12218. 'kindAudioMPEG4' : 'MPEG-4 audio',
  12219. 'kindAudioMIDI' : 'MIDI audio',
  12220. 'kindAudioOGG' : 'Ogg Vorbis audio',
  12221. 'kindAudioWAV' : 'WAV audio',
  12222. 'AudioPlaylist' : 'MP3 playlist',
  12223. 'kindVideo' : 'Video media',
  12224. 'kindVideoDV' : 'DV movie',
  12225. 'kindVideoMPEG' : 'MPEG movie',
  12226. 'kindVideoMPEG4' : 'MPEG-4 movie',
  12227. 'kindVideoAVI' : 'AVI movie',
  12228. 'kindVideoMOV' : 'Quick Time movie',
  12229. 'kindVideoWM' : 'Windows Media movie',
  12230. 'kindVideoFlash' : 'Flash movie',
  12231. 'kindVideoMKV' : 'Matroska movie',
  12232. 'kindVideoOGG' : 'Ogg movie'
  12233. }
  12234. };
  12235. }
  12236. /*
  12237. * File: /js/ui/button.js
  12238. */
  12239. /**
  12240. * @class elFinder toolbar button widget.
  12241. * If command has variants - create menu
  12242. *
  12243. * @author Dmitry (dio) Levashov
  12244. **/
  12245. $.fn.elfinderbutton = function(cmd) {
  12246. return this.each(function() {
  12247. var c = 'class',
  12248. fm = cmd.fm,
  12249. disabled = fm.res(c, 'disabled'),
  12250. active = fm.res(c, 'active'),
  12251. hover = fm.res(c, 'hover'),
  12252. item = 'elfinder-button-menu-item',
  12253. selected = 'elfinder-button-menu-item-selected',
  12254. menu,
  12255. text = $('<span class="elfinder-button-text">'+cmd.title+'</span>'),
  12256. prvCname = 'elfinder-button-icon-' + (cmd.className? cmd.className : cmd.name),
  12257. button = $(this).addClass('ui-state-default elfinder-button')
  12258. .attr('title', cmd.title)
  12259. .append('<span class="elfinder-button-icon ' + prvCname + '"/>', text)
  12260. .on('mouseenter mouseleave', function(e) { !button.hasClass(disabled) && button[e.type == 'mouseleave' ? 'removeClass' : 'addClass'](hover);})
  12261. .on('click', function(e) {
  12262. if (!button.hasClass(disabled)) {
  12263. if (menu && cmd.variants.length >= 1) {
  12264. // close other menus
  12265. menu.is(':hidden') && fm.getUI().click();
  12266. e.stopPropagation();
  12267. menu.css(getMenuOffset()).slideToggle({
  12268. duration: 100,
  12269. done: function(e) {
  12270. fm[menu.is(':visible')? 'toFront' : 'toHide'](menu);
  12271. }
  12272. });
  12273. } else {
  12274. fm.exec(cmd.name, getSelected(), {_userAction: true, _currentType: 'toolbar', _currentNode: button });
  12275. }
  12276. }
  12277. }),
  12278. hideMenu = function() {
  12279. fm.toHide(menu);
  12280. },
  12281. getMenuOffset = function() {
  12282. var fmNode = fm.getUI(),
  12283. baseOffset = fm.getUI().offset(),
  12284. buttonOffset = fmNode.offset();
  12285. return {
  12286. top : buttonOffset.top - baseOffset.top,
  12287. left : buttonOffset.left - baseOffset.left,
  12288. maxHeight : fmNode.height() - 40
  12289. };
  12290. },
  12291. getSelected = function() {
  12292. var sel = fm.selected(),
  12293. cwd;
  12294. if (!sel.length) {
  12295. if (cwd = fm.cwd()) {
  12296. sel = [ fm.cwd().hash ];
  12297. } else {
  12298. sel = void(0);
  12299. }
  12300. }
  12301. return sel;
  12302. },
  12303. tm;
  12304. text.hide();
  12305. // set self button object to cmd object
  12306. cmd.button = button;
  12307. // if command has variants create menu
  12308. if (Array.isArray(cmd.variants)) {
  12309. button.addClass('elfinder-menubutton');
  12310. menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>')
  12311. .hide()
  12312. .appendTo(fm.getUI())
  12313. .on('mouseenter mouseleave', '.'+item, function() { $(this).toggleClass(hover); })
  12314. .on('click', '.'+item, function(e) {
  12315. var opts = $(this).data('value');
  12316. e.preventDefault();
  12317. e.stopPropagation();
  12318. button.removeClass(hover);
  12319. fm.toHide(menu);
  12320. if (typeof opts === 'undefined') {
  12321. opts = {};
  12322. }
  12323. if (typeof opts === 'object') {
  12324. opts._userAction = true;
  12325. }
  12326. fm.exec(cmd.name, getSelected(), opts);
  12327. })
  12328. .on('close', hideMenu);
  12329. fm.bind('disable select', hideMenu).getUI().on('click', hideMenu);
  12330. cmd.change(function() {
  12331. menu.html('');
  12332. $.each(cmd.variants, function(i, variant) {
  12333. menu.append($('<div class="'+item+'">'+variant[1]+'</div>').data('value', variant[0]).addClass(variant[0] == cmd.value ? selected : ''));
  12334. });
  12335. });
  12336. }
  12337. cmd.change(function() {
  12338. var cName;
  12339. tm && cancelAnimationFrame(tm);
  12340. tm = requestAnimationFrame(function() {
  12341. if (cmd.disabled()) {
  12342. button.removeClass(active+' '+hover).addClass(disabled);
  12343. } else {
  12344. button.removeClass(disabled);
  12345. button[cmd.active() ? 'addClass' : 'removeClass'](active);
  12346. }
  12347. if (cmd.syncTitleOnChange) {
  12348. cName = 'elfinder-button-icon-' + (cmd.className? cmd.className : cmd.name);
  12349. if (prvCname !== cName) {
  12350. button.children('.elfinder-button-icon').removeClass(prvCname).addClass(cName);
  12351. prvCname = cName;
  12352. }
  12353. text.html(cmd.title);
  12354. button.attr('title', cmd.title);
  12355. }
  12356. });
  12357. })
  12358. .change();
  12359. });
  12360. };
  12361. /*
  12362. * File: /js/ui/contextmenu.js
  12363. */
  12364. /**
  12365. * @class elFinder contextmenu
  12366. *
  12367. * @author Dmitry (dio) Levashov
  12368. **/
  12369. $.fn.elfindercontextmenu = function(fm) {
  12370. return this.each(function() {
  12371. var self = $(this),
  12372. cmItem = 'elfinder-contextmenu-item',
  12373. smItem = 'elfinder-contextsubmenu-item',
  12374. exIcon = 'elfinder-contextmenu-extra-icon',
  12375. cHover = fm.res('class', 'hover'),
  12376. dragOpt = {
  12377. distance: 8,
  12378. start: function() {
  12379. menu.data('drag', true).data('touching') && menu.find('.'+cHover).removeClass(cHover);
  12380. },
  12381. stop: function() {
  12382. menu.data('draged', true).removeData('drag');
  12383. }
  12384. },
  12385. menu = $(this).addClass('touch-punch ui-helper-reset ui-front ui-widget ui-state-default ui-corner-all elfinder-contextmenu elfinder-contextmenu-'+fm.direction)
  12386. .hide()
  12387. .on('touchstart', function(e) {
  12388. menu.data('touching', true).children().removeClass(cHover);
  12389. })
  12390. .on('touchend', function(e) {
  12391. menu.removeData('touching');
  12392. })
  12393. .on('mouseenter mouseleave', '.'+cmItem, function(e) {
  12394. $(this).toggleClass(cHover, (e.type === 'mouseenter' || (! menu.data('draged') && menu.data('submenuKeep'))? true : false));
  12395. if (menu.data('draged') && menu.data('submenuKeep')) {
  12396. menu.find('.elfinder-contextmenu-sub:visible').parent().addClass(cHover);
  12397. }
  12398. })
  12399. .on('mouseenter mouseleave', '.'+exIcon, function(e) {
  12400. $(this).parent().toggleClass(cHover, e.type === 'mouseleave');
  12401. })
  12402. .on('mouseenter mouseleave', '.'+cmItem+',.'+smItem, function(e) {
  12403. var setIndex = function(target, sub) {
  12404. $.each(sub? subnodes : nodes, function(i, n) {
  12405. if (target[0] === n) {
  12406. (sub? subnodes : nodes)._cur = i;
  12407. if (sub) {
  12408. subselected = target;
  12409. } else {
  12410. selected = target;
  12411. }
  12412. return false;
  12413. }
  12414. });
  12415. };
  12416. if (e.originalEvent) {
  12417. var target = $(this),
  12418. unHover = function() {
  12419. if (selected && !selected.children('div.elfinder-contextmenu-sub:visible').length) {
  12420. selected.removeClass(cHover);
  12421. }
  12422. };
  12423. if (e.type === 'mouseenter') {
  12424. // mouseenter
  12425. if (target.hasClass(smItem)) {
  12426. // submenu
  12427. if (subselected) {
  12428. subselected.removeClass(cHover);
  12429. }
  12430. if (selected) {
  12431. subnodes = selected.find('div.'+smItem);
  12432. }
  12433. setIndex(target, true);
  12434. } else {
  12435. // menu
  12436. unHover();
  12437. setIndex(target);
  12438. }
  12439. } else {
  12440. // mouseleave
  12441. if (target.hasClass(smItem)) {
  12442. //submenu
  12443. subselected = null;
  12444. subnodes = null;
  12445. } else {
  12446. // menu
  12447. unHover();
  12448. (function(sel) {
  12449. setTimeout(function() {
  12450. if (sel === selected) {
  12451. selected = null;
  12452. }
  12453. }, 250);
  12454. })(selected);
  12455. }
  12456. }
  12457. }
  12458. })
  12459. .on('contextmenu', function(){return false;})
  12460. .on('mouseup', function() {
  12461. setTimeout(function() {
  12462. menu.removeData('draged');
  12463. }, 100);
  12464. })
  12465. .draggable(dragOpt),
  12466. ltr = fm.direction === 'ltr',
  12467. subpos = ltr? 'left' : 'right',
  12468. types = Object.assign({}, fm.options.contextmenu),
  12469. tpl = '<div class="'+cmItem+'{className}"><span class="elfinder-button-icon {icon} elfinder-contextmenu-icon"{style}/><span>{label}</span></div>',
  12470. item = function(label, icon, callback, opts) {
  12471. var className = '',
  12472. style = '',
  12473. iconClass = '',
  12474. v, pos;
  12475. if (opts) {
  12476. if (opts.className) {
  12477. className = ' ' + opts.className;
  12478. }
  12479. if (opts.iconClass) {
  12480. iconClass = opts.iconClass;
  12481. icon = '';
  12482. }
  12483. if (opts.iconImg) {
  12484. v = opts.iconImg.split(/ +/);
  12485. pos = v[1] && v[2]? fm.escape(v[1] + 'px ' + v[2] + 'px') : '';
  12486. style = ' style="background:url(\''+fm.escape(v[0])+'\') '+(pos? pos : '0 0')+' no-repeat;'+(pos? '' : 'posbackground-size:contain;')+'"';
  12487. }
  12488. }
  12489. return $(tpl.replace('{icon}', icon ? 'elfinder-button-icon-'+icon : (iconClass? iconClass : ''))
  12490. .replace('{label}', label)
  12491. .replace('{style}', style)
  12492. .replace('{className}', className))
  12493. .on('click', function(e) {
  12494. e.stopPropagation();
  12495. e.preventDefault();
  12496. callback();
  12497. });
  12498. },
  12499. urlIcon = function(iconUrl) {
  12500. var v = iconUrl.split(/ +/),
  12501. pos = v[1] && v[2]? (v[1] + 'px ' + v[2] + 'px') : '';
  12502. return {
  12503. backgroundImage: 'url("'+v[0]+'")',
  12504. backgroundRepeat: 'no-repeat',
  12505. backgroundPosition: pos? pos : '',
  12506. backgroundSize: pos? '' : 'contain'
  12507. };
  12508. },
  12509. base, cwd,
  12510. nodes, selected, subnodes, subselected, autoSyncStop, subHoverTm,
  12511. autoToggle = function() {
  12512. var evTouchStart = 'touchstart.contextmenuAutoToggle';
  12513. menu.data('hideTm') && clearTimeout(menu.data('hideTm'));
  12514. if (menu.is(':visible')) {
  12515. menu.on('touchstart', function(e) {
  12516. if (e.originalEvent.touches.length > 1) {
  12517. return;
  12518. }
  12519. menu.stop();
  12520. fm.toFront(menu);
  12521. menu.data('hideTm') && clearTimeout(menu.data('hideTm'));
  12522. })
  12523. .data('hideTm', setTimeout(function() {
  12524. if (menu.is(':visible')) {
  12525. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  12526. cwd.find('.elfinder-cwd-file.ui-selected')
  12527. .one(evTouchStart, function(e) {
  12528. if (e.originalEvent.touches.length > 1) {
  12529. return;
  12530. }
  12531. var tgt = $(e.target);
  12532. if (menu.first().length && !tgt.is('input:checkbox') && !tgt.hasClass('elfinder-cwd-select')) {
  12533. e.stopPropagation();
  12534. //e.preventDefault();
  12535. open(e.originalEvent.touches[0].pageX, e.originalEvent.touches[0].pageY);
  12536. cwd.data('longtap', true)
  12537. tgt.one('touchend', function() {
  12538. setTimeout(function() {
  12539. cwd.removeData('longtap');
  12540. }, 80);
  12541. });
  12542. return;
  12543. }
  12544. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  12545. })
  12546. .one('unselect.'+fm.namespace, function() {
  12547. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  12548. });
  12549. menu.fadeOut({
  12550. duration: 300,
  12551. fail: function() {
  12552. menu.css('opacity', '1').show();
  12553. },
  12554. done: function() {
  12555. fm.toHide(menu);
  12556. }
  12557. });
  12558. }
  12559. }, 4500));
  12560. }
  12561. },
  12562. keyEvts = function(e) {
  12563. var code = e.keyCode,
  12564. ESC = $.ui.keyCode.ESCAPE,
  12565. ENT = $.ui.keyCode.ENTER,
  12566. LEFT = $.ui.keyCode.LEFT,
  12567. RIGHT = $.ui.keyCode.RIGHT,
  12568. UP = $.ui.keyCode.UP,
  12569. DOWN = $.ui.keyCode.DOWN,
  12570. subent = fm.direction === 'ltr'? RIGHT : LEFT,
  12571. sublev = subent === RIGHT? LEFT : RIGHT;
  12572. if ($.inArray(code, [ESC, ENT, LEFT, RIGHT, UP, DOWN]) !== -1) {
  12573. e.preventDefault();
  12574. e.stopPropagation();
  12575. e.stopImmediatePropagation();
  12576. if (code == ESC || code === sublev) {
  12577. if (selected && subnodes && subselected) {
  12578. subselected.trigger('mouseleave').trigger('submenuclose');
  12579. selected.addClass(cHover);
  12580. subnodes = null;
  12581. subselected = null;
  12582. } else {
  12583. code == ESC && close();
  12584. }
  12585. } else if (code == UP || code == DOWN) {
  12586. if (subnodes) {
  12587. if (subselected) {
  12588. subselected.trigger('mouseleave');
  12589. }
  12590. if (code == DOWN && (! subselected || subnodes.length <= ++subnodes._cur)) {
  12591. subnodes._cur = 0;
  12592. } else if (code == UP && (! subselected || --subnodes._cur < 0)) {
  12593. subnodes._cur = subnodes.length - 1;
  12594. }
  12595. subselected = subnodes.eq(subnodes._cur).trigger('mouseenter');
  12596. } else {
  12597. subnodes = null;
  12598. if (selected) {
  12599. selected.trigger('mouseleave');
  12600. }
  12601. if (code == DOWN && (! selected || nodes.length <= ++nodes._cur)) {
  12602. nodes._cur = 0;
  12603. } else if (code == UP && (! selected || --nodes._cur < 0)) {
  12604. nodes._cur = nodes.length - 1;
  12605. }
  12606. selected = nodes.eq(nodes._cur).addClass(cHover);
  12607. }
  12608. } else if (selected && (code == ENT || code === subent)) {
  12609. if (selected.hasClass('elfinder-contextmenu-group')) {
  12610. if (subselected) {
  12611. code == ENT && subselected.click();
  12612. } else {
  12613. selected.trigger('mouseenter');
  12614. subnodes = selected.find('div.'+smItem);
  12615. subnodes._cur = 0;
  12616. subselected = subnodes.first().addClass(cHover);
  12617. }
  12618. } else {
  12619. code == ENT && selected.click();
  12620. }
  12621. }
  12622. }
  12623. },
  12624. open = function(x, y, css) {
  12625. var width = menu.outerWidth(),
  12626. height = menu.outerHeight(),
  12627. bstyle = base.attr('style'),
  12628. bpos = base.offset(),
  12629. bwidth = base.width(),
  12630. bheight = base.height(),
  12631. mw = fm.UA.Mobile? 40 : 2,
  12632. mh = fm.UA.Mobile? 20 : 2,
  12633. x = x - (bpos? bpos.left : 0),
  12634. y = y - (bpos? bpos.top : 0),
  12635. css = Object.assign(css || {}, {
  12636. top : Math.max(0, y + mh + height < bheight ? y + mh : y - (y + height - bheight)),
  12637. left : Math.max(0, (x < width + mw || x + mw + width < bwidth)? x + mw : x - mw - width),
  12638. opacity : '1'
  12639. }),
  12640. evts;
  12641. autoSyncStop = true;
  12642. fm.autoSync('stop');
  12643. base.width(bwidth);
  12644. menu.stop().removeAttr('style').css(css);
  12645. fm.toFront(menu);
  12646. menu.show();
  12647. base.attr('style', bstyle);
  12648. css[subpos] = parseInt(menu.width());
  12649. menu.find('.elfinder-contextmenu-sub').css(css);
  12650. if (fm.UA.iOS) {
  12651. $('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'auto');
  12652. }
  12653. selected = null;
  12654. subnodes = null;
  12655. subselected = null;
  12656. $(document).on('keydown.' + fm.namespace, keyEvts);
  12657. evts = $._data(document).events;
  12658. if (evts && evts.keydown) {
  12659. evts.keydown.unshift(evts.keydown.pop());
  12660. }
  12661. fm.UA.Mobile && autoToggle();
  12662. requestAnimationFrame(function() {
  12663. fm.getUI().one('click.' + fm.namespace, close);
  12664. });
  12665. },
  12666. close = function() {
  12667. fm.getUI().off('click.' + fm.namespace, close);
  12668. $(document).off('keydown.' + fm.namespace, keyEvts);
  12669. currentType = currentTargets = null;
  12670. if (menu.is(':visible') || menu.children().length) {
  12671. fm.toHide(menu.removeAttr('style').empty().removeData('submenuKeep'));
  12672. try {
  12673. if (! menu.draggable('instance')) {
  12674. menu.draggable(dragOpt);
  12675. }
  12676. } catch(e) {
  12677. if (! menu.hasClass('ui-draggable')) {
  12678. menu.draggable(dragOpt);
  12679. }
  12680. }
  12681. if (menu.data('prevNode')) {
  12682. menu.data('prevNode').after(menu);
  12683. menu.removeData('prevNode');
  12684. }
  12685. fm.trigger('closecontextmenu');
  12686. if (fm.UA.iOS) {
  12687. $('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'touch');
  12688. }
  12689. }
  12690. autoSyncStop && fm.searchStatus.state < 1 && ! fm.searchStatus.ininc && fm.autoSync();
  12691. autoSyncStop = false;
  12692. },
  12693. create = function(type, targets) {
  12694. var sep = false,
  12695. insSep = false,
  12696. disabled = [],
  12697. isCwd = type === 'cwd',
  12698. selcnt = 0,
  12699. cmdMap;
  12700. currentType = type;
  12701. currentTargets = targets;
  12702. // get current uiCmdMap option
  12703. if (!(cmdMap = fm.option('uiCmdMap', isCwd? void(0) : targets[0]))) {
  12704. cmdMap = {};
  12705. }
  12706. if (!isCwd) {
  12707. disabled = fm.getDisabledCmds(targets);
  12708. }
  12709. selcnt = fm.selected().length;
  12710. if (selcnt > 1) {
  12711. menu.append('<div class="ui-corner-top ui-widget-header elfinder-contextmenu-header"><span>'
  12712. + fm.i18n('selectedItems', ''+selcnt)
  12713. + '</span></div>');
  12714. }
  12715. nodes = $();
  12716. $.each(types[type]||[], function(i, name) {
  12717. var cmd, cmdName, useMap, node, submenu, hover;
  12718. if (name === '|') {
  12719. if (sep) {
  12720. insSep = true;
  12721. }
  12722. return;
  12723. }
  12724. if (cmdMap[name]) {
  12725. cmdName = cmdMap[name];
  12726. useMap = true;
  12727. } else {
  12728. cmdName = name;
  12729. }
  12730. cmd = fm.getCommand(cmdName);
  12731. if (cmd && !isCwd && (!fm.searchStatus.state || !cmd.disableOnSearch)) {
  12732. cmd.__disabled = cmd._disabled;
  12733. cmd._disabled = !(cmd.alwaysEnabled || (fm._commands[cmdName] ? $.inArray(name, disabled) === -1 && (!useMap || !disabled[cmdName]) : false));
  12734. $.each(cmd.linkedCmds, function(i, n) {
  12735. var c;
  12736. if (c = fm.getCommand(n)) {
  12737. c.__disabled = c._disabled;
  12738. c._disabled = !(c.alwaysEnabled || (fm._commands[n] ? !disabled[n] : false));
  12739. }
  12740. });
  12741. }
  12742. if (cmd && !cmd._disabled && cmd.getstate(targets) != -1) {
  12743. if (cmd.variants) {
  12744. if (!cmd.variants.length) {
  12745. return;
  12746. }
  12747. node = item(cmd.title, cmd.className? cmd.className : cmd.name, function(){}, cmd.contextmenuOpts);
  12748. submenu = $('<div class="ui-front ui-corner-all elfinder-contextmenu-sub"/>')
  12749. .hide()
  12750. .css('max-height', fm.getUI().height() - 30)
  12751. .appendTo(node.append('<span class="elfinder-contextmenu-arrow"/>'));
  12752. hover = function(show){
  12753. if (! show) {
  12754. submenu.hide();
  12755. } else {
  12756. var bstyle = base.attr('style');
  12757. base.width(base.width());
  12758. // top: '-1000px' to prevent visible scrollbar of window with the elFinder option `height: '100%'`
  12759. submenu.css({ top: '-1000px', left: 'auto', right: 'auto' });
  12760. var nodeOffset = node.offset(),
  12761. nodeleft = nodeOffset.left,
  12762. nodetop = nodeOffset.top,
  12763. nodewidth = node.outerWidth(),
  12764. width = submenu.outerWidth(true),
  12765. height = submenu.outerHeight(true),
  12766. baseOffset = base.offset(),
  12767. wwidth = baseOffset.left + base.width(),
  12768. wheight = baseOffset.top + base.height(),
  12769. cltr = ltr,
  12770. x = nodewidth,
  12771. y, over;
  12772. if (ltr) {
  12773. over = (nodeleft + nodewidth + width) - wwidth;
  12774. if (over > 10) {
  12775. if (nodeleft > width - 5) {
  12776. x = x - 5;
  12777. cltr = false;
  12778. } else {
  12779. if (!fm.UA.Mobile) {
  12780. x = nodewidth - over;
  12781. }
  12782. }
  12783. }
  12784. } else {
  12785. over = width - nodeleft;
  12786. if (over > 0) {
  12787. if ((nodeleft + nodewidth + width - 15) < wwidth) {
  12788. x = x - 5;
  12789. cltr = true;
  12790. } else {
  12791. if (!fm.UA.Mobile) {
  12792. x = nodewidth - over;
  12793. }
  12794. }
  12795. }
  12796. }
  12797. over = (nodetop + 5 + height) - wheight;
  12798. y = (over > 0 && nodetop < wheight)? 5 - over : (over > 0? 30 - height : 5);
  12799. menu.find('.elfinder-contextmenu-sub:visible').hide();
  12800. submenu.css({
  12801. top : y,
  12802. left : cltr? x : 'auto',
  12803. right: cltr? 'auto' : x,
  12804. overflowY: 'auto'
  12805. }).show();
  12806. base.attr('style', bstyle);
  12807. }
  12808. };
  12809. node.addClass('elfinder-contextmenu-group')
  12810. .on('mouseleave', '.elfinder-contextmenu-sub', function(e) {
  12811. if (! menu.data('draged')) {
  12812. menu.removeData('submenuKeep');
  12813. }
  12814. })
  12815. .on('submenuclose', '.elfinder-contextmenu-sub', function(e) {
  12816. hover(false);
  12817. })
  12818. .on('click', '.'+smItem, function(e){
  12819. var opts, $this;
  12820. e.stopPropagation();
  12821. if (! menu.data('draged')) {
  12822. $this = $(this);
  12823. if (!cmd.keepContextmenu) {
  12824. menu.hide();
  12825. } else {
  12826. $this.removeClass(cHover);
  12827. node.addClass(cHover);
  12828. }
  12829. opts = $this.data('exec');
  12830. if (typeof opts === 'undefined') {
  12831. opts = {};
  12832. }
  12833. if (typeof opts === 'object') {
  12834. opts._userAction = true;
  12835. opts._currentType = type;
  12836. opts._currentNode = $this;
  12837. }
  12838. !cmd.keepContextmenu && close();
  12839. fm.exec(cmd.name, targets, opts);
  12840. }
  12841. })
  12842. .on('touchend', function(e) {
  12843. if (! menu.data('drag')) {
  12844. hover(true);
  12845. menu.data('submenuKeep', true);
  12846. }
  12847. })
  12848. .on('mouseenter mouseleave', function(e){
  12849. if (! menu.data('touching')) {
  12850. if (node.data('timer')) {
  12851. clearTimeout(node.data('timer'));
  12852. node.removeData('timer');
  12853. }
  12854. if (!$(e.target).closest('.elfinder-contextmenu-sub', menu).length) {
  12855. if (e.type === 'mouseleave') {
  12856. if (! menu.data('submenuKeep')) {
  12857. node.data('timer', setTimeout(function() {
  12858. node.removeData('timer');
  12859. hover(false);
  12860. }, 250));
  12861. }
  12862. } else {
  12863. node.data('timer', setTimeout(function() {
  12864. node.removeData('timer');
  12865. hover(true);
  12866. }, nodes.find('div.elfinder-contextmenu-sub:visible').length? 250 : 0));
  12867. }
  12868. }
  12869. }
  12870. });
  12871. $.each(cmd.variants, function(i, variant) {
  12872. var item = variant === '|' ? '<div class="elfinder-contextmenu-separator"/>' :
  12873. $('<div class="'+cmItem+' '+smItem+'"><span>'+variant[1]+'</span></div>').data('exec', variant[0]),
  12874. iconClass, icon;
  12875. if (typeof variant[2] !== 'undefined') {
  12876. icon = $('<span/>').addClass('elfinder-button-icon elfinder-contextmenu-icon');
  12877. if (! /\//.test(variant[2])) {
  12878. icon.addClass('elfinder-button-icon-'+variant[2]);
  12879. } else {
  12880. icon.css(urlIcon(variant[2]));
  12881. }
  12882. item.prepend(icon).addClass(smItem+'-icon');
  12883. }
  12884. submenu.append(item);
  12885. });
  12886. } else {
  12887. node = item(cmd.title, cmd.className? cmd.className : cmd.name, function() {
  12888. if (! menu.data('draged')) {
  12889. !cmd.keepContextmenu && close();
  12890. fm.exec(cmd.name, targets, {_userAction: true, _currentType: type, _currentNode: node});
  12891. }
  12892. }, cmd.contextmenuOpts);
  12893. if (cmd.extra && cmd.extra.node) {
  12894. $('<span class="elfinder-button-icon elfinder-button-icon-'+(cmd.extra.icon || '')+' '+exIcon+'"/>')
  12895. .append(cmd.extra.node).appendTo(node);
  12896. $(cmd.extra.node).trigger('ready', {targets: targets});
  12897. } else {
  12898. node.remove('.'+exIcon);
  12899. }
  12900. }
  12901. if (cmd.extendsCmd) {
  12902. node.children('span.elfinder-button-icon').addClass('elfinder-button-icon-' + cmd.extendsCmd);
  12903. }
  12904. if (insSep) {
  12905. menu.append('<div class="elfinder-contextmenu-separator"/>');
  12906. }
  12907. menu.append(node);
  12908. sep = true;
  12909. insSep = false;
  12910. }
  12911. if (cmd && typeof cmd.__disabled !== 'undefined') {
  12912. cmd._disabled = cmd.__disabled;
  12913. delete cmd.__disabled;
  12914. $.each(cmd.linkedCmds, function(i, n) {
  12915. var c;
  12916. if (c = fm.getCommand(n)) {
  12917. c._disabled = c.__disabled;
  12918. delete c.__disabled;
  12919. }
  12920. });
  12921. }
  12922. });
  12923. nodes = menu.children('div.'+cmItem);
  12924. },
  12925. createFromRaw = function(raw) {
  12926. currentType = 'raw';
  12927. $.each(raw, function(i, data) {
  12928. var node;
  12929. if (data === '|') {
  12930. menu.append('<div class="elfinder-contextmenu-separator"/>');
  12931. } else if (data.label && typeof data.callback == 'function') {
  12932. node = item(data.label, data.icon, function() {
  12933. if (! menu.data('draged')) {
  12934. !data.remain && close();
  12935. data.callback();
  12936. }
  12937. }, data.options || null);
  12938. menu.append(node);
  12939. }
  12940. });
  12941. nodes = menu.children('div.'+cmItem);
  12942. },
  12943. currentType = null,
  12944. currentTargets = null;
  12945. fm.one('load', function() {
  12946. base = fm.getUI();
  12947. cwd = fm.getUI('cwd');
  12948. fm.bind('contextmenu', function(e) {
  12949. var data = e.data,
  12950. css = {},
  12951. prevNode;
  12952. if (data.type && data.type !== 'files') {
  12953. cwd.trigger('unselectall');
  12954. }
  12955. close();
  12956. if (data.type && data.targets) {
  12957. fm.trigger('contextmenucreate', data);
  12958. create(data.type, data.targets);
  12959. fm.trigger('contextmenucreatedone', data);
  12960. } else if (data.raw) {
  12961. createFromRaw(data.raw);
  12962. }
  12963. if (menu.children().length) {
  12964. prevNode = data.prevNode || null;
  12965. if (prevNode) {
  12966. menu.data('prevNode', menu.prev());
  12967. prevNode.after(menu);
  12968. }
  12969. if (data.fitHeight) {
  12970. css = {maxHeight: Math.min(fm.getUI().height(), $(window).height()), overflowY: 'auto'};
  12971. menu.draggable('destroy').removeClass('ui-draggable');
  12972. }
  12973. open(data.x, data.y, css);
  12974. // call opened callback function
  12975. if (data.opened && typeof data.opened === 'function') {
  12976. data.opened.call(menu);
  12977. }
  12978. }
  12979. })
  12980. .one('destroy', function() { menu.remove(); })
  12981. .bind('disable', close)
  12982. .bind('select', function(e){
  12983. (currentType === 'files' && (!e.data || e.data.selected.toString() !== currentTargets.toString())) && close();
  12984. });
  12985. })
  12986. .shortcut({
  12987. pattern : fm.OS === 'mac' ? 'ctrl+m' : 'contextmenu shift+f10',
  12988. description : 'contextmenu',
  12989. callback : function(e) {
  12990. e.stopPropagation();
  12991. e.preventDefault();
  12992. $(document).one('contextmenu.' + fm.namespace, function(e) {
  12993. e.preventDefault();
  12994. e.stopPropagation();
  12995. });
  12996. var sel = fm.selected(),
  12997. type, targets, pos, elm;
  12998. if (sel.length) {
  12999. type = 'files';
  13000. targets = sel;
  13001. elm = $('#'+fm.cwdHash2Id(sel[0]));
  13002. } else {
  13003. type = 'cwd';
  13004. targets = [ fm.cwd().hash ];
  13005. pos = fm.getUI('workzone').offset();
  13006. }
  13007. if (! elm || ! elm.length) {
  13008. elm = fm.getUI('workzone');
  13009. }
  13010. pos = elm.offset();
  13011. pos.top += (elm.height() / 2);
  13012. pos.left += (elm.width() / 2);
  13013. fm.trigger('contextmenu', {
  13014. 'type' : type,
  13015. 'targets' : targets,
  13016. 'x' : pos.left,
  13017. 'y' : pos.top
  13018. });
  13019. }
  13020. });
  13021. });
  13022. };
  13023. /*
  13024. * File: /js/ui/cwd.js
  13025. */
  13026. /**
  13027. * elFinder current working directory ui.
  13028. *
  13029. * @author Dmitry (dio) Levashov
  13030. **/
  13031. $.fn.elfindercwd = function(fm, options) {
  13032. this.not('.elfinder-cwd').each(function() {
  13033. // fm.time('cwdLoad');
  13034. var mobile = fm.UA.Mobile,
  13035. list = fm.viewType == 'list',
  13036. undef = 'undefined',
  13037. /**
  13038. * Select event full name
  13039. *
  13040. * @type String
  13041. **/
  13042. evtSelect = 'select.'+fm.namespace,
  13043. /**
  13044. * Unselect event full name
  13045. *
  13046. * @type String
  13047. **/
  13048. evtUnselect = 'unselect.'+fm.namespace,
  13049. /**
  13050. * Disable event full name
  13051. *
  13052. * @type String
  13053. **/
  13054. evtDisable = 'disable.'+fm.namespace,
  13055. /**
  13056. * Disable event full name
  13057. *
  13058. * @type String
  13059. **/
  13060. evtEnable = 'enable.'+fm.namespace,
  13061. c = 'class',
  13062. /**
  13063. * File css class
  13064. *
  13065. * @type String
  13066. **/
  13067. clFile = fm.res(c, 'cwdfile'),
  13068. /**
  13069. * Selected css class
  13070. *
  13071. * @type String
  13072. **/
  13073. fileSelector = '.'+clFile,
  13074. /**
  13075. * Selected css class
  13076. *
  13077. * @type String
  13078. **/
  13079. clSelected = 'ui-selected',
  13080. /**
  13081. * Disabled css class
  13082. *
  13083. * @type String
  13084. **/
  13085. clDisabled = fm.res(c, 'disabled'),
  13086. /**
  13087. * Draggable css class
  13088. *
  13089. * @type String
  13090. **/
  13091. clDraggable = fm.res(c, 'draggable'),
  13092. /**
  13093. * Droppable css class
  13094. *
  13095. * @type String
  13096. **/
  13097. clDroppable = fm.res(c, 'droppable'),
  13098. /**
  13099. * Hover css class
  13100. *
  13101. * @type String
  13102. **/
  13103. clHover = fm.res(c, 'hover'),
  13104. /**
  13105. * Active css class
  13106. *
  13107. * @type String
  13108. **/
  13109. clActive = fm.res(c, 'active'),
  13110. /**
  13111. * Hover css class
  13112. *
  13113. * @type String
  13114. **/
  13115. clDropActive = fm.res(c, 'adroppable'),
  13116. /**
  13117. * Css class for temporary nodes (for mkdir/mkfile) commands
  13118. *
  13119. * @type String
  13120. **/
  13121. clTmp = clFile+'-tmp',
  13122. /**
  13123. * Select checkbox css class
  13124. *
  13125. * @type String
  13126. */
  13127. clSelChk = 'elfinder-cwd-selectchk',
  13128. /**
  13129. * Number of thumbnails to load in one request (new api only)
  13130. *
  13131. * @type Number
  13132. **/
  13133. tmbNum = fm.options.loadTmbs > 0 ? fm.options.loadTmbs : 5,
  13134. /**
  13135. * Current search query.
  13136. *
  13137. * @type String
  13138. */
  13139. query = '',
  13140. /**
  13141. * Currect clipboard(cut) hashes as object key
  13142. *
  13143. * @type Object
  13144. */
  13145. clipCuts = {},
  13146. /**
  13147. * Parents hashes of cwd
  13148. *
  13149. * @type Array
  13150. */
  13151. cwdParents = [],
  13152. /**
  13153. * cwd current hashes
  13154. *
  13155. * @type Array
  13156. */
  13157. cwdHashes = [],
  13158. /**
  13159. * incsearch current hashes
  13160. *
  13161. * @type Array
  13162. */
  13163. incHashes = void 0,
  13164. /**
  13165. * Custom columns name and order
  13166. *
  13167. * @type Array
  13168. */
  13169. customCols = [],
  13170. /**
  13171. * Current clicked element id of first time for dblclick
  13172. *
  13173. * @type String
  13174. */
  13175. curClickId = '',
  13176. /**
  13177. * Custom columns builder
  13178. *
  13179. * @type Function
  13180. */
  13181. customColsBuild = function() {
  13182. var cols = '';
  13183. for (var i = 0; i < customCols.length; i++) {
  13184. cols += '<td class="elfinder-col-'+customCols[i]+'">{' + customCols[i] + '}</td>';
  13185. }
  13186. return cols;
  13187. },
  13188. /**
  13189. * Make template.row from customCols
  13190. *
  13191. * @type Function
  13192. */
  13193. makeTemplateRow = function() {
  13194. 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>';
  13195. },
  13196. 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>' : '',
  13197. colResizing = false,
  13198. colWidth = null,
  13199. /**
  13200. * File templates
  13201. *
  13202. * @type Object
  13203. **/
  13204. templates = {
  13205. 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>',
  13206. row : ''
  13207. },
  13208. permsTpl = fm.res('tpl', 'perms'),
  13209. lockTpl = fm.res('tpl', 'lock'),
  13210. symlinkTpl = fm.res('tpl', 'symlink'),
  13211. /**
  13212. * Template placeholders replacement rules
  13213. *
  13214. * @type Object
  13215. **/
  13216. replacement = {
  13217. id : function(f) {
  13218. return fm.cwdHash2Id(f.hash);
  13219. },
  13220. name : function(f) {
  13221. var name = fm.escape(f.i18 || f.name);
  13222. !list && (name = name.replace(/([_.])/g, '&#8203;$1'));
  13223. return name;
  13224. },
  13225. nametitle : function(f) {
  13226. return fm.escape(f.i18 || f.name);
  13227. },
  13228. permsclass : function(f) {
  13229. return fm.perms2class(f);
  13230. },
  13231. perm : function(f) {
  13232. return fm.formatPermissions(f);
  13233. },
  13234. dirclass : function(f) {
  13235. var cName = f.mime == 'directory' ? 'directory' : '';
  13236. f.isroot && (cName += ' isroot');
  13237. f.csscls && (cName += ' ' + fm.escape(f.csscls));
  13238. options.getClass && (cName += ' ' + options.getClass(f));
  13239. return cName;
  13240. },
  13241. style : function(f) {
  13242. return f.icon? fm.getIconStyle(f) : '';
  13243. },
  13244. mime : function(f) {
  13245. var cName = fm.mime2class(f.mime);
  13246. f.icon && (cName += ' elfinder-cwd-bgurl');
  13247. return cName;
  13248. },
  13249. size : function(f) {
  13250. return (f.mime === 'directory' && !f.size)? '-' : fm.formatSize(f.size);
  13251. },
  13252. date : function(f) {
  13253. return fm.formatDate(f);
  13254. },
  13255. kind : function(f) {
  13256. return fm.mime2kind(f);
  13257. },
  13258. mode : function(f) {
  13259. return f.perm? fm.formatFileMode(f.perm) : '';
  13260. },
  13261. modestr : function(f) {
  13262. return f.perm? fm.formatFileMode(f.perm, 'string') : '';
  13263. },
  13264. modeoct : function(f) {
  13265. return f.perm? fm.formatFileMode(f.perm, 'octal') : '';
  13266. },
  13267. modeboth : function(f) {
  13268. return f.perm? fm.formatFileMode(f.perm, 'both') : '';
  13269. },
  13270. marker : function(f) {
  13271. return (f.alias || f.mime == 'symlink-broken' ? symlinkTpl : '')+(!f.read || !f.write ? permsTpl : '')+(f.locked ? lockTpl : '');
  13272. },
  13273. tooltip : function(f) {
  13274. var title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''),
  13275. info = '';
  13276. if (query && f.path) {
  13277. info = fm.escape(f.path.replace(/\/[^\/]*$/, ''));
  13278. } else {
  13279. info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, '&#13;') : '';
  13280. }
  13281. if (list) {
  13282. info += (info? '&#13;' : '') + fm.escape(f.i18 || f.name);
  13283. }
  13284. return info? info + '&#13;' + title : title;
  13285. }
  13286. },
  13287. /**
  13288. * Type badge CSS added flag
  13289. *
  13290. * @type Object
  13291. */
  13292. addedBadges = {},
  13293. /**
  13294. * Type badge style sheet element
  13295. *
  13296. * @type Object
  13297. */
  13298. addBadgeStyleSheet,
  13299. /**
  13300. * Add type badge CSS into 'head'
  13301. *
  13302. * @type Fundtion
  13303. */
  13304. addBadgeStyle = function(mime, name) {
  13305. var sel, ext, type;
  13306. if (mime && ! addedBadges[mime]) {
  13307. if (typeof addBadgeStyleSheet === 'undefined') {
  13308. if ($('#elfinderAddBadgeStyle'+fm.namespace).length) {
  13309. $('#elfinderAddBadgeStyle'+fm.namespace).remove();
  13310. }
  13311. addBadgeStyleSheet = $('<style id="addBadgeStyle'+fm.namespace+'"/>').insertBefore($('head').children(':first')).get(0).sheet || null;
  13312. }
  13313. if (addBadgeStyleSheet) {
  13314. mime = mime.toLowerCase();
  13315. type = mime.split('/');
  13316. ext = fm.escape(fm.mimeTypes[mime] || (name.replace(/.bac?k$/i, '').match(/\.([^.]+)$/) || ['',''])[1]);
  13317. if (ext) {
  13318. sel = '.elfinder-cwd-icon-' + type[0].replace(/(\.|\+)/g, '-');
  13319. if (typeof type[1] !== 'undefined') {
  13320. sel += '.elfinder-cwd-icon-' + type[1].replace(/(\.|\+)/g, '-');
  13321. }
  13322. try {
  13323. addBadgeStyleSheet.insertRule(sel + ':before{content:"' + ext.toLowerCase() + '"}', 0);
  13324. } catch(e) {}
  13325. }
  13326. addedBadges[mime] = true;
  13327. }
  13328. }
  13329. },
  13330. /**
  13331. * Return file html
  13332. *
  13333. * @param Object file info
  13334. * @return String
  13335. **/
  13336. itemhtml = function(f) {
  13337. f.mime && f.mime !== 'directory' && !addedBadges[f.mime] && addBadgeStyle(f.mime, f.name);
  13338. return templates[list ? 'row' : 'icon']
  13339. .replace(/\{([a-z0-9_]+)\}/g, function(s, e) {
  13340. return replacement[e] ? replacement[e](f, fm) : (f[e] ? f[e] : '');
  13341. });
  13342. },
  13343. /**
  13344. * jQueery node that will be selected next
  13345. *
  13346. * @type Object jQuery node
  13347. */
  13348. selectedNext = $(),
  13349. /**
  13350. * Flag. Required for msie to avoid unselect files on dragstart
  13351. *
  13352. * @type Boolean
  13353. **/
  13354. selectLock = false,
  13355. /**
  13356. * Move selection to prev/next file
  13357. *
  13358. * @param String move direction
  13359. * @param Boolean append to current selection
  13360. * @return void
  13361. * @rise select
  13362. */
  13363. select = function(keyCode, append) {
  13364. var code = $.ui.keyCode,
  13365. prev = keyCode == code.LEFT || keyCode == code.UP,
  13366. sel = cwd.find('[id].'+clSelected),
  13367. selector = prev ? 'first:' : 'last',
  13368. s, n, sib, top, left;
  13369. function sibling(n, direction) {
  13370. return n[direction+'All']('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):first');
  13371. }
  13372. if (sel.length) {
  13373. s = sel.filter(prev ? ':first' : ':last');
  13374. sib = sibling(s, prev ? 'prev' : 'next');
  13375. if (!sib.length) {
  13376. // there is no sibling on required side - do not move selection
  13377. n = s;
  13378. } else if (list || keyCode == code.LEFT || keyCode == code.RIGHT) {
  13379. // find real prevoius file
  13380. n = sib;
  13381. } else {
  13382. // find up/down side file in icons view
  13383. top = s.position().top;
  13384. left = s.position().left;
  13385. n = s;
  13386. if (prev) {
  13387. do {
  13388. n = n.prev('[id]');
  13389. } while (n.length && !(n.position().top < top && n.position().left <= left));
  13390. if (n.hasClass(clDisabled)) {
  13391. n = sibling(n, 'next');
  13392. }
  13393. } else {
  13394. do {
  13395. n = n.next('[id]');
  13396. } while (n.length && !(n.position().top > top && n.position().left >= left));
  13397. if (n.hasClass(clDisabled)) {
  13398. n = sibling(n, 'prev');
  13399. }
  13400. // there is row before last one - select last file
  13401. if (!n.length) {
  13402. sib = cwd.find('[id]:not(.'+clDisabled+'):last');
  13403. if (sib.position().top > top) {
  13404. n = sib;
  13405. }
  13406. }
  13407. }
  13408. }
  13409. // !append && unselectAll();
  13410. } else {
  13411. if (selectedNext.length) {
  13412. n = prev? selectedNext.prev() : selectedNext;
  13413. } else {
  13414. // there are no selected file - select first/last one
  13415. n = cwd.find('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):'+(prev ? 'last' : 'first'));
  13416. }
  13417. }
  13418. if (n && n.length && !n.hasClass('elfinder-cwd-parent')) {
  13419. if (s && append) {
  13420. // append new files to selected
  13421. n = s.add(s[prev ? 'prevUntil' : 'nextUntil']('#'+n.attr('id'))).add(n);
  13422. } else {
  13423. // unselect selected files
  13424. sel.trigger(evtUnselect);
  13425. }
  13426. // select file(s)
  13427. n.trigger(evtSelect);
  13428. // set its visible
  13429. scrollToView(n.filter(prev ? ':first' : ':last'));
  13430. // update cache/view
  13431. trigger();
  13432. }
  13433. },
  13434. selectedFiles = {},
  13435. selectFile = function(hash) {
  13436. $('#'+fm.cwdHash2Id(hash)).trigger(evtSelect);
  13437. },
  13438. allSelected = false,
  13439. selectAll = function() {
  13440. var phash = fm.cwd().hash;
  13441. selectCheckbox && selectAllCheckbox.find('input').prop('checked', true);
  13442. fm.lazy(function() {
  13443. var files;
  13444. if (fm.maxTargets && (incHashes || cwdHashes).length > fm.maxTargets) {
  13445. unselectAll({ notrigger: true });
  13446. files = $.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null; });
  13447. files = files.slice(0, fm.maxTargets);
  13448. selectedFiles = {};
  13449. $.each(files, function(i, v) {
  13450. selectedFiles[v.hash] = true;
  13451. $('#'+fm.cwdHash2Id(v.hash)).trigger(evtSelect);
  13452. });
  13453. fm.toast({mode: 'warning', msg: fm.i18n(['errMaxTargets', fm.maxTargets])});
  13454. } else {
  13455. cwd.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').trigger(evtSelect);
  13456. selectedFiles = fm.arrayFlip(incHashes || cwdHashes, true);
  13457. }
  13458. trigger();
  13459. selectCheckbox && selectAllCheckbox.data('pending', false);
  13460. }, 0, {repaint: true});
  13461. },
  13462. /**
  13463. * Unselect all files
  13464. *
  13465. * @param Object options
  13466. * @return void
  13467. */
  13468. unselectAll = function(opts) {
  13469. var o = opts || {};
  13470. selectCheckbox && selectAllCheckbox.find('input').prop('checked', false);
  13471. if (Object.keys(selectedFiles).length) {
  13472. selectLock = false;
  13473. selectedFiles = {};
  13474. cwd.find('[id].'+clSelected).trigger(evtUnselect);
  13475. selectCheckbox && cwd.find('input:checkbox.'+clSelChk).prop('checked', false);
  13476. }
  13477. !o.notrigger && trigger();
  13478. selectCheckbox && selectAllCheckbox.data('pending', false);
  13479. cwd.removeClass('elfinder-cwd-allselected');
  13480. },
  13481. selectInvert = function() {
  13482. var invHashes = {};
  13483. if (allSelected) {
  13484. unselectAll();
  13485. } else if (! Object.keys(selectedFiles).length) {
  13486. selectAll();
  13487. } else {
  13488. $.each((incHashes || cwdHashes), function(i, h) {
  13489. var itemNode = $('#'+fm.cwdHash2Id(h));
  13490. if (! selectedFiles[h]) {
  13491. invHashes[h] = true;
  13492. itemNode.length && itemNode.trigger(evtSelect);
  13493. } else {
  13494. itemNode.length && itemNode.trigger(evtUnselect);
  13495. }
  13496. });
  13497. selectedFiles = invHashes;
  13498. trigger();
  13499. }
  13500. },
  13501. /**
  13502. * Return selected files hashes list
  13503. *
  13504. * @return Array
  13505. */
  13506. selected = function() {
  13507. return Object.keys(selectedFiles);
  13508. },
  13509. /**
  13510. * Last selected node id
  13511. *
  13512. * @type String|Void
  13513. */
  13514. lastSelect = void 0,
  13515. /**
  13516. * Fire elfinder "select" event and pass selected files to it
  13517. *
  13518. * @return void
  13519. */
  13520. trigger = function() {
  13521. var selected = Object.keys(selectedFiles),
  13522. opts = {
  13523. selected : selected,
  13524. origin : 'cwd'
  13525. };
  13526. if (oldSchoolItem && (selected.length > 1 || selected[0] !== fm.cwdId2Hash(
  13527. oldSchoolItem.attr('id'))) && oldSchoolItem.hasClass(clSelected)) {
  13528. oldSchoolItem.trigger(evtUnselect);
  13529. }
  13530. allSelected = selected.length && (selected.length === (incHashes || cwdHashes).length) && (!fm.maxTargets || selected.length <= fm.maxTargets);
  13531. if (selectCheckbox) {
  13532. selectAllCheckbox.find('input').prop('checked', allSelected);
  13533. cwd[allSelected? 'addClass' : 'removeClass']('elfinder-cwd-allselected');
  13534. }
  13535. if (allSelected) {
  13536. opts.selectall = true;
  13537. } else if (! selected.length) {
  13538. opts.unselectall = true;
  13539. }
  13540. fm.trigger('select', opts);
  13541. },
  13542. /**
  13543. * Scroll file to set it visible
  13544. *
  13545. * @param DOMElement file/dir node
  13546. * @return void
  13547. */
  13548. scrollToView = function(o, blink) {
  13549. if (! o.length) {
  13550. return;
  13551. }
  13552. var ftop = o.position().top,
  13553. fheight = o.outerHeight(true),
  13554. wtop = wrapper.scrollTop(),
  13555. wheight = wrapper.get(0).clientHeight,
  13556. thheight = tableHeader? tableHeader.outerHeight(true) : 0;
  13557. if (ftop + thheight + fheight > wtop + wheight) {
  13558. wrapper.scrollTop(parseInt(ftop + thheight + fheight - wheight));
  13559. } else if (ftop < wtop) {
  13560. wrapper.scrollTop(ftop);
  13561. }
  13562. list && wrapper.scrollLeft(0);
  13563. !!blink && fm.resources.blink(o, 'lookme');
  13564. },
  13565. /**
  13566. * Files we get from server but not show yet
  13567. *
  13568. * @type Array
  13569. **/
  13570. buffer = [],
  13571. /**
  13572. * Extra data of buffer
  13573. *
  13574. * @type Object
  13575. **/
  13576. bufferExt = {},
  13577. /**
  13578. * Return index of elements with required hash in buffer
  13579. *
  13580. * @param String file hash
  13581. * @return Number
  13582. */
  13583. index = function(hash) {
  13584. var l = buffer.length;
  13585. while (l--) {
  13586. if (buffer[l].hash == hash) {
  13587. return l;
  13588. }
  13589. }
  13590. return -1;
  13591. },
  13592. /**
  13593. * Scroll start event name
  13594. *
  13595. * @type String
  13596. **/
  13597. scrollStartEvent = 'elfscrstart',
  13598. /**
  13599. * Scroll stop event name
  13600. *
  13601. * @type String
  13602. **/
  13603. scrollEvent = 'elfscrstop',
  13604. scrolling = false,
  13605. /**
  13606. * jQuery UI selectable option
  13607. *
  13608. * @type Object
  13609. */
  13610. selectableOption = {
  13611. disabled : true,
  13612. filter : '[id]:first',
  13613. stop : trigger,
  13614. delay : 250,
  13615. appendTo : 'body',
  13616. autoRefresh: false,
  13617. selected : function(e, ui) { $(ui.selected).trigger(evtSelect); },
  13618. unselected : function(e, ui) { $(ui.unselected).trigger(evtUnselect); }
  13619. },
  13620. /**
  13621. * hashes of items displayed in current view
  13622. *
  13623. * @type Object ItemHash => DomId
  13624. */
  13625. inViewHashes = {},
  13626. /**
  13627. * Processing when the current view is changed (On open, search, scroll, resize etc.)
  13628. *
  13629. * @return void
  13630. */
  13631. wrapperRepaint = function(init, recnt) {
  13632. var firstNode = (list? cwd.find('tbody:first') : cwd).children('[id]:first');
  13633. if (!firstNode.length) {
  13634. return;
  13635. }
  13636. var selectable = cwd.data('selectable'),
  13637. rec = (function() {
  13638. var wos = wrapper.offset(),
  13639. w = $(window),
  13640. x = (firstNode.width() / 2) * (!list && options.oldSchool? 3 : 1),
  13641. l = wos.left - w.scrollLeft() + (fm.direction === 'ltr'? x : wrapper.width() - x),
  13642. t = wos.top - w.scrollTop() + 10 + (list? (bufferExt.itemH * (options.oldSchool? 2 : 1)) || (fm.UA.Touch? 36 : 24) : 0);
  13643. return {left: Math.max(0, Math.round(l)), top: Math.max(0, Math.round(t))};
  13644. })(),
  13645. tgt = init? firstNode : $(document.elementFromPoint(rec.left , rec.top)),
  13646. ids = {},
  13647. tmbs = {},
  13648. multi = 5,
  13649. cnt = Math.ceil((bufferExt.hpi? Math.ceil((wz.data('rectangle').height / bufferExt.hpi) * 1.5) : showFiles) / multi),
  13650. chk = function() {
  13651. var id, hash, file, i;
  13652. for (i = 0; i < multi; i++) {
  13653. id = tgt.attr('id');
  13654. if (id) {
  13655. bufferExt.getTmbs = [];
  13656. hash = fm.cwdId2Hash(id);
  13657. inViewHashes[hash] = id;
  13658. // for tmbs
  13659. if (bufferExt.attachTmbs[hash]) {
  13660. tmbs[hash] = bufferExt.attachTmbs[hash];
  13661. }
  13662. // for selectable
  13663. selectable && (ids[id] = true);
  13664. }
  13665. // next node
  13666. tgt = tgt.next();
  13667. if (!tgt.length) {
  13668. break;
  13669. }
  13670. }
  13671. },
  13672. done = function() {
  13673. var idsArr;
  13674. if (cwd.data('selectable')) {
  13675. Object.assign(ids, selectedFiles);
  13676. idsArr = Object.keys(ids);
  13677. if (idsArr.length) {
  13678. selectableOption.filter = '#'+idsArr.join(', #');
  13679. cwd.selectable('enable').selectable('option', {filter : selectableOption.filter}).selectable('refresh');
  13680. }
  13681. }
  13682. if (Object.keys(tmbs).length) {
  13683. bufferExt.getTmbs = [];
  13684. attachThumbnails(tmbs);
  13685. }
  13686. },
  13687. setTarget = function() {
  13688. if (!tgt.hasClass(clFile)) {
  13689. tgt = tgt.closest(fileSelector);
  13690. }
  13691. },
  13692. arr, widget;
  13693. inViewHashes = {};
  13694. selectable && cwd.selectable('option', 'disabled');
  13695. if (tgt.length) {
  13696. if (!tgt.hasClass(clFile) && !tgt.closest(fileSelector).length) {
  13697. // dialog, serach button etc.
  13698. widget = fm.getUI().find('.ui-dialog:visible,.ui-widget:visible');
  13699. if (widget.length) {
  13700. widget.hide();
  13701. tgt = $(document.elementFromPoint(rec.left , rec.top));
  13702. widget.show();
  13703. } else {
  13704. widget = null;
  13705. }
  13706. }
  13707. setTarget();
  13708. if (!tgt.length) {
  13709. // try search 5px down
  13710. widget && widget.hide();
  13711. tgt = $(document.elementFromPoint(rec.left , rec.top + 5));
  13712. widget && widget.show();
  13713. setTarget();
  13714. }
  13715. }
  13716. if (tgt.length) {
  13717. if (tgt.attr('id')) {
  13718. if (init) {
  13719. for (var i = 0; i < cnt; i++) {
  13720. chk();
  13721. if (! tgt.length) {
  13722. break;
  13723. }
  13724. }
  13725. done();
  13726. } else {
  13727. bufferExt.repaintJob && bufferExt.repaintJob.state() === 'pending' && bufferExt.repaintJob.reject();
  13728. arr = new Array(cnt);
  13729. bufferExt.repaintJob = fm.asyncJob(function() {
  13730. chk();
  13731. if (! tgt.length) {
  13732. done();
  13733. bufferExt.repaintJob && bufferExt.repaintJob.state() === 'pending' && bufferExt.repaintJob.reject();
  13734. }
  13735. }, arr).done(done);
  13736. }
  13737. }
  13738. } else if (init && bufferExt.renderd) {
  13739. // In initial request, cwd DOM not renderd so doing lazy check
  13740. recnt = recnt || 0;
  13741. if (recnt < 10) { // Prevent infinite loop
  13742. requestAnimationFrame(function() {
  13743. wrapperRepaint(init, ++recnt);
  13744. });
  13745. }
  13746. }
  13747. },
  13748. /**
  13749. * Item node of oldScholl ".."
  13750. */
  13751. oldSchoolItem = null,
  13752. /**
  13753. * display parent folder with ".." name
  13754. *
  13755. * @param String phash
  13756. * @return void
  13757. */
  13758. oldSchool = function(p) {
  13759. var phash = fm.cwd().phash,
  13760. pdir = fm.file(phash) || null,
  13761. set = function(pdir) {
  13762. if (pdir) {
  13763. oldSchoolItem = $(itemhtml($.extend(true, {}, pdir, {name : '..', i18 : '..', mime : 'directory'})))
  13764. .addClass('elfinder-cwd-parent')
  13765. .on('dblclick', function() {
  13766. var hash = fm.cwdId2Hash(this.id);
  13767. fm.trigger('select', {selected : [hash]}).exec('open', hash);
  13768. });
  13769. (list ? oldSchoolItem.children('td:first') : oldSchoolItem).children('.elfinder-cwd-select').remove();
  13770. (list ? cwd.find('tbody') : cwd).prepend(oldSchoolItem);
  13771. }
  13772. };
  13773. if (pdir) {
  13774. set(pdir);
  13775. } else {
  13776. if (fm.getUI('tree').length) {
  13777. fm.one('parents', function() {
  13778. set(fm.file(phash) || null);
  13779. wrapper.trigger(scrollEvent);
  13780. });
  13781. } else {
  13782. fm.request({
  13783. data : {cmd : 'parents', target : fm.cwd().hash},
  13784. preventFail : true
  13785. })
  13786. .done(function(data) {
  13787. set(fm.file(phash) || null);
  13788. wrapper.trigger(scrollEvent);
  13789. });
  13790. }
  13791. }
  13792. },
  13793. showFiles = fm.options.showFiles,
  13794. /**
  13795. * Cwd scroll event handler.
  13796. * Lazy load - append to cwd not shown files
  13797. *
  13798. * @return void
  13799. */
  13800. render = function() {
  13801. if (bufferExt.rendering || (bufferExt.renderd && ! buffer.length)) {
  13802. return;
  13803. }
  13804. var place = (list ? cwd.children('table').children('tbody') : cwd),
  13805. phash,
  13806. chk,
  13807. // created document fragment for jQuery >= 1.12, 2.2, 3.0
  13808. // see Studio-42/elFinder#1544 @ github
  13809. docFlag = $.htmlPrefilter? true : false,
  13810. tempDom = docFlag? $(document.createDocumentFragment()) : $('<div/>'),
  13811. go = function(o){
  13812. var over = o || null,
  13813. html = [],
  13814. dirs = false,
  13815. atmb = {},
  13816. stmb = (fm.option('tmbUrl') === 'self'),
  13817. init = bufferExt.renderd? false : true,
  13818. files, locks, selected;
  13819. files = buffer.splice(0, showFiles + (over || 0) / (bufferExt.hpi || 1));
  13820. bufferExt.renderd += files.length;
  13821. if (! buffer.length) {
  13822. bottomMarker.hide();
  13823. wrapper.off(scrollEvent, render);
  13824. }
  13825. locks = [];
  13826. html = $.map(files, function(f) {
  13827. if (f.hash && f.name) {
  13828. if (f.mime == 'directory') {
  13829. dirs = true;
  13830. }
  13831. if ((f.tmb && (f.tmb != 1 || f.size > 0)) || (stmb && f.mime.indexOf('image/') === 0)) {
  13832. atmb[f.hash] = f.tmb || 'self';
  13833. }
  13834. clipCuts[f.hash] && locks.push(f.hash);
  13835. return itemhtml(f);
  13836. }
  13837. return null;
  13838. });
  13839. // html into temp node
  13840. tempDom.empty().append(html.join(''));
  13841. // make directory droppable
  13842. dirs && !mobile && makeDroppable(tempDom);
  13843. // check selected items
  13844. selected = [];
  13845. if (Object.keys(selectedFiles).length) {
  13846. tempDom.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').each(function() {
  13847. selectedFiles[fm.cwdId2Hash(this.id)] && selected.push($(this));
  13848. });
  13849. }
  13850. // append to cwd
  13851. place.append(docFlag? tempDom : tempDom.children());
  13852. // trigger select
  13853. if (selected.length) {
  13854. $.each(selected, function(i, n) { n.trigger(evtSelect); });
  13855. trigger();
  13856. }
  13857. locks.length && fm.trigger('lockfiles', {files: locks});
  13858. !bufferExt.hpi && bottomMarkerShow(place, files.length);
  13859. if (list) {
  13860. // show thead
  13861. cwd.find('thead').show();
  13862. // fixed table header
  13863. fixTableHeader({fitWidth: ! colWidth});
  13864. }
  13865. if (Object.keys(atmb).length) {
  13866. Object.assign(bufferExt.attachTmbs, atmb);
  13867. }
  13868. if (init) {
  13869. if (! mobile && ! cwd.data('selectable')) {
  13870. // make files selectable
  13871. cwd.selectable(selectableOption).data('selectable', true);
  13872. }
  13873. wrapperRepaint(true);
  13874. }
  13875. ! scrolling && wrapper.trigger(scrollEvent);
  13876. };
  13877. if (! bufferExt.renderd) {
  13878. // first time to go()
  13879. bufferExt.rendering = true;
  13880. // scroll top on dir load to avoid scroll after page reload
  13881. wrapper.scrollTop(0);
  13882. phash = fm.cwd().phash;
  13883. go();
  13884. if (options.oldSchool) {
  13885. if (phash && !query) {
  13886. oldSchool(phash);
  13887. } else {
  13888. oldSchoolItem = $();
  13889. }
  13890. }
  13891. if (list) {
  13892. colWidth && setColwidth();
  13893. fixTableHeader({fitWidth: true});
  13894. }
  13895. bufferExt.itemH = (list? place.find('tr:first') : place.find('[id]:first')).outerHeight(true);
  13896. fm.trigger('cwdrender');
  13897. bufferExt.rendering = false;
  13898. }
  13899. if (! bufferExt.rendering && buffer.length) {
  13900. // next go()
  13901. if ((chk = (wrapper.height() + wrapper.scrollTop() + fm.options.showThreshold + bufferExt.row) - (bufferExt.renderd * bufferExt.hpi)) > 0) {
  13902. bufferExt.rendering = true;
  13903. fm.lazy(function() {
  13904. go(chk);
  13905. bufferExt.rendering = false;
  13906. });
  13907. } else {
  13908. !fm.enabled() && resize();
  13909. }
  13910. } else {
  13911. resize();
  13912. }
  13913. },
  13914. // fixed table header jQuery object
  13915. tableHeader = null,
  13916. // Is UA support CSS sticky
  13917. cssSticky = fm.UA.CSS.positionSticky && fm.UA.CSS.widthMaxContent,
  13918. // To fixed table header colmun
  13919. fixTableHeader = function(optsArg) {
  13920. if (! options.listView.fixedHeader) {
  13921. return;
  13922. }
  13923. var setPos = function() {
  13924. var val, pos;
  13925. pos = (fm.direction === 'ltr')? 'left' : 'right';
  13926. val = ((fm.direction === 'ltr')? wrapper.scrollLeft() : table.outerWidth(true) - wrapper.width() - wrapper.scrollLeft()) * -1;
  13927. if (base.css(pos) !== val) {
  13928. base.css(pos, val);
  13929. }
  13930. },
  13931. opts = optsArg || {},
  13932. cnt, base, table, htable, thead, tbody, hheight, htr, btr, htd, btd, htw, btw, init;
  13933. tbody = cwd.find('tbody');
  13934. btr = tbody.children('tr:first');
  13935. if (btr.length && btr.is(':visible')) {
  13936. table = tbody.parent();
  13937. if (! tableHeader) {
  13938. init = true;
  13939. tbody.addClass('elfinder-cwd-fixheader');
  13940. thead = cwd.find('thead').attr('id', fm.namespace+'-cwd-thead');
  13941. htr = thead.children('tr:first');
  13942. hheight = htr.outerHeight(true);
  13943. cwd.css('margin-top', hheight - parseInt(table.css('padding-top')));
  13944. if (cssSticky) {
  13945. tableHeader = $('<div class="elfinder-table-header-sticky"/>').addClass(cwd.attr('class')).append($('<table/>').append(thead));
  13946. cwd.after(tableHeader);
  13947. wrapper.on('resize.fixheader', function(e) {
  13948. e.stopPropagation();
  13949. fixTableHeader({fitWidth: true});
  13950. });
  13951. } else {
  13952. base = $('<div/>').addClass(cwd.attr('class')).append($('<table/>').append(thead));
  13953. tableHeader = $('<div/>').addClass(wrapper.attr('class') + ' elfinder-cwd-fixheader')
  13954. .removeClass('ui-droppable native-droppable')
  13955. .css(wrapper.position())
  13956. .css({ height: hheight, width: cwd.outerWidth() })
  13957. .append(base);
  13958. if (fm.direction === 'rtl') {
  13959. tableHeader.css('left', (wrapper.data('width') - wrapper.width()) + 'px');
  13960. }
  13961. setPos();
  13962. wrapper.after(tableHeader)
  13963. .on('scroll.fixheader resize.fixheader', function(e) {
  13964. setPos();
  13965. if (e.type === 'resize') {
  13966. e.stopPropagation();
  13967. tableHeader.css(wrapper.position());
  13968. wrapper.data('width', wrapper.css('overflow', 'hidden').width());
  13969. wrapper.css('overflow', 'auto');
  13970. fixTableHeader();
  13971. }
  13972. });
  13973. }
  13974. } else {
  13975. thead = $('#'+fm.namespace+'-cwd-thead');
  13976. htr = thead.children('tr:first');
  13977. }
  13978. if (init || opts.fitWidth || Math.abs(btr.outerWidth() - htr.outerWidth()) > 2) {
  13979. cnt = customCols.length + 1;
  13980. for (var i = 0; i < cnt; i++) {
  13981. htd = htr.children('td:eq('+i+')');
  13982. btd = btr.children('td:eq('+i+')');
  13983. htw = htd.width();
  13984. btw = btd.width();
  13985. if (typeof htd.data('delta') === 'undefined') {
  13986. htd.data('delta', (htd.outerWidth() - htw) - (btd.outerWidth() - btw));
  13987. }
  13988. btw -= htd.data('delta');
  13989. if (! init && ! opts.fitWidth && htw === btw) {
  13990. break;
  13991. }
  13992. htd.css('width', btw + 'px');
  13993. }
  13994. }
  13995. if (!cssSticky) {
  13996. tableHeader.data('widthTimer') && cancelAnimationFrame(tableHeader.data('widthTimer'));
  13997. tableHeader.data('widthTimer', requestAnimationFrame(function() {
  13998. if (tableHeader) {
  13999. tableHeader.css('width', mBoard.width() + 'px');
  14000. if (fm.direction === 'rtl') {
  14001. tableHeader.css('left', (wrapper.data('width') - wrapper.width()) + 'px');
  14002. }
  14003. }
  14004. }));
  14005. }
  14006. }
  14007. },
  14008. // Set colmun width
  14009. setColwidth = function() {
  14010. if (list && colWidth) {
  14011. var cl = 'elfinder-cwd-colwidth',
  14012. first = cwd.find('tr[id]:first'),
  14013. former;
  14014. if (! first.hasClass(cl)) {
  14015. former = cwd.find('tr.'+cl);
  14016. former.removeClass(cl).find('td').css('width', '');
  14017. first.addClass(cl);
  14018. cwd.find('table:first').css('table-layout', 'fixed');
  14019. $.each($.merge(['name'], customCols), function(i, k) {
  14020. var w = colWidth[k] || first.find('td.elfinder-col-'+k).width();
  14021. first.find('td.elfinder-col-'+k).width(w);
  14022. });
  14023. }
  14024. }
  14025. },
  14026. /**
  14027. * Droppable options for cwd.
  14028. * Drop target is `wrapper`
  14029. * Do not add class on childs file over
  14030. *
  14031. * @type Object
  14032. */
  14033. droppable = Object.assign({}, fm.droppable, {
  14034. over : function(e, ui) {
  14035. var dst = $(this),
  14036. helper = ui.helper,
  14037. ctr = (e.shiftKey || e.ctrlKey || e.metaKey),
  14038. hash, status, inParent;
  14039. e.stopPropagation();
  14040. helper.data('dropover', helper.data('dropover') + 1);
  14041. dst.data('dropover', true);
  14042. if (helper.data('namespace') !== fm.namespace || ! fm.insideWorkzone(e.pageX, e.pageY)) {
  14043. dst.removeClass(clDropActive);
  14044. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  14045. return;
  14046. }
  14047. if (dst.hasClass(fm.res(c, 'cwdfile'))) {
  14048. hash = fm.cwdId2Hash(dst.attr('id'));
  14049. dst.data('dropover', hash);
  14050. } else {
  14051. hash = fm.cwd().hash;
  14052. fm.cwd().write && dst.data('dropover', hash);
  14053. }
  14054. inParent = (fm.file(helper.data('files')[0]).phash === hash);
  14055. if (dst.data('dropover') === hash) {
  14056. $.each(helper.data('files'), function(i, h) {
  14057. if (h === hash || (inParent && !ctr && !helper.hasClass('elfinder-drag-helper-plus'))) {
  14058. dst.removeClass(clDropActive);
  14059. return false; // break $.each
  14060. }
  14061. });
  14062. } else {
  14063. dst.removeClass(clDropActive);
  14064. }
  14065. if (helper.data('locked') || inParent) {
  14066. status = 'elfinder-drag-helper-plus';
  14067. } else {
  14068. status = 'elfinder-drag-helper-move';
  14069. if (ctr) {
  14070. status += ' elfinder-drag-helper-plus';
  14071. }
  14072. }
  14073. dst.hasClass(clDropActive) && helper.addClass(status);
  14074. requestAnimationFrame(function(){ dst.hasClass(clDropActive) && helper.addClass(status); });
  14075. },
  14076. out : function(e, ui) {
  14077. var helper = ui.helper;
  14078. e.stopPropagation();
  14079. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  14080. $(this).removeData('dropover')
  14081. .removeClass(clDropActive);
  14082. },
  14083. deactivate : function() {
  14084. $(this).removeData('dropover')
  14085. .removeClass(clDropActive);
  14086. },
  14087. drop : function(e, ui) {
  14088. unselectAll({ notrigger: true });
  14089. fm.droppable.drop.call(this, e, ui);
  14090. }
  14091. }),
  14092. /**
  14093. * Make directory droppable
  14094. *
  14095. * @return void
  14096. */
  14097. makeDroppable = function(place) {
  14098. place = place? place : (list ? cwd.find('tbody') : cwd);
  14099. var targets = place.children('.directory:not(.'+clDroppable+',.elfinder-na,.elfinder-ro)');
  14100. if (fm.isCommandEnabled('paste')) {
  14101. targets.droppable(droppable);
  14102. }
  14103. if (fm.isCommandEnabled('upload')) {
  14104. targets.addClass('native-droppable');
  14105. }
  14106. place.children('.isroot').each(function(i, n) {
  14107. var $n = $(n),
  14108. hash = fm.cwdId2Hash(n.id);
  14109. if (fm.isCommandEnabled('paste', hash)) {
  14110. if (! $n.hasClass(clDroppable+',elfinder-na,elfinder-ro')) {
  14111. $n.droppable(droppable);
  14112. }
  14113. } else {
  14114. if ($n.hasClass(clDroppable)) {
  14115. $n.droppable('destroy');
  14116. }
  14117. }
  14118. if (fm.isCommandEnabled('upload', hash)) {
  14119. if (! $n.hasClass('native-droppable,elfinder-na,elfinder-ro')) {
  14120. $n.addClass('native-droppable');
  14121. }
  14122. } else {
  14123. if ($n.hasClass('native-droppable')) {
  14124. $n.removeClass('native-droppable');
  14125. }
  14126. }
  14127. });
  14128. },
  14129. /**
  14130. * Preload required thumbnails and on load add css to files.
  14131. * Return false if required file is not visible yet (in buffer) -
  14132. * required for old api to stop loading thumbnails.
  14133. *
  14134. * @param Object file hash -> thumbnail map
  14135. * @param Bool reload
  14136. * @return void
  14137. */
  14138. attachThumbnails = function(tmbs, reload) {
  14139. var attach = function(node, tmb) {
  14140. $('<img/>')
  14141. .on('load', function() {
  14142. node.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')");
  14143. })
  14144. .attr('src', tmb.url);
  14145. },
  14146. chk = function(hash, tmb) {
  14147. var node = $('#'+fm.cwdHash2Id(hash)),
  14148. file, tmbObj, reloads = [];
  14149. if (node.length) {
  14150. if (tmb != '1') {
  14151. file = fm.file(hash);
  14152. if (file.tmb !== tmb) {
  14153. file.tmb = tmb;
  14154. }
  14155. tmbObj = fm.tmb(file);
  14156. if (reload) {
  14157. node.find('.elfinder-cwd-icon').addClass(tmbObj.className).css('background-image', "url('"+tmbObj.url+"')");
  14158. } else {
  14159. attach(node, tmbObj);
  14160. }
  14161. delete bufferExt.attachTmbs[hash];
  14162. } else {
  14163. if (reload) {
  14164. loadThumbnails([hash]);
  14165. } else if (! bufferExt.tmbLoading[hash]) {
  14166. bufferExt.getTmbs.push(hash);
  14167. }
  14168. }
  14169. }
  14170. };
  14171. if ($.isPlainObject(tmbs) && Object.keys(tmbs).length) {
  14172. Object.assign(bufferExt.attachTmbs, tmbs);
  14173. $.each(tmbs, chk);
  14174. if (! reload && bufferExt.getTmbs.length && ! Object.keys(bufferExt.tmbLoading).length) {
  14175. loadThumbnails();
  14176. }
  14177. }
  14178. },
  14179. /**
  14180. * Load thumbnails from backend.
  14181. *
  14182. * @param Array|void reloads hashes list for reload thumbnail items
  14183. * @return void
  14184. */
  14185. loadThumbnails = function(reloads) {
  14186. var tmbs = [],
  14187. reload = false;
  14188. if (fm.oldAPI) {
  14189. fm.request({
  14190. data : {cmd : 'tmb', current : fm.cwd().hash},
  14191. preventFail : true
  14192. })
  14193. .done(function(data) {
  14194. if (data.images && Object.keys(data.images).length) {
  14195. attachThumbnails(data.images);
  14196. }
  14197. if (data.tmb) {
  14198. loadThumbnails();
  14199. }
  14200. });
  14201. return;
  14202. }
  14203. if (reloads) {
  14204. reload = true;
  14205. tmbs = reloads.splice(0, tmbNum);
  14206. } else {
  14207. tmbs = bufferExt.getTmbs.splice(0, tmbNum);
  14208. }
  14209. if (tmbs.length) {
  14210. if (reload || inViewHashes[tmbs[0]] || inViewHashes[tmbs[tmbs.length-1]]) {
  14211. $.each(tmbs, function(i, h) {
  14212. bufferExt.tmbLoading[h] = true;
  14213. });
  14214. fm.request({
  14215. data : {cmd : 'tmb', targets : tmbs},
  14216. preventFail : true
  14217. })
  14218. .done(function(data) {
  14219. var errs = [],
  14220. resLen;
  14221. if (data.images) {
  14222. if (resLen = Object.keys(data.images).length) {
  14223. if (resLen < tmbs.length) {
  14224. $.each(tmbs, function(i, h) {
  14225. if (! data.images[h]) {
  14226. errs.push(h);
  14227. }
  14228. });
  14229. }
  14230. attachThumbnails(data.images, reload);
  14231. } else {
  14232. errs = tmbs;
  14233. }
  14234. // unset error items from bufferExt.attachTmbs
  14235. if (errs.length) {
  14236. $.each(errs, function(i, h) {
  14237. delete bufferExt.attachTmbs[h];
  14238. });
  14239. }
  14240. }
  14241. if (reload) {
  14242. if (reloads.length) {
  14243. loadThumbnails(reloads);
  14244. }
  14245. }
  14246. })
  14247. .always(function() {
  14248. bufferExt.tmbLoading = {};
  14249. if (! reload && bufferExt.getTmbs.length) {
  14250. loadThumbnails();
  14251. }
  14252. });
  14253. }
  14254. }
  14255. },
  14256. /**
  14257. * Add new files to cwd/buffer
  14258. *
  14259. * @param Array new files
  14260. * @return void
  14261. */
  14262. add = function(files, mode) {
  14263. var place = list ? cwd.find('tbody') : cwd,
  14264. l = files.length,
  14265. atmb = {},
  14266. findNode = function(file) {
  14267. var pointer = cwd.find('[id]:first'), file2;
  14268. while (pointer.length) {
  14269. file2 = fm.file(fm.cwdId2Hash(pointer.attr('id')));
  14270. if (!pointer.hasClass('elfinder-cwd-parent') && file2 && fm.compare(file, file2) < 0) {
  14271. return pointer;
  14272. }
  14273. pointer = pointer.next('[id]');
  14274. }
  14275. },
  14276. findIndex = function(file) {
  14277. var l = buffer.length, i;
  14278. for (i =0; i < l; i++) {
  14279. if (fm.compare(file, buffer[i]) < 0) {
  14280. return i;
  14281. }
  14282. }
  14283. return l || -1;
  14284. },
  14285. // created document fragment for jQuery >= 1.12, 2.2, 3.0
  14286. // see Studio-42/elFinder#1544 @ github
  14287. docFlag = $.htmlPrefilter? true : false,
  14288. tempDom = docFlag? $(document.createDocumentFragment()) : $('<div/>'),
  14289. file, hash, node, nodes, ndx, stmb;
  14290. if (l > showFiles) {
  14291. // re-render for performance tune
  14292. content();
  14293. selectedFiles = fm.arrayFlip($.map(files, function(f) { return f.hash; }), true);
  14294. trigger();
  14295. } else {
  14296. // add the item immediately
  14297. l && wz.removeClass('elfinder-cwd-wrapper-empty');
  14298. // Self thumbnail
  14299. stmb = (fm.option('tmbUrl') === 'self');
  14300. while (l--) {
  14301. file = files[l];
  14302. hash = file.hash;
  14303. if ($('#'+fm.cwdHash2Id(hash)).length) {
  14304. continue;
  14305. }
  14306. if ((node = findNode(file)) && ! node.length) {
  14307. node = null;
  14308. }
  14309. if (! node && (ndx = findIndex(file)) >= 0) {
  14310. buffer.splice(ndx, 0, file);
  14311. } else {
  14312. tempDom.empty().append(itemhtml(file));
  14313. (file.mime === 'directory') && !mobile && makeDroppable(tempDom);
  14314. nodes = docFlag? tempDom : tempDom.children();
  14315. if (node) {
  14316. node.before(nodes);
  14317. } else {
  14318. place.append(nodes);
  14319. }
  14320. }
  14321. if ($('#'+fm.cwdHash2Id(hash)).length) {
  14322. if ((file.tmb && (file.tmb != 1 || file.size > 0)) || (stmb && file.mime.indexOf('image/') === 0)) {
  14323. atmb[hash] = file.tmb || 'self';
  14324. }
  14325. }
  14326. }
  14327. if (list) {
  14328. setColwidth();
  14329. fixTableHeader({fitWidth: ! colWidth});
  14330. }
  14331. bottomMarkerShow(place);
  14332. if (Object.keys(atmb).length) {
  14333. Object.assign(bufferExt.attachTmbs, atmb);
  14334. }
  14335. }
  14336. },
  14337. /**
  14338. * Remove files from cwd/buffer
  14339. *
  14340. * @param Array files hashes
  14341. * @return void
  14342. */
  14343. remove = function(files) {
  14344. var l = files.length,
  14345. inSearch = fm.searchStatus.state > 1,
  14346. curCmd = fm.getCommand(fm.currentReqCmd) || {},
  14347. hash, n, ndx, found;
  14348. // removed cwd
  14349. if (!fm.cwd().hash && !curCmd.noChangeDirOnRemovedCwd) {
  14350. $.each(cwdParents.reverse(), function(i, h) {
  14351. if (fm.file(h)) {
  14352. found = true;
  14353. fm.one(fm.currentReqCmd + 'done', function() {
  14354. !fm.cwd().hash && fm.exec('open', h);
  14355. });
  14356. return false;
  14357. }
  14358. });
  14359. // fallback to fm.roots[0]
  14360. !found && !fm.cwd().hash && fm.exec('open', fm.roots[Object.keys(fm.roots)[0]]);
  14361. return;
  14362. }
  14363. while (l--) {
  14364. hash = files[l];
  14365. if ((n = $('#'+fm.cwdHash2Id(hash))).length) {
  14366. try {
  14367. n.remove();
  14368. --bufferExt.renderd;
  14369. } catch(e) {
  14370. fm.debug('error', e);
  14371. }
  14372. } else if ((ndx = index(hash)) !== -1) {
  14373. buffer.splice(ndx, 1);
  14374. }
  14375. selectedFiles[hash] && delete selectedFiles[hash];
  14376. if (inSearch) {
  14377. if ((ndx = $.inArray(hash, cwdHashes)) !== -1) {
  14378. cwdHashes.splice(ndx, 1);
  14379. }
  14380. }
  14381. }
  14382. inSearch && fm.trigger('cwdhasheschange', cwdHashes);
  14383. if (list) {
  14384. setColwidth();
  14385. fixTableHeader({fitWidth: ! colWidth});
  14386. }
  14387. },
  14388. customColsNameBuild = function() {
  14389. var name = '',
  14390. customColsName = '';
  14391. for (var i = 0; i < customCols.length; i++) {
  14392. name = fm.getColumnName(customCols[i]);
  14393. customColsName +='<td class="elfinder-cwd-view-th-'+customCols[i]+' sortable-item">'+name+'</td>';
  14394. }
  14395. return customColsName;
  14396. },
  14397. setItemBoxSize = function(boxSize) {
  14398. var place, elm;
  14399. if (!boxSize.height) {
  14400. place = (list ? cwd.find('tbody') : cwd);
  14401. elm = place.find(list? 'tr:first' : '[id]:first');
  14402. boxSize.height = elm.outerHeight(true);
  14403. if (!list) {
  14404. boxSize.width = elm.outerWidth(true);
  14405. }
  14406. }
  14407. },
  14408. bottomMarkerShow = function(cur, cnt) {
  14409. var place = cur || (list ? cwd.find('tbody') : cwd),
  14410. boxSize = itemBoxSize[fm.viewType],
  14411. col = 1,
  14412. row;
  14413. if (buffer.length > 0) {
  14414. if (!bufferExt.hpi) {
  14415. setItemBoxSize(boxSize);
  14416. if (! list) {
  14417. col = Math.floor(place.width() / boxSize.width);
  14418. bufferExt.row = boxSize.height;
  14419. bufferExt.hpi = bufferExt.row / col;
  14420. } else {
  14421. bufferExt.row = bufferExt.hpi = boxSize.height;
  14422. }
  14423. } else if (!list) {
  14424. col = Math.floor(place.width() / boxSize.width);
  14425. }
  14426. row = Math.ceil((buffer.length + (cnt || 0)) / col);
  14427. if (list && tableHeader) {
  14428. ++row;
  14429. }
  14430. bottomMarker.css({top: (bufferExt.row * row) + 'px'}).show();
  14431. }
  14432. },
  14433. wrapperContextMenu = {
  14434. contextmenu : function(e) {
  14435. e.preventDefault();
  14436. if (cwd.data('longtap') !== void(0)) {
  14437. e.stopPropagation();
  14438. return;
  14439. }
  14440. fm.trigger('contextmenu', {
  14441. 'type' : 'cwd',
  14442. 'targets' : [fm.cwd().hash],
  14443. 'x' : e.pageX,
  14444. 'y' : e.pageY
  14445. });
  14446. },
  14447. touchstart : function(e) {
  14448. if (e.originalEvent.touches.length > 1) {
  14449. return;
  14450. }
  14451. if (cwd.data('longtap') !== false) {
  14452. wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY});
  14453. cwd.data('tmlongtap', setTimeout(function(){
  14454. // long tap
  14455. cwd.data('longtap', true);
  14456. fm.trigger('contextmenu', {
  14457. 'type' : 'cwd',
  14458. 'targets' : [fm.cwd().hash],
  14459. 'x' : wrapper.data('touching').x,
  14460. 'y' : wrapper.data('touching').y
  14461. });
  14462. }, 500));
  14463. }
  14464. cwd.data('longtap', null);
  14465. },
  14466. touchend : function(e) {
  14467. if (e.type === 'touchmove') {
  14468. if (! wrapper.data('touching') ||
  14469. ( Math.abs(wrapper.data('touching').x - e.originalEvent.touches[0].pageX)
  14470. + Math.abs(wrapper.data('touching').y - e.originalEvent.touches[0].pageY)) > 4) {
  14471. wrapper.data('touching', null);
  14472. }
  14473. } else {
  14474. setTimeout(function() {
  14475. cwd.removeData('longtap');
  14476. }, 80);
  14477. }
  14478. clearTimeout(cwd.data('tmlongtap'));
  14479. },
  14480. click : function(e) {
  14481. if (cwd.data('longtap')) {
  14482. e.preventDefault();
  14483. e.stopPropagation();
  14484. }
  14485. }
  14486. },
  14487. /**
  14488. * Update directory content
  14489. *
  14490. * @return void
  14491. */
  14492. content = function() {
  14493. fm.lazy(function() {
  14494. var phash, emptyMethod, thtr;
  14495. wz.append(selectAllCheckbox).removeClass('elfinder-cwd-wrapper-empty elfinder-search-result elfinder-incsearch-result elfinder-letsearch-result');
  14496. if (fm.searchStatus.state > 1 || fm.searchStatus.ininc) {
  14497. wz.addClass('elfinder-search-result' + (fm.searchStatus.ininc? ' elfinder-'+(query.substr(0,1) === '/' ? 'let':'inc')+'search-result' : ''));
  14498. }
  14499. // abort attachThumbJob
  14500. bufferExt.attachThumbJob && bufferExt.attachThumbJob._abort();
  14501. // destroy selectable for GC
  14502. cwd.data('selectable') && cwd.selectable('disable').selectable('destroy').removeData('selectable');
  14503. // notify cwd init
  14504. fm.trigger('cwdinit');
  14505. selectedNext = $();
  14506. try {
  14507. // to avoid problem with draggable
  14508. cwd.empty();
  14509. } catch (e) {
  14510. cwd.html('');
  14511. }
  14512. if (tableHeader) {
  14513. wrapper.off('scroll.fixheader resize.fixheader');
  14514. tableHeader.remove();
  14515. tableHeader = null;
  14516. }
  14517. cwd.removeClass('elfinder-cwd-view-icons elfinder-cwd-view-list')
  14518. .addClass('elfinder-cwd-view-'+(list ? 'list' :'icons'))
  14519. .attr('style', '')
  14520. .css('height', 'auto');
  14521. bottomMarker.hide();
  14522. wrapper[list ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-list')._padding = parseInt(wrapper.css('padding-top')) + parseInt(wrapper.css('padding-bottom'));
  14523. if (fm.UA.iOS) {
  14524. wrapper.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch');
  14525. }
  14526. if (list) {
  14527. cwd.html('<table><thead/><tbody/></table>');
  14528. thtr = $('<tr class="ui-state-default"><td class="elfinder-cwd-view-th-name">'+fm.getColumnName('name')+'</td>'+customColsNameBuild()+'</tr>');
  14529. cwd.find('thead').hide().append(thtr).find('td:first').append(selectAllCheckbox);
  14530. if ($.fn.sortable) {
  14531. thtr.addClass('touch-punch touch-punch-keep-default')
  14532. .sortable({
  14533. axis: 'x',
  14534. distance: 8,
  14535. items: '> .sortable-item',
  14536. start: function(e, ui) {
  14537. $(ui.item[0]).data('dragging', true);
  14538. ui.placeholder
  14539. .width(ui.helper.removeClass('ui-state-hover').width())
  14540. .removeClass('ui-state-active')
  14541. .addClass('ui-state-hover')
  14542. .css('visibility', 'visible');
  14543. },
  14544. update: function(e, ui){
  14545. var target = $(ui.item[0]).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', ''),
  14546. prev, done;
  14547. customCols = $.map($(this).children(), function(n) {
  14548. var name = $(n).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', '');
  14549. if (! done) {
  14550. if (target === name) {
  14551. done = true;
  14552. } else {
  14553. prev = name;
  14554. }
  14555. }
  14556. return (name === 'name')? null : name;
  14557. });
  14558. templates.row = makeTemplateRow();
  14559. fm.storage('cwdCols', customCols);
  14560. prev = '.elfinder-col-'+prev+':first';
  14561. target = '.elfinder-col-'+target+':first';
  14562. fm.lazy(function() {
  14563. cwd.find('tbody tr').each(function() {
  14564. var $this = $(this);
  14565. $this.children(prev).after($this.children(target));
  14566. });
  14567. });
  14568. },
  14569. stop: function(e, ui) {
  14570. setTimeout(function() {
  14571. $(ui.item[0]).removeData('dragging');
  14572. }, 100);
  14573. }
  14574. });
  14575. }
  14576. thtr.find('td').addClass('touch-punch').resizable({
  14577. handles: fm.direction === 'ltr'? 'e' : 'w',
  14578. start: function(e, ui) {
  14579. var target = cwd.find('td.elfinder-col-'
  14580. + ui.element.attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', '')
  14581. + ':first');
  14582. ui.element
  14583. .data('dragging', true)
  14584. .data('resizeTarget', target)
  14585. .data('targetWidth', target.width());
  14586. colResizing = true;
  14587. if (cwd.find('table').css('table-layout') !== 'fixed') {
  14588. cwd.find('tbody tr:first td').each(function() {
  14589. $(this).width($(this).width());
  14590. });
  14591. cwd.find('table').css('table-layout', 'fixed');
  14592. }
  14593. },
  14594. resize: function(e, ui) {
  14595. ui.element.data('resizeTarget').width(ui.element.data('targetWidth') - (ui.originalSize.width - ui.size.width));
  14596. },
  14597. stop : function(e, ui) {
  14598. colResizing = false;
  14599. fixTableHeader({fitWidth: true});
  14600. colWidth = {};
  14601. cwd.find('tbody tr:first td').each(function() {
  14602. var name = $(this).attr('class').split(' ')[0].replace('elfinder-col-', '');
  14603. colWidth[name] = $(this).width();
  14604. });
  14605. fm.storage('cwdColWidth', colWidth);
  14606. setTimeout(function() {
  14607. ui.element.removeData('dragging');
  14608. }, 100);
  14609. }
  14610. })
  14611. .find('.ui-resizable-handle').addClass('ui-icon ui-icon-grip-dotted-vertical');
  14612. }
  14613. buffer = $.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null; });
  14614. buffer = fm.sortFiles(buffer);
  14615. if (incHashes) {
  14616. incHashes = $.map(buffer, function(f) { return f.hash; });
  14617. } else {
  14618. cwdHashes = $.map(buffer, function(f) { return f.hash; });
  14619. }
  14620. bufferExt = {
  14621. renderd: 0,
  14622. attachTmbs: {},
  14623. getTmbs: [],
  14624. tmbLoading: {},
  14625. lazyOpts: { tm : 0 }
  14626. };
  14627. wz[(buffer.length < 1) ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-empty');
  14628. wrapper.off(scrollEvent, render).on(scrollEvent, render).trigger(scrollEvent);
  14629. // set droppable
  14630. if (!fm.cwd().write) {
  14631. wrapper.removeClass('native-droppable')
  14632. .droppable('disable')
  14633. .removeClass('ui-state-disabled'); // for old jQueryUI see https://bugs.jqueryui.com/ticket/5974
  14634. } else {
  14635. wrapper[fm.isCommandEnabled('upload')? 'addClass' : 'removeClass']('native-droppable');
  14636. wrapper.droppable(fm.isCommandEnabled('paste')? 'enable' : 'disable');
  14637. }
  14638. });
  14639. },
  14640. /**
  14641. * CWD node itself
  14642. *
  14643. * @type JQuery
  14644. **/
  14645. cwd = $(this)
  14646. .addClass('ui-helper-clearfix elfinder-cwd')
  14647. .attr('unselectable', 'on')
  14648. // fix ui.selectable bugs and add shift+click support
  14649. .on('click.'+fm.namespace, fileSelector, function(e) {
  14650. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  14651. tgt = $(e.target),
  14652. prev,
  14653. next,
  14654. pl,
  14655. nl,
  14656. sib;
  14657. if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) {
  14658. e.stopPropagation();
  14659. e.preventDefault();
  14660. p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect);
  14661. trigger();
  14662. requestAnimationFrame(function() {
  14663. tgt.prop('checked', p.hasClass(clSelected));
  14664. });
  14665. return;
  14666. }
  14667. if (cwd.data('longtap') || tgt.hasClass('elfinder-cwd-nonselect')) {
  14668. e.stopPropagation();
  14669. return;
  14670. }
  14671. if (!curClickId) {
  14672. curClickId = p.attr('id');
  14673. setTimeout(function() {
  14674. curClickId = '';
  14675. }, 500);
  14676. }
  14677. if (e.shiftKey) {
  14678. prev = p.prevAll(lastSelect || '.'+clSelected+':first');
  14679. next = p.nextAll(lastSelect || '.'+clSelected+':first');
  14680. pl = prev.length;
  14681. nl = next.length;
  14682. }
  14683. if (e.shiftKey && (pl || nl)) {
  14684. sib = pl ? p.prevUntil('#'+prev.attr('id')) : p.nextUntil('#'+next.attr('id'));
  14685. sib.add(p).trigger(evtSelect);
  14686. } else if (e.ctrlKey || e.metaKey) {
  14687. p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect);
  14688. } else {
  14689. if (wrapper.data('touching') && p.hasClass(clSelected)) {
  14690. wrapper.data('touching', null);
  14691. fm.dblclick({file : fm.cwdId2Hash(this.id)});
  14692. return;
  14693. } else {
  14694. unselectAll({ notrigger: true });
  14695. p.trigger(evtSelect);
  14696. }
  14697. }
  14698. trigger();
  14699. })
  14700. // call fm.open()
  14701. .on('dblclick.'+fm.namespace, fileSelector, function(e) {
  14702. if (curClickId) {
  14703. var hash = fm.cwdId2Hash(curClickId);
  14704. e.stopPropagation();
  14705. if (this.id !== curClickId) {
  14706. $(this).trigger(evtUnselect);
  14707. $('#'+curClickId).trigger(evtSelect);
  14708. trigger();
  14709. }
  14710. fm.dblclick({file : hash});
  14711. }
  14712. })
  14713. // for touch device
  14714. .on('touchstart.'+fm.namespace, fileSelector, function(e) {
  14715. if (e.originalEvent.touches.length > 1) {
  14716. return;
  14717. }
  14718. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  14719. tgt = $(e.target),
  14720. nodeName = e.target.nodeName,
  14721. sel;
  14722. if ((nodeName === 'INPUT' && e.target.type === 'text') || nodeName === 'TEXTAREA' || tgt.hasClass('elfinder-cwd-nonselect')) {
  14723. e.stopPropagation();
  14724. return;
  14725. }
  14726. // now name editing
  14727. if (p.find('input:text,textarea').length) {
  14728. e.stopPropagation();
  14729. e.preventDefault();
  14730. return;
  14731. }
  14732. wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY});
  14733. if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) {
  14734. return;
  14735. }
  14736. sel = p.prevAll('.'+clSelected+':first').length +
  14737. p.nextAll('.'+clSelected+':first').length;
  14738. cwd.data('longtap', null);
  14739. if (Object.keys(selectedFiles).length
  14740. ||
  14741. (list && e.target.nodeName !== 'TD')
  14742. ||
  14743. (!list && this !== e.target)
  14744. ) {
  14745. cwd.data('longtap', false);
  14746. p.addClass(clHover);
  14747. p.data('tmlongtap', setTimeout(function(){
  14748. // long tap
  14749. cwd.data('longtap', true);
  14750. p.trigger(evtSelect);
  14751. trigger();
  14752. fm.trigger('contextmenu', {
  14753. 'type' : 'files',
  14754. 'targets' : fm.selected(),
  14755. 'x' : e.originalEvent.touches[0].pageX,
  14756. 'y' : e.originalEvent.touches[0].pageY
  14757. });
  14758. }, 500));
  14759. }
  14760. })
  14761. .on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, fileSelector, function(e) {
  14762. var tgt = $(e.target),
  14763. p;
  14764. if (selectCheckbox && (tgt.is('input:checkbox.'+clSelChk) || tgt.hasClass('elfinder-cwd-select'))) {
  14765. return;
  14766. }
  14767. if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA') {
  14768. e.stopPropagation();
  14769. return;
  14770. }
  14771. p = this.id ? $(this) : $(this).parents('[id]:first');
  14772. clearTimeout(p.data('tmlongtap'));
  14773. if (e.type === 'touchmove') {
  14774. wrapper.data('touching', null);
  14775. p.removeClass(clHover);
  14776. } else {
  14777. if (wrapper.data('touching') && !cwd.data('longtap') && p.hasClass(clSelected)) {
  14778. e.preventDefault();
  14779. wrapper.data('touching', null);
  14780. fm.dblclick({file : fm.cwdId2Hash(this.id)});
  14781. }
  14782. setTimeout(function() {
  14783. cwd.removeData('longtap');
  14784. }, 80);
  14785. }
  14786. })
  14787. // attach draggable
  14788. .on('mouseenter.'+fm.namespace, fileSelector, function(e) {
  14789. if (scrolling) { return; }
  14790. var $this = $(this), helper = null;
  14791. if (!mobile && !$this.data('dragRegisted') && !$this.hasClass(clTmp) && !$this.hasClass(clDraggable) && !$this.hasClass(clDisabled)) {
  14792. $this.data('dragRegisted', true);
  14793. if (!fm.isCommandEnabled('copy', fm.searchStatus.state > 1 || $this.hasClass('isroot')? fm.cwdId2Hash($this.attr('id')) : void 0)) {
  14794. return;
  14795. }
  14796. $this.on('mousedown', function(e) {
  14797. // shiftKey or altKey + drag start for HTML5 native drag function
  14798. // Note: can no use shiftKey with the Google Chrome
  14799. var metaKey = e.shiftKey || e.altKey,
  14800. disable = false;
  14801. if (metaKey && !fm.UA.IE && cwd.data('selectable')) {
  14802. // destroy jQuery-ui selectable while trigger native drag
  14803. cwd.selectable('disable').selectable('destroy').removeData('selectable');
  14804. requestAnimationFrame(function(){
  14805. cwd.selectable(selectableOption).selectable('option', {disabled: false}).selectable('refresh').data('selectable', true);
  14806. });
  14807. }
  14808. $this.removeClass('ui-state-disabled');
  14809. if (metaKey) {
  14810. $this.draggable('option', 'disabled', true).attr('draggable', 'true');
  14811. } else {
  14812. if (!$this.hasClass(clSelected)) {
  14813. if (list) {
  14814. disable = $(e.target).closest('span,tr').is('tr');
  14815. } else {
  14816. disable = $(e.target).hasClass('elfinder-cwd-file');
  14817. }
  14818. }
  14819. if (disable) {
  14820. $this.draggable('option', 'disabled', true);
  14821. } else {
  14822. $this.draggable('option', 'disabled', false)
  14823. .removeAttr('draggable')
  14824. .draggable('option', 'cursorAt', {left: 50 - parseInt($(e.currentTarget).css('margin-left')), top: 47});
  14825. }
  14826. }
  14827. })
  14828. .on('dragstart', function(e) {
  14829. var dt = e.dataTransfer || e.originalEvent.dataTransfer || null;
  14830. helper = null;
  14831. if (dt && !fm.UA.IE) {
  14832. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  14833. elm = $('<span>'),
  14834. url = '',
  14835. durl = null,
  14836. murl = null,
  14837. files = [],
  14838. icon = function(f) {
  14839. var mime = f.mime, i, tmb = fm.tmb(f);
  14840. i = '<div class="elfinder-cwd-icon elfinder-cwd-icon-drag '+fm.mime2class(mime)+' ui-corner-all"/>';
  14841. if (tmb) {
  14842. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  14843. }
  14844. return i;
  14845. }, l, geturl = [];
  14846. p.trigger(evtSelect);
  14847. trigger();
  14848. $.each(selectedFiles, function(v){
  14849. var file = fm.file(v),
  14850. furl = file.url;
  14851. if (file && file.mime !== 'directory') {
  14852. if (!furl) {
  14853. furl = fm.url(file.hash);
  14854. } else if (furl == '1') {
  14855. geturl.push(v);
  14856. return true;
  14857. }
  14858. if (furl) {
  14859. furl = fm.convAbsUrl(furl);
  14860. files.push(v);
  14861. $('<a>').attr('href', furl).text(furl).appendTo(elm);
  14862. url += furl + "\n";
  14863. if (!durl) {
  14864. durl = file.mime + ':' + file.name + ':' + furl;
  14865. }
  14866. if (!murl) {
  14867. murl = furl + "\n" + file.name;
  14868. }
  14869. }
  14870. }
  14871. });
  14872. if (geturl.length) {
  14873. $.each(geturl, function(i, v){
  14874. var rfile = fm.file(v);
  14875. rfile.url = '';
  14876. fm.request({
  14877. data : {cmd : 'url', target : v},
  14878. notify : {type : 'url', cnt : 1},
  14879. preventDefault : true
  14880. })
  14881. .always(function(data) {
  14882. rfile.url = data.url? data.url : '1';
  14883. });
  14884. });
  14885. return false;
  14886. } else if (url) {
  14887. if (dt.setDragImage) {
  14888. helper = $('<div class="elfinder-drag-helper html5-native"></div>').append(icon(fm.file(files[0]))).appendTo($(document.body));
  14889. if ((l = files.length) > 1) {
  14890. helper.append(icon(fm.file(files[l-1])) + '<span class="elfinder-drag-num">'+l+'</span>');
  14891. }
  14892. dt.setDragImage(helper.get(0), 50, 47);
  14893. }
  14894. dt.effectAllowed = 'copyLink';
  14895. dt.setData('DownloadURL', durl);
  14896. dt.setData('text/x-moz-url', murl);
  14897. dt.setData('text/uri-list', url);
  14898. dt.setData('text/plain', url);
  14899. dt.setData('text/html', elm.html());
  14900. dt.setData('elfinderfrom', window.location.href + fm.cwd().hash);
  14901. dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), '');
  14902. } else {
  14903. return false;
  14904. }
  14905. }
  14906. })
  14907. .on('dragend', function(e){
  14908. unselectAll({ notrigger: true });
  14909. helper && helper.remove();
  14910. })
  14911. .draggable(fm.draggable);
  14912. }
  14913. })
  14914. // add hover class to selected file
  14915. .on(evtSelect, fileSelector, function(e) {
  14916. var $this = $(this),
  14917. id = fm.cwdId2Hash($this.attr('id'));
  14918. if (!selectLock && !$this.hasClass(clDisabled)) {
  14919. lastSelect = '#'+ this.id;
  14920. $this.addClass(clSelected).children().addClass(clHover).find('input:checkbox.'+clSelChk).prop('checked', true);
  14921. if (! selectedFiles[id]) {
  14922. selectedFiles[id] = true;
  14923. }
  14924. // will be selected next
  14925. selectedNext = cwd.find('[id].'+clSelected+':last').next();
  14926. }
  14927. })
  14928. // remove hover class from unselected file
  14929. .on(evtUnselect, fileSelector, function(e) {
  14930. var $this = $(this),
  14931. id = fm.cwdId2Hash($this.attr('id'));
  14932. if (!selectLock) {
  14933. $this.removeClass(clSelected).children().removeClass(clHover).find('input:checkbox.'+clSelChk).prop('checked', false);
  14934. if (cwd.hasClass('elfinder-cwd-allselected')) {
  14935. selectCheckbox && selectAllCheckbox.children('input').prop('checked', false);
  14936. cwd.removeClass('elfinder-cwd-allselected');
  14937. }
  14938. selectedFiles[id] && delete selectedFiles[id];
  14939. }
  14940. })
  14941. // disable files wich removing or moving
  14942. .on(evtDisable, fileSelector, function() {
  14943. var $this = $(this).removeClass(clHover+' '+clSelected).addClass(clDisabled),
  14944. child = $this.children(),
  14945. target = (list ? $this : child.find('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename'));
  14946. child.removeClass(clHover+' '+clSelected);
  14947. $this.hasClass(clDroppable) && $this.droppable('disable');
  14948. target.hasClass(clDraggable) && target.draggable('disable');
  14949. })
  14950. // if any files was not removed/moved - unlock its
  14951. .on(evtEnable, fileSelector, function() {
  14952. var $this = $(this).removeClass(clDisabled),
  14953. target = list ? $this : $this.children('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename');
  14954. $this.hasClass(clDroppable) && $this.droppable('enable');
  14955. target.hasClass(clDraggable) && target.draggable('enable');
  14956. })
  14957. .on('scrolltoview', fileSelector, function(e, data) {
  14958. scrollToView($(this), (data && typeof data.blink !== 'undefined')? data.blink : true);
  14959. })
  14960. .on('mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, fileSelector, function(e) {
  14961. var enter = (e.type === 'mouseenter');
  14962. if (enter && (scrolling || fm.UA.Mobile)) { return; }
  14963. fm.trigger('hover', {hash : fm.cwdId2Hash($(this).attr('id')), type : e.type});
  14964. $(this).toggleClass(clHover, (e.type == 'mouseenter'));
  14965. })
  14966. // for file contextmenu
  14967. .on('mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, '.elfinder-cwd-file-wrapper,.elfinder-cwd-filename', function(e) {
  14968. var enter = (e.type === 'mouseenter');
  14969. if (enter && scrolling) { return; }
  14970. $(this).closest(fileSelector).children('.elfinder-cwd-file-wrapper,.elfinder-cwd-filename').toggleClass(clActive, (e.type == 'mouseenter'));
  14971. })
  14972. .on('contextmenu.'+fm.namespace, function(e) {
  14973. var file = $(e.target).closest(fileSelector);
  14974. if (file.get(0) === e.target && !selectedFiles[fm.cwdId2Hash(file.get(0).id)]) {
  14975. return;
  14976. }
  14977. // now filename editing
  14978. if (file.find('input:text,textarea').length) {
  14979. e.stopPropagation();
  14980. return;
  14981. }
  14982. if (file.length && (e.target.nodeName != 'TD' || selectedFiles[fm.cwdId2Hash(file.get(0).id)])) {
  14983. e.stopPropagation();
  14984. e.preventDefault();
  14985. if (!file.hasClass(clDisabled) && !wrapper.data('touching')) {
  14986. if (!file.hasClass(clSelected)) {
  14987. unselectAll({ notrigger: true });
  14988. file.trigger(evtSelect);
  14989. trigger();
  14990. }
  14991. fm.trigger('contextmenu', {
  14992. 'type' : 'files',
  14993. 'targets' : fm.selected(),
  14994. 'x' : e.pageX,
  14995. 'y' : e.pageY
  14996. });
  14997. }
  14998. }
  14999. })
  15000. // unselect all on cwd click
  15001. .on('click.'+fm.namespace, function(e) {
  15002. if (e.target === this && ! cwd.data('longtap')) {
  15003. !e.shiftKey && !e.ctrlKey && !e.metaKey && unselectAll();
  15004. }
  15005. })
  15006. // prepend fake file/dir
  15007. .on('create.'+fm.namespace, function(e, f) {
  15008. var parent = list ? cwd.find('tbody') : cwd,
  15009. p = parent.find('.elfinder-cwd-parent'),
  15010. lock = f.move || false,
  15011. file = $(itemhtml(f)).addClass(clTmp),
  15012. selected = fm.selected();
  15013. if (selected.length) {
  15014. lock && fm.trigger('lockfiles', {files: selected});
  15015. } else {
  15016. unselectAll();
  15017. }
  15018. if (p.length) {
  15019. p.after(file);
  15020. } else {
  15021. parent.prepend(file);
  15022. }
  15023. setColwidth();
  15024. wrapper.scrollTop(0).scrollLeft(0);
  15025. })
  15026. // unselect all selected files
  15027. .on('unselectall', unselectAll)
  15028. .on('selectfile', function(e, id) {
  15029. $('#'+fm.cwdHash2Id(id)).trigger(evtSelect);
  15030. trigger();
  15031. })
  15032. .on('colwidth', function() {
  15033. if (list) {
  15034. cwd.find('table').css('table-layout', '')
  15035. .find('td').css('width', '');
  15036. fixTableHeader({fitWidth: true});
  15037. fm.storage('cwdColWidth', colWidth = null);
  15038. }
  15039. })
  15040. .on('iconpref', function(e, data) {
  15041. cwd.removeClass(function(i, cName) {
  15042. return (cName.match(/\belfinder-cwd-size\S+/g) || []).join(' ');
  15043. });
  15044. iconSize = data? (parseInt(data.size) || 0) : 0;
  15045. if (!list) {
  15046. if (iconSize > 0) {
  15047. cwd.addClass('elfinder-cwd-size' + iconSize);
  15048. }
  15049. if (bufferExt.renderd) {
  15050. requestAnimationFrame(function() {
  15051. itemBoxSize.icons = {};
  15052. bufferExt.hpi = null;
  15053. bottomMarkerShow(cwd, bufferExt.renderd);
  15054. wrapperRepaint();
  15055. });
  15056. }
  15057. }
  15058. })
  15059. // Change icon size with mouse wheel event
  15060. .on('onwheel' in document ? 'wheel' : 'mousewheel', function(e) {
  15061. var tm, size, delta;
  15062. if (!list && ((e.ctrlKey && !e.metaKey) || (!e.ctrlKey && e.metaKey))) {
  15063. e.stopPropagation();
  15064. e.preventDefault();
  15065. tm = cwd.data('wheelTm');
  15066. if (typeof tm !== 'undefined') {
  15067. clearTimeout(tm);
  15068. cwd.data('wheelTm', setTimeout(function() {
  15069. cwd.removeData('wheelTm');
  15070. }, 200));
  15071. } else {
  15072. cwd.data('wheelTm', false);
  15073. size = iconSize || 0;
  15074. delta = e.originalEvent.deltaY ? e.originalEvent.deltaY : -(e.originalEvent.wheelDelta);
  15075. if (delta > 0) {
  15076. if (iconSize > 0) {
  15077. size = iconSize - 1;
  15078. }
  15079. } else {
  15080. if (iconSize < options.iconsView.sizeMax) {
  15081. size = iconSize + 1;
  15082. }
  15083. }
  15084. if (size !== iconSize) {
  15085. fm.storage('iconsize', size);
  15086. cwd.trigger('iconpref', {size: size});
  15087. }
  15088. }
  15089. }
  15090. }),
  15091. wrapper = $('<div class="elfinder-cwd-wrapper"/>')
  15092. // make cwd itself droppable for folders from nav panel
  15093. .droppable(Object.assign({}, droppable, {autoDisable: false}))
  15094. .on('contextmenu.'+fm.namespace, wrapperContextMenu.contextmenu)
  15095. .on('touchstart.'+fm.namespace, wrapperContextMenu.touchstart)
  15096. .on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, wrapperContextMenu.touchend)
  15097. .on('click.'+fm.namespace, wrapperContextMenu.click)
  15098. .on('scroll.'+fm.namespace, function() {
  15099. if (! scrolling) {
  15100. cwd.data('selectable') && cwd.selectable('disable');
  15101. wrapper.trigger(scrollStartEvent);
  15102. }
  15103. scrolling = true;
  15104. bufferExt.scrtm && cancelAnimationFrame(bufferExt.scrtm);
  15105. if (bufferExt.scrtm && Math.abs((bufferExt.scrolltop || 0) - (bufferExt.scrolltop = (this.scrollTop || $(this).scrollTop()))) < 5) {
  15106. bufferExt.scrtm = 0;
  15107. wrapper.trigger(scrollEvent);
  15108. }
  15109. bufferExt.scrtm = requestAnimationFrame(function() {
  15110. bufferExt.scrtm = 0;
  15111. wrapper.trigger(scrollEvent);
  15112. });
  15113. })
  15114. .on(scrollEvent, function() {
  15115. scrolling = false;
  15116. wrapperRepaint();
  15117. }),
  15118. bottomMarker = $('<div>&nbsp;</div>')
  15119. .css({position: 'absolute', width: '1px', height: '1px'})
  15120. .hide(),
  15121. selectAllCheckbox = selectCheckbox? $('<div class="elfinder-cwd-selectall"><input type="checkbox"/></div>')
  15122. .attr('title', fm.i18n('selectall'))
  15123. .on('touchstart mousedown click', function(e) {
  15124. e.stopPropagation();
  15125. e.preventDefault();
  15126. if ($(this).data('pending') || e.type === 'click') {
  15127. return false;
  15128. }
  15129. selectAllCheckbox.data('pending', true);
  15130. if (cwd.hasClass('elfinder-cwd-allselected')) {
  15131. selectAllCheckbox.find('input').prop('checked', false);
  15132. requestAnimationFrame(function() {
  15133. unselectAll();
  15134. });
  15135. } else {
  15136. selectAll();
  15137. }
  15138. }) : $(),
  15139. restm = null,
  15140. resize = function(init) {
  15141. var initHeight = function() {
  15142. if (typeof bufferExt.renderd !== 'undefined') {
  15143. var h = 0;
  15144. wrapper.siblings('div.elfinder-panel:visible').each(function() {
  15145. h += $(this).outerHeight(true);
  15146. });
  15147. wrapper.height(wz.height() - h - wrapper._padding);
  15148. }
  15149. };
  15150. init && initHeight();
  15151. restm && cancelAnimationFrame(restm);
  15152. restm = requestAnimationFrame(function(){
  15153. !init && initHeight();
  15154. var wph, cwdoh;
  15155. // fix cwd height if it less then wrapper
  15156. cwd.css('height', 'auto');
  15157. wph = wrapper[0].clientHeight - parseInt(wrapper.css('padding-top')) - parseInt(wrapper.css('padding-bottom')) - parseInt(cwd.css('margin-top')),
  15158. cwdoh = cwd.outerHeight(true);
  15159. if (cwdoh < wph) {
  15160. cwd.height(wph);
  15161. }
  15162. });
  15163. list && ! colResizing && (init? wrapper.trigger('resize.fixheader') : fixTableHeader());
  15164. wrapperRepaint();
  15165. },
  15166. // elfinder node
  15167. parent = $(this).parent().on('resize', resize),
  15168. // workzone node
  15169. wz = parent.children('.elfinder-workzone').append(wrapper.append(this).append(bottomMarker)),
  15170. // message board
  15171. mBoard = $('<div class="elfinder-cwd-message-board"/>').insertAfter(cwd),
  15172. // Volume expires
  15173. vExpires = $('<div class="elfinder-cwd-expires" />'),
  15174. vExpiresTm,
  15175. showVolumeExpires = function() {
  15176. var remain, sec, int;
  15177. vExpiresTm && clearTimeout(vExpiresTm);
  15178. if (curVolId && fm.volumeExpires[curVolId]) {
  15179. sec = fm.volumeExpires[curVolId] - ((+new Date()) / 1000);
  15180. int = (sec % 60) + 0.1;
  15181. remain = Math.floor(sec / 60);
  15182. vExpires.html(fm.i18n(['minsLeft', remain])).show();
  15183. if (remain) {
  15184. vExpiresTm = setTimeout(showVolumeExpires, int * 1000);
  15185. }
  15186. }
  15187. },
  15188. // each item box size
  15189. itemBoxSize = {
  15190. icons : {},
  15191. list : {}
  15192. },
  15193. // has UI tree
  15194. hasUiTree,
  15195. // Icon size of icons view
  15196. iconSize,
  15197. // Current volume id
  15198. curVolId,
  15199. winScrTm;
  15200. // IE < 11 not support CSS `pointer-events: none`
  15201. if (!fm.UA.ltIE10) {
  15202. mBoard.append($('<div class="elfinder-cwd-trash" />').html(fm.i18n('volume_Trash')))
  15203. .append(vExpires);
  15204. }
  15205. // setup by options
  15206. replacement = Object.assign(replacement, options.replacement || {});
  15207. try {
  15208. colWidth = fm.storage('cwdColWidth')? fm.storage('cwdColWidth') : null;
  15209. } catch(e) {
  15210. colWidth = null;
  15211. }
  15212. // setup costomCols
  15213. fm.bind('columnpref', function(e) {
  15214. var opts = e.data || {};
  15215. if (customCols = fm.storage('cwdCols')) {
  15216. customCols = $.grep(customCols, function(n) {
  15217. return (options.listView.columns.indexOf(n) !== -1)? true : false;
  15218. });
  15219. if (options.listView.columns.length > customCols.length) {
  15220. $.each(options.listView.columns, function(i, n) {
  15221. if (customCols.indexOf(n) === -1) {
  15222. customCols.push(n);
  15223. }
  15224. });
  15225. }
  15226. } else {
  15227. customCols = options.listView.columns;
  15228. }
  15229. // column names array that hidden
  15230. var columnhides = fm.storage('columnhides') || null;
  15231. if (columnhides && Object.keys(columnhides).length)
  15232. customCols = $.grep(customCols, function(n) {
  15233. return columnhides[n]? false : true;
  15234. });
  15235. // make template with customCols
  15236. templates.row = makeTemplateRow();
  15237. // repaint if need it
  15238. list && opts.repaint && content();
  15239. }).trigger('columnpref');
  15240. if (mobile) {
  15241. // for iOS5 bug
  15242. $('body').on('touchstart touchmove touchend', function(e){});
  15243. }
  15244. selectCheckbox && cwd.addClass('elfinder-has-checkbox');
  15245. $(window).on('scroll.'+fm.namespace, function() {
  15246. winScrTm && cancelAnimationFrame(winScrTm);
  15247. winScrTm = requestAnimationFrame(function() {
  15248. wrapper.trigger(scrollEvent);
  15249. });
  15250. });
  15251. $(document).on('keydown.'+fm.namespace, function(e) {
  15252. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  15253. if (! fm.getUI().find('.ui-widget:visible').length) {
  15254. unselectAll();
  15255. }
  15256. }
  15257. });
  15258. fm
  15259. .one('init', function(){
  15260. var style = document.createElement('style'),
  15261. sheet, node, base, resizeTm, iconSize, i = 0;
  15262. if (document.head) {
  15263. document.head.appendChild(style);
  15264. sheet = style.sheet;
  15265. sheet.insertRule('.elfinder-cwd-wrapper-empty .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder')+'" }', i++);
  15266. sheet.insertRule('.elfinder-cwd-wrapper-empty .native-droppable .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder'+(mobile? 'LTap' : 'Drop'))+'" }', i++);
  15267. sheet.insertRule('.elfinder-cwd-wrapper-empty .ui-droppable-disabled .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptyFolder')+'" }', i++);
  15268. sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result .elfinder-cwd:not(.elfinder-table-header-sticky):after{ content:"'+fm.i18n('emptySearch')+'" }', i++);
  15269. 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++);
  15270. 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++);
  15271. }
  15272. if (iconSize = fm.storage('iconsize') || 0) {
  15273. cwd.trigger('iconpref', {size: iconSize});
  15274. }
  15275. if (! mobile) {
  15276. fm.one('open', function() {
  15277. sheet && fm.zIndex && sheet.insertRule('.ui-selectable-helper{z-index:'+fm.zIndex+';}', i++);
  15278. });
  15279. base = $('<div style="position:absolute"/>');
  15280. node = fm.getUI();
  15281. node.on('resize', function(e, data) {
  15282. var offset;
  15283. e.preventDefault();
  15284. e.stopPropagation();
  15285. if (data && data.fullscreen) {
  15286. offset = node.offset();
  15287. if (data.fullscreen === 'on') {
  15288. base.css({top:offset.top * -1 , left:offset.left * -1 }).appendTo(node);
  15289. selectableOption.appendTo = base;
  15290. } else {
  15291. base.detach();
  15292. selectableOption.appendTo = 'body';
  15293. }
  15294. cwd.data('selectable') && cwd.selectable('option', {appendTo : selectableOption.appendTo});
  15295. }
  15296. });
  15297. }
  15298. hasUiTree = fm.getUI('tree').length;
  15299. })
  15300. .bind('enable', function() {
  15301. resize();
  15302. })
  15303. .bind('request.open', function() {
  15304. bufferExt.getTmbs = [];
  15305. })
  15306. .one('open', function() {
  15307. if (fm.maxTargets) {
  15308. tmbNum = Math.min(fm.maxTargets, tmbNum);
  15309. }
  15310. })
  15311. .bind('open add remove searchend', function() {
  15312. var phash = fm.cwd().hash,
  15313. type = this.type;
  15314. if (type === 'open' || type === 'searchend' || fm.searchStatus.state < 2) {
  15315. cwdHashes = $.map(fm.files(phash), function(f) { return f.hash; });
  15316. fm.trigger('cwdhasheschange', cwdHashes);
  15317. }
  15318. if (type === 'open') {
  15319. var inTrash = function() {
  15320. var isIn = false;
  15321. $.each(cwdParents, function(i, h) {
  15322. if (fm.trashes[h]) {
  15323. isIn = true;
  15324. return false;
  15325. }
  15326. });
  15327. return isIn;
  15328. },
  15329. req = phash?
  15330. (! fm.file(phash) || hasUiTree?
  15331. (! hasUiTree?
  15332. fm.request({
  15333. data: {
  15334. cmd : 'parents',
  15335. target : fm.cwd().hash
  15336. },
  15337. preventFail : true
  15338. }) : (function() {
  15339. var dfd = $.Deferred();
  15340. fm.one('treesync', function(e) {
  15341. e.data.always(function() {
  15342. dfd.resolve();
  15343. });
  15344. });
  15345. return dfd;
  15346. })()
  15347. ) : null
  15348. ) : null,
  15349. cwdObj = fm.cwd();
  15350. // add/remove volume id class
  15351. if (cwdObj.volumeid !== curVolId) {
  15352. vExpires.empty().hide();
  15353. if (curVolId) {
  15354. wrapper.removeClass('elfinder-cwd-wrapper-' + curVolId);
  15355. }
  15356. curVolId = cwdObj.volumeid;
  15357. showVolumeExpires();
  15358. wrapper.addClass('elfinder-cwd-wrapper-' + curVolId);
  15359. }
  15360. // add/remove trash class
  15361. $.when(req).done(function() {
  15362. cwdParents = fm.parents(cwdObj.hash);
  15363. wrapper[inTrash()? 'addClass':'removeClass']('elfinder-cwd-wrapper-trash');
  15364. });
  15365. incHashes = void 0;
  15366. unselectAll({ notrigger: true });
  15367. content();
  15368. }
  15369. })
  15370. .bind('search', function(e) {
  15371. cwdHashes = $.map(e.data.files, function(f) { return f.hash; });
  15372. fm.trigger('cwdhasheschange', cwdHashes);
  15373. incHashes = void 0;
  15374. fm.searchStatus.ininc = false;
  15375. content();
  15376. fm.autoSync('stop');
  15377. })
  15378. .bind('searchend', function(e) {
  15379. if (query || incHashes) {
  15380. query = '';
  15381. if (incHashes) {
  15382. fm.trigger('incsearchend', e.data);
  15383. } else {
  15384. if (!e.data || !e.data.noupdate) {
  15385. content();
  15386. }
  15387. }
  15388. }
  15389. fm.autoSync();
  15390. })
  15391. .bind('searchstart', function(e) {
  15392. unselectAll();
  15393. query = e.data.query;
  15394. })
  15395. .bind('incsearchstart', function(e) {
  15396. selectedFiles = {};
  15397. fm.lazy(function() {
  15398. // incremental search
  15399. var regex, q, fst = '';
  15400. q = query = e.data.query || '';
  15401. if (q) {
  15402. if (q.substr(0,1) === '/') {
  15403. q = q.substr(1);
  15404. fst = '^';
  15405. }
  15406. regex = new RegExp(fst + q.replace(/([\\*\;\.\?\[\]\{\}\(\)\^\$\-\|])/g, '\\$1'), 'i');
  15407. incHashes = $.grep(cwdHashes, function(hash) {
  15408. var file = fm.file(hash);
  15409. return (file && (file.name.match(regex) || (file.i18 && file.i18.match(regex))))? true : false;
  15410. });
  15411. fm.trigger('incsearch', { hashes: incHashes, query: q })
  15412. .searchStatus.ininc = true;
  15413. content();
  15414. fm.autoSync('stop');
  15415. } else {
  15416. fm.trigger('incsearchend');
  15417. }
  15418. });
  15419. })
  15420. .bind('incsearchend', function(e) {
  15421. query = '';
  15422. fm.searchStatus.ininc = false;
  15423. incHashes = void 0;
  15424. if (!e.data || !e.data.noupdate) {
  15425. content();
  15426. }
  15427. fm.autoSync();
  15428. })
  15429. .bind('sortchange', function() {
  15430. var lastScrollLeft = wrapper.scrollLeft(),
  15431. allsel = cwd.hasClass('elfinder-cwd-allselected');
  15432. content();
  15433. fm.one('cwdrender', function() {
  15434. wrapper.scrollLeft(lastScrollLeft);
  15435. if (allsel) {
  15436. selectedFiles = fm.arrayFlip(incHashes || cwdHashes, true);
  15437. }
  15438. (allsel || Object.keys(selectedFiles).length) && trigger();
  15439. });
  15440. })
  15441. .bind('viewchange', function() {
  15442. var l = fm.storage('view') == 'list',
  15443. allsel = cwd.hasClass('elfinder-cwd-allselected');
  15444. if (l != list) {
  15445. list = l;
  15446. fm.viewType = list? 'list' : 'icons';
  15447. if (iconSize) {
  15448. fm.one('cwdinit', function() {
  15449. cwd.trigger('iconpref', {size: iconSize});
  15450. });
  15451. }
  15452. content();
  15453. resize();
  15454. if (allsel) {
  15455. cwd.addClass('elfinder-cwd-allselected');
  15456. selectAllCheckbox.find('input').prop('checked', true);
  15457. }
  15458. Object.keys(selectedFiles).length && trigger();
  15459. }
  15460. })
  15461. .bind('wzresize', function() {
  15462. var place = list ? cwd.find('tbody') : cwd,
  15463. cwdOffset;
  15464. resize(true);
  15465. if (bufferExt.hpi) {
  15466. bottomMarkerShow(place, place.find('[id]').length);
  15467. }
  15468. cwdOffset = cwd.offset();
  15469. wz.data('rectangle', Object.assign(
  15470. {
  15471. width: wz.width(),
  15472. height: wz.height(),
  15473. cwdEdge: (fm.direction === 'ltr')? cwdOffset.left : cwdOffset.left + cwd.width()
  15474. },
  15475. wz.offset())
  15476. );
  15477. bufferExt.itemH = (list? place.find('tr:first') : place.find('[id]:first')).outerHeight(true);
  15478. })
  15479. .bind('changeclipboard', function(e) {
  15480. clipCuts = {};
  15481. if (e.data && e.data.clipboard && e.data.clipboard.length) {
  15482. $.each(e.data.clipboard, function(i, f) {
  15483. if (f.cut) {
  15484. clipCuts[f.hash] = true;
  15485. }
  15486. });
  15487. }
  15488. })
  15489. .bind('resMixinMake', function() {
  15490. setColwidth();
  15491. })
  15492. .bind('tmbreload', function(e) {
  15493. var imgs = {},
  15494. files = (e.data && e.data.files)? e.data.files : null;
  15495. $.each(files, function(i, f) {
  15496. if (f.tmb && f.tmb != '1') {
  15497. imgs[f.hash] = f.tmb;
  15498. }
  15499. });
  15500. if (Object.keys(imgs).length) {
  15501. attachThumbnails(imgs, true);
  15502. }
  15503. })
  15504. .add(function(e) {
  15505. var regex = query? new RegExp(query.replace(/([\\*\;\.\?\[\]\{\}\(\)\^\$\-\|])/g, '\\$1'), 'i') : null,
  15506. mime = fm.searchStatus.mime,
  15507. inSearch = fm.searchStatus.state > 1,
  15508. phash = inSearch && fm.searchStatus.target? fm.searchStatus.target : fm.cwd().hash,
  15509. curPath = fm.path(phash),
  15510. inTarget = function(f) {
  15511. var res, parents, path;
  15512. res = (f.phash === phash);
  15513. if (!res && inSearch) {
  15514. path = f.path || fm.path(f.hash);
  15515. res = (curPath && path.indexOf(curPath) === 0);
  15516. if (! res && fm.searchStatus.mixed) {
  15517. res = $.grep(fm.searchStatus.mixed, function(vid) { return f.hash.indexOf(vid) === 0? true : false; }).length? true : false;
  15518. }
  15519. }
  15520. if (res && inSearch) {
  15521. if (mime) {
  15522. res = (f.mime.indexOf(mime) === 0);
  15523. } else {
  15524. res = (f.name.match(regex) || (f.i18 && f.i18.match(regex)))? true : false;
  15525. }
  15526. }
  15527. return res;
  15528. },
  15529. files = $.grep(e.data.added || [], function(f) { return inTarget(f)? true : false ;});
  15530. add(files);
  15531. if (fm.searchStatus.state === 2) {
  15532. $.each(files, function(i, f) {
  15533. if ($.inArray(f.hash, cwdHashes) === -1) {
  15534. cwdHashes.push(f.hash);
  15535. }
  15536. });
  15537. fm.trigger('cwdhasheschange', cwdHashes);
  15538. }
  15539. list && resize();
  15540. wrapper.trigger(scrollEvent);
  15541. })
  15542. .change(function(e) {
  15543. var phash = fm.cwd().hash,
  15544. sel = fm.selected(),
  15545. files, added;
  15546. if (query) {
  15547. $.each(e.data.changed || [], function(i, file) {
  15548. if ($('#'+fm.cwdHash2Id(file.hash)).length) {
  15549. remove([file.hash]);
  15550. add([file], 'change');
  15551. $.inArray(file.hash, sel) !== -1 && selectFile(file.hash);
  15552. added = true;
  15553. }
  15554. });
  15555. } else {
  15556. $.each($.grep(e.data.changed || [], function(f) { return f.phash == phash ? true : false; }), function(i, file) {
  15557. if ($('#'+fm.cwdHash2Id(file.hash)).length) {
  15558. remove([file.hash]);
  15559. add([file], 'change');
  15560. $.inArray(file.hash, sel) !== -1 && selectFile(file.hash);
  15561. added = true;
  15562. }
  15563. });
  15564. }
  15565. if (added) {
  15566. fm.trigger('cwdhasheschange', cwdHashes);
  15567. list && resize();
  15568. wrapper.trigger(scrollEvent);
  15569. }
  15570. trigger();
  15571. })
  15572. .remove(function(e) {
  15573. var place = list ? cwd.find('tbody') : cwd;
  15574. remove(e.data.removed || []);
  15575. trigger();
  15576. if (buffer.length < 1 && place.children(fileSelector).length < 1) {
  15577. wz.addClass('elfinder-cwd-wrapper-empty');
  15578. selectCheckbox && selectAllCheckbox.find('input').prop('checked', false);
  15579. bottomMarker.hide();
  15580. wrapper.off(scrollEvent, render);
  15581. resize();
  15582. } else {
  15583. bottomMarkerShow(place);
  15584. wrapper.trigger(scrollEvent);
  15585. }
  15586. })
  15587. // select dragged file if no selected, disable selectable
  15588. .dragstart(function(e) {
  15589. var target = $(e.data.target),
  15590. oe = e.data.originalEvent;
  15591. if (target.hasClass(clFile)) {
  15592. if (!target.hasClass(clSelected)) {
  15593. !(oe.ctrlKey || oe.metaKey || oe.shiftKey) && unselectAll({ notrigger: true });
  15594. target.trigger(evtSelect);
  15595. trigger();
  15596. }
  15597. }
  15598. cwd.removeClass(clDisabled).data('selectable') && cwd.selectable('disable');
  15599. selectLock = true;
  15600. })
  15601. // enable selectable
  15602. .dragstop(function() {
  15603. cwd.data('selectable') && cwd.selectable('enable');
  15604. selectLock = false;
  15605. })
  15606. .bind('lockfiles unlockfiles selectfiles unselectfiles', function(e) {
  15607. var events = {
  15608. lockfiles : evtDisable ,
  15609. unlockfiles : evtEnable ,
  15610. selectfiles : evtSelect,
  15611. unselectfiles : evtUnselect },
  15612. event = events[e.type],
  15613. files = e.data.files || [],
  15614. l = files.length,
  15615. helper = e.data.helper || $(),
  15616. parents, ctr, add;
  15617. if (l > 0) {
  15618. parents = fm.parents(files[0]);
  15619. }
  15620. if (event === evtSelect || event === evtUnselect) {
  15621. add = (event === evtSelect),
  15622. $.each(files, function(i, hash) {
  15623. var all = cwd.hasClass('elfinder-cwd-allselected');
  15624. if (! selectedFiles[hash]) {
  15625. add && (selectedFiles[hash] = true);
  15626. } else {
  15627. if (all) {
  15628. selectCheckbox && selectAllCheckbox.children('input').prop('checked', false);
  15629. cwd.removeClass('elfinder-cwd-allselected');
  15630. all = false;
  15631. }
  15632. ! add && delete selectedFiles[hash];
  15633. }
  15634. });
  15635. }
  15636. if (!helper.data('locked')) {
  15637. while (l--) {
  15638. try {
  15639. $('#'+fm.cwdHash2Id(files[l])).trigger(event);
  15640. } catch(e) {}
  15641. }
  15642. ! e.data.inselect && trigger();
  15643. }
  15644. if (wrapper.data('dropover') && parents.indexOf(wrapper.data('dropover')) !== -1) {
  15645. ctr = e.type !== 'lockfiles';
  15646. helper.toggleClass('elfinder-drag-helper-plus', ctr);
  15647. wrapper.toggleClass(clDropActive, ctr);
  15648. }
  15649. })
  15650. // select new files after some actions
  15651. .bind('mkdir mkfile duplicate upload rename archive extract paste multiupload', function(e) {
  15652. if (e.type == 'upload' && e.data._multiupload) return;
  15653. var phash = fm.cwd().hash, files;
  15654. unselectAll({ notrigger: true });
  15655. $.each((e.data.added || []).concat(e.data.changed || []), function(i, file) {
  15656. file && file.phash == phash && selectFile(file.hash);
  15657. });
  15658. trigger();
  15659. })
  15660. .shortcut({
  15661. pattern :'ctrl+a',
  15662. description : 'selectall',
  15663. callback : selectAll
  15664. })
  15665. .shortcut({
  15666. pattern :'ctrl+shift+i',
  15667. description : 'selectinvert',
  15668. callback : selectInvert
  15669. })
  15670. .shortcut({
  15671. pattern : 'left right up down shift+left shift+right shift+up shift+down',
  15672. description : 'selectfiles',
  15673. type : 'keydown' , //fm.UA.Firefox || fm.UA.Opera ? 'keypress' : 'keydown',
  15674. callback : function(e) { select(e.keyCode, e.shiftKey); }
  15675. })
  15676. .shortcut({
  15677. pattern : 'home',
  15678. description : 'selectffile',
  15679. callback : function(e) {
  15680. unselectAll({ notrigger: true });
  15681. scrollToView(cwd.find('[id]:first').trigger(evtSelect));
  15682. trigger();
  15683. }
  15684. })
  15685. .shortcut({
  15686. pattern : 'end',
  15687. description : 'selectlfile',
  15688. callback : function(e) {
  15689. unselectAll({ notrigger: true });
  15690. scrollToView(cwd.find('[id]:last').trigger(evtSelect)) ;
  15691. trigger();
  15692. }
  15693. })
  15694. .shortcut({
  15695. pattern : 'page_up',
  15696. description : 'pageTurning',
  15697. callback : function(e) {
  15698. if (bufferExt.itemH) {
  15699. wrapper.scrollTop(
  15700. Math.round(
  15701. wrapper.scrollTop()
  15702. - (Math.floor((wrapper.height() + (list? bufferExt.itemH * -1 : 16)) / bufferExt.itemH)) * bufferExt.itemH
  15703. )
  15704. );
  15705. }
  15706. }
  15707. }).shortcut({
  15708. pattern : 'page_down',
  15709. description : 'pageTurning',
  15710. callback : function(e) {
  15711. if (bufferExt.itemH) {
  15712. wrapper.scrollTop(
  15713. Math.round(
  15714. wrapper.scrollTop()
  15715. + (Math.floor((wrapper.height() + (list? bufferExt.itemH * -1 : 16)) / bufferExt.itemH)) * bufferExt.itemH
  15716. )
  15717. );
  15718. }
  15719. }
  15720. });
  15721. });
  15722. // fm.timeEnd('cwdLoad')
  15723. return this;
  15724. };
  15725. /*
  15726. * File: /js/ui/dialog.js
  15727. */
  15728. /**
  15729. * @class elFinder dialog
  15730. *
  15731. * @author Dmitry (dio) Levashov
  15732. **/
  15733. $.fn.elfinderdialog = function(opts, fm) {
  15734. var platformWin = (window.navigator.platform.indexOf('Win') != -1),
  15735. delta = {},
  15736. syncSize = { enabled: false, width: false, height: false, defaultSize: null },
  15737. fitSize = function(dialog) {
  15738. var opts, node;
  15739. if (syncSize.enabled) {
  15740. node = fm.options.dialogContained? elfNode : $(window);
  15741. opts = {
  15742. maxWidth : syncSize.width? node.width() - delta.width : null,
  15743. maxHeight: syncSize.height? node.height() - delta.height : null
  15744. };
  15745. Object.assign(restoreStyle, opts);
  15746. dialog.css(opts).trigger('resize');
  15747. if (dialog.data('hasResizable') && (dialog.resizable('option', 'maxWidth') < opts.maxWidth || dialog.resizable('option', 'maxHeight') < opts.maxHeight)) {
  15748. dialog.resizable('option', opts);
  15749. }
  15750. }
  15751. },
  15752. syncFunc = function(e) {
  15753. var dialog = e.data;
  15754. syncTm && cancelAnimationFrame(syncTm);
  15755. syncTm = requestAnimationFrame(function() {
  15756. var opts, offset;
  15757. if (syncSize.enabled) {
  15758. fitSize(dialog);
  15759. }
  15760. });
  15761. },
  15762. checkEditing = function() {
  15763. var cldialog = 'elfinder-dialog',
  15764. dialogs = elfNode.children('.' + cldialog + '.' + fm.res('class', 'editing') + ':visible');
  15765. fm[dialogs.length? 'disable' : 'enable']();
  15766. },
  15767. propagationEvents = {},
  15768. syncTm, dialog, elfNode, restoreStyle;
  15769. if (fm && fm.ui) {
  15770. elfNode = fm.getUI();
  15771. } else {
  15772. elfNode = this.closest('.elfinder');
  15773. if (! fm) {
  15774. fm = elfNode.elfinder('instance');
  15775. }
  15776. }
  15777. if (typeof opts === 'string') {
  15778. if ((dialog = this.closest('.ui-dialog')).length) {
  15779. if (opts === 'open') {
  15780. if (dialog.css('display') === 'none') {
  15781. // Need dialog.show() and hide() to detect elements size in open() callbacks
  15782. dialog.trigger('posinit').show().trigger('open').hide();
  15783. dialog.fadeIn(120, function() {
  15784. fm.trigger('dialogopened', {dialog: dialog});
  15785. });
  15786. }
  15787. } else if (opts === 'close' || opts === 'destroy') {
  15788. dialog.stop(true);
  15789. if (dialog.is(':visible') || elfNode.is(':hidden')) {
  15790. dialog.trigger('close');
  15791. fm.trigger('dialogclosed', {dialog: dialog});
  15792. }
  15793. if (opts === 'destroy') {
  15794. dialog.remove();
  15795. fm.trigger('dialogremoved', {dialog: dialog});
  15796. }
  15797. } else if (opts === 'toTop') {
  15798. dialog.trigger('totop');
  15799. fm.trigger('dialogtotoped', {dialog: dialog});
  15800. } else if (opts === 'posInit') {
  15801. dialog.trigger('posinit');
  15802. fm.trigger('dialogposinited', {dialog: dialog});
  15803. } else if (opts === 'tabstopsInit') {
  15804. dialog.trigger('tabstopsInit');
  15805. fm.trigger('dialogtabstopsinited', {dialog: dialog});
  15806. } else if (opts === 'checkEditing') {
  15807. checkEditing();
  15808. }
  15809. }
  15810. return this;
  15811. }
  15812. opts = Object.assign({}, $.fn.elfinderdialog.defaults, opts);
  15813. if (opts.allowMinimize && opts.allowMinimize === 'auto') {
  15814. opts.allowMinimize = this.find('textarea,input').length? true : false;
  15815. }
  15816. opts.openMaximized = opts.allowMinimize && opts.openMaximized;
  15817. if (opts.headerBtnPos && opts.headerBtnPos === 'auto') {
  15818. opts.headerBtnPos = platformWin? 'right' : 'left';
  15819. }
  15820. if (opts.headerBtnOrder && opts.headerBtnOrder === 'auto') {
  15821. opts.headerBtnOrder = platformWin? 'close:maximize:minimize' : 'close:minimize:maximize';
  15822. }
  15823. if (opts.modal && opts.allowMinimize) {
  15824. opts.allowMinimize = false;
  15825. }
  15826. if (fm.options.dialogContained) {
  15827. syncSize.width = syncSize.height = syncSize.enabled = true;
  15828. } else {
  15829. syncSize.width = (opts.maxWidth === 'window');
  15830. syncSize.height = (opts.maxHeight === 'window');
  15831. if (syncSize.width || syncSize.height) {
  15832. syncSize.enabled = true;
  15833. }
  15834. }
  15835. propagationEvents = fm.arrayFlip(opts.propagationEvents, true);
  15836. this.filter(':not(.ui-dialog-content)').each(function() {
  15837. var self = $(this).addClass('ui-dialog-content ui-widget-content'),
  15838. clactive = 'elfinder-dialog-active',
  15839. cldialog = 'elfinder-dialog',
  15840. clnotify = 'elfinder-dialog-notify',
  15841. clhover = 'ui-state-hover',
  15842. cltabstop = 'elfinder-tabstop',
  15843. cl1stfocus = 'elfinder-focus',
  15844. clmodal = 'elfinder-dialog-modal',
  15845. id = parseInt(Math.random()*1000000),
  15846. titlebar = $('<div class="ui-dialog-titlebar ui-widget-header ui-corner-top ui-helper-clearfix"><span class="elfinder-dialog-title">'+opts.title+'</span></div>'),
  15847. buttonset = $('<div class="ui-dialog-buttonset"/>'),
  15848. buttonpane = $('<div class=" ui-helper-clearfix ui-dialog-buttonpane ui-widget-content"/>')
  15849. .append(buttonset),
  15850. btnWidth = 0,
  15851. btnCnt = 0,
  15852. tabstops = $(),
  15853. evCover = $('<div style="width:100%;height:100%;position:absolute;top:0px;left:0px;"/>').hide(),
  15854. numberToTel = function() {
  15855. if (opts.optimizeNumber) {
  15856. dialog.find('input[type=number]').each(function() {
  15857. $(this).attr('inputmode', 'numeric');
  15858. $(this).attr('pattern', '[0-9]*');
  15859. });
  15860. }
  15861. },
  15862. tabstopsInit = function() {
  15863. tabstops = dialog.find('.'+cltabstop);
  15864. if (tabstops.length) {
  15865. tabstops.attr('tabindex', '-1');
  15866. if (! tabstops.filter('.'+cl1stfocus).length) {
  15867. buttonset.children('.'+cltabstop+':'+(platformWin? 'first' : 'last')).addClass(cl1stfocus);
  15868. }
  15869. }
  15870. },
  15871. tabstopNext = function(cur) {
  15872. var elms = tabstops.filter(':visible:enabled'),
  15873. node = cur? null : elms.filter('.'+cl1stfocus+':first');
  15874. if (! node || ! node.length) {
  15875. node = elms.first();
  15876. }
  15877. if (cur) {
  15878. $.each(elms, function(i, elm) {
  15879. if (elm === cur && elms[i+1]) {
  15880. node = elms.eq(i+1);
  15881. return false;
  15882. }
  15883. });
  15884. }
  15885. return node;
  15886. },
  15887. tabstopPrev = function(cur) {
  15888. var elms = tabstops.filter(':visible:enabled'),
  15889. node = elms.last();
  15890. $.each(elms, function(i, elm) {
  15891. if (elm === cur && elms[i-1]) {
  15892. node = elms.eq(i-1);
  15893. return false;
  15894. }
  15895. });
  15896. return node;
  15897. },
  15898. makeHeaderBtn = function() {
  15899. $.each(opts.headerBtnOrder.split(':').reverse(), function(i, v) {
  15900. headerBtns[v] && headerBtns[v]();
  15901. });
  15902. if (platformWin) {
  15903. titlebar.children('.elfinder-titlebar-button').addClass('elfinder-titlebar-button-right');
  15904. }
  15905. },
  15906. headerBtns = {
  15907. close: function() {
  15908. 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>')
  15909. .on('mousedown', function(e) {
  15910. e.preventDefault();
  15911. e.stopPropagation();
  15912. self.elfinderdialog('close');
  15913. })
  15914. );
  15915. },
  15916. maximize: function() {
  15917. if (opts.allowMaximize) {
  15918. dialog.on('resize', function(e, data) {
  15919. var full, elm;
  15920. e.preventDefault();
  15921. e.stopPropagation();
  15922. if (data && data.maximize) {
  15923. elm = titlebar.find('.elfinder-titlebar-full');
  15924. full = (data.maximize === 'on');
  15925. elm.children('span.ui-icon')
  15926. .toggleClass('ui-icon-plusthick', ! full)
  15927. .toggleClass('ui-icon-arrowreturnthick-1-s', full);
  15928. if (full) {
  15929. try {
  15930. dialog.hasClass('ui-draggable') && dialog.draggable('disable');
  15931. dialog.hasClass('ui-resizable') && dialog.resizable('disable');
  15932. } catch(e) {}
  15933. self.css('width', '100%').css('height', dialog.height() - dialog.children('.ui-dialog-titlebar').outerHeight(true) - buttonpane.outerHeight(true));
  15934. } else {
  15935. self.attr('style', elm.data('style'));
  15936. elm.removeData('style');
  15937. posCheck();
  15938. try {
  15939. dialog.hasClass('ui-draggable') && dialog.draggable('enable');
  15940. dialog.hasClass('ui-resizable') && dialog.resizable('enable');
  15941. } catch(e) {}
  15942. }
  15943. dialog.trigger('resize', {init: true});
  15944. }
  15945. });
  15946. titlebar.prepend($('<span class="ui-widget-header ui-corner-all elfinder-titlebar-button elfinder-titlebar-full"><span class="ui-icon ui-icon-plusthick"/></span>')
  15947. .on('mousedown', function(e) {
  15948. var elm = $(this);
  15949. e.preventDefault();
  15950. e.stopPropagation();
  15951. if (!dialog.hasClass('elfinder-maximized') && typeof elm.data('style') === 'undefined') {
  15952. self.height(self.height());
  15953. elm.data('style', self.attr('style') || '');
  15954. }
  15955. fm.toggleMaximize(dialog);
  15956. typeof(opts.maximize) === 'function' && opts.maximize.call(self[0]);
  15957. })
  15958. );
  15959. }
  15960. },
  15961. minimize: function() {
  15962. var btn, mnode, doffset;
  15963. if (opts.allowMinimize) {
  15964. btn = $('<span class="ui-widget-header ui-corner-all elfinder-titlebar-button elfinder-titlebar-minimize"><span class="ui-icon ui-icon-minusthick"/></span>')
  15965. .on('mousedown', function(e) {
  15966. var $this = $(this),
  15967. tray = fm.getUI('bottomtray'),
  15968. dumStyle = { width: 70, height: 24 },
  15969. dum = $('<div/>').css(dumStyle).addClass(dialog.get(0).className + ' elfinder-dialog-minimized'),
  15970. pos = {};
  15971. e.preventDefault();
  15972. e.stopPropagation();
  15973. if (!dialog.data('minimized')) {
  15974. // minimize
  15975. doffset = dialog.data('minimized', true).position();
  15976. mnode = dialog.clone().on('mousedown', function() {
  15977. $this.trigger('mousedown');
  15978. }).removeClass('ui-draggable ui-resizable elfinder-frontmost');
  15979. tray.append(dum);
  15980. Object.assign(pos, dum.offset(), dumStyle);
  15981. dum.remove();
  15982. mnode.height(dialog.height()).children('.ui-dialog-content:first').empty();
  15983. fm.toHide(dialog.before(mnode));
  15984. mnode.children('.ui-dialog-content:first,.ui-dialog-buttonpane,.ui-resizable-handle').remove();
  15985. mnode.find('.elfinder-titlebar-minimize,.elfinder-titlebar-full').remove();
  15986. mnode.find('.ui-dialog-titlebar-close').on('mousedown', function(e) {
  15987. e.stopPropagation();
  15988. e.preventDefault();
  15989. mnode.remove();
  15990. dialog.show();
  15991. self.elfinderdialog('close');
  15992. });
  15993. mnode.animate(pos, function() {
  15994. mnode.attr('style', '')
  15995. .css({ maxWidth: dialog.width() })
  15996. .addClass('elfinder-dialog-minimized')
  15997. .appendTo(tray);
  15998. checkEditing();
  15999. typeof(opts.minimize) === 'function' && opts.minimize.call(self[0]);
  16000. });
  16001. } else {
  16002. //restore
  16003. dialog.removeData('minimized').before(mnode.css(Object.assign({'position': 'absolute'}, mnode.offset())));
  16004. fm.toFront(mnode);
  16005. mnode.animate(Object.assign({ width: dialog.width(), height: dialog.height() }, doffset), function() {
  16006. dialog.show();
  16007. fm.toFront(dialog);
  16008. mnode.remove();
  16009. posCheck();
  16010. checkEditing();
  16011. dialog.trigger('resize', {init: true});
  16012. typeof(opts.minimize) === 'function' && opts.minimize.call(self[0]);
  16013. });
  16014. }
  16015. });
  16016. titlebar.on('dblclick', function(e) {
  16017. $(this).children('.elfinder-titlebar-minimize').trigger('mousedown');
  16018. }).prepend(btn);
  16019. dialog.on('togleminimize', function() {
  16020. btn.trigger('mousedown');
  16021. });
  16022. }
  16023. }
  16024. },
  16025. dialog = $('<div class="ui-front ui-dialog ui-widget ui-widget-content ui-corner-all ui-draggable std42-dialog touch-punch '+cldialog+' '+opts.cssClass+'"/>')
  16026. .hide()
  16027. .append(self)
  16028. .appendTo(elfNode)
  16029. .draggable({
  16030. containment : fm.options.dialogContained? elfNode : null,
  16031. handle : '.ui-dialog-titlebar',
  16032. start : function() {
  16033. evCover.show();
  16034. },
  16035. drag : function(e, ui) {
  16036. var top = ui.offset.top,
  16037. left = ui.offset.left;
  16038. if (top < 0) {
  16039. ui.position.top = ui.position.top - top;
  16040. }
  16041. if (left < 0) {
  16042. ui.position.left = ui.position.left - left;
  16043. }
  16044. if (fm.options.dialogContained) {
  16045. ui.position.top < 0 && (ui.position.top = 0);
  16046. ui.position.left < 0 && (ui.position.left = 0);
  16047. }
  16048. },
  16049. stop : function(e, ui) {
  16050. evCover.hide();
  16051. dialog.css({height : opts.height});
  16052. self.data('draged', true);
  16053. }
  16054. })
  16055. .css({
  16056. width : opts.width,
  16057. height : opts.height,
  16058. minWidth : opts.minWidth,
  16059. minHeight : opts.minHeight,
  16060. maxWidth : opts.maxWidth,
  16061. maxHeight : opts.maxHeight
  16062. })
  16063. .on('touchstart touchmove touchend click dblclick mouseup mouseenter mouseleave mouseout mouseover mousemove', function(e) {
  16064. // stopPropagation of user action events
  16065. !propagationEvents[e.type] && e.stopPropagation();
  16066. })
  16067. .on('mousedown', function(e) {
  16068. !propagationEvents[e.type] && e.stopPropagation();
  16069. requestAnimationFrame(function() {
  16070. if (dialog.is(':visible') && !dialog.hasClass('elfinder-frontmost')) {
  16071. toFocusNode = $(':focus');
  16072. if (!toFocusNode.length) {
  16073. toFocusNode = void(0);
  16074. }
  16075. dialog.trigger('totop');
  16076. }
  16077. });
  16078. })
  16079. .on('open', function() {
  16080. dialog.data('margin-y', self.outerHeight(true) - self.height());
  16081. if (syncSize.enabled) {
  16082. if (opts.height && opts.height !== 'auto') {
  16083. dialog.trigger('resize', {init: true});
  16084. }
  16085. if (!syncSize.defaultSize) {
  16086. syncSize.defaultSize = { width: self.width(), height: self.height() };
  16087. }
  16088. fitSize(dialog);
  16089. dialog.trigger('resize').trigger('posinit');
  16090. elfNode.on('resize.'+fm.namespace, dialog, syncFunc);
  16091. }
  16092. if (!dialog.hasClass(clnotify)) {
  16093. elfNode.children('.'+cldialog+':visible:not(.'+clnotify+')').each(function() {
  16094. var d = $(this),
  16095. top = parseInt(d.css('top')),
  16096. left = parseInt(d.css('left')),
  16097. _top = parseInt(dialog.css('top')),
  16098. _left = parseInt(dialog.css('left')),
  16099. ct = Math.abs(top - _top) < 10,
  16100. cl = Math.abs(left - _left) < 10;
  16101. if (d[0] != dialog[0] && (ct || cl)) {
  16102. dialog.css({
  16103. top : ct ? (top + 10) : _top,
  16104. left : cl ? (left + 10) : _left
  16105. });
  16106. }
  16107. });
  16108. }
  16109. if (dialog.data('modal')) {
  16110. dialog.addClass(clmodal);
  16111. fm.getUI('overlay').elfinderoverlay('show');
  16112. }
  16113. dialog.trigger('totop');
  16114. opts.openMaximized && fm.toggleMaximize(dialog);
  16115. fm.trigger('dialogopen', {dialog: dialog});
  16116. typeof(opts.open) == 'function' && $.proxy(opts.open, self[0])();
  16117. if (opts.closeOnEscape) {
  16118. $(document).on('keydown.'+id, function(e) {
  16119. if (e.keyCode == $.ui.keyCode.ESCAPE && dialog.hasClass('elfinder-frontmost')) {
  16120. self.elfinderdialog('close');
  16121. }
  16122. });
  16123. }
  16124. dialog.hasClass(fm.res('class', 'editing')) && checkEditing();
  16125. })
  16126. .on('close', function(e) {
  16127. var dialogs, dfd;
  16128. if (opts.beforeclose && typeof opts.beforeclose === 'function') {
  16129. dfd = opts.beforeclose();
  16130. if (!dfd || !dfd.promise) {
  16131. dfd = !dfd? $.Deferred().reject() : $.Deferred().resolve();
  16132. }
  16133. } else {
  16134. dfd = $.Deferred().resolve();
  16135. }
  16136. dfd.done(function() {
  16137. syncSize.enabled && elfNode.off('resize.'+fm.namespace, syncFunc);
  16138. if (opts.closeOnEscape) {
  16139. $(document).off('keyup.'+id);
  16140. }
  16141. if (opts.allowMaximize) {
  16142. fm.toggleMaximize(dialog, false);
  16143. }
  16144. fm.toHide(dialog);
  16145. dialog.data('modal') && fm.getUI('overlay').elfinderoverlay('hide');
  16146. if (typeof(opts.close) == 'function') {
  16147. $.proxy(opts.close, self[0])();
  16148. }
  16149. if (opts.destroyOnClose && dialog.parent().length) {
  16150. dialog.hide().remove();
  16151. }
  16152. // get focus to next dialog
  16153. dialogs = elfNode.children('.'+cldialog+':visible');
  16154. dialog.hasClass(fm.res('class', 'editing')) && checkEditing();
  16155. });
  16156. })
  16157. .on('totop frontmost', function() {
  16158. var s = fm.storage('autoFocusDialog');
  16159. dialog.data('focusOnMouseOver', s? (s > 0) : fm.options.uiOptions.dialog.focusOnMouseOver);
  16160. if (dialog.data('minimized')) {
  16161. titlebar.children('.elfinder-titlebar-minimize').trigger('mousedown');
  16162. }
  16163. if (!dialog.data('modal') && fm.getUI('overlay').is(':visible')) {
  16164. fm.getUI('overlay').before(dialog);
  16165. } else {
  16166. fm.toFront(dialog);
  16167. }
  16168. elfNode.children('.'+cldialog+':not(.'+clmodal+')').removeClass(clactive);
  16169. dialog.addClass(clactive);
  16170. ! fm.UA.Mobile && (toFocusNode || tabstopNext()).trigger('focus');
  16171. toFocusNode = void(0);
  16172. })
  16173. .on('posinit', function() {
  16174. var css = opts.position,
  16175. nodeOffset, minTop, minLeft, outerSize, win, winSize, nodeFull;
  16176. if (dialog.hasClass('elfinder-maximized')) {
  16177. return;
  16178. }
  16179. if (! css && ! dialog.data('resizing')) {
  16180. nodeFull = elfNode.hasClass('elfinder-fullscreen');
  16181. dialog.css(nodeFull? {
  16182. maxWidth : '100%',
  16183. maxHeight : '100%',
  16184. overflow : 'auto'
  16185. } : restoreStyle);
  16186. if (fm.UA.Mobile && !nodeFull && dialog.data('rotated') === fm.UA.Rotated) {
  16187. return;
  16188. }
  16189. dialog.data('rotated', fm.UA.Rotated);
  16190. win = $(window);
  16191. nodeOffset = elfNode.offset();
  16192. outerSize = {
  16193. width : dialog.outerWidth(true),
  16194. height: dialog.outerHeight(true)
  16195. };
  16196. outerSize.right = nodeOffset.left + outerSize.width;
  16197. outerSize.bottom = nodeOffset.top + outerSize.height;
  16198. winSize = {
  16199. scrLeft: win.scrollLeft(),
  16200. scrTop : win.scrollTop(),
  16201. width : win.width(),
  16202. height : win.height()
  16203. };
  16204. winSize.right = winSize.scrLeft + winSize.width;
  16205. winSize.bottom = winSize.scrTop + winSize.height;
  16206. if (fm.options.dialogContained || nodeFull) {
  16207. minTop = 0;
  16208. minLeft = 0;
  16209. } else {
  16210. minTop = nodeOffset.top * -1 + winSize.scrTop;
  16211. minLeft = nodeOffset.left * -1 + winSize.scrLeft;
  16212. }
  16213. css = {
  16214. top : outerSize.height >= winSize.height? minTop : Math.max(minTop, parseInt((elfNode.height() - outerSize.height)/2 - 42)),
  16215. left : outerSize.width >= winSize.width ? minLeft : Math.max(minLeft, parseInt((elfNode.width() - outerSize.width)/2))
  16216. };
  16217. if (outerSize.right + css.left > winSize.right) {
  16218. css.left = Math.max(minLeft, winSize.right - outerSize.right);
  16219. }
  16220. if (outerSize.bottom + css.top > winSize.bottom) {
  16221. css.top = Math.max(minTop, winSize.bottom - outerSize.bottom);
  16222. }
  16223. }
  16224. if (opts.absolute) {
  16225. css.position = 'absolute';
  16226. }
  16227. css && dialog.css(css);
  16228. })
  16229. .on('resize', function(e, data) {
  16230. var oh = 0, init = data && data.init, h, minH;
  16231. if ((data && (data.minimize || data.maxmize)) || dialog.data('minimized')) {
  16232. return;
  16233. }
  16234. e.stopPropagation();
  16235. e.preventDefault();
  16236. dialog.children('.ui-widget-header,.ui-dialog-buttonpane').each(function() {
  16237. oh += $(this).outerHeight(true);
  16238. });
  16239. if (!init && syncSize.enabled && !e.originalEvent && !dialog.hasClass('elfinder-maximized')) {
  16240. h = Math.min(syncSize.defaultSize.height, Math.max(parseInt(dialog.css('max-height')), parseInt(dialog.css('min-height'))) - oh - dialog.data('margin-y'));
  16241. } else {
  16242. h = dialog.height() - oh - dialog.data('margin-y');
  16243. }
  16244. self.height(h);
  16245. if (init) {
  16246. return;
  16247. }
  16248. posCheck();
  16249. minH = self.height();
  16250. minH = (h < minH)? (minH + oh + dialog.data('margin-y')) : opts.minHeight;
  16251. dialog.css('min-height', minH);
  16252. dialog.data('hasResizable') && dialog.resizable('option', { minHeight: minH });
  16253. if (typeof(opts.resize) === 'function') {
  16254. $.proxy(opts.resize, self[0])(e, data);
  16255. }
  16256. })
  16257. .on('tabstopsInit', tabstopsInit)
  16258. .on('focus', '.'+cltabstop, function() {
  16259. $(this).addClass(clhover).parent('label').addClass(clhover);
  16260. this.id && $(this).parent().find('label[for='+this.id+']').addClass(clhover);
  16261. })
  16262. .on('click', 'select.'+cltabstop, function() {
  16263. var node = $(this);
  16264. node.data('keepFocus')? node.removeData('keepFocus') : node.data('keepFocus', true);
  16265. })
  16266. .on('blur', '.'+cltabstop, function() {
  16267. $(this).removeClass(clhover).removeData('keepFocus').parent('label').removeClass(clhover);
  16268. this.id && $(this).parent().find('label[for='+this.id+']').removeClass(clhover);
  16269. })
  16270. .on('mouseenter mouseleave', '.'+cltabstop+',label', function(e) {
  16271. var $this = $(this), labelfor;
  16272. if (this.nodeName === 'LABEL') {
  16273. if (!$this.children('.'+cltabstop).length && (!(labelfor = $this.attr('for')) || !$('#'+labelfor).hasClass(cltabstop))) {
  16274. return;
  16275. }
  16276. }
  16277. if (opts.btnHoverFocus && dialog.data('focusOnMouseOver')) {
  16278. if (e.type === 'mouseenter' && ! $(':focus').data('keepFocus')) {
  16279. $this.trigger('focus');
  16280. }
  16281. } else {
  16282. $this.toggleClass(clhover, e.type == 'mouseenter');
  16283. }
  16284. })
  16285. .on('keydown', '.'+cltabstop, function(e) {
  16286. var $this = $(this),
  16287. esc, move, moveTo;
  16288. if ($this.is(':focus')) {
  16289. esc = e.keyCode === $.ui.keyCode.ESCAPE;
  16290. if (e.keyCode === $.ui.keyCode.ENTER) {
  16291. e.preventDefault();
  16292. $this.trigger('click');
  16293. } else if (((e.keyCode === $.ui.keyCode.TAB) && e.shiftKey) || e.keyCode === $.ui.keyCode.LEFT || e.keyCode == $.ui.keyCode.UP) {
  16294. move = 'prev';
  16295. } else if (e.keyCode === $.ui.keyCode.TAB || e.keyCode == $.ui.keyCode.RIGHT || e.keyCode == $.ui.keyCode.DOWN) {
  16296. move = 'next';
  16297. }
  16298. if (move
  16299. &&
  16300. (
  16301. ($this.is('textarea') && !(e.ctrlKey || e.metaKey))
  16302. ||
  16303. ($this.is('select,span.ui-slider-handle') && e.keyCode !== $.ui.keyCode.TAB)
  16304. ||
  16305. ($this.is('input:not(:checkbox,:radio)') && (!(e.ctrlKey || e.metaKey) && e.keyCode === $.ui.keyCode[move === 'prev'? 'LEFT':'RIGHT']))
  16306. )
  16307. ) {
  16308. e.stopPropagation();
  16309. return;
  16310. }
  16311. if (!esc) {
  16312. e.stopPropagation();
  16313. } else if ($this.is('input:not(:checkbox,:radio),textarea')) {
  16314. if ($this.val() !== '') {
  16315. $this.val('');
  16316. e.stopPropagation();
  16317. }
  16318. }
  16319. if (move) {
  16320. e.preventDefault();
  16321. (move === 'prev'? tabstopPrev : tabstopNext)(this).trigger('focus');
  16322. }
  16323. }
  16324. })
  16325. .data({modal: opts.modal}),
  16326. posCheck = function() {
  16327. var node = fm.getUI(),
  16328. pos;
  16329. if (node.hasClass('elfinder-fullscreen')) {
  16330. pos = dialog.position();
  16331. dialog.css('top', Math.max(Math.min(Math.max(pos.top, 0), node.height() - 100), 0));
  16332. dialog.css('left', Math.max(Math.min(Math.max(pos.left, 0), node.width() - 200), 0));
  16333. }
  16334. },
  16335. maxSize, toFocusNode;
  16336. dialog.prepend(titlebar);
  16337. makeHeaderBtn();
  16338. $.each(opts.buttons, function(name, cb) {
  16339. var button = $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only '
  16340. +'elfinder-btncnt-'+(btnCnt++)+' '
  16341. +cltabstop
  16342. +'"><span class="ui-button-text">'+name+'</span></button>')
  16343. .on('click', $.proxy(cb, self[0]));
  16344. if (cb._cssClass) {
  16345. button.addClass(cb._cssClass);
  16346. }
  16347. if (platformWin) {
  16348. buttonset.append(button);
  16349. } else {
  16350. buttonset.prepend(button);
  16351. }
  16352. });
  16353. if (buttonset.children().length) {
  16354. dialog.append(buttonpane);
  16355. dialog.show();
  16356. buttonpane.find('button').each(function(i, btn) {
  16357. btnWidth += $(btn).outerWidth(true);
  16358. });
  16359. dialog.hide();
  16360. btnWidth += 20;
  16361. if (dialog.width() < btnWidth) {
  16362. dialog.width(btnWidth);
  16363. }
  16364. }
  16365. dialog.append(evCover);
  16366. if (syncSize.enabled) {
  16367. delta.width = dialog.outerWidth(true) - dialog.width() + ((dialog.outerWidth() - dialog.width()) / 2);
  16368. delta.height = dialog.outerHeight(true) - dialog.height() + ((dialog.outerHeight() - dialog.height()) / 2);
  16369. }
  16370. if (fm.options.dialogContained) {
  16371. maxSize = {
  16372. maxWidth: elfNode.width() - delta.width,
  16373. maxHeight: elfNode.height() - delta.height
  16374. };
  16375. opts.maxWidth = opts.maxWidth? Math.min(maxSize.maxWidth, opts.maxWidth) : maxSize.maxWidth;
  16376. opts.maxHeight = opts.maxHeight? Math.min(maxSize.maxHeight, opts.maxHeight) : maxSize.maxHeight;
  16377. dialog.css(maxSize);
  16378. }
  16379. restoreStyle = {
  16380. maxWidth : dialog.css('max-width'),
  16381. maxHeight : dialog.css('max-height'),
  16382. overflow : dialog.css('overflow')
  16383. };
  16384. if (opts.resizable) {
  16385. dialog.resizable({
  16386. minWidth : opts.minWidth,
  16387. minHeight : opts.minHeight,
  16388. maxWidth : opts.maxWidth,
  16389. maxHeight : opts.maxHeight,
  16390. start : function() {
  16391. evCover.show();
  16392. if (dialog.data('resizing') !== true && dialog.data('resizing')) {
  16393. clearTimeout(dialog.data('resizing'));
  16394. }
  16395. dialog.data('resizing', true);
  16396. },
  16397. stop : function(e, ui) {
  16398. evCover.hide();
  16399. dialog.data('resizing', setTimeout(function() {
  16400. dialog.data('resizing', false);
  16401. }, 200));
  16402. if (syncSize.enabled) {
  16403. syncSize.defaultSize = { width: self.width(), height: self.height() };
  16404. }
  16405. }
  16406. }).data('hasResizable', true);
  16407. }
  16408. numberToTel();
  16409. tabstopsInit();
  16410. typeof(opts.create) == 'function' && $.proxy(opts.create, this)();
  16411. if (opts.autoOpen) {
  16412. if (opts.open) {
  16413. requestAnimationFrame(function() {
  16414. self.elfinderdialog('open');
  16415. });
  16416. } else {
  16417. self.elfinderdialog('open');
  16418. }
  16419. }
  16420. if (opts.resize) {
  16421. fm.bind('themechange', function() {
  16422. setTimeout(function() {
  16423. dialog.data('margin-y', self.outerHeight(true) - self.height());
  16424. dialog.trigger('resize', {init: true});
  16425. }, 300);
  16426. });
  16427. }
  16428. });
  16429. return this;
  16430. };
  16431. $.fn.elfinderdialog.defaults = {
  16432. cssClass : '',
  16433. title : '',
  16434. modal : false,
  16435. resizable : true,
  16436. autoOpen : true,
  16437. closeOnEscape : true,
  16438. destroyOnClose : false,
  16439. buttons : {},
  16440. btnHoverFocus : true,
  16441. position : null,
  16442. absolute : false,
  16443. width : 320,
  16444. height : 'auto',
  16445. minWidth : 200,
  16446. minHeight : 70,
  16447. maxWidth : null,
  16448. maxHeight : null,
  16449. allowMinimize : 'auto',
  16450. allowMaximize : false,
  16451. openMaximized : false,
  16452. headerBtnPos : 'auto',
  16453. headerBtnOrder : 'auto',
  16454. optimizeNumber : true,
  16455. propagationEvents : ['mousemove', 'mouseup']
  16456. };
  16457. /*
  16458. * File: /js/ui/fullscreenbutton.js
  16459. */
  16460. /**
  16461. * @class elFinder toolbar button to switch full scrren mode.
  16462. *
  16463. * @author Naoki Sawada
  16464. **/
  16465. $.fn.elfinderfullscreenbutton = function(cmd) {
  16466. return this.each(function() {
  16467. var button = $(this).elfinderbutton(cmd),
  16468. icon = button.children('.elfinder-button-icon'),
  16469. tm;
  16470. cmd.change(function() {
  16471. tm && cancelAnimationFrame(tm);
  16472. tm = requestAnimationFrame(function() {
  16473. var fullscreen = cmd.value;
  16474. icon.addClass('elfinder-button-icon-fullscreen').toggleClass('elfinder-button-icon-unfullscreen', fullscreen);
  16475. cmd.className = fullscreen? 'unfullscreen' : '';
  16476. });
  16477. });
  16478. });
  16479. };
  16480. /*
  16481. * File: /js/ui/navbar.js
  16482. */
  16483. /**
  16484. * @class elfindernav - elFinder container for diretories tree and places
  16485. *
  16486. * @author Dmitry (dio) Levashov
  16487. **/
  16488. $.fn.elfindernavbar = function(fm, opts) {
  16489. this.not('.elfinder-navbar').each(function() {
  16490. var nav = $(this).hide().addClass('ui-state-default elfinder-navbar'),
  16491. parent = nav.css('overflow', 'hidden').parent(),
  16492. wz = parent.children('.elfinder-workzone').append(nav),
  16493. ltr = fm.direction == 'ltr',
  16494. delta, deltaW, handle, swipeHandle, autoHide, setWidth, navdock,
  16495. setWzRect = function() {
  16496. var cwd = fm.getUI('cwd'),
  16497. wz = fm.getUI('workzone'),
  16498. wzRect = wz.data('rectangle'),
  16499. cwdOffset = cwd.offset();
  16500. wz.data('rectangle', Object.assign(wzRect, { cwdEdge: (fm.direction === 'ltr')? cwdOffset.left : cwdOffset.left + cwd.width() }));
  16501. },
  16502. setDelta = function() {
  16503. nav.css('overflow', 'hidden');
  16504. delta = Math.round(nav.outerHeight() - nav.height());
  16505. deltaW = Math.round(navdock.outerWidth() - navdock.innerWidth());
  16506. nav.css('overflow', 'auto');
  16507. };
  16508. fm.one('init', function() {
  16509. var set = function() {
  16510. navdock =fm.getUI('navdock');
  16511. setDelta();
  16512. fm.trigger('wzresize');
  16513. };
  16514. fm.bind('wzresize', function() {
  16515. var navdockH = 0;
  16516. navdock.width(nav.outerWidth() - deltaW);
  16517. if (navdock.children().length > 1) {
  16518. navdockH = navdock.outerHeight(true);
  16519. }
  16520. nav.height(wz.height() - navdockH - delta);
  16521. });
  16522. if (fm.cssloaded) {
  16523. set();
  16524. } else {
  16525. fm.one('cssloaded', set);
  16526. }
  16527. })
  16528. .one('opendone',function() {
  16529. handle && handle.trigger('resize');
  16530. nav.css('overflow', 'auto');
  16531. }).bind('themechange', setDelta);
  16532. if (fm.UA.Touch) {
  16533. autoHide = fm.storage('autoHide') || {};
  16534. if (typeof autoHide.navbar === 'undefined') {
  16535. autoHide.navbar = (opts.autoHideUA && opts.autoHideUA.length > 0 && $.grep(opts.autoHideUA, function(v){ return fm.UA[v]? true : false; }).length);
  16536. fm.storage('autoHide', autoHide);
  16537. }
  16538. if (autoHide.navbar) {
  16539. fm.one('init', function() {
  16540. if (nav.children().length) {
  16541. fm.uiAutoHide.push(function(){ nav.stop(true, true).trigger('navhide', { duration: 'slow', init: true }); });
  16542. }
  16543. });
  16544. }
  16545. fm.bind('load', function() {
  16546. if (nav.children().length) {
  16547. swipeHandle = $('<div class="elfinder-navbar-swipe-handle"/>').hide().appendTo(wz);
  16548. if (swipeHandle.css('pointer-events') !== 'none') {
  16549. swipeHandle.remove();
  16550. swipeHandle = null;
  16551. }
  16552. }
  16553. });
  16554. nav.on('navshow navhide', function(e, data) {
  16555. var mode = (e.type === 'navshow')? 'show' : 'hide',
  16556. duration = (data && data.duration)? data.duration : 'fast',
  16557. handleW = (data && data.handleW)? data.handleW : Math.max(50, fm.getUI().width() / 10);
  16558. nav.stop(true, true)[mode]({
  16559. duration: duration,
  16560. step : function() {
  16561. fm.trigger('wzresize');
  16562. },
  16563. complete: function() {
  16564. if (swipeHandle) {
  16565. if (mode === 'show') {
  16566. swipeHandle.stop(true, true).hide();
  16567. } else {
  16568. swipeHandle.width(handleW? handleW : '');
  16569. fm.resources.blink(swipeHandle, 'slowonce');
  16570. }
  16571. }
  16572. fm.trigger('navbar'+ mode);
  16573. data.init && fm.trigger('uiautohide');
  16574. setWzRect();
  16575. }
  16576. });
  16577. autoHide.navbar = (mode !== 'show');
  16578. fm.storage('autoHide', Object.assign(fm.storage('autoHide'), {navbar: autoHide.navbar}));
  16579. }).on('touchstart', function(e) {
  16580. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  16581. e.originalEvent._preventSwipeX = true;
  16582. }
  16583. });
  16584. }
  16585. if (! fm.UA.Mobile) {
  16586. handle = nav.resizable({
  16587. handles : ltr ? 'e' : 'w',
  16588. minWidth : opts.minWidth || 150,
  16589. maxWidth : opts.maxWidth || 500,
  16590. resize : function() {
  16591. fm.trigger('wzresize');
  16592. },
  16593. stop : function(e, ui) {
  16594. fm.storage('navbarWidth', ui.size.width);
  16595. setWzRect();
  16596. }
  16597. })
  16598. .on('resize scroll', function(e) {
  16599. var $this = $(this),
  16600. tm = $this.data('posinit');
  16601. e.preventDefault();
  16602. e.stopPropagation();
  16603. if (! ltr && e.type === 'resize') {
  16604. nav.css('left', 0);
  16605. }
  16606. tm && cancelAnimationFrame(tm);
  16607. $this.data('posinit', requestAnimationFrame(function() {
  16608. var offset = (fm.UA.Opera && nav.scrollLeft())? 20 : 2;
  16609. handle.css('top', 0).css({
  16610. top : parseInt(nav.scrollTop())+'px',
  16611. left : ltr ? 'auto' : parseInt(nav.scrollRight() - offset) * -1,
  16612. right: ltr ? parseInt(nav.scrollLeft() - offset) * -1 : 'auto'
  16613. });
  16614. if (e.type === 'resize') {
  16615. fm.getUI('cwd').trigger('resize');
  16616. }
  16617. }));
  16618. })
  16619. .children('.ui-resizable-handle').addClass('ui-front');
  16620. }
  16621. if (setWidth = fm.storage('navbarWidth')) {
  16622. nav.width(setWidth);
  16623. } else {
  16624. if (fm.UA.Mobile) {
  16625. fm.one('cssloaded', function() {
  16626. var set = function() {
  16627. setWidth = nav.parent().width() / 2;
  16628. if (nav.data('defWidth') > setWidth) {
  16629. nav.width(setWidth);
  16630. } else {
  16631. nav.width(nav.data('defWidth'));
  16632. }
  16633. nav.data('width', nav.width());
  16634. fm.trigger('wzresize');
  16635. };
  16636. nav.data('defWidth', nav.width());
  16637. $(window).on('resize.' + fm.namespace, set);
  16638. set();
  16639. });
  16640. }
  16641. }
  16642. });
  16643. return this;
  16644. };
  16645. /*
  16646. * File: /js/ui/navdock.js
  16647. */
  16648. /**
  16649. * @class elfindernavdock - elFinder container for preview etc at below the navbar
  16650. *
  16651. * @author Naoki Sawada
  16652. **/
  16653. $.fn.elfindernavdock = function(fm, opts) {
  16654. this.not('.elfinder-navdock').each(function() {
  16655. var self = $(this).hide().addClass('ui-state-default elfinder-navdock touch-punch'),
  16656. node = self.parent(),
  16657. wz = node.children('.elfinder-workzone').append(self),
  16658. resize = function(to, h) {
  16659. var curH = h || self.height(),
  16660. diff = to - curH,
  16661. len = Object.keys(sizeSyncs).length,
  16662. calc = len? diff / len : 0,
  16663. ovf;
  16664. if (diff) {
  16665. ovf = self.css('overflow');
  16666. self.css('overflow', 'hidden');
  16667. self.height(to);
  16668. $.each(sizeSyncs, function(id, n) {
  16669. n.height(n.height() + calc).trigger('resize.' + fm.namespace);
  16670. });
  16671. fm.trigger('wzresize');
  16672. self.css('overflow', ovf);
  16673. }
  16674. },
  16675. handle = $('<div class="ui-front ui-resizable-handle ui-resizable-n"/>').appendTo(self),
  16676. sizeSyncs = {},
  16677. resizeFn = [],
  16678. initMaxHeight = (parseInt(opts.initMaxHeight) || 50) / 100,
  16679. maxHeight = (parseInt(opts.maxHeight) || 90) / 100,
  16680. basicHeight, hasNode;
  16681. self.data('addNode', function(cNode, opts) {
  16682. var wzH = fm.getUI('workzone').height(),
  16683. imaxH = wzH * initMaxHeight,
  16684. curH, tH, mH;
  16685. opts = Object.assign({
  16686. first: false,
  16687. sizeSync: true,
  16688. init: false
  16689. }, opts);
  16690. if (!cNode.attr('id')) {
  16691. cNode.attr('id', fm.namespace+'-navdock-' + (+new Date()));
  16692. }
  16693. opts.sizeSync && (sizeSyncs[cNode.attr('id')] = cNode);
  16694. curH = self.height();
  16695. tH = curH + cNode.outerHeight(true);
  16696. if (opts.first) {
  16697. handle.after(cNode);
  16698. } else {
  16699. self.append(cNode);
  16700. }
  16701. hasNode = true;
  16702. self.resizable('enable').height(tH).show();
  16703. fm.trigger('wzresize');
  16704. if (opts.init) {
  16705. mH = fm.storage('navdockHeight');
  16706. if (mH) {
  16707. tH = mH;
  16708. } else {
  16709. tH = tH > imaxH? imaxH : tH;
  16710. }
  16711. basicHeight = tH;
  16712. }
  16713. resize(Math.min(tH, wzH * maxHeight));
  16714. return self;
  16715. }).data('removeNode', function(nodeId, appendTo) {
  16716. var cNode = $('#'+nodeId);
  16717. delete sizeSyncs[nodeId];
  16718. self.height(self.height() - $('#'+nodeId).outerHeight(true));
  16719. if (appendTo) {
  16720. if (appendTo === 'detach') {
  16721. cNode = cNode.detach();
  16722. } else {
  16723. appendTo.append(cNode);
  16724. }
  16725. } else {
  16726. cNode.remove();
  16727. }
  16728. if (self.children().length <= 1) {
  16729. hasNode = false;
  16730. self.resizable('disable').height(0).hide();
  16731. }
  16732. fm.trigger('wzresize');
  16733. return cNode;
  16734. });
  16735. if (! opts.disabled) {
  16736. fm.one('init', function() {
  16737. var ovf;
  16738. if (fm.getUI('navbar').children().not('.ui-resizable-handle').length) {
  16739. self.data('dockEnabled', true);
  16740. self.resizable({
  16741. maxHeight: fm.getUI('workzone').height() * maxHeight,
  16742. handles: { n: handle },
  16743. start: function(e, ui) {
  16744. ovf = self.css('overflow');
  16745. self.css('overflow', 'hidden');
  16746. fm.trigger('navdockresizestart', {event: e, ui: ui}, true);
  16747. },
  16748. resize: function(e, ui) {
  16749. self.css('top', '');
  16750. fm.trigger('wzresize', { inNavdockResize : true });
  16751. },
  16752. stop: function(e, ui) {
  16753. fm.trigger('navdockresizestop', {event: e, ui: ui}, true);
  16754. self.css('top', '');
  16755. basicHeight = ui.size.height;
  16756. fm.storage('navdockHeight', basicHeight);
  16757. resize(basicHeight, ui.originalSize.height);
  16758. self.css('overflow', ovf);
  16759. }
  16760. });
  16761. fm.bind('wzresize', function(e) {
  16762. var minH, maxH, h;
  16763. if (self.is(':visible')) {
  16764. maxH = fm.getUI('workzone').height() * maxHeight;
  16765. if (! e.data || ! e.data.inNavdockResize) {
  16766. h = self.height();
  16767. if (maxH < basicHeight) {
  16768. if (Math.abs(h - maxH) > 1) {
  16769. resize(maxH);
  16770. }
  16771. } else {
  16772. if (Math.abs(h - basicHeight) > 1) {
  16773. resize(basicHeight);
  16774. }
  16775. }
  16776. }
  16777. self.resizable('option', 'maxHeight', maxH);
  16778. }
  16779. }).bind('themechange', function() {
  16780. var oldH = Math.round(self.height());
  16781. requestAnimationFrame(function() {
  16782. var curH = Math.round(self.height()),
  16783. diff = oldH - curH;
  16784. if (diff !== 0) {
  16785. resize(self.height(), curH - diff);
  16786. }
  16787. });
  16788. });
  16789. }
  16790. fm.bind('navbarshow navbarhide', function(e) {
  16791. self[hasNode && e.type === 'navbarshow'? 'show' : 'hide']();
  16792. });
  16793. });
  16794. }
  16795. });
  16796. return this;
  16797. };
  16798. /*
  16799. * File: /js/ui/overlay.js
  16800. */
  16801. $.fn.elfinderoverlay = function(opts) {
  16802. var fm = this.parent().elfinder('instance'),
  16803. o, cnt, show, hide;
  16804. this.filter(':not(.elfinder-overlay)').each(function() {
  16805. opts = Object.assign({}, opts);
  16806. $(this).addClass('ui-front ui-widget-overlay elfinder-overlay')
  16807. .hide()
  16808. .on('mousedown', function(e) {
  16809. e.preventDefault();
  16810. e.stopPropagation();
  16811. })
  16812. .data({
  16813. cnt : 0,
  16814. show : typeof(opts.show) == 'function' ? opts.show : function() { },
  16815. hide : typeof(opts.hide) == 'function' ? opts.hide : function() { }
  16816. });
  16817. });
  16818. if (opts == 'show') {
  16819. o = this.eq(0);
  16820. cnt = o.data('cnt') + 1;
  16821. show = o.data('show');
  16822. fm.toFront(o);
  16823. o.data('cnt', cnt);
  16824. if (o.is(':hidden')) {
  16825. o.show();
  16826. show();
  16827. }
  16828. }
  16829. if (opts == 'hide') {
  16830. o = this.eq(0);
  16831. cnt = o.data('cnt') - 1;
  16832. hide = o.data('hide');
  16833. o.data('cnt', cnt);
  16834. if (cnt <= 0) {
  16835. o.hide();
  16836. hide();
  16837. }
  16838. }
  16839. return this;
  16840. };
  16841. /*
  16842. * File: /js/ui/panel.js
  16843. */
  16844. $.fn.elfinderpanel = function(fm) {
  16845. return this.each(function() {
  16846. var panel = $(this).addClass('elfinder-panel ui-state-default ui-corner-all'),
  16847. margin = 'margin-'+(fm.direction == 'ltr' ? 'left' : 'right');
  16848. fm.one('load', function(e) {
  16849. var navbar = fm.getUI('navbar');
  16850. panel.css(margin, parseInt(navbar.outerWidth(true)));
  16851. navbar.on('resize', function(e) {
  16852. e.preventDefault();
  16853. e.stopPropagation();
  16854. panel.is(':visible') && panel.css(margin, parseInt(navbar.outerWidth(true)));
  16855. });
  16856. });
  16857. });
  16858. };
  16859. /*
  16860. * File: /js/ui/path.js
  16861. */
  16862. /**
  16863. * @class elFinder ui
  16864. * Display current folder path in statusbar.
  16865. * Click on folder name in path - open folder
  16866. *
  16867. * @author Dmitry (dio) Levashov
  16868. **/
  16869. $.fn.elfinderpath = function(fm, options) {
  16870. return this.each(function() {
  16871. var query = '',
  16872. target = '',
  16873. mimes = [],
  16874. place = 'statusbar',
  16875. clHover= fm.res('class', 'hover'),
  16876. prefix = 'path' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-',
  16877. wzbase = $('<div class="ui-widget-header ui-helper-clearfix elfinder-workzone-path"/>'),
  16878. path = $(this).addClass('elfinder-path').html('&nbsp;')
  16879. .on('mousedown', 'span.elfinder-path-dir', function(e) {
  16880. var hash = $(this).attr('id').substr(prefix.length);
  16881. e.preventDefault();
  16882. if (hash != fm.cwd().hash) {
  16883. $(this).addClass(clHover);
  16884. if (query) {
  16885. fm.exec('search', query, { target: hash, mime: mimes.join(' ') });
  16886. } else {
  16887. fm.trigger('select', {selected : [hash]}).exec('open', hash);
  16888. }
  16889. }
  16890. })
  16891. .prependTo(fm.getUI('statusbar').show()),
  16892. roots = $('<div class="elfinder-path-roots"/>').on('click', function(e) {
  16893. e.stopPropagation();
  16894. e.preventDefault();
  16895. var roots = $.map(fm.roots, function(h) { return fm.file(h); }),
  16896. raw = [];
  16897. $.each(roots, function(i, f) {
  16898. if (! f.phash && fm.root(fm.cwd().hash, true) !== f.hash) {
  16899. raw.push({
  16900. label : fm.escape(f.i18 || f.name),
  16901. icon : 'home',
  16902. callback : function() { fm.exec('open', f.hash); },
  16903. options : {
  16904. iconClass : f.csscls || '',
  16905. iconImg : f.icon || ''
  16906. }
  16907. });
  16908. }
  16909. });
  16910. fm.trigger('contextmenu', {
  16911. raw: raw,
  16912. x: e.pageX,
  16913. y: e.pageY
  16914. });
  16915. }).append('<span class="elfinder-button-icon elfinder-button-icon-menu" />').appendTo(wzbase),
  16916. render = function(cwd) {
  16917. var dirs = [],
  16918. names = [];
  16919. $.each(fm.parents(cwd), function(i, hash) {
  16920. var c = (cwd === hash)? 'elfinder-path-dir elfinder-path-cwd' : 'elfinder-path-dir',
  16921. f = fm.file(hash),
  16922. name = fm.escape(f.i18 || f.name);
  16923. names.push(name);
  16924. dirs.push('<span id="'+prefix+hash+'" class="'+c+'" title="'+names.join(fm.option('separator'))+'">'+name+'</span>');
  16925. });
  16926. return dirs.join('<span class="elfinder-path-other">'+fm.option('separator')+'</span>');
  16927. },
  16928. toWorkzone = function() {
  16929. var prev;
  16930. path.children('span.elfinder-path-dir').attr('style', '');
  16931. prev = fm.direction === 'ltr'? $('#'+prefix + fm.cwd().hash).prevAll('span.elfinder-path-dir:first') : $();
  16932. path.scrollLeft(prev.length? prev.position().left : 0);
  16933. },
  16934. fit = function() {
  16935. if (fm.UA.CSS.flex) {
  16936. return;
  16937. }
  16938. var dirs = path.children('span.elfinder-path-dir'),
  16939. cnt = dirs.length,
  16940. m, bg = 0, ids;
  16941. if (place === 'workzone' || cnt < 2) {
  16942. dirs.attr('style', '');
  16943. return;
  16944. }
  16945. path.width(path.css('max-width'));
  16946. dirs.css({maxWidth: (100/cnt)+'%', display: 'inline-block'});
  16947. m = path.width() - 9;
  16948. path.children('span.elfinder-path-other').each(function() {
  16949. m -= $(this).width();
  16950. });
  16951. ids = [];
  16952. dirs.each(function(i) {
  16953. var dir = $(this),
  16954. w = dir.width();
  16955. m -= w;
  16956. if (w < this.scrollWidth) {
  16957. ids.push(i);
  16958. }
  16959. });
  16960. path.width('');
  16961. if (ids.length) {
  16962. if (m > 0) {
  16963. m = m / ids.length;
  16964. $.each(ids, function(i, k) {
  16965. var d = $(dirs[k]);
  16966. d.css('max-width', d.width() + m);
  16967. });
  16968. }
  16969. dirs.last().attr('style', '');
  16970. } else {
  16971. dirs.attr('style', '');
  16972. }
  16973. },
  16974. hasUiTree, hasUiStat;
  16975. fm.one('init', function() {
  16976. hasUiTree = fm.getUI('tree').length;
  16977. hasUiStat = fm.getUI('stat').length;
  16978. if (! hasUiTree && options.toWorkzoneWithoutNavbar) {
  16979. wzbase.append(path).insertBefore(fm.getUI('workzone'));
  16980. place = 'workzone';
  16981. fm.bind('open', toWorkzone)
  16982. .one('opendone', function() {
  16983. fm.getUI().trigger('resize');
  16984. });
  16985. }
  16986. })
  16987. .bind('open searchend parents', function() {
  16988. var dirs = [];
  16989. query = '';
  16990. target = '';
  16991. mimes = [];
  16992. path.html(render(fm.cwd().hash));
  16993. if (Object.keys(fm.roots).length > 1) {
  16994. path.css('margin', '');
  16995. roots.show();
  16996. } else {
  16997. path.css('margin', 0);
  16998. roots.hide();
  16999. }
  17000. !hasUiStat && fit();
  17001. })
  17002. .bind('searchstart', function(e) {
  17003. if (e.data) {
  17004. query = e.data.query || '';
  17005. target = e.data.target || '';
  17006. mimes = e.data.mimes || [];
  17007. }
  17008. })
  17009. .bind('search', function(e) {
  17010. var dirs = [],
  17011. html = '';
  17012. if (target) {
  17013. html = render(target);
  17014. } else {
  17015. html = fm.i18n('btnAll');
  17016. }
  17017. path.html('<span class="elfinder-path-other">'+fm.i18n('searcresult') + ': </span>' + html);
  17018. fit();
  17019. })
  17020. // on swipe to navbar show/hide
  17021. .bind('navbarshow navbarhide', function() {
  17022. var wz = fm.getUI('workzone');
  17023. if (this.type === 'navbarshow') {
  17024. fm.unbind('open', toWorkzone);
  17025. path.prependTo(fm.getUI('statusbar'));
  17026. wzbase.detach();
  17027. place = 'statusbar';
  17028. } else {
  17029. wzbase.append(path).insertBefore(wz);
  17030. place = 'workzone';
  17031. toWorkzone();
  17032. fm.bind('open', toWorkzone);
  17033. }
  17034. fm.trigger('uiresize');
  17035. })
  17036. .bind('resize uistatchange', fit);
  17037. });
  17038. };
  17039. /*
  17040. * File: /js/ui/places.js
  17041. */
  17042. /**
  17043. * @class elFinder places/favorites ui
  17044. *
  17045. * @author Dmitry (dio) Levashov
  17046. * @author Naoki Sawada
  17047. **/
  17048. $.fn.elfinderplaces = function(fm, opts) {
  17049. return this.each(function() {
  17050. var dirs = {},
  17051. c = 'class',
  17052. navdir = fm.res(c, 'navdir'),
  17053. collapsed = fm.res(c, 'navcollapse'),
  17054. expanded = fm.res(c, 'navexpand'),
  17055. hover = fm.res(c, 'hover'),
  17056. clroot = fm.res(c, 'treeroot'),
  17057. dropover = fm.res(c, 'adroppable'),
  17058. tpl = fm.res('tpl', 'placedir'),
  17059. ptpl = fm.res('tpl', 'perms'),
  17060. spinner = $(fm.res('tpl', 'navspinner')),
  17061. suffix = opts.suffix? opts.suffix : '',
  17062. key = 'places' + suffix,
  17063. menuTimer = null,
  17064. /**
  17065. * Convert places dir node into dir hash
  17066. *
  17067. * @param String directory id
  17068. * @return String
  17069. **/
  17070. id2hash = function(id) { return id.substr(6); },
  17071. /**
  17072. * Convert places dir node into dir hash
  17073. *
  17074. * @param String directory id
  17075. * @return String
  17076. **/
  17077. hash2id = function(hash) { return 'place-'+hash; },
  17078. /**
  17079. * Save current places state
  17080. *
  17081. * @return void
  17082. **/
  17083. save = function() {
  17084. var hashes = [], data = {};
  17085. hashes = $.map(subtree.children().find('[id]'), function(n) {
  17086. return id2hash(n.id);
  17087. });
  17088. if (hashes.length) {
  17089. $.each(hashes.reverse(), function(i, h) {
  17090. data[h] = dirs[h];
  17091. });
  17092. } else {
  17093. data = null;
  17094. }
  17095. fm.storage(key, data);
  17096. },
  17097. /**
  17098. * Init dir at places
  17099. *
  17100. * @return void
  17101. **/
  17102. init = function() {
  17103. var dat, hashes;
  17104. key = 'places'+(opts.suffix? opts.suffix : ''),
  17105. dirs = {};
  17106. dat = fm.storage(key);
  17107. if (typeof dat === 'string') {
  17108. // old data type elFinder <= 2.1.12
  17109. dat = $.grep(dat.split(','), function(hash) { return hash? true : false;});
  17110. $.each(dat, function(i, d) {
  17111. var dir = d.split('#');
  17112. dirs[dir[0]] = dir[1]? dir[1] : dir[0];
  17113. });
  17114. } else if ($.isPlainObject(dat)) {
  17115. dirs = dat;
  17116. }
  17117. // allow modify `dirs`
  17118. /**
  17119. * example for preset places
  17120. *
  17121. * elfinderInstance.bind('placesload', function(e, fm) {
  17122. * //if (fm.storage(e.data.storageKey) === null) { // for first time only
  17123. * if (!fm.storage(e.data.storageKey)) { // for empty places
  17124. * e.data.dirs[targetHash] = fallbackName; // preset folder
  17125. * }
  17126. * }
  17127. **/
  17128. fm.trigger('placesload', {dirs: dirs, storageKey: key}, true);
  17129. hashes = Object.keys(dirs);
  17130. if (hashes.length) {
  17131. root.prepend(spinner);
  17132. fm.request({
  17133. data : {cmd : 'info', targets : hashes},
  17134. preventDefault : true
  17135. })
  17136. .done(function(data) {
  17137. var exists = {};
  17138. data.files && data.files.length && fm.cache(data.files);
  17139. $.each(data.files, function(i, f) {
  17140. var hash = f.hash;
  17141. exists[hash] = f;
  17142. });
  17143. $.each(dirs, function(h, f) {
  17144. add(exists[h] || Object.assign({notfound: true}, f));
  17145. });
  17146. if (fm.storage('placesState') > 0) {
  17147. root.trigger('click');
  17148. }
  17149. })
  17150. .always(function() {
  17151. spinner.remove();
  17152. });
  17153. }
  17154. },
  17155. /**
  17156. * Return node for given dir object
  17157. *
  17158. * @param Object directory object
  17159. * @return jQuery
  17160. **/
  17161. create = function(dir, hash) {
  17162. return $(tpl.replace(/\{id\}/, hash2id(dir? dir.hash : hash))
  17163. .replace(/\{name\}/, fm.escape(dir? dir.i18 || dir.name : hash))
  17164. .replace(/\{cssclass\}/, dir? (fm.perms2class(dir) + (dir.notfound? ' elfinder-na' : '') + (dir.csscls? ' '+dir.csscls : '')) : '')
  17165. .replace(/\{permissions\}/, (dir && (!dir.read || !dir.write || dir.notfound))? ptpl : '')
  17166. .replace(/\{title\}/, (dir && dir.path)? fm.escape(dir.path) : '')
  17167. .replace(/\{symlink\}/, '')
  17168. .replace(/\{style\}/, (dir && dir.icon)? fm.getIconStyle(dir) : ''));
  17169. },
  17170. /**
  17171. * Add new node into places
  17172. *
  17173. * @param Object directory object
  17174. * @return void
  17175. **/
  17176. add = function(dir) {
  17177. var node, hash;
  17178. if (dir.mime !== 'directory') {
  17179. return false;
  17180. }
  17181. hash = dir.hash;
  17182. if (!fm.files().hasOwnProperty(hash)) {
  17183. // update cache
  17184. fm.trigger('tree', {tree: [dir]});
  17185. }
  17186. node = create(dir, hash);
  17187. dirs[hash] = dir;
  17188. subtree.prepend(node);
  17189. root.addClass(collapsed);
  17190. sortBtn.toggle(subtree.children().length > 1);
  17191. return true;
  17192. },
  17193. /**
  17194. * Remove dir from places
  17195. *
  17196. * @param String directory hash
  17197. * @return String removed name
  17198. **/
  17199. remove = function(hash) {
  17200. var name = null, tgt, cnt;
  17201. if (dirs[hash]) {
  17202. delete dirs[hash];
  17203. tgt = $('#'+hash2id(hash));
  17204. if (tgt.length) {
  17205. name = tgt.text();
  17206. tgt.parent().remove();
  17207. cnt = subtree.children().length;
  17208. sortBtn.toggle(cnt > 1);
  17209. if (! cnt) {
  17210. root.removeClass(collapsed);
  17211. places.removeClass(expanded);
  17212. subtree.slideToggle(false);
  17213. }
  17214. }
  17215. }
  17216. return name;
  17217. },
  17218. /**
  17219. * Move up dir on places
  17220. *
  17221. * @param String directory hash
  17222. * @return void
  17223. **/
  17224. moveup = function(hash) {
  17225. var self = $('#'+hash2id(hash)),
  17226. tgt = self.parent(),
  17227. prev = tgt.prev('div'),
  17228. cls = 'ui-state-hover',
  17229. ctm = fm.getUI('contextmenu');
  17230. menuTimer && clearTimeout(menuTimer);
  17231. if (prev.length) {
  17232. ctm.find(':first').data('placesHash', hash);
  17233. self.addClass(cls);
  17234. tgt.insertBefore(prev);
  17235. prev = tgt.prev('div');
  17236. menuTimer = setTimeout(function() {
  17237. self.removeClass(cls);
  17238. if (ctm.find(':first').data('placesHash') === hash) {
  17239. ctm.hide().empty();
  17240. }
  17241. }, 1500);
  17242. }
  17243. if(!prev.length) {
  17244. self.removeClass(cls);
  17245. ctm.hide().empty();
  17246. }
  17247. },
  17248. /**
  17249. * Update dir at places
  17250. *
  17251. * @param Object directory
  17252. * @param String previous hash
  17253. * @return Boolean
  17254. **/
  17255. update = function(dir, preHash) {
  17256. var hash = dir.hash,
  17257. tgt = $('#'+hash2id(preHash || hash)),
  17258. node = create(dir, hash);
  17259. if (tgt.length > 0) {
  17260. tgt.parent().replaceWith(node);
  17261. dirs[hash] = dir;
  17262. return true;
  17263. } else {
  17264. return false;
  17265. }
  17266. },
  17267. /**
  17268. * Remove all dir from places
  17269. *
  17270. * @return void
  17271. **/
  17272. clear = function() {
  17273. subtree.empty();
  17274. root.removeClass(collapsed);
  17275. places.removeClass(expanded);
  17276. subtree.slideToggle(false);
  17277. },
  17278. /**
  17279. * Sort places dirs A-Z
  17280. *
  17281. * @return void
  17282. **/
  17283. sort = function() {
  17284. $.each(dirs, function(h, f) {
  17285. var dir = fm.file(h) || f,
  17286. node = create(dir, h),
  17287. ret = null;
  17288. if (!dir) {
  17289. node.hide();
  17290. }
  17291. if (subtree.children().length) {
  17292. $.each(subtree.children(), function() {
  17293. var current = $(this);
  17294. if ((dir.i18 || dir.name).localeCompare(current.children('.'+navdir).text()) < 0) {
  17295. ret = !node.insertBefore(current);
  17296. return ret;
  17297. }
  17298. });
  17299. if (ret !== null) {
  17300. return true;
  17301. }
  17302. }
  17303. !$('#'+hash2id(h)).length && subtree.append(node);
  17304. });
  17305. save();
  17306. },
  17307. // sort button
  17308. sortBtn = $('<span class="elfinder-button-icon elfinder-button-icon-sort elfinder-places-root-icon" title="'+fm.i18n('cmdsort')+'"/>')
  17309. .hide()
  17310. .on('click', function(e) {
  17311. e.stopPropagation();
  17312. subtree.empty();
  17313. sort();
  17314. }
  17315. ),
  17316. /**
  17317. * Node - wrapper for places root
  17318. *
  17319. * @type jQuery
  17320. **/
  17321. wrapper = create({
  17322. hash : 'root-'+fm.namespace,
  17323. name : fm.i18n(opts.name, 'places'),
  17324. read : true,
  17325. write : true
  17326. }),
  17327. /**
  17328. * Places root node
  17329. *
  17330. * @type jQuery
  17331. **/
  17332. root = wrapper.children('.'+navdir)
  17333. .addClass(clroot)
  17334. .on('click', function(e) {
  17335. e.stopPropagation();
  17336. if (root.hasClass(collapsed)) {
  17337. places.toggleClass(expanded);
  17338. subtree.slideToggle();
  17339. fm.storage('placesState', places.hasClass(expanded)? 1 : 0);
  17340. }
  17341. })
  17342. .append(sortBtn),
  17343. /**
  17344. * Container for dirs
  17345. *
  17346. * @type jQuery
  17347. **/
  17348. subtree = wrapper.children('.'+fm.res(c, 'navsubtree')),
  17349. /**
  17350. * Main places container
  17351. *
  17352. * @type jQuery
  17353. **/
  17354. places = $(this).addClass(fm.res(c, 'tree')+' elfinder-places ui-corner-all')
  17355. .hide()
  17356. .append(wrapper)
  17357. .appendTo(fm.getUI('navbar'))
  17358. .on('mouseenter mouseleave', '.'+navdir, function(e) {
  17359. $(this).toggleClass('ui-state-hover', (e.type == 'mouseenter'));
  17360. })
  17361. .on('click', '.'+navdir, function(e) {
  17362. var p = $(this);
  17363. if (p.data('longtap')) {
  17364. e.stopPropagation();
  17365. return;
  17366. }
  17367. ! p.hasClass('elfinder-na') && fm.exec('open', p.attr('id').substr(6));
  17368. })
  17369. .on('contextmenu', '.'+navdir+':not(.'+clroot+')', function(e) {
  17370. var self = $(this),
  17371. hash = self.attr('id').substr(6);
  17372. e.preventDefault();
  17373. fm.trigger('contextmenu', {
  17374. raw : [{
  17375. label : fm.i18n('moveUp'),
  17376. icon : 'up',
  17377. remain : true,
  17378. callback : function() { moveup(hash); save(); }
  17379. },'|',{
  17380. label : fm.i18n('rmFromPlaces'),
  17381. icon : 'rm',
  17382. callback : function() { remove(hash); save(); }
  17383. }],
  17384. 'x' : e.pageX,
  17385. 'y' : e.pageY
  17386. });
  17387. self.addClass('ui-state-hover');
  17388. fm.getUI('contextmenu').children().on('mouseenter', function() {
  17389. self.addClass('ui-state-hover');
  17390. });
  17391. fm.bind('closecontextmenu', function() {
  17392. self.removeClass('ui-state-hover');
  17393. });
  17394. })
  17395. .droppable({
  17396. tolerance : 'pointer',
  17397. accept : '.elfinder-cwd-file-wrapper,.elfinder-tree-dir,.elfinder-cwd-file',
  17398. hoverClass : fm.res('class', 'adroppable'),
  17399. classes : { // Deprecated hoverClass jQueryUI>=1.12.0
  17400. 'ui-droppable-hover': fm.res('class', 'adroppable')
  17401. },
  17402. over : function(e, ui) {
  17403. var helper = ui.helper,
  17404. dir = $.grep(helper.data('files'), function(h) { return (fm.file(h).mime === 'directory' && !dirs[h])? true : false; });
  17405. e.stopPropagation();
  17406. helper.data('dropover', helper.data('dropover') + 1);
  17407. if (fm.insideWorkzone(e.pageX, e.pageY)) {
  17408. if (dir.length > 0) {
  17409. helper.addClass('elfinder-drag-helper-plus');
  17410. fm.trigger('unlockfiles', {files : helper.data('files'), helper: helper});
  17411. } else {
  17412. $(this).removeClass(dropover);
  17413. }
  17414. }
  17415. },
  17416. out : function(e, ui) {
  17417. var helper = ui.helper;
  17418. e.stopPropagation();
  17419. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  17420. $(this).removeData('dropover')
  17421. .removeClass(dropover);
  17422. },
  17423. drop : function(e, ui) {
  17424. var helper = ui.helper,
  17425. resolve = true;
  17426. $.each(helper.data('files'), function(i, hash) {
  17427. var dir = fm.file(hash);
  17428. if (dir && dir.mime == 'directory' && !dirs[dir.hash]) {
  17429. add(dir);
  17430. } else {
  17431. resolve = false;
  17432. }
  17433. });
  17434. save();
  17435. resolve && helper.hide();
  17436. }
  17437. })
  17438. // for touch device
  17439. .on('touchstart', '.'+navdir+':not(.'+clroot+')', function(e) {
  17440. if (e.originalEvent.touches.length > 1) {
  17441. return;
  17442. }
  17443. var hash = $(this).attr('id').substr(6),
  17444. p = $(this)
  17445. .addClass(hover)
  17446. .data('longtap', null)
  17447. .data('tmlongtap', setTimeout(function(){
  17448. // long tap
  17449. p.data('longtap', true);
  17450. fm.trigger('contextmenu', {
  17451. raw : [{
  17452. label : fm.i18n('rmFromPlaces'),
  17453. icon : 'rm',
  17454. callback : function() { remove(hash); save(); }
  17455. }],
  17456. 'x' : e.originalEvent.touches[0].pageX,
  17457. 'y' : e.originalEvent.touches[0].pageY
  17458. });
  17459. }, 500));
  17460. })
  17461. .on('touchmove touchend', '.'+navdir+':not(.'+clroot+')', function(e) {
  17462. clearTimeout($(this).data('tmlongtap'));
  17463. if (e.type == 'touchmove') {
  17464. $(this).removeClass(hover);
  17465. }
  17466. });
  17467. if ($.fn.sortable) {
  17468. subtree.addClass('touch-punch')
  17469. .sortable({
  17470. appendTo : fm.getUI(),
  17471. revert : false,
  17472. helper : function(e) {
  17473. var dir = $(e.target).parent();
  17474. dir.children().removeClass('ui-state-hover');
  17475. return $('<div class="ui-widget elfinder-place-drag elfinder-'+fm.direction+'"/>')
  17476. .append($('<div class="elfinder-navbar"/>').show().append(dir.clone()));
  17477. },
  17478. stop : function(e, ui) {
  17479. var target = $(ui.item[0]),
  17480. top = places.offset().top,
  17481. left = places.offset().left,
  17482. width = places.width(),
  17483. height = places.height(),
  17484. x = e.pageX,
  17485. y = e.pageY;
  17486. if (!(x > left && x < left+width && y > top && y < y+height)) {
  17487. remove(id2hash(target.children(':first').attr('id')));
  17488. save();
  17489. }
  17490. },
  17491. update : function(e, ui) {
  17492. save();
  17493. }
  17494. });
  17495. }
  17496. // "on regist" for command exec
  17497. $(this).on('regist', function(e, files){
  17498. var added = false;
  17499. $.each(files, function(i, dir) {
  17500. if (dir && dir.mime == 'directory' && !dirs[dir.hash]) {
  17501. if (add(dir)) {
  17502. added = true;
  17503. }
  17504. }
  17505. });
  17506. added && save();
  17507. });
  17508. // on fm load - show places and load files from backend
  17509. fm.one('load', function() {
  17510. var dat, hashes;
  17511. if (fm.oldAPI) {
  17512. return;
  17513. }
  17514. places.show().parent().show();
  17515. init();
  17516. fm.change(function(e) {
  17517. var changed = false;
  17518. $.each(e.data.changed, function(i, file) {
  17519. if (dirs[file.hash]) {
  17520. if (file.mime !== 'directory') {
  17521. if (remove(file.hash)) {
  17522. changed = true;
  17523. }
  17524. } else {
  17525. if (update(file)) {
  17526. changed = true;
  17527. }
  17528. }
  17529. }
  17530. });
  17531. changed && save();
  17532. })
  17533. .bind('rename', function(e) {
  17534. var changed = false;
  17535. if (e.data.removed) {
  17536. $.each(e.data.removed, function(i, hash) {
  17537. if (e.data.added[i]) {
  17538. if (update(e.data.added[i], hash)) {
  17539. changed = true;
  17540. }
  17541. }
  17542. });
  17543. }
  17544. changed && save();
  17545. })
  17546. .bind('rm paste', function(e) {
  17547. var names = [],
  17548. changed = false;
  17549. if (e.data.removed) {
  17550. $.each(e.data.removed, function(i, hash) {
  17551. var name = remove(hash);
  17552. name && names.push(name);
  17553. });
  17554. }
  17555. if (names.length) {
  17556. changed = true;
  17557. }
  17558. if (e.data.added && names.length) {
  17559. $.each(e.data.added, function(i, file) {
  17560. if ($.inArray(file.name, names) !== 1) {
  17561. file.mime == 'directory' && add(file);
  17562. }
  17563. });
  17564. }
  17565. changed && save();
  17566. })
  17567. .bind('sync netmount', function() {
  17568. var ev = this,
  17569. opSuffix = opts.suffix? opts.suffix : '',
  17570. hashes;
  17571. if (ev.type === 'sync') {
  17572. // check is change of opts.suffix
  17573. if (suffix !== opSuffix) {
  17574. suffix = opSuffix;
  17575. clear();
  17576. init();
  17577. return;
  17578. }
  17579. }
  17580. hashes = Object.keys(dirs);
  17581. if (hashes.length) {
  17582. root.prepend(spinner);
  17583. fm.request({
  17584. data : {cmd : 'info', targets : hashes},
  17585. preventDefault : true
  17586. })
  17587. .done(function(data) {
  17588. var exists = {},
  17589. updated = false,
  17590. cwd = fm.cwd().hash;
  17591. $.each(data.files || [], function(i, file) {
  17592. var hash = file.hash;
  17593. exists[hash] = file;
  17594. if (!fm.files().hasOwnProperty(file.hash)) {
  17595. // update cache
  17596. fm.trigger('tree', {tree: [file]});
  17597. }
  17598. });
  17599. $.each(dirs, function(h, f) {
  17600. if (f.notfound === Boolean(exists[h])) {
  17601. if ((f.phash === cwd && ev.type !== 'netmount') || (exists[h] && exists[h].mime !== 'directory')) {
  17602. if (remove(h)) {
  17603. updated = true;
  17604. }
  17605. } else {
  17606. if (update(exists[h] || Object.assign({notfound: true}, f))) {
  17607. updated = true;
  17608. }
  17609. }
  17610. } else if (exists[h] && exists[h].phash != cwd) {
  17611. // update permission of except cwd
  17612. update(exists[h]);
  17613. }
  17614. });
  17615. updated && save();
  17616. })
  17617. .always(function() {
  17618. spinner.remove();
  17619. });
  17620. }
  17621. });
  17622. });
  17623. });
  17624. };
  17625. /*
  17626. * File: /js/ui/searchbutton.js
  17627. */
  17628. /**
  17629. * @class elFinder toolbar search button widget.
  17630. *
  17631. * @author Dmitry (dio) Levashov
  17632. **/
  17633. $.fn.elfindersearchbutton = function(cmd) {
  17634. return this.each(function() {
  17635. var result = false,
  17636. fm = cmd.fm,
  17637. isopts = cmd.options.incsearch || { enable: false },
  17638. sTypes = cmd.options.searchTypes,
  17639. id = function(name){return fm.namespace + fm.escape(name);},
  17640. toolbar= fm.getUI('toolbar'),
  17641. btnCls = fm.res('class', 'searchbtn'),
  17642. button = $(this)
  17643. .hide()
  17644. .addClass('ui-widget-content elfinder-button '+btnCls)
  17645. .on('click', function(e) {
  17646. e.stopPropagation();
  17647. }),
  17648. search = function() {
  17649. input.data('inctm') && clearTimeout(input.data('inctm'));
  17650. var val = $.trim(input.val()),
  17651. from = !$('#' + id('SearchFromAll')).prop('checked'),
  17652. mime = $('#' + id('SearchMime')).prop('checked'),
  17653. type = '';
  17654. if (from) {
  17655. if ($('#' + id('SearchFromVol')).prop('checked')) {
  17656. from = fm.root(fm.cwd().hash);
  17657. } else {
  17658. from = fm.cwd().hash;
  17659. }
  17660. }
  17661. if (mime) {
  17662. mime = val;
  17663. val = '.';
  17664. }
  17665. if (typeSet) {
  17666. type = typeSet.children('input:checked').val();
  17667. }
  17668. if (val) {
  17669. input.trigger('focus');
  17670. cmd.exec(val, from, mime, type).done(function() {
  17671. result = true;
  17672. }).fail(function() {
  17673. abort();
  17674. });
  17675. } else {
  17676. fm.trigger('searchend');
  17677. }
  17678. },
  17679. abort = function() {
  17680. input.data('inctm') && clearTimeout(input.data('inctm'));
  17681. input.val('').trigger('blur');
  17682. if (result || incVal) {
  17683. result = false;
  17684. incVal = '';
  17685. fm.lazy(function() {
  17686. fm.trigger('searchend');
  17687. });
  17688. }
  17689. },
  17690. incVal = '',
  17691. input = $('<input type="text" size="42"/>')
  17692. .on('focus', function() {
  17693. inFocus = true;
  17694. incVal = '';
  17695. button.addClass('ui-state-active');
  17696. fm.trigger('uiresize');
  17697. opts && opts.slideDown(function() {
  17698. // Care for on browser window re-active
  17699. button.addClass('ui-state-active');
  17700. fm.toFront(opts);
  17701. });
  17702. })
  17703. .on('blur', function() {
  17704. inFocus = false;
  17705. if (opts) {
  17706. if (!opts.data('infocus')) {
  17707. opts.slideUp(function() {
  17708. button.removeClass('ui-state-active');
  17709. fm.trigger('uiresize');
  17710. fm.toHide(opts);
  17711. });
  17712. } else {
  17713. opts.data('infocus', false);
  17714. }
  17715. } else {
  17716. button.removeClass('ui-state-active');
  17717. }
  17718. })
  17719. .appendTo(button)
  17720. // to avoid fm shortcuts on arrows
  17721. .on('keypress', function(e) {
  17722. e.stopPropagation();
  17723. })
  17724. .on('keydown', function(e) {
  17725. e.stopPropagation();
  17726. if (e.keyCode === $.ui.keyCode.ENTER) {
  17727. search();
  17728. } else if (e.keyCode === $.ui.keyCode.ESCAPE) {
  17729. e.preventDefault();
  17730. abort();
  17731. }
  17732. }),
  17733. opts, typeSet, cwdReady, inFocus;
  17734. if (isopts.enable) {
  17735. isopts.minlen = isopts.minlen || 2;
  17736. isopts.wait = isopts.wait || 500;
  17737. input
  17738. .attr('title', fm.i18n('incSearchOnly'))
  17739. .on('compositionstart', function() {
  17740. input.data('composing', true);
  17741. })
  17742. .on('compositionend', function() {
  17743. input.removeData('composing');
  17744. input.trigger('input'); // for IE, edge
  17745. })
  17746. .on('input', function() {
  17747. if (! input.data('composing')) {
  17748. input.data('inctm') && clearTimeout(input.data('inctm'));
  17749. input.data('inctm', setTimeout(function() {
  17750. var val = input.val();
  17751. if (val.length === 0 || val.length >= isopts.minlen) {
  17752. (incVal !== val) && fm.trigger('incsearchstart', { query: val });
  17753. incVal = val;
  17754. if (val === '' && fm.searchStatus.state > 1 && fm.searchStatus.query) {
  17755. input.val(fm.searchStatus.query).trigger('select');
  17756. }
  17757. }
  17758. }, isopts.wait));
  17759. }
  17760. });
  17761. if (fm.UA.ltIE8) {
  17762. input.on('keydown', function(e) {
  17763. if (e.keyCode === 229) {
  17764. input.data('imetm') && clearTimeout(input.data('imetm'));
  17765. input.data('composing', true);
  17766. input.data('imetm', setTimeout(function() {
  17767. input.removeData('composing');
  17768. }, 100));
  17769. }
  17770. })
  17771. .on('keyup', function(e) {
  17772. input.data('imetm') && clearTimeout(input.data('imetm'));
  17773. if (input.data('composing')) {
  17774. e.keyCode === $.ui.keyCode.ENTER && input.trigger('compositionend');
  17775. } else {
  17776. input.trigger('input');
  17777. }
  17778. });
  17779. }
  17780. }
  17781. $('<span class="ui-icon ui-icon-search" title="'+cmd.title+'"/>')
  17782. .appendTo(button)
  17783. .on('mousedown', function(e) {
  17784. e.stopPropagation();
  17785. e.preventDefault();
  17786. if (button.hasClass('ui-state-active')) {
  17787. search();
  17788. } else {
  17789. input.trigger('focus');
  17790. }
  17791. });
  17792. $('<span class="ui-icon ui-icon-close"/>')
  17793. .appendTo(button)
  17794. .on('mousedown', function(e) {
  17795. e.stopPropagation();
  17796. e.preventDefault();
  17797. if (input.val() === '' && !button.hasClass('ui-state-active')) {
  17798. input.trigger('focus');
  17799. } else {
  17800. abort();
  17801. }
  17802. });
  17803. // wait when button will be added to DOM
  17804. fm.bind('toolbarload', function(){
  17805. var parent = button.parent();
  17806. if (parent.length) {
  17807. toolbar.prepend(button.show());
  17808. parent.remove();
  17809. // position icons for ie7
  17810. if (fm.UA.ltIE7) {
  17811. var icon = button.children(fm.direction == 'ltr' ? '.ui-icon-close' : '.ui-icon-search');
  17812. icon.css({
  17813. right : '',
  17814. left : parseInt(button.width())-icon.outerWidth(true)
  17815. });
  17816. }
  17817. }
  17818. });
  17819. fm
  17820. .one('init', function() {
  17821. fm.getUI('cwd').on('touchstart click', function() {
  17822. inFocus && input.trigger('blur');
  17823. });
  17824. })
  17825. .one('open', function() {
  17826. opts = (fm.api < 2.1)? null : $('<div class="ui-front ui-widget ui-widget-content elfinder-button-search-menu ui-corner-all"/>')
  17827. .append(
  17828. $('<div class="buttonset"/>')
  17829. .append(
  17830. $('<input id="'+id('SearchFromCwd')+'" name="serchfrom" type="radio" checked="checked"/><label for="'+id('SearchFromCwd')+'">'+fm.i18n('btnCwd')+'</label>'),
  17831. $('<input id="'+id('SearchFromVol')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromVol')+'">'+fm.i18n('btnVolume')+'</label>'),
  17832. $('<input id="'+id('SearchFromAll')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromAll')+'">'+fm.i18n('btnAll')+'</label>')
  17833. ),
  17834. $('<div class="buttonset elfinder-search-type"/>')
  17835. .append(
  17836. $('<input id="'+id('SearchName')+'" name="serchcol" type="radio" checked="checked" value="SearchName"/><label for="'+id('SearchName')+'">'+fm.i18n('btnFileName')+'</label>')
  17837. )
  17838. )
  17839. .hide()
  17840. .appendTo(fm.getUI());
  17841. if (opts) {
  17842. if (sTypes) {
  17843. typeSet = opts.find('.elfinder-search-type');
  17844. $.each(cmd.options.searchTypes, function(i, v) {
  17845. typeSet.append($('<input id="'+id(i)+'" name="serchcol" type="radio" value="'+fm.escape(i)+'"/><label for="'+id(i)+'">'+fm.i18n(v.name)+'</label>'));
  17846. });
  17847. }
  17848. opts.find('div.buttonset').buttonset();
  17849. $('#'+id('SearchFromAll')).next('label').attr('title', fm.i18n('searchTarget', fm.i18n('btnAll')));
  17850. if (sTypes) {
  17851. $.each(sTypes, function(i, v) {
  17852. if (v.title) {
  17853. $('#'+id(i)).next('label').attr('title', fm.i18n(v.title));
  17854. }
  17855. });
  17856. }
  17857. opts.on('mousedown', 'div.buttonset', function(e){
  17858. e.stopPropagation();
  17859. opts.data('infocus', true);
  17860. })
  17861. .on('click', 'input', function(e) {
  17862. e.stopPropagation();
  17863. $.trim(input.val())? search() : input.trigger('focus');
  17864. })
  17865. .on('close', function() {
  17866. input.trigger('blur');
  17867. });
  17868. }
  17869. })
  17870. .bind('searchend', function() {
  17871. input.val('');
  17872. })
  17873. .bind('open parents', function() {
  17874. var dirs = [],
  17875. volroot = fm.file(fm.root(fm.cwd().hash));
  17876. if (volroot) {
  17877. $.each(fm.parents(fm.cwd().hash), function(i, hash) {
  17878. dirs.push(fm.file(hash).name);
  17879. });
  17880. $('#'+id('SearchFromCwd')).next('label').attr('title', fm.i18n('searchTarget', dirs.join(fm.option('separator'))));
  17881. $('#'+id('SearchFromVol')).next('label').attr('title', fm.i18n('searchTarget', volroot.name));
  17882. }
  17883. })
  17884. .bind('open', function() {
  17885. incVal && abort();
  17886. })
  17887. .bind('cwdinit', function() {
  17888. cwdReady = false;
  17889. })
  17890. .bind('cwdrender',function() {
  17891. cwdReady = true;
  17892. })
  17893. .bind('keydownEsc', function() {
  17894. if (incVal && incVal.substr(0, 1) === '/') {
  17895. incVal = '';
  17896. input.val('');
  17897. fm.trigger('searchend');
  17898. }
  17899. })
  17900. .shortcut({
  17901. pattern : 'ctrl+f f3',
  17902. description : cmd.title,
  17903. callback : function() {
  17904. input.trigger('select').trigger('focus');
  17905. }
  17906. })
  17907. .shortcut({
  17908. 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',
  17909. description : fm.i18n('firstLetterSearch'),
  17910. callback : function(e) {
  17911. if (! cwdReady) { return; }
  17912. var code = e.originalEvent.keyCode,
  17913. next = function() {
  17914. var sel = fm.selected(),
  17915. key = $.ui.keyCode[(!sel.length || $('#'+fm.cwdHash2Id(sel[0])).next('[id]').length)? 'RIGHT' : 'HOME'];
  17916. $(document).trigger($.Event('keydown', { keyCode: key, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  17917. },
  17918. val;
  17919. if (code >= 96 && code <= 105) {
  17920. code -= 48;
  17921. }
  17922. val = '/' + String.fromCharCode(code);
  17923. if (incVal !== val) {
  17924. input.val(val);
  17925. incVal = val;
  17926. fm
  17927. .trigger('incsearchstart', { query: val })
  17928. .one('cwdrender', next);
  17929. } else{
  17930. next();
  17931. }
  17932. }
  17933. });
  17934. });
  17935. };
  17936. /*
  17937. * File: /js/ui/sortbutton.js
  17938. */
  17939. /**
  17940. * @class elFinder toolbar button menu with sort variants.
  17941. *
  17942. * @author Dmitry (dio) Levashov
  17943. **/
  17944. $.fn.elfindersortbutton = function(cmd) {
  17945. return this.each(function() {
  17946. var fm = cmd.fm,
  17947. name = cmd.name,
  17948. c = 'class',
  17949. disabled = fm.res(c, 'disabled'),
  17950. hover = fm.res(c, 'hover'),
  17951. item = 'elfinder-button-menu-item',
  17952. selected = item+'-selected',
  17953. asc = selected+'-asc',
  17954. desc = selected+'-desc',
  17955. text = $('<span class="elfinder-button-text">'+cmd.title+'</span>'),
  17956. button = $(this).addClass('ui-state-default elfinder-button elfinder-menubutton elfiner-button-'+name)
  17957. .attr('title', cmd.title)
  17958. .append('<span class="elfinder-button-icon elfinder-button-icon-'+name+'"/>', text)
  17959. .on('mouseenter mouseleave', function(e) { !button.hasClass(disabled) && button.toggleClass(hover, e.type === 'mouseenter'); })
  17960. .on('click', function(e) {
  17961. if (!button.hasClass(disabled)) {
  17962. e.stopPropagation();
  17963. menu.is(':hidden') && fm.getUI().click();
  17964. menu.css(getMenuOffset()).slideToggle({
  17965. duration: 100,
  17966. done: function(e) {
  17967. fm[menu.is(':visible')? 'toFront' : 'toHide'](menu);
  17968. }
  17969. });
  17970. }
  17971. }),
  17972. hide = function() { fm.toHide(menu); },
  17973. menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>')
  17974. .hide()
  17975. .appendTo(fm.getUI())
  17976. .on('mouseenter mouseleave', '.'+item, function(e) { $(this).toggleClass(hover, e.type === 'mouseenter'); })
  17977. .on('click', function(e) {
  17978. e.preventDefault();
  17979. e.stopPropagation();
  17980. })
  17981. .on('close', hide),
  17982. update = function() {
  17983. menu.children('[rel]').removeClass(selected+' '+asc+' '+desc)
  17984. .filter('[rel="'+fm.sortType+'"]')
  17985. .addClass(selected+' '+(fm.sortOrder == 'asc' ? asc : desc));
  17986. menu.children('.elfinder-sort-stick').toggleClass(selected, fm.sortStickFolders);
  17987. menu.children('.elfinder-sort-tree').toggleClass(selected, fm.sortAlsoTreeview);
  17988. },
  17989. getMenuOffset = function() {
  17990. var baseOffset = fm.getUI().offset(),
  17991. buttonOffset = button.offset();
  17992. return {
  17993. top : buttonOffset.top - baseOffset.top,
  17994. left : buttonOffset.left - baseOffset.left
  17995. };
  17996. },
  17997. tm;
  17998. text.hide();
  17999. $.each(fm.sortRules, function(name, value) {
  18000. 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));
  18001. });
  18002. menu.children().on('click', function(e) {
  18003. cmd.exec([], $(this).removeClass(hover).attr('rel'));
  18004. });
  18005. $('<div class="'+item+' '+item+'-separated elfinder-sort-ext elfinder-sort-stick"><span class="ui-icon ui-icon-check"/>'+fm.i18n('sortFoldersFirst')+'</div>')
  18006. .appendTo(menu)
  18007. .on('click', function() {
  18008. cmd.exec([], 'stick');
  18009. });
  18010. if ($.fn.elfindertree && $.inArray('tree', fm.options.ui) !== -1) {
  18011. $('<div class="'+item+' '+item+'-separated elfinder-sort-ext elfinder-sort-tree"><span class="ui-icon ui-icon-check"/>'+fm.i18n('sortAlsoTreeview')+'</div>')
  18012. .appendTo(menu)
  18013. .on('click', function() {
  18014. cmd.exec([], 'tree');
  18015. });
  18016. }
  18017. fm.bind('disable select', hide).getUI().on('click', hide);
  18018. fm.bind('open', function() {
  18019. menu.children('[rel]').each(function() {
  18020. var $this = $(this);
  18021. $this.toggle(fm.sorters[$this.attr('rel')]);
  18022. });
  18023. }).bind('sortchange', update);
  18024. if (menu.children().length > 1) {
  18025. cmd.change(function() {
  18026. tm && cancelAnimationFrame(tm);
  18027. tm = requestAnimationFrame(function() {
  18028. button.toggleClass(disabled, cmd.disabled());
  18029. update();
  18030. });
  18031. })
  18032. .change();
  18033. } else {
  18034. button.addClass(disabled);
  18035. }
  18036. });
  18037. };
  18038. /*
  18039. * File: /js/ui/stat.js
  18040. */
  18041. /**
  18042. * @class elFinder ui
  18043. * Display number of files/selected files and its size in statusbar
  18044. *
  18045. * @author Dmitry (dio) Levashov
  18046. **/
  18047. $.fn.elfinderstat = function(fm) {
  18048. return this.each(function() {
  18049. var size = $(this).addClass('elfinder-stat-size'),
  18050. sel = $('<div class="elfinder-stat-selected"/>')
  18051. .on('click', 'a', function(e) {
  18052. var hash = $(this).data('hash');
  18053. e.preventDefault();
  18054. fm.exec('opendir', [ hash ]);
  18055. }),
  18056. titleitems = fm.i18n('items'),
  18057. titlesel = fm.i18n('selected'),
  18058. titlesize = fm.i18n('size'),
  18059. setstat = function(files) {
  18060. var c = 0,
  18061. s = 0,
  18062. cwd = fm.cwd(),
  18063. calc = true,
  18064. hasSize = true;
  18065. if (cwd.sizeInfo || cwd.size) {
  18066. s = cwd.size;
  18067. calc = false;
  18068. }
  18069. $.each(files, function(i, file) {
  18070. c++;
  18071. if (calc) {
  18072. s += parseInt(file.size) || 0;
  18073. if (hasSize === true && file.mime === 'directory' && !file.sizeInfo) {
  18074. hasSize = false;
  18075. }
  18076. }
  18077. });
  18078. 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>')
  18079. .attr('title', size.text());
  18080. fm.trigger('uistatchange');
  18081. },
  18082. setIncsearchStat = function(data) {
  18083. size.find('span.elfinder-stat-incsearch').html(data? data.hashes.length + ' / ' : '');
  18084. size.attr('title', size.text());
  18085. fm.trigger('uistatchange');
  18086. },
  18087. setSelect = function(files) {
  18088. var s = 0,
  18089. c = 0,
  18090. dirs = [],
  18091. path, file;
  18092. if (files.length === 1) {
  18093. file = files[0];
  18094. s = file.size;
  18095. if (fm.searchStatus.state === 2) {
  18096. path = fm.escape(file.path? file.path.replace(/\/[^\/]*$/, '') : '..');
  18097. dirs.push('<a href="#elf_'+file.phash+'" data-hash="'+file.hash+'" title="'+path+'">'+path+'</a>');
  18098. }
  18099. dirs.push(fm.escape(file.i18 || file.name));
  18100. sel.html(dirs.join('/') + (s > 0 ? ', '+fm.formatSize(s) : ''));
  18101. } else if (files.length) {
  18102. $.each(files, function(i, file) {
  18103. c++;
  18104. s += parseInt(file.size)||0;
  18105. });
  18106. sel.html(c ? titlesel+': '+c+', '+titlesize+': '+fm.formatSize(s) : '&nbsp;');
  18107. } else {
  18108. sel.html('');
  18109. }
  18110. sel.attr('title', sel.text());
  18111. fm.trigger('uistatchange');
  18112. };
  18113. fm.getUI('statusbar').prepend(size).append(sel).show();
  18114. if (fm.UA.Mobile && $.fn.tooltip) {
  18115. fm.getUI('statusbar').tooltip({
  18116. classes: {
  18117. 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow'
  18118. },
  18119. tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow',
  18120. track: true
  18121. });
  18122. }
  18123. fm
  18124. .bind('cwdhasheschange', function(e) {
  18125. setstat($.map(e.data, function(h) { return fm.file(h); }));
  18126. })
  18127. .change(function(e) {
  18128. var files = e.data.changed || [],
  18129. cwdHash = fm.cwd().hash;
  18130. $.each(files, function() {
  18131. if (this.hash === cwdHash) {
  18132. if (this.size) {
  18133. size.children('.elfinder-stat-size').addClass('elfinder-stat-size-recursive').html(fm.i18n('sum')+': '+fm.formatSize(this.size));
  18134. size.attr('title', size.text());
  18135. }
  18136. return false;
  18137. }
  18138. });
  18139. })
  18140. .select(function() {
  18141. setSelect(fm.selectedFiles());
  18142. })
  18143. .bind('open', function() {
  18144. setSelect([]);
  18145. })
  18146. .bind('incsearch', function(e) {
  18147. setIncsearchStat(e.data);
  18148. })
  18149. .bind('incsearchend', function() {
  18150. setIncsearchStat();
  18151. })
  18152. ;
  18153. });
  18154. };
  18155. /*
  18156. * File: /js/ui/toast.js
  18157. */
  18158. /**
  18159. * @class elFinder toast
  18160. *
  18161. * This was created inspired by the toastr. Thanks to developers of toastr.
  18162. * CodeSeven/toastr: http://johnpapa.net <https://github.com/CodeSeven/toastr>
  18163. *
  18164. * @author Naoki Sawada
  18165. **/
  18166. $.fn.elfindertoast = function(opts, fm) {
  18167. var defOpts = Object.assign({
  18168. mode: 'success', // or 'info', 'warning' and 'error'
  18169. msg: '',
  18170. showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery
  18171. showDuration: 300,
  18172. showEasing: 'swing', //swing and linear are built into jQuery
  18173. onShown: undefined,
  18174. hideMethod: 'fadeOut',
  18175. hideDuration: 1500,
  18176. hideEasing: 'swing',
  18177. onHidden: undefined,
  18178. timeOut: 3000,
  18179. extNode: undefined,
  18180. button: undefined,
  18181. width: undefined
  18182. }, $.isPlainObject(fm.options.uiOptions.toast.defaults)? fm.options.uiOptions.toast.defaults : {});
  18183. return this.each(function() {
  18184. opts = Object.assign({}, defOpts, opts || {});
  18185. var self = $(this),
  18186. show = function(notm) {
  18187. self.stop();
  18188. fm.toFront(self);
  18189. self[opts.showMethod]({
  18190. duration: opts.showDuration,
  18191. easing: opts.showEasing,
  18192. complete: function() {
  18193. opts.onShown && opts.onShown();
  18194. if (!notm && opts.timeOut) {
  18195. rmTm = setTimeout(rm, opts.timeOut);
  18196. }
  18197. }
  18198. });
  18199. },
  18200. rm = function() {
  18201. self[opts.hideMethod]({
  18202. duration: opts.hideDuration,
  18203. easing: opts.hideEasing,
  18204. complete: function() {
  18205. opts.onHidden && opts.onHidden();
  18206. self.remove();
  18207. }
  18208. });
  18209. },
  18210. rmTm;
  18211. self.on('click', function(e) {
  18212. e.stopPropagation();
  18213. e.preventDefault();
  18214. rmTm && clearTimeout(rmTm);
  18215. opts.onHidden && opts.onHidden();
  18216. self.stop().remove();
  18217. }).on('mouseenter mouseleave', function(e) {
  18218. if (opts.timeOut) {
  18219. rmTm && clearTimeout(rmTm);
  18220. rmTm = null;
  18221. if (e.type === 'mouseenter') {
  18222. show(true);
  18223. } else {
  18224. rmTm = setTimeout(rm, opts.timeOut);
  18225. }
  18226. }
  18227. }).hide().addClass('toast-' + opts.mode).append($('<div class="elfinder-toast-msg"/>').html(opts.msg.replace(/%([a-zA-Z0-9]+)%/g, function(m, m1) {
  18228. return fm.i18n(m1);
  18229. })));
  18230. if (opts.extNode) {
  18231. self.append(opts.extNode);
  18232. }
  18233. if (opts.button) {
  18234. self.append(
  18235. $('<button class="ui-button ui-widget ui-state-default ui-corner-all elfinder-tabstop"/>')
  18236. .append($('<span class="ui-button-text"/>').text(fm.i18n(opts.button.text)))
  18237. .on('mouseenter mouseleave', function(e) {
  18238. $(this).toggleClass('ui-state-hover', e.type == 'mouseenter');
  18239. })
  18240. .on('click', opts.button.click || function(){})
  18241. );
  18242. }
  18243. if (opts.width) {
  18244. self.css('max-width', opts.width);
  18245. }
  18246. show();
  18247. });
  18248. };
  18249. /*
  18250. * File: /js/ui/toolbar.js
  18251. */
  18252. /**
  18253. * @class elFinder toolbar
  18254. *
  18255. * @author Dmitry (dio) Levashov
  18256. **/
  18257. $.fn.elfindertoolbar = function(fm, opts) {
  18258. this.not('.elfinder-toolbar').each(function() {
  18259. var commands = fm._commands,
  18260. self = $(this).addClass('ui-helper-clearfix ui-widget-header elfinder-toolbar'),
  18261. options = {
  18262. // default options
  18263. displayTextLabel: false,
  18264. labelExcludeUA: ['Mobile'],
  18265. autoHideUA: ['Mobile'],
  18266. showPreferenceButton: 'none'
  18267. },
  18268. filter = function(opts) {
  18269. return $.grep(opts, function(v) {
  18270. if ($.isPlainObject(v)) {
  18271. options = Object.assign(options, v);
  18272. return false;
  18273. }
  18274. return true;
  18275. });
  18276. },
  18277. render = function(disabled){
  18278. var name,cmdPref;
  18279. $.each(buttons, function(i, b) { b.detach(); });
  18280. self.empty();
  18281. l = panels.length;
  18282. while (l--) {
  18283. if (panels[l]) {
  18284. panel = $('<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>');
  18285. i = panels[l].length;
  18286. while (i--) {
  18287. name = panels[l][i];
  18288. if ((!disabled || !disabled[name]) && (cmd = commands[name])) {
  18289. button = 'elfinder'+cmd.options.ui;
  18290. if (! buttons[name] && $.fn[button]) {
  18291. buttons[name] = $('<div/>')[button](cmd);
  18292. }
  18293. if (buttons[name]) {
  18294. buttons[name].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  18295. panel.prepend(buttons[name]);
  18296. }
  18297. }
  18298. }
  18299. panel.children().length && self.prepend(panel);
  18300. panel.children(':gt(0)').before('<span class="ui-widget-content elfinder-toolbar-button-separator"/>');
  18301. }
  18302. }
  18303. if (cmdPref = commands['preference']) {
  18304. //cmdPref.state = !self.children().length? 0 : -1;
  18305. if (options.showPreferenceButton === 'always' || (!self.children().length && options.showPreferenceButton === 'auto')) {
  18306. //cmdPref.state = 0;
  18307. panel = $('<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>');
  18308. name = 'preference';
  18309. button = 'elfinder'+cmd.options.ui;
  18310. buttons[name] = $('<div/>')[button](cmdPref);
  18311. buttons[name].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  18312. panel.prepend(buttons[name]);
  18313. self.append(panel);
  18314. }
  18315. }
  18316. (! self.data('swipeClose') && self.children().length)? self.show() : self.hide();
  18317. prevHeight = self[0].clientHeight;
  18318. fm.trigger('toolbarload').trigger('uiresize');
  18319. },
  18320. buttons = {},
  18321. panels = filter(opts || []),
  18322. dispre = null,
  18323. uiCmdMapPrev = '',
  18324. prevHeight = 0,
  18325. contextRaw = [],
  18326. l, i, cmd, panel, button, swipeHandle, autoHide, textLabel, resizeTm;
  18327. // normalize options
  18328. options.showPreferenceButton = options.showPreferenceButton.toLowerCase();
  18329. if (options.displayTextLabel !== 'none') {
  18330. // correction of options.displayTextLabel
  18331. textLabel = fm.storage('toolbarTextLabel');
  18332. if (textLabel === null) {
  18333. textLabel = (options.displayTextLabel && (! options.labelExcludeUA || ! options.labelExcludeUA.length || ! $.grep(options.labelExcludeUA, function(v){ return fm.UA[v]? true : false; }).length));
  18334. } else {
  18335. textLabel = (textLabel == 1);
  18336. }
  18337. contextRaw.push({
  18338. label : fm.i18n('textLabel'),
  18339. icon : 'text',
  18340. callback : function() {
  18341. textLabel = ! textLabel;
  18342. self.css('height', '').find('.elfinder-button-text')[textLabel? 'show':'hide']();
  18343. fm.trigger('uiresize').storage('toolbarTextLabel', textLabel? '1' : '0');
  18344. },
  18345. });
  18346. }
  18347. if (options.preferenceInContextmenu && commands['preference']) {
  18348. contextRaw.push({
  18349. label : fm.i18n('toolbarPref'),
  18350. icon : 'preference',
  18351. callback : function() {
  18352. fm.exec('preference', void(0), {tab: 'toolbar'});
  18353. }
  18354. });
  18355. }
  18356. // add contextmenu
  18357. if (contextRaw.length) {
  18358. self.on('contextmenu', function(e) {
  18359. e.stopPropagation();
  18360. e.preventDefault();
  18361. fm.trigger('contextmenu', {
  18362. raw: contextRaw,
  18363. x: e.pageX,
  18364. y: e.pageY
  18365. });
  18366. }).on('touchstart', function(e) {
  18367. if (e.originalEvent.touches.length > 1) {
  18368. return;
  18369. }
  18370. self.data('tmlongtap') && clearTimeout(self.data('tmlongtap'));
  18371. self.removeData('longtap')
  18372. .data('longtap', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY})
  18373. .data('tmlongtap', setTimeout(function() {
  18374. self.removeData('longtapTm')
  18375. .trigger({
  18376. type: 'contextmenu',
  18377. pageX: self.data('longtap').x,
  18378. pageY: self.data('longtap').y
  18379. })
  18380. .data('longtap', {longtap: true});
  18381. }, 500));
  18382. }).on('touchmove touchend', function(e) {
  18383. if (self.data('tmlongtap')) {
  18384. if (e.type === 'touchend' ||
  18385. ( Math.abs(self.data('longtap').x - e.originalEvent.touches[0].pageX)
  18386. + Math.abs(self.data('longtap').y - e.originalEvent.touches[0].pageY)) > 4)
  18387. clearTimeout(self.data('tmlongtap'));
  18388. self.removeData('longtapTm');
  18389. }
  18390. }).on('click', function(e) {
  18391. if (self.data('longtap') && self.data('longtap').longtap) {
  18392. e.stopImmediatePropagation();
  18393. e.preventDefault();
  18394. }
  18395. }).on('touchend click', '.elfinder-button', function(e) {
  18396. if (self.data('longtap') && self.data('longtap').longtap) {
  18397. e.stopImmediatePropagation();
  18398. e.preventDefault();
  18399. }
  18400. }
  18401. );
  18402. }
  18403. self.prev().length && self.parent().prepend(this);
  18404. render();
  18405. fm.bind('open sync select toolbarpref', function() {
  18406. var disabled = Object.assign({}, fm.option('disabledFlip')),
  18407. userHides = fm.storage('toolbarhides'),
  18408. doRender, sel, disabledKeys;
  18409. if (! userHides && Array.isArray(options.defaultHides)) {
  18410. userHides = {};
  18411. $.each(options.defaultHides, function() {
  18412. userHides[this] = true;
  18413. });
  18414. fm.storage('toolbarhides', userHides);
  18415. }
  18416. if (this.type === 'select') {
  18417. if (fm.searchStatus.state < 2) {
  18418. return;
  18419. }
  18420. sel = fm.selected();
  18421. if (sel.length) {
  18422. disabled = fm.getDisabledCmds(sel, true);
  18423. }
  18424. }
  18425. $.each(userHides, function(n) {
  18426. if (!disabled[n]) {
  18427. disabled[n] = true;
  18428. }
  18429. });
  18430. if (Object.keys(fm.commandMap).length) {
  18431. $.each(fm.commandMap, function(from, to){
  18432. if (to === 'hidden') {
  18433. disabled[from] = true;
  18434. }
  18435. });
  18436. }
  18437. disabledKeys = Object.keys(disabled);
  18438. if (!dispre || dispre.toString() !== disabledKeys.sort().toString()) {
  18439. render(disabledKeys.length? disabled : null);
  18440. doRender = true;
  18441. }
  18442. dispre = disabledKeys.sort();
  18443. if (doRender || uiCmdMapPrev !== JSON.stringify(fm.commandMap)) {
  18444. uiCmdMapPrev = JSON.stringify(fm.commandMap);
  18445. if (! doRender) {
  18446. // reset toolbar
  18447. $.each($('div.elfinder-button'), function(){
  18448. var origin = $(this).data('origin');
  18449. if (origin) {
  18450. $(this).after(origin).detach();
  18451. }
  18452. });
  18453. }
  18454. if (Object.keys(fm.commandMap).length) {
  18455. $.each(fm.commandMap, function(from, to){
  18456. var cmd = fm._commands[to],
  18457. button = cmd? 'elfinder'+cmd.options.ui : null,
  18458. btn;
  18459. if (button && $.fn[button]) {
  18460. btn = buttons[from];
  18461. if (btn) {
  18462. if (! buttons[to] && $.fn[button]) {
  18463. buttons[to] = $('<div/>')[button](cmd);
  18464. if (buttons[to]) {
  18465. buttons[to].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  18466. if (cmd.extendsCmd) {
  18467. buttons[to].children('span.elfinder-button-icon').addClass('elfinder-button-icon-' + cmd.extendsCmd);
  18468. }
  18469. }
  18470. }
  18471. if (buttons[to]) {
  18472. btn.after(buttons[to]);
  18473. buttons[to].data('origin', btn.detach());
  18474. }
  18475. }
  18476. }
  18477. });
  18478. }
  18479. }
  18480. }).bind('resize', function(e) {
  18481. resizeTm && cancelAnimationFrame(resizeTm);
  18482. resizeTm = requestAnimationFrame(function() {
  18483. var h = self[0].clientHeight;
  18484. if (prevHeight !== h) {
  18485. prevHeight = h;
  18486. fm.trigger('uiresize');
  18487. }
  18488. });
  18489. });
  18490. if (fm.UA.Touch) {
  18491. autoHide = fm.storage('autoHide') || {};
  18492. if (typeof autoHide.toolbar === 'undefined') {
  18493. autoHide.toolbar = (options.autoHideUA && options.autoHideUA.length > 0 && $.grep(options.autoHideUA, function(v){ return fm.UA[v]? true : false; }).length);
  18494. fm.storage('autoHide', autoHide);
  18495. }
  18496. if (autoHide.toolbar) {
  18497. fm.one('init', function() {
  18498. fm.uiAutoHide.push(function(){ self.stop(true, true).trigger('toggle', { duration: 500, init: true }); });
  18499. });
  18500. }
  18501. fm.bind('load', function() {
  18502. swipeHandle = $('<div class="elfinder-toolbar-swipe-handle"/>').hide().appendTo(fm.getUI());
  18503. if (swipeHandle.css('pointer-events') !== 'none') {
  18504. swipeHandle.remove();
  18505. swipeHandle = null;
  18506. }
  18507. });
  18508. self.on('toggle', function(e, data) {
  18509. var wz = fm.getUI('workzone'),
  18510. toshow= self.is(':hidden'),
  18511. wzh = wz.height(),
  18512. h = self.height(),
  18513. tbh = self.outerHeight(true),
  18514. delta = tbh - h,
  18515. opt = Object.assign({
  18516. step: function(now) {
  18517. wz.height(wzh + (toshow? (now + delta) * -1 : h - now));
  18518. fm.trigger('resize');
  18519. },
  18520. always: function() {
  18521. requestAnimationFrame(function() {
  18522. self.css('height', '');
  18523. fm.trigger('uiresize');
  18524. if (swipeHandle) {
  18525. if (toshow) {
  18526. swipeHandle.stop(true, true).hide();
  18527. } else {
  18528. swipeHandle.height(data.handleH? data.handleH : '');
  18529. fm.resources.blink(swipeHandle, 'slowonce');
  18530. }
  18531. }
  18532. toshow && self.scrollTop('0px');
  18533. data.init && fm.trigger('uiautohide');
  18534. });
  18535. }
  18536. }, data);
  18537. self.data('swipeClose', ! toshow).stop(true, true).animate({height : 'toggle'}, opt);
  18538. autoHide.toolbar = !toshow;
  18539. fm.storage('autoHide', Object.assign(fm.storage('autoHide'), {toolbar: autoHide.toolbar}));
  18540. }).on('touchstart', function(e) {
  18541. if (self.scrollBottom() > 5) {
  18542. e.originalEvent._preventSwipeY = true;
  18543. }
  18544. });
  18545. }
  18546. });
  18547. return this;
  18548. };
  18549. /*
  18550. * File: /js/ui/tree.js
  18551. */
  18552. /**
  18553. * @class elFinder folders tree
  18554. *
  18555. * @author Dmitry (dio) Levashov
  18556. **/
  18557. $.fn.elfindertree = function(fm, opts) {
  18558. var treeclass = fm.res('class', 'tree');
  18559. this.not('.'+treeclass).each(function() {
  18560. var c = 'class', mobile = fm.UA.Mobile,
  18561. /**
  18562. * Root directory class name
  18563. *
  18564. * @type String
  18565. */
  18566. root = fm.res(c, 'treeroot'),
  18567. /**
  18568. * Open root dir if not opened yet
  18569. *
  18570. * @type Boolean
  18571. */
  18572. openRoot = opts.openRootOnLoad,
  18573. /**
  18574. * Open current work dir if not opened yet
  18575. *
  18576. * @type Boolean
  18577. */
  18578. openCwd = opts.openCwdOnOpen,
  18579. /**
  18580. * Auto loading current directory parents and do expand their node
  18581. *
  18582. * @type Boolean
  18583. */
  18584. syncTree = openCwd || opts.syncTree,
  18585. /**
  18586. * Subtree class name
  18587. *
  18588. * @type String
  18589. */
  18590. subtree = fm.res(c, 'navsubtree'),
  18591. /**
  18592. * Directory class name
  18593. *
  18594. * @type String
  18595. */
  18596. navdir = fm.res(c, 'treedir'),
  18597. /**
  18598. * Directory CSS selector
  18599. *
  18600. * @type String
  18601. */
  18602. selNavdir = 'span.' + navdir,
  18603. /**
  18604. * Collapsed arrow class name
  18605. *
  18606. * @type String
  18607. */
  18608. collapsed = fm.res(c, 'navcollapse'),
  18609. /**
  18610. * Expanded arrow class name
  18611. *
  18612. * @type String
  18613. */
  18614. expanded = fm.res(c, 'navexpand'),
  18615. /**
  18616. * Class name to mark arrow for directory with already loaded children
  18617. *
  18618. * @type String
  18619. */
  18620. loaded = 'elfinder-subtree-loaded',
  18621. /**
  18622. * Class name to mark need subdirs request
  18623. *
  18624. * @type String
  18625. */
  18626. chksubdir = 'elfinder-subtree-chksubdir',
  18627. /**
  18628. * Arraw class name
  18629. *
  18630. * @type String
  18631. */
  18632. arrow = fm.res(c, 'navarrow'),
  18633. /**
  18634. * Current directory class name
  18635. *
  18636. * @type String
  18637. */
  18638. active = fm.res(c, 'active'),
  18639. /**
  18640. * Droppable dirs dropover class
  18641. *
  18642. * @type String
  18643. */
  18644. dropover = fm.res(c, 'adroppable'),
  18645. /**
  18646. * Hover class name
  18647. *
  18648. * @type String
  18649. */
  18650. hover = fm.res(c, 'hover'),
  18651. /**
  18652. * Disabled dir class name
  18653. *
  18654. * @type String
  18655. */
  18656. disabled = fm.res(c, 'disabled'),
  18657. /**
  18658. * Draggable dir class name
  18659. *
  18660. * @type String
  18661. */
  18662. draggable = fm.res(c, 'draggable'),
  18663. /**
  18664. * Droppable dir class name
  18665. *
  18666. * @type String
  18667. */
  18668. droppable = fm.res(c, 'droppable'),
  18669. /**
  18670. * root wrapper class
  18671. *
  18672. * @type String
  18673. */
  18674. wrapperRoot = 'elfinder-navbar-wrapper-root',
  18675. /**
  18676. * Un-disabled cmd `paste` volume's root wrapper class
  18677. *
  18678. * @type String
  18679. */
  18680. pastable = 'elfinder-navbar-wrapper-pastable',
  18681. /**
  18682. * Un-disabled cmd `upload` volume's root wrapper class
  18683. *
  18684. * @type String
  18685. */
  18686. uploadable = 'elfinder-navbar-wrapper-uploadable',
  18687. /**
  18688. * Is position x inside Navbar
  18689. *
  18690. * @param x Numbar
  18691. *
  18692. * @return
  18693. */
  18694. insideNavbar = function(x) {
  18695. var left = navbar.offset().left;
  18696. return left <= x && x <= left + navbar.width();
  18697. },
  18698. /**
  18699. * To call subdirs elements queue
  18700. *
  18701. * @type Object
  18702. */
  18703. subdirsQue = {},
  18704. /**
  18705. * To exec subdirs elements ids
  18706. *
  18707. */
  18708. subdirsExecQue = [],
  18709. /**
  18710. * Request subdirs to backend
  18711. *
  18712. * @param id String
  18713. *
  18714. * @return Deferred
  18715. */
  18716. subdirs = function(ids) {
  18717. var targets = [];
  18718. $.each(ids, function(i, id) {
  18719. subdirsQue[id] && targets.push(fm.navId2Hash(id));
  18720. delete subdirsQue[id];
  18721. });
  18722. if (targets.length) {
  18723. return fm.request({
  18724. data: {
  18725. cmd: 'subdirs',
  18726. targets: targets,
  18727. preventDefault : true
  18728. }
  18729. }).done(function(res) {
  18730. if (res && res.subdirs) {
  18731. $.each(res.subdirs, function(hash, subdirs) {
  18732. var elm = $('#'+fm.navHash2Id(hash));
  18733. elm.removeClass(chksubdir);
  18734. elm[subdirs? 'addClass' : 'removeClass'](collapsed);
  18735. });
  18736. }
  18737. });
  18738. }
  18739. },
  18740. subdirsJobRes = null,
  18741. /**
  18742. * To check target element is in window of subdirs
  18743. *
  18744. * @return void
  18745. */
  18746. checkSubdirs = function() {
  18747. var ids = Object.keys(subdirsQue);
  18748. if (ids.length) {
  18749. subdirsJobRes && subdirsJobRes._abort();
  18750. execSubdirsTm && clearTimeout(execSubdirsTm);
  18751. subdirsExecQue = [];
  18752. subdirsJobRes = fm.asyncJob(function(id) {
  18753. return fm.isInWindow($('#'+id))? id : null;
  18754. }, ids, { numPerOnce: 200 })
  18755. .done(function(arr) {
  18756. if (arr.length) {
  18757. subdirsExecQue = arr;
  18758. execSubdirs();
  18759. }
  18760. });
  18761. }
  18762. },
  18763. subdirsPending = 0,
  18764. execSubdirsTm,
  18765. /**
  18766. * Exec subdirs as batch request
  18767. *
  18768. * @return void
  18769. */
  18770. execSubdirs = function() {
  18771. var cnt = opts.subdirsMaxConn - subdirsPending,
  18772. atOnce = fm.maxTargets? Math.min(fm.maxTargets, opts.subdirsAtOnce) : opts.subdirsAtOnce,
  18773. i, ids;
  18774. execSubdirsTm && cancelAnimationFrame(execSubdirsTm);
  18775. if (subdirsExecQue.length) {
  18776. if (cnt > 0) {
  18777. for (i = 0; i < cnt; i++) {
  18778. if (subdirsExecQue.length) {
  18779. subdirsPending++;
  18780. subdirs(subdirsExecQue.splice(0, atOnce)).always(function() {
  18781. subdirsPending--;
  18782. execSubdirs();
  18783. });
  18784. }
  18785. }
  18786. } else {
  18787. execSubdirsTm = requestAnimationFrame(function() {
  18788. subdirsExecQue.length && execSubdirs();
  18789. });
  18790. }
  18791. }
  18792. },
  18793. drop = fm.droppable.drop,
  18794. /**
  18795. * Droppable options
  18796. *
  18797. * @type Object
  18798. */
  18799. droppableopts = $.extend(true, {}, fm.droppable, {
  18800. // show subfolders on dropover
  18801. over : function(e, ui) {
  18802. var dst = $(this),
  18803. helper = ui.helper,
  18804. cl = hover+' '+dropover,
  18805. hash, status;
  18806. e.stopPropagation();
  18807. helper.data('dropover', helper.data('dropover') + 1);
  18808. dst.data('dropover', true);
  18809. if (ui.helper.data('namespace') !== fm.namespace || ! insideNavbar(e.clientX) || ! fm.insideWorkzone(e.pageX, e.pageY)) {
  18810. dst.removeClass(cl);
  18811. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  18812. return;
  18813. }
  18814. dst.addClass(hover);
  18815. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  18816. dst.data('expandTimer', setTimeout(function() {
  18817. dst.is('.'+collapsed+'.'+hover) && dst.children('.'+arrow).trigger('click');
  18818. }, 500));
  18819. }
  18820. if (dst.is('.elfinder-ro,.elfinder-na')) {
  18821. dst.removeClass(dropover);
  18822. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  18823. return;
  18824. }
  18825. hash = fm.navId2Hash(dst.attr('id'));
  18826. dst.data('dropover', hash);
  18827. $.each(ui.helper.data('files'), function(i, h) {
  18828. if (h === hash || (fm.file(h).phash === hash && !ui.helper.hasClass('elfinder-drag-helper-plus'))) {
  18829. dst.removeClass(cl);
  18830. return false; // break $.each
  18831. }
  18832. });
  18833. if (helper.data('locked')) {
  18834. status = 'elfinder-drag-helper-plus';
  18835. } else {
  18836. status = 'elfinder-drag-helper-move';
  18837. if (e.shiftKey || e.ctrlKey || e.metaKey) {
  18838. status += ' elfinder-drag-helper-plus';
  18839. }
  18840. }
  18841. dst.hasClass(dropover) && helper.addClass(status);
  18842. requestAnimationFrame(function(){ dst.hasClass(dropover) && helper.addClass(status); });
  18843. },
  18844. out : function(e, ui) {
  18845. var dst = $(this),
  18846. helper = ui.helper;
  18847. e.stopPropagation();
  18848. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  18849. dst.data('expandTimer') && clearTimeout(dst.data('expandTimer'));
  18850. dst.removeData('dropover')
  18851. .removeClass(hover+' '+dropover);
  18852. },
  18853. deactivate : function() {
  18854. $(this).removeData('dropover')
  18855. .removeClass(hover+' '+dropover);
  18856. },
  18857. drop : function(e, ui) {
  18858. insideNavbar(e.clientX) && drop.call(this, e, ui);
  18859. }
  18860. }),
  18861. spinner = $(fm.res('tpl', 'navspinner')),
  18862. /**
  18863. * Directory html template
  18864. *
  18865. * @type String
  18866. */
  18867. tpl = fm.res('tpl', 'navdir'),
  18868. /**
  18869. * Permissions marker html template
  18870. *
  18871. * @type String
  18872. */
  18873. ptpl = fm.res('tpl', 'perms'),
  18874. /**
  18875. * Lock marker html template
  18876. *
  18877. * @type String
  18878. */
  18879. ltpl = fm.res('tpl', 'lock'),
  18880. /**
  18881. * Symlink marker html template
  18882. *
  18883. * @type String
  18884. */
  18885. stpl = fm.res('tpl', 'symlink'),
  18886. /**
  18887. * Directory hashes that has more pages
  18888. *
  18889. * @type Object
  18890. */
  18891. hasMoreDirs = {},
  18892. /**
  18893. * Html template replacement methods
  18894. *
  18895. * @type Object
  18896. */
  18897. replace = {
  18898. id : function(dir) { return fm.navHash2Id(dir.hash); },
  18899. name : function(dir) { return fm.escape(dir.i18 || dir.name); },
  18900. cssclass : function(dir) {
  18901. var cname = (dir.phash && ! dir.isroot ? '' : root)+' '+navdir+' '+fm.perms2class(dir);
  18902. dir.dirs && !dir.link && (cname += ' ' + collapsed) && dir.dirs == -1 && (cname += ' ' + chksubdir);
  18903. opts.getClass && (cname += ' ' + opts.getClass(dir));
  18904. dir.csscls && (cname += ' ' + fm.escape(dir.csscls));
  18905. return cname;
  18906. },
  18907. root : function(dir) {
  18908. var cls = '';
  18909. if (!dir.phash || dir.isroot) {
  18910. cls += ' '+wrapperRoot;
  18911. if (!dir.disabled || dir.disabled.length < 1) {
  18912. cls += ' '+pastable+' '+uploadable;
  18913. } else {
  18914. if ($.inArray('paste', dir.disabled) === -1) {
  18915. cls += ' '+pastable;
  18916. }
  18917. if ($.inArray('upload', dir.disabled) === -1) {
  18918. cls += ' '+uploadable;
  18919. }
  18920. }
  18921. return cls;
  18922. } else {
  18923. return '';
  18924. }
  18925. },
  18926. permissions : function(dir) { return !dir.read || !dir.write ? ptpl : ''; },
  18927. symlink : function(dir) { return dir.alias ? stpl : ''; },
  18928. style : function(dir) { return dir.icon ? fm.getIconStyle(dir) : ''; }
  18929. },
  18930. /**
  18931. * Return html for given dir
  18932. *
  18933. * @param Object directory
  18934. * @return String
  18935. */
  18936. itemhtml = function(dir) {
  18937. return tpl.replace(/(?:\{([a-z]+)\})/ig, function(m, key) {
  18938. var res = replace[key] ? replace[key](dir) : (dir[key] || '');
  18939. if (key === 'id' && dir.dirs == -1) {
  18940. subdirsQue[res] = res;
  18941. }
  18942. return res;
  18943. });
  18944. },
  18945. /**
  18946. * Return only dirs from files list
  18947. *
  18948. * @param Array files list
  18949. * @param Boolean do check exists
  18950. * @return Array
  18951. */
  18952. filter = function(files, checkExists) {
  18953. return $.map(files || [], function(f) {
  18954. return (f.mime === 'directory' && (!checkExists || $('#'+fm.navHash2Id(f.hash)).length)) ? f : null;
  18955. });
  18956. },
  18957. /**
  18958. * Find parent subtree for required directory
  18959. *
  18960. * @param String dir hash
  18961. * @return jQuery
  18962. */
  18963. findSubtree = function(hash) {
  18964. return hash ? $('#'+fm.navHash2Id(hash)).next('.'+subtree) : tree;
  18965. },
  18966. /**
  18967. * Find directory (wrapper) in required node
  18968. * before which we can insert new directory
  18969. *
  18970. * @param jQuery parent directory
  18971. * @param Object new directory
  18972. * @return jQuery
  18973. */
  18974. findSibling = function(subtree, dir) {
  18975. var node = subtree.children(':first'),
  18976. info;
  18977. while (node.length) {
  18978. info = fm.file(fm.navId2Hash(node.children('[id]').attr('id')));
  18979. if ((info = fm.file(fm.navId2Hash(node.children('[id]').attr('id'))))
  18980. && compare(dir, info) < 0) {
  18981. return node;
  18982. }
  18983. node = node.next();
  18984. }
  18985. return subtree.children('button.elfinder-navbar-pager-next');
  18986. },
  18987. /**
  18988. * Add new dirs in tree
  18989. *
  18990. * @param Array dirs list
  18991. * @return void
  18992. */
  18993. updateTree = function(dirs) {
  18994. var length = dirs.length,
  18995. orphans = [],
  18996. i = length,
  18997. tgts = $(),
  18998. done = {},
  18999. cwd = fm.cwd(),
  19000. append = function(parent, dirs, start, direction) {
  19001. var hashes = {},
  19002. curStart = 0,
  19003. max = fm.newAPI? Math.min(10000, Math.max(10, opts.subTreeMax)) : 10000,
  19004. setHashes = function() {
  19005. hashes = {};
  19006. $.each(dirs, function(i, d) {
  19007. hashes[d.hash] = i;
  19008. });
  19009. },
  19010. change = function(mode) {
  19011. if (mode === 'prepare') {
  19012. $.each(dirs, function(i, d) {
  19013. d.node && parent.append(d.node.hide());
  19014. });
  19015. } else if (mode === 'done') {
  19016. $.each(dirs, function(i, d) {
  19017. d.node && d.node.detach().show();
  19018. });
  19019. }
  19020. },
  19021. update = function(e, data) {
  19022. var i, changed;
  19023. e.stopPropagation();
  19024. if (data.select) {
  19025. render(getStart(data.select));
  19026. return;
  19027. }
  19028. if (data.change) {
  19029. change(data.change);
  19030. return;
  19031. }
  19032. if (data.removed && data.removed.length) {
  19033. dirs = $.grep(dirs, function(d) {
  19034. if (data.removed.indexOf(d.hash) === -1) {
  19035. return true;
  19036. } else {
  19037. !changed && (changed = true);
  19038. return false;
  19039. }
  19040. });
  19041. }
  19042. if (data.added && data.added.length) {
  19043. dirs = dirs.concat($.grep(data.added, function(d) {
  19044. if (hashes[d.hash] === void(0)) {
  19045. !changed && (changed = true);
  19046. return true;
  19047. } else {
  19048. return false;
  19049. }
  19050. }));
  19051. }
  19052. if (changed) {
  19053. dirs.sort(compare);
  19054. setHashes();
  19055. render(curStart);
  19056. }
  19057. },
  19058. getStart = function(target) {
  19059. if (hashes[target] !== void(0)) {
  19060. return Math.floor(hashes[target] / max) * max;
  19061. }
  19062. return void(0);
  19063. },
  19064. target = fm.navId2Hash(parent.prev('[id]').attr('id')),
  19065. render = function(start, direction) {
  19066. var html = [],
  19067. nodes = {},
  19068. total, page, s, parts, prev, next, prevBtn, nextBtn;
  19069. delete hasMoreDirs[target];
  19070. curStart = start;
  19071. parent.off('update.'+fm.namespace, update);
  19072. if (dirs.length > max) {
  19073. parent.on('update.'+fm.namespace, update);
  19074. if (start === void(0)) {
  19075. s = 0;
  19076. setHashes();
  19077. start = getStart(cwd.hash);
  19078. if (start === void(0)) {
  19079. start = 0;
  19080. }
  19081. }
  19082. parts = dirs.slice(start, start + max);
  19083. hasMoreDirs[target] = parent;
  19084. prev = start? Math.max(-1, start - max) : -1;
  19085. next = (start + max >= dirs.length)? 0 : start + max;
  19086. total = Math.ceil(dirs.length/max);
  19087. page = Math.ceil(start/max);
  19088. }
  19089. $.each(parts || dirs, function(i, d) {
  19090. html.push(itemhtml(d));
  19091. if (d.node) {
  19092. nodes[d.hash] = d.node;
  19093. }
  19094. });
  19095. if (prev > -1) {
  19096. prevBtn = $('<button class="elfinder-navbar-pager elfinder-navbar-pager-prev"/>')
  19097. .text(fm.i18n('btnPrevious', page, total))
  19098. .button({
  19099. icons: {
  19100. primary: "ui-icon-caret-1-n"
  19101. }
  19102. })
  19103. .on('click', function(e) {
  19104. e.preventDefault();
  19105. e.stopPropagation();
  19106. render(prev, 'up');
  19107. });
  19108. } else {
  19109. prevBtn = $();
  19110. }
  19111. if (next) {
  19112. nextBtn = $('<button class="elfinder-navbar-pager elfinder-navbar-pager-next"/>')
  19113. .text(fm.i18n('btnNext', page + 2, total))
  19114. .button({
  19115. icons: {
  19116. primary: "ui-icon-caret-1-s"
  19117. }
  19118. })
  19119. .on('click', function(e) {
  19120. e.preventDefault();
  19121. e.stopPropagation();
  19122. render(next, 'down');
  19123. });
  19124. } else {
  19125. nextBtn = $();
  19126. }
  19127. detach();
  19128. parent.empty()[parts? 'addClass' : 'removeClass']('elfinder-navbar-hasmore').append(prevBtn, html.join(''), nextBtn);
  19129. $.each(nodes, function(h, n) {
  19130. $('#'+fm.navHash2Id(h)).parent().replaceWith(n);
  19131. });
  19132. if (direction) {
  19133. autoScroll(fm.navHash2Id(parts[direction === 'up'? parts.length - 1 : 0].hash));
  19134. }
  19135. ! mobile && fm.lazy(function() { updateDroppable(null, parent); });
  19136. },
  19137. detach = function() {
  19138. $.each(parent.children('.elfinder-navbar-wrapper'), function(i, elm) {
  19139. var n = $(elm),
  19140. ch = n.children('[id]:first'),
  19141. h, c;
  19142. if (ch.hasClass(loaded)) {
  19143. h = fm.navId2Hash(ch.attr('id'));
  19144. if (h && (c = hashes[h]) !== void(0)) {
  19145. dirs[c].node = n.detach();
  19146. }
  19147. }
  19148. });
  19149. };
  19150. render();
  19151. },
  19152. dir, html, parent, sibling, init, atonce = {}, updates = [], base, node,
  19153. firstVol = true; // check for netmount volume
  19154. while (i--) {
  19155. dir = dirs[i];
  19156. if (done[dir.hash] || $('#'+fm.navHash2Id(dir.hash)).length) {
  19157. continue;
  19158. }
  19159. done[dir.hash] = true;
  19160. if ((parent = findSubtree(dir.phash)).length) {
  19161. if (dir.phash && ((init = !parent.children().length) || parent.hasClass('elfinder-navbar-hasmore') || (sibling = findSibling(parent, dir)).length)) {
  19162. if (init) {
  19163. if (!atonce[dir.phash]) {
  19164. atonce[dir.phash] = [];
  19165. }
  19166. atonce[dir.phash].push(dir);
  19167. } else {
  19168. if (sibling) {
  19169. node = itemhtml(dir);
  19170. sibling.before(node);
  19171. ! mobile && (tgts = tgts.add(node));
  19172. } else {
  19173. updates.push(dir);
  19174. }
  19175. }
  19176. } else {
  19177. node = itemhtml(dir);
  19178. parent[firstVol || dir.phash ? 'append' : 'prepend'](node);
  19179. firstVol = false;
  19180. if (!dir.phash || dir.isroot) {
  19181. base = $('#'+fm.navHash2Id(dir.hash)).parent();
  19182. }
  19183. ! mobile && updateDroppable(null, base);
  19184. }
  19185. } else {
  19186. orphans.push(dir);
  19187. }
  19188. }
  19189. // When init, html append at once
  19190. if (Object.keys(atonce).length){
  19191. $.each(atonce, function(p, dirs){
  19192. var parent = findSubtree(p),
  19193. html = [];
  19194. dirs.sort(compare);
  19195. append(parent, dirs);
  19196. });
  19197. }
  19198. if (updates.length) {
  19199. parent.trigger('update.' + fm.namespace, { added : updates });
  19200. }
  19201. if (orphans.length && orphans.length < length) {
  19202. updateTree(orphans);
  19203. return;
  19204. }
  19205. ! mobile && tgts.length && fm.lazy(function() { updateDroppable(tgts); });
  19206. },
  19207. /**
  19208. * sort function by dir.name
  19209. *
  19210. */
  19211. compare = function(dir1, dir2) {
  19212. if (! fm.sortAlsoTreeview) {
  19213. return fm.sortRules.name(dir1, dir2);
  19214. } else {
  19215. var asc = fm.sortOrder == 'asc',
  19216. type = fm.sortType,
  19217. rules = fm.sortRules,
  19218. res;
  19219. res = asc? rules[fm.sortType](dir1, dir2) : rules[fm.sortType](dir2, dir1);
  19220. return type !== 'name' && res === 0
  19221. ? res = asc ? rules.name(dir1, dir2) : rules.name(dir2, dir1)
  19222. : res;
  19223. }
  19224. },
  19225. /**
  19226. * Timer ID of autoScroll
  19227. *
  19228. * @type Integer
  19229. */
  19230. autoScrTm,
  19231. /**
  19232. * Auto scroll to cwd
  19233. *
  19234. * @return Object jQuery Deferred
  19235. */
  19236. autoScroll = function(target) {
  19237. var dfrd = $.Deferred(),
  19238. current, parent, top, treeH, bottom, tgtTop;
  19239. autoScrTm && clearTimeout(autoScrTm);
  19240. autoScrTm = setTimeout(function() {
  19241. current = $('#'+(target || fm.navHash2Id(fm.cwd().hash)));
  19242. if (current.length) {
  19243. // expand parents directory
  19244. (openCwd? current : current.parent()).parents('.elfinder-navbar-wrapper').children('.'+loaded).addClass(expanded).next('.'+subtree).show();
  19245. parent = tree.parent().stop(false, true);
  19246. top = parent.offset().top;
  19247. treeH = parent.height();
  19248. bottom = top + treeH - current.outerHeight();
  19249. tgtTop = current.offset().top;
  19250. if (tgtTop < top || tgtTop > bottom) {
  19251. parent.animate({
  19252. scrollTop : parent.scrollTop() + tgtTop - top - treeH / 3
  19253. }, {
  19254. duration : opts.durations.autoScroll,
  19255. complete : function() { dfrd.resolve(); }
  19256. });
  19257. } else {
  19258. dfrd.resolve();
  19259. }
  19260. } else {
  19261. dfrd.reject();
  19262. }
  19263. }, 100);
  19264. return dfrd;
  19265. },
  19266. /**
  19267. * Get hashes array of items of the bottom of the leaf root back from the target
  19268. *
  19269. * @param Object elFinder item(directory) object
  19270. * @return Array hashes
  19271. */
  19272. getEnds = function(d) {
  19273. var cur = d || fm.cwd(),
  19274. res = cur.hash? [ cur.hash ] : [],
  19275. phash, root, dir;
  19276. root = fm.root(cur.hash);
  19277. dir = fm.file(root);
  19278. while (dir && (phash = dir.phash)) {
  19279. res.unshift(phash);
  19280. root = fm.root(phash);
  19281. dir = fm.file(root);
  19282. if ($('#'+fm.navHash2Id(dir.hash)).hasClass(loaded)) {
  19283. break;
  19284. }
  19285. }
  19286. return res;
  19287. },
  19288. /**
  19289. * Select pages back in order to display the target
  19290. *
  19291. * @param Object elFinder item(directory) object
  19292. * @return Object jQuery node object of target node
  19293. */
  19294. selectPages = function(current) {
  19295. var cur = current || fm.cwd(),
  19296. curHash = cur.hash,
  19297. node = $('#'+fm.navHash2Id(curHash));
  19298. if (!node.length) {
  19299. while(cur && cur.phash) {
  19300. if (hasMoreDirs[cur.phash] && !$('#'+fm.navHash2Id(cur.hash)).length) {
  19301. hasMoreDirs[cur.phash].trigger('update.'+fm.namespace, { select : cur.hash });
  19302. }
  19303. cur = fm.file(cur.phash);
  19304. }
  19305. node = $('#'+fm.navHash2Id(curHash));
  19306. }
  19307. return node;
  19308. },
  19309. /**
  19310. * Flag indicating that synchronization is currently in progress
  19311. *
  19312. * @type Boolean
  19313. */
  19314. syncing,
  19315. /**
  19316. * Mark current directory as active
  19317. * If current directory is not in tree - load it and its parents
  19318. *
  19319. * @param Array directory objects of cwd
  19320. * @param Boolean do auto scroll
  19321. * @return Object jQuery Deferred
  19322. */
  19323. sync = function(cwdDirs, aScr) {
  19324. var cwd = fm.cwd(),
  19325. cwdhash = cwd.hash,
  19326. autoScr = aScr === void(0)? syncTree : aScr,
  19327. loadParents = function(dir) {
  19328. var dfd = $.Deferred(),
  19329. reqs = [],
  19330. ends = getEnds(dir),
  19331. makeReq = function(cmd, h, until) {
  19332. var data = {
  19333. cmd : cmd,
  19334. target : h
  19335. };
  19336. if (until) {
  19337. data.until = until;
  19338. }
  19339. return fm.request({
  19340. data : data,
  19341. preventFail : true
  19342. });
  19343. },
  19344. baseHash, baseId;
  19345. reqs = $.map(ends, function(h) {
  19346. var d = fm.file(h),
  19347. isRoot = d? fm.isRoot(d) : false,
  19348. node = $('#'+fm.navHash2Id(h)),
  19349. getPhash = function(h, dep) {
  19350. var d, ph,
  19351. depth = dep || 1;
  19352. ph = (d = fm.file(h))? d.phash : false;
  19353. if (ph && depth > 1) {
  19354. return getPhash(ph, --depth);
  19355. }
  19356. return ph;
  19357. },
  19358. until,
  19359. closest = (function() {
  19360. var phash = getPhash(h);
  19361. until = phash;
  19362. while (phash) {
  19363. if ($('#'+fm.navHash2Id(phash)).hasClass(loaded)) {
  19364. break;
  19365. }
  19366. until = phash;
  19367. phash = getPhash(phash);
  19368. }
  19369. if (!phash) {
  19370. until = void(0);
  19371. phash = fm.root(h);
  19372. }
  19373. return phash;
  19374. })(),
  19375. cmd;
  19376. if (!node.hasClass(loaded) && (isRoot || !d || !$('#'+fm.navHash2Id(d.phash)).hasClass(loaded))) {
  19377. if (isRoot || closest === getPhash(h) || closest === getPhash(h, 2)) {
  19378. until = void(0);
  19379. cmd = 'tree';
  19380. if (!isRoot) {
  19381. h = getPhash(h);
  19382. }
  19383. } else {
  19384. cmd = 'parents';
  19385. }
  19386. if (!baseHash) {
  19387. baseHash = (cmd === 'tree')? h : closest;
  19388. }
  19389. return makeReq(cmd, h, until);
  19390. }
  19391. return null;
  19392. });
  19393. if (reqs.length) {
  19394. selectPages(fm.file(baseHash));
  19395. baseId = fm.navHash2Id(baseHash);
  19396. autoScr && autoScroll(baseId);
  19397. baseNode = $('#'+baseId);
  19398. spinner = $(fm.res('tpl', 'navspinner')).insertBefore(baseNode.children('.'+arrow));
  19399. baseNode.removeClass(collapsed);
  19400. $.when.apply($, reqs)
  19401. .done(function() {
  19402. var res = {},data, treeDirs, dirs, argLen, i;
  19403. argLen = arguments.length;
  19404. if (argLen > 0) {
  19405. for (i = 0; i < argLen; i++) {
  19406. data = arguments[i].tree || [];
  19407. res[ends[i]] = Object.assign([], filter(data));
  19408. }
  19409. }
  19410. dfd.resolve(res);
  19411. })
  19412. .fail(function() {
  19413. dfd.reject();
  19414. });
  19415. return dfd;
  19416. } else {
  19417. return dfd.resolve();
  19418. }
  19419. },
  19420. done= function(res, dfrd) {
  19421. var open = function() {
  19422. if (openRoot && baseNode) {
  19423. findSubtree(baseNode.hash).show().prev(selNavdir).addClass(expanded);
  19424. openRoot = false;
  19425. }
  19426. if (autoScr) {
  19427. autoScroll().done(checkSubdirs);
  19428. } else {
  19429. checkSubdirs();
  19430. }
  19431. },
  19432. current;
  19433. if (res) {
  19434. $.each(res, function(endHash, dirs) {
  19435. dirs && updateTree(dirs);
  19436. selectPages(fm.file(endHash));
  19437. dirs && updateArrows(dirs, loaded);
  19438. });
  19439. }
  19440. if (cwdDirs) {
  19441. (fm.api < 2.1) && cwdDirs.push(cwd);
  19442. updateTree(cwdDirs);
  19443. }
  19444. // set current node
  19445. current = selectPages();
  19446. if (!current.hasClass(active)) {
  19447. tree.find(selNavdir+'.'+active).removeClass(active);
  19448. current.addClass(active);
  19449. }
  19450. // mark as loaded to cwd parents
  19451. current.parents('.elfinder-navbar-wrapper').children('.'+navdir).addClass(loaded);
  19452. if (res) {
  19453. fm.lazy(open).done(function() {
  19454. dfrd.resolve();
  19455. });
  19456. } else {
  19457. open();
  19458. dfrd.resolve();
  19459. }
  19460. },
  19461. rmSpinner = function(fail) {
  19462. if (baseNode) {
  19463. spinner.remove();
  19464. baseNode.addClass(collapsed + (fail? '' : (' ' + loaded)));
  19465. }
  19466. },
  19467. dfrd = $.Deferred(),
  19468. baseNode, spinner;
  19469. if (!$('#'+fm.navHash2Id(cwdhash)).length) {
  19470. syncing = true;
  19471. loadParents()
  19472. .done(function(res) {
  19473. done(res, dfrd);
  19474. rmSpinner();
  19475. })
  19476. .fail(function() {
  19477. rmSpinner(true);
  19478. dfrd.reject();
  19479. })
  19480. .always(function() {
  19481. syncing = false;
  19482. });
  19483. } else {
  19484. done(void(0), dfrd);
  19485. }
  19486. // trigger 'treesync' with my $.Deferred
  19487. fm.trigger('treesync', dfrd);
  19488. return dfrd;
  19489. },
  19490. /**
  19491. * Make writable and not root dirs droppable
  19492. *
  19493. * @return void
  19494. */
  19495. updateDroppable = function(target, node) {
  19496. var limit = 100,
  19497. next;
  19498. if (!target) {
  19499. if (!node || node.closest('div.'+wrapperRoot).hasClass(uploadable)) {
  19500. (node || tree.find('div.'+uploadable)).find(selNavdir+':not(.elfinder-ro,.elfinder-na)').addClass('native-droppable');
  19501. }
  19502. if (!node || node.closest('div.'+wrapperRoot).hasClass(pastable)) {
  19503. target = (node || tree.find('div.'+pastable)).find(selNavdir+':not(.'+droppable+')');
  19504. } else {
  19505. target = $();
  19506. }
  19507. if (node) {
  19508. // check leaf roots
  19509. node.children('div.'+wrapperRoot).each(function() {
  19510. updateDroppable(null, $(this));
  19511. });
  19512. }
  19513. }
  19514. // make droppable on async
  19515. if (target.length) {
  19516. fm.asyncJob(function(elm) {
  19517. $(elm).droppable(droppableopts);
  19518. }, $.makeArray(target), {
  19519. interval : 20,
  19520. numPerOnce : 100
  19521. });
  19522. }
  19523. },
  19524. /**
  19525. * Check required folders for subfolders and update arrow classes
  19526. *
  19527. * @param Array folders to check
  19528. * @param String css class
  19529. * @return void
  19530. */
  19531. updateArrows = function(dirs, cls) {
  19532. var sel = cls == loaded
  19533. ? '.'+collapsed+':not(.'+loaded+')'
  19534. : ':not(.'+collapsed+')';
  19535. $.each(dirs, function(i, dir) {
  19536. $('#'+fm.navHash2Id(dir.phash)+sel)
  19537. .filter(function() { return $.grep($(this).next('.'+subtree).children(), function(n) {
  19538. return ($(n).children().hasClass(root))? false : true;
  19539. }).length > 0; })
  19540. .addClass(cls);
  19541. });
  19542. },
  19543. /**
  19544. * Navigation tree
  19545. *
  19546. * @type JQuery
  19547. */
  19548. tree = $(this).addClass(treeclass)
  19549. // make dirs draggable and toggle hover class
  19550. .on('mouseenter mouseleave', selNavdir, function(e) {
  19551. var enter = (e.type === 'mouseenter');
  19552. if (enter && scrolling) { return; }
  19553. var link = $(this);
  19554. if (!link.hasClass(dropover+' '+disabled)) {
  19555. if (!mobile && enter && !link.data('dragRegisted') && !link.hasClass(root+' '+draggable+' elfinder-na elfinder-wo')) {
  19556. link.data('dragRegisted', true);
  19557. if (fm.isCommandEnabled('copy', fm.navId2Hash(link.attr('id')))) {
  19558. link.draggable(fm.draggable);
  19559. }
  19560. }
  19561. link.toggleClass(hover, enter);
  19562. }
  19563. })
  19564. // native drag enter
  19565. .on('dragenter', selNavdir, function(e) {
  19566. if (e.originalEvent.dataTransfer) {
  19567. var dst = $(this);
  19568. dst.addClass(hover);
  19569. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  19570. dst.data('expandTimer', setTimeout(function() {
  19571. dst.is('.'+collapsed+'.'+hover) && dst.children('.'+arrow).trigger('click');
  19572. }, 500));
  19573. }
  19574. }
  19575. })
  19576. // native drag leave
  19577. .on('dragleave', selNavdir, function(e) {
  19578. if (e.originalEvent.dataTransfer) {
  19579. var dst = $(this);
  19580. dst.data('expandTimer') && clearTimeout(dst.data('expandTimer'));
  19581. dst.removeClass(hover);
  19582. }
  19583. })
  19584. // open dir or open subfolders in tree
  19585. .on('click', selNavdir, function(e) {
  19586. var link = $(this),
  19587. hash = fm.navId2Hash(link.attr('id')),
  19588. file = fm.file(hash);
  19589. if (link.data('longtap')) {
  19590. link.removeData('longtap');
  19591. e.stopPropagation();
  19592. return;
  19593. }
  19594. if (!link.hasClass(active)) {
  19595. tree.find(selNavdir+'.'+active).removeClass(active);
  19596. link.addClass(active);
  19597. }
  19598. if (hash != fm.cwd().hash && !link.hasClass(disabled)) {
  19599. fm.exec('open', hash).done(function() {
  19600. fm.one('opendone', function() {
  19601. fm.select({selected: [hash], origin: 'navbar'});
  19602. });
  19603. });
  19604. } else {
  19605. if (link.hasClass(collapsed)) {
  19606. link.children('.'+arrow).trigger('click');
  19607. }
  19608. fm.select({selected: [hash], origin: 'navbar'});
  19609. }
  19610. })
  19611. // for touch device
  19612. .on('touchstart', selNavdir, function(e) {
  19613. if (e.originalEvent.touches.length > 1) {
  19614. return;
  19615. }
  19616. var evt = e.originalEvent,
  19617. p;
  19618. if (e.target.nodeName === 'INPUT') {
  19619. e.stopPropagation();
  19620. return;
  19621. }
  19622. p = $(this).addClass(hover)
  19623. .removeData('longtap')
  19624. .data('tmlongtap', setTimeout(function(e){
  19625. // long tap
  19626. p.data('longtap', true);
  19627. fm.trigger('contextmenu', {
  19628. 'type' : 'navbar',
  19629. 'targets' : [fm.navId2Hash(p.attr('id'))],
  19630. 'x' : evt.touches[0].pageX,
  19631. 'y' : evt.touches[0].pageY
  19632. });
  19633. }, 500));
  19634. })
  19635. .on('touchmove touchend', selNavdir, function(e) {
  19636. if (e.target.nodeName === 'INPUT') {
  19637. e.stopPropagation();
  19638. return;
  19639. }
  19640. clearTimeout($(this).data('tmlongtap'));
  19641. if (e.type == 'touchmove') {
  19642. $(this).removeClass(hover);
  19643. }
  19644. })
  19645. // toggle subfolders in tree
  19646. .on('click', selNavdir+'.'+collapsed+' .'+arrow, function(e) {
  19647. var arrow = $(this),
  19648. link = arrow.parent(selNavdir),
  19649. stree = link.next('.'+subtree),
  19650. dfrd = $.Deferred(),
  19651. slideTH = 30, cnt;
  19652. e.stopPropagation();
  19653. if (link.hasClass(loaded)) {
  19654. link.toggleClass(expanded);
  19655. fm.lazy(function() {
  19656. cnt = link.hasClass(expanded)? stree.children().length + stree.find('div.elfinder-navbar-subtree[style*=block]').children().length : stree.find('div:visible').length;
  19657. if (cnt > slideTH) {
  19658. stree.toggle();
  19659. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  19660. checkSubdirs();
  19661. } else {
  19662. stree.stop(true, true)[link.hasClass(expanded)? 'slideDown' : 'slideUp'](opts.durations.slideUpDown, function(){
  19663. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  19664. checkSubdirs();
  19665. });
  19666. }
  19667. }).always(function() {
  19668. dfrd.resolve();
  19669. });
  19670. } else {
  19671. spinner.insertBefore(arrow);
  19672. link.removeClass(collapsed);
  19673. fm.request({cmd : 'tree', target : fm.navId2Hash(link.attr('id'))})
  19674. .done(function(data) {
  19675. updateTree(Object.assign([], filter(data.tree)));
  19676. if (stree.children().length) {
  19677. link.addClass(collapsed+' '+expanded);
  19678. if (stree.children().length > slideTH) {
  19679. stree.show();
  19680. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  19681. checkSubdirs();
  19682. } else {
  19683. stree.stop(true, true).slideDown(opts.durations.slideUpDown, function(){
  19684. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  19685. checkSubdirs();
  19686. });
  19687. }
  19688. }
  19689. })
  19690. .always(function(data) {
  19691. spinner.remove();
  19692. link.addClass(loaded);
  19693. fm.one('treedone', function() {
  19694. dfrd.resolve();
  19695. });
  19696. });
  19697. }
  19698. arrow.data('dfrd', dfrd);
  19699. })
  19700. .on('contextmenu', selNavdir, function(e) {
  19701. var self = $(this);
  19702. // now dirname editing
  19703. if (self.find('input:text').length) {
  19704. e.stopPropagation();
  19705. return;
  19706. }
  19707. e.preventDefault();
  19708. fm.trigger('contextmenu', {
  19709. 'type' : 'navbar',
  19710. 'targets' : [fm.navId2Hash($(this).attr('id'))],
  19711. 'x' : e.pageX,
  19712. 'y' : e.pageY
  19713. });
  19714. self.addClass('ui-state-hover');
  19715. fm.getUI('contextmenu').children().on('mouseenter', function() {
  19716. self.addClass('ui-state-hover');
  19717. });
  19718. fm.bind('closecontextmenu', function() {
  19719. self.removeClass('ui-state-hover');
  19720. });
  19721. })
  19722. .on('scrolltoview', selNavdir, function(e, data) {
  19723. var self = $(this);
  19724. autoScroll(self.attr('id')).done(function() {
  19725. if (!data || data.blink === 'undefined' || data.blink) {
  19726. fm.resources.blink(self, 'lookme');
  19727. }
  19728. });
  19729. })
  19730. // prepend fake dir
  19731. .on('create.'+fm.namespace, function(e, item) {
  19732. var pdir = findSubtree(item.phash),
  19733. lock = item.move || false,
  19734. dir = $(itemhtml(item)).addClass('elfinder-navbar-wrapper-tmp'),
  19735. selected = fm.selected();
  19736. lock && selected.length && fm.trigger('lockfiles', {files: selected});
  19737. pdir.prepend(dir);
  19738. }),
  19739. scrolling = false,
  19740. navbarScrTm,
  19741. // move tree into navbar
  19742. navbar = fm.getUI('navbar').append(tree).show().on('scroll', function() {
  19743. scrolling = true;
  19744. navbarScrTm && cancelAnimationFrame(navbarScrTm);
  19745. navbarScrTm = requestAnimationFrame(function() {
  19746. scrolling = false;
  19747. checkSubdirs();
  19748. });
  19749. }),
  19750. prevSortTreeview = fm.sortAlsoTreeview;
  19751. fm.open(function(e) {
  19752. var data = e.data,
  19753. dirs = filter(data.files),
  19754. contextmenu = fm.getUI('contextmenu');
  19755. data.init && tree.empty();
  19756. if (fm.UA.iOS) {
  19757. navbar.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch');
  19758. }
  19759. if (dirs.length) {
  19760. fm.lazy(function() {
  19761. if (!contextmenu.data('cmdMaps')) {
  19762. contextmenu.data('cmdMaps', {});
  19763. }
  19764. updateTree(dirs);
  19765. updateArrows(dirs, loaded);
  19766. sync(dirs);
  19767. });
  19768. } else {
  19769. sync();
  19770. }
  19771. })
  19772. // add new dirs
  19773. .add(function(e) {
  19774. var dirs = filter(e.data.added);
  19775. if (dirs.length) {
  19776. updateTree(dirs);
  19777. updateArrows(dirs, collapsed);
  19778. }
  19779. })
  19780. // update changed dirs
  19781. .change(function(e) {
  19782. // do ot perfome while syncing
  19783. if (syncing) {
  19784. return;
  19785. }
  19786. var dirs = filter(e.data.changed, true),
  19787. length = dirs.length,
  19788. l = length,
  19789. tgts = $(),
  19790. changed = {},
  19791. dir, phash, node, tmp, realParent, reqParent, realSibling, reqSibling, isExpanded, isLoaded, parent, subdirs;
  19792. $.each(hasMoreDirs, function(h, node) {
  19793. node.trigger('update.'+fm.namespace, { change: 'prepare' });
  19794. });
  19795. while (l--) {
  19796. dir = dirs[l];
  19797. phash = dir.phash;
  19798. if ((node = $('#'+fm.navHash2Id(dir.hash))).length) {
  19799. parent = node.parent();
  19800. if (phash) {
  19801. realParent = node.closest('.'+subtree);
  19802. reqParent = findSubtree(phash);
  19803. realSibling = node.parent().next();
  19804. reqSibling = findSibling(reqParent, dir);
  19805. if (!reqParent.length) {
  19806. continue;
  19807. }
  19808. if (reqParent[0] !== realParent[0] || realSibling.get(0) !== reqSibling.get(0)) {
  19809. reqSibling.length ? reqSibling.before(parent) : reqParent.append(parent);
  19810. }
  19811. }
  19812. isExpanded = node.hasClass(expanded);
  19813. isLoaded = node.hasClass(loaded);
  19814. tmp = $(itemhtml(dir));
  19815. node.replaceWith(tmp.children(selNavdir));
  19816. ! mobile && updateDroppable(null, parent);
  19817. if (dir.dirs
  19818. && (isExpanded || isLoaded)
  19819. && (node = $('#'+fm.navHash2Id(dir.hash)))
  19820. && node.next('.'+subtree).children().length) {
  19821. isExpanded && node.addClass(expanded);
  19822. isLoaded && node.addClass(loaded);
  19823. }
  19824. subdirs |= dir.dirs == -1;
  19825. }
  19826. }
  19827. // to check subdirs
  19828. if (subdirs) {
  19829. checkSubdirs();
  19830. }
  19831. $.each(hasMoreDirs, function(h, node) {
  19832. node.trigger('update.'+fm.namespace, { change: 'done' });
  19833. });
  19834. length && sync(void(0), false);
  19835. })
  19836. // remove dirs
  19837. .remove(function(e) {
  19838. var dirs = e.data.removed,
  19839. l = dirs.length,
  19840. node, stree, removed;
  19841. $.each(hasMoreDirs, function(h, node) {
  19842. node.trigger('update.'+fm.namespace, { removed : dirs });
  19843. node.trigger('update.'+fm.namespace, { change: 'prepare' });
  19844. });
  19845. while (l--) {
  19846. if ((node = $('#'+fm.navHash2Id(dirs[l]))).length) {
  19847. removed = true;
  19848. stree = node.closest('.'+subtree);
  19849. node.parent().detach();
  19850. if (!stree.children().length) {
  19851. stree.hide().prev(selNavdir).removeClass(collapsed+' '+expanded+' '+loaded);
  19852. }
  19853. }
  19854. }
  19855. removed && fm.getUI('navbar').children('.ui-resizable-handle').trigger('resize');
  19856. $.each(hasMoreDirs, function(h, node) {
  19857. node.trigger('update.'+fm.namespace, { change: 'done' });
  19858. });
  19859. })
  19860. // lock/unlock dirs while moving
  19861. .bind('lockfiles unlockfiles', function(e) {
  19862. var lock = e.type == 'lockfiles',
  19863. helperLocked = e.data.helper? e.data.helper.data('locked') : false,
  19864. act = (lock && !helperLocked) ? 'disable' : 'enable',
  19865. dirs = $.grep(e.data.files||[], function(h) {
  19866. var dir = fm.file(h);
  19867. return dir && dir.mime == 'directory' ? true : false;
  19868. });
  19869. $.each(dirs, function(i, hash) {
  19870. var dir = $('#'+fm.navHash2Id(hash));
  19871. if (dir.length && !helperLocked) {
  19872. dir.hasClass(draggable) && dir.draggable(act);
  19873. dir.hasClass(droppable) && dir.droppable(act);
  19874. dir[lock ? 'addClass' : 'removeClass'](disabled);
  19875. }
  19876. });
  19877. })
  19878. .bind('sortchange', function() {
  19879. if (fm.sortAlsoTreeview || prevSortTreeview !== fm.sortAlsoTreeview) {
  19880. var dirs,
  19881. ends = [],
  19882. endsMap = {},
  19883. endsVid = {},
  19884. topVid = '',
  19885. single = false,
  19886. current;
  19887. fm.lazy(function() {
  19888. dirs = filter(fm.files());
  19889. prevSortTreeview = fm.sortAlsoTreeview;
  19890. tree.empty();
  19891. // append volume roots at first
  19892. updateTree($.map(fm.roots, function(h) {
  19893. var dir = fm.file(h);
  19894. return dir && !dir.phash? dir : null;
  19895. }));
  19896. if (!Object.keys(hasMoreDirs).length) {
  19897. updateTree(dirs);
  19898. current = selectPages();
  19899. updateArrows(dirs, loaded);
  19900. } else {
  19901. ends = getEnds();
  19902. if (ends.length > 1) {
  19903. $.each(ends, function(i, end) {
  19904. var vid = fm.file(fm.root(end)).volumeid;
  19905. if (i === 0) {
  19906. topVid = vid;
  19907. }
  19908. endsVid[vid] = end;
  19909. endsMap[end] = [];
  19910. });
  19911. $.each(dirs, function(i, d) {
  19912. if (!d.volumeid) {
  19913. single = true;
  19914. return false;
  19915. }
  19916. endsMap[endsVid[d.volumeid] || endsVid[topVid]].push(d);
  19917. });
  19918. } else {
  19919. single = true;
  19920. }
  19921. if (single) {
  19922. $.each(ends, function(i, endHash) {
  19923. updateTree(dirs);
  19924. current = selectPages(fm.file(endHash));
  19925. updateArrows(dirs, loaded);
  19926. });
  19927. } else {
  19928. $.each(endsMap, function(endHash, dirs) {
  19929. updateTree(dirs);
  19930. current = selectPages(fm.file(endHash));
  19931. updateArrows(dirs, loaded);
  19932. });
  19933. }
  19934. }
  19935. sync();
  19936. }, 100);
  19937. }
  19938. });
  19939. });
  19940. return this;
  19941. };
  19942. /*
  19943. * File: /js/ui/uploadButton.js
  19944. */
  19945. /**
  19946. * @class elFinder toolbar's button tor upload file
  19947. *
  19948. * @author Dmitry (dio) Levashov
  19949. **/
  19950. $.fn.elfinderuploadbutton = function(cmd) {
  19951. return this.each(function() {
  19952. var fm = cmd.fm,
  19953. button = $(this).elfinderbutton(cmd)
  19954. .off('click'),
  19955. form = $('<form/>').appendTo(button),
  19956. input = $('<input type="file" multiple="true" title="'+cmd.fm.i18n('selectForUpload')+'"/>')
  19957. .on('change', function() {
  19958. var _input = $(this);
  19959. if (_input.val()) {
  19960. fm.exec('upload', {input : _input.remove()[0]}, void(0), fm.cwd().hash);
  19961. input.clone(true).appendTo(form);
  19962. }
  19963. })
  19964. .on('dragover', function(e) {
  19965. e.originalEvent.dataTransfer.dropEffect = 'copy';
  19966. }),
  19967. tm;
  19968. form.append(input.clone(true));
  19969. cmd.change(function() {
  19970. tm && cancelAnimationFrame(tm);
  19971. tm = requestAnimationFrame(function() {
  19972. var toShow = cmd.disabled();
  19973. if (form.is('visible')) {
  19974. !toShow && form.hide();
  19975. } else {
  19976. toShow && form.show();
  19977. }
  19978. });
  19979. })
  19980. .change();
  19981. });
  19982. };
  19983. /*
  19984. * File: /js/ui/viewbutton.js
  19985. */
  19986. /**
  19987. * @class elFinder toolbar button to switch current directory view.
  19988. *
  19989. * @author Dmitry (dio) Levashov
  19990. **/
  19991. $.fn.elfinderviewbutton = function(cmd) {
  19992. return this.each(function() {
  19993. var button = $(this).elfinderbutton(cmd),
  19994. icon = button.children('.elfinder-button-icon'),
  19995. text = button.children('.elfinder-button-text'),
  19996. tm;
  19997. cmd.change(function() {
  19998. tm && cancelAnimationFrame(tm);
  19999. tm = requestAnimationFrame(function() {
  20000. var icons = cmd.value == 'icons';
  20001. icon.toggleClass('elfinder-button-icon-view-list', icons);
  20002. cmd.className = icons? 'view-list' : '';
  20003. cmd.title = cmd.fm.i18n(icons ? 'viewlist' : 'viewicons');
  20004. button.attr('title', cmd.title);
  20005. text.html(cmd.title);
  20006. });
  20007. });
  20008. });
  20009. };
  20010. /*
  20011. * File: /js/ui/workzone.js
  20012. */
  20013. /**
  20014. * @class elfinderworkzone - elFinder container for nav and current directory
  20015. * @author Dmitry (dio) Levashov
  20016. **/
  20017. $.fn.elfinderworkzone = function(fm) {
  20018. var cl = 'elfinder-workzone';
  20019. this.not('.'+cl).each(function() {
  20020. var wz = $(this).addClass(cl),
  20021. prevH = Math.round(wz.height()),
  20022. parent = wz.parent(),
  20023. setDelta = function() {
  20024. wdelta = wz.outerHeight(true) - wz.height();
  20025. },
  20026. fitsize = function(e) {
  20027. var height = parent.height() - wdelta,
  20028. style = parent.attr('style'),
  20029. curH = Math.round(wz.height());
  20030. if (e) {
  20031. e.preventDefault();
  20032. e.stopPropagation();
  20033. }
  20034. parent.css('overflow', 'hidden')
  20035. .children(':visible:not(.'+cl+')').each(function() {
  20036. var ch = $(this);
  20037. if (ch.css('position') != 'absolute' && ch.css('position') != 'fixed') {
  20038. height -= ch.outerHeight(true);
  20039. }
  20040. });
  20041. parent.attr('style', style || '');
  20042. height = Math.max(0, Math.round(height));
  20043. if (prevH !== height || curH !== height) {
  20044. prevH = Math.round(wz.height());
  20045. wz.height(height);
  20046. fm.trigger('wzresize');
  20047. }
  20048. },
  20049. cssloaded = function() {
  20050. wdelta = wz.outerHeight(true) - wz.height();
  20051. fitsize();
  20052. },
  20053. wdelta;
  20054. setDelta();
  20055. parent.on('resize.' + fm.namespace, fitsize);
  20056. fm.one('cssloaded', cssloaded)
  20057. .bind('uiresize', fitsize)
  20058. .bind('themechange', setDelta);
  20059. });
  20060. return this;
  20061. };
  20062. /*
  20063. * File: /js/commands/archive.js
  20064. */
  20065. /**
  20066. * @class elFinder command "archive"
  20067. * Archive selected files
  20068. *
  20069. * @author Dmitry (dio) Levashov
  20070. **/
  20071. elFinder.prototype.commands.archive = function() {
  20072. var self = this,
  20073. fm = self.fm,
  20074. mimes = [],
  20075. dfrd;
  20076. this.variants = [];
  20077. this.disableOnSearch = false;
  20078. this.nextAction = {};
  20079. /**
  20080. * Update mimes on open/reload
  20081. *
  20082. * @return void
  20083. **/
  20084. fm.bind('open reload', function() {
  20085. self.variants = [];
  20086. $.each((mimes = fm.option('archivers')['create'] || []), function(i, mime) {
  20087. self.variants.push([mime, fm.mime2kind(mime)]);
  20088. });
  20089. self.change();
  20090. });
  20091. this.getstate = function(select) {
  20092. var sel = this.files(select),
  20093. cnt = sel.length,
  20094. chk = (cnt && ! fm.isRoot(sel[0]) && (fm.file(sel[0].phash) || {}).write && ! $.grep(sel, function(f){ return f.read ? false : true; }).length),
  20095. cwdId;
  20096. if (chk && fm.searchStatus.state > 1) {
  20097. cwdId = fm.cwd().volumeid;
  20098. chk = (cnt === $.grep(sel, function(f) { return f.read && f.hash.indexOf(cwdId) === 0 ? true : false; }).length);
  20099. }
  20100. return chk && !this._disabled && mimes.length && (cnt || (dfrd && dfrd.state() == 'pending')) ? 0 : -1;
  20101. };
  20102. this.exec = function(hashes, type) {
  20103. var files = this.files(hashes),
  20104. cnt = files.length,
  20105. mime = type || mimes[0],
  20106. cwd = fm.file(files[0].phash) || null,
  20107. error = ['errArchive', 'errPerm', 'errCreatingTempDir', 'errFtpDownloadFile', 'errFtpUploadFile', 'errFtpMkdir', 'errArchiveExec', 'errExtractExec', 'errRm'],
  20108. i, open;
  20109. dfrd = $.Deferred().fail(function(error) {
  20110. error && fm.error(error);
  20111. });
  20112. if (! (cnt && mimes.length && $.inArray(mime, mimes) !== -1)) {
  20113. return dfrd.reject();
  20114. }
  20115. if (!cwd.write) {
  20116. return dfrd.reject(error);
  20117. }
  20118. for (i = 0; i < cnt; i++) {
  20119. if (!files[i].read) {
  20120. return dfrd.reject(error);
  20121. }
  20122. }
  20123. self.mime = mime;
  20124. self.prefix = ((cnt > 1)? 'Archive' : files[0].name) + (fm.option('archivers')['createext']? '.' + fm.option('archivers')['createext'][mime] : '');
  20125. self.data = {targets : self.hashes(hashes), type : mime};
  20126. if (fm.cwd().hash !== cwd.hash) {
  20127. open = fm.exec('open', cwd.hash).done(function() {
  20128. fm.one('cwdrender', function() {
  20129. fm.selectfiles({files : hashes});
  20130. dfrd = $.proxy(fm.res('mixin', 'make'), self)();
  20131. });
  20132. });
  20133. } else {
  20134. fm.selectfiles({files : hashes});
  20135. dfrd = $.proxy(fm.res('mixin', 'make'), self)();
  20136. }
  20137. return dfrd;
  20138. };
  20139. };
  20140. /*
  20141. * File: /js/commands/back.js
  20142. */
  20143. /**
  20144. * @class elFinder command "back"
  20145. * Open last visited folder
  20146. *
  20147. * @author Dmitry (dio) Levashov
  20148. **/
  20149. (elFinder.prototype.commands.back = function() {
  20150. this.alwaysEnabled = true;
  20151. this.updateOnSelect = false;
  20152. this.shortcuts = [{
  20153. pattern : 'ctrl+left backspace'
  20154. }];
  20155. this.getstate = function() {
  20156. return this.fm.history.canBack() ? 0 : -1;
  20157. };
  20158. this.exec = function() {
  20159. return this.fm.history.back();
  20160. };
  20161. }).prototype = { forceLoad : true }; // this is required command
  20162. /*
  20163. * File: /js/commands/chmod.js
  20164. */
  20165. /**
  20166. * @class elFinder command "chmod".
  20167. * Chmod files.
  20168. *
  20169. * @type elFinder.command
  20170. * @author Naoki Sawada
  20171. */
  20172. elFinder.prototype.commands.chmod = function() {
  20173. this.updateOnSelect = false;
  20174. var fm = this.fm,
  20175. level = {
  20176. 0 : 'owner',
  20177. 1 : 'group',
  20178. 2 : 'other'
  20179. },
  20180. msg = {
  20181. read : fm.i18n('read'),
  20182. write : fm.i18n('write'),
  20183. execute : fm.i18n('execute'),
  20184. perm : fm.i18n('perm'),
  20185. kind : fm.i18n('kind'),
  20186. files : fm.i18n('files')
  20187. },
  20188. isPerm = function(perm){
  20189. return (!isNaN(parseInt(perm, 8) && parseInt(perm, 8) <= 511) || perm.match(/^([r-][w-][x-]){3}$/i));
  20190. };
  20191. this.tpl = {
  20192. main : '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}</div>'
  20193. +'{dataTable}',
  20194. itemTitle : '<strong>{name}</strong><span id="elfinder-info-kind">{kind}</span>',
  20195. groupTitle : '<strong>{items}: {num}</strong>',
  20196. dataTable : '<table id="{id}-table-perm"><tr><td>{0}</td><td>{1}</td><td>{2}</td></tr></table>'
  20197. +'<div class="">'+msg.perm+': <input class="elfinder-tabstop elfinder-focus" id="{id}-perm" type="text" size="4" maxlength="3" value="{value}"></div>',
  20198. fieldset : '<fieldset id="{id}-fieldset-{level}"><legend>{f_title}{name}</legend>'
  20199. +'<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>'
  20200. +'<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>'
  20201. +'<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>'
  20202. };
  20203. this.shortcuts = [{
  20204. //pattern : 'ctrl+p'
  20205. }];
  20206. this.getstate = function(sel) {
  20207. var fm = this.fm;
  20208. sel = sel || fm.selected();
  20209. if (sel.length == 0) {
  20210. sel = [ fm.cwd().hash ];
  20211. }
  20212. return this.checkstate(this.files(sel)) ? 0 : -1;
  20213. };
  20214. this.checkstate = function(sel) {
  20215. var cnt = sel.length;
  20216. if (!cnt) return false;
  20217. var chk = $.grep(sel, function(f) {
  20218. return (f.isowner && f.perm && isPerm(f.perm) && (cnt == 1 || f.mime != 'directory')) ? true : false;
  20219. }).length;
  20220. return (cnt == chk)? true : false;
  20221. };
  20222. this.exec = function(select) {
  20223. var hashes = this.hashes(select),
  20224. files = this.files(hashes);
  20225. if (! files.length) {
  20226. hashes = [ this.fm.cwd().hash ];
  20227. files = this.files(hashes);
  20228. }
  20229. var fm = this.fm,
  20230. dfrd = $.Deferred().always(function() {
  20231. fm.enable();
  20232. }),
  20233. tpl = this.tpl,
  20234. cnt = files.length,
  20235. file = files[0],
  20236. id = fm.namespace + '-perm-' + file.hash,
  20237. view = tpl.main,
  20238. checked = ' checked="checked"',
  20239. buttons = function() {
  20240. var buttons = {};
  20241. buttons[fm.i18n('btnApply')] = save;
  20242. buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); };
  20243. return buttons;
  20244. },
  20245. save = function() {
  20246. var perm = $.trim($('#'+id+'-perm').val()),
  20247. reqData;
  20248. if (!isPerm(perm)) return false;
  20249. dialog.elfinderdialog('close');
  20250. reqData = {
  20251. cmd : 'chmod',
  20252. targets : hashes,
  20253. mode : perm
  20254. };
  20255. fm.request({
  20256. data : reqData,
  20257. notify : {type : 'chmod', cnt : cnt}
  20258. })
  20259. .fail(function(error) {
  20260. dfrd.reject(error);
  20261. })
  20262. .done(function(data) {
  20263. if (data.changed && data.changed.length) {
  20264. data.undo = {
  20265. cmd : 'chmod',
  20266. callback : function() {
  20267. var reqs = [];
  20268. $.each(prevVals, function(perm, hashes) {
  20269. reqs.push(fm.request({
  20270. data : {cmd : 'chmod', targets : hashes, mode : perm},
  20271. notify : {type : 'undo', cnt : hashes.length}
  20272. }));
  20273. });
  20274. return $.when.apply(null, reqs);
  20275. }
  20276. };
  20277. data.redo = {
  20278. cmd : 'chmod',
  20279. callback : function() {
  20280. return fm.request({
  20281. data : reqData,
  20282. notify : {type : 'redo', cnt : hashes.length}
  20283. });
  20284. }
  20285. };
  20286. }
  20287. dfrd.resolve(data);
  20288. });
  20289. },
  20290. setperm = function() {
  20291. var perm = '';
  20292. var _perm;
  20293. for (var i = 0; i < 3; i++){
  20294. _perm = 0;
  20295. if ($("#"+id+"-read-"+level[i]+'-perm').is(':checked')) {
  20296. _perm = (_perm | 4);
  20297. }
  20298. if ($("#"+id+"-write-"+level[i]+'-perm').is(':checked')) {
  20299. _perm = (_perm | 2);
  20300. }
  20301. if ($("#"+id+"-execute-"+level[i]+'-perm').is(':checked')) {
  20302. _perm = (_perm | 1);
  20303. }
  20304. perm += _perm.toString(8);
  20305. }
  20306. $('#'+id+'-perm').val(perm);
  20307. },
  20308. setcheck = function(perm) {
  20309. var _perm;
  20310. for (var i = 0; i < 3; i++){
  20311. _perm = parseInt(perm.slice(i, i+1), 8);
  20312. $("#"+id+"-read-"+level[i]+'-perm').prop("checked", false);
  20313. $("#"+id+"-write-"+level[i]+'-perm').prop("checked", false);
  20314. $("#"+id+"-execute-"+level[i]+'-perm').prop("checked", false);
  20315. if ((_perm & 4) == 4) {
  20316. $("#"+id+"-read-"+level[i]+'-perm').prop("checked", true);
  20317. }
  20318. if ((_perm & 2) == 2) {
  20319. $("#"+id+"-write-"+level[i]+'-perm').prop("checked", true);
  20320. }
  20321. if ((_perm & 1) == 1) {
  20322. $("#"+id+"-execute-"+level[i]+'-perm').prop("checked", true);
  20323. }
  20324. }
  20325. setperm();
  20326. },
  20327. makeperm = function(files) {
  20328. var perm = '777', ret = '', chk, _chk, _perm;
  20329. var len = files.length;
  20330. for (var i2 = 0; i2 < len; i2++) {
  20331. chk = getPerm(files[i2].perm);
  20332. if (! prevVals[chk]) {
  20333. prevVals[chk] = [];
  20334. }
  20335. prevVals[chk].push(files[i2].hash);
  20336. ret = '';
  20337. for (var i = 0; i < 3; i++){
  20338. _chk = parseInt(chk.slice(i, i+1), 8);
  20339. _perm = parseInt(perm.slice(i, i+1), 8);
  20340. if ((_chk & 4) != 4 && (_perm & 4) == 4) {
  20341. _perm -= 4;
  20342. }
  20343. if ((_chk & 2) != 2 && (_perm & 2) == 2) {
  20344. _perm -= 2;
  20345. }
  20346. if ((_chk & 1) != 1 && (_perm & 1) == 1) {
  20347. _perm -= 1;
  20348. }
  20349. ret += _perm.toString(8);
  20350. }
  20351. perm = ret;
  20352. }
  20353. return perm;
  20354. },
  20355. makeName = function(name) {
  20356. return name? ':'+name : '';
  20357. },
  20358. makeDataTable = function(perm, f) {
  20359. var _perm, fieldset;
  20360. var value = '';
  20361. var dataTable = tpl.dataTable;
  20362. for (var i = 0; i < 3; i++){
  20363. _perm = parseInt(perm.slice(i, i+1), 8);
  20364. value += _perm.toString(8);
  20365. fieldset = tpl.fieldset.replace('{f_title}', fm.i18n(level[i])).replace('{name}', makeName(f[level[i]])).replace(/\{level\}/g, level[i]);
  20366. dataTable = dataTable.replace('{'+i+'}', fieldset)
  20367. .replace('{checked-r}', ((_perm & 4) == 4)? checked : '')
  20368. .replace('{checked-w}', ((_perm & 2) == 2)? checked : '')
  20369. .replace('{checked-x}', ((_perm & 1) == 1)? checked : '');
  20370. }
  20371. dataTable = dataTable.replace('{value}', value).replace('{valueCaption}', msg['perm']);
  20372. return dataTable;
  20373. },
  20374. getPerm = function(perm){
  20375. if (isNaN(parseInt(perm, 8))) {
  20376. var mode_array = perm.split('');
  20377. var a = [];
  20378. for (var i = 0, l = mode_array.length; i < l; i++) {
  20379. if (i === 0 || i === 3 || i === 6) {
  20380. if (mode_array[i].match(/[r]/i)) {
  20381. a.push(1);
  20382. } else if (mode_array[i].match(/[-]/)) {
  20383. a.push(0);
  20384. }
  20385. } else if ( i === 1 || i === 4 || i === 7) {
  20386. if (mode_array[i].match(/[w]/i)) {
  20387. a.push(1);
  20388. } else if (mode_array[i].match(/[-]/)) {
  20389. a.push(0);
  20390. }
  20391. } else {
  20392. if (mode_array[i].match(/[x]/i)) {
  20393. a.push(1);
  20394. } else if (mode_array[i].match(/[-]/)) {
  20395. a.push(0);
  20396. }
  20397. }
  20398. }
  20399. a.splice(3, 0, ",");
  20400. a.splice(7, 0, ",");
  20401. var b = a.join("");
  20402. var b_array = b.split(",");
  20403. var c = [];
  20404. for (var j = 0, m = b_array.length; j < m; j++) {
  20405. var p = parseInt(b_array[j], 2).toString(8);
  20406. c.push(p);
  20407. }
  20408. perm = c.join('');
  20409. } else {
  20410. perm = parseInt(perm, 8).toString(8);
  20411. }
  20412. return perm;
  20413. },
  20414. opts = {
  20415. title : this.title,
  20416. width : 'auto',
  20417. buttons : buttons(),
  20418. close : function() { $(this).elfinderdialog('destroy'); }
  20419. },
  20420. dialog = fm.getUI().find('#'+id),
  20421. prevVals = {},
  20422. tmb = '', title, dataTable;
  20423. if (dialog.length) {
  20424. dialog.elfinderdialog('toTop');
  20425. return $.Deferred().resolve();
  20426. }
  20427. view = view.replace('{class}', cnt > 1 ? 'elfinder-cwd-icon-group' : fm.mime2class(file.mime));
  20428. if (cnt > 1) {
  20429. title = tpl.groupTitle.replace('{items}', fm.i18n('items')).replace('{num}', cnt);
  20430. } else {
  20431. title = tpl.itemTitle.replace('{name}', file.name).replace('{kind}', fm.mime2kind(file));
  20432. tmb = fm.tmb(file);
  20433. }
  20434. dataTable = makeDataTable(makeperm(files), files.length == 1? files[0] : {});
  20435. view = view.replace('{title}', title).replace('{dataTable}', dataTable).replace(/{id}/g, id);
  20436. dialog = this.fmDialog(view, opts);
  20437. dialog.attr('id', id);
  20438. // load thumbnail
  20439. if (tmb) {
  20440. $('<img/>')
  20441. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  20442. .attr('src', tmb.url);
  20443. }
  20444. $('#' + id + '-table-perm :checkbox').on('click', function(){setperm('perm');});
  20445. $('#' + id + '-perm').on('keydown', function(e) {
  20446. var c = e.keyCode;
  20447. if (c == $.ui.keyCode.ENTER) {
  20448. e.stopPropagation();
  20449. save();
  20450. return;
  20451. }
  20452. }).on('focus', function(e){
  20453. $(this).trigger('select');
  20454. }).on('keyup', function(e) {
  20455. if ($(this).val().length == 3) {
  20456. $(this).trigger('select');
  20457. setcheck($(this).val());
  20458. }
  20459. });
  20460. return dfrd;
  20461. };
  20462. };
  20463. /*
  20464. * File: /js/commands/colwidth.js
  20465. */
  20466. /**
  20467. * @class elFinder command "colwidth"
  20468. * CWD list table columns width to auto
  20469. *
  20470. * @author Naoki Sawada
  20471. **/
  20472. elFinder.prototype.commands.colwidth = function() {
  20473. this.alwaysEnabled = true;
  20474. this.updateOnSelect = false;
  20475. this.getstate = function() {
  20476. return this.fm.getUI('cwd').find('table').css('table-layout') === 'fixed' ? 0 : -1;
  20477. };
  20478. this.exec = function() {
  20479. this.fm.getUI('cwd').trigger('colwidth');
  20480. return $.Deferred().resolve();
  20481. };
  20482. };
  20483. /*
  20484. * File: /js/commands/copy.js
  20485. */
  20486. /**
  20487. * @class elFinder command "copy".
  20488. * Put files in filemanager clipboard.
  20489. *
  20490. * @type elFinder.command
  20491. * @author Dmitry (dio) Levashov
  20492. */
  20493. elFinder.prototype.commands.copy = function() {
  20494. this.shortcuts = [{
  20495. pattern : 'ctrl+c ctrl+insert'
  20496. }];
  20497. this.getstate = function(select) {
  20498. var sel = this.files(select),
  20499. cnt = sel.length;
  20500. return cnt && $.grep(sel, function(f) { return f.read ? true : false; }).length == cnt ? 0 : -1;
  20501. };
  20502. this.exec = function(hashes) {
  20503. var fm = this.fm,
  20504. dfrd = $.Deferred()
  20505. .fail(function(error) {
  20506. fm.error(error);
  20507. });
  20508. $.each(this.files(hashes), function(i, file) {
  20509. if (! file.read) {
  20510. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  20511. }
  20512. });
  20513. return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes)));
  20514. };
  20515. };
  20516. /*
  20517. * File: /js/commands/cut.js
  20518. */
  20519. /**
  20520. * @class elFinder command "copy".
  20521. * Put files in filemanager clipboard.
  20522. *
  20523. * @type elFinder.command
  20524. * @author Dmitry (dio) Levashov
  20525. */
  20526. elFinder.prototype.commands.cut = function() {
  20527. var fm = this.fm;
  20528. this.shortcuts = [{
  20529. pattern : 'ctrl+x shift+insert'
  20530. }];
  20531. this.getstate = function(select) {
  20532. var sel = this.files(select),
  20533. cnt = sel.length;
  20534. return cnt && $.grep(sel, function(f) { return f.read && ! f.locked && ! fm.isRoot(f) ? true : false; }).length == cnt ? 0 : -1;
  20535. };
  20536. this.exec = function(hashes) {
  20537. var dfrd = $.Deferred()
  20538. .fail(function(error) {
  20539. fm.error(error);
  20540. });
  20541. $.each(this.files(hashes), function(i, file) {
  20542. if (!(file.read && ! file.locked && ! fm.isRoot(file)) ) {
  20543. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  20544. }
  20545. if (file.locked) {
  20546. return !dfrd.reject(['errLocked', file.name]);
  20547. }
  20548. });
  20549. return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes), true));
  20550. };
  20551. };
  20552. /*
  20553. * File: /js/commands/download.js
  20554. */
  20555. /**
  20556. * @class elFinder command "download".
  20557. * Download selected files.
  20558. * Only for new api
  20559. *
  20560. * @author Dmitry (dio) Levashov, dio@std42.ru
  20561. **/
  20562. elFinder.prototype.commands.zipdl = function() {};
  20563. elFinder.prototype.commands.download = function() {
  20564. var self = this,
  20565. fm = this.fm,
  20566. czipdl = null,
  20567. zipOn = false,
  20568. mixed = false,
  20569. dlntf = false,
  20570. cpath = window.location.pathname || '/',
  20571. filter = function(hashes, inExec) {
  20572. var volumeid, mixedCmd;
  20573. if (czipdl !== null) {
  20574. if (fm.searchStatus.state > 1) {
  20575. mixed = fm.searchStatus.mixed;
  20576. } else if (fm.leafRoots[fm.cwd().hash]) {
  20577. volumeid = fm.cwd().volumeid;
  20578. $.each(hashes, function(i, h) {
  20579. if (h.indexOf(volumeid) !== 0) {
  20580. mixed = true;
  20581. return false;
  20582. }
  20583. });
  20584. }
  20585. zipOn = (fm.isCommandEnabled('zipdl', hashes[0]));
  20586. }
  20587. if (mixed) {
  20588. mixedCmd = czipdl? 'zipdl' : 'download';
  20589. hashes = $.grep(hashes, function(h) {
  20590. var f = fm.file(h),
  20591. res = (! f || (! czipdl && f.mime === 'directory') || ! fm.isCommandEnabled(mixedCmd, h))? false : true;
  20592. if (f && inExec && ! res) {
  20593. $('#' + fm.cwdHash2Id(f.hash)).trigger('unselect');
  20594. }
  20595. return res;
  20596. });
  20597. if (! hashes.length) {
  20598. return [];
  20599. }
  20600. } else {
  20601. if (!fm.isCommandEnabled('download', hashes[0])) {
  20602. return [];
  20603. }
  20604. }
  20605. return $.grep(self.files(hashes), function(f) {
  20606. var res = (! f.read || (! zipOn && f.mime == 'directory')) ? false : true;
  20607. if (inExec && ! res) {
  20608. $('#' + fm.cwdHash2Id(f.hash)).trigger('unselect');
  20609. }
  20610. return res;
  20611. });
  20612. };
  20613. this.linkedCmds = ['zipdl'];
  20614. this.shortcuts = [{
  20615. pattern : 'shift+enter'
  20616. }];
  20617. this.getstate = function(select) {
  20618. var sel = this.hashes(select),
  20619. cnt = sel.length,
  20620. maxReq = this.options.maxRequests || 10,
  20621. mixed = false,
  20622. croot = '';
  20623. if (cnt < 1) {
  20624. return -1;
  20625. }
  20626. cnt = filter(sel).length;
  20627. return (cnt && (zipOn || (cnt <= maxReq && ((!fm.UA.IE && !fm.UA.Mobile) || cnt == 1))) ? 0 : -1);
  20628. };
  20629. fm.bind('contextmenu', function(e){
  20630. var fm = self.fm,
  20631. helper = null,
  20632. targets, file, link,
  20633. getExtra = function(file) {
  20634. var link = file.url || fm.url(file.hash);
  20635. return {
  20636. icon: 'link',
  20637. node: $('<a/>')
  20638. .attr({href: link, target: '_blank', title: fm.i18n('link')})
  20639. .text(file.name)
  20640. .on('mousedown click touchstart touchmove touchend contextmenu', function(e){
  20641. e.stopPropagation();
  20642. })
  20643. .on('dragstart', function(e) {
  20644. var dt = e.dataTransfer || e.originalEvent.dataTransfer || null;
  20645. helper = null;
  20646. if (dt) {
  20647. var icon = function(f) {
  20648. var mime = f.mime, i, tmb = fm.tmb(f);
  20649. i = '<div class="elfinder-cwd-icon '+fm.mime2class(mime)+' ui-corner-all"/>';
  20650. if (tmb) {
  20651. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  20652. }
  20653. return i;
  20654. };
  20655. dt.effectAllowed = 'copyLink';
  20656. if (dt.setDragImage) {
  20657. helper = $('<div class="elfinder-drag-helper html5-native">').append(icon(file)).appendTo($(document.body));
  20658. dt.setDragImage(helper.get(0), 50, 47);
  20659. }
  20660. if (!fm.UA.IE) {
  20661. dt.setData('elfinderfrom', window.location.href + file.phash);
  20662. dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), '');
  20663. }
  20664. }
  20665. })
  20666. .on('dragend', function(e) {
  20667. helper && helper.remove();
  20668. })
  20669. };
  20670. };
  20671. self.extra = null;
  20672. if (e.data) {
  20673. targets = e.data.targets || [];
  20674. if (targets.length === 1 && (file = fm.file(targets[0])) && file.mime !== 'directory') {
  20675. if (file.url != '1') {
  20676. self.extra = getExtra(file);
  20677. } else {
  20678. // Get URL ondemand
  20679. var node;
  20680. self.extra = {
  20681. icon: 'link',
  20682. node: $('<a/>')
  20683. .attr({href: '#', title: fm.i18n('getLink'), draggable: 'false'})
  20684. .text(file.name)
  20685. .on('click touchstart', function(e){
  20686. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  20687. return;
  20688. }
  20689. var parent = node.parent();
  20690. e.stopPropagation();
  20691. e.preventDefault();
  20692. parent.removeClass('ui-state-disabled').addClass('elfinder-button-icon-spinner');
  20693. fm.request({
  20694. data : {cmd : 'url', target : file.hash},
  20695. preventDefault : true
  20696. })
  20697. .always(function(data) {
  20698. parent.removeClass('elfinder-button-icon-spinner');
  20699. if (data.url) {
  20700. var rfile = fm.file(file.hash);
  20701. rfile.url = data.url;
  20702. node.replaceWith(getExtra(file).node);
  20703. } else {
  20704. parent.addClass('ui-state-disabled');
  20705. }
  20706. });
  20707. })
  20708. };
  20709. node = self.extra.node;
  20710. node.ready(function(){
  20711. requestAnimationFrame(function(){
  20712. node.parent().addClass('ui-state-disabled').css('pointer-events', 'auto');
  20713. });
  20714. });
  20715. }
  20716. }
  20717. }
  20718. }).one('open', function() {
  20719. if (fm.api >= 2.1012) {
  20720. czipdl = fm.getCommand('zipdl');
  20721. }
  20722. dlntf = fm.api > 2.1038 && !fm.isCORS;
  20723. });
  20724. this.exec = function(select) {
  20725. var hashes = this.hashes(select),
  20726. fm = this.fm,
  20727. base = fm.options.url,
  20728. files = filter(hashes, true),
  20729. dfrd = $.Deferred(),
  20730. iframes = '',
  20731. cdata = '',
  20732. targets = {},
  20733. i, url,
  20734. linkdl = false,
  20735. getTask = function(hashes) {
  20736. return function() {
  20737. var dfd = $.Deferred(),
  20738. root = fm.file(fm.root(hashes[0])),
  20739. single = (hashes.length === 1),
  20740. volName = root? (root.i18 || root.name) : null,
  20741. dir, dlName, phash;
  20742. if (single) {
  20743. if (dir = fm.file(hashes[0])) {
  20744. dlName = (dir.i18 || dir.name);
  20745. }
  20746. } else {
  20747. $.each(hashes, function() {
  20748. var d = fm.file(this);
  20749. if (d && (!phash || phash === d.phash)) {
  20750. phash = d.phash;
  20751. } else {
  20752. phash = null;
  20753. return false;
  20754. }
  20755. });
  20756. if (phash && (dir = fm.file(phash))) {
  20757. dlName = (dir.i18 || dir.name) + '-' + hashes.length;
  20758. }
  20759. }
  20760. if (dlName) {
  20761. volName = dlName;
  20762. }
  20763. volName && (volName = ' (' + volName + ')');
  20764. fm.request({
  20765. data : {cmd : 'zipdl', targets : hashes},
  20766. notify : {type : 'zipdl', cnt : 1, hideCnt : true, msg : fm.i18n('ntfzipdl') + volName},
  20767. cancel : true,
  20768. eachCancel : true,
  20769. preventDefault : true
  20770. }).done(function(e) {
  20771. var zipdl, dialog, btn = {}, dllink, form, iframe, m,
  20772. uniq = 'dlw' + (+new Date());
  20773. if (e.error) {
  20774. fm.error(e.error);
  20775. dfd.resolve();
  20776. } else if (e.zipdl) {
  20777. zipdl = e.zipdl;
  20778. if (dlName) {
  20779. m = fm.splitFileExtention(zipdl.name || '');
  20780. dlName += m[1]? ('.' + m[1]) : '.zip';
  20781. } else {
  20782. dlName = zipdl.name;
  20783. }
  20784. if ((html5dl && (!fm.UA.Safari || fm.isSameOrigin(fm.options.url))) || linkdl) {
  20785. url = fm.options.url + (fm.options.url.indexOf('?') === -1 ? '?' : '&')
  20786. + 'cmd=zipdl&download=1';
  20787. $.each([hashes[0], zipdl.file, dlName, zipdl.mime], function(key, val) {
  20788. url += '&targets%5B%5D='+encodeURIComponent(val);
  20789. });
  20790. $.each(fm.customData, function(key, val) {
  20791. url += '&'+encodeURIComponent(key)+'='+encodeURIComponent(val);
  20792. });
  20793. url += '&'+encodeURIComponent(dlName);
  20794. dllink = $('<a/>')
  20795. .attr('href', url)
  20796. .attr('download', fm.escape(dlName))
  20797. .on('click', function() {
  20798. dfd.resolve();
  20799. dialog && dialog.elfinderdialog('destroy');
  20800. });
  20801. if (linkdl) {
  20802. dllink.attr('target', '_blank')
  20803. .append('<span class="elfinder-button-icon elfinder-button-icon-download"></span>'+fm.escape(dlName));
  20804. btn[fm.i18n('btnCancel')] = function() {
  20805. dialog.elfinderdialog('destroy');
  20806. };
  20807. dialog = self.fmDialog(dllink, {
  20808. title: fm.i18n('link'),
  20809. buttons: btn,
  20810. width: '200px',
  20811. destroyOnClose: true,
  20812. close: function() {
  20813. (dfd.state() !== 'resolved') && dfd.resolve();
  20814. }
  20815. });
  20816. } else {
  20817. click(dllink.hide().appendTo('body').get(0));
  20818. dllink.remove();
  20819. }
  20820. } else {
  20821. form = $('<form action="'+fm.options.url+'" method="post" target="'+uniq+'" style="display:none"/>')
  20822. .append('<input type="hidden" name="cmd" value="zipdl"/>')
  20823. .append('<input type="hidden" name="download" value="1"/>');
  20824. $.each([hashes[0], zipdl.file, dlName, zipdl.mime], function(key, val) {
  20825. form.append('<input type="hidden" name="targets[]" value="'+fm.escape(val)+'"/>');
  20826. });
  20827. $.each(fm.customData, function(key, val) {
  20828. form.append('<input type="hidden" name="'+key+'" value="'+fm.escape(val)+'"/>');
  20829. });
  20830. form.attr('target', uniq).appendTo('body');
  20831. iframe = $('<iframe style="display:none" name="'+uniq+'">')
  20832. .appendTo('body')
  20833. .ready(function() {
  20834. form.submit().remove();
  20835. dfd.resolve();
  20836. setTimeout(function() {
  20837. iframe.remove();
  20838. }, 20000); // give 20 sec file to be saved
  20839. });
  20840. }
  20841. }
  20842. }).fail(function(error) {
  20843. error && fm.error(error);
  20844. dfd.resolve();
  20845. });
  20846. return dfd.promise();
  20847. };
  20848. },
  20849. // use MouseEvent to click element for Safari etc
  20850. click = function(a) {
  20851. var clickEv;
  20852. if (typeof MouseEvent === 'function') {
  20853. clickEv = new MouseEvent('click');
  20854. } else {
  20855. clickEv = document.createEvent('MouseEvents');
  20856. clickEv.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  20857. }
  20858. a.dispatchEvent(clickEv);
  20859. },
  20860. checkCookie = function(id) {
  20861. var name = 'elfdl' + id,
  20862. parts;
  20863. parts = document.cookie.split(name + "=");
  20864. if (parts.length === 2) {
  20865. ntftm && clearTimeout(ntftm);
  20866. document.cookie = name + '=; path=' + cpath + '; max-age=0';
  20867. closeNotify();
  20868. } else {
  20869. setTimeout(function() { checkCookie(id); }, 200);
  20870. }
  20871. },
  20872. closeNotify = function() {
  20873. if (fm.ui.notify.children('.elfinder-notify-download').length) {
  20874. fm.notify({
  20875. type : 'download',
  20876. cnt : -1
  20877. });
  20878. }
  20879. },
  20880. reqids = [],
  20881. link, html5dl, fileCnt, clickEv, cid, ntftm, reqid;
  20882. if (!files.length) {
  20883. return dfrd.reject();
  20884. }
  20885. fileCnt = $.grep(files, function(f) { return f.mime === 'directory'? false : true; }).length;
  20886. link = $('<a>').hide().appendTo('body');
  20887. html5dl = (typeof link.get(0).download === 'string');
  20888. if (zipOn && (fileCnt !== files.length || fileCnt >= (this.options.minFilesZipdl || 1))) {
  20889. link.remove();
  20890. linkdl = (!html5dl && fm.UA.Mobile);
  20891. if (mixed) {
  20892. targets = {};
  20893. $.each(files, function(i, f) {
  20894. var p = f.hash.split('_', 2);
  20895. if (! targets[p[0]]) {
  20896. targets[p[0]] = [ f.hash ];
  20897. } else {
  20898. targets[p[0]].push(f.hash);
  20899. }
  20900. });
  20901. if (!linkdl && fm.UA.Mobile && Object.keys(targets).length > 1) {
  20902. linkdl = true;
  20903. }
  20904. } else {
  20905. targets = [ $.map(files, function(f) { return f.hash; }) ];
  20906. }
  20907. dfrd = fm.sequence($.map(targets, function(t) { return getTask(t); })).always(
  20908. function() {
  20909. fm.trigger('download', {files : files});
  20910. }
  20911. );
  20912. return dfrd;
  20913. } else {
  20914. reqids = [];
  20915. for (i = 0; i < files.length; i++) {
  20916. url = fm.openUrl(files[i].hash, true);
  20917. if (dlntf && url.substr(0, fm.options.url.length) === fm.options.url) {
  20918. reqid = fm.getRequestId();
  20919. reqids.push(reqid);
  20920. url += '&cpath=' + cpath + '&reqid=' + reqid;
  20921. ntftm = setTimeout(function() {
  20922. fm.notify({
  20923. type : 'download',
  20924. cnt : 1,
  20925. cancel : (fm.UA.IE || fm.UA.Edge)? void(0) : function() {
  20926. if (reqids.length) {
  20927. $.each(reqids, function() {
  20928. fm.request({
  20929. data: {
  20930. cmd: 'abort',
  20931. id: this
  20932. },
  20933. preventDefault: true
  20934. });
  20935. });
  20936. }
  20937. reqids = [];
  20938. }
  20939. });
  20940. }, fm.notifyDelay);
  20941. checkCookie(reqid);
  20942. }
  20943. if (html5dl && (!fm.UA.Safari || fm.isSameOrigin(url))) {
  20944. click(link.attr('href', url)
  20945. .attr('download', fm.escape(files[i].name))
  20946. .get(0)
  20947. );
  20948. } else {
  20949. if (fm.UA.Mobile) {
  20950. setTimeout(function(){
  20951. if (! window.open(url)) {
  20952. fm.error('errPopup');
  20953. ntftm && cleaerTimeout(ntftm);
  20954. closeNotify();
  20955. }
  20956. }, 100);
  20957. } else {
  20958. iframes += '<iframe class="downloader" id="downloader-' + files[i].hash+'" style="display:none" src="'+url+'"/>';
  20959. }
  20960. }
  20961. }
  20962. link.remove();
  20963. $(iframes)
  20964. .appendTo('body')
  20965. .ready(function() {
  20966. setTimeout(function() {
  20967. $(iframes).each(function() {
  20968. $('#' + $(this).attr('id')).remove();
  20969. });
  20970. }, 20000 + (10000 * i)); // give 20 sec + 10 sec for each file to be saved
  20971. });
  20972. fm.trigger('download', {files : files});
  20973. return dfrd.resolve();
  20974. }
  20975. };
  20976. };
  20977. /*
  20978. * File: /js/commands/duplicate.js
  20979. */
  20980. /**
  20981. * @class elFinder command "duplicate"
  20982. * Create file/folder copy with suffix "copy Number"
  20983. *
  20984. * @type elFinder.command
  20985. * @author Dmitry (dio) Levashov
  20986. */
  20987. elFinder.prototype.commands.duplicate = function() {
  20988. var fm = this.fm;
  20989. this.getstate = function(select) {
  20990. var sel = this.files(select),
  20991. cnt = sel.length;
  20992. 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;
  20993. };
  20994. this.exec = function(hashes) {
  20995. var fm = this.fm,
  20996. files = this.files(hashes),
  20997. cnt = files.length,
  20998. dfrd = $.Deferred()
  20999. .fail(function(error) {
  21000. error && fm.error(error);
  21001. }),
  21002. args = [];
  21003. if (! cnt) {
  21004. return dfrd.reject();
  21005. }
  21006. $.each(files, function(i, file) {
  21007. if (!file.read || !fm.file(file.phash).write) {
  21008. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  21009. }
  21010. });
  21011. if (dfrd.state() == 'rejected') {
  21012. return dfrd;
  21013. }
  21014. return fm.request({
  21015. data : {cmd : 'duplicate', targets : this.hashes(hashes)},
  21016. notify : {type : 'copy', cnt : cnt},
  21017. navigate : {
  21018. toast : {
  21019. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdduplicate')])}
  21020. }
  21021. }
  21022. });
  21023. };
  21024. };
  21025. /*
  21026. * File: /js/commands/edit.js
  21027. */
  21028. /**
  21029. * @class elFinder command "edit".
  21030. * Edit text file in dialog window
  21031. *
  21032. * @author Dmitry (dio) Levashov, dio@std42.ru
  21033. **/
  21034. elFinder.prototype.commands.edit = function() {
  21035. var self = this,
  21036. fm = this.fm,
  21037. clsEditing = fm.res('class', 'editing'),
  21038. mimesSingle = [],
  21039. mimes = [],
  21040. allowAll = false,
  21041. rtrim = function(str){
  21042. return str.replace(/\s+$/, '');
  21043. },
  21044. getEncSelect = function(heads) {
  21045. var sel = $('<select class="ui-corner-all"/>'),
  21046. hval;
  21047. if (heads) {
  21048. $.each(heads, function(i, head) {
  21049. hval = fm.escape(head.value);
  21050. sel.append('<option value="'+hval+'">'+(head.caption? fm.escape(head.caption) : hval)+'</option>');
  21051. });
  21052. }
  21053. $.each(self.options.encodings, function(i, v) {
  21054. sel.append('<option value="'+v+'">'+v+'</option>');
  21055. });
  21056. return sel;
  21057. },
  21058. getDlgWidth = function() {
  21059. var m, width;
  21060. if (typeof self.options.dialogWidth === 'string' && (m = self.options.dialogWidth.match(/(\d+)%/))) {
  21061. width = parseInt(fm.getUI().width() * (m[1] / 100));
  21062. } else {
  21063. width = parseInt(self.options.dialogWidth || 650);
  21064. }
  21065. return Math.min(width, $(window).width());
  21066. },
  21067. /**
  21068. * Return files acceptable to edit
  21069. *
  21070. * @param Array files hashes
  21071. * @return Array
  21072. **/
  21073. filter = function(files) {
  21074. var cnt = files.length,
  21075. mime, ext, skip;
  21076. if (cnt > 1) {
  21077. mime = files[0].mime;
  21078. ext = files[0].name.replace(/^.*(\.[^.]+)$/, '$1');
  21079. }
  21080. return $.grep(files, function(file) {
  21081. var res;
  21082. if (skip || file.mime === 'directory') {
  21083. return false;
  21084. }
  21085. res = file.read
  21086. && (allowAll || fm.mimeIsText(file.mime) || $.inArray(file.mime, cnt === 1? mimesSingle : mimes) !== -1)
  21087. && (!self.onlyMimes.length || $.inArray(file.mime, self.onlyMimes) !== -1)
  21088. && (cnt === 1 || (file.mime === mime && file.name.substr(ext.length * -1) === ext))
  21089. && (fm.uploadMimeCheck(file.mime, file.phash)? true : false)
  21090. && setEditors(file, cnt)
  21091. && Object.keys(editors).length;
  21092. if (!res) {
  21093. skip = true;
  21094. }
  21095. return res;
  21096. });
  21097. },
  21098. fileSync = function(hash) {
  21099. var old = fm.file(hash),
  21100. f;
  21101. fm.request({
  21102. cmd: 'info',
  21103. targets: [hash],
  21104. preventDefault: true
  21105. }).done(function(data) {
  21106. var changed;
  21107. if (data && data.files && data.files.length) {
  21108. f = data.files[0];
  21109. if (old.ts != f.ts || old.size != f.size) {
  21110. changed = { changed: [ f ] };
  21111. fm.updateCache(changed);
  21112. fm.change(changed);
  21113. }
  21114. }
  21115. });
  21116. },
  21117. /**
  21118. * Open dialog with textarea to edit file
  21119. *
  21120. * @param String id dialog id
  21121. * @param Object file file object
  21122. * @param String content file content
  21123. * @return $.Deferred
  21124. **/
  21125. dialog = function(id, file, content, encoding, editor) {
  21126. var dfrd = $.Deferred(),
  21127. _loaded = false,
  21128. loaded = function() {
  21129. if (!_loaded) {
  21130. fm.toast({
  21131. mode: 'warning',
  21132. msg: fm.i18n('nowLoading')
  21133. });
  21134. return false;
  21135. }
  21136. return true;
  21137. },
  21138. save = function() {
  21139. var encord = selEncoding? selEncoding.val():void(0),
  21140. saveDfd = $.Deferred().fail(function(err) {
  21141. dialogNode.show().find('button.elfinder-btncnt-0,button.elfinder-btncnt-1').hide();
  21142. }),
  21143. conf, res;
  21144. if (!loaded()) {
  21145. return saveDfd.resolve();
  21146. }
  21147. if (ta.editor) {
  21148. ta.editor.save(ta[0], ta.editor.instance);
  21149. conf = ta.editor.confObj;
  21150. if (conf.info && (conf.info.schemeContent || conf.info.arrayBufferContent)) {
  21151. encord = 'scheme';
  21152. }
  21153. }
  21154. res = getContent();
  21155. setOld(res);
  21156. if (res.promise) {
  21157. res.done(function(data) {
  21158. dfrd.notifyWith(ta, [encord, ta.data('hash'), old, saveDfd]);
  21159. }).fail(function(err) {
  21160. saveDfd.reject(err);
  21161. });
  21162. } else {
  21163. dfrd.notifyWith(ta, [encord, ta.data('hash'), old, saveDfd]);
  21164. }
  21165. return saveDfd;
  21166. },
  21167. saveon = function() {
  21168. if (!loaded()) { return; }
  21169. save().fail(function(err) {
  21170. err && fm.error(err);
  21171. });
  21172. },
  21173. cancel = function() {
  21174. ta.elfinderdialog('close');
  21175. },
  21176. savecl = function() {
  21177. if (!loaded()) { return; }
  21178. save().done(function() {
  21179. _loaded = false;
  21180. dialogNode.show();
  21181. cancel();
  21182. }).fail(function(err) {
  21183. dialogNode.show();
  21184. err && fm.error(err);
  21185. });
  21186. dialogNode.hide();
  21187. },
  21188. saveAs = function() {
  21189. if (!loaded()) { return; }
  21190. var prevOld = old,
  21191. phash = fm.file(file.phash)? file.phash : fm.cwd().hash,
  21192. fail = function(err) {
  21193. dialogs.addClass(clsEditing).fadeIn(function() {
  21194. err && fm.error(err);
  21195. });
  21196. old = prevOld;
  21197. fm.disable();
  21198. },
  21199. make = function() {
  21200. self.mime = saveAsFile.mime || file.mime;
  21201. self.prefix = (saveAsFile.name || file.name).replace(/ \d+(\.[^.]+)?$/, '$1');
  21202. self.requestCmd = 'mkfile';
  21203. self.nextAction = {};
  21204. self.data = {target : phash};
  21205. $.proxy(fm.res('mixin', 'make'), self)()
  21206. .done(function(data) {
  21207. if (data.added && data.added.length) {
  21208. ta.data('hash', data.added[0].hash);
  21209. save().done(function() {
  21210. _loaded = false;
  21211. dialogNode.show();
  21212. cancel();
  21213. dialogs.fadeIn();
  21214. }).fail(fail);
  21215. } else {
  21216. fail();
  21217. }
  21218. })
  21219. .progress(function(err) {
  21220. if (err && err === 'errUploadMime') {
  21221. ta.trigger('saveAsFail');
  21222. }
  21223. })
  21224. .fail(fail)
  21225. .always(function() {
  21226. delete self.mime;
  21227. delete self.prefix;
  21228. delete self.nextAction;
  21229. delete self.data;
  21230. });
  21231. fm.trigger('unselectfiles', { files: [ file.hash ] });
  21232. },
  21233. reqOpen = null,
  21234. dialogs = fm.getUI().children('.' + self.dialogClass + ':visible');
  21235. if (dialogNode.is(':hidden')) {
  21236. dialogs = dialogs.add(dialogNode);
  21237. }
  21238. dialogs.removeClass(clsEditing).fadeOut();
  21239. fm.enable();
  21240. if (fm.searchStatus.state < 2 && phash !== fm.cwd().hash) {
  21241. reqOpen = fm.exec('open', [phash], {thash: phash});
  21242. }
  21243. $.when([reqOpen]).done(function() {
  21244. reqOpen? fm.one('cwdrender', make) : make();
  21245. }).fail(fail);
  21246. },
  21247. changed = function() {
  21248. var dfd = $.Deferred(),
  21249. res, tm;
  21250. if (!_loaded) {
  21251. return dfd.resolve(false);
  21252. }
  21253. ta.editor && ta.editor.save(ta[0], ta.editor.instance);
  21254. res = getContent();
  21255. if (res && res.promise) {
  21256. tm = setTimeout(function() {
  21257. fm.notify({
  21258. type : 'chkcontent',
  21259. cnt : 1,
  21260. hideCnt: true
  21261. });
  21262. }, 100);
  21263. res.always(function() {
  21264. tm && clearTimeout(tm);
  21265. fm.notify({ type : 'chkcontent', cnt: -1 });
  21266. }).done(function(d) {
  21267. dfd.resolve(old !== d);
  21268. }).fail(function(err) {
  21269. dfd.resolve(err || true);
  21270. });
  21271. } else {
  21272. dfd.resolve(old !== res);
  21273. }
  21274. return dfd;
  21275. },
  21276. opts = {
  21277. title : fm.escape(file.name),
  21278. width : getDlgWidth(),
  21279. buttons : {},
  21280. cssClass : clsEditing,
  21281. maxWidth : 'window',
  21282. maxHeight : 'window',
  21283. allowMinimize : true,
  21284. allowMaximize : true,
  21285. openMaximized : editorMaximized() || (editor && editor.info && editor.info.openMaximized),
  21286. btnHoverFocus : false,
  21287. closeOnEscape : false,
  21288. propagationEvents : ['mousemove', 'mouseup', 'click'],
  21289. minimize : function() {
  21290. var conf;
  21291. if (ta.editor && dialogNode.closest('.ui-dialog').is(':hidden')) {
  21292. conf = ta.editor.confObj;
  21293. if (conf.info && conf.info.syncInterval) {
  21294. fileSync(file.hash);
  21295. }
  21296. }
  21297. },
  21298. close : function() {
  21299. var close = function() {
  21300. var conf;
  21301. dfrd.resolve();
  21302. if (ta.editor) {
  21303. ta.editor.close(ta[0], ta.editor.instance);
  21304. conf = ta.editor.confObj;
  21305. if (conf.info && conf.info.syncInterval) {
  21306. fileSync(file.hash);
  21307. }
  21308. }
  21309. ta.elfinderdialog('destroy');
  21310. },
  21311. onlySaveAs = (typeof saveAsFile.name !== 'undefined'),
  21312. accept = onlySaveAs? {
  21313. label : 'btnSaveAs',
  21314. callback : function() {
  21315. requestAnimationFrame(saveAs);
  21316. }
  21317. } : {
  21318. label : 'btnSaveClose',
  21319. callback : function() {
  21320. save().done(function() {
  21321. close();
  21322. });
  21323. }
  21324. };
  21325. changed().done(function(change) {
  21326. var msgs = ['confirmNotSave'];
  21327. if (change) {
  21328. if (typeof change === 'string') {
  21329. msgs.unshift(change);
  21330. }
  21331. fm.confirm({
  21332. title : self.title,
  21333. text : msgs,
  21334. accept : accept,
  21335. cancel : {
  21336. label : 'btnClose',
  21337. callback : close
  21338. },
  21339. buttons : onlySaveAs? null : [{
  21340. label : 'btnSaveAs',
  21341. callback : function() {
  21342. requestAnimationFrame(saveAs);
  21343. }
  21344. }]
  21345. });
  21346. } else {
  21347. close();
  21348. }
  21349. });
  21350. },
  21351. open : function() {
  21352. var loadRes, conf, interval;
  21353. ta.initEditArea.call(ta, id, file, content, fm);
  21354. if (ta.editor) {
  21355. loadRes = ta.editor.load(ta[0]) || null;
  21356. if (loadRes && loadRes.done) {
  21357. loadRes.always(function() {
  21358. _loaded = true;
  21359. }).done(function(instance) {
  21360. ta.editor.instance = instance;
  21361. ta.editor.focus(ta[0], ta.editor.instance);
  21362. setOld(getContent());
  21363. requestAnimationFrame(function() {
  21364. dialogNode.trigger('resize');
  21365. });
  21366. }).fail(function(error) {
  21367. error && fm.error(error);
  21368. ta.elfinderdialog('destroy');
  21369. return;
  21370. });
  21371. } else {
  21372. _loaded = true;
  21373. if (loadRes && (typeof loadRes === 'string' || Array.isArray(loadRes))) {
  21374. fm.error(loadRes);
  21375. ta.elfinderdialog('destroy');
  21376. return;
  21377. }
  21378. ta.editor.instance = loadRes;
  21379. ta.editor.focus(ta[0], ta.editor.instance);
  21380. setOld(getContent());
  21381. requestAnimationFrame(function() {
  21382. dialogNode.trigger('resize');
  21383. });
  21384. }
  21385. conf = ta.editor.confObj;
  21386. if (conf.info && conf.info.syncInterval) {
  21387. if (interval = parseInt(conf.info.syncInterval)) {
  21388. setTimeout(function() {
  21389. autoSync(interval);
  21390. }, interval);
  21391. }
  21392. }
  21393. } else {
  21394. _loaded = true;
  21395. setOld(getContent());
  21396. }
  21397. },
  21398. resize : function(e, data) {
  21399. ta.editor && ta.editor.resize(ta[0], ta.editor.instance, e, data || {});
  21400. }
  21401. },
  21402. getContent = function() {
  21403. return ta.getContent.call(ta, ta[0]);
  21404. },
  21405. setOld = function(res) {
  21406. if (res && res.promise) {
  21407. res.done(function(d) {
  21408. old = d;
  21409. });
  21410. } else {
  21411. old = res;
  21412. }
  21413. },
  21414. autoSync = function(interval) {
  21415. if (dialogNode.is(':visible')) {
  21416. fileSync(file.hash);
  21417. setTimeout(function() {
  21418. autoSync(interval);
  21419. }, interval);
  21420. }
  21421. },
  21422. saveAsFile = {},
  21423. ta, old, dialogNode, selEncoding, extEditor, maxW, syncInterval;
  21424. if (editor) {
  21425. if (editor.html) {
  21426. ta = $(editor.html);
  21427. }
  21428. extEditor = {
  21429. init : editor.init || null,
  21430. load : editor.load,
  21431. getContent : editor.getContent || null,
  21432. save : editor.save,
  21433. beforeclose : typeof editor.beforeclose == 'function' ? editor.beforeclose : void 0,
  21434. close : typeof editor.close == 'function' ? editor.close : function() {},
  21435. focus : typeof editor.focus == 'function' ? editor.focus : function() {},
  21436. resize : typeof editor.resize == 'function' ? editor.resize : function() {},
  21437. instance : null,
  21438. doSave : saveon,
  21439. doCancel : cancel,
  21440. doClose : savecl,
  21441. file : file,
  21442. fm : fm,
  21443. confObj : editor,
  21444. trigger : function(evName, data) {
  21445. fm.trigger('editEditor' + evName, Object.assign({}, editor.info || {}, data));
  21446. }
  21447. };
  21448. }
  21449. if (!ta) {
  21450. if (!fm.mimeIsText(file.mime)) {
  21451. return dfrd.reject('errEditorNotFound');
  21452. }
  21453. (function() {
  21454. var stateChange = function() {
  21455. if (selEncoding) {
  21456. changed().done(function(change) {
  21457. if (change) {
  21458. selEncoding.attr('title', fm.i18n('saveAsEncoding')).addClass('elfinder-edit-changed');
  21459. } else {
  21460. selEncoding.attr('title', fm.i18n('openAsEncoding')).removeClass('elfinder-edit-changed');
  21461. }
  21462. });
  21463. }
  21464. };
  21465. ta = $('<textarea class="elfinder-file-edit" rows="20" id="'+id+'-ta"></textarea>')
  21466. .on('input propertychange', stateChange);
  21467. if (!ta.editor || !ta.editor.info || ta.editor.info.useTextAreaEvent) {
  21468. ta.on('keydown', function(e) {
  21469. var code = e.keyCode,
  21470. value, start;
  21471. e.stopPropagation();
  21472. if (code == $.ui.keyCode.TAB) {
  21473. e.preventDefault();
  21474. // insert tab on tab press
  21475. if (this.setSelectionRange) {
  21476. value = this.value;
  21477. start = this.selectionStart;
  21478. this.value = value.substr(0, start) + "\t" + value.substr(this.selectionEnd);
  21479. start += 1;
  21480. this.setSelectionRange(start, start);
  21481. }
  21482. }
  21483. if (e.ctrlKey || e.metaKey) {
  21484. // close on ctrl+w/q
  21485. if (code == 'Q'.charCodeAt(0) || code == 'W'.charCodeAt(0)) {
  21486. e.preventDefault();
  21487. cancel();
  21488. }
  21489. if (code == 'S'.charCodeAt(0)) {
  21490. e.preventDefault();
  21491. saveon();
  21492. }
  21493. }
  21494. })
  21495. .on('mouseenter', function(){this.focus();});
  21496. }
  21497. ta.initEditArea = function(id, file, content) {
  21498. var heads = (encoding && encoding !== 'unknown')? [{value: encoding}] : [],
  21499. wfake = $('<select/>').hide(),
  21500. setSelW = function(init) {
  21501. init && wfake.appendTo(selEncoding.parent());
  21502. wfake.empty().append($('<option/>').text(selEncoding.val()));
  21503. selEncoding.width(wfake.width());
  21504. };
  21505. // ta.hide() for performance tune. Need ta.show() in `load()` if use textarea node.
  21506. ta.hide().val(content);
  21507. if (content === '' || ! encoding || encoding !== 'UTF-8') {
  21508. heads.push({value: 'UTF-8'});
  21509. }
  21510. selEncoding = getEncSelect(heads).on('touchstart', function(e) {
  21511. // for touch punch event handler
  21512. e.stopPropagation();
  21513. }).on('change', function() {
  21514. // reload to change encoding if not edited
  21515. changed().done(function(change) {
  21516. if (! change && getContent() !== '') {
  21517. cancel();
  21518. edit(file, selEncoding.val(), editor).fail(function(err) { err && fm.error(err); });
  21519. }
  21520. });
  21521. setSelW();
  21522. }).on('mouseover', stateChange);
  21523. ta.parent().next().prepend($('<div class="ui-dialog-buttonset elfinder-edit-extras"/>').append(selEncoding));
  21524. setSelW(true);
  21525. };
  21526. })();
  21527. }
  21528. ta.data('hash', file.hash);
  21529. if (extEditor) {
  21530. ta.editor = extEditor;
  21531. if (typeof extEditor.beforeclose === 'function') {
  21532. opts.beforeclose = function() {
  21533. return extEditor.beforeclose(ta[0], extEditor.instance);
  21534. };
  21535. }
  21536. if (typeof extEditor.init === 'function') {
  21537. ta.initEditArea = extEditor.init;
  21538. }
  21539. if (typeof extEditor.getContent === 'function') {
  21540. ta.getContent = extEditor.getContent;
  21541. }
  21542. }
  21543. if (! ta.initEditArea) {
  21544. ta.initEditArea = function() {};
  21545. }
  21546. if (! ta.getContent) {
  21547. ta.getContent = function() {
  21548. return rtrim(ta.val());
  21549. };
  21550. }
  21551. if (!editor || !editor.info || !editor.info.preventGet) {
  21552. opts.buttons[fm.i18n('btnSave')] = saveon;
  21553. opts.buttons[fm.i18n('btnSaveClose')] = savecl;
  21554. opts.buttons[fm.i18n('btnSaveAs')] = saveAs;
  21555. opts.buttons[fm.i18n('btnCancel')] = cancel;
  21556. }
  21557. if (editor && typeof editor.prepare === 'function') {
  21558. editor.prepare(ta, opts, file);
  21559. }
  21560. dialogNode = self.fmDialog(ta, opts)
  21561. .attr('id', id)
  21562. .on('keydown keyup keypress', function(e) {
  21563. e.stopPropagation();
  21564. })
  21565. .css({ overflow: 'hidden', minHeight: '7em' })
  21566. .addClass('elfinder-edit-editor')
  21567. .closest('.ui-dialog')
  21568. .on('changeType', function(e, data) {
  21569. if (data.extention && data.mime) {
  21570. var ext = data.extention,
  21571. mime = data.mime,
  21572. btnSet = $(this).children('.ui-dialog-buttonpane').children('.ui-dialog-buttonset');
  21573. btnSet.children('.elfinder-btncnt-0,.elfinder-btncnt-1').hide();
  21574. saveAsFile.name = fm.splitFileExtention(file.name)[0] + '.' + data.extention;
  21575. saveAsFile.mime = data.mime;
  21576. if (!data.keepEditor) {
  21577. btnSet.children('.elfinder-btncnt-2').trigger('click');
  21578. }
  21579. }
  21580. });
  21581. // care to viewport scale change with mobile devices
  21582. maxW = (fm.options.dialogContained? elfNode : $(window)).width();
  21583. (dialogNode.width() > maxW) && dialogNode.width(maxW);
  21584. return dfrd.promise();
  21585. },
  21586. /**
  21587. * Get file content and
  21588. * open dialog with textarea to edit file content
  21589. *
  21590. * @param String file hash
  21591. * @return jQuery.Deferred
  21592. **/
  21593. edit = function(file, convert, editor) {
  21594. var hash = file.hash,
  21595. opts = fm.options,
  21596. dfrd = $.Deferred(),
  21597. id = 'edit-'+fm.namespace+'-'+file.hash,
  21598. d = fm.getUI().find('#'+id),
  21599. conv = !convert? 0 : convert,
  21600. req, error, res;
  21601. if (d.length) {
  21602. d.elfinderdialog('toTop');
  21603. return dfrd.resolve();
  21604. }
  21605. if (!file.read || (!file.write && (!editor.info || !editor.info.converter))) {
  21606. error = ['errOpen', file.name, 'errPerm'];
  21607. return dfrd.reject(error);
  21608. }
  21609. if (editor && editor.info) {
  21610. if (typeof editor.info.edit === 'function') {
  21611. res = editor.info.edit.call(fm, file, editor);
  21612. if (res.promise) {
  21613. res.done(function() {
  21614. dfrd.resolve();
  21615. }).fail(function(error) {
  21616. dfrd.reject(error);
  21617. });
  21618. } else {
  21619. res? dfrd.resolve() : dfrd.reject();
  21620. }
  21621. return dfrd;
  21622. }
  21623. if (editor.info.urlAsContent || editor.info.preventGet) {
  21624. req = $.Deferred();
  21625. if (! editor.info.preventGet) {
  21626. fm.url(hash, { async: true, temporary: true }).done(function(url) {
  21627. req.resolve({content: url});
  21628. });
  21629. } else {
  21630. req.resolve({});
  21631. }
  21632. } else {
  21633. req = fm.request({
  21634. data : {cmd : 'get', target : hash, conv : conv, _t : file.ts},
  21635. options : {type: 'get', cache : true},
  21636. notify : {type : 'file', cnt : 1},
  21637. preventDefault : true
  21638. });
  21639. }
  21640. req.done(function(data) {
  21641. var selEncoding, reg, m, res;
  21642. if (data.doconv) {
  21643. fm.confirm({
  21644. title : self.title,
  21645. text : data.doconv === 'unknown'? 'confirmNonUTF8' : 'confirmConvUTF8',
  21646. accept : {
  21647. label : 'btnConv',
  21648. callback : function() {
  21649. dfrd = edit(file, selEncoding.val(), editor);
  21650. }
  21651. },
  21652. cancel : {
  21653. label : 'btnCancel',
  21654. callback : function() { dfrd.reject(); }
  21655. },
  21656. optionsCallback : function(options) {
  21657. options.create = function() {
  21658. var base = $('<div class="elfinder-dialog-confirm-encoding"/>'),
  21659. head = {value: data.doconv},
  21660. detected;
  21661. if (data.doconv === 'unknown') {
  21662. head.caption = '-';
  21663. }
  21664. selEncoding = getEncSelect([head]);
  21665. $(this).next().find('.ui-dialog-buttonset')
  21666. .prepend(base.append($('<label>'+fm.i18n('encoding')+' </label>').append(selEncoding)));
  21667. };
  21668. }
  21669. });
  21670. } else {
  21671. if ((!editor || !editor.info || !editor.info.preventGet) && fm.mimeIsText(file.mime)) {
  21672. reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i');
  21673. if (!editor.info.dataScheme) {
  21674. if (window.atob && (m = data.content.match(reg))) {
  21675. data.content = atob(data.content.substr(m[1].length));
  21676. }
  21677. } else {
  21678. if (window.btoa && !data.content.match(reg)) {
  21679. data.content = 'data:'+file.mime+';base64,'+btoa(data.content);
  21680. }
  21681. }
  21682. }
  21683. dialog(id, file, data.content, data.encoding, editor)
  21684. .done(function(data) {
  21685. dfrd.resolve(data);
  21686. })
  21687. .progress(function(encoding, newHash, data, saveDfd) {
  21688. var ta = this;
  21689. if (newHash) {
  21690. hash = newHash;
  21691. }
  21692. fm.request({
  21693. options : {type : 'post'},
  21694. data : {
  21695. cmd : 'put',
  21696. target : hash,
  21697. encoding : encoding || data.encoding,
  21698. content : data
  21699. },
  21700. notify : {type : 'save', cnt : 1},
  21701. syncOnFail : true,
  21702. preventFail : true,
  21703. navigate : {
  21704. target : 'changed',
  21705. toast : {
  21706. inbuffer : {msg: fm.i18n(['complete', fm.i18n('btnSave')])}
  21707. }
  21708. }
  21709. })
  21710. .fail(function(error) {
  21711. dfrd.reject(error);
  21712. saveDfd.reject();
  21713. })
  21714. .done(function(data) {
  21715. requestAnimationFrame(function(){
  21716. ta.trigger('focus');
  21717. ta.editor && ta.editor.focus(ta[0], ta.editor.instance);
  21718. });
  21719. saveDfd.resolve();
  21720. });
  21721. })
  21722. .fail(function(error) {
  21723. dfrd.reject(error);
  21724. });
  21725. }
  21726. })
  21727. .fail(function(error) {
  21728. var err = fm.parseError(error);
  21729. err = Array.isArray(err)? err[0] : err;
  21730. (err !== 'errConvUTF8') && fm.sync();
  21731. dfrd.reject(error);
  21732. });
  21733. }
  21734. return dfrd.promise();
  21735. },
  21736. /**
  21737. * Current editors of selected files
  21738. *
  21739. * @type Object
  21740. */
  21741. editors = {},
  21742. /**
  21743. * Fallback editor (Simple text editor)
  21744. *
  21745. * @type Object
  21746. */
  21747. fallbackEditor = {
  21748. // Simple Text (basic textarea editor)
  21749. info : {
  21750. id : 'textarea',
  21751. name : 'TextArea',
  21752. useTextAreaEvent : true
  21753. },
  21754. load : function(textarea) {
  21755. // trigger event 'editEditorPrepare'
  21756. this.trigger('Prepare', {
  21757. node: textarea,
  21758. editorObj: void(0),
  21759. instance: void(0),
  21760. opts: {}
  21761. });
  21762. textarea.setSelectionRange && textarea.setSelectionRange(0, 0);
  21763. $(textarea).trigger('focus').show();
  21764. },
  21765. save : function(){}
  21766. },
  21767. /**
  21768. * Set current editors
  21769. *
  21770. * @param Object file object
  21771. * @param Number cnt count of selected items
  21772. * @return Void
  21773. */
  21774. setEditors = function(file, cnt) {
  21775. var mimeMatch = function(fileMime, editorMimes){
  21776. if (!editorMimes) {
  21777. return fm.mimeIsText(fileMime);
  21778. } else {
  21779. if (editorMimes[0] === '*' || $.inArray(fileMime, editorMimes) !== -1) {
  21780. return true;
  21781. }
  21782. var i, l;
  21783. l = editorMimes.length;
  21784. for (i = 0; i < l; i++) {
  21785. if (fileMime.indexOf(editorMimes[i]) === 0) {
  21786. return true;
  21787. }
  21788. }
  21789. return false;
  21790. }
  21791. },
  21792. extMatch = function(fileName, editorExts){
  21793. if (!editorExts || !editorExts.length) {
  21794. return true;
  21795. }
  21796. var ext = fileName.replace(/^.+\.([^.]+)|(.+)$/, '$1$2').toLowerCase(),
  21797. i, l;
  21798. l = editorExts.length;
  21799. for (i = 0; i < l; i++) {
  21800. if (ext === editorExts[i].toLowerCase()) {
  21801. return true;
  21802. }
  21803. }
  21804. return false;
  21805. },
  21806. optEditors = self.options.editors || [],
  21807. cwdWrite = fm.cwd().write;
  21808. stored = fm.storage('storedEditors') || {};
  21809. editors = {};
  21810. if (!optEditors.length) {
  21811. optEditors = [fallbackEditor];
  21812. }
  21813. $.each(optEditors, function(i, editor) {
  21814. var name;
  21815. if ((cnt === 1 || !editor.info.single)
  21816. && ((!editor.info || !editor.info.converter)? file.write : cwdWrite)
  21817. && (file.size > 0 || (!editor.info.converter && (editor.info.canMakeEmpty || (editor.info.canMakeEmpty !== false && fm.mimeIsText(file.mime)))))
  21818. && (!editor.info.maxSize || file.size <= editor.info.maxSize)
  21819. && mimeMatch(file.mime, editor.mimes || null)
  21820. && extMatch(file.name, editor.exts || null)
  21821. && typeof editor.load == 'function'
  21822. && typeof editor.save == 'function') {
  21823. name = editor.info.name? editor.info.name : ('Editor ' + i);
  21824. editor.id = editor.info.id? editor.info.id : ('editor' + i),
  21825. editor.name = name;
  21826. editor.i18n = fm.i18n(name);
  21827. editors[editor.id] = editor;
  21828. }
  21829. });
  21830. return Object.keys(editors).length? true : false;
  21831. },
  21832. store = function(mime, editor) {
  21833. if (mime && editor) {
  21834. if (!$.isPlainObject(stored)) {
  21835. stored = {};
  21836. }
  21837. stored[mime] = editor.id;
  21838. fm.storage('storedEditors', stored);
  21839. fm.trigger('selectfiles', {files : fm.selected()});
  21840. }
  21841. },
  21842. useStoredEditor = function() {
  21843. var d = fm.storage('useStoredEditor');
  21844. return d? (d > 0) : self.options.useStoredEditor;
  21845. },
  21846. editorMaximized = function() {
  21847. var d = fm.storage('editorMaximized');
  21848. return d? (d > 0) : self.options.editorMaximized;
  21849. },
  21850. getSubMenuRaw = function(files, callback) {
  21851. var subMenuRaw = [];
  21852. $.each(editors, function(id, ed) {
  21853. subMenuRaw.push(
  21854. {
  21855. label : fm.escape(ed.i18n),
  21856. icon : ed.info && ed.info.icon? ed.info.icon : 'edit',
  21857. options : { iconImg: ed.info && ed.info.iconImg? fm.baseUrl + ed.info.iconImg : void(0) },
  21858. callback : function() {
  21859. store(files[0].mime, ed);
  21860. callback && callback.call(ed);
  21861. }
  21862. }
  21863. );
  21864. });
  21865. return subMenuRaw;
  21866. },
  21867. getStoreId = function(name) {
  21868. // for compatibility to previous version
  21869. return name.toLowerCase().replace(/ +/g, '');
  21870. },
  21871. getStoredEditor = function(mime) {
  21872. var name = stored[mime];
  21873. return name && Object.keys(editors).length? editors[getStoreId(name)] : void(0);
  21874. },
  21875. infoRequest = function() {
  21876. },
  21877. stored;
  21878. this.shortcuts = [{
  21879. pattern : 'ctrl+e'
  21880. }];
  21881. this.init = function() {
  21882. var self = this,
  21883. fm = this.fm,
  21884. opts = this.options,
  21885. cmdChecks = [],
  21886. ccData, dfd;
  21887. this.onlyMimes = this.options.mimes || [];
  21888. fm.one('open', function() {
  21889. // editors setup
  21890. if (opts.editors && Array.isArray(opts.editors)) {
  21891. fm.trigger('canMakeEmptyFile', {mimes: Object.keys(fm.storage('mkfileTextMimes') || {}).concat(opts.makeTextMimes || ['text/plain'])});
  21892. $.each(opts.editors, function(i, editor) {
  21893. if (editor.info && editor.info.cmdCheck) {
  21894. cmdChecks.push(editor.info.cmdCheck);
  21895. }
  21896. });
  21897. if (cmdChecks.length) {
  21898. if (fm.api >= 2.1030) {
  21899. dfd = fm.request({
  21900. data : {
  21901. cmd: 'editor',
  21902. name: cmdChecks,
  21903. method: 'enabled'
  21904. },
  21905. preventDefault : true
  21906. }).done(function(d) {
  21907. ccData = d;
  21908. }).fail(function() {
  21909. ccData = {};
  21910. });
  21911. } else {
  21912. ccData = {};
  21913. dfd = $.Deferred().resolve();
  21914. }
  21915. } else {
  21916. dfd = $.Deferred().resolve();
  21917. }
  21918. dfd.always(function() {
  21919. if (ccData) {
  21920. opts.editors = $.grep(opts.editors, function(e) {
  21921. if (e.info && e.info.cmdCheck) {
  21922. return ccData[e.info.cmdCheck]? true : false;
  21923. } else {
  21924. return true;
  21925. }
  21926. });
  21927. }
  21928. $.each(opts.editors, function(i, editor) {
  21929. if (editor.setup && typeof editor.setup === 'function') {
  21930. editor.setup.call(editor, opts, fm);
  21931. }
  21932. if (!editor.disabled) {
  21933. if (editor.mimes && Array.isArray(editor.mimes)) {
  21934. mimesSingle = mimesSingle.concat(editor.mimes);
  21935. if (!editor.info || !editor.info.single) {
  21936. mimes = mimes.concat(editor.mimes);
  21937. }
  21938. }
  21939. if (!allowAll && editor.mimes && editor.mimes[0] === '*') {
  21940. allowAll = true;
  21941. }
  21942. if (!editor.info) {
  21943. editor.info = {};
  21944. }
  21945. if (editor.info.integrate) {
  21946. fm.trigger('helpIntegration', Object.assign({cmd: 'edit'}, editor.info.integrate));
  21947. }
  21948. if (editor.info.canMakeEmpty) {
  21949. fm.trigger('canMakeEmptyFile', {mimes: editor.mimes});
  21950. }
  21951. }
  21952. });
  21953. mimesSingle = ($.uniqueSort || $.unique)(mimesSingle);
  21954. mimes = ($.uniqueSort || $.unique)(mimes);
  21955. opts.editors = $.grep(opts.editors, function(e) {
  21956. return e.disabled? false : true;
  21957. });
  21958. });
  21959. }
  21960. })
  21961. .bind('select', function() {
  21962. editors = null;
  21963. })
  21964. .bind('contextmenucreate', function(e) {
  21965. var file, editor,
  21966. single = function(editor) {
  21967. var title = self.title;
  21968. fm.one('contextmenucreatedone', function() {
  21969. self.title = title;
  21970. });
  21971. self.title = fm.escape(editor.i18n);
  21972. if (editor.info && editor.info.iconImg) {
  21973. self.contextmenuOpts = {
  21974. iconImg: fm.baseUrl + editor.info.iconImg
  21975. };
  21976. }
  21977. delete self.variants;
  21978. };
  21979. self.contextmenuOpts = void(0);
  21980. if (e.data.type === 'files' && self.enabled()) {
  21981. file = fm.file(e.data.targets[0]);
  21982. if (setEditors(file, e.data.targets.length)) {
  21983. if (Object.keys(editors).length > 1) {
  21984. if (!useStoredEditor() || !(editor = getStoredEditor(file.mime))) {
  21985. delete self.extra;
  21986. self.variants = [];
  21987. $.each(editors, function(id, editor) {
  21988. self.variants.push([{ editor: editor }, editor.i18n, editor.info && editor.info.iconImg? fm.baseUrl + editor.info.iconImg : 'edit']);
  21989. });
  21990. } else {
  21991. single(editor);
  21992. self.extra = {
  21993. icon: 'menu',
  21994. node: $('<span/>')
  21995. .attr({title: fm.i18n('select')})
  21996. .on('click touchstart', function(e){
  21997. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  21998. return;
  21999. }
  22000. var node = $(this);
  22001. e.stopPropagation();
  22002. e.preventDefault();
  22003. fm.trigger('contextmenu', {
  22004. raw: getSubMenuRaw(fm.selectedFiles(), function() {
  22005. var hashes = fm.selected();
  22006. fm.exec('edit', hashes, {editor: this});
  22007. fm.trigger('selectfiles', {files : hashes});
  22008. }),
  22009. x: node.offset().left,
  22010. y: node.offset().top
  22011. });
  22012. })
  22013. };
  22014. }
  22015. } else {
  22016. single(editors[Object.keys(editors)[0]]);
  22017. delete self.extra;
  22018. }
  22019. }
  22020. }
  22021. })
  22022. .bind('canMakeEmptyFile', function(e) {
  22023. if (e.data && e.data.resetTexts) {
  22024. var defs = fm.arrayFlip(self.options.makeTextMimes || ['text/plain']),
  22025. hides = fm.storage('mkfileHides') || {};
  22026. $.each((fm.storage('mkfileTextMimes') || {}), function(mime, type) {
  22027. if (!defs[mime]) {
  22028. delete fm.mimesCanMakeEmpty[mime];
  22029. delete hides[mime];
  22030. }
  22031. });
  22032. fm.storage('mkfileTextMimes', null);
  22033. if (Object.keys(hides).length) {
  22034. fm.storage('mkfileHides', hides);
  22035. } else {
  22036. fm.storage('mkfileHides', null);
  22037. }
  22038. }
  22039. });
  22040. };
  22041. this.getstate = function(select) {
  22042. var sel = this.files(select),
  22043. cnt = sel.length;
  22044. return cnt && filter(sel).length == cnt ? 0 : -1;
  22045. };
  22046. this.exec = function(select, opts) {
  22047. var fm = this.fm,
  22048. files = filter(this.files(select)),
  22049. hashes = $.map(files, function(f) { return f.hash; }),
  22050. list = [],
  22051. editor = opts && opts.editor? opts.editor : null,
  22052. node = $(opts && opts._currentNode? opts._currentNode : $('#'+ fm.cwdHash2Id(hashes[0]))),
  22053. getEditor = function() {
  22054. var dfd = $.Deferred(),
  22055. storedId;
  22056. if (!editor && Object.keys(editors).length > 1) {
  22057. if (useStoredEditor() && (editor = getStoredEditor(files[0].mime))) {
  22058. return dfd.resolve(editor);
  22059. }
  22060. fm.trigger('contextmenu', {
  22061. raw: getSubMenuRaw(files, function() {
  22062. dfd.resolve(this);
  22063. }),
  22064. x: node.offset().left,
  22065. y: node.offset().top + 22,
  22066. opened: function() {
  22067. fm.one('closecontextmenu',function() {
  22068. requestAnimationFrame(function() {
  22069. if (dfd.state() === 'pending') {
  22070. dfd.reject();
  22071. }
  22072. });
  22073. });
  22074. }
  22075. });
  22076. fm.trigger('selectfiles', {files : hashes});
  22077. return dfd;
  22078. } else {
  22079. Object.keys(editors).length > 1 && editor && store(files[0].mime, editor);
  22080. return dfd.resolve(editor? editor : (Object.keys(editors).length? editors[Object.keys(editors)[0]] : null));
  22081. }
  22082. },
  22083. dfrd = $.Deferred(),
  22084. file;
  22085. if (editors === null) {
  22086. setEditors(files[0], hashes.length);
  22087. }
  22088. if (!node.length) {
  22089. node = fm.getUI('cwd');
  22090. }
  22091. getEditor().done(function(editor) {
  22092. while ((file = files.shift())) {
  22093. list.push(edit(file, void(0), editor).fail(function(error) {
  22094. error && fm.error(error);
  22095. }));
  22096. }
  22097. if (list.length) {
  22098. $.when.apply(null, list).done(function() {
  22099. dfrd.resolve();
  22100. }).fail(function() {
  22101. dfrd.reject();
  22102. });
  22103. } else {
  22104. dfrd.reject();
  22105. }
  22106. }).fail(function() {
  22107. dfrd.reject();
  22108. });
  22109. return dfrd;
  22110. };
  22111. };
  22112. /*
  22113. * File: /js/commands/empty.js
  22114. */
  22115. /**
  22116. * @class elFinder command "empty".
  22117. * Empty the folder
  22118. *
  22119. * @type elFinder.command
  22120. * @author Naoki Sawada
  22121. */
  22122. elFinder.prototype.commands.empty = function() {
  22123. var self, fm,
  22124. selFiles = function(select) {
  22125. var sel = self.files(select);
  22126. if (!sel.length) {
  22127. sel = [ fm.cwd() ];
  22128. }
  22129. return sel;
  22130. };
  22131. this.linkedCmds = ['rm'];
  22132. this.init = function() {
  22133. // lazy assign to make possible to become superclass
  22134. self = this;
  22135. fm = this.fm;
  22136. };
  22137. this.getstate = function(select) {
  22138. var sel = selFiles(select),
  22139. cnt;
  22140. cnt = sel.length;
  22141. return $.grep(sel, function(f) { return f.read && f.write && f.mime === 'directory' ? true : false; }).length == cnt ? 0 : -1;
  22142. };
  22143. this.exec = function(hashes) {
  22144. var dirs = selFiles(hashes),
  22145. cnt = dirs.length,
  22146. dfrd = $.Deferred()
  22147. .done(function() {
  22148. var data = {changed: {}};
  22149. fm.toast({msg: fm.i18n(['"'+success.join('", ')+'"', 'complete', fm.i18n('cmdempty')])});
  22150. $.each(dirs, function(i, dir) {
  22151. data.changed[dir.hash] = dir;
  22152. });
  22153. fm.change(data);
  22154. })
  22155. .always(function() {
  22156. var cwd = fm.cwd().hash;
  22157. fm.trigger('selectfiles', {files: $.map(dirs, function(d) { return cwd === d.phash? d.hash : null; })});
  22158. }),
  22159. success = [],
  22160. done = function(res) {
  22161. if (typeof res === 'number') {
  22162. success.push(dirs[res].name);
  22163. delete dirs[res].dirs;
  22164. } else {
  22165. res && fm.error(res);
  22166. }
  22167. (--cnt < 1) && dfrd[success.length? 'resolve' : 'reject']();
  22168. };
  22169. $.each(dirs, function(i, dir) {
  22170. var tm;
  22171. if (!(dir.write && dir.mime === 'directory')) {
  22172. done(['errEmpty', dir.name, 'errPerm']);
  22173. return null;
  22174. }
  22175. if (!fm.isCommandEnabled('rm', dir.hash)) {
  22176. done(['errCmdNoSupport', '"rm"']);
  22177. return null;
  22178. }
  22179. tm = setTimeout(function() {
  22180. fm.notify({type : 'search', cnt : 1, hideCnt : cnt > 1? false : true});
  22181. }, fm.notifyDelay);
  22182. fm.request({
  22183. data : {cmd : 'open', target : dir.hash},
  22184. preventDefault : true,
  22185. asNotOpen : true
  22186. }).done(function(data) {
  22187. var targets = [];
  22188. tm && clearTimeout(tm);
  22189. if (fm.ui.notify.children('.elfinder-notify-search').length) {
  22190. fm.notify({type : 'search', cnt : -1, hideCnt : cnt > 1? false : true});
  22191. }
  22192. if (data && data.files && data.files.length) {
  22193. if (data.files.length > fm.maxTargets) {
  22194. done(['errEmpty', dir.name, 'errMaxTargets', fm.maxTargets]);
  22195. } else {
  22196. fm.updateCache(data);
  22197. $.each(data.files, function(i, f) {
  22198. if (!f.write || f.locked) {
  22199. done(['errEmpty', dir.name, 'errRm', f.name, 'errPerm']);
  22200. targets = [];
  22201. return false;
  22202. }
  22203. targets.push(f.hash);
  22204. });
  22205. if (targets.length) {
  22206. fm.exec('rm', targets, { _userAction : true, addTexts : [ fm.i18n('folderToEmpty', dir.name) ] })
  22207. .fail(function(error) {
  22208. fm.trigger('unselectfiles', {files: fm.selected()});
  22209. done(fm.parseError(error) || '');
  22210. })
  22211. .done(function() { done(i); });
  22212. }
  22213. }
  22214. } else {
  22215. fm.toast({ mode: 'warning', msg: fm.i18n('filderIsEmpty', dir.name)});
  22216. done('');
  22217. }
  22218. }).fail(function(error) {
  22219. done(fm.parseError(error) || '');
  22220. });
  22221. });
  22222. return dfrd;
  22223. };
  22224. };
  22225. /*
  22226. * File: /js/commands/extract.js
  22227. */
  22228. /**
  22229. * @class elFinder command "extract"
  22230. * Extract files from archive
  22231. *
  22232. * @author Dmitry (dio) Levashov
  22233. **/
  22234. elFinder.prototype.commands.extract = function() {
  22235. var self = this,
  22236. fm = self.fm,
  22237. mimes = [],
  22238. filter = function(files) {
  22239. return $.grep(files, function(file) {
  22240. return file.read && $.inArray(file.mime, mimes) !== -1 ? true : false;
  22241. });
  22242. };
  22243. this.variants = [];
  22244. this.disableOnSearch = true;
  22245. // Update mimes list on open/reload
  22246. fm.bind('open reload', function() {
  22247. mimes = fm.option('archivers')['extract'] || [];
  22248. if (fm.api > 2) {
  22249. self.variants = [[{makedir: true}, fm.i18n('cmdmkdir')], [{}, fm.i18n('btnCwd')]];
  22250. } else {
  22251. self.variants = [[{}, fm.i18n('btnCwd')]];
  22252. }
  22253. self.change();
  22254. });
  22255. this.getstate = function(select) {
  22256. var sel = this.files(select),
  22257. cnt = sel.length;
  22258. return cnt && this.fm.cwd().write && filter(sel).length == cnt ? 0 : -1;
  22259. };
  22260. this.exec = function(hashes, opts) {
  22261. var files = this.files(hashes),
  22262. dfrd = $.Deferred(),
  22263. cnt = files.length,
  22264. makedir = opts && opts.makedir ? 1 : 0,
  22265. i, error,
  22266. decision;
  22267. var overwriteAll = false;
  22268. var omitAll = false;
  22269. var mkdirAll = 0;
  22270. var names = $.map(fm.files(hashes), function(file) { return file.name; });
  22271. var map = {};
  22272. $.grep(fm.files(hashes), function(file) {
  22273. map[file.name] = file;
  22274. return false;
  22275. });
  22276. var decide = function(decision) {
  22277. switch (decision) {
  22278. case 'overwrite_all' :
  22279. overwriteAll = true;
  22280. break;
  22281. case 'omit_all':
  22282. omitAll = true;
  22283. break;
  22284. }
  22285. };
  22286. var unpack = function(file) {
  22287. if (!(file.read && fm.file(file.phash).write)) {
  22288. error = ['errExtract', file.name, 'errPerm'];
  22289. fm.error(error);
  22290. dfrd.reject(error);
  22291. } else if ($.inArray(file.mime, mimes) === -1) {
  22292. error = ['errExtract', file.name, 'errNoArchive'];
  22293. fm.error(error);
  22294. dfrd.reject(error);
  22295. } else {
  22296. fm.request({
  22297. data:{cmd:'extract', target:file.hash, makedir:makedir},
  22298. notify:{type:'extract', cnt:1},
  22299. syncOnFail:true,
  22300. navigate:{
  22301. toast : makedir? {
  22302. incwd : {msg: fm.i18n(['complete', fm.i18n('cmdextract')]), action: {cmd: 'open', msg: 'cmdopen'}},
  22303. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdextract')]), action: {cmd: 'open', msg: 'cmdopen'}}
  22304. } : {
  22305. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdextract')])}
  22306. }
  22307. }
  22308. })
  22309. .fail(function (error) {
  22310. if (dfrd.state() != 'rejected') {
  22311. dfrd.reject(error);
  22312. }
  22313. })
  22314. .done(function () {
  22315. });
  22316. }
  22317. };
  22318. var confirm = function(files, index) {
  22319. var file = files[index],
  22320. name = fm.splitFileExtention(file.name)[0],
  22321. existed = ($.inArray(name, names) >= 0),
  22322. next = function(){
  22323. if((index+1) < cnt) {
  22324. confirm(files, index+1);
  22325. } else {
  22326. dfrd.resolve();
  22327. }
  22328. };
  22329. if (!makedir && existed && map[name].mime != 'directory') {
  22330. fm.confirm(
  22331. {
  22332. title : fm.i18n('ntfextract'),
  22333. text : ['errExists', name, 'confirmRepl'],
  22334. accept:{
  22335. label : 'btnYes',
  22336. callback:function (all) {
  22337. decision = all ? 'overwrite_all' : 'overwrite';
  22338. decide(decision);
  22339. if(!overwriteAll && !omitAll) {
  22340. if('overwrite' == decision) {
  22341. unpack(file);
  22342. }
  22343. if((index+1) < cnt) {
  22344. confirm(files, index+1);
  22345. } else {
  22346. dfrd.resolve();
  22347. }
  22348. } else if(overwriteAll) {
  22349. for (i = index; i < cnt; i++) {
  22350. unpack(files[i]);
  22351. }
  22352. dfrd.resolve();
  22353. }
  22354. }
  22355. },
  22356. reject : {
  22357. label : 'btnNo',
  22358. callback:function (all) {
  22359. decision = all ? 'omit_all' : 'omit';
  22360. decide(decision);
  22361. if(!overwriteAll && !omitAll && (index+1) < cnt) {
  22362. confirm(files, index+1);
  22363. } else if (omitAll) {
  22364. dfrd.resolve();
  22365. }
  22366. }
  22367. },
  22368. cancel : {
  22369. label : 'btnCancel',
  22370. callback:function () {
  22371. dfrd.resolve();
  22372. }
  22373. },
  22374. all : ((index+1) < cnt)
  22375. }
  22376. );
  22377. } else if (!makedir) {
  22378. if (mkdirAll == 0) {
  22379. fm.confirm({
  22380. title : fm.i18n('cmdextract'),
  22381. text : [fm.i18n('cmdextract')+' "'+file.name+'"', 'confirmRepl'],
  22382. accept:{
  22383. label : 'btnYes',
  22384. callback:function (all) {
  22385. all && (mkdirAll = 1);
  22386. unpack(file);
  22387. next();
  22388. }
  22389. },
  22390. reject : {
  22391. label : 'btnNo',
  22392. callback:function (all) {
  22393. all && (mkdirAll = -1);
  22394. next();
  22395. }
  22396. },
  22397. cancel : {
  22398. label : 'btnCancel',
  22399. callback:function () {
  22400. dfrd.resolve();
  22401. }
  22402. },
  22403. all : ((index+1) < cnt)
  22404. });
  22405. } else {
  22406. (mkdirAll > 0) && unpack(file);
  22407. next();
  22408. }
  22409. } else {
  22410. unpack(file);
  22411. next();
  22412. }
  22413. };
  22414. if (!(this.enabled() && cnt && mimes.length)) {
  22415. return dfrd.reject();
  22416. }
  22417. if(cnt > 0) {
  22418. confirm(files, 0);
  22419. }
  22420. return dfrd;
  22421. };
  22422. };
  22423. /*
  22424. * File: /js/commands/forward.js
  22425. */
  22426. /**
  22427. * @class elFinder command "forward"
  22428. * Open next visited folder
  22429. *
  22430. * @author Dmitry (dio) Levashov
  22431. **/
  22432. (elFinder.prototype.commands.forward = function() {
  22433. this.alwaysEnabled = true;
  22434. this.updateOnSelect = true;
  22435. this.shortcuts = [{
  22436. pattern : 'ctrl+right'
  22437. }];
  22438. this.getstate = function() {
  22439. return this.fm.history.canForward() ? 0 : -1;
  22440. };
  22441. this.exec = function() {
  22442. return this.fm.history.forward();
  22443. };
  22444. }).prototype = { forceLoad : true }; // this is required command
  22445. /*
  22446. * File: /js/commands/fullscreen.js
  22447. */
  22448. /**
  22449. * @class elFinder command "fullscreen"
  22450. * elFinder node to full scrren mode
  22451. *
  22452. * @author Naoki Sawada
  22453. **/
  22454. elFinder.prototype.commands.fullscreen = function() {
  22455. var self = this,
  22456. fm = this.fm,
  22457. update = function(e, data) {
  22458. e.preventDefault();
  22459. e.stopPropagation();
  22460. if (data && data.fullscreen) {
  22461. self.update(void(0), (data.fullscreen === 'on'));
  22462. }
  22463. };
  22464. this.alwaysEnabled = true;
  22465. this.updateOnSelect = false;
  22466. this.syncTitleOnChange = true;
  22467. this.value = false;
  22468. this.options = {
  22469. ui : 'fullscreenbutton'
  22470. };
  22471. this.getstate = function() {
  22472. return 0;
  22473. };
  22474. this.exec = function() {
  22475. var node = fm.getUI().get(0),
  22476. full = (node === fm.toggleFullscreen(node));
  22477. self.title = fm.i18n(full ? 'reinstate' : 'cmdfullscreen');
  22478. self.update(void(0), full);
  22479. return $.Deferred().resolve();
  22480. };
  22481. fm.bind('init', function() {
  22482. fm.getUI().off('resize.' + fm.namespace, update).on('resize.' + fm.namespace, update);
  22483. });
  22484. };
  22485. /*
  22486. * File: /js/commands/getfile.js
  22487. */
  22488. /**
  22489. * @class elFinder command "getfile".
  22490. * Return selected files info into outer callback.
  22491. * For use elFinder with wysiwyg editors etc.
  22492. *
  22493. * @author Dmitry (dio) Levashov, dio@std42.ru
  22494. **/
  22495. (elFinder.prototype.commands.getfile = function() {
  22496. var self = this,
  22497. fm = this.fm,
  22498. filter = function(files) {
  22499. var o = self.options;
  22500. files = $.grep(files, function(file) {
  22501. return (file.mime != 'directory' || o.folders) && file.read ? true : false;
  22502. });
  22503. return o.multiple || files.length == 1 ? files : [];
  22504. };
  22505. this.alwaysEnabled = true;
  22506. this.callback = fm.options.getFileCallback;
  22507. this._disabled = typeof(this.callback) == 'function';
  22508. this.getstate = function(select) {
  22509. var sel = this.files(select),
  22510. cnt = sel.length;
  22511. return this.callback && cnt && filter(sel).length == cnt ? 0 : -1;
  22512. };
  22513. this.exec = function(hashes) {
  22514. var fm = this.fm,
  22515. opts = this.options,
  22516. files = this.files(hashes),
  22517. cnt = files.length,
  22518. url = fm.option('url'),
  22519. tmb = fm.option('tmbUrl'),
  22520. dfrd = $.Deferred()
  22521. .done(function(data) {
  22522. var res,
  22523. done = function() {
  22524. if (opts.oncomplete == 'close') {
  22525. fm.hide();
  22526. } else if (opts.oncomplete == 'destroy') {
  22527. fm.destroy();
  22528. }
  22529. },
  22530. fail = function(error) {
  22531. if (opts.onerror == 'close') {
  22532. fm.hide();
  22533. } else if (opts.onerror == 'destroy') {
  22534. fm.destroy();
  22535. } else {
  22536. error && fm.error(error);
  22537. }
  22538. };
  22539. fm.trigger('getfile', {files : data});
  22540. try {
  22541. res = self.callback(data, fm);
  22542. } catch(e) {
  22543. fail(['Error in `getFileCallback`.', e.message]);
  22544. return;
  22545. }
  22546. if (typeof res === 'object' && typeof res.done === 'function') {
  22547. res.done(done).fail(fail);
  22548. } else {
  22549. done();
  22550. }
  22551. }),
  22552. result = function(file) {
  22553. return opts.onlyURL
  22554. ? opts.multiple ? $.map(files, function(f) { return f.url; }) : files[0].url
  22555. : opts.multiple ? files : files[0];
  22556. },
  22557. req = [],
  22558. i, file, dim;
  22559. for (i = 0; i < cnt; i++) {
  22560. file = files[i];
  22561. if (file.mime == 'directory' && !opts.folders) {
  22562. return dfrd.reject();
  22563. }
  22564. file.baseUrl = url;
  22565. if (file.url == '1') {
  22566. req.push(fm.request({
  22567. data : {cmd : 'url', target : file.hash},
  22568. notify : {type : 'url', cnt : 1, hideCnt : true},
  22569. preventDefault : true
  22570. })
  22571. .done(function(data) {
  22572. if (data.url) {
  22573. var rfile = fm.file(this.hash);
  22574. rfile.url = this.url = data.url;
  22575. }
  22576. }.bind(file)));
  22577. } else {
  22578. file.url = fm.url(file.hash);
  22579. }
  22580. if (! opts.onlyURL) {
  22581. if (opts.getPath) {
  22582. file.path = fm.path(file.hash);
  22583. if (file.path === '' && file.phash) {
  22584. // get parents
  22585. (function() {
  22586. var dfd = $.Deferred();
  22587. req.push(dfd);
  22588. fm.path(file.hash, false, {})
  22589. .done(function(path) {
  22590. file.path = path;
  22591. })
  22592. .fail(function() {
  22593. file.path = '';
  22594. })
  22595. .always(function() {
  22596. dfd.resolve();
  22597. });
  22598. })();
  22599. }
  22600. }
  22601. if (file.tmb && file.tmb != 1) {
  22602. file.tmb = tmb + file.tmb;
  22603. }
  22604. if (!file.width && !file.height) {
  22605. if (file.dim) {
  22606. dim = file.dim.split('x');
  22607. file.width = dim[0];
  22608. file.height = dim[1];
  22609. } else if (opts.getImgSize && file.mime.indexOf('image') !== -1) {
  22610. req.push(fm.request({
  22611. data : {cmd : 'dim', target : file.hash},
  22612. notify : {type : 'dim', cnt : 1, hideCnt : true},
  22613. preventDefault : true
  22614. })
  22615. .done(function(data) {
  22616. if (data.dim) {
  22617. var dim = data.dim.split('x');
  22618. var rfile = fm.file(this.hash);
  22619. rfile.width = this.width = dim[0];
  22620. rfile.height = this.height = dim[1];
  22621. }
  22622. }.bind(file)));
  22623. }
  22624. }
  22625. }
  22626. }
  22627. if (req.length) {
  22628. $.when.apply(null, req).always(function() {
  22629. dfrd.resolve(result(files));
  22630. });
  22631. return dfrd;
  22632. }
  22633. return dfrd.resolve(result(files));
  22634. };
  22635. }).prototype = { forceLoad : true }; // this is required command
  22636. /*
  22637. * File: /js/commands/help.js
  22638. */
  22639. /**
  22640. * @class elFinder command "help"
  22641. * "About" dialog
  22642. *
  22643. * @author Dmitry (dio) Levashov
  22644. **/
  22645. (elFinder.prototype.commands.help = function() {
  22646. var fm = this.fm,
  22647. self = this,
  22648. linktpl = '<div class="elfinder-help-link"> <a href="{url}">{link}</a></div>',
  22649. linktpltgt = '<div class="elfinder-help-link"> <a href="{url}" target="_blank">{link}</a></div>',
  22650. atpl = '<div class="elfinder-help-team"><div>{author}</div>{work}</div>',
  22651. url = /\{url\}/,
  22652. link = /\{link\}/,
  22653. author = /\{author\}/,
  22654. work = /\{work\}/,
  22655. r = 'replace',
  22656. prim = 'ui-priority-primary',
  22657. sec = 'ui-priority-secondary',
  22658. lic = 'elfinder-help-license',
  22659. tab = '<li class="' + fm.res('class', 'tabstab') + ' elfinder-help-tab-{id}"><a href="#'+fm.namespace+'-help-{id}" class="ui-tabs-anchor">{title}</a></li>',
  22660. html = ['<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-help">',
  22661. '<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-top">'],
  22662. stpl = '<div class="elfinder-help-shortcut"><div class="elfinder-help-shortcut-pattern">{pattern}</div> {descrip}</div>',
  22663. sep = '<div class="elfinder-help-separator"/>',
  22664. selfUrl = $('base').length? document.location.href.replace(/#.*$/, '') : '',
  22665. clTabActive = fm.res('class', 'tabsactive'),
  22666. getTheme = function() {
  22667. var src;
  22668. if (fm.theme && fm.theme.author) {
  22669. 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)+')');
  22670. } else {
  22671. src = '<div class="elfinder-help-team elfinder-help-term-theme" style="display:none"></div>';
  22672. }
  22673. return src;
  22674. },
  22675. about = function() {
  22676. html.push('<div id="'+fm.namespace+'-help-about" class="ui-tabs-panel ui-widget-content ui-corner-bottom"><div class="elfinder-help-logo"/>');
  22677. html.push('<h3>elFinder</h3>');
  22678. html.push('<div class="'+prim+'">'+fm.i18n('webfm')+'</div>');
  22679. html.push('<div class="'+sec+'">'+fm.i18n('ver')+': '+fm.version+'</div>');
  22680. html.push('<div class="'+sec+'">'+fm.i18n('protocolver')+': <span class="apiver"></span></div>');
  22681. html.push('<div class="'+sec+'">jQuery/jQuery UI: '+$().jquery+'/'+$.ui.version+'</div>');
  22682. html.push(sep);
  22683. html.push(linktpltgt[r](url, 'https://studio-42.github.io/elFinder/')[r](link, fm.i18n('homepage')));
  22684. html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder/wiki')[r](link, fm.i18n('docs')));
  22685. html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder')[r](link, fm.i18n('github')));
  22686. //html.push(linktpltgt[r](url, 'http://twitter.com/elrte_elfinder')[r](link, fm.i18n('twitter')));
  22687. html.push(sep);
  22688. html.push('<div class="'+prim+'">'+fm.i18n('team')+'</div>');
  22689. html.push(atpl[r](author, 'Dmitry "dio" Levashov &lt;dio@std42.ru&gt;')[r](work, fm.i18n('chiefdev')));
  22690. html.push(atpl[r](author, 'Naoki Sawada &lt;hypweb+elfinder@gmail.com&gt;')[r](work, fm.i18n('developer')));
  22691. html.push(atpl[r](author, 'Troex Nevelin &lt;troex@fury.scancode.ru&gt;')[r](work, fm.i18n('maintainer')));
  22692. html.push(atpl[r](author, 'Alexey Sukhotin &lt;strogg@yandex.ru&gt;')[r](work, fm.i18n('contributor')));
  22693. if (fm.i18[fm.lang].translator) {
  22694. $.each(fm.i18[fm.lang].translator.split(', '), function() {
  22695. html.push(atpl[r](author, $.trim(this))[r](work, fm.i18n('translator')+' ('+fm.i18[fm.lang].language+')'));
  22696. });
  22697. }
  22698. html.push(getTheme());
  22699. html.push(sep);
  22700. 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>');
  22701. html.push(sep);
  22702. html.push('<div class="'+lic+'">Licence: 3-clauses BSD Licence</div>');
  22703. html.push('<div class="'+lic+'">Copyright © 2009-2018, Studio 42</div>');
  22704. html.push('<div class="'+lic+'">„ …'+fm.i18n('dontforget')+' ”</div>');
  22705. html.push('</div>');
  22706. },
  22707. shortcuts = function() {
  22708. var sh = fm.shortcuts();
  22709. // shortcuts tab
  22710. html.push('<div id="'+fm.namespace+'-help-shortcuts" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  22711. if (sh.length) {
  22712. html.push('<div class="ui-widget-content elfinder-help-shortcuts">');
  22713. $.each(sh, function(i, s) {
  22714. html.push(stpl.replace(/\{pattern\}/, s[0]).replace(/\{descrip\}/, s[1]));
  22715. });
  22716. html.push('</div>');
  22717. } else {
  22718. html.push('<div class="elfinder-help-disabled">'+fm.i18n('shortcutsof')+'</div>');
  22719. }
  22720. html.push('</div>');
  22721. },
  22722. help = function() {
  22723. // help tab
  22724. html.push('<div id="'+fm.namespace+'-help-help" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  22725. html.push('<a href="https://github.com/Studio-42/elFinder/wiki" target="_blank" class="elfinder-dont-panic"><span>DON\'T PANIC</span></a>');
  22726. html.push('</div>');
  22727. // end help
  22728. },
  22729. useInteg = false,
  22730. integrations = function() {
  22731. useInteg = true;
  22732. html.push('<div id="'+fm.namespace+'-help-integrations" class="ui-tabs-panel ui-widget-content ui-corner-bottom"/>');
  22733. },
  22734. useDebug = false,
  22735. debug = function() {
  22736. useDebug = true;
  22737. // debug tab
  22738. html.push('<div id="'+fm.namespace+'-help-debug" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  22739. html.push('<div class="ui-widget-content elfinder-help-debug"><ul></ul></div>');
  22740. html.push('</div>');
  22741. // end debug
  22742. },
  22743. debugRender = function() {
  22744. var render = function(elm, obj) {
  22745. $.each(obj, function(k, v) {
  22746. elm.append($('<dt/>').text(k));
  22747. if (typeof v === 'undefined') {
  22748. elm.append($('<dd/>').append($('<span/>').text('undfined')));
  22749. } else if (typeof v === 'object' && !v) {
  22750. elm.append($('<dd/>').append($('<span/>').text('null')));
  22751. } else if (typeof v === 'object' && ($.isPlainObject(v) || v.length)) {
  22752. elm.append( $('<dd/>').append(render($('<dl/>'), v)));
  22753. } else {
  22754. elm.append($('<dd/>').append($('<span/>').text((v && typeof v === 'object')? '[]' : (v? v : '""'))));
  22755. }
  22756. });
  22757. return elm;
  22758. },
  22759. cnt = debugUL.children('li').length,
  22760. targetL, target, tabId,
  22761. info, lastUL, lastDIV;
  22762. if (self.debug.options || self.debug.debug) {
  22763. if (cnt >= 5) {
  22764. lastUL = debugUL.children('li:last');
  22765. lastDIV = debugDIV.children('div:last');
  22766. if (lastDIV.is(':hidden')) {
  22767. lastUL.remove();
  22768. lastDIV.remove();
  22769. } else {
  22770. lastUL.prev().remove();
  22771. lastDIV.prev().remove();
  22772. }
  22773. }
  22774. tabId = fm.namespace + '-help-debug-' + (+new Date());
  22775. targetL = $('<li/>').html('<a href="'+selfUrl+'#'+tabId+'">'+self.debug.debug.cmd+'</a>').prependTo(debugUL);
  22776. target = $('<div id="'+tabId+'"/>').data('debug', self.debug);
  22777. targetL.on('click.debugrender', function() {
  22778. var debug = target.data('debug');
  22779. target.removeData('debug');
  22780. if (debug) {
  22781. target.hide();
  22782. if (debug.debug) {
  22783. info = $('<fieldset>').append($('<legend/>').text('debug'), render($('<dl/>'), debug.debug));
  22784. target.append(info);
  22785. }
  22786. if (debug.options) {
  22787. info = $('<fieldset>').append($('<legend/>').text('options'), render($('<dl/>'), debug.options));
  22788. target.append(info);
  22789. }
  22790. target.show();
  22791. }
  22792. targetL.off('click.debugrender');
  22793. });
  22794. debugUL.after(target);
  22795. opened && debugDIV.tabs('refresh');
  22796. }
  22797. },
  22798. content = '',
  22799. opened, tabInteg, integDIV, tabDebug, debugDIV, debugUL;
  22800. this.alwaysEnabled = true;
  22801. this.updateOnSelect = false;
  22802. this.state = -1;
  22803. this.shortcuts = [{
  22804. pattern : 'f1',
  22805. description : this.title
  22806. }];
  22807. fm.bind('load', function() {
  22808. var parts = self.options.view || ['about', 'shortcuts', 'help', 'integrations', 'debug'],
  22809. i, helpSource, tabBase, tabNav, tabs, delta;
  22810. // remove 'preference' tab, it moved to command 'preference'
  22811. if ((i = $.inArray('preference', parts)) !== -1) {
  22812. parts.splice(i, 1);
  22813. }
  22814. // debug tab require jQueryUI Tabs Widget
  22815. if (! $.fn.tabs) {
  22816. if ((i = $.inArray(parts, 'debug')) !== -1) {
  22817. parts.splice(i, 1);
  22818. }
  22819. }
  22820. $.each(parts, function(i, title) {
  22821. html.push(tab[r](/\{id\}/g, title)[r](/\{title\}/, fm.i18n(title)));
  22822. });
  22823. html.push('</ul>');
  22824. $.inArray('about', parts) !== -1 && about();
  22825. $.inArray('shortcuts', parts) !== -1 && shortcuts();
  22826. if ($.inArray('help', parts) !== -1) {
  22827. helpSource = fm.i18nBaseUrl + 'help/%s.html.js';
  22828. help();
  22829. }
  22830. $.inArray('integrations', parts) !== -1 && integrations();
  22831. $.inArray('debug', parts) !== -1 && debug();
  22832. html.push('</div>');
  22833. content = $(html.join(''));
  22834. content.find('.ui-tabs-nav li')
  22835. .on('mouseenter mouseleave', function(e) {
  22836. $(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
  22837. })
  22838. .on('focus blur', 'a', function(e) {
  22839. $(e.delegateTarget).toggleClass('ui-state-focus', e.type === 'focusin');
  22840. })
  22841. .children()
  22842. .on('click', function(e) {
  22843. var link = $(this);
  22844. e.preventDefault();
  22845. e.stopPropagation();
  22846. link.parent().addClass(clTabActive).siblings().removeClass(clTabActive);
  22847. content.children('.ui-tabs-panel').hide().filter(link.attr('href')).show();
  22848. })
  22849. .filter(':first').trigger('click');
  22850. if (useInteg) {
  22851. tabInteg = content.find('.elfinder-help-tab-integrations').hide();
  22852. integDIV = content.find('#'+fm.namespace+'-help-integrations').hide().append($('<div class="elfinder-help-integrations-desc"/>').html(fm.i18n('integrationWith')));
  22853. fm.bind('helpIntegration', function(e) {
  22854. var ul = integDIV.children('ul:first'),
  22855. data, elm, cmdUL, cmdCls;
  22856. if (e.data) {
  22857. if ($.isPlainObject(e.data)) {
  22858. data = Object.assign({
  22859. link: '',
  22860. title: '',
  22861. banner: ''
  22862. }, e.data);
  22863. if (data.title || data.link) {
  22864. if (!data.title) {
  22865. data.title = data.link;
  22866. }
  22867. if (data.link) {
  22868. elm = $('<a/>').attr('href', data.link).attr('target', '_blank').text(data.title);
  22869. } else {
  22870. elm = $('<span/>').text(data.title);
  22871. }
  22872. if (data.banner) {
  22873. elm = $('<span/>').append($('<img/>').attr(data.banner), elm);
  22874. }
  22875. }
  22876. } else {
  22877. elm = $(e.data);
  22878. elm.filter('a').each(function() {
  22879. var tgt = $(this);
  22880. if (!tgt.attr('target')) {
  22881. tgt.attr('target', '_blank');;
  22882. }
  22883. });
  22884. }
  22885. if (elm) {
  22886. tabInteg.show();
  22887. if (!ul.length) {
  22888. ul = $('<ul class="elfinder-help-integrations"/>').appendTo(integDIV);
  22889. }
  22890. if (data && data.cmd) {
  22891. cmdCls = 'elfinder-help-integration-' + data.cmd;
  22892. cmdUL = ul.find('ul.' + cmdCls);
  22893. if (!cmdUL.length) {
  22894. cmdUL = $('<ul class="'+cmdCls+'"/>');
  22895. ul.append($('<li/>').append($('<span/>').html(fm.i18n('cmd'+data.cmd))).append(cmdUL));
  22896. }
  22897. elm = cmdUL.append($('<li/>').append(elm));
  22898. } else {
  22899. ul.append($('<li/>').append(elm));
  22900. }
  22901. }
  22902. }
  22903. }).bind('themechange', function() {
  22904. content.find('div.elfinder-help-term-theme').replaceWith(getTheme());
  22905. });
  22906. }
  22907. // debug
  22908. if (useDebug) {
  22909. tabDebug = content.find('.elfinder-help-tab-debug').hide();
  22910. debugDIV = content.find('#'+fm.namespace+'-help-debug').children('div:first');
  22911. debugUL = debugDIV.children('ul:first').on('click', function(e) {
  22912. e.preventDefault();
  22913. e.stopPropagation();
  22914. });
  22915. self.debug = {};
  22916. fm.bind('backenddebug', function(e) {
  22917. // CAUTION: DO NOT TOUCH `e.data`
  22918. if (useDebug && e.data && e.data.debug) {
  22919. self.debug = { options : e.data.options, debug : Object.assign({ cmd : fm.currentReqCmd }, e.data.debug) };
  22920. if (self.dialog) {
  22921. debugRender();
  22922. }
  22923. }
  22924. });
  22925. }
  22926. content.find('#'+fm.namespace+'-help-about').find('.apiver').text(fm.api);
  22927. self.dialog = self.fmDialog(content, {
  22928. title : self.title,
  22929. width : 530,
  22930. maxWidth: 'window',
  22931. maxHeight: 'window',
  22932. autoOpen : false,
  22933. destroyOnClose : false,
  22934. close : function() {
  22935. if (useDebug) {
  22936. tabDebug.hide();
  22937. debugDIV.tabs('destroy');
  22938. }
  22939. opened = false;
  22940. }
  22941. })
  22942. .on('click', function(e) {
  22943. e.stopPropagation();
  22944. })
  22945. .css({
  22946. overflow: 'hidden'
  22947. });
  22948. tabBase = self.dialog.children('.ui-tabs');
  22949. tabNav = tabBase.children('.ui-tabs-nav:first');
  22950. tabs = tabBase.children('.ui-tabs-panel');
  22951. delta = self.dialog.outerHeight(true) - self.dialog.height();
  22952. self.dialog.closest('.ui-dialog').on('resize', function() {
  22953. tabs.height(self.dialog.height() - delta - tabNav.outerHeight(true) - 20);
  22954. });
  22955. if (helpSource) {
  22956. self.dialog.one('initContents', function() {
  22957. $.ajax({
  22958. url: self.options.helpSource? self.options.helpSource : helpSource.replace('%s', fm.lang),
  22959. dataType: 'html'
  22960. }).done(function(source) {
  22961. $('#'+fm.namespace+'-help-help').html(source);
  22962. }).fail(function() {
  22963. $.ajax({
  22964. url: helpSource.replace('%s', 'en'),
  22965. dataType: 'html'
  22966. }).done(function(source) {
  22967. $('#'+fm.namespace+'-help-help').html(source);
  22968. });
  22969. });
  22970. });
  22971. }
  22972. self.state = 0;
  22973. fm.trigger('helpBuilded', self.dialog);
  22974. }).one('open', function() {
  22975. var debug = false;
  22976. fm.one('backenddebug', function() {
  22977. debug =true;
  22978. }).one('opendone', function() {
  22979. requestAnimationFrame(function() {
  22980. if (! debug && useDebug) {
  22981. useDebug = false;
  22982. tabDebug.hide();
  22983. debugDIV.hide();
  22984. debugUL.hide();
  22985. }
  22986. });
  22987. });
  22988. });
  22989. this.getstate = function() {
  22990. return 0;
  22991. };
  22992. this.exec = function(sel, opts) {
  22993. var tab = opts? opts.tab : void(0),
  22994. debugShow = function() {
  22995. if (useDebug) {
  22996. debugDIV.tabs();
  22997. debugUL.find('a:first').trigger('click');
  22998. tabDebug.show();
  22999. opened = true;
  23000. }
  23001. };
  23002. debugShow();
  23003. this.dialog.trigger('initContents').elfinderdialog('open').find((tab? '.elfinder-help-tab-'+tab : '.ui-tabs-nav li') + ' a:first').trigger('click');
  23004. return $.Deferred().resolve();
  23005. };
  23006. }).prototype = { forceLoad : true }; // this is required command
  23007. /*
  23008. * File: /js/commands/hidden.js
  23009. */
  23010. /**
  23011. * @class elFinder command "hidden"
  23012. * Always hidden command for uiCmdMap
  23013. *
  23014. * @author Naoki Sawada
  23015. **/
  23016. elFinder.prototype.commands.hidden = function() {
  23017. this.hidden = true;
  23018. this.updateOnSelect = false;
  23019. this.getstate = function() {
  23020. return -1;
  23021. };
  23022. };
  23023. /*
  23024. * File: /js/commands/hide.js
  23025. */
  23026. /**
  23027. * @class elFinder command "hide".
  23028. * folders/files to hide as personal setting.
  23029. *
  23030. * @type elFinder.command
  23031. * @author Naoki Sawada
  23032. */
  23033. elFinder.prototype.commands.hide = function() {
  23034. var self = this,
  23035. nameCache = {},
  23036. hideData, hideCnt, cMenuType, sOrigin;
  23037. this.syncTitleOnChange = true;
  23038. this.shortcuts = [{
  23039. pattern : 'ctrl+shift+dot',
  23040. description : this.fm.i18n('toggleHidden')
  23041. }];
  23042. this.init = function() {
  23043. var fm = this.fm;
  23044. hideData = fm.storage('hide') || {items: {}};
  23045. hideCnt = Object.keys(hideData.items).length;
  23046. this.title = fm.i18n(hideData.show? 'hideHidden' : 'showHidden');
  23047. self.update(void(0), self.title);
  23048. };
  23049. this.fm.bind('select contextmenucreate closecontextmenu', function(e, fm) {
  23050. var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected();
  23051. if (e.type === 'select' && e.data) {
  23052. sOrigin = e.data.origin;
  23053. } else if (e.type === 'contextmenucreate') {
  23054. cMenuType = e.data.type;
  23055. }
  23056. if (!sel.length || (((e.type !== 'contextmenucreate' && sOrigin !== 'navbar') || cMenuType === 'cwd') && sel[0] === fm.cwd().hash)) {
  23057. self.title = fm.i18n(hideData.show? 'hideHidden' : 'showHidden');
  23058. } else {
  23059. self.title = fm.i18n('cmdhide');
  23060. }
  23061. if (e.type !== 'closecontextmenu') {
  23062. self.update(cMenuType === 'cwd'? (hideCnt? 0 : -1) : void(0), self.title);
  23063. } else {
  23064. cMenuType = '';
  23065. requestAnimationFrame(function() {
  23066. self.update(void(0), self.title);
  23067. });
  23068. }
  23069. });
  23070. this.getstate = function(sel) {
  23071. return (cMenuType !== 'cwd' && (sel || this.fm.selected()).length) || hideCnt? 0 : -1;
  23072. };
  23073. this.exec = function(hashes, opts) {
  23074. var fm = this.fm,
  23075. dfrd = $.Deferred()
  23076. .done(function() {
  23077. fm.trigger('hide', {items: items, opts: opts});
  23078. })
  23079. .fail(function(error) {
  23080. fm.error(error);
  23081. }),
  23082. o = opts || {},
  23083. items = o.targets? o.targets : (hashes || fm.selected()),
  23084. added = [],
  23085. removed = [],
  23086. notifyto, files, res;
  23087. hideData = fm.storage('hide') || {};
  23088. if (!$.isPlainObject(hideData)) {
  23089. hideData = {};
  23090. }
  23091. if (!$.isPlainObject(hideData.items)) {
  23092. hideData.items = {};
  23093. }
  23094. if (opts._currentType === 'shortcut' || !items.length || (opts._currentType !== 'navbar' && sOrigin !=='navbar' && items[0] === fm.cwd().hash)) {
  23095. if (hideData.show) {
  23096. o.hide = true;
  23097. } else if (Object.keys(hideData.items).length) {
  23098. o.show = true;
  23099. }
  23100. }
  23101. if (o.reset) {
  23102. o.show = true;
  23103. hideCnt = 0;
  23104. }
  23105. if (o.show || o.hide) {
  23106. if (o.show) {
  23107. hideData.show = true;
  23108. } else {
  23109. delete hideData.show;
  23110. }
  23111. if (o.show) {
  23112. fm.storage('hide', o.reset? null : hideData);
  23113. self.title = fm.i18n('hideHidden');
  23114. self.update(o.reset? -1 : void(0), self.title);
  23115. $.each(hideData.items, function(h) {
  23116. var f = fm.file(h, true);
  23117. if (f && (fm.searchStatus.state || !f.phash || fm.file(f.phash))) {
  23118. added.push(f);
  23119. }
  23120. });
  23121. if (added.length) {
  23122. fm.updateCache({added: added});
  23123. fm.add({added: added});
  23124. }
  23125. if (o.reset) {
  23126. hideData = {items: {}};
  23127. }
  23128. return dfrd.resolve();
  23129. }
  23130. items = Object.keys(hideData.items);
  23131. }
  23132. if (items.length) {
  23133. $.each(items, function(i, h) {
  23134. var f;
  23135. if (!hideData.items[h]) {
  23136. f = fm.file(h);
  23137. if (f) {
  23138. nameCache[h] = f.i18 || f.name;
  23139. }
  23140. hideData.items[h] = nameCache[h]? nameCache[h] : h;
  23141. }
  23142. });
  23143. hideCnt = Object.keys(hideData.items).length;
  23144. files = this.files(items);
  23145. fm.storage('hide', hideData);
  23146. fm.remove({removed: items});
  23147. if (hideData.show) {
  23148. this.exec(void(0), {hide: true});
  23149. }
  23150. if (!o.hide) {
  23151. res = {};
  23152. res.undo = {
  23153. cmd : 'hide',
  23154. callback : function() {
  23155. var nData = fm.storage('hide');
  23156. if (nData) {
  23157. $.each(items, function(i, h) {
  23158. delete nData.items[h];
  23159. });
  23160. hideCnt = Object.keys(nData.items).length;
  23161. fm.storage('hide', nData);
  23162. fm.trigger('hide', {items: items, opts: {}});
  23163. self.update(hideCnt? 0 : -1);
  23164. }
  23165. fm.updateCache({added: files});
  23166. fm.add({added: files});
  23167. }
  23168. };
  23169. res.redo = {
  23170. cmd : 'hide',
  23171. callback : function() {
  23172. return fm.exec('hide', void(0), {targets: items});
  23173. }
  23174. };
  23175. }
  23176. }
  23177. return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(res);
  23178. };
  23179. };
  23180. /*
  23181. * File: /js/commands/home.js
  23182. */
  23183. (elFinder.prototype.commands.home = function() {
  23184. this.title = 'Home';
  23185. this.alwaysEnabled = true;
  23186. this.updateOnSelect = false;
  23187. this.shortcuts = [{
  23188. pattern : 'ctrl+home ctrl+shift+up',
  23189. description : 'Home'
  23190. }];
  23191. this.getstate = function() {
  23192. var root = this.fm.root(),
  23193. cwd = this.fm.cwd().hash;
  23194. return root && cwd && root != cwd ? 0: -1;
  23195. };
  23196. this.exec = function() {
  23197. return this.fm.exec('open', this.fm.root());
  23198. };
  23199. }).prototype = { forceLoad : true }; // this is required command
  23200. /*
  23201. * File: /js/commands/info.js
  23202. */
  23203. /**
  23204. * @class elFinder command "info".
  23205. * Display dialog with file properties.
  23206. *
  23207. * @author Dmitry (dio) Levashov, dio@std42.ru
  23208. **/
  23209. (elFinder.prototype.commands.info = function() {
  23210. var m = 'msg',
  23211. fm = this.fm,
  23212. spclass = 'elfinder-spinner',
  23213. btnclass = 'elfinder-info-button',
  23214. msg = {
  23215. calc : fm.i18n('calc'),
  23216. size : fm.i18n('size'),
  23217. unknown : fm.i18n('unknown'),
  23218. path : fm.i18n('path'),
  23219. aliasfor : fm.i18n('aliasfor'),
  23220. modify : fm.i18n('modify'),
  23221. perms : fm.i18n('perms'),
  23222. locked : fm.i18n('locked'),
  23223. dim : fm.i18n('dim'),
  23224. kind : fm.i18n('kind'),
  23225. files : fm.i18n('files'),
  23226. folders : fm.i18n('folders'),
  23227. roots : fm.i18n('volumeRoots'),
  23228. items : fm.i18n('items'),
  23229. yes : fm.i18n('yes'),
  23230. no : fm.i18n('no'),
  23231. link : fm.i18n('link'),
  23232. owner : fm.i18n('owner'),
  23233. group : fm.i18n('group'),
  23234. perm : fm.i18n('perm'),
  23235. getlink : fm.i18n('getLink')
  23236. },
  23237. applyZWSP = function(str, remove) {
  23238. if (remove) {
  23239. return str.replace(/\u200B/g, '');
  23240. } else {
  23241. return str.replace(/(\/|\\)/g, "$1\u200B");
  23242. }
  23243. };
  23244. this.items = ['size', 'aliasfor', 'path', 'link', 'dim', 'modify', 'perms', 'locked', 'owner', 'group', 'perm'];
  23245. if (this.options.custom && Object.keys(this.options.custom).length) {
  23246. $.each(this.options.custom, function(name, details) {
  23247. details.label && this.items.push(details.label);
  23248. });
  23249. }
  23250. this.tpl = {
  23251. 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>',
  23252. itemTitle : '<strong>{name}</strong><span class="elfinder-info-kind">{kind}</span>',
  23253. groupTitle : '<strong>{items}: {num}</strong>',
  23254. row : '<tr><td class="elfinder-info-label">{label} : </td><td class="{class}">{value}</td></tr>',
  23255. spinner : '<span>{text}</span> <span class="'+spclass+' '+spclass+'-{name}"/>'
  23256. };
  23257. this.alwaysEnabled = true;
  23258. this.updateOnSelect = false;
  23259. this.shortcuts = [{
  23260. pattern : 'ctrl+i'
  23261. }];
  23262. this.init = function() {
  23263. $.each(msg, function(k, v) {
  23264. msg[k] = fm.i18n(v);
  23265. });
  23266. };
  23267. this.getstate = function() {
  23268. return 0;
  23269. };
  23270. this.exec = function(hashes) {
  23271. var files = this.files(hashes);
  23272. if (! files.length) {
  23273. files = this.files([ this.fm.cwd().hash ]);
  23274. }
  23275. var self = this,
  23276. fm = this.fm,
  23277. o = this.options,
  23278. tpl = this.tpl,
  23279. row = tpl.row,
  23280. cnt = files.length,
  23281. content = [],
  23282. view = tpl.main,
  23283. l = '{label}',
  23284. v = '{value}',
  23285. reqs = [],
  23286. reqDfrd = null,
  23287. opts = {
  23288. title : fm.i18n('selectionInfo'),
  23289. width : 'auto',
  23290. close : function() {
  23291. $(this).elfinderdialog('destroy');
  23292. if (reqDfrd && reqDfrd.state() === 'pending') {
  23293. reqDfrd.reject();
  23294. }
  23295. $.grep(reqs, function(r) {
  23296. r && r.state() === 'pending' && r.reject();
  23297. });
  23298. }
  23299. },
  23300. count = [],
  23301. replSpinner = function(msg, name, className) {
  23302. dialog.find('.'+spclass+'-'+name).parent().html(msg).addClass(className || '');
  23303. },
  23304. id = fm.namespace+'-info-'+$.map(files, function(f) { return f.hash; }).join('-'),
  23305. dialog = fm.getUI().find('#'+id),
  23306. customActions = [],
  23307. style = '',
  23308. hashClass = 'elfinder-font-mono elfinder-info-hash',
  23309. size, tmb, file, title, dcnt, rdcnt, path, getHashAlgorisms, hideItems;
  23310. if (!cnt) {
  23311. return $.Deferred().reject();
  23312. }
  23313. if (dialog.length) {
  23314. dialog.elfinderdialog('toTop');
  23315. return $.Deferred().resolve();
  23316. }
  23317. hideItems = fm.storage('infohides') || fm.arrayFlip(o.hideItems, true);
  23318. if (cnt === 1) {
  23319. file = files[0];
  23320. if (file.icon) {
  23321. style = ' '+fm.getIconStyle(file);
  23322. }
  23323. view = view.replace('{dirclass}', file.csscls? fm.escape(file.csscls) : '').replace('{class}', fm.mime2class(file.mime)).replace('{style}', style);
  23324. title = tpl.itemTitle.replace('{name}', fm.escape(file.i18 || file.name)).replace('{kind}', '<span title="'+fm.escape(file.mime)+'">'+fm.mime2kind(file)+'</span>');
  23325. tmb = fm.tmb(file);
  23326. if (!file.read) {
  23327. size = msg.unknown;
  23328. } else if (file.mime != 'directory' || file.alias) {
  23329. size = fm.formatSize(file.size);
  23330. } else {
  23331. size = tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size');
  23332. count.push(file.hash);
  23333. }
  23334. !hideItems.size && content.push(row.replace(l, msg.size).replace(v, size));
  23335. !hideItems.aleasfor && file.alias && content.push(row.replace(l, msg.aliasfor).replace(v, file.alias));
  23336. if (!hideItems.path) {
  23337. if (path = fm.path(file.hash, true)) {
  23338. content.push(row.replace(l, msg.path).replace(v, applyZWSP(fm.escape(path))).replace('{class}', 'elfinder-info-path'));
  23339. } else {
  23340. content.push(row.replace(l, msg.path).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'path')).replace('{class}', 'elfinder-info-path'));
  23341. reqs.push(fm.path(file.hash, true, {notify: null})
  23342. .fail(function() {
  23343. replSpinner(msg.unknown, 'path');
  23344. })
  23345. .done(function(path) {
  23346. replSpinner(applyZWSP(path), 'path');
  23347. }));
  23348. }
  23349. }
  23350. if (!hideItems.link && file.read) {
  23351. var href,
  23352. name_esc = fm.escape(file.name);
  23353. if (file.url == '1') {
  23354. content.push(row.replace(l, msg.link).replace(v, '<button class="'+btnclass+' '+spclass+'-url">'+msg.getlink+'</button>'));
  23355. } else {
  23356. if (file.url) {
  23357. href = file.url;
  23358. } else if (file.mime === 'directory') {
  23359. if (o.nullUrlDirLinkSelf && file.url === null) {
  23360. var loc = window.location;
  23361. href = loc.pathname + loc.search + '#elf_' + file.hash;
  23362. } else if (file.url !== '' && fm.option('url', (!fm.isRoot(file) && file.phash) || file.hash)) {
  23363. href = fm.url(file.hash);
  23364. }
  23365. } else {
  23366. href = fm.url(file.hash);
  23367. }
  23368. href && content.push(row.replace(l, msg.link).replace(v, '<a href="'+href+'" target="_blank">'+name_esc+'</a>'));
  23369. }
  23370. }
  23371. if (!hideItems.dim) {
  23372. if (file.dim) { // old api
  23373. content.push(row.replace(l, msg.dim).replace(v, file.dim));
  23374. } else if (file.mime.indexOf('image') !== -1) {
  23375. if (file.width && file.height) {
  23376. content.push(row.replace(l, msg.dim).replace(v, file.width+'x'+file.height));
  23377. } else {
  23378. content.push(row.replace(l, msg.dim).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'dim')));
  23379. reqs.push(fm.request({
  23380. data : {cmd : 'dim', target : file.hash},
  23381. preventDefault : true
  23382. })
  23383. .fail(function() {
  23384. replSpinner(msg.unknown, 'dim');
  23385. })
  23386. .done(function(data) {
  23387. replSpinner(data.dim || msg.unknown, 'dim');
  23388. if (data.dim) {
  23389. var dim = data.dim.split('x');
  23390. var rfile = fm.file(file.hash);
  23391. rfile.width = dim[0];
  23392. rfile.height = dim[1];
  23393. }
  23394. }));
  23395. }
  23396. }
  23397. }
  23398. !hideItems.modify && content.push(row.replace(l, msg.modify).replace(v, fm.formatDate(file)));
  23399. !hideItems.perms && content.push(row.replace(l, msg.perms).replace(v, fm.formatPermissions(file)));
  23400. !hideItems.locked && content.push(row.replace(l, msg.locked).replace(v, file.locked ? msg.yes : msg.no));
  23401. !hideItems.owner && file.owner && content.push(row.replace(l, msg.owner).replace(v, file.owner));
  23402. !hideItems.group && file.group && content.push(row.replace(l, msg.group).replace(v, file.group));
  23403. !hideItems.perm && file.perm && content.push(row.replace(l, msg.perm).replace(v, fm.formatFileMode(file.perm)));
  23404. // Get MD5 hash
  23405. if (window.ArrayBuffer && (fm.options.cdns.sparkmd5 || fm.options.cdns.jssha) && file.mime !== 'directory' && file.size > 0 && (!o.showHashMaxsize || file.size <= o.showHashMaxsize)) {
  23406. getHashAlgorisms = [];
  23407. $.each(fm.storage('hashchekcer') || o.showHashAlgorisms, function(i, n) {
  23408. if (!file[n]) {
  23409. content.push(row.replace(l, fm.i18n(n)).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', n)));
  23410. getHashAlgorisms.push(n);
  23411. } else {
  23412. content.push(row.replace(l, fm.i18n(n)).replace(v, file[n]).replace('{class}', hashClass));
  23413. }
  23414. });
  23415. reqs.push(
  23416. fm.getContentsHashes(file.hash, getHashAlgorisms).progress(function(hashes) {
  23417. $.each(getHashAlgorisms, function(i, n) {
  23418. if (hashes[n]) {
  23419. replSpinner(hashes[n], n, hashClass);
  23420. }
  23421. });
  23422. }).always(function() {
  23423. $.each(getHashAlgorisms, function(i, n) {
  23424. replSpinner(msg.unknown, n);
  23425. });
  23426. })
  23427. );
  23428. }
  23429. // Add custom info fields
  23430. if (o.custom) {
  23431. $.each(o.custom, function(name, details) {
  23432. if (
  23433. !hideItems[details.label]
  23434. &&
  23435. (!details.mimes || $.grep(details.mimes, function(m){return (file.mime === m || file.mime.indexOf(m+'/') === 0)? true : false;}).length)
  23436. &&
  23437. (!details.hashRegex || file.hash.match(details.hashRegex))
  23438. ) {
  23439. // Add to the content
  23440. content.push(row.replace(l, fm.i18n(details.label)).replace(v , details.tpl.replace('{id}', id)));
  23441. // Register the action
  23442. if (details.action && (typeof details.action == 'function')) {
  23443. customActions.push(details.action);
  23444. }
  23445. }
  23446. });
  23447. }
  23448. } else {
  23449. view = view.replace('{class}', 'elfinder-cwd-icon-group');
  23450. title = tpl.groupTitle.replace('{items}', msg.items).replace('{num}', cnt);
  23451. dcnt = $.grep(files, function(f) { return f.mime == 'directory' ? true : false ; }).length;
  23452. if (!dcnt) {
  23453. size = 0;
  23454. $.each(files, function(h, f) {
  23455. var s = parseInt(f.size);
  23456. if (s >= 0 && size >= 0) {
  23457. size += s;
  23458. } else {
  23459. size = 'unknown';
  23460. }
  23461. });
  23462. content.push(row.replace(l, msg.kind).replace(v, msg.files));
  23463. !hideItems.size && content.push(row.replace(l, msg.size).replace(v, fm.formatSize(size)));
  23464. } else {
  23465. rdcnt = $.grep(files, function(f) { return f.mime === 'directory' && (! f.phash || f.isroot)? true : false ; }).length;
  23466. dcnt -= rdcnt;
  23467. 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(', ')));
  23468. !hideItems.size && content.push(row.replace(l, msg.size).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size')));
  23469. count = $.map(files, function(f) { return f.hash; });
  23470. }
  23471. }
  23472. view = view.replace('{title}', title).replace('{content}', content.join('').replace(/{class}/g, ''));
  23473. dialog = self.fmDialog(view, opts);
  23474. dialog.attr('id', id).one('mousedown', '.elfinder-info-path', function() {
  23475. $(this).html(applyZWSP($(this).html(), true));
  23476. });
  23477. if (fm.UA.Mobile && $.fn.tooltip) {
  23478. dialog.children('.ui-dialog-content .elfinder-info-title').tooltip({
  23479. classes: {
  23480. 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow'
  23481. },
  23482. tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow',
  23483. track: true
  23484. });
  23485. }
  23486. if (file && file.url == '1') {
  23487. dialog.on('click', '.'+spclass+'-url', function(){
  23488. $(this).parent().html(tpl.spinner.replace('{text}', fm.i18n('ntfurl')).replace('{name}', 'url'));
  23489. fm.request({
  23490. data : {cmd : 'url', target : file.hash},
  23491. preventDefault : true
  23492. })
  23493. .fail(function() {
  23494. replSpinner(name_esc, 'url');
  23495. })
  23496. .done(function(data) {
  23497. if (data.url) {
  23498. replSpinner('<a href="'+data.url+'" target="_blank">'+name_esc+'</a>' || name_esc, 'url');
  23499. var rfile = fm.file(file.hash);
  23500. rfile.url = data.url;
  23501. } else {
  23502. replSpinner(name_esc, 'url');
  23503. }
  23504. });
  23505. });
  23506. }
  23507. // load thumbnail
  23508. if (tmb) {
  23509. $('<img/>')
  23510. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  23511. .attr('src', tmb.url);
  23512. }
  23513. // send request to count total size
  23514. if (count.length) {
  23515. reqDfrd = fm.getSize(count).done(function(data) {
  23516. replSpinner(data.formated, 'size');
  23517. }).fail(function() {
  23518. replSpinner(msg.unknown, 'size');
  23519. });
  23520. }
  23521. // call custom actions
  23522. if (customActions.length) {
  23523. $.each(customActions, function(i, action) {
  23524. try {
  23525. action(file, fm, dialog);
  23526. } catch(e) {
  23527. fm.debug('error', e);
  23528. }
  23529. });
  23530. }
  23531. return $.Deferred().resolve();
  23532. };
  23533. }).prototype = { forceLoad : true }; // this is required command
  23534. /*
  23535. * File: /js/commands/mkdir.js
  23536. */
  23537. /**
  23538. * @class elFinder command "mkdir"
  23539. * Create new folder
  23540. *
  23541. * @author Dmitry (dio) Levashov
  23542. **/
  23543. elFinder.prototype.commands.mkdir = function() {
  23544. var fm = this.fm,
  23545. self = this,
  23546. curOrg;
  23547. this.value = '';
  23548. this.disableOnSearch = true;
  23549. this.updateOnSelect = false;
  23550. this.syncTitleOnChange = true;
  23551. this.mime = 'directory';
  23552. this.prefix = 'untitled folder';
  23553. this.exec = function(select, cOpts) {
  23554. var onCwd;
  23555. if (select && select.length && cOpts && cOpts._currentType && cOpts._currentType === 'navbar') {
  23556. this.origin = cOpts._currentType;
  23557. this.data = {
  23558. target: select[0]
  23559. };
  23560. } else {
  23561. onCwd = fm.cwd().hash === select[0];
  23562. this.origin = curOrg && !onCwd? curOrg : 'cwd';
  23563. delete this.data;
  23564. }
  23565. if (! select && ! this.options.intoNewFolderToolbtn) {
  23566. fm.getUI('cwd').trigger('unselectall');
  23567. }
  23568. //this.move = (!onCwd && curOrg !== 'navbar' && fm.selected().length)? true : false;
  23569. this.move = this.value === fm.i18n('cmdmkdirin');
  23570. return $.proxy(fm.res('mixin', 'make'), self)();
  23571. };
  23572. this.shortcuts = [{
  23573. pattern : 'ctrl+shift+n'
  23574. }];
  23575. this.init = function() {
  23576. if (this.options.intoNewFolderToolbtn) {
  23577. this.syncTitleOnChange = true;
  23578. }
  23579. };
  23580. fm.bind('select contextmenucreate closecontextmenu', function(e) {
  23581. var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected();
  23582. self.className = 'mkdir';
  23583. curOrg = e.data && sel.length? (e.data.origin || e.data.type || '') : '';
  23584. if (!self.options.intoNewFolderToolbtn && curOrg === '') {
  23585. curOrg = 'cwd';
  23586. }
  23587. if (sel.length && curOrg !== 'navbar' && curOrg !== 'cwd' && fm.cwd().hash !== sel[0]) {
  23588. self.title = fm.i18n('cmdmkdirin');
  23589. self.className += ' elfinder-button-icon-mkdirin';
  23590. } else {
  23591. self.title = fm.i18n('cmdmkdir');
  23592. }
  23593. if (e.type !== 'closecontextmenu') {
  23594. self.update(void(0), self.title);
  23595. } else {
  23596. requestAnimationFrame(function() {
  23597. self.update(void(0), self.title);
  23598. });
  23599. }
  23600. });
  23601. this.getstate = function(select) {
  23602. var cwd = fm.cwd(),
  23603. sel = (curOrg === 'navbar' || (select && select[0] !== cwd.hash))? this.files(select || fm.selected()) : [],
  23604. cnt = sel.length;
  23605. if (curOrg === 'navbar') {
  23606. return cnt && sel[0].write && sel[0].read? 0 : -1;
  23607. } else {
  23608. return cwd.write && (!cnt || $.grep(sel, function(f) { return f.read && ! f.locked? true : false; }).length == cnt)? 0 : -1;
  23609. }
  23610. };
  23611. };
  23612. /*
  23613. * File: /js/commands/mkfile.js
  23614. */
  23615. /**
  23616. * @class elFinder command "mkfile"
  23617. * Create new empty file
  23618. *
  23619. * @author Dmitry (dio) Levashov
  23620. **/
  23621. elFinder.prototype.commands.mkfile = function() {
  23622. var self = this;
  23623. this.disableOnSearch = true;
  23624. this.updateOnSelect = false;
  23625. this.mime = 'text/plain';
  23626. this.prefix = 'untitled file.txt';
  23627. this.variants = [];
  23628. this.getTypeName = function(mime, type) {
  23629. var fm = self.fm,
  23630. name;
  23631. if (name = fm.messages['kind' + fm.kinds[mime]]) {
  23632. name = fm.i18n(['extentiontype', type.toUpperCase(), name]);
  23633. } else {
  23634. name = fm.i18n(['extentionfile', type.toUpperCase()]);
  23635. }
  23636. return name;
  23637. };
  23638. this.fm.bind('open reload canMakeEmptyFile', function() {
  23639. var fm = self.fm,
  23640. hides = fm.storage('mkfileHides') || {};
  23641. self.variants = [];
  23642. if (fm.mimesCanMakeEmpty) {
  23643. $.each(fm.mimesCanMakeEmpty, function(mime, type) {
  23644. type && !hides[mime] && fm.uploadMimeCheck(mime) && self.variants.push([mime, self.getTypeName(mime, type)]);
  23645. });
  23646. }
  23647. self.change();
  23648. });
  23649. this.getstate = function() {
  23650. return this.fm.cwd().write ? 0 : -1;
  23651. };
  23652. this.exec = function(_dum, mime) {
  23653. var fm = self.fm,
  23654. type, err;
  23655. if (type = fm.mimesCanMakeEmpty[mime]) {
  23656. if (fm.uploadMimeCheck(mime)) {
  23657. this.mime = mime;
  23658. this.prefix = fm.i18n(['untitled file', type]);
  23659. return $.proxy(fm.res('mixin', 'make'), self)();
  23660. }
  23661. err = ['errMkfile', self.getTypeName(mime, type)];
  23662. }
  23663. return $.Deferred().reject(err);
  23664. };
  23665. };
  23666. /*
  23667. * File: /js/commands/netmount.js
  23668. */
  23669. /**
  23670. * @class elFinder command "netmount"
  23671. * Mount network volume with user credentials.
  23672. *
  23673. * @author Dmitry (dio) Levashov
  23674. **/
  23675. elFinder.prototype.commands.netmount = function() {
  23676. var self = this,
  23677. hasMenus = false,
  23678. content;
  23679. this.alwaysEnabled = true;
  23680. this.updateOnSelect = false;
  23681. this.drivers = [];
  23682. this.handlers = {
  23683. load : function() {
  23684. var fm = self.fm;
  23685. self.drivers = fm.netDrivers;
  23686. if (self.drivers.length) {
  23687. requestAnimationFrame(function() {
  23688. $.each(self.drivers, function() {
  23689. var d = self.options[this];
  23690. if (d) {
  23691. hasMenus = true;
  23692. if (d.integrateInfo) {
  23693. fm.trigger('helpIntegration', Object.assign({cmd: 'netmount'}, d.integrateInfo));
  23694. }
  23695. }
  23696. });
  23697. });
  23698. }
  23699. }
  23700. };
  23701. this.getstate = function() {
  23702. return hasMenus ? 0 : -1;
  23703. };
  23704. this.exec = function() {
  23705. var fm = self.fm,
  23706. dfrd = $.Deferred(),
  23707. o = self.options,
  23708. create = function() {
  23709. var winFocus = function() {
  23710. inputs.protocol.trigger('change', 'winfocus');
  23711. },
  23712. inputs = {
  23713. protocol : $('<select/>')
  23714. .on('change', function(e, data){
  23715. var protocol = this.value;
  23716. content.find('.elfinder-netmount-tr').hide();
  23717. content.find('.elfinder-netmount-tr-'+protocol).show();
  23718. dialogNode && dialogNode.children('.ui-dialog-buttonpane:first').find('button').show();
  23719. if (typeof o[protocol].select == 'function') {
  23720. o[protocol].select(fm, e, data);
  23721. }
  23722. requestAnimationFrame(function() {
  23723. content.find('input:text.elfinder-tabstop:visible:first').trigger('focus');
  23724. });
  23725. })
  23726. .addClass('ui-corner-all')
  23727. },
  23728. opts = {
  23729. title : fm.i18n('netMountDialogTitle'),
  23730. resizable : false,
  23731. modal : true,
  23732. destroyOnClose : false,
  23733. open : function() {
  23734. $(window).on('focus.'+fm.namespace, winFocus);
  23735. inputs.protocol.trigger('change');
  23736. },
  23737. close : function() {
  23738. dfrd.state() == 'pending' && dfrd.reject();
  23739. $(window).off('focus.'+fm.namespace, winFocus);
  23740. },
  23741. buttons : {}
  23742. },
  23743. doMount = function() {
  23744. var protocol = inputs.protocol.val(),
  23745. data = {cmd : 'netmount', protocol: protocol},
  23746. cur = o[protocol];
  23747. $.each(content.find('input.elfinder-netmount-inputs-'+protocol), function(name, input) {
  23748. var val, elm;
  23749. elm = $(input);
  23750. if (elm.is(':radio,:checkbox')) {
  23751. if (elm.is(':checked')) {
  23752. val = $.trim(elm.val());
  23753. }
  23754. } else {
  23755. val = $.trim(elm.val());
  23756. }
  23757. if (val) {
  23758. data[input.name] = val;
  23759. }
  23760. });
  23761. if (!data.host) {
  23762. return fm.trigger('error', {error : 'errNetMountHostReq', opts : {modal: true}});
  23763. }
  23764. fm.request({data : data, notify : {type : 'netmount', cnt : 1, hideCnt : true}})
  23765. .done(function(data) {
  23766. var pdir;
  23767. if (data.added && data.added.length) {
  23768. if (data.added[0].phash) {
  23769. if (pdir = fm.file(data.added[0].phash)) {
  23770. if (! pdir.dirs) {
  23771. pdir.dirs = 1;
  23772. fm.change({ changed: [ pdir ] });
  23773. }
  23774. }
  23775. }
  23776. fm.one('netmountdone', function() {
  23777. fm.exec('open', data.added[0].hash);
  23778. });
  23779. }
  23780. dfrd.resolve();
  23781. })
  23782. .fail(function(error) {
  23783. if (cur.fail && typeof cur.fail == 'function') {
  23784. cur.fail(fm, fm.parseError(error));
  23785. }
  23786. dfrd.reject(error);
  23787. });
  23788. self.dialog.elfinderdialog('close');
  23789. },
  23790. form = $('<form autocomplete="off"/>').on('keydown', 'input', function(e) {
  23791. var comp = true,
  23792. next;
  23793. if (e.keyCode === $.ui.keyCode.ENTER) {
  23794. $.each(form.find('input:visible:not(.elfinder-input-optional)'), function() {
  23795. if ($(this).val() === '') {
  23796. comp = false;
  23797. next = $(this);
  23798. return false;
  23799. }
  23800. });
  23801. if (comp) {
  23802. doMount();
  23803. } else {
  23804. next.trigger('focus');
  23805. }
  23806. }
  23807. }),
  23808. hidden = $('<div/>'),
  23809. dialog;
  23810. content = $('<table class="elfinder-info-tb elfinder-netmount-tb"/>')
  23811. .append($('<tr/>').append($('<td>'+fm.i18n('protocol')+'</td>')).append($('<td/>').append(inputs.protocol)));
  23812. $.each(self.drivers, function(i, protocol) {
  23813. if (o[protocol]) {
  23814. inputs.protocol.append('<option value="'+protocol+'">'+fm.i18n(o[protocol].name || protocol)+'</option>');
  23815. $.each(o[protocol].inputs, function(name, input) {
  23816. input.attr('name', name);
  23817. if (input.attr('type') != 'hidden') {
  23818. input.addClass('ui-corner-all elfinder-netmount-inputs-'+protocol);
  23819. content.append($('<tr/>').addClass('elfinder-netmount-tr elfinder-netmount-tr-'+protocol).append($('<td>'+fm.i18n(name)+'</td>')).append($('<td/>').append(input)));
  23820. } else {
  23821. input.addClass('elfinder-netmount-inputs-'+protocol);
  23822. hidden.append(input);
  23823. }
  23824. });
  23825. o[protocol].protocol = inputs.protocol;
  23826. }
  23827. });
  23828. content.append(hidden);
  23829. content.find('.elfinder-netmount-tr').hide();
  23830. opts.buttons[fm.i18n('btnMount')] = doMount;
  23831. opts.buttons[fm.i18n('btnCancel')] = function() {
  23832. self.dialog.elfinderdialog('close');
  23833. };
  23834. content.find('select,input').addClass('elfinder-tabstop');
  23835. dialog = self.fmDialog(form.append(content), opts);
  23836. dialogNode = dialog.closest('.ui-dialog');
  23837. dialog.ready(function(){
  23838. inputs.protocol.trigger('change');
  23839. dialog.elfinderdialog('posInit');
  23840. });
  23841. return dialog;
  23842. },
  23843. dialogNode;
  23844. if (!self.dialog) {
  23845. self.dialog = create();
  23846. } else {
  23847. self.dialog.elfinderdialog('open');
  23848. }
  23849. return dfrd.promise();
  23850. };
  23851. self.fm.bind('netmount', function(e) {
  23852. var d = e.data || null,
  23853. o = self.options;
  23854. if (d && d.protocol) {
  23855. if (o[d.protocol] && typeof o[d.protocol].done == 'function') {
  23856. o[d.protocol].done(self.fm, d);
  23857. content.find('select,input').addClass('elfinder-tabstop');
  23858. self.dialog.elfinderdialog('tabstopsInit');
  23859. }
  23860. }
  23861. });
  23862. };
  23863. elFinder.prototype.commands.netunmount = function() {
  23864. var self = this;
  23865. this.alwaysEnabled = true;
  23866. this.updateOnSelect = false;
  23867. this.drivers = [];
  23868. this.handlers = {
  23869. load : function() {
  23870. this.drivers = this.fm.netDrivers;
  23871. }
  23872. };
  23873. this.getstate = function(sel) {
  23874. var fm = this.fm,
  23875. file;
  23876. return !!sel && this.drivers.length && !this._disabled && (file = fm.file(sel[0])) && file.netkey ? 0 : -1;
  23877. };
  23878. this.exec = function(hashes) {
  23879. var self = this,
  23880. fm = this.fm,
  23881. dfrd = $.Deferred()
  23882. .fail(function(error) {
  23883. error && fm.error(error);
  23884. }),
  23885. drive = fm.file(hashes[0]),
  23886. childrenRoots = function(hash) {
  23887. var roots = [],
  23888. work;
  23889. if (fm.leafRoots) {
  23890. work = [];
  23891. $.each(fm.leafRoots, function(phash, hashes) {
  23892. var parents = fm.parents(phash),
  23893. idx, deep;
  23894. if ((idx = $.inArray(hash, parents)) !== -1) {
  23895. idx = parents.length - idx;
  23896. $.each(hashes, function(i, h) {
  23897. work.push({i: idx, hash: h});
  23898. });
  23899. }
  23900. });
  23901. if (work.length) {
  23902. work.sort(function(a, b) { return a.i < b.i; });
  23903. $.each(work, function(i, o) {
  23904. roots.push(o.hash);
  23905. });
  23906. }
  23907. }
  23908. return roots;
  23909. };
  23910. if (this._disabled) {
  23911. return dfrd.reject();
  23912. }
  23913. if (dfrd.state() == 'pending') {
  23914. fm.confirm({
  23915. title : self.title,
  23916. text : fm.i18n('confirmUnmount', drive.name),
  23917. accept : {
  23918. label : 'btnUnmount',
  23919. callback : function() {
  23920. var target = drive.hash,
  23921. roots = childrenRoots(target),
  23922. requests = [],
  23923. removed = [],
  23924. doUmount = function() {
  23925. $.when(requests).done(function() {
  23926. fm.request({
  23927. data : {cmd : 'netmount', protocol : 'netunmount', host: drive.netkey, user : target, pass : 'dum'},
  23928. notify : {type : 'netunmount', cnt : 1, hideCnt : true},
  23929. preventFail : true
  23930. })
  23931. .fail(function(error) {
  23932. dfrd.reject(error);
  23933. })
  23934. .done(function(data) {
  23935. drive.volumeid && delete fm.volumeExpires[drive.volumeid];
  23936. dfrd.resolve();
  23937. });
  23938. }).fail(function(error) {
  23939. if (removed.length) {
  23940. fm.remove({ removed: removed });
  23941. }
  23942. dfrd.reject(error);
  23943. });
  23944. };
  23945. if (roots.length) {
  23946. fm.confirm({
  23947. title : self.title,
  23948. text : (function() {
  23949. var msgs = ['unmountChildren'];
  23950. $.each(roots, function(i, hash) {
  23951. msgs.push([fm.file(hash).name]);
  23952. });
  23953. return msgs;
  23954. })(),
  23955. accept : {
  23956. label : 'btnUnmount',
  23957. callback : function() {
  23958. $.each(roots, function(i, hash) {
  23959. var d = fm.file(hash);
  23960. if (d.netkey) {
  23961. requests.push(fm.request({
  23962. data : {cmd : 'netmount', protocol : 'netunmount', host: d.netkey, user : d.hash, pass : 'dum'},
  23963. notify : {type : 'netunmount', cnt : 1, hideCnt : true},
  23964. preventDefault : true
  23965. }).done(function(data) {
  23966. if (data.removed) {
  23967. d.volumeid && delete fm.volumeExpires[d.volumeid];
  23968. removed = removed.concat(data.removed);
  23969. }
  23970. }));
  23971. }
  23972. });
  23973. doUmount();
  23974. }
  23975. },
  23976. cancel : {
  23977. label : 'btnCancel',
  23978. callback : function() {
  23979. dfrd.reject();
  23980. }
  23981. }
  23982. });
  23983. } else {
  23984. requests = null;
  23985. doUmount();
  23986. }
  23987. }
  23988. },
  23989. cancel : {
  23990. label : 'btnCancel',
  23991. callback : function() { dfrd.reject(); }
  23992. }
  23993. });
  23994. }
  23995. return dfrd;
  23996. };
  23997. };
  23998. /*
  23999. * File: /js/commands/open.js
  24000. */
  24001. /**
  24002. * @class elFinder command "open"
  24003. * Enter folder or open files in new windows
  24004. *
  24005. * @author Dmitry (dio) Levashov
  24006. **/
  24007. (elFinder.prototype.commands.open = function() {
  24008. var fm = this.fm;
  24009. this.alwaysEnabled = true;
  24010. this.noChangeDirOnRemovedCwd = true;
  24011. this._handlers = {
  24012. dblclick : function(e) { e.preventDefault(); fm.exec('open', e.data && e.data.file? [ e.data.file ]: void(0)); },
  24013. 'select enable disable reload' : function(e) { this.update(e.type == 'disable' ? -1 : void(0)); }
  24014. };
  24015. this.shortcuts = [{
  24016. pattern : 'ctrl+down numpad_enter'+(fm.OS != 'mac' && ' enter')
  24017. }];
  24018. this.getstate = function(select) {
  24019. var sel = this.files(select),
  24020. cnt = sel.length;
  24021. return cnt == 1
  24022. ? (sel[0].read? 0 : -1)
  24023. : (cnt && !fm.UA.Mobile) ? ($.grep(sel, function(file) { return file.mime == 'directory' || ! file.read ? false : true;}).length == cnt ? 0 : -1) : -1;
  24024. };
  24025. this.exec = function(hashes, cOpts) {
  24026. var dfrd = $.Deferred().fail(function(error) { error && fm.error(error); }),
  24027. files = this.files(hashes),
  24028. cnt = files.length,
  24029. thash = (typeof cOpts == 'object')? cOpts.thash : false,
  24030. opts = this.options,
  24031. into = opts.into || 'window',
  24032. file, url, s, w, imgW, imgH, winW, winH, reg, link, html5dl, inline,
  24033. selAct, cmd;
  24034. if (!cnt && !thash) {
  24035. {
  24036. return dfrd.reject();
  24037. }
  24038. }
  24039. // open folder
  24040. if (thash || (cnt == 1 && (file = files[0]) && file.mime == 'directory')) {
  24041. if (!thash && file && !file.read) {
  24042. return dfrd.reject(['errOpen', file.name, 'errPerm']);
  24043. } else {
  24044. if (fm.keyState.ctrlKey && (fm.keyState.shiftKey || typeof fm.options.getFileCallback !== 'function')) {
  24045. if (fm.getCommand('opennew')) {
  24046. return fm.exec('opennew', [thash? thash : file.hash]);
  24047. }
  24048. }
  24049. return fm.request({
  24050. data : {cmd : 'open', target : thash || file.hash},
  24051. notify : {type : 'open', cnt : 1, hideCnt : true},
  24052. syncOnFail : true,
  24053. lazy : false
  24054. });
  24055. }
  24056. }
  24057. files = $.grep(files, function(file) { return file.mime != 'directory' ? true : false; });
  24058. // nothing to open or files and folders selected - do nothing
  24059. if (cnt != files.length) {
  24060. return dfrd.reject();
  24061. }
  24062. var doOpen = function() {
  24063. var wnd, target, getOnly;
  24064. try {
  24065. reg = new RegExp(fm.option('dispInlineRegex'), 'i');
  24066. } catch(e) {
  24067. reg = false;
  24068. }
  24069. // open files
  24070. link = $('<a>').hide().appendTo($('body')),
  24071. html5dl = (typeof link.get(0).download === 'string');
  24072. cnt = files.length;
  24073. while (cnt--) {
  24074. target = 'elf_open_window';
  24075. file = files[cnt];
  24076. if (!file.read) {
  24077. return dfrd.reject(['errOpen', file.name, 'errPerm']);
  24078. }
  24079. inline = (reg && file.mime.match(reg));
  24080. url = fm.openUrl(file.hash, !inline);
  24081. if (fm.UA.Mobile || !inline) {
  24082. if (html5dl) {
  24083. if (!inline) {
  24084. link.attr('download', file.name);
  24085. } else {
  24086. link.attr('target', '_blank');
  24087. }
  24088. link.attr('href', url).get(0).click();
  24089. } else {
  24090. wnd = window.open(url);
  24091. if (!wnd) {
  24092. return dfrd.reject('errPopup');
  24093. }
  24094. }
  24095. } else {
  24096. getOnly = (typeof opts.method === 'string' && opts.method.toLowerCase() === 'get');
  24097. if (!getOnly
  24098. && url.indexOf(fm.options.url) === 0
  24099. && fm.customData
  24100. && Object.keys(fm.customData).length
  24101. // Since playback by POST request can not be done in Chrome, media allows GET request
  24102. && !file.mime.match(/^(?:video|audio)/)
  24103. ) {
  24104. // Send request as 'POST' method to hide custom data at location bar
  24105. url = '';
  24106. }
  24107. if (into === 'window') {
  24108. // set window size for image if set
  24109. imgW = winW = Math.round(2 * screen.availWidth / 3);
  24110. imgH = winH = Math.round(2 * screen.availHeight / 3);
  24111. if (parseInt(file.width) && parseInt(file.height)) {
  24112. imgW = parseInt(file.width);
  24113. imgH = parseInt(file.height);
  24114. } else if (file.dim) {
  24115. s = file.dim.split('x');
  24116. imgW = parseInt(s[0]);
  24117. imgH = parseInt(s[1]);
  24118. }
  24119. if (winW >= imgW && winH >= imgH) {
  24120. winW = imgW;
  24121. winH = imgH;
  24122. } else {
  24123. if ((imgW - winW) > (imgH - winH)) {
  24124. winH = Math.round(imgH * (winW / imgW));
  24125. } else {
  24126. winW = Math.round(imgW * (winH / imgH));
  24127. }
  24128. }
  24129. w = 'width='+winW+',height='+winH;
  24130. wnd = window.open(url, target, w + ',top=50,left=50,scrollbars=yes,resizable=yes,titlebar=no');
  24131. } else {
  24132. if (into === 'tabs') {
  24133. target = file.hash;
  24134. }
  24135. wnd = window.open('about:blank', target);
  24136. }
  24137. if (!wnd) {
  24138. return dfrd.reject('errPopup');
  24139. }
  24140. if (url === '') {
  24141. var form = document.createElement("form");
  24142. form.action = fm.options.url;
  24143. form.method = 'POST';
  24144. form.target = target;
  24145. form.style.display = 'none';
  24146. var params = Object.assign({}, fm.customData, {
  24147. cmd: 'file',
  24148. target: file.hash,
  24149. _t: file.ts || parseInt(+new Date()/1000)
  24150. });
  24151. $.each(params, function(key, val)
  24152. {
  24153. var input = document.createElement("input");
  24154. input.name = key;
  24155. input.value = val;
  24156. form.appendChild(input);
  24157. });
  24158. document.body.appendChild(form);
  24159. form.submit();
  24160. } else if (into !== 'window') {
  24161. wnd.location = url;
  24162. }
  24163. $(wnd).trigger('focus');
  24164. }
  24165. }
  24166. link.remove();
  24167. return dfrd.resolve(hashes);
  24168. };
  24169. if (cnt > 1) {
  24170. fm.confirm({
  24171. title: 'openMulti',
  24172. text : ['openMultiConfirm', cnt + ''],
  24173. accept : {
  24174. label : 'cmdopen',
  24175. callback : function() { doOpen(); }
  24176. },
  24177. cancel : {
  24178. label : 'btnCancel',
  24179. callback : function() {
  24180. dfrd.reject();
  24181. }
  24182. },
  24183. buttons : (fm.getCommand('zipdl') && fm.isCommandEnabled('zipdl', fm.cwd().hash))? [
  24184. {
  24185. label : 'cmddownload',
  24186. callback : function() {
  24187. fm.exec('download', hashes);
  24188. dfrd.reject();
  24189. }
  24190. }
  24191. ] : []
  24192. });
  24193. } else {
  24194. selAct = fm.storage('selectAction') || opts.selectAction;
  24195. if (selAct) {
  24196. $.each(selAct.split('/'), function() {
  24197. var cmdName = this.valueOf();
  24198. if (cmdName !== 'open' && (cmd = fm.getCommand(cmdName)) && cmd.enabled()) {
  24199. return false;
  24200. }
  24201. cmd = null;
  24202. });
  24203. if (cmd) {
  24204. return fm.exec(cmd.name);
  24205. }
  24206. }
  24207. doOpen();
  24208. }
  24209. return dfrd;
  24210. };
  24211. }).prototype = { forceLoad : true }; // this is required command
  24212. /*
  24213. * File: /js/commands/opendir.js
  24214. */
  24215. /**
  24216. * @class elFinder command "opendir"
  24217. * Enter parent folder
  24218. *
  24219. * @author Naoki Sawada
  24220. **/
  24221. elFinder.prototype.commands.opendir = function() {
  24222. this.alwaysEnabled = true;
  24223. this.getstate = function() {
  24224. var sel = this.fm.selected(),
  24225. cnt = sel.length,
  24226. wz;
  24227. if (cnt !== 1) {
  24228. return -1;
  24229. }
  24230. wz = this.fm.getUI('workzone');
  24231. return wz.hasClass('elfinder-search-result')? 0 : -1;
  24232. };
  24233. this.exec = function(hashes) {
  24234. var fm = this.fm,
  24235. dfrd = $.Deferred(),
  24236. files = this.files(hashes),
  24237. cnt = files.length,
  24238. hash, pcheck = null;
  24239. if (!cnt || !files[0].phash) {
  24240. return dfrd.reject();
  24241. }
  24242. hash = files[0].phash;
  24243. fm.trigger('searchend', { noupdate: true });
  24244. fm.request({
  24245. data : {cmd : 'open', target : hash},
  24246. notify : {type : 'open', cnt : 1, hideCnt : true},
  24247. syncOnFail : false
  24248. });
  24249. return dfrd;
  24250. };
  24251. };
  24252. /*
  24253. * File: /js/commands/opennew.js
  24254. */
  24255. /**
  24256. * @class elFinder command "opennew"
  24257. * Open folder in new window
  24258. *
  24259. * @author Naoki Sawada
  24260. **/
  24261. elFinder.prototype.commands.opennew = function() {
  24262. var fm = this.fm;
  24263. this.shortcuts = [{
  24264. pattern : (typeof(fm.options.getFileCallback) === 'function'? 'shift+' : '') + 'ctrl+enter'
  24265. }];
  24266. this.getstate = function(select) {
  24267. var sel = this.files(select),
  24268. cnt = sel.length;
  24269. return cnt === 1
  24270. ? (sel[0].mime === 'directory' && sel[0].read? 0 : -1)
  24271. : -1;
  24272. };
  24273. this.exec = function(hashes) {
  24274. var dfrd = $.Deferred(),
  24275. files = this.files(hashes),
  24276. cnt = files.length,
  24277. opts = this.options,
  24278. file, loc, url, win;
  24279. // open folder to new tab (window)
  24280. if (cnt === 1 && (file = files[0]) && file.mime === 'directory') {
  24281. loc = window.location;
  24282. if (opts.url) {
  24283. url = opts.url;
  24284. } else {
  24285. url = loc.pathname;
  24286. }
  24287. if (opts.useOriginQuery) {
  24288. if (!url.match(/\?/)) {
  24289. url += loc.search;
  24290. } else if (loc.search) {
  24291. url += '&' + loc.search.substr(1);
  24292. }
  24293. }
  24294. url += '#elf_' + file.hash;
  24295. win = window.open(url, '_blank');
  24296. setTimeout(function() {
  24297. win.focus();
  24298. }, 1000);
  24299. return dfrd.resolve();
  24300. } else {
  24301. return dfrd.reject();
  24302. }
  24303. };
  24304. };
  24305. /*
  24306. * File: /js/commands/paste.js
  24307. */
  24308. /**
  24309. * @class elFinder command "paste"
  24310. * Paste filesfrom clipboard into directory.
  24311. * If files pasted in its parent directory - files duplicates will created
  24312. *
  24313. * @author Dmitry (dio) Levashov
  24314. **/
  24315. elFinder.prototype.commands.paste = function() {
  24316. this.updateOnSelect = false;
  24317. this.handlers = {
  24318. changeclipboard : function() { this.update(); }
  24319. };
  24320. this.shortcuts = [{
  24321. pattern : 'ctrl+v shift+insert'
  24322. }];
  24323. this.getstate = function(dst) {
  24324. if (this._disabled) {
  24325. return -1;
  24326. }
  24327. if (dst) {
  24328. if (Array.isArray(dst)) {
  24329. if (dst.length != 1) {
  24330. return -1;
  24331. }
  24332. dst = this.fm.file(dst[0]);
  24333. }
  24334. } else {
  24335. dst = this.fm.cwd();
  24336. }
  24337. return this.fm.clipboard().length && dst.mime == 'directory' && dst.write ? 0 : -1;
  24338. };
  24339. this.exec = function(select, cOpts) {
  24340. var self = this,
  24341. fm = self.fm,
  24342. opts = cOpts || {},
  24343. dst = select ? this.files(select)[0] : fm.cwd(),
  24344. files = fm.clipboard(),
  24345. cnt = files.length,
  24346. cut = cnt ? files[0].cut : false,
  24347. cmd = opts._cmd? opts._cmd : (cut? 'move' : 'copy'),
  24348. error = 'err' + cmd.charAt(0).toUpperCase() + cmd.substr(1),
  24349. fpaste = [],
  24350. fcopy = [],
  24351. dfrd = $.Deferred()
  24352. .fail(function(error) {
  24353. error && fm.error(error);
  24354. })
  24355. .always(function() {
  24356. fm.unlockfiles({files : $.map(files, function(f) { return f.hash; })});
  24357. }),
  24358. copy = function(files) {
  24359. return files.length && fm._commands.duplicate
  24360. ? fm.exec('duplicate', files)
  24361. : $.Deferred().resolve();
  24362. },
  24363. paste = function(files) {
  24364. var dfrd = $.Deferred(),
  24365. existed = [],
  24366. hashes = {},
  24367. intersect = function(files, names) {
  24368. var ret = [],
  24369. i = files.length;
  24370. while (i--) {
  24371. $.inArray(files[i].name, names) !== -1 && ret.unshift(i);
  24372. }
  24373. return ret;
  24374. },
  24375. confirm = function(ndx) {
  24376. var i = existed[ndx],
  24377. file = files[i],
  24378. last = ndx == existed.length-1;
  24379. if (!file) {
  24380. return;
  24381. }
  24382. fm.confirm({
  24383. title : fm.i18n(cmd + 'Files'),
  24384. text : ['errExists', file.name, cmd === 'restore'? 'confirmRest' : 'confirmRepl'],
  24385. all : !last,
  24386. accept : {
  24387. label : 'btnYes',
  24388. callback : function(all) {
  24389. !last && !all
  24390. ? confirm(++ndx)
  24391. : paste(files);
  24392. }
  24393. },
  24394. reject : {
  24395. label : 'btnNo',
  24396. callback : function(all) {
  24397. var i;
  24398. if (all) {
  24399. i = existed.length;
  24400. while (ndx < i--) {
  24401. files[existed[i]].remove = true;
  24402. }
  24403. } else {
  24404. files[existed[ndx]].remove = true;
  24405. }
  24406. !last && !all
  24407. ? confirm(++ndx)
  24408. : paste(files);
  24409. }
  24410. },
  24411. cancel : {
  24412. label : 'btnCancel',
  24413. callback : function() {
  24414. dfrd.resolve();
  24415. }
  24416. },
  24417. buttons : [
  24418. {
  24419. label : 'btnBackup',
  24420. callback : function(all) {
  24421. var i;
  24422. if (all) {
  24423. i = existed.length;
  24424. while (ndx < i--) {
  24425. files[existed[i]].rename = true;
  24426. }
  24427. } else {
  24428. files[existed[ndx]].rename = true;
  24429. }
  24430. !last && !all
  24431. ? confirm(++ndx)
  24432. : paste(files);
  24433. }
  24434. }
  24435. ]
  24436. });
  24437. },
  24438. valid = function(names) {
  24439. var exists = {}, existedArr;
  24440. if (names) {
  24441. if (Array.isArray(names)) {
  24442. if (names.length) {
  24443. if (typeof names[0] == 'string') {
  24444. // elFinder <= 2.1.6 command `is` results
  24445. existed = intersect(files, names);
  24446. } else {
  24447. $.each(names, function(i, v) {
  24448. exists[v.name] = v.hash;
  24449. });
  24450. existed = intersect(files, $.map(exists, function(h, n) { return n; }));
  24451. $.each(files, function(i, file) {
  24452. if (exists[file.name]) {
  24453. hashes[exists[file.name]] = file.name;
  24454. }
  24455. });
  24456. }
  24457. }
  24458. } else {
  24459. existedArr = [];
  24460. existed = $.map(names, function(n) {
  24461. if (typeof n === 'string') {
  24462. return n;
  24463. } else {
  24464. // support to >=2.1.11 plugin Normalizer, Sanitizer
  24465. existedArr = existedArr.concat(n);
  24466. return false;
  24467. }
  24468. });
  24469. if (existedArr.length) {
  24470. existed = existed.concat(existedArr);
  24471. }
  24472. existed = intersect(files, existed);
  24473. hashes = names;
  24474. }
  24475. }
  24476. existed.length ? confirm(0) : paste(files);
  24477. },
  24478. paste = function(selFiles) {
  24479. var renames = [],
  24480. files = $.grep(selFiles, function(file) {
  24481. if (file.rename) {
  24482. renames.push(file.name);
  24483. }
  24484. return !file.remove ? true : false;
  24485. }),
  24486. cnt = files.length,
  24487. groups = {},
  24488. args = [],
  24489. targets, reqData;
  24490. if (!cnt) {
  24491. return dfrd.resolve();
  24492. }
  24493. targets = $.map(files, function(f) { return f.hash; });
  24494. reqData = {cmd : 'paste', dst : dst.hash, targets : targets, cut : cut ? 1 : 0, renames : renames, hashes : hashes, suffix : fm.options.backupSuffix};
  24495. if (fm.api < 2.1) {
  24496. reqData.src = files[0].phash;
  24497. }
  24498. fm.request({
  24499. data : reqData,
  24500. notify : {type : cmd, cnt : cnt},
  24501. navigate : {
  24502. toast : opts.noToast? {} : {
  24503. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd' + cmd)]), action: {
  24504. cmd: 'open',
  24505. msg: 'cmdopendir',
  24506. data: [dst.hash],
  24507. done: 'select',
  24508. cwdNot: dst.hash
  24509. }}
  24510. }
  24511. }
  24512. })
  24513. .done(function(data) {
  24514. var dsts = {},
  24515. added = data.added && data.added.length? data.added : null;
  24516. if (cut && added) {
  24517. // undo/redo
  24518. $.each(files, function(i, f) {
  24519. var phash = f.phash,
  24520. srcHash = function(name) {
  24521. var hash;
  24522. $.each(added, function(i, f) {
  24523. if (f.name === name) {
  24524. hash = f.hash;
  24525. return false;
  24526. }
  24527. });
  24528. return hash;
  24529. },
  24530. shash = srcHash(f.name);
  24531. if (shash) {
  24532. if (dsts[phash]) {
  24533. dsts[phash].push(shash);
  24534. } else {
  24535. dsts[phash] = [ shash ];
  24536. }
  24537. }
  24538. });
  24539. if (Object.keys(dsts).length) {
  24540. data.undo = {
  24541. cmd : 'move',
  24542. callback : function() {
  24543. var reqs = [];
  24544. $.each(dsts, function(dst, targets) {
  24545. reqs.push(fm.request({
  24546. data : {cmd : 'paste', dst : dst, targets : targets, cut : 1},
  24547. notify : {type : 'undo', cnt : targets.length}
  24548. }));
  24549. });
  24550. return $.when.apply(null, reqs);
  24551. }
  24552. };
  24553. data.redo = {
  24554. cmd : 'move',
  24555. callback : function() {
  24556. return fm.request({
  24557. data : reqData,
  24558. notify : {type : 'redo', cnt : cnt}
  24559. });
  24560. }
  24561. };
  24562. }
  24563. }
  24564. dfrd.resolve(data);
  24565. })
  24566. .fail(function() {
  24567. dfrd.reject();
  24568. })
  24569. .always(function() {
  24570. fm.unlockfiles({files : files});
  24571. });
  24572. },
  24573. internames;
  24574. if (!fm.isCommandEnabled(self.name, dst.hash) || !files.length) {
  24575. return dfrd.resolve();
  24576. }
  24577. if (fm.oldAPI) {
  24578. paste(files);
  24579. } else {
  24580. if (!fm.option('copyOverwrite', dst.hash)) {
  24581. paste(files);
  24582. } else {
  24583. internames = $.map(files, function(f) { return f.name; });
  24584. dst.hash == fm.cwd().hash
  24585. ? valid($.map(fm.files(), function(file) { return file.phash == dst.hash ? {hash: file.hash, name: file.name} : null; }))
  24586. : fm.request({
  24587. data : {cmd : 'ls', target : dst.hash, intersect : internames},
  24588. notify : {type : 'prepare', cnt : 1, hideCnt : true},
  24589. preventFail : true
  24590. })
  24591. .always(function(data) {
  24592. valid(data.list);
  24593. });
  24594. }
  24595. }
  24596. return dfrd;
  24597. },
  24598. parents, fparents;
  24599. if (!cnt || !dst || dst.mime != 'directory') {
  24600. return dfrd.reject();
  24601. }
  24602. if (!dst.write) {
  24603. return dfrd.reject([error, files[0].name, 'errPerm']);
  24604. }
  24605. parents = fm.parents(dst.hash);
  24606. $.each(files, function(i, file) {
  24607. if (!file.read) {
  24608. return !dfrd.reject([error, file.name, 'errPerm']);
  24609. }
  24610. if (cut && file.locked) {
  24611. return !dfrd.reject(['errLocked', file.name]);
  24612. }
  24613. if ($.inArray(file.hash, parents) !== -1) {
  24614. return !dfrd.reject(['errCopyInItself', file.name]);
  24615. }
  24616. if (file.mime && file.mime !== 'directory' && ! fm.uploadMimeCheck(file.mime, dst.hash)) {
  24617. return !dfrd.reject([error, file.name, 'errUploadMime']);
  24618. }
  24619. fparents = fm.parents(file.hash);
  24620. fparents.pop();
  24621. if ($.inArray(dst.hash, fparents) !== -1) {
  24622. if ($.grep(fparents, function(h) { var d = fm.file(h); return d.phash == dst.hash && d.name == file.name ? true : false; }).length) {
  24623. return !dfrd.reject(['errReplByChild', file.name]);
  24624. }
  24625. }
  24626. if (file.phash == dst.hash) {
  24627. fcopy.push(file.hash);
  24628. } else {
  24629. fpaste.push({
  24630. hash : file.hash,
  24631. phash : file.phash,
  24632. name : file.name
  24633. });
  24634. }
  24635. });
  24636. if (dfrd.state() == 'rejected') {
  24637. return dfrd;
  24638. }
  24639. $.when(
  24640. copy(fcopy),
  24641. paste(fpaste)
  24642. )
  24643. .done(function(cr, pr) {
  24644. dfrd.resolve(pr && pr.undo? pr : void(0));
  24645. })
  24646. .fail(function() {
  24647. dfrd.reject();
  24648. })
  24649. .always(function() {
  24650. cut && fm.clipboard([]);
  24651. });
  24652. return dfrd;
  24653. };
  24654. };
  24655. /*
  24656. * File: /js/commands/places.js
  24657. */
  24658. /**
  24659. * @class elFinder command "places"
  24660. * Regist to Places
  24661. *
  24662. * @author Naoki Sawada
  24663. **/
  24664. elFinder.prototype.commands.places = function() {
  24665. var self = this,
  24666. fm = this.fm,
  24667. filter = function(hashes) {
  24668. return $.grep(self.files(hashes), function(f) { return f.mime == 'directory' ? true : false; });
  24669. },
  24670. places = null;
  24671. this.getstate = function(select) {
  24672. var sel = this.hashes(select),
  24673. cnt = sel.length;
  24674. return places && cnt && cnt == filter(sel).length ? 0 : -1;
  24675. };
  24676. this.exec = function(hashes) {
  24677. var files = this.files(hashes);
  24678. places.trigger('regist', [ files ]);
  24679. return $.Deferred().resolve();
  24680. };
  24681. fm.one('load', function(){
  24682. places = fm.ui.places;
  24683. });
  24684. };
  24685. /*
  24686. * File: /js/commands/preference.js
  24687. */
  24688. /**
  24689. * @class elFinder command "preference"
  24690. * "Preference" dialog
  24691. *
  24692. * @author Naoki Sawada
  24693. **/
  24694. elFinder.prototype.commands.preference = function() {
  24695. var self = this,
  24696. fm = this.fm,
  24697. r = 'replace',
  24698. 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>',
  24699. base = $('<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-preference">'),
  24700. ul = $('<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-top">'),
  24701. tabs = $('<div class="elfinder-preference-tabs ui-tabs-panel ui-widget-content ui-corner-bottom"/>'),
  24702. sep = '<div class="elfinder-preference-separator"/>',
  24703. selfUrl = $('base').length? document.location.href.replace(/#.*$/, '') : '',
  24704. selectTab = function(tab) {
  24705. $('#'+fm.namespace+'-preference-tab-'+tab).trigger('mouseover').trigger('click');
  24706. openTab = tab;
  24707. },
  24708. clTabActive = fm.res('class', 'tabsactive'),
  24709. build = function() {
  24710. var cats = self.options.categories || {
  24711. 'language' : ['language'],
  24712. 'theme' : ['theme'],
  24713. 'toolbar' : ['toolbarPref'],
  24714. 'workspace' : ['iconSize','columnPref', 'selectAction', 'makefileTypes', 'useStoredEditor', 'editorMaximized', 'showHidden'],
  24715. 'dialog' : ['autoFocusDialog'],
  24716. 'selectionInfo' : ['infoItems', 'hashChecker'],
  24717. 'reset' : ['clearBrowserData'],
  24718. 'all' : true
  24719. },
  24720. forms = self.options.prefs || ['language', 'theme', 'toolbarPref', 'iconSize', 'columnPref', 'selectAction', 'makefileTypes', 'useStoredEditor', 'editorMaximized', 'showHidden', 'infoItems', 'hashChecker', 'autoFocusDialog', 'clearBrowserData'];
  24721. forms = fm.arrayFlip(forms, true);
  24722. if (fm.options.getFileCallback) {
  24723. delete forms.selectAction;
  24724. }
  24725. forms.language && (forms.language = (function() {
  24726. var langSel = $('<select/>').on('change', function() {
  24727. var lang = $(this).val();
  24728. fm.storage('lang', lang);
  24729. $('#'+fm.id).elfinder('reload');
  24730. }),
  24731. optTags = [],
  24732. langs = self.options.langs || {
  24733. ar: 'اللغة العربية',
  24734. bg: 'Български',
  24735. ca: 'Català',
  24736. cs: 'Čeština',
  24737. da: 'Dansk',
  24738. de: 'Deutsch',
  24739. el: 'Ελληνικά',
  24740. en: 'English',
  24741. es: 'Español',
  24742. fa: 'فارسی',
  24743. fo: 'Føroyskt',
  24744. fr: 'Français',
  24745. he: 'עברית',
  24746. hr: 'Hrvatski',
  24747. hu: 'Magyar',
  24748. id: 'Bahasa Indonesia',
  24749. it: 'Italiano',
  24750. ja: '日本語',
  24751. ko: '한국어',
  24752. nl: 'Nederlands',
  24753. no: 'Norsk',
  24754. pl: 'Polski',
  24755. pt_BR: 'Português',
  24756. ro: 'Română',
  24757. ru: 'Pусский',
  24758. si: 'සිංහල',
  24759. sk: 'Slovenčina',
  24760. sl: 'Slovenščina',
  24761. sr: 'Srpski',
  24762. sv: 'Svenska',
  24763. tr: 'Türkçe',
  24764. ug_CN: 'ئۇيغۇرچە',
  24765. uk: 'Український',
  24766. vi: 'Tiếng Việt',
  24767. zh_CN: '简体中文',
  24768. zh_TW: '正體中文'
  24769. };
  24770. $.each(langs, function(lang, name) {
  24771. optTags.push('<option value="'+lang+'">'+name+'</option>');
  24772. });
  24773. return langSel.append(optTags.join('')).val(fm.lang);
  24774. })());
  24775. forms.theme && (forms.theme = (function() {
  24776. if (!fm.options.themes || !Object.keys(fm.options.themes).length) {
  24777. return null;
  24778. }
  24779. var themeSel = $('<select/>').on('change', function() {
  24780. var theme = $(this).val();
  24781. fm.changeTheme(theme).storage('theme', theme);
  24782. }),
  24783. optTags = [],
  24784. tpl = {
  24785. image: '<img class="elfinder-preference-theme elfinder-preference-theme-image" src="$2" />',
  24786. link: '<a href="$1" target="_blank" title="$3">$2</a>',
  24787. data: '<dt>$1</dt><dd><span class="elfinder-preference-theme elfinder-preference-theme-$0">$2</span></dd>'
  24788. },
  24789. items = ['image', 'description', 'author', 'email', 'license'],
  24790. render = function(key, data) {
  24791. },
  24792. defBtn = $('<button class="ui-button ui-corner-all ui-widget elfinder-preference-theme-default"/>').text(fm.i18n('default')).on('click', function(e) {
  24793. themeSel.val('default').trigger('change');
  24794. }),
  24795. list = $('<div class="elfinder-reference-hide-taball"/>').on('click', 'button', function() {
  24796. var val = $(this).data('themeid')
  24797. themeSel.val(val).trigger('change');
  24798. });
  24799. themeSel.append('<option value="default">'+fm.i18n('default')+'</option>');
  24800. $.each(fm.options.themes, function(id, val) {
  24801. var opt = $('<option class="elfinder-theme-option-'+id+'" value="'+id+'">'+fm.i18n(id)+'</option>'),
  24802. 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>'),
  24803. tm;
  24804. themeSel.append(opt);
  24805. list.append(dsc);
  24806. tm = setTimeout(function() {
  24807. dsc.find('span.elfinder-spinner').replaceWith(fm.i18n(['errRead', id]));
  24808. }, 10000);
  24809. fm.getTheme(id).always(function() {
  24810. tm && clearTimeout(tm);
  24811. }).done(function(data) {
  24812. var link, val = $(), dl = $('<dl/>');
  24813. link = data.link? tpl.link.replace(/\$1/g, data.link).replace(/\$3/g, fm.i18n('website')) : '$2';
  24814. if (data.name) {
  24815. opt.html(fm.i18n(data.name));
  24816. }
  24817. dsc.children('legend').html(link.replace(/\$2/g, fm.i18n(data.name) || id));
  24818. $.each(items, function(i, key) {
  24819. var t = tpl[key] || tpl.data,
  24820. elm;
  24821. if (data[key]) {
  24822. elm = t.replace(/\$0/g, fm.escape(key)).replace(/\$1/g, fm.i18n(key)).replace(/\$2/g, fm.i18n(data[key]));
  24823. if (key === 'image' && data.link) {
  24824. elm = $(elm).on('click', function() {
  24825. themeSel.val(id).trigger('change');
  24826. }).attr('title', fm.i18n('select'));
  24827. }
  24828. dl.append(elm);
  24829. }
  24830. });
  24831. val = val.add(dl);
  24832. 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'))));
  24833. dsc.find('span.elfinder-spinner').replaceWith(val);
  24834. }).fail(function() {
  24835. dsc.find('span.elfinder-spinner').replaceWith(fm.i18n(['errRead', id]));
  24836. });
  24837. });
  24838. return $('<div/>').append(themeSel.val(fm.theme && fm.theme.id? fm.theme.id : 'default'), defBtn, list);
  24839. })());
  24840. forms.toolbarPref && (forms.toolbarPref = (function() {
  24841. var pnls = $.map(fm.options.uiOptions.toolbar, function(v) {
  24842. return $.isArray(v)? v : null;
  24843. }),
  24844. tags = [],
  24845. hides = fm.storage('toolbarhides') || {};
  24846. $.each(pnls, function() {
  24847. var cmd = this,
  24848. name = fm.i18n('cmd'+cmd);
  24849. if (name === 'cmd'+cmd) {
  24850. name = fm.i18n(cmd);
  24851. }
  24852. tags.push('<span class="elfinder-preference-toolbar-item"><label><input type="checkbox" value="'+cmd+'" '+(hides[cmd]? '' : 'checked')+'/>'+name+'</label></span>');
  24853. });
  24854. return $(tags.join(' ')).on('change', 'input', function() {
  24855. var v = $(this).val(),
  24856. o = $(this).is(':checked');
  24857. if (!o && !hides[v]) {
  24858. hides[v] = true;
  24859. } else if (o && hides[v]) {
  24860. delete hides[v];
  24861. }
  24862. fm.storage('toolbarhides', hides);
  24863. fm.trigger('toolbarpref');
  24864. });
  24865. })());
  24866. forms.iconSize && (forms.iconSize = (function() {
  24867. var max = fm.options.uiOptions.cwd.iconsView.sizeMax || 3,
  24868. size = fm.storage('iconsize') || 0,
  24869. sld = $('<div class="touch-punch"/>').slider({
  24870. classes: {
  24871. 'ui-slider-handle': 'elfinder-tabstop',
  24872. },
  24873. value: size,
  24874. max: max,
  24875. slide: function(e, ui) {
  24876. fm.getUI('cwd').trigger('iconpref', {size: ui.value});
  24877. },
  24878. change: function(e, ui) {
  24879. fm.storage('iconsize', ui.value);
  24880. }
  24881. });
  24882. fm.getUI('cwd').on('iconpref', function(e, data) {
  24883. sld.slider('option', 'value', data.size);
  24884. });
  24885. return sld;
  24886. })());
  24887. forms.columnPref && (forms.columnPref = (function() {
  24888. var cols = fm.options.uiOptions.cwd.listView.columns,
  24889. tags = [],
  24890. hides = fm.storage('columnhides') || {};
  24891. $.each(cols, function() {
  24892. var key = this,
  24893. name = fm.getColumnName(key);
  24894. tags.push('<span class="elfinder-preference-column-item"><label><input type="checkbox" value="'+key+'" '+(hides[key]? '' : 'checked')+'/>'+name+'</label></span>');
  24895. });
  24896. return $(tags.join(' ')).on('change', 'input', function() {
  24897. var v = $(this).val(),
  24898. o = $(this).is(':checked');
  24899. if (!o && !hides[v]) {
  24900. hides[v] = true;
  24901. } else if (o && hides[v]) {
  24902. delete hides[v];
  24903. }
  24904. fm.storage('columnhides', hides);
  24905. fm.trigger('columnpref', { repaint: true });
  24906. });
  24907. })());
  24908. forms.selectAction && (forms.selectAction = (function() {
  24909. var actSel = $('<select/>').on('change', function() {
  24910. var act = $(this).val();
  24911. fm.storage('selectAction', act === 'default'? null : act);
  24912. }),
  24913. optTags = [],
  24914. acts = self.options.selectActions,
  24915. defAct = fm.getCommand('open').options.selectAction || 'open';
  24916. if ($.inArray(defAct, acts) === -1) {
  24917. acts.unshift(defAct);
  24918. }
  24919. $.each(acts, function(i, act) {
  24920. var names = $.map(act.split('/'), function(cmd) {
  24921. var name = fm.i18n('cmd'+cmd);
  24922. if (name === 'cmd'+cmd) {
  24923. name = fm.i18n(cmd);
  24924. }
  24925. return name;
  24926. });
  24927. optTags.push('<option value="'+act+'">'+names.join('/')+'</option>');
  24928. });
  24929. return actSel.append(optTags.join('')).val(fm.storage('selectAction') || defAct);
  24930. })());
  24931. forms.makefileTypes && (forms.makefileTypes = (function() {
  24932. var hides = fm.storage('mkfileHides') || {},
  24933. getTag = function() {
  24934. var tags = [];
  24935. // re-assign hides
  24936. hides = fm.storage('mkfileHides') || {};
  24937. $.each(fm.mimesCanMakeEmpty, function(mime, type) {
  24938. var name = fm.getCommand('mkfile').getTypeName(mime, type);
  24939. tags.push('<span class="elfinder-preference-column-item" title="'+fm.escape(name)+'"><label><input type="checkbox" value="'+mime+'" '+(hides[mime]? '' : 'checked')+'/>'+type+'</label></span>');
  24940. });
  24941. return tags.join(' ');
  24942. },
  24943. elm = $('<div/>').on('change', 'input', function() {
  24944. var v = $(this).val(),
  24945. o = $(this).is(':checked');
  24946. if (!o && !hides[v]) {
  24947. hides[v] = true;
  24948. } else if (o && hides[v]) {
  24949. delete hides[v];
  24950. }
  24951. fm.storage('mkfileHides', hides);
  24952. fm.trigger('canMakeEmptyFile');
  24953. }).append(getTag()),
  24954. add = $('<div/>').append(
  24955. $('<input type="text" placeholder="'+fm.i18n('typeOfTextfile')+'"/>').on('keydown', function(e) {
  24956. (e.keyCode === $.ui.keyCode.ENTER) && $(this).next().trigger('click');
  24957. }),
  24958. $('<button class="ui-button"/>').html(fm.i18n('add')).on('click', function() {
  24959. var input = $(this).prev(),
  24960. val = input.val(),
  24961. uiToast = fm.getUI('toast'),
  24962. err = function() {
  24963. uiToast.appendTo(input.closest('.ui-dialog'));
  24964. fm.toast({
  24965. msg: fm.i18n('errUsupportType'),
  24966. mode: 'warning',
  24967. onHidden: function() {
  24968. uiToast.children().length === 1 && uiToast.appendTo(fm.getUI());
  24969. }
  24970. });
  24971. input.trigger('focus');
  24972. return false;
  24973. },
  24974. tmpMimes;
  24975. if (!val.match(/\//)) {
  24976. val = fm.arrayFlip(fm.mimeTypes)[val];
  24977. if (!val) {
  24978. return err();
  24979. }
  24980. input.val(val);
  24981. }
  24982. if (!fm.mimeIsText(val) || !fm.mimeTypes[val]) {
  24983. return err();
  24984. }
  24985. fm.trigger('canMakeEmptyFile', {mimes: [val], unshift: true});
  24986. tmpMimes = {};
  24987. tmpMimes[val] = fm.mimeTypes[val];
  24988. fm.storage('mkfileTextMimes', Object.assign(tmpMimes, fm.storage('mkfileTextMimes') || {}));
  24989. input.val('');
  24990. uiToast.appendTo(input.closest('.ui-dialog'));
  24991. fm.toast({
  24992. msg: fm.i18n(['complete', val + ' (' + tmpMimes[val] + ')']),
  24993. onHidden: function() {
  24994. uiToast.children().length === 1 && uiToast.appendTo(fm.getUI());
  24995. }
  24996. });
  24997. }),
  24998. $('<button class="ui-button"/>').html(fm.i18n('reset')).on('click', function() {
  24999. fm.one('canMakeEmptyFile', {done: function() {
  25000. elm.empty().append(getTag());
  25001. }});
  25002. fm.trigger('canMakeEmptyFile', {resetTexts: true});
  25003. })
  25004. ),
  25005. tm;
  25006. fm.bind('canMakeEmptyFile', {done: function(e) {
  25007. if (e.data && e.data.mimes && e.data.mimes.length) {
  25008. elm.empty().append(getTag());
  25009. }
  25010. }});
  25011. return $('<div/>').append(elm, add);
  25012. })());
  25013. forms.useStoredEditor && (forms.useStoredEditor = $('<input type="checkbox"/>').prop('checked', (function() {
  25014. var s = fm.storage('useStoredEditor');
  25015. return s? (s > 0) : fm.options.commandsOptions.edit.useStoredEditor;
  25016. })()).on('change', function(e) {
  25017. fm.storage('useStoredEditor', $(this).is(':checked')? 1 : -1);
  25018. }));
  25019. forms.editorMaximized && (forms.editorMaximized = $('<input type="checkbox"/>').prop('checked', (function() {
  25020. var s = fm.storage('editorMaximized');
  25021. return s? (s > 0) : fm.options.commandsOptions.edit.editorMaximized;
  25022. })()).on('change', function(e) {
  25023. fm.storage('editorMaximized', $(this).is(':checked')? 1 : -1);
  25024. }));
  25025. if (forms.showHidden) {
  25026. (function() {
  25027. var setTitle = function() {
  25028. var s = fm.storage('hide'),
  25029. t = [],
  25030. v;
  25031. if (s && s.items) {
  25032. $.each(s.items, function(h, n) {
  25033. t.push(fm.escape(n));
  25034. });
  25035. }
  25036. elms.prop('disabled', !t.length)[t.length? 'removeClass' : 'addClass']('ui-state-disabled');
  25037. v = t.length? t.join('\n') : '';
  25038. forms.showHidden.attr('title',v);
  25039. useTooltip && forms.showHidden.tooltip('option', 'content', v.replace(/\n/g, '<br>')).tooltip('close');
  25040. },
  25041. chk = $('<input type="checkbox"/>').prop('checked', (function() {
  25042. var s = fm.storage('hide');
  25043. return s && s.show;
  25044. })()).on('change', function(e) {
  25045. var o = {};
  25046. o[$(this).is(':checked')? 'show' : 'hide'] = true;
  25047. fm.exec('hide', void(0), o);
  25048. }),
  25049. btn = $('<button class="ui-button ui-corner-all ui-widget"/>').append(fm.i18n('reset')).on('click', function() {
  25050. fm.exec('hide', void(0), {reset: true});
  25051. $(this).parent().find('input:first').prop('checked', false);
  25052. setTitle();
  25053. }),
  25054. elms = $().add(chk).add(btn),
  25055. useTooltip;
  25056. forms.showHidden = $('<div/>').append(chk, btn);
  25057. fm.bind('hide', function(e) {
  25058. var d = e.data;
  25059. if (!d.opts || (!d.opts.show && !d.opts.hide)) {
  25060. setTitle();
  25061. }
  25062. });
  25063. if (fm.UA.Mobile && $.fn.tooltip) {
  25064. useTooltip = true;
  25065. forms.showHidden.tooltip({
  25066. classes: {
  25067. 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow'
  25068. },
  25069. tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow',
  25070. track: true
  25071. }).css('user-select', 'none');
  25072. btn.css('user-select', 'none');
  25073. }
  25074. setTitle();
  25075. })();
  25076. }
  25077. forms.infoItems && (forms.infoItems = (function() {
  25078. var items = fm.getCommand('info').items,
  25079. tags = [],
  25080. hides = fm.storage('infohides') || fm.arrayFlip(fm.options.commandsOptions.info.hideItems, true);
  25081. $.each(items, function() {
  25082. var key = this,
  25083. name = fm.i18n(key);
  25084. tags.push('<span class="elfinder-preference-info-item"><label><input type="checkbox" value="'+key+'" '+(hides[key]? '' : 'checked')+'/>'+name+'</label></span>');
  25085. });
  25086. return $(tags.join(' ')).on('change', 'input', function() {
  25087. var v = $(this).val(),
  25088. o = $(this).is(':checked');
  25089. if (!o && !hides[v]) {
  25090. hides[v] = true;
  25091. } else if (o && hides[v]) {
  25092. delete hides[v];
  25093. }
  25094. fm.storage('infohides', hides);
  25095. fm.trigger('infopref', { repaint: true });
  25096. });
  25097. })());
  25098. forms.hashChecker && fm.hashCheckers.length && (forms.hashChecker = (function() {
  25099. var tags = [],
  25100. enabled = fm.arrayFlip(fm.storage('hashchekcer') || fm.options.commandsOptions.info.showHashAlgorisms, true);
  25101. $.each(fm.hashCheckers, function() {
  25102. var cmd = this,
  25103. name = fm.i18n(cmd);
  25104. tags.push('<span class="elfinder-preference-hashchecker-item"><label><input type="checkbox" value="'+cmd+'" '+(enabled[cmd]? 'checked' : '')+'/>'+name+'</label></span>');
  25105. });
  25106. return $(tags.join(' ')).on('change', 'input', function() {
  25107. var v = $(this).val(),
  25108. o = $(this).is(':checked');
  25109. if (o) {
  25110. enabled[v] = true;
  25111. } else if (enabled[v]) {
  25112. delete enabled[v];
  25113. }
  25114. fm.storage('hashchekcer', $.grep(fm.hashCheckers, function(v) {
  25115. return enabled[v];
  25116. }));
  25117. });
  25118. })());
  25119. forms.autoFocusDialog && (forms.autoFocusDialog = $('<input type="checkbox"/>').prop('checked', (function() {
  25120. var s = fm.storage('autoFocusDialog');
  25121. return s? (s > 0) : fm.options.uiOptions.dialog.focusOnMouseOver;
  25122. })()).on('change', function(e) {
  25123. fm.storage('autoFocusDialog', $(this).is(':checked')? 1 : -1);
  25124. }));
  25125. forms.clearBrowserData && (forms.clearBrowserData = $('<button/>').text(fm.i18n('reset')).button().on('click', function(e) {
  25126. e.preventDefault();
  25127. fm.storage();
  25128. $('#'+fm.id).elfinder('reload');
  25129. }));
  25130. $.each(cats, function(id, prefs) {
  25131. var dls, found;
  25132. if (prefs === true) {
  25133. found = 1;
  25134. } else if (prefs) {
  25135. dls = $();
  25136. $.each(prefs, function(i, n) {
  25137. var f, title, chks = '', cbox;
  25138. if (f = forms[n]) {
  25139. found = 2;
  25140. title = fm.i18n(n);
  25141. cbox = $(f).filter('input[type="checkbox"]');
  25142. if (!cbox.length) {
  25143. cbox = $(f).find('input[type="checkbox"]');
  25144. }
  25145. if (cbox.length === 1) {
  25146. if (!cbox.attr('id')) {
  25147. cbox.attr('id', 'elfinder-preference-'+n+'-checkbox');
  25148. }
  25149. title = '<label for="'+cbox.attr('id')+'">'+title+'</label>';
  25150. } else if (cbox.length > 1) {
  25151. chks = ' elfinder-preference-checkboxes';
  25152. }
  25153. dls = dls.add($('<dt class="elfinder-preference-'+n+chks+'">'+title+'</dt>')).add($('<dd class="elfinder-preference-'+n+chks+'"/>').append(f));
  25154. }
  25155. });
  25156. }
  25157. if (found) {
  25158. ul.append(tab[r](/\{id\}/g, id)[r](/\{title\}/, fm.i18n(id))[r](/\{class\}/, openTab === id? 'elfinder-focus' : ''));
  25159. if (found === 2) {
  25160. tabs.append(
  25161. $('<div id="'+fm.namespace+'-preference-'+id+'" class="elfinder-preference-content"/>')
  25162. .hide()
  25163. .append($('<dl/>').append(dls))
  25164. );
  25165. }
  25166. }
  25167. });
  25168. ul.on('click', 'a', function(e) {
  25169. var t = $(e.target),
  25170. h = t.attr('href');
  25171. e.preventDefault();
  25172. e.stopPropagation();
  25173. ul.children().removeClass(clTabActive);
  25174. t.removeClass('ui-state-hover').parent().addClass(clTabActive);
  25175. if (h.match(/all$/)) {
  25176. tabs.addClass('elfinder-preference-taball').children().show();
  25177. } else {
  25178. tabs.removeClass('elfinder-preference-taball').children().hide();
  25179. $(h).show();
  25180. }
  25181. }).on('focus blur', 'a', function(e) {
  25182. $(this).parent().toggleClass('ui-state-focus', e.type === 'focusin');
  25183. }).on('mouseenter mouseleave', 'li', function(e) {
  25184. $(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
  25185. });
  25186. tabs.find('a,input,select,button').addClass('elfinder-tabstop');
  25187. base.append(ul, tabs);
  25188. dialog = self.fmDialog(base, {
  25189. title : self.title,
  25190. width : self.options.width || 600,
  25191. height: self.options.height || 400,
  25192. maxWidth: 'window',
  25193. maxHeight: 'window',
  25194. autoOpen : false,
  25195. destroyOnClose : false,
  25196. allowMinimize : false,
  25197. open : function() {
  25198. openTab && selectTab(openTab);
  25199. openTab = null;
  25200. },
  25201. resize : function() {
  25202. tabs.height(dialog.height() - ul.outerHeight(true) - (tabs.outerHeight(true) - tabs.height()) - 5);
  25203. }
  25204. })
  25205. .on('click', function(e) {
  25206. e.stopPropagation();
  25207. })
  25208. .css({
  25209. overflow: 'hidden'
  25210. });
  25211. dialog.closest('.ui-dialog')
  25212. .css({
  25213. overflow: 'hidden'
  25214. })
  25215. .addClass('elfinder-bg-translucent');
  25216. openTab = 'all';
  25217. },
  25218. dialog, openTab;
  25219. this.shortcuts = [{
  25220. pattern : 'ctrl+comma',
  25221. description : this.title
  25222. }];
  25223. this.alwaysEnabled = true;
  25224. this.getstate = function() {
  25225. return 0;
  25226. };
  25227. this.exec = function(sel, cOpts) {
  25228. !dialog && build();
  25229. if (cOpts) {
  25230. if (cOpts.tab) {
  25231. selectTab(cOpts.tab);
  25232. } else if (cOpts._currentType === 'cwd') {
  25233. selectTab('workspace');
  25234. }
  25235. }
  25236. dialog.elfinderdialog('open');
  25237. return $.Deferred().resolve();
  25238. };
  25239. };
  25240. /*
  25241. * File: /js/commands/quicklook.js
  25242. */
  25243. /**
  25244. * @class elFinder command "quicklook"
  25245. * Fast preview for some files types
  25246. *
  25247. * @author Dmitry (dio) Levashov
  25248. **/
  25249. (elFinder.prototype.commands.quicklook = function() {
  25250. var self = this,
  25251. fm = self.fm,
  25252. /**
  25253. * window closed state
  25254. *
  25255. * @type Number
  25256. **/
  25257. closed = 0,
  25258. /**
  25259. * window animated state
  25260. *
  25261. * @type Number
  25262. **/
  25263. animated = 1,
  25264. /**
  25265. * window opened state
  25266. *
  25267. * @type Number
  25268. **/
  25269. opened = 2,
  25270. /**
  25271. * window docked state
  25272. *
  25273. * @type Number
  25274. **/
  25275. docked = 3,
  25276. /**
  25277. * window docked and hidden state
  25278. *
  25279. * @type Number
  25280. **/
  25281. dockedhidden = 4,
  25282. /**
  25283. * window state
  25284. *
  25285. * @type Number
  25286. **/
  25287. state = closed,
  25288. /**
  25289. * Event name of update
  25290. * for fix conflicts with Prototype.JS
  25291. *
  25292. * `@see https://github.com/Studio-42/elFinder/pull/2346
  25293. * @type String
  25294. **/
  25295. evUpdate = Element.update? 'quicklookupdate' : 'update',
  25296. /**
  25297. * navbar icon class
  25298. *
  25299. * @type String
  25300. **/
  25301. navicon = 'elfinder-quicklook-navbar-icon',
  25302. /**
  25303. * navbar "fullscreen" icon class
  25304. *
  25305. * @type String
  25306. **/
  25307. fullscreen = 'elfinder-quicklook-fullscreen',
  25308. /**
  25309. * info wrapper class
  25310. *
  25311. * @type String
  25312. */
  25313. infocls = 'elfinder-quicklook-info-wrapper',
  25314. /**
  25315. * Triger keydown/keypress event with left/right arrow key code
  25316. *
  25317. * @param Number left/right arrow key code
  25318. * @return void
  25319. **/
  25320. navtrigger = function(code) {
  25321. $(document).trigger($.Event('keydown', { keyCode: code, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  25322. },
  25323. /**
  25324. * Return css for closed window
  25325. *
  25326. * @param jQuery file node in cwd
  25327. * @return void
  25328. **/
  25329. closedCss = function(node) {
  25330. var elf = fm.getUI().offset(),
  25331. base = (function() {
  25332. var target = node.find('.elfinder-cwd-file-wrapper');
  25333. return target.length? target : node;
  25334. })(),
  25335. baseOffset = base.offset() || { top: 0, left: 0 };
  25336. return {
  25337. opacity : 0,
  25338. width : base.width(),
  25339. height : base.height() - 30,
  25340. top : baseOffset.top - elf.top,
  25341. left : baseOffset.left - elf.left
  25342. };
  25343. },
  25344. /**
  25345. * Return css for opened window
  25346. *
  25347. * @return void
  25348. **/
  25349. openedCss = function() {
  25350. var contain = self.options.contain,
  25351. win = contain? fm.getUI() : $(window),
  25352. elf = fm.getUI().offset(),
  25353. w = Math.min(width, win.width()-10),
  25354. h = Math.min(height, win.height()-80);
  25355. return {
  25356. opacity : 1,
  25357. width : w,
  25358. height : h,
  25359. top : parseInt((win.height() - h - 60) / 2 + (contain? 0 : win.scrollTop() - elf.top)),
  25360. left : parseInt((win.width() - w) / 2 + (contain? 0 : win.scrollLeft() - elf.left))
  25361. };
  25362. },
  25363. mediaNode = {},
  25364. support = function(codec, name) {
  25365. var node = name || codec.substr(0, codec.indexOf('/')),
  25366. media = mediaNode[node]? mediaNode[node] : (mediaNode[node] = document.createElement(node)),
  25367. value = false;
  25368. try {
  25369. value = media.canPlayType && media.canPlayType(codec);
  25370. } catch(e) {}
  25371. return (value && value !== '' && value != 'no')? true : false;
  25372. },
  25373. platformWin = (window.navigator.platform.indexOf('Win') != -1),
  25374. /**
  25375. * Opened window width (from config)
  25376. *
  25377. * @type Number
  25378. **/
  25379. width,
  25380. /**
  25381. * Opened window height (from config)
  25382. *
  25383. * @type Number
  25384. **/
  25385. height,
  25386. /**
  25387. * Previous style before docked
  25388. *
  25389. * @type String
  25390. **/
  25391. prevStyle,
  25392. /**
  25393. * elFinder node
  25394. *
  25395. * @type jQuery
  25396. **/
  25397. parent,
  25398. /**
  25399. * elFinder current directory node
  25400. *
  25401. * @type jQuery
  25402. **/
  25403. cwd,
  25404. /**
  25405. * Current directory hash
  25406. *
  25407. * @type String
  25408. **/
  25409. cwdHash,
  25410. dockEnabled = false,
  25411. navdrag = false,
  25412. navmove = false,
  25413. navtm = null,
  25414. leftKey = $.ui.keyCode.LEFT,
  25415. rightKey = $.ui.keyCode.RIGHT,
  25416. coverEv = 'mousemove touchstart ' + ('onwheel' in document? 'wheel' : 'onmousewheel' in document? 'mousewheel' : 'DOMMouseScroll'),
  25417. title = $('<span class="elfinder-dialog-title elfinder-quicklook-title"/>'),
  25418. icon = $('<div/>'),
  25419. info = $('<div class="elfinder-quicklook-info"/>'),//.hide(),
  25420. cover = $('<div class="ui-front elfinder-quicklook-cover"/>'),
  25421. fsicon = $('<div class="'+navicon+' '+navicon+'-fullscreen"/>')
  25422. .on('click touchstart', function(e) {
  25423. if (navmove) {
  25424. return;
  25425. }
  25426. var win = self.window,
  25427. full = win.hasClass(fullscreen),
  25428. $window = $(window),
  25429. resize = function() { self.preview.trigger('changesize'); };
  25430. e.stopPropagation();
  25431. e.preventDefault();
  25432. if (full) {
  25433. navStyle = '';
  25434. navShow();
  25435. win.toggleClass(fullscreen)
  25436. .css(win.data('position'));
  25437. $window.trigger(self.resize).off(self.resize, resize);
  25438. navbar.off('mouseenter mouseleave');
  25439. cover.off(coverEv);
  25440. } else {
  25441. win.toggleClass(fullscreen)
  25442. .data('position', {
  25443. left : win.css('left'),
  25444. top : win.css('top'),
  25445. width : win.width(),
  25446. height : win.height(),
  25447. display: 'block'
  25448. })
  25449. .removeAttr('style');
  25450. $(window).on(self.resize, resize)
  25451. .trigger(self.resize);
  25452. cover.on(coverEv, function(e) {
  25453. if (! navdrag) {
  25454. if (e.type === 'mousemove' || e.type === 'touchstart') {
  25455. navShow();
  25456. navtm = setTimeout(function() {
  25457. if (fm.UA.Mobile || navbar.parent().find('.elfinder-quicklook-navbar:hover').length < 1) {
  25458. navbar.fadeOut('slow', function() {
  25459. cover.show();
  25460. });
  25461. }
  25462. }, 3000);
  25463. }
  25464. if (cover.is(':visible')) {
  25465. coverHide();
  25466. cover.data('tm', setTimeout(function() {
  25467. cover.show();
  25468. }, 3000));
  25469. }
  25470. }
  25471. }).show().trigger('mousemove');
  25472. navbar.on('mouseenter mouseleave', function(e) {
  25473. if (! navdrag) {
  25474. if (e.type === 'mouseenter') {
  25475. navShow();
  25476. } else {
  25477. cover.trigger('mousemove');
  25478. }
  25479. }
  25480. });
  25481. }
  25482. if (fm.zIndex) {
  25483. win.css('z-index', fm.zIndex + 1);
  25484. }
  25485. if (fm.UA.Mobile) {
  25486. navbar.attr('style', navStyle);
  25487. } else {
  25488. navbar.attr('style', navStyle).draggable(full ? 'destroy' : {
  25489. start: function() {
  25490. navdrag = true;
  25491. navmove = true;
  25492. cover.show();
  25493. navShow();
  25494. },
  25495. stop: function() {
  25496. navdrag = false;
  25497. navStyle = self.navbar.attr('style');
  25498. requestAnimationFrame(function() {
  25499. navmove = false;
  25500. });
  25501. }
  25502. });
  25503. }
  25504. $(this).toggleClass(navicon+'-fullscreen-off');
  25505. var collection = win;
  25506. if (parent.is('.ui-resizable')) {
  25507. collection = collection.add(parent);
  25508. }
  25509. collection.resizable(full ? 'enable' : 'disable').removeClass('ui-state-disabled');
  25510. win.trigger('viewchange');
  25511. }
  25512. ),
  25513. updateOnSel = function() {
  25514. self.update(void(0), (function() {
  25515. var fm = self.fm,
  25516. files = fm.selectedFiles(),
  25517. cnt = files.length,
  25518. inDock = self.docked(),
  25519. getInfo = function() {
  25520. var ts = 0;
  25521. $.each(files, function(i, f) {
  25522. var t = parseInt(f.ts);
  25523. if (ts >= 0) {
  25524. if (t > ts) {
  25525. ts = t;
  25526. }
  25527. } else {
  25528. ts = 'unknown';
  25529. }
  25530. });
  25531. return {
  25532. hash : files[0].hash + '/' + (+new Date()),
  25533. name : fm.i18n('items') + ': ' + cnt,
  25534. mime : 'group',
  25535. size : spinner,
  25536. ts : ts,
  25537. files : $.map(files, function(f) { return f.hash; }),
  25538. getSize : true
  25539. };
  25540. };
  25541. if (! cnt) {
  25542. cnt = 1;
  25543. files = [fm.cwd()];
  25544. }
  25545. return (cnt === 1)? files[0] : getInfo();
  25546. })());
  25547. },
  25548. navShow = function() {
  25549. if (self.window.hasClass(fullscreen)) {
  25550. navtm && clearTimeout(navtm);
  25551. navtm = null;
  25552. // if use `show()` it make infinite loop with old jQuery (jQuery/jQuery UI: 1.8.0/1.9.0)
  25553. // see #1478 https://github.com/Studio-42/elFinder/issues/1478
  25554. navbar.stop(true, true).css('display', 'block');
  25555. coverHide();
  25556. }
  25557. },
  25558. coverHide = function() {
  25559. cover.data('tm') && clearTimeout(cover.data('tm'));
  25560. cover.removeData('tm');
  25561. cover.hide();
  25562. },
  25563. prev = $('<div class="'+navicon+' '+navicon+'-prev"/>').on('click touchstart', function(e) { ! navmove && navtrigger(leftKey); return false; }),
  25564. next = $('<div class="'+navicon+' '+navicon+'-next"/>').on('click touchstart', function(e) { ! navmove && navtrigger(rightKey); return false; }),
  25565. navbar = $('<div class="elfinder-quicklook-navbar"/>')
  25566. .append(prev)
  25567. .append(fsicon)
  25568. .append(next)
  25569. .append('<div class="elfinder-quicklook-navbar-separator"/>')
  25570. .append($('<div class="'+navicon+' '+navicon+'-close"/>').on('click touchstart', function(e) { ! navmove && self.window.trigger('close'); return false; }))
  25571. ,
  25572. titleClose = $('<span class="ui-front ui-icon elfinder-icon-close ui-icon-closethick"/>').on('mousedown', function(e) {
  25573. e.stopPropagation();
  25574. self.window.trigger('close');
  25575. }),
  25576. titleDock = $('<span class="ui-front ui-icon elfinder-icon-minimize ui-icon-minusthick"/>').on('mousedown', function(e) {
  25577. e.stopPropagation();
  25578. if (! self.docked()) {
  25579. self.window.trigger('navdockin');
  25580. } else {
  25581. self.window.trigger('navdockout');
  25582. }
  25583. }),
  25584. spinner = '<span class="elfinder-spinner-text">' + fm.i18n('calc') + '</span>' + '<span class="elfinder-spinner"/>',
  25585. navStyle = '',
  25586. init = true,
  25587. dockHeight, getSize, tm4cwd, dockedNode, selectTm;
  25588. this.cover = cover;
  25589. this.evUpdate = evUpdate;
  25590. (this.navbar = navbar)._show = navShow;
  25591. this.resize = 'resize.'+fm.namespace;
  25592. this.info = $('<div/>').addClass(infocls)
  25593. .append(icon)
  25594. .append(info);
  25595. this.autoPlay = function() {
  25596. if (self.opened()) {
  25597. return !! self.options[self.docked()? 'dockAutoplay' : 'autoplay'];
  25598. }
  25599. return false;
  25600. };
  25601. this.preview = $('<div class="elfinder-quicklook-preview ui-helper-clearfix"/>')
  25602. // clean info/icon
  25603. .on('change', function() {
  25604. navShow();
  25605. navbar.attr('style', navStyle);
  25606. self.docked() && navbar.hide();
  25607. self.preview.attr('style', '').removeClass('elfinder-overflow-auto');
  25608. self.info.attr('style', '').hide();
  25609. self.cover.removeClass('elfinder-quicklook-coverbg');
  25610. icon.removeAttr('class').attr('style', '');
  25611. info.html('');
  25612. })
  25613. // update info/icon
  25614. .on(evUpdate, function(e) {
  25615. var preview = self.preview,
  25616. file = e.file,
  25617. tpl = '<div class="elfinder-quicklook-info-data">{value}</div>',
  25618. update = function() {
  25619. var win = self.window.css('overflow', 'hidden');
  25620. name = fm.escape(file.i18 || file.name);
  25621. !file.read && e.stopImmediatePropagation();
  25622. self.window.data('hash', file.hash);
  25623. self.preview.off('changesize').trigger('change').children().remove();
  25624. title.html(name);
  25625. prev.css('visibility', '');
  25626. next.css('visibility', '');
  25627. if (file.hash === fm.cwdId2Hash(cwd.find('[id]:not(.elfinder-cwd-parent):first').attr('id'))) {
  25628. prev.css('visibility', 'hidden');
  25629. }
  25630. if (file.hash === fm.cwdId2Hash(cwd.find('[id]:last').attr('id'))) {
  25631. next.css('visibility', 'hidden');
  25632. }
  25633. if (file.mime === 'directory') {
  25634. getSizeHashes = [ file.hash ];
  25635. } else if (file.mime === 'group' && file.getSize) {
  25636. getSizeHashes = file.files;
  25637. }
  25638. info.html(
  25639. tpl.replace(/\{value\}/, name)
  25640. + tpl.replace(/\{value\}/, fm.mime2kind(file))
  25641. + tpl.replace(/\{value\}/, getSizeHashes.length ? spinner : fm.formatSize(file.size))
  25642. + tpl.replace(/\{value\}/, fm.i18n('modify')+': '+ fm.formatDate(file))
  25643. );
  25644. if (getSizeHashes.length) {
  25645. getSize = fm.getSize(getSizeHashes).done(function(data) {
  25646. info.find('span.elfinder-spinner').parent().html(data.formated);
  25647. }).fail(function() {
  25648. info.find('span.elfinder-spinner').parent().html(fm.i18n('unknown'));
  25649. }).always(function() {
  25650. getSize = null;
  25651. });
  25652. getSize._hash = file.hash;
  25653. }
  25654. icon.addClass('elfinder-cwd-icon ui-corner-all '+fm.mime2class(file.mime));
  25655. if (file.icon) {
  25656. icon.css(fm.getIconStyle(file, true));
  25657. }
  25658. self.info.attr('class', infocls);
  25659. if (file.csscls) {
  25660. self.info.addClass(file.csscls);
  25661. }
  25662. if (file.read && (tmb = fm.tmb(file))) {
  25663. $('<img/>')
  25664. .hide()
  25665. .appendTo(self.preview)
  25666. .on('load', function() {
  25667. icon.addClass(tmb.className).css('background-image', "url('"+tmb.url+"')");
  25668. $(this).remove();
  25669. })
  25670. .attr('src', tmb.url);
  25671. }
  25672. self.info.delay(100).fadeIn(10);
  25673. if (self.window.hasClass(fullscreen)) {
  25674. cover.trigger('mousemove');
  25675. }
  25676. win.css('overflow', '');
  25677. },
  25678. tmb, name, getSizeHashes = [];
  25679. if (file && ! Object.keys(file).length) {
  25680. file = fm.cwd();
  25681. }
  25682. if (file && getSize && getSize.state() === 'pending' && getSize._hash !== file.hash) {
  25683. getSize.reject();
  25684. }
  25685. if (file && (e.forceUpdate || self.window.data('hash') !== file.hash)) {
  25686. update();
  25687. } else {
  25688. e.stopImmediatePropagation();
  25689. }
  25690. });
  25691. this.window = $('<div class="ui-front ui-helper-reset ui-widget elfinder-quicklook touch-punch" style="position:absolute"/>')
  25692. .hide()
  25693. .addClass(fm.UA.Touch? 'elfinder-touch' : '')
  25694. .on('click', function(e) {
  25695. var win = this;
  25696. e.stopPropagation();
  25697. if (state === opened) {
  25698. requestAnimationFrame(function() {
  25699. state === opened && fm.toFront(win);
  25700. });
  25701. }
  25702. })
  25703. .append(
  25704. $('<div class="ui-dialog-titlebar ui-widget-header ui-corner-top ui-helper-clearfix elfinder-quicklook-titlebar"/>')
  25705. .append(
  25706. $('<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(
  25707. titleClose, titleDock
  25708. ),
  25709. title
  25710. ),
  25711. this.preview,
  25712. self.info.hide(),
  25713. cover.hide(),
  25714. navbar
  25715. )
  25716. .draggable({handle : 'div.elfinder-quicklook-titlebar'})
  25717. .on('open', function(e, clcss) {
  25718. var win = self.window,
  25719. file = self.value,
  25720. node = fm.getUI('cwd'),
  25721. open = function(status) {
  25722. state = status;
  25723. self.update(1, self.value);
  25724. self.change();
  25725. win.trigger('resize.' + fm.namespace);
  25726. };
  25727. if (!init && state === closed) {
  25728. if (file && file.hash !== cwdHash) {
  25729. node = $('#'+fm.cwdHash2Id(file.hash.split('/', 2)[0]));
  25730. }
  25731. navStyle = '';
  25732. navbar.attr('style', '');
  25733. state = animated;
  25734. node.trigger('scrolltoview');
  25735. coverHide();
  25736. win.css(clcss || closedCss(node))
  25737. .show()
  25738. .animate(openedCss(), 550, function() {
  25739. open(opened);
  25740. navShow();
  25741. });
  25742. fm.toFront(win);
  25743. } else if (state === dockedhidden) {
  25744. fm.getUI('navdock').data('addNode')(dockedNode);
  25745. open(docked);
  25746. self.preview.trigger('changesize');
  25747. fm.storage('previewDocked', '1');
  25748. if (fm.getUI('navdock').width() === 0) {
  25749. win.trigger('navdockout');
  25750. }
  25751. }
  25752. })
  25753. .on('close', function(e, dfd) {
  25754. var win = self.window,
  25755. preview = self.preview.trigger('change'),
  25756. file = self.value,
  25757. hash = (win.data('hash') || '').split('/', 2)[0],
  25758. close = function(status, winhide) {
  25759. state = status;
  25760. winhide && fm.toHide(win);
  25761. preview.children().remove();
  25762. self.update(0, self.value);
  25763. win.data('hash', '');
  25764. dfd && dfd.resolve();
  25765. },
  25766. node;
  25767. if (self.opened()) {
  25768. getSize && getSize.state() === 'pending' && getSize.reject();
  25769. if (! self.docked()) {
  25770. state = animated;
  25771. win.hasClass(fullscreen) && fsicon.click();
  25772. (hash && (node = cwd.find('#'+hash)).length)
  25773. ? win.animate(closedCss(node), 500, function() { close(closed, true); })
  25774. : close(closed, true);
  25775. } else {
  25776. dockedNode = fm.getUI('navdock').data('removeNode')(self.window.attr('id'), 'detach');
  25777. close(dockedhidden);
  25778. fm.storage('previewDocked', '2');
  25779. }
  25780. }
  25781. })
  25782. .on('navdockin', function(e, data) {
  25783. var w = self.window,
  25784. box = fm.getUI('navdock'),
  25785. height = dockHeight || box.width(),
  25786. opts = data || {};
  25787. if (init) {
  25788. opts.init = true;
  25789. }
  25790. state = docked;
  25791. prevStyle = w.attr('style');
  25792. w.toggleClass('ui-front').removeClass('ui-widget').draggable('disable').resizable('disable').removeAttr('style').css({
  25793. width: '100%',
  25794. height: height,
  25795. boxSizing: 'border-box',
  25796. paddingBottom: 0,
  25797. zIndex: 'unset'
  25798. });
  25799. navbar.hide();
  25800. titleDock.toggleClass('ui-icon-plusthick ui-icon-minusthick elfinder-icon-full elfinder-icon-minimize');
  25801. fm.toHide(w, true);
  25802. box.data('addNode')(w, opts);
  25803. self.preview.trigger('changesize');
  25804. fm.storage('previewDocked', '1');
  25805. })
  25806. .on('navdockout', function(e) {
  25807. var w = self.window,
  25808. box = fm.getUI('navdock'),
  25809. dfd = $.Deferred(),
  25810. clcss = closedCss(self.preview);
  25811. dockHeight = w.outerHeight();
  25812. box.data('removeNode')(w.attr('id'), fm.getUI());
  25813. w.toggleClass('ui-front').addClass('ui-widget').draggable('enable').resizable('enable').attr('style', prevStyle);
  25814. titleDock.toggleClass('ui-icon-plusthick ui-icon-minusthick elfinder-icon-full elfinder-icon-minimize');
  25815. state = closed;
  25816. w.trigger('open', clcss);
  25817. fm.storage('previewDocked', '0');
  25818. })
  25819. .on('resize.' + fm.namespace, function() {
  25820. self.preview.trigger('changesize');
  25821. });
  25822. /**
  25823. * This command cannot be disable by backend
  25824. *
  25825. * @type Boolean
  25826. **/
  25827. this.alwaysEnabled = true;
  25828. /**
  25829. * Selected file
  25830. *
  25831. * @type Object
  25832. **/
  25833. this.value = null;
  25834. this.handlers = {
  25835. // save selected file
  25836. select : function(e, d) {
  25837. selectTm && cancelAnimationFrame(selectTm);
  25838. if (! e.data || ! e.data.selected || ! e.data.selected.length) {
  25839. selectTm = requestAnimationFrame(function() {
  25840. self.opened() && updateOnSel();
  25841. });
  25842. } else {
  25843. self.opened() && updateOnSel();
  25844. }
  25845. },
  25846. error : function() { self.window.is(':visible') && self.window.trigger('close'); },
  25847. 'searchshow searchhide' : function() { this.opened() && this.window.trigger('close'); },
  25848. navbarshow : function() {
  25849. requestAnimationFrame(function() {
  25850. self.docked() && self.preview.trigger('changesize');
  25851. });
  25852. },
  25853. destroy : function() { self.window.remove(); }
  25854. };
  25855. this.shortcuts = [{
  25856. pattern : 'space'
  25857. }];
  25858. this.support = {
  25859. audio : {
  25860. ogg : support('audio/ogg;'),
  25861. webm: support('audio/webm;'),
  25862. mp3 : support('audio/mpeg;'),
  25863. wav : support('audio/wav;'),
  25864. m4a : support('audio/mp4;') || support('audio/x-m4a;') || support('audio/aac;'),
  25865. flac: support('audio/flac;'),
  25866. amr : support('audio/amr;')
  25867. },
  25868. video : {
  25869. ogg : support('video/ogg;'),
  25870. webm : support('video/webm;'),
  25871. mp4 : support('video/mp4;'),
  25872. mkv : support('video/x-matroska;') || support('video/webm;'),
  25873. '3gp': support('video/3gpp;') || support('video/mp4;'), // try as mp4
  25874. m3u8 : support('application/x-mpegURL', 'video') || support('application/vnd.apple.mpegURL', 'video'),
  25875. mpd : support('application/dash+xml', 'video')
  25876. }
  25877. };
  25878. // for GC
  25879. mediaNode = {};
  25880. /**
  25881. * Return true if quickLoock window is hiddenReturn true if quickLoock window is visible and not animated
  25882. *
  25883. * @return Boolean
  25884. **/
  25885. this.closed = function() {
  25886. return (state == closed || state == dockedhidden);
  25887. };
  25888. /**
  25889. * Return true if quickLoock window is visible and not animated
  25890. *
  25891. * @return Boolean
  25892. **/
  25893. this.opened = function() {
  25894. return state == opened || state == docked;
  25895. };
  25896. /**
  25897. * Return true if quickLoock window is in NavDock
  25898. *
  25899. * @return Boolean
  25900. **/
  25901. this.docked = function() {
  25902. return state == docked;
  25903. };
  25904. /**
  25905. * Adds an integration into help dialog.
  25906. *
  25907. * @param Object opts options
  25908. */
  25909. this.addIntegration = function(opts) {
  25910. requestAnimationFrame(function() {
  25911. fm.trigger('helpIntegration', Object.assign({cmd: 'quicklook'}, opts));
  25912. });
  25913. };
  25914. /**
  25915. * Init command.
  25916. * Add default plugins and init other plugins
  25917. *
  25918. * @return Object
  25919. **/
  25920. this.init = function() {
  25921. var o = this.options,
  25922. win = this.window,
  25923. preview = this.preview,
  25924. i, p, cwdDispInlineRegex;
  25925. width = o.width > 0 ? parseInt(o.width) : 450;
  25926. height = o.height > 0 ? parseInt(o.height) : 300;
  25927. if (o.dockHeight !== 'auto') {
  25928. dockHeight = parseInt(o.dockHeight);
  25929. if (! dockHeight) {
  25930. dockHeight = void(0);
  25931. }
  25932. }
  25933. fm.one('load', function() {
  25934. dockEnabled = fm.getUI('navdock').data('dockEnabled');
  25935. ! dockEnabled && titleDock.hide();
  25936. parent = fm.getUI();
  25937. cwd = fm.getUI('cwd');
  25938. if (fm.zIndex) {
  25939. win.css('z-index', fm.zIndex + 1);
  25940. }
  25941. win.appendTo(parent);
  25942. // close window on escape
  25943. $(document).on('keydown.'+fm.namespace, function(e) {
  25944. e.keyCode == $.ui.keyCode.ESCAPE && self.opened() && ! self.docked() && win.hasClass('elfinder-frontmost') && win.trigger('close');
  25945. });
  25946. win.resizable({
  25947. handles : 'se',
  25948. minWidth : 350,
  25949. minHeight : 120,
  25950. resize : function() {
  25951. // use another event to avoid recursion in fullscreen mode
  25952. // may be there is clever solution, but i cant find it :(
  25953. preview.trigger('changesize');
  25954. }
  25955. });
  25956. self.change(function() {
  25957. if (self.opened()) {
  25958. if (self.value) {
  25959. if (self.value.tmb && self.value.tmb == 1) {
  25960. // try re-get file object
  25961. self.value = Object.assign({}, fm.file(self.value.hash));
  25962. }
  25963. preview.trigger($.Event(evUpdate, {file : self.value}));
  25964. }
  25965. }
  25966. });
  25967. preview.on(evUpdate, function(e) {
  25968. var file, hash, serach;
  25969. if (file = e.file) {
  25970. hash = file.hash;
  25971. serach = (fm.searchStatus.mixed && fm.searchStatus.state > 1);
  25972. if (file.mime !== 'directory') {
  25973. if (parseInt(file.size) || file.mime.match(o.mimeRegexNotEmptyCheck)) {
  25974. // set current dispInlineRegex
  25975. self.dispInlineRegex = cwdDispInlineRegex;
  25976. if (serach || fm.optionsByHashes[hash]) {
  25977. try {
  25978. self.dispInlineRegex = new RegExp(fm.option('dispInlineRegex', hash), 'i');
  25979. } catch(e) {
  25980. try {
  25981. self.dispInlineRegex = new RegExp(!fm.isRoot(file)? fm.option('dispInlineRegex', file.phash) : fm.options.dispInlineRegex, 'i');
  25982. } catch(e) {
  25983. self.dispInlineRegex = /^$/;
  25984. }
  25985. }
  25986. }
  25987. } else {
  25988. // do not preview of file that size = 0
  25989. e.stopImmediatePropagation();
  25990. }
  25991. } else {
  25992. self.dispInlineRegex = /^$/;
  25993. }
  25994. self.info.show();
  25995. } else {
  25996. e.stopImmediatePropagation();
  25997. }
  25998. });
  25999. $.each(fm.commands.quicklook.plugins || [], function(i, plugin) {
  26000. if (typeof(plugin) == 'function') {
  26001. new plugin(self);
  26002. }
  26003. });
  26004. }).one('open', function() {
  26005. var dock = Number(fm.storage('previewDocked') || o.docked),
  26006. win;
  26007. if (dockEnabled && dock >= 1) {
  26008. win = self.window;
  26009. self.exec();
  26010. win.trigger('navdockin', { init : true });
  26011. if (dock === 2) {
  26012. win.trigger('close');
  26013. } else {
  26014. self.update(void(0), fm.cwd());
  26015. self.change();
  26016. }
  26017. }
  26018. init = false;
  26019. }).bind('open', function() {
  26020. cwdHash = fm.cwd().hash;
  26021. self.value = fm.cwd();
  26022. // set current volume dispInlineRegex
  26023. try {
  26024. cwdDispInlineRegex = new RegExp(fm.option('dispInlineRegex'), 'i');
  26025. } catch(e) {
  26026. cwdDispInlineRegex = /^$/;
  26027. }
  26028. }).bind('change', function(e) {
  26029. if (e.data && e.data.changed && self.opened()) {
  26030. $.each(e.data.changed, function() {
  26031. if (self.window.data('hash') === this.hash) {
  26032. self.window.data('hash', null);
  26033. self.preview.trigger(evUpdate);
  26034. return false;
  26035. }
  26036. });
  26037. }
  26038. }).bind('navdockresizestart navdockresizestop', function(e) {
  26039. cover[e.type === 'navdockresizestart'? 'show' : 'hide']();
  26040. });
  26041. };
  26042. this.getstate = function() {
  26043. return self.opened()? 1 : 0;
  26044. };
  26045. this.exec = function() {
  26046. self.closed() && updateOnSel();
  26047. self.enabled() && self.window.trigger(self.opened() ? 'close' : 'open');
  26048. return $.Deferred().resolve();
  26049. };
  26050. this.hideinfo = function() {
  26051. this.info.stop(true, true).hide();
  26052. };
  26053. }).prototype = { forceLoad : true }; // this is required command
  26054. /*
  26055. * File: /js/commands/quicklook.plugins.js
  26056. */
  26057. elFinder.prototype.commands.quicklook.plugins = [
  26058. /**
  26059. * Images preview plugin
  26060. *
  26061. * @param elFinder.commands.quicklook
  26062. **/
  26063. function(ql) {
  26064. var mimes = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/x-ms-bmp'],
  26065. preview = ql.preview,
  26066. WebP, flipMime;
  26067. // webp support
  26068. WebP = new Image();
  26069. WebP.onload = WebP.onerror = function() {
  26070. if (WebP.height == 2) {
  26071. mimes.push('image/webp');
  26072. }
  26073. };
  26074. WebP.src='data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
  26075. // what kind of images we can display
  26076. $.each(navigator.mimeTypes, function(i, o) {
  26077. var mime = o.type;
  26078. if (mime.indexOf('image/') === 0 && $.inArray(mime, mimes)) {
  26079. mimes.push(mime);
  26080. }
  26081. });
  26082. preview.on(ql.evUpdate, function(e) {
  26083. var fm = ql.fm,
  26084. file = e.file,
  26085. showed = false,
  26086. dimreq = null,
  26087. setdim = function(dim) {
  26088. var rfile = fm.file(file.hash);
  26089. rfile.width = dim[0];
  26090. rfile.height = dim[1];
  26091. },
  26092. show = function() {
  26093. var elm, varelm, memSize, width, height, prop;
  26094. dimreq && dimreq.state && dimreq.state() === 'pending' && dimreq.reject();
  26095. if (showed) {
  26096. return;
  26097. }
  26098. showed = true;
  26099. elm = img.get(0);
  26100. memSize = file.width && file.height? {w: file.width, h: file.height} : (elm.naturalWidth? null : {w: img.width(), h: img.height()});
  26101. memSize && img.removeAttr('width').removeAttr('height');
  26102. width = file.width || elm.naturalWidth || elm.width || img.width();
  26103. height = file.height || elm.naturalHeight || elm.height || img.height();
  26104. if (!file.width || !file.height) {
  26105. setdim([width, height]);
  26106. }
  26107. memSize && img.width(memSize.w).height(memSize.h);
  26108. prop = (width/height).toFixed(2);
  26109. preview.on('changesize', function() {
  26110. var pw = parseInt(preview.width()),
  26111. ph = parseInt(preview.height()),
  26112. w, h;
  26113. if (prop < (pw/ph).toFixed(2)) {
  26114. h = ph;
  26115. w = Math.floor(h * prop);
  26116. } else {
  26117. w = pw;
  26118. h = Math.floor(w/prop);
  26119. }
  26120. img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0);
  26121. })
  26122. .trigger('changesize');
  26123. //show image
  26124. img.fadeIn(100);
  26125. },
  26126. hideInfo = function() {
  26127. loading.remove();
  26128. // hide info/icon
  26129. ql.hideinfo();
  26130. },
  26131. url, img, loading, m;
  26132. if (!flipMime) {
  26133. flipMime = fm.arrayFlip(mimes);
  26134. }
  26135. if (flipMime[file.mime] && ql.dispInlineRegex.test(file.mime)) {
  26136. // this is our file - stop event propagation
  26137. e.stopImmediatePropagation();
  26138. 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'));
  26139. url = fm.openUrl(file.hash);
  26140. img = $('<img/>')
  26141. .hide()
  26142. .appendTo(preview)
  26143. .on('load', function() {
  26144. hideInfo();
  26145. show();
  26146. })
  26147. .on('error', function() {
  26148. loading.remove();
  26149. })
  26150. .attr('src', url);
  26151. if (file.width && file.height) {
  26152. show();
  26153. } else if (file.size > (ql.options.getDimThreshold || 0)) {
  26154. dimreq = fm.request({
  26155. data : {cmd : 'dim', target : file.hash},
  26156. preventDefault : true
  26157. })
  26158. .done(function(data) {
  26159. if (data.dim) {
  26160. var dim = data.dim.split('x');
  26161. file.width = dim[0];
  26162. file.height = dim[1];
  26163. setdim(dim);
  26164. show();
  26165. }
  26166. });
  26167. }
  26168. }
  26169. });
  26170. },
  26171. /**
  26172. * PSD(Adobe Photoshop data) preview plugin
  26173. *
  26174. * @param elFinder.commands.quicklook
  26175. **/
  26176. function(ql) {
  26177. var fm = ql.fm,
  26178. mimes = fm.arrayFlip(['image/vnd.adobe.photoshop', 'image/x-photoshop']),
  26179. preview = ql.preview,
  26180. load = function(url, img, loading) {
  26181. try {
  26182. fm.replaceXhrSend();
  26183. PSD.fromURL(url).then(function(psd) {
  26184. var prop;
  26185. img.attr('src', psd.image.toBase64());
  26186. requestAnimationFrame(function() {
  26187. prop = (img.width()/img.height()).toFixed(2);
  26188. preview.on('changesize', function() {
  26189. var pw = parseInt(preview.width()),
  26190. ph = parseInt(preview.height()),
  26191. w, h;
  26192. if (prop < (pw/ph).toFixed(2)) {
  26193. h = ph;
  26194. w = Math.floor(h * prop);
  26195. } else {
  26196. w = pw;
  26197. h = Math.floor(w/prop);
  26198. }
  26199. img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0);
  26200. }).trigger('changesize');
  26201. loading.remove();
  26202. // hide info/icon
  26203. ql.hideinfo();
  26204. //show image
  26205. img.fadeIn(100);
  26206. });
  26207. }, function() {
  26208. loading.remove();
  26209. img.remove();
  26210. });
  26211. fm.restoreXhrSend();
  26212. } catch(e) {
  26213. fm.restoreXhrSend();
  26214. loading.remove();
  26215. img.remove();
  26216. }
  26217. },
  26218. PSD;
  26219. preview.on(ql.evUpdate, function(e) {
  26220. var file = e.file,
  26221. url, img, loading, m,
  26222. _define, _require;
  26223. if (mimes[file.mime] && fm.options.cdns.psd && ! fm.UA.ltIE10 && ql.dispInlineRegex.test(file.mime)) {
  26224. // this is our file - stop event propagation
  26225. e.stopImmediatePropagation();
  26226. 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'));
  26227. url = fm.openUrl(file.hash);
  26228. if (!fm.isSameOrigin(url)) {
  26229. url = fm.openUrl(file.hash, true);
  26230. }
  26231. img = $('<img/>').hide().appendTo(preview);
  26232. if (PSD) {
  26233. load(url, img, loading);
  26234. } else {
  26235. _define = window.define;
  26236. _require = window.require;
  26237. window.require = null;
  26238. window.define = null;
  26239. fm.loadScript(
  26240. [ fm.options.cdns.psd ],
  26241. function() {
  26242. PSD = require('psd');
  26243. _define? (window.define = _define) : (delete window.define);
  26244. _require? (window.require = _require) : (delete window.require);
  26245. load(url, img, loading);
  26246. }
  26247. );
  26248. }
  26249. }
  26250. });
  26251. },
  26252. /**
  26253. * HTML preview plugin
  26254. *
  26255. * @param elFinder.commands.quicklook
  26256. **/
  26257. function(ql) {
  26258. var fm = ql.fm,
  26259. mimes = fm.arrayFlip(['text/html', 'application/xhtml+xml']),
  26260. preview = ql.preview;
  26261. preview.on(ql.evUpdate, function(e) {
  26262. var file = e.file, jqxhr, loading;
  26263. if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) {
  26264. e.stopImmediatePropagation();
  26265. 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'));
  26266. // stop loading on change file if not loaded yet
  26267. preview.one('change', function() {
  26268. jqxhr.state() == 'pending' && jqxhr.reject();
  26269. }).addClass('elfinder-overflow-auto');
  26270. jqxhr = fm.request({
  26271. data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts},
  26272. options : {type: 'get', cache : true},
  26273. preventDefault : true
  26274. })
  26275. .done(function(data) {
  26276. ql.hideinfo();
  26277. var doc = $('<iframe class="elfinder-quicklook-preview-html"/>').appendTo(preview)[0].contentWindow.document;
  26278. doc.open();
  26279. doc.write(data.content);
  26280. doc.close();
  26281. })
  26282. .always(function() {
  26283. loading.remove();
  26284. });
  26285. }
  26286. });
  26287. },
  26288. /**
  26289. * MarkDown preview plugin
  26290. *
  26291. * @param elFinder.commands.quicklook
  26292. **/
  26293. function(ql) {
  26294. var fm = ql.fm,
  26295. mimes = fm.arrayFlip(['text/x-markdown']),
  26296. preview = ql.preview,
  26297. marked = null,
  26298. show = function(data, loading) {
  26299. ql.hideinfo();
  26300. var doc = $('<iframe class="elfinder-quicklook-preview-html"/>').appendTo(preview)[0].contentWindow.document;
  26301. doc.open();
  26302. doc.write(marked(data.content));
  26303. doc.close();
  26304. loading.remove();
  26305. },
  26306. error = function(loading) {
  26307. marked = false;
  26308. loading.remove();
  26309. };
  26310. preview.on(ql.evUpdate, function(e) {
  26311. var file = e.file, jqxhr, loading;
  26312. if (mimes[file.mime] && fm.options.cdns.marked && marked !== false && ql.dispInlineRegex.test(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) {
  26313. e.stopImmediatePropagation();
  26314. 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'));
  26315. // stop loading on change file if not loaded yet
  26316. preview.one('change', function() {
  26317. jqxhr.state() == 'pending' && jqxhr.reject();
  26318. }).addClass('elfinder-overflow-auto');
  26319. jqxhr = fm.request({
  26320. data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts},
  26321. options : {type: 'get', cache : true},
  26322. preventDefault : true
  26323. })
  26324. .done(function(data) {
  26325. if (marked || window.marked) {
  26326. if (!marked) {
  26327. marked = window.marked;
  26328. }
  26329. show(data, loading);
  26330. } else {
  26331. fm.loadScript([fm.options.cdns.marked],
  26332. function(res) {
  26333. marked = res || window.marked || false;
  26334. delete window.marked;
  26335. if (marked) {
  26336. show(data, loading);
  26337. } else {
  26338. error(loading);
  26339. }
  26340. },
  26341. {
  26342. tryRequire: true,
  26343. error: function() {
  26344. error(loading);
  26345. }
  26346. }
  26347. );
  26348. }
  26349. })
  26350. .fail(function() {
  26351. error(loading);
  26352. });
  26353. }
  26354. });
  26355. },
  26356. /**
  26357. * PDF/ODT/ODS/ODP preview with ViewerJS
  26358. *
  26359. * @param elFinder.commands.quicklook
  26360. */
  26361. function(ql) {
  26362. if (ql.options.viewerjs) {
  26363. var fm = ql.fm,
  26364. preview = ql.preview,
  26365. opts = ql.options.viewerjs,
  26366. mimes = opts.url? fm.arrayFlip(opts.mimes || []) : [];
  26367. if (opts.url) {
  26368. preview.on('update', function(e) {
  26369. var win = ql.window,
  26370. file = e.file, node, loading;
  26371. if (mimes[file.mime]) {
  26372. var url = fm.openUrl(file.hash);
  26373. if (url && fm.isSameOrigin(url)) {
  26374. e.stopImmediatePropagation();
  26375. 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'));
  26376. node = $('<iframe class="elfinder-quicklook-preview-iframe"/>')
  26377. .css('background-color', 'transparent')
  26378. .on('load', function() {
  26379. ql.hideinfo();
  26380. loading.remove();
  26381. node.css('background-color', '#fff');
  26382. })
  26383. .on('error', function() {
  26384. loading.remove();
  26385. node.remove();
  26386. })
  26387. .appendTo(preview)
  26388. .attr('src', opts.url + '#' + url);
  26389. preview.one('change', function() {
  26390. loading.remove();
  26391. node.off('load').remove();
  26392. });
  26393. }
  26394. }
  26395. });
  26396. }
  26397. }
  26398. },
  26399. /**
  26400. * PDF preview plugin
  26401. *
  26402. * @param elFinder.commands.quicklook
  26403. **/
  26404. function(ql) {
  26405. var fm = ql.fm,
  26406. mime = 'application/pdf',
  26407. preview = ql.preview,
  26408. active = false,
  26409. urlhash = '',
  26410. firefox, toolbar;
  26411. if ((fm.UA.Safari && fm.OS === 'mac' && !fm.UA.iOS) || fm.UA.IE || fm.UA.Firefox) {
  26412. active = true;
  26413. } else {
  26414. $.each(navigator.plugins, function(i, plugins) {
  26415. $.each(plugins, function(i, plugin) {
  26416. if (plugin.type === mime) {
  26417. return !(active = true);
  26418. }
  26419. });
  26420. });
  26421. }
  26422. if (active) {
  26423. if (typeof ql.options.pdfToolbar !== 'undefined' && !ql.options.pdfToolbar) {
  26424. urlhash = '#toolbar=0';
  26425. }
  26426. preview.on(ql.evUpdate, function(e) {
  26427. var file = e.file;
  26428. if (active && file.mime === mime && ql.dispInlineRegex.test(file.mime)) {
  26429. e.stopImmediatePropagation();
  26430. ql.hideinfo();
  26431. ql.cover.addClass('elfinder-quicklook-coverbg');
  26432. $('<object class="elfinder-quicklook-preview-pdf" data="'+fm.openUrl(file.hash)+urlhash+'" type="application/pdf" />')
  26433. .on('error', function(e) {
  26434. active = false;
  26435. ql.update(void(0), fm.cwd());
  26436. ql.update(void(0), file);
  26437. })
  26438. .appendTo(preview);
  26439. }
  26440. });
  26441. }
  26442. },
  26443. /**
  26444. * Flash preview plugin
  26445. *
  26446. * @param elFinder.commands.quicklook
  26447. **/
  26448. function(ql) {
  26449. var fm = ql.fm,
  26450. mime = 'application/x-shockwave-flash',
  26451. preview = ql.preview,
  26452. active = false;
  26453. $.each(navigator.plugins, function(i, plugins) {
  26454. $.each(plugins, function(i, plugin) {
  26455. if (plugin.type === mime) {
  26456. return !(active = true);
  26457. }
  26458. });
  26459. });
  26460. active && preview.on(ql.evUpdate, function(e) {
  26461. var file = e.file,
  26462. node;
  26463. if (file.mime === mime && ql.dispInlineRegex.test(file.mime)) {
  26464. e.stopImmediatePropagation();
  26465. ql.hideinfo();
  26466. 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" />')
  26467. .appendTo(preview);
  26468. }
  26469. });
  26470. },
  26471. /**
  26472. * HTML5 audio preview plugin
  26473. *
  26474. * @param elFinder.commands.quicklook
  26475. **/
  26476. function(ql) {
  26477. var fm = ql.fm,
  26478. preview = ql.preview,
  26479. mimes = {
  26480. 'audio/mpeg' : 'mp3',
  26481. 'audio/mpeg3' : 'mp3',
  26482. 'audio/mp3' : 'mp3',
  26483. 'audio/x-mpeg3' : 'mp3',
  26484. 'audio/x-mp3' : 'mp3',
  26485. 'audio/x-wav' : 'wav',
  26486. 'audio/wav' : 'wav',
  26487. 'audio/x-m4a' : 'm4a',
  26488. 'audio/aac' : 'm4a',
  26489. 'audio/mp4' : 'm4a',
  26490. 'audio/x-mp4' : 'm4a',
  26491. 'audio/ogg' : 'ogg',
  26492. 'audio/webm' : 'webm',
  26493. 'audio/flac' : 'flac',
  26494. 'audio/x-flac' : 'flac',
  26495. 'audio/amr' : 'amr'
  26496. },
  26497. node, curHash,
  26498. win = ql.window,
  26499. navi = ql.navbar,
  26500. AMR, autoplay,
  26501. controlsList = typeof ql.options.mediaControlsList === 'string' && ql.options.mediaControlsList? ' controlsList="' + fm.escape(ql.options.mediaControlsList) + '"' : '',
  26502. setNavi = function() {
  26503. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  26504. },
  26505. getNode = function(src, hash) {
  26506. return $('<audio class="elfinder-quicklook-preview-audio ui-front" controls' + controlsList + ' preload="auto" autobuffer><source src="'+src+'" /></audio>')
  26507. .on('change', function(e) {
  26508. // Firefox fire change event on seek or volume change
  26509. e.stopPropagation();
  26510. })
  26511. .on('error', function(e) {
  26512. node && node.data('hash') === hash && reset();
  26513. })
  26514. .data('hash', hash)
  26515. .appendTo(preview);
  26516. },
  26517. amrToWavUrl = function(hash) {
  26518. var dfd = $.Deferred(),
  26519. loader = $.Deferred().done(function() {
  26520. fm.getContents(hash).done(function(data) {
  26521. try {
  26522. var buffer = AMR.toWAV(new Uint8Array(data));
  26523. if (buffer) {
  26524. dfd.resolve(URL.createObjectURL(new Blob([buffer], { type: 'audio/x-wav' })));
  26525. } else {
  26526. dfd.reject();
  26527. }
  26528. } catch(e) {
  26529. dfd.reject();
  26530. }
  26531. }).fail(function() {
  26532. dfd.reject();
  26533. });
  26534. }).fail(function() {
  26535. AMR = false;
  26536. dfd.reject();
  26537. }),
  26538. _AMR;
  26539. if (window.TextEncoder && window.URL && URL.createObjectURL && typeof AMR === 'undefined') {
  26540. // previous window.AMR
  26541. _AMR = window.AMR;
  26542. delete window.AMR;
  26543. fm.loadScript(
  26544. [ fm.options.cdns.amr ],
  26545. function() {
  26546. AMR = window.AMR? window.AMR : false;
  26547. // restore previous window.AMR
  26548. window.AMR = _AMR;
  26549. loader[AMR? 'resolve':'reject']();
  26550. },
  26551. {
  26552. error: function() {
  26553. loader.reject();
  26554. }
  26555. }
  26556. );
  26557. } else {
  26558. loader[AMR? 'resolve':'reject']();
  26559. }
  26560. return dfd;
  26561. },
  26562. play = function(player) {
  26563. var hash = node.data('hash'),
  26564. playPromise;
  26565. autoplay && (playPromise = player.play());
  26566. // uses "playPromise['catch']" instead "playPromise.catch" to support Old IE
  26567. if (playPromise && playPromise['catch']) {
  26568. playPromise['catch'](function(e) {
  26569. if (!player.paused) {
  26570. node && node.data('hash') === hash && reset();
  26571. }
  26572. });
  26573. }
  26574. },
  26575. reset = function() {
  26576. if (node && node.parent().length) {
  26577. var elm = node[0],
  26578. url = node.children('source').attr('src');
  26579. win.off('viewchange.audio');
  26580. try {
  26581. elm.pause();
  26582. node.empty();
  26583. if (url.match(/^blob:/)) {
  26584. URL.revokeObjectURL(url);
  26585. }
  26586. elm.src = '';
  26587. elm.load();
  26588. } catch(e) {}
  26589. node.remove();
  26590. node = null;
  26591. }
  26592. };
  26593. preview.on(ql.evUpdate, function(e) {
  26594. var file = e.file,
  26595. type = mimes[file.mime],
  26596. html5, srcUrl;
  26597. if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime) && ((html5 = ql.support.audio[type]) || (type === 'amr'))) {
  26598. autoplay = ql.autoPlay();
  26599. curHash = file.hash;
  26600. srcUrl = html5? fm.openUrl(curHash) : '';
  26601. if (!html5) {
  26602. if (fm.options.cdns.amr && type === 'amr' && AMR !== false) {
  26603. e.stopImmediatePropagation();
  26604. node = getNode(srcUrl, curHash);
  26605. amrToWavUrl(file.hash).done(function(url) {
  26606. if (curHash === file.hash) {
  26607. var elm = node[0];
  26608. try {
  26609. node.children('source').attr('src', url);
  26610. elm.pause();
  26611. elm.load();
  26612. play(elm);
  26613. win.on('viewchange.audio', setNavi);
  26614. setNavi();
  26615. } catch(e) {
  26616. URL.revokeObjectURL(url);
  26617. node.remove();
  26618. }
  26619. } else {
  26620. URL.revokeObjectURL(url);
  26621. }
  26622. }).fail(function() {
  26623. node.remove();
  26624. });
  26625. }
  26626. } else {
  26627. e.stopImmediatePropagation();
  26628. node = getNode(srcUrl, curHash);
  26629. play(node[0]);
  26630. win.on('viewchange.audio', setNavi);
  26631. setNavi();
  26632. }
  26633. }
  26634. }).on('change', reset);
  26635. },
  26636. /**
  26637. * HTML5 video preview plugin
  26638. *
  26639. * @param elFinder.commands.quicklook
  26640. **/
  26641. function(ql) {
  26642. var fm = ql.fm,
  26643. preview = ql.preview,
  26644. mimes = {
  26645. 'video/mp4' : 'mp4',
  26646. 'video/x-m4v' : 'mp4',
  26647. 'video/quicktime' : 'mp4',
  26648. 'video/ogg' : 'ogg',
  26649. 'application/ogg' : 'ogg',
  26650. 'video/webm' : 'webm',
  26651. 'video/x-matroska': 'mkv',
  26652. 'video/3gpp' : '3gp',
  26653. 'application/vnd.apple.mpegurl' : 'm3u8',
  26654. 'application/x-mpegurl' : 'm3u8',
  26655. 'application/dash+xml' : 'mpd',
  26656. 'video/x-flv' : 'flv'
  26657. },
  26658. node,
  26659. win = ql.window,
  26660. navi = ql.navbar,
  26661. cHls, cDash, pDash, cFlv, autoplay, tm,
  26662. controlsList = typeof ql.options.mediaControlsList === 'string' && ql.options.mediaControlsList? ' controlsList="' + fm.escape(ql.options.mediaControlsList) + '"' : '',
  26663. setNavi = function() {
  26664. if (fm.UA.iOS) {
  26665. if (win.hasClass('elfinder-quicklook-fullscreen')) {
  26666. preview.css('height', '-webkit-calc(100% - 50px)');
  26667. navi._show();
  26668. } else {
  26669. preview.css('height', '');
  26670. }
  26671. } else {
  26672. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  26673. }
  26674. },
  26675. render = function(file, opts) {
  26676. var errTm = function(e) {
  26677. if (err > 1) {
  26678. tm && clearTimeout(tm);
  26679. tm = setTimeout(function() {
  26680. !canPlay && reset(true);
  26681. }, 800);
  26682. }
  26683. },
  26684. err = 0,
  26685. canPlay;
  26686. //reset();
  26687. pDash = null;
  26688. opts = opts || {};
  26689. ql.hideinfo();
  26690. node = $('<video class="elfinder-quicklook-preview-video" controls' + controlsList + ' preload="auto" autobuffer playsinline>'
  26691. +'</video>')
  26692. .on('change', function(e) {
  26693. // Firefox fire change event on seek or volume change
  26694. e.stopPropagation();
  26695. })
  26696. .on('timeupdate progress', errTm)
  26697. .on('canplay', function() {
  26698. canPlay = true;
  26699. })
  26700. .data('hash', file.hash);
  26701. // can not handling error event with jQuery `on` event handler
  26702. node[0].addEventListener('error', function(e) {
  26703. if (opts.src && fm.convAbsUrl(opts.src) === fm.convAbsUrl(e.target.src)) {
  26704. ++err;
  26705. errTm();
  26706. }
  26707. }, true);
  26708. if (opts.src) {
  26709. node.append('<source src="'+opts.src+'" type="'+file.mime+'"/><source src="'+opts.src+'"/>');
  26710. }
  26711. node.appendTo(preview);
  26712. win.on('viewchange.video', setNavi);
  26713. setNavi();
  26714. },
  26715. loadHls = function(file) {
  26716. var hls;
  26717. render(file);
  26718. hls = new cHls();
  26719. hls.loadSource(fm.openUrl(file.hash));
  26720. hls.attachMedia(node[0]);
  26721. if (autoplay) {
  26722. hls.on(cHls.Events.MANIFEST_PARSED, function() {
  26723. play(node[0]);
  26724. });
  26725. }
  26726. },
  26727. loadDash = function(file) {
  26728. render(file);
  26729. pDash = window.dashjs.MediaPlayer().create();
  26730. pDash.getDebug().setLogToBrowserConsole(false);
  26731. pDash.initialize(node[0], fm.openUrl(file.hash), autoplay);
  26732. pDash.on('error', function(e) {
  26733. reset(true);
  26734. });
  26735. },
  26736. loadFlv = function(file) {
  26737. if (!cFlv.isSupported()) {
  26738. cFlv = false;
  26739. return;
  26740. }
  26741. var player = cFlv.createPlayer({
  26742. type: 'flv',
  26743. url: fm.openUrl(file.hash)
  26744. });
  26745. render(file);
  26746. player.on(cFlv.Events.ERROR, function() {
  26747. player.destroy();
  26748. reset(true);
  26749. });
  26750. player.attachMediaElement(node[0]);
  26751. player.load();
  26752. play(player);
  26753. },
  26754. play = function(player) {
  26755. var hash = node.data('hash'),
  26756. playPromise;
  26757. autoplay && (playPromise = player.play());
  26758. // uses "playPromise['catch']" instead "playPromise.catch" to support Old IE
  26759. if (playPromise && playPromise['catch']) {
  26760. playPromise['catch'](function(e) {
  26761. if (!player.paused) {
  26762. node && node.data('hash') === hash && reset(true);
  26763. }
  26764. });
  26765. }
  26766. },
  26767. reset = function(showInfo) {
  26768. tm && clearTimeout(tm);
  26769. if (node && node.parent().length) {
  26770. var elm = node[0];
  26771. win.off('viewchange.video');
  26772. pDash && pDash.reset();
  26773. try {
  26774. elm.pause();
  26775. node.empty();
  26776. elm.src = '';
  26777. elm.load();
  26778. } catch(e) {}
  26779. node.remove();
  26780. node = null;
  26781. }
  26782. showInfo && ql.info.show();
  26783. };
  26784. preview.on(ql.evUpdate, function(e) {
  26785. var file = e.file,
  26786. mime = file.mime.toLowerCase(),
  26787. type = mimes[mime],
  26788. stock, playPromise;
  26789. if (mimes[mime] && ql.dispInlineRegex.test(file.mime) && (((type === 'm3u8' || (type === 'mpd' && !fm.UA.iOS) || type === 'flv') && !fm.UA.ltIE10) || ql.support.video[type])) {
  26790. autoplay = ql.autoPlay();
  26791. if (ql.support.video[type] && (type !== 'm3u8' || fm.UA.Safari)) {
  26792. e.stopImmediatePropagation();
  26793. render(file, { src: fm.openUrl(file.hash) });
  26794. play(node[0]);
  26795. } else {
  26796. if (cHls !== false && fm.options.cdns.hls && type === 'm3u8') {
  26797. e.stopImmediatePropagation();
  26798. if (cHls) {
  26799. loadHls(file);
  26800. } else {
  26801. stock = window.Hls;
  26802. delete window.Hls;
  26803. fm.loadScript(
  26804. [ fm.options.cdns.hls ],
  26805. function(res) {
  26806. cHls = res || window.Hls || false;
  26807. window.Hls = stock;
  26808. cHls && loadHls(file);
  26809. },
  26810. {
  26811. tryRequire: true,
  26812. error : function() {
  26813. cHls = false;
  26814. }
  26815. }
  26816. );
  26817. }
  26818. } else if (cDash !== false && fm.options.cdns.dash && type === 'mpd') {
  26819. e.stopImmediatePropagation();
  26820. if (cDash) {
  26821. loadDash(file);
  26822. } else {
  26823. fm.loadScript(
  26824. [ fm.options.cdns.dash ],
  26825. function() {
  26826. // dashjs require window.dashjs in global scope
  26827. cDash = window.dashjs? true : false;
  26828. cDash && loadDash(file);
  26829. },
  26830. {
  26831. tryRequire: true,
  26832. error : function() {
  26833. cDash = false;
  26834. }
  26835. }
  26836. );
  26837. }
  26838. } else if (cFlv !== false && fm.options.cdns.flv && type === 'flv') {
  26839. e.stopImmediatePropagation();
  26840. if (cFlv) {
  26841. loadFlv(file);
  26842. } else {
  26843. stock = window.flvjs;
  26844. delete window.flvjs;
  26845. fm.loadScript(
  26846. [ fm.options.cdns.flv ],
  26847. function(res) {
  26848. cFlv = res || window.flvjs || false;
  26849. window.flvjs = stock;
  26850. cFlv && loadFlv(file);
  26851. },
  26852. {
  26853. tryRequire: true,
  26854. error : function() {
  26855. cFlv = false;
  26856. }
  26857. }
  26858. );
  26859. }
  26860. }
  26861. }
  26862. }
  26863. }).on('change', reset);
  26864. },
  26865. /**
  26866. * Audio/video preview plugin using browser plugins
  26867. *
  26868. * @param elFinder.commands.quicklook
  26869. **/
  26870. function(ql) {
  26871. var preview = ql.preview,
  26872. mimes = [],
  26873. node,
  26874. win = ql.window,
  26875. navi = ql.navbar;
  26876. $.each(navigator.plugins, function(i, plugins) {
  26877. $.each(plugins, function(i, plugin) {
  26878. (plugin.type.indexOf('audio/') === 0 || plugin.type.indexOf('video/') === 0) && mimes.push(plugin.type);
  26879. });
  26880. });
  26881. mimes = ql.fm.arrayFlip(mimes);
  26882. preview.on(ql.evUpdate, function(e) {
  26883. var file = e.file,
  26884. mime = file.mime,
  26885. video,
  26886. setNavi = function() {
  26887. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  26888. };
  26889. if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime)) {
  26890. e.stopImmediatePropagation();
  26891. (video = mime.indexOf('video/') === 0) && ql.hideinfo();
  26892. node = $('<embed src="'+ql.fm.openUrl(file.hash)+'" type="'+mime+'" class="elfinder-quicklook-preview-'+(video ? 'video' : 'audio')+'"/>')
  26893. .appendTo(preview);
  26894. win.on('viewchange.embed', setNavi);
  26895. setNavi();
  26896. }
  26897. }).on('change', function() {
  26898. if (node && node.parent().length) {
  26899. win.off('viewchange.embed');
  26900. node.remove();
  26901. node= null;
  26902. }
  26903. });
  26904. },
  26905. /**
  26906. * Archive(zip|gzip|tar) preview plugin using https://github.com/imaya/zlib.js
  26907. *
  26908. * @param elFinder.commands.quicklook
  26909. **/
  26910. function(ql) {
  26911. var fm = ql.fm,
  26912. mimes = fm.arrayFlip(['application/zip', 'application/x-gzip', 'application/x-tar']),
  26913. preview = ql.preview,
  26914. unzipFiles = function() {
  26915. /** @type {Array.<string>} */
  26916. var filenameList = [];
  26917. /** @type {number} */
  26918. var i;
  26919. /** @type {number} */
  26920. var il;
  26921. /** @type {Array.<Zlib.Unzip.FileHeader>} */
  26922. var fileHeaderList;
  26923. // need check this.Y when update cdns.zlibUnzip
  26924. this.Y();
  26925. fileHeaderList = this.i;
  26926. for (i = 0, il = fileHeaderList.length; i < il; ++i) {
  26927. // need check fileHeaderList[i].J when update cdns.zlibUnzip
  26928. filenameList[i] = fileHeaderList[i].filename + (fileHeaderList[i].J? ' (' + fm.formatSize(fileHeaderList[i].J) + ')' : '');
  26929. }
  26930. return filenameList;
  26931. },
  26932. tarFiles = function(tar) {
  26933. var filenames = [],
  26934. tarlen = tar.length,
  26935. offset = 0,
  26936. toStr = function(arr) {
  26937. return String.fromCharCode.apply(null, arr).replace(/\0+$/, '');
  26938. },
  26939. h, name, prefix, size, dbs;
  26940. while (offset < tarlen && tar[offset] !== 0) {
  26941. h = tar.subarray(offset, offset + 512);
  26942. name = toStr(h.subarray(0, 100));
  26943. if (prefix = toStr(h.subarray(345, 500))) {
  26944. name = prefix + name;
  26945. }
  26946. size = parseInt(toStr(h.subarray(124, 136)), 8);
  26947. dbs = Math.ceil(size / 512) * 512;
  26948. if (name === '././@LongLink') {
  26949. name = toStr(tar.subarray(offset + 512, offset + 512 + dbs));
  26950. }
  26951. (name !== 'pax_global_header') && filenames.push(name + (size? ' (' + fm.formatSize(size) + ')': ''));
  26952. offset = offset + 512 + dbs;
  26953. }
  26954. return filenames;
  26955. },
  26956. Zlib;
  26957. if (window.Uint8Array && window.DataView && fm.options.cdns.zlibUnzip && fm.options.cdns.zlibGunzip) {
  26958. preview.on(ql.evUpdate, function(e) {
  26959. var file = e.file,
  26960. isTar = (file.mime === 'application/x-tar');
  26961. if (mimes[file.mime] && (
  26962. isTar
  26963. || ((typeof Zlib === 'undefined' || Zlib) && (file.mime === 'application/zip' || file.mime === 'application/x-gzip'))
  26964. )) {
  26965. var jqxhr, loading, url,
  26966. req = function() {
  26967. url = fm.openUrl(file.hash);
  26968. if (!fm.isSameOrigin(url)) {
  26969. url = fm.openUrl(file.hash, true);
  26970. }
  26971. jqxhr = fm.request({
  26972. data : {cmd : 'get'},
  26973. options : {
  26974. url: url,
  26975. type: 'get',
  26976. cache : true,
  26977. dataType : 'binary',
  26978. responseType :'arraybuffer',
  26979. processData: false
  26980. }
  26981. })
  26982. .fail(function() {
  26983. loading.remove();
  26984. })
  26985. .done(function(data) {
  26986. var unzip, filenames;
  26987. try {
  26988. if (file.mime === 'application/zip') {
  26989. unzip = new Zlib.Unzip(new Uint8Array(data));
  26990. //filenames = unzip.getFilenames();
  26991. filenames = unzipFiles.call(unzip);
  26992. } else if (file.mime === 'application/x-gzip') {
  26993. unzip = new Zlib.Gunzip(new Uint8Array(data));
  26994. filenames = tarFiles(unzip.decompress());
  26995. } else if (file.mime === 'application/x-tar') {
  26996. filenames = tarFiles(new Uint8Array(data));
  26997. }
  26998. makeList(filenames);
  26999. } catch (e) {
  27000. loading.remove();
  27001. fm.debug('error', e);
  27002. }
  27003. });
  27004. },
  27005. makeList = function(filenames) {
  27006. var header, doc;
  27007. if (filenames && filenames.length) {
  27008. filenames = $.map(filenames, function(str) {
  27009. return fm.decodeRawString(str);
  27010. });
  27011. filenames.sort();
  27012. loading.remove();
  27013. header = '<strong>'+fm.escape(file.mime)+'</strong> ('+fm.formatSize(file.size)+')'+'<hr/>';
  27014. doc = $('<div class="elfinder-quicklook-preview-archive-wrapper">'+header+'<pre class="elfinder-quicklook-preview-text">'+fm.escape(filenames.join("\n"))+'</pre></div>')
  27015. .on('touchstart', function(e) {
  27016. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  27017. e.originalEvent._preventSwipeX = true;
  27018. }
  27019. })
  27020. .appendTo(preview);
  27021. ql.hideinfo();
  27022. }
  27023. },
  27024. _Zlib;
  27025. // this is our file - stop event propagation
  27026. e.stopImmediatePropagation();
  27027. 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'));
  27028. // stop loading on change file if not loaded yet
  27029. preview.one('change', function() {
  27030. jqxhr.state() === 'pending' && jqxhr.reject();
  27031. loading.remove();
  27032. });
  27033. if (Zlib) {
  27034. req();
  27035. } else {
  27036. if (window.Zlib) {
  27037. _Zlib = window.Zlib;
  27038. delete window.Zlib;
  27039. }
  27040. fm.loadScript(
  27041. [ fm.options.cdns.zlibUnzip, fm.options.cdns.zlibGunzip ],
  27042. function() {
  27043. if (window.Zlib && (Zlib = window.Zlib)) {
  27044. if (_Zlib) {
  27045. window.Zlib = _Zlib;
  27046. } else {
  27047. delete window.Zlib;
  27048. }
  27049. req();
  27050. } else {
  27051. error();
  27052. }
  27053. }
  27054. );
  27055. }
  27056. }
  27057. });
  27058. }
  27059. },
  27060. /**
  27061. * RAR Archive preview plugin using https://github.com/43081j/rar.js
  27062. *
  27063. * @param elFinder.commands.quicklook
  27064. **/
  27065. function(ql) {
  27066. var fm = ql.fm,
  27067. mimes = fm.arrayFlip(['application/x-rar']),
  27068. preview = ql.preview,
  27069. RAR;
  27070. if (window.DataView) {
  27071. preview.on(ql.evUpdate, function(e) {
  27072. var file = e.file;
  27073. if (mimes[file.mime] && fm.options.cdns.rar && RAR !== false) {
  27074. var loading, url, archive, abort,
  27075. getList = function(url) {
  27076. if (abort) {
  27077. loading.remove();
  27078. return;
  27079. }
  27080. try {
  27081. archive = RAR({
  27082. file: url,
  27083. type: 2,
  27084. xhrHeaders: fm.customHeaders,
  27085. xhrFields: fm.xhrFields
  27086. }, function(err) {
  27087. loading.remove();
  27088. var filenames = [],
  27089. header, doc;
  27090. if (abort || err) {
  27091. // An error occurred (not a rar, read error, etc)
  27092. err && fm.debug('error', err);
  27093. return;
  27094. }
  27095. $.each(archive.entries, function() {
  27096. filenames.push(this.path + (this.size? ' (' + fm.formatSize(this.size) + ')' : ''));
  27097. });
  27098. if (filenames.length) {
  27099. filenames = $.map(filenames, function(str) {
  27100. return fm.decodeRawString(str);
  27101. });
  27102. filenames.sort();
  27103. header = '<strong>'+fm.escape(file.mime)+'</strong> ('+fm.formatSize(file.size)+')'+'<hr/>';
  27104. doc = $('<div class="elfinder-quicklook-preview-archive-wrapper">'+header+'<pre class="elfinder-quicklook-preview-text">'+fm.escape(filenames.join("\n"))+'</pre></div>')
  27105. .on('touchstart', function(e) {
  27106. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  27107. e.originalEvent._preventSwipeX = true;
  27108. }
  27109. })
  27110. .appendTo(preview);
  27111. ql.hideinfo();
  27112. }
  27113. });
  27114. } catch(e) {
  27115. loading.remove();
  27116. }
  27117. },
  27118. error = function() {
  27119. RAR = false;
  27120. loading.remove();
  27121. },
  27122. _RAR;
  27123. // this is our file - stop event propagation
  27124. e.stopImmediatePropagation();
  27125. 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'));
  27126. // stop loading on change file if not loaded yet
  27127. preview.one('change', function() {
  27128. archive && (archive.abort = true);
  27129. loading.remove();
  27130. abort = true;
  27131. });
  27132. url = fm.openUrl(file.hash);
  27133. if (!fm.isSameOrigin(url)) {
  27134. url = fm.openUrl(file.hash, true);
  27135. }
  27136. if (RAR) {
  27137. getList(url);
  27138. } else {
  27139. if (window.RarArchive) {
  27140. _RAR = window.RarArchive;
  27141. delete window.RarArchive;
  27142. }
  27143. fm.loadScript(
  27144. [ fm.options.cdns.rar ],
  27145. function() {
  27146. if (fm.hasRequire) {
  27147. require(['rar'], function(RarArchive) {
  27148. RAR = RarArchive;
  27149. getList(url);
  27150. }, error);
  27151. } else {
  27152. if (RAR = window.RarArchive) {
  27153. if (_RAR) {
  27154. window.RarArchive = _RAR;
  27155. } else {
  27156. delete window.RarArchive;
  27157. }
  27158. getList(url);
  27159. } else {
  27160. error();
  27161. }
  27162. }
  27163. },
  27164. {
  27165. tryRequire: true,
  27166. error : error
  27167. }
  27168. );
  27169. }
  27170. }
  27171. });
  27172. }
  27173. },
  27174. /**
  27175. * CAD-Files and 3D-Models online viewer on sharecad.org
  27176. *
  27177. * @param elFinder.commands.quicklook
  27178. **/
  27179. function(ql) {
  27180. var fm = ql.fm,
  27181. mimes = fm.arrayFlip(ql.options.sharecadMimes || []),
  27182. preview = ql.preview,
  27183. win = ql.window,
  27184. node;
  27185. if (ql.options.sharecadMimes.length) {
  27186. ql.addIntegration({
  27187. title: 'ShareCAD.org CAD and 3D-Models viewer',
  27188. link: 'https://sharecad.org/DWGOnlinePlugin'
  27189. });
  27190. }
  27191. preview.on(ql.evUpdate, function(e) {
  27192. var file = e.file;
  27193. if (mimes[file.mime.toLowerCase()]) {
  27194. var win = ql.window,
  27195. loading, url;
  27196. e.stopImmediatePropagation();
  27197. if (file.url == '1') {
  27198. preview.hide();
  27199. $('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info'))
  27200. .on('click', function() {
  27201. var self = $(this);
  27202. self.html('<span class="elfinder-spinner">');
  27203. fm.request({
  27204. data : {cmd : 'url', target : file.hash},
  27205. preventDefault : true
  27206. })
  27207. .always(function() {
  27208. self.html('');
  27209. })
  27210. .done(function(data) {
  27211. var rfile = fm.file(file.hash);
  27212. file.url = rfile.url = data.url || '';
  27213. if (file.url) {
  27214. preview.trigger({
  27215. type: ql.evUpdate,
  27216. file: file,
  27217. forceUpdate: true
  27218. });
  27219. }
  27220. });
  27221. });
  27222. }
  27223. if (file.url !== '' && file.url != '1') {
  27224. preview.one('change', function() {
  27225. loading.remove();
  27226. node.off('load').remove();
  27227. node = null;
  27228. }).addClass('elfinder-overflow-auto');
  27229. 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'));
  27230. url = fm.convAbsUrl(fm.url(file.hash));
  27231. node = $('<iframe class="elfinder-quicklook-preview-iframe" scrolling="no"/>')
  27232. .css('background-color', 'transparent')
  27233. .appendTo(preview)
  27234. .on('load', function() {
  27235. ql.hideinfo();
  27236. loading.remove();
  27237. ql.preview.after(ql.info);
  27238. $(this).css('background-color', '#fff').show();
  27239. })
  27240. .on('error', function() {
  27241. loading.remove();
  27242. ql.preview.after(ql.info);
  27243. })
  27244. .attr('src', '//sharecad.org/cadframe/load?url=' + encodeURIComponent(url));
  27245. ql.info.after(ql.preview);
  27246. }
  27247. }
  27248. });
  27249. },
  27250. /**
  27251. * KML preview with GoogleMaps API
  27252. *
  27253. * @param elFinder.commands.quicklook
  27254. */
  27255. function(ql) {
  27256. var fm = ql.fm,
  27257. mimes = {
  27258. 'application/vnd.google-earth.kml+xml' : true,
  27259. 'application/vnd.google-earth.kmz' : true
  27260. },
  27261. preview = ql.preview,
  27262. gMaps, loadMap, wGmfail, fail, mapScr;
  27263. if (ql.options.googleMapsApiKey) {
  27264. ql.addIntegration({
  27265. title: 'Google Maps',
  27266. link: 'https://www.google.com/intl/' + fm.lang.replace('_', '-') + '/help/terms_maps.html'
  27267. });
  27268. gMaps = (window.google && google.maps);
  27269. // start load maps
  27270. loadMap = function(file, node) {
  27271. var mapsOpts = ql.options.googleMapsOpts.maps;
  27272. ql.hideinfo();
  27273. try {
  27274. new gMaps.KmlLayer(fm.convAbsUrl(fm.url(file.hash)), Object.assign({
  27275. map: new gMaps.Map(node.get(0), mapsOpts)
  27276. }, ql.options.googleMapsOpts.kml));
  27277. } catch(e) {
  27278. fail();
  27279. }
  27280. };
  27281. // keep stored error handler if exists
  27282. wGmfail = window.gm_authFailure;
  27283. // on error function
  27284. fail = function() {
  27285. mapScr = null;
  27286. };
  27287. // API script url
  27288. mapScr = 'https://maps.googleapis.com/maps/api/js?key=' + ql.options.googleMapsApiKey;
  27289. // error handler
  27290. window.gm_authFailure = function() {
  27291. fail();
  27292. wGmfail && wGmfail();
  27293. };
  27294. preview.on(ql.evUpdate, function(e) {
  27295. var file = e.file;
  27296. if (mapScr && mimes[file.mime.toLowerCase()]) {
  27297. var win = ql.window,
  27298. loading, url, node;
  27299. e.stopImmediatePropagation();
  27300. if (file.url == '1') {
  27301. preview.hide();
  27302. $('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info'))
  27303. .on('click', function() {
  27304. var self = $(this);
  27305. self.html('<span class="elfinder-spinner">');
  27306. fm.request({
  27307. data : {cmd : 'url', target : file.hash},
  27308. preventDefault : true
  27309. })
  27310. .always(function() {
  27311. self.html('');
  27312. })
  27313. .done(function(data) {
  27314. var rfile = fm.file(file.hash);
  27315. file.url = rfile.url = data.url || '';
  27316. if (file.url) {
  27317. preview.trigger({
  27318. type: ql.evUpdate,
  27319. file: file,
  27320. forceUpdate: true
  27321. });
  27322. }
  27323. });
  27324. });
  27325. }
  27326. if (file.url !== '' && file.url != '1') {
  27327. node = $('<div style="width:100%;height:100%;"/>').appendTo(preview);
  27328. preview.one('change', function() {
  27329. node.remove();
  27330. node = null;
  27331. });
  27332. if (!gMaps) {
  27333. fm.loadScript([mapScr], function() {
  27334. gMaps = window.google && google.maps;
  27335. gMaps && loadMap(file, node);
  27336. });
  27337. } else {
  27338. loadMap(file, node);
  27339. }
  27340. }
  27341. }
  27342. });
  27343. }
  27344. },
  27345. /**
  27346. * Any supported files preview plugin using (Google docs | MS Office) online viewer
  27347. *
  27348. * @param elFinder.commands.quicklook
  27349. **/
  27350. function(ql) {
  27351. var fm = ql.fm,
  27352. mimes = Object.assign(fm.arrayFlip(ql.options.googleDocsMimes || [], 'g'), fm.arrayFlip(ql.options.officeOnlineMimes || [], 'm')),
  27353. preview = ql.preview,
  27354. win = ql.window,
  27355. navi = ql.navbar,
  27356. urls = {
  27357. g: 'docs.google.com/gview?embedded=true&url=',
  27358. m: 'view.officeapps.live.com/op/embed.aspx?wdStartOn=0&src='
  27359. },
  27360. navBottom = {
  27361. g: '56px',
  27362. m: '24px'
  27363. },
  27364. mLimits = {
  27365. xls : 5242880, // 5MB
  27366. xlsb : 5242880,
  27367. xlsx : 5242880,
  27368. xlsm : 5242880,
  27369. other: 10485760 // 10MB
  27370. },
  27371. node, enable;
  27372. if (ql.options.googleDocsMimes.length) {
  27373. enable = true;
  27374. ql.addIntegration({
  27375. title: 'Google Docs Viewer',
  27376. link: 'https://docs.google.com/'
  27377. });
  27378. }
  27379. if (ql.options.officeOnlineMimes.length) {
  27380. enable = true;
  27381. ql.addIntegration({
  27382. title: 'MS Online Doc Viewer',
  27383. link: 'https://products.office.com/office-online/view-office-documents-online'
  27384. });
  27385. }
  27386. if (enable) {
  27387. preview.on(ql.evUpdate, function(e) {
  27388. var file = e.file,
  27389. type;
  27390. // 25MB is maximum filesize of Google Docs prevew
  27391. if (file.size <= 26214400 && (type = mimes[file.mime])) {
  27392. var win = ql.window,
  27393. setNavi = function() {
  27394. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? navBottom[type] : '');
  27395. },
  27396. ext = fm.mimeTypes[file.mime],
  27397. loading, url;
  27398. if (type === 'm') {
  27399. if ((mLimits[ext] && file.size > mLimits[ext]) || file.size > mLimits.other) {
  27400. type = 'g';
  27401. }
  27402. }
  27403. if (file.url == '1') {
  27404. preview.hide();
  27405. $('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info'))
  27406. .on('click', function() {
  27407. var self = $(this);
  27408. self.html('<span class="elfinder-spinner">');
  27409. fm.request({
  27410. data : {cmd : 'url', target : file.hash},
  27411. preventDefault : true
  27412. })
  27413. .always(function() {
  27414. self.html('');
  27415. })
  27416. .done(function(data) {
  27417. var rfile = fm.file(file.hash);
  27418. file.url = rfile.url = data.url || '';
  27419. if (file.url) {
  27420. preview.trigger({
  27421. type: ql.evUpdate,
  27422. file: file,
  27423. forceUpdate: true
  27424. });
  27425. }
  27426. });
  27427. });
  27428. }
  27429. if (file.url !== '' && file.url != '1') {
  27430. e.stopImmediatePropagation();
  27431. preview.one('change', function() {
  27432. win.off('viewchange.googledocs');
  27433. loading.remove();
  27434. node.off('load').remove();
  27435. node = null;
  27436. }).addClass('elfinder-overflow-auto');
  27437. 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'));
  27438. url = fm.convAbsUrl(fm.url(file.hash));
  27439. if (file.ts) {
  27440. url += (url.match(/\?/)? '&' : '?') + '_t=' + file.ts;
  27441. }
  27442. node = $('<iframe class="elfinder-quicklook-preview-iframe"/>')
  27443. .css('background-color', 'transparent')
  27444. .appendTo(preview)
  27445. .on('load', function() {
  27446. ql.hideinfo();
  27447. loading.remove();
  27448. ql.preview.after(ql.info);
  27449. $(this).css('background-color', '#fff').show();
  27450. })
  27451. .on('error', function() {
  27452. loading.remove();
  27453. ql.preview.after(ql.info);
  27454. })
  27455. .attr('src', 'https://' + urls[type] + encodeURIComponent(url));
  27456. win.on('viewchange.googledocs', setNavi);
  27457. setNavi();
  27458. ql.info.after(ql.preview);
  27459. }
  27460. }
  27461. });
  27462. }
  27463. },
  27464. /**
  27465. * Texts preview plugin
  27466. *
  27467. * @param elFinder.commands.quicklook
  27468. **/
  27469. function(ql) {
  27470. var fm = ql.fm,
  27471. preview = ql.preview,
  27472. textMaxlen = parseInt(ql.options.textMaxlen) || 2000,
  27473. prettify = function() {
  27474. if (fm.options.cdns.prettify) {
  27475. fm.loadScript([fm.options.cdns.prettify + (fm.options.cdns.prettify.match(/\?/)? '&' : '?') + 'autorun=false']);
  27476. prettify = function() { return true; };
  27477. } else {
  27478. prettify = function() { return false; };
  27479. }
  27480. },
  27481. PRcheck = function(node, cnt) {
  27482. if (prettify()) {
  27483. if (typeof window.PR === 'undefined' && cnt--) {
  27484. setTimeout(function() { PRcheck(node, cnt); }, 100);
  27485. } else {
  27486. if (typeof window.PR === 'object') {
  27487. node.css('cursor', 'wait');
  27488. requestAnimationFrame(function() {
  27489. PR.prettyPrint && PR.prettyPrint(null, node.get(0));
  27490. node.css('cursor', '');
  27491. });
  27492. } else {
  27493. prettify = function() { return false; };
  27494. }
  27495. }
  27496. }
  27497. };
  27498. preview.on(ql.evUpdate, function(e) {
  27499. var file = e.file,
  27500. mime = file.mime,
  27501. jqxhr, loading;
  27502. if (fm.mimeIsText(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) {
  27503. e.stopImmediatePropagation();
  27504. (typeof window.PR === 'undefined') && prettify();
  27505. 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'));
  27506. // stop loading on change file if not loadin yet
  27507. preview.one('change', function() {
  27508. jqxhr.state() == 'pending' && jqxhr.reject();
  27509. });
  27510. jqxhr = fm.request({
  27511. data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts},
  27512. options : {type: 'get', cache : true},
  27513. preventDefault : true
  27514. })
  27515. .done(function(data) {
  27516. var reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i'),
  27517. text = data.content,
  27518. part, more, node, m;
  27519. ql.hideinfo();
  27520. if (window.atob && (m = text.match(reg))) {
  27521. text = atob(text.substr(m[1].length));
  27522. }
  27523. more = text.length - textMaxlen;
  27524. if (more > 100) {
  27525. part = text.substr(0, textMaxlen) + '...';
  27526. } else {
  27527. more = 0;
  27528. }
  27529. node = $('<div class="elfinder-quicklook-preview-text-wrapper"><pre class="elfinder-quicklook-preview-text prettyprint"></pre></div>');
  27530. if (more) {
  27531. node.append($('<div class="elfinder-quicklook-preview-charsleft"><hr/><span>' + fm.i18n('charsLeft', fm.toLocaleString(more)) + '</span></div>')
  27532. .on('click', function() {
  27533. var top = node.scrollTop();
  27534. $(this).remove();
  27535. node.children('pre').removeClass('prettyprinted').text(text).scrollTop(top);
  27536. PRcheck(node, 100);
  27537. })
  27538. );
  27539. }
  27540. node.children('pre').text(part || text);
  27541. node.on('touchstart', function(e) {
  27542. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  27543. e.originalEvent._preventSwipeX = true;
  27544. }
  27545. }).appendTo(preview);
  27546. PRcheck(node, 100);
  27547. })
  27548. .always(function() {
  27549. loading.remove();
  27550. });
  27551. }
  27552. });
  27553. }
  27554. ];
  27555. /*
  27556. * File: /js/commands/reload.js
  27557. */
  27558. /**
  27559. * @class elFinder command "reload"
  27560. * Sync files and folders
  27561. *
  27562. * @author Dmitry (dio) Levashov
  27563. **/
  27564. (elFinder.prototype.commands.reload = function() {
  27565. "use strict";
  27566. var self = this,
  27567. search = false;
  27568. this.alwaysEnabled = true;
  27569. this.updateOnSelect = true;
  27570. this.shortcuts = [{
  27571. pattern : 'ctrl+shift+r f5'
  27572. }];
  27573. this.getstate = function() {
  27574. return 0;
  27575. };
  27576. this.init = function() {
  27577. this.fm.bind('search searchend', function() {
  27578. search = this.type == 'search';
  27579. });
  27580. };
  27581. this.fm.bind('contextmenu', function(){
  27582. var fm = self.fm;
  27583. if (fm.options.sync >= 1000) {
  27584. self.extra = {
  27585. icon: 'accept',
  27586. node: $('<span/>')
  27587. .attr({title: fm.i18n('autoSync')})
  27588. .on('click touchstart', function(e){
  27589. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  27590. return;
  27591. }
  27592. e.stopPropagation();
  27593. e.preventDefault();
  27594. $(this).parent()
  27595. .toggleClass('ui-state-disabled', fm.options.syncStart)
  27596. .parent().removeClass('ui-state-hover');
  27597. fm.options.syncStart = !fm.options.syncStart;
  27598. fm.autoSync(fm.options.syncStart? null : 'stop');
  27599. }).on('ready', function(){
  27600. $(this).parent().toggleClass('ui-state-disabled', !fm.options.syncStart).css('pointer-events', 'auto');
  27601. })
  27602. };
  27603. }
  27604. });
  27605. this.exec = function() {
  27606. var fm = this.fm;
  27607. if (!search) {
  27608. var dfrd = fm.sync(),
  27609. timeout = setTimeout(function() {
  27610. fm.notify({type : 'reload', cnt : 1, hideCnt : true});
  27611. dfrd.always(function() { fm.notify({type : 'reload', cnt : -1}); });
  27612. }, fm.notifyDelay);
  27613. return dfrd.always(function() {
  27614. clearTimeout(timeout);
  27615. fm.trigger('reload');
  27616. });
  27617. } else {
  27618. $('div.elfinder-toolbar > div.'+fm.res('class', 'searchbtn') + ' > span.ui-icon-search').click();
  27619. }
  27620. };
  27621. }).prototype = { forceLoad : true }; // this is required command
  27622. /*
  27623. * File: /js/commands/rename.js
  27624. */
  27625. /**
  27626. * @class elFinder command "rename".
  27627. * Rename selected file.
  27628. *
  27629. * @author Dmitry (dio) Levashov, dio@std42.ru
  27630. * @author Naoki Sawada
  27631. **/
  27632. elFinder.prototype.commands.rename = function() {
  27633. "use strict";
  27634. // set alwaysEnabled to allow root rename on client size
  27635. this.alwaysEnabled = true;
  27636. this.syncTitleOnChange = true;
  27637. var self = this,
  27638. fm = self.fm,
  27639. request = function(dfrd, targtes, file, name) {
  27640. var sel = targtes? [file.hash].concat(targtes) : [file.hash],
  27641. cnt = sel.length,
  27642. data = {}, rootNames;
  27643. fm.lockfiles({files : sel});
  27644. if (fm.isRoot(file)) {
  27645. if (!(rootNames = fm.storage('rootNames'))) {
  27646. rootNames = {};
  27647. }
  27648. if (name === '') {
  27649. if (rootNames[file.hash]) {
  27650. file.name = file._name;
  27651. file.i18 = file._i18;
  27652. delete rootNames[file.hash];
  27653. delete file._name;
  27654. delete file._i18;
  27655. } else {
  27656. dfrd && dfrd.reject();
  27657. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  27658. return;
  27659. }
  27660. } else {
  27661. if (typeof file._name === 'undefined') {
  27662. file._name = file.name;
  27663. file._i18 = file.i18;
  27664. }
  27665. file.name = rootNames[file.hash] = name;
  27666. delete file.i18;
  27667. }
  27668. fm.storage('rootNames', rootNames);
  27669. data = { changed: [file] };
  27670. fm.updateCache(data);
  27671. fm.change(data);
  27672. dfrd && dfrd.resolve(data);
  27673. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  27674. return;
  27675. }
  27676. data = {
  27677. cmd : 'rename',
  27678. name : name,
  27679. target : file.hash
  27680. };
  27681. if (cnt > 1) {
  27682. data['targets'] = targtes;
  27683. if (name.match(/\*/)) {
  27684. data['q'] = name;
  27685. }
  27686. }
  27687. fm.request({
  27688. data : data,
  27689. notify : {type : 'rename', cnt : cnt},
  27690. navigate : {}
  27691. })
  27692. .fail(function(error) {
  27693. var err = fm.parseError(error);
  27694. dfrd && dfrd.reject();
  27695. if (! err || ! Array.isArray(err) || err[0] !== 'errRename') {
  27696. fm.sync();
  27697. }
  27698. })
  27699. .done(function(data) {
  27700. var cwdHash;
  27701. if (data.added && data.added.length && cnt === 1) {
  27702. data.undo = {
  27703. cmd : 'rename',
  27704. callback : function() {
  27705. return fm.request({
  27706. data : {cmd : 'rename', target : data.added[0].hash, name : file.name},
  27707. notify : {type : 'undo', cnt : 1}
  27708. });
  27709. }
  27710. };
  27711. data.redo = {
  27712. cmd : 'rename',
  27713. callback : function() {
  27714. return fm.request({
  27715. data : {cmd : 'rename', target : file.hash, name : name},
  27716. notify : {type : 'rename', cnt : 1}
  27717. });
  27718. }
  27719. };
  27720. }
  27721. dfrd && dfrd.resolve(data);
  27722. if (!(cwdHash = fm.cwd().hash) || cwdHash === file.hash) {
  27723. fm.exec('open', $.map(data.added, function(f) {
  27724. return (f.mime === 'directory')? f.hash : null;
  27725. })[0]);
  27726. }
  27727. })
  27728. .always(function() {
  27729. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  27730. }
  27731. );
  27732. },
  27733. getHint = function(name, target) {
  27734. var sel = target || fm.selected(),
  27735. splits = fm.splitFileExtention(name),
  27736. f1 = fm.file(sel[0]),
  27737. f2 = fm.file(sel[1]),
  27738. ext, hint, add;
  27739. ext = splits[1]? ('.' + splits[1]) : '';
  27740. if (splits[1] && splits[0] === '*') {
  27741. // change extention
  27742. hint = '"' + fm.splitFileExtention(f1.name)[0] + ext + '", ';
  27743. hint += '"' + fm.splitFileExtention(f2.name)[0] + ext + '"';
  27744. } else if (splits[0].length > 1) {
  27745. if (splits[0].substr(-1) === '*') {
  27746. // add prefix
  27747. add = splits[0].substr(0, splits[0].length - 1);
  27748. hint = '"' + add + f1.name+'", ';
  27749. hint += '"' + add + f2.name+'"';
  27750. } else if (splits[0].substr(0, 1) === '*') {
  27751. // add suffix
  27752. add = splits[0].substr(1);
  27753. hint = '"'+fm.splitFileExtention(f1.name)[0] + add + ext + '", ';
  27754. hint += '"'+fm.splitFileExtention(f2.name)[0] + add + ext + '"';
  27755. }
  27756. }
  27757. if (!hint) {
  27758. hint = '"'+splits[0] + '1' + ext + '", "' + splits[0] + '2' + ext + '"';
  27759. }
  27760. if (sel.length > 2) {
  27761. hint += ' ...';
  27762. }
  27763. return hint;
  27764. },
  27765. batchRename = function() {
  27766. var sel = fm.selected(),
  27767. tplr = '<input name="type" type="radio" class="elfinder-tabstop">',
  27768. mkChk = function(node, label) {
  27769. return $('<label class="elfinder-rename-batch-checks">' + fm.i18n(label) + '</label>').prepend(node);
  27770. },
  27771. name = $('<input type="text" class="ui-corner-all elfinder-tabstop">'),
  27772. num = $(tplr),
  27773. prefix = $(tplr),
  27774. suffix = $(tplr),
  27775. extention = $(tplr),
  27776. checks = $('<div/>').append(
  27777. mkChk(num, 'plusNumber'),
  27778. mkChk(prefix, 'asPrefix'),
  27779. mkChk(suffix, 'asSuffix'),
  27780. mkChk(extention, 'changeExtention')
  27781. ),
  27782. preview = $('<div class="elfinder-rename-batch-preview"/>'),
  27783. node = $('<div class="elfinder-rename-batch"/>').append(
  27784. $('<div class="elfinder-rename-batch-name"/>').append(name),
  27785. $('<div class="elfinder-rename-batch-type"/>').append(checks),
  27786. preview
  27787. ),
  27788. opts = {
  27789. title : fm.i18n('batchRename'),
  27790. modal : true,
  27791. destroyOnClose : true,
  27792. width: Math.min(380, fm.getUI().width() - 20),
  27793. buttons : {},
  27794. open : function() {
  27795. name.on('input', mkPrev).trigger('focus');
  27796. }
  27797. },
  27798. getName = function() {
  27799. var vName = name.val(),
  27800. ext = fm.splitFileExtention(fm.file(sel[0]).name)[1];
  27801. if (vName !== '' || num.is(':checked')) {
  27802. if (prefix.is(':checked')) {
  27803. vName += '*';
  27804. } else if (suffix.is(':checked')) {
  27805. vName = '*' + vName + '.' + ext;
  27806. } else if (extention.is(':checked')) {
  27807. vName = '*.' + vName;
  27808. } else if (ext) {
  27809. vName += '.' + ext;
  27810. }
  27811. }
  27812. return vName;
  27813. },
  27814. mkPrev = function() {
  27815. var vName = getName();
  27816. if (vName !== '') {
  27817. preview.html(fm.i18n(['renameMultiple', sel.length, getHint(vName)]));
  27818. } else {
  27819. preview.empty();
  27820. }
  27821. },
  27822. radios = checks.find('input:radio').on('change', mkPrev),
  27823. dialog;
  27824. opts.buttons[fm.i18n('btnApply')] = function() {
  27825. var vName = getName(),
  27826. file, targets;
  27827. if (vName !== '') {
  27828. dialog.elfinderdialog('close');
  27829. targets = sel;
  27830. file = fm.file(targets.shift());
  27831. request(void(0), targets, file, vName);
  27832. }
  27833. };
  27834. opts.buttons[fm.i18n('btnCancel')] = function() {
  27835. dialog.elfinderdialog('close');
  27836. };
  27837. if ($.fn.checkboxradio) {
  27838. radios.checkboxradio({
  27839. create: function(e, ui) {
  27840. if (this === num.get(0)) {
  27841. num.prop('checked', true).change();
  27842. }
  27843. }
  27844. });
  27845. } else {
  27846. checks.buttonset({
  27847. create: function(e, ui) {
  27848. num.prop('checked', true).change();
  27849. }
  27850. });
  27851. }
  27852. dialog = self.fmDialog(node, opts);
  27853. };
  27854. this.noChangeDirOnRemovedCwd = true;
  27855. this.shortcuts = [{
  27856. pattern : 'f2' + (fm.OS == 'mac' ? ' enter' : '')
  27857. }, {
  27858. pattern : 'shift+f2',
  27859. description : 'batchRename',
  27860. callback : function() {
  27861. fm.selected().length > 1 && batchRename();
  27862. }
  27863. }];
  27864. this.getstate = function(select) {
  27865. var sel = this.files(select),
  27866. cnt = sel.length,
  27867. phash, ext, mime, brk, state, isRoot;
  27868. if (!cnt) {
  27869. return -1;
  27870. }
  27871. if (cnt > 1 && sel[0].phash) {
  27872. phash = sel[0].phash;
  27873. ext = fm.splitFileExtention(sel[0].name)[1].toLowerCase();
  27874. mime = sel[0].mime;
  27875. }
  27876. if (cnt === 1) {
  27877. isRoot = fm.isRoot(sel[0]);
  27878. }
  27879. state = (cnt === 1 && (isRoot || !sel[0].locked) || (fm.api > 2.1030 && cnt === $.grep(sel, function(f) {
  27880. if (!brk && !f.locked && f.phash === phash && !fm.isRoot(f) && (mime === f.mime || ext === fm.splitFileExtention(f.name)[1].toLowerCase())) {
  27881. return true;
  27882. } else {
  27883. brk && (brk = true);
  27884. return false;
  27885. }
  27886. }).length)) ? 0 : -1;
  27887. // because alwaysEnabled = true, it need check disabled on connector
  27888. if (!isRoot && state === 0 && fm.option('disabledFlip', sel[0].hash)['rename']) {
  27889. state = -1;
  27890. }
  27891. if (state !== -1 && cnt > 1) {
  27892. self.extra = {
  27893. icon: 'preference',
  27894. node: $('<span/>')
  27895. .attr({title: fm.i18n('batchRename')})
  27896. .on('click touchstart', function(e){
  27897. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  27898. return;
  27899. }
  27900. e.stopPropagation();
  27901. e.preventDefault();
  27902. fm.getUI().trigger('click'); // to close the context menu immediately
  27903. batchRename();
  27904. })
  27905. };
  27906. } else {
  27907. delete self.extra;
  27908. }
  27909. return state;
  27910. };
  27911. this.exec = function(hashes, cOpts) {
  27912. var cwd = fm.getUI('cwd'),
  27913. sel = hashes || (fm.selected().length? fm.selected() : false) || [fm.cwd().hash],
  27914. cnt = sel.length,
  27915. file = fm.file(sel.shift()),
  27916. filename = '.elfinder-cwd-filename',
  27917. opts = cOpts || {},
  27918. incwd = (fm.cwd().hash == file.hash),
  27919. type = (opts._currentType === 'navbar' || opts._currentType === 'files')? opts._currentType : (incwd? 'navbar' : 'files'),
  27920. navbar = (type !== 'files'),
  27921. target = $('#'+fm[navbar? 'navHash2Id' : 'cwdHash2Id'](file.hash)),
  27922. tarea = (!navbar && fm.storage('view') != 'list'),
  27923. split = function(name) {
  27924. var ext = fm.splitFileExtention(name)[1];
  27925. return [name.substr(0, name.length - ext.length - 1), ext];
  27926. },
  27927. unselect = function() {
  27928. requestAnimationFrame(function() {
  27929. input && input.trigger('blur');
  27930. });
  27931. },
  27932. rest = function(){
  27933. if (!overlay.is(':hidden')) {
  27934. overlay.elfinderoverlay('hide').off('click close', cancel);
  27935. }
  27936. pnode.removeClass('ui-front')
  27937. .css('position', '')
  27938. .off('unselect.'+fm.namespace, unselect);
  27939. if (tarea) {
  27940. node && node.css('max-height', '');
  27941. } else if (!navbar) {
  27942. pnode.css('width', '')
  27943. .parent('td').css('overflow', '');
  27944. }
  27945. }, colwidth,
  27946. dfrd = $.Deferred()
  27947. .fail(function(error) {
  27948. var parent = input.parent(),
  27949. name = fm.escape(file.i18 || file.name);
  27950. input.off();
  27951. if (tarea) {
  27952. name = name.replace(/([_.])/g, '&#8203;$1');
  27953. }
  27954. requestAnimationFrame(function() {
  27955. if (navbar) {
  27956. input.replaceWith(name);
  27957. } else {
  27958. if (parent.length) {
  27959. input.remove();
  27960. parent.html(name);
  27961. } else {
  27962. target.find(filename).html(name);
  27963. }
  27964. }
  27965. });
  27966. error && fm.error(error);
  27967. })
  27968. .always(function() {
  27969. rest();
  27970. fm.unbind('resize', resize);
  27971. fm.enable();
  27972. }),
  27973. blur = function(e) {
  27974. var name = $.trim(input.val()),
  27975. splits = fm.splitFileExtention(name),
  27976. valid = true,
  27977. req = function() {
  27978. input.off();
  27979. rest();
  27980. if (navbar) {
  27981. input.replaceWith(fm.escape(name));
  27982. } else {
  27983. node.html(fm.escape(name));
  27984. }
  27985. request(dfrd, sel, file, name);
  27986. };
  27987. if (!overlay.is(':hidden')) {
  27988. pnode.css('z-index', '');
  27989. }
  27990. if (name === '') {
  27991. if (!fm.isRoot(file)) {
  27992. return cancel();
  27993. }
  27994. if (navbar) {
  27995. input.replaceWith(fm.escape(file.name));
  27996. } else {
  27997. node.html(fm.escape(file.name));
  27998. }
  27999. }
  28000. if (!inError && pnode.length) {
  28001. input.off('blur');
  28002. if (cnt === 1 && name === file.name) {
  28003. return dfrd.reject();
  28004. }
  28005. if (fm.options.validName && fm.options.validName.test) {
  28006. try {
  28007. valid = fm.options.validName.test(name);
  28008. } catch(e) {
  28009. valid = false;
  28010. }
  28011. }
  28012. if (name === '.' || name === '..' || !valid) {
  28013. inError = true;
  28014. fm.error(file.mime === 'directory'? 'errInvDirname' : 'errInvName', {modal: true, close: function(){setTimeout(select, 120);}});
  28015. return false;
  28016. }
  28017. if (cnt === 1 && fm.fileByName(name, file.phash)) {
  28018. inError = true;
  28019. fm.error(['errExists', name], {modal: true, close: function(){setTimeout(select, 120);}});
  28020. return false;
  28021. }
  28022. if (cnt === 1) {
  28023. req();
  28024. } else {
  28025. fm.confirm({
  28026. title : 'cmdrename',
  28027. text : ['renameMultiple', cnt, getHint(name, [file.hash].concat(sel))],
  28028. accept : {
  28029. label : 'btnYes',
  28030. callback : req
  28031. },
  28032. cancel : {
  28033. label : 'btnCancel',
  28034. callback : function() {
  28035. setTimeout(function() {
  28036. inError = true;
  28037. select();
  28038. }, 120);
  28039. }
  28040. }
  28041. });
  28042. setTimeout(function() {
  28043. fm.trigger('unselectfiles', {files: fm.selected()})
  28044. .trigger('selectfiles', {files : [file.hash].concat(sel)});
  28045. }, 120);
  28046. }
  28047. }
  28048. },
  28049. input = $(tarea? '<textarea/>' : '<input type="text"/>')
  28050. .on('keyup text', function(){
  28051. if (tarea) {
  28052. this.style.height = '1px';
  28053. this.style.height = this.scrollHeight + 'px';
  28054. } else if (colwidth) {
  28055. this.style.width = colwidth + 'px';
  28056. if (this.scrollWidth > colwidth) {
  28057. this.style.width = this.scrollWidth + 10 + 'px';
  28058. }
  28059. }
  28060. })
  28061. .on('keydown', function(e) {
  28062. e.stopImmediatePropagation();
  28063. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  28064. dfrd.reject();
  28065. } else if (e.keyCode == $.ui.keyCode.ENTER) {
  28066. e.preventDefault();
  28067. input.trigger('blur');
  28068. }
  28069. })
  28070. .on('mousedown click dblclick', function(e) {
  28071. e.stopPropagation();
  28072. if (e.type === 'dblclick') {
  28073. e.preventDefault();
  28074. }
  28075. })
  28076. .on('blur', blur)
  28077. .on('dragenter dragleave dragover drop', function(e) {
  28078. // stop bubbling to prevent upload with native drop event
  28079. e.stopPropagation();
  28080. }),
  28081. select = function() {
  28082. var name = fm.splitFileExtention(input.val())[0];
  28083. if (!inError && fm.UA.Mobile && !fm.UA.iOS) { // since iOS has a bug? (z-index not effect) so disable it
  28084. overlay.on('click close', cancel).elfinderoverlay('show');
  28085. pnode.css('z-index', overlay.css('z-index') + 1);
  28086. }
  28087. ! fm.enabled() && fm.enable();
  28088. if (inError) {
  28089. inError = false;
  28090. input.on('blur', blur);
  28091. }
  28092. input.trigger('focus').trigger('select');
  28093. input[0].setSelectionRange && input[0].setSelectionRange(0, name.length);
  28094. },
  28095. node = navbar? target.contents().filter(function(){ return this.nodeType==3 && $(this).parent().attr('id') === fm.navHash2Id(file.hash); })
  28096. : target.find(filename),
  28097. pnode = node.parent(),
  28098. overlay = fm.getUI('overlay'),
  28099. cancel = function(e) {
  28100. if (!overlay.is(':hidden')) {
  28101. pnode.css('z-index', '');
  28102. }
  28103. if (! inError) {
  28104. dfrd.reject();
  28105. if (e) {
  28106. e.stopPropagation();
  28107. e.preventDefault();
  28108. }
  28109. }
  28110. },
  28111. resize = function() {
  28112. target.trigger('scrolltoview', {blink : false});
  28113. },
  28114. inError = false;
  28115. pnode.addClass('ui-front')
  28116. .css('position', 'relative')
  28117. .on('unselect.'+fm.namespace, unselect);
  28118. fm.bind('resize', resize);
  28119. if (navbar) {
  28120. node.replaceWith(input.val(file.name));
  28121. } else {
  28122. if (tarea) {
  28123. node.css('max-height', 'none');
  28124. } else if (!navbar) {
  28125. colwidth = pnode.width();
  28126. pnode.width(colwidth - 15)
  28127. .parent('td').css('overflow', 'visible');
  28128. }
  28129. node.empty().append(input.val(file.name));
  28130. }
  28131. if (cnt > 1 && fm.api <= 2.1030) {
  28132. return dfrd.reject();
  28133. }
  28134. if (!file || !node.length) {
  28135. return dfrd.reject('errCmdParams', this.title);
  28136. }
  28137. if (file.locked && !fm.isRoot(file)) {
  28138. return dfrd.reject(['errLocked', file.name]);
  28139. }
  28140. fm.one('select', function() {
  28141. input.parent().length && file && $.inArray(file.hash, fm.selected()) === -1 && input.trigger('blur');
  28142. });
  28143. input.trigger('keyup');
  28144. select();
  28145. return dfrd;
  28146. };
  28147. fm.bind('select contextmenucreate closecontextmenu', function(e) {
  28148. var sel = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected(),
  28149. file;
  28150. if (sel && sel.length === 1 && (file = fm.file(sel[0])) && fm.isRoot(file)) {
  28151. self.title = fm.i18n('kindAlias') + ' (' + fm.i18n('preference') + ')';
  28152. } else {
  28153. self.title = fm.i18n('cmdrename');
  28154. }
  28155. if (e.type !== 'closecontextmenu') {
  28156. self.update(void(0), self.title);
  28157. } else {
  28158. requestAnimationFrame(function() {
  28159. self.update(void(0), self.title);
  28160. });
  28161. }
  28162. }).remove(function(e) {
  28163. var rootNames;
  28164. if (e.data && e.data.removed && (rootNames = fm.storage('rootNames'))) {
  28165. $.each(e.data.removed, function(i, h) {
  28166. if (rootNames[h]) {
  28167. delete rootNames[h];
  28168. }
  28169. });
  28170. fm.storage('rootNames', rootNames);
  28171. }
  28172. });
  28173. };
  28174. /*
  28175. * File: /js/commands/resize.js
  28176. */
  28177. /**
  28178. * @class elFinder command "resize"
  28179. * Open dialog to resize image
  28180. *
  28181. * @author Dmitry (dio) Levashov
  28182. * @author Alexey Sukhotin
  28183. * @author Naoki Sawada
  28184. * @author Sergio Jovani
  28185. **/
  28186. elFinder.prototype.commands.resize = function() {
  28187. "use strict";
  28188. var losslessRotate = 0,
  28189. getBounceBox = function(w, h, theta) {
  28190. var srcPts = [
  28191. {x: w/2, y: h/2},
  28192. {x: -w/2, y: h/2},
  28193. {x: -w/2, y: -h/2},
  28194. {x: w/2, y: -h/2}
  28195. ],
  28196. dstPts = [],
  28197. min = {x: Number.MAX_VALUE, y: Number.MAX_VALUE},
  28198. max = {x: Number.MIN_VALUE, y: Number.MIN_VALUE};
  28199. $.each(srcPts, function(i, srcPt){
  28200. dstPts.push({
  28201. x: srcPt.x * Math.cos(theta) - srcPt.y * Math.sin(theta),
  28202. y: srcPt.x * Math.sin(theta) + srcPt.y * Math.cos(theta)
  28203. });
  28204. });
  28205. $.each(dstPts, function(i, pt) {
  28206. min.x = Math.min(min.x, pt.x);
  28207. min.y = Math.min(min.y, pt.y);
  28208. max.x = Math.max(max.x, pt.x);
  28209. max.y = Math.max(max.y, pt.y);
  28210. });
  28211. return {
  28212. width: max.x - min.x, height: max.y - min.y
  28213. };
  28214. };
  28215. this.updateOnSelect = false;
  28216. this.getstate = function() {
  28217. var sel = this.fm.selectedFiles();
  28218. return sel.length == 1 && sel[0].read && sel[0].write && sel[0].mime.indexOf('image/') !== -1 ? 0 : -1;
  28219. };
  28220. this.resizeRequest = function(data, f, dfrd) {
  28221. var fm = this.fm,
  28222. file = f || fm.file(data.target),
  28223. tmb = file? file.tmb : null,
  28224. enabled = fm.isCommandEnabled('resize', data.target);
  28225. if (enabled && (! file || (file && file.read && file.write && file.mime.indexOf('image/') !== -1 ))) {
  28226. return fm.request({
  28227. data : Object.assign(data, {
  28228. cmd : 'resize'
  28229. }),
  28230. notify : {type : 'resize', cnt : 1}
  28231. })
  28232. .fail(function(error) {
  28233. if (dfrd) {
  28234. dfrd.reject(error);
  28235. }
  28236. })
  28237. .done(function() {
  28238. if (data.quality) {
  28239. fm.storage('jpgQuality', data.quality === fm.option('jpgQuality')? null : data.quality);
  28240. }
  28241. dfrd && dfrd.resolve();
  28242. });
  28243. } else {
  28244. var error;
  28245. if (file) {
  28246. if (file.mime.indexOf('image/') === -1) {
  28247. error = ['errResize', file.name, 'errUsupportType'];
  28248. } else {
  28249. error = ['errResize', file.name, 'errPerm'];
  28250. }
  28251. } else {
  28252. error = ['errResize', data.target, 'errPerm'];
  28253. }
  28254. if (dfrd) {
  28255. dfrd.reject(error);
  28256. } else {
  28257. fm.error(error);
  28258. }
  28259. return $.Deferred().reject(error);
  28260. }
  28261. };
  28262. this.exec = function(hashes) {
  28263. var self = this,
  28264. fm = this.fm,
  28265. files = this.files(hashes),
  28266. dfrd = $.Deferred(),
  28267. api2 = (fm.api > 1),
  28268. options = this.options,
  28269. dialogWidth = 650,
  28270. fmnode = fm.getUI(),
  28271. ctrgrup = $().controlgroup? 'controlgroup' : 'buttonset',
  28272. grid8Def = typeof options.grid8px === 'undefined' || options.grid8px !== 'disable'? true : false,
  28273. presetSize = Array.isArray(options.presetSize)? options.presetSize : [],
  28274. clactive = 'elfinder-dialog-active',
  28275. clsediting = fm.res('class', 'editing'),
  28276. open = function(file, id) {
  28277. var isJpeg = (file.mime === 'image/jpeg'),
  28278. dialog = $('<div class="elfinder-resize-container"/>'),
  28279. input = '<input type="number" class="ui-corner-all"/>',
  28280. row = '<div class="elfinder-resize-row"/>',
  28281. label = '<div class="elfinder-resize-label"/>',
  28282. changeTm = null,
  28283. operate = false,
  28284. opStart = function() { operate = true; },
  28285. opStop = function() {
  28286. if (operate) {
  28287. operate = false;
  28288. control.trigger('change');
  28289. }
  28290. },
  28291. control = $('<div class="elfinder-resize-control"/>')
  28292. .on('focus', 'input[type=text],input[type=number]', function() {
  28293. $(this).trigger('select');
  28294. })
  28295. .on('change', function() {
  28296. changeTm && cancelAnimationFrame(changeTm);
  28297. changeTm = requestAnimationFrame(function() {
  28298. var panel, quty, canvas, ctx, img, sx, sy, sw, sh, deg, theta, bb;
  28299. if (sizeImg && ! operate && (canvas = sizeImg.data('canvas'))) {
  28300. panel = control.children('div.elfinder-resize-control-panel:visible');
  28301. quty = panel.find('input.elfinder-resize-quality');
  28302. if (quty.is(':visible')) {
  28303. ctx = sizeImg.data('ctx');
  28304. img = sizeImg.get(0);
  28305. if (panel.hasClass('elfinder-resize-uiresize')) {
  28306. // resize
  28307. sw = canvas.width = width.val();
  28308. sh = canvas.height = height.val();
  28309. ctx.drawImage(img, 0, 0, sw, sh);
  28310. } else if (panel.hasClass('elfinder-resize-uicrop')) {
  28311. // crop
  28312. sx = pointX.val();
  28313. sy = pointY.val();
  28314. sw = offsetX.val();
  28315. sh = offsetY.val();
  28316. canvas.width = sw;
  28317. canvas.height = sh;
  28318. ctx.drawImage(img, sx, sy, sw, sh, 0, 0, sw, sh);
  28319. } else {
  28320. // rotate
  28321. deg = degree.val();
  28322. theta = (degree.val() * Math.PI) / 180;
  28323. bb = getBounceBox(owidth, oheight, theta);
  28324. sw = canvas.width = bb.width;
  28325. sh = canvas.height = bb.height;
  28326. ctx.save();
  28327. if (deg % 90 !== 0) {
  28328. ctx.fillStyle = bg.val() || '#FFF';
  28329. ctx.fillRect(0, 0, sw, sh);
  28330. }
  28331. ctx.translate(sw / 2, sh / 2);
  28332. ctx.rotate(theta);
  28333. ctx.drawImage(img, -img.width/2, -img.height/2, owidth, oheight);
  28334. ctx.restore();
  28335. }
  28336. canvas.toBlob(function(blob) {
  28337. blob && quty.next('span').text(' (' + fm.formatSize(blob.size) + ')');
  28338. }, 'image/jpeg', Math.max(Math.min(quty.val(), 100), 1) / 100);
  28339. }
  28340. }
  28341. });
  28342. })
  28343. .on('mouseup', 'input', function(e) {
  28344. $(e.target).trigger('change');
  28345. }),
  28346. preview = $('<div class="elfinder-resize-preview"/>')
  28347. .on('touchmove', function(e) {
  28348. if ($(e.target).hasClass('touch-punch')) {
  28349. e.stopPropagation();
  28350. e.preventDefault();
  28351. }
  28352. }),
  28353. spinner = $('<div class="elfinder-resize-loading">'+fm.i18n('ntfloadimg')+'</div>'),
  28354. rhandle = $('<div class="elfinder-resize-handle touch-punch"/>'),
  28355. rhandlec = $('<div class="elfinder-resize-handle touch-punch"/>'),
  28356. uiresize = $('<div class="elfinder-resize-uiresize elfinder-resize-control-panel"/>'),
  28357. uicrop = $('<div class="elfinder-resize-uicrop elfinder-resize-control-panel"/>'),
  28358. uirotate = $('<div class="elfinder-resize-rotate elfinder-resize-control-panel"/>'),
  28359. uideg270 = $('<button/>').attr('title',fm.i18n('rotate-cw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-l"/>')),
  28360. uideg90 = $('<button/>').attr('title',fm.i18n('rotate-ccw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-r"/>')),
  28361. uiprop = $('<span />'),
  28362. reset = $('<button class="elfinder-resize-reset">').text(fm.i18n('reset'))
  28363. .on('click', function() {
  28364. resetView();
  28365. })
  28366. .button({
  28367. icons: {
  28368. primary: 'ui-icon-arrowrefresh-1-n'
  28369. },
  28370. text: false
  28371. }),
  28372. uitype = $('<div class="elfinder-resize-type"/>')
  28373. .append('<input type="radio" name="type" id="'+id+'-resize" value="resize" checked="checked" /><label for="'+id+'-resize">'+fm.i18n('resize')+'</label>',
  28374. '<input class="api2" type="radio" name="type" id="'+id+'-crop" value="crop" /><label class="api2" for="'+id+'-crop">'+fm.i18n('crop')+'</label>',
  28375. '<input class="api2" type="radio" name="type" id="'+id+'-rotate" value="rotate" /><label class="api2" for="'+id+'-rotate">'+fm.i18n('rotate')+'</label>'),
  28376. mode = 'resize',
  28377. type = uitype[ctrgrup]()[ctrgrup]('disable').find('input')
  28378. .on('change', function() {
  28379. mode = $(this).val();
  28380. resetView();
  28381. resizable(true);
  28382. croppable(true);
  28383. rotateable(true);
  28384. if (mode == 'resize') {
  28385. uiresize.show();
  28386. uirotate.hide();
  28387. uicrop.hide();
  28388. resizable();
  28389. isJpeg && grid8px.insertAfter(uiresize.find('.elfinder-resize-grid8'));
  28390. }
  28391. else if (mode == 'crop') {
  28392. uirotate.hide();
  28393. uiresize.hide();
  28394. uicrop.show();
  28395. croppable();
  28396. isJpeg && grid8px.insertAfter(uicrop.find('.elfinder-resize-grid8'));
  28397. } else if (mode == 'rotate') {
  28398. uiresize.hide();
  28399. uicrop.hide();
  28400. uirotate.show();
  28401. rotateable();
  28402. }
  28403. }),
  28404. width = $(input)
  28405. .on('change', function() {
  28406. var w = round(parseInt(width.val())),
  28407. h = round(cratio ? w/ratio : parseInt(height.val()));
  28408. if (w > 0 && h > 0) {
  28409. resize.updateView(w, h);
  28410. width.val(w);
  28411. height.val(h);
  28412. }
  28413. }).addClass('elfinder-focus'),
  28414. height = $(input)
  28415. .on('change', function() {
  28416. var h = round(parseInt(height.val())),
  28417. w = round(cratio ? h*ratio : parseInt(width.val()));
  28418. if (w > 0 && h > 0) {
  28419. resize.updateView(w, h);
  28420. width.val(w);
  28421. height.val(h);
  28422. }
  28423. }),
  28424. pointX = $(input).on('change', function(){crop.updateView();}),
  28425. pointY = $(input).on('change', function(){crop.updateView();}),
  28426. offsetX = $(input).on('change', function(){crop.updateView('w');}),
  28427. offsetY = $(input).on('change', function(){crop.updateView('h');}),
  28428. quality = isJpeg && api2?
  28429. $(input).val(fm.storage('jpgQuality') > 0? fm.storage('jpgQuality') : fm.option('jpgQuality'))
  28430. .addClass('elfinder-resize-quality')
  28431. .attr('min', '1').attr('max', '100').attr('title', '1 - 100')
  28432. .on('blur', function(){
  28433. var q = Math.min(100, Math.max(1, parseInt(this.value)));
  28434. control.find('input.elfinder-resize-quality').val(q);
  28435. })
  28436. : null,
  28437. degree = $('<input type="number" class="ui-corner-all" maxlength="3" value="0" />')
  28438. .on('change', function() {
  28439. rotate.update();
  28440. }),
  28441. uidegslider = $('<div class="elfinder-resize-rotate-slider touch-punch"/>')
  28442. .slider({
  28443. min: 0,
  28444. max: 360,
  28445. value: degree.val(),
  28446. animate: true,
  28447. start: opStart,
  28448. stop: opStop,
  28449. change: function(event, ui) {
  28450. if (ui.value != uidegslider.slider('value')) {
  28451. rotate.update(ui.value);
  28452. }
  28453. },
  28454. slide: function(event, ui) {
  28455. rotate.update(ui.value, false);
  28456. }
  28457. }).find('.ui-slider-handle')
  28458. .addClass('elfinder-tabstop')
  28459. .off('keydown')
  28460. .on('keydown', function(e) {
  28461. if (e.keyCode == $.ui.keyCode.LEFT || e.keyCode == $.ui.keyCode.RIGHT) {
  28462. e.stopPropagation();
  28463. e.preventDefault();
  28464. rotate.update(Number(degree.val()) + (e.keyCode == $.ui.keyCode.RIGHT? 1 : -1), false);
  28465. }
  28466. })
  28467. .end(),
  28468. pickimg,
  28469. pickcanv,
  28470. pickctx,
  28471. pickc = {},
  28472. pick = function(e) {
  28473. var color, r, g, b, h, s, l;
  28474. try {
  28475. color = pickc[Math.round(e.offsetX)][Math.round(e.offsetY)];
  28476. } catch(e) {}
  28477. if (!color) return;
  28478. r = color[0]; g = color[1]; b = color[2];
  28479. h = color[3]; s = color[4]; l = color[5];
  28480. setbg(r, g, b, (e.type === 'click'));
  28481. },
  28482. palpick = function(e) {
  28483. setbg($(this).css('backgroundColor'), '', '', (e.type === 'click'));
  28484. },
  28485. setbg = function(r, g, b, off) {
  28486. var s, m, cc;
  28487. if (typeof r === 'string') {
  28488. g = '';
  28489. if (r && (s = $('<span>').css('backgroundColor', r).css('backgroundColor')) && (m = s.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i))) {
  28490. r = Number(m[1]);
  28491. g = Number(m[2]);
  28492. b = Number(m[3]);
  28493. }
  28494. }
  28495. cc = (g === '')? r : '#' + getColorCode(r, g, b);
  28496. bg.val(cc).css({ backgroundColor: cc, backgroundImage: 'none', color: (r+g+b < 384? '#fff' : '#000') });
  28497. preview.css('backgroundColor', cc);
  28498. if (off) {
  28499. imgr.off('.picker').removeClass('elfinder-resize-picking');
  28500. pallet.off('.picker').removeClass('elfinder-resize-picking');
  28501. }
  28502. },
  28503. getColorCode = function(r, g, b) {
  28504. return $.map([r,g,b], function(c){return ('0'+parseInt(c).toString(16)).slice(-2);}).join('');
  28505. },
  28506. picker = $('<button>').text(fm.i18n('colorPicker'))
  28507. .on('click', function() {
  28508. imgr.on('mousemove.picker click.picker', pick).addClass('elfinder-resize-picking');
  28509. pallet.on('mousemove.picker click.picker', 'span', palpick).addClass('elfinder-resize-picking');
  28510. })
  28511. .button({
  28512. icons: {
  28513. primary: 'ui-icon-pin-s'
  28514. },
  28515. text: false
  28516. }),
  28517. reseter = $('<button>').text(fm.i18n('reset'))
  28518. .on('click', function() {
  28519. setbg('', '', '', true);
  28520. })
  28521. .button({
  28522. icons: {
  28523. primary: 'ui-icon-arrowrefresh-1-n'
  28524. },
  28525. text: false
  28526. }),
  28527. bg = $('<input class="ui-corner-all elfinder-resize-bg" type="text">')
  28528. .on('focus', function() {
  28529. $(this).attr('style', '');
  28530. })
  28531. .on('blur', function() {
  28532. setbg($(this).val());
  28533. }),
  28534. pallet = $('<div class="elfinder-resize-pallet">').on('click', 'span', function() {
  28535. setbg($(this).css('backgroundColor'));
  28536. }),
  28537. ratio = 1,
  28538. prop = 1,
  28539. owidth = 0,
  28540. oheight = 0,
  28541. cratio = true,
  28542. cratioc = false,
  28543. pwidth = 0,
  28544. pheight = 0,
  28545. rwidth = 0,
  28546. rheight = 0,
  28547. rdegree = 0,
  28548. grid8 = isJpeg? grid8Def : false,
  28549. constr = $('<button>').html(fm.i18n('aspectRatio'))
  28550. .on('click', function() {
  28551. cratio = ! cratio;
  28552. constr.button('option', {
  28553. icons : { primary: cratio? 'ui-icon-locked' : 'ui-icon-unlocked'}
  28554. });
  28555. resize.fixHeight();
  28556. rhandle.resizable('option', 'aspectRatio', cratio).data('uiResizable')._aspectRatio = cratio;
  28557. })
  28558. .button({
  28559. icons : {
  28560. primary: cratio? 'ui-icon-locked' : 'ui-icon-unlocked'
  28561. },
  28562. text: false
  28563. }),
  28564. constrc = $('<button>').html(fm.i18n('aspectRatio'))
  28565. .on('click', function() {
  28566. cratioc = ! cratioc;
  28567. constrc.button('option', {
  28568. icons : { primary: cratioc? 'ui-icon-locked' : 'ui-icon-unlocked'}
  28569. });
  28570. rhandlec.resizable('option', 'aspectRatio', cratioc).data('uiResizable')._aspectRatio = cratioc;
  28571. })
  28572. .button({
  28573. icons : {
  28574. primary: cratioc? 'ui-icon-locked' : 'ui-icon-unlocked'
  28575. },
  28576. text: false
  28577. }),
  28578. grid8px = $('<button>').html(fm.i18n(grid8? 'enabled' : 'disabled')).toggleClass('ui-state-active', grid8)
  28579. .on('click', function() {
  28580. grid8 = ! grid8;
  28581. grid8px.html(fm.i18n(grid8? 'enabled' : 'disabled')).toggleClass('ui-state-active', grid8);
  28582. setStep8();
  28583. })
  28584. .button(),
  28585. setStep8 = function() {
  28586. var step = grid8? 8 : 1;
  28587. $.each([width, height, offsetX, offsetY, pointX, pointY], function() {
  28588. this.attr('step', step);
  28589. });
  28590. if (grid8) {
  28591. width.val(round(width.val()));
  28592. height.val(round(height.val()));
  28593. offsetX.val(round(offsetX.val()));
  28594. offsetY.val(round(offsetY.val()));
  28595. pointX.val(round(pointX.val()));
  28596. pointY.val(round(pointY.val()));
  28597. if (uiresize.is(':visible')) {
  28598. resize.updateView(width.val(), height.val());
  28599. } else if (uicrop.is(':visible')) {
  28600. crop.updateView();
  28601. }
  28602. }
  28603. },
  28604. setuprimg = function() {
  28605. var r_scale,
  28606. fail = function() {
  28607. bg.parent().hide();
  28608. pallet.hide();
  28609. };
  28610. r_scale = Math.min(pwidth, pheight) / Math.sqrt(Math.pow(owidth, 2) + Math.pow(oheight, 2));
  28611. rwidth = Math.ceil(owidth * r_scale);
  28612. rheight = Math.ceil(oheight * r_scale);
  28613. imgr.width(rwidth)
  28614. .height(rheight)
  28615. .css('margin-top', (pheight-rheight)/2 + 'px')
  28616. .css('margin-left', (pwidth-rwidth)/2 + 'px');
  28617. if (imgr.is(':visible') && bg.is(':visible')) {
  28618. if (file.mime !== 'image/png') {
  28619. preview.css('backgroundColor', bg.val());
  28620. pickimg = $('<img>');
  28621. if (fm.isCORS) {
  28622. pickimg.attr('crossorigin', 'use-credentials');
  28623. }
  28624. pickimg.on('load', function() {
  28625. if (pickcanv && pickcanv.width !== rwidth) {
  28626. setColorData();
  28627. }
  28628. })
  28629. .on('error', fail)
  28630. .attr('src', canvSrc);
  28631. } else {
  28632. fail();
  28633. }
  28634. }
  28635. },
  28636. setupimg = function() {
  28637. resize.updateView(owidth, oheight);
  28638. setuprimg();
  28639. basec
  28640. .width(img.width())
  28641. .height(img.height());
  28642. imgc
  28643. .width(img.width())
  28644. .height(img.height());
  28645. crop.updateView();
  28646. jpgCalc();
  28647. },
  28648. setColorData = function() {
  28649. if (pickctx) {
  28650. var n, w, h, r, g, b, a, s, l, hsl, hue,
  28651. data, scale, tx1, tx2, ty1, ty2, rgb,
  28652. domi = {},
  28653. domic = [],
  28654. domiv, palc,
  28655. rgbToHsl = function (r, g, b) {
  28656. var h, s, l,
  28657. max = Math.max(Math.max(r, g), b),
  28658. min = Math.min(Math.min(r, g), b);
  28659. // Hue, 0 ~ 359
  28660. if (max === min) {
  28661. h = 0;
  28662. } else if (r === max) {
  28663. h = ((g - b) / (max - min) * 60 + 360) % 360;
  28664. } else if (g === max) {
  28665. h = (b - r) / (max - min) * 60 + 120;
  28666. } else if (b === max) {
  28667. h = (r - g) / (max - min) * 60 + 240;
  28668. }
  28669. // Saturation, 0 ~ 1
  28670. s = (max - min) / max;
  28671. // Lightness, 0 ~ 1
  28672. l = (r * 0.3 + g * 0.59 + b * 0.11) / 255;
  28673. return [h, s, l, 'hsl'];
  28674. },
  28675. rgbRound = function(c) {
  28676. return Math.round(c / 8) * 8;
  28677. };
  28678. calc:
  28679. try {
  28680. w = pickcanv.width = imgr.width();
  28681. h = pickcanv.height = imgr.height();
  28682. scale = w / owidth;
  28683. pickctx.scale(scale, scale);
  28684. pickctx.drawImage(pickimg.get(0), 0, 0);
  28685. data = pickctx.getImageData(0, 0, w, h).data;
  28686. // Range to detect the dominant color
  28687. tx1 = w * 0.1;
  28688. tx2 = w * 0.9;
  28689. ty1 = h * 0.1;
  28690. ty2 = h * 0.9;
  28691. for (var y = 0; y < h - 1; y++) {
  28692. for (var x = 0; x < w - 1; x++) {
  28693. n = x * 4 + y * w * 4;
  28694. // RGB
  28695. r = data[n]; g = data[n + 1]; b = data[n + 2]; a = data[n + 3];
  28696. // check alpha ch
  28697. if (a !== 255) {
  28698. bg.parent().hide();
  28699. pallet.hide();
  28700. break calc;
  28701. }
  28702. // HSL
  28703. hsl = rgbToHsl(r, g, b);
  28704. hue = Math.round(hsl[0]); s = Math.round(hsl[1] * 100); l = Math.round(hsl[2] * 100);
  28705. if (! pickc[x]) {
  28706. pickc[x] = {};
  28707. }
  28708. // set pickc
  28709. pickc[x][y] = [r, g, b, hue, s, l];
  28710. // detect the dominant color
  28711. if ((x < tx1 || x > tx2) && (y < ty1 || y > ty2)) {
  28712. rgb = rgbRound(r) + ',' + rgbRound(g) + ',' + rgbRound(b);
  28713. if (! domi[rgb]) {
  28714. domi[rgb] = 1;
  28715. } else {
  28716. ++domi[rgb];
  28717. }
  28718. }
  28719. }
  28720. }
  28721. if (! pallet.children(':first').length) {
  28722. palc = 1;
  28723. $.each(domi, function(c, v) {
  28724. domic.push({c: c, v: v});
  28725. });
  28726. $.each(domic.sort(function(a, b) {
  28727. return (a.v > b.v)? -1 : 1;
  28728. }), function() {
  28729. if (this.v < 2 || palc > 10) {
  28730. return false;
  28731. }
  28732. pallet.append($('<span style="width:20px;height:20px;display:inline-block;background-color:rgb('+this.c+');">'));
  28733. ++palc;
  28734. });
  28735. }
  28736. } catch(e) {
  28737. picker.hide();
  28738. pallet.hide();
  28739. }
  28740. }
  28741. },
  28742. setupPicker = function() {
  28743. try {
  28744. pickcanv = document.createElement('canvas');
  28745. pickctx = pickcanv.getContext('2d');
  28746. } catch(e) {
  28747. picker.hide();
  28748. pallet.hide();
  28749. }
  28750. },
  28751. setupPreset = function() {
  28752. preset.on('click', 'span.elfinder-resize-preset', function() {
  28753. var btn = $(this),
  28754. w = btn.data('s')[0],
  28755. h = btn.data('s')[1],
  28756. r = owidth / oheight;
  28757. btn.data('s', [h, w]).text(h + 'x' + w);
  28758. if (owidth > w || oheight > h) {
  28759. if (owidth <= w) {
  28760. w = round(h * r);
  28761. } else if (oheight <= h) {
  28762. h = round(w / r);
  28763. } else {
  28764. if (owidth - w > oheight - h) {
  28765. h = round(w / r);
  28766. } else {
  28767. w = round(h * r);
  28768. }
  28769. }
  28770. } else {
  28771. w = owidth;
  28772. h = oheight;
  28773. }
  28774. width.val(w);
  28775. height.val(h);
  28776. resize.updateView(w, h);
  28777. jpgCalc();
  28778. });
  28779. presetc.on('click', 'span.elfinder-resize-preset', function() {
  28780. var btn = $(this),
  28781. w = btn.data('s')[0],
  28782. h = btn.data('s')[1],
  28783. x = pointX.val(),
  28784. y = pointY.val();
  28785. btn.data('s', [h, w]).text(h + 'x' + w);
  28786. if (owidth >= w && oheight >= h) {
  28787. if (owidth - w - x < 0) {
  28788. x = owidth - w;
  28789. }
  28790. if (oheight - h - y < 0) {
  28791. y = oheight - h;
  28792. }
  28793. pointX.val(x);
  28794. pointY.val(y);
  28795. offsetX.val(w);
  28796. offsetY.val(h);
  28797. crop.updateView();
  28798. jpgCalc();
  28799. }
  28800. });
  28801. presetc.children('span.elfinder-resize-preset').each(function() {
  28802. var btn = $(this),
  28803. w = btn.data('s')[0],
  28804. h = btn.data('s')[1];
  28805. btn[(owidth >= w && oheight >= h)? 'show' : 'hide']();
  28806. });
  28807. },
  28808. dimreq = null,
  28809. inited = false,
  28810. setdim = function(dim) {
  28811. var rfile = fm.file(file.hash);
  28812. rfile.width = dim[0];
  28813. rfile.height = dim[1];
  28814. },
  28815. init = function() {
  28816. var elm, memSize, r_scale, imgRatio;
  28817. if (inited) {
  28818. return;
  28819. }
  28820. inited = true;
  28821. dimreq && dimreq.state && dimreq.state() === 'pending' && dimreq.reject();
  28822. // check lossless rotete
  28823. if (fm.api >= 2.1030) {
  28824. if (losslessRotate === 0) {
  28825. fm.request({
  28826. data: {
  28827. cmd : 'resize',
  28828. target : file.hash,
  28829. degree : 0,
  28830. mode : 'rotate'
  28831. },
  28832. preventDefault : true
  28833. }).done(function(data) {
  28834. losslessRotate = data.losslessRotate? 1 : -1;
  28835. if (losslessRotate === 1 && (degree.val() % 90 === 0)) {
  28836. uirotate.children('div.elfinder-resize-quality').hide();
  28837. }
  28838. }).fail(function() {
  28839. losslessRotate = -1;
  28840. });
  28841. }
  28842. } else {
  28843. losslessRotate = -1;
  28844. }
  28845. elm = img.get(0);
  28846. memSize = file.width && file.height? {w: file.width, h: file.height} : (elm.naturalWidth? null : {w: img.width(), h: img.height()});
  28847. memSize && img.removeAttr('width').removeAttr('height');
  28848. owidth = file.width || elm.naturalWidth || elm.width || img.width();
  28849. oheight = file.height || elm.naturalHeight || elm.height || img.height();
  28850. if (!file.width || !file.height) {
  28851. setdim([owidth, oheight]);
  28852. }
  28853. memSize && img.width(memSize.w).height(memSize.h);
  28854. dMinBtn.show();
  28855. imgRatio = oheight / owidth;
  28856. if (imgRatio < 1 && preview.height() > preview.width() * imgRatio) {
  28857. preview.height(preview.width() * imgRatio);
  28858. }
  28859. if (preview.height() > img.height() + 20) {
  28860. preview.height(img.height() + 20);
  28861. }
  28862. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  28863. spinner.remove();
  28864. ratio = owidth/oheight;
  28865. rhandle.append(img.show()).show();
  28866. width.val(owidth);
  28867. height.val(oheight);
  28868. setupPicker();
  28869. setupPreset();
  28870. setupimg();
  28871. uitype[ctrgrup]('enable');
  28872. control.find('input,select').prop('disabled', false)
  28873. .filter(':text').on('keydown', function(e) {
  28874. var cOpts;
  28875. if (e.keyCode == $.ui.keyCode.ENTER) {
  28876. e.stopPropagation();
  28877. e.preventDefault();
  28878. cOpts = {
  28879. title : $('input:checked', uitype).val(),
  28880. text : 'confirmReq',
  28881. accept : {
  28882. label : 'btnApply',
  28883. callback : function() {
  28884. save();
  28885. }
  28886. },
  28887. cancel : {
  28888. label : 'btnCancel',
  28889. callback : function(){
  28890. $(this).trigger('focus');
  28891. }
  28892. }
  28893. };
  28894. if (useSaveAs) {
  28895. cOpts['buttons'] = [{
  28896. label : 'btnSaveAs',
  28897. callback : function() {
  28898. requestAnimationFrame(saveAs);
  28899. }
  28900. }];
  28901. }
  28902. fm.confirm(cOpts);
  28903. return;
  28904. }
  28905. })
  28906. .on('keyup', function() {
  28907. var $this = $(this);
  28908. if (! $this.hasClass('elfinder-resize-bg')) {
  28909. requestAnimationFrame(function() {
  28910. $this.val($this.val().replace(/[^0-9]/g, ''));
  28911. });
  28912. }
  28913. })
  28914. .filter(':first');
  28915. setStep8();
  28916. !fm.UA.Mobile && width.trigger('focus');
  28917. resizable();
  28918. },
  28919. img = $('<img/>')
  28920. .on('load', init)
  28921. .on('error', function() {
  28922. spinner.text('Unable to load image').css('background', 'transparent');
  28923. }),
  28924. basec = $('<div/>'),
  28925. imgc = $('<img/>'),
  28926. coverc = $('<div/>'),
  28927. imgr = $('<img class="elfinder-resize-imgrotate" />'),
  28928. round = function(v, max) {
  28929. v = grid8? Math.round(v/8)*8 : Math.round(v);
  28930. v = Math.max(0, v);
  28931. if (max && v > max) {
  28932. v = grid8? Math.floor(max/8)*8 : max;
  28933. }
  28934. return v;
  28935. },
  28936. resetView = function() {
  28937. width.val(owidth);
  28938. height.val(oheight);
  28939. resize.updateView(owidth, oheight);
  28940. pointX.val(0);
  28941. pointY.val(0);
  28942. offsetX.val(owidth);
  28943. offsetY.val(oheight);
  28944. crop.updateView();
  28945. jpgCalc();
  28946. },
  28947. resize = {
  28948. update : function() {
  28949. width.val(round(img.width()/prop));
  28950. height.val(round(img.height()/prop));
  28951. jpgCalc();
  28952. },
  28953. updateView : function(w, h) {
  28954. if (w > pwidth || h > pheight) {
  28955. if (w / pwidth > h / pheight) {
  28956. prop = pwidth / w;
  28957. img.width(pwidth).height(round(h*prop));
  28958. } else {
  28959. prop = pheight / h;
  28960. img.height(pheight).width(round(w*prop));
  28961. }
  28962. } else {
  28963. img.width(round(w)).height(round(h));
  28964. }
  28965. prop = img.width()/w;
  28966. uiprop.text('1 : '+(1/prop).toFixed(2));
  28967. resize.updateHandle();
  28968. },
  28969. updateHandle : function() {
  28970. rhandle.width(img.width()).height(img.height());
  28971. },
  28972. fixHeight : function() {
  28973. var w, h;
  28974. if (cratio) {
  28975. w = width.val();
  28976. h = round(w/ratio);
  28977. resize.updateView(w, h);
  28978. height.val(h);
  28979. }
  28980. }
  28981. },
  28982. crop = {
  28983. update : function(change) {
  28984. pointX.val(round(((rhandlec.data('x')||rhandlec.position().left))/prop, owidth));
  28985. pointY.val(round(((rhandlec.data('y')||rhandlec.position().top))/prop, oheight));
  28986. if (change !== 'xy') {
  28987. offsetX.val(round((rhandlec.data('w')||rhandlec.width())/prop, owidth - pointX.val()));
  28988. offsetY.val(round((rhandlec.data('h')||rhandlec.height())/prop, oheight - pointY.val()));
  28989. }
  28990. jpgCalc();
  28991. },
  28992. updateView : function(change) {
  28993. var r, x, y, w, h;
  28994. pointX.val(round(pointX.val(), owidth - (grid8? 8 : 1)));
  28995. pointY.val(round(pointY.val(), oheight - (grid8? 8 : 1)));
  28996. offsetX.val(round(offsetX.val(), owidth - pointX.val()));
  28997. offsetY.val(round(offsetY.val(), oheight - pointY.val()));
  28998. if (cratioc) {
  28999. r = coverc.width() / coverc.height();
  29000. if (change === 'w') {
  29001. offsetY.val(round(parseInt(offsetX.val()) / r));
  29002. } else if (change === 'h') {
  29003. offsetX.val(round(parseInt(offsetY.val()) * r));
  29004. }
  29005. }
  29006. x = Math.round(parseInt(pointX.val()) * prop);
  29007. y = Math.round(parseInt(pointY.val()) * prop);
  29008. if (change !== 'xy') {
  29009. w = Math.round(parseInt(offsetX.val()) * prop);
  29010. h = Math.round(parseInt(offsetY.val()) * prop);
  29011. } else {
  29012. w = rhandlec.data('w');
  29013. h = rhandlec.data('h');
  29014. }
  29015. rhandlec.data({x: x, y: y, w: w, h: h})
  29016. .width(w)
  29017. .height(h)
  29018. .css({left: x, top: y});
  29019. coverc.width(w)
  29020. .height(h);
  29021. },
  29022. resize_update : function(e, ui) {
  29023. rhandlec.data({x: ui.position.left, y: ui.position.top, w: ui.size.width, h: ui.size.height});
  29024. crop.update();
  29025. crop.updateView();
  29026. },
  29027. drag_update : function(e, ui) {
  29028. rhandlec.data({x: ui.position.left, y: ui.position.top});
  29029. crop.update('xy');
  29030. }
  29031. },
  29032. rotate = {
  29033. mouseStartAngle : 0,
  29034. imageStartAngle : 0,
  29035. imageBeingRotated : false,
  29036. setQuality : function() {
  29037. uirotate.children('div.elfinder-resize-quality')[(losslessRotate > 0 && (degree.val() % 90) === 0)? 'hide' : 'show']();
  29038. },
  29039. update : function(value, animate) {
  29040. if (typeof value == 'undefined') {
  29041. rdegree = value = parseInt(degree.val());
  29042. }
  29043. if (typeof animate == 'undefined') {
  29044. animate = true;
  29045. }
  29046. if (! animate || fm.UA.Opera || fm.UA.ltIE8) {
  29047. imgr.rotate(value);
  29048. } else {
  29049. imgr.animate({rotate: value + 'deg'});
  29050. }
  29051. value = value % 360;
  29052. if (value < 0) {
  29053. value += 360;
  29054. }
  29055. degree.val(parseInt(value));
  29056. uidegslider.slider('value', degree.val());
  29057. rotate.setQuality();
  29058. },
  29059. execute : function ( e ) {
  29060. if ( !rotate.imageBeingRotated ) return;
  29061. var imageCentre = rotate.getCenter( imgr );
  29062. var ev = e.originalEvent.touches? e.originalEvent.touches[0] : e;
  29063. var mouseXFromCentre = ev.pageX - imageCentre[0];
  29064. var mouseYFromCentre = ev.pageY - imageCentre[1];
  29065. var mouseAngle = Math.atan2( mouseYFromCentre, mouseXFromCentre );
  29066. var rotateAngle = mouseAngle - rotate.mouseStartAngle + rotate.imageStartAngle;
  29067. rotateAngle = Math.round(parseFloat(rotateAngle) * 180 / Math.PI);
  29068. if ( e.shiftKey ) {
  29069. rotateAngle = Math.round((rotateAngle + 6)/15) * 15;
  29070. }
  29071. imgr.rotate(rotateAngle);
  29072. rotateAngle = rotateAngle % 360;
  29073. if (rotateAngle < 0) {
  29074. rotateAngle += 360;
  29075. }
  29076. degree.val(rotateAngle);
  29077. uidegslider.slider('value', degree.val());
  29078. rotate.setQuality();
  29079. return false;
  29080. },
  29081. start : function ( e ) {
  29082. if (imgr.hasClass('elfinder-resize-picking')) {
  29083. return;
  29084. }
  29085. opStart();
  29086. rotate.imageBeingRotated = true;
  29087. var imageCentre = rotate.getCenter( imgr );
  29088. var ev = e.originalEvent.touches? e.originalEvent.touches[0] : e;
  29089. var mouseStartXFromCentre = ev.pageX - imageCentre[0];
  29090. var mouseStartYFromCentre = ev.pageY - imageCentre[1];
  29091. rotate.mouseStartAngle = Math.atan2( mouseStartYFromCentre, mouseStartXFromCentre );
  29092. rotate.imageStartAngle = parseFloat(imgr.rotate()) * Math.PI / 180.0;
  29093. $(document).on('mousemove', rotate.execute);
  29094. imgr.on('touchmove', rotate.execute);
  29095. return false;
  29096. },
  29097. stop : function ( e ) {
  29098. if ( !rotate.imageBeingRotated ) return;
  29099. $(document).off('mousemove', rotate.execute);
  29100. imgr.off('touchmove', rotate.execute);
  29101. requestAnimationFrame(function() { rotate.imageBeingRotated = false; });
  29102. opStop();
  29103. return false;
  29104. },
  29105. getCenter : function ( image ) {
  29106. var currentRotation = imgr.rotate();
  29107. imgr.rotate(0);
  29108. var imageOffset = imgr.offset();
  29109. var imageCentreX = imageOffset.left + imgr.width() / 2;
  29110. var imageCentreY = imageOffset.top + imgr.height() / 2;
  29111. imgr.rotate(currentRotation);
  29112. return Array( imageCentreX, imageCentreY );
  29113. }
  29114. },
  29115. resizable = function(destroy) {
  29116. if (destroy) {
  29117. rhandle.filter(':ui-resizable').resizable('destroy');
  29118. rhandle.hide();
  29119. }
  29120. else {
  29121. rhandle.show();
  29122. rhandle.resizable({
  29123. alsoResize : img,
  29124. aspectRatio : cratio,
  29125. resize : resize.update,
  29126. start : opStart,
  29127. stop : function(e) {
  29128. resize.fixHeight;
  29129. resize.updateView(width.val(), height.val());
  29130. opStop();
  29131. }
  29132. });
  29133. dinit();
  29134. }
  29135. },
  29136. croppable = function(destroy) {
  29137. if (destroy) {
  29138. rhandlec.filter(':ui-resizable').resizable('destroy')
  29139. .filter(':ui-draggable').draggable('destroy');
  29140. basec.hide();
  29141. }
  29142. else {
  29143. basec.show();
  29144. rhandlec
  29145. .resizable({
  29146. containment : basec,
  29147. aspectRatio : cratioc,
  29148. resize : crop.resize_update,
  29149. start : opStart,
  29150. stop : opStop,
  29151. handles : 'all'
  29152. })
  29153. .draggable({
  29154. handle : coverc,
  29155. containment : imgc,
  29156. drag : crop.drag_update,
  29157. start : opStart,
  29158. stop : function() {
  29159. crop.updateView('xy');
  29160. opStop();
  29161. }
  29162. });
  29163. dinit();
  29164. crop.update();
  29165. }
  29166. },
  29167. rotateable = function(destroy) {
  29168. if (destroy) {
  29169. imgr.hide();
  29170. }
  29171. else {
  29172. imgr.show();
  29173. dinit();
  29174. }
  29175. },
  29176. checkVals = function() {
  29177. var w, h, x, y, d, q, b = '';
  29178. if (mode == 'resize') {
  29179. w = parseInt(width.val()) || 0;
  29180. h = parseInt(height.val()) || 0;
  29181. } else if (mode == 'crop') {
  29182. w = parseInt(offsetX.val()) || 0;
  29183. h = parseInt(offsetY.val()) || 0;
  29184. x = parseInt(pointX.val()) || 0;
  29185. y = parseInt(pointY.val()) || 0;
  29186. } else if (mode == 'rotate') {
  29187. w = owidth;
  29188. h = oheight;
  29189. d = parseInt(degree.val()) || 0;
  29190. if (d < 0 || d > 360) {
  29191. fm.error('Invalid rotate degree');
  29192. return false;
  29193. }
  29194. if (d == 0 || d == 360) {
  29195. fm.error('errResizeNoChange');
  29196. return false;
  29197. }
  29198. b = bg.val();
  29199. }
  29200. q = quality? parseInt(quality.val()) : 0;
  29201. if (mode != 'rotate') {
  29202. if (w <= 0 || h <= 0) {
  29203. fm.error('Invalid image size');
  29204. return false;
  29205. }
  29206. if (w == owidth && h == oheight) {
  29207. fm.error('errResizeNoChange');
  29208. return false;
  29209. }
  29210. }
  29211. return {w: w, h: h, x: x, y: y, d: d, q: q, b: b};
  29212. },
  29213. save = function() {
  29214. var vals;
  29215. if (vals = checkVals()) {
  29216. dialog.elfinderdialog('close');
  29217. self.resizeRequest({
  29218. target : file.hash,
  29219. width : vals.w,
  29220. height : vals.h,
  29221. x : vals.x,
  29222. y : vals.y,
  29223. degree : vals.d,
  29224. quality: vals.q,
  29225. bg : vals.b,
  29226. mode : mode
  29227. }, file, dfrd);
  29228. }
  29229. },
  29230. saveAs = function() {
  29231. var fail = function() {
  29232. dialogs.addClass(clsediting).fadeIn(function() {
  29233. base.addClass(clactive);
  29234. });
  29235. fm.disable();
  29236. },
  29237. make = function() {
  29238. self.mime = file.mime;
  29239. self.prefix = file.name.replace(/ \d+(\.[^.]+)?$/, '$1');
  29240. self.requestCmd = 'mkfile';
  29241. self.nextAction = {};
  29242. self.data = {target : file.phash};
  29243. $.proxy(fm.res('mixin', 'make'), self)()
  29244. .done(function(data) {
  29245. var hash, dfd;
  29246. if (data.added && data.added.length) {
  29247. hash = data.added[0].hash;
  29248. dfd = fm.api < 2.1032? fm.url(file.hash, { async: true, temporary: true }) : null;
  29249. $.when(dfd).done(function(url) {
  29250. fm.request({
  29251. options : {type : 'post'},
  29252. data : {
  29253. cmd : 'put',
  29254. target : hash,
  29255. encoding: dfd? 'scheme' : 'hash',
  29256. content : dfd? fm.convAbsUrl(url) : file.hash
  29257. },
  29258. notify : {type : 'copy', cnt : 1},
  29259. syncOnFail : true
  29260. })
  29261. .fail(fail)
  29262. .done(function(data) {
  29263. data = fm.normalize(data);
  29264. fm.updateCache(data);
  29265. file = fm.file(hash);
  29266. data.changed && data.changed.length && fm.change(data);
  29267. base.show().find('.elfinder-dialog-title').html(fm.escape(file.name));
  29268. save();
  29269. dialogs.fadeIn();
  29270. });
  29271. }).fail(fail);
  29272. } else {
  29273. fail();
  29274. }
  29275. })
  29276. .fail(fail)
  29277. .always(function() {
  29278. delete self.mime;
  29279. delete self.prefix;
  29280. delete self.nextAction;
  29281. delete self.data;
  29282. });
  29283. fm.trigger('unselectfiles', { files: [ file.hash ] });
  29284. },
  29285. reqOpen = null,
  29286. dialogs;
  29287. if (checkVals()) {
  29288. dialogs = fmnode.children('.' + self.dialogClass + ':visible').removeClass(clsediting).fadeOut();
  29289. base.removeClass(clactive);
  29290. fm.enable();
  29291. if (fm.searchStatus.state < 2 && file.phash !== fm.cwd().hash) {
  29292. reqOpen = fm.exec('open', [file.phash], {thash: file.phash});
  29293. }
  29294. $.when([reqOpen]).done(function() {
  29295. reqOpen? fm.one('cwdrender', make) : make();
  29296. }).fail(fail);
  29297. }
  29298. },
  29299. buttons = {},
  29300. hline = 'elfinder-resize-handle-hline',
  29301. vline = 'elfinder-resize-handle-vline',
  29302. rpoint = 'elfinder-resize-handle-point',
  29303. src = fm.openUrl(file.hash),
  29304. canvSrc = fm.openUrl(file.hash, !fm.isSameOrigin(src)),
  29305. sizeImg = quality? $('<img>').attr('crossorigin', fm.isCORS? 'use-credentials' : '').attr('src', canvSrc).on('load', function() {
  29306. try {
  29307. var canv = document.createElement('canvas');
  29308. sizeImg.data('canvas', canv).data('ctx', canv.getContext('2d'));
  29309. jpgCalc();
  29310. } catch(e) {
  29311. sizeImg.removeData('canvas').removeData('ctx');
  29312. }
  29313. }) : null,
  29314. jpgCalc = function() {
  29315. control.find('input.elfinder-resize-quality:visible').trigger('change');
  29316. },
  29317. dinit = function(e) {
  29318. if (base.hasClass('elfinder-dialog-minimized') || base.is(':hidden')) {
  29319. return;
  29320. }
  29321. preset.hide();
  29322. presetc.hide();
  29323. var win = fm.options.dialogContained? fmnode : $(window),
  29324. winH = win.height(),
  29325. winW = win.width(),
  29326. presW = 'auto',
  29327. presIn = true,
  29328. dw, ctrW, prvW;
  29329. base.width(Math.min(dialogWidth, winW - 30));
  29330. preview.attr('style', '');
  29331. if (owidth && oheight) {
  29332. pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width());
  29333. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  29334. resize.updateView(owidth, oheight);
  29335. }
  29336. ctrW = dialog.find('div.elfinder-resize-control').width();
  29337. prvW = preview.width();
  29338. dw = dialog.width() - 20;
  29339. if (prvW > dw) {
  29340. preview.width(dw);
  29341. presIn = false;
  29342. } else if ((dw - prvW) < ctrW) {
  29343. if (winW > winH) {
  29344. preview.width(dw - ctrW - 20);
  29345. } else {
  29346. preview.css({ float: 'none', marginLeft: 'auto', marginRight: 'auto'});
  29347. presIn = false;
  29348. }
  29349. }
  29350. if (presIn) {
  29351. presW = ctrW;
  29352. }
  29353. pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width());
  29354. if (fmnode.hasClass('elfinder-fullscreen')) {
  29355. if (base.height() > winH) {
  29356. winH -= 2;
  29357. preview.height(winH - base.height() + preview.height());
  29358. base.css('top', 0 - fmnode.offset().top);
  29359. }
  29360. } else {
  29361. winH -= 30;
  29362. (preview.height() > winH) && preview.height(winH);
  29363. }
  29364. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  29365. if (owidth && oheight) {
  29366. setupimg();
  29367. }
  29368. if (img.height() && preview.height() > img.height() + 20) {
  29369. preview.height(img.height() + 20);
  29370. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  29371. setuprimg();
  29372. }
  29373. preset.css('width', presW).show();
  29374. presetc.css('width', presW).show();
  29375. if (!presetc.children('span.elfinder-resize-preset:visible').length) {
  29376. presetc.hide();
  29377. }
  29378. },
  29379. preset = (function() {
  29380. var sets = $('<fieldset class="elfinder-resize-preset-container">').append($('<legend>').html(fm.i18n('presets'))).hide(),
  29381. hasC;
  29382. $.each(presetSize, function(i, s) {
  29383. if (s.length === 2) {
  29384. hasC = true;
  29385. sets.append($('<span class="elfinder-resize-preset"/>')
  29386. .data('s', s)
  29387. .text(s[0]+'x'+s[1])
  29388. .button()
  29389. );
  29390. }
  29391. });
  29392. if (!hasC) {
  29393. return $();
  29394. } else {
  29395. return sets;
  29396. }
  29397. })(),
  29398. presetc = preset.clone(true),
  29399. useSaveAs = fm.uploadMimeCheck(file.mime, file.phash),
  29400. dMinBtn, base;
  29401. uiresize.append(
  29402. $(row).append($(label).text(fm.i18n('width')), width),
  29403. $(row).append($(label).text(fm.i18n('height')), height, $('<div class="elfinder-resize-whctrls">').append(constr, reset)),
  29404. (quality? $(row).append($(label).text(fm.i18n('quality')), quality, $('<span/>')) : $()),
  29405. (isJpeg? $(row).append($(label).text(fm.i18n('8pxgrid')).addClass('elfinder-resize-grid8'), grid8px) : $()),
  29406. $(row).append($(label).text(fm.i18n('scale')), uiprop),
  29407. $(row).append(preset)
  29408. );
  29409. if (api2) {
  29410. uicrop.append(
  29411. $(row).append($(label).text('X'), pointX),
  29412. $(row).append($(label).text('Y')).append(pointY),
  29413. $(row).append($(label).text(fm.i18n('width')), offsetX),
  29414. $(row).append($(label).text(fm.i18n('height')), offsetY, $('<div class="elfinder-resize-whctrls">').append(constrc, reset.clone(true))),
  29415. (quality? $(row).append($(label).text(fm.i18n('quality')), quality.clone(true), $('<span/>')) : $()),
  29416. (isJpeg? $(row).append($(label).text(fm.i18n('8pxgrid')).addClass('elfinder-resize-grid8')) : $()),
  29417. $(row).append(presetc)
  29418. );
  29419. uirotate.append(
  29420. $(row).addClass('elfinder-resize-degree').append(
  29421. $(label).text(fm.i18n('rotate')),
  29422. degree,
  29423. $('<span/>').text(fm.i18n('degree')),
  29424. $('<div/>').append(uideg270, uideg90)[ctrgrup]()
  29425. ),
  29426. $(row).css('height', '20px').append(uidegslider),
  29427. ((quality)? $(row)[losslessRotate < 1? 'show' : 'hide']().addClass('elfinder-resize-quality').append(
  29428. $(label).text(fm.i18n('quality')),
  29429. quality.clone(true),
  29430. $('<span/>')) : $()
  29431. ),
  29432. $(row).append($(label).text(fm.i18n('bgcolor')), bg, picker, reseter),
  29433. $(row).css('height', '20px').append(pallet)
  29434. );
  29435. uideg270.on('click', function() {
  29436. rdegree = rdegree - 90;
  29437. rotate.update(rdegree);
  29438. });
  29439. uideg90.on('click', function(){
  29440. rdegree = rdegree + 90;
  29441. rotate.update(rdegree);
  29442. });
  29443. }
  29444. dialog.append(uitype).on('resize', function(e){
  29445. e.stopPropagation();
  29446. });
  29447. if (api2) {
  29448. control.append(/*$(row), */uiresize, uicrop.hide(), uirotate.hide());
  29449. } else {
  29450. control.append(/*$(row), */uiresize);
  29451. }
  29452. rhandle.append('<div class="'+hline+' '+hline+'-top"/>',
  29453. '<div class="'+hline+' '+hline+'-bottom"/>',
  29454. '<div class="'+vline+' '+vline+'-left"/>',
  29455. '<div class="'+vline+' '+vline+'-right"/>',
  29456. '<div class="'+rpoint+' '+rpoint+'-e"/>',
  29457. '<div class="'+rpoint+' '+rpoint+'-se"/>',
  29458. '<div class="'+rpoint+' '+rpoint+'-s"/>');
  29459. preview.append(spinner).append(rhandle.hide()).append(img.hide());
  29460. if (api2) {
  29461. rhandlec.css('position', 'absolute')
  29462. .append('<div class="'+hline+' '+hline+'-top"/>',
  29463. '<div class="'+hline+' '+hline+'-bottom"/>',
  29464. '<div class="'+vline+' '+vline+'-left"/>',
  29465. '<div class="'+vline+' '+vline+'-right"/>',
  29466. '<div class="'+rpoint+' '+rpoint+'-n"/>',
  29467. '<div class="'+rpoint+' '+rpoint+'-e"/>',
  29468. '<div class="'+rpoint+' '+rpoint+'-s"/>',
  29469. '<div class="'+rpoint+' '+rpoint+'-w"/>',
  29470. '<div class="'+rpoint+' '+rpoint+'-ne"/>',
  29471. '<div class="'+rpoint+' '+rpoint+'-se"/>',
  29472. '<div class="'+rpoint+' '+rpoint+'-sw"/>',
  29473. '<div class="'+rpoint+' '+rpoint+'-nw"/>');
  29474. preview.append(basec.css('position', 'absolute').hide().append(imgc, rhandlec.append(coverc)));
  29475. preview.append(imgr.hide());
  29476. }
  29477. preview.css('overflow', 'hidden');
  29478. dialog.append(preview, control);
  29479. buttons[fm.i18n('btnApply')] = save;
  29480. if (useSaveAs) {
  29481. buttons[fm.i18n('btnSaveAs')] = function() { requestAnimationFrame(saveAs); };
  29482. }
  29483. buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); };
  29484. dialog.find('input,button').addClass('elfinder-tabstop');
  29485. base = self.fmDialog(dialog, {
  29486. title : fm.escape(file.name),
  29487. width : dialogWidth,
  29488. resizable : false,
  29489. buttons : buttons,
  29490. open : function() {
  29491. var substituteImg = (fm.option('substituteImg', file.hash) && file.size > options.dimSubImgSize)? true : false,
  29492. hasSize = (file.width && file.height)? true : false;
  29493. dialog.parent().css('overflow', 'hidden');
  29494. dMinBtn = base.find('.ui-dialog-titlebar .elfinder-titlebar-minimize').hide();
  29495. fm.bind('resize', dinit);
  29496. img.attr('src', src);
  29497. imgc.attr('src', src);
  29498. imgr.attr('src', src);
  29499. if (api2) {
  29500. imgr.on('mousedown touchstart', rotate.start)
  29501. .on('touchend', rotate.stop);
  29502. base.on('mouseup', rotate.stop);
  29503. }
  29504. if (hasSize && !substituteImg) {
  29505. return init();
  29506. }
  29507. if (file.size > (options.getDimThreshold || 0)) {
  29508. dimreq = fm.request({
  29509. data : {cmd : 'dim', target : file.hash, substitute : (substituteImg? 400 : '')},
  29510. preventDefault : true
  29511. })
  29512. .done(function(data) {
  29513. if (data.dim) {
  29514. var dim = data.dim.split('x');
  29515. file.width = dim[0];
  29516. file.height = dim[1];
  29517. setdim(dim);
  29518. if (data.url) {
  29519. img.attr('src', data.url);
  29520. imgc.attr('src', data.url);
  29521. imgr.attr('src', data.url);
  29522. }
  29523. return init();
  29524. }
  29525. });
  29526. } else if (hasSize) {
  29527. return init();
  29528. }
  29529. },
  29530. close : function() {
  29531. if (api2) {
  29532. imgr.off('mousedown touchstart', rotate.start)
  29533. .off('touchend', rotate.stop);
  29534. $(document).off('mouseup', rotate.stop);
  29535. }
  29536. fm.unbind('resize', dinit);
  29537. $(this).elfinderdialog('destroy');
  29538. },
  29539. resize : function(e, data) {
  29540. if (data && data.minimize === 'off') {
  29541. dinit();
  29542. }
  29543. }
  29544. }).attr('id', id).closest('.ui-dialog').addClass(clsediting);
  29545. // for IE < 9 dialog mising at open second+ time.
  29546. if (fm.UA.ltIE8) {
  29547. $('.elfinder-dialog').css('filter', '');
  29548. }
  29549. coverc.css({ 'opacity': 0.2, 'background-color': '#fff', 'position': 'absolute'}),
  29550. rhandlec.css('cursor', 'move');
  29551. rhandlec.find('.elfinder-resize-handle-point').css({
  29552. 'background-color' : '#fff',
  29553. 'opacity': 0.5,
  29554. 'border-color':'#000'
  29555. });
  29556. if (! api2) {
  29557. uitype.find('.api2').remove();
  29558. }
  29559. control.find('input,select').prop('disabled', true);
  29560. control.find('input.elfinder-resize-quality')
  29561. .next('span').addClass('elfinder-resize-jpgsize').attr('title', fm.i18n('roughFileSize'));
  29562. },
  29563. id, dialog
  29564. ;
  29565. if (!files.length || files[0].mime.indexOf('image/') === -1) {
  29566. return dfrd.reject();
  29567. }
  29568. id = 'resize-'+fm.namespace+'-'+files[0].hash;
  29569. dialog = fmnode.find('#'+id);
  29570. if (dialog.length) {
  29571. dialog.elfinderdialog('toTop');
  29572. return dfrd.resolve();
  29573. }
  29574. open(files[0], id);
  29575. return dfrd;
  29576. };
  29577. };
  29578. (function ($) {
  29579. var findProperty = function (styleObject, styleArgs) {
  29580. var i = 0 ;
  29581. for( i in styleArgs) {
  29582. if (typeof styleObject[styleArgs[i]] != 'undefined')
  29583. return styleArgs[i];
  29584. }
  29585. styleObject[styleArgs[i]] = '';
  29586. return styleArgs[i];
  29587. };
  29588. $.cssHooks.rotate = {
  29589. get: function(elem, computed, extra) {
  29590. return $(elem).rotate();
  29591. },
  29592. set: function(elem, value) {
  29593. $(elem).rotate(value);
  29594. return value;
  29595. }
  29596. };
  29597. $.cssHooks.transform = {
  29598. get: function(elem, computed, extra) {
  29599. var name = findProperty( elem.style ,
  29600. ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] );
  29601. return elem.style[name];
  29602. },
  29603. set: function(elem, value) {
  29604. var name = findProperty( elem.style ,
  29605. ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] );
  29606. elem.style[name] = value;
  29607. return value;
  29608. }
  29609. };
  29610. $.fn.rotate = function(val) {
  29611. var r;
  29612. if (typeof val == 'undefined') {
  29613. if (!!window.opera) {
  29614. r = this.css('transform').match(/rotate\((.*?)\)/);
  29615. return ( r && r[1])?
  29616. Math.round(parseFloat(r[1]) * 180 / Math.PI) : 0;
  29617. } else {
  29618. r = this.css('transform').match(/rotate\((.*?)\)/);
  29619. return ( r && r[1])? parseInt(r[1]) : 0;
  29620. }
  29621. }
  29622. this.css('transform',
  29623. this.css('transform').replace(/none|rotate\(.*?\)/, '') + 'rotate(' + parseInt(val) + 'deg)');
  29624. return this;
  29625. };
  29626. $.fx.step.rotate = function(fx) {
  29627. if ( fx.state == 0 ) {
  29628. fx.start = $(fx.elem).rotate();
  29629. fx.now = fx.start;
  29630. }
  29631. $(fx.elem).rotate(fx.now);
  29632. };
  29633. if (typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined") { // IE & IE<9
  29634. var GetAbsoluteXY = function(element) {
  29635. var pnode = element;
  29636. var x = pnode.offsetLeft;
  29637. var y = pnode.offsetTop;
  29638. while ( pnode.offsetParent ) {
  29639. pnode = pnode.offsetParent;
  29640. if (pnode != document.body && pnode.currentStyle['position'] != 'static') {
  29641. break;
  29642. }
  29643. if (pnode != document.body && pnode != document.documentElement) {
  29644. x -= pnode.scrollLeft;
  29645. y -= pnode.scrollTop;
  29646. }
  29647. x += pnode.offsetLeft;
  29648. y += pnode.offsetTop;
  29649. }
  29650. return { x: x, y: y };
  29651. };
  29652. var StaticToAbsolute = function (element) {
  29653. if ( element.currentStyle['position'] != 'static') {
  29654. return ;
  29655. }
  29656. var xy = GetAbsoluteXY(element);
  29657. element.style.position = 'absolute' ;
  29658. element.style.left = xy.x + 'px';
  29659. element.style.top = xy.y + 'px';
  29660. };
  29661. var IETransform = function(element,transform){
  29662. var r;
  29663. var m11 = 1;
  29664. var m12 = 1;
  29665. var m21 = 1;
  29666. var m22 = 1;
  29667. if (typeof element.style['msTransform'] != 'undefined'){
  29668. return true;
  29669. }
  29670. StaticToAbsolute(element);
  29671. r = transform.match(/rotate\((.*?)\)/);
  29672. var rotate = ( r && r[1]) ? parseInt(r[1]) : 0;
  29673. rotate = rotate % 360;
  29674. if (rotate < 0) rotate = 360 + rotate;
  29675. var radian= rotate * Math.PI / 180;
  29676. var cosX =Math.cos(radian);
  29677. var sinY =Math.sin(radian);
  29678. m11 *= cosX;
  29679. m12 *= -sinY;
  29680. m21 *= sinY;
  29681. m22 *= cosX;
  29682. element.style.filter = (element.style.filter || '').replace(/progid:DXImageTransform\.Microsoft\.Matrix\([^)]*\)/, "" ) +
  29683. ("progid:DXImageTransform.Microsoft.Matrix(" +
  29684. "M11=" + m11 +
  29685. ",M12=" + m12 +
  29686. ",M21=" + m21 +
  29687. ",M22=" + m22 +
  29688. ",FilterType='bilinear',sizingMethod='auto expand')")
  29689. ;
  29690. var ow = parseInt(element.style.width || element.width || 0 );
  29691. var oh = parseInt(element.style.height || element.height || 0 );
  29692. radian = rotate * Math.PI / 180;
  29693. var absCosX =Math.abs(Math.cos(radian));
  29694. var absSinY =Math.abs(Math.sin(radian));
  29695. var dx = (ow - (ow * absCosX + oh * absSinY)) / 2;
  29696. var dy = (oh - (ow * absSinY + oh * absCosX)) / 2;
  29697. element.style.marginLeft = Math.floor(dx) + "px";
  29698. element.style.marginTop = Math.floor(dy) + "px";
  29699. return(true);
  29700. };
  29701. var transform_set = $.cssHooks.transform.set;
  29702. $.cssHooks.transform.set = function(elem, value) {
  29703. transform_set.apply(this, [elem, value] );
  29704. IETransform(elem,value);
  29705. return value;
  29706. };
  29707. }
  29708. })(jQuery);
  29709. /*
  29710. * File: /js/commands/restore.js
  29711. */
  29712. /**
  29713. * @class elFinder command "restore"
  29714. * Restore items from the trash
  29715. *
  29716. * @author Naoki Sawada
  29717. **/
  29718. (elFinder.prototype.commands.restore = function() {
  29719. "use strict";
  29720. var self = this,
  29721. fm = this.fm,
  29722. fakeCnt = 0,
  29723. getFilesRecursively = function(files) {
  29724. var dfd = $.Deferred(),
  29725. dirs = [],
  29726. results = [],
  29727. reqs = [],
  29728. phashes = [],
  29729. getFile;
  29730. dfd._xhrReject = function() {
  29731. $.each(reqs, function() {
  29732. this && this.reject && this.reject();
  29733. });
  29734. getFile && getFile._xhrReject();
  29735. };
  29736. $.each(files, function(i, f) {
  29737. f.mime === 'directory'? dirs.push(f) : results.push(f);
  29738. });
  29739. if (dirs.length) {
  29740. $.each(dirs, function(i, d) {
  29741. reqs.push(fm.request({
  29742. data : {cmd : 'open', target : d.hash},
  29743. preventDefault : true,
  29744. asNotOpen : true
  29745. }));
  29746. phashes[i] = d.hash;
  29747. });
  29748. $.when.apply($, reqs).fail(function() {
  29749. dfd.reject();
  29750. }).done(function() {
  29751. var items = [];
  29752. $.each(arguments, function(i, r) {
  29753. var files;
  29754. if (r.files) {
  29755. if (r.files.length) {
  29756. items = items.concat(r.files);
  29757. } else {
  29758. items.push({
  29759. hash: 'fakefile_' + (fakeCnt++),
  29760. phash: phashes[i],
  29761. mime: 'fakefile',
  29762. name: 'fakefile',
  29763. ts: 0
  29764. });
  29765. }
  29766. }
  29767. });
  29768. fm.cache(items);
  29769. getFile = getFilesRecursively(items).done(function(res) {
  29770. results = results.concat(res);
  29771. dfd.resolve(results);
  29772. });
  29773. });
  29774. } else {
  29775. dfd.resolve(results);
  29776. }
  29777. return dfd;
  29778. },
  29779. restore = function(dfrd, files, targets, ops) {
  29780. var rHashes = {},
  29781. others = [],
  29782. found = false,
  29783. dirs = [],
  29784. opts = ops || {},
  29785. id = +new Date(),
  29786. tm, getFile;
  29787. fm.lockfiles({files : targets});
  29788. dirs = $.map(files, function(f) {
  29789. return f.mime === 'directory'? f.hash : null;
  29790. });
  29791. dfrd.done(function() {
  29792. dirs && fm.exec('rm', dirs, {forceRm : true, quiet : true});
  29793. }).always(function() {
  29794. fm.unlockfiles({files : targets});
  29795. });
  29796. tm = setTimeout(function() {
  29797. fm.notify({type : 'search', id : id, cnt : 1, hideCnt : true, cancel : function() {
  29798. getFile && getFile._xhrReject();
  29799. dfrd.reject();
  29800. }});
  29801. }, fm.notifyDelay);
  29802. fakeCnt = 0;
  29803. getFile = getFilesRecursively(files).always(function() {
  29804. tm && clearTimeout(tm);
  29805. fm.notify({type : 'search', id: id, cnt : -1, hideCnt : true});
  29806. }).fail(function() {
  29807. dfrd.reject('errRestore', 'errFileNotFound');
  29808. }).done(function(res) {
  29809. var errFolderNotfound = ['errRestore', 'errFolderNotFound'],
  29810. dirTop = '';
  29811. if (res.length) {
  29812. $.each(res, function(i, f) {
  29813. var phash = f.phash,
  29814. pfile,
  29815. srcRoot, tPath;
  29816. while(phash) {
  29817. if (srcRoot = fm.trashes[phash]) {
  29818. if (! rHashes[srcRoot]) {
  29819. if (found) {
  29820. // Keep items of other trash
  29821. others.push(f.hash);
  29822. return null; // continue $.each
  29823. }
  29824. rHashes[srcRoot] = {};
  29825. found = true;
  29826. }
  29827. tPath = fm.path(f.hash).substr(fm.path(phash).length).replace(/\\/g, '/');
  29828. tPath = tPath.replace(/\/[^\/]+?$/, '');
  29829. if (tPath === '') {
  29830. tPath = '/';
  29831. }
  29832. if (!rHashes[srcRoot][tPath]) {
  29833. rHashes[srcRoot][tPath] = [];
  29834. }
  29835. if (f.mime === 'fakefile') {
  29836. fm.updateCache({removed:[f.hash]});
  29837. } else {
  29838. rHashes[srcRoot][tPath].push(f.hash);
  29839. }
  29840. if (!dirTop || dirTop.length > tPath.length) {
  29841. dirTop = tPath;
  29842. }
  29843. break;
  29844. }
  29845. // Go up one level for next check
  29846. pfile = fm.file(phash);
  29847. if (!pfile) {
  29848. phash = false;
  29849. // Detection method for search results
  29850. $.each(fm.trashes, function(ph) {
  29851. var file = fm.file(ph),
  29852. filePath = fm.path(ph);
  29853. if ((!file.volumeid || f.hash.indexOf(file.volumeid) === 0) && fm.path(f.hash).indexOf(filePath) === 0) {
  29854. phash = ph;
  29855. return false;
  29856. }
  29857. });
  29858. } else {
  29859. phash = pfile.phash;
  29860. }
  29861. }
  29862. });
  29863. if (found) {
  29864. $.each(rHashes, function(src, dsts) {
  29865. var dirs = Object.keys(dsts),
  29866. cnt = dirs.length;
  29867. fm.request({
  29868. data : {cmd : 'mkdir', target : src, dirs : dirs},
  29869. notify : {type : 'chkdir', cnt : cnt},
  29870. preventFail : true
  29871. }).fail(function(error) {
  29872. dfrd.reject(error);
  29873. fm.unlockfiles({files : targets});
  29874. }).done(function(data) {
  29875. var cmdPaste, hashes;
  29876. if (hashes = data.hashes) {
  29877. cmdPaste = fm.getCommand('paste');
  29878. if (cmdPaste) {
  29879. // wait until file cache made
  29880. fm.one('mkdirdone', function() {
  29881. var hasErr = false;
  29882. $.each(dsts, function(dir, files) {
  29883. if (hashes[dir]) {
  29884. if (files.length) {
  29885. if (fm.file(hashes[dir])) {
  29886. fm.clipboard(files, true);
  29887. fm.exec('paste', [ hashes[dir] ], {_cmd : 'restore', noToast : (opts.noToast || dir !== dirTop)})
  29888. .done(function(data) {
  29889. if (data && (data.error || data.warning)) {
  29890. hasErr = true;
  29891. }
  29892. })
  29893. .fail(function() {
  29894. hasErr = true;
  29895. })
  29896. .always(function() {
  29897. if (--cnt < 1) {
  29898. dfrd[hasErr? 'reject' : 'resolve']();
  29899. if (others.length) {
  29900. // Restore items of other trash
  29901. fm.exec('restore', others);
  29902. }
  29903. }
  29904. });
  29905. } else {
  29906. dfrd.reject(errFolderNotfound);
  29907. }
  29908. } else {
  29909. if (--cnt < 1) {
  29910. dfrd.resolve();
  29911. if (others.length) {
  29912. // Restore items of other trash
  29913. fm.exec('restore', others);
  29914. }
  29915. }
  29916. }
  29917. }
  29918. });
  29919. });
  29920. } else {
  29921. dfrd.reject(['errRestore', 'errCmdNoSupport', '(paste)']);
  29922. }
  29923. } else {
  29924. dfrd.reject(errFolderNotfound);
  29925. }
  29926. });
  29927. });
  29928. } else {
  29929. dfrd.reject(errFolderNotfound);
  29930. }
  29931. } else {
  29932. dfrd.reject('errFileNotFound');
  29933. dirs && fm.exec('rm', dirs, {forceRm : true, quiet : true});
  29934. }
  29935. });
  29936. };
  29937. // for to be able to overwrite
  29938. this.restore = restore;
  29939. this.linkedCmds = ['copy', 'paste', 'mkdir', 'rm'];
  29940. this.updateOnSelect = false;
  29941. this.init = function() {
  29942. // re-assign for extended command
  29943. self = this;
  29944. fm = this.fm;
  29945. };
  29946. this.getstate = function(sel, e) {
  29947. sel = sel || fm.selected();
  29948. return sel.length && $.grep(sel, function(h) {var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? true : false; }).length == sel.length
  29949. ? 0 : -1;
  29950. };
  29951. this.exec = function(hashes, opts) {
  29952. var dfrd = $.Deferred()
  29953. .fail(function(error) {
  29954. error && fm.error(error);
  29955. }),
  29956. files = self.files(hashes);
  29957. if (! files.length) {
  29958. return dfrd.reject();
  29959. }
  29960. $.each(files, function(i, file) {
  29961. if (fm.isRoot(file)) {
  29962. return !dfrd.reject(['errRestore', file.name]);
  29963. }
  29964. if (file.locked) {
  29965. return !dfrd.reject(['errLocked', file.name]);
  29966. }
  29967. });
  29968. if (dfrd.state() === 'pending') {
  29969. this.restore(dfrd, files, hashes, opts);
  29970. }
  29971. return dfrd;
  29972. };
  29973. }).prototype = { forceLoad : true }; // this is required command
  29974. /*
  29975. * File: /js/commands/rm.js
  29976. */
  29977. /**
  29978. * @class elFinder command "rm"
  29979. * Delete files
  29980. *
  29981. * @author Dmitry (dio) Levashov
  29982. * @author Naoki Sawada
  29983. **/
  29984. elFinder.prototype.commands.rm = function() {
  29985. "use strict";
  29986. var self = this,
  29987. fm = this.fm,
  29988. 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>',
  29989. confirm = function(dfrd, targets, files, tHash, addTexts) {
  29990. var cnt = targets.length,
  29991. cwd = fm.cwd().hash,
  29992. descs = [],
  29993. spinner = fm.i18n('calc') + '<span class="elfinder-spinner"/>',
  29994. dialog, text, tmb, size, f, fname;
  29995. if (cnt > 1) {
  29996. size = 0;
  29997. $.each(files, function(h, f) {
  29998. if (f.size && f.size != 'unknown' && f.mime !== 'directory') {
  29999. var s = parseInt(f.size);
  30000. if (s >= 0 && size >= 0) {
  30001. size += s;
  30002. }
  30003. } else {
  30004. size = 'unknown';
  30005. return false;
  30006. }
  30007. });
  30008. getSize = (size === 'unknown');
  30009. descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(size)));
  30010. text = [$(tpl.replace('{class}', 'elfinder-cwd-icon-group').replace('{title}', '<strong>' + fm.i18n('items')+ ': ' + cnt + '</strong>').replace('{desc}', descs.join('<br>')))];
  30011. } else {
  30012. f = files[0];
  30013. tmb = fm.tmb(f);
  30014. getSize = (f.mime === 'directory');
  30015. descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(f.size)));
  30016. descs.push(fm.i18n('modify')+': '+fm.formatDate(f));
  30017. fname = fm.escape(f.i18 || f.name).replace(/([_.])/g, '&#8203;$1');
  30018. text = [$(tpl.replace('{class}', fm.mime2class(f.mime)).replace('{title}', '<strong>' + fname + '</strong>').replace('{desc}', descs.join('<br>')))];
  30019. }
  30020. if (addTexts) {
  30021. text = text.concat(addTexts);
  30022. }
  30023. text.push(tHash? 'confirmTrash' : 'confirmRm');
  30024. dialog = fm.confirm({
  30025. title : self.title,
  30026. text : text,
  30027. accept : {
  30028. label : 'btnRm',
  30029. callback : function() {
  30030. if (tHash) {
  30031. self.toTrash(dfrd, targets, tHash);
  30032. } else {
  30033. remove(dfrd, targets);
  30034. }
  30035. }
  30036. },
  30037. cancel : {
  30038. label : 'btnCancel',
  30039. callback : function() {
  30040. fm.unlockfiles({files : targets});
  30041. if (targets.length === 1 && fm.file(targets[0]).phash !== cwd) {
  30042. fm.select({selected : targets});
  30043. } else {
  30044. fm.selectfiles({files : targets});
  30045. }
  30046. dfrd.reject();
  30047. }
  30048. }
  30049. });
  30050. // load thumbnail
  30051. if (tmb) {
  30052. $('<img/>')
  30053. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  30054. .attr('src', tmb.url);
  30055. }
  30056. if (getSize) {
  30057. getSize = fm.getSize($.map(files, function(f) { return f.mime === 'directory'? f.hash : null; })).done(function(data) {
  30058. dialog.find('span.elfinder-spinner').parent().html(fm.i18n('size')+': '+data.formated);
  30059. }).fail(function() {
  30060. dialog.find('span.elfinder-spinner').parent().html(fm.i18n('size')+': '+fm.i18n('unknown'));
  30061. }).always(function() {
  30062. getSize = false;
  30063. });
  30064. }
  30065. },
  30066. toTrash = function(dfrd, targets, tHash) {
  30067. var dsts = {},
  30068. itemCnt = targets.length,
  30069. maxCnt = self.options.toTrashMaxItems,
  30070. checkDirs = [],
  30071. reqDfd = $.Deferred(),
  30072. req, dirs, cnt;
  30073. if (itemCnt > maxCnt) {
  30074. self.confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]);
  30075. return;
  30076. }
  30077. // Directory preparation preparation and directory enumeration
  30078. $.each(targets, function(i, h) {
  30079. var file = fm.file(h),
  30080. path = fm.path(h).replace(/\\/g, '/'),
  30081. m = path.match(/^[^\/]+?(\/(?:[^\/]+?\/)*)[^\/]+?$/);
  30082. if (file) {
  30083. if (m) {
  30084. m[1] = m[1].replace(/(^\/.*?)\/?$/, '$1');
  30085. if (! dsts[m[1]]) {
  30086. dsts[m[1]] = [];
  30087. }
  30088. dsts[m[1]].push(h);
  30089. }
  30090. if (file.mime === 'directory') {
  30091. checkDirs.push(h);
  30092. }
  30093. }
  30094. });
  30095. // Check directory information
  30096. if (checkDirs.length) {
  30097. req = fm.request({
  30098. data : {cmd : 'size', targets : checkDirs},
  30099. notify : {type: 'readdir', cnt: 1, hideCnt: true},
  30100. preventDefault : true
  30101. }).done(function(data) {
  30102. var cnt = 0;
  30103. data.fileCnt && (cnt += parseInt(data.fileCnt));
  30104. data.dirCnt && (cnt += parseInt(data.dirCnt));
  30105. reqDfd[cnt > maxCnt ? 'reject' : 'resolve']();
  30106. }).fail(function() {
  30107. reqDfd.reject();
  30108. });
  30109. setTimeout(function() {
  30110. var xhr = (req && req.xhr)? req.xhr : null;
  30111. if (xhr && xhr.state() == 'pending') {
  30112. req.syncOnFail(false);
  30113. req.reject();
  30114. reqDfd.reject();
  30115. }
  30116. }, self.options.infoCheckWait * 1000);
  30117. } else {
  30118. reqDfd.resolve();
  30119. }
  30120. // Directory creation and paste command execution
  30121. reqDfd.done(function() {
  30122. dirs = Object.keys(dsts);
  30123. cnt = dirs.length;
  30124. if (cnt) {
  30125. fm.request({
  30126. data : {cmd : 'mkdir', target : tHash, dirs : dirs},
  30127. notify : {type : 'chkdir', cnt : cnt},
  30128. preventFail : true
  30129. })
  30130. .fail(function(error) {
  30131. dfrd.reject(error);
  30132. fm.unlockfiles({files : targets});
  30133. })
  30134. .done(function(data) {
  30135. var margeRes = function(data, phash, reqData) {
  30136. var undo, prevUndo, redo, prevRedo;
  30137. $.each(data, function(k, v) {
  30138. if (Array.isArray(v)) {
  30139. if (res[k]) {
  30140. res[k] = res[k].concat(v);
  30141. } else {
  30142. res[k] = v;
  30143. }
  30144. }
  30145. });
  30146. if (data.sync) {
  30147. res.sync = 1;
  30148. }
  30149. if (data.added && data.added.length) {
  30150. undo = function() {
  30151. var targets = [],
  30152. dirs = $.map(data.added, function(f) { return f.mime === 'directory'? f.hash : null; });
  30153. $.each(data.added, function(i, f) {
  30154. if ($.inArray(f.phash, dirs) === -1) {
  30155. targets.push(f.hash);
  30156. }
  30157. });
  30158. return fm.exec('restore', targets, {noToast: true});
  30159. };
  30160. redo = function() {
  30161. return fm.request({
  30162. data : reqData,
  30163. notify : {type : 'redo', cnt : targets.length}
  30164. });
  30165. };
  30166. if (res.undo) {
  30167. prevUndo = res.undo;
  30168. res.undo = function() {
  30169. undo();
  30170. prevUndo();
  30171. };
  30172. } else {
  30173. res.undo = undo;
  30174. }
  30175. if (res.redo) {
  30176. prevRedo = res.redo;
  30177. res.redo = function() {
  30178. redo();
  30179. prevRedo();
  30180. };
  30181. } else {
  30182. res.redo = redo;
  30183. }
  30184. }
  30185. },
  30186. err = ['errTrash'],
  30187. res = {},
  30188. hasNtf = function() {
  30189. return fm.ui.notify.children('.elfinder-notify-trash').length;
  30190. },
  30191. hashes, tm, prg, prgSt;
  30192. if (hashes = data.hashes) {
  30193. prg = 1 / cnt * 100;
  30194. prgSt = cnt === 1? 100 : 5;
  30195. tm = setTimeout(function() {
  30196. fm.notify({type : 'trash', cnt : 1, hideCnt : true, progress : prgSt});
  30197. }, fm.notifyDelay);
  30198. $.each(dsts, function(dir, files) {
  30199. var phash = fm.file(files[0]).phash,
  30200. reqData;
  30201. if (hashes[dir]) {
  30202. reqData = {cmd : 'paste', dst : hashes[dir], targets : files, cut : 1};
  30203. fm.request({
  30204. data : reqData,
  30205. preventDefault : true
  30206. })
  30207. .fail(function(error) {
  30208. if (error) {
  30209. err = err.concat(error);
  30210. }
  30211. })
  30212. .done(function(data) {
  30213. data = fm.normalize(data);
  30214. fm.updateCache(data);
  30215. margeRes(data, phash, reqData);
  30216. if (data.warning) {
  30217. err = err.concat(data.warning);
  30218. delete data.warning;
  30219. }
  30220. // fire some event to update cache/ui
  30221. data.removed && data.removed.length && fm.remove(data);
  30222. data.added && data.added.length && fm.add(data);
  30223. data.changed && data.changed.length && fm.change(data);
  30224. // fire event with command name
  30225. fm.trigger('paste', data);
  30226. // fire event with command name + 'done'
  30227. fm.trigger('pastedone');
  30228. // force update content
  30229. data.sync && fm.sync();
  30230. })
  30231. .always(function() {
  30232. var hashes = [], addTexts, end = 2;
  30233. if (hasNtf()) {
  30234. fm.notify({type : 'trash', cnt : 0, hideCnt : true, progress : prg});
  30235. } else {
  30236. prgSt+= prg;
  30237. }
  30238. if (--cnt < 1) {
  30239. tm && clearTimeout(tm);
  30240. hasNtf() && fm.notify({type : 'trash', cnt : -1});
  30241. fm.unlockfiles({files : targets});
  30242. if (Object.keys(res).length) {
  30243. if (err.length > 1) {
  30244. if (res.removed || res.removed.length) {
  30245. hashes = $.grep(targets, function(h) {
  30246. return $.inArray(h, res.removed) === -1? true : false;
  30247. });
  30248. }
  30249. if (hashes.length) {
  30250. if (err.length > end) {
  30251. end = (fm.messages[err[end-1]] || '').indexOf('$') === -1? end : end + 1;
  30252. }
  30253. dfrd.reject();
  30254. fm.exec('rm', hashes, { addTexts: err.slice(0, end), forceRm: true });
  30255. } else {
  30256. fm.error(err);
  30257. }
  30258. }
  30259. res._noSound = true;
  30260. if (res.undo && res.redo) {
  30261. res.undo = {
  30262. cmd : 'trash',
  30263. callback : res.undo,
  30264. };
  30265. res.redo = {
  30266. cmd : 'trash',
  30267. callback : res.redo
  30268. };
  30269. }
  30270. dfrd.resolve(res);
  30271. } else {
  30272. dfrd.reject(err);
  30273. }
  30274. }
  30275. });
  30276. }
  30277. });
  30278. } else {
  30279. dfrd.reject('errFolderNotFound');
  30280. fm.unlockfiles({files : targets});
  30281. }
  30282. });
  30283. } else {
  30284. dfrd.reject(['error', 'The folder hierarchy to be deleting can not be determined.']);
  30285. fm.unlockfiles({files : targets});
  30286. }
  30287. }).fail(function() {
  30288. self.confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]);
  30289. });
  30290. },
  30291. remove = function(dfrd, targets, quiet) {
  30292. var notify = quiet? {} : {type : 'rm', cnt : targets.length};
  30293. fm.request({
  30294. data : {cmd : 'rm', targets : targets},
  30295. notify : notify,
  30296. preventFail : true
  30297. })
  30298. .fail(function(error) {
  30299. dfrd.reject(error);
  30300. })
  30301. .done(function(data) {
  30302. if (data.error || data.warning) {
  30303. data.sync = true;
  30304. }
  30305. dfrd.resolve(data);
  30306. })
  30307. .always(function() {
  30308. fm.unlockfiles({files : targets});
  30309. });
  30310. },
  30311. getTHash = function(targets) {
  30312. var thash = null,
  30313. root1st;
  30314. if (targets && targets.length) {
  30315. if (targets.length > 1 && fm.searchStatus.state === 2) {
  30316. root1st = fm.file(fm.root(targets[0])).volumeid;
  30317. if (!$.grep(targets, function(h) { return h.indexOf(root1st) !== 0? true : false ; }).length) {
  30318. thash = fm.option('trashHash', targets[0]);
  30319. }
  30320. } else {
  30321. thash = fm.option('trashHash', targets[0]);
  30322. }
  30323. }
  30324. return thash;
  30325. },
  30326. getSize = false;
  30327. // for to be able to overwrite
  30328. this.confirm = confirm;
  30329. this.toTrash = toTrash;
  30330. this.remove = remove;
  30331. this.syncTitleOnChange = true;
  30332. this.updateOnSelect = false;
  30333. this.shortcuts = [{
  30334. pattern : 'delete ctrl+backspace shift+delete'
  30335. }];
  30336. this.value = 'rm';
  30337. this.init = function() {
  30338. // re-assign for extended command
  30339. self = this;
  30340. fm = this.fm;
  30341. // bind function of change
  30342. self.change(function() {
  30343. var targets;
  30344. delete self.extra;
  30345. self.title = fm.i18n('cmd' + self.value);
  30346. self.className = self.value;
  30347. self.button && self.button.children('span.elfinder-button-icon')[self.value === 'trash'? 'addClass' : 'removeClass']('elfinder-button-icon-trash');
  30348. if (self.value === 'trash') {
  30349. self.extra = {
  30350. icon: 'rm',
  30351. node: $('<span/>')
  30352. .attr({title: fm.i18n('cmdrm')})
  30353. .on('ready', function(e, data) {
  30354. targets = data.targets;
  30355. })
  30356. .on('click touchstart', function(e){
  30357. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  30358. return;
  30359. }
  30360. e.stopPropagation();
  30361. e.preventDefault();
  30362. fm.getUI().trigger('click'); // to close the context menu immediately
  30363. fm.exec('rm', targets, {_userAction: true, forceRm : true});
  30364. })
  30365. };
  30366. }
  30367. });
  30368. };
  30369. this.getstate = function(select) {
  30370. var sel = this.hashes(select);
  30371. return sel.length && $.grep(sel, function(h) { var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? true : false; }).length == sel.length
  30372. ? 0 : -1;
  30373. };
  30374. this.exec = function(hashes, cOpts) {
  30375. var opts = cOpts || {},
  30376. dfrd = $.Deferred()
  30377. .always(function() {
  30378. if (getSize && getSize.state && getSize.state() === 'pending') {
  30379. getSize.reject();
  30380. }
  30381. })
  30382. .fail(function(error) {
  30383. error && fm.error(error);
  30384. }).done(function(data) {
  30385. !opts.quiet && !data._noSound && data.removed && data.removed.length && fm.trigger('playsound', {soundFile : 'rm.wav'});
  30386. }),
  30387. files = self.files(hashes),
  30388. cnt = files.length,
  30389. tHash = null,
  30390. addTexts = opts.addTexts? opts.addTexts : null,
  30391. forceRm = opts.forceRm,
  30392. quiet = opts.quiet,
  30393. targets;
  30394. if (! cnt) {
  30395. return dfrd.reject();
  30396. }
  30397. $.each(files, function(i, file) {
  30398. if (fm.isRoot(file)) {
  30399. return !dfrd.reject(['errRm', file.name, 'errPerm']);
  30400. }
  30401. if (file.locked) {
  30402. return !dfrd.reject(['errLocked', file.name]);
  30403. }
  30404. });
  30405. if (dfrd.state() === 'pending') {
  30406. targets = self.hashes(hashes);
  30407. cnt = files.length;
  30408. if (forceRm || (self.event && self.event.originalEvent && self.event.originalEvent.shiftKey)) {
  30409. tHash = '';
  30410. self.title = fm.i18n('cmdrm');
  30411. }
  30412. if (tHash === null) {
  30413. tHash = getTHash(targets);
  30414. }
  30415. fm.lockfiles({files : targets});
  30416. if (tHash && self.options.quickTrash) {
  30417. self.toTrash(dfrd, targets, tHash);
  30418. } else {
  30419. if (quiet) {
  30420. remove(dfrd, targets, quiet);
  30421. } else {
  30422. self.confirm(dfrd, targets, files, tHash, addTexts);
  30423. }
  30424. }
  30425. }
  30426. return dfrd;
  30427. };
  30428. fm.bind('select contextmenucreate closecontextmenu', function(e) {
  30429. var targets = (e.data? (e.data.selected || e.data.targets) : null) || fm.selected();
  30430. if (targets && targets.length) {
  30431. self.update(void(0), (targets? getTHash(targets) : fm.option('trashHash'))? 'trash' : 'rm');
  30432. }
  30433. });
  30434. };
  30435. /*
  30436. * File: /js/commands/search.js
  30437. */
  30438. /**
  30439. * @class elFinder command "search"
  30440. * Find files
  30441. *
  30442. * @author Dmitry (dio) Levashov
  30443. **/
  30444. elFinder.prototype.commands.search = function() {
  30445. "use strict";
  30446. this.title = 'Find files';
  30447. this.options = {ui : 'searchbutton'};
  30448. this.alwaysEnabled = true;
  30449. this.updateOnSelect = false;
  30450. /**
  30451. * Return command status.
  30452. * Search does not support old api.
  30453. *
  30454. * @return Number
  30455. **/
  30456. this.getstate = function() {
  30457. return 0;
  30458. };
  30459. /**
  30460. * Send search request to backend.
  30461. *
  30462. * @param String search string
  30463. * @return $.Deferred
  30464. **/
  30465. this.exec = function(q, target, mime, type) {
  30466. var fm = this.fm,
  30467. reqDef = [],
  30468. sType = type || '',
  30469. onlyMimes = fm.options.onlyMimes,
  30470. phash, targetVolids = [],
  30471. setType = function(data) {
  30472. if (sType && sType !== 'SearchName' && sType !== 'SearchMime') {
  30473. data.type = sType;
  30474. }
  30475. return data;
  30476. };
  30477. if (typeof q == 'string' && q) {
  30478. if (typeof target == 'object') {
  30479. mime = target.mime || '';
  30480. target = target.target || '';
  30481. }
  30482. target = target? target : '';
  30483. if (mime) {
  30484. mime = $.trim(mime).replace(',', ' ').split(' ');
  30485. if (onlyMimes.length) {
  30486. mime = $.map(mime, function(m){
  30487. m = $.trim(m);
  30488. return m && ($.inArray(m, onlyMimes) !== -1
  30489. || $.grep(onlyMimes, function(om) { return m.indexOf(om) === 0? true : false; }).length
  30490. )? m : null;
  30491. });
  30492. }
  30493. } else {
  30494. mime = [].concat(onlyMimes);
  30495. }
  30496. fm.trigger('searchstart', setType({query : q, target : target, mimes : mime}));
  30497. if (! onlyMimes.length || mime.length) {
  30498. if (target === '' && fm.api >= 2.1) {
  30499. $.each(fm.roots, function(id, hash) {
  30500. reqDef.push(fm.request({
  30501. data : setType({cmd : 'search', q : q, target : hash, mimes : mime}),
  30502. notify : {type : 'search', cnt : 1, hideCnt : (reqDef.length? false : true)},
  30503. cancel : true,
  30504. preventDone : true
  30505. }));
  30506. });
  30507. } else {
  30508. reqDef.push(fm.request({
  30509. data : setType({cmd : 'search', q : q, target : target, mimes : mime}),
  30510. notify : {type : 'search', cnt : 1, hideCnt : true},
  30511. cancel : true,
  30512. preventDone : true
  30513. }));
  30514. if (target !== '' && fm.api >= 2.1 && Object.keys(fm.leafRoots).length) {
  30515. $.each(fm.leafRoots, function(hash, roots) {
  30516. phash = hash;
  30517. while(phash) {
  30518. if (target === phash) {
  30519. $.each(roots, function() {
  30520. var f = fm.file(this);
  30521. f && f.volumeid && targetVolids.push(f.volumeid);
  30522. reqDef.push(fm.request({
  30523. data : setType({cmd : 'search', q : q, target : this, mimes : mime}),
  30524. notify : {type : 'search', cnt : 1, hideCnt : false},
  30525. cancel : true,
  30526. preventDone : true
  30527. }));
  30528. });
  30529. }
  30530. phash = (fm.file(phash) || {}).phash;
  30531. }
  30532. });
  30533. }
  30534. }
  30535. } else {
  30536. reqDef = [$.Deferred().resolve({files: []})];
  30537. }
  30538. fm.searchStatus.mixed = (reqDef.length > 1)? targetVolids : false;
  30539. return $.when.apply($, reqDef).done(function(data) {
  30540. var argLen = arguments.length,
  30541. i;
  30542. data.warning && fm.error(data.warning);
  30543. if (argLen > 1) {
  30544. data.files = (data.files || []);
  30545. for(i = 1; i < argLen; i++) {
  30546. arguments[i].warning && fm.error(arguments[i].warning);
  30547. if (arguments[i].files) {
  30548. data.files.push.apply(data.files, arguments[i].files);
  30549. }
  30550. }
  30551. }
  30552. // because "preventDone : true" so update files cache
  30553. data.files && data.files.length && fm.cache(data.files);
  30554. fm.lazy(function() {
  30555. fm.trigger('search', data);
  30556. }).then(function() {
  30557. // fire event with command name + 'done'
  30558. return fm.lazy(function() {
  30559. fm.trigger('searchdone');
  30560. });
  30561. }).then(function() {
  30562. // force update content
  30563. data.sync && fm.sync();
  30564. });
  30565. });
  30566. }
  30567. fm.getUI('toolbar').find('.'+fm.res('class', 'searchbtn')+' :text').trigger('focus');
  30568. return $.Deferred().reject();
  30569. };
  30570. };
  30571. /*
  30572. * File: /js/commands/selectall.js
  30573. */
  30574. /**
  30575. * @class elFinder command "selectall"
  30576. * Select ALL of cwd items
  30577. *
  30578. * @author Naoki Sawada
  30579. **/
  30580. elFinder.prototype.commands.selectall = function() {
  30581. "use strict";
  30582. var self = this,
  30583. state = 0;
  30584. this.fm.bind('select', function(e) {
  30585. state = (e.data && e.data.selectall)? -1 : 0;
  30586. });
  30587. this.state = 0;
  30588. this.updateOnSelect = false;
  30589. this.getstate = function() {
  30590. return state;
  30591. };
  30592. this.exec = function() {
  30593. $(document).trigger($.Event('keydown', { keyCode: 65, ctrlKey : true, shiftKey : false, altKey : false, metaKey : false }));
  30594. return $.Deferred().resolve();
  30595. };
  30596. };
  30597. /*
  30598. * File: /js/commands/selectinvert.js
  30599. */
  30600. /**
  30601. * @class elFinder command "selectinvert"
  30602. * Invert Selection of cwd items
  30603. *
  30604. * @author Naoki Sawada
  30605. **/
  30606. elFinder.prototype.commands.selectinvert = function() {
  30607. "use strict";
  30608. this.updateOnSelect = false;
  30609. this.getstate = function() {
  30610. return 0;
  30611. };
  30612. this.exec = function() {
  30613. $(document).trigger($.Event('keydown', { keyCode: 73, ctrlKey : true, shiftKey : true, altKey : false, metaKey : false }));
  30614. return $.Deferred().resolve();
  30615. };
  30616. };
  30617. /*
  30618. * File: /js/commands/selectnone.js
  30619. */
  30620. /**
  30621. * @class elFinder command "selectnone"
  30622. * Unselect ALL of cwd items
  30623. *
  30624. * @author Naoki Sawada
  30625. **/
  30626. elFinder.prototype.commands.selectnone = function() {
  30627. "use strict";
  30628. var self = this,
  30629. fm = this.fm,
  30630. state = -1;
  30631. fm.bind('select', function(e) {
  30632. state = (e.data && e.data.unselectall)? -1 : 0;
  30633. });
  30634. this.state = -1;
  30635. this.updateOnSelect = false;
  30636. this.getstate = function() {
  30637. return state;
  30638. };
  30639. this.exec = function() {
  30640. fm.getUI('cwd').trigger('unselectall');
  30641. return $.Deferred().resolve();
  30642. };
  30643. };
  30644. /*
  30645. * File: /js/commands/sort.js
  30646. */
  30647. /**
  30648. * @class elFinder command "sort"
  30649. * Change sort files rule
  30650. *
  30651. * @author Dmitry (dio) Levashov
  30652. **/
  30653. elFinder.prototype.commands.sort = function() {
  30654. "use strict";
  30655. var self = this,
  30656. fm = self.fm,
  30657. setVar = function() {
  30658. self.variants = [];
  30659. $.each(fm.sortRules, function(name, value) {
  30660. if (fm.sorters[name]) {
  30661. var arr = (name === fm.sortType)? (fm.sortOrder === 'asc'? 'n' : 's') : '';
  30662. self.variants.push([name, (arr? '<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>' : '') + '&nbsp;' + fm.i18n('sort'+name)]);
  30663. }
  30664. });
  30665. self.variants.push('|');
  30666. self.variants.push([
  30667. 'stick',
  30668. (fm.sortStickFolders? '<span class="ui-icon ui-icon-check"/>' : '') + '&nbsp;' + fm.i18n('sortFoldersFirst')
  30669. ]);
  30670. if (fm.ui.tree) {
  30671. self.variants.push('|');
  30672. self.variants.push([
  30673. 'tree',
  30674. (fm.sortAlsoTreeview? '<span class="ui-icon ui-icon-check"/>' : '') + '&nbsp;' + fm.i18n('sortAlsoTreeview')
  30675. ]);
  30676. }
  30677. updateContextmenu();
  30678. },
  30679. updateContextmenu = function() {
  30680. var cm = fm.getUI('contextmenu'),
  30681. icon, sub;
  30682. if (cm.is(':visible')) {
  30683. icon = cm.find('span.elfinder-button-icon-sort');
  30684. sub = icon.siblings('div.elfinder-contextmenu-sub');
  30685. sub.find('span.ui-icon').remove();
  30686. sub.children('div.elfinder-contextsubmenu-item').each(function() {
  30687. var tgt = $(this).children('span'),
  30688. name = tgt.text().trim(),
  30689. arr;
  30690. if (name === (i18Name.stick || (i18Name.stick = fm.i18n('sortFoldersFirst')))) {
  30691. if (fm.sortStickFolders) {
  30692. tgt.prepend('<span class="ui-icon ui-icon-check"/>');
  30693. }
  30694. } else if (name === (i18Name.tree || (i18Name.tree = fm.i18n('sortAlsoTreeview')))) {
  30695. if (fm.sortAlsoTreeview) {
  30696. tgt.prepend('<span class="ui-icon ui-icon-check"/>');
  30697. }
  30698. } else if (name === (i18Name[fm.sortType] || (i18Name[fm.sortType] = fm.i18n('sort' + fm.sortType)))) {
  30699. arr = fm.sortOrder === 'asc'? 'n' : 's';
  30700. tgt.prepend('<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>');
  30701. }
  30702. });
  30703. }
  30704. },
  30705. i18Name = {};
  30706. /**
  30707. * Command options
  30708. *
  30709. * @type Object
  30710. */
  30711. this.options = {ui : 'sortbutton'};
  30712. this.keepContextmenu = true;
  30713. fm.bind('sortchange', setVar)
  30714. .bind('sorterupdate', function() {
  30715. setVar();
  30716. fm.getUI('toolbar').find('.elfiner-button-sort .elfinder-button-menu .elfinder-button-menu-item').each(function() {
  30717. var tgt = $(this),
  30718. rel = tgt.attr('rel');
  30719. tgt.toggle(! rel || fm.sorters[rel]);
  30720. });
  30721. })
  30722. .bind('cwdrender', function() {
  30723. var cols = $(fm.cwd).find('div.elfinder-cwd-wrapper-list table');
  30724. if (cols.length) {
  30725. $.each(fm.sortRules, function(name, value) {
  30726. var td = cols.find('thead tr td.elfinder-cwd-view-th-'+name);
  30727. if (td.length) {
  30728. var current = ( name == fm.sortType),
  30729. sort = {
  30730. type : name,
  30731. order : current ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder
  30732. },arr;
  30733. if (current) {
  30734. td.addClass('ui-state-active');
  30735. arr = fm.sortOrder == 'asc' ? 'n' : 's';
  30736. $('<span class="ui-icon ui-icon-triangle-1-'+arr+'"/>').appendTo(td);
  30737. }
  30738. $(td).on('click', function(e){
  30739. if (! $(this).data('dragging')) {
  30740. e.stopPropagation();
  30741. if (! fm.getUI('cwd').data('longtap')) {
  30742. fm.exec('sort', [], sort);
  30743. }
  30744. }
  30745. })
  30746. .on('mouseenter mouseleave', function(e) {
  30747. $(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
  30748. });
  30749. }
  30750. });
  30751. }
  30752. });
  30753. this.getstate = function() {
  30754. return 0;
  30755. };
  30756. this.exec = function(hashes, cOpt) {
  30757. var fm = this.fm,
  30758. sortopt = $.isPlainObject(cOpt)? cOpt : (function() {
  30759. cOpt += '';
  30760. var sOpts = {};
  30761. if (cOpt === 'stick') {
  30762. sOpts.stick = !fm.sortStickFolders;
  30763. } else if (cOpt === 'tree') {
  30764. sOpts.tree = !fm.sortAlsoTreeview;
  30765. } else if (fm.sorters[cOpt]) {
  30766. if (fm.sortType === cOpt) {
  30767. sOpts.order = fm.sortOrder === 'asc'? 'desc' : 'asc';
  30768. } else {
  30769. sOpts.type = cOpt;
  30770. }
  30771. }
  30772. return sOpts;
  30773. })(),
  30774. sort = Object.assign({
  30775. type : fm.sortType,
  30776. order : fm.sortOrder,
  30777. stick : fm.sortStickFolders,
  30778. tree : fm.sortAlsoTreeview
  30779. }, sortopt);
  30780. return fm.lazy(function() {
  30781. fm.setSort(sort.type, sort.order, sort.stick, sort.tree);
  30782. this.resolve();
  30783. });
  30784. };
  30785. };
  30786. /*
  30787. * File: /js/commands/undo.js
  30788. */
  30789. /**
  30790. * @class elFinder command "undo"
  30791. * Undo previous commands
  30792. *
  30793. * @author Naoki Sawada
  30794. **/
  30795. elFinder.prototype.commands.undo = function() {
  30796. "use strict";
  30797. var self = this,
  30798. fm = this.fm,
  30799. setTitle = function(undo) {
  30800. if (undo) {
  30801. self.title = fm.i18n('cmdundo') + ' ' + fm.i18n('cmd'+undo.cmd);
  30802. self.state = 0;
  30803. } else {
  30804. self.title = fm.i18n('cmdundo');
  30805. self.state = -1;
  30806. }
  30807. self.change();
  30808. },
  30809. cmds = [];
  30810. this.alwaysEnabled = true;
  30811. this.updateOnSelect = false;
  30812. this.shortcuts = [{
  30813. pattern : 'ctrl+z'
  30814. }];
  30815. this.syncTitleOnChange = true;
  30816. this.getstate = function() {
  30817. return cmds.length? 0 : -1;
  30818. };
  30819. this.setUndo = function(undo, redo) {
  30820. var _undo = {};
  30821. if (undo) {
  30822. if ($.isPlainObject(undo) && undo.cmd && undo.callback) {
  30823. Object.assign(_undo, undo);
  30824. if (redo) {
  30825. delete redo.undo;
  30826. _undo.redo = redo;
  30827. } else {
  30828. fm.getCommand('redo').setRedo(null);
  30829. }
  30830. cmds.push(_undo);
  30831. setTitle(_undo);
  30832. }
  30833. }
  30834. };
  30835. this.exec = function() {
  30836. var redo = fm.getCommand('redo'),
  30837. dfd = $.Deferred(),
  30838. undo, res, _redo = {};
  30839. if (cmds.length) {
  30840. undo = cmds.pop();
  30841. if (undo.redo) {
  30842. Object.assign(_redo, undo.redo);
  30843. delete undo.redo;
  30844. } else {
  30845. _redo = null;
  30846. }
  30847. dfd.done(function() {
  30848. if (_redo) {
  30849. redo.setRedo(_redo, undo);
  30850. }
  30851. });
  30852. setTitle(cmds.length? cmds[cmds.length-1] : void(0));
  30853. res = undo.callback();
  30854. if (res && res.done) {
  30855. res.done(function() {
  30856. dfd.resolve();
  30857. }).fail(function() {
  30858. dfd.reject();
  30859. });
  30860. } else {
  30861. dfd.resolve();
  30862. }
  30863. if (cmds.length) {
  30864. this.update(0, cmds[cmds.length - 1].name);
  30865. } else {
  30866. this.update(-1, '');
  30867. }
  30868. } else {
  30869. dfd.reject();
  30870. }
  30871. return dfd;
  30872. };
  30873. fm.bind('exec', function(e) {
  30874. var data = e.data || {};
  30875. if (data.opts && data.opts._userAction) {
  30876. if (data.dfrd && data.dfrd.done) {
  30877. data.dfrd.done(function(res) {
  30878. if (res && res.undo && res.redo) {
  30879. res.undo.redo = res.redo;
  30880. self.setUndo(res.undo);
  30881. }
  30882. });
  30883. }
  30884. }
  30885. });
  30886. };
  30887. /**
  30888. * @class elFinder command "redo"
  30889. * Redo previous commands
  30890. *
  30891. * @author Naoki Sawada
  30892. **/
  30893. elFinder.prototype.commands.redo = function() {
  30894. "use strict";
  30895. var self = this,
  30896. fm = this.fm,
  30897. setTitle = function(redo) {
  30898. if (redo && redo.callback) {
  30899. self.title = fm.i18n('cmdredo') + ' ' + fm.i18n('cmd'+redo.cmd);
  30900. self.state = 0;
  30901. } else {
  30902. self.title = fm.i18n('cmdredo');
  30903. self.state = -1;
  30904. }
  30905. self.change();
  30906. },
  30907. cmds = [];
  30908. this.alwaysEnabled = true;
  30909. this.updateOnSelect = false;
  30910. this.shortcuts = [{
  30911. pattern : 'shift+ctrl+z ctrl+y'
  30912. }];
  30913. this.syncTitleOnChange = true;
  30914. this.getstate = function() {
  30915. return cmds.length? 0 : -1;
  30916. };
  30917. this.setRedo = function(redo, undo) {
  30918. if (redo === null) {
  30919. cmds = [];
  30920. setTitle();
  30921. } else {
  30922. if (redo && redo.cmd && redo.callback) {
  30923. if (undo) {
  30924. redo.undo = undo;
  30925. }
  30926. cmds.push(redo);
  30927. setTitle(redo);
  30928. }
  30929. }
  30930. };
  30931. this.exec = function() {
  30932. var undo = fm.getCommand('undo'),
  30933. dfd = $.Deferred(),
  30934. redo, res, _undo = {}, _redo = {};
  30935. if (cmds.length) {
  30936. redo = cmds.pop();
  30937. if (redo.undo) {
  30938. Object.assign(_undo, redo.undo);
  30939. Object.assign(_redo, redo);
  30940. delete _redo.undo;
  30941. dfd.done(function() {
  30942. undo.setUndo(_undo, _redo);
  30943. });
  30944. }
  30945. setTitle(cmds.length? cmds[cmds.length-1] : void(0));
  30946. res = redo.callback();
  30947. if (res && res.done) {
  30948. res.done(function() {
  30949. dfd.resolve();
  30950. }).fail(function() {
  30951. dfd.reject();
  30952. });
  30953. } else {
  30954. dfd.resolve();
  30955. }
  30956. return dfd;
  30957. } else {
  30958. return dfd.reject();
  30959. }
  30960. };
  30961. };
  30962. /*
  30963. * File: /js/commands/up.js
  30964. */
  30965. /**
  30966. * @class elFinder command "up"
  30967. * Go into parent directory
  30968. *
  30969. * @author Dmitry (dio) Levashov
  30970. **/
  30971. (elFinder.prototype.commands.up = function() {
  30972. "use strict";
  30973. this.alwaysEnabled = true;
  30974. this.updateOnSelect = false;
  30975. this.shortcuts = [{
  30976. pattern : 'ctrl+up'
  30977. }];
  30978. this.getstate = function() {
  30979. return this.fm.cwd().phash ? 0 : -1;
  30980. };
  30981. this.exec = function() {
  30982. var fm = this.fm,
  30983. cwdhash = fm.cwd().hash;
  30984. return this.fm.cwd().phash ? this.fm.exec('open', this.fm.cwd().phash).done(function() {
  30985. fm.one('opendone', function() {
  30986. fm.selectfiles({files : [cwdhash]});
  30987. });
  30988. }) : $.Deferred().reject();
  30989. };
  30990. }).prototype = { forceLoad : true }; // this is required command
  30991. /*
  30992. * File: /js/commands/upload.js
  30993. */
  30994. /**
  30995. * @class elFinder command "upload"
  30996. * Upload files using iframe or XMLHttpRequest & FormData.
  30997. * Dialog allow to send files using drag and drop
  30998. *
  30999. * @type elFinder.command
  31000. * @author Dmitry (dio) Levashov
  31001. */
  31002. elFinder.prototype.commands.upload = function() {
  31003. "use strict";
  31004. var hover = this.fm.res('class', 'hover');
  31005. this.disableOnSearch = true;
  31006. this.updateOnSelect = false;
  31007. // Shortcut opens dialog
  31008. this.shortcuts = [{
  31009. pattern : 'ctrl+u'
  31010. }];
  31011. /**
  31012. * Return command state
  31013. *
  31014. * @return Number
  31015. **/
  31016. this.getstate = function(select) {
  31017. var fm = this.fm, f,
  31018. sel = (select || [fm.cwd().hash]);
  31019. if (!this._disabled && sel.length == 1) {
  31020. f = fm.file(sel[0]);
  31021. }
  31022. return (f && f.mime == 'directory' && f.write)? 0 : -1;
  31023. };
  31024. this.exec = function(data) {
  31025. var fm = this.fm,
  31026. cwdHash = fm.cwd().hash,
  31027. getTargets = function() {
  31028. var tgts = data && (data instanceof Array)? data : null,
  31029. sel;
  31030. if (! data || data instanceof Array) {
  31031. if (! tgts && (sel = fm.selected()).length === 1 && fm.file(sel[0]).mime === 'directory') {
  31032. tgts = sel;
  31033. } else if (!tgts || tgts.length !== 1 || fm.file(tgts[0]).mime !== 'directory') {
  31034. tgts = [ cwdHash ];
  31035. }
  31036. }
  31037. return tgts;
  31038. },
  31039. targets = getTargets(),
  31040. check = targets? targets[0] : (data && data.target? data.target : null),
  31041. targetDir = check? fm.file(check) : fm.cwd(),
  31042. fmUpload = function(data) {
  31043. fm.upload(data)
  31044. .fail(function(error) {
  31045. dfrd.reject(error);
  31046. })
  31047. .done(function(data) {
  31048. var cwd = fm.getUI('cwd'),
  31049. node;
  31050. dfrd.resolve(data);
  31051. if (data && data.added && data.added[0] && ! fm.ui.notify.children('.elfinder-notify-upload').length) {
  31052. var newItem = fm.findCwdNodes(data.added);
  31053. if (newItem.length) {
  31054. newItem.trigger('scrolltoview');
  31055. } else {
  31056. if (targetDir.hash !== cwdHash) {
  31057. node = $('<div/>').append(
  31058. $('<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>')
  31059. .on('mouseenter mouseleave', function(e) {
  31060. $(this).toggleClass('ui-state-hover', e.type == 'mouseenter');
  31061. }).on('click', function() {
  31062. fm.exec('open', check).done(function() {
  31063. fm.one('opendone', function() {
  31064. fm.trigger('selectfiles', {files : $.map(data.added, function(f) {return f.hash;})});
  31065. });
  31066. });
  31067. })
  31068. );
  31069. } else {
  31070. fm.trigger('selectfiles', {files : $.map(data.added, function(f) {return f.hash;})});
  31071. }
  31072. fm.toast({msg: fm.i18n(['complete', fm.i18n('cmdupload')]), extNode: node});
  31073. }
  31074. }
  31075. })
  31076. .progress(function() {
  31077. dfrd.notifyWith(this, Array.from(arguments));
  31078. });
  31079. },
  31080. upload = function(data) {
  31081. dialog.elfinderdialog('close');
  31082. if (targets) {
  31083. data.target = targets[0];
  31084. }
  31085. fmUpload(data);
  31086. },
  31087. getSelector = function() {
  31088. var hash = targetDir.hash,
  31089. dirs = $.map(fm.files(hash), function(f) {
  31090. return (f.mime === 'directory' && f.write)? f : null;
  31091. });
  31092. if (! dirs.length) {
  31093. return $();
  31094. }
  31095. return $('<div class="elfinder-upload-dirselect elfinder-tabstop" title="' + fm.i18n('folders') + '"/>')
  31096. .on('click', function(e) {
  31097. e.stopPropagation();
  31098. e.preventDefault();
  31099. dirs = fm.sortFiles(dirs);
  31100. var $this = $(this),
  31101. cwd = fm.cwd(),
  31102. base = dialog.closest('div.ui-dialog'),
  31103. getRaw = function(f, icon) {
  31104. return {
  31105. label : fm.escape(f.i18 || f.name),
  31106. icon : icon,
  31107. remain : false,
  31108. callback : function() {
  31109. var title = base.children('.ui-dialog-titlebar:first').find('span.elfinder-upload-target');
  31110. targets = [ f.hash ];
  31111. title.html(' - ' + fm.escape(f.i18 || f.name));
  31112. $this.trigger('focus');
  31113. },
  31114. options : {
  31115. className : (targets && targets.length && f.hash === targets[0])? 'ui-state-active' : '',
  31116. iconClass : f.csscls || '',
  31117. iconImg : f.icon || ''
  31118. }
  31119. };
  31120. },
  31121. raw = [ getRaw(targetDir, 'opendir'), '|' ];
  31122. $.each(dirs, function(i, f) {
  31123. raw.push(getRaw(f, 'dir'));
  31124. });
  31125. $this.trigger('blur');
  31126. fm.trigger('contextmenu', {
  31127. raw: raw,
  31128. x: e.pageX || $(this).offset().left,
  31129. y: e.pageY || $(this).offset().top,
  31130. prevNode: base,
  31131. fitHeight: true
  31132. });
  31133. }).append('<span class="elfinder-button-icon elfinder-button-icon-dir" />');
  31134. },
  31135. inputButton = function(type, caption) {
  31136. var button,
  31137. input = $('<input type="file" ' + type + '/>')
  31138. .on('click', function() {
  31139. // for IE's bug
  31140. if (fm.UA.IE) {
  31141. setTimeout(function() {
  31142. form.css('display', 'none').css('position', 'relative');
  31143. requestAnimationFrame(function() {
  31144. form.css('display', '').css('position', '');
  31145. });
  31146. }, 100);
  31147. }
  31148. })
  31149. .on('change', function() {
  31150. upload({input : input.get(0), type : 'files'});
  31151. })
  31152. .on('dragover', function(e) {
  31153. e.originalEvent.dataTransfer.dropEffect = 'copy';
  31154. }),
  31155. form = $('<form/>').append(input);
  31156. 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>')
  31157. .append(form)
  31158. .on('click', function(e) {
  31159. if (e.target === this) {
  31160. e.stopPropagation();
  31161. e.preventDefault();
  31162. input.trigger('click');
  31163. }
  31164. })
  31165. .on('mouseenter mouseleave', function(e) {
  31166. $(this).toggleClass(hover, e.type === 'mouseenter');
  31167. });
  31168. },
  31169. dfrd = $.Deferred(),
  31170. dialog, dropbox, pastebox, dropUpload, paste, dirs, spinner, uidialog;
  31171. dropUpload = function(e) {
  31172. e.stopPropagation();
  31173. e.preventDefault();
  31174. var file = false,
  31175. type = '',
  31176. elfFrom = null,
  31177. mycwd = '',
  31178. data = null,
  31179. target = e._target || null,
  31180. trf = e.dataTransfer || null,
  31181. kind = (trf.items && trf.items.length && trf.items[0].kind)? trf.items[0].kind : '',
  31182. errors;
  31183. if (trf) {
  31184. try {
  31185. elfFrom = trf.getData('elfinderfrom');
  31186. if (elfFrom) {
  31187. mycwd = window.location.href + fm.cwd().hash;
  31188. if ((!target && elfFrom === mycwd) || target === mycwd) {
  31189. dfrd.reject();
  31190. return;
  31191. }
  31192. }
  31193. } catch(e) {}
  31194. if (kind === 'file' && (trf.items[0].getAsEntry || trf.items[0].webkitGetAsEntry)) {
  31195. file = trf;
  31196. type = 'data';
  31197. } else if (kind !== 'string' && trf.files && trf.files.length && $.inArray('Text', trf.types) === -1) {
  31198. file = trf.files;
  31199. type = 'files';
  31200. } else {
  31201. try {
  31202. if ((data = trf.getData('text/html')) && data.match(/<(?:img|a)/i)) {
  31203. file = [ data ];
  31204. type = 'html';
  31205. }
  31206. } catch(e) {}
  31207. if (! file) {
  31208. if (data = trf.getData('text')) {
  31209. file = [ data ];
  31210. type = 'text';
  31211. } else if (trf && trf.files) {
  31212. // maybe folder uploading but this UA dose not support it
  31213. kind = 'file';
  31214. }
  31215. }
  31216. }
  31217. }
  31218. if (file) {
  31219. fmUpload({files : file, type : type, target : target, dropEvt : e});
  31220. } else {
  31221. errors = ['errUploadNoFiles'];
  31222. if (kind === 'file') {
  31223. errors.push('errFolderUpload');
  31224. }
  31225. fm.error(errors);
  31226. dfrd.reject();
  31227. }
  31228. };
  31229. if (!targets && data) {
  31230. if (data.input || data.files) {
  31231. data.type = 'files';
  31232. fmUpload(data);
  31233. } else if (data.dropEvt) {
  31234. dropUpload(data.dropEvt);
  31235. }
  31236. return dfrd;
  31237. }
  31238. paste = function(ev) {
  31239. var e = ev.originalEvent || ev;
  31240. var files = [], items = [];
  31241. var file;
  31242. if (e.clipboardData) {
  31243. if (e.clipboardData.items && e.clipboardData.items.length){
  31244. items = e.clipboardData.items;
  31245. for (var i=0; i < items.length; i++) {
  31246. if (e.clipboardData.items[i].kind == 'file') {
  31247. file = e.clipboardData.items[i].getAsFile();
  31248. files.push(file);
  31249. }
  31250. }
  31251. } else if (e.clipboardData.files && e.clipboardData.files.length) {
  31252. files = e.clipboardData.files;
  31253. }
  31254. if (files.length) {
  31255. upload({files : files, type : 'files', clipdata : true});
  31256. return;
  31257. }
  31258. }
  31259. var my = e.target || e.srcElement;
  31260. requestAnimationFrame(function() {
  31261. var type = 'text',
  31262. src;
  31263. if (my.innerHTML) {
  31264. $(my).find('img').each(function(i, v){
  31265. if (v.src.match(/^webkit-fake-url:\/\//)) {
  31266. // For Safari's bug.
  31267. // ref. https://bugs.webkit.org/show_bug.cgi?id=49141
  31268. // https://dev.ckeditor.com/ticket/13029
  31269. $(v).remove();
  31270. }
  31271. });
  31272. if ($(my).find('a,img').length) {
  31273. type = 'html';
  31274. }
  31275. src = my.innerHTML;
  31276. my.innerHTML = '';
  31277. upload({files : [ src ], type : type});
  31278. }
  31279. });
  31280. };
  31281. dialog = $('<div class="elfinder-upload-dialog-wrapper"/>')
  31282. .append(inputButton('multiple', 'selectForUpload'));
  31283. if (! fm.UA.Mobile && (function(input) {
  31284. return (typeof input.webkitdirectory !== 'undefined' || typeof input.directory !== 'undefined');})(document.createElement('input'))) {
  31285. dialog.append(inputButton('multiple webkitdirectory directory', 'selectFolder'));
  31286. }
  31287. if (targetDir.dirs) {
  31288. if (targetDir.hash === cwdHash || $('#'+fm.navHash2Id(targetDir.hash)).hasClass('elfinder-subtree-loaded')) {
  31289. getSelector().appendTo(dialog);
  31290. } else {
  31291. spinner = $('<div class="elfinder-upload-dirselect" title="' + fm.i18n('nowLoading') + '"/>')
  31292. .append('<span class="elfinder-button-icon elfinder-button-icon-spinner" />')
  31293. .appendTo(dialog);
  31294. fm.request({cmd : 'tree', target : targetDir.hash})
  31295. .done(function() {
  31296. fm.one('treedone', function() {
  31297. spinner.replaceWith(getSelector());
  31298. uidialog.elfinderdialog('tabstopsInit');
  31299. });
  31300. })
  31301. .fail(function() {
  31302. spinner.remove();
  31303. });
  31304. }
  31305. }
  31306. if (fm.dragUpload) {
  31307. dropbox = $('<div class="ui-corner-all elfinder-upload-dropbox elfinder-tabstop" contenteditable="true" data-ph="'+fm.i18n('dropPasteFiles')+'"></div>')
  31308. .on('paste', function(e){
  31309. paste(e);
  31310. })
  31311. .on('mousedown click', function(){
  31312. $(this).trigger('focus');
  31313. })
  31314. .on('focus', function(){
  31315. this.innerHTML = '';
  31316. })
  31317. .on('mouseover', function(){
  31318. $(this).addClass(hover);
  31319. })
  31320. .on('mouseout', function(){
  31321. $(this).removeClass(hover);
  31322. })
  31323. .on('dragenter', function(e) {
  31324. e.stopPropagation();
  31325. e.preventDefault();
  31326. $(this).addClass(hover);
  31327. })
  31328. .on('dragleave', function(e) {
  31329. e.stopPropagation();
  31330. e.preventDefault();
  31331. $(this).removeClass(hover);
  31332. })
  31333. .on('dragover', function(e) {
  31334. e.stopPropagation();
  31335. e.preventDefault();
  31336. e.originalEvent.dataTransfer.dropEffect = 'copy';
  31337. $(this).addClass(hover);
  31338. })
  31339. .on('drop', function(e) {
  31340. dialog.elfinderdialog('close');
  31341. targets && (e.originalEvent._target = targets[0]);
  31342. dropUpload(e.originalEvent);
  31343. })
  31344. .prependTo(dialog)
  31345. .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0];
  31346. } else {
  31347. pastebox = $('<div class="ui-corner-all elfinder-upload-dropbox" contenteditable="true">'+fm.i18n('dropFilesBrowser')+'</div>')
  31348. .on('paste drop', function(e){
  31349. paste(e);
  31350. })
  31351. .on('mousedown click', function(){
  31352. $(this).trigger('focus');
  31353. })
  31354. .on('focus', function(){
  31355. this.innerHTML = '';
  31356. })
  31357. .on('dragenter mouseover', function(){
  31358. $(this).addClass(hover);
  31359. })
  31360. .on('dragleave mouseout', function(){
  31361. $(this).removeClass(hover);
  31362. })
  31363. .prependTo(dialog)
  31364. .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0];
  31365. }
  31366. uidialog = this.fmDialog(dialog, {
  31367. title : this.title + '<span class="elfinder-upload-target">' + (targetDir? ' - ' + fm.escape(targetDir.i18 || targetDir.name) : '') + '</span>',
  31368. modal : true,
  31369. resizable : false,
  31370. destroyOnClose : true,
  31371. propagationEvents : ['mousemove', 'mouseup', 'click'],
  31372. close : function() {
  31373. var cm = fm.getUI('contextmenu');
  31374. if (cm.is(':visible')) {
  31375. cm.click();
  31376. }
  31377. }
  31378. });
  31379. return dfrd;
  31380. };
  31381. };
  31382. /*
  31383. * File: /js/commands/view.js
  31384. */
  31385. /**
  31386. * @class elFinder command "view"
  31387. * Change current directory view (icons/list)
  31388. *
  31389. * @author Dmitry (dio) Levashov
  31390. **/
  31391. elFinder.prototype.commands.view = function() {
  31392. "use strict";
  31393. var self = this,
  31394. fm = this.fm,
  31395. subMenuRaw;
  31396. this.value = fm.viewType;
  31397. this.alwaysEnabled = true;
  31398. this.updateOnSelect = false;
  31399. this.options = { ui : 'viewbutton'};
  31400. this.getstate = function() {
  31401. return 0;
  31402. };
  31403. this.extra = {
  31404. icon: 'menu',
  31405. node: $('<span/>')
  31406. .attr({title: fm.i18n('viewtype')})
  31407. .on('click touchstart', function(e){
  31408. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  31409. return;
  31410. }
  31411. var node = $(this);
  31412. e.stopPropagation();
  31413. e.preventDefault();
  31414. fm.trigger('contextmenu', {
  31415. raw: getSubMenuRaw(),
  31416. x: node.offset().left,
  31417. y: node.offset().top
  31418. });
  31419. })
  31420. };
  31421. this.exec = function() {
  31422. var self = this,
  31423. value = fm.storage('view', this.value == 'list' ? 'icons' : 'list');
  31424. return fm.lazy(function() {
  31425. fm.viewchange();
  31426. self.update(void(0), value);
  31427. this.resolve();
  31428. });
  31429. };
  31430. fm.bind('init', function() {
  31431. subMenuRaw = (function() {
  31432. var cwd = fm.getUI('cwd'),
  31433. raws = [],
  31434. sizeNames = fm.options.uiOptions.cwd.iconsView.sizeNames,
  31435. max = fm.options.uiOptions.cwd.iconsView.sizeMax,
  31436. i, size;
  31437. for (i = 0; i <= max; i++) {
  31438. raws.push(
  31439. {
  31440. label : fm.i18n(sizeNames[i] || ('Size-' + i + ' icons')),
  31441. icon : 'view',
  31442. callback : (function(s) {
  31443. return function() {
  31444. cwd.trigger('iconpref', {size: s});
  31445. fm.storage('iconsize', s);
  31446. if (self.value === 'list') {
  31447. self.exec();
  31448. }
  31449. };
  31450. })(i)
  31451. }
  31452. );
  31453. }
  31454. raws.push('|');
  31455. raws.push(
  31456. {
  31457. label : fm.i18n('viewlist'),
  31458. icon : 'view-list',
  31459. callback : function() {
  31460. if (self.value !== 'list') {
  31461. self.exec();
  31462. }
  31463. }
  31464. }
  31465. );
  31466. return raws;
  31467. })();
  31468. }).bind('contextmenucreate', function() {
  31469. self.extra = {
  31470. icon: 'menu',
  31471. node: $('<span/>')
  31472. .attr({title: fm.i18n('cmdview')})
  31473. .on('click touchstart', function(e){
  31474. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  31475. return;
  31476. }
  31477. var node = $(this),
  31478. raw = subMenuRaw.concat(),
  31479. idx, i;
  31480. if (self.value === 'list') {
  31481. idx = subMenuRaw.length - 1;
  31482. } else {
  31483. idx = parseInt(fm.storage('iconsize') || 0);
  31484. }
  31485. for (i = 0; i < subMenuRaw.length; i++) {
  31486. if (subMenuRaw[i] !== '|') {
  31487. subMenuRaw[i].options = (i === idx? {'className': 'ui-state-active'} : void(0))
  31488. ;
  31489. }
  31490. }
  31491. e.stopPropagation();
  31492. e.preventDefault();
  31493. fm.trigger('contextmenu', {
  31494. raw: subMenuRaw,
  31495. x: node.offset().left,
  31496. y: node.offset().top
  31497. });
  31498. })
  31499. };
  31500. });
  31501. };
  31502. return elFinder;
  31503. }));