media-views.js 259 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160
  1. /******/ (function(modules) { // webpackBootstrap
  2. /******/ // The module cache
  3. /******/ var installedModules = {};
  4. /******/
  5. /******/ // The require function
  6. /******/ function __webpack_require__(moduleId) {
  7. /******/
  8. /******/ // Check if module is in cache
  9. /******/ if(installedModules[moduleId]) {
  10. /******/ return installedModules[moduleId].exports;
  11. /******/ }
  12. /******/ // Create a new module (and put it into the cache)
  13. /******/ var module = installedModules[moduleId] = {
  14. /******/ i: moduleId,
  15. /******/ l: false,
  16. /******/ exports: {}
  17. /******/ };
  18. /******/
  19. /******/ // Execute the module function
  20. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  21. /******/
  22. /******/ // Flag the module as loaded
  23. /******/ module.l = true;
  24. /******/
  25. /******/ // Return the exports of the module
  26. /******/ return module.exports;
  27. /******/ }
  28. /******/
  29. /******/
  30. /******/ // expose the modules object (__webpack_modules__)
  31. /******/ __webpack_require__.m = modules;
  32. /******/
  33. /******/ // expose the module cache
  34. /******/ __webpack_require__.c = installedModules;
  35. /******/
  36. /******/ // define getter function for harmony exports
  37. /******/ __webpack_require__.d = function(exports, name, getter) {
  38. /******/ if(!__webpack_require__.o(exports, name)) {
  39. /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  40. /******/ }
  41. /******/ };
  42. /******/
  43. /******/ // define __esModule on exports
  44. /******/ __webpack_require__.r = function(exports) {
  45. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  46. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  47. /******/ }
  48. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  49. /******/ };
  50. /******/
  51. /******/ // create a fake namespace object
  52. /******/ // mode & 1: value is a module id, require it
  53. /******/ // mode & 2: merge all properties of value into the ns
  54. /******/ // mode & 4: return value when already ns object
  55. /******/ // mode & 8|1: behave like require
  56. /******/ __webpack_require__.t = function(value, mode) {
  57. /******/ if(mode & 1) value = __webpack_require__(value);
  58. /******/ if(mode & 8) return value;
  59. /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  60. /******/ var ns = Object.create(null);
  61. /******/ __webpack_require__.r(ns);
  62. /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  63. /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  64. /******/ return ns;
  65. /******/ };
  66. /******/
  67. /******/ // getDefaultExport function for compatibility with non-harmony modules
  68. /******/ __webpack_require__.n = function(module) {
  69. /******/ var getter = module && module.__esModule ?
  70. /******/ function getDefault() { return module['default']; } :
  71. /******/ function getModuleExports() { return module; };
  72. /******/ __webpack_require__.d(getter, 'a', getter);
  73. /******/ return getter;
  74. /******/ };
  75. /******/
  76. /******/ // Object.prototype.hasOwnProperty.call
  77. /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  78. /******/
  79. /******/ // __webpack_public_path__
  80. /******/ __webpack_require__.p = "";
  81. /******/
  82. /******/
  83. /******/ // Load entry module and return exports
  84. /******/ return __webpack_require__(__webpack_require__.s = 29);
  85. /******/ })
  86. /************************************************************************/
  87. /******/ (Array(29).concat([
  88. /* 29 */
  89. /***/ (function(module, exports, __webpack_require__) {
  90. module.exports = __webpack_require__(30);
  91. /***/ }),
  92. /* 30 */
  93. /***/ (function(module, exports, __webpack_require__) {
  94. /**
  95. * @output wp-includes/js/media-views.js
  96. */
  97. var media = wp.media,
  98. $ = jQuery,
  99. l10n;
  100. media.isTouchDevice = ( 'ontouchend' in document );
  101. // Link any localized strings.
  102. l10n = media.view.l10n = window._wpMediaViewsL10n || {};
  103. // Link any settings.
  104. media.view.settings = l10n.settings || {};
  105. delete l10n.settings;
  106. // Copy the `post` setting over to the model settings.
  107. media.model.settings.post = media.view.settings.post;
  108. // Check if the browser supports CSS 3.0 transitions
  109. $.support.transition = (function(){
  110. var style = document.documentElement.style,
  111. transitions = {
  112. WebkitTransition: 'webkitTransitionEnd',
  113. MozTransition: 'transitionend',
  114. OTransition: 'oTransitionEnd otransitionend',
  115. transition: 'transitionend'
  116. }, transition;
  117. transition = _.find( _.keys( transitions ), function( transition ) {
  118. return ! _.isUndefined( style[ transition ] );
  119. });
  120. return transition && {
  121. end: transitions[ transition ]
  122. };
  123. }());
  124. /**
  125. * A shared event bus used to provide events into
  126. * the media workflows that 3rd-party devs can use to hook
  127. * in.
  128. */
  129. media.events = _.extend( {}, Backbone.Events );
  130. /**
  131. * Makes it easier to bind events using transitions.
  132. *
  133. * @param {string} selector
  134. * @param {Number} sensitivity
  135. * @returns {Promise}
  136. */
  137. media.transition = function( selector, sensitivity ) {
  138. var deferred = $.Deferred();
  139. sensitivity = sensitivity || 2000;
  140. if ( $.support.transition ) {
  141. if ( ! (selector instanceof $) ) {
  142. selector = $( selector );
  143. }
  144. // Resolve the deferred when the first element finishes animating.
  145. selector.first().one( $.support.transition.end, deferred.resolve );
  146. // Just in case the event doesn't trigger, fire a callback.
  147. _.delay( deferred.resolve, sensitivity );
  148. // Otherwise, execute on the spot.
  149. } else {
  150. deferred.resolve();
  151. }
  152. return deferred.promise();
  153. };
  154. media.controller.Region = __webpack_require__( 31 );
  155. media.controller.StateMachine = __webpack_require__( 32 );
  156. media.controller.State = __webpack_require__( 33 );
  157. media.selectionSync = __webpack_require__( 34 );
  158. media.controller.Library = __webpack_require__( 35 );
  159. media.controller.ImageDetails = __webpack_require__( 36 );
  160. media.controller.GalleryEdit = __webpack_require__( 37 );
  161. media.controller.GalleryAdd = __webpack_require__( 38 );
  162. media.controller.CollectionEdit = __webpack_require__( 39 );
  163. media.controller.CollectionAdd = __webpack_require__( 40 );
  164. media.controller.FeaturedImage = __webpack_require__( 41 );
  165. media.controller.ReplaceImage = __webpack_require__( 42 );
  166. media.controller.EditImage = __webpack_require__( 43 );
  167. media.controller.MediaLibrary = __webpack_require__( 44 );
  168. media.controller.Embed = __webpack_require__( 45 );
  169. media.controller.Cropper = __webpack_require__( 46 );
  170. media.controller.CustomizeImageCropper = __webpack_require__( 47 );
  171. media.controller.SiteIconCropper = __webpack_require__( 48 );
  172. media.View = __webpack_require__( 49 );
  173. media.view.Frame = __webpack_require__( 50 );
  174. media.view.MediaFrame = __webpack_require__( 51 );
  175. media.view.MediaFrame.Select = __webpack_require__( 52 );
  176. media.view.MediaFrame.Post = __webpack_require__( 53 );
  177. media.view.MediaFrame.ImageDetails = __webpack_require__( 54 );
  178. media.view.Modal = __webpack_require__( 55 );
  179. media.view.FocusManager = __webpack_require__( 56 );
  180. media.view.UploaderWindow = __webpack_require__( 57 );
  181. media.view.EditorUploader = __webpack_require__( 58 );
  182. media.view.UploaderInline = __webpack_require__( 59 );
  183. media.view.UploaderStatus = __webpack_require__( 60 );
  184. media.view.UploaderStatusError = __webpack_require__( 61 );
  185. media.view.Toolbar = __webpack_require__( 62 );
  186. media.view.Toolbar.Select = __webpack_require__( 63 );
  187. media.view.Toolbar.Embed = __webpack_require__( 64 );
  188. media.view.Button = __webpack_require__( 65 );
  189. media.view.ButtonGroup = __webpack_require__( 66 );
  190. media.view.PriorityList = __webpack_require__( 67 );
  191. media.view.MenuItem = __webpack_require__( 68 );
  192. media.view.Menu = __webpack_require__( 69 );
  193. media.view.RouterItem = __webpack_require__( 70 );
  194. media.view.Router = __webpack_require__( 71 );
  195. media.view.Sidebar = __webpack_require__( 72 );
  196. media.view.Attachment = __webpack_require__( 73 );
  197. media.view.Attachment.Library = __webpack_require__( 74 );
  198. media.view.Attachment.EditLibrary = __webpack_require__( 75 );
  199. media.view.Attachments = __webpack_require__( 76 );
  200. media.view.Search = __webpack_require__( 77 );
  201. media.view.AttachmentFilters = __webpack_require__( 78 );
  202. media.view.DateFilter = __webpack_require__( 79 );
  203. media.view.AttachmentFilters.Uploaded = __webpack_require__( 80 );
  204. media.view.AttachmentFilters.All = __webpack_require__( 81 );
  205. media.view.AttachmentsBrowser = __webpack_require__( 82 );
  206. media.view.Selection = __webpack_require__( 83 );
  207. media.view.Attachment.Selection = __webpack_require__( 84 );
  208. media.view.Attachments.Selection = __webpack_require__( 85 );
  209. media.view.Attachment.EditSelection = __webpack_require__( 86 );
  210. media.view.Settings = __webpack_require__( 87 );
  211. media.view.Settings.AttachmentDisplay = __webpack_require__( 88 );
  212. media.view.Settings.Gallery = __webpack_require__( 89 );
  213. media.view.Settings.Playlist = __webpack_require__( 90 );
  214. media.view.Attachment.Details = __webpack_require__( 91 );
  215. media.view.AttachmentCompat = __webpack_require__( 92 );
  216. media.view.Iframe = __webpack_require__( 93 );
  217. media.view.Embed = __webpack_require__( 94 );
  218. media.view.Label = __webpack_require__( 95 );
  219. media.view.EmbedUrl = __webpack_require__( 96 );
  220. media.view.EmbedLink = __webpack_require__( 97 );
  221. media.view.EmbedImage = __webpack_require__( 98 );
  222. media.view.ImageDetails = __webpack_require__( 99 );
  223. media.view.Cropper = __webpack_require__( 100 );
  224. media.view.SiteIconCropper = __webpack_require__( 101 );
  225. media.view.SiteIconPreview = __webpack_require__( 102 );
  226. media.view.EditImage = __webpack_require__( 103 );
  227. media.view.Spinner = __webpack_require__( 104 );
  228. media.view.Heading = __webpack_require__( 105 );
  229. /***/ }),
  230. /* 31 */
  231. /***/ (function(module, exports) {
  232. /**
  233. * wp.media.controller.Region
  234. *
  235. * A region is a persistent application layout area.
  236. *
  237. * A region assumes one mode at any time, and can be switched to another.
  238. *
  239. * When mode changes, events are triggered on the region's parent view.
  240. * The parent view will listen to specific events and fill the region with an
  241. * appropriate view depending on mode. For example, a frame listens for the
  242. * 'browse' mode t be activated on the 'content' view and then fills the region
  243. * with an AttachmentsBrowser view.
  244. *
  245. * @memberOf wp.media.controller
  246. *
  247. * @class
  248. *
  249. * @param {object} options Options hash for the region.
  250. * @param {string} options.id Unique identifier for the region.
  251. * @param {Backbone.View} options.view A parent view the region exists within.
  252. * @param {string} options.selector jQuery selector for the region within the parent view.
  253. */
  254. var Region = function( options ) {
  255. _.extend( this, _.pick( options || {}, 'id', 'view', 'selector' ) );
  256. };
  257. // Use Backbone's self-propagating `extend` inheritance method.
  258. Region.extend = Backbone.Model.extend;
  259. _.extend( Region.prototype,/** @lends wp.media.controller.Region.prototype */{
  260. /**
  261. * Activate a mode.
  262. *
  263. * @since 3.5.0
  264. *
  265. * @param {string} mode
  266. *
  267. * @fires Region#activate
  268. * @fires Region#deactivate
  269. *
  270. * @returns {wp.media.controller.Region} Returns itself to allow chaining.
  271. */
  272. mode: function( mode ) {
  273. if ( ! mode ) {
  274. return this._mode;
  275. }
  276. // Bail if we're trying to change to the current mode.
  277. if ( mode === this._mode ) {
  278. return this;
  279. }
  280. /**
  281. * Region mode deactivation event.
  282. *
  283. * @event wp.media.controller.Region#deactivate
  284. */
  285. this.trigger('deactivate');
  286. this._mode = mode;
  287. this.render( mode );
  288. /**
  289. * Region mode activation event.
  290. *
  291. * @event wp.media.controller.Region#activate
  292. */
  293. this.trigger('activate');
  294. return this;
  295. },
  296. /**
  297. * Render a mode.
  298. *
  299. * @since 3.5.0
  300. *
  301. * @param {string} mode
  302. *
  303. * @fires Region#create
  304. * @fires Region#render
  305. *
  306. * @returns {wp.media.controller.Region} Returns itself to allow chaining
  307. */
  308. render: function( mode ) {
  309. // If the mode isn't active, activate it.
  310. if ( mode && mode !== this._mode ) {
  311. return this.mode( mode );
  312. }
  313. var set = { view: null },
  314. view;
  315. /**
  316. * Create region view event.
  317. *
  318. * Region view creation takes place in an event callback on the frame.
  319. *
  320. * @event wp.media.controller.Region#create
  321. * @type {object}
  322. * @property {object} view
  323. */
  324. this.trigger( 'create', set );
  325. view = set.view;
  326. /**
  327. * Render region view event.
  328. *
  329. * Region view creation takes place in an event callback on the frame.
  330. *
  331. * @event wp.media.controller.Region#render
  332. * @type {object}
  333. */
  334. this.trigger( 'render', view );
  335. if ( view ) {
  336. this.set( view );
  337. }
  338. return this;
  339. },
  340. /**
  341. * Get the region's view.
  342. *
  343. * @since 3.5.0
  344. *
  345. * @returns {wp.media.View}
  346. */
  347. get: function() {
  348. return this.view.views.first( this.selector );
  349. },
  350. /**
  351. * Set the region's view as a subview of the frame.
  352. *
  353. * @since 3.5.0
  354. *
  355. * @param {Array|Object} views
  356. * @param {Object} [options={}]
  357. * @returns {wp.Backbone.Subviews} Subviews is returned to allow chaining
  358. */
  359. set: function( views, options ) {
  360. if ( options ) {
  361. options.add = false;
  362. }
  363. return this.view.views.set( this.selector, views, options );
  364. },
  365. /**
  366. * Trigger regional view events on the frame.
  367. *
  368. * @since 3.5.0
  369. *
  370. * @param {string} event
  371. * @returns {undefined|wp.media.controller.Region} Returns itself to allow chaining.
  372. */
  373. trigger: function( event ) {
  374. var base, args;
  375. if ( ! this._mode ) {
  376. return;
  377. }
  378. args = _.toArray( arguments );
  379. base = this.id + ':' + event;
  380. // Trigger `{this.id}:{event}:{this._mode}` event on the frame.
  381. args[0] = base + ':' + this._mode;
  382. this.view.trigger.apply( this.view, args );
  383. // Trigger `{this.id}:{event}` event on the frame.
  384. args[0] = base;
  385. this.view.trigger.apply( this.view, args );
  386. return this;
  387. }
  388. });
  389. module.exports = Region;
  390. /***/ }),
  391. /* 32 */
  392. /***/ (function(module, exports) {
  393. /**
  394. * wp.media.controller.StateMachine
  395. *
  396. * A state machine keeps track of state. It is in one state at a time,
  397. * and can change from one state to another.
  398. *
  399. * States are stored as models in a Backbone collection.
  400. *
  401. * @memberOf wp.media.controller
  402. *
  403. * @since 3.5.0
  404. *
  405. * @class
  406. * @augments Backbone.Model
  407. * @mixin
  408. * @mixes Backbone.Events
  409. */
  410. var StateMachine = function() {
  411. return {
  412. // Use Backbone's self-propagating `extend` inheritance method.
  413. extend: Backbone.Model.extend
  414. };
  415. };
  416. _.extend( StateMachine.prototype, Backbone.Events,/** @lends wp.media.controller.StateMachine.prototype */{
  417. /**
  418. * Fetch a state.
  419. *
  420. * If no `id` is provided, returns the active state.
  421. *
  422. * Implicitly creates states.
  423. *
  424. * Ensure that the `states` collection exists so the `StateMachine`
  425. * can be used as a mixin.
  426. *
  427. * @since 3.5.0
  428. *
  429. * @param {string} id
  430. * @returns {wp.media.controller.State} Returns a State model
  431. * from the StateMachine collection
  432. */
  433. state: function( id ) {
  434. this.states = this.states || new Backbone.Collection();
  435. // Default to the active state.
  436. id = id || this._state;
  437. if ( id && ! this.states.get( id ) ) {
  438. this.states.add({ id: id });
  439. }
  440. return this.states.get( id );
  441. },
  442. /**
  443. * Sets the active state.
  444. *
  445. * Bail if we're trying to select the current state, if we haven't
  446. * created the `states` collection, or are trying to select a state
  447. * that does not exist.
  448. *
  449. * @since 3.5.0
  450. *
  451. * @param {string} id
  452. *
  453. * @fires wp.media.controller.State#deactivate
  454. * @fires wp.media.controller.State#activate
  455. *
  456. * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining
  457. */
  458. setState: function( id ) {
  459. var previous = this.state();
  460. if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) ) {
  461. return this;
  462. }
  463. if ( previous ) {
  464. previous.trigger('deactivate');
  465. this._lastState = previous.id;
  466. }
  467. this._state = id;
  468. this.state().trigger('activate');
  469. return this;
  470. },
  471. /**
  472. * Returns the previous active state.
  473. *
  474. * Call the `state()` method with no parameters to retrieve the current
  475. * active state.
  476. *
  477. * @since 3.5.0
  478. *
  479. * @returns {wp.media.controller.State} Returns a State model
  480. * from the StateMachine collection
  481. */
  482. lastState: function() {
  483. if ( this._lastState ) {
  484. return this.state( this._lastState );
  485. }
  486. }
  487. });
  488. // Map all event binding and triggering on a StateMachine to its `states` collection.
  489. _.each([ 'on', 'off', 'trigger' ], function( method ) {
  490. /**
  491. * @function on
  492. * @memberOf wp.media.controller.StateMachine
  493. * @instance
  494. * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining.
  495. */
  496. /**
  497. * @function off
  498. * @memberOf wp.media.controller.StateMachine
  499. * @instance
  500. * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining.
  501. */
  502. /**
  503. * @function trigger
  504. * @memberOf wp.media.controller.StateMachine
  505. * @instance
  506. * @returns {wp.media.controller.StateMachine} Returns itself to allow chaining.
  507. */
  508. StateMachine.prototype[ method ] = function() {
  509. // Ensure that the `states` collection exists so the `StateMachine`
  510. // can be used as a mixin.
  511. this.states = this.states || new Backbone.Collection();
  512. // Forward the method to the `states` collection.
  513. this.states[ method ].apply( this.states, arguments );
  514. return this;
  515. };
  516. });
  517. module.exports = StateMachine;
  518. /***/ }),
  519. /* 33 */
  520. /***/ (function(module, exports) {
  521. /**
  522. * wp.media.controller.State
  523. *
  524. * A state is a step in a workflow that when set will trigger the controllers
  525. * for the regions to be updated as specified in the frame.
  526. *
  527. * A state has an event-driven lifecycle:
  528. *
  529. * 'ready' triggers when a state is added to a state machine's collection.
  530. * 'activate' triggers when a state is activated by a state machine.
  531. * 'deactivate' triggers when a state is deactivated by a state machine.
  532. * 'reset' is not triggered automatically. It should be invoked by the
  533. * proper controller to reset the state to its default.
  534. *
  535. * @memberOf wp.media.controller
  536. *
  537. * @class
  538. * @augments Backbone.Model
  539. */
  540. var State = Backbone.Model.extend(/** @lends wp.media.controller.State.prototype */{
  541. /**
  542. * Constructor.
  543. *
  544. * @since 3.5.0
  545. */
  546. constructor: function() {
  547. this.on( 'activate', this._preActivate, this );
  548. this.on( 'activate', this.activate, this );
  549. this.on( 'activate', this._postActivate, this );
  550. this.on( 'deactivate', this._deactivate, this );
  551. this.on( 'deactivate', this.deactivate, this );
  552. this.on( 'reset', this.reset, this );
  553. this.on( 'ready', this._ready, this );
  554. this.on( 'ready', this.ready, this );
  555. /**
  556. * Call parent constructor with passed arguments
  557. */
  558. Backbone.Model.apply( this, arguments );
  559. this.on( 'change:menu', this._updateMenu, this );
  560. },
  561. /**
  562. * Ready event callback.
  563. *
  564. * @abstract
  565. * @since 3.5.0
  566. */
  567. ready: function() {},
  568. /**
  569. * Activate event callback.
  570. *
  571. * @abstract
  572. * @since 3.5.0
  573. */
  574. activate: function() {},
  575. /**
  576. * Deactivate event callback.
  577. *
  578. * @abstract
  579. * @since 3.5.0
  580. */
  581. deactivate: function() {},
  582. /**
  583. * Reset event callback.
  584. *
  585. * @abstract
  586. * @since 3.5.0
  587. */
  588. reset: function() {},
  589. /**
  590. * @access private
  591. * @since 3.5.0
  592. */
  593. _ready: function() {
  594. this._updateMenu();
  595. },
  596. /**
  597. * @access private
  598. * @since 3.5.0
  599. */
  600. _preActivate: function() {
  601. this.active = true;
  602. },
  603. /**
  604. * @access private
  605. * @since 3.5.0
  606. */
  607. _postActivate: function() {
  608. this.on( 'change:menu', this._menu, this );
  609. this.on( 'change:titleMode', this._title, this );
  610. this.on( 'change:content', this._content, this );
  611. this.on( 'change:toolbar', this._toolbar, this );
  612. this.frame.on( 'title:render:default', this._renderTitle, this );
  613. this._title();
  614. this._menu();
  615. this._toolbar();
  616. this._content();
  617. this._router();
  618. },
  619. /**
  620. * @access private
  621. * @since 3.5.0
  622. */
  623. _deactivate: function() {
  624. this.active = false;
  625. this.frame.off( 'title:render:default', this._renderTitle, this );
  626. this.off( 'change:menu', this._menu, this );
  627. this.off( 'change:titleMode', this._title, this );
  628. this.off( 'change:content', this._content, this );
  629. this.off( 'change:toolbar', this._toolbar, this );
  630. },
  631. /**
  632. * @access private
  633. * @since 3.5.0
  634. */
  635. _title: function() {
  636. this.frame.title.render( this.get('titleMode') || 'default' );
  637. },
  638. /**
  639. * @access private
  640. * @since 3.5.0
  641. */
  642. _renderTitle: function( view ) {
  643. view.$el.text( this.get('title') || '' );
  644. },
  645. /**
  646. * @access private
  647. * @since 3.5.0
  648. */
  649. _router: function() {
  650. var router = this.frame.router,
  651. mode = this.get('router'),
  652. view;
  653. this.frame.$el.toggleClass( 'hide-router', ! mode );
  654. if ( ! mode ) {
  655. return;
  656. }
  657. this.frame.router.render( mode );
  658. view = router.get();
  659. if ( view && view.select ) {
  660. view.select( this.frame.content.mode() );
  661. }
  662. },
  663. /**
  664. * @access private
  665. * @since 3.5.0
  666. */
  667. _menu: function() {
  668. var menu = this.frame.menu,
  669. mode = this.get('menu'),
  670. view;
  671. this.frame.$el.toggleClass( 'hide-menu', ! mode );
  672. if ( ! mode ) {
  673. return;
  674. }
  675. menu.mode( mode );
  676. view = menu.get();
  677. if ( view && view.select ) {
  678. view.select( this.id );
  679. }
  680. },
  681. /**
  682. * @access private
  683. * @since 3.5.0
  684. */
  685. _updateMenu: function() {
  686. var previous = this.previous('menu'),
  687. menu = this.get('menu');
  688. if ( previous ) {
  689. this.frame.off( 'menu:render:' + previous, this._renderMenu, this );
  690. }
  691. if ( menu ) {
  692. this.frame.on( 'menu:render:' + menu, this._renderMenu, this );
  693. }
  694. },
  695. /**
  696. * Create a view in the media menu for the state.
  697. *
  698. * @access private
  699. * @since 3.5.0
  700. *
  701. * @param {media.view.Menu} view The menu view.
  702. */
  703. _renderMenu: function( view ) {
  704. var menuItem = this.get('menuItem'),
  705. title = this.get('title'),
  706. priority = this.get('priority');
  707. if ( ! menuItem && title ) {
  708. menuItem = { text: title };
  709. if ( priority ) {
  710. menuItem.priority = priority;
  711. }
  712. }
  713. if ( ! menuItem ) {
  714. return;
  715. }
  716. view.set( this.id, menuItem );
  717. }
  718. });
  719. _.each(['toolbar','content'], function( region ) {
  720. /**
  721. * @access private
  722. */
  723. State.prototype[ '_' + region ] = function() {
  724. var mode = this.get( region );
  725. if ( mode ) {
  726. this.frame[ region ].render( mode );
  727. }
  728. };
  729. });
  730. module.exports = State;
  731. /***/ }),
  732. /* 34 */
  733. /***/ (function(module, exports) {
  734. /**
  735. * wp.media.selectionSync
  736. *
  737. * Sync an attachments selection in a state with another state.
  738. *
  739. * Allows for selecting multiple images in the Add Media workflow, and then
  740. * switching to the Insert Gallery workflow while preserving the attachments selection.
  741. *
  742. * @memberOf wp.media
  743. *
  744. * @mixin
  745. */
  746. var selectionSync = {
  747. /**
  748. * @since 3.5.0
  749. */
  750. syncSelection: function() {
  751. var selection = this.get('selection'),
  752. manager = this.frame._selection;
  753. if ( ! this.get('syncSelection') || ! manager || ! selection ) {
  754. return;
  755. }
  756. // If the selection supports multiple items, validate the stored
  757. // attachments based on the new selection's conditions. Record
  758. // the attachments that are not included; we'll maintain a
  759. // reference to those. Other attachments are considered in flux.
  760. if ( selection.multiple ) {
  761. selection.reset( [], { silent: true });
  762. selection.validateAll( manager.attachments );
  763. manager.difference = _.difference( manager.attachments.models, selection.models );
  764. }
  765. // Sync the selection's single item with the master.
  766. selection.single( manager.single );
  767. },
  768. /**
  769. * Record the currently active attachments, which is a combination
  770. * of the selection's attachments and the set of selected
  771. * attachments that this specific selection considered invalid.
  772. * Reset the difference and record the single attachment.
  773. *
  774. * @since 3.5.0
  775. */
  776. recordSelection: function() {
  777. var selection = this.get('selection'),
  778. manager = this.frame._selection;
  779. if ( ! this.get('syncSelection') || ! manager || ! selection ) {
  780. return;
  781. }
  782. if ( selection.multiple ) {
  783. manager.attachments.reset( selection.toArray().concat( manager.difference ) );
  784. manager.difference = [];
  785. } else {
  786. manager.attachments.add( selection.toArray() );
  787. }
  788. manager.single = selection._single;
  789. }
  790. };
  791. module.exports = selectionSync;
  792. /***/ }),
  793. /* 35 */
  794. /***/ (function(module, exports) {
  795. var l10n = wp.media.view.l10n,
  796. getUserSetting = window.getUserSetting,
  797. setUserSetting = window.setUserSetting,
  798. Library;
  799. /**
  800. * wp.media.controller.Library
  801. *
  802. * A state for choosing an attachment or group of attachments from the media library.
  803. *
  804. * @memberOf wp.media.controller
  805. *
  806. * @class
  807. * @augments wp.media.controller.State
  808. * @augments Backbone.Model
  809. * @mixes media.selectionSync
  810. *
  811. * @param {object} [attributes] The attributes hash passed to the state.
  812. * @param {string} [attributes.id=library] Unique identifier.
  813. * @param {string} [attributes.title=Media library] Title for the state. Displays in the media menu and the frame's title region.
  814. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
  815. * If one is not supplied, a collection of all attachments will be created.
  816. * @param {wp.media.model.Selection|object} [attributes.selection] A collection to contain attachment selections within the state.
  817. * If the 'selection' attribute is a plain JS object,
  818. * a Selection will be created using its values as the selection instance's `props` model.
  819. * Otherwise, it will copy the library's `props` model.
  820. * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
  821. * @param {string} [attributes.content=upload] Initial mode for the content region.
  822. * Overridden by persistent user setting if 'contentUserSetting' is true.
  823. * @param {string} [attributes.menu=default] Initial mode for the menu region.
  824. * @param {string} [attributes.router=browse] Initial mode for the router region.
  825. * @param {string} [attributes.toolbar=select] Initial mode for the toolbar region.
  826. * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
  827. * @param {boolean|string} [attributes.filterable=false] Whether the library is filterable, and if so what filters should be shown.
  828. * Accepts 'all', 'uploaded', or 'unattached'.
  829. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  830. * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
  831. * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
  832. * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
  833. * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state.
  834. */
  835. Library = wp.media.controller.State.extend(/** @lends wp.media.controller.Library.prototype */{
  836. defaults: {
  837. id: 'library',
  838. title: l10n.mediaLibraryTitle,
  839. multiple: false,
  840. content: 'upload',
  841. menu: 'default',
  842. router: 'browse',
  843. toolbar: 'select',
  844. searchable: true,
  845. filterable: false,
  846. sortable: true,
  847. autoSelect: true,
  848. describe: false,
  849. contentUserSetting: true,
  850. syncSelection: true
  851. },
  852. /**
  853. * If a library isn't provided, query all media items.
  854. * If a selection instance isn't provided, create one.
  855. *
  856. * @since 3.5.0
  857. */
  858. initialize: function() {
  859. var selection = this.get('selection'),
  860. props;
  861. if ( ! this.get('library') ) {
  862. this.set( 'library', wp.media.query() );
  863. }
  864. if ( ! ( selection instanceof wp.media.model.Selection ) ) {
  865. props = selection;
  866. if ( ! props ) {
  867. props = this.get('library').props.toJSON();
  868. props = _.omit( props, 'orderby', 'query' );
  869. }
  870. this.set( 'selection', new wp.media.model.Selection( null, {
  871. multiple: this.get('multiple'),
  872. props: props
  873. }) );
  874. }
  875. this.resetDisplays();
  876. },
  877. /**
  878. * @since 3.5.0
  879. */
  880. activate: function() {
  881. this.syncSelection();
  882. wp.Uploader.queue.on( 'add', this.uploading, this );
  883. this.get('selection').on( 'add remove reset', this.refreshContent, this );
  884. if ( this.get( 'router' ) && this.get('contentUserSetting') ) {
  885. this.frame.on( 'content:activate', this.saveContentMode, this );
  886. this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) );
  887. }
  888. },
  889. /**
  890. * @since 3.5.0
  891. */
  892. deactivate: function() {
  893. this.recordSelection();
  894. this.frame.off( 'content:activate', this.saveContentMode, this );
  895. // Unbind all event handlers that use this state as the context
  896. // from the selection.
  897. this.get('selection').off( null, null, this );
  898. wp.Uploader.queue.off( null, null, this );
  899. },
  900. /**
  901. * Reset the library to its initial state.
  902. *
  903. * @since 3.5.0
  904. */
  905. reset: function() {
  906. this.get('selection').reset();
  907. this.resetDisplays();
  908. this.refreshContent();
  909. },
  910. /**
  911. * Reset the attachment display settings defaults to the site options.
  912. *
  913. * If site options don't define them, fall back to a persistent user setting.
  914. *
  915. * @since 3.5.0
  916. */
  917. resetDisplays: function() {
  918. var defaultProps = wp.media.view.settings.defaultProps;
  919. this._displays = [];
  920. this._defaultDisplaySettings = {
  921. align: getUserSetting( 'align', defaultProps.align ) || 'none',
  922. size: getUserSetting( 'imgsize', defaultProps.size ) || 'medium',
  923. link: getUserSetting( 'urlbutton', defaultProps.link ) || 'none'
  924. };
  925. },
  926. /**
  927. * Create a model to represent display settings (alignment, etc.) for an attachment.
  928. *
  929. * @since 3.5.0
  930. *
  931. * @param {wp.media.model.Attachment} attachment
  932. * @returns {Backbone.Model}
  933. */
  934. display: function( attachment ) {
  935. var displays = this._displays;
  936. if ( ! displays[ attachment.cid ] ) {
  937. displays[ attachment.cid ] = new Backbone.Model( this.defaultDisplaySettings( attachment ) );
  938. }
  939. return displays[ attachment.cid ];
  940. },
  941. /**
  942. * Given an attachment, create attachment display settings properties.
  943. *
  944. * @since 3.6.0
  945. *
  946. * @param {wp.media.model.Attachment} attachment
  947. * @returns {Object}
  948. */
  949. defaultDisplaySettings: function( attachment ) {
  950. var settings = _.clone( this._defaultDisplaySettings );
  951. if ( settings.canEmbed = this.canEmbed( attachment ) ) {
  952. settings.link = 'embed';
  953. } else if ( ! this.isImageAttachment( attachment ) && settings.link === 'none' ) {
  954. settings.link = 'file';
  955. }
  956. return settings;
  957. },
  958. /**
  959. * Whether an attachment is image.
  960. *
  961. * @since 4.4.1
  962. *
  963. * @param {wp.media.model.Attachment} attachment
  964. * @returns {Boolean}
  965. */
  966. isImageAttachment: function( attachment ) {
  967. // If uploading, we know the filename but not the mime type.
  968. if ( attachment.get('uploading') ) {
  969. return /\.(jpe?g|png|gif)$/i.test( attachment.get('filename') );
  970. }
  971. return attachment.get('type') === 'image';
  972. },
  973. /**
  974. * Whether an attachment can be embedded (audio or video).
  975. *
  976. * @since 3.6.0
  977. *
  978. * @param {wp.media.model.Attachment} attachment
  979. * @returns {Boolean}
  980. */
  981. canEmbed: function( attachment ) {
  982. // If uploading, we know the filename but not the mime type.
  983. if ( ! attachment.get('uploading') ) {
  984. var type = attachment.get('type');
  985. if ( type !== 'audio' && type !== 'video' ) {
  986. return false;
  987. }
  988. }
  989. return _.contains( wp.media.view.settings.embedExts, attachment.get('filename').split('.').pop() );
  990. },
  991. /**
  992. * If the state is active, no items are selected, and the current
  993. * content mode is not an option in the state's router (provided
  994. * the state has a router), reset the content mode to the default.
  995. *
  996. * @since 3.5.0
  997. */
  998. refreshContent: function() {
  999. var selection = this.get('selection'),
  1000. frame = this.frame,
  1001. router = frame.router.get(),
  1002. mode = frame.content.mode();
  1003. if ( this.active && ! selection.length && router && ! router.get( mode ) ) {
  1004. this.frame.content.render( this.get('content') );
  1005. }
  1006. },
  1007. /**
  1008. * Callback handler when an attachment is uploaded.
  1009. *
  1010. * Switch to the Media Library if uploaded from the 'Upload Files' tab.
  1011. *
  1012. * Adds any uploading attachments to the selection.
  1013. *
  1014. * If the state only supports one attachment to be selected and multiple
  1015. * attachments are uploaded, the last attachment in the upload queue will
  1016. * be selected.
  1017. *
  1018. * @since 3.5.0
  1019. *
  1020. * @param {wp.media.model.Attachment} attachment
  1021. */
  1022. uploading: function( attachment ) {
  1023. var content = this.frame.content;
  1024. if ( 'upload' === content.mode() ) {
  1025. this.frame.content.mode('browse');
  1026. }
  1027. if ( this.get( 'autoSelect' ) ) {
  1028. this.get('selection').add( attachment );
  1029. this.frame.trigger( 'library:selection:add' );
  1030. }
  1031. },
  1032. /**
  1033. * Persist the mode of the content region as a user setting.
  1034. *
  1035. * @since 3.5.0
  1036. */
  1037. saveContentMode: function() {
  1038. if ( 'browse' !== this.get('router') ) {
  1039. return;
  1040. }
  1041. var mode = this.frame.content.mode(),
  1042. view = this.frame.router.get();
  1043. if ( view && view.get( mode ) ) {
  1044. setUserSetting( 'libraryContent', mode );
  1045. }
  1046. }
  1047. });
  1048. // Make selectionSync available on any Media Library state.
  1049. _.extend( Library.prototype, wp.media.selectionSync );
  1050. module.exports = Library;
  1051. /***/ }),
  1052. /* 36 */
  1053. /***/ (function(module, exports) {
  1054. var State = wp.media.controller.State,
  1055. Library = wp.media.controller.Library,
  1056. l10n = wp.media.view.l10n,
  1057. ImageDetails;
  1058. /**
  1059. * wp.media.controller.ImageDetails
  1060. *
  1061. * A state for editing the attachment display settings of an image that's been
  1062. * inserted into the editor.
  1063. *
  1064. * @memberOf wp.media.controller
  1065. *
  1066. * @class
  1067. * @augments wp.media.controller.State
  1068. * @augments Backbone.Model
  1069. *
  1070. * @param {object} [attributes] The attributes hash passed to the state.
  1071. * @param {string} [attributes.id=image-details] Unique identifier.
  1072. * @param {string} [attributes.title=Image Details] Title for the state. Displays in the frame's title region.
  1073. * @param {wp.media.model.Attachment} attributes.image The image's model.
  1074. * @param {string|false} [attributes.content=image-details] Initial mode for the content region.
  1075. * @param {string|false} [attributes.menu=false] Initial mode for the menu region.
  1076. * @param {string|false} [attributes.router=false] Initial mode for the router region.
  1077. * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region.
  1078. * @param {boolean} [attributes.editing=false] Unused.
  1079. * @param {int} [attributes.priority=60] Unused.
  1080. *
  1081. * @todo This state inherits some defaults from media.controller.Library.prototype.defaults,
  1082. * however this may not do anything.
  1083. */
  1084. ImageDetails = State.extend(/** @lends wp.media.controller.ImageDetails.prototype */{
  1085. defaults: _.defaults({
  1086. id: 'image-details',
  1087. title: l10n.imageDetailsTitle,
  1088. content: 'image-details',
  1089. menu: false,
  1090. router: false,
  1091. toolbar: 'image-details',
  1092. editing: false,
  1093. priority: 60
  1094. }, Library.prototype.defaults ),
  1095. /**
  1096. * @since 3.9.0
  1097. *
  1098. * @param options Attributes
  1099. */
  1100. initialize: function( options ) {
  1101. this.image = options.image;
  1102. State.prototype.initialize.apply( this, arguments );
  1103. },
  1104. /**
  1105. * @since 3.9.0
  1106. */
  1107. activate: function() {
  1108. this.frame.modal.$el.addClass('image-details');
  1109. }
  1110. });
  1111. module.exports = ImageDetails;
  1112. /***/ }),
  1113. /* 37 */
  1114. /***/ (function(module, exports) {
  1115. var Library = wp.media.controller.Library,
  1116. l10n = wp.media.view.l10n,
  1117. GalleryEdit;
  1118. /**
  1119. * wp.media.controller.GalleryEdit
  1120. *
  1121. * A state for editing a gallery's images and settings.
  1122. *
  1123. * @since 3.5.0
  1124. *
  1125. * @class
  1126. * @augments wp.media.controller.Library
  1127. * @augments wp.media.controller.State
  1128. * @augments Backbone.Model
  1129. *
  1130. * @memberOf wp.media.controller
  1131. *
  1132. * @param {Object} [attributes] The attributes hash passed to the state.
  1133. * @param {string} [attributes.id=gallery-edit] Unique identifier.
  1134. * @param {string} [attributes.title=Edit Gallery] Title for the state. Displays in the frame's title region.
  1135. * @param {wp.media.model.Attachments} [attributes.library] The collection of attachments in the gallery.
  1136. * If one is not supplied, an empty media.model.Selection collection is created.
  1137. * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
  1138. * @param {boolean} [attributes.searchable=false] Whether the library is searchable.
  1139. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  1140. * @param {boolean} [attributes.date=true] Whether to show the date filter in the browser's toolbar.
  1141. * @param {string|false} [attributes.content=browse] Initial mode for the content region.
  1142. * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region.
  1143. * @param {boolean} [attributes.describe=true] Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
  1144. * @param {boolean} [attributes.displaySettings=true] Whether to show the attachment display settings interface.
  1145. * @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable.
  1146. * @param {number} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments.
  1147. * @param {boolean} [attributes.editing=false] Whether the gallery is being created, or editing an existing instance.
  1148. * @param {number} [attributes.priority=60] The priority for the state link in the media menu.
  1149. * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
  1150. * Defaults to false for this state, because the library passed in *is* the selection.
  1151. * @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`.
  1152. * If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
  1153. */
  1154. GalleryEdit = Library.extend(/** @lends wp.media.controller.GalleryEdit.prototype */{
  1155. defaults: {
  1156. id: 'gallery-edit',
  1157. title: l10n.editGalleryTitle,
  1158. multiple: false,
  1159. searchable: false,
  1160. sortable: true,
  1161. date: false,
  1162. display: false,
  1163. content: 'browse',
  1164. toolbar: 'gallery-edit',
  1165. describe: true,
  1166. displaySettings: true,
  1167. dragInfo: true,
  1168. idealColumnWidth: 170,
  1169. editing: false,
  1170. priority: 60,
  1171. syncSelection: false
  1172. },
  1173. /**
  1174. * Initializes the library.
  1175. *
  1176. * Creates a selection if a library isn't supplied and creates an attachment
  1177. * view if no attachment view is supplied.
  1178. *
  1179. * @since 3.5.0
  1180. *
  1181. * @return {void}
  1182. */
  1183. initialize: function() {
  1184. // If we haven't been provided a `library`, create a `Selection`.
  1185. if ( ! this.get('library') ) {
  1186. this.set( 'library', new wp.media.model.Selection() );
  1187. }
  1188. // The single `Attachment` view to be used in the `Attachments` view.
  1189. if ( ! this.get('AttachmentView') ) {
  1190. this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary );
  1191. }
  1192. Library.prototype.initialize.apply( this, arguments );
  1193. },
  1194. /**
  1195. * Activates the library.
  1196. *
  1197. * Limits the library to images, watches for uploaded attachments. Watches for
  1198. * the browse event on the frame and binds it to gallerySettings.
  1199. *
  1200. * @since 3.5.0
  1201. *
  1202. * @return {void}
  1203. */
  1204. activate: function() {
  1205. var library = this.get('library');
  1206. // Limit the library to images only.
  1207. library.props.set( 'type', 'image' );
  1208. // Watch for uploaded attachments.
  1209. this.get('library').observe( wp.Uploader.queue );
  1210. this.frame.on( 'content:render:browse', this.gallerySettings, this );
  1211. Library.prototype.activate.apply( this, arguments );
  1212. },
  1213. /**
  1214. * Deactivates the library.
  1215. *
  1216. * Stops watching for uploaded attachments and browse events.
  1217. *
  1218. * @since 3.5.0
  1219. *
  1220. * @return {void}
  1221. */
  1222. deactivate: function() {
  1223. // Stop watching for uploaded attachments.
  1224. this.get('library').unobserve( wp.Uploader.queue );
  1225. this.frame.off( 'content:render:browse', this.gallerySettings, this );
  1226. Library.prototype.deactivate.apply( this, arguments );
  1227. },
  1228. /**
  1229. * Adds the gallery settings to the sidebar and adds a reverse button to the
  1230. * toolbar.
  1231. *
  1232. * @since 3.5.0
  1233. *
  1234. * @param {wp.media.view.Frame} browser The file browser.
  1235. *
  1236. * @return {void}
  1237. */
  1238. gallerySettings: function( browser ) {
  1239. if ( ! this.get('displaySettings') ) {
  1240. return;
  1241. }
  1242. var library = this.get('library');
  1243. if ( ! library || ! browser ) {
  1244. return;
  1245. }
  1246. library.gallery = library.gallery || new Backbone.Model();
  1247. browser.sidebar.set({
  1248. gallery: new wp.media.view.Settings.Gallery({
  1249. controller: this,
  1250. model: library.gallery,
  1251. priority: 40
  1252. })
  1253. });
  1254. browser.toolbar.set( 'reverse', {
  1255. text: l10n.reverseOrder,
  1256. priority: 80,
  1257. click: function() {
  1258. library.reset( library.toArray().reverse() );
  1259. }
  1260. });
  1261. }
  1262. });
  1263. module.exports = GalleryEdit;
  1264. /***/ }),
  1265. /* 38 */
  1266. /***/ (function(module, exports) {
  1267. var Selection = wp.media.model.Selection,
  1268. Library = wp.media.controller.Library,
  1269. l10n = wp.media.view.l10n,
  1270. GalleryAdd;
  1271. /**
  1272. * wp.media.controller.GalleryAdd
  1273. *
  1274. * A state for selecting more images to add to a gallery.
  1275. *
  1276. * @since 3.5.0
  1277. *
  1278. * @class
  1279. * @augments wp.media.controller.Library
  1280. * @augments wp.media.controller.State
  1281. * @augments Backbone.Model
  1282. *
  1283. * @memberof wp.media.controller
  1284. *
  1285. * @param {Object} [attributes] The attributes hash passed to the state.
  1286. * @param {string} [attributes.id=gallery-library] Unique identifier.
  1287. * @param {string} [attributes.title=Add to Gallery] Title for the state. Displays in the frame's title region.
  1288. * @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
  1289. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
  1290. * If one is not supplied, a collection of all images will be created.
  1291. * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown.
  1292. * Accepts 'all', 'uploaded', or 'unattached'.
  1293. * @param {string} [attributes.menu=gallery] Initial mode for the menu region.
  1294. * @param {string} [attributes.content=upload] Initial mode for the content region.
  1295. * Overridden by persistent user setting if 'contentUserSetting' is true.
  1296. * @param {string} [attributes.router=browse] Initial mode for the router region.
  1297. * @param {string} [attributes.toolbar=gallery-add] Initial mode for the toolbar region.
  1298. * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
  1299. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  1300. * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
  1301. * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
  1302. * @param {number} [attributes.priority=100] The priority for the state link in the media menu.
  1303. * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
  1304. * Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
  1305. */
  1306. GalleryAdd = Library.extend(/** @lends wp.media.controller.GalleryAdd.prototype */{
  1307. defaults: _.defaults({
  1308. id: 'gallery-library',
  1309. title: l10n.addToGalleryTitle,
  1310. multiple: 'add',
  1311. filterable: 'uploaded',
  1312. menu: 'gallery',
  1313. toolbar: 'gallery-add',
  1314. priority: 100,
  1315. syncSelection: false
  1316. }, Library.prototype.defaults ),
  1317. /**
  1318. * Initializes the library. Creates a library of images if a library isn't supplied.
  1319. *
  1320. * @since 3.5.0
  1321. *
  1322. * @returns {void}
  1323. */
  1324. initialize: function() {
  1325. if ( ! this.get('library') ) {
  1326. this.set( 'library', wp.media.query({ type: 'image' }) );
  1327. }
  1328. Library.prototype.initialize.apply( this, arguments );
  1329. },
  1330. /**
  1331. * Activates the library.
  1332. *
  1333. * Removes all event listeners if in edit mode. Creates a validator to check an attachment.
  1334. * Resets library and re-enables event listeners. Activates edit mode. Calls the parent's activate method.
  1335. *
  1336. * @since 3.5.0
  1337. *
  1338. * @returns {void}
  1339. */
  1340. activate: function() {
  1341. var library = this.get('library'),
  1342. edit = this.frame.state('gallery-edit').get('library');
  1343. if ( this.editLibrary && this.editLibrary !== edit ) {
  1344. library.unobserve( this.editLibrary );
  1345. }
  1346. /*
  1347. * Accept attachments that exist in the original library but
  1348. * that do not exist in gallery's library yet.
  1349. */
  1350. library.validator = function( attachment ) {
  1351. return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments );
  1352. };
  1353. /*
  1354. * Reset the library to ensure that all attachments are re-added
  1355. * to the collection. Do so silently, as calling `observe` will
  1356. * trigger the `reset` event.
  1357. */
  1358. library.reset( library.mirroring.models, { silent: true });
  1359. library.observe( edit );
  1360. this.editLibrary = edit;
  1361. Library.prototype.activate.apply( this, arguments );
  1362. }
  1363. });
  1364. module.exports = GalleryAdd;
  1365. /***/ }),
  1366. /* 39 */
  1367. /***/ (function(module, exports) {
  1368. var Library = wp.media.controller.Library,
  1369. l10n = wp.media.view.l10n,
  1370. $ = jQuery,
  1371. CollectionEdit;
  1372. /**
  1373. * wp.media.controller.CollectionEdit
  1374. *
  1375. * A state for editing a collection, which is used by audio and video playlists,
  1376. * and can be used for other collections.
  1377. *
  1378. * @memberOf wp.media.controller
  1379. *
  1380. * @class
  1381. * @augments wp.media.controller.Library
  1382. * @augments wp.media.controller.State
  1383. * @augments Backbone.Model
  1384. *
  1385. * @param {object} [attributes] The attributes hash passed to the state.
  1386. * @param {string} attributes.title Title for the state. Displays in the media menu and the frame's title region.
  1387. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to edit.
  1388. * If one is not supplied, an empty media.model.Selection collection is created.
  1389. * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
  1390. * @param {string} [attributes.content=browse] Initial mode for the content region.
  1391. * @param {string} attributes.menu Initial mode for the menu region. @todo this needs a better explanation.
  1392. * @param {boolean} [attributes.searchable=false] Whether the library is searchable.
  1393. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  1394. * @param {boolean} [attributes.date=true] Whether to show the date filter in the browser's toolbar.
  1395. * @param {boolean} [attributes.describe=true] Whether to offer UI to describe the attachments - e.g. captioning images in a gallery.
  1396. * @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable.
  1397. * @param {boolean} [attributes.dragInfoText] Instructional text about the attachments being sortable.
  1398. * @param {int} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments.
  1399. * @param {boolean} [attributes.editing=false] Whether the gallery is being created, or editing an existing instance.
  1400. * @param {int} [attributes.priority=60] The priority for the state link in the media menu.
  1401. * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
  1402. * Defaults to false for this state, because the library passed in *is* the selection.
  1403. * @param {view} [attributes.SettingsView] The view to edit the collection instance settings (e.g. Playlist settings with "Show tracklist" checkbox).
  1404. * @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`.
  1405. * If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
  1406. * @param {string} attributes.type The collection's media type. (e.g. 'video').
  1407. * @param {string} attributes.collectionType The collection type. (e.g. 'playlist').
  1408. */
  1409. CollectionEdit = Library.extend(/** @lends wp.media.controller.CollectionEdit.prototype */{
  1410. defaults: {
  1411. multiple: false,
  1412. sortable: true,
  1413. date: false,
  1414. searchable: false,
  1415. content: 'browse',
  1416. describe: true,
  1417. dragInfo: true,
  1418. idealColumnWidth: 170,
  1419. editing: false,
  1420. priority: 60,
  1421. SettingsView: false,
  1422. syncSelection: false
  1423. },
  1424. /**
  1425. * @since 3.9.0
  1426. */
  1427. initialize: function() {
  1428. var collectionType = this.get('collectionType');
  1429. if ( 'video' === this.get( 'type' ) ) {
  1430. collectionType = 'video-' + collectionType;
  1431. }
  1432. this.set( 'id', collectionType + '-edit' );
  1433. this.set( 'toolbar', collectionType + '-edit' );
  1434. // If we haven't been provided a `library`, create a `Selection`.
  1435. if ( ! this.get('library') ) {
  1436. this.set( 'library', new wp.media.model.Selection() );
  1437. }
  1438. // The single `Attachment` view to be used in the `Attachments` view.
  1439. if ( ! this.get('AttachmentView') ) {
  1440. this.set( 'AttachmentView', wp.media.view.Attachment.EditLibrary );
  1441. }
  1442. Library.prototype.initialize.apply( this, arguments );
  1443. },
  1444. /**
  1445. * @since 3.9.0
  1446. */
  1447. activate: function() {
  1448. var library = this.get('library');
  1449. // Limit the library to images only.
  1450. library.props.set( 'type', this.get( 'type' ) );
  1451. // Watch for uploaded attachments.
  1452. this.get('library').observe( wp.Uploader.queue );
  1453. this.frame.on( 'content:render:browse', this.renderSettings, this );
  1454. Library.prototype.activate.apply( this, arguments );
  1455. },
  1456. /**
  1457. * @since 3.9.0
  1458. */
  1459. deactivate: function() {
  1460. // Stop watching for uploaded attachments.
  1461. this.get('library').unobserve( wp.Uploader.queue );
  1462. this.frame.off( 'content:render:browse', this.renderSettings, this );
  1463. Library.prototype.deactivate.apply( this, arguments );
  1464. },
  1465. /**
  1466. * Render the collection embed settings view in the browser sidebar.
  1467. *
  1468. * @todo This is against the pattern elsewhere in media. Typically the frame
  1469. * is responsible for adding region mode callbacks. Explain.
  1470. *
  1471. * @since 3.9.0
  1472. *
  1473. * @param {wp.media.view.attachmentsBrowser} The attachments browser view.
  1474. */
  1475. renderSettings: function( attachmentsBrowserView ) {
  1476. var library = this.get('library'),
  1477. collectionType = this.get('collectionType'),
  1478. dragInfoText = this.get('dragInfoText'),
  1479. SettingsView = this.get('SettingsView'),
  1480. obj = {};
  1481. if ( ! library || ! attachmentsBrowserView ) {
  1482. return;
  1483. }
  1484. library[ collectionType ] = library[ collectionType ] || new Backbone.Model();
  1485. obj[ collectionType ] = new SettingsView({
  1486. controller: this,
  1487. model: library[ collectionType ],
  1488. priority: 40
  1489. });
  1490. attachmentsBrowserView.sidebar.set( obj );
  1491. if ( dragInfoText ) {
  1492. attachmentsBrowserView.toolbar.set( 'dragInfo', new wp.media.View({
  1493. el: $( '<div class="instructions">' + dragInfoText + '</div>' )[0],
  1494. priority: -40
  1495. }) );
  1496. }
  1497. // Add the 'Reverse order' button to the toolbar.
  1498. attachmentsBrowserView.toolbar.set( 'reverse', {
  1499. text: l10n.reverseOrder,
  1500. priority: 80,
  1501. click: function() {
  1502. library.reset( library.toArray().reverse() );
  1503. }
  1504. });
  1505. }
  1506. });
  1507. module.exports = CollectionEdit;
  1508. /***/ }),
  1509. /* 40 */
  1510. /***/ (function(module, exports) {
  1511. var Selection = wp.media.model.Selection,
  1512. Library = wp.media.controller.Library,
  1513. CollectionAdd;
  1514. /**
  1515. * wp.media.controller.CollectionAdd
  1516. *
  1517. * A state for adding attachments to a collection (e.g. video playlist).
  1518. *
  1519. * @memberOf wp.media.controller
  1520. *
  1521. * @class
  1522. * @augments wp.media.controller.Library
  1523. * @augments wp.media.controller.State
  1524. * @augments Backbone.Model
  1525. *
  1526. * @param {object} [attributes] The attributes hash passed to the state.
  1527. * @param {string} [attributes.id=library] Unique identifier.
  1528. * @param {string} attributes.title Title for the state. Displays in the frame's title region.
  1529. * @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
  1530. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
  1531. * If one is not supplied, a collection of attachments of the specified type will be created.
  1532. * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown.
  1533. * Accepts 'all', 'uploaded', or 'unattached'.
  1534. * @param {string} [attributes.menu=gallery] Initial mode for the menu region.
  1535. * @param {string} [attributes.content=upload] Initial mode for the content region.
  1536. * Overridden by persistent user setting if 'contentUserSetting' is true.
  1537. * @param {string} [attributes.router=browse] Initial mode for the router region.
  1538. * @param {string} [attributes.toolbar=gallery-add] Initial mode for the toolbar region.
  1539. * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
  1540. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  1541. * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
  1542. * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
  1543. * @param {int} [attributes.priority=100] The priority for the state link in the media menu.
  1544. * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
  1545. * Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
  1546. * @param {string} attributes.type The collection's media type. (e.g. 'video').
  1547. * @param {string} attributes.collectionType The collection type. (e.g. 'playlist').
  1548. */
  1549. CollectionAdd = Library.extend(/** @lends wp.media.controller.CollectionAdd.prototype */{
  1550. defaults: _.defaults( {
  1551. // Selection defaults. @see media.model.Selection
  1552. multiple: 'add',
  1553. // Attachments browser defaults. @see media.view.AttachmentsBrowser
  1554. filterable: 'uploaded',
  1555. priority: 100,
  1556. syncSelection: false
  1557. }, Library.prototype.defaults ),
  1558. /**
  1559. * @since 3.9.0
  1560. */
  1561. initialize: function() {
  1562. var collectionType = this.get('collectionType');
  1563. if ( 'video' === this.get( 'type' ) ) {
  1564. collectionType = 'video-' + collectionType;
  1565. }
  1566. this.set( 'id', collectionType + '-library' );
  1567. this.set( 'toolbar', collectionType + '-add' );
  1568. this.set( 'menu', collectionType );
  1569. // If we haven't been provided a `library`, create a `Selection`.
  1570. if ( ! this.get('library') ) {
  1571. this.set( 'library', wp.media.query({ type: this.get('type') }) );
  1572. }
  1573. Library.prototype.initialize.apply( this, arguments );
  1574. },
  1575. /**
  1576. * @since 3.9.0
  1577. */
  1578. activate: function() {
  1579. var library = this.get('library'),
  1580. editLibrary = this.get('editLibrary'),
  1581. edit = this.frame.state( this.get('collectionType') + '-edit' ).get('library');
  1582. if ( editLibrary && editLibrary !== edit ) {
  1583. library.unobserve( editLibrary );
  1584. }
  1585. // Accepts attachments that exist in the original library and
  1586. // that do not exist in gallery's library.
  1587. library.validator = function( attachment ) {
  1588. return !! this.mirroring.get( attachment.cid ) && ! edit.get( attachment.cid ) && Selection.prototype.validator.apply( this, arguments );
  1589. };
  1590. // Reset the library to ensure that all attachments are re-added
  1591. // to the collection. Do so silently, as calling `observe` will
  1592. // trigger the `reset` event.
  1593. library.reset( library.mirroring.models, { silent: true });
  1594. library.observe( edit );
  1595. this.set('editLibrary', edit);
  1596. Library.prototype.activate.apply( this, arguments );
  1597. }
  1598. });
  1599. module.exports = CollectionAdd;
  1600. /***/ }),
  1601. /* 41 */
  1602. /***/ (function(module, exports) {
  1603. var Attachment = wp.media.model.Attachment,
  1604. Library = wp.media.controller.Library,
  1605. l10n = wp.media.view.l10n,
  1606. FeaturedImage;
  1607. /**
  1608. * wp.media.controller.FeaturedImage
  1609. *
  1610. * A state for selecting a featured image for a post.
  1611. *
  1612. * @memberOf wp.media.controller
  1613. *
  1614. * @class
  1615. * @augments wp.media.controller.Library
  1616. * @augments wp.media.controller.State
  1617. * @augments Backbone.Model
  1618. *
  1619. * @param {object} [attributes] The attributes hash passed to the state.
  1620. * @param {string} [attributes.id=featured-image] Unique identifier.
  1621. * @param {string} [attributes.title=Set Featured Image] Title for the state. Displays in the media menu and the frame's title region.
  1622. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
  1623. * If one is not supplied, a collection of all images will be created.
  1624. * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
  1625. * @param {string} [attributes.content=upload] Initial mode for the content region.
  1626. * Overridden by persistent user setting if 'contentUserSetting' is true.
  1627. * @param {string} [attributes.menu=default] Initial mode for the menu region.
  1628. * @param {string} [attributes.router=browse] Initial mode for the router region.
  1629. * @param {string} [attributes.toolbar=featured-image] Initial mode for the toolbar region.
  1630. * @param {int} [attributes.priority=60] The priority for the state link in the media menu.
  1631. * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
  1632. * @param {boolean|string} [attributes.filterable=false] Whether the library is filterable, and if so what filters should be shown.
  1633. * Accepts 'all', 'uploaded', or 'unattached'.
  1634. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  1635. * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
  1636. * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
  1637. * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
  1638. * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state.
  1639. */
  1640. FeaturedImage = Library.extend(/** @lends wp.media.controller.FeaturedImage.prototype */{
  1641. defaults: _.defaults({
  1642. id: 'featured-image',
  1643. title: l10n.setFeaturedImageTitle,
  1644. multiple: false,
  1645. filterable: 'uploaded',
  1646. toolbar: 'featured-image',
  1647. priority: 60,
  1648. syncSelection: true
  1649. }, Library.prototype.defaults ),
  1650. /**
  1651. * @since 3.5.0
  1652. */
  1653. initialize: function() {
  1654. var library, comparator;
  1655. // If we haven't been provided a `library`, create a `Selection`.
  1656. if ( ! this.get('library') ) {
  1657. this.set( 'library', wp.media.query({ type: 'image' }) );
  1658. }
  1659. Library.prototype.initialize.apply( this, arguments );
  1660. library = this.get('library');
  1661. comparator = library.comparator;
  1662. // Overload the library's comparator to push items that are not in
  1663. // the mirrored query to the front of the aggregate collection.
  1664. library.comparator = function( a, b ) {
  1665. var aInQuery = !! this.mirroring.get( a.cid ),
  1666. bInQuery = !! this.mirroring.get( b.cid );
  1667. if ( ! aInQuery && bInQuery ) {
  1668. return -1;
  1669. } else if ( aInQuery && ! bInQuery ) {
  1670. return 1;
  1671. } else {
  1672. return comparator.apply( this, arguments );
  1673. }
  1674. };
  1675. // Add all items in the selection to the library, so any featured
  1676. // images that are not initially loaded still appear.
  1677. library.observe( this.get('selection') );
  1678. },
  1679. /**
  1680. * @since 3.5.0
  1681. */
  1682. activate: function() {
  1683. this.updateSelection();
  1684. this.frame.on( 'open', this.updateSelection, this );
  1685. Library.prototype.activate.apply( this, arguments );
  1686. },
  1687. /**
  1688. * @since 3.5.0
  1689. */
  1690. deactivate: function() {
  1691. this.frame.off( 'open', this.updateSelection, this );
  1692. Library.prototype.deactivate.apply( this, arguments );
  1693. },
  1694. /**
  1695. * @since 3.5.0
  1696. */
  1697. updateSelection: function() {
  1698. var selection = this.get('selection'),
  1699. id = wp.media.view.settings.post.featuredImageId,
  1700. attachment;
  1701. if ( '' !== id && -1 !== id ) {
  1702. attachment = Attachment.get( id );
  1703. attachment.fetch();
  1704. }
  1705. selection.reset( attachment ? [ attachment ] : [] );
  1706. }
  1707. });
  1708. module.exports = FeaturedImage;
  1709. /***/ }),
  1710. /* 42 */
  1711. /***/ (function(module, exports) {
  1712. var Library = wp.media.controller.Library,
  1713. l10n = wp.media.view.l10n,
  1714. ReplaceImage;
  1715. /**
  1716. * wp.media.controller.ReplaceImage
  1717. *
  1718. * A state for replacing an image.
  1719. *
  1720. * @memberOf wp.media.controller
  1721. *
  1722. * @class
  1723. * @augments wp.media.controller.Library
  1724. * @augments wp.media.controller.State
  1725. * @augments Backbone.Model
  1726. *
  1727. * @param {object} [attributes] The attributes hash passed to the state.
  1728. * @param {string} [attributes.id=replace-image] Unique identifier.
  1729. * @param {string} [attributes.title=Replace Image] Title for the state. Displays in the media menu and the frame's title region.
  1730. * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
  1731. * If one is not supplied, a collection of all images will be created.
  1732. * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
  1733. * @param {string} [attributes.content=upload] Initial mode for the content region.
  1734. * Overridden by persistent user setting if 'contentUserSetting' is true.
  1735. * @param {string} [attributes.menu=default] Initial mode for the menu region.
  1736. * @param {string} [attributes.router=browse] Initial mode for the router region.
  1737. * @param {string} [attributes.toolbar=replace] Initial mode for the toolbar region.
  1738. * @param {int} [attributes.priority=60] The priority for the state link in the media menu.
  1739. * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
  1740. * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown.
  1741. * Accepts 'all', 'uploaded', or 'unattached'.
  1742. * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
  1743. * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
  1744. * @param {boolean} [attributes.describe=false] Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
  1745. * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
  1746. * @param {boolean} [attributes.syncSelection=true] Whether the Attachments selection should be persisted from the last state.
  1747. */
  1748. ReplaceImage = Library.extend(/** @lends wp.media.controller.ReplaceImage.prototype */{
  1749. defaults: _.defaults({
  1750. id: 'replace-image',
  1751. title: l10n.replaceImageTitle,
  1752. multiple: false,
  1753. filterable: 'uploaded',
  1754. toolbar: 'replace',
  1755. menu: false,
  1756. priority: 60,
  1757. syncSelection: true
  1758. }, Library.prototype.defaults ),
  1759. /**
  1760. * @since 3.9.0
  1761. *
  1762. * @param options
  1763. */
  1764. initialize: function( options ) {
  1765. var library, comparator;
  1766. this.image = options.image;
  1767. // If we haven't been provided a `library`, create a `Selection`.
  1768. if ( ! this.get('library') ) {
  1769. this.set( 'library', wp.media.query({ type: 'image' }) );
  1770. }
  1771. Library.prototype.initialize.apply( this, arguments );
  1772. library = this.get('library');
  1773. comparator = library.comparator;
  1774. // Overload the library's comparator to push items that are not in
  1775. // the mirrored query to the front of the aggregate collection.
  1776. library.comparator = function( a, b ) {
  1777. var aInQuery = !! this.mirroring.get( a.cid ),
  1778. bInQuery = !! this.mirroring.get( b.cid );
  1779. if ( ! aInQuery && bInQuery ) {
  1780. return -1;
  1781. } else if ( aInQuery && ! bInQuery ) {
  1782. return 1;
  1783. } else {
  1784. return comparator.apply( this, arguments );
  1785. }
  1786. };
  1787. // Add all items in the selection to the library, so any featured
  1788. // images that are not initially loaded still appear.
  1789. library.observe( this.get('selection') );
  1790. },
  1791. /**
  1792. * @since 3.9.0
  1793. */
  1794. activate: function() {
  1795. this.updateSelection();
  1796. Library.prototype.activate.apply( this, arguments );
  1797. },
  1798. /**
  1799. * @since 3.9.0
  1800. */
  1801. updateSelection: function() {
  1802. var selection = this.get('selection'),
  1803. attachment = this.image.attachment;
  1804. selection.reset( attachment ? [ attachment ] : [] );
  1805. }
  1806. });
  1807. module.exports = ReplaceImage;
  1808. /***/ }),
  1809. /* 43 */
  1810. /***/ (function(module, exports) {
  1811. var l10n = wp.media.view.l10n,
  1812. EditImage;
  1813. /**
  1814. * wp.media.controller.EditImage
  1815. *
  1816. * A state for editing (cropping, etc.) an image.
  1817. *
  1818. * @memberOf wp.media.controller
  1819. *
  1820. * @class
  1821. * @augments wp.media.controller.State
  1822. * @augments Backbone.Model
  1823. *
  1824. * @param {object} attributes The attributes hash passed to the state.
  1825. * @param {wp.media.model.Attachment} attributes.model The attachment.
  1826. * @param {string} [attributes.id=edit-image] Unique identifier.
  1827. * @param {string} [attributes.title=Edit Image] Title for the state. Displays in the media menu and the frame's title region.
  1828. * @param {string} [attributes.content=edit-image] Initial mode for the content region.
  1829. * @param {string} [attributes.toolbar=edit-image] Initial mode for the toolbar region.
  1830. * @param {string} [attributes.menu=false] Initial mode for the menu region.
  1831. * @param {string} [attributes.url] Unused. @todo Consider removal.
  1832. */
  1833. EditImage = wp.media.controller.State.extend(/** @lends wp.media.controller.EditImage.prototype */{
  1834. defaults: {
  1835. id: 'edit-image',
  1836. title: l10n.editImage,
  1837. menu: false,
  1838. toolbar: 'edit-image',
  1839. content: 'edit-image',
  1840. url: ''
  1841. },
  1842. /**
  1843. * Activates a frame for editing a featured image.
  1844. *
  1845. * @since 3.9.0
  1846. *
  1847. * @returns {void}
  1848. */
  1849. activate: function() {
  1850. this.frame.on( 'toolbar:render:edit-image', _.bind( this.toolbar, this ) );
  1851. },
  1852. /**
  1853. * Deactivates a frame for editing a featured image.
  1854. *
  1855. * @since 3.9.0
  1856. *
  1857. * @returns {void}
  1858. */
  1859. deactivate: function() {
  1860. this.frame.off( 'toolbar:render:edit-image' );
  1861. },
  1862. /**
  1863. * Adds a toolbar with a back button.
  1864. *
  1865. * When the back button is pressed it checks whether there is a previous state.
  1866. * In case there is a previous state it sets that previous state otherwise it
  1867. * closes the frame.
  1868. *
  1869. * @since 3.9.0
  1870. *
  1871. * @returns {void}
  1872. */
  1873. toolbar: function() {
  1874. var frame = this.frame,
  1875. lastState = frame.lastState(),
  1876. previous = lastState && lastState.id;
  1877. frame.toolbar.set( new wp.media.view.Toolbar({
  1878. controller: frame,
  1879. items: {
  1880. back: {
  1881. style: 'primary',
  1882. text: l10n.back,
  1883. priority: 20,
  1884. click: function() {
  1885. if ( previous ) {
  1886. frame.setState( previous );
  1887. } else {
  1888. frame.close();
  1889. }
  1890. }
  1891. }
  1892. }
  1893. }) );
  1894. }
  1895. });
  1896. module.exports = EditImage;
  1897. /***/ }),
  1898. /* 44 */
  1899. /***/ (function(module, exports) {
  1900. /**
  1901. * wp.media.controller.MediaLibrary
  1902. *
  1903. * @memberOf wp.media.controller
  1904. *
  1905. * @class
  1906. * @augments wp.media.controller.Library
  1907. * @augments wp.media.controller.State
  1908. * @augments Backbone.Model
  1909. */
  1910. var Library = wp.media.controller.Library,
  1911. MediaLibrary;
  1912. MediaLibrary = Library.extend(/** @lends wp.media.controller.MediaLibrary.prototype */{
  1913. defaults: _.defaults({
  1914. // Attachments browser defaults. @see media.view.AttachmentsBrowser
  1915. filterable: 'uploaded',
  1916. displaySettings: false,
  1917. priority: 80,
  1918. syncSelection: false
  1919. }, Library.prototype.defaults ),
  1920. /**
  1921. * @since 3.9.0
  1922. *
  1923. * @param options
  1924. */
  1925. initialize: function( options ) {
  1926. this.media = options.media;
  1927. this.type = options.type;
  1928. this.set( 'library', wp.media.query({ type: this.type }) );
  1929. Library.prototype.initialize.apply( this, arguments );
  1930. },
  1931. /**
  1932. * @since 3.9.0
  1933. */
  1934. activate: function() {
  1935. // @todo this should use this.frame.
  1936. if ( wp.media.frame.lastMime ) {
  1937. this.set( 'library', wp.media.query({ type: wp.media.frame.lastMime }) );
  1938. delete wp.media.frame.lastMime;
  1939. }
  1940. Library.prototype.activate.apply( this, arguments );
  1941. }
  1942. });
  1943. module.exports = MediaLibrary;
  1944. /***/ }),
  1945. /* 45 */
  1946. /***/ (function(module, exports) {
  1947. var l10n = wp.media.view.l10n,
  1948. $ = Backbone.$,
  1949. Embed;
  1950. /**
  1951. * wp.media.controller.Embed
  1952. *
  1953. * A state for embedding media from a URL.
  1954. *
  1955. * @memberOf wp.media.controller
  1956. *
  1957. * @class
  1958. * @augments wp.media.controller.State
  1959. * @augments Backbone.Model
  1960. *
  1961. * @param {object} attributes The attributes hash passed to the state.
  1962. * @param {string} [attributes.id=embed] Unique identifier.
  1963. * @param {string} [attributes.title=Insert From URL] Title for the state. Displays in the media menu and the frame's title region.
  1964. * @param {string} [attributes.content=embed] Initial mode for the content region.
  1965. * @param {string} [attributes.menu=default] Initial mode for the menu region.
  1966. * @param {string} [attributes.toolbar=main-embed] Initial mode for the toolbar region.
  1967. * @param {string} [attributes.menu=false] Initial mode for the menu region.
  1968. * @param {int} [attributes.priority=120] The priority for the state link in the media menu.
  1969. * @param {string} [attributes.type=link] The type of embed. Currently only link is supported.
  1970. * @param {string} [attributes.url] The embed URL.
  1971. * @param {object} [attributes.metadata={}] Properties of the embed, which will override attributes.url if set.
  1972. */
  1973. Embed = wp.media.controller.State.extend(/** @lends wp.media.controller.Embed.prototype */{
  1974. defaults: {
  1975. id: 'embed',
  1976. title: l10n.insertFromUrlTitle,
  1977. content: 'embed',
  1978. menu: 'default',
  1979. toolbar: 'main-embed',
  1980. priority: 120,
  1981. type: 'link',
  1982. url: '',
  1983. metadata: {}
  1984. },
  1985. // The amount of time used when debouncing the scan.
  1986. sensitivity: 400,
  1987. initialize: function(options) {
  1988. this.metadata = options.metadata;
  1989. this.debouncedScan = _.debounce( _.bind( this.scan, this ), this.sensitivity );
  1990. this.props = new Backbone.Model( this.metadata || { url: '' });
  1991. this.props.on( 'change:url', this.debouncedScan, this );
  1992. this.props.on( 'change:url', this.refresh, this );
  1993. this.on( 'scan', this.scanImage, this );
  1994. },
  1995. /**
  1996. * Trigger a scan of the embedded URL's content for metadata required to embed.
  1997. *
  1998. * @fires wp.media.controller.Embed#scan
  1999. */
  2000. scan: function() {
  2001. var scanners,
  2002. embed = this,
  2003. attributes = {
  2004. type: 'link',
  2005. scanners: []
  2006. };
  2007. // Scan is triggered with the list of `attributes` to set on the
  2008. // state, useful for the 'type' attribute and 'scanners' attribute,
  2009. // an array of promise objects for asynchronous scan operations.
  2010. if ( this.props.get('url') ) {
  2011. this.trigger( 'scan', attributes );
  2012. }
  2013. if ( attributes.scanners.length ) {
  2014. scanners = attributes.scanners = $.when.apply( $, attributes.scanners );
  2015. scanners.always( function() {
  2016. if ( embed.get('scanners') === scanners ) {
  2017. embed.set( 'loading', false );
  2018. }
  2019. });
  2020. } else {
  2021. attributes.scanners = null;
  2022. }
  2023. attributes.loading = !! attributes.scanners;
  2024. this.set( attributes );
  2025. },
  2026. /**
  2027. * Try scanning the embed as an image to discover its dimensions.
  2028. *
  2029. * @param {Object} attributes
  2030. */
  2031. scanImage: function( attributes ) {
  2032. var frame = this.frame,
  2033. state = this,
  2034. url = this.props.get('url'),
  2035. image = new Image(),
  2036. deferred = $.Deferred();
  2037. attributes.scanners.push( deferred.promise() );
  2038. // Try to load the image and find its width/height.
  2039. image.onload = function() {
  2040. deferred.resolve();
  2041. if ( state !== frame.state() || url !== state.props.get('url') ) {
  2042. return;
  2043. }
  2044. state.set({
  2045. type: 'image'
  2046. });
  2047. state.props.set({
  2048. width: image.width,
  2049. height: image.height
  2050. });
  2051. };
  2052. image.onerror = deferred.reject;
  2053. image.src = url;
  2054. },
  2055. refresh: function() {
  2056. this.frame.toolbar.get().refresh();
  2057. },
  2058. reset: function() {
  2059. this.props.clear().set({ url: '' });
  2060. if ( this.active ) {
  2061. this.refresh();
  2062. }
  2063. }
  2064. });
  2065. module.exports = Embed;
  2066. /***/ }),
  2067. /* 46 */
  2068. /***/ (function(module, exports) {
  2069. var l10n = wp.media.view.l10n,
  2070. Cropper;
  2071. /**
  2072. * wp.media.controller.Cropper
  2073. *
  2074. * A class for cropping an image when called from the header media customization panel.
  2075. *
  2076. * @memberOf wp.media.controller
  2077. *
  2078. * @class
  2079. * @augments wp.media.controller.State
  2080. * @augments Backbone.Model
  2081. */
  2082. Cropper = wp.media.controller.State.extend(/** @lends wp.media.controller.Cropper.prototype */{
  2083. defaults: {
  2084. id: 'cropper',
  2085. title: l10n.cropImage,
  2086. // Region mode defaults.
  2087. toolbar: 'crop',
  2088. content: 'crop',
  2089. router: false,
  2090. canSkipCrop: false,
  2091. // Default doCrop Ajax arguments to allow the Customizer (for example) to inject state.
  2092. doCropArgs: {}
  2093. },
  2094. /**
  2095. * Shows the crop image window when called from the Add new image button.
  2096. *
  2097. * @since 4.2.0
  2098. *
  2099. * @returns {void}
  2100. */
  2101. activate: function() {
  2102. this.frame.on( 'content:create:crop', this.createCropContent, this );
  2103. this.frame.on( 'close', this.removeCropper, this );
  2104. this.set('selection', new Backbone.Collection(this.frame._selection.single));
  2105. },
  2106. /**
  2107. * Changes the state of the toolbar window to browse mode.
  2108. *
  2109. * @since 4.2.0
  2110. *
  2111. * @returns {void}
  2112. */
  2113. deactivate: function() {
  2114. this.frame.toolbar.mode('browse');
  2115. },
  2116. /**
  2117. * Creates the crop image window.
  2118. *
  2119. * Initialized when clicking on the Select and Crop button.
  2120. *
  2121. * @since 4.2.0
  2122. *
  2123. * @fires crop window
  2124. *
  2125. * @returns {void}
  2126. */
  2127. createCropContent: function() {
  2128. this.cropperView = new wp.media.view.Cropper({
  2129. controller: this,
  2130. attachment: this.get('selection').first()
  2131. });
  2132. this.cropperView.on('image-loaded', this.createCropToolbar, this);
  2133. this.frame.content.set(this.cropperView);
  2134. },
  2135. /**
  2136. * Removes the image selection and closes the cropping window.
  2137. *
  2138. * @since 4.2.0
  2139. *
  2140. * @returns {void}
  2141. */
  2142. removeCropper: function() {
  2143. this.imgSelect.cancelSelection();
  2144. this.imgSelect.setOptions({remove: true});
  2145. this.imgSelect.update();
  2146. this.cropperView.remove();
  2147. },
  2148. /**
  2149. * Checks if cropping can be skipped and creates crop toolbar accordingly.
  2150. *
  2151. * @since 4.2.0
  2152. *
  2153. * @returns {void}
  2154. */
  2155. createCropToolbar: function() {
  2156. var canSkipCrop, toolbarOptions;
  2157. canSkipCrop = this.get('canSkipCrop') || false;
  2158. toolbarOptions = {
  2159. controller: this.frame,
  2160. items: {
  2161. insert: {
  2162. style: 'primary',
  2163. text: l10n.cropImage,
  2164. priority: 80,
  2165. requires: { library: false, selection: false },
  2166. click: function() {
  2167. var controller = this.controller,
  2168. selection;
  2169. selection = controller.state().get('selection').first();
  2170. selection.set({cropDetails: controller.state().imgSelect.getSelection()});
  2171. this.$el.text(l10n.cropping);
  2172. this.$el.attr('disabled', true);
  2173. controller.state().doCrop( selection ).done( function( croppedImage ) {
  2174. controller.trigger('cropped', croppedImage );
  2175. controller.close();
  2176. }).fail( function() {
  2177. controller.trigger('content:error:crop');
  2178. });
  2179. }
  2180. }
  2181. }
  2182. };
  2183. if ( canSkipCrop ) {
  2184. _.extend( toolbarOptions.items, {
  2185. skip: {
  2186. style: 'secondary',
  2187. text: l10n.skipCropping,
  2188. priority: 70,
  2189. requires: { library: false, selection: false },
  2190. click: function() {
  2191. var selection = this.controller.state().get('selection').first();
  2192. this.controller.state().cropperView.remove();
  2193. this.controller.trigger('skippedcrop', selection);
  2194. this.controller.close();
  2195. }
  2196. }
  2197. });
  2198. }
  2199. this.frame.toolbar.set( new wp.media.view.Toolbar(toolbarOptions) );
  2200. },
  2201. /**
  2202. * Creates an object with the image attachment and crop properties.
  2203. *
  2204. * @since 4.2.0
  2205. *
  2206. * @returns {$.promise} A jQuery promise with the custom header crop details.
  2207. */
  2208. doCrop: function( attachment ) {
  2209. return wp.ajax.post( 'custom-header-crop', _.extend(
  2210. {},
  2211. this.defaults.doCropArgs,
  2212. {
  2213. nonce: attachment.get( 'nonces' ).edit,
  2214. id: attachment.get( 'id' ),
  2215. cropDetails: attachment.get( 'cropDetails' )
  2216. }
  2217. ) );
  2218. }
  2219. });
  2220. module.exports = Cropper;
  2221. /***/ }),
  2222. /* 47 */
  2223. /***/ (function(module, exports) {
  2224. var Controller = wp.media.controller,
  2225. CustomizeImageCropper;
  2226. /**
  2227. * A state for cropping an image in the customizer.
  2228. *
  2229. * @since 4.3.0
  2230. *
  2231. * @constructs wp.media.controller.CustomizeImageCropper
  2232. * @memberOf wp.media.controller
  2233. * @augments wp.media.controller.CustomizeImageCropper.Cropper
  2234. * @inheritDoc
  2235. */
  2236. CustomizeImageCropper = Controller.Cropper.extend(/** @lends wp.media.controller.CustomizeImageCropper.prototype */{
  2237. /**
  2238. * Posts the crop details to the admin.
  2239. *
  2240. * Uses crop measurements when flexible in both directions.
  2241. * Constrains flexible side based on image ratio and size of the fixed side.
  2242. *
  2243. * @since 4.3.0
  2244. *
  2245. * @param {Object} attachment The attachment to crop.
  2246. *
  2247. * @returns {$.promise} A jQuery promise that represents the crop image request.
  2248. */
  2249. doCrop: function( attachment ) {
  2250. var cropDetails = attachment.get( 'cropDetails' ),
  2251. control = this.get( 'control' ),
  2252. ratio = cropDetails.width / cropDetails.height;
  2253. // Use crop measurements when flexible in both directions.
  2254. if ( control.params.flex_width && control.params.flex_height ) {
  2255. cropDetails.dst_width = cropDetails.width;
  2256. cropDetails.dst_height = cropDetails.height;
  2257. // Constrain flexible side based on image ratio and size of the fixed side.
  2258. } else {
  2259. cropDetails.dst_width = control.params.flex_width ? control.params.height * ratio : control.params.width;
  2260. cropDetails.dst_height = control.params.flex_height ? control.params.width / ratio : control.params.height;
  2261. }
  2262. return wp.ajax.post( 'crop-image', {
  2263. wp_customize: 'on',
  2264. nonce: attachment.get( 'nonces' ).edit,
  2265. id: attachment.get( 'id' ),
  2266. context: control.id,
  2267. cropDetails: cropDetails
  2268. } );
  2269. }
  2270. });
  2271. module.exports = CustomizeImageCropper;
  2272. /***/ }),
  2273. /* 48 */
  2274. /***/ (function(module, exports) {
  2275. var Controller = wp.media.controller,
  2276. SiteIconCropper;
  2277. /**
  2278. * wp.media.controller.SiteIconCropper
  2279. *
  2280. * A state for cropping a Site Icon.
  2281. *
  2282. * @memberOf wp.media.controller
  2283. *
  2284. * @class
  2285. * @augments wp.media.controller.Cropper
  2286. * @augments wp.media.controller.State
  2287. * @augments Backbone.Model
  2288. */
  2289. SiteIconCropper = Controller.Cropper.extend(/** @lends wp.media.controller.SiteIconCropper.prototype */{
  2290. activate: function() {
  2291. this.frame.on( 'content:create:crop', this.createCropContent, this );
  2292. this.frame.on( 'close', this.removeCropper, this );
  2293. this.set('selection', new Backbone.Collection(this.frame._selection.single));
  2294. },
  2295. createCropContent: function() {
  2296. this.cropperView = new wp.media.view.SiteIconCropper({
  2297. controller: this,
  2298. attachment: this.get('selection').first()
  2299. });
  2300. this.cropperView.on('image-loaded', this.createCropToolbar, this);
  2301. this.frame.content.set(this.cropperView);
  2302. },
  2303. doCrop: function( attachment ) {
  2304. var cropDetails = attachment.get( 'cropDetails' ),
  2305. control = this.get( 'control' );
  2306. cropDetails.dst_width = control.params.width;
  2307. cropDetails.dst_height = control.params.height;
  2308. return wp.ajax.post( 'crop-image', {
  2309. nonce: attachment.get( 'nonces' ).edit,
  2310. id: attachment.get( 'id' ),
  2311. context: 'site-icon',
  2312. cropDetails: cropDetails
  2313. } );
  2314. }
  2315. });
  2316. module.exports = SiteIconCropper;
  2317. /***/ }),
  2318. /* 49 */
  2319. /***/ (function(module, exports) {
  2320. /**
  2321. * wp.media.View
  2322. *
  2323. * The base view class for media.
  2324. *
  2325. * Undelegating events, removing events from the model, and
  2326. * removing events from the controller mirror the code for
  2327. * `Backbone.View.dispose` in Backbone 0.9.8 development.
  2328. *
  2329. * This behavior has since been removed, and should not be used
  2330. * outside of the media manager.
  2331. *
  2332. * @memberOf wp.media
  2333. *
  2334. * @class
  2335. * @augments wp.Backbone.View
  2336. * @augments Backbone.View
  2337. */
  2338. var View = wp.Backbone.View.extend(/** @lends wp.media.View.prototype */{
  2339. constructor: function( options ) {
  2340. if ( options && options.controller ) {
  2341. this.controller = options.controller;
  2342. }
  2343. wp.Backbone.View.apply( this, arguments );
  2344. },
  2345. /**
  2346. * @todo The internal comment mentions this might have been a stop-gap
  2347. * before Backbone 0.9.8 came out. Figure out if Backbone core takes
  2348. * care of this in Backbone.View now.
  2349. *
  2350. * @returns {wp.media.View} Returns itself to allow chaining
  2351. */
  2352. dispose: function() {
  2353. // Undelegating events, removing events from the model, and
  2354. // removing events from the controller mirror the code for
  2355. // `Backbone.View.dispose` in Backbone 0.9.8 development.
  2356. this.undelegateEvents();
  2357. if ( this.model && this.model.off ) {
  2358. this.model.off( null, null, this );
  2359. }
  2360. if ( this.collection && this.collection.off ) {
  2361. this.collection.off( null, null, this );
  2362. }
  2363. // Unbind controller events.
  2364. if ( this.controller && this.controller.off ) {
  2365. this.controller.off( null, null, this );
  2366. }
  2367. return this;
  2368. },
  2369. /**
  2370. * @returns {wp.media.View} Returns itself to allow chaining
  2371. */
  2372. remove: function() {
  2373. this.dispose();
  2374. /**
  2375. * call 'remove' directly on the parent class
  2376. */
  2377. return wp.Backbone.View.prototype.remove.apply( this, arguments );
  2378. }
  2379. });
  2380. module.exports = View;
  2381. /***/ }),
  2382. /* 50 */
  2383. /***/ (function(module, exports) {
  2384. /**
  2385. * wp.media.view.Frame
  2386. *
  2387. * A frame is a composite view consisting of one or more regions and one or more
  2388. * states.
  2389. *
  2390. * @memberOf wp.media.view
  2391. *
  2392. * @see wp.media.controller.State
  2393. * @see wp.media.controller.Region
  2394. *
  2395. * @class
  2396. * @augments wp.media.View
  2397. * @augments wp.Backbone.View
  2398. * @augments Backbone.View
  2399. * @mixes wp.media.controller.StateMachine
  2400. */
  2401. var Frame = wp.media.View.extend(/** @lends wp.media.view.Frame.prototype */{
  2402. initialize: function() {
  2403. _.defaults( this.options, {
  2404. mode: [ 'select' ]
  2405. });
  2406. this._createRegions();
  2407. this._createStates();
  2408. this._createModes();
  2409. },
  2410. _createRegions: function() {
  2411. // Clone the regions array.
  2412. this.regions = this.regions ? this.regions.slice() : [];
  2413. // Initialize regions.
  2414. _.each( this.regions, function( region ) {
  2415. this[ region ] = new wp.media.controller.Region({
  2416. view: this,
  2417. id: region,
  2418. selector: '.media-frame-' + region
  2419. });
  2420. }, this );
  2421. },
  2422. /**
  2423. * Create the frame's states.
  2424. *
  2425. * @see wp.media.controller.State
  2426. * @see wp.media.controller.StateMachine
  2427. *
  2428. * @fires wp.media.controller.State#ready
  2429. */
  2430. _createStates: function() {
  2431. // Create the default `states` collection.
  2432. this.states = new Backbone.Collection( null, {
  2433. model: wp.media.controller.State
  2434. });
  2435. // Ensure states have a reference to the frame.
  2436. this.states.on( 'add', function( model ) {
  2437. model.frame = this;
  2438. model.trigger('ready');
  2439. }, this );
  2440. if ( this.options.states ) {
  2441. this.states.add( this.options.states );
  2442. }
  2443. },
  2444. /**
  2445. * A frame can be in a mode or multiple modes at one time.
  2446. *
  2447. * For example, the manage media frame can be in the `Bulk Select` or `Edit` mode.
  2448. */
  2449. _createModes: function() {
  2450. // Store active "modes" that the frame is in. Unrelated to region modes.
  2451. this.activeModes = new Backbone.Collection();
  2452. this.activeModes.on( 'add remove reset', _.bind( this.triggerModeEvents, this ) );
  2453. _.each( this.options.mode, function( mode ) {
  2454. this.activateMode( mode );
  2455. }, this );
  2456. },
  2457. /**
  2458. * Reset all states on the frame to their defaults.
  2459. *
  2460. * @returns {wp.media.view.Frame} Returns itself to allow chaining
  2461. */
  2462. reset: function() {
  2463. this.states.invoke( 'trigger', 'reset' );
  2464. return this;
  2465. },
  2466. /**
  2467. * Map activeMode collection events to the frame.
  2468. */
  2469. triggerModeEvents: function( model, collection, options ) {
  2470. var collectionEvent,
  2471. modeEventMap = {
  2472. add: 'activate',
  2473. remove: 'deactivate'
  2474. },
  2475. eventToTrigger;
  2476. // Probably a better way to do this.
  2477. _.each( options, function( value, key ) {
  2478. if ( value ) {
  2479. collectionEvent = key;
  2480. }
  2481. } );
  2482. if ( ! _.has( modeEventMap, collectionEvent ) ) {
  2483. return;
  2484. }
  2485. eventToTrigger = model.get('id') + ':' + modeEventMap[collectionEvent];
  2486. this.trigger( eventToTrigger );
  2487. },
  2488. /**
  2489. * Activate a mode on the frame.
  2490. *
  2491. * @param string mode Mode ID.
  2492. * @returns {this} Returns itself to allow chaining.
  2493. */
  2494. activateMode: function( mode ) {
  2495. // Bail if the mode is already active.
  2496. if ( this.isModeActive( mode ) ) {
  2497. return;
  2498. }
  2499. this.activeModes.add( [ { id: mode } ] );
  2500. // Add a CSS class to the frame so elements can be styled for the mode.
  2501. this.$el.addClass( 'mode-' + mode );
  2502. return this;
  2503. },
  2504. /**
  2505. * Deactivate a mode on the frame.
  2506. *
  2507. * @param string mode Mode ID.
  2508. * @returns {this} Returns itself to allow chaining.
  2509. */
  2510. deactivateMode: function( mode ) {
  2511. // Bail if the mode isn't active.
  2512. if ( ! this.isModeActive( mode ) ) {
  2513. return this;
  2514. }
  2515. this.activeModes.remove( this.activeModes.where( { id: mode } ) );
  2516. this.$el.removeClass( 'mode-' + mode );
  2517. /**
  2518. * Frame mode deactivation event.
  2519. *
  2520. * @event wp.media.view.Frame#{mode}:deactivate
  2521. */
  2522. this.trigger( mode + ':deactivate' );
  2523. return this;
  2524. },
  2525. /**
  2526. * Check if a mode is enabled on the frame.
  2527. *
  2528. * @param string mode Mode ID.
  2529. * @return bool
  2530. */
  2531. isModeActive: function( mode ) {
  2532. return Boolean( this.activeModes.where( { id: mode } ).length );
  2533. }
  2534. });
  2535. // Make the `Frame` a `StateMachine`.
  2536. _.extend( Frame.prototype, wp.media.controller.StateMachine.prototype );
  2537. module.exports = Frame;
  2538. /***/ }),
  2539. /* 51 */
  2540. /***/ (function(module, exports) {
  2541. var Frame = wp.media.view.Frame,
  2542. l10n = wp.media.view.l10n,
  2543. $ = jQuery,
  2544. MediaFrame;
  2545. /**
  2546. * wp.media.view.MediaFrame
  2547. *
  2548. * The frame used to create the media modal.
  2549. *
  2550. * @memberOf wp.media.view
  2551. *
  2552. * @class
  2553. * @augments wp.media.view.Frame
  2554. * @augments wp.media.View
  2555. * @augments wp.Backbone.View
  2556. * @augments Backbone.View
  2557. * @mixes wp.media.controller.StateMachine
  2558. */
  2559. MediaFrame = Frame.extend(/** @lends wp.media.view.MediaFrame.prototype */{
  2560. className: 'media-frame',
  2561. template: wp.template('media-frame'),
  2562. regions: ['menu','title','content','toolbar','router'],
  2563. events: {
  2564. 'click .media-frame-menu-toggle': 'toggleMenu'
  2565. },
  2566. /**
  2567. * @constructs
  2568. */
  2569. initialize: function() {
  2570. Frame.prototype.initialize.apply( this, arguments );
  2571. _.defaults( this.options, {
  2572. title: l10n.mediaFrameDefaultTitle,
  2573. modal: true,
  2574. uploader: true
  2575. });
  2576. // Ensure core UI is enabled.
  2577. this.$el.addClass('wp-core-ui');
  2578. // Initialize modal container view.
  2579. if ( this.options.modal ) {
  2580. this.modal = new wp.media.view.Modal({
  2581. controller: this,
  2582. title: this.options.title
  2583. });
  2584. this.modal.content( this );
  2585. }
  2586. // Force the uploader off if the upload limit has been exceeded or
  2587. // if the browser isn't supported.
  2588. if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
  2589. this.options.uploader = false;
  2590. }
  2591. // Initialize window-wide uploader.
  2592. if ( this.options.uploader ) {
  2593. this.uploader = new wp.media.view.UploaderWindow({
  2594. controller: this,
  2595. uploader: {
  2596. dropzone: this.modal ? this.modal.$el : this.$el,
  2597. container: this.$el
  2598. }
  2599. });
  2600. this.views.set( '.media-frame-uploader', this.uploader );
  2601. }
  2602. this.on( 'attach', _.bind( this.views.ready, this.views ), this );
  2603. // Bind default title creation.
  2604. this.on( 'title:create:default', this.createTitle, this );
  2605. this.title.mode('default');
  2606. // Bind default menu.
  2607. this.on( 'menu:create:default', this.createMenu, this );
  2608. // Set the menu ARIA tab panel attributes when the modal opens.
  2609. this.on( 'open', this.setMenuTabPanelAriaAttributes, this );
  2610. // Set the router ARIA tab panel attributes when the modal opens.
  2611. this.on( 'open', this.setRouterTabPanelAriaAttributes, this );
  2612. // Update the menu ARIA tab panel attributes when the content updates.
  2613. this.on( 'content:render', this.setMenuTabPanelAriaAttributes, this );
  2614. // Update the router ARIA tab panel attributes when the content updates.
  2615. this.on( 'content:render', this.setRouterTabPanelAriaAttributes, this );
  2616. },
  2617. /**
  2618. * Sets the attributes to be used on the menu ARIA tab panel.
  2619. *
  2620. * @since 5.3.0
  2621. *
  2622. * @returns {void}
  2623. */
  2624. setMenuTabPanelAriaAttributes: function() {
  2625. var stateId = this.state().get( 'id' ),
  2626. tabPanelEl = this.$el.find( '.media-frame-tab-panel' ),
  2627. ariaLabelledby;
  2628. tabPanelEl.removeAttr( 'role aria-labelledby tabindex' );
  2629. if ( this.state().get( 'menu' ) && this.menuView && this.menuView.isVisible ) {
  2630. ariaLabelledby = 'menu-item-' + stateId;
  2631. // Set the tab panel attributes only if the tabs are visible.
  2632. tabPanelEl
  2633. .attr( {
  2634. role: 'tabpanel',
  2635. 'aria-labelledby': ariaLabelledby,
  2636. tabIndex: '0'
  2637. } );
  2638. }
  2639. },
  2640. /**
  2641. * Sets the attributes to be used on the router ARIA tab panel.
  2642. *
  2643. * @since 5.3.0
  2644. *
  2645. * @returns {void}
  2646. */
  2647. setRouterTabPanelAriaAttributes: function() {
  2648. var tabPanelEl = this.$el.find( '.media-frame-content' ),
  2649. ariaLabelledby;
  2650. tabPanelEl.removeAttr( 'role aria-labelledby tabindex' );
  2651. // Set the tab panel attributes only if the tabs are visible.
  2652. if ( this.state().get( 'router' ) && this.routerView && this.routerView.isVisible && this.content._mode ) {
  2653. ariaLabelledby = 'menu-item-' + this.content._mode;
  2654. tabPanelEl
  2655. .attr( {
  2656. role: 'tabpanel',
  2657. 'aria-labelledby': ariaLabelledby,
  2658. tabIndex: '0'
  2659. } );
  2660. }
  2661. },
  2662. /**
  2663. * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
  2664. */
  2665. render: function() {
  2666. // Activate the default state if no active state exists.
  2667. if ( ! this.state() && this.options.state ) {
  2668. this.setState( this.options.state );
  2669. }
  2670. /**
  2671. * call 'render' directly on the parent class
  2672. */
  2673. return Frame.prototype.render.apply( this, arguments );
  2674. },
  2675. /**
  2676. * @param {Object} title
  2677. * @this wp.media.controller.Region
  2678. */
  2679. createTitle: function( title ) {
  2680. title.view = new wp.media.View({
  2681. controller: this,
  2682. tagName: 'h1'
  2683. });
  2684. },
  2685. /**
  2686. * @param {Object} menu
  2687. * @this wp.media.controller.Region
  2688. */
  2689. createMenu: function( menu ) {
  2690. menu.view = new wp.media.view.Menu({
  2691. controller: this,
  2692. attributes: {
  2693. role: 'tablist',
  2694. 'aria-orientation': 'vertical'
  2695. }
  2696. });
  2697. this.menuView = menu.view;
  2698. },
  2699. toggleMenu: function( event ) {
  2700. var menu = this.$el.find( '.media-menu' );
  2701. menu.toggleClass( 'visible' );
  2702. $( event.target ).attr( 'aria-expanded', menu.hasClass( 'visible' ) );
  2703. },
  2704. /**
  2705. * @param {Object} toolbar
  2706. * @this wp.media.controller.Region
  2707. */
  2708. createToolbar: function( toolbar ) {
  2709. toolbar.view = new wp.media.view.Toolbar({
  2710. controller: this
  2711. });
  2712. },
  2713. /**
  2714. * @param {Object} router
  2715. * @this wp.media.controller.Region
  2716. */
  2717. createRouter: function( router ) {
  2718. router.view = new wp.media.view.Router({
  2719. controller: this,
  2720. attributes: {
  2721. role: 'tablist',
  2722. 'aria-orientation': 'horizontal'
  2723. }
  2724. });
  2725. this.routerView = router.view;
  2726. },
  2727. /**
  2728. * @param {Object} options
  2729. */
  2730. createIframeStates: function( options ) {
  2731. var settings = wp.media.view.settings,
  2732. tabs = settings.tabs,
  2733. tabUrl = settings.tabUrl,
  2734. $postId;
  2735. if ( ! tabs || ! tabUrl ) {
  2736. return;
  2737. }
  2738. // Add the post ID to the tab URL if it exists.
  2739. $postId = $('#post_ID');
  2740. if ( $postId.length ) {
  2741. tabUrl += '&post_id=' + $postId.val();
  2742. }
  2743. // Generate the tab states.
  2744. _.each( tabs, function( title, id ) {
  2745. this.state( 'iframe:' + id ).set( _.defaults({
  2746. tab: id,
  2747. src: tabUrl + '&tab=' + id,
  2748. title: title,
  2749. content: 'iframe',
  2750. menu: 'default'
  2751. }, options ) );
  2752. }, this );
  2753. this.on( 'content:create:iframe', this.iframeContent, this );
  2754. this.on( 'content:deactivate:iframe', this.iframeContentCleanup, this );
  2755. this.on( 'menu:render:default', this.iframeMenu, this );
  2756. this.on( 'open', this.hijackThickbox, this );
  2757. this.on( 'close', this.restoreThickbox, this );
  2758. },
  2759. /**
  2760. * @param {Object} content
  2761. * @this wp.media.controller.Region
  2762. */
  2763. iframeContent: function( content ) {
  2764. this.$el.addClass('hide-toolbar');
  2765. content.view = new wp.media.view.Iframe({
  2766. controller: this
  2767. });
  2768. },
  2769. iframeContentCleanup: function() {
  2770. this.$el.removeClass('hide-toolbar');
  2771. },
  2772. iframeMenu: function( view ) {
  2773. var views = {};
  2774. if ( ! view ) {
  2775. return;
  2776. }
  2777. _.each( wp.media.view.settings.tabs, function( title, id ) {
  2778. views[ 'iframe:' + id ] = {
  2779. text: this.state( 'iframe:' + id ).get('title'),
  2780. priority: 200
  2781. };
  2782. }, this );
  2783. view.set( views );
  2784. },
  2785. hijackThickbox: function() {
  2786. var frame = this;
  2787. if ( ! window.tb_remove || this._tb_remove ) {
  2788. return;
  2789. }
  2790. this._tb_remove = window.tb_remove;
  2791. window.tb_remove = function() {
  2792. frame.close();
  2793. frame.reset();
  2794. frame.setState( frame.options.state );
  2795. frame._tb_remove.call( window );
  2796. };
  2797. },
  2798. restoreThickbox: function() {
  2799. if ( ! this._tb_remove ) {
  2800. return;
  2801. }
  2802. window.tb_remove = this._tb_remove;
  2803. delete this._tb_remove;
  2804. }
  2805. });
  2806. // Map some of the modal's methods to the frame.
  2807. _.each(['open','close','attach','detach','escape'], function( method ) {
  2808. /**
  2809. * @function open
  2810. * @memberOf wp.media.view.MediaFrame
  2811. * @instance
  2812. *
  2813. * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
  2814. */
  2815. /**
  2816. * @function close
  2817. * @memberOf wp.media.view.MediaFrame
  2818. * @instance
  2819. *
  2820. * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
  2821. */
  2822. /**
  2823. * @function attach
  2824. * @memberOf wp.media.view.MediaFrame
  2825. * @instance
  2826. *
  2827. * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
  2828. */
  2829. /**
  2830. * @function detach
  2831. * @memberOf wp.media.view.MediaFrame
  2832. * @instance
  2833. *
  2834. * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
  2835. */
  2836. /**
  2837. * @function escape
  2838. * @memberOf wp.media.view.MediaFrame
  2839. * @instance
  2840. *
  2841. * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
  2842. */
  2843. MediaFrame.prototype[ method ] = function() {
  2844. if ( this.modal ) {
  2845. this.modal[ method ].apply( this.modal, arguments );
  2846. }
  2847. return this;
  2848. };
  2849. });
  2850. module.exports = MediaFrame;
  2851. /***/ }),
  2852. /* 52 */
  2853. /***/ (function(module, exports) {
  2854. var MediaFrame = wp.media.view.MediaFrame,
  2855. l10n = wp.media.view.l10n,
  2856. Select;
  2857. /**
  2858. * wp.media.view.MediaFrame.Select
  2859. *
  2860. * A frame for selecting an item or items from the media library.
  2861. *
  2862. * @memberOf wp.media.view.MediaFrame
  2863. *
  2864. * @class
  2865. * @augments wp.media.view.MediaFrame
  2866. * @augments wp.media.view.Frame
  2867. * @augments wp.media.View
  2868. * @augments wp.Backbone.View
  2869. * @augments Backbone.View
  2870. * @mixes wp.media.controller.StateMachine
  2871. */
  2872. Select = MediaFrame.extend(/** @lends wp.media.view.MediaFrame.Select.prototype */{
  2873. initialize: function() {
  2874. // Call 'initialize' directly on the parent class.
  2875. MediaFrame.prototype.initialize.apply( this, arguments );
  2876. _.defaults( this.options, {
  2877. selection: [],
  2878. library: {},
  2879. multiple: false,
  2880. state: 'library'
  2881. });
  2882. this.createSelection();
  2883. this.createStates();
  2884. this.bindHandlers();
  2885. },
  2886. /**
  2887. * Attach a selection collection to the frame.
  2888. *
  2889. * A selection is a collection of attachments used for a specific purpose
  2890. * by a media frame. e.g. Selecting an attachment (or many) to insert into
  2891. * post content.
  2892. *
  2893. * @see media.model.Selection
  2894. */
  2895. createSelection: function() {
  2896. var selection = this.options.selection;
  2897. if ( ! (selection instanceof wp.media.model.Selection) ) {
  2898. this.options.selection = new wp.media.model.Selection( selection, {
  2899. multiple: this.options.multiple
  2900. });
  2901. }
  2902. this._selection = {
  2903. attachments: new wp.media.model.Attachments(),
  2904. difference: []
  2905. };
  2906. },
  2907. editImageContent: function() {
  2908. var image = this.state().get('image'),
  2909. view = new wp.media.view.EditImage( { model: image, controller: this } ).render();
  2910. this.content.set( view );
  2911. // after creating the wrapper view, load the actual editor via an ajax call
  2912. view.loadEditor();
  2913. },
  2914. /**
  2915. * Create the default states on the frame.
  2916. */
  2917. createStates: function() {
  2918. var options = this.options;
  2919. if ( this.options.states ) {
  2920. return;
  2921. }
  2922. // Add the default states.
  2923. this.states.add([
  2924. // Main states.
  2925. new wp.media.controller.Library({
  2926. library: wp.media.query( options.library ),
  2927. multiple: options.multiple,
  2928. title: options.title,
  2929. priority: 20
  2930. }),
  2931. new wp.media.controller.EditImage( { model: options.editImage } )
  2932. ]);
  2933. },
  2934. /**
  2935. * Bind region mode event callbacks.
  2936. *
  2937. * @see media.controller.Region.render
  2938. */
  2939. bindHandlers: function() {
  2940. this.on( 'router:create:browse', this.createRouter, this );
  2941. this.on( 'router:render:browse', this.browseRouter, this );
  2942. this.on( 'content:create:browse', this.browseContent, this );
  2943. this.on( 'content:render:upload', this.uploadContent, this );
  2944. this.on( 'toolbar:create:select', this.createSelectToolbar, this );
  2945. this.on( 'content:render:edit-image', this.editImageContent, this );
  2946. },
  2947. /**
  2948. * Render callback for the router region in the `browse` mode.
  2949. *
  2950. * @param {wp.media.view.Router} routerView
  2951. */
  2952. browseRouter: function( routerView ) {
  2953. routerView.set({
  2954. upload: {
  2955. text: l10n.uploadFilesTitle,
  2956. priority: 20
  2957. },
  2958. browse: {
  2959. text: l10n.mediaLibraryTitle,
  2960. priority: 40
  2961. }
  2962. });
  2963. },
  2964. /**
  2965. * Render callback for the content region in the `browse` mode.
  2966. *
  2967. * @param {wp.media.controller.Region} contentRegion
  2968. */
  2969. browseContent: function( contentRegion ) {
  2970. var state = this.state();
  2971. this.$el.removeClass('hide-toolbar');
  2972. // Browse our library of attachments.
  2973. contentRegion.view = new wp.media.view.AttachmentsBrowser({
  2974. controller: this,
  2975. collection: state.get('library'),
  2976. selection: state.get('selection'),
  2977. model: state,
  2978. sortable: state.get('sortable'),
  2979. search: state.get('searchable'),
  2980. filters: state.get('filterable'),
  2981. date: state.get('date'),
  2982. display: state.has('display') ? state.get('display') : state.get('displaySettings'),
  2983. dragInfo: state.get('dragInfo'),
  2984. idealColumnWidth: state.get('idealColumnWidth'),
  2985. suggestedWidth: state.get('suggestedWidth'),
  2986. suggestedHeight: state.get('suggestedHeight'),
  2987. AttachmentView: state.get('AttachmentView')
  2988. });
  2989. },
  2990. /**
  2991. * Render callback for the content region in the `upload` mode.
  2992. */
  2993. uploadContent: function() {
  2994. this.$el.removeClass( 'hide-toolbar' );
  2995. this.content.set( new wp.media.view.UploaderInline({
  2996. controller: this
  2997. }) );
  2998. },
  2999. /**
  3000. * Toolbars
  3001. *
  3002. * @param {Object} toolbar
  3003. * @param {Object} [options={}]
  3004. * @this wp.media.controller.Region
  3005. */
  3006. createSelectToolbar: function( toolbar, options ) {
  3007. options = options || this.options.button || {};
  3008. options.controller = this;
  3009. toolbar.view = new wp.media.view.Toolbar.Select( options );
  3010. }
  3011. });
  3012. module.exports = Select;
  3013. /***/ }),
  3014. /* 53 */
  3015. /***/ (function(module, exports) {
  3016. var Select = wp.media.view.MediaFrame.Select,
  3017. Library = wp.media.controller.Library,
  3018. l10n = wp.media.view.l10n,
  3019. Post;
  3020. /**
  3021. * wp.media.view.MediaFrame.Post
  3022. *
  3023. * The frame for manipulating media on the Edit Post page.
  3024. *
  3025. * @memberOf wp.media.view.MediaFrame
  3026. *
  3027. * @class
  3028. * @augments wp.media.view.MediaFrame.Select
  3029. * @augments wp.media.view.MediaFrame
  3030. * @augments wp.media.view.Frame
  3031. * @augments wp.media.View
  3032. * @augments wp.Backbone.View
  3033. * @augments Backbone.View
  3034. * @mixes wp.media.controller.StateMachine
  3035. */
  3036. Post = Select.extend(/** @lends wp.media.view.MediaFrame.Post.prototype */{
  3037. initialize: function() {
  3038. this.counts = {
  3039. audio: {
  3040. count: wp.media.view.settings.attachmentCounts.audio,
  3041. state: 'playlist'
  3042. },
  3043. video: {
  3044. count: wp.media.view.settings.attachmentCounts.video,
  3045. state: 'video-playlist'
  3046. }
  3047. };
  3048. _.defaults( this.options, {
  3049. multiple: true,
  3050. editing: false,
  3051. state: 'insert',
  3052. metadata: {}
  3053. });
  3054. // Call 'initialize' directly on the parent class.
  3055. Select.prototype.initialize.apply( this, arguments );
  3056. this.createIframeStates();
  3057. },
  3058. /**
  3059. * Create the default states.
  3060. */
  3061. createStates: function() {
  3062. var options = this.options;
  3063. this.states.add([
  3064. // Main states.
  3065. new Library({
  3066. id: 'insert',
  3067. title: l10n.insertMediaTitle,
  3068. priority: 20,
  3069. toolbar: 'main-insert',
  3070. filterable: 'all',
  3071. library: wp.media.query( options.library ),
  3072. multiple: options.multiple ? 'reset' : false,
  3073. editable: true,
  3074. // If the user isn't allowed to edit fields,
  3075. // can they still edit it locally?
  3076. allowLocalEdits: true,
  3077. // Show the attachment display settings.
  3078. displaySettings: true,
  3079. // Update user settings when users adjust the
  3080. // attachment display settings.
  3081. displayUserSettings: true
  3082. }),
  3083. new Library({
  3084. id: 'gallery',
  3085. title: l10n.createGalleryTitle,
  3086. priority: 40,
  3087. toolbar: 'main-gallery',
  3088. filterable: 'uploaded',
  3089. multiple: 'add',
  3090. editable: false,
  3091. library: wp.media.query( _.defaults({
  3092. type: 'image'
  3093. }, options.library ) )
  3094. }),
  3095. // Embed states.
  3096. new wp.media.controller.Embed( { metadata: options.metadata } ),
  3097. new wp.media.controller.EditImage( { model: options.editImage } ),
  3098. // Gallery states.
  3099. new wp.media.controller.GalleryEdit({
  3100. library: options.selection,
  3101. editing: options.editing,
  3102. menu: 'gallery'
  3103. }),
  3104. new wp.media.controller.GalleryAdd(),
  3105. new Library({
  3106. id: 'playlist',
  3107. title: l10n.createPlaylistTitle,
  3108. priority: 60,
  3109. toolbar: 'main-playlist',
  3110. filterable: 'uploaded',
  3111. multiple: 'add',
  3112. editable: false,
  3113. library: wp.media.query( _.defaults({
  3114. type: 'audio'
  3115. }, options.library ) )
  3116. }),
  3117. // Playlist states.
  3118. new wp.media.controller.CollectionEdit({
  3119. type: 'audio',
  3120. collectionType: 'playlist',
  3121. title: l10n.editPlaylistTitle,
  3122. SettingsView: wp.media.view.Settings.Playlist,
  3123. library: options.selection,
  3124. editing: options.editing,
  3125. menu: 'playlist',
  3126. dragInfoText: l10n.playlistDragInfo,
  3127. dragInfo: false
  3128. }),
  3129. new wp.media.controller.CollectionAdd({
  3130. type: 'audio',
  3131. collectionType: 'playlist',
  3132. title: l10n.addToPlaylistTitle
  3133. }),
  3134. new Library({
  3135. id: 'video-playlist',
  3136. title: l10n.createVideoPlaylistTitle,
  3137. priority: 60,
  3138. toolbar: 'main-video-playlist',
  3139. filterable: 'uploaded',
  3140. multiple: 'add',
  3141. editable: false,
  3142. library: wp.media.query( _.defaults({
  3143. type: 'video'
  3144. }, options.library ) )
  3145. }),
  3146. new wp.media.controller.CollectionEdit({
  3147. type: 'video',
  3148. collectionType: 'playlist',
  3149. title: l10n.editVideoPlaylistTitle,
  3150. SettingsView: wp.media.view.Settings.Playlist,
  3151. library: options.selection,
  3152. editing: options.editing,
  3153. menu: 'video-playlist',
  3154. dragInfoText: l10n.videoPlaylistDragInfo,
  3155. dragInfo: false
  3156. }),
  3157. new wp.media.controller.CollectionAdd({
  3158. type: 'video',
  3159. collectionType: 'playlist',
  3160. title: l10n.addToVideoPlaylistTitle
  3161. })
  3162. ]);
  3163. if ( wp.media.view.settings.post.featuredImageId ) {
  3164. this.states.add( new wp.media.controller.FeaturedImage() );
  3165. }
  3166. },
  3167. bindHandlers: function() {
  3168. var handlers, checkCounts;
  3169. Select.prototype.bindHandlers.apply( this, arguments );
  3170. this.on( 'activate', this.activate, this );
  3171. // Only bother checking media type counts if one of the counts is zero
  3172. checkCounts = _.find( this.counts, function( type ) {
  3173. return type.count === 0;
  3174. } );
  3175. if ( typeof checkCounts !== 'undefined' ) {
  3176. this.listenTo( wp.media.model.Attachments.all, 'change:type', this.mediaTypeCounts );
  3177. }
  3178. this.on( 'menu:create:gallery', this.createMenu, this );
  3179. this.on( 'menu:create:playlist', this.createMenu, this );
  3180. this.on( 'menu:create:video-playlist', this.createMenu, this );
  3181. this.on( 'toolbar:create:main-insert', this.createToolbar, this );
  3182. this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
  3183. this.on( 'toolbar:create:main-playlist', this.createToolbar, this );
  3184. this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this );
  3185. this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this );
  3186. this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this );
  3187. handlers = {
  3188. menu: {
  3189. 'default': 'mainMenu',
  3190. 'gallery': 'galleryMenu',
  3191. 'playlist': 'playlistMenu',
  3192. 'video-playlist': 'videoPlaylistMenu'
  3193. },
  3194. content: {
  3195. 'embed': 'embedContent',
  3196. 'edit-image': 'editImageContent',
  3197. 'edit-selection': 'editSelectionContent'
  3198. },
  3199. toolbar: {
  3200. 'main-insert': 'mainInsertToolbar',
  3201. 'main-gallery': 'mainGalleryToolbar',
  3202. 'gallery-edit': 'galleryEditToolbar',
  3203. 'gallery-add': 'galleryAddToolbar',
  3204. 'main-playlist': 'mainPlaylistToolbar',
  3205. 'playlist-edit': 'playlistEditToolbar',
  3206. 'playlist-add': 'playlistAddToolbar',
  3207. 'main-video-playlist': 'mainVideoPlaylistToolbar',
  3208. 'video-playlist-edit': 'videoPlaylistEditToolbar',
  3209. 'video-playlist-add': 'videoPlaylistAddToolbar'
  3210. }
  3211. };
  3212. _.each( handlers, function( regionHandlers, region ) {
  3213. _.each( regionHandlers, function( callback, handler ) {
  3214. this.on( region + ':render:' + handler, this[ callback ], this );
  3215. }, this );
  3216. }, this );
  3217. },
  3218. activate: function() {
  3219. // Hide menu items for states tied to particular media types if there are no items
  3220. _.each( this.counts, function( type ) {
  3221. if ( type.count < 1 ) {
  3222. this.menuItemVisibility( type.state, 'hide' );
  3223. }
  3224. }, this );
  3225. },
  3226. mediaTypeCounts: function( model, attr ) {
  3227. if ( typeof this.counts[ attr ] !== 'undefined' && this.counts[ attr ].count < 1 ) {
  3228. this.counts[ attr ].count++;
  3229. this.menuItemVisibility( this.counts[ attr ].state, 'show' );
  3230. }
  3231. },
  3232. // Menus
  3233. /**
  3234. * @param {wp.Backbone.View} view
  3235. */
  3236. mainMenu: function( view ) {
  3237. view.set({
  3238. 'library-separator': new wp.media.View({
  3239. className: 'separator',
  3240. priority: 100,
  3241. attributes: {
  3242. role: 'presentation'
  3243. }
  3244. })
  3245. });
  3246. },
  3247. menuItemVisibility: function( state, visibility ) {
  3248. var menu = this.menu.get();
  3249. if ( visibility === 'hide' ) {
  3250. menu.hide( state );
  3251. } else if ( visibility === 'show' ) {
  3252. menu.show( state );
  3253. }
  3254. },
  3255. /**
  3256. * @param {wp.Backbone.View} view
  3257. */
  3258. galleryMenu: function( view ) {
  3259. var lastState = this.lastState(),
  3260. previous = lastState && lastState.id,
  3261. frame = this;
  3262. view.set({
  3263. cancel: {
  3264. text: l10n.cancelGalleryTitle,
  3265. priority: 20,
  3266. click: function() {
  3267. if ( previous ) {
  3268. frame.setState( previous );
  3269. } else {
  3270. frame.close();
  3271. }
  3272. // Move focus to the modal after canceling a Gallery.
  3273. this.controller.modal.focusManager.focus();
  3274. }
  3275. },
  3276. separateCancel: new wp.media.View({
  3277. className: 'separator',
  3278. priority: 40
  3279. })
  3280. });
  3281. },
  3282. playlistMenu: function( view ) {
  3283. var lastState = this.lastState(),
  3284. previous = lastState && lastState.id,
  3285. frame = this;
  3286. view.set({
  3287. cancel: {
  3288. text: l10n.cancelPlaylistTitle,
  3289. priority: 20,
  3290. click: function() {
  3291. if ( previous ) {
  3292. frame.setState( previous );
  3293. } else {
  3294. frame.close();
  3295. }
  3296. // Move focus to the modal after canceling an Audio Playlist.
  3297. this.controller.modal.focusManager.focus();
  3298. }
  3299. },
  3300. separateCancel: new wp.media.View({
  3301. className: 'separator',
  3302. priority: 40
  3303. })
  3304. });
  3305. },
  3306. videoPlaylistMenu: function( view ) {
  3307. var lastState = this.lastState(),
  3308. previous = lastState && lastState.id,
  3309. frame = this;
  3310. view.set({
  3311. cancel: {
  3312. text: l10n.cancelVideoPlaylistTitle,
  3313. priority: 20,
  3314. click: function() {
  3315. if ( previous ) {
  3316. frame.setState( previous );
  3317. } else {
  3318. frame.close();
  3319. }
  3320. // Move focus to the modal after canceling a Video Playlist.
  3321. this.controller.modal.focusManager.focus();
  3322. }
  3323. },
  3324. separateCancel: new wp.media.View({
  3325. className: 'separator',
  3326. priority: 40
  3327. })
  3328. });
  3329. },
  3330. // Content
  3331. embedContent: function() {
  3332. var view = new wp.media.view.Embed({
  3333. controller: this,
  3334. model: this.state()
  3335. }).render();
  3336. this.content.set( view );
  3337. },
  3338. editSelectionContent: function() {
  3339. var state = this.state(),
  3340. selection = state.get('selection'),
  3341. view;
  3342. view = new wp.media.view.AttachmentsBrowser({
  3343. controller: this,
  3344. collection: selection,
  3345. selection: selection,
  3346. model: state,
  3347. sortable: true,
  3348. search: false,
  3349. date: false,
  3350. dragInfo: true,
  3351. AttachmentView: wp.media.view.Attachments.EditSelection
  3352. }).render();
  3353. view.toolbar.set( 'backToLibrary', {
  3354. text: l10n.returnToLibrary,
  3355. priority: -100,
  3356. click: function() {
  3357. this.controller.content.mode('browse');
  3358. // Move focus to the modal when jumping back from Edit Selection to Add Media view.
  3359. this.controller.modal.focusManager.focus();
  3360. }
  3361. });
  3362. // Browse our library of attachments.
  3363. this.content.set( view );
  3364. // Trigger the controller to set focus
  3365. this.trigger( 'edit:selection', this );
  3366. },
  3367. editImageContent: function() {
  3368. var image = this.state().get('image'),
  3369. view = new wp.media.view.EditImage( { model: image, controller: this } ).render();
  3370. this.content.set( view );
  3371. // after creating the wrapper view, load the actual editor via an ajax call
  3372. view.loadEditor();
  3373. },
  3374. // Toolbars
  3375. /**
  3376. * @param {wp.Backbone.View} view
  3377. */
  3378. selectionStatusToolbar: function( view ) {
  3379. var editable = this.state().get('editable');
  3380. view.set( 'selection', new wp.media.view.Selection({
  3381. controller: this,
  3382. collection: this.state().get('selection'),
  3383. priority: -40,
  3384. // If the selection is editable, pass the callback to
  3385. // switch the content mode.
  3386. editable: editable && function() {
  3387. this.controller.content.mode('edit-selection');
  3388. }
  3389. }).render() );
  3390. },
  3391. /**
  3392. * @param {wp.Backbone.View} view
  3393. */
  3394. mainInsertToolbar: function( view ) {
  3395. var controller = this;
  3396. this.selectionStatusToolbar( view );
  3397. view.set( 'insert', {
  3398. style: 'primary',
  3399. priority: 80,
  3400. text: l10n.insertIntoPost,
  3401. requires: { selection: true },
  3402. /**
  3403. * @ignore
  3404. *
  3405. * @fires wp.media.controller.State#insert
  3406. */
  3407. click: function() {
  3408. var state = controller.state(),
  3409. selection = state.get('selection');
  3410. controller.close();
  3411. state.trigger( 'insert', selection ).reset();
  3412. }
  3413. });
  3414. },
  3415. /**
  3416. * @param {wp.Backbone.View} view
  3417. */
  3418. mainGalleryToolbar: function( view ) {
  3419. var controller = this;
  3420. this.selectionStatusToolbar( view );
  3421. view.set( 'gallery', {
  3422. style: 'primary',
  3423. text: l10n.createNewGallery,
  3424. priority: 60,
  3425. requires: { selection: true },
  3426. click: function() {
  3427. var selection = controller.state().get('selection'),
  3428. edit = controller.state('gallery-edit'),
  3429. models = selection.where({ type: 'image' });
  3430. edit.set( 'library', new wp.media.model.Selection( models, {
  3431. props: selection.props.toJSON(),
  3432. multiple: true
  3433. }) );
  3434. // Jump to Edit Gallery view.
  3435. this.controller.setState( 'gallery-edit' );
  3436. // Move focus to the modal after jumping to Edit Gallery view.
  3437. this.controller.modal.focusManager.focus();
  3438. }
  3439. });
  3440. },
  3441. mainPlaylistToolbar: function( view ) {
  3442. var controller = this;
  3443. this.selectionStatusToolbar( view );
  3444. view.set( 'playlist', {
  3445. style: 'primary',
  3446. text: l10n.createNewPlaylist,
  3447. priority: 100,
  3448. requires: { selection: true },
  3449. click: function() {
  3450. var selection = controller.state().get('selection'),
  3451. edit = controller.state('playlist-edit'),
  3452. models = selection.where({ type: 'audio' });
  3453. edit.set( 'library', new wp.media.model.Selection( models, {
  3454. props: selection.props.toJSON(),
  3455. multiple: true
  3456. }) );
  3457. // Jump to Edit Audio Playlist view.
  3458. this.controller.setState( 'playlist-edit' );
  3459. // Move focus to the modal after jumping to Edit Audio Playlist view.
  3460. this.controller.modal.focusManager.focus();
  3461. }
  3462. });
  3463. },
  3464. mainVideoPlaylistToolbar: function( view ) {
  3465. var controller = this;
  3466. this.selectionStatusToolbar( view );
  3467. view.set( 'video-playlist', {
  3468. style: 'primary',
  3469. text: l10n.createNewVideoPlaylist,
  3470. priority: 100,
  3471. requires: { selection: true },
  3472. click: function() {
  3473. var selection = controller.state().get('selection'),
  3474. edit = controller.state('video-playlist-edit'),
  3475. models = selection.where({ type: 'video' });
  3476. edit.set( 'library', new wp.media.model.Selection( models, {
  3477. props: selection.props.toJSON(),
  3478. multiple: true
  3479. }) );
  3480. // Jump to Edit Video Playlist view.
  3481. this.controller.setState( 'video-playlist-edit' );
  3482. // Move focus to the modal after jumping to Edit Video Playlist view.
  3483. this.controller.modal.focusManager.focus();
  3484. }
  3485. });
  3486. },
  3487. featuredImageToolbar: function( toolbar ) {
  3488. this.createSelectToolbar( toolbar, {
  3489. text: l10n.setFeaturedImage,
  3490. state: this.options.state
  3491. });
  3492. },
  3493. mainEmbedToolbar: function( toolbar ) {
  3494. toolbar.view = new wp.media.view.Toolbar.Embed({
  3495. controller: this
  3496. });
  3497. },
  3498. galleryEditToolbar: function() {
  3499. var editing = this.state().get('editing');
  3500. this.toolbar.set( new wp.media.view.Toolbar({
  3501. controller: this,
  3502. items: {
  3503. insert: {
  3504. style: 'primary',
  3505. text: editing ? l10n.updateGallery : l10n.insertGallery,
  3506. priority: 80,
  3507. requires: { library: true },
  3508. /**
  3509. * @fires wp.media.controller.State#update
  3510. */
  3511. click: function() {
  3512. var controller = this.controller,
  3513. state = controller.state();
  3514. controller.close();
  3515. state.trigger( 'update', state.get('library') );
  3516. // Restore and reset the default state.
  3517. controller.setState( controller.options.state );
  3518. controller.reset();
  3519. }
  3520. }
  3521. }
  3522. }) );
  3523. },
  3524. galleryAddToolbar: function() {
  3525. this.toolbar.set( new wp.media.view.Toolbar({
  3526. controller: this,
  3527. items: {
  3528. insert: {
  3529. style: 'primary',
  3530. text: l10n.addToGallery,
  3531. priority: 80,
  3532. requires: { selection: true },
  3533. /**
  3534. * @fires wp.media.controller.State#reset
  3535. */
  3536. click: function() {
  3537. var controller = this.controller,
  3538. state = controller.state(),
  3539. edit = controller.state('gallery-edit');
  3540. edit.get('library').add( state.get('selection').models );
  3541. state.trigger('reset');
  3542. controller.setState('gallery-edit');
  3543. // Move focus to the modal when jumping back from Add to Gallery to Edit Gallery view.
  3544. this.controller.modal.focusManager.focus();
  3545. }
  3546. }
  3547. }
  3548. }) );
  3549. },
  3550. playlistEditToolbar: function() {
  3551. var editing = this.state().get('editing');
  3552. this.toolbar.set( new wp.media.view.Toolbar({
  3553. controller: this,
  3554. items: {
  3555. insert: {
  3556. style: 'primary',
  3557. text: editing ? l10n.updatePlaylist : l10n.insertPlaylist,
  3558. priority: 80,
  3559. requires: { library: true },
  3560. /**
  3561. * @fires wp.media.controller.State#update
  3562. */
  3563. click: function() {
  3564. var controller = this.controller,
  3565. state = controller.state();
  3566. controller.close();
  3567. state.trigger( 'update', state.get('library') );
  3568. // Restore and reset the default state.
  3569. controller.setState( controller.options.state );
  3570. controller.reset();
  3571. }
  3572. }
  3573. }
  3574. }) );
  3575. },
  3576. playlistAddToolbar: function() {
  3577. this.toolbar.set( new wp.media.view.Toolbar({
  3578. controller: this,
  3579. items: {
  3580. insert: {
  3581. style: 'primary',
  3582. text: l10n.addToPlaylist,
  3583. priority: 80,
  3584. requires: { selection: true },
  3585. /**
  3586. * @fires wp.media.controller.State#reset
  3587. */
  3588. click: function() {
  3589. var controller = this.controller,
  3590. state = controller.state(),
  3591. edit = controller.state('playlist-edit');
  3592. edit.get('library').add( state.get('selection').models );
  3593. state.trigger('reset');
  3594. controller.setState('playlist-edit');
  3595. // Move focus to the modal when jumping back from Add to Audio Playlist to Edit Audio Playlist view.
  3596. this.controller.modal.focusManager.focus();
  3597. }
  3598. }
  3599. }
  3600. }) );
  3601. },
  3602. videoPlaylistEditToolbar: function() {
  3603. var editing = this.state().get('editing');
  3604. this.toolbar.set( new wp.media.view.Toolbar({
  3605. controller: this,
  3606. items: {
  3607. insert: {
  3608. style: 'primary',
  3609. text: editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist,
  3610. priority: 140,
  3611. requires: { library: true },
  3612. click: function() {
  3613. var controller = this.controller,
  3614. state = controller.state(),
  3615. library = state.get('library');
  3616. library.type = 'video';
  3617. controller.close();
  3618. state.trigger( 'update', library );
  3619. // Restore and reset the default state.
  3620. controller.setState( controller.options.state );
  3621. controller.reset();
  3622. }
  3623. }
  3624. }
  3625. }) );
  3626. },
  3627. videoPlaylistAddToolbar: function() {
  3628. this.toolbar.set( new wp.media.view.Toolbar({
  3629. controller: this,
  3630. items: {
  3631. insert: {
  3632. style: 'primary',
  3633. text: l10n.addToVideoPlaylist,
  3634. priority: 140,
  3635. requires: { selection: true },
  3636. click: function() {
  3637. var controller = this.controller,
  3638. state = controller.state(),
  3639. edit = controller.state('video-playlist-edit');
  3640. edit.get('library').add( state.get('selection').models );
  3641. state.trigger('reset');
  3642. controller.setState('video-playlist-edit');
  3643. // Move focus to the modal when jumping back from Add to Video Playlist to Edit Video Playlist view.
  3644. this.controller.modal.focusManager.focus();
  3645. }
  3646. }
  3647. }
  3648. }) );
  3649. }
  3650. });
  3651. module.exports = Post;
  3652. /***/ }),
  3653. /* 54 */
  3654. /***/ (function(module, exports) {
  3655. var Select = wp.media.view.MediaFrame.Select,
  3656. l10n = wp.media.view.l10n,
  3657. ImageDetails;
  3658. /**
  3659. * wp.media.view.MediaFrame.ImageDetails
  3660. *
  3661. * A media frame for manipulating an image that's already been inserted
  3662. * into a post.
  3663. *
  3664. * @memberOf wp.media.view.MediaFrame
  3665. *
  3666. * @class
  3667. * @augments wp.media.view.MediaFrame.Select
  3668. * @augments wp.media.view.MediaFrame
  3669. * @augments wp.media.view.Frame
  3670. * @augments wp.media.View
  3671. * @augments wp.Backbone.View
  3672. * @augments Backbone.View
  3673. * @mixes wp.media.controller.StateMachine
  3674. */
  3675. ImageDetails = Select.extend(/** @lends wp.media.view.MediaFrame.ImageDetails.prototype */{
  3676. defaults: {
  3677. id: 'image',
  3678. url: '',
  3679. menu: 'image-details',
  3680. content: 'image-details',
  3681. toolbar: 'image-details',
  3682. type: 'link',
  3683. title: l10n.imageDetailsTitle,
  3684. priority: 120
  3685. },
  3686. initialize: function( options ) {
  3687. this.image = new wp.media.model.PostImage( options.metadata );
  3688. this.options.selection = new wp.media.model.Selection( this.image.attachment, { multiple: false } );
  3689. Select.prototype.initialize.apply( this, arguments );
  3690. },
  3691. bindHandlers: function() {
  3692. Select.prototype.bindHandlers.apply( this, arguments );
  3693. this.on( 'menu:create:image-details', this.createMenu, this );
  3694. this.on( 'content:create:image-details', this.imageDetailsContent, this );
  3695. this.on( 'content:render:edit-image', this.editImageContent, this );
  3696. this.on( 'toolbar:render:image-details', this.renderImageDetailsToolbar, this );
  3697. // override the select toolbar
  3698. this.on( 'toolbar:render:replace', this.renderReplaceImageToolbar, this );
  3699. },
  3700. createStates: function() {
  3701. this.states.add([
  3702. new wp.media.controller.ImageDetails({
  3703. image: this.image,
  3704. editable: false
  3705. }),
  3706. new wp.media.controller.ReplaceImage({
  3707. id: 'replace-image',
  3708. library: wp.media.query( { type: 'image' } ),
  3709. image: this.image,
  3710. multiple: false,
  3711. title: l10n.imageReplaceTitle,
  3712. toolbar: 'replace',
  3713. priority: 80,
  3714. displaySettings: true
  3715. }),
  3716. new wp.media.controller.EditImage( {
  3717. image: this.image,
  3718. selection: this.options.selection
  3719. } )
  3720. ]);
  3721. },
  3722. imageDetailsContent: function( options ) {
  3723. options.view = new wp.media.view.ImageDetails({
  3724. controller: this,
  3725. model: this.state().image,
  3726. attachment: this.state().image.attachment
  3727. });
  3728. },
  3729. editImageContent: function() {
  3730. var state = this.state(),
  3731. model = state.get('image'),
  3732. view;
  3733. if ( ! model ) {
  3734. return;
  3735. }
  3736. view = new wp.media.view.EditImage( { model: model, controller: this } ).render();
  3737. this.content.set( view );
  3738. // after bringing in the frame, load the actual editor via an ajax call
  3739. view.loadEditor();
  3740. },
  3741. renderImageDetailsToolbar: function() {
  3742. this.toolbar.set( new wp.media.view.Toolbar({
  3743. controller: this,
  3744. items: {
  3745. select: {
  3746. style: 'primary',
  3747. text: l10n.update,
  3748. priority: 80,
  3749. click: function() {
  3750. var controller = this.controller,
  3751. state = controller.state();
  3752. controller.close();
  3753. // not sure if we want to use wp.media.string.image which will create a shortcode or
  3754. // perhaps wp.html.string to at least to build the <img />
  3755. state.trigger( 'update', controller.image.toJSON() );
  3756. // Restore and reset the default state.
  3757. controller.setState( controller.options.state );
  3758. controller.reset();
  3759. }
  3760. }
  3761. }
  3762. }) );
  3763. },
  3764. renderReplaceImageToolbar: function() {
  3765. var frame = this,
  3766. lastState = frame.lastState(),
  3767. previous = lastState && lastState.id;
  3768. this.toolbar.set( new wp.media.view.Toolbar({
  3769. controller: this,
  3770. items: {
  3771. back: {
  3772. text: l10n.back,
  3773. priority: 80,
  3774. click: function() {
  3775. if ( previous ) {
  3776. frame.setState( previous );
  3777. } else {
  3778. frame.close();
  3779. }
  3780. }
  3781. },
  3782. replace: {
  3783. style: 'primary',
  3784. text: l10n.replace,
  3785. priority: 20,
  3786. requires: { selection: true },
  3787. click: function() {
  3788. var controller = this.controller,
  3789. state = controller.state(),
  3790. selection = state.get( 'selection' ),
  3791. attachment = selection.single();
  3792. controller.close();
  3793. controller.image.changeAttachment( attachment, state.display( attachment ) );
  3794. // not sure if we want to use wp.media.string.image which will create a shortcode or
  3795. // perhaps wp.html.string to at least to build the <img />
  3796. state.trigger( 'replace', controller.image.toJSON() );
  3797. // Restore and reset the default state.
  3798. controller.setState( controller.options.state );
  3799. controller.reset();
  3800. }
  3801. }
  3802. }
  3803. }) );
  3804. }
  3805. });
  3806. module.exports = ImageDetails;
  3807. /***/ }),
  3808. /* 55 */
  3809. /***/ (function(module, exports) {
  3810. var $ = jQuery,
  3811. Modal;
  3812. /**
  3813. * wp.media.view.Modal
  3814. *
  3815. * A modal view, which the media modal uses as its default container.
  3816. *
  3817. * @memberOf wp.media.view
  3818. *
  3819. * @class
  3820. * @augments wp.media.View
  3821. * @augments wp.Backbone.View
  3822. * @augments Backbone.View
  3823. */
  3824. Modal = wp.media.View.extend(/** @lends wp.media.view.Modal.prototype */{
  3825. tagName: 'div',
  3826. template: wp.template('media-modal'),
  3827. events: {
  3828. 'click .media-modal-backdrop, .media-modal-close': 'escapeHandler',
  3829. 'keydown': 'keydown'
  3830. },
  3831. clickedOpenerEl: null,
  3832. initialize: function() {
  3833. _.defaults( this.options, {
  3834. container: document.body,
  3835. title: '',
  3836. propagate: true,
  3837. hasCloseButton: true
  3838. });
  3839. this.focusManager = new wp.media.view.FocusManager({
  3840. el: this.el
  3841. });
  3842. },
  3843. /**
  3844. * @returns {Object}
  3845. */
  3846. prepare: function() {
  3847. return {
  3848. title: this.options.title,
  3849. hasCloseButton: this.options.hasCloseButton
  3850. };
  3851. },
  3852. /**
  3853. * @returns {wp.media.view.Modal} Returns itself to allow chaining
  3854. */
  3855. attach: function() {
  3856. if ( this.views.attached ) {
  3857. return this;
  3858. }
  3859. if ( ! this.views.rendered ) {
  3860. this.render();
  3861. }
  3862. this.$el.appendTo( this.options.container );
  3863. // Manually mark the view as attached and trigger ready.
  3864. this.views.attached = true;
  3865. this.views.ready();
  3866. return this.propagate('attach');
  3867. },
  3868. /**
  3869. * @returns {wp.media.view.Modal} Returns itself to allow chaining
  3870. */
  3871. detach: function() {
  3872. if ( this.$el.is(':visible') ) {
  3873. this.close();
  3874. }
  3875. this.$el.detach();
  3876. this.views.attached = false;
  3877. return this.propagate('detach');
  3878. },
  3879. /**
  3880. * @returns {wp.media.view.Modal} Returns itself to allow chaining
  3881. */
  3882. open: function() {
  3883. var $el = this.$el,
  3884. mceEditor;
  3885. if ( $el.is(':visible') ) {
  3886. return this;
  3887. }
  3888. this.clickedOpenerEl = document.activeElement;
  3889. if ( ! this.views.attached ) {
  3890. this.attach();
  3891. }
  3892. // Disable page scrolling.
  3893. $( 'body' ).addClass( 'modal-open' );
  3894. $el.show();
  3895. // Try to close the onscreen keyboard
  3896. if ( 'ontouchend' in document ) {
  3897. if ( ( mceEditor = window.tinymce && window.tinymce.activeEditor ) && ! mceEditor.isHidden() && mceEditor.iframeElement ) {
  3898. mceEditor.iframeElement.focus();
  3899. mceEditor.iframeElement.blur();
  3900. setTimeout( function() {
  3901. mceEditor.iframeElement.blur();
  3902. }, 100 );
  3903. }
  3904. }
  3905. // Set initial focus on the content instead of this view element, to avoid page scrolling.
  3906. this.$( '.media-modal' ).focus();
  3907. // Hide the page content from assistive technologies.
  3908. this.focusManager.setAriaHiddenOnBodyChildren( $el );
  3909. return this.propagate('open');
  3910. },
  3911. /**
  3912. * @param {Object} options
  3913. * @returns {wp.media.view.Modal} Returns itself to allow chaining
  3914. */
  3915. close: function( options ) {
  3916. if ( ! this.views.attached || ! this.$el.is(':visible') ) {
  3917. return this;
  3918. }
  3919. // Enable page scrolling.
  3920. $( 'body' ).removeClass( 'modal-open' );
  3921. // Hide modal and remove restricted media modal tab focus once it's closed
  3922. this.$el.hide().undelegate( 'keydown' );
  3923. /*
  3924. * Make visible again to assistive technologies all body children that
  3925. * have been made hidden when the modal opened.
  3926. */
  3927. this.focusManager.removeAriaHiddenFromBodyChildren();
  3928. // Move focus back in useful location once modal is closed.
  3929. if ( null !== this.clickedOpenerEl ) {
  3930. // Move focus back to the element that opened the modal.
  3931. this.clickedOpenerEl.focus();
  3932. } else {
  3933. // Fallback to the admin page main element.
  3934. $( '#wpbody-content' )
  3935. .attr( 'tabindex', '-1' )
  3936. .focus();
  3937. }
  3938. this.propagate('close');
  3939. if ( options && options.escape ) {
  3940. this.propagate('escape');
  3941. }
  3942. return this;
  3943. },
  3944. /**
  3945. * @returns {wp.media.view.Modal} Returns itself to allow chaining
  3946. */
  3947. escape: function() {
  3948. return this.close({ escape: true });
  3949. },
  3950. /**
  3951. * @param {Object} event
  3952. */
  3953. escapeHandler: function( event ) {
  3954. event.preventDefault();
  3955. this.escape();
  3956. },
  3957. /**
  3958. * @param {Array|Object} content Views to register to '.media-modal-content'
  3959. * @returns {wp.media.view.Modal} Returns itself to allow chaining
  3960. */
  3961. content: function( content ) {
  3962. this.views.set( '.media-modal-content', content );
  3963. return this;
  3964. },
  3965. /**
  3966. * Triggers a modal event and if the `propagate` option is set,
  3967. * forwards events to the modal's controller.
  3968. *
  3969. * @param {string} id
  3970. * @returns {wp.media.view.Modal} Returns itself to allow chaining
  3971. */
  3972. propagate: function( id ) {
  3973. this.trigger( id );
  3974. if ( this.options.propagate ) {
  3975. this.controller.trigger( id );
  3976. }
  3977. return this;
  3978. },
  3979. /**
  3980. * @param {Object} event
  3981. */
  3982. keydown: function( event ) {
  3983. // Close the modal when escape is pressed.
  3984. if ( 27 === event.which && this.$el.is(':visible') ) {
  3985. this.escape();
  3986. event.stopImmediatePropagation();
  3987. }
  3988. }
  3989. });
  3990. module.exports = Modal;
  3991. /***/ }),
  3992. /* 56 */
  3993. /***/ (function(module, exports) {
  3994. var $ = jQuery;
  3995. /**
  3996. * wp.media.view.FocusManager
  3997. *
  3998. * @memberOf wp.media.view
  3999. *
  4000. * @class
  4001. * @augments wp.media.View
  4002. * @augments wp.Backbone.View
  4003. * @augments Backbone.View
  4004. */
  4005. var FocusManager = wp.media.View.extend(/** @lends wp.media.view.FocusManager.prototype */{
  4006. events: {
  4007. 'keydown': 'focusManagementMode'
  4008. },
  4009. /**
  4010. * Initializes the Focus Manager.
  4011. *
  4012. * @param {object} options The Focus Manager options.
  4013. *
  4014. * @since 5.3.0
  4015. *
  4016. * @return {void}
  4017. */
  4018. initialize: function( options ) {
  4019. this.mode = options.mode || 'constrainTabbing';
  4020. this.tabsAutomaticActivation = options.tabsAutomaticActivation || false;
  4021. },
  4022. /**
  4023. * Determines which focus management mode to use.
  4024. *
  4025. * @since 5.3.0
  4026. *
  4027. * @param {object} event jQuery event object.
  4028. *
  4029. * @returns {void}
  4030. */
  4031. focusManagementMode: function( event ) {
  4032. if ( this.mode === 'constrainTabbing' ) {
  4033. this.constrainTabbing( event );
  4034. }
  4035. if ( this.mode === 'tabsNavigation' ) {
  4036. this.tabsNavigation( event );
  4037. }
  4038. },
  4039. /**
  4040. * Gets all the tabbable elements.
  4041. *
  4042. * @since 5.3.0
  4043. *
  4044. * @returns {object} A jQuery collection of tabbable elements.
  4045. */
  4046. getTabbables: function() {
  4047. // Skip the file input added by Plupload.
  4048. return this.$( ':tabbable' ).not( '.moxie-shim input[type="file"]' );
  4049. },
  4050. /**
  4051. * Moves focus to the modal dialog.
  4052. *
  4053. * @since 3.5.0
  4054. *
  4055. * @returns {void}
  4056. */
  4057. focus: function() {
  4058. this.$( '.media-modal' ).focus();
  4059. },
  4060. /**
  4061. * Constrains navigation with the Tab key within the media view element.
  4062. *
  4063. * @since 4.0.0
  4064. *
  4065. * @param {Object} event A keydown jQuery event.
  4066. *
  4067. * @returns {void}
  4068. */
  4069. constrainTabbing: function( event ) {
  4070. var tabbables;
  4071. // Look for the tab key.
  4072. if ( 9 !== event.keyCode ) {
  4073. return;
  4074. }
  4075. tabbables = this.getTabbables();
  4076. // Keep tab focus within media modal while it's open
  4077. if ( tabbables.last()[0] === event.target && ! event.shiftKey ) {
  4078. tabbables.first().focus();
  4079. return false;
  4080. } else if ( tabbables.first()[0] === event.target && event.shiftKey ) {
  4081. tabbables.last().focus();
  4082. return false;
  4083. }
  4084. },
  4085. /**
  4086. * Hides from assistive technologies all the body children.
  4087. *
  4088. * Sets an `aria-hidden="true"` attribute on all the body children except
  4089. * the provided element and other elements that should not be hidden.
  4090. *
  4091. * The reason why we use `aria-hidden` is that `aria-modal="true"` is buggy
  4092. * in Safari 11.1 and support is spotty in other browsers. Also, `aria-modal="true"`
  4093. * prevents the `wp.a11y.speak()` ARIA live regions to work as they're outside
  4094. * of the modal dialog and get hidden from assistive technologies.
  4095. *
  4096. * @since 5.2.3
  4097. *
  4098. * @param {object} visibleElement The jQuery object representing the element that should not be hidden.
  4099. *
  4100. * @returns {void}
  4101. */
  4102. setAriaHiddenOnBodyChildren: function( visibleElement ) {
  4103. var bodyChildren,
  4104. self = this;
  4105. if ( this.isBodyAriaHidden ) {
  4106. return;
  4107. }
  4108. // Get all the body children.
  4109. bodyChildren = document.body.children;
  4110. // Loop through the body children and hide the ones that should be hidden.
  4111. _.each( bodyChildren, function( element ) {
  4112. // Don't hide the modal element.
  4113. if ( element === visibleElement[0] ) {
  4114. return;
  4115. }
  4116. // Determine the body children to hide.
  4117. if ( self.elementShouldBeHidden( element ) ) {
  4118. element.setAttribute( 'aria-hidden', 'true' );
  4119. // Store the hidden elements.
  4120. self.ariaHiddenElements.push( element );
  4121. }
  4122. } );
  4123. this.isBodyAriaHidden = true;
  4124. },
  4125. /**
  4126. * Unhides from assistive technologies all the body children.
  4127. *
  4128. * Makes visible again to assistive technologies all the body children
  4129. * previously hidden and stored in this.ariaHiddenElements.
  4130. *
  4131. * @since 5.2.3
  4132. *
  4133. * @returns {void}
  4134. */
  4135. removeAriaHiddenFromBodyChildren: function() {
  4136. _.each( this.ariaHiddenElements, function( element ) {
  4137. element.removeAttribute( 'aria-hidden' );
  4138. } );
  4139. this.ariaHiddenElements = [];
  4140. this.isBodyAriaHidden = false;
  4141. },
  4142. /**
  4143. * Determines if the passed element should not be hidden from assistive technologies.
  4144. *
  4145. * @since 5.2.3
  4146. *
  4147. * @param {object} element The DOM element that should be checked.
  4148. *
  4149. * @returns {boolean} Whether the element should not be hidden from assistive technologies.
  4150. */
  4151. elementShouldBeHidden: function( element ) {
  4152. var role = element.getAttribute( 'role' ),
  4153. liveRegionsRoles = [ 'alert', 'status', 'log', 'marquee', 'timer' ];
  4154. /*
  4155. * Don't hide scripts, elements that already have `aria-hidden`, and
  4156. * ARIA live regions.
  4157. */
  4158. return ! (
  4159. element.tagName === 'SCRIPT' ||
  4160. element.hasAttribute( 'aria-hidden' ) ||
  4161. element.hasAttribute( 'aria-live' ) ||
  4162. liveRegionsRoles.indexOf( role ) !== -1
  4163. );
  4164. },
  4165. /**
  4166. * Whether the body children are hidden from assistive technologies.
  4167. *
  4168. * @since 5.2.3
  4169. */
  4170. isBodyAriaHidden: false,
  4171. /**
  4172. * Stores an array of DOM elements that should be hidden from assistive
  4173. * technologies, for example when the media modal dialog opens.
  4174. *
  4175. * @since 5.2.3
  4176. */
  4177. ariaHiddenElements: [],
  4178. /**
  4179. * Holds the jQuery collection of ARIA tabs.
  4180. *
  4181. * @since 5.3.0
  4182. */
  4183. tabs: $(),
  4184. /**
  4185. * Sets up tabs in an ARIA tabbed interface.
  4186. *
  4187. * @since 5.3.0
  4188. *
  4189. * @param {object} event jQuery event object.
  4190. *
  4191. * @returns {void}
  4192. */
  4193. setupAriaTabs: function() {
  4194. this.tabs = this.$( '[role="tab"]' );
  4195. // Set up initial attributes.
  4196. this.tabs.attr( {
  4197. 'aria-selected': 'false',
  4198. tabIndex: '-1'
  4199. } );
  4200. // Set up attributes on the initially active tab.
  4201. this.tabs.filter( '.active' )
  4202. .removeAttr( 'tabindex' )
  4203. .attr( 'aria-selected', 'true' );
  4204. },
  4205. /**
  4206. * Enables arrows navigation within the ARIA tabbed interface.
  4207. *
  4208. * @since 5.3.0
  4209. *
  4210. * @param {object} event jQuery event object.
  4211. *
  4212. * @returns {void}
  4213. */
  4214. tabsNavigation: function( event ) {
  4215. var orientation = 'horizontal',
  4216. keys = [ 32, 35, 36, 37, 38, 39, 40 ];
  4217. // Return if not Spacebar, End, Home, or Arrow keys.
  4218. if ( keys.indexOf( event.which ) === -1 ) {
  4219. return;
  4220. }
  4221. // Determine navigation direction.
  4222. if ( this.$el.attr( 'aria-orientation' ) === 'vertical' ) {
  4223. orientation = 'vertical';
  4224. }
  4225. // Make Up and Down arrow keys do nothing with horizontal tabs.
  4226. if ( orientation === 'horizontal' && [ 38, 40 ].indexOf( event.which ) !== -1 ) {
  4227. return;
  4228. }
  4229. // Make Left and Right arrow keys do nothing with vertical tabs.
  4230. if ( orientation === 'vertical' && [ 37, 39 ].indexOf( event.which ) !== -1 ) {
  4231. return;
  4232. }
  4233. this.switchTabs( event, this.tabs );
  4234. },
  4235. /**
  4236. * Switches tabs in the ARIA tabbed interface.
  4237. *
  4238. * @since 5.3.0
  4239. *
  4240. * @param {object} event jQuery event object.
  4241. *
  4242. * @returns {void}
  4243. */
  4244. switchTabs: function( event ) {
  4245. var key = event.which,
  4246. index = this.tabs.index( $( event.target ) ),
  4247. newIndex;
  4248. switch ( key ) {
  4249. // Space bar: Activate current targeted tab.
  4250. case 32: {
  4251. this.activateTab( this.tabs[ index ] );
  4252. break;
  4253. }
  4254. // End key: Activate last tab.
  4255. case 35: {
  4256. event.preventDefault();
  4257. this.activateTab( this.tabs[ this.tabs.length - 1 ] );
  4258. break;
  4259. }
  4260. // Home key: Activate first tab.
  4261. case 36: {
  4262. event.preventDefault();
  4263. this.activateTab( this.tabs[ 0 ] );
  4264. break;
  4265. }
  4266. // Left and up keys: Activate previous tab.
  4267. case 37:
  4268. case 38: {
  4269. event.preventDefault();
  4270. newIndex = ( index - 1 ) < 0 ? this.tabs.length - 1 : index - 1;
  4271. this.activateTab( this.tabs[ newIndex ] );
  4272. break;
  4273. }
  4274. // Right and down keys: Activate next tab.
  4275. case 39:
  4276. case 40: {
  4277. event.preventDefault();
  4278. newIndex = ( index + 1 ) === this.tabs.length ? 0 : index + 1;
  4279. this.activateTab( this.tabs[ newIndex ] );
  4280. break;
  4281. }
  4282. }
  4283. },
  4284. /**
  4285. * Sets a single tab to be focusable and semantically selected.
  4286. *
  4287. * @since 5.3.0
  4288. *
  4289. * @param {object} tab The tab DOM element.
  4290. *
  4291. * @returns {void}
  4292. */
  4293. activateTab: function( tab ) {
  4294. if ( ! tab ) {
  4295. return;
  4296. }
  4297. // The tab is a DOM element: no need for jQuery methods.
  4298. tab.focus();
  4299. // Handle automatic activation.
  4300. if ( this.tabsAutomaticActivation ) {
  4301. tab.removeAttribute( 'tabindex' );
  4302. tab.setAttribute( 'aria-selected', 'true' );
  4303. tab.click();
  4304. return;
  4305. }
  4306. // Handle manual activation.
  4307. $( tab ).on( 'click', function() {
  4308. tab.removeAttribute( 'tabindex' );
  4309. tab.setAttribute( 'aria-selected', 'true' );
  4310. } );
  4311. }
  4312. });
  4313. module.exports = FocusManager;
  4314. /***/ }),
  4315. /* 57 */
  4316. /***/ (function(module, exports) {
  4317. var $ = jQuery,
  4318. UploaderWindow;
  4319. /**
  4320. * wp.media.view.UploaderWindow
  4321. *
  4322. * An uploader window that allows for dragging and dropping media.
  4323. *
  4324. * @memberOf wp.media.view
  4325. *
  4326. * @class
  4327. * @augments wp.media.View
  4328. * @augments wp.Backbone.View
  4329. * @augments Backbone.View
  4330. *
  4331. * @param {object} [options] Options hash passed to the view.
  4332. * @param {object} [options.uploader] Uploader properties.
  4333. * @param {jQuery} [options.uploader.browser]
  4334. * @param {jQuery} [options.uploader.dropzone] jQuery collection of the dropzone.
  4335. * @param {object} [options.uploader.params]
  4336. */
  4337. UploaderWindow = wp.media.View.extend(/** @lends wp.media.view.UploaderWindow.prototype */{
  4338. tagName: 'div',
  4339. className: 'uploader-window',
  4340. template: wp.template('uploader-window'),
  4341. initialize: function() {
  4342. var uploader;
  4343. this.$browser = $( '<button type="button" class="browser" />' ).hide().appendTo( 'body' );
  4344. uploader = this.options.uploader = _.defaults( this.options.uploader || {}, {
  4345. dropzone: this.$el,
  4346. browser: this.$browser,
  4347. params: {}
  4348. });
  4349. // Ensure the dropzone is a jQuery collection.
  4350. if ( uploader.dropzone && ! (uploader.dropzone instanceof $) ) {
  4351. uploader.dropzone = $( uploader.dropzone );
  4352. }
  4353. this.controller.on( 'activate', this.refresh, this );
  4354. this.controller.on( 'detach', function() {
  4355. this.$browser.remove();
  4356. }, this );
  4357. },
  4358. refresh: function() {
  4359. if ( this.uploader ) {
  4360. this.uploader.refresh();
  4361. }
  4362. },
  4363. ready: function() {
  4364. var postId = wp.media.view.settings.post.id,
  4365. dropzone;
  4366. // If the uploader already exists, bail.
  4367. if ( this.uploader ) {
  4368. return;
  4369. }
  4370. if ( postId ) {
  4371. this.options.uploader.params.post_id = postId;
  4372. }
  4373. this.uploader = new wp.Uploader( this.options.uploader );
  4374. dropzone = this.uploader.dropzone;
  4375. dropzone.on( 'dropzone:enter', _.bind( this.show, this ) );
  4376. dropzone.on( 'dropzone:leave', _.bind( this.hide, this ) );
  4377. $( this.uploader ).on( 'uploader:ready', _.bind( this._ready, this ) );
  4378. },
  4379. _ready: function() {
  4380. this.controller.trigger( 'uploader:ready' );
  4381. },
  4382. show: function() {
  4383. var $el = this.$el.show();
  4384. // Ensure that the animation is triggered by waiting until
  4385. // the transparent element is painted into the DOM.
  4386. _.defer( function() {
  4387. $el.css({ opacity: 1 });
  4388. });
  4389. },
  4390. hide: function() {
  4391. var $el = this.$el.css({ opacity: 0 });
  4392. wp.media.transition( $el ).done( function() {
  4393. // Transition end events are subject to race conditions.
  4394. // Make sure that the value is set as intended.
  4395. if ( '0' === $el.css('opacity') ) {
  4396. $el.hide();
  4397. }
  4398. });
  4399. // https://core.trac.wordpress.org/ticket/27341
  4400. _.delay( function() {
  4401. if ( '0' === $el.css('opacity') && $el.is(':visible') ) {
  4402. $el.hide();
  4403. }
  4404. }, 500 );
  4405. }
  4406. });
  4407. module.exports = UploaderWindow;
  4408. /***/ }),
  4409. /* 58 */
  4410. /***/ (function(module, exports) {
  4411. var View = wp.media.View,
  4412. l10n = wp.media.view.l10n,
  4413. $ = jQuery,
  4414. EditorUploader;
  4415. /**
  4416. * Creates a dropzone on WP editor instances (elements with .wp-editor-wrap)
  4417. * and relays drag'n'dropped files to a media workflow.
  4418. *
  4419. * wp.media.view.EditorUploader
  4420. *
  4421. * @memberOf wp.media.view
  4422. *
  4423. * @class
  4424. * @augments wp.media.View
  4425. * @augments wp.Backbone.View
  4426. * @augments Backbone.View
  4427. */
  4428. EditorUploader = View.extend(/** @lends wp.media.view.EditorUploader.prototype */{
  4429. tagName: 'div',
  4430. className: 'uploader-editor',
  4431. template: wp.template( 'uploader-editor' ),
  4432. localDrag: false,
  4433. overContainer: false,
  4434. overDropzone: false,
  4435. draggingFile: null,
  4436. /**
  4437. * Bind drag'n'drop events to callbacks.
  4438. */
  4439. initialize: function() {
  4440. this.initialized = false;
  4441. // Bail if not enabled or UA does not support drag'n'drop or File API.
  4442. if ( ! window.tinyMCEPreInit || ! window.tinyMCEPreInit.dragDropUpload || ! this.browserSupport() ) {
  4443. return this;
  4444. }
  4445. this.$document = $(document);
  4446. this.dropzones = [];
  4447. this.files = [];
  4448. this.$document.on( 'drop', '.uploader-editor', _.bind( this.drop, this ) );
  4449. this.$document.on( 'dragover', '.uploader-editor', _.bind( this.dropzoneDragover, this ) );
  4450. this.$document.on( 'dragleave', '.uploader-editor', _.bind( this.dropzoneDragleave, this ) );
  4451. this.$document.on( 'click', '.uploader-editor', _.bind( this.click, this ) );
  4452. this.$document.on( 'dragover', _.bind( this.containerDragover, this ) );
  4453. this.$document.on( 'dragleave', _.bind( this.containerDragleave, this ) );
  4454. this.$document.on( 'dragstart dragend drop', _.bind( function( event ) {
  4455. this.localDrag = event.type === 'dragstart';
  4456. if ( event.type === 'drop' ) {
  4457. this.containerDragleave();
  4458. }
  4459. }, this ) );
  4460. this.initialized = true;
  4461. return this;
  4462. },
  4463. /**
  4464. * Check browser support for drag'n'drop.
  4465. *
  4466. * @return Boolean
  4467. */
  4468. browserSupport: function() {
  4469. var supports = false, div = document.createElement('div');
  4470. supports = ( 'draggable' in div ) || ( 'ondragstart' in div && 'ondrop' in div );
  4471. supports = supports && !! ( window.File && window.FileList && window.FileReader );
  4472. return supports;
  4473. },
  4474. isDraggingFile: function( event ) {
  4475. if ( this.draggingFile !== null ) {
  4476. return this.draggingFile;
  4477. }
  4478. if ( _.isUndefined( event.originalEvent ) || _.isUndefined( event.originalEvent.dataTransfer ) ) {
  4479. return false;
  4480. }
  4481. this.draggingFile = _.indexOf( event.originalEvent.dataTransfer.types, 'Files' ) > -1 &&
  4482. _.indexOf( event.originalEvent.dataTransfer.types, 'text/plain' ) === -1;
  4483. return this.draggingFile;
  4484. },
  4485. refresh: function( e ) {
  4486. var dropzone_id;
  4487. for ( dropzone_id in this.dropzones ) {
  4488. // Hide the dropzones only if dragging has left the screen.
  4489. this.dropzones[ dropzone_id ].toggle( this.overContainer || this.overDropzone );
  4490. }
  4491. if ( ! _.isUndefined( e ) ) {
  4492. $( e.target ).closest( '.uploader-editor' ).toggleClass( 'droppable', this.overDropzone );
  4493. }
  4494. if ( ! this.overContainer && ! this.overDropzone ) {
  4495. this.draggingFile = null;
  4496. }
  4497. return this;
  4498. },
  4499. render: function() {
  4500. if ( ! this.initialized ) {
  4501. return this;
  4502. }
  4503. View.prototype.render.apply( this, arguments );
  4504. $( '.wp-editor-wrap' ).each( _.bind( this.attach, this ) );
  4505. return this;
  4506. },
  4507. attach: function( index, editor ) {
  4508. // Attach a dropzone to an editor.
  4509. var dropzone = this.$el.clone();
  4510. this.dropzones.push( dropzone );
  4511. $( editor ).append( dropzone );
  4512. return this;
  4513. },
  4514. /**
  4515. * When a file is dropped on the editor uploader, open up an editor media workflow
  4516. * and upload the file immediately.
  4517. *
  4518. * @param {jQuery.Event} event The 'drop' event.
  4519. */
  4520. drop: function( event ) {
  4521. var $wrap, uploadView;
  4522. this.containerDragleave( event );
  4523. this.dropzoneDragleave( event );
  4524. this.files = event.originalEvent.dataTransfer.files;
  4525. if ( this.files.length < 1 ) {
  4526. return;
  4527. }
  4528. // Set the active editor to the drop target.
  4529. $wrap = $( event.target ).parents( '.wp-editor-wrap' );
  4530. if ( $wrap.length > 0 && $wrap[0].id ) {
  4531. window.wpActiveEditor = $wrap[0].id.slice( 3, -5 );
  4532. }
  4533. if ( ! this.workflow ) {
  4534. this.workflow = wp.media.editor.open( window.wpActiveEditor, {
  4535. frame: 'post',
  4536. state: 'insert',
  4537. title: l10n.addMedia,
  4538. multiple: true
  4539. });
  4540. uploadView = this.workflow.uploader;
  4541. if ( uploadView.uploader && uploadView.uploader.ready ) {
  4542. this.addFiles.apply( this );
  4543. } else {
  4544. this.workflow.on( 'uploader:ready', this.addFiles, this );
  4545. }
  4546. } else {
  4547. this.workflow.state().reset();
  4548. this.addFiles.apply( this );
  4549. this.workflow.open();
  4550. }
  4551. return false;
  4552. },
  4553. /**
  4554. * Add the files to the uploader.
  4555. */
  4556. addFiles: function() {
  4557. if ( this.files.length ) {
  4558. this.workflow.uploader.uploader.uploader.addFile( _.toArray( this.files ) );
  4559. this.files = [];
  4560. }
  4561. return this;
  4562. },
  4563. containerDragover: function( event ) {
  4564. if ( this.localDrag || ! this.isDraggingFile( event ) ) {
  4565. return;
  4566. }
  4567. this.overContainer = true;
  4568. this.refresh();
  4569. },
  4570. containerDragleave: function() {
  4571. this.overContainer = false;
  4572. // Throttle dragleave because it's called when bouncing from some elements to others.
  4573. _.delay( _.bind( this.refresh, this ), 50 );
  4574. },
  4575. dropzoneDragover: function( event ) {
  4576. if ( this.localDrag || ! this.isDraggingFile( event ) ) {
  4577. return;
  4578. }
  4579. this.overDropzone = true;
  4580. this.refresh( event );
  4581. return false;
  4582. },
  4583. dropzoneDragleave: function( e ) {
  4584. this.overDropzone = false;
  4585. _.delay( _.bind( this.refresh, this, e ), 50 );
  4586. },
  4587. click: function( e ) {
  4588. // In the rare case where the dropzone gets stuck, hide it on click.
  4589. this.containerDragleave( e );
  4590. this.dropzoneDragleave( e );
  4591. this.localDrag = false;
  4592. }
  4593. });
  4594. module.exports = EditorUploader;
  4595. /***/ }),
  4596. /* 59 */
  4597. /***/ (function(module, exports) {
  4598. var View = wp.media.View,
  4599. UploaderInline;
  4600. /**
  4601. * wp.media.view.UploaderInline
  4602. *
  4603. * The inline uploader that shows up in the 'Upload Files' tab.
  4604. *
  4605. * @memberOf wp.media.view
  4606. *
  4607. * @class
  4608. * @augments wp.media.View
  4609. * @augments wp.Backbone.View
  4610. * @augments Backbone.View
  4611. */
  4612. UploaderInline = View.extend(/** @lends wp.media.view.UploaderInline.prototype */{
  4613. tagName: 'div',
  4614. className: 'uploader-inline',
  4615. template: wp.template('uploader-inline'),
  4616. events: {
  4617. 'click .close': 'hide'
  4618. },
  4619. initialize: function() {
  4620. _.defaults( this.options, {
  4621. message: '',
  4622. status: true,
  4623. canClose: false
  4624. });
  4625. if ( ! this.options.$browser && this.controller.uploader ) {
  4626. this.options.$browser = this.controller.uploader.$browser;
  4627. }
  4628. if ( _.isUndefined( this.options.postId ) ) {
  4629. this.options.postId = wp.media.view.settings.post.id;
  4630. }
  4631. if ( this.options.status ) {
  4632. this.views.set( '.upload-inline-status', new wp.media.view.UploaderStatus({
  4633. controller: this.controller
  4634. }) );
  4635. }
  4636. },
  4637. prepare: function() {
  4638. var suggestedWidth = this.controller.state().get('suggestedWidth'),
  4639. suggestedHeight = this.controller.state().get('suggestedHeight'),
  4640. data = {};
  4641. data.message = this.options.message;
  4642. data.canClose = this.options.canClose;
  4643. if ( suggestedWidth && suggestedHeight ) {
  4644. data.suggestedWidth = suggestedWidth;
  4645. data.suggestedHeight = suggestedHeight;
  4646. }
  4647. return data;
  4648. },
  4649. /**
  4650. * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining
  4651. */
  4652. dispose: function() {
  4653. if ( this.disposing ) {
  4654. /**
  4655. * call 'dispose' directly on the parent class
  4656. */
  4657. return View.prototype.dispose.apply( this, arguments );
  4658. }
  4659. // Run remove on `dispose`, so we can be sure to refresh the
  4660. // uploader with a view-less DOM. Track whether we're disposing
  4661. // so we don't trigger an infinite loop.
  4662. this.disposing = true;
  4663. return this.remove();
  4664. },
  4665. /**
  4666. * @returns {wp.media.view.UploaderInline} Returns itself to allow chaining
  4667. */
  4668. remove: function() {
  4669. /**
  4670. * call 'remove' directly on the parent class
  4671. */
  4672. var result = View.prototype.remove.apply( this, arguments );
  4673. _.defer( _.bind( this.refresh, this ) );
  4674. return result;
  4675. },
  4676. refresh: function() {
  4677. var uploader = this.controller.uploader;
  4678. if ( uploader ) {
  4679. uploader.refresh();
  4680. }
  4681. },
  4682. /**
  4683. * @returns {wp.media.view.UploaderInline}
  4684. */
  4685. ready: function() {
  4686. var $browser = this.options.$browser,
  4687. $placeholder;
  4688. if ( this.controller.uploader ) {
  4689. $placeholder = this.$('.browser');
  4690. // Check if we've already replaced the placeholder.
  4691. if ( $placeholder[0] === $browser[0] ) {
  4692. return;
  4693. }
  4694. $browser.detach().text( $placeholder.text() );
  4695. $browser[0].className = $placeholder[0].className;
  4696. $placeholder.replaceWith( $browser.show() );
  4697. }
  4698. this.refresh();
  4699. return this;
  4700. },
  4701. show: function() {
  4702. this.$el.removeClass( 'hidden' );
  4703. if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) {
  4704. this.controller.$uploaderToggler.attr( 'aria-expanded', 'true' );
  4705. }
  4706. },
  4707. hide: function() {
  4708. this.$el.addClass( 'hidden' );
  4709. if ( this.controller.$uploaderToggler && this.controller.$uploaderToggler.length ) {
  4710. this.controller.$uploaderToggler
  4711. .attr( 'aria-expanded', 'false' )
  4712. // Move focus back to the toggle button when closing the uploader.
  4713. .focus();
  4714. }
  4715. }
  4716. });
  4717. module.exports = UploaderInline;
  4718. /***/ }),
  4719. /* 60 */
  4720. /***/ (function(module, exports) {
  4721. var View = wp.media.View,
  4722. UploaderStatus;
  4723. /**
  4724. * wp.media.view.UploaderStatus
  4725. *
  4726. * An uploader status for on-going uploads.
  4727. *
  4728. * @memberOf wp.media.view
  4729. *
  4730. * @class
  4731. * @augments wp.media.View
  4732. * @augments wp.Backbone.View
  4733. * @augments Backbone.View
  4734. */
  4735. UploaderStatus = View.extend(/** @lends wp.media.view.UploaderStatus.prototype */{
  4736. className: 'media-uploader-status',
  4737. template: wp.template('uploader-status'),
  4738. events: {
  4739. 'click .upload-dismiss-errors': 'dismiss'
  4740. },
  4741. initialize: function() {
  4742. this.queue = wp.Uploader.queue;
  4743. this.queue.on( 'add remove reset', this.visibility, this );
  4744. this.queue.on( 'add remove reset change:percent', this.progress, this );
  4745. this.queue.on( 'add remove reset change:uploading', this.info, this );
  4746. this.errors = wp.Uploader.errors;
  4747. this.errors.reset();
  4748. this.errors.on( 'add remove reset', this.visibility, this );
  4749. this.errors.on( 'add', this.error, this );
  4750. },
  4751. /**
  4752. * @returns {wp.media.view.UploaderStatus}
  4753. */
  4754. dispose: function() {
  4755. wp.Uploader.queue.off( null, null, this );
  4756. /**
  4757. * call 'dispose' directly on the parent class
  4758. */
  4759. View.prototype.dispose.apply( this, arguments );
  4760. return this;
  4761. },
  4762. visibility: function() {
  4763. this.$el.toggleClass( 'uploading', !! this.queue.length );
  4764. this.$el.toggleClass( 'errors', !! this.errors.length );
  4765. this.$el.toggle( !! this.queue.length || !! this.errors.length );
  4766. },
  4767. ready: function() {
  4768. _.each({
  4769. '$bar': '.media-progress-bar div',
  4770. '$index': '.upload-index',
  4771. '$total': '.upload-total',
  4772. '$filename': '.upload-filename'
  4773. }, function( selector, key ) {
  4774. this[ key ] = this.$( selector );
  4775. }, this );
  4776. this.visibility();
  4777. this.progress();
  4778. this.info();
  4779. },
  4780. progress: function() {
  4781. var queue = this.queue,
  4782. $bar = this.$bar;
  4783. if ( ! $bar || ! queue.length ) {
  4784. return;
  4785. }
  4786. $bar.width( ( queue.reduce( function( memo, attachment ) {
  4787. if ( ! attachment.get('uploading') ) {
  4788. return memo + 100;
  4789. }
  4790. var percent = attachment.get('percent');
  4791. return memo + ( _.isNumber( percent ) ? percent : 100 );
  4792. }, 0 ) / queue.length ) + '%' );
  4793. },
  4794. info: function() {
  4795. var queue = this.queue,
  4796. index = 0, active;
  4797. if ( ! queue.length ) {
  4798. return;
  4799. }
  4800. active = this.queue.find( function( attachment, i ) {
  4801. index = i;
  4802. return attachment.get('uploading');
  4803. });
  4804. this.$index.text( index + 1 );
  4805. this.$total.text( queue.length );
  4806. this.$filename.html( active ? this.filename( active.get('filename') ) : '' );
  4807. },
  4808. /**
  4809. * @param {string} filename
  4810. * @returns {string}
  4811. */
  4812. filename: function( filename ) {
  4813. return _.escape( filename );
  4814. },
  4815. /**
  4816. * @param {Backbone.Model} error
  4817. */
  4818. error: function( error ) {
  4819. var statusError = new wp.media.view.UploaderStatusError( {
  4820. filename: this.filename( error.get( 'file' ).name ),
  4821. message: error.get( 'message' )
  4822. } );
  4823. // Can show additional info here while retrying to create image sub-sizes.
  4824. this.views.add( '.upload-errors', statusError, { at: 0 } );
  4825. },
  4826. dismiss: function() {
  4827. var errors = this.views.get('.upload-errors');
  4828. if ( errors ) {
  4829. _.invoke( errors, 'remove' );
  4830. }
  4831. wp.Uploader.errors.reset();
  4832. // Move focus to the modal after the dismiss button gets removed from the DOM.
  4833. if ( this.controller.modal ) {
  4834. this.controller.modal.focusManager.focus();
  4835. }
  4836. }
  4837. });
  4838. module.exports = UploaderStatus;
  4839. /***/ }),
  4840. /* 61 */
  4841. /***/ (function(module, exports) {
  4842. /**
  4843. * wp.media.view.UploaderStatusError
  4844. *
  4845. * @memberOf wp.media.view
  4846. *
  4847. * @class
  4848. * @augments wp.media.View
  4849. * @augments wp.Backbone.View
  4850. * @augments Backbone.View
  4851. */
  4852. var UploaderStatusError = wp.media.View.extend(/** @lends wp.media.view.UploaderStatusError.prototype */{
  4853. className: 'upload-error',
  4854. template: wp.template('uploader-status-error')
  4855. });
  4856. module.exports = UploaderStatusError;
  4857. /***/ }),
  4858. /* 62 */
  4859. /***/ (function(module, exports) {
  4860. var View = wp.media.View,
  4861. Toolbar;
  4862. /**
  4863. * wp.media.view.Toolbar
  4864. *
  4865. * A toolbar which consists of a primary and a secondary section. Each sections
  4866. * can be filled with views.
  4867. *
  4868. * @memberOf wp.media.view
  4869. *
  4870. * @class
  4871. * @augments wp.media.View
  4872. * @augments wp.Backbone.View
  4873. * @augments Backbone.View
  4874. */
  4875. Toolbar = View.extend(/** @lends wp.media.view.Toolbar.prototype */{
  4876. tagName: 'div',
  4877. className: 'media-toolbar',
  4878. initialize: function() {
  4879. var state = this.controller.state(),
  4880. selection = this.selection = state.get('selection'),
  4881. library = this.library = state.get('library');
  4882. this._views = {};
  4883. // The toolbar is composed of two `PriorityList` views.
  4884. this.primary = new wp.media.view.PriorityList();
  4885. this.secondary = new wp.media.view.PriorityList();
  4886. this.primary.$el.addClass('media-toolbar-primary search-form');
  4887. this.secondary.$el.addClass('media-toolbar-secondary');
  4888. this.views.set([ this.secondary, this.primary ]);
  4889. if ( this.options.items ) {
  4890. this.set( this.options.items, { silent: true });
  4891. }
  4892. if ( ! this.options.silent ) {
  4893. this.render();
  4894. }
  4895. if ( selection ) {
  4896. selection.on( 'add remove reset', this.refresh, this );
  4897. }
  4898. if ( library ) {
  4899. library.on( 'add remove reset', this.refresh, this );
  4900. }
  4901. },
  4902. /**
  4903. * @returns {wp.media.view.Toolbar} Returns itsef to allow chaining
  4904. */
  4905. dispose: function() {
  4906. if ( this.selection ) {
  4907. this.selection.off( null, null, this );
  4908. }
  4909. if ( this.library ) {
  4910. this.library.off( null, null, this );
  4911. }
  4912. /**
  4913. * call 'dispose' directly on the parent class
  4914. */
  4915. return View.prototype.dispose.apply( this, arguments );
  4916. },
  4917. ready: function() {
  4918. this.refresh();
  4919. },
  4920. /**
  4921. * @param {string} id
  4922. * @param {Backbone.View|Object} view
  4923. * @param {Object} [options={}]
  4924. * @returns {wp.media.view.Toolbar} Returns itself to allow chaining
  4925. */
  4926. set: function( id, view, options ) {
  4927. var list;
  4928. options = options || {};
  4929. // Accept an object with an `id` : `view` mapping.
  4930. if ( _.isObject( id ) ) {
  4931. _.each( id, function( view, id ) {
  4932. this.set( id, view, { silent: true });
  4933. }, this );
  4934. } else {
  4935. if ( ! ( view instanceof Backbone.View ) ) {
  4936. view.classes = [ 'media-button-' + id ].concat( view.classes || [] );
  4937. view = new wp.media.view.Button( view ).render();
  4938. }
  4939. view.controller = view.controller || this.controller;
  4940. this._views[ id ] = view;
  4941. list = view.options.priority < 0 ? 'secondary' : 'primary';
  4942. this[ list ].set( id, view, options );
  4943. }
  4944. if ( ! options.silent ) {
  4945. this.refresh();
  4946. }
  4947. return this;
  4948. },
  4949. /**
  4950. * @param {string} id
  4951. * @returns {wp.media.view.Button}
  4952. */
  4953. get: function( id ) {
  4954. return this._views[ id ];
  4955. },
  4956. /**
  4957. * @param {string} id
  4958. * @param {Object} options
  4959. * @returns {wp.media.view.Toolbar} Returns itself to allow chaining
  4960. */
  4961. unset: function( id, options ) {
  4962. delete this._views[ id ];
  4963. this.primary.unset( id, options );
  4964. this.secondary.unset( id, options );
  4965. if ( ! options || ! options.silent ) {
  4966. this.refresh();
  4967. }
  4968. return this;
  4969. },
  4970. refresh: function() {
  4971. var state = this.controller.state(),
  4972. library = state.get('library'),
  4973. selection = state.get('selection');
  4974. _.each( this._views, function( button ) {
  4975. if ( ! button.model || ! button.options || ! button.options.requires ) {
  4976. return;
  4977. }
  4978. var requires = button.options.requires,
  4979. disabled = false;
  4980. // Prevent insertion of attachments if any of them are still uploading
  4981. if ( selection && selection.models ) {
  4982. disabled = _.some( selection.models, function( attachment ) {
  4983. return attachment.get('uploading') === true;
  4984. });
  4985. }
  4986. if ( requires.selection && selection && ! selection.length ) {
  4987. disabled = true;
  4988. } else if ( requires.library && library && ! library.length ) {
  4989. disabled = true;
  4990. }
  4991. button.model.set( 'disabled', disabled );
  4992. });
  4993. }
  4994. });
  4995. module.exports = Toolbar;
  4996. /***/ }),
  4997. /* 63 */
  4998. /***/ (function(module, exports) {
  4999. var Toolbar = wp.media.view.Toolbar,
  5000. l10n = wp.media.view.l10n,
  5001. Select;
  5002. /**
  5003. * wp.media.view.Toolbar.Select
  5004. *
  5005. * @memberOf wp.media.view.Toolbar
  5006. *
  5007. * @class
  5008. * @augments wp.media.view.Toolbar
  5009. * @augments wp.media.View
  5010. * @augments wp.Backbone.View
  5011. * @augments Backbone.View
  5012. */
  5013. Select = Toolbar.extend(/** @lends wp.media.view.Toolbar.Select.prototype */{
  5014. initialize: function() {
  5015. var options = this.options;
  5016. _.bindAll( this, 'clickSelect' );
  5017. _.defaults( options, {
  5018. event: 'select',
  5019. state: false,
  5020. reset: true,
  5021. close: true,
  5022. text: l10n.select,
  5023. // Does the button rely on the selection?
  5024. requires: {
  5025. selection: true
  5026. }
  5027. });
  5028. options.items = _.defaults( options.items || {}, {
  5029. select: {
  5030. style: 'primary',
  5031. text: options.text,
  5032. priority: 80,
  5033. click: this.clickSelect,
  5034. requires: options.requires
  5035. }
  5036. });
  5037. // Call 'initialize' directly on the parent class.
  5038. Toolbar.prototype.initialize.apply( this, arguments );
  5039. },
  5040. clickSelect: function() {
  5041. var options = this.options,
  5042. controller = this.controller;
  5043. if ( options.close ) {
  5044. controller.close();
  5045. }
  5046. if ( options.event ) {
  5047. controller.state().trigger( options.event );
  5048. }
  5049. if ( options.state ) {
  5050. controller.setState( options.state );
  5051. }
  5052. if ( options.reset ) {
  5053. controller.reset();
  5054. }
  5055. }
  5056. });
  5057. module.exports = Select;
  5058. /***/ }),
  5059. /* 64 */
  5060. /***/ (function(module, exports) {
  5061. var Select = wp.media.view.Toolbar.Select,
  5062. l10n = wp.media.view.l10n,
  5063. Embed;
  5064. /**
  5065. * wp.media.view.Toolbar.Embed
  5066. *
  5067. * @memberOf wp.media.view.Toolbar
  5068. *
  5069. * @class
  5070. * @augments wp.media.view.Toolbar.Select
  5071. * @augments wp.media.view.Toolbar
  5072. * @augments wp.media.View
  5073. * @augments wp.Backbone.View
  5074. * @augments Backbone.View
  5075. */
  5076. Embed = Select.extend(/** @lends wp.media.view.Toolbar.Embed.prototype */{
  5077. initialize: function() {
  5078. _.defaults( this.options, {
  5079. text: l10n.insertIntoPost,
  5080. requires: false
  5081. });
  5082. // Call 'initialize' directly on the parent class.
  5083. Select.prototype.initialize.apply( this, arguments );
  5084. },
  5085. refresh: function() {
  5086. var url = this.controller.state().props.get('url');
  5087. this.get('select').model.set( 'disabled', ! url || url === 'http://' );
  5088. /**
  5089. * call 'refresh' directly on the parent class
  5090. */
  5091. Select.prototype.refresh.apply( this, arguments );
  5092. }
  5093. });
  5094. module.exports = Embed;
  5095. /***/ }),
  5096. /* 65 */
  5097. /***/ (function(module, exports) {
  5098. /**
  5099. * wp.media.view.Button
  5100. *
  5101. * @memberOf wp.media.view
  5102. *
  5103. * @class
  5104. * @augments wp.media.View
  5105. * @augments wp.Backbone.View
  5106. * @augments Backbone.View
  5107. */
  5108. var Button = wp.media.View.extend(/** @lends wp.media.view.Button.prototype */{
  5109. tagName: 'button',
  5110. className: 'media-button',
  5111. attributes: { type: 'button' },
  5112. events: {
  5113. 'click': 'click'
  5114. },
  5115. defaults: {
  5116. text: '',
  5117. style: '',
  5118. size: 'large',
  5119. disabled: false
  5120. },
  5121. initialize: function() {
  5122. /**
  5123. * Create a model with the provided `defaults`.
  5124. *
  5125. * @member {Backbone.Model}
  5126. */
  5127. this.model = new Backbone.Model( this.defaults );
  5128. // If any of the `options` have a key from `defaults`, apply its
  5129. // value to the `model` and remove it from the `options object.
  5130. _.each( this.defaults, function( def, key ) {
  5131. var value = this.options[ key ];
  5132. if ( _.isUndefined( value ) ) {
  5133. return;
  5134. }
  5135. this.model.set( key, value );
  5136. delete this.options[ key ];
  5137. }, this );
  5138. this.listenTo( this.model, 'change', this.render );
  5139. },
  5140. /**
  5141. * @returns {wp.media.view.Button} Returns itself to allow chaining
  5142. */
  5143. render: function() {
  5144. var classes = [ 'button', this.className ],
  5145. model = this.model.toJSON();
  5146. if ( model.style ) {
  5147. classes.push( 'button-' + model.style );
  5148. }
  5149. if ( model.size ) {
  5150. classes.push( 'button-' + model.size );
  5151. }
  5152. classes = _.uniq( classes.concat( this.options.classes ) );
  5153. this.el.className = classes.join(' ');
  5154. this.$el.attr( 'disabled', model.disabled );
  5155. this.$el.text( this.model.get('text') );
  5156. return this;
  5157. },
  5158. /**
  5159. * @param {Object} event
  5160. */
  5161. click: function( event ) {
  5162. if ( '#' === this.attributes.href ) {
  5163. event.preventDefault();
  5164. }
  5165. if ( this.options.click && ! this.model.get('disabled') ) {
  5166. this.options.click.apply( this, arguments );
  5167. }
  5168. }
  5169. });
  5170. module.exports = Button;
  5171. /***/ }),
  5172. /* 66 */
  5173. /***/ (function(module, exports) {
  5174. var $ = Backbone.$,
  5175. ButtonGroup;
  5176. /**
  5177. * wp.media.view.ButtonGroup
  5178. *
  5179. * @memberOf wp.media.view
  5180. *
  5181. * @class
  5182. * @augments wp.media.View
  5183. * @augments wp.Backbone.View
  5184. * @augments Backbone.View
  5185. */
  5186. ButtonGroup = wp.media.View.extend(/** @lends wp.media.view.ButtonGroup.prototype */{
  5187. tagName: 'div',
  5188. className: 'button-group button-large media-button-group',
  5189. initialize: function() {
  5190. /**
  5191. * @member {wp.media.view.Button[]}
  5192. */
  5193. this.buttons = _.map( this.options.buttons || [], function( button ) {
  5194. if ( button instanceof Backbone.View ) {
  5195. return button;
  5196. } else {
  5197. return new wp.media.view.Button( button ).render();
  5198. }
  5199. });
  5200. delete this.options.buttons;
  5201. if ( this.options.classes ) {
  5202. this.$el.addClass( this.options.classes );
  5203. }
  5204. },
  5205. /**
  5206. * @returns {wp.media.view.ButtonGroup}
  5207. */
  5208. render: function() {
  5209. this.$el.html( $( _.pluck( this.buttons, 'el' ) ).detach() );
  5210. return this;
  5211. }
  5212. });
  5213. module.exports = ButtonGroup;
  5214. /***/ }),
  5215. /* 67 */
  5216. /***/ (function(module, exports) {
  5217. /**
  5218. * wp.media.view.PriorityList
  5219. *
  5220. * @memberOf wp.media.view
  5221. *
  5222. * @class
  5223. * @augments wp.media.View
  5224. * @augments wp.Backbone.View
  5225. * @augments Backbone.View
  5226. */
  5227. var PriorityList = wp.media.View.extend(/** @lends wp.media.view.PriorityList.prototype */{
  5228. tagName: 'div',
  5229. initialize: function() {
  5230. this._views = {};
  5231. this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
  5232. delete this.options.views;
  5233. if ( ! this.options.silent ) {
  5234. this.render();
  5235. }
  5236. },
  5237. /**
  5238. * @param {string} id
  5239. * @param {wp.media.View|Object} view
  5240. * @param {Object} options
  5241. * @returns {wp.media.view.PriorityList} Returns itself to allow chaining
  5242. */
  5243. set: function( id, view, options ) {
  5244. var priority, views, index;
  5245. options = options || {};
  5246. // Accept an object with an `id` : `view` mapping.
  5247. if ( _.isObject( id ) ) {
  5248. _.each( id, function( view, id ) {
  5249. this.set( id, view );
  5250. }, this );
  5251. return this;
  5252. }
  5253. if ( ! (view instanceof Backbone.View) ) {
  5254. view = this.toView( view, id, options );
  5255. }
  5256. view.controller = view.controller || this.controller;
  5257. this.unset( id );
  5258. priority = view.options.priority || 10;
  5259. views = this.views.get() || [];
  5260. _.find( views, function( existing, i ) {
  5261. if ( existing.options.priority > priority ) {
  5262. index = i;
  5263. return true;
  5264. }
  5265. });
  5266. this._views[ id ] = view;
  5267. this.views.add( view, {
  5268. at: _.isNumber( index ) ? index : views.length || 0
  5269. });
  5270. return this;
  5271. },
  5272. /**
  5273. * @param {string} id
  5274. * @returns {wp.media.View}
  5275. */
  5276. get: function( id ) {
  5277. return this._views[ id ];
  5278. },
  5279. /**
  5280. * @param {string} id
  5281. * @returns {wp.media.view.PriorityList}
  5282. */
  5283. unset: function( id ) {
  5284. var view = this.get( id );
  5285. if ( view ) {
  5286. view.remove();
  5287. }
  5288. delete this._views[ id ];
  5289. return this;
  5290. },
  5291. /**
  5292. * @param {Object} options
  5293. * @returns {wp.media.View}
  5294. */
  5295. toView: function( options ) {
  5296. return new wp.media.View( options );
  5297. }
  5298. });
  5299. module.exports = PriorityList;
  5300. /***/ }),
  5301. /* 68 */
  5302. /***/ (function(module, exports) {
  5303. var MenuItem;
  5304. /**
  5305. * wp.media.view.MenuItem
  5306. *
  5307. * @memberOf wp.media.view
  5308. *
  5309. * @class
  5310. * @augments wp.media.View
  5311. * @augments wp.Backbone.View
  5312. * @augments Backbone.View
  5313. */
  5314. MenuItem = wp.media.View.extend(/** @lends wp.media.view.MenuItem.prototype */{
  5315. tagName: 'button',
  5316. className: 'media-menu-item',
  5317. attributes: {
  5318. type: 'button',
  5319. role: 'tab'
  5320. },
  5321. events: {
  5322. 'click': '_click'
  5323. },
  5324. /**
  5325. * Allows to override the click event.
  5326. */
  5327. _click: function() {
  5328. var clickOverride = this.options.click;
  5329. if ( clickOverride ) {
  5330. clickOverride.call( this );
  5331. } else {
  5332. this.click();
  5333. }
  5334. },
  5335. click: function() {
  5336. var state = this.options.state;
  5337. if ( state ) {
  5338. this.controller.setState( state );
  5339. // Toggle the menu visibility in the responsive view.
  5340. this.views.parent.$el.removeClass( 'visible' ); // TODO: or hide on any click, see below
  5341. }
  5342. },
  5343. /**
  5344. * @returns {wp.media.view.MenuItem} returns itself to allow chaining
  5345. */
  5346. render: function() {
  5347. var options = this.options,
  5348. menuProperty = options.state || options.contentMode;
  5349. if ( options.text ) {
  5350. this.$el.text( options.text );
  5351. } else if ( options.html ) {
  5352. this.$el.html( options.html );
  5353. }
  5354. // Set the menu item ID based on the frame state associated to the menu item.
  5355. this.$el.attr( 'id', 'menu-item-' + menuProperty );
  5356. return this;
  5357. }
  5358. });
  5359. module.exports = MenuItem;
  5360. /***/ }),
  5361. /* 69 */
  5362. /***/ (function(module, exports) {
  5363. var MenuItem = wp.media.view.MenuItem,
  5364. PriorityList = wp.media.view.PriorityList,
  5365. Menu;
  5366. /**
  5367. * wp.media.view.Menu
  5368. *
  5369. * @memberOf wp.media.view
  5370. *
  5371. * @class
  5372. * @augments wp.media.view.PriorityList
  5373. * @augments wp.media.View
  5374. * @augments wp.Backbone.View
  5375. * @augments Backbone.View
  5376. */
  5377. Menu = PriorityList.extend(/** @lends wp.media.view.Menu.prototype */{
  5378. tagName: 'div',
  5379. className: 'media-menu',
  5380. property: 'state',
  5381. ItemView: MenuItem,
  5382. region: 'menu',
  5383. attributes: {
  5384. role: 'tablist',
  5385. 'aria-orientation': 'horizontal'
  5386. },
  5387. initialize: function() {
  5388. this._views = {};
  5389. this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
  5390. delete this.options.views;
  5391. if ( ! this.options.silent ) {
  5392. this.render();
  5393. }
  5394. // Initialize the Focus Manager.
  5395. this.focusManager = new wp.media.view.FocusManager( {
  5396. el: this.el,
  5397. mode: 'tabsNavigation'
  5398. } );
  5399. // The menu is always rendered and can be visible or hidden on some frames.
  5400. this.isVisible = true;
  5401. },
  5402. /**
  5403. * @param {Object} options
  5404. * @param {string} id
  5405. * @returns {wp.media.View}
  5406. */
  5407. toView: function( options, id ) {
  5408. options = options || {};
  5409. options[ this.property ] = options[ this.property ] || id;
  5410. return new this.ItemView( options ).render();
  5411. },
  5412. ready: function() {
  5413. /**
  5414. * call 'ready' directly on the parent class
  5415. */
  5416. PriorityList.prototype.ready.apply( this, arguments );
  5417. this.visibility();
  5418. // Set up aria tabs initial attributes.
  5419. this.focusManager.setupAriaTabs();
  5420. },
  5421. set: function() {
  5422. /**
  5423. * call 'set' directly on the parent class
  5424. */
  5425. PriorityList.prototype.set.apply( this, arguments );
  5426. this.visibility();
  5427. },
  5428. unset: function() {
  5429. /**
  5430. * call 'unset' directly on the parent class
  5431. */
  5432. PriorityList.prototype.unset.apply( this, arguments );
  5433. this.visibility();
  5434. },
  5435. visibility: function() {
  5436. var region = this.region,
  5437. view = this.controller[ region ].get(),
  5438. views = this.views.get(),
  5439. hide = ! views || views.length < 2;
  5440. if ( this === view ) {
  5441. // Flag this menu as hidden or visible.
  5442. this.isVisible = ! hide;
  5443. // Set or remove a CSS class to hide the menu.
  5444. this.controller.$el.toggleClass( 'hide-' + region, hide );
  5445. }
  5446. },
  5447. /**
  5448. * @param {string} id
  5449. */
  5450. select: function( id ) {
  5451. var view = this.get( id );
  5452. if ( ! view ) {
  5453. return;
  5454. }
  5455. this.deselect();
  5456. view.$el.addClass('active');
  5457. // Set up again the aria tabs initial attributes after the menu updates.
  5458. this.focusManager.setupAriaTabs();
  5459. },
  5460. deselect: function() {
  5461. this.$el.children().removeClass('active');
  5462. },
  5463. hide: function( id ) {
  5464. var view = this.get( id );
  5465. if ( ! view ) {
  5466. return;
  5467. }
  5468. view.$el.addClass('hidden');
  5469. },
  5470. show: function( id ) {
  5471. var view = this.get( id );
  5472. if ( ! view ) {
  5473. return;
  5474. }
  5475. view.$el.removeClass('hidden');
  5476. }
  5477. });
  5478. module.exports = Menu;
  5479. /***/ }),
  5480. /* 70 */
  5481. /***/ (function(module, exports) {
  5482. /**
  5483. * wp.media.view.RouterItem
  5484. *
  5485. * @memberOf wp.media.view
  5486. *
  5487. * @class
  5488. * @augments wp.media.view.MenuItem
  5489. * @augments wp.media.View
  5490. * @augments wp.Backbone.View
  5491. * @augments Backbone.View
  5492. */
  5493. var RouterItem = wp.media.view.MenuItem.extend(/** @lends wp.media.view.RouterItem.prototype */{
  5494. /**
  5495. * On click handler to activate the content region's corresponding mode.
  5496. */
  5497. click: function() {
  5498. var contentMode = this.options.contentMode;
  5499. if ( contentMode ) {
  5500. this.controller.content.mode( contentMode );
  5501. }
  5502. }
  5503. });
  5504. module.exports = RouterItem;
  5505. /***/ }),
  5506. /* 71 */
  5507. /***/ (function(module, exports) {
  5508. var Menu = wp.media.view.Menu,
  5509. Router;
  5510. /**
  5511. * wp.media.view.Router
  5512. *
  5513. * @memberOf wp.media.view
  5514. *
  5515. * @class
  5516. * @augments wp.media.view.Menu
  5517. * @augments wp.media.view.PriorityList
  5518. * @augments wp.media.View
  5519. * @augments wp.Backbone.View
  5520. * @augments Backbone.View
  5521. */
  5522. Router = Menu.extend(/** @lends wp.media.view.Router.prototype */{
  5523. tagName: 'div',
  5524. className: 'media-router',
  5525. property: 'contentMode',
  5526. ItemView: wp.media.view.RouterItem,
  5527. region: 'router',
  5528. attributes: {
  5529. role: 'tablist',
  5530. 'aria-orientation': 'horizontal'
  5531. },
  5532. initialize: function() {
  5533. this.controller.on( 'content:render', this.update, this );
  5534. // Call 'initialize' directly on the parent class.
  5535. Menu.prototype.initialize.apply( this, arguments );
  5536. },
  5537. update: function() {
  5538. var mode = this.controller.content.mode();
  5539. if ( mode ) {
  5540. this.select( mode );
  5541. }
  5542. }
  5543. });
  5544. module.exports = Router;
  5545. /***/ }),
  5546. /* 72 */
  5547. /***/ (function(module, exports) {
  5548. /**
  5549. * wp.media.view.Sidebar
  5550. *
  5551. * @memberOf wp.media.view
  5552. *
  5553. * @class
  5554. * @augments wp.media.view.PriorityList
  5555. * @augments wp.media.View
  5556. * @augments wp.Backbone.View
  5557. * @augments Backbone.View
  5558. */
  5559. var Sidebar = wp.media.view.PriorityList.extend(/** @lends wp.media.view.Sidebar.prototype */{
  5560. className: 'media-sidebar'
  5561. });
  5562. module.exports = Sidebar;
  5563. /***/ }),
  5564. /* 73 */
  5565. /***/ (function(module, exports) {
  5566. var View = wp.media.View,
  5567. $ = jQuery,
  5568. Attachment;
  5569. /**
  5570. * wp.media.view.Attachment
  5571. *
  5572. * @memberOf wp.media.view
  5573. *
  5574. * @class
  5575. * @augments wp.media.View
  5576. * @augments wp.Backbone.View
  5577. * @augments Backbone.View
  5578. */
  5579. Attachment = View.extend(/** @lends wp.media.view.Attachment.prototype */{
  5580. tagName: 'li',
  5581. className: 'attachment',
  5582. template: wp.template('attachment'),
  5583. attributes: function() {
  5584. return {
  5585. 'tabIndex': 0,
  5586. 'role': 'checkbox',
  5587. 'aria-label': this.model.get( 'title' ),
  5588. 'aria-checked': false,
  5589. 'data-id': this.model.get( 'id' )
  5590. };
  5591. },
  5592. events: {
  5593. 'click': 'toggleSelectionHandler',
  5594. 'change [data-setting]': 'updateSetting',
  5595. 'change [data-setting] input': 'updateSetting',
  5596. 'change [data-setting] select': 'updateSetting',
  5597. 'change [data-setting] textarea': 'updateSetting',
  5598. 'click .attachment-close': 'removeFromLibrary',
  5599. 'click .check': 'checkClickHandler',
  5600. 'keydown': 'toggleSelectionHandler'
  5601. },
  5602. buttons: {},
  5603. initialize: function() {
  5604. var selection = this.options.selection,
  5605. options = _.defaults( this.options, {
  5606. rerenderOnModelChange: true
  5607. } );
  5608. if ( options.rerenderOnModelChange ) {
  5609. this.listenTo( this.model, 'change', this.render );
  5610. } else {
  5611. this.listenTo( this.model, 'change:percent', this.progress );
  5612. }
  5613. this.listenTo( this.model, 'change:title', this._syncTitle );
  5614. this.listenTo( this.model, 'change:caption', this._syncCaption );
  5615. this.listenTo( this.model, 'change:artist', this._syncArtist );
  5616. this.listenTo( this.model, 'change:album', this._syncAlbum );
  5617. // Update the selection.
  5618. this.listenTo( this.model, 'add', this.select );
  5619. this.listenTo( this.model, 'remove', this.deselect );
  5620. if ( selection ) {
  5621. selection.on( 'reset', this.updateSelect, this );
  5622. // Update the model's details view.
  5623. this.listenTo( this.model, 'selection:single selection:unsingle', this.details );
  5624. this.details( this.model, this.controller.state().get('selection') );
  5625. }
  5626. this.listenTo( this.controller.states, 'attachment:compat:waiting attachment:compat:ready', this.updateSave );
  5627. },
  5628. /**
  5629. * @returns {wp.media.view.Attachment} Returns itself to allow chaining
  5630. */
  5631. dispose: function() {
  5632. var selection = this.options.selection;
  5633. // Make sure all settings are saved before removing the view.
  5634. this.updateAll();
  5635. if ( selection ) {
  5636. selection.off( null, null, this );
  5637. }
  5638. /**
  5639. * call 'dispose' directly on the parent class
  5640. */
  5641. View.prototype.dispose.apply( this, arguments );
  5642. return this;
  5643. },
  5644. /**
  5645. * @returns {wp.media.view.Attachment} Returns itself to allow chaining
  5646. */
  5647. render: function() {
  5648. var options = _.defaults( this.model.toJSON(), {
  5649. orientation: 'landscape',
  5650. uploading: false,
  5651. type: '',
  5652. subtype: '',
  5653. icon: '',
  5654. filename: '',
  5655. caption: '',
  5656. title: '',
  5657. dateFormatted: '',
  5658. width: '',
  5659. height: '',
  5660. compat: false,
  5661. alt: '',
  5662. description: ''
  5663. }, this.options );
  5664. options.buttons = this.buttons;
  5665. options.describe = this.controller.state().get('describe');
  5666. if ( 'image' === options.type ) {
  5667. options.size = this.imageSize();
  5668. }
  5669. options.can = {};
  5670. if ( options.nonces ) {
  5671. options.can.remove = !! options.nonces['delete'];
  5672. options.can.save = !! options.nonces.update;
  5673. }
  5674. if ( this.controller.state().get('allowLocalEdits') ) {
  5675. options.allowLocalEdits = true;
  5676. }
  5677. if ( options.uploading && ! options.percent ) {
  5678. options.percent = 0;
  5679. }
  5680. this.views.detach();
  5681. this.$el.html( this.template( options ) );
  5682. this.$el.toggleClass( 'uploading', options.uploading );
  5683. if ( options.uploading ) {
  5684. this.$bar = this.$('.media-progress-bar div');
  5685. } else {
  5686. delete this.$bar;
  5687. }
  5688. // Check if the model is selected.
  5689. this.updateSelect();
  5690. // Update the save status.
  5691. this.updateSave();
  5692. this.views.render();
  5693. return this;
  5694. },
  5695. progress: function() {
  5696. if ( this.$bar && this.$bar.length ) {
  5697. this.$bar.width( this.model.get('percent') + '%' );
  5698. }
  5699. },
  5700. /**
  5701. * @param {Object} event
  5702. */
  5703. toggleSelectionHandler: function( event ) {
  5704. var method;
  5705. // Don't do anything inside inputs and on the attachment check and remove buttons.
  5706. if ( 'INPUT' === event.target.nodeName || 'BUTTON' === event.target.nodeName ) {
  5707. return;
  5708. }
  5709. // Catch arrow events
  5710. if ( 37 === event.keyCode || 38 === event.keyCode || 39 === event.keyCode || 40 === event.keyCode ) {
  5711. this.controller.trigger( 'attachment:keydown:arrow', event );
  5712. return;
  5713. }
  5714. // Catch enter and space events
  5715. if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
  5716. return;
  5717. }
  5718. event.preventDefault();
  5719. // In the grid view, bubble up an edit:attachment event to the controller.
  5720. if ( this.controller.isModeActive( 'grid' ) ) {
  5721. if ( this.controller.isModeActive( 'edit' ) ) {
  5722. // Pass the current target to restore focus when closing
  5723. this.controller.trigger( 'edit:attachment', this.model, event.currentTarget );
  5724. return;
  5725. }
  5726. if ( this.controller.isModeActive( 'select' ) ) {
  5727. method = 'toggle';
  5728. }
  5729. }
  5730. if ( event.shiftKey ) {
  5731. method = 'between';
  5732. } else if ( event.ctrlKey || event.metaKey ) {
  5733. method = 'toggle';
  5734. }
  5735. this.toggleSelection({
  5736. method: method
  5737. });
  5738. this.controller.trigger( 'selection:toggle' );
  5739. },
  5740. /**
  5741. * @param {Object} options
  5742. */
  5743. toggleSelection: function( options ) {
  5744. var collection = this.collection,
  5745. selection = this.options.selection,
  5746. model = this.model,
  5747. method = options && options.method,
  5748. single, models, singleIndex, modelIndex;
  5749. if ( ! selection ) {
  5750. return;
  5751. }
  5752. single = selection.single();
  5753. method = _.isUndefined( method ) ? selection.multiple : method;
  5754. // If the `method` is set to `between`, select all models that
  5755. // exist between the current and the selected model.
  5756. if ( 'between' === method && single && selection.multiple ) {
  5757. // If the models are the same, short-circuit.
  5758. if ( single === model ) {
  5759. return;
  5760. }
  5761. singleIndex = collection.indexOf( single );
  5762. modelIndex = collection.indexOf( this.model );
  5763. if ( singleIndex < modelIndex ) {
  5764. models = collection.models.slice( singleIndex, modelIndex + 1 );
  5765. } else {
  5766. models = collection.models.slice( modelIndex, singleIndex + 1 );
  5767. }
  5768. selection.add( models );
  5769. selection.single( model );
  5770. return;
  5771. // If the `method` is set to `toggle`, just flip the selection
  5772. // status, regardless of whether the model is the single model.
  5773. } else if ( 'toggle' === method ) {
  5774. selection[ this.selected() ? 'remove' : 'add' ]( model );
  5775. selection.single( model );
  5776. return;
  5777. } else if ( 'add' === method ) {
  5778. selection.add( model );
  5779. selection.single( model );
  5780. return;
  5781. }
  5782. // Fixes bug that loses focus when selecting a featured image
  5783. if ( ! method ) {
  5784. method = 'add';
  5785. }
  5786. if ( method !== 'add' ) {
  5787. method = 'reset';
  5788. }
  5789. if ( this.selected() ) {
  5790. // If the model is the single model, remove it.
  5791. // If it is not the same as the single model,
  5792. // it now becomes the single model.
  5793. selection[ single === model ? 'remove' : 'single' ]( model );
  5794. } else {
  5795. // If the model is not selected, run the `method` on the
  5796. // selection. By default, we `reset` the selection, but the
  5797. // `method` can be set to `add` the model to the selection.
  5798. selection[ method ]( model );
  5799. selection.single( model );
  5800. }
  5801. },
  5802. updateSelect: function() {
  5803. this[ this.selected() ? 'select' : 'deselect' ]();
  5804. },
  5805. /**
  5806. * @returns {unresolved|Boolean}
  5807. */
  5808. selected: function() {
  5809. var selection = this.options.selection;
  5810. if ( selection ) {
  5811. return !! selection.get( this.model.cid );
  5812. }
  5813. },
  5814. /**
  5815. * @param {Backbone.Model} model
  5816. * @param {Backbone.Collection} collection
  5817. */
  5818. select: function( model, collection ) {
  5819. var selection = this.options.selection,
  5820. controller = this.controller;
  5821. // Check if a selection exists and if it's the collection provided.
  5822. // If they're not the same collection, bail; we're in another
  5823. // selection's event loop.
  5824. if ( ! selection || ( collection && collection !== selection ) ) {
  5825. return;
  5826. }
  5827. // Bail if the model is already selected.
  5828. if ( this.$el.hasClass( 'selected' ) ) {
  5829. return;
  5830. }
  5831. // Add 'selected' class to model, set aria-checked to true.
  5832. this.$el.addClass( 'selected' ).attr( 'aria-checked', true );
  5833. // Make the checkbox tabable, except in media grid (bulk select mode).
  5834. if ( ! ( controller.isModeActive( 'grid' ) && controller.isModeActive( 'select' ) ) ) {
  5835. this.$( '.check' ).attr( 'tabindex', '0' );
  5836. }
  5837. },
  5838. /**
  5839. * @param {Backbone.Model} model
  5840. * @param {Backbone.Collection} collection
  5841. */
  5842. deselect: function( model, collection ) {
  5843. var selection = this.options.selection;
  5844. // Check if a selection exists and if it's the collection provided.
  5845. // If they're not the same collection, bail; we're in another
  5846. // selection's event loop.
  5847. if ( ! selection || ( collection && collection !== selection ) ) {
  5848. return;
  5849. }
  5850. this.$el.removeClass( 'selected' ).attr( 'aria-checked', false )
  5851. .find( '.check' ).attr( 'tabindex', '-1' );
  5852. },
  5853. /**
  5854. * @param {Backbone.Model} model
  5855. * @param {Backbone.Collection} collection
  5856. */
  5857. details: function( model, collection ) {
  5858. var selection = this.options.selection,
  5859. details;
  5860. if ( selection !== collection ) {
  5861. return;
  5862. }
  5863. details = selection.single();
  5864. this.$el.toggleClass( 'details', details === this.model );
  5865. },
  5866. /**
  5867. * @param {string} size
  5868. * @returns {Object}
  5869. */
  5870. imageSize: function( size ) {
  5871. var sizes = this.model.get('sizes'), matched = false;
  5872. size = size || 'medium';
  5873. // Use the provided image size if possible.
  5874. if ( sizes ) {
  5875. if ( sizes[ size ] ) {
  5876. matched = sizes[ size ];
  5877. } else if ( sizes.large ) {
  5878. matched = sizes.large;
  5879. } else if ( sizes.thumbnail ) {
  5880. matched = sizes.thumbnail;
  5881. } else if ( sizes.full ) {
  5882. matched = sizes.full;
  5883. }
  5884. if ( matched ) {
  5885. return _.clone( matched );
  5886. }
  5887. }
  5888. return {
  5889. url: this.model.get('url'),
  5890. width: this.model.get('width'),
  5891. height: this.model.get('height'),
  5892. orientation: this.model.get('orientation')
  5893. };
  5894. },
  5895. /**
  5896. * @param {Object} event
  5897. */
  5898. updateSetting: function( event ) {
  5899. var $setting = $( event.target ).closest('[data-setting]'),
  5900. setting, value;
  5901. if ( ! $setting.length ) {
  5902. return;
  5903. }
  5904. setting = $setting.data('setting');
  5905. value = event.target.value;
  5906. if ( this.model.get( setting ) !== value ) {
  5907. this.save( setting, value );
  5908. }
  5909. },
  5910. /**
  5911. * Pass all the arguments to the model's save method.
  5912. *
  5913. * Records the aggregate status of all save requests and updates the
  5914. * view's classes accordingly.
  5915. */
  5916. save: function() {
  5917. var view = this,
  5918. save = this._save = this._save || { status: 'ready' },
  5919. request = this.model.save.apply( this.model, arguments ),
  5920. requests = save.requests ? $.when( request, save.requests ) : request;
  5921. // If we're waiting to remove 'Saved.', stop.
  5922. if ( save.savedTimer ) {
  5923. clearTimeout( save.savedTimer );
  5924. }
  5925. this.updateSave('waiting');
  5926. save.requests = requests;
  5927. requests.always( function() {
  5928. // If we've performed another request since this one, bail.
  5929. if ( save.requests !== requests ) {
  5930. return;
  5931. }
  5932. view.updateSave( requests.state() === 'resolved' ? 'complete' : 'error' );
  5933. save.savedTimer = setTimeout( function() {
  5934. view.updateSave('ready');
  5935. delete save.savedTimer;
  5936. }, 2000 );
  5937. });
  5938. },
  5939. /**
  5940. * @param {string} status
  5941. * @returns {wp.media.view.Attachment} Returns itself to allow chaining
  5942. */
  5943. updateSave: function( status ) {
  5944. var save = this._save = this._save || { status: 'ready' };
  5945. if ( status && status !== save.status ) {
  5946. this.$el.removeClass( 'save-' + save.status );
  5947. save.status = status;
  5948. }
  5949. this.$el.addClass( 'save-' + save.status );
  5950. return this;
  5951. },
  5952. updateAll: function() {
  5953. var $settings = this.$('[data-setting]'),
  5954. model = this.model,
  5955. changed;
  5956. changed = _.chain( $settings ).map( function( el ) {
  5957. var $input = $('input, textarea, select, [value]', el ),
  5958. setting, value;
  5959. if ( ! $input.length ) {
  5960. return;
  5961. }
  5962. setting = $(el).data('setting');
  5963. value = $input.val();
  5964. // Record the value if it changed.
  5965. if ( model.get( setting ) !== value ) {
  5966. return [ setting, value ];
  5967. }
  5968. }).compact().object().value();
  5969. if ( ! _.isEmpty( changed ) ) {
  5970. model.save( changed );
  5971. }
  5972. },
  5973. /**
  5974. * @param {Object} event
  5975. */
  5976. removeFromLibrary: function( event ) {
  5977. // Catch enter and space events
  5978. if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
  5979. return;
  5980. }
  5981. // Stop propagation so the model isn't selected.
  5982. event.stopPropagation();
  5983. this.collection.remove( this.model );
  5984. },
  5985. /**
  5986. * Add the model if it isn't in the selection, if it is in the selection,
  5987. * remove it.
  5988. *
  5989. * @param {[type]} event [description]
  5990. * @return {[type]} [description]
  5991. */
  5992. checkClickHandler: function ( event ) {
  5993. var selection = this.options.selection;
  5994. if ( ! selection ) {
  5995. return;
  5996. }
  5997. event.stopPropagation();
  5998. if ( selection.where( { id: this.model.get( 'id' ) } ).length ) {
  5999. selection.remove( this.model );
  6000. // Move focus back to the attachment tile (from the check).
  6001. this.$el.focus();
  6002. } else {
  6003. selection.add( this.model );
  6004. }
  6005. // Trigger an action button update.
  6006. this.controller.trigger( 'selection:toggle' );
  6007. }
  6008. });
  6009. // Ensure settings remain in sync between attachment views.
  6010. _.each({
  6011. caption: '_syncCaption',
  6012. title: '_syncTitle',
  6013. artist: '_syncArtist',
  6014. album: '_syncAlbum'
  6015. }, function( method, setting ) {
  6016. /**
  6017. * @function _syncCaption
  6018. * @memberOf wp.media.view.Attachment
  6019. * @instance
  6020. *
  6021. * @param {Backbone.Model} model
  6022. * @param {string} value
  6023. * @returns {wp.media.view.Attachment} Returns itself to allow chaining
  6024. */
  6025. /**
  6026. * @function _syncTitle
  6027. * @memberOf wp.media.view.Attachment
  6028. * @instance
  6029. *
  6030. * @param {Backbone.Model} model
  6031. * @param {string} value
  6032. * @returns {wp.media.view.Attachment} Returns itself to allow chaining
  6033. */
  6034. /**
  6035. * @function _syncArtist
  6036. * @memberOf wp.media.view.Attachment
  6037. * @instance
  6038. *
  6039. * @param {Backbone.Model} model
  6040. * @param {string} value
  6041. * @returns {wp.media.view.Attachment} Returns itself to allow chaining
  6042. */
  6043. /**
  6044. * @function _syncAlbum
  6045. * @memberOf wp.media.view.Attachment
  6046. * @instance
  6047. *
  6048. * @param {Backbone.Model} model
  6049. * @param {string} value
  6050. * @returns {wp.media.view.Attachment} Returns itself to allow chaining
  6051. */
  6052. Attachment.prototype[ method ] = function( model, value ) {
  6053. var $setting = this.$('[data-setting="' + setting + '"]');
  6054. if ( ! $setting.length ) {
  6055. return this;
  6056. }
  6057. // If the updated value is in sync with the value in the DOM, there
  6058. // is no need to re-render. If we're currently editing the value,
  6059. // it will automatically be in sync, suppressing the re-render for
  6060. // the view we're editing, while updating any others.
  6061. if ( value === $setting.find('input, textarea, select, [value]').val() ) {
  6062. return this;
  6063. }
  6064. return this.render();
  6065. };
  6066. });
  6067. module.exports = Attachment;
  6068. /***/ }),
  6069. /* 74 */
  6070. /***/ (function(module, exports) {
  6071. /**
  6072. * wp.media.view.Attachment.Library
  6073. *
  6074. * @memberOf wp.media.view.Attachment
  6075. *
  6076. * @class
  6077. * @augments wp.media.view.Attachment
  6078. * @augments wp.media.View
  6079. * @augments wp.Backbone.View
  6080. * @augments Backbone.View
  6081. */
  6082. var Library = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.Library.prototype */{
  6083. buttons: {
  6084. check: true
  6085. }
  6086. });
  6087. module.exports = Library;
  6088. /***/ }),
  6089. /* 75 */
  6090. /***/ (function(module, exports) {
  6091. /**
  6092. * wp.media.view.Attachment.EditLibrary
  6093. *
  6094. * @memberOf wp.media.view.Attachment
  6095. *
  6096. * @class
  6097. * @augments wp.media.view.Attachment
  6098. * @augments wp.media.View
  6099. * @augments wp.Backbone.View
  6100. * @augments Backbone.View
  6101. */
  6102. var EditLibrary = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.EditLibrary.prototype */{
  6103. buttons: {
  6104. close: true
  6105. }
  6106. });
  6107. module.exports = EditLibrary;
  6108. /***/ }),
  6109. /* 76 */
  6110. /***/ (function(module, exports) {
  6111. var View = wp.media.View,
  6112. $ = jQuery,
  6113. Attachments;
  6114. Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{
  6115. tagName: 'ul',
  6116. className: 'attachments',
  6117. attributes: {
  6118. tabIndex: -1
  6119. },
  6120. /**
  6121. * Represents the overview of attachments in the Media Library.
  6122. *
  6123. * The constructor binds events to the collection this view represents when
  6124. * adding or removing attachments or resetting the entire collection.
  6125. *
  6126. * @since 3.5.0
  6127. *
  6128. * @constructs
  6129. * @memberof wp.media.view
  6130. *
  6131. * @augments wp.media.View
  6132. *
  6133. * @listens collection:add
  6134. * @listens collection:remove
  6135. * @listens collection:reset
  6136. * @listens controller:library:selection:add
  6137. * @listens scrollElement:scroll
  6138. * @listens this:ready
  6139. * @listens controller:open
  6140. */
  6141. initialize: function() {
  6142. this.el.id = _.uniqueId('__attachments-view-');
  6143. /**
  6144. * @param refreshSensitivity The time in milliseconds to throttle the scroll
  6145. * handler.
  6146. * @param refreshThreshold The amount of pixels that should be scrolled before
  6147. * loading more attachments from the server.
  6148. * @param AttachmentView The view class to be used for models in the
  6149. * collection.
  6150. * @param sortable A jQuery sortable options object
  6151. * ( http://api.jqueryui.com/sortable/ ).
  6152. * @param resize A boolean indicating whether or not to listen to
  6153. * resize events.
  6154. * @param idealColumnWidth The width in pixels which a column should have when
  6155. * calculating the total number of columns.
  6156. */
  6157. _.defaults( this.options, {
  6158. refreshSensitivity: wp.media.isTouchDevice ? 300 : 200,
  6159. refreshThreshold: 3,
  6160. AttachmentView: wp.media.view.Attachment,
  6161. sortable: false,
  6162. resize: true,
  6163. idealColumnWidth: $( window ).width() < 640 ? 135 : 150
  6164. });
  6165. this._viewsByCid = {};
  6166. this.$window = $( window );
  6167. this.resizeEvent = 'resize.media-modal-columns';
  6168. this.collection.on( 'add', function( attachment ) {
  6169. this.views.add( this.createAttachmentView( attachment ), {
  6170. at: this.collection.indexOf( attachment )
  6171. });
  6172. }, this );
  6173. /*
  6174. * Find the view to be removed, delete it and call the remove function to clear
  6175. * any set event handlers.
  6176. */
  6177. this.collection.on( 'remove', function( attachment ) {
  6178. var view = this._viewsByCid[ attachment.cid ];
  6179. delete this._viewsByCid[ attachment.cid ];
  6180. if ( view ) {
  6181. view.remove();
  6182. }
  6183. }, this );
  6184. this.collection.on( 'reset', this.render, this );
  6185. this.controller.on( 'library:selection:add', this.attachmentFocus, this );
  6186. // Throttle the scroll handler and bind this.
  6187. this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value();
  6188. this.options.scrollElement = this.options.scrollElement || this.el;
  6189. $( this.options.scrollElement ).on( 'scroll', this.scroll );
  6190. this.initSortable();
  6191. _.bindAll( this, 'setColumns' );
  6192. if ( this.options.resize ) {
  6193. this.on( 'ready', this.bindEvents );
  6194. this.controller.on( 'open', this.setColumns );
  6195. /*
  6196. * Call this.setColumns() after this view has been rendered in the
  6197. * DOM so attachments get proper width applied.
  6198. */
  6199. _.defer( this.setColumns, this );
  6200. }
  6201. },
  6202. /**
  6203. * Listens to the resizeEvent on the window.
  6204. *
  6205. * Adjusts the amount of columns accordingly. First removes any existing event
  6206. * handlers to prevent duplicate listeners.
  6207. *
  6208. * @since 4.0.0
  6209. *
  6210. * @listens window:resize
  6211. *
  6212. * @returns {void}
  6213. */
  6214. bindEvents: function() {
  6215. this.$window.off( this.resizeEvent ).on( this.resizeEvent, _.debounce( this.setColumns, 50 ) );
  6216. },
  6217. /**
  6218. * Focuses the first item in the collection.
  6219. *
  6220. * @since 4.0.0
  6221. *
  6222. * @returns {void}
  6223. */
  6224. attachmentFocus: function() {
  6225. /*
  6226. * @todo: when uploading new attachments, this tries to move focus to the
  6227. * attachmentz grid. Actually, a progress bar gets initially displayed
  6228. * and then updated when uploading completes, so focus is lost.
  6229. * Additionally: this view is used for both the attachments list and the
  6230. * list of selected attachments in the bottom media toolbar. Thus, when
  6231. * uploading attachments, it is called twice and returns two different `this`.
  6232. * `this.columns` is truthy within the modal.
  6233. */
  6234. if ( this.columns ) {
  6235. // Move focus to the grid list within the modal.
  6236. this.$el.focus();
  6237. }
  6238. },
  6239. /**
  6240. * Restores focus to the selected item in the collection.
  6241. *
  6242. * Moves focus back to the first selected attachment in the grid. Used when
  6243. * tabbing backwards from the attachment details sidebar.
  6244. * See media.view.AttachmentsBrowser.
  6245. *
  6246. * @since 4.0.0
  6247. *
  6248. * @returns {void}
  6249. */
  6250. restoreFocus: function() {
  6251. this.$( 'li.selected:first' ).focus();
  6252. },
  6253. /**
  6254. * Handles events for arrow key presses.
  6255. *
  6256. * Focuses the attachment in the direction of the used arrow key if it exists.
  6257. *
  6258. * @since 4.0.0
  6259. *
  6260. * @param {KeyboardEvent} event The keyboard event that triggered this function.
  6261. *
  6262. * @returns {void}
  6263. */
  6264. arrowEvent: function( event ) {
  6265. var attachments = this.$el.children( 'li' ),
  6266. perRow = this.columns,
  6267. index = attachments.filter( ':focus' ).index(),
  6268. row = ( index + 1 ) <= perRow ? 1 : Math.ceil( ( index + 1 ) / perRow );
  6269. if ( index === -1 ) {
  6270. return;
  6271. }
  6272. // Left arrow = 37.
  6273. if ( 37 === event.keyCode ) {
  6274. if ( 0 === index ) {
  6275. return;
  6276. }
  6277. attachments.eq( index - 1 ).focus();
  6278. }
  6279. // Up arrow = 38.
  6280. if ( 38 === event.keyCode ) {
  6281. if ( 1 === row ) {
  6282. return;
  6283. }
  6284. attachments.eq( index - perRow ).focus();
  6285. }
  6286. // Right arrow = 39.
  6287. if ( 39 === event.keyCode ) {
  6288. if ( attachments.length === index ) {
  6289. return;
  6290. }
  6291. attachments.eq( index + 1 ).focus();
  6292. }
  6293. // Down arrow = 40.
  6294. if ( 40 === event.keyCode ) {
  6295. if ( Math.ceil( attachments.length / perRow ) === row ) {
  6296. return;
  6297. }
  6298. attachments.eq( index + perRow ).focus();
  6299. }
  6300. },
  6301. /**
  6302. * Clears any set event handlers.
  6303. *
  6304. * @since 3.5.0
  6305. *
  6306. * @returns {void}
  6307. */
  6308. dispose: function() {
  6309. this.collection.props.off( null, null, this );
  6310. if ( this.options.resize ) {
  6311. this.$window.off( this.resizeEvent );
  6312. }
  6313. // Call 'dispose' directly on the parent class.
  6314. View.prototype.dispose.apply( this, arguments );
  6315. },
  6316. /**
  6317. * Calculates the amount of columns.
  6318. *
  6319. * Calculates the amount of columns and sets it on the data-columns attribute
  6320. * of .media-frame-content.
  6321. *
  6322. * @since 4.0.0
  6323. *
  6324. * @returns {void}
  6325. */
  6326. setColumns: function() {
  6327. var prev = this.columns,
  6328. width = this.$el.width();
  6329. if ( width ) {
  6330. this.columns = Math.min( Math.round( width / this.options.idealColumnWidth ), 12 ) || 1;
  6331. if ( ! prev || prev !== this.columns ) {
  6332. this.$el.closest( '.media-frame-content' ).attr( 'data-columns', this.columns );
  6333. }
  6334. }
  6335. },
  6336. /**
  6337. * Initializes jQuery sortable on the attachment list.
  6338. *
  6339. * Fails gracefully if jQuery sortable doesn't exist or isn't passed in the
  6340. * options.
  6341. *
  6342. * @since 3.5.0
  6343. *
  6344. * @fires collection:reset
  6345. *
  6346. * @returns {void}
  6347. */
  6348. initSortable: function() {
  6349. var collection = this.collection;
  6350. if ( ! this.options.sortable || ! $.fn.sortable ) {
  6351. return;
  6352. }
  6353. this.$el.sortable( _.extend({
  6354. // If the `collection` has a `comparator`, disable sorting.
  6355. disabled: !! collection.comparator,
  6356. /*
  6357. * Change the position of the attachment as soon as the mouse pointer overlaps a
  6358. * thumbnail.
  6359. */
  6360. tolerance: 'pointer',
  6361. // Record the initial `index` of the dragged model.
  6362. start: function( event, ui ) {
  6363. ui.item.data('sortableIndexStart', ui.item.index());
  6364. },
  6365. /*
  6366. * Update the model's index in the collection. Do so silently, as the view
  6367. * is already accurate.
  6368. */
  6369. update: function( event, ui ) {
  6370. var model = collection.at( ui.item.data('sortableIndexStart') ),
  6371. comparator = collection.comparator;
  6372. // Temporarily disable the comparator to prevent `add`
  6373. // from re-sorting.
  6374. delete collection.comparator;
  6375. // Silently shift the model to its new index.
  6376. collection.remove( model, {
  6377. silent: true
  6378. });
  6379. collection.add( model, {
  6380. silent: true,
  6381. at: ui.item.index()
  6382. });
  6383. // Restore the comparator.
  6384. collection.comparator = comparator;
  6385. // Fire the `reset` event to ensure other collections sync.
  6386. collection.trigger( 'reset', collection );
  6387. // If the collection is sorted by menu order, update the menu order.
  6388. collection.saveMenuOrder();
  6389. }
  6390. }, this.options.sortable ) );
  6391. /*
  6392. * If the `orderby` property is changed on the `collection`, check to see if we
  6393. * have a `comparator`. If so, disable sorting.
  6394. */
  6395. collection.props.on( 'change:orderby', function() {
  6396. this.$el.sortable( 'option', 'disabled', !! collection.comparator );
  6397. }, this );
  6398. this.collection.props.on( 'change:orderby', this.refreshSortable, this );
  6399. this.refreshSortable();
  6400. },
  6401. /**
  6402. * Disables jQuery sortable if collection has a comparator or collection.orderby
  6403. * equals menuOrder.
  6404. *
  6405. * @since 3.5.0
  6406. *
  6407. * @returns {void}
  6408. */
  6409. refreshSortable: function() {
  6410. if ( ! this.options.sortable || ! $.fn.sortable ) {
  6411. return;
  6412. }
  6413. var collection = this.collection,
  6414. orderby = collection.props.get('orderby'),
  6415. enabled = 'menuOrder' === orderby || ! collection.comparator;
  6416. this.$el.sortable( 'option', 'disabled', ! enabled );
  6417. },
  6418. /**
  6419. * Creates a new view for an attachment and adds it to _viewsByCid.
  6420. *
  6421. * @since 3.5.0
  6422. *
  6423. * @param {wp.media.model.Attachment} attachment
  6424. *
  6425. * @returns {wp.media.View} The created view.
  6426. */
  6427. createAttachmentView: function( attachment ) {
  6428. var view = new this.options.AttachmentView({
  6429. controller: this.controller,
  6430. model: attachment,
  6431. collection: this.collection,
  6432. selection: this.options.selection
  6433. });
  6434. return this._viewsByCid[ attachment.cid ] = view;
  6435. },
  6436. /**
  6437. * Prepares view for display.
  6438. *
  6439. * Creates views for every attachment in collection if the collection is not
  6440. * empty, otherwise clears all views and loads more attachments.
  6441. *
  6442. * @since 3.5.0
  6443. *
  6444. * @returns {void}
  6445. */
  6446. prepare: function() {
  6447. if ( this.collection.length ) {
  6448. this.views.set( this.collection.map( this.createAttachmentView, this ) );
  6449. } else {
  6450. this.views.unset();
  6451. this.collection.more().done( this.scroll );
  6452. }
  6453. },
  6454. /**
  6455. * Triggers the scroll function to check if we should query for additional
  6456. * attachments right away.
  6457. *
  6458. * @since 3.5.0
  6459. *
  6460. * @returns {void}
  6461. */
  6462. ready: function() {
  6463. this.scroll();
  6464. },
  6465. /**
  6466. * Handles scroll events.
  6467. *
  6468. * Shows the spinner if we're close to the bottom. Loads more attachments from
  6469. * server if we're {refreshThreshold} times away from the bottom.
  6470. *
  6471. * @since 3.5.0
  6472. *
  6473. * @returns {void}
  6474. */
  6475. scroll: function() {
  6476. var view = this,
  6477. el = this.options.scrollElement,
  6478. scrollTop = el.scrollTop,
  6479. toolbar;
  6480. /*
  6481. * The scroll event occurs on the document, but the element that should be
  6482. * checked is the document body.
  6483. */
  6484. if ( el === document ) {
  6485. el = document.body;
  6486. scrollTop = $(document).scrollTop();
  6487. }
  6488. if ( ! $(el).is(':visible') || ! this.collection.hasMore() ) {
  6489. return;
  6490. }
  6491. toolbar = this.views.parent.toolbar;
  6492. // Show the spinner only if we are close to the bottom.
  6493. if ( el.scrollHeight - ( scrollTop + el.clientHeight ) < el.clientHeight / 3 ) {
  6494. toolbar.get('spinner').show();
  6495. }
  6496. if ( el.scrollHeight < scrollTop + ( el.clientHeight * this.options.refreshThreshold ) ) {
  6497. this.collection.more().done(function() {
  6498. view.scroll();
  6499. toolbar.get('spinner').hide();
  6500. });
  6501. }
  6502. }
  6503. });
  6504. module.exports = Attachments;
  6505. /***/ }),
  6506. /* 77 */
  6507. /***/ (function(module, exports) {
  6508. var Search;
  6509. /**
  6510. * wp.media.view.Search
  6511. *
  6512. * @memberOf wp.media.view
  6513. *
  6514. * @class
  6515. * @augments wp.media.View
  6516. * @augments wp.Backbone.View
  6517. * @augments Backbone.View
  6518. */
  6519. Search = wp.media.View.extend(/** @lends wp.media.view.Search.prototype */{
  6520. tagName: 'input',
  6521. className: 'search',
  6522. id: 'media-search-input',
  6523. attributes: {
  6524. type: 'search'
  6525. },
  6526. events: {
  6527. 'input': 'search'
  6528. },
  6529. /**
  6530. * @returns {wp.media.view.Search} Returns itself to allow chaining
  6531. */
  6532. render: function() {
  6533. this.el.value = this.model.escape('search');
  6534. return this;
  6535. },
  6536. search: _.debounce( function( event ) {
  6537. var searchTerm = event.target.value.trim();
  6538. // Trigger the search only after 2 ASCII characters.
  6539. if ( searchTerm && searchTerm.length > 1 ) {
  6540. this.model.set( 'search', searchTerm );
  6541. } else {
  6542. this.model.unset( 'search' );
  6543. }
  6544. }, 500 )
  6545. });
  6546. module.exports = Search;
  6547. /***/ }),
  6548. /* 78 */
  6549. /***/ (function(module, exports) {
  6550. var $ = jQuery,
  6551. AttachmentFilters;
  6552. /**
  6553. * wp.media.view.AttachmentFilters
  6554. *
  6555. * @memberOf wp.media.view
  6556. *
  6557. * @class
  6558. * @augments wp.media.View
  6559. * @augments wp.Backbone.View
  6560. * @augments Backbone.View
  6561. */
  6562. AttachmentFilters = wp.media.View.extend(/** @lends wp.media.view.AttachmentFilters.prototype */{
  6563. tagName: 'select',
  6564. className: 'attachment-filters',
  6565. id: 'media-attachment-filters',
  6566. events: {
  6567. change: 'change'
  6568. },
  6569. keys: [],
  6570. initialize: function() {
  6571. this.createFilters();
  6572. _.extend( this.filters, this.options.filters );
  6573. // Build `<option>` elements.
  6574. this.$el.html( _.chain( this.filters ).map( function( filter, value ) {
  6575. return {
  6576. el: $( '<option></option>' ).val( value ).html( filter.text )[0],
  6577. priority: filter.priority || 50
  6578. };
  6579. }, this ).sortBy('priority').pluck('el').value() );
  6580. this.listenTo( this.model, 'change', this.select );
  6581. this.select();
  6582. },
  6583. /**
  6584. * @abstract
  6585. */
  6586. createFilters: function() {
  6587. this.filters = {};
  6588. },
  6589. /**
  6590. * When the selected filter changes, update the Attachment Query properties to match.
  6591. */
  6592. change: function() {
  6593. var filter = this.filters[ this.el.value ];
  6594. if ( filter ) {
  6595. this.model.set( filter.props );
  6596. }
  6597. },
  6598. select: function() {
  6599. var model = this.model,
  6600. value = 'all',
  6601. props = model.toJSON();
  6602. _.find( this.filters, function( filter, id ) {
  6603. var equal = _.all( filter.props, function( prop, key ) {
  6604. return prop === ( _.isUndefined( props[ key ] ) ? null : props[ key ] );
  6605. });
  6606. if ( equal ) {
  6607. return value = id;
  6608. }
  6609. });
  6610. this.$el.val( value );
  6611. }
  6612. });
  6613. module.exports = AttachmentFilters;
  6614. /***/ }),
  6615. /* 79 */
  6616. /***/ (function(module, exports) {
  6617. var l10n = wp.media.view.l10n,
  6618. DateFilter;
  6619. /**
  6620. * A filter dropdown for month/dates.
  6621. *
  6622. * @memberOf wp.media.view.AttachmentFilters
  6623. *
  6624. * @class
  6625. * @augments wp.media.view.AttachmentFilters
  6626. * @augments wp.media.View
  6627. * @augments wp.Backbone.View
  6628. * @augments Backbone.View
  6629. */
  6630. DateFilter = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Date.prototype */{
  6631. id: 'media-attachment-date-filters',
  6632. createFilters: function() {
  6633. var filters = {};
  6634. _.each( wp.media.view.settings.months || {}, function( value, index ) {
  6635. filters[ index ] = {
  6636. text: value.text,
  6637. props: {
  6638. year: value.year,
  6639. monthnum: value.month
  6640. }
  6641. };
  6642. });
  6643. filters.all = {
  6644. text: l10n.allDates,
  6645. props: {
  6646. monthnum: false,
  6647. year: false
  6648. },
  6649. priority: 10
  6650. };
  6651. this.filters = filters;
  6652. }
  6653. });
  6654. module.exports = DateFilter;
  6655. /***/ }),
  6656. /* 80 */
  6657. /***/ (function(module, exports) {
  6658. var l10n = wp.media.view.l10n,
  6659. Uploaded;
  6660. /**
  6661. * wp.media.view.AttachmentFilters.Uploaded
  6662. *
  6663. * @memberOf wp.media.view.AttachmentFilters
  6664. *
  6665. * @class
  6666. * @augments wp.media.view.AttachmentFilters
  6667. * @augments wp.media.View
  6668. * @augments wp.Backbone.View
  6669. * @augments Backbone.View
  6670. */
  6671. Uploaded = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.Uploaded.prototype */{
  6672. createFilters: function() {
  6673. var type = this.model.get('type'),
  6674. types = wp.media.view.settings.mimeTypes,
  6675. uid = window.userSettings ? parseInt( window.userSettings.uid, 10 ) : 0,
  6676. text;
  6677. if ( types && type ) {
  6678. text = types[ type ];
  6679. }
  6680. this.filters = {
  6681. all: {
  6682. text: text || l10n.allMediaItems,
  6683. props: {
  6684. uploadedTo: null,
  6685. orderby: 'date',
  6686. order: 'DESC',
  6687. author: null
  6688. },
  6689. priority: 10
  6690. },
  6691. uploaded: {
  6692. text: l10n.uploadedToThisPost,
  6693. props: {
  6694. uploadedTo: wp.media.view.settings.post.id,
  6695. orderby: 'menuOrder',
  6696. order: 'ASC',
  6697. author: null
  6698. },
  6699. priority: 20
  6700. },
  6701. unattached: {
  6702. text: l10n.unattached,
  6703. props: {
  6704. uploadedTo: 0,
  6705. orderby: 'menuOrder',
  6706. order: 'ASC',
  6707. author: null
  6708. },
  6709. priority: 50
  6710. }
  6711. };
  6712. if ( uid ) {
  6713. this.filters.mine = {
  6714. text: l10n.mine,
  6715. props: {
  6716. orderby: 'date',
  6717. order: 'DESC',
  6718. author: uid
  6719. },
  6720. priority: 50
  6721. };
  6722. }
  6723. }
  6724. });
  6725. module.exports = Uploaded;
  6726. /***/ }),
  6727. /* 81 */
  6728. /***/ (function(module, exports) {
  6729. var l10n = wp.media.view.l10n,
  6730. All;
  6731. /**
  6732. * wp.media.view.AttachmentFilters.All
  6733. *
  6734. * @memberOf wp.media.view.AttachmentFilters
  6735. *
  6736. * @class
  6737. * @augments wp.media.view.AttachmentFilters
  6738. * @augments wp.media.View
  6739. * @augments wp.Backbone.View
  6740. * @augments Backbone.View
  6741. */
  6742. All = wp.media.view.AttachmentFilters.extend(/** @lends wp.media.view.AttachmentFilters.All.prototype */{
  6743. createFilters: function() {
  6744. var filters = {},
  6745. uid = window.userSettings ? parseInt( window.userSettings.uid, 10 ) : 0;
  6746. _.each( wp.media.view.settings.mimeTypes || {}, function( text, key ) {
  6747. filters[ key ] = {
  6748. text: text,
  6749. props: {
  6750. status: null,
  6751. type: key,
  6752. uploadedTo: null,
  6753. orderby: 'date',
  6754. order: 'DESC',
  6755. author: null
  6756. }
  6757. };
  6758. });
  6759. filters.all = {
  6760. text: l10n.allMediaItems,
  6761. props: {
  6762. status: null,
  6763. type: null,
  6764. uploadedTo: null,
  6765. orderby: 'date',
  6766. order: 'DESC',
  6767. author: null
  6768. },
  6769. priority: 10
  6770. };
  6771. if ( wp.media.view.settings.post.id ) {
  6772. filters.uploaded = {
  6773. text: l10n.uploadedToThisPost,
  6774. props: {
  6775. status: null,
  6776. type: null,
  6777. uploadedTo: wp.media.view.settings.post.id,
  6778. orderby: 'menuOrder',
  6779. order: 'ASC',
  6780. author: null
  6781. },
  6782. priority: 20
  6783. };
  6784. }
  6785. filters.unattached = {
  6786. text: l10n.unattached,
  6787. props: {
  6788. status: null,
  6789. uploadedTo: 0,
  6790. type: null,
  6791. orderby: 'menuOrder',
  6792. order: 'ASC',
  6793. author: null
  6794. },
  6795. priority: 50
  6796. };
  6797. if ( uid ) {
  6798. filters.mine = {
  6799. text: l10n.mine,
  6800. props: {
  6801. status: null,
  6802. type: null,
  6803. uploadedTo: null,
  6804. orderby: 'date',
  6805. order: 'DESC',
  6806. author: uid
  6807. },
  6808. priority: 50
  6809. };
  6810. }
  6811. if ( wp.media.view.settings.mediaTrash &&
  6812. this.controller.isModeActive( 'grid' ) ) {
  6813. filters.trash = {
  6814. text: l10n.trash,
  6815. props: {
  6816. uploadedTo: null,
  6817. status: 'trash',
  6818. type: null,
  6819. orderby: 'date',
  6820. order: 'DESC',
  6821. author: null
  6822. },
  6823. priority: 50
  6824. };
  6825. }
  6826. this.filters = filters;
  6827. }
  6828. });
  6829. module.exports = All;
  6830. /***/ }),
  6831. /* 82 */
  6832. /***/ (function(module, exports) {
  6833. var View = wp.media.View,
  6834. mediaTrash = wp.media.view.settings.mediaTrash,
  6835. l10n = wp.media.view.l10n,
  6836. $ = jQuery,
  6837. AttachmentsBrowser;
  6838. /**
  6839. * wp.media.view.AttachmentsBrowser
  6840. *
  6841. * @memberOf wp.media.view
  6842. *
  6843. * @class
  6844. * @augments wp.media.View
  6845. * @augments wp.Backbone.View
  6846. * @augments Backbone.View
  6847. *
  6848. * @param {object} [options] The options hash passed to the view.
  6849. * @param {boolean|string} [options.filters=false] Which filters to show in the browser's toolbar.
  6850. * Accepts 'uploaded' and 'all'.
  6851. * @param {boolean} [options.search=true] Whether to show the search interface in the
  6852. * browser's toolbar.
  6853. * @param {boolean} [options.date=true] Whether to show the date filter in the
  6854. * browser's toolbar.
  6855. * @param {boolean} [options.display=false] Whether to show the attachments display settings
  6856. * view in the sidebar.
  6857. * @param {boolean|string} [options.sidebar=true] Whether to create a sidebar for the browser.
  6858. * Accepts true, false, and 'errors'.
  6859. */
  6860. AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.prototype */{
  6861. tagName: 'div',
  6862. className: 'attachments-browser',
  6863. initialize: function() {
  6864. _.defaults( this.options, {
  6865. filters: false,
  6866. search: true,
  6867. date: true,
  6868. display: false,
  6869. sidebar: true,
  6870. AttachmentView: wp.media.view.Attachment.Library
  6871. });
  6872. this.controller.on( 'toggle:upload:attachment', this.toggleUploader, this );
  6873. this.controller.on( 'edit:selection', this.editSelection );
  6874. // In the Media Library, the sidebar is used to display errors before the attachments grid.
  6875. if ( this.options.sidebar && 'errors' === this.options.sidebar ) {
  6876. this.createSidebar();
  6877. }
  6878. /*
  6879. * For accessibility reasons, place the Inline Uploader before other sections.
  6880. * This way, in the Media Library, it's right after the Add New button, see ticket #37188.
  6881. */
  6882. this.createUploader();
  6883. /*
  6884. * Create a multi-purpose toolbar. Used as main toolbar in the Media Library
  6885. * and also for other things, for example the "Drag and drop to reorder" and
  6886. * "Suggested dimensions" info in the media modal.
  6887. */
  6888. this.createToolbar();
  6889. // Add a heading before the attachments list.
  6890. this.createAttachmentsHeading();
  6891. // Create the list of attachments.
  6892. this.createAttachments();
  6893. // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909.
  6894. if ( this.options.sidebar && 'errors' !== this.options.sidebar ) {
  6895. this.createSidebar();
  6896. }
  6897. this.updateContent();
  6898. if ( ! this.options.sidebar || 'errors' === this.options.sidebar ) {
  6899. this.$el.addClass( 'hide-sidebar' );
  6900. if ( 'errors' === this.options.sidebar ) {
  6901. this.$el.addClass( 'sidebar-for-errors' );
  6902. }
  6903. }
  6904. this.collection.on( 'add remove reset', this.updateContent, this );
  6905. // The non-cached or cached attachments query has completed.
  6906. this.collection.on( 'attachments:received', this.announceSearchResults, this );
  6907. },
  6908. /**
  6909. * Updates the `wp.a11y.speak()` ARIA live region with a message to communicate
  6910. * the number of search results to screen reader users. This function is
  6911. * debounced because the collection updates multiple times.
  6912. *
  6913. * @since 5.3.0
  6914. *
  6915. * @returns {void}
  6916. */
  6917. announceSearchResults: _.debounce( function() {
  6918. var count;
  6919. if ( this.collection.mirroring.args.s ) {
  6920. count = this.collection.length;
  6921. if ( 0 === count ) {
  6922. wp.a11y.speak( l10n.noMediaTryNewSearch );
  6923. return;
  6924. }
  6925. if ( this.collection.hasMore() ) {
  6926. wp.a11y.speak( l10n.mediaFoundHasMoreResults.replace( '%d', count ) );
  6927. return;
  6928. }
  6929. wp.a11y.speak( l10n.mediaFound.replace( '%d', count ) );
  6930. }
  6931. }, 200 ),
  6932. editSelection: function( modal ) {
  6933. // When editing a selection, move focus to the "Return to library" button.
  6934. modal.$( '.media-button-backToLibrary' ).focus();
  6935. },
  6936. /**
  6937. * @returns {wp.media.view.AttachmentsBrowser} Returns itself to allow chaining
  6938. */
  6939. dispose: function() {
  6940. this.options.selection.off( null, null, this );
  6941. View.prototype.dispose.apply( this, arguments );
  6942. return this;
  6943. },
  6944. createToolbar: function() {
  6945. var LibraryViewSwitcher, Filters, toolbarOptions,
  6946. showFilterByType = -1 !== $.inArray( this.options.filters, [ 'uploaded', 'all' ] );
  6947. toolbarOptions = {
  6948. controller: this.controller
  6949. };
  6950. if ( this.controller.isModeActive( 'grid' ) ) {
  6951. toolbarOptions.className = 'media-toolbar wp-filter';
  6952. }
  6953. /**
  6954. * @member {wp.media.view.Toolbar}
  6955. */
  6956. this.toolbar = new wp.media.view.Toolbar( toolbarOptions );
  6957. this.views.add( this.toolbar );
  6958. this.toolbar.set( 'spinner', new wp.media.view.Spinner({
  6959. priority: -20
  6960. }) );
  6961. if ( showFilterByType || this.options.date ) {
  6962. /*
  6963. * Create a h2 heading before the select elements that filter attachments.
  6964. * This heading is visible in the modal and visually hidden in the grid.
  6965. */
  6966. this.toolbar.set( 'filters-heading', new wp.media.view.Heading( {
  6967. priority: -100,
  6968. text: l10n.filterAttachments,
  6969. level: 'h2',
  6970. className: 'media-attachments-filter-heading'
  6971. }).render() );
  6972. }
  6973. if ( showFilterByType ) {
  6974. // "Filters" is a <select>, a visually hidden label element needs to be rendered before.
  6975. this.toolbar.set( 'filtersLabel', new wp.media.view.Label({
  6976. value: l10n.filterByType,
  6977. attributes: {
  6978. 'for': 'media-attachment-filters'
  6979. },
  6980. priority: -80
  6981. }).render() );
  6982. if ( 'uploaded' === this.options.filters ) {
  6983. this.toolbar.set( 'filters', new wp.media.view.AttachmentFilters.Uploaded({
  6984. controller: this.controller,
  6985. model: this.collection.props,
  6986. priority: -80
  6987. }).render() );
  6988. } else {
  6989. Filters = new wp.media.view.AttachmentFilters.All({
  6990. controller: this.controller,
  6991. model: this.collection.props,
  6992. priority: -80
  6993. });
  6994. this.toolbar.set( 'filters', Filters.render() );
  6995. }
  6996. }
  6997. // Feels odd to bring the global media library switcher into the Attachment
  6998. // browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar );
  6999. // which the controller can tap into and add this view?
  7000. if ( this.controller.isModeActive( 'grid' ) ) {
  7001. LibraryViewSwitcher = View.extend({
  7002. className: 'view-switch media-grid-view-switch',
  7003. template: wp.template( 'media-library-view-switcher')
  7004. });
  7005. this.toolbar.set( 'libraryViewSwitcher', new LibraryViewSwitcher({
  7006. controller: this.controller,
  7007. priority: -90
  7008. }).render() );
  7009. // DateFilter is a <select>, a visually hidden label element needs to be rendered before.
  7010. this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({
  7011. value: l10n.filterByDate,
  7012. attributes: {
  7013. 'for': 'media-attachment-date-filters'
  7014. },
  7015. priority: -75
  7016. }).render() );
  7017. this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({
  7018. controller: this.controller,
  7019. model: this.collection.props,
  7020. priority: -75
  7021. }).render() );
  7022. // BulkSelection is a <div> with subviews, including screen reader text
  7023. this.toolbar.set( 'selectModeToggleButton', new wp.media.view.SelectModeToggleButton({
  7024. text: l10n.bulkSelect,
  7025. controller: this.controller,
  7026. priority: -70
  7027. }).render() );
  7028. this.toolbar.set( 'deleteSelectedButton', new wp.media.view.DeleteSelectedButton({
  7029. filters: Filters,
  7030. style: 'primary',
  7031. disabled: true,
  7032. text: mediaTrash ? l10n.trashSelected : l10n.deletePermanently,
  7033. controller: this.controller,
  7034. priority: -80,
  7035. click: function() {
  7036. var changed = [], removed = [],
  7037. selection = this.controller.state().get( 'selection' ),
  7038. library = this.controller.state().get( 'library' );
  7039. if ( ! selection.length ) {
  7040. return;
  7041. }
  7042. if ( ! mediaTrash && ! window.confirm( l10n.warnBulkDelete ) ) {
  7043. return;
  7044. }
  7045. if ( mediaTrash &&
  7046. 'trash' !== selection.at( 0 ).get( 'status' ) &&
  7047. ! window.confirm( l10n.warnBulkTrash ) ) {
  7048. return;
  7049. }
  7050. selection.each( function( model ) {
  7051. if ( ! model.get( 'nonces' )['delete'] ) {
  7052. removed.push( model );
  7053. return;
  7054. }
  7055. if ( mediaTrash && 'trash' === model.get( 'status' ) ) {
  7056. model.set( 'status', 'inherit' );
  7057. changed.push( model.save() );
  7058. removed.push( model );
  7059. } else if ( mediaTrash ) {
  7060. model.set( 'status', 'trash' );
  7061. changed.push( model.save() );
  7062. removed.push( model );
  7063. } else {
  7064. model.destroy({wait: true});
  7065. }
  7066. } );
  7067. if ( changed.length ) {
  7068. selection.remove( removed );
  7069. $.when.apply( null, changed ).then( _.bind( function() {
  7070. library._requery( true );
  7071. this.controller.trigger( 'selection:action:done' );
  7072. }, this ) );
  7073. } else {
  7074. this.controller.trigger( 'selection:action:done' );
  7075. }
  7076. }
  7077. }).render() );
  7078. if ( mediaTrash ) {
  7079. this.toolbar.set( 'deleteSelectedPermanentlyButton', new wp.media.view.DeleteSelectedPermanentlyButton({
  7080. filters: Filters,
  7081. style: 'link button-link-delete',
  7082. disabled: true,
  7083. text: l10n.deletePermanently,
  7084. controller: this.controller,
  7085. priority: -55,
  7086. click: function() {
  7087. var removed = [],
  7088. destroy = [],
  7089. selection = this.controller.state().get( 'selection' );
  7090. if ( ! selection.length || ! window.confirm( l10n.warnBulkDelete ) ) {
  7091. return;
  7092. }
  7093. selection.each( function( model ) {
  7094. if ( ! model.get( 'nonces' )['delete'] ) {
  7095. removed.push( model );
  7096. return;
  7097. }
  7098. destroy.push( model );
  7099. } );
  7100. if ( removed.length ) {
  7101. selection.remove( removed );
  7102. }
  7103. if ( destroy.length ) {
  7104. $.when.apply( null, destroy.map( function (item) {
  7105. return item.destroy();
  7106. } ) ).then( _.bind( function() {
  7107. this.controller.trigger( 'selection:action:done' );
  7108. }, this ) );
  7109. }
  7110. }
  7111. }).render() );
  7112. }
  7113. } else if ( this.options.date ) {
  7114. // DateFilter is a <select>, a visually hidden label element needs to be rendered before.
  7115. this.toolbar.set( 'dateFilterLabel', new wp.media.view.Label({
  7116. value: l10n.filterByDate,
  7117. attributes: {
  7118. 'for': 'media-attachment-date-filters'
  7119. },
  7120. priority: -75
  7121. }).render() );
  7122. this.toolbar.set( 'dateFilter', new wp.media.view.DateFilter({
  7123. controller: this.controller,
  7124. model: this.collection.props,
  7125. priority: -75
  7126. }).render() );
  7127. }
  7128. if ( this.options.search ) {
  7129. // Search is an input, a visually hidden label element needs to be rendered before.
  7130. this.toolbar.set( 'searchLabel', new wp.media.view.Label({
  7131. value: l10n.searchLabel,
  7132. className: 'media-search-input-label',
  7133. attributes: {
  7134. 'for': 'media-search-input'
  7135. },
  7136. priority: 60
  7137. }).render() );
  7138. this.toolbar.set( 'search', new wp.media.view.Search({
  7139. controller: this.controller,
  7140. model: this.collection.props,
  7141. priority: 60
  7142. }).render() );
  7143. }
  7144. if ( this.options.dragInfo ) {
  7145. this.toolbar.set( 'dragInfo', new View({
  7146. el: $( '<div class="instructions">' + l10n.dragInfo + '</div>' )[0],
  7147. priority: -40
  7148. }) );
  7149. }
  7150. if ( this.options.suggestedWidth && this.options.suggestedHeight ) {
  7151. this.toolbar.set( 'suggestedDimensions', new View({
  7152. el: $( '<div class="instructions">' + l10n.suggestedDimensions.replace( '%1$s', this.options.suggestedWidth ).replace( '%2$s', this.options.suggestedHeight ) + '</div>' )[0],
  7153. priority: -40
  7154. }) );
  7155. }
  7156. },
  7157. updateContent: function() {
  7158. var view = this,
  7159. noItemsView;
  7160. if ( this.controller.isModeActive( 'grid' ) ) {
  7161. noItemsView = view.attachmentsNoResults;
  7162. } else {
  7163. noItemsView = view.uploader;
  7164. }
  7165. if ( ! this.collection.length ) {
  7166. this.toolbar.get( 'spinner' ).show();
  7167. this.dfd = this.collection.more().done( function() {
  7168. if ( ! view.collection.length ) {
  7169. noItemsView.$el.removeClass( 'hidden' );
  7170. } else {
  7171. noItemsView.$el.addClass( 'hidden' );
  7172. }
  7173. view.toolbar.get( 'spinner' ).hide();
  7174. } );
  7175. } else {
  7176. noItemsView.$el.addClass( 'hidden' );
  7177. view.toolbar.get( 'spinner' ).hide();
  7178. }
  7179. },
  7180. createUploader: function() {
  7181. this.uploader = new wp.media.view.UploaderInline({
  7182. controller: this.controller,
  7183. status: false,
  7184. message: this.controller.isModeActive( 'grid' ) ? '' : l10n.noItemsFound,
  7185. canClose: this.controller.isModeActive( 'grid' )
  7186. });
  7187. this.uploader.$el.addClass( 'hidden' );
  7188. this.views.add( this.uploader );
  7189. },
  7190. toggleUploader: function() {
  7191. if ( this.uploader.$el.hasClass( 'hidden' ) ) {
  7192. this.uploader.show();
  7193. } else {
  7194. this.uploader.hide();
  7195. }
  7196. },
  7197. createAttachments: function() {
  7198. this.attachments = new wp.media.view.Attachments({
  7199. controller: this.controller,
  7200. collection: this.collection,
  7201. selection: this.options.selection,
  7202. model: this.model,
  7203. sortable: this.options.sortable,
  7204. scrollElement: this.options.scrollElement,
  7205. idealColumnWidth: this.options.idealColumnWidth,
  7206. // The single `Attachment` view to be used in the `Attachments` view.
  7207. AttachmentView: this.options.AttachmentView
  7208. });
  7209. // Add keydown listener to the instance of the Attachments view
  7210. this.controller.on( 'attachment:keydown:arrow', _.bind( this.attachments.arrowEvent, this.attachments ) );
  7211. this.controller.on( 'attachment:details:shift-tab', _.bind( this.attachments.restoreFocus, this.attachments ) );
  7212. this.views.add( this.attachments );
  7213. if ( this.controller.isModeActive( 'grid' ) ) {
  7214. this.attachmentsNoResults = new View({
  7215. controller: this.controller,
  7216. tagName: 'p'
  7217. });
  7218. this.attachmentsNoResults.$el.addClass( 'hidden no-media' );
  7219. this.attachmentsNoResults.$el.html( l10n.noMedia );
  7220. this.views.add( this.attachmentsNoResults );
  7221. }
  7222. },
  7223. createAttachmentsHeading: function() {
  7224. this.attachmentsHeading = new wp.media.view.Heading( {
  7225. text: l10n.attachmentsList,
  7226. level: 'h2',
  7227. className: 'media-views-heading screen-reader-text'
  7228. } );
  7229. this.views.add( this.attachmentsHeading );
  7230. },
  7231. createSidebar: function() {
  7232. var options = this.options,
  7233. selection = options.selection,
  7234. sidebar = this.sidebar = new wp.media.view.Sidebar({
  7235. controller: this.controller
  7236. });
  7237. this.views.add( sidebar );
  7238. if ( this.controller.uploader ) {
  7239. sidebar.set( 'uploads', new wp.media.view.UploaderStatus({
  7240. controller: this.controller,
  7241. priority: 40
  7242. }) );
  7243. }
  7244. selection.on( 'selection:single', this.createSingle, this );
  7245. selection.on( 'selection:unsingle', this.disposeSingle, this );
  7246. if ( selection.single() ) {
  7247. this.createSingle();
  7248. }
  7249. },
  7250. createSingle: function() {
  7251. var sidebar = this.sidebar,
  7252. single = this.options.selection.single();
  7253. sidebar.set( 'details', new wp.media.view.Attachment.Details({
  7254. controller: this.controller,
  7255. model: single,
  7256. priority: 80
  7257. }) );
  7258. sidebar.set( 'compat', new wp.media.view.AttachmentCompat({
  7259. controller: this.controller,
  7260. model: single,
  7261. priority: 120
  7262. }) );
  7263. if ( this.options.display ) {
  7264. sidebar.set( 'display', new wp.media.view.Settings.AttachmentDisplay({
  7265. controller: this.controller,
  7266. model: this.model.display( single ),
  7267. attachment: single,
  7268. priority: 160,
  7269. userSettings: this.model.get('displayUserSettings')
  7270. }) );
  7271. }
  7272. // Show the sidebar on mobile
  7273. if ( this.model.id === 'insert' ) {
  7274. sidebar.$el.addClass( 'visible' );
  7275. }
  7276. },
  7277. disposeSingle: function() {
  7278. var sidebar = this.sidebar;
  7279. sidebar.unset('details');
  7280. sidebar.unset('compat');
  7281. sidebar.unset('display');
  7282. // Hide the sidebar on mobile
  7283. sidebar.$el.removeClass( 'visible' );
  7284. }
  7285. });
  7286. module.exports = AttachmentsBrowser;
  7287. /***/ }),
  7288. /* 83 */
  7289. /***/ (function(module, exports) {
  7290. var _n = wp.i18n._n,
  7291. sprintf = wp.i18n.sprintf,
  7292. Selection;
  7293. /**
  7294. * wp.media.view.Selection
  7295. *
  7296. * @memberOf wp.media.view
  7297. *
  7298. * @class
  7299. * @augments wp.media.View
  7300. * @augments wp.Backbone.View
  7301. * @augments Backbone.View
  7302. */
  7303. Selection = wp.media.View.extend(/** @lends wp.media.view.Selection.prototype */{
  7304. tagName: 'div',
  7305. className: 'media-selection',
  7306. template: wp.template('media-selection'),
  7307. events: {
  7308. 'click .edit-selection': 'edit',
  7309. 'click .clear-selection': 'clear'
  7310. },
  7311. initialize: function() {
  7312. _.defaults( this.options, {
  7313. editable: false,
  7314. clearable: true
  7315. });
  7316. /**
  7317. * @member {wp.media.view.Attachments.Selection}
  7318. */
  7319. this.attachments = new wp.media.view.Attachments.Selection({
  7320. controller: this.controller,
  7321. collection: this.collection,
  7322. selection: this.collection,
  7323. model: new Backbone.Model()
  7324. });
  7325. this.views.set( '.selection-view', this.attachments );
  7326. this.collection.on( 'add remove reset', this.refresh, this );
  7327. this.controller.on( 'content:activate', this.refresh, this );
  7328. },
  7329. ready: function() {
  7330. this.refresh();
  7331. },
  7332. refresh: function() {
  7333. // If the selection hasn't been rendered, bail.
  7334. if ( ! this.$el.children().length ) {
  7335. return;
  7336. }
  7337. var collection = this.collection,
  7338. editing = 'edit-selection' === this.controller.content.mode();
  7339. // If nothing is selected, display nothing.
  7340. this.$el.toggleClass( 'empty', ! collection.length );
  7341. this.$el.toggleClass( 'one', 1 === collection.length );
  7342. this.$el.toggleClass( 'editing', editing );
  7343. this.$( '.count' ).text(
  7344. /* translators: %s: Number of selected media attachments. */
  7345. sprintf( _n( '%s item selected', '%s items selected', collection.length ), collection.length )
  7346. );
  7347. },
  7348. edit: function( event ) {
  7349. event.preventDefault();
  7350. if ( this.options.editable ) {
  7351. this.options.editable.call( this, this.collection );
  7352. }
  7353. },
  7354. clear: function( event ) {
  7355. event.preventDefault();
  7356. this.collection.reset();
  7357. // Move focus to the modal.
  7358. this.controller.modal.focusManager.focus();
  7359. }
  7360. });
  7361. module.exports = Selection;
  7362. /***/ }),
  7363. /* 84 */
  7364. /***/ (function(module, exports) {
  7365. /**
  7366. * wp.media.view.Attachment.Selection
  7367. *
  7368. * @memberOf wp.media.view.Attachment
  7369. *
  7370. * @class
  7371. * @augments wp.media.view.Attachment
  7372. * @augments wp.media.View
  7373. * @augments wp.Backbone.View
  7374. * @augments Backbone.View
  7375. */
  7376. var Selection = wp.media.view.Attachment.extend(/** @lends wp.media.view.Attachment.Selection.prototype */{
  7377. className: 'attachment selection',
  7378. // On click, just select the model, instead of removing the model from
  7379. // the selection.
  7380. toggleSelection: function() {
  7381. this.options.selection.single( this.model );
  7382. }
  7383. });
  7384. module.exports = Selection;
  7385. /***/ }),
  7386. /* 85 */
  7387. /***/ (function(module, exports) {
  7388. var Attachments = wp.media.view.Attachments,
  7389. Selection;
  7390. /**
  7391. * wp.media.view.Attachments.Selection
  7392. *
  7393. * @memberOf wp.media.view.Attachments
  7394. *
  7395. * @class
  7396. * @augments wp.media.view.Attachments
  7397. * @augments wp.media.View
  7398. * @augments wp.Backbone.View
  7399. * @augments Backbone.View
  7400. */
  7401. Selection = Attachments.extend(/** @lends wp.media.view.Attachments.Selection.prototype */{
  7402. events: {},
  7403. initialize: function() {
  7404. _.defaults( this.options, {
  7405. sortable: false,
  7406. resize: false,
  7407. // The single `Attachment` view to be used in the `Attachments` view.
  7408. AttachmentView: wp.media.view.Attachment.Selection
  7409. });
  7410. // Call 'initialize' directly on the parent class.
  7411. return Attachments.prototype.initialize.apply( this, arguments );
  7412. }
  7413. });
  7414. module.exports = Selection;
  7415. /***/ }),
  7416. /* 86 */
  7417. /***/ (function(module, exports) {
  7418. /**
  7419. * wp.media.view.Attachment.EditSelection
  7420. *
  7421. * @memberOf wp.media.view.Attachment
  7422. *
  7423. * @class
  7424. * @augments wp.media.view.Attachment.Selection
  7425. * @augments wp.media.view.Attachment
  7426. * @augments wp.media.View
  7427. * @augments wp.Backbone.View
  7428. * @augments Backbone.View
  7429. */
  7430. var EditSelection = wp.media.view.Attachment.Selection.extend(/** @lends wp.media.view.Attachment.EditSelection.prototype */{
  7431. buttons: {
  7432. close: true
  7433. }
  7434. });
  7435. module.exports = EditSelection;
  7436. /***/ }),
  7437. /* 87 */
  7438. /***/ (function(module, exports) {
  7439. var View = wp.media.View,
  7440. $ = Backbone.$,
  7441. Settings;
  7442. /**
  7443. * wp.media.view.Settings
  7444. *
  7445. * @memberOf wp.media.view
  7446. *
  7447. * @class
  7448. * @augments wp.media.View
  7449. * @augments wp.Backbone.View
  7450. * @augments Backbone.View
  7451. */
  7452. Settings = View.extend(/** @lends wp.media.view.Settings.prototype */{
  7453. events: {
  7454. 'click button': 'updateHandler',
  7455. 'change input': 'updateHandler',
  7456. 'change select': 'updateHandler',
  7457. 'change textarea': 'updateHandler'
  7458. },
  7459. initialize: function() {
  7460. this.model = this.model || new Backbone.Model();
  7461. this.listenTo( this.model, 'change', this.updateChanges );
  7462. },
  7463. prepare: function() {
  7464. return _.defaults({
  7465. model: this.model.toJSON()
  7466. }, this.options );
  7467. },
  7468. /**
  7469. * @returns {wp.media.view.Settings} Returns itself to allow chaining
  7470. */
  7471. render: function() {
  7472. View.prototype.render.apply( this, arguments );
  7473. // Select the correct values.
  7474. _( this.model.attributes ).chain().keys().each( this.update, this );
  7475. return this;
  7476. },
  7477. /**
  7478. * @param {string} key
  7479. */
  7480. update: function( key ) {
  7481. var value = this.model.get( key ),
  7482. $setting = this.$('[data-setting="' + key + '"]'),
  7483. $buttons, $value;
  7484. // Bail if we didn't find a matching setting.
  7485. if ( ! $setting.length ) {
  7486. return;
  7487. }
  7488. // Attempt to determine how the setting is rendered and update
  7489. // the selected value.
  7490. // Handle dropdowns.
  7491. if ( $setting.is('select') ) {
  7492. $value = $setting.find('[value="' + value + '"]');
  7493. if ( $value.length ) {
  7494. $setting.find('option').prop( 'selected', false );
  7495. $value.prop( 'selected', true );
  7496. } else {
  7497. // If we can't find the desired value, record what *is* selected.
  7498. this.model.set( key, $setting.find(':selected').val() );
  7499. }
  7500. // Handle button groups.
  7501. } else if ( $setting.hasClass('button-group') ) {
  7502. $buttons = $setting.find('button').removeClass('active');
  7503. $buttons.filter( '[value="' + value + '"]' ).addClass('active');
  7504. // Handle text inputs and textareas.
  7505. } else if ( $setting.is('input[type="text"], textarea') ) {
  7506. if ( ! $setting.is(':focus') ) {
  7507. $setting.val( value );
  7508. }
  7509. // Handle checkboxes.
  7510. } else if ( $setting.is('input[type="checkbox"]') ) {
  7511. $setting.prop( 'checked', !! value && 'false' !== value );
  7512. }
  7513. },
  7514. /**
  7515. * @param {Object} event
  7516. */
  7517. updateHandler: function( event ) {
  7518. var $setting = $( event.target ).closest('[data-setting]'),
  7519. value = event.target.value,
  7520. userSetting;
  7521. event.preventDefault();
  7522. if ( ! $setting.length ) {
  7523. return;
  7524. }
  7525. // Use the correct value for checkboxes.
  7526. if ( $setting.is('input[type="checkbox"]') ) {
  7527. value = $setting[0].checked;
  7528. }
  7529. // Update the corresponding setting.
  7530. this.model.set( $setting.data('setting'), value );
  7531. // If the setting has a corresponding user setting,
  7532. // update that as well.
  7533. if ( userSetting = $setting.data('userSetting') ) {
  7534. window.setUserSetting( userSetting, value );
  7535. }
  7536. },
  7537. updateChanges: function( model ) {
  7538. if ( model.hasChanged() ) {
  7539. _( model.changed ).chain().keys().each( this.update, this );
  7540. }
  7541. }
  7542. });
  7543. module.exports = Settings;
  7544. /***/ }),
  7545. /* 88 */
  7546. /***/ (function(module, exports) {
  7547. var Settings = wp.media.view.Settings,
  7548. AttachmentDisplay;
  7549. /**
  7550. * wp.media.view.Settings.AttachmentDisplay
  7551. *
  7552. * @memberOf wp.media.view.Settings
  7553. *
  7554. * @class
  7555. * @augments wp.media.view.Settings
  7556. * @augments wp.media.View
  7557. * @augments wp.Backbone.View
  7558. * @augments Backbone.View
  7559. */
  7560. AttachmentDisplay = Settings.extend(/** @lends wp.media.view.Settings.AttachmentDisplay.prototype */{
  7561. className: 'attachment-display-settings',
  7562. template: wp.template('attachment-display-settings'),
  7563. initialize: function() {
  7564. var attachment = this.options.attachment;
  7565. _.defaults( this.options, {
  7566. userSettings: false
  7567. });
  7568. // Call 'initialize' directly on the parent class.
  7569. Settings.prototype.initialize.apply( this, arguments );
  7570. this.listenTo( this.model, 'change:link', this.updateLinkTo );
  7571. if ( attachment ) {
  7572. attachment.on( 'change:uploading', this.render, this );
  7573. }
  7574. },
  7575. dispose: function() {
  7576. var attachment = this.options.attachment;
  7577. if ( attachment ) {
  7578. attachment.off( null, null, this );
  7579. }
  7580. /**
  7581. * call 'dispose' directly on the parent class
  7582. */
  7583. Settings.prototype.dispose.apply( this, arguments );
  7584. },
  7585. /**
  7586. * @returns {wp.media.view.AttachmentDisplay} Returns itself to allow chaining
  7587. */
  7588. render: function() {
  7589. var attachment = this.options.attachment;
  7590. if ( attachment ) {
  7591. _.extend( this.options, {
  7592. sizes: attachment.get('sizes'),
  7593. type: attachment.get('type')
  7594. });
  7595. }
  7596. /**
  7597. * call 'render' directly on the parent class
  7598. */
  7599. Settings.prototype.render.call( this );
  7600. this.updateLinkTo();
  7601. return this;
  7602. },
  7603. updateLinkTo: function() {
  7604. var linkTo = this.model.get('link'),
  7605. $input = this.$('.link-to-custom'),
  7606. attachment = this.options.attachment;
  7607. if ( 'none' === linkTo || 'embed' === linkTo || ( ! attachment && 'custom' !== linkTo ) ) {
  7608. $input.closest( '.setting' ).addClass( 'hidden' );
  7609. return;
  7610. }
  7611. if ( attachment ) {
  7612. if ( 'post' === linkTo ) {
  7613. $input.val( attachment.get('link') );
  7614. } else if ( 'file' === linkTo ) {
  7615. $input.val( attachment.get('url') );
  7616. } else if ( ! this.model.get('linkUrl') ) {
  7617. $input.val('http://');
  7618. }
  7619. $input.prop( 'readonly', 'custom' !== linkTo );
  7620. }
  7621. $input.closest( '.setting' ).removeClass( 'hidden' );
  7622. }
  7623. });
  7624. module.exports = AttachmentDisplay;
  7625. /***/ }),
  7626. /* 89 */
  7627. /***/ (function(module, exports) {
  7628. /**
  7629. * wp.media.view.Settings.Gallery
  7630. *
  7631. * @memberOf wp.media.view.Settings
  7632. *
  7633. * @class
  7634. * @augments wp.media.view.Settings
  7635. * @augments wp.media.View
  7636. * @augments wp.Backbone.View
  7637. * @augments Backbone.View
  7638. */
  7639. var Gallery = wp.media.view.Settings.extend(/** @lends wp.media.view.Settings.Gallery.prototype */{
  7640. className: 'collection-settings gallery-settings',
  7641. template: wp.template('gallery-settings')
  7642. });
  7643. module.exports = Gallery;
  7644. /***/ }),
  7645. /* 90 */
  7646. /***/ (function(module, exports) {
  7647. /**
  7648. * wp.media.view.Settings.Playlist
  7649. *
  7650. * @memberOf wp.media.view.Settings
  7651. *
  7652. * @class
  7653. * @augments wp.media.view.Settings
  7654. * @augments wp.media.View
  7655. * @augments wp.Backbone.View
  7656. * @augments Backbone.View
  7657. */
  7658. var Playlist = wp.media.view.Settings.extend(/** @lends wp.media.view.Settings.Playlist.prototype */{
  7659. className: 'collection-settings playlist-settings',
  7660. template: wp.template('playlist-settings')
  7661. });
  7662. module.exports = Playlist;
  7663. /***/ }),
  7664. /* 91 */
  7665. /***/ (function(module, exports) {
  7666. var Attachment = wp.media.view.Attachment,
  7667. l10n = wp.media.view.l10n,
  7668. $ = jQuery,
  7669. Details;
  7670. Details = Attachment.extend(/** @lends wp.media.view.Attachment.Details.prototype */{
  7671. tagName: 'div',
  7672. className: 'attachment-details',
  7673. template: wp.template('attachment-details'),
  7674. /*
  7675. * Reset all the attributes inherited from Attachment including role=checkbox,
  7676. * tabindex, etc., as they are inappropriate for this view. See #47458 and [30483] / #30390.
  7677. */
  7678. attributes: {},
  7679. events: {
  7680. 'change [data-setting]': 'updateSetting',
  7681. 'change [data-setting] input': 'updateSetting',
  7682. 'change [data-setting] select': 'updateSetting',
  7683. 'change [data-setting] textarea': 'updateSetting',
  7684. 'click .delete-attachment': 'deleteAttachment',
  7685. 'click .trash-attachment': 'trashAttachment',
  7686. 'click .untrash-attachment': 'untrashAttachment',
  7687. 'click .edit-attachment': 'editAttachment',
  7688. 'keydown': 'toggleSelectionHandler'
  7689. },
  7690. /**
  7691. * Shows the details of an attachment.
  7692. *
  7693. * @since 3.5.0
  7694. *
  7695. * @constructs wp.media.view.Attachment.Details
  7696. * @augments wp.media.view.Attachment
  7697. *
  7698. * @returns {void}
  7699. */
  7700. initialize: function() {
  7701. this.options = _.defaults( this.options, {
  7702. rerenderOnModelChange: false
  7703. });
  7704. // Call 'initialize' directly on the parent class.
  7705. Attachment.prototype.initialize.apply( this, arguments );
  7706. },
  7707. /**
  7708. * Gets the focusable elements to move focus to.
  7709. *
  7710. * @since 5.3.0
  7711. */
  7712. getFocusableElements: function() {
  7713. var editedAttachment = $( 'li[data-id="' + this.model.id + '"]' );
  7714. this.previousAttachment = editedAttachment.prev();
  7715. this.nextAttachment = editedAttachment.next();
  7716. },
  7717. /**
  7718. * Moves focus to the previous or next attachment in the grid.
  7719. * Fallbacks to the upload button or media frame when there are no attachments.
  7720. *
  7721. * @since 5.3.0
  7722. */
  7723. moveFocus: function() {
  7724. if ( this.previousAttachment.length ) {
  7725. this.previousAttachment.focus();
  7726. return;
  7727. }
  7728. if ( this.nextAttachment.length ) {
  7729. this.nextAttachment.focus();
  7730. return;
  7731. }
  7732. // Fallback: move focus to the "Select Files" button in the media modal.
  7733. if ( this.controller.uploader && this.controller.uploader.$browser ) {
  7734. this.controller.uploader.$browser.focus();
  7735. return;
  7736. }
  7737. // Last fallback.
  7738. this.moveFocusToLastFallback();
  7739. },
  7740. /**
  7741. * Moves focus to the media frame as last fallback.
  7742. *
  7743. * @since 5.3.0
  7744. */
  7745. moveFocusToLastFallback: function() {
  7746. // Last fallback: make the frame focusable and move focus to it.
  7747. $( '.media-frame' )
  7748. .attr( 'tabindex', '-1' )
  7749. .focus();
  7750. },
  7751. /**
  7752. * Deletes an attachment.
  7753. *
  7754. * Deletes an attachment after asking for confirmation. After deletion,
  7755. * keeps focus in the modal.
  7756. *
  7757. * @since 3.5.0
  7758. *
  7759. * @param {MouseEvent} event A click event.
  7760. *
  7761. * @returns {void}
  7762. */
  7763. deleteAttachment: function( event ) {
  7764. event.preventDefault();
  7765. this.getFocusableElements();
  7766. if ( window.confirm( l10n.warnDelete ) ) {
  7767. this.model.destroy();
  7768. this.moveFocus();
  7769. }
  7770. },
  7771. /**
  7772. * Sets the trash state on an attachment, or destroys the model itself.
  7773. *
  7774. * If the mediaTrash setting is set to true, trashes the attachment.
  7775. * Otherwise, the model itself is destroyed.
  7776. *
  7777. * @since 3.9.0
  7778. *
  7779. * @param {MouseEvent} event A click event.
  7780. *
  7781. * @returns {void}
  7782. */
  7783. trashAttachment: function( event ) {
  7784. var library = this.controller.library,
  7785. self = this;
  7786. event.preventDefault();
  7787. this.getFocusableElements();
  7788. // When in the Media Library and the Media trash is enabled.
  7789. if ( wp.media.view.settings.mediaTrash &&
  7790. 'edit-metadata' === this.controller.content.mode() ) {
  7791. this.model.set( 'status', 'trash' );
  7792. this.model.save().done( function() {
  7793. library._requery( true );
  7794. /*
  7795. * @todo: We need to move focus back to the previous, next, or first
  7796. * attachment but the library gets re-queried and refreshed. Thus,
  7797. * the references to the previous attachments are lost. We need an
  7798. * alternate method.
  7799. */
  7800. self.moveFocusToLastFallback();
  7801. } );
  7802. } else {
  7803. this.model.destroy();
  7804. this.moveFocus();
  7805. }
  7806. },
  7807. /**
  7808. * Untrashes an attachment.
  7809. *
  7810. * @since 4.0.0
  7811. *
  7812. * @param {MouseEvent} event A click event.
  7813. *
  7814. * @returns {void}
  7815. */
  7816. untrashAttachment: function( event ) {
  7817. var library = this.controller.library;
  7818. event.preventDefault();
  7819. this.model.set( 'status', 'inherit' );
  7820. this.model.save().done( function() {
  7821. library._requery( true );
  7822. } );
  7823. },
  7824. /**
  7825. * Opens the edit page for a specific attachment.
  7826. *
  7827. * @since 3.5.0
  7828. *
  7829. * @param {MouseEvent} event A click event.
  7830. *
  7831. * @returns {void}
  7832. */
  7833. editAttachment: function( event ) {
  7834. var editState = this.controller.states.get( 'edit-image' );
  7835. if ( window.imageEdit && editState ) {
  7836. event.preventDefault();
  7837. editState.set( 'image', this.model );
  7838. this.controller.setState( 'edit-image' );
  7839. } else {
  7840. this.$el.addClass('needs-refresh');
  7841. }
  7842. },
  7843. /**
  7844. * Triggers an event on the controller when reverse tabbing (shift+tab).
  7845. *
  7846. * This event can be used to make sure to move the focus correctly.
  7847. *
  7848. * @since 4.0.0
  7849. *
  7850. * @fires wp.media.controller.MediaLibrary#attachment:details:shift-tab
  7851. * @fires wp.media.controller.MediaLibrary#attachment:keydown:arrow
  7852. *
  7853. * @param {KeyboardEvent} event A keyboard event.
  7854. *
  7855. * @returns {boolean|void} Returns false or undefined.
  7856. */
  7857. toggleSelectionHandler: function( event ) {
  7858. if ( 'keydown' === event.type && 9 === event.keyCode && event.shiftKey && event.target === this.$( ':tabbable' ).get( 0 ) ) {
  7859. this.controller.trigger( 'attachment:details:shift-tab', event );
  7860. return false;
  7861. }
  7862. }
  7863. });
  7864. module.exports = Details;
  7865. /***/ }),
  7866. /* 92 */
  7867. /***/ (function(module, exports) {
  7868. var View = wp.media.View,
  7869. AttachmentCompat;
  7870. /**
  7871. * wp.media.view.AttachmentCompat
  7872. *
  7873. * A view to display fields added via the `attachment_fields_to_edit` filter.
  7874. *
  7875. * @memberOf wp.media.view
  7876. *
  7877. * @class
  7878. * @augments wp.media.View
  7879. * @augments wp.Backbone.View
  7880. * @augments Backbone.View
  7881. */
  7882. AttachmentCompat = View.extend(/** @lends wp.media.view.AttachmentCompat.prototype */{
  7883. tagName: 'form',
  7884. className: 'compat-item',
  7885. events: {
  7886. 'submit': 'preventDefault',
  7887. 'change input': 'save',
  7888. 'change select': 'save',
  7889. 'change textarea': 'save'
  7890. },
  7891. initialize: function() {
  7892. this.listenTo( this.model, 'change:compat', this.render );
  7893. },
  7894. /**
  7895. * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining
  7896. */
  7897. dispose: function() {
  7898. if ( this.$(':focus').length ) {
  7899. this.save();
  7900. }
  7901. /**
  7902. * call 'dispose' directly on the parent class
  7903. */
  7904. return View.prototype.dispose.apply( this, arguments );
  7905. },
  7906. /**
  7907. * @returns {wp.media.view.AttachmentCompat} Returns itself to allow chaining
  7908. */
  7909. render: function() {
  7910. var compat = this.model.get('compat');
  7911. if ( ! compat || ! compat.item ) {
  7912. return;
  7913. }
  7914. this.views.detach();
  7915. this.$el.html( compat.item );
  7916. this.views.render();
  7917. return this;
  7918. },
  7919. /**
  7920. * @param {Object} event
  7921. */
  7922. preventDefault: function( event ) {
  7923. event.preventDefault();
  7924. },
  7925. /**
  7926. * @param {Object} event
  7927. */
  7928. save: function( event ) {
  7929. var data = {};
  7930. if ( event ) {
  7931. event.preventDefault();
  7932. }
  7933. _.each( this.$el.serializeArray(), function( pair ) {
  7934. data[ pair.name ] = pair.value;
  7935. });
  7936. this.controller.trigger( 'attachment:compat:waiting', ['waiting'] );
  7937. this.model.saveCompat( data ).always( _.bind( this.postSave, this ) );
  7938. },
  7939. postSave: function() {
  7940. this.controller.trigger( 'attachment:compat:ready', ['ready'] );
  7941. }
  7942. });
  7943. module.exports = AttachmentCompat;
  7944. /***/ }),
  7945. /* 93 */
  7946. /***/ (function(module, exports) {
  7947. /**
  7948. * wp.media.view.Iframe
  7949. *
  7950. * @memberOf wp.media.view
  7951. *
  7952. * @class
  7953. * @augments wp.media.View
  7954. * @augments wp.Backbone.View
  7955. * @augments Backbone.View
  7956. */
  7957. var Iframe = wp.media.View.extend(/** @lends wp.media.view.Iframe.prototype */{
  7958. className: 'media-iframe',
  7959. /**
  7960. * @returns {wp.media.view.Iframe} Returns itself to allow chaining
  7961. */
  7962. render: function() {
  7963. this.views.detach();
  7964. this.$el.html( '<iframe src="' + this.controller.state().get('src') + '" />' );
  7965. this.views.render();
  7966. return this;
  7967. }
  7968. });
  7969. module.exports = Iframe;
  7970. /***/ }),
  7971. /* 94 */
  7972. /***/ (function(module, exports) {
  7973. /**
  7974. * wp.media.view.Embed
  7975. *
  7976. * @memberOf wp.media.view
  7977. *
  7978. * @class
  7979. * @augments wp.media.View
  7980. * @augments wp.Backbone.View
  7981. * @augments Backbone.View
  7982. */
  7983. var Embed = wp.media.View.extend(/** @lends wp.media.view.Ember.prototype */{
  7984. className: 'media-embed',
  7985. initialize: function() {
  7986. /**
  7987. * @member {wp.media.view.EmbedUrl}
  7988. */
  7989. this.url = new wp.media.view.EmbedUrl({
  7990. controller: this.controller,
  7991. model: this.model.props
  7992. }).render();
  7993. this.views.set([ this.url ]);
  7994. this.refresh();
  7995. this.listenTo( this.model, 'change:type', this.refresh );
  7996. this.listenTo( this.model, 'change:loading', this.loading );
  7997. },
  7998. /**
  7999. * @param {Object} view
  8000. */
  8001. settings: function( view ) {
  8002. if ( this._settings ) {
  8003. this._settings.remove();
  8004. }
  8005. this._settings = view;
  8006. this.views.add( view );
  8007. },
  8008. refresh: function() {
  8009. var type = this.model.get('type'),
  8010. constructor;
  8011. if ( 'image' === type ) {
  8012. constructor = wp.media.view.EmbedImage;
  8013. } else if ( 'link' === type ) {
  8014. constructor = wp.media.view.EmbedLink;
  8015. } else {
  8016. return;
  8017. }
  8018. this.settings( new constructor({
  8019. controller: this.controller,
  8020. model: this.model.props,
  8021. priority: 40
  8022. }) );
  8023. },
  8024. loading: function() {
  8025. this.$el.toggleClass( 'embed-loading', this.model.get('loading') );
  8026. }
  8027. });
  8028. module.exports = Embed;
  8029. /***/ }),
  8030. /* 95 */
  8031. /***/ (function(module, exports) {
  8032. /**
  8033. * wp.media.view.Label
  8034. *
  8035. * @memberOf wp.media.view
  8036. *
  8037. * @class
  8038. * @augments wp.media.View
  8039. * @augments wp.Backbone.View
  8040. * @augments Backbone.View
  8041. */
  8042. var Label = wp.media.View.extend(/** @lends wp.media.view.Label.prototype */{
  8043. tagName: 'label',
  8044. className: 'screen-reader-text',
  8045. initialize: function() {
  8046. this.value = this.options.value;
  8047. },
  8048. render: function() {
  8049. this.$el.html( this.value );
  8050. return this;
  8051. }
  8052. });
  8053. module.exports = Label;
  8054. /***/ }),
  8055. /* 96 */
  8056. /***/ (function(module, exports) {
  8057. var View = wp.media.View,
  8058. $ = jQuery,
  8059. l10n = wp.media.view.l10n,
  8060. EmbedUrl;
  8061. /**
  8062. * wp.media.view.EmbedUrl
  8063. *
  8064. * @memberOf wp.media.view
  8065. *
  8066. * @class
  8067. * @augments wp.media.View
  8068. * @augments wp.Backbone.View
  8069. * @augments Backbone.View
  8070. */
  8071. EmbedUrl = View.extend(/** @lends wp.media.view.EmbedUrl.prototype */{
  8072. tagName: 'span',
  8073. className: 'embed-url',
  8074. events: {
  8075. 'input': 'url'
  8076. },
  8077. initialize: function() {
  8078. this.$input = $( '<input id="embed-url-field" type="url" />' )
  8079. .attr( 'aria-label', l10n.insertFromUrlTitle )
  8080. .val( this.model.get('url') );
  8081. this.input = this.$input[0];
  8082. this.spinner = $('<span class="spinner" />')[0];
  8083. this.$el.append([ this.input, this.spinner ]);
  8084. this.listenTo( this.model, 'change:url', this.render );
  8085. if ( this.model.get( 'url' ) ) {
  8086. _.delay( _.bind( function () {
  8087. this.model.trigger( 'change:url' );
  8088. }, this ), 500 );
  8089. }
  8090. },
  8091. /**
  8092. * @returns {wp.media.view.EmbedUrl} Returns itself to allow chaining
  8093. */
  8094. render: function() {
  8095. var $input = this.$input;
  8096. if ( $input.is(':focus') ) {
  8097. return;
  8098. }
  8099. this.input.value = this.model.get('url') || 'http://';
  8100. /**
  8101. * Call `render` directly on parent class with passed arguments
  8102. */
  8103. View.prototype.render.apply( this, arguments );
  8104. return this;
  8105. },
  8106. url: function( event ) {
  8107. this.model.set( 'url', $.trim( event.target.value ) );
  8108. }
  8109. });
  8110. module.exports = EmbedUrl;
  8111. /***/ }),
  8112. /* 97 */
  8113. /***/ (function(module, exports) {
  8114. var $ = jQuery,
  8115. EmbedLink;
  8116. /**
  8117. * wp.media.view.EmbedLink
  8118. *
  8119. * @memberOf wp.media.view
  8120. *
  8121. * @class
  8122. * @augments wp.media.view.Settings
  8123. * @augments wp.media.View
  8124. * @augments wp.Backbone.View
  8125. * @augments Backbone.View
  8126. */
  8127. EmbedLink = wp.media.view.Settings.extend(/** @lends wp.media.view.EmbedLink.prototype */{
  8128. className: 'embed-link-settings',
  8129. template: wp.template('embed-link-settings'),
  8130. initialize: function() {
  8131. this.listenTo( this.model, 'change:url', this.updateoEmbed );
  8132. },
  8133. updateoEmbed: _.debounce( function() {
  8134. var url = this.model.get( 'url' );
  8135. // clear out previous results
  8136. this.$('.embed-container').hide().find('.embed-preview').empty();
  8137. this.$( '.setting' ).hide();
  8138. // only proceed with embed if the field contains more than 11 characters
  8139. // Example: http://a.io is 11 chars
  8140. if ( url && ( url.length < 11 || ! url.match(/^http(s)?:\/\//) ) ) {
  8141. return;
  8142. }
  8143. this.fetch();
  8144. }, wp.media.controller.Embed.sensitivity ),
  8145. fetch: function() {
  8146. var url = this.model.get( 'url' ), re, youTubeEmbedMatch;
  8147. // check if they haven't typed in 500 ms
  8148. if ( $('#embed-url-field').val() !== url ) {
  8149. return;
  8150. }
  8151. if ( this.dfd && 'pending' === this.dfd.state() ) {
  8152. this.dfd.abort();
  8153. }
  8154. // Support YouTube embed urls, since they work once in the editor.
  8155. re = /https?:\/\/www\.youtube\.com\/embed\/([^/]+)/;
  8156. youTubeEmbedMatch = re.exec( url );
  8157. if ( youTubeEmbedMatch ) {
  8158. url = 'https://www.youtube.com/watch?v=' + youTubeEmbedMatch[ 1 ];
  8159. }
  8160. this.dfd = wp.apiRequest({
  8161. url: wp.media.view.settings.oEmbedProxyUrl,
  8162. data: {
  8163. url: url,
  8164. maxwidth: this.model.get( 'width' ),
  8165. maxheight: this.model.get( 'height' )
  8166. },
  8167. type: 'GET',
  8168. dataType: 'json',
  8169. context: this
  8170. })
  8171. .done( function( response ) {
  8172. this.renderoEmbed( {
  8173. data: {
  8174. body: response.html || ''
  8175. }
  8176. } );
  8177. } )
  8178. .fail( this.renderFail );
  8179. },
  8180. renderFail: function ( response, status ) {
  8181. if ( 'abort' === status ) {
  8182. return;
  8183. }
  8184. this.$( '.link-text' ).show();
  8185. },
  8186. renderoEmbed: function( response ) {
  8187. var html = ( response && response.data && response.data.body ) || '';
  8188. if ( html ) {
  8189. this.$('.embed-container').show().find('.embed-preview').html( html );
  8190. } else {
  8191. this.renderFail();
  8192. }
  8193. }
  8194. });
  8195. module.exports = EmbedLink;
  8196. /***/ }),
  8197. /* 98 */
  8198. /***/ (function(module, exports) {
  8199. var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay,
  8200. EmbedImage;
  8201. /**
  8202. * wp.media.view.EmbedImage
  8203. *
  8204. * @memberOf wp.media.view
  8205. *
  8206. * @class
  8207. * @augments wp.media.view.Settings.AttachmentDisplay
  8208. * @augments wp.media.view.Settings
  8209. * @augments wp.media.View
  8210. * @augments wp.Backbone.View
  8211. * @augments Backbone.View
  8212. */
  8213. EmbedImage = AttachmentDisplay.extend(/** @lends wp.media.view.EmbedImage.prototype */{
  8214. className: 'embed-media-settings',
  8215. template: wp.template('embed-image-settings'),
  8216. initialize: function() {
  8217. /**
  8218. * Call `initialize` directly on parent class with passed arguments
  8219. */
  8220. AttachmentDisplay.prototype.initialize.apply( this, arguments );
  8221. this.listenTo( this.model, 'change:url', this.updateImage );
  8222. },
  8223. updateImage: function() {
  8224. this.$('img').attr( 'src', this.model.get('url') );
  8225. }
  8226. });
  8227. module.exports = EmbedImage;
  8228. /***/ }),
  8229. /* 99 */
  8230. /***/ (function(module, exports) {
  8231. var AttachmentDisplay = wp.media.view.Settings.AttachmentDisplay,
  8232. $ = jQuery,
  8233. ImageDetails;
  8234. /**
  8235. * wp.media.view.ImageDetails
  8236. *
  8237. * @memberOf wp.media.view
  8238. *
  8239. * @class
  8240. * @augments wp.media.view.Settings.AttachmentDisplay
  8241. * @augments wp.media.view.Settings
  8242. * @augments wp.media.View
  8243. * @augments wp.Backbone.View
  8244. * @augments Backbone.View
  8245. */
  8246. ImageDetails = AttachmentDisplay.extend(/** @lends wp.media.view.ImageDetails.prototype */{
  8247. className: 'image-details',
  8248. template: wp.template('image-details'),
  8249. events: _.defaults( AttachmentDisplay.prototype.events, {
  8250. 'click .edit-attachment': 'editAttachment',
  8251. 'click .replace-attachment': 'replaceAttachment',
  8252. 'click .advanced-toggle': 'onToggleAdvanced',
  8253. 'change [data-setting="customWidth"]': 'onCustomSize',
  8254. 'change [data-setting="customHeight"]': 'onCustomSize',
  8255. 'keyup [data-setting="customWidth"]': 'onCustomSize',
  8256. 'keyup [data-setting="customHeight"]': 'onCustomSize'
  8257. } ),
  8258. initialize: function() {
  8259. // used in AttachmentDisplay.prototype.updateLinkTo
  8260. this.options.attachment = this.model.attachment;
  8261. this.listenTo( this.model, 'change:url', this.updateUrl );
  8262. this.listenTo( this.model, 'change:link', this.toggleLinkSettings );
  8263. this.listenTo( this.model, 'change:size', this.toggleCustomSize );
  8264. AttachmentDisplay.prototype.initialize.apply( this, arguments );
  8265. },
  8266. prepare: function() {
  8267. var attachment = false;
  8268. if ( this.model.attachment ) {
  8269. attachment = this.model.attachment.toJSON();
  8270. }
  8271. return _.defaults({
  8272. model: this.model.toJSON(),
  8273. attachment: attachment
  8274. }, this.options );
  8275. },
  8276. render: function() {
  8277. var args = arguments;
  8278. if ( this.model.attachment && 'pending' === this.model.dfd.state() ) {
  8279. this.model.dfd
  8280. .done( _.bind( function() {
  8281. AttachmentDisplay.prototype.render.apply( this, args );
  8282. this.postRender();
  8283. }, this ) )
  8284. .fail( _.bind( function() {
  8285. this.model.attachment = false;
  8286. AttachmentDisplay.prototype.render.apply( this, args );
  8287. this.postRender();
  8288. }, this ) );
  8289. } else {
  8290. AttachmentDisplay.prototype.render.apply( this, arguments );
  8291. this.postRender();
  8292. }
  8293. return this;
  8294. },
  8295. postRender: function() {
  8296. setTimeout( _.bind( this.scrollToTop, this ), 10 );
  8297. this.toggleLinkSettings();
  8298. if ( window.getUserSetting( 'advImgDetails' ) === 'show' ) {
  8299. this.toggleAdvanced( true );
  8300. }
  8301. this.trigger( 'post-render' );
  8302. },
  8303. scrollToTop: function() {
  8304. this.$( '.embed-media-settings' ).scrollTop( 0 );
  8305. },
  8306. updateUrl: function() {
  8307. this.$( '.image img' ).attr( 'src', this.model.get( 'url' ) );
  8308. this.$( '.url' ).val( this.model.get( 'url' ) );
  8309. },
  8310. toggleLinkSettings: function() {
  8311. if ( this.model.get( 'link' ) === 'none' ) {
  8312. this.$( '.link-settings' ).addClass('hidden');
  8313. } else {
  8314. this.$( '.link-settings' ).removeClass('hidden');
  8315. }
  8316. },
  8317. toggleCustomSize: function() {
  8318. if ( this.model.get( 'size' ) !== 'custom' ) {
  8319. this.$( '.custom-size' ).addClass('hidden');
  8320. } else {
  8321. this.$( '.custom-size' ).removeClass('hidden');
  8322. }
  8323. },
  8324. onCustomSize: function( event ) {
  8325. var dimension = $( event.target ).data('setting'),
  8326. num = $( event.target ).val(),
  8327. value;
  8328. // Ignore bogus input
  8329. if ( ! /^\d+/.test( num ) || parseInt( num, 10 ) < 1 ) {
  8330. event.preventDefault();
  8331. return;
  8332. }
  8333. if ( dimension === 'customWidth' ) {
  8334. value = Math.round( 1 / this.model.get( 'aspectRatio' ) * num );
  8335. this.model.set( 'customHeight', value, { silent: true } );
  8336. this.$( '[data-setting="customHeight"]' ).val( value );
  8337. } else {
  8338. value = Math.round( this.model.get( 'aspectRatio' ) * num );
  8339. this.model.set( 'customWidth', value, { silent: true } );
  8340. this.$( '[data-setting="customWidth"]' ).val( value );
  8341. }
  8342. },
  8343. onToggleAdvanced: function( event ) {
  8344. event.preventDefault();
  8345. this.toggleAdvanced();
  8346. },
  8347. toggleAdvanced: function( show ) {
  8348. var $advanced = this.$el.find( '.advanced-section' ),
  8349. mode;
  8350. if ( $advanced.hasClass('advanced-visible') || show === false ) {
  8351. $advanced.removeClass('advanced-visible');
  8352. $advanced.find('.advanced-settings').addClass('hidden');
  8353. mode = 'hide';
  8354. } else {
  8355. $advanced.addClass('advanced-visible');
  8356. $advanced.find('.advanced-settings').removeClass('hidden');
  8357. mode = 'show';
  8358. }
  8359. window.setUserSetting( 'advImgDetails', mode );
  8360. },
  8361. editAttachment: function( event ) {
  8362. var editState = this.controller.states.get( 'edit-image' );
  8363. if ( window.imageEdit && editState ) {
  8364. event.preventDefault();
  8365. editState.set( 'image', this.model.attachment );
  8366. this.controller.setState( 'edit-image' );
  8367. }
  8368. },
  8369. replaceAttachment: function( event ) {
  8370. event.preventDefault();
  8371. this.controller.setState( 'replace-image' );
  8372. }
  8373. });
  8374. module.exports = ImageDetails;
  8375. /***/ }),
  8376. /* 100 */
  8377. /***/ (function(module, exports) {
  8378. var View = wp.media.View,
  8379. UploaderStatus = wp.media.view.UploaderStatus,
  8380. l10n = wp.media.view.l10n,
  8381. $ = jQuery,
  8382. Cropper;
  8383. /**
  8384. * wp.media.view.Cropper
  8385. *
  8386. * Uses the imgAreaSelect plugin to allow a user to crop an image.
  8387. *
  8388. * Takes imgAreaSelect options from
  8389. * wp.customize.HeaderControl.calculateImageSelectOptions via
  8390. * wp.customize.HeaderControl.openMM.
  8391. *
  8392. * @memberOf wp.media.view
  8393. *
  8394. * @class
  8395. * @augments wp.media.View
  8396. * @augments wp.Backbone.View
  8397. * @augments Backbone.View
  8398. */
  8399. Cropper = View.extend(/** @lends wp.media.view.Cropper.prototype */{
  8400. className: 'crop-content',
  8401. template: wp.template('crop-content'),
  8402. initialize: function() {
  8403. _.bindAll(this, 'onImageLoad');
  8404. },
  8405. ready: function() {
  8406. this.controller.frame.on('content:error:crop', this.onError, this);
  8407. this.$image = this.$el.find('.crop-image');
  8408. this.$image.on('load', this.onImageLoad);
  8409. $(window).on('resize.cropper', _.debounce(this.onImageLoad, 250));
  8410. },
  8411. remove: function() {
  8412. $(window).off('resize.cropper');
  8413. this.$el.remove();
  8414. this.$el.off();
  8415. View.prototype.remove.apply(this, arguments);
  8416. },
  8417. prepare: function() {
  8418. return {
  8419. title: l10n.cropYourImage,
  8420. url: this.options.attachment.get('url')
  8421. };
  8422. },
  8423. onImageLoad: function() {
  8424. var imgOptions = this.controller.get('imgSelectOptions'),
  8425. imgSelect;
  8426. if (typeof imgOptions === 'function') {
  8427. imgOptions = imgOptions(this.options.attachment, this.controller);
  8428. }
  8429. imgOptions = _.extend(imgOptions, {
  8430. parent: this.$el,
  8431. onInit: function() {
  8432. // Store the set ratio.
  8433. var setRatio = imgSelect.getOptions().aspectRatio;
  8434. // On mousedown, if no ratio is set and the Shift key is down, use a 1:1 ratio.
  8435. this.parent.children().on( 'mousedown touchstart', function( e ) {
  8436. // If no ratio is set and the shift key is down, use a 1:1 ratio.
  8437. if ( ! setRatio && e.shiftKey ) {
  8438. imgSelect.setOptions( {
  8439. aspectRatio: '1:1'
  8440. } );
  8441. }
  8442. } );
  8443. this.parent.children().on( 'mouseup touchend', function() {
  8444. // Restore the set ratio.
  8445. imgSelect.setOptions( {
  8446. aspectRatio: setRatio ? setRatio : false
  8447. } );
  8448. } );
  8449. }
  8450. } );
  8451. this.trigger('image-loaded');
  8452. imgSelect = this.controller.imgSelect = this.$image.imgAreaSelect(imgOptions);
  8453. },
  8454. onError: function() {
  8455. var filename = this.options.attachment.get('filename');
  8456. this.views.add( '.upload-errors', new wp.media.view.UploaderStatusError({
  8457. filename: UploaderStatus.prototype.filename(filename),
  8458. message: window._wpMediaViewsL10n.cropError
  8459. }), { at: 0 });
  8460. }
  8461. });
  8462. module.exports = Cropper;
  8463. /***/ }),
  8464. /* 101 */
  8465. /***/ (function(module, exports) {
  8466. var View = wp.media.view,
  8467. SiteIconCropper;
  8468. /**
  8469. * wp.media.view.SiteIconCropper
  8470. *
  8471. * Uses the imgAreaSelect plugin to allow a user to crop a Site Icon.
  8472. *
  8473. * Takes imgAreaSelect options from
  8474. * wp.customize.SiteIconControl.calculateImageSelectOptions.
  8475. *
  8476. * @memberOf wp.media.view
  8477. *
  8478. * @class
  8479. * @augments wp.media.view.Cropper
  8480. * @augments wp.media.View
  8481. * @augments wp.Backbone.View
  8482. * @augments Backbone.View
  8483. */
  8484. SiteIconCropper = View.Cropper.extend(/** @lends wp.media.view.SiteIconCropper.prototype */{
  8485. className: 'crop-content site-icon',
  8486. ready: function () {
  8487. View.Cropper.prototype.ready.apply( this, arguments );
  8488. this.$( '.crop-image' ).on( 'load', _.bind( this.addSidebar, this ) );
  8489. },
  8490. addSidebar: function() {
  8491. this.sidebar = new wp.media.view.Sidebar({
  8492. controller: this.controller
  8493. });
  8494. this.sidebar.set( 'preview', new wp.media.view.SiteIconPreview({
  8495. controller: this.controller,
  8496. attachment: this.options.attachment
  8497. }) );
  8498. this.controller.cropperView.views.add( this.sidebar );
  8499. }
  8500. });
  8501. module.exports = SiteIconCropper;
  8502. /***/ }),
  8503. /* 102 */
  8504. /***/ (function(module, exports) {
  8505. var View = wp.media.View,
  8506. $ = jQuery,
  8507. SiteIconPreview;
  8508. /**
  8509. * wp.media.view.SiteIconPreview
  8510. *
  8511. * Shows a preview of the Site Icon as a favicon and app icon while cropping.
  8512. *
  8513. * @memberOf wp.media.view
  8514. *
  8515. * @class
  8516. * @augments wp.media.View
  8517. * @augments wp.Backbone.View
  8518. * @augments Backbone.View
  8519. */
  8520. SiteIconPreview = View.extend(/** @lends wp.media.view.SiteIconPreview.prototype */{
  8521. className: 'site-icon-preview',
  8522. template: wp.template( 'site-icon-preview' ),
  8523. ready: function() {
  8524. this.controller.imgSelect.setOptions({
  8525. onInit: this.updatePreview,
  8526. onSelectChange: this.updatePreview
  8527. });
  8528. },
  8529. prepare: function() {
  8530. return {
  8531. url: this.options.attachment.get( 'url' )
  8532. };
  8533. },
  8534. updatePreview: function( img, coords ) {
  8535. var rx = 64 / coords.width,
  8536. ry = 64 / coords.height,
  8537. preview_rx = 16 / coords.width,
  8538. preview_ry = 16 / coords.height;
  8539. $( '#preview-app-icon' ).css({
  8540. width: Math.round(rx * this.imageWidth ) + 'px',
  8541. height: Math.round(ry * this.imageHeight ) + 'px',
  8542. marginLeft: '-' + Math.round(rx * coords.x1) + 'px',
  8543. marginTop: '-' + Math.round(ry * coords.y1) + 'px'
  8544. });
  8545. $( '#preview-favicon' ).css({
  8546. width: Math.round( preview_rx * this.imageWidth ) + 'px',
  8547. height: Math.round( preview_ry * this.imageHeight ) + 'px',
  8548. marginLeft: '-' + Math.round( preview_rx * coords.x1 ) + 'px',
  8549. marginTop: '-' + Math.floor( preview_ry* coords.y1 ) + 'px'
  8550. });
  8551. }
  8552. });
  8553. module.exports = SiteIconPreview;
  8554. /***/ }),
  8555. /* 103 */
  8556. /***/ (function(module, exports) {
  8557. var View = wp.media.View,
  8558. EditImage;
  8559. /**
  8560. * wp.media.view.EditImage
  8561. *
  8562. * @memberOf wp.media.view
  8563. *
  8564. * @class
  8565. * @augments wp.media.View
  8566. * @augments wp.Backbone.View
  8567. * @augments Backbone.View
  8568. */
  8569. EditImage = View.extend(/** @lends wp.media.view.EditImage.prototype */{
  8570. className: 'image-editor',
  8571. template: wp.template('image-editor'),
  8572. initialize: function( options ) {
  8573. this.editor = window.imageEdit;
  8574. this.controller = options.controller;
  8575. View.prototype.initialize.apply( this, arguments );
  8576. },
  8577. prepare: function() {
  8578. return this.model.toJSON();
  8579. },
  8580. loadEditor: function() {
  8581. this.editor.open( this.model.get( 'id' ), this.model.get( 'nonces' ).edit, this );
  8582. },
  8583. back: function() {
  8584. var lastState = this.controller.lastState();
  8585. this.controller.setState( lastState );
  8586. },
  8587. refresh: function() {
  8588. this.model.fetch();
  8589. },
  8590. save: function() {
  8591. var lastState = this.controller.lastState();
  8592. this.model.fetch().done( _.bind( function() {
  8593. this.controller.setState( lastState );
  8594. }, this ) );
  8595. }
  8596. });
  8597. module.exports = EditImage;
  8598. /***/ }),
  8599. /* 104 */
  8600. /***/ (function(module, exports) {
  8601. /**
  8602. * wp.media.view.Spinner
  8603. *
  8604. * Represents a spinner in the Media Library.
  8605. *
  8606. * @since 3.9.0
  8607. *
  8608. * @memberOf wp.media.view
  8609. *
  8610. * @class
  8611. * @augments wp.media.View
  8612. * @augments wp.Backbone.View
  8613. * @augments Backbone.View
  8614. */
  8615. var Spinner = wp.media.View.extend(/** @lends wp.media.view.Spinner.prototype */{
  8616. tagName: 'span',
  8617. className: 'spinner',
  8618. spinnerTimeout: false,
  8619. delay: 400,
  8620. /**
  8621. * Shows the spinner. Delays the visibility by the configured amount.
  8622. *
  8623. * @since 3.9.0
  8624. *
  8625. * @return {wp.media.view.Spinner} The spinner.
  8626. */
  8627. show: function() {
  8628. if ( ! this.spinnerTimeout ) {
  8629. this.spinnerTimeout = _.delay(function( $el ) {
  8630. $el.addClass( 'is-active' );
  8631. }, this.delay, this.$el );
  8632. }
  8633. return this;
  8634. },
  8635. /**
  8636. * Hides the spinner.
  8637. *
  8638. * @since 3.9.0
  8639. *
  8640. * @return {wp.media.view.Spinner} The spinner.
  8641. */
  8642. hide: function() {
  8643. this.$el.removeClass( 'is-active' );
  8644. this.spinnerTimeout = clearTimeout( this.spinnerTimeout );
  8645. return this;
  8646. }
  8647. });
  8648. module.exports = Spinner;
  8649. /***/ }),
  8650. /* 105 */
  8651. /***/ (function(module, exports) {
  8652. /**
  8653. * wp.media.view.Heading
  8654. *
  8655. * A reusable heading component for the media library
  8656. *
  8657. * Used to add accessibility friendly headers in the media library/modal.
  8658. *
  8659. * @class
  8660. * @augments wp.media.View
  8661. * @augments wp.Backbone.View
  8662. * @augments Backbone.View
  8663. */
  8664. var Heading = wp.media.View.extend( {
  8665. tagName: function() {
  8666. return this.options.level || 'h1';
  8667. },
  8668. className: 'media-views-heading',
  8669. initialize: function() {
  8670. if ( this.options.className ) {
  8671. this.$el.addClass( this.options.className );
  8672. }
  8673. this.text = this.options.text;
  8674. },
  8675. render: function() {
  8676. this.$el.html( this.text );
  8677. return this;
  8678. }
  8679. } );
  8680. module.exports = Heading;
  8681. /***/ })
  8682. /******/ ]));