index.cjs 354 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var state = require('@codemirror/state');
  4. var styleMod = require('style-mod');
  5. var w3cKeyname = require('w3c-keyname');
  6. function getSelection(root) {
  7. let target;
  8. // Browsers differ on whether shadow roots have a getSelection
  9. // method. If it exists, use that, otherwise, call it on the
  10. // document.
  11. if (root.nodeType == 11) { // Shadow root
  12. target = root.getSelection ? root : root.ownerDocument;
  13. }
  14. else {
  15. target = root;
  16. }
  17. return target.getSelection();
  18. }
  19. function contains(dom, node) {
  20. return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
  21. }
  22. function deepActiveElement() {
  23. let elt = document.activeElement;
  24. while (elt && elt.shadowRoot)
  25. elt = elt.shadowRoot.activeElement;
  26. return elt;
  27. }
  28. function hasSelection(dom, selection) {
  29. if (!selection.anchorNode)
  30. return false;
  31. try {
  32. // Firefox will raise 'permission denied' errors when accessing
  33. // properties of `sel.anchorNode` when it's in a generated CSS
  34. // element.
  35. return contains(dom, selection.anchorNode);
  36. }
  37. catch (_) {
  38. return false;
  39. }
  40. }
  41. function clientRectsFor(dom) {
  42. if (dom.nodeType == 3)
  43. return textRange(dom, 0, dom.nodeValue.length).getClientRects();
  44. else if (dom.nodeType == 1)
  45. return dom.getClientRects();
  46. else
  47. return [];
  48. }
  49. // Scans forward and backward through DOM positions equivalent to the
  50. // given one to see if the two are in the same place (i.e. after a
  51. // text node vs at the end of that text node)
  52. function isEquivalentPosition(node, off, targetNode, targetOff) {
  53. return targetNode ? (scanFor(node, off, targetNode, targetOff, -1) ||
  54. scanFor(node, off, targetNode, targetOff, 1)) : false;
  55. }
  56. function domIndex(node) {
  57. for (var index = 0;; index++) {
  58. node = node.previousSibling;
  59. if (!node)
  60. return index;
  61. }
  62. }
  63. function scanFor(node, off, targetNode, targetOff, dir) {
  64. for (;;) {
  65. if (node == targetNode && off == targetOff)
  66. return true;
  67. if (off == (dir < 0 ? 0 : maxOffset(node))) {
  68. if (node.nodeName == "DIV")
  69. return false;
  70. let parent = node.parentNode;
  71. if (!parent || parent.nodeType != 1)
  72. return false;
  73. off = domIndex(node) + (dir < 0 ? 0 : 1);
  74. node = parent;
  75. }
  76. else if (node.nodeType == 1) {
  77. node = node.childNodes[off + (dir < 0 ? -1 : 0)];
  78. if (node.nodeType == 1 && node.contentEditable == "false")
  79. return false;
  80. off = dir < 0 ? maxOffset(node) : 0;
  81. }
  82. else {
  83. return false;
  84. }
  85. }
  86. }
  87. function maxOffset(node) {
  88. return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
  89. }
  90. const Rect0 = { left: 0, right: 0, top: 0, bottom: 0 };
  91. function flattenRect(rect, left) {
  92. let x = left ? rect.left : rect.right;
  93. return { left: x, right: x, top: rect.top, bottom: rect.bottom };
  94. }
  95. function windowRect(win) {
  96. return { left: 0, right: win.innerWidth,
  97. top: 0, bottom: win.innerHeight };
  98. }
  99. function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
  100. let doc = dom.ownerDocument, win = doc.defaultView;
  101. for (let cur = dom; cur;) {
  102. if (cur.nodeType == 1) { // Element
  103. let bounding, top = cur == doc.body;
  104. if (top) {
  105. bounding = windowRect(win);
  106. }
  107. else {
  108. if (cur.scrollHeight <= cur.clientHeight && cur.scrollWidth <= cur.clientWidth) {
  109. cur = cur.parentNode;
  110. continue;
  111. }
  112. let rect = cur.getBoundingClientRect();
  113. // Make sure scrollbar width isn't included in the rectangle
  114. bounding = { left: rect.left, right: rect.left + cur.clientWidth,
  115. top: rect.top, bottom: rect.top + cur.clientHeight };
  116. }
  117. let moveX = 0, moveY = 0;
  118. if (y == "nearest") {
  119. if (rect.top < bounding.top) {
  120. moveY = -(bounding.top - rect.top + yMargin);
  121. if (side > 0 && rect.bottom > bounding.bottom + moveY)
  122. moveY = rect.bottom - bounding.bottom + moveY + yMargin;
  123. }
  124. else if (rect.bottom > bounding.bottom) {
  125. moveY = rect.bottom - bounding.bottom + yMargin;
  126. if (side < 0 && (rect.top - moveY) < bounding.top)
  127. moveY = -(bounding.top + moveY - rect.top + yMargin);
  128. }
  129. }
  130. else {
  131. let rectHeight = rect.bottom - rect.top, boundingHeight = bounding.bottom - bounding.top;
  132. let targetTop = y == "center" && rectHeight <= boundingHeight ? rect.top + rectHeight / 2 - boundingHeight / 2 :
  133. y == "start" || y == "center" && side < 0 ? rect.top - yMargin :
  134. rect.bottom - boundingHeight + yMargin;
  135. moveY = targetTop - bounding.top;
  136. }
  137. if (x == "nearest") {
  138. if (rect.left < bounding.left) {
  139. moveX = -(bounding.left - rect.left + xMargin);
  140. if (side > 0 && rect.right > bounding.right + moveX)
  141. moveX = rect.right - bounding.right + moveX + xMargin;
  142. }
  143. else if (rect.right > bounding.right) {
  144. moveX = rect.right - bounding.right + xMargin;
  145. if (side < 0 && rect.left < bounding.left + moveX)
  146. moveX = -(bounding.left + moveX - rect.left + xMargin);
  147. }
  148. }
  149. else {
  150. let targetLeft = x == "center" ? rect.left + (rect.right - rect.left) / 2 - (bounding.right - bounding.left) / 2 :
  151. (x == "start") == ltr ? rect.left - xMargin :
  152. rect.right - (bounding.right - bounding.left) + xMargin;
  153. moveX = targetLeft - bounding.left;
  154. }
  155. if (moveX || moveY) {
  156. if (top) {
  157. win.scrollBy(moveX, moveY);
  158. }
  159. else {
  160. if (moveY) {
  161. let start = cur.scrollTop;
  162. cur.scrollTop += moveY;
  163. moveY = cur.scrollTop - start;
  164. }
  165. if (moveX) {
  166. let start = cur.scrollLeft;
  167. cur.scrollLeft += moveX;
  168. moveX = cur.scrollLeft - start;
  169. }
  170. rect = { left: rect.left - moveX, top: rect.top - moveY,
  171. right: rect.right - moveX, bottom: rect.bottom - moveY };
  172. }
  173. }
  174. if (top)
  175. break;
  176. cur = cur.assignedSlot || cur.parentNode;
  177. x = y = "nearest";
  178. }
  179. else if (cur.nodeType == 11) { // A shadow root
  180. cur = cur.host;
  181. }
  182. else {
  183. break;
  184. }
  185. }
  186. }
  187. class DOMSelectionState {
  188. constructor() {
  189. this.anchorNode = null;
  190. this.anchorOffset = 0;
  191. this.focusNode = null;
  192. this.focusOffset = 0;
  193. }
  194. eq(domSel) {
  195. return this.anchorNode == domSel.anchorNode && this.anchorOffset == domSel.anchorOffset &&
  196. this.focusNode == domSel.focusNode && this.focusOffset == domSel.focusOffset;
  197. }
  198. setRange(range) {
  199. this.set(range.anchorNode, range.anchorOffset, range.focusNode, range.focusOffset);
  200. }
  201. set(anchorNode, anchorOffset, focusNode, focusOffset) {
  202. this.anchorNode = anchorNode;
  203. this.anchorOffset = anchorOffset;
  204. this.focusNode = focusNode;
  205. this.focusOffset = focusOffset;
  206. }
  207. }
  208. let preventScrollSupported = null;
  209. // Feature-detects support for .focus({preventScroll: true}), and uses
  210. // a fallback kludge when not supported.
  211. function focusPreventScroll(dom) {
  212. if (dom.setActive)
  213. return dom.setActive(); // in IE
  214. if (preventScrollSupported)
  215. return dom.focus(preventScrollSupported);
  216. let stack = [];
  217. for (let cur = dom; cur; cur = cur.parentNode) {
  218. stack.push(cur, cur.scrollTop, cur.scrollLeft);
  219. if (cur == cur.ownerDocument)
  220. break;
  221. }
  222. dom.focus(preventScrollSupported == null ? {
  223. get preventScroll() {
  224. preventScrollSupported = { preventScroll: true };
  225. return true;
  226. }
  227. } : undefined);
  228. if (!preventScrollSupported) {
  229. preventScrollSupported = false;
  230. for (let i = 0; i < stack.length;) {
  231. let elt = stack[i++], top = stack[i++], left = stack[i++];
  232. if (elt.scrollTop != top)
  233. elt.scrollTop = top;
  234. if (elt.scrollLeft != left)
  235. elt.scrollLeft = left;
  236. }
  237. }
  238. }
  239. let scratchRange;
  240. function textRange(node, from, to = from) {
  241. let range = scratchRange || (scratchRange = document.createRange());
  242. range.setEnd(node, to);
  243. range.setStart(node, from);
  244. return range;
  245. }
  246. function dispatchKey(elt, name, code) {
  247. let options = { key: name, code: name, keyCode: code, which: code, cancelable: true };
  248. let down = new KeyboardEvent("keydown", options);
  249. down.synthetic = true;
  250. elt.dispatchEvent(down);
  251. let up = new KeyboardEvent("keyup", options);
  252. up.synthetic = true;
  253. elt.dispatchEvent(up);
  254. return down.defaultPrevented || up.defaultPrevented;
  255. }
  256. function getRoot(node) {
  257. while (node) {
  258. if (node && (node.nodeType == 9 || node.nodeType == 11 && node.host))
  259. return node;
  260. node = node.assignedSlot || node.parentNode;
  261. }
  262. return null;
  263. }
  264. function clearAttributes(node) {
  265. while (node.attributes.length)
  266. node.removeAttributeNode(node.attributes[0]);
  267. }
  268. class DOMPos {
  269. constructor(node, offset, precise = true) {
  270. this.node = node;
  271. this.offset = offset;
  272. this.precise = precise;
  273. }
  274. static before(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom), precise); }
  275. static after(dom, precise) { return new DOMPos(dom.parentNode, domIndex(dom) + 1, precise); }
  276. }
  277. const noChildren = [];
  278. class ContentView {
  279. constructor() {
  280. this.parent = null;
  281. this.dom = null;
  282. this.dirty = 2 /* Node */;
  283. }
  284. get editorView() {
  285. if (!this.parent)
  286. throw new Error("Accessing view in orphan content view");
  287. return this.parent.editorView;
  288. }
  289. get overrideDOMText() { return null; }
  290. get posAtStart() {
  291. return this.parent ? this.parent.posBefore(this) : 0;
  292. }
  293. get posAtEnd() {
  294. return this.posAtStart + this.length;
  295. }
  296. posBefore(view) {
  297. let pos = this.posAtStart;
  298. for (let child of this.children) {
  299. if (child == view)
  300. return pos;
  301. pos += child.length + child.breakAfter;
  302. }
  303. throw new RangeError("Invalid child in posBefore");
  304. }
  305. posAfter(view) {
  306. return this.posBefore(view) + view.length;
  307. }
  308. // Will return a rectangle directly before (when side < 0), after
  309. // (side > 0) or directly on (when the browser supports it) the
  310. // given position.
  311. coordsAt(_pos, _side) { return null; }
  312. sync(track) {
  313. if (this.dirty & 2 /* Node */) {
  314. let parent = this.dom;
  315. let prev = null, next;
  316. for (let child of this.children) {
  317. if (child.dirty) {
  318. if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
  319. let contentView = ContentView.get(next);
  320. if (!contentView || !contentView.parent && contentView.constructor == child.constructor)
  321. child.reuseDOM(next);
  322. }
  323. child.sync(track);
  324. child.dirty = 0 /* Not */;
  325. }
  326. next = prev ? prev.nextSibling : parent.firstChild;
  327. if (track && !track.written && track.node == parent && next != child.dom)
  328. track.written = true;
  329. if (child.dom.parentNode == parent) {
  330. while (next && next != child.dom)
  331. next = rm$1(next);
  332. }
  333. else {
  334. parent.insertBefore(child.dom, next);
  335. }
  336. prev = child.dom;
  337. }
  338. next = prev ? prev.nextSibling : parent.firstChild;
  339. if (next && track && track.node == parent)
  340. track.written = true;
  341. while (next)
  342. next = rm$1(next);
  343. }
  344. else if (this.dirty & 1 /* Child */) {
  345. for (let child of this.children)
  346. if (child.dirty) {
  347. child.sync(track);
  348. child.dirty = 0 /* Not */;
  349. }
  350. }
  351. }
  352. reuseDOM(_dom) { }
  353. localPosFromDOM(node, offset) {
  354. let after;
  355. if (node == this.dom) {
  356. after = this.dom.childNodes[offset];
  357. }
  358. else {
  359. let bias = maxOffset(node) == 0 ? 0 : offset == 0 ? -1 : 1;
  360. for (;;) {
  361. let parent = node.parentNode;
  362. if (parent == this.dom)
  363. break;
  364. if (bias == 0 && parent.firstChild != parent.lastChild) {
  365. if (node == parent.firstChild)
  366. bias = -1;
  367. else
  368. bias = 1;
  369. }
  370. node = parent;
  371. }
  372. if (bias < 0)
  373. after = node;
  374. else
  375. after = node.nextSibling;
  376. }
  377. if (after == this.dom.firstChild)
  378. return 0;
  379. while (after && !ContentView.get(after))
  380. after = after.nextSibling;
  381. if (!after)
  382. return this.length;
  383. for (let i = 0, pos = 0;; i++) {
  384. let child = this.children[i];
  385. if (child.dom == after)
  386. return pos;
  387. pos += child.length + child.breakAfter;
  388. }
  389. }
  390. domBoundsAround(from, to, offset = 0) {
  391. let fromI = -1, fromStart = -1, toI = -1, toEnd = -1;
  392. for (let i = 0, pos = offset, prevEnd = offset; i < this.children.length; i++) {
  393. let child = this.children[i], end = pos + child.length;
  394. if (pos < from && end > to)
  395. return child.domBoundsAround(from, to, pos);
  396. if (end >= from && fromI == -1) {
  397. fromI = i;
  398. fromStart = pos;
  399. }
  400. if (pos > to && child.dom.parentNode == this.dom) {
  401. toI = i;
  402. toEnd = prevEnd;
  403. break;
  404. }
  405. prevEnd = end;
  406. pos = end + child.breakAfter;
  407. }
  408. return { from: fromStart, to: toEnd < 0 ? offset + this.length : toEnd,
  409. startDOM: (fromI ? this.children[fromI - 1].dom.nextSibling : null) || this.dom.firstChild,
  410. endDOM: toI < this.children.length && toI >= 0 ? this.children[toI].dom : null };
  411. }
  412. markDirty(andParent = false) {
  413. this.dirty |= 2 /* Node */;
  414. this.markParentsDirty(andParent);
  415. }
  416. markParentsDirty(childList) {
  417. for (let parent = this.parent; parent; parent = parent.parent) {
  418. if (childList)
  419. parent.dirty |= 2 /* Node */;
  420. if (parent.dirty & 1 /* Child */)
  421. return;
  422. parent.dirty |= 1 /* Child */;
  423. childList = false;
  424. }
  425. }
  426. setParent(parent) {
  427. if (this.parent != parent) {
  428. this.parent = parent;
  429. if (this.dirty)
  430. this.markParentsDirty(true);
  431. }
  432. }
  433. setDOM(dom) {
  434. if (this.dom)
  435. this.dom.cmView = null;
  436. this.dom = dom;
  437. dom.cmView = this;
  438. }
  439. get rootView() {
  440. for (let v = this;;) {
  441. let parent = v.parent;
  442. if (!parent)
  443. return v;
  444. v = parent;
  445. }
  446. }
  447. replaceChildren(from, to, children = noChildren) {
  448. this.markDirty();
  449. for (let i = from; i < to; i++) {
  450. let child = this.children[i];
  451. if (child.parent == this)
  452. child.destroy();
  453. }
  454. this.children.splice(from, to - from, ...children);
  455. for (let i = 0; i < children.length; i++)
  456. children[i].setParent(this);
  457. }
  458. ignoreMutation(_rec) { return false; }
  459. ignoreEvent(_event) { return false; }
  460. childCursor(pos = this.length) {
  461. return new ChildCursor(this.children, pos, this.children.length);
  462. }
  463. childPos(pos, bias = 1) {
  464. return this.childCursor().findPos(pos, bias);
  465. }
  466. toString() {
  467. let name = this.constructor.name.replace("View", "");
  468. return name + (this.children.length ? "(" + this.children.join() + ")" :
  469. this.length ? "[" + (name == "Text" ? this.text : this.length) + "]" : "") +
  470. (this.breakAfter ? "#" : "");
  471. }
  472. static get(node) { return node.cmView; }
  473. get isEditable() { return true; }
  474. merge(from, to, source, hasStart, openStart, openEnd) {
  475. return false;
  476. }
  477. become(other) { return false; }
  478. // When this is a zero-length view with a side, this should return a
  479. // number <= 0 to indicate it is before its position, or a
  480. // number > 0 when after its position.
  481. getSide() { return 0; }
  482. destroy() {
  483. this.parent = null;
  484. }
  485. }
  486. ContentView.prototype.breakAfter = 0;
  487. // Remove a DOM node and return its next sibling.
  488. function rm$1(dom) {
  489. let next = dom.nextSibling;
  490. dom.parentNode.removeChild(dom);
  491. return next;
  492. }
  493. class ChildCursor {
  494. constructor(children, pos, i) {
  495. this.children = children;
  496. this.pos = pos;
  497. this.i = i;
  498. this.off = 0;
  499. }
  500. findPos(pos, bias = 1) {
  501. for (;;) {
  502. if (pos > this.pos || pos == this.pos &&
  503. (bias > 0 || this.i == 0 || this.children[this.i - 1].breakAfter)) {
  504. this.off = pos - this.pos;
  505. return this;
  506. }
  507. let next = this.children[--this.i];
  508. this.pos -= next.length + next.breakAfter;
  509. }
  510. }
  511. }
  512. function replaceRange(parent, fromI, fromOff, toI, toOff, insert, breakAtStart, openStart, openEnd) {
  513. let { children } = parent;
  514. let before = children.length ? children[fromI] : null;
  515. let last = insert.length ? insert[insert.length - 1] : null;
  516. let breakAtEnd = last ? last.breakAfter : breakAtStart;
  517. // Change within a single child
  518. if (fromI == toI && before && !breakAtStart && !breakAtEnd && insert.length < 2 &&
  519. before.merge(fromOff, toOff, insert.length ? last : null, fromOff == 0, openStart, openEnd))
  520. return;
  521. if (toI < children.length) {
  522. let after = children[toI];
  523. // Make sure the end of the child after the update is preserved in `after`
  524. if (after && toOff < after.length) {
  525. // If we're splitting a child, separate part of it to avoid that
  526. // being mangled when updating the child before the update.
  527. if (fromI == toI) {
  528. after = after.split(toOff);
  529. toOff = 0;
  530. }
  531. // If the element after the replacement should be merged with
  532. // the last replacing element, update `content`
  533. if (!breakAtEnd && last && after.merge(0, toOff, last, true, 0, openEnd)) {
  534. insert[insert.length - 1] = after;
  535. }
  536. else {
  537. // Remove the start of the after element, if necessary, and
  538. // add it to `content`.
  539. if (toOff)
  540. after.merge(0, toOff, null, false, 0, openEnd);
  541. insert.push(after);
  542. }
  543. }
  544. else if (after === null || after === void 0 ? void 0 : after.breakAfter) {
  545. // The element at `toI` is entirely covered by this range.
  546. // Preserve its line break, if any.
  547. if (last)
  548. last.breakAfter = 1;
  549. else
  550. breakAtStart = 1;
  551. }
  552. // Since we've handled the next element from the current elements
  553. // now, make sure `toI` points after that.
  554. toI++;
  555. }
  556. if (before) {
  557. before.breakAfter = breakAtStart;
  558. if (fromOff > 0) {
  559. if (!breakAtStart && insert.length && before.merge(fromOff, before.length, insert[0], false, openStart, 0)) {
  560. before.breakAfter = insert.shift().breakAfter;
  561. }
  562. else if (fromOff < before.length || before.children.length && before.children[before.children.length - 1].length == 0) {
  563. before.merge(fromOff, before.length, null, false, openStart, 0);
  564. }
  565. fromI++;
  566. }
  567. }
  568. // Try to merge widgets on the boundaries of the replacement
  569. while (fromI < toI && insert.length) {
  570. if (children[toI - 1].become(insert[insert.length - 1])) {
  571. toI--;
  572. insert.pop();
  573. openEnd = insert.length ? 0 : openStart;
  574. }
  575. else if (children[fromI].become(insert[0])) {
  576. fromI++;
  577. insert.shift();
  578. openStart = insert.length ? 0 : openEnd;
  579. }
  580. else {
  581. break;
  582. }
  583. }
  584. if (!insert.length && fromI && toI < children.length && !children[fromI - 1].breakAfter &&
  585. children[toI].merge(0, 0, children[fromI - 1], false, openStart, openEnd))
  586. fromI--;
  587. if (fromI < toI || insert.length)
  588. parent.replaceChildren(fromI, toI, insert);
  589. }
  590. function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
  591. let cur = parent.childCursor();
  592. let { i: toI, off: toOff } = cur.findPos(to, 1);
  593. let { i: fromI, off: fromOff } = cur.findPos(from, -1);
  594. let dLen = from - to;
  595. for (let view of insert)
  596. dLen += view.length;
  597. parent.length += dLen;
  598. replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
  599. }
  600. let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
  601. let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
  602. const ie_edge = /Edge\/(\d+)/.exec(nav.userAgent);
  603. const ie_upto10 = /MSIE \d/.test(nav.userAgent);
  604. const ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(nav.userAgent);
  605. const ie = !!(ie_upto10 || ie_11up || ie_edge);
  606. const gecko = !ie && /gecko\/(\d+)/i.test(nav.userAgent);
  607. const chrome = !ie && /Chrome\/(\d+)/.exec(nav.userAgent);
  608. const webkit = "webkitFontSmoothing" in doc.documentElement.style;
  609. const safari = !ie && /Apple Computer/.test(nav.vendor);
  610. const ios = safari && (/Mobile\/\w+/.test(nav.userAgent) || nav.maxTouchPoints > 2);
  611. var browser = {
  612. mac: ios || /Mac/.test(nav.platform),
  613. windows: /Win/.test(nav.platform),
  614. linux: /Linux|X11/.test(nav.platform),
  615. ie,
  616. ie_version: ie_upto10 ? doc.documentMode || 6 : ie_11up ? +ie_11up[1] : ie_edge ? +ie_edge[1] : 0,
  617. gecko,
  618. gecko_version: gecko ? +(/Firefox\/(\d+)/.exec(nav.userAgent) || [0, 0])[1] : 0,
  619. chrome: !!chrome,
  620. chrome_version: chrome ? +chrome[1] : 0,
  621. ios,
  622. android: /Android\b/.test(nav.userAgent),
  623. webkit,
  624. safari,
  625. webkit_version: webkit ? +(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1] : 0,
  626. tabSize: doc.documentElement.style.tabSize != null ? "tab-size" : "-moz-tab-size"
  627. };
  628. const MaxJoinLen = 256;
  629. class TextView extends ContentView {
  630. constructor(text) {
  631. super();
  632. this.text = text;
  633. }
  634. get length() { return this.text.length; }
  635. createDOM(textDOM) {
  636. this.setDOM(textDOM || document.createTextNode(this.text));
  637. }
  638. sync(track) {
  639. if (!this.dom)
  640. this.createDOM();
  641. if (this.dom.nodeValue != this.text) {
  642. if (track && track.node == this.dom)
  643. track.written = true;
  644. this.dom.nodeValue = this.text;
  645. }
  646. }
  647. reuseDOM(dom) {
  648. if (dom.nodeType == 3)
  649. this.createDOM(dom);
  650. }
  651. merge(from, to, source) {
  652. if (source && (!(source instanceof TextView) || this.length - (to - from) + source.length > MaxJoinLen))
  653. return false;
  654. this.text = this.text.slice(0, from) + (source ? source.text : "") + this.text.slice(to);
  655. this.markDirty();
  656. return true;
  657. }
  658. split(from) {
  659. let result = new TextView(this.text.slice(from));
  660. this.text = this.text.slice(0, from);
  661. this.markDirty();
  662. return result;
  663. }
  664. localPosFromDOM(node, offset) {
  665. return node == this.dom ? offset : offset ? this.text.length : 0;
  666. }
  667. domAtPos(pos) { return new DOMPos(this.dom, pos); }
  668. domBoundsAround(_from, _to, offset) {
  669. return { from: offset, to: offset + this.length, startDOM: this.dom, endDOM: this.dom.nextSibling };
  670. }
  671. coordsAt(pos, side) {
  672. return textCoords(this.dom, pos, side);
  673. }
  674. }
  675. class MarkView extends ContentView {
  676. constructor(mark, children = [], length = 0) {
  677. super();
  678. this.mark = mark;
  679. this.children = children;
  680. this.length = length;
  681. for (let ch of children)
  682. ch.setParent(this);
  683. }
  684. setAttrs(dom) {
  685. clearAttributes(dom);
  686. if (this.mark.class)
  687. dom.className = this.mark.class;
  688. if (this.mark.attrs)
  689. for (let name in this.mark.attrs)
  690. dom.setAttribute(name, this.mark.attrs[name]);
  691. return dom;
  692. }
  693. reuseDOM(node) {
  694. if (node.nodeName == this.mark.tagName.toUpperCase()) {
  695. this.setDOM(node);
  696. this.dirty |= 4 /* Attrs */ | 2 /* Node */;
  697. }
  698. }
  699. sync(track) {
  700. if (!this.dom)
  701. this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
  702. else if (this.dirty & 4 /* Attrs */)
  703. this.setAttrs(this.dom);
  704. super.sync(track);
  705. }
  706. merge(from, to, source, _hasStart, openStart, openEnd) {
  707. if (source && (!(source instanceof MarkView && source.mark.eq(this.mark)) ||
  708. (from && openStart <= 0) || (to < this.length && openEnd <= 0)))
  709. return false;
  710. mergeChildrenInto(this, from, to, source ? source.children : [], openStart - 1, openEnd - 1);
  711. this.markDirty();
  712. return true;
  713. }
  714. split(from) {
  715. let result = [], off = 0, detachFrom = -1, i = 0;
  716. for (let elt of this.children) {
  717. let end = off + elt.length;
  718. if (end > from)
  719. result.push(off < from ? elt.split(from - off) : elt);
  720. if (detachFrom < 0 && off >= from)
  721. detachFrom = i;
  722. off = end;
  723. i++;
  724. }
  725. let length = this.length - from;
  726. this.length = from;
  727. if (detachFrom > -1) {
  728. this.children.length = detachFrom;
  729. this.markDirty();
  730. }
  731. return new MarkView(this.mark, result, length);
  732. }
  733. domAtPos(pos) {
  734. return inlineDOMAtPos(this.dom, this.children, pos);
  735. }
  736. coordsAt(pos, side) {
  737. return coordsInChildren(this, pos, side);
  738. }
  739. }
  740. function textCoords(text, pos, side) {
  741. let length = text.nodeValue.length;
  742. if (pos > length)
  743. pos = length;
  744. let from = pos, to = pos, flatten = 0;
  745. if (pos == 0 && side < 0 || pos == length && side >= 0) {
  746. if (!(browser.chrome || browser.gecko)) { // These browsers reliably return valid rectangles for empty ranges
  747. if (pos) {
  748. from--;
  749. flatten = 1;
  750. } // FIXME this is wrong in RTL text
  751. else if (to < length) {
  752. to++;
  753. flatten = -1;
  754. }
  755. }
  756. }
  757. else {
  758. if (side < 0)
  759. from--;
  760. else if (to < length)
  761. to++;
  762. }
  763. let rects = textRange(text, from, to).getClientRects();
  764. if (!rects.length)
  765. return Rect0;
  766. let rect = rects[(flatten ? flatten < 0 : side >= 0) ? 0 : rects.length - 1];
  767. if (browser.safari && !flatten && rect.width == 0)
  768. rect = Array.prototype.find.call(rects, r => r.width) || rect;
  769. return flatten ? flattenRect(rect, flatten < 0) : rect || null;
  770. }
  771. // Also used for collapsed ranges that don't have a placeholder widget!
  772. class WidgetView extends ContentView {
  773. constructor(widget, length, side) {
  774. super();
  775. this.widget = widget;
  776. this.length = length;
  777. this.side = side;
  778. this.prevWidget = null;
  779. }
  780. static create(widget, length, side) {
  781. return new (widget.customView || WidgetView)(widget, length, side);
  782. }
  783. split(from) {
  784. let result = WidgetView.create(this.widget, this.length - from, this.side);
  785. this.length -= from;
  786. return result;
  787. }
  788. sync() {
  789. if (!this.dom || !this.widget.updateDOM(this.dom)) {
  790. if (this.dom && this.prevWidget)
  791. this.prevWidget.destroy(this.dom);
  792. this.prevWidget = null;
  793. this.setDOM(this.widget.toDOM(this.editorView));
  794. this.dom.contentEditable = "false";
  795. }
  796. }
  797. getSide() { return this.side; }
  798. merge(from, to, source, hasStart, openStart, openEnd) {
  799. if (source && (!(source instanceof WidgetView) || !this.widget.compare(source.widget) ||
  800. from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
  801. return false;
  802. this.length = from + (source ? source.length : 0) + (this.length - to);
  803. return true;
  804. }
  805. become(other) {
  806. if (other.length == this.length && other instanceof WidgetView && other.side == this.side) {
  807. if (this.widget.constructor == other.widget.constructor) {
  808. if (!this.widget.eq(other.widget))
  809. this.markDirty(true);
  810. if (this.dom && !this.prevWidget)
  811. this.prevWidget = this.widget;
  812. this.widget = other.widget;
  813. return true;
  814. }
  815. }
  816. return false;
  817. }
  818. ignoreMutation() { return true; }
  819. ignoreEvent(event) { return this.widget.ignoreEvent(event); }
  820. get overrideDOMText() {
  821. if (this.length == 0)
  822. return state.Text.empty;
  823. let top = this;
  824. while (top.parent)
  825. top = top.parent;
  826. let view = top.editorView, text = view && view.state.doc, start = this.posAtStart;
  827. return text ? text.slice(start, start + this.length) : state.Text.empty;
  828. }
  829. domAtPos(pos) {
  830. return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length);
  831. }
  832. domBoundsAround() { return null; }
  833. coordsAt(pos, side) {
  834. let rects = this.dom.getClientRects(), rect = null;
  835. if (!rects.length)
  836. return Rect0;
  837. for (let i = pos > 0 ? rects.length - 1 : 0;; i += (pos > 0 ? -1 : 1)) {
  838. rect = rects[i];
  839. if (pos > 0 ? i == 0 : i == rects.length - 1 || rect.top < rect.bottom)
  840. break;
  841. }
  842. return (pos == 0 && side > 0 || pos == this.length && side <= 0) ? rect : flattenRect(rect, pos == 0);
  843. }
  844. get isEditable() { return false; }
  845. destroy() {
  846. super.destroy();
  847. if (this.dom)
  848. this.widget.destroy(this.dom);
  849. }
  850. }
  851. class CompositionView extends WidgetView {
  852. domAtPos(pos) {
  853. let { topView, text } = this.widget;
  854. if (!topView)
  855. return new DOMPos(text, Math.min(pos, text.nodeValue.length));
  856. return scanCompositionTree(pos, 0, topView, text, (v, p) => v.domAtPos(p), p => new DOMPos(text, Math.min(p, text.nodeValue.length)));
  857. }
  858. sync() { this.setDOM(this.widget.toDOM()); }
  859. localPosFromDOM(node, offset) {
  860. let { topView, text } = this.widget;
  861. if (!topView)
  862. return Math.min(offset, this.length);
  863. return posFromDOMInCompositionTree(node, offset, topView, text);
  864. }
  865. ignoreMutation() { return false; }
  866. get overrideDOMText() { return null; }
  867. coordsAt(pos, side) {
  868. let { topView, text } = this.widget;
  869. if (!topView)
  870. return textCoords(text, pos, side);
  871. return scanCompositionTree(pos, side, topView, text, (v, pos, side) => v.coordsAt(pos, side), (pos, side) => textCoords(text, pos, side));
  872. }
  873. destroy() {
  874. var _a;
  875. super.destroy();
  876. (_a = this.widget.topView) === null || _a === void 0 ? void 0 : _a.destroy();
  877. }
  878. get isEditable() { return true; }
  879. }
  880. // Uses the old structure of a chunk of content view frozen for
  881. // composition to try and find a reasonable DOM location for the given
  882. // offset.
  883. function scanCompositionTree(pos, side, view, text, enterView, fromText) {
  884. if (view instanceof MarkView) {
  885. for (let child of view.children) {
  886. let hasComp = contains(child.dom, text);
  887. let len = hasComp ? text.nodeValue.length : child.length;
  888. if (pos < len || pos == len && child.getSide() <= 0)
  889. return hasComp ? scanCompositionTree(pos, side, child, text, enterView, fromText) : enterView(child, pos, side);
  890. pos -= len;
  891. }
  892. return enterView(view, view.length, -1);
  893. }
  894. else if (view.dom == text) {
  895. return fromText(pos, side);
  896. }
  897. else {
  898. return enterView(view, pos, side);
  899. }
  900. }
  901. function posFromDOMInCompositionTree(node, offset, view, text) {
  902. if (view instanceof MarkView) {
  903. for (let child of view.children) {
  904. let pos = 0, hasComp = contains(child.dom, text);
  905. if (contains(child.dom, node))
  906. return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, child, text) : child.localPosFromDOM(node, offset));
  907. pos += hasComp ? text.nodeValue.length : child.length;
  908. }
  909. }
  910. else if (view.dom == text) {
  911. return Math.min(offset, text.nodeValue.length);
  912. }
  913. return view.localPosFromDOM(node, offset);
  914. }
  915. // These are drawn around uneditable widgets to avoid a number of
  916. // browser bugs that show up when the cursor is directly next to
  917. // uneditable inline content.
  918. class WidgetBufferView extends ContentView {
  919. constructor(side) {
  920. super();
  921. this.side = side;
  922. }
  923. get length() { return 0; }
  924. merge() { return false; }
  925. become(other) {
  926. return other instanceof WidgetBufferView && other.side == this.side;
  927. }
  928. split() { return new WidgetBufferView(this.side); }
  929. sync() {
  930. if (!this.dom) {
  931. let dom = document.createElement("img");
  932. dom.className = "cm-widgetBuffer";
  933. dom.setAttribute("aria-hidden", "true");
  934. this.setDOM(dom);
  935. }
  936. }
  937. getSide() { return this.side; }
  938. domAtPos(pos) { return DOMPos.before(this.dom); }
  939. localPosFromDOM() { return 0; }
  940. domBoundsAround() { return null; }
  941. coordsAt(pos) {
  942. let imgRect = this.dom.getBoundingClientRect();
  943. // Since the <img> height doesn't correspond to text height, try
  944. // to borrow the height from some sibling node.
  945. let siblingRect = inlineSiblingRect(this, this.side > 0 ? -1 : 1);
  946. return siblingRect && siblingRect.top < imgRect.bottom && siblingRect.bottom > imgRect.top
  947. ? { left: imgRect.left, right: imgRect.right, top: siblingRect.top, bottom: siblingRect.bottom } : imgRect;
  948. }
  949. get overrideDOMText() {
  950. return state.Text.empty;
  951. }
  952. }
  953. TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
  954. function inlineSiblingRect(view, side) {
  955. let parent = view.parent, index = parent ? parent.children.indexOf(view) : -1;
  956. while (parent && index >= 0) {
  957. if (side < 0 ? index > 0 : index < parent.children.length) {
  958. let next = parent.children[index + side];
  959. if (next instanceof TextView) {
  960. let nextRect = next.coordsAt(side < 0 ? next.length : 0, side);
  961. if (nextRect)
  962. return nextRect;
  963. }
  964. index += side;
  965. }
  966. else if (parent instanceof MarkView && parent.parent) {
  967. index = parent.parent.children.indexOf(parent) + (side < 0 ? 0 : 1);
  968. parent = parent.parent;
  969. }
  970. else {
  971. let last = parent.dom.lastChild;
  972. if (last && last.nodeName == "BR")
  973. return last.getClientRects()[0];
  974. break;
  975. }
  976. }
  977. return undefined;
  978. }
  979. function inlineDOMAtPos(dom, children, pos) {
  980. let i = 0;
  981. for (let off = 0; i < children.length; i++) {
  982. let child = children[i], end = off + child.length;
  983. if (end == off && child.getSide() <= 0)
  984. continue;
  985. if (pos > off && pos < end && child.dom.parentNode == dom)
  986. return child.domAtPos(pos - off);
  987. if (pos <= off)
  988. break;
  989. off = end;
  990. }
  991. for (; i > 0; i--) {
  992. let before = children[i - 1].dom;
  993. if (before.parentNode == dom)
  994. return DOMPos.after(before);
  995. }
  996. return new DOMPos(dom, 0);
  997. }
  998. // Assumes `view`, if a mark view, has precisely 1 child.
  999. function joinInlineInto(parent, view, open) {
  1000. let last, { children } = parent;
  1001. if (open > 0 && view instanceof MarkView && children.length &&
  1002. (last = children[children.length - 1]) instanceof MarkView && last.mark.eq(view.mark)) {
  1003. joinInlineInto(last, view.children[0], open - 1);
  1004. }
  1005. else {
  1006. children.push(view);
  1007. view.setParent(parent);
  1008. }
  1009. parent.length += view.length;
  1010. }
  1011. function coordsInChildren(view, pos, side) {
  1012. for (let off = 0, i = 0; i < view.children.length; i++) {
  1013. let child = view.children[i], end = off + child.length, next;
  1014. if ((side <= 0 || end == view.length || child.getSide() > 0 ? end >= pos : end > pos) &&
  1015. (pos < end || i + 1 == view.children.length || (next = view.children[i + 1]).length || next.getSide() > 0)) {
  1016. let flatten = 0;
  1017. if (end == off) {
  1018. if (child.getSide() <= 0)
  1019. continue;
  1020. flatten = side = -child.getSide();
  1021. }
  1022. let rect = child.coordsAt(Math.max(0, pos - off), side);
  1023. return flatten && rect ? flattenRect(rect, side < 0) : rect;
  1024. }
  1025. off = end;
  1026. }
  1027. let last = view.dom.lastChild;
  1028. if (!last)
  1029. return view.dom.getBoundingClientRect();
  1030. let rects = clientRectsFor(last);
  1031. return rects[rects.length - 1] || null;
  1032. }
  1033. function combineAttrs(source, target) {
  1034. for (let name in source) {
  1035. if (name == "class" && target.class)
  1036. target.class += " " + source.class;
  1037. else if (name == "style" && target.style)
  1038. target.style += ";" + source.style;
  1039. else
  1040. target[name] = source[name];
  1041. }
  1042. return target;
  1043. }
  1044. function attrsEq(a, b) {
  1045. if (a == b)
  1046. return true;
  1047. if (!a || !b)
  1048. return false;
  1049. let keysA = Object.keys(a), keysB = Object.keys(b);
  1050. if (keysA.length != keysB.length)
  1051. return false;
  1052. for (let key of keysA) {
  1053. if (keysB.indexOf(key) == -1 || a[key] !== b[key])
  1054. return false;
  1055. }
  1056. return true;
  1057. }
  1058. function updateAttrs(dom, prev, attrs) {
  1059. let changed = null;
  1060. if (prev)
  1061. for (let name in prev)
  1062. if (!(attrs && name in attrs))
  1063. dom.removeAttribute(changed = name);
  1064. if (attrs)
  1065. for (let name in attrs)
  1066. if (!(prev && prev[name] == attrs[name]))
  1067. dom.setAttribute(changed = name, attrs[name]);
  1068. return !!changed;
  1069. }
  1070. /**
  1071. Widgets added to the content are described by subclasses of this
  1072. class. Using a description object like that makes it possible to
  1073. delay creating of the DOM structure for a widget until it is
  1074. needed, and to avoid redrawing widgets even if the decorations
  1075. that define them are recreated.
  1076. */
  1077. class WidgetType {
  1078. /**
  1079. Compare this instance to another instance of the same type.
  1080. (TypeScript can't express this, but only instances of the same
  1081. specific class will be passed to this method.) This is used to
  1082. avoid redrawing widgets when they are replaced by a new
  1083. decoration of the same type. The default implementation just
  1084. returns `false`, which will cause new instances of the widget to
  1085. always be redrawn.
  1086. */
  1087. eq(widget) { return false; }
  1088. /**
  1089. Update a DOM element created by a widget of the same type (but
  1090. different, non-`eq` content) to reflect this widget. May return
  1091. true to indicate that it could update, false to indicate it
  1092. couldn't (in which case the widget will be redrawn). The default
  1093. implementation just returns false.
  1094. */
  1095. updateDOM(dom) { return false; }
  1096. /**
  1097. @internal
  1098. */
  1099. compare(other) {
  1100. return this == other || this.constructor == other.constructor && this.eq(other);
  1101. }
  1102. /**
  1103. The estimated height this widget will have, to be used when
  1104. estimating the height of content that hasn't been drawn. May
  1105. return -1 to indicate you don't know. The default implementation
  1106. returns -1.
  1107. */
  1108. get estimatedHeight() { return -1; }
  1109. /**
  1110. Can be used to configure which kinds of events inside the widget
  1111. should be ignored by the editor. The default is to ignore all
  1112. events.
  1113. */
  1114. ignoreEvent(event) { return true; }
  1115. /**
  1116. @internal
  1117. */
  1118. get customView() { return null; }
  1119. /**
  1120. This is called when the an instance of the widget is removed
  1121. from the editor view.
  1122. */
  1123. destroy(dom) { }
  1124. }
  1125. /**
  1126. The different types of blocks that can occur in an editor view.
  1127. */
  1128. exports.BlockType = void 0;
  1129. (function (BlockType) {
  1130. /**
  1131. A line of text.
  1132. */
  1133. BlockType[BlockType["Text"] = 0] = "Text";
  1134. /**
  1135. A block widget associated with the position after it.
  1136. */
  1137. BlockType[BlockType["WidgetBefore"] = 1] = "WidgetBefore";
  1138. /**
  1139. A block widget associated with the position before it.
  1140. */
  1141. BlockType[BlockType["WidgetAfter"] = 2] = "WidgetAfter";
  1142. /**
  1143. A block widget [replacing](https://codemirror.net/6/docs/ref/#view.Decoration^replace) a range of content.
  1144. */
  1145. BlockType[BlockType["WidgetRange"] = 3] = "WidgetRange";
  1146. })(exports.BlockType || (exports.BlockType = {}));
  1147. /**
  1148. A decoration provides information on how to draw or style a piece
  1149. of content. You'll usually use it wrapped in a
  1150. [`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position.
  1151. @nonabstract
  1152. */
  1153. class Decoration extends state.RangeValue {
  1154. constructor(
  1155. /**
  1156. @internal
  1157. */
  1158. startSide,
  1159. /**
  1160. @internal
  1161. */
  1162. endSide,
  1163. /**
  1164. @internal
  1165. */
  1166. widget,
  1167. /**
  1168. The config object used to create this decoration. You can
  1169. include additional properties in there to store metadata about
  1170. your decoration.
  1171. */
  1172. spec) {
  1173. super();
  1174. this.startSide = startSide;
  1175. this.endSide = endSide;
  1176. this.widget = widget;
  1177. this.spec = spec;
  1178. }
  1179. /**
  1180. @internal
  1181. */
  1182. get heightRelevant() { return false; }
  1183. /**
  1184. Create a mark decoration, which influences the styling of the
  1185. content in its range. Nested mark decorations will cause nested
  1186. DOM elements to be created. Nesting order is determined by
  1187. precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with
  1188. the higher-precedence decorations creating the inner DOM nodes.
  1189. Such elements are split on line boundaries and on the boundaries
  1190. of lower-precedence decorations.
  1191. */
  1192. static mark(spec) {
  1193. return new MarkDecoration(spec);
  1194. }
  1195. /**
  1196. Create a widget decoration, which displays a DOM element at the
  1197. given position.
  1198. */
  1199. static widget(spec) {
  1200. let side = spec.side || 0, block = !!spec.block;
  1201. side += block ? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */) : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
  1202. return new PointDecoration(spec, side, side, block, spec.widget || null, false);
  1203. }
  1204. /**
  1205. Create a replace decoration which replaces the given range with
  1206. a widget, or simply hides it.
  1207. */
  1208. static replace(spec) {
  1209. let block = !!spec.block, startSide, endSide;
  1210. if (spec.isBlockGap) {
  1211. startSide = -500000000 /* GapStart */;
  1212. endSide = 400000000 /* GapEnd */;
  1213. }
  1214. else {
  1215. let { start, end } = getInclusive(spec, block);
  1216. startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 500000000 /* NonIncStart */) - 1;
  1217. endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -600000000 /* NonIncEnd */) + 1;
  1218. }
  1219. return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
  1220. }
  1221. /**
  1222. Create a line decoration, which can add DOM attributes to the
  1223. line starting at the given position.
  1224. */
  1225. static line(spec) {
  1226. return new LineDecoration(spec);
  1227. }
  1228. /**
  1229. Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given
  1230. decorated range or ranges. If the ranges aren't already sorted,
  1231. pass `true` for `sort` to make the library sort them for you.
  1232. */
  1233. static set(of, sort = false) {
  1234. return state.RangeSet.of(of, sort);
  1235. }
  1236. /**
  1237. @internal
  1238. */
  1239. hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; }
  1240. }
  1241. /**
  1242. The empty set of decorations.
  1243. */
  1244. Decoration.none = state.RangeSet.empty;
  1245. class MarkDecoration extends Decoration {
  1246. constructor(spec) {
  1247. let { start, end } = getInclusive(spec);
  1248. super(start ? -1 /* InlineIncStart */ : 500000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -600000000 /* NonIncEnd */, null, spec);
  1249. this.tagName = spec.tagName || "span";
  1250. this.class = spec.class || "";
  1251. this.attrs = spec.attributes || null;
  1252. }
  1253. eq(other) {
  1254. return this == other ||
  1255. other instanceof MarkDecoration &&
  1256. this.tagName == other.tagName &&
  1257. this.class == other.class &&
  1258. attrsEq(this.attrs, other.attrs);
  1259. }
  1260. range(from, to = from) {
  1261. if (from >= to)
  1262. throw new RangeError("Mark decorations may not be empty");
  1263. return super.range(from, to);
  1264. }
  1265. }
  1266. MarkDecoration.prototype.point = false;
  1267. class LineDecoration extends Decoration {
  1268. constructor(spec) {
  1269. super(-200000000 /* Line */, -200000000 /* Line */, null, spec);
  1270. }
  1271. eq(other) {
  1272. return other instanceof LineDecoration && attrsEq(this.spec.attributes, other.spec.attributes);
  1273. }
  1274. range(from, to = from) {
  1275. if (to != from)
  1276. throw new RangeError("Line decoration ranges must be zero-length");
  1277. return super.range(from, to);
  1278. }
  1279. }
  1280. LineDecoration.prototype.mapMode = state.MapMode.TrackBefore;
  1281. LineDecoration.prototype.point = true;
  1282. class PointDecoration extends Decoration {
  1283. constructor(spec, startSide, endSide, block, widget, isReplace) {
  1284. super(startSide, endSide, widget, spec);
  1285. this.block = block;
  1286. this.isReplace = isReplace;
  1287. this.mapMode = !block ? state.MapMode.TrackDel : startSide <= 0 ? state.MapMode.TrackBefore : state.MapMode.TrackAfter;
  1288. }
  1289. // Only relevant when this.block == true
  1290. get type() {
  1291. return this.startSide < this.endSide ? exports.BlockType.WidgetRange
  1292. : this.startSide <= 0 ? exports.BlockType.WidgetBefore : exports.BlockType.WidgetAfter;
  1293. }
  1294. get heightRelevant() { return this.block || !!this.widget && this.widget.estimatedHeight >= 5; }
  1295. eq(other) {
  1296. return other instanceof PointDecoration &&
  1297. widgetsEq(this.widget, other.widget) &&
  1298. this.block == other.block &&
  1299. this.startSide == other.startSide && this.endSide == other.endSide;
  1300. }
  1301. range(from, to = from) {
  1302. if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
  1303. throw new RangeError("Invalid range for replacement decoration");
  1304. if (!this.isReplace && to != from)
  1305. throw new RangeError("Widget decorations can only have zero-length ranges");
  1306. return super.range(from, to);
  1307. }
  1308. }
  1309. PointDecoration.prototype.point = true;
  1310. function getInclusive(spec, block = false) {
  1311. let { inclusiveStart: start, inclusiveEnd: end } = spec;
  1312. if (start == null)
  1313. start = spec.inclusive;
  1314. if (end == null)
  1315. end = spec.inclusive;
  1316. return { start: start !== null && start !== void 0 ? start : block, end: end !== null && end !== void 0 ? end : block };
  1317. }
  1318. function widgetsEq(a, b) {
  1319. return a == b || !!(a && b && a.compare(b));
  1320. }
  1321. function addRange(from, to, ranges, margin = 0) {
  1322. let last = ranges.length - 1;
  1323. if (last >= 0 && ranges[last] + margin >= from)
  1324. ranges[last] = Math.max(ranges[last], to);
  1325. else
  1326. ranges.push(from, to);
  1327. }
  1328. class LineView extends ContentView {
  1329. constructor() {
  1330. super(...arguments);
  1331. this.children = [];
  1332. this.length = 0;
  1333. this.prevAttrs = undefined;
  1334. this.attrs = null;
  1335. this.breakAfter = 0;
  1336. }
  1337. // Consumes source
  1338. merge(from, to, source, hasStart, openStart, openEnd) {
  1339. if (source) {
  1340. if (!(source instanceof LineView))
  1341. return false;
  1342. if (!this.dom)
  1343. source.transferDOM(this); // Reuse source.dom when appropriate
  1344. }
  1345. if (hasStart)
  1346. this.setDeco(source ? source.attrs : null);
  1347. mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
  1348. return true;
  1349. }
  1350. split(at) {
  1351. let end = new LineView;
  1352. end.breakAfter = this.breakAfter;
  1353. if (this.length == 0)
  1354. return end;
  1355. let { i, off } = this.childPos(at);
  1356. if (off) {
  1357. end.append(this.children[i].split(off), 0);
  1358. this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
  1359. i++;
  1360. }
  1361. for (let j = i; j < this.children.length; j++)
  1362. end.append(this.children[j], 0);
  1363. while (i > 0 && this.children[i - 1].length == 0)
  1364. this.children[--i].destroy();
  1365. this.children.length = i;
  1366. this.markDirty();
  1367. this.length = at;
  1368. return end;
  1369. }
  1370. transferDOM(other) {
  1371. if (!this.dom)
  1372. return;
  1373. this.markDirty();
  1374. other.setDOM(this.dom);
  1375. other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
  1376. this.prevAttrs = undefined;
  1377. this.dom = null;
  1378. }
  1379. setDeco(attrs) {
  1380. if (!attrsEq(this.attrs, attrs)) {
  1381. if (this.dom) {
  1382. this.prevAttrs = this.attrs;
  1383. this.markDirty();
  1384. }
  1385. this.attrs = attrs;
  1386. }
  1387. }
  1388. append(child, openStart) {
  1389. joinInlineInto(this, child, openStart);
  1390. }
  1391. // Only called when building a line view in ContentBuilder
  1392. addLineDeco(deco) {
  1393. let attrs = deco.spec.attributes, cls = deco.spec.class;
  1394. if (attrs)
  1395. this.attrs = combineAttrs(attrs, this.attrs || {});
  1396. if (cls)
  1397. this.attrs = combineAttrs({ class: cls }, this.attrs || {});
  1398. }
  1399. domAtPos(pos) {
  1400. return inlineDOMAtPos(this.dom, this.children, pos);
  1401. }
  1402. reuseDOM(node) {
  1403. if (node.nodeName == "DIV") {
  1404. this.setDOM(node);
  1405. this.dirty |= 4 /* Attrs */ | 2 /* Node */;
  1406. }
  1407. }
  1408. sync(track) {
  1409. var _a;
  1410. if (!this.dom) {
  1411. this.setDOM(document.createElement("div"));
  1412. this.dom.className = "cm-line";
  1413. this.prevAttrs = this.attrs ? null : undefined;
  1414. }
  1415. else if (this.dirty & 4 /* Attrs */) {
  1416. clearAttributes(this.dom);
  1417. this.dom.className = "cm-line";
  1418. this.prevAttrs = this.attrs ? null : undefined;
  1419. }
  1420. if (this.prevAttrs !== undefined) {
  1421. updateAttrs(this.dom, this.prevAttrs, this.attrs);
  1422. this.dom.classList.add("cm-line");
  1423. this.prevAttrs = undefined;
  1424. }
  1425. super.sync(track);
  1426. let last = this.dom.lastChild;
  1427. while (last && ContentView.get(last) instanceof MarkView)
  1428. last = last.lastChild;
  1429. if (!last || !this.length ||
  1430. last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
  1431. (!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
  1432. let hack = document.createElement("BR");
  1433. hack.cmIgnore = true;
  1434. this.dom.appendChild(hack);
  1435. }
  1436. }
  1437. measureTextSize() {
  1438. if (this.children.length == 0 || this.length > 20)
  1439. return null;
  1440. let totalWidth = 0;
  1441. for (let child of this.children) {
  1442. if (!(child instanceof TextView))
  1443. return null;
  1444. let rects = clientRectsFor(child.dom);
  1445. if (rects.length != 1)
  1446. return null;
  1447. totalWidth += rects[0].width;
  1448. }
  1449. return { lineHeight: this.dom.getBoundingClientRect().height,
  1450. charWidth: totalWidth / this.length };
  1451. }
  1452. coordsAt(pos, side) {
  1453. return coordsInChildren(this, pos, side);
  1454. }
  1455. become(_other) { return false; }
  1456. get type() { return exports.BlockType.Text; }
  1457. static find(docView, pos) {
  1458. for (let i = 0, off = 0; i < docView.children.length; i++) {
  1459. let block = docView.children[i], end = off + block.length;
  1460. if (end >= pos) {
  1461. if (block instanceof LineView)
  1462. return block;
  1463. if (end > pos)
  1464. break;
  1465. }
  1466. off = end + block.breakAfter;
  1467. }
  1468. return null;
  1469. }
  1470. }
  1471. class BlockWidgetView extends ContentView {
  1472. constructor(widget, length, type) {
  1473. super();
  1474. this.widget = widget;
  1475. this.length = length;
  1476. this.type = type;
  1477. this.breakAfter = 0;
  1478. this.prevWidget = null;
  1479. }
  1480. merge(from, to, source, _takeDeco, openStart, openEnd) {
  1481. if (source && (!(source instanceof BlockWidgetView) || !this.widget.compare(source.widget) ||
  1482. from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
  1483. return false;
  1484. this.length = from + (source ? source.length : 0) + (this.length - to);
  1485. return true;
  1486. }
  1487. domAtPos(pos) {
  1488. return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length);
  1489. }
  1490. split(at) {
  1491. let len = this.length - at;
  1492. this.length = at;
  1493. let end = new BlockWidgetView(this.widget, len, this.type);
  1494. end.breakAfter = this.breakAfter;
  1495. return end;
  1496. }
  1497. get children() { return noChildren; }
  1498. sync() {
  1499. if (!this.dom || !this.widget.updateDOM(this.dom)) {
  1500. if (this.dom && this.prevWidget)
  1501. this.prevWidget.destroy(this.dom);
  1502. this.prevWidget = null;
  1503. this.setDOM(this.widget.toDOM(this.editorView));
  1504. this.dom.contentEditable = "false";
  1505. }
  1506. }
  1507. get overrideDOMText() {
  1508. return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : state.Text.empty;
  1509. }
  1510. domBoundsAround() { return null; }
  1511. become(other) {
  1512. if (other instanceof BlockWidgetView && other.type == this.type &&
  1513. other.widget.constructor == this.widget.constructor) {
  1514. if (!other.widget.eq(this.widget))
  1515. this.markDirty(true);
  1516. if (this.dom && !this.prevWidget)
  1517. this.prevWidget = this.widget;
  1518. this.widget = other.widget;
  1519. this.length = other.length;
  1520. this.breakAfter = other.breakAfter;
  1521. return true;
  1522. }
  1523. return false;
  1524. }
  1525. ignoreMutation() { return true; }
  1526. ignoreEvent(event) { return this.widget.ignoreEvent(event); }
  1527. destroy() {
  1528. super.destroy();
  1529. if (this.dom)
  1530. this.widget.destroy(this.dom);
  1531. }
  1532. }
  1533. class ContentBuilder {
  1534. constructor(doc, pos, end, disallowBlockEffectsFor) {
  1535. this.doc = doc;
  1536. this.pos = pos;
  1537. this.end = end;
  1538. this.disallowBlockEffectsFor = disallowBlockEffectsFor;
  1539. this.content = [];
  1540. this.curLine = null;
  1541. this.breakAtStart = 0;
  1542. this.pendingBuffer = 0 /* No */;
  1543. // Set to false directly after a widget that covers the position after it
  1544. this.atCursorPos = true;
  1545. this.openStart = -1;
  1546. this.openEnd = -1;
  1547. this.text = "";
  1548. this.textOff = 0;
  1549. this.cursor = doc.iter();
  1550. this.skip = pos;
  1551. }
  1552. posCovered() {
  1553. if (this.content.length == 0)
  1554. return !this.breakAtStart && this.doc.lineAt(this.pos).from != this.pos;
  1555. let last = this.content[this.content.length - 1];
  1556. return !last.breakAfter && !(last instanceof BlockWidgetView && last.type == exports.BlockType.WidgetBefore);
  1557. }
  1558. getLine() {
  1559. if (!this.curLine) {
  1560. this.content.push(this.curLine = new LineView);
  1561. this.atCursorPos = true;
  1562. }
  1563. return this.curLine;
  1564. }
  1565. flushBuffer(active) {
  1566. if (this.pendingBuffer) {
  1567. this.curLine.append(wrapMarks(new WidgetBufferView(-1), active), active.length);
  1568. this.pendingBuffer = 0 /* No */;
  1569. }
  1570. }
  1571. addBlockWidget(view) {
  1572. this.flushBuffer([]);
  1573. this.curLine = null;
  1574. this.content.push(view);
  1575. }
  1576. finish(openEnd) {
  1577. if (!openEnd)
  1578. this.flushBuffer([]);
  1579. else
  1580. this.pendingBuffer = 0 /* No */;
  1581. if (!this.posCovered())
  1582. this.getLine();
  1583. }
  1584. buildText(length, active, openStart) {
  1585. while (length > 0) {
  1586. if (this.textOff == this.text.length) {
  1587. let { value, lineBreak, done } = this.cursor.next(this.skip);
  1588. this.skip = 0;
  1589. if (done)
  1590. throw new Error("Ran out of text content when drawing inline views");
  1591. if (lineBreak) {
  1592. if (!this.posCovered())
  1593. this.getLine();
  1594. if (this.content.length)
  1595. this.content[this.content.length - 1].breakAfter = 1;
  1596. else
  1597. this.breakAtStart = 1;
  1598. this.flushBuffer([]);
  1599. this.curLine = null;
  1600. length--;
  1601. continue;
  1602. }
  1603. else {
  1604. this.text = value;
  1605. this.textOff = 0;
  1606. }
  1607. }
  1608. let take = Math.min(this.text.length - this.textOff, length, 512 /* Chunk */);
  1609. this.flushBuffer(active.slice(0, openStart));
  1610. this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
  1611. this.atCursorPos = true;
  1612. this.textOff += take;
  1613. length -= take;
  1614. openStart = 0;
  1615. }
  1616. }
  1617. span(from, to, active, openStart) {
  1618. this.buildText(to - from, active, openStart);
  1619. this.pos = to;
  1620. if (this.openStart < 0)
  1621. this.openStart = openStart;
  1622. }
  1623. point(from, to, deco, active, openStart, index) {
  1624. if (this.disallowBlockEffectsFor[index] && deco instanceof PointDecoration) {
  1625. if (deco.block)
  1626. throw new RangeError("Block decorations may not be specified via plugins");
  1627. if (to > this.doc.lineAt(this.pos).to)
  1628. throw new RangeError("Decorations that replace line breaks may not be specified via plugins");
  1629. }
  1630. let len = to - from;
  1631. if (deco instanceof PointDecoration) {
  1632. if (deco.block) {
  1633. let { type } = deco;
  1634. if (type == exports.BlockType.WidgetAfter && !this.posCovered())
  1635. this.getLine();
  1636. this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
  1637. }
  1638. else {
  1639. let view = WidgetView.create(deco.widget || new NullWidget("span"), len, deco.startSide);
  1640. let cursorBefore = this.atCursorPos && !view.isEditable && openStart <= active.length && (from < to || deco.startSide > 0);
  1641. let cursorAfter = !view.isEditable && (from < to || deco.startSide <= 0);
  1642. let line = this.getLine();
  1643. if (this.pendingBuffer == 2 /* IfCursor */ && !cursorBefore)
  1644. this.pendingBuffer = 0 /* No */;
  1645. this.flushBuffer(active);
  1646. if (cursorBefore) {
  1647. line.append(wrapMarks(new WidgetBufferView(1), active), openStart);
  1648. openStart = active.length + Math.max(0, openStart - active.length);
  1649. }
  1650. line.append(wrapMarks(view, active), openStart);
  1651. this.atCursorPos = cursorAfter;
  1652. this.pendingBuffer = !cursorAfter ? 0 /* No */ : from < to ? 1 /* Yes */ : 2 /* IfCursor */;
  1653. }
  1654. }
  1655. else if (this.doc.lineAt(this.pos).from == this.pos) { // Line decoration
  1656. this.getLine().addLineDeco(deco);
  1657. }
  1658. if (len) {
  1659. // Advance the iterator past the replaced content
  1660. if (this.textOff + len <= this.text.length) {
  1661. this.textOff += len;
  1662. }
  1663. else {
  1664. this.skip += len - (this.text.length - this.textOff);
  1665. this.text = "";
  1666. this.textOff = 0;
  1667. }
  1668. this.pos = to;
  1669. }
  1670. if (this.openStart < 0)
  1671. this.openStart = openStart;
  1672. }
  1673. static build(text, from, to, decorations, dynamicDecorationMap) {
  1674. let builder = new ContentBuilder(text, from, to, dynamicDecorationMap);
  1675. builder.openEnd = state.RangeSet.spans(decorations, from, to, builder);
  1676. if (builder.openStart < 0)
  1677. builder.openStart = builder.openEnd;
  1678. builder.finish(builder.openEnd);
  1679. return builder;
  1680. }
  1681. }
  1682. function wrapMarks(view, active) {
  1683. for (let mark of active)
  1684. view = new MarkView(mark, [view], view.length);
  1685. return view;
  1686. }
  1687. class NullWidget extends WidgetType {
  1688. constructor(tag) {
  1689. super();
  1690. this.tag = tag;
  1691. }
  1692. eq(other) { return other.tag == this.tag; }
  1693. toDOM() { return document.createElement(this.tag); }
  1694. updateDOM(elt) { return elt.nodeName.toLowerCase() == this.tag; }
  1695. }
  1696. const clickAddsSelectionRange = state.Facet.define();
  1697. const dragMovesSelection$1 = state.Facet.define();
  1698. const mouseSelectionStyle = state.Facet.define();
  1699. const exceptionSink = state.Facet.define();
  1700. const updateListener = state.Facet.define();
  1701. const inputHandler = state.Facet.define();
  1702. const perLineTextDirection = state.Facet.define({
  1703. combine: values => values.some(x => x)
  1704. });
  1705. class ScrollTarget {
  1706. constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
  1707. this.range = range;
  1708. this.y = y;
  1709. this.x = x;
  1710. this.yMargin = yMargin;
  1711. this.xMargin = xMargin;
  1712. }
  1713. map(changes) {
  1714. return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin);
  1715. }
  1716. }
  1717. const scrollIntoView = state.StateEffect.define({ map: (t, ch) => t.map(ch) });
  1718. /**
  1719. Log or report an unhandled exception in client code. Should
  1720. probably only be used by extension code that allows client code to
  1721. provide functions, and calls those functions in a context where an
  1722. exception can't be propagated to calling code in a reasonable way
  1723. (for example when in an event handler).
  1724. Either calls a handler registered with
  1725. [`EditorView.exceptionSink`](https://codemirror.net/6/docs/ref/#view.EditorView^exceptionSink),
  1726. `window.onerror`, if defined, or `console.error` (in which case
  1727. it'll pass `context`, when given, as first argument).
  1728. */
  1729. function logException(state, exception, context) {
  1730. let handler = state.facet(exceptionSink);
  1731. if (handler.length)
  1732. handler[0](exception);
  1733. else if (window.onerror)
  1734. window.onerror(String(exception), context, undefined, undefined, exception);
  1735. else if (context)
  1736. console.error(context + ":", exception);
  1737. else
  1738. console.error(exception);
  1739. }
  1740. const editable = state.Facet.define({ combine: values => values.length ? values[0] : true });
  1741. let nextPluginID = 0;
  1742. const viewPlugin = state.Facet.define();
  1743. /**
  1744. View plugins associate stateful values with a view. They can
  1745. influence the way the content is drawn, and are notified of things
  1746. that happen in the view.
  1747. */
  1748. class ViewPlugin {
  1749. constructor(
  1750. /**
  1751. @internal
  1752. */
  1753. id,
  1754. /**
  1755. @internal
  1756. */
  1757. create,
  1758. /**
  1759. @internal
  1760. */
  1761. domEventHandlers, buildExtensions) {
  1762. this.id = id;
  1763. this.create = create;
  1764. this.domEventHandlers = domEventHandlers;
  1765. this.extension = buildExtensions(this);
  1766. }
  1767. /**
  1768. Define a plugin from a constructor function that creates the
  1769. plugin's value, given an editor view.
  1770. */
  1771. static define(create, spec) {
  1772. const { eventHandlers, provide, decorations: deco } = spec || {};
  1773. return new ViewPlugin(nextPluginID++, create, eventHandlers, plugin => {
  1774. let ext = [viewPlugin.of(plugin)];
  1775. if (deco)
  1776. ext.push(decorations.of(view => {
  1777. let pluginInst = view.plugin(plugin);
  1778. return pluginInst ? deco(pluginInst) : Decoration.none;
  1779. }));
  1780. if (provide)
  1781. ext.push(provide(plugin));
  1782. return ext;
  1783. });
  1784. }
  1785. /**
  1786. Create a plugin for a class whose constructor takes a single
  1787. editor view as argument.
  1788. */
  1789. static fromClass(cls, spec) {
  1790. return ViewPlugin.define(view => new cls(view), spec);
  1791. }
  1792. }
  1793. class PluginInstance {
  1794. constructor(spec) {
  1795. this.spec = spec;
  1796. // When starting an update, all plugins have this field set to the
  1797. // update object, indicating they need to be updated. When finished
  1798. // updating, it is set to `false`. Retrieving a plugin that needs to
  1799. // be updated with `view.plugin` forces an eager update.
  1800. this.mustUpdate = null;
  1801. // This is null when the plugin is initially created, but
  1802. // initialized on the first update.
  1803. this.value = null;
  1804. }
  1805. update(view) {
  1806. if (!this.value) {
  1807. if (this.spec) {
  1808. try {
  1809. this.value = this.spec.create(view);
  1810. }
  1811. catch (e) {
  1812. logException(view.state, e, "CodeMirror plugin crashed");
  1813. this.deactivate();
  1814. }
  1815. }
  1816. }
  1817. else if (this.mustUpdate) {
  1818. let update = this.mustUpdate;
  1819. this.mustUpdate = null;
  1820. if (this.value.update) {
  1821. try {
  1822. this.value.update(update);
  1823. }
  1824. catch (e) {
  1825. logException(update.state, e, "CodeMirror plugin crashed");
  1826. if (this.value.destroy)
  1827. try {
  1828. this.value.destroy();
  1829. }
  1830. catch (_) { }
  1831. this.deactivate();
  1832. }
  1833. }
  1834. }
  1835. return this;
  1836. }
  1837. destroy(view) {
  1838. var _a;
  1839. if ((_a = this.value) === null || _a === void 0 ? void 0 : _a.destroy) {
  1840. try {
  1841. this.value.destroy();
  1842. }
  1843. catch (e) {
  1844. logException(view.state, e, "CodeMirror plugin crashed");
  1845. }
  1846. }
  1847. }
  1848. deactivate() {
  1849. this.spec = this.value = null;
  1850. }
  1851. }
  1852. const editorAttributes = state.Facet.define();
  1853. const contentAttributes = state.Facet.define();
  1854. // Provide decorations
  1855. const decorations = state.Facet.define();
  1856. const atomicRanges = state.Facet.define();
  1857. const scrollMargins = state.Facet.define();
  1858. const styleModule = state.Facet.define();
  1859. class ChangedRange {
  1860. constructor(fromA, toA, fromB, toB) {
  1861. this.fromA = fromA;
  1862. this.toA = toA;
  1863. this.fromB = fromB;
  1864. this.toB = toB;
  1865. }
  1866. join(other) {
  1867. return new ChangedRange(Math.min(this.fromA, other.fromA), Math.max(this.toA, other.toA), Math.min(this.fromB, other.fromB), Math.max(this.toB, other.toB));
  1868. }
  1869. addToSet(set) {
  1870. let i = set.length, me = this;
  1871. for (; i > 0; i--) {
  1872. let range = set[i - 1];
  1873. if (range.fromA > me.toA)
  1874. continue;
  1875. if (range.toA < me.fromA)
  1876. break;
  1877. me = me.join(range);
  1878. set.splice(i - 1, 1);
  1879. }
  1880. set.splice(i, 0, me);
  1881. return set;
  1882. }
  1883. static extendWithRanges(diff, ranges) {
  1884. if (ranges.length == 0)
  1885. return diff;
  1886. let result = [];
  1887. for (let dI = 0, rI = 0, posA = 0, posB = 0;; dI++) {
  1888. let next = dI == diff.length ? null : diff[dI], off = posA - posB;
  1889. let end = next ? next.fromB : 1e9;
  1890. while (rI < ranges.length && ranges[rI] < end) {
  1891. let from = ranges[rI], to = ranges[rI + 1];
  1892. let fromB = Math.max(posB, from), toB = Math.min(end, to);
  1893. if (fromB <= toB)
  1894. new ChangedRange(fromB + off, toB + off, fromB, toB).addToSet(result);
  1895. if (to > end)
  1896. break;
  1897. else
  1898. rI += 2;
  1899. }
  1900. if (!next)
  1901. return result;
  1902. new ChangedRange(next.fromA, next.toA, next.fromB, next.toB).addToSet(result);
  1903. posA = next.toA;
  1904. posB = next.toB;
  1905. }
  1906. }
  1907. }
  1908. /**
  1909. View [plugins](https://codemirror.net/6/docs/ref/#view.ViewPlugin) are given instances of this
  1910. class, which describe what happened, whenever the view is updated.
  1911. */
  1912. class ViewUpdate {
  1913. constructor(
  1914. /**
  1915. The editor view that the update is associated with.
  1916. */
  1917. view,
  1918. /**
  1919. The new editor state.
  1920. */
  1921. state$1,
  1922. /**
  1923. The transactions involved in the update. May be empty.
  1924. */
  1925. transactions) {
  1926. this.view = view;
  1927. this.state = state$1;
  1928. this.transactions = transactions;
  1929. /**
  1930. @internal
  1931. */
  1932. this.flags = 0;
  1933. this.startState = view.state;
  1934. this.changes = state.ChangeSet.empty(this.startState.doc.length);
  1935. for (let tr of transactions)
  1936. this.changes = this.changes.compose(tr.changes);
  1937. let changedRanges = [];
  1938. this.changes.iterChangedRanges((fromA, toA, fromB, toB) => changedRanges.push(new ChangedRange(fromA, toA, fromB, toB)));
  1939. this.changedRanges = changedRanges;
  1940. let focus = view.hasFocus;
  1941. if (focus != view.inputState.notifiedFocused) {
  1942. view.inputState.notifiedFocused = focus;
  1943. this.flags |= 1 /* Focus */;
  1944. }
  1945. }
  1946. /**
  1947. @internal
  1948. */
  1949. static create(view, state, transactions) {
  1950. return new ViewUpdate(view, state, transactions);
  1951. }
  1952. /**
  1953. Tells you whether the [viewport](https://codemirror.net/6/docs/ref/#view.EditorView.viewport) or
  1954. [visible ranges](https://codemirror.net/6/docs/ref/#view.EditorView.visibleRanges) changed in this
  1955. update.
  1956. */
  1957. get viewportChanged() {
  1958. return (this.flags & 4 /* Viewport */) > 0;
  1959. }
  1960. /**
  1961. Indicates whether the height of a block element in the editor
  1962. changed in this update.
  1963. */
  1964. get heightChanged() {
  1965. return (this.flags & 2 /* Height */) > 0;
  1966. }
  1967. /**
  1968. Returns true when the document was modified or the size of the
  1969. editor, or elements within the editor, changed.
  1970. */
  1971. get geometryChanged() {
  1972. return this.docChanged || (this.flags & (8 /* Geometry */ | 2 /* Height */)) > 0;
  1973. }
  1974. /**
  1975. True when this update indicates a focus change.
  1976. */
  1977. get focusChanged() {
  1978. return (this.flags & 1 /* Focus */) > 0;
  1979. }
  1980. /**
  1981. Whether the document changed in this update.
  1982. */
  1983. get docChanged() {
  1984. return !this.changes.empty;
  1985. }
  1986. /**
  1987. Whether the selection was explicitly set in this update.
  1988. */
  1989. get selectionSet() {
  1990. return this.transactions.some(tr => tr.selection);
  1991. }
  1992. /**
  1993. @internal
  1994. */
  1995. get empty() { return this.flags == 0 && this.transactions.length == 0; }
  1996. }
  1997. /**
  1998. Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
  1999. */
  2000. exports.Direction = void 0;
  2001. (function (Direction) {
  2002. // (These are chosen to match the base levels, in bidi algorithm
  2003. // terms, of spans in that direction.)
  2004. /**
  2005. Left-to-right.
  2006. */
  2007. Direction[Direction["LTR"] = 0] = "LTR";
  2008. /**
  2009. Right-to-left.
  2010. */
  2011. Direction[Direction["RTL"] = 1] = "RTL";
  2012. })(exports.Direction || (exports.Direction = {}));
  2013. const LTR = exports.Direction.LTR, RTL = exports.Direction.RTL;
  2014. // Decode a string with each type encoded as log2(type)
  2015. function dec(str) {
  2016. let result = [];
  2017. for (let i = 0; i < str.length; i++)
  2018. result.push(1 << +str[i]);
  2019. return result;
  2020. }
  2021. // Character types for codepoints 0 to 0xf8
  2022. const LowTypes = dec("88888888888888888888888888888888888666888888787833333333337888888000000000000000000000000008888880000000000000000000000000088888888888888888888888888888888888887866668888088888663380888308888800000000000000000000000800000000000000000000000000000008");
  2023. // Character types for codepoints 0x600 to 0x6f9
  2024. const ArabicTypes = dec("4444448826627288999999999992222222222222222222222222222222222222222222222229999999999999999999994444444444644222822222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222999999949999999229989999223333333333");
  2025. const Brackets = Object.create(null), BracketStack = [];
  2026. // There's a lot more in
  2027. // https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt,
  2028. // which are left out to keep code size down.
  2029. for (let p of ["()", "[]", "{}"]) {
  2030. let l = p.charCodeAt(0), r = p.charCodeAt(1);
  2031. Brackets[l] = r;
  2032. Brackets[r] = -l;
  2033. }
  2034. function charType(ch) {
  2035. return ch <= 0xf7 ? LowTypes[ch] :
  2036. 0x590 <= ch && ch <= 0x5f4 ? 2 /* R */ :
  2037. 0x600 <= ch && ch <= 0x6f9 ? ArabicTypes[ch - 0x600] :
  2038. 0x6ee <= ch && ch <= 0x8ac ? 4 /* AL */ :
  2039. 0x2000 <= ch && ch <= 0x200b ? 256 /* NI */ :
  2040. ch == 0x200c ? 256 /* NI */ : 1 /* L */;
  2041. }
  2042. const BidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
  2043. /**
  2044. Represents a contiguous range of text that has a single direction
  2045. (as in left-to-right or right-to-left).
  2046. */
  2047. class BidiSpan {
  2048. /**
  2049. @internal
  2050. */
  2051. constructor(
  2052. /**
  2053. The start of the span (relative to the start of the line).
  2054. */
  2055. from,
  2056. /**
  2057. The end of the span.
  2058. */
  2059. to,
  2060. /**
  2061. The ["bidi
  2062. level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
  2063. of the span (in this context, 0 means
  2064. left-to-right, 1 means right-to-left, 2 means left-to-right
  2065. number inside right-to-left text).
  2066. */
  2067. level) {
  2068. this.from = from;
  2069. this.to = to;
  2070. this.level = level;
  2071. }
  2072. /**
  2073. The direction of this span.
  2074. */
  2075. get dir() { return this.level % 2 ? RTL : LTR; }
  2076. /**
  2077. @internal
  2078. */
  2079. side(end, dir) { return (this.dir == dir) == end ? this.to : this.from; }
  2080. /**
  2081. @internal
  2082. */
  2083. static find(order, index, level, assoc) {
  2084. let maybe = -1;
  2085. for (let i = 0; i < order.length; i++) {
  2086. let span = order[i];
  2087. if (span.from <= index && span.to >= index) {
  2088. if (span.level == level)
  2089. return i;
  2090. // When multiple spans match, if assoc != 0, take the one that
  2091. // covers that side, otherwise take the one with the minimum
  2092. // level.
  2093. if (maybe < 0 || (assoc != 0 ? (assoc < 0 ? span.from < index : span.to > index) : order[maybe].level > span.level))
  2094. maybe = i;
  2095. }
  2096. }
  2097. if (maybe < 0)
  2098. throw new RangeError("Index out of range");
  2099. return maybe;
  2100. }
  2101. }
  2102. // Reused array of character types
  2103. const types = [];
  2104. function computeOrder(line, direction) {
  2105. let len = line.length, outerType = direction == LTR ? 1 /* L */ : 2 /* R */, oppositeType = direction == LTR ? 2 /* R */ : 1 /* L */;
  2106. if (!line || outerType == 1 /* L */ && !BidiRE.test(line))
  2107. return trivialOrder(len);
  2108. // W1. Examine each non-spacing mark (NSM) in the level run, and
  2109. // change the type of the NSM to the type of the previous
  2110. // character. If the NSM is at the start of the level run, it will
  2111. // get the type of sor.
  2112. // W2. Search backwards from each instance of a European number
  2113. // until the first strong type (R, L, AL, or sor) is found. If an
  2114. // AL is found, change the type of the European number to Arabic
  2115. // number.
  2116. // W3. Change all ALs to R.
  2117. // (Left after this: L, R, EN, AN, ET, CS, NI)
  2118. for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
  2119. let type = charType(line.charCodeAt(i));
  2120. if (type == 512 /* NSM */)
  2121. type = prev;
  2122. else if (type == 8 /* EN */ && prevStrong == 4 /* AL */)
  2123. type = 16 /* AN */;
  2124. types[i] = type == 4 /* AL */ ? 2 /* R */ : type;
  2125. if (type & 7 /* Strong */)
  2126. prevStrong = type;
  2127. prev = type;
  2128. }
  2129. // W5. A sequence of European terminators adjacent to European
  2130. // numbers changes to all European numbers.
  2131. // W6. Otherwise, separators and terminators change to Other
  2132. // Neutral.
  2133. // W7. Search backwards from each instance of a European number
  2134. // until the first strong type (R, L, or sor) is found. If an L is
  2135. // found, then change the type of the European number to L.
  2136. // (Left after this: L, R, EN+AN, NI)
  2137. for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
  2138. let type = types[i];
  2139. if (type == 128 /* CS */) {
  2140. if (i < len - 1 && prev == types[i + 1] && (prev & 24 /* Num */))
  2141. type = types[i] = prev;
  2142. else
  2143. types[i] = 256 /* NI */;
  2144. }
  2145. else if (type == 64 /* ET */) {
  2146. let end = i + 1;
  2147. while (end < len && types[end] == 64 /* ET */)
  2148. end++;
  2149. let replace = (i && prev == 8 /* EN */) || (end < len && types[end] == 8 /* EN */) ? (prevStrong == 1 /* L */ ? 1 /* L */ : 8 /* EN */) : 256 /* NI */;
  2150. for (let j = i; j < end; j++)
  2151. types[j] = replace;
  2152. i = end - 1;
  2153. }
  2154. else if (type == 8 /* EN */ && prevStrong == 1 /* L */) {
  2155. types[i] = 1 /* L */;
  2156. }
  2157. prev = type;
  2158. if (type & 7 /* Strong */)
  2159. prevStrong = type;
  2160. }
  2161. // N0. Process bracket pairs in an isolating run sequence
  2162. // sequentially in the logical order of the text positions of the
  2163. // opening paired brackets using the logic given below. Within this
  2164. // scope, bidirectional types EN and AN are treated as R.
  2165. for (let i = 0, sI = 0, context = 0, ch, br, type; i < len; i++) {
  2166. // Keeps [startIndex, type, strongSeen] triples for each open
  2167. // bracket on BracketStack.
  2168. if (br = Brackets[ch = line.charCodeAt(i)]) {
  2169. if (br < 0) { // Closing bracket
  2170. for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
  2171. if (BracketStack[sJ + 1] == -br) {
  2172. let flags = BracketStack[sJ + 2];
  2173. let type = (flags & 2 /* EmbedInside */) ? outerType :
  2174. !(flags & 4 /* OppositeInside */) ? 0 :
  2175. (flags & 1 /* OppositeBefore */) ? oppositeType : outerType;
  2176. if (type)
  2177. types[i] = types[BracketStack[sJ]] = type;
  2178. sI = sJ;
  2179. break;
  2180. }
  2181. }
  2182. }
  2183. else if (BracketStack.length == 189 /* MaxDepth */) {
  2184. break;
  2185. }
  2186. else {
  2187. BracketStack[sI++] = i;
  2188. BracketStack[sI++] = ch;
  2189. BracketStack[sI++] = context;
  2190. }
  2191. }
  2192. else if ((type = types[i]) == 2 /* R */ || type == 1 /* L */) {
  2193. let embed = type == outerType;
  2194. context = embed ? 0 : 1 /* OppositeBefore */;
  2195. for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
  2196. let cur = BracketStack[sJ + 2];
  2197. if (cur & 2 /* EmbedInside */)
  2198. break;
  2199. if (embed) {
  2200. BracketStack[sJ + 2] |= 2 /* EmbedInside */;
  2201. }
  2202. else {
  2203. if (cur & 4 /* OppositeInside */)
  2204. break;
  2205. BracketStack[sJ + 2] |= 4 /* OppositeInside */;
  2206. }
  2207. }
  2208. }
  2209. }
  2210. // N1. A sequence of neutrals takes the direction of the
  2211. // surrounding strong text if the text on both sides has the same
  2212. // direction. European and Arabic numbers act as if they were R in
  2213. // terms of their influence on neutrals. Start-of-level-run (sor)
  2214. // and end-of-level-run (eor) are used at level run boundaries.
  2215. // N2. Any remaining neutrals take the embedding direction.
  2216. // (Left after this: L, R, EN+AN)
  2217. for (let i = 0; i < len; i++) {
  2218. if (types[i] == 256 /* NI */) {
  2219. let end = i + 1;
  2220. while (end < len && types[end] == 256 /* NI */)
  2221. end++;
  2222. let beforeL = (i ? types[i - 1] : outerType) == 1 /* L */;
  2223. let afterL = (end < len ? types[end] : outerType) == 1 /* L */;
  2224. let replace = beforeL == afterL ? (beforeL ? 1 /* L */ : 2 /* R */) : outerType;
  2225. for (let j = i; j < end; j++)
  2226. types[j] = replace;
  2227. i = end - 1;
  2228. }
  2229. }
  2230. // Here we depart from the documented algorithm, in order to avoid
  2231. // building up an actual levels array. Since there are only three
  2232. // levels (0, 1, 2) in an implementation that doesn't take
  2233. // explicit embedding into account, we can build up the order on
  2234. // the fly, without following the level-based algorithm.
  2235. let order = [];
  2236. if (outerType == 1 /* L */) {
  2237. for (let i = 0; i < len;) {
  2238. let start = i, rtl = types[i++] != 1 /* L */;
  2239. while (i < len && rtl == (types[i] != 1 /* L */))
  2240. i++;
  2241. if (rtl) {
  2242. for (let j = i; j > start;) {
  2243. let end = j, l = types[--j] != 2 /* R */;
  2244. while (j > start && l == (types[j - 1] != 2 /* R */))
  2245. j--;
  2246. order.push(new BidiSpan(j, end, l ? 2 : 1));
  2247. }
  2248. }
  2249. else {
  2250. order.push(new BidiSpan(start, i, 0));
  2251. }
  2252. }
  2253. }
  2254. else {
  2255. for (let i = 0; i < len;) {
  2256. let start = i, rtl = types[i++] == 2 /* R */;
  2257. while (i < len && rtl == (types[i] == 2 /* R */))
  2258. i++;
  2259. order.push(new BidiSpan(start, i, rtl ? 1 : 2));
  2260. }
  2261. }
  2262. return order;
  2263. }
  2264. function trivialOrder(length) {
  2265. return [new BidiSpan(0, length, 0)];
  2266. }
  2267. let movedOver = "";
  2268. function moveVisually(line, order, dir, start, forward) {
  2269. var _a;
  2270. let startIndex = start.head - line.from, spanI = -1;
  2271. if (startIndex == 0) {
  2272. if (!forward || !line.length)
  2273. return null;
  2274. if (order[0].level != dir) {
  2275. startIndex = order[0].side(false, dir);
  2276. spanI = 0;
  2277. }
  2278. }
  2279. else if (startIndex == line.length) {
  2280. if (forward)
  2281. return null;
  2282. let last = order[order.length - 1];
  2283. if (last.level != dir) {
  2284. startIndex = last.side(true, dir);
  2285. spanI = order.length - 1;
  2286. }
  2287. }
  2288. if (spanI < 0)
  2289. spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc);
  2290. let span = order[spanI];
  2291. // End of span. (But not end of line--that was checked for above.)
  2292. if (startIndex == span.side(forward, dir)) {
  2293. span = order[spanI += forward ? 1 : -1];
  2294. startIndex = span.side(!forward, dir);
  2295. }
  2296. let indexForward = forward == (span.dir == dir);
  2297. let nextIndex = state.findClusterBreak(line.text, startIndex, indexForward);
  2298. movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
  2299. if (nextIndex != span.side(forward, dir))
  2300. return state.EditorSelection.cursor(nextIndex + line.from, indexForward ? -1 : 1, span.level);
  2301. let nextSpan = spanI == (forward ? order.length - 1 : 0) ? null : order[spanI + (forward ? 1 : -1)];
  2302. if (!nextSpan && span.level != dir)
  2303. return state.EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1, dir);
  2304. if (nextSpan && nextSpan.level < span.level)
  2305. return state.EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, forward ? 1 : -1, nextSpan.level);
  2306. return state.EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
  2307. }
  2308. const LineBreakPlaceholder = "\uffff";
  2309. class DOMReader {
  2310. constructor(points, state$1) {
  2311. this.points = points;
  2312. this.text = "";
  2313. this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
  2314. }
  2315. append(text) {
  2316. this.text += text;
  2317. }
  2318. lineBreak() {
  2319. this.text += LineBreakPlaceholder;
  2320. }
  2321. readRange(start, end) {
  2322. if (!start)
  2323. return this;
  2324. let parent = start.parentNode;
  2325. for (let cur = start;;) {
  2326. this.findPointBefore(parent, cur);
  2327. this.readNode(cur);
  2328. let next = cur.nextSibling;
  2329. if (next == end)
  2330. break;
  2331. let view = ContentView.get(cur), nextView = ContentView.get(next);
  2332. if (view && nextView ? view.breakAfter :
  2333. (view ? view.breakAfter : isBlockElement(cur)) ||
  2334. (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
  2335. this.lineBreak();
  2336. cur = next;
  2337. }
  2338. this.findPointBefore(parent, end);
  2339. return this;
  2340. }
  2341. readTextNode(node) {
  2342. let text = node.nodeValue;
  2343. for (let point of this.points)
  2344. if (point.node == node)
  2345. point.pos = this.text.length + Math.min(point.offset, text.length);
  2346. for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
  2347. let nextBreak = -1, breakSize = 1, m;
  2348. if (this.lineSeparator) {
  2349. nextBreak = text.indexOf(this.lineSeparator, off);
  2350. breakSize = this.lineSeparator.length;
  2351. }
  2352. else if (m = re.exec(text)) {
  2353. nextBreak = m.index;
  2354. breakSize = m[0].length;
  2355. }
  2356. this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
  2357. if (nextBreak < 0)
  2358. break;
  2359. this.lineBreak();
  2360. if (breakSize > 1)
  2361. for (let point of this.points)
  2362. if (point.node == node && point.pos > this.text.length)
  2363. point.pos -= breakSize - 1;
  2364. off = nextBreak + breakSize;
  2365. }
  2366. }
  2367. readNode(node) {
  2368. if (node.cmIgnore)
  2369. return;
  2370. let view = ContentView.get(node);
  2371. let fromView = view && view.overrideDOMText;
  2372. if (fromView != null) {
  2373. this.findPointInside(node, fromView.length);
  2374. for (let i = fromView.iter(); !i.next().done;) {
  2375. if (i.lineBreak)
  2376. this.lineBreak();
  2377. else
  2378. this.append(i.value);
  2379. }
  2380. }
  2381. else if (node.nodeType == 3) {
  2382. this.readTextNode(node);
  2383. }
  2384. else if (node.nodeName == "BR") {
  2385. if (node.nextSibling)
  2386. this.lineBreak();
  2387. }
  2388. else if (node.nodeType == 1) {
  2389. this.readRange(node.firstChild, null);
  2390. }
  2391. }
  2392. findPointBefore(node, next) {
  2393. for (let point of this.points)
  2394. if (point.node == node && node.childNodes[point.offset] == next)
  2395. point.pos = this.text.length;
  2396. }
  2397. findPointInside(node, maxLen) {
  2398. for (let point of this.points)
  2399. if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
  2400. point.pos = this.text.length + Math.min(maxLen, point.offset);
  2401. }
  2402. }
  2403. function isBlockElement(node) {
  2404. return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
  2405. }
  2406. class DOMPoint {
  2407. constructor(node, offset) {
  2408. this.node = node;
  2409. this.offset = offset;
  2410. this.pos = -1;
  2411. }
  2412. }
  2413. class DocView extends ContentView {
  2414. constructor(view) {
  2415. super();
  2416. this.view = view;
  2417. this.compositionDeco = Decoration.none;
  2418. this.decorations = [];
  2419. this.dynamicDecorationMap = [];
  2420. // Track a minimum width for the editor. When measuring sizes in
  2421. // measureVisibleLineHeights, this is updated to point at the width
  2422. // of a given element and its extent in the document. When a change
  2423. // happens in that range, these are reset. That way, once we've seen
  2424. // a line/element of a given length, we keep the editor wide enough
  2425. // to fit at least that element, until it is changed, at which point
  2426. // we forget it again.
  2427. this.minWidth = 0;
  2428. this.minWidthFrom = 0;
  2429. this.minWidthTo = 0;
  2430. // Track whether the DOM selection was set in a lossy way, so that
  2431. // we don't mess it up when reading it back it
  2432. this.impreciseAnchor = null;
  2433. this.impreciseHead = null;
  2434. this.forceSelection = false;
  2435. // Used by the resize observer to ignore resizes that we caused
  2436. // ourselves
  2437. this.lastUpdate = Date.now();
  2438. this.setDOM(view.contentDOM);
  2439. this.children = [new LineView];
  2440. this.children[0].setParent(this);
  2441. this.updateDeco();
  2442. this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], 0);
  2443. }
  2444. get root() { return this.view.root; }
  2445. get editorView() { return this.view; }
  2446. get length() { return this.view.state.doc.length; }
  2447. // Update the document view to a given state. scrollIntoView can be
  2448. // used as a hint to compute a new viewport that includes that
  2449. // position, if we know the editor is going to scroll that position
  2450. // into view.
  2451. update(update) {
  2452. let changedRanges = update.changedRanges;
  2453. if (this.minWidth > 0 && changedRanges.length) {
  2454. if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
  2455. this.minWidth = this.minWidthFrom = this.minWidthTo = 0;
  2456. }
  2457. else {
  2458. this.minWidthFrom = update.changes.mapPos(this.minWidthFrom, 1);
  2459. this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
  2460. }
  2461. }
  2462. if (this.view.inputState.composing < 0)
  2463. this.compositionDeco = Decoration.none;
  2464. else if (update.transactions.length || this.dirty)
  2465. this.compositionDeco = computeCompositionDeco(this.view, update.changes);
  2466. // When the DOM nodes around the selection are moved to another
  2467. // parent, Chrome sometimes reports a different selection through
  2468. // getSelection than the one that it actually shows to the user.
  2469. // This forces a selection update when lines are joined to work
  2470. // around that. Issue #54
  2471. if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
  2472. update.state.doc.lines != update.startState.doc.lines)
  2473. this.forceSelection = true;
  2474. let prevDeco = this.decorations, deco = this.updateDeco();
  2475. let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
  2476. changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
  2477. if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
  2478. return false;
  2479. }
  2480. else {
  2481. this.updateInner(changedRanges, update.startState.doc.length);
  2482. if (update.transactions.length)
  2483. this.lastUpdate = Date.now();
  2484. return true;
  2485. }
  2486. }
  2487. // Used by update and the constructor do perform the actual DOM
  2488. // update
  2489. updateInner(changes, oldLength) {
  2490. this.view.viewState.mustMeasureContent = true;
  2491. this.updateChildren(changes, oldLength);
  2492. let { observer } = this.view;
  2493. observer.ignore(() => {
  2494. // Lock the height during redrawing, since Chrome sometimes
  2495. // messes with the scroll position during DOM mutation (though
  2496. // no relayout is triggered and I cannot imagine how it can
  2497. // recompute the scroll position without a layout)
  2498. this.dom.style.height = this.view.viewState.contentHeight + "px";
  2499. this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
  2500. // Chrome will sometimes, when DOM mutations occur directly
  2501. // around the selection, get confused and report a different
  2502. // selection from the one it displays (issue #218). This tries
  2503. // to detect that situation.
  2504. let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
  2505. this.sync(track);
  2506. this.dirty = 0 /* Not */;
  2507. if (track && (track.written || observer.selectionRange.focusNode != track.node))
  2508. this.forceSelection = true;
  2509. this.dom.style.height = "";
  2510. });
  2511. let gaps = [];
  2512. if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
  2513. for (let child of this.children)
  2514. if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
  2515. gaps.push(child.dom);
  2516. observer.updateGaps(gaps);
  2517. }
  2518. updateChildren(changes, oldLength) {
  2519. let cursor = this.childCursor(oldLength);
  2520. for (let i = changes.length - 1;; i--) {
  2521. let next = i >= 0 ? changes[i] : null;
  2522. if (!next)
  2523. break;
  2524. let { fromA, toA, fromB, toB } = next;
  2525. let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, this.decorations, this.dynamicDecorationMap);
  2526. let { i: toI, off: toOff } = cursor.findPos(toA, 1);
  2527. let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
  2528. replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
  2529. }
  2530. }
  2531. // Sync the DOM selection to this.state.selection
  2532. updateSelection(mustRead = false, fromPointer = false) {
  2533. if (mustRead || !this.view.observer.selectionRange.focusNode)
  2534. this.view.observer.readSelectionRange();
  2535. if (!(fromPointer || this.mayControlSelection()) ||
  2536. browser.ios && this.view.inputState.rapidCompositionStart)
  2537. return;
  2538. let force = this.forceSelection;
  2539. this.forceSelection = false;
  2540. let main = this.view.state.selection.main;
  2541. // FIXME need to handle the case where the selection falls inside a block range
  2542. let anchor = this.domAtPos(main.anchor);
  2543. let head = main.empty ? anchor : this.domAtPos(main.head);
  2544. // Always reset on Firefox when next to an uneditable node to
  2545. // avoid invisible cursor bugs (#111)
  2546. if (browser.gecko && main.empty && betweenUneditable(anchor)) {
  2547. let dummy = document.createTextNode("");
  2548. this.view.observer.ignore(() => anchor.node.insertBefore(dummy, anchor.node.childNodes[anchor.offset] || null));
  2549. anchor = head = new DOMPos(dummy, 0);
  2550. force = true;
  2551. }
  2552. let domSel = this.view.observer.selectionRange;
  2553. // If the selection is already here, or in an equivalent position, don't touch it
  2554. if (force || !domSel.focusNode ||
  2555. !isEquivalentPosition(anchor.node, anchor.offset, domSel.anchorNode, domSel.anchorOffset) ||
  2556. !isEquivalentPosition(head.node, head.offset, domSel.focusNode, domSel.focusOffset)) {
  2557. this.view.observer.ignore(() => {
  2558. // Chrome Android will hide the virtual keyboard when tapping
  2559. // inside an uneditable node, and not bring it back when we
  2560. // move the cursor to its proper position. This tries to
  2561. // restore the keyboard by cycling focus.
  2562. if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) &&
  2563. inUneditable(domSel.focusNode, this.dom)) {
  2564. this.dom.blur();
  2565. this.dom.focus({ preventScroll: true });
  2566. }
  2567. let rawSel = getSelection(this.root);
  2568. if (!rawSel) ;
  2569. else if (main.empty) {
  2570. // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076
  2571. if (browser.gecko) {
  2572. let nextTo = nextToUneditable(anchor.node, anchor.offset);
  2573. if (nextTo && nextTo != (1 /* Before */ | 2 /* After */)) {
  2574. let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* Before */ ? 1 : -1);
  2575. if (text)
  2576. anchor = new DOMPos(text, nextTo == 1 /* Before */ ? 0 : text.nodeValue.length);
  2577. }
  2578. }
  2579. rawSel.collapse(anchor.node, anchor.offset);
  2580. if (main.bidiLevel != null && domSel.cursorBidiLevel != null)
  2581. domSel.cursorBidiLevel = main.bidiLevel;
  2582. }
  2583. else if (rawSel.extend) {
  2584. // Selection.extend can be used to create an 'inverted' selection
  2585. // (one where the focus is before the anchor), but not all
  2586. // browsers support it yet.
  2587. rawSel.collapse(anchor.node, anchor.offset);
  2588. rawSel.extend(head.node, head.offset);
  2589. }
  2590. else {
  2591. // Primitive (IE) way
  2592. let range = document.createRange();
  2593. if (main.anchor > main.head)
  2594. [anchor, head] = [head, anchor];
  2595. range.setEnd(head.node, head.offset);
  2596. range.setStart(anchor.node, anchor.offset);
  2597. rawSel.removeAllRanges();
  2598. rawSel.addRange(range);
  2599. }
  2600. });
  2601. this.view.observer.setSelectionRange(anchor, head);
  2602. }
  2603. this.impreciseAnchor = anchor.precise ? null : new DOMPos(domSel.anchorNode, domSel.anchorOffset);
  2604. this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
  2605. }
  2606. enforceCursorAssoc() {
  2607. if (this.compositionDeco.size)
  2608. return;
  2609. let cursor = this.view.state.selection.main;
  2610. let sel = getSelection(this.root);
  2611. if (!sel || !cursor.empty || !cursor.assoc || !sel.modify)
  2612. return;
  2613. let line = LineView.find(this, cursor.head);
  2614. if (!line)
  2615. return;
  2616. let lineStart = line.posAtStart;
  2617. if (cursor.head == lineStart || cursor.head == lineStart + line.length)
  2618. return;
  2619. let before = this.coordsAt(cursor.head, -1), after = this.coordsAt(cursor.head, 1);
  2620. if (!before || !after || before.bottom > after.top)
  2621. return;
  2622. let dom = this.domAtPos(cursor.head + cursor.assoc);
  2623. sel.collapse(dom.node, dom.offset);
  2624. sel.modify("move", cursor.assoc < 0 ? "forward" : "backward", "lineboundary");
  2625. }
  2626. mayControlSelection() {
  2627. let active = this.root.activeElement;
  2628. return active == this.dom ||
  2629. hasSelection(this.dom, this.view.observer.selectionRange) && !(active && this.dom.contains(active));
  2630. }
  2631. nearest(dom) {
  2632. for (let cur = dom; cur;) {
  2633. let domView = ContentView.get(cur);
  2634. if (domView && domView.rootView == this)
  2635. return domView;
  2636. cur = cur.parentNode;
  2637. }
  2638. return null;
  2639. }
  2640. posFromDOM(node, offset) {
  2641. let view = this.nearest(node);
  2642. if (!view)
  2643. throw new RangeError("Trying to find position for a DOM position outside of the document");
  2644. return view.localPosFromDOM(node, offset) + view.posAtStart;
  2645. }
  2646. domAtPos(pos) {
  2647. let { i, off } = this.childCursor().findPos(pos, -1);
  2648. for (; i < this.children.length - 1;) {
  2649. let child = this.children[i];
  2650. if (off < child.length || child instanceof LineView)
  2651. break;
  2652. i++;
  2653. off = 0;
  2654. }
  2655. return this.children[i].domAtPos(off);
  2656. }
  2657. coordsAt(pos, side) {
  2658. for (let off = this.length, i = this.children.length - 1;; i--) {
  2659. let child = this.children[i], start = off - child.breakAfter - child.length;
  2660. if (pos > start ||
  2661. (pos == start && child.type != exports.BlockType.WidgetBefore && child.type != exports.BlockType.WidgetAfter &&
  2662. (!i || side == 2 || this.children[i - 1].breakAfter ||
  2663. (this.children[i - 1].type == exports.BlockType.WidgetBefore && side > -2))))
  2664. return child.coordsAt(pos - start, side);
  2665. off = start;
  2666. }
  2667. }
  2668. measureVisibleLineHeights(viewport) {
  2669. let result = [], { from, to } = viewport;
  2670. let contentWidth = this.view.contentDOM.clientWidth;
  2671. let isWider = contentWidth > Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
  2672. let widest = -1, ltr = this.view.textDirection == exports.Direction.LTR;
  2673. for (let pos = 0, i = 0; i < this.children.length; i++) {
  2674. let child = this.children[i], end = pos + child.length;
  2675. if (end > to)
  2676. break;
  2677. if (pos >= from) {
  2678. let childRect = child.dom.getBoundingClientRect();
  2679. result.push(childRect.height);
  2680. if (isWider) {
  2681. let last = child.dom.lastChild;
  2682. let rects = last ? clientRectsFor(last) : [];
  2683. if (rects.length) {
  2684. let rect = rects[rects.length - 1];
  2685. let width = ltr ? rect.right - childRect.left : childRect.right - rect.left;
  2686. if (width > widest) {
  2687. widest = width;
  2688. this.minWidth = contentWidth;
  2689. this.minWidthFrom = pos;
  2690. this.minWidthTo = end;
  2691. }
  2692. }
  2693. }
  2694. }
  2695. pos = end + child.breakAfter;
  2696. }
  2697. return result;
  2698. }
  2699. textDirectionAt(pos) {
  2700. let { i } = this.childPos(pos, 1);
  2701. return getComputedStyle(this.children[i].dom).direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
  2702. }
  2703. measureTextSize() {
  2704. for (let child of this.children) {
  2705. if (child instanceof LineView) {
  2706. let measure = child.measureTextSize();
  2707. if (measure)
  2708. return measure;
  2709. }
  2710. }
  2711. // If no workable line exists, force a layout of a measurable element
  2712. let dummy = document.createElement("div"), lineHeight, charWidth;
  2713. dummy.className = "cm-line";
  2714. dummy.style.width = "99999px";
  2715. dummy.textContent = "abc def ghi jkl mno pqr stu";
  2716. this.view.observer.ignore(() => {
  2717. this.dom.appendChild(dummy);
  2718. let rect = clientRectsFor(dummy.firstChild)[0];
  2719. lineHeight = dummy.getBoundingClientRect().height;
  2720. charWidth = rect ? rect.width / 27 : 7;
  2721. dummy.remove();
  2722. });
  2723. return { lineHeight, charWidth };
  2724. }
  2725. childCursor(pos = this.length) {
  2726. // Move back to start of last element when possible, so that
  2727. // `ChildCursor.findPos` doesn't have to deal with the edge case
  2728. // of being after the last element.
  2729. let i = this.children.length;
  2730. if (i)
  2731. pos -= this.children[--i].length;
  2732. return new ChildCursor(this.children, pos, i);
  2733. }
  2734. computeBlockGapDeco() {
  2735. let deco = [], vs = this.view.viewState;
  2736. for (let pos = 0, i = 0;; i++) {
  2737. let next = i == vs.viewports.length ? null : vs.viewports[i];
  2738. let end = next ? next.from - 1 : this.length;
  2739. if (end > pos) {
  2740. let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
  2741. deco.push(Decoration.replace({
  2742. widget: new BlockGapWidget(height),
  2743. block: true,
  2744. inclusive: true,
  2745. isBlockGap: true,
  2746. }).range(pos, end));
  2747. }
  2748. if (!next)
  2749. break;
  2750. pos = next.to + 1;
  2751. }
  2752. return Decoration.set(deco);
  2753. }
  2754. updateDeco() {
  2755. let allDeco = this.view.state.facet(decorations).map((d, i) => {
  2756. let dynamic = this.dynamicDecorationMap[i] = typeof d == "function";
  2757. return dynamic ? d(this.view) : d;
  2758. });
  2759. for (let i = allDeco.length; i < allDeco.length + 3; i++)
  2760. this.dynamicDecorationMap[i] = false;
  2761. return this.decorations = [
  2762. ...allDeco,
  2763. this.compositionDeco,
  2764. this.computeBlockGapDeco(),
  2765. this.view.viewState.lineGapDeco
  2766. ];
  2767. }
  2768. scrollIntoView(target) {
  2769. let { range } = target;
  2770. let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
  2771. if (!rect)
  2772. return;
  2773. if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
  2774. rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
  2775. right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
  2776. let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
  2777. for (let margins of this.view.state.facet(scrollMargins).map(f => f(this.view)))
  2778. if (margins) {
  2779. let { left, right, top, bottom } = margins;
  2780. if (left != null)
  2781. mLeft = Math.max(mLeft, left);
  2782. if (right != null)
  2783. mRight = Math.max(mRight, right);
  2784. if (top != null)
  2785. mTop = Math.max(mTop, top);
  2786. if (bottom != null)
  2787. mBottom = Math.max(mBottom, bottom);
  2788. }
  2789. let targetRect = {
  2790. left: rect.left - mLeft, top: rect.top - mTop,
  2791. right: rect.right + mRight, bottom: rect.bottom + mBottom
  2792. };
  2793. scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, target.xMargin, target.yMargin, this.view.textDirection == exports.Direction.LTR);
  2794. }
  2795. }
  2796. function betweenUneditable(pos) {
  2797. return pos.node.nodeType == 1 && pos.node.firstChild &&
  2798. (pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
  2799. (pos.offset == pos.node.childNodes.length || pos.node.childNodes[pos.offset].contentEditable == "false");
  2800. }
  2801. class BlockGapWidget extends WidgetType {
  2802. constructor(height) {
  2803. super();
  2804. this.height = height;
  2805. }
  2806. toDOM() {
  2807. let elt = document.createElement("div");
  2808. this.updateDOM(elt);
  2809. return elt;
  2810. }
  2811. eq(other) { return other.height == this.height; }
  2812. updateDOM(elt) {
  2813. elt.style.height = this.height + "px";
  2814. return true;
  2815. }
  2816. get estimatedHeight() { return this.height; }
  2817. }
  2818. function compositionSurroundingNode(view) {
  2819. let sel = view.observer.selectionRange;
  2820. let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
  2821. if (!textNode)
  2822. return null;
  2823. let cView = view.docView.nearest(textNode);
  2824. if (!cView)
  2825. return null;
  2826. if (cView instanceof LineView) {
  2827. let topNode = textNode;
  2828. while (topNode.parentNode != cView.dom)
  2829. topNode = topNode.parentNode;
  2830. let prev = topNode.previousSibling;
  2831. while (prev && !ContentView.get(prev))
  2832. prev = prev.previousSibling;
  2833. let pos = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
  2834. return { from: pos, to: pos, node: topNode, text: textNode };
  2835. }
  2836. else {
  2837. for (;;) {
  2838. let { parent } = cView;
  2839. if (!parent)
  2840. return null;
  2841. if (parent instanceof LineView)
  2842. break;
  2843. cView = parent;
  2844. }
  2845. let from = cView.posAtStart;
  2846. return { from, to: from + cView.length, node: cView.dom, text: textNode };
  2847. }
  2848. }
  2849. function computeCompositionDeco(view, changes) {
  2850. let surrounding = compositionSurroundingNode(view);
  2851. if (!surrounding)
  2852. return Decoration.none;
  2853. let { from, to, node, text: textNode } = surrounding;
  2854. let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
  2855. let { state } = view, text = node.nodeType == 3 ? node.nodeValue :
  2856. new DOMReader([], state).readRange(node.firstChild, null).text;
  2857. if (newTo - newFrom < text.length) {
  2858. if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length), LineBreakPlaceholder) == text)
  2859. newTo = newFrom + text.length;
  2860. else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo, LineBreakPlaceholder) == text)
  2861. newFrom = newTo - text.length;
  2862. else
  2863. return Decoration.none;
  2864. }
  2865. else if (state.doc.sliceString(newFrom, newTo, LineBreakPlaceholder) != text) {
  2866. return Decoration.none;
  2867. }
  2868. let topView = ContentView.get(node);
  2869. if (topView instanceof CompositionView)
  2870. topView = topView.widget.topView;
  2871. else if (topView)
  2872. topView.parent = null;
  2873. return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView), inclusive: true })
  2874. .range(newFrom, newTo));
  2875. }
  2876. class CompositionWidget extends WidgetType {
  2877. constructor(top, text, topView) {
  2878. super();
  2879. this.top = top;
  2880. this.text = text;
  2881. this.topView = topView;
  2882. }
  2883. eq(other) { return this.top == other.top && this.text == other.text; }
  2884. toDOM() { return this.top; }
  2885. ignoreEvent() { return false; }
  2886. get customView() { return CompositionView; }
  2887. }
  2888. function nearbyTextNode(node, offset, side) {
  2889. for (;;) {
  2890. if (node.nodeType == 3)
  2891. return node;
  2892. if (node.nodeType == 1 && offset > 0 && side <= 0) {
  2893. node = node.childNodes[offset - 1];
  2894. offset = maxOffset(node);
  2895. }
  2896. else if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
  2897. node = node.childNodes[offset];
  2898. offset = 0;
  2899. }
  2900. else {
  2901. return null;
  2902. }
  2903. }
  2904. }
  2905. function nextToUneditable(node, offset) {
  2906. if (node.nodeType != 1)
  2907. return 0;
  2908. return (offset && node.childNodes[offset - 1].contentEditable == "false" ? 1 /* Before */ : 0) |
  2909. (offset < node.childNodes.length && node.childNodes[offset].contentEditable == "false" ? 2 /* After */ : 0);
  2910. }
  2911. class DecorationComparator$1 {
  2912. constructor() {
  2913. this.changes = [];
  2914. }
  2915. compareRange(from, to) { addRange(from, to, this.changes); }
  2916. comparePoint(from, to) { addRange(from, to, this.changes); }
  2917. }
  2918. function findChangedDeco(a, b, diff) {
  2919. let comp = new DecorationComparator$1;
  2920. state.RangeSet.compare(a, b, diff, comp);
  2921. return comp.changes;
  2922. }
  2923. function inUneditable(node, inside) {
  2924. for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
  2925. if (cur.nodeType == 1 && cur.contentEditable == 'false') {
  2926. return true;
  2927. }
  2928. }
  2929. return false;
  2930. }
  2931. function groupAt(state$1, pos, bias = 1) {
  2932. let categorize = state$1.charCategorizer(pos);
  2933. let line = state$1.doc.lineAt(pos), linePos = pos - line.from;
  2934. if (line.length == 0)
  2935. return state.EditorSelection.cursor(pos);
  2936. if (linePos == 0)
  2937. bias = 1;
  2938. else if (linePos == line.length)
  2939. bias = -1;
  2940. let from = linePos, to = linePos;
  2941. if (bias < 0)
  2942. from = state.findClusterBreak(line.text, linePos, false);
  2943. else
  2944. to = state.findClusterBreak(line.text, linePos);
  2945. let cat = categorize(line.text.slice(from, to));
  2946. while (from > 0) {
  2947. let prev = state.findClusterBreak(line.text, from, false);
  2948. if (categorize(line.text.slice(prev, from)) != cat)
  2949. break;
  2950. from = prev;
  2951. }
  2952. while (to < line.length) {
  2953. let next = state.findClusterBreak(line.text, to);
  2954. if (categorize(line.text.slice(to, next)) != cat)
  2955. break;
  2956. to = next;
  2957. }
  2958. return state.EditorSelection.range(from + line.from, to + line.from);
  2959. }
  2960. // Search the DOM for the {node, offset} position closest to the given
  2961. // coordinates. Very inefficient and crude, but can usually be avoided
  2962. // by calling caret(Position|Range)FromPoint instead.
  2963. function getdx(x, rect) {
  2964. return rect.left > x ? rect.left - x : Math.max(0, x - rect.right);
  2965. }
  2966. function getdy(y, rect) {
  2967. return rect.top > y ? rect.top - y : Math.max(0, y - rect.bottom);
  2968. }
  2969. function yOverlap(a, b) {
  2970. return a.top < b.bottom - 1 && a.bottom > b.top + 1;
  2971. }
  2972. function upTop(rect, top) {
  2973. return top < rect.top ? { top, left: rect.left, right: rect.right, bottom: rect.bottom } : rect;
  2974. }
  2975. function upBot(rect, bottom) {
  2976. return bottom > rect.bottom ? { top: rect.top, left: rect.left, right: rect.right, bottom } : rect;
  2977. }
  2978. function domPosAtCoords(parent, x, y) {
  2979. let closest, closestRect, closestX, closestY;
  2980. let above, below, aboveRect, belowRect;
  2981. for (let child = parent.firstChild; child; child = child.nextSibling) {
  2982. let rects = clientRectsFor(child);
  2983. for (let i = 0; i < rects.length; i++) {
  2984. let rect = rects[i];
  2985. if (closestRect && yOverlap(closestRect, rect))
  2986. rect = upTop(upBot(rect, closestRect.bottom), closestRect.top);
  2987. let dx = getdx(x, rect), dy = getdy(y, rect);
  2988. if (dx == 0 && dy == 0)
  2989. return child.nodeType == 3 ? domPosInText(child, x, y) : domPosAtCoords(child, x, y);
  2990. if (!closest || closestY > dy || closestY == dy && closestX > dx) {
  2991. closest = child;
  2992. closestRect = rect;
  2993. closestX = dx;
  2994. closestY = dy;
  2995. }
  2996. if (dx == 0) {
  2997. if (y > rect.bottom && (!aboveRect || aboveRect.bottom < rect.bottom)) {
  2998. above = child;
  2999. aboveRect = rect;
  3000. }
  3001. else if (y < rect.top && (!belowRect || belowRect.top > rect.top)) {
  3002. below = child;
  3003. belowRect = rect;
  3004. }
  3005. }
  3006. else if (aboveRect && yOverlap(aboveRect, rect)) {
  3007. aboveRect = upBot(aboveRect, rect.bottom);
  3008. }
  3009. else if (belowRect && yOverlap(belowRect, rect)) {
  3010. belowRect = upTop(belowRect, rect.top);
  3011. }
  3012. }
  3013. }
  3014. if (aboveRect && aboveRect.bottom >= y) {
  3015. closest = above;
  3016. closestRect = aboveRect;
  3017. }
  3018. else if (belowRect && belowRect.top <= y) {
  3019. closest = below;
  3020. closestRect = belowRect;
  3021. }
  3022. if (!closest)
  3023. return { node: parent, offset: 0 };
  3024. let clipX = Math.max(closestRect.left, Math.min(closestRect.right, x));
  3025. if (closest.nodeType == 3)
  3026. return domPosInText(closest, clipX, y);
  3027. if (!closestX && closest.contentEditable == "true")
  3028. return domPosAtCoords(closest, clipX, y);
  3029. let offset = Array.prototype.indexOf.call(parent.childNodes, closest) +
  3030. (x >= (closestRect.left + closestRect.right) / 2 ? 1 : 0);
  3031. return { node: parent, offset };
  3032. }
  3033. function domPosInText(node, x, y) {
  3034. let len = node.nodeValue.length;
  3035. let closestOffset = -1, closestDY = 1e9, generalSide = 0;
  3036. for (let i = 0; i < len; i++) {
  3037. let rects = textRange(node, i, i + 1).getClientRects();
  3038. for (let j = 0; j < rects.length; j++) {
  3039. let rect = rects[j];
  3040. if (rect.top == rect.bottom)
  3041. continue;
  3042. if (!generalSide)
  3043. generalSide = x - rect.left;
  3044. let dy = (rect.top > y ? rect.top - y : y - rect.bottom) - 1;
  3045. if (rect.left - 1 <= x && rect.right + 1 >= x && dy < closestDY) {
  3046. let right = x >= (rect.left + rect.right) / 2, after = right;
  3047. if (browser.chrome || browser.gecko) {
  3048. // Check for RTL on browsers that support getting client
  3049. // rects for empty ranges.
  3050. let rectBefore = textRange(node, i).getBoundingClientRect();
  3051. if (rectBefore.left == rect.right)
  3052. after = !right;
  3053. }
  3054. if (dy <= 0)
  3055. return { node, offset: i + (after ? 1 : 0) };
  3056. closestOffset = i + (after ? 1 : 0);
  3057. closestDY = dy;
  3058. }
  3059. }
  3060. }
  3061. return { node, offset: closestOffset > -1 ? closestOffset : generalSide > 0 ? node.nodeValue.length : 0 };
  3062. }
  3063. function posAtCoords(view, { x, y }, precise, bias = -1) {
  3064. var _a;
  3065. let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
  3066. let block, { docHeight } = view.viewState;
  3067. let yOffset = y - docTop;
  3068. if (yOffset < 0)
  3069. return 0;
  3070. if (yOffset > docHeight)
  3071. return view.state.doc.length;
  3072. // Scan for a text block near the queried y position
  3073. for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
  3074. block = view.elementAtHeight(yOffset);
  3075. if (block.type == exports.BlockType.Text)
  3076. break;
  3077. for (;;) {
  3078. // Move the y position out of this block
  3079. yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
  3080. if (yOffset >= 0 && yOffset <= docHeight)
  3081. break;
  3082. // If the document consists entirely of replaced widgets, we
  3083. // won't find a text block, so return 0
  3084. if (bounced)
  3085. return precise ? null : 0;
  3086. bounced = true;
  3087. bias = -bias;
  3088. }
  3089. }
  3090. y = docTop + yOffset;
  3091. let lineStart = block.from;
  3092. // If this is outside of the rendered viewport, we can't determine a position
  3093. if (lineStart < view.viewport.from)
  3094. return view.viewport.from == 0 ? 0 : precise ? null : posAtCoordsImprecise(view, content, block, x, y);
  3095. if (lineStart > view.viewport.to)
  3096. return view.viewport.to == view.state.doc.length ? view.state.doc.length :
  3097. precise ? null : posAtCoordsImprecise(view, content, block, x, y);
  3098. // Prefer ShadowRootOrDocument.elementFromPoint if present, fall back to document if not
  3099. let doc = view.dom.ownerDocument;
  3100. let root = view.root.elementFromPoint ? view.root : doc;
  3101. let element = root.elementFromPoint(x, y);
  3102. if (element && !view.contentDOM.contains(element))
  3103. element = null;
  3104. // If the element is unexpected, clip x at the sides of the content area and try again
  3105. if (!element) {
  3106. x = Math.max(content.left + 1, Math.min(content.right - 1, x));
  3107. element = root.elementFromPoint(x, y);
  3108. if (element && !view.contentDOM.contains(element))
  3109. element = null;
  3110. }
  3111. // There's visible editor content under the point, so we can try
  3112. // using caret(Position|Range)FromPoint as a shortcut
  3113. let node, offset = -1;
  3114. if (element && ((_a = view.docView.nearest(element)) === null || _a === void 0 ? void 0 : _a.isEditable) != false) {
  3115. if (doc.caretPositionFromPoint) {
  3116. let pos = doc.caretPositionFromPoint(x, y);
  3117. if (pos)
  3118. ({ offsetNode: node, offset } = pos);
  3119. }
  3120. else if (doc.caretRangeFromPoint) {
  3121. let range = doc.caretRangeFromPoint(x, y);
  3122. if (range) {
  3123. ({ startContainer: node, startOffset: offset } = range);
  3124. if (browser.safari && isSuspiciousSafariCaretResult(node, offset, x) ||
  3125. browser.chrome && isSuspiciousChromeCaretResult(node, offset, x))
  3126. node = undefined;
  3127. }
  3128. }
  3129. }
  3130. // No luck, do our own (potentially expensive) search
  3131. if (!node || !view.docView.dom.contains(node)) {
  3132. let line = LineView.find(view.docView, lineStart);
  3133. if (!line)
  3134. return yOffset > block.top + block.height / 2 ? block.to : block.from;
  3135. ({ node, offset } = domPosAtCoords(line.dom, x, y));
  3136. }
  3137. return view.docView.posFromDOM(node, offset);
  3138. }
  3139. function posAtCoordsImprecise(view, contentRect, block, x, y) {
  3140. let into = Math.round((x - contentRect.left) * view.defaultCharacterWidth);
  3141. if (view.lineWrapping && block.height > view.defaultLineHeight * 1.5) {
  3142. let line = Math.floor((y - block.top) / view.defaultLineHeight);
  3143. into += line * view.viewState.heightOracle.lineLength;
  3144. }
  3145. let content = view.state.sliceDoc(block.from, block.to);
  3146. return block.from + state.findColumn(content, into, view.state.tabSize);
  3147. }
  3148. // In case of a high line height, Safari's caretRangeFromPoint treats
  3149. // the space between lines as belonging to the last character of the
  3150. // line before. This is used to detect such a result so that it can be
  3151. // ignored (issue #401).
  3152. function isSuspiciousSafariCaretResult(node, offset, x) {
  3153. let len;
  3154. if (node.nodeType != 3 || offset != (len = node.nodeValue.length))
  3155. return false;
  3156. for (let next = node.nextSibling; next; next = next.nextSibling)
  3157. if (next.nodeType != 1 || next.nodeName != "BR")
  3158. return false;
  3159. return textRange(node, len - 1, len).getBoundingClientRect().left > x;
  3160. }
  3161. // Chrome will move positions between lines to the start of the next line
  3162. function isSuspiciousChromeCaretResult(node, offset, x) {
  3163. if (offset != 0)
  3164. return false;
  3165. for (let cur = node;;) {
  3166. let parent = cur.parentNode;
  3167. if (!parent || parent.nodeType != 1 || parent.firstChild != cur)
  3168. return false;
  3169. if (parent.classList.contains("cm-line"))
  3170. break;
  3171. cur = parent;
  3172. }
  3173. let rect = node.nodeType == 1 ? node.getBoundingClientRect()
  3174. : textRange(node, 0, Math.max(node.nodeValue.length, 1)).getBoundingClientRect();
  3175. return x - rect.left > 5;
  3176. }
  3177. function moveToLineBoundary(view, start, forward, includeWrap) {
  3178. let line = view.state.doc.lineAt(start.head);
  3179. let coords = !includeWrap || !view.lineWrapping ? null
  3180. : view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head);
  3181. if (coords) {
  3182. let editorRect = view.dom.getBoundingClientRect();
  3183. let direction = view.textDirectionAt(line.from);
  3184. let pos = view.posAtCoords({ x: forward == (direction == exports.Direction.LTR) ? editorRect.right - 1 : editorRect.left + 1,
  3185. y: (coords.top + coords.bottom) / 2 });
  3186. if (pos != null)
  3187. return state.EditorSelection.cursor(pos, forward ? -1 : 1);
  3188. }
  3189. let lineView = LineView.find(view.docView, start.head);
  3190. let end = lineView ? (forward ? lineView.posAtEnd : lineView.posAtStart) : (forward ? line.to : line.from);
  3191. return state.EditorSelection.cursor(end, forward ? -1 : 1);
  3192. }
  3193. function moveByChar(view, start, forward, by) {
  3194. let line = view.state.doc.lineAt(start.head), spans = view.bidiSpans(line);
  3195. let direction = view.textDirectionAt(line.from);
  3196. for (let cur = start, check = null;;) {
  3197. let next = moveVisually(line, spans, direction, cur, forward), char = movedOver;
  3198. if (!next) {
  3199. if (line.number == (forward ? view.state.doc.lines : 1))
  3200. return cur;
  3201. char = "\n";
  3202. line = view.state.doc.line(line.number + (forward ? 1 : -1));
  3203. spans = view.bidiSpans(line);
  3204. next = state.EditorSelection.cursor(forward ? line.from : line.to);
  3205. }
  3206. if (!check) {
  3207. if (!by)
  3208. return next;
  3209. check = by(char);
  3210. }
  3211. else if (!check(char)) {
  3212. return cur;
  3213. }
  3214. cur = next;
  3215. }
  3216. }
  3217. function byGroup(view, pos, start) {
  3218. let categorize = view.state.charCategorizer(pos);
  3219. let cat = categorize(start);
  3220. return (next) => {
  3221. let nextCat = categorize(next);
  3222. if (cat == state.CharCategory.Space)
  3223. cat = nextCat;
  3224. return cat == nextCat;
  3225. };
  3226. }
  3227. function moveVertically(view, start, forward, distance) {
  3228. let startPos = start.head, dir = forward ? 1 : -1;
  3229. if (startPos == (forward ? view.state.doc.length : 0))
  3230. return state.EditorSelection.cursor(startPos, start.assoc);
  3231. let goal = start.goalColumn, startY;
  3232. let rect = view.contentDOM.getBoundingClientRect();
  3233. let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
  3234. if (startCoords) {
  3235. if (goal == null)
  3236. goal = startCoords.left - rect.left;
  3237. startY = dir < 0 ? startCoords.top : startCoords.bottom;
  3238. }
  3239. else {
  3240. let line = view.viewState.lineBlockAt(startPos);
  3241. if (goal == null)
  3242. goal = Math.min(rect.right - rect.left, view.defaultCharacterWidth * (startPos - line.from));
  3243. startY = (dir < 0 ? line.top : line.bottom) + docTop;
  3244. }
  3245. let resolvedGoal = rect.left + goal;
  3246. let dist = distance !== null && distance !== void 0 ? distance : (view.defaultLineHeight >> 1);
  3247. for (let extra = 0;; extra += 10) {
  3248. let curY = startY + (dist + extra) * dir;
  3249. let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir);
  3250. if (curY < rect.top || curY > rect.bottom || (dir < 0 ? pos < startPos : pos > startPos))
  3251. return state.EditorSelection.cursor(pos, start.assoc, undefined, goal);
  3252. }
  3253. }
  3254. function skipAtoms(view, oldPos, pos) {
  3255. let atoms = view.state.facet(atomicRanges).map(f => f(view));
  3256. for (;;) {
  3257. let moved = false;
  3258. for (let set of atoms) {
  3259. set.between(pos.from - 1, pos.from + 1, (from, to, value) => {
  3260. if (pos.from > from && pos.from < to) {
  3261. pos = oldPos.from > pos.from ? state.EditorSelection.cursor(from, 1) : state.EditorSelection.cursor(to, -1);
  3262. moved = true;
  3263. }
  3264. });
  3265. }
  3266. if (!moved)
  3267. return pos;
  3268. }
  3269. }
  3270. // This will also be where dragging info and such goes
  3271. class InputState {
  3272. constructor(view) {
  3273. this.lastKeyCode = 0;
  3274. this.lastKeyTime = 0;
  3275. this.chromeScrollHack = -1;
  3276. // On iOS, some keys need to have their default behavior happen
  3277. // (after which we retroactively handle them and reset the DOM) to
  3278. // avoid messing up the virtual keyboard state.
  3279. this.pendingIOSKey = undefined;
  3280. this.lastSelectionOrigin = null;
  3281. this.lastSelectionTime = 0;
  3282. this.lastEscPress = 0;
  3283. this.lastContextMenu = 0;
  3284. this.scrollHandlers = [];
  3285. this.registeredEvents = [];
  3286. this.customHandlers = [];
  3287. // -1 means not in a composition. Otherwise, this counts the number
  3288. // of changes made during the composition. The count is used to
  3289. // avoid treating the start state of the composition, before any
  3290. // changes have been made, as part of the composition.
  3291. this.composing = -1;
  3292. // Tracks whether the next change should be marked as starting the
  3293. // composition (null means no composition, true means next is the
  3294. // first, false means first has already been marked for this
  3295. // composition)
  3296. this.compositionFirstChange = null;
  3297. this.compositionEndedAt = 0;
  3298. this.rapidCompositionStart = false;
  3299. this.mouseSelection = null;
  3300. for (let type in handlers) {
  3301. let handler = handlers[type];
  3302. view.contentDOM.addEventListener(type, (event) => {
  3303. if (!eventBelongsToEditor(view, event) || this.ignoreDuringComposition(event))
  3304. return;
  3305. if (type == "keydown" && this.keydown(view, event))
  3306. return;
  3307. if (this.mustFlushObserver(event))
  3308. view.observer.forceFlush();
  3309. if (this.runCustomHandlers(type, view, event))
  3310. event.preventDefault();
  3311. else
  3312. handler(view, event);
  3313. });
  3314. this.registeredEvents.push(type);
  3315. }
  3316. if (browser.chrome && browser.chrome_version >= 102) {
  3317. // On Chrome 102, viewport updates somehow stop wheel-based
  3318. // scrolling. Turning off pointer events during the scroll seems
  3319. // to avoid the issue.
  3320. view.scrollDOM.addEventListener("wheel", () => {
  3321. if (this.chromeScrollHack < 0)
  3322. view.contentDOM.style.pointerEvents = "none";
  3323. else
  3324. window.clearTimeout(this.chromeScrollHack);
  3325. this.chromeScrollHack = setTimeout(() => {
  3326. this.chromeScrollHack = -1;
  3327. view.contentDOM.style.pointerEvents = "";
  3328. }, 100);
  3329. }, { passive: true });
  3330. }
  3331. this.notifiedFocused = view.hasFocus;
  3332. // On Safari adding an input event handler somehow prevents an
  3333. // issue where the composition vanishes when you press enter.
  3334. if (browser.safari)
  3335. view.contentDOM.addEventListener("input", () => null);
  3336. }
  3337. setSelectionOrigin(origin) {
  3338. this.lastSelectionOrigin = origin;
  3339. this.lastSelectionTime = Date.now();
  3340. }
  3341. ensureHandlers(view, plugins) {
  3342. var _a;
  3343. let handlers;
  3344. this.customHandlers = [];
  3345. for (let plugin of plugins)
  3346. if (handlers = (_a = plugin.update(view).spec) === null || _a === void 0 ? void 0 : _a.domEventHandlers) {
  3347. this.customHandlers.push({ plugin: plugin.value, handlers });
  3348. for (let type in handlers)
  3349. if (this.registeredEvents.indexOf(type) < 0 && type != "scroll") {
  3350. this.registeredEvents.push(type);
  3351. view.contentDOM.addEventListener(type, (event) => {
  3352. if (!eventBelongsToEditor(view, event))
  3353. return;
  3354. if (this.runCustomHandlers(type, view, event))
  3355. event.preventDefault();
  3356. });
  3357. }
  3358. }
  3359. }
  3360. runCustomHandlers(type, view, event) {
  3361. for (let set of this.customHandlers) {
  3362. let handler = set.handlers[type];
  3363. if (handler) {
  3364. try {
  3365. if (handler.call(set.plugin, event, view) || event.defaultPrevented)
  3366. return true;
  3367. }
  3368. catch (e) {
  3369. logException(view.state, e);
  3370. }
  3371. }
  3372. }
  3373. return false;
  3374. }
  3375. runScrollHandlers(view, event) {
  3376. for (let set of this.customHandlers) {
  3377. let handler = set.handlers.scroll;
  3378. if (handler) {
  3379. try {
  3380. handler.call(set.plugin, event, view);
  3381. }
  3382. catch (e) {
  3383. logException(view.state, e);
  3384. }
  3385. }
  3386. }
  3387. }
  3388. keydown(view, event) {
  3389. // Must always run, even if a custom handler handled the event
  3390. this.lastKeyCode = event.keyCode;
  3391. this.lastKeyTime = Date.now();
  3392. if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
  3393. return true;
  3394. // Chrome for Android usually doesn't fire proper key events, but
  3395. // occasionally does, usually surrounded by a bunch of complicated
  3396. // composition changes. When an enter or backspace key event is
  3397. // seen, hold off on handling DOM events for a bit, and then
  3398. // dispatch it.
  3399. if (browser.android && browser.chrome && !event.synthetic &&
  3400. (event.keyCode == 13 || event.keyCode == 8)) {
  3401. view.observer.delayAndroidKey(event.key, event.keyCode);
  3402. return true;
  3403. }
  3404. // Prevent the default behavior of Enter on iOS makes the
  3405. // virtual keyboard get stuck in the wrong (lowercase)
  3406. // state. So we let it go through, and then, in
  3407. // applyDOMChange, notify key handlers of it and reset to
  3408. // the state they produce.
  3409. let pending;
  3410. if (browser.ios && (pending = PendingKeys.find(key => key.keyCode == event.keyCode)) &&
  3411. !(event.ctrlKey || event.altKey || event.metaKey) && !event.synthetic) {
  3412. this.pendingIOSKey = pending;
  3413. setTimeout(() => this.flushIOSKey(view), 250);
  3414. return true;
  3415. }
  3416. return false;
  3417. }
  3418. flushIOSKey(view) {
  3419. let key = this.pendingIOSKey;
  3420. if (!key)
  3421. return false;
  3422. this.pendingIOSKey = undefined;
  3423. return dispatchKey(view.contentDOM, key.key, key.keyCode);
  3424. }
  3425. ignoreDuringComposition(event) {
  3426. if (!/^key/.test(event.type))
  3427. return false;
  3428. if (this.composing > 0)
  3429. return true;
  3430. // See https://www.stum.de/2016/06/24/handling-ime-events-in-javascript/.
  3431. // On some input method editors (IMEs), the Enter key is used to
  3432. // confirm character selection. On Safari, when Enter is pressed,
  3433. // compositionend and keydown events are sometimes emitted in the
  3434. // wrong order. The key event should still be ignored, even when
  3435. // it happens after the compositionend event.
  3436. if (browser.safari && Date.now() - this.compositionEndedAt < 100) {
  3437. this.compositionEndedAt = 0;
  3438. return true;
  3439. }
  3440. return false;
  3441. }
  3442. mustFlushObserver(event) {
  3443. return (event.type == "keydown" && event.keyCode != 229) ||
  3444. event.type == "compositionend" && !browser.ios;
  3445. }
  3446. startMouseSelection(mouseSelection) {
  3447. if (this.mouseSelection)
  3448. this.mouseSelection.destroy();
  3449. this.mouseSelection = mouseSelection;
  3450. }
  3451. update(update) {
  3452. if (this.mouseSelection)
  3453. this.mouseSelection.update(update);
  3454. if (update.transactions.length)
  3455. this.lastKeyCode = this.lastSelectionTime = 0;
  3456. }
  3457. destroy() {
  3458. if (this.mouseSelection)
  3459. this.mouseSelection.destroy();
  3460. }
  3461. }
  3462. const PendingKeys = [
  3463. { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
  3464. { key: "Enter", keyCode: 13, inputType: "insertParagraph" },
  3465. { key: "Delete", keyCode: 46, inputType: "deleteContentForward" }
  3466. ];
  3467. // Key codes for modifier keys
  3468. const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225];
  3469. class MouseSelection {
  3470. constructor(view, startEvent, style, mustSelect) {
  3471. this.view = view;
  3472. this.style = style;
  3473. this.mustSelect = mustSelect;
  3474. this.lastEvent = startEvent;
  3475. let doc = view.contentDOM.ownerDocument;
  3476. doc.addEventListener("mousemove", this.move = this.move.bind(this));
  3477. doc.addEventListener("mouseup", this.up = this.up.bind(this));
  3478. this.extend = startEvent.shiftKey;
  3479. this.multiple = view.state.facet(state.EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
  3480. this.dragMove = dragMovesSelection(view, startEvent);
  3481. this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
  3482. // When clicking outside of the selection, immediately apply the
  3483. // effect of starting the selection
  3484. if (this.dragging === false) {
  3485. startEvent.preventDefault();
  3486. this.select(startEvent);
  3487. }
  3488. }
  3489. move(event) {
  3490. if (event.buttons == 0)
  3491. return this.destroy();
  3492. if (this.dragging !== false)
  3493. return;
  3494. this.select(this.lastEvent = event);
  3495. }
  3496. up(event) {
  3497. if (this.dragging == null)
  3498. this.select(this.lastEvent);
  3499. if (!this.dragging)
  3500. event.preventDefault();
  3501. this.destroy();
  3502. }
  3503. destroy() {
  3504. let doc = this.view.contentDOM.ownerDocument;
  3505. doc.removeEventListener("mousemove", this.move);
  3506. doc.removeEventListener("mouseup", this.up);
  3507. this.view.inputState.mouseSelection = null;
  3508. }
  3509. select(event) {
  3510. let selection = this.style.get(event, this.extend, this.multiple);
  3511. if (this.mustSelect || !selection.eq(this.view.state.selection) ||
  3512. selection.main.assoc != this.view.state.selection.main.assoc)
  3513. this.view.dispatch({
  3514. selection,
  3515. userEvent: "select.pointer",
  3516. scrollIntoView: true
  3517. });
  3518. this.mustSelect = false;
  3519. }
  3520. update(update) {
  3521. if (update.docChanged && this.dragging)
  3522. this.dragging = this.dragging.map(update.changes);
  3523. if (this.style.update(update))
  3524. setTimeout(() => this.select(this.lastEvent), 20);
  3525. }
  3526. }
  3527. function addsSelectionRange(view, event) {
  3528. let facet = view.state.facet(clickAddsSelectionRange);
  3529. return facet.length ? facet[0](event) : browser.mac ? event.metaKey : event.ctrlKey;
  3530. }
  3531. function dragMovesSelection(view, event) {
  3532. let facet = view.state.facet(dragMovesSelection$1);
  3533. return facet.length ? facet[0](event) : browser.mac ? !event.altKey : !event.ctrlKey;
  3534. }
  3535. function isInPrimarySelection(view, event) {
  3536. let { main } = view.state.selection;
  3537. if (main.empty)
  3538. return false;
  3539. // On boundary clicks, check whether the coordinates are inside the
  3540. // selection's client rectangles
  3541. let sel = getSelection(view.root);
  3542. if (!sel || sel.rangeCount == 0)
  3543. return true;
  3544. let rects = sel.getRangeAt(0).getClientRects();
  3545. for (let i = 0; i < rects.length; i++) {
  3546. let rect = rects[i];
  3547. if (rect.left <= event.clientX && rect.right >= event.clientX &&
  3548. rect.top <= event.clientY && rect.bottom >= event.clientY)
  3549. return true;
  3550. }
  3551. return false;
  3552. }
  3553. function eventBelongsToEditor(view, event) {
  3554. if (!event.bubbles)
  3555. return true;
  3556. if (event.defaultPrevented)
  3557. return false;
  3558. for (let node = event.target, cView; node != view.contentDOM; node = node.parentNode)
  3559. if (!node || node.nodeType == 11 || ((cView = ContentView.get(node)) && cView.ignoreEvent(event)))
  3560. return false;
  3561. return true;
  3562. }
  3563. const handlers = Object.create(null);
  3564. // This is very crude, but unfortunately both these browsers _pretend_
  3565. // that they have a clipboard API—all the objects and methods are
  3566. // there, they just don't work, and they are hard to test.
  3567. const brokenClipboardAPI = (browser.ie && browser.ie_version < 15) ||
  3568. (browser.ios && browser.webkit_version < 604);
  3569. function capturePaste(view) {
  3570. let parent = view.dom.parentNode;
  3571. if (!parent)
  3572. return;
  3573. let target = parent.appendChild(document.createElement("textarea"));
  3574. target.style.cssText = "position: fixed; left: -10000px; top: 10px";
  3575. target.focus();
  3576. setTimeout(() => {
  3577. view.focus();
  3578. target.remove();
  3579. doPaste(view, target.value);
  3580. }, 50);
  3581. }
  3582. function doPaste(view, input) {
  3583. let { state: state$1 } = view, changes, i = 1, text = state$1.toText(input);
  3584. let byLine = text.lines == state$1.selection.ranges.length;
  3585. let linewise = lastLinewiseCopy != null && state$1.selection.ranges.every(r => r.empty) && lastLinewiseCopy == text.toString();
  3586. if (linewise) {
  3587. let lastLine = -1;
  3588. changes = state$1.changeByRange(range => {
  3589. let line = state$1.doc.lineAt(range.from);
  3590. if (line.from == lastLine)
  3591. return { range };
  3592. lastLine = line.from;
  3593. let insert = state$1.toText((byLine ? text.line(i++).text : input) + state$1.lineBreak);
  3594. return { changes: { from: line.from, insert },
  3595. range: state.EditorSelection.cursor(range.from + insert.length) };
  3596. });
  3597. }
  3598. else if (byLine) {
  3599. changes = state$1.changeByRange(range => {
  3600. let line = text.line(i++);
  3601. return { changes: { from: range.from, to: range.to, insert: line.text },
  3602. range: state.EditorSelection.cursor(range.from + line.length) };
  3603. });
  3604. }
  3605. else {
  3606. changes = state$1.replaceSelection(text);
  3607. }
  3608. view.dispatch(changes, {
  3609. userEvent: "input.paste",
  3610. scrollIntoView: true
  3611. });
  3612. }
  3613. handlers.keydown = (view, event) => {
  3614. view.inputState.setSelectionOrigin("select");
  3615. if (event.keyCode == 27)
  3616. view.inputState.lastEscPress = Date.now();
  3617. else if (modifierCodes.indexOf(event.keyCode) < 0)
  3618. view.inputState.lastEscPress = 0;
  3619. };
  3620. let lastTouch = 0;
  3621. handlers.touchstart = (view, e) => {
  3622. lastTouch = Date.now();
  3623. view.inputState.setSelectionOrigin("select.pointer");
  3624. };
  3625. handlers.touchmove = view => {
  3626. view.inputState.setSelectionOrigin("select.pointer");
  3627. };
  3628. handlers.mousedown = (view, event) => {
  3629. view.observer.flush();
  3630. if (lastTouch > Date.now() - 2000 && getClickType(event) == 1)
  3631. return; // Ignore touch interaction
  3632. let style = null;
  3633. for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
  3634. style = makeStyle(view, event);
  3635. if (style)
  3636. break;
  3637. }
  3638. if (!style && event.button == 0)
  3639. style = basicMouseSelection(view, event);
  3640. if (style) {
  3641. let mustFocus = view.root.activeElement != view.contentDOM;
  3642. if (mustFocus)
  3643. view.observer.ignore(() => focusPreventScroll(view.contentDOM));
  3644. view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
  3645. }
  3646. };
  3647. function rangeForClick(view, pos, bias, type) {
  3648. if (type == 1) { // Single click
  3649. return state.EditorSelection.cursor(pos, bias);
  3650. }
  3651. else if (type == 2) { // Double click
  3652. return groupAt(view.state, pos, bias);
  3653. }
  3654. else { // Triple click
  3655. let visual = LineView.find(view.docView, pos), line = view.state.doc.lineAt(visual ? visual.posAtEnd : pos);
  3656. let from = visual ? visual.posAtStart : line.from, to = visual ? visual.posAtEnd : line.to;
  3657. if (to < view.state.doc.length && to == line.to)
  3658. to++;
  3659. return state.EditorSelection.range(from, to);
  3660. }
  3661. }
  3662. let insideY = (y, rect) => y >= rect.top && y <= rect.bottom;
  3663. let inside = (x, y, rect) => insideY(y, rect) && x >= rect.left && x <= rect.right;
  3664. // Try to determine, for the given coordinates, associated with the
  3665. // given position, whether they are related to the element before or
  3666. // the element after the position.
  3667. function findPositionSide(view, pos, x, y) {
  3668. let line = LineView.find(view.docView, pos);
  3669. if (!line)
  3670. return 1;
  3671. let off = pos - line.posAtStart;
  3672. // Line boundaries point into the line
  3673. if (off == 0)
  3674. return 1;
  3675. if (off == line.length)
  3676. return -1;
  3677. // Positions on top of an element point at that element
  3678. let before = line.coordsAt(off, -1);
  3679. if (before && inside(x, y, before))
  3680. return -1;
  3681. let after = line.coordsAt(off, 1);
  3682. if (after && inside(x, y, after))
  3683. return 1;
  3684. // This is probably a line wrap point. Pick before if the point is
  3685. // beside it.
  3686. return before && insideY(y, before) ? -1 : 1;
  3687. }
  3688. function queryPos(view, event) {
  3689. let pos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
  3690. return { pos, bias: findPositionSide(view, pos, event.clientX, event.clientY) };
  3691. }
  3692. const BadMouseDetail = browser.ie && browser.ie_version <= 11;
  3693. let lastMouseDown = null, lastMouseDownCount = 0, lastMouseDownTime = 0;
  3694. function getClickType(event) {
  3695. if (!BadMouseDetail)
  3696. return event.detail;
  3697. let last = lastMouseDown, lastTime = lastMouseDownTime;
  3698. lastMouseDown = event;
  3699. lastMouseDownTime = Date.now();
  3700. return lastMouseDownCount = !last || (lastTime > Date.now() - 400 && Math.abs(last.clientX - event.clientX) < 2 &&
  3701. Math.abs(last.clientY - event.clientY) < 2) ? (lastMouseDownCount + 1) % 3 : 1;
  3702. }
  3703. function basicMouseSelection(view, event) {
  3704. let start = queryPos(view, event), type = getClickType(event);
  3705. let startSel = view.state.selection;
  3706. let last = start, lastEvent = event;
  3707. return {
  3708. update(update) {
  3709. if (update.docChanged) {
  3710. if (start)
  3711. start.pos = update.changes.mapPos(start.pos);
  3712. startSel = startSel.map(update.changes);
  3713. lastEvent = null;
  3714. }
  3715. },
  3716. get(event, extend, multiple) {
  3717. let cur;
  3718. if (lastEvent && event.clientX == lastEvent.clientX && event.clientY == lastEvent.clientY)
  3719. cur = last;
  3720. else {
  3721. cur = last = queryPos(view, event);
  3722. lastEvent = event;
  3723. }
  3724. if (!cur || !start)
  3725. return startSel;
  3726. let range = rangeForClick(view, cur.pos, cur.bias, type);
  3727. if (start.pos != cur.pos && !extend) {
  3728. let startRange = rangeForClick(view, start.pos, start.bias, type);
  3729. let from = Math.min(startRange.from, range.from), to = Math.max(startRange.to, range.to);
  3730. range = from < range.from ? state.EditorSelection.range(from, to) : state.EditorSelection.range(to, from);
  3731. }
  3732. if (extend)
  3733. return startSel.replaceRange(startSel.main.extend(range.from, range.to));
  3734. else if (multiple && startSel.ranges.length > 1 && startSel.ranges.some(r => r.eq(range)))
  3735. return removeRange(startSel, range);
  3736. else if (multiple)
  3737. return startSel.addRange(range);
  3738. else
  3739. return state.EditorSelection.create([range]);
  3740. }
  3741. };
  3742. }
  3743. function removeRange(sel, range) {
  3744. for (let i = 0;; i++) {
  3745. if (sel.ranges[i].eq(range))
  3746. return state.EditorSelection.create(sel.ranges.slice(0, i).concat(sel.ranges.slice(i + 1)), sel.mainIndex == i ? 0 : sel.mainIndex - (sel.mainIndex > i ? 1 : 0));
  3747. }
  3748. }
  3749. handlers.dragstart = (view, event) => {
  3750. let { selection: { main } } = view.state;
  3751. let { mouseSelection } = view.inputState;
  3752. if (mouseSelection)
  3753. mouseSelection.dragging = main;
  3754. if (event.dataTransfer) {
  3755. event.dataTransfer.setData("Text", view.state.sliceDoc(main.from, main.to));
  3756. event.dataTransfer.effectAllowed = "copyMove";
  3757. }
  3758. };
  3759. function dropText(view, event, text, direct) {
  3760. if (!text)
  3761. return;
  3762. let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
  3763. event.preventDefault();
  3764. let { mouseSelection } = view.inputState;
  3765. let del = direct && mouseSelection && mouseSelection.dragging && mouseSelection.dragMove ?
  3766. { from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
  3767. let ins = { from: dropPos, insert: text };
  3768. let changes = view.state.changes(del ? [del, ins] : ins);
  3769. view.focus();
  3770. view.dispatch({
  3771. changes,
  3772. selection: { anchor: changes.mapPos(dropPos, -1), head: changes.mapPos(dropPos, 1) },
  3773. userEvent: del ? "move.drop" : "input.drop"
  3774. });
  3775. }
  3776. handlers.drop = (view, event) => {
  3777. if (!event.dataTransfer)
  3778. return;
  3779. if (view.state.readOnly)
  3780. return event.preventDefault();
  3781. let files = event.dataTransfer.files;
  3782. if (files && files.length) { // For a file drop, read the file's text.
  3783. event.preventDefault();
  3784. let text = Array(files.length), read = 0;
  3785. let finishFile = () => {
  3786. if (++read == files.length)
  3787. dropText(view, event, text.filter(s => s != null).join(view.state.lineBreak), false);
  3788. };
  3789. for (let i = 0; i < files.length; i++) {
  3790. let reader = new FileReader;
  3791. reader.onerror = finishFile;
  3792. reader.onload = () => {
  3793. if (!/[\x00-\x08\x0e-\x1f]{2}/.test(reader.result))
  3794. text[i] = reader.result;
  3795. finishFile();
  3796. };
  3797. reader.readAsText(files[i]);
  3798. }
  3799. }
  3800. else {
  3801. dropText(view, event, event.dataTransfer.getData("Text"), true);
  3802. }
  3803. };
  3804. handlers.paste = (view, event) => {
  3805. if (view.state.readOnly)
  3806. return event.preventDefault();
  3807. view.observer.flush();
  3808. let data = brokenClipboardAPI ? null : event.clipboardData;
  3809. if (data) {
  3810. doPaste(view, data.getData("text/plain"));
  3811. event.preventDefault();
  3812. }
  3813. else {
  3814. capturePaste(view);
  3815. }
  3816. };
  3817. function captureCopy(view, text) {
  3818. // The extra wrapper is somehow necessary on IE/Edge to prevent the
  3819. // content from being mangled when it is put onto the clipboard
  3820. let parent = view.dom.parentNode;
  3821. if (!parent)
  3822. return;
  3823. let target = parent.appendChild(document.createElement("textarea"));
  3824. target.style.cssText = "position: fixed; left: -10000px; top: 10px";
  3825. target.value = text;
  3826. target.focus();
  3827. target.selectionEnd = text.length;
  3828. target.selectionStart = 0;
  3829. setTimeout(() => {
  3830. target.remove();
  3831. view.focus();
  3832. }, 50);
  3833. }
  3834. function copiedRange(state) {
  3835. let content = [], ranges = [], linewise = false;
  3836. for (let range of state.selection.ranges)
  3837. if (!range.empty) {
  3838. content.push(state.sliceDoc(range.from, range.to));
  3839. ranges.push(range);
  3840. }
  3841. if (!content.length) {
  3842. // Nothing selected, do a line-wise copy
  3843. let upto = -1;
  3844. for (let { from } of state.selection.ranges) {
  3845. let line = state.doc.lineAt(from);
  3846. if (line.number > upto) {
  3847. content.push(line.text);
  3848. ranges.push({ from: line.from, to: Math.min(state.doc.length, line.to + 1) });
  3849. }
  3850. upto = line.number;
  3851. }
  3852. linewise = true;
  3853. }
  3854. return { text: content.join(state.lineBreak), ranges, linewise };
  3855. }
  3856. let lastLinewiseCopy = null;
  3857. handlers.copy = handlers.cut = (view, event) => {
  3858. let { text, ranges, linewise } = copiedRange(view.state);
  3859. if (!text && !linewise)
  3860. return;
  3861. lastLinewiseCopy = linewise ? text : null;
  3862. let data = brokenClipboardAPI ? null : event.clipboardData;
  3863. if (data) {
  3864. event.preventDefault();
  3865. data.clearData();
  3866. data.setData("text/plain", text);
  3867. }
  3868. else {
  3869. captureCopy(view, text);
  3870. }
  3871. if (event.type == "cut" && !view.state.readOnly)
  3872. view.dispatch({
  3873. changes: ranges,
  3874. scrollIntoView: true,
  3875. userEvent: "delete.cut"
  3876. });
  3877. };
  3878. function updateForFocusChange(view) {
  3879. setTimeout(() => {
  3880. if (view.hasFocus != view.inputState.notifiedFocused)
  3881. view.update([]);
  3882. }, 10);
  3883. }
  3884. handlers.focus = updateForFocusChange;
  3885. handlers.blur = view => {
  3886. view.observer.clearSelectionRange();
  3887. updateForFocusChange(view);
  3888. };
  3889. function forceClearComposition(view, rapid) {
  3890. if (view.docView.compositionDeco.size) {
  3891. view.inputState.rapidCompositionStart = rapid;
  3892. try {
  3893. view.update([]);
  3894. }
  3895. finally {
  3896. view.inputState.rapidCompositionStart = false;
  3897. }
  3898. }
  3899. }
  3900. handlers.compositionstart = handlers.compositionupdate = view => {
  3901. if (view.inputState.compositionFirstChange == null)
  3902. view.inputState.compositionFirstChange = true;
  3903. if (view.inputState.composing < 0) {
  3904. // FIXME possibly set a timeout to clear it again on Android
  3905. view.inputState.composing = 0;
  3906. if (view.docView.compositionDeco.size) {
  3907. view.observer.flush();
  3908. forceClearComposition(view, true);
  3909. }
  3910. }
  3911. };
  3912. handlers.compositionend = view => {
  3913. view.inputState.composing = -1;
  3914. view.inputState.compositionEndedAt = Date.now();
  3915. view.inputState.compositionFirstChange = null;
  3916. setTimeout(() => {
  3917. if (view.inputState.composing < 0)
  3918. forceClearComposition(view, false);
  3919. }, 50);
  3920. };
  3921. handlers.contextmenu = view => {
  3922. view.inputState.lastContextMenu = Date.now();
  3923. };
  3924. handlers.beforeinput = (view, event) => {
  3925. var _a;
  3926. // Because Chrome Android doesn't fire useful key events, use
  3927. // beforeinput to detect backspace (and possibly enter and delete,
  3928. // but those usually don't even seem to fire beforeinput events at
  3929. // the moment) and fake a key event for it.
  3930. //
  3931. // (preventDefault on beforeinput, though supported in the spec,
  3932. // seems to do nothing at all on Chrome).
  3933. let pending;
  3934. if (browser.chrome && browser.android && (pending = PendingKeys.find(key => key.inputType == event.inputType))) {
  3935. view.observer.delayAndroidKey(pending.key, pending.keyCode);
  3936. if (pending.key == "Backspace" || pending.key == "Delete") {
  3937. let startViewHeight = ((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0;
  3938. setTimeout(() => {
  3939. var _a;
  3940. // Backspacing near uneditable nodes on Chrome Android sometimes
  3941. // closes the virtual keyboard. This tries to crudely detect
  3942. // that and refocus to get it back.
  3943. if ((((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0) > startViewHeight + 10 && view.hasFocus) {
  3944. view.contentDOM.blur();
  3945. view.focus();
  3946. }
  3947. }, 100);
  3948. }
  3949. }
  3950. };
  3951. const wrappingWhiteSpace = ["pre-wrap", "normal", "pre-line", "break-spaces"];
  3952. class HeightOracle {
  3953. constructor() {
  3954. this.doc = state.Text.empty;
  3955. this.lineWrapping = false;
  3956. this.heightSamples = {};
  3957. this.lineHeight = 14;
  3958. this.charWidth = 7;
  3959. this.lineLength = 30;
  3960. // Used to track, during updateHeight, if any actual heights changed
  3961. this.heightChanged = false;
  3962. }
  3963. heightForGap(from, to) {
  3964. let lines = this.doc.lineAt(to).number - this.doc.lineAt(from).number + 1;
  3965. if (this.lineWrapping)
  3966. lines += Math.ceil(((to - from) - (lines * this.lineLength * 0.5)) / this.lineLength);
  3967. return this.lineHeight * lines;
  3968. }
  3969. heightForLine(length) {
  3970. if (!this.lineWrapping)
  3971. return this.lineHeight;
  3972. let lines = 1 + Math.max(0, Math.ceil((length - this.lineLength) / (this.lineLength - 5)));
  3973. return lines * this.lineHeight;
  3974. }
  3975. setDoc(doc) { this.doc = doc; return this; }
  3976. mustRefreshForWrapping(whiteSpace) {
  3977. return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping;
  3978. }
  3979. mustRefreshForHeights(lineHeights) {
  3980. let newHeight = false;
  3981. for (let i = 0; i < lineHeights.length; i++) {
  3982. let h = lineHeights[i];
  3983. if (h < 0) {
  3984. i++;
  3985. }
  3986. else if (!this.heightSamples[Math.floor(h * 10)]) { // Round to .1 pixels
  3987. newHeight = true;
  3988. this.heightSamples[Math.floor(h * 10)] = true;
  3989. }
  3990. }
  3991. return newHeight;
  3992. }
  3993. refresh(whiteSpace, lineHeight, charWidth, lineLength, knownHeights) {
  3994. let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
  3995. let changed = Math.round(lineHeight) != Math.round(this.lineHeight) || this.lineWrapping != lineWrapping;
  3996. this.lineWrapping = lineWrapping;
  3997. this.lineHeight = lineHeight;
  3998. this.charWidth = charWidth;
  3999. this.lineLength = lineLength;
  4000. if (changed) {
  4001. this.heightSamples = {};
  4002. for (let i = 0; i < knownHeights.length; i++) {
  4003. let h = knownHeights[i];
  4004. if (h < 0)
  4005. i++;
  4006. else
  4007. this.heightSamples[Math.floor(h * 10)] = true;
  4008. }
  4009. }
  4010. return changed;
  4011. }
  4012. }
  4013. // This object is used by `updateHeight` to make DOM measurements
  4014. // arrive at the right nides. The `heights` array is a sequence of
  4015. // block heights, starting from position `from`.
  4016. class MeasuredHeights {
  4017. constructor(from, heights) {
  4018. this.from = from;
  4019. this.heights = heights;
  4020. this.index = 0;
  4021. }
  4022. get more() { return this.index < this.heights.length; }
  4023. }
  4024. /**
  4025. Record used to represent information about a block-level element
  4026. in the editor view.
  4027. */
  4028. class BlockInfo {
  4029. /**
  4030. @internal
  4031. */
  4032. constructor(
  4033. /**
  4034. The start of the element in the document.
  4035. */
  4036. from,
  4037. /**
  4038. The length of the element.
  4039. */
  4040. length,
  4041. /**
  4042. The top position of the element (relative to the top of the
  4043. document).
  4044. */
  4045. top,
  4046. /**
  4047. Its height.
  4048. */
  4049. height,
  4050. /**
  4051. The type of element this is. When querying lines, this may be
  4052. an array of all the blocks that make up the line.
  4053. */
  4054. type) {
  4055. this.from = from;
  4056. this.length = length;
  4057. this.top = top;
  4058. this.height = height;
  4059. this.type = type;
  4060. }
  4061. /**
  4062. The end of the element as a document position.
  4063. */
  4064. get to() { return this.from + this.length; }
  4065. /**
  4066. The bottom position of the element.
  4067. */
  4068. get bottom() { return this.top + this.height; }
  4069. /**
  4070. @internal
  4071. */
  4072. join(other) {
  4073. let detail = (Array.isArray(this.type) ? this.type : [this])
  4074. .concat(Array.isArray(other.type) ? other.type : [other]);
  4075. return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, detail);
  4076. }
  4077. }
  4078. var QueryType;
  4079. (function (QueryType) {
  4080. QueryType[QueryType["ByPos"] = 0] = "ByPos";
  4081. QueryType[QueryType["ByHeight"] = 1] = "ByHeight";
  4082. QueryType[QueryType["ByPosNoHeight"] = 2] = "ByPosNoHeight";
  4083. })(QueryType || (QueryType = {}));
  4084. const Epsilon = 1e-3;
  4085. class HeightMap {
  4086. constructor(length, // The number of characters covered
  4087. height, // Height of this part of the document
  4088. flags = 2 /* Outdated */) {
  4089. this.length = length;
  4090. this.height = height;
  4091. this.flags = flags;
  4092. }
  4093. get outdated() { return (this.flags & 2 /* Outdated */) > 0; }
  4094. set outdated(value) { this.flags = (value ? 2 /* Outdated */ : 0) | (this.flags & ~2 /* Outdated */); }
  4095. setHeight(oracle, height) {
  4096. if (this.height != height) {
  4097. if (Math.abs(this.height - height) > Epsilon)
  4098. oracle.heightChanged = true;
  4099. this.height = height;
  4100. }
  4101. }
  4102. // Base case is to replace a leaf node, which simply builds a tree
  4103. // from the new nodes and returns that (HeightMapBranch and
  4104. // HeightMapGap override this to actually use from/to)
  4105. replace(_from, _to, nodes) {
  4106. return HeightMap.of(nodes);
  4107. }
  4108. // Again, these are base cases, and are overridden for branch and gap nodes.
  4109. decomposeLeft(_to, result) { result.push(this); }
  4110. decomposeRight(_from, result) { result.push(this); }
  4111. applyChanges(decorations, oldDoc, oracle, changes) {
  4112. let me = this;
  4113. for (let i = changes.length - 1; i >= 0; i--) {
  4114. let { fromA, toA, fromB, toB } = changes[i];
  4115. let start = me.lineAt(fromA, QueryType.ByPosNoHeight, oldDoc, 0, 0);
  4116. let end = start.to >= toA ? start : me.lineAt(toA, QueryType.ByPosNoHeight, oldDoc, 0, 0);
  4117. toB += end.to - toA;
  4118. toA = end.to;
  4119. while (i > 0 && start.from <= changes[i - 1].toA) {
  4120. fromA = changes[i - 1].fromA;
  4121. fromB = changes[i - 1].fromB;
  4122. i--;
  4123. if (fromA < start.from)
  4124. start = me.lineAt(fromA, QueryType.ByPosNoHeight, oldDoc, 0, 0);
  4125. }
  4126. fromB += start.from - fromA;
  4127. fromA = start.from;
  4128. let nodes = NodeBuilder.build(oracle, decorations, fromB, toB);
  4129. me = me.replace(fromA, toA, nodes);
  4130. }
  4131. return me.updateHeight(oracle, 0);
  4132. }
  4133. static empty() { return new HeightMapText(0, 0); }
  4134. // nodes uses null values to indicate the position of line breaks.
  4135. // There are never line breaks at the start or end of the array, or
  4136. // two line breaks next to each other, and the array isn't allowed
  4137. // to be empty (same restrictions as return value from the builder).
  4138. static of(nodes) {
  4139. if (nodes.length == 1)
  4140. return nodes[0];
  4141. let i = 0, j = nodes.length, before = 0, after = 0;
  4142. for (;;) {
  4143. if (i == j) {
  4144. if (before > after * 2) {
  4145. let split = nodes[i - 1];
  4146. if (split.break)
  4147. nodes.splice(--i, 1, split.left, null, split.right);
  4148. else
  4149. nodes.splice(--i, 1, split.left, split.right);
  4150. j += 1 + split.break;
  4151. before -= split.size;
  4152. }
  4153. else if (after > before * 2) {
  4154. let split = nodes[j];
  4155. if (split.break)
  4156. nodes.splice(j, 1, split.left, null, split.right);
  4157. else
  4158. nodes.splice(j, 1, split.left, split.right);
  4159. j += 2 + split.break;
  4160. after -= split.size;
  4161. }
  4162. else {
  4163. break;
  4164. }
  4165. }
  4166. else if (before < after) {
  4167. let next = nodes[i++];
  4168. if (next)
  4169. before += next.size;
  4170. }
  4171. else {
  4172. let next = nodes[--j];
  4173. if (next)
  4174. after += next.size;
  4175. }
  4176. }
  4177. let brk = 0;
  4178. if (nodes[i - 1] == null) {
  4179. brk = 1;
  4180. i--;
  4181. }
  4182. else if (nodes[i] == null) {
  4183. brk = 1;
  4184. j++;
  4185. }
  4186. return new HeightMapBranch(HeightMap.of(nodes.slice(0, i)), brk, HeightMap.of(nodes.slice(j)));
  4187. }
  4188. }
  4189. HeightMap.prototype.size = 1;
  4190. class HeightMapBlock extends HeightMap {
  4191. constructor(length, height, type) {
  4192. super(length, height);
  4193. this.type = type;
  4194. }
  4195. blockAt(_height, _doc, top, offset) {
  4196. return new BlockInfo(offset, this.length, top, this.height, this.type);
  4197. }
  4198. lineAt(_value, _type, doc, top, offset) {
  4199. return this.blockAt(0, doc, top, offset);
  4200. }
  4201. forEachLine(from, to, doc, top, offset, f) {
  4202. if (from <= offset + this.length && to >= offset)
  4203. f(this.blockAt(0, doc, top, offset));
  4204. }
  4205. updateHeight(oracle, offset = 0, _force = false, measured) {
  4206. if (measured && measured.from <= offset && measured.more)
  4207. this.setHeight(oracle, measured.heights[measured.index++]);
  4208. this.outdated = false;
  4209. return this;
  4210. }
  4211. toString() { return `block(${this.length})`; }
  4212. }
  4213. class HeightMapText extends HeightMapBlock {
  4214. constructor(length, height) {
  4215. super(length, height, exports.BlockType.Text);
  4216. this.collapsed = 0; // Amount of collapsed content in the line
  4217. this.widgetHeight = 0; // Maximum inline widget height
  4218. }
  4219. replace(_from, _to, nodes) {
  4220. let node = nodes[0];
  4221. if (nodes.length == 1 && (node instanceof HeightMapText || node instanceof HeightMapGap && (node.flags & 4 /* SingleLine */)) &&
  4222. Math.abs(this.length - node.length) < 10) {
  4223. if (node instanceof HeightMapGap)
  4224. node = new HeightMapText(node.length, this.height);
  4225. else
  4226. node.height = this.height;
  4227. if (!this.outdated)
  4228. node.outdated = false;
  4229. return node;
  4230. }
  4231. else {
  4232. return HeightMap.of(nodes);
  4233. }
  4234. }
  4235. updateHeight(oracle, offset = 0, force = false, measured) {
  4236. if (measured && measured.from <= offset && measured.more)
  4237. this.setHeight(oracle, measured.heights[measured.index++]);
  4238. else if (force || this.outdated)
  4239. this.setHeight(oracle, Math.max(this.widgetHeight, oracle.heightForLine(this.length - this.collapsed)));
  4240. this.outdated = false;
  4241. return this;
  4242. }
  4243. toString() {
  4244. return `line(${this.length}${this.collapsed ? -this.collapsed : ""}${this.widgetHeight ? ":" + this.widgetHeight : ""})`;
  4245. }
  4246. }
  4247. class HeightMapGap extends HeightMap {
  4248. constructor(length) { super(length, 0); }
  4249. lines(doc, offset) {
  4250. let firstLine = doc.lineAt(offset).number, lastLine = doc.lineAt(offset + this.length).number;
  4251. return { firstLine, lastLine, lineHeight: this.height / (lastLine - firstLine + 1) };
  4252. }
  4253. blockAt(height, doc, top, offset) {
  4254. let { firstLine, lastLine, lineHeight } = this.lines(doc, offset);
  4255. let line = Math.max(0, Math.min(lastLine - firstLine, Math.floor((height - top) / lineHeight)));
  4256. let { from, length } = doc.line(firstLine + line);
  4257. return new BlockInfo(from, length, top + lineHeight * line, lineHeight, exports.BlockType.Text);
  4258. }
  4259. lineAt(value, type, doc, top, offset) {
  4260. if (type == QueryType.ByHeight)
  4261. return this.blockAt(value, doc, top, offset);
  4262. if (type == QueryType.ByPosNoHeight) {
  4263. let { from, to } = doc.lineAt(value);
  4264. return new BlockInfo(from, to - from, 0, 0, exports.BlockType.Text);
  4265. }
  4266. let { firstLine, lineHeight } = this.lines(doc, offset);
  4267. let { from, length, number } = doc.lineAt(value);
  4268. return new BlockInfo(from, length, top + lineHeight * (number - firstLine), lineHeight, exports.BlockType.Text);
  4269. }
  4270. forEachLine(from, to, doc, top, offset, f) {
  4271. let { firstLine, lineHeight } = this.lines(doc, offset);
  4272. for (let pos = Math.max(from, offset), end = Math.min(offset + this.length, to); pos <= end;) {
  4273. let line = doc.lineAt(pos);
  4274. if (pos == from)
  4275. top += lineHeight * (line.number - firstLine);
  4276. f(new BlockInfo(line.from, line.length, top, lineHeight, exports.BlockType.Text));
  4277. top += lineHeight;
  4278. pos = line.to + 1;
  4279. }
  4280. }
  4281. replace(from, to, nodes) {
  4282. let after = this.length - to;
  4283. if (after > 0) {
  4284. let last = nodes[nodes.length - 1];
  4285. if (last instanceof HeightMapGap)
  4286. nodes[nodes.length - 1] = new HeightMapGap(last.length + after);
  4287. else
  4288. nodes.push(null, new HeightMapGap(after - 1));
  4289. }
  4290. if (from > 0) {
  4291. let first = nodes[0];
  4292. if (first instanceof HeightMapGap)
  4293. nodes[0] = new HeightMapGap(from + first.length);
  4294. else
  4295. nodes.unshift(new HeightMapGap(from - 1), null);
  4296. }
  4297. return HeightMap.of(nodes);
  4298. }
  4299. decomposeLeft(to, result) {
  4300. result.push(new HeightMapGap(to - 1), null);
  4301. }
  4302. decomposeRight(from, result) {
  4303. result.push(null, new HeightMapGap(this.length - from - 1));
  4304. }
  4305. updateHeight(oracle, offset = 0, force = false, measured) {
  4306. let end = offset + this.length;
  4307. if (measured && measured.from <= offset + this.length && measured.more) {
  4308. // Fill in part of this gap with measured lines. We know there
  4309. // can't be widgets or collapsed ranges in those lines, because
  4310. // they would already have been added to the heightmap (gaps
  4311. // only contain plain text).
  4312. let nodes = [], pos = Math.max(offset, measured.from), singleHeight = -1;
  4313. let wasChanged = oracle.heightChanged;
  4314. if (measured.from > offset)
  4315. nodes.push(new HeightMapGap(measured.from - offset - 1).updateHeight(oracle, offset));
  4316. while (pos <= end && measured.more) {
  4317. let len = oracle.doc.lineAt(pos).length;
  4318. if (nodes.length)
  4319. nodes.push(null);
  4320. let height = measured.heights[measured.index++];
  4321. if (singleHeight == -1)
  4322. singleHeight = height;
  4323. else if (Math.abs(height - singleHeight) >= Epsilon)
  4324. singleHeight = -2;
  4325. let line = new HeightMapText(len, height);
  4326. line.outdated = false;
  4327. nodes.push(line);
  4328. pos += len + 1;
  4329. }
  4330. if (pos <= end)
  4331. nodes.push(null, new HeightMapGap(end - pos).updateHeight(oracle, pos));
  4332. let result = HeightMap.of(nodes);
  4333. oracle.heightChanged = wasChanged || singleHeight < 0 || Math.abs(result.height - this.height) >= Epsilon ||
  4334. Math.abs(singleHeight - this.lines(oracle.doc, offset).lineHeight) >= Epsilon;
  4335. return result;
  4336. }
  4337. else if (force || this.outdated) {
  4338. this.setHeight(oracle, oracle.heightForGap(offset, offset + this.length));
  4339. this.outdated = false;
  4340. }
  4341. return this;
  4342. }
  4343. toString() { return `gap(${this.length})`; }
  4344. }
  4345. class HeightMapBranch extends HeightMap {
  4346. constructor(left, brk, right) {
  4347. super(left.length + brk + right.length, left.height + right.height, brk | (left.outdated || right.outdated ? 2 /* Outdated */ : 0));
  4348. this.left = left;
  4349. this.right = right;
  4350. this.size = left.size + right.size;
  4351. }
  4352. get break() { return this.flags & 1 /* Break */; }
  4353. blockAt(height, doc, top, offset) {
  4354. let mid = top + this.left.height;
  4355. return height < mid ? this.left.blockAt(height, doc, top, offset)
  4356. : this.right.blockAt(height, doc, mid, offset + this.left.length + this.break);
  4357. }
  4358. lineAt(value, type, doc, top, offset) {
  4359. let rightTop = top + this.left.height, rightOffset = offset + this.left.length + this.break;
  4360. let left = type == QueryType.ByHeight ? value < rightTop : value < rightOffset;
  4361. let base = left ? this.left.lineAt(value, type, doc, top, offset)
  4362. : this.right.lineAt(value, type, doc, rightTop, rightOffset);
  4363. if (this.break || (left ? base.to < rightOffset : base.from > rightOffset))
  4364. return base;
  4365. let subQuery = type == QueryType.ByPosNoHeight ? QueryType.ByPosNoHeight : QueryType.ByPos;
  4366. if (left)
  4367. return base.join(this.right.lineAt(rightOffset, subQuery, doc, rightTop, rightOffset));
  4368. else
  4369. return this.left.lineAt(rightOffset, subQuery, doc, top, offset).join(base);
  4370. }
  4371. forEachLine(from, to, doc, top, offset, f) {
  4372. let rightTop = top + this.left.height, rightOffset = offset + this.left.length + this.break;
  4373. if (this.break) {
  4374. if (from < rightOffset)
  4375. this.left.forEachLine(from, to, doc, top, offset, f);
  4376. if (to >= rightOffset)
  4377. this.right.forEachLine(from, to, doc, rightTop, rightOffset, f);
  4378. }
  4379. else {
  4380. let mid = this.lineAt(rightOffset, QueryType.ByPos, doc, top, offset);
  4381. if (from < mid.from)
  4382. this.left.forEachLine(from, mid.from - 1, doc, top, offset, f);
  4383. if (mid.to >= from && mid.from <= to)
  4384. f(mid);
  4385. if (to > mid.to)
  4386. this.right.forEachLine(mid.to + 1, to, doc, rightTop, rightOffset, f);
  4387. }
  4388. }
  4389. replace(from, to, nodes) {
  4390. let rightStart = this.left.length + this.break;
  4391. if (to < rightStart)
  4392. return this.balanced(this.left.replace(from, to, nodes), this.right);
  4393. if (from > this.left.length)
  4394. return this.balanced(this.left, this.right.replace(from - rightStart, to - rightStart, nodes));
  4395. let result = [];
  4396. if (from > 0)
  4397. this.decomposeLeft(from, result);
  4398. let left = result.length;
  4399. for (let node of nodes)
  4400. result.push(node);
  4401. if (from > 0)
  4402. mergeGaps(result, left - 1);
  4403. if (to < this.length) {
  4404. let right = result.length;
  4405. this.decomposeRight(to, result);
  4406. mergeGaps(result, right);
  4407. }
  4408. return HeightMap.of(result);
  4409. }
  4410. decomposeLeft(to, result) {
  4411. let left = this.left.length;
  4412. if (to <= left)
  4413. return this.left.decomposeLeft(to, result);
  4414. result.push(this.left);
  4415. if (this.break) {
  4416. left++;
  4417. if (to >= left)
  4418. result.push(null);
  4419. }
  4420. if (to > left)
  4421. this.right.decomposeLeft(to - left, result);
  4422. }
  4423. decomposeRight(from, result) {
  4424. let left = this.left.length, right = left + this.break;
  4425. if (from >= right)
  4426. return this.right.decomposeRight(from - right, result);
  4427. if (from < left)
  4428. this.left.decomposeRight(from, result);
  4429. if (this.break && from < right)
  4430. result.push(null);
  4431. result.push(this.right);
  4432. }
  4433. balanced(left, right) {
  4434. if (left.size > 2 * right.size || right.size > 2 * left.size)
  4435. return HeightMap.of(this.break ? [left, null, right] : [left, right]);
  4436. this.left = left;
  4437. this.right = right;
  4438. this.height = left.height + right.height;
  4439. this.outdated = left.outdated || right.outdated;
  4440. this.size = left.size + right.size;
  4441. this.length = left.length + this.break + right.length;
  4442. return this;
  4443. }
  4444. updateHeight(oracle, offset = 0, force = false, measured) {
  4445. let { left, right } = this, rightStart = offset + left.length + this.break, rebalance = null;
  4446. if (measured && measured.from <= offset + left.length && measured.more)
  4447. rebalance = left = left.updateHeight(oracle, offset, force, measured);
  4448. else
  4449. left.updateHeight(oracle, offset, force);
  4450. if (measured && measured.from <= rightStart + right.length && measured.more)
  4451. rebalance = right = right.updateHeight(oracle, rightStart, force, measured);
  4452. else
  4453. right.updateHeight(oracle, rightStart, force);
  4454. if (rebalance)
  4455. return this.balanced(left, right);
  4456. this.height = this.left.height + this.right.height;
  4457. this.outdated = false;
  4458. return this;
  4459. }
  4460. toString() { return this.left + (this.break ? " " : "-") + this.right; }
  4461. }
  4462. function mergeGaps(nodes, around) {
  4463. let before, after;
  4464. if (nodes[around] == null &&
  4465. (before = nodes[around - 1]) instanceof HeightMapGap &&
  4466. (after = nodes[around + 1]) instanceof HeightMapGap)
  4467. nodes.splice(around - 1, 3, new HeightMapGap(before.length + 1 + after.length));
  4468. }
  4469. const relevantWidgetHeight = 5;
  4470. class NodeBuilder {
  4471. constructor(pos, oracle) {
  4472. this.pos = pos;
  4473. this.oracle = oracle;
  4474. this.nodes = [];
  4475. this.lineStart = -1;
  4476. this.lineEnd = -1;
  4477. this.covering = null;
  4478. this.writtenTo = pos;
  4479. }
  4480. get isCovered() {
  4481. return this.covering && this.nodes[this.nodes.length - 1] == this.covering;
  4482. }
  4483. span(_from, to) {
  4484. if (this.lineStart > -1) {
  4485. let end = Math.min(to, this.lineEnd), last = this.nodes[this.nodes.length - 1];
  4486. if (last instanceof HeightMapText)
  4487. last.length += end - this.pos;
  4488. else if (end > this.pos || !this.isCovered)
  4489. this.nodes.push(new HeightMapText(end - this.pos, -1));
  4490. this.writtenTo = end;
  4491. if (to > end) {
  4492. this.nodes.push(null);
  4493. this.writtenTo++;
  4494. this.lineStart = -1;
  4495. }
  4496. }
  4497. this.pos = to;
  4498. }
  4499. point(from, to, deco) {
  4500. if (from < to || deco.heightRelevant) {
  4501. let height = deco.widget ? deco.widget.estimatedHeight : 0;
  4502. if (height < 0)
  4503. height = this.oracle.lineHeight;
  4504. let len = to - from;
  4505. if (deco.block) {
  4506. this.addBlock(new HeightMapBlock(len, height, deco.type));
  4507. }
  4508. else if (len || height >= relevantWidgetHeight) {
  4509. this.addLineDeco(height, len);
  4510. }
  4511. }
  4512. else if (to > from) {
  4513. this.span(from, to);
  4514. }
  4515. if (this.lineEnd > -1 && this.lineEnd < this.pos)
  4516. this.lineEnd = this.oracle.doc.lineAt(this.pos).to;
  4517. }
  4518. enterLine() {
  4519. if (this.lineStart > -1)
  4520. return;
  4521. let { from, to } = this.oracle.doc.lineAt(this.pos);
  4522. this.lineStart = from;
  4523. this.lineEnd = to;
  4524. if (this.writtenTo < from) {
  4525. if (this.writtenTo < from - 1 || this.nodes[this.nodes.length - 1] == null)
  4526. this.nodes.push(this.blankContent(this.writtenTo, from - 1));
  4527. this.nodes.push(null);
  4528. }
  4529. if (this.pos > from)
  4530. this.nodes.push(new HeightMapText(this.pos - from, -1));
  4531. this.writtenTo = this.pos;
  4532. }
  4533. blankContent(from, to) {
  4534. let gap = new HeightMapGap(to - from);
  4535. if (this.oracle.doc.lineAt(from).to == to)
  4536. gap.flags |= 4 /* SingleLine */;
  4537. return gap;
  4538. }
  4539. ensureLine() {
  4540. this.enterLine();
  4541. let last = this.nodes.length ? this.nodes[this.nodes.length - 1] : null;
  4542. if (last instanceof HeightMapText)
  4543. return last;
  4544. let line = new HeightMapText(0, -1);
  4545. this.nodes.push(line);
  4546. return line;
  4547. }
  4548. addBlock(block) {
  4549. this.enterLine();
  4550. if (block.type == exports.BlockType.WidgetAfter && !this.isCovered)
  4551. this.ensureLine();
  4552. this.nodes.push(block);
  4553. this.writtenTo = this.pos = this.pos + block.length;
  4554. if (block.type != exports.BlockType.WidgetBefore)
  4555. this.covering = block;
  4556. }
  4557. addLineDeco(height, length) {
  4558. let line = this.ensureLine();
  4559. line.length += length;
  4560. line.collapsed += length;
  4561. line.widgetHeight = Math.max(line.widgetHeight, height);
  4562. this.writtenTo = this.pos = this.pos + length;
  4563. }
  4564. finish(from) {
  4565. let last = this.nodes.length == 0 ? null : this.nodes[this.nodes.length - 1];
  4566. if (this.lineStart > -1 && !(last instanceof HeightMapText) && !this.isCovered)
  4567. this.nodes.push(new HeightMapText(0, -1));
  4568. else if (this.writtenTo < this.pos || last == null)
  4569. this.nodes.push(this.blankContent(this.writtenTo, this.pos));
  4570. let pos = from;
  4571. for (let node of this.nodes) {
  4572. if (node instanceof HeightMapText)
  4573. node.updateHeight(this.oracle, pos);
  4574. pos += node ? node.length : 1;
  4575. }
  4576. return this.nodes;
  4577. }
  4578. // Always called with a region that on both sides either stretches
  4579. // to a line break or the end of the document.
  4580. // The returned array uses null to indicate line breaks, but never
  4581. // starts or ends in a line break, or has multiple line breaks next
  4582. // to each other.
  4583. static build(oracle, decorations, from, to) {
  4584. let builder = new NodeBuilder(from, oracle);
  4585. state.RangeSet.spans(decorations, from, to, builder, 0);
  4586. return builder.finish(from);
  4587. }
  4588. }
  4589. function heightRelevantDecoChanges(a, b, diff) {
  4590. let comp = new DecorationComparator;
  4591. state.RangeSet.compare(a, b, diff, comp, 0);
  4592. return comp.changes;
  4593. }
  4594. class DecorationComparator {
  4595. constructor() {
  4596. this.changes = [];
  4597. }
  4598. compareRange() { }
  4599. comparePoint(from, to, a, b) {
  4600. if (from < to || a && a.heightRelevant || b && b.heightRelevant)
  4601. addRange(from, to, this.changes, 5);
  4602. }
  4603. }
  4604. function visiblePixelRange(dom, paddingTop) {
  4605. let rect = dom.getBoundingClientRect();
  4606. let left = Math.max(0, rect.left), right = Math.min(innerWidth, rect.right);
  4607. let top = Math.max(0, rect.top), bottom = Math.min(innerHeight, rect.bottom);
  4608. let body = dom.ownerDocument.body;
  4609. for (let parent = dom.parentNode; parent && parent != body;) {
  4610. if (parent.nodeType == 1) {
  4611. let elt = parent;
  4612. let style = window.getComputedStyle(elt);
  4613. if ((elt.scrollHeight > elt.clientHeight || elt.scrollWidth > elt.clientWidth) &&
  4614. style.overflow != "visible") {
  4615. let parentRect = elt.getBoundingClientRect();
  4616. left = Math.max(left, parentRect.left);
  4617. right = Math.min(right, parentRect.right);
  4618. top = Math.max(top, parentRect.top);
  4619. bottom = Math.min(bottom, parentRect.bottom);
  4620. }
  4621. parent = style.position == "absolute" || style.position == "fixed" ? elt.offsetParent : elt.parentNode;
  4622. }
  4623. else if (parent.nodeType == 11) { // Shadow root
  4624. parent = parent.host;
  4625. }
  4626. else {
  4627. break;
  4628. }
  4629. }
  4630. return { left: left - rect.left, right: Math.max(left, right) - rect.left,
  4631. top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) };
  4632. }
  4633. function fullPixelRange(dom, paddingTop) {
  4634. let rect = dom.getBoundingClientRect();
  4635. return { left: 0, right: rect.right - rect.left,
  4636. top: paddingTop, bottom: rect.bottom - (rect.top + paddingTop) };
  4637. }
  4638. // Line gaps are placeholder widgets used to hide pieces of overlong
  4639. // lines within the viewport, as a kludge to keep the editor
  4640. // responsive when a ridiculously long line is loaded into it.
  4641. class LineGap {
  4642. constructor(from, to, size) {
  4643. this.from = from;
  4644. this.to = to;
  4645. this.size = size;
  4646. }
  4647. static same(a, b) {
  4648. if (a.length != b.length)
  4649. return false;
  4650. for (let i = 0; i < a.length; i++) {
  4651. let gA = a[i], gB = b[i];
  4652. if (gA.from != gB.from || gA.to != gB.to || gA.size != gB.size)
  4653. return false;
  4654. }
  4655. return true;
  4656. }
  4657. draw(wrapping) {
  4658. return Decoration.replace({ widget: new LineGapWidget(this.size, wrapping) }).range(this.from, this.to);
  4659. }
  4660. }
  4661. class LineGapWidget extends WidgetType {
  4662. constructor(size, vertical) {
  4663. super();
  4664. this.size = size;
  4665. this.vertical = vertical;
  4666. }
  4667. eq(other) { return other.size == this.size && other.vertical == this.vertical; }
  4668. toDOM() {
  4669. let elt = document.createElement("div");
  4670. if (this.vertical) {
  4671. elt.style.height = this.size + "px";
  4672. }
  4673. else {
  4674. elt.style.width = this.size + "px";
  4675. elt.style.height = "2px";
  4676. elt.style.display = "inline-block";
  4677. }
  4678. return elt;
  4679. }
  4680. get estimatedHeight() { return this.vertical ? this.size : -1; }
  4681. }
  4682. class ViewState {
  4683. constructor(state$1) {
  4684. this.state = state$1;
  4685. // These are contentDOM-local coordinates
  4686. this.pixelViewport = { left: 0, right: window.innerWidth, top: 0, bottom: 0 };
  4687. this.inView = true;
  4688. this.paddingTop = 0;
  4689. this.paddingBottom = 0;
  4690. this.contentDOMWidth = 0;
  4691. this.contentDOMHeight = 0;
  4692. this.editorHeight = 0;
  4693. this.editorWidth = 0;
  4694. this.heightOracle = new HeightOracle;
  4695. // See VP.MaxDOMHeight
  4696. this.scaler = IdScaler;
  4697. this.scrollTarget = null;
  4698. // Briefly set to true when printing, to disable viewport limiting
  4699. this.printing = false;
  4700. // Flag set when editor content was redrawn, so that the next
  4701. // measure stage knows it must read DOM layout
  4702. this.mustMeasureContent = true;
  4703. this.defaultTextDirection = exports.Direction.RTL;
  4704. this.visibleRanges = [];
  4705. // Cursor 'assoc' is only significant when the cursor is on a line
  4706. // wrap point, where it must stick to the character that it is
  4707. // associated with. Since browsers don't provide a reasonable
  4708. // interface to set or query this, when a selection is set that
  4709. // might cause this to be significant, this flag is set. The next
  4710. // measure phase will check whether the cursor is on a line-wrapping
  4711. // boundary and, if so, reset it to make sure it is positioned in
  4712. // the right place.
  4713. this.mustEnforceCursorAssoc = false;
  4714. this.stateDeco = state$1.facet(decorations).filter(d => typeof d != "function");
  4715. this.heightMap = HeightMap.empty().applyChanges(this.stateDeco, state.Text.empty, this.heightOracle.setDoc(state$1.doc), [new ChangedRange(0, 0, 0, state$1.doc.length)]);
  4716. this.viewport = this.getViewport(0, null);
  4717. this.updateViewportLines();
  4718. this.updateForViewport();
  4719. this.lineGaps = this.ensureLineGaps([]);
  4720. this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(false)));
  4721. this.computeVisibleRanges();
  4722. }
  4723. updateForViewport() {
  4724. let viewports = [this.viewport], { main } = this.state.selection;
  4725. for (let i = 0; i <= 1; i++) {
  4726. let pos = i ? main.head : main.anchor;
  4727. if (!viewports.some(({ from, to }) => pos >= from && pos <= to)) {
  4728. let { from, to } = this.lineBlockAt(pos);
  4729. viewports.push(new Viewport(from, to));
  4730. }
  4731. }
  4732. this.viewports = viewports.sort((a, b) => a.from - b.from);
  4733. this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
  4734. new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
  4735. }
  4736. updateViewportLines() {
  4737. this.viewportLines = [];
  4738. this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.state.doc, 0, 0, block => {
  4739. this.viewportLines.push(this.scaler.scale == 1 ? block : scaleBlock(block, this.scaler));
  4740. });
  4741. }
  4742. update(update, scrollTarget = null) {
  4743. this.state = update.state;
  4744. let prevDeco = this.stateDeco;
  4745. this.stateDeco = this.state.facet(decorations).filter(d => typeof d != "function");
  4746. let contentChanges = update.changedRanges;
  4747. let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : state.ChangeSet.empty(this.state.doc.length)));
  4748. let prevHeight = this.heightMap.height;
  4749. this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
  4750. if (this.heightMap.height != prevHeight)
  4751. update.flags |= 2 /* Height */;
  4752. let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
  4753. if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
  4754. !this.viewportIsAppropriate(viewport))
  4755. viewport = this.getViewport(0, scrollTarget);
  4756. let updateLines = !update.changes.empty || (update.flags & 2 /* Height */) ||
  4757. viewport.from != this.viewport.from || viewport.to != this.viewport.to;
  4758. this.viewport = viewport;
  4759. this.updateForViewport();
  4760. if (updateLines)
  4761. this.updateViewportLines();
  4762. if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
  4763. this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
  4764. update.flags |= this.computeVisibleRanges();
  4765. if (scrollTarget)
  4766. this.scrollTarget = scrollTarget;
  4767. if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping &&
  4768. update.state.selection.main.empty && update.state.selection.main.assoc)
  4769. this.mustEnforceCursorAssoc = true;
  4770. }
  4771. measure(view) {
  4772. let dom = view.contentDOM, style = window.getComputedStyle(dom);
  4773. let oracle = this.heightOracle;
  4774. let whiteSpace = style.whiteSpace;
  4775. this.defaultTextDirection = style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
  4776. let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace);
  4777. let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
  4778. this.contentDOMHeight = dom.clientHeight;
  4779. this.mustMeasureContent = false;
  4780. let result = 0, bias = 0;
  4781. // Vertical padding
  4782. let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
  4783. if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
  4784. this.paddingTop = paddingTop;
  4785. this.paddingBottom = paddingBottom;
  4786. result |= 8 /* Geometry */ | 2 /* Height */;
  4787. }
  4788. if (this.editorWidth != view.scrollDOM.clientWidth) {
  4789. if (oracle.lineWrapping)
  4790. measureContent = true;
  4791. this.editorWidth = view.scrollDOM.clientWidth;
  4792. result |= 8 /* Geometry */;
  4793. }
  4794. // Pixel viewport
  4795. let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
  4796. let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
  4797. this.pixelViewport = pixelViewport;
  4798. let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
  4799. if (inView != this.inView) {
  4800. this.inView = inView;
  4801. if (inView)
  4802. measureContent = true;
  4803. }
  4804. if (!this.inView)
  4805. return 0;
  4806. let contentWidth = dom.clientWidth;
  4807. if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight) {
  4808. this.contentDOMWidth = contentWidth;
  4809. this.editorHeight = view.scrollDOM.clientHeight;
  4810. result |= 8 /* Geometry */;
  4811. }
  4812. if (measureContent) {
  4813. let lineHeights = view.docView.measureVisibleLineHeights(this.viewport);
  4814. if (oracle.mustRefreshForHeights(lineHeights))
  4815. refresh = true;
  4816. if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) {
  4817. let { lineHeight, charWidth } = view.docView.measureTextSize();
  4818. refresh = oracle.refresh(whiteSpace, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
  4819. if (refresh) {
  4820. view.docView.minWidth = 0;
  4821. result |= 8 /* Geometry */;
  4822. }
  4823. }
  4824. if (dTop > 0 && dBottom > 0)
  4825. bias = Math.max(dTop, dBottom);
  4826. else if (dTop < 0 && dBottom < 0)
  4827. bias = Math.min(dTop, dBottom);
  4828. oracle.heightChanged = false;
  4829. for (let vp of this.viewports) {
  4830. let heights = vp.from == this.viewport.from ? lineHeights : view.docView.measureVisibleLineHeights(vp);
  4831. this.heightMap = this.heightMap.updateHeight(oracle, 0, refresh, new MeasuredHeights(vp.from, heights));
  4832. }
  4833. if (oracle.heightChanged)
  4834. result |= 2 /* Height */;
  4835. }
  4836. let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) ||
  4837. this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to);
  4838. if (viewportChange)
  4839. this.viewport = this.getViewport(bias, this.scrollTarget);
  4840. this.updateForViewport();
  4841. if ((result & 2 /* Height */) || viewportChange)
  4842. this.updateViewportLines();
  4843. if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
  4844. this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
  4845. result |= this.computeVisibleRanges();
  4846. if (this.mustEnforceCursorAssoc) {
  4847. this.mustEnforceCursorAssoc = false;
  4848. // This is done in the read stage, because moving the selection
  4849. // to a line end is going to trigger a layout anyway, so it
  4850. // can't be a pure write. It should be rare that it does any
  4851. // writing.
  4852. view.docView.enforceCursorAssoc();
  4853. }
  4854. return result;
  4855. }
  4856. get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top); }
  4857. get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom); }
  4858. getViewport(bias, scrollTarget) {
  4859. // This will divide VP.Margin between the top and the
  4860. // bottom, depending on the bias (the change in viewport position
  4861. // since the last update). It'll hold a number between 0 and 1
  4862. let marginTop = 0.5 - Math.max(-0.5, Math.min(0.5, bias / 1000 /* Margin */ / 2));
  4863. let map = this.heightMap, doc = this.state.doc, { visibleTop, visibleBottom } = this;
  4864. let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).to);
  4865. // If scrollTarget is given, make sure the viewport includes that position
  4866. if (scrollTarget) {
  4867. let { head } = scrollTarget.range;
  4868. if (head < viewport.from || head > viewport.to) {
  4869. let viewHeight = Math.min(this.editorHeight, this.pixelViewport.bottom - this.pixelViewport.top);
  4870. let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
  4871. if (scrollTarget.y == "center")
  4872. topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
  4873. else if (scrollTarget.y == "start" || scrollTarget.y == "nearest" && head < viewport.from)
  4874. topPos = block.top;
  4875. else
  4876. topPos = block.bottom - viewHeight;
  4877. viewport = new Viewport(map.lineAt(topPos - 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(topPos + viewHeight + 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
  4878. }
  4879. }
  4880. return viewport;
  4881. }
  4882. mapViewport(viewport, changes) {
  4883. let from = changes.mapPos(viewport.from, -1), to = changes.mapPos(viewport.to, 1);
  4884. return new Viewport(this.heightMap.lineAt(from, QueryType.ByPos, this.state.doc, 0, 0).from, this.heightMap.lineAt(to, QueryType.ByPos, this.state.doc, 0, 0).to);
  4885. }
  4886. // Checks if a given viewport covers the visible part of the
  4887. // document and not too much beyond that.
  4888. viewportIsAppropriate({ from, to }, bias = 0) {
  4889. if (!this.inView)
  4890. return true;
  4891. let { top } = this.heightMap.lineAt(from, QueryType.ByPos, this.state.doc, 0, 0);
  4892. let { bottom } = this.heightMap.lineAt(to, QueryType.ByPos, this.state.doc, 0, 0);
  4893. let { visibleTop, visibleBottom } = this;
  4894. return (from == 0 || top <= visibleTop - Math.max(10 /* MinCoverMargin */, Math.min(-bias, 250 /* MaxCoverMargin */))) &&
  4895. (to == this.state.doc.length ||
  4896. bottom >= visibleBottom + Math.max(10 /* MinCoverMargin */, Math.min(bias, 250 /* MaxCoverMargin */))) &&
  4897. (top > visibleTop - 2 * 1000 /* Margin */ && bottom < visibleBottom + 2 * 1000 /* Margin */);
  4898. }
  4899. mapLineGaps(gaps, changes) {
  4900. if (!gaps.length || changes.empty)
  4901. return gaps;
  4902. let mapped = [];
  4903. for (let gap of gaps)
  4904. if (!changes.touchesRange(gap.from, gap.to))
  4905. mapped.push(new LineGap(changes.mapPos(gap.from), changes.mapPos(gap.to), gap.size));
  4906. return mapped;
  4907. }
  4908. // Computes positions in the viewport where the start or end of a
  4909. // line should be hidden, trying to reuse existing line gaps when
  4910. // appropriate to avoid unneccesary redraws.
  4911. // Uses crude character-counting for the positioning and sizing,
  4912. // since actual DOM coordinates aren't always available and
  4913. // predictable. Relies on generous margins (see LG.Margin) to hide
  4914. // the artifacts this might produce from the user.
  4915. ensureLineGaps(current) {
  4916. let gaps = [];
  4917. // This won't work at all in predominantly right-to-left text.
  4918. if (this.defaultTextDirection != exports.Direction.LTR)
  4919. return gaps;
  4920. for (let line of this.viewportLines) {
  4921. if (line.length < 4000 /* DoubleMargin */)
  4922. continue;
  4923. let structure = lineStructure(line.from, line.to, this.stateDeco);
  4924. if (structure.total < 4000 /* DoubleMargin */)
  4925. continue;
  4926. let viewFrom, viewTo;
  4927. if (this.heightOracle.lineWrapping) {
  4928. let marginHeight = (2000 /* Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
  4929. viewFrom = findPosition(structure, (this.visibleTop - line.top - marginHeight) / line.height);
  4930. viewTo = findPosition(structure, (this.visibleBottom - line.top + marginHeight) / line.height);
  4931. }
  4932. else {
  4933. let totalWidth = structure.total * this.heightOracle.charWidth;
  4934. let marginWidth = 2000 /* Margin */ * this.heightOracle.charWidth;
  4935. viewFrom = findPosition(structure, (this.pixelViewport.left - marginWidth) / totalWidth);
  4936. viewTo = findPosition(structure, (this.pixelViewport.right + marginWidth) / totalWidth);
  4937. }
  4938. let outside = [];
  4939. if (viewFrom > line.from)
  4940. outside.push({ from: line.from, to: viewFrom });
  4941. if (viewTo < line.to)
  4942. outside.push({ from: viewTo, to: line.to });
  4943. let sel = this.state.selection.main;
  4944. // Make sure the gaps don't cover a selection end
  4945. if (sel.from >= line.from && sel.from <= line.to)
  4946. cutRange(outside, sel.from - 10 /* SelectionMargin */, sel.from + 10 /* SelectionMargin */);
  4947. if (!sel.empty && sel.to >= line.from && sel.to <= line.to)
  4948. cutRange(outside, sel.to - 10 /* SelectionMargin */, sel.to + 10 /* SelectionMargin */);
  4949. for (let { from, to } of outside)
  4950. if (to - from > 1000 /* HalfMargin */) {
  4951. gaps.push(find(current, gap => gap.from >= line.from && gap.to <= line.to &&
  4952. Math.abs(gap.from - from) < 1000 /* HalfMargin */ && Math.abs(gap.to - to) < 1000 /* HalfMargin */) ||
  4953. new LineGap(from, to, this.gapSize(line, from, to, structure)));
  4954. }
  4955. }
  4956. return gaps;
  4957. }
  4958. gapSize(line, from, to, structure) {
  4959. let fraction = findFraction(structure, to) - findFraction(structure, from);
  4960. if (this.heightOracle.lineWrapping) {
  4961. return line.height * fraction;
  4962. }
  4963. else {
  4964. return structure.total * this.heightOracle.charWidth * fraction;
  4965. }
  4966. }
  4967. updateLineGaps(gaps) {
  4968. if (!LineGap.same(gaps, this.lineGaps)) {
  4969. this.lineGaps = gaps;
  4970. this.lineGapDeco = Decoration.set(gaps.map(gap => gap.draw(this.heightOracle.lineWrapping)));
  4971. }
  4972. }
  4973. computeVisibleRanges() {
  4974. let deco = this.stateDeco;
  4975. if (this.lineGaps.length)
  4976. deco = deco.concat(this.lineGapDeco);
  4977. let ranges = [];
  4978. state.RangeSet.spans(deco, this.viewport.from, this.viewport.to, {
  4979. span(from, to) { ranges.push({ from, to }); },
  4980. point() { }
  4981. }, 20);
  4982. let changed = ranges.length != this.visibleRanges.length ||
  4983. this.visibleRanges.some((r, i) => r.from != ranges[i].from || r.to != ranges[i].to);
  4984. this.visibleRanges = ranges;
  4985. return changed ? 4 /* Viewport */ : 0;
  4986. }
  4987. lineBlockAt(pos) {
  4988. return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
  4989. scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.state.doc, 0, 0), this.scaler);
  4990. }
  4991. lineBlockAtHeight(height) {
  4992. return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.state.doc, 0, 0), this.scaler);
  4993. }
  4994. elementAtHeight(height) {
  4995. return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
  4996. }
  4997. get docHeight() {
  4998. return this.scaler.toDOM(this.heightMap.height);
  4999. }
  5000. get contentHeight() {
  5001. return this.docHeight + this.paddingTop + this.paddingBottom;
  5002. }
  5003. }
  5004. class Viewport {
  5005. constructor(from, to) {
  5006. this.from = from;
  5007. this.to = to;
  5008. }
  5009. }
  5010. function lineStructure(from, to, stateDeco) {
  5011. let ranges = [], pos = from, total = 0;
  5012. state.RangeSet.spans(stateDeco, from, to, {
  5013. span() { },
  5014. point(from, to) {
  5015. if (from > pos) {
  5016. ranges.push({ from: pos, to: from });
  5017. total += from - pos;
  5018. }
  5019. pos = to;
  5020. }
  5021. }, 20); // We're only interested in collapsed ranges of a significant size
  5022. if (pos < to) {
  5023. ranges.push({ from: pos, to });
  5024. total += to - pos;
  5025. }
  5026. return { total, ranges };
  5027. }
  5028. function findPosition({ total, ranges }, ratio) {
  5029. if (ratio <= 0)
  5030. return ranges[0].from;
  5031. if (ratio >= 1)
  5032. return ranges[ranges.length - 1].to;
  5033. let dist = Math.floor(total * ratio);
  5034. for (let i = 0;; i++) {
  5035. let { from, to } = ranges[i], size = to - from;
  5036. if (dist <= size)
  5037. return from + dist;
  5038. dist -= size;
  5039. }
  5040. }
  5041. function findFraction(structure, pos) {
  5042. let counted = 0;
  5043. for (let { from, to } of structure.ranges) {
  5044. if (pos <= to) {
  5045. counted += pos - from;
  5046. break;
  5047. }
  5048. counted += to - from;
  5049. }
  5050. return counted / structure.total;
  5051. }
  5052. function cutRange(ranges, from, to) {
  5053. for (let i = 0; i < ranges.length; i++) {
  5054. let r = ranges[i];
  5055. if (r.from < to && r.to > from) {
  5056. let pieces = [];
  5057. if (r.from < from)
  5058. pieces.push({ from: r.from, to: from });
  5059. if (r.to > to)
  5060. pieces.push({ from: to, to: r.to });
  5061. ranges.splice(i, 1, ...pieces);
  5062. i += pieces.length - 1;
  5063. }
  5064. }
  5065. }
  5066. function find(array, f) {
  5067. for (let val of array)
  5068. if (f(val))
  5069. return val;
  5070. return undefined;
  5071. }
  5072. // Don't scale when the document height is within the range of what
  5073. // the DOM can handle.
  5074. const IdScaler = {
  5075. toDOM(n) { return n; },
  5076. fromDOM(n) { return n; },
  5077. scale: 1
  5078. };
  5079. // When the height is too big (> VP.MaxDOMHeight), scale down the
  5080. // regions outside the viewports so that the total height is
  5081. // VP.MaxDOMHeight.
  5082. class BigScaler {
  5083. constructor(doc, heightMap, viewports) {
  5084. let vpHeight = 0, base = 0, domBase = 0;
  5085. this.viewports = viewports.map(({ from, to }) => {
  5086. let top = heightMap.lineAt(from, QueryType.ByPos, doc, 0, 0).top;
  5087. let bottom = heightMap.lineAt(to, QueryType.ByPos, doc, 0, 0).bottom;
  5088. vpHeight += bottom - top;
  5089. return { from, to, top, bottom, domTop: 0, domBottom: 0 };
  5090. });
  5091. this.scale = (7000000 /* MaxDOMHeight */ - vpHeight) / (heightMap.height - vpHeight);
  5092. for (let obj of this.viewports) {
  5093. obj.domTop = domBase + (obj.top - base) * this.scale;
  5094. domBase = obj.domBottom = obj.domTop + (obj.bottom - obj.top);
  5095. base = obj.bottom;
  5096. }
  5097. }
  5098. toDOM(n) {
  5099. for (let i = 0, base = 0, domBase = 0;; i++) {
  5100. let vp = i < this.viewports.length ? this.viewports[i] : null;
  5101. if (!vp || n < vp.top)
  5102. return domBase + (n - base) * this.scale;
  5103. if (n <= vp.bottom)
  5104. return vp.domTop + (n - vp.top);
  5105. base = vp.bottom;
  5106. domBase = vp.domBottom;
  5107. }
  5108. }
  5109. fromDOM(n) {
  5110. for (let i = 0, base = 0, domBase = 0;; i++) {
  5111. let vp = i < this.viewports.length ? this.viewports[i] : null;
  5112. if (!vp || n < vp.domTop)
  5113. return base + (n - domBase) / this.scale;
  5114. if (n <= vp.domBottom)
  5115. return vp.top + (n - vp.domTop);
  5116. base = vp.bottom;
  5117. domBase = vp.domBottom;
  5118. }
  5119. }
  5120. }
  5121. function scaleBlock(block, scaler) {
  5122. if (scaler.scale == 1)
  5123. return block;
  5124. let bTop = scaler.toDOM(block.top), bBottom = scaler.toDOM(block.bottom);
  5125. return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block.type) ? block.type.map(b => scaleBlock(b, scaler)) : block.type);
  5126. }
  5127. const theme = state.Facet.define({ combine: strs => strs.join(" ") });
  5128. const darkTheme = state.Facet.define({ combine: values => values.indexOf(true) > -1 });
  5129. const baseThemeID = styleMod.StyleModule.newName(), baseLightID = styleMod.StyleModule.newName(), baseDarkID = styleMod.StyleModule.newName();
  5130. const lightDarkIDs = { "&light": "." + baseLightID, "&dark": "." + baseDarkID };
  5131. function buildTheme(main, spec, scopes) {
  5132. return new styleMod.StyleModule(spec, {
  5133. finish(sel) {
  5134. return /&/.test(sel) ? sel.replace(/&\w*/, m => {
  5135. if (m == "&")
  5136. return main;
  5137. if (!scopes || !scopes[m])
  5138. throw new RangeError(`Unsupported selector: ${m}`);
  5139. return scopes[m];
  5140. }) : main + " " + sel;
  5141. }
  5142. });
  5143. }
  5144. const baseTheme$1 = buildTheme("." + baseThemeID, {
  5145. "&.cm-editor": {
  5146. position: "relative !important",
  5147. boxSizing: "border-box",
  5148. "&.cm-focused": {
  5149. // Provide a simple default outline to make sure a focused
  5150. // editor is visually distinct. Can't leave the default behavior
  5151. // because that will apply to the content element, which is
  5152. // inside the scrollable container and doesn't include the
  5153. // gutters. We also can't use an 'auto' outline, since those
  5154. // are, for some reason, drawn behind the element content, which
  5155. // will cause things like the active line background to cover
  5156. // the outline (#297).
  5157. outline: "1px dotted #212121"
  5158. },
  5159. display: "flex !important",
  5160. flexDirection: "column"
  5161. },
  5162. ".cm-scroller": {
  5163. display: "flex !important",
  5164. alignItems: "flex-start !important",
  5165. fontFamily: "monospace",
  5166. lineHeight: 1.4,
  5167. height: "100%",
  5168. overflowX: "auto",
  5169. position: "relative",
  5170. zIndex: 0
  5171. },
  5172. ".cm-content": {
  5173. margin: 0,
  5174. flexGrow: 2,
  5175. flexShrink: 0,
  5176. minHeight: "100%",
  5177. display: "block",
  5178. whiteSpace: "pre",
  5179. wordWrap: "normal",
  5180. boxSizing: "border-box",
  5181. padding: "4px 0",
  5182. outline: "none",
  5183. "&[contenteditable=true]": {
  5184. WebkitUserModify: "read-write-plaintext-only",
  5185. }
  5186. },
  5187. ".cm-lineWrapping": {
  5188. whiteSpace_fallback: "pre-wrap",
  5189. whiteSpace: "break-spaces",
  5190. wordBreak: "break-word",
  5191. overflowWrap: "anywhere",
  5192. flexShrink: 1
  5193. },
  5194. "&light .cm-content": { caretColor: "black" },
  5195. "&dark .cm-content": { caretColor: "white" },
  5196. ".cm-line": {
  5197. display: "block",
  5198. padding: "0 2px 0 4px"
  5199. },
  5200. ".cm-selectionLayer": {
  5201. zIndex: -1,
  5202. contain: "size style"
  5203. },
  5204. ".cm-selectionBackground": {
  5205. position: "absolute",
  5206. },
  5207. "&light .cm-selectionBackground": {
  5208. background: "#d9d9d9"
  5209. },
  5210. "&dark .cm-selectionBackground": {
  5211. background: "#222"
  5212. },
  5213. "&light.cm-focused .cm-selectionBackground": {
  5214. background: "#d7d4f0"
  5215. },
  5216. "&dark.cm-focused .cm-selectionBackground": {
  5217. background: "#233"
  5218. },
  5219. ".cm-cursorLayer": {
  5220. zIndex: 100,
  5221. contain: "size style",
  5222. pointerEvents: "none"
  5223. },
  5224. "&.cm-focused .cm-cursorLayer": {
  5225. animation: "steps(1) cm-blink 1.2s infinite"
  5226. },
  5227. // Two animations defined so that we can switch between them to
  5228. // restart the animation without forcing another style
  5229. // recomputation.
  5230. "@keyframes cm-blink": { "0%": {}, "50%": { opacity: 0 }, "100%": {} },
  5231. "@keyframes cm-blink2": { "0%": {}, "50%": { opacity: 0 }, "100%": {} },
  5232. ".cm-cursor, .cm-dropCursor": {
  5233. position: "absolute",
  5234. borderLeft: "1.2px solid black",
  5235. marginLeft: "-0.6px",
  5236. pointerEvents: "none",
  5237. },
  5238. ".cm-cursor": {
  5239. display: "none"
  5240. },
  5241. "&dark .cm-cursor": {
  5242. borderLeftColor: "#444"
  5243. },
  5244. "&.cm-focused .cm-cursor": {
  5245. display: "block"
  5246. },
  5247. "&light .cm-activeLine": { backgroundColor: "#f3f9ff" },
  5248. "&dark .cm-activeLine": { backgroundColor: "#223039" },
  5249. "&light .cm-specialChar": { color: "red" },
  5250. "&dark .cm-specialChar": { color: "#f78" },
  5251. ".cm-gutters": {
  5252. display: "flex",
  5253. height: "100%",
  5254. boxSizing: "border-box",
  5255. left: 0,
  5256. zIndex: 200
  5257. },
  5258. "&light .cm-gutters": {
  5259. backgroundColor: "#f5f5f5",
  5260. color: "#6c6c6c",
  5261. borderRight: "1px solid #ddd"
  5262. },
  5263. "&dark .cm-gutters": {
  5264. backgroundColor: "#333338",
  5265. color: "#ccc"
  5266. },
  5267. ".cm-gutter": {
  5268. display: "flex !important",
  5269. flexDirection: "column",
  5270. flexShrink: 0,
  5271. boxSizing: "border-box",
  5272. minHeight: "100%",
  5273. overflow: "hidden"
  5274. },
  5275. ".cm-gutterElement": {
  5276. boxSizing: "border-box"
  5277. },
  5278. ".cm-lineNumbers .cm-gutterElement": {
  5279. padding: "0 3px 0 5px",
  5280. minWidth: "20px",
  5281. textAlign: "right",
  5282. whiteSpace: "nowrap"
  5283. },
  5284. "&light .cm-activeLineGutter": {
  5285. backgroundColor: "#e2f2ff"
  5286. },
  5287. "&dark .cm-activeLineGutter": {
  5288. backgroundColor: "#222227"
  5289. },
  5290. ".cm-panels": {
  5291. boxSizing: "border-box",
  5292. position: "sticky",
  5293. left: 0,
  5294. right: 0
  5295. },
  5296. "&light .cm-panels": {
  5297. backgroundColor: "#f5f5f5",
  5298. color: "black"
  5299. },
  5300. "&light .cm-panels-top": {
  5301. borderBottom: "1px solid #ddd"
  5302. },
  5303. "&light .cm-panels-bottom": {
  5304. borderTop: "1px solid #ddd"
  5305. },
  5306. "&dark .cm-panels": {
  5307. backgroundColor: "#333338",
  5308. color: "white"
  5309. },
  5310. ".cm-tab": {
  5311. display: "inline-block",
  5312. overflow: "hidden",
  5313. verticalAlign: "bottom"
  5314. },
  5315. ".cm-widgetBuffer": {
  5316. verticalAlign: "text-top",
  5317. height: "1em",
  5318. display: "inline"
  5319. },
  5320. ".cm-placeholder": {
  5321. color: "#888",
  5322. display: "inline-block",
  5323. verticalAlign: "top",
  5324. },
  5325. ".cm-button": {
  5326. verticalAlign: "middle",
  5327. color: "inherit",
  5328. fontSize: "70%",
  5329. padding: ".2em 1em",
  5330. borderRadius: "1px"
  5331. },
  5332. "&light .cm-button": {
  5333. backgroundImage: "linear-gradient(#eff1f5, #d9d9df)",
  5334. border: "1px solid #888",
  5335. "&:active": {
  5336. backgroundImage: "linear-gradient(#b4b4b4, #d0d3d6)"
  5337. }
  5338. },
  5339. "&dark .cm-button": {
  5340. backgroundImage: "linear-gradient(#393939, #111)",
  5341. border: "1px solid #888",
  5342. "&:active": {
  5343. backgroundImage: "linear-gradient(#111, #333)"
  5344. }
  5345. },
  5346. ".cm-textfield": {
  5347. verticalAlign: "middle",
  5348. color: "inherit",
  5349. fontSize: "70%",
  5350. border: "1px solid silver",
  5351. padding: ".2em .5em"
  5352. },
  5353. "&light .cm-textfield": {
  5354. backgroundColor: "white"
  5355. },
  5356. "&dark .cm-textfield": {
  5357. border: "1px solid #555",
  5358. backgroundColor: "inherit"
  5359. }
  5360. }, lightDarkIDs);
  5361. const observeOptions = {
  5362. childList: true,
  5363. characterData: true,
  5364. subtree: true,
  5365. attributes: true,
  5366. characterDataOldValue: true
  5367. };
  5368. // IE11 has very broken mutation observers, so we also listen to
  5369. // DOMCharacterDataModified there
  5370. const useCharData = browser.ie && browser.ie_version <= 11;
  5371. class DOMObserver {
  5372. constructor(view, onChange, onScrollChanged) {
  5373. this.view = view;
  5374. this.onChange = onChange;
  5375. this.onScrollChanged = onScrollChanged;
  5376. this.active = false;
  5377. // The known selection. Kept in our own object, as opposed to just
  5378. // directly accessing the selection because:
  5379. // - Safari doesn't report the right selection in shadow DOM
  5380. // - Reading from the selection forces a DOM layout
  5381. // - This way, we can ignore selectionchange events if we have
  5382. // already seen the 'new' selection
  5383. this.selectionRange = new DOMSelectionState;
  5384. // Set when a selection change is detected, cleared on flush
  5385. this.selectionChanged = false;
  5386. this.delayedFlush = -1;
  5387. this.resizeTimeout = -1;
  5388. this.queue = [];
  5389. this.delayedAndroidKey = null;
  5390. this.scrollTargets = [];
  5391. this.intersection = null;
  5392. this.resize = null;
  5393. this.intersecting = false;
  5394. this.gapIntersection = null;
  5395. this.gaps = [];
  5396. // Timeout for scheduling check of the parents that need scroll handlers
  5397. this.parentCheck = -1;
  5398. this.dom = view.contentDOM;
  5399. this.observer = new MutationObserver(mutations => {
  5400. for (let mut of mutations)
  5401. this.queue.push(mut);
  5402. // IE11 will sometimes (on typing over a selection or
  5403. // backspacing out a single character text node) call the
  5404. // observer callback before actually updating the DOM.
  5405. //
  5406. // Unrelatedly, iOS Safari will, when ending a composition,
  5407. // sometimes first clear it, deliver the mutations, and then
  5408. // reinsert the finished text. CodeMirror's handling of the
  5409. // deletion will prevent the reinsertion from happening,
  5410. // breaking composition.
  5411. if ((browser.ie && browser.ie_version <= 11 || browser.ios && view.composing) &&
  5412. mutations.some(m => m.type == "childList" && m.removedNodes.length ||
  5413. m.type == "characterData" && m.oldValue.length > m.target.nodeValue.length))
  5414. this.flushSoon();
  5415. else
  5416. this.flush();
  5417. });
  5418. if (useCharData)
  5419. this.onCharData = (event) => {
  5420. this.queue.push({ target: event.target,
  5421. type: "characterData",
  5422. oldValue: event.prevValue });
  5423. this.flushSoon();
  5424. };
  5425. this.onSelectionChange = this.onSelectionChange.bind(this);
  5426. window.addEventListener("resize", this.onResize = this.onResize.bind(this));
  5427. if (typeof ResizeObserver == "function") {
  5428. this.resize = new ResizeObserver(() => {
  5429. if (this.view.docView.lastUpdate < Date.now() - 75)
  5430. this.onResize();
  5431. });
  5432. this.resize.observe(view.scrollDOM);
  5433. }
  5434. window.addEventListener("beforeprint", this.onPrint = this.onPrint.bind(this));
  5435. this.start();
  5436. window.addEventListener("scroll", this.onScroll = this.onScroll.bind(this));
  5437. if (typeof IntersectionObserver == "function") {
  5438. this.intersection = new IntersectionObserver(entries => {
  5439. if (this.parentCheck < 0)
  5440. this.parentCheck = setTimeout(this.listenForScroll.bind(this), 1000);
  5441. if (entries.length > 0 && (entries[entries.length - 1].intersectionRatio > 0) != this.intersecting) {
  5442. this.intersecting = !this.intersecting;
  5443. if (this.intersecting != this.view.inView)
  5444. this.onScrollChanged(document.createEvent("Event"));
  5445. }
  5446. }, {});
  5447. this.intersection.observe(this.dom);
  5448. this.gapIntersection = new IntersectionObserver(entries => {
  5449. if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0)
  5450. this.onScrollChanged(document.createEvent("Event"));
  5451. }, {});
  5452. }
  5453. this.listenForScroll();
  5454. this.readSelectionRange();
  5455. this.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
  5456. }
  5457. onScroll(e) {
  5458. if (this.intersecting)
  5459. this.flush(false);
  5460. this.onScrollChanged(e);
  5461. }
  5462. onResize() {
  5463. if (this.resizeTimeout < 0)
  5464. this.resizeTimeout = setTimeout(() => {
  5465. this.resizeTimeout = -1;
  5466. this.view.requestMeasure();
  5467. }, 50);
  5468. }
  5469. onPrint() {
  5470. this.view.viewState.printing = true;
  5471. this.view.measure();
  5472. setTimeout(() => {
  5473. this.view.viewState.printing = false;
  5474. this.view.requestMeasure();
  5475. }, 500);
  5476. }
  5477. updateGaps(gaps) {
  5478. if (this.gapIntersection && (gaps.length != this.gaps.length || this.gaps.some((g, i) => g != gaps[i]))) {
  5479. this.gapIntersection.disconnect();
  5480. for (let gap of gaps)
  5481. this.gapIntersection.observe(gap);
  5482. this.gaps = gaps;
  5483. }
  5484. }
  5485. onSelectionChange(event) {
  5486. if (!this.readSelectionRange() || this.delayedAndroidKey)
  5487. return;
  5488. let { view } = this, sel = this.selectionRange;
  5489. if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(view.dom, sel))
  5490. return;
  5491. let context = sel.anchorNode && view.docView.nearest(sel.anchorNode);
  5492. if (context && context.ignoreEvent(event))
  5493. return;
  5494. // Deletions on IE11 fire their events in the wrong order, giving
  5495. // us a selection change event before the DOM changes are
  5496. // reported.
  5497. // Chrome Android has a similar issue when backspacing out a
  5498. // selection (#645).
  5499. if ((browser.ie && browser.ie_version <= 11 || browser.android && browser.chrome) && !view.state.selection.main.empty &&
  5500. // (Selection.isCollapsed isn't reliable on IE)
  5501. sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset))
  5502. this.flushSoon();
  5503. else
  5504. this.flush(false);
  5505. }
  5506. readSelectionRange() {
  5507. let { root } = this.view;
  5508. // The Selection object is broken in shadow roots in Safari. See
  5509. // https://github.com/codemirror/dev/issues/414
  5510. let range = browser.safari && root.nodeType == 11 && deepActiveElement() == this.view.contentDOM &&
  5511. safariSelectionRangeHack(this.view) || getSelection(root);
  5512. if (!range || this.selectionRange.eq(range))
  5513. return false;
  5514. this.selectionRange.setRange(range);
  5515. return this.selectionChanged = true;
  5516. }
  5517. setSelectionRange(anchor, head) {
  5518. this.selectionRange.set(anchor.node, anchor.offset, head.node, head.offset);
  5519. this.selectionChanged = false;
  5520. }
  5521. clearSelectionRange() {
  5522. this.selectionRange.set(null, 0, null, 0);
  5523. }
  5524. listenForScroll() {
  5525. this.parentCheck = -1;
  5526. let i = 0, changed = null;
  5527. for (let dom = this.dom; dom;) {
  5528. if (dom.nodeType == 1) {
  5529. if (!changed && i < this.scrollTargets.length && this.scrollTargets[i] == dom)
  5530. i++;
  5531. else if (!changed)
  5532. changed = this.scrollTargets.slice(0, i);
  5533. if (changed)
  5534. changed.push(dom);
  5535. dom = dom.assignedSlot || dom.parentNode;
  5536. }
  5537. else if (dom.nodeType == 11) { // Shadow root
  5538. dom = dom.host;
  5539. }
  5540. else {
  5541. break;
  5542. }
  5543. }
  5544. if (i < this.scrollTargets.length && !changed)
  5545. changed = this.scrollTargets.slice(0, i);
  5546. if (changed) {
  5547. for (let dom of this.scrollTargets)
  5548. dom.removeEventListener("scroll", this.onScroll);
  5549. for (let dom of this.scrollTargets = changed)
  5550. dom.addEventListener("scroll", this.onScroll);
  5551. }
  5552. }
  5553. ignore(f) {
  5554. if (!this.active)
  5555. return f();
  5556. try {
  5557. this.stop();
  5558. return f();
  5559. }
  5560. finally {
  5561. this.start();
  5562. this.clear();
  5563. }
  5564. }
  5565. start() {
  5566. if (this.active)
  5567. return;
  5568. this.observer.observe(this.dom, observeOptions);
  5569. if (useCharData)
  5570. this.dom.addEventListener("DOMCharacterDataModified", this.onCharData);
  5571. this.active = true;
  5572. }
  5573. stop() {
  5574. if (!this.active)
  5575. return;
  5576. this.active = false;
  5577. this.observer.disconnect();
  5578. if (useCharData)
  5579. this.dom.removeEventListener("DOMCharacterDataModified", this.onCharData);
  5580. }
  5581. // Throw away any pending changes
  5582. clear() {
  5583. this.processRecords();
  5584. this.queue.length = 0;
  5585. this.selectionChanged = false;
  5586. }
  5587. // Chrome Android, especially in combination with GBoard, not only
  5588. // doesn't reliably fire regular key events, but also often
  5589. // surrounds the effect of enter or backspace with a bunch of
  5590. // composition events that, when interrupted, cause text duplication
  5591. // or other kinds of corruption. This hack makes the editor back off
  5592. // from handling DOM changes for a moment when such a key is
  5593. // detected (via beforeinput or keydown), and then tries to flush
  5594. // them or, if that has no effect, dispatches the given key.
  5595. delayAndroidKey(key, keyCode) {
  5596. if (!this.delayedAndroidKey)
  5597. requestAnimationFrame(() => {
  5598. let key = this.delayedAndroidKey;
  5599. this.delayedAndroidKey = null;
  5600. this.delayedFlush = -1;
  5601. if (!this.flush())
  5602. dispatchKey(this.view.contentDOM, key.key, key.keyCode);
  5603. });
  5604. // Since backspace beforeinput is sometimes signalled spuriously,
  5605. // Enter always takes precedence.
  5606. if (!this.delayedAndroidKey || key == "Enter")
  5607. this.delayedAndroidKey = { key, keyCode };
  5608. }
  5609. flushSoon() {
  5610. if (this.delayedFlush < 0)
  5611. this.delayedFlush = window.setTimeout(() => { this.delayedFlush = -1; this.flush(); }, 20);
  5612. }
  5613. forceFlush() {
  5614. if (this.delayedFlush >= 0) {
  5615. window.clearTimeout(this.delayedFlush);
  5616. this.delayedFlush = -1;
  5617. this.flush();
  5618. }
  5619. }
  5620. processRecords() {
  5621. let records = this.queue;
  5622. for (let mut of this.observer.takeRecords())
  5623. records.push(mut);
  5624. if (records.length)
  5625. this.queue = [];
  5626. let from = -1, to = -1, typeOver = false;
  5627. for (let record of records) {
  5628. let range = this.readMutation(record);
  5629. if (!range)
  5630. continue;
  5631. if (range.typeOver)
  5632. typeOver = true;
  5633. if (from == -1) {
  5634. ({ from, to } = range);
  5635. }
  5636. else {
  5637. from = Math.min(range.from, from);
  5638. to = Math.max(range.to, to);
  5639. }
  5640. }
  5641. return { from, to, typeOver };
  5642. }
  5643. // Apply pending changes, if any
  5644. flush(readSelection = true) {
  5645. // Completely hold off flushing when pending keys are set—the code
  5646. // managing those will make sure processRecords is called and the
  5647. // view is resynchronized after
  5648. if (this.delayedFlush >= 0 || this.delayedAndroidKey)
  5649. return;
  5650. if (readSelection)
  5651. this.readSelectionRange();
  5652. let { from, to, typeOver } = this.processRecords();
  5653. let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
  5654. if (from < 0 && !newSel)
  5655. return;
  5656. this.selectionChanged = false;
  5657. let startState = this.view.state;
  5658. let handled = this.onChange(from, to, typeOver);
  5659. // The view wasn't updated
  5660. if (this.view.state == startState)
  5661. this.view.update([]);
  5662. return handled;
  5663. }
  5664. readMutation(rec) {
  5665. let cView = this.view.docView.nearest(rec.target);
  5666. if (!cView || cView.ignoreMutation(rec))
  5667. return null;
  5668. cView.markDirty(rec.type == "attributes");
  5669. if (rec.type == "attributes")
  5670. cView.dirty |= 4 /* Attrs */;
  5671. if (rec.type == "childList") {
  5672. let childBefore = findChild(cView, rec.previousSibling || rec.target.previousSibling, -1);
  5673. let childAfter = findChild(cView, rec.nextSibling || rec.target.nextSibling, 1);
  5674. return { from: childBefore ? cView.posAfter(childBefore) : cView.posAtStart,
  5675. to: childAfter ? cView.posBefore(childAfter) : cView.posAtEnd, typeOver: false };
  5676. }
  5677. else if (rec.type == "characterData") {
  5678. return { from: cView.posAtStart, to: cView.posAtEnd, typeOver: rec.target.nodeValue == rec.oldValue };
  5679. }
  5680. else {
  5681. return null;
  5682. }
  5683. }
  5684. destroy() {
  5685. var _a, _b, _c;
  5686. this.stop();
  5687. (_a = this.intersection) === null || _a === void 0 ? void 0 : _a.disconnect();
  5688. (_b = this.gapIntersection) === null || _b === void 0 ? void 0 : _b.disconnect();
  5689. (_c = this.resize) === null || _c === void 0 ? void 0 : _c.disconnect();
  5690. for (let dom of this.scrollTargets)
  5691. dom.removeEventListener("scroll", this.onScroll);
  5692. window.removeEventListener("scroll", this.onScroll);
  5693. window.removeEventListener("resize", this.onResize);
  5694. window.removeEventListener("beforeprint", this.onPrint);
  5695. this.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
  5696. clearTimeout(this.parentCheck);
  5697. clearTimeout(this.resizeTimeout);
  5698. }
  5699. }
  5700. function findChild(cView, dom, dir) {
  5701. while (dom) {
  5702. let curView = ContentView.get(dom);
  5703. if (curView && curView.parent == cView)
  5704. return curView;
  5705. let parent = dom.parentNode;
  5706. dom = parent != cView.dom ? parent : dir > 0 ? dom.nextSibling : dom.previousSibling;
  5707. }
  5708. return null;
  5709. }
  5710. // Used to work around a Safari Selection/shadow DOM bug (#414)
  5711. function safariSelectionRangeHack(view) {
  5712. let found = null;
  5713. // Because Safari (at least in 2018-2021) doesn't provide regular
  5714. // access to the selection inside a shadowroot, we have to perform a
  5715. // ridiculous hack to get at it—using `execCommand` to trigger a
  5716. // `beforeInput` event so that we can read the target range from the
  5717. // event.
  5718. function read(event) {
  5719. event.preventDefault();
  5720. event.stopImmediatePropagation();
  5721. found = event.getTargetRanges()[0];
  5722. }
  5723. view.contentDOM.addEventListener("beforeinput", read, true);
  5724. document.execCommand("indent");
  5725. view.contentDOM.removeEventListener("beforeinput", read, true);
  5726. if (!found)
  5727. return null;
  5728. let anchorNode = found.startContainer, anchorOffset = found.startOffset;
  5729. let focusNode = found.endContainer, focusOffset = found.endOffset;
  5730. let curAnchor = view.docView.domAtPos(view.state.selection.main.anchor);
  5731. // Since such a range doesn't distinguish between anchor and head,
  5732. // use a heuristic that flips it around if its end matches the
  5733. // current anchor.
  5734. if (isEquivalentPosition(curAnchor.node, curAnchor.offset, focusNode, focusOffset))
  5735. [anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
  5736. return { anchorNode, anchorOffset, focusNode, focusOffset };
  5737. }
  5738. function applyDOMChange(view, start, end, typeOver) {
  5739. let change, newSel;
  5740. let sel = view.state.selection.main;
  5741. if (start > -1) {
  5742. let bounds = view.docView.domBoundsAround(start, end, 0);
  5743. if (!bounds || view.state.readOnly)
  5744. return false;
  5745. let { from, to } = bounds;
  5746. let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
  5747. let reader = new DOMReader(selPoints, view.state);
  5748. reader.readRange(bounds.startDOM, bounds.endDOM);
  5749. let preferredPos = sel.from, preferredSide = null;
  5750. // Prefer anchoring to end when Backspace is pressed (or, on
  5751. // Android, when something was deleted)
  5752. if (view.inputState.lastKeyCode === 8 && view.inputState.lastKeyTime > Date.now() - 100 ||
  5753. browser.android && reader.text.length < to - from) {
  5754. preferredPos = sel.to;
  5755. preferredSide = "end";
  5756. }
  5757. let diff = findDiff(view.state.doc.sliceString(from, to, LineBreakPlaceholder), reader.text, preferredPos - from, preferredSide);
  5758. if (diff) {
  5759. // Chrome inserts two newlines when pressing shift-enter at the
  5760. // end of a line. This drops one of those.
  5761. if (browser.chrome && view.inputState.lastKeyCode == 13 &&
  5762. diff.toB == diff.from + 2 && reader.text.slice(diff.from, diff.toB) == LineBreakPlaceholder + LineBreakPlaceholder)
  5763. diff.toB--;
  5764. change = { from: from + diff.from, to: from + diff.toA,
  5765. insert: state.Text.of(reader.text.slice(diff.from, diff.toB).split(LineBreakPlaceholder)) };
  5766. }
  5767. newSel = selectionFromPoints(selPoints, from);
  5768. }
  5769. else if (view.hasFocus || !view.state.facet(editable)) {
  5770. let domSel = view.observer.selectionRange;
  5771. let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
  5772. let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset ||
  5773. !contains(view.contentDOM, domSel.focusNode)
  5774. ? view.state.selection.main.head
  5775. : view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset);
  5776. let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset ||
  5777. !contains(view.contentDOM, domSel.anchorNode)
  5778. ? view.state.selection.main.anchor
  5779. : view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset);
  5780. if (head != sel.head || anchor != sel.anchor)
  5781. newSel = state.EditorSelection.single(anchor, head);
  5782. }
  5783. if (!change && !newSel)
  5784. return false;
  5785. // Heuristic to notice typing over a selected character
  5786. if (!change && typeOver && !sel.empty && newSel && newSel.main.empty)
  5787. change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
  5788. // If the change is inside the selection and covers most of it,
  5789. // assume it is a selection replace (with identical characters at
  5790. // the start/end not included in the diff)
  5791. else if (change && change.from >= sel.from && change.to <= sel.to &&
  5792. (change.from != sel.from || change.to != sel.to) &&
  5793. (sel.to - sel.from) - (change.to - change.from) <= 4)
  5794. change = {
  5795. from: sel.from, to: sel.to,
  5796. insert: view.state.doc.slice(sel.from, change.from).append(change.insert).append(view.state.doc.slice(change.to, sel.to))
  5797. };
  5798. // Detect insert-period-on-double-space Mac behavior, and transform
  5799. // it into a regular space insert.
  5800. else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
  5801. change.insert.toString() == ".")
  5802. change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) };
  5803. if (change) {
  5804. let startState = view.state;
  5805. if (browser.ios && view.inputState.flushIOSKey(view))
  5806. return true;
  5807. // Android browsers don't fire reasonable key events for enter,
  5808. // backspace, or delete. So this detects changes that look like
  5809. // they're caused by those keys, and reinterprets them as key
  5810. // events. (Some of these keys are also handled by beforeinput
  5811. // events and the pendingAndroidKey mechanism, but that's not
  5812. // reliable in all situations.)
  5813. if (browser.android &&
  5814. ((change.from == sel.from && change.to == sel.to &&
  5815. change.insert.length == 1 && change.insert.lines == 2 &&
  5816. dispatchKey(view.contentDOM, "Enter", 13)) ||
  5817. (change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
  5818. dispatchKey(view.contentDOM, "Backspace", 8)) ||
  5819. (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
  5820. dispatchKey(view.contentDOM, "Delete", 46))))
  5821. return true;
  5822. let text = change.insert.toString();
  5823. if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
  5824. return true;
  5825. if (view.inputState.composing >= 0)
  5826. view.inputState.composing++;
  5827. let tr;
  5828. if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
  5829. (!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) &&
  5830. view.inputState.composing < 0) {
  5831. let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : "";
  5832. let after = sel.to > change.to ? startState.sliceDoc(change.to, sel.to) : "";
  5833. tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) + after));
  5834. }
  5835. else {
  5836. let changes = startState.changes(change);
  5837. let mainSel = newSel && !startState.selection.main.eq(newSel.main) && newSel.main.to <= changes.newLength
  5838. ? newSel.main : undefined;
  5839. // Try to apply a composition change to all cursors
  5840. if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
  5841. change.to <= sel.to && change.to >= sel.to - 10) {
  5842. let replaced = view.state.sliceDoc(change.from, change.to);
  5843. let compositionRange = compositionSurroundingNode(view) || view.state.doc.lineAt(sel.head);
  5844. let offset = sel.to - change.to, size = sel.to - sel.from;
  5845. tr = startState.changeByRange(range => {
  5846. if (range.from == sel.from && range.to == sel.to)
  5847. return { changes, range: mainSel || range.map(changes) };
  5848. let to = range.to - offset, from = to - replaced.length;
  5849. if (range.to - range.from != size || view.state.sliceDoc(from, to) != replaced ||
  5850. // Unfortunately, there's no way to make multiple
  5851. // changes in the same node work without aborting
  5852. // composition, so cursors in the composition range are
  5853. // ignored.
  5854. compositionRange && range.to >= compositionRange.from && range.from <= compositionRange.to)
  5855. return { range };
  5856. let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
  5857. return {
  5858. changes: rangeChanges,
  5859. range: !mainSel ? range.map(rangeChanges) :
  5860. state.EditorSelection.range(Math.max(0, mainSel.anchor + selOff), Math.max(0, mainSel.head + selOff))
  5861. };
  5862. });
  5863. }
  5864. else {
  5865. tr = {
  5866. changes,
  5867. selection: mainSel && startState.selection.replaceRange(mainSel)
  5868. };
  5869. }
  5870. }
  5871. let userEvent = "input.type";
  5872. if (view.composing) {
  5873. userEvent += ".compose";
  5874. if (view.inputState.compositionFirstChange) {
  5875. userEvent += ".start";
  5876. view.inputState.compositionFirstChange = false;
  5877. }
  5878. }
  5879. view.dispatch(tr, { scrollIntoView: true, userEvent });
  5880. return true;
  5881. }
  5882. else if (newSel && !newSel.main.eq(sel)) {
  5883. let scrollIntoView = false, userEvent = "select";
  5884. if (view.inputState.lastSelectionTime > Date.now() - 50) {
  5885. if (view.inputState.lastSelectionOrigin == "select")
  5886. scrollIntoView = true;
  5887. userEvent = view.inputState.lastSelectionOrigin;
  5888. }
  5889. view.dispatch({ selection: newSel, scrollIntoView, userEvent });
  5890. return true;
  5891. }
  5892. else {
  5893. return false;
  5894. }
  5895. }
  5896. function findDiff(a, b, preferredPos, preferredSide) {
  5897. let minLen = Math.min(a.length, b.length);
  5898. let from = 0;
  5899. while (from < minLen && a.charCodeAt(from) == b.charCodeAt(from))
  5900. from++;
  5901. if (from == minLen && a.length == b.length)
  5902. return null;
  5903. let toA = a.length, toB = b.length;
  5904. while (toA > 0 && toB > 0 && a.charCodeAt(toA - 1) == b.charCodeAt(toB - 1)) {
  5905. toA--;
  5906. toB--;
  5907. }
  5908. if (preferredSide == "end") {
  5909. let adjust = Math.max(0, from - Math.min(toA, toB));
  5910. preferredPos -= toA + adjust - from;
  5911. }
  5912. if (toA < from && a.length < b.length) {
  5913. let move = preferredPos <= from && preferredPos >= toA ? from - preferredPos : 0;
  5914. from -= move;
  5915. toB = from + (toB - toA);
  5916. toA = from;
  5917. }
  5918. else if (toB < from) {
  5919. let move = preferredPos <= from && preferredPos >= toB ? from - preferredPos : 0;
  5920. from -= move;
  5921. toA = from + (toA - toB);
  5922. toB = from;
  5923. }
  5924. return { from, toA, toB };
  5925. }
  5926. function selectionPoints(view) {
  5927. let result = [];
  5928. if (view.root.activeElement != view.contentDOM)
  5929. return result;
  5930. let { anchorNode, anchorOffset, focusNode, focusOffset } = view.observer.selectionRange;
  5931. if (anchorNode) {
  5932. result.push(new DOMPoint(anchorNode, anchorOffset));
  5933. if (focusNode != anchorNode || focusOffset != anchorOffset)
  5934. result.push(new DOMPoint(focusNode, focusOffset));
  5935. }
  5936. return result;
  5937. }
  5938. function selectionFromPoints(points, base) {
  5939. if (points.length == 0)
  5940. return null;
  5941. let anchor = points[0].pos, head = points.length == 2 ? points[1].pos : anchor;
  5942. return anchor > -1 && head > -1 ? state.EditorSelection.single(anchor + base, head + base) : null;
  5943. }
  5944. // The editor's update state machine looks something like this:
  5945. //
  5946. // Idle → Updating ⇆ Idle (unchecked) → Measuring → Idle
  5947. // ↑ ↓
  5948. // Updating (measure)
  5949. //
  5950. // The difference between 'Idle' and 'Idle (unchecked)' lies in
  5951. // whether a layout check has been scheduled. A regular update through
  5952. // the `update` method updates the DOM in a write-only fashion, and
  5953. // relies on a check (scheduled with `requestAnimationFrame`) to make
  5954. // sure everything is where it should be and the viewport covers the
  5955. // visible code. That check continues to measure and then optionally
  5956. // update until it reaches a coherent state.
  5957. /**
  5958. An editor view represents the editor's user interface. It holds
  5959. the editable DOM surface, and possibly other elements such as the
  5960. line number gutter. It handles events and dispatches state
  5961. transactions for editing actions.
  5962. */
  5963. class EditorView {
  5964. /**
  5965. Construct a new view. You'll want to either provide a `parent`
  5966. option, or put `view.dom` into your document after creating a
  5967. view, so that the user can see the editor.
  5968. */
  5969. constructor(config = {}) {
  5970. this.plugins = [];
  5971. this.pluginMap = new Map;
  5972. this.editorAttrs = {};
  5973. this.contentAttrs = {};
  5974. this.bidiCache = [];
  5975. this.destroyed = false;
  5976. /**
  5977. @internal
  5978. */
  5979. this.updateState = 2 /* Updating */;
  5980. /**
  5981. @internal
  5982. */
  5983. this.measureScheduled = -1;
  5984. /**
  5985. @internal
  5986. */
  5987. this.measureRequests = [];
  5988. this.contentDOM = document.createElement("div");
  5989. this.scrollDOM = document.createElement("div");
  5990. this.scrollDOM.tabIndex = -1;
  5991. this.scrollDOM.className = "cm-scroller";
  5992. this.scrollDOM.appendChild(this.contentDOM);
  5993. this.announceDOM = document.createElement("div");
  5994. this.announceDOM.style.cssText = "position: absolute; top: -10000px";
  5995. this.announceDOM.setAttribute("aria-live", "polite");
  5996. this.dom = document.createElement("div");
  5997. this.dom.appendChild(this.announceDOM);
  5998. this.dom.appendChild(this.scrollDOM);
  5999. this._dispatch = config.dispatch || ((tr) => this.update([tr]));
  6000. this.dispatch = this.dispatch.bind(this);
  6001. this.root = (config.root || getRoot(config.parent) || document);
  6002. this.viewState = new ViewState(config.state || state.EditorState.create(config));
  6003. this.plugins = this.state.facet(viewPlugin).map(spec => new PluginInstance(spec));
  6004. for (let plugin of this.plugins)
  6005. plugin.update(this);
  6006. this.observer = new DOMObserver(this, (from, to, typeOver) => {
  6007. return applyDOMChange(this, from, to, typeOver);
  6008. }, event => {
  6009. this.inputState.runScrollHandlers(this, event);
  6010. if (this.observer.intersecting)
  6011. this.measure();
  6012. });
  6013. this.inputState = new InputState(this);
  6014. this.inputState.ensureHandlers(this, this.plugins);
  6015. this.docView = new DocView(this);
  6016. this.mountStyles();
  6017. this.updateAttrs();
  6018. this.updateState = 0 /* Idle */;
  6019. this.requestMeasure();
  6020. if (config.parent)
  6021. config.parent.appendChild(this.dom);
  6022. }
  6023. /**
  6024. The current editor state.
  6025. */
  6026. get state() { return this.viewState.state; }
  6027. /**
  6028. To be able to display large documents without consuming too much
  6029. memory or overloading the browser, CodeMirror only draws the
  6030. code that is visible (plus a margin around it) to the DOM. This
  6031. property tells you the extent of the current drawn viewport, in
  6032. document positions.
  6033. */
  6034. get viewport() { return this.viewState.viewport; }
  6035. /**
  6036. When there are, for example, large collapsed ranges in the
  6037. viewport, its size can be a lot bigger than the actual visible
  6038. content. Thus, if you are doing something like styling the
  6039. content in the viewport, it is preferable to only do so for
  6040. these ranges, which are the subset of the viewport that is
  6041. actually drawn.
  6042. */
  6043. get visibleRanges() { return this.viewState.visibleRanges; }
  6044. /**
  6045. Returns false when the editor is entirely scrolled out of view
  6046. or otherwise hidden.
  6047. */
  6048. get inView() { return this.viewState.inView; }
  6049. /**
  6050. Indicates whether the user is currently composing text via
  6051. [IME](https://en.wikipedia.org/wiki/Input_method), and at least
  6052. one change has been made in the current composition.
  6053. */
  6054. get composing() { return this.inputState.composing > 0; }
  6055. /**
  6056. Indicates whether the user is currently in composing state. Note
  6057. that on some platforms, like Android, this will be the case a
  6058. lot, since just putting the cursor on a word starts a
  6059. composition there.
  6060. */
  6061. get compositionStarted() { return this.inputState.composing >= 0; }
  6062. dispatch(...input) {
  6063. this._dispatch(input.length == 1 && input[0] instanceof state.Transaction ? input[0]
  6064. : this.state.update(...input));
  6065. }
  6066. /**
  6067. Update the view for the given array of transactions. This will
  6068. update the visible document and selection to match the state
  6069. produced by the transactions, and notify view plugins of the
  6070. change. You should usually call
  6071. [`dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) instead, which uses this
  6072. as a primitive.
  6073. */
  6074. update(transactions) {
  6075. if (this.updateState != 0 /* Idle */)
  6076. throw new Error("Calls to EditorView.update are not allowed while an update is in progress");
  6077. let redrawn = false, attrsChanged = false, update;
  6078. let state$1 = this.state;
  6079. for (let tr of transactions) {
  6080. if (tr.startState != state$1)
  6081. throw new RangeError("Trying to update state with a transaction that doesn't start from the previous state.");
  6082. state$1 = tr.state;
  6083. }
  6084. if (this.destroyed) {
  6085. this.viewState.state = state$1;
  6086. return;
  6087. }
  6088. this.observer.clear();
  6089. // When the phrases change, redraw the editor
  6090. if (state$1.facet(state.EditorState.phrases) != this.state.facet(state.EditorState.phrases))
  6091. return this.setState(state$1);
  6092. update = ViewUpdate.create(this, state$1, transactions);
  6093. let scrollTarget = this.viewState.scrollTarget;
  6094. try {
  6095. this.updateState = 2 /* Updating */;
  6096. for (let tr of transactions) {
  6097. if (scrollTarget)
  6098. scrollTarget = scrollTarget.map(tr.changes);
  6099. if (tr.scrollIntoView) {
  6100. let { main } = tr.state.selection;
  6101. scrollTarget = new ScrollTarget(main.empty ? main : state.EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
  6102. }
  6103. for (let e of tr.effects)
  6104. if (e.is(scrollIntoView))
  6105. scrollTarget = e.value;
  6106. }
  6107. this.viewState.update(update, scrollTarget);
  6108. this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
  6109. if (!update.empty) {
  6110. this.updatePlugins(update);
  6111. this.inputState.update(update);
  6112. }
  6113. redrawn = this.docView.update(update);
  6114. if (this.state.facet(styleModule) != this.styleModules)
  6115. this.mountStyles();
  6116. attrsChanged = this.updateAttrs();
  6117. this.showAnnouncements(transactions);
  6118. this.docView.updateSelection(redrawn, transactions.some(tr => tr.isUserEvent("select.pointer")));
  6119. }
  6120. finally {
  6121. this.updateState = 0 /* Idle */;
  6122. }
  6123. if (update.startState.facet(theme) != update.state.facet(theme))
  6124. this.viewState.mustMeasureContent = true;
  6125. if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
  6126. this.requestMeasure();
  6127. if (!update.empty)
  6128. for (let listener of this.state.facet(updateListener))
  6129. listener(update);
  6130. }
  6131. /**
  6132. Reset the view to the given state. (This will cause the entire
  6133. document to be redrawn and all view plugins to be reinitialized,
  6134. so you should probably only use it when the new state isn't
  6135. derived from the old state. Otherwise, use
  6136. [`dispatch`](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) instead.)
  6137. */
  6138. setState(newState) {
  6139. if (this.updateState != 0 /* Idle */)
  6140. throw new Error("Calls to EditorView.setState are not allowed while an update is in progress");
  6141. if (this.destroyed) {
  6142. this.viewState.state = newState;
  6143. return;
  6144. }
  6145. this.updateState = 2 /* Updating */;
  6146. let hadFocus = this.hasFocus;
  6147. try {
  6148. for (let plugin of this.plugins)
  6149. plugin.destroy(this);
  6150. this.viewState = new ViewState(newState);
  6151. this.plugins = newState.facet(viewPlugin).map(spec => new PluginInstance(spec));
  6152. this.pluginMap.clear();
  6153. for (let plugin of this.plugins)
  6154. plugin.update(this);
  6155. this.docView = new DocView(this);
  6156. this.inputState.ensureHandlers(this, this.plugins);
  6157. this.mountStyles();
  6158. this.updateAttrs();
  6159. this.bidiCache = [];
  6160. }
  6161. finally {
  6162. this.updateState = 0 /* Idle */;
  6163. }
  6164. if (hadFocus)
  6165. this.focus();
  6166. this.requestMeasure();
  6167. }
  6168. updatePlugins(update) {
  6169. let prevSpecs = update.startState.facet(viewPlugin), specs = update.state.facet(viewPlugin);
  6170. if (prevSpecs != specs) {
  6171. let newPlugins = [];
  6172. for (let spec of specs) {
  6173. let found = prevSpecs.indexOf(spec);
  6174. if (found < 0) {
  6175. newPlugins.push(new PluginInstance(spec));
  6176. }
  6177. else {
  6178. let plugin = this.plugins[found];
  6179. plugin.mustUpdate = update;
  6180. newPlugins.push(plugin);
  6181. }
  6182. }
  6183. for (let plugin of this.plugins)
  6184. if (plugin.mustUpdate != update)
  6185. plugin.destroy(this);
  6186. this.plugins = newPlugins;
  6187. this.pluginMap.clear();
  6188. this.inputState.ensureHandlers(this, this.plugins);
  6189. }
  6190. else {
  6191. for (let p of this.plugins)
  6192. p.mustUpdate = update;
  6193. }
  6194. for (let i = 0; i < this.plugins.length; i++)
  6195. this.plugins[i].update(this);
  6196. }
  6197. /**
  6198. @internal
  6199. */
  6200. measure(flush = true) {
  6201. if (this.destroyed)
  6202. return;
  6203. if (this.measureScheduled > -1)
  6204. cancelAnimationFrame(this.measureScheduled);
  6205. this.measureScheduled = 0; // Prevent requestMeasure calls from scheduling another animation frame
  6206. if (flush)
  6207. this.observer.flush();
  6208. let updated = null;
  6209. try {
  6210. for (let i = 0;; i++) {
  6211. this.updateState = 1 /* Measuring */;
  6212. let oldViewport = this.viewport;
  6213. let changed = this.viewState.measure(this);
  6214. if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
  6215. break;
  6216. if (i > 5) {
  6217. console.warn(this.measureRequests.length
  6218. ? "Measure loop restarted more than 5 times"
  6219. : "Viewport failed to stabilize");
  6220. break;
  6221. }
  6222. let measuring = [];
  6223. // Only run measure requests in this cycle when the viewport didn't change
  6224. if (!(changed & 4 /* Viewport */))
  6225. [this.measureRequests, measuring] = [measuring, this.measureRequests];
  6226. let measured = measuring.map(m => {
  6227. try {
  6228. return m.read(this);
  6229. }
  6230. catch (e) {
  6231. logException(this.state, e);
  6232. return BadMeasure;
  6233. }
  6234. });
  6235. let update = ViewUpdate.create(this, this.state, []), redrawn = false, scrolled = false;
  6236. update.flags |= changed;
  6237. if (!updated)
  6238. updated = update;
  6239. else
  6240. updated.flags |= changed;
  6241. this.updateState = 2 /* Updating */;
  6242. if (!update.empty) {
  6243. this.updatePlugins(update);
  6244. this.inputState.update(update);
  6245. this.updateAttrs();
  6246. redrawn = this.docView.update(update);
  6247. }
  6248. for (let i = 0; i < measuring.length; i++)
  6249. if (measured[i] != BadMeasure) {
  6250. try {
  6251. let m = measuring[i];
  6252. if (m.write)
  6253. m.write(measured[i], this);
  6254. }
  6255. catch (e) {
  6256. logException(this.state, e);
  6257. }
  6258. }
  6259. if (this.viewState.scrollTarget) {
  6260. this.docView.scrollIntoView(this.viewState.scrollTarget);
  6261. this.viewState.scrollTarget = null;
  6262. scrolled = true;
  6263. }
  6264. if (redrawn)
  6265. this.docView.updateSelection(true);
  6266. if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
  6267. !scrolled && this.measureRequests.length == 0)
  6268. break;
  6269. }
  6270. }
  6271. finally {
  6272. this.updateState = 0 /* Idle */;
  6273. this.measureScheduled = -1;
  6274. }
  6275. if (updated && !updated.empty)
  6276. for (let listener of this.state.facet(updateListener))
  6277. listener(updated);
  6278. }
  6279. /**
  6280. Get the CSS classes for the currently active editor themes.
  6281. */
  6282. get themeClasses() {
  6283. return baseThemeID + " " +
  6284. (this.state.facet(darkTheme) ? baseDarkID : baseLightID) + " " +
  6285. this.state.facet(theme);
  6286. }
  6287. updateAttrs() {
  6288. let editorAttrs = attrsFromFacet(this, editorAttributes, {
  6289. class: "cm-editor" + (this.hasFocus ? " cm-focused " : " ") + this.themeClasses
  6290. });
  6291. let contentAttrs = {
  6292. spellcheck: "false",
  6293. autocorrect: "off",
  6294. autocapitalize: "off",
  6295. translate: "no",
  6296. contenteditable: !this.state.facet(editable) ? "false" : "true",
  6297. class: "cm-content",
  6298. style: `${browser.tabSize}: ${this.state.tabSize}`,
  6299. role: "textbox",
  6300. "aria-multiline": "true"
  6301. };
  6302. if (this.state.readOnly)
  6303. contentAttrs["aria-readonly"] = "true";
  6304. attrsFromFacet(this, contentAttributes, contentAttrs);
  6305. let changed = this.observer.ignore(() => {
  6306. let changedContent = updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
  6307. let changedEditor = updateAttrs(this.dom, this.editorAttrs, editorAttrs);
  6308. return changedContent || changedEditor;
  6309. });
  6310. this.editorAttrs = editorAttrs;
  6311. this.contentAttrs = contentAttrs;
  6312. return changed;
  6313. }
  6314. showAnnouncements(trs) {
  6315. let first = true;
  6316. for (let tr of trs)
  6317. for (let effect of tr.effects)
  6318. if (effect.is(EditorView.announce)) {
  6319. if (first)
  6320. this.announceDOM.textContent = "";
  6321. first = false;
  6322. let div = this.announceDOM.appendChild(document.createElement("div"));
  6323. div.textContent = effect.value;
  6324. }
  6325. }
  6326. mountStyles() {
  6327. this.styleModules = this.state.facet(styleModule);
  6328. styleMod.StyleModule.mount(this.root, this.styleModules.concat(baseTheme$1).reverse());
  6329. }
  6330. readMeasured() {
  6331. if (this.updateState == 2 /* Updating */)
  6332. throw new Error("Reading the editor layout isn't allowed during an update");
  6333. if (this.updateState == 0 /* Idle */ && this.measureScheduled > -1)
  6334. this.measure(false);
  6335. }
  6336. /**
  6337. Schedule a layout measurement, optionally providing callbacks to
  6338. do custom DOM measuring followed by a DOM write phase. Using
  6339. this is preferable reading DOM layout directly from, for
  6340. example, an event handler, because it'll make sure measuring and
  6341. drawing done by other components is synchronized, avoiding
  6342. unnecessary DOM layout computations.
  6343. */
  6344. requestMeasure(request) {
  6345. if (this.measureScheduled < 0)
  6346. this.measureScheduled = requestAnimationFrame(() => this.measure());
  6347. if (request) {
  6348. if (request.key != null)
  6349. for (let i = 0; i < this.measureRequests.length; i++) {
  6350. if (this.measureRequests[i].key === request.key) {
  6351. this.measureRequests[i] = request;
  6352. return;
  6353. }
  6354. }
  6355. this.measureRequests.push(request);
  6356. }
  6357. }
  6358. /**
  6359. Get the value of a specific plugin, if present. Note that
  6360. plugins that crash can be dropped from a view, so even when you
  6361. know you registered a given plugin, it is recommended to check
  6362. the return value of this method.
  6363. */
  6364. plugin(plugin) {
  6365. let known = this.pluginMap.get(plugin);
  6366. if (known === undefined || known && known.spec != plugin)
  6367. this.pluginMap.set(plugin, known = this.plugins.find(p => p.spec == plugin) || null);
  6368. return known && known.update(this).value;
  6369. }
  6370. /**
  6371. The top position of the document, in screen coordinates. This
  6372. may be negative when the editor is scrolled down. Points
  6373. directly to the top of the first line, not above the padding.
  6374. */
  6375. get documentTop() {
  6376. return this.contentDOM.getBoundingClientRect().top + this.viewState.paddingTop;
  6377. }
  6378. /**
  6379. Reports the padding above and below the document.
  6380. */
  6381. get documentPadding() {
  6382. return { top: this.viewState.paddingTop, bottom: this.viewState.paddingBottom };
  6383. }
  6384. /**
  6385. Find the text line or block widget at the given vertical
  6386. position (which is interpreted as relative to the [top of the
  6387. document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
  6388. */
  6389. elementAtHeight(height) {
  6390. this.readMeasured();
  6391. return this.viewState.elementAtHeight(height);
  6392. }
  6393. /**
  6394. Find the line block (see
  6395. [`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
  6396. height.
  6397. */
  6398. lineBlockAtHeight(height) {
  6399. this.readMeasured();
  6400. return this.viewState.lineBlockAtHeight(height);
  6401. }
  6402. /**
  6403. Get the extent and vertical position of all [line
  6404. blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
  6405. are relative to the [top of the
  6406. document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop);
  6407. */
  6408. get viewportLineBlocks() {
  6409. return this.viewState.viewportLines;
  6410. }
  6411. /**
  6412. Find the line block around the given document position. A line
  6413. block is a range delimited on both sides by either a
  6414. non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^replace) line breaks, or the
  6415. start/end of the document. It will usually just hold a line of
  6416. text, but may be broken into multiple textblocks by block
  6417. widgets.
  6418. */
  6419. lineBlockAt(pos) {
  6420. return this.viewState.lineBlockAt(pos);
  6421. }
  6422. /**
  6423. The editor's total content height.
  6424. */
  6425. get contentHeight() {
  6426. return this.viewState.contentHeight;
  6427. }
  6428. /**
  6429. Move a cursor position by [grapheme
  6430. cluster](https://codemirror.net/6/docs/ref/#state.findClusterBreak). `forward` determines whether
  6431. the motion is away from the line start, or towards it. In
  6432. bidirectional text, the line is traversed in visual order, using
  6433. the editor's [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
  6434. When the start position was the last one on the line, the
  6435. returned position will be across the line break. If there is no
  6436. further line, the original position is returned.
  6437. By default, this method moves over a single cluster. The
  6438. optional `by` argument can be used to move across more. It will
  6439. be called with the first cluster as argument, and should return
  6440. a predicate that determines, for each subsequent cluster,
  6441. whether it should also be moved over.
  6442. */
  6443. moveByChar(start, forward, by) {
  6444. return skipAtoms(this, start, moveByChar(this, start, forward, by));
  6445. }
  6446. /**
  6447. Move a cursor position across the next group of either
  6448. [letters](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer) or non-letter
  6449. non-whitespace characters.
  6450. */
  6451. moveByGroup(start, forward) {
  6452. return skipAtoms(this, start, moveByChar(this, start, forward, initial => byGroup(this, start.head, initial)));
  6453. }
  6454. /**
  6455. Move to the next line boundary in the given direction. If
  6456. `includeWrap` is true, line wrapping is on, and there is a
  6457. further wrap point on the current line, the wrap point will be
  6458. returned. Otherwise this function will return the start or end
  6459. of the line.
  6460. */
  6461. moveToLineBoundary(start, forward, includeWrap = true) {
  6462. return moveToLineBoundary(this, start, forward, includeWrap);
  6463. }
  6464. /**
  6465. Move a cursor position vertically. When `distance` isn't given,
  6466. it defaults to moving to the next line (including wrapped
  6467. lines). Otherwise, `distance` should provide a positive distance
  6468. in pixels.
  6469. When `start` has a
  6470. [`goalColumn`](https://codemirror.net/6/docs/ref/#state.SelectionRange.goalColumn), the vertical
  6471. motion will use that as a target horizontal position. Otherwise,
  6472. the cursor's own horizontal position is used. The returned
  6473. cursor will have its goal column set to whichever column was
  6474. used.
  6475. */
  6476. moveVertically(start, forward, distance) {
  6477. return skipAtoms(this, start, moveVertically(this, start, forward, distance));
  6478. }
  6479. /**
  6480. Find the DOM parent node and offset (child offset if `node` is
  6481. an element, character offset when it is a text node) at the
  6482. given document position.
  6483. Note that for positions that aren't currently in
  6484. `visibleRanges`, the resulting DOM position isn't necessarily
  6485. meaningful (it may just point before or after a placeholder
  6486. element).
  6487. */
  6488. domAtPos(pos) {
  6489. return this.docView.domAtPos(pos);
  6490. }
  6491. /**
  6492. Find the document position at the given DOM node. Can be useful
  6493. for associating positions with DOM events. Will raise an error
  6494. when `node` isn't part of the editor content.
  6495. */
  6496. posAtDOM(node, offset = 0) {
  6497. return this.docView.posFromDOM(node, offset);
  6498. }
  6499. posAtCoords(coords, precise = true) {
  6500. this.readMeasured();
  6501. return posAtCoords(this, coords, precise);
  6502. }
  6503. /**
  6504. Get the screen coordinates at the given document position.
  6505. `side` determines whether the coordinates are based on the
  6506. element before (-1) or after (1) the position (if no element is
  6507. available on the given side, the method will transparently use
  6508. another strategy to get reasonable coordinates).
  6509. */
  6510. coordsAtPos(pos, side = 1) {
  6511. this.readMeasured();
  6512. let rect = this.docView.coordsAt(pos, side);
  6513. if (!rect || rect.left == rect.right)
  6514. return rect;
  6515. let line = this.state.doc.lineAt(pos), order = this.bidiSpans(line);
  6516. let span = order[BidiSpan.find(order, pos - line.from, -1, side)];
  6517. return flattenRect(rect, (span.dir == exports.Direction.LTR) == (side > 0));
  6518. }
  6519. /**
  6520. The default width of a character in the editor. May not
  6521. accurately reflect the width of all characters (given variable
  6522. width fonts or styling of invididual ranges).
  6523. */
  6524. get defaultCharacterWidth() { return this.viewState.heightOracle.charWidth; }
  6525. /**
  6526. The default height of a line in the editor. May not be accurate
  6527. for all lines.
  6528. */
  6529. get defaultLineHeight() { return this.viewState.heightOracle.lineHeight; }
  6530. /**
  6531. The text direction
  6532. ([`direction`](https://developer.mozilla.org/en-US/docs/Web/CSS/direction)
  6533. CSS property) of the editor's content element.
  6534. */
  6535. get textDirection() { return this.viewState.defaultTextDirection; }
  6536. /**
  6537. Find the text direction of the block at the given position, as
  6538. assigned by CSS. If
  6539. [`perLineTextDirection`](https://codemirror.net/6/docs/ref/#view.EditorView^perLineTextDirection)
  6540. isn't enabled, or the given position is outside of the viewport,
  6541. this will always return the same as
  6542. [`textDirection`](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection). Note that
  6543. this may trigger a DOM layout.
  6544. */
  6545. textDirectionAt(pos) {
  6546. let perLine = this.state.facet(perLineTextDirection);
  6547. if (!perLine || pos < this.viewport.from || pos > this.viewport.to)
  6548. return this.textDirection;
  6549. this.readMeasured();
  6550. return this.docView.textDirectionAt(pos);
  6551. }
  6552. /**
  6553. Whether this editor [wraps lines](https://codemirror.net/6/docs/ref/#view.EditorView.lineWrapping)
  6554. (as determined by the
  6555. [`white-space`](https://developer.mozilla.org/en-US/docs/Web/CSS/white-space)
  6556. CSS property of its content element).
  6557. */
  6558. get lineWrapping() { return this.viewState.heightOracle.lineWrapping; }
  6559. /**
  6560. Returns the bidirectional text structure of the given line
  6561. (which should be in the current document) as an array of span
  6562. objects. The order of these spans matches the [text
  6563. direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection)—if that is
  6564. left-to-right, the leftmost spans come first, otherwise the
  6565. rightmost spans come first.
  6566. */
  6567. bidiSpans(line) {
  6568. if (line.length > MaxBidiLine)
  6569. return trivialOrder(line.length);
  6570. let dir = this.textDirectionAt(line.from);
  6571. for (let entry of this.bidiCache)
  6572. if (entry.from == line.from && entry.dir == dir)
  6573. return entry.order;
  6574. let order = computeOrder(line.text, dir);
  6575. this.bidiCache.push(new CachedOrder(line.from, line.to, dir, order));
  6576. return order;
  6577. }
  6578. /**
  6579. Check whether the editor has focus.
  6580. */
  6581. get hasFocus() {
  6582. var _a;
  6583. // Safari return false for hasFocus when the context menu is open
  6584. // or closing, which leads us to ignore selection changes from the
  6585. // context menu because it looks like the editor isn't focused.
  6586. // This kludges around that.
  6587. return (document.hasFocus() || browser.safari && ((_a = this.inputState) === null || _a === void 0 ? void 0 : _a.lastContextMenu) > Date.now() - 3e4) &&
  6588. this.root.activeElement == this.contentDOM;
  6589. }
  6590. /**
  6591. Put focus on the editor.
  6592. */
  6593. focus() {
  6594. this.observer.ignore(() => {
  6595. focusPreventScroll(this.contentDOM);
  6596. this.docView.updateSelection();
  6597. });
  6598. }
  6599. /**
  6600. Clean up this editor view, removing its element from the
  6601. document, unregistering event handlers, and notifying
  6602. plugins. The view instance can no longer be used after
  6603. calling this.
  6604. */
  6605. destroy() {
  6606. for (let plugin of this.plugins)
  6607. plugin.destroy(this);
  6608. this.plugins = [];
  6609. this.inputState.destroy();
  6610. this.dom.remove();
  6611. this.observer.destroy();
  6612. if (this.measureScheduled > -1)
  6613. cancelAnimationFrame(this.measureScheduled);
  6614. this.destroyed = true;
  6615. }
  6616. /**
  6617. Returns an effect that can be
  6618. [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a transaction to
  6619. cause it to scroll the given position or range into view.
  6620. */
  6621. static scrollIntoView(pos, options = {}) {
  6622. return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? state.EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
  6623. }
  6624. /**
  6625. Returns an extension that can be used to add DOM event handlers.
  6626. The value should be an object mapping event names to handler
  6627. functions. For any given event, such functions are ordered by
  6628. extension precedence, and the first handler to return true will
  6629. be assumed to have handled that event, and no other handlers or
  6630. built-in behavior will be activated for it. These are registered
  6631. on the [content element](https://codemirror.net/6/docs/ref/#view.EditorView.contentDOM), except
  6632. for `scroll` handlers, which will be called any time the
  6633. editor's [scroll element](https://codemirror.net/6/docs/ref/#view.EditorView.scrollDOM) or one of
  6634. its parent nodes is scrolled.
  6635. */
  6636. static domEventHandlers(handlers) {
  6637. return ViewPlugin.define(() => ({}), { eventHandlers: handlers });
  6638. }
  6639. /**
  6640. Create a theme extension. The first argument can be a
  6641. [`style-mod`](https://github.com/marijnh/style-mod#documentation)
  6642. style spec providing the styles for the theme. These will be
  6643. prefixed with a generated class for the style.
  6644. Because the selectors will be prefixed with a scope class, rule
  6645. that directly match the editor's [wrapper
  6646. element](https://codemirror.net/6/docs/ref/#view.EditorView.dom)—to which the scope class will be
  6647. added—need to be explicitly differentiated by adding an `&` to
  6648. the selector for that element—for example
  6649. `&.cm-focused`.
  6650. When `dark` is set to true, the theme will be marked as dark,
  6651. which will cause the `&dark` rules from [base
  6652. themes](https://codemirror.net/6/docs/ref/#view.EditorView^baseTheme) to be used (as opposed to
  6653. `&light` when a light theme is active).
  6654. */
  6655. static theme(spec, options) {
  6656. let prefix = styleMod.StyleModule.newName();
  6657. let result = [theme.of(prefix), styleModule.of(buildTheme(`.${prefix}`, spec))];
  6658. if (options && options.dark)
  6659. result.push(darkTheme.of(true));
  6660. return result;
  6661. }
  6662. /**
  6663. Create an extension that adds styles to the base theme. Like
  6664. with [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme), use `&` to indicate the
  6665. place of the editor wrapper element when directly targeting
  6666. that. You can also use `&dark` or `&light` instead to only
  6667. target editors with a dark or light theme.
  6668. */
  6669. static baseTheme(spec) {
  6670. return state.Prec.lowest(styleModule.of(buildTheme("." + baseThemeID, spec, lightDarkIDs)));
  6671. }
  6672. /**
  6673. Retrieve an editor view instance from the view's DOM
  6674. representation.
  6675. */
  6676. static findFromDOM(dom) {
  6677. var _a;
  6678. let content = dom.querySelector(".cm-content");
  6679. let cView = content && ContentView.get(content) || ContentView.get(dom);
  6680. return ((_a = cView === null || cView === void 0 ? void 0 : cView.rootView) === null || _a === void 0 ? void 0 : _a.view) || null;
  6681. }
  6682. }
  6683. /**
  6684. Facet to add a [style
  6685. module](https://github.com/marijnh/style-mod#documentation) to
  6686. an editor view. The view will ensure that the module is
  6687. mounted in its [document
  6688. root](https://codemirror.net/6/docs/ref/#view.EditorView.constructor^config.root).
  6689. */
  6690. EditorView.styleModule = styleModule;
  6691. /**
  6692. An input handler can override the way changes to the editable
  6693. DOM content are handled. Handlers are passed the document
  6694. positions between which the change was found, and the new
  6695. content. When one returns true, no further input handlers are
  6696. called and the default behavior is prevented.
  6697. */
  6698. EditorView.inputHandler = inputHandler;
  6699. /**
  6700. By default, the editor assumes all its content has the same
  6701. [text direction](https://codemirror.net/6/docs/ref/#view.Direction). Configure this with a `true`
  6702. value to make it read the text direction of every (rendered)
  6703. line separately.
  6704. */
  6705. EditorView.perLineTextDirection = perLineTextDirection;
  6706. /**
  6707. Allows you to provide a function that should be called when the
  6708. library catches an exception from an extension (mostly from view
  6709. plugins, but may be used by other extensions to route exceptions
  6710. from user-code-provided callbacks). This is mostly useful for
  6711. debugging and logging. See [`logException`](https://codemirror.net/6/docs/ref/#view.logException).
  6712. */
  6713. EditorView.exceptionSink = exceptionSink;
  6714. /**
  6715. A facet that can be used to register a function to be called
  6716. every time the view updates.
  6717. */
  6718. EditorView.updateListener = updateListener;
  6719. /**
  6720. Facet that controls whether the editor content DOM is editable.
  6721. When its highest-precedence value is `false`, the element will
  6722. not have its `contenteditable` attribute set. (Note that this
  6723. doesn't affect API calls that change the editor content, even
  6724. when those are bound to keys or buttons. See the
  6725. [`readOnly`](https://codemirror.net/6/docs/ref/#state.EditorState.readOnly) facet for that.)
  6726. */
  6727. EditorView.editable = editable;
  6728. /**
  6729. Allows you to influence the way mouse selection happens. The
  6730. functions in this facet will be called for a `mousedown` event
  6731. on the editor, and can return an object that overrides the way a
  6732. selection is computed from that mouse click or drag.
  6733. */
  6734. EditorView.mouseSelectionStyle = mouseSelectionStyle;
  6735. /**
  6736. Facet used to configure whether a given selection drag event
  6737. should move or copy the selection. The given predicate will be
  6738. called with the `mousedown` event, and can return `true` when
  6739. the drag should move the content.
  6740. */
  6741. EditorView.dragMovesSelection = dragMovesSelection$1;
  6742. /**
  6743. Facet used to configure whether a given selecting click adds a
  6744. new range to the existing selection or replaces it entirely. The
  6745. default behavior is to check `event.metaKey` on macOS, and
  6746. `event.ctrlKey` elsewhere.
  6747. */
  6748. EditorView.clickAddsSelectionRange = clickAddsSelectionRange;
  6749. /**
  6750. A facet that determines which [decorations](https://codemirror.net/6/docs/ref/#view.Decoration)
  6751. are shown in the view. Decorations can be provided in two
  6752. ways—directly, or via a function that takes an editor view.
  6753. Only decoration sets provided directly are allowed to influence
  6754. the editor's vertical layout structure. The ones provided as
  6755. functions are called _after_ the new viewport has been computed,
  6756. and thus **must not** introduce block widgets or replacing
  6757. decorations that cover line breaks.
  6758. */
  6759. EditorView.decorations = decorations;
  6760. /**
  6761. Used to provide ranges that should be treated as atoms as far as
  6762. cursor motion is concerned. This causes methods like
  6763. [`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and
  6764. [`moveVertically`](https://codemirror.net/6/docs/ref/#view.EditorView.moveVertically) (and the
  6765. commands built on top of them) to skip across such regions when
  6766. a selection endpoint would enter them. This does _not_ prevent
  6767. direct programmatic [selection
  6768. updates](https://codemirror.net/6/docs/ref/#state.TransactionSpec.selection) from moving into such
  6769. regions.
  6770. */
  6771. EditorView.atomicRanges = atomicRanges;
  6772. /**
  6773. Facet that allows extensions to provide additional scroll
  6774. margins (space around the sides of the scrolling element that
  6775. should be considered invisible). This can be useful when the
  6776. plugin introduces elements that cover part of that element (for
  6777. example a horizontally fixed gutter).
  6778. */
  6779. EditorView.scrollMargins = scrollMargins;
  6780. /**
  6781. This facet records whether a dark theme is active. The extension
  6782. returned by [`theme`](https://codemirror.net/6/docs/ref/#view.EditorView^theme) automatically
  6783. includes an instance of this when the `dark` option is set to
  6784. true.
  6785. */
  6786. EditorView.darkTheme = darkTheme;
  6787. /**
  6788. Facet that provides additional DOM attributes for the editor's
  6789. editable DOM element.
  6790. */
  6791. EditorView.contentAttributes = contentAttributes;
  6792. /**
  6793. Facet that provides DOM attributes for the editor's outer
  6794. element.
  6795. */
  6796. EditorView.editorAttributes = editorAttributes;
  6797. /**
  6798. An extension that enables line wrapping in the editor (by
  6799. setting CSS `white-space` to `pre-wrap` in the content).
  6800. */
  6801. EditorView.lineWrapping = EditorView.contentAttributes.of({ "class": "cm-lineWrapping" });
  6802. /**
  6803. State effect used to include screen reader announcements in a
  6804. transaction. These will be added to the DOM in a visually hidden
  6805. element with `aria-live="polite"` set, and should be used to
  6806. describe effects that are visually obvious but may not be
  6807. noticed by screen reader users (such as moving to the next
  6808. search match).
  6809. */
  6810. EditorView.announce = state.StateEffect.define();
  6811. // Maximum line length for which we compute accurate bidi info
  6812. const MaxBidiLine = 4096;
  6813. const BadMeasure = {};
  6814. class CachedOrder {
  6815. constructor(from, to, dir, order) {
  6816. this.from = from;
  6817. this.to = to;
  6818. this.dir = dir;
  6819. this.order = order;
  6820. }
  6821. static update(cache, changes) {
  6822. if (changes.empty)
  6823. return cache;
  6824. let result = [], lastDir = cache.length ? cache[cache.length - 1].dir : exports.Direction.LTR;
  6825. for (let i = Math.max(0, cache.length - 10); i < cache.length; i++) {
  6826. let entry = cache[i];
  6827. if (entry.dir == lastDir && !changes.touchesRange(entry.from, entry.to))
  6828. result.push(new CachedOrder(changes.mapPos(entry.from, 1), changes.mapPos(entry.to, -1), entry.dir, entry.order));
  6829. }
  6830. return result;
  6831. }
  6832. }
  6833. function attrsFromFacet(view, facet, base) {
  6834. for (let sources = view.state.facet(facet), i = sources.length - 1; i >= 0; i--) {
  6835. let source = sources[i], value = typeof source == "function" ? source(view) : source;
  6836. if (value)
  6837. combineAttrs(value, base);
  6838. }
  6839. return base;
  6840. }
  6841. const currentPlatform = browser.mac ? "mac" : browser.windows ? "win" : browser.linux ? "linux" : "key";
  6842. function normalizeKeyName(name, platform) {
  6843. const parts = name.split(/-(?!$)/);
  6844. let result = parts[parts.length - 1];
  6845. if (result == "Space")
  6846. result = " ";
  6847. let alt, ctrl, shift, meta;
  6848. for (let i = 0; i < parts.length - 1; ++i) {
  6849. const mod = parts[i];
  6850. if (/^(cmd|meta|m)$/i.test(mod))
  6851. meta = true;
  6852. else if (/^a(lt)?$/i.test(mod))
  6853. alt = true;
  6854. else if (/^(c|ctrl|control)$/i.test(mod))
  6855. ctrl = true;
  6856. else if (/^s(hift)?$/i.test(mod))
  6857. shift = true;
  6858. else if (/^mod$/i.test(mod)) {
  6859. if (platform == "mac")
  6860. meta = true;
  6861. else
  6862. ctrl = true;
  6863. }
  6864. else
  6865. throw new Error("Unrecognized modifier name: " + mod);
  6866. }
  6867. if (alt)
  6868. result = "Alt-" + result;
  6869. if (ctrl)
  6870. result = "Ctrl-" + result;
  6871. if (meta)
  6872. result = "Meta-" + result;
  6873. if (shift)
  6874. result = "Shift-" + result;
  6875. return result;
  6876. }
  6877. function modifiers(name, event, shift) {
  6878. if (event.altKey)
  6879. name = "Alt-" + name;
  6880. if (event.ctrlKey)
  6881. name = "Ctrl-" + name;
  6882. if (event.metaKey)
  6883. name = "Meta-" + name;
  6884. if (shift !== false && event.shiftKey)
  6885. name = "Shift-" + name;
  6886. return name;
  6887. }
  6888. const handleKeyEvents = state.Prec.default(EditorView.domEventHandlers({
  6889. keydown(event, view) {
  6890. return runHandlers(getKeymap(view.state), event, view, "editor");
  6891. }
  6892. }));
  6893. /**
  6894. Facet used for registering keymaps.
  6895. You can add multiple keymaps to an editor. Their priorities
  6896. determine their precedence (the ones specified early or with high
  6897. priority get checked first). When a handler has returned `true`
  6898. for a given key, no further handlers are called.
  6899. */
  6900. const keymap = state.Facet.define({ enables: handleKeyEvents });
  6901. const Keymaps = new WeakMap();
  6902. // This is hidden behind an indirection, rather than directly computed
  6903. // by the facet, to keep internal types out of the facet's type.
  6904. function getKeymap(state) {
  6905. let bindings = state.facet(keymap);
  6906. let map = Keymaps.get(bindings);
  6907. if (!map)
  6908. Keymaps.set(bindings, map = buildKeymap(bindings.reduce((a, b) => a.concat(b), [])));
  6909. return map;
  6910. }
  6911. /**
  6912. Run the key handlers registered for a given scope. The event
  6913. object should be a `"keydown"` event. Returns true if any of the
  6914. handlers handled it.
  6915. */
  6916. function runScopeHandlers(view, event, scope) {
  6917. return runHandlers(getKeymap(view.state), event, view, scope);
  6918. }
  6919. let storedPrefix = null;
  6920. const PrefixTimeout = 4000;
  6921. function buildKeymap(bindings, platform = currentPlatform) {
  6922. let bound = Object.create(null);
  6923. let isPrefix = Object.create(null);
  6924. let checkPrefix = (name, is) => {
  6925. let current = isPrefix[name];
  6926. if (current == null)
  6927. isPrefix[name] = is;
  6928. else if (current != is)
  6929. throw new Error("Key binding " + name + " is used both as a regular binding and as a multi-stroke prefix");
  6930. };
  6931. let add = (scope, key, command, preventDefault) => {
  6932. let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
  6933. let parts = key.split(/ (?!$)/).map(k => normalizeKeyName(k, platform));
  6934. for (let i = 1; i < parts.length; i++) {
  6935. let prefix = parts.slice(0, i).join(" ");
  6936. checkPrefix(prefix, true);
  6937. if (!scopeObj[prefix])
  6938. scopeObj[prefix] = {
  6939. preventDefault: true,
  6940. commands: [(view) => {
  6941. let ourObj = storedPrefix = { view, prefix, scope };
  6942. setTimeout(() => { if (storedPrefix == ourObj)
  6943. storedPrefix = null; }, PrefixTimeout);
  6944. return true;
  6945. }]
  6946. };
  6947. }
  6948. let full = parts.join(" ");
  6949. checkPrefix(full, false);
  6950. let binding = scopeObj[full] || (scopeObj[full] = { preventDefault: false, commands: [] });
  6951. binding.commands.push(command);
  6952. if (preventDefault)
  6953. binding.preventDefault = true;
  6954. };
  6955. for (let b of bindings) {
  6956. let name = b[platform] || b.key;
  6957. if (!name)
  6958. continue;
  6959. for (let scope of b.scope ? b.scope.split(" ") : ["editor"]) {
  6960. add(scope, name, b.run, b.preventDefault);
  6961. if (b.shift)
  6962. add(scope, "Shift-" + name, b.shift, b.preventDefault);
  6963. }
  6964. }
  6965. return bound;
  6966. }
  6967. function runHandlers(map, event, view, scope) {
  6968. let name = w3cKeyname.keyName(event);
  6969. let charCode = state.codePointAt(name, 0), isChar = state.codePointSize(charCode) == name.length && name != " ";
  6970. let prefix = "", fallthrough = false;
  6971. if (storedPrefix && storedPrefix.view == view && storedPrefix.scope == scope) {
  6972. prefix = storedPrefix.prefix + " ";
  6973. if (fallthrough = modifierCodes.indexOf(event.keyCode) < 0)
  6974. storedPrefix = null;
  6975. }
  6976. let runFor = (binding) => {
  6977. if (binding) {
  6978. for (let cmd of binding.commands)
  6979. if (cmd(view))
  6980. return true;
  6981. if (binding.preventDefault)
  6982. fallthrough = true;
  6983. }
  6984. return false;
  6985. };
  6986. let scopeObj = map[scope], baseName;
  6987. if (scopeObj) {
  6988. if (runFor(scopeObj[prefix + modifiers(name, event, !isChar)]))
  6989. return true;
  6990. if (isChar && (event.shiftKey || event.altKey || event.metaKey || charCode > 127) &&
  6991. (baseName = w3cKeyname.base[event.keyCode]) && baseName != name) {
  6992. if (runFor(scopeObj[prefix + modifiers(baseName, event, true)]))
  6993. return true;
  6994. else if (event.shiftKey && w3cKeyname.shift[event.keyCode] != baseName &&
  6995. runFor(scopeObj[prefix + modifiers(w3cKeyname.shift[event.keyCode], event, false)]))
  6996. return true;
  6997. }
  6998. else if (isChar && event.shiftKey) {
  6999. if (runFor(scopeObj[prefix + modifiers(name, event, true)]))
  7000. return true;
  7001. }
  7002. }
  7003. return fallthrough;
  7004. }
  7005. const CanHidePrimary = !browser.ios; // FIXME test IE
  7006. const selectionConfig = state.Facet.define({
  7007. combine(configs) {
  7008. return state.combineConfig(configs, {
  7009. cursorBlinkRate: 1200,
  7010. drawRangeCursor: true
  7011. }, {
  7012. cursorBlinkRate: (a, b) => Math.min(a, b),
  7013. drawRangeCursor: (a, b) => a || b
  7014. });
  7015. }
  7016. });
  7017. /**
  7018. Returns an extension that hides the browser's native selection and
  7019. cursor, replacing the selection with a background behind the text
  7020. (with the `cm-selectionBackground` class), and the
  7021. cursors with elements overlaid over the code (using
  7022. `cm-cursor-primary` and `cm-cursor-secondary`).
  7023. This allows the editor to display secondary selection ranges, and
  7024. tends to produce a type of selection more in line with that users
  7025. expect in a text editor (the native selection styling will often
  7026. leave gaps between lines and won't fill the horizontal space after
  7027. a line when the selection continues past it).
  7028. It does have a performance cost, in that it requires an extra DOM
  7029. layout cycle for many updates (the selection is drawn based on DOM
  7030. layout information that's only available after laying out the
  7031. content).
  7032. */
  7033. function drawSelection(config = {}) {
  7034. return [
  7035. selectionConfig.of(config),
  7036. drawSelectionPlugin,
  7037. hideNativeSelection
  7038. ];
  7039. }
  7040. class Piece {
  7041. constructor(left, top, width, height, className) {
  7042. this.left = left;
  7043. this.top = top;
  7044. this.width = width;
  7045. this.height = height;
  7046. this.className = className;
  7047. }
  7048. draw() {
  7049. let elt = document.createElement("div");
  7050. elt.className = this.className;
  7051. this.adjust(elt);
  7052. return elt;
  7053. }
  7054. adjust(elt) {
  7055. elt.style.left = this.left + "px";
  7056. elt.style.top = this.top + "px";
  7057. if (this.width >= 0)
  7058. elt.style.width = this.width + "px";
  7059. elt.style.height = this.height + "px";
  7060. }
  7061. eq(p) {
  7062. return this.left == p.left && this.top == p.top && this.width == p.width && this.height == p.height &&
  7063. this.className == p.className;
  7064. }
  7065. }
  7066. const drawSelectionPlugin = ViewPlugin.fromClass(class {
  7067. constructor(view) {
  7068. this.view = view;
  7069. this.rangePieces = [];
  7070. this.cursors = [];
  7071. this.measureReq = { read: this.readPos.bind(this), write: this.drawSel.bind(this) };
  7072. this.selectionLayer = view.scrollDOM.appendChild(document.createElement("div"));
  7073. this.selectionLayer.className = "cm-selectionLayer";
  7074. this.selectionLayer.setAttribute("aria-hidden", "true");
  7075. this.cursorLayer = view.scrollDOM.appendChild(document.createElement("div"));
  7076. this.cursorLayer.className = "cm-cursorLayer";
  7077. this.cursorLayer.setAttribute("aria-hidden", "true");
  7078. view.requestMeasure(this.measureReq);
  7079. this.setBlinkRate();
  7080. }
  7081. setBlinkRate() {
  7082. this.cursorLayer.style.animationDuration = this.view.state.facet(selectionConfig).cursorBlinkRate + "ms";
  7083. }
  7084. update(update) {
  7085. let confChanged = update.startState.facet(selectionConfig) != update.state.facet(selectionConfig);
  7086. if (confChanged || update.selectionSet || update.geometryChanged || update.viewportChanged)
  7087. this.view.requestMeasure(this.measureReq);
  7088. if (update.transactions.some(tr => tr.scrollIntoView))
  7089. this.cursorLayer.style.animationName = this.cursorLayer.style.animationName == "cm-blink" ? "cm-blink2" : "cm-blink";
  7090. if (confChanged)
  7091. this.setBlinkRate();
  7092. }
  7093. readPos() {
  7094. let { state } = this.view, conf = state.facet(selectionConfig);
  7095. let rangePieces = state.selection.ranges.map(r => r.empty ? [] : measureRange(this.view, r)).reduce((a, b) => a.concat(b));
  7096. let cursors = [];
  7097. for (let r of state.selection.ranges) {
  7098. let prim = r == state.selection.main;
  7099. if (r.empty ? !prim || CanHidePrimary : conf.drawRangeCursor) {
  7100. let piece = measureCursor(this.view, r, prim);
  7101. if (piece)
  7102. cursors.push(piece);
  7103. }
  7104. }
  7105. return { rangePieces, cursors };
  7106. }
  7107. drawSel({ rangePieces, cursors }) {
  7108. if (rangePieces.length != this.rangePieces.length || rangePieces.some((p, i) => !p.eq(this.rangePieces[i]))) {
  7109. this.selectionLayer.textContent = "";
  7110. for (let p of rangePieces)
  7111. this.selectionLayer.appendChild(p.draw());
  7112. this.rangePieces = rangePieces;
  7113. }
  7114. if (cursors.length != this.cursors.length || cursors.some((c, i) => !c.eq(this.cursors[i]))) {
  7115. let oldCursors = this.cursorLayer.children;
  7116. if (oldCursors.length !== cursors.length) {
  7117. this.cursorLayer.textContent = "";
  7118. for (const c of cursors)
  7119. this.cursorLayer.appendChild(c.draw());
  7120. }
  7121. else {
  7122. cursors.forEach((c, idx) => c.adjust(oldCursors[idx]));
  7123. }
  7124. this.cursors = cursors;
  7125. }
  7126. }
  7127. destroy() {
  7128. this.selectionLayer.remove();
  7129. this.cursorLayer.remove();
  7130. }
  7131. });
  7132. const themeSpec = {
  7133. ".cm-line": {
  7134. "& ::selection": { backgroundColor: "transparent !important" },
  7135. "&::selection": { backgroundColor: "transparent !important" }
  7136. }
  7137. };
  7138. if (CanHidePrimary)
  7139. themeSpec[".cm-line"].caretColor = "transparent !important";
  7140. const hideNativeSelection = state.Prec.highest(EditorView.theme(themeSpec));
  7141. function getBase(view) {
  7142. let rect = view.scrollDOM.getBoundingClientRect();
  7143. let left = view.textDirection == exports.Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth;
  7144. return { left: left - view.scrollDOM.scrollLeft, top: rect.top - view.scrollDOM.scrollTop };
  7145. }
  7146. function wrappedLine(view, pos, inside) {
  7147. let range = state.EditorSelection.cursor(pos);
  7148. return { from: Math.max(inside.from, view.moveToLineBoundary(range, false, true).from),
  7149. to: Math.min(inside.to, view.moveToLineBoundary(range, true, true).from),
  7150. type: exports.BlockType.Text };
  7151. }
  7152. function blockAt(view, pos) {
  7153. let line = view.lineBlockAt(pos);
  7154. if (Array.isArray(line.type))
  7155. for (let l of line.type) {
  7156. if (l.to > pos || l.to == pos && (l.to == line.to || l.type == exports.BlockType.Text))
  7157. return l;
  7158. }
  7159. return line;
  7160. }
  7161. function measureRange(view, range) {
  7162. if (range.to <= view.viewport.from || range.from >= view.viewport.to)
  7163. return [];
  7164. let from = Math.max(range.from, view.viewport.from), to = Math.min(range.to, view.viewport.to);
  7165. let ltr = view.textDirection == exports.Direction.LTR;
  7166. let content = view.contentDOM, contentRect = content.getBoundingClientRect(), base = getBase(view);
  7167. let lineStyle = window.getComputedStyle(content.firstChild);
  7168. let leftSide = contentRect.left + parseInt(lineStyle.paddingLeft) + Math.min(0, parseInt(lineStyle.textIndent));
  7169. let rightSide = contentRect.right - parseInt(lineStyle.paddingRight);
  7170. let startBlock = blockAt(view, from), endBlock = blockAt(view, to);
  7171. let visualStart = startBlock.type == exports.BlockType.Text ? startBlock : null;
  7172. let visualEnd = endBlock.type == exports.BlockType.Text ? endBlock : null;
  7173. if (view.lineWrapping) {
  7174. if (visualStart)
  7175. visualStart = wrappedLine(view, from, visualStart);
  7176. if (visualEnd)
  7177. visualEnd = wrappedLine(view, to, visualEnd);
  7178. }
  7179. if (visualStart && visualEnd && visualStart.from == visualEnd.from) {
  7180. return pieces(drawForLine(range.from, range.to, visualStart));
  7181. }
  7182. else {
  7183. let top = visualStart ? drawForLine(range.from, null, visualStart) : drawForWidget(startBlock, false);
  7184. let bottom = visualEnd ? drawForLine(null, range.to, visualEnd) : drawForWidget(endBlock, true);
  7185. let between = [];
  7186. if ((visualStart || startBlock).to < (visualEnd || endBlock).from - 1)
  7187. between.push(piece(leftSide, top.bottom, rightSide, bottom.top));
  7188. else if (top.bottom < bottom.top && view.elementAtHeight((top.bottom + bottom.top) / 2).type == exports.BlockType.Text)
  7189. top.bottom = bottom.top = (top.bottom + bottom.top) / 2;
  7190. return pieces(top).concat(between).concat(pieces(bottom));
  7191. }
  7192. function piece(left, top, right, bottom) {
  7193. return new Piece(left - base.left, top - base.top - 0.01 /* Epsilon */, right - left, bottom - top + 0.01 /* Epsilon */, "cm-selectionBackground");
  7194. }
  7195. function pieces({ top, bottom, horizontal }) {
  7196. let pieces = [];
  7197. for (let i = 0; i < horizontal.length; i += 2)
  7198. pieces.push(piece(horizontal[i], top, horizontal[i + 1], bottom));
  7199. return pieces;
  7200. }
  7201. // Gets passed from/to in line-local positions
  7202. function drawForLine(from, to, line) {
  7203. let top = 1e9, bottom = -1e9, horizontal = [];
  7204. function addSpan(from, fromOpen, to, toOpen, dir) {
  7205. // Passing 2/-2 is a kludge to force the view to return
  7206. // coordinates on the proper side of block widgets, since
  7207. // normalizing the side there, though appropriate for most
  7208. // coordsAtPos queries, would break selection drawing.
  7209. let fromCoords = view.coordsAtPos(from, (from == line.to ? -2 : 2));
  7210. let toCoords = view.coordsAtPos(to, (to == line.from ? 2 : -2));
  7211. top = Math.min(fromCoords.top, toCoords.top, top);
  7212. bottom = Math.max(fromCoords.bottom, toCoords.bottom, bottom);
  7213. if (dir == exports.Direction.LTR)
  7214. horizontal.push(ltr && fromOpen ? leftSide : fromCoords.left, ltr && toOpen ? rightSide : toCoords.right);
  7215. else
  7216. horizontal.push(!ltr && toOpen ? leftSide : toCoords.left, !ltr && fromOpen ? rightSide : fromCoords.right);
  7217. }
  7218. let start = from !== null && from !== void 0 ? from : line.from, end = to !== null && to !== void 0 ? to : line.to;
  7219. // Split the range by visible range and document line
  7220. for (let r of view.visibleRanges)
  7221. if (r.to > start && r.from < end) {
  7222. for (let pos = Math.max(r.from, start), endPos = Math.min(r.to, end);;) {
  7223. let docLine = view.state.doc.lineAt(pos);
  7224. for (let span of view.bidiSpans(docLine)) {
  7225. let spanFrom = span.from + docLine.from, spanTo = span.to + docLine.from;
  7226. if (spanFrom >= endPos)
  7227. break;
  7228. if (spanTo > pos)
  7229. addSpan(Math.max(spanFrom, pos), from == null && spanFrom <= start, Math.min(spanTo, endPos), to == null && spanTo >= end, span.dir);
  7230. }
  7231. pos = docLine.to + 1;
  7232. if (pos >= endPos)
  7233. break;
  7234. }
  7235. }
  7236. if (horizontal.length == 0)
  7237. addSpan(start, from == null, end, to == null, view.textDirection);
  7238. return { top, bottom, horizontal };
  7239. }
  7240. function drawForWidget(block, top) {
  7241. let y = contentRect.top + (top ? block.top : block.bottom);
  7242. return { top: y, bottom: y, horizontal: [] };
  7243. }
  7244. }
  7245. function measureCursor(view, cursor, primary) {
  7246. let pos = view.coordsAtPos(cursor.head, cursor.assoc || 1);
  7247. if (!pos)
  7248. return null;
  7249. let base = getBase(view);
  7250. return new Piece(pos.left - base.left, pos.top - base.top, -1, pos.bottom - pos.top, primary ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary");
  7251. }
  7252. const setDropCursorPos = state.StateEffect.define({
  7253. map(pos, mapping) { return pos == null ? null : mapping.mapPos(pos); }
  7254. });
  7255. const dropCursorPos = state.StateField.define({
  7256. create() { return null; },
  7257. update(pos, tr) {
  7258. if (pos != null)
  7259. pos = tr.changes.mapPos(pos);
  7260. return tr.effects.reduce((pos, e) => e.is(setDropCursorPos) ? e.value : pos, pos);
  7261. }
  7262. });
  7263. const drawDropCursor = ViewPlugin.fromClass(class {
  7264. constructor(view) {
  7265. this.view = view;
  7266. this.cursor = null;
  7267. this.measureReq = { read: this.readPos.bind(this), write: this.drawCursor.bind(this) };
  7268. }
  7269. update(update) {
  7270. var _a;
  7271. let cursorPos = update.state.field(dropCursorPos);
  7272. if (cursorPos == null) {
  7273. if (this.cursor != null) {
  7274. (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.remove();
  7275. this.cursor = null;
  7276. }
  7277. }
  7278. else {
  7279. if (!this.cursor) {
  7280. this.cursor = this.view.scrollDOM.appendChild(document.createElement("div"));
  7281. this.cursor.className = "cm-dropCursor";
  7282. }
  7283. if (update.startState.field(dropCursorPos) != cursorPos || update.docChanged || update.geometryChanged)
  7284. this.view.requestMeasure(this.measureReq);
  7285. }
  7286. }
  7287. readPos() {
  7288. let pos = this.view.state.field(dropCursorPos);
  7289. let rect = pos != null && this.view.coordsAtPos(pos);
  7290. if (!rect)
  7291. return null;
  7292. let outer = this.view.scrollDOM.getBoundingClientRect();
  7293. return {
  7294. left: rect.left - outer.left + this.view.scrollDOM.scrollLeft,
  7295. top: rect.top - outer.top + this.view.scrollDOM.scrollTop,
  7296. height: rect.bottom - rect.top
  7297. };
  7298. }
  7299. drawCursor(pos) {
  7300. if (this.cursor) {
  7301. if (pos) {
  7302. this.cursor.style.left = pos.left + "px";
  7303. this.cursor.style.top = pos.top + "px";
  7304. this.cursor.style.height = pos.height + "px";
  7305. }
  7306. else {
  7307. this.cursor.style.left = "-100000px";
  7308. }
  7309. }
  7310. }
  7311. destroy() {
  7312. if (this.cursor)
  7313. this.cursor.remove();
  7314. }
  7315. setDropPos(pos) {
  7316. if (this.view.state.field(dropCursorPos) != pos)
  7317. this.view.dispatch({ effects: setDropCursorPos.of(pos) });
  7318. }
  7319. }, {
  7320. eventHandlers: {
  7321. dragover(event) {
  7322. this.setDropPos(this.view.posAtCoords({ x: event.clientX, y: event.clientY }));
  7323. },
  7324. dragleave(event) {
  7325. if (event.target == this.view.contentDOM || !this.view.contentDOM.contains(event.relatedTarget))
  7326. this.setDropPos(null);
  7327. },
  7328. dragend() {
  7329. this.setDropPos(null);
  7330. },
  7331. drop() {
  7332. this.setDropPos(null);
  7333. }
  7334. }
  7335. });
  7336. /**
  7337. Draws a cursor at the current drop position when something is
  7338. dragged over the editor.
  7339. */
  7340. function dropCursor() {
  7341. return [dropCursorPos, drawDropCursor];
  7342. }
  7343. function iterMatches(doc, re, from, to, f) {
  7344. re.lastIndex = 0;
  7345. for (let cursor = doc.iterRange(from, to), pos = from, m; !cursor.next().done; pos += cursor.value.length) {
  7346. if (!cursor.lineBreak)
  7347. while (m = re.exec(cursor.value))
  7348. f(pos + m.index, pos + m.index + m[0].length, m);
  7349. }
  7350. }
  7351. function matchRanges(view, maxLength) {
  7352. let visible = view.visibleRanges;
  7353. if (visible.length == 1 && visible[0].from == view.viewport.from &&
  7354. visible[0].to == view.viewport.to)
  7355. return visible;
  7356. let result = [];
  7357. for (let { from, to } of visible) {
  7358. from = Math.max(view.state.doc.lineAt(from).from, from - maxLength);
  7359. to = Math.min(view.state.doc.lineAt(to).to, to + maxLength);
  7360. if (result.length && result[result.length - 1].to >= from)
  7361. result[result.length - 1].to = to;
  7362. else
  7363. result.push({ from, to });
  7364. }
  7365. return result;
  7366. }
  7367. /**
  7368. Helper class used to make it easier to maintain decorations on
  7369. visible code that matches a given regular expression. To be used
  7370. in a [view plugin](https://codemirror.net/6/docs/ref/#view.ViewPlugin). Instances of this object
  7371. represent a matching configuration.
  7372. */
  7373. class MatchDecorator {
  7374. /**
  7375. Create a decorator.
  7376. */
  7377. constructor(config) {
  7378. let { regexp, decoration, boundary, maxLength = 1000 } = config;
  7379. if (!regexp.global)
  7380. throw new RangeError("The regular expression given to MatchDecorator should have its 'g' flag set");
  7381. this.regexp = regexp;
  7382. this.getDeco = typeof decoration == "function" ? decoration : () => decoration;
  7383. this.boundary = boundary;
  7384. this.maxLength = maxLength;
  7385. }
  7386. /**
  7387. Compute the full set of decorations for matches in the given
  7388. view's viewport. You'll want to call this when initializing your
  7389. plugin.
  7390. */
  7391. createDeco(view) {
  7392. let build = new state.RangeSetBuilder();
  7393. for (let { from, to } of matchRanges(view, this.maxLength))
  7394. iterMatches(view.state.doc, this.regexp, from, to, (a, b, m) => build.add(a, b, this.getDeco(m, view, a)));
  7395. return build.finish();
  7396. }
  7397. /**
  7398. Update a set of decorations for a view update. `deco` _must_ be
  7399. the set of decorations produced by _this_ `MatchDecorator` for
  7400. the view state before the update.
  7401. */
  7402. updateDeco(update, deco) {
  7403. let changeFrom = 1e9, changeTo = -1;
  7404. if (update.docChanged)
  7405. update.changes.iterChanges((_f, _t, from, to) => {
  7406. if (to > update.view.viewport.from && from < update.view.viewport.to) {
  7407. changeFrom = Math.min(from, changeFrom);
  7408. changeTo = Math.max(to, changeTo);
  7409. }
  7410. });
  7411. if (update.viewportChanged || changeTo - changeFrom > 1000)
  7412. return this.createDeco(update.view);
  7413. if (changeTo > -1)
  7414. return this.updateRange(update.view, deco.map(update.changes), changeFrom, changeTo);
  7415. return deco;
  7416. }
  7417. updateRange(view, deco, updateFrom, updateTo) {
  7418. for (let r of view.visibleRanges) {
  7419. let from = Math.max(r.from, updateFrom), to = Math.min(r.to, updateTo);
  7420. if (to > from) {
  7421. let fromLine = view.state.doc.lineAt(from), toLine = fromLine.to < to ? view.state.doc.lineAt(to) : fromLine;
  7422. let start = Math.max(r.from, fromLine.from), end = Math.min(r.to, toLine.to);
  7423. if (this.boundary) {
  7424. for (; from > fromLine.from; from--)
  7425. if (this.boundary.test(fromLine.text[from - 1 - fromLine.from])) {
  7426. start = from;
  7427. break;
  7428. }
  7429. for (; to < toLine.to; to++)
  7430. if (this.boundary.test(toLine.text[to - toLine.from])) {
  7431. end = to;
  7432. break;
  7433. }
  7434. }
  7435. let ranges = [], m;
  7436. if (fromLine == toLine) {
  7437. this.regexp.lastIndex = start - fromLine.from;
  7438. while ((m = this.regexp.exec(fromLine.text)) && m.index < end - fromLine.from) {
  7439. let pos = m.index + fromLine.from;
  7440. ranges.push(this.getDeco(m, view, pos).range(pos, pos + m[0].length));
  7441. }
  7442. }
  7443. else {
  7444. iterMatches(view.state.doc, this.regexp, start, end, (from, to, m) => ranges.push(this.getDeco(m, view, from).range(from, to)));
  7445. }
  7446. deco = deco.update({ filterFrom: start, filterTo: end, filter: (from, to) => from < start || to > end, add: ranges });
  7447. }
  7448. }
  7449. return deco;
  7450. }
  7451. }
  7452. const UnicodeRegexpSupport = /x/.unicode != null ? "gu" : "g";
  7453. const Specials = new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
  7454. const Names = {
  7455. 0: "null",
  7456. 7: "bell",
  7457. 8: "backspace",
  7458. 10: "newline",
  7459. 11: "vertical tab",
  7460. 13: "carriage return",
  7461. 27: "escape",
  7462. 8203: "zero width space",
  7463. 8204: "zero width non-joiner",
  7464. 8205: "zero width joiner",
  7465. 8206: "left-to-right mark",
  7466. 8207: "right-to-left mark",
  7467. 8232: "line separator",
  7468. 8237: "left-to-right override",
  7469. 8238: "right-to-left override",
  7470. 8233: "paragraph separator",
  7471. 65279: "zero width no-break space",
  7472. 65532: "object replacement"
  7473. };
  7474. let _supportsTabSize = null;
  7475. function supportsTabSize() {
  7476. var _a;
  7477. if (_supportsTabSize == null && typeof document != "undefined" && document.body) {
  7478. let styles = document.body.style;
  7479. _supportsTabSize = ((_a = styles.tabSize) !== null && _a !== void 0 ? _a : styles.MozTabSize) != null;
  7480. }
  7481. return _supportsTabSize || false;
  7482. }
  7483. const specialCharConfig = state.Facet.define({
  7484. combine(configs) {
  7485. let config = state.combineConfig(configs, {
  7486. render: null,
  7487. specialChars: Specials,
  7488. addSpecialChars: null
  7489. });
  7490. if (config.replaceTabs = !supportsTabSize())
  7491. config.specialChars = new RegExp("\t|" + config.specialChars.source, UnicodeRegexpSupport);
  7492. if (config.addSpecialChars)
  7493. config.specialChars = new RegExp(config.specialChars.source + "|" + config.addSpecialChars.source, UnicodeRegexpSupport);
  7494. return config;
  7495. }
  7496. });
  7497. /**
  7498. Returns an extension that installs highlighting of special
  7499. characters.
  7500. */
  7501. function highlightSpecialChars(
  7502. /**
  7503. Configuration options.
  7504. */
  7505. config = {}) {
  7506. return [specialCharConfig.of(config), specialCharPlugin()];
  7507. }
  7508. let _plugin = null;
  7509. function specialCharPlugin() {
  7510. return _plugin || (_plugin = ViewPlugin.fromClass(class {
  7511. constructor(view) {
  7512. this.view = view;
  7513. this.decorations = Decoration.none;
  7514. this.decorationCache = Object.create(null);
  7515. this.decorator = this.makeDecorator(view.state.facet(specialCharConfig));
  7516. this.decorations = this.decorator.createDeco(view);
  7517. }
  7518. makeDecorator(conf) {
  7519. return new MatchDecorator({
  7520. regexp: conf.specialChars,
  7521. decoration: (m, view, pos) => {
  7522. let { doc } = view.state;
  7523. let code = state.codePointAt(m[0], 0);
  7524. if (code == 9) {
  7525. let line = doc.lineAt(pos);
  7526. let size = view.state.tabSize, col = state.countColumn(line.text, size, pos - line.from);
  7527. return Decoration.replace({ widget: new TabWidget((size - (col % size)) * this.view.defaultCharacterWidth) });
  7528. }
  7529. return this.decorationCache[code] ||
  7530. (this.decorationCache[code] = Decoration.replace({ widget: new SpecialCharWidget(conf, code) }));
  7531. },
  7532. boundary: conf.replaceTabs ? undefined : /[^]/
  7533. });
  7534. }
  7535. update(update) {
  7536. let conf = update.state.facet(specialCharConfig);
  7537. if (update.startState.facet(specialCharConfig) != conf) {
  7538. this.decorator = this.makeDecorator(conf);
  7539. this.decorations = this.decorator.createDeco(update.view);
  7540. }
  7541. else {
  7542. this.decorations = this.decorator.updateDeco(update, this.decorations);
  7543. }
  7544. }
  7545. }, {
  7546. decorations: v => v.decorations
  7547. }));
  7548. }
  7549. const DefaultPlaceholder = "\u2022";
  7550. // Assigns placeholder characters from the Control Pictures block to
  7551. // ASCII control characters
  7552. function placeholder$1(code) {
  7553. if (code >= 32)
  7554. return DefaultPlaceholder;
  7555. if (code == 10)
  7556. return "\u2424";
  7557. return String.fromCharCode(9216 + code);
  7558. }
  7559. class SpecialCharWidget extends WidgetType {
  7560. constructor(options, code) {
  7561. super();
  7562. this.options = options;
  7563. this.code = code;
  7564. }
  7565. eq(other) { return other.code == this.code; }
  7566. toDOM(view) {
  7567. let ph = placeholder$1(this.code);
  7568. let desc = view.state.phrase("Control character") + " " + (Names[this.code] || "0x" + this.code.toString(16));
  7569. let custom = this.options.render && this.options.render(this.code, desc, ph);
  7570. if (custom)
  7571. return custom;
  7572. let span = document.createElement("span");
  7573. span.textContent = ph;
  7574. span.title = desc;
  7575. span.setAttribute("aria-label", desc);
  7576. span.className = "cm-specialChar";
  7577. return span;
  7578. }
  7579. ignoreEvent() { return false; }
  7580. }
  7581. class TabWidget extends WidgetType {
  7582. constructor(width) {
  7583. super();
  7584. this.width = width;
  7585. }
  7586. eq(other) { return other.width == this.width; }
  7587. toDOM() {
  7588. let span = document.createElement("span");
  7589. span.textContent = "\t";
  7590. span.className = "cm-tab";
  7591. span.style.width = this.width + "px";
  7592. return span;
  7593. }
  7594. ignoreEvent() { return false; }
  7595. }
  7596. const plugin = ViewPlugin.fromClass(class {
  7597. constructor() {
  7598. this.height = 1000;
  7599. this.attrs = { style: "padding-bottom: 1000px" };
  7600. }
  7601. update(update) {
  7602. let height = update.view.viewState.editorHeight - update.view.defaultLineHeight;
  7603. if (height != this.height) {
  7604. this.height = height;
  7605. this.attrs = { style: `padding-bottom: ${height}px` };
  7606. }
  7607. }
  7608. });
  7609. /**
  7610. Returns an extension that makes sure the content has a bottom
  7611. margin equivalent to the height of the editor, minus one line
  7612. height, so that every line in the document can be scrolled to the
  7613. top of the editor.
  7614. This is only meaningful when the editor is scrollable, and should
  7615. not be enabled in editors that take the size of their content.
  7616. */
  7617. function scrollPastEnd() {
  7618. return [plugin, contentAttributes.of(view => { var _a; return ((_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.attrs) || null; })];
  7619. }
  7620. /**
  7621. Mark lines that have a cursor on them with the `"cm-activeLine"`
  7622. DOM class.
  7623. */
  7624. function highlightActiveLine() {
  7625. return activeLineHighlighter;
  7626. }
  7627. const lineDeco = Decoration.line({ class: "cm-activeLine" });
  7628. const activeLineHighlighter = ViewPlugin.fromClass(class {
  7629. constructor(view) {
  7630. this.decorations = this.getDeco(view);
  7631. }
  7632. update(update) {
  7633. if (update.docChanged || update.selectionSet)
  7634. this.decorations = this.getDeco(update.view);
  7635. }
  7636. getDeco(view) {
  7637. let lastLineStart = -1, deco = [];
  7638. for (let r of view.state.selection.ranges) {
  7639. if (!r.empty)
  7640. return Decoration.none;
  7641. let line = view.lineBlockAt(r.head);
  7642. if (line.from > lastLineStart) {
  7643. deco.push(lineDeco.range(line.from));
  7644. lastLineStart = line.from;
  7645. }
  7646. }
  7647. return Decoration.set(deco);
  7648. }
  7649. }, {
  7650. decorations: v => v.decorations
  7651. });
  7652. class Placeholder extends WidgetType {
  7653. constructor(content) {
  7654. super();
  7655. this.content = content;
  7656. }
  7657. toDOM() {
  7658. let wrap = document.createElement("span");
  7659. wrap.className = "cm-placeholder";
  7660. wrap.style.pointerEvents = "none";
  7661. wrap.appendChild(typeof this.content == "string" ? document.createTextNode(this.content) : this.content);
  7662. if (typeof this.content == "string")
  7663. wrap.setAttribute("aria-label", "placeholder " + this.content);
  7664. else
  7665. wrap.setAttribute("aria-hidden", "true");
  7666. return wrap;
  7667. }
  7668. ignoreEvent() { return false; }
  7669. }
  7670. /**
  7671. Extension that enables a placeholder—a piece of example content
  7672. to show when the editor is empty.
  7673. */
  7674. function placeholder(content) {
  7675. return ViewPlugin.fromClass(class {
  7676. constructor(view) {
  7677. this.view = view;
  7678. this.placeholder = Decoration.set([Decoration.widget({ widget: new Placeholder(content), side: 1 }).range(0)]);
  7679. }
  7680. get decorations() { return this.view.state.doc.length ? Decoration.none : this.placeholder; }
  7681. }, { decorations: v => v.decorations });
  7682. }
  7683. // Don't compute precise column positions for line offsets above this
  7684. // (since it could get expensive). Assume offset==column for them.
  7685. const MaxOff = 2000;
  7686. function rectangleFor(state$1, a, b) {
  7687. let startLine = Math.min(a.line, b.line), endLine = Math.max(a.line, b.line);
  7688. let ranges = [];
  7689. if (a.off > MaxOff || b.off > MaxOff || a.col < 0 || b.col < 0) {
  7690. let startOff = Math.min(a.off, b.off), endOff = Math.max(a.off, b.off);
  7691. for (let i = startLine; i <= endLine; i++) {
  7692. let line = state$1.doc.line(i);
  7693. if (line.length <= endOff)
  7694. ranges.push(state.EditorSelection.range(line.from + startOff, line.to + endOff));
  7695. }
  7696. }
  7697. else {
  7698. let startCol = Math.min(a.col, b.col), endCol = Math.max(a.col, b.col);
  7699. for (let i = startLine; i <= endLine; i++) {
  7700. let line = state$1.doc.line(i);
  7701. let start = state.findColumn(line.text, startCol, state$1.tabSize, true);
  7702. if (start > -1) {
  7703. let end = state.findColumn(line.text, endCol, state$1.tabSize);
  7704. ranges.push(state.EditorSelection.range(line.from + start, line.from + end));
  7705. }
  7706. }
  7707. }
  7708. return ranges;
  7709. }
  7710. function absoluteColumn(view, x) {
  7711. let ref = view.coordsAtPos(view.viewport.from);
  7712. return ref ? Math.round(Math.abs((ref.left - x) / view.defaultCharacterWidth)) : -1;
  7713. }
  7714. function getPos(view, event) {
  7715. let offset = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
  7716. let line = view.state.doc.lineAt(offset), off = offset - line.from;
  7717. let col = off > MaxOff ? -1
  7718. : off == line.length ? absoluteColumn(view, event.clientX)
  7719. : state.countColumn(line.text, view.state.tabSize, offset - line.from);
  7720. return { line: line.number, col, off };
  7721. }
  7722. function rectangleSelectionStyle(view, event) {
  7723. let start = getPos(view, event), startSel = view.state.selection;
  7724. if (!start)
  7725. return null;
  7726. return {
  7727. update(update) {
  7728. if (update.docChanged) {
  7729. let newStart = update.changes.mapPos(update.startState.doc.line(start.line).from);
  7730. let newLine = update.state.doc.lineAt(newStart);
  7731. start = { line: newLine.number, col: start.col, off: Math.min(start.off, newLine.length) };
  7732. startSel = startSel.map(update.changes);
  7733. }
  7734. },
  7735. get(event, _extend, multiple) {
  7736. let cur = getPos(view, event);
  7737. if (!cur)
  7738. return startSel;
  7739. let ranges = rectangleFor(view.state, start, cur);
  7740. if (!ranges.length)
  7741. return startSel;
  7742. if (multiple)
  7743. return state.EditorSelection.create(ranges.concat(startSel.ranges));
  7744. else
  7745. return state.EditorSelection.create(ranges);
  7746. }
  7747. };
  7748. }
  7749. /**
  7750. Create an extension that enables rectangular selections. By
  7751. default, it will react to left mouse drag with the Alt key held
  7752. down. When such a selection occurs, the text within the rectangle
  7753. that was dragged over will be selected, as one selection
  7754. [range](https://codemirror.net/6/docs/ref/#state.SelectionRange) per line.
  7755. */
  7756. function rectangularSelection(options) {
  7757. let filter = (options === null || options === void 0 ? void 0 : options.eventFilter) || (e => e.altKey && e.button == 0);
  7758. return EditorView.mouseSelectionStyle.of((view, event) => filter(event) ? rectangleSelectionStyle(view, event) : null);
  7759. }
  7760. const keys = {
  7761. Alt: [18, e => e.altKey],
  7762. Control: [17, e => e.ctrlKey],
  7763. Shift: [16, e => e.shiftKey],
  7764. Meta: [91, e => e.metaKey]
  7765. };
  7766. const showCrosshair = { style: "cursor: crosshair" };
  7767. /**
  7768. Returns an extension that turns the pointer cursor into a
  7769. crosshair when a given modifier key, defaulting to Alt, is held
  7770. down. Can serve as a visual hint that rectangular selection is
  7771. going to happen when paired with
  7772. [`rectangularSelection`](https://codemirror.net/6/docs/ref/#view.rectangularSelection).
  7773. */
  7774. function crosshairCursor(options = {}) {
  7775. let [code, getter] = keys[options.key || "Alt"];
  7776. let plugin = ViewPlugin.fromClass(class {
  7777. constructor(view) {
  7778. this.view = view;
  7779. this.isDown = false;
  7780. }
  7781. set(isDown) {
  7782. if (this.isDown != isDown) {
  7783. this.isDown = isDown;
  7784. this.view.update([]);
  7785. }
  7786. }
  7787. }, {
  7788. eventHandlers: {
  7789. keydown(e) {
  7790. this.set(e.keyCode == code || getter(e));
  7791. },
  7792. keyup(e) {
  7793. if (e.keyCode == code || !getter(e))
  7794. this.set(false);
  7795. }
  7796. }
  7797. });
  7798. return [
  7799. plugin,
  7800. EditorView.contentAttributes.of(view => { var _a; return ((_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.isDown) ? showCrosshair : null; })
  7801. ];
  7802. }
  7803. const Outside = "-10000px";
  7804. class TooltipViewManager {
  7805. constructor(view, facet, createTooltipView) {
  7806. this.facet = facet;
  7807. this.createTooltipView = createTooltipView;
  7808. this.input = view.state.facet(facet);
  7809. this.tooltips = this.input.filter(t => t);
  7810. this.tooltipViews = this.tooltips.map(createTooltipView);
  7811. }
  7812. update(update) {
  7813. let input = update.state.facet(this.facet);
  7814. let tooltips = input.filter(x => x);
  7815. if (input === this.input) {
  7816. for (let t of this.tooltipViews)
  7817. if (t.update)
  7818. t.update(update);
  7819. return false;
  7820. }
  7821. let tooltipViews = [];
  7822. for (let i = 0; i < tooltips.length; i++) {
  7823. let tip = tooltips[i], known = -1;
  7824. if (!tip)
  7825. continue;
  7826. for (let i = 0; i < this.tooltips.length; i++) {
  7827. let other = this.tooltips[i];
  7828. if (other && other.create == tip.create)
  7829. known = i;
  7830. }
  7831. if (known < 0) {
  7832. tooltipViews[i] = this.createTooltipView(tip);
  7833. }
  7834. else {
  7835. let tooltipView = tooltipViews[i] = this.tooltipViews[known];
  7836. if (tooltipView.update)
  7837. tooltipView.update(update);
  7838. }
  7839. }
  7840. for (let t of this.tooltipViews)
  7841. if (tooltipViews.indexOf(t) < 0)
  7842. t.dom.remove();
  7843. this.input = input;
  7844. this.tooltips = tooltips;
  7845. this.tooltipViews = tooltipViews;
  7846. return true;
  7847. }
  7848. }
  7849. /**
  7850. Creates an extension that configures tooltip behavior.
  7851. */
  7852. function tooltips(config = {}) {
  7853. return tooltipConfig.of(config);
  7854. }
  7855. function windowSpace() {
  7856. return { top: 0, left: 0, bottom: innerHeight, right: innerWidth };
  7857. }
  7858. const tooltipConfig = state.Facet.define({
  7859. combine: values => {
  7860. var _a, _b, _c;
  7861. return ({
  7862. position: browser.ios ? "absolute" : ((_a = values.find(conf => conf.position)) === null || _a === void 0 ? void 0 : _a.position) || "fixed",
  7863. parent: ((_b = values.find(conf => conf.parent)) === null || _b === void 0 ? void 0 : _b.parent) || null,
  7864. tooltipSpace: ((_c = values.find(conf => conf.tooltipSpace)) === null || _c === void 0 ? void 0 : _c.tooltipSpace) || windowSpace,
  7865. });
  7866. }
  7867. });
  7868. const tooltipPlugin = ViewPlugin.fromClass(class {
  7869. constructor(view) {
  7870. var _a;
  7871. this.view = view;
  7872. this.inView = true;
  7873. this.lastTransaction = 0;
  7874. this.measureTimeout = -1;
  7875. let config = view.state.facet(tooltipConfig);
  7876. this.position = config.position;
  7877. this.parent = config.parent;
  7878. this.classes = view.themeClasses;
  7879. this.createContainer();
  7880. this.measureReq = { read: this.readMeasure.bind(this), write: this.writeMeasure.bind(this), key: this };
  7881. this.manager = new TooltipViewManager(view, showTooltip, t => this.createTooltip(t));
  7882. this.intersectionObserver = typeof IntersectionObserver == "function" ? new IntersectionObserver(entries => {
  7883. if (Date.now() > this.lastTransaction - 50 &&
  7884. entries.length > 0 && entries[entries.length - 1].intersectionRatio < 1)
  7885. this.measureSoon();
  7886. }, { threshold: [1] }) : null;
  7887. this.observeIntersection();
  7888. (_a = view.dom.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.addEventListener("resize", this.measureSoon = this.measureSoon.bind(this));
  7889. this.maybeMeasure();
  7890. }
  7891. createContainer() {
  7892. if (this.parent) {
  7893. this.container = document.createElement("div");
  7894. this.container.style.position = "relative";
  7895. this.container.className = this.view.themeClasses;
  7896. this.parent.appendChild(this.container);
  7897. }
  7898. else {
  7899. this.container = this.view.dom;
  7900. }
  7901. }
  7902. observeIntersection() {
  7903. if (this.intersectionObserver) {
  7904. this.intersectionObserver.disconnect();
  7905. for (let tooltip of this.manager.tooltipViews)
  7906. this.intersectionObserver.observe(tooltip.dom);
  7907. }
  7908. }
  7909. measureSoon() {
  7910. if (this.measureTimeout < 0)
  7911. this.measureTimeout = setTimeout(() => {
  7912. this.measureTimeout = -1;
  7913. this.maybeMeasure();
  7914. }, 50);
  7915. }
  7916. update(update) {
  7917. if (update.transactions.length)
  7918. this.lastTransaction = Date.now();
  7919. let updated = this.manager.update(update);
  7920. if (updated)
  7921. this.observeIntersection();
  7922. let shouldMeasure = updated || update.geometryChanged;
  7923. let newConfig = update.state.facet(tooltipConfig);
  7924. if (newConfig.position != this.position) {
  7925. this.position = newConfig.position;
  7926. for (let t of this.manager.tooltipViews)
  7927. t.dom.style.position = this.position;
  7928. shouldMeasure = true;
  7929. }
  7930. if (newConfig.parent != this.parent) {
  7931. if (this.parent)
  7932. this.container.remove();
  7933. this.parent = newConfig.parent;
  7934. this.createContainer();
  7935. for (let t of this.manager.tooltipViews)
  7936. this.container.appendChild(t.dom);
  7937. shouldMeasure = true;
  7938. }
  7939. else if (this.parent && this.view.themeClasses != this.classes) {
  7940. this.classes = this.container.className = this.view.themeClasses;
  7941. }
  7942. if (shouldMeasure)
  7943. this.maybeMeasure();
  7944. }
  7945. createTooltip(tooltip) {
  7946. let tooltipView = tooltip.create(this.view);
  7947. tooltipView.dom.classList.add("cm-tooltip");
  7948. if (tooltip.arrow && !tooltipView.dom.querySelector(".cm-tooltip > .cm-tooltip-arrow")) {
  7949. let arrow = document.createElement("div");
  7950. arrow.className = "cm-tooltip-arrow";
  7951. tooltipView.dom.appendChild(arrow);
  7952. }
  7953. tooltipView.dom.style.position = this.position;
  7954. tooltipView.dom.style.top = Outside;
  7955. this.container.appendChild(tooltipView.dom);
  7956. if (tooltipView.mount)
  7957. tooltipView.mount(this.view);
  7958. return tooltipView;
  7959. }
  7960. destroy() {
  7961. var _a, _b;
  7962. (_a = this.view.dom.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.removeEventListener("resize", this.measureSoon);
  7963. for (let { dom } of this.manager.tooltipViews)
  7964. dom.remove();
  7965. (_b = this.intersectionObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
  7966. clearTimeout(this.measureTimeout);
  7967. }
  7968. readMeasure() {
  7969. let editor = this.view.dom.getBoundingClientRect();
  7970. return {
  7971. editor,
  7972. parent: this.parent ? this.container.getBoundingClientRect() : editor,
  7973. pos: this.manager.tooltips.map((t, i) => {
  7974. let tv = this.manager.tooltipViews[i];
  7975. return tv.getCoords ? tv.getCoords(t.pos) : this.view.coordsAtPos(t.pos);
  7976. }),
  7977. size: this.manager.tooltipViews.map(({ dom }) => dom.getBoundingClientRect()),
  7978. space: this.view.state.facet(tooltipConfig).tooltipSpace(this.view),
  7979. };
  7980. }
  7981. writeMeasure(measured) {
  7982. let { editor, space } = measured;
  7983. let others = [];
  7984. for (let i = 0; i < this.manager.tooltips.length; i++) {
  7985. let tooltip = this.manager.tooltips[i], tView = this.manager.tooltipViews[i], { dom } = tView;
  7986. let pos = measured.pos[i], size = measured.size[i];
  7987. // Hide tooltips that are outside of the editor.
  7988. if (!pos || pos.bottom <= Math.max(editor.top, space.top) ||
  7989. pos.top >= Math.min(editor.bottom, space.bottom) ||
  7990. pos.right < Math.max(editor.left, space.left) - .1 ||
  7991. pos.left > Math.min(editor.right, space.right) + .1) {
  7992. dom.style.top = Outside;
  7993. continue;
  7994. }
  7995. let arrow = tooltip.arrow ? tView.dom.querySelector(".cm-tooltip-arrow") : null;
  7996. let arrowHeight = arrow ? 7 /* Size */ : 0;
  7997. let width = size.right - size.left, height = size.bottom - size.top;
  7998. let offset = tView.offset || noOffset, ltr = this.view.textDirection == exports.Direction.LTR;
  7999. let left = size.width > space.right - space.left ? (ltr ? space.left : space.right - size.width)
  8000. : ltr ? Math.min(pos.left - (arrow ? 14 /* Offset */ : 0) + offset.x, space.right - width)
  8001. : Math.max(space.left, pos.left - width + (arrow ? 14 /* Offset */ : 0) - offset.x);
  8002. let above = !!tooltip.above;
  8003. if (!tooltip.strictSide && (above
  8004. ? pos.top - (size.bottom - size.top) - offset.y < space.top
  8005. : pos.bottom + (size.bottom - size.top) + offset.y > space.bottom) &&
  8006. above == (space.bottom - pos.bottom > pos.top - space.top))
  8007. above = !above;
  8008. let top = above ? pos.top - height - arrowHeight - offset.y : pos.bottom + arrowHeight + offset.y;
  8009. let right = left + width;
  8010. if (tView.overlap !== true)
  8011. for (let r of others)
  8012. if (r.left < right && r.right > left && r.top < top + height && r.bottom > top)
  8013. top = above ? r.top - height - 2 - arrowHeight : r.bottom + arrowHeight + 2;
  8014. if (this.position == "absolute") {
  8015. dom.style.top = (top - measured.parent.top) + "px";
  8016. dom.style.left = (left - measured.parent.left) + "px";
  8017. }
  8018. else {
  8019. dom.style.top = top + "px";
  8020. dom.style.left = left + "px";
  8021. }
  8022. if (arrow)
  8023. arrow.style.left = `${pos.left + (ltr ? offset.x : -offset.x) - (left + 14 /* Offset */ - 7 /* Size */)}px`;
  8024. if (tView.overlap !== true)
  8025. others.push({ left, top, right, bottom: top + height });
  8026. dom.classList.toggle("cm-tooltip-above", above);
  8027. dom.classList.toggle("cm-tooltip-below", !above);
  8028. if (tView.positioned)
  8029. tView.positioned();
  8030. }
  8031. }
  8032. maybeMeasure() {
  8033. if (this.manager.tooltips.length) {
  8034. if (this.view.inView)
  8035. this.view.requestMeasure(this.measureReq);
  8036. if (this.inView != this.view.inView) {
  8037. this.inView = this.view.inView;
  8038. if (!this.inView)
  8039. for (let tv of this.manager.tooltipViews)
  8040. tv.dom.style.top = Outside;
  8041. }
  8042. }
  8043. }
  8044. }, {
  8045. eventHandlers: {
  8046. scroll() { this.maybeMeasure(); }
  8047. }
  8048. });
  8049. const baseTheme = EditorView.baseTheme({
  8050. ".cm-tooltip": {
  8051. zIndex: 100
  8052. },
  8053. "&light .cm-tooltip": {
  8054. border: "1px solid #bbb",
  8055. backgroundColor: "#f5f5f5"
  8056. },
  8057. "&light .cm-tooltip-section:not(:first-child)": {
  8058. borderTop: "1px solid #bbb",
  8059. },
  8060. "&dark .cm-tooltip": {
  8061. backgroundColor: "#333338",
  8062. color: "white"
  8063. },
  8064. ".cm-tooltip-arrow": {
  8065. height: `${7 /* Size */}px`,
  8066. width: `${7 /* Size */ * 2}px`,
  8067. position: "absolute",
  8068. zIndex: -1,
  8069. overflow: "hidden",
  8070. "&:before, &:after": {
  8071. content: "''",
  8072. position: "absolute",
  8073. width: 0,
  8074. height: 0,
  8075. borderLeft: `${7 /* Size */}px solid transparent`,
  8076. borderRight: `${7 /* Size */}px solid transparent`,
  8077. },
  8078. ".cm-tooltip-above &": {
  8079. bottom: `-${7 /* Size */}px`,
  8080. "&:before": {
  8081. borderTop: `${7 /* Size */}px solid #bbb`,
  8082. },
  8083. "&:after": {
  8084. borderTop: `${7 /* Size */}px solid #f5f5f5`,
  8085. bottom: "1px"
  8086. }
  8087. },
  8088. ".cm-tooltip-below &": {
  8089. top: `-${7 /* Size */}px`,
  8090. "&:before": {
  8091. borderBottom: `${7 /* Size */}px solid #bbb`,
  8092. },
  8093. "&:after": {
  8094. borderBottom: `${7 /* Size */}px solid #f5f5f5`,
  8095. top: "1px"
  8096. }
  8097. },
  8098. },
  8099. "&dark .cm-tooltip .cm-tooltip-arrow": {
  8100. "&:before": {
  8101. borderTopColor: "#333338",
  8102. borderBottomColor: "#333338"
  8103. },
  8104. "&:after": {
  8105. borderTopColor: "transparent",
  8106. borderBottomColor: "transparent"
  8107. }
  8108. }
  8109. });
  8110. const noOffset = { x: 0, y: 0 };
  8111. /**
  8112. Facet to which an extension can add a value to show a tooltip.
  8113. */
  8114. const showTooltip = state.Facet.define({
  8115. enables: [tooltipPlugin, baseTheme]
  8116. });
  8117. const showHoverTooltip = state.Facet.define();
  8118. class HoverTooltipHost {
  8119. constructor(view) {
  8120. this.view = view;
  8121. this.mounted = false;
  8122. this.dom = document.createElement("div");
  8123. this.dom.classList.add("cm-tooltip-hover");
  8124. this.manager = new TooltipViewManager(view, showHoverTooltip, t => this.createHostedView(t));
  8125. }
  8126. // Needs to be static so that host tooltip instances always match
  8127. static create(view) {
  8128. return new HoverTooltipHost(view);
  8129. }
  8130. createHostedView(tooltip) {
  8131. let hostedView = tooltip.create(this.view);
  8132. hostedView.dom.classList.add("cm-tooltip-section");
  8133. this.dom.appendChild(hostedView.dom);
  8134. if (this.mounted && hostedView.mount)
  8135. hostedView.mount(this.view);
  8136. return hostedView;
  8137. }
  8138. mount(view) {
  8139. for (let hostedView of this.manager.tooltipViews) {
  8140. if (hostedView.mount)
  8141. hostedView.mount(view);
  8142. }
  8143. this.mounted = true;
  8144. }
  8145. positioned() {
  8146. for (let hostedView of this.manager.tooltipViews) {
  8147. if (hostedView.positioned)
  8148. hostedView.positioned();
  8149. }
  8150. }
  8151. update(update) {
  8152. this.manager.update(update);
  8153. }
  8154. }
  8155. const showHoverTooltipHost = showTooltip.compute([showHoverTooltip], state => {
  8156. let tooltips = state.facet(showHoverTooltip).filter(t => t);
  8157. if (tooltips.length === 0)
  8158. return null;
  8159. return {
  8160. pos: Math.min(...tooltips.map(t => t.pos)),
  8161. end: Math.max(...tooltips.filter(t => t.end != null).map(t => t.end)),
  8162. create: HoverTooltipHost.create,
  8163. above: tooltips[0].above,
  8164. arrow: tooltips.some(t => t.arrow),
  8165. };
  8166. });
  8167. class HoverPlugin {
  8168. constructor(view, source, field, setHover, hoverTime) {
  8169. this.view = view;
  8170. this.source = source;
  8171. this.field = field;
  8172. this.setHover = setHover;
  8173. this.hoverTime = hoverTime;
  8174. this.hoverTimeout = -1;
  8175. this.restartTimeout = -1;
  8176. this.pending = null;
  8177. this.lastMove = { x: 0, y: 0, target: view.dom, time: 0 };
  8178. this.checkHover = this.checkHover.bind(this);
  8179. view.dom.addEventListener("mouseleave", this.mouseleave = this.mouseleave.bind(this));
  8180. view.dom.addEventListener("mousemove", this.mousemove = this.mousemove.bind(this));
  8181. }
  8182. update() {
  8183. if (this.pending) {
  8184. this.pending = null;
  8185. clearTimeout(this.restartTimeout);
  8186. this.restartTimeout = setTimeout(() => this.startHover(), 20);
  8187. }
  8188. }
  8189. get active() {
  8190. return this.view.state.field(this.field);
  8191. }
  8192. checkHover() {
  8193. this.hoverTimeout = -1;
  8194. if (this.active)
  8195. return;
  8196. let hovered = Date.now() - this.lastMove.time;
  8197. if (hovered < this.hoverTime)
  8198. this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime - hovered);
  8199. else
  8200. this.startHover();
  8201. }
  8202. startHover() {
  8203. clearTimeout(this.restartTimeout);
  8204. let { lastMove } = this;
  8205. let pos = this.view.contentDOM.contains(lastMove.target) ? this.view.posAtCoords(lastMove) : null;
  8206. if (pos == null)
  8207. return;
  8208. let posCoords = this.view.coordsAtPos(pos);
  8209. if (posCoords == null || lastMove.y < posCoords.top || lastMove.y > posCoords.bottom ||
  8210. lastMove.x < posCoords.left - this.view.defaultCharacterWidth ||
  8211. lastMove.x > posCoords.right + this.view.defaultCharacterWidth)
  8212. return;
  8213. let bidi = this.view.bidiSpans(this.view.state.doc.lineAt(pos)).find(s => s.from <= pos && s.to >= pos);
  8214. let rtl = bidi && bidi.dir == exports.Direction.RTL ? -1 : 1;
  8215. let open = this.source(this.view, pos, (lastMove.x < posCoords.left ? -rtl : rtl));
  8216. if (open === null || open === void 0 ? void 0 : open.then) {
  8217. let pending = this.pending = { pos };
  8218. open.then(result => {
  8219. if (this.pending == pending) {
  8220. this.pending = null;
  8221. if (result)
  8222. this.view.dispatch({ effects: this.setHover.of(result) });
  8223. }
  8224. }, e => logException(this.view.state, e, "hover tooltip"));
  8225. }
  8226. else if (open) {
  8227. this.view.dispatch({ effects: this.setHover.of(open) });
  8228. }
  8229. }
  8230. mousemove(event) {
  8231. var _a;
  8232. this.lastMove = { x: event.clientX, y: event.clientY, target: event.target, time: Date.now() };
  8233. if (this.hoverTimeout < 0)
  8234. this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
  8235. let tooltip = this.active;
  8236. if (tooltip && !isInTooltip(this.lastMove.target) || this.pending) {
  8237. let { pos } = tooltip || this.pending, end = (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.end) !== null && _a !== void 0 ? _a : pos;
  8238. if ((pos == end ? this.view.posAtCoords(this.lastMove) != pos
  8239. : !isOverRange(this.view, pos, end, event.clientX, event.clientY, 6 /* MaxDist */))) {
  8240. this.view.dispatch({ effects: this.setHover.of(null) });
  8241. this.pending = null;
  8242. }
  8243. }
  8244. }
  8245. mouseleave() {
  8246. clearTimeout(this.hoverTimeout);
  8247. this.hoverTimeout = -1;
  8248. if (this.active)
  8249. this.view.dispatch({ effects: this.setHover.of(null) });
  8250. }
  8251. destroy() {
  8252. clearTimeout(this.hoverTimeout);
  8253. this.view.dom.removeEventListener("mouseleave", this.mouseleave);
  8254. this.view.dom.removeEventListener("mousemove", this.mousemove);
  8255. }
  8256. }
  8257. function isInTooltip(elt) {
  8258. for (let cur = elt; cur; cur = cur.parentNode)
  8259. if (cur.nodeType == 1 && cur.classList.contains("cm-tooltip"))
  8260. return true;
  8261. return false;
  8262. }
  8263. function isOverRange(view, from, to, x, y, margin) {
  8264. let range = document.createRange();
  8265. let fromDOM = view.domAtPos(from), toDOM = view.domAtPos(to);
  8266. range.setEnd(toDOM.node, toDOM.offset);
  8267. range.setStart(fromDOM.node, fromDOM.offset);
  8268. let rects = range.getClientRects();
  8269. range.detach();
  8270. for (let i = 0; i < rects.length; i++) {
  8271. let rect = rects[i];
  8272. let dist = Math.max(rect.top - y, y - rect.bottom, rect.left - x, x - rect.right);
  8273. if (dist <= margin)
  8274. return true;
  8275. }
  8276. return false;
  8277. }
  8278. /**
  8279. Set up a hover tooltip, which shows up when the pointer hovers
  8280. over ranges of text. The callback is called when the mouse hovers
  8281. over the document text. It should, if there is a tooltip
  8282. associated with position `pos`, return the tooltip description
  8283. (either directly or in a promise). The `side` argument indicates
  8284. on which side of the position the pointer is—it will be -1 if the
  8285. pointer is before the position, 1 if after the position.
  8286. Note that all hover tooltips are hosted within a single tooltip
  8287. container element. This allows multiple tooltips over the same
  8288. range to be "merged" together without overlapping.
  8289. */
  8290. function hoverTooltip(source, options = {}) {
  8291. let setHover = state.StateEffect.define();
  8292. let hoverState = state.StateField.define({
  8293. create() { return null; },
  8294. update(value, tr) {
  8295. if (value && (options.hideOnChange && (tr.docChanged || tr.selection) ||
  8296. options.hideOn && options.hideOn(tr, value)))
  8297. return null;
  8298. if (value && tr.docChanged) {
  8299. let newPos = tr.changes.mapPos(value.pos, -1, state.MapMode.TrackDel);
  8300. if (newPos == null)
  8301. return null;
  8302. let copy = Object.assign(Object.create(null), value);
  8303. copy.pos = newPos;
  8304. if (value.end != null)
  8305. copy.end = tr.changes.mapPos(value.end);
  8306. value = copy;
  8307. }
  8308. for (let effect of tr.effects) {
  8309. if (effect.is(setHover))
  8310. value = effect.value;
  8311. if (effect.is(closeHoverTooltipEffect))
  8312. value = null;
  8313. }
  8314. return value;
  8315. },
  8316. provide: f => showHoverTooltip.from(f)
  8317. });
  8318. return [
  8319. hoverState,
  8320. ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover, options.hoverTime || 300 /* Time */)),
  8321. showHoverTooltipHost
  8322. ];
  8323. }
  8324. /**
  8325. Get the active tooltip view for a given tooltip, if available.
  8326. */
  8327. function getTooltip(view, tooltip) {
  8328. let plugin = view.plugin(tooltipPlugin);
  8329. if (!plugin)
  8330. return null;
  8331. let found = plugin.manager.tooltips.indexOf(tooltip);
  8332. return found < 0 ? null : plugin.manager.tooltipViews[found];
  8333. }
  8334. /**
  8335. Returns true if any hover tooltips are currently active.
  8336. */
  8337. function hasHoverTooltips(state) {
  8338. return state.facet(showHoverTooltip).some(x => x);
  8339. }
  8340. const closeHoverTooltipEffect = state.StateEffect.define();
  8341. /**
  8342. Transaction effect that closes all hover tooltips.
  8343. */
  8344. const closeHoverTooltips = closeHoverTooltipEffect.of(null);
  8345. /**
  8346. Tell the tooltip extension to recompute the position of the active
  8347. tooltips. This can be useful when something happens (such as a
  8348. re-positioning or CSS change affecting the editor) that could
  8349. invalidate the existing tooltip positions.
  8350. */
  8351. function repositionTooltips(view) {
  8352. var _a;
  8353. (_a = view.plugin(tooltipPlugin)) === null || _a === void 0 ? void 0 : _a.maybeMeasure();
  8354. }
  8355. const panelConfig = state.Facet.define({
  8356. combine(configs) {
  8357. let topContainer, bottomContainer;
  8358. for (let c of configs) {
  8359. topContainer = topContainer || c.topContainer;
  8360. bottomContainer = bottomContainer || c.bottomContainer;
  8361. }
  8362. return { topContainer, bottomContainer };
  8363. }
  8364. });
  8365. /**
  8366. Configures the panel-managing extension.
  8367. */
  8368. function panels(config) {
  8369. return config ? [panelConfig.of(config)] : [];
  8370. }
  8371. /**
  8372. Get the active panel created by the given constructor, if any.
  8373. This can be useful when you need access to your panels' DOM
  8374. structure.
  8375. */
  8376. function getPanel(view, panel) {
  8377. let plugin = view.plugin(panelPlugin);
  8378. let index = plugin ? plugin.specs.indexOf(panel) : -1;
  8379. return index > -1 ? plugin.panels[index] : null;
  8380. }
  8381. const panelPlugin = ViewPlugin.fromClass(class {
  8382. constructor(view) {
  8383. this.input = view.state.facet(showPanel);
  8384. this.specs = this.input.filter(s => s);
  8385. this.panels = this.specs.map(spec => spec(view));
  8386. let conf = view.state.facet(panelConfig);
  8387. this.top = new PanelGroup(view, true, conf.topContainer);
  8388. this.bottom = new PanelGroup(view, false, conf.bottomContainer);
  8389. this.top.sync(this.panels.filter(p => p.top));
  8390. this.bottom.sync(this.panels.filter(p => !p.top));
  8391. for (let p of this.panels) {
  8392. p.dom.classList.add("cm-panel");
  8393. if (p.mount)
  8394. p.mount();
  8395. }
  8396. }
  8397. update(update) {
  8398. let conf = update.state.facet(panelConfig);
  8399. if (this.top.container != conf.topContainer) {
  8400. this.top.sync([]);
  8401. this.top = new PanelGroup(update.view, true, conf.topContainer);
  8402. }
  8403. if (this.bottom.container != conf.bottomContainer) {
  8404. this.bottom.sync([]);
  8405. this.bottom = new PanelGroup(update.view, false, conf.bottomContainer);
  8406. }
  8407. this.top.syncClasses();
  8408. this.bottom.syncClasses();
  8409. let input = update.state.facet(showPanel);
  8410. if (input != this.input) {
  8411. let specs = input.filter(x => x);
  8412. let panels = [], top = [], bottom = [], mount = [];
  8413. for (let spec of specs) {
  8414. let known = this.specs.indexOf(spec), panel;
  8415. if (known < 0) {
  8416. panel = spec(update.view);
  8417. mount.push(panel);
  8418. }
  8419. else {
  8420. panel = this.panels[known];
  8421. if (panel.update)
  8422. panel.update(update);
  8423. }
  8424. panels.push(panel);
  8425. (panel.top ? top : bottom).push(panel);
  8426. }
  8427. this.specs = specs;
  8428. this.panels = panels;
  8429. this.top.sync(top);
  8430. this.bottom.sync(bottom);
  8431. for (let p of mount) {
  8432. p.dom.classList.add("cm-panel");
  8433. if (p.mount)
  8434. p.mount();
  8435. }
  8436. }
  8437. else {
  8438. for (let p of this.panels)
  8439. if (p.update)
  8440. p.update(update);
  8441. }
  8442. }
  8443. destroy() {
  8444. this.top.sync([]);
  8445. this.bottom.sync([]);
  8446. }
  8447. }, {
  8448. provide: plugin => EditorView.scrollMargins.of(view => {
  8449. let value = view.plugin(plugin);
  8450. return value && { top: value.top.scrollMargin(), bottom: value.bottom.scrollMargin() };
  8451. })
  8452. });
  8453. class PanelGroup {
  8454. constructor(view, top, container) {
  8455. this.view = view;
  8456. this.top = top;
  8457. this.container = container;
  8458. this.dom = undefined;
  8459. this.classes = "";
  8460. this.panels = [];
  8461. this.syncClasses();
  8462. }
  8463. sync(panels) {
  8464. for (let p of this.panels)
  8465. if (p.destroy && panels.indexOf(p) < 0)
  8466. p.destroy();
  8467. this.panels = panels;
  8468. this.syncDOM();
  8469. }
  8470. syncDOM() {
  8471. if (this.panels.length == 0) {
  8472. if (this.dom) {
  8473. this.dom.remove();
  8474. this.dom = undefined;
  8475. }
  8476. return;
  8477. }
  8478. if (!this.dom) {
  8479. this.dom = document.createElement("div");
  8480. this.dom.className = this.top ? "cm-panels cm-panels-top" : "cm-panels cm-panels-bottom";
  8481. this.dom.style[this.top ? "top" : "bottom"] = "0";
  8482. let parent = this.container || this.view.dom;
  8483. parent.insertBefore(this.dom, this.top ? parent.firstChild : null);
  8484. }
  8485. let curDOM = this.dom.firstChild;
  8486. for (let panel of this.panels) {
  8487. if (panel.dom.parentNode == this.dom) {
  8488. while (curDOM != panel.dom)
  8489. curDOM = rm(curDOM);
  8490. curDOM = curDOM.nextSibling;
  8491. }
  8492. else {
  8493. this.dom.insertBefore(panel.dom, curDOM);
  8494. }
  8495. }
  8496. while (curDOM)
  8497. curDOM = rm(curDOM);
  8498. }
  8499. scrollMargin() {
  8500. return !this.dom || this.container ? 0
  8501. : Math.max(0, this.top ?
  8502. this.dom.getBoundingClientRect().bottom - Math.max(0, this.view.scrollDOM.getBoundingClientRect().top) :
  8503. Math.min(innerHeight, this.view.scrollDOM.getBoundingClientRect().bottom) - this.dom.getBoundingClientRect().top);
  8504. }
  8505. syncClasses() {
  8506. if (!this.container || this.classes == this.view.themeClasses)
  8507. return;
  8508. for (let cls of this.classes.split(" "))
  8509. if (cls)
  8510. this.container.classList.remove(cls);
  8511. for (let cls of (this.classes = this.view.themeClasses).split(" "))
  8512. if (cls)
  8513. this.container.classList.add(cls);
  8514. }
  8515. }
  8516. function rm(node) {
  8517. let next = node.nextSibling;
  8518. node.remove();
  8519. return next;
  8520. }
  8521. /**
  8522. Opening a panel is done by providing a constructor function for
  8523. the panel through this facet. (The panel is closed again when its
  8524. constructor is no longer provided.) Values of `null` are ignored.
  8525. */
  8526. const showPanel = state.Facet.define({
  8527. enables: panelPlugin
  8528. });
  8529. /**
  8530. A gutter marker represents a bit of information attached to a line
  8531. in a specific gutter. Your own custom markers have to extend this
  8532. class.
  8533. */
  8534. class GutterMarker extends state.RangeValue {
  8535. /**
  8536. @internal
  8537. */
  8538. compare(other) {
  8539. return this == other || this.constructor == other.constructor && this.eq(other);
  8540. }
  8541. /**
  8542. Compare this marker to another marker of the same type.
  8543. */
  8544. eq(other) { return false; }
  8545. /**
  8546. Called if the marker has a `toDOM` method and its representation
  8547. was removed from a gutter.
  8548. */
  8549. destroy(dom) { }
  8550. }
  8551. GutterMarker.prototype.elementClass = "";
  8552. GutterMarker.prototype.toDOM = undefined;
  8553. GutterMarker.prototype.mapMode = state.MapMode.TrackBefore;
  8554. GutterMarker.prototype.startSide = GutterMarker.prototype.endSide = -1;
  8555. GutterMarker.prototype.point = true;
  8556. /**
  8557. Facet used to add a class to all gutter elements for a given line.
  8558. Markers given to this facet should _only_ define an
  8559. [`elementclass`](https://codemirror.net/6/docs/ref/#view.GutterMarker.elementClass), not a
  8560. [`toDOM`](https://codemirror.net/6/docs/ref/#view.GutterMarker.toDOM) (or the marker will appear
  8561. in all gutters for the line).
  8562. */
  8563. const gutterLineClass = state.Facet.define();
  8564. const defaults = {
  8565. class: "",
  8566. renderEmptyElements: false,
  8567. elementStyle: "",
  8568. markers: () => state.RangeSet.empty,
  8569. lineMarker: () => null,
  8570. lineMarkerChange: null,
  8571. initialSpacer: null,
  8572. updateSpacer: null,
  8573. domEventHandlers: {}
  8574. };
  8575. const activeGutters = state.Facet.define();
  8576. /**
  8577. Define an editor gutter. The order in which the gutters appear is
  8578. determined by their extension priority.
  8579. */
  8580. function gutter(config) {
  8581. return [gutters(), activeGutters.of(Object.assign(Object.assign({}, defaults), config))];
  8582. }
  8583. const unfixGutters = state.Facet.define({
  8584. combine: values => values.some(x => x)
  8585. });
  8586. /**
  8587. The gutter-drawing plugin is automatically enabled when you add a
  8588. gutter, but you can use this function to explicitly configure it.
  8589. Unless `fixed` is explicitly set to `false`, the gutters are
  8590. fixed, meaning they don't scroll along with the content
  8591. horizontally (except on Internet Explorer, which doesn't support
  8592. CSS [`position:
  8593. sticky`](https://developer.mozilla.org/en-US/docs/Web/CSS/position#sticky)).
  8594. */
  8595. function gutters(config) {
  8596. let result = [
  8597. gutterView,
  8598. ];
  8599. if (config && config.fixed === false)
  8600. result.push(unfixGutters.of(true));
  8601. return result;
  8602. }
  8603. const gutterView = ViewPlugin.fromClass(class {
  8604. constructor(view) {
  8605. this.view = view;
  8606. this.prevViewport = view.viewport;
  8607. this.dom = document.createElement("div");
  8608. this.dom.className = "cm-gutters";
  8609. this.dom.setAttribute("aria-hidden", "true");
  8610. this.dom.style.minHeight = this.view.contentHeight + "px";
  8611. this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf));
  8612. for (let gutter of this.gutters)
  8613. this.dom.appendChild(gutter.dom);
  8614. this.fixed = !view.state.facet(unfixGutters);
  8615. if (this.fixed) {
  8616. // FIXME IE11 fallback, which doesn't support position: sticky,
  8617. // by using position: relative + event handlers that realign the
  8618. // gutter (or just force fixed=false on IE11?)
  8619. this.dom.style.position = "sticky";
  8620. }
  8621. this.syncGutters(false);
  8622. view.scrollDOM.insertBefore(this.dom, view.contentDOM);
  8623. }
  8624. update(update) {
  8625. if (this.updateGutters(update)) {
  8626. // Detach during sync when the viewport changed significantly
  8627. // (such as during scrolling), since for large updates that is
  8628. // faster.
  8629. let vpA = this.prevViewport, vpB = update.view.viewport;
  8630. let vpOverlap = Math.min(vpA.to, vpB.to) - Math.max(vpA.from, vpB.from);
  8631. this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8);
  8632. }
  8633. if (update.geometryChanged)
  8634. this.dom.style.minHeight = this.view.contentHeight + "px";
  8635. if (this.view.state.facet(unfixGutters) != !this.fixed) {
  8636. this.fixed = !this.fixed;
  8637. this.dom.style.position = this.fixed ? "sticky" : "";
  8638. }
  8639. this.prevViewport = update.view.viewport;
  8640. }
  8641. syncGutters(detach) {
  8642. let after = this.dom.nextSibling;
  8643. if (detach)
  8644. this.dom.remove();
  8645. let lineClasses = state.RangeSet.iter(this.view.state.facet(gutterLineClass), this.view.viewport.from);
  8646. let classSet = [];
  8647. let contexts = this.gutters.map(gutter => new UpdateContext(gutter, this.view.viewport, -this.view.documentPadding.top));
  8648. for (let line of this.view.viewportLineBlocks) {
  8649. let text;
  8650. if (Array.isArray(line.type)) {
  8651. for (let b of line.type)
  8652. if (b.type == exports.BlockType.Text) {
  8653. text = b;
  8654. break;
  8655. }
  8656. }
  8657. else {
  8658. text = line.type == exports.BlockType.Text ? line : undefined;
  8659. }
  8660. if (!text)
  8661. continue;
  8662. if (classSet.length)
  8663. classSet = [];
  8664. advanceCursor(lineClasses, classSet, line.from);
  8665. for (let cx of contexts)
  8666. cx.line(this.view, text, classSet);
  8667. }
  8668. for (let cx of contexts)
  8669. cx.finish();
  8670. if (detach)
  8671. this.view.scrollDOM.insertBefore(this.dom, after);
  8672. }
  8673. updateGutters(update) {
  8674. let prev = update.startState.facet(activeGutters), cur = update.state.facet(activeGutters);
  8675. let change = update.docChanged || update.heightChanged || update.viewportChanged ||
  8676. !state.RangeSet.eq(update.startState.facet(gutterLineClass), update.state.facet(gutterLineClass), update.view.viewport.from, update.view.viewport.to);
  8677. if (prev == cur) {
  8678. for (let gutter of this.gutters)
  8679. if (gutter.update(update))
  8680. change = true;
  8681. }
  8682. else {
  8683. change = true;
  8684. let gutters = [];
  8685. for (let conf of cur) {
  8686. let known = prev.indexOf(conf);
  8687. if (known < 0) {
  8688. gutters.push(new SingleGutterView(this.view, conf));
  8689. }
  8690. else {
  8691. this.gutters[known].update(update);
  8692. gutters.push(this.gutters[known]);
  8693. }
  8694. }
  8695. for (let g of this.gutters) {
  8696. g.dom.remove();
  8697. if (gutters.indexOf(g) < 0)
  8698. g.destroy();
  8699. }
  8700. for (let g of gutters)
  8701. this.dom.appendChild(g.dom);
  8702. this.gutters = gutters;
  8703. }
  8704. return change;
  8705. }
  8706. destroy() {
  8707. for (let view of this.gutters)
  8708. view.destroy();
  8709. this.dom.remove();
  8710. }
  8711. }, {
  8712. provide: plugin => EditorView.scrollMargins.of(view => {
  8713. let value = view.plugin(plugin);
  8714. if (!value || value.gutters.length == 0 || !value.fixed)
  8715. return null;
  8716. return view.textDirection == exports.Direction.LTR ? { left: value.dom.offsetWidth } : { right: value.dom.offsetWidth };
  8717. })
  8718. });
  8719. function asArray(val) { return (Array.isArray(val) ? val : [val]); }
  8720. function advanceCursor(cursor, collect, pos) {
  8721. while (cursor.value && cursor.from <= pos) {
  8722. if (cursor.from == pos)
  8723. collect.push(cursor.value);
  8724. cursor.next();
  8725. }
  8726. }
  8727. class UpdateContext {
  8728. constructor(gutter, viewport, height) {
  8729. this.gutter = gutter;
  8730. this.height = height;
  8731. this.localMarkers = [];
  8732. this.i = 0;
  8733. this.cursor = state.RangeSet.iter(gutter.markers, viewport.from);
  8734. }
  8735. line(view, line, extraMarkers) {
  8736. if (this.localMarkers.length)
  8737. this.localMarkers = [];
  8738. advanceCursor(this.cursor, this.localMarkers, line.from);
  8739. let localMarkers = extraMarkers.length ? this.localMarkers.concat(extraMarkers) : this.localMarkers;
  8740. let forLine = this.gutter.config.lineMarker(view, line, localMarkers);
  8741. if (forLine)
  8742. localMarkers.unshift(forLine);
  8743. let gutter = this.gutter;
  8744. if (localMarkers.length == 0 && !gutter.config.renderEmptyElements)
  8745. return;
  8746. let above = line.top - this.height;
  8747. if (this.i == gutter.elements.length) {
  8748. let newElt = new GutterElement(view, line.height, above, localMarkers);
  8749. gutter.elements.push(newElt);
  8750. gutter.dom.appendChild(newElt.dom);
  8751. }
  8752. else {
  8753. gutter.elements[this.i].update(view, line.height, above, localMarkers);
  8754. }
  8755. this.height = line.bottom;
  8756. this.i++;
  8757. }
  8758. finish() {
  8759. let gutter = this.gutter;
  8760. while (gutter.elements.length > this.i) {
  8761. let last = gutter.elements.pop();
  8762. gutter.dom.removeChild(last.dom);
  8763. last.destroy();
  8764. }
  8765. }
  8766. }
  8767. class SingleGutterView {
  8768. constructor(view, config) {
  8769. this.view = view;
  8770. this.config = config;
  8771. this.elements = [];
  8772. this.spacer = null;
  8773. this.dom = document.createElement("div");
  8774. this.dom.className = "cm-gutter" + (this.config.class ? " " + this.config.class : "");
  8775. for (let prop in config.domEventHandlers) {
  8776. this.dom.addEventListener(prop, (event) => {
  8777. let line = view.lineBlockAtHeight(event.clientY - view.documentTop);
  8778. if (config.domEventHandlers[prop](view, line, event))
  8779. event.preventDefault();
  8780. });
  8781. }
  8782. this.markers = asArray(config.markers(view));
  8783. if (config.initialSpacer) {
  8784. this.spacer = new GutterElement(view, 0, 0, [config.initialSpacer(view)]);
  8785. this.dom.appendChild(this.spacer.dom);
  8786. this.spacer.dom.style.cssText += "visibility: hidden; pointer-events: none";
  8787. }
  8788. }
  8789. update(update) {
  8790. let prevMarkers = this.markers;
  8791. this.markers = asArray(this.config.markers(update.view));
  8792. if (this.spacer && this.config.updateSpacer) {
  8793. let updated = this.config.updateSpacer(this.spacer.markers[0], update);
  8794. if (updated != this.spacer.markers[0])
  8795. this.spacer.update(update.view, 0, 0, [updated]);
  8796. }
  8797. let vp = update.view.viewport;
  8798. return !state.RangeSet.eq(this.markers, prevMarkers, vp.from, vp.to) ||
  8799. (this.config.lineMarkerChange ? this.config.lineMarkerChange(update) : false);
  8800. }
  8801. destroy() {
  8802. for (let elt of this.elements)
  8803. elt.destroy();
  8804. }
  8805. }
  8806. class GutterElement {
  8807. constructor(view, height, above, markers) {
  8808. this.height = -1;
  8809. this.above = 0;
  8810. this.markers = [];
  8811. this.dom = document.createElement("div");
  8812. this.dom.className = "cm-gutterElement";
  8813. this.update(view, height, above, markers);
  8814. }
  8815. update(view, height, above, markers) {
  8816. if (this.height != height)
  8817. this.dom.style.height = (this.height = height) + "px";
  8818. if (this.above != above)
  8819. this.dom.style.marginTop = (this.above = above) ? above + "px" : "";
  8820. if (!sameMarkers(this.markers, markers))
  8821. this.setMarkers(view, markers);
  8822. }
  8823. setMarkers(view, markers) {
  8824. let cls = "cm-gutterElement", domPos = this.dom.firstChild;
  8825. for (let iNew = 0, iOld = 0;;) {
  8826. let skipTo = iOld, marker = iNew < markers.length ? markers[iNew++] : null, matched = false;
  8827. if (marker) {
  8828. let c = marker.elementClass;
  8829. if (c)
  8830. cls += " " + c;
  8831. for (let i = iOld; i < this.markers.length; i++)
  8832. if (this.markers[i].compare(marker)) {
  8833. skipTo = i;
  8834. matched = true;
  8835. break;
  8836. }
  8837. }
  8838. else {
  8839. skipTo = this.markers.length;
  8840. }
  8841. while (iOld < skipTo) {
  8842. let next = this.markers[iOld++];
  8843. if (next.toDOM) {
  8844. next.destroy(domPos);
  8845. let after = domPos.nextSibling;
  8846. domPos.remove();
  8847. domPos = after;
  8848. }
  8849. }
  8850. if (!marker)
  8851. break;
  8852. if (marker.toDOM) {
  8853. if (matched)
  8854. domPos = domPos.nextSibling;
  8855. else
  8856. this.dom.insertBefore(marker.toDOM(view), domPos);
  8857. }
  8858. if (matched)
  8859. iOld++;
  8860. }
  8861. this.dom.className = cls;
  8862. this.markers = markers;
  8863. }
  8864. destroy() {
  8865. this.setMarkers(null, []); // First argument not used unless creating markers
  8866. }
  8867. }
  8868. function sameMarkers(a, b) {
  8869. if (a.length != b.length)
  8870. return false;
  8871. for (let i = 0; i < a.length; i++)
  8872. if (!a[i].compare(b[i]))
  8873. return false;
  8874. return true;
  8875. }
  8876. /**
  8877. Facet used to provide markers to the line number gutter.
  8878. */
  8879. const lineNumberMarkers = state.Facet.define();
  8880. const lineNumberConfig = state.Facet.define({
  8881. combine(values) {
  8882. return state.combineConfig(values, { formatNumber: String, domEventHandlers: {} }, {
  8883. domEventHandlers(a, b) {
  8884. let result = Object.assign({}, a);
  8885. for (let event in b) {
  8886. let exists = result[event], add = b[event];
  8887. result[event] = exists ? (view, line, event) => exists(view, line, event) || add(view, line, event) : add;
  8888. }
  8889. return result;
  8890. }
  8891. });
  8892. }
  8893. });
  8894. class NumberMarker extends GutterMarker {
  8895. constructor(number) {
  8896. super();
  8897. this.number = number;
  8898. }
  8899. eq(other) { return this.number == other.number; }
  8900. toDOM() { return document.createTextNode(this.number); }
  8901. }
  8902. function formatNumber(view, number) {
  8903. return view.state.facet(lineNumberConfig).formatNumber(number, view.state);
  8904. }
  8905. const lineNumberGutter = activeGutters.compute([lineNumberConfig], state => ({
  8906. class: "cm-lineNumbers",
  8907. renderEmptyElements: false,
  8908. markers(view) { return view.state.facet(lineNumberMarkers); },
  8909. lineMarker(view, line, others) {
  8910. if (others.some(m => m.toDOM))
  8911. return null;
  8912. return new NumberMarker(formatNumber(view, view.state.doc.lineAt(line.from).number));
  8913. },
  8914. lineMarkerChange: update => update.startState.facet(lineNumberConfig) != update.state.facet(lineNumberConfig),
  8915. initialSpacer(view) {
  8916. return new NumberMarker(formatNumber(view, maxLineNumber(view.state.doc.lines)));
  8917. },
  8918. updateSpacer(spacer, update) {
  8919. let max = formatNumber(update.view, maxLineNumber(update.view.state.doc.lines));
  8920. return max == spacer.number ? spacer : new NumberMarker(max);
  8921. },
  8922. domEventHandlers: state.facet(lineNumberConfig).domEventHandlers
  8923. }));
  8924. /**
  8925. Create a line number gutter extension.
  8926. */
  8927. function lineNumbers(config = {}) {
  8928. return [
  8929. lineNumberConfig.of(config),
  8930. gutters(),
  8931. lineNumberGutter
  8932. ];
  8933. }
  8934. function maxLineNumber(lines) {
  8935. let last = 9;
  8936. while (last < lines)
  8937. last = last * 10 + 9;
  8938. return last;
  8939. }
  8940. const activeLineGutterMarker = new class extends GutterMarker {
  8941. constructor() {
  8942. super(...arguments);
  8943. this.elementClass = "cm-activeLineGutter";
  8944. }
  8945. };
  8946. const activeLineGutterHighlighter = gutterLineClass.compute(["selection"], state$1 => {
  8947. let marks = [], last = -1;
  8948. for (let range of state$1.selection.ranges)
  8949. if (range.empty) {
  8950. let linePos = state$1.doc.lineAt(range.head).from;
  8951. if (linePos > last) {
  8952. last = linePos;
  8953. marks.push(activeLineGutterMarker.range(linePos));
  8954. }
  8955. }
  8956. return state.RangeSet.of(marks);
  8957. });
  8958. /**
  8959. Returns an extension that adds a `cm-activeLineGutter` class to
  8960. all gutter elements on the [active
  8961. line](https://codemirror.net/6/docs/ref/#view.highlightActiveLine).
  8962. */
  8963. function highlightActiveLineGutter() {
  8964. return activeLineGutterHighlighter;
  8965. }
  8966. /**
  8967. @internal
  8968. */
  8969. const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRange, computeOrder, moveVisually };
  8970. exports.BidiSpan = BidiSpan;
  8971. exports.BlockInfo = BlockInfo;
  8972. exports.Decoration = Decoration;
  8973. exports.EditorView = EditorView;
  8974. exports.GutterMarker = GutterMarker;
  8975. exports.MatchDecorator = MatchDecorator;
  8976. exports.ViewPlugin = ViewPlugin;
  8977. exports.ViewUpdate = ViewUpdate;
  8978. exports.WidgetType = WidgetType;
  8979. exports.__test = __test;
  8980. exports.closeHoverTooltips = closeHoverTooltips;
  8981. exports.crosshairCursor = crosshairCursor;
  8982. exports.drawSelection = drawSelection;
  8983. exports.dropCursor = dropCursor;
  8984. exports.getPanel = getPanel;
  8985. exports.getTooltip = getTooltip;
  8986. exports.gutter = gutter;
  8987. exports.gutterLineClass = gutterLineClass;
  8988. exports.gutters = gutters;
  8989. exports.hasHoverTooltips = hasHoverTooltips;
  8990. exports.highlightActiveLine = highlightActiveLine;
  8991. exports.highlightActiveLineGutter = highlightActiveLineGutter;
  8992. exports.highlightSpecialChars = highlightSpecialChars;
  8993. exports.hoverTooltip = hoverTooltip;
  8994. exports.keymap = keymap;
  8995. exports.lineNumberMarkers = lineNumberMarkers;
  8996. exports.lineNumbers = lineNumbers;
  8997. exports.logException = logException;
  8998. exports.panels = panels;
  8999. exports.placeholder = placeholder;
  9000. exports.rectangularSelection = rectangularSelection;
  9001. exports.repositionTooltips = repositionTooltips;
  9002. exports.runScopeHandlers = runScopeHandlers;
  9003. exports.scrollPastEnd = scrollPastEnd;
  9004. exports.showPanel = showPanel;
  9005. exports.showTooltip = showTooltip;
  9006. exports.tooltips = tooltips;