emmet.js 386 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669
  1. (function(){
  2. // Underscore.js 1.3.3
  3. // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
  4. // Underscore is freely distributable under the MIT license.
  5. // Portions of Underscore are inspired or borrowed from Prototype,
  6. // Oliver Steele's Functional, and John Resig's Micro-Templating.
  7. // For all details and documentation:
  8. // http://documentcloud.github.com/underscore
  9. var _ = (function() {
  10. // Baseline setup
  11. // --------------
  12. // Establish the root object, `window` in the browser, or `global` on the server.
  13. var root = this;
  14. // Save the previous value of the `_` variable.
  15. var previousUnderscore = root._;
  16. // Establish the object that gets returned to break out of a loop iteration.
  17. var breaker = {};
  18. // Save bytes in the minified (but not gzipped) version:
  19. var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
  20. // Create quick reference variables for speed access to core prototypes.
  21. var slice = ArrayProto.slice,
  22. unshift = ArrayProto.unshift,
  23. toString = ObjProto.toString,
  24. hasOwnProperty = ObjProto.hasOwnProperty;
  25. // All **ECMAScript 5** native function implementations that we hope to use
  26. // are declared here.
  27. var
  28. nativeForEach = ArrayProto.forEach,
  29. nativeMap = ArrayProto.map,
  30. nativeReduce = ArrayProto.reduce,
  31. nativeReduceRight = ArrayProto.reduceRight,
  32. nativeFilter = ArrayProto.filter,
  33. nativeEvery = ArrayProto.every,
  34. nativeSome = ArrayProto.some,
  35. nativeIndexOf = ArrayProto.indexOf,
  36. nativeLastIndexOf = ArrayProto.lastIndexOf,
  37. nativeIsArray = Array.isArray,
  38. nativeKeys = Object.keys,
  39. nativeBind = FuncProto.bind;
  40. // Create a safe reference to the Underscore object for use below.
  41. var _ = function(obj) { return new wrapper(obj); };
  42. // Export the Underscore object for **Node.js**, with
  43. // backwards-compatibility for the old `require()` API. If we're in
  44. // the browser, add `_` as a global object via a string identifier,
  45. // for Closure Compiler "advanced" mode.
  46. if (typeof exports !== 'undefined') {
  47. if (typeof module !== 'undefined' && module.exports) {
  48. exports = module.exports = _;
  49. }
  50. exports._ = _;
  51. } else {
  52. root['_'] = _;
  53. }
  54. // Current version.
  55. _.VERSION = '1.3.3';
  56. // Collection Functions
  57. // --------------------
  58. // The cornerstone, an `each` implementation, aka `forEach`.
  59. // Handles objects with the built-in `forEach`, arrays, and raw objects.
  60. // Delegates to **ECMAScript 5**'s native `forEach` if available.
  61. var each = _.each = _.forEach = function(obj, iterator, context) {
  62. if (obj == null) return;
  63. if (nativeForEach && obj.forEach === nativeForEach) {
  64. obj.forEach(iterator, context);
  65. } else if (obj.length === +obj.length) {
  66. for (var i = 0, l = obj.length; i < l; i++) {
  67. if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
  68. }
  69. } else {
  70. for (var key in obj) {
  71. if (_.has(obj, key)) {
  72. if (iterator.call(context, obj[key], key, obj) === breaker) return;
  73. }
  74. }
  75. }
  76. };
  77. // Return the results of applying the iterator to each element.
  78. // Delegates to **ECMAScript 5**'s native `map` if available.
  79. _.map = _.collect = function(obj, iterator, context) {
  80. var results = [];
  81. if (obj == null) return results;
  82. if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
  83. each(obj, function(value, index, list) {
  84. results[results.length] = iterator.call(context, value, index, list);
  85. });
  86. if (obj.length === +obj.length) results.length = obj.length;
  87. return results;
  88. };
  89. // **Reduce** builds up a single result from a list of values, aka `inject`,
  90. // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
  91. _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
  92. var initial = arguments.length > 2;
  93. if (obj == null) obj = [];
  94. if (nativeReduce && obj.reduce === nativeReduce) {
  95. if (context) iterator = _.bind(iterator, context);
  96. return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
  97. }
  98. each(obj, function(value, index, list) {
  99. if (!initial) {
  100. memo = value;
  101. initial = true;
  102. } else {
  103. memo = iterator.call(context, memo, value, index, list);
  104. }
  105. });
  106. if (!initial) throw new TypeError('Reduce of empty array with no initial value');
  107. return memo;
  108. };
  109. // The right-associative version of reduce, also known as `foldr`.
  110. // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
  111. _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
  112. var initial = arguments.length > 2;
  113. if (obj == null) obj = [];
  114. if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
  115. if (context) iterator = _.bind(iterator, context);
  116. return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
  117. }
  118. var reversed = _.toArray(obj).reverse();
  119. if (context && !initial) iterator = _.bind(iterator, context);
  120. return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
  121. };
  122. // Return the first value which passes a truth test. Aliased as `detect`.
  123. _.find = _.detect = function(obj, iterator, context) {
  124. var result;
  125. any(obj, function(value, index, list) {
  126. if (iterator.call(context, value, index, list)) {
  127. result = value;
  128. return true;
  129. }
  130. });
  131. return result;
  132. };
  133. // Return all the elements that pass a truth test.
  134. // Delegates to **ECMAScript 5**'s native `filter` if available.
  135. // Aliased as `select`.
  136. _.filter = _.select = function(obj, iterator, context) {
  137. var results = [];
  138. if (obj == null) return results;
  139. if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
  140. each(obj, function(value, index, list) {
  141. if (iterator.call(context, value, index, list)) results[results.length] = value;
  142. });
  143. return results;
  144. };
  145. // Return all the elements for which a truth test fails.
  146. _.reject = function(obj, iterator, context) {
  147. var results = [];
  148. if (obj == null) return results;
  149. each(obj, function(value, index, list) {
  150. if (!iterator.call(context, value, index, list)) results[results.length] = value;
  151. });
  152. return results;
  153. };
  154. // Determine whether all of the elements match a truth test.
  155. // Delegates to **ECMAScript 5**'s native `every` if available.
  156. // Aliased as `all`.
  157. _.every = _.all = function(obj, iterator, context) {
  158. var result = true;
  159. if (obj == null) return result;
  160. if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
  161. each(obj, function(value, index, list) {
  162. if (!(result = result && iterator.call(context, value, index, list))) return breaker;
  163. });
  164. return !!result;
  165. };
  166. // Determine if at least one element in the object matches a truth test.
  167. // Delegates to **ECMAScript 5**'s native `some` if available.
  168. // Aliased as `any`.
  169. var any = _.some = _.any = function(obj, iterator, context) {
  170. iterator || (iterator = _.identity);
  171. var result = false;
  172. if (obj == null) return result;
  173. if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
  174. each(obj, function(value, index, list) {
  175. if (result || (result = iterator.call(context, value, index, list))) return breaker;
  176. });
  177. return !!result;
  178. };
  179. // Determine if a given value is included in the array or object using `===`.
  180. // Aliased as `contains`.
  181. _.include = _.contains = function(obj, target) {
  182. var found = false;
  183. if (obj == null) return found;
  184. if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
  185. found = any(obj, function(value) {
  186. return value === target;
  187. });
  188. return found;
  189. };
  190. // Invoke a method (with arguments) on every item in a collection.
  191. _.invoke = function(obj, method) {
  192. var args = slice.call(arguments, 2);
  193. return _.map(obj, function(value) {
  194. return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
  195. });
  196. };
  197. // Convenience version of a common use case of `map`: fetching a property.
  198. _.pluck = function(obj, key) {
  199. return _.map(obj, function(value){ return value[key]; });
  200. };
  201. // Return the maximum element or (element-based computation).
  202. _.max = function(obj, iterator, context) {
  203. if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj);
  204. if (!iterator && _.isEmpty(obj)) return -Infinity;
  205. var result = {computed : -Infinity};
  206. each(obj, function(value, index, list) {
  207. var computed = iterator ? iterator.call(context, value, index, list) : value;
  208. computed >= result.computed && (result = {value : value, computed : computed});
  209. });
  210. return result.value;
  211. };
  212. // Return the minimum element (or element-based computation).
  213. _.min = function(obj, iterator, context) {
  214. if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj);
  215. if (!iterator && _.isEmpty(obj)) return Infinity;
  216. var result = {computed : Infinity};
  217. each(obj, function(value, index, list) {
  218. var computed = iterator ? iterator.call(context, value, index, list) : value;
  219. computed < result.computed && (result = {value : value, computed : computed});
  220. });
  221. return result.value;
  222. };
  223. // Shuffle an array.
  224. _.shuffle = function(obj) {
  225. var shuffled = [], rand;
  226. each(obj, function(value, index, list) {
  227. rand = Math.floor(Math.random() * (index + 1));
  228. shuffled[index] = shuffled[rand];
  229. shuffled[rand] = value;
  230. });
  231. return shuffled;
  232. };
  233. // Sort the object's values by a criterion produced by an iterator.
  234. _.sortBy = function(obj, val, context) {
  235. var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
  236. return _.pluck(_.map(obj, function(value, index, list) {
  237. return {
  238. value : value,
  239. criteria : iterator.call(context, value, index, list)
  240. };
  241. }).sort(function(left, right) {
  242. var a = left.criteria, b = right.criteria;
  243. if (a === void 0) return 1;
  244. if (b === void 0) return -1;
  245. return a < b ? -1 : a > b ? 1 : 0;
  246. }), 'value');
  247. };
  248. // Groups the object's values by a criterion. Pass either a string attribute
  249. // to group by, or a function that returns the criterion.
  250. _.groupBy = function(obj, val) {
  251. var result = {};
  252. var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
  253. each(obj, function(value, index) {
  254. var key = iterator(value, index);
  255. (result[key] || (result[key] = [])).push(value);
  256. });
  257. return result;
  258. };
  259. // Use a comparator function to figure out at what index an object should
  260. // be inserted so as to maintain order. Uses binary search.
  261. _.sortedIndex = function(array, obj, iterator) {
  262. iterator || (iterator = _.identity);
  263. var low = 0, high = array.length;
  264. while (low < high) {
  265. var mid = (low + high) >> 1;
  266. iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
  267. }
  268. return low;
  269. };
  270. // Safely convert anything iterable into a real, live array.
  271. _.toArray = function(obj) {
  272. if (!obj) return [];
  273. if (_.isArray(obj)) return slice.call(obj);
  274. if (_.isArguments(obj)) return slice.call(obj);
  275. if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray();
  276. return _.values(obj);
  277. };
  278. // Return the number of elements in an object.
  279. _.size = function(obj) {
  280. return _.isArray(obj) ? obj.length : _.keys(obj).length;
  281. };
  282. // Array Functions
  283. // ---------------
  284. // Get the first element of an array. Passing **n** will return the first N
  285. // values in the array. Aliased as `head` and `take`. The **guard** check
  286. // allows it to work with `_.map`.
  287. _.first = _.head = _.take = function(array, n, guard) {
  288. return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
  289. };
  290. // Returns everything but the last entry of the array. Especcialy useful on
  291. // the arguments object. Passing **n** will return all the values in
  292. // the array, excluding the last N. The **guard** check allows it to work with
  293. // `_.map`.
  294. _.initial = function(array, n, guard) {
  295. return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
  296. };
  297. // Get the last element of an array. Passing **n** will return the last N
  298. // values in the array. The **guard** check allows it to work with `_.map`.
  299. _.last = function(array, n, guard) {
  300. if ((n != null) && !guard) {
  301. return slice.call(array, Math.max(array.length - n, 0));
  302. } else {
  303. return array[array.length - 1];
  304. }
  305. };
  306. // Returns everything but the first entry of the array. Aliased as `tail`.
  307. // Especially useful on the arguments object. Passing an **index** will return
  308. // the rest of the values in the array from that index onward. The **guard**
  309. // check allows it to work with `_.map`.
  310. _.rest = _.tail = function(array, index, guard) {
  311. return slice.call(array, (index == null) || guard ? 1 : index);
  312. };
  313. // Trim out all falsy values from an array.
  314. _.compact = function(array) {
  315. return _.filter(array, function(value){ return !!value; });
  316. };
  317. // Return a completely flattened version of an array.
  318. _.flatten = function(array, shallow) {
  319. return _.reduce(array, function(memo, value) {
  320. if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
  321. memo[memo.length] = value;
  322. return memo;
  323. }, []);
  324. };
  325. // Return a version of the array that does not contain the specified value(s).
  326. _.without = function(array) {
  327. return _.difference(array, slice.call(arguments, 1));
  328. };
  329. // Produce a duplicate-free version of the array. If the array has already
  330. // been sorted, you have the option of using a faster algorithm.
  331. // Aliased as `unique`.
  332. _.uniq = _.unique = function(array, isSorted, iterator) {
  333. var initial = iterator ? _.map(array, iterator) : array;
  334. var results = [];
  335. // The `isSorted` flag is irrelevant if the array only contains two elements.
  336. if (array.length < 3) isSorted = true;
  337. _.reduce(initial, function (memo, value, index) {
  338. if (isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {
  339. memo.push(value);
  340. results.push(array[index]);
  341. }
  342. return memo;
  343. }, []);
  344. return results;
  345. };
  346. // Produce an array that contains the union: each distinct element from all of
  347. // the passed-in arrays.
  348. _.union = function() {
  349. return _.uniq(_.flatten(arguments, true));
  350. };
  351. // Produce an array that contains every item shared between all the
  352. // passed-in arrays. (Aliased as "intersect" for back-compat.)
  353. _.intersection = _.intersect = function(array) {
  354. var rest = slice.call(arguments, 1);
  355. return _.filter(_.uniq(array), function(item) {
  356. return _.every(rest, function(other) {
  357. return _.indexOf(other, item) >= 0;
  358. });
  359. });
  360. };
  361. // Take the difference between one array and a number of other arrays.
  362. // Only the elements present in just the first array will remain.
  363. _.difference = function(array) {
  364. var rest = _.flatten(slice.call(arguments, 1), true);
  365. return _.filter(array, function(value){ return !_.include(rest, value); });
  366. };
  367. // Zip together multiple lists into a single array -- elements that share
  368. // an index go together.
  369. _.zip = function() {
  370. var args = slice.call(arguments);
  371. var length = _.max(_.pluck(args, 'length'));
  372. var results = new Array(length);
  373. for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
  374. return results;
  375. };
  376. // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
  377. // we need this function. Return the position of the first occurrence of an
  378. // item in an array, or -1 if the item is not included in the array.
  379. // Delegates to **ECMAScript 5**'s native `indexOf` if available.
  380. // If the array is large and already in sort order, pass `true`
  381. // for **isSorted** to use binary search.
  382. _.indexOf = function(array, item, isSorted) {
  383. if (array == null) return -1;
  384. var i, l;
  385. if (isSorted) {
  386. i = _.sortedIndex(array, item);
  387. return array[i] === item ? i : -1;
  388. }
  389. if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
  390. for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
  391. return -1;
  392. };
  393. // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
  394. _.lastIndexOf = function(array, item) {
  395. if (array == null) return -1;
  396. if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
  397. var i = array.length;
  398. while (i--) if (i in array && array[i] === item) return i;
  399. return -1;
  400. };
  401. // Generate an integer Array containing an arithmetic progression. A port of
  402. // the native Python `range()` function. See
  403. // [the Python documentation](http://docs.python.org/library/functions.html#range).
  404. _.range = function(start, stop, step) {
  405. if (arguments.length <= 1) {
  406. stop = start || 0;
  407. start = 0;
  408. }
  409. step = arguments[2] || 1;
  410. var len = Math.max(Math.ceil((stop - start) / step), 0);
  411. var idx = 0;
  412. var range = new Array(len);
  413. while(idx < len) {
  414. range[idx++] = start;
  415. start += step;
  416. }
  417. return range;
  418. };
  419. // Function (ahem) Functions
  420. // ------------------
  421. // Reusable constructor function for prototype setting.
  422. var ctor = function(){};
  423. // Create a function bound to a given object (assigning `this`, and arguments,
  424. // optionally). Binding with arguments is also known as `curry`.
  425. // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
  426. // We check for `func.bind` first, to fail fast when `func` is undefined.
  427. _.bind = function bind(func, context) {
  428. var bound, args;
  429. if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
  430. if (!_.isFunction(func)) throw new TypeError;
  431. args = slice.call(arguments, 2);
  432. return bound = function() {
  433. if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
  434. ctor.prototype = func.prototype;
  435. var self = new ctor;
  436. var result = func.apply(self, args.concat(slice.call(arguments)));
  437. if (Object(result) === result) return result;
  438. return self;
  439. };
  440. };
  441. // Bind all of an object's methods to that object. Useful for ensuring that
  442. // all callbacks defined on an object belong to it.
  443. _.bindAll = function(obj) {
  444. var funcs = slice.call(arguments, 1);
  445. if (funcs.length == 0) funcs = _.functions(obj);
  446. each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
  447. return obj;
  448. };
  449. // Memoize an expensive function by storing its results.
  450. _.memoize = function(func, hasher) {
  451. var memo = {};
  452. hasher || (hasher = _.identity);
  453. return function() {
  454. var key = hasher.apply(this, arguments);
  455. return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
  456. };
  457. };
  458. // Delays a function for the given number of milliseconds, and then calls
  459. // it with the arguments supplied.
  460. _.delay = function(func, wait) {
  461. var args = slice.call(arguments, 2);
  462. return setTimeout(function(){ return func.apply(null, args); }, wait);
  463. };
  464. // Defers a function, scheduling it to run after the current call stack has
  465. // cleared.
  466. _.defer = function(func) {
  467. return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
  468. };
  469. // Returns a function, that, when invoked, will only be triggered at most once
  470. // during a given window of time.
  471. _.throttle = function(func, wait) {
  472. var context, args, timeout, throttling, more, result;
  473. var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
  474. return function() {
  475. context = this; args = arguments;
  476. var later = function() {
  477. timeout = null;
  478. if (more) func.apply(context, args);
  479. whenDone();
  480. };
  481. if (!timeout) timeout = setTimeout(later, wait);
  482. if (throttling) {
  483. more = true;
  484. } else {
  485. result = func.apply(context, args);
  486. }
  487. whenDone();
  488. throttling = true;
  489. return result;
  490. };
  491. };
  492. // Returns a function, that, as long as it continues to be invoked, will not
  493. // be triggered. The function will be called after it stops being called for
  494. // N milliseconds. If `immediate` is passed, trigger the function on the
  495. // leading edge, instead of the trailing.
  496. _.debounce = function(func, wait, immediate) {
  497. var timeout;
  498. return function() {
  499. var context = this, args = arguments;
  500. var later = function() {
  501. timeout = null;
  502. if (!immediate) func.apply(context, args);
  503. };
  504. if (immediate && !timeout) func.apply(context, args);
  505. clearTimeout(timeout);
  506. timeout = setTimeout(later, wait);
  507. };
  508. };
  509. // Returns a function that will be executed at most one time, no matter how
  510. // often you call it. Useful for lazy initialization.
  511. _.once = function(func) {
  512. var ran = false, memo;
  513. return function() {
  514. if (ran) return memo;
  515. ran = true;
  516. return memo = func.apply(this, arguments);
  517. };
  518. };
  519. // Returns the first function passed as an argument to the second,
  520. // allowing you to adjust arguments, run code before and after, and
  521. // conditionally execute the original function.
  522. _.wrap = function(func, wrapper) {
  523. return function() {
  524. var args = [func].concat(slice.call(arguments, 0));
  525. return wrapper.apply(this, args);
  526. };
  527. };
  528. // Returns a function that is the composition of a list of functions, each
  529. // consuming the return value of the function that follows.
  530. _.compose = function() {
  531. var funcs = arguments;
  532. return function() {
  533. var args = arguments;
  534. for (var i = funcs.length - 1; i >= 0; i--) {
  535. args = [funcs[i].apply(this, args)];
  536. }
  537. return args[0];
  538. };
  539. };
  540. // Returns a function that will only be executed after being called N times.
  541. _.after = function(times, func) {
  542. if (times <= 0) return func();
  543. return function() {
  544. if (--times < 1) { return func.apply(this, arguments); }
  545. };
  546. };
  547. // Object Functions
  548. // ----------------
  549. // Retrieve the names of an object's properties.
  550. // Delegates to **ECMAScript 5**'s native `Object.keys`
  551. _.keys = nativeKeys || function(obj) {
  552. if (obj !== Object(obj)) throw new TypeError('Invalid object');
  553. var keys = [];
  554. for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
  555. return keys;
  556. };
  557. // Retrieve the values of an object's properties.
  558. _.values = function(obj) {
  559. return _.map(obj, _.identity);
  560. };
  561. // Return a sorted list of the function names available on the object.
  562. // Aliased as `methods`
  563. _.functions = _.methods = function(obj) {
  564. var names = [];
  565. for (var key in obj) {
  566. if (_.isFunction(obj[key])) names.push(key);
  567. }
  568. return names.sort();
  569. };
  570. // Extend a given object with all the properties in passed-in object(s).
  571. _.extend = function(obj) {
  572. each(slice.call(arguments, 1), function(source) {
  573. for (var prop in source) {
  574. obj[prop] = source[prop];
  575. }
  576. });
  577. return obj;
  578. };
  579. // Return a copy of the object only containing the whitelisted properties.
  580. _.pick = function(obj) {
  581. var result = {};
  582. each(_.flatten(slice.call(arguments, 1)), function(key) {
  583. if (key in obj) result[key] = obj[key];
  584. });
  585. return result;
  586. };
  587. // Fill in a given object with default properties.
  588. _.defaults = function(obj) {
  589. each(slice.call(arguments, 1), function(source) {
  590. for (var prop in source) {
  591. if (obj[prop] == null) obj[prop] = source[prop];
  592. }
  593. });
  594. return obj;
  595. };
  596. // Create a (shallow-cloned) duplicate of an object.
  597. _.clone = function(obj) {
  598. if (!_.isObject(obj)) return obj;
  599. return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
  600. };
  601. // Invokes interceptor with the obj, and then returns obj.
  602. // The primary purpose of this method is to "tap into" a method chain, in
  603. // order to perform operations on intermediate results within the chain.
  604. _.tap = function(obj, interceptor) {
  605. interceptor(obj);
  606. return obj;
  607. };
  608. // Internal recursive comparison function.
  609. function eq(a, b, stack) {
  610. // Identical objects are equal. `0 === -0`, but they aren't identical.
  611. // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
  612. if (a === b) return a !== 0 || 1 / a == 1 / b;
  613. // A strict comparison is necessary because `null == undefined`.
  614. if (a == null || b == null) return a === b;
  615. // Unwrap any wrapped objects.
  616. if (a._chain) a = a._wrapped;
  617. if (b._chain) b = b._wrapped;
  618. // Invoke a custom `isEqual` method if one is provided.
  619. if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
  620. if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
  621. // Compare `[[Class]]` names.
  622. var className = toString.call(a);
  623. if (className != toString.call(b)) return false;
  624. switch (className) {
  625. // Strings, numbers, dates, and booleans are compared by value.
  626. case '[object String]':
  627. // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
  628. // equivalent to `new String("5")`.
  629. return a == String(b);
  630. case '[object Number]':
  631. // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
  632. // other numeric values.
  633. return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
  634. case '[object Date]':
  635. case '[object Boolean]':
  636. // Coerce dates and booleans to numeric primitive values. Dates are compared by their
  637. // millisecond representations. Note that invalid dates with millisecond representations
  638. // of `NaN` are not equivalent.
  639. return +a == +b;
  640. // RegExps are compared by their source patterns and flags.
  641. case '[object RegExp]':
  642. return a.source == b.source &&
  643. a.global == b.global &&
  644. a.multiline == b.multiline &&
  645. a.ignoreCase == b.ignoreCase;
  646. }
  647. if (typeof a != 'object' || typeof b != 'object') return false;
  648. // Assume equality for cyclic structures. The algorithm for detecting cyclic
  649. // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
  650. var length = stack.length;
  651. while (length--) {
  652. // Linear search. Performance is inversely proportional to the number of
  653. // unique nested structures.
  654. if (stack[length] == a) return true;
  655. }
  656. // Add the first object to the stack of traversed objects.
  657. stack.push(a);
  658. var size = 0, result = true;
  659. // Recursively compare objects and arrays.
  660. if (className == '[object Array]') {
  661. // Compare array lengths to determine if a deep comparison is necessary.
  662. size = a.length;
  663. result = size == b.length;
  664. if (result) {
  665. // Deep compare the contents, ignoring non-numeric properties.
  666. while (size--) {
  667. // Ensure commutative equality for sparse arrays.
  668. if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
  669. }
  670. }
  671. } else {
  672. // Objects with different constructors are not equivalent.
  673. if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
  674. // Deep compare objects.
  675. for (var key in a) {
  676. if (_.has(a, key)) {
  677. // Count the expected number of properties.
  678. size++;
  679. // Deep compare each member.
  680. if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
  681. }
  682. }
  683. // Ensure that both objects contain the same number of properties.
  684. if (result) {
  685. for (key in b) {
  686. if (_.has(b, key) && !(size--)) break;
  687. }
  688. result = !size;
  689. }
  690. }
  691. // Remove the first object from the stack of traversed objects.
  692. stack.pop();
  693. return result;
  694. }
  695. // Perform a deep comparison to check if two objects are equal.
  696. _.isEqual = function(a, b) {
  697. return eq(a, b, []);
  698. };
  699. // Is a given array, string, or object empty?
  700. // An "empty" object has no enumerable own-properties.
  701. _.isEmpty = function(obj) {
  702. if (obj == null) return true;
  703. if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
  704. for (var key in obj) if (_.has(obj, key)) return false;
  705. return true;
  706. };
  707. // Is a given value a DOM element?
  708. _.isElement = function(obj) {
  709. return !!(obj && obj.nodeType == 1);
  710. };
  711. // Is a given value an array?
  712. // Delegates to ECMA5's native Array.isArray
  713. _.isArray = nativeIsArray || function(obj) {
  714. return toString.call(obj) == '[object Array]';
  715. };
  716. // Is a given variable an object?
  717. _.isObject = function(obj) {
  718. return obj === Object(obj);
  719. };
  720. // Is a given variable an arguments object?
  721. _.isArguments = function(obj) {
  722. return toString.call(obj) == '[object Arguments]';
  723. };
  724. if (!_.isArguments(arguments)) {
  725. _.isArguments = function(obj) {
  726. return !!(obj && _.has(obj, 'callee'));
  727. };
  728. }
  729. // Is a given value a function?
  730. _.isFunction = function(obj) {
  731. return toString.call(obj) == '[object Function]';
  732. };
  733. // Is a given value a string?
  734. _.isString = function(obj) {
  735. return toString.call(obj) == '[object String]';
  736. };
  737. // Is a given value a number?
  738. _.isNumber = function(obj) {
  739. return toString.call(obj) == '[object Number]';
  740. };
  741. // Is a given object a finite number?
  742. _.isFinite = function(obj) {
  743. return _.isNumber(obj) && isFinite(obj);
  744. };
  745. // Is the given value `NaN`?
  746. _.isNaN = function(obj) {
  747. // `NaN` is the only value for which `===` is not reflexive.
  748. return obj !== obj;
  749. };
  750. // Is a given value a boolean?
  751. _.isBoolean = function(obj) {
  752. return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
  753. };
  754. // Is a given value a date?
  755. _.isDate = function(obj) {
  756. return toString.call(obj) == '[object Date]';
  757. };
  758. // Is the given value a regular expression?
  759. _.isRegExp = function(obj) {
  760. return toString.call(obj) == '[object RegExp]';
  761. };
  762. // Is a given value equal to null?
  763. _.isNull = function(obj) {
  764. return obj === null;
  765. };
  766. // Is a given variable undefined?
  767. _.isUndefined = function(obj) {
  768. return obj === void 0;
  769. };
  770. // Has own property?
  771. _.has = function(obj, key) {
  772. return hasOwnProperty.call(obj, key);
  773. };
  774. // Utility Functions
  775. // -----------------
  776. // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
  777. // previous owner. Returns a reference to the Underscore object.
  778. _.noConflict = function() {
  779. root._ = previousUnderscore;
  780. return this;
  781. };
  782. // Keep the identity function around for default iterators.
  783. _.identity = function(value) {
  784. return value;
  785. };
  786. // Run a function **n** times.
  787. _.times = function (n, iterator, context) {
  788. for (var i = 0; i < n; i++) iterator.call(context, i);
  789. };
  790. // Escape a string for HTML interpolation.
  791. _.escape = function(string) {
  792. return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
  793. };
  794. // If the value of the named property is a function then invoke it;
  795. // otherwise, return it.
  796. _.result = function(object, property) {
  797. if (object == null) return null;
  798. var value = object[property];
  799. return _.isFunction(value) ? value.call(object) : value;
  800. };
  801. // Add your own custom functions to the Underscore object, ensuring that
  802. // they're correctly added to the OOP wrapper as well.
  803. _.mixin = function(obj) {
  804. each(_.functions(obj), function(name){
  805. addToWrapper(name, _[name] = obj[name]);
  806. });
  807. };
  808. // Generate a unique integer id (unique within the entire client session).
  809. // Useful for temporary DOM ids.
  810. var idCounter = 0;
  811. _.uniqueId = function(prefix) {
  812. var id = idCounter++;
  813. return prefix ? prefix + id : id;
  814. };
  815. // By default, Underscore uses ERB-style template delimiters, change the
  816. // following template settings to use alternative delimiters.
  817. _.templateSettings = {
  818. evaluate : /<%([\s\S]+?)%>/g,
  819. interpolate : /<%=([\s\S]+?)%>/g,
  820. escape : /<%-([\s\S]+?)%>/g
  821. };
  822. // When customizing `templateSettings`, if you don't want to define an
  823. // interpolation, evaluation or escaping regex, we need one that is
  824. // guaranteed not to match.
  825. var noMatch = /.^/;
  826. // Certain characters need to be escaped so that they can be put into a
  827. // string literal.
  828. var escapes = {
  829. '\\': '\\',
  830. "'": "'",
  831. 'r': '\r',
  832. 'n': '\n',
  833. 't': '\t',
  834. 'u2028': '\u2028',
  835. 'u2029': '\u2029'
  836. };
  837. for (var p in escapes) escapes[escapes[p]] = p;
  838. var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
  839. var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
  840. // Within an interpolation, evaluation, or escaping, remove HTML escaping
  841. // that had been previously added.
  842. var unescape = function(code) {
  843. return code.replace(unescaper, function(match, escape) {
  844. return escapes[escape];
  845. });
  846. };
  847. // JavaScript micro-templating, similar to John Resig's implementation.
  848. // Underscore templating handles arbitrary delimiters, preserves whitespace,
  849. // and correctly escapes quotes within interpolated code.
  850. _.template = function(text, data, settings) {
  851. settings = _.defaults(settings || {}, _.templateSettings);
  852. // Compile the template source, taking care to escape characters that
  853. // cannot be included in a string literal and then unescape them in code
  854. // blocks.
  855. var source = "__p+='" + text
  856. .replace(escaper, function(match) {
  857. return '\\' + escapes[match];
  858. })
  859. .replace(settings.escape || noMatch, function(match, code) {
  860. return "'+\n_.escape(" + unescape(code) + ")+\n'";
  861. })
  862. .replace(settings.interpolate || noMatch, function(match, code) {
  863. return "'+\n(" + unescape(code) + ")+\n'";
  864. })
  865. .replace(settings.evaluate || noMatch, function(match, code) {
  866. return "';\n" + unescape(code) + "\n;__p+='";
  867. }) + "';\n";
  868. // If a variable is not specified, place data values in local scope.
  869. if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
  870. source = "var __p='';" +
  871. "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" +
  872. source + "return __p;\n";
  873. var render = new Function(settings.variable || 'obj', '_', source);
  874. if (data) return render(data, _);
  875. var template = function(data) {
  876. return render.call(this, data, _);
  877. };
  878. // Provide the compiled function source as a convenience for build time
  879. // precompilation.
  880. template.source = 'function(' + (settings.variable || 'obj') + '){\n' +
  881. source + '}';
  882. return template;
  883. };
  884. // Add a "chain" function, which will delegate to the wrapper.
  885. _.chain = function(obj) {
  886. return _(obj).chain();
  887. };
  888. // The OOP Wrapper
  889. // ---------------
  890. // If Underscore is called as a function, it returns a wrapped object that
  891. // can be used OO-style. This wrapper holds altered versions of all the
  892. // underscore functions. Wrapped objects may be chained.
  893. var wrapper = function(obj) { this._wrapped = obj; };
  894. // Expose `wrapper.prototype` as `_.prototype`
  895. _.prototype = wrapper.prototype;
  896. // Helper function to continue chaining intermediate results.
  897. var result = function(obj, chain) {
  898. return chain ? _(obj).chain() : obj;
  899. };
  900. // A method to easily add functions to the OOP wrapper.
  901. var addToWrapper = function(name, func) {
  902. wrapper.prototype[name] = function() {
  903. var args = slice.call(arguments);
  904. unshift.call(args, this._wrapped);
  905. return result(func.apply(_, args), this._chain);
  906. };
  907. };
  908. // Add all of the Underscore functions to the wrapper object.
  909. _.mixin(_);
  910. // Add all mutator Array functions to the wrapper.
  911. each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
  912. var method = ArrayProto[name];
  913. wrapper.prototype[name] = function() {
  914. var wrapped = this._wrapped;
  915. method.apply(wrapped, arguments);
  916. var length = wrapped.length;
  917. if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
  918. return result(wrapped, this._chain);
  919. };
  920. });
  921. // Add all accessor Array functions to the wrapper.
  922. each(['concat', 'join', 'slice'], function(name) {
  923. var method = ArrayProto[name];
  924. wrapper.prototype[name] = function() {
  925. return result(method.apply(this._wrapped, arguments), this._chain);
  926. };
  927. });
  928. // Start chaining a wrapped Underscore object.
  929. wrapper.prototype.chain = function() {
  930. this._chain = true;
  931. return this;
  932. };
  933. // Extracts the result from a wrapped and chained object.
  934. wrapper.prototype.value = function() {
  935. return this._wrapped;
  936. };
  937. return _;
  938. }).call({});
  939. /**
  940. * Core Emmet object, available in global scope
  941. */
  942. var emmet = (function(global) {
  943. var defaultSyntax = 'html';
  944. var defaultProfile = 'plain';
  945. if (typeof _ == 'undefined') {
  946. try {
  947. // avoid collisions with RequireJS loader
  948. // also, JS obfuscators tends to translate
  949. // a["name"] to a.name, which also breaks RequireJS
  950. _ = global[['require'][0]]('underscore'); // node.js
  951. } catch (e) {}
  952. }
  953. if (typeof _ == 'undefined') {
  954. throw 'Cannot access to Underscore.js lib';
  955. }
  956. /** List of registered modules */
  957. var modules = {
  958. _ : _
  959. };
  960. /**
  961. * Shared empty constructor function to aid in prototype-chain creation.
  962. */
  963. var ctor = function(){};
  964. /**
  965. * Helper function to correctly set up the prototype chain, for subclasses.
  966. * Similar to `goog.inherits`, but uses a hash of prototype properties and
  967. * class properties to be extended.
  968. * Took it from Backbone.
  969. * @param {Object} parent
  970. * @param {Object} protoProps
  971. * @param {Object} staticProps
  972. * @returns {Object}
  973. */
  974. function inherits(parent, protoProps, staticProps) {
  975. var child;
  976. // The constructor function for the new subclass is either defined by
  977. // you (the "constructor" property in your `extend` definition), or
  978. // defaulted by us to simply call the parent's constructor.
  979. if (protoProps && protoProps.hasOwnProperty('constructor')) {
  980. child = protoProps.constructor;
  981. } else {
  982. child = function() {
  983. parent.apply(this, arguments);
  984. };
  985. }
  986. // Inherit class (static) properties from parent.
  987. _.extend(child, parent);
  988. // Set the prototype chain to inherit from `parent`, without calling
  989. // `parent`'s constructor function.
  990. ctor.prototype = parent.prototype;
  991. child.prototype = new ctor();
  992. // Add prototype properties (instance properties) to the subclass,
  993. // if supplied.
  994. if (protoProps)
  995. _.extend(child.prototype, protoProps);
  996. // Add static properties to the constructor function, if supplied.
  997. if (staticProps)
  998. _.extend(child, staticProps);
  999. // Correctly set child's `prototype.constructor`.
  1000. child.prototype.constructor = child;
  1001. // Set a convenience property in case the parent's prototype is needed
  1002. // later.
  1003. child.__super__ = parent.prototype;
  1004. return child;
  1005. };
  1006. /**
  1007. * @type Function Function that loads module definition if it's not defined
  1008. */
  1009. var moduleLoader = null;
  1010. /**
  1011. * Generic Emmet module loader (actually, it doesn’t load anything, just
  1012. * returns module reference). Not using `require` name to avoid conflicts
  1013. * with Node.js and RequireJS
  1014. */
  1015. function r(name) {
  1016. if (!(name in modules) && moduleLoader)
  1017. moduleLoader(name);
  1018. return modules[name];
  1019. }
  1020. return {
  1021. /**
  1022. * Simple, AMD-like module definition. The module will be added into
  1023. * <code>emmet</code> object and will be available via
  1024. * <code>emmet.require(name)</code> or <code>emmet[name]</code>
  1025. * @param {String} name
  1026. * @param {Function} factory
  1027. * @memberOf emmet
  1028. */
  1029. define: function(name, factory) {
  1030. // do not let redefine existing properties
  1031. if (!(name in modules)) {
  1032. modules[name] = _.isFunction(factory)
  1033. ? this.exec(factory)
  1034. : factory;
  1035. }
  1036. },
  1037. /**
  1038. * Returns reference to Emmet module
  1039. * @param {String} name Module name
  1040. */
  1041. require: r,
  1042. /**
  1043. * Helper method that just executes passed function but with all
  1044. * important arguments like 'require' and '_'
  1045. * @param {Function} fn
  1046. * @param {Object} context Execution context
  1047. */
  1048. exec: function(fn, context) {
  1049. return fn.call(context || global, _.bind(r, this), _, this);
  1050. },
  1051. /**
  1052. * The self-propagating extend function for classes.
  1053. * Took it from Backbone
  1054. * @param {Object} protoProps
  1055. * @param {Object} classProps
  1056. * @returns {Object}
  1057. */
  1058. extend: function(protoProps, classProps) {
  1059. var child = inherits(this, protoProps, classProps);
  1060. child.extend = this.extend;
  1061. // a hack required to WSH inherit `toString` method
  1062. if (protoProps.hasOwnProperty('toString'))
  1063. child.prototype.toString = protoProps.toString;
  1064. return child;
  1065. },
  1066. /**
  1067. * The essential function that expands Emmet abbreviation
  1068. * @param {String} abbr Abbreviation to parse
  1069. * @param {String} syntax Abbreviation's context syntax
  1070. * @param {String} profile Output profile (or its name)
  1071. * @param {Object} contextNode Contextual node where abbreviation is
  1072. * written
  1073. * @return {String}
  1074. */
  1075. expandAbbreviation: function(abbr, syntax, profile, contextNode) {
  1076. if (!abbr) return '';
  1077. syntax = syntax || defaultSyntax;
  1078. // profile = profile || defaultProfile;
  1079. var filters = r('filters');
  1080. var parser = r('abbreviationParser');
  1081. profile = r('profile').get(profile, syntax);
  1082. r('tabStops').resetTabstopIndex();
  1083. var data = filters.extractFromAbbreviation(abbr);
  1084. var outputTree = parser.parse(data[0], {
  1085. syntax: syntax,
  1086. contextNode: contextNode
  1087. });
  1088. var filtersList = filters.composeList(syntax, profile, data[1]);
  1089. filters.apply(outputTree, filtersList, profile);
  1090. return outputTree.toString();
  1091. },
  1092. /**
  1093. * Returns default syntax name used in abbreviation engine
  1094. * @returns {String}
  1095. */
  1096. defaultSyntax: function() {
  1097. return defaultSyntax;
  1098. },
  1099. /**
  1100. * Returns default profile name used in abbreviation engine
  1101. * @returns {String}
  1102. */
  1103. defaultProfile: function() {
  1104. return defaultProfile;
  1105. },
  1106. /**
  1107. * Log message into console if it exists
  1108. */
  1109. log: function() {
  1110. if (global.console && global.console.log)
  1111. global.console.log.apply(global.console, arguments);
  1112. },
  1113. /**
  1114. * Setups function that should synchronously load undefined modules
  1115. * @param {Function} fn
  1116. */
  1117. setModuleLoader: function(fn) {
  1118. moduleLoader = fn;
  1119. }
  1120. };
  1121. })(this);
  1122. // export core for Node.JS
  1123. if (typeof exports !== 'undefined') {
  1124. if (typeof module !== 'undefined' && module.exports) {
  1125. exports = module.exports = emmet;
  1126. }
  1127. exports.emmet = emmet;
  1128. }
  1129. // export as Require.js module
  1130. if (typeof define !== 'undefined') {
  1131. define("emmet", [], emmet);
  1132. }/**
  1133. * Emmet abbreviation parser.
  1134. * Takes string abbreviation and recursively parses it into a tree. The parsed
  1135. * tree can be transformed into a string representation with
  1136. * <code>toString()</code> method. Note that string representation is defined
  1137. * by custom processors (called <i>filters</i>), not by abbreviation parser
  1138. * itself.
  1139. *
  1140. * This module can be extended with custom pre-/post-processors to shape-up
  1141. * final tree or its representation. Actually, many features of abbreviation
  1142. * engine are defined in other modules as tree processors
  1143. *
  1144. *
  1145. * @author Sergey Chikuyonok (serge.che@gmail.com)
  1146. * @link http://chikuyonok.ru
  1147. * @memberOf __abbreviationParser
  1148. * @constructor
  1149. * @param {Function} require
  1150. * @param {Underscore} _
  1151. */
  1152. emmet.define('abbreviationParser', function(require, _) {
  1153. var reValidName = /^[\w\-\$\:@\!%]+\+?$/i;
  1154. var reWord = /[\w\-:\$@]/;
  1155. var pairs = {
  1156. '[': ']',
  1157. '(': ')',
  1158. '{': '}'
  1159. };
  1160. var spliceFn = Array.prototype.splice;
  1161. var preprocessors = [];
  1162. var postprocessors = [];
  1163. var outputProcessors = [];
  1164. /**
  1165. * @type AbbreviationNode
  1166. */
  1167. function AbbreviationNode(parent) {
  1168. /** @type AbbreviationNode */
  1169. this.parent = null;
  1170. this.children = [];
  1171. this._attributes = [];
  1172. /** @type String Raw abbreviation for current node */
  1173. this.abbreviation = '';
  1174. this.counter = 1;
  1175. this._name = null;
  1176. this._text = '';
  1177. this.repeatCount = 1;
  1178. this.hasImplicitRepeat = false;
  1179. /** Custom data dictionary */
  1180. this._data = {};
  1181. // output properties
  1182. this.start = '';
  1183. this.end = '';
  1184. this.content = '';
  1185. this.padding = '';
  1186. }
  1187. AbbreviationNode.prototype = {
  1188. /**
  1189. * Adds passed node as child or creates new child
  1190. * @param {AbbreviationNode} child
  1191. * @param {Number} position Index in children array where child should
  1192. * be inserted
  1193. * @return {AbbreviationNode}
  1194. */
  1195. addChild: function(child, position) {
  1196. child = child || new AbbreviationNode;
  1197. child.parent = this;
  1198. if (_.isUndefined(position)) {
  1199. this.children.push(child);
  1200. } else {
  1201. this.children.splice(position, 0, child);
  1202. }
  1203. return child;
  1204. },
  1205. /**
  1206. * Creates a deep copy of current node
  1207. * @returns {AbbreviationNode}
  1208. */
  1209. clone: function() {
  1210. var node = new AbbreviationNode();
  1211. var attrs = ['abbreviation', 'counter', '_name', '_text', 'repeatCount', 'hasImplicitRepeat', 'start', 'end', 'content', 'padding'];
  1212. _.each(attrs, function(a) {
  1213. node[a] = this[a];
  1214. }, this);
  1215. // clone attributes
  1216. node._attributes = _.map(this._attributes, function(attr) {
  1217. return _.clone(attr);
  1218. });
  1219. node._data = _.clone(this._data);
  1220. // clone children
  1221. node.children = _.map(this.children, function(child) {
  1222. child = child.clone();
  1223. child.parent = node;
  1224. return child;
  1225. });
  1226. return node;
  1227. },
  1228. /**
  1229. * Removes current node from parent‘s child list
  1230. * @returns {AbbreviationNode} Current node itself
  1231. */
  1232. remove: function() {
  1233. if (this.parent) {
  1234. this.parent.children = _.without(this.parent.children, this);
  1235. }
  1236. return this;
  1237. },
  1238. /**
  1239. * Replaces current node in parent‘s children list with passed nodes
  1240. * @param {AbbreviationNode} node Replacement node or array of nodes
  1241. */
  1242. replace: function() {
  1243. var parent = this.parent;
  1244. var ix = _.indexOf(parent.children, this);
  1245. var items = _.flatten(arguments);
  1246. spliceFn.apply(parent.children, [ix, 1].concat(items));
  1247. // update parent
  1248. _.each(items, function(item) {
  1249. item.parent = parent;
  1250. });
  1251. },
  1252. /**
  1253. * Recursively sets <code>property</code> to <code>value</code> of current
  1254. * node and its children
  1255. * @param {String} name Property to update
  1256. * @param {Object} value New property value
  1257. */
  1258. updateProperty: function(name, value) {
  1259. this[name] = value;
  1260. _.each(this.children, function(child) {
  1261. child.updateProperty(name, value);
  1262. });
  1263. return this;
  1264. },
  1265. /**
  1266. * Finds first child node that matches truth test for passed
  1267. * <code>fn</code> function
  1268. * @param {Function} fn
  1269. * @returns {AbbreviationNode}
  1270. */
  1271. find: function(fn) {
  1272. return this.findAll(fn)[0];
  1273. // if (!_.isFunction(fn)) {
  1274. // var elemName = fn.toLowerCase();
  1275. // fn = function(item) {return item.name().toLowerCase() == elemName;};
  1276. // }
  1277. //
  1278. // var result = null;
  1279. // _.find(this.children, function(child) {
  1280. // if (fn(child)) {
  1281. // return result = child;
  1282. // }
  1283. //
  1284. // return result = child.find(fn);
  1285. // });
  1286. //
  1287. // return result;
  1288. },
  1289. /**
  1290. * Finds all child nodes that matches truth test for passed
  1291. * <code>fn</code> function
  1292. * @param {Function} fn
  1293. * @returns {Array}
  1294. */
  1295. findAll: function(fn) {
  1296. if (!_.isFunction(fn)) {
  1297. var elemName = fn.toLowerCase();
  1298. fn = function(item) {return item.name().toLowerCase() == elemName;};
  1299. }
  1300. var result = [];
  1301. _.each(this.children, function(child) {
  1302. if (fn(child))
  1303. result.push(child);
  1304. result = result.concat(child.findAll(fn));
  1305. });
  1306. return _.compact(result);
  1307. },
  1308. /**
  1309. * Sets/gets custom data
  1310. * @param {String} name
  1311. * @param {Object} value
  1312. * @returns {Object}
  1313. */
  1314. data: function(name, value) {
  1315. if (arguments.length == 2) {
  1316. this._data[name] = value;
  1317. if (name == 'resource' && require('elements').is(value, 'snippet')) {
  1318. // setting snippet as matched resource: update `content`
  1319. // property with snippet value
  1320. this.content = value.data;
  1321. if (this._text) {
  1322. this.content = require('abbreviationUtils')
  1323. .insertChildContent(value.data, this._text);
  1324. }
  1325. }
  1326. }
  1327. return this._data[name];
  1328. },
  1329. /**
  1330. * Returns name of current node
  1331. * @returns {String}
  1332. */
  1333. name: function() {
  1334. var res = this.matchedResource();
  1335. if (require('elements').is(res, 'element')) {
  1336. return res.name;
  1337. }
  1338. return this._name;
  1339. },
  1340. /**
  1341. * Returns list of attributes for current node
  1342. * @returns {Array}
  1343. */
  1344. attributeList: function() {
  1345. var attrs = [];
  1346. var res = this.matchedResource();
  1347. if (require('elements').is(res, 'element') && _.isArray(res.attributes)) {
  1348. attrs = attrs.concat(res.attributes);
  1349. }
  1350. return optimizeAttributes(attrs.concat(this._attributes));
  1351. },
  1352. /**
  1353. * Returns or sets attribute value
  1354. * @param {String} name Attribute name
  1355. * @param {String} value New attribute value
  1356. * @returns {String}
  1357. */
  1358. attribute: function(name, value) {
  1359. if (arguments.length == 2) {
  1360. // modifying attribute
  1361. var ix = _.indexOf(_.pluck(this._attributes, 'name'), name.toLowerCase());
  1362. if (~ix) {
  1363. this._attributes[ix].value = value;
  1364. } else {
  1365. this._attributes.push({
  1366. name: name,
  1367. value: value
  1368. });
  1369. }
  1370. }
  1371. return (_.find(this.attributeList(), function(attr) {
  1372. return attr.name == name;
  1373. }) || {}).value;
  1374. },
  1375. /**
  1376. * Returns reference to the matched <code>element</code>, if any.
  1377. * See {@link elements} module for a list of available elements
  1378. * @returns {Object}
  1379. */
  1380. matchedResource: function() {
  1381. return this.data('resource');
  1382. },
  1383. /**
  1384. * Returns index of current node in parent‘s children list
  1385. * @returns {Number}
  1386. */
  1387. index: function() {
  1388. return this.parent ? _.indexOf(this.parent.children, this) : -1;
  1389. },
  1390. /**
  1391. * Sets how many times current element should be repeated
  1392. * @private
  1393. */
  1394. _setRepeat: function(count) {
  1395. if (count) {
  1396. this.repeatCount = parseInt(count, 10) || 1;
  1397. } else {
  1398. this.hasImplicitRepeat = true;
  1399. }
  1400. },
  1401. /**
  1402. * Sets abbreviation that belongs to current node
  1403. * @param {String} abbr
  1404. */
  1405. setAbbreviation: function(abbr) {
  1406. abbr = abbr || '';
  1407. var that = this;
  1408. // find multiplier
  1409. abbr = abbr.replace(/\*(\d+)?$/, function(str, repeatCount) {
  1410. that._setRepeat(repeatCount);
  1411. return '';
  1412. });
  1413. this.abbreviation = abbr;
  1414. var abbrText = extractText(abbr);
  1415. if (abbrText) {
  1416. abbr = abbrText.element;
  1417. this.content = this._text = abbrText.text;
  1418. }
  1419. var abbrAttrs = parseAttributes(abbr);
  1420. if (abbrAttrs) {
  1421. abbr = abbrAttrs.element;
  1422. this._attributes = abbrAttrs.attributes;
  1423. }
  1424. this._name = abbr;
  1425. // validate name
  1426. if (this._name && !reValidName.test(this._name)) {
  1427. throw 'Invalid abbreviation';
  1428. }
  1429. },
  1430. /**
  1431. * Returns string representation of current node
  1432. * @return {String}
  1433. */
  1434. toString: function() {
  1435. var utils = require('utils');
  1436. var start = this.start;
  1437. var end = this.end;
  1438. var content = this.content;
  1439. // apply output processors
  1440. var node = this;
  1441. _.each(outputProcessors, function(fn) {
  1442. start = fn(start, node, 'start');
  1443. content = fn(content, node, 'content');
  1444. end = fn(end, node, 'end');
  1445. });
  1446. var innerContent = _.map(this.children, function(child) {
  1447. return child.toString();
  1448. }).join('');
  1449. content = require('abbreviationUtils').insertChildContent(content, innerContent, {
  1450. keepVariable: false
  1451. });
  1452. return start + utils.padString(content, this.padding) + end;
  1453. },
  1454. /**
  1455. * Check if current node contains children with empty <code>expr</code>
  1456. * property
  1457. * @return {Boolean}
  1458. */
  1459. hasEmptyChildren: function() {
  1460. return !!_.find(this.children, function(child) {
  1461. return child.isEmpty();
  1462. });
  1463. },
  1464. /**
  1465. * Check if current node has implied name that should be resolved
  1466. * @returns {Boolean}
  1467. */
  1468. hasImplicitName: function() {
  1469. return !this._name && !this.isTextNode();
  1470. },
  1471. /**
  1472. * Indicates that current element is a grouping one, e.g. has no
  1473. * representation but serves as a container for other nodes
  1474. * @returns {Boolean}
  1475. */
  1476. isGroup: function() {
  1477. return !this.abbreviation;
  1478. },
  1479. /**
  1480. * Indicates empty node (i.e. without abbreviation). It may be a
  1481. * grouping node and should not be outputted
  1482. * @return {Boolean}
  1483. */
  1484. isEmpty: function() {
  1485. return !this.abbreviation && !this.children.length;
  1486. },
  1487. /**
  1488. * Indicates that current node should be repeated
  1489. * @returns {Boolean}
  1490. */
  1491. isRepeating: function() {
  1492. return this.repeatCount > 1 || this.hasImplicitRepeat;
  1493. },
  1494. /**
  1495. * Check if current node is a text-only node
  1496. * @return {Boolean}
  1497. */
  1498. isTextNode: function() {
  1499. return !this.name() && !this.attributeList().length;
  1500. },
  1501. /**
  1502. * Indicates whether this node may be used to build elements or snippets
  1503. * @returns {Boolean}
  1504. */
  1505. isElement: function() {
  1506. return !this.isEmpty() && !this.isTextNode();
  1507. },
  1508. /**
  1509. * Returns latest and deepest child of current tree
  1510. * @returns {AbbreviationNode}
  1511. */
  1512. deepestChild: function() {
  1513. if (!this.children.length)
  1514. return null;
  1515. var deepestChild = this;
  1516. while (deepestChild.children.length) {
  1517. deepestChild = _.last(deepestChild.children);
  1518. }
  1519. return deepestChild;
  1520. }
  1521. };
  1522. /**
  1523. * Returns stripped string: a string without first and last character.
  1524. * Used for “unquoting” strings
  1525. * @param {String} str
  1526. * @returns {String}
  1527. */
  1528. function stripped(str) {
  1529. return str.substring(1, str.length - 1);
  1530. }
  1531. function consumeQuotedValue(stream, quote) {
  1532. var ch;
  1533. while (ch = stream.next()) {
  1534. if (ch === quote)
  1535. return true;
  1536. if (ch == '\\')
  1537. continue;
  1538. }
  1539. return false;
  1540. }
  1541. /**
  1542. * Parses abbreviation into a tree
  1543. * @param {String} abbr
  1544. * @returns {AbbreviationNode}
  1545. */
  1546. function parseAbbreviation(abbr) {
  1547. abbr = require('utils').trim(abbr);
  1548. var root = new AbbreviationNode;
  1549. var context = root.addChild(), ch;
  1550. /** @type StringStream */
  1551. var stream = require('stringStream').create(abbr);
  1552. var loopProtector = 1000, multiplier;
  1553. while (!stream.eol() && --loopProtector > 0) {
  1554. ch = stream.peek();
  1555. switch (ch) {
  1556. case '(': // abbreviation group
  1557. stream.start = stream.pos;
  1558. if (stream.skipToPair('(', ')')) {
  1559. var inner = parseAbbreviation(stripped(stream.current()));
  1560. if (multiplier = stream.match(/^\*(\d+)?/, true)) {
  1561. context._setRepeat(multiplier[1]);
  1562. }
  1563. _.each(inner.children, function(child) {
  1564. context.addChild(child);
  1565. });
  1566. } else {
  1567. throw 'Invalid abbreviation: mo matching ")" found for character at ' + stream.pos;
  1568. }
  1569. break;
  1570. case '>': // child operator
  1571. context = context.addChild();
  1572. stream.next();
  1573. break;
  1574. case '+': // sibling operator
  1575. context = context.parent.addChild();
  1576. stream.next();
  1577. break;
  1578. case '^': // climb up operator
  1579. var parent = context.parent || context;
  1580. context = (parent.parent || parent).addChild();
  1581. stream.next();
  1582. break;
  1583. default: // consume abbreviation
  1584. stream.start = stream.pos;
  1585. stream.eatWhile(function(c) {
  1586. if (c == '[' || c == '{') {
  1587. if (stream.skipToPair(c, pairs[c])) {
  1588. stream.backUp(1);
  1589. return true;
  1590. }
  1591. throw 'Invalid abbreviation: mo matching "' + pairs[c] + '" found for character at ' + stream.pos;
  1592. }
  1593. if (c == '+') {
  1594. // let's see if this is an expando marker
  1595. stream.next();
  1596. var isMarker = stream.eol() || ~'+>^*'.indexOf(stream.peek());
  1597. stream.backUp(1);
  1598. return isMarker;
  1599. }
  1600. return c != '(' && isAllowedChar(c);
  1601. });
  1602. context.setAbbreviation(stream.current());
  1603. stream.start = stream.pos;
  1604. }
  1605. }
  1606. if (loopProtector < 1)
  1607. throw 'Endless loop detected';
  1608. return root;
  1609. }
  1610. /**
  1611. * Extract attributes and their values from attribute set:
  1612. * <code>[attr col=3 title="Quoted string"]</code>
  1613. * @param {String} attrSet
  1614. * @returns {Array}
  1615. */
  1616. function extractAttributes(attrSet, attrs) {
  1617. attrSet = require('utils').trim(attrSet);
  1618. var result = [];
  1619. /** @type StringStream */
  1620. var stream = require('stringStream').create(attrSet);
  1621. stream.eatSpace();
  1622. while (!stream.eol()) {
  1623. stream.start = stream.pos;
  1624. if (stream.eatWhile(reWord)) {
  1625. var attrName = stream.current();
  1626. var attrValue = '';
  1627. if (stream.peek() == '=') {
  1628. stream.next();
  1629. stream.start = stream.pos;
  1630. var quote = stream.peek();
  1631. if (quote == '"' || quote == "'") {
  1632. stream.next();
  1633. if (consumeQuotedValue(stream, quote)) {
  1634. attrValue = stream.current();
  1635. // strip quotes
  1636. attrValue = attrValue.substring(1, attrValue.length - 1);
  1637. } else {
  1638. throw 'Invalid attribute value';
  1639. }
  1640. } else if (stream.eatWhile(/[^\s\]]/)) {
  1641. attrValue = stream.current();
  1642. } else {
  1643. throw 'Invalid attribute value';
  1644. }
  1645. }
  1646. result.push({
  1647. name: attrName,
  1648. value: attrValue
  1649. });
  1650. stream.eatSpace();
  1651. } else {
  1652. break;
  1653. }
  1654. }
  1655. return result;
  1656. }
  1657. /**
  1658. * Parses tag attributes extracted from abbreviation. If attributes found,
  1659. * returns object with <code>element</code> and <code>attributes</code>
  1660. * properties
  1661. * @param {String} abbr
  1662. * @returns {Object} Returns <code>null</code> if no attributes found in
  1663. * abbreviation
  1664. */
  1665. function parseAttributes(abbr) {
  1666. /*
  1667. * Example of incoming data:
  1668. * #header
  1669. * .some.data
  1670. * .some.data#header
  1671. * [attr]
  1672. * #item[attr=Hello other="World"].class
  1673. */
  1674. var result = [];
  1675. var attrMap = {'#': 'id', '.': 'class'};
  1676. var nameEnd = null;
  1677. /** @type StringStream */
  1678. var stream = require('stringStream').create(abbr);
  1679. while (!stream.eol()) {
  1680. switch (stream.peek()) {
  1681. case '#': // id
  1682. case '.': // class
  1683. if (nameEnd === null)
  1684. nameEnd = stream.pos;
  1685. var attrName = attrMap[stream.peek()];
  1686. stream.next();
  1687. stream.start = stream.pos;
  1688. stream.eatWhile(reWord);
  1689. result.push({
  1690. name: attrName,
  1691. value: stream.current()
  1692. });
  1693. break;
  1694. case '[': //begin attribute set
  1695. if (nameEnd === null)
  1696. nameEnd = stream.pos;
  1697. stream.start = stream.pos;
  1698. if (!stream.skipToPair('[', ']'))
  1699. throw 'Invalid attribute set definition';
  1700. result = result.concat(
  1701. extractAttributes(stripped(stream.current()))
  1702. );
  1703. break;
  1704. default:
  1705. stream.next();
  1706. }
  1707. }
  1708. if (!result.length)
  1709. return null;
  1710. return {
  1711. element: abbr.substring(0, nameEnd),
  1712. attributes: optimizeAttributes(result)
  1713. };
  1714. }
  1715. /**
  1716. * Optimize attribute set: remove duplicates and merge class attributes
  1717. * @param attrs
  1718. */
  1719. function optimizeAttributes(attrs) {
  1720. // clone all attributes to make sure that original objects are
  1721. // not modified
  1722. attrs = _.map(attrs, function(attr) {
  1723. return _.clone(attr);
  1724. });
  1725. var lookup = {};
  1726. return _.filter(attrs, function(attr) {
  1727. if (!(attr.name in lookup)) {
  1728. return lookup[attr.name] = attr;
  1729. }
  1730. var la = lookup[attr.name];
  1731. if (attr.name.toLowerCase() == 'class') {
  1732. la.value += (la.value.length ? ' ' : '') + attr.value;
  1733. } else {
  1734. la.value = attr.value;
  1735. }
  1736. return false;
  1737. });
  1738. }
  1739. /**
  1740. * Extract text data from abbreviation: if <code>a{hello}</code> abbreviation
  1741. * is passed, returns object <code>{element: 'a', text: 'hello'}</code>.
  1742. * If nothing found, returns <code>null</code>
  1743. * @param {String} abbr
  1744. *
  1745. */
  1746. function extractText(abbr) {
  1747. if (!~abbr.indexOf('{'))
  1748. return null;
  1749. /** @type StringStream */
  1750. var stream = require('stringStream').create(abbr);
  1751. while (!stream.eol()) {
  1752. switch (stream.peek()) {
  1753. case '[':
  1754. case '(':
  1755. stream.skipToPair(stream.peek(), pairs[stream.peek()]); break;
  1756. case '{':
  1757. stream.start = stream.pos;
  1758. stream.skipToPair('{', '}');
  1759. return {
  1760. element: abbr.substring(0, stream.start),
  1761. text: stripped(stream.current())
  1762. };
  1763. default:
  1764. stream.next();
  1765. }
  1766. }
  1767. }
  1768. /**
  1769. * “Un-rolls“ contents of current node: recursively replaces all repeating
  1770. * children with their repeated clones
  1771. * @param {AbbreviationNode} node
  1772. * @returns {AbbreviationNode}
  1773. */
  1774. function unroll(node) {
  1775. for (var i = node.children.length - 1, j, child, maxCount; i >= 0; i--) {
  1776. child = node.children[i];
  1777. if (child.isRepeating()) {
  1778. maxCount = j = child.repeatCount;
  1779. child.repeatCount = 1;
  1780. child.updateProperty('counter', 1);
  1781. child.updateProperty('maxCount', maxCount);
  1782. while (--j > 0) {
  1783. child.parent.addChild(child.clone(), i + 1)
  1784. .updateProperty('counter', j + 1)
  1785. .updateProperty('maxCount', maxCount);
  1786. }
  1787. }
  1788. }
  1789. // to keep proper 'counter' property, we need to walk
  1790. // on children once again
  1791. _.each(node.children, unroll);
  1792. return node;
  1793. }
  1794. /**
  1795. * Optimizes tree node: replaces empty nodes with their children
  1796. * @param {AbbreviationNode} node
  1797. * @return {AbbreviationNode}
  1798. */
  1799. function squash(node) {
  1800. for (var i = node.children.length - 1; i >= 0; i--) {
  1801. /** @type AbbreviationNode */
  1802. var n = node.children[i];
  1803. if (n.isGroup()) {
  1804. n.replace(squash(n).children);
  1805. } else if (n.isEmpty()) {
  1806. n.remove();
  1807. }
  1808. }
  1809. _.each(node.children, squash);
  1810. return node;
  1811. }
  1812. function isAllowedChar(ch) {
  1813. var charCode = ch.charCodeAt(0);
  1814. var specialChars = '#.*:$-_!@|%';
  1815. return (charCode > 64 && charCode < 91) // uppercase letter
  1816. || (charCode > 96 && charCode < 123) // lowercase letter
  1817. || (charCode > 47 && charCode < 58) // number
  1818. || specialChars.indexOf(ch) != -1; // special character
  1819. }
  1820. // XXX add counter replacer function as output processor
  1821. outputProcessors.push(function(text, node) {
  1822. return require('utils').replaceCounter(text, node.counter, node.maxCount);
  1823. });
  1824. return {
  1825. /**
  1826. * Parses abbreviation into tree with respect of groups,
  1827. * text nodes and attributes. Each node of the tree is a single
  1828. * abbreviation. Tree represents actual structure of the outputted
  1829. * result
  1830. * @memberOf abbreviationParser
  1831. * @param {String} abbr Abbreviation to parse
  1832. * @param {Object} options Additional options for parser and processors
  1833. *
  1834. * @return {AbbreviationNode}
  1835. */
  1836. parse: function(abbr, options) {
  1837. options = options || {};
  1838. var tree = parseAbbreviation(abbr);
  1839. if (options.contextNode) {
  1840. // add info about context node –
  1841. // a parent XHTML node in editor inside which abbreviation is
  1842. // expanded
  1843. tree._name = options.contextNode.name;
  1844. var attrLookup = {};
  1845. _.each(tree._attributes, function(attr) {
  1846. attrLookup[attr.name] = attr;
  1847. });
  1848. _.each(options.contextNode.attributes, function(attr) {
  1849. if (attr.name in attrLookup) {
  1850. attrLookup[attr.name].value = attr.value;
  1851. } else {
  1852. attr = _.clone(attr);
  1853. tree._attributes.push(attr);
  1854. attrLookup[attr.name] = attr;
  1855. }
  1856. });
  1857. }
  1858. // apply preprocessors
  1859. _.each(preprocessors, function(fn) {
  1860. fn(tree, options);
  1861. });
  1862. tree = squash(unroll(tree));
  1863. // apply postprocessors
  1864. _.each(postprocessors, function(fn) {
  1865. fn(tree, options);
  1866. });
  1867. return tree;
  1868. },
  1869. AbbreviationNode: AbbreviationNode,
  1870. /**
  1871. * Add new abbreviation preprocessor. <i>Preprocessor</i> is a function
  1872. * that applies to a parsed abbreviation tree right after it get parsed.
  1873. * The passed tree is in unoptimized state.
  1874. * @param {Function} fn Preprocessor function. This function receives
  1875. * two arguments: parsed abbreviation tree (<code>AbbreviationNode</code>)
  1876. * and <code>options</code> hash that was passed to <code>parse</code>
  1877. * method
  1878. */
  1879. addPreprocessor: function(fn) {
  1880. if (!_.include(preprocessors, fn))
  1881. preprocessors.push(fn);
  1882. },
  1883. /**
  1884. * Removes registered preprocessor
  1885. */
  1886. removeFilter: function(fn) {
  1887. preprocessor = _.without(preprocessors, fn);
  1888. },
  1889. /**
  1890. * Adds new abbreviation postprocessor. <i>Postprocessor</i> is a
  1891. * functinon that applies to <i>optimized</i> parsed abbreviation tree
  1892. * right before it returns from <code>parse()</code> method
  1893. * @param {Function} fn Postprocessor function. This function receives
  1894. * two arguments: parsed abbreviation tree (<code>AbbreviationNode</code>)
  1895. * and <code>options</code> hash that was passed to <code>parse</code>
  1896. * method
  1897. */
  1898. addPostprocessor: function(fn) {
  1899. if (!_.include(postprocessors, fn))
  1900. postprocessors.push(fn);
  1901. },
  1902. /**
  1903. * Removes registered postprocessor function
  1904. */
  1905. removePostprocessor: function(fn) {
  1906. postprocessors = _.without(postprocessors, fn);
  1907. },
  1908. /**
  1909. * Registers output postprocessor. <i>Output processor</i> is a
  1910. * function that applies to output part (<code>start</code>,
  1911. * <code>end</code> and <code>content</code>) when
  1912. * <code>AbbreviationNode.toString()</code> method is called
  1913. */
  1914. addOutputProcessor: function(fn) {
  1915. if (!_.include(outputProcessors, fn))
  1916. outputProcessors.push(fn);
  1917. },
  1918. /**
  1919. * Removes registered output processor
  1920. */
  1921. removeOutputProcessor: function(fn) {
  1922. outputProcessors = _.without(outputProcessors, fn);
  1923. },
  1924. /**
  1925. * Check if passed symbol is valid symbol for abbreviation expression
  1926. * @param {String} ch
  1927. * @return {Boolean}
  1928. */
  1929. isAllowedChar: function(ch) {
  1930. ch = String(ch); // convert Java object to JS
  1931. return isAllowedChar(ch) || ~'>+^[](){}'.indexOf(ch);
  1932. }
  1933. };
  1934. });/**
  1935. * Processor function that matches parsed <code>AbbreviationNode</code>
  1936. * against resources defined in <code>resource</code> module
  1937. * @param {Function} require
  1938. * @param {Underscore} _
  1939. */
  1940. emmet.exec(function(require, _) {
  1941. /**
  1942. * Finds matched resources for child nodes of passed <code>node</code>
  1943. * element. A matched resource is a reference to <i>snippets.json</i> entry
  1944. * that describes output of parsed node
  1945. * @param {AbbreviationNode} node
  1946. * @param {String} syntax
  1947. */
  1948. function matchResources(node, syntax) {
  1949. var resources = require('resources');
  1950. var elements = require('elements');
  1951. var parser = require('abbreviationParser');
  1952. // do a shallow copy because the children list can be modified during
  1953. // resource matching
  1954. _.each(_.clone(node.children), /** @param {AbbreviationNode} child */ function(child) {
  1955. var r = resources.getMatchedResource(child, syntax);
  1956. if (_.isString(r)) {
  1957. child.data('resource', elements.create('snippet', r));
  1958. } else if (elements.is(r, 'reference')) {
  1959. // it’s a reference to another abbreviation:
  1960. // parse it and insert instead of current child
  1961. /** @type AbbreviationNode */
  1962. var subtree = parser.parse(r.data, {
  1963. syntax: syntax
  1964. });
  1965. // if context element should be repeated, check if we need to
  1966. // transfer repeated element to specific child node
  1967. if (child.repeatCount > 1) {
  1968. var repeatedChildren = subtree.findAll(function(node) {
  1969. return node.hasImplicitRepeat;
  1970. });
  1971. _.each(repeatedChildren, function(node) {
  1972. node.repeatCount = child.repeatCount;
  1973. node.hasImplicitRepeat = false;
  1974. });
  1975. }
  1976. // move child‘s children into the deepest child of new subtree
  1977. var deepestChild = subtree.deepestChild();
  1978. if (deepestChild) {
  1979. _.each(child.children, function(c) {
  1980. deepestChild.addChild(c);
  1981. });
  1982. }
  1983. // copy current attributes to children
  1984. _.each(subtree.children, function(node) {
  1985. _.each(child.attributeList(), function(attr) {
  1986. node.attribute(attr.name, attr.value);
  1987. });
  1988. });
  1989. child.replace(subtree.children);
  1990. } else {
  1991. child.data('resource', r);
  1992. }
  1993. matchResources(child, syntax);
  1994. });
  1995. }
  1996. // XXX register abbreviation filter that creates references to resources
  1997. // on abbreviation nodes
  1998. /**
  1999. * @param {AbbreviationNode} tree
  2000. */
  2001. require('abbreviationParser').addPreprocessor(function(tree, options) {
  2002. var syntax = options.syntax || emmet.defaultSyntax();
  2003. matchResources(tree, syntax);
  2004. });
  2005. });/**
  2006. * Pasted content abbreviation processor. A pasted content is a content that
  2007. * should be inserted into implicitly repeated abbreviation nodes.
  2008. * This processor powers “Wrap With Abbreviation” action
  2009. * @param {Function} require
  2010. * @param {Underscore} _
  2011. */
  2012. emmet.exec(function(require, _) {
  2013. var parser = require('abbreviationParser');
  2014. var outputPlaceholder = '$#';
  2015. /**
  2016. * Locates output placeholders inside text
  2017. * @param {String} text
  2018. * @returns {Array} Array of ranges of output placeholder in text
  2019. */
  2020. function locateOutputPlaceholder(text) {
  2021. var range = require('range');
  2022. var result = [];
  2023. /** @type StringStream */
  2024. var stream = require('stringStream').create(text);
  2025. while (!stream.eol()) {
  2026. if (stream.peek() == '\\') {
  2027. stream.next();
  2028. } else {
  2029. stream.start = stream.pos;
  2030. if (stream.match(outputPlaceholder, true)) {
  2031. result.push(range.create(stream.start, outputPlaceholder));
  2032. continue;
  2033. }
  2034. }
  2035. stream.next();
  2036. }
  2037. return result;
  2038. }
  2039. /**
  2040. * Replaces output placeholders inside <code>source</code> with
  2041. * <code>value</code>
  2042. * @param {String} source
  2043. * @param {String} value
  2044. * @returns {String}
  2045. */
  2046. function replaceOutputPlaceholders(source, value) {
  2047. var utils = require('utils');
  2048. var ranges = locateOutputPlaceholder(source);
  2049. ranges.reverse();
  2050. _.each(ranges, function(r) {
  2051. source = utils.replaceSubstring(source, value, r);
  2052. });
  2053. return source;
  2054. }
  2055. /**
  2056. * Check if parsed node contains output placeholder – a target where
  2057. * pasted content should be inserted
  2058. * @param {AbbreviationNode} node
  2059. * @returns {Boolean}
  2060. */
  2061. function hasOutputPlaceholder(node) {
  2062. if (locateOutputPlaceholder(node.content).length)
  2063. return true;
  2064. // check if attributes contains placeholder
  2065. return !!_.find(node.attributeList(), function(attr) {
  2066. return !!locateOutputPlaceholder(attr.value).length;
  2067. });
  2068. }
  2069. /**
  2070. * Insert pasted content into correct positions of parsed node
  2071. * @param {AbbreviationNode} node
  2072. * @param {String} content
  2073. * @param {Boolean} overwrite Overwrite node content if no value placeholders
  2074. * found instead of appending to existing content
  2075. */
  2076. function insertPastedContent(node, content, overwrite) {
  2077. var nodesWithPlaceholders = node.findAll(function(item) {
  2078. return hasOutputPlaceholder(item);
  2079. });
  2080. if (hasOutputPlaceholder(node))
  2081. nodesWithPlaceholders.unshift(node);
  2082. if (nodesWithPlaceholders.length) {
  2083. _.each(nodesWithPlaceholders, function(item) {
  2084. item.content = replaceOutputPlaceholders(item.content, content);
  2085. _.each(item._attributes, function(attr) {
  2086. attr.value = replaceOutputPlaceholders(attr.value, content);
  2087. });
  2088. });
  2089. } else {
  2090. // on output placeholders in subtree, insert content in the deepest
  2091. // child node
  2092. var deepest = node.deepestChild() || node;
  2093. if (overwrite) {
  2094. deepest.content = content;
  2095. } else {
  2096. deepest.content = require('abbreviationUtils').insertChildContent(deepest.content, content);
  2097. }
  2098. }
  2099. }
  2100. /**
  2101. * @param {AbbreviationNode} tree
  2102. * @param {Object} options
  2103. */
  2104. parser.addPreprocessor(function(tree, options) {
  2105. if (options.pastedContent) {
  2106. var utils = require('utils');
  2107. var lines = _.map(utils.splitByLines(options.pastedContent, true), utils.trim);
  2108. // set repeat count for implicitly repeated elements before
  2109. // tree is unrolled
  2110. tree.findAll(function(item) {
  2111. if (item.hasImplicitRepeat) {
  2112. item.data('paste', lines);
  2113. return item.repeatCount = lines.length;
  2114. }
  2115. });
  2116. }
  2117. });
  2118. /**
  2119. * @param {AbbreviationNode} tree
  2120. * @param {Object} options
  2121. */
  2122. parser.addPostprocessor(function(tree, options) {
  2123. // for each node with pasted content, update text data
  2124. var targets = tree.findAll(function(item) {
  2125. var pastedContentObj = item.data('paste');
  2126. var pastedContent = '';
  2127. if (_.isArray(pastedContentObj)) {
  2128. pastedContent = pastedContentObj[item.counter - 1];
  2129. } else if (_.isFunction(pastedContentObj)) {
  2130. pastedContent = pastedContentObj(item.counter - 1, item.content);
  2131. } else if (pastedContentObj) {
  2132. pastedContent = pastedContentObj;
  2133. }
  2134. if (pastedContent) {
  2135. insertPastedContent(item, pastedContent, !!item.data('pasteOverwrites'));
  2136. }
  2137. item.data('paste', null);
  2138. return !!pastedContentObj;
  2139. });
  2140. if (!targets.length && options.pastedContent) {
  2141. // no implicitly repeated elements, put pasted content in
  2142. // the deepest child
  2143. insertPastedContent(tree, options.pastedContent);
  2144. }
  2145. });
  2146. });/**
  2147. * Resolves tag names in abbreviations with implied name
  2148. */
  2149. emmet.exec(function(require, _) {
  2150. /**
  2151. * Resolves implicit node names in parsed tree
  2152. * @param {AbbreviationNode} tree
  2153. */
  2154. function resolveNodeNames(tree) {
  2155. var tagName = require('tagName');
  2156. _.each(tree.children, function(node) {
  2157. if (node.hasImplicitName() || node.data('forceNameResolving')) {
  2158. node._name = tagName.resolve(node.parent.name());
  2159. }
  2160. resolveNodeNames(node);
  2161. });
  2162. return tree;
  2163. }
  2164. require('abbreviationParser').addPostprocessor(resolveNodeNames);
  2165. });/**
  2166. * @author Stoyan Stefanov
  2167. * @link https://github.com/stoyan/etc/tree/master/cssex
  2168. */
  2169. emmet.define('cssParser', function(require, _) {
  2170. var walker, tokens = [], isOp, isNameChar, isDigit;
  2171. // walks around the source
  2172. walker = {
  2173. lines: null,
  2174. total_lines: 0,
  2175. linenum: -1,
  2176. line: '',
  2177. ch: '',
  2178. chnum: -1,
  2179. init: function (source) {
  2180. var me = walker;
  2181. // source, yumm
  2182. me.lines = source
  2183. .replace(/\r\n/g, '\n')
  2184. .replace(/\r/g, '\n')
  2185. .split('\n');
  2186. me.total_lines = me.lines.length;
  2187. // reset
  2188. me.chnum = -1;
  2189. me.linenum = -1;
  2190. me.ch = '';
  2191. me.line = '';
  2192. // advance
  2193. me.nextLine();
  2194. me.nextChar();
  2195. },
  2196. nextLine: function () {
  2197. var me = this;
  2198. me.linenum += 1;
  2199. if (me.total_lines <= me.linenum) {
  2200. me.line = false;
  2201. } else {
  2202. me.line = me.lines[me.linenum];
  2203. }
  2204. if (me.chnum !== -1) {
  2205. me.chnum = 0;
  2206. }
  2207. return me.line;
  2208. },
  2209. nextChar: function () {
  2210. var me = this;
  2211. me.chnum += 1;
  2212. while (me.line.charAt(me.chnum) === '') {
  2213. if (this.nextLine() === false) {
  2214. me.ch = false;
  2215. return false; // end of source
  2216. }
  2217. me.chnum = -1;
  2218. me.ch = '\n';
  2219. return '\n';
  2220. }
  2221. me.ch = me.line.charAt(me.chnum);
  2222. return me.ch;
  2223. },
  2224. peek: function() {
  2225. return this.line.charAt(this.chnum + 1);
  2226. }
  2227. };
  2228. // utility helpers
  2229. isNameChar = function (c) {
  2230. // be more tolerate for name tokens: allow & character for LESS syntax
  2231. return (c == '&' || c === '_' || c === '-' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
  2232. };
  2233. isDigit = function (ch) {
  2234. return (ch !== false && ch >= '0' && ch <= '9');
  2235. };
  2236. isOp = (function () {
  2237. var opsa = "{}[]()+*=.,;:>~|\\%$#@^!".split(''),
  2238. opsmatcha = "*^|$~".split(''),
  2239. ops = {},
  2240. opsmatch = {},
  2241. i = 0;
  2242. for (; i < opsa.length; i += 1) {
  2243. ops[opsa[i]] = true;
  2244. }
  2245. for (i = 0; i < opsmatcha.length; i += 1) {
  2246. opsmatch[opsmatcha[i]] = true;
  2247. }
  2248. return function (ch, matchattr) {
  2249. if (matchattr) {
  2250. return !!opsmatch[ch];
  2251. }
  2252. return !!ops[ch];
  2253. };
  2254. }());
  2255. // shorthands
  2256. function isset(v) {
  2257. return typeof v !== 'undefined';
  2258. }
  2259. function getConf() {
  2260. return {
  2261. 'char': walker.chnum,
  2262. line: walker.linenum
  2263. };
  2264. }
  2265. // creates token objects and pushes them to a list
  2266. function tokener(value, type, conf) {
  2267. var w = walker, c = conf || {};
  2268. tokens.push({
  2269. charstart: isset(c['char']) ? c['char'] : w.chnum,
  2270. charend: isset(c.charend) ? c.charend : w.chnum,
  2271. linestart: isset(c.line) ? c.line : w.linenum,
  2272. lineend: isset(c.lineend) ? c.lineend : w.linenum,
  2273. value: value,
  2274. type: type || value
  2275. });
  2276. }
  2277. // oops
  2278. function error(m, config) {
  2279. var w = walker,
  2280. conf = config || {},
  2281. c = isset(conf['char']) ? conf['char'] : w.chnum,
  2282. l = isset(conf.line) ? conf.line : w.linenum;
  2283. return {
  2284. name: "ParseError",
  2285. message: m + " at line " + (l + 1) + ' char ' + (c + 1),
  2286. walker: w,
  2287. tokens: tokens
  2288. };
  2289. }
  2290. // token handlers follow for:
  2291. // white space, comment, string, identifier, number, operator
  2292. function white() {
  2293. var c = walker.ch,
  2294. token = '',
  2295. conf = getConf();
  2296. while (c === " " || c === "\t") {
  2297. token += c;
  2298. c = walker.nextChar();
  2299. }
  2300. tokener(token, 'white', conf);
  2301. }
  2302. function comment() {
  2303. var w = walker,
  2304. c = w.ch,
  2305. token = c,
  2306. cnext,
  2307. conf = getConf();
  2308. cnext = w.nextChar();
  2309. if (cnext !== '*') {
  2310. // oops, not a comment, just a /
  2311. conf.charend = conf['char'];
  2312. conf.lineend = conf.line;
  2313. return tokener(token, token, conf);
  2314. }
  2315. while (!(c === "*" && cnext === "/")) {
  2316. token += cnext;
  2317. c = cnext;
  2318. cnext = w.nextChar();
  2319. }
  2320. token += cnext;
  2321. w.nextChar();
  2322. tokener(token, 'comment', conf);
  2323. }
  2324. function str() {
  2325. var w = walker,
  2326. c = w.ch,
  2327. q = c,
  2328. token = c,
  2329. cnext,
  2330. conf = getConf();
  2331. c = w.nextChar();
  2332. while (c !== q) {
  2333. if (c === '\n') {
  2334. cnext = w.nextChar();
  2335. if (cnext === "\\") {
  2336. token += c + cnext;
  2337. } else {
  2338. // end of line with no \ escape = bad
  2339. throw error("Unterminated string", conf);
  2340. }
  2341. } else {
  2342. if (c === "\\") {
  2343. token += c + w.nextChar();
  2344. } else {
  2345. token += c;
  2346. }
  2347. }
  2348. c = w.nextChar();
  2349. }
  2350. token += c;
  2351. w.nextChar();
  2352. tokener(token, 'string', conf);
  2353. }
  2354. function brace() {
  2355. var w = walker,
  2356. c = w.ch,
  2357. depth = 0,
  2358. token = c,
  2359. conf = getConf();
  2360. c = w.nextChar();
  2361. while (c !== ')' && !depth) {
  2362. if (c === '(') {
  2363. depth++;
  2364. } else if (c === ')') {
  2365. depth--;
  2366. } else if (c === false) {
  2367. throw error("Unterminated brace", conf);
  2368. }
  2369. token += c;
  2370. c = w.nextChar();
  2371. }
  2372. token += c;
  2373. w.nextChar();
  2374. tokener(token, 'brace', conf);
  2375. }
  2376. function identifier(pre) {
  2377. var w = walker,
  2378. c = w.ch,
  2379. conf = getConf(),
  2380. token = (pre) ? pre + c : c;
  2381. c = w.nextChar();
  2382. if (pre) { // adjust token position
  2383. conf['char'] -= pre.length;
  2384. }
  2385. while (isNameChar(c) || isDigit(c)) {
  2386. token += c;
  2387. c = w.nextChar();
  2388. }
  2389. tokener(token, 'identifier', conf);
  2390. }
  2391. function num() {
  2392. var w = walker,
  2393. c = w.ch,
  2394. conf = getConf(),
  2395. token = c,
  2396. point = token === '.',
  2397. nondigit;
  2398. c = w.nextChar();
  2399. nondigit = !isDigit(c);
  2400. // .2px or .classname?
  2401. if (point && nondigit) {
  2402. // meh, NaN, could be a class name, so it's an operator for now
  2403. conf.charend = conf['char'];
  2404. conf.lineend = conf.line;
  2405. return tokener(token, '.', conf);
  2406. }
  2407. // -2px or -moz-something
  2408. if (token === '-' && nondigit) {
  2409. return identifier('-');
  2410. }
  2411. while (c !== false && (isDigit(c) || (!point && c === '.'))) { // not end of source && digit or first instance of .
  2412. if (c === '.') {
  2413. point = true;
  2414. }
  2415. token += c;
  2416. c = w.nextChar();
  2417. }
  2418. tokener(token, 'number', conf);
  2419. }
  2420. function op() {
  2421. var w = walker,
  2422. c = w.ch,
  2423. conf = getConf(),
  2424. token = c,
  2425. next = w.nextChar();
  2426. if (next === "=" && isOp(token, true)) {
  2427. token += next;
  2428. tokener(token, 'match', conf);
  2429. w.nextChar();
  2430. return;
  2431. }
  2432. conf.charend = conf['char'] + 1;
  2433. conf.lineend = conf.line;
  2434. tokener(token, token, conf);
  2435. }
  2436. // call the appropriate handler based on the first character in a token suspect
  2437. function tokenize() {
  2438. var ch = walker.ch;
  2439. if (ch === " " || ch === "\t") {
  2440. return white();
  2441. }
  2442. if (ch === '/') {
  2443. return comment();
  2444. }
  2445. if (ch === '"' || ch === "'") {
  2446. return str();
  2447. }
  2448. if (ch === '(') {
  2449. return brace();
  2450. }
  2451. if (ch === '-' || ch === '.' || isDigit(ch)) { // tricky - char: minus (-1px) or dash (-moz-stuff)
  2452. return num();
  2453. }
  2454. if (isNameChar(ch)) {
  2455. return identifier();
  2456. }
  2457. if (isOp(ch)) {
  2458. return op();
  2459. }
  2460. if (ch === "\n") {
  2461. tokener("line");
  2462. walker.nextChar();
  2463. return;
  2464. }
  2465. throw error("Unrecognized character");
  2466. }
  2467. /**
  2468. * Returns newline character at specified position in content
  2469. * @param {String} content
  2470. * @param {Number} pos
  2471. * @return {String}
  2472. */
  2473. function getNewline(content, pos) {
  2474. return content.charAt(pos) == '\r' && content.charAt(pos + 1) == '\n'
  2475. ? '\r\n'
  2476. : content.charAt(pos);
  2477. }
  2478. return {
  2479. /**
  2480. * @param source
  2481. * @returns
  2482. * @memberOf emmet.cssParser
  2483. */
  2484. lex: function (source) {
  2485. walker.init(source);
  2486. tokens = [];
  2487. while (walker.ch !== false) {
  2488. tokenize();
  2489. }
  2490. return tokens;
  2491. },
  2492. /**
  2493. * Tokenizes CSS source
  2494. * @param {String} source
  2495. * @returns {Array}
  2496. */
  2497. parse: function(source) {
  2498. // transform tokens
  2499. var pos = 0;
  2500. return _.map(this.lex(source), function(token) {
  2501. if (token.type == 'line') {
  2502. token.value = getNewline(source, pos);
  2503. }
  2504. return {
  2505. type: token.type,
  2506. start: pos,
  2507. end: (pos += token.value.length)
  2508. };
  2509. });
  2510. },
  2511. toSource: function (toks) {
  2512. var i = 0, max = toks.length, t, src = '';
  2513. for (; i < max; i += 1) {
  2514. t = toks[i];
  2515. if (t.type === 'line') {
  2516. src += '\n';
  2517. } else {
  2518. src += t.value;
  2519. }
  2520. }
  2521. return src;
  2522. }
  2523. };
  2524. });/**
  2525. * HTML tokenizer by Marijn Haverbeke
  2526. * http://codemirror.net/
  2527. * @constructor
  2528. * @memberOf __xmlParseDefine
  2529. * @param {Function} require
  2530. * @param {Underscore} _
  2531. */
  2532. emmet.define('xmlParser', function(require, _) {
  2533. var Kludges = {
  2534. autoSelfClosers : {},
  2535. implicitlyClosed : {},
  2536. contextGrabbers : {},
  2537. doNotIndent : {},
  2538. allowUnquoted : true,
  2539. allowMissing : true
  2540. };
  2541. // Return variables for tokenizers
  2542. var tagName = null, type = null;
  2543. function inText(stream, state) {
  2544. function chain(parser) {
  2545. state.tokenize = parser;
  2546. return parser(stream, state);
  2547. }
  2548. var ch = stream.next();
  2549. if (ch == "<") {
  2550. if (stream.eat("!")) {
  2551. if (stream.eat("[")) {
  2552. if (stream.match("CDATA["))
  2553. return chain(inBlock("atom", "]]>"));
  2554. else
  2555. return null;
  2556. } else if (stream.match("--"))
  2557. return chain(inBlock("comment", "-->"));
  2558. else if (stream.match("DOCTYPE", true, true)) {
  2559. stream.eatWhile(/[\w\._\-]/);
  2560. return chain(doctype(1));
  2561. } else
  2562. return null;
  2563. } else if (stream.eat("?")) {
  2564. stream.eatWhile(/[\w\._\-]/);
  2565. state.tokenize = inBlock("meta", "?>");
  2566. return "meta";
  2567. } else {
  2568. type = stream.eat("/") ? "closeTag" : "openTag";
  2569. stream.eatSpace();
  2570. tagName = "";
  2571. var c;
  2572. while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/)))
  2573. tagName += c;
  2574. state.tokenize = inTag;
  2575. return "tag";
  2576. }
  2577. } else if (ch == "&") {
  2578. var ok;
  2579. if (stream.eat("#")) {
  2580. if (stream.eat("x")) {
  2581. ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
  2582. } else {
  2583. ok = stream.eatWhile(/[\d]/) && stream.eat(";");
  2584. }
  2585. } else {
  2586. ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
  2587. }
  2588. return ok ? "atom" : "error";
  2589. } else {
  2590. stream.eatWhile(/[^&<]/);
  2591. return "text";
  2592. }
  2593. }
  2594. function inTag(stream, state) {
  2595. var ch = stream.next();
  2596. if (ch == ">" || (ch == "/" && stream.eat(">"))) {
  2597. state.tokenize = inText;
  2598. type = ch == ">" ? "endTag" : "selfcloseTag";
  2599. return "tag";
  2600. } else if (ch == "=") {
  2601. type = "equals";
  2602. return null;
  2603. } else if (/[\'\"]/.test(ch)) {
  2604. state.tokenize = inAttribute(ch);
  2605. return state.tokenize(stream, state);
  2606. } else {
  2607. stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
  2608. return "word";
  2609. }
  2610. }
  2611. function inAttribute(quote) {
  2612. return function(stream, state) {
  2613. while (!stream.eol()) {
  2614. if (stream.next() == quote) {
  2615. state.tokenize = inTag;
  2616. break;
  2617. }
  2618. }
  2619. return "string";
  2620. };
  2621. }
  2622. function inBlock(style, terminator) {
  2623. return function(stream, state) {
  2624. while (!stream.eol()) {
  2625. if (stream.match(terminator)) {
  2626. state.tokenize = inText;
  2627. break;
  2628. }
  2629. stream.next();
  2630. }
  2631. return style;
  2632. };
  2633. }
  2634. function doctype(depth) {
  2635. return function(stream, state) {
  2636. var ch;
  2637. while ((ch = stream.next()) != null) {
  2638. if (ch == "<") {
  2639. state.tokenize = doctype(depth + 1);
  2640. return state.tokenize(stream, state);
  2641. } else if (ch == ">") {
  2642. if (depth == 1) {
  2643. state.tokenize = inText;
  2644. break;
  2645. } else {
  2646. state.tokenize = doctype(depth - 1);
  2647. return state.tokenize(stream, state);
  2648. }
  2649. }
  2650. }
  2651. return "meta";
  2652. };
  2653. }
  2654. var curState = null, setStyle;
  2655. function pass() {
  2656. for (var i = arguments.length - 1; i >= 0; i--)
  2657. curState.cc.push(arguments[i]);
  2658. }
  2659. function cont() {
  2660. pass.apply(null, arguments);
  2661. return true;
  2662. }
  2663. function pushContext(tagName, startOfLine) {
  2664. var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName)
  2665. || (curState.context && curState.context.noIndent);
  2666. curState.context = {
  2667. prev : curState.context,
  2668. tagName : tagName,
  2669. indent : curState.indented,
  2670. startOfLine : startOfLine,
  2671. noIndent : noIndent
  2672. };
  2673. }
  2674. function popContext() {
  2675. if (curState.context)
  2676. curState.context = curState.context.prev;
  2677. }
  2678. function element(type) {
  2679. if (type == "openTag") {
  2680. curState.tagName = tagName;
  2681. return cont(attributes, endtag(curState.startOfLine));
  2682. } else if (type == "closeTag") {
  2683. var err = false;
  2684. if (curState.context) {
  2685. if (curState.context.tagName != tagName) {
  2686. if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
  2687. popContext();
  2688. }
  2689. err = !curState.context || curState.context.tagName != tagName;
  2690. }
  2691. } else {
  2692. err = true;
  2693. }
  2694. if (err)
  2695. setStyle = "error";
  2696. return cont(endclosetag(err));
  2697. }
  2698. return cont();
  2699. }
  2700. function endtag(startOfLine) {
  2701. return function(type) {
  2702. if (type == "selfcloseTag"
  2703. || (type == "endTag" && Kludges.autoSelfClosers
  2704. .hasOwnProperty(curState.tagName
  2705. .toLowerCase()))) {
  2706. maybePopContext(curState.tagName.toLowerCase());
  2707. return cont();
  2708. }
  2709. if (type == "endTag") {
  2710. maybePopContext(curState.tagName.toLowerCase());
  2711. pushContext(curState.tagName, startOfLine);
  2712. return cont();
  2713. }
  2714. return cont();
  2715. };
  2716. }
  2717. function endclosetag(err) {
  2718. return function(type) {
  2719. if (err)
  2720. setStyle = "error";
  2721. if (type == "endTag") {
  2722. popContext();
  2723. return cont();
  2724. }
  2725. setStyle = "error";
  2726. return cont(arguments.callee);
  2727. };
  2728. }
  2729. function maybePopContext(nextTagName) {
  2730. var parentTagName;
  2731. while (true) {
  2732. if (!curState.context) {
  2733. return;
  2734. }
  2735. parentTagName = curState.context.tagName.toLowerCase();
  2736. if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName)
  2737. || !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
  2738. return;
  2739. }
  2740. popContext();
  2741. }
  2742. }
  2743. function attributes(type) {
  2744. if (type == "word") {
  2745. setStyle = "attribute";
  2746. return cont(attribute, attributes);
  2747. }
  2748. if (type == "endTag" || type == "selfcloseTag")
  2749. return pass();
  2750. setStyle = "error";
  2751. return cont(attributes);
  2752. }
  2753. function attribute(type) {
  2754. if (type == "equals")
  2755. return cont(attvalue, attributes);
  2756. if (!Kludges.allowMissing)
  2757. setStyle = "error";
  2758. return (type == "endTag" || type == "selfcloseTag") ? pass()
  2759. : cont();
  2760. }
  2761. function attvalue(type) {
  2762. if (type == "string")
  2763. return cont(attvaluemaybe);
  2764. if (type == "word" && Kludges.allowUnquoted) {
  2765. setStyle = "string";
  2766. return cont();
  2767. }
  2768. setStyle = "error";
  2769. return (type == "endTag" || type == "selfCloseTag") ? pass()
  2770. : cont();
  2771. }
  2772. function attvaluemaybe(type) {
  2773. if (type == "string")
  2774. return cont(attvaluemaybe);
  2775. else
  2776. return pass();
  2777. }
  2778. function startState() {
  2779. return {
  2780. tokenize : inText,
  2781. cc : [],
  2782. indented : 0,
  2783. startOfLine : true,
  2784. tagName : null,
  2785. context : null
  2786. };
  2787. }
  2788. function token(stream, state) {
  2789. if (stream.sol()) {
  2790. state.startOfLine = true;
  2791. state.indented = 0;
  2792. }
  2793. if (stream.eatSpace())
  2794. return null;
  2795. setStyle = type = tagName = null;
  2796. var style = state.tokenize(stream, state);
  2797. state.type = type;
  2798. if ((style || type) && style != "comment") {
  2799. curState = state;
  2800. while (true) {
  2801. var comb = state.cc.pop() || element;
  2802. if (comb(type || style))
  2803. break;
  2804. }
  2805. }
  2806. state.startOfLine = false;
  2807. return setStyle || style;
  2808. }
  2809. return {
  2810. /**
  2811. * @memberOf emmet.xmlParser
  2812. * @returns
  2813. */
  2814. parse: function(data, offset) {
  2815. offset = offset || 0;
  2816. var state = startState();
  2817. var stream = require('stringStream').create(data);
  2818. var tokens = [];
  2819. while (!stream.eol()) {
  2820. tokens.push({
  2821. type: token(stream, state),
  2822. start: stream.start + offset,
  2823. end: stream.pos + offset
  2824. });
  2825. stream.start = stream.pos;
  2826. }
  2827. return tokens;
  2828. }
  2829. };
  2830. });
  2831. /*!
  2832. * string_score.js: String Scoring Algorithm 0.1.10
  2833. *
  2834. * http://joshaven.com/string_score
  2835. * https://github.com/joshaven/string_score
  2836. *
  2837. * Copyright (C) 2009-2011 Joshaven Potter <yourtech@gmail.com>
  2838. * Special thanks to all of the contributors listed here https://github.com/joshaven/string_score
  2839. * MIT license: http://www.opensource.org/licenses/mit-license.php
  2840. *
  2841. * Date: Tue Mar 1 2011
  2842. */
  2843. /**
  2844. * Scores a string against another string.
  2845. * 'Hello World'.score('he'); //=> 0.5931818181818181
  2846. * 'Hello World'.score('Hello'); //=> 0.7318181818181818
  2847. */
  2848. emmet.define('string-score', function(require, _) {
  2849. return {
  2850. score: function(string, abbreviation, fuzziness) {
  2851. // If the string is equal to the abbreviation, perfect match.
  2852. if (string == abbreviation) {return 1;}
  2853. //if it's not a perfect match and is empty return 0
  2854. if(abbreviation == "") {return 0;}
  2855. var total_character_score = 0,
  2856. abbreviation_length = abbreviation.length,
  2857. string_length = string.length,
  2858. start_of_string_bonus,
  2859. abbreviation_score,
  2860. fuzzies=1,
  2861. final_score;
  2862. // Walk through abbreviation and add up scores.
  2863. for (var i = 0,
  2864. character_score/* = 0*/,
  2865. index_in_string/* = 0*/,
  2866. c/* = ''*/,
  2867. index_c_lowercase/* = 0*/,
  2868. index_c_uppercase/* = 0*/,
  2869. min_index/* = 0*/;
  2870. i < abbreviation_length;
  2871. ++i) {
  2872. // Find the first case-insensitive match of a character.
  2873. c = abbreviation.charAt(i);
  2874. index_c_lowercase = string.indexOf(c.toLowerCase());
  2875. index_c_uppercase = string.indexOf(c.toUpperCase());
  2876. min_index = Math.min(index_c_lowercase, index_c_uppercase);
  2877. index_in_string = (min_index > -1) ? min_index : Math.max(index_c_lowercase, index_c_uppercase);
  2878. if (index_in_string === -1) {
  2879. if (fuzziness) {
  2880. fuzzies += 1-fuzziness;
  2881. continue;
  2882. } else {
  2883. return 0;
  2884. }
  2885. } else {
  2886. character_score = 0.1;
  2887. }
  2888. // Set base score for matching 'c'.
  2889. // Same case bonus.
  2890. if (string[index_in_string] === c) {
  2891. character_score += 0.1;
  2892. }
  2893. // Consecutive letter & start-of-string Bonus
  2894. if (index_in_string === 0) {
  2895. // Increase the score when matching first character of the remainder of the string
  2896. character_score += 0.6;
  2897. if (i === 0) {
  2898. // If match is the first character of the string
  2899. // & the first character of abbreviation, add a
  2900. // start-of-string match bonus.
  2901. start_of_string_bonus = 1; //true;
  2902. }
  2903. }
  2904. else {
  2905. // Acronym Bonus
  2906. // Weighing Logic: Typing the first character of an acronym is as if you
  2907. // preceded it with two perfect character matches.
  2908. if (string.charAt(index_in_string - 1) === ' ') {
  2909. character_score += 0.8; // * Math.min(index_in_string, 5); // Cap bonus at 0.4 * 5
  2910. }
  2911. }
  2912. // Left trim the already matched part of the string
  2913. // (forces sequential matching).
  2914. string = string.substring(index_in_string + 1, string_length);
  2915. total_character_score += character_score;
  2916. } // end of for loop
  2917. // Uncomment to weigh smaller words higher.
  2918. // return total_character_score / string_length;
  2919. abbreviation_score = total_character_score / abbreviation_length;
  2920. //percentage_of_matched_string = abbreviation_length / string_length;
  2921. //word_score = abbreviation_score * percentage_of_matched_string;
  2922. // Reduce penalty for longer strings.
  2923. //final_score = (word_score + abbreviation_score) / 2;
  2924. final_score = ((abbreviation_score * (abbreviation_length / string_length)) + abbreviation_score) / 2;
  2925. final_score = final_score / fuzzies;
  2926. if (start_of_string_bonus && (final_score + 0.15 < 1)) {
  2927. final_score += 0.15;
  2928. }
  2929. return final_score;
  2930. }
  2931. };
  2932. });/**
  2933. * Utility module for Emmet
  2934. * @param {Function} require
  2935. * @param {Underscore} _
  2936. */
  2937. emmet.define('utils', function(require, _) {
  2938. /**
  2939. * Special token used as a placeholder for caret positions inside
  2940. * generated output
  2941. */
  2942. var caretPlaceholder = '${0}';
  2943. /**
  2944. * A simple string builder, optimized for faster text concatenation
  2945. * @param {String} value Initial value
  2946. */
  2947. function StringBuilder(value) {
  2948. this._data = [];
  2949. this.length = 0;
  2950. if (value)
  2951. this.append(value);
  2952. }
  2953. StringBuilder.prototype = {
  2954. /**
  2955. * Append string
  2956. * @param {String} text
  2957. */
  2958. append: function(text) {
  2959. this._data.push(text);
  2960. this.length += text.length;
  2961. },
  2962. /**
  2963. * @returns {String}
  2964. */
  2965. toString: function() {
  2966. return this._data.join('');
  2967. },
  2968. /**
  2969. * @returns {String}
  2970. */
  2971. valueOf: function() {
  2972. return this.toString();
  2973. }
  2974. };
  2975. return {
  2976. /** @memberOf utils */
  2977. reTag: /<\/?[\w:\-]+(?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*\s*(\/?)>$/,
  2978. /**
  2979. * Test if passed string ends with XHTML tag. This method is used for testing
  2980. * '>' character: it belongs to tag or it's a part of abbreviation?
  2981. * @param {String} str
  2982. * @return {Boolean}
  2983. */
  2984. endsWithTag: function(str) {
  2985. return this.reTag.test(str);
  2986. },
  2987. /**
  2988. * Check if passed symbol is a number
  2989. * @param {String} ch
  2990. * @returns {Boolean}
  2991. */
  2992. isNumeric: function(ch) {
  2993. if (typeof(ch) == 'string')
  2994. ch = ch.charCodeAt(0);
  2995. return (ch && ch > 47 && ch < 58);
  2996. },
  2997. /**
  2998. * Trim whitespace from string
  2999. * @param {String} text
  3000. * @return {String}
  3001. */
  3002. trim: function(text) {
  3003. return (text || "").replace(/^\s+|\s+$/g, "");
  3004. },
  3005. /**
  3006. * Returns newline character
  3007. * @returns {String}
  3008. */
  3009. getNewline: function() {
  3010. var res = require('resources');
  3011. if (!res) {
  3012. return '\n';
  3013. }
  3014. var nl = res.getVariable('newline');
  3015. return _.isString(nl) ? nl : '\n';
  3016. },
  3017. /**
  3018. * Sets new newline character that will be used in output
  3019. * @param {String} str
  3020. */
  3021. setNewline: function(str) {
  3022. var res = require('resources');
  3023. res.setVariable('newline', str);
  3024. res.setVariable('nl', str);
  3025. },
  3026. /**
  3027. * Split text into lines. Set <code>remove_empty</code> to true to filter
  3028. * empty lines
  3029. * @param {String} text Text to split
  3030. * @param {Boolean} removeEmpty Remove empty lines from result
  3031. * @return {Array}
  3032. */
  3033. splitByLines: function(text, removeEmpty) {
  3034. // IE fails to split string by regexp,
  3035. // need to normalize newlines first
  3036. // Also, Mozilla's Rhiho JS engine has a weird newline bug
  3037. var nl = this.getNewline();
  3038. var lines = (text || '')
  3039. .replace(/\r\n/g, '\n')
  3040. .replace(/\n\r/g, '\n')
  3041. .replace(/\r/g, '\n')
  3042. .replace(/\n/g, nl)
  3043. .split(nl);
  3044. if (removeEmpty) {
  3045. lines = _.filter(lines, function(line) {
  3046. return line.length && !!this.trim(line);
  3047. }, this);
  3048. }
  3049. return lines;
  3050. },
  3051. /**
  3052. * Normalizes newline character: replaces newlines in <code>text</code>
  3053. * with newline defined in preferences
  3054. * @param {String} text
  3055. * @returns {String}
  3056. */
  3057. normalizeNewline: function(text) {
  3058. return this.splitByLines(text).join(this.getNewline());
  3059. },
  3060. /**
  3061. * Repeats string <code>howMany</code> times
  3062. * @param {String} str
  3063. * @param {Number} how_many
  3064. * @return {String}
  3065. */
  3066. repeatString: function(str, howMany) {
  3067. var result = [];
  3068. for (var i = 0; i < howMany; i++)
  3069. result.push(str);
  3070. return result.join('');
  3071. },
  3072. /**
  3073. * Returns list of paddings that should be used to align passed string
  3074. * @param {Array} strings
  3075. * @returns {Array}
  3076. */
  3077. getStringsPads: function(strings) {
  3078. var lengths = _.map(strings, function(s) {
  3079. return _.isString(s) ? s.length : +s;
  3080. });
  3081. var max = _.max(lengths);
  3082. return _.map(lengths, function(l) {
  3083. var pad = max - l;
  3084. return pad ? this.repeatString(' ', pad) : '';
  3085. }, this);
  3086. },
  3087. /**
  3088. * Indents text with padding
  3089. * @param {String} text Text to indent
  3090. * @param {String} pad Padding size (number) or padding itself (string)
  3091. * @return {String}
  3092. */
  3093. padString: function(text, pad) {
  3094. var padStr = (_.isNumber(pad))
  3095. ? this.repeatString(require('resources').getVariable('indentation') || '\t', pad)
  3096. : pad;
  3097. var result = [];
  3098. var lines = this.splitByLines(text);
  3099. var nl = this.getNewline();
  3100. result.push(lines[0]);
  3101. for (var j = 1; j < lines.length; j++)
  3102. result.push(nl + padStr + lines[j]);
  3103. return result.join('');
  3104. },
  3105. /**
  3106. * Pad string with zeroes
  3107. * @param {String} str String to pad
  3108. * @param {Number} pad Desired string length
  3109. * @return {String}
  3110. */
  3111. zeroPadString: function(str, pad) {
  3112. var padding = '';
  3113. var il = str.length;
  3114. while (pad > il++) padding += '0';
  3115. return padding + str;
  3116. },
  3117. /**
  3118. * Removes padding at the beginning of each text's line
  3119. * @param {String} text
  3120. * @param {String} pad
  3121. */
  3122. unindentString: function(text, pad) {
  3123. var lines = this.splitByLines(text);
  3124. for (var i = 0; i < lines.length; i++) {
  3125. if (lines[i].search(pad) == 0)
  3126. lines[i] = lines[i].substr(pad.length);
  3127. }
  3128. return lines.join(this.getNewline());
  3129. },
  3130. /**
  3131. * Replaces unescaped symbols in <code>str</code>. For example, the '$' symbol
  3132. * will be replaced in 'item$count', but not in 'item\$count'.
  3133. * @param {String} str Original string
  3134. * @param {String} symbol Symbol to replace
  3135. * @param {String} replace Symbol replacement. Might be a function that
  3136. * returns new value
  3137. * @return {String}
  3138. */
  3139. replaceUnescapedSymbol: function(str, symbol, replace) {
  3140. var i = 0;
  3141. var il = str.length;
  3142. var sl = symbol.length;
  3143. var matchCount = 0;
  3144. while (i < il) {
  3145. if (str.charAt(i) == '\\') {
  3146. // escaped symbol, skip next character
  3147. i += sl + 1;
  3148. } else if (str.substr(i, sl) == symbol) {
  3149. // have match
  3150. var curSl = sl;
  3151. matchCount++;
  3152. var newValue = replace;
  3153. if (_.isFunction(replace)) {
  3154. var replaceData = replace(str, symbol, i, matchCount);
  3155. if (replaceData) {
  3156. curSl = replaceData[0].length;
  3157. newValue = replaceData[1];
  3158. } else {
  3159. newValue = false;
  3160. }
  3161. }
  3162. if (newValue === false) { // skip replacement
  3163. i++;
  3164. continue;
  3165. }
  3166. str = str.substring(0, i) + newValue + str.substring(i + curSl);
  3167. // adjust indexes
  3168. il = str.length;
  3169. i += newValue.length;
  3170. } else {
  3171. i++;
  3172. }
  3173. }
  3174. return str;
  3175. },
  3176. /**
  3177. * Replace variables like ${var} in string
  3178. * @param {String} str
  3179. * @param {Object} vars Variable set (defaults to variables defined in
  3180. * <code>snippets.json</code>) or variable resolver (<code>Function</code>)
  3181. * @return {String}
  3182. */
  3183. replaceVariables: function(str, vars) {
  3184. vars = vars || {};
  3185. var resolver = _.isFunction(vars) ? vars : function(str, p1) {
  3186. return p1 in vars ? vars[p1] : null;
  3187. };
  3188. var res = require('resources');
  3189. return require('tabStops').processText(str, {
  3190. variable: function(data) {
  3191. var newValue = resolver(data.token, data.name, data);
  3192. if (newValue === null) {
  3193. // try to find variable in resources
  3194. newValue = res.getVariable(data.name);
  3195. }
  3196. if (newValue === null || _.isUndefined(newValue))
  3197. // nothing found, return token itself
  3198. newValue = data.token;
  3199. return newValue;
  3200. }
  3201. });
  3202. },
  3203. /**
  3204. * Replaces '$' character in string assuming it might be escaped with '\'
  3205. * @param {String} str String where character should be replaced
  3206. * @param {String} value New value
  3207. * @return {String}
  3208. */
  3209. replaceCounter: function(str, value, total) {
  3210. var symbol = '$';
  3211. // in case we received strings from Java, convert the to native strings
  3212. str = String(str);
  3213. value = String(value);
  3214. if (/^\-?\d+$/.test(value)) {
  3215. value = +value;
  3216. }
  3217. var that = this;
  3218. return this.replaceUnescapedSymbol(str, symbol, function(str, symbol, pos, matchNum){
  3219. if (str.charAt(pos + 1) == '{' || that.isNumeric(str.charAt(pos + 1)) ) {
  3220. // it's a variable, skip it
  3221. return false;
  3222. }
  3223. // replace sequense of $ symbols with padded number
  3224. var j = pos + 1;
  3225. while(str.charAt(j) == '$' && str.charAt(j + 1) != '{') j++;
  3226. var pad = j - pos;
  3227. // get counter base
  3228. var base = 0, decrement = false, m;
  3229. if (m = str.substr(j).match(/^@(\-?)(\d*)/)) {
  3230. j += m[0].length;
  3231. if (m[1]) {
  3232. decrement = true;
  3233. }
  3234. base = parseInt(m[2] || 1) - 1;
  3235. }
  3236. if (decrement && total && _.isNumber(value)) {
  3237. value = total - value + 1;
  3238. }
  3239. value += base;
  3240. return [str.substring(pos, j), that.zeroPadString(value + '', pad)];
  3241. });
  3242. },
  3243. /**
  3244. * Check if string matches against <code>reTag</code> regexp. This
  3245. * function may be used to test if provided string contains HTML tags
  3246. * @param {String} str
  3247. * @returns {Boolean}
  3248. */
  3249. matchesTag: function(str) {
  3250. return this.reTag.test(str || '');
  3251. },
  3252. /**
  3253. * Escapes special characters used in Emmet, like '$', '|', etc.
  3254. * Use this method before passing to actions like "Wrap with Abbreviation"
  3255. * to make sure that existing special characters won't be altered
  3256. * @param {String} text
  3257. * @return {String}
  3258. */
  3259. escapeText: function(text) {
  3260. return text.replace(/([\$\\])/g, '\\$1');
  3261. },
  3262. /**
  3263. * Unescapes special characters used in Emmet, like '$', '|', etc.
  3264. * @param {String} text
  3265. * @return {String}
  3266. */
  3267. unescapeText: function(text) {
  3268. return text.replace(/\\(.)/g, '$1');
  3269. },
  3270. /**
  3271. * Returns caret placeholder
  3272. * @returns {String}
  3273. */
  3274. getCaretPlaceholder: function() {
  3275. return _.isFunction(caretPlaceholder)
  3276. ? caretPlaceholder.apply(this, arguments)
  3277. : caretPlaceholder;
  3278. },
  3279. /**
  3280. * Sets new representation for carets in generated output
  3281. * @param {String} value New caret placeholder. Might be a
  3282. * <code>Function</code>
  3283. */
  3284. setCaretPlaceholder: function(value) {
  3285. caretPlaceholder = value;
  3286. },
  3287. /**
  3288. * Returns line padding
  3289. * @param {String} line
  3290. * @return {String}
  3291. */
  3292. getLinePadding: function(line) {
  3293. return (line.match(/^(\s+)/) || [''])[0];
  3294. },
  3295. /**
  3296. * Helper function that returns padding of line of <code>pos</code>
  3297. * position in <code>content</code>
  3298. * @param {String} content
  3299. * @param {Number} pos
  3300. * @returns {String}
  3301. */
  3302. getLinePaddingFromPosition: function(content, pos) {
  3303. var lineRange = this.findNewlineBounds(content, pos);
  3304. return this.getLinePadding(lineRange.substring(content));
  3305. },
  3306. /**
  3307. * Escape special regexp chars in string, making it usable for creating dynamic
  3308. * regular expressions
  3309. * @param {String} str
  3310. * @return {String}
  3311. */
  3312. escapeForRegexp: function(str) {
  3313. var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{}\
  3314. return str.replace(specials, "\\$&");
  3315. },
  3316. /**
  3317. * Make decimal number look good: convert it to fixed precision end remove
  3318. * traling zeroes
  3319. * @param {Number} num
  3320. * @param {Number} fracion Fraction numbers (default is 2)
  3321. * @return {String}
  3322. */
  3323. prettifyNumber: function(num, fraction) {
  3324. return num.toFixed(typeof fraction == 'undefined' ? 2 : fraction).replace(/\.?0+$/, '');
  3325. },
  3326. /**
  3327. * A simple mutable string shim, optimized for faster text concatenation
  3328. * @param {String} value Initial value
  3329. * @returns {StringBuilder}
  3330. */
  3331. stringBuilder: function(value) {
  3332. return new StringBuilder(value);
  3333. },
  3334. /**
  3335. * Replace substring of <code>str</code> with <code>value</code>
  3336. * @param {String} str String where to replace substring
  3337. * @param {String} value New substring value
  3338. * @param {Number} start Start index of substring to replace. May also
  3339. * be a <code>Range</code> object: in this case, the <code>end</code>
  3340. * argument is not required
  3341. * @param {Number} end End index of substring to replace. If ommited,
  3342. * <code>start</code> argument is used
  3343. */
  3344. replaceSubstring: function(str, value, start, end) {
  3345. if (_.isObject(start) && 'end' in start) {
  3346. end = start.end;
  3347. start = start.start;
  3348. }
  3349. if (_.isString(end))
  3350. end = start + end.length;
  3351. if (_.isUndefined(end))
  3352. end = start;
  3353. if (start < 0 || start > str.length)
  3354. return str;
  3355. return str.substring(0, start) + value + str.substring(end);
  3356. },
  3357. /**
  3358. * Narrows down text range, adjusting selection to non-space characters
  3359. * @param {String} text
  3360. * @param {Number} start Starting range in <code>text</code> where
  3361. * slection should be adjusted. Can also be any object that is accepted
  3362. * by <code>Range</code> class
  3363. * @return {Range}
  3364. */
  3365. narrowToNonSpace: function(text, start, end) {
  3366. var range = require('range').create(start, end);
  3367. var reSpace = /[\s\n\r\u00a0]/;
  3368. // narrow down selection until first non-space character
  3369. while (range.start < range.end) {
  3370. if (!reSpace.test(text.charAt(range.start)))
  3371. break;
  3372. range.start++;
  3373. }
  3374. while (range.end > range.start) {
  3375. range.end--;
  3376. if (!reSpace.test(text.charAt(range.end))) {
  3377. range.end++;
  3378. break;
  3379. }
  3380. }
  3381. return range;
  3382. },
  3383. /**
  3384. * Find start and end index of text line for <code>from</code> index
  3385. * @param {String} text
  3386. * @param {Number} from
  3387. */
  3388. findNewlineBounds: function(text, from) {
  3389. var len = text.length,
  3390. start = 0,
  3391. end = len - 1;
  3392. // search left
  3393. for (var i = from - 1; i > 0; i--) {
  3394. var ch = text.charAt(i);
  3395. if (ch == '\n' || ch == '\r') {
  3396. start = i + 1;
  3397. break;
  3398. }
  3399. }
  3400. // search right
  3401. for (var j = from; j < len; j++) {
  3402. var ch = text.charAt(j);
  3403. if (ch == '\n' || ch == '\r') {
  3404. end = j;
  3405. break;
  3406. }
  3407. }
  3408. return require('range').create(start, end - start);
  3409. },
  3410. /**
  3411. * Deep merge of two or more objects. Taken from jQuery.extend()
  3412. */
  3413. deepMerge: function() {
  3414. var options, name, src, copy, copyIsArray, clone,
  3415. target = arguments[0] || {},
  3416. i = 1,
  3417. length = arguments.length;
  3418. // Handle case when target is a string or something (possible in deep copy)
  3419. if (!_.isObject(target) && !_.isFunction(target)) {
  3420. target = {};
  3421. }
  3422. for ( ; i < length; i++ ) {
  3423. // Only deal with non-null/undefined values
  3424. if ( (options = arguments[ i ]) != null ) {
  3425. // Extend the base object
  3426. for ( name in options ) {
  3427. src = target[ name ];
  3428. copy = options[ name ];
  3429. // Prevent never-ending loop
  3430. if ( target === copy ) {
  3431. continue;
  3432. }
  3433. // Recurse if we're merging plain objects or arrays
  3434. if ( copy && ( _.isObject(copy) || (copyIsArray = _.isArray(copy)) ) ) {
  3435. if ( copyIsArray ) {
  3436. copyIsArray = false;
  3437. clone = src && _.isArray(src) ? src : [];
  3438. } else {
  3439. clone = src && _.isObject(src) ? src : {};
  3440. }
  3441. // Never move original objects, clone them
  3442. target[ name ] = this.deepMerge(clone, copy );
  3443. // Don't bring in undefined values
  3444. } else if ( copy !== undefined ) {
  3445. target[ name ] = copy;
  3446. }
  3447. }
  3448. }
  3449. }
  3450. // Return the modified object
  3451. return target;
  3452. }
  3453. };
  3454. });
  3455. /**
  3456. * Helper module to work with ranges
  3457. * @constructor
  3458. * @memberOf __rangeDefine
  3459. * @param {Function} require
  3460. * @param {Underscore} _
  3461. */
  3462. emmet.define('range', function(require, _) {
  3463. function cmp(a, b, op) {
  3464. switch (op) {
  3465. case 'eq':
  3466. case '==':
  3467. return a === b;
  3468. case 'lt':
  3469. case '<':
  3470. return a < b;
  3471. case 'lte':
  3472. case '<=':
  3473. return a <= b;
  3474. case 'gt':
  3475. case '>':
  3476. return a > b;
  3477. case 'gte':
  3478. case '>=':
  3479. return a >= b;
  3480. }
  3481. }
  3482. /**
  3483. * @type Range
  3484. * @constructor
  3485. * @param {Object} start
  3486. * @param {Number} len
  3487. */
  3488. function Range(start, len) {
  3489. if (_.isObject(start) && 'start' in start) {
  3490. // create range from object stub
  3491. this.start = Math.min(start.start, start.end);
  3492. this.end = Math.max(start.start, start.end);
  3493. } else if (_.isArray(start)) {
  3494. this.start = start[0];
  3495. this.end = start[1];
  3496. } else {
  3497. len = _.isString(len) ? len.length : +len;
  3498. this.start = start;
  3499. this.end = start + len;
  3500. }
  3501. }
  3502. Range.prototype = {
  3503. length: function() {
  3504. return Math.abs(this.end - this.start);
  3505. },
  3506. /**
  3507. * Returns <code>true</code> if passed range is equals to current one
  3508. * @param {Range} range
  3509. * @returns {Boolean}
  3510. */
  3511. equal: function(range) {
  3512. return this.cmp(range, 'eq', 'eq');
  3513. // return this.start === range.start && this.end === range.end;
  3514. },
  3515. /**
  3516. * Shifts indexes position with passed <code>delat</code>
  3517. * @param {Number} delta
  3518. * @returns {Range} range itself
  3519. */
  3520. shift: function(delta) {
  3521. this.start += delta;
  3522. this.end += delta;
  3523. return this;
  3524. },
  3525. /**
  3526. * Check if two ranges are overlapped
  3527. * @param {Range} range
  3528. * @returns {Boolean}
  3529. */
  3530. overlap: function(range) {
  3531. return range.start <= this.end && range.end >= this.start;
  3532. },
  3533. /**
  3534. * Finds intersection of two ranges
  3535. * @param {Range} range
  3536. * @returns {Range} <code>null</code> if ranges does not overlap
  3537. */
  3538. intersection: function(range) {
  3539. if (this.overlap(range)) {
  3540. var start = Math.max(range.start, this.start);
  3541. var end = Math.min(range.end, this.end);
  3542. return new Range(start, end - start);
  3543. }
  3544. return null;
  3545. },
  3546. /**
  3547. * Returns the union of the thow ranges.
  3548. * @param {Range} range
  3549. * @returns {Range} <code>null</code> if ranges are not overlapped
  3550. */
  3551. union: function(range) {
  3552. if (this.overlap(range)) {
  3553. var start = Math.min(range.start, this.start);
  3554. var end = Math.max(range.end, this.end);
  3555. return new Range(start, end - start);
  3556. }
  3557. return null;
  3558. },
  3559. /**
  3560. * Returns a Boolean value that indicates whether a specified position
  3561. * is in a given range.
  3562. * @param {Number} loc
  3563. */
  3564. inside: function(loc) {
  3565. return this.cmp(loc, 'lte', 'gt');
  3566. // return this.start <= loc && this.end > loc;
  3567. },
  3568. /**
  3569. * Returns a Boolean value that indicates whether a specified position
  3570. * is in a given range, but not equals bounds.
  3571. * @param {Number} loc
  3572. */
  3573. contains: function(loc) {
  3574. return this.cmp(loc, 'lt', 'gt');
  3575. },
  3576. /**
  3577. * Check if current range completely includes specified one
  3578. * @param {Range} r
  3579. * @returns {Boolean}
  3580. */
  3581. include: function(r) {
  3582. return this.cmp(loc, 'lte', 'gte');
  3583. // return this.start <= r.start && this.end >= r.end;
  3584. },
  3585. /**
  3586. * Low-level comparision method
  3587. * @param {Number} loc
  3588. * @param {String} left Left comparison operator
  3589. * @param {String} right Right comaprison operator
  3590. */
  3591. cmp: function(loc, left, right) {
  3592. var a, b;
  3593. if (loc instanceof Range) {
  3594. a = loc.start;
  3595. b = loc.end;
  3596. } else {
  3597. a = b = loc;
  3598. }
  3599. return cmp(this.start, a, left || '<=') && cmp(this.end, b, right || '>');
  3600. },
  3601. /**
  3602. * Returns substring of specified <code>str</code> for current range
  3603. * @param {String} str
  3604. * @returns {String}
  3605. */
  3606. substring: function(str) {
  3607. return this.length() > 0
  3608. ? str.substring(this.start, this.end)
  3609. : '';
  3610. },
  3611. /**
  3612. * Creates copy of current range
  3613. * @returns {Range}
  3614. */
  3615. clone: function() {
  3616. return new Range(this.start, this.length());
  3617. },
  3618. /**
  3619. * @returns {Array}
  3620. */
  3621. toArray: function() {
  3622. return [this.start, this.end];
  3623. },
  3624. toString: function() {
  3625. return '{' + this.start + ', ' + this.length() + '}';
  3626. }
  3627. };
  3628. return {
  3629. /**
  3630. * Creates new range object instance
  3631. * @param {Object} start Range start or array with 'start' and 'end'
  3632. * as two first indexes or object with 'start' and 'end' properties
  3633. * @param {Number} len Range length or string to produce range from
  3634. * @returns {Range}
  3635. * @memberOf emmet.range
  3636. */
  3637. create: function(start, len) {
  3638. if (_.isUndefined(start) || start === null)
  3639. return null;
  3640. if (start instanceof Range)
  3641. return start;
  3642. if (_.isObject(start) && 'start' in start && 'end' in start) {
  3643. len = start.end - start.start;
  3644. start = start.start;
  3645. }
  3646. return new Range(start, len);
  3647. },
  3648. /**
  3649. * <code>Range</code> object factory, the same as <code>this.create()</code>
  3650. * but last argument represents end of range, not length
  3651. * @returns {Range}
  3652. */
  3653. create2: function(start, end) {
  3654. if (_.isNumber(start) && _.isNumber(end)) {
  3655. end -= start;
  3656. }
  3657. return this.create(start, end);
  3658. }
  3659. };
  3660. });/**
  3661. * Utility module that provides ordered storage of function handlers.
  3662. * Many Emmet modules' functionality can be extended/overridden by custom
  3663. * function. This modules provides unified storage of handler functions, their
  3664. * management and execution
  3665. *
  3666. * @constructor
  3667. * @memberOf __handlerListDefine
  3668. * @param {Function} require
  3669. * @param {Underscore} _
  3670. */
  3671. emmet.define('handlerList', function(require, _) {
  3672. /**
  3673. * @type HandlerList
  3674. * @constructor
  3675. */
  3676. function HandlerList() {
  3677. this._list = [];
  3678. }
  3679. HandlerList.prototype = {
  3680. /**
  3681. * Adds function handler
  3682. * @param {Function} fn Handler
  3683. * @param {Object} options Handler options. Possible values are:<br><br>
  3684. * <b>order</b> : (<code>Number</code>) – order in handler list. Handlers
  3685. * with higher order value will be executed earlier.
  3686. */
  3687. add: function(fn, options) {
  3688. this._list.push(_.extend({order: 0}, options || {}, {fn: fn}));
  3689. },
  3690. /**
  3691. * Removes handler from list
  3692. * @param {Function} fn
  3693. */
  3694. remove: function(fn) {
  3695. this._list = _.without(this._list, _.find(this._list, function(item) {
  3696. return item.fn === fn;
  3697. }));
  3698. },
  3699. /**
  3700. * Returns ordered list of handlers. By default, handlers
  3701. * with the same <code>order</code> option returned in reverse order,
  3702. * i.e. the latter function was added into the handlers list, the higher
  3703. * it will be in the returned array
  3704. * @returns {Array}
  3705. */
  3706. list: function() {
  3707. return _.sortBy(this._list, 'order').reverse();
  3708. },
  3709. /**
  3710. * Returns ordered list of handler functions
  3711. * @returns {Array}
  3712. */
  3713. listFn: function() {
  3714. return _.pluck(this.list(), 'fn');
  3715. },
  3716. /**
  3717. * Executes handler functions in their designated order. If function
  3718. * returns <code>skipVal</code>, meaning that function was unable to
  3719. * handle passed <code>args</code>, the next function will be executed
  3720. * and so on.
  3721. * @param {Object} skipValue If function returns this value, execute
  3722. * next handler.
  3723. * @param {Array} args Arguments to pass to handler function
  3724. * @returns {Boolean} Whether any of registered handlers performed
  3725. * successfully
  3726. */
  3727. exec: function(skipValue, args) {
  3728. args = args || [];
  3729. var result = null;
  3730. _.find(this.list(), function(h) {
  3731. result = h.fn.apply(h, args);
  3732. if (result !== skipValue)
  3733. return true;
  3734. });
  3735. return result;
  3736. }
  3737. };
  3738. return {
  3739. /**
  3740. * Factory method that produces <code>HandlerList</code> instance
  3741. * @returns {HandlerList}
  3742. * @memberOf handlerList
  3743. */
  3744. create: function() {
  3745. return new HandlerList();
  3746. }
  3747. };
  3748. });/**
  3749. * Helper class for convenient token iteration
  3750. */
  3751. emmet.define('tokenIterator', function(require, _) {
  3752. /**
  3753. * @type TokenIterator
  3754. * @param {Array} tokens
  3755. * @type TokenIterator
  3756. * @constructor
  3757. */
  3758. function TokenIterator(tokens) {
  3759. /** @type Array */
  3760. this.tokens = tokens;
  3761. this._position = 0;
  3762. this.reset();
  3763. }
  3764. TokenIterator.prototype = {
  3765. next: function() {
  3766. if (this.hasNext()) {
  3767. var token = this.tokens[++this._i];
  3768. this._position = token.start;
  3769. return token;
  3770. }
  3771. return null;
  3772. },
  3773. current: function() {
  3774. return this.tokens[this._i];
  3775. },
  3776. position: function() {
  3777. return this._position;
  3778. },
  3779. hasNext: function() {
  3780. return this._i < this._il - 1;
  3781. },
  3782. reset: function() {
  3783. this._i = -1;
  3784. this._il = this.tokens.length;
  3785. },
  3786. item: function() {
  3787. return this.tokens[this._i];
  3788. },
  3789. itemNext: function() {
  3790. return this.tokens[this._i + 1];
  3791. },
  3792. itemPrev: function() {
  3793. return this.tokens[this._i - 1];
  3794. },
  3795. nextUntil: function(type, callback) {
  3796. var token;
  3797. var test = _.isString(type)
  3798. ? function(t){return t.type == type;}
  3799. : type;
  3800. while (token = this.next()) {
  3801. if (callback)
  3802. callback.call(this, token);
  3803. if (test.call(this, token))
  3804. break;
  3805. }
  3806. }
  3807. };
  3808. return {
  3809. create: function(tokens) {
  3810. return new TokenIterator(tokens);
  3811. }
  3812. };
  3813. });/**
  3814. * A trimmed version of CodeMirror's StringStream module for string parsing
  3815. */
  3816. emmet.define('stringStream', function(require, _) {
  3817. /**
  3818. * @type StringStream
  3819. * @constructor
  3820. * @param {String} string
  3821. */
  3822. function StringStream(string) {
  3823. this.pos = this.start = 0;
  3824. this.string = string;
  3825. }
  3826. StringStream.prototype = {
  3827. /**
  3828. * Returns true only if the stream is at the end of the line.
  3829. * @returns {Boolean}
  3830. */
  3831. eol: function() {
  3832. return this.pos >= this.string.length;
  3833. },
  3834. /**
  3835. * Returns true only if the stream is at the start of the line
  3836. * @returns {Boolean}
  3837. */
  3838. sol: function() {
  3839. return this.pos == 0;
  3840. },
  3841. /**
  3842. * Returns the next character in the stream without advancing it.
  3843. * Will return <code>undefined</code> at the end of the line.
  3844. * @returns {String}
  3845. */
  3846. peek: function() {
  3847. return this.string.charAt(this.pos);
  3848. },
  3849. /**
  3850. * Returns the next character in the stream and advances it.
  3851. * Also returns <code>undefined</code> when no more characters are available.
  3852. * @returns {String}
  3853. */
  3854. next: function() {
  3855. if (this.pos < this.string.length)
  3856. return this.string.charAt(this.pos++);
  3857. },
  3858. /**
  3859. * match can be a character, a regular expression, or a function that
  3860. * takes a character and returns a boolean. If the next character in the
  3861. * stream 'matches' the given argument, it is consumed and returned.
  3862. * Otherwise, undefined is returned.
  3863. * @param {Object} match
  3864. * @returns {String}
  3865. */
  3866. eat: function(match) {
  3867. var ch = this.string.charAt(this.pos), ok;
  3868. if (typeof match == "string")
  3869. ok = ch == match;
  3870. else
  3871. ok = ch && (match.test ? match.test(ch) : match(ch));
  3872. if (ok) {
  3873. ++this.pos;
  3874. return ch;
  3875. }
  3876. },
  3877. /**
  3878. * Repeatedly calls <code>eat</code> with the given argument, until it
  3879. * fails. Returns <code>true</code> if any characters were eaten.
  3880. * @param {Object} match
  3881. * @returns {Boolean}
  3882. */
  3883. eatWhile: function(match) {
  3884. var start = this.pos;
  3885. while (this.eat(match)) {}
  3886. return this.pos > start;
  3887. },
  3888. /**
  3889. * Shortcut for <code>eatWhile</code> when matching white-space.
  3890. * @returns {Boolean}
  3891. */
  3892. eatSpace: function() {
  3893. var start = this.pos;
  3894. while (/[\s\u00a0]/.test(this.string.charAt(this.pos)))
  3895. ++this.pos;
  3896. return this.pos > start;
  3897. },
  3898. /**
  3899. * Moves the position to the end of the line.
  3900. */
  3901. skipToEnd: function() {
  3902. this.pos = this.string.length;
  3903. },
  3904. /**
  3905. * Skips to the next occurrence of the given character, if found on the
  3906. * current line (doesn't advance the stream if the character does not
  3907. * occur on the line). Returns true if the character was found.
  3908. * @param {String} ch
  3909. * @returns {Boolean}
  3910. */
  3911. skipTo: function(ch) {
  3912. var found = this.string.indexOf(ch, this.pos);
  3913. if (found > -1) {
  3914. this.pos = found;
  3915. return true;
  3916. }
  3917. },
  3918. /**
  3919. * Skips to <code>close</code> character which is pair to <code>open</code>
  3920. * character, considering possible pair nesting. This function is used
  3921. * to consume pair of characters, like opening and closing braces
  3922. * @param {String} open
  3923. * @param {String} close
  3924. * @returns {Boolean} Returns <code>true</code> if pair was successfully
  3925. * consumed
  3926. */
  3927. skipToPair: function(open, close) {
  3928. var braceCount = 0, ch;
  3929. var pos = this.pos, len = this.string.length;
  3930. while (pos < len) {
  3931. ch = this.string.charAt(pos++);
  3932. if (ch == open) {
  3933. braceCount++;
  3934. } else if (ch == close) {
  3935. braceCount--;
  3936. if (braceCount < 1) {
  3937. this.pos = pos;
  3938. return true;
  3939. }
  3940. }
  3941. }
  3942. return false;
  3943. },
  3944. /**
  3945. * Backs up the stream n characters. Backing it up further than the
  3946. * start of the current token will cause things to break, so be careful.
  3947. * @param {Number} n
  3948. */
  3949. backUp : function(n) {
  3950. this.pos -= n;
  3951. },
  3952. /**
  3953. * Act like a multi-character <code>eat</code>—if <code>consume</code> is true or
  3954. * not given—or a look-ahead that doesn't update the stream position—if
  3955. * it is false. <code>pattern</code> can be either a string or a
  3956. * regular expression starting with ^. When it is a string,
  3957. * <code>caseInsensitive</code> can be set to true to make the match
  3958. * case-insensitive. When successfully matching a regular expression,
  3959. * the returned value will be the array returned by <code>match</code>,
  3960. * in case you need to extract matched groups.
  3961. *
  3962. * @param {RegExp} pattern
  3963. * @param {Boolean} consume
  3964. * @param {Boolean} caseInsensitive
  3965. * @returns
  3966. */
  3967. match: function(pattern, consume, caseInsensitive) {
  3968. if (typeof pattern == "string") {
  3969. var cased = caseInsensitive
  3970. ? function(str) {return str.toLowerCase();}
  3971. : function(str) {return str;};
  3972. if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
  3973. if (consume !== false)
  3974. this.pos += pattern.length;
  3975. return true;
  3976. }
  3977. } else {
  3978. var match = this.string.slice(this.pos).match(pattern);
  3979. if (match && consume !== false)
  3980. this.pos += match[0].length;
  3981. return match;
  3982. }
  3983. },
  3984. /**
  3985. * Get the string between the start of the current token and the
  3986. * current stream position.
  3987. * @returns {String}
  3988. */
  3989. current: function() {
  3990. return this.string.slice(this.start, this.pos);
  3991. }
  3992. };
  3993. return {
  3994. create: function(string) {
  3995. return new StringStream(string);
  3996. }
  3997. };
  3998. });/**
  3999. * Parsed resources (snippets, abbreviations, variables, etc.) for Emmet.
  4000. * Contains convenient method to get access for snippets with respect of
  4001. * inheritance. Also provides ability to store data in different vocabularies
  4002. * ('system' and 'user') for fast and safe resource update
  4003. * @author Sergey Chikuyonok (serge.che@gmail.com)
  4004. * @link http://chikuyonok.ru
  4005. *
  4006. * @param {Function} require
  4007. * @param {Underscore} _
  4008. */
  4009. emmet.define('resources', function(require, _) {
  4010. var VOC_SYSTEM = 'system';
  4011. var VOC_USER = 'user';
  4012. var cache = {};
  4013. /** Regular expression for XML tag matching */
  4014. var reTag = /^<(\w+\:?[\w\-]*)((?:\s+[\w\:\-]+\s*=\s*(['"]).*?\3)*)\s*(\/?)>/;
  4015. var systemSettings = {};
  4016. var userSettings = {};
  4017. /** @type HandlerList List of registered abbreviation resolvers */
  4018. var resolvers = require('handlerList').create();
  4019. /**
  4020. * Normalizes caret plceholder in passed text: replaces | character with
  4021. * default caret placeholder
  4022. * @param {String} text
  4023. * @returns {String}
  4024. */
  4025. function normalizeCaretPlaceholder(text) {
  4026. var utils = require('utils');
  4027. return utils.replaceUnescapedSymbol(text, '|', utils.getCaretPlaceholder());
  4028. }
  4029. function parseItem(name, value, type) {
  4030. value = normalizeCaretPlaceholder(value);
  4031. if (type == 'snippets') {
  4032. return require('elements').create('snippet', value);
  4033. }
  4034. if (type == 'abbreviations') {
  4035. return parseAbbreviation(name, value);
  4036. }
  4037. }
  4038. /**
  4039. * Parses single abbreviation
  4040. * @param {String} key Abbreviation name
  4041. * @param {String} value Abbreviation value
  4042. * @return {Object}
  4043. */
  4044. function parseAbbreviation(key, value) {
  4045. key = require('utils').trim(key);
  4046. var elements = require('elements');
  4047. var m;
  4048. if (m = reTag.exec(value)) {
  4049. return elements.create('element', m[1], m[2], m[4] == '/');
  4050. } else {
  4051. // assume it's reference to another abbreviation
  4052. return elements.create('reference', value);
  4053. }
  4054. }
  4055. /**
  4056. * Normalizes snippet key name for better fuzzy search
  4057. * @param {String} str
  4058. * @returns {String}
  4059. */
  4060. function normalizeName(str) {
  4061. return str.replace(/:$/, '').replace(/:/g, '-');
  4062. }
  4063. return {
  4064. /**
  4065. * Sets new unparsed data for specified settings vocabulary
  4066. * @param {Object} data
  4067. * @param {String} type Vocabulary type ('system' or 'user')
  4068. * @memberOf resources
  4069. */
  4070. setVocabulary: function(data, type) {
  4071. cache = {};
  4072. if (type == VOC_SYSTEM)
  4073. systemSettings = data;
  4074. else
  4075. userSettings = data;
  4076. },
  4077. /**
  4078. * Returns resource vocabulary by its name
  4079. * @param {String} name Vocabulary name ('system' or 'user')
  4080. * @return {Object}
  4081. */
  4082. getVocabulary: function(name) {
  4083. return name == VOC_SYSTEM ? systemSettings : userSettings;
  4084. },
  4085. /**
  4086. * Returns resource (abbreviation, snippet, etc.) matched for passed
  4087. * abbreviation
  4088. * @param {TreeNode} node
  4089. * @param {String} syntax
  4090. * @returns {Object}
  4091. */
  4092. getMatchedResource: function(node, syntax) {
  4093. return resolvers.exec(null, _.toArray(arguments))
  4094. || this.findSnippet(syntax, node.name());
  4095. },
  4096. /**
  4097. * Returns variable value
  4098. * @return {String}
  4099. */
  4100. getVariable: function(name) {
  4101. return (this.getSection('variables') || {})[name];
  4102. },
  4103. /**
  4104. * Store runtime variable in user storage
  4105. * @param {String} name Variable name
  4106. * @param {String} value Variable value
  4107. */
  4108. setVariable: function(name, value){
  4109. var voc = this.getVocabulary('user') || {};
  4110. if (!('variables' in voc))
  4111. voc.variables = {};
  4112. voc.variables[name] = value;
  4113. this.setVocabulary(voc, 'user');
  4114. },
  4115. /**
  4116. * Check if there are resources for specified syntax
  4117. * @param {String} syntax
  4118. * @return {Boolean}
  4119. */
  4120. hasSyntax: function(syntax) {
  4121. return syntax in this.getVocabulary(VOC_USER)
  4122. || syntax in this.getVocabulary(VOC_SYSTEM);
  4123. },
  4124. /**
  4125. * Registers new abbreviation resolver.
  4126. * @param {Function} fn Abbreviation resolver which will receive
  4127. * abbreviation as first argument and should return parsed abbreviation
  4128. * object if abbreviation has handled successfully, <code>null</code>
  4129. * otherwise
  4130. * @param {Object} options Options list as described in
  4131. * {@link HandlerList#add()} method
  4132. */
  4133. addResolver: function(fn, options) {
  4134. resolvers.add(fn, options);
  4135. },
  4136. removeResolver: function(fn) {
  4137. resolvers.remove(fn);
  4138. },
  4139. /**
  4140. * Returns actual section data, merged from both
  4141. * system and user data
  4142. * @param {String} name Section name (syntax)
  4143. * @param {String} ...args Subsections
  4144. * @returns
  4145. */
  4146. getSection: function(name) {
  4147. if (!name)
  4148. return null;
  4149. if (!(name in cache)) {
  4150. cache[name] = require('utils').deepMerge({}, systemSettings[name], userSettings[name]);
  4151. }
  4152. var data = cache[name], subsections = _.rest(arguments), key;
  4153. while (data && (key = subsections.shift())) {
  4154. if (key in data) {
  4155. data = data[key];
  4156. } else {
  4157. return null;
  4158. }
  4159. }
  4160. return data;
  4161. },
  4162. /**
  4163. * Recursively searches for a item inside top level sections (syntaxes)
  4164. * with respect of `extends` attribute
  4165. * @param {String} topSection Top section name (syntax)
  4166. * @param {String} subsection Inner section name
  4167. * @returns {Object}
  4168. */
  4169. findItem: function(topSection, subsection) {
  4170. var data = this.getSection(topSection);
  4171. while (data) {
  4172. if (subsection in data)
  4173. return data[subsection];
  4174. data = this.getSection(data['extends']);
  4175. }
  4176. },
  4177. /**
  4178. * Recursively searches for a snippet definition inside syntax section.
  4179. * Definition is searched inside `snippets` and `abbreviations`
  4180. * subsections
  4181. * @param {String} syntax Top-level section name (syntax)
  4182. * @param {String} name Snippet name
  4183. * @returns {Object}
  4184. */
  4185. findSnippet: function(syntax, name, memo) {
  4186. if (!syntax || !name)
  4187. return null;
  4188. memo = memo || [];
  4189. var names = [name];
  4190. // create automatic aliases to properties with colons,
  4191. // e.g. pos-a == pos:a
  4192. if (~name.indexOf('-'))
  4193. names.push(name.replace(/\-/g, ':'));
  4194. var data = this.getSection(syntax), matchedItem = null;
  4195. _.find(['snippets', 'abbreviations'], function(sectionName) {
  4196. var data = this.getSection(syntax, sectionName);
  4197. if (data) {
  4198. return _.find(names, function(n) {
  4199. if (data[n])
  4200. return matchedItem = parseItem(n, data[n], sectionName);
  4201. });
  4202. }
  4203. }, this);
  4204. memo.push(syntax);
  4205. if (!matchedItem && data['extends'] && !_.include(memo, data['extends'])) {
  4206. // try to find item in parent syntax section
  4207. return this.findSnippet(data['extends'], name, memo);
  4208. }
  4209. return matchedItem;
  4210. },
  4211. /**
  4212. * Performs fuzzy search of snippet definition
  4213. * @param {String} syntax Top-level section name (syntax)
  4214. * @param {String} name Snippet name
  4215. * @returns
  4216. */
  4217. fuzzyFindSnippet: function(syntax, name, minScore) {
  4218. minScore = minScore || 0.3;
  4219. var payload = this.getAllSnippets(syntax);
  4220. var sc = require('string-score');
  4221. name = normalizeName(name);
  4222. var scores = _.map(payload, function(value, key) {
  4223. return {
  4224. key: key,
  4225. score: sc.score(value.nk, name, 0.1)
  4226. };
  4227. });
  4228. var result = _.last(_.sortBy(scores, 'score'));
  4229. if (result && result.score >= minScore) {
  4230. var k = result.key;
  4231. return payload[k].parsedValue;
  4232. // return parseItem(k, payload[k].value, payload[k].type);
  4233. }
  4234. },
  4235. /**
  4236. * Returns plain dictionary of all available abbreviations and snippets
  4237. * for specified syntax with respect of inheritance
  4238. * @param {String} syntax
  4239. * @returns {Object}
  4240. */
  4241. getAllSnippets: function(syntax) {
  4242. var cacheKey = 'all-' + syntax;
  4243. if (!cache[cacheKey]) {
  4244. var stack = [], sectionKey = syntax;
  4245. var memo = [];
  4246. do {
  4247. var section = this.getSection(sectionKey);
  4248. if (!section)
  4249. break;
  4250. _.each(['snippets', 'abbreviations'], function(sectionName) {
  4251. var stackItem = {};
  4252. _.each(section[sectionName] || null, function(v, k) {
  4253. stackItem[k] = {
  4254. nk: normalizeName(k),
  4255. value: v,
  4256. parsedValue: parseItem(k, v, sectionName),
  4257. type: sectionName
  4258. };
  4259. });
  4260. stack.push(stackItem);
  4261. });
  4262. memo.push(sectionKey);
  4263. sectionKey = section['extends'];
  4264. } while (sectionKey && !_.include(memo, sectionKey));
  4265. cache[cacheKey] = _.extend.apply(_, stack.reverse());
  4266. }
  4267. return cache[cacheKey];
  4268. }
  4269. };
  4270. });/**
  4271. * Module describes and performs Emmet actions. The actions themselves are
  4272. * defined in <i>actions</i> folder
  4273. * @param {Function} require
  4274. * @param {Underscore} _
  4275. */
  4276. emmet.define('actions', function(require, _, zc) {
  4277. var actions = {};
  4278. /**
  4279. * “Humanizes” action name, makes it more readable for people
  4280. * @param {String} name Action name (like 'expand_abbreviation')
  4281. * @return Humanized name (like 'Expand Abbreviation')
  4282. */
  4283. function humanizeActionName(name) {
  4284. return require('utils').trim(name.charAt(0).toUpperCase()
  4285. + name.substring(1).replace(/_[a-z]/g, function(str) {
  4286. return ' ' + str.charAt(1).toUpperCase();
  4287. }));
  4288. }
  4289. return {
  4290. /**
  4291. * Registers new action
  4292. * @param {String} name Action name
  4293. * @param {Function} fn Action function
  4294. * @param {Object} options Custom action options:<br>
  4295. * <b>label</b> : (<code>String</code>) – Human-readable action name.
  4296. * May contain '/' symbols as submenu separators<br>
  4297. * <b>hidden</b> : (<code>Boolean</code>) – Indicates whether action
  4298. * should be displayed in menu (<code>getMenu()</code> method)
  4299. *
  4300. * @memberOf actions
  4301. */
  4302. add: function(name, fn, options) {
  4303. name = name.toLowerCase();
  4304. options = options || {};
  4305. if (!options.label) {
  4306. options.label = humanizeActionName(name);
  4307. }
  4308. actions[name] = {
  4309. name: name,
  4310. fn: fn,
  4311. options: options
  4312. };
  4313. },
  4314. /**
  4315. * Returns action object
  4316. * @param {String} name Action name
  4317. * @returns {Object}
  4318. */
  4319. get: function(name) {
  4320. return actions[name.toLowerCase()];
  4321. },
  4322. /**
  4323. * Runs Emmet action. For list of available actions and their
  4324. * arguments see <i>actions</i> folder.
  4325. * @param {String} name Action name
  4326. * @param {Array} args Additional arguments. It may be array of arguments
  4327. * or inline arguments. The first argument should be <code>IEmmetEditor</code> instance
  4328. * @returns {Boolean} Status of performed operation, <code>true</code>
  4329. * means action was performed successfully.
  4330. * @example
  4331. * emmet.require('actions').run('expand_abbreviation', editor);
  4332. * emmet.require('actions').run('wrap_with_abbreviation', [editor, 'div']);
  4333. */
  4334. run: function(name, args) {
  4335. if (!_.isArray(args)) {
  4336. args = _.rest(arguments);
  4337. }
  4338. var action = this.get(name);
  4339. if (action) {
  4340. return action.fn.apply(emmet, args);
  4341. } else {
  4342. emmet.log('Action "%s" is not defined', name);
  4343. return false;
  4344. }
  4345. },
  4346. /**
  4347. * Returns all registered actions as object
  4348. * @returns {Object}
  4349. */
  4350. getAll: function() {
  4351. return actions;
  4352. },
  4353. /**
  4354. * Returns all registered actions as array
  4355. * @returns {Array}
  4356. */
  4357. getList: function() {
  4358. return _.values(this.getAll());
  4359. },
  4360. /**
  4361. * Returns actions list as structured menu. If action has <i>label</i>,
  4362. * it will be splitted by '/' symbol into submenus (for example:
  4363. * CSS/Reflect Value) and grouped with other items
  4364. * @param {Array} skipActions List of action identifiers that should be
  4365. * skipped from menu
  4366. * @returns {Array}
  4367. */
  4368. getMenu: function(skipActions) {
  4369. var result = [];
  4370. skipActions = skipActions || [];
  4371. _.each(this.getList(), function(action) {
  4372. if (action.options.hidden || _.include(skipActions, action.name))
  4373. return;
  4374. var actionName = humanizeActionName(action.name);
  4375. var ctx = result;
  4376. if (action.options.label) {
  4377. var parts = action.options.label.split('/');
  4378. actionName = parts.pop();
  4379. // create submenus, if needed
  4380. var menuName, submenu;
  4381. while (menuName = parts.shift()) {
  4382. submenu = _.find(ctx, function(item) {
  4383. return item.type == 'submenu' && item.name == menuName;
  4384. });
  4385. if (!submenu) {
  4386. submenu = {
  4387. name: menuName,
  4388. type: 'submenu',
  4389. items: []
  4390. };
  4391. ctx.push(submenu);
  4392. }
  4393. ctx = submenu.items;
  4394. }
  4395. }
  4396. ctx.push({
  4397. type: 'action',
  4398. name: action.name,
  4399. label: actionName
  4400. });
  4401. });
  4402. return result;
  4403. },
  4404. /**
  4405. * Returns action name associated with menu item title
  4406. * @param {String} title
  4407. * @returns {String}
  4408. */
  4409. getActionNameForMenuTitle: function(title, menu) {
  4410. var item = null;
  4411. _.find(menu || this.getMenu(), function(val) {
  4412. if (val.type == 'action') {
  4413. if (val.label == title || val.name == title) {
  4414. return item = val.name;
  4415. }
  4416. } else {
  4417. return item = this.getActionNameForMenuTitle(title, val.items);
  4418. }
  4419. }, this);
  4420. return item || null;
  4421. }
  4422. };
  4423. });/**
  4424. * Output profile module.
  4425. * Profile defines how XHTML output data should look like
  4426. * @param {Function} require
  4427. * @param {Underscore} _
  4428. */
  4429. emmet.define('profile', function(require, _) {
  4430. var profiles = {};
  4431. var defaultProfile = {
  4432. tag_case: 'asis',
  4433. attr_case: 'asis',
  4434. attr_quotes: 'double',
  4435. // each tag on new line
  4436. tag_nl: 'decide',
  4437. // with tag_nl === true, defines if leaf node (e.g. node with no children)
  4438. // should have formatted line breaks
  4439. tag_nl_leaf: false,
  4440. place_cursor: true,
  4441. // indent tags
  4442. indent: true,
  4443. // how many inline elements should be to force line break
  4444. // (set to 0 to disable)
  4445. inline_break: 3,
  4446. // use self-closing style for writing empty elements, e.g. <br /> or <br>
  4447. self_closing_tag: 'xhtml',
  4448. // Profile-level output filters, re-defines syntax filters
  4449. filters: '',
  4450. // Additional filters applied to abbreviation.
  4451. // Unlike "filters", this preference doesn't override default filters
  4452. // but add the instead every time given profile is chosen
  4453. extraFilters: ''
  4454. };
  4455. /**
  4456. * @constructor
  4457. * @type OutputProfile
  4458. * @param {Object} options
  4459. */
  4460. function OutputProfile(options) {
  4461. _.extend(this, defaultProfile, options);
  4462. }
  4463. OutputProfile.prototype = {
  4464. /**
  4465. * Transforms tag name case depending on current profile settings
  4466. * @param {String} name String to transform
  4467. * @returns {String}
  4468. */
  4469. tagName: function(name) {
  4470. return stringCase(name, this.tag_case);
  4471. },
  4472. /**
  4473. * Transforms attribute name case depending on current profile settings
  4474. * @param {String} name String to transform
  4475. * @returns {String}
  4476. */
  4477. attributeName: function(name) {
  4478. return stringCase(name, this.attr_case);
  4479. },
  4480. /**
  4481. * Returns quote character for current profile
  4482. * @returns {String}
  4483. */
  4484. attributeQuote: function() {
  4485. return this.attr_quotes == 'single' ? "'" : '"';
  4486. },
  4487. /**
  4488. * Returns self-closing tag symbol for current profile
  4489. * @param {String} param
  4490. * @returns {String}
  4491. */
  4492. selfClosing: function(param) {
  4493. if (this.self_closing_tag == 'xhtml')
  4494. return ' /';
  4495. if (this.self_closing_tag === true)
  4496. return '/';
  4497. return '';
  4498. },
  4499. /**
  4500. * Returns cursor token based on current profile settings
  4501. * @returns {String}
  4502. */
  4503. cursor: function() {
  4504. return this.place_cursor ? require('utils').getCaretPlaceholder() : '';
  4505. }
  4506. };
  4507. /**
  4508. * Helper function that converts string case depending on
  4509. * <code>caseValue</code>
  4510. * @param {String} str String to transform
  4511. * @param {String} caseValue Case value: can be <i>lower</i>,
  4512. * <i>upper</i> and <i>leave</i>
  4513. * @returns {String}
  4514. */
  4515. function stringCase(str, caseValue) {
  4516. switch (String(caseValue || '').toLowerCase()) {
  4517. case 'lower':
  4518. return str.toLowerCase();
  4519. case 'upper':
  4520. return str.toUpperCase();
  4521. }
  4522. return str;
  4523. }
  4524. /**
  4525. * Creates new output profile
  4526. * @param {String} name Profile name
  4527. * @param {Object} options Profile options
  4528. */
  4529. function createProfile(name, options) {
  4530. return profiles[name.toLowerCase()] = new OutputProfile(options);
  4531. }
  4532. function createDefaultProfiles() {
  4533. createProfile('xhtml');
  4534. createProfile('html', {self_closing_tag: false});
  4535. createProfile('xml', {self_closing_tag: true, tag_nl: true});
  4536. createProfile('plain', {tag_nl: false, indent: false, place_cursor: false});
  4537. createProfile('line', {tag_nl: false, indent: false, extraFilters: 's'});
  4538. }
  4539. createDefaultProfiles();
  4540. return {
  4541. /**
  4542. * Creates new output profile and adds it into internal dictionary
  4543. * @param {String} name Profile name
  4544. * @param {Object} options Profile options
  4545. * @memberOf emmet.profile
  4546. * @returns {Object} New profile
  4547. */
  4548. create: function(name, options) {
  4549. if (arguments.length == 2)
  4550. return createProfile(name, options);
  4551. else
  4552. // create profile object only
  4553. return new OutputProfile(_.defaults(name || {}, defaultProfile));
  4554. },
  4555. /**
  4556. * Returns profile by its name. If profile wasn't found, returns
  4557. * 'plain' profile
  4558. * @param {String} name Profile name. Might be profile itself
  4559. * @param {String} syntax. Optional. Current editor syntax. If defined,
  4560. * profile is searched in resources first, then in predefined profiles
  4561. * @returns {Object}
  4562. */
  4563. get: function(name, syntax) {
  4564. if (!name && syntax) {
  4565. // search in user resources first
  4566. var profile = require('resources').findItem(syntax, 'profile');
  4567. if (profile) {
  4568. name = profile;
  4569. }
  4570. }
  4571. if (!name) {
  4572. return profiles.plain;
  4573. }
  4574. if (name instanceof OutputProfile) {
  4575. return name;
  4576. }
  4577. if (_.isString(name) && name.toLowerCase() in profiles) {
  4578. return profiles[name.toLowerCase()];
  4579. }
  4580. return this.create(name);
  4581. },
  4582. /**
  4583. * Deletes profile with specified name
  4584. * @param {String} name Profile name
  4585. */
  4586. remove: function(name) {
  4587. name = (name || '').toLowerCase();
  4588. if (name in profiles)
  4589. delete profiles[name];
  4590. },
  4591. /**
  4592. * Resets all user-defined profiles
  4593. */
  4594. reset: function() {
  4595. profiles = {};
  4596. createDefaultProfiles();
  4597. },
  4598. /**
  4599. * Helper function that converts string case depending on
  4600. * <code>caseValue</code>
  4601. * @param {String} str String to transform
  4602. * @param {String} caseValue Case value: can be <i>lower</i>,
  4603. * <i>upper</i> and <i>leave</i>
  4604. * @returns {String}
  4605. */
  4606. stringCase: stringCase
  4607. };
  4608. });/**
  4609. * Utility module used to prepare text for pasting into back-end editor
  4610. * @param {Function} require
  4611. * @param {Underscore} _
  4612. * @author Sergey Chikuyonok (serge.che@gmail.com) <http://chikuyonok.ru>
  4613. */
  4614. emmet.define('editorUtils', function(require, _) {
  4615. return {
  4616. /**
  4617. * Check if cursor is placed inside XHTML tag
  4618. * @param {String} html Contents of the document
  4619. * @param {Number} caretPos Current caret position inside tag
  4620. * @return {Boolean}
  4621. */
  4622. isInsideTag: function(html, caretPos) {
  4623. var reTag = /^<\/?\w[\w\:\-]*.*?>/;
  4624. // search left to find opening brace
  4625. var pos = caretPos;
  4626. while (pos > -1) {
  4627. if (html.charAt(pos) == '<')
  4628. break;
  4629. pos--;
  4630. }
  4631. if (pos != -1) {
  4632. var m = reTag.exec(html.substring(pos));
  4633. if (m && caretPos > pos && caretPos < pos + m[0].length)
  4634. return true;
  4635. }
  4636. return false;
  4637. },
  4638. /**
  4639. * Sanitizes incoming editor data and provides default values for
  4640. * output-specific info
  4641. * @param {IEmmetEditor} editor
  4642. * @param {String} syntax
  4643. * @param {String} profile
  4644. */
  4645. outputInfo: function(editor, syntax, profile) {
  4646. // most of this code makes sense for Java/Rhino environment
  4647. // because string that comes from Java are not actually JS string
  4648. // but Java String object so the have to be explicitly converted
  4649. // to native string
  4650. profile = profile || editor.getProfileName();
  4651. return {
  4652. /** @memberOf outputInfo */
  4653. syntax: String(syntax || editor.getSyntax()),
  4654. profile: profile ? String(profile) : null,
  4655. content: String(editor.getContent())
  4656. };
  4657. },
  4658. /**
  4659. * Unindent content, thus preparing text for tag wrapping
  4660. * @param {IEmmetEditor} editor Editor instance
  4661. * @param {String} text
  4662. * @return {String}
  4663. */
  4664. unindent: function(editor, text) {
  4665. return require('utils').unindentString(text, this.getCurrentLinePadding(editor));
  4666. },
  4667. /**
  4668. * Returns padding of current editor's line
  4669. * @param {IEmmetEditor} Editor instance
  4670. * @return {String}
  4671. */
  4672. getCurrentLinePadding: function(editor) {
  4673. return require('utils').getLinePadding(editor.getCurrentLine());
  4674. }
  4675. };
  4676. });
  4677. /**
  4678. * Utility methods for Emmet actions
  4679. * @param {Function} require
  4680. * @param {Underscore} _
  4681. * @author Sergey Chikuyonok (serge.che@gmail.com) <http://chikuyonok.ru>
  4682. */
  4683. emmet.define('actionUtils', function(require, _) {
  4684. return {
  4685. mimeTypes: {
  4686. 'gif' : 'image/gif',
  4687. 'png' : 'image/png',
  4688. 'jpg' : 'image/jpeg',
  4689. 'jpeg': 'image/jpeg',
  4690. 'svg' : 'image/svg+xml',
  4691. 'html': 'text/html',
  4692. 'htm' : 'text/html'
  4693. },
  4694. /**
  4695. * Extracts abbreviations from text stream, starting from the end
  4696. * @param {String} str
  4697. * @return {String} Abbreviation or empty string
  4698. * @memberOf emmet.actionUtils
  4699. */
  4700. extractAbbreviation: function(str) {
  4701. var curOffset = str.length;
  4702. var startIndex = -1;
  4703. var groupCount = 0;
  4704. var braceCount = 0;
  4705. var textCount = 0;
  4706. var utils = require('utils');
  4707. var parser = require('abbreviationParser');
  4708. while (true) {
  4709. curOffset--;
  4710. if (curOffset < 0) {
  4711. // moved to the beginning of the line
  4712. startIndex = 0;
  4713. break;
  4714. }
  4715. var ch = str.charAt(curOffset);
  4716. if (ch == ']') {
  4717. braceCount++;
  4718. } else if (ch == '[') {
  4719. if (!braceCount) { // unexpected brace
  4720. startIndex = curOffset + 1;
  4721. break;
  4722. }
  4723. braceCount--;
  4724. } else if (ch == '}') {
  4725. textCount++;
  4726. } else if (ch == '{') {
  4727. if (!textCount) { // unexpected brace
  4728. startIndex = curOffset + 1;
  4729. break;
  4730. }
  4731. textCount--;
  4732. } else if (ch == ')') {
  4733. groupCount++;
  4734. } else if (ch == '(') {
  4735. if (!groupCount) { // unexpected brace
  4736. startIndex = curOffset + 1;
  4737. break;
  4738. }
  4739. groupCount--;
  4740. } else {
  4741. if (braceCount || textCount)
  4742. // respect all characters inside attribute sets or text nodes
  4743. continue;
  4744. else if (!parser.isAllowedChar(ch) || (ch == '>' && utils.endsWithTag(str.substring(0, curOffset + 1)))) {
  4745. // found stop symbol
  4746. startIndex = curOffset + 1;
  4747. break;
  4748. }
  4749. }
  4750. }
  4751. if (startIndex != -1 && !textCount && !braceCount && !groupCount)
  4752. // found something, remove some invalid symbols from the
  4753. // beginning and return abbreviation
  4754. return str.substring(startIndex).replace(/^[\*\+\>\^]+/, '');
  4755. else
  4756. return '';
  4757. },
  4758. /**
  4759. * Gets image size from image byte stream.
  4760. * @author http://romeda.org/rePublish/
  4761. * @param {String} stream Image byte stream (use <code>IEmmetFile.read()</code>)
  4762. * @return {Object} Object with <code>width</code> and <code>height</code> properties
  4763. */
  4764. getImageSize: function(stream) {
  4765. var pngMagicNum = "\211PNG\r\n\032\n",
  4766. jpgMagicNum = "\377\330",
  4767. gifMagicNum = "GIF8",
  4768. nextByte = function() {
  4769. return stream.charCodeAt(pos++);
  4770. };
  4771. if (stream.substr(0, 8) === pngMagicNum) {
  4772. // PNG. Easy peasy.
  4773. var pos = stream.indexOf('IHDR') + 4;
  4774. return { width: (nextByte() << 24) | (nextByte() << 16) |
  4775. (nextByte() << 8) | nextByte(),
  4776. height: (nextByte() << 24) | (nextByte() << 16) |
  4777. (nextByte() << 8) | nextByte() };
  4778. } else if (stream.substr(0, 4) === gifMagicNum) {
  4779. pos = 6;
  4780. return {
  4781. width: nextByte() | (nextByte() << 8),
  4782. height: nextByte() | (nextByte() << 8)
  4783. };
  4784. } else if (stream.substr(0, 2) === jpgMagicNum) {
  4785. pos = 2;
  4786. var l = stream.length;
  4787. while (pos < l) {
  4788. if (nextByte() != 0xFF) return;
  4789. var marker = nextByte();
  4790. if (marker == 0xDA) break;
  4791. var size = (nextByte() << 8) | nextByte();
  4792. if (marker >= 0xC0 && marker <= 0xCF && !(marker & 0x4) && !(marker & 0x8)) {
  4793. pos += 1;
  4794. return { height: (nextByte() << 8) | nextByte(),
  4795. width: (nextByte() << 8) | nextByte() };
  4796. } else {
  4797. pos += size - 2;
  4798. }
  4799. }
  4800. }
  4801. },
  4802. /**
  4803. * Captures context XHTML element from editor under current caret position.
  4804. * This node can be used as a helper for abbreviation extraction
  4805. * @param {IEmmetEditor} editor
  4806. * @returns {Object}
  4807. */
  4808. captureContext: function(editor) {
  4809. var allowedSyntaxes = {'html': 1, 'xml': 1, 'xsl': 1};
  4810. var syntax = String(editor.getSyntax());
  4811. if (syntax in allowedSyntaxes) {
  4812. var content = String(editor.getContent());
  4813. var tag = require('htmlMatcher').find(content, editor.getCaretPos());
  4814. if (tag && tag.type == 'tag') {
  4815. var reAttr = /([\w\-:]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
  4816. var startTag = tag.open;
  4817. var tagAttrs = startTag.range.substring(content).replace(/^<[\w\-\:]+/, '');
  4818. // var tagAttrs = startTag.full_tag.replace(/^<[\w\-\:]+/, '');
  4819. var contextNode = {
  4820. name: startTag.name,
  4821. attributes: []
  4822. };
  4823. // parse attributes
  4824. var m;
  4825. while (m = reAttr.exec(tagAttrs)) {
  4826. contextNode.attributes.push({
  4827. name: m[1],
  4828. value: m[2]
  4829. });
  4830. }
  4831. return contextNode;
  4832. }
  4833. }
  4834. return null;
  4835. },
  4836. /**
  4837. * Find expression bounds in current editor at caret position.
  4838. * On each character a <code>fn</code> function will be called and must
  4839. * return <code>true</code> if current character meets requirements,
  4840. * <code>false</code> otherwise
  4841. * @param {IEmmetEditor} editor
  4842. * @param {Function} fn Function to test each character of expression
  4843. * @return {Range}
  4844. */
  4845. findExpressionBounds: function(editor, fn) {
  4846. var content = String(editor.getContent());
  4847. var il = content.length;
  4848. var exprStart = editor.getCaretPos() - 1;
  4849. var exprEnd = exprStart + 1;
  4850. // start by searching left
  4851. while (exprStart >= 0 && fn(content.charAt(exprStart), exprStart, content)) exprStart--;
  4852. // then search right
  4853. while (exprEnd < il && fn(content.charAt(exprEnd), exprEnd, content)) exprEnd++;
  4854. if (exprEnd > exprStart) {
  4855. return require('range').create([++exprStart, exprEnd]);
  4856. }
  4857. },
  4858. /**
  4859. * @param {IEmmetEditor} editor
  4860. * @param {Object} data
  4861. * @returns {Boolean}
  4862. */
  4863. compoundUpdate: function(editor, data) {
  4864. if (data) {
  4865. var sel = editor.getSelectionRange();
  4866. editor.replaceContent(data.data, data.start, data.end, true);
  4867. editor.createSelection(data.caret, data.caret + sel.end - sel.start);
  4868. return true;
  4869. }
  4870. return false;
  4871. },
  4872. /**
  4873. * Common syntax detection method for editors that doesn’t provide any
  4874. * info about current syntax scope.
  4875. * @param {IEmmetEditor} editor Current editor
  4876. * @param {String} hint Any syntax hint that editor can provide
  4877. * for syntax detection. Default is 'html'
  4878. * @returns {String}
  4879. */
  4880. detectSyntax: function(editor, hint) {
  4881. var syntax = hint || 'html';
  4882. if (!require('resources').hasSyntax(syntax)) {
  4883. syntax = 'html';
  4884. }
  4885. if (syntax == 'html' && (this.isStyle(editor) || this.isInlineCSS(editor))) {
  4886. syntax = 'css';
  4887. }
  4888. return syntax;
  4889. },
  4890. /**
  4891. * Common method for detecting output profile
  4892. * @param {IEmmetEditor} editor
  4893. * @returns {String}
  4894. */
  4895. detectProfile: function(editor) {
  4896. var syntax = editor.getSyntax();
  4897. // get profile from syntax definition
  4898. var profile = require('resources').findItem(syntax, 'profile');
  4899. if (profile) {
  4900. return profile;
  4901. }
  4902. switch(syntax) {
  4903. case 'xml':
  4904. case 'xsl':
  4905. return 'xml';
  4906. case 'css':
  4907. if (this.isInlineCSS(editor)) {
  4908. return 'line';
  4909. }
  4910. break;
  4911. case 'html':
  4912. var profile = require('resources').getVariable('profile');
  4913. if (!profile) { // no forced profile, guess from content
  4914. // html or xhtml?
  4915. profile = this.isXHTML(editor) ? 'xhtml': 'html';
  4916. }
  4917. return profile;
  4918. }
  4919. return 'xhtml';
  4920. },
  4921. /**
  4922. * Tries to detect if current document is XHTML one.
  4923. * @param {IEmmetEditor} editor
  4924. * @returns {Boolean}
  4925. */
  4926. isXHTML: function(editor) {
  4927. return editor.getContent().search(/<!DOCTYPE[^>]+XHTML/i) != -1;
  4928. },
  4929. /**
  4930. * Check if current caret position is inside &lt;style&gt; tag
  4931. * @param {IEmmetEditor} editor
  4932. * @returns
  4933. */
  4934. isStyle: function(editor) {
  4935. var content = String(editor.getContent());
  4936. var caretPos = editor.getCaretPos();
  4937. var tag = require('htmlMatcher').tag(content, caretPos);
  4938. return tag && tag.open.name.toLowerCase() == 'style'
  4939. && tag.innerRange.cmp(caretPos, 'lte', 'gte');
  4940. },
  4941. /**
  4942. * Check if current caret position is inside "style" attribute of HTML
  4943. * element
  4944. * @param {IEmmetEditor} editor
  4945. * @returns {Boolean}
  4946. */
  4947. isInlineCSS: function(editor) {
  4948. var content = String(editor.getContent());
  4949. var caretPos = editor.getCaretPos();
  4950. var tree = require('xmlEditTree').parseFromPosition(content, caretPos, true);
  4951. if (tree) {
  4952. var attr = tree.itemFromPosition(caretPos, true);
  4953. return attr && attr.name().toLowerCase() == 'style'
  4954. && attr.valueRange(true).cmp(caretPos, 'lte', 'gte');
  4955. }
  4956. return false;
  4957. }
  4958. };
  4959. });/**
  4960. * Utility functions to work with <code>AbbreviationNode</code> as HTML element
  4961. * @param {Function} require
  4962. * @param {Underscore} _
  4963. */
  4964. emmet.define('abbreviationUtils', function(require, _) {
  4965. return {
  4966. /**
  4967. * Check if passed abbreviation node has matched snippet resource
  4968. * @param {AbbreviationNode} node
  4969. * @returns {Boolean}
  4970. * @memberOf abbreviationUtils
  4971. */
  4972. isSnippet: function(node) {
  4973. return require('elements').is(node.matchedResource(), 'snippet');
  4974. },
  4975. /**
  4976. * Test if passed node is unary (no closing tag)
  4977. * @param {AbbreviationNode} node
  4978. * @return {Boolean}
  4979. */
  4980. isUnary: function(node) {
  4981. if (node.children.length || node._text || this.isSnippet(node)) {
  4982. return false;
  4983. }
  4984. var r = node.matchedResource();
  4985. return r && r.is_empty;
  4986. },
  4987. /**
  4988. * Test if passed node is inline-level (like &lt;strong&gt;, &lt;img&gt;)
  4989. * @param {AbbreviationNode} node
  4990. * @return {Boolean}
  4991. */
  4992. isInline: function(node) {
  4993. return node.isTextNode()
  4994. || !node.name()
  4995. || require('tagName').isInlineLevel(node.name());
  4996. },
  4997. /**
  4998. * Test if passed node is block-level
  4999. * @param {AbbreviationNode} node
  5000. * @return {Boolean}
  5001. */
  5002. isBlock: function(node) {
  5003. return this.isSnippet(node) || !this.isInline(node);
  5004. },
  5005. /**
  5006. * Test if given node is a snippet
  5007. * @param {AbbreviationNode} node
  5008. * @return {Boolean}
  5009. */
  5010. isSnippet: function(node) {
  5011. return require('elements').is(node.matchedResource(), 'snippet');
  5012. },
  5013. /**
  5014. * This function tests if passed node content contains HTML tags.
  5015. * This function is mostly used for output formatting
  5016. * @param {AbbreviationNode} node
  5017. * @returns {Boolean}
  5018. */
  5019. hasTagsInContent: function(node) {
  5020. return require('utils').matchesTag(node.content);
  5021. },
  5022. /**
  5023. * Test if current element contains block-level children
  5024. * @param {AbbreviationNode} node
  5025. * @return {Boolean}
  5026. */
  5027. hasBlockChildren: function(node) {
  5028. return (this.hasTagsInContent(node) && this.isBlock(node))
  5029. || _.any(node.children, function(child) {
  5030. return this.isBlock(child);
  5031. }, this);
  5032. },
  5033. /**
  5034. * Utility function that inserts content instead of <code>${child}</code>
  5035. * variables on <code>text</code>
  5036. * @param {String} text Text where child content should be inserted
  5037. * @param {String} childContent Content to insert
  5038. * @param {Object} options
  5039. * @returns {String
  5040. */
  5041. insertChildContent: function(text, childContent, options) {
  5042. options = _.extend({
  5043. keepVariable: true,
  5044. appendIfNoChild: true
  5045. }, options || {});
  5046. var childVariableReplaced = false;
  5047. var utils = require('utils');
  5048. text = utils.replaceVariables(text, function(variable, name, data) {
  5049. var output = variable;
  5050. if (name == 'child') {
  5051. // add correct indentation
  5052. output = utils.padString(childContent, utils.getLinePaddingFromPosition(text, data.start));
  5053. childVariableReplaced = true;
  5054. if (options.keepVariable)
  5055. output += variable;
  5056. }
  5057. return output;
  5058. });
  5059. if (!childVariableReplaced && options.appendIfNoChild) {
  5060. text += childContent;
  5061. }
  5062. return text;
  5063. }
  5064. };
  5065. });/**
  5066. * @author Sergey Chikuyonok (serge.che@gmail.com)
  5067. * @link http://chikuyonok.ru
  5068. */
  5069. emmet.define('base64', function(require, _) {
  5070. var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  5071. return {
  5072. /**
  5073. * Encodes data using base64 algorithm
  5074. * @author Tyler Akins (http://rumkin.com)
  5075. * @param {String} input
  5076. * @returns {String}
  5077. * @memberOf emmet.base64
  5078. */
  5079. encode : function(input) {
  5080. var output = [];
  5081. var chr1, chr2, chr3, enc1, enc2, enc3, enc4, cdp1, cdp2, cdp3;
  5082. var i = 0, il = input.length, b64 = chars;
  5083. while (i < il) {
  5084. cdp1 = input.charCodeAt(i++);
  5085. cdp2 = input.charCodeAt(i++);
  5086. cdp3 = input.charCodeAt(i++);
  5087. chr1 = cdp1 & 0xff;
  5088. chr2 = cdp2 & 0xff;
  5089. chr3 = cdp3 & 0xff;
  5090. enc1 = chr1 >> 2;
  5091. enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
  5092. enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
  5093. enc4 = chr3 & 63;
  5094. if (isNaN(cdp2)) {
  5095. enc3 = enc4 = 64;
  5096. } else if (isNaN(cdp3)) {
  5097. enc4 = 64;
  5098. }
  5099. output.push(b64.charAt(enc1) + b64.charAt(enc2) + b64.charAt(enc3) + b64.charAt(enc4));
  5100. }
  5101. return output.join('');
  5102. },
  5103. /**
  5104. * Decodes string using MIME base64 algorithm
  5105. *
  5106. * @author Tyler Akins (http://rumkin.com)
  5107. * @param {String} data
  5108. * @return {String}
  5109. */
  5110. decode : function(data) {
  5111. var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, tmpArr = [];
  5112. var b64 = chars, il = data.length;
  5113. if (!data) {
  5114. return data;
  5115. }
  5116. data += '';
  5117. do { // unpack four hexets into three octets using index points in b64
  5118. h1 = b64.indexOf(data.charAt(i++));
  5119. h2 = b64.indexOf(data.charAt(i++));
  5120. h3 = b64.indexOf(data.charAt(i++));
  5121. h4 = b64.indexOf(data.charAt(i++));
  5122. bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
  5123. o1 = bits >> 16 & 0xff;
  5124. o2 = bits >> 8 & 0xff;
  5125. o3 = bits & 0xff;
  5126. if (h3 == 64) {
  5127. tmpArr[ac++] = String.fromCharCode(o1);
  5128. } else if (h4 == 64) {
  5129. tmpArr[ac++] = String.fromCharCode(o1, o2);
  5130. } else {
  5131. tmpArr[ac++] = String.fromCharCode(o1, o2, o3);
  5132. }
  5133. } while (i < il);
  5134. return tmpArr.join('');
  5135. }
  5136. };
  5137. });/**
  5138. * HTML matcher: takes string and searches for HTML tag pairs for given position
  5139. *
  5140. * Unlike “classic” matchers, it parses content from the specified
  5141. * position, not from the start, so it may work even outside HTML documents
  5142. * (for example, inside strings of programming languages like JavaScript, Python
  5143. * etc.)
  5144. * @constructor
  5145. * @memberOf __htmlMatcherDefine
  5146. */
  5147. emmet.define('htmlMatcher', function(require, _) {
  5148. // Regular Expressions for parsing tags and attributes
  5149. var reOpenTag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
  5150. var reCloseTag = /^<\/([\w\:\-]+)[^>]*>/;
  5151. function openTag(i, match) {
  5152. return {
  5153. name: match[1],
  5154. selfClose: !!match[3],
  5155. /** @type Range */
  5156. range: require('range').create(i, match[0]),
  5157. type: 'open'
  5158. };
  5159. }
  5160. function closeTag(i, match) {
  5161. return {
  5162. name: match[1],
  5163. /** @type Range */
  5164. range: require('range').create(i, match[0]),
  5165. type: 'close'
  5166. };
  5167. }
  5168. function comment(i, match) {
  5169. return {
  5170. /** @type Range */
  5171. range: require('range').create(i, _.isNumber(match) ? match - i : match[0]),
  5172. type: 'comment'
  5173. };
  5174. }
  5175. /**
  5176. * Creates new tag matcher session
  5177. * @param {String} text
  5178. */
  5179. function createMatcher(text) {
  5180. var memo = {}, m;
  5181. return {
  5182. /**
  5183. * Test if given position matches opening tag
  5184. * @param {Number} i
  5185. * @returns {Object} Matched tag object
  5186. */
  5187. open: function(i) {
  5188. var m = this.matches(i);
  5189. return m && m.type == 'open' ? m : null;
  5190. },
  5191. /**
  5192. * Test if given position matches closing tag
  5193. * @param {Number} i
  5194. * @returns {Object} Matched tag object
  5195. */
  5196. close: function(i) {
  5197. var m = this.matches(i);
  5198. return m && m.type == 'close' ? m : null;
  5199. },
  5200. /**
  5201. * Matches either opening or closing tag for given position
  5202. * @param i
  5203. * @returns
  5204. */
  5205. matches: function(i) {
  5206. var key = 'p' + i;
  5207. if (!(key in memo)) {
  5208. if (text.charAt(i) == '<') {
  5209. var substr = text.slice(i);
  5210. if (m = substr.match(reOpenTag)) {
  5211. memo[key] = openTag(i, m);
  5212. } else if (m = substr.match(reCloseTag)) {
  5213. memo[key] = closeTag(i, m);
  5214. } else {
  5215. // remember that given position contains no valid tag
  5216. memo[key] = false;
  5217. }
  5218. }
  5219. }
  5220. return memo[key];
  5221. },
  5222. /**
  5223. * Returns original text
  5224. * @returns {String}
  5225. */
  5226. text: function() {
  5227. return text;
  5228. }
  5229. };
  5230. }
  5231. function matches(text, pos, pattern) {
  5232. return text.substring(pos, pos + pattern.length) == pattern;
  5233. }
  5234. /**
  5235. * Search for closing pair of opening tag
  5236. * @param {Object} open Open tag instance
  5237. * @param {Object} matcher Matcher instance
  5238. */
  5239. function findClosingPair(open, matcher) {
  5240. var stack = [], tag = null;
  5241. var text = matcher.text();
  5242. for (var pos = open.range.end, len = text.length; pos < len; pos++) {
  5243. if (matches(text, pos, '<!--')) {
  5244. // skip to end of comment
  5245. for (var j = pos; j < len; j++) {
  5246. if (matches(text, j, '-->')) {
  5247. pos = j + 3;
  5248. break;
  5249. }
  5250. }
  5251. }
  5252. if (tag = matcher.matches(pos)) {
  5253. if (tag.type == 'open' && !tag.selfClose) {
  5254. stack.push(tag.name);
  5255. } else if (tag.type == 'close') {
  5256. if (!stack.length) { // found valid pair?
  5257. return tag.name == open.name ? tag : null;
  5258. }
  5259. // check if current closing tag matches previously opened one
  5260. if (_.last(stack) == tag.name) {
  5261. stack.pop();
  5262. } else {
  5263. var found = false;
  5264. while (stack.length && !found) {
  5265. var last = stack.pop();
  5266. if (last == tag.name) {
  5267. found = true;
  5268. }
  5269. }
  5270. if (!stack.length && !found) {
  5271. return tag.name == open.name ? tag : null;
  5272. }
  5273. }
  5274. }
  5275. }
  5276. }
  5277. }
  5278. return {
  5279. /**
  5280. * Main function: search for tag pair in <code>text</code> for given
  5281. * position
  5282. * @memberOf htmlMatcher
  5283. * @param {String} text
  5284. * @param {Number} pos
  5285. * @returns {Object}
  5286. */
  5287. find: function(text, pos) {
  5288. var range = require('range');
  5289. var matcher = createMatcher(text);
  5290. var open = null, close = null;
  5291. for (var i = pos; i >= 0; i--) {
  5292. if (open = matcher.open(i)) {
  5293. // found opening tag
  5294. if (open.selfClose) {
  5295. if (open.range.cmp(pos, 'lt', 'gt')) {
  5296. // inside self-closing tag, found match
  5297. break;
  5298. }
  5299. // outside self-closing tag, continue
  5300. continue;
  5301. }
  5302. close = findClosingPair(open, matcher);
  5303. if (close) {
  5304. // found closing tag.
  5305. var r = range.create2(open.range.start, close.range.end);
  5306. if (r.contains(pos)) {
  5307. break;
  5308. }
  5309. } else if (open.range.contains(pos)) {
  5310. // we inside empty HTML tag like <br>
  5311. break;
  5312. }
  5313. open = null;
  5314. } else if (matches(text, i, '-->')) {
  5315. // skip back to comment start
  5316. for (var j = i - 1; j >= 0; j--) {
  5317. if (matches(text, j, '-->')) {
  5318. // found another comment end, do nothing
  5319. break;
  5320. } else if (matches(text, j, '<!--')) {
  5321. i = j;
  5322. break;
  5323. }
  5324. }
  5325. } else if (matches(text, i, '<!--')) {
  5326. // we're inside comment, match it
  5327. var j = i + 4, jl = text.length;
  5328. for (; j < jl; j++) {
  5329. if (matches(text, j, '-->')) {
  5330. j += 3;
  5331. break;
  5332. }
  5333. }
  5334. open = comment(i, j);
  5335. break;
  5336. }
  5337. }
  5338. if (open) {
  5339. var outerRange = null;
  5340. var innerRange = null;
  5341. if (close) {
  5342. outerRange = range.create2(open.range.start, close.range.end);
  5343. innerRange = range.create2(open.range.end, close.range.start);
  5344. } else {
  5345. outerRange = innerRange = range.create2(open.range.start, open.range.end);
  5346. }
  5347. if (open.type == 'comment') {
  5348. // adjust positions of inner range for comment
  5349. var _c = outerRange.substring(text);
  5350. innerRange.start += _c.length - _c.replace(/^<\!--\s*/, '').length;
  5351. innerRange.end -= _c.length - _c.replace(/\s*-->$/, '').length;
  5352. }
  5353. return {
  5354. open: open,
  5355. close: close,
  5356. type: open.type == 'comment' ? 'comment' : 'tag',
  5357. innerRange: innerRange,
  5358. innerContent: function() {
  5359. return this.innerRange.substring(text);
  5360. },
  5361. outerRange: outerRange,
  5362. outerContent: function() {
  5363. return this.outerRange.substring(text);
  5364. },
  5365. range: !innerRange.length() || !innerRange.cmp(pos, 'lte', 'gte') ? outerRange : innerRange,
  5366. content: function() {
  5367. return this.range.substring(text);
  5368. },
  5369. source: text
  5370. };
  5371. }
  5372. },
  5373. /**
  5374. * The same as <code>find()</code> method, but restricts matched result
  5375. * to <code>tag</code> type
  5376. * @param {String} text
  5377. * @param {Number} pos
  5378. * @returns {Object}
  5379. */
  5380. tag: function(text, pos) {
  5381. var result = this.find(text, pos);
  5382. if (result && result.type == 'tag') {
  5383. return result;
  5384. }
  5385. }
  5386. };
  5387. });/**
  5388. * Utility module for handling tabstops tokens generated by Emmet's
  5389. * "Expand Abbreviation" action. The main <code>extract</code> method will take
  5390. * raw text (for example: <i>${0} some ${1:text}</i>), find all tabstops
  5391. * occurrences, replace them with tokens suitable for your editor of choice and
  5392. * return object with processed text and list of found tabstops and their ranges.
  5393. * For sake of portability (Objective-C/Java) the tabstops list is a plain
  5394. * sorted array with plain objects.
  5395. *
  5396. * Placeholders with the same are meant to be <i>linked</i> in your editor.
  5397. * @param {Function} require
  5398. * @param {Underscore} _
  5399. */
  5400. emmet.define('tabStops', function(require, _) {
  5401. /**
  5402. * Global placeholder value, automatically incremented by
  5403. * <code>variablesResolver()</code> function
  5404. */
  5405. var startPlaceholderNum = 100;
  5406. var tabstopIndex = 0;
  5407. var defaultOptions = {
  5408. replaceCarets: false,
  5409. escape: function(ch) {
  5410. return '\\' + ch;
  5411. },
  5412. tabstop: function(data) {
  5413. return data.token;
  5414. },
  5415. variable: function(data) {
  5416. return data.token;
  5417. }
  5418. };
  5419. // XXX register output processor that will upgrade tabstops of parsed node
  5420. // in order to prevent tabstop index conflicts
  5421. require('abbreviationParser').addOutputProcessor(function(text, node, type) {
  5422. var maxNum = 0;
  5423. var tabstops = require('tabStops');
  5424. var utils = require('utils');
  5425. var tsOptions = {
  5426. tabstop: function(data) {
  5427. var group = parseInt(data.group);
  5428. if (group == 0)
  5429. return '${0}';
  5430. if (group > maxNum) maxNum = group;
  5431. if (data.placeholder) {
  5432. // respect nested placeholders
  5433. var ix = group + tabstopIndex;
  5434. var placeholder = tabstops.processText(data.placeholder, tsOptions);
  5435. return '${' + ix + ':' + placeholder + '}';
  5436. } else {
  5437. return '${' + (group + tabstopIndex) + '}';
  5438. }
  5439. }
  5440. };
  5441. // upgrade tabstops
  5442. text = tabstops.processText(text, tsOptions);
  5443. // resolve variables
  5444. text = utils.replaceVariables(text, tabstops.variablesResolver(node));
  5445. tabstopIndex += maxNum + 1;
  5446. return text;
  5447. });
  5448. return {
  5449. /**
  5450. * Main function that looks for a tabstops in provided <code>text</code>
  5451. * and returns a processed version of <code>text</code> with expanded
  5452. * placeholders and list of tabstops found.
  5453. * @param {String} text Text to process
  5454. * @param {Object} options List of processor options:<br>
  5455. *
  5456. * <b>replaceCarets</b> : <code>Boolean</code> — replace all default
  5457. * caret placeholders (like <i>{%::emmet-caret::%}</i>) with <i>${0:caret}</i><br>
  5458. *
  5459. * <b>escape</b> : <code>Function</code> — function that handle escaped
  5460. * characters (mostly '$'). By default, it returns the character itself
  5461. * to be displayed as is in output, but sometimes you will use
  5462. * <code>extract</code> method as intermediate solution for further
  5463. * processing and want to keep character escaped. Thus, you should override
  5464. * <code>escape</code> method to return escaped symbol (e.g. '\\$')<br>
  5465. *
  5466. * <b>tabstop</b> : <code>Function</code> – a tabstop handler. Receives
  5467. * a single argument – an object describing token: its position, number
  5468. * group, placeholder and token itself. Should return a replacement
  5469. * string that will appear in final output
  5470. *
  5471. * <b>variable</b> : <code>Function</code> – variable handler. Receives
  5472. * a single argument – an object describing token: its position, name
  5473. * and original token itself. Should return a replacement
  5474. * string that will appear in final output
  5475. *
  5476. * @returns {Object} Object with processed <code>text</code> property
  5477. * and array of <code>tabstops</code> found
  5478. * @memberOf tabStops
  5479. */
  5480. extract: function(text, options) {
  5481. // prepare defaults
  5482. var utils = require('utils');
  5483. var placeholders = {carets: ''};
  5484. var marks = [];
  5485. options = _.extend({}, defaultOptions, options, {
  5486. tabstop: function(data) {
  5487. var token = data.token;
  5488. var ret = '';
  5489. if (data.placeholder == 'cursor') {
  5490. marks.push({
  5491. start: data.start,
  5492. end: data.start + token.length,
  5493. group: 'carets',
  5494. value: ''
  5495. });
  5496. } else {
  5497. // unify placeholder value for single group
  5498. if ('placeholder' in data)
  5499. placeholders[data.group] = data.placeholder;
  5500. if (data.group in placeholders)
  5501. ret = placeholders[data.group];
  5502. marks.push({
  5503. start: data.start,
  5504. end: data.start + token.length,
  5505. group: data.group,
  5506. value: ret
  5507. });
  5508. }
  5509. return token;
  5510. }
  5511. });
  5512. if (options.replaceCarets) {
  5513. text = text.replace(new RegExp( utils.escapeForRegexp( utils.getCaretPlaceholder() ), 'g'), '${0:cursor}');
  5514. }
  5515. // locate tabstops and unify group's placeholders
  5516. text = this.processText(text, options);
  5517. // now, replace all tabstops with placeholders
  5518. var buf = utils.stringBuilder(), lastIx = 0;
  5519. var tabStops = _.map(marks, function(mark) {
  5520. buf.append(text.substring(lastIx, mark.start));
  5521. var pos = buf.length;
  5522. var ph = placeholders[mark.group] || '';
  5523. buf.append(ph);
  5524. lastIx = mark.end;
  5525. return {
  5526. group: mark.group,
  5527. start: pos,
  5528. end: pos + ph.length
  5529. };
  5530. });
  5531. buf.append(text.substring(lastIx));
  5532. return {
  5533. text: buf.toString(),
  5534. tabstops: _.sortBy(tabStops, 'start')
  5535. };
  5536. },
  5537. /**
  5538. * Text processing routine. Locates escaped characters and tabstops and
  5539. * replaces them with values returned by handlers defined in
  5540. * <code>options</code>
  5541. * @param {String} text
  5542. * @param {Object} options See <code>extract</code> method options
  5543. * description
  5544. * @returns {String}
  5545. */
  5546. processText: function(text, options) {
  5547. options = _.extend({}, defaultOptions, options);
  5548. var buf = require('utils').stringBuilder();
  5549. /** @type StringStream */
  5550. var stream = require('stringStream').create(text);
  5551. var ch, m, a;
  5552. while (ch = stream.next()) {
  5553. if (ch == '\\' && !stream.eol()) {
  5554. // handle escaped character
  5555. buf.append(options.escape(stream.next()));
  5556. continue;
  5557. }
  5558. a = ch;
  5559. if (ch == '$') {
  5560. // looks like a tabstop
  5561. stream.start = stream.pos - 1;
  5562. if (m = stream.match(/^[0-9]+/)) {
  5563. // it's $N
  5564. a = options.tabstop({
  5565. start: buf.length,
  5566. group: stream.current().substr(1),
  5567. token: stream.current()
  5568. });
  5569. } else if (m = stream.match(/^\{([a-z_\-][\w\-]*)\}/)) {
  5570. // ${variable}
  5571. a = options.variable({
  5572. start: buf.length,
  5573. name: m[1],
  5574. token: stream.current()
  5575. });
  5576. } else if (m = stream.match(/^\{([0-9]+)(:.+?)?\}/, false)) {
  5577. // ${N:value} or ${N} placeholder
  5578. // parse placeholder, including nested ones
  5579. stream.skipToPair('{', '}');
  5580. var obj = {
  5581. start: buf.length,
  5582. group: m[1],
  5583. token: stream.current()
  5584. };
  5585. var placeholder = obj.token.substring(obj.group.length + 2, obj.token.length - 1);
  5586. if (placeholder) {
  5587. obj.placeholder = placeholder.substr(1);
  5588. }
  5589. a = options.tabstop(obj);
  5590. }
  5591. }
  5592. buf.append(a);
  5593. }
  5594. return buf.toString();
  5595. },
  5596. /**
  5597. * Upgrades tabstops in output node in order to prevent naming conflicts
  5598. * @param {AbbreviationNode} node
  5599. * @param {Number} offset Tab index offset
  5600. * @returns {Number} Maximum tabstop index in element
  5601. */
  5602. upgrade: function(node, offset) {
  5603. var maxNum = 0;
  5604. var options = {
  5605. tabstop: function(data) {
  5606. var group = parseInt(data.group);
  5607. if (group > maxNum) maxNum = group;
  5608. if (data.placeholder)
  5609. return '${' + (group + offset) + ':' + data.placeholder + '}';
  5610. else
  5611. return '${' + (group + offset) + '}';
  5612. }
  5613. };
  5614. _.each(['start', 'end', 'content'], function(p) {
  5615. node[p] = this.processText(node[p], options);
  5616. }, this);
  5617. return maxNum;
  5618. },
  5619. /**
  5620. * Helper function that produces a callback function for
  5621. * <code>replaceVariables()</code> method from {@link utils}
  5622. * module. This callback will replace variable definitions (like
  5623. * ${var_name}) with their value defined in <i>resource</i> module,
  5624. * or outputs tabstop with variable name otherwise.
  5625. * @param {AbbreviationNode} node Context node
  5626. * @returns {Function}
  5627. */
  5628. variablesResolver: function(node) {
  5629. var placeholderMemo = {};
  5630. var res = require('resources');
  5631. return function(str, varName) {
  5632. // do not mark `child` variable as placeholder – it‘s a reserved
  5633. // variable name
  5634. if (varName == 'child')
  5635. return str;
  5636. if (varName == 'cursor')
  5637. return require('utils').getCaretPlaceholder();
  5638. var attr = node.attribute(varName);
  5639. if (!_.isUndefined(attr) && attr !== str) {
  5640. return attr;
  5641. }
  5642. var varValue = res.getVariable(varName);
  5643. if (varValue)
  5644. return varValue;
  5645. // output as placeholder
  5646. if (!placeholderMemo[varName])
  5647. placeholderMemo[varName] = startPlaceholderNum++;
  5648. return '${' + placeholderMemo[varName] + ':' + varName + '}';
  5649. };
  5650. },
  5651. /**
  5652. * Resets global tabstop index. When parsed tree is converted to output
  5653. * string (<code>AbbreviationNode.toString()</code>), all tabstops
  5654. * defined in snippets and elements are upgraded in order to prevent
  5655. * naming conflicts of nested. For example, <code>${1}</code> of a node
  5656. * should not be linked with the same placehilder of the child node.
  5657. * By default, <code>AbbreviationNode.toString()</code> automatically
  5658. * upgrades tabstops of the same index for each node and writes maximum
  5659. * tabstop index into the <code>tabstopIndex</code> variable. To keep
  5660. * this variable at reasonable value, it is recommended to call
  5661. * <code>resetTabstopIndex()</code> method each time you expand variable
  5662. * @returns
  5663. */
  5664. resetTabstopIndex: function() {
  5665. tabstopIndex = 0;
  5666. startPlaceholderNum = 100;
  5667. }
  5668. };
  5669. });/**
  5670. * Common module's preferences storage. This module
  5671. * provides general storage for all module preferences, their description and
  5672. * default values.<br><br>
  5673. *
  5674. * This module can also be used to list all available properties to create
  5675. * UI for updating properties
  5676. *
  5677. * @memberOf __preferencesDefine
  5678. * @constructor
  5679. * @param {Function} require
  5680. * @param {Underscore} _
  5681. */
  5682. emmet.define('preferences', function(require, _) {
  5683. var preferences = {};
  5684. var defaults = {};
  5685. var _dbgDefaults = null;
  5686. var _dbgPreferences = null;
  5687. function toBoolean(val) {
  5688. if (_.isString(val)) {
  5689. val = val.toLowerCase();
  5690. return val == 'yes' || val == 'true' || val == '1';
  5691. }
  5692. return !!val;
  5693. }
  5694. function isValueObj(obj) {
  5695. return _.isObject(obj)
  5696. && 'value' in obj
  5697. && _.keys(obj).length < 3;
  5698. }
  5699. return {
  5700. /**
  5701. * Creates new preference item with default value
  5702. * @param {String} name Preference name. You can also pass object
  5703. * with many options
  5704. * @param {Object} value Preference default value
  5705. * @param {String} description Item textual description
  5706. * @memberOf preferences
  5707. */
  5708. define: function(name, value, description) {
  5709. var prefs = name;
  5710. if (_.isString(name)) {
  5711. prefs = {};
  5712. prefs[name] = {
  5713. value: value,
  5714. description: description
  5715. };
  5716. }
  5717. _.each(prefs, function(v, k) {
  5718. defaults[k] = isValueObj(v) ? v : {value: v};
  5719. });
  5720. },
  5721. /**
  5722. * Updates preference item value. Preference value should be defined
  5723. * first with <code>define</code> method.
  5724. * @param {String} name Preference name. You can also pass object
  5725. * with many options
  5726. * @param {Object} value Preference default value
  5727. * @memberOf preferences
  5728. */
  5729. set: function(name, value) {
  5730. var prefs = name;
  5731. if (_.isString(name)) {
  5732. prefs = {};
  5733. prefs[name] = value;
  5734. }
  5735. _.each(prefs, function(v, k) {
  5736. if (!(k in defaults)) {
  5737. throw 'Property "' + k + '" is not defined. You should define it first with `define` method of current module';
  5738. }
  5739. // do not set value if it equals to default value
  5740. if (v !== defaults[k].value) {
  5741. // make sure we have value of correct type
  5742. switch (typeof defaults[k].value) {
  5743. case 'boolean':
  5744. v = toBoolean(v);
  5745. break;
  5746. case 'number':
  5747. v = parseInt(v + '', 10) || 0;
  5748. break;
  5749. default: // convert to string
  5750. v += '';
  5751. }
  5752. preferences[k] = v;
  5753. } else if (k in preferences) {
  5754. delete preferences[k];
  5755. }
  5756. });
  5757. },
  5758. /**
  5759. * Returns preference value
  5760. * @param {String} name
  5761. * @returns {String} Returns <code>undefined</code> if preference is
  5762. * not defined
  5763. */
  5764. get: function(name) {
  5765. if (name in preferences)
  5766. return preferences[name];
  5767. if (name in defaults)
  5768. return defaults[name].value;
  5769. return void 0;
  5770. },
  5771. /**
  5772. * Returns comma-separated preference value as array of values
  5773. * @param {String} name
  5774. * @returns {Array} Returns <code>undefined</code> if preference is
  5775. * not defined, <code>null</code> if string cannot be converted to array
  5776. */
  5777. getArray: function(name) {
  5778. var val = this.get(name);
  5779. if (!_.isUndefined(val)) {
  5780. val = _.map(val.split(','), require('utils').trim);
  5781. if (!val.length)
  5782. val = null;
  5783. }
  5784. return val;
  5785. },
  5786. /**
  5787. * Returns comma and colon-separated preference value as dictionary
  5788. * @param {String} name
  5789. * @returns {Object}
  5790. */
  5791. getDict: function(name) {
  5792. var result = {};
  5793. _.each(this.getArray(name), function(val) {
  5794. var parts = val.split(':');
  5795. result[parts[0]] = parts[1];
  5796. });
  5797. return result;
  5798. },
  5799. /**
  5800. * Returns description of preference item
  5801. * @param {String} name Preference name
  5802. * @returns {Object}
  5803. */
  5804. description: function(name) {
  5805. return name in defaults ? defaults[name].description : void 0;
  5806. },
  5807. /**
  5808. * Completely removes specified preference(s)
  5809. * @param {String} name Preference name (or array of names)
  5810. */
  5811. remove: function(name) {
  5812. if (!_.isArray(name))
  5813. name = [name];
  5814. _.each(name, function(key) {
  5815. if (key in preferences)
  5816. delete preferences[key];
  5817. if (key in defaults)
  5818. delete defaults[key];
  5819. });
  5820. },
  5821. /**
  5822. * Returns sorted list of all available properties
  5823. * @returns {Array}
  5824. */
  5825. list: function() {
  5826. return _.map(_.keys(defaults).sort(), function(key) {
  5827. return {
  5828. name: key,
  5829. value: this.get(key),
  5830. type: typeof defaults[key].value,
  5831. description: defaults[key].description
  5832. };
  5833. }, this);
  5834. },
  5835. /**
  5836. * Loads user-defined preferences from JSON
  5837. * @param {Object} json
  5838. * @returns
  5839. */
  5840. load: function(json) {
  5841. _.each(json, function(value, key) {
  5842. this.set(key, value);
  5843. }, this);
  5844. },
  5845. /**
  5846. * Returns hash of user-modified preferences
  5847. * @returns {Object}
  5848. */
  5849. exportModified: function() {
  5850. return _.clone(preferences);
  5851. },
  5852. /**
  5853. * Reset to defaults
  5854. * @returns
  5855. */
  5856. reset: function() {
  5857. preferences = {};
  5858. },
  5859. /**
  5860. * For unit testing: use empty storage
  5861. */
  5862. _startTest: function() {
  5863. _dbgDefaults = defaults;
  5864. _dbgPreferences = preferences;
  5865. defaults = {};
  5866. preferences = {};
  5867. },
  5868. /**
  5869. * For unit testing: restore original storage
  5870. */
  5871. _stopTest: function() {
  5872. defaults = _dbgDefaults;
  5873. preferences = _dbgPreferences;
  5874. }
  5875. };
  5876. });/**
  5877. * Module for handling filters
  5878. * @param {Function} require
  5879. * @param {Underscore} _
  5880. * @author Sergey Chikuyonok (serge.che@gmail.com) <http://chikuyonok.ru>
  5881. */
  5882. emmet.define('filters', function(require, _) {
  5883. /** List of registered filters */
  5884. var registeredFilters = {};
  5885. /** Filters that will be applied for unknown syntax */
  5886. var basicFilters = 'html';
  5887. function list(filters) {
  5888. if (!filters)
  5889. return [];
  5890. if (_.isString(filters))
  5891. return filters.split(/[\|,]/g);
  5892. return filters;
  5893. }
  5894. return {
  5895. /**
  5896. * Register new filter
  5897. * @param {String} name Filter name
  5898. * @param {Function} fn Filter function
  5899. */
  5900. add: function(name, fn) {
  5901. registeredFilters[name] = fn;
  5902. },
  5903. /**
  5904. * Apply filters for final output tree
  5905. * @param {AbbreviationNode} tree Output tree
  5906. * @param {Array} filters List of filters to apply. Might be a
  5907. * <code>String</code>
  5908. * @param {Object} profile Output profile, defined in <i>profile</i>
  5909. * module. Filters defined it profile are not used, <code>profile</code>
  5910. * is passed to filter function
  5911. * @memberOf emmet.filters
  5912. * @returns {AbbreviationNode}
  5913. */
  5914. apply: function(tree, filters, profile) {
  5915. var utils = require('utils');
  5916. profile = require('profile').get(profile);
  5917. _.each(list(filters), function(filter) {
  5918. var name = utils.trim(filter.toLowerCase());
  5919. if (name && name in registeredFilters) {
  5920. tree = registeredFilters[name](tree, profile);
  5921. }
  5922. });
  5923. return tree;
  5924. },
  5925. /**
  5926. * Composes list of filters that should be applied to a tree, based on
  5927. * passed data
  5928. * @param {String} syntax Syntax name ('html', 'css', etc.)
  5929. * @param {Object} profile Output profile
  5930. * @param {String} additionalFilters List or pipe-separated
  5931. * string of additional filters to apply
  5932. * @returns {Array}
  5933. */
  5934. composeList: function(syntax, profile, additionalFilters) {
  5935. profile = require('profile').get(profile);
  5936. var filters = list(profile.filters || require('resources').findItem(syntax, 'filters') || basicFilters);
  5937. if (profile.extraFilters) {
  5938. filters = filters.concat(list(profile.extraFilters));
  5939. }
  5940. if (additionalFilters) {
  5941. filters = filters.concat(list(additionalFilters));
  5942. }
  5943. if (!filters || !filters.length) {
  5944. // looks like unknown syntax, apply basic filters
  5945. filters = list(basicFilters);
  5946. }
  5947. return filters;
  5948. },
  5949. /**
  5950. * Extracts filter list from abbreviation
  5951. * @param {String} abbr
  5952. * @returns {Array} Array with cleaned abbreviation and list of
  5953. * extracted filters
  5954. */
  5955. extractFromAbbreviation: function(abbr) {
  5956. var filters = '';
  5957. abbr = abbr.replace(/\|([\w\|\-]+)$/, function(str, p1){
  5958. filters = p1;
  5959. return '';
  5960. });
  5961. return [abbr, list(filters)];
  5962. }
  5963. };
  5964. });/**
  5965. * Module that contains factories for element types used by Emmet
  5966. * @param {Function} require
  5967. * @param {Underscore} _
  5968. */
  5969. emmet.define('elements', function(require, _) {
  5970. var factories = {};
  5971. var reAttrs = /([\w\-]+)\s*=\s*(['"])(.*?)\2/g;
  5972. var result = {
  5973. /**
  5974. * Create new element factory
  5975. * @param {String} name Element identifier
  5976. * @param {Function} factory Function that produces element of specified
  5977. * type. The object generated by this factory is automatically
  5978. * augmented with <code>type</code> property pointing to element
  5979. * <code>name</code>
  5980. * @memberOf elements
  5981. */
  5982. add: function(name, factory) {
  5983. var that = this;
  5984. factories[name] = function() {
  5985. var elem = factory.apply(that, arguments);
  5986. if (elem)
  5987. elem.type = name;
  5988. return elem;
  5989. };
  5990. },
  5991. /**
  5992. * Returns factory for specified name
  5993. * @param {String} name
  5994. * @returns {Function}
  5995. */
  5996. get: function(name) {
  5997. return factories[name];
  5998. },
  5999. /**
  6000. * Creates new element with specified type
  6001. * @param {String} name
  6002. * @returns {Object}
  6003. */
  6004. create: function(name) {
  6005. var args = [].slice.call(arguments, 1);
  6006. var factory = this.get(name);
  6007. return factory ? factory.apply(this, args) : null;
  6008. },
  6009. /**
  6010. * Check if passed element is of specified type
  6011. * @param {Object} elem
  6012. * @param {String} type
  6013. * @returns {Boolean}
  6014. */
  6015. is: function(elem, type) {
  6016. return elem && elem.type === type;
  6017. }
  6018. };
  6019. // register resource references
  6020. function commonFactory(value) {
  6021. return {data: value};
  6022. }
  6023. /**
  6024. * Element factory
  6025. * @param {String} elementName Name of output element
  6026. * @param {String} attrs Attributes definition. You may also pass
  6027. * <code>Array</code> where each contains object with <code>name</code>
  6028. * and <code>value</code> properties, or <code>Object</code>
  6029. * @param {Boolean} isEmpty Is expanded element should be empty
  6030. */
  6031. result.add('element', function(elementName, attrs, isEmpty) {
  6032. var ret = {
  6033. /** @memberOf __emmetDataElement */
  6034. name: elementName,
  6035. is_empty: !!isEmpty
  6036. };
  6037. if (attrs) {
  6038. ret.attributes = [];
  6039. if (_.isArray(attrs)) {
  6040. ret.attributes = attrs;
  6041. } else if (_.isString(attrs)) {
  6042. var m;
  6043. while (m = reAttrs.exec(attrs)) {
  6044. ret.attributes.push({
  6045. name: m[1],
  6046. value: m[3]
  6047. });
  6048. }
  6049. } else {
  6050. _.each(attrs, function(value, name) {
  6051. ret.attributes.push({
  6052. name: name,
  6053. value: value
  6054. });
  6055. });
  6056. }
  6057. }
  6058. return ret;
  6059. });
  6060. result.add('snippet', commonFactory);
  6061. result.add('reference', commonFactory);
  6062. result.add('empty', function() {
  6063. return {};
  6064. });
  6065. return result;
  6066. });/**
  6067. * Abstract implementation of edit tree interface.
  6068. * Edit tree is a named container of editable “name-value” child elements,
  6069. * parsed from <code>source</code>. This container provides convenient methods
  6070. * for editing/adding/removing child elements. All these update actions are
  6071. * instantly reflected in the <code>source</code> code with respect of formatting.
  6072. * <br><br>
  6073. * For example, developer can create an edit tree from CSS rule and add or
  6074. * remove properties from it–all changes will be immediately reflected in the
  6075. * original source.
  6076. * <br><br>
  6077. * All classes defined in this module should be extended the same way as in
  6078. * Backbone framework: using <code>extend</code> method to create new class and
  6079. * <code>initialize</code> method to define custom class constructor.
  6080. *
  6081. * @example
  6082. * <pre><code>
  6083. * var MyClass = require('editTree').EditElement.extend({
  6084. * initialize: function() {
  6085. * // constructor code here
  6086. * }
  6087. * });
  6088. *
  6089. * var elem = new MyClass();
  6090. * </code></pre>
  6091. *
  6092. *
  6093. * @param {Function} require
  6094. * @param {Underscore} _
  6095. * @constructor
  6096. * @memberOf __editTreeDefine
  6097. */
  6098. emmet.define('editTree', function(require, _, core) {
  6099. var range = require('range').create;
  6100. /**
  6101. * Named container of edited source
  6102. * @type EditContainer
  6103. * @param {String} source
  6104. * @param {Object} options
  6105. */
  6106. function EditContainer(source, options) {
  6107. this.options = _.extend({offset: 0}, options);
  6108. /**
  6109. * Source code of edited structure. All changes in the structure are
  6110. * immediately reflected into this property
  6111. */
  6112. this.source = source;
  6113. /**
  6114. * List of all editable children
  6115. * @private
  6116. */
  6117. this._children = [];
  6118. /**
  6119. * Hash of all positions of container
  6120. * @private
  6121. */
  6122. this._positions = {
  6123. name: 0
  6124. };
  6125. this.initialize.apply(this, arguments);
  6126. }
  6127. /**
  6128. * The self-propagating extend function for classes.
  6129. * @type Function
  6130. */
  6131. EditContainer.extend = core.extend;
  6132. EditContainer.prototype = {
  6133. /**
  6134. * Child class constructor
  6135. */
  6136. initialize: function() {},
  6137. /**
  6138. * Replace substring of tag's source
  6139. * @param {String} value
  6140. * @param {Number} start
  6141. * @param {Number} end
  6142. * @private
  6143. */
  6144. _updateSource: function(value, start, end) {
  6145. // create modification range
  6146. var r = range(start, _.isUndefined(end) ? 0 : end - start);
  6147. var delta = value.length - r.length();
  6148. var update = function(obj) {
  6149. _.each(obj, function(v, k) {
  6150. if (v >= r.end)
  6151. obj[k] += delta;
  6152. });
  6153. };
  6154. // update affected positions of current container
  6155. update(this._positions);
  6156. // update affected positions of children
  6157. _.each(this.list(), function(item) {
  6158. update(item._positions);
  6159. });
  6160. this.source = require('utils').replaceSubstring(this.source, value, r);
  6161. },
  6162. /**
  6163. * Adds new attribute
  6164. * @param {String} name Property name
  6165. * @param {String} value Property value
  6166. * @param {Number} pos Position at which to insert new property. By
  6167. * default the property is inserted at the end of rule
  6168. * @returns {EditElement} Newly created element
  6169. */
  6170. add: function(name, value, pos) {
  6171. // this is abstract implementation
  6172. var item = new EditElement(name, value);
  6173. this._children.push(item);
  6174. return item;
  6175. },
  6176. /**
  6177. * Returns attribute object
  6178. * @param {String} name Attribute name or its index
  6179. * @returns {EditElement}
  6180. */
  6181. get: function(name) {
  6182. if (_.isNumber(name))
  6183. return this.list()[name];
  6184. if (_.isString(name))
  6185. return _.find(this.list(), function(prop) {
  6186. return prop.name() === name;
  6187. });
  6188. return name;
  6189. },
  6190. /**
  6191. * Returns all children by name or indexes
  6192. * @param {Object} name Element name(s) or indexes (<code>String</code>,
  6193. * <code>Array</code>, <code>Number</code>)
  6194. * @returns {Array}
  6195. */
  6196. getAll: function(name) {
  6197. if (!_.isArray(name))
  6198. name = [name];
  6199. // split names and indexes
  6200. var names = [], indexes = [];
  6201. _.each(name, function(item) {
  6202. if (_.isString(item))
  6203. names.push(item);
  6204. else if (_.isNumber(item))
  6205. indexes.push(item);
  6206. });
  6207. return _.filter(this.list(), function(attribute, i) {
  6208. return _.include(indexes, i) || _.include(names, attribute.name());
  6209. });
  6210. },
  6211. /**
  6212. * Returns or updates element value. If such element doesn't exists,
  6213. * it will be created automatically and added at the end of child list.
  6214. * @param {String} name Element name or its index
  6215. * @param {String} value New element value
  6216. * @returns {String}
  6217. */
  6218. value: function(name, value, pos) {
  6219. var element = this.get(name);
  6220. if (element)
  6221. return element.value(value);
  6222. if (!_.isUndefined(value)) {
  6223. // no such element — create it
  6224. return this.add(name, value, pos);
  6225. }
  6226. },
  6227. /**
  6228. * Returns all values of child elements found by <code>getAll()</code>
  6229. * method
  6230. * @param {Object} name Element name(s) or indexes (<code>String</code>,
  6231. * <code>Array</code>, <code>Number</code>)
  6232. * @returns {Array}
  6233. */
  6234. values: function(name) {
  6235. return _.map(this.getAll(name), function(element) {
  6236. return element.value();
  6237. });
  6238. },
  6239. /**
  6240. * Remove child element
  6241. * @param {String} name Property name or its index
  6242. */
  6243. remove: function(name) {
  6244. var element = this.get(name);
  6245. if (element) {
  6246. this._updateSource('', element.fullRange());
  6247. this._children = _.without(this._children, element);
  6248. }
  6249. },
  6250. /**
  6251. * Returns list of all editable child elements
  6252. * @returns {Array}
  6253. */
  6254. list: function() {
  6255. return this._children;
  6256. },
  6257. /**
  6258. * Returns index of editble child in list
  6259. * @param {Object} item
  6260. * @returns {Number}
  6261. */
  6262. indexOf: function(item) {
  6263. return _.indexOf(this.list(), this.get(item));
  6264. },
  6265. /**
  6266. * Sets or gets container name
  6267. * @param {String} val New name. If not passed, current
  6268. * name is returned
  6269. * @return {String}
  6270. */
  6271. name: function(val) {
  6272. if (!_.isUndefined(val) && this._name !== (val = String(val))) {
  6273. this._updateSource(val, this._positions.name, this._positions.name + this._name.length);
  6274. this._name = val;
  6275. }
  6276. return this._name;
  6277. },
  6278. /**
  6279. * Returns name range object
  6280. * @param {Boolean} isAbsolute Return absolute range (with respect of
  6281. * rule offset)
  6282. * @returns {Range}
  6283. */
  6284. nameRange: function(isAbsolute) {
  6285. return range(this._positions.name + (isAbsolute ? this.options.offset : 0), this.name());
  6286. },
  6287. /**
  6288. * Returns range of current source
  6289. * @param {Boolean} isAbsolute
  6290. */
  6291. range: function(isAbsolute) {
  6292. return range(isAbsolute ? this.options.offset : 0, this.toString());
  6293. },
  6294. /**
  6295. * Returns element that belongs to specified position
  6296. * @param {Number} pos
  6297. * @param {Boolean} isAbsolute
  6298. * @returns {EditElement}
  6299. */
  6300. itemFromPosition: function(pos, isAbsolute) {
  6301. return _.find(this.list(), function(elem) {
  6302. return elem.range(isAbsolute).inside(pos);
  6303. });
  6304. },
  6305. /**
  6306. * Returns source code of current container
  6307. * @returns {String}
  6308. */
  6309. toString: function() {
  6310. return this.source;
  6311. }
  6312. };
  6313. /**
  6314. * @param {EditContainer} parent
  6315. * @param {Object} nameToken
  6316. * @param {Object} valueToken
  6317. */
  6318. function EditElement(parent, nameToken, valueToken) {
  6319. /** @type EditContainer */
  6320. this.parent = parent;
  6321. this._name = nameToken.value;
  6322. this._value = valueToken ? valueToken.value : '';
  6323. this._positions = {
  6324. name: nameToken.start,
  6325. value: valueToken ? valueToken.start : -1
  6326. };
  6327. this.initialize.apply(this, arguments);
  6328. }
  6329. /**
  6330. * The self-propagating extend function for classes.
  6331. * @type Function
  6332. */
  6333. EditElement.extend = core.extend;
  6334. EditElement.prototype = {
  6335. /**
  6336. * Child class constructor
  6337. */
  6338. initialize: function() {},
  6339. /**
  6340. * Make position absolute
  6341. * @private
  6342. * @param {Number} num
  6343. * @param {Boolean} isAbsolute
  6344. * @returns {Boolean}
  6345. */
  6346. _pos: function(num, isAbsolute) {
  6347. return num + (isAbsolute ? this.parent.options.offset : 0);
  6348. },
  6349. /**
  6350. * Sets of gets element value
  6351. * @param {String} val New element value. If not passed, current
  6352. * value is returned
  6353. * @returns {String}
  6354. */
  6355. value: function(val) {
  6356. if (!_.isUndefined(val) && this._value !== (val = String(val))) {
  6357. this.parent._updateSource(val, this.valueRange());
  6358. this._value = val;
  6359. }
  6360. return this._value;
  6361. },
  6362. /**
  6363. * Sets of gets element name
  6364. * @param {String} val New element name. If not passed, current
  6365. * name is returned
  6366. * @returns {String}
  6367. */
  6368. name: function(val) {
  6369. if (!_.isUndefined(val) && this._name !== (val = String(val))) {
  6370. this.parent._updateSource(val, this.nameRange());
  6371. this._name = val;
  6372. }
  6373. return this._name;
  6374. },
  6375. /**
  6376. * Returns position of element name token
  6377. * @param {Boolean} isAbsolute Return absolute position
  6378. * @returns {Number}
  6379. */
  6380. namePosition: function(isAbsolute) {
  6381. return this._pos(this._positions.name, isAbsolute);
  6382. },
  6383. /**
  6384. * Returns position of element value token
  6385. * @param {Boolean} isAbsolute Return absolute position
  6386. * @returns {Number}
  6387. */
  6388. valuePosition: function(isAbsolute) {
  6389. return this._pos(this._positions.value, isAbsolute);
  6390. },
  6391. /**
  6392. * Returns element name
  6393. * @param {Boolean} isAbsolute Return absolute range
  6394. * @returns {Range}
  6395. */
  6396. range: function(isAbsolute) {
  6397. return range(this.namePosition(isAbsolute), this.toString());
  6398. },
  6399. /**
  6400. * Returns full element range, including possible indentation
  6401. * @param {Boolean} isAbsolute Return absolute range
  6402. * @returns {Range}
  6403. */
  6404. fullRange: function(isAbsolute) {
  6405. return this.range(isAbsolute);
  6406. },
  6407. /**
  6408. * Returns element name range
  6409. * @param {Boolean} isAbsolute Return absolute range
  6410. * @returns {Range}
  6411. */
  6412. nameRange: function(isAbsolute) {
  6413. return range(this.namePosition(isAbsolute), this.name());
  6414. },
  6415. /**
  6416. * Returns element value range
  6417. * @param {Boolean} isAbsolute Return absolute range
  6418. * @returns {Range}
  6419. */
  6420. valueRange: function(isAbsolute) {
  6421. return range(this.valuePosition(isAbsolute), this.value());
  6422. },
  6423. /**
  6424. * Returns current element string representation
  6425. * @returns {String}
  6426. */
  6427. toString: function() {
  6428. return this.name() + this.value();
  6429. },
  6430. valueOf: function() {
  6431. return this.toString();
  6432. }
  6433. };
  6434. return {
  6435. EditContainer: EditContainer,
  6436. EditElement: EditElement,
  6437. /**
  6438. * Creates token that can be fed to <code>EditElement</code>
  6439. * @param {Number} start
  6440. * @param {String} value
  6441. * @param {String} type
  6442. * @returns
  6443. */
  6444. createToken: function(start, value, type) {
  6445. var obj = {
  6446. start: start || 0,
  6447. value: value || '',
  6448. type: type
  6449. };
  6450. obj.end = obj.start + obj.value.length;
  6451. return obj;
  6452. }
  6453. };
  6454. });/**
  6455. * CSS EditTree is a module that can parse a CSS rule into a tree with
  6456. * convenient methods for adding, modifying and removing CSS properties. These
  6457. * changes can be written back to string with respect of code formatting.
  6458. *
  6459. * @memberOf __cssEditTreeDefine
  6460. * @constructor
  6461. * @param {Function} require
  6462. * @param {Underscore} _
  6463. */
  6464. emmet.define('cssEditTree', function(require, _) {
  6465. var defaultOptions = {
  6466. styleBefore: '\n\t',
  6467. styleSeparator: ': ',
  6468. offset: 0
  6469. };
  6470. var WHITESPACE_REMOVE_FROM_START = 1;
  6471. var WHITESPACE_REMOVE_FROM_END = 2;
  6472. /**
  6473. * Returns range object
  6474. * @param {Number} start
  6475. * @param {Number} len
  6476. * @returns {Range}
  6477. */
  6478. function range(start, len) {
  6479. return require('range').create(start, len);
  6480. }
  6481. /**
  6482. * Removes whitespace tokens from the array ends
  6483. * @param {Array} tokens
  6484. * @param {Number} mask Mask indicating from which end whitespace should be
  6485. * removed
  6486. * @returns {Array}
  6487. */
  6488. function trimWhitespaceTokens(tokens, mask) {
  6489. mask = mask || (WHITESPACE_REMOVE_FROM_START | WHITESPACE_REMOVE_FROM_END);
  6490. var whitespace = ['white', 'line'];
  6491. if ((mask & WHITESPACE_REMOVE_FROM_END) == WHITESPACE_REMOVE_FROM_END)
  6492. while (tokens.length && _.include(whitespace, _.last(tokens).type)) {
  6493. tokens.pop();
  6494. }
  6495. if ((mask & WHITESPACE_REMOVE_FROM_START) == WHITESPACE_REMOVE_FROM_START)
  6496. while (tokens.length && _.include(whitespace, tokens[0].type)) {
  6497. tokens.shift();
  6498. }
  6499. return tokens;
  6500. }
  6501. /**
  6502. * Helper function that searches for selector range for <code>CSSEditRule</code>
  6503. * @param {TokenIterator} it
  6504. * @returns {Range}
  6505. */
  6506. function findSelectorRange(it) {
  6507. var tokens = [], token;
  6508. var start = it.position(), end;
  6509. while (token = it.next()) {
  6510. if (token.type == '{')
  6511. break;
  6512. tokens.push(token);
  6513. }
  6514. trimWhitespaceTokens(tokens);
  6515. if (tokens.length) {
  6516. start = tokens[0].start;
  6517. end = _.last(tokens).end;
  6518. } else {
  6519. end = start;
  6520. }
  6521. return range(start, end - start);
  6522. }
  6523. /**
  6524. * Helper function that searches for CSS property value range next to
  6525. * iterator's current position
  6526. * @param {TokenIterator} it
  6527. * @returns {Range}
  6528. */
  6529. function findValueRange(it) {
  6530. // find value start position
  6531. var skipTokens = ['white', 'line', ':'];
  6532. var tokens = [], token, start, end;
  6533. it.nextUntil(function(tok) {
  6534. return !_.include(skipTokens, this.itemNext().type);
  6535. });
  6536. start = it.current().end;
  6537. // consume value
  6538. while (token = it.next()) {
  6539. if (token.type == '}' || token.type == ';') {
  6540. // found value end
  6541. trimWhitespaceTokens(tokens, WHITESPACE_REMOVE_FROM_START
  6542. | (token.type == '}' ? WHITESPACE_REMOVE_FROM_END : 0));
  6543. if (tokens.length) {
  6544. start = tokens[0].start;
  6545. end = _.last(tokens).end;
  6546. } else {
  6547. end = start;
  6548. }
  6549. return range(start, end - start);
  6550. }
  6551. tokens.push(token);
  6552. }
  6553. // reached the end of tokens list
  6554. if (tokens.length) {
  6555. return range(tokens[0].start, _.last(tokens).end - tokens[0].start);
  6556. }
  6557. }
  6558. /**
  6559. * Finds parts of complex CSS value
  6560. * @param {String} str
  6561. * @returns {Array} Returns list of <code>Range</code>'s
  6562. */
  6563. function findParts(str) {
  6564. /** @type StringStream */
  6565. var stream = require('stringStream').create(str);
  6566. var ch;
  6567. var result = [];
  6568. var sep = /[\s\u00a0,]/;
  6569. var add = function() {
  6570. stream.next();
  6571. result.push(range(stream.start, stream.current()));
  6572. stream.start = stream.pos;
  6573. };
  6574. // skip whitespace
  6575. stream.eatSpace();
  6576. stream.start = stream.pos;
  6577. while (ch = stream.next()) {
  6578. if (ch == '"' || ch == "'") {
  6579. stream.next();
  6580. if (!stream.skipTo(ch)) break;
  6581. add();
  6582. } else if (ch == '(') {
  6583. // function found, may have nested function
  6584. stream.backUp(1);
  6585. if (!stream.skipToPair('(', ')')) break;
  6586. stream.backUp(1);
  6587. add();
  6588. } else {
  6589. if (sep.test(ch)) {
  6590. result.push(range(stream.start, stream.current().length - 1));
  6591. stream.eatWhile(sep);
  6592. stream.start = stream.pos;
  6593. }
  6594. }
  6595. }
  6596. add();
  6597. return _.chain(result)
  6598. .filter(function(item) {
  6599. return !!item.length();
  6600. })
  6601. .uniq(false, function(item) {
  6602. return item.toString();
  6603. })
  6604. .value();
  6605. }
  6606. /**
  6607. * A bit hacky way to identify invalid CSS property definition: when user
  6608. * starts writing new abbreviation in CSS rule, he actually creates invalid
  6609. * CSS property definition and this method tries to identify such abbreviation
  6610. * and prevent it from being added to CSS edit tree
  6611. * @param {TokenIterator} it
  6612. */
  6613. function isValidIdentifier(it) {
  6614. // return true;
  6615. var tokens = it.tokens;
  6616. for (var i = it._i + 1, il = tokens.length; i < il; i++) {
  6617. if (tokens[i].type == ':')
  6618. return true;
  6619. if (tokens[i].type == 'identifier' || tokens[i].type == 'line')
  6620. return false;
  6621. }
  6622. return false;
  6623. }
  6624. /**
  6625. * @class
  6626. * @extends EditContainer
  6627. */
  6628. var CSSEditContainer = require('editTree').EditContainer.extend({
  6629. initialize: function(source, options) {
  6630. _.defaults(this.options, defaultOptions);
  6631. var editTree = require('editTree');
  6632. /** @type TokenIterator */
  6633. var it = require('tokenIterator').create(
  6634. require('cssParser').parse(source));
  6635. var selectorRange = findSelectorRange(it);
  6636. this._positions.name = selectorRange.start;
  6637. this._name = selectorRange.substring(source);
  6638. if (!it.current() || it.current().type != '{')
  6639. throw 'Invalid CSS rule';
  6640. this._positions.contentStart = it.position() + 1;
  6641. // consume properties
  6642. var propertyRange, valueRange, token;
  6643. while (token = it.next()) {
  6644. if (token.type == 'identifier' && isValidIdentifier(it)) {
  6645. propertyRange = range(token);
  6646. valueRange = findValueRange(it);
  6647. var end = (it.current() && it.current().type == ';')
  6648. ? range(it.current())
  6649. : range(valueRange.end, 0);
  6650. this._children.push(new CSSEditElement(this,
  6651. editTree.createToken(propertyRange.start, propertyRange.substring(source)),
  6652. editTree.createToken(valueRange.start, valueRange.substring(source)),
  6653. editTree.createToken(end.start, end.substring(source))
  6654. ));
  6655. }
  6656. }
  6657. this._saveStyle();
  6658. },
  6659. /**
  6660. * Remembers all styles of properties
  6661. * @private
  6662. */
  6663. _saveStyle: function() {
  6664. var start = this._positions.contentStart;
  6665. var source = this.source;
  6666. var utils = require('utils');
  6667. _.each(this.list(), /** @param {CSSEditProperty} p */ function(p) {
  6668. p.styleBefore = source.substring(start, p.namePosition());
  6669. // a small hack here:
  6670. // Sometimes users add empty lines before properties to logically
  6671. // separate groups of properties. In this case, a blind copy of
  6672. // characters between rules may lead to undesired behavior,
  6673. // especially when current rule is duplicated or used as a donor
  6674. // to create new rule.
  6675. // To solve this issue, we‘ll take only last newline indentation
  6676. var lines = utils.splitByLines(p.styleBefore);
  6677. if (lines.length > 1) {
  6678. p.styleBefore = '\n' + _.last(lines);
  6679. }
  6680. p.styleSeparator = source.substring(p.nameRange().end, p.valuePosition());
  6681. // graceful and naive comments removal
  6682. p.styleBefore = _.last(p.styleBefore.split('*/'));
  6683. p.styleSeparator = p.styleSeparator.replace(/\/\*.*?\*\//g, '');
  6684. start = p.range().end;
  6685. });
  6686. },
  6687. /**
  6688. * Adds new CSS property
  6689. * @param {String} name Property name
  6690. * @param {String} value Property value
  6691. * @param {Number} pos Position at which to insert new property. By
  6692. * default the property is inserted at the end of rule
  6693. * @returns {CSSEditProperty}
  6694. */
  6695. add: function(name, value, pos) {
  6696. var list = this.list();
  6697. var start = this._positions.contentStart;
  6698. var styles = _.pick(this.options, 'styleBefore', 'styleSeparator');
  6699. var editTree = require('editTree');
  6700. if (_.isUndefined(pos))
  6701. pos = list.length;
  6702. /** @type CSSEditProperty */
  6703. var donor = list[pos];
  6704. if (donor) {
  6705. start = donor.fullRange().start;
  6706. } else if (donor = list[pos - 1]) {
  6707. // make sure that donor has terminating semicolon
  6708. donor.end(';');
  6709. start = donor.range().end;
  6710. }
  6711. if (donor) {
  6712. styles = _.pick(donor, 'styleBefore', 'styleSeparator');
  6713. }
  6714. var nameToken = editTree.createToken(start + styles.styleBefore.length, name);
  6715. var valueToken = editTree.createToken(nameToken.end + styles.styleSeparator.length, value);
  6716. var property = new CSSEditElement(this, nameToken, valueToken,
  6717. editTree.createToken(valueToken.end, ';'));
  6718. _.extend(property, styles);
  6719. // write new property into the source
  6720. this._updateSource(property.styleBefore + property.toString(), start);
  6721. // insert new property
  6722. this._children.splice(pos, 0, property);
  6723. return property;
  6724. }
  6725. });
  6726. /**
  6727. * @class
  6728. * @type CSSEditElement
  6729. * @constructor
  6730. */
  6731. var CSSEditElement = require('editTree').EditElement.extend({
  6732. initialize: function(rule, name, value, end) {
  6733. this.styleBefore = rule.options.styleBefore;
  6734. this.styleSeparator = rule.options.styleSeparator;
  6735. this._end = end.value;
  6736. this._positions.end = end.start;
  6737. },
  6738. /**
  6739. * Returns ranges of complex value parts
  6740. * @returns {Array} Returns <code>null</code> if value is not complex
  6741. */
  6742. valueParts: function(isAbsolute) {
  6743. var parts = findParts(this.value());
  6744. if (isAbsolute) {
  6745. var offset = this.valuePosition(true);
  6746. _.each(parts, function(p) {
  6747. p.shift(offset);
  6748. });
  6749. }
  6750. return parts;
  6751. },
  6752. /**
  6753. * Sets of gets property end value (basically, it's a semicolon)
  6754. * @param {String} val New end value. If not passed, current
  6755. * value is returned
  6756. */
  6757. end: function(val) {
  6758. if (!_.isUndefined(val) && this._end !== val) {
  6759. this.parent._updateSource(val, this._positions.end, this._positions.end + this._end.length);
  6760. this._end = val;
  6761. }
  6762. return this._end;
  6763. },
  6764. /**
  6765. * Returns full rule range, with indentation
  6766. * @param {Boolean} isAbsolute Return absolute range (with respect of
  6767. * rule offset)
  6768. * @returns {Range}
  6769. */
  6770. fullRange: function(isAbsolute) {
  6771. var r = this.range(isAbsolute);
  6772. r.start -= this.styleBefore.length;
  6773. return r;
  6774. },
  6775. /**
  6776. * Returns item string representation
  6777. * @returns {String}
  6778. */
  6779. toString: function() {
  6780. return this.name() + this.styleSeparator + this.value() + this.end();
  6781. }
  6782. });
  6783. return {
  6784. /**
  6785. * Parses CSS rule into editable tree
  6786. * @param {String} source
  6787. * @param {Object} options
  6788. * @memberOf emmet.cssEditTree
  6789. * @returns {EditContainer}
  6790. */
  6791. parse: function(source, options) {
  6792. return new CSSEditContainer(source, options);
  6793. },
  6794. /**
  6795. * Extract and parse CSS rule from specified position in <code>content</code>
  6796. * @param {String} content CSS source code
  6797. * @param {Number} pos Character position where to start source code extraction
  6798. * @returns {EditContainer}
  6799. */
  6800. parseFromPosition: function(content, pos, isBackward) {
  6801. var bounds = this.extractRule(content, pos, isBackward);
  6802. if (!bounds || !bounds.inside(pos))
  6803. // no matching CSS rule or caret outside rule bounds
  6804. return null;
  6805. return this.parse(bounds.substring(content), {
  6806. offset: bounds.start
  6807. });
  6808. },
  6809. /**
  6810. * Extracts single CSS selector definition from source code
  6811. * @param {String} content CSS source code
  6812. * @param {Number} pos Character position where to start source code extraction
  6813. * @returns {Range}
  6814. */
  6815. extractRule: function(content, pos, isBackward) {
  6816. var result = '';
  6817. var len = content.length;
  6818. var offset = pos;
  6819. var stopChars = '{}/\\<>\n\r';
  6820. var bracePos = -1, ch;
  6821. // search left until we find rule edge
  6822. while (offset >= 0) {
  6823. ch = content.charAt(offset);
  6824. if (ch == '{') {
  6825. bracePos = offset;
  6826. break;
  6827. }
  6828. else if (ch == '}' && !isBackward) {
  6829. offset++;
  6830. break;
  6831. }
  6832. offset--;
  6833. }
  6834. // search right for full rule set
  6835. while (offset < len) {
  6836. ch = content.charAt(offset);
  6837. if (ch == '{') {
  6838. bracePos = offset;
  6839. } else if (ch == '}') {
  6840. if (bracePos != -1)
  6841. result = content.substring(bracePos, offset + 1);
  6842. break;
  6843. }
  6844. offset++;
  6845. }
  6846. if (result) {
  6847. // find CSS selector
  6848. offset = bracePos - 1;
  6849. var selector = '';
  6850. while (offset >= 0) {
  6851. ch = content.charAt(offset);
  6852. if (stopChars.indexOf(ch) != -1) break;
  6853. offset--;
  6854. }
  6855. // also trim whitespace
  6856. selector = content.substring(offset + 1, bracePos).replace(/^[\s\n\r]+/m, '');
  6857. return require('range').create(bracePos - selector.length, result.length + selector.length);
  6858. }
  6859. return null;
  6860. },
  6861. /**
  6862. * Removes vendor prefix from CSS property
  6863. * @param {String} name CSS property
  6864. * @return {String}
  6865. */
  6866. baseName: function(name) {
  6867. return name.replace(/^\s*\-\w+\-/, '');
  6868. },
  6869. /**
  6870. * Finds parts of complex CSS value
  6871. * @param {String} str
  6872. * @returns {Array}
  6873. */
  6874. findParts: findParts
  6875. };
  6876. });/**
  6877. * XML EditTree is a module that can parse an XML/HTML element into a tree with
  6878. * convenient methods for adding, modifying and removing attributes. These
  6879. * changes can be written back to string with respect of code formatting.
  6880. *
  6881. * @memberOf __xmlEditTreeDefine
  6882. * @constructor
  6883. * @param {Function} require
  6884. * @param {Underscore} _
  6885. */
  6886. emmet.define('xmlEditTree', function(require, _) {
  6887. var defaultOptions = {
  6888. styleBefore: ' ',
  6889. styleSeparator: '=',
  6890. styleQuote: '"',
  6891. offset: 0
  6892. };
  6893. var startTag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/m;
  6894. var XMLEditContainer = require('editTree').EditContainer.extend({
  6895. initialize: function(source, options) {
  6896. _.defaults(this.options, defaultOptions);
  6897. this._positions.name = 1;
  6898. var attrToken = null;
  6899. var tokens = require('xmlParser').parse(source);
  6900. var range = require('range');
  6901. _.each(tokens, function(token) {
  6902. token.value = range.create(token).substring(source);
  6903. switch (token.type) {
  6904. case 'tag':
  6905. if (/^<[^\/]+/.test(token.value)) {
  6906. this._name = token.value.substring(1);
  6907. }
  6908. break;
  6909. case 'attribute':
  6910. // add empty attribute
  6911. if (attrToken) {
  6912. this._children.push(new XMLEditElement(this, attrToken));
  6913. }
  6914. attrToken = token;
  6915. break;
  6916. case 'string':
  6917. this._children.push(new XMLEditElement(this, attrToken, token));
  6918. attrToken = null;
  6919. break;
  6920. }
  6921. }, this);
  6922. if (attrToken) {
  6923. this._children.push(new XMLEditElement(this, attrToken));
  6924. }
  6925. this._saveStyle();
  6926. },
  6927. /**
  6928. * Remembers all styles of properties
  6929. * @private
  6930. */
  6931. _saveStyle: function() {
  6932. var start = this.nameRange().end;
  6933. var source = this.source;
  6934. _.each(this.list(), /** @param {EditElement} p */ function(p) {
  6935. p.styleBefore = source.substring(start, p.namePosition());
  6936. if (p.valuePosition() !== -1) {
  6937. p.styleSeparator = source.substring(p.namePosition() + p.name().length, p.valuePosition() - p.styleQuote.length);
  6938. }
  6939. start = p.range().end;
  6940. });
  6941. },
  6942. /**
  6943. * Adds new attribute
  6944. * @param {String} name Property name
  6945. * @param {String} value Property value
  6946. * @param {Number} pos Position at which to insert new property. By
  6947. * default the property is inserted at the end of rule
  6948. */
  6949. add: function(name, value, pos) {
  6950. var list = this.list();
  6951. var start = this.nameRange().end;
  6952. var editTree = require('editTree');
  6953. var styles = _.pick(this.options, 'styleBefore', 'styleSeparator', 'styleQuote');
  6954. if (_.isUndefined(pos))
  6955. pos = list.length;
  6956. /** @type XMLEditAttribute */
  6957. var donor = list[pos];
  6958. if (donor) {
  6959. start = donor.fullRange().start;
  6960. } else if (donor = list[pos - 1]) {
  6961. start = donor.range().end;
  6962. }
  6963. if (donor) {
  6964. styles = _.pick(donor, 'styleBefore', 'styleSeparator', 'styleQuote');
  6965. }
  6966. value = styles.styleQuote + value + styles.styleQuote;
  6967. var attribute = new XMLEditElement(this,
  6968. editTree.createToken(start + styles.styleBefore.length, name),
  6969. editTree.createToken(start + styles.styleBefore.length + name.length
  6970. + styles.styleSeparator.length, value)
  6971. );
  6972. _.extend(attribute, styles);
  6973. // write new attribute into the source
  6974. this._updateSource(attribute.styleBefore + attribute.toString(), start);
  6975. // insert new attribute
  6976. this._children.splice(pos, 0, attribute);
  6977. return attribute;
  6978. }
  6979. });
  6980. var XMLEditElement = require('editTree').EditElement.extend({
  6981. initialize: function(parent, nameToken, valueToken) {
  6982. this.styleBefore = parent.options.styleBefore;
  6983. this.styleSeparator = parent.options.styleSeparator;
  6984. var value = '', quote = parent.options.styleQuote;
  6985. if (valueToken) {
  6986. value = valueToken.value;
  6987. quote = value.charAt(0);
  6988. if (quote == '"' || quote == "'") {
  6989. value = value.substring(1);
  6990. } else {
  6991. quote = '';
  6992. }
  6993. if (quote && value.charAt(value.length - 1) == quote) {
  6994. value = value.substring(0, value.length - 1);
  6995. }
  6996. }
  6997. this.styleQuote = quote;
  6998. this._value = value;
  6999. this._positions.value = valueToken ? valueToken.start + quote.length : -1;
  7000. },
  7001. /**
  7002. * Returns full rule range, with indentation
  7003. * @param {Boolean} isAbsolute Return absolute range (with respect of
  7004. * rule offset)
  7005. * @returns {Range}
  7006. */
  7007. fullRange: function(isAbsolute) {
  7008. var r = this.range(isAbsolute);
  7009. r.start -= this.styleBefore.length;
  7010. return r;
  7011. },
  7012. toString: function() {
  7013. return this.name() + this.styleSeparator
  7014. + this.styleQuote + this.value() + this.styleQuote;
  7015. }
  7016. });
  7017. return {
  7018. /**
  7019. * Parses HTML element into editable tree
  7020. * @param {String} source
  7021. * @param {Object} options
  7022. * @memberOf emmet.htmlEditTree
  7023. * @returns {EditContainer}
  7024. */
  7025. parse: function(source, options) {
  7026. return new XMLEditContainer(source, options);
  7027. },
  7028. /**
  7029. * Extract and parse HTML from specified position in <code>content</code>
  7030. * @param {String} content CSS source code
  7031. * @param {Number} pos Character position where to start source code extraction
  7032. * @returns {XMLEditElement}
  7033. */
  7034. parseFromPosition: function(content, pos, isBackward) {
  7035. var bounds = this.extractTag(content, pos, isBackward);
  7036. if (!bounds || !bounds.inside(pos))
  7037. // no matching HTML tag or caret outside tag bounds
  7038. return null;
  7039. return this.parse(bounds.substring(content), {
  7040. offset: bounds.start
  7041. });
  7042. },
  7043. /**
  7044. * Extracts nearest HTML tag range from <code>content</code>, starting at
  7045. * <code>pos</code> position
  7046. * @param {String} content
  7047. * @param {Number} pos
  7048. * @param {Boolean} isBackward
  7049. * @returns {Range}
  7050. */
  7051. extractTag: function(content, pos, isBackward) {
  7052. var len = content.length, i;
  7053. var range = require('range');
  7054. // max extraction length. I don't think there may be tags larger
  7055. // than 2000 characters length
  7056. var maxLen = Math.min(2000, len);
  7057. /** @type Range */
  7058. var r = null;
  7059. var match = function(pos) {
  7060. var m;
  7061. if (content.charAt(pos) == '<' && (m = content.substr(pos, maxLen).match(startTag)))
  7062. return range.create(pos, m[0]);
  7063. };
  7064. // lookup backward, in case we are inside tag already
  7065. for (i = pos; i >= 0; i--) {
  7066. if (r = match(i)) break;
  7067. }
  7068. if (r && (r.inside(pos) || isBackward))
  7069. return r;
  7070. if (!r && isBackward)
  7071. return null;
  7072. // search forward
  7073. for (i = pos; i < len; i++) {
  7074. if (r = match(i))
  7075. return r;
  7076. }
  7077. }
  7078. };
  7079. });/**
  7080. * 'Expand abbreviation' editor action: extracts abbreviation from current caret
  7081. * position and replaces it with formatted output.
  7082. * <br><br>
  7083. * This behavior can be overridden with custom handlers which can perform
  7084. * different actions when 'Expand Abbreviation' action is called.
  7085. * For example, a CSS gradient handler that produces vendor-prefixed gradient
  7086. * definitions registers its own expand abbreviation handler.
  7087. *
  7088. * @constructor
  7089. * @memberOf __expandAbbreviationActionDefine
  7090. * @param {Function} require
  7091. * @param {Underscore} _
  7092. */
  7093. emmet.define('expandAbbreviation', function(require, _) {
  7094. /**
  7095. * @type HandlerList List of registered handlers
  7096. */
  7097. var handlers = require('handlerList').create();
  7098. /** Back-reference to module */
  7099. var module = null;
  7100. var actions = require('actions');
  7101. /**
  7102. * 'Expand abbreviation' editor action
  7103. * @param {IEmmetEditor} editor Editor instance
  7104. * @param {String} syntax Syntax type (html, css, etc.)
  7105. * @param {String} profile Output profile name (html, xml, xhtml)
  7106. * @return {Boolean} Returns <code>true</code> if abbreviation was expanded
  7107. * successfully
  7108. */
  7109. actions.add('expand_abbreviation', function(editor, syntax, profile) {
  7110. var args = _.toArray(arguments);
  7111. // normalize incoming arguments
  7112. var info = require('editorUtils').outputInfo(editor, syntax, profile);
  7113. args[1] = info.syntax;
  7114. args[2] = info.profile;
  7115. return handlers.exec(false, args);
  7116. });
  7117. /**
  7118. * A special version of <code>expandAbbreviation</code> function: if it can't
  7119. * find abbreviation, it will place Tab character at caret position
  7120. * @param {IEmmetEditor} editor Editor instance
  7121. * @param {String} syntax Syntax type (html, css, etc.)
  7122. * @param {String} profile Output profile name (html, xml, xhtml)
  7123. */
  7124. actions.add('expand_abbreviation_with_tab', function(editor, syntax, profile) {
  7125. var sel = editor.getSelection();
  7126. var indent = require('resources').getVariable('indentation');
  7127. if (sel) {
  7128. // indent selection
  7129. var utils = require('utils');
  7130. var selRange = require('range').create(editor.getSelectionRange());
  7131. var content = utils.padString(sel, indent);
  7132. editor.replaceContent(indent + '${0}', editor.getCaretPos());
  7133. var replaceRange = require('range').create(editor.getCaretPos(), selRange.length());
  7134. editor.replaceContent(content, replaceRange.start, replaceRange.end, true);
  7135. editor.createSelection(replaceRange.start, replaceRange.start + content.length);
  7136. return true;
  7137. }
  7138. if (!actions.run('expand_abbreviation', editor, syntax, profile)) {
  7139. editor.replaceContent(indent, editor.getCaretPos());
  7140. }
  7141. return true;
  7142. }, {hidden: true});
  7143. // XXX setup default handler
  7144. /**
  7145. * Extracts abbreviation from current caret
  7146. * position and replaces it with formatted output
  7147. * @param {IEmmetEditor} editor Editor instance
  7148. * @param {String} syntax Syntax type (html, css, etc.)
  7149. * @param {String} profile Output profile name (html, xml, xhtml)
  7150. * @return {Boolean} Returns <code>true</code> if abbreviation was expanded
  7151. * successfully
  7152. */
  7153. handlers.add(function(editor, syntax, profile) {
  7154. var caretPos = editor.getSelectionRange().end;
  7155. var abbr = module.findAbbreviation(editor);
  7156. if (abbr) {
  7157. var content = emmet.expandAbbreviation(abbr, syntax, profile,
  7158. require('actionUtils').captureContext(editor));
  7159. if (content) {
  7160. editor.replaceContent(content, caretPos - abbr.length, caretPos);
  7161. return true;
  7162. }
  7163. }
  7164. return false;
  7165. }, {order: -1});
  7166. return module = {
  7167. /**
  7168. * Adds custom expand abbreviation handler. The passed function should
  7169. * return <code>true</code> if it was performed successfully,
  7170. * <code>false</code> otherwise.
  7171. *
  7172. * Added handlers will be called when 'Expand Abbreviation' is called
  7173. * in order they were added
  7174. * @memberOf expandAbbreviation
  7175. * @param {Function} fn
  7176. * @param {Object} options
  7177. */
  7178. addHandler: function(fn, options) {
  7179. handlers.add(fn, options);
  7180. },
  7181. /**
  7182. * Removes registered handler
  7183. * @returns
  7184. */
  7185. removeHandler: function(fn) {
  7186. handlers.remove(fn, options);
  7187. },
  7188. /**
  7189. * Search for abbreviation in editor from current caret position
  7190. * @param {IEmmetEditor} editor Editor instance
  7191. * @return {String}
  7192. */
  7193. findAbbreviation: function(editor) {
  7194. /** @type Range */
  7195. var range = require('range').create(editor.getSelectionRange());
  7196. var content = String(editor.getContent());
  7197. if (range.length()) {
  7198. // abbreviation is selected by user
  7199. return range.substring(content);
  7200. }
  7201. // search for new abbreviation from current caret position
  7202. var curLine = editor.getCurrentLineRange();
  7203. return require('actionUtils').extractAbbreviation(content.substring(curLine.start, range.start));
  7204. }
  7205. };
  7206. });/**
  7207. * Action that wraps content with abbreviation. For convenience, action is
  7208. * defined as reusable module
  7209. * @constructor
  7210. * @memberOf __wrapWithAbbreviationDefine
  7211. */
  7212. emmet.define('wrapWithAbbreviation', function(require, _) {
  7213. /** Back-references to current module */
  7214. var module = null;
  7215. /**
  7216. * Wraps content with abbreviation
  7217. * @param {IEmmetEditor} Editor instance
  7218. * @param {String} abbr Abbreviation to wrap with
  7219. * @param {String} syntax Syntax type (html, css, etc.)
  7220. * @param {String} profile Output profile name (html, xml, xhtml)
  7221. */
  7222. require('actions').add('wrap_with_abbreviation', function (editor, abbr, syntax, profile) {
  7223. var info = require('editorUtils').outputInfo(editor, syntax, profile);
  7224. var utils = require('utils');
  7225. /** @type emmet.editorUtils */
  7226. var editorUtils = require('editorUtils');
  7227. abbr = abbr || editor.prompt("Enter abbreviation");
  7228. if (!abbr)
  7229. return null;
  7230. abbr = String(abbr);
  7231. var range = require('range').create(editor.getSelectionRange());
  7232. if (!range.length()) {
  7233. // no selection, find tag pair
  7234. var match = require('htmlMatcher').tag(info.content, range.start);
  7235. if (!match) { // nothing to wrap
  7236. return false;
  7237. }
  7238. range = utils.narrowToNonSpace(info.content, match.range);
  7239. }
  7240. var newContent = utils.escapeText(range.substring(info.content));
  7241. var result = module
  7242. .wrap(abbr, editorUtils.unindent(editor, newContent), info.syntax,
  7243. info.profile, require('actionUtils').captureContext(editor));
  7244. if (result) {
  7245. editor.replaceContent(result, range.start, range.end);
  7246. return true;
  7247. }
  7248. return false;
  7249. });
  7250. return module = {
  7251. /**
  7252. * Wraps passed text with abbreviation. Text will be placed inside last
  7253. * expanded element
  7254. * @memberOf wrapWithAbbreviation
  7255. * @param {String} abbr Abbreviation
  7256. * @param {String} text Text to wrap
  7257. * @param {String} syntax Document type (html, xml, etc.). Default is 'html'
  7258. * @param {String} profile Output profile's name. Default is 'plain'
  7259. * @param {Object} contextNode Context node inside which abbreviation
  7260. * is wrapped. It will be used as a reference for node name resolvers
  7261. * @return {String}
  7262. */
  7263. wrap: function(abbr, text, syntax, profile, contextNode) {
  7264. /** @type emmet.filters */
  7265. var filters = require('filters');
  7266. /** @type emmet.utils */
  7267. var utils = require('utils');
  7268. syntax = syntax || emmet.defaultSyntax();
  7269. profile = require('profile').get(profile, syntax);
  7270. require('tabStops').resetTabstopIndex();
  7271. var data = filters.extractFromAbbreviation(abbr);
  7272. var parsedTree = require('abbreviationParser').parse(data[0], {
  7273. syntax: syntax,
  7274. pastedContent: text,
  7275. contextNode: contextNode
  7276. });
  7277. if (parsedTree) {
  7278. var filtersList = filters.composeList(syntax, profile, data[1]);
  7279. filters.apply(parsedTree, filtersList, profile);
  7280. return utils.replaceVariables(parsedTree.toString());
  7281. }
  7282. return null;
  7283. }
  7284. };
  7285. });/**
  7286. * Toggles HTML and CSS comments depending on current caret context. Unlike
  7287. * the same action in most editors, this action toggles comment on currently
  7288. * matched item—HTML tag or CSS selector—when nothing is selected.
  7289. *
  7290. * @param {Function} require
  7291. * @param {Underscore} _
  7292. * @memberOf __toggleCommentAction
  7293. * @constructor
  7294. */
  7295. emmet.exec(function(require, _) {
  7296. /**
  7297. * Toggle HTML comment on current selection or tag
  7298. * @param {IEmmetEditor} editor
  7299. * @return {Boolean} Returns <code>true</code> if comment was toggled
  7300. */
  7301. function toggleHTMLComment(editor) {
  7302. /** @type Range */
  7303. var range = require('range').create(editor.getSelectionRange());
  7304. var info = require('editorUtils').outputInfo(editor);
  7305. if (!range.length()) {
  7306. // no selection, find matching tag
  7307. var tag = require('htmlMatcher').tag(info.content, editor.getCaretPos());
  7308. if (tag) { // found pair
  7309. range = tag.outerRange;
  7310. }
  7311. }
  7312. return genericCommentToggle(editor, '<!--', '-->', range);
  7313. }
  7314. /**
  7315. * Simple CSS commenting
  7316. * @param {IEmmetEditor} editor
  7317. * @return {Boolean} Returns <code>true</code> if comment was toggled
  7318. */
  7319. function toggleCSSComment(editor) {
  7320. /** @type Range */
  7321. var range = require('range').create(editor.getSelectionRange());
  7322. var info = require('editorUtils').outputInfo(editor);
  7323. if (!range.length()) {
  7324. // no selection, try to get current rule
  7325. /** @type CSSRule */
  7326. var rule = require('cssEditTree').parseFromPosition(info.content, editor.getCaretPos());
  7327. if (rule) {
  7328. var property = cssItemFromPosition(rule, editor.getCaretPos());
  7329. range = property
  7330. ? property.range(true)
  7331. : require('range').create(rule.nameRange(true).start, rule.source);
  7332. }
  7333. }
  7334. if (!range.length()) {
  7335. // still no selection, get current line
  7336. range = require('range').create(editor.getCurrentLineRange());
  7337. require('utils').narrowToNonSpace(info.content, range);
  7338. }
  7339. return genericCommentToggle(editor, '/*', '*/', range);
  7340. }
  7341. /**
  7342. * Returns CSS property from <code>rule</code> that matches passed position
  7343. * @param {EditContainer} rule
  7344. * @param {Number} absPos
  7345. * @returns {EditElement}
  7346. */
  7347. function cssItemFromPosition(rule, absPos) {
  7348. // do not use default EditContainer.itemFromPosition() here, because
  7349. // we need to make a few assumptions to make CSS commenting more reliable
  7350. var relPos = absPos - (rule.options.offset || 0);
  7351. var reSafeChar = /^[\s\n\r]/;
  7352. return _.find(rule.list(), function(item) {
  7353. if (item.range().end === relPos) {
  7354. // at the end of property, but outside of it
  7355. // if there’s a space character at current position,
  7356. // use current property
  7357. return reSafeChar.test(rule.source.charAt(relPos));
  7358. }
  7359. return item.range().inside(relPos);
  7360. });
  7361. }
  7362. /**
  7363. * Search for nearest comment in <code>str</code>, starting from index <code>from</code>
  7364. * @param {String} text Where to search
  7365. * @param {Number} from Search start index
  7366. * @param {String} start_token Comment start string
  7367. * @param {String} end_token Comment end string
  7368. * @return {Range} Returns null if comment wasn't found
  7369. */
  7370. function searchComment(text, from, startToken, endToken) {
  7371. var commentStart = -1;
  7372. var commentEnd = -1;
  7373. var hasMatch = function(str, start) {
  7374. return text.substr(start, str.length) == str;
  7375. };
  7376. // search for comment start
  7377. while (from--) {
  7378. if (hasMatch(startToken, from)) {
  7379. commentStart = from;
  7380. break;
  7381. }
  7382. }
  7383. if (commentStart != -1) {
  7384. // search for comment end
  7385. from = commentStart;
  7386. var contentLen = text.length;
  7387. while (contentLen >= from++) {
  7388. if (hasMatch(endToken, from)) {
  7389. commentEnd = from + endToken.length;
  7390. break;
  7391. }
  7392. }
  7393. }
  7394. return (commentStart != -1 && commentEnd != -1)
  7395. ? require('range').create(commentStart, commentEnd - commentStart)
  7396. : null;
  7397. }
  7398. /**
  7399. * Generic comment toggling routine
  7400. * @param {IEmmetEditor} editor
  7401. * @param {String} commentStart Comment start token
  7402. * @param {String} commentEnd Comment end token
  7403. * @param {Range} range Selection range
  7404. * @return {Boolean}
  7405. */
  7406. function genericCommentToggle(editor, commentStart, commentEnd, range) {
  7407. var editorUtils = require('editorUtils');
  7408. var content = editorUtils.outputInfo(editor).content;
  7409. var caretPos = editor.getCaretPos();
  7410. var newContent = null;
  7411. var utils = require('utils');
  7412. /**
  7413. * Remove comment markers from string
  7414. * @param {Sting} str
  7415. * @return {String}
  7416. */
  7417. function removeComment(str) {
  7418. return str
  7419. .replace(new RegExp('^' + utils.escapeForRegexp(commentStart) + '\\s*'), function(str){
  7420. caretPos -= str.length;
  7421. return '';
  7422. }).replace(new RegExp('\\s*' + utils.escapeForRegexp(commentEnd) + '$'), '');
  7423. }
  7424. // first, we need to make sure that this substring is not inside
  7425. // comment
  7426. var commentRange = searchComment(content, caretPos, commentStart, commentEnd);
  7427. if (commentRange && commentRange.overlap(range)) {
  7428. // we're inside comment, remove it
  7429. range = commentRange;
  7430. newContent = removeComment(range.substring(content));
  7431. } else {
  7432. // should add comment
  7433. // make sure that there's no comment inside selection
  7434. newContent = commentStart + ' ' +
  7435. range.substring(content)
  7436. .replace(new RegExp(utils.escapeForRegexp(commentStart) + '\\s*|\\s*' + utils.escapeForRegexp(commentEnd), 'g'), '') +
  7437. ' ' + commentEnd;
  7438. // adjust caret position
  7439. caretPos += commentStart.length + 1;
  7440. }
  7441. // replace editor content
  7442. if (newContent !== null) {
  7443. newContent = utils.escapeText(newContent);
  7444. editor.setCaretPos(range.start);
  7445. editor.replaceContent(editorUtils.unindent(editor, newContent), range.start, range.end);
  7446. editor.setCaretPos(caretPos);
  7447. return true;
  7448. }
  7449. return false;
  7450. }
  7451. /**
  7452. * Toggle comment on current editor's selection or HTML tag/CSS rule
  7453. * @param {IEmmetEditor} editor
  7454. */
  7455. require('actions').add('toggle_comment', function(editor) {
  7456. var info = require('editorUtils').outputInfo(editor);
  7457. if (info.syntax == 'css') {
  7458. // in case our editor is good enough and can recognize syntax from
  7459. // current token, we have to make sure that cursor is not inside
  7460. // 'style' attribute of html element
  7461. var caretPos = editor.getCaretPos();
  7462. var tag = require('htmlMatcher').tag(info.content, caretPos);
  7463. if (tag && tag.open.range.inside(caretPos)) {
  7464. info.syntax = 'html';
  7465. }
  7466. }
  7467. if (info.syntax == 'css')
  7468. return toggleCSSComment(editor);
  7469. return toggleHTMLComment(editor);
  7470. });
  7471. });/**
  7472. * Move between next/prev edit points. 'Edit points' are places between tags
  7473. * and quotes of empty attributes in html
  7474. * @constructor
  7475. *
  7476. * @memberOf __editPointActionDefine
  7477. * @param {Function} require
  7478. * @param {Underscore} _
  7479. */
  7480. emmet.exec(function(require, _) {
  7481. /**
  7482. * Search for new caret insertion point
  7483. * @param {IEmmetEditor} editor Editor instance
  7484. * @param {Number} inc Search increment: -1 — search left, 1 — search right
  7485. * @param {Number} offset Initial offset relative to current caret position
  7486. * @return {Number} Returns -1 if insertion point wasn't found
  7487. */
  7488. function findNewEditPoint(editor, inc, offset) {
  7489. inc = inc || 1;
  7490. offset = offset || 0;
  7491. var curPoint = editor.getCaretPos() + offset;
  7492. var content = String(editor.getContent());
  7493. var maxLen = content.length;
  7494. var nextPoint = -1;
  7495. var reEmptyLine = /^\s+$/;
  7496. function getLine(ix) {
  7497. var start = ix;
  7498. while (start >= 0) {
  7499. var c = content.charAt(start);
  7500. if (c == '\n' || c == '\r')
  7501. break;
  7502. start--;
  7503. }
  7504. return content.substring(start, ix);
  7505. }
  7506. while (curPoint <= maxLen && curPoint >= 0) {
  7507. curPoint += inc;
  7508. var curChar = content.charAt(curPoint);
  7509. var nextChar = content.charAt(curPoint + 1);
  7510. var prevChar = content.charAt(curPoint - 1);
  7511. switch (curChar) {
  7512. case '"':
  7513. case '\'':
  7514. if (nextChar == curChar && prevChar == '=') {
  7515. // empty attribute
  7516. nextPoint = curPoint + 1;
  7517. }
  7518. break;
  7519. case '>':
  7520. if (nextChar == '<') {
  7521. // between tags
  7522. nextPoint = curPoint + 1;
  7523. }
  7524. break;
  7525. case '\n':
  7526. case '\r':
  7527. // empty line
  7528. if (reEmptyLine.test(getLine(curPoint - 1))) {
  7529. nextPoint = curPoint;
  7530. }
  7531. break;
  7532. }
  7533. if (nextPoint != -1)
  7534. break;
  7535. }
  7536. return nextPoint;
  7537. }
  7538. /** @type emmet.actions */
  7539. var actions = require('actions');
  7540. /**
  7541. * Move caret to previous edit point
  7542. * @param {IEmmetEditor} editor Editor instance
  7543. */
  7544. actions.add('prev_edit_point', function(editor) {
  7545. var curPos = editor.getCaretPos();
  7546. var newPoint = findNewEditPoint(editor, -1);
  7547. if (newPoint == curPos)
  7548. // we're still in the same point, try searching from the other place
  7549. newPoint = findNewEditPoint(editor, -1, -2);
  7550. if (newPoint != -1) {
  7551. editor.setCaretPos(newPoint);
  7552. return true;
  7553. }
  7554. return false;
  7555. }, {label: 'Previous Edit Point'});
  7556. /**
  7557. * Move caret to next edit point
  7558. * @param {IEmmetEditor} editor Editor instance
  7559. */
  7560. actions.add('next_edit_point', function(editor) {
  7561. var newPoint = findNewEditPoint(editor, 1);
  7562. if (newPoint != -1) {
  7563. editor.setCaretPos(newPoint);
  7564. return true;
  7565. }
  7566. return false;
  7567. });
  7568. });/**
  7569. * Actions that use stream parsers and tokenizers for traversing:
  7570. * -- Search for next/previous items in HTML
  7571. * -- Search for next/previous items in CSS
  7572. * @constructor
  7573. * @memberOf __selectItemActionDefine
  7574. * @param {Function} require
  7575. * @param {Underscore} _
  7576. */
  7577. emmet.exec(function(require, _) {
  7578. var startTag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
  7579. /**
  7580. * Generic function for searching for items to select
  7581. * @param {IEmmetEditor} editor
  7582. * @param {Boolean} isBackward Search backward (search forward otherwise)
  7583. * @param {Function} extractFn Function that extracts item content
  7584. * @param {Function} rangeFn Function that search for next token range
  7585. */
  7586. function findItem(editor, isBackward, extractFn, rangeFn) {
  7587. var range = require('range');
  7588. var content = require('editorUtils').outputInfo(editor).content;
  7589. var contentLength = content.length;
  7590. var itemRange, rng;
  7591. /** @type Range */
  7592. var prevRange = range.create(-1, 0);
  7593. /** @type Range */
  7594. var sel = range.create(editor.getSelectionRange());
  7595. var searchPos = sel.start, loop = 100000; // endless loop protection
  7596. while (searchPos >= 0 && searchPos < contentLength && --loop > 0) {
  7597. if ( (itemRange = extractFn(content, searchPos, isBackward)) ) {
  7598. if (prevRange.equal(itemRange)) {
  7599. break;
  7600. }
  7601. prevRange = itemRange.clone();
  7602. rng = rangeFn(itemRange.substring(content), itemRange.start, sel.clone());
  7603. if (rng) {
  7604. editor.createSelection(rng.start, rng.end);
  7605. return true;
  7606. } else {
  7607. searchPos = isBackward ? itemRange.start : itemRange.end - 1;
  7608. }
  7609. }
  7610. searchPos += isBackward ? -1 : 1;
  7611. }
  7612. return false;
  7613. }
  7614. // XXX HTML section
  7615. /**
  7616. * Find next HTML item
  7617. * @param {IEmmetEditor} editor
  7618. */
  7619. function findNextHTMLItem(editor) {
  7620. var isFirst = true;
  7621. return findItem(editor, false, function(content, searchPos){
  7622. if (isFirst) {
  7623. isFirst = false;
  7624. return findOpeningTagFromPosition(content, searchPos);
  7625. } else {
  7626. return getOpeningTagFromPosition(content, searchPos);
  7627. }
  7628. }, function(tag, offset, selRange) {
  7629. return getRangeForHTMLItem(tag, offset, selRange, false);
  7630. });
  7631. }
  7632. /**
  7633. * Find previous HTML item
  7634. * @param {IEmmetEditor} editor
  7635. */
  7636. function findPrevHTMLItem(editor) {
  7637. return findItem(editor, true, getOpeningTagFromPosition, function (tag, offset, selRange) {
  7638. return getRangeForHTMLItem(tag, offset, selRange, true);
  7639. });
  7640. }
  7641. /**
  7642. * Creates possible selection ranges for HTML tag
  7643. * @param {String} source Original HTML source for tokens
  7644. * @param {Array} tokens List of HTML tokens
  7645. * @returns {Array}
  7646. */
  7647. function makePossibleRangesHTML(source, tokens, offset) {
  7648. offset = offset || 0;
  7649. var range = require('range');
  7650. var result = [];
  7651. var attrStart = -1, attrName = '', attrValue = '', attrValueRange, tagName;
  7652. _.each(tokens, function(tok) {
  7653. switch (tok.type) {
  7654. case 'tag':
  7655. tagName = source.substring(tok.start, tok.end);
  7656. if (/^<[\w\:\-]/.test(tagName)) {
  7657. // add tag name
  7658. result.push(range.create({
  7659. start: tok.start + 1,
  7660. end: tok.end
  7661. }));
  7662. }
  7663. break;
  7664. case 'attribute':
  7665. attrStart = tok.start;
  7666. attrName = source.substring(tok.start, tok.end);
  7667. break;
  7668. case 'string':
  7669. // attribute value
  7670. // push full attribute first
  7671. result.push(range.create(attrStart, tok.end - attrStart));
  7672. attrValueRange = range.create(tok);
  7673. attrValue = attrValueRange.substring(source);
  7674. // is this a quoted attribute?
  7675. if (isQuote(attrValue.charAt(0)))
  7676. attrValueRange.start++;
  7677. if (isQuote(attrValue.charAt(attrValue.length - 1)))
  7678. attrValueRange.end--;
  7679. result.push(attrValueRange);
  7680. if (attrName == 'class') {
  7681. result = result.concat(classNameRanges(attrValueRange.substring(source), attrValueRange.start));
  7682. }
  7683. break;
  7684. }
  7685. });
  7686. // offset ranges
  7687. _.each(result, function(r) {
  7688. r.shift(offset);
  7689. });
  7690. return _.chain(result)
  7691. .filter(function(item) { // remove empty
  7692. return !!item.length();
  7693. })
  7694. .uniq(false, function(item) { // remove duplicates
  7695. return item.toString();
  7696. })
  7697. .value();
  7698. }
  7699. /**
  7700. * Returns ranges of class names in "class" attribute value
  7701. * @param {String} className
  7702. * @returns {Array}
  7703. */
  7704. function classNameRanges(className, offset) {
  7705. offset = offset || 0;
  7706. var result = [];
  7707. /** @type StringStream */
  7708. var stream = require('stringStream').create(className);
  7709. var range = require('range');
  7710. // skip whitespace
  7711. stream.eatSpace();
  7712. stream.start = stream.pos;
  7713. var ch;
  7714. while (ch = stream.next()) {
  7715. if (/[\s\u00a0]/.test(ch)) {
  7716. result.push(range.create(stream.start + offset, stream.pos - stream.start - 1));
  7717. stream.eatSpace();
  7718. stream.start = stream.pos;
  7719. }
  7720. }
  7721. result.push(range.create(stream.start + offset, stream.pos - stream.start));
  7722. return result;
  7723. }
  7724. /**
  7725. * Returns best HTML tag range match for current selection
  7726. * @param {String} tag Tag declaration
  7727. * @param {Number} offset Tag's position index inside content
  7728. * @param {Range} selRange Selection range
  7729. * @return {Range} Returns range if next item was found, <code>null</code> otherwise
  7730. */
  7731. function getRangeForHTMLItem(tag, offset, selRange, isBackward) {
  7732. var ranges = makePossibleRangesHTML(tag, require('xmlParser').parse(tag), offset);
  7733. if (isBackward)
  7734. ranges.reverse();
  7735. // try to find selected range
  7736. var curRange = _.find(ranges, function(r) {
  7737. return r.equal(selRange);
  7738. });
  7739. if (curRange) {
  7740. var ix = _.indexOf(ranges, curRange);
  7741. if (ix < ranges.length - 1)
  7742. return ranges[ix + 1];
  7743. return null;
  7744. }
  7745. // no selected range, find nearest one
  7746. if (isBackward)
  7747. // search backward
  7748. return _.find(ranges, function(r) {
  7749. return r.start < selRange.start;
  7750. });
  7751. // search forward
  7752. // to deal with overlapping ranges (like full attribute definition
  7753. // and attribute value) let's find range under caret first
  7754. if (!curRange) {
  7755. var matchedRanges = _.filter(ranges, function(r) {
  7756. return r.inside(selRange.end);
  7757. });
  7758. if (matchedRanges.length > 1)
  7759. return matchedRanges[1];
  7760. }
  7761. return _.find(ranges, function(r) {
  7762. return r.end > selRange.end;
  7763. });
  7764. }
  7765. /**
  7766. * Search for opening tag in content, starting at specified position
  7767. * @param {String} html Where to search tag
  7768. * @param {Number} pos Character index where to start searching
  7769. * @return {Range} Returns range if valid opening tag was found,
  7770. * <code>null</code> otherwise
  7771. */
  7772. function findOpeningTagFromPosition(html, pos) {
  7773. var tag;
  7774. while (pos >= 0) {
  7775. if (tag = getOpeningTagFromPosition(html, pos))
  7776. return tag;
  7777. pos--;
  7778. }
  7779. return null;
  7780. }
  7781. /**
  7782. * @param {String} html Where to search tag
  7783. * @param {Number} pos Character index where to start searching
  7784. * @return {Range} Returns range if valid opening tag was found,
  7785. * <code>null</code> otherwise
  7786. */
  7787. function getOpeningTagFromPosition(html, pos) {
  7788. var m;
  7789. if (html.charAt(pos) == '<' && (m = html.substring(pos, html.length).match(startTag))) {
  7790. return require('range').create(pos, m[0]);
  7791. }
  7792. }
  7793. function isQuote(ch) {
  7794. return ch == '"' || ch == "'";
  7795. }
  7796. /**
  7797. * Makes all possible selection ranges for specified CSS property
  7798. * @param {CSSProperty} property
  7799. * @returns {Array}
  7800. */
  7801. function makePossibleRangesCSS(property) {
  7802. // find all possible ranges, sorted by position and size
  7803. var valueRange = property.valueRange(true);
  7804. var result = [property.range(true), valueRange];
  7805. var stringStream = require('stringStream');
  7806. var cssEditTree = require('cssEditTree');
  7807. var range = require('range');
  7808. // locate parts of complex values.
  7809. // some examples:
  7810. // – 1px solid red: 3 parts
  7811. // – arial, sans-serif: enumeration, 2 parts
  7812. // – url(image.png): function value part
  7813. var value = property.value();
  7814. _.each(property.valueParts(), function(r) {
  7815. // add absolute range
  7816. var clone = r.clone();
  7817. result.push(clone.shift(valueRange.start));
  7818. /** @type StringStream */
  7819. var stream = stringStream.create(r.substring(value));
  7820. if (stream.match(/^[\w\-]+\(/, true)) {
  7821. // we have a function, find values in it.
  7822. // but first add function contents
  7823. stream.start = stream.pos;
  7824. stream.skipToPair('(', ')');
  7825. var fnBody = stream.current();
  7826. result.push(range.create(clone.start + stream.start, fnBody));
  7827. // find parts
  7828. _.each(cssEditTree.findParts(fnBody), function(part) {
  7829. result.push(range.create(clone.start + stream.start + part.start, part.substring(fnBody)));
  7830. });
  7831. }
  7832. });
  7833. // optimize result: remove empty ranges and duplicates
  7834. return _.chain(result)
  7835. .filter(function(item) {
  7836. return !!item.length();
  7837. })
  7838. .uniq(false, function(item) {
  7839. return item.toString();
  7840. })
  7841. .value();
  7842. }
  7843. /**
  7844. * Tries to find matched CSS property and nearest range for selection
  7845. * @param {CSSRule} rule
  7846. * @param {Range} selRange
  7847. * @param {Boolean} isBackward
  7848. * @returns {Range}
  7849. */
  7850. function matchedRangeForCSSProperty(rule, selRange, isBackward) {
  7851. /** @type CSSProperty */
  7852. var property = null;
  7853. var possibleRanges, curRange = null, ix;
  7854. var list = rule.list();
  7855. var searchFn, nearestItemFn;
  7856. if (isBackward) {
  7857. list.reverse();
  7858. searchFn = function(p) {
  7859. return p.range(true).start <= selRange.start;
  7860. };
  7861. nearestItemFn = function(r) {
  7862. return r.start < selRange.start;
  7863. };
  7864. } else {
  7865. searchFn = function(p) {
  7866. return p.range(true).end >= selRange.end;
  7867. };
  7868. nearestItemFn = function(r) {
  7869. return r.end > selRange.start;
  7870. };
  7871. }
  7872. // search for nearest to selection CSS property
  7873. while (property = _.find(list, searchFn)) {
  7874. possibleRanges = makePossibleRangesCSS(property);
  7875. if (isBackward)
  7876. possibleRanges.reverse();
  7877. // check if any possible range is already selected
  7878. curRange = _.find(possibleRanges, function(r) {
  7879. return r.equal(selRange);
  7880. });
  7881. if (!curRange) {
  7882. // no selection, select nearest item
  7883. var matchedRanges = _.filter(possibleRanges, function(r) {
  7884. return r.inside(selRange.end);
  7885. });
  7886. if (matchedRanges.length > 1) {
  7887. curRange = matchedRanges[1];
  7888. break;
  7889. }
  7890. if (curRange = _.find(possibleRanges, nearestItemFn))
  7891. break;
  7892. } else {
  7893. ix = _.indexOf(possibleRanges, curRange);
  7894. if (ix != possibleRanges.length - 1) {
  7895. curRange = possibleRanges[ix + 1];
  7896. break;
  7897. }
  7898. }
  7899. curRange = null;
  7900. selRange.start = selRange.end = isBackward
  7901. ? property.range(true).start - 1
  7902. : property.range(true).end + 1;
  7903. }
  7904. return curRange;
  7905. }
  7906. function findNextCSSItem(editor) {
  7907. return findItem(editor, false, require('cssEditTree').extractRule, getRangeForNextItemInCSS);
  7908. }
  7909. function findPrevCSSItem(editor) {
  7910. return findItem(editor, true, require('cssEditTree').extractRule, getRangeForPrevItemInCSS);
  7911. }
  7912. /**
  7913. * Returns range for item to be selected in CSS after current caret
  7914. * (selection) position
  7915. * @param {String} rule CSS rule declaration
  7916. * @param {Number} offset Rule's position index inside content
  7917. * @param {Range} selRange Selection range
  7918. * @return {Range} Returns range if next item was found, <code>null</code> otherwise
  7919. */
  7920. function getRangeForNextItemInCSS(rule, offset, selRange) {
  7921. var tree = require('cssEditTree').parse(rule, {
  7922. offset: offset
  7923. });
  7924. // check if selector is matched
  7925. var range = tree.nameRange(true);
  7926. if (selRange.end < range.end) {
  7927. return range;
  7928. }
  7929. return matchedRangeForCSSProperty(tree, selRange, false);
  7930. }
  7931. /**
  7932. * Returns range for item to be selected in CSS before current caret
  7933. * (selection) position
  7934. * @param {String} rule CSS rule declaration
  7935. * @param {Number} offset Rule's position index inside content
  7936. * @param {Range} selRange Selection range
  7937. * @return {Range} Returns range if previous item was found, <code>null</code> otherwise
  7938. */
  7939. function getRangeForPrevItemInCSS(rule, offset, selRange) {
  7940. var tree = require('cssEditTree').parse(rule, {
  7941. offset: offset
  7942. });
  7943. var curRange = matchedRangeForCSSProperty(tree, selRange, true);
  7944. if (!curRange) {
  7945. // no matched property, try to match selector
  7946. var range = tree.nameRange(true);
  7947. if (selRange.start > range.start) {
  7948. return range;
  7949. }
  7950. }
  7951. return curRange;
  7952. }
  7953. // XXX register actions
  7954. var actions = require('actions');
  7955. actions.add('select_next_item', function(editor){
  7956. if (editor.getSyntax() == 'css')
  7957. return findNextCSSItem(editor);
  7958. else
  7959. return findNextHTMLItem(editor);
  7960. });
  7961. actions.add('select_previous_item', function(editor){
  7962. if (editor.getSyntax() == 'css')
  7963. return findPrevCSSItem(editor);
  7964. else
  7965. return findPrevHTMLItem(editor);
  7966. });
  7967. });/**
  7968. * HTML pair matching (balancing) actions
  7969. * @constructor
  7970. * @memberOf __matchPairActionDefine
  7971. * @param {Function} require
  7972. * @param {Underscore} _
  7973. */
  7974. emmet.exec(function(require, _) {
  7975. /** @type emmet.actions */
  7976. var actions = require('actions');
  7977. var matcher = require('htmlMatcher');
  7978. var lastMatch = null;
  7979. /**
  7980. * Find and select HTML tag pair
  7981. * @param {IEmmetEditor} editor Editor instance
  7982. * @param {String} direction Direction of pair matching: 'in' or 'out'.
  7983. * Default is 'out'
  7984. */
  7985. function matchPair(editor, direction) {
  7986. direction = String((direction || 'out').toLowerCase());
  7987. var info = require('editorUtils').outputInfo(editor);
  7988. var range = require('range');
  7989. /** @type Range */
  7990. var sel = range.create(editor.getSelectionRange());
  7991. var content = info.content;
  7992. // validate previous match
  7993. if (lastMatch && !lastMatch.range.equal(sel)) {
  7994. lastMatch = null;
  7995. }
  7996. if (lastMatch && sel.length()) {
  7997. if (direction == 'in') {
  7998. // user has previously selected tag and wants to move inward
  7999. if (lastMatch.type == 'tag' && !lastMatch.close) {
  8000. // unary tag was selected, can't move inward
  8001. return false;
  8002. } else {
  8003. if (lastMatch.range.equal(lastMatch.outerRange)) {
  8004. lastMatch.range = lastMatch.innerRange;
  8005. } else {
  8006. var narrowed = require('utils').narrowToNonSpace(content, lastMatch.innerRange);
  8007. lastMatch = matcher.find(content, narrowed.start + 1);
  8008. if (lastMatch && lastMatch.range.equal(sel) && lastMatch.outerRange.equal(sel)) {
  8009. lastMatch.range = lastMatch.innerRange;
  8010. }
  8011. }
  8012. }
  8013. } else {
  8014. if (
  8015. !lastMatch.innerRange.equal(lastMatch.outerRange)
  8016. && lastMatch.range.equal(lastMatch.innerRange)
  8017. && sel.equal(lastMatch.range)) {
  8018. lastMatch.range = lastMatch.outerRange;
  8019. } else {
  8020. lastMatch = matcher.find(content, sel.start);
  8021. if (lastMatch && lastMatch.range.equal(sel) && lastMatch.innerRange.equal(sel)) {
  8022. lastMatch.range = lastMatch.outerRange;
  8023. }
  8024. }
  8025. }
  8026. } else {
  8027. lastMatch = matcher.find(content, sel.start);
  8028. }
  8029. if (lastMatch && !lastMatch.range.equal(sel)) {
  8030. editor.createSelection(lastMatch.range.start, lastMatch.range.end);
  8031. return true;
  8032. }
  8033. lastMatch = null;
  8034. return false;
  8035. }
  8036. actions.add('match_pair', matchPair, {hidden: true});
  8037. actions.add('match_pair_inward', function(editor){
  8038. return matchPair(editor, 'in');
  8039. }, {label: 'HTML/Match Pair Tag (inward)'});
  8040. actions.add('match_pair_outward', function(editor){
  8041. return matchPair(editor, 'out');
  8042. }, {label: 'HTML/Match Pair Tag (outward)'});
  8043. /**
  8044. * Moves caret to matching opening or closing tag
  8045. * @param {IEmmetEditor} editor
  8046. */
  8047. actions.add('matching_pair', function(editor) {
  8048. var content = String(editor.getContent());
  8049. var caretPos = editor.getCaretPos();
  8050. if (content.charAt(caretPos) == '<')
  8051. // looks like caret is outside of tag pair
  8052. caretPos++;
  8053. var tag = matcher.tag(content, caretPos);
  8054. if (tag && tag.close) { // exclude unary tags
  8055. if (tag.open.range.inside(caretPos)) {
  8056. editor.setCaretPos(tag.close.range.start);
  8057. } else {
  8058. editor.setCaretPos(tag.open.range.start);
  8059. }
  8060. return true;
  8061. }
  8062. return false;
  8063. }, {label: 'HTML/Go To Matching Tag Pair'});
  8064. });/**
  8065. * Gracefully removes tag under cursor
  8066. *
  8067. * @param {Function} require
  8068. * @param {Underscore} _
  8069. */
  8070. emmet.exec(function(require, _) {
  8071. require('actions').add('remove_tag', function(editor) {
  8072. var utils = require('utils');
  8073. var info = require('editorUtils').outputInfo(editor);
  8074. // search for tag
  8075. var tag = require('htmlMatcher').tag(info.content, editor.getCaretPos());
  8076. if (tag) {
  8077. if (!tag.close) {
  8078. // simply remove unary tag
  8079. editor.replaceContent(utils.getCaretPlaceholder(), tag.range.start, tag.range.end);
  8080. } else {
  8081. // remove tag and its newlines
  8082. /** @type Range */
  8083. var tagContentRange = utils.narrowToNonSpace(info.content, tag.innerRange);
  8084. /** @type Range */
  8085. var startLineBounds = utils.findNewlineBounds(info.content, tagContentRange.start);
  8086. var startLinePad = utils.getLinePadding(startLineBounds.substring(info.content));
  8087. var tagContent = tagContentRange.substring(info.content);
  8088. tagContent = utils.unindentString(tagContent, startLinePad);
  8089. editor.replaceContent(utils.getCaretPlaceholder() + utils.escapeText(tagContent), tag.outerRange.start, tag.outerRange.end);
  8090. }
  8091. return true;
  8092. }
  8093. return false;
  8094. }, {label: 'HTML/Remove Tag'});
  8095. });
  8096. /**
  8097. * Splits or joins tag, e.g. transforms it into a short notation and vice versa:<br>
  8098. * &lt;div&gt;&lt;/div&gt; → &lt;div /&gt; : join<br>
  8099. * &lt;div /&gt; → &lt;div&gt;&lt;/div&gt; : split
  8100. * @param {Function} require
  8101. * @param {Underscore} _
  8102. * @memberOf __splitJoinTagAction
  8103. * @constructor
  8104. */
  8105. emmet.exec(function(require, _) {
  8106. /**
  8107. * @param {IEmmetEditor} editor
  8108. * @param {Object} profile
  8109. * @param {Object} tag
  8110. */
  8111. function joinTag(editor, profile, tag) {
  8112. /** @type emmet.utils */
  8113. var utils = require('utils');
  8114. // empty closing slash is a nonsense for this action
  8115. var slash = profile.selfClosing() || ' /';
  8116. var content = tag.open.range.substring(tag.source).replace(/\s*>$/, slash + '>');
  8117. var caretPos = editor.getCaretPos();
  8118. // update caret position
  8119. if (content.length + tag.outerRange.start < caretPos) {
  8120. caretPos = content.length + tag.outerRange.start;
  8121. }
  8122. content = utils.escapeText(content);
  8123. editor.replaceContent(content, tag.outerRange.start, tag.outerRange.end);
  8124. editor.setCaretPos(caretPos);
  8125. return true;
  8126. }
  8127. function splitTag(editor, profile, tag) {
  8128. /** @type emmet.utils */
  8129. var utils = require('utils');
  8130. var nl = utils.getNewline();
  8131. var pad = require('resources').getVariable('indentation');
  8132. var caretPos = editor.getCaretPos();
  8133. // define tag content depending on profile
  8134. var tagContent = (profile.tag_nl === true) ? nl + pad + nl : '';
  8135. var content = tag.outerContent().replace(/\s*\/>$/, '>');
  8136. caretPos = tag.outerRange.start + content.length;
  8137. content += tagContent + '</' + tag.open.name + '>';
  8138. content = utils.escapeText(content);
  8139. editor.replaceContent(content, tag.outerRange.start, tag.outerRange.end);
  8140. editor.setCaretPos(caretPos);
  8141. return true;
  8142. }
  8143. require('actions').add('split_join_tag', function(editor, profileName) {
  8144. var matcher = require('htmlMatcher');
  8145. var info = require('editorUtils').outputInfo(editor, null, profileName);
  8146. var profile = require('profile').get(info.profile);
  8147. // find tag at current position
  8148. var tag = matcher.tag(info.content, editor.getCaretPos());
  8149. if (tag) {
  8150. return tag.close
  8151. ? joinTag(editor, profile, tag)
  8152. : splitTag(editor, profile, tag);
  8153. }
  8154. return false;
  8155. }, {label: 'HTML/Split\\Join Tag Declaration'});
  8156. });/**
  8157. * Reflect CSS value: takes rule's value under caret and pastes it for the same
  8158. * rules with vendor prefixes
  8159. * @constructor
  8160. * @memberOf __reflectCSSActionDefine
  8161. * @param {Function} require
  8162. * @param {Underscore} _
  8163. */
  8164. emmet.define('reflectCSSValue', function(require, _) {
  8165. /**
  8166. * @type HandlerList List of registered handlers
  8167. */
  8168. var handlers = require('handlerList').create();
  8169. require('actions').add('reflect_css_value', function(editor) {
  8170. if (editor.getSyntax() != 'css') return false;
  8171. return require('actionUtils').compoundUpdate(editor, doCSSReflection(editor));
  8172. }, {label: 'CSS/Reflect Value'});
  8173. function doCSSReflection(editor) {
  8174. /** @type emmet.cssEditTree */
  8175. var cssEditTree = require('cssEditTree');
  8176. var outputInfo = require('editorUtils').outputInfo(editor);
  8177. var caretPos = editor.getCaretPos();
  8178. var cssRule = cssEditTree.parseFromPosition(outputInfo.content, caretPos);
  8179. if (!cssRule) return;
  8180. var property = cssRule.itemFromPosition(caretPos, true);
  8181. // no property under cursor, nothing to reflect
  8182. if (!property) return;
  8183. var oldRule = cssRule.source;
  8184. var offset = cssRule.options.offset;
  8185. var caretDelta = caretPos - offset - property.range().start;
  8186. handlers.exec(false, [property]);
  8187. if (oldRule !== cssRule.source) {
  8188. return {
  8189. data: cssRule.source,
  8190. start: offset,
  8191. end: offset + oldRule.length,
  8192. caret: offset + property.range().start + caretDelta
  8193. };
  8194. }
  8195. }
  8196. /**
  8197. * Returns regexp that should match reflected CSS property names
  8198. * @param {String} name Current CSS property name
  8199. * @return {RegExp}
  8200. */
  8201. function getReflectedCSSName(name) {
  8202. name = require('cssEditTree').baseName(name);
  8203. var vendorPrefix = '^(?:\\-\\w+\\-)?', m;
  8204. if (name == 'opacity' || name == 'filter') {
  8205. return new RegExp(vendorPrefix + '(?:opacity|filter)$');
  8206. } else if (m = name.match(/^border-radius-(top|bottom)(left|right)/)) {
  8207. // Mozilla-style border radius
  8208. return new RegExp(vendorPrefix + '(?:' + name + '|border-' + m[1] + '-' + m[2] + '-radius)$');
  8209. } else if (m = name.match(/^border-(top|bottom)-(left|right)-radius/)) {
  8210. return new RegExp(vendorPrefix + '(?:' + name + '|border-radius-' + m[1] + m[2] + ')$');
  8211. }
  8212. return new RegExp(vendorPrefix + name + '$');
  8213. }
  8214. /**
  8215. * Reflects value from <code>donor</code> into <code>receiver</code>
  8216. * @param {CSSProperty} donor Donor CSS property from which value should
  8217. * be reflected
  8218. * @param {CSSProperty} receiver Property that should receive reflected
  8219. * value from donor
  8220. */
  8221. function reflectValue(donor, receiver) {
  8222. var value = getReflectedValue(donor.name(), donor.value(),
  8223. receiver.name(), receiver.value());
  8224. receiver.value(value);
  8225. }
  8226. /**
  8227. * Returns value that should be reflected for <code>refName</code> CSS property
  8228. * from <code>curName</code> property. This function is used for special cases,
  8229. * when the same result must be achieved with different properties for different
  8230. * browsers. For example: opаcity:0.5; → filter:alpha(opacity=50);<br><br>
  8231. *
  8232. * This function does value conversion between different CSS properties
  8233. *
  8234. * @param {String} curName Current CSS property name
  8235. * @param {String} curValue Current CSS property value
  8236. * @param {String} refName Receiver CSS property's name
  8237. * @param {String} refValue Receiver CSS property's value
  8238. * @return {String} New value for receiver property
  8239. */
  8240. function getReflectedValue(curName, curValue, refName, refValue) {
  8241. var cssEditTree = require('cssEditTree');
  8242. var utils = require('utils');
  8243. curName = cssEditTree.baseName(curName);
  8244. refName = cssEditTree.baseName(refName);
  8245. if (curName == 'opacity' && refName == 'filter') {
  8246. return refValue.replace(/opacity=[^)]*/i, 'opacity=' + Math.floor(parseFloat(curValue) * 100));
  8247. } else if (curName == 'filter' && refName == 'opacity') {
  8248. var m = curValue.match(/opacity=([^)]*)/i);
  8249. return m ? utils.prettifyNumber(parseInt(m[1]) / 100) : refValue;
  8250. }
  8251. return curValue;
  8252. }
  8253. // XXX add default handler
  8254. handlers.add(function(property) {
  8255. var reName = getReflectedCSSName(property.name());
  8256. _.each(property.parent.list(), function(p) {
  8257. if (reName.test(p.name())) {
  8258. reflectValue(property, p);
  8259. }
  8260. });
  8261. }, {order: -1});
  8262. return {
  8263. /**
  8264. * Adds custom reflect handler. The passed function will receive matched
  8265. * CSS property (as <code>CSSEditElement</code> object) and should
  8266. * return <code>true</code> if it was performed successfully (handled
  8267. * reflection), <code>false</code> otherwise.
  8268. * @param {Function} fn
  8269. * @param {Object} options
  8270. */
  8271. addHandler: function(fn, options) {
  8272. handlers.add(fn, options);
  8273. },
  8274. /**
  8275. * Removes registered handler
  8276. * @returns
  8277. */
  8278. removeHandler: function(fn) {
  8279. handlers.remove(fn, options);
  8280. }
  8281. };
  8282. });/**
  8283. * Evaluates simple math expression under caret
  8284. * @param {Function} require
  8285. * @param {Underscore} _
  8286. */
  8287. emmet.exec(function(require, _) {
  8288. require('actions').add('evaluate_math_expression', function(editor) {
  8289. var actionUtils = require('actionUtils');
  8290. var utils = require('utils');
  8291. var content = String(editor.getContent());
  8292. var chars = '.+-*/\\';
  8293. /** @type Range */
  8294. var sel = require('range').create(editor.getSelectionRange());
  8295. if (!sel.length()) {
  8296. sel = actionUtils.findExpressionBounds(editor, function(ch) {
  8297. return utils.isNumeric(ch) || chars.indexOf(ch) != -1;
  8298. });
  8299. }
  8300. if (sel && sel.length()) {
  8301. var expr = sel.substring(content);
  8302. // replace integral division: 11\2 => Math.round(11/2)
  8303. expr = expr.replace(/([\d\.\-]+)\\([\d\.\-]+)/g, 'Math.round($1/$2)');
  8304. try {
  8305. var result = utils.prettifyNumber(new Function('return ' + expr)());
  8306. editor.replaceContent(result, sel.start, sel.end);
  8307. editor.setCaretPos(sel.start + result.length);
  8308. return true;
  8309. } catch (e) {}
  8310. }
  8311. return false;
  8312. }, {label: 'Numbers/Evaluate Math Expression'});
  8313. });
  8314. /**
  8315. * Increment/decrement number under cursor
  8316. * @param {Function} require
  8317. * @param {Underscore} _
  8318. */
  8319. emmet.exec(function(require, _) {
  8320. /**
  8321. * Extract number from current caret position of the <code>editor</code> and
  8322. * increment it by <code>step</code>
  8323. * @param {IEmmetEditor} editor
  8324. * @param {Number} step Increment step (may be negative)
  8325. */
  8326. function incrementNumber(editor, step) {
  8327. var utils = require('utils');
  8328. var actionUtils = require('actionUtils');
  8329. var hasSign = false;
  8330. var hasDecimal = false;
  8331. var r = actionUtils.findExpressionBounds(editor, function(ch, pos, content) {
  8332. if (utils.isNumeric(ch))
  8333. return true;
  8334. if (ch == '.') {
  8335. // make sure that next character is numeric too
  8336. if (!utils.isNumeric(content.charAt(pos + 1)))
  8337. return false;
  8338. return hasDecimal ? false : hasDecimal = true;
  8339. }
  8340. if (ch == '-')
  8341. return hasSign ? false : hasSign = true;
  8342. return false;
  8343. });
  8344. if (r && r.length()) {
  8345. var strNum = r.substring(String(editor.getContent()));
  8346. var num = parseFloat(strNum);
  8347. if (!_.isNaN(num)) {
  8348. num = utils.prettifyNumber(num + step);
  8349. // do we have zero-padded number?
  8350. if (/^(\-?)0+[1-9]/.test(strNum)) {
  8351. var minus = '';
  8352. if (RegExp.$1) {
  8353. minus = '-';
  8354. num = num.substring(1);
  8355. }
  8356. var parts = num.split('.');
  8357. parts[0] = utils.zeroPadString(parts[0], intLength(strNum));
  8358. num = minus + parts.join('.');
  8359. }
  8360. editor.replaceContent(num, r.start, r.end);
  8361. editor.createSelection(r.start, r.start + num.length);
  8362. return true;
  8363. }
  8364. }
  8365. return false;
  8366. }
  8367. /**
  8368. * Returns length of integer part of number
  8369. * @param {String} num
  8370. */
  8371. function intLength(num) {
  8372. num = num.replace(/^\-/, '');
  8373. if (~num.indexOf('.')) {
  8374. return num.split('.')[0].length;
  8375. }
  8376. return num.length;
  8377. }
  8378. var actions = require('actions');
  8379. _.each([1, -1, 10, -10, 0.1, -0.1], function(num) {
  8380. var prefix = num > 0 ? 'increment' : 'decrement';
  8381. actions.add(prefix + '_number_by_' + String(Math.abs(num)).replace('.', '').substring(0, 2), function(editor) {
  8382. return incrementNumber(editor, num);
  8383. }, {label: 'Numbers/' + prefix.charAt(0).toUpperCase() + prefix.substring(1) + ' number by ' + Math.abs(num)});
  8384. });
  8385. });/**
  8386. * Actions to insert line breaks. Some simple editors (like browser's
  8387. * &lt;textarea&gt;, for example) do not provide such simple things
  8388. * @param {Function} require
  8389. * @param {Underscore} _
  8390. */
  8391. emmet.exec(function(require, _) {
  8392. var actions = require('actions');
  8393. /** @type emmet.preferences */
  8394. var prefs = require('preferences');
  8395. // setup default preferences
  8396. prefs.define('css.closeBraceIndentation', '\n',
  8397. 'Indentation before closing brace of CSS rule. Some users prefere '
  8398. + 'indented closing brace of CSS rule for better readability. '
  8399. + 'This preference’s value will be automatically inserted before '
  8400. + 'closing brace when user adds newline in newly created CSS rule '
  8401. + '(e.g. when “Insert formatted linebreak” action will be performed '
  8402. + 'in CSS file). If you’re such user, you may want to write put a value '
  8403. + 'like <code>\\n\\t</code> in this preference.');
  8404. /**
  8405. * Inserts newline character with proper indentation in specific positions only.
  8406. * @param {IEmmetEditor} editor
  8407. * @return {Boolean} Returns <code>true</code> if line break was inserted
  8408. */
  8409. actions.add('insert_formatted_line_break_only', function(editor) {
  8410. var utils = require('utils');
  8411. /** @type emmet.resources */
  8412. var res = require('resources');
  8413. var info = require('editorUtils').outputInfo(editor);
  8414. var caretPos = editor.getCaretPos();
  8415. var nl = utils.getNewline();
  8416. if (_.include(['html', 'xml', 'xsl'], info.syntax)) {
  8417. var pad = res.getVariable('indentation');
  8418. // let's see if we're breaking newly created tag
  8419. var tag = require('htmlMatcher').tag(info.content, caretPos);
  8420. if (tag && !tag.innerRange.length()) {
  8421. editor.replaceContent(nl + pad + utils.getCaretPlaceholder() + nl, caretPos);
  8422. return true;
  8423. }
  8424. } else if (info.syntax == 'css') {
  8425. /** @type String */
  8426. var content = info.content;
  8427. if (caretPos && content.charAt(caretPos - 1) == '{') {
  8428. var append = prefs.get('css.closeBraceIndentation');
  8429. var pad = res.getVariable('indentation');
  8430. var hasCloseBrace = content.charAt(caretPos) == '}';
  8431. if (!hasCloseBrace) {
  8432. // do we really need special formatting here?
  8433. // check if this is really a newly created rule,
  8434. // look ahead for a closing brace
  8435. for (var i = caretPos, il = content.length, ch; i < il; i++) {
  8436. ch = content.charAt(i);
  8437. if (ch == '{') {
  8438. // ok, this is a new rule without closing brace
  8439. break;
  8440. }
  8441. if (ch == '}') {
  8442. // not a new rule, just add indentation
  8443. append = '';
  8444. hasCloseBrace = true;
  8445. break;
  8446. }
  8447. }
  8448. }
  8449. if (!hasCloseBrace) {
  8450. append += '}';
  8451. }
  8452. // defining rule set
  8453. var insValue = nl + pad + utils.getCaretPlaceholder() + append;
  8454. editor.replaceContent(insValue, caretPos);
  8455. return true;
  8456. }
  8457. }
  8458. return false;
  8459. }, {hidden: !true});
  8460. /**
  8461. * Inserts newline character with proper indentation. This action is used in
  8462. * editors that doesn't have indentation control (like textarea element) to
  8463. * provide proper indentation
  8464. * @param {IEmmetEditor} editor Editor instance
  8465. */
  8466. actions.add('insert_formatted_line_break', function(editor) {
  8467. if (!actions.run('insert_formatted_line_break_only', editor)) {
  8468. var utils = require('utils');
  8469. var curPadding = require('editorUtils').getCurrentLinePadding(editor);
  8470. var content = String(editor.getContent());
  8471. var caretPos = editor.getCaretPos();
  8472. var len = content.length;
  8473. var nl = utils.getNewline();
  8474. // check out next line padding
  8475. var lineRange = editor.getCurrentLineRange();
  8476. var nextPadding = '';
  8477. for (var i = lineRange.end + 1, ch; i < len; i++) {
  8478. ch = content.charAt(i);
  8479. if (ch == ' ' || ch == '\t')
  8480. nextPadding += ch;
  8481. else
  8482. break;
  8483. }
  8484. if (nextPadding.length > curPadding.length)
  8485. editor.replaceContent(nl + nextPadding, caretPos, caretPos, true);
  8486. else
  8487. editor.replaceContent(nl, caretPos);
  8488. }
  8489. return true;
  8490. }, {hidden: true});
  8491. });/**
  8492. * Merges selected lines or lines between XHTML tag pairs
  8493. * @param {Function} require
  8494. * @param {Underscore} _
  8495. */
  8496. emmet.exec(function(require, _) {
  8497. require('actions').add('merge_lines', function(editor) {
  8498. var matcher = require('htmlMatcher');
  8499. var utils = require('utils');
  8500. var editorUtils = require('editorUtils');
  8501. var info = editorUtils.outputInfo(editor);
  8502. /** @type Range */
  8503. var selection = require('range').create(editor.getSelectionRange());
  8504. if (!selection.length()) {
  8505. // find matching tag
  8506. var pair = matcher.find(info.content, editor.getCaretPos());
  8507. if (pair) {
  8508. selection = pair.outerRange;
  8509. }
  8510. }
  8511. if (selection.length()) {
  8512. // got range, merge lines
  8513. var text = selection.substring(info.content);
  8514. var lines = utils.splitByLines(text);
  8515. for (var i = 1; i < lines.length; i++) {
  8516. lines[i] = lines[i].replace(/^\s+/, '');
  8517. }
  8518. text = lines.join('').replace(/\s{2,}/, ' ');
  8519. var textLen = text.length;
  8520. text = utils.escapeText(text);
  8521. editor.replaceContent(text, selection.start, selection.end);
  8522. editor.createSelection(selection.start, selection.start + textLen);
  8523. return true;
  8524. }
  8525. return false;
  8526. });
  8527. });/**
  8528. * Encodes/decodes image under cursor to/from base64
  8529. * @param {IEmmetEditor} editor
  8530. * @since 0.65
  8531. *
  8532. * @memberOf __base64ActionDefine
  8533. * @constructor
  8534. * @param {Function} require
  8535. * @param {Underscore} _
  8536. */
  8537. emmet.exec(function(require, _) {
  8538. require('actions').add('encode_decode_data_url', function(editor) {
  8539. var data = String(editor.getSelection());
  8540. var caretPos = editor.getCaretPos();
  8541. if (!data) {
  8542. // no selection, try to find image bounds from current caret position
  8543. var text = String(editor.getContent()), m;
  8544. while (caretPos-- >= 0) {
  8545. if (startsWith('src=', text, caretPos)) { // found <img src="">
  8546. if (m = text.substr(caretPos).match(/^(src=(["'])?)([^'"<>\s]+)\1?/)) {
  8547. data = m[3];
  8548. caretPos += m[1].length;
  8549. }
  8550. break;
  8551. } else if (startsWith('url(', text, caretPos)) { // found CSS url() pattern
  8552. if (m = text.substr(caretPos).match(/^(url\((['"])?)([^'"\)\s]+)\1?/)) {
  8553. data = m[3];
  8554. caretPos += m[1].length;
  8555. }
  8556. break;
  8557. }
  8558. }
  8559. }
  8560. if (data) {
  8561. if (startsWith('data:', data))
  8562. return decodeFromBase64(editor, data, caretPos);
  8563. else
  8564. return encodeToBase64(editor, data, caretPos);
  8565. }
  8566. return false;
  8567. }, {label: 'Encode\\Decode data:URL image'});
  8568. /**
  8569. * Test if <code>text</code> starts with <code>token</code> at <code>pos</code>
  8570. * position. If <code>pos</code> is omitted, search from beginning of text
  8571. * @param {String} token Token to test
  8572. * @param {String} text Where to search
  8573. * @param {Number} pos Position where to start search
  8574. * @return {Boolean}
  8575. * @since 0.65
  8576. */
  8577. function startsWith(token, text, pos) {
  8578. pos = pos || 0;
  8579. return text.charAt(pos) == token.charAt(0) && text.substr(pos, token.length) == token;
  8580. }
  8581. /**
  8582. * Encodes image to base64
  8583. *
  8584. * @param {IEmmetEditor} editor
  8585. * @param {String} imgPath Path to image
  8586. * @param {Number} pos Caret position where image is located in the editor
  8587. * @return {Boolean}
  8588. */
  8589. function encodeToBase64(editor, imgPath, pos) {
  8590. var file = require('file');
  8591. var actionUtils = require('actionUtils');
  8592. var editorFile = editor.getFilePath();
  8593. var defaultMimeType = 'application/octet-stream';
  8594. if (editorFile === null) {
  8595. throw "You should save your file before using this action";
  8596. }
  8597. // locate real image path
  8598. var realImgPath = file.locateFile(editorFile, imgPath);
  8599. if (realImgPath === null) {
  8600. throw "Can't find " + imgPath + ' file';
  8601. }
  8602. file.read(realImgPath, function(err, content) {
  8603. if (err) {
  8604. throw 'Unable to read ' + realImgPath + ': ' + err;
  8605. }
  8606. var b64 = require('base64').encode(String(content));
  8607. if (!b64) {
  8608. throw "Can't encode file content to base64";
  8609. }
  8610. b64 = 'data:' + (actionUtils.mimeTypes[String(file.getExt(realImgPath))] || defaultMimeType) +
  8611. ';base64,' + b64;
  8612. editor.replaceContent('$0' + b64, pos, pos + imgPath.length);
  8613. });
  8614. return true;
  8615. }
  8616. /**
  8617. * Decodes base64 string back to file.
  8618. * @param {IEmmetEditor} editor
  8619. * @param {String} data Base64-encoded file content
  8620. * @param {Number} pos Caret position where image is located in the editor
  8621. */
  8622. function decodeFromBase64(editor, data, pos) {
  8623. // ask user to enter path to file
  8624. var filePath = String(editor.prompt('Enter path to file (absolute or relative)'));
  8625. if (!filePath)
  8626. return false;
  8627. var file = require('file');
  8628. var absPath = file.createPath(editor.getFilePath(), filePath);
  8629. if (!absPath) {
  8630. throw "Can't save file";
  8631. }
  8632. file.save(absPath, require('base64').decode( data.replace(/^data\:.+?;.+?,/, '') ));
  8633. editor.replaceContent('$0' + filePath, pos, pos + data.length);
  8634. return true;
  8635. }
  8636. });
  8637. /**
  8638. * Automatically updates image size attributes in HTML's &lt;img&gt; element or
  8639. * CSS rule
  8640. * @param {Function} require
  8641. * @param {Underscore} _
  8642. * @constructor
  8643. * @memberOf __updateImageSizeAction
  8644. */
  8645. emmet.exec(function(require, _) {
  8646. /**
  8647. * Updates image size of &lt;img src=""&gt; tag
  8648. * @param {IEmmetEditor} editor
  8649. */
  8650. function updateImageSizeHTML(editor) {
  8651. var offset = editor.getCaretPos();
  8652. // find tag from current caret position
  8653. var info = require('editorUtils').outputInfo(editor);
  8654. var xmlElem = require('xmlEditTree').parseFromPosition(info.content, offset, true);
  8655. if (xmlElem && (xmlElem.name() || '').toLowerCase() == 'img') {
  8656. getImageSizeForSource(editor, xmlElem.value('src'), function(size) {
  8657. if (size) {
  8658. var compoundData = xmlElem.range(true);
  8659. xmlElem.value('width', size.width);
  8660. xmlElem.value('height', size.height, xmlElem.indexOf('width') + 1);
  8661. require('actionUtils').compoundUpdate(editor, _.extend(compoundData, {
  8662. data: xmlElem.toString(),
  8663. caret: offset
  8664. }));
  8665. }
  8666. });
  8667. }
  8668. }
  8669. /**
  8670. * Updates image size of CSS property
  8671. * @param {IEmmetEditor} editor
  8672. */
  8673. function updateImageSizeCSS(editor) {
  8674. var offset = editor.getCaretPos();
  8675. // find tag from current caret position
  8676. var info = require('editorUtils').outputInfo(editor);
  8677. var cssRule = require('cssEditTree').parseFromPosition(info.content, offset, true);
  8678. if (cssRule) {
  8679. // check if there is property with image under caret
  8680. var prop = cssRule.itemFromPosition(offset, true), m;
  8681. if (prop && (m = /url\((["']?)(.+?)\1\)/i.exec(prop.value() || ''))) {
  8682. getImageSizeForSource(editor, m[2], function(size) {
  8683. if (size) {
  8684. var compoundData = cssRule.range(true);
  8685. cssRule.value('width', size.width + 'px');
  8686. cssRule.value('height', size.height + 'px', cssRule.indexOf('width') + 1);
  8687. require('actionUtils').compoundUpdate(editor, _.extend(compoundData, {
  8688. data: cssRule.toString(),
  8689. caret: offset
  8690. }));
  8691. }
  8692. });
  8693. }
  8694. }
  8695. }
  8696. /**
  8697. * Returns image dimensions for source
  8698. * @param {IEmmetEditor} editor
  8699. * @param {String} src Image source (path or data:url)
  8700. */
  8701. function getImageSizeForSource(editor, src, callback) {
  8702. var fileContent;
  8703. var au = require('actionUtils');
  8704. if (src) {
  8705. // check if it is data:url
  8706. if (/^data:/.test(src)) {
  8707. fileContent = require('base64').decode( src.replace(/^data\:.+?;.+?,/, '') );
  8708. return callback(au.getImageSize(fileContent));
  8709. }
  8710. var file = require('file');
  8711. var absPath = file.locateFile(editor.getFilePath(), src);
  8712. if (absPath === null) {
  8713. throw "Can't find " + src + ' file';
  8714. }
  8715. file.read(absPath, function(err, content) {
  8716. if (err) {
  8717. throw 'Unable to read ' + absPath + ': ' + err;
  8718. }
  8719. content = String(content);
  8720. callback(au.getImageSize(content));
  8721. });
  8722. }
  8723. }
  8724. require('actions').add('update_image_size', function(editor) {
  8725. // this action will definitely won’t work in SASS dialect,
  8726. // but may work in SCSS or LESS
  8727. if (_.include(['css', 'less', 'scss'], String(editor.getSyntax()))) {
  8728. updateImageSizeCSS(editor);
  8729. } else {
  8730. updateImageSizeHTML(editor);
  8731. }
  8732. return true;
  8733. });
  8734. });/**
  8735. * Resolver for fast CSS typing. Handles abbreviations with the following
  8736. * notation:<br>
  8737. *
  8738. * <code>(-vendor prefix)?property(value)*(!)?</code>
  8739. *
  8740. * <br><br>
  8741. * <b>Abbreviation handling</b><br>
  8742. *
  8743. * By default, Emmet searches for matching snippet definition for provided abbreviation.
  8744. * If snippet wasn't found, Emmet automatically generates element with
  8745. * abbreviation's name. For example, <code>foo</code> abbreviation will generate
  8746. * <code>&lt;foo&gt;&lt;/foo&gt;</code> output.
  8747. * <br><br>
  8748. * This module will capture all expanded properties and upgrade them with values,
  8749. * vendor prefixes and !important declarations. All unmatched abbreviations will
  8750. * be automatically transformed into <code>property-name: ${1}</code> snippets.
  8751. *
  8752. * <b>Vendor prefixes<b><br>
  8753. *
  8754. * If CSS-property is preceded with dash, resolver should output property with
  8755. * all <i>known</i> vendor prefixes. For example, if <code>brad</code>
  8756. * abbreviation generates <code>border-radius: ${value};</code> snippet,
  8757. * the <code>-brad</code> abbreviation should generate:
  8758. * <pre><code>
  8759. * -webkit-border-radius: ${value};
  8760. * -moz-border-radius: ${value};
  8761. * border-radius: ${value};
  8762. * </code></pre>
  8763. * Note that <i>o</i> and <i>ms</i> prefixes are omitted since Opera and IE
  8764. * supports unprefixed property.<br><br>
  8765. *
  8766. * Users can also provide an explicit list of one-character prefixes for any
  8767. * CSS property. For example, <code>-wm-float</code> will produce
  8768. *
  8769. * <pre><code>
  8770. * -webkit-float: ${1};
  8771. * -moz-float: ${1};
  8772. * float: ${1};
  8773. * </code></pre>
  8774. *
  8775. * Although this example looks pointless, users can use this feature to write
  8776. * cutting-edge properties implemented by browser vendors recently.
  8777. *
  8778. * @constructor
  8779. * @memberOf __cssResolverDefine
  8780. * @param {Function} require
  8781. * @param {Underscore} _
  8782. */
  8783. emmet.define('cssResolver', function(require, _) {
  8784. /** Back-reference to module */
  8785. var module = null;
  8786. var prefixObj = {
  8787. /** Real vendor prefix name */
  8788. prefix: 'emmet',
  8789. /**
  8790. * Indicates this prefix is obsolete and should't be used when user
  8791. * wants to generate all-prefixed properties
  8792. */
  8793. obsolete: false,
  8794. /**
  8795. * Returns prefixed CSS property name
  8796. * @param {String} name Unprefixed CSS property
  8797. */
  8798. transformName: function(name) {
  8799. return '-' + this.prefix + '-' + name;
  8800. },
  8801. /**
  8802. * List of unprefixed CSS properties that supported by
  8803. * current prefix. This list is used to generate all-prefixed property
  8804. * @returns {Array}
  8805. */
  8806. properties: function() {
  8807. return getProperties('css.' + this.prefix + 'Properties') || [];
  8808. },
  8809. /**
  8810. * Check if given property is supported by current prefix
  8811. * @param name
  8812. */
  8813. supports: function(name) {
  8814. return _.include(this.properties(), name);
  8815. }
  8816. };
  8817. /**
  8818. * List of registered one-character prefixes. Key is a one-character prefix,
  8819. * value is an <code>prefixObj</code> object
  8820. */
  8821. var vendorPrefixes = {};
  8822. var defaultValue = '${1};';
  8823. // XXX module preferences
  8824. var prefs = require('preferences');
  8825. prefs.define('css.valueSeparator', ': ',
  8826. 'Defines a symbol that should be placed between CSS property and '
  8827. + 'value when expanding CSS abbreviations.');
  8828. prefs.define('css.propertyEnd', ';',
  8829. 'Defines a symbol that should be placed at the end of CSS property '
  8830. + 'when expanding CSS abbreviations.');
  8831. prefs.define('stylus.valueSeparator', ' ',
  8832. 'Defines a symbol that should be placed between CSS property and '
  8833. + 'value when expanding CSS abbreviations in Stylus dialect.');
  8834. prefs.define('stylus.propertyEnd', '',
  8835. 'Defines a symbol that should be placed at the end of CSS property '
  8836. + 'when expanding CSS abbreviations in Stylus dialect.');
  8837. prefs.define('sass.propertyEnd', '',
  8838. 'Defines a symbol that should be placed at the end of CSS property '
  8839. + 'when expanding CSS abbreviations in SASS dialect.');
  8840. prefs.define('css.autoInsertVendorPrefixes', true,
  8841. 'Automatically generate vendor-prefixed copies of expanded CSS '
  8842. + 'property. By default, Emmet will generate vendor-prefixed '
  8843. + 'properties only when you put dash before abbreviation '
  8844. + '(e.g. <code>-bxsh</code>). With this option enabled, you don’t '
  8845. + 'need dashes before abbreviations: Emmet will produce '
  8846. + 'vendor-prefixed properties for you.');
  8847. var descTemplate = _.template('A comma-separated list of CSS properties that may have '
  8848. + '<code><%= vendor %></code> vendor prefix. This list is used to generate '
  8849. + 'a list of prefixed properties when expanding <code>-property</code> '
  8850. + 'abbreviations. Empty list means that all possible CSS values may '
  8851. + 'have <code><%= vendor %></code> prefix.');
  8852. var descAddonTemplate = _.template('A comma-separated list of <em>additional</em> CSS properties '
  8853. + 'for <code>css.<%= vendor %>Preperties</code> preference. '
  8854. + 'You should use this list if you want to add or remove a few CSS '
  8855. + 'properties to original set. To add a new property, simply write its name, '
  8856. + 'to remove it, precede property with hyphen.<br>'
  8857. + 'For example, to add <em>foo</em> property and remove <em>border-radius</em> one, '
  8858. + 'the preference value will look like this: <code>foo, -border-radius</code>.');
  8859. // properties list is created from cssFeatures.html file
  8860. var props = {
  8861. 'webkit': 'animation, animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-clip, background-composite, background-origin, background-size, border-fit, border-horizontal-spacing, border-image, border-vertical-spacing, box-align, box-direction, box-flex, box-flex-group, box-lines, box-ordinal-group, box-orient, box-pack, box-reflect, box-shadow, color-correction, column-break-after, column-break-before, column-break-inside, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-span, column-width, dashboard-region, font-smoothing, highlight, hyphenate-character, hyphenate-limit-after, hyphenate-limit-before, hyphens, line-box-contain, line-break, line-clamp, locale, margin-before-collapse, margin-after-collapse, marquee-direction, marquee-increment, marquee-repetition, marquee-style, mask-attachment, mask-box-image, mask-box-image-outset, mask-box-image-repeat, mask-box-image-slice, mask-box-image-source, mask-box-image-width, mask-clip, mask-composite, mask-image, mask-origin, mask-position, mask-repeat, mask-size, nbsp-mode, perspective, perspective-origin, rtl-ordering, text-combine, text-decorations-in-effect, text-emphasis-color, text-emphasis-position, text-emphasis-style, text-fill-color, text-orientation, text-security, text-stroke-color, text-stroke-width, transform, transition, transform-origin, transform-style, transition-delay, transition-duration, transition-property, transition-timing-function, user-drag, user-modify, user-select, writing-mode, svg-shadow, box-sizing, border-radius',
  8862. 'moz': 'animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-inline-policy, binding, border-bottom-colors, border-image, border-left-colors, border-right-colors, border-top-colors, box-align, box-direction, box-flex, box-ordinal-group, box-orient, box-pack, box-shadow, box-sizing, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-width, float-edge, font-feature-settings, font-language-override, force-broken-image-icon, hyphens, image-region, orient, outline-radius-bottomleft, outline-radius-bottomright, outline-radius-topleft, outline-radius-topright, perspective, perspective-origin, stack-sizing, tab-size, text-blink, text-decoration-color, text-decoration-line, text-decoration-style, text-size-adjust, transform, transform-origin, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-focus, user-input, user-modify, user-select, window-shadow, background-clip, border-radius',
  8863. 'ms': 'accelerator, backface-visibility, background-position-x, background-position-y, behavior, block-progression, box-align, box-direction, box-flex, box-line-progression, box-lines, box-ordinal-group, box-orient, box-pack, content-zoom-boundary, content-zoom-boundary-max, content-zoom-boundary-min, content-zoom-chaining, content-zoom-snap, content-zoom-snap-points, content-zoom-snap-type, content-zooming, filter, flow-from, flow-into, font-feature-settings, grid-column, grid-column-align, grid-column-span, grid-columns, grid-layer, grid-row, grid-row-align, grid-row-span, grid-rows, high-contrast-adjust, hyphenate-limit-chars, hyphenate-limit-lines, hyphenate-limit-zone, hyphens, ime-mode, interpolation-mode, layout-flow, layout-grid, layout-grid-char, layout-grid-line, layout-grid-mode, layout-grid-type, line-break, overflow-style, perspective, perspective-origin, perspective-origin-x, perspective-origin-y, scroll-boundary, scroll-boundary-bottom, scroll-boundary-left, scroll-boundary-right, scroll-boundary-top, scroll-chaining, scroll-rails, scroll-snap-points-x, scroll-snap-points-y, scroll-snap-type, scroll-snap-x, scroll-snap-y, scrollbar-arrow-color, scrollbar-base-color, scrollbar-darkshadow-color, scrollbar-face-color, scrollbar-highlight-color, scrollbar-shadow-color, scrollbar-track-color, text-align-last, text-autospace, text-justify, text-kashida-space, text-overflow, text-size-adjust, text-underline-position, touch-action, transform, transform-origin, transform-origin-x, transform-origin-y, transform-origin-z, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-select, word-break, word-wrap, wrap-flow, wrap-margin, wrap-through, writing-mode',
  8864. 'o': 'dashboard-region, animation, animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, border-image, link, link-source, object-fit, object-position, tab-size, table-baseline, transform, transform-origin, transition, transition-delay, transition-duration, transition-property, transition-timing-function, accesskey, input-format, input-required, marquee-dir, marquee-loop, marquee-speed, marquee-style'
  8865. };
  8866. _.each(props, function(v, k) {
  8867. prefs.define('css.' + k + 'Properties', v, descTemplate({vendor: k}));
  8868. prefs.define('css.' + k + 'PropertiesAddon', '', descAddonTemplate({vendor: k}));
  8869. });
  8870. prefs.define('css.unitlessProperties', 'z-index, line-height, opacity, font-weight, zoom',
  8871. 'The list of properties whose values ​​must not contain units.');
  8872. prefs.define('css.intUnit', 'px', 'Default unit for integer values');
  8873. prefs.define('css.floatUnit', 'em', 'Default unit for float values');
  8874. prefs.define('css.keywords', 'auto, inherit',
  8875. 'A comma-separated list of valid keywords that can be used in CSS abbreviations.');
  8876. prefs.define('css.keywordAliases', 'a:auto, i:inherit, s:solid, da:dashed, do:dotted, t:transparent',
  8877. 'A comma-separated list of keyword aliases, used in CSS abbreviation. '
  8878. + 'Each alias should be defined as <code>alias:keyword_name</code>.');
  8879. prefs.define('css.unitAliases', 'e:em, p:%, x:ex, r:rem',
  8880. 'A comma-separated list of unit aliases, used in CSS abbreviation. '
  8881. + 'Each alias should be defined as <code>alias:unit_value</code>.');
  8882. prefs.define('css.color.short', true,
  8883. 'Should color values like <code>#ffffff</code> be shortened to '
  8884. + '<code>#fff</code> after abbreviation with color was expanded.');
  8885. prefs.define('css.color.case', 'keep',
  8886. 'Letter case of color values generated by abbreviations with color '
  8887. + '(like <code>c#0</code>). Possible values are <code>upper</code>, '
  8888. + '<code>lower</code> and <code>keep</code>.');
  8889. prefs.define('css.fuzzySearch', true,
  8890. 'Enable fuzzy search among CSS snippet names. When enabled, every '
  8891. + '<em>unknown</em> snippet will be scored against available snippet '
  8892. + 'names (not values or CSS properties!). The match with best score '
  8893. + 'will be used to resolve snippet value. For example, with this '
  8894. + 'preference enabled, the following abbreviations are equal: '
  8895. + '<code>ov:h</code> == <code>ov-h</code> == <code>o-h</code> == '
  8896. + '<code>oh</code>');
  8897. prefs.define('css.fuzzySearchMinScore', 0.3,
  8898. 'The minium score (from 0 to 1) that fuzzy-matched abbreviation should '
  8899. + 'achive. Lower values may produce many false-positive matches, '
  8900. + 'higher values may reduce possible matches.');
  8901. prefs.define('css.alignVendor', false,
  8902. 'If set to <code>true</code>, all generated vendor-prefixed properties '
  8903. + 'will be aligned by real property name.');
  8904. function isNumeric(ch) {
  8905. var code = ch && ch.charCodeAt(0);
  8906. return (ch && ch == '.' || (code > 47 && code < 58));
  8907. }
  8908. /**
  8909. * Check if provided snippet contains only one CSS property and value.
  8910. * @param {String} snippet
  8911. * @returns {Boolean}
  8912. */
  8913. function isSingleProperty(snippet) {
  8914. var utils = require('utils');
  8915. snippet = utils.trim(snippet);
  8916. // check if it doesn't contain a comment and a newline
  8917. if (~snippet.indexOf('/*') || /[\n\r]/.test(snippet)) {
  8918. return false;
  8919. }
  8920. // check if it's a valid snippet definition
  8921. if (!/^[a-z0-9\-]+\s*\:/i.test(snippet)) {
  8922. return false;
  8923. }
  8924. snippet = require('tabStops').processText(snippet, {
  8925. replaceCarets: true,
  8926. tabstop: function() {
  8927. return 'value';
  8928. }
  8929. });
  8930. return snippet.split(':').length == 2;
  8931. }
  8932. /**
  8933. * Normalizes abbreviated value to final CSS one
  8934. * @param {String} value
  8935. * @returns {String}
  8936. */
  8937. function normalizeValue(value) {
  8938. if (value.charAt(0) == '-' && !/^\-[\.\d]/.test(value)) {
  8939. value = value.replace(/^\-+/, '');
  8940. }
  8941. if (value.charAt(0) == '#') {
  8942. return normalizeHexColor(value);
  8943. }
  8944. return getKeyword(value);
  8945. }
  8946. function normalizeHexColor(value) {
  8947. var hex = value.replace(/^#+/, '') || '0';
  8948. if (hex.toLowerCase() == 't') {
  8949. return 'transparent';
  8950. }
  8951. var repeat = require('utils').repeatString;
  8952. var color = null;
  8953. switch (hex.length) {
  8954. case 1:
  8955. color = repeat(hex, 6);
  8956. break;
  8957. case 2:
  8958. color = repeat(hex, 3);
  8959. break;
  8960. case 3:
  8961. color = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
  8962. break;
  8963. case 4:
  8964. color = hex + hex.substr(0, 2);
  8965. break;
  8966. case 5:
  8967. color = hex + hex.charAt(0);
  8968. break;
  8969. default:
  8970. color = hex.substr(0, 6);
  8971. }
  8972. // color must be shortened?
  8973. if (prefs.get('css.color.short')) {
  8974. var p = color.split('');
  8975. if (p[0] == p[1] && p[2] == p[3] && p[4] == p[5]) {
  8976. color = p[0] + p[2] + p[4];
  8977. }
  8978. }
  8979. // should transform case?
  8980. switch (prefs.get('css.color.case')) {
  8981. case 'upper':
  8982. color = color.toUpperCase();
  8983. break;
  8984. case 'lower':
  8985. color = color.toLowerCase();
  8986. break;
  8987. }
  8988. return '#' + color;
  8989. }
  8990. function getKeyword(name) {
  8991. var aliases = prefs.getDict('css.keywordAliases');
  8992. return name in aliases ? aliases[name] : name;
  8993. }
  8994. function getUnit(name) {
  8995. var aliases = prefs.getDict('css.unitAliases');
  8996. return name in aliases ? aliases[name] : name;
  8997. }
  8998. function isValidKeyword(keyword) {
  8999. return _.include(prefs.getArray('css.keywords'), getKeyword(keyword));
  9000. }
  9001. /**
  9002. * Check if passed CSS property support specified vendor prefix
  9003. * @param {String} property
  9004. * @param {String} prefix
  9005. */
  9006. function hasPrefix(property, prefix) {
  9007. var info = vendorPrefixes[prefix];
  9008. if (!info)
  9009. info = _.find(vendorPrefixes, function(data) {
  9010. return data.prefix == prefix;
  9011. });
  9012. return info && info.supports(property);
  9013. }
  9014. /**
  9015. * Search for a list of supported prefixes for CSS property. This list
  9016. * is used to generate all-prefixed snippet
  9017. * @param {String} property CSS property name
  9018. * @returns {Array}
  9019. */
  9020. function findPrefixes(property, noAutofill) {
  9021. var result = [];
  9022. _.each(vendorPrefixes, function(obj, prefix) {
  9023. if (hasPrefix(property, prefix)) {
  9024. result.push(prefix);
  9025. }
  9026. });
  9027. if (!result.length && !noAutofill) {
  9028. // add all non-obsolete prefixes
  9029. _.each(vendorPrefixes, function(obj, prefix) {
  9030. if (!obj.obsolete)
  9031. result.push(prefix);
  9032. });
  9033. }
  9034. return result;
  9035. }
  9036. function addPrefix(name, obj) {
  9037. if (_.isString(obj))
  9038. obj = {prefix: obj};
  9039. vendorPrefixes[name] = _.extend({}, prefixObj, obj);
  9040. }
  9041. function getSyntaxPreference(name, syntax) {
  9042. if (syntax) {
  9043. var val = prefs.get(syntax + '.' + name);
  9044. if (!_.isUndefined(val))
  9045. return val;
  9046. }
  9047. return prefs.get('css.' + name);
  9048. }
  9049. /**
  9050. * Format CSS property according to current syntax dialect
  9051. * @param {String} property
  9052. * @param {String} syntax
  9053. * @returns {String}
  9054. */
  9055. function formatProperty(property, syntax) {
  9056. var ix = property.indexOf(':');
  9057. property = property.substring(0, ix).replace(/\s+$/, '')
  9058. + getSyntaxPreference('valueSeparator', syntax)
  9059. + require('utils').trim(property.substring(ix + 1));
  9060. return property.replace(/\s*;\s*$/, getSyntaxPreference('propertyEnd', syntax));
  9061. }
  9062. /**
  9063. * Transforms snippet value if required. For example, this transformation
  9064. * may add <i>!important</i> declaration to CSS property
  9065. * @param {String} snippet
  9066. * @param {Boolean} isImportant
  9067. * @returns {String}
  9068. */
  9069. function transformSnippet(snippet, isImportant, syntax) {
  9070. if (!_.isString(snippet))
  9071. snippet = snippet.data;
  9072. if (!isSingleProperty(snippet))
  9073. return snippet;
  9074. if (isImportant) {
  9075. if (~snippet.indexOf(';')) {
  9076. snippet = snippet.split(';').join(' !important;');
  9077. } else {
  9078. snippet += ' !important';
  9079. }
  9080. }
  9081. return formatProperty(snippet, syntax);
  9082. }
  9083. /**
  9084. * Helper function that parses comma-separated list of elements into array
  9085. * @param {String} list
  9086. * @returns {Array}
  9087. */
  9088. function parseList(list) {
  9089. var result = _.map((list || '').split(','), require('utils').trim);
  9090. return result.length ? result : null;
  9091. }
  9092. function getProperties(key) {
  9093. var list = prefs.getArray(key);
  9094. _.each(prefs.getArray(key + 'Addon'), function(prop) {
  9095. if (prop.charAt(0) == '-') {
  9096. list = _.without(list, prop.substr(1));
  9097. } else {
  9098. if (prop.charAt(0) == '+')
  9099. prop = prop.substr(1);
  9100. list.push(prop);
  9101. }
  9102. });
  9103. return list;
  9104. }
  9105. // TODO refactor, this looks awkward now
  9106. addPrefix('w', {
  9107. prefix: 'webkit'
  9108. });
  9109. addPrefix('m', {
  9110. prefix: 'moz'
  9111. });
  9112. addPrefix('s', {
  9113. prefix: 'ms'
  9114. });
  9115. addPrefix('o', {
  9116. prefix: 'o'
  9117. });
  9118. // I think nobody uses it
  9119. // addPrefix('k', {
  9120. // prefix: 'khtml',
  9121. // obsolete: true
  9122. // });
  9123. var cssSyntaxes = ['css', 'less', 'sass', 'scss', 'stylus'];
  9124. /**
  9125. * XXX register resolver
  9126. * @param {TreeNode} node
  9127. * @param {String} syntax
  9128. */
  9129. require('resources').addResolver(function(node, syntax) {
  9130. if (_.include(cssSyntaxes, syntax) && node.isElement()) {
  9131. return module.expandToSnippet(node.abbreviation, syntax);
  9132. }
  9133. return null;
  9134. });
  9135. var ea = require('expandAbbreviation');
  9136. /**
  9137. * For CSS-like syntaxes, we need to handle a special use case. Some editors
  9138. * (like Sublime Text 2) may insert semicolons automatically when user types
  9139. * abbreviation. After expansion, user receives a double semicolon. This
  9140. * handler automatically removes semicolon from generated content in such cases.
  9141. * @param {IEmmetEditor} editor
  9142. * @param {String} syntax
  9143. * @param {String} profile
  9144. */
  9145. ea.addHandler(function(editor, syntax, profile) {
  9146. if (!_.include(cssSyntaxes, syntax)) {
  9147. return false;
  9148. }
  9149. var caretPos = editor.getSelectionRange().end;
  9150. var abbr = ea.findAbbreviation(editor);
  9151. if (abbr) {
  9152. var content = emmet.expandAbbreviation(abbr, syntax, profile);
  9153. if (content) {
  9154. var replaceFrom = caretPos - abbr.length;
  9155. var replaceTo = caretPos;
  9156. if (editor.getContent().charAt(caretPos) == ';' && content.charAt(content.length - 1) == ';') {
  9157. replaceTo++;
  9158. }
  9159. editor.replaceContent(content, replaceFrom, replaceTo);
  9160. return true;
  9161. }
  9162. }
  9163. return false;
  9164. });
  9165. return module = {
  9166. /**
  9167. * Adds vendor prefix
  9168. * @param {String} name One-character prefix name
  9169. * @param {Object} obj Object describing vendor prefix
  9170. * @memberOf cssResolver
  9171. */
  9172. addPrefix: addPrefix,
  9173. /**
  9174. * Check if passed CSS property supports specified vendor prefix
  9175. * @param {String} property
  9176. * @param {String} prefix
  9177. */
  9178. supportsPrefix: hasPrefix,
  9179. /**
  9180. * Returns prefixed version of passed CSS property, only if this
  9181. * property supports such prefix
  9182. * @param {String} property
  9183. * @param {String} prefix
  9184. * @returns
  9185. */
  9186. prefixed: function(property, prefix) {
  9187. return hasPrefix(property, prefix)
  9188. ? '-' + prefix + '-' + property
  9189. : property;
  9190. },
  9191. /**
  9192. * Returns list of all registered vendor prefixes
  9193. * @returns {Array}
  9194. */
  9195. listPrefixes: function() {
  9196. return _.map(vendorPrefixes, function(obj) {
  9197. return obj.prefix;
  9198. });
  9199. },
  9200. /**
  9201. * Returns object describing vendor prefix
  9202. * @param {String} name
  9203. * @returns {Object}
  9204. */
  9205. getPrefix: function(name) {
  9206. return vendorPrefixes[name];
  9207. },
  9208. /**
  9209. * Removes prefix object
  9210. * @param {String} name
  9211. */
  9212. removePrefix: function(name) {
  9213. if (name in vendorPrefixes)
  9214. delete vendorPrefixes[name];
  9215. },
  9216. /**
  9217. * Extract vendor prefixes from abbreviation
  9218. * @param {String} abbr
  9219. * @returns {Object} Object containing array of prefixes and clean
  9220. * abbreviation name
  9221. */
  9222. extractPrefixes: function(abbr) {
  9223. if (abbr.charAt(0) != '-') {
  9224. return {
  9225. property: abbr,
  9226. prefixes: null
  9227. };
  9228. }
  9229. // abbreviation may either contain sequence of one-character prefixes
  9230. // or just dash, meaning that user wants to produce all possible
  9231. // prefixed properties
  9232. var i = 1, il = abbr.length, ch;
  9233. var prefixes = [];
  9234. while (i < il) {
  9235. ch = abbr.charAt(i);
  9236. if (ch == '-') {
  9237. // end-sequence character found, stop searching
  9238. i++;
  9239. break;
  9240. }
  9241. if (ch in vendorPrefixes) {
  9242. prefixes.push(ch);
  9243. } else {
  9244. // no prefix found, meaning user want to produce all
  9245. // vendor-prefixed properties
  9246. prefixes.length = 0;
  9247. i = 1;
  9248. break;
  9249. }
  9250. i++;
  9251. }
  9252. // reached end of abbreviation and no property name left
  9253. if (i == il -1) {
  9254. i = 1;
  9255. prefixes.length = 1;
  9256. }
  9257. return {
  9258. property: abbr.substring(i),
  9259. prefixes: prefixes.length ? prefixes : 'all'
  9260. };
  9261. },
  9262. /**
  9263. * Search for value substring in abbreviation
  9264. * @param {String} abbr
  9265. * @returns {String} Value substring
  9266. */
  9267. findValuesInAbbreviation: function(abbr, syntax) {
  9268. syntax = syntax || 'css';
  9269. var i = 0, il = abbr.length, value = '', ch;
  9270. while (i < il) {
  9271. ch = abbr.charAt(i);
  9272. if (isNumeric(ch) || ch == '#' || (ch == '-' && isNumeric(abbr.charAt(i + 1)))) {
  9273. value = abbr.substring(i);
  9274. break;
  9275. }
  9276. i++;
  9277. }
  9278. // try to find keywords in abbreviation
  9279. var property = abbr.substring(0, abbr.length - value.length);
  9280. var res = require('resources');
  9281. var keywords = [];
  9282. // try to extract some commonly-used properties
  9283. while (~property.indexOf('-') && !res.findSnippet(syntax, property)) {
  9284. var parts = property.split('-');
  9285. var lastPart = parts.pop();
  9286. if (!isValidKeyword(lastPart)) {
  9287. break;
  9288. }
  9289. keywords.unshift(lastPart);
  9290. property = parts.join('-');
  9291. }
  9292. return keywords.join('-') + value;
  9293. },
  9294. parseValues: function(str) {
  9295. /** @type StringStream */
  9296. var stream = require('stringStream').create(str);
  9297. var values = [];
  9298. var ch = null;
  9299. while (ch = stream.next()) {
  9300. if (ch == '#') {
  9301. stream.match(/^t|[0-9a-f]+/i, true);
  9302. values.push(stream.current());
  9303. } else if (ch == '-') {
  9304. if (isValidKeyword(_.last(values)) ||
  9305. ( stream.start && isNumeric(str.charAt(stream.start - 1)) )
  9306. ) {
  9307. stream.start = stream.pos;
  9308. }
  9309. stream.match(/^\-?[0-9]*(\.[0-9]+)?[a-z%\.]*/, true);
  9310. values.push(stream.current());
  9311. } else {
  9312. stream.match(/^[0-9]*(\.[0-9]*)?[a-z%]*/, true);
  9313. values.push(stream.current());
  9314. }
  9315. stream.start = stream.pos;
  9316. }
  9317. return _.map(_.compact(values), normalizeValue);
  9318. },
  9319. /**
  9320. * Extracts values from abbreviation
  9321. * @param {String} abbr
  9322. * @returns {Object} Object containing array of values and clean
  9323. * abbreviation name
  9324. */
  9325. extractValues: function(abbr) {
  9326. // search for value start
  9327. var abbrValues = this.findValuesInAbbreviation(abbr);
  9328. if (!abbrValues) {
  9329. return {
  9330. property: abbr,
  9331. values: null
  9332. };
  9333. }
  9334. return {
  9335. property: abbr.substring(0, abbr.length - abbrValues.length).replace(/-$/, ''),
  9336. values: this.parseValues(abbrValues)
  9337. };
  9338. },
  9339. /**
  9340. * Normalizes value, defined in abbreviation.
  9341. * @param {String} value
  9342. * @param {String} property
  9343. * @returns {String}
  9344. */
  9345. normalizeValue: function(value, property) {
  9346. property = (property || '').toLowerCase();
  9347. var unitlessProps = prefs.getArray('css.unitlessProperties');
  9348. return value.replace(/^(\-?[0-9\.]+)([a-z]*)$/, function(str, val, unit) {
  9349. if (!unit && (val == '0' || _.include(unitlessProps, property)))
  9350. return val;
  9351. if (!unit)
  9352. return val.replace(/\.$/, '') + prefs.get(~val.indexOf('.') ? 'css.floatUnit' : 'css.intUnit');
  9353. return val + getUnit(unit);
  9354. });
  9355. },
  9356. /**
  9357. * Expands abbreviation into a snippet
  9358. * @param {String} abbr Abbreviation name to expand
  9359. * @param {String} value Abbreviation value
  9360. * @param {String} syntax Currect syntax or dialect. Default is 'css'
  9361. * @returns {Object} Array of CSS properties and values or predefined
  9362. * snippet (string or element)
  9363. */
  9364. expand: function(abbr, value, syntax) {
  9365. syntax = syntax || 'css';
  9366. var resources = require('resources');
  9367. var autoInsertPrefixes = prefs.get('css.autoInsertVendorPrefixes');
  9368. // check if snippet should be transformed to !important
  9369. var isImportant;
  9370. if (isImportant = /^(.+)\!$/.test(abbr)) {
  9371. abbr = RegExp.$1;
  9372. }
  9373. // check if we have abbreviated resource
  9374. var snippet = resources.findSnippet(syntax, abbr);
  9375. if (snippet && !autoInsertPrefixes) {
  9376. return transformSnippet(snippet, isImportant, syntax);
  9377. }
  9378. // no abbreviated resource, parse abbreviation
  9379. var prefixData = this.extractPrefixes(abbr);
  9380. var valuesData = this.extractValues(prefixData.property);
  9381. var abbrData = _.extend(prefixData, valuesData);
  9382. if (!snippet) {
  9383. snippet = resources.findSnippet(syntax, abbrData.property);
  9384. } else {
  9385. abbrData.values = null;
  9386. }
  9387. if (!snippet && prefs.get('css.fuzzySearch')) {
  9388. // let’s try fuzzy search
  9389. snippet = resources.fuzzyFindSnippet(syntax, abbrData.property, parseFloat(prefs.get('css.fuzzySearchMinScore')));
  9390. }
  9391. if (!snippet) {
  9392. snippet = abbrData.property + ':' + defaultValue;
  9393. } else if (!_.isString(snippet)) {
  9394. snippet = snippet.data;
  9395. }
  9396. if (!isSingleProperty(snippet)) {
  9397. return snippet;
  9398. }
  9399. var snippetObj = this.splitSnippet(snippet);
  9400. var result = [];
  9401. if (!value && abbrData.values) {
  9402. value = _.map(abbrData.values, function(val) {
  9403. return this.normalizeValue(val, snippetObj.name);
  9404. }, this).join(' ') + ';';
  9405. }
  9406. snippetObj.value = value || snippetObj.value;
  9407. var prefixes = abbrData.prefixes == 'all' || (!abbrData.prefixes && autoInsertPrefixes)
  9408. ? findPrefixes(snippetObj.name, autoInsertPrefixes && abbrData.prefixes != 'all')
  9409. : abbrData.prefixes;
  9410. var names = [], propName;
  9411. _.each(prefixes, function(p) {
  9412. if (p in vendorPrefixes) {
  9413. propName = vendorPrefixes[p].transformName(snippetObj.name);
  9414. names.push(propName);
  9415. result.push(transformSnippet(propName + ':' + snippetObj.value,
  9416. isImportant, syntax));
  9417. }
  9418. });
  9419. // put the original property
  9420. result.push(transformSnippet(snippetObj.name + ':' + snippetObj.value, isImportant, syntax));
  9421. names.push(snippetObj.name);
  9422. if (prefs.get('css.alignVendor')) {
  9423. var pads = require('utils').getStringsPads(names);
  9424. result = _.map(result, function(prop, i) {
  9425. return pads[i] + prop;
  9426. });
  9427. }
  9428. return result;
  9429. },
  9430. /**
  9431. * Same as <code>expand</code> method but transforms output into
  9432. * Emmet snippet
  9433. * @param {String} abbr
  9434. * @param {String} syntax
  9435. * @returns {String}
  9436. */
  9437. expandToSnippet: function(abbr, syntax) {
  9438. var snippet = this.expand(abbr, null, syntax);
  9439. if (_.isArray(snippet)) {
  9440. return snippet.join('\n');
  9441. }
  9442. if (!_.isString(snippet))
  9443. return snippet.data;
  9444. return String(snippet);
  9445. },
  9446. /**
  9447. * Split snippet into a CSS property-value pair
  9448. * @param {String} snippet
  9449. */
  9450. splitSnippet: function(snippet) {
  9451. var utils = require('utils');
  9452. snippet = utils.trim(snippet);
  9453. if (snippet.indexOf(':') == -1) {
  9454. return {
  9455. name: snippet,
  9456. value: defaultValue
  9457. };
  9458. }
  9459. var pair = snippet.split(':');
  9460. return {
  9461. name: utils.trim(pair.shift()),
  9462. // replace ${0} tabstop to produce valid vendor-prefixed values
  9463. // where possible
  9464. value: utils.trim(pair.join(':')).replace(/^(\$\{0\}|\$0)(\s*;?)$/, '${1}$2')
  9465. };
  9466. },
  9467. getSyntaxPreference: getSyntaxPreference,
  9468. transformSnippet: transformSnippet
  9469. };
  9470. });
  9471. /**
  9472. * 'Expand Abbreviation' handler that parses gradient definition from under
  9473. * cursor and updates CSS rule with vendor-prefixed values.
  9474. *
  9475. * @memberOf __cssGradientHandlerDefine
  9476. * @param {Function} require
  9477. * @param {Underscore} _
  9478. */
  9479. emmet.define('cssGradient', function(require, _) {
  9480. var defaultLinearDirections = ['top', 'to bottom', '0deg'];
  9481. /** Back-reference to current module */
  9482. var module = null;
  9483. var cssSyntaxes = ['css', 'less', 'sass', 'scss', 'stylus', 'styl'];
  9484. var reDeg = /\d+deg/i;
  9485. var reKeyword = /top|bottom|left|right/i;
  9486. // XXX define preferences
  9487. /** @type preferences */
  9488. var prefs = require('preferences');
  9489. prefs.define('css.gradient.prefixes', 'webkit, moz, o',
  9490. 'A comma-separated list of vendor-prefixes for which values should '
  9491. + 'be generated.');
  9492. prefs.define('css.gradient.oldWebkit', true,
  9493. 'Generate gradient definition for old Webkit implementations');
  9494. prefs.define('css.gradient.omitDefaultDirection', true,
  9495. 'Do not output default direction definition in generated gradients.');
  9496. prefs.define('css.gradient.defaultProperty', 'background-image',
  9497. 'When gradient expanded outside CSS value context, it will produce '
  9498. + 'properties with this name.');
  9499. prefs.define('css.gradient.fallback', false,
  9500. 'With this option enabled, CSS gradient generator will produce '
  9501. + '<code>background-color</code> property with gradient first color '
  9502. + 'as fallback for old browsers.');
  9503. function normalizeSpace(str) {
  9504. return require('utils').trim(str).replace(/\s+/g, ' ');
  9505. }
  9506. /**
  9507. * Parses linear gradient definition
  9508. * @param {String}
  9509. */
  9510. function parseLinearGradient(gradient) {
  9511. var direction = defaultLinearDirections[0];
  9512. // extract tokens
  9513. /** @type StringStream */
  9514. var stream = require('stringStream').create(require('utils').trim(gradient));
  9515. var colorStops = [], ch;
  9516. while (ch = stream.next()) {
  9517. if (stream.peek() == ',') {
  9518. colorStops.push(stream.current());
  9519. stream.next();
  9520. stream.eatSpace();
  9521. stream.start = stream.pos;
  9522. } else if (ch == '(') { // color definition, like 'rgb(0,0,0)'
  9523. stream.skipTo(')');
  9524. }
  9525. }
  9526. // add last token
  9527. colorStops.push(stream.current());
  9528. colorStops = _.compact(_.map(colorStops, normalizeSpace));
  9529. if (!colorStops.length)
  9530. return null;
  9531. // let's see if the first color stop is actually a direction
  9532. if (reDeg.test(colorStops[0]) || reKeyword.test(colorStops[0])) {
  9533. direction = colorStops.shift();
  9534. }
  9535. return {
  9536. type: 'linear',
  9537. direction: direction,
  9538. colorStops: _.map(colorStops, parseColorStop)
  9539. };
  9540. }
  9541. /**
  9542. * Parses color stop definition
  9543. * @param {String} colorStop
  9544. * @returns {Object}
  9545. */
  9546. function parseColorStop(colorStop) {
  9547. colorStop = normalizeSpace(colorStop);
  9548. // find color declaration
  9549. // first, try complex color declaration, like rgb(0,0,0)
  9550. var color = null;
  9551. colorStop = colorStop.replace(/^(\w+\(.+?\))\s*/, function(str, c) {
  9552. color = c;
  9553. return '';
  9554. });
  9555. if (!color) {
  9556. // try simple declaration, like yellow, #fco, #ffffff, etc.
  9557. var parts = colorStop.split(' ');
  9558. color = parts[0];
  9559. colorStop = parts[1] || '';
  9560. }
  9561. var result = {
  9562. color: color
  9563. };
  9564. if (colorStop) {
  9565. // there's position in color stop definition
  9566. colorStop.replace(/^(\-?[\d\.]+)([a-z%]+)?$/, function(str, pos, unit) {
  9567. result.position = pos;
  9568. if (~pos.indexOf('.')) {
  9569. unit = '';
  9570. } else if (!unit) {
  9571. unit = '%';
  9572. }
  9573. if (unit)
  9574. result.unit = unit;
  9575. });
  9576. }
  9577. return result;
  9578. }
  9579. /**
  9580. * Resolves property name (abbreviation): searches for snippet definition in
  9581. * 'resources' and returns new name of matched property
  9582. */
  9583. function resolvePropertyName(name, syntax) {
  9584. var res = require('resources');
  9585. var prefs = require('preferences');
  9586. var snippet = res.findSnippet(syntax, name);
  9587. if (!snippet && prefs.get('css.fuzzySearch')) {
  9588. snippet = res.fuzzyFindSnippet(syntax, name,
  9589. parseFloat(prefs.get('css.fuzzySearchMinScore')));
  9590. }
  9591. if (snippet) {
  9592. if (!_.isString(snippet)) {
  9593. snippet = snippet.data;
  9594. }
  9595. return require('cssResolver').splitSnippet(snippet).name;
  9596. }
  9597. }
  9598. /**
  9599. * Fills-out implied positions in color-stops. This function is useful for
  9600. * old Webkit gradient definitions
  9601. */
  9602. function fillImpliedPositions(colorStops) {
  9603. var from = 0;
  9604. _.each(colorStops, function(cs, i) {
  9605. // make sure that first and last positions are defined
  9606. if (!i)
  9607. return cs.position = cs.position || 0;
  9608. if (i == colorStops.length - 1 && !('position' in cs))
  9609. cs.position = 1;
  9610. if ('position' in cs) {
  9611. var start = colorStops[from].position || 0;
  9612. var step = (cs.position - start) / (i - from);
  9613. _.each(colorStops.slice(from, i), function(cs2, j) {
  9614. cs2.position = start + step * j;
  9615. });
  9616. from = i;
  9617. }
  9618. });
  9619. }
  9620. /**
  9621. * Returns textual version of direction expressed in degrees
  9622. * @param {String} direction
  9623. * @returns {String}
  9624. */
  9625. function textualDirection(direction) {
  9626. var angle = parseFloat(direction);
  9627. if(!_.isNaN(angle)) {
  9628. switch(angle % 360) {
  9629. case 0: return 'left';
  9630. case 90: return 'bottom';
  9631. case 180: return 'right';
  9632. case 240: return 'top';
  9633. }
  9634. }
  9635. return direction;
  9636. }
  9637. /**
  9638. * Creates direction definition for old Webkit gradients
  9639. * @param {String} direction
  9640. * @returns {String}
  9641. */
  9642. function oldWebkitDirection(direction) {
  9643. direction = textualDirection(direction);
  9644. if(reDeg.test(direction))
  9645. throw "The direction is an angle that can’t be converted.";
  9646. var v = function(pos) {
  9647. return ~direction.indexOf(pos) ? '100%' : '0';
  9648. };
  9649. return v('right') + ' ' + v('bottom') + ', ' + v('left') + ' ' + v('top');
  9650. }
  9651. function getPrefixedNames(name) {
  9652. var prefixes = prefs.getArray('css.gradient.prefixes');
  9653. var names = _.map(prefixes, function(p) {
  9654. return '-' + p + '-' + name;
  9655. });
  9656. names.push(name);
  9657. return names;
  9658. }
  9659. /**
  9660. * Returns list of CSS properties with gradient
  9661. * @param {Object} gradient
  9662. * @param {String} propertyName Original CSS property name
  9663. * @returns {Array}
  9664. */
  9665. function getPropertiesForGradient(gradient, propertyName) {
  9666. var props = [];
  9667. var css = require('cssResolver');
  9668. if (prefs.get('css.gradient.fallback') && ~propertyName.toLowerCase().indexOf('background')) {
  9669. props.push({
  9670. name: 'background-color',
  9671. value: '${1:' + gradient.colorStops[0].color + '}'
  9672. });
  9673. }
  9674. _.each(prefs.getArray('css.gradient.prefixes'), function(prefix) {
  9675. var name = css.prefixed(propertyName, prefix);
  9676. if (prefix == 'webkit' && prefs.get('css.gradient.oldWebkit')) {
  9677. try {
  9678. props.push({
  9679. name: name,
  9680. value: module.oldWebkitLinearGradient(gradient)
  9681. });
  9682. } catch(e) {}
  9683. }
  9684. props.push({
  9685. name: name,
  9686. value: module.toString(gradient, prefix)
  9687. });
  9688. });
  9689. return props.sort(function(a, b) {
  9690. return b.name.length - a.name.length;
  9691. });
  9692. }
  9693. /**
  9694. * Pastes gradient definition into CSS rule with correct vendor-prefixes
  9695. * @param {EditElement} property Matched CSS property
  9696. * @param {Object} gradient Parsed gradient
  9697. * @param {Range} valueRange If passed, only this range within property
  9698. * value will be replaced with gradient. Otherwise, full value will be
  9699. * replaced
  9700. */
  9701. function pasteGradient(property, gradient, valueRange) {
  9702. var rule = property.parent;
  9703. var utils = require('utils');
  9704. var alignVendor = require('preferences').get('css.alignVendor');
  9705. // we may have aligned gradient definitions: find the smallest value
  9706. // separator
  9707. var sep = property.styleSeparator;
  9708. var before = property.styleBefore;
  9709. // first, remove all properties within CSS rule with the same name and
  9710. // gradient definition
  9711. _.each(rule.getAll(getPrefixedNames(property.name())), function(item) {
  9712. if (item != property && /gradient/i.test(item.value())) {
  9713. if (item.styleSeparator.length < sep.length) {
  9714. sep = item.styleSeparator;
  9715. }
  9716. if (item.styleBefore.length < before.length) {
  9717. before = item.styleBefore;
  9718. }
  9719. rule.remove(item);
  9720. }
  9721. });
  9722. if (alignVendor) {
  9723. // update prefix
  9724. if (before != property.styleBefore) {
  9725. var fullRange = property.fullRange();
  9726. rule._updateSource(before, fullRange.start, fullRange.start + property.styleBefore.length);
  9727. property.styleBefore = before;
  9728. }
  9729. // update separator value
  9730. if (sep != property.styleSeparator) {
  9731. rule._updateSource(sep, property.nameRange().end, property.valueRange().start);
  9732. property.styleSeparator = sep;
  9733. }
  9734. }
  9735. var value = property.value();
  9736. if (!valueRange)
  9737. valueRange = require('range').create(0, property.value());
  9738. var val = function(v) {
  9739. return utils.replaceSubstring(value, v, valueRange);
  9740. };
  9741. // put vanilla-clean gradient definition into current rule
  9742. property.value(val(module.toString(gradient)) + '${2}');
  9743. // create list of properties to insert
  9744. var propsToInsert = getPropertiesForGradient(gradient, property.name());
  9745. // align prefixed values
  9746. if (alignVendor) {
  9747. var values = _.pluck(propsToInsert, 'value');
  9748. var names = _.pluck(propsToInsert, 'name');
  9749. values.push(property.value());
  9750. names.push(property.name());
  9751. var valuePads = utils.getStringsPads(_.map(values, function(v) {
  9752. return v.substring(0, v.indexOf('('));
  9753. }));
  9754. var namePads = utils.getStringsPads(names);
  9755. property.name(_.last(namePads) + property.name());
  9756. _.each(propsToInsert, function(prop, i) {
  9757. prop.name = namePads[i] + prop.name;
  9758. prop.value = valuePads[i] + prop.value;
  9759. });
  9760. property.value(_.last(valuePads) + property.value());
  9761. }
  9762. // put vendor-prefixed definitions before current rule
  9763. _.each(propsToInsert, function(prop) {
  9764. rule.add(prop.name, prop.value, rule.indexOf(property));
  9765. });
  9766. }
  9767. /**
  9768. * Search for gradient definition inside CSS property value
  9769. */
  9770. function findGradient(cssProp) {
  9771. var value = cssProp.value();
  9772. var gradient = null;
  9773. var matchedPart = _.find(cssProp.valueParts(), function(part) {
  9774. return gradient = module.parse(part.substring(value));
  9775. });
  9776. if (matchedPart && gradient) {
  9777. return {
  9778. gradient: gradient,
  9779. valueRange: matchedPart
  9780. };
  9781. }
  9782. return null;
  9783. }
  9784. /**
  9785. * Tries to expand gradient outside CSS value
  9786. * @param {IEmmetEditor} editor
  9787. * @param {String} syntax
  9788. */
  9789. function expandGradientOutsideValue(editor, syntax) {
  9790. var propertyName = prefs.get('css.gradient.defaultProperty');
  9791. if (!propertyName)
  9792. return false;
  9793. // assuming that gradient definition is written on new line,
  9794. // do a simplified parsing
  9795. var content = String(editor.getContent());
  9796. /** @type Range */
  9797. var lineRange = require('range').create(editor.getCurrentLineRange());
  9798. // get line content and adjust range with padding
  9799. var line = lineRange.substring(content)
  9800. .replace(/^\s+/, function(pad) {
  9801. lineRange.start += pad.length;
  9802. return '';
  9803. })
  9804. .replace(/\s+$/, function(pad) {
  9805. lineRange.end -= pad.length;
  9806. return '';
  9807. });
  9808. var css = require('cssResolver');
  9809. var gradient = module.parse(line);
  9810. if (gradient) {
  9811. var props = getPropertiesForGradient(gradient, propertyName);
  9812. props.push({
  9813. name: propertyName,
  9814. value: module.toString(gradient) + '${2}'
  9815. });
  9816. var sep = css.getSyntaxPreference('valueSeparator', syntax);
  9817. var end = css.getSyntaxPreference('propertyEnd', syntax);
  9818. if (require('preferences').get('css.alignVendor')) {
  9819. var pads = require('utils').getStringsPads(_.map(props, function(prop) {
  9820. return prop.value.substring(0, prop.value.indexOf('('));
  9821. }));
  9822. _.each(props, function(prop, i) {
  9823. prop.value = pads[i] + prop.value;
  9824. });
  9825. }
  9826. props = _.map(props, function(item) {
  9827. return item.name + sep + item.value + end;
  9828. });
  9829. editor.replaceContent(props.join('\n'), lineRange.start, lineRange.end);
  9830. return true;
  9831. }
  9832. return false;
  9833. }
  9834. /**
  9835. * Search for gradient definition inside CSS value under cursor
  9836. * @param {String} content
  9837. * @param {Number} pos
  9838. * @returns {Object}
  9839. */
  9840. function findGradientFromPosition(content, pos) {
  9841. var cssProp = null;
  9842. /** @type EditContainer */
  9843. var cssRule = require('cssEditTree').parseFromPosition(content, pos, true);
  9844. if (cssRule) {
  9845. cssProp = cssRule.itemFromPosition(pos, true);
  9846. if (!cssProp) {
  9847. // in case user just started writing CSS property
  9848. // and didn't include semicolon–try another approach
  9849. cssProp = _.find(cssRule.list(), function(elem) {
  9850. return elem.range(true).end == pos;
  9851. });
  9852. }
  9853. }
  9854. return {
  9855. rule: cssRule,
  9856. property: cssProp
  9857. };
  9858. }
  9859. // XXX register expand abbreviation handler
  9860. /**
  9861. * @param {IEmmetEditor} editor
  9862. * @param {String} syntax
  9863. * @param {String} profile
  9864. */
  9865. require('expandAbbreviation').addHandler(function(editor, syntax, profile) {
  9866. var info = require('editorUtils').outputInfo(editor, syntax, profile);
  9867. if (!_.include(cssSyntaxes, info.syntax))
  9868. return false;
  9869. // let's see if we are expanding gradient definition
  9870. var caret = editor.getCaretPos();
  9871. var content = info.content;
  9872. var css = findGradientFromPosition(content, caret);
  9873. if (css.property) {
  9874. // make sure that caret is inside property value with gradient
  9875. // definition
  9876. var g = findGradient(css.property);
  9877. if (g) {
  9878. var ruleStart = css.rule.options.offset || 0;
  9879. var ruleEnd = ruleStart + css.rule.toString().length;
  9880. // Handle special case:
  9881. // user wrote gradient definition between existing CSS
  9882. // properties and did not finished it with semicolon.
  9883. // In this case, we have semicolon right after gradient
  9884. // definition and re-parse rule again
  9885. if (/[\n\r]/.test(css.property.value())) {
  9886. // insert semicolon at the end of gradient definition
  9887. var insertPos = css.property.valueRange(true).start + g.valueRange.end;
  9888. content = require('utils').replaceSubstring(content, ';', insertPos);
  9889. var newCss = findGradientFromPosition(content, caret);
  9890. if (newCss.property) {
  9891. g = findGradient(newCss.property);
  9892. css = newCss;
  9893. }
  9894. }
  9895. // make sure current property has terminating semicolon
  9896. css.property.end(';');
  9897. // resolve CSS property name
  9898. var resolvedName = resolvePropertyName(css.property.name(), syntax);
  9899. if (resolvedName) {
  9900. css.property.name(resolvedName);
  9901. }
  9902. pasteGradient(css.property, g.gradient, g.valueRange);
  9903. editor.replaceContent(css.rule.toString(), ruleStart, ruleEnd, true);
  9904. return true;
  9905. }
  9906. }
  9907. return expandGradientOutsideValue(editor, syntax);
  9908. });
  9909. // XXX register "Reflect CSS Value" action delegate
  9910. /**
  9911. * @param {EditElement} property
  9912. */
  9913. require('reflectCSSValue').addHandler(function(property) {
  9914. var utils = require('utils');
  9915. var g = findGradient(property);
  9916. if (!g)
  9917. return false;
  9918. var value = property.value();
  9919. var val = function(v) {
  9920. return utils.replaceSubstring(value, v, g.valueRange);
  9921. };
  9922. // reflect value for properties with the same name
  9923. _.each(property.parent.getAll(getPrefixedNames(property.name())), function(prop) {
  9924. if (prop === property)
  9925. return;
  9926. // check if property value starts with gradient definition
  9927. var m = prop.value().match(/^\s*(\-([a-z]+)\-)?linear\-gradient/);
  9928. if (m) {
  9929. prop.value(val(module.toString(g.gradient, m[2] || '')));
  9930. } else if (m = prop.value().match(/\s*\-webkit\-gradient/)) {
  9931. // old webkit gradient definition
  9932. prop.value(val(module.oldWebkitLinearGradient(g.gradient)));
  9933. }
  9934. });
  9935. return true;
  9936. });
  9937. return module = {
  9938. /**
  9939. * Parses gradient definition
  9940. * @param {String} gradient
  9941. * @returns {Object}
  9942. */
  9943. parse: function(gradient) {
  9944. var result = null;
  9945. require('utils').trim(gradient).replace(/^([\w\-]+)\((.+?)\)$/, function(str, type, definition) {
  9946. // remove vendor prefix
  9947. type = type.toLowerCase().replace(/^\-[a-z]+\-/, '');
  9948. if (type == 'linear-gradient' || type == 'lg') {
  9949. result = parseLinearGradient(definition);
  9950. return '';
  9951. }
  9952. return str;
  9953. });
  9954. return result;
  9955. },
  9956. /**
  9957. * Produces linear gradient definition used in early Webkit
  9958. * implementations
  9959. * @param {Object} gradient Parsed gradient
  9960. * @returns {String}
  9961. */
  9962. oldWebkitLinearGradient: function(gradient) {
  9963. if (_.isString(gradient))
  9964. gradient = this.parse(gradient);
  9965. if (!gradient)
  9966. return null;
  9967. var colorStops = _.map(gradient.colorStops, _.clone);
  9968. // normalize color-stops position
  9969. _.each(colorStops, function(cs) {
  9970. if (!('position' in cs)) // implied position
  9971. return;
  9972. if (~cs.position.indexOf('.') || cs.unit == '%') {
  9973. cs.position = parseFloat(cs.position) / (cs.unit == '%' ? 100 : 1);
  9974. } else {
  9975. throw "Can't convert color stop '" + (cs.position + (cs.unit || '')) + "'";
  9976. }
  9977. });
  9978. fillImpliedPositions(colorStops);
  9979. // transform color-stops into string representation
  9980. colorStops = _.map(colorStops, function(cs, i) {
  9981. if (!cs.position && !i)
  9982. return 'from(' + cs.color + ')';
  9983. if (cs.position == 1 && i == colorStops.length - 1)
  9984. return 'to(' + cs.color + ')';
  9985. return 'color-stop(' + (cs.position.toFixed(2).replace(/\.?0+$/, '')) + ', ' + cs.color + ')';
  9986. });
  9987. return '-webkit-gradient(linear, '
  9988. + oldWebkitDirection(gradient.direction)
  9989. + ', '
  9990. + colorStops.join(', ')
  9991. + ')';
  9992. },
  9993. /**
  9994. * Returns string representation of parsed gradient
  9995. * @param {Object} gradient Parsed gradient
  9996. * @param {String} prefix Vendor prefix
  9997. * @returns {String}
  9998. */
  9999. toString: function(gradient, prefix) {
  10000. if (gradient.type == 'linear') {
  10001. var fn = (prefix ? '-' + prefix + '-' : '') + 'linear-gradient';
  10002. // transform color-stops
  10003. var colorStops = _.map(gradient.colorStops, function(cs) {
  10004. return cs.color + ('position' in cs
  10005. ? ' ' + cs.position + (cs.unit || '')
  10006. : '');
  10007. });
  10008. if (gradient.direction
  10009. && (!prefs.get('css.gradient.omitDefaultDirection')
  10010. || !_.include(defaultLinearDirections, gradient.direction))) {
  10011. colorStops.unshift(gradient.direction);
  10012. }
  10013. return fn + '(' + colorStops.join(', ') + ')';
  10014. }
  10015. }
  10016. };
  10017. });/**
  10018. * Module adds support for generators: a regexp-based abbreviation resolver
  10019. * that can produce custom output.
  10020. * @param {Function} require
  10021. * @param {Underscore} _
  10022. */
  10023. emmet.exec(function(require, _) {
  10024. /** @type HandlerList */
  10025. var generators = require('handlerList').create();
  10026. var resources = require('resources');
  10027. _.extend(resources, {
  10028. /**
  10029. * Add generator. A generator function <code>fn</code> will be called
  10030. * only if current abbreviation matches <code>regexp</code> regular
  10031. * expression and this function should return <code>null</code> if
  10032. * abbreviation cannot be resolved
  10033. * @param {RegExp} regexp Regular expression for abbreviation element name
  10034. * @param {Function} fn Resolver function
  10035. * @param {Object} options Options list as described in
  10036. * {@link HandlerList#add()} method
  10037. */
  10038. addGenerator: function(regexp, fn, options) {
  10039. if (_.isString(regexp))
  10040. regexp = new RegExp(regexp);
  10041. generators.add(function(node, syntax) {
  10042. var m;
  10043. if ((m = regexp.exec(node.name()))) {
  10044. return fn(m, node, syntax);
  10045. }
  10046. return null;
  10047. }, options);
  10048. }
  10049. });
  10050. resources.addResolver(function(node, syntax) {
  10051. return generators.exec(null, _.toArray(arguments));
  10052. });
  10053. });/**
  10054. * Module for resolving tag names: returns best matched tag name for child
  10055. * element based on passed parent's tag name. Also provides utility function
  10056. * for element type detection (inline, block-level, empty)
  10057. * @param {Function} require
  10058. * @param {Underscore} _
  10059. */
  10060. emmet.define('tagName', function(require, _) {
  10061. var elementTypes = {
  10062. // empty: 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,keygen,command'.split(','),
  10063. empty: [],
  10064. blockLevel: 'address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,link,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul,h1,h2,h3,h4,h5,h6'.split(','),
  10065. inlineLevel: 'a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'.split(',')
  10066. };
  10067. var elementMap = {
  10068. 'p': 'span',
  10069. 'ul': 'li',
  10070. 'ol': 'li',
  10071. 'table': 'tr',
  10072. 'tr': 'td',
  10073. 'tbody': 'tr',
  10074. 'thead': 'tr',
  10075. 'tfoot': 'tr',
  10076. 'colgroup': 'col',
  10077. 'select': 'option',
  10078. 'optgroup': 'option',
  10079. 'audio': 'source',
  10080. 'video': 'source',
  10081. 'object': 'param',
  10082. 'map': 'area'
  10083. };
  10084. return {
  10085. /**
  10086. * Returns best matched child element name for passed parent's
  10087. * tag name
  10088. * @param {String} name
  10089. * @returns {String}
  10090. * @memberOf tagName
  10091. */
  10092. resolve: function(name) {
  10093. name = (name || '').toLowerCase();
  10094. if (name in elementMap)
  10095. return this.getMapping(name);
  10096. if (this.isInlineLevel(name))
  10097. return 'span';
  10098. return 'div';
  10099. },
  10100. /**
  10101. * Returns mapped child element name for passed parent's name
  10102. * @param {String} name
  10103. * @returns {String}
  10104. */
  10105. getMapping: function(name) {
  10106. return elementMap[name.toLowerCase()];
  10107. },
  10108. /**
  10109. * Check if passed element name belongs to inline-level element
  10110. * @param {String} name
  10111. * @returns {Boolean}
  10112. */
  10113. isInlineLevel: function(name) {
  10114. return this.isTypeOf(name, 'inlineLevel');
  10115. },
  10116. /**
  10117. * Check if passed element belongs to block-level element.
  10118. * For better matching of unknown elements (for XML, for example),
  10119. * you should use <code>!this.isInlineLevel(name)</code>
  10120. * @returns {Boolean}
  10121. */
  10122. isBlockLevel: function(name) {
  10123. return this.isTypeOf(name, 'blockLevel');
  10124. },
  10125. /**
  10126. * Check if passed element is void (i.e. should not have closing tag).
  10127. * @returns {Boolean}
  10128. */
  10129. isEmptyElement: function(name) {
  10130. return this.isTypeOf(name, 'empty');
  10131. },
  10132. /**
  10133. * Generic function for testing if element name belongs to specified
  10134. * elements collection
  10135. * @param {String} name Element name
  10136. * @param {String} type Collection name
  10137. * @returns {Boolean}
  10138. */
  10139. isTypeOf: function(name, type) {
  10140. return _.include(elementTypes[type], name);
  10141. },
  10142. /**
  10143. * Adds new parent–child mapping
  10144. * @param {String} parent
  10145. * @param {String} child
  10146. */
  10147. addMapping: function(parent, child) {
  10148. elementMap[parent] = child;
  10149. },
  10150. /**
  10151. * Removes parent-child mapping
  10152. */
  10153. removeMapping: function(parent) {
  10154. if (parent in elementMap)
  10155. delete elementMap[parent];
  10156. },
  10157. /**
  10158. * Adds new element into collection
  10159. * @param {String} name Element name
  10160. * @param {String} collection Collection name
  10161. */
  10162. addElementToCollection: function(name, collection) {
  10163. if (!elementTypes[collection])
  10164. elementTypes[collection] = [];
  10165. var col = this.getCollection(collection);
  10166. if (!_.include(col, name))
  10167. col.push(name);
  10168. },
  10169. /**
  10170. * Removes element name from specified collection
  10171. * @param {String} name Element name
  10172. * @param {String} collection Collection name
  10173. * @returns
  10174. */
  10175. removeElementFromCollection: function(name, collection) {
  10176. if (collection in elementTypes) {
  10177. elementTypes[collection] = _.without(this.getCollection(collection), name);
  10178. }
  10179. },
  10180. /**
  10181. * Returns elements name collection
  10182. * @param {String} name Collection name
  10183. * @returns {Array}
  10184. */
  10185. getCollection: function(name) {
  10186. return elementTypes[name];
  10187. }
  10188. };
  10189. });/**
  10190. * Filter for aiding of writing elements with complex class names as described
  10191. * in Yandex's BEM (Block, Element, Modifier) methodology. This filter will
  10192. * automatically inherit block and element names from parent elements and insert
  10193. * them into child element classes
  10194. * @memberOf __bemFilterDefine
  10195. * @constructor
  10196. * @param {Function} require
  10197. * @param {Underscore} _
  10198. */
  10199. emmet.exec(function(require, _) {
  10200. var prefs = require('preferences');
  10201. prefs.define('bem.elementSeparator', '__', 'Class name’s element separator.');
  10202. prefs.define('bem.modifierSeparator', '_', 'Class name’s modifier separator.');
  10203. prefs.define('bem.shortElementPrefix', '-',
  10204. 'Symbol for describing short “block-element” notation. Class names '
  10205. + 'prefixed with this symbol will be treated as element name for parent‘s '
  10206. + 'block name. Each symbol instance traverses one level up in parsed '
  10207. + 'tree for block name lookup. Empty value will disable short notation.');
  10208. var shouldRunHtmlFilter = false;
  10209. function getSeparators() {
  10210. return {
  10211. element: prefs.get('bem.elementSeparator'),
  10212. modifier: prefs.get('bem.modifierSeparator')
  10213. };
  10214. }
  10215. /**
  10216. * @param {AbbreviationNode} item
  10217. */
  10218. function bemParse(item) {
  10219. if (require('abbreviationUtils').isSnippet(item))
  10220. return item;
  10221. // save BEM stuff in cache for faster lookups
  10222. item.__bem = {
  10223. block: '',
  10224. element: '',
  10225. modifier: ''
  10226. };
  10227. var classNames = normalizeClassName(item.attribute('class')).split(' ');
  10228. // guess best match for block name
  10229. var reBlockName = /^[a-z]\-/i;
  10230. item.__bem.block = _.find(classNames, function(name) {
  10231. return reBlockName.test(name);
  10232. });
  10233. // guessing doesn't worked, pick first class name as block name
  10234. if (!item.__bem.block) {
  10235. reBlockName = /^[a-z]/i;
  10236. item.__bem.block = _.find(classNames, function(name) {
  10237. return reBlockName.test(name);
  10238. }) || '';
  10239. }
  10240. classNames = _.chain(classNames)
  10241. .map(function(name) {return processClassName(name, item);})
  10242. .flatten()
  10243. .uniq()
  10244. .value()
  10245. .join(' ');
  10246. if (classNames)
  10247. item.attribute('class', classNames);
  10248. return item;
  10249. }
  10250. /**
  10251. * @param {String} className
  10252. * @returns {String}
  10253. */
  10254. function normalizeClassName(className) {
  10255. var utils = require('utils');
  10256. className = (' ' + (className || '') + ' ').replace(/\s+/g, ' ');
  10257. var shortSymbol = prefs.get('bem.shortElementPrefix');
  10258. if (shortSymbol) {
  10259. var re = new RegExp('\\s(' + utils.escapeForRegexp(shortSymbol) + '+)', 'g');
  10260. className = className.replace(re, function(str, p1) {
  10261. return ' ' + utils.repeatString(getSeparators().element, p1.length);
  10262. });
  10263. }
  10264. return utils.trim(className);
  10265. }
  10266. /**
  10267. * Processes class name
  10268. * @param {String} name Class name item to process
  10269. * @param {AbbreviationNode} item Host node for provided class name
  10270. * @returns Processed class name. May return <code>Array</code> of
  10271. * class names
  10272. */
  10273. function processClassName(name, item) {
  10274. name = transformClassName(name, item, 'element');
  10275. name = transformClassName(name, item, 'modifier');
  10276. // expand class name
  10277. // possible values:
  10278. // * block__element
  10279. // * block__element_modifier
  10280. // * block__element_modifier1_modifier2
  10281. // * block_modifier
  10282. var block = '', element = '', modifier = '';
  10283. var separators = getSeparators();
  10284. if (~name.indexOf(separators.element)) {
  10285. var blockElem = name.split(separators.element);
  10286. var elemModifiers = blockElem[1].split(separators.modifier);
  10287. block = blockElem[0];
  10288. element = elemModifiers.shift();
  10289. modifier = elemModifiers.join(separators.modifier);
  10290. } else if (~name.indexOf(separators.modifier)) {
  10291. var blockModifiers = name.split(separators.modifier);
  10292. block = blockModifiers.shift();
  10293. modifier = blockModifiers.join(separators.modifier);
  10294. }
  10295. if (block || element || modifier) {
  10296. if (!block) {
  10297. block = item.__bem.block;
  10298. }
  10299. // inherit parent bem element, if exists
  10300. // if (item.parent && item.parent.__bem && item.parent.__bem.element)
  10301. // element = item.parent.__bem.element + separators.element + element;
  10302. // produce multiple classes
  10303. var prefix = block;
  10304. var result = [];
  10305. if (element) {
  10306. prefix += separators.element + element;
  10307. result.push(prefix);
  10308. } else {
  10309. result.push(prefix);
  10310. }
  10311. if (modifier) {
  10312. result.push(prefix + separators.modifier + modifier);
  10313. }
  10314. item.__bem.block = block;
  10315. item.__bem.element = element;
  10316. item.__bem.modifier = modifier;
  10317. return result;
  10318. }
  10319. // ...otherwise, return processed or original class name
  10320. return name;
  10321. }
  10322. /**
  10323. * Low-level function to transform user-typed class name into full BEM class
  10324. * @param {String} name Class name item to process
  10325. * @param {AbbreviationNode} item Host node for provided class name
  10326. * @param {String} entityType Type of entity to be tried to transform
  10327. * ('element' or 'modifier')
  10328. * @returns {String} Processed class name or original one if it can't be
  10329. * transformed
  10330. */
  10331. function transformClassName(name, item, entityType) {
  10332. var separators = getSeparators();
  10333. var reSep = new RegExp('^(' + separators[entityType] + ')+', 'g');
  10334. if (reSep.test(name)) {
  10335. var depth = 0; // parent lookup depth
  10336. var cleanName = name.replace(reSep, function(str, p1) {
  10337. depth = str.length / separators[entityType].length;
  10338. return '';
  10339. });
  10340. // find donor element
  10341. var donor = item;
  10342. while (donor.parent && depth--) {
  10343. donor = donor.parent;
  10344. }
  10345. if (!donor || !donor.__bem)
  10346. donor = item;
  10347. if (donor && donor.__bem) {
  10348. var prefix = donor.__bem.block;
  10349. // decide if we should inherit element name
  10350. // if (entityType == 'element') {
  10351. // var curElem = cleanName.split(separators.modifier, 1)[0];
  10352. // if (donor.__bem.element && donor.__bem.element != curElem)
  10353. // prefix += separators.element + donor.__bem.element;
  10354. // }
  10355. if (entityType == 'modifier' && donor.__bem.element)
  10356. prefix += separators.element + donor.__bem.element;
  10357. return prefix + separators[entityType] + cleanName;
  10358. }
  10359. }
  10360. return name;
  10361. }
  10362. /**
  10363. * Recursive function for processing tags, which extends class names
  10364. * according to BEM specs: http://bem.github.com/bem-method/pages/beginning/beginning.ru.html
  10365. * <br><br>
  10366. * It does several things:<br>
  10367. * <ul>
  10368. * <li>Expands complex class name (according to BEM symbol semantics):
  10369. * .block__elem_modifier → .block.block__elem.block__elem_modifier
  10370. * </li>
  10371. * <li>Inherits block name on child elements:
  10372. * .b-block > .__el > .__el → .b-block > .b-block__el > .b-block__el__el
  10373. * </li>
  10374. * <li>Treats first dash symbol as '__'</li>
  10375. * <li>Double underscore (or typographic '–') is also treated as an element
  10376. * level lookup, e.g. ____el will search for element definition in parent’s
  10377. * parent element:
  10378. * .b-block > .__el1 > .____el2 → .b-block > .b-block__el1 > .b-block__el2
  10379. * </li>
  10380. * </ul>
  10381. *
  10382. * @param {AbbreviationNode} tree
  10383. * @param {Object} profile
  10384. */
  10385. function process(tree, profile) {
  10386. if (tree.name)
  10387. bemParse(tree, profile);
  10388. var abbrUtils = require('abbreviationUtils');
  10389. _.each(tree.children, function(item) {
  10390. process(item, profile);
  10391. if (!abbrUtils.isSnippet(item) && item.start)
  10392. shouldRunHtmlFilter = true;
  10393. });
  10394. return tree;
  10395. };
  10396. require('filters').add('bem', function(tree, profile) {
  10397. shouldRunHtmlFilter = false;
  10398. tree = process(tree, profile);
  10399. // in case 'bem' filter is applied after 'html' filter: run it again
  10400. // to update output
  10401. if (shouldRunHtmlFilter) {
  10402. tree = require('filters').apply(tree, 'html', profile);
  10403. }
  10404. return tree;
  10405. });
  10406. });
  10407. /**
  10408. * Comment important tags (with 'id' and 'class' attributes)
  10409. * @author Sergey Chikuyonok (serge.che@gmail.com)
  10410. * @link http://chikuyonok.ru
  10411. * @constructor
  10412. * @memberOf __commentFilterDefine
  10413. * @param {Function} require
  10414. * @param {Underscore} _
  10415. */
  10416. emmet.exec(function(require, _) {
  10417. // define some preferences
  10418. /** @type emmet.preferences */
  10419. var prefs = require('preferences');
  10420. prefs.define('filter.commentAfter',
  10421. '\n<!-- /<%= attr("id", "#") %><%= attr("class", ".") %> -->',
  10422. 'A definition of comment that should be placed <i>after</i> matched '
  10423. + 'element when <code>comment</code> filter is applied. This definition '
  10424. + 'is an ERB-style template passed to <code>_.template()</code> '
  10425. + 'function (see Underscore.js docs for details). In template context, '
  10426. + 'the following properties and functions are availabe:\n'
  10427. + '<ul>'
  10428. + '<li><code>attr(name, before, after)</code> – a function that outputs'
  10429. + 'specified attribute value concatenated with <code>before</code> '
  10430. + 'and <code>after</code> strings. If attribute doesn\'t exists, the '
  10431. + 'empty string will be returned.</li>'
  10432. + '<li><code>node</code> – current node (instance of <code>AbbreviationNode</code>)</li>'
  10433. + '<li><code>name</code> – name of current tag</li>'
  10434. + '<li><code>padding</code> – current string padding, can be used '
  10435. + 'for formatting</li>'
  10436. +'</ul>');
  10437. prefs.define('filter.commentBefore',
  10438. '',
  10439. 'A definition of comment that should be placed <i>before</i> matched '
  10440. + 'element when <code>comment</code> filter is applied. '
  10441. + 'For more info, read description of <code>filter.commentAfter</code> '
  10442. + 'property');
  10443. prefs.define('filter.commentTrigger', 'id, class',
  10444. 'A comma-separated list of attribute names that should exist in abbreviatoin '
  10445. + 'where comment should be added. If you wish to add comment for '
  10446. + 'every element, set this option to <code>*</code>');
  10447. /**
  10448. * Add comments to tag
  10449. * @param {AbbreviationNode} node
  10450. */
  10451. function addComments(node, templateBefore, templateAfter) {
  10452. var utils = require('utils');
  10453. // check if comments should be added
  10454. var trigger = prefs.get('filter.commentTrigger');
  10455. if (trigger != '*') {
  10456. var shouldAdd = _.find(trigger.split(','), function(name) {
  10457. return !!node.attribute(utils.trim(name));
  10458. });
  10459. if (!shouldAdd) return;
  10460. }
  10461. var ctx = {
  10462. node: node,
  10463. name: node.name(),
  10464. padding: node.parent ? node.parent.padding : '',
  10465. attr: function(name, before, after) {
  10466. var attr = node.attribute(name);
  10467. if (attr) {
  10468. return (before || '') + attr + (after || '');
  10469. }
  10470. return '';
  10471. }
  10472. };
  10473. var nodeBefore = utils.normalizeNewline(templateBefore ? templateBefore(ctx) : '');
  10474. var nodeAfter = utils.normalizeNewline(templateAfter ? templateAfter(ctx) : '');
  10475. node.start = node.start.replace(/</, nodeBefore + '<');
  10476. node.end = node.end.replace(/>/, '>' + nodeAfter);
  10477. }
  10478. function process(tree, before, after) {
  10479. var abbrUtils = require('abbreviationUtils');
  10480. _.each(tree.children, function(item) {
  10481. if (abbrUtils.isBlock(item))
  10482. addComments(item, before, after);
  10483. process(item, before, after);
  10484. });
  10485. return tree;
  10486. }
  10487. require('filters').add('c', function(tree) {
  10488. var templateBefore = _.template(prefs.get('filter.commentBefore'));
  10489. var templateAfter = _.template(prefs.get('filter.commentAfter'));
  10490. return process(tree, templateBefore, templateAfter);
  10491. });
  10492. });
  10493. /**
  10494. * Filter for escaping unsafe XML characters: <, >, &
  10495. * @author Sergey Chikuyonok (serge.che@gmail.com)
  10496. * @link http://chikuyonok.ru
  10497. */
  10498. emmet.exec(function(require, _) {
  10499. var charMap = {
  10500. '<': '&lt;',
  10501. '>': '&gt;',
  10502. '&': '&amp;'
  10503. };
  10504. function escapeChars(str) {
  10505. return str.replace(/([<>&])/g, function(str, p1){
  10506. return charMap[p1];
  10507. });
  10508. }
  10509. require('filters').add('e', function process(tree) {
  10510. _.each(tree.children, function(item) {
  10511. item.start = escapeChars(item.start);
  10512. item.end = escapeChars(item.end);
  10513. item.content = escapeChars(item.content);
  10514. process(item);
  10515. });
  10516. return tree;
  10517. });
  10518. });/**
  10519. * Generic formatting filter: creates proper indentation for each tree node,
  10520. * placing "%s" placeholder where the actual output should be. You can use
  10521. * this filter to preformat tree and then replace %s placeholder to whatever you
  10522. * need. This filter should't be called directly from editor as a part
  10523. * of abbreviation.
  10524. * @author Sergey Chikuyonok (serge.che@gmail.com)
  10525. * @link http://chikuyonok.ru
  10526. * @constructor
  10527. * @memberOf __formatFilterDefine
  10528. * @param {Function} require
  10529. * @param {Underscore} _
  10530. */
  10531. emmet.exec(function(require, _){
  10532. var placeholder = '%s';
  10533. /** @type preferences */
  10534. var prefs = require('preferences');
  10535. prefs.define('format.noIndentTags', 'html',
  10536. 'A comma-separated list of tag names that should not get inner indentation.');
  10537. prefs.define('format.forceIndentationForTags', 'body',
  10538. 'A comma-separated list of tag names that should <em>always</em> get inner indentation.');
  10539. /**
  10540. * Get indentation for given node
  10541. * @param {AbbreviationNode} node
  10542. * @returns {String}
  10543. */
  10544. function getIndentation(node) {
  10545. if (_.include(prefs.getArray('format.noIndentTags') || [], node.name())) {
  10546. return '';
  10547. }
  10548. return require('resources').getVariable('indentation');
  10549. }
  10550. /**
  10551. * Test if passed node has block-level sibling element
  10552. * @param {AbbreviationNode} item
  10553. * @return {Boolean}
  10554. */
  10555. function hasBlockSibling(item) {
  10556. return item.parent && require('abbreviationUtils').hasBlockChildren(item.parent);
  10557. }
  10558. /**
  10559. * Test if passed item is very first child in parsed tree
  10560. * @param {AbbreviationNode} item
  10561. */
  10562. function isVeryFirstChild(item) {
  10563. return item.parent && !item.parent.parent && !item.index();
  10564. }
  10565. /**
  10566. * Check if a newline should be added before element
  10567. * @param {AbbreviationNode} node
  10568. * @param {OutputProfile} profile
  10569. * @return {Boolean}
  10570. */
  10571. function shouldAddLineBreak(node, profile) {
  10572. var abbrUtils = require('abbreviationUtils');
  10573. if (profile.tag_nl === true || abbrUtils.isBlock(node))
  10574. return true;
  10575. if (!node.parent || !profile.inline_break)
  10576. return false;
  10577. // check if there are required amount of adjacent inline element
  10578. return shouldFormatInline(node.parent, profile);
  10579. }
  10580. /**
  10581. * Need to add newline because <code>item</code> has too many inline children
  10582. * @param {AbbreviationNode} node
  10583. * @param {OutputProfile} profile
  10584. */
  10585. function shouldBreakChild(node, profile) {
  10586. // we need to test only one child element, because
  10587. // hasBlockChildren() method will do the rest
  10588. return node.children.length && shouldAddLineBreak(node.children[0], profile);
  10589. }
  10590. function shouldFormatInline(node, profile) {
  10591. var nodeCount = 0;
  10592. var abbrUtils = require('abbreviationUtils');
  10593. return !!_.find(node.children, function(child) {
  10594. if (child.isTextNode() || !abbrUtils.isInline(child))
  10595. nodeCount = 0;
  10596. else if (abbrUtils.isInline(child))
  10597. nodeCount++;
  10598. if (nodeCount >= profile.inline_break)
  10599. return true;
  10600. });
  10601. }
  10602. function isRoot(item) {
  10603. return !item.parent;
  10604. }
  10605. /**
  10606. * Processes element with matched resource of type <code>snippet</code>
  10607. * @param {AbbreviationNode} item
  10608. * @param {OutputProfile} profile
  10609. * @param {Number} level Depth level
  10610. */
  10611. function processSnippet(item, profile, level) {
  10612. item.start = item.end = '';
  10613. if (!isVeryFirstChild(item) && profile.tag_nl !== false && shouldAddLineBreak(item, profile)) {
  10614. // check if we’re not inside inline element
  10615. if (isRoot(item.parent) || !require('abbreviationUtils').isInline(item.parent)) {
  10616. item.start = require('utils').getNewline() + item.start;
  10617. }
  10618. }
  10619. return item;
  10620. }
  10621. /**
  10622. * Check if we should add line breaks inside inline element
  10623. * @param {AbbreviationNode} node
  10624. * @param {OutputProfile} profile
  10625. * @return {Boolean}
  10626. */
  10627. function shouldBreakInsideInline(node, profile) {
  10628. var abbrUtils = require('abbreviationUtils');
  10629. var hasBlockElems = _.any(node.children, function(child) {
  10630. if (abbrUtils.isSnippet(child))
  10631. return false;
  10632. return !abbrUtils.isInline(child);
  10633. });
  10634. if (!hasBlockElems) {
  10635. return shouldFormatInline(node, profile);
  10636. }
  10637. return true;
  10638. }
  10639. /**
  10640. * Processes element with <code>tag</code> type
  10641. * @param {AbbreviationNode} item
  10642. * @param {OutputProfile} profile
  10643. * @param {Number} level Depth level
  10644. */
  10645. function processTag(item, profile, level) {
  10646. item.start = item.end = placeholder;
  10647. var utils = require('utils');
  10648. var abbrUtils = require('abbreviationUtils');
  10649. var isUnary = abbrUtils.isUnary(item);
  10650. var nl = utils.getNewline();
  10651. var indent = getIndentation(item);
  10652. // formatting output
  10653. if (profile.tag_nl !== false) {
  10654. var forceNl = profile.tag_nl === true && (profile.tag_nl_leaf || item.children.length);
  10655. if (!forceNl) {
  10656. forceNl = _.include(prefs.getArray('format.forceIndentationForTags') || [], item.name());
  10657. }
  10658. // formatting block-level elements
  10659. if (!item.isTextNode()) {
  10660. if (shouldAddLineBreak(item, profile)) {
  10661. // - do not indent the very first element
  10662. // - do not indent first child of a snippet
  10663. if (!isVeryFirstChild(item) && (!abbrUtils.isSnippet(item.parent) || item.index()))
  10664. item.start = nl + item.start;
  10665. if (abbrUtils.hasBlockChildren(item) || shouldBreakChild(item, profile) || (forceNl && !isUnary))
  10666. item.end = nl + item.end;
  10667. if (abbrUtils.hasTagsInContent(item) || (forceNl && !item.children.length && !isUnary))
  10668. item.start += nl + indent;
  10669. } else if (abbrUtils.isInline(item) && hasBlockSibling(item) && !isVeryFirstChild(item)) {
  10670. item.start = nl + item.start;
  10671. } else if (abbrUtils.isInline(item) && shouldBreakInsideInline(item, profile)) {
  10672. item.end = nl + item.end;
  10673. }
  10674. item.padding = indent;
  10675. }
  10676. }
  10677. return item;
  10678. }
  10679. /**
  10680. * Processes simplified tree, making it suitable for output as HTML structure
  10681. * @param {AbbreviationNode} tree
  10682. * @param {OutputProfile} profile
  10683. * @param {Number} level Depth level
  10684. */
  10685. require('filters').add('_format', function process(tree, profile, level) {
  10686. level = level || 0;
  10687. var abbrUtils = require('abbreviationUtils');
  10688. _.each(tree.children, function(item) {
  10689. if (abbrUtils.isSnippet(item))
  10690. processSnippet(item, profile, level);
  10691. else
  10692. processTag(item, profile, level);
  10693. process(item, profile, level + 1);
  10694. });
  10695. return tree;
  10696. });
  10697. });/**
  10698. * Filter for producing HAML code from abbreviation.
  10699. * @author Sergey Chikuyonok (serge.che@gmail.com)
  10700. * @link http://chikuyonok.ru
  10701. * @constructor
  10702. * @memberOf __hamlFilterDefine
  10703. * @param {Function} require
  10704. * @param {Underscore} _
  10705. */
  10706. emmet.exec(function(require, _) {
  10707. var childToken = '${child}';
  10708. function transformClassName(className) {
  10709. return require('utils').trim(className).replace(/\s+/g, '.');
  10710. }
  10711. /**
  10712. * Creates HAML attributes string from tag according to profile settings
  10713. * @param {AbbreviationNode} tag
  10714. * @param {Object} profile
  10715. */
  10716. function makeAttributesString(tag, profile) {
  10717. var attrs = '';
  10718. var otherAttrs = [];
  10719. var attrQuote = profile.attributeQuote();
  10720. var cursor = profile.cursor();
  10721. _.each(tag.attributeList(), function(a) {
  10722. var attrName = profile.attributeName(a.name);
  10723. switch (attrName.toLowerCase()) {
  10724. // use short notation for ID and CLASS attributes
  10725. case 'id':
  10726. attrs += '#' + (a.value || cursor);
  10727. break;
  10728. case 'class':
  10729. attrs += '.' + transformClassName(a.value || cursor);
  10730. break;
  10731. // process other attributes
  10732. default:
  10733. otherAttrs.push(':' +attrName + ' => ' + attrQuote + (a.value || cursor) + attrQuote);
  10734. }
  10735. });
  10736. if (otherAttrs.length)
  10737. attrs += '{' + otherAttrs.join(', ') + '}';
  10738. return attrs;
  10739. }
  10740. /**
  10741. * Test if passed node has block-level sibling element
  10742. * @param {AbbreviationNode} item
  10743. * @return {Boolean}
  10744. */
  10745. function hasBlockSibling(item) {
  10746. return item.parent && item.parent.hasBlockChildren();
  10747. }
  10748. /**
  10749. * Processes element with <code>tag</code> type
  10750. * @param {AbbreviationNode} item
  10751. * @param {OutputProfile} profile
  10752. * @param {Number} level Depth level
  10753. */
  10754. function processTag(item, profile, level) {
  10755. if (!item.parent)
  10756. // looks like it's root element
  10757. return item;
  10758. var abbrUtils = require('abbreviationUtils');
  10759. var utils = require('utils');
  10760. var attrs = makeAttributesString(item, profile);
  10761. var cursor = profile.cursor();
  10762. var isUnary = abbrUtils.isUnary(item);
  10763. var selfClosing = profile.self_closing_tag && isUnary ? '/' : '';
  10764. var start= '';
  10765. // define tag name
  10766. var tagName = '%' + profile.tagName(item.name());
  10767. if (tagName.toLowerCase() == '%div' && attrs && attrs.indexOf('{') == -1)
  10768. // omit div tag
  10769. tagName = '';
  10770. item.end = '';
  10771. start = tagName + attrs + selfClosing + ' ';
  10772. var placeholder = '%s';
  10773. // We can't just replace placeholder with new value because
  10774. // JavaScript will treat double $ character as a single one, assuming
  10775. // we're using RegExp literal.
  10776. item.start = utils.replaceSubstring(item.start, start, item.start.indexOf(placeholder), placeholder);
  10777. if (!item.children.length && !isUnary)
  10778. item.start += cursor;
  10779. return item;
  10780. }
  10781. /**
  10782. * Processes simplified tree, making it suitable for output as HTML structure
  10783. * @param {AbbreviationNode} tree
  10784. * @param {Object} profile
  10785. * @param {Number} level Depth level
  10786. */
  10787. require('filters').add('haml', function process(tree, profile, level) {
  10788. level = level || 0;
  10789. var abbrUtils = require('abbreviationUtils');
  10790. if (!level) {
  10791. tree = require('filters').apply(tree, '_format', profile);
  10792. }
  10793. _.each(tree.children, function(item) {
  10794. if (!abbrUtils.isSnippet(item))
  10795. processTag(item, profile, level);
  10796. process(item, profile, level + 1);
  10797. });
  10798. return tree;
  10799. });
  10800. });/**
  10801. * Filter that produces HTML tree
  10802. * @author Sergey Chikuyonok (serge.che@gmail.com)
  10803. * @link http://chikuyonok.ru
  10804. * @constructor
  10805. * @memberOf __htmlFilterDefine
  10806. * @param {Function} require
  10807. * @param {Underscore} _
  10808. */
  10809. emmet.exec(function(require, _) {
  10810. /**
  10811. * Creates HTML attributes string from tag according to profile settings
  10812. * @param {AbbreviationNode} node
  10813. * @param {OutputProfile} profile
  10814. */
  10815. function makeAttributesString(node, profile) {
  10816. var attrQuote = profile.attributeQuote();
  10817. var cursor = profile.cursor();
  10818. return _.map(node.attributeList(), function(a) {
  10819. var attrName = profile.attributeName(a.name);
  10820. return ' ' + attrName + '=' + attrQuote + (a.value || cursor) + attrQuote;
  10821. }).join('');
  10822. }
  10823. /**
  10824. * Processes element with <code>tag</code> type
  10825. * @param {AbbreviationNode} item
  10826. * @param {OutputProfile} profile
  10827. * @param {Number} level Depth level
  10828. */
  10829. function processTag(item, profile, level) {
  10830. if (!item.parent) // looks like it's root element
  10831. return item;
  10832. var abbrUtils = require('abbreviationUtils');
  10833. var utils = require('utils');
  10834. var attrs = makeAttributesString(item, profile);
  10835. var cursor = profile.cursor();
  10836. var isUnary = abbrUtils.isUnary(item);
  10837. var start= '';
  10838. var end = '';
  10839. // define opening and closing tags
  10840. if (!item.isTextNode()) {
  10841. var tagName = profile.tagName(item.name());
  10842. if (isUnary) {
  10843. start = '<' + tagName + attrs + profile.selfClosing() + '>';
  10844. item.end = '';
  10845. } else {
  10846. start = '<' + tagName + attrs + '>';
  10847. end = '</' + tagName + '>';
  10848. }
  10849. }
  10850. var placeholder = '%s';
  10851. // We can't just replace placeholder with new value because
  10852. // JavaScript will treat double $ character as a single one, assuming
  10853. // we're using RegExp literal.
  10854. item.start = utils.replaceSubstring(item.start, start, item.start.indexOf(placeholder), placeholder);
  10855. item.end = utils.replaceSubstring(item.end, end, item.end.indexOf(placeholder), placeholder);
  10856. // should we put caret placeholder after opening tag?
  10857. if (
  10858. !item.children.length
  10859. && !isUnary
  10860. && !~item.content.indexOf(cursor)
  10861. && !require('tabStops').extract(item.content).tabstops.length
  10862. ) {
  10863. item.start += cursor;
  10864. }
  10865. return item;
  10866. }
  10867. /**
  10868. * Processes simplified tree, making it suitable for output as HTML structure
  10869. * @param {AbbreviationNode} tree
  10870. * @param {Object} profile
  10871. * @param {Number} level Depth level
  10872. */
  10873. require('filters').add('html', function process(tree, profile, level) {
  10874. level = level || 0;
  10875. var abbrUtils = require('abbreviationUtils');
  10876. if (!level) {
  10877. tree = require('filters').apply(tree, '_format', profile);
  10878. }
  10879. _.each(tree.children, function(item) {
  10880. if (!abbrUtils.isSnippet(item))
  10881. processTag(item, profile, level);
  10882. process(item, profile, level + 1);
  10883. });
  10884. return tree;
  10885. });
  10886. });/**
  10887. * Output abbreviation on a single line (i.e. no line breaks)
  10888. * @author Sergey Chikuyonok (serge.che@gmail.com)
  10889. * @link http://chikuyonok.ru
  10890. * @constructor
  10891. * @memberOf __singleLineFilterDefine
  10892. * @param {Function} require
  10893. * @param {Underscore} _
  10894. */
  10895. emmet.exec(function(require, _) {
  10896. var rePad = /^\s+/;
  10897. var reNl = /[\n\r]/g;
  10898. require('filters').add('s', function process(tree, profile, level) {
  10899. var abbrUtils = require('abbreviationUtils');
  10900. _.each(tree.children, function(item) {
  10901. if (!abbrUtils.isSnippet(item)) {
  10902. // remove padding from item
  10903. item.start = item.start.replace(rePad, '');
  10904. item.end = item.end.replace(rePad, '');
  10905. }
  10906. // remove newlines
  10907. item.start = item.start.replace(reNl, '');
  10908. item.end = item.end.replace(reNl, '');
  10909. item.content = item.content.replace(reNl, '');
  10910. process(item);
  10911. });
  10912. return tree;
  10913. });
  10914. });
  10915. /**
  10916. * Trim filter: removes characters at the beginning of the text
  10917. * content that indicates lists: numbers, #, *, -, etc.
  10918. *
  10919. * Useful for wrapping lists with abbreviation.
  10920. *
  10921. * @author Sergey Chikuyonok (serge.che@gmail.com)
  10922. * @link http://chikuyonok.ru
  10923. *
  10924. * @constructor
  10925. * @memberOf __trimFilterDefine
  10926. * @param {Function} require
  10927. * @param {Underscore} _
  10928. */
  10929. emmet.exec(function(require, _) {
  10930. require('preferences').define('filter.trimRegexp', '[\\s|\\u00a0]*[\\d|#|\\-|\*|\\u2022]+\\.?\\s*',
  10931. 'Regular expression used to remove list markers (numbers, dashes, '
  10932. + 'bullets, etc.) in <code>t</code> (trim) filter. The trim filter '
  10933. + 'is useful for wrapping with abbreviation lists, pased from other '
  10934. + 'documents (for example, Word documents).');
  10935. function process(tree, re) {
  10936. _.each(tree.children, function(item) {
  10937. if (item.content)
  10938. item.content = item.content.replace(re, '');
  10939. process(item, re);
  10940. });
  10941. return tree;
  10942. }
  10943. require('filters').add('t', function(tree) {
  10944. var re = new RegExp(require('preferences').get('filter.trimRegexp'));
  10945. return process(tree, re);
  10946. });
  10947. });
  10948. /**
  10949. * Filter for trimming "select" attributes from some tags that contains
  10950. * child elements
  10951. * @author Sergey Chikuyonok (serge.che@gmail.com)
  10952. * @link http://chikuyonok.ru
  10953. *
  10954. * @constructor
  10955. * @memberOf __xslFilterDefine
  10956. * @param {Function} require
  10957. * @param {Underscore} _
  10958. */
  10959. emmet.exec(function(require, _) {
  10960. var tags = {
  10961. 'xsl:variable': 1,
  10962. 'xsl:with-param': 1
  10963. };
  10964. /**
  10965. * Removes "select" attribute from node
  10966. * @param {AbbreviationNode} node
  10967. */
  10968. function trimAttribute(node) {
  10969. node.start = node.start.replace(/\s+select\s*=\s*(['"]).*?\1/, '');
  10970. }
  10971. require('filters').add('xsl', function process(tree) {
  10972. var abbrUtils = require('abbreviationUtils');
  10973. _.each(tree.children, function(item) {
  10974. if (!abbrUtils.isSnippet(item)
  10975. && (item.name() || '').toLowerCase() in tags
  10976. && item.children.length)
  10977. trimAttribute(item);
  10978. process(item);
  10979. });
  10980. return tree;
  10981. });
  10982. });/**
  10983. * "Lorem ipsum" text generator. Matches <code>lipsum(num)?</code> or
  10984. * <code>lorem(num)?</code> abbreviation.
  10985. * This code is based on Django's contribution:
  10986. * https://code.djangoproject.com/browser/django/trunk/django/contrib/webdesign/lorem_ipsum.py
  10987. * <br><br>
  10988. * Examples to test:<br>
  10989. * <code>lipsum</code> – generates 30 words text.<br>
  10990. * <code>lipsum*6</code> – generates 6 paragraphs (autowrapped with &lt;p&gt; element) of text.<br>
  10991. * <code>ol>lipsum10*5</code> — generates ordered list with 5 list items (autowrapped with &lt;li&gt; tag)
  10992. * with text of 10 words on each line<br>
  10993. * <code>span*3>lipsum20</code> – generates 3 paragraphs of 20-words text, each wrapped with &lt;span&gt; element .
  10994. * Each paragraph phrase is unique
  10995. * @param {Function} require
  10996. * @param {Underscore} _
  10997. * @constructor
  10998. * @memberOf __loremIpsumGeneratorDefine
  10999. */
  11000. emmet.exec(function(require, _) {
  11001. /**
  11002. * @param {AbbreviationNode} tree
  11003. * @param {Object} options
  11004. */
  11005. require('abbreviationParser').addPreprocessor(function(tree, options) {
  11006. var re = /^(?:lorem|lipsum)(\d*)$/i, match;
  11007. /** @param {AbbreviationNode} node */
  11008. tree.findAll(function(node) {
  11009. if (node._name && (match = node._name.match(re))) {
  11010. var wordCound = match[1] || 30;
  11011. // force node name resolving if node should be repeated
  11012. // or contains attributes. In this case, node should be outputed
  11013. // as tag, otherwise as text-only node
  11014. node._name = '';
  11015. node.data('forceNameResolving', node.isRepeating() || node.attributeList().length);
  11016. node.data('pasteOverwrites', true);
  11017. node.data('paste', function(i, content) {
  11018. return paragraph(wordCound, !i);
  11019. });
  11020. }
  11021. });
  11022. });
  11023. var COMMON_P = 'lorem ipsum dolor sit amet consectetur adipisicing elit'.split(' ');
  11024. var WORDS = ['exercitationem', 'perferendis', 'perspiciatis', 'laborum', 'eveniet',
  11025. 'sunt', 'iure', 'nam', 'nobis', 'eum', 'cum', 'officiis', 'excepturi',
  11026. 'odio', 'consectetur', 'quasi', 'aut', 'quisquam', 'vel', 'eligendi',
  11027. 'itaque', 'non', 'odit', 'tempore', 'quaerat', 'dignissimos',
  11028. 'facilis', 'neque', 'nihil', 'expedita', 'vitae', 'vero', 'ipsum',
  11029. 'nisi', 'animi', 'cumque', 'pariatur', 'velit', 'modi', 'natus',
  11030. 'iusto', 'eaque', 'sequi', 'illo', 'sed', 'ex', 'et', 'voluptatibus',
  11031. 'tempora', 'veritatis', 'ratione', 'assumenda', 'incidunt', 'nostrum',
  11032. 'placeat', 'aliquid', 'fuga', 'provident', 'praesentium', 'rem',
  11033. 'necessitatibus', 'suscipit', 'adipisci', 'quidem', 'possimus',
  11034. 'voluptas', 'debitis', 'sint', 'accusantium', 'unde', 'sapiente',
  11035. 'voluptate', 'qui', 'aspernatur', 'laudantium', 'soluta', 'amet',
  11036. 'quo', 'aliquam', 'saepe', 'culpa', 'libero', 'ipsa', 'dicta',
  11037. 'reiciendis', 'nesciunt', 'doloribus', 'autem', 'impedit', 'minima',
  11038. 'maiores', 'repudiandae', 'ipsam', 'obcaecati', 'ullam', 'enim',
  11039. 'totam', 'delectus', 'ducimus', 'quis', 'voluptates', 'dolores',
  11040. 'molestiae', 'harum', 'dolorem', 'quia', 'voluptatem', 'molestias',
  11041. 'magni', 'distinctio', 'omnis', 'illum', 'dolorum', 'voluptatum', 'ea',
  11042. 'quas', 'quam', 'corporis', 'quae', 'blanditiis', 'atque', 'deserunt',
  11043. 'laboriosam', 'earum', 'consequuntur', 'hic', 'cupiditate',
  11044. 'quibusdam', 'accusamus', 'ut', 'rerum', 'error', 'minus', 'eius',
  11045. 'ab', 'ad', 'nemo', 'fugit', 'officia', 'at', 'in', 'id', 'quos',
  11046. 'reprehenderit', 'numquam', 'iste', 'fugiat', 'sit', 'inventore',
  11047. 'beatae', 'repellendus', 'magnam', 'recusandae', 'quod', 'explicabo',
  11048. 'doloremque', 'aperiam', 'consequatur', 'asperiores', 'commodi',
  11049. 'optio', 'dolor', 'labore', 'temporibus', 'repellat', 'veniam',
  11050. 'architecto', 'est', 'esse', 'mollitia', 'nulla', 'a', 'similique',
  11051. 'eos', 'alias', 'dolore', 'tenetur', 'deleniti', 'porro', 'facere',
  11052. 'maxime', 'corrupti'];
  11053. /**
  11054. * Returns random integer between <code>from</code> and <code>to</code> values
  11055. * @param {Number} from
  11056. * @param {Number} to
  11057. * @returns {Number}
  11058. */
  11059. function randint(from, to) {
  11060. return Math.round(Math.random() * (to - from) + from);
  11061. }
  11062. /**
  11063. * @param {Array} arr
  11064. * @param {Number} count
  11065. * @returns {Array}
  11066. */
  11067. function sample(arr, count) {
  11068. var len = arr.length;
  11069. var iterations = Math.min(len, count);
  11070. var result = [];
  11071. while (result.length < iterations) {
  11072. var randIx = randint(0, len - 1);
  11073. if (!_.include(result, randIx))
  11074. result.push(randIx);
  11075. }
  11076. return _.map(result, function(ix) {
  11077. return arr[ix];
  11078. });
  11079. }
  11080. function choice(val) {
  11081. if (_.isString(val))
  11082. return val.charAt(randint(0, val.length - 1));
  11083. return val[randint(0, val.length - 1)];
  11084. }
  11085. function sentence(words, end) {
  11086. if (words.length) {
  11087. words[0] = words[0].charAt(0).toUpperCase() + words[0].substring(1);
  11088. }
  11089. return words.join(' ') + (end || choice('?!...')); // more dots that question marks
  11090. }
  11091. /**
  11092. * Insert commas at randomly selected words. This function modifies values
  11093. * inside <code>words</code> array
  11094. * @param {Array} words
  11095. */
  11096. function insertCommas(words) {
  11097. var len = words.length;
  11098. var totalCommas = 0;
  11099. if (len > 3 && len <= 6) {
  11100. totalCommas = randint(0, 1);
  11101. } else if (len > 6 && len <= 12) {
  11102. totalCommas = randint(0, 2);
  11103. } else {
  11104. totalCommas = randint(1, 4);
  11105. }
  11106. _.each(sample(_.range(totalCommas)), function(ix) {
  11107. words[ix] += ',';
  11108. });
  11109. }
  11110. /**
  11111. * Generate a paragraph of "Lorem ipsum" text
  11112. * @param {Number} wordCount Words count in paragraph
  11113. * @param {Boolean} startWithCommon Should paragraph start with common
  11114. * "lorem ipsum" sentence.
  11115. * @returns {String}
  11116. */
  11117. function paragraph(wordCount, startWithCommon) {
  11118. var result = [];
  11119. var totalWords = 0;
  11120. var words;
  11121. wordCount = parseInt(wordCount, 10);
  11122. if (startWithCommon) {
  11123. words = COMMON_P.slice(0, wordCount);
  11124. if (words.length > 5)
  11125. words[4] += ',';
  11126. totalWords += words.length;
  11127. result.push(sentence(words, '.'));
  11128. }
  11129. while (totalWords < wordCount) {
  11130. words = sample(WORDS, Math.min(randint(3, 12) * randint(1, 5), wordCount - totalWords));
  11131. totalWords += words.length;
  11132. insertCommas(words);
  11133. result.push(sentence(words));
  11134. }
  11135. return result.join(' ');
  11136. }
  11137. });
  11138. emmet.define('bootstrap', function(require, _) {
  11139. var snippets = {
  11140. "variables": {
  11141. "lang": "en",
  11142. "locale": "en-US",
  11143. "charset": "UTF-8",
  11144. "indentation": "\t",
  11145. "newline": "\n"
  11146. },
  11147. "css": {
  11148. "filters": "html",
  11149. "snippets": {
  11150. "@i": "@import url(|);",
  11151. "@import": "@import url(|);",
  11152. "@m": "@media ${1:screen} {\n\t|\n}",
  11153. "@media": "@media ${1:screen} {\n\t|\n}",
  11154. "@f": "@font-face {\n\tfont-family:|;\n\tsrc:url(|);\n}",
  11155. "@f+": "@font-face {\n\tfont-family: '${1:FontName}';\n\tsrc: url('${2:FileName}.eot');\n\tsrc: url('${2:FileName}.eot?#iefix') format('embedded-opentype'),\n\t\t url('${2:FileName}.woff') format('woff'),\n\t\t url('${2:FileName}.ttf') format('truetype'),\n\t\t url('${2:FileName}.svg#${1:FontName}') format('svg');\n\tfont-style: ${3:normal};\n\tfont-weight: ${4:normal};\n}",
  11156. "@kf": "@-webkit-keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}\n@-o-keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}\n@-moz-keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}\n@keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}",
  11157. "anim": "animation:|;",
  11158. "anim-": "animation:${1:name} ${2:duration} ${3:timing-function} ${4:delay} ${5:iteration-count} ${6:direction} ${7:fill-mode};",
  11159. "animdel": "animation-delay:${1:time};",
  11160. "animdir": "animation-direction:${1:normal};",
  11161. "animdir:n": "animation-direction:normal;",
  11162. "animdir:r": "animation-direction:reverse;",
  11163. "animdir:a": "animation-direction:alternate;",
  11164. "animdir:ar": "animation-direction:alternate-reverse;",
  11165. "animdur": "animation-duration:${1:0}s;",
  11166. "animfm": "animation-fill-mode:${1:both};",
  11167. "animfm:f": "animation-fill-mode:forwards;",
  11168. "animfm:b": "animation-fill-mode:backwards;",
  11169. "animfm:bt": "animation-fill-mode:both;",
  11170. "animfm:bh": "animation-fill-mode:both;",
  11171. "animic": "animation-iteration-count:${1:1};",
  11172. "animic:i": "animation-iteration-count:infinite;",
  11173. "animn": "animation-name:${1:none};",
  11174. "animps": "animation-play-state:${1:running};",
  11175. "animps:p": "animation-play-state:paused;",
  11176. "animps:r": "animation-play-state:running;",
  11177. "animtf": "animation-timing-function:${1:linear};",
  11178. "animtf:e": "animation-timing-function:ease;",
  11179. "animtf:ei": "animation-timing-function:ease-in;",
  11180. "animtf:eo": "animation-timing-function:ease-out;",
  11181. "animtf:eio": "animation-timing-function:ease-in-out;",
  11182. "animtf:l": "animation-timing-function:linear;",
  11183. "animtf:cb": "animation-timing-function:cubic-bezier(${1:0.1}, ${2:0.7}, ${3:1.0}, ${3:0.1});",
  11184. "ap": "appearance:${none};",
  11185. "!": "!important",
  11186. "pos": "position:${1:relative};",
  11187. "pos:s": "position:static;",
  11188. "pos:a": "position:absolute;",
  11189. "pos:r": "position:relative;",
  11190. "pos:f": "position:fixed;",
  11191. "t": "top:|;",
  11192. "t:a": "top:auto;",
  11193. "r": "right:|;",
  11194. "r:a": "right:auto;",
  11195. "b": "bottom:|;",
  11196. "b:a": "bottom:auto;",
  11197. "l": "left:|;",
  11198. "l:a": "left:auto;",
  11199. "z": "z-index:|;",
  11200. "z:a": "z-index:auto;",
  11201. "fl": "float:${1:left};",
  11202. "fl:n": "float:none;",
  11203. "fl:l": "float:left;",
  11204. "fl:r": "float:right;",
  11205. "cl": "clear:${1:both};",
  11206. "cl:n": "clear:none;",
  11207. "cl:l": "clear:left;",
  11208. "cl:r": "clear:right;",
  11209. "cl:b": "clear:both;",
  11210. "colm": "columns:|;",
  11211. "colmc": "column-count:|;",
  11212. "colmf": "column-fill:|;",
  11213. "colmg": "column-gap:|;",
  11214. "colmr": "column-rule:|;",
  11215. "colmrc": "column-rule-color:|;",
  11216. "colmrs": "column-rule-style:|;",
  11217. "colmrw": "column-rule-width:|;",
  11218. "colms": "column-span:|;",
  11219. "colmw": "column-width:|;",
  11220. "d": "display:${1:block};",
  11221. "d:n": "display:none;",
  11222. "d:b": "display:block;",
  11223. "d:i": "display:inline;",
  11224. "d:ib": "display:inline-block;",
  11225. "d:li": "display:list-item;",
  11226. "d:ri": "display:run-in;",
  11227. "d:cp": "display:compact;",
  11228. "d:tb": "display:table;",
  11229. "d:itb": "display:inline-table;",
  11230. "d:tbcp": "display:table-caption;",
  11231. "d:tbcl": "display:table-column;",
  11232. "d:tbclg": "display:table-column-group;",
  11233. "d:tbhg": "display:table-header-group;",
  11234. "d:tbfg": "display:table-footer-group;",
  11235. "d:tbr": "display:table-row;",
  11236. "d:tbrg": "display:table-row-group;",
  11237. "d:tbc": "display:table-cell;",
  11238. "d:rb": "display:ruby;",
  11239. "d:rbb": "display:ruby-base;",
  11240. "d:rbbg": "display:ruby-base-group;",
  11241. "d:rbt": "display:ruby-text;",
  11242. "d:rbtg": "display:ruby-text-group;",
  11243. "v": "visibility:${1:hidden};",
  11244. "v:v": "visibility:visible;",
  11245. "v:h": "visibility:hidden;",
  11246. "v:c": "visibility:collapse;",
  11247. "ov": "overflow:${1:hidden};",
  11248. "ov:v": "overflow:visible;",
  11249. "ov:h": "overflow:hidden;",
  11250. "ov:s": "overflow:scroll;",
  11251. "ov:a": "overflow:auto;",
  11252. "ovx": "overflow-x:${1:hidden};",
  11253. "ovx:v": "overflow-x:visible;",
  11254. "ovx:h": "overflow-x:hidden;",
  11255. "ovx:s": "overflow-x:scroll;",
  11256. "ovx:a": "overflow-x:auto;",
  11257. "ovy": "overflow-y:${1:hidden};",
  11258. "ovy:v": "overflow-y:visible;",
  11259. "ovy:h": "overflow-y:hidden;",
  11260. "ovy:s": "overflow-y:scroll;",
  11261. "ovy:a": "overflow-y:auto;",
  11262. "ovs": "overflow-style:${1:scrollbar};",
  11263. "ovs:a": "overflow-style:auto;",
  11264. "ovs:s": "overflow-style:scrollbar;",
  11265. "ovs:p": "overflow-style:panner;",
  11266. "ovs:m": "overflow-style:move;",
  11267. "ovs:mq": "overflow-style:marquee;",
  11268. "zoo": "zoom:1;",
  11269. "zm": "zoom:1;",
  11270. "cp": "clip:|;",
  11271. "cp:a": "clip:auto;",
  11272. "cp:r": "clip:rect(${1:top} ${2:right} ${3:bottom} ${4:left});",
  11273. "bxz": "box-sizing:${1:border-box};",
  11274. "bxz:cb": "box-sizing:content-box;",
  11275. "bxz:bb": "box-sizing:border-box;",
  11276. "bxsh": "box-shadow:${1:inset }${2:hoff} ${3:voff} ${4:blur} ${5:color};",
  11277. "bxsh:r": "box-shadow:${1:inset }${2:hoff} ${3:voff} ${4:blur} ${5:spread }rgb(${6:0}, ${7:0}, ${8:0});",
  11278. "bxsh:ra": "box-shadow:${1:inset }${2:h} ${3:v} ${4:blur} ${5:spread }rgba(${6:0}, ${7:0}, ${8:0}, .${9:5});",
  11279. "bxsh:n": "box-shadow:none;",
  11280. "m": "margin:|;",
  11281. "m:a": "margin:auto;",
  11282. "mt": "margin-top:|;",
  11283. "mt:a": "margin-top:auto;",
  11284. "mr": "margin-right:|;",
  11285. "mr:a": "margin-right:auto;",
  11286. "mb": "margin-bottom:|;",
  11287. "mb:a": "margin-bottom:auto;",
  11288. "ml": "margin-left:|;",
  11289. "ml:a": "margin-left:auto;",
  11290. "p": "padding:|;",
  11291. "pt": "padding-top:|;",
  11292. "pr": "padding-right:|;",
  11293. "pb": "padding-bottom:|;",
  11294. "pl": "padding-left:|;",
  11295. "w": "width:|;",
  11296. "w:a": "width:auto;",
  11297. "h": "height:|;",
  11298. "h:a": "height:auto;",
  11299. "maw": "max-width:|;",
  11300. "maw:n": "max-width:none;",
  11301. "mah": "max-height:|;",
  11302. "mah:n": "max-height:none;",
  11303. "miw": "min-width:|;",
  11304. "mih": "min-height:|;",
  11305. "mar": "max-resolution:${1:res};",
  11306. "mir": "min-resolution:${1:res};",
  11307. "ori": "orientation:|;",
  11308. "ori:l": "orientation:landscape;",
  11309. "ori:p": "orientation:portrait;",
  11310. "ol": "outline:|;",
  11311. "ol:n": "outline:none;",
  11312. "olo": "outline-offset:|;",
  11313. "olw": "outline-width:|;",
  11314. "ols": "outline-style:|;",
  11315. "olc": "outline-color:#${1:000};",
  11316. "olc:i": "outline-color:invert;",
  11317. "bd": "border:|;",
  11318. "bd+": "border:${1:1px} ${2:solid} ${3:#000};",
  11319. "bd:n": "border:none;",
  11320. "bdbk": "border-break:${1:close};",
  11321. "bdbk:c": "border-break:close;",
  11322. "bdcl": "border-collapse:|;",
  11323. "bdcl:c": "border-collapse:collapse;",
  11324. "bdcl:s": "border-collapse:separate;",
  11325. "bdc": "border-color:#${1:000};",
  11326. "bdc:t": "border-color:transparent;",
  11327. "bdi": "border-image:url(|);",
  11328. "bdi:n": "border-image:none;",
  11329. "bdti": "border-top-image:url(|);",
  11330. "bdti:n": "border-top-image:none;",
  11331. "bdri": "border-right-image:url(|);",
  11332. "bdri:n": "border-right-image:none;",
  11333. "bdbi": "border-bottom-image:url(|);",
  11334. "bdbi:n": "border-bottom-image:none;",
  11335. "bdli": "border-left-image:url(|);",
  11336. "bdli:n": "border-left-image:none;",
  11337. "bdci": "border-corner-image:url(|);",
  11338. "bdci:n": "border-corner-image:none;",
  11339. "bdci:c": "border-corner-image:continue;",
  11340. "bdtli": "border-top-left-image:url(|);",
  11341. "bdtli:n": "border-top-left-image:none;",
  11342. "bdtli:c": "border-top-left-image:continue;",
  11343. "bdtri": "border-top-right-image:url(|);",
  11344. "bdtri:n": "border-top-right-image:none;",
  11345. "bdtri:c": "border-top-right-image:continue;",
  11346. "bdbri": "border-bottom-right-image:url(|);",
  11347. "bdbri:n": "border-bottom-right-image:none;",
  11348. "bdbri:c": "border-bottom-right-image:continue;",
  11349. "bdbli": "border-bottom-left-image:url(|);",
  11350. "bdbli:n": "border-bottom-left-image:none;",
  11351. "bdbli:c": "border-bottom-left-image:continue;",
  11352. "bdf": "border-fit:${1:repeat};",
  11353. "bdf:c": "border-fit:clip;",
  11354. "bdf:r": "border-fit:repeat;",
  11355. "bdf:sc": "border-fit:scale;",
  11356. "bdf:st": "border-fit:stretch;",
  11357. "bdf:ow": "border-fit:overwrite;",
  11358. "bdf:of": "border-fit:overflow;",
  11359. "bdf:sp": "border-fit:space;",
  11360. "bdlen": "border-length:|;",
  11361. "bdlen:a": "border-length:auto;",
  11362. "bdsp": "border-spacing:|;",
  11363. "bds": "border-style:|;",
  11364. "bds:n": "border-style:none;",
  11365. "bds:h": "border-style:hidden;",
  11366. "bds:dt": "border-style:dotted;",
  11367. "bds:ds": "border-style:dashed;",
  11368. "bds:s": "border-style:solid;",
  11369. "bds:db": "border-style:double;",
  11370. "bds:dtds": "border-style:dot-dash;",
  11371. "bds:dtdtds": "border-style:dot-dot-dash;",
  11372. "bds:w": "border-style:wave;",
  11373. "bds:g": "border-style:groove;",
  11374. "bds:r": "border-style:ridge;",
  11375. "bds:i": "border-style:inset;",
  11376. "bds:o": "border-style:outset;",
  11377. "bdw": "border-width:|;",
  11378. "bdtw": "border-top-width:|;",
  11379. "bdrw": "border-right-width:|;",
  11380. "bdbw": "border-bottom-width:|;",
  11381. "bdlw": "border-left-width:|;",
  11382. "bdt": "border-top:|;",
  11383. "bt": "border-top:|;",
  11384. "bdt+": "border-top:${1:1px} ${2:solid} ${3:#000};",
  11385. "bdt:n": "border-top:none;",
  11386. "bdts": "border-top-style:|;",
  11387. "bdts:n": "border-top-style:none;",
  11388. "bdtc": "border-top-color:#${1:000};",
  11389. "bdtc:t": "border-top-color:transparent;",
  11390. "bdr": "border-right:|;",
  11391. "br": "border-right:|;",
  11392. "bdr+": "border-right:${1:1px} ${2:solid} ${3:#000};",
  11393. "bdr:n": "border-right:none;",
  11394. "bdrst": "border-right-style:|;",
  11395. "bdrst:n": "border-right-style:none;",
  11396. "bdrc": "border-right-color:#${1:000};",
  11397. "bdrc:t": "border-right-color:transparent;",
  11398. "bdb": "border-bottom:|;",
  11399. "bb": "border-bottom:|;",
  11400. "bdb+": "border-bottom:${1:1px} ${2:solid} ${3:#000};",
  11401. "bdb:n": "border-bottom:none;",
  11402. "bdbs": "border-bottom-style:|;",
  11403. "bdbs:n": "border-bottom-style:none;",
  11404. "bdbc": "border-bottom-color:#${1:000};",
  11405. "bdbc:t": "border-bottom-color:transparent;",
  11406. "bdl": "border-left:|;",
  11407. "bl": "border-left:|;",
  11408. "bdl+": "border-left:${1:1px} ${2:solid} ${3:#000};",
  11409. "bdl:n": "border-left:none;",
  11410. "bdls": "border-left-style:|;",
  11411. "bdls:n": "border-left-style:none;",
  11412. "bdlc": "border-left-color:#${1:000};",
  11413. "bdlc:t": "border-left-color:transparent;",
  11414. "bdrs": "border-radius:|;",
  11415. "bdtrrs": "border-top-right-radius:|;",
  11416. "bdtlrs": "border-top-left-radius:|;",
  11417. "bdbrrs": "border-bottom-right-radius:|;",
  11418. "bdblrs": "border-bottom-left-radius:|;",
  11419. "bg": "background:|;",
  11420. "bg+": "background:${1:#fff} url(${2}) ${3:0} ${4:0} ${5:no-repeat};",
  11421. "bg:n": "background:none;",
  11422. "bg:ie": "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='${1:x}.png',sizingMethod='${2:crop}');",
  11423. "bgc": "background-color:#${1:fff};",
  11424. "bgc:t": "background-color:transparent;",
  11425. "bgi": "background-image:url(|);",
  11426. "bgi:n": "background-image:none;",
  11427. "bgr": "background-repeat:|;",
  11428. "bgr:n": "background-repeat:no-repeat;",
  11429. "bgr:x": "background-repeat:repeat-x;",
  11430. "bgr:y": "background-repeat:repeat-y;",
  11431. "bgr:sp": "background-repeat:space;",
  11432. "bgr:rd": "background-repeat:round;",
  11433. "bga": "background-attachment:|;",
  11434. "bga:f": "background-attachment:fixed;",
  11435. "bga:s": "background-attachment:scroll;",
  11436. "bgp": "background-position:${1:0} ${2:0};",
  11437. "bgpx": "background-position-x:|;",
  11438. "bgpy": "background-position-y:|;",
  11439. "bgbk": "background-break:|;",
  11440. "bgbk:bb": "background-break:bounding-box;",
  11441. "bgbk:eb": "background-break:each-box;",
  11442. "bgbk:c": "background-break:continuous;",
  11443. "bgcp": "background-clip:${1:padding-box};",
  11444. "bgcp:bb": "background-clip:border-box;",
  11445. "bgcp:pb": "background-clip:padding-box;",
  11446. "bgcp:cb": "background-clip:content-box;",
  11447. "bgcp:nc": "background-clip:no-clip;",
  11448. "bgo": "background-origin:|;",
  11449. "bgo:pb": "background-origin:padding-box;",
  11450. "bgo:bb": "background-origin:border-box;",
  11451. "bgo:cb": "background-origin:content-box;",
  11452. "bgsz": "background-size:|;",
  11453. "bgsz:a": "background-size:auto;",
  11454. "bgsz:ct": "background-size:contain;",
  11455. "bgsz:cv": "background-size:cover;",
  11456. "c": "color:#${1:000};",
  11457. "c:r": "color:rgb(${1:0}, ${2:0}, ${3:0});",
  11458. "c:ra": "color:rgba(${1:0}, ${2:0}, ${3:0}, .${4:5});",
  11459. "cm": "/* |${child} */",
  11460. "cnt": "content:'|';",
  11461. "cnt:n": "content:normal;",
  11462. "cnt:oq": "content:open-quote;",
  11463. "cnt:noq": "content:no-open-quote;",
  11464. "cnt:cq": "content:close-quote;",
  11465. "cnt:ncq": "content:no-close-quote;",
  11466. "cnt:a": "content:attr(|);",
  11467. "cnt:c": "content:counter(|);",
  11468. "cnt:cs": "content:counters(|);",
  11469. "tbl": "table-layout:|;",
  11470. "tbl:a": "table-layout:auto;",
  11471. "tbl:f": "table-layout:fixed;",
  11472. "cps": "caption-side:|;",
  11473. "cps:t": "caption-side:top;",
  11474. "cps:b": "caption-side:bottom;",
  11475. "ec": "empty-cells:|;",
  11476. "ec:s": "empty-cells:show;",
  11477. "ec:h": "empty-cells:hide;",
  11478. "lis": "list-style:|;",
  11479. "lis:n": "list-style:none;",
  11480. "lisp": "list-style-position:|;",
  11481. "lisp:i": "list-style-position:inside;",
  11482. "lisp:o": "list-style-position:outside;",
  11483. "list": "list-style-type:|;",
  11484. "list:n": "list-style-type:none;",
  11485. "list:d": "list-style-type:disc;",
  11486. "list:c": "list-style-type:circle;",
  11487. "list:s": "list-style-type:square;",
  11488. "list:dc": "list-style-type:decimal;",
  11489. "list:dclz": "list-style-type:decimal-leading-zero;",
  11490. "list:lr": "list-style-type:lower-roman;",
  11491. "list:ur": "list-style-type:upper-roman;",
  11492. "lisi": "list-style-image:|;",
  11493. "lisi:n": "list-style-image:none;",
  11494. "q": "quotes:|;",
  11495. "q:n": "quotes:none;",
  11496. "q:ru": "quotes:'\\00AB' '\\00BB' '\\201E' '\\201C';",
  11497. "q:en": "quotes:'\\201C' '\\201D' '\\2018' '\\2019';",
  11498. "ct": "content:|;",
  11499. "ct:n": "content:normal;",
  11500. "ct:oq": "content:open-quote;",
  11501. "ct:noq": "content:no-open-quote;",
  11502. "ct:cq": "content:close-quote;",
  11503. "ct:ncq": "content:no-close-quote;",
  11504. "ct:a": "content:attr(|);",
  11505. "ct:c": "content:counter(|);",
  11506. "ct:cs": "content:counters(|);",
  11507. "coi": "counter-increment:|;",
  11508. "cor": "counter-reset:|;",
  11509. "va": "vertical-align:${1:top};",
  11510. "va:sup": "vertical-align:super;",
  11511. "va:t": "vertical-align:top;",
  11512. "va:tt": "vertical-align:text-top;",
  11513. "va:m": "vertical-align:middle;",
  11514. "va:bl": "vertical-align:baseline;",
  11515. "va:b": "vertical-align:bottom;",
  11516. "va:tb": "vertical-align:text-bottom;",
  11517. "va:sub": "vertical-align:sub;",
  11518. "ta": "text-align:${1:left};",
  11519. "ta:l": "text-align:left;",
  11520. "ta:c": "text-align:center;",
  11521. "ta:r": "text-align:right;",
  11522. "ta:j": "text-align:justify;",
  11523. "ta-lst": "text-align-last:|;",
  11524. "tal:a": "text-align-last:auto;",
  11525. "tal:l": "text-align-last:left;",
  11526. "tal:c": "text-align-last:center;",
  11527. "tal:r": "text-align-last:right;",
  11528. "td": "text-decoration:${1:none};",
  11529. "td:n": "text-decoration:none;",
  11530. "td:u": "text-decoration:underline;",
  11531. "td:o": "text-decoration:overline;",
  11532. "td:l": "text-decoration:line-through;",
  11533. "te": "text-emphasis:|;",
  11534. "te:n": "text-emphasis:none;",
  11535. "te:ac": "text-emphasis:accent;",
  11536. "te:dt": "text-emphasis:dot;",
  11537. "te:c": "text-emphasis:circle;",
  11538. "te:ds": "text-emphasis:disc;",
  11539. "te:b": "text-emphasis:before;",
  11540. "te:a": "text-emphasis:after;",
  11541. "th": "text-height:|;",
  11542. "th:a": "text-height:auto;",
  11543. "th:f": "text-height:font-size;",
  11544. "th:t": "text-height:text-size;",
  11545. "th:m": "text-height:max-size;",
  11546. "ti": "text-indent:|;",
  11547. "ti:-": "text-indent:-9999px;",
  11548. "tj": "text-justify:|;",
  11549. "tj:a": "text-justify:auto;",
  11550. "tj:iw": "text-justify:inter-word;",
  11551. "tj:ii": "text-justify:inter-ideograph;",
  11552. "tj:ic": "text-justify:inter-cluster;",
  11553. "tj:d": "text-justify:distribute;",
  11554. "tj:k": "text-justify:kashida;",
  11555. "tj:t": "text-justify:tibetan;",
  11556. "tov": "text-overflow:${ellipsis};",
  11557. "tov:e": "text-overflow:ellipsis;",
  11558. "tov:c": "text-overflow:clip;",
  11559. "to": "text-outline:|;",
  11560. "to+": "text-outline:${1:0} ${2:0} ${3:#000};",
  11561. "to:n": "text-outline:none;",
  11562. "tr": "text-replace:|;",
  11563. "tr:n": "text-replace:none;",
  11564. "tt": "text-transform:${1:uppercase};",
  11565. "tt:n": "text-transform:none;",
  11566. "tt:c": "text-transform:capitalize;",
  11567. "tt:u": "text-transform:uppercase;",
  11568. "tt:l": "text-transform:lowercase;",
  11569. "tw": "text-wrap:|;",
  11570. "tw:n": "text-wrap:normal;",
  11571. "tw:no": "text-wrap:none;",
  11572. "tw:u": "text-wrap:unrestricted;",
  11573. "tw:s": "text-wrap:suppress;",
  11574. "tsh": "text-shadow:${1:hoff} ${2:voff} ${3:blur} ${4:#000};",
  11575. "tsh:r": "text-shadow:${1:h} ${2:v} ${3:blur} rgb(${4:0}, ${5:0}, ${6:0});",
  11576. "tsh:ra": "text-shadow:${1:h} ${2:v} ${3:blur} rgba(${4:0}, ${5:0}, ${6:0}, .${7:5});",
  11577. "tsh+": "text-shadow:${1:0} ${2:0} ${3:0} ${4:#000};",
  11578. "tsh:n": "text-shadow:none;",
  11579. "trf": "transform:|;",
  11580. "trf:skx": "transform: skewX(${1:angle});",
  11581. "trf:sky": "transform: skewY(${1:angle});",
  11582. "trf:sc": "transform: scale(${1:x}, ${2:y});",
  11583. "trf:scx": "transform: scaleX(${1:x});",
  11584. "trf:scy": "transform: scaleY(${1:y});",
  11585. "trf:r": "transform: rotate(${1:angle});",
  11586. "trf:t": "transform: translate(${1:x}, ${2:y});",
  11587. "trf:tx": "transform: translateX(${1:x});",
  11588. "trf:ty": "transform: translateY(${1:y});",
  11589. "trfo": "transform-origin:|;",
  11590. "trfs": "transform-style:${1:preserve-3d};",
  11591. "trs": "transition:${1:prop} ${2:time};",
  11592. "trsde": "transition-delay:${1:time};",
  11593. "trsdu": "transition-duration:${1:time};",
  11594. "trsp": "transition-property:${1:prop};",
  11595. "trstf": "transition-timing-function:${1:tfunc};",
  11596. "lh": "line-height:|;",
  11597. "whs": "white-space:|;",
  11598. "whs:n": "white-space:normal;",
  11599. "whs:p": "white-space:pre;",
  11600. "whs:nw": "white-space:nowrap;",
  11601. "whs:pw": "white-space:pre-wrap;",
  11602. "whs:pl": "white-space:pre-line;",
  11603. "whsc": "white-space-collapse:|;",
  11604. "whsc:n": "white-space-collapse:normal;",
  11605. "whsc:k": "white-space-collapse:keep-all;",
  11606. "whsc:l": "white-space-collapse:loose;",
  11607. "whsc:bs": "white-space-collapse:break-strict;",
  11608. "whsc:ba": "white-space-collapse:break-all;",
  11609. "wob": "word-break:|;",
  11610. "wob:n": "word-break:normal;",
  11611. "wob:k": "word-break:keep-all;",
  11612. "wob:l": "word-break:loose;",
  11613. "wob:bs": "word-break:break-strict;",
  11614. "wob:ba": "word-break:break-all;",
  11615. "wos": "word-spacing:|;",
  11616. "wow": "word-wrap:|;",
  11617. "wow:nm": "word-wrap:normal;",
  11618. "wow:n": "word-wrap:none;",
  11619. "wow:u": "word-wrap:unrestricted;",
  11620. "wow:s": "word-wrap:suppress;",
  11621. "lts": "letter-spacing:|;",
  11622. "f": "font:|;",
  11623. "f+": "font:${1:1em} ${2:Arial,sans-serif};",
  11624. "fw": "font-weight:|;",
  11625. "fw:n": "font-weight:normal;",
  11626. "fw:b": "font-weight:bold;",
  11627. "fw:br": "font-weight:bolder;",
  11628. "fw:lr": "font-weight:lighter;",
  11629. "fs": "font-style:${italic};",
  11630. "fs:n": "font-style:normal;",
  11631. "fs:i": "font-style:italic;",
  11632. "fs:o": "font-style:oblique;",
  11633. "fv": "font-variant:|;",
  11634. "fv:n": "font-variant:normal;",
  11635. "fv:sc": "font-variant:small-caps;",
  11636. "fz": "font-size:|;",
  11637. "fza": "font-size-adjust:|;",
  11638. "fza:n": "font-size-adjust:none;",
  11639. "ff": "font-family:|;",
  11640. "ff:s": "font-family:serif;",
  11641. "ff:ss": "font-family:sans-serif;",
  11642. "ff:c": "font-family:cursive;",
  11643. "ff:f": "font-family:fantasy;",
  11644. "ff:m": "font-family:monospace;",
  11645. "fef": "font-effect:|;",
  11646. "fef:n": "font-effect:none;",
  11647. "fef:eg": "font-effect:engrave;",
  11648. "fef:eb": "font-effect:emboss;",
  11649. "fef:o": "font-effect:outline;",
  11650. "fem": "font-emphasize:|;",
  11651. "femp": "font-emphasize-position:|;",
  11652. "femp:b": "font-emphasize-position:before;",
  11653. "femp:a": "font-emphasize-position:after;",
  11654. "fems": "font-emphasize-style:|;",
  11655. "fems:n": "font-emphasize-style:none;",
  11656. "fems:ac": "font-emphasize-style:accent;",
  11657. "fems:dt": "font-emphasize-style:dot;",
  11658. "fems:c": "font-emphasize-style:circle;",
  11659. "fems:ds": "font-emphasize-style:disc;",
  11660. "fsm": "font-smooth:|;",
  11661. "fsm:a": "font-smooth:auto;",
  11662. "fsm:n": "font-smooth:never;",
  11663. "fsm:aw": "font-smooth:always;",
  11664. "fst": "font-stretch:|;",
  11665. "fst:n": "font-stretch:normal;",
  11666. "fst:uc": "font-stretch:ultra-condensed;",
  11667. "fst:ec": "font-stretch:extra-condensed;",
  11668. "fst:c": "font-stretch:condensed;",
  11669. "fst:sc": "font-stretch:semi-condensed;",
  11670. "fst:se": "font-stretch:semi-expanded;",
  11671. "fst:e": "font-stretch:expanded;",
  11672. "fst:ee": "font-stretch:extra-expanded;",
  11673. "fst:ue": "font-stretch:ultra-expanded;",
  11674. "op": "opacity:|;",
  11675. "op:ie": "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);",
  11676. "op:ms": "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';",
  11677. "rsz": "resize:|;",
  11678. "rsz:n": "resize:none;",
  11679. "rsz:b": "resize:both;",
  11680. "rsz:h": "resize:horizontal;",
  11681. "rsz:v": "resize:vertical;",
  11682. "cur": "cursor:${pointer};",
  11683. "cur:a": "cursor:auto;",
  11684. "cur:d": "cursor:default;",
  11685. "cur:c": "cursor:crosshair;",
  11686. "cur:ha": "cursor:hand;",
  11687. "cur:he": "cursor:help;",
  11688. "cur:m": "cursor:move;",
  11689. "cur:p": "cursor:pointer;",
  11690. "cur:t": "cursor:text;",
  11691. "pgbb": "page-break-before:|;",
  11692. "pgbb:au": "page-break-before:auto;",
  11693. "pgbb:al": "page-break-before:always;",
  11694. "pgbb:l": "page-break-before:left;",
  11695. "pgbb:r": "page-break-before:right;",
  11696. "pgbi": "page-break-inside:|;",
  11697. "pgbi:au": "page-break-inside:auto;",
  11698. "pgbi:av": "page-break-inside:avoid;",
  11699. "pgba": "page-break-after:|;",
  11700. "pgba:au": "page-break-after:auto;",
  11701. "pgba:al": "page-break-after:always;",
  11702. "pgba:l": "page-break-after:left;",
  11703. "pgba:r": "page-break-after:right;",
  11704. "orp": "orphans:|;",
  11705. "us": "user-select:${none};",
  11706. "wid": "widows:|;",
  11707. "wfsm": "-webkit-font-smoothing:${antialiased};",
  11708. "wfsm:a": "-webkit-font-smoothing:antialiased;",
  11709. "wfsm:s": "-webkit-font-smoothing:subpixel-antialiased;",
  11710. "wfsm:sa": "-webkit-font-smoothing:subpixel-antialiased;",
  11711. "wfsm:n": "-webkit-font-smoothing:none;"
  11712. }
  11713. },
  11714. "html": {
  11715. "filters": "html",
  11716. "profile": "html",
  11717. "snippets": {
  11718. "!!!": "<!doctype html>",
  11719. "!!!4t": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">",
  11720. "!!!4s": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">",
  11721. "!!!xt": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">",
  11722. "!!!xs": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">",
  11723. "!!!xxs": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">",
  11724. "c": "<!-- |${child} -->",
  11725. "cc:ie6": "<!--[if lte IE 6]>\n\t${child}|\n<![endif]-->",
  11726. "cc:ie": "<!--[if IE]>\n\t${child}|\n<![endif]-->",
  11727. "cc:noie": "<!--[if !IE]><!-->\n\t${child}|\n<!--<![endif]-->"
  11728. },
  11729. "abbreviations": {
  11730. "!": "html:5",
  11731. "a": "<a href=\"\">",
  11732. "a:link": "<a href=\"http://|\">",
  11733. "a:mail": "<a href=\"mailto:|\">",
  11734. "abbr": "<abbr title=\"\">",
  11735. "acronym": "<acronym title=\"\">",
  11736. "base": "<base href=\"\" />",
  11737. "basefont": "<basefont/>",
  11738. "br": "<br/>",
  11739. "frame": "<frame/>",
  11740. "hr": "<hr/>",
  11741. "bdo": "<bdo dir=\"\">",
  11742. "bdo:r": "<bdo dir=\"rtl\">",
  11743. "bdo:l": "<bdo dir=\"ltr\">",
  11744. "col": "<col/>",
  11745. "link": "<link rel=\"stylesheet\" href=\"\" />",
  11746. "link:css": "<link rel=\"stylesheet\" href=\"${1:style}.css\" media=\"all\" />",
  11747. "link:print": "<link rel=\"stylesheet\" href=\"${1:print}.css\" media=\"print\" />",
  11748. "link:favicon": "<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"${1:favicon.ico}\" />",
  11749. "link:touch": "<link rel=\"apple-touch-icon\" href=\"${1:favicon.png}\" />",
  11750. "link:rss": "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS\" href=\"${1:rss.xml}\" />",
  11751. "link:atom": "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"Atom\" href=\"${1:atom.xml}\" />",
  11752. "meta": "<meta/>",
  11753. "meta:utf": "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\" />",
  11754. "meta:win": "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=windows-1251\" />",
  11755. "meta:vp": "<meta name=\"viewport\" content=\"width=${1:device-width}, user-scalable=${2:no}, initial-scale=${3:1.0}, maximum-scale=${4:1.0}, minimum-scale=${5:1.0}\" />",
  11756. "meta:compat": "<meta http-equiv=\"X-UA-Compatible\" content=\"${1:IE=7}\" />",
  11757. "style": "<style>",
  11758. "script": "<script>",
  11759. "script:src": "<script src=\"\">",
  11760. "img": "<img src=\"\" alt=\"\" />",
  11761. "iframe": "<iframe src=\"\" frameborder=\"0\">",
  11762. "embed": "<embed src=\"\" type=\"\" />",
  11763. "object": "<object data=\"\" type=\"\">",
  11764. "param": "<param name=\"\" value=\"\" />",
  11765. "map": "<map name=\"\">",
  11766. "area": "<area shape=\"\" coords=\"\" href=\"\" alt=\"\" />",
  11767. "area:d": "<area shape=\"default\" href=\"\" alt=\"\" />",
  11768. "area:c": "<area shape=\"circle\" coords=\"\" href=\"\" alt=\"\" />",
  11769. "area:r": "<area shape=\"rect\" coords=\"\" href=\"\" alt=\"\" />",
  11770. "area:p": "<area shape=\"poly\" coords=\"\" href=\"\" alt=\"\" />",
  11771. "form": "<form action=\"\">",
  11772. "form:get": "<form action=\"\" method=\"get\">",
  11773. "form:post": "<form action=\"\" method=\"post\">",
  11774. "label": "<label for=\"\">",
  11775. "input": "<input type=\"${1:text}\" />",
  11776. "inp": "<input type=\"${1:text}\" name=\"\" id=\"\" />",
  11777. "input:hidden": "input[type=hidden name]",
  11778. "input:h": "input:hidden",
  11779. "input:text": "inp",
  11780. "input:t": "inp",
  11781. "input:search": "inp[type=search]",
  11782. "input:email": "inp[type=email]",
  11783. "input:url": "inp[type=url]",
  11784. "input:password": "inp[type=password]",
  11785. "input:p": "input:password",
  11786. "input:datetime": "inp[type=datetime]",
  11787. "input:date": "inp[type=date]",
  11788. "input:datetime-local": "inp[type=datetime-local]",
  11789. "input:month": "inp[type=month]",
  11790. "input:week": "inp[type=week]",
  11791. "input:time": "inp[type=time]",
  11792. "input:number": "inp[type=number]",
  11793. "input:color": "inp[type=color]",
  11794. "input:checkbox": "inp[type=checkbox]",
  11795. "input:c": "input:checkbox",
  11796. "input:radio": "inp[type=radio]",
  11797. "input:r": "input:radio",
  11798. "input:range": "inp[type=range]",
  11799. "input:file": "inp[type=file]",
  11800. "input:f": "input:file",
  11801. "input:submit": "<input type=\"submit\" value=\"\" />",
  11802. "input:s": "input:submit",
  11803. "input:image": "<input type=\"image\" src=\"\" alt=\"\" />",
  11804. "input:i": "input:image",
  11805. "input:button": "<input type=\"button\" value=\"\" />",
  11806. "input:b": "input:button",
  11807. "isindex": "<isindex/>",
  11808. "input:reset": "input:button[type=reset]",
  11809. "select": "<select name=\"\" id=\"\">",
  11810. "option": "<option value=\"\">",
  11811. "textarea": "<textarea name=\"\" id=\"\" cols=\"${1:30}\" rows=\"${2:10}\">",
  11812. "menu:context": "menu[type=context]>",
  11813. "menu:c": "menu:context",
  11814. "menu:toolbar": "menu[type=toolbar]>",
  11815. "menu:t": "menu:toolbar",
  11816. "video": "<video src=\"\">",
  11817. "audio": "<audio src=\"\">",
  11818. "html:xml": "<html xmlns=\"http://www.w3.org/1999/xhtml\">",
  11819. "keygen": "<keygen/>",
  11820. "command": "<command/>",
  11821. "bq": "blockquote",
  11822. "acr": "acronym",
  11823. "fig": "figure",
  11824. "figc": "figcaption",
  11825. "ifr": "iframe",
  11826. "emb": "embed",
  11827. "obj": "object",
  11828. "src": "source",
  11829. "cap": "caption",
  11830. "colg": "colgroup",
  11831. "fst": "fieldset",
  11832. "btn": "button",
  11833. "btn:b": "button[type=button]",
  11834. "btn:r": "button[type=reset]",
  11835. "btn:s": "button[type=submit]",
  11836. "optg": "optgroup",
  11837. "opt": "option",
  11838. "tarea": "textarea",
  11839. "leg": "legend",
  11840. "sect": "section",
  11841. "art": "article",
  11842. "hdr": "header",
  11843. "ftr": "footer",
  11844. "adr": "address",
  11845. "dlg": "dialog",
  11846. "str": "strong",
  11847. "prog": "progress",
  11848. "fset": "fieldset",
  11849. "datag": "datagrid",
  11850. "datal": "datalist",
  11851. "kg": "keygen",
  11852. "out": "output",
  11853. "det": "details",
  11854. "cmd": "command",
  11855. "doc": "html>(head>meta[charset=UTF-8]+title{${1:Document}})+body",
  11856. "doc4": "html>(head>meta[http-equiv=\"Content-Type\" content=\"text/html;charset=${charset}\"]+title{${1:Document}})",
  11857. "html:4t": "!!!4t+doc4[lang=${lang}]",
  11858. "html:4s": "!!!4s+doc4[lang=${lang}]",
  11859. "html:xt": "!!!xt+doc4[xmlns=http://www.w3.org/1999/xhtml xml:lang=${lang}]",
  11860. "html:xs": "!!!xs+doc4[xmlns=http://www.w3.org/1999/xhtml xml:lang=${lang}]",
  11861. "html:xxs": "!!!xxs+doc4[xmlns=http://www.w3.org/1999/xhtml xml:lang=${lang}]",
  11862. "html:5": "!!!+doc[lang=${lang}]",
  11863. "ol+": "ol>li",
  11864. "ul+": "ul>li",
  11865. "dl+": "dl>dt+dd",
  11866. "map+": "map>area",
  11867. "table+": "table>tr>td",
  11868. "colgroup+": "colgroup>col",
  11869. "colg+": "colgroup>col",
  11870. "tr+": "tr>td",
  11871. "select+": "select>option",
  11872. "optgroup+": "optgroup>option",
  11873. "optg+": "optgroup>option"
  11874. }
  11875. },
  11876. "xml": {
  11877. "extends": "html",
  11878. "profile": "xml",
  11879. "filters": "html"
  11880. },
  11881. "xsl": {
  11882. "extends": "html",
  11883. "profile": "xml",
  11884. "filters": "html, xsl",
  11885. "abbreviations": {
  11886. "tm": "<xsl:template match=\"\" mode=\"\">",
  11887. "tmatch": "tm",
  11888. "tn": "<xsl:template name=\"\">",
  11889. "tname": "tn",
  11890. "call": "<xsl:call-template name=\"\"/>",
  11891. "ap": "<xsl:apply-templates select=\"\" mode=\"\"/>",
  11892. "api": "<xsl:apply-imports/>",
  11893. "imp": "<xsl:import href=\"\"/>",
  11894. "inc": "<xsl:include href=\"\"/>",
  11895. "ch": "<xsl:choose>",
  11896. "xsl:when": "<xsl:when test=\"\">",
  11897. "wh": "xsl:when",
  11898. "ot": "<xsl:otherwise>",
  11899. "if": "<xsl:if test=\"\">",
  11900. "par": "<xsl:param name=\"\">",
  11901. "pare": "<xsl:param name=\"\" select=\"\"/>",
  11902. "var": "<xsl:variable name=\"\">",
  11903. "vare": "<xsl:variable name=\"\" select=\"\"/>",
  11904. "wp": "<xsl:with-param name=\"\" select=\"\"/>",
  11905. "key": "<xsl:key name=\"\" match=\"\" use=\"\"/>",
  11906. "elem": "<xsl:element name=\"\">",
  11907. "attr": "<xsl:attribute name=\"\">",
  11908. "attrs": "<xsl:attribute-set name=\"\">",
  11909. "cp": "<xsl:copy select=\"\"/>",
  11910. "co": "<xsl:copy-of select=\"\"/>",
  11911. "val": "<xsl:value-of select=\"\"/>",
  11912. "each": "<xsl:for-each select=\"\">",
  11913. "for": "each",
  11914. "tex": "<xsl:text></xsl:text>",
  11915. "com": "<xsl:comment>",
  11916. "msg": "<xsl:message terminate=\"no\">",
  11917. "fall": "<xsl:fallback>",
  11918. "num": "<xsl:number value=\"\"/>",
  11919. "nam": "<namespace-alias stylesheet-prefix=\"\" result-prefix=\"\"/>",
  11920. "pres": "<xsl:preserve-space elements=\"\"/>",
  11921. "strip": "<xsl:strip-space elements=\"\"/>",
  11922. "proc": "<xsl:processing-instruction name=\"\">",
  11923. "sort": "<xsl:sort select=\"\" order=\"\"/>",
  11924. "choose+": "xsl:choose>xsl:when+xsl:otherwise",
  11925. "xsl": "!!!+xsl:stylesheet[version=1.0 xmlns:xsl=http://www.w3.org/1999/XSL/Transform]>{\n|}"
  11926. },
  11927. "snippets": {
  11928. "!!!": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
  11929. }
  11930. },
  11931. "haml": {
  11932. "filters": "haml",
  11933. "extends": "html",
  11934. "profile": "xml"
  11935. },
  11936. "scss": {
  11937. "extends": "css"
  11938. },
  11939. "sass": {
  11940. "extends": "css"
  11941. },
  11942. "less": {
  11943. "extends": "css"
  11944. },
  11945. "stylus": {
  11946. "extends": "css"
  11947. }
  11948. }
  11949. var res = require('resources');
  11950. var userData = res.getVocabulary('user') || {};
  11951. res.setVocabulary(require('utils').deepMerge(userData, snippets), 'user');
  11952. });
  11953. window.emmet = emmet;
  11954. })();