elfinder.full.js 879 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028
  1. /*!
  2. * elFinder - file manager for web
  3. * Version 2.1.37 (2018-03-28)
  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'],
  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. * Is shortcuts/commands enabled
  101. *
  102. * @type Boolean
  103. **/
  104. enabled = false,
  105. /**
  106. * Store enabled value before ajax request
  107. *
  108. * @type Boolean
  109. **/
  110. prevEnabled = false,
  111. /**
  112. * List of build-in events which mapped into methods with same names
  113. *
  114. * @type Array
  115. **/
  116. events = ['enable', 'disable', 'load', 'open', 'reload', 'select', 'add', 'remove', 'change', 'dblclick', 'getfile', 'lockfiles', 'unlockfiles', 'selectfiles', 'unselectfiles', 'dragstart', 'dragstop', 'search', 'searchend', 'viewchange'],
  117. /**
  118. * Rules to validate data from backend
  119. *
  120. * @type Object
  121. **/
  122. rules = {},
  123. /**
  124. * Current working directory hash
  125. *
  126. * @type String
  127. **/
  128. cwd = '',
  129. /**
  130. * Current working directory options default
  131. *
  132. * @type Object
  133. **/
  134. cwdOptionsDefault = {
  135. path : '',
  136. url : '',
  137. tmbUrl : '',
  138. disabled : [],
  139. separator : '/',
  140. archives : [],
  141. extract : [],
  142. copyOverwrite : true,
  143. uploadOverwrite : true,
  144. uploadMaxSize : 0,
  145. jpgQuality : 100,
  146. tmbCrop : false,
  147. tmb : false // old API
  148. },
  149. /**
  150. * Current working directory options
  151. *
  152. * @type Object
  153. **/
  154. cwdOptions = {},
  155. /**
  156. * Files/dirs cache
  157. *
  158. * @type Object
  159. **/
  160. files = {},
  161. /**
  162. * Files/dirs hash cache of each dirs
  163. *
  164. * @type Object
  165. **/
  166. ownFiles = {},
  167. /**
  168. * Selected files hashes
  169. *
  170. * @type Array
  171. **/
  172. selected = [],
  173. /**
  174. * Events listeners
  175. *
  176. * @type Object
  177. **/
  178. listeners = {},
  179. /**
  180. * Shortcuts
  181. *
  182. * @type Object
  183. **/
  184. shortcuts = {},
  185. /**
  186. * Buffer for copied files
  187. *
  188. * @type Array
  189. **/
  190. clipboard = [],
  191. /**
  192. * Copied/cuted files hashes
  193. * Prevent from remove its from cache.
  194. * Required for dispaly correct files names in error messages
  195. *
  196. * @type Object
  197. **/
  198. remember = {},
  199. /**
  200. * Queue for 'open' requests
  201. *
  202. * @type Array
  203. **/
  204. queue = [],
  205. /**
  206. * Queue for only cwd requests e.g. `tmb`
  207. *
  208. * @type Array
  209. **/
  210. cwdQueue = [],
  211. /**
  212. * Commands prototype
  213. *
  214. * @type Object
  215. **/
  216. base = new self.command(self),
  217. /**
  218. * elFinder node width
  219. *
  220. * @type String
  221. * @default "auto"
  222. **/
  223. width = 'auto',
  224. /**
  225. * elFinder node height
  226. * Number: pixcel or String: Number + "%"
  227. *
  228. * @type Number | String
  229. * @default 400
  230. **/
  231. height = 400,
  232. /**
  233. * Base node object or selector
  234. * Element which is the reference of the height percentage
  235. *
  236. * @type Object|String
  237. * @default null | $(window) (if height is percentage)
  238. **/
  239. heightBase = null,
  240. /**
  241. * MIME type list(Associative array) handled as a text file
  242. *
  243. * @type Object|null
  244. */
  245. textMimes = null,
  246. /**
  247. * elfinder path for sound played on remove
  248. * @type String
  249. * @default ./sounds/
  250. **/
  251. soundPath = './sounds/',
  252. beeper = $(document.createElement('audio')).hide().appendTo('body')[0],
  253. syncInterval,
  254. autoSyncStop = 0,
  255. uiCmdMapPrev = '',
  256. gcJobRes = null,
  257. open = function(data) {
  258. // NOTES: Do not touch data object
  259. var volumeid, contextmenu, emptyDirs = {}, stayDirs = {},
  260. rmClass, hashes, calc, gc, collapsed, prevcwd;
  261. if (self.api >= 2.1) {
  262. // support volume driver option `uiCmdMap`
  263. self.commandMap = (data.options.uiCmdMap && Object.keys(data.options.uiCmdMap).length)? data.options.uiCmdMap : {};
  264. if (uiCmdMapPrev !== JSON.stringify(self.commandMap)) {
  265. uiCmdMapPrev = JSON.stringify(self.commandMap);
  266. }
  267. } else {
  268. self.options.sync = 0;
  269. }
  270. if (data.init) {
  271. // init - reset cache
  272. files = {};
  273. ownFiles = {};
  274. } else {
  275. // remove only files from prev cwd
  276. // and collapsed directory (included 100+ directories) to empty for perfomance tune in DnD
  277. prevcwd = cwd;
  278. rmClass = 'elfinder-subtree-loaded ' + self.res('class', 'navexpand');
  279. collapsed = self.res('class', 'navcollapse');
  280. hashes = Object.keys(files);
  281. calc = function(i) {
  282. if (!files[i]) {
  283. return true;
  284. }
  285. var isDir = (files[i].mime === 'directory'),
  286. phash = files[i].phash,
  287. pnav;
  288. if (
  289. (!isDir
  290. || emptyDirs[phash]
  291. || (!stayDirs[phash]
  292. && $('#'+self.navHash2Id(files[i].hash)).is(':hidden')
  293. && $('#'+self.navHash2Id(phash)).next('.elfinder-navbar-subtree').children().length > 100
  294. )
  295. )
  296. && (isDir || phash !== cwd)
  297. && ! remember[i]
  298. ) {
  299. if (isDir && !emptyDirs[phash]) {
  300. emptyDirs[phash] = true;
  301. $('#'+self.navHash2Id(phash))
  302. .removeClass(rmClass)
  303. .next('.elfinder-navbar-subtree').empty();
  304. }
  305. deleteCache(files[i]);
  306. } else if (isDir) {
  307. stayDirs[phash] = true;
  308. }
  309. };
  310. gc = function() {
  311. if (hashes.length) {
  312. gcJobRes && gcJobRes._abort();
  313. gcJobRes = self.asyncJob(calc, hashes, {
  314. interval : 20,
  315. numPerOnce : 100
  316. });
  317. }
  318. };
  319. self.trigger('filesgc').one('filesgc', function() {
  320. hashes = [];
  321. });
  322. self.one('opendone', function() {
  323. if (prevcwd !== cwd) {
  324. if (! node.data('lazycnt')) {
  325. gc();
  326. } else {
  327. self.one('lazydone', gc);
  328. }
  329. }
  330. });
  331. }
  332. self.sorters = [];
  333. cwd = data.cwd.hash;
  334. cache(data.files);
  335. if (!files[cwd]) {
  336. cache([data.cwd]);
  337. }
  338. self.lastDir(cwd);
  339. self.autoSync();
  340. },
  341. /**
  342. * Store info about files/dirs in "files" object.
  343. *
  344. * @param Array files
  345. * @param String data type
  346. * @return void
  347. **/
  348. cache = function(data, type) {
  349. var defsorter = { name: true, perm: true, date: true, size: true, kind: true },
  350. sorterChk = (self.sorters.length === 0),
  351. l = data.length,
  352. setSorter = function(file) {
  353. var f = file || {};
  354. self.sorters = [];
  355. $.each(self.sortRules, function(key) {
  356. if (defsorter[key] || typeof f[key] !== 'undefined' || (key === 'mode' && typeof f.perm !== 'undefined')) {
  357. self.sorters.push(key);
  358. }
  359. });
  360. },
  361. keeps = ['sizeInfo'],
  362. changedParents = {},
  363. f, i, keepProp, parents;
  364. for (i = 0; i < l; i++) {
  365. f = Object.assign({}, data[i]);
  366. if (f.name && f.hash && f.mime) {
  367. if (sorterChk && f.phash === cwd) {
  368. setSorter(f);
  369. sorterChk = false;
  370. }
  371. if (f.phash && (type === 'add' || type === 'change')) {
  372. if (parents = self.parents(f.phash)) {
  373. $.each(parents, function() {
  374. changedParents[this] = true;
  375. });
  376. }
  377. }
  378. if (files[f.hash]) {
  379. $.each(keeps, function() {
  380. if(files[f.hash][this] && ! f[this]) {
  381. f[this] = files[f.hash][this];
  382. }
  383. });
  384. if (f.sizeInfo && !f.size) {
  385. f.size = f.sizeInfo.size;
  386. }
  387. deleteCache(files[f.hash], true);
  388. }
  389. files[f.hash] = f;
  390. if (f.mime === 'directory' && !ownFiles[f.hash]) {
  391. ownFiles[f.hash] = {};
  392. }
  393. if (f.phash) {
  394. if (!ownFiles[f.phash]) {
  395. ownFiles[f.phash] = {};
  396. }
  397. ownFiles[f.phash][f.hash] = true;
  398. }
  399. }
  400. }
  401. // delete sizeInfo cache
  402. $.each(Object.keys(changedParents), function() {
  403. var target = files[this];
  404. if (target && target.sizeInfo) {
  405. delete target.sizeInfo;
  406. }
  407. });
  408. // for empty folder
  409. sorterChk && setSorter();
  410. },
  411. /**
  412. * Delete file object from files caches
  413. *
  414. * @param Array removed hashes
  415. * @return void
  416. */
  417. remove = function(removed) {
  418. var l = removed.length,
  419. roots = {},
  420. rm = function(hash) {
  421. var file = files[hash], i;
  422. if (file) {
  423. if (file.mime === 'directory') {
  424. if (roots[hash]) {
  425. delete self.roots[roots[hash]];
  426. }
  427. // restore stats of deleted root parent directory
  428. $.each(self.leafRoots, function(phash, roots) {
  429. var idx, pdir;
  430. if ((idx = $.inArray(hash, roots))!== -1) {
  431. if (roots.length === 1) {
  432. if ((pdir = Object.assign({}, files[phash])) && pdir._realStats) {
  433. $.each(pdir._realStats, function(k, v) {
  434. pdir[k] = v;
  435. });
  436. remove(files[phash]._realStats);
  437. self.change({ changed: [pdir] });
  438. }
  439. delete self.leafRoots[phash];
  440. } else {
  441. self.leafRoots[phash].splice(idx, 1);
  442. }
  443. }
  444. });
  445. if (self.searchStatus.state < 2) {
  446. $.each(files, function(h, f) {
  447. f.phash == hash && rm(h);
  448. });
  449. }
  450. }
  451. if (file.phash) {
  452. if (parents = self.parents(file.phash)) {
  453. $.each(parents, function() {
  454. changedParents[this] = true;
  455. });
  456. }
  457. }
  458. deleteCache(files[hash]);
  459. }
  460. },
  461. changedParents = {},
  462. parents;
  463. $.each(self.roots, function(k, v) {
  464. roots[v] = k;
  465. });
  466. while (l--) {
  467. rm(removed[l]);
  468. }
  469. // delete sizeInfo cache
  470. $.each(Object.keys(changedParents), function() {
  471. var target = files[this];
  472. if (target && target.sizeInfo) {
  473. delete target.sizeInfo;
  474. }
  475. });
  476. },
  477. /**
  478. * Update file object in files caches
  479. *
  480. * @param Array changed file objects
  481. * @return void
  482. */
  483. change = function(changed) {
  484. $.each(changed, function(i, file) {
  485. var hash = file.hash;
  486. if (files[hash]) {
  487. $.each(Object.keys(files[hash]), function(i, v){
  488. if (typeof file[v] === 'undefined') {
  489. delete files[hash][v];
  490. }
  491. });
  492. }
  493. files[hash] = files[hash] ? Object.assign(files[hash], file) : file;
  494. });
  495. },
  496. /**
  497. * Delete cache data of files, ownFiles and self.optionsByHashes
  498. *
  499. * @param Object file
  500. * @param Boolean update
  501. * @return void
  502. */
  503. deleteCache = function(file, update) {
  504. var hash = file.hash,
  505. phash = file.phash;
  506. if (phash && ownFiles[phash]) {
  507. delete ownFiles[phash][hash];
  508. }
  509. if (!update) {
  510. ownFiles[hash] && delete ownFiles[hash];
  511. self.optionsByHashes[hash] && delete self.optionsByHashes[hash];
  512. }
  513. delete files[hash];
  514. },
  515. /**
  516. * Maximum number of concurrent connections on request
  517. *
  518. * @type Number
  519. */
  520. requestMaxConn,
  521. /**
  522. * Current number of connections
  523. *
  524. * @type Number
  525. */
  526. requestCnt = 0,
  527. /**
  528. * Queue waiting for connection
  529. *
  530. * @type Array
  531. */
  532. requestQueue = [],
  533. /**
  534. * Flag to cancel the `open` command waiting for connection
  535. *
  536. * @type Boolean
  537. */
  538. requestQueueSkipOpen = false,
  539. /**
  540. * Exec shortcut
  541. *
  542. * @param jQuery.Event keydown/keypress event
  543. * @return void
  544. */
  545. execShortcut = function(e) {
  546. var code = e.keyCode,
  547. ctrlKey = !!(e.ctrlKey || e.metaKey),
  548. ddm;
  549. if (enabled) {
  550. $.each(shortcuts, function(i, shortcut) {
  551. if (shortcut.type == e.type
  552. && shortcut.keyCode == code
  553. && shortcut.shiftKey == e.shiftKey
  554. && shortcut.ctrlKey == ctrlKey
  555. && shortcut.altKey == e.altKey) {
  556. e.preventDefault();
  557. e.stopPropagation();
  558. shortcut.callback(e, self);
  559. self.debug('shortcut-exec', i+' : '+shortcut.description);
  560. }
  561. });
  562. // prevent tab out of elfinder
  563. if (code == $.ui.keyCode.TAB && !$(e.target).is(':input')) {
  564. e.preventDefault();
  565. }
  566. // cancel any actions by [Esc] key
  567. if (e.type === 'keydown' && code == $.ui.keyCode.ESCAPE) {
  568. // copy or cut
  569. if (! node.find('.ui-widget:visible').length) {
  570. self.clipboard().length && self.clipboard([]);
  571. }
  572. // dragging
  573. if ($.ui.ddmanager) {
  574. ddm = $.ui.ddmanager.current;
  575. ddm && ddm.helper && ddm.cancel();
  576. }
  577. // button menus
  578. node.find('.ui-widget.elfinder-button-menu').hide();
  579. // trigger keydownEsc
  580. self.trigger('keydownEsc', e);
  581. }
  582. }
  583. },
  584. date = new Date(),
  585. utc,
  586. i18n,
  587. inFrame = (window.parent !== window),
  588. parentIframe = (function() {
  589. var pifm, ifms;
  590. if (inFrame) {
  591. try {
  592. ifms = $('iframe', window.parent.document);
  593. if (ifms.length) {
  594. $.each(ifms, function(i, ifm) {
  595. if (ifm.contentWindow === window) {
  596. pifm = $(ifm);
  597. return false;
  598. }
  599. });
  600. }
  601. } catch(e) {}
  602. }
  603. return pifm;
  604. })(),
  605. /**
  606. * elFinder boot up function
  607. *
  608. * @type Function
  609. */
  610. bootUp,
  611. /**
  612. * Original function of XMLHttpRequest.prototype.send
  613. *
  614. * @type Function
  615. */
  616. savedXhrSend;
  617. // opts must be an object
  618. if (!opts) {
  619. opts = {};
  620. }
  621. // set UA.Angle, UA.Rotated for mobile devices
  622. if (self.UA.Mobile) {
  623. $(window).on('orientationchange.'+namespace, function() {
  624. var a = ((screen && screen.orientation && screen.orientation.angle) || window.orientation || 0) + 0;
  625. if (a === -90) {
  626. a = 270;
  627. }
  628. self.UA.Angle = a;
  629. self.UA.Rotated = a % 180 === 0? false : true;
  630. }).trigger('orientationchange.'+namespace);
  631. }
  632. // check opt.bootCallback
  633. if (opts.bootCallback && typeof opts.bootCallback === 'function') {
  634. (function() {
  635. var func = bootCallback,
  636. opFunc = opts.bootCallback;
  637. bootCallback = function(fm, extraObj) {
  638. func && typeof func === 'function' && func.call(this, fm, extraObj);
  639. opFunc.call(this, fm, extraObj);
  640. };
  641. })();
  642. }
  643. delete opts.bootCallback;
  644. /**
  645. * Protocol version
  646. *
  647. * @type String
  648. **/
  649. this.api = null;
  650. /**
  651. * elFinder use new api
  652. *
  653. * @type Boolean
  654. **/
  655. this.newAPI = false;
  656. /**
  657. * elFinder use old api
  658. *
  659. * @type Boolean
  660. **/
  661. this.oldAPI = false;
  662. /**
  663. * Net drivers names
  664. *
  665. * @type Array
  666. **/
  667. this.netDrivers = [];
  668. /**
  669. * Base URL of elfFinder library starting from Manager HTML
  670. *
  671. * @type String
  672. */
  673. this.baseUrl = '';
  674. /**
  675. * Is elFinder CSS loaded
  676. *
  677. * @type Boolean
  678. */
  679. this.cssloaded = false;
  680. /**
  681. * Callback function at boot up that option specified at elFinder starting
  682. *
  683. * @type Function
  684. */
  685. this.bootCallback;
  686. /**
  687. * Configuration options
  688. *
  689. * @type Object
  690. **/
  691. //this.options = $.extend(true, {}, this._options, opts);
  692. this.options = Object.assign({}, this._options);
  693. // for old type configuration
  694. if (opts.uiOptions) {
  695. if (opts.uiOptions.toolbar && Array.isArray(opts.uiOptions.toolbar)) {
  696. if ($.isPlainObject(opts.uiOptions.toolbar[opts.uiOptions.toolbar.length - 1])) {
  697. self.options.uiOptions.toolbarExtra = Object.assign(self.options.uiOptions.toolbarExtra || {}, opts.uiOptions.toolbar.pop());
  698. }
  699. }
  700. }
  701. // Overwrite if opts value is an array
  702. (function() {
  703. var arrOv = function(obj, base) {
  704. if ($.isPlainObject(obj)) {
  705. $.each(obj, function(k, v) {
  706. if ($.isPlainObject(v)) {
  707. if (!base[k]) {
  708. base[k] = {};
  709. }
  710. arrOv(v, base[k]);
  711. } else {
  712. base[k] = v;
  713. }
  714. });
  715. }
  716. };
  717. arrOv(opts, self.options);
  718. })();
  719. // join toolbarExtra to toolbar
  720. this.options.uiOptions.toolbar.push(this.options.uiOptions.toolbarExtra);
  721. delete this.options.uiOptions.toolbarExtra;
  722. // set fm.baseUrl
  723. this.baseUrl = (function() {
  724. var myTag, myCss, base, baseUrl;
  725. if (self.options.baseUrl) {
  726. return self.options.baseUrl;
  727. } else {
  728. baseUrl = '';
  729. myTag = $('head > script[src$="js/elfinder.min.js"],script[src$="js/elfinder.full.js"]:first');
  730. if (myTag.length) {
  731. myCss = $('head > link[href$="css/elfinder.min.css"],link[href$="css/elfinder.full.css"]:first').length;
  732. if (! myCss) {
  733. // to request CSS auto loading
  734. self.cssloaded = null;
  735. }
  736. baseUrl = myTag.attr('src').replace(/js\/[^\/]+$/, '');
  737. if (! baseUrl.match(/^(https?\/\/|\/)/)) {
  738. // check <base> tag
  739. if (base = $('head > base[href]').attr('href')) {
  740. baseUrl = base.replace(/\/$/, '') + '/' + baseUrl;
  741. }
  742. }
  743. }
  744. if (baseUrl !== '') {
  745. self.options.baseUrl = baseUrl;
  746. } else {
  747. if (! self.options.baseUrl) {
  748. self.options.baseUrl = './';
  749. }
  750. baseUrl = self.options.baseUrl;
  751. }
  752. return baseUrl;
  753. }
  754. })();
  755. // set dispInlineRegex
  756. cwdOptionsDefault['dispInlineRegex'] = this.options.dispInlineRegex;
  757. // auto load required CSS
  758. if (this.options.cssAutoLoad) {
  759. (function() {
  760. var baseUrl = self.baseUrl;
  761. if (self.cssloaded === null) {
  762. // hide elFinder node while css loading
  763. node.data('cssautoloadHide', $('<style>.elfinder{visibility:hidden;overflow:hidden}</style>'));
  764. $('head').append(node.data('cssautoloadHide'));
  765. // load CSS
  766. self.loadCss([baseUrl+'css/elfinder.min.css', baseUrl+'css/theme.css']);
  767. // additional CSS files
  768. if (Array.isArray(self.options.cssAutoLoad)) {
  769. self.loadCss(self.options.cssAutoLoad);
  770. }
  771. }
  772. self.options.cssAutoLoad = false;
  773. })();
  774. }
  775. /**
  776. * Volume option to set the properties of the root Stat
  777. *
  778. * @type Object
  779. */
  780. this.optionProperties = {
  781. icon: void(0),
  782. csscls: void(0),
  783. tmbUrl: void(0),
  784. uiCmdMap: {},
  785. netkey: void(0),
  786. disabled: []
  787. };
  788. if (! inFrame && ! this.options.enableAlways && $('body').children().length === 2) { // only node and beeper
  789. this.options.enableAlways = true;
  790. }
  791. if (this.baseUrl === '') {
  792. this.baseUrl = this.options.baseUrl? this.options.baseUrl : '';
  793. }
  794. // make options.debug
  795. if (this.options.debug === true) {
  796. this.options.debug = 'all';
  797. } else if (Array.isArray(this.options.debug)) {
  798. (function() {
  799. var d = {};
  800. $.each(self.options.debug, function() {
  801. d[this] = true;
  802. });
  803. self.options.debug = d;
  804. })();
  805. } else {
  806. this.options.debug = false;
  807. }
  808. /**
  809. * Original functions evacuated by conflict check
  810. *
  811. * @type Object
  812. */
  813. this.noConflicts = {};
  814. /**
  815. * Check and save conflicts with bootstrap etc
  816. *
  817. * @type Function
  818. */
  819. this.noConflict = function() {
  820. $.each(conflictChecks, function(i, p) {
  821. if ($.fn[p] && typeof $.fn[p].noConflict === 'function') {
  822. self.noConflicts[p] = $.fn[p].noConflict();
  823. }
  824. });
  825. };
  826. // do check conflict
  827. this.noConflict();
  828. /**
  829. * Is elFinder over CORS
  830. *
  831. * @type Boolean
  832. **/
  833. this.isCORS = false;
  834. // configure for CORS
  835. (function(){
  836. if (typeof self.options.cors !== 'undefined' && self.options.cors !== null) {
  837. self.isCORS = self.options.cors? true : false;
  838. } else {
  839. var parseUrl = document.createElement('a'),
  840. parseUploadUrl,
  841. selfProtocol = window.location.protocol,
  842. portReg = function(protocol) {
  843. protocol = (!protocol || protocol === ':')? selfProtocol : protocol;
  844. return protocol === 'https:'? /\:443$/ : /\:80$/;
  845. },
  846. selfHost = window.location.host.replace(portReg(selfProtocol), '');
  847. parseUrl.href = opts.url;
  848. if (opts.urlUpload && (opts.urlUpload !== opts.url)) {
  849. parseUploadUrl = document.createElement('a');
  850. parseUploadUrl.href = opts.urlUpload;
  851. }
  852. if (selfHost !== parseUrl.host.replace(portReg(parseUrl.protocol), '')
  853. || (parseUrl.protocol !== ':'&& parseUrl.protocol !== '' && (selfProtocol !== parseUrl.protocol))
  854. || (parseUploadUrl &&
  855. (selfHost !== parseUploadUrl.host.replace(portReg(parseUploadUrl.protocol), '')
  856. || (parseUploadUrl.protocol !== ':' && parseUploadUrl.protocol !== '' && (selfProtocol !== parseUploadUrl.protocol))
  857. )
  858. )
  859. ) {
  860. self.isCORS = true;
  861. }
  862. }
  863. if (self.isCORS) {
  864. if (!$.isPlainObject(self.options.customHeaders)) {
  865. self.options.customHeaders = {};
  866. }
  867. if (!$.isPlainObject(self.options.xhrFields)) {
  868. self.options.xhrFields = {};
  869. }
  870. self.options.requestType = 'post';
  871. self.options.customHeaders['X-Requested-With'] = 'XMLHttpRequest';
  872. self.options.xhrFields['withCredentials'] = true;
  873. }
  874. })();
  875. /**
  876. * Ajax request type
  877. *
  878. * @type String
  879. * @default "get"
  880. **/
  881. this.requestType = /^(get|post)$/i.test(this.options.requestType) ? this.options.requestType.toLowerCase() : 'get';
  882. // set `requestMaxConn` by option
  883. requestMaxConn = Math.max(parseInt(this.options.requestMaxConn), 1);
  884. /**
  885. * Custom data that given as options
  886. *
  887. * @type Object
  888. * @default {}
  889. */
  890. this.optsCustomData = $.isPlainObject(this.options.customData) ? this.options.customData : {};
  891. /**
  892. * Any data to send across every ajax request
  893. *
  894. * @type Object
  895. * @default {}
  896. **/
  897. this.customData = Object.assign({}, this.optsCustomData);
  898. /**
  899. * Previous custom data from connector
  900. *
  901. * @type Object|null
  902. */
  903. this.prevCustomData = null;
  904. /**
  905. * Any custom headers to send across every ajax request
  906. *
  907. * @type Object
  908. * @default {}
  909. */
  910. this.customHeaders = $.isPlainObject(this.options.customHeaders) ? this.options.customHeaders : {};
  911. /**
  912. * Any custom xhrFields to send across every ajax request
  913. *
  914. * @type Object
  915. * @default {}
  916. */
  917. this.xhrFields = $.isPlainObject(this.options.xhrFields) ? this.options.xhrFields : {};
  918. /**
  919. * Replace XMLHttpRequest.prototype.send to extended function for 3rd party libs XHR request etc.
  920. *
  921. * @type Function
  922. */
  923. this.replaceXhrSend = function() {
  924. if (! savedXhrSend) {
  925. savedXhrSend = XMLHttpRequest.prototype.send;
  926. }
  927. XMLHttpRequest.prototype.send = function() {
  928. var xhr = this;
  929. // set request headers
  930. if (self.customHeaders) {
  931. $.each(self.customHeaders, function(key) {
  932. xhr.setRequestHeader(key, this);
  933. });
  934. }
  935. // set xhrFields
  936. if (self.xhrFields) {
  937. $.each(self.xhrFields, function(key) {
  938. if (key in xhr) {
  939. xhr[key] = this;
  940. }
  941. });
  942. }
  943. return savedXhrSend.apply(this, arguments);
  944. };
  945. };
  946. /**
  947. * Restore saved original XMLHttpRequest.prototype.send
  948. *
  949. * @type Function
  950. */
  951. this.restoreXhrSend = function() {
  952. savedXhrSend && (XMLHttpRequest.prototype.send = savedXhrSend);
  953. };
  954. /**
  955. * command names for into queue for only cwd requests
  956. * these commands aborts before `open` request
  957. *
  958. * @type Array
  959. * @default ['tmb', 'parents']
  960. */
  961. this.abortCmdsOnOpen = this.options.abortCmdsOnOpen || ['tmb', 'parents'];
  962. /**
  963. * ID. Required to create unique cookie name
  964. *
  965. * @type String
  966. **/
  967. this.id = id;
  968. /**
  969. * ui.nav id prefix
  970. *
  971. * @type String
  972. */
  973. this.navPrefix = 'nav' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-';
  974. /**
  975. * ui.cwd id prefix
  976. *
  977. * @type String
  978. */
  979. this.cwdPrefix = elFinder.prototype.uniqueid? ('cwd' + elFinder.prototype.uniqueid + '-') : '';
  980. // Increment elFinder.prototype.uniqueid
  981. ++elFinder.prototype.uniqueid;
  982. /**
  983. * URL to upload files
  984. *
  985. * @type String
  986. **/
  987. this.uploadURL = opts.urlUpload || opts.url;
  988. /**
  989. * Events namespace
  990. *
  991. * @type String
  992. **/
  993. this.namespace = namespace;
  994. /**
  995. * Today timestamp
  996. *
  997. * @type Number
  998. **/
  999. this.today = (new Date(date.getFullYear(), date.getMonth(), date.getDate())).getTime()/1000;
  1000. /**
  1001. * Yesterday timestamp
  1002. *
  1003. * @type Number
  1004. **/
  1005. this.yesterday = this.today - 86400;
  1006. utc = this.options.UTCDate ? 'UTC' : '';
  1007. this.getHours = 'get'+utc+'Hours';
  1008. this.getMinutes = 'get'+utc+'Minutes';
  1009. this.getSeconds = 'get'+utc+'Seconds';
  1010. this.getDate = 'get'+utc+'Date';
  1011. this.getDay = 'get'+utc+'Day';
  1012. this.getMonth = 'get'+utc+'Month';
  1013. this.getFullYear = 'get'+utc+'FullYear';
  1014. /**
  1015. * elFinder node z-index (auto detect on elFinder load)
  1016. *
  1017. * @type null | Number
  1018. **/
  1019. this.zIndex;
  1020. /**
  1021. * Current search status
  1022. *
  1023. * @type Object
  1024. */
  1025. this.searchStatus = {
  1026. state : 0, // 0: search ended, 1: search started, 2: in search result
  1027. query : '',
  1028. target : '',
  1029. mime : '',
  1030. mixed : false, // in multi volumes search: false or Array that target volume ids
  1031. ininc : false // in incremental search
  1032. };
  1033. /**
  1034. * Method to store/fetch data
  1035. *
  1036. * @type Function
  1037. **/
  1038. this.storage = (function() {
  1039. try {
  1040. if ('localStorage' in window && window['localStorage'] !== null) {
  1041. if (self.UA.Safari) {
  1042. // check for Mac/iOS safari private browsing mode
  1043. window.localStorage.setItem('elfstoragecheck', 1);
  1044. window.localStorage.removeItem('elfstoragecheck');
  1045. }
  1046. return self.localStorage;
  1047. } else {
  1048. return self.cookie;
  1049. }
  1050. } catch (e) {
  1051. return self.cookie;
  1052. }
  1053. })();
  1054. /**
  1055. * Interface language
  1056. *
  1057. * @type String
  1058. * @default "en"
  1059. **/
  1060. this.lang = this.storage('lang') || this.options.lang;
  1061. if (this.lang === 'jp') {
  1062. this.lang = this.options.lang = 'ja';
  1063. }
  1064. this.viewType = this.storage('view') || this.options.defaultView || 'icons';
  1065. this.sortType = this.storage('sortType') || this.options.sortType || 'name';
  1066. this.sortOrder = this.storage('sortOrder') || this.options.sortOrder || 'asc';
  1067. this.sortStickFolders = this.storage('sortStickFolders');
  1068. if (this.sortStickFolders === null) {
  1069. this.sortStickFolders = !!this.options.sortStickFolders;
  1070. } else {
  1071. this.sortStickFolders = !!this.sortStickFolders;
  1072. }
  1073. this.sortAlsoTreeview = this.storage('sortAlsoTreeview');
  1074. if (this.sortAlsoTreeview === null) {
  1075. this.sortAlsoTreeview = !!this.options.sortAlsoTreeview;
  1076. } else {
  1077. this.sortAlsoTreeview = !!this.sortAlsoTreeview;
  1078. }
  1079. this.sortRules = $.extend(true, {}, this._sortRules, this.options.sortRules);
  1080. $.each(this.sortRules, function(name, method) {
  1081. if (typeof method != 'function') {
  1082. delete self.sortRules[name];
  1083. }
  1084. });
  1085. this.compare = $.proxy(this.compare, this);
  1086. /**
  1087. * Delay in ms before open notification dialog
  1088. *
  1089. * @type Number
  1090. * @default 500
  1091. **/
  1092. this.notifyDelay = this.options.notifyDelay > 0 ? parseInt(this.options.notifyDelay) : 500;
  1093. /**
  1094. * Dragging UI Helper object
  1095. *
  1096. * @type jQuery | null
  1097. **/
  1098. this.draggingUiHelper = null;
  1099. /**
  1100. * Base droppable options
  1101. *
  1102. * @type Object
  1103. **/
  1104. this.droppable = {
  1105. greedy : true,
  1106. tolerance : 'pointer',
  1107. accept : '.elfinder-cwd-file-wrapper,.elfinder-navbar-dir,.elfinder-cwd-file,.elfinder-cwd-filename',
  1108. hoverClass : this.res('class', 'adroppable'),
  1109. classes : { // Deprecated hoverClass jQueryUI>=1.12.0
  1110. 'ui-droppable-hover': this.res('class', 'adroppable')
  1111. },
  1112. autoDisable: true, // elFinder original, see jquery.elfinder.js
  1113. drop : function(e, ui) {
  1114. var dst = $(this),
  1115. targets = $.grep(ui.helper.data('files')||[], function(h) { return h? true : false; }),
  1116. result = [],
  1117. dups = [],
  1118. faults = [],
  1119. isCopy = ui.helper.hasClass('elfinder-drag-helper-plus'),
  1120. c = 'class',
  1121. cnt, hash, i, h;
  1122. if (typeof e.button === 'undefined' || ui.helper.data('namespace') !== namespace || ! self.insideWorkzone(e.pageX, e.pageY)) {
  1123. return false;
  1124. }
  1125. if (dst.hasClass(self.res(c, 'cwdfile'))) {
  1126. hash = self.cwdId2Hash(dst.attr('id'));
  1127. } else if (dst.hasClass(self.res(c, 'navdir'))) {
  1128. hash = self.navId2Hash(dst.attr('id'));
  1129. } else {
  1130. hash = cwd;
  1131. }
  1132. cnt = targets.length;
  1133. while (cnt--) {
  1134. h = targets[cnt];
  1135. // ignore drop into itself or in own location
  1136. if (h != hash && files[h].phash != hash) {
  1137. result.push(h);
  1138. } else {
  1139. ((isCopy && h !== hash && files[hash].write)? dups : faults).push(h);
  1140. }
  1141. }
  1142. if (faults.length) {
  1143. return false;
  1144. }
  1145. ui.helper.data('droped', true);
  1146. if (dups.length) {
  1147. ui.helper.hide();
  1148. self.exec('duplicate', dups, {_userAction: true});
  1149. }
  1150. if (result.length) {
  1151. ui.helper.hide();
  1152. self.clipboard(result, !isCopy);
  1153. self.exec('paste', hash, {_userAction: true}, hash).always(function(){
  1154. self.clipboard([]);
  1155. self.trigger('unlockfiles', {files : targets});
  1156. });
  1157. self.trigger('drop', {files : targets});
  1158. }
  1159. }
  1160. };
  1161. /**
  1162. * Return true if filemanager is active
  1163. *
  1164. * @return Boolean
  1165. **/
  1166. this.enabled = function() {
  1167. return enabled && this.visible();
  1168. };
  1169. /**
  1170. * Return true if filemanager is visible
  1171. *
  1172. * @return Boolean
  1173. **/
  1174. this.visible = function() {
  1175. return node[0].elfinder && node.is(':visible');
  1176. };
  1177. /**
  1178. * Return file is root?
  1179. *
  1180. * @param Object target file object
  1181. * @return Boolean
  1182. */
  1183. this.isRoot = function(file) {
  1184. return (file.isroot || ! file.phash)? true : false;
  1185. };
  1186. /**
  1187. * Return root dir hash for current working directory
  1188. *
  1189. * @param String target hash
  1190. * @param Boolean include fake parent (optional)
  1191. * @return String
  1192. */
  1193. this.root = function(hash, fake) {
  1194. hash = hash || cwd;
  1195. var dir, i;
  1196. if (! fake) {
  1197. $.each(self.roots, function(id, rhash) {
  1198. if (hash.indexOf(id) === 0) {
  1199. dir = rhash;
  1200. return false;
  1201. }
  1202. });
  1203. if (dir) {
  1204. return dir;
  1205. }
  1206. }
  1207. dir = files[hash];
  1208. while (dir && dir.phash && (fake || ! dir.isroot)) {
  1209. dir = files[dir.phash];
  1210. }
  1211. if (dir) {
  1212. return dir.hash;
  1213. }
  1214. while (i in files && files.hasOwnProperty(i)) {
  1215. dir = files[i];
  1216. if (dir.mime === 'directory' && !dir.phash && dir.read) {
  1217. return dir.hash;
  1218. }
  1219. }
  1220. return '';
  1221. };
  1222. /**
  1223. * Return current working directory info
  1224. *
  1225. * @return Object
  1226. */
  1227. this.cwd = function() {
  1228. return files[cwd] || {};
  1229. };
  1230. /**
  1231. * Return required cwd option
  1232. *
  1233. * @param String option name
  1234. * @param String target hash (optional)
  1235. * @return mixed
  1236. */
  1237. this.option = function(name, target) {
  1238. var res, item;
  1239. target = target || cwd;
  1240. if (self.optionsByHashes[target] && typeof self.optionsByHashes[target][name] !== 'undefined') {
  1241. return self.optionsByHashes[target][name];
  1242. }
  1243. if (self.hasVolOptions && cwd !== target && (!(item = self.file(target)) || item.phash !== cwd)) {
  1244. res = '';
  1245. $.each(self.volOptions, function(id, opt) {
  1246. if (target.indexOf(id) === 0) {
  1247. res = opt[name] || '';
  1248. return false;
  1249. }
  1250. });
  1251. return res;
  1252. } else {
  1253. return cwdOptions[name] || '';
  1254. }
  1255. };
  1256. /**
  1257. * Return disabled commands by each folder
  1258. *
  1259. * @param Array target hashes
  1260. * @return Array
  1261. */
  1262. this.getDisabledCmds = function(targets, flip) {
  1263. var disabled = {'hidden': true};
  1264. if (! Array.isArray(targets)) {
  1265. targets = [ targets ];
  1266. }
  1267. $.each(targets, function(i, h) {
  1268. var disCmds = self.option('disabledFlip', h);
  1269. if (disCmds) {
  1270. Object.assign(disabled, disCmds);
  1271. }
  1272. });
  1273. return flip? disabled : Object.keys(disabled);
  1274. };
  1275. /**
  1276. * Return file data from current dir or tree by it's hash
  1277. *
  1278. * @param String file hash
  1279. * @return Object
  1280. */
  1281. this.file = function(hash) {
  1282. return hash? files[hash] : void(0);
  1283. };
  1284. /**
  1285. * Return all cached files
  1286. *
  1287. * @param String parent hash
  1288. * @return Object
  1289. */
  1290. this.files = function(phash) {
  1291. var items = {};
  1292. if (phash) {
  1293. if (!ownFiles[phash]) {
  1294. return {};
  1295. }
  1296. $.each(ownFiles[phash], function(h) {
  1297. if (files[h]) {
  1298. items[h] = files[h];
  1299. } else {
  1300. delete ownFiles[phash][h];
  1301. }
  1302. });
  1303. return Object.assign({}, items);
  1304. }
  1305. return Object.assign({}, files);
  1306. };
  1307. /**
  1308. * Return list of file parents hashes include file hash
  1309. *
  1310. * @param String file hash
  1311. * @return Array
  1312. */
  1313. this.parents = function(hash) {
  1314. var parents = [],
  1315. dir;
  1316. while (hash && (dir = this.file(hash))) {
  1317. parents.unshift(dir.hash);
  1318. hash = dir.phash;
  1319. }
  1320. return parents;
  1321. };
  1322. this.path2array = function(hash, i18) {
  1323. var file,
  1324. path = [];
  1325. while (hash) {
  1326. if ((file = files[hash]) && file.hash) {
  1327. path.unshift(i18 && file.i18 ? file.i18 : file.name);
  1328. hash = file.isroot? null : file.phash;
  1329. } else {
  1330. path = [];
  1331. break;
  1332. }
  1333. }
  1334. return path;
  1335. };
  1336. /**
  1337. * Return file path or Get path async with jQuery.Deferred
  1338. *
  1339. * @param Object file
  1340. * @param Boolean i18
  1341. * @param Object asyncOpt
  1342. * @return String|jQuery.Deferred
  1343. */
  1344. this.path = function(hash, i18, asyncOpt) {
  1345. var path = files[hash] && files[hash].path
  1346. ? files[hash].path
  1347. : this.path2array(hash, i18).join(cwdOptions.separator);
  1348. if (! asyncOpt || ! files[hash]) {
  1349. return path;
  1350. } else {
  1351. asyncOpt = Object.assign({notify: {type : 'parents', cnt : 1, hideCnt : true}}, asyncOpt);
  1352. var dfd = $.Deferred(),
  1353. notify = asyncOpt.notify,
  1354. noreq = false,
  1355. req = function() {
  1356. self.request({
  1357. data : {cmd : 'parents', target : files[hash].phash},
  1358. notify : notify,
  1359. preventFail : true
  1360. })
  1361. .done(done)
  1362. .fail(function() {
  1363. dfd.reject();
  1364. });
  1365. },
  1366. done = function() {
  1367. self.one('parentsdone', function() {
  1368. path = self.path(hash, i18);
  1369. if (path === '' && noreq) {
  1370. //retry with request
  1371. noreq = false;
  1372. req();
  1373. } else {
  1374. if (notify) {
  1375. clearTimeout(ntftm);
  1376. notify.cnt = -(parseInt(notify.cnt || 0));
  1377. self.notify(notify);
  1378. }
  1379. dfd.resolve(path);
  1380. }
  1381. });
  1382. },
  1383. ntftm;
  1384. if (path) {
  1385. return dfd.resolve(path);
  1386. } else {
  1387. if (self.ui['tree']) {
  1388. // try as no request
  1389. if (notify) {
  1390. ntftm = setTimeout(function() {
  1391. self.notify(notify);
  1392. }, self.notifyDelay);
  1393. }
  1394. noreq = true;
  1395. done(true);
  1396. } else {
  1397. req();
  1398. }
  1399. return dfd;
  1400. }
  1401. }
  1402. };
  1403. /**
  1404. * Return file url if set
  1405. *
  1406. * @param String file hash
  1407. * @param Object Options
  1408. * @return String
  1409. */
  1410. this.url = function(hash, o) {
  1411. var file = files[hash],
  1412. opts = o || {},
  1413. async = opts.async || false,
  1414. temp = opts.temporary || false,
  1415. dfrd = async? $.Deferred() : null,
  1416. getUrl = function(url) {
  1417. if (url) {
  1418. return url;
  1419. }
  1420. if (file.url) {
  1421. return file.url;
  1422. }
  1423. baseUrl = self.option('url', file.phash || file.hash);
  1424. if (baseUrl) {
  1425. return baseUrl + $.map(self.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/');
  1426. }
  1427. var params = Object.assign({}, self.customData, {
  1428. cmd: 'file',
  1429. target: file.hash
  1430. });
  1431. if (self.oldAPI) {
  1432. params.cmd = 'open';
  1433. params.current = file.phash;
  1434. }
  1435. return self.options.url + (self.options.url.indexOf('?') === -1 ? '?' : '&') + $.param(params, true);
  1436. },
  1437. baseUrl, res;
  1438. if (!file || !file.read) {
  1439. return async? dfrd.resolve('') : '';
  1440. }
  1441. if (file.url == '1') {
  1442. this.request({
  1443. data : { cmd : 'url', target : hash, options : { temporary: temp? 1 : 0 } },
  1444. preventDefault : true,
  1445. options: {async: async},
  1446. notify: async? {type : temp? 'file' : 'url', cnt : 1, hideCnt : true} : {}
  1447. })
  1448. .done(function(data) {
  1449. file.url = data.url || '';
  1450. })
  1451. .fail(function() {
  1452. file.url = '';
  1453. })
  1454. .always(function() {
  1455. var url;
  1456. if (file.url && temp) {
  1457. url = file.url;
  1458. file.url = '1'; // restore
  1459. }
  1460. if (async) {
  1461. dfrd.resolve(getUrl(url));
  1462. } else {
  1463. return getUrl(url);
  1464. }
  1465. });
  1466. } else {
  1467. if (async) {
  1468. dfrd.resolve(getUrl());
  1469. } else {
  1470. return getUrl();
  1471. }
  1472. }
  1473. if (async) {
  1474. return dfrd;
  1475. }
  1476. };
  1477. /**
  1478. * Return file url for open in elFinder
  1479. *
  1480. * @param String file hash
  1481. * @param Boolean for download link
  1482. * @return String
  1483. */
  1484. this.openUrl = function(hash, download) {
  1485. var file = files[hash],
  1486. url = '';
  1487. if (!file || !file.read) {
  1488. return '';
  1489. }
  1490. if (!download) {
  1491. if (file.url) {
  1492. if (file.url != 1) {
  1493. url = file.url;
  1494. }
  1495. } else if (cwdOptions.url && file.hash.indexOf(self.cwd().volumeid) === 0) {
  1496. url = cwdOptions.url + $.map(this.path2array(hash), function(n) { return encodeURIComponent(n); }).slice(1).join('/');
  1497. }
  1498. if (url) {
  1499. url += (url.match(/\?/)? '&' : '?') + '_'.repeat((url.match(/[\?&](_+)t=/g) || ['&t=']).sort().shift().match(/[\?&](_*)t=/)[1].length + 1) + 't=' + (file.ts || parseInt(+new Date()/1000));
  1500. return url;
  1501. }
  1502. }
  1503. url = this.options.url;
  1504. url = url + (url.indexOf('?') === -1 ? '?' : '&')
  1505. + (this.oldAPI ? 'cmd=open&current='+file.phash : 'cmd=file')
  1506. + '&target=' + file.hash
  1507. + '&_t=' + (file.ts || parseInt(+new Date()/1000));
  1508. if (download) {
  1509. url += '&download=1';
  1510. }
  1511. $.each(this.customData, function(key, val) {
  1512. url += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val);
  1513. });
  1514. return url;
  1515. };
  1516. /**
  1517. * Return thumbnail url
  1518. *
  1519. * @param Object file object
  1520. * @return String
  1521. */
  1522. this.tmb = function(file) {
  1523. var tmbUrl, tmbCrop,
  1524. cls = 'elfinder-cwd-bgurl',
  1525. url = '';
  1526. if ($.isPlainObject(file)) {
  1527. if (self.searchStatus.state && file.hash.indexOf(self.cwd().volumeid) !== 0) {
  1528. tmbUrl = self.option('tmbUrl', file.hash);
  1529. tmbCrop = self.option('tmbCrop', file.hash);
  1530. } else {
  1531. tmbUrl = cwdOptions['tmbUrl'];
  1532. tmbCrop = cwdOptions['tmbCrop'];
  1533. }
  1534. if (tmbCrop) {
  1535. cls += ' elfinder-cwd-bgurl-crop';
  1536. }
  1537. if (tmbUrl === 'self' && file.mime.indexOf('image/') === 0) {
  1538. url = self.openUrl(file.hash);
  1539. cls += ' elfinder-cwd-bgself';
  1540. } else if ((self.oldAPI || tmbUrl) && file && file.tmb && file.tmb != 1) {
  1541. url = tmbUrl + file.tmb;
  1542. } else if (self.newAPI && file && file.tmb && file.tmb != 1) {
  1543. url = file.tmb;
  1544. }
  1545. if (url) {
  1546. if (file.ts) {
  1547. url += (url.match(/\?/)? '&' : '?') + '_t=' + file.ts;
  1548. }
  1549. return { url: url, className: cls };
  1550. }
  1551. }
  1552. return false;
  1553. };
  1554. /**
  1555. * Return selected files hashes
  1556. *
  1557. * @return Array
  1558. **/
  1559. this.selected = function() {
  1560. return selected.slice(0);
  1561. };
  1562. /**
  1563. * Return selected files info
  1564. *
  1565. * @return Array
  1566. */
  1567. this.selectedFiles = function() {
  1568. return $.map(selected, function(hash) { return files[hash] ? Object.assign({}, files[hash]) : null; });
  1569. };
  1570. /**
  1571. * Return true if file with required name existsin required folder
  1572. *
  1573. * @param String file name
  1574. * @param String parent folder hash
  1575. * @return Boolean
  1576. */
  1577. this.fileByName = function(name, phash) {
  1578. var hash;
  1579. for (hash in files) {
  1580. if (files.hasOwnProperty(hash) && files[hash].phash == phash && files[hash].name == name) {
  1581. return files[hash];
  1582. }
  1583. }
  1584. };
  1585. /**
  1586. * Valid data for required command based on rules
  1587. *
  1588. * @param String command name
  1589. * @param Object cammand's data
  1590. * @return Boolean
  1591. */
  1592. this.validResponse = function(cmd, data) {
  1593. return data.error || this.rules[this.rules[cmd] ? cmd : 'defaults'](data);
  1594. };
  1595. /**
  1596. * Return bytes from ini formated size
  1597. *
  1598. * @param String ini formated size
  1599. * @return Integer
  1600. */
  1601. this.returnBytes = function(val) {
  1602. var last;
  1603. if (isNaN(val)) {
  1604. if (! val) {
  1605. val = '';
  1606. }
  1607. // for ex. 1mb, 1KB
  1608. val = val.replace(/b$/i, '');
  1609. last = val.charAt(val.length - 1).toLowerCase();
  1610. val = val.replace(/[tgmk]$/i, '');
  1611. if (last == 't') {
  1612. val = val * 1024 * 1024 * 1024 * 1024;
  1613. } else if (last == 'g') {
  1614. val = val * 1024 * 1024 * 1024;
  1615. } else if (last == 'm') {
  1616. val = val * 1024 * 1024;
  1617. } else if (last == 'k') {
  1618. val = val * 1024;
  1619. }
  1620. val = isNaN(val)? 0 : parseInt(val);
  1621. } else {
  1622. val = parseInt(val);
  1623. if (val < 1) val = 0;
  1624. }
  1625. return val;
  1626. };
  1627. /**
  1628. * Process ajax request.
  1629. * Fired events :
  1630. * @todo
  1631. * @example
  1632. * @todo
  1633. * @return $.Deferred
  1634. */
  1635. this.request = function(opts) {
  1636. var self = this,
  1637. o = this.options,
  1638. dfrd = $.Deferred(),
  1639. // request ID
  1640. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16),
  1641. // request data
  1642. data = Object.assign({}, self.customData, {mimes : o.onlyMimes}, opts.data || opts),
  1643. // command name
  1644. cmd = data.cmd,
  1645. // request type is binary
  1646. isBinary = (opts.options || {}).dataType === 'binary',
  1647. // current cmd is "open"
  1648. isOpen = (!opts.asNotOpen && cmd === 'open'),
  1649. // call default fail callback (display error dialog) ?
  1650. deffail = !(isBinary || opts.preventDefault || opts.preventFail),
  1651. // call default success callback ?
  1652. defdone = !(isBinary || opts.preventDefault || opts.preventDone),
  1653. // options for notify dialog
  1654. notify = Object.assign({}, opts.notify),
  1655. // make cancel button
  1656. cancel = !!opts.cancel,
  1657. // do not normalize data - return as is
  1658. raw = isBinary || !!opts.raw,
  1659. // sync files on request fail
  1660. syncOnFail = opts.syncOnFail,
  1661. // use lazy()
  1662. lazy = !!opts.lazy,
  1663. // prepare function before done()
  1664. prepare = opts.prepare,
  1665. // navigate option object when cmd done
  1666. navigate = opts.navigate,
  1667. // open notify dialog timeout
  1668. timeout,
  1669. // use browser cache
  1670. useCache = (opts.options || {}).cache,
  1671. // request options
  1672. options = Object.assign({
  1673. url : o.url,
  1674. async : true,
  1675. type : this.requestType,
  1676. dataType : 'json',
  1677. cache : (self.api >= 2.1029), // api >= 2.1029 has unique request ID
  1678. data : data,
  1679. headers : this.customHeaders,
  1680. xhrFields: this.xhrFields
  1681. }, opts.options || {}),
  1682. /**
  1683. * Default success handler.
  1684. * Call default data handlers and fire event with command name.
  1685. *
  1686. * @param Object normalized response data
  1687. * @return void
  1688. **/
  1689. done = function(data) {
  1690. data.warning && self.error(data.warning);
  1691. if (isOpen) {
  1692. open(data);
  1693. } else {
  1694. self.updateCache(data);
  1695. }
  1696. data.changed && data.changed.length && change(data.changed);
  1697. self.lazy(function() {
  1698. // fire some event to update cache/ui
  1699. data.removed && data.removed.length && self.remove(data);
  1700. data.added && data.added.length && self.add(data);
  1701. data.changed && data.changed.length && self.change(data);
  1702. }).then(function() {
  1703. // fire event with command name
  1704. return self.lazy(function() {
  1705. self.trigger(cmd, data, false);
  1706. });
  1707. }).then(function() {
  1708. // fire event with command name + 'done'
  1709. return self.lazy(function() {
  1710. self.trigger(cmd + 'done');
  1711. });
  1712. }).then(function() {
  1713. // force update content
  1714. data.sync && self.sync();
  1715. });
  1716. },
  1717. /**
  1718. * Request error handler. Reject dfrd with correct error message.
  1719. *
  1720. * @param jqxhr request object
  1721. * @param String request status
  1722. * @return void
  1723. **/
  1724. error = function(xhr, status) {
  1725. var error, data,
  1726. d = self.options.debug;
  1727. switch (status) {
  1728. case 'abort':
  1729. error = xhr.quiet ? '' : ['errConnect', 'errAbort'];
  1730. break;
  1731. case 'timeout':
  1732. error = ['errConnect', 'errTimeout'];
  1733. break;
  1734. case 'parsererror':
  1735. error = ['errResponse', 'errDataNotJSON'];
  1736. if (xhr.responseText) {
  1737. if (! cwd || (d && (d === 'all' || d['backend-error']))) {
  1738. error.push(xhr.responseText);
  1739. }
  1740. }
  1741. break;
  1742. default:
  1743. if (xhr.responseText) {
  1744. // check responseText, Is that JSON?
  1745. try {
  1746. data = JSON.parse(xhr.responseText);
  1747. if (data && data.error) {
  1748. error = data.error;
  1749. }
  1750. } catch(e) {}
  1751. }
  1752. if (! error) {
  1753. if (xhr.status == 403) {
  1754. error = ['errConnect', 'errAccess', 'HTTP error ' + xhr.status];
  1755. } else if (xhr.status == 404) {
  1756. error = ['errConnect', 'errNotFound', 'HTTP error ' + xhr.status];
  1757. } else if (xhr.status >= 500) {
  1758. error = ['errResponse', 'errServerError', 'HTTP error ' + xhr.status];
  1759. } else {
  1760. if (xhr.status == 414 && options.type === 'get') {
  1761. // retry by POST method
  1762. options.type = 'post';
  1763. self.abortXHR(xhr);
  1764. dfrd.xhr = xhr = self.transport.send(options).fail(error).done(success);
  1765. return;
  1766. }
  1767. error = xhr.quiet ? '' : ['errConnect', 'HTTP error ' + xhr.status];
  1768. }
  1769. }
  1770. }
  1771. self.trigger(cmd + 'done');
  1772. dfrd.reject(error, xhr, status);
  1773. },
  1774. /**
  1775. * Request success handler. Valid response data and reject/resolve dfrd.
  1776. *
  1777. * @param Object response data
  1778. * @param String request status
  1779. * @return void
  1780. **/
  1781. success = function(response) {
  1782. var d = self.options.debug;
  1783. // Set currrent request command name
  1784. self.currentReqCmd = cmd;
  1785. if (response.debug && (!d || d !== 'all')) {
  1786. if (!d) {
  1787. d = self.options.debug = {};
  1788. }
  1789. d['backend-error'] = true;
  1790. d['warning'] = true;
  1791. }
  1792. if (raw) {
  1793. self.abortXHR(xhr);
  1794. response && response.debug && self.debug('backend-debug', response);
  1795. return dfrd.resolve(response);
  1796. }
  1797. if (!response) {
  1798. return dfrd.reject(['errResponse', 'errDataEmpty'], xhr, response);
  1799. } else if (!$.isPlainObject(response)) {
  1800. return dfrd.reject(['errResponse', 'errDataNotJSON'], xhr, response);
  1801. } else if (response.error) {
  1802. if (isOpen) {
  1803. // check leafRoots
  1804. $.each(self.leafRoots, function(phash, roots) {
  1805. self.leafRoots[phash] = $.grep(roots, function(h) { return h !== data.target; });
  1806. });
  1807. }
  1808. return dfrd.reject(response.error, xhr, response);
  1809. }
  1810. var resolve = function() {
  1811. var pushLeafRoots = function(name) {
  1812. if (self.leafRoots[data.target] && response[name]) {
  1813. $.each(self.leafRoots[data.target], function(i, h) {
  1814. var root;
  1815. if (root = self.file(h)) {
  1816. response[name].push(root);
  1817. }
  1818. });
  1819. }
  1820. },
  1821. setTextMimes = function() {
  1822. self.textMimes = {};
  1823. $.each(self.resources.mimes.text, function() {
  1824. self.textMimes[this] = true;
  1825. });
  1826. },
  1827. actionTarget;
  1828. if (isOpen) {
  1829. pushLeafRoots('files');
  1830. } else if (cmd === 'tree') {
  1831. pushLeafRoots('tree');
  1832. }
  1833. response = self.normalize(response);
  1834. if (!self.validResponse(cmd, response)) {
  1835. return dfrd.reject((response.norError || 'errResponse'), xhr, response);
  1836. }
  1837. if (isOpen) {
  1838. if (!self.api) {
  1839. self.api = response.api || 1;
  1840. if (self.api == '2.0' && typeof response.options.uploadMaxSize !== 'undefined') {
  1841. self.api = '2.1';
  1842. }
  1843. self.newAPI = self.api >= 2;
  1844. self.oldAPI = !self.newAPI;
  1845. }
  1846. if (response.textMimes && Array.isArray(response.textMimes)) {
  1847. self.resources.mimes.text = response.textMimes;
  1848. setTextMimes();
  1849. }
  1850. !self.textMimes && setTextMimes();
  1851. if (response.options) {
  1852. cwdOptions = Object.assign({}, cwdOptionsDefault, response.options);
  1853. }
  1854. if (response.netDrivers) {
  1855. self.netDrivers = response.netDrivers;
  1856. }
  1857. if (response.maxTargets) {
  1858. self.maxTargets = response.maxTargets;
  1859. }
  1860. if (!!data.init) {
  1861. self.uplMaxSize = self.returnBytes(response.uplMaxSize);
  1862. self.uplMaxFile = !!response.uplMaxFile? parseInt(response.uplMaxFile) : 20;
  1863. }
  1864. }
  1865. if (typeof prepare === 'function') {
  1866. prepare(response);
  1867. }
  1868. if (navigate) {
  1869. actionTarget = navigate.target || 'added';
  1870. if (response[actionTarget] && response[actionTarget].length) {
  1871. self.one(cmd + 'done', function() {
  1872. var targets = response[actionTarget],
  1873. newItems = self.findCwdNodes(targets),
  1874. inCwdHashes = function() {
  1875. var cwdHash = self.cwd().hash;
  1876. return $.map(targets, function(f) { return (f.phash && cwdHash === f.phash)? f.hash : null; });
  1877. },
  1878. hashes = inCwdHashes(),
  1879. makeToast = function(t) {
  1880. var node = void(0),
  1881. data = t.action? t.action.data : void(0),
  1882. cmd, msg, done;
  1883. if ((data || hashes.length) && t.action && (msg = t.action.msg) && (cmd = t.action.cmd) && (!t.action.cwdNot || t.action.cwdNot !== self.cwd().hash)) {
  1884. done = t.action.done;
  1885. data = t.action.data;
  1886. node = $('<div/>')
  1887. .append(
  1888. $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all elfinder-tabstop"><span class="ui-button-text">'
  1889. +self.i18n(msg)
  1890. +'</span></button>')
  1891. .on('mouseenter mouseleave', function(e) {
  1892. $(this).toggleClass('ui-state-hover', e.type == 'mouseenter');
  1893. })
  1894. .on('click', function() {
  1895. self.exec(cmd, data || hashes, {_userAction: true, _currentType: 'toast', _currentNode: $(this) });
  1896. if (done) {
  1897. self.one(cmd+'done', function() {
  1898. if (typeof done === 'function') {
  1899. done();
  1900. } else if (done === 'select') {
  1901. self.trigger('selectfiles', {files : inCwdHashes()});
  1902. }
  1903. });
  1904. }
  1905. })
  1906. );
  1907. }
  1908. delete t.action;
  1909. t.extNode = node;
  1910. return t;
  1911. };
  1912. if (! navigate.toast) {
  1913. navigate.toast = {};
  1914. }
  1915. !navigate.noselect && self.trigger('selectfiles', {files : self.searchStatus.state > 1 ? $.map(targets, function(f) { return f.hash; }) : hashes});
  1916. if (newItems.length) {
  1917. if (!navigate.noscroll) {
  1918. newItems.first().trigger('scrolltoview', {blink : false});
  1919. self.resources.blink(newItems, 'lookme');
  1920. }
  1921. if ($.isPlainObject(navigate.toast.incwd)) {
  1922. self.toast(makeToast(navigate.toast.incwd));
  1923. }
  1924. } else {
  1925. if ($.isPlainObject(navigate.toast.inbuffer)) {
  1926. self.toast(makeToast(navigate.toast.inbuffer));
  1927. }
  1928. }
  1929. });
  1930. }
  1931. }
  1932. dfrd.resolve(response);
  1933. response.debug && self.debug('backend-debug', response);
  1934. };
  1935. self.abortXHR(xhr);
  1936. lazy? self.lazy(resolve) : resolve();
  1937. },
  1938. xhr, _xhr,
  1939. xhrAbort = function(e) {
  1940. if (xhr && xhr.state() === 'pending') {
  1941. self.abortXHR(xhr, { quiet: true , abort: true });
  1942. if (!e || (e.type !== 'unload' && e.type !== 'destroy')) {
  1943. self.autoSync();
  1944. }
  1945. }
  1946. },
  1947. abort = function(e){
  1948. self.trigger(cmd + 'done');
  1949. if (e.type == 'autosync') {
  1950. if (e.data.action != 'stop') return;
  1951. } else if (e.type != 'unload' && e.type != 'destroy' && e.type != 'openxhrabort') {
  1952. if (!e.data.added || !e.data.added.length) {
  1953. return;
  1954. }
  1955. }
  1956. xhrAbort(e);
  1957. },
  1958. request = function(mode) {
  1959. var queueAbort = function() {
  1960. syncOnFail = false;
  1961. dfrd.reject();
  1962. };
  1963. if (mode) {
  1964. if (mode === 'cmd') {
  1965. return cmd;
  1966. }
  1967. }
  1968. if (isOpen) {
  1969. if (requestQueueSkipOpen) {
  1970. return dfrd.reject();
  1971. }
  1972. requestQueueSkipOpen = true;
  1973. }
  1974. requestCnt++;
  1975. dfrd.always(function() {
  1976. delete options.headers['X-elFinderReqid'];
  1977. }).fail(function(error, xhr, response) {
  1978. // unset this cmd queue when user canceling
  1979. if (error === 0) {
  1980. if (requestQueue.length) {
  1981. requestQueue = $.grep(requestQueue, function(req) {
  1982. return (req('cmd') === cmd) ? false : true;
  1983. });
  1984. }
  1985. }
  1986. xhrAbort();
  1987. self.trigger(cmd + 'fail', response);
  1988. if (error) {
  1989. deffail ? self.error(error) : self.debug('error', self.i18n(error));
  1990. }
  1991. syncOnFail && self.sync();
  1992. });
  1993. if (!cmd) {
  1994. syncOnFail = false;
  1995. return dfrd.reject('errCmdReq');
  1996. }
  1997. if (self.maxTargets && data.targets && data.targets.length > self.maxTargets) {
  1998. syncOnFail = false;
  1999. return dfrd.reject(['errMaxTargets', self.maxTargets]);
  2000. }
  2001. defdone && dfrd.done(done);
  2002. // quiet abort not completed "open" requests
  2003. if (isOpen) {
  2004. while ((_xhr = queue.pop())) {
  2005. _xhr.queueAbort();
  2006. }
  2007. if (cwd !== data.target) {
  2008. while ((_xhr = cwdQueue.pop())) {
  2009. _xhr.queueAbort();
  2010. }
  2011. }
  2012. }
  2013. // trigger abort autoSync for commands to add the item
  2014. if ($.inArray(cmd, (self.cmdsToAdd + ' autosync').split(' ')) !== -1) {
  2015. if (cmd !== 'autosync') {
  2016. self.autoSync('stop');
  2017. dfrd.always(function() {
  2018. self.autoSync();
  2019. });
  2020. }
  2021. self.trigger('openxhrabort');
  2022. }
  2023. delete options.preventFail;
  2024. if (self.api >= 2.1029) {
  2025. if (useCache) {
  2026. options.headers['X-elFinderReqid'] = reqId;
  2027. } else {
  2028. Object.assign(options.data, { reqid : reqId });
  2029. }
  2030. }
  2031. // function for set value of this syncOnFail
  2032. dfrd.syncOnFail = function(state) {
  2033. syncOnFail = !!state;
  2034. };
  2035. dfrd.xhr = xhr = self.transport.send(options).always(function() {
  2036. --requestCnt;
  2037. if (requestQueue.length) {
  2038. requestQueue.shift()();
  2039. } else {
  2040. requestQueueSkipOpen = false;
  2041. }
  2042. }).fail(error).done(success);
  2043. if (self.api >= 2.1029) {
  2044. xhr._requestId = reqId;
  2045. }
  2046. if (isOpen || (data.compare && cmd === 'info')) {
  2047. // regist function queueAbort
  2048. xhr.queueAbort = queueAbort;
  2049. // add autoSync xhr into queue
  2050. queue.unshift(xhr);
  2051. // bind abort()
  2052. data.compare && self.bind(self.cmdsToAdd + ' autosync openxhrabort', abort);
  2053. dfrd.always(function() {
  2054. var ndx = $.inArray(xhr, queue);
  2055. data.compare && self.unbind(self.cmdsToAdd + ' autosync openxhrabort', abort);
  2056. ndx !== -1 && queue.splice(ndx, 1);
  2057. });
  2058. } else if ($.inArray(cmd, self.abortCmdsOnOpen) !== -1) {
  2059. // regist function queueAbort
  2060. xhr.queueAbort = queueAbort;
  2061. // add "open" xhr, only cwd xhr into queue
  2062. cwdQueue.unshift(xhr);
  2063. dfrd.always(function() {
  2064. var ndx = $.inArray(xhr, cwdQueue);
  2065. ndx !== -1 && cwdQueue.splice(ndx, 1);
  2066. });
  2067. }
  2068. // abort pending xhr on window unload or elFinder destroy
  2069. self.bind('unload destroy', abort);
  2070. dfrd.always(function() {
  2071. self.unbind('unload destroy', abort);
  2072. });
  2073. return dfrd;
  2074. },
  2075. queueingRequest = function() {
  2076. // show notify
  2077. if (notify.type && notify.cnt) {
  2078. if (cancel) {
  2079. notify.cancel = dfrd;
  2080. opts.eachCancel && (notify.id = +new Date());
  2081. }
  2082. timeout = setTimeout(function() {
  2083. self.notify(notify);
  2084. dfrd.always(function() {
  2085. notify.cnt = -(parseInt(notify.cnt)||0);
  2086. self.notify(notify);
  2087. });
  2088. }, self.notifyDelay);
  2089. dfrd.always(function() {
  2090. clearTimeout(timeout);
  2091. });
  2092. }
  2093. // queueing
  2094. if (isOpen) {
  2095. requestQueueSkipOpen = false;
  2096. }
  2097. if (requestCnt < requestMaxConn) {
  2098. // do request
  2099. return request();
  2100. } else {
  2101. if (isOpen) {
  2102. requestQueue.unshift(request);
  2103. } else {
  2104. requestQueue.push(request);
  2105. }
  2106. return dfrd;
  2107. }
  2108. },
  2109. bindData = {opts: opts, result: true};
  2110. // prevent request initial request is completed
  2111. if (!self.api && !data.init) {
  2112. syncOnFail = false;
  2113. return dfrd.reject();
  2114. }
  2115. // trigger "request.cmd" that callback be able to cancel request by substituting "false" for "event.data.result"
  2116. self.trigger('request.' + cmd, bindData, true);
  2117. if (! bindData.result) {
  2118. self.trigger(cmd + 'done');
  2119. return dfrd.reject();
  2120. } else if (typeof bindData.result === 'object' && bindData.result.promise) {
  2121. bindData.result
  2122. .done(queueingRequest)
  2123. .fail(function() {
  2124. self.trigger(cmd + 'done');
  2125. dfrd.reject();
  2126. });
  2127. return dfrd;
  2128. }
  2129. return queueingRequest();
  2130. };
  2131. /**
  2132. * Call cache()
  2133. * Store info about files/dirs in "files" object.
  2134. *
  2135. * @param Array files
  2136. * @return void
  2137. */
  2138. this.cache = function(dataArray) {
  2139. if (! Array.isArray(dataArray)) {
  2140. dataArray = [ dataArray ];
  2141. }
  2142. cache(dataArray);
  2143. };
  2144. /**
  2145. * Update file object caches by respose data object
  2146. *
  2147. * @param Object respose data object
  2148. * @return void
  2149. */
  2150. this.updateCache = function(data) {
  2151. if ($.isPlainObject(data)) {
  2152. data.files && data.files.length && cache(data.files, 'files');
  2153. data.tree && data.tree.length && cache(data.tree, 'tree');
  2154. data.removed && data.removed.length && remove(data.removed);
  2155. data.added && data.added.length && cache(data.added, 'add');
  2156. data.changed && data.changed.length && change(data.changed, 'change');
  2157. }
  2158. };
  2159. /**
  2160. * Compare current files cache with new files and return diff
  2161. *
  2162. * @param Array new files
  2163. * @param String target folder hash
  2164. * @param Array exclude properties to compare
  2165. * @return Object
  2166. */
  2167. this.diff = function(incoming, onlydir, excludeProps) {
  2168. var raw = {},
  2169. added = [],
  2170. removed = [],
  2171. changed = [],
  2172. excludes = null,
  2173. isChanged = function(hash) {
  2174. var l = changed.length;
  2175. while (l--) {
  2176. if (changed[l].hash == hash) {
  2177. return true;
  2178. }
  2179. }
  2180. };
  2181. $.each(incoming, function(i, f) {
  2182. raw[f.hash] = f;
  2183. });
  2184. // make excludes object
  2185. if (excludeProps && excludeProps.length) {
  2186. excludes = {};
  2187. $.each(excludeProps, function() {
  2188. excludes[this] = true;
  2189. });
  2190. }
  2191. // find removed
  2192. $.each(files, function(hash, f) {
  2193. if (! raw[hash] && (! onlydir || f.phash === onlydir)) {
  2194. removed.push(hash);
  2195. }
  2196. });
  2197. // compare files
  2198. $.each(raw, function(hash, file) {
  2199. var origin = files[hash],
  2200. orgKeys = {},
  2201. chkKeyLen;
  2202. if (!origin) {
  2203. added.push(file);
  2204. } else {
  2205. // make orgKeys object
  2206. $.each(Object.keys(origin), function() {
  2207. orgKeys[this] = true;
  2208. });
  2209. $.each(file, function(prop) {
  2210. delete orgKeys[prop];
  2211. if (! excludes || ! excludes[prop]) {
  2212. if (file[prop] !== origin[prop]) {
  2213. changed.push(file);
  2214. orgKeys = {};
  2215. return false;
  2216. }
  2217. }
  2218. });
  2219. chkKeyLen = Object.keys(orgKeys).length;
  2220. if (chkKeyLen !== 0) {
  2221. if (excludes) {
  2222. $.each(orgKeys, function(prop) {
  2223. if (excludes[prop]) {
  2224. --chkKeyLen;
  2225. }
  2226. });
  2227. }
  2228. (chkKeyLen !== 0) && changed.push(file);
  2229. }
  2230. }
  2231. });
  2232. // parents of removed dirs mark as changed (required for tree correct work)
  2233. $.each(removed, function(i, hash) {
  2234. var file = files[hash],
  2235. phash = file.phash;
  2236. if (phash
  2237. && file.mime == 'directory'
  2238. && $.inArray(phash, removed) === -1
  2239. && raw[phash]
  2240. && !isChanged(phash)) {
  2241. changed.push(raw[phash]);
  2242. }
  2243. });
  2244. return {
  2245. added : added,
  2246. removed : removed,
  2247. changed : changed
  2248. };
  2249. };
  2250. /**
  2251. * Sync content
  2252. *
  2253. * @return jQuery.Deferred
  2254. */
  2255. this.sync = function(onlydir, polling) {
  2256. this.autoSync('stop');
  2257. var self = this,
  2258. compare = function(){
  2259. var c = '', cnt = 0, mtime = 0;
  2260. if (onlydir && polling) {
  2261. $.each(files, function(h, f) {
  2262. if (f.phash && f.phash === onlydir) {
  2263. ++cnt;
  2264. mtime = Math.max(mtime, f.ts);
  2265. }
  2266. c = cnt+':'+mtime;
  2267. });
  2268. }
  2269. return c;
  2270. },
  2271. comp = compare(),
  2272. dfrd = $.Deferred().done(function() { self.trigger('sync'); }),
  2273. opts = [this.request({
  2274. data : {cmd : 'open', reload : 1, target : cwd, tree : (! onlydir && this.ui.tree) ? 1 : 0, compare : comp},
  2275. preventDefault : true
  2276. })],
  2277. exParents = function() {
  2278. var parents = [],
  2279. curRoot = self.file(self.root(cwd)),
  2280. curId = curRoot? curRoot.volumeid : null,
  2281. phash = self.cwd().phash,
  2282. isroot,pdir;
  2283. while(phash) {
  2284. if (pdir = self.file(phash)) {
  2285. if (phash.indexOf(curId) !== 0) {
  2286. parents.push( {target: phash, cmd: 'tree'} );
  2287. if (! self.isRoot(pdir)) {
  2288. parents.push( {target: phash, cmd: 'parents'} );
  2289. }
  2290. curRoot = self.file(self.root(phash));
  2291. curId = curRoot? curRoot.volumeid : null;
  2292. }
  2293. phash = pdir.phash;
  2294. } else {
  2295. phash = null;
  2296. }
  2297. }
  2298. return parents;
  2299. };
  2300. if (! onlydir && self.api >= 2) {
  2301. (cwd !== this.root()) && opts.push(this.request({
  2302. data : {cmd : 'parents', target : cwd},
  2303. preventDefault : true
  2304. }));
  2305. $.each(exParents(), function(i, data) {
  2306. opts.push(self.request({
  2307. data : {cmd : data.cmd, target : data.target},
  2308. preventDefault : true
  2309. }));
  2310. });
  2311. }
  2312. $.when.apply($, opts)
  2313. .fail(function(error, xhr) {
  2314. if (! polling || $.inArray('errOpen', error) !== -1) {
  2315. dfrd.reject(error);
  2316. error && self.request({
  2317. data : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1},
  2318. notify : {type : 'open', cnt : 1, hideCnt : true}
  2319. });
  2320. } else {
  2321. dfrd.reject((error && xhr.status != 0)? error : void 0);
  2322. }
  2323. })
  2324. .done(function(odata) {
  2325. var pdata, argLen, i;
  2326. if (odata.cwd.compare) {
  2327. if (comp === odata.cwd.compare) {
  2328. return dfrd.reject();
  2329. }
  2330. }
  2331. // for 2nd and more requests
  2332. pdata = {tree : []};
  2333. // results marge of 2nd and more requests
  2334. argLen = arguments.length;
  2335. if (argLen > 1) {
  2336. for(i = 1; i < argLen; i++) {
  2337. if (arguments[i].tree && arguments[i].tree.length) {
  2338. pdata.tree.push.apply(pdata.tree, arguments[i].tree);
  2339. }
  2340. }
  2341. }
  2342. if (self.api < 2.1) {
  2343. if (! pdata.tree) {
  2344. pdata.tree = [];
  2345. }
  2346. pdata.tree.push(odata.cwd);
  2347. }
  2348. // data normalize
  2349. odata = self.normalize(odata);
  2350. if (!self.validResponse('open', odata)) {
  2351. return dfrd.reject((odata.norError || 'errResponse'));
  2352. }
  2353. pdata = self.normalize(pdata);
  2354. if (!self.validResponse('tree', pdata)) {
  2355. return dfrd.reject((pdata.norError || 'errResponse'));
  2356. }
  2357. var diff = self.diff(odata.files.concat(pdata && pdata.tree ? pdata.tree : []), onlydir);
  2358. diff.added.push(odata.cwd);
  2359. self.updateCache(diff);
  2360. // trigger events
  2361. diff.removed.length && self.remove(diff);
  2362. diff.added.length && self.add(diff);
  2363. diff.changed.length && self.change(diff);
  2364. return dfrd.resolve(diff);
  2365. })
  2366. .always(function() {
  2367. self.autoSync();
  2368. });
  2369. return dfrd;
  2370. };
  2371. this.upload = function(files) {
  2372. return this.transport.upload(files, this);
  2373. };
  2374. /**
  2375. * Arrays that has to unbind events
  2376. *
  2377. * @type Object
  2378. */
  2379. this.toUnbindEvents = {};
  2380. /**
  2381. * Attach listener to events
  2382. * To bind to multiply events at once, separate events names by space
  2383. *
  2384. * @param String event(s) name(s)
  2385. * @param Object event handler
  2386. * @return elFinder
  2387. */
  2388. this.bind = function(event, callback) {
  2389. var i, len;
  2390. if (typeof(callback) == 'function') {
  2391. event = ('' + event).toLowerCase().replace(/^\s+|\s+$/g, '').split(/\s+/);
  2392. len = event.length;
  2393. for (i = 0; i < len; i++) {
  2394. if (listeners[event[i]] === void(0)) {
  2395. listeners[event[i]] = [];
  2396. }
  2397. listeners[event[i]].push(callback);
  2398. }
  2399. }
  2400. return this;
  2401. };
  2402. /**
  2403. * Remove event listener if exists
  2404. * To un-bind to multiply events at once, separate events names by space
  2405. *
  2406. * @param String event(s) name(s)
  2407. * @param Function callback
  2408. * @return elFinder
  2409. */
  2410. this.unbind = function(event, callback) {
  2411. var i, len, l, ci;
  2412. event = ('' + event).toLowerCase().split(/\s+/);
  2413. len = event.length;
  2414. for (i = 0; i < len; i++) {
  2415. if (l = listeners[event[i]]) {
  2416. ci = $.inArray(callback, l);
  2417. ci > -1 && l.splice(ci, 1);
  2418. }
  2419. }
  2420. callback = null;
  2421. return this;
  2422. };
  2423. /**
  2424. * Fire event - send notification to all event listeners
  2425. * In the callback `this` becames an event object
  2426. *
  2427. * @param String event type
  2428. * @param Object data to send across event
  2429. * @param Boolean allow modify data (call by reference of data) default: true
  2430. * @return elFinder
  2431. */
  2432. this.trigger = function(evType, data, allowModify) {
  2433. var type = evType.toLowerCase(),
  2434. isopen = (type === 'open'),
  2435. dataIsObj = (typeof data === 'object'),
  2436. handlers = listeners[type] || [], i, l, jst, event;
  2437. this.debug('event-'+type, data);
  2438. if (! dataIsObj || typeof allowModify === 'undefined') {
  2439. allowModify = true;
  2440. }
  2441. if (l = handlers.length) {
  2442. event = $.Event(type);
  2443. if (allowModify) {
  2444. event.data = data;
  2445. }
  2446. for (i = 0; i < l; i++) {
  2447. if (! handlers[i]) {
  2448. // probably un-binded this handler
  2449. continue;
  2450. }
  2451. // set `event.data` only callback has argument
  2452. if (handlers[i].length) {
  2453. if (!allowModify) {
  2454. // to avoid data modifications. remember about "sharing" passing arguments in js :)
  2455. if (typeof jst === 'undefined') {
  2456. try {
  2457. jst = JSON.stringify(data);
  2458. } catch(e) {
  2459. jst = false;
  2460. }
  2461. }
  2462. event.data = jst? JSON.parse(jst) : data;
  2463. }
  2464. }
  2465. try {
  2466. if (handlers[i].call(event, event, this) === false
  2467. || event.isDefaultPrevented()) {
  2468. this.debug('event-stoped', event.type);
  2469. break;
  2470. }
  2471. } catch (ex) {
  2472. window.console && window.console.log && window.console.log(ex);
  2473. }
  2474. }
  2475. if (this.toUnbindEvents[type] && this.toUnbindEvents[type].length) {
  2476. $.each(this.toUnbindEvents[type], function(i, v) {
  2477. self.unbind(v.type, v.callback);
  2478. });
  2479. delete this.toUnbindEvents[type];
  2480. }
  2481. }
  2482. return this;
  2483. };
  2484. /**
  2485. * Get event listeners
  2486. *
  2487. * @param String event type
  2488. * @return Array listed event functions
  2489. */
  2490. this.getListeners = function(event) {
  2491. return event? listeners[event.toLowerCase()] : listeners;
  2492. };
  2493. /**
  2494. * Bind keybord shortcut to keydown event
  2495. *
  2496. * @example
  2497. * elfinder.shortcut({
  2498. * pattern : 'ctrl+a',
  2499. * description : 'Select all files',
  2500. * callback : function(e) { ... },
  2501. * keypress : true|false (bind to keypress instead of keydown)
  2502. * })
  2503. *
  2504. * @param Object shortcut config
  2505. * @return elFinder
  2506. */
  2507. this.shortcut = function(s) {
  2508. var patterns, pattern, code, i, parts;
  2509. if (this.options.allowShortcuts && s.pattern && $.isFunction(s.callback)) {
  2510. patterns = s.pattern.toUpperCase().split(/\s+/);
  2511. for (i= 0; i < patterns.length; i++) {
  2512. pattern = patterns[i];
  2513. parts = pattern.split('+');
  2514. code = (code = parts.pop()).length == 1
  2515. ? code > 0 ? code : code.charCodeAt(0)
  2516. : (code > 0 ? code : $.ui.keyCode[code]);
  2517. if (code && !shortcuts[pattern]) {
  2518. shortcuts[pattern] = {
  2519. keyCode : code,
  2520. altKey : $.inArray('ALT', parts) != -1,
  2521. ctrlKey : $.inArray('CTRL', parts) != -1,
  2522. shiftKey : $.inArray('SHIFT', parts) != -1,
  2523. type : s.type || 'keydown',
  2524. callback : s.callback,
  2525. description : s.description,
  2526. pattern : pattern
  2527. };
  2528. }
  2529. }
  2530. }
  2531. return this;
  2532. };
  2533. /**
  2534. * Registered shortcuts
  2535. *
  2536. * @type Object
  2537. **/
  2538. this.shortcuts = function() {
  2539. var ret = [];
  2540. $.each(shortcuts, function(i, s) {
  2541. ret.push([s.pattern, self.i18n(s.description)]);
  2542. });
  2543. return ret;
  2544. };
  2545. /**
  2546. * Get/set clipboard content.
  2547. * Return new clipboard content.
  2548. *
  2549. * @example
  2550. * this.clipboard([]) - clean clipboard
  2551. * this.clipboard([{...}, {...}], true) - put 2 files in clipboard and mark it as cutted
  2552. *
  2553. * @param Array new files hashes
  2554. * @param Boolean cut files?
  2555. * @return Array
  2556. */
  2557. this.clipboard = function(hashes, cut) {
  2558. var map = function() { return $.map(clipboard, function(f) { return f.hash; }); };
  2559. if (hashes !== void(0)) {
  2560. clipboard.length && this.trigger('unlockfiles', {files : map()});
  2561. remember = {};
  2562. clipboard = $.map(hashes||[], function(hash) {
  2563. var file = files[hash];
  2564. if (file) {
  2565. remember[hash] = true;
  2566. return {
  2567. hash : hash,
  2568. phash : file.phash,
  2569. name : file.name,
  2570. mime : file.mime,
  2571. read : file.read,
  2572. locked : file.locked,
  2573. cut : !!cut
  2574. };
  2575. }
  2576. return null;
  2577. });
  2578. this.trigger('changeclipboard', {clipboard : clipboard.slice(0, clipboard.length)});
  2579. cut && this.trigger('lockfiles', {files : map()});
  2580. }
  2581. // return copy of clipboard instead of refrence
  2582. return clipboard.slice(0, clipboard.length);
  2583. };
  2584. /**
  2585. * Return true if command enabled
  2586. *
  2587. * @param String command name
  2588. * @param String|void hash for check of own volume's disabled cmds
  2589. * @return Boolean
  2590. */
  2591. this.isCommandEnabled = function(name, dstHash) {
  2592. var disabled, cmd,
  2593. cvid = self.cwd().volumeid || '';
  2594. // In serach results use selected item hash to check
  2595. if (!dstHash && self.searchStatus.state > 1 && self.selected().length) {
  2596. dstHash = self.selected()[0];
  2597. }
  2598. if (dstHash && (! cvid || dstHash.indexOf(cvid) !== 0)) {
  2599. disabled = self.option('disabledFlip', dstHash);
  2600. //if (! disabled) {
  2601. // disabled = {};
  2602. //}
  2603. } else {
  2604. disabled = cwdOptions.disabledFlip/* || {}*/;
  2605. }
  2606. cmd = this._commands[name];
  2607. return cmd ? (cmd.alwaysEnabled || !disabled[name]) : false;
  2608. };
  2609. /**
  2610. * Exec command and return result;
  2611. *
  2612. * @param String command name
  2613. * @param String|Array usualy files hashes
  2614. * @param String|Array command options
  2615. * @param String|void hash for enabled check of own volume's disabled cmds
  2616. * @return $.Deferred
  2617. */
  2618. this.exec = function(cmd, files, opts, dstHash) {
  2619. var dfrd, resType;
  2620. if (cmd === 'open') {
  2621. if (this.searchStatus.state || this.searchStatus.ininc) {
  2622. this.trigger('searchend', { noupdate: true });
  2623. }
  2624. this.autoSync('stop');
  2625. }
  2626. if (!dstHash && files) {
  2627. if ($.isArray(files)) {
  2628. if (files.length) {
  2629. dstHash = files[0];
  2630. }
  2631. } else {
  2632. dstHash = files;
  2633. }
  2634. }
  2635. dfrd = this._commands[cmd] && this.isCommandEnabled(cmd, dstHash)
  2636. ? this._commands[cmd].exec(files, opts)
  2637. : $.Deferred().reject('No such command');
  2638. resType = typeof dfrd;
  2639. if (!(resType === 'object' && dfrd.promise)) {
  2640. self.debug('warning', '"cmd.exec()" should be returned "$.Deferred" but cmd "' + cmd + '" returned "' + resType + '"');
  2641. dfrd = $.Deferred().resolve();
  2642. }
  2643. this.trigger('exec', { dfrd : dfrd, cmd : cmd, files : files, opts : opts, dstHash : dstHash });
  2644. return dfrd;
  2645. };
  2646. /**
  2647. * Create and return dialog.
  2648. *
  2649. * @param String|DOMElement dialog content
  2650. * @param Object dialog options
  2651. * @return jQuery
  2652. */
  2653. this.dialog = function(content, options) {
  2654. var dialog = $('<div/>').append(content).appendTo(node).elfinderdialog(options, this),
  2655. dnode = dialog.closest('.ui-dialog'),
  2656. resize = function(){
  2657. ! dialog.data('draged') && dialog.is(':visible') && dialog.elfinderdialog('posInit');
  2658. };
  2659. if (dnode.length) {
  2660. self.bind('resize', resize);
  2661. dnode.on('remove', function() {
  2662. self.unbind('resize', resize);
  2663. });
  2664. }
  2665. return dialog;
  2666. };
  2667. /**
  2668. * Create and return toast.
  2669. *
  2670. * @param Object toast options - see ui/toast.js
  2671. * @return jQuery
  2672. */
  2673. this.toast = function(options) {
  2674. return $('<div class="ui-front"/>').appendTo(this.ui.toast).elfindertoast(options || {}, this);
  2675. };
  2676. /**
  2677. * Return UI widget or node
  2678. *
  2679. * @param String ui name
  2680. * @return jQuery
  2681. */
  2682. this.getUI = function(ui) {
  2683. return this.ui[ui] || (ui? $() : node);
  2684. };
  2685. /**
  2686. * Return elFinder.command instance or instances array
  2687. *
  2688. * @param String command name
  2689. * @return Object | Array
  2690. */
  2691. this.getCommand = function(name) {
  2692. return name === void(0) ? this._commands : this._commands[name];
  2693. };
  2694. /**
  2695. * Resize elfinder node
  2696. *
  2697. * @param String|Number width
  2698. * @param String|Number height
  2699. * @return void
  2700. */
  2701. this.resize = function(w, h) {
  2702. var getMargin = function() {
  2703. var m = node.outerHeight(true) - node.innerHeight(),
  2704. p = node;
  2705. while(p.get(0) !== heightBase.get(0)) {
  2706. p = p.parent();
  2707. m += p.outerHeight(true) - p.innerHeight();
  2708. if (! p.parent().length) {
  2709. // reached the document
  2710. break;
  2711. }
  2712. }
  2713. return m;
  2714. },
  2715. fit = ! node.hasClass('ui-resizable'),
  2716. prv = node.data('resizeSize') || {w: 0, h: 0},
  2717. mt, size = {};
  2718. if (heightBase && heightBase.data('resizeTm')) {
  2719. clearTimeout(heightBase.data('resizeTm'));
  2720. }
  2721. if (typeof h === 'string') {
  2722. if (mt = h.match(/^([0-9.]+)%$/)) {
  2723. // setup heightBase
  2724. if (! heightBase || ! heightBase.length) {
  2725. heightBase = $(window);
  2726. }
  2727. if (! heightBase.data('marginToMyNode')) {
  2728. heightBase.data('marginToMyNode', getMargin());
  2729. }
  2730. if (! heightBase.data('fitToBaseFunc')) {
  2731. heightBase.data('fitToBaseFunc', function(e) {
  2732. var tm = heightBase.data('resizeTm');
  2733. e.preventDefault();
  2734. e.stopPropagation();
  2735. tm && clearTimeout(tm);
  2736. if (! node.hasClass('elfinder-fullscreen') && (!self.UA.Mobile || heightBase.data('rotated') !== self.UA.Rotated)) {
  2737. heightBase.data('rotated', self.UA.Rotated);
  2738. heightBase.data('resizeTm', setTimeout(function() {
  2739. self.restoreSize();
  2740. }, 50));
  2741. }
  2742. });
  2743. }
  2744. if (typeof heightBase.data('rotated') === 'undefined') {
  2745. heightBase.data('rotated', self.UA.Rotated);
  2746. }
  2747. h = heightBase.height() * (mt[1] / 100) - heightBase.data('marginToMyNode');
  2748. heightBase.off('resize.' + self.namespace, heightBase.data('fitToBaseFunc'));
  2749. fit && heightBase.on('resize.' + self.namespace, heightBase.data('fitToBaseFunc'));
  2750. }
  2751. }
  2752. node.css({ width : w, height : parseInt(h) });
  2753. size.w = Math.round(node.width());
  2754. size.h = Math.round(node.height());
  2755. node.data('resizeSize', size);
  2756. if (size.w !== prv.w || size.h !== prv.h) {
  2757. node.trigger('resize');
  2758. this.trigger('resize', {width : size.w, height : size.h});
  2759. }
  2760. };
  2761. /**
  2762. * Restore elfinder node size
  2763. *
  2764. * @return elFinder
  2765. */
  2766. this.restoreSize = function() {
  2767. this.resize(width, height);
  2768. };
  2769. this.show = function() {
  2770. node.show();
  2771. this.enable().trigger('show');
  2772. };
  2773. this.hide = function() {
  2774. if (this.options.enableAlways) {
  2775. prevEnabled = enabled;
  2776. enabled = false;
  2777. }
  2778. this.disable().trigger('hide');
  2779. node.hide();
  2780. };
  2781. /**
  2782. * Lazy execution function
  2783. *
  2784. * @param Object function
  2785. * @param Number delay
  2786. * @param Object options
  2787. * @return Object jQuery.Deferred
  2788. */
  2789. this.lazy = function(func, delay, opts) {
  2790. var busy = function(state) {
  2791. var cnt = node.data('lazycnt'),
  2792. repaint;
  2793. if (state) {
  2794. repaint = node.data('lazyrepaint')? false : opts.repaint;
  2795. if (! cnt) {
  2796. node.data('lazycnt', 1)
  2797. .addClass('elfinder-processing');
  2798. } else {
  2799. node.data('lazycnt', ++cnt);
  2800. }
  2801. if (repaint) {
  2802. node.data('lazyrepaint', true).css('display'); // force repaint
  2803. }
  2804. } else {
  2805. if (cnt && cnt > 1) {
  2806. node.data('lazycnt', --cnt);
  2807. } else {
  2808. repaint = node.data('lazyrepaint');
  2809. node.data('lazycnt', 0)
  2810. .removeData('lazyrepaint')
  2811. .removeClass('elfinder-processing');
  2812. repaint && node.css('display'); // force repaint;
  2813. self.trigger('lazydone');
  2814. }
  2815. }
  2816. },
  2817. dfd = $.Deferred();
  2818. delay = delay || 0;
  2819. opts = opts || {};
  2820. busy(true);
  2821. setTimeout(function() {
  2822. dfd.resolve(func.call(dfd));
  2823. busy(false);
  2824. }, delay);
  2825. return dfd;
  2826. };
  2827. /**
  2828. * Destroy this elFinder instance
  2829. *
  2830. * @return void
  2831. **/
  2832. this.destroy = function() {
  2833. if (node && node[0].elfinder) {
  2834. node.hasClass('elfinder-fullscreen') && self.toggleFullscreen(node);
  2835. this.options.syncStart = false;
  2836. this.autoSync('forcestop');
  2837. this.trigger('destroy').disable();
  2838. clipboard = [];
  2839. selected = [];
  2840. listeners = {};
  2841. shortcuts = {};
  2842. $(window).off('.' + namespace);
  2843. $(document).off('.' + namespace);
  2844. self.trigger = function(){};
  2845. $(beeper).remove();
  2846. node.off()
  2847. .removeData()
  2848. .empty()
  2849. .append(prevContent.contents())
  2850. .attr('class', prevContent.attr('class'))
  2851. .attr('style', prevContent.attr('style'));
  2852. delete node[0].elfinder;
  2853. // restore kept events
  2854. $.each(prevEvents, function(n, arr) {
  2855. $.each(arr, function(i, o) {
  2856. node.on(o.type + (o.namespace? '.'+o.namespace : ''), o.selector, o.handler);
  2857. });
  2858. });
  2859. }
  2860. };
  2861. /**
  2862. * Start or stop auto sync
  2863. *
  2864. * @param String|Bool stop
  2865. * @return void
  2866. */
  2867. this.autoSync = function(mode) {
  2868. var sync;
  2869. if (self.options.sync >= 1000) {
  2870. if (syncInterval) {
  2871. clearTimeout(syncInterval);
  2872. syncInterval = null;
  2873. self.trigger('autosync', {action : 'stop'});
  2874. }
  2875. if (mode === 'stop') {
  2876. ++autoSyncStop;
  2877. } else {
  2878. autoSyncStop = Math.max(0, --autoSyncStop);
  2879. }
  2880. if (autoSyncStop || mode === 'forcestop' || ! self.options.syncStart) {
  2881. return;
  2882. }
  2883. // run interval sync
  2884. sync = function(start){
  2885. var timeout;
  2886. if (cwdOptions.syncMinMs && (start || syncInterval)) {
  2887. start && self.trigger('autosync', {action : 'start'});
  2888. timeout = Math.max(self.options.sync, cwdOptions.syncMinMs);
  2889. syncInterval && clearTimeout(syncInterval);
  2890. syncInterval = setTimeout(function() {
  2891. var dosync = true, hash = cwd, cts;
  2892. if (cwdOptions.syncChkAsTs && files[hash] && (cts = files[hash].ts)) {
  2893. self.request({
  2894. data : {cmd : 'info', targets : [hash], compare : cts, reload : 1},
  2895. preventDefault : true
  2896. })
  2897. .done(function(data){
  2898. var ts;
  2899. dosync = true;
  2900. if (data.compare) {
  2901. ts = data.compare;
  2902. if (ts == cts) {
  2903. dosync = false;
  2904. }
  2905. }
  2906. if (dosync) {
  2907. self.sync(hash).always(function(){
  2908. if (ts) {
  2909. // update ts for cache clear etc.
  2910. files[hash].ts = ts;
  2911. }
  2912. sync();
  2913. });
  2914. } else {
  2915. sync();
  2916. }
  2917. })
  2918. .fail(function(error, xhr){
  2919. if (error && xhr.status != 0) {
  2920. self.error(error);
  2921. if ($.inArray('errOpen', error) !== -1) {
  2922. self.request({
  2923. data : {cmd : 'open', target : (self.lastDir('') || self.root()), tree : 1, init : 1},
  2924. notify : {type : 'open', cnt : 1, hideCnt : true}
  2925. });
  2926. }
  2927. } else {
  2928. syncInterval = setTimeout(function() {
  2929. sync();
  2930. }, timeout);
  2931. }
  2932. });
  2933. } else {
  2934. self.sync(cwd, true).always(function(){
  2935. sync();
  2936. });
  2937. }
  2938. }, timeout);
  2939. }
  2940. };
  2941. sync(true);
  2942. }
  2943. };
  2944. /**
  2945. * Return bool is inside work zone of specific point
  2946. *
  2947. * @param Number event.pageX
  2948. * @param Number event.pageY
  2949. * @return Bool
  2950. */
  2951. this.insideWorkzone = function(x, y, margin) {
  2952. var rectangle = this.getUI('workzone').data('rectangle');
  2953. margin = margin || 1;
  2954. if (x < rectangle.left + margin
  2955. || x > rectangle.left + rectangle.width + margin
  2956. || y < rectangle.top + margin
  2957. || y > rectangle.top + rectangle.height + margin) {
  2958. return false;
  2959. }
  2960. return true;
  2961. };
  2962. /**
  2963. * Target ui node move to last of children of elFinder node fot to show front
  2964. *
  2965. * @param Object target Target jQuery node object
  2966. */
  2967. this.toFront = function(target) {
  2968. var nodes = node.children('.ui-front').removeClass('elfinder-frontmost'),
  2969. lastnode = nodes.last();
  2970. nodes.css('z-index', '');
  2971. $(target).addClass('ui-front elfinder-frontmost').css('z-index', lastnode.css('z-index') + 1);
  2972. };
  2973. /**
  2974. * Return css object for maximize
  2975. *
  2976. * @return Object
  2977. */
  2978. this.getMaximizeCss = function() {
  2979. return {
  2980. width : '100%',
  2981. height : '100%',
  2982. margin : 0,
  2983. padding : 0,
  2984. top : 0,
  2985. left : 0,
  2986. display : 'block',
  2987. position: 'fixed',
  2988. zIndex : Math.max(self.zIndex? (self.zIndex + 1) : 0 , 1000),
  2989. maxWidth : '',
  2990. maxHeight: ''
  2991. };
  2992. };
  2993. // Closure for togglefullscreen
  2994. (function() {
  2995. // check is in iframe
  2996. if (inFrame && self.UA.Fullscreen) {
  2997. self.UA.Fullscreen = false;
  2998. if (parentIframe && typeof parentIframe.attr('allowfullscreen') !== 'undefined') {
  2999. self.UA.Fullscreen = true;
  3000. }
  3001. }
  3002. var orgStyle, bodyOvf, resizeTm, fullElm, exitFull, toFull,
  3003. cls = 'elfinder-fullscreen',
  3004. clsN = 'elfinder-fullscreen-native',
  3005. checkDialog = function() {
  3006. var t = 0,
  3007. l = 0;
  3008. $.each(node.children('.ui-dialog,.ui-draggable'), function(i, d) {
  3009. var $d = $(d),
  3010. pos = $d.position();
  3011. if (pos.top < 0) {
  3012. $d.css('top', t);
  3013. t += 20;
  3014. }
  3015. if (pos.left < 0) {
  3016. $d.css('left', l);
  3017. l += 20;
  3018. }
  3019. });
  3020. },
  3021. funcObj = self.UA.Fullscreen? {
  3022. // native full screen mode
  3023. fullElm: function() {
  3024. return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement || null;
  3025. },
  3026. exitFull: function() {
  3027. if (document.exitFullscreen) {
  3028. return document.exitFullscreen();
  3029. } else if (document.webkitExitFullscreen) {
  3030. return document.webkitExitFullscreen();
  3031. } else if (document.mozCancelFullScreen) {
  3032. return document.mozCancelFullScreen();
  3033. } else if (document.msExitFullscreen) {
  3034. return document.msExitFullscreen();
  3035. }
  3036. },
  3037. toFull: function(elem) {
  3038. if (elem.requestFullscreen) {
  3039. return elem.requestFullscreen();
  3040. } else if (elem.webkitRequestFullscreen) {
  3041. return elem.webkitRequestFullscreen();
  3042. } else if (elem.mozRequestFullScreen) {
  3043. return elem.mozRequestFullScreen();
  3044. } else if (elem.msRequestFullscreen) {
  3045. return elem.msRequestFullscreen();
  3046. }
  3047. return false;
  3048. }
  3049. } : {
  3050. // node element maximize mode
  3051. fullElm: function() {
  3052. var full;
  3053. if (node.hasClass(cls)) {
  3054. return node.get(0);
  3055. } else {
  3056. full = node.find('.' + cls);
  3057. if (full.length) {
  3058. return full.get(0);
  3059. }
  3060. }
  3061. return null;
  3062. },
  3063. exitFull: function() {
  3064. var elm;
  3065. $(window).off('resize.' + namespace, resize);
  3066. if (bodyOvf !== void(0)) {
  3067. $('body').css('overflow', bodyOvf);
  3068. }
  3069. bodyOvf = void(0);
  3070. if (orgStyle) {
  3071. elm = orgStyle.elm;
  3072. restoreStyle(elm);
  3073. $(elm).trigger('resize', {fullscreen: 'off'});
  3074. }
  3075. $(window).trigger('resize');
  3076. },
  3077. toFull: function(elem) {
  3078. bodyOvf = $('body').css('overflow') || '';
  3079. $('body').css('overflow', 'hidden');
  3080. $(elem).css(self.getMaximizeCss())
  3081. .addClass(cls)
  3082. .trigger('resize', {fullscreen: 'on'});
  3083. checkDialog();
  3084. $(window).on('resize.' + namespace, resize).trigger('resize');
  3085. return true;
  3086. }
  3087. },
  3088. restoreStyle = function(elem) {
  3089. if (orgStyle && orgStyle.elm == elem) {
  3090. $(elem).removeClass(cls + ' ' + clsN).attr('style', orgStyle.style);
  3091. orgStyle = null;
  3092. }
  3093. },
  3094. resize = function(e) {
  3095. var elm;
  3096. if (e.target === window) {
  3097. resizeTm && clearTimeout(resizeTm);
  3098. resizeTm = setTimeout(function() {
  3099. if (elm = funcObj.fullElm()) {
  3100. $(elm).trigger('resize', {fullscreen: 'on'});
  3101. }
  3102. }, 100);
  3103. }
  3104. };
  3105. $(document).on('fullscreenchange.' + namespace + ' webkitfullscreenchange.' + namespace + ' mozfullscreenchange.' + namespace + ' MSFullscreenChange.' + namespace, function(e){
  3106. if (self.UA.Fullscreen) {
  3107. var elm = funcObj.fullElm(),
  3108. win = $(window);
  3109. resizeTm && clearTimeout(resizeTm);
  3110. if (elm === null) {
  3111. win.off('resize.' + namespace, resize);
  3112. if (orgStyle) {
  3113. elm = orgStyle.elm;
  3114. restoreStyle(elm);
  3115. $(elm).trigger('resize', {fullscreen: 'off'});
  3116. }
  3117. } else {
  3118. $(elm).addClass(cls + ' ' + clsN)
  3119. .attr('style', 'width:100%; height:100%; margin:0; padding:0;')
  3120. .trigger('resize', {fullscreen: 'on'});
  3121. win.on('resize.' + namespace, resize);
  3122. checkDialog();
  3123. }
  3124. win.trigger('resize');
  3125. }
  3126. });
  3127. /**
  3128. * Toggle Full Scrren Mode
  3129. *
  3130. * @param Object target
  3131. * @param Bool full
  3132. * @return Object | Null DOM node object of current full scrren
  3133. */
  3134. self.toggleFullscreen = function(target, full) {
  3135. var elm = $(target).get(0),
  3136. curElm = null;
  3137. curElm = funcObj.fullElm();
  3138. if (curElm) {
  3139. if (curElm == elm) {
  3140. if (full === true) {
  3141. return curElm;
  3142. }
  3143. } else {
  3144. if (full === false) {
  3145. return curElm;
  3146. }
  3147. }
  3148. funcObj.exitFull();
  3149. return null;
  3150. } else {
  3151. if (full === false) {
  3152. return null;
  3153. }
  3154. }
  3155. orgStyle = {elm: elm, style: $(elm).attr('style')};
  3156. if (funcObj.toFull(elm) !== false) {
  3157. return elm;
  3158. } else {
  3159. orgStyle = null;
  3160. return null;
  3161. }
  3162. };
  3163. })();
  3164. // Closure for toggleMaximize
  3165. (function(){
  3166. var cls = 'elfinder-maximized',
  3167. resizeTm,
  3168. resize = function(e) {
  3169. if (e.target === window && e.data && e.data.elm) {
  3170. var elm = e.data.elm;
  3171. resizeTm && clearTimeout(resizeTm);
  3172. resizeTm = setTimeout(function() {
  3173. elm.trigger('resize', {maximize: 'on'});
  3174. }, 100);
  3175. }
  3176. },
  3177. exitMax = function(elm) {
  3178. $(window).off('resize.' + namespace, resize);
  3179. $('body').css('overflow', elm.data('bodyOvf'));
  3180. elm.removeClass(cls)
  3181. .attr('style', elm.data('orgStyle'))
  3182. .removeData('bodyOvf')
  3183. .removeData('orgStyle');
  3184. elm.trigger('resize', {maximize: 'off'});
  3185. },
  3186. toMax = function(elm) {
  3187. elm.data('bodyOvf', $('body').css('overflow') || '')
  3188. .data('orgStyle', elm.attr('style'))
  3189. .addClass(cls)
  3190. .css(self.getMaximizeCss());
  3191. $('body').css('overflow', 'hidden');
  3192. $(window).on('resize.' + namespace, {elm: elm}, resize);
  3193. elm.trigger('resize', {maximize: 'on'});
  3194. };
  3195. /**
  3196. * Toggle Maximize target node
  3197. *
  3198. * @param Object target
  3199. * @param Bool max
  3200. * @return void
  3201. */
  3202. self.toggleMaximize = function(target, max) {
  3203. var elm = $(target),
  3204. maximized = elm.hasClass(cls);
  3205. if (maximized) {
  3206. if (max === true) {
  3207. return;
  3208. }
  3209. exitMax(elm);
  3210. } else {
  3211. if (max === false) {
  3212. return;
  3213. }
  3214. toMax(elm);
  3215. }
  3216. };
  3217. })();
  3218. /************* init stuffs ****************/
  3219. Object.assign($.ui.keyCode, {
  3220. 'F1' : 112,
  3221. 'F2' : 113,
  3222. 'F3' : 114,
  3223. 'F4' : 115,
  3224. 'F5' : 116,
  3225. 'F6' : 117,
  3226. 'F7' : 118,
  3227. 'F8' : 119,
  3228. 'F9' : 120,
  3229. 'F10' : 121,
  3230. 'F11' : 122,
  3231. 'F12' : 123,
  3232. 'DIG0' : 48,
  3233. 'DIG1' : 49,
  3234. 'DIG2' : 50,
  3235. 'DIG3' : 51,
  3236. 'DIG4' : 52,
  3237. 'DIG5' : 53,
  3238. 'DIG6' : 54,
  3239. 'DIG7' : 55,
  3240. 'DIG8' : 56,
  3241. 'DIG9' : 57,
  3242. 'NUM0' : 96,
  3243. 'NUM1' : 97,
  3244. 'NUM2' : 98,
  3245. 'NUM3' : 99,
  3246. 'NUM4' : 100,
  3247. 'NUM5' : 101,
  3248. 'NUM6' : 102,
  3249. 'NUM7' : 103,
  3250. 'NUM8' : 104,
  3251. 'NUM9' : 105,
  3252. 'CONTEXTMENU' : 93
  3253. });
  3254. this.dragUpload = false;
  3255. this.xhrUpload = (typeof XMLHttpRequestUpload != 'undefined' || typeof XMLHttpRequestEventTarget != 'undefined') && typeof File != 'undefined' && typeof FormData != 'undefined';
  3256. // configure transport object
  3257. this.transport = {};
  3258. if (typeof(this.options.transport) == 'object') {
  3259. this.transport = this.options.transport;
  3260. if (typeof(this.transport.init) == 'function') {
  3261. this.transport.init(this);
  3262. }
  3263. }
  3264. if (typeof(this.transport.send) != 'function') {
  3265. this.transport.send = function(opts) { return $.ajax(opts); };
  3266. }
  3267. if (this.transport.upload == 'iframe') {
  3268. this.transport.upload = $.proxy(this.uploads.iframe, this);
  3269. } else if (typeof(this.transport.upload) == 'function') {
  3270. this.dragUpload = !!this.options.dragUploadAllow;
  3271. } else if (this.xhrUpload && !!this.options.dragUploadAllow) {
  3272. this.transport.upload = $.proxy(this.uploads.xhr, this);
  3273. this.dragUpload = true;
  3274. } else {
  3275. this.transport.upload = $.proxy(this.uploads.iframe, this);
  3276. }
  3277. /**
  3278. * Decoding 'raw' string converted to unicode
  3279. *
  3280. * @param String str
  3281. * @return String
  3282. */
  3283. this.decodeRawString = function(str) {
  3284. var charCodes = function(str) {
  3285. var i, len, arr;
  3286. for (i=0,len=str.length,arr=[]; i<len; i++) {
  3287. arr.push(str.charCodeAt(i));
  3288. }
  3289. return arr;
  3290. },
  3291. scalarValues = function(arr) {
  3292. var scalars = [], i, len, c;
  3293. if (typeof arr === 'string') {arr = charCodes(arr);}
  3294. for (i=0,len=arr.length; c=arr[i],i<len; i++) {
  3295. if (c >= 0xd800 && c <= 0xdbff) {
  3296. scalars.push((c & 1023) + 64 << 10 | arr[++i] & 1023);
  3297. } else {
  3298. scalars.push(c);
  3299. }
  3300. }
  3301. return scalars;
  3302. },
  3303. decodeUTF8 = function(arr) {
  3304. var i, len, c, str, char = String.fromCharCode;
  3305. for (i=0,len=arr.length,str=""; c=arr[i],i<len; i++) {
  3306. if (c <= 0x7f) {
  3307. str += char(c);
  3308. } else if (c <= 0xdf && c >= 0xc2) {
  3309. str += char((c&31)<<6 | arr[++i]&63);
  3310. } else if (c <= 0xef && c >= 0xe0) {
  3311. str += char((c&15)<<12 | (arr[++i]&63)<<6 | arr[++i]&63);
  3312. } else if (c <= 0xf7 && c >= 0xf0) {
  3313. str += char(
  3314. 0xd800 | ((c&7)<<8 | (arr[++i]&63)<<2 | arr[++i]>>>4&3) - 64,
  3315. 0xdc00 | (arr[i++]&15)<<6 | arr[i]&63
  3316. );
  3317. } else {
  3318. str += char(0xfffd);
  3319. }
  3320. }
  3321. return str;
  3322. };
  3323. return decodeUTF8(scalarValues(str));
  3324. };
  3325. /**
  3326. * Gets target file contents by file.hash
  3327. *
  3328. * @param String hash The hash
  3329. * @param String responseType 'blob' or 'arraybuffer' (default)
  3330. * @return arraybuffer|blob The contents.
  3331. */
  3332. this.getContents = function(hash, responseType) {
  3333. var self = this,
  3334. dfd = $.Deferred(),
  3335. type = responseType || 'arraybuffer',
  3336. url, req;
  3337. dfd.fail(function() {
  3338. req && req.state() === 'pending' && req.reject();
  3339. });
  3340. url = self.openUrl(hash);
  3341. if (!self.isSameOrigin(url)) {
  3342. url = self.openUrl(hash, true);
  3343. }
  3344. req = self.request({
  3345. data : {cmd : 'get'},
  3346. options : {
  3347. url: url,
  3348. type: 'get',
  3349. cache : true,
  3350. dataType : 'binary',
  3351. responseType : type,
  3352. processData: false
  3353. }
  3354. })
  3355. .fail(function() {
  3356. dfd.reject();
  3357. })
  3358. .done(function(data) {
  3359. dfd.resolve(data);
  3360. });
  3361. return dfd;
  3362. };
  3363. /**
  3364. * Supported check hash algorisms
  3365. *
  3366. * @type Array
  3367. */
  3368. self.hashCheckers = [];
  3369. /**
  3370. * Closure of getContentsHashes()
  3371. */
  3372. (function(self) {
  3373. var hashLibs = {
  3374. check : true
  3375. },
  3376. md5Calc = function(arr) {
  3377. var spark = new hashLibs.SparkMD5.ArrayBuffer(),
  3378. job;
  3379. job = self.asyncJob(function(buf) {
  3380. spark.append(buf);
  3381. }, arr).done(function() {
  3382. job._md5 = spark.end();
  3383. });
  3384. return job;
  3385. },
  3386. shaCalc = function(arr, length) {
  3387. var sha, job;
  3388. try {
  3389. sha = new hashLibs.jsSHA('SHA' + (length.substr(0, 1) === '3'? length : ('-' + length)), 'ARRAYBUFFER');
  3390. job = self.asyncJob(function(buf) {
  3391. sha.update(buf);
  3392. }, arr).done(function() {
  3393. job._sha = sha.getHash('HEX');
  3394. });
  3395. } catch(e) {
  3396. job = $.Deferred.reject();
  3397. }
  3398. return job;
  3399. };
  3400. // make fm.hashCheckers
  3401. if (self.options.cdns.sparkmd5) {
  3402. self.hashCheckers.push('md5');
  3403. }
  3404. if (self.options.cdns.jssha) {
  3405. self.hashCheckers = self.hashCheckers.concat(['sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'shake128', 'shake256']);
  3406. }
  3407. /**
  3408. * Gets the contents hashes.
  3409. *
  3410. * @param String target target file.hash
  3411. * @param Object needHashes need hash lib names
  3412. * @return Object hashes with lib name as key
  3413. */
  3414. self.getContentsHashes = function(target, needHashes) {
  3415. var dfd = $.Deferred(),
  3416. needs = self.arrayFlip(needHashes || ['md5'], true),
  3417. libs = [],
  3418. jobs = [],
  3419. res = {},
  3420. req;
  3421. dfd.fail(function() {
  3422. req && req.reject();
  3423. });
  3424. if (hashLibs.check) {
  3425. delete hashLibs.check;
  3426. // load SparkMD5
  3427. var libsmd5 = $.Deferred();
  3428. if (window.ArrayBuffer && self.options.cdns.sparkmd5) {
  3429. libs.push(libsmd5);
  3430. self.loadScript([self.options.cdns.sparkmd5],
  3431. function(res) {
  3432. var SparkMD5 = res || window.SparkMD5;
  3433. window.SparkMD5 && delete window.SparkMD5;
  3434. libsmd5.resolve();
  3435. if (SparkMD5) {
  3436. hashLibs.SparkMD5 = SparkMD5;
  3437. }
  3438. },
  3439. {
  3440. tryRequire: true,
  3441. error: function() {
  3442. libsmd5.reject();
  3443. }
  3444. }
  3445. );
  3446. }
  3447. // load jsSha
  3448. var libssha = $.Deferred();
  3449. if (window.ArrayBuffer && self.options.cdns.jssha) {
  3450. libs.push(libssha);
  3451. self.loadScript([self.options.cdns.jssha],
  3452. function(res) {
  3453. var jsSHA = res || window.jsSHA;
  3454. window.jsSHA && delete window.jsSHA;
  3455. libssha.resolve();
  3456. if (jsSHA) {
  3457. hashLibs.jsSHA = jsSHA;
  3458. }
  3459. },
  3460. {
  3461. tryRequire: true,
  3462. error: function() {
  3463. libssha.reject();
  3464. }
  3465. }
  3466. );
  3467. }
  3468. }
  3469. $.when.apply(null, libs).always(function() {
  3470. if (Object.keys(hashLibs).length) {
  3471. req = self.getContents(target).done(function(arrayBuffer) {
  3472. var arr = (arrayBuffer instanceof ArrayBuffer && arrayBuffer.byteLength > 0)? self.sliceArrayBuffer(arrayBuffer, 1048576) : false,
  3473. i;
  3474. if (needs.md5 && hashLibs.SparkMD5) {
  3475. jobs.push(function() {
  3476. var job = md5Calc(arr).done(function() {
  3477. var f;
  3478. res.md5 = job._md5;
  3479. if (f = self.file(target)) {
  3480. f.md5 = job._md5;
  3481. }
  3482. dfd.notify(res);
  3483. });
  3484. dfd.fail(function() {
  3485. job.reject();
  3486. });
  3487. return job;
  3488. });
  3489. }
  3490. if (hashLibs.jsSHA) {
  3491. $.each(['1', '224', '256', '384', '512', '3-224', '3-256', '3-384', '3-512', 'ke128', 'ke256'], function(i, v) {
  3492. if (needs['sha' + v]) {
  3493. jobs.push(function() {
  3494. var job = shaCalc(arr, v).done(function() {
  3495. var f;
  3496. res['sha' + v] = job._sha;
  3497. if (f = self.file(target)) {
  3498. f['sha' + v] = job._sha;
  3499. }
  3500. dfd.notify(res);
  3501. });
  3502. return job;
  3503. });
  3504. }
  3505. });
  3506. }
  3507. if (jobs.length) {
  3508. self.sequence(jobs).always(function() {
  3509. dfd.resolve(res);
  3510. });
  3511. } else {
  3512. dfd.reject();
  3513. }
  3514. }).fail(function() {
  3515. dfd.reject();
  3516. });
  3517. } else {
  3518. dfd.reject();
  3519. }
  3520. });
  3521. return dfd;
  3522. };
  3523. })(this);
  3524. /**
  3525. * Alias for this.trigger('error', {error : 'message'})
  3526. *
  3527. * @param String error message
  3528. * @return elFinder
  3529. **/
  3530. this.error = function() {
  3531. var arg = arguments[0],
  3532. opts = arguments[1] || null;
  3533. return arguments.length == 1 && typeof(arg) == 'function'
  3534. ? self.bind('error', arg)
  3535. : (arg === true? this : self.trigger('error', {error : arg, opts : opts}));
  3536. };
  3537. // create bind/trigger aliases for build-in events
  3538. $.each(events, function(i, name) {
  3539. self[name] = function() {
  3540. var arg = arguments[0];
  3541. return arguments.length == 1 && typeof(arg) == 'function'
  3542. ? self.bind(name, arg)
  3543. : self.trigger(name, $.isPlainObject(arg) ? arg : {});
  3544. };
  3545. });
  3546. // bind core event handlers
  3547. this
  3548. .enable(function() {
  3549. if (!enabled && self.api && self.visible() && self.ui.overlay.is(':hidden') && ! node.children('.elfinder-dialog.' + self.res('class', 'editing') + ':visible').length) {
  3550. enabled = true;
  3551. document.activeElement && document.activeElement.blur();
  3552. node.removeClass('elfinder-disabled');
  3553. }
  3554. })
  3555. .disable(function() {
  3556. prevEnabled = enabled;
  3557. enabled = false;
  3558. node.addClass('elfinder-disabled');
  3559. })
  3560. .open(function() {
  3561. selected = [];
  3562. })
  3563. .select(function(e) {
  3564. var cnt = 0,
  3565. unselects = [];
  3566. selected = $.grep(e.data.selected || e.data.value|| [], function(hash) {
  3567. if (unselects.length || (self.maxTargets && ++cnt > self.maxTargets)) {
  3568. unselects.push(hash);
  3569. return false;
  3570. } else {
  3571. return files[hash] ? true : false;
  3572. }
  3573. });
  3574. if (unselects.length) {
  3575. self.trigger('unselectfiles', {files: unselects, inselect: true});
  3576. self.toast({mode: 'warning', msg: self.i18n(['errMaxTargets', self.maxTargets])});
  3577. }
  3578. })
  3579. .error(function(e) {
  3580. var opts = {
  3581. cssClass : 'elfinder-dialog-error',
  3582. title : self.i18n(self.i18n('error')),
  3583. resizable : false,
  3584. destroyOnClose : true,
  3585. buttons : {}
  3586. };
  3587. opts.buttons[self.i18n(self.i18n('btnClose'))] = function() { $(this).elfinderdialog('close'); };
  3588. if (e.data.opts && $.isPlainObject(e.data.opts)) {
  3589. Object.assign(opts, e.data.opts);
  3590. }
  3591. self.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-error"/>'+self.i18n(e.data.error), opts);
  3592. })
  3593. .bind('tmb', function(e) {
  3594. $.each(e.data.images||[], function(hash, tmb) {
  3595. if (files[hash]) {
  3596. files[hash].tmb = tmb;
  3597. }
  3598. });
  3599. })
  3600. .bind('searchstart', function(e) {
  3601. Object.assign(self.searchStatus, e.data);
  3602. self.searchStatus.state = 1;
  3603. })
  3604. .bind('search', function(e) {
  3605. self.searchStatus.state = 2;
  3606. })
  3607. .bind('searchend', function() {
  3608. self.searchStatus.state = 0;
  3609. self.searchStatus.ininc = false;
  3610. self.searchStatus.mixed = false;
  3611. })
  3612. ;
  3613. // We listen and emit a sound on delete according to option
  3614. if (true === this.options.sound) {
  3615. this.bind('playsound', function(e) {
  3616. var play = beeper.canPlayType && beeper.canPlayType('audio/wav; codecs="1"'),
  3617. file = e.data && e.data.soundFile;
  3618. play && file && play != '' && play != 'no' && $(beeper).html('<source src="' + soundPath + file + '" type="audio/wav">')[0].play();
  3619. });
  3620. }
  3621. // bind external event handlers
  3622. $.each(this.options.handlers, function(event, callback) {
  3623. self.bind(event, callback);
  3624. });
  3625. /**
  3626. * History object. Store visited folders
  3627. *
  3628. * @type Object
  3629. **/
  3630. this.history = new this.history(this);
  3631. /**
  3632. * Root hashed
  3633. *
  3634. * @type Object
  3635. */
  3636. this.roots = {};
  3637. /**
  3638. * leaf roots
  3639. *
  3640. * @type Object
  3641. */
  3642. this.leafRoots = {};
  3643. /**
  3644. * Loaded commands
  3645. *
  3646. * @type Object
  3647. **/
  3648. this._commands = {};
  3649. if (!Array.isArray(this.options.commands)) {
  3650. this.options.commands = [];
  3651. }
  3652. if ($.inArray('*', this.options.commands) !== -1) {
  3653. this.options.commands = Object.keys(this.commands);
  3654. }
  3655. /**
  3656. * UI command map of cwd volume ( That volume driver option `uiCmdMap` )
  3657. *
  3658. * @type Object
  3659. **/
  3660. this.commandMap = {};
  3661. /**
  3662. * cwd options of each volume
  3663. * key: volumeid
  3664. * val: options object
  3665. *
  3666. * @type Object
  3667. */
  3668. this.volOptions = {};
  3669. /**
  3670. * Has volOptions data
  3671. *
  3672. * @type Boolean
  3673. */
  3674. this.hasVolOptions = false;
  3675. /**
  3676. * Hash of trash holders
  3677. * key: trash folder hash
  3678. * val: source volume hash
  3679. *
  3680. * @type Object
  3681. */
  3682. this.trashes = {};
  3683. /**
  3684. * cwd options of each folder/file
  3685. * key: hash
  3686. * val: options object
  3687. *
  3688. * @type Object
  3689. */
  3690. this.optionsByHashes = {};
  3691. /**
  3692. * UI Auto Hide Functions
  3693. * Each auto hide function mast be call to `fm.trigger('uiautohide')` at end of process
  3694. *
  3695. * @type Array
  3696. **/
  3697. this.uiAutoHide = [];
  3698. // trigger `uiautohide`
  3699. this.one('open', function() {
  3700. if (self.uiAutoHide.length) {
  3701. setTimeout(function() {
  3702. self.trigger('uiautohide');
  3703. }, 500);
  3704. }
  3705. });
  3706. // Auto Hide Functions sequential processing start
  3707. this.bind('uiautohide', function() {
  3708. if (self.uiAutoHide.length) {
  3709. self.uiAutoHide.shift()();
  3710. }
  3711. });
  3712. if (this.options.width) {
  3713. width = this.options.width;
  3714. }
  3715. if (this.options.height) {
  3716. height = this.options.height;
  3717. }
  3718. if (this.options.heightBase) {
  3719. heightBase = $(this.options.heightBase);
  3720. }
  3721. if (this.options.soundPath) {
  3722. soundPath = this.options.soundPath.replace(/\/+$/, '') + '/';
  3723. }
  3724. self.one('opendone', function() {
  3725. var tm;
  3726. // attach events to document
  3727. $(document)
  3728. // disable elfinder on click outside elfinder
  3729. .on('click.'+namespace, function(e) { enabled && ! self.options.enableAlways && !$(e.target).closest(node).length && self.disable(); })
  3730. // exec shortcuts
  3731. .on(keydown+' '+keypress, execShortcut);
  3732. // attach events to window
  3733. self.options.useBrowserHistory && $(window)
  3734. .on('popstate.' + namespace, function(ev) {
  3735. var target = ev.originalEvent.state && ev.originalEvent.state.thash;
  3736. target && !$.isEmptyObject(self.files()) && self.request({
  3737. data : {cmd : 'open', target : target, onhistory : 1},
  3738. notify : {type : 'open', cnt : 1, hideCnt : true},
  3739. syncOnFail : true
  3740. });
  3741. });
  3742. $(window).on('resize.' + namespace, function(e){
  3743. if (e.target === this) {
  3744. tm && clearTimeout(tm);
  3745. tm = setTimeout(function() {
  3746. self.trigger('resize', {width : node.width(), height : node.height()});
  3747. }, 100);
  3748. }
  3749. })
  3750. .on('beforeunload.' + namespace,function(e){
  3751. var msg, cnt;
  3752. if (node.is(':visible')) {
  3753. if (self.ui.notify.children().length && $.inArray('hasNotifyDialog', self.options.windowCloseConfirm) !== -1) {
  3754. msg = self.i18n('ntfsmth');
  3755. } else if (node.find('.'+self.res('class', 'editing')).length && $.inArray('editingFile', self.options.windowCloseConfirm) !== -1) {
  3756. msg = self.i18n('editingFile');
  3757. } else if ((cnt = Object.keys(self.selected()).length) && $.inArray('hasSelectedItem', self.options.windowCloseConfirm) !== -1) {
  3758. msg = self.i18n('hasSelected', ''+cnt);
  3759. } else if ((cnt = Object.keys(self.clipboard()).length) && $.inArray('hasClipboardData', self.options.windowCloseConfirm) !== -1) {
  3760. msg = self.i18n('hasClipboard', ''+cnt);
  3761. }
  3762. if (msg) {
  3763. e.returnValue = msg;
  3764. return msg;
  3765. }
  3766. }
  3767. self.trigger('unload');
  3768. });
  3769. // bind window onmessage for CORS
  3770. $(window).on('message.' + namespace, function(e){
  3771. var res = e.originalEvent || null,
  3772. obj, data;
  3773. if (res && self.uploadURL.indexOf(res.origin) === 0) {
  3774. try {
  3775. obj = JSON.parse(res.data);
  3776. data = obj.data || null;
  3777. if (data) {
  3778. if (data.error) {
  3779. if (obj.bind) {
  3780. self.trigger(obj.bind+'fail', data);
  3781. }
  3782. self.error(data.error);
  3783. } else {
  3784. data.warning && self.error(data.warning);
  3785. self.updateCache(data);
  3786. data.removed && data.removed.length && self.remove(data);
  3787. data.added && data.added.length && self.add(data);
  3788. data.changed && data.changed.length && self.change(data);
  3789. if (obj.bind) {
  3790. self.trigger(obj.bind, data);
  3791. self.trigger(obj.bind+'done');
  3792. }
  3793. data.sync && self.sync();
  3794. }
  3795. }
  3796. } catch (e) {
  3797. self.sync();
  3798. }
  3799. }
  3800. });
  3801. // elFinder enable always
  3802. if (self.options.enableAlways) {
  3803. $(window).on('focus.' + namespace, function(e){
  3804. (e.target === this) && self.enable();
  3805. });
  3806. if (inFrame) {
  3807. $(window.top).on('focus.' + namespace, function() {
  3808. if (self.enable() && (! parentIframe || parentIframe.is(':visible'))) {
  3809. setTimeout(function() {
  3810. $(window).trigger('focus');
  3811. }, 10);
  3812. }
  3813. });
  3814. }
  3815. } else if (inFrame) {
  3816. $(window).on('blur.' + namespace, function(e){
  3817. enabled && e.target === this && self.disable();
  3818. });
  3819. }
  3820. // return focus to the window on click (elFInder in the frame)
  3821. if (inFrame) {
  3822. node.on('click', function(e) {
  3823. $(window).trigger('focus');
  3824. });
  3825. }
  3826. // elFinder to enable by mouse over
  3827. if (self.options.enableByMouseOver) {
  3828. node.on('mouseenter touchstart', function(e) {
  3829. (inFrame) && $(window).trigger('focus');
  3830. ! self.enabled() && self.enable();
  3831. });
  3832. }
  3833. });
  3834. // store instance in node
  3835. node[0].elfinder = this;
  3836. // auto load language file
  3837. dfrdsBeforeBootup.push((function() {
  3838. var lang = self.lang,
  3839. langJs = self.baseUrl + 'js/i18n/elfinder.' + lang + '.js',
  3840. dfd = $.Deferred().done(function() {
  3841. if (self.i18[lang]) {
  3842. self.lang = lang;
  3843. }
  3844. self.trigger('i18load');
  3845. i18n = self.lang === 'en'
  3846. ? self.i18['en']
  3847. : $.extend(true, {}, self.i18['en'], self.i18[self.lang]);
  3848. });
  3849. if (!self.i18[lang]) {
  3850. self.lang = 'en';
  3851. if (self.hasRequire) {
  3852. require([langJs], function() {
  3853. dfd.resolve();
  3854. }, function() {
  3855. dfd.resolve();
  3856. });
  3857. } else {
  3858. self.loadScript([langJs], function() {
  3859. dfd.resolve();
  3860. }, {
  3861. loadType: 'tag',
  3862. error : function() {
  3863. dfd.resolve();
  3864. }
  3865. });
  3866. }
  3867. } else {
  3868. dfd.resolve();
  3869. }
  3870. return dfd;
  3871. })());
  3872. // elFinder boot up function
  3873. bootUp = function() {
  3874. var columnNames;
  3875. /**
  3876. * i18 messages
  3877. *
  3878. * @type Object
  3879. **/
  3880. self.messages = i18n.messages;
  3881. // check jquery ui
  3882. if (!($.fn.selectable && $.fn.draggable && $.fn.droppable && $.fn.resizable)) {
  3883. return alert(self.i18n('errJqui'));
  3884. }
  3885. // check node
  3886. if (!node.length) {
  3887. return alert(self.i18n('errNode'));
  3888. }
  3889. // check connector url
  3890. if (!self.options.url) {
  3891. return alert(self.i18n('errURL'));
  3892. }
  3893. // column key/name map for fm.getColumnName()
  3894. columnNames = Object.assign({
  3895. name : self.i18n('name'),
  3896. perm : self.i18n('perms'),
  3897. date : self.i18n('modify'),
  3898. size : self.i18n('size'),
  3899. kind : self.i18n('kind'),
  3900. modestr : self.i18n('mode'),
  3901. modeoct : self.i18n('mode'),
  3902. modeboth : self.i18n('mode')
  3903. }, self.options.uiOptions.cwd.listView.columnsCustomName);
  3904. /**
  3905. * Gets the column name of cwd list view
  3906. *
  3907. * @param String key The key
  3908. * @return String The column name.
  3909. */
  3910. self.getColumnName = function(key) {
  3911. return columnNames[key] || self.i18n(key);
  3912. };
  3913. /**
  3914. * Interface direction
  3915. *
  3916. * @type String
  3917. * @default "ltr"
  3918. **/
  3919. self.direction = i18n.direction;
  3920. /**
  3921. * Date/time format
  3922. *
  3923. * @type String
  3924. * @default "m.d.Y"
  3925. **/
  3926. self.dateFormat = self.options.dateFormat || i18n.dateFormat;
  3927. /**
  3928. * Date format like "Yesterday 10:20:12"
  3929. *
  3930. * @type String
  3931. * @default "{day} {time}"
  3932. **/
  3933. self.fancyFormat = self.options.fancyDateFormat || i18n.fancyDateFormat;
  3934. /**
  3935. * Date format for if upload file has not original unique name
  3936. * e.g. Clipboard image data, Image data taken with iOS
  3937. *
  3938. * @type String
  3939. * @default "ymd-His"
  3940. **/
  3941. self.nonameDateFormat = (self.options.nonameDateFormat || i18n.nonameDateFormat).replace(/[\/\\]/g, '_');
  3942. /**
  3943. * Css classes
  3944. *
  3945. * @type String
  3946. **/
  3947. self.cssClass = 'ui-helper-reset ui-helper-clearfix ui-widget ui-widget-content ui-corner-all elfinder elfinder-'
  3948. +(self.direction == 'rtl' ? 'rtl' : 'ltr')
  3949. +(self.UA.Touch? (' elfinder-touch' + (self.options.resizable ? ' touch-punch' : '')) : '')
  3950. +(self.UA.Mobile? ' elfinder-mobile' : '')
  3951. +' '+self.options.cssClass;
  3952. // prepare node
  3953. node.addClass(self.cssClass)
  3954. .on(mousedown, function() {
  3955. !enabled && self.enable();
  3956. });
  3957. // draggable closure
  3958. (function() {
  3959. var ltr, wzRect, wzBottom, wzBottom2, nodeStyle,
  3960. keyEvt = keydown + 'draggable' + ' keyup.' + namespace + 'draggable';
  3961. /**
  3962. * Base draggable options
  3963. *
  3964. * @type Object
  3965. **/
  3966. self.draggable = {
  3967. appendTo : node,
  3968. addClasses : false,
  3969. distance : 4,
  3970. revert : true,
  3971. refreshPositions : false,
  3972. cursor : 'crosshair',
  3973. cursorAt : {left : 50, top : 47},
  3974. scroll : false,
  3975. start : function(e, ui) {
  3976. var helper = ui.helper,
  3977. targets = $.grep(helper.data('files')||[], function(h) {
  3978. if (h) {
  3979. remember[h] = true;
  3980. return true;
  3981. }
  3982. return false;
  3983. }),
  3984. locked = false,
  3985. cnt, h;
  3986. // fix node size
  3987. nodeStyle = node.attr('style');
  3988. node.width(node.width()).height(node.height());
  3989. // set var for drag()
  3990. ltr = (self.direction === 'ltr');
  3991. wzRect = self.getUI('workzone').data('rectangle');
  3992. wzBottom = wzRect.top + wzRect.height;
  3993. wzBottom2 = wzBottom - self.getUI('navdock').outerHeight(true);
  3994. self.draggingUiHelper = helper;
  3995. cnt = targets.length;
  3996. while (cnt--) {
  3997. h = targets[cnt];
  3998. if (files[h].locked) {
  3999. locked = true;
  4000. helper.data('locked', true);
  4001. break;
  4002. }
  4003. }
  4004. !locked && self.trigger('lockfiles', {files : targets});
  4005. helper.data('autoScrTm', setInterval(function() {
  4006. if (helper.data('autoScr')) {
  4007. self.autoScroll[helper.data('autoScr')](helper.data('autoScrVal'));
  4008. }
  4009. }, 50));
  4010. },
  4011. drag : function(e, ui) {
  4012. var helper = ui.helper,
  4013. autoScr, autoUp, bottom;
  4014. if ((autoUp = wzRect.top > e.pageY) || wzBottom2 < e.pageY) {
  4015. if (wzRect.cwdEdge > e.pageX) {
  4016. autoScr = (ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down');
  4017. } else {
  4018. autoScr = (ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down');
  4019. }
  4020. if (!autoUp) {
  4021. if (autoScr.substr(0, 3) === 'cwd') {
  4022. if (wzBottom < e.pageY) {
  4023. bottom = wzBottom;
  4024. } else {
  4025. autoScr = null;
  4026. }
  4027. } else {
  4028. bottom = wzBottom2;
  4029. }
  4030. }
  4031. if (autoScr) {
  4032. helper.data('autoScr', autoScr);
  4033. helper.data('autoScrVal', Math.pow((autoUp? wzRect.top - e.pageY : e.pageY - bottom), 1.3));
  4034. }
  4035. }
  4036. if (! autoScr) {
  4037. if (helper.data('autoScr')) {
  4038. helper.data('refreshPositions', 1).data('autoScr', null);
  4039. }
  4040. }
  4041. if (helper.data('refreshPositions') && $(this).elfUiWidgetInstance('draggable')) {
  4042. if (helper.data('refreshPositions') > 0) {
  4043. $(this).draggable('option', { refreshPositions : true, elfRefresh : true });
  4044. helper.data('refreshPositions', -1);
  4045. } else {
  4046. $(this).draggable('option', { refreshPositions : false, elfRefresh : false });
  4047. helper.data('refreshPositions', null);
  4048. }
  4049. }
  4050. },
  4051. stop : function(e, ui) {
  4052. var helper = ui.helper,
  4053. files;
  4054. $(document).off(keyEvt);
  4055. $(this).elfUiWidgetInstance('draggable') && $(this).draggable('option', { refreshPositions : false });
  4056. self.draggingUiHelper = null;
  4057. self.trigger('focus').trigger('dragstop');
  4058. if (! helper.data('droped')) {
  4059. files = $.grep(helper.data('files')||[], function(h) { return h? true : false ;});
  4060. self.trigger('unlockfiles', {files : files});
  4061. self.trigger('selectfiles', {files : files});
  4062. }
  4063. self.enable();
  4064. // restore node style
  4065. node.attr('style', nodeStyle);
  4066. helper.data('autoScrTm') && clearInterval(helper.data('autoScrTm'));
  4067. },
  4068. helper : function(e, ui) {
  4069. var element = this.id ? $(this) : $(this).parents('[id]:first'),
  4070. helper = $('<div class="elfinder-drag-helper"><span class="elfinder-drag-helper-icon-status"/></div>'),
  4071. icon = function(f) {
  4072. var mime = f.mime, i, tmb = self.tmb(f);
  4073. i = '<div class="elfinder-cwd-icon elfinder-cwd-icon-drag '+self.mime2class(mime)+' ui-corner-all"/>';
  4074. if (tmb) {
  4075. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  4076. }
  4077. return i;
  4078. },
  4079. hashes, l, ctr;
  4080. self.draggingUiHelper && self.draggingUiHelper.stop(true, true);
  4081. self.trigger('dragstart', {target : element[0], originalEvent : e}, true);
  4082. hashes = element.hasClass(self.res('class', 'cwdfile'))
  4083. ? self.selected()
  4084. : [self.navId2Hash(element.attr('id'))];
  4085. helper.append(icon(files[hashes[0]])).data('files', hashes).data('locked', false).data('droped', false).data('namespace', namespace).data('dropover', 0);
  4086. if ((l = hashes.length) > 1) {
  4087. helper.append(icon(files[hashes[l-1]]) + '<span class="elfinder-drag-num">'+l+'</span>');
  4088. }
  4089. $(document).on(keyEvt, function(e){
  4090. var chk = (e.shiftKey||e.ctrlKey||e.metaKey);
  4091. if (ctr !== chk) {
  4092. ctr = chk;
  4093. if (helper.is(':visible') && helper.data('dropover') && ! helper.data('droped')) {
  4094. helper.toggleClass('elfinder-drag-helper-plus', helper.data('locked')? true : ctr);
  4095. self.trigger(ctr? 'unlockfiles' : 'lockfiles', {files : hashes, helper: helper});
  4096. }
  4097. }
  4098. });
  4099. return helper;
  4100. }
  4101. };
  4102. })();
  4103. // in getFileCallback set - change default actions on double click/enter/ctrl+enter
  4104. if (self.commands.getfile) {
  4105. if (typeof(self.options.getFileCallback) == 'function') {
  4106. self.bind('dblclick', function(e) {
  4107. e.preventDefault();
  4108. self.exec('getfile').fail(function() {
  4109. self.exec('open', e.data && e.data.file? [ e.data.file ]: void(0));
  4110. });
  4111. });
  4112. self.shortcut({
  4113. pattern : 'enter',
  4114. description : self.i18n('cmdgetfile'),
  4115. callback : function() { self.exec('getfile').fail(function() { self.exec(self.OS == 'mac' ? 'rename' : 'open'); }); }
  4116. })
  4117. .shortcut({
  4118. pattern : 'ctrl+enter',
  4119. description : self.i18n(self.OS == 'mac' ? 'cmdrename' : 'cmdopen'),
  4120. callback : function() { self.exec(self.OS == 'mac' ? 'rename' : 'open'); }
  4121. });
  4122. } else {
  4123. self.options.getFileCallback = null;
  4124. }
  4125. }
  4126. // load commands
  4127. $.each(self.commands, function(name, cmd) {
  4128. var proto = Object.assign({}, cmd.prototype),
  4129. extendsCmd, opts;
  4130. if ($.isFunction(cmd) && !self._commands[name] && (cmd.prototype.forceLoad || $.inArray(name, self.options.commands) !== -1)) {
  4131. extendsCmd = cmd.prototype.extendsCmd || '';
  4132. if (extendsCmd) {
  4133. if ($.isFunction(self.commands[extendsCmd])) {
  4134. cmd.prototype = Object.assign({}, base, new self.commands[extendsCmd](), cmd.prototype);
  4135. } else {
  4136. return true;
  4137. }
  4138. } else {
  4139. cmd.prototype = Object.assign({}, base, cmd.prototype);
  4140. }
  4141. self._commands[name] = new cmd();
  4142. cmd.prototype = proto;
  4143. opts = self.options.commandsOptions[name] || {};
  4144. if (extendsCmd && self.options.commandsOptions[extendsCmd]) {
  4145. opts = $.extend(true, {}, self.options.commandsOptions[extendsCmd], opts);
  4146. }
  4147. self._commands[name].setup(name, opts);
  4148. // setup linked commands
  4149. if (self._commands[name].linkedCmds.length) {
  4150. $.each(self._commands[name].linkedCmds, function(i, n) {
  4151. var lcmd = self.commands[n];
  4152. if ($.isFunction(lcmd) && !self._commands[n]) {
  4153. lcmd.prototype = base;
  4154. self._commands[n] = new lcmd();
  4155. self._commands[n].setup(n, self.options.commandsOptions[n]||{});
  4156. }
  4157. });
  4158. }
  4159. }
  4160. });
  4161. /**
  4162. * UI nodes
  4163. *
  4164. * @type Object
  4165. **/
  4166. self.ui = {
  4167. // container for nav panel and current folder container
  4168. workzone : $('<div/>').appendTo(node).elfinderworkzone(self),
  4169. // container for folders tree / places
  4170. navbar : $('<div/>').appendTo(node).elfindernavbar(self, self.options.uiOptions.navbar || {}),
  4171. // container for for preview etc at below the navbar
  4172. navdock : $('<div/>').appendTo(node).elfindernavdock(self, self.options.uiOptions.navdock || {}),
  4173. // contextmenu
  4174. contextmenu : $('<div/>').appendTo(node).elfindercontextmenu(self),
  4175. // overlay
  4176. overlay : $('<div/>').appendTo(node).elfinderoverlay({
  4177. show : function() { self.disable(); },
  4178. hide : function() { prevEnabled && self.enable(); }
  4179. }),
  4180. // current folder container
  4181. cwd : $('<div/>').appendTo(node).elfindercwd(self, self.options.uiOptions.cwd || {}),
  4182. // notification dialog window
  4183. notify : self.dialog('', {
  4184. cssClass : 'elfinder-dialog-notify',
  4185. position : self.options.notifyDialog.position,
  4186. absolute : true,
  4187. resizable : false,
  4188. autoOpen : false,
  4189. closeOnEscape : false,
  4190. title : '&nbsp;',
  4191. width : parseInt(self.options.notifyDialog.width)
  4192. }),
  4193. statusbar : $('<div class="ui-widget-header ui-helper-clearfix ui-corner-bottom elfinder-statusbar"/>').hide().appendTo(node),
  4194. toast : $('<div class="elfinder-toast"/>').appendTo(node),
  4195. bottomtray : $('<div class="elfinder-bottomtray">').appendTo(node)
  4196. };
  4197. // load required ui
  4198. $.each(self.options.ui || [], function(i, ui) {
  4199. var name = 'elfinder'+ui,
  4200. opts = self.options.uiOptions[ui] || {};
  4201. if (!self.ui[ui] && $.fn[name]) {
  4202. // regist to self.ui before make instance
  4203. self.ui[ui] = $('<'+(opts.tag || 'div')+'/>').appendTo(node);
  4204. self.ui[ui][name](self, opts);
  4205. }
  4206. });
  4207. // update size
  4208. self.resize(width, height);
  4209. // make node resizable
  4210. if (self.options.resizable) {
  4211. node.resizable({
  4212. resize : function(e, ui) {
  4213. self.resize(ui.size.width, ui.size.height);
  4214. },
  4215. handles : 'se',
  4216. minWidth : 300,
  4217. minHeight : 200
  4218. });
  4219. if (self.UA.Touch) {
  4220. node.addClass('touch-punch');
  4221. }
  4222. }
  4223. (function() {
  4224. var navbar = self.getUI('navbar'),
  4225. cwd = self.getUI('cwd').parent();
  4226. self.autoScroll = {
  4227. navbarUp : function(v) {
  4228. navbar.scrollTop(Math.max(0, navbar.scrollTop() - v));
  4229. },
  4230. navbarDown : function(v) {
  4231. navbar.scrollTop(navbar.scrollTop() + v);
  4232. },
  4233. cwdUp : function(v) {
  4234. cwd.scrollTop(Math.max(0, cwd.scrollTop() - v));
  4235. },
  4236. cwdDown : function(v) {
  4237. cwd.scrollTop(cwd.scrollTop() + v);
  4238. }
  4239. };
  4240. })();
  4241. // Swipe on the touch devices to show/hide of toolbar or navbar
  4242. if (self.UA.Touch) {
  4243. (function() {
  4244. var lastX, lastY, nodeOffset, nodeWidth, nodeTop, navbarW, toolbarH,
  4245. navbar = self.getUI('navbar'),
  4246. toolbar = self.getUI('toolbar'),
  4247. moveEv = 'touchmove.stopscroll',
  4248. moveTm,
  4249. moveUpOn = function(e) {
  4250. var touches = e.originalEvent.touches || [{}],
  4251. y = touches[0].pageY || null;
  4252. if (!lastY || y < lastY) {
  4253. e.preventDefault();
  4254. moveTm && clearTimeout(moveTm);
  4255. }
  4256. },
  4257. moveDownOn = function(e) {
  4258. e.preventDefault();
  4259. moveTm && clearTimeout(moveTm);
  4260. },
  4261. moveOff = function() {
  4262. moveTm = setTimeout(function() {
  4263. node.off(moveEv);
  4264. }, 100);
  4265. },
  4266. handleW, handleH = 50;
  4267. navbar = navbar.children().length? navbar : null;
  4268. toolbar = toolbar.length? toolbar : null;
  4269. node.on('touchstart touchmove touchend', function(e) {
  4270. if (e.type === 'touchend') {
  4271. lastX = false;
  4272. lastY = false;
  4273. moveOff();
  4274. return;
  4275. }
  4276. var touches = e.originalEvent.touches || [{}],
  4277. x = touches[0].pageX || null,
  4278. y = touches[0].pageY || null,
  4279. ltr = (self.direction === 'ltr'),
  4280. navbarMode, treeWidth, swipeX, moveX, toolbarT, mode;
  4281. if (x === null || y === null || (e.type === 'touchstart' && touches.length > 1)) {
  4282. return;
  4283. }
  4284. if (e.type === 'touchstart') {
  4285. nodeOffset = node.offset();
  4286. nodeWidth = node.width();
  4287. if (navbar) {
  4288. lastX = false;
  4289. if (navbar.is(':hidden')) {
  4290. if (! handleW) {
  4291. handleW = Math.max(50, nodeWidth / 10);
  4292. }
  4293. if ((ltr? (x - nodeOffset.left) : (nodeWidth + nodeOffset.left - x)) < handleW) {
  4294. lastX = x;
  4295. }
  4296. } else if (! e.originalEvent._preventSwipeX) {
  4297. navbarW = navbar.width();
  4298. if (ltr) {
  4299. swipeX = (x < nodeOffset.left + navbarW);
  4300. } else {
  4301. swipeX = (x > nodeOffset.left + nodeWidth - navbarW);
  4302. }
  4303. if (swipeX) {
  4304. handleW = Math.max(50, nodeWidth / 10);
  4305. lastX = x;
  4306. } else {
  4307. lastX = false;
  4308. }
  4309. }
  4310. }
  4311. if (toolbar) {
  4312. lastY = false;
  4313. if (! e.originalEvent._preventSwipeY) {
  4314. toolbarH = toolbar.height();
  4315. nodeTop = nodeOffset.top;
  4316. if (y - nodeTop < (toolbar.is(':hidden')? handleH : (toolbarH + 30))) {
  4317. lastY = y;
  4318. node.on(moveEv, toolbar.is(':hidden')? moveDownOn: moveUpOn);
  4319. }
  4320. }
  4321. }
  4322. } else {
  4323. if (navbar && lastX !== false) {
  4324. navbarMode = (ltr? (lastX > x) : (lastX < x))? 'navhide' : 'navshow';
  4325. moveX = Math.abs(lastX - x);
  4326. if (navbarMode === 'navhide' && moveX > navbarW * 0.6
  4327. || (moveX > (navbarMode === 'navhide'? navbarW / 3 : 45)
  4328. && (navbarMode === 'navshow'
  4329. || (ltr? x < nodeOffset.left + 20 : x > nodeOffset.left + nodeWidth - 20)
  4330. ))
  4331. ) {
  4332. self.getUI('navbar').trigger(navbarMode, {handleW: handleW});
  4333. lastX = false;
  4334. }
  4335. }
  4336. if (toolbar && lastY !== false ) {
  4337. toolbarT = toolbar.offset().top;
  4338. if (Math.abs(lastY - y) > Math.min(45, toolbarH / 3)) {
  4339. mode = (lastY > y)? 'slideUp' : 'slideDown';
  4340. if (mode === 'slideDown' || toolbarT + 20 > y) {
  4341. if (toolbar.is(mode === 'slideDown' ? ':hidden' : ':visible')) {
  4342. toolbar.stop(true, true).trigger('toggle', {duration: 100, handleH: handleH});
  4343. }
  4344. lastY = false;
  4345. }
  4346. }
  4347. }
  4348. }
  4349. });
  4350. })();
  4351. }
  4352. if (self.dragUpload) {
  4353. // add event listener for HTML5 DnD upload
  4354. (function() {
  4355. var isin = function(e) {
  4356. return (e.target.nodeName !== 'TEXTAREA' && e.target.nodeName !== 'INPUT' && $(e.target).closest('div.ui-dialog-content').length === 0);
  4357. },
  4358. ent = 'native-drag-enter',
  4359. disable = 'native-drag-disable',
  4360. c = 'class',
  4361. navdir = self.res(c, 'navdir'),
  4362. droppable = self.res(c, 'droppable'),
  4363. dropover = self.res(c, 'adroppable'),
  4364. arrow = self.res(c, 'navarrow'),
  4365. clDropActive = self.res(c, 'adroppable'),
  4366. wz = self.getUI('workzone'),
  4367. ltr = (self.direction === 'ltr'),
  4368. clearTm = function() {
  4369. autoScrTm && clearTimeout(autoScrTm);
  4370. autoScrTm = null;
  4371. },
  4372. wzRect, autoScrFn, autoScrTm;
  4373. node.on('dragenter', function(e) {
  4374. clearTm();
  4375. if (isin(e)) {
  4376. e.preventDefault();
  4377. e.stopPropagation();
  4378. wzRect = wz.data('rectangle');
  4379. }
  4380. })
  4381. .on('dragleave', function(e) {
  4382. clearTm();
  4383. if (isin(e)) {
  4384. e.preventDefault();
  4385. e.stopPropagation();
  4386. }
  4387. })
  4388. .on('dragover', function(e) {
  4389. var autoUp;
  4390. if (isin(e)) {
  4391. e.preventDefault();
  4392. e.stopPropagation();
  4393. e.originalEvent.dataTransfer.dropEffect = 'none';
  4394. if (! autoScrTm) {
  4395. autoScrTm = setTimeout(function() {
  4396. var wzBottom = wzRect.top + wzRect.height,
  4397. wzBottom2 = wzBottom - self.getUI('navdock').outerHeight(true),
  4398. fn;
  4399. if ((autoUp = e.pageY < wzRect.top) || e.pageY > wzBottom2 ) {
  4400. if (wzRect.cwdEdge > e.pageX) {
  4401. fn = (ltr? 'navbar' : 'cwd') + (autoUp? 'Up' : 'Down');
  4402. } else {
  4403. fn = (ltr? 'cwd' : 'navbar') + (autoUp? 'Up' : 'Down');
  4404. }
  4405. if (!autoUp) {
  4406. if (fn.substr(0, 3) === 'cwd') {
  4407. if (wzBottom < e.pageY) {
  4408. wzBottom2 = wzBottom;
  4409. } else {
  4410. fn = '';
  4411. }
  4412. }
  4413. }
  4414. fn && self.autoScroll[fn](Math.pow((autoUp? wzRect.top - e.pageY : e.pageY - wzBottom2), 1.3));
  4415. }
  4416. autoScrTm = null;
  4417. }, 20);
  4418. }
  4419. } else {
  4420. clearTm();
  4421. }
  4422. })
  4423. .on('drop', function(e) {
  4424. clearTm();
  4425. if (isin(e)) {
  4426. e.stopPropagation();
  4427. e.preventDefault();
  4428. }
  4429. });
  4430. node.on('dragenter', '.native-droppable', function(e){
  4431. if (e.originalEvent.dataTransfer) {
  4432. var $elm = $(e.currentTarget),
  4433. id = e.currentTarget.id || null,
  4434. cwd = null,
  4435. elfFrom;
  4436. if (!id) { // target is cwd
  4437. cwd = self.cwd();
  4438. $elm.data(disable, false);
  4439. try {
  4440. $.each(e.originalEvent.dataTransfer.types, function(i, v){
  4441. if (v.substr(0, 13) === 'elfinderfrom:') {
  4442. elfFrom = v.substr(13).toLowerCase();
  4443. }
  4444. });
  4445. } catch(e) {}
  4446. }
  4447. if (!cwd || (cwd.write && (!elfFrom || elfFrom !== (window.location.href + cwd.hash).toLowerCase()))) {
  4448. e.preventDefault();
  4449. e.stopPropagation();
  4450. $elm.data(ent, true);
  4451. $elm.addClass(clDropActive);
  4452. } else {
  4453. $elm.data(disable, true);
  4454. }
  4455. }
  4456. })
  4457. .on('dragleave', '.native-droppable', function(e){
  4458. if (e.originalEvent.dataTransfer) {
  4459. var $elm = $(e.currentTarget);
  4460. e.preventDefault();
  4461. e.stopPropagation();
  4462. if ($elm.data(ent)) {
  4463. $elm.data(ent, false);
  4464. } else {
  4465. $elm.removeClass(clDropActive);
  4466. }
  4467. }
  4468. })
  4469. .on('dragover', '.native-droppable', function(e){
  4470. if (e.originalEvent.dataTransfer) {
  4471. var $elm = $(e.currentTarget);
  4472. e.preventDefault();
  4473. e.stopPropagation();
  4474. e.originalEvent.dataTransfer.dropEffect = $elm.data(disable)? 'none' : 'copy';
  4475. $elm.data(ent, false);
  4476. }
  4477. })
  4478. .on('drop', '.native-droppable', function(e){
  4479. if (e.originalEvent && e.originalEvent.dataTransfer) {
  4480. var $elm = $(e.currentTarget),
  4481. id;
  4482. e.preventDefault();
  4483. e.stopPropagation();
  4484. $elm.removeClass(clDropActive);
  4485. if (e.currentTarget.id) {
  4486. id = $elm.hasClass(navdir)? self.navId2Hash(e.currentTarget.id) : self.cwdId2Hash(e.currentTarget.id);
  4487. } else {
  4488. id = self.cwd().hash;
  4489. }
  4490. e.originalEvent._target = id;
  4491. self.exec('upload', {dropEvt: e.originalEvent, target: id}, void 0, id);
  4492. }
  4493. });
  4494. })();
  4495. }
  4496. // trigger event cssloaded if cddAutoLoad disabled
  4497. if (self.cssloaded === null) {
  4498. // check css loaded and remove hide
  4499. (function() {
  4500. var loaded = function() {
  4501. node.data('cssautoloadHide').remove();
  4502. node.removeData('cssautoloadHide');
  4503. self.cssloaded = true;
  4504. self.trigger('cssloaded');
  4505. },
  4506. cnt, fi;
  4507. if (node.css('visibility') === 'hidden') {
  4508. cnt = 1000; // timeout 10 secs
  4509. fi = setInterval(function() {
  4510. if (--cnt < 0 || node.css('visibility') !== 'hidden') {
  4511. clearInterval(fi);
  4512. loaded();
  4513. }
  4514. }, 10);
  4515. } else {
  4516. loaded();
  4517. }
  4518. })();
  4519. } else {
  4520. self.cssloaded = true;
  4521. self.trigger('cssloaded');
  4522. }
  4523. // calculate elFinder node z-index
  4524. self.zIndexCalc();
  4525. // send initial request and start to pray >_<
  4526. self.trigger('init')
  4527. .request({
  4528. data : {cmd : 'open', target : self.startDir(), init : 1, tree : 1},
  4529. preventDone : true,
  4530. notify : {type : 'open', cnt : 1, hideCnt : true},
  4531. freeze : true
  4532. })
  4533. .fail(function() {
  4534. self.trigger('fail').disable().lastDir('');
  4535. listeners = {};
  4536. shortcuts = {};
  4537. $(document).add(node).off('.'+namespace);
  4538. self.trigger = function() { };
  4539. })
  4540. .done(function(data) {
  4541. var trashDisable = function(th) {
  4542. var src = self.file(self.trashes[th]),
  4543. d = self.options.debug,
  4544. error;
  4545. if (src && src.volumeid) {
  4546. delete self.volOptions[src.volumeid].trashHash;
  4547. }
  4548. self.trashes[th] = false;
  4549. self.debug('backend-error', 'Trash hash "'+th+'" was not found or not writable.');
  4550. },
  4551. toChkTh = {};
  4552. // regist rawStringDecoder
  4553. if (self.options.rawStringDecoder) {
  4554. self.registRawStringDecoder(self.options.rawStringDecoder);
  4555. }
  4556. // re-calculate elFinder node z-index
  4557. self.zIndexCalc();
  4558. self.load().debug('api', self.api);
  4559. // update ui's size after init
  4560. node.trigger('resize');
  4561. // initial open
  4562. open(data);
  4563. self.trigger('open', data, false);
  4564. self.trigger('opendone');
  4565. if (inFrame && self.options.enableAlways) {
  4566. $(window).trigger('focus');
  4567. }
  4568. // check self.trashes
  4569. $.each(self.trashes, function(th) {
  4570. var dir = self.file(th),
  4571. src;
  4572. if (! dir) {
  4573. toChkTh[th] = true;
  4574. } else if (dir.mime !== 'directory' || ! dir.write) {
  4575. trashDisable(th);
  4576. }
  4577. });
  4578. if (Object.keys(toChkTh).length) {
  4579. self.request({
  4580. data : {cmd : 'info', targets : Object.keys(toChkTh)},
  4581. preventDefault : true
  4582. }).done(function(data) {
  4583. if (data && data.files) {
  4584. $.each(data.files, function(i, dir) {
  4585. if (dir.mime === 'directory' && dir.write) {
  4586. delete toChkTh[dir.hash];
  4587. }
  4588. });
  4589. }
  4590. }).always(function() {
  4591. $.each(toChkTh, trashDisable);
  4592. });
  4593. }
  4594. // to enable / disable
  4595. self[self.options.enableAlways? 'enable' : 'disable']();
  4596. });
  4597. // self.timeEnd('load');
  4598. // End of bootUp()
  4599. };
  4600. // call bootCallback function with elFinder instance, extraObject - { dfrdsBeforeBootup: dfrdsBeforeBootup }
  4601. if (bootCallback && typeof bootCallback === 'function') {
  4602. self.bootCallback = bootCallback;
  4603. bootCallback.call(node.get(0), self, { dfrdsBeforeBootup: dfrdsBeforeBootup });
  4604. }
  4605. // call dfrdsBeforeBootup functions then boot up elFinder
  4606. $.when.apply(null, dfrdsBeforeBootup).done(function() {
  4607. bootUp();
  4608. }).fail(function(error) {
  4609. self.error(error);
  4610. });
  4611. };
  4612. //register elFinder to global scope
  4613. if (typeof toGlobal === 'undefined' || toGlobal) {
  4614. window.elFinder = elFinder;
  4615. }
  4616. /**
  4617. * Prototype
  4618. *
  4619. * @type Object
  4620. */
  4621. elFinder.prototype = {
  4622. uniqueid : 0,
  4623. res : function(type, id) {
  4624. return this.resources[type] && this.resources[type][id];
  4625. },
  4626. /**
  4627. * User os. Required to bind native shortcuts for open/rename
  4628. *
  4629. * @type String
  4630. **/
  4631. OS : navigator.userAgent.indexOf('Mac') !== -1 ? 'mac' : navigator.userAgent.indexOf('Win') !== -1 ? 'win' : 'other',
  4632. /**
  4633. * User browser UA.
  4634. * jQuery.browser: version deprecated: 1.3, removed: 1.9
  4635. *
  4636. * @type Object
  4637. **/
  4638. UA : (function(){
  4639. var self = this,
  4640. webkit = !document.unqueID && !window.opera && !window.sidebar && window.localStorage && 'WebkitAppearance' in document.documentElement.style,
  4641. /*setRotated = function() {
  4642. var a = ((screen && screen.orientation && screen.orientation.angle) || window.orientation || 0) + 0;
  4643. if (a === -90) {
  4644. a = 270;
  4645. }
  4646. UA.Angle = a;
  4647. UA.Rotated = a % 180 === 0? false : true;
  4648. },*/
  4649. UA = {
  4650. // Browser IE <= IE 6
  4651. ltIE6 : typeof window.addEventListener == "undefined" && typeof document.documentElement.style.maxHeight == "undefined",
  4652. // Browser IE <= IE 7
  4653. ltIE7 : typeof window.addEventListener == "undefined" && typeof document.querySelectorAll == "undefined",
  4654. // Browser IE <= IE 8
  4655. ltIE8 : typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined",
  4656. // Browser IE <= IE 9
  4657. ltIE9 : document.uniqueID && document.documentMode <= 9,
  4658. // Browser IE <= IE 10
  4659. ltIE10 : document.uniqueID && document.documentMode <= 10,
  4660. // Browser IE >= IE 11
  4661. gtIE11 : document.uniqueID && document.documentMode >= 11,
  4662. IE : document.uniqueID,
  4663. Firefox : window.sidebar,
  4664. Opera : window.opera,
  4665. Webkit : webkit,
  4666. Chrome : webkit && window.chrome,
  4667. Safari : webkit && !window.chrome,
  4668. Mobile : typeof window.orientation != "undefined",
  4669. Touch : typeof window.ontouchstart != "undefined",
  4670. iOS : navigator.platform.match(/^iP(?:[ao]d|hone)/),
  4671. Fullscreen : (typeof (document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen || document.msExitFullscreen) !== 'undefined'),
  4672. Angle : 0,
  4673. Rotated : false
  4674. };
  4675. return UA;
  4676. })(),
  4677. /**
  4678. * Has RequireJS?
  4679. *
  4680. * @type Boolean
  4681. */
  4682. hasRequire : (typeof define === 'function' && define.amd),
  4683. /**
  4684. * Current request command
  4685. *
  4686. * @type String
  4687. */
  4688. currentReqCmd : '',
  4689. /**
  4690. * Internationalization object
  4691. *
  4692. * @type Object
  4693. */
  4694. i18 : {
  4695. en : {
  4696. translator : '',
  4697. language : 'English',
  4698. direction : 'ltr',
  4699. dateFormat : 'd.m.Y H:i',
  4700. fancyDateFormat : '$1 H:i',
  4701. nonameDateFormat : 'ymd-His',
  4702. messages : {}
  4703. },
  4704. months : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
  4705. monthsShort : ['msJan', 'msFeb', 'msMar', 'msApr', 'msMay', 'msJun', 'msJul', 'msAug', 'msSep', 'msOct', 'msNov', 'msDec'],
  4706. days : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
  4707. daysShort : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
  4708. },
  4709. /**
  4710. * File mimetype to kind mapping
  4711. *
  4712. * @type Object
  4713. */
  4714. kinds : {
  4715. 'unknown' : 'Unknown',
  4716. 'directory' : 'Folder',
  4717. 'group' : 'Selects',
  4718. 'symlink' : 'Alias',
  4719. 'symlink-broken' : 'AliasBroken',
  4720. 'application/x-empty' : 'TextPlain',
  4721. 'application/postscript' : 'Postscript',
  4722. 'application/vnd.ms-office' : 'MsOffice',
  4723. 'application/msword' : 'MsWord',
  4724. 'application/vnd.ms-word' : 'MsWord',
  4725. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : 'MsWord',
  4726. 'application/vnd.ms-word.document.macroEnabled.12' : 'MsWord',
  4727. 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' : 'MsWord',
  4728. 'application/vnd.ms-word.template.macroEnabled.12' : 'MsWord',
  4729. 'application/vnd.ms-excel' : 'MsExcel',
  4730. 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : 'MsExcel',
  4731. 'application/vnd.ms-excel.sheet.macroEnabled.12' : 'MsExcel',
  4732. 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' : 'MsExcel',
  4733. 'application/vnd.ms-excel.template.macroEnabled.12' : 'MsExcel',
  4734. 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' : 'MsExcel',
  4735. 'application/vnd.ms-excel.addin.macroEnabled.12' : 'MsExcel',
  4736. 'application/vnd.ms-powerpoint' : 'MsPP',
  4737. 'application/vnd.openxmlformats-officedocument.presentationml.presentation' : 'MsPP',
  4738. 'application/vnd.ms-powerpoint.presentation.macroEnabled.12' : 'MsPP',
  4739. 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' : 'MsPP',
  4740. 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' : 'MsPP',
  4741. 'application/vnd.openxmlformats-officedocument.presentationml.template' : 'MsPP',
  4742. 'application/vnd.ms-powerpoint.template.macroEnabled.12' : 'MsPP',
  4743. 'application/vnd.ms-powerpoint.addin.macroEnabled.12' : 'MsPP',
  4744. 'application/vnd.openxmlformats-officedocument.presentationml.slide' : 'MsPP',
  4745. 'application/vnd.ms-powerpoint.slide.macroEnabled.12' : 'MsPP',
  4746. 'application/pdf' : 'PDF',
  4747. 'application/xml' : 'XML',
  4748. 'application/vnd.oasis.opendocument.text' : 'OO',
  4749. 'application/vnd.oasis.opendocument.text-template' : 'OO',
  4750. 'application/vnd.oasis.opendocument.text-web' : 'OO',
  4751. 'application/vnd.oasis.opendocument.text-master' : 'OO',
  4752. 'application/vnd.oasis.opendocument.graphics' : 'OO',
  4753. 'application/vnd.oasis.opendocument.graphics-template' : 'OO',
  4754. 'application/vnd.oasis.opendocument.presentation' : 'OO',
  4755. 'application/vnd.oasis.opendocument.presentation-template' : 'OO',
  4756. 'application/vnd.oasis.opendocument.spreadsheet' : 'OO',
  4757. 'application/vnd.oasis.opendocument.spreadsheet-template' : 'OO',
  4758. 'application/vnd.oasis.opendocument.chart' : 'OO',
  4759. 'application/vnd.oasis.opendocument.formula' : 'OO',
  4760. 'application/vnd.oasis.opendocument.database' : 'OO',
  4761. 'application/vnd.oasis.opendocument.image' : 'OO',
  4762. 'application/vnd.openofficeorg.extension' : 'OO',
  4763. 'application/x-shockwave-flash' : 'AppFlash',
  4764. 'application/flash-video' : 'Flash video',
  4765. 'application/x-bittorrent' : 'Torrent',
  4766. 'application/javascript' : 'JS',
  4767. 'application/rtf' : 'RTF',
  4768. 'application/rtfd' : 'RTF',
  4769. 'application/x-font-ttf' : 'TTF',
  4770. 'application/x-font-otf' : 'OTF',
  4771. 'application/x-rpm' : 'RPM',
  4772. 'application/x-web-config' : 'TextPlain',
  4773. 'application/xhtml+xml' : 'HTML',
  4774. 'application/docbook+xml' : 'DOCBOOK',
  4775. 'application/x-awk' : 'AWK',
  4776. 'application/x-gzip' : 'GZIP',
  4777. 'application/x-bzip2' : 'BZIP',
  4778. 'application/x-xz' : 'XZ',
  4779. 'application/zip' : 'ZIP',
  4780. 'application/x-zip' : 'ZIP',
  4781. 'application/x-rar' : 'RAR',
  4782. 'application/x-tar' : 'TAR',
  4783. 'application/x-7z-compressed' : '7z',
  4784. 'application/x-jar' : 'JAR',
  4785. 'text/plain' : 'TextPlain',
  4786. 'text/x-php' : 'PHP',
  4787. 'text/html' : 'HTML',
  4788. 'text/javascript' : 'JS',
  4789. 'text/css' : 'CSS',
  4790. 'text/rtf' : 'RTF',
  4791. 'text/rtfd' : 'RTF',
  4792. 'text/x-c' : 'C',
  4793. 'text/x-csrc' : 'C',
  4794. 'text/x-chdr' : 'CHeader',
  4795. 'text/x-c++' : 'CPP',
  4796. 'text/x-c++src' : 'CPP',
  4797. 'text/x-c++hdr' : 'CPPHeader',
  4798. 'text/x-shellscript' : 'Shell',
  4799. 'application/x-csh' : 'Shell',
  4800. 'text/x-python' : 'Python',
  4801. 'text/x-java' : 'Java',
  4802. 'text/x-java-source' : 'Java',
  4803. 'text/x-ruby' : 'Ruby',
  4804. 'text/x-perl' : 'Perl',
  4805. 'text/x-sql' : 'SQL',
  4806. 'text/xml' : 'XML',
  4807. 'text/x-comma-separated-values' : 'CSV',
  4808. 'text/x-markdown' : 'Markdown',
  4809. 'image/x-ms-bmp' : 'BMP',
  4810. 'image/jpeg' : 'JPEG',
  4811. 'image/gif' : 'GIF',
  4812. 'image/png' : 'PNG',
  4813. 'image/tiff' : 'TIFF',
  4814. 'image/x-targa' : 'TGA',
  4815. 'image/vnd.adobe.photoshop' : 'PSD',
  4816. 'image/xbm' : 'XBITMAP',
  4817. 'image/pxm' : 'PXM',
  4818. 'audio/mpeg' : 'AudioMPEG',
  4819. 'audio/midi' : 'AudioMIDI',
  4820. 'audio/ogg' : 'AudioOGG',
  4821. 'audio/mp4' : 'AudioMPEG4',
  4822. 'audio/x-m4a' : 'AudioMPEG4',
  4823. 'audio/wav' : 'AudioWAV',
  4824. 'audio/x-mp3-playlist' : 'AudioPlaylist',
  4825. 'video/x-dv' : 'VideoDV',
  4826. 'video/mp4' : 'VideoMPEG4',
  4827. 'video/mpeg' : 'VideoMPEG',
  4828. 'video/x-msvideo' : 'VideoAVI',
  4829. 'video/quicktime' : 'VideoMOV',
  4830. 'video/x-ms-wmv' : 'VideoWM',
  4831. 'video/x-flv' : 'VideoFlash',
  4832. 'video/x-matroska' : 'VideoMKV',
  4833. 'video/ogg' : 'VideoOGG'
  4834. },
  4835. /**
  4836. * File mimetype to file extention mapping
  4837. *
  4838. * @type Object
  4839. * @see elFinder.mimetypes.js
  4840. */
  4841. mimeTypes : {},
  4842. /**
  4843. * Ajax request data validation rules
  4844. *
  4845. * @type Object
  4846. */
  4847. rules : {
  4848. defaults : function(data) {
  4849. if (!data
  4850. || (data.added && !Array.isArray(data.added))
  4851. || (data.removed && !Array.isArray(data.removed))
  4852. || (data.changed && !Array.isArray(data.changed))) {
  4853. return false;
  4854. }
  4855. return true;
  4856. },
  4857. open : function(data) { return data && data.cwd && data.files && $.isPlainObject(data.cwd) && Array.isArray(data.files); },
  4858. tree : function(data) { return data && data.tree && Array.isArray(data.tree); },
  4859. parents : function(data) { return data && data.tree && Array.isArray(data.tree); },
  4860. tmb : function(data) { return data && data.images && ($.isPlainObject(data.images) || Array.isArray(data.images)); },
  4861. upload : function(data) { return data && ($.isPlainObject(data.added) || Array.isArray(data.added));},
  4862. search : function(data) { return data && data.files && Array.isArray(data.files); }
  4863. },
  4864. /**
  4865. * Commands costructors
  4866. *
  4867. * @type Object
  4868. */
  4869. commands : {},
  4870. /**
  4871. * Commands to add the item (space delimited)
  4872. *
  4873. * @type String
  4874. */
  4875. cmdsToAdd : 'archive duplicate extract mkdir mkfile paste rm upload',
  4876. parseUploadData : function(text) {
  4877. var self = this,
  4878. data;
  4879. if (!$.trim(text)) {
  4880. return {error : ['errResponse', 'errDataEmpty']};
  4881. }
  4882. try {
  4883. data = JSON.parse(text);
  4884. } catch (e) {
  4885. return {error : ['errResponse', 'errDataNotJSON']};
  4886. }
  4887. data = self.normalize(data);
  4888. if (!self.validResponse('upload', data)) {
  4889. return {error : (response.norError || ['errResponse'])};
  4890. }
  4891. data.removed = $.merge((data.removed || []), $.map(data.added || [], function(f) { return self.file(f.hash)? f.hash : null; }));
  4892. return data;
  4893. },
  4894. iframeCnt : 0,
  4895. uploads : {
  4896. // xhr muiti uploading flag
  4897. xhrUploading: false,
  4898. // Timer of request fail to sync
  4899. failSyncTm: null,
  4900. // current chunkfail requesting chunk
  4901. chunkfailReq: {},
  4902. // check file/dir exists
  4903. checkExists: function(files, target, fm, isDir) {
  4904. var dfrd = $.Deferred(),
  4905. names, renames = [], hashes = {}, chkFiles = [],
  4906. cancel = function() {
  4907. var i = files.length;
  4908. while (--i > -1) {
  4909. files[i]._remove = true;
  4910. }
  4911. },
  4912. resolve = function() {
  4913. dfrd.resolve(renames, hashes);
  4914. },
  4915. check = function() {
  4916. var existed = [], exists = [], i, c,
  4917. pathStr = target !== fm.cwd().hash? fm.path(target, true) + fm.option('separator', target) : '',
  4918. confirm = function(ndx) {
  4919. var last = ndx == exists.length-1,
  4920. opts = {
  4921. cssClass : 'elfinder-confirm-upload',
  4922. title : fm.i18n('cmdupload'),
  4923. text : ['errExists', pathStr + exists[ndx].name, 'confirmRepl'],
  4924. all : !last,
  4925. accept : {
  4926. label : 'btnYes',
  4927. callback : function(all) {
  4928. !last && !all
  4929. ? confirm(++ndx)
  4930. : resolve();
  4931. }
  4932. },
  4933. reject : {
  4934. label : 'btnNo',
  4935. callback : function(all) {
  4936. var i;
  4937. if (all) {
  4938. i = exists.length;
  4939. while (ndx < i--) {
  4940. files[exists[i].i]._remove = true;
  4941. }
  4942. } else {
  4943. files[exists[ndx].i]._remove = true;
  4944. }
  4945. !last && !all
  4946. ? confirm(++ndx)
  4947. : resolve();
  4948. }
  4949. },
  4950. cancel : {
  4951. label : 'btnCancel',
  4952. callback : function() {
  4953. cancel();
  4954. resolve();
  4955. }
  4956. },
  4957. buttons : [
  4958. {
  4959. label : 'btnBackup',
  4960. cssClass : 'elfinder-confirm-btn-backup',
  4961. callback : function(all) {
  4962. var i;
  4963. if (all) {
  4964. i = exists.length;
  4965. while (ndx < i--) {
  4966. renames.push(exists[i].name);
  4967. }
  4968. } else {
  4969. renames.push(exists[ndx].name);
  4970. }
  4971. !last && !all
  4972. ? confirm(++ndx)
  4973. : resolve();
  4974. }
  4975. }
  4976. ]
  4977. };
  4978. if (!isDir) {
  4979. opts.buttons.push({
  4980. label : 'btnRename' + (last? '' : 'All'),
  4981. cssClass : 'elfinder-confirm-btn-rename',
  4982. callback : function() {
  4983. renames = null;
  4984. resolve();
  4985. }
  4986. });
  4987. }
  4988. if (fm.iframeCnt > 0) {
  4989. delete opts.reject;
  4990. }
  4991. fm.confirm(opts);
  4992. };
  4993. if (! fm.file(target).read) {
  4994. // for dropbox type
  4995. resolve();
  4996. return;
  4997. }
  4998. names = $.map(files, function(file, i) { return file.name && (!fm.UA.iOS || file.name !== 'image.jpg')? {i: i, name: file.name} : null ;});
  4999. fm.request({
  5000. data : {cmd : 'ls', target : target, intersect : $.map(names, function(item) { return item.name;})},
  5001. notify : {type : 'preupload', cnt : 1, hideCnt : true},
  5002. preventDefault : true
  5003. })
  5004. .done(function(data) {
  5005. var existedArr, cwdItems;
  5006. if (data) {
  5007. if (data.error) {
  5008. cancel();
  5009. } else {
  5010. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  5011. if (data.list) {
  5012. if (Array.isArray(data.list)) {
  5013. existed = data.list || [];
  5014. } else {
  5015. existedArr = [];
  5016. existed = $.map(data.list, function(n) {
  5017. if (typeof n === 'string') {
  5018. return n;
  5019. } else {
  5020. // support to >=2.1.11 plugin Normalizer, Sanitizer
  5021. existedArr = existedArr.concat(n);
  5022. return false;
  5023. }
  5024. });
  5025. if (existedArr.length) {
  5026. existed = existed.concat(existedArr);
  5027. }
  5028. hashes = data.list;
  5029. }
  5030. exists = $.grep(names, function(name){
  5031. return $.inArray(name.name, existed) !== -1 ? true : false ;
  5032. });
  5033. if (exists.length && existed.length && target == fm.cwd().hash) {
  5034. cwdItems = $.map(fm.files(target), function(file) { return file.name; } );
  5035. if ($.grep(existed, function(n) {
  5036. return $.inArray(n, cwdItems) === -1? true : false;
  5037. }).length){
  5038. fm.sync();
  5039. }
  5040. }
  5041. }
  5042. }
  5043. }
  5044. }
  5045. if (exists.length > 0) {
  5046. confirm(0);
  5047. } else {
  5048. resolve();
  5049. }
  5050. })
  5051. .fail(function(error) {
  5052. cancel();
  5053. resolve();
  5054. error && fm.error(error);
  5055. });
  5056. };
  5057. if (fm.api >= 2.1 && typeof files[0] == 'object') {
  5058. check();
  5059. } else {
  5060. resolve();
  5061. }
  5062. return dfrd;
  5063. },
  5064. // check droped contents
  5065. checkFile : function(data, fm, target) {
  5066. if (!!data.checked || data.type == 'files') {
  5067. return data.files;
  5068. } else if (data.type == 'data') {
  5069. var dfrd = $.Deferred(),
  5070. files = [],
  5071. paths = [],
  5072. dirctorys = [],
  5073. entries = [],
  5074. processing = 0,
  5075. items,
  5076. mkdirs = [],
  5077. cancel = false,
  5078. toArray = function(list) {
  5079. return Array.prototype.slice.call(list || [], 0);
  5080. },
  5081. doScan = function(items) {
  5082. var dirReader, entry, readEntries,
  5083. entries = [],
  5084. excludes = fm.options.folderUploadExclude[fm.OS] || null,
  5085. length = items.length;
  5086. for (var i = 0; i < length; i++) {
  5087. if (cancel) {
  5088. break;
  5089. }
  5090. entry = items[i];
  5091. if (entry) {
  5092. if (entry.isFile) {
  5093. processing++;
  5094. entry.file(function (file) {
  5095. if (! excludes || ! file.name.match(excludes)) {
  5096. paths.push(entry.fullPath || '');
  5097. files.push(file);
  5098. }
  5099. processing--;
  5100. });
  5101. } else if (entry.isDirectory) {
  5102. if (fm.api >= 2.1) {
  5103. processing++;
  5104. mkdirs.push(entry.fullPath);
  5105. dirReader = entry.createReader();
  5106. entries = [];
  5107. // Call the reader.readEntries() until no more results are returned.
  5108. readEntries = function() {
  5109. dirReader.readEntries(function(results) {
  5110. if (cancel || !results.length) {
  5111. for (var i = 0; i < entries.length; i++) {
  5112. if (cancel) {
  5113. break;
  5114. }
  5115. doScan([entries[i]]);
  5116. }
  5117. processing--;
  5118. } else {
  5119. entries = entries.concat(toArray(results));
  5120. readEntries();
  5121. }
  5122. }, function(){
  5123. processing--;
  5124. });
  5125. };
  5126. readEntries(); // Start reading dirs.
  5127. }
  5128. }
  5129. }
  5130. }
  5131. }, hasDirs;
  5132. items = $.map(data.files.items, function(item){
  5133. return item.getAsEntry? item.getAsEntry() : item.webkitGetAsEntry();
  5134. });
  5135. $.each(items, function(i, item) {
  5136. if (item.isDirectory) {
  5137. hasDirs = true;
  5138. return false;
  5139. }
  5140. });
  5141. if (items.length > 0) {
  5142. fm.uploads.checkExists(items, target, fm, hasDirs).done(function(renames, hashes){
  5143. var dfds = [];
  5144. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  5145. if (renames === null) {
  5146. data.overwrite = 0;
  5147. renames = [];
  5148. }
  5149. items = $.grep(items, function(item){
  5150. var i, bak, hash, dfd, hi;
  5151. if (item.isDirectory && renames.length) {
  5152. i = $.inArray(item.name, renames);
  5153. if (i !== -1) {
  5154. renames.splice(i, 1);
  5155. bak = fm.uniqueName(item.name + fm.options.backupSuffix , null, '');
  5156. $.each(hashes, function(h, name) {
  5157. if (item.name == name) {
  5158. hash = h;
  5159. return false;
  5160. }
  5161. });
  5162. if (! hash) {
  5163. hash = fm.fileByName(item.name, target).hash;
  5164. }
  5165. fm.lockfiles({files : [hash]});
  5166. dfd = fm.request({
  5167. data : {cmd : 'rename', target : hash, name : bak},
  5168. notify : {type : 'rename', cnt : 1}
  5169. })
  5170. .fail(function(error) {
  5171. item._remove = true;
  5172. fm.sync();
  5173. })
  5174. .always(function() {
  5175. fm.unlockfiles({files : [hash]});
  5176. });
  5177. dfds.push(dfd);
  5178. }
  5179. }
  5180. return !item._remove? true : false;
  5181. });
  5182. }
  5183. $.when.apply($, dfds).done(function(){
  5184. var notifyto, msg,
  5185. id = +new Date(),
  5186. wait = function() {
  5187. if (!cancel && processing > 0) {
  5188. setTimeout(wait, 10);
  5189. } else {
  5190. notifyto && clearTimeout(notifyto);
  5191. fm.notify({type : 'readdir', id: id, cnt : -1});
  5192. if (cancel) {
  5193. dfrd.reject();
  5194. } else {
  5195. dfrd.resolve([files, paths, renames, hashes, mkdirs]);
  5196. }
  5197. }
  5198. };
  5199. if (items.length > 0) {
  5200. msg = fm.escape(items[0].name);
  5201. if (items.length > 1) {
  5202. msg += ' ... ' + items.length + fm.i18n('items');
  5203. }
  5204. notifyto = setTimeout(function() {
  5205. fm.notify({
  5206. type : 'readdir',
  5207. id : id,
  5208. cnt : 1,
  5209. hideCnt: true,
  5210. msg : fm.i18n('ntfreaddir') + ' (' + msg + ')',
  5211. cancel: function() {
  5212. cancel = true;
  5213. }
  5214. });
  5215. }, fm.options.notifyDelay);
  5216. doScan(items);
  5217. setTimeout(wait, 10);
  5218. } else {
  5219. dfrd.reject();
  5220. }
  5221. });
  5222. });
  5223. return dfrd.promise();
  5224. } else {
  5225. return dfrd.reject();
  5226. }
  5227. } else {
  5228. var ret = [];
  5229. var check = [];
  5230. var str = data.files[0];
  5231. if (data.type == 'html') {
  5232. var tmp = $("<html/>").append($.parseHTML(str.replace(/ src=/ig, ' _elfsrc='))),
  5233. atag;
  5234. $('img[_elfsrc]', tmp).each(function(){
  5235. var url, purl,
  5236. self = $(this),
  5237. pa = self.closest('a');
  5238. if (pa && pa.attr('href') && pa.attr('href').match(/\.(?:jpe?g|gif|bmp|png)/i)) {
  5239. purl = pa.attr('href');
  5240. }
  5241. url = self.attr('_elfsrc');
  5242. if (url) {
  5243. if (purl) {
  5244. $.inArray(purl, ret) == -1 && ret.push(purl);
  5245. $.inArray(url, check) == -1 && check.push(url);
  5246. } else {
  5247. $.inArray(url, ret) == -1 && ret.push(url);
  5248. }
  5249. }
  5250. // Probably it's clipboard data
  5251. if (ret.length === 1 && ret[0].match(/^data:image\/png/)) {
  5252. data.clipdata = true;
  5253. }
  5254. });
  5255. atag = $('a[href]', tmp);
  5256. atag.each(function(){
  5257. var loc,
  5258. parseUrl = function(url) {
  5259. var a = document.createElement('a');
  5260. a.href = url;
  5261. return a;
  5262. };
  5263. if ($(this).text()) {
  5264. loc = parseUrl($(this).attr('href'));
  5265. if (loc.href && (atag.length === 1 || ! loc.pathname.match(/(?:\.html?|\/[^\/.]*)$/i))) {
  5266. if ($.inArray(loc.href, ret) == -1 && $.inArray(loc.href, check) == -1) ret.push(loc.href);
  5267. }
  5268. }
  5269. });
  5270. } else {
  5271. var regex, m, url;
  5272. regex = /(http[^<>"{}|\\^\[\]`\s]+)/ig;
  5273. while (m = regex.exec(str)) {
  5274. url = m[1].replace(/&amp;/g, '&');
  5275. if ($.inArray(url, ret) == -1) ret.push(url);
  5276. }
  5277. }
  5278. return ret;
  5279. }
  5280. },
  5281. // upload transport using XMLHttpRequest
  5282. xhr : function(data, fm) {
  5283. var self = fm ? fm : this,
  5284. node = self.getUI(),
  5285. xhr = new XMLHttpRequest(),
  5286. notifyto = null, notifyto2 = null,
  5287. dataChecked = data.checked,
  5288. isDataType = (data.isDataType || data.type == 'data'),
  5289. target = (data.target || self.cwd().hash),
  5290. dropEvt = (data.dropEvt || null),
  5291. chunkEnable = (self.option('uploadMaxConn', target) != -1),
  5292. multiMax = Math.min(5, Math.max(1, self.option('uploadMaxConn', target))),
  5293. retryWait = 10000, // 10 sec
  5294. retryMax = 30, // 10 sec * 30 = 300 secs (Max 5 mins)
  5295. retry = 0,
  5296. getFile = function(files) {
  5297. var dfd = $.Deferred(),
  5298. file;
  5299. if (files.promise) {
  5300. files.always(function(f) {
  5301. dfd.resolve(Array.isArray(f) && f.length? (isDataType? f[0][0] : f[0]) : {});
  5302. });
  5303. } else {
  5304. dfd.resolve(files.length? (isDataType? files[0][0] : files[0]) : {});
  5305. }
  5306. return dfd;
  5307. },
  5308. dfrd = $.Deferred()
  5309. .fail(function(error) {
  5310. var userAbort;
  5311. if (error === 'userabort') {
  5312. userAbort = true;
  5313. error = void 0;
  5314. }
  5315. if (files && (self.uploads.xhrUploading || userAbort)) {
  5316. // send request om fail
  5317. getFile(files).done(function(file) {
  5318. if (! file._cid) {
  5319. // send sync request
  5320. self.uploads.failSyncTm && clearTimeout(self.uploads.failSyncTm);
  5321. self.uploads.failSyncTm = setTimeout(function() {
  5322. self.sync(target);
  5323. }, 1000);
  5324. } else if (! self.uploads.chunkfailReq[file._cid]) {
  5325. // send chunkfail request
  5326. self.uploads.chunkfailReq[file._cid] = true;
  5327. setTimeout(function() {
  5328. fm.request({
  5329. data : {
  5330. cmd: 'upload',
  5331. target: target,
  5332. chunk: file._chunk,
  5333. cid: file._cid,
  5334. upload: ['chunkfail'],
  5335. mimes: 'chunkfail'
  5336. },
  5337. options : {
  5338. type: 'post',
  5339. url: self.uploadURL
  5340. },
  5341. preventDefault: true
  5342. }).always(function() {
  5343. delete self.uploads.chunkfailReq[file._chunk];
  5344. });
  5345. }, 1000);
  5346. }
  5347. });
  5348. }
  5349. !userAbort && self.sync();
  5350. self.uploads.xhrUploading = false;
  5351. files = null;
  5352. error && self.error(error);
  5353. })
  5354. .done(function(data) {
  5355. self.uploads.xhrUploading = false;
  5356. files = null;
  5357. if (data) {
  5358. self.currentReqCmd = 'upload';
  5359. data.warning && self.error(data.warning);
  5360. self.updateCache(data);
  5361. data.removed && data.removed.length && self.remove(data);
  5362. data.added && data.added.length && self.add(data);
  5363. data.changed && data.changed.length && self.change(data);
  5364. self.trigger('upload', data, false);
  5365. self.trigger('uploaddone');
  5366. data.sync && self.sync();
  5367. data.debug && fm.debug('backend-debug', data);
  5368. }
  5369. })
  5370. .always(function() {
  5371. self.abortXHR(xhr);
  5372. // unregist fnAbort function
  5373. node.off('uploadabort', fnAbort);
  5374. $(window).off('unload', fnAbort);
  5375. notifyto && clearTimeout(notifyto);
  5376. notifyto2 && clearTimeout(notifyto2);
  5377. dataChecked && !data.multiupload && checkNotify() && self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  5378. chunkMerge && notifyElm.children('.elfinder-notify-chunkmerge').length && self.notify({type : 'chunkmerge', cnt : -1});
  5379. }),
  5380. formData = new FormData(),
  5381. files = data.input ? data.input.files : self.uploads.checkFile(data, self, target),
  5382. cnt = data.checked? (isDataType? files[0].length : files.length) : files.length,
  5383. loaded = 0,
  5384. prev = 0,
  5385. filesize = 0,
  5386. notify = false,
  5387. notifyElm = self.ui.notify,
  5388. cancelBtn = true,
  5389. abort = false,
  5390. checkNotify = function() {
  5391. if (!notify && (ntfUpload = notifyElm.children('.elfinder-notify-upload')).length) {
  5392. notify = true;
  5393. }
  5394. return notify;
  5395. },
  5396. fnAbort = function(e, error) {
  5397. abort = true;
  5398. self.abortXHR(xhr, { quiet: true, abort: true });
  5399. dfrd.reject(error);
  5400. if (checkNotify()) {
  5401. self.notify({type : 'upload', cnt : ntfUpload.data('cnt') * -1, progress : 0, size : 0});
  5402. }
  5403. },
  5404. cancelToggle = function(show) {
  5405. ntfUpload.children('.elfinder-notify-cancel')[show? 'show':'hide']();
  5406. },
  5407. startNotify = function(size) {
  5408. if (!size) size = filesize;
  5409. return setTimeout(function() {
  5410. notify = true;
  5411. self.notify({type : 'upload', cnt : cnt, progress : loaded - prev, size : size,
  5412. cancel: function() {
  5413. node.trigger('uploadabort', 'userabort');
  5414. }
  5415. });
  5416. ntfUpload = notifyElm.children('.elfinder-notify-upload');
  5417. prev = loaded;
  5418. if (data.multiupload) {
  5419. cancelBtn && cancelToggle(true);
  5420. } else {
  5421. cancelToggle(cancelBtn && loaded < size);
  5422. }
  5423. }, self.options.notifyDelay);
  5424. },
  5425. doRetry = function() {
  5426. if (retry++ <= retryMax) {
  5427. if (checkNotify() && prev) {
  5428. self.notify({type : 'upload', cnt : 0, progress : 0, size : prev});
  5429. }
  5430. self.abortXHR(xhr, { quiet: true });
  5431. prev = loaded = 0;
  5432. setTimeout(function() {
  5433. var reqId;
  5434. if (! abort) {
  5435. xhr.open('POST', self.uploadURL, true);
  5436. if (self.api >= 2.1029) {
  5437. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
  5438. (typeof formData['delete'] === 'function') && formData['delete']('reqid');
  5439. formData.append('reqid', reqId);
  5440. xhr._requestId = reqId;
  5441. }
  5442. xhr.send(formData);
  5443. }
  5444. }, retryWait);
  5445. } else {
  5446. node.trigger('uploadabort', ['errAbort', 'errTimeout']);
  5447. }
  5448. },
  5449. progress = function() {
  5450. var node;
  5451. if (notify) {
  5452. dfrd.notifyWith(ntfUpload, [{
  5453. cnt: ntfUpload.data('cnt'),
  5454. progress: ntfUpload.data('progress'),
  5455. total: ntfUpload.data('total')
  5456. }]);
  5457. }
  5458. },
  5459. renames = (data.renames || null),
  5460. hashes = (data.hashes || null),
  5461. chunkMerge = false,
  5462. ntfUpload = $();
  5463. // regist fnAbort function
  5464. node.one('uploadabort', fnAbort);
  5465. $(window).one('unload.' + fm.namespace, fnAbort);
  5466. !chunkMerge && (prev = loaded);
  5467. if (!isDataType && !cnt) {
  5468. return dfrd.reject(['errUploadNoFiles']);
  5469. }
  5470. xhr.addEventListener('error', function() {
  5471. if (xhr.status == 0) {
  5472. if (abort) {
  5473. dfrd.reject();
  5474. } else {
  5475. // ff bug while send zero sized file
  5476. // for safari - send directory
  5477. if (!isDataType && data.files && $.grep(data.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) {
  5478. errors.push('errFolderUpload');
  5479. dfrd.reject(['errAbort', 'errFolderUpload']);
  5480. } else if (data.input && $.grep(data.input.files, function(f){return ! f.type && f.size === (self.UA.Safari? 1802 : 0)? true : false;}).length) {
  5481. dfrd.reject(['errUploadNoFiles']);
  5482. } else {
  5483. doRetry();
  5484. }
  5485. }
  5486. } else {
  5487. node.trigger('uploadabort', 'errConnect');
  5488. }
  5489. }, false);
  5490. xhr.addEventListener('load', function(e) {
  5491. var status = xhr.status, res, curr = 0, error = '';
  5492. if (status >= 400) {
  5493. if (status > 500) {
  5494. error = 'errResponse';
  5495. } else {
  5496. error = ['errResponse', 'errServerError'];
  5497. }
  5498. } else {
  5499. if (!xhr.responseText) {
  5500. error = ['errResponse', 'errDataEmpty'];
  5501. }
  5502. }
  5503. if (error) {
  5504. node.trigger('uploadabort');
  5505. getFile(files).done(function(file) {
  5506. return dfrd.reject(file._cid? null : error);
  5507. });
  5508. }
  5509. loaded = filesize;
  5510. if (checkNotify() && (curr = loaded - prev)) {
  5511. self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
  5512. progress();
  5513. }
  5514. res = self.parseUploadData(xhr.responseText);
  5515. // chunked upload commit
  5516. if (res._chunkmerged) {
  5517. formData = new FormData();
  5518. var _file = [{_chunkmerged: res._chunkmerged, _name: res._name, _mtime: res._mtime}];
  5519. chunkMerge = true;
  5520. node.off('uploadabort', fnAbort);
  5521. notifyto2 = setTimeout(function() {
  5522. self.notify({type : 'chunkmerge', cnt : 1});
  5523. }, self.options.notifyDelay);
  5524. isDataType? send(_file, files[1]) : send(_file);
  5525. return;
  5526. }
  5527. res._multiupload = data.multiupload? true : false;
  5528. if (res.error) {
  5529. self.trigger('uploadfail', res);
  5530. if (res._chunkfailure || res._multiupload) {
  5531. abort = true;
  5532. self.uploads.xhrUploading = false;
  5533. notifyto && clearTimeout(notifyto);
  5534. if (ntfUpload.length) {
  5535. self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  5536. dfrd.reject(res.error);
  5537. } else {
  5538. // for multi connection
  5539. dfrd.reject();
  5540. }
  5541. } else {
  5542. dfrd.reject(res.error);
  5543. }
  5544. } else {
  5545. dfrd.resolve(res);
  5546. }
  5547. }, false);
  5548. xhr.upload.addEventListener('loadstart', function(e) {
  5549. if (!chunkMerge && e.lengthComputable) {
  5550. loaded = e.loaded;
  5551. retry && (loaded = 0);
  5552. filesize = e.total;
  5553. if (!loaded) {
  5554. loaded = parseInt(filesize * 0.05);
  5555. }
  5556. if (checkNotify()) {
  5557. self.notify({type : 'upload', cnt : 0, progress : loaded - prev, size : data.multiupload? 0 : filesize});
  5558. prev = loaded;
  5559. progress();
  5560. }
  5561. }
  5562. }, false);
  5563. xhr.upload.addEventListener('progress', function(e) {
  5564. var curr;
  5565. if (e.lengthComputable && !chunkMerge && xhr.readyState < 2) {
  5566. loaded = e.loaded;
  5567. // to avoid strange bug in safari (not in chrome) with drag&drop.
  5568. // bug: macos finder opened in any folder,
  5569. // reset safari cache (option+command+e), reload elfinder page,
  5570. // drop file from finder
  5571. // on first attempt request starts (progress callback called ones) but never ends.
  5572. // any next drop - successfull.
  5573. if (!data.checked && loaded > 0 && !notifyto) {
  5574. notifyto = startNotify(xhr._totalSize - loaded);
  5575. }
  5576. if (!filesize) {
  5577. filesize = e.total;
  5578. if (!loaded) {
  5579. loaded = parseInt(filesize * 0.05);
  5580. }
  5581. }
  5582. curr = loaded - prev;
  5583. if (checkNotify() && (curr/e.total) >= 0.05) {
  5584. self.notify({type : 'upload', cnt : 0, progress : curr, size : 0});
  5585. prev = loaded;
  5586. progress();
  5587. }
  5588. if (! data.multiupload && loaded >= filesize) {
  5589. cancelBtn = false;
  5590. cancelToggle(false);
  5591. }
  5592. }
  5593. }, false);
  5594. var send = function(files, paths){
  5595. var size = 0,
  5596. fcnt = 1,
  5597. sfiles = [],
  5598. c = 0,
  5599. total = cnt,
  5600. maxFileSize,
  5601. totalSize = 0,
  5602. chunked = [],
  5603. chunkID = new Date().getTime().toString().substr(-9), // for take care of the 32bit backend system
  5604. BYTES_PER_CHUNK = Math.min((fm.uplMaxSize? fm.uplMaxSize : 2097152) - 8190, fm.options.uploadMaxChunkSize), // uplMaxSize margin 8kb or options.uploadMaxChunkSize
  5605. blobSlice = chunkEnable? false : '',
  5606. blobSize, blobMtime, i, start, end, chunks, blob, chunk, added, done, last, failChunk,
  5607. multi = function(files, num){
  5608. var sfiles = [], cid, sfilesLen = 0, cancelChk;
  5609. if (!abort) {
  5610. while(files.length && sfiles.length < num) {
  5611. sfiles.push(files.shift());
  5612. }
  5613. sfilesLen = sfiles.length;
  5614. if (sfilesLen) {
  5615. cancelChk = sfilesLen;
  5616. for (var i=0; i < sfilesLen; i++) {
  5617. if (abort) {
  5618. break;
  5619. }
  5620. cid = isDataType? (sfiles[i][0][0]._cid || null) : (sfiles[i][0]._cid || null);
  5621. if (!!failChunk[cid]) {
  5622. last--;
  5623. continue;
  5624. }
  5625. fm.exec('upload', {
  5626. type: data.type,
  5627. isDataType: isDataType,
  5628. files: sfiles[i],
  5629. checked: true,
  5630. target: target,
  5631. dropEvt: dropEvt,
  5632. renames: renames,
  5633. hashes: hashes,
  5634. multiupload: true,
  5635. overwrite: data.overwrite === 0? 0 : void 0
  5636. }, void 0, target)
  5637. .fail(function(error) {
  5638. if (error && error === 'No such command') {
  5639. abort = true;
  5640. fm.error(['errUpload', 'errPerm']);
  5641. }
  5642. if (cid) {
  5643. failChunk[cid] = true;
  5644. }
  5645. })
  5646. .always(function(e) {
  5647. if (e && e.added) added = $.merge(added, e.added);
  5648. if (last <= ++done) {
  5649. fm.trigger('multiupload', {added: added});
  5650. notifyto && clearTimeout(notifyto);
  5651. if (checkNotify()) {
  5652. self.notify({type : 'upload', cnt : -cnt, progress : 0, size : 0});
  5653. }
  5654. }
  5655. if (files.length) {
  5656. multi(files, 1); // Next one
  5657. } else {
  5658. if (--cancelChk <= 1) {
  5659. cancelBtn = false;
  5660. cancelToggle(false);
  5661. }
  5662. }
  5663. });
  5664. }
  5665. }
  5666. }
  5667. if (sfiles.length < 1 || abort) {
  5668. if (abort) {
  5669. notifyto && clearTimeout(notifyto);
  5670. if (cid) {
  5671. failChunk[cid] = true;
  5672. }
  5673. dfrd.reject();
  5674. } else {
  5675. dfrd.resolve();
  5676. self.uploads.xhrUploading = false;
  5677. }
  5678. }
  5679. },
  5680. check = function(){
  5681. if (!self.uploads.xhrUploading) {
  5682. self.uploads.xhrUploading = true;
  5683. multi(sfiles, multiMax); // Max connection: 3
  5684. } else {
  5685. setTimeout(function(){ check(); }, 100);
  5686. }
  5687. },
  5688. reqId;
  5689. if (! dataChecked && (isDataType || data.type == 'files')) {
  5690. if (! (maxFileSize = fm.option('uploadMaxSize', target))) {
  5691. maxFileSize = 0;
  5692. }
  5693. for (i=0; i < files.length; i++) {
  5694. try {
  5695. blob = files[i];
  5696. blobSize = blob.size;
  5697. if (blobSlice === false) {
  5698. blobSlice = '';
  5699. if (self.api >= 2.1) {
  5700. if ('slice' in blob) {
  5701. blobSlice = 'slice';
  5702. } else if ('mozSlice' in blob) {
  5703. blobSlice = 'mozSlice';
  5704. } else if ('webkitSlice' in blob) {
  5705. blobSlice = 'webkitSlice';
  5706. }
  5707. }
  5708. }
  5709. } catch(e) {
  5710. cnt--;
  5711. total--;
  5712. continue;
  5713. }
  5714. // file size check
  5715. if ((maxFileSize && blobSize > maxFileSize) || (!blobSlice && fm.uplMaxSize && blobSize > fm.uplMaxSize)) {
  5716. self.error(self.i18n('errUploadFile', blob.name) + ' ' + self.i18n('errUploadFileSize'));
  5717. cnt--;
  5718. total--;
  5719. continue;
  5720. }
  5721. // file mime check
  5722. if (blob.type && ! self.uploadMimeCheck(blob.type, target)) {
  5723. self.error(self.i18n('errUploadFile', blob.name) + ' ' + self.i18n('errUploadMime') + ' (' + self.escape(blob.type) + ')');
  5724. cnt--;
  5725. total--;
  5726. continue;
  5727. }
  5728. if (blobSlice && blobSize > BYTES_PER_CHUNK) {
  5729. start = 0;
  5730. end = BYTES_PER_CHUNK;
  5731. chunks = -1;
  5732. total = Math.floor(blobSize / BYTES_PER_CHUNK);
  5733. blobMtime = blob.lastModified? Math.round(blob.lastModified/1000) : 0;
  5734. totalSize += blobSize;
  5735. chunked[chunkID] = 0;
  5736. while(start <= blobSize) {
  5737. chunk = blob[blobSlice](start, end);
  5738. chunk._chunk = blob.name + '.' + (++chunks) + '_' + total + '.part';
  5739. chunk._cid = chunkID;
  5740. chunk._range = start + ',' + chunk.size + ',' + blobSize;
  5741. chunk._mtime = blobMtime;
  5742. chunked[chunkID]++;
  5743. if (size) {
  5744. c++;
  5745. }
  5746. if (typeof sfiles[c] == 'undefined') {
  5747. sfiles[c] = [];
  5748. if (isDataType) {
  5749. sfiles[c][0] = [];
  5750. sfiles[c][1] = [];
  5751. }
  5752. }
  5753. size = BYTES_PER_CHUNK;
  5754. fcnt = 1;
  5755. if (isDataType) {
  5756. sfiles[c][0].push(chunk);
  5757. sfiles[c][1].push(paths[i]);
  5758. } else {
  5759. sfiles[c].push(chunk);
  5760. }
  5761. start = end;
  5762. end = start + BYTES_PER_CHUNK;
  5763. }
  5764. if (chunk == null) {
  5765. self.error(self.i18n('errUploadFile', blob.name) + ' ' + self.i18n('errUploadFileSize'));
  5766. cnt--;
  5767. total--;
  5768. } else {
  5769. total += chunks;
  5770. size = 0;
  5771. fcnt = 1;
  5772. c++;
  5773. }
  5774. continue;
  5775. }
  5776. if ((fm.uplMaxSize && size + blobSize >= fm.uplMaxSize) || fcnt > fm.uplMaxFile) {
  5777. size = 0;
  5778. fcnt = 1;
  5779. c++;
  5780. }
  5781. if (typeof sfiles[c] == 'undefined') {
  5782. sfiles[c] = [];
  5783. if (isDataType) {
  5784. sfiles[c][0] = [];
  5785. sfiles[c][1] = [];
  5786. }
  5787. }
  5788. if (isDataType) {
  5789. sfiles[c][0].push(blob);
  5790. sfiles[c][1].push(paths[i]);
  5791. } else {
  5792. sfiles[c].push(blob);
  5793. }
  5794. size += blobSize;
  5795. totalSize += blobSize;
  5796. fcnt++;
  5797. }
  5798. if (sfiles.length == 0) {
  5799. // no data
  5800. data.checked = true;
  5801. return false;
  5802. }
  5803. if (sfiles.length > 1) {
  5804. // multi upload
  5805. notifyto = startNotify(totalSize);
  5806. added = [];
  5807. done = 0;
  5808. last = sfiles.length;
  5809. failChunk = [];
  5810. check();
  5811. return true;
  5812. }
  5813. // single upload
  5814. if (isDataType) {
  5815. files = sfiles[0][0];
  5816. paths = sfiles[0][1];
  5817. } else {
  5818. files = sfiles[0];
  5819. }
  5820. }
  5821. if (!dataChecked) {
  5822. if (!fm.UA.Safari || !data.files) {
  5823. notifyto = startNotify(totalSize);
  5824. } else {
  5825. xhr._totalSize = totalSize;
  5826. }
  5827. }
  5828. dataChecked = true;
  5829. if (! files.length) {
  5830. dfrd.reject(['errUploadNoFiles']);
  5831. }
  5832. xhr.open('POST', self.uploadURL, true);
  5833. // set request headers
  5834. if (fm.customHeaders) {
  5835. $.each(fm.customHeaders, function(key) {
  5836. xhr.setRequestHeader(key, this);
  5837. });
  5838. }
  5839. // set xhrFields
  5840. if (fm.xhrFields) {
  5841. $.each(fm.xhrFields, function(key) {
  5842. if (key in xhr) {
  5843. xhr[key] = this;
  5844. }
  5845. });
  5846. }
  5847. if (self.api >= 2.1029) {
  5848. // request ID
  5849. reqId = (+ new Date()).toString(16) + Math.floor(1000 * Math.random()).toString(16);
  5850. formData.append('reqid', reqId);
  5851. xhr._requestId = reqId;
  5852. }
  5853. formData.append('cmd', 'upload');
  5854. formData.append(self.newAPI ? 'target' : 'current', target);
  5855. if (renames && renames.length) {
  5856. $.each(renames, function(i, v) {
  5857. formData.append('renames[]', v);
  5858. });
  5859. formData.append('suffix', fm.options.backupSuffix);
  5860. }
  5861. if (hashes) {
  5862. $.each(hashes, function(i, v) {
  5863. formData.append('hashes['+ i +']', v);
  5864. });
  5865. }
  5866. $.each(self.customData, function(key, val) {
  5867. formData.append(key, val);
  5868. });
  5869. $.each(self.options.onlyMimes, function(i, mime) {
  5870. formData.append('mimes[]', mime);
  5871. });
  5872. $.each(files, function(i, file) {
  5873. if (file._chunkmerged) {
  5874. formData.append('chunk', file._chunkmerged);
  5875. formData.append('upload[]', file._name);
  5876. formData.append('mtime[]', file._mtime);
  5877. } else {
  5878. if (file._chunkfail) {
  5879. formData.append('upload[]', 'chunkfail');
  5880. formData.append('mimes', 'chunkfail');
  5881. } else {
  5882. formData.append('upload[]', file);
  5883. if (data.clipdata) {
  5884. data.overwrite = 0;
  5885. formData.append('name[]', fm.date(fm.nonameDateFormat) + '.png');
  5886. }
  5887. if (fm.UA.iOS) {
  5888. if (file.name.match(/^image\.jpe?g$/i)) {
  5889. data.overwrite = 0;
  5890. formData.append('name[]', fm.date(fm.nonameDateFormat) + '.jpg');
  5891. } else if (file.name.match(/^capturedvideo\.mov$/i)) {
  5892. data.overwrite = 0;
  5893. formData.append('name[]', fm.date(fm.nonameDateFormat) + '.mov');
  5894. }
  5895. }
  5896. }
  5897. if (file._chunk) {
  5898. formData.append('chunk', file._chunk);
  5899. formData.append('cid' , file._cid);
  5900. formData.append('range', file._range);
  5901. formData.append('mtime[]', file._mtime);
  5902. } else {
  5903. formData.append('mtime[]', file.lastModified? Math.round(file.lastModified/1000) : 0);
  5904. }
  5905. }
  5906. });
  5907. if (isDataType) {
  5908. $.each(paths, function(i, path) {
  5909. formData.append('upload_path[]', path);
  5910. });
  5911. }
  5912. if (data.overwrite === 0) {
  5913. formData.append('overwrite', 0);
  5914. }
  5915. // send int value that which meta key was pressed when dropped as `dropWith`
  5916. if (dropEvt) {
  5917. formData.append('dropWith', parseInt(
  5918. (dropEvt.altKey ? '1' : '0')+
  5919. (dropEvt.ctrlKey ? '1' : '0')+
  5920. (dropEvt.metaKey ? '1' : '0')+
  5921. (dropEvt.shiftKey? '1' : '0'), 2));
  5922. }
  5923. xhr.send(formData);
  5924. return true;
  5925. };
  5926. if (! isDataType) {
  5927. if (files.length > 0) {
  5928. if (! data.clipdata && renames == null) {
  5929. var mkdirs = [],
  5930. paths = [],
  5931. excludes = fm.options.folderUploadExclude[fm.OS] || null;
  5932. $.each(files, function(i, file) {
  5933. var relPath = file.webkitRelativePath || file.relativePath || '',
  5934. idx, rootDir;
  5935. if (! relPath) {
  5936. return false;
  5937. }
  5938. if (excludes && file.name.match(excludes)) {
  5939. file._remove = true;
  5940. relPath = void(0);
  5941. } else {
  5942. relPath = relPath.replace(/\/[^\/]*$/, '');
  5943. if (relPath && $.inArray(relPath, mkdirs) === -1) {
  5944. mkdirs.push(relPath);
  5945. // checking the root directory to supports <input type="file" webkitdirectory> see #2378
  5946. idx = relPath.indexOf('/');
  5947. if (idx !== -1 && (rootDir = relPath.substr(0, idx)) && $.inArray(rootDir, mkdirs) === -1) {
  5948. mkdirs.unshift(rootDir);
  5949. }
  5950. }
  5951. }
  5952. paths.push(relPath);
  5953. });
  5954. renames = [];
  5955. hashes = {};
  5956. if (mkdirs.length) {
  5957. (function() {
  5958. var checkDirs = $.map(mkdirs, function(name) { return name.indexOf('/') === -1 ? {name: name} : null;}),
  5959. cancelDirs = [];
  5960. fm.uploads.checkExists(checkDirs, target, fm, true).done(
  5961. function(res, res2) {
  5962. var dfds = [], dfd, bak, hash;
  5963. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  5964. cancelDirs = $.map(checkDirs, function(dir) { return dir._remove? dir.name : null ;} );
  5965. checkDirs = $.grep(checkDirs, function(dir) { return !dir._remove? true : false ;} );
  5966. }
  5967. if (cancelDirs.length) {
  5968. $.each(paths.concat(), function(i, path) {
  5969. if ($.inArray(path, cancelDirs) === 0) {
  5970. files[i]._remove = true;
  5971. delete paths[i];
  5972. }
  5973. });
  5974. }
  5975. files = $.grep(files, function(file) { return file._remove? false : true; });
  5976. paths = $.grep(paths, function(path) { return path === void 0 ? false : true; });
  5977. if (checkDirs.length) {
  5978. dfd = $.Deferred();
  5979. if (res.length) {
  5980. $.each(res, function(i, existName) {
  5981. // backup
  5982. bak = fm.uniqueName(existName + fm.options.backupSuffix , null, '');
  5983. $.each(res2, function(h, name) {
  5984. if (res[0] == name) {
  5985. hash = h;
  5986. return false;
  5987. }
  5988. });
  5989. if (! hash) {
  5990. hash = fm.fileByName(res[0], target).hash;
  5991. }
  5992. fm.lockfiles({files : [hash]});
  5993. dfds.push(
  5994. fm.request({
  5995. data : {cmd : 'rename', target : hash, name : bak},
  5996. notify : {type : 'rename', cnt : 1}
  5997. })
  5998. .fail(function(error) {
  5999. dfrd.reject(error);
  6000. fm.sync();
  6001. })
  6002. .always(function() {
  6003. fm.unlockfiles({files : [hash]});
  6004. })
  6005. );
  6006. });
  6007. } else {
  6008. dfds.push(null);
  6009. }
  6010. $.when.apply($, dfds).done(function() {
  6011. // ensure directories
  6012. fm.request({
  6013. data : {cmd : 'mkdir', target : target, dirs : mkdirs},
  6014. notify : {type : 'mkdir', cnt : mkdirs.length},
  6015. preventFail: true
  6016. })
  6017. .fail(function(error) {
  6018. error = error || ['errUnknown'];
  6019. if (error[0] === 'errCmdParams') {
  6020. multiMax = 1;
  6021. } else {
  6022. multiMax = 0;
  6023. dfrd.reject(error);
  6024. }
  6025. })
  6026. .done(function(data) {
  6027. if (data.hashes) {
  6028. paths = $.map(paths.concat(), function(p) {
  6029. if (p === '') {
  6030. return target;
  6031. } else {
  6032. return data.hashes['/' + p];
  6033. }
  6034. });
  6035. }
  6036. })
  6037. .always(function(data) {
  6038. if (multiMax) {
  6039. isDataType = true;
  6040. if (! send(files, paths)) {
  6041. dfrd.reject();
  6042. }
  6043. }
  6044. });
  6045. });
  6046. } else {
  6047. dfrd.reject();
  6048. }
  6049. }
  6050. );
  6051. })();
  6052. } else {
  6053. fm.uploads.checkExists(files, target, fm).done(
  6054. function(res, res2){
  6055. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  6056. hashes = res2;
  6057. if (res === null) {
  6058. data.overwrite = 0;
  6059. } else {
  6060. renames = res;
  6061. }
  6062. files = $.grep(files, function(file){return !file._remove? true : false ;});
  6063. }
  6064. cnt = files.length;
  6065. if (cnt > 0) {
  6066. if (! send(files)) {
  6067. dfrd.reject();
  6068. }
  6069. } else {
  6070. dfrd.reject();
  6071. }
  6072. }
  6073. );
  6074. }
  6075. } else {
  6076. if (! send(files)) {
  6077. dfrd.reject();
  6078. }
  6079. }
  6080. } else {
  6081. dfrd.reject();
  6082. }
  6083. } else {
  6084. if (dataChecked) {
  6085. send(files[0], files[1]);
  6086. } else {
  6087. files.done(function(result) { // result: [files, paths, renames, hashes, mkdirs]
  6088. renames = [];
  6089. cnt = result[0].length;
  6090. if (cnt) {
  6091. if (result[4] && result[4].length) {
  6092. // ensure directories
  6093. fm.request({
  6094. data : {cmd : 'mkdir', target : target, dirs : result[4]},
  6095. notify : {type : 'mkdir', cnt : result[4].length},
  6096. preventFail: true
  6097. })
  6098. .fail(function(error) {
  6099. error = error || ['errUnknown'];
  6100. if (error[0] === 'errCmdParams') {
  6101. multiMax = 1;
  6102. } else {
  6103. multiMax = 0;
  6104. dfrd.reject(error);
  6105. }
  6106. })
  6107. .done(function(data) {
  6108. if (data.hashes) {
  6109. result[1] = $.map(result[1], function(p) {
  6110. p = p.replace(/\/[^\/]*$/, '');
  6111. if (p === '') {
  6112. return target;
  6113. } else {
  6114. return data.hashes[p];
  6115. }
  6116. });
  6117. }
  6118. })
  6119. .always(function(data) {
  6120. if (multiMax) {
  6121. renames = result[2];
  6122. hashes = result[3];
  6123. send(result[0], result[1]);
  6124. }
  6125. });
  6126. return;
  6127. } else {
  6128. result[1] = $.map(result[1], function() { return target; });
  6129. }
  6130. renames = result[2];
  6131. hashes = result[3];
  6132. send(result[0], result[1]);
  6133. } else {
  6134. dfrd.reject(['errUploadNoFiles']);
  6135. }
  6136. }).fail(function(){
  6137. dfrd.reject();
  6138. });
  6139. }
  6140. }
  6141. return dfrd;
  6142. },
  6143. // upload transport using iframe
  6144. iframe : function(data, fm) {
  6145. var self = fm ? fm : this,
  6146. input = data.input? data.input : false,
  6147. files = !input ? self.uploads.checkFile(data, self) : false,
  6148. dfrd = $.Deferred()
  6149. .fail(function(error) {
  6150. error && self.error(error);
  6151. }),
  6152. name = 'iframe-'+fm.namespace+(++self.iframeCnt),
  6153. 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>'),
  6154. msie = this.UA.IE,
  6155. // clear timeouts, close notification dialog, remove form/iframe
  6156. onload = function() {
  6157. abortto && clearTimeout(abortto);
  6158. notifyto && clearTimeout(notifyto);
  6159. notify && self.notify({type : 'upload', cnt : -cnt});
  6160. setTimeout(function() {
  6161. msie && $('<iframe src="javascript:false;"/>').appendTo(form);
  6162. form.remove();
  6163. iframe.remove();
  6164. }, 100);
  6165. },
  6166. iframe = $('<iframe src="'+(msie ? 'javascript:false;' : 'about:blank')+'" name="'+name+'" style="position:absolute;left:-1000px;top:-1000px" />')
  6167. .on('load', function() {
  6168. iframe.off('load')
  6169. .on('load', function() {
  6170. onload();
  6171. // data will be processed in callback response or window onmessage
  6172. dfrd.resolve();
  6173. });
  6174. // notify dialog
  6175. notifyto = setTimeout(function() {
  6176. notify = true;
  6177. self.notify({type : 'upload', cnt : cnt});
  6178. }, self.options.notifyDelay);
  6179. // emulate abort on timeout
  6180. if (self.options.iframeTimeout > 0) {
  6181. abortto = setTimeout(function() {
  6182. onload();
  6183. dfrd.reject([errors.connect, errors.timeout]);
  6184. }, self.options.iframeTimeout);
  6185. }
  6186. form.submit();
  6187. }),
  6188. target = (data.target || self.cwd().hash),
  6189. names = [],
  6190. dfds = [],
  6191. renames = [],
  6192. hashes = {},
  6193. cnt, notify, notifyto, abortto;
  6194. if (files && files.length) {
  6195. $.each(files, function(i, val) {
  6196. form.append('<input type="hidden" name="upload[]" value="'+val+'"/>');
  6197. });
  6198. cnt = 1;
  6199. } else if (input && $(input).is(':file') && $(input).val()) {
  6200. if (fm.options.overwriteUploadConfirm && fm.option('uploadOverwrite', target)) {
  6201. names = input.files? input.files : [{ name: $(input).val().replace(/^(?:.+[\\\/])?([^\\\/]+)$/, '$1') }];
  6202. //names = $.map(names, function(file){return file.name? { name: file.name } : null ;});
  6203. dfds.push(self.uploads.checkExists(names, target, self).done(
  6204. function(res, res2){
  6205. hashes = res2;
  6206. if (res === null) {
  6207. data.overwrite = 0;
  6208. } else{
  6209. renames = res;
  6210. cnt = $.grep(names, function(file){return !file._remove? true : false ;}).length;
  6211. if (cnt != names.length) {
  6212. cnt = 0;
  6213. }
  6214. }
  6215. }
  6216. ));
  6217. }
  6218. cnt = input.files ? input.files.length : 1;
  6219. form.append(input);
  6220. } else {
  6221. return dfrd.reject();
  6222. }
  6223. $.when.apply($, dfds).done(function() {
  6224. if (cnt < 1) {
  6225. return dfrd.reject();
  6226. }
  6227. form.append('<input type="hidden" name="'+(self.newAPI ? 'target' : 'current')+'" value="'+target+'"/>')
  6228. .append('<input type="hidden" name="html" value="1"/>')
  6229. .append('<input type="hidden" name="node" value="'+self.id+'"/>')
  6230. .append($(input).attr('name', 'upload[]'));
  6231. if (renames.length > 0) {
  6232. $.each(renames, function(i, rename) {
  6233. form.append('<input type="hidden" name="renames[]" value="'+self.escape(rename)+'"/>');
  6234. });
  6235. form.append('<input type="hidden" name="suffix" value="'+fm.options.backupSuffix+'"/>');
  6236. }
  6237. if (hashes) {
  6238. $.each(renames, function(i, v) {
  6239. form.append('<input type="hidden" name="['+i+']" value="'+self.escape(v)+'"/>');
  6240. });
  6241. }
  6242. if (data.overwrite === 0) {
  6243. form.append('<input type="hidden" name="overwrite" value="0"/>');
  6244. }
  6245. $.each(self.options.onlyMimes||[], function(i, mime) {
  6246. form.append('<input type="hidden" name="mimes[]" value="'+self.escape(mime)+'"/>');
  6247. });
  6248. $.each(self.customData, function(key, val) {
  6249. form.append('<input type="hidden" name="'+key+'" value="'+self.escape(val)+'"/>');
  6250. });
  6251. form.appendTo('body');
  6252. iframe.appendTo('body');
  6253. });
  6254. return dfrd;
  6255. }
  6256. },
  6257. /**
  6258. * Bind callback to event(s) The callback is executed at most once per event.
  6259. * To bind to multiply events at once, separate events names by space
  6260. *
  6261. * @param String event name
  6262. * @param Function callback
  6263. * @return elFinder
  6264. */
  6265. one : function(ev, callback) {
  6266. var self = this,
  6267. event = ev.toLowerCase(),
  6268. h = function(e, f) {
  6269. if (!self.toUnbindEvents[event]) {
  6270. self.toUnbindEvents[event] = [];
  6271. }
  6272. self.toUnbindEvents[event].push({
  6273. type: event,
  6274. callback: h
  6275. });
  6276. return callback.apply(this, arguments);
  6277. };
  6278. return this.bind(event, h);
  6279. },
  6280. /**
  6281. * Set/get data into/from localStorage
  6282. *
  6283. * @param String key
  6284. * @param String|void value
  6285. * @return String|null
  6286. */
  6287. localStorage : function(key, val) {
  6288. var self = this,
  6289. s = window.localStorage,
  6290. oldkey = 'elfinder-'+(key || '')+this.id, // old key of elFinder < 2.1.6
  6291. prefix = window.location.pathname+'-elfinder-',
  6292. suffix = this.id,
  6293. clrs = [],
  6294. retval, oldval, t, precnt, sufcnt;
  6295. // reset this node data
  6296. if (typeof(key) === 'undefined') {
  6297. precnt = prefix.length;
  6298. sufcnt = suffix.length * -1;
  6299. $.each(s, function(key) {
  6300. if (key.substr(0, precnt) === prefix && key.substr(sufcnt) === suffix) {
  6301. clrs.push(key);
  6302. }
  6303. });
  6304. $.each(clrs, function(i, key) {
  6305. s.removeItem(key);
  6306. });
  6307. return true;
  6308. }
  6309. // new key of elFinder >= 2.1.6
  6310. key = prefix+key+suffix;
  6311. if (val === null) {
  6312. return s.removeItem(key);
  6313. }
  6314. if (val === void(0) && !(retval = s.getItem(key)) && (oldval = s.getItem(oldkey))) {
  6315. val = oldval;
  6316. s.removeItem(oldkey);
  6317. }
  6318. if (val !== void(0)) {
  6319. t = typeof val;
  6320. if (t !== 'string' && t !== 'number') {
  6321. val = JSON.stringify(val);
  6322. }
  6323. try {
  6324. s.setItem(key, val);
  6325. } catch (e) {
  6326. try {
  6327. s.clear();
  6328. s.setItem(key, val);
  6329. } catch (e) {
  6330. self.debug('error', e.toString());
  6331. }
  6332. }
  6333. retval = s.getItem(key);
  6334. }
  6335. if (retval && (retval.substr(0,1) === '{' || retval.substr(0,1) === '[')) {
  6336. try {
  6337. return JSON.parse(retval);
  6338. } catch(e) {}
  6339. }
  6340. return retval;
  6341. },
  6342. /**
  6343. * Get/set cookie
  6344. *
  6345. * @param String cookie name
  6346. * @param String|void cookie value
  6347. * @return String|null
  6348. */
  6349. cookie : function(name, value) {
  6350. var d, o, c, i, retval, t;
  6351. name = 'elfinder-'+name+this.id;
  6352. if (value === void(0)) {
  6353. if (document.cookie && document.cookie != '') {
  6354. c = document.cookie.split(';');
  6355. name += '=';
  6356. for (i=0; i<c.length; i++) {
  6357. c[i] = $.trim(c[i]);
  6358. if (c[i].substring(0, name.length) == name) {
  6359. retval = decodeURIComponent(c[i].substring(name.length));
  6360. if (retval.substr(0,1) === '{' || retval.substr(0,1) === '[') {
  6361. try {
  6362. return JSON.parse(retval);
  6363. } catch(e) {}
  6364. }
  6365. return retval;
  6366. }
  6367. }
  6368. }
  6369. return null;
  6370. }
  6371. o = Object.assign({}, this.options.cookie);
  6372. if (value === null) {
  6373. value = '';
  6374. o.expires = -1;
  6375. } else {
  6376. t = typeof value;
  6377. if (t !== 'string' && t !== 'number') {
  6378. value = JSON.stringify(value);
  6379. }
  6380. }
  6381. if (typeof(o.expires) == 'number') {
  6382. d = new Date();
  6383. d.setTime(d.getTime()+(o.expires * 86400000));
  6384. o.expires = d;
  6385. }
  6386. document.cookie = name+'='+encodeURIComponent(value)+'; expires='+o.expires.toUTCString()+(o.path ? '; path='+o.path : '')+(o.domain ? '; domain='+o.domain : '')+(o.secure ? '; secure' : '');
  6387. if (value && (value.substr(0,1) === '{' || value.substr(0,1) === '[')) {
  6388. try {
  6389. return JSON.parse(value);
  6390. } catch(e) {}
  6391. }
  6392. return value;
  6393. },
  6394. /**
  6395. * Get start directory (by location.hash or last opened directory)
  6396. *
  6397. * @return String
  6398. */
  6399. startDir : function() {
  6400. var locHash = window.location.hash;
  6401. if (locHash && locHash.match(/^#elf_/)) {
  6402. return locHash.replace(/^#elf_/, '');
  6403. } else if (this.options.startPathHash) {
  6404. return this.options.startPathHash;
  6405. } else {
  6406. return this.lastDir();
  6407. }
  6408. },
  6409. /**
  6410. * Get/set last opened directory
  6411. *
  6412. * @param String|undefined dir hash
  6413. * @return String
  6414. */
  6415. lastDir : function(hash) {
  6416. return this.options.rememberLastDir ? this.storage('lastdir', hash) : '';
  6417. },
  6418. /**
  6419. * Node for escape html entities in texts
  6420. *
  6421. * @type jQuery
  6422. */
  6423. _node : $('<span/>'),
  6424. /**
  6425. * Replace not html-safe symbols to html entities
  6426. *
  6427. * @param String text to escape
  6428. * @return String
  6429. */
  6430. escape : function(name) {
  6431. return this._node.text(name).html().replace(/"/g, '&quot;').replace(/'/g, '&#039;');
  6432. },
  6433. /**
  6434. * Cleanup ajax data.
  6435. * For old api convert data into new api format
  6436. *
  6437. * @param String command name
  6438. * @param Object data from backend
  6439. * @return Object
  6440. */
  6441. normalize : function(data) {
  6442. var self = this,
  6443. fileFilter = (function() {
  6444. var func, filter;
  6445. if (filter = self.options.fileFilter) {
  6446. if (typeof filter === 'function') {
  6447. func = function(file) {
  6448. return filter.call(self, file);
  6449. };
  6450. } else if (filter instanceof RegExp) {
  6451. func = function(file) {
  6452. return filter.test(file.name);
  6453. };
  6454. }
  6455. }
  6456. return func? func : null;
  6457. })(),
  6458. chkCmdMap = function(opts) {
  6459. // Disable command to replace with other command
  6460. var disabled;
  6461. if (opts.uiCmdMap) {
  6462. if ($.isPlainObject(opts.uiCmdMap) && Object.keys(opts.uiCmdMap).length) {
  6463. if (!opts.disabledFlip) {
  6464. opts.disabledFlip = {};
  6465. }
  6466. disabled = opts.disabledFlip;
  6467. $.each(opts.uiCmdMap, function(f, t) {
  6468. if (t === 'hidden' && !disabled[f]) {
  6469. opts.disabled.push(f);
  6470. opts.disabledFlip[f] = true;
  6471. }
  6472. });
  6473. } else {
  6474. delete opts.uiCmdMap;
  6475. }
  6476. }
  6477. },
  6478. normalizeOptions = function(opts) {
  6479. var getType = function(v) {
  6480. var type = typeof v;
  6481. if (type === 'object' && Array.isArray(v)) {
  6482. type = 'array';
  6483. }
  6484. return type;
  6485. };
  6486. $.each(self.optionProperties, function(k, empty) {
  6487. if (empty !== void(0)) {
  6488. if (opts[k] && getType(opts[k]) !== getType(empty)) {
  6489. opts[k] = empty;
  6490. }
  6491. }
  6492. });
  6493. if (opts['disabled']) {
  6494. opts['disabledFlip'] = self.arrayFlip(opts['disabled'], true);
  6495. } else {
  6496. opts['disabledFlip'] = {};
  6497. }
  6498. return opts;
  6499. },
  6500. filter = function(file, asMap, type) {
  6501. var res = asMap? file : true,
  6502. ign = asMap? null : false,
  6503. vid, targetOptions, isRoot, rootNames;
  6504. if (file && file.hash && file.name && file.mime) {
  6505. if (file.mime === 'application/x-empty') {
  6506. file.mime = 'text/plain';
  6507. }
  6508. isRoot = self.isRoot(file);
  6509. if (isRoot && ! file.volumeid) {
  6510. self.debug('warning', 'The volume root statuses requires `volumeid` property.');
  6511. }
  6512. if (isRoot || file.mime === 'directory') {
  6513. // Prevention of circular reference
  6514. if (file.phash) {
  6515. if (file.phash === file.hash) {
  6516. error = error.concat(['Parent folder of "$1" is itself.', file.name]);
  6517. return ign;
  6518. }
  6519. if (isRoot && file.volumeid && file.phash.indexOf(file.volumeid) === 0) {
  6520. error = error.concat(['Parent folder of "$1" is inner itself.', file.name]);
  6521. return ign;
  6522. }
  6523. }
  6524. // set options, tmbUrls for each volume
  6525. if (file.volumeid) {
  6526. vid = file.volumeid;
  6527. if (isRoot) {
  6528. // make or update of leaf roots cache
  6529. if (file.phash) {
  6530. if (! self.leafRoots[file.phash]) {
  6531. self.leafRoots[file.phash] = [ file.hash ];
  6532. } else {
  6533. if ($.inArray(file.hash, self.leafRoots[file.phash]) === -1) {
  6534. self.leafRoots[file.phash].push(file.hash);
  6535. }
  6536. }
  6537. }
  6538. self.hasVolOptions = true;
  6539. if (! self.volOptions[vid]) {
  6540. self.volOptions[vid] = {
  6541. // set dispInlineRegex
  6542. dispInlineRegex: self.options.dispInlineRegex
  6543. };
  6544. }
  6545. targetOptions = self.volOptions[vid];
  6546. if (file.options) {
  6547. // >= v.2.1.14 has file.options
  6548. Object.assign(targetOptions, file.options);
  6549. }
  6550. // for compat <= v2.1.13
  6551. if (file.disabled) {
  6552. targetOptions.disabled = file.disabled;
  6553. targetOptions.disabledFlip = self.arrayFlip(file.disabled, true);
  6554. }
  6555. if (file.tmbUrl) {
  6556. targetOptions.tmbUrl = file.tmbUrl;
  6557. }
  6558. // '/' required at the end of url
  6559. if (targetOptions.url && targetOptions.url.substr(-1) !== '/') {
  6560. targetOptions.url += '/';
  6561. }
  6562. // check uiCmdMap
  6563. chkCmdMap(targetOptions);
  6564. // check trash bin hash
  6565. if (targetOptions.trashHash) {
  6566. if (self.trashes[targetOptions.trashHash] === false) {
  6567. delete targetOptions.trashHash;
  6568. } else {
  6569. self.trashes[targetOptions.trashHash] = file.hash;
  6570. }
  6571. }
  6572. // set immediate properties
  6573. $.each(self.optionProperties, function(k) {
  6574. if (targetOptions[k]) {
  6575. file[k] = targetOptions[k];
  6576. }
  6577. });
  6578. // regist fm.roots
  6579. if (type !== 'cwd') {
  6580. self.roots[vid] = file.hash;
  6581. }
  6582. }
  6583. if (prevId !== vid) {
  6584. prevId = vid;
  6585. i18nFolderName = self.option('i18nFolderName', vid);
  6586. }
  6587. }
  6588. // volume root i18n name
  6589. if (isRoot && ! file.i18) {
  6590. name = 'volume_' + file.name,
  6591. i18 = self.i18n(false, name);
  6592. if (name !== i18) {
  6593. file.i18 = i18;
  6594. }
  6595. }
  6596. // i18nFolderName
  6597. if (i18nFolderName && ! file.i18) {
  6598. name = 'folder_' + file.name,
  6599. i18 = self.i18n(false, name);
  6600. if (name !== i18) {
  6601. file.i18 = i18;
  6602. }
  6603. }
  6604. if (isRoot) {
  6605. if (rootNames = self.storage('rootNames')) {
  6606. if (rootNames[file.hash]) {
  6607. file._name = file.name;
  6608. file._i18 = file.i18;
  6609. file.name = rootNames[file.hash] = rootNames[file.hash];
  6610. delete file.i18;
  6611. }
  6612. self.storage('rootNames', rootNames);
  6613. }
  6614. }
  6615. // lock trash bins holder
  6616. if (self.trashes[file.hash]) {
  6617. file.locked = true;
  6618. }
  6619. } else if (fileFilter) {
  6620. try {
  6621. if (! fileFilter(file)) {
  6622. return ign;
  6623. }
  6624. } catch(e) {
  6625. self.debug(e);
  6626. }
  6627. }
  6628. if (file.options) {
  6629. self.optionsByHashes[file.hash] = normalizeOptions(file.options);
  6630. }
  6631. delete file.options;
  6632. return res;
  6633. }
  6634. return ign;
  6635. },
  6636. getDescendants = function(hashes) {
  6637. var res = [];
  6638. $.each(self.files(), function(h, f) {
  6639. $.each(self.parents(h), function(i, ph) {
  6640. if ($.inArray(ph, hashes) !== -1 && $.inArray(h, hashes) === -1) {
  6641. res.push(h);
  6642. return false;
  6643. }
  6644. });
  6645. });
  6646. return res;
  6647. },
  6648. applyLeafRootStats = function(dataArr, type) {
  6649. $.each(dataArr, function(i, f) {
  6650. var pfile, done;
  6651. if (self.leafRoots[f.hash]) {
  6652. self.applyLeafRootStats(f);
  6653. }
  6654. // update leaf root parent stat
  6655. if (type !== 'change' && f.phash && self.isRoot(f) && (pfile = self.file(f.phash))) {
  6656. self.applyLeafRootStats(pfile);
  6657. // add to data.changed
  6658. if (!data.changed) {
  6659. data.changed = [pfile];
  6660. } else {
  6661. $.each(data.changed, function(i, f) {
  6662. if (f.hash === pfile.hash) {
  6663. data.changed[i] = pfile;
  6664. done = true;
  6665. return false;
  6666. }
  6667. });
  6668. if (!done) {
  6669. data.changed.push(pfile);
  6670. }
  6671. }
  6672. }
  6673. });
  6674. },
  6675. error = [],
  6676. name, i18, i18nFolderName, prevId, cData;
  6677. // set cunstom data
  6678. if (data.customData && data.customData !== self.prevCustomData) {
  6679. self.prevCustomData = data.customData;
  6680. try {
  6681. cData = JSON.parse(data.customData);
  6682. if ($.isPlainObject(cData)) {
  6683. self.prevCustomData = cData;
  6684. $.each(Object.keys(cData), function(i, key) {
  6685. if (cData[key] === null) {
  6686. delete cData[key];
  6687. delete self.optsCustomData[key];
  6688. }
  6689. });
  6690. self.customData = Object.assign({}, self.optsCustomData, cData);
  6691. }
  6692. } catch(e) {}
  6693. }
  6694. if (data.options) {
  6695. normalizeOptions(data.options);
  6696. }
  6697. if (data.cwd) {
  6698. if (data.cwd.volumeid && data.options && Object.keys(data.options).length && self.isRoot(data.cwd)) {
  6699. self.hasVolOptions = true;
  6700. self.volOptions[data.cwd.volumeid] = data.options;
  6701. }
  6702. data.cwd = filter(data.cwd, true, 'cwd');
  6703. }
  6704. if (data.files) {
  6705. data.files = $.grep(data.files, filter);
  6706. }
  6707. if (data.tree) {
  6708. data.tree = $.grep(data.tree, filter);
  6709. }
  6710. if (data.added) {
  6711. data.added = $.grep(data.added, filter);
  6712. }
  6713. if (data.changed) {
  6714. data.changed = $.grep(data.changed, filter);
  6715. }
  6716. if (data.removed && data.removed.length && self.searchStatus.state === 2) {
  6717. data.removed = data.removed.concat(getDescendants(data.removed));
  6718. }
  6719. if (data.api) {
  6720. data.init = true;
  6721. }
  6722. if (Object.keys(self.leafRoots).length) {
  6723. data.files && applyLeafRootStats(data.files);
  6724. data.tree && applyLeafRootStats(data.tree);
  6725. data.added && applyLeafRootStats(data.added);
  6726. data.changed && applyLeafRootStats(data.changed, 'change');
  6727. }
  6728. // merge options that apply only to cwd
  6729. if (data.cwd && data.cwd.options && data.options) {
  6730. Object.assign(data.options, normalizeOptions(data.cwd.options));
  6731. }
  6732. // '/' required at the end of url
  6733. if (data.options && data.options.url && data.options.url.substr(-1) !== '/') {
  6734. data.options.url += '/';
  6735. }
  6736. // check error
  6737. if (error.length) {
  6738. data.norError = ['errResponse'].concat(error);
  6739. }
  6740. return data;
  6741. },
  6742. /**
  6743. * Update sort options
  6744. *
  6745. * @param {String} sort type
  6746. * @param {String} sort order
  6747. * @param {Boolean} show folder first
  6748. */
  6749. setSort : function(type, order, stickFolders, alsoTreeview) {
  6750. this.storage('sortType', (this.sortType = this.sortRules[type] ? type : 'name'));
  6751. this.storage('sortOrder', (this.sortOrder = /asc|desc/.test(order) ? order : 'asc'));
  6752. this.storage('sortStickFolders', (this.sortStickFolders = !!stickFolders) ? 1 : '');
  6753. this.storage('sortAlsoTreeview', (this.sortAlsoTreeview = !!alsoTreeview) ? 1 : '');
  6754. this.trigger('sortchange');
  6755. },
  6756. _sortRules : {
  6757. name : function(file1, file2) {
  6758. return elFinder.prototype.naturalCompare(file1.i18 || file1.name, file2.i18 || file2.name);
  6759. },
  6760. size : function(file1, file2) {
  6761. var size1 = parseInt(file1.size) || 0,
  6762. size2 = parseInt(file2.size) || 0;
  6763. return size1 === size2 ? 0 : size1 > size2 ? 1 : -1;
  6764. },
  6765. kind : function(file1, file2) {
  6766. return elFinder.prototype.naturalCompare(file1.mime, file2.mime);
  6767. },
  6768. date : function(file1, file2) {
  6769. var date1 = file1.ts || file1.date || 0,
  6770. date2 = file2.ts || file2.date || 0;
  6771. return date1 === date2 ? 0 : date1 > date2 ? 1 : -1;
  6772. },
  6773. perm : function(file1, file2) {
  6774. var val = function(file) { return (file.write? 2 : 0) + (file.read? 1 : 0); },
  6775. v1 = val(file1),
  6776. v2 = val(file2);
  6777. return v1 === v2 ? 0 : v1 > v2 ? 1 : -1;
  6778. },
  6779. mode : function(file1, file2) {
  6780. var v1 = file1.mode || (file1.perm || ''),
  6781. v2 = file2.mode || (file2.perm || '');
  6782. return elFinder.prototype.naturalCompare(v1, v2);
  6783. },
  6784. owner : function(file1, file2) {
  6785. var v1 = file1.owner || '',
  6786. v2 = file2.owner || '';
  6787. return elFinder.prototype.naturalCompare(v1, v2);
  6788. },
  6789. group : function(file1, file2) {
  6790. var v1 = file1.group || '',
  6791. v2 = file2.group || '';
  6792. return elFinder.prototype.naturalCompare(v1, v2);
  6793. }
  6794. },
  6795. /**
  6796. * Valid sort rule names
  6797. *
  6798. * @type Array
  6799. */
  6800. sorters : [],
  6801. /**
  6802. * Compare strings for natural sort
  6803. *
  6804. * @param String
  6805. * @param String
  6806. * @return Number
  6807. */
  6808. naturalCompare : function(a, b) {
  6809. var self = elFinder.prototype.naturalCompare;
  6810. if (typeof self.loc == 'undefined') {
  6811. self.loc = (navigator.userLanguage || navigator.browserLanguage || navigator.language || 'en-US');
  6812. }
  6813. if (typeof self.sort == 'undefined') {
  6814. if ('11'.localeCompare('2', self.loc, {numeric: true}) > 0) {
  6815. // Native support
  6816. if (window.Intl && window.Intl.Collator) {
  6817. self.sort = new Intl.Collator(self.loc, {numeric: true}).compare;
  6818. } else {
  6819. self.sort = function(a, b) {
  6820. return a.localeCompare(b, self.loc, {numeric: true});
  6821. };
  6822. }
  6823. } else {
  6824. /*
  6825. * Edited for elFinder (emulates localeCompare() by numeric) by Naoki Sawada aka nao-pon
  6826. */
  6827. /*
  6828. * Huddle/javascript-natural-sort (https://github.com/Huddle/javascript-natural-sort)
  6829. */
  6830. /*
  6831. * Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
  6832. * Author: Jim Palmer (based on chunking idea from Dave Koelle)
  6833. * http://opensource.org/licenses/mit-license.php
  6834. */
  6835. self.sort = function(a, b) {
  6836. var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi,
  6837. sre = /(^[ ]*|[ ]*$)/g,
  6838. dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
  6839. hre = /^0x[0-9a-f]+$/i,
  6840. ore = /^0/,
  6841. syre = /^[\x01\x21-\x2f\x3a-\x40\x5b-\x60\x7b-\x7e]/, // symbol first - (Naoki Sawada)
  6842. i = function(s) { return self.sort.insensitive && (''+s).toLowerCase() || ''+s; },
  6843. // convert all to strings strip whitespace
  6844. // first character is "_", it's smallest - (Naoki Sawada)
  6845. x = i(a).replace(sre, '').replace(/^_/, "\x01") || '',
  6846. y = i(b).replace(sre, '').replace(/^_/, "\x01") || '',
  6847. // chunk/tokenize
  6848. xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  6849. yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
  6850. // numeric, hex or date detection
  6851. xD = parseInt(x.match(hre)) || (xN.length != 1 && x.match(dre) && Date.parse(x)),
  6852. yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
  6853. oFxNcL, oFyNcL,
  6854. locRes = 0;
  6855. // first try and sort Hex codes or Dates
  6856. if (yD) {
  6857. if ( xD < yD ) return -1;
  6858. else if ( xD > yD ) return 1;
  6859. }
  6860. // natural sorting through split numeric strings and default strings
  6861. for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
  6862. // find floats not starting with '0', string or 0 if not defined (Clint Priest)
  6863. oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
  6864. oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
  6865. // handle numeric vs string comparison - number < string - (Kyle Adams)
  6866. // but symbol first < number - (Naoki Sawada)
  6867. if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
  6868. if (isNaN(oFxNcL) && (typeof oFxNcL !== 'string' || ! oFxNcL.match(syre))) {
  6869. return 1;
  6870. } else if (typeof oFyNcL !== 'string' || ! oFyNcL.match(syre)) {
  6871. return -1;
  6872. }
  6873. }
  6874. // use decimal number comparison if either value is string zero
  6875. if (parseInt(oFxNcL, 10) === 0) oFxNcL = 0;
  6876. if (parseInt(oFyNcL, 10) === 0) oFyNcL = 0;
  6877. // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
  6878. if (typeof oFxNcL !== typeof oFyNcL) {
  6879. oFxNcL += '';
  6880. oFyNcL += '';
  6881. }
  6882. // use locale sensitive sort for strings when case insensitive
  6883. // note: localeCompare interleaves uppercase with lowercase (e.g. A,a,B,b)
  6884. if (self.sort.insensitive && typeof oFxNcL === 'string' && typeof oFyNcL === 'string') {
  6885. locRes = oFxNcL.localeCompare(oFyNcL, self.loc);
  6886. if (locRes !== 0) return locRes;
  6887. }
  6888. if (oFxNcL < oFyNcL) return -1;
  6889. if (oFxNcL > oFyNcL) return 1;
  6890. }
  6891. return 0;
  6892. };
  6893. self.sort.insensitive = true;
  6894. }
  6895. }
  6896. return self.sort(a, b);
  6897. },
  6898. /**
  6899. * Compare files based on elFinder.sort
  6900. *
  6901. * @param Object file
  6902. * @param Object file
  6903. * @return Number
  6904. */
  6905. compare : function(file1, file2) {
  6906. var self = this,
  6907. type = self.sortType,
  6908. asc = self.sortOrder == 'asc',
  6909. stick = self.sortStickFolders,
  6910. rules = self.sortRules,
  6911. sort = rules[type],
  6912. d1 = file1.mime == 'directory',
  6913. d2 = file2.mime == 'directory',
  6914. res;
  6915. if (stick) {
  6916. if (d1 && !d2) {
  6917. return -1;
  6918. } else if (!d1 && d2) {
  6919. return 1;
  6920. }
  6921. }
  6922. res = asc ? sort(file1, file2) : sort(file2, file1);
  6923. return type !== 'name' && res === 0
  6924. ? res = asc ? rules.name(file1, file2) : rules.name(file2, file1)
  6925. : res;
  6926. },
  6927. /**
  6928. * Sort files based on config
  6929. *
  6930. * @param Array files
  6931. * @return Array
  6932. */
  6933. sortFiles : function(files) {
  6934. return files.sort(this.compare);
  6935. },
  6936. /**
  6937. * Open notification dialog
  6938. * and append/update message for required notification type.
  6939. *
  6940. * @param Object options
  6941. * @example
  6942. * this.notify({
  6943. * type : 'copy',
  6944. * msg : 'Copy files', // not required for known types @see this.notifyType
  6945. * cnt : 3,
  6946. * hideCnt : false, // true for not show count
  6947. * progress : 10, // progress bar percents (use cnt : 0 to update progress bar)
  6948. * cancel : callback // callback function for cancel button
  6949. * })
  6950. * @return elFinder
  6951. */
  6952. notify : function(opts) {
  6953. var type = opts.type,
  6954. id = opts.id? 'elfinder-notify-'+opts.id : '',
  6955. msg = this.i18n((typeof opts.msg !== 'undefined')? opts.msg : (this.messages['ntf'+type] ? 'ntf'+type : 'ntfsmth')),
  6956. ndialog = this.ui.notify,
  6957. notify = ndialog.children('.elfinder-notify-'+type+(id? ('.'+id) : '')),
  6958. button = notify.children('div.elfinder-notify-cancel').children('button'),
  6959. 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>',
  6960. delta = opts.cnt,
  6961. size = (typeof opts.size != 'undefined')? parseInt(opts.size) : null,
  6962. progress = (typeof opts.progress != 'undefined' && opts.progress >= 0) ? opts.progress : null,
  6963. cancel = opts.cancel,
  6964. clhover = 'ui-state-hover',
  6965. close = function() {
  6966. notify._esc && $(document).off('keydown', notify._esc);
  6967. notify.remove();
  6968. !ndialog.children().length && ndialog.elfinderdialog('close');
  6969. },
  6970. cnt, total, prc;
  6971. if (!type) {
  6972. return this;
  6973. }
  6974. if (!notify.length) {
  6975. notify = $(ntpl.replace(/\{type\}/g, type).replace(/\{msg\}/g, msg))
  6976. .appendTo(ndialog)
  6977. .data('cnt', 0);
  6978. if (progress != null) {
  6979. notify.data({progress : 0, total : 0});
  6980. }
  6981. if (cancel) {
  6982. 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>')
  6983. .on('mouseenter mouseleave', function(e) {
  6984. $(this).toggleClass(clhover, e.type === 'mouseenter');
  6985. });
  6986. notify.children('div.elfinder-notify-cancel').append(button);
  6987. }
  6988. } else if (typeof opts.msg !== 'undefined') {
  6989. notify.children('span.elfinder-notify-msg').html(msg);
  6990. }
  6991. cnt = delta + parseInt(notify.data('cnt'));
  6992. if (cnt > 0) {
  6993. if (cancel && button.length) {
  6994. if ($.isFunction(cancel) || (typeof cancel === 'object' && cancel.promise)) {
  6995. notify._esc = function(e) {
  6996. if (e.type == 'keydown' && e.keyCode != $.ui.keyCode.ESCAPE) {
  6997. return;
  6998. }
  6999. e.preventDefault();
  7000. e.stopPropagation();
  7001. close();
  7002. if (cancel.promise) {
  7003. cancel.reject(0); // 0 is canceling flag
  7004. } else {
  7005. cancel(e);
  7006. }
  7007. };
  7008. button.on('click', function(e) {
  7009. notify._esc(e);
  7010. });
  7011. $(document).on('keydown.' + this.namespace, notify._esc);
  7012. }
  7013. }
  7014. !opts.hideCnt && notify.children('.elfinder-notify-cnt').text('('+cnt+')');
  7015. ndialog.is(':hidden') && ndialog.elfinderdialog('open', this).height('auto');
  7016. notify.data('cnt', cnt);
  7017. if ((progress != null)
  7018. && (total = notify.data('total')) >= 0
  7019. && (prc = notify.data('progress')) >= 0) {
  7020. total += size != null? size : delta;
  7021. prc += progress;
  7022. (size == null && delta < 0) && (prc += delta * 100);
  7023. notify.data({progress : prc, total : total});
  7024. if (size != null) {
  7025. prc *= 100;
  7026. total = Math.max(1, total);
  7027. }
  7028. progress = parseInt(prc/total);
  7029. notify.find('.elfinder-notify-progress')
  7030. .animate({
  7031. width : (progress < 100 ? progress : 100)+'%'
  7032. }, 20);
  7033. }
  7034. } else {
  7035. close();
  7036. }
  7037. return this;
  7038. },
  7039. /**
  7040. * Open confirmation dialog
  7041. *
  7042. * @param Object options
  7043. * @example
  7044. * this.confirm({
  7045. * cssClass : 'elfinder-confirm-mydialog',
  7046. * title : 'Remove files',
  7047. * text : 'Here is question text',
  7048. * accept : { // accept callback - required
  7049. * label : 'Continue',
  7050. * callback : function(applyToAll) { fm.log('Ok') }
  7051. * },
  7052. * cancel : { // cancel callback - required
  7053. * label : 'Cancel',
  7054. * callback : function() { fm.log('Cancel')}
  7055. * },
  7056. * reject : { // reject callback - optionally
  7057. * label : 'No',
  7058. * callback : function(applyToAll) { fm.log('No')}
  7059. * },
  7060. * buttons : [ // additional buttons callback - optionally
  7061. * {
  7062. * label : 'Btn1',
  7063. * callback : function(applyToAll) { fm.log('Btn1')}
  7064. * }
  7065. * ],
  7066. * all : true // display checkbox "Apply to all"
  7067. * })
  7068. * @return elFinder
  7069. */
  7070. confirm : function(opts) {
  7071. var self = this,
  7072. complete = false,
  7073. options = {
  7074. cssClass : 'elfinder-dialog-confirm',
  7075. modal : true,
  7076. resizable : false,
  7077. title : this.i18n(opts.title || 'confirmReq'),
  7078. buttons : {},
  7079. close : function() {
  7080. !complete && opts.cancel.callback();
  7081. $(this).elfinderdialog('destroy');
  7082. }
  7083. },
  7084. apply = this.i18n('apllyAll'),
  7085. label, checkbox, btnNum;
  7086. if (opts.cssClass) {
  7087. options.cssClass += ' ' + opts.cssClass;
  7088. }
  7089. options.buttons[this.i18n(opts.accept.label)] = function() {
  7090. opts.accept.callback(!!(checkbox && checkbox.prop('checked')));
  7091. complete = true;
  7092. $(this).elfinderdialog('close');
  7093. };
  7094. options.buttons[this.i18n(opts.accept.label)]._cssClass = 'elfinder-confirm-accept';
  7095. if (opts.reject) {
  7096. options.buttons[this.i18n(opts.reject.label)] = function() {
  7097. opts.reject.callback(!!(checkbox && checkbox.prop('checked')));
  7098. complete = true;
  7099. $(this).elfinderdialog('close');
  7100. };
  7101. options.buttons[this.i18n(opts.reject.label)]._cssClass = 'elfinder-confirm-reject';
  7102. }
  7103. if (opts.buttons && opts.buttons.length > 0) {
  7104. btnNum = 1;
  7105. $.each(opts.buttons, function(i, v){
  7106. options.buttons[self.i18n(v.label)] = function() {
  7107. v.callback(!!(checkbox && checkbox.prop('checked')));
  7108. complete = true;
  7109. $(this).elfinderdialog('close');
  7110. };
  7111. options.buttons[self.i18n(v.label)]._cssClass = 'elfinder-confirm-extbtn' + (btnNum++);
  7112. if (v.cssClass) {
  7113. options.buttons[self.i18n(v.label)]._cssClass += ' ' + v.cssClass;
  7114. }
  7115. });
  7116. }
  7117. options.buttons[this.i18n(opts.cancel.label)] = function() {
  7118. $(this).elfinderdialog('close');
  7119. };
  7120. options.buttons[this.i18n(opts.cancel.label)]._cssClass = 'elfinder-confirm-cancel';
  7121. if (opts.all) {
  7122. options.create = function() {
  7123. var base = $('<div class="elfinder-dialog-confirm-applyall"/>');
  7124. checkbox = $('<input type="checkbox" />');
  7125. $(this).next().find('.ui-dialog-buttonset')
  7126. .prepend(base.append($('<label>'+apply+'</label>').prepend(checkbox)));
  7127. };
  7128. }
  7129. if (opts.optionsCallback && $.isFunction(opts.optionsCallback)) {
  7130. opts.optionsCallback(options);
  7131. }
  7132. return this.dialog('<span class="elfinder-dialog-icon elfinder-dialog-icon-confirm"/>' + this.i18n(opts.text), options);
  7133. },
  7134. /**
  7135. * Create unique file name in required dir
  7136. *
  7137. * @param String file name
  7138. * @param String parent dir hash
  7139. * @param String glue
  7140. * @return String
  7141. */
  7142. uniqueName : function(prefix, phash, glue) {
  7143. var i = 0, ext = '', p, name;
  7144. prefix = this.i18n(false, prefix);
  7145. phash = phash || this.cwd().hash;
  7146. glue = (typeof glue === 'undefined')? ' ' : glue;
  7147. if (p = prefix.match(/^(.+)(\.[^.]+)$/)) {
  7148. ext = p[2];
  7149. prefix = p[1];
  7150. }
  7151. name = prefix+ext;
  7152. if (!this.fileByName(name, phash)) {
  7153. return name;
  7154. }
  7155. while (i < 10000) {
  7156. name = prefix + glue + (++i) + ext;
  7157. if (!this.fileByName(name, phash)) {
  7158. return name;
  7159. }
  7160. }
  7161. return prefix + Math.random() + ext;
  7162. },
  7163. /**
  7164. * Return message translated onto current language
  7165. * Allowed accept HTML element that was wrapped in jQuery object
  7166. * To be careful to XSS vulnerability of HTML element Ex. You should use `fm.escape(file.name)`
  7167. *
  7168. * @param String|Array message[s]|Object jQuery
  7169. * @return String
  7170. **/
  7171. i18n : function() {
  7172. var self = this,
  7173. messages = this.messages,
  7174. input = [],
  7175. ignore = [],
  7176. message = function(m) {
  7177. var file;
  7178. if (m.indexOf('#') === 0) {
  7179. if ((file = self.file(m.substr(1)))) {
  7180. return file.name;
  7181. }
  7182. }
  7183. return m;
  7184. },
  7185. i, j, m, escFunc, start = 0;
  7186. if (arguments.length && arguments[0] === false) {
  7187. escFunc = function(m){ return m; };
  7188. start = 1;
  7189. }
  7190. for (i = start; i< arguments.length; i++) {
  7191. m = arguments[i];
  7192. if (Array.isArray(m)) {
  7193. for (j = 0; j < m.length; j++) {
  7194. if (m[j] instanceof jQuery) {
  7195. // jQuery object is HTML element
  7196. input.push(m[j]);
  7197. } else if (typeof m[j] !== 'undefined'){
  7198. input.push(message('' + m[j]));
  7199. }
  7200. }
  7201. } else if (m instanceof jQuery) {
  7202. // jQuery object is HTML element
  7203. input.push(m[j]);
  7204. } else if (typeof m !== 'undefined'){
  7205. input.push(message('' + m));
  7206. }
  7207. }
  7208. for (i = 0; i < input.length; i++) {
  7209. // dont translate placeholders
  7210. if ($.inArray(i, ignore) !== -1) {
  7211. continue;
  7212. }
  7213. m = input[i];
  7214. if (typeof m == 'string') {
  7215. // translate message
  7216. m = messages[m] || (escFunc? escFunc(m) : self.escape(m));
  7217. // replace placeholders in message
  7218. m = m.replace(/\$(\d+)/g, function(match, placeholder) {
  7219. placeholder = i + parseInt(placeholder);
  7220. if (placeholder > 0 && input[placeholder]) {
  7221. ignore.push(placeholder);
  7222. }
  7223. return escFunc? escFunc(input[placeholder]) : self.escape(input[placeholder]);
  7224. });
  7225. } else {
  7226. // get HTML from jQuery object
  7227. m = m.get(0).outerHTML;
  7228. }
  7229. input[i] = m;
  7230. }
  7231. return $.grep(input, function(m, i) { return $.inArray(i, ignore) === -1 ? true : false; }).join('<br>');
  7232. },
  7233. /**
  7234. * Get icon style from file.icon
  7235. *
  7236. * @param Object elFinder file object
  7237. * @return String|Object
  7238. */
  7239. getIconStyle : function(file, asObject) {
  7240. var self = this,
  7241. template = {
  7242. 'background' : 'url(\'{url}\') 0 0 no-repeat',
  7243. 'background-size' : 'contain'
  7244. },
  7245. style = '',
  7246. cssObj = {},
  7247. i = 0;
  7248. if (file.icon) {
  7249. style = 'style="';
  7250. $.each(template, function(k, v) {
  7251. if (i++ === 0) {
  7252. v = v.replace('{url}', self.escape(file.icon));
  7253. }
  7254. if (asObject) {
  7255. cssObj[k] = v;
  7256. } else {
  7257. style += k+':'+v+';';
  7258. }
  7259. });
  7260. style += '"';
  7261. }
  7262. return asObject? cssObj : style;
  7263. },
  7264. /**
  7265. * Convert mimetype into css classes
  7266. *
  7267. * @param String file mimetype
  7268. * @return String
  7269. */
  7270. mime2class : function(mimeType) {
  7271. var prefix = 'elfinder-cwd-icon-',
  7272. mime = mimeType.toLowerCase(),
  7273. isText = this.textMimes[mime];
  7274. mime = mime.split('/');
  7275. if (isText) {
  7276. mime[0] += ' ' + prefix + 'text';
  7277. }
  7278. return prefix + mime[0] + (mime[1] ? ' ' + prefix + mime[1].replace(/(\.|\+)/g, '-') : '');
  7279. },
  7280. /**
  7281. * Return localized kind of file
  7282. *
  7283. * @param Object|String file or file mimetype
  7284. * @return String
  7285. */
  7286. mime2kind : function(f) {
  7287. var isObj = typeof(f) == 'object' ? true : false,
  7288. mime = isObj ? f.mime : f,
  7289. kind;
  7290. if (isObj && f.alias && mime != 'symlink-broken') {
  7291. kind = 'Alias';
  7292. } else if (this.kinds[mime]) {
  7293. if (isObj && mime === 'directory' && (! f.phash || f.isroot)) {
  7294. kind = 'Root';
  7295. } else {
  7296. kind = this.kinds[mime];
  7297. }
  7298. }
  7299. if (! kind) {
  7300. if (mime.indexOf('text') === 0) {
  7301. kind = 'Text';
  7302. } else if (mime.indexOf('image') === 0) {
  7303. kind = 'Image';
  7304. } else if (mime.indexOf('audio') === 0) {
  7305. kind = 'Audio';
  7306. } else if (mime.indexOf('video') === 0) {
  7307. kind = 'Video';
  7308. } else if (mime.indexOf('application') === 0) {
  7309. kind = 'App';
  7310. } else {
  7311. kind = mime;
  7312. }
  7313. }
  7314. return this.messages['kind'+kind] ? this.i18n('kind'+kind) : mime;
  7315. },
  7316. /**
  7317. * Return boolean Is mime-type text file
  7318. *
  7319. * @param String mime-type
  7320. * @return Boolean
  7321. */
  7322. mimeIsText : function(mime) {
  7323. return (this.textMimes[mime] || (mime.indexOf('text/') === 0 && mime.substr(5, 3) !== 'rtf'))? true : false;
  7324. },
  7325. /**
  7326. * Returns a date string formatted according to the given format string
  7327. *
  7328. * @param String format string
  7329. * @param Object Date object
  7330. * @return String
  7331. */
  7332. date : function(format, date) {
  7333. var self = this,
  7334. output, d, dw, m, y, h, g, i, s;
  7335. if (! date) {
  7336. date = new Date();
  7337. }
  7338. h = date[self.getHours]();
  7339. g = h > 12 ? h - 12 : h;
  7340. i = date[self.getMinutes]();
  7341. s = date[self.getSeconds]();
  7342. d = date[self.getDate]();
  7343. dw = date[self.getDay]();
  7344. m = date[self.getMonth]() + 1;
  7345. y = date[self.getFullYear]();
  7346. output = format.replace(/[a-z]/gi, function(val) {
  7347. switch (val) {
  7348. case 'd': return d > 9 ? d : '0'+d;
  7349. case 'j': return d;
  7350. case 'D': return self.i18n(self.i18.daysShort[dw]);
  7351. case 'l': return self.i18n(self.i18.days[dw]);
  7352. case 'm': return m > 9 ? m : '0'+m;
  7353. case 'n': return m;
  7354. case 'M': return self.i18n(self.i18.monthsShort[m-1]);
  7355. case 'F': return self.i18n(self.i18.months[m-1]);
  7356. case 'Y': return y;
  7357. case 'y': return (''+y).substr(2);
  7358. case 'H': return h > 9 ? h : '0'+h;
  7359. case 'G': return h;
  7360. case 'g': return g;
  7361. case 'h': return g > 9 ? g : '0'+g;
  7362. case 'a': return h >= 12 ? 'pm' : 'am';
  7363. case 'A': return h >= 12 ? 'PM' : 'AM';
  7364. case 'i': return i > 9 ? i : '0'+i;
  7365. case 's': return s > 9 ? s : '0'+s;
  7366. }
  7367. return val;
  7368. });
  7369. return output;
  7370. },
  7371. /**
  7372. * Return localized date
  7373. *
  7374. * @param Object file object
  7375. * @return String
  7376. */
  7377. formatDate : function(file, t) {
  7378. var self = this,
  7379. ts = t || file.ts,
  7380. i18 = self.i18,
  7381. date, format, output, d, dw, m, y, h, g, i, s;
  7382. if (self.options.clientFormatDate && ts > 0) {
  7383. date = new Date(ts*1000);
  7384. format = ts >= this.yesterday
  7385. ? this.fancyFormat
  7386. : this.dateFormat;
  7387. output = self.date(format, date);
  7388. return ts >= this.yesterday
  7389. ? output.replace('$1', this.i18n(ts >= this.today ? 'Today' : 'Yesterday'))
  7390. : output;
  7391. } else if (file.date) {
  7392. return file.date.replace(/([a-z]+)\s/i, function(a1, a2) { return self.i18n(a2)+' '; });
  7393. }
  7394. return self.i18n('dateUnknown');
  7395. },
  7396. /**
  7397. * Return localized number string
  7398. *
  7399. * @param Number
  7400. * @return String
  7401. */
  7402. toLocaleString : function(num) {
  7403. var v = new Number(num);
  7404. if (v) {
  7405. if (v.toLocaleString) {
  7406. return v.toLocaleString();
  7407. } else {
  7408. return String(num).replace( /(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
  7409. }
  7410. }
  7411. return num;
  7412. },
  7413. /**
  7414. * Return css class marks file permissions
  7415. *
  7416. * @param Object file
  7417. * @return String
  7418. */
  7419. perms2class : function(o) {
  7420. var c = '';
  7421. if (!o.read && !o.write) {
  7422. c = 'elfinder-na';
  7423. } else if (!o.read) {
  7424. c = 'elfinder-wo';
  7425. } else if (!o.write) {
  7426. c = 'elfinder-ro';
  7427. }
  7428. if (o.type) {
  7429. c += ' elfinder-' + this.escape(o.type);
  7430. }
  7431. return c;
  7432. },
  7433. /**
  7434. * Return localized string with file permissions
  7435. *
  7436. * @param Object file
  7437. * @return String
  7438. */
  7439. formatPermissions : function(f) {
  7440. var p = [];
  7441. f.read && p.push(this.i18n('read'));
  7442. f.write && p.push(this.i18n('write'));
  7443. return p.length ? p.join(' '+this.i18n('and')+' ') : this.i18n('noaccess');
  7444. },
  7445. /**
  7446. * Return formated file size
  7447. *
  7448. * @param Number file size
  7449. * @return String
  7450. */
  7451. formatSize : function(s) {
  7452. var n = 1, u = 'b';
  7453. if (s == 'unknown') {
  7454. return this.i18n('unknown');
  7455. }
  7456. if (s > 1073741824) {
  7457. n = 1073741824;
  7458. u = 'GB';
  7459. } else if (s > 1048576) {
  7460. n = 1048576;
  7461. u = 'MB';
  7462. } else if (s > 1024) {
  7463. n = 1024;
  7464. u = 'KB';
  7465. }
  7466. s = s/n;
  7467. return (s > 0 ? n >= 1048576 ? s.toFixed(2) : Math.round(s) : 0) +' '+u;
  7468. },
  7469. /**
  7470. * Return formated file mode by options.fileModeStyle
  7471. *
  7472. * @param String file mode
  7473. * @param String format style
  7474. * @return String
  7475. */
  7476. formatFileMode : function(p, style) {
  7477. var i, o, s, b, sticy, suid, sgid, str, oct;
  7478. if (!style) {
  7479. style = this.options.fileModeStyle.toLowerCase();
  7480. }
  7481. p = $.trim(p);
  7482. if (p.match(/[rwxs-]{9}$/i)) {
  7483. str = p = p.substr(-9);
  7484. if (style == 'string') {
  7485. return str;
  7486. }
  7487. oct = '';
  7488. s = 0;
  7489. for (i=0; i<7; i=i+3) {
  7490. o = p.substr(i, 3);
  7491. b = 0;
  7492. if (o.match(/[r]/i)) {
  7493. b += 4;
  7494. }
  7495. if (o.match(/[w]/i)) {
  7496. b += 2;
  7497. }
  7498. if (o.match(/[xs]/i)) {
  7499. if (o.match(/[xs]/)) {
  7500. b += 1;
  7501. }
  7502. if (o.match(/[s]/i)) {
  7503. if (i == 0) {
  7504. s += 4;
  7505. } else if (i == 3) {
  7506. s += 2;
  7507. }
  7508. }
  7509. }
  7510. oct += b.toString(8);
  7511. }
  7512. if (s) {
  7513. oct = s.toString(8) + oct;
  7514. }
  7515. } else {
  7516. p = parseInt(p, 8);
  7517. oct = p? p.toString(8) : '';
  7518. if (!p || style == 'octal') {
  7519. return oct;
  7520. }
  7521. o = p.toString(8);
  7522. s = 0;
  7523. if (o.length > 3) {
  7524. o = o.substr(-4);
  7525. s = parseInt(o.substr(0, 1), 8);
  7526. o = o.substr(1);
  7527. }
  7528. sticy = ((s & 1) == 1); // not support
  7529. sgid = ((s & 2) == 2);
  7530. suid = ((s & 4) == 4);
  7531. str = '';
  7532. for(i=0; i<3; i++) {
  7533. if ((parseInt(o.substr(i, 1), 8) & 4) == 4) {
  7534. str += 'r';
  7535. } else {
  7536. str += '-';
  7537. }
  7538. if ((parseInt(o.substr(i, 1), 8) & 2) == 2) {
  7539. str += 'w';
  7540. } else {
  7541. str += '-';
  7542. }
  7543. if ((parseInt(o.substr(i, 1), 8) & 1) == 1) {
  7544. str += ((i==0 && suid)||(i==1 && sgid))? 's' : 'x';
  7545. } else {
  7546. str += '-';
  7547. }
  7548. }
  7549. }
  7550. if (style == 'both') {
  7551. return str + ' (' + oct + ')';
  7552. } else if (style == 'string') {
  7553. return str;
  7554. } else {
  7555. return oct;
  7556. }
  7557. },
  7558. /**
  7559. * Regist this.decodeRawString function
  7560. *
  7561. * @return void
  7562. */
  7563. registRawStringDecoder : function(rawStringDecoder) {
  7564. if ($.isFunction(rawStringDecoder)) {
  7565. this.decodeRawString = this.options.rawStringDecoder = rawStringDecoder;
  7566. }
  7567. },
  7568. /**
  7569. * Return boolean that uploadable MIME type into target folder
  7570. *
  7571. * @param String mime MIME type
  7572. * @param String target target folder hash
  7573. * @return Bool
  7574. */
  7575. uploadMimeCheck : function(mime, target) {
  7576. target = target || this.cwd().hash;
  7577. var res = true, // default is allow
  7578. mimeChecker = this.option('uploadMime', target),
  7579. allow,
  7580. deny,
  7581. check = function(checker) {
  7582. var ret = false;
  7583. if (typeof checker === 'string' && checker.toLowerCase() === 'all') {
  7584. ret = true;
  7585. } else if (Array.isArray(checker) && checker.length) {
  7586. $.each(checker, function(i, v) {
  7587. v = v.toLowerCase();
  7588. if (v === 'all' || mime.indexOf(v) === 0) {
  7589. ret = true;
  7590. return false;
  7591. }
  7592. });
  7593. }
  7594. return ret;
  7595. };
  7596. if (mime && $.isPlainObject(mimeChecker)) {
  7597. mime = mime.toLowerCase();
  7598. allow = check(mimeChecker.allow);
  7599. deny = check(mimeChecker.deny);
  7600. if (mimeChecker.firstOrder === 'allow') {
  7601. res = false; // default is deny
  7602. if (! deny && allow === true) { // match only allow
  7603. res = true;
  7604. }
  7605. } else {
  7606. res = true; // default is allow
  7607. if (deny === true && ! allow) { // match only deny
  7608. res = false;
  7609. }
  7610. }
  7611. }
  7612. return res;
  7613. },
  7614. /**
  7615. * call chained sequence of async deferred functions
  7616. *
  7617. * @param Array tasks async functions
  7618. * @return Object jQuery.Deferred
  7619. */
  7620. sequence : function(tasks) {
  7621. var l = tasks.length,
  7622. chain = function(task, idx) {
  7623. ++idx;
  7624. if (tasks[idx]) {
  7625. return chain(task.then(tasks[idx]), idx);
  7626. } else {
  7627. return task;
  7628. }
  7629. };
  7630. if (l > 1) {
  7631. return chain(tasks[0](), 0);
  7632. } else {
  7633. return tasks[0]();
  7634. }
  7635. },
  7636. /**
  7637. * Reload contents of target URL for clear browser cache
  7638. *
  7639. * @param String url target URL
  7640. * @return Object jQuery.Deferred
  7641. */
  7642. reloadContents : function(url) {
  7643. var dfd = $.Deferred(),
  7644. ifm;
  7645. try {
  7646. ifm = $('<iframe width="1" height="1" scrolling="no" frameborder="no" style="position:absolute; top:-1px; left:-1px" crossorigin="use-credentials">')
  7647. .attr('src', url)
  7648. .one('load', function() {
  7649. var ifm = $(this);
  7650. try {
  7651. this.contentDocument.location.reload(true);
  7652. ifm.one('load', function() {
  7653. ifm.remove();
  7654. dfd.resolve();
  7655. });
  7656. } catch(e) {
  7657. ifm.attr('src', '').attr('src', url).one('load', function() {
  7658. ifm.remove();
  7659. dfd.resolve();
  7660. });
  7661. }
  7662. })
  7663. .appendTo('body');
  7664. } catch(e) {
  7665. ifm && ifm.remove();
  7666. dfd.reject();
  7667. }
  7668. return dfd;
  7669. },
  7670. /**
  7671. * Make netmount option for OAuth2
  7672. *
  7673. * @param String protocol
  7674. * @param String name
  7675. * @param String host
  7676. * @param Object opts Default {noOffline: false, root: 'root', pathI18n: 'folderId', folders: true}
  7677. }
  7678. *
  7679. * @return Object
  7680. */
  7681. makeNetmountOptionOauth : function(protocol, name, host, opt) {
  7682. var noOffline = typeof opt === 'boolean'? opt : null, // for backward compat
  7683. opts = Object.assign({
  7684. noOffline : false,
  7685. root : 'root',
  7686. pathI18n : 'folderId',
  7687. folders : true
  7688. }, (noOffline === null? (opt || {}) : {noOffline : noOffline})),
  7689. addFolders = function(fm, bro, folders) {
  7690. var self = this,
  7691. cnt = Object.keys($.isPlainObject(folders)? folders : {}).length,
  7692. select;
  7693. bro.next().remove();
  7694. if (cnt) {
  7695. select = $('<select class="ui-corner-all elfinder-tabstop" style="max-width:200px;">').append(
  7696. $($.map(folders, function(n,i){return '<option value="'+fm.escape((i+'').trim())+'">'+fm.escape(n)+'</option>';}).join(''))
  7697. ).on('change click', function(e){
  7698. var node = $(this),
  7699. path = node.val(),
  7700. spn;
  7701. self.inputs.path.val(path);
  7702. if (opts.folders && (e.type === 'change' || node.data('current') !== path)) {
  7703. node.next().remove();
  7704. node.data('current', path);
  7705. if (path != opts.root) {
  7706. spn = spinner();
  7707. if (xhr && xhr.state() === 'pending') {
  7708. fm.abortXHR(xhr, { quiet: true , abort: true });
  7709. }
  7710. node.after(spn);
  7711. xhr = fm.request({
  7712. data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', path: path, pass: 'folders'},
  7713. preventDefault : true
  7714. }).done(function(data){
  7715. addFolders.call(self, fm, node, data.folders);
  7716. }).always(function() {
  7717. fm.abortXHR(xhr, { quiet: true });
  7718. spn.remove();
  7719. }).xhr;
  7720. }
  7721. }
  7722. });
  7723. bro.after($('<div/>').append(select))
  7724. .closest('.ui-dialog').trigger('tabstopsInit');
  7725. select.trigger('focus');
  7726. }
  7727. },
  7728. spinner = function() {
  7729. return $('<div class="elfinder-netmount-spinner"/>').append('<span class="elfinder-info-spinner"/>');
  7730. },
  7731. xhr;
  7732. return {
  7733. vars : {},
  7734. name : name,
  7735. inputs: {
  7736. offline : $('<input type="checkbox"/>').on('change', function() {
  7737. $(this).parents('table.elfinder-netmount-tb').find('select:first').trigger('change', 'reset');
  7738. }),
  7739. host : $('<span><span class="elfinder-info-spinner"/></span><input type="hidden"/>'),
  7740. path : $('<input type="text" value="'+opts.root+'"/>'),
  7741. user : $('<input type="hidden"/>'),
  7742. pass : $('<input type="hidden"/>')
  7743. },
  7744. select: function(fm, ev, d){
  7745. var f = this.inputs,
  7746. oline = f.offline,
  7747. f0 = $(f.host[0]),
  7748. data = d || null;
  7749. this.vars.mbtn = f.host.closest('.ui-dialog').children('.ui-dialog-buttonpane:first').find('button.elfinder-btncnt-0');
  7750. if (! f0.data('inrequest')
  7751. && (f0.find('span.elfinder-info-spinner').length
  7752. || data === 'reset'
  7753. || (data === 'winfocus' && ! f0.siblings('span.elfinder-button-icon-reload').length))
  7754. )
  7755. {
  7756. if (oline.parent().children().length === 1) {
  7757. f.path.parent().prev().html(fm.i18n(opts.pathI18n));
  7758. oline.attr('title', fm.i18n('offlineAccess'));
  7759. oline.uniqueId().after($('<label/>').attr('for', oline.attr('id')).html(' '+fm.i18n('offlineAccess')));
  7760. }
  7761. f0.data('inrequest', true).empty().addClass('elfinder-info-spinner')
  7762. .parent().find('span.elfinder-button-icon').remove();
  7763. fm.request({
  7764. data : {cmd : 'netmount', protocol: protocol, host: host, user: 'init', options: {id: fm.id, offline: oline.prop('checked')? 1:0, pass: f.host[1].value}},
  7765. preventDefault : true
  7766. }).done(function(data){
  7767. f0.removeClass("elfinder-info-spinner").html(data.body.replace(/\{msg:([^}]+)\}/g, function(whole,s1){return fm.i18n(s1, host);}));
  7768. });
  7769. opts.noOffline && oline.closest('tr').hide();
  7770. } else {
  7771. oline.closest('tr')[(opts.noOffline || f.user.val())? 'hide':'show']();
  7772. f0.data('funcexpup') && f0.data('funcexpup')();
  7773. }
  7774. this.vars.mbtn[$(f.host[1]).val()? 'show':'hide']();
  7775. },
  7776. done: function(fm, data){
  7777. var f = this.inputs,
  7778. p = this.protocol,
  7779. f0 = $(f.host[0]),
  7780. f1 = $(f.host[1]),
  7781. expires = '&nbsp;';
  7782. opts.noOffline && f.offline.closest('tr').hide();
  7783. if (data.mode == 'makebtn') {
  7784. f0.removeClass('elfinder-info-spinner').removeData('expires').removeData('funcexpup');
  7785. f.host.find('input').on('mouseenter mouseleave', function(){$(this).toggleClass('ui-state-hover');});
  7786. f1.val('');
  7787. f.path.val(opts.root).next().remove();
  7788. f.user.val('');
  7789. f.pass.val('');
  7790. ! opts.noOffline && f.offline.closest('tr').show();
  7791. this.vars.mbtn.hide();
  7792. } else if (data.mode == 'folders') {
  7793. if (data.folders) {
  7794. addFolders.call(this, fm, f.path.nextAll(':last'), data.folders);
  7795. }
  7796. } else {
  7797. if (data.expires) {
  7798. expires = '()';
  7799. f0.data('expires', data.expires);
  7800. }
  7801. f0.html(host + expires).removeClass('elfinder-info-spinner');
  7802. if (data.expires) {
  7803. f0.data('funcexpup', function() {
  7804. var rem = Math.floor((f0.data('expires') - (+new Date()) / 1000) / 60);
  7805. if (rem < 3) {
  7806. f0.parent().children('.elfinder-button-icon-reload').click();
  7807. } else {
  7808. f0.text(f0.text().replace(/\(.*\)/, '('+fm.i18n(['minsLeft', rem])+')'));
  7809. setTimeout(function() {
  7810. if (f0.is(':visible')) {
  7811. f0.data('funcexpup')();
  7812. }
  7813. }, 60000);
  7814. }
  7815. });
  7816. f0.data('funcexpup')();
  7817. }
  7818. if (data.reset) {
  7819. p.trigger('change', 'reset');
  7820. return;
  7821. }
  7822. f0.parent().append($('<span class="elfinder-button-icon elfinder-button-icon-reload" title="'+fm.i18n('reAuth')+'">')
  7823. .on('click', function() {
  7824. f1.val('reauth');
  7825. p.trigger('change', 'reset');
  7826. }));
  7827. f1.val(protocol);
  7828. this.vars.mbtn.show();
  7829. if (data.folders) {
  7830. addFolders.call(this, fm, f.path, data.folders);
  7831. }
  7832. f.user.val('done');
  7833. f.pass.val('done');
  7834. f.offline.closest('tr').hide();
  7835. }
  7836. f0.removeData('inrequest');
  7837. },
  7838. fail: function(fm, err){
  7839. $(this.inputs.host[0]).removeData('inrequest');
  7840. this.protocol.trigger('change', 'reset');
  7841. }
  7842. };
  7843. },
  7844. /**
  7845. * Find cwd's nodes from files
  7846. *
  7847. * @param Array files
  7848. * @param Object opts {firstOnly: true|false}
  7849. */
  7850. findCwdNodes : function(files, opts) {
  7851. var self = this,
  7852. cwd = this.getUI('cwd'),
  7853. cwdHash = this.cwd().hash,
  7854. newItem = $();
  7855. opts = opts || {};
  7856. $.each(files, function(i, f) {
  7857. if (f.phash === cwdHash || self.searchStatus.state > 1) {
  7858. newItem = newItem.add(cwd.find('#'+self.cwdHash2Id(f.hash)));
  7859. if (opts.firstOnly) {
  7860. return false;
  7861. }
  7862. }
  7863. });
  7864. return newItem;
  7865. },
  7866. /**
  7867. * Convert from relative URL to abstract URL based on current URL
  7868. *
  7869. * @param String URL
  7870. * @return String
  7871. */
  7872. convAbsUrl : function(url) {
  7873. if (url.match(/^http/i)) {
  7874. return url;
  7875. }
  7876. if (url.substr(0,2) === '//') {
  7877. return window.location.protocol + url;
  7878. }
  7879. var root = window.location.protocol + '//' + window.location.host,
  7880. reg = /[^\/]+\/\.\.\//,
  7881. ret;
  7882. if (url.substr(0, 1) === '/') {
  7883. ret = root + url;
  7884. } else {
  7885. ret = root + window.location.pathname.replace(/\/[^\/]+$/, '/') + url;
  7886. }
  7887. ret = ret.replace('/./', '/');
  7888. while(reg.test(ret)) {
  7889. ret = ret.replace(reg, '');
  7890. }
  7891. return ret;
  7892. },
  7893. /**
  7894. * Is same origin to current site
  7895. *
  7896. * @param String check url
  7897. * @return Boolean
  7898. */
  7899. isSameOrigin : function (checkUrl) {
  7900. var url;
  7901. checkUrl = this.convAbsUrl(checkUrl);
  7902. if (location.origin && window.URL) {
  7903. try {
  7904. url = new URL(checkUrl);
  7905. return location.origin === url.origin;
  7906. } catch(e) {}
  7907. }
  7908. url = document.createElement('a');
  7909. url.href = checkUrl;
  7910. return location.protocol === url.protocol && location.host === url.host && location.port && url.port;
  7911. },
  7912. navHash2Id : function(hash) {
  7913. return this.navPrefix + hash;
  7914. },
  7915. navId2Hash : function(id) {
  7916. return typeof(id) == 'string' ? id.substr(this.navPrefix.length) : false;
  7917. },
  7918. cwdHash2Id : function(hash) {
  7919. return this.cwdPrefix + hash;
  7920. },
  7921. cwdId2Hash : function(id) {
  7922. return typeof(id) == 'string' ? id.substr(this.cwdPrefix.length) : false;
  7923. },
  7924. isInWindow : function(elem, nochkHide) {
  7925. var elm, rect;
  7926. if (! (elm = elem.get(0))) {
  7927. return false;
  7928. }
  7929. if (! nochkHide && elm.offsetParent === null) {
  7930. return false;
  7931. }
  7932. rect = elm.getBoundingClientRect();
  7933. return document.elementFromPoint(rect.left, rect.top)? true : false;
  7934. },
  7935. /**
  7936. * calculate elFinder node z-index
  7937. *
  7938. * @return void
  7939. */
  7940. zIndexCalc : function() {
  7941. var self = this,
  7942. node = this.getUI(),
  7943. ni = node.css('z-index');
  7944. if (ni && ni !== 'auto' && ni !== 'inherit') {
  7945. self.zIndex = ni;
  7946. } else {
  7947. node.parents().each(function(i, n) {
  7948. var z = $(n).css('z-index');
  7949. if (z !== 'auto' && z !== 'inherit' && (z = parseInt(z))) {
  7950. self.zIndex = z;
  7951. return false;
  7952. }
  7953. });
  7954. }
  7955. },
  7956. /**
  7957. * Load JavaScript files
  7958. *
  7959. * @param Array urls to load JavaScript file URLs
  7960. * @param Function callback call back function on script loaded
  7961. * @param Object opts Additional options to $.ajax OR {loadType: 'tag'} to load by script tag
  7962. * @param Object check { obj: (Object)ParentObject, name: (String)"Attribute name", timeout: (Integer)milliseconds }
  7963. * @return elFinder
  7964. */
  7965. loadScript : function(urls, callback, opts, check) {
  7966. var defOpts = {
  7967. dataType : 'script',
  7968. cache : true
  7969. },
  7970. success, cnt, scripts = {}, results = {};
  7971. opts = opts || {};
  7972. if (opts.tryRequire && this.hasRequire) {
  7973. require(urls, callback, opts.error);
  7974. } else {
  7975. success = function() {
  7976. var cnt, fi, hasError;
  7977. $.each(results, function(i, status) {
  7978. if (status !== 'success' && status !== 'notmodified') {
  7979. hasError = true;
  7980. return false;
  7981. }
  7982. });
  7983. if (!hasError) {
  7984. if ($.isFunction(callback)) {
  7985. if (check) {
  7986. if (typeof check.obj[check.name] === 'undefined') {
  7987. cnt = check.timeout? (check.timeout / 10) : 1;
  7988. fi = setInterval(function() {
  7989. if (--cnt < 0 || typeof check.obj[check.name] !== 'undefined') {
  7990. clearInterval(fi);
  7991. callback();
  7992. }
  7993. }, 10);
  7994. } else {
  7995. callback();
  7996. }
  7997. } else {
  7998. callback();
  7999. }
  8000. }
  8001. } else {
  8002. if (opts.error && $.isFunction(opts.error)) {
  8003. opts.error({ loadResults: results });
  8004. }
  8005. }
  8006. };
  8007. if (opts.loadType === 'tag') {
  8008. $('head > script').each(function() {
  8009. scripts[this.src] = this;
  8010. });
  8011. cnt = urls.length;
  8012. $.each(urls, function(i, url) {
  8013. var done = false,
  8014. script;
  8015. if (scripts[url]) {
  8016. results[i] = scripts[url]._error || 'success';
  8017. (--cnt < 1) && success();
  8018. } else {
  8019. script = document.createElement('script');
  8020. script.charset = opts.charset || 'UTF-8';
  8021. $('head').append(script);
  8022. script.onload = script.onreadystatechange = function() {
  8023. if ( !done && (!this.readyState ||
  8024. this.readyState === 'loaded' || this.readyState === 'complete') ) {
  8025. done = true;
  8026. results[i] = 'success';
  8027. (--cnt < 1) && success();
  8028. }
  8029. };
  8030. script.onerror = function(err) {
  8031. results[i] = script._error = (err && err.type)? err.type : 'error';
  8032. (--cnt < 1) && success();
  8033. };
  8034. script.src = url;
  8035. }
  8036. });
  8037. } else {
  8038. opts = $.isPlainObject(opts)? Object.assign(defOpts, opts) : defOpts;
  8039. cnt = 0;
  8040. (function appendScript(d, status) {
  8041. if (d !== void(0)) {
  8042. results[cnt++] = status;
  8043. }
  8044. if (urls.length) {
  8045. $.ajax(Object.assign({}, opts, {
  8046. url: urls.shift(),
  8047. success: appendScript,
  8048. error: appendScript
  8049. }));
  8050. } else {
  8051. success();
  8052. }
  8053. })();
  8054. }
  8055. }
  8056. return this;
  8057. },
  8058. /**
  8059. * Load CSS files
  8060. *
  8061. * @param Array to load CSS file URLs
  8062. * @return elFinder
  8063. */
  8064. loadCss : function(urls) {
  8065. var self = this;
  8066. if (typeof urls === 'string') {
  8067. urls = [ urls ];
  8068. }
  8069. $.each(urls, function(i, url) {
  8070. url = self.convAbsUrl(url).replace(/^https?:/i, '');
  8071. if (! $("head > link[href='+url+']").length) {
  8072. $('head').append('<link rel="stylesheet" type="text/css" href="' + url + '" />');
  8073. }
  8074. });
  8075. return this;
  8076. },
  8077. /**
  8078. * Abortable async job performer
  8079. *
  8080. * @param func Function
  8081. * @param arr Array
  8082. * @param opts Object
  8083. *
  8084. * @return Object $.Deferred that has an extended method _abort()
  8085. */
  8086. asyncJob : function(func, arr, opts) {
  8087. var dfrd = $.Deferred(),
  8088. abortFlg = false,
  8089. parms = Object.assign({
  8090. interval : 0,
  8091. numPerOnce : 1
  8092. }, opts || {}),
  8093. resArr = [],
  8094. vars =[],
  8095. curVars = [],
  8096. exec,
  8097. tm;
  8098. dfrd._abort = function(resolve) {
  8099. tm && clearTimeout(tm);
  8100. vars = [];
  8101. abortFlg = true;
  8102. if (dfrd.state() === 'pending') {
  8103. dfrd[resolve? 'resolve' : 'reject'](resArr);
  8104. }
  8105. };
  8106. dfrd.fail(function() {
  8107. dfrd._abort();
  8108. }).always(function() {
  8109. dfrd._abort = function() {};
  8110. });
  8111. if (typeof func === 'function' && Array.isArray(arr)) {
  8112. vars = arr.concat();
  8113. exec = function() {
  8114. var i, len, res;
  8115. if (abortFlg) {
  8116. return;
  8117. }
  8118. curVars = vars.splice(0, parms.numPerOnce);
  8119. len = curVars.length;
  8120. for (i = 0; i < len; i++) {
  8121. if (abortFlg) {
  8122. break;
  8123. }
  8124. res = func(curVars[i]);
  8125. (res !== null) && resArr.push(res);
  8126. }
  8127. if (abortFlg) {
  8128. return;
  8129. }
  8130. if (vars.length) {
  8131. tm = setTimeout(exec, parms.interval);
  8132. } else {
  8133. dfrd.resolve(resArr);
  8134. }
  8135. };
  8136. if (vars.length) {
  8137. tm = setTimeout(exec, 0);
  8138. } else {
  8139. dfrd.resolve(resArr);
  8140. }
  8141. } else {
  8142. dfrd.reject();
  8143. }
  8144. return dfrd;
  8145. },
  8146. getSize : function(targets) {
  8147. var self = this,
  8148. reqs = [],
  8149. dfrd = $.Deferred().fail(function() {
  8150. $.each(reqs, function(i, req) {
  8151. if (req) {
  8152. req.syncOnFail && req.syncOnFail(false);
  8153. req.reject();
  8154. }
  8155. });
  8156. }),
  8157. getLeafRoots = function(file) {
  8158. var targets = [];
  8159. if (file.mime === 'directory') {
  8160. $.each(self.leafRoots, function(hash, roots) {
  8161. var phash;
  8162. if (hash === file.hash) {
  8163. targets.push.apply(targets, roots);
  8164. } else {
  8165. phash = (self.file(hash) || {}).phash;
  8166. while(phash) {
  8167. if (phash === file.hash) {
  8168. targets.push.apply(targets, roots);
  8169. }
  8170. phash = (self.file(phash) || {}).phash;
  8171. }
  8172. }
  8173. });
  8174. }
  8175. return targets;
  8176. },
  8177. checkPhash = function(hash) {
  8178. var dfd = $.Deferred(),
  8179. dir = self.file(hash),
  8180. target = dir? dir.phash : hash;
  8181. if (target && ! self.file(target)) {
  8182. self.request({
  8183. data : {
  8184. cmd : 'parents',
  8185. target : target
  8186. },
  8187. preventFail : true
  8188. }).done(function() {
  8189. self.one('parentsdone', function() {
  8190. dfd.resolve();
  8191. });
  8192. }).fail(function() {
  8193. dfd.resolve();
  8194. });
  8195. } else {
  8196. dfd.resolve();
  8197. }
  8198. return dfd;
  8199. },
  8200. cache = function() {
  8201. var dfd = $.Deferred(),
  8202. cnt = Object.keys(self.leafRoots).length;
  8203. if (cnt > 0) {
  8204. $.each(self.leafRoots, function(hash) {
  8205. checkPhash(hash).done(function() {
  8206. --cnt;
  8207. if (cnt < 1) {
  8208. dfd.resolve();
  8209. }
  8210. });
  8211. });
  8212. } else {
  8213. dfd.resolve();
  8214. }
  8215. return dfd;
  8216. };
  8217. self.autoSync('stop');
  8218. cache().done(function() {
  8219. var files = [], grps = {}, dfds = [], cache = [], singles = {};
  8220. $.each(targets, function() {
  8221. files.push.apply(files, getLeafRoots(self.file(this)));
  8222. });
  8223. targets.push.apply(targets, files);
  8224. $.each(targets, function() {
  8225. var root = self.root(this),
  8226. file = self.file(this);
  8227. if (file && (file.sizeInfo || file.mime !== 'directory')) {
  8228. cache.push($.Deferred().resolve(file.sizeInfo? file.sizeInfo : {size: file.size, dirCnt: 0, fileCnt : 1}));
  8229. } else {
  8230. if (! grps[root]) {
  8231. grps[root] = [ this ];
  8232. } else {
  8233. grps[root].push(this);
  8234. }
  8235. }
  8236. });
  8237. $.each(grps, function() {
  8238. var idx = dfds.length;
  8239. if (this.length === 1) {
  8240. singles[idx] = this[0];
  8241. }
  8242. dfds.push(self.request({
  8243. data : {cmd : 'size', targets : this},
  8244. preventDefault : true
  8245. }));
  8246. });
  8247. reqs.push.apply(reqs, dfds);
  8248. dfds.push.apply(dfds, cache);
  8249. $.when.apply($, dfds).fail(function() {
  8250. dfrd.reject();
  8251. }).done(function() {
  8252. var cache = function(h, data) {
  8253. var file;
  8254. if (file = self.file(h)) {
  8255. file.sizeInfo = { isCache: true };
  8256. $.each(['size', 'dirCnt', 'fileCnt'], function() {
  8257. file.sizeInfo[this] = data[this] || 0;
  8258. });
  8259. file.size = parseInt(file.sizeInfo.size);
  8260. changed.push(file);
  8261. }
  8262. },
  8263. size = 0,
  8264. fileCnt = 0,
  8265. dirCnt = 0,
  8266. argLen = arguments.length,
  8267. cnts = [],
  8268. cntsTxt = '',
  8269. changed = [],
  8270. i, file, data;
  8271. for (i = 0; i < argLen; i++) {
  8272. data = arguments[i];
  8273. file = null;
  8274. if (!data.isCache) {
  8275. if (singles[i] && (file = self.file(singles[i]))) {
  8276. cache(singles[i], data);
  8277. } else if (data.sizes && $.isPlainObject(data.sizes)) {
  8278. $.each(data.sizes, function(h, sizeInfo) {
  8279. cache(h, sizeInfo);
  8280. });
  8281. }
  8282. }
  8283. size += parseInt(data.size);
  8284. if (fileCnt !== false) {
  8285. if (typeof data.fileCnt === 'undefined') {
  8286. fileCnt = false;
  8287. }
  8288. fileCnt += parseInt(data.fileCnt || 0);
  8289. }
  8290. if (dirCnt !== false) {
  8291. if (typeof data.dirCnt === 'undefined') {
  8292. dirCnt = false;
  8293. }
  8294. dirCnt += parseInt(data.dirCnt || 0);
  8295. }
  8296. }
  8297. changed.length && self.change({changed: changed});
  8298. if (dirCnt !== false){
  8299. cnts.push(self.i18n('folders') + ': ' + dirCnt);
  8300. }
  8301. if (fileCnt !== false){
  8302. cnts.push(self.i18n('files') + ': ' + fileCnt);
  8303. }
  8304. if (cnts.length) {
  8305. cntsTxt = '<br>' + cnts.join(', ');
  8306. }
  8307. dfrd.resolve({
  8308. size: size,
  8309. fileCnt: fileCnt,
  8310. dirCnt: dirCnt,
  8311. formated: (size >= 0 ? self.formatSize(size) : self.i18n('unknown')) + cntsTxt
  8312. });
  8313. });
  8314. self.autoSync();
  8315. });
  8316. return dfrd;
  8317. },
  8318. /**
  8319. * Apply leaf root stats to target directory
  8320. *
  8321. * @param object dir object of target directory
  8322. * @param boolean update is force update
  8323. *
  8324. * @return boolean dir object was chenged
  8325. */
  8326. applyLeafRootStats : function(dir, update) {
  8327. var self = this,
  8328. prev = update? dir : (self.file(dir.hash) || dir),
  8329. prevTs = prev.ts,
  8330. change = false;
  8331. // backup original stats
  8332. if (update || !dir._realStats) {
  8333. dir._realStats = {
  8334. locked: dir.locked || 0,
  8335. dirs: dir.dirs || 0,
  8336. ts: dir.ts
  8337. };
  8338. }
  8339. // set lock
  8340. if (!prev.locked) {
  8341. dir.locked = 1;
  8342. change = true;
  8343. }
  8344. // has leaf root to `dirs: 1`
  8345. if (!prev.dirs) {
  8346. dir.dirs = 1;
  8347. change = true;
  8348. }
  8349. // set ts
  8350. $.each(self.leafRoots[dir.hash], function() {
  8351. var f = self.file(this);
  8352. if (f && f.ts && (dir.ts || 0) < f.ts) {
  8353. dir.ts = f.ts;
  8354. }
  8355. });
  8356. if (prevTs !== dir.ts) {
  8357. change = true;
  8358. }
  8359. return change;
  8360. },
  8361. /**
  8362. * To aborted XHR object
  8363. *
  8364. * @param Object xhr
  8365. * @param Object opts
  8366. *
  8367. * @return void
  8368. */
  8369. abortXHR : function(xhr, o) {
  8370. var opts = o || {};
  8371. if (xhr) {
  8372. opts.quiet && (xhr.quiet = true);
  8373. if (opts.abort && xhr._requestId) {
  8374. this.request({
  8375. data: {
  8376. cmd: 'abort',
  8377. id: xhr._requestId
  8378. },
  8379. preventDefault: true
  8380. });
  8381. }
  8382. xhr.abort();
  8383. xhr = void 0;
  8384. }
  8385. },
  8386. /**
  8387. * Flip key and value of array or object
  8388. *
  8389. * @param Array | Object { a: 1, b: 1, c: 2 }
  8390. * @param Mixed Static value
  8391. * @return Object { 1: "b", 2: "c" }
  8392. */
  8393. arrayFlip : function (trans, val) {
  8394. var key,
  8395. tmpArr = {},
  8396. isArr = $.isArray(trans);
  8397. for (key in trans) {
  8398. if (isArr || trans.hasOwnProperty(key)) {
  8399. tmpArr[trans[key]] = val || key;
  8400. }
  8401. }
  8402. return tmpArr;
  8403. },
  8404. /**
  8405. * Return array ["name without extention", "extention"]
  8406. *
  8407. * @param String name
  8408. *
  8409. * @return Array
  8410. *
  8411. */
  8412. splitFileExtention : function(name) {
  8413. var m;
  8414. if (m = name.match(/^(.+?)?\.((?:tar\.(?:gz|bz|bz2|z|lzo))|cpio\.gz|ps\.gz|xcf\.(?:gz|bz2)|[a-z0-9]{1,4})$/i)) {
  8415. if (typeof m[1] === 'undefined') {
  8416. m[1] = '';
  8417. }
  8418. return [m[1], m[2]];
  8419. } else {
  8420. return [name, ''];
  8421. }
  8422. },
  8423. /**
  8424. * Slice the ArrayBuffer by sliceSize
  8425. *
  8426. * @param arraybuffer arrayBuffer The array buffer
  8427. * @param Number sliceSize The slice size
  8428. * @return Array Array of sleced arraybuffer
  8429. */
  8430. sliceArrayBuffer : function(arrayBuffer, sliceSize) {
  8431. var segments= [],
  8432. fi = 0;
  8433. while(fi * sliceSize < arrayBuffer.byteLength){
  8434. segments.push(arrayBuffer.slice(fi * sliceSize, (fi + 1) * sliceSize));
  8435. fi++;
  8436. }
  8437. return segments;
  8438. },
  8439. log : function(m) { window.console && window.console.log && window.console.log(m); return this; },
  8440. debug : function(type, m) {
  8441. var d = this.options.debug;
  8442. if (d && (d === 'all' || d[type])) {
  8443. window.console && window.console.log && window.console.log('elfinder debug: ['+type+'] ['+this.id+']', m);
  8444. }
  8445. if (type === 'backend-error') {
  8446. if (! this.cwd().hash || (d && (d === 'all' || d['backend-error']))) {
  8447. m = Array.isArray(m)? m : [ m ];
  8448. this.error(m);
  8449. }
  8450. } else if (type === 'backend-debug') {
  8451. this.trigger('backenddebug', m);
  8452. }
  8453. return this;
  8454. },
  8455. time : function(l) { window.console && window.console.time && window.console.time(l); },
  8456. timeEnd : function(l) { window.console && window.console.timeEnd && window.console.timeEnd(l); }
  8457. };
  8458. /**
  8459. * for conpat ex. ie8...
  8460. *
  8461. * Object.keys() - JavaScript | MDN
  8462. * https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
  8463. */
  8464. if (!Object.keys) {
  8465. Object.keys = (function () {
  8466. var hasOwnProperty = Object.prototype.hasOwnProperty,
  8467. hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
  8468. dontEnums = [
  8469. 'toString',
  8470. 'toLocaleString',
  8471. 'valueOf',
  8472. 'hasOwnProperty',
  8473. 'isPrototypeOf',
  8474. 'propertyIsEnumerable',
  8475. 'constructor'
  8476. ],
  8477. dontEnumsLength = dontEnums.length;
  8478. return function (obj) {
  8479. if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
  8480. var result = [];
  8481. for (var prop in obj) {
  8482. if (hasOwnProperty.call(obj, prop)) result.push(prop);
  8483. }
  8484. if (hasDontEnumBug) {
  8485. for (var i=0; i < dontEnumsLength; i++) {
  8486. if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
  8487. }
  8488. }
  8489. return result;
  8490. };
  8491. })();
  8492. }
  8493. // Array.isArray
  8494. if (!Array.isArray) {
  8495. Array.isArray = function(arr) {
  8496. return jQuery.isArray(arr);
  8497. };
  8498. }
  8499. // Object.assign
  8500. if (!Object.assign) {
  8501. Object.assign = function() {
  8502. return jQuery.extend.apply(null, arguments);
  8503. };
  8504. }
  8505. // String.repeat
  8506. if (!String.prototype.repeat) {
  8507. String.prototype.repeat = function(count) {
  8508. 'use strict';
  8509. if (this == null) {
  8510. throw new TypeError('can\'t convert ' + this + ' to object');
  8511. }
  8512. var str = '' + this;
  8513. count = +count;
  8514. if (count != count) {
  8515. count = 0;
  8516. }
  8517. if (count < 0) {
  8518. throw new RangeError('repeat count must be non-negative');
  8519. }
  8520. if (count == Infinity) {
  8521. throw new RangeError('repeat count must be less than infinity');
  8522. }
  8523. count = Math.floor(count);
  8524. if (str.length == 0 || count == 0) {
  8525. return '';
  8526. }
  8527. // Ensuring count is a 31-bit integer allows us to heavily optimize the
  8528. // main part. But anyway, most current (August 2014) browsers can't handle
  8529. // strings 1 << 28 chars or longer, so:
  8530. if (str.length * count >= 1 << 28) {
  8531. throw new RangeError('repeat count must not overflow maximum string size');
  8532. }
  8533. var rpt = '';
  8534. for (var i = 0; i < count; i++) {
  8535. rpt += str;
  8536. }
  8537. return rpt;
  8538. };
  8539. }
  8540. // Array.apply
  8541. (function () {
  8542. try {
  8543. Array.apply(null, {});
  8544. return;
  8545. } catch (e) { }
  8546. var toString = Object.prototype.toString,
  8547. arrayType = '[object Array]',
  8548. _apply = Function.prototype.apply,
  8549. slice = /*@cc_on @if (@_jscript_version <= 5.8)
  8550. function () {
  8551. var a = [], i = this.length;
  8552. while (i-- > 0) a[i] = this[i];
  8553. return a;
  8554. }@else@*/Array.prototype.slice/*@end@*/;
  8555. Function.prototype.apply = function apply(thisArg, argArray) {
  8556. return _apply.call(this, thisArg,
  8557. toString.call(argArray) === arrayType ? argArray : slice.call(argArray));
  8558. };
  8559. })();
  8560. // Array.from
  8561. if (!Array.from) {
  8562. Array.from = function(obj) {
  8563. return obj.length === 1 ? [obj[0]] : Array.apply(null, obj);
  8564. };
  8565. }
  8566. /*
  8567. * File: /js/elFinder.version.js
  8568. */
  8569. /**
  8570. * Application version
  8571. *
  8572. * @type String
  8573. **/
  8574. elFinder.prototype.version = '2.1.37';
  8575. /*
  8576. * File: /js/jquery.elfinder.js
  8577. */
  8578. /*** jQuery UI droppable performance tune for elFinder ***/
  8579. (function(){
  8580. if ($.ui) {
  8581. if ($.ui.ddmanager) {
  8582. var origin = $.ui.ddmanager.prepareOffsets;
  8583. $.ui.ddmanager.prepareOffsets = function( t, event ) {
  8584. var isOutView = function(elem) {
  8585. if (elem.is(':hidden')) {
  8586. return true;
  8587. }
  8588. var rect = elem[0].getBoundingClientRect();
  8589. return document.elementFromPoint(rect.left, rect.top)? false : true;
  8590. };
  8591. if (event.type === 'mousedown' || t.options.elfRefresh) {
  8592. var i, d,
  8593. m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
  8594. l = m.length;
  8595. for ( i = 0; i < l; i++ ) {
  8596. d = m[ i ];
  8597. if (d.options.autoDisable && (!d.options.disabled || d.options.autoDisable > 1)) {
  8598. d.options.disabled = isOutView(d.element);
  8599. d.options.autoDisable = d.options.disabled? 2 : 1;
  8600. }
  8601. }
  8602. }
  8603. // call origin function
  8604. return origin( t, event );
  8605. };
  8606. }
  8607. }
  8608. })();
  8609. /**
  8610. *
  8611. * jquery.binarytransport.js
  8612. *
  8613. * @description. jQuery ajax transport for making binary data type requests.
  8614. * @version 1.0
  8615. * @author Henry Algus <henryalgus@gmail.com>
  8616. *
  8617. */
  8618. // use this transport for "binary" data type
  8619. $.ajaxTransport('+binary', function(options, originalOptions, jqXHR) {
  8620. // check for conditions and support for blob / arraybuffer response type
  8621. if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob)))))
  8622. {
  8623. var xhr;
  8624. return {
  8625. // create new XMLHttpRequest
  8626. send: function(headers, callback){
  8627. // setup all variables
  8628. xhr = new XMLHttpRequest();
  8629. var url = options.url,
  8630. type = options.type,
  8631. async = options.async || true,
  8632. // blob or arraybuffer. Default is blob
  8633. dataType = options.responseType || 'blob',
  8634. data = options.data || null,
  8635. username = options.username,
  8636. password = options.password;
  8637. xhr.addEventListener('load', function(){
  8638. var data = {};
  8639. data[options.dataType] = xhr.response;
  8640. // make callback and send data
  8641. callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders());
  8642. });
  8643. xhr.open(type, url, async, username, password);
  8644. // setup custom headers
  8645. for (var i in headers ) {
  8646. xhr.setRequestHeader(i, headers[i] );
  8647. }
  8648. // setuo xhrFields
  8649. if (options.xhrFields) {
  8650. for (var key in options.xhrFields) {
  8651. if (key in xhr) {
  8652. xhr[key] = options.xhrFields[key];
  8653. }
  8654. }
  8655. }
  8656. xhr.responseType = dataType;
  8657. xhr.send(data);
  8658. },
  8659. abort: function(){
  8660. xhr.abort();
  8661. }
  8662. };
  8663. }
  8664. });
  8665. /*!
  8666. * jQuery UI Touch Punch 0.2.3
  8667. *
  8668. * Copyright 2011–2014, Dave Furfero
  8669. * Dual licensed under the MIT or GPL Version 2 licenses.
  8670. *
  8671. * Depends:
  8672. * jquery.ui.widget.js
  8673. * jquery.ui.mouse.js
  8674. */
  8675. (function ($) {
  8676. // Detect touch support
  8677. $.support.touch = 'ontouchend' in document;
  8678. // Ignore browsers without touch support
  8679. if (!$.support.touch) {
  8680. return;
  8681. }
  8682. var mouseProto = $.ui.mouse.prototype,
  8683. _mouseInit = mouseProto._mouseInit,
  8684. _mouseDestroy = mouseProto._mouseDestroy,
  8685. touchHandled,
  8686. posX, posY;
  8687. /**
  8688. * Simulate a mouse event based on a corresponding touch event
  8689. * @param {Object} event A touch event
  8690. * @param {String} simulatedType The corresponding mouse event
  8691. */
  8692. function simulateMouseEvent (event, simulatedType) {
  8693. // Ignore multi-touch events
  8694. if (event.originalEvent.touches.length > 1) {
  8695. return;
  8696. }
  8697. if (! $(event.currentTarget).hasClass('touch-punch-keep-default')) {
  8698. event.preventDefault();
  8699. }
  8700. var touch = event.originalEvent.changedTouches[0],
  8701. simulatedEvent = document.createEvent('MouseEvents');
  8702. // Initialize the simulated mouse event using the touch event's coordinates
  8703. simulatedEvent.initMouseEvent(
  8704. simulatedType, // type
  8705. true, // bubbles
  8706. true, // cancelable
  8707. window, // view
  8708. 1, // detail
  8709. touch.screenX, // screenX
  8710. touch.screenY, // screenY
  8711. touch.clientX, // clientX
  8712. touch.clientY, // clientY
  8713. false, // ctrlKey
  8714. false, // altKey
  8715. false, // shiftKey
  8716. false, // metaKey
  8717. 0, // button
  8718. null // relatedTarget
  8719. );
  8720. // Dispatch the simulated event to the target element
  8721. event.target.dispatchEvent(simulatedEvent);
  8722. }
  8723. /**
  8724. * Handle the jQuery UI widget's touchstart events
  8725. * @param {Object} event The widget element's touchstart event
  8726. */
  8727. mouseProto._touchStart = function (event) {
  8728. var self = this;
  8729. // Ignore the event if another widget is already being handled
  8730. if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
  8731. return;
  8732. }
  8733. // Track element position to avoid "false" move
  8734. posX = event.originalEvent.changedTouches[0].screenX.toFixed(0);
  8735. posY = event.originalEvent.changedTouches[0].screenY.toFixed(0);
  8736. // Set the flag to prevent other widgets from inheriting the touch event
  8737. touchHandled = true;
  8738. // Track movement to determine if interaction was a click
  8739. self._touchMoved = false;
  8740. // Simulate the mouseover event
  8741. simulateMouseEvent(event, 'mouseover');
  8742. // Simulate the mousemove event
  8743. simulateMouseEvent(event, 'mousemove');
  8744. // Simulate the mousedown event
  8745. simulateMouseEvent(event, 'mousedown');
  8746. };
  8747. /**
  8748. * Handle the jQuery UI widget's touchmove events
  8749. * @param {Object} event The document's touchmove event
  8750. */
  8751. mouseProto._touchMove = function (event) {
  8752. // Ignore event if not handled
  8753. if (!touchHandled) {
  8754. return;
  8755. }
  8756. // Ignore if it's a "false" move (position not changed)
  8757. var x = event.originalEvent.changedTouches[0].screenX.toFixed(0);
  8758. var y = event.originalEvent.changedTouches[0].screenY.toFixed(0);
  8759. // Ignore if it's a "false" move (position not changed)
  8760. if (Math.abs(posX - x) <= 4 && Math.abs(posY - y) <= 4) {
  8761. return;
  8762. }
  8763. // Interaction was not a click
  8764. this._touchMoved = true;
  8765. // Simulate the mousemove event
  8766. simulateMouseEvent(event, 'mousemove');
  8767. };
  8768. /**
  8769. * Handle the jQuery UI widget's touchend events
  8770. * @param {Object} event The document's touchend event
  8771. */
  8772. mouseProto._touchEnd = function (event) {
  8773. // Ignore event if not handled
  8774. if (!touchHandled) {
  8775. return;
  8776. }
  8777. // Simulate the mouseup event
  8778. simulateMouseEvent(event, 'mouseup');
  8779. // Simulate the mouseout event
  8780. simulateMouseEvent(event, 'mouseout');
  8781. // If the touch interaction did not move, it should trigger a click
  8782. if (!this._touchMoved) {
  8783. // Simulate the click event
  8784. simulateMouseEvent(event, 'click');
  8785. }
  8786. // Unset the flag to allow other widgets to inherit the touch event
  8787. touchHandled = false;
  8788. this._touchMoved = false;
  8789. };
  8790. /**
  8791. * A duck punch of the $.ui.mouse _mouseInit method to support touch events.
  8792. * This method extends the widget with bound touch event handlers that
  8793. * translate touch events to mouse events and pass them to the widget's
  8794. * original mouse event handling methods.
  8795. */
  8796. mouseProto._mouseInit = function () {
  8797. var self = this;
  8798. if (self.element.hasClass('touch-punch')) {
  8799. // Delegate the touch handlers to the widget's element
  8800. self.element.on({
  8801. touchstart: $.proxy(self, '_touchStart'),
  8802. touchmove: $.proxy(self, '_touchMove'),
  8803. touchend: $.proxy(self, '_touchEnd')
  8804. });
  8805. }
  8806. // Call the original $.ui.mouse init method
  8807. _mouseInit.call(self);
  8808. };
  8809. /**
  8810. * Remove the touch event handlers
  8811. */
  8812. mouseProto._mouseDestroy = function () {
  8813. var self = this;
  8814. if (self.element.hasClass('touch-punch')) {
  8815. // Delegate the touch handlers to the widget's element
  8816. self.element.off({
  8817. touchstart: $.proxy(self, '_touchStart'),
  8818. touchmove: $.proxy(self, '_touchMove'),
  8819. touchend: $.proxy(self, '_touchEnd')
  8820. });
  8821. }
  8822. // Call the original $.ui.mouse destroy method
  8823. _mouseDestroy.call(self);
  8824. };
  8825. })(jQuery);
  8826. $.fn.elfinder = function(o, o2) {
  8827. if (o === 'instance') {
  8828. return this.getElFinder();
  8829. }
  8830. return this.each(function() {
  8831. var cmd = typeof o === 'string' ? o : '',
  8832. bootCallback = typeof o2 === 'function'? o2 : void(0),
  8833. opts;
  8834. if (!this.elfinder) {
  8835. if ($.isPlainObject(o)) {
  8836. new elFinder(this, o, bootCallback);
  8837. }
  8838. } else {
  8839. switch(cmd) {
  8840. case 'close':
  8841. case 'hide':
  8842. this.elfinder.hide();
  8843. break;
  8844. case 'open':
  8845. case 'show':
  8846. this.elfinder.show();
  8847. break;
  8848. case 'destroy':
  8849. this.elfinder.destroy();
  8850. break;
  8851. case 'reload':
  8852. case 'restart':
  8853. if (this.elfinder) {
  8854. opts = this.elfinder.options;
  8855. bootCallback = this.elfinder.bootCallback;
  8856. this.elfinder.destroy();
  8857. new elFinder(this, $.extend(true, opts, $.isPlainObject(o2)? o2 : {}), bootCallback);
  8858. }
  8859. break;
  8860. }
  8861. }
  8862. });
  8863. };
  8864. $.fn.getElFinder = function() {
  8865. var instance;
  8866. this.each(function() {
  8867. if (this.elfinder) {
  8868. instance = this.elfinder;
  8869. return false;
  8870. }
  8871. });
  8872. return instance;
  8873. };
  8874. $.fn.elfUiWidgetInstance = function(name) {
  8875. try {
  8876. return this[name]('instance');
  8877. } catch(e) {
  8878. // fallback for jQuery UI < 1.11
  8879. var data = this.data('ui-' + name);
  8880. if (data && typeof data === 'object' && data.widgetFullName === 'ui-' + name) {
  8881. return data;
  8882. }
  8883. return null;
  8884. }
  8885. };
  8886. // function scrollRight
  8887. if (! $.fn.scrollRight) {
  8888. $.fn.extend({
  8889. scrollRight: function (val) {
  8890. var node = this.get(0);
  8891. if (val === undefined) {
  8892. return Math.max(0, node.scrollWidth - (node.scrollLeft + node.clientWidth));
  8893. }
  8894. return this.scrollLeft(node.scrollWidth - node.clientWidth - val);
  8895. }
  8896. });
  8897. }
  8898. // function scrollBottom
  8899. if (! $.fn.scrollBottom) {
  8900. $.fn.extend({
  8901. scrollBottom: function(val) {
  8902. var node = this.get(0);
  8903. if (val === undefined) {
  8904. return Math.max(0, node.scrollHeight - (node.scrollTop + node.clientHeight));
  8905. }
  8906. return this.scrollTop(node.scrollHeight - node.clientHeight - val);
  8907. }
  8908. });
  8909. }
  8910. /*
  8911. * File: /js/elFinder.mimetypes.js
  8912. */
  8913. elFinder.prototype.mimeTypes = {"application\/postscript":"ai","application\/x-executable":"exe","application\/msword":"doc","application\/vnd.ms-excel":"xls","application\/vnd.ms-powerpoint":"ppt","application\/pdf":"pdf","text\/xml":"xml","application\/x-shockwave-flash":"swf","application\/x-bittorrent":"torrent","application\/x-jar":"jar","application\/vnd.oasis.opendocument.text":"odt","application\/vnd.oasis.opendocument.text-template":"ott","application\/vnd.oasis.opendocument.text-web":"oth","application\/vnd.oasis.opendocument.text-master":"odm","application\/vnd.oasis.opendocument.graphics":"odg","application\/vnd.oasis.opendocument.graphics-template":"otg","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.chart":"odc","application\/vnd.oasis.opendocument.formula":"odf","application\/vnd.oasis.opendocument.database":"odb","application\/vnd.oasis.opendocument.image":"odi","application\/vnd.openofficeorg.extension":"oxt","application\/vnd.openxmlformats-officedocument.wordprocessingml.document":"docx","application\/vnd.ms-word.document.macroEnabled.12":"docm","application\/vnd.openxmlformats-officedocument.wordprocessingml.template":"dotx","application\/vnd.ms-word.template.macroEnabled.12":"dotm","application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet":"xlsx","application\/vnd.ms-excel.sheet.macroEnabled.12":"xlsm","application\/vnd.openxmlformats-officedocument.spreadsheetml.template":"xltx","application\/vnd.ms-excel.template.macroEnabled.12":"xltm","application\/vnd.ms-excel.sheet.binary.macroEnabled.12":"xlsb","application\/vnd.ms-excel.addin.macroEnabled.12":"xlam","application\/vnd.openxmlformats-officedocument.presentationml.presentation":"pptx","application\/vnd.ms-powerpoint.presentation.macroEnabled.12":"pptm","application\/vnd.openxmlformats-officedocument.presentationml.slideshow":"ppsx","application\/vnd.ms-powerpoint.slideshow.macroEnabled.12":"ppsm","application\/vnd.openxmlformats-officedocument.presentationml.template":"potx","application\/vnd.ms-powerpoint.template.macroEnabled.12":"potm","application\/vnd.ms-powerpoint.addin.macroEnabled.12":"ppam","application\/vnd.openxmlformats-officedocument.presentationml.slide":"sldx","application\/vnd.ms-powerpoint.slide.macroEnabled.12":"sldm","application\/x-gzip":"gz","application\/x-bzip2":"bz","application\/x-xz":"xz","application\/zip":"zip","application\/x-rar":"rar","application\/x-tar":"tar","application\/x-7z-compressed":"7z","text\/plain":"txt","text\/x-php":"php","text\/html":"html","text\/javascript":"js","text\/css":"css","application\/rtf":"rtf","application\/rtfd":"rtfd","text\/x-python":"py","text\/x-java-source":"java","text\/x-ruby":"rb","text\/x-shellscript":"sh","text\/x-perl":"pl","text\/x-sql":"sql","text\/x-csrc":"c","text\/x-chdr":"h","text\/x-c++src":"cpp","text\/x-c++hdr":"hh","text\/csv":"csv","text\/x-markdown":"md","image\/x-ms-bmp":"bmp","image\/jpeg":"jpg","image\/gif":"gif","image\/png":"png","image\/tiff":"tif","image\/x-targa":"tga","image\/vnd.adobe.photoshop":"psd","image\/xbm":"xbm","image\/pxm":"pxm","audio\/mpeg":"mp3","audio\/midi":"mid","audio\/ogg":"ogg","audio\/mp4":"m4a","audio\/wav":"wav","audio\/x-ms-wma":"wma","video\/x-msvideo":"avi","video\/x-dv":"dv","video\/mp4":"mp4","video\/mpeg":"mpeg","video\/quicktime":"mov","video\/x-ms-wmv":"wm","video\/x-flv":"flv","video\/x-matroska":"mkv","video\/webm":"webm","video\/ogg":"ogv","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\/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\/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\/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\/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-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-project":"mpp","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-template":"otc","application\/vnd.oasis.opendocument.formula-template":"odft","application\/vnd.oasis.opendocument.image-template":"oti","application\/vnd.olpc-sugar":"xo","application\/vnd.oma.dd2+xml":"dd2","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-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-blorb":"blb","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-silverlight-app":"xap","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-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-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","audio\/adpcm":"adp","audio\/basic":"au","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-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\/ief":"ief","image\/ktx":"ktx","image\/prs.btif":"btif","image\/sgi":"sgi","image\/svg+xml":"svg","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\/n3":"n3","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-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\/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\/x-f4v":"f4v","video\/x-fli":"fli","video\/x-m4v":"m4v","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-sgi-movie":"movie","video\/x-smv":"smv","x-conference\/x-cooltalk":"ice","text\/x-httpd-cgi":"cgi","text\/x-asap":"asp","text\/x-jsp":"jsp"};
  8914. /*
  8915. * File: /js/elFinder.options.js
  8916. */
  8917. /**
  8918. * Default elFinder config
  8919. *
  8920. * @type Object
  8921. * @autor Dmitry (dio) Levashov
  8922. */
  8923. elFinder.prototype._options = {
  8924. /**
  8925. * URLs of 3rd party libraries CDN
  8926. *
  8927. * @type Object
  8928. */
  8929. cdns : {
  8930. // for editor etc.
  8931. ace : '//cdnjs.cloudflare.com/ajax/libs/ace/1.3.1',
  8932. codemirror : '//cdnjs.cloudflare.com/ajax/libs/codemirror/5.35.0',
  8933. ckeditor : '//cdnjs.cloudflare.com/ajax/libs/ckeditor/4.9.0',
  8934. //ckeditor5 : '//cdn.ckeditor.com/ckeditor5/1.0.0-beta.1',
  8935. ckeditor5 : '//cdn.rawgit.com/ckeditor/ckeditor5-build-',
  8936. tinymce : '//cdnjs.cloudflare.com/ajax/libs/tinymce/4.7.9',
  8937. simplemde : '//cdnjs.cloudflare.com/ajax/libs/simplemde/1.11.2',
  8938. // for quicklook etc.
  8939. hls : '//cdnjs.cloudflare.com/ajax/libs/hls.js/0.8.9/hls.min.js',
  8940. dash : '//cdnjs.cloudflare.com/ajax/libs/dashjs/2.6.7/dash.all.min.js',
  8941. prettify : '//cdn.rawgit.com/google/code-prettify/fbd527e9f76914e36f730ec9849f2115473a65d8/loader/run_prettify.js',
  8942. psd : '//cdnjs.cloudflare.com/ajax/libs/psd.js/3.2.0/psd.min.js',
  8943. rar : '//cdn.rawgit.com/nao-pon/rar.js/6cef13ec66dd67992fc7f3ea22f132d770ebaf8b/rar.min.js',
  8944. zlibUnzip : '//cdn.rawgit.com/imaya/zlib.js/0.3.1/bin/unzip.min.js', // need check unzipFiles() in quicklook.plugins.js when update
  8945. zlibGunzip : '//cdn.rawgit.com/imaya/zlib.js/0.3.1/bin/gunzip.min.js',
  8946. marked : '//cdnjs.cloudflare.com/ajax/libs/marked/0.3.17/marked.min.js',
  8947. sparkmd5 : '//cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.min.js',
  8948. jssha : '//cdnjs.cloudflare.com/ajax/libs/jsSHA/2.3.1/sha.js'
  8949. },
  8950. /**
  8951. * Connector url. Required!
  8952. *
  8953. * @type String
  8954. */
  8955. url : '',
  8956. /**
  8957. * Ajax request type.
  8958. *
  8959. * @type String
  8960. * @default "get"
  8961. */
  8962. requestType : 'get',
  8963. /**
  8964. * Use CORS to connector url
  8965. *
  8966. * @type Boolean|null true|false|null(Auto detect)
  8967. */
  8968. cors : null,
  8969. /**
  8970. * Maximum number of concurrent connections on request
  8971. *
  8972. * @type Number
  8973. * @default 3
  8974. */
  8975. requestMaxConn : 3,
  8976. /**
  8977. * Transport to send request to backend.
  8978. * Required for future extensions using websockets/webdav etc.
  8979. * Must be an object with "send" method.
  8980. * transport.send must return $.Deferred() object
  8981. *
  8982. * @type Object
  8983. * @default null
  8984. * @example
  8985. * transport : {
  8986. * init : function(elfinderInstance) { },
  8987. * send : function(options) {
  8988. * var dfrd = $.Deferred();
  8989. * // connect to backend ...
  8990. * return dfrd;
  8991. * },
  8992. * upload : function(data) {
  8993. * var dfrd = $.Deferred();
  8994. * // upload ...
  8995. * return dfrd;
  8996. * }
  8997. *
  8998. * }
  8999. **/
  9000. transport : {},
  9001. /**
  9002. * URL to upload file to.
  9003. * If not set - connector URL will be used
  9004. *
  9005. * @type String
  9006. * @default ''
  9007. */
  9008. urlUpload : '',
  9009. /**
  9010. * Allow to drag and drop to upload files
  9011. *
  9012. * @type Boolean|String
  9013. * @default 'auto'
  9014. */
  9015. dragUploadAllow : 'auto',
  9016. /**
  9017. * Confirmation dialog displayed at the time of overwriting upload
  9018. *
  9019. * @type Boolean
  9020. * @default true
  9021. */
  9022. overwriteUploadConfirm : true,
  9023. /**
  9024. * Max size of chunked data of file upload
  9025. *
  9026. * @type Number
  9027. * @default 10485760(10MB)
  9028. */
  9029. uploadMaxChunkSize : 10485760,
  9030. /**
  9031. * Regular expression of file name to exclude when uploading folder
  9032. *
  9033. * @type Object
  9034. * @default { win: /^(?:desktop\.ini|thumbs\.db)$/i, mac: /^\.ds_store$/i }
  9035. */
  9036. folderUploadExclude : {
  9037. win: /^(?:desktop\.ini|thumbs\.db)$/i,
  9038. mac: /^\.ds_store$/i
  9039. },
  9040. /**
  9041. * Timeout for upload using iframe
  9042. *
  9043. * @type Number
  9044. * @default 0 - no timeout
  9045. */
  9046. iframeTimeout : 0,
  9047. /**
  9048. * Data to append to all requests and to upload files
  9049. *
  9050. * @type Object
  9051. * @default {}
  9052. */
  9053. customData : {},
  9054. /**
  9055. * Event listeners to bind on elFinder init
  9056. *
  9057. * @type Object
  9058. * @default {}
  9059. */
  9060. handlers : {},
  9061. /**
  9062. * Any custom headers to send across every ajax request
  9063. *
  9064. * @type Object
  9065. * @default {}
  9066. */
  9067. customHeaders : {},
  9068. /**
  9069. * Any custom xhrFields to send across every ajax request
  9070. *
  9071. * @type Object
  9072. * @default {}
  9073. */
  9074. xhrFields : {},
  9075. /**
  9076. * Interface language
  9077. *
  9078. * @type String
  9079. * @default "en"
  9080. */
  9081. lang : 'en',
  9082. /**
  9083. * Base URL of elfFinder library starting from Manager HTML
  9084. * Auto detect when empty value`
  9085. *
  9086. * @type String
  9087. * @default ""
  9088. */
  9089. baseUrl : '',
  9090. /**
  9091. * Auto load required CSS
  9092. * `false` to disable this function or
  9093. * CSS URL Array to load additional CSS files
  9094. *
  9095. * @type Boolean|Array
  9096. * @default true
  9097. */
  9098. cssAutoLoad : true,
  9099. /**
  9100. * Additional css class for filemanager node.
  9101. *
  9102. * @type String
  9103. */
  9104. cssClass : '',
  9105. /**
  9106. * Active commands list. '*' means all of the commands that have been load.
  9107. * If some required commands will be missed here, elFinder will add its
  9108. *
  9109. * @type Array
  9110. */
  9111. commands : ['*'],
  9112. // Available commands list
  9113. //commands : [
  9114. // 'archive', 'back', 'chmod', 'colwidth', 'copy', 'cut', 'download', 'duplicate', 'edit', 'extract',
  9115. // 'forward', 'fullscreen', 'getfile', 'help', 'home', 'info', 'mkdir', 'mkfile', 'netmount', 'netunmount',
  9116. // 'open', 'opendir', 'paste', 'places', 'quicklook', 'reload', 'rename', 'resize', 'restore', 'rm',
  9117. // 'search', 'sort', 'up', 'upload', 'view', 'zipdl'
  9118. //],
  9119. /**
  9120. * Commands options.
  9121. *
  9122. * @type Object
  9123. **/
  9124. commandsOptions : {
  9125. // // configure shortcuts of any command
  9126. // // add `shortcuts` property into each command
  9127. // any_command_name : {
  9128. // shortcuts : [] // for disable this command's shortcuts
  9129. // },
  9130. // any_command_name : {
  9131. // shortcuts : function(fm, shortcuts) {
  9132. // // for add `CTRL + E` for this command action
  9133. // shortcuts[0]['pattern'] += ' ctrl+e';
  9134. // return shortcuts;
  9135. // }
  9136. // },
  9137. // any_command_name : {
  9138. // shortcuts : function(fm, shortcuts) {
  9139. // // for full customize of this command's shortcuts
  9140. // return [ { pattern: 'ctrl+e ctrl+down numpad_enter' + (fm.OS != 'mac' && ' enter') } ];
  9141. // }
  9142. // },
  9143. // "getfile" command options.
  9144. getfile : {
  9145. onlyURL : false,
  9146. // allow to return multiple files info
  9147. multiple : false,
  9148. // allow to return filers info
  9149. folders : false,
  9150. // action after callback (""/"close"/"destroy")
  9151. oncomplete : '',
  9152. // get path before callback call
  9153. getPath : true,
  9154. // get image sizes before callback call
  9155. getImgSize : false
  9156. },
  9157. open : {
  9158. // HTTP method that request to the connector when item URL is not valid URL.
  9159. // If you set to "get" will be displayed request parameter in the browser's location field
  9160. // so if you want to conceal its parameters should be given "post".
  9161. // Nevertheless, please specify "get" if you want to enable the partial request by HTTP Range header.
  9162. method : 'post',
  9163. // Where to open into : 'window'(default), 'tab' or 'tabs'
  9164. // 'tabs' opens in each tabs
  9165. into : 'window'
  9166. },
  9167. // "upload" command options.
  9168. upload : {
  9169. // Open elFinder upload dialog: 'button' OR Open system OS upload dialog: 'uploadbutton'
  9170. ui : 'button'
  9171. },
  9172. // "download" command options.
  9173. download : {
  9174. // max request to download files when zipdl disabled
  9175. maxRequests : 10,
  9176. // minimum count of files to use zipdl
  9177. minFilesZipdl : 2
  9178. },
  9179. // "quicklook" command options.
  9180. quicklook : {
  9181. autoplay : true,
  9182. width : 450,
  9183. height : 300,
  9184. // Maximum characters length to preview
  9185. textMaxlen : 2000,
  9186. // quicklook window must be contained in elFinder node on window open (true|false)
  9187. contain : false,
  9188. // preview window into NavDock (0 : undocked | 1 : docked(show) | 2 : docked(hide))
  9189. docked : 0,
  9190. // Docked preview height ('auto' or Number of pixel) 'auto' is setted to the Navbar width
  9191. dockHeight : 'auto',
  9192. // media auto play when docked
  9193. dockAutoplay : false,
  9194. // MIME types to use Google Docs online viewer
  9195. // 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']
  9196. googleDocsMimes : [],
  9197. // File size (byte) threshold when using the dim command for obtain the image size necessary to image preview
  9198. getDimThreshold : 200000,
  9199. // MIME-Type regular expression that does not check empty files
  9200. mimeRegexNotEmptyCheck : /^application\/vnd\.google-apps\./
  9201. },
  9202. // "quicklook" command options.
  9203. edit : {
  9204. // dialog width, integer(px) or integer+'%' (example: 650, '80%' ...)
  9205. dialogWidth : void(0),
  9206. // list of allowed mimetypes to edit of text files
  9207. // if empty - any text files can be edited
  9208. mimes : [],
  9209. // Use the editor stored in the browser (do not display the choices)
  9210. // This value allowd overwrite with user preferences
  9211. useStoredEditor : false,
  9212. // edit files in wysisyg's
  9213. editors : [
  9214. // {
  9215. // /**
  9216. // * editor info
  9217. // * @type Object
  9218. // */
  9219. // info : { name: 'Editor Name' },
  9220. // /**
  9221. // * files mimetypes allowed to edit in current wysisyg
  9222. // * @type Array
  9223. // */
  9224. // mimes : ['text/html'],
  9225. // /**
  9226. // * HTML element for editing area (optional for text editor)
  9227. // * @type String
  9228. // */
  9229. // html : '<textarea></textarea>',
  9230. // /**
  9231. // * Initialize editing area node (optional for text editor)
  9232. // *
  9233. // * @param String dialog DOM id
  9234. // * @param Object target file object
  9235. // * @param String target file content (text or Data URI Scheme(binary file))
  9236. // * @param Object elFinder instance
  9237. // * @type Function
  9238. // */
  9239. // init : function(id, file, content, fm) {
  9240. // $(this).attr('id', id + '-text').val(content);
  9241. // },
  9242. // /**
  9243. // * Get edited contents (optional for text editor)
  9244. // * @type Function
  9245. // */
  9246. // getContent : function() {
  9247. // return $(this).val();
  9248. // },
  9249. // /**
  9250. // * Called when "edit" dialog loaded.
  9251. // * Place to init wysisyg.
  9252. // * Can return wysisyg instance
  9253. // *
  9254. // * @param DOMElement textarea node
  9255. // * @return Object editor instance|jQuery.Deferred(return instance on resolve())
  9256. // */
  9257. // load : function(textarea) { },
  9258. // /**
  9259. // * Called before "edit" dialog closed.
  9260. // * Place to destroy wysisyg instance.
  9261. // *
  9262. // * @param DOMElement textarea node
  9263. // * @param Object wysisyg instance (if was returned by "load" callback)
  9264. // * @return void
  9265. // */
  9266. // close : function(textarea, instance) { },
  9267. // /**
  9268. // * Called before file content send to backend.
  9269. // * Place to update textarea content if needed.
  9270. // *
  9271. // * @param DOMElement textarea node
  9272. // * @param Object wysisyg instance (if was returned by "load" callback)
  9273. // * @return void
  9274. // */
  9275. // save : function(textarea, instance) {},
  9276. // /**
  9277. // * Called after load() or save().
  9278. // * Set focus to wysisyg editor.
  9279. // *
  9280. // * @param DOMElement textarea node
  9281. // * @param Object wysisyg instance (if was returned by "load" callback)
  9282. // * @return void
  9283. // */
  9284. // focus : function(textarea, instance) {}
  9285. // /**
  9286. // * Called after dialog resized..
  9287. // *
  9288. // * @param DOMElement textarea node
  9289. // * @param Object wysisyg instance (if was returned by "load" callback)
  9290. // * @param Object resize event object
  9291. // * @param Object data object
  9292. // * @return void
  9293. // */
  9294. // resize : function(textarea, instance, event, data) {}
  9295. //
  9296. // }
  9297. ],
  9298. // Character encodings of select box
  9299. encodings : ['Big5', 'Big5-HKSCS', 'Cp437', 'Cp737', 'Cp775', 'Cp850', 'Cp852', 'Cp855', 'Cp857', 'Cp858',
  9300. 'Cp862', 'Cp866', 'Cp874', 'EUC-CN', 'EUC-JP', 'EUC-KR', 'GB18030', 'ISO-2022-CN', 'ISO-2022-JP', 'ISO-2022-KR',
  9301. 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4', 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7',
  9302. 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-13', 'ISO-8859-15', 'KOI8-R', 'KOI8-U', 'Shift-JIS',
  9303. 'Windows-1250', 'Windows-1251', 'Windows-1252', 'Windows-1253', 'Windows-1254', 'Windows-1257'],
  9304. // options for extra editors
  9305. extraOptions : {
  9306. // Specify the Creative Cloud API key when using Creative SDK image editor of Creative Cloud.
  9307. // You can get the API key at https://console.adobe.io/.
  9308. creativeCloudApiKey : '',
  9309. // Browsing manager URL for CKEditor, TinyMCE
  9310. // Uses self location with the empty value or not defined.
  9311. //managerUrl : 'elfinder.html'
  9312. managerUrl : null,
  9313. // CKEditor5' builds mode - 'classic', 'inline' or 'balloon'
  9314. ckeditor5Mode : 'balloon'
  9315. }
  9316. },
  9317. search : {
  9318. // Incremental search from the current view
  9319. incsearch : {
  9320. enable : true, // is enable true or false
  9321. minlen : 1, // minimum number of characters
  9322. wait : 500 // wait milliseconds
  9323. }
  9324. },
  9325. // "info" command options.
  9326. info : {
  9327. nullUrlDirLinkSelf : true,
  9328. // Maximum file size (byte) to get file contents hash (md5, sha256 ...)
  9329. showHashMaxsize : 104857600, // 100 MB
  9330. // Array of hash algorisms to show on info dialog
  9331. // These name are 'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'shake128' and 'shake256'
  9332. showHashAlgorisms : ['md5', 'sha256'],
  9333. custom : {
  9334. // /**
  9335. // * Example of custom info `desc`
  9336. // */
  9337. // desc : {
  9338. // /**
  9339. // * Lable (require)
  9340. // * It is filtered by the `fm.i18n()`
  9341. // *
  9342. // * @type String
  9343. // */
  9344. // label : 'Description',
  9345. //
  9346. // /**
  9347. // * Template (require)
  9348. // * `{id}` is replaced in dialog.id
  9349. // *
  9350. // * @type String
  9351. // */
  9352. // tpl : '<div class="elfinder-info-desc"><span class="elfinder-info-spinner"></span></div>',
  9353. //
  9354. // /**
  9355. // * Restricts to mimetypes (optional)
  9356. // * Exact match or category match
  9357. // *
  9358. // * @type Array
  9359. // */
  9360. // mimes : ['text', 'image/jpeg', 'directory'],
  9361. //
  9362. // /**
  9363. // * Restricts to file.hash (optional)
  9364. // *
  9365. // * @ type Regex
  9366. // */
  9367. // hashRegex : /^l\d+_/,
  9368. //
  9369. // /**
  9370. // * Request that asks for the description and sets the field (optional)
  9371. // *
  9372. // * @type Function
  9373. // */
  9374. // action : function(file, fm, dialog) {
  9375. // fm.request({
  9376. // data : { cmd : 'desc', target: file.hash },
  9377. // preventDefault: true,
  9378. // })
  9379. // .fail(function() {
  9380. // dialog.find('div.elfinder-info-desc').html(fm.i18n('unknown'));
  9381. // })
  9382. // .done(function(data) {
  9383. // dialog.find('div.elfinder-info-desc').html(data.desc);
  9384. // });
  9385. // }
  9386. // }
  9387. }
  9388. },
  9389. mkdir: {
  9390. // Enable automatic switching function ["New Folder" / "Into New Folder"] of toolbar buttton
  9391. intoNewFolderToolbtn: false
  9392. },
  9393. resize: {
  9394. // defalt status of snap to 8px grid of the jpeg image ("enable" or "disable")
  9395. grid8px : 'disable',
  9396. // Preset size array [width, height]
  9397. presetSize : [[320, 240], [400, 400], [640, 480], [800,600]],
  9398. // File size (bytes) threshold when using the `dim` command for obtain the image size necessary to start editing
  9399. getDimThreshold : 204800,
  9400. // File size (bytes) to request to get substitute image (400px) with the `dim` command
  9401. dimSubImgSize : 307200
  9402. },
  9403. rm: {
  9404. // If trash is valid, items moves immediately to the trash holder without confirm.
  9405. quickTrash : true,
  9406. // Maximum wait seconds when checking the number of items to into the trash
  9407. infoCheckWait : 10,
  9408. // Maximum number of items that can be placed into the Trash at one time
  9409. toTrashMaxItems : 1000
  9410. },
  9411. help : {
  9412. // Tabs to show
  9413. view : ['about', 'shortcuts', 'help', 'preference', 'debug'],
  9414. // HTML source URL of the heip tab
  9415. helpSource : '',
  9416. // Command list of action when select file
  9417. // Array value are 'Command Name' or 'Command Name1/CommandName2...'
  9418. selectActions : ['open', 'edit/download', 'resize/edit/download', 'download', 'quicklook']
  9419. }
  9420. },
  9421. /**
  9422. * Callback for prepare boot up
  9423. *
  9424. * - The this object in the function is an elFinder node
  9425. * - The first parameter is elFinder Instance
  9426. * - The second parameter is an object of other parameters
  9427. * For now it can use `dfrdsBeforeBootup` Array
  9428. *
  9429. * @type Function
  9430. * @default null
  9431. * @return void
  9432. */
  9433. bootCallback : null,
  9434. /**
  9435. * Callback for "getfile" commands.
  9436. * Required to use elFinder with WYSIWYG editors etc..
  9437. *
  9438. * @type Function
  9439. * @default null (command not active)
  9440. */
  9441. getFileCallback : null,
  9442. /**
  9443. * Default directory view. icons/list
  9444. *
  9445. * @type String
  9446. * @default "icons"
  9447. */
  9448. defaultView : 'icons',
  9449. /**
  9450. * Hash of default directory path to open
  9451. *
  9452. * NOTE: This setting will be disabled if the target folder is specified in location.hash.
  9453. *
  9454. * If you want to find the hash in Javascript
  9455. * can be obtained with the following code. (In the case of a standard hashing method)
  9456. *
  9457. * var volumeId = 'l1_'; // volume id
  9458. * var path = 'path/to/target'; // without root path
  9459. * //var path = 'path\\to\\target'; // use \ on windows server
  9460. * var hash = volumeId + btoa(path).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '.').replace(/\.+$/, '');
  9461. *
  9462. * @type String
  9463. * @default ""
  9464. */
  9465. startPathHash : '',
  9466. /**
  9467. * Emit a sound when a file is deleted
  9468. * Sounds are in sounds/ folder
  9469. *
  9470. * @type Boolean
  9471. * @default true
  9472. */
  9473. sound : true,
  9474. /**
  9475. * UI plugins to load.
  9476. * Current dir ui and dialogs loads always.
  9477. * Here set not required plugins as folders tree/toolbar/statusbar etc.
  9478. *
  9479. * @type Array
  9480. * @default ['toolbar', 'tree', 'path', 'stat']
  9481. * @full ['toolbar', 'places', 'tree', 'path', 'stat']
  9482. */
  9483. ui : ['toolbar', 'tree', 'path', 'stat'],
  9484. /**
  9485. * Some UI plugins options.
  9486. * @type Object
  9487. */
  9488. uiOptions : {
  9489. // toolbar configuration
  9490. toolbar : [
  9491. ['home', 'back', 'forward', 'up', 'reload'],
  9492. ['netmount'],
  9493. ['mkdir', 'mkfile', 'upload'],
  9494. ['open', 'download', 'getfile'],
  9495. ['undo', 'redo'],
  9496. ['copy', 'cut', 'paste', 'rm', 'empty'],
  9497. ['duplicate', 'rename', 'edit', 'resize', 'chmod'],
  9498. ['selectall', 'selectnone', 'selectinvert'],
  9499. ['quicklook', 'info'],
  9500. ['extract', 'archive'],
  9501. ['search'],
  9502. ['view', 'sort'],
  9503. ['help'],
  9504. ['fullscreen']
  9505. ],
  9506. // toolbar extra options
  9507. toolbarExtra : {
  9508. // also displays the text label on the button (true / false)
  9509. displayTextLabel: false,
  9510. // Exclude `displayTextLabel` setting UA type
  9511. labelExcludeUA: ['Mobile'],
  9512. // auto hide on initial open
  9513. autoHideUA: ['Mobile'],
  9514. // Initial setting value of hide button in toolbar setting
  9515. defaultHides: ['home', 'reload'],
  9516. // show Preference button ('none', 'auto', 'always')
  9517. // If you do not include 'preference' in the context menu you should specify 'auto' or 'always'
  9518. showPreferenceButton: 'none'
  9519. },
  9520. // directories tree options
  9521. tree : {
  9522. // expand current root on init
  9523. openRootOnLoad : true,
  9524. // expand current work directory on open
  9525. openCwdOnOpen : true,
  9526. // auto loading current directory parents and do expand their node.
  9527. syncTree : true,
  9528. // Maximum number of display of each child trees
  9529. // The tree of directories with children exceeding this number will be split
  9530. subTreeMax : 100,
  9531. // Numbar of max connctions of subdirs request
  9532. subdirsMaxConn : 2,
  9533. // Number of max simultaneous processing directory of subdirs
  9534. subdirsAtOnce : 5
  9535. // ,
  9536. // /**
  9537. // * Add CSS class name to navbar directories (optional)
  9538. // * see: https://github.com/Studio-42/elFinder/pull/1061,
  9539. // * https://github.com/Studio-42/elFinder/issues/1231
  9540. // *
  9541. // * @type Function
  9542. // */
  9543. // getClass: function(dir) {
  9544. // // e.g. This adds the directory's name (lowercase) with prefix as a CSS class
  9545. // return 'elfinder-tree-' + dir.name.replace(/[ "]/g, '').toLowerCase();
  9546. // }
  9547. },
  9548. // navbar options
  9549. navbar : {
  9550. minWidth : 150,
  9551. maxWidth : 500,
  9552. // auto hide on initial open
  9553. autoHideUA: [] // e.g. ['Mobile']
  9554. },
  9555. navdock : {
  9556. // disabled navdock ui
  9557. disabled : false,
  9558. // percentage of initial maximum height to work zone
  9559. initMaxHeight : '50%',
  9560. // percentage of maximum height to work zone by user resize action
  9561. maxHeight : '90%'
  9562. },
  9563. cwd : {
  9564. // display parent folder with ".." name :)
  9565. oldSchool : false,
  9566. // fm.UA types array to show item select checkboxes e.g. ['All'] or ['Mobile'] etc. default: ['Touch']
  9567. showSelectCheckboxUA : ['Touch'],
  9568. // file info columns displayed
  9569. listView : {
  9570. // name is always displayed, cols are ordered
  9571. // e.g. ['perm', 'date', 'size', 'kind', 'owner', 'group', 'mode']
  9572. // mode: 'mode'(by `fileModeStyle` setting), 'modestr'(rwxr-xr-x) , 'modeoct'(755), 'modeboth'(rwxr-xr-x (755))
  9573. // 'owner', 'group' and 'mode', It's necessary set volume driver option "statOwner" to `true`
  9574. // for custom, characters that can be used in the name is `a-z0-9_`
  9575. columns : ['perm', 'date', 'size', 'kind'],
  9576. // override this if you want custom columns name
  9577. // example
  9578. // columnsCustomName : {
  9579. // date : 'Last modification',
  9580. // kind : 'Mime type'
  9581. // }
  9582. columnsCustomName : {},
  9583. // fixed list header colmun
  9584. fixedHeader : true
  9585. }
  9586. // /**
  9587. // * Add CSS class name to cwd directories (optional)
  9588. // * see: https://github.com/Studio-42/elFinder/pull/1061,
  9589. // * https://github.com/Studio-42/elFinder/issues/1231
  9590. // *
  9591. // * @type Function
  9592. // */
  9593. // ,
  9594. // getClass: function(file) {
  9595. // // e.g. This adds the directory's name (lowercase) with prefix as a CSS class
  9596. // return 'elfinder-cwd-' + file.name.replace(/[ "]/g, '').toLowerCase();
  9597. //}
  9598. //,
  9599. //// Template placeholders replacement rules for overwrite. see ui/cwd.js replacement
  9600. //replacement : {
  9601. // tooltip : function(f, fm) {
  9602. // var list = fm.viewType == 'list', // current view type
  9603. // query = fm.searchStatus.state == 2, // is in search results
  9604. // title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''),
  9605. // info = '';
  9606. // if (query && f.path) {
  9607. // info = fm.escape(f.path.replace(/\/[^\/]*$/, ''));
  9608. // } else {
  9609. // info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, '&#13;') : '';
  9610. // }
  9611. // if (list) {
  9612. // info += (info? '&#13;' : '') + fm.escape(f.name);
  9613. // }
  9614. // return info? info + '&#13;' + title : title;
  9615. // }
  9616. //}
  9617. },
  9618. path : {
  9619. // Move to head of work zone without UI navbar
  9620. toWorkzoneWithoutNavbar : true
  9621. },
  9622. dialog : {
  9623. // Enable to auto focusing on mouse over in the target form element
  9624. focusOnMouseOver : true
  9625. }
  9626. },
  9627. /**
  9628. * MIME regex of send HTTP header "Content-Disposition: inline" or allow preview in quicklook
  9629. * This option will overwrite by connector configuration
  9630. *
  9631. * @type String
  9632. * @default '^(?:(?:image|video|audio)|text/plain|application/pdf$)'
  9633. * @example
  9634. * dispInlineRegex : '.', // is allow inline of all of MIME types
  9635. * dispInlineRegex : '$^', // is not allow inline of all of MIME types
  9636. */
  9637. dispInlineRegex : '^(?:(?:image|video|audio)|application/(?:x-mpegURL|dash\+xml)|(?:text/plain|application/pdf)$)',
  9638. /**
  9639. * Display only required files by types
  9640. *
  9641. * @type Array
  9642. * @default []
  9643. * @example
  9644. * onlyMimes : ["image"] - display all images
  9645. * onlyMimes : ["image/png", "application/x-shockwave-flash"] - display png and flash
  9646. */
  9647. onlyMimes : [],
  9648. /**
  9649. * Custom files sort rules.
  9650. * All default rules (name/size/kind/date/perm/mode/owner/group) set in elFinder._sortRules
  9651. *
  9652. * @type {Object}
  9653. * @example
  9654. * sortRules : {
  9655. * name : function(file1, file2) { return file1.name.toLowerCase().localeCompare(file2.name.toLowerCase()); }
  9656. * }
  9657. */
  9658. sortRules : {},
  9659. /**
  9660. * Default sort type.
  9661. *
  9662. * @type {String}
  9663. */
  9664. sortType : 'name',
  9665. /**
  9666. * Default sort order.
  9667. *
  9668. * @type {String}
  9669. * @default "asc"
  9670. */
  9671. sortOrder : 'asc',
  9672. /**
  9673. * Display folders first?
  9674. *
  9675. * @type {Boolean}
  9676. * @default true
  9677. */
  9678. sortStickFolders : true,
  9679. /**
  9680. * Sort also applies to the treeview
  9681. *
  9682. * @type {Boolean}
  9683. * @default false
  9684. */
  9685. sortAlsoTreeview : false,
  9686. /**
  9687. * If true - elFinder will formating dates itself,
  9688. * otherwise - backend date will be used.
  9689. *
  9690. * @type Boolean
  9691. */
  9692. clientFormatDate : true,
  9693. /**
  9694. * Show UTC dates.
  9695. * Required set clientFormatDate to true
  9696. *
  9697. * @type Boolean
  9698. */
  9699. UTCDate : false,
  9700. /**
  9701. * File modification datetime format.
  9702. * Value from selected language data is used by default.
  9703. * Set format here to overwrite it.
  9704. *
  9705. * @type String
  9706. * @default ""
  9707. */
  9708. dateFormat : '',
  9709. /**
  9710. * File modification datetime format in form "Yesterday 12:23:01".
  9711. * Value from selected language data is used by default.
  9712. * Set format here to overwrite it.
  9713. * Use $1 for "Today"/"Yesterday" placeholder
  9714. *
  9715. * @type String
  9716. * @default ""
  9717. * @example "$1 H:m:i"
  9718. */
  9719. fancyDateFormat : '',
  9720. /**
  9721. * Style of file mode at cwd-list, info dialog
  9722. * 'string' (ex. rwxr-xr-x) or 'octal' (ex. 755) or 'both' (ex. rwxr-xr-x (755))
  9723. *
  9724. * @type {String}
  9725. * @default 'both'
  9726. */
  9727. fileModeStyle : 'both',
  9728. /**
  9729. * elFinder width
  9730. *
  9731. * @type String|Number
  9732. * @default "auto"
  9733. */
  9734. width : 'auto',
  9735. /**
  9736. * elFinder node height
  9737. * Number: pixcel or String: Number + "%"
  9738. *
  9739. * @type Number | String
  9740. * @default 400
  9741. */
  9742. height : 400,
  9743. /**
  9744. * Base node object or selector
  9745. * Element which is the reference of the height percentage
  9746. *
  9747. * @type Object|String
  9748. * @default null | $(window) (if height is percentage)
  9749. **/
  9750. heightBase : null,
  9751. /**
  9752. * Make elFinder resizable if jquery ui resizable available
  9753. *
  9754. * @type Boolean
  9755. * @default true
  9756. */
  9757. resizable : true,
  9758. /**
  9759. * Timeout before open notifications dialogs
  9760. *
  9761. * @type Number
  9762. * @default 500 (.5 sec)
  9763. */
  9764. notifyDelay : 500,
  9765. /**
  9766. * Position CSS, Width of notifications dialogs
  9767. *
  9768. * @type Object
  9769. * @default {position: {top : '12px', right : '12px'}, width : 280}
  9770. * position: CSS object | null (null: position center & middle)
  9771. */
  9772. notifyDialog : {position: {top : '12px', right : '12px'}, width : 280},
  9773. /**
  9774. * Dialog contained in the elFinder node
  9775. *
  9776. * @type Boolean
  9777. * @default false
  9778. */
  9779. dialogContained : false,
  9780. /**
  9781. * Allow shortcuts
  9782. *
  9783. * @type Boolean
  9784. * @default true
  9785. */
  9786. allowShortcuts : true,
  9787. /**
  9788. * Remeber last opened dir to open it after reload or in next session
  9789. *
  9790. * @type Boolean
  9791. * @default true
  9792. */
  9793. rememberLastDir : true,
  9794. /**
  9795. * Clear historys(elFinder) on reload(not browser) function
  9796. * Historys was cleared on Reload function on elFinder 2.0 (value is true)
  9797. *
  9798. * @type Boolean
  9799. * @default false
  9800. */
  9801. reloadClearHistory : false,
  9802. /**
  9803. * Use browser native history with supported browsers
  9804. *
  9805. * @type Boolean
  9806. * @default true
  9807. */
  9808. useBrowserHistory : true,
  9809. /**
  9810. * Lazy load config.
  9811. * How many files display at once?
  9812. *
  9813. * @type Number
  9814. * @default 50
  9815. */
  9816. showFiles : 50,
  9817. /**
  9818. * Lazy load config.
  9819. * Distance in px to cwd bottom edge to start display files
  9820. *
  9821. * @type Number
  9822. * @default 50
  9823. */
  9824. showThreshold : 50,
  9825. /**
  9826. * Additional rule to valid new file name.
  9827. * By default not allowed empty names or '..'
  9828. * This setting does not have a sense of security.
  9829. *
  9830. * @type false|RegExp|function
  9831. * @default false
  9832. * @example
  9833. * disable names with spaces:
  9834. * validName : /^[^\s]+$/,
  9835. */
  9836. validName : false,
  9837. /**
  9838. * Additional rule to filtering for browsing.
  9839. * This setting does not have a sense of security.
  9840. *
  9841. * The object `this` is elFinder instance object in this function
  9842. *
  9843. * @type false|RegExp|function
  9844. * @default false
  9845. * @example
  9846. * show only png and jpg files:
  9847. * fileFilter : /.*\.(png|jpg)$/i,
  9848. *
  9849. * show only image type files:
  9850. * fileFilter : function(file) { return file.mime && file.mime.match(/^image\//i); },
  9851. */
  9852. fileFilter : false,
  9853. /**
  9854. * Backup name suffix.
  9855. *
  9856. * @type String
  9857. * @default "~"
  9858. */
  9859. backupSuffix : '~',
  9860. /**
  9861. * Sync content interval
  9862. *
  9863. * @type Number
  9864. * @default 0 (do not sync)
  9865. */
  9866. sync : 0,
  9867. /**
  9868. * Sync start on load if sync value >= 1000
  9869. *
  9870. * @type Bool
  9871. * @default true
  9872. */
  9873. syncStart : true,
  9874. /**
  9875. * How many thumbnails create in one request
  9876. *
  9877. * @type Number
  9878. * @default 5
  9879. */
  9880. loadTmbs : 5,
  9881. /**
  9882. * Cookie option for browsersdoes not suppot localStorage
  9883. *
  9884. * @type Object
  9885. */
  9886. cookie : {
  9887. expires : 30,
  9888. domain : '',
  9889. path : '/',
  9890. secure : false
  9891. },
  9892. /**
  9893. * Contextmenu config
  9894. *
  9895. * @type Object
  9896. */
  9897. contextmenu : {
  9898. // navbarfolder menu
  9899. navbar : ['open', 'download', '|', 'upload', 'mkdir', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', 'empty', '|', 'rename', '|', 'archive', '|', 'places', 'info', 'chmod', 'netunmount'],
  9900. // current directory menu
  9901. cwd : ['undo', 'redo', '|', 'back', 'up', 'reload', '|', 'upload', 'mkdir', 'mkfile', 'paste', '|', 'empty', '|', 'view', 'sort', 'selectall', 'colwidth', '|', 'places', 'info', 'chmod', 'netunmount', '|', 'fullscreen', '|', 'preference'],
  9902. // current directory file menu
  9903. files : ['getfile', '|' ,'open', 'download', 'opendir', 'quicklook', '|', 'upload', 'mkdir', '|', 'copy', 'cut', 'paste', 'duplicate', '|', 'rm', 'empty', '|', 'rename', 'edit', 'resize', '|', 'archive', 'extract', '|', 'selectall', 'selectinvert', '|', 'places', 'info', 'chmod', 'netunmount']
  9904. },
  9905. /**
  9906. * elFinder node enable always
  9907. * This value will set to `true` if <body> has elFinder node only
  9908. *
  9909. * @type Bool
  9910. * @default false
  9911. */
  9912. enableAlways : false,
  9913. /**
  9914. * elFinder node enable by mouse over
  9915. *
  9916. * @type Bool
  9917. * @default true
  9918. */
  9919. enableByMouseOver : true,
  9920. /**
  9921. * Show window close confirm dialog
  9922. * Value is which state to show
  9923. * 'hasNotifyDialog', 'editingFile', 'hasSelectedItem' and 'hasClipboardData'
  9924. *
  9925. * @type Array
  9926. * @default ['hasNotifyDialog', 'editingFile']
  9927. */
  9928. windowCloseConfirm : ['hasNotifyDialog', 'editingFile'],
  9929. /**
  9930. * Function decoding 'raw' string converted to unicode
  9931. * It is used instead of fm.decodeRawString(str)
  9932. *
  9933. * @type Null|Function
  9934. */
  9935. rawStringDecoder : typeof Encoding === 'object' && $.isFunction(Encoding.convert)? function(str) {
  9936. return Encoding.convert(str, {
  9937. to: 'UNICODE',
  9938. type: 'string'
  9939. });
  9940. } : null,
  9941. /**
  9942. * Debug config
  9943. *
  9944. * @type Array|String('auto')|Boolean(true|false)
  9945. */
  9946. // debug : true
  9947. debug : ['error', 'warning', 'event-destroy']
  9948. };
  9949. /*
  9950. * File: /js/elFinder.options.netmount.js
  9951. */
  9952. /**
  9953. * Default elFinder config of commandsOptions.netmount
  9954. *
  9955. * @type Object
  9956. */
  9957. elFinder.prototype._options.commandsOptions.netmount = {
  9958. ftp: {
  9959. name : 'FTP',
  9960. inputs: {
  9961. host : $('<input type="text"/>'),
  9962. port : $('<input type="number" placeholder="21" class="elfinder-input-optional"/>'),
  9963. path : $('<input type="text" value="/"/>'),
  9964. user : $('<input type="text"/>'),
  9965. pass : $('<input type="password" autocomplete="new-password"/>'),
  9966. FTPS : $('<input type="checkbox" value="1" title="File Transfer Protocol over SSL/TLS"/>'),
  9967. encoding : $('<input type="text" placeholder="Optional" class="elfinder-input-optional"/>'),
  9968. locale : $('<input type="text" placeholder="Optional" class="elfinder-input-optional"/>')
  9969. }
  9970. },
  9971. dropbox2: elFinder.prototype.makeNetmountOptionOauth('dropbox2', 'Dropbox', 'Dropbox', {noOffline : true, root : '/', pathI18n : 'path'}),
  9972. googledrive: elFinder.prototype.makeNetmountOptionOauth('googledrive', 'Google Drive', 'Google'),
  9973. onedrive: elFinder.prototype.makeNetmountOptionOauth('onedrive', 'One Drive', 'OneDrive'),
  9974. box: elFinder.prototype.makeNetmountOptionOauth('box', 'Box', 'Box', {noOffline : true})
  9975. };
  9976. /*
  9977. * File: /js/elFinder.history.js
  9978. */
  9979. /**
  9980. * @class elFinder.history
  9981. * Store visited folders
  9982. * and provide "back" and "forward" methods
  9983. *
  9984. * @author Dmitry (dio) Levashov
  9985. */
  9986. elFinder.prototype.history = function(fm) {
  9987. var self = this,
  9988. /**
  9989. * Update history on "open" event?
  9990. *
  9991. * @type Boolean
  9992. */
  9993. update = true,
  9994. /**
  9995. * Directories hashes storage
  9996. *
  9997. * @type Array
  9998. */
  9999. history = [],
  10000. /**
  10001. * Current directory index in history
  10002. *
  10003. * @type Number
  10004. */
  10005. current,
  10006. /**
  10007. * Clear history
  10008. *
  10009. * @return void
  10010. */
  10011. reset = function() {
  10012. history = [fm.cwd().hash];
  10013. current = 0;
  10014. update = true;
  10015. },
  10016. /**
  10017. * Browser native history object
  10018. */
  10019. nativeHistory = (fm.options.useBrowserHistory && window.history && window.history.pushState)? window.history : null,
  10020. /**
  10021. * Open prev/next folder
  10022. *
  10023. * @Boolen open next folder?
  10024. * @return jQuery.Deferred
  10025. */
  10026. go = function(fwd) {
  10027. if ((fwd && self.canForward()) || (!fwd && self.canBack())) {
  10028. update = false;
  10029. return fm.exec('open', history[fwd ? ++current : --current]).fail(reset);
  10030. }
  10031. return $.Deferred().reject();
  10032. };
  10033. /**
  10034. * Return true if there is previous visited directories
  10035. *
  10036. * @return Boolen
  10037. */
  10038. this.canBack = function() {
  10039. return current > 0;
  10040. };
  10041. /**
  10042. * Return true if can go forward
  10043. *
  10044. * @return Boolen
  10045. */
  10046. this.canForward = function() {
  10047. return current < history.length - 1;
  10048. };
  10049. /**
  10050. * Go back
  10051. *
  10052. * @return void
  10053. */
  10054. this.back = go;
  10055. /**
  10056. * Go forward
  10057. *
  10058. * @return void
  10059. */
  10060. this.forward = function() {
  10061. return go(true);
  10062. };
  10063. // bind to elfinder events
  10064. fm.open(function() {
  10065. var l = history.length,
  10066. cwd = fm.cwd().hash;
  10067. if (update) {
  10068. current >= 0 && l > current + 1 && history.splice(current+1);
  10069. history[history.length-1] != cwd && history.push(cwd);
  10070. current = history.length - 1;
  10071. }
  10072. update = true;
  10073. if (nativeHistory) {
  10074. if (! nativeHistory.state) {
  10075. nativeHistory.replaceState({thash: cwd}, null, location.pathname + location.search + '#elf_' + cwd);
  10076. } else {
  10077. nativeHistory.state.thash != cwd && nativeHistory.pushState({thash: cwd}, null, location.pathname + location.search + '#elf_' + cwd);
  10078. }
  10079. }
  10080. })
  10081. .reload(fm.options.reloadClearHistory && reset);
  10082. };
  10083. /*
  10084. * File: /js/elFinder.command.js
  10085. */
  10086. /**
  10087. * elFinder command prototype
  10088. *
  10089. * @type elFinder.command
  10090. * @author Dmitry (dio) Levashov
  10091. */
  10092. elFinder.prototype.command = function(fm) {
  10093. /**
  10094. * elFinder instance
  10095. *
  10096. * @type elFinder
  10097. */
  10098. this.fm = fm;
  10099. /**
  10100. * Command name, same as class name
  10101. *
  10102. * @type String
  10103. */
  10104. this.name = '';
  10105. /**
  10106. * Command icon class name with out 'elfinder-button-icon-'
  10107. * Use this.name if it is empty
  10108. *
  10109. * @type String
  10110. */
  10111. this.className = '';
  10112. /**
  10113. * Short command description
  10114. *
  10115. * @type String
  10116. */
  10117. this.title = '';
  10118. /**
  10119. * Linked(Child) commands name
  10120. * They are loaded together when tthis command is loaded.
  10121. *
  10122. * @type Array
  10123. */
  10124. this.linkedCmds = [];
  10125. /**
  10126. * Current command state
  10127. *
  10128. * @example
  10129. * this.state = -1; // command disabled
  10130. * this.state = 0; // command enabled
  10131. * this.state = 1; // command active (for example "fullscreen" command while elfinder in fullscreen mode)
  10132. * @default -1
  10133. * @type Number
  10134. */
  10135. this.state = -1;
  10136. /**
  10137. * If true, command can not be disabled by connector.
  10138. * @see this.update()
  10139. *
  10140. * @type Boolen
  10141. */
  10142. this.alwaysEnabled = false;
  10143. /**
  10144. * Do not change dirctory on removed current work directory
  10145. *
  10146. * @type Boolen
  10147. */
  10148. this.noChangeDirOnRemovedCwd = false;
  10149. /**
  10150. * If true, this means command was disabled by connector.
  10151. * @see this.update()
  10152. *
  10153. * @type Boolen
  10154. */
  10155. this._disabled = false;
  10156. this.disableOnSearch = false;
  10157. this.updateOnSelect = true;
  10158. this.syncTitleOnChange = false;
  10159. /**
  10160. * elFinder events defaults handlers.
  10161. * Inside handlers "this" is current command object
  10162. *
  10163. * @type Object
  10164. */
  10165. this._handlers = {
  10166. enable : function() { this.update(void(0), this.value); },
  10167. disable : function() { this.update(-1, this.value); },
  10168. 'open reload load sync' : function() {
  10169. this._disabled = !(this.alwaysEnabled || this.fm.isCommandEnabled(this.name));
  10170. this.update(void(0), this.value);
  10171. this.change();
  10172. }
  10173. };
  10174. /**
  10175. * elFinder events handlers.
  10176. * Inside handlers "this" is current command object
  10177. *
  10178. * @type Object
  10179. */
  10180. this.handlers = {};
  10181. /**
  10182. * Shortcuts
  10183. *
  10184. * @type Array
  10185. */
  10186. this.shortcuts = [];
  10187. /**
  10188. * Command options
  10189. *
  10190. * @type Object
  10191. */
  10192. this.options = {ui : 'button'};
  10193. /**
  10194. * Prepare object -
  10195. * bind events and shortcuts
  10196. *
  10197. * @return void
  10198. */
  10199. this.setup = function(name, opts) {
  10200. var self = this,
  10201. fm = this.fm,
  10202. setCallback = function(s) {
  10203. var cb = s.callback || function(){ fm.exec(self.name, void(0), {_userAction: true}); };
  10204. s.callback = function(e) {
  10205. var enabled, checks = {};
  10206. if (self.enabled()) {
  10207. if (fm.searchStatus.state < 2) {
  10208. enabled = fm.isCommandEnabled(self.name);
  10209. } else {
  10210. $.each(fm.selected(), function(i, h) {
  10211. if (fm.optionsByHashes[h]) {
  10212. checks[h] = true;
  10213. } else {
  10214. $.each(fm.volOptions, function(id) {
  10215. if (!checks[id] && h.indexOf(id) === 0) {
  10216. checks[id] = true;
  10217. return false;
  10218. }
  10219. });
  10220. }
  10221. });
  10222. $.each(checks, function(h) {
  10223. enabled = fm.isCommandEnabled(self.name, h);
  10224. if (! enabled) {
  10225. return false;
  10226. }
  10227. });
  10228. }
  10229. if (enabled) {
  10230. self.event = e;
  10231. cb.call(self);
  10232. delete self.event;
  10233. }
  10234. }
  10235. };
  10236. },
  10237. i, s, sc;
  10238. this.name = name;
  10239. this.title = fm.messages['cmd'+name] ? fm.i18n('cmd'+name)
  10240. : ((this.extendsCmd && fm.messages['cmd'+this.extendsCmd]) ? fm.i18n('cmd'+this.extendsCmd) : name);
  10241. this.options = Object.assign({}, this.options, opts);
  10242. this.listeners = [];
  10243. if (opts.shortcuts) {
  10244. if (typeof opts.shortcuts === 'function') {
  10245. sc = opts.shortcuts(this.fm, this.shortcuts);
  10246. } else if (Array.isArray(opts.shortcuts)) {
  10247. sc = opts.shortcuts;
  10248. }
  10249. this.shortcuts = sc || [];
  10250. }
  10251. if (this.updateOnSelect) {
  10252. this._handlers.select = function() { this.update(void(0), this.value); };
  10253. }
  10254. $.each(Object.assign({}, self._handlers, self.handlers), function(cmd, handler) {
  10255. fm.bind(cmd, $.proxy(handler, self));
  10256. });
  10257. for (i = 0; i < this.shortcuts.length; i++) {
  10258. s = this.shortcuts[i];
  10259. setCallback(s);
  10260. !s.description && (s.description = this.title);
  10261. fm.shortcut(s);
  10262. }
  10263. if (this.disableOnSearch) {
  10264. fm.bind('search searchend', function() {
  10265. self._disabled = this.type === 'search'? true : ! (this.alwaysEnabled || fm.isCommandEnabled(name));
  10266. self.update(void(0), self.value);
  10267. });
  10268. }
  10269. this.init();
  10270. };
  10271. /**
  10272. * Command specific init stuffs
  10273. *
  10274. * @return void
  10275. */
  10276. this.init = function() {};
  10277. /**
  10278. * Exec command
  10279. *
  10280. * @param Array target files hashes
  10281. * @param Array|Object command value
  10282. * @return $.Deferred
  10283. */
  10284. this.exec = function(files, opts) {
  10285. return $.Deferred().reject();
  10286. };
  10287. this.getUndo = function(opts, resData) {
  10288. return false;
  10289. };
  10290. /**
  10291. * Return true if command disabled.
  10292. *
  10293. * @return Boolen
  10294. */
  10295. this.disabled = function() {
  10296. return this.state < 0;
  10297. };
  10298. /**
  10299. * Return true if command enabled.
  10300. *
  10301. * @return Boolen
  10302. */
  10303. this.enabled = function() {
  10304. return this.state > -1;
  10305. };
  10306. /**
  10307. * Return true if command active.
  10308. *
  10309. * @return Boolen
  10310. */
  10311. this.active = function() {
  10312. return this.state > 0;
  10313. };
  10314. /**
  10315. * Return current command state.
  10316. * Must be overloaded in most commands
  10317. *
  10318. * @return Number
  10319. */
  10320. this.getstate = function() {
  10321. return -1;
  10322. };
  10323. /**
  10324. * Update command state/value
  10325. * and rize 'change' event if smth changed
  10326. *
  10327. * @param Number new state or undefined to auto update state
  10328. * @param mixed new value
  10329. * @return void
  10330. */
  10331. this.update = function(s, v) {
  10332. var state = this.state,
  10333. value = this.value;
  10334. if (this._disabled && this.fm.searchStatus === 0) {
  10335. this.state = -1;
  10336. } else {
  10337. this.state = s !== void(0) ? s : this.getstate();
  10338. }
  10339. this.value = v;
  10340. if (state != this.state || value != this.value) {
  10341. this.change();
  10342. }
  10343. };
  10344. /**
  10345. * Bind handler / fire 'change' event.
  10346. *
  10347. * @param Function|undefined event callback
  10348. * @return void
  10349. */
  10350. this.change = function(c) {
  10351. var cmd, i;
  10352. if (typeof(c) === 'function') {
  10353. this.listeners.push(c);
  10354. } else {
  10355. for (i = 0; i < this.listeners.length; i++) {
  10356. cmd = this.listeners[i];
  10357. try {
  10358. cmd(this.state, this.value);
  10359. } catch (e) {
  10360. this.fm.debug('error', e);
  10361. }
  10362. }
  10363. }
  10364. return this;
  10365. };
  10366. /**
  10367. * With argument check given files hashes and return list of existed files hashes.
  10368. * Without argument return selected files hashes.
  10369. *
  10370. * @param Array|String|void hashes
  10371. * @return Array
  10372. */
  10373. this.hashes = function(hashes) {
  10374. return hashes
  10375. ? $.grep(Array.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) ? true : false; })
  10376. : fm.selected();
  10377. };
  10378. /**
  10379. * Return only existed files from given fils hashes | selected files
  10380. *
  10381. * @param Array|String|void hashes
  10382. * @return Array
  10383. */
  10384. this.files = function(hashes) {
  10385. var fm = this.fm;
  10386. return hashes
  10387. ? $.map(Array.isArray(hashes) ? hashes : [hashes], function(hash) { return fm.file(hash) || null; })
  10388. : fm.selectedFiles();
  10389. };
  10390. };
  10391. /*
  10392. * File: /js/elFinder.resources.js
  10393. */
  10394. /**
  10395. * elFinder resources registry.
  10396. * Store shared data
  10397. *
  10398. * @type Object
  10399. * @author Dmitry (dio) Levashov
  10400. **/
  10401. elFinder.prototype.resources = {
  10402. 'class' : {
  10403. hover : 'ui-state-hover',
  10404. active : 'ui-state-active',
  10405. disabled : 'ui-state-disabled',
  10406. draggable : 'ui-draggable',
  10407. droppable : 'ui-droppable',
  10408. adroppable : 'elfinder-droppable-active',
  10409. cwdfile : 'elfinder-cwd-file',
  10410. cwd : 'elfinder-cwd',
  10411. tree : 'elfinder-tree',
  10412. treeroot : 'elfinder-navbar-root',
  10413. navdir : 'elfinder-navbar-dir',
  10414. navdirwrap : 'elfinder-navbar-dir-wrapper',
  10415. navarrow : 'elfinder-navbar-arrow',
  10416. navsubtree : 'elfinder-navbar-subtree',
  10417. navcollapse : 'elfinder-navbar-collapsed',
  10418. navexpand : 'elfinder-navbar-expanded',
  10419. treedir : 'elfinder-tree-dir',
  10420. placedir : 'elfinder-place-dir',
  10421. searchbtn : 'elfinder-button-search',
  10422. editing : 'elfinder-to-editing'
  10423. },
  10424. tpl : {
  10425. perms : '<span class="elfinder-perms"/>',
  10426. lock : '<span class="elfinder-lock"/>',
  10427. symlink : '<span class="elfinder-symlink"/>',
  10428. navicon : '<span class="elfinder-nav-icon"/>',
  10429. navspinner : '<span class="elfinder-navbar-spinner"/>',
  10430. 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>',
  10431. 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>'
  10432. },
  10433. // mimes.text will be overwritten with connector config if `textMimes` is included in initial response
  10434. // @see php/elFInder.class.php `public static $textMimes`
  10435. mimes : {
  10436. text : [
  10437. 'application/x-empty',
  10438. 'application/javascript',
  10439. 'application/json',
  10440. 'application/xhtml+xml',
  10441. 'audio/x-mp3-playlist',
  10442. 'application/x-web-config',
  10443. 'application/docbook+xml',
  10444. 'application/x-php',
  10445. 'application/x-perl',
  10446. 'application/x-awk',
  10447. 'application/x-config',
  10448. 'application/x-csh',
  10449. 'application/xml',
  10450. 'application/sql'
  10451. ]
  10452. },
  10453. mixin : {
  10454. make : function() {
  10455. var self = this,
  10456. fm = this.fm,
  10457. cmd = this.name,
  10458. req = this.requestCmd || cmd,
  10459. wz = fm.getUI('workzone'),
  10460. org = (this.origin && this.origin === 'navbar')? 'tree' : 'cwd',
  10461. ui = fm.getUI(org),
  10462. tree = (org === 'tree'),
  10463. find = tree? 'navHash2Id' : 'cwdHash2Id',
  10464. tarea= (! tree && fm.storage('view') != 'list'),
  10465. sel = fm.selected(),
  10466. move = this.move || false,
  10467. empty= wz.hasClass('elfinder-cwd-wrapper-empty'),
  10468. rest = function(){
  10469. if (!overlay.is(':hidden')) {
  10470. overlay.elfinderoverlay('hide').off('click', cancel);
  10471. }
  10472. node.removeClass('ui-front').css('position', '');
  10473. if (tarea) {
  10474. nnode && nnode.css('max-height', '');
  10475. } else if (pnode) {
  10476. pnode.css('width', '')
  10477. .parent('td').css('overflow', '');
  10478. }
  10479. }, colwidth,
  10480. dfrd = $.Deferred()
  10481. .fail(function(error) {
  10482. dstCls && dst.attr('class', dstCls);
  10483. empty && wz.addClass('elfinder-cwd-wrapper-empty');
  10484. if (sel) {
  10485. move && fm.trigger('unlockfiles', {files: sel});
  10486. fm.clipboard([]);
  10487. fm.trigger('selectfiles', { files: sel });
  10488. }
  10489. error && fm.error(error);
  10490. })
  10491. .always(function() {
  10492. rest();
  10493. cleanup();
  10494. fm.enable().unbind('open', openCallback).trigger('resMixinMake');
  10495. }),
  10496. id = 'tmp_'+parseInt(Math.random()*100000),
  10497. phash = this.data && this.data.target? this.data.target : (tree? fm.file(sel[0]).hash : fm.cwd().hash),
  10498. date = new Date(),
  10499. file = {
  10500. hash : id,
  10501. phash : phash,
  10502. name : fm.uniqueName(this.prefix, phash),
  10503. mime : this.mime,
  10504. read : true,
  10505. write : true,
  10506. date : 'Today '+date.getHours()+':'+date.getMinutes(),
  10507. move : move
  10508. },
  10509. data = this.data || {},
  10510. node = ui.trigger('create.'+fm.namespace, file).find('#'+fm[find](id))
  10511. .on('unselect.'+fm.namespace, function() {
  10512. setTimeout(function() {
  10513. input && input.trigger('blur');
  10514. }, 50);
  10515. }),
  10516. nnode, pnode,
  10517. overlay = fm.getUI('overlay'),
  10518. cleanup = function() {
  10519. if (node && node.length) {
  10520. input.off();
  10521. node.hide();
  10522. fm.unselectfiles({files : [id]}).unbind('resize', resize);
  10523. setTimeout(function() {
  10524. if (tree) {
  10525. node.closest('.elfinder-navbar-wrapper').remove();
  10526. } else {
  10527. node.remove();
  10528. }
  10529. }, 0);
  10530. }
  10531. },
  10532. cancel = function(e) {
  10533. if (!overlay.is(':hidden')) {
  10534. pnode.css('z-index', '');
  10535. }
  10536. if (! inError) {
  10537. cleanup();
  10538. dfrd.reject();
  10539. if (e) {
  10540. e.stopPropagation();
  10541. e.preventDefault();
  10542. }
  10543. }
  10544. },
  10545. input = $(tarea? '<textarea/>' : '<input type="text"/>')
  10546. .on('keyup text', function(){
  10547. if (tarea) {
  10548. this.style.height = '1px';
  10549. this.style.height = this.scrollHeight + 'px';
  10550. } else if (colwidth) {
  10551. this.style.width = colwidth + 'px';
  10552. if (this.scrollWidth > colwidth) {
  10553. this.style.width = this.scrollWidth + 10 + 'px';
  10554. }
  10555. }
  10556. })
  10557. .on('keydown', function(e) {
  10558. e.stopImmediatePropagation();
  10559. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  10560. dfrd.reject();
  10561. } else if (e.keyCode == $.ui.keyCode.ENTER) {
  10562. input.trigger('blur');
  10563. }
  10564. })
  10565. .on('mousedown click dblclick', function(e) {
  10566. e.stopPropagation();
  10567. if (e.type === 'dblclick') {
  10568. e.preventDefault();
  10569. }
  10570. })
  10571. .on('blur', function() {
  10572. var name = $.trim(input.val()),
  10573. parent = input.parent(),
  10574. valid = true,
  10575. cut;
  10576. if (!overlay.is(':hidden')) {
  10577. pnode.css('z-index', '');
  10578. }
  10579. if (name === '') {
  10580. return cancel();
  10581. }
  10582. if (!inError && parent.length) {
  10583. if (fm.options.validName && fm.options.validName.test) {
  10584. try {
  10585. valid = fm.options.validName.test(name);
  10586. } catch(e) {
  10587. valid = false;
  10588. }
  10589. }
  10590. if (!name || name === '.' || name === '..' || !valid) {
  10591. inError = true;
  10592. fm.error(file.mime === 'directory'? 'errInvDirname' : 'errInvName', {modal: true, close: function(){setTimeout(select, 120);}});
  10593. return false;
  10594. }
  10595. if (fm.fileByName(name, phash)) {
  10596. inError = true;
  10597. fm.error(['errExists', name], {modal: true, close: function(){setTimeout(select, 120);}});
  10598. return false;
  10599. }
  10600. cut = (sel && move)? fm.exec('cut', sel) : null;
  10601. $.when(cut)
  10602. .done(function() {
  10603. var toast = {},
  10604. nextAct = {};
  10605. rest();
  10606. input.hide().before($('<span>').text(name));
  10607. fm.lockfiles({files : [id]});
  10608. fm.request({
  10609. data : Object.assign({cmd : req, name : name, target : phash}, data || {}),
  10610. notify : {type : req, cnt : 1},
  10611. preventFail : true,
  10612. syncOnFail : true,
  10613. navigate : {toast : toast},
  10614. })
  10615. .fail(function(error) {
  10616. fm.unlockfiles({files : [id]});
  10617. inError = true;
  10618. input.show().prev().remove();
  10619. fm.error(error, {modal: true, close: function(){setTimeout(select, 120);}});
  10620. })
  10621. .done(function(data) {
  10622. if (data && data.added && data.added[0]) {
  10623. var item = data.added[0],
  10624. dirhash = item.hash,
  10625. newItem = ui.find('#'+fm[find](dirhash)),
  10626. acts = {
  10627. 'directory' : { cmd: 'open', msg: 'cmdopendir' },
  10628. 'text' : { cmd: 'edit', msg: 'cmdedit' },
  10629. 'default' : { cmd: 'open', msg: 'cmdopen' }
  10630. };
  10631. if (sel && move) {
  10632. fm.one(req+'done', function() {
  10633. fm.exec('paste', dirhash);
  10634. });
  10635. }
  10636. if (!move) {
  10637. Object.assign(nextAct, nextAction || acts[item.mime] || acts[item.mime.split('/')[0]] || acts[$.inArray(item.mime, fm.resources.mimes.text) !== -1 ? 'text' : 'none'] || acts['default']);
  10638. Object.assign(toast, nextAct.cmd ? {
  10639. incwd : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)]), action: nextAct},
  10640. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)]), action: nextAct}
  10641. } : {
  10642. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd'+cmd)])}
  10643. });
  10644. }
  10645. }
  10646. dfrd.resolve(data);
  10647. });
  10648. })
  10649. .fail(function() {
  10650. dfrd.reject();
  10651. });
  10652. }
  10653. }),
  10654. select = function() {
  10655. var name = fm.splitFileExtention(input.val())[0];
  10656. if (!inError && fm.UA.Mobile && !fm.UA.iOS) { // since iOS has a bug? (z-index not effect) so disable it
  10657. overlay.on('click', cancel).elfinderoverlay('show');
  10658. pnode.css('z-index', overlay.css('z-index') + 1);
  10659. }
  10660. inError = false;
  10661. ! fm.enabled() && fm.enable();
  10662. input.trigger('focus').trigger('select');
  10663. input[0].setSelectionRange && input[0].setSelectionRange(0, name.length);
  10664. },
  10665. resize = function() {
  10666. node.trigger('scrolltoview', {blink : false});
  10667. },
  10668. openCallback = function() {
  10669. dfrd && (dfrd.state() === 'pending') && dfrd.reject();
  10670. },
  10671. inError = false,
  10672. nextAction,
  10673. // for tree
  10674. dst, dstCls, collapsed, expanded, arrow, subtree;
  10675. if (!fm.isCommandEnabled(req, phash) || !node.length) {
  10676. return dfrd.reject();
  10677. }
  10678. if ($.isPlainObject(self.nextAction)){
  10679. nextAction = Object.assign({}, self.nextAction);
  10680. }
  10681. if (fm.UA.iOS) {
  10682. // prevent auto zoom
  10683. input.css('font-size', '16px');
  10684. }
  10685. if (tree) {
  10686. dst = $('#'+fm[find](phash));
  10687. collapsed = fm.res('class', 'navcollapse');
  10688. expanded = fm.res('class', 'navexpand');
  10689. arrow = fm.res('class', 'navarrow');
  10690. subtree = fm.res('class', 'navsubtree');
  10691. node.closest('.'+subtree).show();
  10692. if (! dst.hasClass(collapsed)) {
  10693. dstCls = dst.attr('class');
  10694. dst.addClass(collapsed+' '+expanded+' elfinder-subtree-loaded');
  10695. }
  10696. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  10697. dst.children('.'+arrow).trigger('click').data('dfrd').done(function() {
  10698. if (input.val() === file.name) {
  10699. input.val(fm.uniqueName(self.prefix, phash)).trigger('select').trigger('focus');
  10700. }
  10701. });
  10702. }
  10703. nnode = node.contents().filter(function(){ return this.nodeType==3 && $(this).parent().attr('id') === fm.navHash2Id(file.hash); });
  10704. nnode.replaceWith(input.val(file.name));
  10705. } else {
  10706. empty && wz.removeClass('elfinder-cwd-wrapper-empty');
  10707. nnode = node.find('.elfinder-cwd-filename');
  10708. pnode = nnode.parent();
  10709. node.css('position', 'relative').addClass('ui-front');
  10710. if (tarea) {
  10711. nnode.css('max-height', 'none');
  10712. } else {
  10713. colwidth = pnode.width();
  10714. pnode.width(colwidth - 15)
  10715. .parent('td').css('overflow', 'visible');
  10716. }
  10717. nnode.empty().append(input.val(file.name));
  10718. }
  10719. fm.bind('resize', resize).one('open', openCallback);
  10720. input.trigger('keyup');
  10721. select();
  10722. return dfrd;
  10723. }
  10724. },
  10725. blink: function(elm, mode) {
  10726. var acts = {
  10727. slowonce : function(){elm.hide().delay(250).fadeIn(750).delay(500).fadeOut(3500);},
  10728. lookme : function(){elm.show().fadeOut(500).fadeIn(750);}
  10729. }, func;
  10730. mode = mode || 'slowonce';
  10731. func = acts[mode] || acts['lookme'];
  10732. elm.stop(true, true);
  10733. func();
  10734. }
  10735. };
  10736. /*
  10737. * File: /js/jquery.dialogelfinder.js
  10738. */
  10739. /**
  10740. * @class dialogelfinder - open elFinder in dialog window
  10741. *
  10742. * @param Object elFinder options with dialog options
  10743. * @example
  10744. * $(selector).dialogelfinder({
  10745. * // some elfinder options
  10746. * title : 'My files', // dialog title, default = "Files"
  10747. * width : 850, // dialog width, default 840
  10748. * autoOpen : false, // if false - dialog will not be opened after init, default = true
  10749. * destroyOnClose : true // destroy elFinder on close dialog, default = false
  10750. * })
  10751. * @author Dmitry (dio) Levashov
  10752. **/
  10753. $.fn.dialogelfinder = function(opts) {
  10754. var position = 'elfinderPosition',
  10755. destroy = 'elfinderDestroyOnClose',
  10756. node;
  10757. this.not('.elfinder').each(function() {
  10758. var doc = $(document),
  10759. toolbar = $('<div class="ui-widget-header dialogelfinder-drag ui-corner-top">'+(opts.title || 'Files')+'</div>'),
  10760. button = $('<a href="#" class="dialogelfinder-drag-close ui-corner-all"><span class="ui-icon ui-icon-closethick"> </span></a>')
  10761. .appendTo(toolbar)
  10762. .on('click', function(e) {
  10763. e.preventDefault();
  10764. node.dialogelfinder('close');
  10765. }),
  10766. node = $(this).addClass('dialogelfinder')
  10767. .css('position', 'absolute')
  10768. .hide()
  10769. .appendTo('body')
  10770. .draggable({
  10771. handle : '.dialogelfinder-drag',
  10772. containment : 'window',
  10773. stop : function() {
  10774. node.trigger('resize');
  10775. elfinder.trigger('resize');
  10776. }
  10777. })
  10778. .elfinder(opts)
  10779. .prepend(toolbar),
  10780. elfinder = node.elfinder('instance');
  10781. node.width(parseInt(node.width()) || 840) // fix width if set to "auto"
  10782. .data(destroy, !!opts.destroyOnClose)
  10783. .find('.elfinder-toolbar').removeClass('ui-corner-top');
  10784. opts.position && node.data(position, opts.position);
  10785. opts.autoOpen !== false && $(this).dialogelfinder('open');
  10786. });
  10787. if (opts == 'open') {
  10788. node = $(this),
  10789. pos = node.data(position) || {
  10790. top : parseInt($(document).scrollTop() + ($(window).height() < node.height() ? 2 : ($(window).height() - node.height())/2)),
  10791. left : parseInt($(document).scrollLeft() + ($(window).width() < node.width() ? 2 : ($(window).width() - node.width())/2))
  10792. };
  10793. if (node.is(':hidden')) {
  10794. node.addClass('ui-front').css(pos).show().trigger('resize');
  10795. setTimeout(function() {
  10796. // fix resize icon position and make elfinder active
  10797. node.trigger('resize').mousedown();
  10798. }, 200);
  10799. }
  10800. } else if (opts == 'close') {
  10801. node = $(this).removeClass('ui-front');
  10802. if (node.is(':visible')) {
  10803. !!node.data(destroy)
  10804. ? node.elfinder('destroy').remove()
  10805. : node.elfinder('close');
  10806. }
  10807. } else if (opts == 'instance') {
  10808. return $(this).getElFinder();
  10809. }
  10810. return this;
  10811. };
  10812. /*
  10813. * File: /js/i18n/elfinder.en.js
  10814. */
  10815. /**
  10816. * English translation
  10817. * @author Troex Nevelin <troex@fury.scancode.ru>
  10818. * @author Naoki Sawada <hypweb+elfinder@gmail.com>
  10819. * @version 2018-03-10
  10820. */
  10821. // elfinder.en.js is integrated into elfinder.(full|min).js by jake build
  10822. if (typeof elFinder === 'function' && elFinder.prototype.i18) {
  10823. elFinder.prototype.i18.en = {
  10824. translator : 'Troex Nevelin &lt;troex@fury.scancode.ru&gt;, Naoki Sawada &lt;hypweb+elfinder@gmail.com&gt;',
  10825. language : 'English',
  10826. direction : 'ltr',
  10827. dateFormat : 'M d, Y h:i A', // Mar 13, 2012 05:27 PM
  10828. fancyDateFormat : '$1 h:i A', // will produce smth like: Today 12:25 PM,
  10829. nonameDateFormat : 'ymd-His', // to apply if upload file is noname: 120513172700
  10830. messages : {
  10831. /********************************** errors **********************************/
  10832. 'error' : 'Error',
  10833. 'errUnknown' : 'Unknown error.',
  10834. 'errUnknownCmd' : 'Unknown command.',
  10835. 'errJqui' : 'Invalid jQuery UI configuration. Selectable, draggable and droppable components must be included.',
  10836. 'errNode' : 'elFinder requires DOM Element to be created.',
  10837. 'errURL' : 'Invalid elFinder configuration! URL option is not set.',
  10838. 'errAccess' : 'Access denied.',
  10839. 'errConnect' : 'Unable to connect to backend.',
  10840. 'errAbort' : 'Connection aborted.',
  10841. 'errTimeout' : 'Connection timeout.',
  10842. 'errNotFound' : 'Backend not found.',
  10843. 'errResponse' : 'Invalid backend response.',
  10844. 'errConf' : 'Invalid backend configuration.',
  10845. 'errJSON' : 'PHP JSON module not installed.',
  10846. 'errNoVolumes' : 'Readable volumes not available.',
  10847. 'errCmdParams' : 'Invalid parameters for command "$1".',
  10848. 'errDataNotJSON' : 'Data is not JSON.',
  10849. 'errDataEmpty' : 'Data is empty.',
  10850. 'errCmdReq' : 'Backend request requires command name.',
  10851. 'errOpen' : 'Unable to open "$1".',
  10852. 'errNotFolder' : 'Object is not a folder.',
  10853. 'errNotFile' : 'Object is not a file.',
  10854. 'errRead' : 'Unable to read "$1".',
  10855. 'errWrite' : 'Unable to write into "$1".',
  10856. 'errPerm' : 'Permission denied.',
  10857. 'errLocked' : '"$1" is locked and can not be renamed, moved or removed.',
  10858. 'errExists' : 'Item named "$1" already exists.',
  10859. 'errInvName' : 'Invalid file name.',
  10860. 'errInvDirname' : 'Invalid folder name.', // from v2.1.24 added 12.4.2017
  10861. 'errFolderNotFound' : 'Folder not found.',
  10862. 'errFileNotFound' : 'File not found.',
  10863. 'errTrgFolderNotFound' : 'Target folder "$1" not found.',
  10864. 'errPopup' : 'Browser prevented opening popup window. To open file enable it in browser options.',
  10865. 'errMkdir' : 'Unable to create folder "$1".',
  10866. 'errMkfile' : 'Unable to create file "$1".',
  10867. 'errRename' : 'Unable to rename "$1".',
  10868. 'errCopyFrom' : 'Copying files from volume "$1" not allowed.',
  10869. 'errCopyTo' : 'Copying files to volume "$1" not allowed.',
  10870. 'errMkOutLink' : 'Unable to create a link to outside the volume root.', // from v2.1 added 03.10.2015
  10871. 'errUpload' : 'Upload error.', // old name - errUploadCommon
  10872. 'errUploadFile' : 'Unable to upload "$1".', // old name - errUpload
  10873. 'errUploadNoFiles' : 'No files found for upload.',
  10874. 'errUploadTotalSize' : 'Data exceeds the maximum allowed size.', // old name - errMaxSize
  10875. 'errUploadFileSize' : 'File exceeds maximum allowed size.', // old name - errFileMaxSize
  10876. 'errUploadMime' : 'File type not allowed.',
  10877. 'errUploadTransfer' : '"$1" transfer error.',
  10878. 'errUploadTemp' : 'Unable to make temporary file for upload.', // from v2.1 added 26.09.2015
  10879. 'errNotReplace' : 'Object "$1" already exists at this location and can not be replaced by object with another type.', // new
  10880. 'errReplace' : 'Unable to replace "$1".',
  10881. 'errSave' : 'Unable to save "$1".',
  10882. 'errCopy' : 'Unable to copy "$1".',
  10883. 'errMove' : 'Unable to move "$1".',
  10884. 'errCopyInItself' : 'Unable to copy "$1" into itself.',
  10885. 'errRm' : 'Unable to remove "$1".',
  10886. 'errTrash' : 'Unable into trash.', // from v2.1.24 added 30.4.2017
  10887. 'errRmSrc' : 'Unable remove source file(s).',
  10888. 'errExtract' : 'Unable to extract files from "$1".',
  10889. 'errArchive' : 'Unable to create archive.',
  10890. 'errArcType' : 'Unsupported archive type.',
  10891. 'errNoArchive' : 'File is not archive or has unsupported archive type.',
  10892. 'errCmdNoSupport' : 'Backend does not support this command.',
  10893. 'errReplByChild' : 'The folder "$1" can\'t be replaced by an item it contains.',
  10894. 'errArcSymlinks' : 'For security reason denied to unpack archives contains symlinks or files with not allowed names.', // edited 24.06.2012
  10895. 'errArcMaxSize' : 'Archive files exceeds maximum allowed size.',
  10896. 'errResize' : 'Unable to resize "$1".',
  10897. 'errResizeDegree' : 'Invalid rotate degree.', // added 7.3.2013
  10898. 'errResizeRotate' : 'Unable to rotate image.', // added 7.3.2013
  10899. 'errResizeSize' : 'Invalid image size.', // added 7.3.2013
  10900. 'errResizeNoChange' : 'Image size not changed.', // added 7.3.2013
  10901. 'errUsupportType' : 'Unsupported file type.',
  10902. 'errNotUTF8Content' : 'File "$1" is not in UTF-8 and cannot be edited.', // added 9.11.2011
  10903. 'errNetMount' : 'Unable to mount "$1".', // added 17.04.2012
  10904. 'errNetMountNoDriver' : 'Unsupported protocol.', // added 17.04.2012
  10905. 'errNetMountFailed' : 'Mount failed.', // added 17.04.2012
  10906. 'errNetMountHostReq' : 'Host required.', // added 18.04.2012
  10907. 'errSessionExpires' : 'Your session has expired due to inactivity.',
  10908. 'errCreatingTempDir' : 'Unable to create temporary directory: "$1"',
  10909. 'errFtpDownloadFile' : 'Unable to download file from FTP: "$1"',
  10910. 'errFtpUploadFile' : 'Unable to upload file to FTP: "$1"',
  10911. 'errFtpMkdir' : 'Unable to create remote directory on FTP: "$1"',
  10912. 'errArchiveExec' : 'Error while archiving files: "$1"',
  10913. 'errExtractExec' : 'Error while extracting files: "$1"',
  10914. 'errNetUnMount' : 'Unable to unmount.', // from v2.1 added 30.04.2012
  10915. 'errConvUTF8' : 'Not convertible to UTF-8', // from v2.1 added 08.04.2014
  10916. 'errFolderUpload' : 'Try the modern browser, If you\'d like to upload the folder.', // from v2.1 added 26.6.2015
  10917. 'errSearchTimeout' : 'Timed out while searching "$1". Search result is partial.', // from v2.1 added 12.1.2016
  10918. 'errReauthRequire' : 'Re-authorization is required.', // from v2.1.10 added 24.3.2016
  10919. 'errMaxTargets' : 'Max number of selectable items is $1.', // from v2.1.17 added 17.10.2016
  10920. 'errRestore' : 'Unable to restore from the trash. Can\'t identify the restore destination.', // from v2.1.24 added 3.5.2017
  10921. 'errEditorNotFound' : 'Editor not found to this file type.', // from v2.1.25 added 23.5.2017
  10922. 'errServerError' : 'Error occurred on the server side.', // from v2.1.25 added 16.6.2017
  10923. 'errEmpty' : 'Unable to empty folder "$1".', // from v2.1.25 added 22.6.2017
  10924. /******************************* commands names ********************************/
  10925. 'cmdarchive' : 'Create archive',
  10926. 'cmdback' : 'Back',
  10927. 'cmdcopy' : 'Copy',
  10928. 'cmdcut' : 'Cut',
  10929. 'cmddownload' : 'Download',
  10930. 'cmdduplicate' : 'Duplicate',
  10931. 'cmdedit' : 'Edit file',
  10932. 'cmdextract' : 'Extract files from archive',
  10933. 'cmdforward' : 'Forward',
  10934. 'cmdgetfile' : 'Select files',
  10935. 'cmdhelp' : 'About this software',
  10936. 'cmdhome' : 'Root',
  10937. 'cmdinfo' : 'Get info',
  10938. 'cmdmkdir' : 'New folder',
  10939. 'cmdmkdirin' : 'Into New Folder', // from v2.1.7 added 19.2.2016
  10940. 'cmdmkfile' : 'New text file',
  10941. 'cmdopen' : 'Open',
  10942. 'cmdpaste' : 'Paste',
  10943. 'cmdquicklook' : 'Preview',
  10944. 'cmdreload' : 'Reload',
  10945. 'cmdrename' : 'Rename',
  10946. 'cmdrm' : 'Delete',
  10947. 'cmdtrash' : 'Into trash', //from v2.1.24 added 29.4.2017
  10948. 'cmdrestore' : 'Restore', //from v2.1.24 added 3.5.2017
  10949. 'cmdsearch' : 'Find files',
  10950. 'cmdup' : 'Go to parent folder',
  10951. 'cmdupload' : 'Upload files',
  10952. 'cmdview' : 'View',
  10953. 'cmdresize' : 'Resize & Rotate',
  10954. 'cmdsort' : 'Sort',
  10955. 'cmdnetmount' : 'Mount network volume', // added 18.04.2012
  10956. 'cmdnetunmount': 'Unmount', // from v2.1 added 30.04.2012
  10957. 'cmdplaces' : 'To Places', // added 28.12.2014
  10958. 'cmdchmod' : 'Change mode', // from v2.1 added 20.6.2015
  10959. 'cmdopendir' : 'Open a folder', // from v2.1 added 13.1.2016
  10960. 'cmdcolwidth' : 'Reset column width', // from v2.1.13 added 12.06.2016
  10961. 'cmdfullscreen': 'Full Screen', // from v2.1.15 added 03.08.2016
  10962. 'cmdmove' : 'Move', // from v2.1.15 added 21.08.2016
  10963. 'cmdempty' : 'Empty the folder', // from v2.1.25 added 22.06.2017
  10964. 'cmdundo' : 'Undo', // from v2.1.27 added 31.07.2017
  10965. 'cmdredo' : 'Redo', // from v2.1.27 added 31.07.2017
  10966. 'cmdpreference': 'Preferences', // from v2.1.27 added 03.08.2017
  10967. 'cmdselectall' : 'Select all', // from v2.1.28 added 15.08.2017
  10968. 'cmdselectnone': 'Select none', // from v2.1.28 added 15.08.2017
  10969. 'cmdselectinvert': 'Invert selection', // from v2.1.28 added 15.08.2017
  10970. /*********************************** buttons ***********************************/
  10971. 'btnClose' : 'Close',
  10972. 'btnSave' : 'Save',
  10973. 'btnRm' : 'Remove',
  10974. 'btnApply' : 'Apply',
  10975. 'btnCancel' : 'Cancel',
  10976. 'btnNo' : 'No',
  10977. 'btnYes' : 'Yes',
  10978. 'btnMount' : 'Mount', // added 18.04.2012
  10979. 'btnApprove': 'Goto $1 & approve', // from v2.1 added 26.04.2012
  10980. 'btnUnmount': 'Unmount', // from v2.1 added 30.04.2012
  10981. 'btnConv' : 'Convert', // from v2.1 added 08.04.2014
  10982. 'btnCwd' : 'Here', // from v2.1 added 22.5.2015
  10983. 'btnVolume' : 'Volume', // from v2.1 added 22.5.2015
  10984. 'btnAll' : 'All', // from v2.1 added 22.5.2015
  10985. 'btnMime' : 'MIME Type', // from v2.1 added 22.5.2015
  10986. 'btnFileName':'Filename', // from v2.1 added 22.5.2015
  10987. 'btnSaveClose': 'Save & Close', // from v2.1 added 12.6.2015
  10988. 'btnBackup' : 'Backup', // fromv2.1 added 28.11.2015
  10989. 'btnRename' : 'Rename', // from v2.1.24 added 6.4.2017
  10990. 'btnRenameAll' : 'Rename(All)', // from v2.1.24 added 6.4.2017
  10991. 'btnPrevious' : 'Prev ($1/$2)', // from v2.1.24 added 11.5.2017
  10992. 'btnNext' : 'Next ($1/$2)', // from v2.1.24 added 11.5.2017
  10993. 'btnSaveAs' : 'Save As', // from v2.1.25 added 24.5.2017
  10994. /******************************** notifications ********************************/
  10995. 'ntfopen' : 'Open folder',
  10996. 'ntffile' : 'Open file',
  10997. 'ntfreload' : 'Reload folder content',
  10998. 'ntfmkdir' : 'Creating folder',
  10999. 'ntfmkfile' : 'Creating files',
  11000. 'ntfrm' : 'Delete items',
  11001. 'ntfcopy' : 'Copy items',
  11002. 'ntfmove' : 'Move items',
  11003. 'ntfprepare' : 'Checking existing items',
  11004. 'ntfrename' : 'Rename files',
  11005. 'ntfupload' : 'Uploading files',
  11006. 'ntfdownload' : 'Downloading files',
  11007. 'ntfsave' : 'Save files',
  11008. 'ntfarchive' : 'Creating archive',
  11009. 'ntfextract' : 'Extracting files from archive',
  11010. 'ntfsearch' : 'Searching files',
  11011. 'ntfresize' : 'Resizing images',
  11012. 'ntfsmth' : 'Doing something',
  11013. 'ntfloadimg' : 'Loading image',
  11014. 'ntfnetmount' : 'Mounting network volume', // added 18.04.2012
  11015. 'ntfnetunmount': 'Unmounting network volume', // from v2.1 added 30.04.2012
  11016. 'ntfdim' : 'Acquiring image dimension', // added 20.05.2013
  11017. 'ntfreaddir' : 'Reading folder infomation', // from v2.1 added 01.07.2013
  11018. 'ntfurl' : 'Getting URL of link', // from v2.1 added 11.03.2014
  11019. 'ntfchmod' : 'Changing file mode', // from v2.1 added 20.6.2015
  11020. 'ntfpreupload': 'Verifying upload file name', // from v2.1 added 31.11.2015
  11021. 'ntfzipdl' : 'Creating a file for download', // from v2.1.7 added 23.1.2016
  11022. 'ntfparents' : 'Getting path infomation', // from v2.1.17 added 2.11.2016
  11023. 'ntfchunkmerge': 'Processing the uploaded file', // from v2.1.17 added 2.11.2016
  11024. 'ntftrash' : 'Doing throw in the trash', // from v2.1.24 added 2.5.2017
  11025. 'ntfrestore' : 'Doing restore from the trash', // from v2.1.24 added 3.5.2017
  11026. 'ntfchkdir' : 'Checking destination folder', // from v2.1.24 added 3.5.2017
  11027. 'ntfundo' : 'Undoing previous operation', // from v2.1.27 added 31.07.2017
  11028. 'ntfredo' : 'Redoing previous undone', // from v2.1.27 added 31.07.2017
  11029. /*********************************** volumes *********************************/
  11030. 'volume_Trash' : 'Trash', //from v2.1.24 added 29.4.2017
  11031. /************************************ dates **********************************/
  11032. 'dateUnknown' : 'unknown',
  11033. 'Today' : 'Today',
  11034. 'Yesterday' : 'Yesterday',
  11035. 'msJan' : 'Jan',
  11036. 'msFeb' : 'Feb',
  11037. 'msMar' : 'Mar',
  11038. 'msApr' : 'Apr',
  11039. 'msMay' : 'May',
  11040. 'msJun' : 'Jun',
  11041. 'msJul' : 'Jul',
  11042. 'msAug' : 'Aug',
  11043. 'msSep' : 'Sep',
  11044. 'msOct' : 'Oct',
  11045. 'msNov' : 'Nov',
  11046. 'msDec' : 'Dec',
  11047. 'January' : 'January',
  11048. 'February' : 'February',
  11049. 'March' : 'March',
  11050. 'April' : 'April',
  11051. 'May' : 'May',
  11052. 'June' : 'June',
  11053. 'July' : 'July',
  11054. 'August' : 'August',
  11055. 'September' : 'September',
  11056. 'October' : 'October',
  11057. 'November' : 'November',
  11058. 'December' : 'December',
  11059. 'Sunday' : 'Sunday',
  11060. 'Monday' : 'Monday',
  11061. 'Tuesday' : 'Tuesday',
  11062. 'Wednesday' : 'Wednesday',
  11063. 'Thursday' : 'Thursday',
  11064. 'Friday' : 'Friday',
  11065. 'Saturday' : 'Saturday',
  11066. 'Sun' : 'Sun',
  11067. 'Mon' : 'Mon',
  11068. 'Tue' : 'Tue',
  11069. 'Wed' : 'Wed',
  11070. 'Thu' : 'Thu',
  11071. 'Fri' : 'Fri',
  11072. 'Sat' : 'Sat',
  11073. /******************************** sort variants ********************************/
  11074. 'sortname' : 'by name',
  11075. 'sortkind' : 'by kind',
  11076. 'sortsize' : 'by size',
  11077. 'sortdate' : 'by date',
  11078. 'sortFoldersFirst' : 'Folders first',
  11079. 'sortperm' : 'by permission', // from v2.1.13 added 13.06.2016
  11080. 'sortmode' : 'by mode', // from v2.1.13 added 13.06.2016
  11081. 'sortowner' : 'by owner', // from v2.1.13 added 13.06.2016
  11082. 'sortgroup' : 'by group', // from v2.1.13 added 13.06.2016
  11083. 'sortAlsoTreeview' : 'Also Treeview', // from v2.1.15 added 01.08.2016
  11084. /********************************** new items **********************************/
  11085. 'untitled file.txt' : 'NewFile.txt', // added 10.11.2015
  11086. 'untitled folder' : 'NewFolder', // added 10.11.2015
  11087. 'Archive' : 'NewArchive', // from v2.1 added 10.11.2015
  11088. /********************************** messages **********************************/
  11089. 'confirmReq' : 'Confirmation required',
  11090. 'confirmRm' : 'Are you sure you want to permanently remove items?<br/>This cannot be undone!',
  11091. 'confirmRepl' : 'Replace old file with new one? (If it contains folders, it will be merged. To backup and replace, select Backup.)',
  11092. 'confirmRest' : 'Replace existing item with the item in trash?', // fromv2.1.24 added 5.5.2017
  11093. '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
  11094. '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
  11095. 'confirmNotSave' : 'It has been modified.<br/>Losing work if you do not save changes.', // from v2.1 added 15.7.2015
  11096. 'confirmTrash' : 'Are you sure you want to move items to trash bin?', //from v2.1.24 added 29.4.2017
  11097. 'apllyAll' : 'Apply to all',
  11098. 'name' : 'Name',
  11099. 'size' : 'Size',
  11100. 'perms' : 'Permissions',
  11101. 'modify' : 'Modified',
  11102. 'kind' : 'Kind',
  11103. 'read' : 'read',
  11104. 'write' : 'write',
  11105. 'noaccess' : 'no access',
  11106. 'and' : 'and',
  11107. 'unknown' : 'unknown',
  11108. 'selectall' : 'Select all items',
  11109. 'selectfiles' : 'Select item(s)',
  11110. 'selectffile' : 'Select first item',
  11111. 'selectlfile' : 'Select last item',
  11112. 'viewlist' : 'List view',
  11113. 'viewicons' : 'Icons view',
  11114. 'places' : 'Places',
  11115. 'calc' : 'Calculate',
  11116. 'path' : 'Path',
  11117. 'aliasfor' : 'Alias for',
  11118. 'locked' : 'Locked',
  11119. 'dim' : 'Dimensions',
  11120. 'files' : 'Files',
  11121. 'folders' : 'Folders',
  11122. 'items' : 'Items',
  11123. 'yes' : 'yes',
  11124. 'no' : 'no',
  11125. 'link' : 'Link',
  11126. 'searcresult' : 'Search results',
  11127. 'selected' : 'selected items',
  11128. 'about' : 'About',
  11129. 'shortcuts' : 'Shortcuts',
  11130. 'help' : 'Help',
  11131. 'webfm' : 'Web file manager',
  11132. 'ver' : 'Version',
  11133. 'protocolver' : 'protocol version',
  11134. 'homepage' : 'Project home',
  11135. 'docs' : 'Documentation',
  11136. 'github' : 'Fork us on Github',
  11137. 'twitter' : 'Follow us on twitter',
  11138. 'facebook' : 'Join us on facebook',
  11139. 'team' : 'Team',
  11140. 'chiefdev' : 'chief developer',
  11141. 'developer' : 'developer',
  11142. 'contributor' : 'contributor',
  11143. 'maintainer' : 'maintainer',
  11144. 'translator' : 'translator',
  11145. 'icons' : 'Icons',
  11146. 'dontforget' : 'and don\'t forget to take your towel',
  11147. 'shortcutsof' : 'Shortcuts disabled',
  11148. 'dropFiles' : 'Drop files here',
  11149. 'or' : 'or',
  11150. 'selectForUpload' : 'Select files',
  11151. 'moveFiles' : 'Move items',
  11152. 'copyFiles' : 'Copy items',
  11153. 'restoreFiles' : 'Restore items', // from v2.1.24 added 5.5.2017
  11154. 'rmFromPlaces' : 'Remove from places',
  11155. 'aspectRatio' : 'Aspect ratio',
  11156. 'scale' : 'Scale',
  11157. 'width' : 'Width',
  11158. 'height' : 'Height',
  11159. 'resize' : 'Resize',
  11160. 'crop' : 'Crop',
  11161. 'rotate' : 'Rotate',
  11162. 'rotate-cw' : 'Rotate 90 degrees CW',
  11163. 'rotate-ccw' : 'Rotate 90 degrees CCW',
  11164. 'degree' : '°',
  11165. 'netMountDialogTitle' : 'Mount network volume', // added 18.04.2012
  11166. 'protocol' : 'Protocol', // added 18.04.2012
  11167. 'host' : 'Host', // added 18.04.2012
  11168. 'port' : 'Port', // added 18.04.2012
  11169. 'user' : 'User', // added 18.04.2012
  11170. 'pass' : 'Password', // added 18.04.2012
  11171. 'confirmUnmount' : 'Are you sure to unmount $1?', // from v2.1 added 30.04.2012
  11172. 'dropFilesBrowser': 'Drop or Paste files from browser', // from v2.1 added 30.05.2012
  11173. 'dropPasteFiles' : 'Drop files, Paste URLs or images(clipboard) here', // from v2.1 added 07.04.2014
  11174. 'encoding' : 'Encoding', // from v2.1 added 19.12.2014
  11175. 'locale' : 'Locale', // from v2.1 added 19.12.2014
  11176. 'searchTarget' : 'Target: $1', // from v2.1 added 22.5.2015
  11177. 'searchMime' : 'Search by input MIME Type', // from v2.1 added 22.5.2015
  11178. 'owner' : 'Owner', // from v2.1 added 20.6.2015
  11179. 'group' : 'Group', // from v2.1 added 20.6.2015
  11180. 'other' : 'Other', // from v2.1 added 20.6.2015
  11181. 'execute' : 'Execute', // from v2.1 added 20.6.2015
  11182. 'perm' : 'Permission', // from v2.1 added 20.6.2015
  11183. 'mode' : 'Mode', // from v2.1 added 20.6.2015
  11184. 'emptyFolder' : 'Folder is empty', // from v2.1.6 added 30.12.2015
  11185. 'emptyFolderDrop' : 'Folder is empty\\A Drop to add items', // from v2.1.6 added 30.12.2015
  11186. 'emptyFolderLTap' : 'Folder is empty\\A Long tap to add items', // from v2.1.6 added 30.12.2015
  11187. 'quality' : 'Quality', // from v2.1.6 added 5.1.2016
  11188. 'autoSync' : 'Auto sync', // from v2.1.6 added 10.1.2016
  11189. 'moveUp' : 'Move up', // from v2.1.6 added 18.1.2016
  11190. 'getLink' : 'Get URL link', // from v2.1.7 added 9.2.2016
  11191. 'selectedItems' : 'Selected items ($1)', // from v2.1.7 added 2.19.2016
  11192. 'folderId' : 'Folder ID', // from v2.1.10 added 3.25.2016
  11193. 'offlineAccess' : 'Allow offline access', // from v2.1.10 added 3.25.2016
  11194. 'reAuth' : 'To re-authenticate', // from v2.1.10 added 3.25.2016
  11195. 'nowLoading' : 'Now loading...', // from v2.1.12 added 4.26.2016
  11196. 'openMulti' : 'Open multiple files', // from v2.1.12 added 5.14.2016
  11197. '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
  11198. 'emptySearch' : 'Search results is empty in search target.', // from v2.1.12 added 5.16.2016
  11199. 'editingFile' : 'It is editing a file.', // from v2.1.13 added 6.3.2016
  11200. 'hasSelected' : 'You have selected $1 items.', // from v2.1.13 added 6.3.2016
  11201. 'hasClipboard' : 'You have $1 items in the clipboard.', // from v2.1.13 added 6.3.2016
  11202. 'incSearchOnly' : 'Incremental search is only from the current view.', // from v2.1.13 added 6.30.2016
  11203. 'reinstate' : 'Reinstate', // from v2.1.15 added 3.8.2016
  11204. 'complete' : '$1 complete', // from v2.1.15 added 21.8.2016
  11205. 'contextmenu' : 'Context menu', // from v2.1.15 added 9.9.2016
  11206. 'pageTurning' : 'Page turning', // from v2.1.15 added 10.9.2016
  11207. 'volumeRoots' : 'Volume roots', // from v2.1.16 added 16.9.2016
  11208. 'reset' : 'Reset', // from v2.1.16 added 1.10.2016
  11209. 'bgcolor' : 'Background color', // from v2.1.16 added 1.10.2016
  11210. 'colorPicker' : 'Color picker', // from v2.1.16 added 1.10.2016
  11211. '8pxgrid' : '8px Grid', // from v2.1.16 added 4.10.2016
  11212. 'enabled' : 'Enabled', // from v2.1.16 added 4.10.2016
  11213. 'disabled' : 'Disabled', // from v2.1.16 added 4.10.2016
  11214. 'emptyIncSearch' : 'Search results is empty in current view.\\A Press [Enter] to expand search target.', // from v2.1.16 added 5.10.2016
  11215. 'emptyLetSearch' : 'First letter search results is empty in current view.', // from v2.1.23 added 24.3.2017
  11216. 'textLabel' : 'Text label', // from v2.1.17 added 13.10.2016
  11217. 'minsLeft' : '$1 mins left', // from v2.1.17 added 13.11.2016
  11218. 'openAsEncoding' : 'Reopen with selected encoding', // from v2.1.19 added 2.12.2016
  11219. 'saveAsEncoding' : 'Save with the selected encoding', // from v2.1.19 added 2.12.2016
  11220. 'selectFolder' : 'Select folder', // from v2.1.20 added 13.12.2016
  11221. 'firstLetterSearch': 'First letter search', // from v2.1.23 added 24.3.2017
  11222. 'presets' : 'Presets', // from v2.1.25 added 26.5.2017
  11223. 'tooManyToTrash' : 'It\'s too many items so it can\'t into trash.', // from v2.1.25 added 9.6.2017
  11224. 'TextArea' : 'TextArea', // from v2.1.25 added 14.6.2017
  11225. 'folderToEmpty' : 'Empty the folder "$1".', // from v2.1.25 added 22.6.2017
  11226. 'filderIsEmpty' : 'There are no items in a folder "$1".', // from v2.1.25 added 22.6.2017
  11227. 'preference' : 'Preference', // from v2.1.26 added 28.6.2017
  11228. 'language' : 'Language setting', // from v2.1.26 added 28.6.2017
  11229. 'clearBrowserData': 'Initialize the settings saved in this browser', // from v2.1.26 added 28.6.2017
  11230. 'toolbarPref' : 'Toolbar settings', // from v2.1.27 added 2.8.2017
  11231. 'charsLeft' : '... $1 chars left.', // from v2.1.29 added 30.8.2017
  11232. 'sum' : 'Sum', // from v2.1.29 added 28.9.2017
  11233. 'roughFileSize' : 'Rough file size', // from v2.1.30 added 2.11.2017
  11234. 'autoFocusDialog' : 'Focus on the element of dialog with mouseover', // from v2.1.30 added 2.11.2017
  11235. 'select' : 'Select', // from v2.1.30 added 23.11.2017
  11236. 'selectAction' : 'Action when select file', // from v2.1.30 added 23.11.2017
  11237. 'useStoredEditor' : 'Open with the editor used last time', // from v2.1.30 added 23.11.2017
  11238. 'selectinvert' : 'Invert selection', // from v2.1.30 added 25.11.2017
  11239. '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
  11240. 'batchRename' : 'Batch rename', // from v2.1.31 added 8.12.2017
  11241. 'plusNumber' : '+ Number', // from v2.1.31 added 8.12.2017
  11242. 'asPrefix' : 'Add prefix', // from v2.1.31 added 8.12.2017
  11243. 'asSuffix' : 'Add suffix', // from v2.1.31 added 8.12.2017
  11244. 'changeExtention' : 'Change extention', // from v2.1.31 added 8.12.2017
  11245. 'columnPref' : 'Columns settings (List view)', // from v2.1.32 added 6.2.2018
  11246. 'reflectOnImmediate' : 'All changes will reflect immediately to the archive.', // from v2.1.33 added 2.3.2018
  11247. 'reflectOnUnmount' : 'Any changes will not reflect until un-mount this volume.', // from v2.1.33 added 2.3.2018
  11248. '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
  11249. 'selectionInfo' : 'Selection Info', // from v2.1.33 added 7.3.2018
  11250. 'hashChecker' : 'Algorithms to show the file hash', // from v2.1.33 added 10.3.2018
  11251. /********************************** mimetypes **********************************/
  11252. 'kindUnknown' : 'Unknown',
  11253. 'kindRoot' : 'Volume Root', // from v2.1.16 added 16.10.2016
  11254. 'kindFolder' : 'Folder',
  11255. 'kindSelects' : 'Selections', // from v2.1.29 added 29.8.2017
  11256. 'kindAlias' : 'Alias',
  11257. 'kindAliasBroken' : 'Broken alias',
  11258. // applications
  11259. 'kindApp' : 'Application',
  11260. 'kindPostscript' : 'Postscript document',
  11261. 'kindMsOffice' : 'Microsoft Office document',
  11262. 'kindMsWord' : 'Microsoft Word document',
  11263. 'kindMsExcel' : 'Microsoft Excel document',
  11264. 'kindMsPP' : 'Microsoft Powerpoint presentation',
  11265. 'kindOO' : 'Open Office document',
  11266. 'kindAppFlash' : 'Flash application',
  11267. 'kindPDF' : 'Portable Document Format (PDF)',
  11268. 'kindTorrent' : 'Bittorrent file',
  11269. 'kind7z' : '7z archive',
  11270. 'kindTAR' : 'TAR archive',
  11271. 'kindGZIP' : 'GZIP archive',
  11272. 'kindBZIP' : 'BZIP archive',
  11273. 'kindXZ' : 'XZ archive',
  11274. 'kindZIP' : 'ZIP archive',
  11275. 'kindRAR' : 'RAR archive',
  11276. 'kindJAR' : 'Java JAR file',
  11277. 'kindTTF' : 'True Type font',
  11278. 'kindOTF' : 'Open Type font',
  11279. 'kindRPM' : 'RPM package',
  11280. // texts
  11281. 'kindText' : 'Text document',
  11282. 'kindTextPlain' : 'Plain text',
  11283. 'kindPHP' : 'PHP source',
  11284. 'kindCSS' : 'Cascading style sheet',
  11285. 'kindHTML' : 'HTML document',
  11286. 'kindJS' : 'Javascript source',
  11287. 'kindRTF' : 'Rich Text Format',
  11288. 'kindC' : 'C source',
  11289. 'kindCHeader' : 'C header source',
  11290. 'kindCPP' : 'C++ source',
  11291. 'kindCPPHeader' : 'C++ header source',
  11292. 'kindShell' : 'Unix shell script',
  11293. 'kindPython' : 'Python source',
  11294. 'kindJava' : 'Java source',
  11295. 'kindRuby' : 'Ruby source',
  11296. 'kindPerl' : 'Perl script',
  11297. 'kindSQL' : 'SQL source',
  11298. 'kindXML' : 'XML document',
  11299. 'kindAWK' : 'AWK source',
  11300. 'kindCSV' : 'Comma separated values',
  11301. 'kindDOCBOOK' : 'Docbook XML document',
  11302. 'kindMarkdown' : 'Markdown text', // added 20.7.2015
  11303. // images
  11304. 'kindImage' : 'Image',
  11305. 'kindBMP' : 'BMP image',
  11306. 'kindJPEG' : 'JPEG image',
  11307. 'kindGIF' : 'GIF Image',
  11308. 'kindPNG' : 'PNG Image',
  11309. 'kindTIFF' : 'TIFF image',
  11310. 'kindTGA' : 'TGA image',
  11311. 'kindPSD' : 'Adobe Photoshop image',
  11312. 'kindXBITMAP' : 'X bitmap image',
  11313. 'kindPXM' : 'Pixelmator image',
  11314. // media
  11315. 'kindAudio' : 'Audio media',
  11316. 'kindAudioMPEG' : 'MPEG audio',
  11317. 'kindAudioMPEG4' : 'MPEG-4 audio',
  11318. 'kindAudioMIDI' : 'MIDI audio',
  11319. 'kindAudioOGG' : 'Ogg Vorbis audio',
  11320. 'kindAudioWAV' : 'WAV audio',
  11321. 'AudioPlaylist' : 'MP3 playlist',
  11322. 'kindVideo' : 'Video media',
  11323. 'kindVideoDV' : 'DV movie',
  11324. 'kindVideoMPEG' : 'MPEG movie',
  11325. 'kindVideoMPEG4' : 'MPEG-4 movie',
  11326. 'kindVideoAVI' : 'AVI movie',
  11327. 'kindVideoMOV' : 'Quick Time movie',
  11328. 'kindVideoWM' : 'Windows Media movie',
  11329. 'kindVideoFlash' : 'Flash movie',
  11330. 'kindVideoMKV' : 'Matroska movie',
  11331. 'kindVideoOGG' : 'Ogg movie'
  11332. }
  11333. };
  11334. }
  11335. /*
  11336. * File: /js/ui/button.js
  11337. */
  11338. /**
  11339. * @class elFinder toolbar button widget.
  11340. * If command has variants - create menu
  11341. *
  11342. * @author Dmitry (dio) Levashov
  11343. **/
  11344. $.fn.elfinderbutton = function(cmd) {
  11345. return this.each(function() {
  11346. var c = 'class',
  11347. fm = cmd.fm,
  11348. disabled = fm.res(c, 'disabled'),
  11349. active = fm.res(c, 'active'),
  11350. hover = fm.res(c, 'hover'),
  11351. item = 'elfinder-button-menu-item',
  11352. selected = 'elfinder-button-menu-item-selected',
  11353. menu,
  11354. text = $('<span class="elfinder-button-text">'+cmd.title+'</span>'),
  11355. button = $(this).addClass('ui-state-default elfinder-button')
  11356. .attr('title', cmd.title)
  11357. .append('<span class="elfinder-button-icon elfinder-button-icon-' + (cmd.className? cmd.className : cmd.name) + '"/>', text)
  11358. .on('mouseenter mouseleave', function(e) { !button.hasClass(disabled) && button[e.type == 'mouseleave' ? 'removeClass' : 'addClass'](hover);})
  11359. .on('click', function(e) {
  11360. if (!button.hasClass(disabled)) {
  11361. if (menu && cmd.variants.length >= 1) {
  11362. // close other menus
  11363. menu.is(':hidden') && fm.getUI().click();
  11364. e.stopPropagation();
  11365. menu.css(getMenuOffset()).slideToggle(100);
  11366. } else {
  11367. fm.exec(cmd.name, getSelected(), {_userAction: true, _currentType: 'toolbar', _currentNode: button });
  11368. }
  11369. }
  11370. }),
  11371. hideMenu = function() {
  11372. menu.hide();
  11373. },
  11374. getMenuOffset = function() {
  11375. var baseOffset = fm.getUI().offset(),
  11376. buttonOffset = button.offset();
  11377. return {
  11378. top : buttonOffset.top - baseOffset.top,
  11379. left : buttonOffset.left - baseOffset.left
  11380. };
  11381. },
  11382. getSelected = function() {
  11383. var sel = fm.selected(),
  11384. cwd;
  11385. if (!sel.length) {
  11386. if (cwd = fm.cwd()) {
  11387. sel = [ fm.cwd().hash ];
  11388. } else {
  11389. sel = void(0);
  11390. }
  11391. }
  11392. return sel;
  11393. };
  11394. text.hide();
  11395. // set self button object to cmd object
  11396. cmd.button = button;
  11397. // if command has variants create menu
  11398. if (Array.isArray(cmd.variants)) {
  11399. button.addClass('elfinder-menubutton');
  11400. menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>')
  11401. .hide()
  11402. .appendTo(fm.getUI())
  11403. .on('mouseenter mouseleave', '.'+item, function() { $(this).toggleClass(hover); })
  11404. .on('click', '.'+item, function(e) {
  11405. var opts = $(this).data('value');
  11406. e.preventDefault();
  11407. e.stopPropagation();
  11408. button.removeClass(hover);
  11409. menu.hide();
  11410. if (typeof opts === 'undefined') {
  11411. opts = {};
  11412. }
  11413. if (typeof opts === 'object') {
  11414. opts._userAction = true;
  11415. }
  11416. fm.exec(cmd.name, getSelected(), opts);
  11417. });
  11418. fm.bind('disable select', hideMenu).getUI().on('click', hideMenu);
  11419. cmd.change(function() {
  11420. menu.html('');
  11421. $.each(cmd.variants, function(i, variant) {
  11422. menu.append($('<div class="'+item+'">'+variant[1]+'</div>').data('value', variant[0]).addClass(variant[0] == cmd.value ? selected : ''));
  11423. });
  11424. });
  11425. }
  11426. cmd.change(function() {
  11427. if (cmd.disabled()) {
  11428. button.removeClass(active+' '+hover).addClass(disabled);
  11429. } else {
  11430. button.removeClass(disabled);
  11431. button[cmd.active() ? 'addClass' : 'removeClass'](active);
  11432. }
  11433. if (cmd.syncTitleOnChange) {
  11434. text.html(cmd.title);
  11435. button.attr('title', cmd.title);
  11436. }
  11437. })
  11438. .change();
  11439. });
  11440. };
  11441. /*
  11442. * File: /js/ui/contextmenu.js
  11443. */
  11444. /**
  11445. * @class elFinder contextmenu
  11446. *
  11447. * @author Dmitry (dio) Levashov
  11448. **/
  11449. $.fn.elfindercontextmenu = function(fm) {
  11450. return this.each(function() {
  11451. var self = $(this),
  11452. cmItem = 'elfinder-contextmenu-item',
  11453. smItem = 'elfinder-contextsubmenu-item',
  11454. exIcon = 'elfinder-contextmenu-extra-icon',
  11455. dragOpt = {
  11456. distance: 8,
  11457. start: function() {
  11458. menu.data('drag', true).data('touching') && menu.find('.ui-state-hover').removeClass('ui-state-hover');
  11459. },
  11460. stop: function() {
  11461. menu.data('draged', true).removeData('drag');
  11462. }
  11463. },
  11464. menu = $(this).addClass('touch-punch ui-helper-reset ui-front ui-widget ui-state-default ui-corner-all elfinder-contextmenu elfinder-contextmenu-'+fm.direction)
  11465. .hide()
  11466. .on('touchstart', function(e) {
  11467. menu.data('touching', true).children().removeClass('ui-state-hover');
  11468. })
  11469. .on('touchend', function(e) {
  11470. menu.removeData('touching');
  11471. })
  11472. .on('mouseenter mouseleave', '.'+cmItem, function(e) {
  11473. $(this).toggleClass('ui-state-hover', (e.type === 'mouseenter' || (! menu.data('draged') && menu.data('submenuKeep'))? true : false));
  11474. if (menu.data('draged') && menu.data('submenuKeep')) {
  11475. menu.find('.elfinder-contextmenu-sub:visible').parent().addClass('ui-state-hover');
  11476. }
  11477. })
  11478. .on('mouseenter mouseleave', '.'+exIcon, function(e) {
  11479. $(this).parent().toggleClass('ui-state-hover', e.type === 'mouseleave');
  11480. })
  11481. .on('mouseenter mouseleave', '.'+cmItem+',.'+smItem, function(e) {
  11482. var setIndex = function(target, sub) {
  11483. $.each(sub? subnodes : nodes, function(i, n) {
  11484. if (target[0] === n) {
  11485. (sub? subnodes : nodes)._cur = i;
  11486. if (sub) {
  11487. subselected = target;
  11488. } else {
  11489. selected = target;
  11490. }
  11491. return false;
  11492. }
  11493. });
  11494. };
  11495. if (e.originalEvent) {
  11496. var target = $(this),
  11497. unHover = function() {
  11498. if (selected && !selected.children('div.elfinder-contextmenu-sub:visible').length) {
  11499. selected.removeClass('ui-state-hover');
  11500. }
  11501. };
  11502. if (e.type === 'mouseenter') {
  11503. // mouseenter
  11504. if (target.hasClass(smItem)) {
  11505. // submenu
  11506. if (subselected) {
  11507. subselected.removeClass('ui-state-hover');
  11508. }
  11509. if (selected) {
  11510. subnodes = selected.find('div.'+smItem);
  11511. }
  11512. setIndex(target, true);
  11513. } else {
  11514. // menu
  11515. unHover();
  11516. setIndex(target);
  11517. }
  11518. } else {
  11519. // mouseleave
  11520. if (target.hasClass(smItem)) {
  11521. //submenu
  11522. subselected = null;
  11523. subnodes = null;
  11524. } else {
  11525. // menu
  11526. unHover();
  11527. (function(sel) {
  11528. setTimeout(function() {
  11529. if (sel === selected) {
  11530. selected = null;
  11531. }
  11532. }, 250);
  11533. })(selected);
  11534. }
  11535. }
  11536. }
  11537. })
  11538. .on('contextmenu', function(){return false;})
  11539. .on('mouseup', function() {
  11540. setTimeout(function() {
  11541. menu.removeData('draged');
  11542. }, 100);
  11543. })
  11544. .draggable(dragOpt),
  11545. subpos = fm.direction == 'ltr' ? 'left' : 'right',
  11546. types = Object.assign({}, fm.options.contextmenu),
  11547. tpl = '<div class="'+cmItem+'{className}"><span class="elfinder-button-icon {icon} elfinder-contextmenu-icon"{style}/><span>{label}</span></div>',
  11548. item = function(label, icon, callback, opts) {
  11549. var className = '',
  11550. style = '',
  11551. iconClass = '';
  11552. if (opts) {
  11553. if (opts.className) {
  11554. className = ' ' + opts.className;
  11555. }
  11556. if (opts.iconClass) {
  11557. iconClass = opts.iconClass;
  11558. icon = '';
  11559. }
  11560. if (opts.iconImg) {
  11561. style = ' style="background:url(\''+fm.escape(opts.iconImg)+'\') 0 0 no-repeat;background-size:contain;"';
  11562. }
  11563. }
  11564. return $(tpl.replace('{icon}', icon ? 'elfinder-button-icon-'+icon : (iconClass? iconClass : ''))
  11565. .replace('{label}', label)
  11566. .replace('{style}', style)
  11567. .replace('{className}', className))
  11568. .on('click', function(e) {
  11569. e.stopPropagation();
  11570. e.preventDefault();
  11571. callback();
  11572. });
  11573. },
  11574. urlIcon = function(iconUrl) {
  11575. return {
  11576. backgroundImage: 'url("'+iconUrl+'")',
  11577. backgroundRepeat: 'no-repeat',
  11578. backgroundSize: 'contain'
  11579. };
  11580. },
  11581. base, cwd,
  11582. nodes, selected, subnodes, subselected, autoSyncStop, subHoverTm,
  11583. autoToggle = function() {
  11584. var evTouchStart = 'touchstart.contextmenuAutoToggle';
  11585. menu.data('hideTm') && clearTimeout(menu.data('hideTm'));
  11586. if (menu.is(':visible')) {
  11587. menu.on('touchstart', function(e) {
  11588. if (e.originalEvent.touches.length > 1) {
  11589. return;
  11590. }
  11591. menu.stop().show();
  11592. menu.data('hideTm') && clearTimeout(menu.data('hideTm'));
  11593. })
  11594. .data('hideTm', setTimeout(function() {
  11595. if (menu.is(':visible')) {
  11596. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  11597. cwd.find('.elfinder-cwd-file.ui-selected')
  11598. .one(evTouchStart, function(e) {
  11599. if (e.originalEvent.touches.length > 1) {
  11600. return;
  11601. }
  11602. var tgt = $(e.target);
  11603. if (menu.first().length && !tgt.is('input:checkbox') && !tgt.hasClass('elfinder-cwd-select')) {
  11604. open(e.originalEvent.touches[0].pageX, e.originalEvent.touches[0].pageY);
  11605. return false;
  11606. }
  11607. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  11608. })
  11609. .one('unselect.'+fm.namespace, function() {
  11610. cwd.find('.elfinder-cwd-file').off(evTouchStart);
  11611. });
  11612. menu.fadeOut({
  11613. duration: 300,
  11614. fail: function() {
  11615. menu.css('opacity', '1').show();
  11616. }
  11617. });
  11618. }
  11619. }, 4500));
  11620. }
  11621. },
  11622. keyEvts = function(e) {
  11623. var code = e.keyCode,
  11624. ESC = $.ui.keyCode.ESCAPE,
  11625. ENT = $.ui.keyCode.ENTER,
  11626. LEFT = $.ui.keyCode.LEFT,
  11627. RIGHT = $.ui.keyCode.RIGHT,
  11628. UP = $.ui.keyCode.UP,
  11629. DOWN = $.ui.keyCode.DOWN,
  11630. subent = fm.direction === 'ltr'? RIGHT : LEFT,
  11631. sublev = subent === RIGHT? LEFT : RIGHT;
  11632. if ($.inArray(code, [ESC, ENT, LEFT, RIGHT, UP, DOWN]) !== -1) {
  11633. e.preventDefault();
  11634. e.stopPropagation();
  11635. e.stopImmediatePropagation();
  11636. if (code == ESC || code === sublev) {
  11637. if (selected && subnodes && subselected) {
  11638. subselected.trigger('mouseleave');
  11639. selected.addClass('ui-state-hover');
  11640. subnodes = null;
  11641. subselected = null;
  11642. } else {
  11643. code == ESC && close();
  11644. }
  11645. } else if (code == UP || code == DOWN) {
  11646. if (subnodes) {
  11647. if (subselected) {
  11648. subselected.trigger('mouseleave');
  11649. }
  11650. if (code == DOWN && (! subselected || subnodes.length <= ++subnodes._cur)) {
  11651. subnodes._cur = 0;
  11652. } else if (code == UP && (! subselected || --subnodes._cur < 0)) {
  11653. subnodes._cur = subnodes.length - 1;
  11654. }
  11655. subselected = subnodes.eq(subnodes._cur).trigger('mouseenter');
  11656. } else {
  11657. subnodes = null;
  11658. if (selected) {
  11659. selected.trigger('mouseleave');
  11660. }
  11661. if (code == DOWN && (! selected || nodes.length <= ++nodes._cur)) {
  11662. nodes._cur = 0;
  11663. } else if (code == UP && (! selected || --nodes._cur < 0)) {
  11664. nodes._cur = nodes.length - 1;
  11665. }
  11666. selected = nodes.eq(nodes._cur).addClass('ui-state-hover');
  11667. }
  11668. } else if (selected && (code == ENT || code === subent)) {
  11669. if (selected.hasClass('elfinder-contextmenu-group')) {
  11670. if (subselected) {
  11671. code == ENT && subselected.click();
  11672. } else {
  11673. selected.trigger('mouseenter');
  11674. subnodes = selected.find('div.'+smItem);
  11675. subnodes._cur = 0;
  11676. subselected = subnodes.first().addClass('ui-state-hover');
  11677. }
  11678. } else {
  11679. code == ENT && selected.click();
  11680. }
  11681. }
  11682. }
  11683. },
  11684. open = function(x, y, css) {
  11685. var width = menu.outerWidth(),
  11686. height = menu.outerHeight(),
  11687. bstyle = base.attr('style'),
  11688. bpos = base.offset(),
  11689. bwidth = base.width(),
  11690. bheight = base.height(),
  11691. mw = fm.UA.Mobile? 40 : 2,
  11692. mh = fm.UA.Mobile? 20 : 2,
  11693. x = x - (bpos? bpos.left : 0),
  11694. y = y - (bpos? bpos.top : 0),
  11695. css = Object.assign(css || {}, {
  11696. top : Math.max(0, y + mh + height < bheight ? y + mh : y - (y + height - bheight)),
  11697. left : Math.max(0, (x < width + mw || x + mw + width < bwidth)? x + mw : x - mw - width),
  11698. opacity : '1'
  11699. }),
  11700. evts;
  11701. autoSyncStop = true;
  11702. fm.autoSync('stop');
  11703. base.width(bwidth);
  11704. menu.stop().removeAttr('style').css(css);
  11705. fm.toFront(menu);
  11706. menu.show();
  11707. base.attr('style', bstyle);
  11708. css[subpos] = parseInt(menu.width());
  11709. menu.find('.elfinder-contextmenu-sub').css(css);
  11710. if (fm.UA.iOS) {
  11711. $('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'auto');
  11712. }
  11713. selected = null;
  11714. subnodes = null;
  11715. subselected = null;
  11716. $(document).on('keydown.' + fm.namespace, keyEvts);
  11717. evts = $._data(document).events;
  11718. if (evts && evts.keydown) {
  11719. evts.keydown.unshift(evts.keydown.pop());
  11720. }
  11721. fm.UA.Mobile && autoToggle();
  11722. setTimeout(function() {
  11723. fm.getUI().one('click.' + fm.namespace, close);
  11724. }, 0);
  11725. },
  11726. close = function() {
  11727. fm.getUI().off('click.' + fm.namespace, close);
  11728. $(document).off('keydown.' + fm.namespace, keyEvts);
  11729. if (prevSelected) {
  11730. fm.select({selected: prevSelected});
  11731. prevSelected = null;
  11732. }
  11733. currentType = currentTargets = null;
  11734. if (menu.is(':visible') || menu.children().length) {
  11735. menu.removeAttr('style').hide().empty().removeData('submenuKeep');
  11736. try {
  11737. if (! menu.draggable('instance')) {
  11738. menu.draggable(dragOpt);
  11739. }
  11740. } catch(e) {
  11741. if (! menu.hasClass('ui-draggable')) {
  11742. menu.draggable(dragOpt);
  11743. }
  11744. }
  11745. if (menu.data('prevNode')) {
  11746. menu.data('prevNode').after(menu);
  11747. menu.removeData('prevNode');
  11748. }
  11749. fm.trigger('closecontextmenu');
  11750. if (fm.UA.iOS) {
  11751. $('div.elfinder div.overflow-scrolling-touch').css('-webkit-overflow-scrolling', 'touch');
  11752. }
  11753. }
  11754. autoSyncStop && fm.searchStatus.state < 1 && ! fm.searchStatus.ininc && fm.autoSync();
  11755. autoSyncStop = false;
  11756. },
  11757. create = function(type, targets) {
  11758. var sep = false,
  11759. insSep = false,
  11760. disabled = [],
  11761. isCwd = type === 'cwd',
  11762. selcnt = 0,
  11763. cmdMap;
  11764. currentType = type;
  11765. currentTargets = targets;
  11766. // get current uiCmdMap option
  11767. if (!(cmdMap = fm.option('uiCmdMap', isCwd? void(0) : targets[0]))) {
  11768. cmdMap = {};
  11769. }
  11770. if (!isCwd) {
  11771. disabled = fm.getDisabledCmds(targets);
  11772. }
  11773. if (type === 'navbar') {
  11774. prevSelected = fm.selected();
  11775. fm.select({selected: targets, origin: 'navbar'});
  11776. }
  11777. selcnt = fm.selected().length;
  11778. if (selcnt > 1) {
  11779. menu.append('<div class="ui-corner-top ui-widget-header elfinder-contextmenu-header"><span>'
  11780. + fm.i18n('selectedItems', ''+selcnt)
  11781. + '</span></div>');
  11782. }
  11783. nodes = $();
  11784. $.each(types[type]||[], function(i, name) {
  11785. var cmd, cmdName, useMap, node, submenu, hover;
  11786. if (name === '|') {
  11787. if (sep) {
  11788. insSep = true;
  11789. }
  11790. return;
  11791. }
  11792. if (cmdMap[name]) {
  11793. cmdName = cmdMap[name];
  11794. useMap = true;
  11795. } else {
  11796. cmdName = name;
  11797. }
  11798. cmd = fm.getCommand(cmdName);
  11799. if (cmd && !isCwd && (!fm.searchStatus.state || !cmd.disableOnSearch)) {
  11800. cmd.__disabled = cmd._disabled;
  11801. cmd._disabled = !(cmd.alwaysEnabled || (fm._commands[cmdName] ? $.inArray(name, disabled) === -1 && (!useMap || !disabled[cmdName]) : false));
  11802. $.each(cmd.linkedCmds, function(i, n) {
  11803. var c;
  11804. if (c = fm.getCommand(n)) {
  11805. c.__disabled = c._disabled;
  11806. c._disabled = !(c.alwaysEnabled || (fm._commands[n] ? !disabled[n] : false));
  11807. }
  11808. });
  11809. }
  11810. if (cmd && !cmd._disabled && cmd.getstate(targets) != -1) {
  11811. if (cmd.variants) {
  11812. if (!cmd.variants.length) {
  11813. return;
  11814. }
  11815. node = item(cmd.title, cmd.className? cmd.className : cmd.name, function(){});
  11816. submenu = $('<div class="ui-front ui-corner-all elfinder-contextmenu-sub"/>')
  11817. .hide()
  11818. .appendTo(node.append('<span class="elfinder-contextmenu-arrow"/>'));
  11819. hover = function(show){
  11820. if (! show) {
  11821. submenu.hide();
  11822. } else {
  11823. var bstyle = base.attr('style');
  11824. base.width(base.width());
  11825. submenu.css({ left: 'auto', right: 'auto' });
  11826. var nodeOffset = node.offset(),
  11827. nodeleft = nodeOffset.left,
  11828. nodetop = nodeOffset.top,
  11829. nodewidth = node.outerWidth(),
  11830. width = submenu.outerWidth(true),
  11831. height = submenu.outerHeight(true),
  11832. baseOffset = base.offset(),
  11833. wwidth = baseOffset.left + base.width(),
  11834. wheight = baseOffset.top + base.height(),
  11835. x, y, over;
  11836. over = (nodeleft + nodewidth + width) - wwidth;
  11837. x = (nodeleft > width && over > 0)? (fm.UA.Mobile? 10 - width : nodewidth - over) : nodewidth;
  11838. if (subpos === 'right' && nodeleft < width) {
  11839. x = fm.UA.Mobile? 30 - nodewidth : nodewidth - (width - nodeleft);
  11840. }
  11841. over = (nodetop + 5 + height) - wheight;
  11842. y = (over > 0 && nodetop < wheight)? 5 - over : (over > 0? 30 - height : 5);
  11843. menu.find('.elfinder-contextmenu-sub:visible').hide();
  11844. submenu.css({ top : y }).css(subpos, x).show();
  11845. base.attr('style', bstyle);
  11846. }
  11847. };
  11848. node.addClass('elfinder-contextmenu-group')
  11849. .on('mouseleave', '.elfinder-contextmenu-sub', function(e) {
  11850. if (! menu.data('draged')) {
  11851. menu.removeData('submenuKeep');
  11852. }
  11853. })
  11854. .on('click', '.'+smItem, function(e){
  11855. var opts, $this;
  11856. e.stopPropagation();
  11857. if (! menu.data('draged')) {
  11858. menu.hide();
  11859. $this = $(this);
  11860. opts = $this.data('exec');
  11861. if (typeof opts === 'undefined') {
  11862. opts = {};
  11863. }
  11864. if (typeof opts === 'object') {
  11865. opts._userAction = true;
  11866. opts._currentType = type;
  11867. opts._currentNode = $this;
  11868. }
  11869. close();
  11870. fm.exec(cmd.name, targets, opts);
  11871. }
  11872. })
  11873. .on('touchend', function(e) {
  11874. if (! menu.data('drag')) {
  11875. hover(true);
  11876. menu.data('submenuKeep', true);
  11877. }
  11878. })
  11879. .on('mouseenter mouseleave', function(e){
  11880. if (! menu.data('touching')) {
  11881. if (node.data('timer')) {
  11882. clearTimeout(node.data('timer'));
  11883. node.removeData('timer');
  11884. }
  11885. if (e.type === 'mouseleave') {
  11886. if (! menu.data('submenuKeep')) {
  11887. node.data('timer', setTimeout(function() {
  11888. node.removeData('timer');
  11889. hover(false);
  11890. }, 250));
  11891. }
  11892. } else {
  11893. node.data('timer', setTimeout(function() {
  11894. node.removeData('timer');
  11895. hover(true);
  11896. }, nodes.find('div.elfinder-contextmenu-sub:visible').length? 250 : 0));
  11897. }
  11898. }
  11899. });
  11900. $.each(cmd.variants, function(i, variant) {
  11901. var item = variant === '|' ? '<div class="elfinder-contextmenu-separator"/>' :
  11902. $('<div class="'+cmItem+' '+smItem+'"><span>'+variant[1]+'</span></div>').data('exec', variant[0]),
  11903. iconClass, icon;
  11904. if (typeof variant[2] !== 'undefined') {
  11905. icon = $('<span/>').addClass('elfinder-button-icon elfinder-contextmenu-icon');
  11906. if (! /\//.test(variant[2])) {
  11907. icon.addClass('elfinder-button-icon-'+variant[2]);
  11908. } else {
  11909. icon.css(urlIcon(variant[2]));
  11910. }
  11911. item.prepend(icon).addClass(smItem+'-icon');
  11912. }
  11913. submenu.append(item);
  11914. });
  11915. } else {
  11916. node = item(cmd.title, cmd.className? cmd.className : cmd.name, function() {
  11917. if (! menu.data('draged')) {
  11918. close();
  11919. fm.exec(cmd.name, targets, {_userAction: true, _currentType: type, _currentNode: node});
  11920. }
  11921. });
  11922. if (cmd.extra && cmd.extra.node) {
  11923. $('<span class="elfinder-button-icon elfinder-button-icon-'+(cmd.extra.icon || '')+' '+exIcon+'"/>')
  11924. .append(cmd.extra.node).appendTo(node);
  11925. $(cmd.extra.node).trigger('ready', {targets: targets});
  11926. } else {
  11927. node.remove('.'+exIcon);
  11928. }
  11929. }
  11930. if (cmd.extendsCmd) {
  11931. node.children('span.elfinder-button-icon').addClass('elfinder-button-icon-' + cmd.extendsCmd);
  11932. }
  11933. if (insSep) {
  11934. menu.append('<div class="elfinder-contextmenu-separator"/>');
  11935. }
  11936. menu.append(node);
  11937. sep = true;
  11938. insSep = false;
  11939. }
  11940. if (cmd && typeof cmd.__disabled !== 'undefined') {
  11941. cmd._disabled = cmd.__disabled;
  11942. delete cmd.__disabled;
  11943. $.each(cmd.linkedCmds, function(i, n) {
  11944. var c;
  11945. if (c = fm.getCommand(n)) {
  11946. c._disabled = c.__disabled;
  11947. delete c.__disabled;
  11948. }
  11949. });
  11950. }
  11951. });
  11952. nodes = menu.children('div.'+cmItem);
  11953. },
  11954. createFromRaw = function(raw) {
  11955. currentType = 'raw';
  11956. $.each(raw, function(i, data) {
  11957. var node;
  11958. if (data === '|') {
  11959. menu.append('<div class="elfinder-contextmenu-separator"/>');
  11960. } else if (data.label && typeof data.callback == 'function') {
  11961. node = item(data.label, data.icon, function() {
  11962. if (! menu.data('draged')) {
  11963. !data.remain && close();
  11964. data.callback();
  11965. }
  11966. }, data.options || null);
  11967. menu.append(node);
  11968. }
  11969. });
  11970. nodes = menu.children('div.'+cmItem);
  11971. },
  11972. currentType = null,
  11973. currentTargets = null,
  11974. prevSelected = null;
  11975. fm.one('load', function() {
  11976. base = fm.getUI();
  11977. cwd = fm.getUI('cwd');
  11978. fm.bind('contextmenu', function(e) {
  11979. var data = e.data,
  11980. css = {},
  11981. prevNode;
  11982. if (data.type && data.type !== 'files') {
  11983. cwd.trigger('unselectall');
  11984. }
  11985. close();
  11986. if (data.type && data.targets) {
  11987. fm.trigger('contextmenucreate', data);
  11988. create(data.type, data.targets);
  11989. fm.trigger('contextmenucreatedone', data);
  11990. } else if (data.raw) {
  11991. createFromRaw(data.raw);
  11992. }
  11993. if (menu.children().length) {
  11994. prevNode = data.prevNode || null;
  11995. if (prevNode) {
  11996. menu.data('prevNode', menu.prev());
  11997. prevNode.after(menu);
  11998. }
  11999. if (data.fitHeight) {
  12000. css = {maxHeight: Math.min(fm.getUI().height(), $(window).height()), overflowY: 'auto'};
  12001. menu.draggable('destroy').removeClass('ui-draggable');
  12002. }
  12003. open(data.x, data.y, css);
  12004. // call opened callback function
  12005. if (data.opened && typeof data.opened === 'function') {
  12006. data.opened.call(menu);
  12007. }
  12008. }
  12009. })
  12010. .one('destroy', function() { menu.remove(); })
  12011. .bind('disable', close)
  12012. .bind('select', function(e){
  12013. (currentType === 'files' && (!e.data || e.data.selected.toString() !== currentTargets.toString())) && close();
  12014. });
  12015. })
  12016. .shortcut({
  12017. pattern : fm.OS === 'mac' ? 'ctrl+m' : 'contextmenu shift+f10',
  12018. description : 'contextmenu',
  12019. callback : function(e) {
  12020. e.stopPropagation();
  12021. e.preventDefault();
  12022. $(document).one('contextmenu.' + fm.namespace, function(e) {
  12023. e.preventDefault();
  12024. e.stopPropagation();
  12025. });
  12026. var sel = fm.selected(),
  12027. type, targets, pos, elm;
  12028. if (sel.length) {
  12029. type = 'files';
  12030. targets = sel;
  12031. elm = $('#'+fm.cwdHash2Id(sel[0]));
  12032. } else {
  12033. type = 'cwd';
  12034. targets = [ fm.cwd().hash ];
  12035. pos = fm.getUI('workzone').offset();
  12036. }
  12037. if (! elm || ! elm.length) {
  12038. elm = fm.getUI('workzone');
  12039. }
  12040. pos = elm.offset();
  12041. pos.top += (elm.height() / 2);
  12042. pos.left += (elm.width() / 2);
  12043. fm.trigger('contextmenu', {
  12044. 'type' : type,
  12045. 'targets' : targets,
  12046. 'x' : pos.left,
  12047. 'y' : pos.top
  12048. });
  12049. }
  12050. });
  12051. });
  12052. };
  12053. /*
  12054. * File: /js/ui/cwd.js
  12055. */
  12056. /**
  12057. * elFinder current working directory ui.
  12058. *
  12059. * @author Dmitry (dio) Levashov
  12060. **/
  12061. $.fn.elfindercwd = function(fm, options) {
  12062. this.not('.elfinder-cwd').each(function() {
  12063. // fm.time('cwdLoad');
  12064. var mobile = fm.UA.Mobile,
  12065. list = fm.viewType == 'list',
  12066. undef = 'undefined',
  12067. /**
  12068. * Select event full name
  12069. *
  12070. * @type String
  12071. **/
  12072. evtSelect = 'select.'+fm.namespace,
  12073. /**
  12074. * Unselect event full name
  12075. *
  12076. * @type String
  12077. **/
  12078. evtUnselect = 'unselect.'+fm.namespace,
  12079. /**
  12080. * Disable event full name
  12081. *
  12082. * @type String
  12083. **/
  12084. evtDisable = 'disable.'+fm.namespace,
  12085. /**
  12086. * Disable event full name
  12087. *
  12088. * @type String
  12089. **/
  12090. evtEnable = 'enable.'+fm.namespace,
  12091. c = 'class',
  12092. /**
  12093. * File css class
  12094. *
  12095. * @type String
  12096. **/
  12097. clFile = fm.res(c, 'cwdfile'),
  12098. /**
  12099. * Selected css class
  12100. *
  12101. * @type String
  12102. **/
  12103. fileSelector = '.'+clFile + (options.oldSchool? ':not(.elfinder-cwd-parent)' : ''),
  12104. /**
  12105. * Selected css class
  12106. *
  12107. * @type String
  12108. **/
  12109. clSelected = 'ui-selected',
  12110. /**
  12111. * Disabled css class
  12112. *
  12113. * @type String
  12114. **/
  12115. clDisabled = fm.res(c, 'disabled'),
  12116. /**
  12117. * Draggable css class
  12118. *
  12119. * @type String
  12120. **/
  12121. clDraggable = fm.res(c, 'draggable'),
  12122. /**
  12123. * Droppable css class
  12124. *
  12125. * @type String
  12126. **/
  12127. clDroppable = fm.res(c, 'droppable'),
  12128. /**
  12129. * Hover css class
  12130. *
  12131. * @type String
  12132. **/
  12133. clHover = fm.res(c, 'hover'),
  12134. /**
  12135. * Hover css class
  12136. *
  12137. * @type String
  12138. **/
  12139. clDropActive = fm.res(c, 'adroppable'),
  12140. /**
  12141. * Css class for temporary nodes (for mkdir/mkfile) commands
  12142. *
  12143. * @type String
  12144. **/
  12145. clTmp = clFile+'-tmp',
  12146. /**
  12147. * Number of thumbnails to load in one request (new api only)
  12148. *
  12149. * @type Number
  12150. **/
  12151. tmbNum = fm.options.loadTmbs > 0 ? fm.options.loadTmbs : 5,
  12152. /**
  12153. * Current search query.
  12154. *
  12155. * @type String
  12156. */
  12157. query = '',
  12158. /**
  12159. * Currect clipboard(cut) hashes as object key
  12160. *
  12161. * @type Object
  12162. */
  12163. clipCuts = {},
  12164. /**
  12165. * Parents hashes of cwd
  12166. *
  12167. * @type Array
  12168. */
  12169. cwdParents = [],
  12170. /**
  12171. * cwd current hashes
  12172. *
  12173. * @type Array
  12174. */
  12175. cwdHashes = [],
  12176. /**
  12177. * incsearch current hashes
  12178. *
  12179. * @type Array
  12180. */
  12181. incHashes = void 0,
  12182. /**
  12183. * Custom columns name and order
  12184. *
  12185. * @type Array
  12186. */
  12187. customCols = [],
  12188. /**
  12189. * Current clicked element id of first time for dblclick
  12190. *
  12191. * @type String
  12192. */
  12193. curClickId = '',
  12194. /**
  12195. * Custom columns builder
  12196. *
  12197. * @type Function
  12198. */
  12199. customColsBuild = function() {
  12200. var cols = '';
  12201. for (var i = 0; i < customCols.length; i++) {
  12202. cols += '<td class="elfinder-col-'+customCols[i]+'">{' + customCols[i] + '}</td>';
  12203. }
  12204. return cols;
  12205. },
  12206. /**
  12207. * Make template.row from customCols
  12208. *
  12209. * @type Function
  12210. */
  12211. makeTemplateRow = function() {
  12212. 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>';
  12213. },
  12214. 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"></div>' : '',
  12215. colResizing = false,
  12216. colWidth = null,
  12217. /**
  12218. * File templates
  12219. *
  12220. * @type Object
  12221. **/
  12222. templates = {
  12223. 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>',
  12224. row : ''
  12225. },
  12226. permsTpl = fm.res('tpl', 'perms'),
  12227. lockTpl = fm.res('tpl', 'lock'),
  12228. symlinkTpl = fm.res('tpl', 'symlink'),
  12229. /**
  12230. * Template placeholders replacement rules
  12231. *
  12232. * @type Object
  12233. **/
  12234. replacement = {
  12235. id : function(f) {
  12236. return fm.cwdHash2Id(f.hash);
  12237. },
  12238. name : function(f) {
  12239. var name = fm.escape(f.i18 || f.name);
  12240. !list && (name = name.replace(/([_.])/g, '&#8203;$1'));
  12241. return name;
  12242. },
  12243. nametitle : function(f) {
  12244. return fm.escape(f.i18 || f.name);
  12245. },
  12246. permsclass : function(f) {
  12247. return fm.perms2class(f);
  12248. },
  12249. perm : function(f) {
  12250. return fm.formatPermissions(f);
  12251. },
  12252. dirclass : function(f) {
  12253. var cName = f.mime == 'directory' ? 'directory' : '';
  12254. f.isroot && (cName += ' isroot');
  12255. f.csscls && (cName += ' ' + fm.escape(f.csscls));
  12256. options.getClass && (cName += ' ' + options.getClass(f));
  12257. return cName;
  12258. },
  12259. style : function(f) {
  12260. return f.icon? fm.getIconStyle(f) : '';
  12261. },
  12262. mime : function(f) {
  12263. return fm.mime2class(f.mime);
  12264. },
  12265. size : function(f) {
  12266. return (f.mime === 'directory' && !f.size)? '-' : fm.formatSize(f.size);
  12267. },
  12268. date : function(f) {
  12269. return fm.formatDate(f);
  12270. },
  12271. kind : function(f) {
  12272. return fm.mime2kind(f);
  12273. },
  12274. mode : function(f) {
  12275. return f.perm? fm.formatFileMode(f.perm) : '';
  12276. },
  12277. modestr : function(f) {
  12278. return f.perm? fm.formatFileMode(f.perm, 'string') : '';
  12279. },
  12280. modeoct : function(f) {
  12281. return f.perm? fm.formatFileMode(f.perm, 'octal') : '';
  12282. },
  12283. modeboth : function(f) {
  12284. return f.perm? fm.formatFileMode(f.perm, 'both') : '';
  12285. },
  12286. marker : function(f) {
  12287. return (f.alias || f.mime == 'symlink-broken' ? symlinkTpl : '')+(!f.read || !f.write ? permsTpl : '')+(f.locked ? lockTpl : '');
  12288. },
  12289. tooltip : function(f) {
  12290. var title = fm.formatDate(f) + (f.size > 0 ? ' ('+fm.formatSize(f.size)+')' : ''),
  12291. info = '';
  12292. if (query && f.path) {
  12293. info = fm.escape(f.path.replace(/\/[^\/]*$/, ''));
  12294. } else {
  12295. info = f.tooltip? fm.escape(f.tooltip).replace(/\r/g, '&#13;') : '';
  12296. }
  12297. if (list) {
  12298. info += (info? '&#13;' : '') + fm.escape(f.i18 || f.name);
  12299. }
  12300. return info? info + '&#13;' + title : title;
  12301. }
  12302. },
  12303. /**
  12304. * Type badge CSS added flag
  12305. *
  12306. * @type Object
  12307. */
  12308. addedBadges = {},
  12309. /**
  12310. * Type badge style sheet element
  12311. *
  12312. * @type Object
  12313. */
  12314. addBadgeStyleSheet,
  12315. /**
  12316. * Add type badge CSS into 'head'
  12317. *
  12318. * @type Fundtion
  12319. */
  12320. addBadgeStyle = function(mime, name) {
  12321. var sel, ext, type;
  12322. if (mime && ! addedBadges[mime]) {
  12323. if (typeof addBadgeStyleSheet === 'undefined') {
  12324. if ($('#elfinderAddBadgeStyle'+fm.namespace).length) {
  12325. $('#elfinderAddBadgeStyle'+fm.namespace).remove();
  12326. }
  12327. addBadgeStyleSheet = $('<style id="addBadgeStyle'+fm.namespace+'"/>').insertBefore($('head').children(':first')).get(0).sheet || null;
  12328. }
  12329. if (addBadgeStyleSheet) {
  12330. mime = mime.toLowerCase();
  12331. type = mime.split('/');
  12332. ext = fm.escape(fm.mimeTypes[mime] || (name.replace(/.bac?k$/i, '').match(/\.([^.]+)$/) || ['',''])[1]);
  12333. if (ext) {
  12334. sel = '.elfinder-cwd-icon-' + type[0].replace(/(\.|\+)/g, '-');
  12335. if (typeof type[1] !== 'undefined') {
  12336. sel += '.elfinder-cwd-icon-' + type[1].replace(/(\.|\+)/g, '-');
  12337. }
  12338. try {
  12339. addBadgeStyleSheet.insertRule(sel + ':before{content:"' + ext.toLowerCase() + '"}', 0);
  12340. } catch(e) {}
  12341. }
  12342. addedBadges[mime] = true;
  12343. }
  12344. }
  12345. },
  12346. /**
  12347. * Return file html
  12348. *
  12349. * @param Object file info
  12350. * @return String
  12351. **/
  12352. itemhtml = function(f) {
  12353. f.mime && f.mime !== 'directory' && !addedBadges[f.mime] && addBadgeStyle(f.mime, f.name);
  12354. return templates[list ? 'row' : 'icon']
  12355. .replace(/\{([a-z0-9_]+)\}/g, function(s, e) {
  12356. return replacement[e] ? replacement[e](f, fm) : (f[e] ? f[e] : '');
  12357. });
  12358. },
  12359. /**
  12360. * jQueery node that will be selected next
  12361. *
  12362. * @type Object jQuery node
  12363. */
  12364. selectedNext = $(),
  12365. /**
  12366. * Flag. Required for msie to avoid unselect files on dragstart
  12367. *
  12368. * @type Boolean
  12369. **/
  12370. selectLock = false,
  12371. /**
  12372. * Move selection to prev/next file
  12373. *
  12374. * @param String move direction
  12375. * @param Boolean append to current selection
  12376. * @return void
  12377. * @rise select
  12378. */
  12379. select = function(keyCode, append) {
  12380. var code = $.ui.keyCode,
  12381. prev = keyCode == code.LEFT || keyCode == code.UP,
  12382. sel = cwd.find('[id].'+clSelected),
  12383. selector = prev ? 'first:' : 'last',
  12384. s, n, sib, top, left;
  12385. function sibling(n, direction) {
  12386. return n[direction+'All']('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):first');
  12387. }
  12388. if (sel.length) {
  12389. s = sel.filter(prev ? ':first' : ':last');
  12390. sib = sibling(s, prev ? 'prev' : 'next');
  12391. if (!sib.length) {
  12392. // there is no sibling on required side - do not move selection
  12393. n = s;
  12394. } else if (list || keyCode == code.LEFT || keyCode == code.RIGHT) {
  12395. // find real prevoius file
  12396. n = sib;
  12397. } else {
  12398. // find up/down side file in icons view
  12399. top = s.position().top;
  12400. left = s.position().left;
  12401. n = s;
  12402. if (prev) {
  12403. do {
  12404. n = n.prev('[id]');
  12405. } while (n.length && !(n.position().top < top && n.position().left <= left));
  12406. if (n.hasClass(clDisabled)) {
  12407. n = sibling(n, 'next');
  12408. }
  12409. } else {
  12410. do {
  12411. n = n.next('[id]');
  12412. } while (n.length && !(n.position().top > top && n.position().left >= left));
  12413. if (n.hasClass(clDisabled)) {
  12414. n = sibling(n, 'prev');
  12415. }
  12416. // there is row before last one - select last file
  12417. if (!n.length) {
  12418. sib = cwd.find('[id]:not(.'+clDisabled+'):last');
  12419. if (sib.position().top > top) {
  12420. n = sib;
  12421. }
  12422. }
  12423. }
  12424. }
  12425. // !append && unselectAll();
  12426. } else {
  12427. if (selectedNext.length) {
  12428. n = prev? selectedNext.prev() : selectedNext;
  12429. } else {
  12430. // there are no selected file - select first/last one
  12431. n = cwd.find('[id]:not(.'+clDisabled+'):not(.elfinder-cwd-parent):'+(prev ? 'last' : 'first'));
  12432. }
  12433. }
  12434. if (n && n.length && !n.hasClass('elfinder-cwd-parent')) {
  12435. if (s && append) {
  12436. // append new files to selected
  12437. n = s.add(s[prev ? 'prevUntil' : 'nextUntil']('#'+n.attr('id'))).add(n);
  12438. } else {
  12439. // unselect selected files
  12440. sel.trigger(evtUnselect);
  12441. }
  12442. // select file(s)
  12443. n.trigger(evtSelect);
  12444. // set its visible
  12445. scrollToView(n.filter(prev ? ':first' : ':last'));
  12446. // update cache/view
  12447. trigger();
  12448. }
  12449. },
  12450. selectedFiles = {},
  12451. selectFile = function(hash) {
  12452. $('#'+fm.cwdHash2Id(hash)).trigger(evtSelect);
  12453. },
  12454. allSelected = false,
  12455. selectAll = function() {
  12456. var phash = fm.cwd().hash;
  12457. selectCheckbox && selectAllCheckbox.find('input').prop('checked', true);
  12458. fm.lazy(function() {
  12459. var files;
  12460. cwd.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').trigger(evtSelect);
  12461. if (fm.maxTargets && (incHashes || cwdHashes).length > fm.maxTargets) {
  12462. files = $.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null; });
  12463. files = files.slice(0, fm.maxTargets);
  12464. selectedFiles = {};
  12465. $.each(files, function(i, v) {
  12466. selectedFiles[v.hash] = true;
  12467. });
  12468. fm.toast({mode: 'warning', msg: fm.i18n(['errMaxTargets', fm.maxTargets])});
  12469. } else {
  12470. selectedFiles = fm.arrayFlip(incHashes || cwdHashes, true);
  12471. }
  12472. trigger();
  12473. selectCheckbox && selectAllCheckbox.data('pending', false);
  12474. }, 0, {repaint: true});
  12475. },
  12476. /**
  12477. * Unselect all files
  12478. *
  12479. * @param Object options
  12480. * @return void
  12481. */
  12482. unselectAll = function(opts) {
  12483. var o = opts || {};
  12484. selectCheckbox && selectAllCheckbox.find('input').prop('checked', false);
  12485. if (Object.keys(selectedFiles).length) {
  12486. selectLock = false;
  12487. selectedFiles = {};
  12488. cwd.find('[id].'+clSelected).trigger(evtUnselect);
  12489. selectCheckbox && cwd.find('input:checkbox').prop('checked', false);
  12490. }
  12491. !o.notrigger && trigger();
  12492. selectCheckbox && selectAllCheckbox.data('pending', false);
  12493. cwd.removeClass('elfinder-cwd-allselected');
  12494. },
  12495. selectInvert = function() {
  12496. var invHashes = {};
  12497. if (allSelected) {
  12498. unselectAll();
  12499. } else if (! Object.keys(selectedFiles).length) {
  12500. selectAll();
  12501. } else {
  12502. $.each((incHashes || cwdHashes), function(i, h) {
  12503. var itemNode = $('#'+fm.cwdHash2Id(h));
  12504. if (! selectedFiles[h]) {
  12505. invHashes[h] = true;
  12506. itemNode.length && itemNode.trigger(evtSelect);
  12507. } else {
  12508. itemNode.length && itemNode.trigger(evtUnselect);
  12509. }
  12510. });
  12511. selectedFiles = invHashes;
  12512. trigger();
  12513. }
  12514. },
  12515. /**
  12516. * Return selected files hashes list
  12517. *
  12518. * @return Array
  12519. */
  12520. selected = function() {
  12521. return Object.keys(selectedFiles);
  12522. },
  12523. /**
  12524. * Last selected node id
  12525. *
  12526. * @type String|Void
  12527. */
  12528. lastSelect = void 0,
  12529. /**
  12530. * Fire elfinder "select" event and pass selected files to it
  12531. *
  12532. * @return void
  12533. */
  12534. trigger = function() {
  12535. var selected = Object.keys(selectedFiles),
  12536. opts = { selected : selected };
  12537. allSelected = selected.length && (selected.length === (incHashes || cwdHashes).length) && (!fm.maxTargets || selected.length <= fm.maxTargets);
  12538. if (selectCheckbox) {
  12539. selectAllCheckbox.find('input').prop('checked', allSelected);
  12540. cwd[allSelected? 'addClass' : 'removeClass']('elfinder-cwd-allselected');
  12541. }
  12542. if (allSelected) {
  12543. opts.selectall = true;
  12544. } else if (! selected.length) {
  12545. opts.unselectall = true;
  12546. }
  12547. fm.trigger('select', opts);
  12548. },
  12549. /**
  12550. * Scroll file to set it visible
  12551. *
  12552. * @param DOMElement file/dir node
  12553. * @return void
  12554. */
  12555. scrollToView = function(o, blink) {
  12556. if (! o.length) {
  12557. return;
  12558. }
  12559. var ftop = o.position().top,
  12560. fheight = o.outerHeight(true),
  12561. wtop = wrapper.scrollTop(),
  12562. wheight = wrapper.get(0).clientHeight,
  12563. thheight = tableHeader? tableHeader.outerHeight(true) : 0;
  12564. if (ftop + thheight + fheight > wtop + wheight) {
  12565. wrapper.scrollTop(parseInt(ftop + thheight + fheight - wheight));
  12566. } else if (ftop < wtop) {
  12567. wrapper.scrollTop(ftop);
  12568. }
  12569. list && wrapper.scrollLeft(0);
  12570. !!blink && fm.resources.blink(o, 'lookme');
  12571. },
  12572. /**
  12573. * Files we get from server but not show yet
  12574. *
  12575. * @type Array
  12576. **/
  12577. buffer = [],
  12578. /**
  12579. * Extra data of buffer
  12580. *
  12581. * @type Object
  12582. **/
  12583. bufferExt = {},
  12584. /**
  12585. * Return index of elements with required hash in buffer
  12586. *
  12587. * @param String file hash
  12588. * @return Number
  12589. */
  12590. index = function(hash) {
  12591. var l = buffer.length;
  12592. while (l--) {
  12593. if (buffer[l].hash == hash) {
  12594. return l;
  12595. }
  12596. }
  12597. return -1;
  12598. },
  12599. /**
  12600. * Scroll start event name
  12601. *
  12602. * @type String
  12603. **/
  12604. scrollStartEvent = 'elfscrstart',
  12605. /**
  12606. * Scroll stop event name
  12607. *
  12608. * @type String
  12609. **/
  12610. scrollEvent = 'elfscrstop',
  12611. scrolling = false,
  12612. /**
  12613. * jQuery UI selectable option
  12614. *
  12615. * @type Object
  12616. */
  12617. selectableOption = {
  12618. disabled : true,
  12619. filter : '[id]:first',
  12620. stop : trigger,
  12621. delay : 250,
  12622. appendTo : 'body',
  12623. autoRefresh: false,
  12624. selected : function(e, ui) { $(ui.selected).trigger(evtSelect); },
  12625. unselected : function(e, ui) { $(ui.unselected).trigger(evtUnselect); }
  12626. },
  12627. /**
  12628. * hashes of items displayed in current view
  12629. *
  12630. * @type Object ItemHash => DomId
  12631. */
  12632. inViewHashes = {},
  12633. /**
  12634. * Processing when the current view is changed (On open, search, scroll, resize etc.)
  12635. *
  12636. * @return void
  12637. */
  12638. wrapperRepaint = function(init) {
  12639. var selectable = cwd.data('selectable'),
  12640. rec = (function() {
  12641. var wos = wrapper.offset(),
  12642. w = $(window),
  12643. l = wos.left - w.scrollLeft() + (fm.direction === 'ltr'? 30 : wrapper.width() - 30),
  12644. t = wos.top - w.scrollTop() + 10 + (list? bufferExt.itemH || 24 : 0);
  12645. return {left: Math.max(0, Math.round(l)), top: Math.max(0, Math.round(t))};
  12646. })(),
  12647. tgt = $(document.elementFromPoint(rec.left , rec.top)),
  12648. ids = {},
  12649. tmbs = {},
  12650. multi = 5,
  12651. cnt = Math.ceil((bufferExt.hpi? Math.ceil((wz.data('rectangle').height / bufferExt.hpi) * 1.5) : showFiles) / multi),
  12652. chk = function() {
  12653. var id, hash, file, i;
  12654. for (i = 0; i < multi; i++) {
  12655. id = tgt.attr('id');
  12656. if (id) {
  12657. bufferExt.getTmbs = [];
  12658. hash = fm.cwdId2Hash(id);
  12659. inViewHashes[hash] = id;
  12660. // for tmbs
  12661. if (bufferExt.attachTmbs[hash]) {
  12662. tmbs[hash] = bufferExt.attachTmbs[hash];
  12663. }
  12664. // for selectable
  12665. selectable && (ids[id] = true);
  12666. }
  12667. // next node
  12668. tgt = tgt.next();
  12669. if (!tgt.length) {
  12670. break;
  12671. }
  12672. }
  12673. },
  12674. done = function() {
  12675. var idsArr;
  12676. if (cwd.data('selectable')) {
  12677. Object.assign(ids, selectedFiles);
  12678. idsArr = Object.keys(ids);
  12679. if (idsArr.length) {
  12680. selectableOption.filter = '#'+idsArr.join(', #');
  12681. cwd.selectable('enable').selectable('option', {filter : selectableOption.filter}).selectable('refresh');
  12682. }
  12683. }
  12684. if (Object.keys(tmbs).length) {
  12685. bufferExt.getTmbs = [];
  12686. attachThumbnails(tmbs);
  12687. }
  12688. },
  12689. arr, widget;
  12690. inViewHashes = {};
  12691. selectable && cwd.selectable('option', 'disabled');
  12692. if (tgt.length) {
  12693. if (tgt.hasClass('ui-widget')) {
  12694. // serach button etc.
  12695. widget = tgt;
  12696. widget.hide();
  12697. tgt = $(document.elementFromPoint(rec.left , rec.top));
  12698. widget.show();
  12699. }
  12700. if (! tgt.hasClass(clFile)) {
  12701. tgt = tgt.closest(fileSelector);
  12702. }
  12703. }
  12704. if (tgt.length) {
  12705. if (tgt.attr('id')) {
  12706. if (init) {
  12707. for (var i = 0; i < cnt; i++) {
  12708. chk();
  12709. if (! tgt.length) {
  12710. break;
  12711. }
  12712. }
  12713. done();
  12714. } else {
  12715. bufferExt.repaintJob && bufferExt.repaintJob.state() === 'pending' && bufferExt.repaintJob.reject();
  12716. arr = new Array(cnt);
  12717. bufferExt.repaintJob = fm.asyncJob(function() {
  12718. chk();
  12719. if (! tgt.length) {
  12720. done();
  12721. bufferExt.repaintJob && bufferExt.repaintJob.state() === 'pending' && bufferExt.repaintJob.reject();
  12722. }
  12723. }, arr).done(done);
  12724. }
  12725. }
  12726. }
  12727. },
  12728. /**
  12729. * display parent folder with ".." name
  12730. *
  12731. * @param String phash
  12732. * @return void
  12733. */
  12734. oldSchool = function(p) {
  12735. var phash = fm.cwd().p,
  12736. pdir = fm.file(phash) || null,
  12737. set = function(pdir) {
  12738. if (pdir) {
  12739. parent = $(itemhtml($.extend(true, {}, pdir, {name : '..', i18 : '..', mime : 'directory'})))
  12740. .addClass('elfinder-cwd-parent')
  12741. .on('dblclick', function() {
  12742. var hash = fm.cwdId2Hash(this.id);
  12743. fm.trigger('select', {selected : [hash]}).exec('open', hash);
  12744. }
  12745. );
  12746. (list ? parent.children('td:first') : parent).children('.elfinder-cwd-select').remove();
  12747. (list ? cwd.find('tbody') : cwd).prepend(parent);
  12748. }
  12749. };
  12750. if (pdir) {
  12751. set(pdir);
  12752. } else {
  12753. if (fm.getUI('tree').length) {
  12754. fm.one('parents', function() {
  12755. set(fm.file(phash) || null);
  12756. wrapper.trigger(scrollEvent);
  12757. });
  12758. } else {
  12759. fm.request({
  12760. data : {cmd : 'parents', target : fm.cwd().hash},
  12761. preventFail : true
  12762. })
  12763. .done(function(data) {
  12764. set(fm.file(phash) || null);
  12765. wrapper.trigger(scrollEvent);
  12766. });
  12767. }
  12768. }
  12769. },
  12770. showFiles = fm.options.showFiles,
  12771. /**
  12772. * Cwd scroll event handler.
  12773. * Lazy load - append to cwd not shown files
  12774. *
  12775. * @return void
  12776. */
  12777. render = function() {
  12778. if (bufferExt.rendering || (bufferExt.renderd && ! buffer.length)) {
  12779. return;
  12780. }
  12781. var place = (list ? cwd.children('table').children('tbody') : cwd),
  12782. phash,
  12783. chk,
  12784. // created document fragment for jQuery >= 1.12, 2.2, 3.0
  12785. // see Studio-42/elFinder#1544 @ github
  12786. docFlag = $.htmlPrefilter? true : false,
  12787. tempDom = docFlag? $(document.createDocumentFragment()) : $('<div/>'),
  12788. go = function(o){
  12789. var over = o || null,
  12790. html = [],
  12791. dirs = false,
  12792. atmb = {},
  12793. stmb = (fm.option('tmbUrl') === 'self'),
  12794. init = bufferExt.renderd? false : true,
  12795. files, locks, selected;
  12796. files = buffer.splice(0, showFiles + (over || 0) / (bufferExt.hpi || 1));
  12797. bufferExt.renderd += files.length;
  12798. if (! buffer.length) {
  12799. bottomMarker.hide();
  12800. wrapper.off(scrollEvent, render);
  12801. }
  12802. locks = [];
  12803. html = $.map(files, function(f) {
  12804. if (f.hash && f.name) {
  12805. if (f.mime == 'directory') {
  12806. dirs = true;
  12807. }
  12808. if (f.tmb || (stmb && f.mime.indexOf('image/') === 0)) {
  12809. atmb[f.hash] = f.tmb;
  12810. }
  12811. clipCuts[f.hash] && locks.push(f.hash);
  12812. return itemhtml(f);
  12813. }
  12814. return null;
  12815. });
  12816. // html into temp node
  12817. tempDom.empty().append(html.join(''));
  12818. // make directory droppable
  12819. dirs && !mobile && makeDroppable(tempDom);
  12820. // check selected items
  12821. selected = [];
  12822. if (Object.keys(selectedFiles).length) {
  12823. tempDom.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').each(function() {
  12824. selectedFiles[fm.cwdId2Hash(this.id)] && selected.push($(this));
  12825. });
  12826. }
  12827. // append to cwd
  12828. place.append(docFlag? tempDom : tempDom.children());
  12829. // trigger select
  12830. if (selected.length) {
  12831. $.each(selected, function(i, n) { n.trigger(evtSelect); });
  12832. trigger();
  12833. }
  12834. locks.length && fm.trigger('lockfiles', {files: locks});
  12835. !bufferExt.hpi && bottomMarkerShow(place, files.length);
  12836. if (list) {
  12837. // show thead
  12838. cwd.find('thead').show();
  12839. // fixed table header
  12840. fixTableHeader({fitWidth: ! colWidth});
  12841. }
  12842. if (Object.keys(atmb).length) {
  12843. Object.assign(bufferExt.attachTmbs, atmb);
  12844. }
  12845. if (init) {
  12846. if (! mobile && ! cwd.data('selectable')) {
  12847. // make files selectable
  12848. cwd.selectable(selectableOption).data('selectable', true);
  12849. }
  12850. wrapperRepaint(true);
  12851. }
  12852. ! scrolling && wrapper.trigger(scrollEvent);
  12853. };
  12854. if (! bufferExt.renderd) {
  12855. // first time to go()
  12856. bufferExt.rendering = true;
  12857. // scroll top on dir load to avoid scroll after page reload
  12858. wrapper.scrollTop(0);
  12859. phash = fm.cwd().phash;
  12860. go();
  12861. if (options.oldSchool && phash && !query) {
  12862. oldSchool(phash);
  12863. }
  12864. if (list) {
  12865. colWidth && setColwidth();
  12866. fixTableHeader({fitWidth: true});
  12867. }
  12868. bufferExt.itemH = (list? place.find('tr:first') : place.find('[id]:first')).outerHeight(true);
  12869. fm.trigger('cwdrender');
  12870. bufferExt.rendering = false;
  12871. }
  12872. if (! bufferExt.rendering && buffer.length) {
  12873. // next go()
  12874. if ((chk = (wrapper.height() + wrapper.scrollTop() + fm.options.showThreshold + bufferExt.row) - (bufferExt.renderd * bufferExt.hpi)) > 0) {
  12875. bufferExt.rendering = true;
  12876. fm.lazy(function() {
  12877. go(chk);
  12878. bufferExt.rendering = false;
  12879. });
  12880. }
  12881. }
  12882. },
  12883. // fixed table header jQuery object
  12884. tableHeader = null,
  12885. // To fixed table header colmun
  12886. fixTableHeader = function(optsArg) {
  12887. if (! options.listView.fixedHeader) {
  12888. return;
  12889. }
  12890. var setPos = function() {
  12891. var val;
  12892. val = (fm.direction === 'ltr')? wrapper.scrollLeft() * -1 : table.outerWidth(true) - wrapper.width() - wrapper.scrollLeft();
  12893. if (base.css('left') !== val) {
  12894. base.css('left', val);
  12895. }
  12896. },
  12897. opts = optsArg || {},
  12898. cnt, base, table, thead, tbody, hheight, htr, btr, htd, btd, htw, btw, init;
  12899. tbody = cwd.find('tbody');
  12900. btr = tbody.children('tr:first');
  12901. if (btr.length) {
  12902. table = tbody.parent();
  12903. if (! tableHeader) {
  12904. init = true;
  12905. tbody.addClass('elfinder-cwd-fixheader');
  12906. thead = cwd.find('thead').attr('id', fm.namespace+'-cwd-thead');
  12907. htr = thead.children('tr:first');
  12908. hheight = htr.outerHeight(true);
  12909. cwd.css('margin-top', hheight - parseInt(table.css('padding-top')));
  12910. base = $('<div/>').addClass(cwd.attr('class')).append($('<table/>').append(thead));
  12911. tableHeader = $('<div/>').addClass(wrapper.attr('class') + ' elfinder-cwd-fixheader')
  12912. .removeClass('ui-droppable native-droppable')
  12913. .css(wrapper.position())
  12914. .css({ height: hheight, width: cwd.outerWidth() })
  12915. .append(base);
  12916. if (fm.direction === 'rtl') {
  12917. tableHeader.css('left', (wrapper.data('width') - wrapper.width()) + 'px');
  12918. }
  12919. setPos();
  12920. wrapper.after(tableHeader)
  12921. .on('scroll.fixheader resize.fixheader', function(e) {
  12922. setPos();
  12923. if (e.type === 'resize') {
  12924. e.stopPropagation();
  12925. tableHeader.css(wrapper.position());
  12926. wrapper.data('width', wrapper.css('overflow', 'hidden').width());
  12927. wrapper.css('overflow', 'auto');
  12928. fixTableHeader();
  12929. }
  12930. });
  12931. } else {
  12932. thead = $('#'+fm.namespace+'-cwd-thead');
  12933. htr = thead.children('tr:first');
  12934. }
  12935. if (init || opts.fitWidth || Math.abs(btr.outerWidth() - htr.outerWidth()) > 2) {
  12936. cnt = customCols.length + 1;
  12937. for (var i = 0; i < cnt; i++) {
  12938. htd = htr.children('td:eq('+i+')');
  12939. btd = btr.children('td:eq('+i+')');
  12940. htw = htd.width();
  12941. btw = btd.width();
  12942. if (typeof htd.data('delta') === 'undefined') {
  12943. htd.data('delta', (htd.outerWidth() - htw) - (btd.outerWidth() - btw));
  12944. }
  12945. btw -= htd.data('delta');
  12946. if (! init && ! opts.fitWidth && htw === btw) {
  12947. break;
  12948. }
  12949. htd.css('width', btw + 'px');
  12950. }
  12951. }
  12952. tableHeader.data('widthTimer') && clearTimeout(tableHeader.data('widthTimer'));
  12953. tableHeader.data('widthTimer', setTimeout(function() {
  12954. if (tableHeader) {
  12955. tableHeader.css('width', cwd.outerWidth() + 'px');
  12956. if (fm.direction === 'rtl') {
  12957. tableHeader.css('left', (wrapper.data('width') - wrapper.width()) + 'px');
  12958. }
  12959. }
  12960. }, 10));
  12961. }
  12962. },
  12963. // Set colmun width
  12964. setColwidth = function() {
  12965. if (list && colWidth) {
  12966. var cl = 'elfinder-cwd-colwidth',
  12967. first = cwd.find('tr[id]:first'),
  12968. former;
  12969. if (! first.hasClass(cl)) {
  12970. former = cwd.find('tr.'+cl);
  12971. former.removeClass(cl).find('td').css('width', '');
  12972. first.addClass(cl);
  12973. cwd.find('table:first').css('table-layout', 'fixed');
  12974. $.each($.merge(['name'], customCols), function(i, k) {
  12975. var w = colWidth[k] || first.find('td.elfinder-col-'+k).width();
  12976. first.find('td.elfinder-col-'+k).width(w);
  12977. });
  12978. }
  12979. }
  12980. },
  12981. /**
  12982. * Droppable options for cwd.
  12983. * Drop target is `wrapper`
  12984. * Do not add class on childs file over
  12985. *
  12986. * @type Object
  12987. */
  12988. droppable = Object.assign({}, fm.droppable, {
  12989. over : function(e, ui) {
  12990. var dst = $(this),
  12991. helper = ui.helper,
  12992. ctr = (e.shiftKey || e.ctrlKey || e.metaKey),
  12993. hash, status, inParent;
  12994. e.stopPropagation();
  12995. helper.data('dropover', helper.data('dropover') + 1);
  12996. dst.data('dropover', true);
  12997. if (helper.data('namespace') !== fm.namespace || ! fm.insideWorkzone(e.pageX, e.pageY)) {
  12998. dst.removeClass(clDropActive);
  12999. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  13000. return;
  13001. }
  13002. if (dst.hasClass(fm.res(c, 'cwdfile'))) {
  13003. hash = fm.cwdId2Hash(dst.attr('id'));
  13004. dst.data('dropover', hash);
  13005. } else {
  13006. hash = fm.cwd().hash;
  13007. fm.cwd().write && dst.data('dropover', hash);
  13008. }
  13009. inParent = (fm.file(helper.data('files')[0]).phash === hash);
  13010. if (dst.data('dropover') === hash) {
  13011. $.each(helper.data('files'), function(i, h) {
  13012. if (h === hash || (inParent && !ctr && !helper.hasClass('elfinder-drag-helper-plus'))) {
  13013. dst.removeClass(clDropActive);
  13014. return false; // break $.each
  13015. }
  13016. });
  13017. } else {
  13018. dst.removeClass(clDropActive);
  13019. }
  13020. if (helper.data('locked') || inParent) {
  13021. status = 'elfinder-drag-helper-plus';
  13022. } else {
  13023. status = 'elfinder-drag-helper-move';
  13024. if (ctr) {
  13025. status += ' elfinder-drag-helper-plus';
  13026. }
  13027. }
  13028. dst.hasClass(clDropActive) && helper.addClass(status);
  13029. setTimeout(function(){ dst.hasClass(clDropActive) && helper.addClass(status); }, 20);
  13030. },
  13031. out : function(e, ui) {
  13032. var helper = ui.helper;
  13033. e.stopPropagation();
  13034. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  13035. $(this).removeData('dropover')
  13036. .removeClass(clDropActive);
  13037. },
  13038. deactivate : function() {
  13039. $(this).removeData('dropover')
  13040. .removeClass(clDropActive);
  13041. },
  13042. drop : function(e, ui) {
  13043. unselectAll({ notrigger: true });
  13044. fm.droppable.drop.call(this, e, ui);
  13045. }
  13046. }),
  13047. /**
  13048. * Make directory droppable
  13049. *
  13050. * @return void
  13051. */
  13052. makeDroppable = function(place) {
  13053. place = place? place : (list ? cwd.find('tbody') : cwd);
  13054. var targets = place.children('.directory:not(.'+clDroppable+',.elfinder-na,.elfinder-ro)');
  13055. if (fm.isCommandEnabled('paste')) {
  13056. targets.droppable(droppable);
  13057. }
  13058. if (fm.isCommandEnabled('upload')) {
  13059. targets.addClass('native-droppable');
  13060. }
  13061. place.children('.isroot').each(function(i, n) {
  13062. var $n = $(n),
  13063. hash = fm.cwdId2Hash(n.id);
  13064. if (fm.isCommandEnabled('paste', hash)) {
  13065. if (! $n.hasClass(clDroppable+',elfinder-na,elfinder-ro')) {
  13066. $n.droppable(droppable);
  13067. }
  13068. } else {
  13069. if ($n.hasClass(clDroppable)) {
  13070. $n.droppable('destroy');
  13071. }
  13072. }
  13073. if (fm.isCommandEnabled('upload', hash)) {
  13074. if (! $n.hasClass('native-droppable,elfinder-na,elfinder-ro')) {
  13075. $n.addClass('native-droppable');
  13076. }
  13077. } else {
  13078. if ($n.hasClass('native-droppable')) {
  13079. $n.removeClass('native-droppable');
  13080. }
  13081. }
  13082. });
  13083. },
  13084. /**
  13085. * Preload required thumbnails and on load add css to files.
  13086. * Return false if required file is not visible yet (in buffer) -
  13087. * required for old api to stop loading thumbnails.
  13088. *
  13089. * @param Object file hash -> thumbnail map
  13090. * @param Bool reload
  13091. * @return void
  13092. */
  13093. attachThumbnails = function(tmbs, reload) {
  13094. var attach = function(node, tmb) {
  13095. $('<img/>')
  13096. .on('load', function() {
  13097. node.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')");
  13098. })
  13099. .attr('src', tmb.url);
  13100. },
  13101. chk = function(hash, tmb) {
  13102. var node = $('#'+fm.cwdHash2Id(hash)),
  13103. file, tmbObj, reloads = [];
  13104. if (node.length) {
  13105. if (tmb != '1') {
  13106. file = fm.file(hash);
  13107. if (file.tmb !== tmb) {
  13108. file.tmb = tmb;
  13109. }
  13110. tmbObj = fm.tmb(file);
  13111. if (reload) {
  13112. node.find('.elfinder-cwd-icon').addClass(tmbObj.className).css('background-image', "url('"+tmbObj.url+"')");
  13113. } else {
  13114. attach(node, tmbObj);
  13115. }
  13116. delete bufferExt.attachTmbs[hash];
  13117. } else {
  13118. if (reload) {
  13119. loadThumbnails([hash]);
  13120. } else if (! bufferExt.tmbLoading[hash]) {
  13121. bufferExt.getTmbs.push(hash);
  13122. }
  13123. }
  13124. }
  13125. };
  13126. if ($.isPlainObject(tmbs) && Object.keys(tmbs).length) {
  13127. Object.assign(bufferExt.attachTmbs, tmbs);
  13128. $.each(tmbs, chk);
  13129. if (! reload && bufferExt.getTmbs.length && ! Object.keys(bufferExt.tmbLoading).length) {
  13130. loadThumbnails();
  13131. }
  13132. }
  13133. },
  13134. /**
  13135. * Load thumbnails from backend.
  13136. *
  13137. * @param Array|void reloads hashes list for reload thumbnail items
  13138. * @return void
  13139. */
  13140. loadThumbnails = function(reloads) {
  13141. var tmbs = [],
  13142. reload = false;
  13143. if (fm.oldAPI) {
  13144. fm.request({
  13145. data : {cmd : 'tmb', current : fm.cwd().hash},
  13146. preventFail : true
  13147. })
  13148. .done(function(data) {
  13149. if (data.images && Object.keys(data.images).length) {
  13150. attachThumbnails(data.images);
  13151. }
  13152. if (data.tmb) {
  13153. loadThumbnails();
  13154. }
  13155. });
  13156. return;
  13157. }
  13158. if (reloads) {
  13159. reload = true;
  13160. tmbs = reloads.splice(0, tmbNum);
  13161. } else {
  13162. tmbs = bufferExt.getTmbs.splice(0, tmbNum);
  13163. }
  13164. if (tmbs.length) {
  13165. if (reload || inViewHashes[tmbs[0]] || inViewHashes[tmbs[tmbs.length-1]]) {
  13166. $.each(tmbs, function(i, h) {
  13167. bufferExt.tmbLoading[h] = true;
  13168. });
  13169. fm.request({
  13170. data : {cmd : 'tmb', targets : tmbs},
  13171. preventFail : true
  13172. })
  13173. .done(function(data) {
  13174. var errs = [],
  13175. resLen;
  13176. if (data.images) {
  13177. if (resLen = Object.keys(data.images).length) {
  13178. if (resLen < tmbs.length) {
  13179. $.each(tmbs, function(i, h) {
  13180. if (! data.images[h]) {
  13181. errs.push(h);
  13182. }
  13183. });
  13184. }
  13185. attachThumbnails(data.images, reload);
  13186. } else {
  13187. errs = tmbs;
  13188. }
  13189. // unset error items from bufferExt.attachTmbs
  13190. if (errs.length) {
  13191. $.each(errs, function(i, h) {
  13192. delete bufferExt.attachTmbs[h];
  13193. });
  13194. }
  13195. }
  13196. if (reload) {
  13197. if (reloads.length) {
  13198. loadThumbnails(reloads);
  13199. }
  13200. }
  13201. })
  13202. .always(function() {
  13203. bufferExt.tmbLoading = {};
  13204. if (! reload && bufferExt.getTmbs.length) {
  13205. loadThumbnails();
  13206. }
  13207. });
  13208. }
  13209. }
  13210. },
  13211. /**
  13212. * Add new files to cwd/buffer
  13213. *
  13214. * @param Array new files
  13215. * @return void
  13216. */
  13217. add = function(files, mode) {
  13218. var place = list ? cwd.find('tbody') : cwd,
  13219. l = files.length,
  13220. atmb = {},
  13221. findNode = function(file) {
  13222. var pointer = cwd.find('[id]:first'), file2;
  13223. while (pointer.length) {
  13224. file2 = fm.file(fm.cwdId2Hash(pointer.attr('id')));
  13225. if (!pointer.hasClass('elfinder-cwd-parent') && file2 && fm.compare(file, file2) < 0) {
  13226. return pointer;
  13227. }
  13228. pointer = pointer.next('[id]');
  13229. }
  13230. },
  13231. findIndex = function(file) {
  13232. var l = buffer.length, i;
  13233. for (i =0; i < l; i++) {
  13234. if (fm.compare(file, buffer[i]) < 0) {
  13235. return i;
  13236. }
  13237. }
  13238. return l || -1;
  13239. },
  13240. // created document fragment for jQuery >= 1.12, 2.2, 3.0
  13241. // see Studio-42/elFinder#1544 @ github
  13242. docFlag = $.htmlPrefilter? true : false,
  13243. tempDom = docFlag? $(document.createDocumentFragment()) : $('<div/>'),
  13244. file, hash, node, nodes, ndx;
  13245. if (l > showFiles) {
  13246. // re-render for performance tune
  13247. content();
  13248. selectedFiles = fm.arrayFlip($.map(files, function(f) { return f.hash; }), true);
  13249. trigger();
  13250. } else {
  13251. // add the item immediately
  13252. l && wz.removeClass('elfinder-cwd-wrapper-empty');
  13253. while (l--) {
  13254. file = files[l];
  13255. hash = file.hash;
  13256. if ($('#'+fm.cwdHash2Id(hash)).length) {
  13257. continue;
  13258. }
  13259. if ((node = findNode(file)) && ! node.length) {
  13260. node = null;
  13261. }
  13262. if (! node && (ndx = findIndex(file)) >= 0) {
  13263. buffer.splice(ndx, 0, file);
  13264. } else {
  13265. tempDom.empty().append(itemhtml(file));
  13266. (file.mime === 'directory') && !mobile && makeDroppable(tempDom);
  13267. nodes = docFlag? tempDom : tempDom.children();
  13268. if (node) {
  13269. node.before(nodes);
  13270. } else {
  13271. place.append(nodes);
  13272. }
  13273. }
  13274. if ($('#'+fm.cwdHash2Id(hash)).length) {
  13275. if (file.tmb) {
  13276. atmb[hash] = file.tmb;
  13277. }
  13278. }
  13279. }
  13280. if (list) {
  13281. setColwidth();
  13282. fixTableHeader({fitWidth: ! colWidth});
  13283. }
  13284. bottomMarkerShow(place);
  13285. if (Object.keys(atmb).length) {
  13286. Object.assign(bufferExt.attachTmbs, atmb);
  13287. }
  13288. }
  13289. },
  13290. /**
  13291. * Remove files from cwd/buffer
  13292. *
  13293. * @param Array files hashes
  13294. * @return void
  13295. */
  13296. remove = function(files) {
  13297. var l = files.length,
  13298. inSearch = fm.searchStatus.state > 1,
  13299. curCmd = fm.getCommand(fm.currentReqCmd) || {},
  13300. hash, n, ndx, found;
  13301. // removed cwd
  13302. if (!fm.cwd().hash && !curCmd.noChangeDirOnRemovedCwd) {
  13303. $.each(cwdParents.reverse(), function(i, h) {
  13304. if (fm.file(h)) {
  13305. found = true;
  13306. fm.one(fm.currentReqCmd + 'done', function() {
  13307. !fm.cwd().hash && fm.exec('open', h);
  13308. });
  13309. return false;
  13310. }
  13311. });
  13312. // fallback to fm.roots[0]
  13313. !found && !fm.cwd().hash && fm.exec('open', fm.roots[Object.keys(fm.roots)[0]]);
  13314. return;
  13315. }
  13316. while (l--) {
  13317. hash = files[l];
  13318. if ((n = $('#'+fm.cwdHash2Id(hash))).length) {
  13319. try {
  13320. n.remove();
  13321. --bufferExt.renderd;
  13322. } catch(e) {
  13323. fm.debug('error', e);
  13324. }
  13325. } else if ((ndx = index(hash)) !== -1) {
  13326. buffer.splice(ndx, 1);
  13327. }
  13328. selectedFiles[hash] && delete selectedFiles[hash];
  13329. if (inSearch) {
  13330. if ((ndx = $.inArray(hash, cwdHashes)) !== -1) {
  13331. cwdHashes.splice(ndx, 1);
  13332. }
  13333. }
  13334. }
  13335. inSearch && fm.trigger('cwdhasheschange', cwdHashes);
  13336. if (list) {
  13337. setColwidth();
  13338. fixTableHeader({fitWidth: ! colWidth});
  13339. }
  13340. },
  13341. customColsNameBuild = function() {
  13342. var name = '',
  13343. customColsName = '';
  13344. for (var i = 0; i < customCols.length; i++) {
  13345. name = fm.getColumnName(customCols[i]);
  13346. customColsName +='<td class="elfinder-cwd-view-th-'+customCols[i]+' sortable-item">'+name+'</td>';
  13347. }
  13348. return customColsName;
  13349. },
  13350. bottomMarkerShow = function(place, cnt) {
  13351. var ph, col = 1;
  13352. place = place || (list ? cwd.find('tbody') : cwd);
  13353. if (buffer.length > 0) {
  13354. place.css({height: 'auto'});
  13355. ph = place.height();
  13356. if (cnt) {
  13357. if (! list) {
  13358. col = Math.floor(place.width()/place.find('[id]:first').width());
  13359. cnt = Math.ceil(cnt/col) * col;
  13360. }
  13361. bufferExt.hpi = ph / cnt;
  13362. bufferExt.row = bufferExt.hpi * col;
  13363. }
  13364. bottomMarker.css({top: (bufferExt.hpi * buffer.length + ph) + 'px'}).show();
  13365. }
  13366. },
  13367. wrapperContextMenu = {
  13368. contextmenu : function(e) {
  13369. e.preventDefault();
  13370. fm.trigger('contextmenu', {
  13371. 'type' : 'cwd',
  13372. 'targets' : [fm.cwd().hash],
  13373. 'x' : e.pageX,
  13374. 'y' : e.pageY
  13375. });
  13376. },
  13377. touchstart : function(e) {
  13378. if (e.originalEvent.touches.length > 1) {
  13379. return;
  13380. }
  13381. cwd.data('longtap', null);
  13382. wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY});
  13383. if (e.target === this || e.target === cwd.get(0)) {
  13384. cwd.data('tmlongtap', setTimeout(function(){
  13385. // long tap
  13386. cwd.data('longtap', true);
  13387. fm.trigger('contextmenu', {
  13388. 'type' : 'cwd',
  13389. 'targets' : [fm.cwd().hash],
  13390. 'x' : wrapper.data('touching').x,
  13391. 'y' : wrapper.data('touching').y
  13392. });
  13393. }, 500));
  13394. }
  13395. },
  13396. touchend : function(e) {
  13397. if (e.type === 'touchmove') {
  13398. if (! wrapper.data('touching') ||
  13399. ( Math.abs(wrapper.data('touching').x - e.originalEvent.touches[0].pageX)
  13400. + Math.abs(wrapper.data('touching').y - e.originalEvent.touches[0].pageY)) > 4) {
  13401. wrapper.data('touching', null);
  13402. }
  13403. }
  13404. clearTimeout(cwd.data('tmlongtap'));
  13405. },
  13406. click : function(e) {
  13407. if (cwd.data('longtap')) {
  13408. e.preventDefault();
  13409. e.stopPropagation();
  13410. }
  13411. }
  13412. },
  13413. /**
  13414. * Update directory content
  13415. *
  13416. * @return void
  13417. */
  13418. content = function() {
  13419. var phash, emptyMethod, thtr;
  13420. wz.append(selectAllCheckbox).removeClass('elfinder-cwd-wrapper-empty elfinder-search-result elfinder-incsearch-result elfinder-letsearch-result');
  13421. if (fm.searchStatus.state > 1 || fm.searchStatus.ininc) {
  13422. wz.addClass('elfinder-search-result' + (fm.searchStatus.ininc? ' elfinder-'+(query.substr(0,1) === '/' ? 'let':'inc')+'search-result' : ''));
  13423. }
  13424. // abort attachThumbJob
  13425. bufferExt.attachThumbJob && bufferExt.attachThumbJob._abort();
  13426. // destroy selectable for GC
  13427. cwd.data('selectable') && cwd.selectable('disable').selectable('destroy').removeData('selectable');
  13428. // notify cwd init
  13429. fm.trigger('cwdinit');
  13430. selectedNext = $();
  13431. try {
  13432. // to avoid problem with draggable
  13433. cwd.empty();
  13434. } catch (e) {
  13435. cwd.html('');
  13436. }
  13437. if (tableHeader) {
  13438. wrapper.off('scroll.fixheader resize.fixheader');
  13439. tableHeader.remove();
  13440. tableHeader = null;
  13441. }
  13442. cwd.removeClass('elfinder-cwd-view-icons elfinder-cwd-view-list')
  13443. .addClass('elfinder-cwd-view-'+(list ? 'list' :'icons'))
  13444. .attr('style', '')
  13445. .css('height', 'auto');
  13446. bottomMarker.hide();
  13447. wrapper[list ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-list')
  13448. ._padding = parseInt(wrapper.css('padding-top')) + parseInt(wrapper.css('padding-bottom'));
  13449. if (fm.UA.iOS) {
  13450. wrapper.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch');
  13451. }
  13452. if (list) {
  13453. cwd.html('<table><thead/><tbody/></table>');
  13454. thtr = $('<tr class="ui-state-default"><td class="elfinder-cwd-view-th-name">'+fm.getColumnName('name')+'</td>'+customColsNameBuild()+'</tr>');
  13455. cwd.find('thead').hide().append(
  13456. thtr
  13457. .on('contextmenu.'+fm.namespace, wrapperContextMenu.contextmenu)
  13458. .on('touchstart.'+fm.namespace, 'td', wrapperContextMenu.touchstart)
  13459. .on('touchmove.'+fm.namespace+' touchend.'+fm.namespace+' mouseup.'+fm.namespace, 'td', wrapperContextMenu.touchend)
  13460. .on('click.'+fm.namespace,'td', wrapperContextMenu.click)
  13461. ).find('td:first').append(selectAllCheckbox);
  13462. if ($.fn.sortable) {
  13463. thtr.addClass('touch-punch touch-punch-keep-default')
  13464. .sortable({
  13465. axis: 'x',
  13466. distance: 8,
  13467. items: '> .sortable-item',
  13468. start: function(e, ui) {
  13469. $(ui.item[0]).data('dragging', true);
  13470. ui.placeholder
  13471. .width(ui.helper.removeClass('ui-state-hover').width())
  13472. .removeClass('ui-state-active')
  13473. .addClass('ui-state-hover')
  13474. .css('visibility', 'visible');
  13475. },
  13476. update: function(e, ui){
  13477. var target = $(ui.item[0]).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', ''),
  13478. prev, done;
  13479. customCols = $.map($(this).children(), function(n) {
  13480. var name = $(n).attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', '');
  13481. if (! done) {
  13482. if (target === name) {
  13483. done = true;
  13484. } else {
  13485. prev = name;
  13486. }
  13487. }
  13488. return (name === 'name')? null : name;
  13489. });
  13490. templates.row = makeTemplateRow();
  13491. fm.storage('cwdCols', customCols);
  13492. prev = '.elfinder-col-'+prev+':first';
  13493. target = '.elfinder-col-'+target+':first';
  13494. fm.lazy(function() {
  13495. cwd.find('tbody tr').each(function() {
  13496. var $this = $(this);
  13497. $this.children(prev).after($this.children(target));
  13498. });
  13499. });
  13500. },
  13501. stop: function(e, ui) {
  13502. setTimeout(function() {
  13503. $(ui.item[0]).removeData('dragging');
  13504. }, 100);
  13505. }
  13506. });
  13507. }
  13508. thtr.find('td').addClass('touch-punch').resizable({
  13509. handles: fm.direction === 'ltr'? 'e' : 'w',
  13510. start: function(e, ui) {
  13511. var target = cwd.find('td.elfinder-col-'
  13512. + ui.element.attr('class').split(' ')[0].replace('elfinder-cwd-view-th-', '')
  13513. + ':first');
  13514. ui.element
  13515. .data('resizeTarget', target)
  13516. .data('targetWidth', target.width());
  13517. colResizing = true;
  13518. if (cwd.find('table').css('table-layout') !== 'fixed') {
  13519. cwd.find('tbody tr:first td').each(function() {
  13520. $(this).width($(this).width());
  13521. });
  13522. cwd.find('table').css('table-layout', 'fixed');
  13523. }
  13524. },
  13525. resize: function(e, ui) {
  13526. ui.element.data('resizeTarget').width(ui.element.data('targetWidth') - (ui.originalSize.width - ui.size.width));
  13527. },
  13528. stop : function() {
  13529. colResizing = false;
  13530. fixTableHeader({fitWidth: true});
  13531. colWidth = {};
  13532. cwd.find('tbody tr:first td').each(function() {
  13533. var name = $(this).attr('class').split(' ')[0].replace('elfinder-col-', '');
  13534. colWidth[name] = $(this).width();
  13535. });
  13536. fm.storage('cwdColWidth', colWidth);
  13537. }
  13538. })
  13539. .find('.ui-resizable-handle').addClass('ui-icon ui-icon-grip-dotted-vertical');
  13540. }
  13541. fm.lazy(function() {
  13542. buffer = $.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null; });
  13543. buffer = fm.sortFiles(buffer);
  13544. if (incHashes) {
  13545. incHashes = $.map(buffer, function(f) { return f.hash; });
  13546. } else {
  13547. cwdHashes = $.map(buffer, function(f) { return f.hash; });
  13548. }
  13549. bufferExt = {
  13550. renderd: 0,
  13551. attachTmbs: {},
  13552. getTmbs: [],
  13553. tmbLoading: {},
  13554. lazyOpts: { tm : 0 }
  13555. };
  13556. wz[(buffer.length < 1) ? 'addClass' : 'removeClass']('elfinder-cwd-wrapper-empty');
  13557. wrapper.off(scrollEvent, render).on(scrollEvent, render).trigger(scrollEvent);
  13558. // set droppable
  13559. if (!fm.cwd().write) {
  13560. wrapper.removeClass('native-droppable')
  13561. .droppable('disable')
  13562. .removeClass('ui-state-disabled'); // for old jQueryUI see https://bugs.jqueryui.com/ticket/5974
  13563. } else {
  13564. wrapper[fm.isCommandEnabled('upload')? 'addClass' : 'removeClass']('native-droppable');
  13565. wrapper.droppable(fm.isCommandEnabled('paste')? 'enable' : 'disable');
  13566. }
  13567. });
  13568. },
  13569. /**
  13570. * CWD node itself
  13571. *
  13572. * @type JQuery
  13573. **/
  13574. cwd = $(this)
  13575. .addClass('ui-helper-clearfix elfinder-cwd')
  13576. .attr('unselectable', 'on')
  13577. // fix ui.selectable bugs and add shift+click support
  13578. .on('click.'+fm.namespace, fileSelector, function(e) {
  13579. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  13580. tgt = $(e.target),
  13581. prev,
  13582. next,
  13583. pl,
  13584. nl,
  13585. sib;
  13586. if (selectCheckbox && (tgt.is('input:checkbox') || tgt.hasClass('elfinder-cwd-select'))) {
  13587. e.stopPropagation();
  13588. e.preventDefault();
  13589. p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect);
  13590. trigger();
  13591. setTimeout(function() {
  13592. tgt.prop('checked', p.hasClass(clSelected));
  13593. }, 10);
  13594. return;
  13595. }
  13596. if (cwd.data('longtap')) {
  13597. e.stopPropagation();
  13598. return;
  13599. }
  13600. if (!curClickId) {
  13601. curClickId = p.attr('id');
  13602. setTimeout(function() {
  13603. curClickId = '';
  13604. }, 500);
  13605. }
  13606. if (e.shiftKey) {
  13607. prev = p.prevAll(lastSelect || '.'+clSelected+':first');
  13608. next = p.nextAll(lastSelect || '.'+clSelected+':first');
  13609. pl = prev.length;
  13610. nl = next.length;
  13611. }
  13612. if (e.shiftKey && (pl || nl)) {
  13613. sib = pl ? p.prevUntil('#'+prev.attr('id')) : p.nextUntil('#'+next.attr('id'));
  13614. sib.add(p).trigger(evtSelect);
  13615. } else if (e.ctrlKey || e.metaKey) {
  13616. p.trigger(p.hasClass(clSelected) ? evtUnselect : evtSelect);
  13617. } else {
  13618. if (wrapper.data('touching') && p.hasClass(clSelected)) {
  13619. wrapper.data('touching', null);
  13620. fm.dblclick({file : fm.cwdId2Hash(this.id)});
  13621. return;
  13622. } else {
  13623. unselectAll({ notrigger: true });
  13624. p.trigger(evtSelect);
  13625. }
  13626. }
  13627. trigger();
  13628. })
  13629. // call fm.open()
  13630. .on('dblclick.'+fm.namespace, fileSelector, function(e) {
  13631. if (curClickId) {
  13632. var hash = fm.cwdId2Hash(curClickId);
  13633. e.stopPropagation();
  13634. if (this.id !== curClickId) {
  13635. $(this).trigger(evtUnselect);
  13636. $('#'+curClickId).trigger(evtSelect);
  13637. trigger();
  13638. }
  13639. fm.dblclick({file : hash});
  13640. }
  13641. })
  13642. // for touch device
  13643. .on('touchstart.'+fm.namespace, fileSelector, function(e) {
  13644. if (e.originalEvent.touches.length > 1) {
  13645. return;
  13646. }
  13647. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  13648. tgt = $(e.target),
  13649. nodeName = e.target.nodeName,
  13650. sel;
  13651. if (nodeName === 'INPUT' || nodeName === 'TEXTAREA') {
  13652. e.stopPropagation();
  13653. return;
  13654. }
  13655. // now name editing
  13656. if (p.find('input:text,textarea').length) {
  13657. e.stopPropagation();
  13658. e.preventDefault();
  13659. return;
  13660. }
  13661. wrapper.data('touching', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY});
  13662. if (selectCheckbox && (tgt.is('input:checkbox') || tgt.hasClass('elfinder-cwd-select'))) {
  13663. e.stopPropagation();
  13664. return;
  13665. }
  13666. sel = p.prevAll('.'+clSelected+':first').length +
  13667. p.nextAll('.'+clSelected+':first').length;
  13668. cwd.data('longtap', null);
  13669. p.addClass(clHover)
  13670. .data('tmlongtap', setTimeout(function(){
  13671. // long tap
  13672. cwd.data('longtap', true);
  13673. if (e.target.nodeName != 'TD' || fm.selected().length > 0) {
  13674. p.trigger(evtSelect);
  13675. trigger();
  13676. fm.trigger('contextmenu', {
  13677. 'type' : 'files',
  13678. 'targets' : fm.selected(),
  13679. 'x' : e.originalEvent.touches[0].pageX,
  13680. 'y' : e.originalEvent.touches[0].pageY
  13681. });
  13682. }
  13683. }, 500));
  13684. })
  13685. .on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, fileSelector, function(e) {
  13686. if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA' || $(e.target).hasClass('elfinder-cwd-select')) {
  13687. e.stopPropagation();
  13688. return;
  13689. }
  13690. var p = this.id ? $(this) : $(this).parents('[id]:first');
  13691. clearTimeout(p.data('tmlongtap'));
  13692. if (e.type === 'touchmove') {
  13693. wrapper.data('touching', null);
  13694. p.removeClass(clHover);
  13695. } else if (wrapper.data('touching') && !cwd.data('longtap') && p.hasClass(clSelected)) {
  13696. e.preventDefault();
  13697. wrapper.data('touching', null);
  13698. fm.dblclick({file : fm.cwdId2Hash(this.id)});
  13699. }
  13700. })
  13701. // attach draggable
  13702. .on('mouseenter.'+fm.namespace, fileSelector, function(e) {
  13703. if (scrolling) { return; }
  13704. var $this = $(this), helper = null,
  13705. target = list ? $this : $this.children('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename');
  13706. if (!mobile && !$this.data('dragRegisted') && !$this.hasClass(clTmp) && !target.hasClass(clDraggable) && !target.hasClass(clDisabled)) {
  13707. $this.data('dragRegisted', true);
  13708. if (!fm.isCommandEnabled('copy', fm.searchStatus.state > 1? fm.cwdId2Hash($this.attr('id')) : void 0)) {
  13709. return;
  13710. }
  13711. target.on('mousedown', function(e) {
  13712. // shiftKey or altKey + drag start for HTML5 native drag function
  13713. // Note: can no use shiftKey with the Google Chrome
  13714. var metaKey = e.shiftKey || e.altKey;
  13715. if (metaKey && !fm.UA.IE && cwd.data('selectable')) {
  13716. // destroy jQuery-ui selectable while trigger native drag
  13717. cwd.selectable('disable').selectable('destroy').removeData('selectable');
  13718. setTimeout(function(){
  13719. cwd.selectable(selectableOption).selectable('option', {disabled: false}).selectable('refresh').data('selectable', true);
  13720. }, 10);
  13721. }
  13722. target.draggable('option', 'disabled', metaKey).removeClass('ui-state-disabled');
  13723. if (metaKey) {
  13724. target.attr('draggable', 'true');
  13725. } else {
  13726. target.removeAttr('draggable')
  13727. .draggable('option', 'cursorAt', {left: 50 - parseInt($(e.currentTarget).css('margin-left')), top: 47});
  13728. }
  13729. })
  13730. .on('dragstart', function(e) {
  13731. var dt = e.dataTransfer || e.originalEvent.dataTransfer || null;
  13732. helper = null;
  13733. if (dt && !fm.UA.IE) {
  13734. var p = this.id ? $(this) : $(this).parents('[id]:first'),
  13735. elm = $('<span>'),
  13736. url = '',
  13737. durl = null,
  13738. murl = null,
  13739. files = [],
  13740. icon = function(f) {
  13741. var mime = f.mime, i, tmb = fm.tmb(f);
  13742. i = '<div class="elfinder-cwd-icon elfinder-cwd-icon-drag '+fm.mime2class(mime)+' ui-corner-all"/>';
  13743. if (tmb) {
  13744. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  13745. }
  13746. return i;
  13747. }, l, geturl = [];
  13748. p.trigger(evtSelect);
  13749. trigger();
  13750. $.each(selectedFiles, function(v){
  13751. var file = fm.file(v),
  13752. furl = file.url;
  13753. if (file && file.mime !== 'directory') {
  13754. if (!furl) {
  13755. furl = fm.url(file.hash);
  13756. } else if (furl == '1') {
  13757. geturl.push(v);
  13758. return true;
  13759. }
  13760. if (furl) {
  13761. furl = fm.convAbsUrl(furl);
  13762. files.push(v);
  13763. $('<a>').attr('href', furl).text(furl).appendTo(elm);
  13764. url += furl + "\n";
  13765. if (!durl) {
  13766. durl = file.mime + ':' + file.name + ':' + furl;
  13767. }
  13768. if (!murl) {
  13769. murl = furl + "\n" + file.name;
  13770. }
  13771. }
  13772. }
  13773. });
  13774. if (geturl.length) {
  13775. $.each(geturl, function(i, v){
  13776. var rfile = fm.file(v);
  13777. rfile.url = '';
  13778. fm.request({
  13779. data : {cmd : 'url', target : v},
  13780. notify : {type : 'url', cnt : 1},
  13781. preventDefault : true
  13782. })
  13783. .always(function(data) {
  13784. rfile.url = data.url? data.url : '1';
  13785. });
  13786. });
  13787. return false;
  13788. } else if (url) {
  13789. if (dt.setDragImage) {
  13790. helper = $('<div class="elfinder-drag-helper html5-native"></div>').append(icon(fm.file(files[0]))).appendTo($(document.body));
  13791. if ((l = files.length) > 1) {
  13792. helper.append(icon(fm.file(files[l-1])) + '<span class="elfinder-drag-num">'+l+'</span>');
  13793. }
  13794. dt.setDragImage(helper.get(0), 50, 47);
  13795. }
  13796. dt.effectAllowed = 'copyLink';
  13797. dt.setData('DownloadURL', durl);
  13798. dt.setData('text/x-moz-url', murl);
  13799. dt.setData('text/uri-list', url);
  13800. dt.setData('text/plain', url);
  13801. dt.setData('text/html', elm.html());
  13802. dt.setData('elfinderfrom', window.location.href + fm.cwd().hash);
  13803. dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), '');
  13804. } else {
  13805. return false;
  13806. }
  13807. }
  13808. })
  13809. .on('dragend', function(e){
  13810. unselectAll({ notrigger: true });
  13811. helper && helper.remove();
  13812. })
  13813. .draggable(fm.draggable);
  13814. }
  13815. })
  13816. // add hover class to selected file
  13817. .on(evtSelect, fileSelector, function(e) {
  13818. var $this = $(this),
  13819. id = fm.cwdId2Hash($this.attr('id'));
  13820. if (!selectLock && !$this.hasClass(clDisabled)) {
  13821. lastSelect = '#'+ this.id;
  13822. $this.addClass(clSelected).children().addClass(clHover).find('input:checkbox').prop('checked', true);
  13823. if (! selectedFiles[id]) {
  13824. selectedFiles[id] = true;
  13825. }
  13826. // will be selected next
  13827. selectedNext = cwd.find('[id].'+clSelected+':last').next();
  13828. }
  13829. })
  13830. // remove hover class from unselected file
  13831. .on(evtUnselect, fileSelector, function(e) {
  13832. var $this = $(this),
  13833. id = fm.cwdId2Hash($this.attr('id'));
  13834. if (!selectLock) {
  13835. $this.removeClass(clSelected).children().removeClass(clHover).find('input:checkbox').prop('checked', false);
  13836. if (cwd.hasClass('elfinder-cwd-allselected')) {
  13837. selectCheckbox && selectAllCheckbox.children('input').prop('checked', false);
  13838. cwd.removeClass('elfinder-cwd-allselected');
  13839. }
  13840. selectedFiles[id] && delete selectedFiles[id];
  13841. }
  13842. })
  13843. // disable files wich removing or moving
  13844. .on(evtDisable, fileSelector, function() {
  13845. var $this = $(this).removeClass(clHover+' '+clSelected).addClass(clDisabled),
  13846. child = $this.children(),
  13847. target = (list ? $this : child.find('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename'));
  13848. child.removeClass(clHover+' '+clSelected);
  13849. $this.hasClass(clDroppable) && $this.droppable('disable');
  13850. target.hasClass(clDraggable) && target.draggable('disable');
  13851. })
  13852. // if any files was not removed/moved - unlock its
  13853. .on(evtEnable, fileSelector, function() {
  13854. var $this = $(this).removeClass(clDisabled),
  13855. target = list ? $this : $this.children('div.elfinder-cwd-file-wrapper,div.elfinder-cwd-filename');
  13856. $this.hasClass(clDroppable) && $this.droppable('enable');
  13857. target.hasClass(clDraggable) && target.draggable('enable');
  13858. })
  13859. .on('scrolltoview', fileSelector, function(e, data) {
  13860. scrollToView($(this), (data && typeof data.blink !== 'undefined')? data.blink : true);
  13861. })
  13862. .on('mouseenter.'+fm.namespace+' mouseleave.'+fm.namespace, fileSelector, function(e) {
  13863. var enter = (e.type === 'mouseenter');
  13864. if (enter && scrolling) { return; }
  13865. fm.trigger('hover', {hash : fm.cwdId2Hash($(this).attr('id')), type : e.type});
  13866. $(this).toggleClass(clHover, (e.type == 'mouseenter'));
  13867. })
  13868. .on('contextmenu.'+fm.namespace, function(e) {
  13869. var file = $(e.target).closest(fileSelector);
  13870. // now filename editing
  13871. if (file.find('input:text,textarea').length) {
  13872. e.stopPropagation();
  13873. return;
  13874. }
  13875. if (file.length && (e.target.nodeName != 'TD' || $.inArray(fm.cwdId2Hash(file.get(0).id), fm.selected()) > -1)) {
  13876. e.stopPropagation();
  13877. e.preventDefault();
  13878. if (!file.hasClass(clDisabled) && !wrapper.data('touching')) {
  13879. if (!file.hasClass(clSelected)) {
  13880. unselectAll({ notrigger: true });
  13881. file.trigger(evtSelect);
  13882. trigger();
  13883. }
  13884. fm.trigger('contextmenu', {
  13885. 'type' : 'files',
  13886. 'targets' : fm.selected(),
  13887. 'x' : e.pageX,
  13888. 'y' : e.pageY
  13889. });
  13890. }
  13891. }
  13892. })
  13893. // unselect all on cwd click
  13894. .on('click.'+fm.namespace, function(e) {
  13895. if (e.target === this && ! cwd.data('longtap')) {
  13896. !e.shiftKey && !e.ctrlKey && !e.metaKey && unselectAll();
  13897. }
  13898. })
  13899. // prepend fake file/dir
  13900. .on('create.'+fm.namespace, function(e, f) {
  13901. var parent = list ? cwd.find('tbody') : cwd,
  13902. p = parent.find('.elfinder-cwd-parent'),
  13903. lock = f.move || false,
  13904. file = $(itemhtml(f)).addClass(clTmp),
  13905. selected = fm.selected();
  13906. if (selected.length) {
  13907. lock && fm.trigger('lockfiles', {files: selected});
  13908. } else {
  13909. unselectAll();
  13910. }
  13911. if (p.length) {
  13912. p.after(file);
  13913. } else {
  13914. parent.prepend(file);
  13915. }
  13916. setColwidth();
  13917. wrapper.scrollTop(0).scrollLeft(0);
  13918. })
  13919. // unselect all selected files
  13920. .on('unselectall', unselectAll)
  13921. .on('selectfile', function(e, id) {
  13922. $('#'+fm.cwdHash2Id(id)).trigger(evtSelect);
  13923. trigger();
  13924. })
  13925. .on('colwidth', function() {
  13926. if (list) {
  13927. cwd.find('table').css('table-layout', '')
  13928. .find('td').css('width', '');
  13929. fixTableHeader({fitWidth: true});
  13930. fm.storage('cwdColWidth', colWidth = null);
  13931. }
  13932. }),
  13933. wrapper = $('<div class="elfinder-cwd-wrapper"/>')
  13934. // make cwd itself droppable for folders from nav panel
  13935. .droppable(Object.assign({}, droppable, {autoDisable: false}))
  13936. .on('contextmenu.'+fm.namespace, wrapperContextMenu.contextmenu)
  13937. .on('touchstart.'+fm.namespace, wrapperContextMenu.touchstart)
  13938. .on('touchmove.'+fm.namespace+' touchend.'+fm.namespace, wrapperContextMenu.touchend)
  13939. .on('click.'+fm.namespace, wrapperContextMenu.click)
  13940. .on('scroll.'+fm.namespace, function() {
  13941. if (! scrolling) {
  13942. cwd.data('selectable') && cwd.selectable('disable');
  13943. wrapper.trigger(scrollStartEvent);
  13944. }
  13945. scrolling = true;
  13946. bufferExt.scrtm && clearTimeout(bufferExt.scrtm);
  13947. if (bufferExt.scrtm && Math.abs((bufferExt.scrolltop || 0) - (bufferExt.scrolltop = (this.scrollTop || $(this).scrollTop()))) < 5) {
  13948. bufferExt.scrtm = 0;
  13949. wrapper.trigger(scrollEvent);
  13950. }
  13951. bufferExt.scrtm = setTimeout(function() {
  13952. bufferExt.scrtm = 0;
  13953. wrapper.trigger(scrollEvent);
  13954. }, 20);
  13955. })
  13956. .on(scrollEvent, function() {
  13957. scrolling = false;
  13958. wrapperRepaint();
  13959. }),
  13960. bottomMarker = $('<div>&nbsp;</div>')
  13961. .css({position: 'absolute', width: '1px', height: '1px'})
  13962. .hide(),
  13963. selectAllCheckbox = selectCheckbox? $('<div class="elfinder-cwd-selectall"><input type="checkbox"/></div>')
  13964. .attr('title', fm.i18n('selectall'))
  13965. .on('touchstart mousedown click', function(e) {
  13966. e.stopPropagation();
  13967. e.preventDefault();
  13968. if ($(this).data('pending') || e.type === 'click') {
  13969. return false;
  13970. }
  13971. selectAllCheckbox.data('pending', true);
  13972. if (cwd.hasClass('elfinder-cwd-allselected')) {
  13973. selectAllCheckbox.find('input').prop('checked', false);
  13974. setTimeout(function() {
  13975. unselectAll();
  13976. }, 10);
  13977. } else {
  13978. selectAll();
  13979. }
  13980. }) : $(),
  13981. restm = null,
  13982. resize = function(init) {
  13983. var initHeight = function() {
  13984. var h = 0;
  13985. wrapper.siblings('div.elfinder-panel:visible').each(function() {
  13986. h += $(this).outerHeight(true);
  13987. });
  13988. wrapper.height(wz.height() - h - wrapper._padding);
  13989. };
  13990. init && initHeight();
  13991. restm && clearTimeout(restm);
  13992. restm = setTimeout(function(){
  13993. !init && initHeight();
  13994. var wph, cwdoh;
  13995. // fix cwd height if it less then wrapper
  13996. cwd.css('height', 'auto');
  13997. wph = wrapper[0].clientHeight - parseInt(wrapper.css('padding-top')) - parseInt(wrapper.css('padding-bottom')) - parseInt(cwd.css('margin-top')),
  13998. cwdoh = cwd.outerHeight(true);
  13999. if (cwdoh < wph) {
  14000. cwd.height(wph);
  14001. }
  14002. }, 10);
  14003. list && ! colResizing && (init? wrapper.trigger('resize.fixheader') : fixTableHeader());
  14004. wrapperRepaint();
  14005. },
  14006. // elfinder node
  14007. parent = $(this).parent().on('resize', resize),
  14008. // workzone node
  14009. wz = parent.children('.elfinder-workzone').append(wrapper.append(this).append(bottomMarker)),
  14010. // has UI tree
  14011. hasUiTree,
  14012. winScrTm;
  14013. // setup by options
  14014. replacement = Object.assign(replacement, options.replacement || {});
  14015. try {
  14016. colWidth = fm.storage('cwdColWidth')? fm.storage('cwdColWidth') : null;
  14017. } catch(e) {
  14018. colWidth = null;
  14019. }
  14020. // setup costomCols
  14021. fm.bind('columnpref', function(e) {
  14022. var opts = e.data || {};
  14023. if (customCols = fm.storage('cwdCols')) {
  14024. customCols = $.grep(customCols, function(n) {
  14025. return (options.listView.columns.indexOf(n) !== -1)? true : false;
  14026. });
  14027. if (options.listView.columns.length > customCols.length) {
  14028. $.each(options.listView.columns, function(i, n) {
  14029. if (customCols.indexOf(n) === -1) {
  14030. customCols.push(n);
  14031. }
  14032. });
  14033. }
  14034. } else {
  14035. customCols = options.listView.columns;
  14036. }
  14037. // column names array that hidden
  14038. var columnhides = fm.storage('columnhides') || null;
  14039. if (columnhides && Object.keys(columnhides).length)
  14040. customCols = $.grep(customCols, function(n) {
  14041. return columnhides[n]? false : true;
  14042. });
  14043. // make template with customCols
  14044. templates.row = makeTemplateRow();
  14045. // repaint if need it
  14046. list && opts.repaint && content();
  14047. }).trigger('columnpref');
  14048. if (mobile) {
  14049. // for iOS5 bug
  14050. $('body').on('touchstart touchmove touchend', function(e){});
  14051. }
  14052. selectCheckbox && cwd.addClass('elfinder-has-checkbox');
  14053. $(window).on('scroll.'+fm.namespace, function() {
  14054. winScrTm && clearTimeout(winScrTm);
  14055. winScrTm = setTimeout(function() {
  14056. wrapper.trigger(scrollEvent);
  14057. }, 50);
  14058. });
  14059. $(document).on('keydown.'+fm.namespace, function(e) {
  14060. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  14061. if (! fm.getUI().find('.ui-widget:visible').length) {
  14062. unselectAll();
  14063. }
  14064. }
  14065. });
  14066. fm
  14067. .one('init', function(){
  14068. var style = document.createElement('style'),
  14069. sheet, node, base, resizeTm, i = 0;
  14070. if (document.head) {
  14071. document.head.appendChild(style);
  14072. sheet = style.sheet;
  14073. sheet.insertRule('.elfinder-cwd-wrapper-empty .elfinder-cwd:after{ content:"'+fm.i18n('emptyFolder')+'" }', i++);
  14074. sheet.insertRule('.elfinder-cwd-wrapper-empty .ui-droppable .elfinder-cwd:after{ content:"'+fm.i18n('emptyFolder'+(mobile? 'LTap' : 'Drop'))+'" }', i++);
  14075. sheet.insertRule('.elfinder-cwd-wrapper-empty .ui-droppable-disabled .elfinder-cwd:after{ content:"'+fm.i18n('emptyFolder')+'" }', i++);
  14076. sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result .elfinder-cwd:after{ content:"'+fm.i18n('emptySearch')+'" }', i++);
  14077. sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result.elfinder-incsearch-result .elfinder-cwd:after{ content:"'+fm.i18n('emptyIncSearch')+'" }', i++);
  14078. sheet.insertRule('.elfinder-cwd-wrapper-empty.elfinder-search-result.elfinder-letsearch-result .elfinder-cwd:after{ content:"'+fm.i18n('emptyLetSearch')+'" }', i++);
  14079. }
  14080. if (! mobile) {
  14081. fm.one('open', function() {
  14082. sheet && fm.zIndex && sheet.insertRule('.ui-selectable-helper{z-index:'+fm.zIndex+';}', i++);
  14083. });
  14084. base = $('<div style="position:absolute"/>');
  14085. node = fm.getUI();
  14086. node.on('resize', function(e, data) {
  14087. var offset;
  14088. e.preventDefault();
  14089. e.stopPropagation();
  14090. if (data && data.fullscreen) {
  14091. offset = node.offset();
  14092. if (data.fullscreen === 'on') {
  14093. base.css({top:offset.top * -1 , left:offset.left * -1 }).appendTo(node);
  14094. selectableOption.appendTo = base;
  14095. } else {
  14096. base.detach();
  14097. selectableOption.appendTo = 'body';
  14098. }
  14099. cwd.data('selectable') && cwd.selectable('option', {appendTo : selectableOption.appendTo});
  14100. }
  14101. });
  14102. }
  14103. hasUiTree = fm.getUI('tree').length;
  14104. })
  14105. .bind('enable', function() {
  14106. resize();
  14107. })
  14108. .bind('request.open', function() {
  14109. bufferExt.getTmbs = [];
  14110. })
  14111. .bind('open add remove searchend', function() {
  14112. var phash = fm.cwd().hash,
  14113. type = this.type;
  14114. if (type === 'open' || type === 'searchend' || fm.searchStatus.state < 2) {
  14115. cwdHashes = $.map(fm.files(phash), function(f) { return f.hash; });
  14116. fm.trigger('cwdhasheschange', cwdHashes);
  14117. }
  14118. if (type === 'open') {
  14119. var inTrash = function() {
  14120. var isIn = false;
  14121. $.each(cwdParents, function(i, h) {
  14122. if (fm.trashes[h]) {
  14123. isIn = true;
  14124. return false;
  14125. }
  14126. });
  14127. return isIn;
  14128. },
  14129. req = phash?
  14130. (! fm.file(phash) || hasUiTree?
  14131. (! hasUiTree?
  14132. fm.request({
  14133. data: {
  14134. cmd : 'parents',
  14135. target : fm.cwd().hash
  14136. },
  14137. preventFail : true
  14138. }) : (function() {
  14139. var dfd = $.Deferred();
  14140. fm.one('treesync', function(e) {
  14141. e.data.always(function() {
  14142. dfd.resolve();
  14143. });
  14144. });
  14145. return dfd;
  14146. })()
  14147. ) : null
  14148. ) : null;
  14149. $.when(req).done(function() {
  14150. cwdParents = fm.parents(fm.cwd().hash);
  14151. wrapper[inTrash()? 'addClass':'removeClass']('elfinder-cwd-wrapper-trash');
  14152. });
  14153. incHashes = void 0;
  14154. unselectAll({ notrigger: true });
  14155. content();
  14156. resize();
  14157. }
  14158. })
  14159. .bind('search', function(e) {
  14160. cwdHashes = $.map(e.data.files, function(f) { return f.hash; });
  14161. fm.trigger('cwdhasheschange', cwdHashes);
  14162. incHashes = void 0;
  14163. fm.searchStatus.ininc = false;
  14164. content();
  14165. fm.autoSync('stop');
  14166. resize();
  14167. })
  14168. .bind('searchend', function(e) {
  14169. if (query || incHashes) {
  14170. query = '';
  14171. if (incHashes) {
  14172. fm.trigger('incsearchend', e.data);
  14173. } else {
  14174. if (!e.data || !e.data.noupdate) {
  14175. content();
  14176. }
  14177. }
  14178. }
  14179. fm.autoSync();
  14180. resize();
  14181. })
  14182. .bind('searchstart', function(e) {
  14183. unselectAll();
  14184. query = e.data.query;
  14185. })
  14186. .bind('incsearchstart', function(e) {
  14187. selectedFiles = {};
  14188. fm.lazy(function() {
  14189. // incremental search
  14190. var regex, q, fst = '';
  14191. q = query = e.data.query || '';
  14192. if (q) {
  14193. if (q.substr(0,1) === '/') {
  14194. q = q.substr(1);
  14195. fst = '^';
  14196. }
  14197. regex = new RegExp(fst + q.replace(/([\\*\;\.\?\[\]\{\}\(\)\^\$\-\|])/g, '\\$1'), 'i');
  14198. incHashes = $.grep(cwdHashes, function(hash) {
  14199. var file = fm.file(hash);
  14200. return (file && (file.name.match(regex) || (file.i18 && file.i18.match(regex))))? true : false;
  14201. });
  14202. fm.trigger('incsearch', { hashes: incHashes, query: q })
  14203. .searchStatus.ininc = true;
  14204. content();
  14205. fm.autoSync('stop');
  14206. } else {
  14207. fm.trigger('incsearchend');
  14208. }
  14209. resize();
  14210. });
  14211. })
  14212. .bind('incsearchend', function(e) {
  14213. query = '';
  14214. fm.searchStatus.ininc = false;
  14215. incHashes = void 0;
  14216. if (!e.data || !e.data.noupdate) {
  14217. content();
  14218. }
  14219. fm.autoSync();
  14220. })
  14221. .bind('sortchange', function() {
  14222. var lastScrollLeft = wrapper.scrollLeft(),
  14223. allsel = cwd.hasClass('elfinder-cwd-allselected');
  14224. content();
  14225. fm.one('cwdrender', function() {
  14226. wrapper.scrollLeft(lastScrollLeft);
  14227. if (allsel) {
  14228. selectedFiles = fm.arrayFlip(incHashes || cwdHashes, true);
  14229. }
  14230. (allsel || Object.keys(selectedFiles).length) && trigger();
  14231. resize();
  14232. });
  14233. })
  14234. .bind('viewchange', function() {
  14235. var l = fm.storage('view') == 'list',
  14236. allsel = cwd.hasClass('elfinder-cwd-allselected');
  14237. if (l != list) {
  14238. list = l;
  14239. fm.viewType = list? 'list' : 'icons';
  14240. content();
  14241. if (allsel) {
  14242. cwd.addClass('elfinder-cwd-allselected');
  14243. selectAllCheckbox.find('input').prop('checked', true);
  14244. }
  14245. Object.keys(selectedFiles).length && trigger();
  14246. }
  14247. resize();
  14248. })
  14249. .bind('wzresize', function() {
  14250. var place = list ? cwd.find('tbody') : cwd,
  14251. cwdOffset;
  14252. resize(true);
  14253. if (bufferExt.hpi) {
  14254. bottomMarkerShow(place, place.find('[id]').length);
  14255. }
  14256. cwdOffset = cwd.offset();
  14257. wz.data('rectangle', Object.assign(
  14258. {
  14259. width: wz.width(),
  14260. height: wz.height(),
  14261. cwdEdge: (fm.direction === 'ltr')? cwdOffset.left : cwdOffset.left + cwd.width()
  14262. },
  14263. wz.offset())
  14264. );
  14265. bufferExt.itemH = (list? place.find('tr:first') : place.find('[id]:first')).outerHeight(true);
  14266. })
  14267. .bind('changeclipboard', function(e) {
  14268. clipCuts = {};
  14269. if (e.data && e.data.clipboard && e.data.clipboard.length) {
  14270. $.each(e.data.clipboard, function(i, f) {
  14271. if (f.cut) {
  14272. clipCuts[f.hash] = true;
  14273. }
  14274. });
  14275. }
  14276. })
  14277. .bind('resMixinMake', function() {
  14278. setColwidth();
  14279. })
  14280. .bind('tmbreload', function(e) {
  14281. var imgs = {},
  14282. files = (e.data && e.data.files)? e.data.files : null;
  14283. $.each(files, function(i, f) {
  14284. if (f.tmb && f.tmb != '1') {
  14285. imgs[f.hash] = f.tmb;
  14286. }
  14287. });
  14288. if (Object.keys(imgs).length) {
  14289. attachThumbnails(imgs, true);
  14290. }
  14291. })
  14292. .add(function(e) {
  14293. var regex = query? new RegExp(query.replace(/([\\*\;\.\?\[\]\{\}\(\)\^\$\-\|])/g, '\\$1'), 'i') : null,
  14294. mime = fm.searchStatus.mime,
  14295. inSearch = fm.searchStatus.state > 1,
  14296. phash = inSearch && fm.searchStatus.target? fm.searchStatus.target : fm.cwd().hash,
  14297. curPath = fm.path(phash),
  14298. inTarget = function(f) {
  14299. var res, parents, path;
  14300. res = (f.phash === phash);
  14301. if (!res && inSearch) {
  14302. path = f.path || fm.path(f.hash);
  14303. res = (curPath && path.indexOf(curPath) === 0);
  14304. if (! res && fm.searchStatus.mixed) {
  14305. res = $.grep(fm.searchStatus.mixed, function(vid) { return f.hash.indexOf(vid) === 0? true : false; }).length? true : false;
  14306. }
  14307. }
  14308. if (res && inSearch) {
  14309. if (mime) {
  14310. res = (f.mime.indexOf(mime) === 0);
  14311. } else {
  14312. res = (f.name.match(regex) || (f.i18 && f.i18.match(regex)))? true : false;
  14313. }
  14314. }
  14315. return res;
  14316. },
  14317. files = $.grep(e.data.added || [], function(f) { return inTarget(f)? true : false ;});
  14318. add(files);
  14319. if (fm.searchStatus.state === 2) {
  14320. $.each(files, function(i, f) {
  14321. if ($.inArray(f.hash, cwdHashes) === -1) {
  14322. cwdHashes.push(f.hash);
  14323. }
  14324. });
  14325. fm.trigger('cwdhasheschange', cwdHashes);
  14326. }
  14327. list && resize();
  14328. wrapper.trigger(scrollEvent);
  14329. })
  14330. .change(function(e) {
  14331. var phash = fm.cwd().hash,
  14332. sel = fm.selected(),
  14333. files, added;
  14334. if (query) {
  14335. $.each(e.data.changed || [], function(i, file) {
  14336. if ($('#'+fm.cwdHash2Id(file.hash)).length) {
  14337. remove([file.hash]);
  14338. if (file.name.indexOf(query) !== -1) {
  14339. add([file], 'change');
  14340. $.inArray(file.hash, sel) !== -1 && selectFile(file.hash);
  14341. added = true;
  14342. }
  14343. }
  14344. });
  14345. } else {
  14346. $.each($.grep(e.data.changed || [], function(f) { return f.phash == phash ? true : false; }), function(i, file) {
  14347. if ($('#'+fm.cwdHash2Id(file.hash)).length) {
  14348. remove([file.hash]);
  14349. add([file], 'change');
  14350. $.inArray(file.hash, sel) !== -1 && selectFile(file.hash);
  14351. added = true;
  14352. }
  14353. });
  14354. }
  14355. if (added) {
  14356. fm.trigger('cwdhasheschange', cwdHashes);
  14357. list && resize();
  14358. wrapper.trigger(scrollEvent);
  14359. }
  14360. trigger();
  14361. })
  14362. .remove(function(e) {
  14363. var place = list ? cwd.find('tbody') : cwd;
  14364. remove(e.data.removed || []);
  14365. trigger();
  14366. if (buffer.length < 1 && place.children(fileSelector).length < 1) {
  14367. wz.addClass('elfinder-cwd-wrapper-empty');
  14368. selectCheckbox && selectAllCheckbox.find('input').prop('checked', false);
  14369. bottomMarker.hide();
  14370. wrapper.off(scrollEvent, render);
  14371. resize();
  14372. } else {
  14373. bottomMarkerShow(place);
  14374. wrapper.trigger(scrollEvent);
  14375. }
  14376. })
  14377. // select dragged file if no selected, disable selectable
  14378. .dragstart(function(e) {
  14379. var target = $(e.data.target),
  14380. oe = e.data.originalEvent;
  14381. if (target.hasClass(clFile)) {
  14382. if (!target.hasClass(clSelected)) {
  14383. !(oe.ctrlKey || oe.metaKey || oe.shiftKey) && unselectAll({ notrigger: true });
  14384. target.trigger(evtSelect);
  14385. trigger();
  14386. }
  14387. }
  14388. cwd.removeClass(clDisabled).data('selectable') && cwd.selectable('disable');
  14389. selectLock = true;
  14390. })
  14391. // enable selectable
  14392. .dragstop(function() {
  14393. cwd.data('selectable') && cwd.selectable('enable');
  14394. selectLock = false;
  14395. })
  14396. .bind('lockfiles unlockfiles selectfiles unselectfiles', function(e) {
  14397. var events = {
  14398. lockfiles : evtDisable ,
  14399. unlockfiles : evtEnable ,
  14400. selectfiles : evtSelect,
  14401. unselectfiles : evtUnselect },
  14402. event = events[e.type],
  14403. files = e.data.files || [],
  14404. l = files.length,
  14405. helper = e.data.helper || $(),
  14406. parents, ctr, add;
  14407. if (l > 0) {
  14408. parents = fm.parents(files[0]);
  14409. }
  14410. if (event === evtSelect || event === evtUnselect) {
  14411. add = (event === evtSelect),
  14412. $.each(files, function(i, hash) {
  14413. var all = cwd.hasClass('elfinder-cwd-allselected');
  14414. if (! selectedFiles[hash]) {
  14415. add && (selectedFiles[hash] = true);
  14416. } else {
  14417. if (all) {
  14418. selectCheckbox && selectAllCheckbox.children('input').prop('checked', false);
  14419. cwd.removeClass('elfinder-cwd-allselected');
  14420. all = false;
  14421. }
  14422. ! add && delete selectedFiles[hash];
  14423. }
  14424. });
  14425. }
  14426. if (!helper.data('locked')) {
  14427. while (l--) {
  14428. try {
  14429. $('#'+fm.cwdHash2Id(files[l])).trigger(event);
  14430. } catch(e) {}
  14431. }
  14432. ! e.data.inselect && trigger();
  14433. }
  14434. if (wrapper.data('dropover') && parents.indexOf(wrapper.data('dropover')) !== -1) {
  14435. ctr = e.type !== 'lockfiles';
  14436. helper.toggleClass('elfinder-drag-helper-plus', ctr);
  14437. wrapper.toggleClass(clDropActive, ctr);
  14438. }
  14439. })
  14440. // select new files after some actions
  14441. .bind('mkdir mkfile duplicate upload rename archive extract paste multiupload', function(e) {
  14442. if (e.type == 'upload' && e.data._multiupload) return;
  14443. var phash = fm.cwd().hash, files;
  14444. unselectAll({ notrigger: true });
  14445. $.each((e.data.added || []).concat(e.data.changed || []), function(i, file) {
  14446. file && file.phash == phash && selectFile(file.hash);
  14447. });
  14448. trigger();
  14449. })
  14450. .shortcut({
  14451. pattern :'ctrl+a',
  14452. description : 'selectall',
  14453. callback : selectAll
  14454. })
  14455. .shortcut({
  14456. pattern :'ctrl+shift+i',
  14457. description : 'selectinvert',
  14458. callback : selectInvert
  14459. })
  14460. .shortcut({
  14461. pattern : 'left right up down shift+left shift+right shift+up shift+down',
  14462. description : 'selectfiles',
  14463. type : 'keydown' , //fm.UA.Firefox || fm.UA.Opera ? 'keypress' : 'keydown',
  14464. callback : function(e) { select(e.keyCode, e.shiftKey); }
  14465. })
  14466. .shortcut({
  14467. pattern : 'home',
  14468. description : 'selectffile',
  14469. callback : function(e) {
  14470. unselectAll({ notrigger: true });
  14471. scrollToView(cwd.find('[id]:first').trigger(evtSelect));
  14472. trigger();
  14473. }
  14474. })
  14475. .shortcut({
  14476. pattern : 'end',
  14477. description : 'selectlfile',
  14478. callback : function(e) {
  14479. unselectAll({ notrigger: true });
  14480. scrollToView(cwd.find('[id]:last').trigger(evtSelect)) ;
  14481. trigger();
  14482. }
  14483. })
  14484. .shortcut({
  14485. pattern : 'page_up',
  14486. description : 'pageTurning',
  14487. callback : function(e) {
  14488. if (bufferExt.itemH) {
  14489. wrapper.scrollTop(
  14490. Math.round(
  14491. wrapper.scrollTop()
  14492. - (Math.floor((wrapper.height() + (list? bufferExt.itemH * -1 : 16)) / bufferExt.itemH)) * bufferExt.itemH
  14493. )
  14494. );
  14495. }
  14496. }
  14497. }).shortcut({
  14498. pattern : 'page_down',
  14499. description : 'pageTurning',
  14500. callback : function(e) {
  14501. if (bufferExt.itemH) {
  14502. wrapper.scrollTop(
  14503. Math.round(
  14504. wrapper.scrollTop()
  14505. + (Math.floor((wrapper.height() + (list? bufferExt.itemH * -1 : 16)) / bufferExt.itemH)) * bufferExt.itemH
  14506. )
  14507. );
  14508. }
  14509. }
  14510. });
  14511. });
  14512. // fm.timeEnd('cwdLoad')
  14513. return this;
  14514. };
  14515. /*
  14516. * File: /js/ui/dialog.js
  14517. */
  14518. /**
  14519. * @class elFinder dialog
  14520. *
  14521. * @author Dmitry (dio) Levashov
  14522. **/
  14523. $.fn.elfinderdialog = function(opts, fm) {
  14524. var platformWin = (window.navigator.platform.indexOf('Win') != -1),
  14525. delta = {},
  14526. syncSize = { enabled: false, width: false, height: false, defaultSize: null },
  14527. fitSize = function(dialog) {
  14528. var opts, node;
  14529. if (syncSize.enabled) {
  14530. node = fm.options.dialogContained? elfNode : $(window);
  14531. opts = {
  14532. maxWidth : syncSize.width? node.width() - delta.width : null,
  14533. maxHeight: syncSize.height? node.height() - delta.height : null
  14534. };
  14535. Object.assign(restoreStyle, opts);
  14536. dialog.css(opts).trigger('resize');
  14537. if (dialog.data('hasResizable') && (dialog.resizable('option', 'maxWidth') < opts.maxWidth || dialog.resizable('option', 'maxHeight') < opts.maxHeight)) {
  14538. dialog.resizable('option', opts);
  14539. }
  14540. }
  14541. },
  14542. syncFunc = function(e) {
  14543. var dialog = e.data;
  14544. syncTm && clearTimeout(syncTm);
  14545. syncTm = setTimeout(function() {
  14546. var opts, offset;
  14547. if (syncSize.enabled) {
  14548. fitSize(dialog);
  14549. }
  14550. }, 50);
  14551. },
  14552. checkEditing = function() {
  14553. var cldialog = 'elfinder-dialog',
  14554. dialogs = elfNode.children('.' + cldialog + '.' + fm.res('class', 'editing') + ':visible');
  14555. fm[dialogs.length? 'disable' : 'enable']();
  14556. },
  14557. syncTm, dialog, elfNode, restoreStyle;
  14558. if (fm && fm.ui) {
  14559. elfNode = fm.getUI();
  14560. } else {
  14561. elfNode = this.closest('.elfinder');
  14562. if (! fm) {
  14563. fm = elfNode.elfinder('instance');
  14564. }
  14565. }
  14566. if (typeof opts === 'string') {
  14567. if ((dialog = this.closest('.ui-dialog')).length) {
  14568. if (opts === 'open') {
  14569. dialog.css('display') === 'none' && dialog.fadeIn(120, function() {
  14570. dialog.trigger('open');
  14571. fm.trigger('dialogopened', {dialog: dialog});
  14572. });
  14573. } else if (opts === 'close' || opts === 'destroy') {
  14574. dialog.stop(true);
  14575. if (dialog.is(':visible') || elfNode.is(':hidden')) {
  14576. dialog.trigger('close');
  14577. fm.trigger('dialogclosed', {dialog: dialog});
  14578. }
  14579. if (opts === 'destroy') {
  14580. dialog.remove();
  14581. fm.trigger('dialogremoved', {dialog: dialog});
  14582. }
  14583. } else if (opts === 'toTop') {
  14584. dialog.trigger('totop');
  14585. fm.trigger('dialogtotoped', {dialog: dialog});
  14586. } else if (opts === 'posInit') {
  14587. dialog.trigger('posinit');
  14588. fm.trigger('dialogposinited', {dialog: dialog});
  14589. } else if (opts === 'tabstopsInit') {
  14590. dialog.trigger('tabstopsInit');
  14591. fm.trigger('dialogtabstopsinited', {dialog: dialog});
  14592. } else if (opts === 'checkEditing') {
  14593. checkEditing();
  14594. }
  14595. }
  14596. return this;
  14597. }
  14598. opts = Object.assign({}, $.fn.elfinderdialog.defaults, opts);
  14599. if (opts.allowMinimize && opts.allowMinimize === 'auto') {
  14600. opts.allowMinimize = this.find('textarea,input').length? true : false;
  14601. }
  14602. if (opts.headerBtnPos && opts.headerBtnPos === 'auto') {
  14603. opts.headerBtnPos = platformWin? 'right' : 'left';
  14604. }
  14605. if (opts.headerBtnOrder && opts.headerBtnOrder === 'auto') {
  14606. opts.headerBtnOrder = platformWin? 'close:maximize:minimize' : 'close:minimize:maximize';
  14607. }
  14608. if (opts.modal && opts.allowMinimize) {
  14609. opts.allowMinimize = false;
  14610. }
  14611. if (fm.options.dialogContained) {
  14612. syncSize.width = syncSize.height = syncSize.enabled = true;
  14613. } else {
  14614. syncSize.width = (opts.maxWidth === 'window');
  14615. syncSize.height = (opts.maxHeight === 'window');
  14616. if (syncSize.width || syncSize.height) {
  14617. syncSize.enabled = true;
  14618. }
  14619. }
  14620. this.filter(':not(.ui-dialog-content)').each(function() {
  14621. var self = $(this).addClass('ui-dialog-content ui-widget-content'),
  14622. clactive = 'elfinder-dialog-active',
  14623. cldialog = 'elfinder-dialog',
  14624. clnotify = 'elfinder-dialog-notify',
  14625. clhover = 'ui-state-hover',
  14626. cltabstop = 'elfinder-tabstop',
  14627. cl1stfocus = 'elfinder-focus',
  14628. clmodal = 'elfinder-dialog-modal',
  14629. id = parseInt(Math.random()*1000000),
  14630. titlebar = $('<div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix"><span class="elfinder-dialog-title">'+opts.title+'</span></div>'),
  14631. buttonset = $('<div class="ui-dialog-buttonset"/>'),
  14632. buttonpane = $('<div class=" ui-helper-clearfix ui-dialog-buttonpane ui-widget-content"/>')
  14633. .append(buttonset),
  14634. btnWidth = 0,
  14635. btnCnt = 0,
  14636. tabstops = $(),
  14637. evCover = $('<div style="width:100%;height:100%;position:absolute;top:0px;left:0px;"/>').hide(),
  14638. numberToTel = function() {
  14639. if (opts.optimizeNumber) {
  14640. dialog.find('input[type=number]').each(function() {
  14641. $(this).attr('inputmode', 'numeric');
  14642. $(this).attr('pattern', '[0-9]*');
  14643. });
  14644. }
  14645. },
  14646. tabstopsInit = function() {
  14647. tabstops = dialog.find('.'+cltabstop);
  14648. if (tabstops.length) {
  14649. tabstops.attr('tabindex', '-1');
  14650. if (! tabstops.filter('.'+cl1stfocus).length) {
  14651. buttonset.children('.'+cltabstop+':'+(platformWin? 'first' : 'last')).addClass(cl1stfocus);
  14652. }
  14653. }
  14654. },
  14655. tabstopNext = function(cur) {
  14656. var elms = tabstops.filter(':visible'),
  14657. node = cur? null : elms.filter('.'+cl1stfocus+':first');
  14658. if (! node || ! node.length) {
  14659. node = elms.first();
  14660. }
  14661. if (cur) {
  14662. $.each(elms, function(i, elm) {
  14663. if (elm === cur && elms[i+1]) {
  14664. node = elms.eq(i+1);
  14665. return false;
  14666. }
  14667. });
  14668. }
  14669. return node;
  14670. },
  14671. tabstopPrev = function(cur) {
  14672. var elms = tabstops.filter(':visible'),
  14673. node = elms.last();
  14674. $.each(elms, function(i, elm) {
  14675. if (elm === cur && elms[i-1]) {
  14676. node = elms.eq(i-1);
  14677. return false;
  14678. }
  14679. });
  14680. return node;
  14681. },
  14682. makeHeaderBtn = function() {
  14683. $.each(opts.headerBtnOrder.split(':').reverse(), function(i, v) {
  14684. headerBtns[v] && headerBtns[v]();
  14685. });
  14686. if (platformWin) {
  14687. titlebar.children('.elfinder-titlebar-button').addClass('elfinder-titlebar-button-right');
  14688. }
  14689. },
  14690. headerBtns = {
  14691. close: function() {
  14692. 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>')
  14693. .on('mousedown', function(e) {
  14694. e.preventDefault();
  14695. e.stopPropagation();
  14696. self.elfinderdialog('close');
  14697. })
  14698. );
  14699. },
  14700. maximize: function() {
  14701. if (opts.allowMaximize) {
  14702. dialog.on('resize', function(e, data) {
  14703. var full, elm;
  14704. e.preventDefault();
  14705. e.stopPropagation();
  14706. if (data && data.maximize) {
  14707. elm = titlebar.find('.elfinder-titlebar-full');
  14708. full = (data.maximize === 'on');
  14709. elm.children('span.ui-icon')
  14710. .toggleClass('ui-icon-plusthick', ! full)
  14711. .toggleClass('ui-icon-arrowreturnthick-1-s', full);
  14712. if (full) {
  14713. try {
  14714. dialog.hasClass('ui-draggable') && dialog.draggable('disable');
  14715. dialog.hasClass('ui-resizable') && dialog.resizable('disable');
  14716. } catch(e) {}
  14717. self.css('width', '100%').css('height', dialog.height() - dialog.children('.ui-dialog-titlebar').outerHeight(true) - buttonpane.outerHeight(true));
  14718. } else {
  14719. self.attr('style', elm.data('style'));
  14720. elm.removeData('style');
  14721. posCheck();
  14722. try {
  14723. dialog.hasClass('ui-draggable') && dialog.draggable('enable');
  14724. dialog.hasClass('ui-resizable') && dialog.resizable('enable');
  14725. } catch(e) {}
  14726. }
  14727. dialog.trigger('resize');
  14728. }
  14729. });
  14730. titlebar.prepend($('<span class="ui-widget-header ui-corner-all elfinder-titlebar-button elfinder-titlebar-full"><span class="ui-icon ui-icon-plusthick"/></span>')
  14731. .on('mousedown', function(e) {
  14732. var elm = $(this);
  14733. e.preventDefault();
  14734. e.stopPropagation();
  14735. if (!dialog.hasClass('elfinder-maximized') && typeof elm.data('style') === 'undefined') {
  14736. self.height(self.height());
  14737. elm.data('style', self.attr('style') || '');
  14738. }
  14739. fm.toggleMaximize(dialog);
  14740. })
  14741. );
  14742. }
  14743. },
  14744. minimize: function() {
  14745. var mnode, doffset;
  14746. if (opts.allowMinimize) {
  14747. titlebar.on('dblclick', function(e) {
  14748. $(this).children('.elfinder-titlebar-minimize').trigger('mousedown');
  14749. })
  14750. .prepend($('<span class="ui-widget-header ui-corner-all elfinder-titlebar-button elfinder-titlebar-minimize"><span class="ui-icon ui-icon-minusthick"/></span>')
  14751. .on('mousedown', function(e) {
  14752. var $this = $(this),
  14753. tray = fm.getUI('bottomtray'),
  14754. dumStyle = { width: 70, height: 24 },
  14755. dum = $('<div/>').css(dumStyle).addClass(dialog.get(0).className + ' elfinder-dialog-minimized'),
  14756. pos = {};
  14757. e.preventDefault();
  14758. e.stopPropagation();
  14759. if (!dialog.data('minimized')) {
  14760. // minimize
  14761. doffset = dialog.data('minimized', true).position();
  14762. mnode = dialog.clone().on('mousedown', function() {
  14763. $this.trigger('mousedown');
  14764. }).removeClass('ui-draggable ui-resizable');
  14765. tray.append(dum);
  14766. Object.assign(pos, dum.offset(), dumStyle);
  14767. dum.remove();
  14768. mnode.height(dialog.height()).children('.ui-dialog-content:first').empty();
  14769. dialog.before(mnode).hide();
  14770. mnode.children('.ui-dialog-content:first,.ui-dialog-buttonpane,.ui-resizable-handle').remove();
  14771. mnode.find('.elfinder-titlebar-minimize,.elfinder-titlebar-full').remove();
  14772. mnode.find('.ui-dialog-titlebar-close').on('mousedown', function(e) {
  14773. e.stopPropagation();
  14774. e.preventDefault();
  14775. mnode.remove();
  14776. dialog.show();
  14777. self.elfinderdialog('close');
  14778. });
  14779. mnode.animate(pos, function() {
  14780. mnode.attr('style', '')
  14781. .css({ maxWidth: dialog.width() })
  14782. .addClass('elfinder-dialog-minimized')
  14783. .appendTo(tray);
  14784. checkEditing();
  14785. });
  14786. } else {
  14787. //restore
  14788. dialog.removeData('minimized').before(mnode.css(Object.assign({'position': 'absolute'}, mnode.offset())));
  14789. fm.toFront(mnode);
  14790. mnode.animate(Object.assign({ width: dialog.width(), height: dialog.height() }, doffset), function() {
  14791. dialog.show();
  14792. fm.toFront(dialog);
  14793. mnode.remove();
  14794. checkEditing();
  14795. });
  14796. }
  14797. })
  14798. );
  14799. }
  14800. }
  14801. },
  14802. dialog = $('<div class="ui-front ui-dialog ui-widget ui-widget-content ui-corner-all ui-draggable std42-dialog touch-punch '+cldialog+' '+opts.cssClass+'"/>')
  14803. .hide()
  14804. .append(self)
  14805. .appendTo(elfNode)
  14806. .draggable({
  14807. containment : fm.options.dialogContained? elfNode : null,
  14808. handle : '.ui-dialog-titlebar',
  14809. start : function() {
  14810. evCover.show();
  14811. },
  14812. drag : function(e, ui) {
  14813. var top = ui.offset.top,
  14814. left = ui.offset.left;
  14815. if (top < 0) {
  14816. ui.position.top = ui.position.top - top;
  14817. }
  14818. if (left < 0) {
  14819. ui.position.left = ui.position.left - left;
  14820. }
  14821. if (fm.options.dialogContained) {
  14822. ui.position.top < 0 && (ui.position.top = 0);
  14823. ui.position.left < 0 && (ui.position.left = 0);
  14824. }
  14825. },
  14826. stop : function(e, ui) {
  14827. evCover.hide();
  14828. dialog.css({height : opts.height});
  14829. self.data('draged', true);
  14830. }
  14831. })
  14832. .css({
  14833. width : opts.width,
  14834. height : opts.height,
  14835. minWidth : opts.minWidth,
  14836. minHeight : opts.minHeight,
  14837. maxWidth : opts.maxWidth,
  14838. maxHeight : opts.maxHeight
  14839. })
  14840. .on('touchstart touchmove touchend', function(e) {
  14841. // stopPropagation of touch events
  14842. e.stopPropagation();
  14843. })
  14844. .on('mousedown', function(e) {
  14845. setTimeout(function() {
  14846. if (dialog.is(':visible') && !dialog.hasClass('elfinder-frontmost')) {
  14847. toFocusNode = $(':focus');
  14848. if (!toFocusNode.length) {
  14849. toFocusNode = void(0);
  14850. }
  14851. dialog.trigger('totop');
  14852. }
  14853. }, 10);
  14854. })
  14855. .on('open', function() {
  14856. var d = $(this);
  14857. if (syncSize.enabled) {
  14858. if (!syncSize.defaultSize) {
  14859. syncSize.defaultSize = { width: self.width(), height: self.height() };
  14860. }
  14861. fitSize(dialog);
  14862. dialog.trigger('resize').trigger('posinit');
  14863. elfNode.on('resize.'+fm.namespace, dialog, syncFunc);
  14864. }
  14865. if (!dialog.hasClass(clnotify)) {
  14866. elfNode.children('.'+cldialog+':visible:not(.'+clnotify+')').each(function() {
  14867. var d = $(this),
  14868. top = parseInt(d.css('top')),
  14869. left = parseInt(d.css('left')),
  14870. _top = parseInt(dialog.css('top')),
  14871. _left = parseInt(dialog.css('left')),
  14872. ct = Math.abs(top - _top) < 10,
  14873. cl = Math.abs(left - _left) < 10;
  14874. if (d[0] != dialog[0] && (ct || cl)) {
  14875. dialog.css({
  14876. top : ct ? (top + 10) : _top,
  14877. left : cl ? (left + 10) : _left
  14878. });
  14879. }
  14880. });
  14881. }
  14882. if (dialog.data('modal')) {
  14883. dialog.addClass(clmodal);
  14884. fm.getUI('overlay').elfinderoverlay('show');
  14885. }
  14886. dialog.trigger('totop');
  14887. typeof(opts.open) == 'function' && $.proxy(opts.open, self[0])();
  14888. if (opts.closeOnEscape) {
  14889. $(document).on('keyup.'+id, function(e) {
  14890. if (e.keyCode == $.ui.keyCode.ESCAPE && dialog.hasClass(clactive)) {
  14891. self.elfinderdialog('close');
  14892. }
  14893. });
  14894. }
  14895. dialog.hasClass(fm.res('class', 'editing')) && checkEditing();
  14896. })
  14897. .on('close', function(e) {
  14898. var dialogs, dfd;
  14899. if (opts.beforeclose && typeof opts.beforeclose === 'function') {
  14900. dfd = opts.beforeclose();
  14901. if (!dfd || !dfd.promise) {
  14902. dfd = !dfd? $.Deferred().reject() : $.Deferred().resolve();
  14903. }
  14904. } else {
  14905. dfd = $.Deferred().resolve();
  14906. }
  14907. dfd.done(function() {
  14908. syncSize.enabled && elfNode.off('resize.'+fm.namespace, syncFunc);
  14909. if (opts.closeOnEscape) {
  14910. $(document).off('keyup.'+id);
  14911. }
  14912. if (opts.allowMaximize) {
  14913. fm.toggleMaximize(dialog, false);
  14914. }
  14915. dialog.hide().data('modal') && fm.getUI('overlay').elfinderoverlay('hide');
  14916. if (typeof(opts.close) == 'function') {
  14917. $.proxy(opts.close, self[0])();
  14918. }
  14919. if (opts.destroyOnClose && dialog.parent().length) {
  14920. dialog.hide().remove();
  14921. }
  14922. // get focus to next dialog
  14923. dialogs = elfNode.children('.'+cldialog+':visible');
  14924. if (dialogs.length) {
  14925. dialogs.filter(':last').trigger('totop');
  14926. }
  14927. dialog.hasClass(fm.res('class', 'editing')) && checkEditing();
  14928. });
  14929. })
  14930. .on('totop', function() {
  14931. var s = fm.storage('autoFocusDialog');
  14932. dialog.data('focusOnMouseOver', s? (s > 0) : fm.options.uiOptions.dialog.focusOnMouseOver);
  14933. if (dialog.data('minimized')) {
  14934. titlebar.children('.elfinder-titlebar-minimize').trigger('mousedown');
  14935. }
  14936. if (!dialog.data('modal') && fm.getUI('overlay').is(':visible')) {
  14937. fm.getUI('overlay').before(dialog);
  14938. } else {
  14939. fm.toFront(dialog);
  14940. }
  14941. elfNode.children('.'+cldialog+':not(.'+clmodal+')').removeClass(clactive);
  14942. dialog.addClass(clactive);
  14943. ! fm.UA.Mobile && (toFocusNode || tabstopNext()).trigger('focus');
  14944. toFocusNode = void(0);
  14945. })
  14946. .on('posinit', function() {
  14947. var css = opts.position,
  14948. nodeOffset, minTop, minLeft, outerSize, win, winSize, nodeFull;
  14949. if (dialog.hasClass('elfinder-maximized')) {
  14950. return;
  14951. }
  14952. if (! css && ! dialog.data('resizing')) {
  14953. nodeFull = elfNode.hasClass('elfinder-fullscreen');
  14954. dialog.css(nodeFull? {
  14955. maxWidth : '100%',
  14956. maxHeight : '100%',
  14957. overflow : 'auto'
  14958. } : restoreStyle);
  14959. if (fm.UA.Mobile && !nodeFull && dialog.data('rotated') === fm.UA.Rotated) {
  14960. return;
  14961. }
  14962. dialog.data('rotated', fm.UA.Rotated);
  14963. win = $(window);
  14964. nodeOffset = elfNode.offset();
  14965. outerSize = {
  14966. width : dialog.outerWidth(true),
  14967. height: dialog.outerHeight(true)
  14968. };
  14969. outerSize.right = nodeOffset.left + outerSize.width;
  14970. outerSize.bottom = nodeOffset.top + outerSize.height;
  14971. winSize = {
  14972. scrLeft: win.scrollLeft(),
  14973. scrTop : win.scrollTop(),
  14974. width : win.width(),
  14975. height : win.height()
  14976. };
  14977. winSize.right = winSize.scrLeft + winSize.width;
  14978. winSize.bottom = winSize.scrTop + winSize.height;
  14979. if (fm.options.dialogContained || nodeFull) {
  14980. minTop = 0;
  14981. minLeft = 0;
  14982. } else {
  14983. minTop = nodeOffset.top * -1 + winSize.scrTop;
  14984. minLeft = nodeOffset.left * -1 + winSize.scrLeft;
  14985. }
  14986. css = {
  14987. top : outerSize.height >= winSize.height? minTop : Math.max(minTop, parseInt((elfNode.height() - outerSize.height)/2 - 42)),
  14988. left : outerSize.width >= winSize.width ? minLeft : Math.max(minLeft, parseInt((elfNode.width() - outerSize.width)/2))
  14989. };
  14990. if (outerSize.right + css.left > winSize.right) {
  14991. css.left = Math.max(minLeft, winSize.right - outerSize.right);
  14992. }
  14993. if (outerSize.bottom + css.top > winSize.bottom) {
  14994. css.top = Math.max(minTop, winSize.bottom - outerSize.bottom);
  14995. }
  14996. }
  14997. if (opts.absolute) {
  14998. css.position = 'absolute';
  14999. }
  15000. css && dialog.css(css);
  15001. })
  15002. .on('resize', function(e, data) {
  15003. var oh = 0, h, minH;
  15004. if ((data && (data.minimize || data.maxmize)) || dialog.data('minimized')) {
  15005. return;
  15006. }
  15007. e.stopPropagation();
  15008. e.preventDefault();
  15009. dialog.children('.ui-widget-header,.ui-dialog-buttonpane').each(function() {
  15010. oh += $(this).outerHeight(true);
  15011. });
  15012. if (syncSize.enabled && !e.originalEvent && !dialog.hasClass('elfinder-maximized')) {
  15013. h = Math.min(syncSize.defaultSize.height, Math.max(parseInt(dialog.css('max-height')), parseInt(dialog.css('min-height'))) - oh - dialog.data('margin-y'));
  15014. } else {
  15015. h = dialog.height() - oh - dialog.data('margin-y');
  15016. }
  15017. self.height(h);
  15018. posCheck();
  15019. setTimeout(function() { // Firefox need setTimeout to get new height value
  15020. minH = self.height();
  15021. minH = (h < minH)? (minH + oh + dialog.data('margin-y')) : opts.minHeight;
  15022. dialog.css('min-height', minH);
  15023. dialog.data('hasResizable') && dialog.resizable('option', { minHeight: minH });
  15024. }, 0);
  15025. if (typeof(opts.resize) === 'function') {
  15026. $.proxy(opts.resize, self[0])(e, data);
  15027. }
  15028. })
  15029. .on('tabstopsInit', tabstopsInit)
  15030. .on('focus', '.'+cltabstop, function() {
  15031. $(this).addClass(clhover).parent('label').addClass(clhover);
  15032. this.id && $(this).parent().find('label[for='+this.id+']').addClass(clhover);
  15033. })
  15034. .on('click', 'select.'+cltabstop, function() {
  15035. var node = $(this);
  15036. node.data('keepFocus')? node.removeData('keepFocus') : node.data('keepFocus', true);
  15037. })
  15038. .on('blur', '.'+cltabstop, function() {
  15039. $(this).removeClass(clhover).removeData('keepFocus').parent('label').removeClass(clhover);
  15040. this.id && $(this).parent().find('label[for='+this.id+']').removeClass(clhover);
  15041. })
  15042. .on('mouseenter mouseleave', '.'+cltabstop, function(e) {
  15043. var $this = $(this);
  15044. if (opts.btnHoverFocus && dialog.data('focusOnMouseOver')) {
  15045. if (e.type === 'mouseenter' && ! $(':focus').data('keepFocus')) {
  15046. $this.trigger('focus');
  15047. }
  15048. } else {
  15049. $this.toggleClass(clhover, e.type == 'mouseenter');
  15050. }
  15051. })
  15052. .on('keydown', '.'+cltabstop, function(e) {
  15053. var $this = $(this);
  15054. if ($this.is(':focus')) {
  15055. e.stopPropagation();
  15056. if (e.keyCode == $.ui.keyCode.ENTER) {
  15057. e.preventDefault();
  15058. $this.trigger('click');
  15059. } else if ((e.keyCode == $.ui.keyCode.TAB && e.shiftKey) || e.keyCode == $.ui.keyCode.LEFT || e.keyCode == $.ui.keyCode.UP) {
  15060. if ($this.is('input:text') && (!(e.ctrlKey || e.metaKey) && e.keyCode == $.ui.keyCode.LEFT)) {
  15061. return;
  15062. }
  15063. if ($this.is('select') && e.keyCode != $.ui.keyCode.TAB) {
  15064. return;
  15065. }
  15066. if ($this.is('textarea') && !(e.ctrlKey || e.metaKey)) {
  15067. return;
  15068. }
  15069. e.preventDefault();
  15070. tabstopPrev(this).trigger('focus');
  15071. } else if (e.keyCode == $.ui.keyCode.TAB || e.keyCode == $.ui.keyCode.RIGHT || e.keyCode == $.ui.keyCode.DOWN) {
  15072. if ($this.is('input:text') && (!(e.ctrlKey || e.metaKey) && e.keyCode == $.ui.keyCode.RIGHT)) {
  15073. return;
  15074. }
  15075. if ($this.is('select') && e.keyCode != $.ui.keyCode.TAB) {
  15076. return;
  15077. }
  15078. if ($this.is('textarea') && !(e.ctrlKey || e.metaKey)) {
  15079. return;
  15080. }
  15081. e.preventDefault();
  15082. tabstopNext(this).trigger('focus');
  15083. }
  15084. }
  15085. })
  15086. .data({modal: opts.modal}),
  15087. posCheck = function() {
  15088. var node = fm.getUI(),
  15089. pos;
  15090. if (node.hasClass('elfinder-fullscreen')) {
  15091. pos = dialog.position();
  15092. dialog.css('top', Math.max(Math.min(Math.max(pos.top, 0), node.height() - 100), 0));
  15093. dialog.css('left', Math.max(Math.min(Math.max(pos.left, 0), node.width() - 200), 0));
  15094. }
  15095. },
  15096. maxSize, toFocusNode;
  15097. dialog.prepend(titlebar);
  15098. makeHeaderBtn();
  15099. $.each(opts.buttons, function(name, cb) {
  15100. var button = $('<button type="button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only '
  15101. +'elfinder-btncnt-'+(btnCnt++)+' '
  15102. +cltabstop
  15103. +'"><span class="ui-button-text">'+name+'</span></button>')
  15104. .on('click', $.proxy(cb, self[0]));
  15105. if (cb._cssClass) {
  15106. button.addClass(cb._cssClass);
  15107. }
  15108. if (platformWin) {
  15109. buttonset.append(button);
  15110. } else {
  15111. buttonset.prepend(button);
  15112. }
  15113. });
  15114. if (buttonset.children().length) {
  15115. dialog.append(buttonpane);
  15116. dialog.show();
  15117. buttonpane.find('button').each(function(i, btn) {
  15118. btnWidth += $(btn).outerWidth(true);
  15119. });
  15120. dialog.hide();
  15121. btnWidth += 20;
  15122. if (dialog.width() < btnWidth) {
  15123. dialog.width(btnWidth);
  15124. }
  15125. }
  15126. dialog.append(evCover);
  15127. if (syncSize.enabled) {
  15128. delta.width = dialog.outerWidth(true) - dialog.width() + ((dialog.outerWidth() - dialog.width()) / 2);
  15129. delta.height = dialog.outerHeight(true) - dialog.height() + ((dialog.outerHeight() - dialog.height()) / 2);
  15130. }
  15131. if (fm.options.dialogContained) {
  15132. maxSize = {
  15133. maxWidth: elfNode.width() - delta.width,
  15134. maxHeight: elfNode.height() - delta.height
  15135. };
  15136. opts.maxWidth = opts.maxWidth? Math.min(maxSize.maxWidth, opts.maxWidth) : maxSize.maxWidth;
  15137. opts.maxHeight = opts.maxHeight? Math.min(maxSize.maxHeight, opts.maxHeight) : maxSize.maxHeight;
  15138. dialog.css(maxSize);
  15139. }
  15140. restoreStyle = {
  15141. maxWidth : dialog.css('max-width'),
  15142. maxHeight : dialog.css('max-height'),
  15143. overflow : dialog.css('overflow')
  15144. };
  15145. dialog.trigger('posinit').data('margin-y', self.outerHeight(true) - self.height());
  15146. if (opts.resizable) {
  15147. dialog.resizable({
  15148. minWidth : opts.minWidth,
  15149. minHeight : opts.minHeight,
  15150. maxWidth : opts.maxWidth,
  15151. maxHeight : opts.maxHeight,
  15152. start : function() {
  15153. evCover.show();
  15154. if (dialog.data('resizing') !== true && dialog.data('resizing')) {
  15155. clearTimeout(dialog.data('resizing'));
  15156. }
  15157. dialog.data('resizing', true);
  15158. },
  15159. stop : function(e, ui) {
  15160. evCover.hide();
  15161. dialog.data('resizing', setTimeout(function() {
  15162. dialog.data('resizing', false);
  15163. }, 200));
  15164. if (syncSize.enabled) {
  15165. syncSize.defaultSize = { width: self.width(), height: self.height() };
  15166. }
  15167. }
  15168. }).data('hasResizable', true);
  15169. }
  15170. typeof(opts.create) == 'function' && $.proxy(opts.create, this)();
  15171. numberToTel();
  15172. tabstopsInit();
  15173. opts.autoOpen && self.elfinderdialog('open');
  15174. });
  15175. return this;
  15176. };
  15177. $.fn.elfinderdialog.defaults = {
  15178. cssClass : '',
  15179. title : '',
  15180. modal : false,
  15181. resizable : true,
  15182. autoOpen : true,
  15183. closeOnEscape : true,
  15184. destroyOnClose : false,
  15185. buttons : {},
  15186. btnHoverFocus : true,
  15187. position : null,
  15188. absolute : false,
  15189. width : 320,
  15190. height : 'auto',
  15191. minWidth : 200,
  15192. minHeight : 70,
  15193. maxWidth : null,
  15194. maxHeight : null,
  15195. allowMinimize : 'auto',
  15196. allowMaximize : false,
  15197. headerBtnPos : 'auto',
  15198. headerBtnOrder : 'auto',
  15199. optimizeNumber : true
  15200. };
  15201. /*
  15202. * File: /js/ui/fullscreenbutton.js
  15203. */
  15204. /**
  15205. * @class elFinder toolbar button to switch full scrren mode.
  15206. *
  15207. * @author Naoki Sawada
  15208. **/
  15209. $.fn.elfinderfullscreenbutton = function(cmd) {
  15210. return this.each(function() {
  15211. var button = $(this).elfinderbutton(cmd),
  15212. icon = button.children('.elfinder-button-icon');
  15213. cmd.change(function() {
  15214. var fullscreen = cmd.value;
  15215. icon.toggleClass('elfinder-button-icon-unfullscreen', fullscreen);
  15216. cmd.className = fullscreen? 'unfullscreen' : '';
  15217. });
  15218. });
  15219. };
  15220. /*
  15221. * File: /js/ui/navbar.js
  15222. */
  15223. /**
  15224. * @class elfindernav - elFinder container for diretories tree and places
  15225. *
  15226. * @author Dmitry (dio) Levashov
  15227. **/
  15228. $.fn.elfindernavbar = function(fm, opts) {
  15229. this.not('.elfinder-navbar').each(function() {
  15230. var nav = $(this).hide().addClass('ui-state-default elfinder-navbar'),
  15231. parent = nav.css('overflow', 'hidden').parent(),
  15232. wz = parent.children('.elfinder-workzone').append(nav),
  15233. delta = nav.outerHeight() - nav.height(),
  15234. ltr = fm.direction == 'ltr',
  15235. handle, swipeHandle, autoHide, setWidth, navdock,
  15236. setWzRect = function() {
  15237. var cwd = fm.getUI('cwd'),
  15238. wz = fm.getUI('workzone'),
  15239. wzRect = wz.data('rectangle'),
  15240. cwdOffset = cwd.offset();
  15241. wz.data('rectangle', Object.assign(wzRect, { cwdEdge: (fm.direction === 'ltr')? cwdOffset.left : cwdOffset.left + cwd.width() }));
  15242. };
  15243. fm.one('cssloaded', function() {
  15244. delta = nav.outerHeight() - nav.height();
  15245. }).one('opendone',function() {
  15246. handle && handle.trigger('resize');
  15247. nav.css('overflow', 'auto');
  15248. }).bind('wzresize', function() {
  15249. var navdockH = 0;
  15250. if (! navdock) {
  15251. navdock =fm.getUI('navdock');
  15252. }
  15253. navdock.width(nav.get(0).offsetWidth - 2);
  15254. if (navdock.children().length > 1) {
  15255. navdockH = navdock.outerHeight(true);
  15256. }
  15257. nav.height(wz.height() - navdockH - delta);
  15258. });
  15259. if (fm.UA.Touch) {
  15260. autoHide = fm.storage('autoHide') || {};
  15261. if (typeof autoHide.navbar === 'undefined') {
  15262. autoHide.navbar = (opts.autoHideUA && opts.autoHideUA.length > 0 && $.grep(opts.autoHideUA, function(v){ return fm.UA[v]? true : false; }).length);
  15263. fm.storage('autoHide', autoHide);
  15264. }
  15265. if (autoHide.navbar) {
  15266. fm.one('init', function() {
  15267. if (nav.children().length) {
  15268. fm.uiAutoHide.push(function(){ nav.stop(true, true).trigger('navhide', { duration: 'slow', init: true }); });
  15269. }
  15270. });
  15271. }
  15272. fm.bind('load', function() {
  15273. if (nav.children().length) {
  15274. swipeHandle = $('<div class="elfinder-navbar-swipe-handle"/>').hide().appendTo(wz);
  15275. if (swipeHandle.css('pointer-events') !== 'none') {
  15276. swipeHandle.remove();
  15277. swipeHandle = null;
  15278. }
  15279. }
  15280. });
  15281. nav.on('navshow navhide', function(e, data) {
  15282. var mode = (e.type === 'navshow')? 'show' : 'hide',
  15283. duration = (data && data.duration)? data.duration : 'fast',
  15284. handleW = (data && data.handleW)? data.handleW : Math.max(50, fm.getUI().width() / 10);
  15285. nav.stop(true, true)[mode]({
  15286. duration: duration,
  15287. step : function() {
  15288. fm.trigger('wzresize');
  15289. },
  15290. complete: function() {
  15291. if (swipeHandle) {
  15292. if (mode === 'show') {
  15293. swipeHandle.stop(true, true).hide();
  15294. } else {
  15295. swipeHandle.width(handleW? handleW : '');
  15296. fm.resources.blink(swipeHandle, 'slowonce');
  15297. }
  15298. }
  15299. fm.trigger('navbar'+ mode);
  15300. data.init && fm.trigger('uiautohide');
  15301. setWzRect();
  15302. }
  15303. });
  15304. autoHide.navbar = (mode !== 'show');
  15305. fm.storage('autoHide', Object.assign(fm.storage('autoHide'), {navbar: autoHide.navbar}));
  15306. }).on('touchstart', function(e) {
  15307. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  15308. e.originalEvent._preventSwipeX = true;
  15309. }
  15310. });
  15311. }
  15312. if (! fm.UA.Mobile) {
  15313. handle = nav.resizable({
  15314. handles : ltr ? 'e' : 'w',
  15315. minWidth : opts.minWidth || 150,
  15316. maxWidth : opts.maxWidth || 500,
  15317. resize : function() {
  15318. fm.trigger('wzresize');
  15319. },
  15320. stop : function(e, ui) {
  15321. fm.storage('navbarWidth', ui.size.width);
  15322. setWzRect();
  15323. }
  15324. })
  15325. .on('resize scroll', function(e) {
  15326. e.preventDefault();
  15327. e.stopPropagation();
  15328. if (! ltr && e.type === 'resize') {
  15329. nav.css('left', 0);
  15330. }
  15331. clearTimeout($(this).data('posinit'));
  15332. $(this).data('posinit', setTimeout(function() {
  15333. var offset = (fm.UA.Opera && nav.scrollLeft())? 20 : 2;
  15334. handle.css('top', 0).css({
  15335. top : parseInt(nav.scrollTop())+'px',
  15336. left : ltr ? 'auto' : parseInt(nav.scrollRight() - offset) * -1,
  15337. right: ltr ? parseInt(nav.scrollLeft() - offset) * -1 : 'auto'
  15338. });
  15339. if (e.type === 'resize') {
  15340. fm.getUI('cwd').trigger('resize');
  15341. }
  15342. }, 50));
  15343. })
  15344. .children('.ui-resizable-handle').addClass('ui-front');
  15345. }
  15346. if (setWidth = fm.storage('navbarWidth')) {
  15347. nav.width(setWidth);
  15348. } else {
  15349. if (fm.UA.Mobile) {
  15350. fm.one('cssloaded', function() {
  15351. var set = function() {
  15352. setWidth = nav.parent().width() / 2;
  15353. if (nav.data('defWidth') > setWidth) {
  15354. nav.width(setWidth);
  15355. } else {
  15356. nav.width(nav.data('defWidth'));
  15357. }
  15358. nav.data('width', nav.width());
  15359. fm.trigger('wzresize');
  15360. };
  15361. nav.data('defWidth', nav.width());
  15362. $(window).on('resize.' + fm.namespace, set);
  15363. set();
  15364. });
  15365. }
  15366. }
  15367. });
  15368. return this;
  15369. };
  15370. /*
  15371. * File: /js/ui/navdock.js
  15372. */
  15373. /**
  15374. * @class elfindernavdock - elFinder container for preview etc at below the navbar
  15375. *
  15376. * @author Naoki Sawada
  15377. **/
  15378. $.fn.elfindernavdock = function(fm, opts) {
  15379. this.not('.elfinder-navdock').each(function() {
  15380. var self = $(this).hide().addClass('ui-state-default elfinder-navdock touch-punch'),
  15381. node = self.parent(),
  15382. wz = node.children('.elfinder-workzone').append(self),
  15383. resize = function(to, h) {
  15384. var curH = h || self.height(),
  15385. diff = to - curH,
  15386. len = Object.keys(sizeSyncs).length,
  15387. calc = len? diff / len : 0,
  15388. ovf;
  15389. if (diff) {
  15390. ovf = self.css('overflow');
  15391. self.css('overflow', 'hidden');
  15392. self.height(to);
  15393. $.each(sizeSyncs, function(id, n) {
  15394. n.height(n.height() + calc).trigger('resize.' + fm.namespace);
  15395. });
  15396. fm.trigger('wzresize');
  15397. self.css('overflow', ovf);
  15398. }
  15399. },
  15400. handle = $('<div class="ui-front ui-resizable-handle ui-resizable-n"/>').appendTo(self),
  15401. sizeSyncs = {},
  15402. resizeFn = [],
  15403. initMaxHeight = (parseInt(opts.initMaxHeight) || 50) / 100,
  15404. maxHeight = (parseInt(opts.maxHeight) || 90) / 100,
  15405. basicHeight, hasNode;
  15406. self.data('addNode', function(cNode, opts) {
  15407. var wzH = fm.getUI('workzone').height(),
  15408. imaxH = wzH * initMaxHeight,
  15409. curH, tH, mH;
  15410. opts = Object.assign({
  15411. first: false,
  15412. sizeSync: true,
  15413. init: false
  15414. }, opts);
  15415. if (!cNode.attr('id')) {
  15416. cNode.attr('id', fm.namespace+'-navdock-' + (+new Date()));
  15417. }
  15418. opts.sizeSync && (sizeSyncs[cNode.attr('id')] = cNode);
  15419. curH = self.height();
  15420. tH = curH + cNode.outerHeight(true);
  15421. if (opts.first) {
  15422. handle.after(cNode);
  15423. } else {
  15424. self.append(cNode);
  15425. }
  15426. hasNode = true;
  15427. self.resizable('enable').height(tH).show();
  15428. fm.trigger('wzresize');
  15429. if (opts.init) {
  15430. mH = fm.storage('navdockHeight');
  15431. if (mH) {
  15432. tH = mH;
  15433. } else {
  15434. tH = tH > imaxH? imaxH : tH;
  15435. }
  15436. basicHeight = tH;
  15437. }
  15438. resize(Math.min(tH, wzH * maxHeight));
  15439. return self;
  15440. }).data('removeNode', function(nodeId, appendTo) {
  15441. var cNode = $('#'+nodeId);
  15442. delete sizeSyncs[nodeId];
  15443. self.height(self.height() - $('#'+nodeId).outerHeight(true));
  15444. if (appendTo) {
  15445. if (appendTo === 'detach') {
  15446. cNode = cNode.detach();
  15447. } else {
  15448. appendTo.append(cNode);
  15449. }
  15450. } else {
  15451. cNode.remove();
  15452. }
  15453. if (self.children().length <= 1) {
  15454. hasNode = false;
  15455. self.resizable('disable').height(0).hide();
  15456. }
  15457. fm.trigger('wzresize');
  15458. return cNode;
  15459. });
  15460. if (! opts.disabled) {
  15461. fm.one('init', function() {
  15462. var ovf;
  15463. if (fm.getUI('navbar').children().not('.ui-resizable-handle').length) {
  15464. self.data('dockEnabled', true);
  15465. self.resizable({
  15466. maxHeight: fm.getUI('workzone').height() * maxHeight,
  15467. handles: { n: handle },
  15468. start: function(e, ui) {
  15469. ovf = self.css('overflow');
  15470. self.css('overflow', 'hidden');
  15471. fm.trigger('navdockresizestart', {event: e, ui: ui}, true);
  15472. },
  15473. resize: function(e, ui) {
  15474. self.css('top', '');
  15475. fm.trigger('wzresize', { inNavdockResize : true });
  15476. },
  15477. stop: function(e, ui) {
  15478. fm.trigger('navdockresizestop', {event: e, ui: ui}, true);
  15479. self.css('top', '');
  15480. basicHeight = ui.size.height;
  15481. fm.storage('navdockHeight', basicHeight);
  15482. resize(basicHeight, ui.originalSize.height);
  15483. self.css('overflow', ovf);
  15484. }
  15485. });
  15486. fm.bind('wzresize', function(e) {
  15487. var minH, maxH, h;
  15488. if (self.is(':visible')) {
  15489. maxH = fm.getUI('workzone').height() * maxHeight;
  15490. if (! e.data || ! e.data.inNavdockResize) {
  15491. h = self.height();
  15492. if (maxH < basicHeight) {
  15493. if (Math.abs(h - maxH) > 1) {
  15494. resize(maxH);
  15495. }
  15496. } else {
  15497. if (Math.abs(h - basicHeight) > 1) {
  15498. resize(basicHeight);
  15499. }
  15500. }
  15501. }
  15502. self.resizable('option', 'maxHeight', maxH);
  15503. }
  15504. });
  15505. }
  15506. fm.bind('navbarshow navbarhide', function(e) {
  15507. self[hasNode && e.type === 'navbarshow'? 'show' : 'hide']();
  15508. });
  15509. });
  15510. }
  15511. });
  15512. return this;
  15513. };
  15514. /*
  15515. * File: /js/ui/overlay.js
  15516. */
  15517. $.fn.elfinderoverlay = function(opts) {
  15518. var fm = this.parent().elfinder('instance'),
  15519. o, cnt, show, hide;
  15520. this.filter(':not(.elfinder-overlay)').each(function() {
  15521. opts = Object.assign({}, opts);
  15522. $(this).addClass('ui-front ui-widget-overlay elfinder-overlay')
  15523. .hide()
  15524. .on('mousedown', function(e) {
  15525. e.preventDefault();
  15526. e.stopPropagation();
  15527. })
  15528. .data({
  15529. cnt : 0,
  15530. show : typeof(opts.show) == 'function' ? opts.show : function() { },
  15531. hide : typeof(opts.hide) == 'function' ? opts.hide : function() { }
  15532. });
  15533. });
  15534. if (opts == 'show') {
  15535. o = this.eq(0);
  15536. cnt = o.data('cnt') + 1;
  15537. show = o.data('show');
  15538. fm.toFront(o);
  15539. o.data('cnt', cnt);
  15540. if (o.is(':hidden')) {
  15541. o.show();
  15542. show();
  15543. }
  15544. }
  15545. if (opts == 'hide') {
  15546. o = this.eq(0);
  15547. cnt = o.data('cnt') - 1;
  15548. hide = o.data('hide');
  15549. o.data('cnt', cnt);
  15550. if (cnt <= 0) {
  15551. o.hide();
  15552. hide();
  15553. }
  15554. }
  15555. return this;
  15556. };
  15557. /*
  15558. * File: /js/ui/panel.js
  15559. */
  15560. $.fn.elfinderpanel = function(fm) {
  15561. return this.each(function() {
  15562. var panel = $(this).addClass('elfinder-panel ui-state-default ui-corner-all'),
  15563. margin = 'margin-'+(fm.direction == 'ltr' ? 'left' : 'right');
  15564. fm.one('load', function(e) {
  15565. var navbar = fm.getUI('navbar');
  15566. panel.css(margin, parseInt(navbar.outerWidth(true)));
  15567. navbar.on('resize', function(e) {
  15568. e.preventDefault();
  15569. e.stopPropagation();
  15570. panel.is(':visible') && panel.css(margin, parseInt(navbar.outerWidth(true)));
  15571. });
  15572. });
  15573. });
  15574. };
  15575. /*
  15576. * File: /js/ui/path.js
  15577. */
  15578. /**
  15579. * @class elFinder ui
  15580. * Display current folder path in statusbar.
  15581. * Click on folder name in path - open folder
  15582. *
  15583. * @author Dmitry (dio) Levashov
  15584. **/
  15585. $.fn.elfinderpath = function(fm, options) {
  15586. return this.each(function() {
  15587. var query = '',
  15588. target = '',
  15589. mimes = [],
  15590. place = 'statusbar',
  15591. clHover= fm.res('class', 'hover'),
  15592. prefix = 'path' + (elFinder.prototype.uniqueid? elFinder.prototype.uniqueid : '') + '-',
  15593. wzbase = $('<div class="ui-widget-header ui-helper-clearfix elfinder-workzone-path"/>'),
  15594. path = $(this).addClass('elfinder-path').html('&nbsp;')
  15595. .on('mousedown', 'span.elfinder-path-dir', function(e) {
  15596. var hash = $(this).attr('id').substr(prefix.length);
  15597. e.preventDefault();
  15598. if (hash != fm.cwd().hash) {
  15599. $(this).addClass(clHover);
  15600. if (query) {
  15601. fm.exec('search', query, { target: hash, mime: mimes.join(' ') });
  15602. } else {
  15603. fm.trigger('select', {selected : [hash]}).exec('open', hash);
  15604. }
  15605. }
  15606. })
  15607. .prependTo(fm.getUI('statusbar').show()),
  15608. roots = $('<div class="elfinder-path-roots"/>').on('click', function(e) {
  15609. e.stopPropagation();
  15610. e.preventDefault();
  15611. var roots = $.map(fm.roots, function(h) { return fm.file(h); }),
  15612. raw = [];
  15613. $.each(roots, function(i, f) {
  15614. if (! f.phash && fm.root(fm.cwd().hash, true) !== f.hash) {
  15615. raw.push({
  15616. label : fm.escape(f.i18 || f.name),
  15617. icon : 'home',
  15618. callback : function() { fm.exec('open', f.hash); },
  15619. options : {
  15620. iconClass : f.csscls || '',
  15621. iconImg : f.icon || ''
  15622. }
  15623. });
  15624. }
  15625. });
  15626. fm.trigger('contextmenu', {
  15627. raw: raw,
  15628. x: e.pageX,
  15629. y: e.pageY
  15630. });
  15631. }).append('<span class="elfinder-button-icon elfinder-button-icon-menu" />').appendTo(wzbase),
  15632. render = function(cwd) {
  15633. var dirs = [],
  15634. names = [];
  15635. $.each(fm.parents(cwd), function(i, hash) {
  15636. var c = (cwd === hash)? 'elfinder-path-dir elfinder-path-cwd' : 'elfinder-path-dir',
  15637. f = fm.file(hash),
  15638. name = fm.escape(f.i18 || f.name);
  15639. names.push(name);
  15640. dirs.push('<span id="'+prefix+hash+'" class="'+c+'" title="'+names.join(fm.option('separator'))+'">'+name+'</span>');
  15641. });
  15642. return dirs.join('<span class="elfinder-path-other">'+fm.option('separator')+'</span>');
  15643. },
  15644. toWorkzone = function() {
  15645. var prev;
  15646. path.children('span.elfinder-path-dir').attr('style', '');
  15647. prev = fm.direction === 'ltr'? $('#'+prefix + fm.cwd().hash).prevAll('span.elfinder-path-dir:first') : $();
  15648. path.scrollLeft(prev.length? prev.position().left : 0);
  15649. },
  15650. fit = function() {
  15651. var dirs = path.children('span.elfinder-path-dir'),
  15652. cnt = dirs.length,
  15653. m, bg = 0, ids;
  15654. if (place === 'workzone' || cnt < 2) {
  15655. dirs.attr('style', '');
  15656. return;
  15657. }
  15658. path.width(path.css('max-width'));
  15659. dirs.css({maxWidth: (100/cnt)+'%', display: 'inline-block'});
  15660. m = path.width() - 9;
  15661. path.children('span.elfinder-path-other').each(function() {
  15662. m -= $(this).width();
  15663. });
  15664. ids = [];
  15665. dirs.each(function(i) {
  15666. var dir = $(this),
  15667. w = dir.width();
  15668. m -= w;
  15669. if (w < this.scrollWidth) {
  15670. ids.push(i);
  15671. }
  15672. });
  15673. path.width('');
  15674. if (ids.length) {
  15675. if (m > 0) {
  15676. m = m / ids.length;
  15677. $.each(ids, function(i, k) {
  15678. var d = $(dirs[k]);
  15679. d.css('max-width', d.width() + m);
  15680. });
  15681. }
  15682. dirs.last().attr('style', '');
  15683. } else {
  15684. dirs.attr('style', '');
  15685. }
  15686. },
  15687. hasUiTree;
  15688. fm.one('init', function() {
  15689. hasUiTree = fm.getUI('tree').length;
  15690. if (! hasUiTree && options.toWorkzoneWithoutNavbar) {
  15691. wzbase.append(path).insertBefore(fm.getUI('workzone'));
  15692. place = 'workzone';
  15693. fm.bind('open', toWorkzone)
  15694. .one('opendone', function() {
  15695. fm.getUI().trigger('resize');
  15696. });
  15697. }
  15698. })
  15699. .bind('open searchend parents', function() {
  15700. var dirs = [];
  15701. query = '';
  15702. target = '';
  15703. mimes = [];
  15704. path.html(render(fm.cwd().hash));
  15705. if (Object.keys(fm.roots).length > 1) {
  15706. path.css('margin', '');
  15707. roots.show();
  15708. } else {
  15709. path.css('margin', 0);
  15710. roots.hide();
  15711. }
  15712. fit();
  15713. })
  15714. .bind('searchstart', function(e) {
  15715. if (e.data) {
  15716. query = e.data.query || '';
  15717. target = e.data.target || '';
  15718. mimes = e.data.mimes || [];
  15719. }
  15720. })
  15721. .bind('search', function(e) {
  15722. var dirs = [],
  15723. html = '';
  15724. if (target) {
  15725. html = render(target);
  15726. } else {
  15727. html = fm.i18n('btnAll');
  15728. }
  15729. path.html('<span class="elfinder-path-other">'+fm.i18n('searcresult') + ': </span>' + html);
  15730. fit();
  15731. })
  15732. // on swipe to navbar show/hide
  15733. .bind('navbarshow navbarhide', function() {
  15734. var wz = fm.getUI('workzone');
  15735. if (this.type === 'navbarshow') {
  15736. fm.unbind('open', toWorkzone);
  15737. path.prependTo(fm.getUI('statusbar'));
  15738. wzbase.detach();
  15739. place = 'statusbar';
  15740. } else {
  15741. wzbase.append(path).insertBefore(wz);
  15742. place = 'workzone';
  15743. toWorkzone();
  15744. fm.bind('open', toWorkzone);
  15745. }
  15746. fm.trigger('uiresize');
  15747. })
  15748. .bind('resize', fit);
  15749. });
  15750. };
  15751. /*
  15752. * File: /js/ui/places.js
  15753. */
  15754. /**
  15755. * @class elFinder places/favorites ui
  15756. *
  15757. * @author Dmitry (dio) Levashov
  15758. * @author Naoki Sawada
  15759. **/
  15760. $.fn.elfinderplaces = function(fm, opts) {
  15761. return this.each(function() {
  15762. var dirs = {},
  15763. c = 'class',
  15764. navdir = fm.res(c, 'navdir'),
  15765. collapsed = fm.res(c, 'navcollapse'),
  15766. expanded = fm.res(c, 'navexpand'),
  15767. hover = fm.res(c, 'hover'),
  15768. clroot = fm.res(c, 'treeroot'),
  15769. dropover = fm.res(c, 'adroppable'),
  15770. tpl = fm.res('tpl', 'placedir'),
  15771. ptpl = fm.res('tpl', 'perms'),
  15772. spinner = $(fm.res('tpl', 'navspinner')),
  15773. suffix = opts.suffix? opts.suffix : '',
  15774. key = 'places' + suffix,
  15775. menuTimer = null,
  15776. /**
  15777. * Convert places dir node into dir hash
  15778. *
  15779. * @param String directory id
  15780. * @return String
  15781. **/
  15782. id2hash = function(id) { return id.substr(6); },
  15783. /**
  15784. * Convert places dir node into dir hash
  15785. *
  15786. * @param String directory id
  15787. * @return String
  15788. **/
  15789. hash2id = function(hash) { return 'place-'+hash; },
  15790. /**
  15791. * Save current places state
  15792. *
  15793. * @return void
  15794. **/
  15795. save = function() {
  15796. var hashes = [], data = {};
  15797. hashes = $.map(subtree.children().find('[id]'), function(n) {
  15798. return id2hash(n.id);
  15799. });
  15800. if (hashes.length) {
  15801. $.each(hashes.reverse(), function(i, h) {
  15802. data[h] = dirs[h];
  15803. });
  15804. } else {
  15805. data = null;
  15806. }
  15807. fm.storage(key, data);
  15808. },
  15809. /**
  15810. * Init dir at places
  15811. *
  15812. * @return void
  15813. **/
  15814. init = function() {
  15815. var dat, hashes;
  15816. key = 'places'+(opts.suffix? opts.suffix : ''),
  15817. dirs = {};
  15818. dat = fm.storage(key);
  15819. if (typeof dat === 'string') {
  15820. // old data type elFinder <= 2.1.12
  15821. dat = $.grep(dat.split(','), function(hash) { return hash? true : false;});
  15822. $.each(dat, function(i, d) {
  15823. var dir = d.split('#');
  15824. dirs[dir[0]] = dir[1]? dir[1] : dir[0];
  15825. });
  15826. } else if ($.isPlainObject(dat)) {
  15827. dirs = dat;
  15828. }
  15829. // allow modify `dirs`
  15830. /**
  15831. * example for preset places
  15832. *
  15833. * elfinderInstance.bind('placesload', function(e, fm) {
  15834. * //if (fm.storage(e.data.storageKey) === null) { // for first time only
  15835. * if (!fm.storage(e.data.storageKey)) { // for empty places
  15836. * e.data.dirs[targetHash] = fallbackName; // preset folder
  15837. * }
  15838. * }
  15839. **/
  15840. fm.trigger('placesload', {dirs: dirs, storageKey: key}, true);
  15841. hashes = Object.keys(dirs);
  15842. if (hashes.length) {
  15843. root.prepend(spinner);
  15844. fm.request({
  15845. data : {cmd : 'info', targets : hashes},
  15846. preventDefault : true
  15847. })
  15848. .done(function(data) {
  15849. var exists = {};
  15850. data.files && data.files.length && fm.cache(data.files);
  15851. $.each(data.files, function(i, f) {
  15852. var hash = f.hash;
  15853. exists[hash] = f;
  15854. });
  15855. $.each(dirs, function(h, f) {
  15856. add(exists[h] || Object.assign({notfound: true}, f));
  15857. });
  15858. if (fm.storage('placesState') > 0) {
  15859. root.trigger('click');
  15860. }
  15861. })
  15862. .always(function() {
  15863. spinner.remove();
  15864. });
  15865. }
  15866. },
  15867. /**
  15868. * Return node for given dir object
  15869. *
  15870. * @param Object directory object
  15871. * @return jQuery
  15872. **/
  15873. create = function(dir, hash) {
  15874. return $(tpl.replace(/\{id\}/, hash2id(dir? dir.hash : hash))
  15875. .replace(/\{name\}/, fm.escape(dir? dir.i18 || dir.name : hash))
  15876. .replace(/\{cssclass\}/, dir? (fm.perms2class(dir) + (dir.notfound? ' elfinder-na' : '') + (dir.csscls? ' '+dir.csscls : '')) : '')
  15877. .replace(/\{permissions\}/, (dir && (!dir.read || !dir.write || dir.notfound))? ptpl : '')
  15878. .replace(/\{title\}/, (dir && dir.path)? fm.escape(dir.path) : '')
  15879. .replace(/\{symlink\}/, '')
  15880. .replace(/\{style\}/, (dir && dir.icon)? fm.getIconStyle(dir) : ''));
  15881. },
  15882. /**
  15883. * Add new node into places
  15884. *
  15885. * @param Object directory object
  15886. * @return void
  15887. **/
  15888. add = function(dir) {
  15889. var node, hash;
  15890. if (dir.mime !== 'directory') {
  15891. return false;
  15892. }
  15893. hash = dir.hash;
  15894. if (!fm.files().hasOwnProperty(hash)) {
  15895. // update cache
  15896. fm.trigger('tree', {tree: [dir]});
  15897. }
  15898. node = create(dir, hash);
  15899. dirs[hash] = dir;
  15900. subtree.prepend(node);
  15901. root.addClass(collapsed);
  15902. sortBtn.toggle(subtree.children().length > 1);
  15903. return true;
  15904. },
  15905. /**
  15906. * Remove dir from places
  15907. *
  15908. * @param String directory hash
  15909. * @return String removed name
  15910. **/
  15911. remove = function(hash) {
  15912. var name = null, tgt, cnt;
  15913. if (dirs[hash]) {
  15914. delete dirs[hash];
  15915. tgt = $('#'+hash2id(hash));
  15916. if (tgt.length) {
  15917. name = tgt.text();
  15918. tgt.parent().remove();
  15919. cnt = subtree.children().length;
  15920. sortBtn.toggle(cnt > 1);
  15921. if (! cnt) {
  15922. root.removeClass(collapsed);
  15923. places.removeClass(expanded);
  15924. subtree.slideToggle(false);
  15925. }
  15926. }
  15927. }
  15928. return name;
  15929. },
  15930. /**
  15931. * Move up dir on places
  15932. *
  15933. * @param String directory hash
  15934. * @return void
  15935. **/
  15936. moveup = function(hash) {
  15937. var self = $('#'+hash2id(hash)),
  15938. tgt = self.parent(),
  15939. prev = tgt.prev('div'),
  15940. cls = 'ui-state-hover',
  15941. ctm = fm.getUI('contextmenu');
  15942. menuTimer && clearTimeout(menuTimer);
  15943. if (prev.length) {
  15944. ctm.find(':first').data('placesHash', hash);
  15945. self.addClass(cls);
  15946. tgt.insertBefore(prev);
  15947. prev = tgt.prev('div');
  15948. menuTimer = setTimeout(function() {
  15949. self.removeClass(cls);
  15950. if (ctm.find(':first').data('placesHash') === hash) {
  15951. ctm.hide().empty();
  15952. }
  15953. }, 1500);
  15954. }
  15955. if(!prev.length) {
  15956. self.removeClass(cls);
  15957. ctm.hide().empty();
  15958. }
  15959. },
  15960. /**
  15961. * Update dir at places
  15962. *
  15963. * @param Object directory
  15964. * @param String previous hash
  15965. * @return Boolean
  15966. **/
  15967. update = function(dir, preHash) {
  15968. var hash = dir.hash,
  15969. tgt = $('#'+hash2id(preHash || hash)),
  15970. node = create(dir, hash);
  15971. if (tgt.length > 0) {
  15972. tgt.parent().replaceWith(node);
  15973. dirs[hash] = dir;
  15974. return true;
  15975. } else {
  15976. return false;
  15977. }
  15978. },
  15979. /**
  15980. * Remove all dir from places
  15981. *
  15982. * @return void
  15983. **/
  15984. clear = function() {
  15985. subtree.empty();
  15986. root.removeClass(collapsed);
  15987. places.removeClass(expanded);
  15988. subtree.slideToggle(false);
  15989. },
  15990. /**
  15991. * Sort places dirs A-Z
  15992. *
  15993. * @return void
  15994. **/
  15995. sort = function() {
  15996. $.each(dirs, function(h, f) {
  15997. var dir = fm.file(h) || f,
  15998. node = create(dir, h),
  15999. ret = null;
  16000. if (!dir) {
  16001. node.hide();
  16002. }
  16003. if (subtree.children().length) {
  16004. $.each(subtree.children(), function() {
  16005. var current = $(this);
  16006. if ((dir.i18 || dir.name).localeCompare(current.children('.'+navdir).text()) < 0) {
  16007. ret = !node.insertBefore(current);
  16008. return ret;
  16009. }
  16010. });
  16011. if (ret !== null) {
  16012. return true;
  16013. }
  16014. }
  16015. !$('#'+hash2id(h)).length && subtree.append(node);
  16016. });
  16017. save();
  16018. },
  16019. // sort button
  16020. sortBtn = $('<span class="elfinder-button-icon elfinder-button-icon-sort elfinder-places-root-icon" title="'+fm.i18n('cmdsort')+'"/>')
  16021. .hide()
  16022. .on('click', function(e) {
  16023. e.stopPropagation();
  16024. subtree.empty();
  16025. sort();
  16026. }
  16027. ),
  16028. /**
  16029. * Node - wrapper for places root
  16030. *
  16031. * @type jQuery
  16032. **/
  16033. wrapper = create({
  16034. hash : 'root-'+fm.namespace,
  16035. name : fm.i18n(opts.name, 'places'),
  16036. read : true,
  16037. write : true
  16038. }),
  16039. /**
  16040. * Places root node
  16041. *
  16042. * @type jQuery
  16043. **/
  16044. root = wrapper.children('.'+navdir)
  16045. .addClass(clroot)
  16046. .on('click', function(e) {
  16047. e.stopPropagation();
  16048. if (root.hasClass(collapsed)) {
  16049. places.toggleClass(expanded);
  16050. subtree.slideToggle();
  16051. fm.storage('placesState', places.hasClass(expanded)? 1 : 0);
  16052. }
  16053. })
  16054. .append(sortBtn),
  16055. /**
  16056. * Container for dirs
  16057. *
  16058. * @type jQuery
  16059. **/
  16060. subtree = wrapper.children('.'+fm.res(c, 'navsubtree')),
  16061. /**
  16062. * Main places container
  16063. *
  16064. * @type jQuery
  16065. **/
  16066. places = $(this).addClass(fm.res(c, 'tree')+' elfinder-places ui-corner-all')
  16067. .hide()
  16068. .append(wrapper)
  16069. .appendTo(fm.getUI('navbar'))
  16070. .on('mouseenter mouseleave', '.'+navdir, function(e) {
  16071. $(this).toggleClass('ui-state-hover', (e.type == 'mouseenter'));
  16072. })
  16073. .on('click', '.'+navdir, function(e) {
  16074. var p = $(this);
  16075. if (p.data('longtap')) {
  16076. e.stopPropagation();
  16077. return;
  16078. }
  16079. ! p.hasClass('elfinder-na') && fm.exec('open', p.attr('id').substr(6));
  16080. })
  16081. .on('contextmenu', '.'+navdir+':not(.'+clroot+')', function(e) {
  16082. var self = $(this),
  16083. hash = self.attr('id').substr(6);
  16084. e.preventDefault();
  16085. fm.trigger('contextmenu', {
  16086. raw : [{
  16087. label : fm.i18n('moveUp'),
  16088. icon : 'up',
  16089. remain : true,
  16090. callback : function() { moveup(hash); save(); }
  16091. },'|',{
  16092. label : fm.i18n('rmFromPlaces'),
  16093. icon : 'rm',
  16094. callback : function() { remove(hash); save(); }
  16095. }],
  16096. 'x' : e.pageX,
  16097. 'y' : e.pageY
  16098. });
  16099. self.addClass('ui-state-hover');
  16100. fm.getUI('contextmenu').children().on('mouseenter', function() {
  16101. self.addClass('ui-state-hover');
  16102. });
  16103. fm.bind('closecontextmenu', function() {
  16104. self.removeClass('ui-state-hover');
  16105. });
  16106. })
  16107. .droppable({
  16108. tolerance : 'pointer',
  16109. accept : '.elfinder-cwd-file-wrapper,.elfinder-tree-dir,.elfinder-cwd-file',
  16110. hoverClass : fm.res('class', 'adroppable'),
  16111. classes : { // Deprecated hoverClass jQueryUI>=1.12.0
  16112. 'ui-droppable-hover': fm.res('class', 'adroppable')
  16113. },
  16114. over : function(e, ui) {
  16115. var helper = ui.helper,
  16116. dir = $.grep(helper.data('files'), function(h) { return (fm.file(h).mime === 'directory' && !dirs[h])? true : false; });
  16117. e.stopPropagation();
  16118. helper.data('dropover', helper.data('dropover') + 1);
  16119. if (fm.insideWorkzone(e.pageX, e.pageY)) {
  16120. if (dir.length > 0) {
  16121. helper.addClass('elfinder-drag-helper-plus');
  16122. fm.trigger('unlockfiles', {files : helper.data('files'), helper: helper});
  16123. } else {
  16124. $(this).removeClass(dropover);
  16125. }
  16126. }
  16127. },
  16128. out : function(e, ui) {
  16129. var helper = ui.helper;
  16130. e.stopPropagation();
  16131. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  16132. $(this).removeData('dropover')
  16133. .removeClass(dropover);
  16134. },
  16135. drop : function(e, ui) {
  16136. var helper = ui.helper,
  16137. resolve = true;
  16138. $.each(helper.data('files'), function(i, hash) {
  16139. var dir = fm.file(hash);
  16140. if (dir && dir.mime == 'directory' && !dirs[dir.hash]) {
  16141. add(dir);
  16142. } else {
  16143. resolve = false;
  16144. }
  16145. });
  16146. save();
  16147. resolve && helper.hide();
  16148. }
  16149. })
  16150. // for touch device
  16151. .on('touchstart', '.'+navdir+':not(.'+clroot+')', function(e) {
  16152. if (e.originalEvent.touches.length > 1) {
  16153. return;
  16154. }
  16155. var hash = $(this).attr('id').substr(6),
  16156. p = $(this)
  16157. .addClass(hover)
  16158. .data('longtap', null)
  16159. .data('tmlongtap', setTimeout(function(){
  16160. // long tap
  16161. p.data('longtap', true);
  16162. fm.trigger('contextmenu', {
  16163. raw : [{
  16164. label : fm.i18n('rmFromPlaces'),
  16165. icon : 'rm',
  16166. callback : function() { remove(hash); save(); }
  16167. }],
  16168. 'x' : e.originalEvent.touches[0].pageX,
  16169. 'y' : e.originalEvent.touches[0].pageY
  16170. });
  16171. }, 500));
  16172. })
  16173. .on('touchmove touchend', '.'+navdir+':not(.'+clroot+')', function(e) {
  16174. clearTimeout($(this).data('tmlongtap'));
  16175. if (e.type == 'touchmove') {
  16176. $(this).removeClass(hover);
  16177. }
  16178. });
  16179. if ($.fn.sortable) {
  16180. subtree.addClass('touch-punch')
  16181. .sortable({
  16182. appendTo : fm.getUI(),
  16183. revert : false,
  16184. helper : function(e) {
  16185. var dir = $(e.target).parent();
  16186. dir.children().removeClass('ui-state-hover');
  16187. return $('<div class="ui-widget elfinder-place-drag elfinder-'+fm.direction+'"/>')
  16188. .append($('<div class="elfinder-navbar"/>').show().append(dir.clone()));
  16189. },
  16190. stop : function(e, ui) {
  16191. var target = $(ui.item[0]),
  16192. top = places.offset().top,
  16193. left = places.offset().left,
  16194. width = places.width(),
  16195. height = places.height(),
  16196. x = e.pageX,
  16197. y = e.pageY;
  16198. if (!(x > left && x < left+width && y > top && y < y+height)) {
  16199. remove(id2hash(target.children(':first').attr('id')));
  16200. save();
  16201. }
  16202. },
  16203. update : function(e, ui) {
  16204. save();
  16205. }
  16206. });
  16207. }
  16208. // "on regist" for command exec
  16209. $(this).on('regist', function(e, files){
  16210. var added = false;
  16211. $.each(files, function(i, dir) {
  16212. if (dir && dir.mime == 'directory' && !dirs[dir.hash]) {
  16213. if (add(dir)) {
  16214. added = true;
  16215. }
  16216. }
  16217. });
  16218. added && save();
  16219. });
  16220. // on fm load - show places and load files from backend
  16221. fm.one('load', function() {
  16222. var dat, hashes;
  16223. if (fm.oldAPI) {
  16224. return;
  16225. }
  16226. places.show().parent().show();
  16227. init();
  16228. fm.change(function(e) {
  16229. var changed = false;
  16230. $.each(e.data.changed, function(i, file) {
  16231. if (dirs[file.hash]) {
  16232. if (file.mime !== 'directory') {
  16233. if (remove(file.hash)) {
  16234. changed = true;
  16235. }
  16236. } else {
  16237. if (update(file)) {
  16238. changed = true;
  16239. }
  16240. }
  16241. }
  16242. });
  16243. changed && save();
  16244. })
  16245. .bind('rename', function(e) {
  16246. var changed = false;
  16247. if (e.data.removed) {
  16248. $.each(e.data.removed, function(i, hash) {
  16249. if (e.data.added[i]) {
  16250. if (update(e.data.added[i], hash)) {
  16251. changed = true;
  16252. }
  16253. }
  16254. });
  16255. }
  16256. changed && save();
  16257. })
  16258. .bind('rm paste', function(e) {
  16259. var names = [],
  16260. changed = false;
  16261. if (e.data.removed) {
  16262. $.each(e.data.removed, function(i, hash) {
  16263. var name = remove(hash);
  16264. name && names.push(name);
  16265. });
  16266. }
  16267. if (names.length) {
  16268. changed = true;
  16269. }
  16270. if (e.data.added && names.length) {
  16271. $.each(e.data.added, function(i, file) {
  16272. if ($.inArray(file.name, names) !== 1) {
  16273. file.mime == 'directory' && add(file);
  16274. }
  16275. });
  16276. }
  16277. changed && save();
  16278. })
  16279. .bind('sync netmount', function() {
  16280. var ev = this,
  16281. opSuffix = opts.suffix? opts.suffix : '',
  16282. hashes;
  16283. if (ev.type === 'sync') {
  16284. // check is change of opts.suffix
  16285. if (suffix !== opSuffix) {
  16286. suffix = opSuffix;
  16287. clear();
  16288. init();
  16289. return;
  16290. }
  16291. }
  16292. hashes = Object.keys(dirs);
  16293. if (hashes.length) {
  16294. root.prepend(spinner);
  16295. fm.request({
  16296. data : {cmd : 'info', targets : hashes},
  16297. preventDefault : true
  16298. })
  16299. .done(function(data) {
  16300. var exists = {},
  16301. updated = false,
  16302. cwd = fm.cwd().hash;
  16303. $.each(data.files || [], function(i, file) {
  16304. var hash = file.hash;
  16305. exists[hash] = file;
  16306. if (!fm.files().hasOwnProperty(file.hash)) {
  16307. // update cache
  16308. fm.trigger('tree', {tree: [file]});
  16309. }
  16310. });
  16311. $.each(dirs, function(h, f) {
  16312. if (f.notfound === Boolean(exists[h])) {
  16313. if ((f.phash === cwd && ev.type !== 'netmount') || (exists[h] && exists[h].mime !== 'directory')) {
  16314. if (remove(h)) {
  16315. updated = true;
  16316. }
  16317. } else {
  16318. if (update(exists[h] || Object.assign({notfound: true}, f))) {
  16319. updated = true;
  16320. }
  16321. }
  16322. } else if (exists[h] && exists[h].phash != cwd) {
  16323. // update permission of except cwd
  16324. update(exists[h]);
  16325. }
  16326. });
  16327. updated && save();
  16328. })
  16329. .always(function() {
  16330. spinner.remove();
  16331. });
  16332. }
  16333. });
  16334. });
  16335. });
  16336. };
  16337. /*
  16338. * File: /js/ui/searchbutton.js
  16339. */
  16340. /**
  16341. * @class elFinder toolbar search button widget.
  16342. *
  16343. * @author Dmitry (dio) Levashov
  16344. **/
  16345. $.fn.elfindersearchbutton = function(cmd) {
  16346. return this.each(function() {
  16347. var result = false,
  16348. fm = cmd.fm,
  16349. isopts = cmd.options.incsearch || { enable: false },
  16350. id = function(name){return fm.namespace + name;},
  16351. toolbar= fm.getUI('toolbar'),
  16352. btnCls = fm.res('class', 'searchbtn'),
  16353. button = $(this)
  16354. .hide()
  16355. .addClass('ui-widget-content elfinder-button '+btnCls)
  16356. .on('click', function(e) {
  16357. e.stopPropagation();
  16358. }),
  16359. search = function() {
  16360. input.data('inctm') && clearTimeout(input.data('inctm'));
  16361. var val = $.trim(input.val()),
  16362. from = !$('#' + id('SearchFromAll')).prop('checked'),
  16363. mime = $('#' + id('SearchMime')).prop('checked');
  16364. if (from) {
  16365. if ($('#' + id('SearchFromVol')).prop('checked')) {
  16366. from = fm.root(fm.cwd().hash);
  16367. } else {
  16368. from = fm.cwd().hash;
  16369. }
  16370. }
  16371. if (mime) {
  16372. mime = val;
  16373. val = '.';
  16374. }
  16375. if (val) {
  16376. input.trigger('focus');
  16377. cmd.exec(val, from, mime).done(function() {
  16378. result = true;
  16379. }).fail(function() {
  16380. abort();
  16381. });
  16382. } else {
  16383. fm.trigger('searchend');
  16384. }
  16385. },
  16386. abort = function() {
  16387. input.data('inctm') && clearTimeout(input.data('inctm'));
  16388. input.val('').trigger('blur');
  16389. if (result || incVal) {
  16390. result = false;
  16391. incVal = '';
  16392. fm.lazy(function() {
  16393. fm.trigger('searchend');
  16394. });
  16395. }
  16396. },
  16397. incVal = '',
  16398. input = $('<input type="text" size="42"/>')
  16399. .on('focus', function() {
  16400. inFocus = true;
  16401. incVal = '';
  16402. button.addClass('ui-state-active');
  16403. fm.trigger('uiresize');
  16404. opts && opts.slideDown(function() {
  16405. // Care for on browser window re-active
  16406. button.addClass('ui-state-active');
  16407. });
  16408. })
  16409. .on('blur', function() {
  16410. inFocus = false;
  16411. if (opts) {
  16412. if (!opts.data('infocus')) {
  16413. opts.slideUp(function() {
  16414. button.removeClass('ui-state-active');
  16415. fm.trigger('uiresize');
  16416. });
  16417. } else {
  16418. opts.data('infocus', false);
  16419. }
  16420. } else {
  16421. button.removeClass('ui-state-active');
  16422. }
  16423. })
  16424. .appendTo(button)
  16425. // to avoid fm shortcuts on arrows
  16426. .on('keypress', function(e) {
  16427. e.stopPropagation();
  16428. })
  16429. .on('keydown', function(e) {
  16430. e.stopPropagation();
  16431. e.keyCode == $.ui.keyCode.ENTER && search();
  16432. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  16433. e.preventDefault();
  16434. abort();
  16435. }
  16436. }),
  16437. opts, cwdReady, inFocus;
  16438. if (isopts.enable) {
  16439. isopts.minlen = isopts.minlen || 2;
  16440. isopts.wait = isopts.wait || 500;
  16441. input
  16442. .attr('title', fm.i18n('incSearchOnly'))
  16443. .on('compositionstart', function() {
  16444. input.data('composing', true);
  16445. })
  16446. .on('compositionend', function() {
  16447. input.removeData('composing');
  16448. input.trigger('input'); // for IE, edge
  16449. })
  16450. .on('input', function() {
  16451. if (! input.data('composing')) {
  16452. input.data('inctm') && clearTimeout(input.data('inctm'));
  16453. input.data('inctm', setTimeout(function() {
  16454. var val = input.val();
  16455. if (val.length === 0 || val.length >= isopts.minlen) {
  16456. (incVal !== val) && fm.trigger('incsearchstart', { query: val });
  16457. incVal = val;
  16458. if (val === '' && fm.searchStatus.state > 1 && fm.searchStatus.query) {
  16459. input.val(fm.searchStatus.query).trigger('select');
  16460. }
  16461. }
  16462. }, isopts.wait));
  16463. }
  16464. });
  16465. if (fm.UA.ltIE8) {
  16466. input.on('keydown', function(e) {
  16467. if (e.keyCode === 229) {
  16468. input.data('imetm') && clearTimeout(input.data('imetm'));
  16469. input.data('composing', true);
  16470. input.data('imetm', setTimeout(function() {
  16471. input.removeData('composing');
  16472. }, 100));
  16473. }
  16474. })
  16475. .on('keyup', function(e) {
  16476. input.data('imetm') && clearTimeout(input.data('imetm'));
  16477. if (input.data('composing')) {
  16478. e.keyCode === $.ui.keyCode.ENTER && input.trigger('compositionend');
  16479. } else {
  16480. input.trigger('input');
  16481. }
  16482. });
  16483. }
  16484. }
  16485. $('<span class="ui-icon ui-icon-search" title="'+cmd.title+'"/>')
  16486. .appendTo(button)
  16487. .on('mousedown', function(e) {
  16488. e.stopPropagation();
  16489. e.preventDefault();
  16490. if (button.hasClass('ui-state-active')) {
  16491. search();
  16492. } else {
  16493. input.trigger('focus');
  16494. }
  16495. });
  16496. $('<span class="ui-icon ui-icon-close"/>')
  16497. .appendTo(button)
  16498. .on('mousedown', function(e) {
  16499. e.stopPropagation();
  16500. e.preventDefault();
  16501. if (input.val() === '' && !button.hasClass('ui-state-active')) {
  16502. input.trigger('focus');
  16503. } else {
  16504. abort();
  16505. }
  16506. });
  16507. // wait when button will be added to DOM
  16508. fm.bind('toolbarload', function(){
  16509. var parent = button.parent();
  16510. if (parent.length) {
  16511. toolbar.prepend(button.show());
  16512. parent.remove();
  16513. // position icons for ie7
  16514. if (fm.UA.ltIE7) {
  16515. var icon = button.children(fm.direction == 'ltr' ? '.ui-icon-close' : '.ui-icon-search');
  16516. icon.css({
  16517. right : '',
  16518. left : parseInt(button.width())-icon.outerWidth(true)
  16519. });
  16520. }
  16521. }
  16522. });
  16523. fm
  16524. .one('init', function() {
  16525. fm.getUI('cwd').on('touchstart click', function() {
  16526. inFocus && input.trigger('blur');
  16527. });
  16528. })
  16529. .one('open', function() {
  16530. opts = (fm.api < 2.1)? null : $('<div class="ui-front ui-widget ui-widget-content elfinder-button-search-menu ui-corner-all"/>')
  16531. .append(
  16532. $('<div class="buttonset"/>')
  16533. .append(
  16534. $('<input id="'+id('SearchFromCwd')+'" name="serchfrom" type="radio" checked="checked"/><label for="'+id('SearchFromCwd')+'">'+fm.i18n('btnCwd')+'</label>'),
  16535. $('<input id="'+id('SearchFromVol')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromVol')+'">'+fm.i18n('btnVolume')+'</label>'),
  16536. $('<input id="'+id('SearchFromAll')+'" name="serchfrom" type="radio"/><label for="'+id('SearchFromAll')+'">'+fm.i18n('btnAll')+'</label>')
  16537. ),
  16538. $('<div class="buttonset"/>')
  16539. .append(
  16540. $('<input id="'+id('SearchName')+'" name="serchcol" type="radio" checked="checked"/><label for="'+id('SearchName')+'">'+fm.i18n('btnFileName')+'</label>'),
  16541. $('<input id="'+id('SearchMime')+'" name="serchcol" type="radio"/><label for="'+id('SearchMime')+'">'+fm.i18n('btnMime')+'</label>')
  16542. )
  16543. )
  16544. .hide()
  16545. .appendTo(fm.getUI());
  16546. if (opts) {
  16547. opts.find('div.buttonset').buttonset();
  16548. $('#'+id('SearchFromAll')).next('label').attr('title', fm.i18n('searchTarget', fm.i18n('btnAll')));
  16549. $('#'+id('SearchMime')).next('label').attr('title', fm.i18n('searchMime'));
  16550. opts.on('mousedown', 'div.buttonset', function(e){
  16551. e.stopPropagation();
  16552. opts.data('infocus', true);
  16553. })
  16554. .on('click', 'input', function(e) {
  16555. e.stopPropagation();
  16556. $.trim(input.val())? search() : input.trigger('focus');
  16557. });
  16558. }
  16559. })
  16560. .bind('searchend', function() {
  16561. input.val('');
  16562. })
  16563. .bind('open parents', function() {
  16564. var dirs = [],
  16565. volroot = fm.file(fm.root(fm.cwd().hash));
  16566. if (volroot) {
  16567. $.each(fm.parents(fm.cwd().hash), function(i, hash) {
  16568. dirs.push(fm.file(hash).name);
  16569. });
  16570. $('#'+id('SearchFromCwd')).next('label').attr('title', fm.i18n('searchTarget', dirs.join(fm.option('separator'))));
  16571. $('#'+id('SearchFromVol')).next('label').attr('title', fm.i18n('searchTarget', volroot.name));
  16572. }
  16573. })
  16574. .bind('open', function() {
  16575. incVal && abort();
  16576. })
  16577. .bind('cwdinit', function() {
  16578. cwdReady = false;
  16579. })
  16580. .bind('cwdrender',function() {
  16581. cwdReady = true;
  16582. })
  16583. .bind('keydownEsc', function() {
  16584. if (incVal && incVal.substr(0, 1) === '/') {
  16585. incVal = '';
  16586. input.val('');
  16587. fm.trigger('searchend');
  16588. }
  16589. })
  16590. .shortcut({
  16591. pattern : 'ctrl+f f3',
  16592. description : cmd.title,
  16593. callback : function() {
  16594. input.trigger('select').trigger('focus');
  16595. }
  16596. })
  16597. .shortcut({
  16598. 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',
  16599. description : fm.i18n('firstLetterSearch'),
  16600. callback : function(e) {
  16601. if (! cwdReady) { return; }
  16602. var code = e.originalEvent.keyCode,
  16603. next = function() {
  16604. var sel = fm.selected(),
  16605. key = $.ui.keyCode[(!sel.length || $('#'+fm.cwdHash2Id(sel[0])).next('[id]').length)? 'RIGHT' : 'HOME'];
  16606. $(document).trigger($.Event('keydown', { keyCode: key, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  16607. },
  16608. val;
  16609. if (code >= 96 && code <= 105) {
  16610. code -= 48;
  16611. }
  16612. val = '/' + String.fromCharCode(code);
  16613. if (incVal !== val) {
  16614. input.val(val);
  16615. incVal = val;
  16616. fm
  16617. .trigger('incsearchstart', { query: val })
  16618. .one('cwdrender', next);
  16619. } else{
  16620. next();
  16621. }
  16622. }
  16623. });
  16624. });
  16625. };
  16626. /*
  16627. * File: /js/ui/sortbutton.js
  16628. */
  16629. /**
  16630. * @class elFinder toolbar button menu with sort variants.
  16631. *
  16632. * @author Dmitry (dio) Levashov
  16633. **/
  16634. $.fn.elfindersortbutton = function(cmd) {
  16635. return this.each(function() {
  16636. var fm = cmd.fm,
  16637. name = cmd.name,
  16638. c = 'class',
  16639. disabled = fm.res(c, 'disabled'),
  16640. hover = fm.res(c, 'hover'),
  16641. item = 'elfinder-button-menu-item',
  16642. selected = item+'-selected',
  16643. asc = selected+'-asc',
  16644. desc = selected+'-desc',
  16645. text = $('<span class="elfinder-button-text">'+cmd.title+'</span>'),
  16646. button = $(this).addClass('ui-state-default elfinder-button elfinder-menubutton elfiner-button-'+name)
  16647. .attr('title', cmd.title)
  16648. .append('<span class="elfinder-button-icon elfinder-button-icon-'+name+'"/>', text)
  16649. .on('mouseenter mouseleave', function(e) { !button.hasClass(disabled) && button.toggleClass(hover); })
  16650. .on('click', function(e) {
  16651. if (!button.hasClass(disabled)) {
  16652. e.stopPropagation();
  16653. menu.is(':hidden') && fm.getUI().click();
  16654. menu.css(getMenuOffset()).slideToggle(100);
  16655. }
  16656. }),
  16657. menu = $('<div class="ui-front ui-widget ui-widget-content elfinder-button-menu ui-corner-all"/>')
  16658. .hide()
  16659. .appendTo(fm.getUI())
  16660. .on('mouseenter mouseleave', '.'+item, function() { $(this).toggleClass(hover); })
  16661. .on('click', '.'+item, function(e) {
  16662. e.preventDefault();
  16663. e.stopPropagation();
  16664. hide();
  16665. }),
  16666. update = function() {
  16667. menu.children('[rel]').removeClass(selected+' '+asc+' '+desc)
  16668. .filter('[rel="'+fm.sortType+'"]')
  16669. .addClass(selected+' '+(fm.sortOrder == 'asc' ? asc : desc));
  16670. menu.children('.elfinder-sort-stick').toggleClass(selected, fm.sortStickFolders);
  16671. menu.children('.elfinder-sort-tree').toggleClass(selected, fm.sortAlsoTreeview);
  16672. },
  16673. hide = function() { menu.hide(); },
  16674. getMenuOffset = function() {
  16675. var baseOffset = fm.getUI().offset(),
  16676. buttonOffset = button.offset();
  16677. return {
  16678. top : buttonOffset.top - baseOffset.top,
  16679. left : buttonOffset.left - baseOffset.left
  16680. };
  16681. };
  16682. text.hide();
  16683. $.each(fm.sortRules, function(name, value) {
  16684. 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));
  16685. });
  16686. menu.children().on('click', function(e) {
  16687. var type = $(this).attr('rel');
  16688. cmd.exec([], {
  16689. type : type,
  16690. order : type == fm.sortType ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder,
  16691. stick : fm.sortStickFolders,
  16692. tree : fm.sortAlsoTreeview
  16693. });
  16694. });
  16695. $('<div class="'+item+' '+item+'-separated elfinder-sort-ext elfinder-sort-stick"><span class="ui-icon ui-icon-check"/>'+fm.i18n('sortFoldersFirst')+'</div>')
  16696. .appendTo(menu)
  16697. .on('click', function() {
  16698. cmd.exec([], {type : fm.sortType, order : fm.sortOrder, stick : !fm.sortStickFolders, tree : fm.sortAlsoTreeview});
  16699. });
  16700. if ($.fn.elfindertree && $.inArray('tree', fm.options.ui) !== -1) {
  16701. $('<div class="'+item+' '+item+'-separated elfinder-sort-ext elfinder-sort-tree"><span class="ui-icon ui-icon-check"/>'+fm.i18n('sortAlsoTreeview')+'</div>')
  16702. .appendTo(menu)
  16703. .on('click', function() {
  16704. cmd.exec([], {type : fm.sortType, order : fm.sortOrder, stick : fm.sortStickFolders, tree : !fm.sortAlsoTreeview});
  16705. });
  16706. }
  16707. fm.bind('disable select', hide).getUI().on('click', hide);
  16708. fm.bind('sortchange', update);
  16709. if (menu.children().length > 1) {
  16710. cmd.change(function() {
  16711. button.toggleClass(disabled, cmd.disabled());
  16712. update();
  16713. })
  16714. .change();
  16715. } else {
  16716. button.addClass(disabled);
  16717. }
  16718. });
  16719. };
  16720. /*
  16721. * File: /js/ui/stat.js
  16722. */
  16723. /**
  16724. * @class elFinder ui
  16725. * Display number of files/selected files and its size in statusbar
  16726. *
  16727. * @author Dmitry (dio) Levashov
  16728. **/
  16729. $.fn.elfinderstat = function(fm) {
  16730. return this.each(function() {
  16731. var size = $(this).addClass('elfinder-stat-size'),
  16732. sel = $('<div class="elfinder-stat-selected"/>')
  16733. .on('click', 'a', function(e) {
  16734. var hash = $(this).data('hash');
  16735. e.preventDefault();
  16736. fm.exec('opendir', [ hash ]);
  16737. }),
  16738. titleitems = fm.i18n('items'),
  16739. titlesel = fm.i18n('selected'),
  16740. titlesize = fm.i18n('size'),
  16741. setstat = function(files) {
  16742. var c = 0,
  16743. s = 0,
  16744. cwd = fm.cwd(),
  16745. calc = true,
  16746. hasSize = true;
  16747. if (cwd.sizeInfo || cwd.size) {
  16748. s = cwd.size;
  16749. calc = false;
  16750. }
  16751. $.each(files, function(i, file) {
  16752. c++;
  16753. if (calc) {
  16754. s += parseInt(file.size) || 0;
  16755. if (hasSize === true && file.mime === 'directory' && !file.sizeInfo) {
  16756. hasSize = false;
  16757. }
  16758. }
  16759. });
  16760. 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>')
  16761. .attr('title', size.text());
  16762. },
  16763. setIncsearchStat = function(data) {
  16764. size.find('span.elfinder-stat-incsearch').html(data? data.hashes.length + ' / ' : '');
  16765. size.attr('title', size.text());
  16766. };
  16767. fm.getUI('statusbar').prepend(size).append(sel).show();
  16768. if (fm.UA.Mobile && $.fn.tooltip) {
  16769. fm.getUI('statusbar').tooltip({
  16770. classes: {
  16771. 'ui-tooltip': 'elfinder-ui-tooltip ui-widget-shadow'
  16772. },
  16773. tooltipClass: 'elfinder-ui-tooltip ui-widget-shadow',
  16774. track: true
  16775. });
  16776. }
  16777. fm
  16778. .bind('cwdhasheschange', function(e) {
  16779. setstat($.map(e.data, function(h) { return fm.file(h); }));
  16780. })
  16781. .change(function(e) {
  16782. var files = e.data.changed || [],
  16783. cwdHash = fm.cwd().hash;
  16784. $.each(files, function() {
  16785. if (this.hash === cwdHash) {
  16786. if (this.size) {
  16787. size.children('.elfinder-stat-size').addClass('elfinder-stat-size-recursive').html(fm.i18n('sum')+': '+fm.formatSize(this.size));
  16788. size.attr('title', size.text());
  16789. }
  16790. return false;
  16791. }
  16792. });
  16793. })
  16794. .select(function() {
  16795. var s = 0,
  16796. c = 0,
  16797. files = fm.selectedFiles(),
  16798. dirs = [],
  16799. path, file;
  16800. if (files.length === 1) {
  16801. file = files[0];
  16802. s = file.size;
  16803. if (fm.searchStatus.state === 2) {
  16804. path = fm.escape(file.path? file.path.replace(/\/[^\/]*$/, '') : '..');
  16805. dirs.push('<a href="#elf_'+file.phash+'" data-hash="'+file.hash+'" title="'+path+'">'+path+'</a>');
  16806. }
  16807. dirs.push(fm.escape(file.i18 || file.name));
  16808. sel.html(dirs.join('/') + (s > 0 ? ', '+fm.formatSize(s) : ''));
  16809. } else if (files.length) {
  16810. $.each(files, function(i, file) {
  16811. c++;
  16812. s += parseInt(file.size)||0;
  16813. });
  16814. sel.html(c ? titlesel+': '+c+', '+titlesize+': '+fm.formatSize(s) : '&nbsp;');
  16815. } else {
  16816. sel.html('');
  16817. }
  16818. sel.attr('title', sel.text());
  16819. })
  16820. .bind('incsearch', function(e) {
  16821. setIncsearchStat(e.data);
  16822. })
  16823. .bind('incsearchend', function() {
  16824. setIncsearchStat();
  16825. })
  16826. ;
  16827. });
  16828. };
  16829. /*
  16830. * File: /js/ui/toast.js
  16831. */
  16832. /**
  16833. * @class elFinder toast
  16834. *
  16835. * This was created inspired by the toastr. Thanks to developers of toastr.
  16836. * CodeSeven/toastr: http://johnpapa.net <https://github.com/CodeSeven/toastr>
  16837. *
  16838. * @author Naoki Sawada
  16839. **/
  16840. $.fn.elfindertoast = function(opts, fm) {
  16841. var defOpts = {
  16842. mode: 'success',
  16843. msg: '',
  16844. showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery
  16845. showDuration: 300,
  16846. showEasing: 'swing', //swing and linear are built into jQuery
  16847. onShown: undefined,
  16848. hideMethod: 'fadeOut',
  16849. hideDuration: 1500,
  16850. hideEasing: 'swing',
  16851. onHidden: undefined,
  16852. timeOut: 3000,
  16853. extNode: undefined
  16854. };
  16855. return this.each(function() {
  16856. opts = Object.assign({}, defOpts, opts || {});
  16857. var self = $(this),
  16858. show = function(notm) {
  16859. self.stop();
  16860. self[opts.showMethod]({
  16861. duration: opts.showDuration,
  16862. easing: opts.showEasing,
  16863. complete: function() {
  16864. opts.onShown && opts.onShown();
  16865. if (!notm && opts.timeOut) {
  16866. rmTm = setTimeout(rm, opts.timeOut);
  16867. }
  16868. }
  16869. });
  16870. },
  16871. rm = function() {
  16872. self[opts.hideMethod]({
  16873. duration: opts.hideDuration,
  16874. easing: opts.hideEasing,
  16875. complete: function() {
  16876. opts.onHidden && opts.onHidden();
  16877. self.remove();
  16878. }
  16879. });
  16880. },
  16881. rmTm;
  16882. self.on('click', function(e) {
  16883. e.stopPropagation();
  16884. e.preventDefault();
  16885. self.stop().remove();
  16886. }).on('mouseenter mouseleave', function(e) {
  16887. if (opts.timeOut) {
  16888. rmTm && clearTimeout(rmTm);
  16889. rmTm = null;
  16890. if (e.type === 'mouseenter') {
  16891. show(true);
  16892. } else {
  16893. rmTm = setTimeout(rm, opts.timeOut);
  16894. }
  16895. }
  16896. }).hide().addClass('toast-' + opts.mode).append($('<div class="elfinder-toast-msg"/>').html(opts.msg.replace(/%([a-zA-Z0-9]+)%/g, function(m, m1) {
  16897. return fm.i18n(m1);
  16898. })));
  16899. if (opts.extNode) {
  16900. self.append(opts.extNode);
  16901. }
  16902. show();
  16903. });
  16904. };
  16905. /*
  16906. * File: /js/ui/toolbar.js
  16907. */
  16908. /**
  16909. * @class elFinder toolbar
  16910. *
  16911. * @author Dmitry (dio) Levashov
  16912. **/
  16913. $.fn.elfindertoolbar = function(fm, opts) {
  16914. this.not('.elfinder-toolbar').each(function() {
  16915. var commands = fm._commands,
  16916. self = $(this).addClass('ui-helper-clearfix ui-widget-header ui-corner-top elfinder-toolbar'),
  16917. options = {
  16918. // default options
  16919. displayTextLabel: false,
  16920. labelExcludeUA: ['Mobile'],
  16921. autoHideUA: ['Mobile'],
  16922. showPreferenceButton: 'none'
  16923. },
  16924. filter = function(opts) {
  16925. return $.grep(opts, function(v) {
  16926. if ($.isPlainObject(v)) {
  16927. options = Object.assign(options, v);
  16928. return false;
  16929. }
  16930. return true;
  16931. });
  16932. },
  16933. render = function(disabled){
  16934. var name,cmdPref;
  16935. $.each(buttons, function(i, b) { b.detach(); });
  16936. self.empty();
  16937. l = panels.length;
  16938. while (l--) {
  16939. if (panels[l]) {
  16940. panel = $('<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>');
  16941. i = panels[l].length;
  16942. while (i--) {
  16943. name = panels[l][i];
  16944. if ((!disabled || !disabled[name]) && (cmd = commands[name])) {
  16945. button = 'elfinder'+cmd.options.ui;
  16946. if (! buttons[name] && $.fn[button]) {
  16947. buttons[name] = $('<div/>')[button](cmd);
  16948. }
  16949. if (buttons[name]) {
  16950. buttons[name].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  16951. panel.prepend(buttons[name]);
  16952. }
  16953. }
  16954. }
  16955. panel.children().length && self.prepend(panel);
  16956. panel.children(':gt(0)').before('<span class="ui-widget-content elfinder-toolbar-button-separator"/>');
  16957. }
  16958. }
  16959. if (cmdPref = commands['preference']) {
  16960. //cmdPref.state = !self.children().length? 0 : -1;
  16961. if (options.showPreferenceButton === 'always' || (!self.children().length && options.showPreferenceButton === 'auto')) {
  16962. //cmdPref.state = 0;
  16963. panel = $('<div class="ui-widget-content ui-corner-all elfinder-buttonset"/>');
  16964. name = 'preference';
  16965. button = 'elfinder'+cmd.options.ui;
  16966. buttons[name] = $('<div/>')[button](cmdPref);
  16967. buttons[name].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  16968. panel.prepend(buttons[name]);
  16969. self.append(panel);
  16970. }
  16971. }
  16972. (! self.data('swipeClose') && self.children().length)? self.show() : self.hide();
  16973. fm.trigger('toolbarload').trigger('uiresize');
  16974. },
  16975. buttons = {},
  16976. panels = filter(opts || []),
  16977. dispre = null,
  16978. uiCmdMapPrev = '',
  16979. l, i, cmd, panel, button, swipeHandle, autoHide, textLabel;
  16980. // normalize options
  16981. options.showPreferenceButton = options.showPreferenceButton.toLowerCase();
  16982. // correction of options.displayTextLabel
  16983. textLabel = fm.storage('toolbarTextLabel');
  16984. if (textLabel === null) {
  16985. textLabel = (options.displayTextLabel && (! options.labelExcludeUA || ! options.labelExcludeUA.length || ! $.grep(options.labelExcludeUA, function(v){ return fm.UA[v]? true : false; }).length));
  16986. } else {
  16987. textLabel = (textLabel == 1);
  16988. }
  16989. // add contextmenu
  16990. self.on('contextmenu', function(e) {
  16991. e.stopPropagation();
  16992. e.preventDefault();
  16993. fm.trigger('contextmenu', {
  16994. raw: [{
  16995. label : fm.i18n('textLabel'),
  16996. icon : 'accept',
  16997. callback : function() {
  16998. textLabel = ! textLabel;
  16999. self.css('height', '').find('.elfinder-button-text')[textLabel? 'show':'hide']();
  17000. fm.trigger('uiresize').storage('toolbarTextLabel', textLabel? '1' : '0');
  17001. },
  17002. },{
  17003. label : fm.i18n('toolbarPref'),
  17004. icon : 'preference',
  17005. callback : function() {
  17006. fm.exec('help', void(0), {tab: 'preference'});
  17007. }
  17008. }],
  17009. x: e.pageX,
  17010. y: e.pageY
  17011. });
  17012. }).on('touchstart', function(e) {
  17013. if (e.originalEvent.touches.length > 1) {
  17014. return;
  17015. }
  17016. self.data('tmlongtap') && clearTimeout(self.data('tmlongtap'));
  17017. self.removeData('longtap')
  17018. .data('longtap', {x: e.originalEvent.touches[0].pageX, y: e.originalEvent.touches[0].pageY})
  17019. .data('tmlongtap', setTimeout(function() {
  17020. self.removeData('longtapTm')
  17021. .trigger({
  17022. type: 'contextmenu',
  17023. pageX: self.data('longtap').x,
  17024. pageY: self.data('longtap').y
  17025. })
  17026. .data('longtap', {longtap: true});
  17027. }, 500));
  17028. }).on('touchmove touchend', function(e) {
  17029. if (self.data('tmlongtap')) {
  17030. if (e.type === 'touchend' ||
  17031. ( Math.abs(self.data('longtap').x - e.originalEvent.touches[0].pageX)
  17032. + Math.abs(self.data('longtap').y - e.originalEvent.touches[0].pageY)) > 4)
  17033. clearTimeout(self.data('tmlongtap'));
  17034. self.removeData('longtapTm');
  17035. }
  17036. }).on('click', function(e) {
  17037. if (self.data('longtap') && self.data('longtap').longtap) {
  17038. e.stopImmediatePropagation();
  17039. e.preventDefault();
  17040. }
  17041. }).on('touchend click', '.elfinder-button', function(e) {
  17042. if (self.data('longtap') && self.data('longtap').longtap) {
  17043. e.stopImmediatePropagation();
  17044. e.preventDefault();
  17045. }
  17046. });
  17047. self.prev().length && self.parent().prepend(this);
  17048. render();
  17049. fm.bind('open sync select toolbarpref', function() {
  17050. var disabled = Object.assign({}, fm.option('disabledFlip')),
  17051. userHides = fm.storage('toolbarhides'),
  17052. doRender, sel, disabledKeys;
  17053. if (! userHides && Array.isArray(options.defaultHides)) {
  17054. userHides = {};
  17055. $.each(options.defaultHides, function() {
  17056. userHides[this] = true;
  17057. });
  17058. fm.storage('toolbarhides', userHides);
  17059. }
  17060. if (this.type === 'select') {
  17061. if (fm.searchStatus.state < 2) {
  17062. return;
  17063. }
  17064. sel = fm.selected();
  17065. if (sel.length) {
  17066. disabled = fm.getDisabledCmds(sel, true);
  17067. }
  17068. }
  17069. $.each(userHides, function(n) {
  17070. if (!disabled[n]) {
  17071. disabled[n] = true;
  17072. }
  17073. });
  17074. if (Object.keys(fm.commandMap).length) {
  17075. $.each(fm.commandMap, function(from, to){
  17076. if (to === 'hidden') {
  17077. disabled[from] = true;
  17078. }
  17079. });
  17080. }
  17081. disabledKeys = Object.keys(disabled);
  17082. if (!dispre || dispre.toString() !== disabledKeys.sort().toString()) {
  17083. render(disabledKeys.length? disabled : null);
  17084. doRender = true;
  17085. }
  17086. dispre = disabledKeys.sort();
  17087. if (doRender || uiCmdMapPrev !== JSON.stringify(fm.commandMap)) {
  17088. uiCmdMapPrev = JSON.stringify(fm.commandMap);
  17089. if (! doRender) {
  17090. // reset toolbar
  17091. $.each($('div.elfinder-button'), function(){
  17092. var origin = $(this).data('origin');
  17093. if (origin) {
  17094. $(this).after(origin).detach();
  17095. }
  17096. });
  17097. }
  17098. if (Object.keys(fm.commandMap).length) {
  17099. $.each(fm.commandMap, function(from, to){
  17100. var cmd = fm._commands[to],
  17101. button = cmd? 'elfinder'+cmd.options.ui : null,
  17102. btn;
  17103. if (button && $.fn[button]) {
  17104. btn = buttons[from];
  17105. if (btn) {
  17106. if (! buttons[to] && $.fn[button]) {
  17107. buttons[to] = $('<div/>')[button](fm._commands[to]);
  17108. if (buttons[to]) {
  17109. buttons[to].children('.elfinder-button-text')[textLabel? 'show' : 'hide']();
  17110. if (cmd.extendsCmd) {
  17111. buttons[to].children('span.elfinder-button-icon').addClass('elfinder-button-icon-' + cmd.extendsCmd);
  17112. }
  17113. }
  17114. }
  17115. if (buttons[to]) {
  17116. btn.after(buttons[to]);
  17117. buttons[to].data('origin', btn.detach());
  17118. }
  17119. }
  17120. }
  17121. });
  17122. }
  17123. }
  17124. });
  17125. if (fm.UA.Touch) {
  17126. autoHide = fm.storage('autoHide') || {};
  17127. if (typeof autoHide.toolbar === 'undefined') {
  17128. autoHide.toolbar = (options.autoHideUA && options.autoHideUA.length > 0 && $.grep(options.autoHideUA, function(v){ return fm.UA[v]? true : false; }).length);
  17129. fm.storage('autoHide', autoHide);
  17130. }
  17131. if (autoHide.toolbar) {
  17132. fm.one('init', function() {
  17133. fm.uiAutoHide.push(function(){ self.stop(true, true).trigger('toggle', { duration: 500, init: true }); });
  17134. });
  17135. }
  17136. fm.bind('load', function() {
  17137. swipeHandle = $('<div class="elfinder-toolbar-swipe-handle"/>').hide().appendTo(fm.getUI());
  17138. if (swipeHandle.css('pointer-events') !== 'none') {
  17139. swipeHandle.remove();
  17140. swipeHandle = null;
  17141. }
  17142. });
  17143. self.on('toggle', function(e, data) {
  17144. var wz = fm.getUI('workzone'),
  17145. toshow= self.is(':hidden'),
  17146. wzh = wz.height(),
  17147. h = self.height(),
  17148. tbh = self.outerHeight(true),
  17149. delta = tbh - h,
  17150. opt = Object.assign({
  17151. step: function(now) {
  17152. wz.height(wzh + (toshow? (now + delta) * -1 : h - now));
  17153. fm.trigger('resize');
  17154. },
  17155. always: function() {
  17156. setTimeout(function() {
  17157. self.css('height', '');
  17158. fm.trigger('uiresize');
  17159. if (swipeHandle) {
  17160. if (toshow) {
  17161. swipeHandle.stop(true, true).hide();
  17162. } else {
  17163. swipeHandle.height(data.handleH? data.handleH : '');
  17164. fm.resources.blink(swipeHandle, 'slowonce');
  17165. }
  17166. }
  17167. toshow && self.scrollTop('0px');
  17168. data.init && fm.trigger('uiautohide');
  17169. }, 0);
  17170. }
  17171. }, data);
  17172. self.data('swipeClose', ! toshow).stop(true, true).animate({height : 'toggle'}, opt);
  17173. autoHide.toolbar = !toshow;
  17174. fm.storage('autoHide', Object.assign(fm.storage('autoHide'), {toolbar: autoHide.toolbar}));
  17175. }).on('touchstart', function(e) {
  17176. if (self.scrollBottom() > 5) {
  17177. e.originalEvent._preventSwipeY = true;
  17178. }
  17179. });
  17180. }
  17181. });
  17182. return this;
  17183. };
  17184. /*
  17185. * File: /js/ui/tree.js
  17186. */
  17187. /**
  17188. * @class elFinder folders tree
  17189. *
  17190. * @author Dmitry (dio) Levashov
  17191. **/
  17192. $.fn.elfindertree = function(fm, opts) {
  17193. var treeclass = fm.res('class', 'tree');
  17194. this.not('.'+treeclass).each(function() {
  17195. var c = 'class', mobile = fm.UA.Mobile,
  17196. /**
  17197. * Root directory class name
  17198. *
  17199. * @type String
  17200. */
  17201. root = fm.res(c, 'treeroot'),
  17202. /**
  17203. * Open root dir if not opened yet
  17204. *
  17205. * @type Boolean
  17206. */
  17207. openRoot = opts.openRootOnLoad,
  17208. /**
  17209. * Open current work dir if not opened yet
  17210. *
  17211. * @type Boolean
  17212. */
  17213. openCwd = opts.openCwdOnOpen,
  17214. /**
  17215. * Auto loading current directory parents and do expand their node
  17216. *
  17217. * @type Boolean
  17218. */
  17219. syncTree = openCwd || opts.syncTree,
  17220. /**
  17221. * Subtree class name
  17222. *
  17223. * @type String
  17224. */
  17225. subtree = fm.res(c, 'navsubtree'),
  17226. /**
  17227. * Directory class name
  17228. *
  17229. * @type String
  17230. */
  17231. navdir = fm.res(c, 'treedir'),
  17232. /**
  17233. * Directory CSS selector
  17234. *
  17235. * @type String
  17236. */
  17237. selNavdir = 'span.' + navdir,
  17238. /**
  17239. * Collapsed arrow class name
  17240. *
  17241. * @type String
  17242. */
  17243. collapsed = fm.res(c, 'navcollapse'),
  17244. /**
  17245. * Expanded arrow class name
  17246. *
  17247. * @type String
  17248. */
  17249. expanded = fm.res(c, 'navexpand'),
  17250. /**
  17251. * Class name to mark arrow for directory with already loaded children
  17252. *
  17253. * @type String
  17254. */
  17255. loaded = 'elfinder-subtree-loaded',
  17256. /**
  17257. * Class name to mark need subdirs request
  17258. *
  17259. * @type String
  17260. */
  17261. chksubdir = 'elfinder-subtree-chksubdir',
  17262. /**
  17263. * Arraw class name
  17264. *
  17265. * @type String
  17266. */
  17267. arrow = fm.res(c, 'navarrow'),
  17268. /**
  17269. * Current directory class name
  17270. *
  17271. * @type String
  17272. */
  17273. active = fm.res(c, 'active'),
  17274. /**
  17275. * Droppable dirs dropover class
  17276. *
  17277. * @type String
  17278. */
  17279. dropover = fm.res(c, 'adroppable'),
  17280. /**
  17281. * Hover class name
  17282. *
  17283. * @type String
  17284. */
  17285. hover = fm.res(c, 'hover'),
  17286. /**
  17287. * Disabled dir class name
  17288. *
  17289. * @type String
  17290. */
  17291. disabled = fm.res(c, 'disabled'),
  17292. /**
  17293. * Draggable dir class name
  17294. *
  17295. * @type String
  17296. */
  17297. draggable = fm.res(c, 'draggable'),
  17298. /**
  17299. * Droppable dir class name
  17300. *
  17301. * @type String
  17302. */
  17303. droppable = fm.res(c, 'droppable'),
  17304. /**
  17305. * root wrapper class
  17306. *
  17307. * @type String
  17308. */
  17309. wrapperRoot = 'elfinder-navbar-wrapper-root',
  17310. /**
  17311. * Un-disabled cmd `paste` volume's root wrapper class
  17312. *
  17313. * @type String
  17314. */
  17315. pastable = 'elfinder-navbar-wrapper-pastable',
  17316. /**
  17317. * Un-disabled cmd `upload` volume's root wrapper class
  17318. *
  17319. * @type String
  17320. */
  17321. uploadable = 'elfinder-navbar-wrapper-uploadable',
  17322. /**
  17323. * Is position x inside Navbar
  17324. *
  17325. * @param x Numbar
  17326. *
  17327. * @return
  17328. */
  17329. insideNavbar = function(x) {
  17330. var left = navbar.offset().left;
  17331. return left <= x && x <= left + navbar.width();
  17332. },
  17333. /**
  17334. * To call subdirs elements queue
  17335. *
  17336. * @type Object
  17337. */
  17338. subdirsQue = {},
  17339. /**
  17340. * To exec subdirs elements ids
  17341. *
  17342. */
  17343. subdirsExecQue = [],
  17344. /**
  17345. * Request subdirs to backend
  17346. *
  17347. * @param id String
  17348. *
  17349. * @return Deferred
  17350. */
  17351. subdirs = function(ids) {
  17352. var targets = [];
  17353. $.each(ids, function(i, id) {
  17354. subdirsQue[id] && targets.push(fm.navId2Hash(id));
  17355. delete subdirsQue[id];
  17356. });
  17357. if (targets.length) {
  17358. return fm.request({
  17359. data: {
  17360. cmd: 'subdirs',
  17361. targets: targets,
  17362. preventDefault : true
  17363. }
  17364. }).done(function(res) {
  17365. if (res && res.subdirs) {
  17366. $.each(res.subdirs, function(hash, subdirs) {
  17367. var elm = $('#'+fm.navHash2Id(hash));
  17368. elm.removeClass(chksubdir);
  17369. elm[subdirs? 'addClass' : 'removeClass'](collapsed);
  17370. });
  17371. }
  17372. });
  17373. }
  17374. },
  17375. subdirsJobRes = null,
  17376. /**
  17377. * To check target element is in window of subdirs
  17378. *
  17379. * @return void
  17380. */
  17381. checkSubdirs = function() {
  17382. var ids = Object.keys(subdirsQue);
  17383. if (ids.length) {
  17384. subdirsJobRes && subdirsJobRes._abort();
  17385. execSubdirsTm && clearTimeout(execSubdirsTm);
  17386. subdirsExecQue = [];
  17387. subdirsJobRes = fm.asyncJob(function(id) {
  17388. return fm.isInWindow($('#'+id))? id : null;
  17389. }, ids, { numPerOnce: 200 })
  17390. .done(function(arr) {
  17391. if (arr.length) {
  17392. subdirsExecQue = arr;
  17393. execSubdirs();
  17394. }
  17395. });
  17396. }
  17397. },
  17398. subdirsPending = 0,
  17399. execSubdirsTm,
  17400. /**
  17401. * Exec subdirs as batch request
  17402. *
  17403. * @return void
  17404. */
  17405. execSubdirs = function() {
  17406. var cnt = opts.subdirsMaxConn - subdirsPending,
  17407. i, ids;
  17408. execSubdirsTm && clearTimeout(execSubdirsTm);
  17409. if (subdirsExecQue.length) {
  17410. if (cnt > 0) {
  17411. for (i = 0; i < cnt; i++) {
  17412. if (subdirsExecQue.length) {
  17413. subdirsPending++;
  17414. subdirs(subdirsExecQue.splice(0, opts.subdirsAtOnce)).always(function() {
  17415. subdirsPending--;
  17416. execSubdirs();
  17417. });
  17418. }
  17419. }
  17420. } else {
  17421. execSubdirsTm = setTimeout(function() {
  17422. subdirsExecQue.length && execSubdirs();
  17423. }, 50);
  17424. }
  17425. }
  17426. },
  17427. drop = fm.droppable.drop,
  17428. /**
  17429. * Droppable options
  17430. *
  17431. * @type Object
  17432. */
  17433. droppableopts = $.extend(true, {}, fm.droppable, {
  17434. // show subfolders on dropover
  17435. over : function(e, ui) {
  17436. var dst = $(this),
  17437. helper = ui.helper,
  17438. cl = hover+' '+dropover,
  17439. hash, status;
  17440. e.stopPropagation();
  17441. helper.data('dropover', helper.data('dropover') + 1);
  17442. dst.data('dropover', true);
  17443. if (ui.helper.data('namespace') !== fm.namespace || ! insideNavbar(e.clientX) || ! fm.insideWorkzone(e.pageX, e.pageY)) {
  17444. dst.removeClass(cl);
  17445. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  17446. return;
  17447. }
  17448. dst.addClass(hover);
  17449. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  17450. dst.data('expandTimer', setTimeout(function() {
  17451. dst.is('.'+collapsed+'.'+hover) && dst.children('.'+arrow).trigger('click');
  17452. }, 500));
  17453. }
  17454. if (dst.is('.elfinder-ro,.elfinder-na')) {
  17455. dst.removeClass(dropover);
  17456. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus');
  17457. return;
  17458. }
  17459. hash = fm.navId2Hash(dst.attr('id'));
  17460. dst.data('dropover', hash);
  17461. $.each(ui.helper.data('files'), function(i, h) {
  17462. if (h === hash || (fm.file(h).phash === hash && !ui.helper.hasClass('elfinder-drag-helper-plus'))) {
  17463. dst.removeClass(cl);
  17464. return false; // break $.each
  17465. }
  17466. });
  17467. if (helper.data('locked')) {
  17468. status = 'elfinder-drag-helper-plus';
  17469. } else {
  17470. status = 'elfinder-drag-helper-move';
  17471. if (e.shiftKey || e.ctrlKey || e.metaKey) {
  17472. status += ' elfinder-drag-helper-plus';
  17473. }
  17474. }
  17475. dst.hasClass(dropover) && helper.addClass(status);
  17476. setTimeout(function(){ dst.hasClass(dropover) && helper.addClass(status); }, 20);
  17477. },
  17478. out : function(e, ui) {
  17479. var dst = $(this),
  17480. helper = ui.helper;
  17481. e.stopPropagation();
  17482. helper.removeClass('elfinder-drag-helper-move elfinder-drag-helper-plus').data('dropover', Math.max(helper.data('dropover') - 1, 0));
  17483. dst.data('expandTimer') && clearTimeout(dst.data('expandTimer'));
  17484. dst.removeData('dropover')
  17485. .removeClass(hover+' '+dropover);
  17486. },
  17487. deactivate : function() {
  17488. $(this).removeData('dropover')
  17489. .removeClass(hover+' '+dropover);
  17490. },
  17491. drop : function(e, ui) {
  17492. insideNavbar(e.clientX) && drop.call(this, e, ui);
  17493. }
  17494. }),
  17495. spinner = $(fm.res('tpl', 'navspinner')),
  17496. /**
  17497. * Directory html template
  17498. *
  17499. * @type String
  17500. */
  17501. tpl = fm.res('tpl', 'navdir'),
  17502. /**
  17503. * Permissions marker html template
  17504. *
  17505. * @type String
  17506. */
  17507. ptpl = fm.res('tpl', 'perms'),
  17508. /**
  17509. * Lock marker html template
  17510. *
  17511. * @type String
  17512. */
  17513. ltpl = fm.res('tpl', 'lock'),
  17514. /**
  17515. * Symlink marker html template
  17516. *
  17517. * @type String
  17518. */
  17519. stpl = fm.res('tpl', 'symlink'),
  17520. /**
  17521. * Directory hashes that has more pages
  17522. *
  17523. * @type Object
  17524. */
  17525. hasMoreDirs = {},
  17526. /**
  17527. * Html template replacement methods
  17528. *
  17529. * @type Object
  17530. */
  17531. replace = {
  17532. id : function(dir) { return fm.navHash2Id(dir.hash); },
  17533. name : function(dir) { return fm.escape(dir.i18 || dir.name); },
  17534. cssclass : function(dir) {
  17535. var cname = (dir.phash && ! dir.isroot ? '' : root)+' '+navdir+' '+fm.perms2class(dir);
  17536. dir.dirs && !dir.link && (cname += ' ' + collapsed) && dir.dirs == -1 && (cname += ' ' + chksubdir);
  17537. opts.getClass && (cname += ' ' + opts.getClass(dir));
  17538. dir.csscls && (cname += ' ' + fm.escape(dir.csscls));
  17539. return cname;
  17540. },
  17541. root : function(dir) {
  17542. var cls = '';
  17543. if (!dir.phash || dir.isroot) {
  17544. cls += ' '+wrapperRoot;
  17545. if (!dir.disabled || dir.disabled.length < 1) {
  17546. cls += ' '+pastable+' '+uploadable;
  17547. } else {
  17548. if ($.inArray('paste', dir.disabled) === -1) {
  17549. cls += ' '+pastable;
  17550. }
  17551. if ($.inArray('upload', dir.disabled) === -1) {
  17552. cls += ' '+uploadable;
  17553. }
  17554. }
  17555. return cls;
  17556. } else {
  17557. return '';
  17558. }
  17559. },
  17560. permissions : function(dir) { return !dir.read || !dir.write ? ptpl : ''; },
  17561. symlink : function(dir) { return dir.alias ? stpl : ''; },
  17562. style : function(dir) { return dir.icon ? fm.getIconStyle(dir) : ''; }
  17563. },
  17564. /**
  17565. * Return html for given dir
  17566. *
  17567. * @param Object directory
  17568. * @return String
  17569. */
  17570. itemhtml = function(dir) {
  17571. return tpl.replace(/(?:\{([a-z]+)\})/ig, function(m, key) {
  17572. var res = replace[key] ? replace[key](dir) : (dir[key] || '');
  17573. if (key === 'id' && dir.dirs == -1) {
  17574. subdirsQue[res] = res;
  17575. }
  17576. return res;
  17577. });
  17578. },
  17579. /**
  17580. * Return only dirs from files list
  17581. *
  17582. * @param Array files list
  17583. * @param Boolean do check exists
  17584. * @return Array
  17585. */
  17586. filter = function(files, checkExists) {
  17587. return $.map(files || [], function(f) {
  17588. return (f.mime === 'directory' && (!checkExists || $('#'+fm.navHash2Id(f.hash)).length)) ? f : null;
  17589. });
  17590. },
  17591. /**
  17592. * Find parent subtree for required directory
  17593. *
  17594. * @param String dir hash
  17595. * @return jQuery
  17596. */
  17597. findSubtree = function(hash) {
  17598. return hash ? $('#'+fm.navHash2Id(hash)).next('.'+subtree) : tree;
  17599. },
  17600. /**
  17601. * Find directory (wrapper) in required node
  17602. * before which we can insert new directory
  17603. *
  17604. * @param jQuery parent directory
  17605. * @param Object new directory
  17606. * @return jQuery
  17607. */
  17608. findSibling = function(subtree, dir) {
  17609. var node = subtree.children(':first'),
  17610. info;
  17611. while (node.length) {
  17612. info = fm.file(fm.navId2Hash(node.children('[id]').attr('id')));
  17613. if ((info = fm.file(fm.navId2Hash(node.children('[id]').attr('id'))))
  17614. && compare(dir, info) < 0) {
  17615. return node;
  17616. }
  17617. node = node.next();
  17618. }
  17619. return subtree.children('button.elfinder-navbar-pager-next');
  17620. },
  17621. /**
  17622. * Add new dirs in tree
  17623. *
  17624. * @param Array dirs list
  17625. * @return void
  17626. */
  17627. updateTree = function(dirs) {
  17628. var length = dirs.length,
  17629. orphans = [],
  17630. i = length,
  17631. tgts = $(),
  17632. done = {},
  17633. cwd = fm.cwd(),
  17634. append = function(parent, dirs, start, direction) {
  17635. var hashes = {},
  17636. curStart = 0,
  17637. max = fm.newAPI? Math.min(10000, Math.max(10, opts.subTreeMax)) : 10000,
  17638. setHashes = function() {
  17639. hashes = {};
  17640. $.each(dirs, function(i, d) {
  17641. hashes[d.hash] = i;
  17642. });
  17643. },
  17644. change = function(mode) {
  17645. if (mode === 'prepare') {
  17646. $.each(dirs, function(i, d) {
  17647. d.node && parent.append(d.node.hide());
  17648. });
  17649. } else if (mode === 'done') {
  17650. $.each(dirs, function(i, d) {
  17651. d.node && d.node.detach().show();
  17652. });
  17653. }
  17654. },
  17655. update = function(e, data) {
  17656. var i, changed;
  17657. e.stopPropagation();
  17658. if (data.select) {
  17659. render(getStart(data.select));
  17660. return;
  17661. }
  17662. if (data.change) {
  17663. change(data.change);
  17664. return;
  17665. }
  17666. if (data.removed && data.removed.length) {
  17667. dirs = $.grep(dirs, function(d) {
  17668. if (data.removed.indexOf(d.hash) === -1) {
  17669. return true;
  17670. } else {
  17671. !changed && (changed = true);
  17672. return false;
  17673. }
  17674. });
  17675. }
  17676. if (data.added && data.added.length) {
  17677. dirs = dirs.concat($.grep(data.added, function(d) {
  17678. if (hashes[d.hash] === void(0)) {
  17679. !changed && (changed = true);
  17680. return true;
  17681. } else {
  17682. return false;
  17683. }
  17684. }));
  17685. }
  17686. if (changed) {
  17687. dirs.sort(compare);
  17688. setHashes();
  17689. render(curStart);
  17690. }
  17691. },
  17692. getStart = function(target) {
  17693. if (hashes[target] !== void(0)) {
  17694. return Math.floor(hashes[target] / max) * max;
  17695. }
  17696. return void(0);
  17697. },
  17698. target = fm.navId2Hash(parent.prev('[id]').attr('id')),
  17699. render = function(start, direction) {
  17700. var html = [],
  17701. nodes = {},
  17702. total, page, s, parts, prev, next, prevBtn, nextBtn;
  17703. delete hasMoreDirs[target];
  17704. curStart = start;
  17705. parent.off('update.'+fm.namespace, update);
  17706. if (dirs.length > max) {
  17707. parent.on('update.'+fm.namespace, update);
  17708. if (start === void(0)) {
  17709. s = 0;
  17710. setHashes();
  17711. start = getStart(cwd.hash);
  17712. if (start === void(0)) {
  17713. start = 0;
  17714. }
  17715. }
  17716. parts = dirs.slice(start, start + max);
  17717. hasMoreDirs[target] = parent;
  17718. prev = start? Math.max(-1, start - max) : -1;
  17719. next = (start + max >= dirs.length)? 0 : start + max;
  17720. total = Math.ceil(dirs.length/max);
  17721. page = Math.ceil(start/max);
  17722. }
  17723. $.each(parts || dirs, function(i, d) {
  17724. html.push(itemhtml(d));
  17725. if (d.node) {
  17726. nodes[d.hash] = d.node;
  17727. }
  17728. });
  17729. if (prev > -1) {
  17730. prevBtn = $('<button class="elfinder-navbar-pager elfinder-navbar-pager-prev"/>')
  17731. .text(fm.i18n('btnPrevious', page, total))
  17732. .button({
  17733. icons: {
  17734. primary: "ui-icon-caret-1-n"
  17735. }
  17736. })
  17737. .on('click', function(e) {
  17738. e.preventDefault();
  17739. e.stopPropagation();
  17740. render(prev, 'up');
  17741. });
  17742. } else {
  17743. prevBtn = $();
  17744. }
  17745. if (next) {
  17746. nextBtn = $('<button class="elfinder-navbar-pager elfinder-navbar-pager-next"/>')
  17747. .text(fm.i18n('btnNext', page + 2, total))
  17748. .button({
  17749. icons: {
  17750. primary: "ui-icon-caret-1-s"
  17751. }
  17752. })
  17753. .on('click', function(e) {
  17754. e.preventDefault();
  17755. e.stopPropagation();
  17756. render(next, 'down');
  17757. });
  17758. } else {
  17759. nextBtn = $();
  17760. }
  17761. detach();
  17762. parent.empty()[parts? 'addClass' : 'removeClass']('elfinder-navbar-hasmore').append(prevBtn, html.join(''), nextBtn);
  17763. $.each(nodes, function(h, n) {
  17764. $('#'+fm.navHash2Id(h)).parent().replaceWith(n);
  17765. });
  17766. if (direction) {
  17767. autoScroll(fm.navHash2Id(parts[direction === 'up'? parts.length - 1 : 0].hash));
  17768. }
  17769. ! mobile && fm.lazy(function() { updateDroppable(null, parent); });
  17770. },
  17771. detach = function() {
  17772. $.each(parent.children('.elfinder-navbar-wrapper'), function(i, elm) {
  17773. var n = $(elm),
  17774. ch = n.children('[id]:first'),
  17775. h, c;
  17776. if (ch.hasClass(loaded)) {
  17777. h = fm.navId2Hash(ch.attr('id'));
  17778. if (h && (c = hashes[h]) !== void(0)) {
  17779. dirs[c].node = n.detach();
  17780. }
  17781. }
  17782. });
  17783. };
  17784. render();
  17785. },
  17786. dir, html, parent, sibling, init, atonce = {}, updates = [], base, node,
  17787. firstVol = true; // check for netmount volume
  17788. while (i--) {
  17789. dir = dirs[i];
  17790. if (done[dir.hash] || $('#'+fm.navHash2Id(dir.hash)).length) {
  17791. continue;
  17792. }
  17793. done[dir.hash] = true;
  17794. if ((parent = findSubtree(dir.phash)).length) {
  17795. if (dir.phash && ((init = !parent.children().length) || parent.hasClass('elfinder-navbar-hasmore') || (sibling = findSibling(parent, dir)).length)) {
  17796. if (init) {
  17797. if (!atonce[dir.phash]) {
  17798. atonce[dir.phash] = [];
  17799. }
  17800. atonce[dir.phash].push(dir);
  17801. } else {
  17802. if (sibling) {
  17803. node = itemhtml(dir);
  17804. sibling.before(node);
  17805. ! mobile && (tgts = tgts.add(node));
  17806. } else {
  17807. updates.push(dir);
  17808. }
  17809. }
  17810. } else {
  17811. node = itemhtml(dir);
  17812. parent[firstVol || dir.phash ? 'append' : 'prepend'](node);
  17813. firstVol = false;
  17814. if (!dir.phash || dir.isroot) {
  17815. base = $('#'+fm.navHash2Id(dir.hash)).parent();
  17816. }
  17817. ! mobile && updateDroppable(null, base);
  17818. }
  17819. } else {
  17820. orphans.push(dir);
  17821. }
  17822. }
  17823. // When init, html append at once
  17824. if (Object.keys(atonce).length){
  17825. $.each(atonce, function(p, dirs){
  17826. var parent = findSubtree(p),
  17827. html = [];
  17828. dirs.sort(compare);
  17829. append(parent, dirs);
  17830. });
  17831. }
  17832. if (updates.length) {
  17833. parent.trigger('update.' + fm.namespace, { added : updates });
  17834. }
  17835. if (orphans.length && orphans.length < length) {
  17836. updateTree(orphans);
  17837. return;
  17838. }
  17839. ! mobile && tgts.length && fm.lazy(function() { updateDroppable(tgts); });
  17840. },
  17841. /**
  17842. * sort function by dir.name
  17843. *
  17844. */
  17845. compare = function(dir1, dir2) {
  17846. if (! fm.sortAlsoTreeview) {
  17847. return fm.sortRules.name(dir1, dir2);
  17848. } else {
  17849. var asc = fm.sortOrder == 'asc',
  17850. type = fm.sortType,
  17851. rules = fm.sortRules,
  17852. res;
  17853. res = asc? rules[fm.sortType](dir1, dir2) : rules[fm.sortType](dir2, dir1);
  17854. return type !== 'name' && res === 0
  17855. ? res = asc ? rules.name(dir1, dir2) : rules.name(dir2, dir1)
  17856. : res;
  17857. }
  17858. },
  17859. /**
  17860. * Auto scroll to cwd
  17861. *
  17862. * @return void
  17863. */
  17864. autoScroll = function(target) {
  17865. var self = $(this),
  17866. dfrd = $.Deferred(),
  17867. current, parent, top, treeH, bottom, tgtTop;
  17868. self.data('autoScrTm') && clearTimeout(self.data('autoScrTm'));
  17869. self.data('autoScrTm', setTimeout(function() {
  17870. current = $('#'+(target || fm.navHash2Id(fm.cwd().hash)));
  17871. if (current.length) {
  17872. // expand parents directory
  17873. (openCwd? current : current.parent()).parents('.elfinder-navbar-wrapper').children('.'+loaded).addClass(expanded).next('.'+subtree).show();
  17874. parent = tree.parent().stop(false, true);
  17875. top = parent.offset().top;
  17876. treeH = parent.height();
  17877. bottom = top + treeH - current.outerHeight();
  17878. tgtTop = current.offset().top;
  17879. if (tgtTop < top || tgtTop > bottom) {
  17880. parent.animate({
  17881. scrollTop : parent.scrollTop() + tgtTop - top - treeH / 3
  17882. }, {
  17883. duration : 'fast',
  17884. complete : function() { dfrd.resolve(); }
  17885. });
  17886. } else {
  17887. dfrd.resolve();
  17888. }
  17889. } else {
  17890. dfrd.reject();
  17891. }
  17892. }, 100));
  17893. return dfrd;
  17894. },
  17895. /**
  17896. * Get hashes array of items of the bottom of the leaf root back from the target
  17897. *
  17898. * @param Object elFinder item(directory) object
  17899. * @return Array hashes
  17900. */
  17901. getEnds = function(d) {
  17902. var cur = d || fm.cwd(),
  17903. res = cur.hash? [ cur.hash ] : [],
  17904. phash, root, dir;
  17905. root = fm.root(cur.hash);
  17906. dir = fm.file(root);
  17907. while (dir && (phash = dir.phash)) {
  17908. res.unshift(phash);
  17909. root = fm.root(phash);
  17910. dir = fm.file(root);
  17911. if ($('#'+fm.navHash2Id(dir.hash)).hasClass(loaded)) {
  17912. break;
  17913. }
  17914. }
  17915. return res;
  17916. },
  17917. /**
  17918. * Select pages back in order to display the target
  17919. *
  17920. * @param Object elFinder item(directory) object
  17921. * @return Object jQuery node object of target node
  17922. */
  17923. selectPages = function(current) {
  17924. var cur = current || fm.cwd(),
  17925. curHash = cur.hash,
  17926. node = $('#'+fm.navHash2Id(curHash));
  17927. if (!node.length) {
  17928. while(cur && cur.phash) {
  17929. if (hasMoreDirs[cur.phash] && !$('#'+fm.navHash2Id(cur.hash)).length) {
  17930. hasMoreDirs[cur.phash].trigger('update.'+fm.namespace, { select : cur.hash });
  17931. }
  17932. cur = fm.file(cur.phash);
  17933. }
  17934. node = $('#'+fm.navHash2Id(curHash));
  17935. }
  17936. return node;
  17937. },
  17938. /**
  17939. * Flag indicating that synchronization is currently in progress
  17940. *
  17941. * @type Boolean
  17942. */
  17943. syncing,
  17944. /**
  17945. * Mark current directory as active
  17946. * If current directory is not in tree - load it and its parents
  17947. *
  17948. * @param Array directory objects of cwd
  17949. * @param Boolean do auto scroll
  17950. * @return Object jQuery Deferred
  17951. */
  17952. sync = function(cwdDirs, aScr) {
  17953. var cwd = fm.cwd(),
  17954. cwdhash = cwd.hash,
  17955. autoScr = aScr === void(0)? syncTree : aScr,
  17956. loadParents = function(dir) {
  17957. var dfd = $.Deferred(),
  17958. reqs = [],
  17959. ends = getEnds(dir),
  17960. makeReq = function(cmd, h, until) {
  17961. var data = {
  17962. cmd : cmd,
  17963. target : h
  17964. };
  17965. if (until) {
  17966. data.until = until;
  17967. }
  17968. return fm.request({
  17969. data : data,
  17970. preventFail : true
  17971. });
  17972. },
  17973. baseHash, baseId;
  17974. reqs = $.map(ends, function(h) {
  17975. var d = fm.file(h),
  17976. isRoot = d? fm.isRoot(d) : false,
  17977. node = $('#'+fm.navHash2Id(h)),
  17978. getPhash = function(h, dep) {
  17979. var d, ph,
  17980. depth = dep || 1;
  17981. ph = (d = fm.file(h))? d.phash : false;
  17982. if (ph && depth > 1) {
  17983. return getPhash(ph, --depth);
  17984. }
  17985. return ph;
  17986. },
  17987. until,
  17988. closest = (function() {
  17989. var phash = getPhash(h);
  17990. until = phash;
  17991. while (phash) {
  17992. if ($('#'+fm.navHash2Id(phash)).hasClass(loaded)) {
  17993. break;
  17994. }
  17995. until = phash;
  17996. phash = getPhash(phash);
  17997. }
  17998. if (!phash) {
  17999. until = void(0);
  18000. phash = fm.root(h);
  18001. }
  18002. return phash;
  18003. })(),
  18004. cmd;
  18005. if (!node.hasClass(loaded) && (isRoot || !d || !$('#'+fm.navHash2Id(d.phash)).hasClass(loaded))) {
  18006. if (isRoot || closest === getPhash(h) || closest === getPhash(h, 2)) {
  18007. until = void(0);
  18008. cmd = 'tree';
  18009. if (!isRoot) {
  18010. h = getPhash(h);
  18011. }
  18012. } else {
  18013. cmd = 'parents';
  18014. }
  18015. if (!baseHash) {
  18016. baseHash = (cmd === 'tree')? h : closest;
  18017. }
  18018. return makeReq(cmd, h, until);
  18019. }
  18020. return null;
  18021. });
  18022. if (reqs.length) {
  18023. selectPages(fm.file(baseHash));
  18024. baseId = fm.navHash2Id(baseHash);
  18025. autoScr && autoScroll(baseId);
  18026. baseNode = $('#'+baseId);
  18027. spinner = $(fm.res('tpl', 'navspinner')).insertBefore(baseNode.children('.'+arrow));
  18028. baseNode.removeClass(collapsed);
  18029. $.when.apply($, reqs)
  18030. .done(function() {
  18031. var res = {},data, treeDirs, dirs, argLen, i;
  18032. argLen = arguments.length;
  18033. if (argLen > 0) {
  18034. for (i = 0; i < argLen; i++) {
  18035. data = arguments[i].tree || [];
  18036. res[ends[i]] = Object.assign([], filter(data));
  18037. }
  18038. }
  18039. dfd.resolve(res);
  18040. })
  18041. .fail(function() {
  18042. dfd.reject();
  18043. });
  18044. return dfd;
  18045. } else {
  18046. return dfd.resolve();
  18047. }
  18048. },
  18049. done= function(res, dfrd) {
  18050. var open = function() {
  18051. if (openRoot && baseNode) {
  18052. findSubtree(baseNode.hash).show().prev(selNavdir).addClass(expanded);
  18053. openRoot = false;
  18054. }
  18055. if (autoScr) {
  18056. autoScroll().done(checkSubdirs);
  18057. } else {
  18058. checkSubdirs();
  18059. }
  18060. },
  18061. current;
  18062. if (res) {
  18063. $.each(res, function(endHash, dirs) {
  18064. dirs && updateTree(dirs);
  18065. selectPages(fm.file(endHash));
  18066. dirs && updateArrows(dirs, loaded);
  18067. });
  18068. }
  18069. if (cwdDirs) {
  18070. (fm.api < 2.1) && cwdDirs.push(cwd);
  18071. updateTree(cwdDirs);
  18072. }
  18073. // set current node
  18074. current = selectPages();
  18075. if (!current.hasClass(active)) {
  18076. tree.find(selNavdir+'.'+active).removeClass(active);
  18077. current.addClass(active);
  18078. }
  18079. // mark as loaded to cwd parents
  18080. current.parents('.elfinder-navbar-wrapper').children('.'+navdir).addClass(loaded);
  18081. if (res) {
  18082. fm.lazy(open).done(function() {
  18083. dfrd.resolve();
  18084. });
  18085. } else {
  18086. open();
  18087. dfrd.resolve();
  18088. }
  18089. },
  18090. rmSpinner = function(fail) {
  18091. if (baseNode) {
  18092. spinner.remove();
  18093. baseNode.addClass(collapsed + (fail? '' : (' ' + loaded)));
  18094. }
  18095. },
  18096. dfrd = $.Deferred(),
  18097. baseNode, spinner;
  18098. if (!$('#'+fm.navHash2Id(cwdhash)).length) {
  18099. syncing = true;
  18100. loadParents()
  18101. .done(function(res) {
  18102. done(res, dfrd);
  18103. rmSpinner();
  18104. })
  18105. .fail(function() {
  18106. rmSpinner(true);
  18107. dfrd.reject();
  18108. })
  18109. .always(function() {
  18110. syncing = false;
  18111. });
  18112. } else {
  18113. done(void(0), dfrd);
  18114. }
  18115. // trigger 'treesync' with my $.Deferred
  18116. fm.trigger('treesync', dfrd);
  18117. return dfrd;
  18118. },
  18119. /**
  18120. * Make writable and not root dirs droppable
  18121. *
  18122. * @return void
  18123. */
  18124. updateDroppable = function(target, node) {
  18125. var limit = 100,
  18126. next;
  18127. if (!target) {
  18128. if (!node || node.closest('div.'+wrapperRoot).hasClass(uploadable)) {
  18129. (node || tree.find('div.'+uploadable)).find(selNavdir+':not(.elfinder-ro,.elfinder-na)').addClass('native-droppable');
  18130. }
  18131. if (!node || node.closest('div.'+wrapperRoot).hasClass(pastable)) {
  18132. target = (node || tree.find('div.'+pastable)).find(selNavdir+':not(.'+droppable+')');
  18133. } else {
  18134. target = $();
  18135. }
  18136. if (node) {
  18137. // check leaf roots
  18138. node.children('div.'+wrapperRoot).each(function() {
  18139. updateDroppable(null, $(this));
  18140. });
  18141. }
  18142. }
  18143. // make droppable on async
  18144. if (target.length) {
  18145. fm.asyncJob(function(elm) {
  18146. $(elm).droppable(droppableopts);
  18147. }, $.makeArray(target), {
  18148. interval : 20,
  18149. numPerOnce : 100
  18150. });
  18151. }
  18152. },
  18153. /**
  18154. * Check required folders for subfolders and update arrow classes
  18155. *
  18156. * @param Array folders to check
  18157. * @param String css class
  18158. * @return void
  18159. */
  18160. updateArrows = function(dirs, cls) {
  18161. var sel = cls == loaded
  18162. ? '.'+collapsed+':not(.'+loaded+')'
  18163. : ':not(.'+collapsed+')';
  18164. $.each(dirs, function(i, dir) {
  18165. $('#'+fm.navHash2Id(dir.phash)+sel)
  18166. .filter(function() { return $.grep($(this).next('.'+subtree).children(), function(n) {
  18167. return ($(n).children().hasClass(root))? false : true;
  18168. }).length > 0; })
  18169. .addClass(cls);
  18170. });
  18171. },
  18172. /**
  18173. * Navigation tree
  18174. *
  18175. * @type JQuery
  18176. */
  18177. tree = $(this).addClass(treeclass)
  18178. // make dirs draggable and toggle hover class
  18179. .on('mouseenter mouseleave', selNavdir, function(e) {
  18180. var enter = (e.type === 'mouseenter');
  18181. if (enter && scrolling) { return; }
  18182. var link = $(this);
  18183. if (!link.hasClass(dropover+' '+disabled)) {
  18184. if (!mobile && enter && !link.data('dragRegisted') && !link.hasClass(root+' '+draggable+' elfinder-na elfinder-wo')) {
  18185. link.data('dragRegisted', true);
  18186. if (fm.isCommandEnabled('copy', fm.navId2Hash(link.attr('id')))) {
  18187. link.draggable(fm.draggable);
  18188. }
  18189. }
  18190. link.toggleClass(hover, enter);
  18191. }
  18192. })
  18193. // native drag enter
  18194. .on('dragenter', selNavdir, function(e) {
  18195. if (e.originalEvent.dataTransfer) {
  18196. var dst = $(this);
  18197. dst.addClass(hover);
  18198. if (dst.is('.'+collapsed+':not(.'+expanded+')')) {
  18199. dst.data('expandTimer', setTimeout(function() {
  18200. dst.is('.'+collapsed+'.'+hover) && dst.children('.'+arrow).trigger('click');
  18201. }, 500));
  18202. }
  18203. }
  18204. })
  18205. // native drag leave
  18206. .on('dragleave', selNavdir, function(e) {
  18207. if (e.originalEvent.dataTransfer) {
  18208. var dst = $(this);
  18209. dst.data('expandTimer') && clearTimeout(dst.data('expandTimer'));
  18210. dst.removeClass(hover);
  18211. }
  18212. })
  18213. // open dir or open subfolders in tree
  18214. .on('click', selNavdir, function(e) {
  18215. var link = $(this),
  18216. hash = fm.navId2Hash(link.attr('id')),
  18217. file = fm.file(hash);
  18218. if (link.data('longtap')) {
  18219. link.removeData('longtap');
  18220. e.stopPropagation();
  18221. return;
  18222. }
  18223. if (hash != fm.cwd().hash && !link.hasClass(disabled)) {
  18224. fm.exec('open', hash).done(function() {
  18225. fm.one('opendone', function() {
  18226. fm.select({selected: [hash], origin: 'navbar'});
  18227. });
  18228. });
  18229. } else {
  18230. if (link.hasClass(collapsed)) {
  18231. link.children('.'+arrow).trigger('click');
  18232. }
  18233. fm.select({selected: [hash], origin: 'navbar'});
  18234. }
  18235. })
  18236. // for touch device
  18237. .on('touchstart', selNavdir, function(e) {
  18238. if (e.originalEvent.touches.length > 1) {
  18239. return;
  18240. }
  18241. var evt = e.originalEvent,
  18242. p;
  18243. if (e.target.nodeName === 'INPUT') {
  18244. e.stopPropagation();
  18245. return;
  18246. }
  18247. p = $(this).addClass(hover)
  18248. .removeData('longtap')
  18249. .data('tmlongtap', setTimeout(function(e){
  18250. // long tap
  18251. p.data('longtap', true);
  18252. fm.trigger('contextmenu', {
  18253. 'type' : 'navbar',
  18254. 'targets' : [fm.navId2Hash(p.attr('id'))],
  18255. 'x' : evt.touches[0].pageX,
  18256. 'y' : evt.touches[0].pageY
  18257. });
  18258. }, 500));
  18259. })
  18260. .on('touchmove touchend', selNavdir, function(e) {
  18261. if (e.target.nodeName === 'INPUT') {
  18262. e.stopPropagation();
  18263. return;
  18264. }
  18265. clearTimeout($(this).data('tmlongtap'));
  18266. if (e.type == 'touchmove') {
  18267. $(this).removeClass(hover);
  18268. }
  18269. })
  18270. // toggle subfolders in tree
  18271. .on('click', selNavdir+'.'+collapsed+' .'+arrow, function(e) {
  18272. var arrow = $(this),
  18273. link = arrow.parent(selNavdir),
  18274. stree = link.next('.'+subtree),
  18275. dfrd = $.Deferred(),
  18276. slideTH = 30, cnt;
  18277. e.stopPropagation();
  18278. if (link.hasClass(loaded)) {
  18279. link.toggleClass(expanded);
  18280. fm.lazy(function() {
  18281. cnt = link.hasClass(expanded)? stree.children().length + stree.find('div.elfinder-navbar-subtree[style*=block]').children().length : stree.find('div:visible').length;
  18282. if (cnt > slideTH) {
  18283. stree.toggle();
  18284. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  18285. checkSubdirs();
  18286. } else {
  18287. stree.stop(true, true)[link.hasClass(expanded)? 'slideDown' : 'slideUp']('normal', function(){
  18288. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  18289. checkSubdirs();
  18290. });
  18291. }
  18292. }).always(function() {
  18293. dfrd.resolve();
  18294. });
  18295. } else {
  18296. spinner.insertBefore(arrow);
  18297. link.removeClass(collapsed);
  18298. fm.request({cmd : 'tree', target : fm.navId2Hash(link.attr('id'))})
  18299. .done(function(data) {
  18300. updateTree(Object.assign([], filter(data.tree)));
  18301. if (stree.children().length) {
  18302. link.addClass(collapsed+' '+expanded);
  18303. if (stree.children().length > slideTH) {
  18304. stree.show();
  18305. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  18306. checkSubdirs();
  18307. } else {
  18308. stree.stop(true, true).slideDown('normal', function(){
  18309. fm.draggingUiHelper && fm.draggingUiHelper.data('refreshPositions', 1);
  18310. checkSubdirs();
  18311. });
  18312. }
  18313. }
  18314. })
  18315. .always(function(data) {
  18316. spinner.remove();
  18317. link.addClass(loaded);
  18318. fm.one('treedone', function() {
  18319. dfrd.resolve();
  18320. });
  18321. });
  18322. }
  18323. arrow.data('dfrd', dfrd);
  18324. })
  18325. .on('contextmenu', selNavdir, function(e) {
  18326. var self = $(this);
  18327. // now dirname editing
  18328. if (self.find('input:text').length) {
  18329. e.stopPropagation();
  18330. return;
  18331. }
  18332. e.preventDefault();
  18333. fm.trigger('contextmenu', {
  18334. 'type' : 'navbar',
  18335. 'targets' : [fm.navId2Hash($(this).attr('id'))],
  18336. 'x' : e.pageX,
  18337. 'y' : e.pageY
  18338. });
  18339. self.addClass('ui-state-hover');
  18340. fm.getUI('contextmenu').children().on('mouseenter', function() {
  18341. self.addClass('ui-state-hover');
  18342. });
  18343. fm.bind('closecontextmenu', function() {
  18344. self.removeClass('ui-state-hover');
  18345. });
  18346. })
  18347. .on('scrolltoview', selNavdir, function(e, data) {
  18348. var self = $(this);
  18349. autoScroll(self.attr('id')).done(function() {
  18350. if (!data || data.blink === 'undefined' || data.blink) {
  18351. fm.resources.blink(self, 'lookme');
  18352. }
  18353. });
  18354. })
  18355. // prepend fake dir
  18356. .on('create.'+fm.namespace, function(e, item) {
  18357. var pdir = findSubtree(item.phash),
  18358. lock = item.move || false,
  18359. dir = $(itemhtml(item)).addClass('elfinder-navbar-wrapper-tmp'),
  18360. selected = fm.selected();
  18361. lock && selected.length && fm.trigger('lockfiles', {files: selected});
  18362. pdir.prepend(dir);
  18363. }),
  18364. scrolling = false,
  18365. navbarScrTm,
  18366. // move tree into navbar
  18367. navbar = fm.getUI('navbar').append(tree).show().on('scroll', function() {
  18368. scrolling = true;
  18369. navbarScrTm && clearTimeout(navbarScrTm);
  18370. navbarScrTm = setTimeout(function() {
  18371. scrolling = false;
  18372. checkSubdirs();
  18373. }, 50);
  18374. }),
  18375. prevSortTreeview = fm.sortAlsoTreeview;
  18376. fm.open(function(e) {
  18377. var data = e.data,
  18378. dirs = filter(data.files),
  18379. contextmenu = fm.getUI('contextmenu');
  18380. data.init && tree.empty();
  18381. if (fm.UA.iOS) {
  18382. navbar.removeClass('overflow-scrolling-touch').addClass('overflow-scrolling-touch');
  18383. }
  18384. if (dirs.length) {
  18385. fm.lazy(function() {
  18386. if (!contextmenu.data('cmdMaps')) {
  18387. contextmenu.data('cmdMaps', {});
  18388. }
  18389. updateTree(dirs);
  18390. updateArrows(dirs, loaded);
  18391. sync(dirs);
  18392. });
  18393. } else {
  18394. sync();
  18395. }
  18396. })
  18397. // add new dirs
  18398. .add(function(e) {
  18399. var dirs = filter(e.data.added);
  18400. if (dirs.length) {
  18401. updateTree(dirs);
  18402. updateArrows(dirs, collapsed);
  18403. }
  18404. })
  18405. // update changed dirs
  18406. .change(function(e) {
  18407. // do ot perfome while syncing
  18408. if (syncing) {
  18409. return;
  18410. }
  18411. var dirs = filter(e.data.changed, true),
  18412. length = dirs.length,
  18413. l = length,
  18414. tgts = $(),
  18415. changed = {},
  18416. dir, phash, node, tmp, realParent, reqParent, realSibling, reqSibling, isExpanded, isLoaded, parent, subdirs;
  18417. $.each(hasMoreDirs, function(h, node) {
  18418. node.trigger('update.'+fm.namespace, { change: 'prepare' });
  18419. });
  18420. while (l--) {
  18421. dir = dirs[l];
  18422. phash = dir.phash;
  18423. if ((node = $('#'+fm.navHash2Id(dir.hash))).length) {
  18424. parent = node.parent();
  18425. if (phash) {
  18426. realParent = node.closest('.'+subtree);
  18427. reqParent = findSubtree(phash);
  18428. realSibling = node.parent().next();
  18429. reqSibling = findSibling(reqParent, dir);
  18430. if (!reqParent.length) {
  18431. continue;
  18432. }
  18433. if (reqParent[0] !== realParent[0] || realSibling.get(0) !== reqSibling.get(0)) {
  18434. reqSibling.length ? reqSibling.before(parent) : reqParent.append(parent);
  18435. }
  18436. }
  18437. isExpanded = node.hasClass(expanded);
  18438. isLoaded = node.hasClass(loaded);
  18439. tmp = $(itemhtml(dir));
  18440. node.replaceWith(tmp.children(selNavdir));
  18441. ! mobile && updateDroppable(null, parent);
  18442. if (dir.dirs
  18443. && (isExpanded || isLoaded)
  18444. && (node = $('#'+fm.navHash2Id(dir.hash)))
  18445. && node.next('.'+subtree).children().length) {
  18446. isExpanded && node.addClass(expanded);
  18447. isLoaded && node.addClass(loaded);
  18448. }
  18449. subdirs |= dir.dirs == -1;
  18450. }
  18451. }
  18452. // to check subdirs
  18453. if (subdirs) {
  18454. checkSubdirs();
  18455. }
  18456. $.each(hasMoreDirs, function(h, node) {
  18457. node.trigger('update.'+fm.namespace, { change: 'done' });
  18458. });
  18459. length && sync(void(0), false);
  18460. })
  18461. // remove dirs
  18462. .remove(function(e) {
  18463. var dirs = e.data.removed,
  18464. l = dirs.length,
  18465. node, stree, removed;
  18466. $.each(hasMoreDirs, function(h, node) {
  18467. node.trigger('update.'+fm.namespace, { removed : dirs });
  18468. node.trigger('update.'+fm.namespace, { change: 'prepare' });
  18469. });
  18470. while (l--) {
  18471. if ((node = $('#'+fm.navHash2Id(dirs[l]))).length) {
  18472. removed = true;
  18473. stree = node.closest('.'+subtree);
  18474. node.parent().detach();
  18475. if (!stree.children().length) {
  18476. stree.hide().prev(selNavdir).removeClass(collapsed+' '+expanded+' '+loaded);
  18477. }
  18478. }
  18479. }
  18480. removed && fm.getUI('navbar').children('.ui-resizable-handle').trigger('resize');
  18481. $.each(hasMoreDirs, function(h, node) {
  18482. node.trigger('update.'+fm.namespace, { change: 'done' });
  18483. });
  18484. })
  18485. // lock/unlock dirs while moving
  18486. .bind('lockfiles unlockfiles', function(e) {
  18487. var lock = e.type == 'lockfiles',
  18488. helperLocked = e.data.helper? e.data.helper.data('locked') : false,
  18489. act = (lock && !helperLocked) ? 'disable' : 'enable',
  18490. dirs = $.grep(e.data.files||[], function(h) {
  18491. var dir = fm.file(h);
  18492. return dir && dir.mime == 'directory' ? true : false;
  18493. });
  18494. $.each(dirs, function(i, hash) {
  18495. var dir = $('#'+fm.navHash2Id(hash));
  18496. if (dir.length && !helperLocked) {
  18497. dir.hasClass(draggable) && dir.draggable(act);
  18498. dir.hasClass(droppable) && dir.droppable(act);
  18499. dir[lock ? 'addClass' : 'removeClass'](disabled);
  18500. }
  18501. });
  18502. })
  18503. .bind('sortchange', function() {
  18504. if (fm.sortAlsoTreeview || prevSortTreeview !== fm.sortAlsoTreeview) {
  18505. var dirs,
  18506. ends = [],
  18507. endsMap = {},
  18508. endsVid = {},
  18509. topVid = '',
  18510. single = false,
  18511. current;
  18512. fm.lazy(function() {
  18513. dirs = filter(fm.files());
  18514. prevSortTreeview = fm.sortAlsoTreeview;
  18515. tree.empty();
  18516. // append volume roots at first
  18517. updateTree($.map(fm.roots, function(h) {
  18518. var dir = fm.file(h);
  18519. return dir && !dir.phash? dir : null;
  18520. }));
  18521. if (!Object.keys(hasMoreDirs).length) {
  18522. updateTree(dirs);
  18523. current = selectPages();
  18524. updateArrows(dirs, loaded);
  18525. } else {
  18526. ends = getEnds();
  18527. if (ends.length > 1) {
  18528. $.each(ends, function(i, end) {
  18529. var vid = fm.file(fm.root(end)).volumeid;
  18530. if (i === 0) {
  18531. topVid = vid;
  18532. }
  18533. endsVid[vid] = end;
  18534. endsMap[end] = [];
  18535. });
  18536. $.each(dirs, function(i, d) {
  18537. if (!d.volumeid) {
  18538. single = true;
  18539. return false;
  18540. }
  18541. endsMap[endsVid[d.volumeid] || endsVid[topVid]].push(d);
  18542. });
  18543. } else {
  18544. single = true;
  18545. }
  18546. if (single) {
  18547. $.each(ends, function(i, endHash) {
  18548. updateTree(dirs);
  18549. current = selectPages(fm.file(endHash));
  18550. updateArrows(dirs, loaded);
  18551. });
  18552. } else {
  18553. $.each(endsMap, function(endHash, dirs) {
  18554. updateTree(dirs);
  18555. current = selectPages(fm.file(endHash));
  18556. updateArrows(dirs, loaded);
  18557. });
  18558. }
  18559. }
  18560. sync();
  18561. }, 100);
  18562. }
  18563. });
  18564. });
  18565. return this;
  18566. };
  18567. /*
  18568. * File: /js/ui/uploadButton.js
  18569. */
  18570. /**
  18571. * @class elFinder toolbar's button tor upload file
  18572. *
  18573. * @author Dmitry (dio) Levashov
  18574. **/
  18575. $.fn.elfinderuploadbutton = function(cmd) {
  18576. return this.each(function() {
  18577. var fm = cmd.fm,
  18578. button = $(this).elfinderbutton(cmd)
  18579. .off('click'),
  18580. form = $('<form/>').appendTo(button),
  18581. input = $('<input type="file" multiple="true" title="'+cmd.fm.i18n('selectForUpload')+'"/>')
  18582. .on('change', function() {
  18583. var _input = $(this);
  18584. if (_input.val()) {
  18585. fm.exec('upload', {input : _input.remove()[0]}, void(0), fm.cwd().hash);
  18586. input.clone(true).appendTo(form);
  18587. }
  18588. })
  18589. .on('dragover', function(e) {
  18590. e.originalEvent.dataTransfer.dropEffect = 'copy';
  18591. });
  18592. form.append(input.clone(true));
  18593. cmd.change(function() {
  18594. form[cmd.disabled() ? 'hide' : 'show']();
  18595. })
  18596. .change();
  18597. });
  18598. };
  18599. /*
  18600. * File: /js/ui/viewbutton.js
  18601. */
  18602. /**
  18603. * @class elFinder toolbar button to switch current directory view.
  18604. *
  18605. * @author Dmitry (dio) Levashov
  18606. **/
  18607. $.fn.elfinderviewbutton = function(cmd) {
  18608. return this.each(function() {
  18609. var button = $(this).elfinderbutton(cmd),
  18610. icon = button.children('.elfinder-button-icon');
  18611. cmd.change(function() {
  18612. var icons = cmd.value == 'icons';
  18613. icon.toggleClass('elfinder-button-icon-view-list', icons);
  18614. cmd.className = icons? 'view-list' : '';
  18615. cmd.title = cmd.fm.i18n(icons ? 'viewlist' : 'viewicons');
  18616. button.attr('title', cmd.title);
  18617. });
  18618. });
  18619. };
  18620. /*
  18621. * File: /js/ui/workzone.js
  18622. */
  18623. /**
  18624. * @class elfinderworkzone - elFinder container for nav and current directory
  18625. * @author Dmitry (dio) Levashov
  18626. **/
  18627. $.fn.elfinderworkzone = function(fm) {
  18628. var cl = 'elfinder-workzone';
  18629. this.not('.'+cl).each(function() {
  18630. var wz = $(this).addClass(cl),
  18631. wdelta = wz.outerHeight(true) - wz.height(),
  18632. prevH = Math.round(wz.height()),
  18633. parent = wz.parent(),
  18634. fitsize = function(e) {
  18635. var height = parent.height() - wdelta,
  18636. style = parent.attr('style'),
  18637. curH = Math.round(wz.height());
  18638. if (e) {
  18639. e.preventDefault();
  18640. e.stopPropagation();
  18641. }
  18642. parent.css('overflow', 'hidden')
  18643. .children(':visible:not(.'+cl+')').each(function() {
  18644. var ch = $(this);
  18645. if (ch.css('position') != 'absolute' && ch.css('position') != 'fixed') {
  18646. height -= ch.outerHeight(true);
  18647. }
  18648. });
  18649. parent.attr('style', style || '');
  18650. height = Math.max(0, Math.round(height));
  18651. if (prevH !== height || curH !== height) {
  18652. prevH = Math.round(wz.height());
  18653. wz.height(height);
  18654. fm.trigger('wzresize');
  18655. }
  18656. },
  18657. cssloaded = function() {
  18658. wdelta = wz.outerHeight(true) - wz.height();
  18659. fitsize();
  18660. };
  18661. parent.on('resize.' + fm.namespace, fitsize);
  18662. fm.one('cssloaded', cssloaded).bind('uiresize', fitsize);
  18663. });
  18664. return this;
  18665. };
  18666. /*
  18667. * File: /js/commands/archive.js
  18668. */
  18669. /**
  18670. * @class elFinder command "archive"
  18671. * Archive selected files
  18672. *
  18673. * @author Dmitry (dio) Levashov
  18674. **/
  18675. elFinder.prototype.commands.archive = function() {
  18676. var self = this,
  18677. fm = self.fm,
  18678. mimes = [],
  18679. dfrd;
  18680. this.variants = [];
  18681. this.disableOnSearch = false;
  18682. this.nextAction = {};
  18683. /**
  18684. * Update mimes on open/reload
  18685. *
  18686. * @return void
  18687. **/
  18688. fm.bind('open reload', function() {
  18689. self.variants = [];
  18690. $.each((mimes = fm.option('archivers')['create'] || []), function(i, mime) {
  18691. self.variants.push([mime, fm.mime2kind(mime)]);
  18692. });
  18693. self.change();
  18694. });
  18695. this.getstate = function(select) {
  18696. var sel = this.files(select),
  18697. cnt = sel.length,
  18698. chk = (cnt && ! fm.isRoot(sel[0]) && (fm.file(sel[0].phash) || {}).write && ! $.grep(sel, function(f){ return f.read ? false : true; }).length),
  18699. cwdId;
  18700. if (chk && fm.searchStatus.state > 1) {
  18701. cwdId = fm.cwd().volumeid;
  18702. chk = (cnt === $.grep(sel, function(f) { return f.read && f.hash.indexOf(cwdId) === 0 ? true : false; }).length);
  18703. }
  18704. return chk && !this._disabled && mimes.length && (cnt || (dfrd && dfrd.state() == 'pending')) ? 0 : -1;
  18705. };
  18706. this.exec = function(hashes, type) {
  18707. var files = this.files(hashes),
  18708. cnt = files.length,
  18709. mime = type || mimes[0],
  18710. cwd = fm.file(files[0].phash) || null,
  18711. error = ['errArchive', 'errPerm', 'errCreatingTempDir', 'errFtpDownloadFile', 'errFtpUploadFile', 'errFtpMkdir', 'errArchiveExec', 'errExtractExec', 'errRm'],
  18712. i, open;
  18713. dfrd = $.Deferred().fail(function(error) {
  18714. error && fm.error(error);
  18715. });
  18716. if (! (cnt && mimes.length && $.inArray(mime, mimes) !== -1)) {
  18717. return dfrd.reject();
  18718. }
  18719. if (!cwd.write) {
  18720. return dfrd.reject(error);
  18721. }
  18722. for (i = 0; i < cnt; i++) {
  18723. if (!files[i].read) {
  18724. return dfrd.reject(error);
  18725. }
  18726. }
  18727. self.mime = mime;
  18728. self.prefix = ((cnt > 1)? 'Archive' : files[0].name) + (fm.option('archivers')['createext']? '.' + fm.option('archivers')['createext'][mime] : '');
  18729. self.data = {targets : self.hashes(hashes), type : mime};
  18730. if (fm.cwd().hash !== cwd.hash) {
  18731. open = fm.exec('open', cwd.hash).done(function() {
  18732. fm.one('cwdrender', function() {
  18733. fm.selectfiles({files : hashes});
  18734. dfrd = $.proxy(fm.res('mixin', 'make'), self)();
  18735. });
  18736. });
  18737. } else {
  18738. fm.selectfiles({files : hashes});
  18739. dfrd = $.proxy(fm.res('mixin', 'make'), self)();
  18740. }
  18741. return dfrd;
  18742. };
  18743. };
  18744. /*
  18745. * File: /js/commands/back.js
  18746. */
  18747. /**
  18748. * @class elFinder command "back"
  18749. * Open last visited folder
  18750. *
  18751. * @author Dmitry (dio) Levashov
  18752. **/
  18753. (elFinder.prototype.commands.back = function() {
  18754. this.alwaysEnabled = true;
  18755. this.updateOnSelect = false;
  18756. this.shortcuts = [{
  18757. pattern : 'ctrl+left backspace'
  18758. }];
  18759. this.getstate = function() {
  18760. return this.fm.history.canBack() ? 0 : -1;
  18761. };
  18762. this.exec = function() {
  18763. return this.fm.history.back();
  18764. };
  18765. }).prototype = { forceLoad : true }; // this is required command
  18766. /*
  18767. * File: /js/commands/chmod.js
  18768. */
  18769. /**
  18770. * @class elFinder command "chmod".
  18771. * Chmod files.
  18772. *
  18773. * @type elFinder.command
  18774. * @author Naoki Sawada
  18775. */
  18776. elFinder.prototype.commands.chmod = function() {
  18777. this.updateOnSelect = false;
  18778. var fm = this.fm,
  18779. level = {
  18780. 0 : 'owner',
  18781. 1 : 'group',
  18782. 2 : 'other'
  18783. },
  18784. msg = {
  18785. read : fm.i18n('read'),
  18786. write : fm.i18n('write'),
  18787. execute : fm.i18n('execute'),
  18788. perm : fm.i18n('perm'),
  18789. kind : fm.i18n('kind'),
  18790. files : fm.i18n('files')
  18791. },
  18792. isPerm = function(perm){
  18793. return (!isNaN(parseInt(perm, 8) && parseInt(perm, 8) <= 511) || perm.match(/^([r-][w-][x-]){3}$/i));
  18794. };
  18795. this.tpl = {
  18796. main : '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}</div>'
  18797. +'{dataTable}',
  18798. itemTitle : '<strong>{name}</strong><span id="elfinder-info-kind">{kind}</span>',
  18799. groupTitle : '<strong>{items}: {num}</strong>',
  18800. dataTable : '<table id="{id}-table-perm"><tr><td>{0}</td><td>{1}</td><td>{2}</td></tr></table>'
  18801. +'<div class="">'+msg.perm+': <input id="{id}-perm" type="text" size="4" maxlength="3" value="{value}"></div>',
  18802. fieldset : '<fieldset id="{id}-fieldset-{level}"><legend>{f_title}{name}</legend>'
  18803. +'<input type="checkbox" value="4" id="{id}-read-{level}-perm"{checked-r}> <label for="{id}-read-{level}-perm">'+msg.read+'</label><br>'
  18804. +'<input type="checkbox" value="6" id="{id}-write-{level}-perm"{checked-w}> <label for="{id}-write-{level}-perm">'+msg.write+'</label><br>'
  18805. +'<input type="checkbox" value="5" id="{id}-execute-{level}-perm"{checked-x}> <label for="{id}-execute-{level}-perm">'+msg.execute+'</label><br>'
  18806. };
  18807. this.shortcuts = [{
  18808. //pattern : 'ctrl+p'
  18809. }];
  18810. this.getstate = function(sel) {
  18811. var fm = this.fm;
  18812. sel = sel || fm.selected();
  18813. if (sel.length == 0) {
  18814. sel = [ fm.cwd().hash ];
  18815. }
  18816. return this.checkstate(this.files(sel)) ? 0 : -1;
  18817. };
  18818. this.checkstate = function(sel) {
  18819. var cnt = sel.length;
  18820. if (!cnt) return false;
  18821. var chk = $.grep(sel, function(f) {
  18822. return (f.isowner && f.perm && isPerm(f.perm) && (cnt == 1 || f.mime != 'directory')) ? true : false;
  18823. }).length;
  18824. return (cnt == chk)? true : false;
  18825. };
  18826. this.exec = function(select) {
  18827. var hashes = this.hashes(select),
  18828. files = this.files(hashes);
  18829. if (! files.length) {
  18830. hashes = [ this.fm.cwd().hash ];
  18831. files = this.files(hashes);
  18832. }
  18833. var fm = this.fm,
  18834. dfrd = $.Deferred().always(function() {
  18835. fm.enable();
  18836. }),
  18837. tpl = this.tpl,
  18838. cnt = files.length,
  18839. file = files[0],
  18840. id = fm.namespace + '-perm-' + file.hash,
  18841. view = tpl.main,
  18842. checked = ' checked="checked"',
  18843. buttons = function() {
  18844. var buttons = {};
  18845. buttons[fm.i18n('btnApply')] = save;
  18846. buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); };
  18847. return buttons;
  18848. },
  18849. save = function() {
  18850. var perm = $.trim($('#'+id+'-perm').val()),
  18851. reqData;
  18852. if (!isPerm(perm)) return false;
  18853. dialog.elfinderdialog('close');
  18854. reqData = {
  18855. cmd : 'chmod',
  18856. targets : hashes,
  18857. mode : perm
  18858. };
  18859. fm.request({
  18860. data : reqData,
  18861. notify : {type : 'chmod', cnt : cnt}
  18862. })
  18863. .fail(function(error) {
  18864. dfrd.reject(error);
  18865. })
  18866. .done(function(data) {
  18867. if (data.changed && data.changed.length) {
  18868. data.undo = {
  18869. cmd : 'chmod',
  18870. callback : function() {
  18871. var reqs = [];
  18872. $.each(prevVals, function(perm, hashes) {
  18873. reqs.push(fm.request({
  18874. data : {cmd : 'chmod', targets : hashes, mode : perm},
  18875. notify : {type : 'undo', cnt : hashes.length}
  18876. }));
  18877. });
  18878. return $.when.apply(null, reqs);
  18879. }
  18880. };
  18881. data.redo = {
  18882. cmd : 'chmod',
  18883. callback : function() {
  18884. return fm.request({
  18885. data : reqData,
  18886. notify : {type : 'redo', cnt : hashes.length}
  18887. });
  18888. }
  18889. };
  18890. }
  18891. dfrd.resolve(data);
  18892. });
  18893. },
  18894. setperm = function() {
  18895. var perm = '';
  18896. var _perm;
  18897. for (var i = 0; i < 3; i++){
  18898. _perm = 0;
  18899. if ($("#"+id+"-read-"+level[i]+'-perm').is(':checked')) {
  18900. _perm = (_perm | 4);
  18901. }
  18902. if ($("#"+id+"-write-"+level[i]+'-perm').is(':checked')) {
  18903. _perm = (_perm | 2);
  18904. }
  18905. if ($("#"+id+"-execute-"+level[i]+'-perm').is(':checked')) {
  18906. _perm = (_perm | 1);
  18907. }
  18908. perm += _perm.toString(8);
  18909. }
  18910. $('#'+id+'-perm').val(perm);
  18911. },
  18912. setcheck = function(perm) {
  18913. var _perm;
  18914. for (var i = 0; i < 3; i++){
  18915. _perm = parseInt(perm.slice(i, i+1), 8);
  18916. $("#"+id+"-read-"+level[i]+'-perm').prop("checked", false);
  18917. $("#"+id+"-write-"+level[i]+'-perm').prop("checked", false);
  18918. $("#"+id+"-execute-"+level[i]+'-perm').prop("checked", false);
  18919. if ((_perm & 4) == 4) {
  18920. $("#"+id+"-read-"+level[i]+'-perm').prop("checked", true);
  18921. }
  18922. if ((_perm & 2) == 2) {
  18923. $("#"+id+"-write-"+level[i]+'-perm').prop("checked", true);
  18924. }
  18925. if ((_perm & 1) == 1) {
  18926. $("#"+id+"-execute-"+level[i]+'-perm').prop("checked", true);
  18927. }
  18928. }
  18929. setperm();
  18930. },
  18931. makeperm = function(files) {
  18932. var perm = '777', ret = '', chk, _chk, _perm;
  18933. var len = files.length;
  18934. for (var i2 = 0; i2 < len; i2++) {
  18935. chk = getPerm(files[i2].perm);
  18936. if (! prevVals[chk]) {
  18937. prevVals[chk] = [];
  18938. }
  18939. prevVals[chk].push(files[i2].hash);
  18940. ret = '';
  18941. for (var i = 0; i < 3; i++){
  18942. _chk = parseInt(chk.slice(i, i+1), 8);
  18943. _perm = parseInt(perm.slice(i, i+1), 8);
  18944. if ((_chk & 4) != 4 && (_perm & 4) == 4) {
  18945. _perm -= 4;
  18946. }
  18947. if ((_chk & 2) != 2 && (_perm & 2) == 2) {
  18948. _perm -= 2;
  18949. }
  18950. if ((_chk & 1) != 1 && (_perm & 1) == 1) {
  18951. _perm -= 1;
  18952. }
  18953. ret += _perm.toString(8);
  18954. }
  18955. perm = ret;
  18956. }
  18957. return perm;
  18958. },
  18959. makeName = function(name) {
  18960. return name? ':'+name : '';
  18961. },
  18962. makeDataTable = function(perm, f) {
  18963. var _perm, fieldset;
  18964. var value = '';
  18965. var dataTable = tpl.dataTable;
  18966. for (var i = 0; i < 3; i++){
  18967. _perm = parseInt(perm.slice(i, i+1), 8);
  18968. value += _perm.toString(8);
  18969. fieldset = tpl.fieldset.replace('{f_title}', fm.i18n(level[i])).replace('{name}', makeName(f[level[i]])).replace(/\{level\}/g, level[i]);
  18970. dataTable = dataTable.replace('{'+i+'}', fieldset)
  18971. .replace('{checked-r}', ((_perm & 4) == 4)? checked : '')
  18972. .replace('{checked-w}', ((_perm & 2) == 2)? checked : '')
  18973. .replace('{checked-x}', ((_perm & 1) == 1)? checked : '');
  18974. }
  18975. dataTable = dataTable.replace('{value}', value).replace('{valueCaption}', msg['perm']);
  18976. return dataTable;
  18977. },
  18978. getPerm = function(perm){
  18979. if (isNaN(parseInt(perm, 8))) {
  18980. var mode_array = perm.split('');
  18981. var a = [];
  18982. for (var i = 0, l = mode_array.length; i < l; i++) {
  18983. if (i === 0 || i === 3 || i === 6) {
  18984. if (mode_array[i].match(/[r]/i)) {
  18985. a.push(1);
  18986. } else if (mode_array[i].match(/[-]/)) {
  18987. a.push(0);
  18988. }
  18989. } else if ( i === 1 || i === 4 || i === 7) {
  18990. if (mode_array[i].match(/[w]/i)) {
  18991. a.push(1);
  18992. } else if (mode_array[i].match(/[-]/)) {
  18993. a.push(0);
  18994. }
  18995. } else {
  18996. if (mode_array[i].match(/[x]/i)) {
  18997. a.push(1);
  18998. } else if (mode_array[i].match(/[-]/)) {
  18999. a.push(0);
  19000. }
  19001. }
  19002. }
  19003. a.splice(3, 0, ",");
  19004. a.splice(7, 0, ",");
  19005. var b = a.join("");
  19006. var b_array = b.split(",");
  19007. var c = [];
  19008. for (var j = 0, m = b_array.length; j < m; j++) {
  19009. var p = parseInt(b_array[j], 2).toString(8);
  19010. c.push(p);
  19011. }
  19012. perm = c.join('');
  19013. } else {
  19014. perm = parseInt(perm, 8).toString(8);
  19015. }
  19016. return perm;
  19017. },
  19018. opts = {
  19019. title : this.title,
  19020. width : 'auto',
  19021. buttons : buttons(),
  19022. close : function() { $(this).elfinderdialog('destroy'); }
  19023. },
  19024. dialog = fm.getUI().find('#'+id),
  19025. prevVals = {},
  19026. tmb = '', title, dataTable;
  19027. if (dialog.length) {
  19028. dialog.elfinderdialog('toTop');
  19029. return $.Deferred().resolve();
  19030. }
  19031. view = view.replace('{class}', cnt > 1 ? 'elfinder-cwd-icon-group' : fm.mime2class(file.mime));
  19032. if (cnt > 1) {
  19033. title = tpl.groupTitle.replace('{items}', fm.i18n('items')).replace('{num}', cnt);
  19034. } else {
  19035. title = tpl.itemTitle.replace('{name}', file.name).replace('{kind}', fm.mime2kind(file));
  19036. tmb = fm.tmb(file);
  19037. }
  19038. dataTable = makeDataTable(makeperm(files), files.length == 1? files[0] : {});
  19039. view = view.replace('{title}', title).replace('{dataTable}', dataTable).replace(/{id}/g, id);
  19040. dialog = fm.dialog(view, opts);
  19041. dialog.attr('id', id);
  19042. // load thumbnail
  19043. if (tmb) {
  19044. $('<img/>')
  19045. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  19046. .attr('src', tmb.url);
  19047. }
  19048. $('#' + id + '-table-perm :checkbox').on('click', function(){setperm('perm');});
  19049. $('#' + id + '-perm').on('keydown', function(e) {
  19050. var c = e.keyCode;
  19051. e.stopPropagation();
  19052. if (c == $.ui.keyCode.ENTER) {
  19053. save();
  19054. return;
  19055. }
  19056. }).on('focus', function(e){
  19057. $(this).trigger('select');
  19058. }).on('keyup', function(e) {
  19059. if ($(this).val().length == 3) {
  19060. $(this).trigger('select');
  19061. setcheck($(this).val());
  19062. }
  19063. });
  19064. return dfrd;
  19065. };
  19066. };
  19067. /*
  19068. * File: /js/commands/colwidth.js
  19069. */
  19070. /**
  19071. * @class elFinder command "colwidth"
  19072. * CWD list table columns width to auto
  19073. *
  19074. * @author Naoki Sawada
  19075. **/
  19076. elFinder.prototype.commands.colwidth = function() {
  19077. this.alwaysEnabled = true;
  19078. this.updateOnSelect = false;
  19079. this.getstate = function() {
  19080. return this.fm.getUI('cwd').find('table').css('table-layout') === 'fixed' ? 0 : -1;
  19081. };
  19082. this.exec = function() {
  19083. this.fm.getUI('cwd').trigger('colwidth');
  19084. };
  19085. };
  19086. /*
  19087. * File: /js/commands/copy.js
  19088. */
  19089. /**
  19090. * @class elFinder command "copy".
  19091. * Put files in filemanager clipboard.
  19092. *
  19093. * @type elFinder.command
  19094. * @author Dmitry (dio) Levashov
  19095. */
  19096. elFinder.prototype.commands.copy = function() {
  19097. this.shortcuts = [{
  19098. pattern : 'ctrl+c ctrl+insert'
  19099. }];
  19100. this.getstate = function(select) {
  19101. var sel = this.files(select),
  19102. cnt = sel.length;
  19103. return cnt && $.grep(sel, function(f) { return f.read ? true : false; }).length == cnt ? 0 : -1;
  19104. };
  19105. this.exec = function(hashes) {
  19106. var fm = this.fm,
  19107. dfrd = $.Deferred()
  19108. .fail(function(error) {
  19109. fm.error(error);
  19110. });
  19111. $.each(this.files(hashes), function(i, file) {
  19112. if (! file.read) {
  19113. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  19114. }
  19115. });
  19116. return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes)));
  19117. };
  19118. };
  19119. /*
  19120. * File: /js/commands/cut.js
  19121. */
  19122. /**
  19123. * @class elFinder command "copy".
  19124. * Put files in filemanager clipboard.
  19125. *
  19126. * @type elFinder.command
  19127. * @author Dmitry (dio) Levashov
  19128. */
  19129. elFinder.prototype.commands.cut = function() {
  19130. var fm = this.fm;
  19131. this.shortcuts = [{
  19132. pattern : 'ctrl+x shift+insert'
  19133. }];
  19134. this.getstate = function(select) {
  19135. var sel = this.files(select),
  19136. cnt = sel.length;
  19137. return cnt && $.grep(sel, function(f) { return f.read && ! f.locked && ! fm.isRoot(f) ? true : false; }).length == cnt ? 0 : -1;
  19138. };
  19139. this.exec = function(hashes) {
  19140. var dfrd = $.Deferred()
  19141. .fail(function(error) {
  19142. fm.error(error);
  19143. });
  19144. $.each(this.files(hashes), function(i, file) {
  19145. if (!(file.read && ! file.locked && ! fm.isRoot(file)) ) {
  19146. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  19147. }
  19148. if (file.locked) {
  19149. return !dfrd.reject(['errLocked', file.name]);
  19150. }
  19151. });
  19152. return dfrd.state() == 'rejected' ? dfrd : dfrd.resolve(fm.clipboard(this.hashes(hashes), true));
  19153. };
  19154. };
  19155. /*
  19156. * File: /js/commands/download.js
  19157. */
  19158. /**
  19159. * @class elFinder command "download".
  19160. * Download selected files.
  19161. * Only for new api
  19162. *
  19163. * @author Dmitry (dio) Levashov, dio@std42.ru
  19164. **/
  19165. elFinder.prototype.commands.zipdl = function() {};
  19166. elFinder.prototype.commands.download = function() {
  19167. var self = this,
  19168. fm = this.fm,
  19169. czipdl = null,
  19170. zipOn = false,
  19171. mixed = false,
  19172. filter = function(hashes, inExec) {
  19173. var volumeid, mixedCmd;
  19174. if (czipdl !== null) {
  19175. if (fm.searchStatus.state > 1) {
  19176. mixed = fm.searchStatus.mixed;
  19177. } else if (fm.leafRoots[fm.cwd().hash]) {
  19178. volumeid = fm.cwd().volumeid;
  19179. $.each(hashes, function(i, h) {
  19180. if (h.indexOf(volumeid) !== 0) {
  19181. mixed = true;
  19182. return false;
  19183. }
  19184. });
  19185. }
  19186. zipOn = (fm.isCommandEnabled('zipdl', hashes[0]));
  19187. }
  19188. if (mixed) {
  19189. mixedCmd = czipdl? 'zipdl' : 'download';
  19190. hashes = $.grep(hashes, function(h) {
  19191. var f = fm.file(h),
  19192. res = (! f || (! czipdl && f.mime === 'directory') || ! fm.isCommandEnabled(mixedCmd, h))? false : true;
  19193. if (f && inExec && ! res) {
  19194. $('#' + fm.cwdHash2Id(f.hash)).trigger('unselect');
  19195. }
  19196. return res;
  19197. });
  19198. if (! hashes.length) {
  19199. return [];
  19200. }
  19201. } else {
  19202. if (!fm.isCommandEnabled('download', hashes[0])) {
  19203. return [];
  19204. }
  19205. }
  19206. return $.grep(self.files(hashes), function(f) {
  19207. var res = (! f.read || (! zipOn && f.mime == 'directory')) ? false : true;
  19208. if (inExec && ! res) {
  19209. $('#' + fm.cwdHash2Id(f.hash)).trigger('unselect');
  19210. }
  19211. return res;
  19212. });
  19213. };
  19214. this.linkedCmds = ['zipdl'];
  19215. this.shortcuts = [{
  19216. pattern : 'shift+enter'
  19217. }];
  19218. this.getstate = function(select) {
  19219. var sel = this.hashes(select),
  19220. cnt = sel.length,
  19221. maxReq = this.options.maxRequests || 10,
  19222. mixed = false,
  19223. croot = '';
  19224. if (cnt < 1) {
  19225. return -1;
  19226. }
  19227. cnt = filter(sel).length;
  19228. return (cnt && (zipOn || (cnt <= maxReq && ((!fm.UA.IE && !fm.UA.Mobile) || cnt == 1))) ? 0 : -1);
  19229. };
  19230. fm.bind('contextmenu', function(e){
  19231. var fm = self.fm,
  19232. helper = null,
  19233. targets, file, link,
  19234. getExtra = function(file) {
  19235. var link = file.url || fm.url(file.hash);
  19236. return {
  19237. icon: 'link',
  19238. node: $('<a/>')
  19239. .attr({href: link, target: '_blank', title: fm.i18n('link')})
  19240. .text(file.name)
  19241. .on('mousedown click touchstart touchmove touchend contextmenu', function(e){
  19242. e.stopPropagation();
  19243. })
  19244. .on('dragstart', function(e) {
  19245. var dt = e.dataTransfer || e.originalEvent.dataTransfer || null;
  19246. helper = null;
  19247. if (dt) {
  19248. var icon = function(f) {
  19249. var mime = f.mime, i, tmb = fm.tmb(f);
  19250. i = '<div class="elfinder-cwd-icon '+fm.mime2class(mime)+' ui-corner-all"/>';
  19251. if (tmb) {
  19252. i = $(i).addClass(tmb.className).css('background-image', "url('"+tmb.url+"')").get(0).outerHTML;
  19253. }
  19254. return i;
  19255. };
  19256. dt.effectAllowed = 'copyLink';
  19257. if (dt.setDragImage) {
  19258. helper = $('<div class="elfinder-drag-helper html5-native">').append(icon(file)).appendTo($(document.body));
  19259. dt.setDragImage(helper.get(0), 50, 47);
  19260. }
  19261. if (!fm.UA.IE) {
  19262. dt.setData('elfinderfrom', window.location.href + file.phash);
  19263. dt.setData('elfinderfrom:' + dt.getData('elfinderfrom'), '');
  19264. }
  19265. }
  19266. })
  19267. .on('dragend', function(e) {
  19268. helper && helper.remove();
  19269. })
  19270. };
  19271. };
  19272. self.extra = null;
  19273. if (e.data) {
  19274. targets = e.data.targets || [];
  19275. if (targets.length === 1 && (file = fm.file(targets[0])) && file.mime !== 'directory') {
  19276. if (file.url != '1') {
  19277. self.extra = getExtra(file);
  19278. } else {
  19279. // Get URL ondemand
  19280. var node;
  19281. self.extra = {
  19282. icon: 'link',
  19283. node: $('<a/>')
  19284. .attr({href: '#', title: fm.i18n('getLink'), draggable: 'false'})
  19285. .text(file.name)
  19286. .on('click touchstart', function(e){
  19287. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  19288. return;
  19289. }
  19290. var parent = node.parent();
  19291. e.stopPropagation();
  19292. e.preventDefault();
  19293. parent.removeClass('ui-state-disabled').addClass('elfinder-button-icon-spinner');
  19294. fm.request({
  19295. data : {cmd : 'url', target : file.hash},
  19296. preventDefault : true
  19297. })
  19298. .always(function(data) {
  19299. parent.removeClass('elfinder-button-icon-spinner');
  19300. if (data.url) {
  19301. var rfile = fm.file(file.hash);
  19302. rfile.url = data.url;
  19303. node.replaceWith(getExtra(file).node);
  19304. } else {
  19305. parent.addClass('ui-state-disabled');
  19306. }
  19307. });
  19308. })
  19309. };
  19310. node = self.extra.node;
  19311. node.ready(function(){
  19312. setTimeout(function(){
  19313. node.parent().addClass('ui-state-disabled').css('pointer-events', 'auto');
  19314. }, 10);
  19315. });
  19316. }
  19317. }
  19318. }
  19319. }).one('open', function() {
  19320. if (fm.api >= 2.1012) {
  19321. czipdl = fm.getCommand('zipdl');
  19322. }
  19323. });
  19324. this.exec = function(select) {
  19325. var hashes = this.hashes(select),
  19326. fm = this.fm,
  19327. base = fm.options.url,
  19328. files = filter(hashes, true),
  19329. dfrd = $.Deferred(),
  19330. iframes = '',
  19331. cdata = '',
  19332. targets = {},
  19333. i, url,
  19334. linkdl = false,
  19335. getTask = function(hashes) {
  19336. return function() {
  19337. var dfd = $.Deferred(),
  19338. root = fm.file(fm.root(hashes[0])),
  19339. single = (hashes.length === 1),
  19340. volName = root? (root.i18 || root.name) : null,
  19341. dir, dlName, phash;
  19342. if (single) {
  19343. if (dir = fm.file(hashes[0])) {
  19344. dlName = (dir.i18 || dir.name);
  19345. }
  19346. } else {
  19347. $.each(hashes, function() {
  19348. var d = fm.file(this);
  19349. if (d && (!phash || phash === d.phash)) {
  19350. phash = d.phash;
  19351. } else {
  19352. phash = null;
  19353. return false;
  19354. }
  19355. });
  19356. if (phash && (dir = fm.file(phash))) {
  19357. dlName = (dir.i18 || dir.name) + '-' + hashes.length;
  19358. }
  19359. }
  19360. if (dlName) {
  19361. volName = dlName;
  19362. }
  19363. volName && (volName = ' (' + volName + ')');
  19364. fm.request({
  19365. data : {cmd : 'zipdl', targets : hashes},
  19366. notify : {type : 'zipdl', cnt : 1, hideCnt : true, msg : fm.i18n('ntfzipdl') + volName},
  19367. cancel : true,
  19368. eachCancel : true,
  19369. preventDefault : true
  19370. }).done(function(e) {
  19371. var zipdl, dialog, btn = {}, dllink, form, iframe,
  19372. uniq = 'dlw' + (+new Date());
  19373. if (e.error) {
  19374. fm.error(e.error);
  19375. dfd.resolve();
  19376. } else if (e.zipdl) {
  19377. zipdl = e.zipdl;
  19378. if (dlName) {
  19379. dlName += '.zip';
  19380. } else {
  19381. dlName = zipdl.name;
  19382. }
  19383. if ((html5dl && (!fm.UA.Safari || fm.isSameOrigin(fm.options.url))) || linkdl) {
  19384. url = fm.options.url + (fm.options.url.indexOf('?') === -1 ? '?' : '&')
  19385. + 'cmd=zipdl&download=1';
  19386. $.each([hashes[0], zipdl.file, dlName, zipdl.mime], function(key, val) {
  19387. url += '&targets%5B%5D='+encodeURIComponent(val);
  19388. });
  19389. $.each(fm.customData, function(key, val) {
  19390. url += '&'+encodeURIComponent(key)+'='+encodeURIComponent(val);
  19391. });
  19392. url += '&'+encodeURIComponent(dlName);
  19393. dllink = $('<a/>')
  19394. .attr('href', url)
  19395. .attr('download', fm.escape(dlName))
  19396. .attr('target', '_blank')
  19397. .on('click', function() {
  19398. dfd.resolve();
  19399. dialog && dialog.elfinderdialog('destroy');
  19400. });
  19401. if (linkdl) {
  19402. dllink.append('<span class="elfinder-button-icon elfinder-button-icon-download"></span>'+fm.escape(dlName));
  19403. btn[fm.i18n('btnCancel')] = function() {
  19404. dialog.elfinderdialog('destroy');
  19405. };
  19406. dialog = fm.dialog(dllink, {
  19407. title: fm.i18n('link'),
  19408. buttons: btn,
  19409. width: '200px',
  19410. destroyOnClose: true,
  19411. close: function() {
  19412. (dfd.state() !== 'resolved') && dfd.resolve();
  19413. }
  19414. });
  19415. } else {
  19416. click(dllink.hide().appendTo('body').get(0));
  19417. dllink.remove();
  19418. }
  19419. } else {
  19420. form = $('<form action="'+fm.options.url+'" method="post" target="'+uniq+'" style="display:none"/>')
  19421. .append('<input type="hidden" name="cmd" value="zipdl"/>')
  19422. .append('<input type="hidden" name="download" value="1"/>');
  19423. $.each([hashes[0], zipdl.file, dlName, zipdl.mime], function(key, val) {
  19424. form.append('<input type="hidden" name="targets[]" value="'+fm.escape(val)+'"/>');
  19425. });
  19426. $.each(fm.customData, function(key, val) {
  19427. form.append('<input type="hidden" name="'+key+'" value="'+fm.escape(val)+'"/>');
  19428. });
  19429. form.attr('target', uniq).appendTo('body');
  19430. iframe = $('<iframe style="display:none" name="'+uniq+'">')
  19431. .appendTo('body')
  19432. .ready(function() {
  19433. form.submit().remove();
  19434. dfd.resolve();
  19435. setTimeout(function() {
  19436. iframe.remove();
  19437. }, 20000); // give 20 sec file to be saved
  19438. });
  19439. }
  19440. }
  19441. }).fail(function(error) {
  19442. error && fm.error(error);
  19443. dfd.resolve();
  19444. });
  19445. return dfd.promise();
  19446. };
  19447. },
  19448. // use MouseEvent to click element for Safari etc
  19449. click = function(a) {
  19450. var clickEv;
  19451. if (typeof MouseEvent === 'function') {
  19452. clickEv = new MouseEvent('click');
  19453. } else {
  19454. clickEv = document.createEvent('MouseEvents');
  19455. clickEv.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  19456. }
  19457. a.dispatchEvent(clickEv);
  19458. },
  19459. link, html5dl, fileCnt, clickEv;
  19460. if (!files.length) {
  19461. return dfrd.reject();
  19462. }
  19463. fileCnt = $.grep(files, function(f) { return f.mime === 'directory'? false : true; }).length;
  19464. link = $('<a>').hide().appendTo('body');
  19465. html5dl = (typeof link.get(0).download === 'string');
  19466. if (zipOn && (fileCnt !== files.length || fileCnt >= (this.options.minFilesZipdl || 1))) {
  19467. link.remove();
  19468. linkdl = (!html5dl && fm.UA.Mobile);
  19469. if (mixed) {
  19470. targets = {};
  19471. $.each(files, function(i, f) {
  19472. var p = f.hash.split('_', 2);
  19473. if (! targets[p[0]]) {
  19474. targets[p[0]] = [ f.hash ];
  19475. } else {
  19476. targets[p[0]].push(f.hash);
  19477. }
  19478. });
  19479. if (!linkdl && fm.UA.Mobile && Object.keys(targets).length > 1) {
  19480. linkdl = true;
  19481. }
  19482. } else {
  19483. targets = [ $.map(files, function(f) { return f.hash; }) ];
  19484. }
  19485. dfrd = fm.sequence($.map(targets, function(t) { return getTask(t); })).always(
  19486. function() {
  19487. fm.trigger('download', {files : files});
  19488. }
  19489. );
  19490. return dfrd;
  19491. } else {
  19492. for (i = 0; i < files.length; i++) {
  19493. url = fm.openUrl(files[i].hash, true);
  19494. if (html5dl && (!fm.UA.Safari || fm.isSameOrigin(url))) {
  19495. click(link.attr('href', url)
  19496. .attr('download', fm.escape(files[i].name))
  19497. .attr('target', '_blank')
  19498. .get(0)
  19499. );
  19500. } else {
  19501. if (fm.UA.Mobile) {
  19502. setTimeout(function(){
  19503. if (! window.open(url)) {
  19504. fm.error('errPopup');
  19505. }
  19506. }, 100);
  19507. } else {
  19508. iframes += '<iframe class="downloader" id="downloader-' + files[i].hash+'" style="display:none" src="'+url+'"/>';
  19509. }
  19510. }
  19511. }
  19512. link.remove();
  19513. $(iframes)
  19514. .appendTo('body')
  19515. .ready(function() {
  19516. setTimeout(function() {
  19517. $(iframes).each(function() {
  19518. $('#' + $(this).attr('id')).remove();
  19519. });
  19520. }, 20000 + (10000 * i)); // give 20 sec + 10 sec for each file to be saved
  19521. });
  19522. fm.trigger('download', {files : files});
  19523. return dfrd.resolve();
  19524. }
  19525. };
  19526. };
  19527. /*
  19528. * File: /js/commands/duplicate.js
  19529. */
  19530. /**
  19531. * @class elFinder command "duplicate"
  19532. * Create file/folder copy with suffix "copy Number"
  19533. *
  19534. * @type elFinder.command
  19535. * @author Dmitry (dio) Levashov
  19536. */
  19537. elFinder.prototype.commands.duplicate = function() {
  19538. var fm = this.fm;
  19539. this.getstate = function(select) {
  19540. var sel = this.files(select),
  19541. cnt = sel.length;
  19542. 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;
  19543. };
  19544. this.exec = function(hashes) {
  19545. var fm = this.fm,
  19546. files = this.files(hashes),
  19547. cnt = files.length,
  19548. dfrd = $.Deferred()
  19549. .fail(function(error) {
  19550. error && fm.error(error);
  19551. }),
  19552. args = [];
  19553. if (! cnt) {
  19554. return dfrd.reject();
  19555. }
  19556. $.each(files, function(i, file) {
  19557. if (!file.read || !fm.file(file.phash).write) {
  19558. return !dfrd.reject(['errCopy', file.name, 'errPerm']);
  19559. }
  19560. });
  19561. if (dfrd.state() == 'rejected') {
  19562. return dfrd;
  19563. }
  19564. return fm.request({
  19565. data : {cmd : 'duplicate', targets : this.hashes(hashes)},
  19566. notify : {type : 'copy', cnt : cnt},
  19567. navigate : {
  19568. toast : {
  19569. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdduplicate')])}
  19570. }
  19571. }
  19572. });
  19573. };
  19574. };
  19575. /*
  19576. * File: /js/commands/edit.js
  19577. */
  19578. /**
  19579. * @class elFinder command "edit".
  19580. * Edit text file in dialog window
  19581. *
  19582. * @author Dmitry (dio) Levashov, dio@std42.ru
  19583. **/
  19584. elFinder.prototype.commands.edit = function() {
  19585. var self = this,
  19586. fm = this.fm,
  19587. dlcls = 'elfinder-dialog-edit',
  19588. mimesSingle = [],
  19589. mimes = [],
  19590. rtrim = function(str){
  19591. return str.replace(/\s+$/, '');
  19592. },
  19593. getEncSelect = function(heads) {
  19594. var sel = $('<select class="ui-corner-all"/>'),
  19595. hval;
  19596. if (heads) {
  19597. $.each(heads, function(i, head) {
  19598. hval = fm.escape(head.value);
  19599. sel.append('<option value="'+hval+'">'+(head.caption? fm.escape(head.caption) : hval)+'</option>');
  19600. });
  19601. }
  19602. $.each(self.options.encodings, function(i, v) {
  19603. sel.append('<option value="'+v+'">'+v+'</option>');
  19604. });
  19605. return sel;
  19606. },
  19607. getDlgWidth = function() {
  19608. var m, width;
  19609. if (typeof self.options.dialogWidth === 'string' && (m = self.options.dialogWidth.match(/(\d+)%/))) {
  19610. width = parseInt(fm.getUI().width() * (m[1] / 100));
  19611. } else {
  19612. width = parseInt(self.options.dialogWidth || 650);
  19613. }
  19614. return Math.min(width, $(window).width());
  19615. },
  19616. /**
  19617. * Return files acceptable to edit
  19618. *
  19619. * @param Array files hashes
  19620. * @return Array
  19621. **/
  19622. filter = function(files) {
  19623. var cnt = files.length,
  19624. mime, ext, skip;
  19625. if (cnt > 1) {
  19626. mime = files[0].mime;
  19627. ext = files[0].name.replace(/^.*(\.[^.]+)$/, '$1');
  19628. }
  19629. return $.grep(files, function(file) {
  19630. var res;
  19631. if (skip) {
  19632. return false;
  19633. }
  19634. res = (fm.mimeIsText(file.mime) || $.inArray(file.mime, cnt === 1? mimesSingle : mimes) !== -1)
  19635. && (!self.onlyMimes.length || $.inArray(file.mime, self.onlyMimes) !== -1)
  19636. && file.read && file.write
  19637. && (cnt === 1 || (file.mime === mime && file.name.substr(ext.length * -1) === ext))
  19638. && fm.uploadMimeCheck(file.mime, file.phash)? true : false;
  19639. if (!res) {
  19640. skip = true;
  19641. }
  19642. return res;
  19643. });
  19644. },
  19645. /**
  19646. * Open dialog with textarea to edit file
  19647. *
  19648. * @param String id dialog id
  19649. * @param Object file file object
  19650. * @param String content file content
  19651. * @return $.Deferred
  19652. **/
  19653. dialog = function(id, file, content, encoding, editor) {
  19654. var dfrd = $.Deferred(),
  19655. save = function() {
  19656. var encord = selEncoding? selEncoding.val():void(0),
  19657. conf;
  19658. ta.one('_savefail', function() {
  19659. ta.off('_savedone');
  19660. dialogNode.show().find('button.elfinder-btncnt-0,button.elfinder-btncnt-1').hide();
  19661. }).one('_savedone', function() {
  19662. ta.off('_savefail');
  19663. });
  19664. if (ta.editor) {
  19665. ta.editor.save(ta[0], ta.editor.instance);
  19666. conf = ta.editor.confObj;
  19667. if (conf.info && conf.info.schemeContent) {
  19668. encord = 'scheme';
  19669. }
  19670. }
  19671. old = getContent();
  19672. dfrd.notifyWith(ta, [encord, ta.data('hash')]);
  19673. },
  19674. cancel = function() {
  19675. ta.elfinderdialog('close');
  19676. },
  19677. savecl = function() {
  19678. ta.one('_savedone', function() {
  19679. dialogNode.show();
  19680. cancel();
  19681. });
  19682. save();
  19683. dialogNode.hide();
  19684. },
  19685. saveAs = function() {
  19686. var prevOld = old,
  19687. phash = fm.file(file.phash)? file.phash : fm.cwd().hash,
  19688. fail = function() {
  19689. dialogs.addClass(clsEditing).fadeIn();
  19690. old = prevOld;
  19691. fm.disable();
  19692. },
  19693. make = function() {
  19694. self.mime = file.mime;
  19695. self.prefix = file.name.replace(/ \d+(\.[^.]+)?$/, '$1');
  19696. self.requestCmd = 'mkfile';
  19697. self.nextAction = {};
  19698. self.data = {target : phash};
  19699. $.proxy(fm.res('mixin', 'make'), self)()
  19700. .done(function(data) {
  19701. if (data.added && data.added.length) {
  19702. ta.data('hash', data.added[0].hash);
  19703. save();
  19704. dialogNode.show();
  19705. cancel();
  19706. } else {
  19707. fail();
  19708. }
  19709. dialogs.fadeIn();
  19710. })
  19711. .fail(fail)
  19712. .always(function() {
  19713. delete self.mime;
  19714. delete self.prefix;
  19715. delete self.nextAction;
  19716. delete self.data;
  19717. });
  19718. fm.trigger('unselectfiles', { files: [ file.hash ] });
  19719. },
  19720. reqOpen = null,
  19721. dialogs = fm.getUI().children('.' + dlcls + ':visible');
  19722. if (dialogNode.is(':hidden')) {
  19723. dialogs = dialogs.add(dialogNode);
  19724. }
  19725. dialogs.removeClass(clsEditing).fadeOut();
  19726. fm.enable();
  19727. if (fm.searchStatus.state < 2 && phash !== fm.cwd().hash) {
  19728. reqOpen = fm.exec('open', [phash], {thash: phash});
  19729. }
  19730. $.when([reqOpen]).done(function() {
  19731. reqOpen? fm.one('cwdrender', make) : make();
  19732. }).fail(fail);
  19733. },
  19734. changed = function() {
  19735. ta.editor && ta.editor.save(ta[0], ta.editor.instance);
  19736. return (old !== getContent());
  19737. },
  19738. opts = {
  19739. title : fm.escape(file.name),
  19740. width : getDlgWidth(),
  19741. buttons : {},
  19742. maxWidth : 'window',
  19743. maxHeight : 'window',
  19744. allowMinimize : true,
  19745. allowMaximize : true,
  19746. btnHoverFocus : false,
  19747. closeOnEscape : false,
  19748. close : function() {
  19749. var close = function(){
  19750. dfrd.resolve();
  19751. ta.editor && ta.editor.close(ta[0], ta.editor.instance);
  19752. ta.elfinderdialog('destroy');
  19753. };
  19754. if (changed()) {
  19755. fm.confirm({
  19756. title : self.title,
  19757. text : 'confirmNotSave',
  19758. accept : {
  19759. label : 'btnSaveClose',
  19760. callback : function() {
  19761. save();
  19762. close();
  19763. }
  19764. },
  19765. cancel : {
  19766. label : 'btnClose',
  19767. callback : close
  19768. },
  19769. buttons : [{
  19770. label : 'btnSaveAs',
  19771. callback : function() {
  19772. setTimeout(saveAs, 10);
  19773. }
  19774. }]
  19775. });
  19776. } else {
  19777. close();
  19778. }
  19779. },
  19780. open : function() {
  19781. var loadRes;
  19782. ta.initEditArea.call(ta, id, file, content, fm);
  19783. old = getContent();
  19784. if (ta.editor) {
  19785. loadRes = ta.editor.load(ta[0]) || null;
  19786. if (loadRes && loadRes.done) {
  19787. loadRes.done(function(instance) {
  19788. ta.editor.instance = instance;
  19789. ta.editor.focus(ta[0], ta.editor.instance);
  19790. old = getContent();
  19791. }).fail(function(error) {
  19792. error && fm.error(error);
  19793. ta.elfinderdialog('destroy');
  19794. });
  19795. } else {
  19796. if (loadRes && (typeof loadRes === 'string' || Array.isArray(loadRes))) {
  19797. fm.error(loadRes);
  19798. ta.elfinderdialog('destroy');
  19799. return;
  19800. }
  19801. ta.editor.instance = loadRes;
  19802. ta.editor.focus(ta[0], ta.editor.instance);
  19803. old = getContent();
  19804. }
  19805. }
  19806. },
  19807. resize : function(e, data) {
  19808. ta.editor && ta.editor.resize(ta[0], ta.editor.instance, e, data || {});
  19809. }
  19810. },
  19811. getContent = function() {
  19812. return ta.getContent.call(ta, ta[0]);
  19813. },
  19814. clsEditing = fm.res('class', 'editing'),
  19815. ta, old, dialogNode, selEncoding, extEditor, maxW;
  19816. if (editor) {
  19817. if (editor.html) {
  19818. ta = $(editor.html);
  19819. }
  19820. extEditor = {
  19821. init : editor.init || null,
  19822. load : editor.load,
  19823. getContent : editor.getContent || null,
  19824. save : editor.save,
  19825. beforeclose : typeof editor.beforeclose == 'function' ? editor.beforeclose : void 0,
  19826. close : typeof editor.close == 'function' ? editor.close : function() {},
  19827. focus : typeof editor.focus == 'function' ? editor.focus : function() {},
  19828. resize : typeof editor.resize == 'function' ? editor.resize : function() {},
  19829. instance : null,
  19830. doSave : save,
  19831. doCancel : cancel,
  19832. doClose : savecl,
  19833. file : file,
  19834. fm : fm,
  19835. confObj : editor,
  19836. trigger : function(evName, data) {
  19837. fm.trigger('editEditor' + evName, Object.assign({}, editor.info || {}, data));
  19838. }
  19839. };
  19840. }
  19841. if (!ta) {
  19842. if (!fm.mimeIsText(file.mime)) {
  19843. return dfrd.reject('errEditorNotFound');
  19844. }
  19845. (function() {
  19846. var stateChange = function() {
  19847. if (selEncoding) {
  19848. if (changed()) {
  19849. selEncoding.attr('title', fm.i18n('saveAsEncoding')).addClass('elfinder-edit-changed');
  19850. } else {
  19851. selEncoding.attr('title', fm.i18n('openAsEncoding')).removeClass('elfinder-edit-changed');
  19852. }
  19853. }
  19854. };
  19855. ta = $('<textarea class="elfinder-file-edit" rows="20" id="'+id+'-ta"></textarea>')
  19856. .on('input propertychange', stateChange);
  19857. if (!ta.editor || !ta.editor.info || ta.editor.info.useTextAreaEvent) {
  19858. ta.on('keydown', function(e) {
  19859. var code = e.keyCode,
  19860. value, start;
  19861. e.stopPropagation();
  19862. if (code == $.ui.keyCode.TAB) {
  19863. e.preventDefault();
  19864. // insert tab on tab press
  19865. if (this.setSelectionRange) {
  19866. value = this.value;
  19867. start = this.selectionStart;
  19868. this.value = value.substr(0, start) + "\t" + value.substr(this.selectionEnd);
  19869. start += 1;
  19870. this.setSelectionRange(start, start);
  19871. }
  19872. }
  19873. if (e.ctrlKey || e.metaKey) {
  19874. // close on ctrl+w/q
  19875. if (code == 'Q'.charCodeAt(0) || code == 'W'.charCodeAt(0)) {
  19876. e.preventDefault();
  19877. cancel();
  19878. }
  19879. if (code == 'S'.charCodeAt(0)) {
  19880. e.preventDefault();
  19881. save();
  19882. }
  19883. }
  19884. })
  19885. .on('mouseenter', function(){this.focus();});
  19886. }
  19887. ta.initEditArea = function(id, file, content) {
  19888. var heads = (encoding && encoding !== 'unknown')? [{value: encoding}] : [];
  19889. ta.val(content);
  19890. if (content === '' || ! encoding || encoding !== 'UTF-8') {
  19891. heads.push({value: 'UTF-8'});
  19892. }
  19893. selEncoding = getEncSelect(heads).on('touchstart', function(e) {
  19894. // for touch punch event handler
  19895. e.stopPropagation();
  19896. }).on('change', function() {
  19897. // reload to change encoding if not edited
  19898. if (! changed() && getContent() !== '') {
  19899. cancel();
  19900. edit(file, $(this).val(), editor).fail(function(err) { err && fm.error(err); });
  19901. }
  19902. }).on('mouseover', stateChange);
  19903. ta.parent().prev().find('.elfinder-titlebar-button:last')
  19904. .after($('<span class="elfinder-titlebar-button-right"/>').append(selEncoding));
  19905. setTimeout(function() {
  19906. ta[0].setSelectionRange && ta[0].setSelectionRange(0, 0);
  19907. ta.trigger('focus');
  19908. }, 10);
  19909. };
  19910. })();
  19911. }
  19912. ta.data('hash', file.hash);
  19913. if (extEditor) {
  19914. ta.editor = extEditor;
  19915. if (typeof extEditor.beforeclose === 'function') {
  19916. opts.beforeclose = function() {
  19917. return extEditor.beforeclose(ta[0], extEditor.instance);
  19918. };
  19919. }
  19920. if (typeof extEditor.init === 'function') {
  19921. ta.initEditArea = extEditor.init;
  19922. }
  19923. if (typeof extEditor.getContent === 'function') {
  19924. ta.getContent = extEditor.getContent;
  19925. }
  19926. }
  19927. if (! ta.initEditArea) {
  19928. ta.initEditArea = function() {};
  19929. }
  19930. if (! ta.getContent) {
  19931. ta.getContent = function() {
  19932. return rtrim(ta.val());
  19933. };
  19934. }
  19935. if (!editor || !editor.info || !editor.info.preventGet) {
  19936. opts.buttons[fm.i18n('btnSave')] = save;
  19937. opts.buttons[fm.i18n('btnSaveClose')] = savecl;
  19938. opts.buttons[fm.i18n('btnSaveAs')] = saveAs;
  19939. opts.buttons[fm.i18n('btnCancel')] = cancel;
  19940. }
  19941. if (editor && typeof editor.prepare === 'function') {
  19942. editor.prepare(ta, opts, file);
  19943. }
  19944. dialogNode = fm.dialog(ta, opts)
  19945. .attr('id', id)
  19946. .on('keydown keyup keypress', function(e) {
  19947. e.stopPropagation();
  19948. })
  19949. .css({ overflow: 'hidden', minHeight: '7em' })
  19950. .addClass('elfinder-edit-editor')
  19951. .closest('.ui-dialog').addClass(dlcls + ' ' + clsEditing);
  19952. // care to viewport scale change with mobile devices
  19953. maxW = (fm.options.dialogContained? elfNode : $(window)).width();
  19954. (dialogNode.width() > maxW) && dialogNode.width(maxW);
  19955. return dfrd.promise();
  19956. },
  19957. /**
  19958. * Get file content and
  19959. * open dialog with textarea to edit file content
  19960. *
  19961. * @param String file hash
  19962. * @return jQuery.Deferred
  19963. **/
  19964. edit = function(file, convert, editor) {
  19965. var hash = file.hash,
  19966. opts = fm.options,
  19967. dfrd = $.Deferred(),
  19968. id = 'edit-'+fm.namespace+'-'+file.hash,
  19969. d = fm.getUI().find('#'+id),
  19970. conv = !convert? 0 : convert,
  19971. req, error, res;
  19972. if (d.length) {
  19973. d.elfinderdialog('toTop');
  19974. return dfrd.resolve();
  19975. }
  19976. if (!file.read || !file.write) {
  19977. error = ['errOpen', file.name, 'errPerm'];
  19978. fm.error(error);
  19979. return dfrd.reject(error);
  19980. }
  19981. if (editor && editor.info && typeof editor.info.edit === 'function') {
  19982. res = editor.info.edit.call(fm, file, editor);
  19983. if (res.promise) {
  19984. res.done(function() {
  19985. dfrd.resolve();
  19986. }).fail(function(error) {
  19987. dfrd.reject(error);
  19988. });
  19989. } else {
  19990. res? dfrd.resolve() : dfrd.reject();
  19991. }
  19992. return dfrd;
  19993. }
  19994. if (editor && editor.info && (editor.info.urlAsContent || editor.info.preventGet)) {
  19995. req = $.Deferred();
  19996. if (! editor.info.preventGet) {
  19997. fm.url(hash, { async: true, temporary: true }).done(function(url) {
  19998. req.resolve({content: url});
  19999. });
  20000. } else {
  20001. req.resolve({});
  20002. }
  20003. } else {
  20004. req = fm.request({
  20005. data : {cmd : 'get', target : hash, conv : conv, _t : file.ts},
  20006. options : {type: 'get', cache : true},
  20007. notify : {type : 'file', cnt : 1},
  20008. preventDefault : true
  20009. });
  20010. }
  20011. req.done(function(data) {
  20012. var selEncoding, reg, m, res;
  20013. if (data.doconv) {
  20014. fm.confirm({
  20015. title : self.title,
  20016. text : data.doconv === 'unknown'? 'confirmNonUTF8' : 'confirmConvUTF8',
  20017. accept : {
  20018. label : 'btnConv',
  20019. callback : function() {
  20020. dfrd = edit(file, selEncoding.val(), editor);
  20021. }
  20022. },
  20023. cancel : {
  20024. label : 'btnCancel',
  20025. callback : function() { dfrd.reject(); }
  20026. },
  20027. optionsCallback : function(options) {
  20028. options.create = function() {
  20029. var base = $('<div class="elfinder-dialog-confirm-encoding"/>'),
  20030. head = {value: data.doconv},
  20031. detected;
  20032. if (data.doconv === 'unknown') {
  20033. head.caption = '-';
  20034. }
  20035. selEncoding = getEncSelect([head]);
  20036. $(this).next().find('.ui-dialog-buttonset')
  20037. .prepend(base.append($('<label>'+fm.i18n('encoding')+' </label>').append(selEncoding)));
  20038. };
  20039. }
  20040. });
  20041. } else {
  20042. if ((!editor || !editor.info || !editor.info.preventGet) && fm.mimeIsText(file.mime)) {
  20043. reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i');
  20044. if (window.atob && (m = data.content.match(reg))) {
  20045. data.content = atob(data.content.substr(m[1].length));
  20046. }
  20047. }
  20048. dialog(id, file, data.content, data.encoding, editor)
  20049. .done(function(data) {
  20050. dfrd.resolve(data);
  20051. })
  20052. .progress(function(encoding, newHash) {
  20053. var ta = this;
  20054. if (newHash) {
  20055. hash = newHash;
  20056. }
  20057. fm.request({
  20058. options : {type : 'post'},
  20059. data : {
  20060. cmd : 'put',
  20061. target : hash,
  20062. encoding : encoding || data.encoding,
  20063. content : ta.getContent.call(ta, ta[0])
  20064. },
  20065. notify : {type : 'save', cnt : 1},
  20066. syncOnFail : true,
  20067. preventFail : true,
  20068. navigate : {
  20069. target : 'changed',
  20070. toast : {
  20071. inbuffer : {msg: fm.i18n(['complete', fm.i18n('btnSave')])}
  20072. }
  20073. }
  20074. })
  20075. .fail(function(error) {
  20076. dfrd.reject(error);
  20077. ta.trigger('_savefail');
  20078. })
  20079. .done(function(data) {
  20080. setTimeout(function(){
  20081. ta.trigger('focus');
  20082. ta.editor && ta.editor.focus(ta[0], ta.editor.instance);
  20083. }, 50);
  20084. ta.trigger('_savedone');
  20085. });
  20086. })
  20087. .fail(function(error) {
  20088. dfrd.reject(error);
  20089. });
  20090. }
  20091. })
  20092. .fail(function(error) {
  20093. var err = Array.isArray(error)? error[0] : error;
  20094. (err !== 'errConvUTF8') && fm.sync();
  20095. dfrd.reject(error);
  20096. });
  20097. return dfrd.promise();
  20098. },
  20099. /**
  20100. * Current editors of selected files
  20101. *
  20102. * @type Object
  20103. */
  20104. editors = {},
  20105. /**
  20106. * Set current editors
  20107. *
  20108. * @param Object file object
  20109. * @param Number cnt count of selected items
  20110. * @return Void
  20111. */
  20112. setEditors = function(file, cnt) {
  20113. var mimeMatch = function(fileMime, editorMimes){
  20114. if (!editorMimes) {
  20115. return fm.mimeIsText(fileMime);
  20116. } else {
  20117. if ($.inArray(fileMime, editorMimes) !== -1 ) {
  20118. return true;
  20119. }
  20120. var i, l;
  20121. l = editorMimes.length;
  20122. for (i = 0; i < l; i++) {
  20123. if (fileMime.indexOf(editorMimes[i]) === 0) {
  20124. return true;
  20125. }
  20126. }
  20127. return false;
  20128. }
  20129. },
  20130. extMatch = function(fileName, editorExts){
  20131. if (!editorExts || !editorExts.length) {
  20132. return true;
  20133. }
  20134. var ext = fileName.replace(/^.+\.([^.]+)|(.+)$/, '$1$2').toLowerCase(),
  20135. i, l;
  20136. l = editorExts.length;
  20137. for (i = 0; i < l; i++) {
  20138. if (ext === editorExts[i].toLowerCase()) {
  20139. return true;
  20140. }
  20141. }
  20142. return false;
  20143. };
  20144. stored = fm.storage('storedEditors') || {};
  20145. editors = {};
  20146. $.each(self.options.editors || [], function(i, editor) {
  20147. var name;
  20148. if ((cnt === 1 || !editor.info || !editor.info.single)
  20149. && mimeMatch(file.mime, editor.mimes || null)
  20150. && extMatch(file.name, editor.exts || null)
  20151. && typeof editor.load == 'function'
  20152. && typeof editor.save == 'function') {
  20153. name = editor.info && editor.info.name? editor.info.name : ('Editor ' + i);
  20154. editor.id = editor.info && editor.info.id? editor.info.id : ('editor' + i),
  20155. editor.name = name;
  20156. editor.i18n = fm.i18n(name);
  20157. editors[editor.id] = editor;
  20158. }
  20159. });
  20160. },
  20161. store = function(mime, editor) {
  20162. if (mime && editor) {
  20163. if (!$.isPlainObject(stored)) {
  20164. stored = {};
  20165. }
  20166. stored[mime] = editor.id;
  20167. fm.storage('storedEditors', stored);
  20168. fm.trigger('selectfiles', {files : fm.selected()});
  20169. }
  20170. },
  20171. useStoredEditor = function() {
  20172. var d = fm.storage('useStoredEditor');
  20173. return d? (d > 0) : self.options.useStoredEditor;
  20174. },
  20175. getSubMenuRaw = function(files, callback) {
  20176. var subMenuRaw = [];
  20177. $.each(editors, function(id, ed) {
  20178. subMenuRaw.push(
  20179. {
  20180. label : fm.escape(ed.i18n),
  20181. icon : ed.info && ed.info.icon? ed.info.icon : 'edit',
  20182. options : { iconImg: ed.info && ed.info.iconImg? fm.baseUrl + ed.info.iconImg : void(0) },
  20183. callback : function() {
  20184. store(files[0].mime, ed);
  20185. callback && callback.call(ed);
  20186. }
  20187. }
  20188. );
  20189. });
  20190. return subMenuRaw;
  20191. },
  20192. getStoreId = function(name) {
  20193. // for compatibility to previous version
  20194. return name.toLowerCase().replace(/ +/g, '');
  20195. },
  20196. getStoredEditor = function(mime) {
  20197. var name = stored[mime];
  20198. return name && Object.keys(editors).length? editors[getStoreId(name)] : void(0);
  20199. },
  20200. stored;
  20201. this.shortcuts = [{
  20202. pattern : 'ctrl+e'
  20203. }];
  20204. this.init = function() {
  20205. var self = this,
  20206. fm = this.fm,
  20207. opts = this.options,
  20208. cmdChecks = [],
  20209. ccData, dfd;
  20210. this.onlyMimes = this.options.mimes || [];
  20211. fm.one('open', function() {
  20212. // editors setup
  20213. if (opts.editors && Array.isArray(opts.editors)) {
  20214. $.each(opts.editors, function(i, editor) {
  20215. if (editor.info && editor.info.cmdCheck) {
  20216. cmdChecks.push(editor.info.cmdCheck);
  20217. }
  20218. });
  20219. if (cmdChecks.length) {
  20220. if (fm.api >= 2.1030) {
  20221. dfd = fm.request({
  20222. data : {
  20223. cmd: 'editor',
  20224. name: cmdChecks,
  20225. method: 'enabled'
  20226. },
  20227. preventDefault : true
  20228. }).done(function(d) {
  20229. ccData = d;
  20230. }).fail(function() {
  20231. ccData = {};
  20232. });
  20233. } else {
  20234. ccData = {};
  20235. dfd = $.Deferred().resolve();
  20236. }
  20237. } else {
  20238. dfd = $.Deferred().resolve();
  20239. }
  20240. dfd.always(function() {
  20241. if (ccData) {
  20242. opts.editors = $.grep(opts.editors, function(e) {
  20243. if (e.info && e.info.cmdCheck) {
  20244. return ccData[e.info.cmdCheck]? true : false;
  20245. } else {
  20246. return true;
  20247. }
  20248. });
  20249. }
  20250. $.each(opts.editors, function(i, editor) {
  20251. if (editor.setup && typeof editor.setup === 'function') {
  20252. editor.setup.call(editor, opts, fm);
  20253. }
  20254. if (!editor.disabled) {
  20255. if (editor.mimes && Array.isArray(editor.mimes)) {
  20256. mimesSingle = mimesSingle.concat(editor.mimes);
  20257. if (!editor.info || !editor.info.single) {
  20258. mimes = mimes.concat(editor.mimes);
  20259. }
  20260. }
  20261. }
  20262. });
  20263. mimesSingle = ($.uniqueSort || $.unique)(mimesSingle);
  20264. mimes = ($.uniqueSort || $.unique)(mimes);
  20265. opts.editors = $.grep(opts.editors, function(e) {
  20266. return e.disabled? false : true;
  20267. });
  20268. });
  20269. }
  20270. })
  20271. .bind('select', function() {
  20272. editors = null;
  20273. })
  20274. .bind('contextmenucreate', function(e) {
  20275. var file, editor,
  20276. single = function(editor) {
  20277. var title = self.title;
  20278. fm.one('contextmenucreatedone', function() {
  20279. self.title = title;
  20280. });
  20281. self.title = fm.escape(editor.i18n);
  20282. delete self.variants;
  20283. };
  20284. if (e.data.type === 'files' && self.enabled()) {
  20285. file = fm.file(e.data.targets[0]);
  20286. setEditors(file, e.data.targets.length);
  20287. if (Object.keys(editors).length > 1) {
  20288. if (!useStoredEditor() || !(editor = getStoredEditor(file.mime))) {
  20289. delete self.extra;
  20290. self.variants = [];
  20291. $.each(editors, function(id, editor) {
  20292. self.variants.push([{ editor: editor }, editor.i18n, editor.info && editor.info.iconImg? fm.baseUrl + editor.info.iconImg : 'edit']);
  20293. });
  20294. } else {
  20295. single(editor);
  20296. self.extra = {
  20297. icon: 'menu',
  20298. node: $('<span/>')
  20299. .attr({title: fm.i18n('select')})
  20300. .on('click touchstart', function(e){
  20301. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  20302. return;
  20303. }
  20304. var node = $(this);
  20305. e.stopPropagation();
  20306. e.preventDefault();
  20307. fm.trigger('contextmenu', {
  20308. raw: getSubMenuRaw(fm.selectedFiles(), function() {
  20309. var hashes = fm.selected();
  20310. fm.exec('edit', hashes, {editor: this});
  20311. fm.trigger('selectfiles', {files : hashes});
  20312. }),
  20313. x: node.offset().left,
  20314. y: node.offset().top
  20315. });
  20316. })
  20317. };
  20318. }
  20319. } else {
  20320. single(editors[Object.keys(editors)[0]]);
  20321. delete self.extra;
  20322. }
  20323. }
  20324. });
  20325. };
  20326. this.getstate = function(select) {
  20327. var sel = this.files(select),
  20328. cnt = sel.length;
  20329. return cnt && filter(sel).length == cnt ? 0 : -1;
  20330. };
  20331. this.exec = function(select, opts) {
  20332. var fm = this.fm,
  20333. files = filter(this.files(select)),
  20334. hashes = $.map(files, function(f) { return f.hash; }),
  20335. list = [],
  20336. editor = opts && opts.editor? opts.editor : null,
  20337. node = $(opts && opts._currentNode? opts._currentNode : $('#'+ fm.cwdHash2Id(hashes[0]))),
  20338. getEditor = function() {
  20339. var dfd = $.Deferred(),
  20340. storedId;
  20341. if (!editor && Object.keys(editors).length > 1) {
  20342. if (useStoredEditor() && (editor = getStoredEditor(files[0].mime))) {
  20343. return dfd.resolve(editor);
  20344. }
  20345. fm.trigger('contextmenu', {
  20346. raw: getSubMenuRaw(files, function() {
  20347. dfd.resolve(this);
  20348. }),
  20349. x: node.offset().left,
  20350. y: node.offset().top + 22,
  20351. opened: function() {
  20352. fm.one('closecontextmenu',function() {
  20353. setTimeout(function() {
  20354. if (dfd.state() === 'pending') {
  20355. dfd.reject();
  20356. }
  20357. }, 10);
  20358. });
  20359. }
  20360. });
  20361. fm.trigger('selectfiles', {files : hashes});
  20362. return dfd;
  20363. } else {
  20364. Object.keys(editors).length > 1 && editor && store(files[0].mime, editor);
  20365. return dfd.resolve(editor? editor : (Object.keys(editors).length? editors[Object.keys(editors)[0]] : null));
  20366. }
  20367. },
  20368. dfrd = $.Deferred(),
  20369. file;
  20370. if (editors === null) {
  20371. setEditors(files[0], hashes.length);
  20372. }
  20373. if (!node.length) {
  20374. node = fm.getUI('cwd');
  20375. }
  20376. getEditor().done(function(editor) {
  20377. while ((file = files.shift())) {
  20378. list.push(edit(file, void(0), editor).fail(function(error) {
  20379. error && fm.error(error);
  20380. }));
  20381. }
  20382. if (list.length) {
  20383. $.when.apply(null, list).done(function() {
  20384. dfrd.resolve();
  20385. }).fail(function() {
  20386. dfrd.reject();
  20387. });
  20388. } else {
  20389. dfrd.reject();
  20390. }
  20391. }).fail(function() {
  20392. dfrd.reject();
  20393. });
  20394. return dfrd;
  20395. };
  20396. };
  20397. /*
  20398. * File: /js/commands/empty.js
  20399. */
  20400. /**
  20401. * @class elFinder command "empty".
  20402. * Empty the folder
  20403. *
  20404. * @type elFinder.command
  20405. * @author Naoki Sawada
  20406. */
  20407. elFinder.prototype.commands.empty = function() {
  20408. var fm = this.fm,
  20409. self = this,
  20410. selFiles = function(select) {
  20411. var sel = self.files(select);
  20412. if (!sel.length) {
  20413. sel = [ fm.cwd() ];
  20414. }
  20415. return sel;
  20416. };
  20417. this.linkedCmds = ['rm'];
  20418. this.getstate = function(select) {
  20419. var sel = selFiles(select),
  20420. cnt;
  20421. cnt = sel.length;
  20422. return $.grep(sel, function(f) { return f.write && f.mime === 'directory' ? true : false; }).length == cnt ? 0 : -1;
  20423. };
  20424. this.exec = function(hashes) {
  20425. var dirs = selFiles(hashes),
  20426. cnt = dirs.length,
  20427. dfrd = $.Deferred()
  20428. .done(function() {
  20429. var data = {changed: {}};
  20430. fm.toast({msg: fm.i18n(['"'+success.join('", ')+'"', 'complete', fm.i18n('cmdempty')])});
  20431. $.each(dirs, function(i, dir) {
  20432. data.changed[dir.hash] = dir;
  20433. });
  20434. fm.change(data);
  20435. })
  20436. .always(function() {
  20437. var cwd = fm.cwd().hash;
  20438. fm.trigger('selectfiles', {files: $.map(dirs, function(d) { return cwd === d.phash? d.hash : null; })});
  20439. }),
  20440. success = [],
  20441. done = function(res) {
  20442. if (typeof res === 'number') {
  20443. success.push(dirs[res].name);
  20444. delete dirs[res].dirs;
  20445. } else {
  20446. res && fm.error(res);
  20447. }
  20448. (--cnt < 1) && dfrd[success.length? 'resolve' : 'reject']();
  20449. };
  20450. $.each(dirs, function(i, dir) {
  20451. var tm;
  20452. if (!(dir.write && dir.mime === 'directory')) {
  20453. done(['errEmpty', dir.name, 'errPerm']);
  20454. return null;
  20455. }
  20456. if (!fm.isCommandEnabled('rm', dir.hash)) {
  20457. done(['errCmdNoSupport', '"rm"']);
  20458. return null;
  20459. }
  20460. tm = setTimeout(function() {
  20461. fm.notify({type : 'search', cnt : 1, hideCnt : cnt > 1? false : true});
  20462. }, fm.notifyDelay);
  20463. fm.request({
  20464. data : {cmd : 'open', target : dir.hash},
  20465. preventDefault : true,
  20466. asNotOpen : true
  20467. }).done(function(data) {
  20468. var targets = [];
  20469. tm && clearTimeout(tm);
  20470. if (fm.ui.notify.children('.elfinder-notify-search').length) {
  20471. fm.notify({type : 'search', cnt : -1, hideCnt : cnt > 1? false : true});
  20472. }
  20473. if (data && data.files && data.files.length) {
  20474. if (data.files.length > fm.maxTargets) {
  20475. done(['errEmpty', dir.name, 'errMaxTargets', fm.maxTargets]);
  20476. } else {
  20477. fm.updateCache(data);
  20478. $.each(data.files, function(i, f) {
  20479. if (!f.write || f.locked) {
  20480. done(['errEmpty', dir.name, 'errRm', f.name, 'errPerm']);
  20481. targets = [];
  20482. return false;
  20483. }
  20484. targets.push(f.hash);
  20485. });
  20486. if (targets.length) {
  20487. fm.exec('rm', targets, { _userAction : true, addTexts : [ fm.i18n('folderToEmpty', dir.name) ] })
  20488. .fail(function(error) {
  20489. fm.trigger('unselectfiles', {files: fm.selected()});
  20490. done(error || '');
  20491. })
  20492. .done(function() { done(i); });
  20493. }
  20494. }
  20495. } else {
  20496. fm.toast({ mode: 'warning', msg: fm.i18n('filderIsEmpty', dir.name)});
  20497. done('');
  20498. }
  20499. }).fail(function(error) {
  20500. done(error || '');
  20501. });
  20502. });
  20503. return dfrd;
  20504. };
  20505. };
  20506. /*
  20507. * File: /js/commands/extract.js
  20508. */
  20509. /**
  20510. * @class elFinder command "extract"
  20511. * Extract files from archive
  20512. *
  20513. * @author Dmitry (dio) Levashov
  20514. **/
  20515. elFinder.prototype.commands.extract = function() {
  20516. var self = this,
  20517. fm = self.fm,
  20518. mimes = [],
  20519. filter = function(files) {
  20520. return $.grep(files, function(file) {
  20521. return file.read && $.inArray(file.mime, mimes) !== -1 ? true : false;
  20522. });
  20523. };
  20524. this.variants = [];
  20525. this.disableOnSearch = true;
  20526. // Update mimes list on open/reload
  20527. fm.bind('open reload', function() {
  20528. mimes = fm.option('archivers')['extract'] || [];
  20529. if (fm.api > 2) {
  20530. self.variants = [[{makedir: true}, fm.i18n('cmdmkdir')], [{}, fm.i18n('btnCwd')]];
  20531. } else {
  20532. self.variants = [[{}, fm.i18n('btnCwd')]];
  20533. }
  20534. self.change();
  20535. });
  20536. this.getstate = function(select) {
  20537. var sel = this.files(select),
  20538. cnt = sel.length;
  20539. return cnt && this.fm.cwd().write && filter(sel).length == cnt ? 0 : -1;
  20540. };
  20541. this.exec = function(hashes, opts) {
  20542. var files = this.files(hashes),
  20543. dfrd = $.Deferred(),
  20544. cnt = files.length,
  20545. makedir = opts && opts.makedir ? 1 : 0,
  20546. i, error,
  20547. decision;
  20548. var overwriteAll = false;
  20549. var omitAll = false;
  20550. var mkdirAll = 0;
  20551. var names = $.map(fm.files(hashes), function(file) { return file.name; });
  20552. var map = {};
  20553. $.grep(fm.files(hashes), function(file) {
  20554. map[file.name] = file;
  20555. return false;
  20556. });
  20557. var decide = function(decision) {
  20558. switch (decision) {
  20559. case 'overwrite_all' :
  20560. overwriteAll = true;
  20561. break;
  20562. case 'omit_all':
  20563. omitAll = true;
  20564. break;
  20565. }
  20566. };
  20567. var unpack = function(file) {
  20568. if (!(file.read && fm.file(file.phash).write)) {
  20569. error = ['errExtract', file.name, 'errPerm'];
  20570. fm.error(error);
  20571. dfrd.reject(error);
  20572. } else if ($.inArray(file.mime, mimes) === -1) {
  20573. error = ['errExtract', file.name, 'errNoArchive'];
  20574. fm.error(error);
  20575. dfrd.reject(error);
  20576. } else {
  20577. fm.request({
  20578. data:{cmd:'extract', target:file.hash, makedir:makedir},
  20579. notify:{type:'extract', cnt:1},
  20580. syncOnFail:true,
  20581. navigate:{
  20582. toast : makedir? {
  20583. incwd : {msg: fm.i18n(['complete', fm.i18n('cmdextract')]), action: {cmd: 'open', msg: 'cmdopen'}},
  20584. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdextract')]), action: {cmd: 'open', msg: 'cmdopen'}}
  20585. } : {
  20586. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmdextract')])}
  20587. }
  20588. }
  20589. })
  20590. .fail(function (error) {
  20591. if (dfrd.state() != 'rejected') {
  20592. dfrd.reject(error);
  20593. }
  20594. })
  20595. .done(function () {
  20596. });
  20597. }
  20598. };
  20599. var confirm = function(files, index) {
  20600. var file = files[index],
  20601. name = fm.splitFileExtention(file.name)[0],
  20602. existed = ($.inArray(name, names) >= 0),
  20603. next = function(){
  20604. if((index+1) < cnt) {
  20605. confirm(files, index+1);
  20606. } else {
  20607. dfrd.resolve();
  20608. }
  20609. };
  20610. if (!makedir && existed && map[name].mime != 'directory') {
  20611. fm.confirm(
  20612. {
  20613. title : fm.i18n('ntfextract'),
  20614. text : ['errExists', name, 'confirmRepl'],
  20615. accept:{
  20616. label : 'btnYes',
  20617. callback:function (all) {
  20618. decision = all ? 'overwrite_all' : 'overwrite';
  20619. decide(decision);
  20620. if(!overwriteAll && !omitAll) {
  20621. if('overwrite' == decision) {
  20622. unpack(file);
  20623. }
  20624. if((index+1) < cnt) {
  20625. confirm(files, index+1);
  20626. } else {
  20627. dfrd.resolve();
  20628. }
  20629. } else if(overwriteAll) {
  20630. for (i = index; i < cnt; i++) {
  20631. unpack(files[i]);
  20632. }
  20633. dfrd.resolve();
  20634. }
  20635. }
  20636. },
  20637. reject : {
  20638. label : 'btnNo',
  20639. callback:function (all) {
  20640. decision = all ? 'omit_all' : 'omit';
  20641. decide(decision);
  20642. if(!overwriteAll && !omitAll && (index+1) < cnt) {
  20643. confirm(files, index+1);
  20644. } else if (omitAll) {
  20645. dfrd.resolve();
  20646. }
  20647. }
  20648. },
  20649. cancel : {
  20650. label : 'btnCancel',
  20651. callback:function () {
  20652. dfrd.resolve();
  20653. }
  20654. },
  20655. all : ((index+1) < cnt)
  20656. }
  20657. );
  20658. } else if (!makedir) {
  20659. if (mkdirAll == 0) {
  20660. fm.confirm({
  20661. title : fm.i18n('cmdextract'),
  20662. text : [fm.i18n('cmdextract')+' "'+file.name+'"', 'confirmRepl'],
  20663. accept:{
  20664. label : 'btnYes',
  20665. callback:function (all) {
  20666. all && (mkdirAll = 1);
  20667. unpack(file);
  20668. next();
  20669. }
  20670. },
  20671. reject : {
  20672. label : 'btnNo',
  20673. callback:function (all) {
  20674. all && (mkdirAll = -1);
  20675. next();
  20676. }
  20677. },
  20678. cancel : {
  20679. label : 'btnCancel',
  20680. callback:function () {
  20681. dfrd.resolve();
  20682. }
  20683. },
  20684. all : ((index+1) < cnt)
  20685. });
  20686. } else {
  20687. (mkdirAll > 0) && unpack(file);
  20688. next();
  20689. }
  20690. } else {
  20691. unpack(file);
  20692. next();
  20693. }
  20694. };
  20695. if (!(this.enabled() && cnt && mimes.length)) {
  20696. return dfrd.reject();
  20697. }
  20698. if(cnt > 0) {
  20699. confirm(files, 0);
  20700. }
  20701. return dfrd;
  20702. };
  20703. };
  20704. /*
  20705. * File: /js/commands/forward.js
  20706. */
  20707. /**
  20708. * @class elFinder command "forward"
  20709. * Open next visited folder
  20710. *
  20711. * @author Dmitry (dio) Levashov
  20712. **/
  20713. (elFinder.prototype.commands.forward = function() {
  20714. this.alwaysEnabled = true;
  20715. this.updateOnSelect = true;
  20716. this.shortcuts = [{
  20717. pattern : 'ctrl+right'
  20718. }];
  20719. this.getstate = function() {
  20720. return this.fm.history.canForward() ? 0 : -1;
  20721. };
  20722. this.exec = function() {
  20723. return this.fm.history.forward();
  20724. };
  20725. }).prototype = { forceLoad : true }; // this is required command
  20726. /*
  20727. * File: /js/commands/fullscreen.js
  20728. */
  20729. /**
  20730. * @class elFinder command "fullscreen"
  20731. * elFinder node to full scrren mode
  20732. *
  20733. * @author Naoki Sawada
  20734. **/
  20735. elFinder.prototype.commands.fullscreen = function() {
  20736. var self = this,
  20737. fm = this.fm,
  20738. update = function(e, data) {
  20739. e.preventDefault();
  20740. e.stopPropagation();
  20741. if (data && data.fullscreen) {
  20742. self.update(void(0), (data.fullscreen === 'on'));
  20743. }
  20744. };
  20745. this.alwaysEnabled = true;
  20746. this.updateOnSelect = false;
  20747. this.syncTitleOnChange = true;
  20748. this.value = false;
  20749. this.options = {
  20750. ui : 'fullscreenbutton'
  20751. };
  20752. this.getstate = function() {
  20753. return 0;
  20754. };
  20755. this.exec = function() {
  20756. var node = fm.getUI().get(0),
  20757. full = (node === fm.toggleFullscreen(node));
  20758. self.title = fm.i18n(full ? 'reinstate' : 'cmdfullscreen');
  20759. self.update(void(0), full);
  20760. return $.Deferred().resolve();
  20761. };
  20762. fm.bind('init', function() {
  20763. fm.getUI().off('resize.' + fm.namespace, update).on('resize.' + fm.namespace, update);
  20764. });
  20765. };
  20766. /*
  20767. * File: /js/commands/getfile.js
  20768. */
  20769. /**
  20770. * @class elFinder command "getfile".
  20771. * Return selected files info into outer callback.
  20772. * For use elFinder with wysiwyg editors etc.
  20773. *
  20774. * @author Dmitry (dio) Levashov, dio@std42.ru
  20775. **/
  20776. (elFinder.prototype.commands.getfile = function() {
  20777. var self = this,
  20778. fm = this.fm,
  20779. filter = function(files) {
  20780. var o = self.options;
  20781. files = $.grep(files, function(file) {
  20782. return (file.mime != 'directory' || o.folders) && file.read ? true : false;
  20783. });
  20784. return o.multiple || files.length == 1 ? files : [];
  20785. };
  20786. this.alwaysEnabled = true;
  20787. this.callback = fm.options.getFileCallback;
  20788. this._disabled = typeof(this.callback) == 'function';
  20789. this.getstate = function(select) {
  20790. var sel = this.files(select),
  20791. cnt = sel.length;
  20792. return this.callback && cnt && filter(sel).length == cnt ? 0 : -1;
  20793. };
  20794. this.exec = function(hashes) {
  20795. var fm = this.fm,
  20796. opts = this.options,
  20797. files = this.files(hashes),
  20798. cnt = files.length,
  20799. url = fm.option('url'),
  20800. tmb = fm.option('tmbUrl'),
  20801. dfrd = $.Deferred()
  20802. .done(function(data) {
  20803. var res,
  20804. done = function() {
  20805. if (opts.oncomplete == 'close') {
  20806. fm.hide();
  20807. } else if (opts.oncomplete == 'destroy') {
  20808. fm.destroy();
  20809. }
  20810. };
  20811. fm.trigger('getfile', {files : data});
  20812. try {
  20813. res = self.callback(data, fm);
  20814. } catch(e) {
  20815. fm.error(['Error in `getFileCallback`.', e.message]);
  20816. return;
  20817. }
  20818. if (typeof res === 'object' && typeof res.done === 'function') {
  20819. res.done(done)
  20820. .fail(function(error) {
  20821. error && fm.error(error);
  20822. });
  20823. } else {
  20824. done();
  20825. }
  20826. }),
  20827. result = function(file) {
  20828. return opts.onlyURL
  20829. ? opts.multiple ? $.map(files, function(f) { return f.url; }) : files[0].url
  20830. : opts.multiple ? files : files[0];
  20831. },
  20832. req = [],
  20833. i, file, dim;
  20834. for (i = 0; i < cnt; i++) {
  20835. file = files[i];
  20836. if (file.mime == 'directory' && !opts.folders) {
  20837. return dfrd.reject();
  20838. }
  20839. file.baseUrl = url;
  20840. if (file.url == '1') {
  20841. req.push(fm.request({
  20842. data : {cmd : 'url', target : file.hash},
  20843. notify : {type : 'url', cnt : 1, hideCnt : true},
  20844. preventDefault : true
  20845. })
  20846. .done(function(data) {
  20847. if (data.url) {
  20848. var rfile = fm.file(this.hash);
  20849. rfile.url = this.url = data.url;
  20850. }
  20851. }.bind(file)));
  20852. } else {
  20853. file.url = fm.url(file.hash);
  20854. }
  20855. if (! opts.onlyURL) {
  20856. if (opts.getPath) {
  20857. file.path = fm.path(file.hash);
  20858. if (file.path === '' && file.phash) {
  20859. // get parents
  20860. (function() {
  20861. var dfd = $.Deferred();
  20862. req.push(dfd);
  20863. fm.path(file.hash, false, {})
  20864. .done(function(path) {
  20865. file.path = path;
  20866. })
  20867. .fail(function() {
  20868. file.path = '';
  20869. })
  20870. .always(function() {
  20871. dfd.resolve();
  20872. });
  20873. })();
  20874. }
  20875. }
  20876. if (file.tmb && file.tmb != 1) {
  20877. file.tmb = tmb + file.tmb;
  20878. }
  20879. if (!file.width && !file.height) {
  20880. if (file.dim) {
  20881. dim = file.dim.split('x');
  20882. file.width = dim[0];
  20883. file.height = dim[1];
  20884. } else if (opts.getImgSize && file.mime.indexOf('image') !== -1) {
  20885. req.push(fm.request({
  20886. data : {cmd : 'dim', target : file.hash},
  20887. notify : {type : 'dim', cnt : 1, hideCnt : true},
  20888. preventDefault : true
  20889. })
  20890. .done(function(data) {
  20891. if (data.dim) {
  20892. var dim = data.dim.split('x');
  20893. var rfile = fm.file(this.hash);
  20894. rfile.width = this.width = dim[0];
  20895. rfile.height = this.height = dim[1];
  20896. }
  20897. }.bind(file)));
  20898. }
  20899. }
  20900. }
  20901. }
  20902. if (req.length) {
  20903. $.when.apply(null, req).always(function() {
  20904. dfrd.resolve(result(files));
  20905. });
  20906. return dfrd;
  20907. }
  20908. return dfrd.resolve(result(files));
  20909. };
  20910. }).prototype = { forceLoad : true }; // this is required command
  20911. /*
  20912. * File: /js/commands/help.js
  20913. */
  20914. /**
  20915. * @class elFinder command "help"
  20916. * "About" dialog
  20917. *
  20918. * @author Dmitry (dio) Levashov
  20919. **/
  20920. (elFinder.prototype.commands.help = function() {
  20921. var fm = this.fm,
  20922. self = this,
  20923. linktpl = '<div class="elfinder-help-link"> <a href="{url}">{link}</a></div>',
  20924. linktpltgt = '<div class="elfinder-help-link"> <a href="{url}" target="_blank">{link}</a></div>',
  20925. atpl = '<div class="elfinder-help-team"><div>{author}</div>{work}</div>',
  20926. url = /\{url\}/,
  20927. link = /\{link\}/,
  20928. author = /\{author\}/,
  20929. work = /\{work\}/,
  20930. r = 'replace',
  20931. prim = 'ui-priority-primary',
  20932. sec = 'ui-priority-secondary',
  20933. lic = 'elfinder-help-license',
  20934. tab = '<li class="ui-state-default ui-corner-top elfinder-help-tab-{id}"><a href="#'+fm.namespace+'-help-{id}">{title}</a></li>',
  20935. html = ['<div class="ui-tabs ui-widget ui-widget-content ui-corner-all elfinder-help">',
  20936. '<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all">'],
  20937. stpl = '<div class="elfinder-help-shortcut"><div class="elfinder-help-shortcut-pattern">{pattern}</div> {descrip}</div>',
  20938. sep = '<div class="elfinder-help-separator"/>',
  20939. selfUrl = $('base').length? document.location.href.replace(/#.*$/, '') : '',
  20940. about = function() {
  20941. html.push('<div id="'+fm.namespace+'-help-about" class="ui-tabs-panel ui-widget-content ui-corner-bottom"><div class="elfinder-help-logo"/>');
  20942. html.push('<h3>elFinder</h3>');
  20943. html.push('<div class="'+prim+'">'+fm.i18n('webfm')+'</div>');
  20944. html.push('<div class="'+sec+'">'+fm.i18n('ver')+': '+fm.version+', '+fm.i18n('protocolver')+': <span class="apiver"></span></div>');
  20945. html.push('<div class="'+sec+'">jQuery/jQuery UI: '+$().jquery+'/'+$.ui.version+'</div>');
  20946. html.push(sep);
  20947. html.push(linktpltgt[r](url, 'http://elfinder.org/')[r](link, fm.i18n('homepage')));
  20948. html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder/wiki')[r](link, fm.i18n('docs')));
  20949. html.push(linktpltgt[r](url, 'https://github.com/Studio-42/elFinder')[r](link, fm.i18n('github')));
  20950. //html.push(linktpltgt[r](url, 'http://twitter.com/elrte_elfinder')[r](link, fm.i18n('twitter')));
  20951. html.push(sep);
  20952. html.push('<div class="'+prim+'">'+fm.i18n('team')+'</div>');
  20953. html.push(atpl[r](author, 'Dmitry "dio" Levashov &lt;dio@std42.ru&gt;')[r](work, fm.i18n('chiefdev')));
  20954. html.push(atpl[r](author, 'Naoki Sawada &lt;hypweb+elfinder@gmail.com&gt;')[r](work, fm.i18n('developer')));
  20955. html.push(atpl[r](author, 'Troex Nevelin &lt;troex@fury.scancode.ru&gt;')[r](work, fm.i18n('maintainer')));
  20956. html.push(atpl[r](author, 'Alexey Sukhotin &lt;strogg@yandex.ru&gt;')[r](work, fm.i18n('contributor')));
  20957. if (fm.i18[fm.lang].translator) {
  20958. $.each(fm.i18[fm.lang].translator.split(', '), function() {
  20959. html.push(atpl[r](author, $.trim(this))[r](work, fm.i18n('translator')+' ('+fm.i18[fm.lang].language+')'));
  20960. });
  20961. }
  20962. html.push(sep);
  20963. html.push('<div class="'+lic+'">'+fm.i18n('icons')+': Pixelmixer, <a href="http://p.yusukekamiyamane.com" target="_blank">Fugue</a></div>');
  20964. html.push(sep);
  20965. html.push('<div class="'+lic+'">Licence: 3-clauses BSD Licence</div>');
  20966. html.push('<div class="'+lic+'">Copyright © 2009-2017, Studio 42</div>');
  20967. html.push('<div class="'+lic+'">„ …'+fm.i18n('dontforget')+' ”</div>');
  20968. html.push('</div>');
  20969. },
  20970. shortcuts = function() {
  20971. var sh = fm.shortcuts();
  20972. // shortcuts tab
  20973. html.push('<div id="'+fm.namespace+'-help-shortcuts" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  20974. if (sh.length) {
  20975. html.push('<div class="ui-widget-content elfinder-help-shortcuts">');
  20976. $.each(sh, function(i, s) {
  20977. html.push(stpl.replace(/\{pattern\}/, s[0]).replace(/\{descrip\}/, s[1]));
  20978. });
  20979. html.push('</div>');
  20980. } else {
  20981. html.push('<div class="elfinder-help-disabled">'+fm.i18n('shortcutsof')+'</div>');
  20982. }
  20983. html.push('</div>');
  20984. },
  20985. help = function() {
  20986. // help tab
  20987. html.push('<div id="'+fm.namespace+'-help-help" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  20988. html.push('<a href="https://github.com/Studio-42/elFinder/wiki" target="_blank" class="elfinder-dont-panic"><span>DON\'T PANIC</span></a>');
  20989. html.push('</div>');
  20990. // end help
  20991. },
  20992. usePref = false,
  20993. preference = function() {
  20994. usePref = true;
  20995. // preference tab
  20996. html.push('<div id="'+fm.namespace+'-help-preference" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  20997. html.push('<div class="ui-widget-content elfinder-help-preference"></div>');
  20998. html.push('</div>');
  20999. // end preference
  21000. },
  21001. useDebug = false,
  21002. debug = function() {
  21003. useDebug = true;
  21004. // debug tab
  21005. html.push('<div id="'+fm.namespace+'-help-debug" class="ui-tabs-panel ui-widget-content ui-corner-bottom">');
  21006. html.push('<div class="ui-widget-content elfinder-help-debug"><ul></ul></div>');
  21007. html.push('</div>');
  21008. // end debug
  21009. },
  21010. debugRender = function() {
  21011. var render = function(elm, obj) {
  21012. $.each(obj, function(k, v) {
  21013. elm.append($('<dt/>').text(k));
  21014. if (typeof v === 'undefined') {
  21015. elm.append($('<dd/>').append($('<span/>').text('undfined')));
  21016. } else if (typeof v === 'object' && !v) {
  21017. elm.append($('<dd/>').append($('<span/>').text('null')));
  21018. } else if (typeof v === 'object' && ($.isPlainObject(v) || v.length)) {
  21019. elm.append( $('<dd/>').append(render($('<dl/>'), v)));
  21020. } else {
  21021. elm.append($('<dd/>').append($('<span/>').text((v && typeof v === 'object')? '[]' : (v? v : '""'))));
  21022. }
  21023. });
  21024. return elm;
  21025. },
  21026. cnt = debugUL.children('li').length,
  21027. targetL, target, tabId,
  21028. info, lastUL, lastDIV;
  21029. if (self.debug.options || self.debug.debug) {
  21030. if (cnt >= 5) {
  21031. lastUL = debugUL.children('li:last');
  21032. lastDIV = debugDIV.children('div:last');
  21033. if (lastDIV.is(':hidden')) {
  21034. lastUL.remove();
  21035. lastDIV.remove();
  21036. } else {
  21037. lastUL.prev().remove();
  21038. lastDIV.prev().remove();
  21039. }
  21040. }
  21041. tabId = fm.namespace + '-help-debug-' + (+new Date());
  21042. targetL = $('<li/>').html('<a href="'+selfUrl+'#'+tabId+'">'+self.debug.debug.cmd+'</a>').prependTo(debugUL);
  21043. target = $('<div id="'+tabId+'"/>').data('debug', self.debug);
  21044. targetL.on('click.debugrender', function() {
  21045. var debug = target.data('debug');
  21046. target.removeData('debug');
  21047. if (debug) {
  21048. target.hide();
  21049. if (debug.debug) {
  21050. info = $('<fieldset>').append($('<legend/>').text('debug'), render($('<dl/>'), debug.debug));
  21051. target.append(info);
  21052. }
  21053. if (debug.options) {
  21054. info = $('<fieldset>').append($('<legend/>').text('options'), render($('<dl/>'), debug.options));
  21055. target.append(info);
  21056. }
  21057. target.show();
  21058. }
  21059. targetL.off('click.debugrender');
  21060. });
  21061. debugUL.after(target);
  21062. opened && debugDIV.tabs('refresh');
  21063. }
  21064. },
  21065. content = '',
  21066. initCallbacks = [],
  21067. init = function(fn) {
  21068. if (fn && typeof fn === 'function') {
  21069. initCallbacks.push(fn);
  21070. } else if (initCallbacks.length) {
  21071. $.each(initCallbacks, function() {
  21072. this.call(self);
  21073. });
  21074. initCallbacks = [];
  21075. }
  21076. },
  21077. loaded, opened, tabDebug, debugDIV, debugUL;
  21078. this.alwaysEnabled = true;
  21079. this.updateOnSelect = false;
  21080. this.state = -1;
  21081. this.shortcuts = [{
  21082. pattern : 'f1',
  21083. description : this.title
  21084. }];
  21085. fm.bind('load', function() {
  21086. var setupPref = function() {
  21087. var tab = content.find('.elfinder-help-preference'),
  21088. forms = self.options.prefs || ['language', 'toolbarPref', 'columnPref', 'selectAction', 'useStoredEditor', 'hashChecker', 'autoFocusDialog', 'clearBrowserData'],
  21089. dls = $();
  21090. forms = fm.arrayFlip(forms, true);
  21091. if (fm.options.getFileCallback) {
  21092. delete forms.selectAction;
  21093. }
  21094. forms.language && (forms.language = (function() {
  21095. var node = $('<div/>');
  21096. init(function() {
  21097. var langSel = $('<select/>').on('change', function() {
  21098. var lang = $(this).val();
  21099. fm.storage('lang', lang);
  21100. $('#'+fm.id).elfinder('reload');
  21101. }),
  21102. optTags = [],
  21103. langs = self.options.langs || {
  21104. ar: 'اللغة العربية',
  21105. bg: 'Български',
  21106. ca: 'Català',
  21107. cs: 'Čeština',
  21108. da: 'Dansk',
  21109. de: 'Deutsch',
  21110. el: 'Ελληνικά',
  21111. en: 'English',
  21112. es: 'Español',
  21113. fa: 'فارسی‌‎, پارسی‌',
  21114. fo: 'Føroyskt',
  21115. fr: 'Français',
  21116. he: 'עברית‎',
  21117. hr: 'Hrvatski',
  21118. hu: 'Magyar',
  21119. id: 'Bahasa Indonesia',
  21120. it: 'Italiano',
  21121. ja: '日本語',
  21122. ko: '한국어',
  21123. nl: 'Nederlands',
  21124. no: 'Norsk',
  21125. pl: 'Polski',
  21126. pt_BR: 'Português',
  21127. ro: 'Română',
  21128. ru: 'Pусский',
  21129. si: 'සිංහල',
  21130. sk: 'Slovenčina',
  21131. sl: 'Slovenščina',
  21132. sr: 'Srpski',
  21133. sv: 'Svenska',
  21134. tr: 'Türkçe',
  21135. ug_CN: 'ئۇيغۇرچە',
  21136. uk: 'Український',
  21137. vi: 'Tiếng Việt',
  21138. zh_CN: '简体中文',
  21139. zh_TW: '正體中文'
  21140. };
  21141. $.each(langs, function(lang, name) {
  21142. optTags.push('<option value="'+lang+'">'+name+'</option>');
  21143. });
  21144. node.replaceWith(langSel.append(optTags.join('')).val(fm.lang));
  21145. });
  21146. return node;
  21147. })());
  21148. forms.toolbarPref && (forms.toolbarPref = (function() {
  21149. var node = $('<div/>');
  21150. init(function() {
  21151. var pnls = $.map(fm.options.uiOptions.toolbar, function(v) {
  21152. return $.isArray(v)? v : null;
  21153. }),
  21154. tags = [],
  21155. hides = fm.storage('toolbarhides') || {};
  21156. $.each(pnls, function() {
  21157. var cmd = this,
  21158. name = fm.i18n('cmd'+cmd);
  21159. if (name === 'cmd'+cmd) {
  21160. name = fm.i18n(cmd);
  21161. }
  21162. tags.push('<span class="elfinder-help-toolbar-item"><label><input type="checkbox" value="'+cmd+'" '+(hides[cmd]? '' : 'checked')+'/>'+name+'</label></span>');
  21163. });
  21164. node.replaceWith($(tags.join(' ')).on('change', 'input', function() {
  21165. var v = $(this).val(),
  21166. o = $(this).is(':checked');
  21167. if (!o && !hides[v]) {
  21168. hides[v] = true;
  21169. } else if (o && hides[v]) {
  21170. delete hides[v];
  21171. }
  21172. fm.storage('toolbarhides', hides);
  21173. fm.trigger('toolbarpref');
  21174. }));
  21175. });
  21176. return node;
  21177. })());
  21178. forms.columnPref && (forms.columnPref = (function() {
  21179. var node = $('<div/>');
  21180. init(function() {
  21181. var cols = fm.options.uiOptions.cwd.listView.columns,
  21182. tags = [],
  21183. hides = fm.storage('columnhides') || {};
  21184. $.each(cols, function() {
  21185. var key = this,
  21186. name = fm.getColumnName(key);
  21187. tags.push('<span class="elfinder-help-column-item"><label><input type="checkbox" value="'+key+'" '+(hides[key]? '' : 'checked')+'/>'+name+'</label></span>');
  21188. });
  21189. node.replaceWith($(tags.join(' ')).on('change', 'input', function() {
  21190. var v = $(this).val(),
  21191. o = $(this).is(':checked');
  21192. if (!o && !hides[v]) {
  21193. hides[v] = true;
  21194. } else if (o && hides[v]) {
  21195. delete hides[v];
  21196. }
  21197. fm.storage('columnhides', hides);
  21198. fm.trigger('columnpref', { repaint: true });
  21199. }));
  21200. });
  21201. return node;
  21202. })());
  21203. forms.selectAction && (forms.selectAction = (function() {
  21204. var node = $('<div/>');
  21205. init(function() {
  21206. var actSel = $('<select/>').on('change', function() {
  21207. var act = $(this).val();
  21208. fm.storage('selectAction', act === 'default'? null : act);
  21209. }),
  21210. optTags = [],
  21211. acts = self.options.selectActions;
  21212. if ($.inArray('open', acts) === -1) {
  21213. acts.unshift('open');
  21214. }
  21215. $.each(acts, function(i, act) {
  21216. var names = $.map(act.split('/'), function(cmd) {
  21217. var name = fm.i18n('cmd'+cmd);
  21218. if (name === 'cmd'+cmd) {
  21219. name = fm.i18n(cmd);
  21220. }
  21221. return name;
  21222. });
  21223. optTags.push('<option value="'+act+'">'+names.join('/')+'</option>');
  21224. });
  21225. node.replaceWith(actSel.append(optTags.join('')).val(fm.storage('selectAction') || 'open'));
  21226. });
  21227. return node;
  21228. })());
  21229. forms.useStoredEditor && (forms.useStoredEditor = $('<input type="checkbox"/>').prop('checked', (function() {
  21230. var s = fm.storage('useStoredEditor');
  21231. return s? (s > 0) : fm.options.commandsOptions.edit.useStoredEditor;
  21232. })()).on('change', function(e) {
  21233. fm.storage('useStoredEditor', $(this).is(':checked')? 1 : -1);
  21234. fm.trigger('selectfiles', {files : fm.selected()});
  21235. }));
  21236. forms.hashChecker && fm.hashCheckers.length && (forms.hashChecker = (function() {
  21237. var node = $('<div/>');
  21238. init(function() {
  21239. var tags = [],
  21240. enabled = fm.arrayFlip(fm.storage('hashchekcer') || fm.options.commandsOptions.info.showHashAlgorisms, true);
  21241. $.each(fm.hashCheckers, function() {
  21242. var cmd = this,
  21243. name = fm.i18n(cmd);
  21244. tags.push('<span class="elfinder-help-hashchecker-item"><label><input type="checkbox" value="'+cmd+'" '+(enabled[cmd]? 'checked' : '')+'/>'+name+'</label></span>');
  21245. });
  21246. node.replaceWith($(tags.join(' ')).on('change', 'input', function() {
  21247. var v = $(this).val(),
  21248. o = $(this).is(':checked');
  21249. if (o) {
  21250. enabled[v] = true;
  21251. } else if (enabled[v]) {
  21252. delete enabled[v];
  21253. }
  21254. fm.storage('hashchekcer', $.grep(fm.hashCheckers, function(v) {
  21255. return enabled[v];
  21256. }));
  21257. }));
  21258. });
  21259. return node;
  21260. })());
  21261. forms.autoFocusDialog && (forms.autoFocusDialog = $('<input type="checkbox"/>').prop('checked', (function() {
  21262. var s = fm.storage('autoFocusDialog');
  21263. return s? (s > 0) : fm.options.uiOptions.dialog.focusOnMouseOver;
  21264. })()).on('change', function(e) {
  21265. fm.storage('autoFocusDialog', $(this).is(':checked')? 1 : -1);
  21266. }));
  21267. forms.clearBrowserData && (forms.clearBrowserData = $('<button/>').text(fm.i18n('reset')).button().on('click', function(e) {
  21268. e.preventDefault();
  21269. fm.storage();
  21270. $('#'+fm.id).elfinder('reload');
  21271. }));
  21272. $.each(forms, function(n, f) {
  21273. var checkboxes = fm.arrayFlip(['toolbarPref', 'columnPref', 'hashChecker'], ' elfinder-help-checkboxes'),
  21274. title;
  21275. if (f && f !== true) {
  21276. title = fm.i18n(n);
  21277. if (f instanceof jQuery && f.length === 1 && f.is('input:checkbox')) {
  21278. if (!f.attr('id')) {
  21279. f.attr('id', 'elfinder-help-'+n+'-checkbox');
  21280. }
  21281. title = '<label for="'+f.attr('id')+'">'+title+'</label>';
  21282. }
  21283. dls = dls.add($('<dt class="elfinder-help-'+n+checkboxes[n]+'">'+title+'</dt>')).add($('<dd class="elfinder-help-'+n+'"/>').append(f));
  21284. }
  21285. });
  21286. tab.append($('<dl/>').append(dls));
  21287. },
  21288. parts = self.options.view || ['about', 'shortcuts', 'help', 'preference', 'debug'],
  21289. i, helpSource, tabBase, tabNav, tabs, delta;
  21290. // force enable 'preference' tab
  21291. if ($.inArray('preference', parts) === -1) {
  21292. parts.push('preference');
  21293. }
  21294. // debug tab require jQueryUI Tabs Widget
  21295. if (! $.fn.tabs) {
  21296. if ((i = $.inArray(parts, 'debug')) !== false) {
  21297. parts.splice(i, 1);
  21298. }
  21299. }
  21300. $.each(parts, function(i, title) {
  21301. html.push(tab[r](/\{id\}/g, title)[r](/\{title\}/, fm.i18n(title)));
  21302. });
  21303. html.push('</ul>');
  21304. $.inArray('about', parts) !== -1 && about();
  21305. $.inArray('shortcuts', parts) !== -1 && shortcuts();
  21306. if ($.inArray('help', parts) !== -1) {
  21307. helpSource = fm.baseUrl+'js/i18n/help/%s.html.js';
  21308. help();
  21309. }
  21310. $.inArray('preference', parts) !== -1 && preference();
  21311. $.inArray('debug', parts) !== -1 && debug();
  21312. html.push('</div>');
  21313. content = $(html.join(''));
  21314. content.find('.ui-tabs-nav li')
  21315. .on('mouseenter mouseleave', function() {
  21316. $(this).toggleClass('ui-state-hover');
  21317. })
  21318. .children()
  21319. .on('click', function(e) {
  21320. var link = $(this);
  21321. e.preventDefault();
  21322. e.stopPropagation();
  21323. if (!link.hasClass('ui-tabs-selected')) {
  21324. link.parent().addClass('ui-tabs-selected ui-state-active').siblings().removeClass('ui-tabs-selected').removeClass('ui-state-active');
  21325. content.children('.ui-tabs-panel').hide().filter(link.attr('href')).show();
  21326. }
  21327. })
  21328. .filter(':first').trigger('click');
  21329. // preference
  21330. usePref && setupPref();
  21331. // debug
  21332. if (useDebug) {
  21333. tabDebug = content.find('.elfinder-help-tab-debug').hide();
  21334. debugDIV = content.find('#'+fm.namespace+'-help-debug').children('div:first');
  21335. debugUL = debugDIV.children('ul:first').on('click', function(e) {
  21336. e.preventDefault();
  21337. e.stopPropagation();
  21338. });
  21339. self.debug = {};
  21340. fm.bind('backenddebug', function(e) {
  21341. // CAUTION: DO NOT TOUCH `e.data`
  21342. if (useDebug && e.data && e.data.debug) {
  21343. self.debug = { options : e.data.options, debug : Object.assign({ cmd : fm.currentReqCmd }, e.data.debug) };
  21344. if (self.dialog) {
  21345. debugRender();
  21346. }
  21347. }
  21348. });
  21349. }
  21350. content.find('#'+fm.namespace+'-help-about').find('.apiver').text(fm.api);
  21351. self.dialog = fm.dialog(content, {
  21352. title : self.title,
  21353. width : 530,
  21354. maxWidth: 'window',
  21355. maxHeight: 'window',
  21356. autoOpen : false,
  21357. destroyOnClose : false,
  21358. close : function() {
  21359. if (useDebug) {
  21360. tabDebug.hide();
  21361. debugDIV.tabs('destroy');
  21362. }
  21363. opened = false;
  21364. }
  21365. })
  21366. .on('click', function(e) {
  21367. e.stopPropagation();
  21368. })
  21369. .css({
  21370. overflow: 'hidden'
  21371. });
  21372. tabBase = self.dialog.children('.ui-tabs');
  21373. tabNav = tabBase.children('.ui-tabs-nav:first');
  21374. tabs = tabBase.children('.ui-tabs-panel');
  21375. delta = self.dialog.outerHeight(true) - self.dialog.height();
  21376. self.dialog.closest('.ui-dialog').on('resize', function() {
  21377. tabs.height(self.dialog.height() - delta - tabNav.outerHeight(true) - 20);
  21378. });
  21379. if (helpSource) {
  21380. self.dialog.one('initContents', function() {
  21381. $.ajax({
  21382. url: self.options.helpSource? self.options.helpSource : helpSource.replace('%s', fm.lang),
  21383. dataType: 'html'
  21384. }).done(function(source) {
  21385. $('#'+fm.namespace+'-help-help').html(source);
  21386. }).fail(function() {
  21387. $.ajax({
  21388. url: helpSource.replace('%s', 'en'),
  21389. dataType: 'html'
  21390. }).done(function(source) {
  21391. $('#'+fm.namespace+'-help-help').html(source);
  21392. });
  21393. });
  21394. });
  21395. }
  21396. self.state = 0;
  21397. }).one('open', function() {
  21398. var debug = false;
  21399. fm.one('backenddebug', function() {
  21400. debug =true;
  21401. }).one('opendone', function() {
  21402. setTimeout(function() {
  21403. if (! debug && useDebug) {
  21404. useDebug = false;
  21405. tabDebug.hide();
  21406. debugDIV.hide();
  21407. debugUL.hide();
  21408. }
  21409. }, 0);
  21410. });
  21411. });
  21412. this.getstate = function() {
  21413. return 0;
  21414. };
  21415. this.exec = function(sel, opts) {
  21416. var tab = opts? opts.tab : void(0),
  21417. debugShow = function() {
  21418. if (useDebug) {
  21419. debugDIV.tabs();
  21420. debugUL.find('a:first').trigger('click');
  21421. tabDebug.show();
  21422. opened = true;
  21423. }
  21424. };
  21425. if (! loaded) {
  21426. loaded = true;
  21427. fm.lazy(init).done(debugShow);
  21428. } else {
  21429. debugShow();
  21430. }
  21431. this.dialog.trigger('initContents').elfinderdialog('open').find((tab? '.elfinder-help-tab-'+tab : '.ui-tabs-nav li') + ' a:first').trigger('click');
  21432. return $.Deferred().resolve();
  21433. };
  21434. }).prototype = { forceLoad : true }; // this is required command
  21435. elFinder.prototype.commands.preference = function() {
  21436. this.linkedCmds = ['help'];
  21437. this.alwaysEnabled = true;
  21438. this.getstate = function() {
  21439. return 0;
  21440. };
  21441. this.exec = function() {
  21442. return this.fm.exec('help', void(0), {tab: 'preference'});
  21443. };
  21444. };
  21445. /*
  21446. * File: /js/commands/hidden.js
  21447. */
  21448. /**
  21449. * @class elFinder command "hidden"
  21450. * Always hidden command for uiCmdMap
  21451. *
  21452. * @author Naoki Sawada
  21453. **/
  21454. elFinder.prototype.commands.hidden = function() {
  21455. this.hidden = true;
  21456. this.updateOnSelect = false;
  21457. this.getstate = function() {
  21458. return -1;
  21459. };
  21460. };
  21461. /*
  21462. * File: /js/commands/home.js
  21463. */
  21464. (elFinder.prototype.commands.home = function() {
  21465. this.title = 'Home';
  21466. this.alwaysEnabled = true;
  21467. this.updateOnSelect = false;
  21468. this.shortcuts = [{
  21469. pattern : 'ctrl+home ctrl+shift+up',
  21470. description : 'Home'
  21471. }];
  21472. this.getstate = function() {
  21473. var root = this.fm.root(),
  21474. cwd = this.fm.cwd().hash;
  21475. return root && cwd && root != cwd ? 0: -1;
  21476. };
  21477. this.exec = function() {
  21478. return this.fm.exec('open', this.fm.root());
  21479. };
  21480. }).prototype = { forceLoad : true }; // this is required command
  21481. /*
  21482. * File: /js/commands/info.js
  21483. */
  21484. /**
  21485. * @class elFinder command "info".
  21486. * Display dialog with file properties.
  21487. *
  21488. * @author Dmitry (dio) Levashov, dio@std42.ru
  21489. **/
  21490. (elFinder.prototype.commands.info = function() {
  21491. var m = 'msg',
  21492. fm = this.fm,
  21493. spclass = 'elfinder-info-spinner',
  21494. btnclass = 'elfinder-info-button',
  21495. msg = {
  21496. calc : fm.i18n('calc'),
  21497. size : fm.i18n('size'),
  21498. unknown : fm.i18n('unknown'),
  21499. path : fm.i18n('path'),
  21500. aliasfor : fm.i18n('aliasfor'),
  21501. modify : fm.i18n('modify'),
  21502. perms : fm.i18n('perms'),
  21503. locked : fm.i18n('locked'),
  21504. dim : fm.i18n('dim'),
  21505. kind : fm.i18n('kind'),
  21506. files : fm.i18n('files'),
  21507. folders : fm.i18n('folders'),
  21508. roots : fm.i18n('volumeRoots'),
  21509. items : fm.i18n('items'),
  21510. yes : fm.i18n('yes'),
  21511. no : fm.i18n('no'),
  21512. link : fm.i18n('link'),
  21513. owner : fm.i18n('owner'),
  21514. group : fm.i18n('group'),
  21515. perm : fm.i18n('perm'),
  21516. getlink : fm.i18n('getLink')
  21517. };
  21518. this.tpl = {
  21519. 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>',
  21520. itemTitle : '<strong>{name}</strong><span class="elfinder-info-kind">{kind}</span>',
  21521. groupTitle : '<strong>{items}: {num}</strong>',
  21522. row : '<tr><td class="elfinder-info-label">{label} : </td><td class="{class}">{value}</td></tr>',
  21523. spinner : '<span>{text}</span> <span class="'+spclass+' '+spclass+'-{name}"/>'
  21524. };
  21525. this.alwaysEnabled = true;
  21526. this.updateOnSelect = false;
  21527. this.shortcuts = [{
  21528. pattern : 'ctrl+i'
  21529. }];
  21530. this.init = function() {
  21531. $.each(msg, function(k, v) {
  21532. msg[k] = fm.i18n(v);
  21533. });
  21534. };
  21535. this.getstate = function() {
  21536. return 0;
  21537. };
  21538. this.exec = function(hashes) {
  21539. var files = this.files(hashes);
  21540. if (! files.length) {
  21541. files = this.files([ this.fm.cwd().hash ]);
  21542. }
  21543. var self = this,
  21544. fm = this.fm,
  21545. o = this.options,
  21546. tpl = this.tpl,
  21547. row = tpl.row,
  21548. cnt = files.length,
  21549. content = [],
  21550. view = tpl.main,
  21551. l = '{label}',
  21552. v = '{value}',
  21553. reqs = [],
  21554. reqDfrd = null,
  21555. opts = {
  21556. title : fm.i18n('selectionInfo'),
  21557. width : 'auto',
  21558. close : function() {
  21559. $(this).elfinderdialog('destroy');
  21560. if (reqDfrd && reqDfrd.state() === 'pending') {
  21561. reqDfrd.reject();
  21562. }
  21563. $.grep(reqs, function(r) {
  21564. r && r.state() === 'pending' && r.reject();
  21565. });
  21566. }
  21567. },
  21568. count = [],
  21569. replSpinner = function(msg, name, className) {
  21570. dialog.find('.'+spclass+'-'+name).parent().html(msg).addClass(className || '');
  21571. },
  21572. id = fm.namespace+'-info-'+$.map(files, function(f) { return f.hash; }).join('-'),
  21573. dialog = fm.getUI().find('#'+id),
  21574. customActions = [],
  21575. style = '',
  21576. hashClass = 'elfinder-font-mono elfinder-info-hash',
  21577. size, tmb, file, title, dcnt, rdcnt, path, getHashAlgorisms;
  21578. if (!cnt) {
  21579. return $.Deferred().reject();
  21580. }
  21581. if (dialog.length) {
  21582. dialog.elfinderdialog('toTop');
  21583. return $.Deferred().resolve();
  21584. }
  21585. if (cnt === 1) {
  21586. file = files[0];
  21587. if (file.icon) {
  21588. style = ' '+fm.getIconStyle(file);
  21589. }
  21590. view = view.replace('{dirclass}', file.csscls? fm.escape(file.csscls) : '').replace('{class}', fm.mime2class(file.mime)).replace('{style}', style);
  21591. title = tpl.itemTitle.replace('{name}', fm.escape(file.i18 || file.name)).replace('{kind}', '<span title="'+fm.escape(file.mime)+'">'+fm.mime2kind(file)+'</span>');
  21592. tmb = fm.tmb(file);
  21593. if (!file.read) {
  21594. size = msg.unknown;
  21595. } else if (file.mime != 'directory' || file.alias) {
  21596. size = fm.formatSize(file.size);
  21597. } else {
  21598. size = tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size');
  21599. count.push(file.hash);
  21600. }
  21601. content.push(row.replace(l, msg.size).replace(v, size));
  21602. file.alias && content.push(row.replace(l, msg.aliasfor).replace(v, file.alias));
  21603. if (path = fm.path(file.hash, true)) {
  21604. content.push(row.replace(l, msg.path).replace(v, fm.escape(path).replace(/(\/|\\)/g, "$1&#8203;")).replace('{class}', 'elfinder-info-path'));
  21605. } else {
  21606. content.push(row.replace(l, msg.path).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'path')).replace('{class}', 'elfinder-info-path'));
  21607. reqs.push(fm.path(file.hash, true, {notify: null})
  21608. .fail(function() {
  21609. replSpinner(msg.unknown, 'path');
  21610. })
  21611. .done(function(path) {
  21612. replSpinner(path.replace(/(\/|\\)/g, "$1&#8203;"), 'path');
  21613. }));
  21614. }
  21615. if (file.read) {
  21616. var href,
  21617. name_esc = fm.escape(file.name);
  21618. if (file.url == '1') {
  21619. content.push(row.replace(l, msg.link).replace(v, '<button class="'+btnclass+' '+spclass+'-url">'+msg.getlink+'</button>'));
  21620. } else {
  21621. if (o.nullUrlDirLinkSelf && file.mime == 'directory' && file.url === null) {
  21622. var loc = window.location;
  21623. href = loc.pathname + loc.search + '#elf_' + file.hash;
  21624. } else {
  21625. href = fm.url(file.hash);
  21626. }
  21627. content.push(row.replace(l, msg.link).replace(v, '<a href="'+href+'" target="_blank">'+name_esc+'</a>'));
  21628. }
  21629. }
  21630. if (file.dim) { // old api
  21631. content.push(row.replace(l, msg.dim).replace(v, file.dim));
  21632. } else if (file.mime.indexOf('image') !== -1) {
  21633. if (file.width && file.height) {
  21634. content.push(row.replace(l, msg.dim).replace(v, file.width+'x'+file.height));
  21635. } else {
  21636. content.push(row.replace(l, msg.dim).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'dim')));
  21637. reqs.push(fm.request({
  21638. data : {cmd : 'dim', target : file.hash},
  21639. preventDefault : true
  21640. })
  21641. .fail(function() {
  21642. replSpinner(msg.unknown, 'dim');
  21643. })
  21644. .done(function(data) {
  21645. replSpinner(data.dim || msg.unknown, 'dim');
  21646. if (data.dim) {
  21647. var dim = data.dim.split('x');
  21648. var rfile = fm.file(file.hash);
  21649. rfile.width = dim[0];
  21650. rfile.height = dim[1];
  21651. }
  21652. }));
  21653. }
  21654. }
  21655. content.push(row.replace(l, msg.modify).replace(v, fm.formatDate(file)));
  21656. content.push(row.replace(l, msg.perms).replace(v, fm.formatPermissions(file)));
  21657. content.push(row.replace(l, msg.locked).replace(v, file.locked ? msg.yes : msg.no));
  21658. file.owner && content.push(row.replace(l, msg.owner).replace(v, file.owner));
  21659. file.group && content.push(row.replace(l, msg.group).replace(v, file.group));
  21660. file.perm && content.push(row.replace(l, msg.perm).replace(v, fm.formatFileMode(file.perm)));
  21661. // Get MD5 hash
  21662. if (window.ArrayBuffer && (fm.options.cdns.sparkmd5 || fm.options.cdns.jssha) && file.mime !== 'directory' && file.size > 0 && (!o.showHashMaxsize || file.size <= o.showHashMaxsize)) {
  21663. getHashAlgorisms = [];
  21664. $.each(fm.storage('hashchekcer') || o.showHashAlgorisms, function(i, n) {
  21665. if (!file[n]) {
  21666. content.push(row.replace(l, fm.i18n(n)).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', n)));
  21667. getHashAlgorisms.push(n);
  21668. } else {
  21669. content.push(row.replace(l, fm.i18n(n)).replace(v, file[n]).replace('{class}', hashClass));
  21670. }
  21671. });
  21672. reqs.push(
  21673. fm.getContentsHashes(file.hash, getHashAlgorisms).progress(function(hashes) {
  21674. $.each(getHashAlgorisms, function(i, n) {
  21675. if (hashes[n]) {
  21676. replSpinner(hashes[n], n, hashClass);
  21677. }
  21678. });
  21679. }).always(function() {
  21680. $.each(getHashAlgorisms, function(i, n) {
  21681. replSpinner(msg.unknown, n);
  21682. });
  21683. })
  21684. );
  21685. }
  21686. // Add custom info fields
  21687. if (o.custom) {
  21688. $.each(o.custom, function(name, details) {
  21689. if (
  21690. (!details.mimes || $.grep(details.mimes, function(m){return (file.mime === m || file.mime.indexOf(m+'/') === 0)? true : false;}).length)
  21691. &&
  21692. (!details.hashRegex || file.hash.match(details.hashRegex))
  21693. ) {
  21694. // Add to the content
  21695. content.push(row.replace(l, fm.i18n(details.label)).replace(v , details.tpl.replace('{id}', id)));
  21696. // Register the action
  21697. if (details.action && (typeof details.action == 'function')) {
  21698. customActions.push(details.action);
  21699. }
  21700. }
  21701. });
  21702. }
  21703. } else {
  21704. view = view.replace('{class}', 'elfinder-cwd-icon-group');
  21705. title = tpl.groupTitle.replace('{items}', msg.items).replace('{num}', cnt);
  21706. dcnt = $.grep(files, function(f) { return f.mime == 'directory' ? true : false ; }).length;
  21707. if (!dcnt) {
  21708. size = 0;
  21709. $.each(files, function(h, f) {
  21710. var s = parseInt(f.size);
  21711. if (s >= 0 && size >= 0) {
  21712. size += s;
  21713. } else {
  21714. size = 'unknown';
  21715. }
  21716. });
  21717. content.push(row.replace(l, msg.kind).replace(v, msg.files));
  21718. content.push(row.replace(l, msg.size).replace(v, fm.formatSize(size)));
  21719. } else {
  21720. rdcnt = $.grep(files, function(f) { return f.mime === 'directory' && (! f.phash || f.isroot)? true : false ; }).length;
  21721. dcnt -= rdcnt;
  21722. 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(', ')));
  21723. content.push(row.replace(l, msg.size).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{name}', 'size')));
  21724. count = $.map(files, function(f) { return f.hash; });
  21725. }
  21726. }
  21727. view = view.replace('{title}', title).replace('{content}', content.join('').replace(/{class}/g, ''));
  21728. dialog = fm.dialog(view, opts);
  21729. dialog.attr('id', id);
  21730. if (file && file.url == '1') {
  21731. dialog.on('click', '.'+spclass+'-url', function(){
  21732. $(this).parent().html(tpl.spinner.replace('{text}', fm.i18n('ntfurl')).replace('{name}', 'url'));
  21733. fm.request({
  21734. data : {cmd : 'url', target : file.hash},
  21735. preventDefault : true
  21736. })
  21737. .fail(function() {
  21738. replSpinner(name_esc, 'url');
  21739. })
  21740. .done(function(data) {
  21741. if (data.url) {
  21742. replSpinner('<a href="'+data.url+'" target="_blank">'+name_esc+'</a>' || name_esc, 'url');
  21743. var rfile = fm.file(file.hash);
  21744. rfile.url = data.url;
  21745. } else {
  21746. replSpinner(name_esc, 'url');
  21747. }
  21748. });
  21749. });
  21750. }
  21751. // load thumbnail
  21752. if (tmb) {
  21753. $('<img/>')
  21754. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  21755. .attr('src', tmb.url);
  21756. }
  21757. // send request to count total size
  21758. if (count.length) {
  21759. reqDfrd = fm.getSize(count).done(function(data) {
  21760. replSpinner(data.formated, 'size');
  21761. }).fail(function() {
  21762. replSpinner(msg.unknown, 'size');
  21763. });
  21764. }
  21765. // call custom actions
  21766. if (customActions.length) {
  21767. $.each(customActions, function(i, action) {
  21768. try {
  21769. action(file, fm, dialog);
  21770. } catch(e) {
  21771. fm.debug('error', e);
  21772. }
  21773. });
  21774. }
  21775. return $.Deferred().resolve();
  21776. };
  21777. }).prototype = { forceLoad : true }; // this is required command
  21778. /*
  21779. * File: /js/commands/mkdir.js
  21780. */
  21781. /**
  21782. * @class elFinder command "mkdir"
  21783. * Create new folder
  21784. *
  21785. * @author Dmitry (dio) Levashov
  21786. **/
  21787. elFinder.prototype.commands.mkdir = function() {
  21788. var fm = this.fm,
  21789. self = this,
  21790. curOrg;
  21791. this.value = '';
  21792. this.disableOnSearch = true;
  21793. this.updateOnSelect = false;
  21794. this.syncTitleOnChange = true;
  21795. this.mime = 'directory';
  21796. this.prefix = 'untitled folder';
  21797. this.exec = function(select, cOpts) {
  21798. var onCwd;
  21799. if (select && select.length && cOpts && cOpts._currentType && cOpts._currentType === 'navbar') {
  21800. this.origin = cOpts._currentType;
  21801. this.data = {
  21802. target: select[0]
  21803. };
  21804. } else {
  21805. onCwd = fm.cwd().hash === select[0];
  21806. this.origin = curOrg && !onCwd? curOrg : 'cwd';
  21807. delete this.data;
  21808. }
  21809. if (! select && ! this.options.intoNewFolderToolbtn) {
  21810. fm.getUI('cwd').trigger('unselectall');
  21811. }
  21812. this.move = (!onCwd && curOrg !== 'navbar' && fm.selected().length)? true : false;
  21813. return $.proxy(fm.res('mixin', 'make'), self)();
  21814. };
  21815. this.shortcuts = [{
  21816. pattern : 'ctrl+shift+n'
  21817. }];
  21818. this.init = function() {
  21819. if (this.options.intoNewFolderToolbtn) {
  21820. this.syncTitleOnChange = true;
  21821. }
  21822. };
  21823. fm.bind('select', function(e) {
  21824. var sel = (e.data && e.data.selected)? e.data.selected : [];
  21825. self.className = 'mkdir';
  21826. curOrg = sel.length? (e.data.origin || '') : '';
  21827. if (sel.length && curOrg !== 'navbar' && fm.cwd().hash !== sel[0]) {
  21828. self.title = fm.i18n('cmdmkdirin');
  21829. self.className += ' elfinder-button-icon-mkdirin';
  21830. } else {
  21831. self.title = fm.i18n('cmdmkdir');
  21832. }
  21833. self.update(void(0), self.title);
  21834. });
  21835. this.getstate = function(select) {
  21836. var cwd = fm.cwd(),
  21837. sel = (curOrg === 'navbar' || (select && select[0] !== cwd.hash))? this.files(select || fm.selected()) : [],
  21838. cnt = sel.length;
  21839. if (curOrg === 'navbar') {
  21840. return cnt && sel[0].write && sel[0].read? 0 : -1;
  21841. } else {
  21842. return cwd.write && (!cnt || $.grep(sel, function(f) { return f.read && ! f.locked? true : false; }).length == cnt)? 0 : -1;
  21843. }
  21844. };
  21845. };
  21846. /*
  21847. * File: /js/commands/mkfile.js
  21848. */
  21849. /**
  21850. * @class elFinder command "mkfile"
  21851. * Create new empty file
  21852. *
  21853. * @author Dmitry (dio) Levashov
  21854. **/
  21855. elFinder.prototype.commands.mkfile = function() {
  21856. this.disableOnSearch = true;
  21857. this.updateOnSelect = false;
  21858. this.mime = 'text/plain';
  21859. this.prefix = 'untitled file.txt';
  21860. this.exec = $.proxy(this.fm.res('mixin', 'make'), this);
  21861. this.getstate = function() {
  21862. return this.fm.cwd().write ? 0 : -1;
  21863. };
  21864. };
  21865. /*
  21866. * File: /js/commands/netmount.js
  21867. */
  21868. /**
  21869. * @class elFinder command "netmount"
  21870. * Mount network volume with user credentials.
  21871. *
  21872. * @author Dmitry (dio) Levashov
  21873. **/
  21874. elFinder.prototype.commands.netmount = function() {
  21875. var self = this,
  21876. content;
  21877. this.alwaysEnabled = true;
  21878. this.updateOnSelect = false;
  21879. this.drivers = [];
  21880. this.handlers = {
  21881. load : function() {
  21882. this.drivers = this.fm.netDrivers;
  21883. }
  21884. };
  21885. this.getstate = function() {
  21886. return this.drivers.length ? 0 : -1;
  21887. };
  21888. this.exec = function() {
  21889. var fm = self.fm,
  21890. dfrd = $.Deferred(),
  21891. o = self.options,
  21892. create = function() {
  21893. var winFocus = function() {
  21894. inputs.protocol.trigger('change', 'winfocus');
  21895. },
  21896. inputs = {
  21897. protocol : $('<select/>')
  21898. .on('change', function(e, data){
  21899. var protocol = this.value;
  21900. content.find('.elfinder-netmount-tr').hide();
  21901. content.find('.elfinder-netmount-tr-'+protocol).show();
  21902. dialogNode.children('.ui-dialog-buttonpane:first').find('button').show();
  21903. if (typeof o[protocol].select == 'function') {
  21904. o[protocol].select(fm, e, data);
  21905. }
  21906. setTimeout(function() {
  21907. content.find('input:text.elfinder-tabstop:visible:first').trigger('focus');
  21908. }, 20);
  21909. })
  21910. .addClass('ui-corner-all')
  21911. },
  21912. opts = {
  21913. title : fm.i18n('netMountDialogTitle'),
  21914. resizable : false,
  21915. modal : true,
  21916. destroyOnClose : false,
  21917. open : function() {
  21918. $(window).on('focus.'+fm.namespace, winFocus);
  21919. inputs.protocol.trigger('change');
  21920. },
  21921. close : function() {
  21922. dfrd.state() == 'pending' && dfrd.reject();
  21923. $(window).off('focus.'+fm.namespace, winFocus);
  21924. },
  21925. buttons : {}
  21926. },
  21927. doMount = function() {
  21928. var protocol = inputs.protocol.val(),
  21929. data = {cmd : 'netmount', protocol: protocol},
  21930. cur = o[protocol];
  21931. $.each(content.find('input.elfinder-netmount-inputs-'+protocol), function(name, input) {
  21932. var val, elm;
  21933. elm = $(input);
  21934. if (elm.is(':radio,:checkbox')) {
  21935. if (elm.is(':checked')) {
  21936. val = $.trim(elm.val());
  21937. }
  21938. } else {
  21939. val = $.trim(elm.val());
  21940. }
  21941. if (val) {
  21942. data[input.name] = val;
  21943. }
  21944. });
  21945. if (!data.host) {
  21946. return fm.trigger('error', {error : 'errNetMountHostReq', opts : {modal: true}});
  21947. }
  21948. fm.request({data : data, notify : {type : 'netmount', cnt : 1, hideCnt : true}})
  21949. .done(function(data) {
  21950. var pdir;
  21951. if (data.added && data.added.length) {
  21952. if (data.added[0].phash) {
  21953. if (pdir = fm.file(data.added[0].phash)) {
  21954. if (! pdir.dirs) {
  21955. pdir.dirs = 1;
  21956. fm.change({ changed: [ pdir ] });
  21957. }
  21958. }
  21959. }
  21960. fm.one('netmountdone', function() {
  21961. fm.exec('open', data.added[0].hash);
  21962. });
  21963. }
  21964. dfrd.resolve();
  21965. })
  21966. .fail(function(error) {
  21967. if (cur.fail && typeof cur.fail == 'function') {
  21968. cur.fail(fm, error);
  21969. }
  21970. dfrd.reject(error);
  21971. });
  21972. self.dialog.elfinderdialog('close');
  21973. },
  21974. form = $('<form autocomplete="off"/>').on('keydown', 'input', function(e) {
  21975. var comp = true,
  21976. next;
  21977. if (e.keyCode === $.ui.keyCode.ENTER) {
  21978. $.each(form.find('input:visible:not(.elfinder-input-optional)'), function() {
  21979. if ($(this).val() === '') {
  21980. comp = false;
  21981. next = $(this);
  21982. return false;
  21983. }
  21984. });
  21985. if (comp) {
  21986. doMount();
  21987. } else {
  21988. next.trigger('focus');
  21989. }
  21990. }
  21991. }),
  21992. hidden = $('<div/>'),
  21993. dialog;
  21994. content = $('<table class="elfinder-info-tb elfinder-netmount-tb"/>')
  21995. .append($('<tr/>').append($('<td>'+fm.i18n('protocol')+'</td>')).append($('<td/>').append(inputs.protocol)));
  21996. $.each(self.drivers, function(i, protocol) {
  21997. if (o[protocol]) {
  21998. inputs.protocol.append('<option value="'+protocol+'">'+fm.i18n(o[protocol].name || protocol)+'</option>');
  21999. $.each(o[protocol].inputs, function(name, input) {
  22000. input.attr('name', name);
  22001. if (input.attr('type') != 'hidden') {
  22002. input.addClass('ui-corner-all elfinder-netmount-inputs-'+protocol);
  22003. content.append($('<tr/>').addClass('elfinder-netmount-tr elfinder-netmount-tr-'+protocol).append($('<td>'+fm.i18n(name)+'</td>')).append($('<td/>').append(input)));
  22004. } else {
  22005. input.addClass('elfinder-netmount-inputs-'+protocol);
  22006. hidden.append(input);
  22007. }
  22008. });
  22009. o[protocol].protocol = inputs.protocol;
  22010. }
  22011. });
  22012. content.append(hidden);
  22013. content.find('.elfinder-netmount-tr').hide();
  22014. opts.buttons[fm.i18n('btnMount')] = doMount;
  22015. opts.buttons[fm.i18n('btnCancel')] = function() {
  22016. self.dialog.elfinderdialog('close');
  22017. };
  22018. content.find('select,input').addClass('elfinder-tabstop');
  22019. dialog = fm.dialog(form.append(content), opts);
  22020. dialogNode = dialog.closest('.ui-dialog');
  22021. dialog.ready(function(){
  22022. inputs.protocol.trigger('change');
  22023. dialog.elfinderdialog('posInit');
  22024. });
  22025. return dialog;
  22026. },
  22027. dialogNode;
  22028. if (!self.dialog) {
  22029. self.dialog = create();
  22030. } else {
  22031. self.dialog.elfinderdialog('open');
  22032. }
  22033. return dfrd.promise();
  22034. };
  22035. self.fm.bind('netmount', function(e) {
  22036. var d = e.data || null,
  22037. o = self.options;
  22038. if (d && d.protocol) {
  22039. if (o[d.protocol] && typeof o[d.protocol].done == 'function') {
  22040. o[d.protocol].done(self.fm, d);
  22041. content.find('select,input').addClass('elfinder-tabstop');
  22042. self.dialog.elfinderdialog('tabstopsInit');
  22043. }
  22044. }
  22045. });
  22046. };
  22047. elFinder.prototype.commands.netunmount = function() {
  22048. var self = this;
  22049. this.alwaysEnabled = true;
  22050. this.updateOnSelect = false;
  22051. this.drivers = [];
  22052. this.handlers = {
  22053. load : function() {
  22054. this.drivers = this.fm.netDrivers;
  22055. }
  22056. };
  22057. this.getstate = function(sel) {
  22058. var fm = this.fm,
  22059. file;
  22060. return !!sel && this.drivers.length && !this._disabled && (file = fm.file(sel[0])) && file.netkey ? 0 : -1;
  22061. };
  22062. this.exec = function(hashes) {
  22063. var self = this,
  22064. fm = this.fm,
  22065. dfrd = $.Deferred()
  22066. .fail(function(error) {
  22067. error && fm.error(error);
  22068. }),
  22069. drive = fm.file(hashes[0]),
  22070. childrenRoots = function(hash) {
  22071. var roots = [],
  22072. work;
  22073. if (fm.leafRoots) {
  22074. work = [];
  22075. $.each(fm.leafRoots, function(phash, hashes) {
  22076. var parents = fm.parents(phash),
  22077. idx, deep;
  22078. if ((idx = $.inArray(hash, parents)) !== -1) {
  22079. idx = parents.length - idx;
  22080. $.each(hashes, function(i, h) {
  22081. work.push({i: idx, hash: h});
  22082. });
  22083. }
  22084. });
  22085. if (work.length) {
  22086. work.sort(function(a, b) { return a.i < b.i; });
  22087. $.each(work, function(i, o) {
  22088. roots.push(o.hash);
  22089. });
  22090. }
  22091. }
  22092. return roots;
  22093. };
  22094. if (this._disabled) {
  22095. return dfrd.reject();
  22096. }
  22097. if (dfrd.state() == 'pending') {
  22098. fm.confirm({
  22099. title : self.title,
  22100. text : fm.i18n('confirmUnmount', drive.name),
  22101. accept : {
  22102. label : 'btnUnmount',
  22103. callback : function() {
  22104. var target = drive.hash,
  22105. roots = childrenRoots(target),
  22106. requests = [],
  22107. removed = [],
  22108. doUmount = function() {
  22109. $.when(requests).done(function() {
  22110. fm.request({
  22111. data : {cmd : 'netmount', protocol : 'netunmount', host: drive.netkey, user : target, pass : 'dum'},
  22112. notify : {type : 'netunmount', cnt : 1, hideCnt : true},
  22113. preventFail : true
  22114. })
  22115. .fail(function(error) {
  22116. dfrd.reject(error);
  22117. })
  22118. .done(function(data) {
  22119. dfrd.resolve();
  22120. });
  22121. }).fail(function(error) {
  22122. if (removed.length) {
  22123. fm.remove({ removed: removed });
  22124. }
  22125. dfrd.reject(error);
  22126. });
  22127. };
  22128. if (roots.length) {
  22129. fm.confirm({
  22130. title : self.title,
  22131. text : (function() {
  22132. var msgs = ['unmountChildren'];
  22133. $.each(roots, function(i, hash) {
  22134. msgs.push([fm.file(hash).name]);
  22135. });
  22136. return msgs;
  22137. })(),
  22138. accept : {
  22139. label : 'btnUnmount',
  22140. callback : function() {
  22141. $.each(roots, function(i, hash) {
  22142. var d = fm.file(hash);
  22143. if (d.netkey) {
  22144. requests.push(fm.request({
  22145. data : {cmd : 'netmount', protocol : 'netunmount', host: d.netkey, user : d.hash, pass : 'dum'},
  22146. notify : {type : 'netunmount', cnt : 1, hideCnt : true},
  22147. preventDefault : true
  22148. }).done(function(data) {
  22149. if (data.removed) {
  22150. removed = removed.concat(data.removed);
  22151. }
  22152. }));
  22153. }
  22154. });
  22155. doUmount();
  22156. }
  22157. },
  22158. cancel : {
  22159. label : 'btnCancel',
  22160. callback : function() {
  22161. dfrd.reject();
  22162. }
  22163. }
  22164. });
  22165. } else {
  22166. requests = null;
  22167. doUmount();
  22168. }
  22169. }
  22170. },
  22171. cancel : {
  22172. label : 'btnCancel',
  22173. callback : function() { dfrd.reject(); }
  22174. }
  22175. });
  22176. }
  22177. return dfrd;
  22178. };
  22179. };
  22180. /*
  22181. * File: /js/commands/open.js
  22182. */
  22183. /**
  22184. * @class elFinder command "open"
  22185. * Enter folder or open files in new windows
  22186. *
  22187. * @author Dmitry (dio) Levashov
  22188. **/
  22189. (elFinder.prototype.commands.open = function() {
  22190. var fm = this.fm;
  22191. this.alwaysEnabled = true;
  22192. this.noChangeDirOnRemovedCwd = true;
  22193. this._handlers = {
  22194. dblclick : function(e) { e.preventDefault(); fm.exec('open', e.data && e.data.file? [ e.data.file ]: void(0)); },
  22195. 'select enable disable reload' : function(e) { this.update(e.type == 'disable' ? -1 : void(0)); }
  22196. };
  22197. this.shortcuts = [{
  22198. pattern : 'ctrl+down numpad_enter'+(fm.OS != 'mac' && ' enter')
  22199. }];
  22200. this.getstate = function(select) {
  22201. var sel = this.files(select),
  22202. cnt = sel.length;
  22203. return cnt == 1
  22204. ? (sel[0].read? 0 : -1)
  22205. : (cnt && !fm.UA.Mobile) ? ($.grep(sel, function(file) { return file.mime == 'directory' || ! file.read ? false : true;}).length == cnt ? 0 : -1) : -1;
  22206. };
  22207. this.exec = function(hashes, cOpts) {
  22208. var dfrd = $.Deferred().fail(function(error) { error && fm.error(error); }),
  22209. files = this.files(hashes),
  22210. cnt = files.length,
  22211. thash = (typeof cOpts == 'object')? cOpts.thash : false,
  22212. opts = this.options,
  22213. into = opts.into || 'window',
  22214. file, url, s, w, imgW, imgH, winW, winH, reg, link, html5dl, inline,
  22215. selAct, cmd;
  22216. if (!cnt && !thash) {
  22217. {
  22218. return dfrd.reject();
  22219. }
  22220. }
  22221. // open folder
  22222. if (thash || (cnt == 1 && (file = files[0]) && file.mime == 'directory')) {
  22223. return !thash && file && !file.read
  22224. ? dfrd.reject(['errOpen', file.name, 'errPerm'])
  22225. : fm.request({
  22226. data : {cmd : 'open', target : thash || file.hash},
  22227. notify : {type : 'open', cnt : 1, hideCnt : true},
  22228. syncOnFail : true,
  22229. lazy : false
  22230. });
  22231. }
  22232. files = $.grep(files, function(file) { return file.mime != 'directory' ? true : false; });
  22233. // nothing to open or files and folders selected - do nothing
  22234. if (cnt != files.length) {
  22235. return dfrd.reject();
  22236. }
  22237. var doOpen = function() {
  22238. var wnd, target, getOnly;
  22239. try {
  22240. reg = new RegExp(fm.option('dispInlineRegex'), 'i');
  22241. } catch(e) {
  22242. reg = false;
  22243. }
  22244. // open files
  22245. link = $('<a>').hide().appendTo($('body')),
  22246. html5dl = (typeof link.get(0).download === 'string');
  22247. cnt = files.length;
  22248. while (cnt--) {
  22249. target = 'elf_open_window';
  22250. file = files[cnt];
  22251. if (!file.read) {
  22252. return dfrd.reject(['errOpen', file.name, 'errPerm']);
  22253. }
  22254. inline = (reg && file.mime.match(reg));
  22255. url = fm.openUrl(file.hash, !inline);
  22256. if (fm.UA.Mobile || !inline) {
  22257. if (html5dl) {
  22258. !inline && link.attr('download', file.name);
  22259. link.attr('href', url)
  22260. .attr('target', '_blank')
  22261. .get(0).click();
  22262. } else {
  22263. wnd = window.open(url);
  22264. if (!wnd) {
  22265. return dfrd.reject('errPopup');
  22266. }
  22267. }
  22268. } else {
  22269. getOnly = (typeof opts.method === 'string' && opts.method.toLowerCase() === 'get');
  22270. if (!getOnly
  22271. && url.indexOf(fm.options.url) === 0
  22272. && fm.customData
  22273. && Object.keys(fm.customData).length
  22274. // Since playback by POST request can not be done in Chrome, media allows GET request
  22275. && !file.mime.match(/^(?:video|audio)/)
  22276. ) {
  22277. // Send request as 'POST' method to hide custom data at location bar
  22278. url = '';
  22279. }
  22280. if (into === 'window') {
  22281. // set window size for image if set
  22282. imgW = winW = Math.round(2 * screen.availWidth / 3);
  22283. imgH = winH = Math.round(2 * screen.availHeight / 3);
  22284. if (parseInt(file.width) && parseInt(file.height)) {
  22285. imgW = parseInt(file.width);
  22286. imgH = parseInt(file.height);
  22287. } else if (file.dim) {
  22288. s = file.dim.split('x');
  22289. imgW = parseInt(s[0]);
  22290. imgH = parseInt(s[1]);
  22291. }
  22292. if (winW >= imgW && winH >= imgH) {
  22293. winW = imgW;
  22294. winH = imgH;
  22295. } else {
  22296. if ((imgW - winW) > (imgH - winH)) {
  22297. winH = Math.round(imgH * (winW / imgW));
  22298. } else {
  22299. winW = Math.round(imgW * (winH / imgH));
  22300. }
  22301. }
  22302. w = 'width='+winW+',height='+winH;
  22303. wnd = window.open(url, target, w + ',top=50,left=50,scrollbars=yes,resizable=yes,titlebar=no');
  22304. } else {
  22305. if (into === 'tabs') {
  22306. target = file.hash;
  22307. }
  22308. wnd = window.open('about:blank', target);
  22309. }
  22310. if (!wnd) {
  22311. return dfrd.reject('errPopup');
  22312. }
  22313. if (url === '') {
  22314. var form = document.createElement("form");
  22315. form.action = fm.options.url;
  22316. form.method = 'POST';
  22317. form.target = target;
  22318. form.style.display = 'none';
  22319. var params = Object.assign({}, fm.customData, {
  22320. cmd: 'file',
  22321. target: file.hash,
  22322. _t: file.ts || parseInt(+new Date()/1000)
  22323. });
  22324. $.each(params, function(key, val)
  22325. {
  22326. var input = document.createElement("input");
  22327. input.name = key;
  22328. input.value = val;
  22329. form.appendChild(input);
  22330. });
  22331. document.body.appendChild(form);
  22332. form.submit();
  22333. } else if (into !== 'window') {
  22334. wnd.location = url;
  22335. }
  22336. $(wnd).trigger('focus');
  22337. }
  22338. }
  22339. link.remove();
  22340. return dfrd.resolve(hashes);
  22341. };
  22342. if (cnt > 1) {
  22343. fm.confirm({
  22344. title: 'openMulti',
  22345. text : ['openMultiConfirm', cnt + ''],
  22346. accept : {
  22347. label : 'cmdopen',
  22348. callback : function() { doOpen(); }
  22349. },
  22350. cancel : {
  22351. label : 'btnCancel',
  22352. callback : function() {
  22353. dfrd.reject();
  22354. }
  22355. },
  22356. buttons : (fm.getCommand('zipdl') && fm.isCommandEnabled('zipdl', fm.cwd().hash))? [
  22357. {
  22358. label : 'cmddownload',
  22359. callback : function() {
  22360. fm.exec('download', hashes);
  22361. dfrd.reject();
  22362. }
  22363. }
  22364. ] : []
  22365. });
  22366. } else {
  22367. selAct = fm.storage('selectAction');
  22368. if (selAct) {
  22369. $.each(selAct.split('/'), function() {
  22370. var cmdName = this.valueOf();
  22371. if (cmdName !== 'open' && (cmd = fm.getCommand(cmdName)) && cmd.enabled()) {
  22372. return false;
  22373. }
  22374. cmd = null;
  22375. });
  22376. if (cmd) {
  22377. return fm.exec(cmd.name);
  22378. }
  22379. }
  22380. doOpen();
  22381. }
  22382. return dfrd;
  22383. };
  22384. }).prototype = { forceLoad : true }; // this is required command
  22385. /*
  22386. * File: /js/commands/opendir.js
  22387. */
  22388. /**
  22389. * @class elFinder command "opendir"
  22390. * Enter parent folder
  22391. *
  22392. * @author Naoki Sawada
  22393. **/
  22394. elFinder.prototype.commands.opendir = function() {
  22395. this.alwaysEnabled = true;
  22396. this.getstate = function() {
  22397. var sel = this.fm.selected(),
  22398. cnt = sel.length,
  22399. wz;
  22400. if (cnt !== 1) {
  22401. return -1;
  22402. }
  22403. wz = this.fm.getUI('workzone');
  22404. return wz.hasClass('elfinder-search-result')? 0 : -1;
  22405. };
  22406. this.exec = function(hashes) {
  22407. var fm = this.fm,
  22408. dfrd = $.Deferred(),
  22409. files = this.files(hashes),
  22410. cnt = files.length,
  22411. hash, pcheck = null;
  22412. if (!cnt || !files[0].phash) {
  22413. return dfrd.reject();
  22414. }
  22415. hash = files[0].phash;
  22416. fm.trigger('searchend', { noupdate: true });
  22417. fm.request({
  22418. data : {cmd : 'open', target : hash},
  22419. notify : {type : 'open', cnt : 1, hideCnt : true},
  22420. syncOnFail : false
  22421. });
  22422. return dfrd;
  22423. };
  22424. };
  22425. /*
  22426. * File: /js/commands/paste.js
  22427. */
  22428. /**
  22429. * @class elFinder command "paste"
  22430. * Paste filesfrom clipboard into directory.
  22431. * If files pasted in its parent directory - files duplicates will created
  22432. *
  22433. * @author Dmitry (dio) Levashov
  22434. **/
  22435. elFinder.prototype.commands.paste = function() {
  22436. this.updateOnSelect = false;
  22437. this.handlers = {
  22438. changeclipboard : function() { this.update(); }
  22439. };
  22440. this.shortcuts = [{
  22441. pattern : 'ctrl+v shift+insert'
  22442. }];
  22443. this.getstate = function(dst) {
  22444. if (this._disabled) {
  22445. return -1;
  22446. }
  22447. if (dst) {
  22448. if (Array.isArray(dst)) {
  22449. if (dst.length != 1) {
  22450. return -1;
  22451. }
  22452. dst = this.fm.file(dst[0]);
  22453. }
  22454. } else {
  22455. dst = this.fm.cwd();
  22456. }
  22457. return this.fm.clipboard().length && dst.mime == 'directory' && dst.write ? 0 : -1;
  22458. };
  22459. this.exec = function(select, cOpts) {
  22460. var self = this,
  22461. fm = self.fm,
  22462. opts = cOpts || {},
  22463. dst = select ? this.files(select)[0] : fm.cwd(),
  22464. files = fm.clipboard(),
  22465. cnt = files.length,
  22466. cut = cnt ? files[0].cut : false,
  22467. cmd = opts._cmd? opts._cmd : (cut? 'move' : 'copy'),
  22468. error = 'err' + cmd.charAt(0).toUpperCase() + cmd.substr(1),
  22469. fpaste = [],
  22470. fcopy = [],
  22471. dfrd = $.Deferred()
  22472. .fail(function(error) {
  22473. error && fm.error(error);
  22474. })
  22475. .always(function() {
  22476. fm.unlockfiles({files : $.map(files, function(f) { return f.hash; })});
  22477. }),
  22478. copy = function(files) {
  22479. return files.length && fm._commands.duplicate
  22480. ? fm.exec('duplicate', files)
  22481. : $.Deferred().resolve();
  22482. },
  22483. paste = function(files) {
  22484. var dfrd = $.Deferred(),
  22485. existed = [],
  22486. hashes = {},
  22487. intersect = function(files, names) {
  22488. var ret = [],
  22489. i = files.length;
  22490. while (i--) {
  22491. $.inArray(files[i].name, names) !== -1 && ret.unshift(i);
  22492. }
  22493. return ret;
  22494. },
  22495. confirm = function(ndx) {
  22496. var i = existed[ndx],
  22497. file = files[i],
  22498. last = ndx == existed.length-1;
  22499. if (!file) {
  22500. return;
  22501. }
  22502. fm.confirm({
  22503. title : fm.i18n(cmd + 'Files'),
  22504. text : ['errExists', file.name, cmd === 'restore'? 'confirmRest' : 'confirmRepl'],
  22505. all : !last,
  22506. accept : {
  22507. label : 'btnYes',
  22508. callback : function(all) {
  22509. !last && !all
  22510. ? confirm(++ndx)
  22511. : paste(files);
  22512. }
  22513. },
  22514. reject : {
  22515. label : 'btnNo',
  22516. callback : function(all) {
  22517. var i;
  22518. if (all) {
  22519. i = existed.length;
  22520. while (ndx < i--) {
  22521. files[existed[i]].remove = true;
  22522. }
  22523. } else {
  22524. files[existed[ndx]].remove = true;
  22525. }
  22526. !last && !all
  22527. ? confirm(++ndx)
  22528. : paste(files);
  22529. }
  22530. },
  22531. cancel : {
  22532. label : 'btnCancel',
  22533. callback : function() {
  22534. dfrd.resolve();
  22535. }
  22536. },
  22537. buttons : [
  22538. {
  22539. label : 'btnBackup',
  22540. callback : function(all) {
  22541. var i;
  22542. if (all) {
  22543. i = existed.length;
  22544. while (ndx < i--) {
  22545. files[existed[i]].rename = true;
  22546. }
  22547. } else {
  22548. files[existed[ndx]].rename = true;
  22549. }
  22550. !last && !all
  22551. ? confirm(++ndx)
  22552. : paste(files);
  22553. }
  22554. }
  22555. ]
  22556. });
  22557. },
  22558. valid = function(names) {
  22559. var exists = {}, existedArr;
  22560. if (names) {
  22561. if (Array.isArray(names)) {
  22562. if (names.length) {
  22563. if (typeof names[0] == 'string') {
  22564. // elFinder <= 2.1.6 command `is` results
  22565. existed = intersect(files, names);
  22566. } else {
  22567. $.each(names, function(i, v) {
  22568. exists[v.name] = v.hash;
  22569. });
  22570. existed = intersect(files, $.map(exists, function(h, n) { return n; }));
  22571. $.each(files, function(i, file) {
  22572. if (exists[file.name]) {
  22573. hashes[exists[file.name]] = file.name;
  22574. }
  22575. });
  22576. }
  22577. }
  22578. } else {
  22579. existedArr = [];
  22580. existed = $.map(names, function(n) {
  22581. if (typeof n === 'string') {
  22582. return n;
  22583. } else {
  22584. // support to >=2.1.11 plugin Normalizer, Sanitizer
  22585. existedArr = existedArr.concat(n);
  22586. return false;
  22587. }
  22588. });
  22589. if (existedArr.length) {
  22590. existed = existed.concat(existedArr);
  22591. }
  22592. existed = intersect(files, existed);
  22593. hashes = names;
  22594. }
  22595. }
  22596. existed.length ? confirm(0) : paste(files);
  22597. },
  22598. paste = function(selFiles) {
  22599. var renames = [],
  22600. files = $.grep(selFiles, function(file) {
  22601. if (file.rename) {
  22602. renames.push(file.name);
  22603. }
  22604. return !file.remove ? true : false;
  22605. }),
  22606. cnt = files.length,
  22607. groups = {},
  22608. args = [],
  22609. targets, reqData;
  22610. if (!cnt) {
  22611. return dfrd.resolve();
  22612. }
  22613. targets = $.map(files, function(f) { return f.hash; });
  22614. reqData = {cmd : 'paste', dst : dst.hash, targets : targets, cut : cut ? 1 : 0, renames : renames, hashes : hashes, suffix : fm.options.backupSuffix};
  22615. if (fm.api < 2.1) {
  22616. reqData.src = files[0].phash;
  22617. }
  22618. fm.request({
  22619. data : reqData,
  22620. notify : {type : cmd, cnt : cnt},
  22621. navigate : {
  22622. toast : opts.noToast? {} : {
  22623. inbuffer : {msg: fm.i18n(['complete', fm.i18n('cmd' + cmd)]), action: {
  22624. cmd: 'open',
  22625. msg: 'cmdopendir',
  22626. data: [dst.hash],
  22627. done: 'select',
  22628. cwdNot: dst.hash
  22629. }}
  22630. }
  22631. }
  22632. })
  22633. .done(function(data) {
  22634. var dsts = {},
  22635. added = data.added && data.added.length? data.added : null;
  22636. if (cut && added) {
  22637. // undo/redo
  22638. $.each(files, function(i, f) {
  22639. var phash = f.phash,
  22640. srcHash = function(name) {
  22641. var hash;
  22642. $.each(added, function(i, f) {
  22643. if (f.name === name) {
  22644. hash = f.hash;
  22645. return false;
  22646. }
  22647. });
  22648. return hash;
  22649. },
  22650. shash = srcHash(f.name);
  22651. if (shash) {
  22652. if (dsts[phash]) {
  22653. dsts[phash].push(shash);
  22654. } else {
  22655. dsts[phash] = [ shash ];
  22656. }
  22657. }
  22658. });
  22659. if (Object.keys(dsts).length) {
  22660. data.undo = {
  22661. cmd : 'move',
  22662. callback : function() {
  22663. var reqs = [];
  22664. $.each(dsts, function(dst, targets) {
  22665. reqs.push(fm.request({
  22666. data : {cmd : 'paste', dst : dst, targets : targets, cut : 1},
  22667. notify : {type : 'undo', cnt : targets.length}
  22668. }));
  22669. });
  22670. return $.when.apply(null, reqs);
  22671. }
  22672. };
  22673. data.redo = {
  22674. cmd : 'move',
  22675. callback : function() {
  22676. return fm.request({
  22677. data : reqData,
  22678. notify : {type : 'redo', cnt : cnt}
  22679. });
  22680. }
  22681. };
  22682. }
  22683. }
  22684. dfrd.resolve(data);
  22685. })
  22686. .fail(function() {
  22687. dfrd.reject();
  22688. })
  22689. .always(function() {
  22690. fm.unlockfiles({files : files});
  22691. });
  22692. },
  22693. internames;
  22694. if (!fm.isCommandEnabled(self.name, dst.hash) || !files.length) {
  22695. return dfrd.resolve();
  22696. }
  22697. if (fm.oldAPI) {
  22698. paste(files);
  22699. } else {
  22700. if (!fm.option('copyOverwrite', dst.hash)) {
  22701. paste(files);
  22702. } else {
  22703. internames = $.map(files, function(f) { return f.name; });
  22704. dst.hash == fm.cwd().hash
  22705. ? valid($.map(fm.files(), function(file) { return file.phash == dst.hash ? {hash: file.hash, name: file.name} : null; }))
  22706. : fm.request({
  22707. data : {cmd : 'ls', target : dst.hash, intersect : internames},
  22708. notify : {type : 'prepare', cnt : 1, hideCnt : true},
  22709. preventFail : true
  22710. })
  22711. .always(function(data) {
  22712. valid(data.list);
  22713. });
  22714. }
  22715. }
  22716. return dfrd;
  22717. },
  22718. parents, fparents;
  22719. if (!cnt || !dst || dst.mime != 'directory') {
  22720. return dfrd.reject();
  22721. }
  22722. if (!dst.write) {
  22723. return dfrd.reject([error, files[0].name, 'errPerm']);
  22724. }
  22725. parents = fm.parents(dst.hash);
  22726. $.each(files, function(i, file) {
  22727. if (!file.read) {
  22728. return !dfrd.reject([error, file.name, 'errPerm']);
  22729. }
  22730. if (cut && file.locked) {
  22731. return !dfrd.reject(['errLocked', file.name]);
  22732. }
  22733. if ($.inArray(file.hash, parents) !== -1) {
  22734. return !dfrd.reject(['errCopyInItself', file.name]);
  22735. }
  22736. if (file.mime && file.mime !== 'directory' && ! fm.uploadMimeCheck(file.mime, dst.hash)) {
  22737. return !dfrd.reject([error, file.name, 'errUploadMime']);
  22738. }
  22739. fparents = fm.parents(file.hash);
  22740. fparents.pop();
  22741. if ($.inArray(dst.hash, fparents) !== -1) {
  22742. if ($.grep(fparents, function(h) { var d = fm.file(h); return d.phash == dst.hash && d.name == file.name ? true : false; }).length) {
  22743. return !dfrd.reject(['errReplByChild', file.name]);
  22744. }
  22745. }
  22746. if (file.phash == dst.hash) {
  22747. fcopy.push(file.hash);
  22748. } else {
  22749. fpaste.push({
  22750. hash : file.hash,
  22751. phash : file.phash,
  22752. name : file.name
  22753. });
  22754. }
  22755. });
  22756. if (dfrd.state() == 'rejected') {
  22757. return dfrd;
  22758. }
  22759. $.when(
  22760. copy(fcopy),
  22761. paste(fpaste)
  22762. )
  22763. .done(function(cr, pr) {
  22764. dfrd.resolve(pr && pr.undo? pr : void(0));
  22765. })
  22766. .fail(function() {
  22767. dfrd.reject();
  22768. })
  22769. .always(function() {
  22770. cut && fm.clipboard([]);
  22771. });
  22772. return dfrd;
  22773. };
  22774. };
  22775. /*
  22776. * File: /js/commands/places.js
  22777. */
  22778. /**
  22779. * @class elFinder command "places"
  22780. * Regist to Places
  22781. *
  22782. * @author Naoki Sawada
  22783. **/
  22784. elFinder.prototype.commands.places = function() {
  22785. var self = this,
  22786. fm = this.fm,
  22787. filter = function(hashes) {
  22788. return $.grep(self.files(hashes), function(f) { return f.mime == 'directory' ? true : false; });
  22789. },
  22790. places = null;
  22791. this.getstate = function(select) {
  22792. var sel = this.hashes(select),
  22793. cnt = sel.length;
  22794. return places && cnt && cnt == filter(sel).length ? 0 : -1;
  22795. };
  22796. this.exec = function(hashes) {
  22797. var files = this.files(hashes);
  22798. places.trigger('regist', [ files ]);
  22799. return $.Deferred().resolve();
  22800. };
  22801. fm.one('load', function(){
  22802. places = fm.ui.places;
  22803. });
  22804. };
  22805. /*
  22806. * File: /js/commands/quicklook.js
  22807. */
  22808. /**
  22809. * @class elFinder command "quicklook"
  22810. * Fast preview for some files types
  22811. *
  22812. * @author Dmitry (dio) Levashov
  22813. **/
  22814. (elFinder.prototype.commands.quicklook = function() {
  22815. var self = this,
  22816. fm = self.fm,
  22817. /**
  22818. * window closed state
  22819. *
  22820. * @type Number
  22821. **/
  22822. closed = 0,
  22823. /**
  22824. * window animated state
  22825. *
  22826. * @type Number
  22827. **/
  22828. animated = 1,
  22829. /**
  22830. * window opened state
  22831. *
  22832. * @type Number
  22833. **/
  22834. opened = 2,
  22835. /**
  22836. * window docked state
  22837. *
  22838. * @type Number
  22839. **/
  22840. docked = 3,
  22841. /**
  22842. * window docked and hidden state
  22843. *
  22844. * @type Number
  22845. **/
  22846. dockedhidden = 4,
  22847. /**
  22848. * window state
  22849. *
  22850. * @type Number
  22851. **/
  22852. state = closed,
  22853. /**
  22854. * Event name of update
  22855. * for fix conflicts with Prototype.JS
  22856. *
  22857. * `@see https://github.com/Studio-42/elFinder/pull/2346
  22858. * @type String
  22859. **/
  22860. evUpdate = Element.update? 'quicklookupdate' : 'update',
  22861. /**
  22862. * navbar icon class
  22863. *
  22864. * @type String
  22865. **/
  22866. navicon = 'elfinder-quicklook-navbar-icon',
  22867. /**
  22868. * navbar "fullscreen" icon class
  22869. *
  22870. * @type String
  22871. **/
  22872. fullscreen = 'elfinder-quicklook-fullscreen',
  22873. /**
  22874. * info wrapper class
  22875. *
  22876. * @type String
  22877. */
  22878. infocls = 'elfinder-quicklook-info-wrapper',
  22879. /**
  22880. * Triger keydown/keypress event with left/right arrow key code
  22881. *
  22882. * @param Number left/right arrow key code
  22883. * @return void
  22884. **/
  22885. navtrigger = function(code) {
  22886. $(document).trigger($.Event('keydown', { keyCode: code, ctrlKey : false, shiftKey : false, altKey : false, metaKey : false }));
  22887. },
  22888. /**
  22889. * Return css for closed window
  22890. *
  22891. * @param jQuery file node in cwd
  22892. * @return void
  22893. **/
  22894. closedCss = function(node) {
  22895. var elf = fm.getUI().offset(),
  22896. base = (function() {
  22897. var target = node.find('.elfinder-cwd-file-wrapper');
  22898. return target.length? target : node;
  22899. })(),
  22900. baseOffset = base.offset() || { top: 0, left: 0 };
  22901. return {
  22902. opacity : 0,
  22903. width : base.width(),
  22904. height : base.height() - 30,
  22905. top : baseOffset.top - elf.top,
  22906. left : baseOffset.left - elf.left
  22907. };
  22908. },
  22909. /**
  22910. * Return css for opened window
  22911. *
  22912. * @return void
  22913. **/
  22914. openedCss = function() {
  22915. var contain = self.options.contain,
  22916. win = contain? fm.getUI() : $(window),
  22917. elf = fm.getUI().offset(),
  22918. w = Math.min(width, win.width()-10),
  22919. h = Math.min(height, win.height()-80);
  22920. return {
  22921. opacity : 1,
  22922. width : w,
  22923. height : h,
  22924. top : parseInt((win.height() - h - 60) / 2 + (contain? 0 : win.scrollTop() - elf.top)),
  22925. left : parseInt((win.width() - w) / 2 + (contain? 0 : win.scrollLeft() - elf.left))
  22926. };
  22927. },
  22928. support = function(codec, name) {
  22929. var media = document.createElement(name || codec.substr(0, codec.indexOf('/'))),
  22930. value = false;
  22931. try {
  22932. value = media.canPlayType && media.canPlayType(codec);
  22933. } catch (e) {
  22934. }
  22935. return (value && value !== '' && value != 'no')? true : false;
  22936. },
  22937. platformWin = (window.navigator.platform.indexOf('Win') != -1),
  22938. /**
  22939. * Opened window width (from config)
  22940. *
  22941. * @type Number
  22942. **/
  22943. width,
  22944. /**
  22945. * Opened window height (from config)
  22946. *
  22947. * @type Number
  22948. **/
  22949. height,
  22950. /**
  22951. * Previous style before docked
  22952. *
  22953. * @type String
  22954. **/
  22955. prevStyle,
  22956. /**
  22957. * elFinder node
  22958. *
  22959. * @type jQuery
  22960. **/
  22961. parent,
  22962. /**
  22963. * elFinder current directory node
  22964. *
  22965. * @type jQuery
  22966. **/
  22967. cwd,
  22968. /**
  22969. * Current directory hash
  22970. *
  22971. * @type String
  22972. **/
  22973. cwdHash,
  22974. dockEnabled = false,
  22975. navdrag = false,
  22976. navmove = false,
  22977. navtm = null,
  22978. leftKey = $.ui.keyCode.LEFT,
  22979. rightKey = $.ui.keyCode.RIGHT,
  22980. coverEv = 'mousemove touchstart ' + ('onwheel' in document? 'wheel' : 'onmousewheel' in document? 'mousewheel' : 'DOMMouseScroll'),
  22981. title = $('<div class="elfinder-quicklook-title"/>'),
  22982. icon = $('<div/>'),
  22983. info = $('<div class="elfinder-quicklook-info"/>'),//.hide(),
  22984. cover = $('<div class="ui-front elfinder-quicklook-cover"/>'),
  22985. fsicon = $('<div class="'+navicon+' '+navicon+'-fullscreen"/>')
  22986. .on('click touchstart', function(e) {
  22987. if (navmove) {
  22988. return;
  22989. }
  22990. var win = self.window,
  22991. full = win.hasClass(fullscreen),
  22992. $window = $(window),
  22993. resize = function() { self.preview.trigger('changesize'); };
  22994. e.stopPropagation();
  22995. e.preventDefault();
  22996. if (full) {
  22997. navStyle = '';
  22998. navShow();
  22999. win.toggleClass(fullscreen)
  23000. .css(win.data('position'));
  23001. $window.trigger(self.resize).off(self.resize, resize);
  23002. navbar.off('mouseenter mouseleave');
  23003. cover.off(coverEv);
  23004. } else {
  23005. win.toggleClass(fullscreen)
  23006. .data('position', {
  23007. left : win.css('left'),
  23008. top : win.css('top'),
  23009. width : win.width(),
  23010. height : win.height(),
  23011. display: 'block'
  23012. })
  23013. .removeAttr('style');
  23014. $(window).on(self.resize, resize)
  23015. .trigger(self.resize);
  23016. cover.on(coverEv, function(e) {
  23017. if (! navdrag) {
  23018. if (e.type === 'mousemove' || e.type === 'touchstart') {
  23019. navShow();
  23020. navtm = setTimeout(function() {
  23021. if (fm.UA.Mobile || navbar.parent().find('.elfinder-quicklook-navbar:hover').length < 1) {
  23022. navbar.fadeOut('slow', function() {
  23023. cover.show();
  23024. });
  23025. }
  23026. }, 3000);
  23027. }
  23028. if (cover.is(':visible')) {
  23029. coverHide();
  23030. cover.data('tm', setTimeout(function() {
  23031. cover.show();
  23032. }, 3000));
  23033. }
  23034. }
  23035. }).show().trigger('mousemove');
  23036. navbar.on('mouseenter mouseleave', function(e) {
  23037. if (! navdrag) {
  23038. if (e.type === 'mouseenter') {
  23039. navShow();
  23040. } else {
  23041. cover.trigger('mousemove');
  23042. }
  23043. }
  23044. });
  23045. }
  23046. if (fm.zIndex) {
  23047. win.css('z-index', fm.zIndex + 1);
  23048. }
  23049. if (fm.UA.Mobile) {
  23050. navbar.attr('style', navStyle);
  23051. } else {
  23052. navbar.attr('style', navStyle).draggable(full ? 'destroy' : {
  23053. start: function() {
  23054. navdrag = true;
  23055. navmove = true;
  23056. cover.show();
  23057. navShow();
  23058. },
  23059. stop: function() {
  23060. navdrag = false;
  23061. navStyle = self.navbar.attr('style');
  23062. setTimeout(function() {
  23063. navmove = false;
  23064. }, 20);
  23065. }
  23066. });
  23067. }
  23068. $(this).toggleClass(navicon+'-fullscreen-off');
  23069. var collection = win;
  23070. if (parent.is('.ui-resizable')) {
  23071. collection = collection.add(parent);
  23072. }
  23073. collection.resizable(full ? 'enable' : 'disable').removeClass('ui-state-disabled');
  23074. win.trigger('viewchange');
  23075. }
  23076. ),
  23077. updateOnSel = function() {
  23078. self.update(void(0), (function() {
  23079. var fm = self.fm,
  23080. files = fm.selectedFiles(),
  23081. cnt = files.length,
  23082. inDock = self.docked(),
  23083. getInfo = function() {
  23084. var ts = 0;
  23085. $.each(files, function(i, f) {
  23086. var t = parseInt(f.ts);
  23087. if (ts >= 0) {
  23088. if (t > ts) {
  23089. ts = t;
  23090. }
  23091. } else {
  23092. ts = 'unknown';
  23093. }
  23094. });
  23095. return {
  23096. hash : files[0].hash + '/' + (+new Date()),
  23097. name : fm.i18n('items') + ': ' + cnt,
  23098. mime : 'group',
  23099. size : spinner,
  23100. ts : ts,
  23101. files : $.map(files, function(f) { return f.hash; }),
  23102. getSize : true
  23103. };
  23104. };
  23105. if (! cnt) {
  23106. cnt = 1;
  23107. files = [fm.cwd()];
  23108. }
  23109. return (cnt === 1)? files[0] : getInfo();
  23110. })());
  23111. },
  23112. navShow = function() {
  23113. if (self.window.hasClass(fullscreen)) {
  23114. navtm && clearTimeout(navtm);
  23115. navtm = null;
  23116. // if use `show()` it make infinite loop with old jQuery (jQuery/jQuery UI: 1.8.0/1.9.0)
  23117. // see #1478 https://github.com/Studio-42/elFinder/issues/1478
  23118. navbar.stop(true, true).css('display', 'block');
  23119. coverHide();
  23120. }
  23121. },
  23122. coverHide = function() {
  23123. cover.data('tm') && clearTimeout(cover.data('tm'));
  23124. cover.removeData('tm');
  23125. cover.hide();
  23126. },
  23127. prev = $('<div class="'+navicon+' '+navicon+'-prev"/>').on('click touchstart', function(e) { ! navmove && navtrigger(leftKey); return false; }),
  23128. next = $('<div class="'+navicon+' '+navicon+'-next"/>').on('click touchstart', function(e) { ! navmove && navtrigger(rightKey); return false; }),
  23129. navbar = $('<div class="elfinder-quicklook-navbar"/>')
  23130. .append(prev)
  23131. .append(fsicon)
  23132. .append(next)
  23133. .append('<div class="elfinder-quicklook-navbar-separator"/>')
  23134. .append($('<div class="'+navicon+' '+navicon+'-close"/>').on('click touchstart', function(e) { ! navmove && self.window.trigger('close'); return false; }))
  23135. ,
  23136. titleClose = $('<span class="ui-front ui-icon elfinder-icon-close ui-icon-closethick"/>').on('mousedown', function(e) {
  23137. e.stopPropagation();
  23138. self.window.trigger('close');
  23139. }),
  23140. titleDock = $('<span class="ui-front ui-icon elfinder-icon-minimize ui-icon-minusthick"/>').on('mousedown', function(e) {
  23141. e.stopPropagation();
  23142. if (! self.docked()) {
  23143. self.window.trigger('navdockin');
  23144. } else {
  23145. self.window.trigger('navdockout');
  23146. }
  23147. }),
  23148. spinner = '<span class="elfinder-info-spinner"/>' + fm.i18n('calc'),
  23149. navStyle = '',
  23150. init = true,
  23151. dockHeight, getSize, tm4cwd, dockedNode, selectTm;
  23152. this.evUpdate = evUpdate;
  23153. (this.navbar = navbar)._show = navShow;
  23154. this.resize = 'resize.'+fm.namespace;
  23155. this.info = $('<div/>').addClass(infocls)
  23156. .append(icon)
  23157. .append(info);
  23158. this.autoPlay = function() {
  23159. if (self.opened()) {
  23160. return !! self.options[self.docked()? 'dockAutoplay' : 'autoplay'];
  23161. }
  23162. return false;
  23163. };
  23164. this.preview = $('<div class="elfinder-quicklook-preview ui-helper-clearfix"/>')
  23165. // clean info/icon
  23166. .on('change', function() {
  23167. navShow();
  23168. navbar.attr('style', navStyle);
  23169. self.docked() && navbar.hide();
  23170. self.preview.attr('style', '').removeClass('elfinder-overflow-auto');
  23171. self.info.attr('style', '').hide();
  23172. icon.removeAttr('class').attr('style', '');
  23173. info.html('');
  23174. })
  23175. // update info/icon
  23176. .on(evUpdate, function(e) {
  23177. var preview = self.preview,
  23178. file = e.file,
  23179. tpl = '<div class="elfinder-quicklook-info-data">{value}</div>',
  23180. update = function() {
  23181. var win = self.window.css('overflow', 'hidden');
  23182. name = fm.escape(file.i18 || file.name);
  23183. !file.read && e.stopImmediatePropagation();
  23184. self.window.data('hash', file.hash);
  23185. self.preview.off('changesize').trigger('change').children().remove();
  23186. title.html(name);
  23187. prev.css('visibility', '');
  23188. next.css('visibility', '');
  23189. if (file.hash === fm.cwdId2Hash(cwd.find('[id]:not(.elfinder-cwd-parent):first').attr('id'))) {
  23190. prev.css('visibility', 'hidden');
  23191. }
  23192. if (file.hash === fm.cwdId2Hash(cwd.find('[id]:last').attr('id'))) {
  23193. next.css('visibility', 'hidden');
  23194. }
  23195. if (file.mime === 'directory') {
  23196. getSizeHashes = [ file.hash ];
  23197. } else if (file.mime === 'group' && file.getSize) {
  23198. getSizeHashes = file.files;
  23199. }
  23200. info.html(
  23201. tpl.replace(/\{value\}/, name)
  23202. + tpl.replace(/\{value\}/, fm.mime2kind(file))
  23203. + tpl.replace(/\{value\}/, getSizeHashes.length ? spinner : fm.formatSize(file.size))
  23204. + tpl.replace(/\{value\}/, fm.i18n('modify')+': '+ fm.formatDate(file))
  23205. );
  23206. if (getSizeHashes.length) {
  23207. getSize = fm.getSize(getSizeHashes).done(function(data) {
  23208. info.find('span.elfinder-info-spinner').parent().html(data.formated);
  23209. }).fail(function() {
  23210. info.find('span.elfinder-info-spinner').parent().html(fm.i18n('unknown'));
  23211. }).always(function() {
  23212. getSize = null;
  23213. });
  23214. getSize._hash = file.hash;
  23215. }
  23216. icon.addClass('elfinder-cwd-icon ui-corner-all '+fm.mime2class(file.mime));
  23217. if (file.icon) {
  23218. icon.css(fm.getIconStyle(file, true));
  23219. }
  23220. self.info.attr('class', infocls);
  23221. if (file.csscls) {
  23222. self.info.addClass(file.csscls);
  23223. }
  23224. if (file.read && (tmb = fm.tmb(file))) {
  23225. $('<img/>')
  23226. .hide()
  23227. .appendTo(self.preview)
  23228. .on('load', function() {
  23229. icon.addClass(tmb.className).css('background-image', "url('"+tmb.url+"')");
  23230. $(this).remove();
  23231. })
  23232. .attr('src', tmb.url);
  23233. }
  23234. self.info.delay(100).fadeIn(10);
  23235. if (self.window.hasClass(fullscreen)) {
  23236. cover.trigger('mousemove');
  23237. }
  23238. win.css('overflow', '');
  23239. },
  23240. tmb, name, getSizeHashes = [];
  23241. if (file && ! Object.keys(file).length) {
  23242. file = fm.cwd();
  23243. }
  23244. if (file && getSize && getSize.state() === 'pending' && getSize._hash !== file.hash) {
  23245. getSize.reject();
  23246. }
  23247. if (file && (e.forceUpdate || self.window.data('hash') !== file.hash)) {
  23248. update();
  23249. } else {
  23250. e.stopImmediatePropagation();
  23251. }
  23252. });
  23253. this.window = $('<div class="ui-front ui-helper-reset ui-widget elfinder-quicklook touch-punch" style="position:absolute"/>')
  23254. .hide()
  23255. .addClass(fm.UA.Touch? 'elfinder-touch' : '')
  23256. .on('click', function(e) {
  23257. var win = this;
  23258. e.stopPropagation();
  23259. if (state === opened) {
  23260. setTimeout(function() {
  23261. state === opened && fm.toFront(win);
  23262. }, 10);
  23263. }
  23264. })
  23265. .append(
  23266. $('<div class="elfinder-quicklook-titlebar"/>')
  23267. .append(
  23268. title,
  23269. $('<span class="elfinder-quicklook-titlebar-icon'+(platformWin? ' elfinder-platformWin' : '')+'"/>').append(
  23270. titleClose, titleDock
  23271. )
  23272. ),
  23273. this.preview,
  23274. self.info.hide(),
  23275. cover.hide(),
  23276. navbar
  23277. )
  23278. .draggable({handle : 'div.elfinder-quicklook-titlebar'})
  23279. .on('open', function(e, clcss) {
  23280. var win = self.window,
  23281. file = self.value,
  23282. node = fm.getUI('cwd'),
  23283. open = function(status) {
  23284. state = status;
  23285. self.update(1, self.value);
  23286. self.change();
  23287. win.trigger('resize.' + fm.namespace);
  23288. };
  23289. if (!init && state === closed) {
  23290. if (file && file.hash !== cwdHash) {
  23291. node = $('#'+fm.cwdHash2Id(file.hash.split('/', 2)[0]));
  23292. }
  23293. navStyle = '';
  23294. navbar.attr('style', '');
  23295. state = animated;
  23296. node.trigger('scrolltoview');
  23297. coverHide();
  23298. win.css(clcss || closedCss(node))
  23299. .show()
  23300. .animate(openedCss(), 550, function() {
  23301. open(opened);
  23302. navShow();
  23303. });
  23304. fm.toFront(win);
  23305. } else if (state === dockedhidden) {
  23306. fm.getUI('navdock').data('addNode')(dockedNode);
  23307. open(docked);
  23308. self.preview.trigger('changesize');
  23309. fm.storage('previewDocked', '1');
  23310. }
  23311. })
  23312. .on('close', function(e, dfd) {
  23313. var win = self.window,
  23314. preview = self.preview.trigger('change'),
  23315. file = self.value,
  23316. hash = (win.data('hash') || '').split('/', 2)[0],
  23317. close = function(status, winhide) {
  23318. state = status;
  23319. winhide && win.hide();
  23320. preview.children().remove();
  23321. self.update(0, self.value);
  23322. win.data('hash', '');
  23323. dfd && dfd.resolve();
  23324. },
  23325. node;
  23326. if (self.opened()) {
  23327. getSize && getSize.state() === 'pending' && getSize.reject();
  23328. if (! self.docked()) {
  23329. state = animated;
  23330. win.hasClass(fullscreen) && fsicon.click();
  23331. (hash && (node = cwd.find('#'+hash)).length)
  23332. ? win.animate(closedCss(node), 500, function() { close(closed, true); })
  23333. : close(closed, true);
  23334. } else {
  23335. dockedNode = fm.getUI('navdock').data('removeNode')(self.window.attr('id'), 'detach');
  23336. close(dockedhidden);
  23337. fm.storage('previewDocked', '2');
  23338. }
  23339. }
  23340. })
  23341. .on('navdockin', function(e, data) {
  23342. var w = self.window,
  23343. box = fm.getUI('navdock'),
  23344. height = dockHeight || box.width(),
  23345. opts = data || {};
  23346. if (init) {
  23347. opts.init = true;
  23348. }
  23349. state = docked;
  23350. prevStyle = w.attr('style');
  23351. w.toggleClass('ui-front').removeClass('ui-widget').draggable('disable').resizable('disable').removeAttr('style').css({
  23352. width: '100%',
  23353. height: height,
  23354. boxSizing: 'border-box',
  23355. paddingBottom: 0,
  23356. zIndex: 'unset'
  23357. });
  23358. navbar.hide();
  23359. titleDock.toggleClass('ui-icon-plusthick ui-icon-minusthick elfinder-icon-full elfinder-icon-minimize');
  23360. box.data('addNode')(w, opts);
  23361. self.preview.trigger('changesize');
  23362. fm.storage('previewDocked', '1');
  23363. })
  23364. .on('navdockout', function(e) {
  23365. var w = self.window,
  23366. box = fm.getUI('navdock'),
  23367. dfd = $.Deferred(),
  23368. clcss = closedCss(self.preview);
  23369. dockHeight = w.outerHeight();
  23370. box.data('removeNode')(w.attr('id'), fm.getUI());
  23371. w.toggleClass('ui-front').addClass('ui-widget').draggable('enable').resizable('enable').attr('style', prevStyle);
  23372. titleDock.toggleClass('ui-icon-plusthick ui-icon-minusthick elfinder-icon-full elfinder-icon-minimize');
  23373. state = closed;
  23374. w.trigger('open', clcss);
  23375. fm.storage('previewDocked', '0');
  23376. })
  23377. .on('resize.' + fm.namespace, function() {
  23378. self.preview.trigger('changesize');
  23379. });
  23380. /**
  23381. * This command cannot be disable by backend
  23382. *
  23383. * @type Boolean
  23384. **/
  23385. this.alwaysEnabled = true;
  23386. /**
  23387. * Selected file
  23388. *
  23389. * @type Object
  23390. **/
  23391. this.value = null;
  23392. this.handlers = {
  23393. // save selected file
  23394. select : function(e, d) {
  23395. selectTm && clearTimeout(selectTm);
  23396. if (! e.data || ! e.data.selected || ! e.data.selected.length) {
  23397. selectTm = setTimeout(function() {
  23398. self.opened() && updateOnSel();
  23399. }, 0);
  23400. } else {
  23401. self.opened() && updateOnSel();
  23402. }
  23403. },
  23404. error : function() { self.window.is(':visible') && self.window.trigger('close'); },
  23405. 'searchshow searchhide' : function() { this.opened() && this.window.trigger('close'); },
  23406. navbarshow : function() {
  23407. setTimeout(function() {
  23408. self.docked() && self.preview.trigger('changesize');
  23409. }, 0);
  23410. },
  23411. destroy : function() { self.window.remove(); }
  23412. };
  23413. this.shortcuts = [{
  23414. pattern : 'space'
  23415. }];
  23416. this.support = {
  23417. audio : {
  23418. ogg : support('audio/ogg; codecs="vorbis"') || support('audio/ogg; codecs="flac"'),
  23419. mp3 : support('audio/mpeg;'),
  23420. wav : support('audio/wav; codecs="1"'),
  23421. m4a : support('audio/mp4;') || support('audio/x-m4a;') || support('audio/aac;'),
  23422. flac: support('audio/flac;')
  23423. },
  23424. video : {
  23425. ogg : support('video/ogg; codecs="theora"'),
  23426. webm : support('video/webm; codecs="vp8, vorbis"') || support('video/webm; codecs="vp9"'),
  23427. mp4 : support('video/mp4; codecs="avc1.42E01E"') || support('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'),
  23428. m3u8 : support('application/x-mpegURL', 'video') || support('application/vnd.apple.mpegURL', 'video'),
  23429. mpd : support('application/dash+xml', 'video')
  23430. }
  23431. };
  23432. /**
  23433. * Return true if quickLoock window is hiddenReturn true if quickLoock window is visible and not animated
  23434. *
  23435. * @return Boolean
  23436. **/
  23437. this.closed = function() {
  23438. return (state == closed || state == dockedhidden);
  23439. };
  23440. /**
  23441. * Return true if quickLoock window is visible and not animated
  23442. *
  23443. * @return Boolean
  23444. **/
  23445. this.opened = function() {
  23446. return state == opened || state == docked;
  23447. };
  23448. /**
  23449. * Return true if quickLoock window is in NavDock
  23450. *
  23451. * @return Boolean
  23452. **/
  23453. this.docked = function() {
  23454. return state == docked;
  23455. };
  23456. /**
  23457. * Init command.
  23458. * Add default plugins and init other plugins
  23459. *
  23460. * @return Object
  23461. **/
  23462. this.init = function() {
  23463. var o = this.options,
  23464. win = this.window,
  23465. preview = this.preview,
  23466. i, p, cwdDispInlineRegex;
  23467. width = o.width > 0 ? parseInt(o.width) : 450;
  23468. height = o.height > 0 ? parseInt(o.height) : 300;
  23469. if (o.dockHeight !== 'auto') {
  23470. dockHeight = parseInt(o.dockHeight);
  23471. if (! dockHeight) {
  23472. dockHeight = void(0);
  23473. }
  23474. }
  23475. fm.one('load', function() {
  23476. dockEnabled = fm.getUI('navdock').data('dockEnabled');
  23477. ! dockEnabled && titleDock.hide();
  23478. parent = fm.getUI();
  23479. cwd = fm.getUI('cwd');
  23480. if (fm.zIndex) {
  23481. win.css('z-index', fm.zIndex + 1);
  23482. }
  23483. win.appendTo(parent);
  23484. // close window on escape
  23485. $(document).on('keydown.'+fm.namespace, function(e) {
  23486. e.keyCode == $.ui.keyCode.ESCAPE && self.opened() && ! self.docked() && win.trigger('close');
  23487. });
  23488. win.resizable({
  23489. handles : 'se',
  23490. minWidth : 350,
  23491. minHeight : 120,
  23492. resize : function() {
  23493. // use another event to avoid recursion in fullscreen mode
  23494. // may be there is clever solution, but i cant find it :(
  23495. preview.trigger('changesize');
  23496. }
  23497. });
  23498. self.change(function() {
  23499. if (self.opened()) {
  23500. if (self.value) {
  23501. if (self.value.tmb && self.value.tmb == 1) {
  23502. // try re-get file object
  23503. self.value = Object.assign({}, fm.file(self.value.hash));
  23504. }
  23505. preview.trigger($.Event(evUpdate, {file : self.value}));
  23506. }
  23507. }
  23508. });
  23509. preview.on(evUpdate, function(e) {
  23510. var file, hash, serach;
  23511. if (file = e.file) {
  23512. hash = file.hash;
  23513. serach = (fm.searchStatus.mixed && fm.searchStatus.state > 1);
  23514. if (file.mime !== 'directory') {
  23515. if (parseInt(file.size) || file.mime.match(o.mimeRegexNotEmptyCheck)) {
  23516. // set current dispInlineRegex
  23517. self.dispInlineRegex = cwdDispInlineRegex;
  23518. if (serach || fm.optionsByHashes[hash]) {
  23519. try {
  23520. self.dispInlineRegex = new RegExp(fm.option('dispInlineRegex', hash), 'i');
  23521. } catch(e) {
  23522. try {
  23523. self.dispInlineRegex = new RegExp(!fm.isRoot(file)? fm.option('dispInlineRegex', file.phash) : fm.options.dispInlineRegex, 'i');
  23524. } catch(e) {
  23525. self.dispInlineRegex = /^$/;
  23526. }
  23527. }
  23528. }
  23529. } else {
  23530. // do not preview of file that size = 0
  23531. e.stopImmediatePropagation();
  23532. }
  23533. } else {
  23534. self.dispInlineRegex = /^$/;
  23535. }
  23536. self.info.show();
  23537. } else {
  23538. e.stopImmediatePropagation();
  23539. }
  23540. });
  23541. $.each(fm.commands.quicklook.plugins || [], function(i, plugin) {
  23542. if (typeof(plugin) == 'function') {
  23543. new plugin(self);
  23544. }
  23545. });
  23546. }).one('open', function() {
  23547. var dock = Number(fm.storage('previewDocked') || o.docked),
  23548. win;
  23549. if (dockEnabled && dock >= 1) {
  23550. win = self.window;
  23551. self.exec();
  23552. win.trigger('navdockin', { init : true });
  23553. if (dock === 2) {
  23554. win.trigger('close');
  23555. } else {
  23556. self.update(void(0), fm.cwd());
  23557. self.change();
  23558. }
  23559. }
  23560. init = false;
  23561. }).bind('open', function() {
  23562. cwdHash = fm.cwd().hash;
  23563. self.value = fm.cwd();
  23564. // set current volume dispInlineRegex
  23565. try {
  23566. cwdDispInlineRegex = new RegExp(fm.option('dispInlineRegex'), 'i');
  23567. } catch(e) {
  23568. cwdDispInlineRegex = /^$/;
  23569. }
  23570. }).bind('change', function(e) {
  23571. if (e.data && e.data.changed && self.opened()) {
  23572. $.each(e.data.changed, function() {
  23573. if (self.window.data('hash') === this.hash) {
  23574. self.window.data('hash', null);
  23575. self.preview.trigger(evUpdate);
  23576. return false;
  23577. }
  23578. });
  23579. }
  23580. }).bind('navdockresizestart navdockresizestop', function(e) {
  23581. cover[e.type === 'navdockresizestart'? 'show' : 'hide']();
  23582. });
  23583. };
  23584. this.getstate = function() {
  23585. return self.opened()? 1 : 0;
  23586. };
  23587. this.exec = function() {
  23588. self.closed() && updateOnSel();
  23589. self.enabled() && self.window.trigger(self.opened() ? 'close' : 'open');
  23590. return $.Deferred().resolve();
  23591. };
  23592. this.hideinfo = function() {
  23593. this.info.stop(true, true).hide();
  23594. };
  23595. }).prototype = { forceLoad : true }; // this is required command
  23596. /*
  23597. * File: /js/commands/quicklook.plugins.js
  23598. */
  23599. elFinder.prototype.commands.quicklook.plugins = [
  23600. /**
  23601. * Images preview plugin
  23602. *
  23603. * @param elFinder.commands.quicklook
  23604. **/
  23605. function(ql) {
  23606. var mimes = ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/x-ms-bmp'],
  23607. preview = ql.preview,
  23608. WebP, flipMime;
  23609. // webp support
  23610. WebP = new Image();
  23611. WebP.onload = WebP.onerror = function() {
  23612. if (WebP.height == 2) {
  23613. mimes.push('image/webp');
  23614. }
  23615. };
  23616. WebP.src='data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
  23617. // what kind of images we can display
  23618. $.each(navigator.mimeTypes, function(i, o) {
  23619. var mime = o.type;
  23620. if (mime.indexOf('image/') === 0 && $.inArray(mime, mimes)) {
  23621. mimes.push(mime);
  23622. }
  23623. });
  23624. preview.on(ql.evUpdate, function(e) {
  23625. var fm = ql.fm,
  23626. file = e.file,
  23627. showed = false,
  23628. dimreq = null,
  23629. setdim = function(dim) {
  23630. var rfile = fm.file(file.hash);
  23631. rfile.width = dim[0];
  23632. rfile.height = dim[1];
  23633. },
  23634. show = function() {
  23635. var elm, varelm, memSize, width, height, prop;
  23636. dimreq && dimreq.state && dimreq.state() === 'pending' && dimreq.reject();
  23637. if (showed) {
  23638. return;
  23639. }
  23640. showed = true;
  23641. elm = img.get(0);
  23642. memSize = file.width && file.height? {w: file.width, h: file.height} : (elm.naturalWidth? null : {w: img.width(), h: img.height()});
  23643. memSize && img.removeAttr('width').removeAttr('height');
  23644. width = file.width || elm.naturalWidth || elm.width || img.width();
  23645. height = file.height || elm.naturalHeight || elm.height || img.height();
  23646. if (!file.width || !file.height) {
  23647. setdim([width, height]);
  23648. }
  23649. memSize && img.width(memSize.w).height(memSize.h);
  23650. prop = (width/height).toFixed(2);
  23651. preview.on('changesize', function() {
  23652. var pw = parseInt(preview.width()),
  23653. ph = parseInt(preview.height()),
  23654. w, h;
  23655. if (prop < (pw/ph).toFixed(2)) {
  23656. h = ph;
  23657. w = Math.floor(h * prop);
  23658. } else {
  23659. w = pw;
  23660. h = Math.floor(w/prop);
  23661. }
  23662. img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0);
  23663. })
  23664. .trigger('changesize');
  23665. //show image
  23666. img.fadeIn(100);
  23667. },
  23668. hideInfo = function() {
  23669. loading.remove();
  23670. // hide info/icon
  23671. ql.hideinfo();
  23672. },
  23673. url, img, loading, m;
  23674. if (!flipMime) {
  23675. flipMime = fm.arrayFlip(mimes);
  23676. }
  23677. if (flipMime[file.mime] && ql.dispInlineRegex.test(file.mime)) {
  23678. // this is our file - stop event propagation
  23679. e.stopImmediatePropagation();
  23680. loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  23681. url = fm.openUrl(file.hash);
  23682. img = $('<img/>')
  23683. .hide()
  23684. .appendTo(preview)
  23685. .on('load', function() {
  23686. hideInfo();
  23687. show();
  23688. })
  23689. .on('error', function() {
  23690. loading.remove();
  23691. })
  23692. .attr('src', url);
  23693. if (file.width && file.height) {
  23694. show();
  23695. } else if (file.size > (ql.options.getDimThreshold || 0)) {
  23696. dimreq = fm.request({
  23697. data : {cmd : 'dim', target : file.hash},
  23698. preventDefault : true
  23699. })
  23700. .done(function(data) {
  23701. if (data.dim) {
  23702. var dim = data.dim.split('x');
  23703. file.width = dim[0];
  23704. file.height = dim[1];
  23705. setdim(dim);
  23706. show();
  23707. }
  23708. });
  23709. }
  23710. }
  23711. });
  23712. },
  23713. /**
  23714. * PSD(Adobe Photoshop data) preview plugin
  23715. *
  23716. * @param elFinder.commands.quicklook
  23717. **/
  23718. function(ql) {
  23719. var fm = ql.fm,
  23720. mimes = fm.arrayFlip(['image/vnd.adobe.photoshop', 'image/x-photoshop']),
  23721. preview = ql.preview,
  23722. load = function(url, img, loading) {
  23723. try {
  23724. fm.replaceXhrSend();
  23725. PSD.fromURL(url).then(function(psd) {
  23726. var prop;
  23727. img.attr('src', psd.image.toBase64());
  23728. setTimeout(function() {
  23729. prop = (img.width()/img.height()).toFixed(2);
  23730. preview.on('changesize', function() {
  23731. var pw = parseInt(preview.width()),
  23732. ph = parseInt(preview.height()),
  23733. w, h;
  23734. if (prop < (pw/ph).toFixed(2)) {
  23735. h = ph;
  23736. w = Math.floor(h * prop);
  23737. } else {
  23738. w = pw;
  23739. h = Math.floor(w/prop);
  23740. }
  23741. img.width(w).height(h).css('margin-top', h < ph ? Math.floor((ph - h)/2) : 0);
  23742. }).trigger('changesize');
  23743. loading.remove();
  23744. // hide info/icon
  23745. ql.hideinfo();
  23746. //show image
  23747. img.fadeIn(100);
  23748. }, 1);
  23749. }, function() {
  23750. loading.remove();
  23751. img.remove();
  23752. });
  23753. fm.restoreXhrSend();
  23754. } catch(e) {
  23755. fm.restoreXhrSend();
  23756. loading.remove();
  23757. img.remove();
  23758. }
  23759. },
  23760. PSD;
  23761. preview.on(ql.evUpdate, function(e) {
  23762. var file = e.file,
  23763. url, img, loading, m,
  23764. _define, _require;
  23765. if (mimes[file.mime] && fm.options.cdns.psd && ! fm.UA.ltIE10 && ql.dispInlineRegex.test(file.mime)) {
  23766. // this is our file - stop event propagation
  23767. e.stopImmediatePropagation();
  23768. loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  23769. url = fm.openUrl(file.hash);
  23770. if (!fm.isSameOrigin(url)) {
  23771. url = fm.openUrl(file.hash, true);
  23772. }
  23773. img = $('<img/>').hide().appendTo(preview);
  23774. if (PSD) {
  23775. load(url, img, loading);
  23776. } else {
  23777. _define = window.define;
  23778. _require = window.require;
  23779. window.require = null;
  23780. window.define = null;
  23781. fm.loadScript(
  23782. [ fm.options.cdns.psd ],
  23783. function() {
  23784. PSD = require('psd');
  23785. _define? (window.define = _define) : (delete window.define);
  23786. _require? (window.require = _require) : (delete window.require);
  23787. load(url, img, loading);
  23788. }
  23789. );
  23790. }
  23791. }
  23792. });
  23793. },
  23794. /**
  23795. * HTML preview plugin
  23796. *
  23797. * @param elFinder.commands.quicklook
  23798. **/
  23799. function(ql) {
  23800. var fm = ql.fm,
  23801. mimes = fm.arrayFlip(['text/html', 'application/xhtml+xml']),
  23802. preview = ql.preview;
  23803. preview.on(ql.evUpdate, function(e) {
  23804. var file = e.file, jqxhr, loading;
  23805. if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) {
  23806. e.stopImmediatePropagation();
  23807. loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  23808. // stop loading on change file if not loaded yet
  23809. preview.one('change', function() {
  23810. jqxhr.state() == 'pending' && jqxhr.reject();
  23811. }).addClass('elfinder-overflow-auto');
  23812. jqxhr = fm.request({
  23813. data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts},
  23814. options : {type: 'get', cache : true},
  23815. preventDefault : true
  23816. })
  23817. .done(function(data) {
  23818. ql.hideinfo();
  23819. var doc = $('<iframe class="elfinder-quicklook-preview-html"/>').appendTo(preview)[0].contentWindow.document;
  23820. doc.open();
  23821. doc.write(data.content);
  23822. doc.close();
  23823. })
  23824. .always(function() {
  23825. loading.remove();
  23826. });
  23827. }
  23828. });
  23829. },
  23830. /**
  23831. * MarkDown preview plugin
  23832. *
  23833. * @param elFinder.commands.quicklook
  23834. **/
  23835. function(ql) {
  23836. var fm = ql.fm,
  23837. mimes = fm.arrayFlip(['text/x-markdown']),
  23838. preview = ql.preview,
  23839. marked = null,
  23840. show = function(data, loading) {
  23841. ql.hideinfo();
  23842. var doc = $('<iframe class="elfinder-quicklook-preview-html"/>').appendTo(preview)[0].contentWindow.document;
  23843. doc.open();
  23844. doc.write(marked(data.content));
  23845. doc.close();
  23846. loading.remove();
  23847. },
  23848. error = function(loading) {
  23849. marked = false;
  23850. loading.remove();
  23851. };
  23852. preview.on(ql.evUpdate, function(e) {
  23853. var file = e.file, jqxhr, loading;
  23854. if (mimes[file.mime] && fm.options.cdns.marked && marked !== false && ql.dispInlineRegex.test(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) {
  23855. e.stopImmediatePropagation();
  23856. loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  23857. // stop loading on change file if not loaded yet
  23858. preview.one('change', function() {
  23859. jqxhr.state() == 'pending' && jqxhr.reject();
  23860. }).addClass('elfinder-overflow-auto');
  23861. jqxhr = fm.request({
  23862. data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts},
  23863. options : {type: 'get', cache : true},
  23864. preventDefault : true
  23865. })
  23866. .done(function(data) {
  23867. if (marked || window.marked) {
  23868. if (!marked) {
  23869. marked = window.marked;
  23870. }
  23871. show(data, loading);
  23872. } else {
  23873. fm.loadScript([fm.options.cdns.marked],
  23874. function(res) {
  23875. marked = res || window.marked || false;
  23876. delete window.marked;
  23877. if (marked) {
  23878. show(data, loading);
  23879. } else {
  23880. error(loading);
  23881. }
  23882. },
  23883. {
  23884. tryRequire: true,
  23885. error: function() {
  23886. error(loading);
  23887. }
  23888. }
  23889. );
  23890. }
  23891. })
  23892. .fail(function() {
  23893. error(loading);
  23894. });
  23895. }
  23896. });
  23897. },
  23898. /**
  23899. * Texts preview plugin
  23900. *
  23901. * @param elFinder.commands.quicklook
  23902. **/
  23903. function(ql) {
  23904. var fm = ql.fm,
  23905. preview = ql.preview,
  23906. textMaxlen = parseInt(ql.options.textMaxlen) || 2000,
  23907. prettify = function() {
  23908. if (fm.options.cdns.prettify) {
  23909. fm.loadScript([fm.options.cdns.prettify + (fm.options.cdns.prettify.match(/\?/)? '&' : '?') + 'autorun=false']);
  23910. prettify = function() { return true; };
  23911. } else {
  23912. prettify = function() { return false; };
  23913. }
  23914. },
  23915. PRcheck = function(node, cnt) {
  23916. if (prettify()) {
  23917. if (typeof window.PR === 'undefined' && cnt--) {
  23918. setTimeout(function() { PRcheck(node, cnt); }, 100);
  23919. } else {
  23920. if (typeof window.PR === 'object') {
  23921. node.css('cursor', 'wait');
  23922. setTimeout(function() {
  23923. PR.prettyPrint && PR.prettyPrint(null, node.get(0));
  23924. node.css('cursor', '');
  23925. }, 0);
  23926. } else {
  23927. prettify = function() { return false; };
  23928. }
  23929. }
  23930. }
  23931. };
  23932. preview.on(ql.evUpdate, function(e) {
  23933. var file = e.file,
  23934. mime = file.mime,
  23935. jqxhr, loading;
  23936. if (fm.mimeIsText(file.mime) && (!ql.options.getSizeMax || file.size <= ql.options.getSizeMax)) {
  23937. e.stopImmediatePropagation();
  23938. (typeof window.PR === 'undefined') && prettify();
  23939. loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  23940. // stop loading on change file if not loadin yet
  23941. preview.one('change', function() {
  23942. jqxhr.state() == 'pending' && jqxhr.reject();
  23943. });
  23944. jqxhr = fm.request({
  23945. data : {cmd : 'get', target : file.hash, conv : 1, _t : file.ts},
  23946. options : {type: 'get', cache : true},
  23947. preventDefault : true
  23948. })
  23949. .done(function(data) {
  23950. var reg = new RegExp('^(data:'+file.mime.replace(/([.+])/g, '\\$1')+';base64,)', 'i'),
  23951. text = data.content,
  23952. part, more, node, m;
  23953. ql.hideinfo();
  23954. if (window.atob && (m = text.match(reg))) {
  23955. text = atob(text.substr(m[1].length));
  23956. }
  23957. more = text.length - textMaxlen;
  23958. if (more > 100) {
  23959. part = text.substr(0, textMaxlen) + '...';
  23960. } else {
  23961. more = 0;
  23962. }
  23963. node = $('<div class="elfinder-quicklook-preview-text-wrapper"><pre class="elfinder-quicklook-preview-text prettyprint"></pre></div>');
  23964. if (more) {
  23965. node.append($('<div class="elfinder-quicklook-preview-charsleft"><hr/><span>' + fm.i18n('charsLeft', fm.toLocaleString(more)) + '</span></div>')
  23966. .on('click', function() {
  23967. var top = node.scrollTop();
  23968. $(this).remove();
  23969. node.children('pre').removeClass('prettyprinted').text(text).scrollTop(top);
  23970. PRcheck(node, 100);
  23971. })
  23972. );
  23973. }
  23974. node.children('pre').text(part || text);
  23975. node.on('touchstart', function(e) {
  23976. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  23977. e.originalEvent._preventSwipeX = true;
  23978. }
  23979. }).appendTo(preview);
  23980. PRcheck(node, 100);
  23981. })
  23982. .always(function() {
  23983. loading.remove();
  23984. });
  23985. }
  23986. });
  23987. },
  23988. /**
  23989. * PDF preview plugin
  23990. *
  23991. * @param elFinder.commands.quicklook
  23992. **/
  23993. function(ql) {
  23994. var fm = ql.fm,
  23995. mime = 'application/pdf',
  23996. preview = ql.preview,
  23997. active = false;
  23998. if ((fm.UA.Safari && fm.OS === 'mac' && !fm.UA.iOS) || fm.UA.IE) {
  23999. active = true;
  24000. } else {
  24001. $.each(navigator.plugins, function(i, plugins) {
  24002. $.each(plugins, function(i, plugin) {
  24003. if (plugin.type === mime) {
  24004. return !(active = true);
  24005. }
  24006. });
  24007. });
  24008. }
  24009. active && preview.on(ql.evUpdate, function(e) {
  24010. var file = e.file, node;
  24011. if (file.mime === mime && ql.dispInlineRegex.test(file.mime)) {
  24012. e.stopImmediatePropagation();
  24013. ql.hideinfo();
  24014. node = $('<object class="elfinder-quicklook-preview-pdf" data="'+fm.openUrl(file.hash)+'" type="application/pdf" />')
  24015. .appendTo(preview);
  24016. }
  24017. });
  24018. },
  24019. /**
  24020. * Flash preview plugin
  24021. *
  24022. * @param elFinder.commands.quicklook
  24023. **/
  24024. function(ql) {
  24025. var fm = ql.fm,
  24026. mime = 'application/x-shockwave-flash',
  24027. preview = ql.preview,
  24028. active = false;
  24029. $.each(navigator.plugins, function(i, plugins) {
  24030. $.each(plugins, function(i, plugin) {
  24031. if (plugin.type === mime) {
  24032. return !(active = true);
  24033. }
  24034. });
  24035. });
  24036. active && preview.on(ql.evUpdate, function(e) {
  24037. var file = e.file,
  24038. node;
  24039. if (file.mime === mime && ql.dispInlineRegex.test(file.mime)) {
  24040. e.stopImmediatePropagation();
  24041. ql.hideinfo();
  24042. 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" />')
  24043. .appendTo(preview);
  24044. }
  24045. });
  24046. },
  24047. /**
  24048. * HTML5 audio preview plugin
  24049. *
  24050. * @param elFinder.commands.quicklook
  24051. **/
  24052. function(ql) {
  24053. var preview = ql.preview,
  24054. mimes = {
  24055. 'audio/mpeg' : 'mp3',
  24056. 'audio/mpeg3' : 'mp3',
  24057. 'audio/mp3' : 'mp3',
  24058. 'audio/x-mpeg3' : 'mp3',
  24059. 'audio/x-mp3' : 'mp3',
  24060. 'audio/x-wav' : 'wav',
  24061. 'audio/wav' : 'wav',
  24062. 'audio/x-m4a' : 'm4a',
  24063. 'audio/aac' : 'm4a',
  24064. 'audio/mp4' : 'm4a',
  24065. 'audio/x-mp4' : 'm4a',
  24066. 'audio/ogg' : 'ogg',
  24067. 'audio/flac' : 'flac',
  24068. 'audio/x-flac' : 'flac'
  24069. },
  24070. node,
  24071. win = ql.window,
  24072. navi = ql.navbar;
  24073. preview.on(ql.evUpdate, function(e) {
  24074. var file = e.file,
  24075. type = mimes[file.mime],
  24076. autoplay = ql.autoPlay(),
  24077. setNavi = function() {
  24078. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  24079. };
  24080. if (ql.dispInlineRegex.test(file.mime) && ql.support.audio[type]) {
  24081. e.stopImmediatePropagation();
  24082. node = $('<audio class="elfinder-quicklook-preview-audio" controls preload="auto" autobuffer><source src="'+ql.fm.openUrl(file.hash)+'" /></audio>')
  24083. .on('change', function(e) {
  24084. // Firefox fire change event on seek or volume change
  24085. e.stopPropagation();
  24086. })
  24087. .appendTo(preview);
  24088. autoplay && node[0].play();
  24089. win.on('viewchange.audio', setNavi);
  24090. setNavi();
  24091. }
  24092. }).on('change', function() {
  24093. if (node && node.parent().length) {
  24094. var elm = node[0];
  24095. win.off('viewchange.audio');
  24096. try {
  24097. elm.pause();
  24098. elm.src = '';
  24099. elm.load();
  24100. } catch(e) {}
  24101. node.remove();
  24102. node= null;
  24103. }
  24104. });
  24105. },
  24106. /**
  24107. * HTML5 video preview plugin
  24108. *
  24109. * @param elFinder.commands.quicklook
  24110. **/
  24111. function(ql) {
  24112. var fm = ql.fm,
  24113. preview = ql.preview,
  24114. mimes = {
  24115. 'video/mp4' : 'mp4',
  24116. 'video/x-m4v' : 'mp4',
  24117. 'video/quicktime' : 'mp4',
  24118. 'video/ogg' : 'ogg',
  24119. 'application/ogg' : 'ogg',
  24120. 'video/webm' : 'webm',
  24121. 'application/vnd.apple.mpegurl' : 'm3u8',
  24122. 'application/x-mpegurl' : 'm3u8',
  24123. 'application/dash+xml' : 'mpd'
  24124. },
  24125. node,
  24126. win = ql.window,
  24127. navi = ql.navbar,
  24128. cHls, cDash;
  24129. preview.on(ql.evUpdate, function(e) {
  24130. var file = e.file,
  24131. autoplay = ql.autoPlay(),
  24132. type = mimes[file.mime.toLowerCase()],
  24133. setNavi = function() {
  24134. if (fm.UA.iOS) {
  24135. if (win.hasClass('elfinder-quicklook-fullscreen')) {
  24136. preview.css('height', '-webkit-calc(100% - 50px)');
  24137. navi._show();
  24138. } else {
  24139. preview.css('height', '');
  24140. }
  24141. } else {
  24142. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  24143. }
  24144. },
  24145. render = function(opts) {
  24146. opts = opts || {};
  24147. ql.hideinfo();
  24148. node = $('<video class="elfinder-quicklook-preview-video" controls preload="auto" autobuffer playsinline>'
  24149. +'</video>')
  24150. .on('change', function(e) {
  24151. // Firefox fire change event on seek or volume change
  24152. e.stopPropagation();
  24153. });
  24154. if (opts.src) {
  24155. node.append('<source src="'+opts.src+'" type="'+file.mime+'"/><source src="'+opts.src+'"/>');
  24156. }
  24157. node.appendTo(preview);
  24158. win.on('viewchange.video', setNavi);
  24159. setNavi();
  24160. },
  24161. loadHls = function() {
  24162. var hls;
  24163. render();
  24164. hls = new cHls();
  24165. hls.loadSource(fm.openUrl(file.hash));
  24166. hls.attachMedia(node[0]);
  24167. if (autoplay) {
  24168. hls.on(cHls.Events.MANIFEST_PARSED,function() {
  24169. node[0].play();
  24170. });
  24171. }
  24172. },
  24173. loadDash = function() {
  24174. var player;
  24175. render();
  24176. player = cDash.MediaPlayer().create();
  24177. player.initialize(node[0], fm.openUrl(file.hash), autoplay);
  24178. };
  24179. if (ql.dispInlineRegex.test(file.mime) && (((type === 'm3u8' || type === 'mpd') && !fm.UA.ltIE10) || ql.support.video[type])) {
  24180. if (ql.support.video[type] && (type !== 'm3u8' || fm.UA.Safari)) {
  24181. e.stopImmediatePropagation();
  24182. render({ src: fm.openUrl(file.hash) });
  24183. autoplay && node[0].play();
  24184. } else {
  24185. if (fm.options.cdns.hls && type === 'm3u8') {
  24186. e.stopImmediatePropagation();
  24187. if (cHls) {
  24188. loadHls();
  24189. } else {
  24190. fm.loadScript(
  24191. [ fm.options.cdns.hls ],
  24192. function(res) {
  24193. cHls = res || window.Hls;
  24194. loadHls();
  24195. },
  24196. {tryRequire: true}
  24197. );
  24198. }
  24199. } else if (fm.options.cdns.dash && type === 'mpd') {
  24200. e.stopImmediatePropagation();
  24201. if (cDash) {
  24202. loadDash();
  24203. } else {
  24204. fm.loadScript(
  24205. [ fm.options.cdns.dash ],
  24206. function() {
  24207. cDash = window.dashjs;
  24208. loadDash();
  24209. },
  24210. {tryRequire: true}
  24211. );
  24212. }
  24213. }
  24214. }
  24215. }
  24216. }).on('change', function() {
  24217. if (node && node.parent().length) {
  24218. var elm = node[0];
  24219. win.off('viewchange.video');
  24220. try {
  24221. elm.pause();
  24222. elm.src = '';
  24223. elm.load();
  24224. } catch(e) {}
  24225. node.remove();
  24226. node= null;
  24227. }
  24228. });
  24229. },
  24230. /**
  24231. * Audio/video preview plugin using browser plugins
  24232. *
  24233. * @param elFinder.commands.quicklook
  24234. **/
  24235. function(ql) {
  24236. var preview = ql.preview,
  24237. mimes = [],
  24238. node,
  24239. win = ql.window,
  24240. navi = ql.navbar;
  24241. $.each(navigator.plugins, function(i, plugins) {
  24242. $.each(plugins, function(i, plugin) {
  24243. (plugin.type.indexOf('audio/') === 0 || plugin.type.indexOf('video/') === 0) && mimes.push(plugin.type);
  24244. });
  24245. });
  24246. mimes = ql.fm.arrayFlip(mimes);
  24247. preview.on(ql.evUpdate, function(e) {
  24248. var file = e.file,
  24249. mime = file.mime,
  24250. video,
  24251. setNavi = function() {
  24252. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '50px' : '');
  24253. };
  24254. if (mimes[file.mime] && ql.dispInlineRegex.test(file.mime)) {
  24255. e.stopImmediatePropagation();
  24256. (video = mime.indexOf('video/') === 0) && ql.hideinfo();
  24257. node = $('<embed src="'+ql.fm.openUrl(file.hash)+'" type="'+mime+'" class="elfinder-quicklook-preview-'+(video ? 'video' : 'audio')+'"/>')
  24258. .appendTo(preview);
  24259. win.on('viewchange.embed', setNavi);
  24260. setNavi();
  24261. }
  24262. }).on('change', function() {
  24263. if (node && node.parent().length) {
  24264. win.off('viewchange.embed');
  24265. node.remove();
  24266. node= null;
  24267. }
  24268. });
  24269. },
  24270. /**
  24271. * Archive(zip|gzip|tar) preview plugin using https://github.com/imaya/zlib.js
  24272. *
  24273. * @param elFinder.commands.quicklook
  24274. **/
  24275. function(ql) {
  24276. var fm = ql.fm,
  24277. mimes = fm.arrayFlip(['application/zip', 'application/x-gzip', 'application/x-tar']),
  24278. preview = ql.preview,
  24279. unzipFiles = function() {
  24280. /** @type {Array.<string>} */
  24281. var filenameList = [];
  24282. /** @type {number} */
  24283. var i;
  24284. /** @type {number} */
  24285. var il;
  24286. /** @type {Array.<Zlib.Unzip.FileHeader>} */
  24287. var fileHeaderList;
  24288. // need check this.Y when update cdns.zlibUnzip
  24289. this.Y();
  24290. fileHeaderList = this.i;
  24291. for (i = 0, il = fileHeaderList.length; i < il; ++i) {
  24292. // need check fileHeaderList[i].J when update cdns.zlibUnzip
  24293. filenameList[i] = fileHeaderList[i].filename + (fileHeaderList[i].J? ' (' + fm.formatSize(fileHeaderList[i].J) + ')' : '');
  24294. }
  24295. return filenameList;
  24296. },
  24297. tarFiles = function(tar) {
  24298. var filenames = [],
  24299. tarlen = tar.length,
  24300. offset = 0,
  24301. toStr = function(arr) {
  24302. return String.fromCharCode.apply(null, arr).replace(/\0+$/, '');
  24303. },
  24304. h, name, prefix, size, dbs;
  24305. while (offset < tarlen && tar[offset] !== 0) {
  24306. h = tar.subarray(offset, offset + 512);
  24307. name = toStr(h.subarray(0, 100));
  24308. if (prefix = toStr(h.subarray(345, 500))) {
  24309. name = prefix + name;
  24310. }
  24311. size = parseInt(toStr(h.subarray(124, 136)), 8);
  24312. dbs = Math.ceil(size / 512) * 512;
  24313. if (name === '././@LongLink') {
  24314. name = toStr(tar.subarray(offset + 512, offset + 512 + dbs));
  24315. }
  24316. (name !== 'pax_global_header') && filenames.push(name + (size? ' (' + fm.formatSize(size) + ')': ''));
  24317. offset = offset + 512 + dbs;
  24318. }
  24319. return filenames;
  24320. },
  24321. Zlib;
  24322. if (window.Uint8Array && window.DataView && fm.options.cdns.zlibUnzip && fm.options.cdns.zlibGunzip) {
  24323. preview.on(ql.evUpdate, function(e) {
  24324. var file = e.file,
  24325. isTar = (file.mime === 'application/x-tar');
  24326. if (mimes[file.mime] && (
  24327. isTar
  24328. || ((typeof Zlib === 'undefined' || Zlib) && (file.mime === 'application/zip' || file.mime === 'application/x-gzip'))
  24329. )) {
  24330. var jqxhr, loading, url,
  24331. req = function() {
  24332. url = fm.openUrl(file.hash);
  24333. if (!fm.isSameOrigin(url)) {
  24334. url = fm.openUrl(file.hash, true);
  24335. }
  24336. jqxhr = fm.request({
  24337. data : {cmd : 'get'},
  24338. options : {
  24339. url: url,
  24340. type: 'get',
  24341. cache : true,
  24342. dataType : 'binary',
  24343. responseType :'arraybuffer',
  24344. processData: false
  24345. }
  24346. })
  24347. .fail(function() {
  24348. loading.remove();
  24349. })
  24350. .done(function(data) {
  24351. var unzip, filenames;
  24352. try {
  24353. if (file.mime === 'application/zip') {
  24354. unzip = new Zlib.Unzip(new Uint8Array(data));
  24355. //filenames = unzip.getFilenames();
  24356. filenames = unzipFiles.call(unzip);
  24357. } else if (file.mime === 'application/x-gzip') {
  24358. unzip = new Zlib.Gunzip(new Uint8Array(data));
  24359. filenames = tarFiles(unzip.decompress());
  24360. } else if (file.mime === 'application/x-tar') {
  24361. filenames = tarFiles(new Uint8Array(data));
  24362. }
  24363. makeList(filenames);
  24364. } catch (e) {
  24365. loading.remove();
  24366. fm.debug('error', e);
  24367. }
  24368. });
  24369. },
  24370. makeList = function(filenames) {
  24371. var header, doc;
  24372. if (filenames && filenames.length) {
  24373. filenames = $.map(filenames, function(str) {
  24374. return fm.decodeRawString(str);
  24375. });
  24376. filenames.sort();
  24377. loading.remove();
  24378. header = '<strong>'+fm.escape(file.mime)+'</strong> ('+fm.formatSize(file.size)+')'+'<hr/>';
  24379. doc = $('<div class="elfinder-quicklook-preview-archive-wrapper">'+header+'<pre class="elfinder-quicklook-preview-text">'+fm.escape(filenames.join("\n"))+'</pre></div>')
  24380. .on('touchstart', function(e) {
  24381. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  24382. e.originalEvent._preventSwipeX = true;
  24383. }
  24384. })
  24385. .appendTo(preview);
  24386. ql.hideinfo();
  24387. }
  24388. },
  24389. _Zlib;
  24390. // this is our file - stop event propagation
  24391. e.stopImmediatePropagation();
  24392. loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  24393. // stop loading on change file if not loaded yet
  24394. preview.one('change', function() {
  24395. jqxhr.state() === 'pending' && jqxhr.reject();
  24396. loading.remove();
  24397. });
  24398. if (Zlib) {
  24399. req();
  24400. } else {
  24401. if (window.Zlib) {
  24402. _Zlib = window.Zlib;
  24403. delete window.Zlib;
  24404. }
  24405. fm.loadScript(
  24406. [ fm.options.cdns.zlibUnzip, fm.options.cdns.zlibGunzip ],
  24407. function() {
  24408. if (window.Zlib && (Zlib = window.Zlib)) {
  24409. if (_Zlib) {
  24410. window.Zlib = _Zlib;
  24411. } else {
  24412. delete window.Zlib;
  24413. }
  24414. req();
  24415. } else {
  24416. error();
  24417. }
  24418. }
  24419. );
  24420. }
  24421. }
  24422. });
  24423. }
  24424. },
  24425. /**
  24426. * RAR Archive preview plugin using https://github.com/43081j/rar.js
  24427. *
  24428. * @param elFinder.commands.quicklook
  24429. **/
  24430. function(ql) {
  24431. var fm = ql.fm,
  24432. mimes = fm.arrayFlip(['application/x-rar']),
  24433. preview = ql.preview,
  24434. RAR;
  24435. if (window.DataView) {
  24436. preview.on(ql.evUpdate, function(e) {
  24437. var file = e.file;
  24438. if (mimes[file.mime] && fm.options.cdns.rar && RAR !== false) {
  24439. var loading, url, archive, abort,
  24440. getList = function(url) {
  24441. if (abort) {
  24442. loading.remove();
  24443. return;
  24444. }
  24445. try {
  24446. archive = RAR({
  24447. file: url,
  24448. type: 2,
  24449. xhrHeaders: fm.customHeaders,
  24450. xhrFields: fm.xhrFields
  24451. }, function(err) {
  24452. loading.remove();
  24453. var filenames = [],
  24454. header, doc;
  24455. if (abort || err) {
  24456. // An error occurred (not a rar, read error, etc)
  24457. err && fm.debug('error', err);
  24458. return;
  24459. }
  24460. $.each(archive.entries, function() {
  24461. filenames.push(this.path + (this.size? ' (' + fm.formatSize(this.size) + ')' : ''));
  24462. });
  24463. if (filenames.length) {
  24464. filenames = $.map(filenames, function(str) {
  24465. return fm.decodeRawString(str);
  24466. });
  24467. filenames.sort();
  24468. header = '<strong>'+fm.escape(file.mime)+'</strong> ('+fm.formatSize(file.size)+')'+'<hr/>';
  24469. doc = $('<div class="elfinder-quicklook-preview-archive-wrapper">'+header+'<pre class="elfinder-quicklook-preview-text">'+fm.escape(filenames.join("\n"))+'</pre></div>')
  24470. .on('touchstart', function(e) {
  24471. if ($(this)['scroll' + (fm.direction === 'ltr'? 'Right' : 'Left')]() > 5) {
  24472. e.originalEvent._preventSwipeX = true;
  24473. }
  24474. })
  24475. .appendTo(preview);
  24476. ql.hideinfo();
  24477. }
  24478. });
  24479. } catch(e) {
  24480. loading.remove();
  24481. }
  24482. },
  24483. error = function() {
  24484. RAR = false;
  24485. loading.remove();
  24486. },
  24487. _RAR;
  24488. // this is our file - stop event propagation
  24489. e.stopImmediatePropagation();
  24490. loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  24491. // stop loading on change file if not loaded yet
  24492. preview.one('change', function() {
  24493. archive && (archive.abort = true);
  24494. loading.remove();
  24495. abort = true;
  24496. });
  24497. url = fm.openUrl(file.hash);
  24498. if (!fm.isSameOrigin(url)) {
  24499. url = fm.openUrl(file.hash, true);
  24500. }
  24501. if (RAR) {
  24502. getList(url);
  24503. } else {
  24504. if (window.RarArchive) {
  24505. _RAR = window.RarArchive;
  24506. delete window.RarArchive;
  24507. }
  24508. fm.loadScript(
  24509. [ fm.options.cdns.rar ],
  24510. function() {
  24511. if (fm.hasRequire) {
  24512. require(['rar'], function(RarArchive) {
  24513. RAR = RarArchive;
  24514. getList(url);
  24515. }, error);
  24516. } else {
  24517. if (RAR = window.RarArchive) {
  24518. if (_RAR) {
  24519. window.RarArchive = _RAR;
  24520. } else {
  24521. delete window.RarArchive;
  24522. }
  24523. getList(url);
  24524. } else {
  24525. error();
  24526. }
  24527. }
  24528. },
  24529. {
  24530. tryRequire: true,
  24531. error : error
  24532. }
  24533. );
  24534. }
  24535. }
  24536. });
  24537. }
  24538. },
  24539. /**
  24540. * Any supported files preview plugin using Google docs online viewer
  24541. *
  24542. * @param elFinder.commands.quicklook
  24543. **/
  24544. function(ql) {
  24545. var fm = ql.fm,
  24546. mimes = fm.arrayFlip(ql.options.googleDocsMimes || []),
  24547. preview = ql.preview,
  24548. win = ql.window,
  24549. navi = ql.navbar,
  24550. node;
  24551. preview.on(ql.evUpdate, function(e) {
  24552. var file = e.file;
  24553. if (mimes[file.mime]) {
  24554. var win = ql.window,
  24555. setNavi = function() {
  24556. navi.css('bottom', win.hasClass('elfinder-quicklook-fullscreen')? '56px' : '');
  24557. },
  24558. loading;
  24559. if (file.url == '1') {
  24560. preview.hide();
  24561. $('<div class="elfinder-quicklook-info-data"><button class="elfinder-info-button">'+fm.i18n('getLink')+'</button></div>').appendTo(ql.info.find('.elfinder-quicklook-info'))
  24562. .on('click', function() {
  24563. var self = $(this);
  24564. self.html('<span class="elfinder-info-spinner">');
  24565. fm.request({
  24566. data : {cmd : 'url', target : file.hash},
  24567. preventDefault : true
  24568. })
  24569. .always(function() {
  24570. self.html('');
  24571. })
  24572. .done(function(data) {
  24573. var rfile = fm.file(file.hash);
  24574. file.url = rfile.url = data.url || '';
  24575. if (file.url) {
  24576. preview.trigger({
  24577. type: ql.evUpdate,
  24578. file: file,
  24579. forceUpdate: true
  24580. });
  24581. }
  24582. });
  24583. });
  24584. }
  24585. if (file.url !== '' && file.url != '1') {
  24586. e.stopImmediatePropagation();
  24587. preview.one('change', function() {
  24588. win.off('viewchange.googledocs');
  24589. loading.remove();
  24590. node.off('load').remove();
  24591. node = null;
  24592. }).addClass('elfinder-overflow-auto');
  24593. loading = $('<div class="elfinder-quicklook-info-data"> '+fm.i18n('nowLoading')+'<span class="elfinder-info-spinner"></div>').appendTo(ql.info.find('.elfinder-quicklook-info'));
  24594. node = $('<iframe class="elfinder-quicklook-preview-iframe"/>')
  24595. .css('background-color', 'transparent')
  24596. .appendTo(preview)
  24597. .on('load', function() {
  24598. ql.hideinfo();
  24599. loading.remove();
  24600. ql.preview.after(ql.info);
  24601. $(this).css('background-color', '#fff').show();
  24602. })
  24603. .on('error', function() {
  24604. loading.remove();
  24605. ql.preview.after(ql.info);
  24606. })
  24607. .attr('src', '//docs.google.com/gview?embedded=true&url=' + encodeURIComponent(fm.convAbsUrl(fm.url(file.hash))));
  24608. win.on('viewchange.googledocs', setNavi);
  24609. setNavi();
  24610. ql.info.after(ql.preview);
  24611. }
  24612. }
  24613. });
  24614. }
  24615. ];
  24616. /*
  24617. * File: /js/commands/reload.js
  24618. */
  24619. /**
  24620. * @class elFinder command "reload"
  24621. * Sync files and folders
  24622. *
  24623. * @author Dmitry (dio) Levashov
  24624. **/
  24625. (elFinder.prototype.commands.reload = function() {
  24626. var self = this,
  24627. search = false;
  24628. this.alwaysEnabled = true;
  24629. this.updateOnSelect = true;
  24630. this.shortcuts = [{
  24631. pattern : 'ctrl+shift+r f5'
  24632. }];
  24633. this.getstate = function() {
  24634. return 0;
  24635. };
  24636. this.init = function() {
  24637. this.fm.bind('search searchend', function() {
  24638. search = this.type == 'search';
  24639. });
  24640. };
  24641. this.fm.bind('contextmenu', function(){
  24642. var fm = self.fm;
  24643. if (fm.options.sync >= 1000) {
  24644. self.extra = {
  24645. icon: 'accept',
  24646. node: $('<span/>')
  24647. .attr({title: fm.i18n('autoSync')})
  24648. .on('click touchstart', function(e){
  24649. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  24650. return;
  24651. }
  24652. e.stopPropagation();
  24653. e.preventDefault();
  24654. $(this).parent()
  24655. .toggleClass('ui-state-disabled', fm.options.syncStart)
  24656. .parent().removeClass('ui-state-hover');
  24657. fm.options.syncStart = !fm.options.syncStart;
  24658. fm.autoSync(fm.options.syncStart? null : 'stop');
  24659. }).on('ready', function(){
  24660. $(this).parent().toggleClass('ui-state-disabled', !fm.options.syncStart).css('pointer-events', 'auto');
  24661. })
  24662. };
  24663. }
  24664. });
  24665. this.exec = function() {
  24666. var fm = this.fm;
  24667. if (!search) {
  24668. var dfrd = fm.sync(),
  24669. timeout = setTimeout(function() {
  24670. fm.notify({type : 'reload', cnt : 1, hideCnt : true});
  24671. dfrd.always(function() { fm.notify({type : 'reload', cnt : -1}); });
  24672. }, fm.notifyDelay);
  24673. return dfrd.always(function() {
  24674. clearTimeout(timeout);
  24675. fm.trigger('reload');
  24676. });
  24677. } else {
  24678. $('div.elfinder-toolbar > div.'+fm.res('class', 'searchbtn') + ' > span.ui-icon-search').click();
  24679. }
  24680. };
  24681. }).prototype = { forceLoad : true }; // this is required command
  24682. /*
  24683. * File: /js/commands/rename.js
  24684. */
  24685. /**
  24686. * @class elFinder command "rename".
  24687. * Rename selected file.
  24688. *
  24689. * @author Dmitry (dio) Levashov, dio@std42.ru
  24690. * @author Naoki Sawada
  24691. **/
  24692. elFinder.prototype.commands.rename = function() {
  24693. // set alwaysEnabled to allow root rename on client size
  24694. this.alwaysEnabled = true;
  24695. var self = this,
  24696. fm = self.fm,
  24697. request = function(dfrd, targtes, file, name) {
  24698. var sel = targtes? [file.hash].concat(targtes) : [file.hash],
  24699. cnt = sel.length,
  24700. data = {}, rootNames;
  24701. fm.lockfiles({files : sel});
  24702. if (fm.isRoot(file)) {
  24703. if (!(rootNames = fm.storage('rootNames'))) {
  24704. rootNames = {};
  24705. }
  24706. if (name === '') {
  24707. if (rootNames[file.hash]) {
  24708. file.name = file._name;
  24709. file.i18 = file._i18;
  24710. delete rootNames[file.hash];
  24711. delete file._name;
  24712. delete file._i18;
  24713. } else {
  24714. dfrd && dfrd.reject();
  24715. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  24716. return;
  24717. }
  24718. } else {
  24719. if (typeof file._name === 'undefined') {
  24720. file._name = file.name;
  24721. file._i18 = file.i18;
  24722. }
  24723. file.name = rootNames[file.hash] = name;
  24724. delete file.i18;
  24725. }
  24726. fm.storage('rootNames', rootNames);
  24727. data = { changed: [file] };
  24728. fm.updateCache(data);
  24729. fm.change(data);
  24730. dfrd && dfrd.resolve(data);
  24731. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  24732. return;
  24733. }
  24734. data = {
  24735. cmd : 'rename',
  24736. name : name,
  24737. target : file.hash
  24738. };
  24739. if (cnt > 1) {
  24740. data['targets'] = targtes;
  24741. if (name.match(/\*/)) {
  24742. data['q'] = name;
  24743. }
  24744. }
  24745. fm.request({
  24746. data : data,
  24747. notify : {type : 'rename', cnt : cnt},
  24748. navigate : {}
  24749. })
  24750. .fail(function(error) {
  24751. dfrd && dfrd.reject();
  24752. if (! error || ! Array.isArray(error) || error[0] !== 'errRename') {
  24753. fm.sync();
  24754. }
  24755. })
  24756. .done(function(data) {
  24757. var cwdHash;
  24758. if (data.added && data.added.length && cnt === 1) {
  24759. data.undo = {
  24760. cmd : 'rename',
  24761. callback : function() {
  24762. return fm.request({
  24763. data : {cmd : 'rename', target : data.added[0].hash, name : file.name},
  24764. notify : {type : 'undo', cnt : 1}
  24765. });
  24766. }
  24767. };
  24768. data.redo = {
  24769. cmd : 'rename',
  24770. callback : function() {
  24771. return fm.request({
  24772. data : {cmd : 'rename', target : file.hash, name : name},
  24773. notify : {type : 'rename', cnt : 1}
  24774. });
  24775. }
  24776. };
  24777. }
  24778. dfrd && dfrd.resolve(data);
  24779. if (!(cwdHash = fm.cwd().hash) || cwdHash === file.hash) {
  24780. fm.exec('open', $.map(data.added, function(f) {
  24781. return (f.mime === 'directory')? f.hash : null;
  24782. })[0]);
  24783. }
  24784. })
  24785. .always(function() {
  24786. fm.unlockfiles({files : sel}).trigger('selectfiles', {files : sel});
  24787. }
  24788. );
  24789. },
  24790. getHint = function(name, target) {
  24791. var sel = target || fm.selected(),
  24792. splits = fm.splitFileExtention(name),
  24793. f1 = fm.file(sel[0]),
  24794. f2 = fm.file(sel[1]),
  24795. ext, hint, add;
  24796. ext = splits[1]? ('.' + splits[1]) : '';
  24797. if (splits[1] && splits[0] === '*') {
  24798. // change extention
  24799. hint = '"' + fm.splitFileExtention(f1.name)[0] + ext + '", ';
  24800. hint += '"' + fm.splitFileExtention(f2.name)[0] + ext + '"';
  24801. } else if (splits[0].length > 1) {
  24802. if (splits[0].substr(-1) === '*') {
  24803. // add prefix
  24804. add = splits[0].substr(0, splits[0].length - 1);
  24805. hint = '"' + add + f1.name+'", ';
  24806. hint += '"' + add + f2.name+'"';
  24807. } else if (splits[0].substr(0, 1) === '*') {
  24808. // add suffix
  24809. add = splits[0].substr(1);
  24810. hint = '"'+fm.splitFileExtention(f1.name)[0] + add + ext + '", ';
  24811. hint += '"'+fm.splitFileExtention(f2.name)[0] + add + ext + '"';
  24812. }
  24813. }
  24814. if (!hint) {
  24815. hint = '"'+splits[0] + '1' + ext + '", "' + splits[0] + '2' + ext + '"';
  24816. }
  24817. if (sel.length > 2) {
  24818. hint += ' ...';
  24819. }
  24820. return hint;
  24821. },
  24822. batchRename = function() {
  24823. var sel = fm.selected(),
  24824. tplr = '<input name="type" type="radio" class="elfinder-tabstop">',
  24825. mkChk = function(node, label) {
  24826. return $('<label class="elfinder-rename-batch-checks">' + fm.i18n(label) + '</label>').prepend(node);
  24827. },
  24828. name = $('<input type="text" class="ui-corner-all elfinder-tabstop">'),
  24829. num = $(tplr),
  24830. prefix = $(tplr),
  24831. suffix = $(tplr),
  24832. extention = $(tplr),
  24833. checks = $('<div/>').append(
  24834. mkChk(num, 'plusNumber'),
  24835. mkChk(prefix, 'asPrefix'),
  24836. mkChk(suffix, 'asSuffix'),
  24837. mkChk(extention, 'changeExtention')
  24838. ),
  24839. preview = $('<div class="elfinder-rename-batch-preview"/>'),
  24840. node = $('<div class="elfinder-rename-batch"/>').append(
  24841. $('<div class="elfinder-rename-batch-name"/>').append(name),
  24842. $('<div class="elfinder-rename-batch-type"/>').append(checks),
  24843. preview
  24844. ),
  24845. opts = {
  24846. title : fm.i18n('batchRename'),
  24847. modal : true,
  24848. destroyOnClose : true,
  24849. width: Math.min(380, fm.getUI().width() - 20),
  24850. buttons : {},
  24851. open : function() {
  24852. name.on('input', mkPrev).trigger('focus');
  24853. }
  24854. },
  24855. getName = function() {
  24856. var vName = name.val(),
  24857. ext = fm.splitFileExtention(fm.file(sel[0]).name)[1];
  24858. if (vName !== '' || num.is(':checked')) {
  24859. if (prefix.is(':checked')) {
  24860. vName += '*';
  24861. } else if (suffix.is(':checked')) {
  24862. vName = '*' + vName + '.' + ext;
  24863. } else if (extention.is(':checked')) {
  24864. vName = '*.' + vName;
  24865. } else if (ext) {
  24866. vName += '.' + ext;
  24867. }
  24868. }
  24869. return vName;
  24870. },
  24871. mkPrev = function() {
  24872. var vName = getName();
  24873. if (vName !== '') {
  24874. preview.html(fm.i18n(['renameMultiple', sel.length, getHint(vName)]));
  24875. } else {
  24876. preview.empty();
  24877. }
  24878. },
  24879. radios = checks.find('input:radio').on('change', mkPrev),
  24880. dialog;
  24881. opts.buttons[fm.i18n('btnApply')] = function() {
  24882. var vName = getName(),
  24883. file, targets;
  24884. if (vName !== '') {
  24885. dialog.elfinderdialog('close');
  24886. targets = sel;
  24887. file = fm.file(targets.shift());
  24888. request(void(0), targets, file, vName);
  24889. }
  24890. };
  24891. opts.buttons[fm.i18n('btnCancel')] = function() {
  24892. dialog.elfinderdialog('close');
  24893. };
  24894. if ($.fn.checkboxradio) {
  24895. radios.checkboxradio({
  24896. create: function(e, ui) {
  24897. if (this === num.get(0)) {
  24898. num.prop('checked', true).change();
  24899. }
  24900. }
  24901. });
  24902. } else {
  24903. checks.buttonset({
  24904. create: function(e, ui) {
  24905. num.prop('checked', true).change();
  24906. }
  24907. });
  24908. }
  24909. dialog = fm.dialog(node, opts);
  24910. };
  24911. this.noChangeDirOnRemovedCwd = true;
  24912. this.shortcuts = [{
  24913. pattern : 'f2' + (fm.OS == 'mac' ? ' enter' : '')
  24914. }, {
  24915. pattern : 'shift+f2',
  24916. description : 'batchRename',
  24917. callback : function() {
  24918. fm.selected().length > 1 && batchRename();
  24919. }
  24920. }];
  24921. this.getstate = function(select) {
  24922. var sel = this.files(select),
  24923. cnt = sel.length,
  24924. phash, ext, mime, brk, state, isRoot;
  24925. if (!cnt) {
  24926. return -1;
  24927. }
  24928. if (cnt > 1 && sel[0].phash) {
  24929. phash = sel[0].phash;
  24930. ext = fm.splitFileExtention(sel[0].name)[1].toLowerCase();
  24931. mime = sel[0].mime;
  24932. }
  24933. if (cnt === 1) {
  24934. isRoot = fm.isRoot(sel[0]);
  24935. }
  24936. state = (cnt === 1 && (isRoot || !sel[0].locked) || (fm.api > 2.1030 && cnt === $.grep(sel, function(f) {
  24937. if (!brk && !f.locked && f.phash === phash && !fm.isRoot(f) && (mime === f.mime || ext === fm.splitFileExtention(f.name)[1].toLowerCase())) {
  24938. return true;
  24939. } else {
  24940. brk && (brk = true);
  24941. return false;
  24942. }
  24943. }).length)) ? 0 : -1;
  24944. // because alwaysEnabled = true, it need check disabled on connector
  24945. if (!isRoot && state === 0 && fm.option('disabledFlip', sel[0].hash)['rename']) {
  24946. state = -1;
  24947. }
  24948. if (state !== -1 && cnt > 1) {
  24949. self.extra = {
  24950. icon: 'preference',
  24951. node: $('<span/>')
  24952. .attr({title: fm.i18n('batchRename')})
  24953. .on('click touchstart', function(e){
  24954. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  24955. return;
  24956. }
  24957. e.stopPropagation();
  24958. e.preventDefault();
  24959. fm.getUI().trigger('click'); // to close the context menu immediately
  24960. batchRename();
  24961. })
  24962. };
  24963. } else {
  24964. delete self.extra;
  24965. }
  24966. return state;
  24967. };
  24968. this.exec = function(hashes, cOpts) {
  24969. var cwd = fm.getUI('cwd'),
  24970. sel = hashes || (fm.selected().length? fm.selected() : false) || [fm.cwd().hash],
  24971. cnt = sel.length,
  24972. file = fm.file(sel.shift()),
  24973. filename = '.elfinder-cwd-filename',
  24974. opts = cOpts || {},
  24975. incwd = (fm.cwd().hash == file.hash),
  24976. type = (opts._currentType === 'navbar' || opts._currentType === 'files')? opts._currentType : (incwd? 'navbar' : 'files'),
  24977. navbar = (type !== 'files'),
  24978. target = $('#'+fm[navbar? 'navHash2Id' : 'cwdHash2Id'](file.hash)),
  24979. tarea = (!navbar && fm.storage('view') != 'list'),
  24980. split = function(name) {
  24981. var ext = fm.splitFileExtention(name)[1];
  24982. return [name.substr(0, name.length - ext.length - 1), ext];
  24983. },
  24984. unselect = function() {
  24985. setTimeout(function() {
  24986. input && input.trigger('blur');
  24987. }, 50);
  24988. },
  24989. rest = function(){
  24990. if (!overlay.is(':hidden')) {
  24991. overlay.elfinderoverlay('hide').off('click', cancel);
  24992. }
  24993. pnode.removeClass('ui-front')
  24994. .css('position', '')
  24995. .off('unselect.'+fm.namespace, unselect);
  24996. if (tarea) {
  24997. node && node.css('max-height', '');
  24998. } else if (!navbar) {
  24999. pnode.css('width', '')
  25000. .parent('td').css('overflow', '');
  25001. }
  25002. }, colwidth,
  25003. dfrd = $.Deferred()
  25004. .fail(function(error) {
  25005. var parent = input.parent(),
  25006. name = fm.escape(file.i18 || file.name);
  25007. input.off();
  25008. if (tarea) {
  25009. name = name.replace(/([_.])/g, '&#8203;$1');
  25010. }
  25011. setTimeout(function() {
  25012. if (navbar) {
  25013. input.replaceWith(name);
  25014. } else {
  25015. if (parent.length) {
  25016. input.remove();
  25017. parent.html(name);
  25018. } else {
  25019. target.find(filename).html(name);
  25020. }
  25021. }
  25022. }, 0);
  25023. error && fm.error(error);
  25024. })
  25025. .always(function() {
  25026. rest();
  25027. fm.unbind('resize', resize);
  25028. fm.enable();
  25029. }),
  25030. blur = function(e) {
  25031. var name = $.trim(input.val()),
  25032. splits = fm.splitFileExtention(name),
  25033. valid = true,
  25034. req = function() {
  25035. input.off();
  25036. rest();
  25037. if (navbar) {
  25038. input.replaceWith(fm.escape(name));
  25039. } else {
  25040. node.html(fm.escape(name));
  25041. }
  25042. request(dfrd, sel, file, name);
  25043. };
  25044. if (!overlay.is(':hidden')) {
  25045. pnode.css('z-index', '');
  25046. }
  25047. if (name === '') {
  25048. if (!fm.isRoot(file)) {
  25049. return cancel();
  25050. }
  25051. if (navbar) {
  25052. input.replaceWith(fm.escape(file.name));
  25053. } else {
  25054. node.html(fm.escape(file.name));
  25055. }
  25056. }
  25057. if (!inError && pnode.length) {
  25058. input.off('blur');
  25059. if (cnt === 1 && name === file.name) {
  25060. return dfrd.reject();
  25061. }
  25062. if (fm.options.validName && fm.options.validName.test) {
  25063. try {
  25064. valid = fm.options.validName.test(name);
  25065. } catch(e) {
  25066. valid = false;
  25067. }
  25068. }
  25069. if (name === '.' || name === '..' || !valid) {
  25070. inError = true;
  25071. fm.error(file.mime === 'directory'? 'errInvDirname' : 'errInvName', {modal: true, close: function(){setTimeout(select, 120);}});
  25072. return false;
  25073. }
  25074. if (cnt === 1 && fm.fileByName(name, file.phash)) {
  25075. inError = true;
  25076. fm.error(['errExists', name], {modal: true, close: function(){setTimeout(select, 120);}});
  25077. return false;
  25078. }
  25079. if (cnt === 1) {
  25080. req();
  25081. } else {
  25082. fm.confirm({
  25083. title : 'cmdrename',
  25084. text : ['renameMultiple', cnt, getHint(name, [file.hash].concat(sel))],
  25085. accept : {
  25086. label : 'btnYes',
  25087. callback : req
  25088. },
  25089. cancel : {
  25090. label : 'btnCancel',
  25091. callback : function() {
  25092. setTimeout(function() {
  25093. inError = true;
  25094. select();
  25095. }, 120);
  25096. }
  25097. }
  25098. });
  25099. setTimeout(function() {
  25100. fm.trigger('unselectfiles', {files: fm.selected()})
  25101. .trigger('selectfiles', {files : [file.hash].concat(sel)});
  25102. }, 120);
  25103. }
  25104. }
  25105. },
  25106. input = $(tarea? '<textarea/>' : '<input type="text"/>')
  25107. .on('keyup text', function(){
  25108. if (tarea) {
  25109. this.style.height = '1px';
  25110. this.style.height = this.scrollHeight + 'px';
  25111. } else if (colwidth) {
  25112. this.style.width = colwidth + 'px';
  25113. if (this.scrollWidth > colwidth) {
  25114. this.style.width = this.scrollWidth + 10 + 'px';
  25115. }
  25116. }
  25117. })
  25118. .on('keydown', function(e) {
  25119. e.stopImmediatePropagation();
  25120. if (e.keyCode == $.ui.keyCode.ESCAPE) {
  25121. dfrd.reject();
  25122. } else if (e.keyCode == $.ui.keyCode.ENTER) {
  25123. e.preventDefault();
  25124. input.trigger('blur');
  25125. }
  25126. })
  25127. .on('mousedown click dblclick', function(e) {
  25128. e.stopPropagation();
  25129. if (e.type === 'dblclick') {
  25130. e.preventDefault();
  25131. }
  25132. })
  25133. .on('blur', blur),
  25134. select = function() {
  25135. var name = fm.splitFileExtention(input.val())[0];
  25136. if (!inError && fm.UA.Mobile && !fm.UA.iOS) { // since iOS has a bug? (z-index not effect) so disable it
  25137. overlay.on('click', cancel).elfinderoverlay('show');
  25138. pnode.css('z-index', overlay.css('z-index') + 1);
  25139. }
  25140. ! fm.enabled() && fm.enable();
  25141. if (inError) {
  25142. inError = false;
  25143. input.on('blur', blur);
  25144. }
  25145. input.trigger('focus').trigger('select');
  25146. input[0].setSelectionRange && input[0].setSelectionRange(0, name.length);
  25147. },
  25148. node = navbar? target.contents().filter(function(){ return this.nodeType==3 && $(this).parent().attr('id') === fm.navHash2Id(file.hash); })
  25149. : target.find(filename),
  25150. pnode = node.parent(),
  25151. overlay = fm.getUI('overlay'),
  25152. cancel = function(e) {
  25153. if (!overlay.is(':hidden')) {
  25154. pnode.css('z-index', '');
  25155. }
  25156. if (! inError) {
  25157. dfrd.reject();
  25158. if (e) {
  25159. e.stopPropagation();
  25160. e.preventDefault();
  25161. }
  25162. }
  25163. },
  25164. resize = function() {
  25165. target.trigger('scrolltoview', {blink : false});
  25166. },
  25167. inError = false;
  25168. if (fm.UA.iOS) {
  25169. // prevent auto zoom
  25170. input.css('font-size', '16px');
  25171. }
  25172. pnode.addClass('ui-front')
  25173. .css('position', 'relative')
  25174. .on('unselect.'+fm.namespace, unselect);
  25175. fm.bind('resize', resize);
  25176. if (navbar) {
  25177. node.replaceWith(input.val(file.name));
  25178. } else {
  25179. if (tarea) {
  25180. node.css('max-height', 'none');
  25181. } else if (!navbar) {
  25182. colwidth = pnode.width();
  25183. pnode.width(colwidth - 15)
  25184. .parent('td').css('overflow', 'visible');
  25185. }
  25186. node.empty().append(input.val(file.name));
  25187. }
  25188. if (cnt > 1 && fm.api <= 2.1030) {
  25189. return dfrd.reject();
  25190. }
  25191. if (!file || !node.length) {
  25192. return dfrd.reject('errCmdParams', this.title);
  25193. }
  25194. if (file.locked && !fm.isRoot(file)) {
  25195. return dfrd.reject(['errLocked', file.name]);
  25196. }
  25197. fm.one('select', function() {
  25198. input.parent().length && file && $.inArray(file.hash, fm.selected()) === -1 && input.trigger('blur');
  25199. });
  25200. input.trigger('keyup');
  25201. select();
  25202. return dfrd;
  25203. };
  25204. fm.remove(function(e) {
  25205. var rootNames;
  25206. if (e.data && e.data.removed && (rootNames = fm.storage('rootNames'))) {
  25207. $.each(e.data.removed, function(i, h) {
  25208. if (rootNames[h]) {
  25209. delete rootNames[h];
  25210. }
  25211. });
  25212. fm.storage('rootNames', rootNames);
  25213. }
  25214. });
  25215. };
  25216. /*
  25217. * File: /js/commands/resize.js
  25218. */
  25219. /**
  25220. * @class elFinder command "resize"
  25221. * Open dialog to resize image
  25222. *
  25223. * @author Dmitry (dio) Levashov
  25224. * @author Alexey Sukhotin
  25225. * @author Naoki Sawada
  25226. * @author Sergio Jovani
  25227. **/
  25228. elFinder.prototype.commands.resize = function() {
  25229. "use strict";
  25230. var losslessRotate = 0,
  25231. getBounceBox = function(w, h, theta) {
  25232. var srcPts = [
  25233. {x: w/2, y: h/2},
  25234. {x: -w/2, y: h/2},
  25235. {x: -w/2, y: -h/2},
  25236. {x: w/2, y: -h/2}
  25237. ],
  25238. dstPts = [],
  25239. min = {x: Number.MAX_VALUE, y: Number.MAX_VALUE},
  25240. max = {x: Number.MIN_VALUE, y: Number.MIN_VALUE};
  25241. $.each(srcPts, function(i, srcPt){
  25242. dstPts.push({
  25243. x: srcPt.x * Math.cos(theta) - srcPt.y * Math.sin(theta),
  25244. y: srcPt.x * Math.sin(theta) + srcPt.y * Math.cos(theta)
  25245. });
  25246. });
  25247. $.each(dstPts, function(i, pt) {
  25248. min.x = Math.min(min.x, pt.x);
  25249. min.y = Math.min(min.y, pt.y);
  25250. max.x = Math.max(max.x, pt.x);
  25251. max.y = Math.max(max.y, pt.y);
  25252. });
  25253. return {
  25254. width: max.x - min.x, height: max.y - min.y
  25255. };
  25256. };
  25257. this.updateOnSelect = false;
  25258. this.getstate = function() {
  25259. var sel = this.fm.selectedFiles();
  25260. return sel.length == 1 && sel[0].read && sel[0].write && sel[0].mime.indexOf('image/') !== -1 ? 0 : -1;
  25261. };
  25262. this.resizeRequest = function(data, f, dfrd) {
  25263. var fm = this.fm,
  25264. file = f || fm.file(data.target),
  25265. tmb = file? file.tmb : null,
  25266. enabled = fm.isCommandEnabled('resize', data.target);
  25267. if (enabled && (! file || (file && file.read && file.write && file.mime.indexOf('image/') !== -1 ))) {
  25268. return fm.request({
  25269. data : Object.assign(data, {
  25270. cmd : 'resize'
  25271. }),
  25272. notify : {type : 'resize', cnt : 1}
  25273. })
  25274. .fail(function(error) {
  25275. if (dfrd) {
  25276. dfrd.reject(error);
  25277. }
  25278. })
  25279. .done(function() {
  25280. if (data.quality) {
  25281. fm.storage('jpgQuality', data.quality === fm.option('jpgQuality')? null : data.quality);
  25282. }
  25283. dfrd && dfrd.resolve();
  25284. });
  25285. } else {
  25286. var error;
  25287. if (file) {
  25288. if (file.mime.indexOf('image/') === -1) {
  25289. error = ['errResize', file.name, 'errUsupportType'];
  25290. } else {
  25291. error = ['errResize', file.name, 'errPerm'];
  25292. }
  25293. } else {
  25294. error = ['errResize', data.target, 'errPerm'];
  25295. }
  25296. if (dfrd) {
  25297. dfrd.reject(error);
  25298. } else {
  25299. fm.error(error);
  25300. }
  25301. return $.Deferred().reject(error);
  25302. }
  25303. };
  25304. this.exec = function(hashes) {
  25305. var self = this,
  25306. fm = this.fm,
  25307. files = this.files(hashes),
  25308. dfrd = $.Deferred(),
  25309. api2 = (fm.api > 1),
  25310. options = this.options,
  25311. dialogWidth = 650,
  25312. fmnode = fm.getUI(),
  25313. ctrgrup = $().controlgroup? 'controlgroup' : 'buttonset',
  25314. grid8Def = typeof options.grid8px === 'undefined' || options.grid8px !== 'disable'? true : false,
  25315. presetSize = Array.isArray(options.presetSize)? options.presetSize : [],
  25316. dlcls = 'elfinder-dialog-resize',
  25317. clactive = 'elfinder-dialog-active',
  25318. clsediting = fm.res('class', 'editing'),
  25319. open = function(file, id) {
  25320. var isJpeg = (file.mime === 'image/jpeg'),
  25321. dialog = $('<div class="elfinder-dialog-resize"/>'),
  25322. input = '<input type="number" class="ui-corner-all"/>',
  25323. row = '<div class="elfinder-resize-row"/>',
  25324. label = '<div class="elfinder-resize-label"/>',
  25325. changeTm = null,
  25326. operate = false,
  25327. opStart = function() { operate = true; },
  25328. opStop = function() {
  25329. if (operate) {
  25330. operate = false;
  25331. control.trigger('change');
  25332. }
  25333. },
  25334. control = $('<div class="elfinder-resize-control"/>')
  25335. .on('focus', 'input[type=text],input[type=number]', function() {
  25336. $(this).trigger('select');
  25337. })
  25338. .on('change', function() {
  25339. changeTm && clearTimeout(changeTm);
  25340. changeTm = setTimeout(function() {
  25341. var panel, quty, canvas, ctx, img, sx, sy, sw, sh, deg, theta, bb;
  25342. if (sizeImg && ! operate && (canvas = sizeImg.data('canvas'))) {
  25343. panel = control.children('div.elfinder-resize-control-panel:visible');
  25344. quty = panel.find('input.elfinder-resize-quality');
  25345. if (quty.is(':visible')) {
  25346. ctx = sizeImg.data('ctx');
  25347. img = sizeImg.get(0);
  25348. if (panel.hasClass('elfinder-resize-uiresize')) {
  25349. // resize
  25350. sw = canvas.width = width.val();
  25351. sh = canvas.height = height.val();
  25352. ctx.drawImage(img, 0, 0, sw, sh);
  25353. } else if (panel.hasClass('elfinder-resize-uicrop')) {
  25354. // crop
  25355. sx = pointX.val();
  25356. sy = pointY.val();
  25357. sw = offsetX.val();
  25358. sh = offsetY.val();
  25359. canvas.width = sw;
  25360. canvas.height = sh;
  25361. ctx.drawImage(img, sx, sy, sw, sh, 0, 0, sw, sh);
  25362. } else {
  25363. // rotate
  25364. deg = degree.val();
  25365. theta = (degree.val() * Math.PI) / 180;
  25366. bb = getBounceBox(owidth, oheight, theta);
  25367. sw = canvas.width = bb.width;
  25368. sh = canvas.height = bb.height;
  25369. ctx.save();
  25370. if (deg % 90 !== 0) {
  25371. ctx.fillStyle = bg.val() || '#FFF';
  25372. ctx.fillRect(0, 0, sw, sh);
  25373. }
  25374. ctx.translate(sw / 2, sh / 2);
  25375. ctx.rotate(theta);
  25376. ctx.drawImage(img, -img.width/2, -img.height/2, owidth, oheight);
  25377. ctx.restore();
  25378. }
  25379. canvas.toBlob(function(blob) {
  25380. blob && quty.next('span').text(' (' + fm.formatSize(blob.size) + ')');
  25381. }, 'image/jpeg', Math.max(Math.min(quty.val(), 100), 1) / 100);
  25382. }
  25383. }
  25384. }, 60);
  25385. })
  25386. .on('mouseup', 'input', function(e) {
  25387. $(e.target).trigger('change');
  25388. }),
  25389. preview = $('<div class="elfinder-resize-preview"/>')
  25390. .on('touchmove', function(e) {
  25391. if ($(e.target).hasClass('touch-punch')) {
  25392. e.stopPropagation();
  25393. e.preventDefault();
  25394. }
  25395. }),
  25396. spinner = $('<div class="elfinder-resize-spinner">'+fm.i18n('ntfloadimg')+'</div>'),
  25397. rhandle = $('<div class="elfinder-resize-handle touch-punch"/>'),
  25398. rhandlec = $('<div class="elfinder-resize-handle touch-punch"/>'),
  25399. uiresize = $('<div class="elfinder-resize-uiresize elfinder-resize-control-panel"/>'),
  25400. uicrop = $('<div class="elfinder-resize-uicrop elfinder-resize-control-panel"/>'),
  25401. uirotate = $('<div class="elfinder-resize-rotate elfinder-resize-control-panel"/>'),
  25402. uideg270 = $('<button/>').attr('title',fm.i18n('rotate-cw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-l"/>')),
  25403. uideg90 = $('<button/>').attr('title',fm.i18n('rotate-ccw')).append($('<span class="elfinder-button-icon elfinder-button-icon-rotate-r"/>')),
  25404. uiprop = $('<span />'),
  25405. reset = $('<button class="elfinder-resize-reset">').text(fm.i18n('reset'))
  25406. .on('click', function() {
  25407. resetView();
  25408. })
  25409. .button({
  25410. icons: {
  25411. primary: 'ui-icon-arrowrefresh-1-n'
  25412. },
  25413. text: false
  25414. }),
  25415. uitype = $('<div class="elfinder-resize-type"/>')
  25416. .append('<input type="radio" name="type" id="'+id+'-resize" value="resize" checked="checked" /><label for="'+id+'-resize">'+fm.i18n('resize')+'</label>',
  25417. '<input class="api2" type="radio" name="type" id="'+id+'-crop" value="crop" /><label class="api2" for="'+id+'-crop">'+fm.i18n('crop')+'</label>',
  25418. '<input class="api2" type="radio" name="type" id="'+id+'-rotate" value="rotate" /><label class="api2" for="'+id+'-rotate">'+fm.i18n('rotate')+'</label>'),
  25419. mode = 'resize',
  25420. type = uitype[ctrgrup]()[ctrgrup]('disable').find('input')
  25421. .on('change', function() {
  25422. mode = $(this).val();
  25423. resetView();
  25424. resizable(true);
  25425. croppable(true);
  25426. rotateable(true);
  25427. if (mode == 'resize') {
  25428. uiresize.show();
  25429. uirotate.hide();
  25430. uicrop.hide();
  25431. resizable();
  25432. isJpeg && grid8px.insertAfter(uiresize.find('.elfinder-resize-grid8'));
  25433. }
  25434. else if (mode == 'crop') {
  25435. uirotate.hide();
  25436. uiresize.hide();
  25437. uicrop.show();
  25438. croppable();
  25439. isJpeg && grid8px.insertAfter(uicrop.find('.elfinder-resize-grid8'));
  25440. } else if (mode == 'rotate') {
  25441. uiresize.hide();
  25442. uicrop.hide();
  25443. uirotate.show();
  25444. rotateable();
  25445. }
  25446. }),
  25447. width = $(input)
  25448. .on('change', function() {
  25449. var w = round(parseInt(width.val())),
  25450. h = round(cratio ? w/ratio : parseInt(height.val()));
  25451. if (w > 0 && h > 0) {
  25452. resize.updateView(w, h);
  25453. width.val(w);
  25454. height.val(h);
  25455. }
  25456. }),
  25457. height = $(input)
  25458. .on('change', function() {
  25459. var h = round(parseInt(height.val())),
  25460. w = round(cratio ? h*ratio : parseInt(width.val()));
  25461. if (w > 0 && h > 0) {
  25462. resize.updateView(w, h);
  25463. width.val(w);
  25464. height.val(h);
  25465. }
  25466. }),
  25467. pointX = $(input).on('change', function(){crop.updateView();}),
  25468. pointY = $(input).on('change', function(){crop.updateView();}),
  25469. offsetX = $(input).on('change', function(){crop.updateView('w');}),
  25470. offsetY = $(input).on('change', function(){crop.updateView('h');}),
  25471. quality = isJpeg && api2?
  25472. $(input).val(fm.storage('jpgQuality') > 0? fm.storage('jpgQuality') : fm.option('jpgQuality'))
  25473. .addClass('elfinder-resize-quality')
  25474. .attr('min', '1').attr('max', '100').attr('title', '1 - 100')
  25475. .on('blur', function(){
  25476. var q = Math.min(100, Math.max(1, parseInt(this.value)));
  25477. control.find('input.elfinder-resize-quality').val(q);
  25478. })
  25479. : null,
  25480. degree = $('<input type="number" class="ui-corner-all" maxlength="3" value="0" />')
  25481. .on('change', function() {
  25482. rotate.update();
  25483. }),
  25484. uidegslider = $('<div class="elfinder-resize-rotate-slider touch-punch"/>')
  25485. .slider({
  25486. min: 0,
  25487. max: 360,
  25488. value: degree.val(),
  25489. animate: true,
  25490. start: opStart,
  25491. stop: opStop,
  25492. change: function(event, ui) {
  25493. if (ui.value != uidegslider.slider('value')) {
  25494. rotate.update(ui.value);
  25495. }
  25496. },
  25497. slide: function(event, ui) {
  25498. rotate.update(ui.value, false);
  25499. }
  25500. }).find('.ui-slider-handle')
  25501. .addClass('elfinder-tabstop')
  25502. .off('keydown')
  25503. .on('keydown', function(e) {
  25504. if (e.keyCode == $.ui.keyCode.LEFT || e.keyCode == $.ui.keyCode.RIGHT) {
  25505. e.stopPropagation();
  25506. e.preventDefault();
  25507. rotate.update(Number(degree.val()) + (e.keyCode == $.ui.keyCode.RIGHT? 1 : -1), false);
  25508. }
  25509. })
  25510. .end(),
  25511. pickimg,
  25512. pickcanv,
  25513. pickctx,
  25514. pickc = {},
  25515. pick = function(e) {
  25516. var color, r, g, b, h, s, l;
  25517. try {
  25518. color = pickc[Math.round(e.offsetX)][Math.round(e.offsetY)];
  25519. } catch(e) {}
  25520. if (!color) return;
  25521. r = color[0]; g = color[1]; b = color[2];
  25522. h = color[3]; s = color[4]; l = color[5];
  25523. setbg(r, g, b, (e.type === 'click'));
  25524. },
  25525. palpick = function(e) {
  25526. setbg($(this).css('backgroundColor'), '', '', (e.type === 'click'));
  25527. },
  25528. setbg = function(r, g, b, off) {
  25529. var s, m, cc;
  25530. if (typeof r === 'string') {
  25531. g = '';
  25532. if (r && (s = $('<span>').css('backgroundColor', r).css('backgroundColor')) && (m = s.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i))) {
  25533. r = Number(m[1]);
  25534. g = Number(m[2]);
  25535. b = Number(m[3]);
  25536. }
  25537. }
  25538. cc = (g === '')? r : '#' + getColorCode(r, g, b);
  25539. bg.val(cc).css({ backgroundColor: cc, backgroundImage: 'none', color: (r+g+b < 384? '#fff' : '#000') });
  25540. preview.css('backgroundColor', cc);
  25541. if (off) {
  25542. imgr.off('.picker').removeClass('elfinder-resize-picking');
  25543. pallet.off('.picker').removeClass('elfinder-resize-picking');
  25544. }
  25545. },
  25546. getColorCode = function(r, g, b) {
  25547. return $.map([r,g,b], function(c){return ('0'+parseInt(c).toString(16)).slice(-2);}).join('');
  25548. },
  25549. picker = $('<button>').text(fm.i18n('colorPicker'))
  25550. .on('click', function() {
  25551. imgr.on('mousemove.picker click.picker', pick).addClass('elfinder-resize-picking');
  25552. pallet.on('mousemove.picker click.picker', 'span', palpick).addClass('elfinder-resize-picking');
  25553. })
  25554. .button({
  25555. icons: {
  25556. primary: 'ui-icon-pin-s'
  25557. },
  25558. text: false
  25559. }),
  25560. reseter = $('<button>').text(fm.i18n('reset'))
  25561. .on('click', function() {
  25562. setbg('', '', '', true);
  25563. })
  25564. .button({
  25565. icons: {
  25566. primary: 'ui-icon-arrowrefresh-1-n'
  25567. },
  25568. text: false
  25569. }),
  25570. bg = $('<input class="ui-corner-all elfinder-resize-bg" type="text">')
  25571. .on('focus', function() {
  25572. $(this).attr('style', '');
  25573. })
  25574. .on('blur', function() {
  25575. setbg($(this).val());
  25576. }),
  25577. pallet = $('<div class="elfinder-resize-pallet">').on('click', 'span', function() {
  25578. setbg($(this).css('backgroundColor'));
  25579. }),
  25580. ratio = 1,
  25581. prop = 1,
  25582. owidth = 0,
  25583. oheight = 0,
  25584. cratio = true,
  25585. cratioc = false,
  25586. pwidth = 0,
  25587. pheight = 0,
  25588. rwidth = 0,
  25589. rheight = 0,
  25590. rdegree = 0,
  25591. grid8 = isJpeg? grid8Def : false,
  25592. constr = $('<button>').html(fm.i18n('aspectRatio'))
  25593. .on('click', function() {
  25594. cratio = ! cratio;
  25595. constr.button('option', {
  25596. icons : { primary: cratio? 'ui-icon-locked' : 'ui-icon-unlocked'}
  25597. });
  25598. resize.fixHeight();
  25599. rhandle.resizable('option', 'aspectRatio', cratio).data('uiResizable')._aspectRatio = cratio;
  25600. })
  25601. .button({
  25602. icons : {
  25603. primary: cratio? 'ui-icon-locked' : 'ui-icon-unlocked'
  25604. },
  25605. text: false
  25606. }),
  25607. constrc = $('<button>').html(fm.i18n('aspectRatio'))
  25608. .on('click', function() {
  25609. cratioc = ! cratioc;
  25610. constrc.button('option', {
  25611. icons : { primary: cratioc? 'ui-icon-locked' : 'ui-icon-unlocked'}
  25612. });
  25613. rhandlec.resizable('option', 'aspectRatio', cratioc).data('uiResizable')._aspectRatio = cratioc;
  25614. })
  25615. .button({
  25616. icons : {
  25617. primary: cratioc? 'ui-icon-locked' : 'ui-icon-unlocked'
  25618. },
  25619. text: false
  25620. }),
  25621. grid8px = $('<button>').html(fm.i18n(grid8? 'enabled' : 'disabled')).toggleClass('ui-state-active', grid8)
  25622. .on('click', function() {
  25623. grid8 = ! grid8;
  25624. grid8px.html(fm.i18n(grid8? 'enabled' : 'disabled')).toggleClass('ui-state-active', grid8);
  25625. setStep8();
  25626. })
  25627. .button(),
  25628. setStep8 = function() {
  25629. var step = grid8? 8 : 1;
  25630. $.each([width, height, offsetX, offsetY, pointX, pointY], function() {
  25631. this.attr('step', step);
  25632. });
  25633. if (grid8) {
  25634. width.val(round(width.val()));
  25635. height.val(round(height.val()));
  25636. offsetX.val(round(offsetX.val()));
  25637. offsetY.val(round(offsetY.val()));
  25638. pointX.val(round(pointX.val()));
  25639. pointY.val(round(pointY.val()));
  25640. if (uiresize.is(':visible')) {
  25641. resize.updateView(width.val(), height.val());
  25642. } else if (uicrop.is(':visible')) {
  25643. crop.updateView();
  25644. }
  25645. }
  25646. },
  25647. setuprimg = function() {
  25648. var r_scale,
  25649. fail = function() {
  25650. bg.parent().hide();
  25651. pallet.hide();
  25652. };
  25653. r_scale = Math.min(pwidth, pheight) / Math.sqrt(Math.pow(owidth, 2) + Math.pow(oheight, 2));
  25654. rwidth = Math.ceil(owidth * r_scale);
  25655. rheight = Math.ceil(oheight * r_scale);
  25656. imgr.width(rwidth)
  25657. .height(rheight)
  25658. .css('margin-top', (pheight-rheight)/2 + 'px')
  25659. .css('margin-left', (pwidth-rwidth)/2 + 'px');
  25660. if (imgr.is(':visible') && bg.is(':visible')) {
  25661. if (file.mime !== 'image/png') {
  25662. preview.css('backgroundColor', bg.val());
  25663. pickimg = $('<img>');
  25664. if (fm.isCORS) {
  25665. pickimg.attr('crossorigin', 'use-credentials');
  25666. }
  25667. pickimg.on('load', function() {
  25668. if (pickcanv && pickcanv.width !== rwidth) {
  25669. setColorData();
  25670. }
  25671. })
  25672. .on('error', fail)
  25673. .attr('src', canvSrc);
  25674. } else {
  25675. fail();
  25676. }
  25677. }
  25678. },
  25679. setupimg = function() {
  25680. resize.updateView(owidth, oheight);
  25681. setuprimg();
  25682. basec
  25683. .width(img.width())
  25684. .height(img.height());
  25685. imgc
  25686. .width(img.width())
  25687. .height(img.height());
  25688. crop.updateView();
  25689. jpgCalc();
  25690. },
  25691. setColorData = function() {
  25692. if (pickctx) {
  25693. var n, w, h, r, g, b, a, s, l, hsl, hue,
  25694. data, scale, tx1, tx2, ty1, ty2, rgb,
  25695. domi = {},
  25696. domic = [],
  25697. domiv, palc,
  25698. rgbToHsl = function (r, g, b) {
  25699. var h, s, l,
  25700. max = Math.max(Math.max(r, g), b),
  25701. min = Math.min(Math.min(r, g), b);
  25702. // Hue, 0 ~ 359
  25703. if (max === min) {
  25704. h = 0;
  25705. } else if (r === max) {
  25706. h = ((g - b) / (max - min) * 60 + 360) % 360;
  25707. } else if (g === max) {
  25708. h = (b - r) / (max - min) * 60 + 120;
  25709. } else if (b === max) {
  25710. h = (r - g) / (max - min) * 60 + 240;
  25711. }
  25712. // Saturation, 0 ~ 1
  25713. s = (max - min) / max;
  25714. // Lightness, 0 ~ 1
  25715. l = (r * 0.3 + g * 0.59 + b * 0.11) / 255;
  25716. return [h, s, l, 'hsl'];
  25717. },
  25718. rgbRound = function(c) {
  25719. return Math.round(c / 8) * 8;
  25720. };
  25721. calc:
  25722. try {
  25723. w = pickcanv.width = imgr.width();
  25724. h = pickcanv.height = imgr.height();
  25725. scale = w / owidth;
  25726. pickctx.scale(scale, scale);
  25727. pickctx.drawImage(pickimg.get(0), 0, 0);
  25728. data = pickctx.getImageData(0, 0, w, h).data;
  25729. // Range to detect the dominant color
  25730. tx1 = w * 0.1;
  25731. tx2 = w * 0.9;
  25732. ty1 = h * 0.1;
  25733. ty2 = h * 0.9;
  25734. for (var y = 0; y < h - 1; y++) {
  25735. for (var x = 0; x < w - 1; x++) {
  25736. n = x * 4 + y * w * 4;
  25737. // RGB
  25738. r = data[n]; g = data[n + 1]; b = data[n + 2]; a = data[n + 3];
  25739. // check alpha ch
  25740. if (a !== 255) {
  25741. bg.parent().hide();
  25742. pallet.hide();
  25743. break calc;
  25744. }
  25745. // HSL
  25746. hsl = rgbToHsl(r, g, b);
  25747. hue = Math.round(hsl[0]); s = Math.round(hsl[1] * 100); l = Math.round(hsl[2] * 100);
  25748. if (! pickc[x]) {
  25749. pickc[x] = {};
  25750. }
  25751. // set pickc
  25752. pickc[x][y] = [r, g, b, hue, s, l];
  25753. // detect the dominant color
  25754. if ((x < tx1 || x > tx2) && (y < ty1 || y > ty2)) {
  25755. rgb = rgbRound(r) + ',' + rgbRound(g) + ',' + rgbRound(b);
  25756. if (! domi[rgb]) {
  25757. domi[rgb] = 1;
  25758. } else {
  25759. ++domi[rgb];
  25760. }
  25761. }
  25762. }
  25763. }
  25764. if (! pallet.children(':first').length) {
  25765. palc = 1;
  25766. $.each(domi, function(c, v) {
  25767. domic.push({c: c, v: v});
  25768. });
  25769. $.each(domic.sort(function(a, b) {
  25770. return (a.v > b.v)? -1 : 1;
  25771. }), function() {
  25772. if (this.v < 2 || palc > 10) {
  25773. return false;
  25774. }
  25775. pallet.append($('<span style="width:20px;height:20px;display:inline-block;background-color:rgb('+this.c+');">'));
  25776. ++palc;
  25777. });
  25778. }
  25779. } catch(e) {
  25780. picker.hide();
  25781. pallet.hide();
  25782. }
  25783. }
  25784. },
  25785. setupPicker = function() {
  25786. try {
  25787. pickcanv = document.createElement('canvas');
  25788. pickctx = pickcanv.getContext('2d');
  25789. } catch(e) {
  25790. picker.hide();
  25791. pallet.hide();
  25792. }
  25793. },
  25794. setupPreset = function() {
  25795. preset.on('click', 'span.elfinder-resize-preset', function() {
  25796. var btn = $(this),
  25797. w = btn.data('s')[0],
  25798. h = btn.data('s')[1],
  25799. r = owidth / oheight;
  25800. btn.data('s', [h, w]).text(h + 'x' + w);
  25801. if (owidth > w || oheight > h) {
  25802. if (owidth <= w) {
  25803. w = round(h * r);
  25804. } else if (oheight <= h) {
  25805. h = round(w / r);
  25806. } else {
  25807. if (owidth - w > oheight - h) {
  25808. h = round(w / r);
  25809. } else {
  25810. w = round(h * r);
  25811. }
  25812. }
  25813. } else {
  25814. w = owidth;
  25815. h = oheight;
  25816. }
  25817. width.val(w);
  25818. height.val(h);
  25819. resize.updateView(w, h);
  25820. jpgCalc();
  25821. });
  25822. presetc.on('click', 'span.elfinder-resize-preset', function() {
  25823. var btn = $(this),
  25824. w = btn.data('s')[0],
  25825. h = btn.data('s')[1],
  25826. x = pointX.val(),
  25827. y = pointY.val();
  25828. btn.data('s', [h, w]).text(h + 'x' + w);
  25829. if (owidth >= w && oheight >= h) {
  25830. if (owidth - w - x < 0) {
  25831. x = owidth - w;
  25832. }
  25833. if (oheight - h - y < 0) {
  25834. y = oheight - h;
  25835. }
  25836. pointX.val(x);
  25837. pointY.val(y);
  25838. offsetX.val(w);
  25839. offsetY.val(h);
  25840. crop.updateView();
  25841. jpgCalc();
  25842. }
  25843. });
  25844. presetc.children('span.elfinder-resize-preset').each(function() {
  25845. var btn = $(this),
  25846. w = btn.data('s')[0],
  25847. h = btn.data('s')[1];
  25848. btn[(owidth >= w && oheight >= h)? 'show' : 'hide']();
  25849. });
  25850. },
  25851. dimreq = null,
  25852. inited = false,
  25853. setdim = function(dim) {
  25854. var rfile = fm.file(file.hash);
  25855. rfile.width = dim[0];
  25856. rfile.height = dim[1];
  25857. },
  25858. init = function() {
  25859. var elm, memSize, r_scale, inputFirst, imgRatio;
  25860. if (inited) {
  25861. return;
  25862. }
  25863. inited = true;
  25864. dimreq && dimreq.state && dimreq.state() === 'pending' && dimreq.reject();
  25865. // check lossless rotete
  25866. if (fm.api >= 2.1030) {
  25867. if (losslessRotate === 0) {
  25868. fm.request({
  25869. data: {
  25870. cmd : 'resize',
  25871. target : file.hash,
  25872. degree : 0,
  25873. mode : 'rotate'
  25874. },
  25875. preventDefault : true
  25876. }).done(function(data) {
  25877. losslessRotate = data.losslessRotate? 1 : -1;
  25878. if (losslessRotate === 1 && (degree.val() % 90 === 0)) {
  25879. uirotate.children('div.elfinder-resize-quality').hide();
  25880. }
  25881. }).fail(function() {
  25882. losslessRotate = -1;
  25883. });
  25884. }
  25885. } else {
  25886. losslessRotate = -1;
  25887. }
  25888. elm = img.get(0);
  25889. memSize = file.width && file.height? {w: file.width, h: file.height} : (elm.naturalWidth? null : {w: img.width(), h: img.height()});
  25890. memSize && img.removeAttr('width').removeAttr('height');
  25891. owidth = file.width || elm.naturalWidth || elm.width || img.width();
  25892. oheight = file.height || elm.naturalHeight || elm.height || img.height();
  25893. if (!file.width || !file.height) {
  25894. setdim([owidth, oheight]);
  25895. }
  25896. memSize && img.width(memSize.w).height(memSize.h);
  25897. dMinBtn.show();
  25898. imgRatio = oheight / owidth;
  25899. if (imgRatio < 1 && preview.height() > preview.width() * imgRatio) {
  25900. preview.height(preview.width() * imgRatio);
  25901. }
  25902. if (preview.height() > img.height() + 20) {
  25903. preview.height(img.height() + 20);
  25904. }
  25905. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  25906. spinner.remove();
  25907. ratio = owidth/oheight;
  25908. rhandle.append(img.show()).show();
  25909. width.val(owidth);
  25910. height.val(oheight);
  25911. setupPicker();
  25912. setupPreset();
  25913. setupimg();
  25914. uitype[ctrgrup]('enable');
  25915. inputFirst = control.find('input,select').prop('disabled', false)
  25916. .filter(':text').on('keydown', function(e) {
  25917. var cOpts;
  25918. if (e.keyCode == $.ui.keyCode.ENTER) {
  25919. e.stopPropagation();
  25920. e.preventDefault();
  25921. cOpts = {
  25922. title : $('input:checked', uitype).val(),
  25923. text : 'confirmReq',
  25924. accept : {
  25925. label : 'btnApply',
  25926. callback : function() {
  25927. save();
  25928. }
  25929. },
  25930. cancel : {
  25931. label : 'btnCancel',
  25932. callback : function(){
  25933. $(this).trigger('focus');
  25934. }
  25935. }
  25936. };
  25937. if (useSaveAs) {
  25938. cOpts['buttons'] = [{
  25939. label : 'btnSaveAs',
  25940. callback : function() {
  25941. setTimeout(saveAs, 10);
  25942. }
  25943. }];
  25944. }
  25945. fm.confirm(cOpts);
  25946. return;
  25947. }
  25948. })
  25949. .on('keyup', function() {
  25950. var $this = $(this);
  25951. if (! $this.hasClass('elfinder-resize-bg')) {
  25952. setTimeout(function() {
  25953. $this.val($this.val().replace(/[^0-9]/g, ''));
  25954. }, 10);
  25955. }
  25956. })
  25957. .filter(':first');
  25958. setStep8();
  25959. !fm.UA.Mobile && inputFirst.trigger('focus');
  25960. resizable();
  25961. },
  25962. img = $('<img/>')
  25963. .on('load', init)
  25964. .on('error', function() {
  25965. spinner.text('Unable to load image').css('background', 'transparent');
  25966. }),
  25967. basec = $('<div/>'),
  25968. imgc = $('<img/>'),
  25969. coverc = $('<div/>'),
  25970. imgr = $('<img class="elfinder-resize-imgrotate" />'),
  25971. round = function(v, max) {
  25972. v = grid8? Math.round(v/8)*8 : Math.round(v);
  25973. v = Math.max(0, v);
  25974. if (max && v > max) {
  25975. v = grid8? Math.floor(max/8)*8 : max;
  25976. }
  25977. return v;
  25978. },
  25979. resetView = function() {
  25980. width.val(owidth);
  25981. height.val(oheight);
  25982. resize.updateView(owidth, oheight);
  25983. pointX.val(0);
  25984. pointY.val(0);
  25985. offsetX.val(owidth);
  25986. offsetY.val(oheight);
  25987. crop.updateView();
  25988. jpgCalc();
  25989. },
  25990. resize = {
  25991. update : function() {
  25992. width.val(round(img.width()/prop));
  25993. height.val(round(img.height()/prop));
  25994. jpgCalc();
  25995. },
  25996. updateView : function(w, h) {
  25997. if (w > pwidth || h > pheight) {
  25998. if (w / pwidth > h / pheight) {
  25999. prop = pwidth / w;
  26000. img.width(pwidth).height(round(h*prop));
  26001. } else {
  26002. prop = pheight / h;
  26003. img.height(pheight).width(round(w*prop));
  26004. }
  26005. } else {
  26006. img.width(round(w)).height(round(h));
  26007. }
  26008. prop = img.width()/w;
  26009. uiprop.text('1 : '+(1/prop).toFixed(2));
  26010. resize.updateHandle();
  26011. },
  26012. updateHandle : function() {
  26013. rhandle.width(img.width()).height(img.height());
  26014. },
  26015. fixHeight : function() {
  26016. var w, h;
  26017. if (cratio) {
  26018. w = width.val();
  26019. h = round(w/ratio);
  26020. resize.updateView(w, h);
  26021. height.val(h);
  26022. }
  26023. }
  26024. },
  26025. crop = {
  26026. update : function(change) {
  26027. pointX.val(round(((rhandlec.data('x')||rhandlec.position().left))/prop, owidth));
  26028. pointY.val(round(((rhandlec.data('y')||rhandlec.position().top))/prop, oheight));
  26029. if (change !== 'xy') {
  26030. offsetX.val(round((rhandlec.data('w')||rhandlec.width())/prop, owidth - pointX.val()));
  26031. offsetY.val(round((rhandlec.data('h')||rhandlec.height())/prop, oheight - pointY.val()));
  26032. }
  26033. jpgCalc();
  26034. },
  26035. updateView : function(change) {
  26036. var r, x, y, w, h;
  26037. pointX.val(round(pointX.val(), owidth - (grid8? 8 : 1)));
  26038. pointY.val(round(pointY.val(), oheight - (grid8? 8 : 1)));
  26039. offsetX.val(round(offsetX.val(), owidth - pointX.val()));
  26040. offsetY.val(round(offsetY.val(), oheight - pointY.val()));
  26041. if (cratioc) {
  26042. r = coverc.width() / coverc.height();
  26043. if (change === 'w') {
  26044. offsetY.val(round(parseInt(offsetX.val()) / r));
  26045. } else if (change === 'h') {
  26046. offsetX.val(round(parseInt(offsetY.val()) * r));
  26047. }
  26048. }
  26049. x = Math.round(parseInt(pointX.val()) * prop);
  26050. y = Math.round(parseInt(pointY.val()) * prop);
  26051. if (change !== 'xy') {
  26052. w = Math.round(parseInt(offsetX.val()) * prop);
  26053. h = Math.round(parseInt(offsetY.val()) * prop);
  26054. } else {
  26055. w = rhandlec.data('w');
  26056. h = rhandlec.data('h');
  26057. }
  26058. rhandlec.data({x: x, y: y, w: w, h: h})
  26059. .width(w)
  26060. .height(h)
  26061. .css({left: x, top: y});
  26062. coverc.width(w)
  26063. .height(h);
  26064. },
  26065. resize_update : function(e, ui) {
  26066. rhandlec.data({x: ui.position.left, y: ui.position.top, w: ui.size.width, h: ui.size.height});
  26067. crop.update();
  26068. crop.updateView();
  26069. },
  26070. drag_update : function(e, ui) {
  26071. rhandlec.data({x: ui.position.left, y: ui.position.top});
  26072. crop.update('xy');
  26073. }
  26074. },
  26075. rotate = {
  26076. mouseStartAngle : 0,
  26077. imageStartAngle : 0,
  26078. imageBeingRotated : false,
  26079. setQuality : function() {
  26080. uirotate.children('div.elfinder-resize-quality')[(losslessRotate > 0 && (degree.val() % 90) === 0)? 'hide' : 'show']();
  26081. },
  26082. update : function(value, animate) {
  26083. if (typeof value == 'undefined') {
  26084. rdegree = value = parseInt(degree.val());
  26085. }
  26086. if (typeof animate == 'undefined') {
  26087. animate = true;
  26088. }
  26089. if (! animate || fm.UA.Opera || fm.UA.ltIE8) {
  26090. imgr.rotate(value);
  26091. } else {
  26092. imgr.animate({rotate: value + 'deg'});
  26093. }
  26094. value = value % 360;
  26095. if (value < 0) {
  26096. value += 360;
  26097. }
  26098. degree.val(parseInt(value));
  26099. uidegslider.slider('value', degree.val());
  26100. rotate.setQuality();
  26101. },
  26102. execute : function ( e ) {
  26103. if ( !rotate.imageBeingRotated ) return;
  26104. var imageCentre = rotate.getCenter( imgr );
  26105. var ev = e.originalEvent.touches? e.originalEvent.touches[0] : e;
  26106. var mouseXFromCentre = ev.pageX - imageCentre[0];
  26107. var mouseYFromCentre = ev.pageY - imageCentre[1];
  26108. var mouseAngle = Math.atan2( mouseYFromCentre, mouseXFromCentre );
  26109. var rotateAngle = mouseAngle - rotate.mouseStartAngle + rotate.imageStartAngle;
  26110. rotateAngle = Math.round(parseFloat(rotateAngle) * 180 / Math.PI);
  26111. if ( e.shiftKey ) {
  26112. rotateAngle = Math.round((rotateAngle + 6)/15) * 15;
  26113. }
  26114. imgr.rotate(rotateAngle);
  26115. rotateAngle = rotateAngle % 360;
  26116. if (rotateAngle < 0) {
  26117. rotateAngle += 360;
  26118. }
  26119. degree.val(rotateAngle);
  26120. uidegslider.slider('value', degree.val());
  26121. rotate.setQuality();
  26122. return false;
  26123. },
  26124. start : function ( e ) {
  26125. if (imgr.hasClass('elfinder-resize-picking')) {
  26126. return;
  26127. }
  26128. opStart();
  26129. rotate.imageBeingRotated = true;
  26130. var imageCentre = rotate.getCenter( imgr );
  26131. var ev = e.originalEvent.touches? e.originalEvent.touches[0] : e;
  26132. var mouseStartXFromCentre = ev.pageX - imageCentre[0];
  26133. var mouseStartYFromCentre = ev.pageY - imageCentre[1];
  26134. rotate.mouseStartAngle = Math.atan2( mouseStartYFromCentre, mouseStartXFromCentre );
  26135. rotate.imageStartAngle = parseFloat(imgr.rotate()) * Math.PI / 180.0;
  26136. $(document).on('mousemove', rotate.execute);
  26137. imgr.on('touchmove', rotate.execute);
  26138. return false;
  26139. },
  26140. stop : function ( e ) {
  26141. if ( !rotate.imageBeingRotated ) return;
  26142. $(document).off('mousemove', rotate.execute);
  26143. imgr.off('touchmove', rotate.execute);
  26144. setTimeout( function() { rotate.imageBeingRotated = false; }, 10 );
  26145. opStop();
  26146. return false;
  26147. },
  26148. getCenter : function ( image ) {
  26149. var currentRotation = imgr.rotate();
  26150. imgr.rotate(0);
  26151. var imageOffset = imgr.offset();
  26152. var imageCentreX = imageOffset.left + imgr.width() / 2;
  26153. var imageCentreY = imageOffset.top + imgr.height() / 2;
  26154. imgr.rotate(currentRotation);
  26155. return Array( imageCentreX, imageCentreY );
  26156. }
  26157. },
  26158. resizable = function(destroy) {
  26159. if (destroy) {
  26160. rhandle.filter(':ui-resizable').resizable('destroy');
  26161. rhandle.hide();
  26162. }
  26163. else {
  26164. rhandle.show();
  26165. rhandle.resizable({
  26166. alsoResize : img,
  26167. aspectRatio : cratio,
  26168. resize : resize.update,
  26169. start : opStart,
  26170. stop : function(e) {
  26171. resize.fixHeight;
  26172. resize.updateView(width.val(), height.val());
  26173. opStop();
  26174. }
  26175. });
  26176. dinit();
  26177. }
  26178. },
  26179. croppable = function(destroy) {
  26180. if (destroy) {
  26181. rhandlec.filter(':ui-resizable').resizable('destroy')
  26182. .filter(':ui-draggable').draggable('destroy');
  26183. basec.hide();
  26184. }
  26185. else {
  26186. basec.show();
  26187. rhandlec
  26188. .resizable({
  26189. containment : basec,
  26190. aspectRatio : cratioc,
  26191. resize : crop.resize_update,
  26192. start : opStart,
  26193. stop : opStop,
  26194. handles : 'all'
  26195. })
  26196. .draggable({
  26197. handle : coverc,
  26198. containment : imgc,
  26199. drag : crop.drag_update,
  26200. start : opStart,
  26201. stop : function() {
  26202. crop.updateView('xy');
  26203. opStop();
  26204. }
  26205. });
  26206. dinit();
  26207. crop.update();
  26208. }
  26209. },
  26210. rotateable = function(destroy) {
  26211. if (destroy) {
  26212. imgr.hide();
  26213. }
  26214. else {
  26215. imgr.show();
  26216. dinit();
  26217. }
  26218. },
  26219. checkVals = function() {
  26220. var w, h, x, y, d, q, b = '';
  26221. if (mode == 'resize') {
  26222. w = parseInt(width.val()) || 0;
  26223. h = parseInt(height.val()) || 0;
  26224. } else if (mode == 'crop') {
  26225. w = parseInt(offsetX.val()) || 0;
  26226. h = parseInt(offsetY.val()) || 0;
  26227. x = parseInt(pointX.val()) || 0;
  26228. y = parseInt(pointY.val()) || 0;
  26229. } else if (mode == 'rotate') {
  26230. w = owidth;
  26231. h = oheight;
  26232. d = parseInt(degree.val()) || 0;
  26233. if (d < 0 || d > 360) {
  26234. fm.error('Invalid rotate degree');
  26235. return false;
  26236. }
  26237. if (d == 0 || d == 360) {
  26238. fm.error('errResizeNoChange');
  26239. return false;
  26240. }
  26241. b = bg.val();
  26242. }
  26243. q = quality? parseInt(quality.val()) : 0;
  26244. if (mode != 'rotate') {
  26245. if (w <= 0 || h <= 0) {
  26246. fm.error('Invalid image size');
  26247. return false;
  26248. }
  26249. if (w == owidth && h == oheight) {
  26250. fm.error('errResizeNoChange');
  26251. return false;
  26252. }
  26253. }
  26254. return {w: w, h: h, x: x, y: y, d: d, q: q, b: b};
  26255. },
  26256. save = function() {
  26257. var vals;
  26258. if (vals = checkVals()) {
  26259. dialog.elfinderdialog('close');
  26260. self.resizeRequest({
  26261. target : file.hash,
  26262. width : vals.w,
  26263. height : vals.h,
  26264. x : vals.x,
  26265. y : vals.y,
  26266. degree : vals.d,
  26267. quality: vals.q,
  26268. bg : vals.b,
  26269. mode : mode
  26270. }, file, dfrd);
  26271. }
  26272. },
  26273. saveAs = function() {
  26274. var fail = function() {
  26275. dialogs.addClass(clsediting).fadeIn(function() {
  26276. base.addClass(clactive);
  26277. });
  26278. fm.disable();
  26279. },
  26280. make = function() {
  26281. self.mime = file.mime;
  26282. self.prefix = file.name.replace(/ \d+(\.[^.]+)?$/, '$1');
  26283. self.requestCmd = 'mkfile';
  26284. self.nextAction = {};
  26285. self.data = {target : file.phash};
  26286. $.proxy(fm.res('mixin', 'make'), self)()
  26287. .done(function(data) {
  26288. var hash, dfd;
  26289. if (data.added && data.added.length) {
  26290. hash = data.added[0].hash;
  26291. dfd = fm.api < 2.1032? fm.url(file.hash, { async: true, temporary: true }) : null;
  26292. $.when(dfd).done(function(url) {
  26293. fm.request({
  26294. options : {type : 'post'},
  26295. data : {
  26296. cmd : 'put',
  26297. target : hash,
  26298. encoding: dfd? 'scheme' : 'hash',
  26299. content : dfd? fm.convAbsUrl(url) : file.hash
  26300. },
  26301. notify : {type : 'copy', cnt : 1},
  26302. syncOnFail : true
  26303. })
  26304. .fail(fail)
  26305. .done(function(data) {
  26306. data = fm.normalize(data);
  26307. fm.updateCache(data);
  26308. file = fm.file(hash);
  26309. data.changed && data.changed.length && fm.change(data);
  26310. base.show().find('.elfinder-dialog-title').html(fm.escape(file.name));
  26311. save();
  26312. dialogs.fadeIn();
  26313. });
  26314. }).fail(fail);
  26315. } else {
  26316. fail();
  26317. }
  26318. })
  26319. .fail(fail)
  26320. .always(function() {
  26321. delete self.mime;
  26322. delete self.prefix;
  26323. delete self.nextAction;
  26324. delete self.data;
  26325. });
  26326. fm.trigger('unselectfiles', { files: [ file.hash ] });
  26327. },
  26328. reqOpen = null,
  26329. dialogs;
  26330. if (checkVals()) {
  26331. dialogs = fmnode.children('.' + dlcls + ':visible').removeClass(clsediting).fadeOut();
  26332. base.removeClass(clactive);
  26333. fm.enable();
  26334. if (fm.searchStatus.state < 2 && file.phash !== fm.cwd().hash) {
  26335. reqOpen = fm.exec('open', [file.phash], {thash: file.phash});
  26336. }
  26337. $.when([reqOpen]).done(function() {
  26338. reqOpen? fm.one('cwdrender', make) : make();
  26339. }).fail(fail);
  26340. }
  26341. },
  26342. buttons = {},
  26343. hline = 'elfinder-resize-handle-hline',
  26344. vline = 'elfinder-resize-handle-vline',
  26345. rpoint = 'elfinder-resize-handle-point',
  26346. src = fm.openUrl(file.hash),
  26347. canvSrc = fm.openUrl(file.hash, !fm.isSameOrigin(src)),
  26348. sizeImg = quality? $('<img>').attr('crossorigin', fm.isCORS? 'use-credentials' : '').attr('src', canvSrc).on('load', function() {
  26349. try {
  26350. var canv = document.createElement('canvas');
  26351. sizeImg.data('canvas', canv).data('ctx', canv.getContext('2d'));
  26352. jpgCalc();
  26353. } catch(e) {
  26354. sizeImg.removeData('canvas').removeData('ctx');
  26355. }
  26356. }) : null,
  26357. jpgCalc = function() {
  26358. control.find('input.elfinder-resize-quality:visible').trigger('change');
  26359. },
  26360. dinit = function(e) {
  26361. if (base.hasClass('elfinder-dialog-minimized') || base.is(':hidden')) {
  26362. return;
  26363. }
  26364. preset.hide();
  26365. presetc.hide();
  26366. var win = fm.options.dialogContained? fmnode : $(window),
  26367. winH = win.height(),
  26368. winW = win.width(),
  26369. presW = 'auto',
  26370. presIn = true,
  26371. dw, ctrW, prvW;
  26372. base.width(Math.min(dialogWidth, winW - 30));
  26373. preview.attr('style', '');
  26374. if (owidth && oheight) {
  26375. pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width());
  26376. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  26377. resize.updateView(owidth, oheight);
  26378. }
  26379. ctrW = dialog.find('div.elfinder-resize-control').width();
  26380. prvW = preview.width();
  26381. dw = dialog.width() - 20;
  26382. if (prvW > dw) {
  26383. preview.width(dw);
  26384. presIn = false;
  26385. } else if ((dw - prvW) < ctrW) {
  26386. if (winW > winH) {
  26387. preview.width(dw - ctrW - 20);
  26388. } else {
  26389. preview.css({ float: 'none', marginLeft: 'auto', marginRight: 'auto'});
  26390. presIn = false;
  26391. }
  26392. }
  26393. if (presIn) {
  26394. presW = ctrW;
  26395. }
  26396. pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width());
  26397. if (fmnode.hasClass('elfinder-fullscreen')) {
  26398. if (base.height() > winH) {
  26399. winH -= 2;
  26400. preview.height(winH - base.height() + preview.height());
  26401. base.css('top', 0 - fmnode.offset().top);
  26402. }
  26403. } else {
  26404. winH -= 30;
  26405. (preview.height() > winH) && preview.height(winH);
  26406. }
  26407. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  26408. if (owidth && oheight) {
  26409. setupimg();
  26410. }
  26411. if (img.height() && preview.height() > img.height() + 20) {
  26412. preview.height(img.height() + 20);
  26413. pheight = preview.height() - (rhandle.outerHeight() - rhandle.height());
  26414. setuprimg();
  26415. }
  26416. preset.css('width', presW).show();
  26417. presetc.css('width', presW).show();
  26418. if (!presetc.children('span.elfinder-resize-preset:visible').length) {
  26419. presetc.hide();
  26420. }
  26421. },
  26422. preset = (function() {
  26423. var sets = $('<fieldset class="elfinder-resize-preset-container">').append($('<legend>').html(fm.i18n('presets'))).hide(),
  26424. hasC;
  26425. $.each(presetSize, function(i, s) {
  26426. if (s.length === 2) {
  26427. hasC = true;
  26428. sets.append($('<span class="elfinder-resize-preset"/>')
  26429. .data('s', s)
  26430. .text(s[0]+'x'+s[1])
  26431. .button()
  26432. );
  26433. }
  26434. });
  26435. if (!hasC) {
  26436. return $();
  26437. } else {
  26438. return sets;
  26439. }
  26440. })(),
  26441. presetc = preset.clone(true),
  26442. useSaveAs = fm.uploadMimeCheck(file.mime, file.phash),
  26443. dMinBtn, base;
  26444. uiresize.append(
  26445. $(row).append($(label).text(fm.i18n('width')), width),
  26446. $(row).append($(label).text(fm.i18n('height')), height, $('<div class="elfinder-resize-whctrls">').append(constr, reset)),
  26447. (quality? $(row).append($(label).text(fm.i18n('quality')), quality, $('<span/>')) : $()),
  26448. (isJpeg? $(row).append($(label).text(fm.i18n('8pxgrid')).addClass('elfinder-resize-grid8'), grid8px) : $()),
  26449. $(row).append($(label).text(fm.i18n('scale')), uiprop),
  26450. $(row).append(preset)
  26451. );
  26452. if (api2) {
  26453. uicrop.append(
  26454. $(row).append($(label).text('X'), pointX),
  26455. $(row).append($(label).text('Y')).append(pointY),
  26456. $(row).append($(label).text(fm.i18n('width')), offsetX),
  26457. $(row).append($(label).text(fm.i18n('height')), offsetY, $('<div class="elfinder-resize-whctrls">').append(constrc, reset.clone(true))),
  26458. (quality? $(row).append($(label).text(fm.i18n('quality')), quality.clone(true), $('<span/>')) : $()),
  26459. (isJpeg? $(row).append($(label).text(fm.i18n('8pxgrid')).addClass('elfinder-resize-grid8')) : $()),
  26460. $(row).append(presetc)
  26461. );
  26462. uirotate.append(
  26463. $(row).addClass('elfinder-resize-degree').append(
  26464. $(label).text(fm.i18n('rotate')),
  26465. degree,
  26466. $('<span/>').text(fm.i18n('degree')),
  26467. $('<div/>').append(uideg270, uideg90)[ctrgrup]()
  26468. ),
  26469. $(row).css('height', '20px').append(uidegslider),
  26470. ((quality)? $(row)[losslessRotate < 1? 'show' : 'hide']().addClass('elfinder-resize-quality').append(
  26471. $(label).text(fm.i18n('quality')),
  26472. quality.clone(true),
  26473. $('<span/>')) : $()
  26474. ),
  26475. $(row).append($(label).text(fm.i18n('bgcolor')), bg, picker, reseter),
  26476. $(row).css('height', '20px').append(pallet)
  26477. );
  26478. uideg270.on('click', function() {
  26479. rdegree = rdegree - 90;
  26480. rotate.update(rdegree);
  26481. });
  26482. uideg90.on('click', function(){
  26483. rdegree = rdegree + 90;
  26484. rotate.update(rdegree);
  26485. });
  26486. }
  26487. dialog.append(uitype).on('resize', function(e){
  26488. e.stopPropagation();
  26489. });
  26490. if (api2) {
  26491. control.append(/*$(row), */uiresize, uicrop.hide(), uirotate.hide());
  26492. } else {
  26493. control.append(/*$(row), */uiresize);
  26494. }
  26495. rhandle.append('<div class="'+hline+' '+hline+'-top"/>',
  26496. '<div class="'+hline+' '+hline+'-bottom"/>',
  26497. '<div class="'+vline+' '+vline+'-left"/>',
  26498. '<div class="'+vline+' '+vline+'-right"/>',
  26499. '<div class="'+rpoint+' '+rpoint+'-e"/>',
  26500. '<div class="'+rpoint+' '+rpoint+'-se"/>',
  26501. '<div class="'+rpoint+' '+rpoint+'-s"/>');
  26502. preview.append(spinner).append(rhandle.hide()).append(img.hide());
  26503. if (api2) {
  26504. rhandlec.css('position', 'absolute')
  26505. .append('<div class="'+hline+' '+hline+'-top"/>',
  26506. '<div class="'+hline+' '+hline+'-bottom"/>',
  26507. '<div class="'+vline+' '+vline+'-left"/>',
  26508. '<div class="'+vline+' '+vline+'-right"/>',
  26509. '<div class="'+rpoint+' '+rpoint+'-n"/>',
  26510. '<div class="'+rpoint+' '+rpoint+'-e"/>',
  26511. '<div class="'+rpoint+' '+rpoint+'-s"/>',
  26512. '<div class="'+rpoint+' '+rpoint+'-w"/>',
  26513. '<div class="'+rpoint+' '+rpoint+'-ne"/>',
  26514. '<div class="'+rpoint+' '+rpoint+'-se"/>',
  26515. '<div class="'+rpoint+' '+rpoint+'-sw"/>',
  26516. '<div class="'+rpoint+' '+rpoint+'-nw"/>');
  26517. preview.append(basec.css('position', 'absolute').hide().append(imgc, rhandlec.append(coverc)));
  26518. preview.append(imgr.hide());
  26519. }
  26520. preview.css('overflow', 'hidden');
  26521. dialog.append(preview, control);
  26522. buttons[fm.i18n('btnApply')] = save;
  26523. if (useSaveAs) {
  26524. buttons[fm.i18n('btnSaveAs')] = function() { setTimeout(saveAs, 10); };
  26525. }
  26526. buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); };
  26527. dialog.find('input,button').addClass('elfinder-tabstop');
  26528. base = fm.dialog(dialog, {
  26529. title : fm.escape(file.name),
  26530. width : dialogWidth,
  26531. resizable : false,
  26532. buttons : buttons,
  26533. open : function() {
  26534. var substituteImg = (fm.option('substituteImg', file.hash) && file.size > options.dimSubImgSize)? true : false,
  26535. hasSize = (file.width && file.height)? true : false;
  26536. dialog.parent().css('overflow', 'hidden');
  26537. dMinBtn = base.find('.ui-dialog-titlebar .elfinder-titlebar-minimize').hide();
  26538. fm.bind('resize', dinit);
  26539. img.attr('src', src);
  26540. imgc.attr('src', src);
  26541. imgr.attr('src', src);
  26542. if (api2) {
  26543. imgr.on('mousedown touchstart', rotate.start)
  26544. .on('touchend', rotate.stop);
  26545. $(document).on('mouseup', rotate.stop);
  26546. }
  26547. if (hasSize && !substituteImg) {
  26548. return init();
  26549. }
  26550. if (file.size > (options.getDimThreshold || 0)) {
  26551. dimreq = fm.request({
  26552. data : {cmd : 'dim', target : file.hash, substitute : (substituteImg? 400 : '')},
  26553. preventDefault : true
  26554. })
  26555. .done(function(data) {
  26556. if (data.dim) {
  26557. var dim = data.dim.split('x');
  26558. file.width = dim[0];
  26559. file.height = dim[1];
  26560. setdim(dim);
  26561. if (data.url) {
  26562. img.attr('src', data.url);
  26563. imgc.attr('src', data.url);
  26564. imgr.attr('src', data.url);
  26565. }
  26566. return init();
  26567. }
  26568. });
  26569. } else if (hasSize) {
  26570. return init();
  26571. }
  26572. },
  26573. close : function() {
  26574. if (api2) {
  26575. imgr.off('mousedown touchstart', rotate.start)
  26576. .off('touchend', rotate.stop);
  26577. $(document).off('mouseup', rotate.stop);
  26578. }
  26579. fm.unbind('resize', dinit);
  26580. $(this).elfinderdialog('destroy');
  26581. },
  26582. resize : function(e, data) {
  26583. if (data && data.minimize === 'off') {
  26584. dinit();
  26585. }
  26586. }
  26587. }).attr('id', id).closest('.ui-dialog').addClass(dlcls + ' ' + clsediting);
  26588. // for IE < 9 dialog mising at open second+ time.
  26589. if (fm.UA.ltIE8) {
  26590. $('.elfinder-dialog').css('filter', '');
  26591. }
  26592. coverc.css({ 'opacity': 0.2, 'background-color': '#fff', 'position': 'absolute'}),
  26593. rhandlec.css('cursor', 'move');
  26594. rhandlec.find('.elfinder-resize-handle-point').css({
  26595. 'background-color' : '#fff',
  26596. 'opacity': 0.5,
  26597. 'border-color':'#000'
  26598. });
  26599. if (! api2) {
  26600. uitype.find('.api2').remove();
  26601. }
  26602. control.find('input,select').prop('disabled', true);
  26603. control.find('input.elfinder-resize-quality')
  26604. .next('span').addClass('elfinder-resize-jpgsize').attr('title', fm.i18n('roughFileSize'));
  26605. },
  26606. id, dialog
  26607. ;
  26608. if (!files.length || files[0].mime.indexOf('image/') === -1) {
  26609. return dfrd.reject();
  26610. }
  26611. id = 'resize-'+fm.namespace+'-'+files[0].hash;
  26612. dialog = fmnode.find('#'+id);
  26613. if (dialog.length) {
  26614. dialog.elfinderdialog('toTop');
  26615. return dfrd.resolve();
  26616. }
  26617. open(files[0], id);
  26618. return dfrd;
  26619. };
  26620. };
  26621. (function ($) {
  26622. var findProperty = function (styleObject, styleArgs) {
  26623. var i = 0 ;
  26624. for( i in styleArgs) {
  26625. if (typeof styleObject[styleArgs[i]] != 'undefined')
  26626. return styleArgs[i];
  26627. }
  26628. styleObject[styleArgs[i]] = '';
  26629. return styleArgs[i];
  26630. };
  26631. $.cssHooks.rotate = {
  26632. get: function(elem, computed, extra) {
  26633. return $(elem).rotate();
  26634. },
  26635. set: function(elem, value) {
  26636. $(elem).rotate(value);
  26637. return value;
  26638. }
  26639. };
  26640. $.cssHooks.transform = {
  26641. get: function(elem, computed, extra) {
  26642. var name = findProperty( elem.style ,
  26643. ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] );
  26644. return elem.style[name];
  26645. },
  26646. set: function(elem, value) {
  26647. var name = findProperty( elem.style ,
  26648. ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] );
  26649. elem.style[name] = value;
  26650. return value;
  26651. }
  26652. };
  26653. $.fn.rotate = function(val) {
  26654. var r;
  26655. if (typeof val == 'undefined') {
  26656. if (!!window.opera) {
  26657. r = this.css('transform').match(/rotate\((.*?)\)/);
  26658. return ( r && r[1])?
  26659. Math.round(parseFloat(r[1]) * 180 / Math.PI) : 0;
  26660. } else {
  26661. r = this.css('transform').match(/rotate\((.*?)\)/);
  26662. return ( r && r[1])? parseInt(r[1]) : 0;
  26663. }
  26664. }
  26665. this.css('transform',
  26666. this.css('transform').replace(/none|rotate\(.*?\)/, '') + 'rotate(' + parseInt(val) + 'deg)');
  26667. return this;
  26668. };
  26669. $.fx.step.rotate = function(fx) {
  26670. if ( fx.state == 0 ) {
  26671. fx.start = $(fx.elem).rotate();
  26672. fx.now = fx.start;
  26673. }
  26674. $(fx.elem).rotate(fx.now);
  26675. };
  26676. if (typeof window.addEventListener == "undefined" && typeof document.getElementsByClassName == "undefined") { // IE & IE<9
  26677. var GetAbsoluteXY = function(element) {
  26678. var pnode = element;
  26679. var x = pnode.offsetLeft;
  26680. var y = pnode.offsetTop;
  26681. while ( pnode.offsetParent ) {
  26682. pnode = pnode.offsetParent;
  26683. if (pnode != document.body && pnode.currentStyle['position'] != 'static') {
  26684. break;
  26685. }
  26686. if (pnode != document.body && pnode != document.documentElement) {
  26687. x -= pnode.scrollLeft;
  26688. y -= pnode.scrollTop;
  26689. }
  26690. x += pnode.offsetLeft;
  26691. y += pnode.offsetTop;
  26692. }
  26693. return { x: x, y: y };
  26694. };
  26695. var StaticToAbsolute = function (element) {
  26696. if ( element.currentStyle['position'] != 'static') {
  26697. return ;
  26698. }
  26699. var xy = GetAbsoluteXY(element);
  26700. element.style.position = 'absolute' ;
  26701. element.style.left = xy.x + 'px';
  26702. element.style.top = xy.y + 'px';
  26703. };
  26704. var IETransform = function(element,transform){
  26705. var r;
  26706. var m11 = 1;
  26707. var m12 = 1;
  26708. var m21 = 1;
  26709. var m22 = 1;
  26710. if (typeof element.style['msTransform'] != 'undefined'){
  26711. return true;
  26712. }
  26713. StaticToAbsolute(element);
  26714. r = transform.match(/rotate\((.*?)\)/);
  26715. var rotate = ( r && r[1]) ? parseInt(r[1]) : 0;
  26716. rotate = rotate % 360;
  26717. if (rotate < 0) rotate = 360 + rotate;
  26718. var radian= rotate * Math.PI / 180;
  26719. var cosX =Math.cos(radian);
  26720. var sinY =Math.sin(radian);
  26721. m11 *= cosX;
  26722. m12 *= -sinY;
  26723. m21 *= sinY;
  26724. m22 *= cosX;
  26725. element.style.filter = (element.style.filter || '').replace(/progid:DXImageTransform\.Microsoft\.Matrix\([^)]*\)/, "" ) +
  26726. ("progid:DXImageTransform.Microsoft.Matrix(" +
  26727. "M11=" + m11 +
  26728. ",M12=" + m12 +
  26729. ",M21=" + m21 +
  26730. ",M22=" + m22 +
  26731. ",FilterType='bilinear',sizingMethod='auto expand')")
  26732. ;
  26733. var ow = parseInt(element.style.width || element.width || 0 );
  26734. var oh = parseInt(element.style.height || element.height || 0 );
  26735. radian = rotate * Math.PI / 180;
  26736. var absCosX =Math.abs(Math.cos(radian));
  26737. var absSinY =Math.abs(Math.sin(radian));
  26738. var dx = (ow - (ow * absCosX + oh * absSinY)) / 2;
  26739. var dy = (oh - (ow * absSinY + oh * absCosX)) / 2;
  26740. element.style.marginLeft = Math.floor(dx) + "px";
  26741. element.style.marginTop = Math.floor(dy) + "px";
  26742. return(true);
  26743. };
  26744. var transform_set = $.cssHooks.transform.set;
  26745. $.cssHooks.transform.set = function(elem, value) {
  26746. transform_set.apply(this, [elem, value] );
  26747. IETransform(elem,value);
  26748. return value;
  26749. };
  26750. }
  26751. })(jQuery);
  26752. /*
  26753. * File: /js/commands/restore.js
  26754. */
  26755. /**
  26756. * @class elFinder command "restore"
  26757. * Restore items from the trash
  26758. *
  26759. * @author Naoki Sawada
  26760. **/
  26761. (elFinder.prototype.commands.restore = function() {
  26762. "use strict";
  26763. var self = this,
  26764. fm = this.fm,
  26765. fakeCnt = 0,
  26766. getFilesRecursively = function(files) {
  26767. var dfd = $.Deferred(),
  26768. dirs = [],
  26769. results = [],
  26770. reqs = [],
  26771. phashes = [],
  26772. getFile;
  26773. dfd._xhrReject = function() {
  26774. $.each(reqs, function() {
  26775. this && this.reject && this.reject();
  26776. });
  26777. getFile && getFile._xhrReject();
  26778. };
  26779. $.each(files, function(i, f) {
  26780. f.mime === 'directory'? dirs.push(f) : results.push(f);
  26781. });
  26782. if (dirs.length) {
  26783. $.each(dirs, function(i, d) {
  26784. reqs.push(fm.request({
  26785. data : {cmd : 'open', target : d.hash},
  26786. preventDefault : true,
  26787. asNotOpen : true
  26788. }));
  26789. phashes[i] = d.hash;
  26790. });
  26791. $.when.apply($, reqs).fail(function() {
  26792. dfd.reject();
  26793. }).done(function() {
  26794. var items = [];
  26795. $.each(arguments, function(i, r) {
  26796. var files;
  26797. if (r.files) {
  26798. if (r.files.length) {
  26799. items = items.concat(r.files);
  26800. } else {
  26801. items.push({
  26802. hash: 'fakefile_' + (fakeCnt++),
  26803. phash: phashes[i],
  26804. mime: 'fakefile',
  26805. name: 'fakefile',
  26806. ts: 0
  26807. });
  26808. }
  26809. }
  26810. });
  26811. fm.cache(items);
  26812. getFile = getFilesRecursively(items).done(function(res) {
  26813. results = results.concat(res);
  26814. dfd.resolve(results);
  26815. });
  26816. });
  26817. } else {
  26818. dfd.resolve(results);
  26819. }
  26820. return dfd;
  26821. },
  26822. restore = function(dfrd, files, targets, ops) {
  26823. var rHashes = {},
  26824. others = [],
  26825. found = false,
  26826. dirs = [],
  26827. opts = ops || {},
  26828. id = +new Date(),
  26829. tm, getFile;
  26830. fm.lockfiles({files : targets});
  26831. dirs = $.map(files, function(f) {
  26832. return f.mime === 'directory'? f.hash : null;
  26833. });
  26834. dfrd.done(function() {
  26835. dirs && fm.exec('rm', dirs, {forceRm : true, quiet : true});
  26836. }).always(function() {
  26837. fm.unlockfiles({files : targets});
  26838. });
  26839. tm = setTimeout(function() {
  26840. fm.notify({type : 'search', id : id, cnt : 1, hideCnt : true, cancel : function() {
  26841. getFile && getFile._xhrReject();
  26842. dfrd.reject();
  26843. }});
  26844. }, fm.notifyDelay);
  26845. fakeCnt = 0;
  26846. getFile = getFilesRecursively(files).always(function() {
  26847. tm && clearTimeout(tm);
  26848. fm.notify({type : 'search', id: id, cnt : -1, hideCnt : true});
  26849. }).fail(function() {
  26850. dfrd.reject('errRestore', 'errFileNotFound');
  26851. }).done(function(res) {
  26852. var errFolderNotfound = ['errRestore', 'errFolderNotFound'],
  26853. dirTop = '';
  26854. if (res.length) {
  26855. $.each(res, function(i, f) {
  26856. var phash = f.phash,
  26857. pfile,
  26858. srcRoot, tPath;
  26859. while(phash) {
  26860. if (srcRoot = fm.trashes[phash]) {
  26861. if (! rHashes[srcRoot]) {
  26862. if (found) {
  26863. // Keep items of other trash
  26864. others.push(f.hash);
  26865. return null; // continue $.each
  26866. }
  26867. rHashes[srcRoot] = {};
  26868. found = true;
  26869. }
  26870. tPath = fm.path(f.hash).substr(fm.path(phash).length).replace(/\\/g, '/');
  26871. tPath = tPath.replace(/\/[^\/]+?$/, '');
  26872. if (tPath === '') {
  26873. tPath = '/';
  26874. }
  26875. if (!rHashes[srcRoot][tPath]) {
  26876. rHashes[srcRoot][tPath] = [];
  26877. }
  26878. if (f.mime === 'fakefile') {
  26879. fm.updateCache({removed:[f.hash]});
  26880. } else {
  26881. rHashes[srcRoot][tPath].push(f.hash);
  26882. }
  26883. if (!dirTop || dirTop.length > tPath.length) {
  26884. dirTop = tPath;
  26885. }
  26886. break;
  26887. }
  26888. // Go up one level for next check
  26889. pfile = fm.file(phash);
  26890. if (!pfile) {
  26891. phash = false;
  26892. // Detection method for search results
  26893. $.each(fm.trashes, function(ph) {
  26894. var file = fm.file(ph),
  26895. filePath = fm.path(ph);
  26896. if ((!file.volumeid || f.hash.indexOf(file.volumeid) === 0) && fm.path(f.hash).indexOf(filePath) === 0) {
  26897. phash = ph;
  26898. return false;
  26899. }
  26900. });
  26901. } else {
  26902. phash = pfile.phash;
  26903. }
  26904. }
  26905. });
  26906. if (found) {
  26907. $.each(rHashes, function(src, dsts) {
  26908. var dirs = Object.keys(dsts),
  26909. cnt = dirs.length;
  26910. fm.request({
  26911. data : {cmd : 'mkdir', target : src, dirs : dirs},
  26912. notify : {type : 'chkdir', cnt : cnt},
  26913. preventFail : true
  26914. }).fail(function(error) {
  26915. dfrd.reject(error);
  26916. fm.unlockfiles({files : targets});
  26917. }).done(function(data) {
  26918. var cmdPaste, hashes;
  26919. if (hashes = data.hashes) {
  26920. cmdPaste = fm.getCommand('paste');
  26921. if (cmdPaste) {
  26922. // wait until file cache made
  26923. fm.one('mkdirdone', function() {
  26924. var hasErr = false;
  26925. $.each(dsts, function(dir, files) {
  26926. if (hashes[dir]) {
  26927. if (files.length) {
  26928. if (fm.file(hashes[dir])) {
  26929. fm.clipboard(files, true);
  26930. fm.exec('paste', [ hashes[dir] ], {_cmd : 'restore', noToast : (opts.noToast || dir !== dirTop)})
  26931. .done(function(data) {
  26932. if (data && (data.error || data.warning)) {
  26933. hasErr = true;
  26934. }
  26935. })
  26936. .fail(function() {
  26937. hasErr = true;
  26938. })
  26939. .always(function() {
  26940. if (--cnt < 1) {
  26941. dfrd[hasErr? 'reject' : 'resolve']();
  26942. if (others.length) {
  26943. // Restore items of other trash
  26944. fm.exec('restore', others);
  26945. }
  26946. }
  26947. });
  26948. } else {
  26949. dfrd.reject(errFolderNotfound);
  26950. }
  26951. } else {
  26952. if (--cnt < 1) {
  26953. dfrd.resolve();
  26954. if (others.length) {
  26955. // Restore items of other trash
  26956. fm.exec('restore', others);
  26957. }
  26958. }
  26959. }
  26960. }
  26961. });
  26962. });
  26963. } else {
  26964. dfrd.reject(['errRestore', 'errCmdNoSupport', '(paste)']);
  26965. }
  26966. } else {
  26967. dfrd.reject(errFolderNotfound);
  26968. }
  26969. });
  26970. });
  26971. } else {
  26972. dfrd.reject(errFolderNotfound);
  26973. }
  26974. } else {
  26975. dfrd.reject('errFileNotFound');
  26976. dirs && fm.exec('rm', dirs, {forceRm : true, quiet : true});
  26977. }
  26978. });
  26979. };
  26980. this.linkedCmds = ['copy', 'paste', 'mkdir', 'rm'];
  26981. this.updateOnSelect = false;
  26982. //this.shortcuts = [{
  26983. // pattern : 'ctrl+z'
  26984. //}];
  26985. this.getstate = function(sel, e) {
  26986. sel = sel || fm.selected();
  26987. return sel.length && $.grep(sel, function(h) {var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? true : false; }).length == sel.length
  26988. ? 0 : -1;
  26989. };
  26990. this.exec = function(hashes, opts) {
  26991. var dfrd = $.Deferred()
  26992. .fail(function(error) {
  26993. error && fm.error(error);
  26994. }),
  26995. files = self.files(hashes);
  26996. if (! files.length) {
  26997. return dfrd.reject();
  26998. }
  26999. $.each(files, function(i, file) {
  27000. if (fm.isRoot(file)) {
  27001. return !dfrd.reject(['errRestore', file.name]);
  27002. }
  27003. if (file.locked) {
  27004. return !dfrd.reject(['errLocked', file.name]);
  27005. }
  27006. });
  27007. if (dfrd.state() === 'pending') {
  27008. restore(dfrd, files, hashes, opts);
  27009. }
  27010. return dfrd;
  27011. };
  27012. }).prototype = { forceLoad : true }; // this is required command
  27013. /*
  27014. * File: /js/commands/rm.js
  27015. */
  27016. /**
  27017. * @class elFinder command "rm"
  27018. * Delete files
  27019. *
  27020. * @author Dmitry (dio) Levashov
  27021. * @author Naoki Sawada
  27022. **/
  27023. elFinder.prototype.commands.rm = function() {
  27024. "use strict";
  27025. var self = this,
  27026. fm = this.fm,
  27027. 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>',
  27028. confirm = function(dfrd, targets, files, tHash, addTexts) {
  27029. var cnt = targets.length,
  27030. cwd = fm.cwd().hash,
  27031. descs = [],
  27032. spinner = '<span class="elfinder-info-spinner"/>' + fm.i18n('calc'),
  27033. dialog, text, tmb, size, f, fname;
  27034. if (cnt > 1) {
  27035. size = 0;
  27036. $.each(files, function(h, f) {
  27037. if (f.size && f.size != 'unknown' && f.mime !== 'directory') {
  27038. var s = parseInt(f.size);
  27039. if (s >= 0 && size >= 0) {
  27040. size += s;
  27041. }
  27042. } else {
  27043. size = 'unknown';
  27044. return false;
  27045. }
  27046. });
  27047. getSize = (size === 'unknown');
  27048. descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(size)));
  27049. text = [$(tpl.replace('{class}', 'elfinder-cwd-icon-group').replace('{title}', '<strong>' + fm.i18n('items')+ ': ' + cnt + '</strong>').replace('{desc}', descs.join('<br>')))];
  27050. } else {
  27051. f = files[0];
  27052. tmb = fm.tmb(f);
  27053. getSize = (f.mime === 'directory');
  27054. descs.push(fm.i18n('size')+': '+(getSize? spinner : fm.formatSize(f.size)));
  27055. descs.push(fm.i18n('modify')+': '+fm.formatDate(f));
  27056. fname = fm.escape(f.i18 || f.name).replace(/([_.])/g, '&#8203;$1');
  27057. text = [$(tpl.replace('{class}', fm.mime2class(f.mime)).replace('{title}', '<strong>' + fname + '</strong>').replace('{desc}', descs.join('<br>')))];
  27058. }
  27059. if (addTexts) {
  27060. text = text.concat(addTexts);
  27061. }
  27062. text.push(tHash? 'confirmTrash' : 'confirmRm');
  27063. dialog = fm.confirm({
  27064. title : self.title,
  27065. text : text,
  27066. accept : {
  27067. label : 'btnRm',
  27068. callback : function() {
  27069. if (tHash) {
  27070. toTrash(dfrd, targets, tHash);
  27071. } else {
  27072. remove(dfrd, targets);
  27073. }
  27074. }
  27075. },
  27076. cancel : {
  27077. label : 'btnCancel',
  27078. callback : function() {
  27079. fm.unlockfiles({files : targets});
  27080. if (targets.length === 1 && fm.file(targets[0]).phash !== cwd) {
  27081. fm.select({selected : targets});
  27082. } else {
  27083. fm.selectfiles({files : targets});
  27084. }
  27085. dfrd.reject();
  27086. }
  27087. }
  27088. });
  27089. // load thumbnail
  27090. if (tmb) {
  27091. $('<img/>')
  27092. .on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
  27093. .attr('src', tmb.url);
  27094. }
  27095. if (getSize) {
  27096. getSize = fm.getSize($.map(files, function(f) { return f.mime === 'directory'? f.hash : null; })).done(function(data) {
  27097. dialog.find('span.elfinder-info-spinner').parent().html(fm.i18n('size')+': '+data.formated);
  27098. }).fail(function() {
  27099. dialog.find('span.elfinder-info-spinner').parent().html(fm.i18n('size')+': '+fm.i18n('unknown'));
  27100. }).always(function() {
  27101. getSize = false;
  27102. });
  27103. }
  27104. },
  27105. toTrash = function(dfrd, targets, tHash) {
  27106. var dsts = {},
  27107. itemCnt = targets.length,
  27108. maxCnt = self.options.toTrashMaxItems,
  27109. checkDirs = [],
  27110. reqDfd = $.Deferred(),
  27111. req, dirs, cnt;
  27112. if (itemCnt > maxCnt) {
  27113. confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]);
  27114. return;
  27115. }
  27116. // Directory preparation preparation and directory enumeration
  27117. $.each(targets, function(i, h) {
  27118. var file = fm.file(h),
  27119. path = fm.path(h).replace(/\\/g, '/'),
  27120. m = path.match(/^[^\/]+?(\/(?:[^\/]+?\/)*)[^\/]+?$/);
  27121. if (file) {
  27122. if (m) {
  27123. m[1] = m[1].replace(/(^\/.*?)\/?$/, '$1');
  27124. if (! dsts[m[1]]) {
  27125. dsts[m[1]] = [];
  27126. }
  27127. dsts[m[1]].push(h);
  27128. }
  27129. if (file.mime === 'directory') {
  27130. checkDirs.push(h);
  27131. }
  27132. }
  27133. });
  27134. // Check directory information
  27135. if (checkDirs.length) {
  27136. req = fm.request({
  27137. data : {cmd : 'size', targets : checkDirs},
  27138. notify : {type: 'readdir', cnt: 1, hideCnt: true},
  27139. preventDefault : true
  27140. }).done(function(data) {
  27141. var cnt = 0;
  27142. data.fileCnt && (cnt += parseInt(data.fileCnt));
  27143. data.dirCnt && (cnt += parseInt(data.dirCnt));
  27144. reqDfd[cnt > maxCnt ? 'reject' : 'resolve']();
  27145. }).fail(function() {
  27146. reqDfd.reject();
  27147. });
  27148. setTimeout(function() {
  27149. var xhr = (req && req.xhr)? req.xhr : null;
  27150. if (xhr && xhr.state() == 'pending') {
  27151. req.syncOnFail(false);
  27152. req.reject();
  27153. reqDfd.reject();
  27154. }
  27155. }, self.options.infoCheckWait * 1000);
  27156. } else {
  27157. reqDfd.resolve();
  27158. }
  27159. // Directory creation and paste command execution
  27160. reqDfd.done(function() {
  27161. dirs = Object.keys(dsts);
  27162. cnt = dirs.length;
  27163. if (cnt) {
  27164. fm.request({
  27165. data : {cmd : 'mkdir', target : tHash, dirs : dirs},
  27166. notify : {type : 'chkdir', cnt : cnt},
  27167. preventFail : true
  27168. })
  27169. .fail(function(error) {
  27170. dfrd.reject(error);
  27171. fm.unlockfiles({files : targets});
  27172. })
  27173. .done(function(data) {
  27174. var margeRes = function(data, phash, reqData) {
  27175. var undo, prevUndo, redo, prevRedo;
  27176. $.each(data, function(k, v) {
  27177. if (Array.isArray(v)) {
  27178. if (res[k]) {
  27179. res[k] = res[k].concat(v);
  27180. } else {
  27181. res[k] = v;
  27182. }
  27183. }
  27184. });
  27185. if (data.sync) {
  27186. res.sync = 1;
  27187. }
  27188. if (data.added && data.added.length) {
  27189. undo = function() {
  27190. var targets = [],
  27191. dirs = $.map(data.added, function(f) { return f.mime === 'directory'? f.hash : null; });
  27192. $.each(data.added, function(i, f) {
  27193. if ($.inArray(f.phash, dirs) === -1) {
  27194. targets.push(f.hash);
  27195. }
  27196. });
  27197. return fm.exec('restore', targets, {noToast: true});
  27198. };
  27199. redo = function() {
  27200. return fm.request({
  27201. data : reqData,
  27202. notify : {type : 'redo', cnt : targets.length}
  27203. });
  27204. };
  27205. if (res.undo) {
  27206. prevUndo = res.undo;
  27207. res.undo = function() {
  27208. undo();
  27209. prevUndo();
  27210. };
  27211. } else {
  27212. res.undo = undo;
  27213. }
  27214. if (res.redo) {
  27215. prevRedo = res.redo;
  27216. res.redo = function() {
  27217. redo();
  27218. prevRedo();
  27219. };
  27220. } else {
  27221. res.redo = redo;
  27222. }
  27223. }
  27224. },
  27225. err = ['errTrash'],
  27226. res = {},
  27227. hasNtf = function() {
  27228. return fm.ui.notify.children('.elfinder-notify-trash').length;
  27229. },
  27230. hashes, tm, prg, prgSt;
  27231. if (hashes = data.hashes) {
  27232. prg = 1 / cnt * 100;
  27233. prgSt = cnt === 1? 100 : 5;
  27234. tm = setTimeout(function() {
  27235. fm.notify({type : 'trash', cnt : 1, hideCnt : true, progress : prgSt});
  27236. }, fm.notifyDelay);
  27237. $.each(dsts, function(dir, files) {
  27238. var reqData;
  27239. if (hashes[dir]) {
  27240. reqData = {cmd : 'paste', dst : hashes[dir], targets : files, cut : 1};
  27241. fm.request({
  27242. data : reqData,
  27243. preventDefault : true
  27244. })
  27245. .fail(function(error) {
  27246. if (error) {
  27247. err = err.concat(error);
  27248. }
  27249. })
  27250. .done(function(data) {
  27251. var phash = fm.file(files[0]).phash;
  27252. data = fm.normalize(data);
  27253. fm.updateCache(data);
  27254. margeRes(data, phash, reqData);
  27255. if (data.warning) {
  27256. err = err.concat(data.warning);
  27257. delete data.warning;
  27258. }
  27259. // fire some event to update cache/ui
  27260. data.removed && data.removed.length && fm.remove(data);
  27261. data.added && data.added.length && fm.add(data);
  27262. data.changed && data.changed.length && fm.change(data);
  27263. // fire event with command name
  27264. fm.trigger('paste', data);
  27265. // fire event with command name + 'done'
  27266. fm.trigger('pastedone');
  27267. // force update content
  27268. data.sync && fm.sync();
  27269. })
  27270. .always(function() {
  27271. var hashes = [], addTexts, end = 2;
  27272. if (hasNtf()) {
  27273. fm.notify({type : 'trash', cnt : 0, hideCnt : true, progress : prg});
  27274. } else {
  27275. prgSt+= prg;
  27276. }
  27277. if (--cnt < 1) {
  27278. tm && clearTimeout(tm);
  27279. hasNtf() && fm.notify({type : 'trash', cnt : -1});
  27280. fm.unlockfiles({files : targets});
  27281. if (Object.keys(res).length) {
  27282. if (err.length > 1) {
  27283. if (res.removed || res.removed.length) {
  27284. hashes = $.grep(targets, function(h) {
  27285. return $.inArray(h, res.removed) === -1? true : false;
  27286. });
  27287. }
  27288. if (hashes.length) {
  27289. if (err.length > end) {
  27290. end = (fm.messages[err[end-1]] || '').indexOf('$') === -1? end : end + 1;
  27291. }
  27292. dfrd.reject();
  27293. fm.exec('rm', hashes, { addTexts: err.slice(0, end), forceRm: true });
  27294. } else {
  27295. fm.error(err);
  27296. }
  27297. }
  27298. res._noSound = true;
  27299. if (res.undo && res.redo) {
  27300. res.undo = {
  27301. cmd : 'trash',
  27302. callback : res.undo,
  27303. };
  27304. res.redo = {
  27305. cmd : 'trash',
  27306. callback : res.redo
  27307. };
  27308. }
  27309. dfrd.resolve(res);
  27310. } else {
  27311. dfrd.reject(err);
  27312. }
  27313. }
  27314. });
  27315. }
  27316. });
  27317. } else {
  27318. dfrd.reject('errFolderNotFound');
  27319. fm.unlockfiles({files : targets});
  27320. }
  27321. });
  27322. } else {
  27323. dfrd.reject(['error', 'The folder hierarchy to be deleting can not be determined.']);
  27324. fm.unlockfiles({files : targets});
  27325. }
  27326. }).fail(function() {
  27327. confirm(dfrd, targets, self.files(targets), null, [fm.i18n('tooManyToTrash')]);
  27328. });
  27329. },
  27330. remove = function(dfrd, targets, quiet) {
  27331. var notify = quiet? {} : {type : 'rm', cnt : targets.length};
  27332. fm.request({
  27333. data : {cmd : 'rm', targets : targets},
  27334. notify : notify,
  27335. preventFail : true
  27336. })
  27337. .fail(function(error) {
  27338. dfrd.reject(error);
  27339. })
  27340. .done(function(data) {
  27341. if (data.error || data.warning) {
  27342. data.sync = true;
  27343. }
  27344. dfrd.resolve(data);
  27345. })
  27346. .always(function() {
  27347. fm.unlockfiles({files : targets});
  27348. });
  27349. },
  27350. getTHash = function(targets) {
  27351. var thash = null,
  27352. root1st;
  27353. if (targets && targets.length) {
  27354. if (targets.length > 1 && fm.searchStatus.state === 2) {
  27355. root1st = fm.file(fm.root(targets[0])).volumeid;
  27356. if (!$.grep(targets, function(h) { return h.indexOf(root1st) !== 0? true : false ; }).length) {
  27357. thash = fm.option('trashHash', targets[0]);
  27358. }
  27359. } else {
  27360. thash = fm.option('trashHash', targets[0]);
  27361. }
  27362. }
  27363. return thash;
  27364. },
  27365. getSize = false;
  27366. this.syncTitleOnChange = true;
  27367. this.updateOnSelect = false;
  27368. this.shortcuts = [{
  27369. pattern : 'delete ctrl+backspace shift+delete'
  27370. }];
  27371. this.handlers = {
  27372. 'select' : function(e) {
  27373. var targets = e.data && e.data.selected && e.data.selected.length? e.data.selected : null;
  27374. self.update(void(0), (targets? getTHash(targets) : fm.option('trashHash'))? 'trash' : 'rm');
  27375. }
  27376. };
  27377. this.value = 'rm';
  27378. this.init = function() {
  27379. self.change(function() {
  27380. var targets;
  27381. delete self.extra;
  27382. self.title = fm.i18n('cmd' + self.value);
  27383. self.className = self.value;
  27384. self.button && self.button.children('span.elfinder-button-icon')[self.value === 'trash'? 'addClass' : 'removeClass']('elfinder-button-icon-trash');
  27385. if (self.value === 'trash') {
  27386. self.extra = {
  27387. icon: 'rm',
  27388. node: $('<span/>')
  27389. .attr({title: fm.i18n('cmdrm')})
  27390. .on('ready', function(e, data) {
  27391. targets = data.targets;
  27392. })
  27393. .on('click touchstart', function(e){
  27394. if (e.type === 'touchstart' && e.originalEvent.touches.length > 1) {
  27395. return;
  27396. }
  27397. e.stopPropagation();
  27398. e.preventDefault();
  27399. fm.getUI().trigger('click'); // to close the context menu immediately
  27400. fm.exec('rm', targets, {_userAction: true, forceRm : true});
  27401. })
  27402. };
  27403. }
  27404. });
  27405. };
  27406. this.getstate = function(select) {
  27407. var sel = this.hashes(select);
  27408. return sel.length && $.grep(sel, function(h) { var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? true : false; }).length == sel.length
  27409. ? 0 : -1;
  27410. };
  27411. this.exec = function(hashes, cOpts) {
  27412. var opts = cOpts || {},
  27413. dfrd = $.Deferred()
  27414. .always(function() {
  27415. if (getSize && getSize.state && getSize.state() === 'pending') {
  27416. getSize.reject();
  27417. }
  27418. })
  27419. .fail(function(error) {
  27420. error && fm.error(error);
  27421. }).done(function(data) {
  27422. !opts.quiet && !data._noSound && data.removed && data.removed.length && fm.trigger('playsound', {soundFile : 'rm.wav'});
  27423. }),
  27424. files = self.files(hashes),
  27425. cnt = files.length,
  27426. tHash = null,
  27427. addTexts = opts.addTexts? opts.addTexts : null,
  27428. forceRm = opts.forceRm,
  27429. quiet = opts.quiet,
  27430. targets;
  27431. if (! cnt) {
  27432. return dfrd.reject();
  27433. }
  27434. $.each(files, function(i, file) {
  27435. if (fm.isRoot(file)) {
  27436. return !dfrd.reject(['errRm', file.name, 'errPerm']);
  27437. }
  27438. if (file.locked) {
  27439. return !dfrd.reject(['errLocked', file.name]);
  27440. }
  27441. });
  27442. if (dfrd.state() === 'pending') {
  27443. targets = self.hashes(hashes);
  27444. cnt = files.length;
  27445. if (forceRm || (self.event && self.event.originalEvent && self.event.originalEvent.shiftKey)) {
  27446. tHash = '';
  27447. self.title = fm.i18n('cmdrm');
  27448. }
  27449. if (tHash === null) {
  27450. tHash = getTHash(targets);
  27451. }
  27452. fm.lockfiles({files : targets});
  27453. if (tHash && self.options.quickTrash) {
  27454. toTrash(dfrd, targets, tHash);
  27455. } else {
  27456. if (quiet) {
  27457. remove(dfrd, targets, quiet);
  27458. } else {
  27459. confirm(dfrd, targets, files, tHash, addTexts);
  27460. }
  27461. }
  27462. }
  27463. return dfrd;
  27464. };
  27465. };
  27466. /*
  27467. * File: /js/commands/search.js
  27468. */
  27469. /**
  27470. * @class elFinder command "search"
  27471. * Find files
  27472. *
  27473. * @author Dmitry (dio) Levashov
  27474. **/
  27475. elFinder.prototype.commands.search = function() {
  27476. "use strict";
  27477. this.title = 'Find files';
  27478. this.options = {ui : 'searchbutton'};
  27479. this.alwaysEnabled = true;
  27480. this.updateOnSelect = false;
  27481. /**
  27482. * Return command status.
  27483. * Search does not support old api.
  27484. *
  27485. * @return Number
  27486. **/
  27487. this.getstate = function() {
  27488. return 0;
  27489. };
  27490. /**
  27491. * Send search request to backend.
  27492. *
  27493. * @param String search string
  27494. * @return $.Deferred
  27495. **/
  27496. this.exec = function(q, target, mime) {
  27497. var fm = this.fm,
  27498. reqDef = [],
  27499. onlyMimes = fm.options.onlyMimes,
  27500. phash, targetVolids = [];
  27501. if (typeof q == 'string' && q) {
  27502. if (typeof target == 'object') {
  27503. mime = target.mime || '';
  27504. target = target.target || '';
  27505. }
  27506. target = target? target : '';
  27507. if (mime) {
  27508. mime = $.trim(mime).replace(',', ' ').split(' ');
  27509. if (onlyMimes.length) {
  27510. mime = $.map(mime, function(m){
  27511. m = $.trim(m);
  27512. return m && ($.inArray(m, onlyMimes) !== -1
  27513. || $.grep(onlyMimes, function(om) { return m.indexOf(om) === 0? true : false; }).length
  27514. )? m : null;
  27515. });
  27516. }
  27517. } else {
  27518. mime = [].concat(onlyMimes);
  27519. }
  27520. fm.trigger('searchstart', {query : q, target : target, mimes : mime});
  27521. if (! onlyMimes.length || mime.length) {
  27522. if (target === '' && fm.api >= 2.1) {
  27523. $.each(fm.roots, function(id, hash) {
  27524. reqDef.push(fm.request({
  27525. data : {cmd : 'search', q : q, target : hash, mimes : mime},
  27526. notify : {type : 'search', cnt : 1, hideCnt : (reqDef.length? false : true)},
  27527. cancel : true,
  27528. preventDone : true
  27529. }));
  27530. });
  27531. } else {
  27532. reqDef.push(fm.request({
  27533. data : {cmd : 'search', q : q, target : target, mimes : mime},
  27534. notify : {type : 'search', cnt : 1, hideCnt : true},
  27535. cancel : true,
  27536. preventDone : true
  27537. }));
  27538. if (target !== '' && fm.api >= 2.1 && Object.keys(fm.leafRoots).length) {
  27539. $.each(fm.leafRoots, function(hash, roots) {
  27540. phash = hash;
  27541. while(phash) {
  27542. if (target === phash) {
  27543. $.each(roots, function() {
  27544. var f = fm.file(this);
  27545. f && f.volumeid && targetVolids.push(f.volumeid);
  27546. reqDef.push(fm.request({
  27547. data : {cmd : 'search', q : q, target : this, mimes : mime},
  27548. notify : {type : 'search', cnt : 1, hideCnt : false},
  27549. cancel : true,
  27550. preventDone : true
  27551. }));
  27552. });
  27553. }
  27554. phash = (fm.file(phash) || {}).phash;
  27555. }
  27556. });
  27557. }
  27558. }
  27559. } else {
  27560. reqDef = [$.Deferred().resolve({files: []})];
  27561. }
  27562. fm.searchStatus.mixed = (reqDef.length > 1)? targetVolids : false;
  27563. return $.when.apply($, reqDef).done(function(data) {
  27564. var argLen = arguments.length,
  27565. i;
  27566. data.warning && fm.error(data.warning);
  27567. if (argLen > 1) {
  27568. data.files = (data.files || []);
  27569. for(i = 1; i < argLen; i++) {
  27570. arguments[i].warning && fm.error(arguments[i].warning);
  27571. if (arguments[i].files) {
  27572. data.files.push.apply(data.files, arguments[i].files);
  27573. }
  27574. }
  27575. }
  27576. // because "preventDone : true" so update files cache
  27577. data.files && data.files.length && fm.cache(data.files);
  27578. fm.lazy(function() {
  27579. fm.trigger('search', data);
  27580. }).then(function() {
  27581. // fire event with command name + 'done'
  27582. return fm.lazy(function() {
  27583. fm.trigger('searchdone');
  27584. });
  27585. }).then(function() {
  27586. // force update content
  27587. data.sync && fm.sync();
  27588. });
  27589. });
  27590. }
  27591. fm.getUI('toolbar').find('.'+fm.res('class', 'searchbtn')+' :text').trigger('focus');
  27592. return $.Deferred().reject();
  27593. };
  27594. };
  27595. /*
  27596. * File: /js/commands/selectall.js
  27597. */
  27598. /**
  27599. * @class elFinder command "selectall"
  27600. * Select ALL of cwd items
  27601. *
  27602. * @author Naoki Sawada
  27603. **/
  27604. elFinder.prototype.commands.selectall = function() {
  27605. "use strict";
  27606. var self = this,
  27607. state = 0;
  27608. this.fm.bind('select', function(e) {
  27609. state = (e.data && e.data.selectall)? -1 : 0;
  27610. });
  27611. this.state = 0;
  27612. this.updateOnSelect = false;
  27613. this.getstate = function() {
  27614. return state;
  27615. };
  27616. this.exec = function() {
  27617. $(document).trigger($.Event('keydown', { keyCode: 65, ctrlKey : true, shiftKey : false, altKey : false, metaKey : false }));
  27618. return $.Deferred().resolve();
  27619. };
  27620. };
  27621. /*
  27622. * File: /js/commands/selectinvert.js
  27623. */
  27624. /**
  27625. * @class elFinder command "selectinvert"
  27626. * Invert Selection of cwd items
  27627. *
  27628. * @author Naoki Sawada
  27629. **/
  27630. elFinder.prototype.commands.selectinvert = function() {
  27631. "use strict";
  27632. this.updateOnSelect = false;
  27633. this.getstate = function() {
  27634. return 0;
  27635. };
  27636. this.exec = function() {
  27637. $(document).trigger($.Event('keydown', { keyCode: 73, ctrlKey : true, shiftKey : true, altKey : false, metaKey : false }));
  27638. return $.Deferred().resolve();
  27639. };
  27640. };
  27641. /*
  27642. * File: /js/commands/selectnone.js
  27643. */
  27644. /**
  27645. * @class elFinder command "selectnone"
  27646. * Unselect ALL of cwd items
  27647. *
  27648. * @author Naoki Sawada
  27649. **/
  27650. elFinder.prototype.commands.selectnone = function() {
  27651. "use strict";
  27652. var self = this,
  27653. fm = this.fm,
  27654. state = -1;
  27655. fm.bind('select', function(e) {
  27656. state = (e.data && e.data.unselectall)? -1 : 0;
  27657. });
  27658. this.state = -1;
  27659. this.updateOnSelect = false;
  27660. this.getstate = function() {
  27661. return state;
  27662. };
  27663. this.exec = function() {
  27664. fm.getUI('cwd').trigger('unselectall');
  27665. return $.Deferred().resolve();
  27666. };
  27667. };
  27668. /*
  27669. * File: /js/commands/sort.js
  27670. */
  27671. /**
  27672. * @class elFinder command "sort"
  27673. * Change sort files rule
  27674. *
  27675. * @author Dmitry (dio) Levashov
  27676. **/
  27677. elFinder.prototype.commands.sort = function() {
  27678. "use strict";
  27679. var self = this,
  27680. fm = self.fm,
  27681. setVar = function() {
  27682. self.variants = [];
  27683. $.each(fm.sortRules, function(name, value) {
  27684. var sort = {
  27685. type : name,
  27686. order : name == fm.sortType ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder
  27687. };
  27688. if ($.inArray(name, fm.sorters) !== -1) {
  27689. var arr = name == fm.sortType ? (sort.order == 'asc'? 's' : 'n') : '';
  27690. self.variants.push([sort, (arr? '<span class="ui-icon ui-icon-arrowthick-1-'+arr+'"></span>' : '') + '&nbsp;' + fm.i18n('sort'+name)]);
  27691. }
  27692. });
  27693. self.variants.push('|');
  27694. self.variants.push([
  27695. {
  27696. type : fm.sortType,
  27697. order : fm.sortOrder,
  27698. stick : !fm.sortStickFolders,
  27699. tree : fm.sortAlsoTreeview
  27700. },
  27701. (fm.sortStickFolders? '<span class="ui-icon ui-icon-check"/>' : '') + '&nbsp;' + fm.i18n('sortFoldersFirst')
  27702. ]);
  27703. if (fm.ui.tree) {
  27704. self.variants.push('|');
  27705. self.variants.push([
  27706. {
  27707. type : fm.sortType,
  27708. order : fm.sortOrder,
  27709. stick : fm.sortStickFolders,
  27710. tree : !fm.sortAlsoTreeview
  27711. },
  27712. (fm.sortAlsoTreeview? '<span class="ui-icon ui-icon-check"/>' : '') + '&nbsp;' + fm.i18n('sortAlsoTreeview')
  27713. ]);
  27714. }
  27715. };
  27716. /**
  27717. * Command options
  27718. *
  27719. * @type Object
  27720. */
  27721. this.options = {ui : 'sortbutton'};
  27722. fm.bind('open sortchange', setVar)
  27723. .bind('open', function() {
  27724. fm.unbind('add', setVar).one('add', setVar);
  27725. fm.getUI('toolbar').find('.elfiner-button-sort .elfinder-button-menu .elfinder-button-menu-item').each(function() {
  27726. var tgt = $(this),
  27727. rel = tgt.attr('rel');
  27728. tgt.toggle(! rel || $.inArray(rel, fm.sorters) !== -1);
  27729. });
  27730. })
  27731. .bind('cwdrender', function() {
  27732. var cols = $(fm.cwd).find('div.elfinder-cwd-wrapper-list table');
  27733. if (cols.length) {
  27734. $.each(fm.sortRules, function(name, value) {
  27735. var td = cols.find('thead tr td.elfinder-cwd-view-th-'+name);
  27736. if (td.length) {
  27737. var current = ( name == fm.sortType),
  27738. sort = {
  27739. type : name,
  27740. order : current ? fm.sortOrder == 'asc' ? 'desc' : 'asc' : fm.sortOrder
  27741. },arr;
  27742. if (current) {
  27743. td.addClass('ui-state-active');
  27744. arr = fm.sortOrder == 'asc' ? 'n' : 's';
  27745. $('<span class="ui-icon ui-icon-triangle-1-'+arr+'"/>').appendTo(td);
  27746. }
  27747. $(td).on('click', function(e){
  27748. if (! $(this).data('dragging')) {
  27749. e.stopPropagation();
  27750. if (! fm.getUI('cwd').data('longtap')) {
  27751. fm.exec('sort', [], sort);
  27752. }
  27753. }
  27754. })
  27755. .on('mouseenter mouseleave', function(e) {
  27756. $(this).toggleClass('ui-state-hover', e.type === 'mouseenter');
  27757. });
  27758. }
  27759. });
  27760. }
  27761. });
  27762. this.getstate = function() {
  27763. return 0;
  27764. };
  27765. this.exec = function(hashes, sortopt) {
  27766. var fm = this.fm,
  27767. sort = Object.assign({
  27768. type : fm.sortType,
  27769. order : fm.sortOrder,
  27770. stick : fm.sortStickFolders,
  27771. tree : fm.sortAlsoTreeview
  27772. }, sortopt);
  27773. return fm.lazy(function() {
  27774. fm.setSort(sort.type, sort.order, sort.stick, sort.tree);
  27775. this.resolve();
  27776. });
  27777. };
  27778. };
  27779. /*
  27780. * File: /js/commands/undo.js
  27781. */
  27782. /**
  27783. * @class elFinder command "undo"
  27784. * Undo previous commands
  27785. *
  27786. * @author Naoki Sawada
  27787. **/
  27788. elFinder.prototype.commands.undo = function() {
  27789. "use strict";
  27790. var self = this,
  27791. fm = this.fm,
  27792. setTitle = function(undo) {
  27793. if (undo) {
  27794. self.title = fm.i18n('cmdundo') + ' ' + fm.i18n('cmd'+undo.cmd);
  27795. self.state = 0;
  27796. } else {
  27797. self.title = fm.i18n('cmdundo');
  27798. self.state = -1;
  27799. }
  27800. self.change();
  27801. },
  27802. cmds = [];
  27803. this.alwaysEnabled = true;
  27804. this.updateOnSelect = false;
  27805. this.shortcuts = [{
  27806. pattern : 'ctrl+z'
  27807. }];
  27808. this.syncTitleOnChange = true;
  27809. this.getstate = function() {
  27810. return this.state;
  27811. };
  27812. this.setUndo = function(undo, redo) {
  27813. var _undo = {};
  27814. if (undo) {
  27815. if ($.isPlainObject(undo) && undo.cmd && undo.callback) {
  27816. Object.assign(_undo, undo);
  27817. if (redo) {
  27818. delete redo.undo;
  27819. _undo.redo = redo;
  27820. } else {
  27821. fm.getCommand('redo').setRedo(null);
  27822. }
  27823. cmds.push(_undo);
  27824. setTitle(_undo);
  27825. }
  27826. }
  27827. };
  27828. this.exec = function() {
  27829. var redo = fm.getCommand('redo'),
  27830. dfd = $.Deferred(),
  27831. undo, res, _redo = {};
  27832. if (cmds.length) {
  27833. undo = cmds.pop();
  27834. if (undo.redo) {
  27835. Object.assign(_redo, undo.redo);
  27836. delete undo.redo;
  27837. } else {
  27838. _redo = null;
  27839. }
  27840. dfd.done(function() {
  27841. if (_redo) {
  27842. redo.setRedo(_redo, undo);
  27843. }
  27844. });
  27845. setTitle(cmds.length? cmds[cmds.length-1] : void(0));
  27846. res = undo.callback();
  27847. if (res && res.done) {
  27848. res.done(function() {
  27849. dfd.resolve();
  27850. }).fail(function() {
  27851. dfd.reject();
  27852. });
  27853. } else {
  27854. dfd.resolve();
  27855. }
  27856. if (cmds.length) {
  27857. this.update(0, cmds[cmds.length - 1].name);
  27858. } else {
  27859. this.update(-1, '');
  27860. }
  27861. } else {
  27862. dfd.reject();
  27863. }
  27864. return dfd;
  27865. };
  27866. fm.bind('exec', function(e) {
  27867. var data = e.data || {};
  27868. if (data.opts && data.opts._userAction) {
  27869. if (data.dfrd && data.dfrd.done) {
  27870. data.dfrd.done(function(res) {
  27871. if (res && res.undo && res.redo) {
  27872. res.undo.redo = res.redo;
  27873. self.setUndo(res.undo);
  27874. }
  27875. });
  27876. }
  27877. }
  27878. });
  27879. };
  27880. /**
  27881. * @class elFinder command "redo"
  27882. * Redo previous commands
  27883. *
  27884. * @author Naoki Sawada
  27885. **/
  27886. elFinder.prototype.commands.redo = function() {
  27887. "use strict";
  27888. var self = this,
  27889. fm = this.fm,
  27890. setTitle = function(redo) {
  27891. if (redo && redo.callback) {
  27892. self.title = fm.i18n('cmdredo') + ' ' + fm.i18n('cmd'+redo.cmd);
  27893. self.state = 0;
  27894. } else {
  27895. self.title = fm.i18n('cmdredo');
  27896. self.state = -1;
  27897. }
  27898. self.change();
  27899. },
  27900. cmds = [];
  27901. this.alwaysEnabled = true;
  27902. this.updateOnSelect = false;
  27903. this.shortcuts = [{
  27904. pattern : 'shift+ctrl+z ctrl+y'
  27905. }];
  27906. this.syncTitleOnChange = true;
  27907. this.getstate = function() {
  27908. return this.state;
  27909. };
  27910. this.setRedo = function(redo, undo) {
  27911. if (redo === null) {
  27912. cmds = [];
  27913. setTitle();
  27914. } else {
  27915. if (redo && redo.cmd && redo.callback) {
  27916. if (undo) {
  27917. redo.undo = undo;
  27918. }
  27919. cmds.push(redo);
  27920. setTitle(redo);
  27921. }
  27922. }
  27923. };
  27924. this.exec = function() {
  27925. var undo = fm.getCommand('undo'),
  27926. dfd = $.Deferred(),
  27927. redo, res, _undo = {}, _redo = {};
  27928. if (cmds.length) {
  27929. redo = cmds.pop();
  27930. if (redo.undo) {
  27931. Object.assign(_undo, redo.undo);
  27932. Object.assign(_redo, redo);
  27933. delete _redo.undo;
  27934. dfd.done(function() {
  27935. undo.setUndo(_undo, _redo);
  27936. });
  27937. }
  27938. setTitle(cmds.length? cmds[cmds.length-1] : void(0));
  27939. res = redo.callback();
  27940. if (res && res.done) {
  27941. res.done(function() {
  27942. dfd.resolve();
  27943. }).fail(function() {
  27944. dfd.reject();
  27945. });
  27946. } else {
  27947. dfd.resolve();
  27948. }
  27949. return dfd;
  27950. } else {
  27951. return dfd.reject();
  27952. }
  27953. };
  27954. };
  27955. /*
  27956. * File: /js/commands/up.js
  27957. */
  27958. /**
  27959. * @class elFinder command "up"
  27960. * Go into parent directory
  27961. *
  27962. * @author Dmitry (dio) Levashov
  27963. **/
  27964. (elFinder.prototype.commands.up = function() {
  27965. "use strict";
  27966. this.alwaysEnabled = true;
  27967. this.updateOnSelect = false;
  27968. this.shortcuts = [{
  27969. pattern : 'ctrl+up'
  27970. }];
  27971. this.getstate = function() {
  27972. return this.fm.cwd().phash ? 0 : -1;
  27973. };
  27974. this.exec = function() {
  27975. var fm = this.fm,
  27976. cwdhash = fm.cwd().hash;
  27977. return this.fm.cwd().phash ? this.fm.exec('open', this.fm.cwd().phash).done(function() {
  27978. fm.one('opendone', function() {
  27979. fm.selectfiles({files : [cwdhash]});
  27980. });
  27981. }) : $.Deferred().reject();
  27982. };
  27983. }).prototype = { forceLoad : true }; // this is required command
  27984. /*
  27985. * File: /js/commands/upload.js
  27986. */
  27987. /**
  27988. * @class elFinder command "upload"
  27989. * Upload files using iframe or XMLHttpRequest & FormData.
  27990. * Dialog allow to send files using drag and drop
  27991. *
  27992. * @type elFinder.command
  27993. * @author Dmitry (dio) Levashov
  27994. */
  27995. elFinder.prototype.commands.upload = function() {
  27996. "use strict";
  27997. var hover = this.fm.res('class', 'hover');
  27998. this.disableOnSearch = true;
  27999. this.updateOnSelect = false;
  28000. // Shortcut opens dialog
  28001. this.shortcuts = [{
  28002. pattern : 'ctrl+u'
  28003. }];
  28004. /**
  28005. * Return command state
  28006. *
  28007. * @return Number
  28008. **/
  28009. this.getstate = function(select) {
  28010. var fm = this.fm, f,
  28011. sel = (select || [fm.cwd().hash]);
  28012. if (!this._disabled && sel.length == 1) {
  28013. f = fm.file(sel[0]);
  28014. }
  28015. return (f && f.mime == 'directory' && f.write)? 0 : -1;
  28016. };
  28017. this.exec = function(data) {
  28018. var fm = this.fm,
  28019. cwdHash = fm.cwd().hash,
  28020. getTargets = function() {
  28021. var tgts = data && (data instanceof Array)? data : null,
  28022. sel;
  28023. if (! data || data instanceof Array) {
  28024. if (! tgts && (sel = fm.selected()).length === 1 && fm.file(sel[0]).mime === 'directory') {
  28025. tgts = sel;
  28026. } else if (!tgts || tgts.length !== 1 || fm.file(tgts[0]).mime !== 'directory') {
  28027. tgts = [ cwdHash ];
  28028. }
  28029. }
  28030. return tgts;
  28031. },
  28032. targets = getTargets(),
  28033. check = targets? targets[0] : (data && data.target? data.target : null),
  28034. targetDir = check? fm.file(check) : fm.cwd(),
  28035. fmUpload = function(data) {
  28036. fm.upload(data)
  28037. .fail(function(error) {
  28038. dfrd.reject(error);
  28039. })
  28040. .done(function(data) {
  28041. var cwd = fm.getUI('cwd'),
  28042. node;
  28043. dfrd.resolve(data);
  28044. if (data && data.added && data.added[0] && ! fm.ui.notify.children('.elfinder-notify-upload').length) {
  28045. var newItem = fm.findCwdNodes(data.added);
  28046. if (newItem.length) {
  28047. newItem.trigger('scrolltoview');
  28048. } else {
  28049. if (targetDir.hash !== cwdHash) {
  28050. node = $('<div/>').append(
  28051. $('<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>')
  28052. .on('mouseenter mouseleave', function(e) {
  28053. $(this).toggleClass('ui-state-hover', e.type == 'mouseenter');
  28054. }).on('click', function() {
  28055. fm.exec('open', check).done(function() {
  28056. fm.one('opendone', function() {
  28057. fm.trigger('selectfiles', {files : $.map(data.added, function(f) {return f.hash;})});
  28058. });
  28059. });
  28060. })
  28061. );
  28062. } else {
  28063. fm.trigger('selectfiles', {files : $.map(data.added, function(f) {return f.hash;})});
  28064. }
  28065. fm.toast({msg: fm.i18n(['complete', fm.i18n('cmdupload')]), extNode: node});
  28066. }
  28067. }
  28068. })
  28069. .progress(function() {
  28070. dfrd.notifyWith(this, Array.from(arguments));
  28071. });
  28072. },
  28073. upload = function(data) {
  28074. dialog.elfinderdialog('close');
  28075. if (targets) {
  28076. data.target = targets[0];
  28077. }
  28078. fmUpload(data);
  28079. },
  28080. getSelector = function() {
  28081. var hash = targetDir.hash,
  28082. dirs = $.map(fm.files(hash), function(f) {
  28083. return (f.mime === 'directory' && f.write)? f : null;
  28084. });
  28085. if (! dirs.length) {
  28086. return $();
  28087. }
  28088. return $('<div class="elfinder-upload-dirselect elfinder-tabstop" title="' + fm.i18n('folders') + '"/>')
  28089. .on('click', function(e) {
  28090. e.stopPropagation();
  28091. e.preventDefault();
  28092. dirs = fm.sortFiles(dirs);
  28093. var $this = $(this),
  28094. cwd = fm.cwd(),
  28095. base = dialog.closest('div.ui-dialog'),
  28096. getRaw = function(f, icon) {
  28097. return {
  28098. label : fm.escape(f.i18 || f.name),
  28099. icon : icon,
  28100. remain : false,
  28101. callback : function() {
  28102. var title = base.children('.ui-dialog-titlebar:first').find('span.elfinder-upload-target');
  28103. targets = [ f.hash ];
  28104. title.html(' - ' + fm.escape(f.i18 || f.name));
  28105. $this.trigger('focus');
  28106. },
  28107. options : {
  28108. className : (targets && targets.length && f.hash === targets[0])? 'ui-state-active' : '',
  28109. iconClass : f.csscls || '',
  28110. iconImg : f.icon || ''
  28111. }
  28112. };
  28113. },
  28114. raw = [ getRaw(targetDir, 'opendir'), '|' ];
  28115. $.each(dirs, function(i, f) {
  28116. raw.push(getRaw(f, 'dir'));
  28117. });
  28118. $this.trigger('blur');
  28119. fm.trigger('contextmenu', {
  28120. raw: raw,
  28121. x: e.pageX || $(this).offset().left,
  28122. y: e.pageY || $(this).offset().top,
  28123. prevNode: base,
  28124. fitHeight: true
  28125. });
  28126. }).append('<span class="elfinder-button-icon elfinder-button-icon-dir" />');
  28127. },
  28128. inputButton = function(type, caption) {
  28129. var button,
  28130. input = $('<input type="file" ' + type + '/>')
  28131. .on('change', function() {
  28132. upload({input : input.get(0), type : 'files'});
  28133. })
  28134. .on('dragover', function(e) {
  28135. e.originalEvent.dataTransfer.dropEffect = 'copy';
  28136. });
  28137. 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>')
  28138. .append($('<form/>').append(input))
  28139. .on('click', function(e) {
  28140. if (e.target === this) {
  28141. e.stopPropagation();
  28142. e.preventDefault();
  28143. input.click();
  28144. }
  28145. })
  28146. .on('mouseenter mouseleave', function(e) {
  28147. $(this).toggleClass(hover, e.type === 'mouseenter');
  28148. });
  28149. },
  28150. dfrd = $.Deferred(),
  28151. dialog, dropbox, pastebox, dropUpload, paste, dirs, spinner, uidialog;
  28152. dropUpload = function(e) {
  28153. e.stopPropagation();
  28154. e.preventDefault();
  28155. var file = false,
  28156. type = '',
  28157. elfFrom = null,
  28158. mycwd = '',
  28159. data = null,
  28160. target = e._target || null,
  28161. trf = e.dataTransfer || null,
  28162. kind = (trf.items && trf.items.length && trf.items[0].kind)? trf.items[0].kind : '',
  28163. errors;
  28164. if (trf) {
  28165. try {
  28166. elfFrom = trf.getData('elfinderfrom');
  28167. if (elfFrom) {
  28168. mycwd = window.location.href + fm.cwd().hash;
  28169. if ((!target && elfFrom === mycwd) || target === mycwd) {
  28170. dfrd.reject();
  28171. return;
  28172. }
  28173. }
  28174. } catch(e) {}
  28175. if (kind === 'file' && (trf.items[0].getAsEntry || trf.items[0].webkitGetAsEntry)) {
  28176. file = trf;
  28177. type = 'data';
  28178. } else if (kind !== 'string' && trf.files && trf.files.length && $.inArray('Text', trf.types) === -1) {
  28179. file = trf.files;
  28180. type = 'files';
  28181. } else {
  28182. try {
  28183. if ((data = trf.getData('text/html')) && data.match(/<(?:img|a)/i)) {
  28184. file = [ data ];
  28185. type = 'html';
  28186. }
  28187. } catch(e) {}
  28188. if (! file && (data = trf.getData('text'))) {
  28189. file = [ data ];
  28190. type = 'text';
  28191. }
  28192. }
  28193. }
  28194. if (file) {
  28195. fmUpload({files : file, type : type, target : target, dropEvt : e});
  28196. } else {
  28197. errors = ['errUploadNoFiles'];
  28198. if (kind === 'file') {
  28199. errors.push('errFolderUpload');
  28200. }
  28201. fm.error(errors);
  28202. dfrd.reject();
  28203. }
  28204. };
  28205. if (!targets && data) {
  28206. if (data.input || data.files) {
  28207. data.type = 'files';
  28208. fmUpload(data);
  28209. } else if (data.dropEvt) {
  28210. dropUpload(data.dropEvt);
  28211. }
  28212. return dfrd;
  28213. }
  28214. paste = function(ev) {
  28215. var e = ev.originalEvent || ev;
  28216. var files = [], items = [];
  28217. var file;
  28218. if (e.clipboardData) {
  28219. if (e.clipboardData.items && e.clipboardData.items.length){
  28220. items = e.clipboardData.items;
  28221. for (var i=0; i < items.length; i++) {
  28222. if (e.clipboardData.items[i].kind == 'file') {
  28223. file = e.clipboardData.items[i].getAsFile();
  28224. files.push(file);
  28225. }
  28226. }
  28227. } else if (e.clipboardData.files && e.clipboardData.files.length) {
  28228. files = e.clipboardData.files;
  28229. }
  28230. if (files.length) {
  28231. upload({files : files, type : 'files', clipdata : true});
  28232. return;
  28233. }
  28234. }
  28235. var my = e.target || e.srcElement;
  28236. setTimeout(function () {
  28237. var type = 'text',
  28238. src;
  28239. if (my.innerHTML) {
  28240. $(my).find('img').each(function(i, v){
  28241. if (v.src.match(/^webkit-fake-url:\/\//)) {
  28242. // For Safari's bug.
  28243. // ref. https://bugs.webkit.org/show_bug.cgi?id=49141
  28244. // https://dev.ckeditor.com/ticket/13029
  28245. $(v).remove();
  28246. }
  28247. });
  28248. if ($(my).find('a,img').length) {
  28249. type = 'html';
  28250. }
  28251. src = my.innerHTML;
  28252. my.innerHTML = '';
  28253. upload({files : [ src ], type : type});
  28254. }
  28255. }, 1);
  28256. };
  28257. dialog = $('<div class="elfinder-upload-dialog-wrapper"/>')
  28258. .append(inputButton('multiple', 'selectForUpload'));
  28259. if (! fm.UA.Mobile && (function(input) {
  28260. return (typeof input.webkitdirectory !== 'undefined' || typeof input.directory !== 'undefined');})(document.createElement('input'))) {
  28261. dialog.append(inputButton('multiple webkitdirectory directory', 'selectFolder'));
  28262. }
  28263. if (targetDir.dirs) {
  28264. if (targetDir.hash === cwdHash || $('#'+fm.navHash2Id(targetDir.hash)).hasClass('elfinder-subtree-loaded')) {
  28265. getSelector().appendTo(dialog);
  28266. } else {
  28267. spinner = $('<div class="elfinder-upload-dirselect" title="' + fm.i18n('nowLoading') + '"/>')
  28268. .append('<span class="elfinder-button-icon elfinder-button-icon-spinner" />')
  28269. .appendTo(dialog);
  28270. fm.request({cmd : 'tree', target : targetDir.hash})
  28271. .done(function() {
  28272. fm.one('treedone', function() {
  28273. spinner.replaceWith(getSelector());
  28274. uidialog.elfinderdialog('tabstopsInit');
  28275. });
  28276. })
  28277. .fail(function() {
  28278. spinner.remove();
  28279. });
  28280. }
  28281. }
  28282. if (fm.dragUpload) {
  28283. dropbox = $('<div class="ui-corner-all elfinder-upload-dropbox elfinder-tabstop" contenteditable="true" data-ph="'+fm.i18n('dropPasteFiles')+'"></div>')
  28284. .on('paste', function(e){
  28285. paste(e);
  28286. })
  28287. .on('mousedown click', function(){
  28288. $(this).trigger('focus');
  28289. })
  28290. .on('focus', function(){
  28291. this.innerHTML = '';
  28292. })
  28293. .on('mouseover', function(){
  28294. $(this).addClass(hover);
  28295. })
  28296. .on('mouseout', function(){
  28297. $(this).removeClass(hover);
  28298. })
  28299. .on('dragenter', function(e) {
  28300. e.stopPropagation();
  28301. e.preventDefault();
  28302. $(this).addClass(hover);
  28303. })
  28304. .on('dragleave', function(e) {
  28305. e.stopPropagation();
  28306. e.preventDefault();
  28307. $(this).removeClass(hover);
  28308. })
  28309. .on('dragover', function(e) {
  28310. e.stopPropagation();
  28311. e.preventDefault();
  28312. e.originalEvent.dataTransfer.dropEffect = 'copy';
  28313. $(this).addClass(hover);
  28314. })
  28315. .on('drop', function(e) {
  28316. dialog.elfinderdialog('close');
  28317. targets && (e.originalEvent._target = targets[0]);
  28318. dropUpload(e.originalEvent);
  28319. })
  28320. .prependTo(dialog)
  28321. .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0];
  28322. } else {
  28323. pastebox = $('<div class="ui-corner-all elfinder-upload-dropbox" contenteditable="true">'+fm.i18n('dropFilesBrowser')+'</div>')
  28324. .on('paste drop', function(e){
  28325. paste(e);
  28326. })
  28327. .on('mousedown click', function(){
  28328. $(this).trigger('focus');
  28329. })
  28330. .on('focus', function(){
  28331. this.innerHTML = '';
  28332. })
  28333. .on('dragenter mouseover', function(){
  28334. $(this).addClass(hover);
  28335. })
  28336. .on('dragleave mouseout', function(){
  28337. $(this).removeClass(hover);
  28338. })
  28339. .prependTo(dialog)
  28340. .after('<div class="elfinder-upload-dialog-or">'+fm.i18n('or')+'</div>')[0];
  28341. }
  28342. uidialog = fm.dialog(dialog, {
  28343. title : this.title + '<span class="elfinder-upload-target">' + (targetDir? ' - ' + fm.escape(targetDir.i18 || targetDir.name) : '') + '</span>',
  28344. modal : true,
  28345. resizable : false,
  28346. destroyOnClose : true,
  28347. close : function() {
  28348. var cm = fm.getUI('contextmenu');
  28349. if (cm.is(':visible')) {
  28350. cm.click();
  28351. }
  28352. }
  28353. });
  28354. return dfrd;
  28355. };
  28356. };
  28357. /*
  28358. * File: /js/commands/view.js
  28359. */
  28360. /**
  28361. * @class elFinder command "view"
  28362. * Change current directory view (icons/list)
  28363. *
  28364. * @author Dmitry (dio) Levashov
  28365. **/
  28366. elFinder.prototype.commands.view = function() {
  28367. "use strict";
  28368. var fm = this.fm;
  28369. this.value = fm.viewType;
  28370. this.alwaysEnabled = true;
  28371. this.updateOnSelect = false;
  28372. this.options = { ui : 'viewbutton'};
  28373. this.getstate = function() {
  28374. return 0;
  28375. };
  28376. this.exec = function() {
  28377. var self = this,
  28378. value = fm.storage('view', this.value == 'list' ? 'icons' : 'list');
  28379. return fm.lazy(function() {
  28380. fm.viewchange();
  28381. self.update(void(0), value);
  28382. this.resolve();
  28383. });
  28384. };
  28385. };
  28386. return elFinder;
  28387. }));