csslint-worker.js 362 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961
  1. /*!
  2. CSSLint v1.0.4
  3. Copyright (c) 2016 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the 'Software'), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. */
  20. var exports = exports || {};
  21. /*!
  22. Parser-Lib
  23. Copyright (c) 2009-2016 Nicholas C. Zakas. All rights reserved.
  24. Permission is hereby granted, free of charge, to any person obtaining a copy
  25. of this software and associated documentation files (the "Software"), to deal
  26. in the Software without restriction, including without limitation the rights
  27. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  28. copies of the Software, and to permit persons to whom the Software is
  29. furnished to do so, subject to the following conditions:
  30. The above copyright notice and this permission notice shall be included in
  31. all copies or substantial portions of the Software.
  32. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  33. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  34. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  35. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  36. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  37. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  38. THE SOFTWARE.
  39. */
  40. /* Version v1.1.0, Build time: 6-December-2016 10:31:29 */
  41. var parserlib = (function () {
  42. var require;
  43. require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  44. "use strict";
  45. /* exported Colors */
  46. var Colors = module.exports = {
  47. __proto__ :null,
  48. aliceblue :"#f0f8ff",
  49. antiquewhite :"#faebd7",
  50. aqua :"#00ffff",
  51. aquamarine :"#7fffd4",
  52. azure :"#f0ffff",
  53. beige :"#f5f5dc",
  54. bisque :"#ffe4c4",
  55. black :"#000000",
  56. blanchedalmond :"#ffebcd",
  57. blue :"#0000ff",
  58. blueviolet :"#8a2be2",
  59. brown :"#a52a2a",
  60. burlywood :"#deb887",
  61. cadetblue :"#5f9ea0",
  62. chartreuse :"#7fff00",
  63. chocolate :"#d2691e",
  64. coral :"#ff7f50",
  65. cornflowerblue :"#6495ed",
  66. cornsilk :"#fff8dc",
  67. crimson :"#dc143c",
  68. cyan :"#00ffff",
  69. darkblue :"#00008b",
  70. darkcyan :"#008b8b",
  71. darkgoldenrod :"#b8860b",
  72. darkgray :"#a9a9a9",
  73. darkgrey :"#a9a9a9",
  74. darkgreen :"#006400",
  75. darkkhaki :"#bdb76b",
  76. darkmagenta :"#8b008b",
  77. darkolivegreen :"#556b2f",
  78. darkorange :"#ff8c00",
  79. darkorchid :"#9932cc",
  80. darkred :"#8b0000",
  81. darksalmon :"#e9967a",
  82. darkseagreen :"#8fbc8f",
  83. darkslateblue :"#483d8b",
  84. darkslategray :"#2f4f4f",
  85. darkslategrey :"#2f4f4f",
  86. darkturquoise :"#00ced1",
  87. darkviolet :"#9400d3",
  88. deeppink :"#ff1493",
  89. deepskyblue :"#00bfff",
  90. dimgray :"#696969",
  91. dimgrey :"#696969",
  92. dodgerblue :"#1e90ff",
  93. firebrick :"#b22222",
  94. floralwhite :"#fffaf0",
  95. forestgreen :"#228b22",
  96. fuchsia :"#ff00ff",
  97. gainsboro :"#dcdcdc",
  98. ghostwhite :"#f8f8ff",
  99. gold :"#ffd700",
  100. goldenrod :"#daa520",
  101. gray :"#808080",
  102. grey :"#808080",
  103. green :"#008000",
  104. greenyellow :"#adff2f",
  105. honeydew :"#f0fff0",
  106. hotpink :"#ff69b4",
  107. indianred :"#cd5c5c",
  108. indigo :"#4b0082",
  109. ivory :"#fffff0",
  110. khaki :"#f0e68c",
  111. lavender :"#e6e6fa",
  112. lavenderblush :"#fff0f5",
  113. lawngreen :"#7cfc00",
  114. lemonchiffon :"#fffacd",
  115. lightblue :"#add8e6",
  116. lightcoral :"#f08080",
  117. lightcyan :"#e0ffff",
  118. lightgoldenrodyellow :"#fafad2",
  119. lightgray :"#d3d3d3",
  120. lightgrey :"#d3d3d3",
  121. lightgreen :"#90ee90",
  122. lightpink :"#ffb6c1",
  123. lightsalmon :"#ffa07a",
  124. lightseagreen :"#20b2aa",
  125. lightskyblue :"#87cefa",
  126. lightslategray :"#778899",
  127. lightslategrey :"#778899",
  128. lightsteelblue :"#b0c4de",
  129. lightyellow :"#ffffe0",
  130. lime :"#00ff00",
  131. limegreen :"#32cd32",
  132. linen :"#faf0e6",
  133. magenta :"#ff00ff",
  134. maroon :"#800000",
  135. mediumaquamarine:"#66cdaa",
  136. mediumblue :"#0000cd",
  137. mediumorchid :"#ba55d3",
  138. mediumpurple :"#9370db",
  139. mediumseagreen :"#3cb371",
  140. mediumslateblue :"#7b68ee",
  141. mediumspringgreen :"#00fa9a",
  142. mediumturquoise :"#48d1cc",
  143. mediumvioletred :"#c71585",
  144. midnightblue :"#191970",
  145. mintcream :"#f5fffa",
  146. mistyrose :"#ffe4e1",
  147. moccasin :"#ffe4b5",
  148. navajowhite :"#ffdead",
  149. navy :"#000080",
  150. oldlace :"#fdf5e6",
  151. olive :"#808000",
  152. olivedrab :"#6b8e23",
  153. orange :"#ffa500",
  154. orangered :"#ff4500",
  155. orchid :"#da70d6",
  156. palegoldenrod :"#eee8aa",
  157. palegreen :"#98fb98",
  158. paleturquoise :"#afeeee",
  159. palevioletred :"#db7093",
  160. papayawhip :"#ffefd5",
  161. peachpuff :"#ffdab9",
  162. peru :"#cd853f",
  163. pink :"#ffc0cb",
  164. plum :"#dda0dd",
  165. powderblue :"#b0e0e6",
  166. purple :"#800080",
  167. red :"#ff0000",
  168. rosybrown :"#bc8f8f",
  169. royalblue :"#4169e1",
  170. saddlebrown :"#8b4513",
  171. salmon :"#fa8072",
  172. sandybrown :"#f4a460",
  173. seagreen :"#2e8b57",
  174. seashell :"#fff5ee",
  175. sienna :"#a0522d",
  176. silver :"#c0c0c0",
  177. skyblue :"#87ceeb",
  178. slateblue :"#6a5acd",
  179. slategray :"#708090",
  180. slategrey :"#708090",
  181. snow :"#fffafa",
  182. springgreen :"#00ff7f",
  183. steelblue :"#4682b4",
  184. tan :"#d2b48c",
  185. teal :"#008080",
  186. thistle :"#d8bfd8",
  187. tomato :"#ff6347",
  188. turquoise :"#40e0d0",
  189. violet :"#ee82ee",
  190. wheat :"#f5deb3",
  191. white :"#ffffff",
  192. whitesmoke :"#f5f5f5",
  193. yellow :"#ffff00",
  194. yellowgreen :"#9acd32",
  195. //'currentColor' color keyword https://www.w3.org/TR/css3-color/#currentcolor
  196. currentColor :"The value of the 'color' property.",
  197. //CSS2 system colors https://www.w3.org/TR/css3-color/#css2-system
  198. activeborder :"Active window border.",
  199. activecaption :"Active window caption.",
  200. appworkspace :"Background color of multiple document interface.",
  201. background :"Desktop background.",
  202. buttonface :"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.",
  203. buttonhighlight :"The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
  204. buttonshadow :"The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
  205. buttontext :"Text on push buttons.",
  206. captiontext :"Text in caption, size box, and scrollbar arrow box.",
  207. graytext :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",
  208. greytext :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",
  209. highlight :"Item(s) selected in a control.",
  210. highlighttext :"Text of item(s) selected in a control.",
  211. inactiveborder :"Inactive window border.",
  212. inactivecaption :"Inactive window caption.",
  213. inactivecaptiontext :"Color of text in an inactive caption.",
  214. infobackground :"Background color for tooltip controls.",
  215. infotext :"Text color for tooltip controls.",
  216. menu :"Menu background.",
  217. menutext :"Text in menus.",
  218. scrollbar :"Scroll bar gray area.",
  219. threeddarkshadow :"The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
  220. threedface :"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
  221. threedhighlight :"The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
  222. threedlightshadow :"The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
  223. threedshadow :"The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
  224. window :"Window background.",
  225. windowframe :"Window frame.",
  226. windowtext :"Text in windows."
  227. };
  228. },{}],2:[function(require,module,exports){
  229. "use strict";
  230. module.exports = Combinator;
  231. var SyntaxUnit = require("../util/SyntaxUnit");
  232. var Parser = require("./Parser");
  233. /**
  234. * Represents a selector combinator (whitespace, +, >).
  235. * @namespace parserlib.css
  236. * @class Combinator
  237. * @extends parserlib.util.SyntaxUnit
  238. * @constructor
  239. * @param {String} text The text representation of the unit.
  240. * @param {int} line The line of text on which the unit resides.
  241. * @param {int} col The column of text on which the unit resides.
  242. */
  243. function Combinator(text, line, col) {
  244. SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE);
  245. /**
  246. * The type of modifier.
  247. * @type String
  248. * @property type
  249. */
  250. this.type = "unknown";
  251. //pretty simple
  252. if (/^\s+$/.test(text)) {
  253. this.type = "descendant";
  254. } else if (text === ">") {
  255. this.type = "child";
  256. } else if (text === "+") {
  257. this.type = "adjacent-sibling";
  258. } else if (text === "~") {
  259. this.type = "sibling";
  260. }
  261. }
  262. Combinator.prototype = new SyntaxUnit();
  263. Combinator.prototype.constructor = Combinator;
  264. },{"../util/SyntaxUnit":26,"./Parser":6}],3:[function(require,module,exports){
  265. "use strict";
  266. module.exports = Matcher;
  267. var StringReader = require("../util/StringReader");
  268. var SyntaxError = require("../util/SyntaxError");
  269. /**
  270. * This class implements a combinator library for matcher functions.
  271. * The combinators are described at:
  272. * https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax#Component_value_combinators
  273. */
  274. function Matcher(matchFunc, toString) {
  275. this.match = function(expression) {
  276. // Save/restore marks to ensure that failed matches always restore
  277. // the original location in the expression.
  278. var result;
  279. expression.mark();
  280. result = matchFunc(expression);
  281. if (result) {
  282. expression.drop();
  283. } else {
  284. expression.restore();
  285. }
  286. return result;
  287. };
  288. this.toString = typeof toString === "function" ? toString : function() {
  289. return toString;
  290. };
  291. }
  292. /** Precedence table of combinators. */
  293. Matcher.prec = {
  294. MOD: 5,
  295. SEQ: 4,
  296. ANDAND: 3,
  297. OROR: 2,
  298. ALT: 1
  299. };
  300. /** Simple recursive-descent grammar to build matchers from strings. */
  301. Matcher.parse = function(str) {
  302. var reader, eat, expr, oror, andand, seq, mod, term, result;
  303. reader = new StringReader(str);
  304. eat = function(matcher) {
  305. var result = reader.readMatch(matcher);
  306. if (result === null) {
  307. throw new SyntaxError(
  308. "Expected "+matcher, reader.getLine(), reader.getCol());
  309. }
  310. return result;
  311. };
  312. expr = function() {
  313. // expr = oror (" | " oror)*
  314. var m = [ oror() ];
  315. while (reader.readMatch(" | ") !== null) {
  316. m.push(oror());
  317. }
  318. return m.length === 1 ? m[0] : Matcher.alt.apply(Matcher, m);
  319. };
  320. oror = function() {
  321. // oror = andand ( " || " andand)*
  322. var m = [ andand() ];
  323. while (reader.readMatch(" || ") !== null) {
  324. m.push(andand());
  325. }
  326. return m.length === 1 ? m[0] : Matcher.oror.apply(Matcher, m);
  327. };
  328. andand = function() {
  329. // andand = seq ( " && " seq)*
  330. var m = [ seq() ];
  331. while (reader.readMatch(" && ") !== null) {
  332. m.push(seq());
  333. }
  334. return m.length === 1 ? m[0] : Matcher.andand.apply(Matcher, m);
  335. };
  336. seq = function() {
  337. // seq = mod ( " " mod)*
  338. var m = [ mod() ];
  339. while (reader.readMatch(/^ (?![&|\]])/) !== null) {
  340. m.push(mod());
  341. }
  342. return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m);
  343. };
  344. mod = function() {
  345. // mod = term ( "?" | "*" | "+" | "#" | "{<num>,<num>}" )?
  346. var m = term();
  347. if (reader.readMatch("?") !== null) {
  348. return m.question();
  349. } else if (reader.readMatch("*") !== null) {
  350. return m.star();
  351. } else if (reader.readMatch("+") !== null) {
  352. return m.plus();
  353. } else if (reader.readMatch("#") !== null) {
  354. return m.hash();
  355. } else if (reader.readMatch(/^\{\s*/) !== null) {
  356. var min = eat(/^\d+/);
  357. eat(/^\s*,\s*/);
  358. var max = eat(/^\d+/);
  359. eat(/^\s*\}/);
  360. return m.braces(+min, +max);
  361. }
  362. return m;
  363. };
  364. term = function() {
  365. // term = <nt> | literal | "[ " expression " ]"
  366. if (reader.readMatch("[ ") !== null) {
  367. var m = expr();
  368. eat(" ]");
  369. return m;
  370. }
  371. return Matcher.fromType(eat(/^[^ ?*+#{]+/));
  372. };
  373. result = expr();
  374. if (!reader.eof()) {
  375. throw new SyntaxError(
  376. "Expected end of string", reader.getLine(), reader.getCol());
  377. }
  378. return result;
  379. };
  380. /**
  381. * Convert a string to a matcher (parsing simple alternations),
  382. * or do nothing if the argument is already a matcher.
  383. */
  384. Matcher.cast = function(m) {
  385. if (m instanceof Matcher) {
  386. return m;
  387. }
  388. return Matcher.parse(m);
  389. };
  390. /**
  391. * Create a matcher for a single type.
  392. */
  393. Matcher.fromType = function(type) {
  394. // Late require of ValidationTypes to break a dependency cycle.
  395. var ValidationTypes = require("./ValidationTypes");
  396. return new Matcher(function(expression) {
  397. return expression.hasNext() && ValidationTypes.isType(expression, type);
  398. }, type);
  399. };
  400. /**
  401. * Create a matcher for one or more juxtaposed words, which all must
  402. * occur, in the given order.
  403. */
  404. Matcher.seq = function() {
  405. var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
  406. if (ms.length === 1) {
  407. return ms[0];
  408. }
  409. return new Matcher(function(expression) {
  410. var i, result = true;
  411. for (i = 0; result && i < ms.length; i++) {
  412. result = ms[i].match(expression);
  413. }
  414. return result;
  415. }, function(prec) {
  416. var p = Matcher.prec.SEQ;
  417. var s = ms.map(function(m) {
  418. return m.toString(p);
  419. }).join(" ");
  420. if (prec > p) {
  421. s = "[ " + s + " ]";
  422. }
  423. return s;
  424. });
  425. };
  426. /**
  427. * Create a matcher for one or more alternatives, where exactly one
  428. * must occur.
  429. */
  430. Matcher.alt = function() {
  431. var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
  432. if (ms.length === 1) {
  433. return ms[0];
  434. }
  435. return new Matcher(function(expression) {
  436. var i, result = false;
  437. for (i = 0; !result && i < ms.length; i++) {
  438. result = ms[i].match(expression);
  439. }
  440. return result;
  441. }, function(prec) {
  442. var p = Matcher.prec.ALT;
  443. var s = ms.map(function(m) {
  444. return m.toString(p);
  445. }).join(" | ");
  446. if (prec > p) {
  447. s = "[ " + s + " ]";
  448. }
  449. return s;
  450. });
  451. };
  452. /**
  453. * Create a matcher for two or more options. This implements the
  454. * double bar (||) and double ampersand (&&) operators, as well as
  455. * variants of && where some of the alternatives are optional.
  456. * This will backtrack through even successful matches to try to
  457. * maximize the number of items matched.
  458. */
  459. Matcher.many = function(required) {
  460. var ms = Array.prototype.slice.call(arguments, 1).reduce(function(acc, v) {
  461. if (v.expand) {
  462. // Insert all of the options for the given complex rule as
  463. // individual options.
  464. var ValidationTypes = require("./ValidationTypes");
  465. acc.push.apply(acc, ValidationTypes.complex[v.expand].options);
  466. } else {
  467. acc.push(Matcher.cast(v));
  468. }
  469. return acc;
  470. }, []);
  471. if (required === true) {
  472. required = ms.map(function() {
  473. return true;
  474. });
  475. }
  476. var result = new Matcher(function(expression) {
  477. var seen = [], max = 0, pass = 0;
  478. var success = function(matchCount) {
  479. if (pass === 0) {
  480. max = Math.max(matchCount, max);
  481. return matchCount === ms.length;
  482. } else {
  483. return matchCount === max;
  484. }
  485. };
  486. var tryMatch = function(matchCount) {
  487. for (var i = 0; i < ms.length; i++) {
  488. if (seen[i]) {
  489. continue;
  490. }
  491. expression.mark();
  492. if (ms[i].match(expression)) {
  493. seen[i] = true;
  494. // Increase matchCount iff this was a required element
  495. // (or if all the elements are optional)
  496. if (tryMatch(matchCount + ((required === false || required[i]) ? 1 : 0))) {
  497. expression.drop();
  498. return true;
  499. }
  500. // Backtrack: try *not* matching using this rule, and
  501. // let's see if it leads to a better overall match.
  502. expression.restore();
  503. seen[i] = false;
  504. } else {
  505. expression.drop();
  506. }
  507. }
  508. return success(matchCount);
  509. };
  510. if (!tryMatch(0)) {
  511. // Couldn't get a complete match, retrace our steps to make the
  512. // match with the maximum # of required elements.
  513. pass++;
  514. tryMatch(0);
  515. }
  516. if (required === false) {
  517. return max > 0;
  518. }
  519. // Use finer-grained specification of which matchers are required.
  520. for (var i = 0; i < ms.length; i++) {
  521. if (required[i] && !seen[i]) {
  522. return false;
  523. }
  524. }
  525. return true;
  526. }, function(prec) {
  527. var p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND;
  528. var s = ms.map(function(m, i) {
  529. if (required !== false && !required[i]) {
  530. return m.toString(Matcher.prec.MOD) + "?";
  531. }
  532. return m.toString(p);
  533. }).join(required === false ? " || " : " && ");
  534. if (prec > p) {
  535. s = "[ " + s + " ]";
  536. }
  537. return s;
  538. });
  539. result.options = ms;
  540. return result;
  541. };
  542. /**
  543. * Create a matcher for two or more options, where all options are
  544. * mandatory but they may appear in any order.
  545. */
  546. Matcher.andand = function() {
  547. var args = Array.prototype.slice.call(arguments);
  548. args.unshift(true);
  549. return Matcher.many.apply(Matcher, args);
  550. };
  551. /**
  552. * Create a matcher for two or more options, where options are
  553. * optional and may appear in any order, but at least one must be
  554. * present.
  555. */
  556. Matcher.oror = function() {
  557. var args = Array.prototype.slice.call(arguments);
  558. args.unshift(false);
  559. return Matcher.many.apply(Matcher, args);
  560. };
  561. /** Instance methods on Matchers. */
  562. Matcher.prototype = {
  563. constructor: Matcher,
  564. // These are expected to be overridden in every instance.
  565. match: function() { throw new Error("unimplemented"); },
  566. toString: function() { throw new Error("unimplemented"); },
  567. // This returns a standalone function to do the matching.
  568. func: function() { return this.match.bind(this); },
  569. // Basic combinators
  570. then: function(m) { return Matcher.seq(this, m); },
  571. or: function(m) { return Matcher.alt(this, m); },
  572. andand: function(m) { return Matcher.many(true, this, m); },
  573. oror: function(m) { return Matcher.many(false, this, m); },
  574. // Component value multipliers
  575. star: function() { return this.braces(0, Infinity, "*"); },
  576. plus: function() { return this.braces(1, Infinity, "+"); },
  577. question: function() { return this.braces(0, 1, "?"); },
  578. hash: function() {
  579. return this.braces(1, Infinity, "#", Matcher.cast(","));
  580. },
  581. braces: function(min, max, marker, optSep) {
  582. var m1 = this, m2 = optSep ? optSep.then(this) : this;
  583. if (!marker) {
  584. marker = "{" + min + "," + max + "}";
  585. }
  586. return new Matcher(function(expression) {
  587. var result = true, i;
  588. for (i = 0; i < max; i++) {
  589. if (i > 0 && optSep) {
  590. result = m2.match(expression);
  591. } else {
  592. result = m1.match(expression);
  593. }
  594. if (!result) {
  595. break;
  596. }
  597. }
  598. return i >= min;
  599. }, function() {
  600. return m1.toString(Matcher.prec.MOD) + marker;
  601. });
  602. }
  603. };
  604. },{"../util/StringReader":24,"../util/SyntaxError":25,"./ValidationTypes":21}],4:[function(require,module,exports){
  605. "use strict";
  606. module.exports = MediaFeature;
  607. var SyntaxUnit = require("../util/SyntaxUnit");
  608. var Parser = require("./Parser");
  609. /**
  610. * Represents a media feature, such as max-width:500.
  611. * @namespace parserlib.css
  612. * @class MediaFeature
  613. * @extends parserlib.util.SyntaxUnit
  614. * @constructor
  615. * @param {SyntaxUnit} name The name of the feature.
  616. * @param {SyntaxUnit} value The value of the feature or null if none.
  617. */
  618. function MediaFeature(name, value) {
  619. SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE);
  620. /**
  621. * The name of the media feature
  622. * @type String
  623. * @property name
  624. */
  625. this.name = name;
  626. /**
  627. * The value for the feature or null if there is none.
  628. * @type SyntaxUnit
  629. * @property value
  630. */
  631. this.value = value;
  632. }
  633. MediaFeature.prototype = new SyntaxUnit();
  634. MediaFeature.prototype.constructor = MediaFeature;
  635. },{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){
  636. "use strict";
  637. module.exports = MediaQuery;
  638. var SyntaxUnit = require("../util/SyntaxUnit");
  639. var Parser = require("./Parser");
  640. /**
  641. * Represents an individual media query.
  642. * @namespace parserlib.css
  643. * @class MediaQuery
  644. * @extends parserlib.util.SyntaxUnit
  645. * @constructor
  646. * @param {String} modifier The modifier "not" or "only" (or null).
  647. * @param {String} mediaType The type of media (i.e., "print").
  648. * @param {Array} parts Array of selectors parts making up this selector.
  649. * @param {int} line The line of text on which the unit resides.
  650. * @param {int} col The column of text on which the unit resides.
  651. */
  652. function MediaQuery(modifier, mediaType, features, line, col) {
  653. SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType : "") + (mediaType && features.length > 0 ? " and " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE);
  654. /**
  655. * The media modifier ("not" or "only")
  656. * @type String
  657. * @property modifier
  658. */
  659. this.modifier = modifier;
  660. /**
  661. * The mediaType (i.e., "print")
  662. * @type String
  663. * @property mediaType
  664. */
  665. this.mediaType = mediaType;
  666. /**
  667. * The parts that make up the selector.
  668. * @type Array
  669. * @property features
  670. */
  671. this.features = features;
  672. }
  673. MediaQuery.prototype = new SyntaxUnit();
  674. MediaQuery.prototype.constructor = MediaQuery;
  675. },{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){
  676. "use strict";
  677. module.exports = Parser;
  678. var EventTarget = require("../util/EventTarget");
  679. var SyntaxError = require("../util/SyntaxError");
  680. var SyntaxUnit = require("../util/SyntaxUnit");
  681. var Combinator = require("./Combinator");
  682. var MediaFeature = require("./MediaFeature");
  683. var MediaQuery = require("./MediaQuery");
  684. var PropertyName = require("./PropertyName");
  685. var PropertyValue = require("./PropertyValue");
  686. var PropertyValuePart = require("./PropertyValuePart");
  687. var Selector = require("./Selector");
  688. var SelectorPart = require("./SelectorPart");
  689. var SelectorSubPart = require("./SelectorSubPart");
  690. var TokenStream = require("./TokenStream");
  691. var Tokens = require("./Tokens");
  692. var Validation = require("./Validation");
  693. /**
  694. * A CSS3 parser.
  695. * @namespace parserlib.css
  696. * @class Parser
  697. * @constructor
  698. * @param {Object} options (Optional) Various options for the parser:
  699. * starHack (true|false) to allow IE6 star hack as valid,
  700. * underscoreHack (true|false) to interpret leading underscores
  701. * as IE6-7 targeting for known properties, ieFilters (true|false)
  702. * to indicate that IE < 8 filters should be accepted and not throw
  703. * syntax errors.
  704. */
  705. function Parser(options) {
  706. //inherit event functionality
  707. EventTarget.call(this);
  708. this.options = options || {};
  709. this._tokenStream = null;
  710. }
  711. //Static constants
  712. Parser.DEFAULT_TYPE = 0;
  713. Parser.COMBINATOR_TYPE = 1;
  714. Parser.MEDIA_FEATURE_TYPE = 2;
  715. Parser.MEDIA_QUERY_TYPE = 3;
  716. Parser.PROPERTY_NAME_TYPE = 4;
  717. Parser.PROPERTY_VALUE_TYPE = 5;
  718. Parser.PROPERTY_VALUE_PART_TYPE = 6;
  719. Parser.SELECTOR_TYPE = 7;
  720. Parser.SELECTOR_PART_TYPE = 8;
  721. Parser.SELECTOR_SUB_PART_TYPE = 9;
  722. Parser.prototype = function() {
  723. var proto = new EventTarget(), //new prototype
  724. prop,
  725. additions = {
  726. __proto__: null,
  727. //restore constructor
  728. constructor: Parser,
  729. //instance constants - yuck
  730. DEFAULT_TYPE : 0,
  731. COMBINATOR_TYPE : 1,
  732. MEDIA_FEATURE_TYPE : 2,
  733. MEDIA_QUERY_TYPE : 3,
  734. PROPERTY_NAME_TYPE : 4,
  735. PROPERTY_VALUE_TYPE : 5,
  736. PROPERTY_VALUE_PART_TYPE : 6,
  737. SELECTOR_TYPE : 7,
  738. SELECTOR_PART_TYPE : 8,
  739. SELECTOR_SUB_PART_TYPE : 9,
  740. //-----------------------------------------------------------------
  741. // Grammar
  742. //-----------------------------------------------------------------
  743. _stylesheet: function() {
  744. /*
  745. * stylesheet
  746. * : [ CHARSET_SYM S* STRING S* ';' ]?
  747. * [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
  748. * [ namespace [S|CDO|CDC]* ]*
  749. * [ [ ruleset | media | page | font_face | keyframes_rule | supports_rule ] [S|CDO|CDC]* ]*
  750. * ;
  751. */
  752. var tokenStream = this._tokenStream,
  753. count,
  754. token,
  755. tt;
  756. this.fire("startstylesheet");
  757. //try to read character set
  758. this._charset();
  759. this._skipCruft();
  760. //try to read imports - may be more than one
  761. while (tokenStream.peek() === Tokens.IMPORT_SYM) {
  762. this._import();
  763. this._skipCruft();
  764. }
  765. //try to read namespaces - may be more than one
  766. while (tokenStream.peek() === Tokens.NAMESPACE_SYM) {
  767. this._namespace();
  768. this._skipCruft();
  769. }
  770. //get the next token
  771. tt = tokenStream.peek();
  772. //try to read the rest
  773. while (tt > Tokens.EOF) {
  774. try {
  775. switch (tt) {
  776. case Tokens.MEDIA_SYM:
  777. this._media();
  778. this._skipCruft();
  779. break;
  780. case Tokens.PAGE_SYM:
  781. this._page();
  782. this._skipCruft();
  783. break;
  784. case Tokens.FONT_FACE_SYM:
  785. this._font_face();
  786. this._skipCruft();
  787. break;
  788. case Tokens.KEYFRAMES_SYM:
  789. this._keyframes();
  790. this._skipCruft();
  791. break;
  792. case Tokens.VIEWPORT_SYM:
  793. this._viewport();
  794. this._skipCruft();
  795. break;
  796. case Tokens.DOCUMENT_SYM:
  797. this._document();
  798. this._skipCruft();
  799. break;
  800. case Tokens.SUPPORTS_SYM:
  801. this._supports();
  802. this._skipCruft();
  803. break;
  804. case Tokens.UNKNOWN_SYM: //unknown @ rule
  805. tokenStream.get();
  806. if (!this.options.strict) {
  807. //fire error event
  808. this.fire({
  809. type: "error",
  810. error: null,
  811. message: "Unknown @ rule: " + tokenStream.LT(0).value + ".",
  812. line: tokenStream.LT(0).startLine,
  813. col: tokenStream.LT(0).startCol
  814. });
  815. //skip braces
  816. count=0;
  817. while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) === Tokens.LBRACE) {
  818. count++; //keep track of nesting depth
  819. }
  820. while (count) {
  821. tokenStream.advance([Tokens.RBRACE]);
  822. count--;
  823. }
  824. } else {
  825. //not a syntax error, rethrow it
  826. throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
  827. }
  828. break;
  829. case Tokens.S:
  830. this._readWhitespace();
  831. break;
  832. default:
  833. if (!this._ruleset()) {
  834. //error handling for known issues
  835. switch (tt) {
  836. case Tokens.CHARSET_SYM:
  837. token = tokenStream.LT(1);
  838. this._charset(false);
  839. throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
  840. case Tokens.IMPORT_SYM:
  841. token = tokenStream.LT(1);
  842. this._import(false);
  843. throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
  844. case Tokens.NAMESPACE_SYM:
  845. token = tokenStream.LT(1);
  846. this._namespace(false);
  847. throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
  848. default:
  849. tokenStream.get(); //get the last token
  850. this._unexpectedToken(tokenStream.token());
  851. }
  852. }
  853. }
  854. } catch (ex) {
  855. if (ex instanceof SyntaxError && !this.options.strict) {
  856. this.fire({
  857. type: "error",
  858. error: ex,
  859. message: ex.message,
  860. line: ex.line,
  861. col: ex.col
  862. });
  863. } else {
  864. throw ex;
  865. }
  866. }
  867. tt = tokenStream.peek();
  868. }
  869. if (tt !== Tokens.EOF) {
  870. this._unexpectedToken(tokenStream.token());
  871. }
  872. this.fire("endstylesheet");
  873. },
  874. _charset: function(emit) {
  875. var tokenStream = this._tokenStream,
  876. charset,
  877. token,
  878. line,
  879. col;
  880. if (tokenStream.match(Tokens.CHARSET_SYM)) {
  881. line = tokenStream.token().startLine;
  882. col = tokenStream.token().startCol;
  883. this._readWhitespace();
  884. tokenStream.mustMatch(Tokens.STRING);
  885. token = tokenStream.token();
  886. charset = token.value;
  887. this._readWhitespace();
  888. tokenStream.mustMatch(Tokens.SEMICOLON);
  889. if (emit !== false) {
  890. this.fire({
  891. type: "charset",
  892. charset:charset,
  893. line: line,
  894. col: col
  895. });
  896. }
  897. }
  898. },
  899. _import: function(emit) {
  900. /*
  901. * import
  902. * : IMPORT_SYM S*
  903. * [STRING|URI] S* media_query_list? ';' S*
  904. */
  905. var tokenStream = this._tokenStream,
  906. uri,
  907. importToken,
  908. mediaList = [];
  909. //read import symbol
  910. tokenStream.mustMatch(Tokens.IMPORT_SYM);
  911. importToken = tokenStream.token();
  912. this._readWhitespace();
  913. tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
  914. //grab the URI value
  915. uri = tokenStream.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/, "$1");
  916. this._readWhitespace();
  917. mediaList = this._media_query_list();
  918. //must end with a semicolon
  919. tokenStream.mustMatch(Tokens.SEMICOLON);
  920. this._readWhitespace();
  921. if (emit !== false) {
  922. this.fire({
  923. type: "import",
  924. uri: uri,
  925. media: mediaList,
  926. line: importToken.startLine,
  927. col: importToken.startCol
  928. });
  929. }
  930. },
  931. _namespace: function(emit) {
  932. /*
  933. * namespace
  934. * : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
  935. */
  936. var tokenStream = this._tokenStream,
  937. line,
  938. col,
  939. prefix,
  940. uri;
  941. //read import symbol
  942. tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
  943. line = tokenStream.token().startLine;
  944. col = tokenStream.token().startCol;
  945. this._readWhitespace();
  946. //it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT
  947. if (tokenStream.match(Tokens.IDENT)) {
  948. prefix = tokenStream.token().value;
  949. this._readWhitespace();
  950. }
  951. tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
  952. /*if (!tokenStream.match(Tokens.STRING)){
  953. tokenStream.mustMatch(Tokens.URI);
  954. }*/
  955. //grab the URI value
  956. uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
  957. this._readWhitespace();
  958. //must end with a semicolon
  959. tokenStream.mustMatch(Tokens.SEMICOLON);
  960. this._readWhitespace();
  961. if (emit !== false) {
  962. this.fire({
  963. type: "namespace",
  964. prefix: prefix,
  965. uri: uri,
  966. line: line,
  967. col: col
  968. });
  969. }
  970. },
  971. _supports: function(emit) {
  972. /*
  973. * supports_rule
  974. * : SUPPORTS_SYM S* supports_condition S* group_rule_body
  975. * ;
  976. */
  977. var tokenStream = this._tokenStream,
  978. line,
  979. col;
  980. if (tokenStream.match(Tokens.SUPPORTS_SYM)) {
  981. line = tokenStream.token().startLine;
  982. col = tokenStream.token().startCol;
  983. this._readWhitespace();
  984. this._supports_condition();
  985. this._readWhitespace();
  986. tokenStream.mustMatch(Tokens.LBRACE);
  987. this._readWhitespace();
  988. if (emit !== false) {
  989. this.fire({
  990. type: "startsupports",
  991. line: line,
  992. col: col
  993. });
  994. }
  995. while (true) {
  996. if (!this._ruleset()) {
  997. break;
  998. }
  999. }
  1000. tokenStream.mustMatch(Tokens.RBRACE);
  1001. this._readWhitespace();
  1002. this.fire({
  1003. type: "endsupports",
  1004. line: line,
  1005. col: col
  1006. });
  1007. }
  1008. },
  1009. _supports_condition: function() {
  1010. /*
  1011. * supports_condition
  1012. * : supports_negation | supports_conjunction | supports_disjunction |
  1013. * supports_condition_in_parens
  1014. * ;
  1015. */
  1016. var tokenStream = this._tokenStream,
  1017. ident;
  1018. if (tokenStream.match(Tokens.IDENT)) {
  1019. ident = tokenStream.token().value.toLowerCase();
  1020. if (ident === "not") {
  1021. tokenStream.mustMatch(Tokens.S);
  1022. this._supports_condition_in_parens();
  1023. } else {
  1024. tokenStream.unget();
  1025. }
  1026. } else {
  1027. this._supports_condition_in_parens();
  1028. this._readWhitespace();
  1029. while (tokenStream.peek() === Tokens.IDENT) {
  1030. ident = tokenStream.LT(1).value.toLowerCase();
  1031. if (ident === "and" || ident === "or") {
  1032. tokenStream.mustMatch(Tokens.IDENT);
  1033. this._readWhitespace();
  1034. this._supports_condition_in_parens();
  1035. this._readWhitespace();
  1036. }
  1037. }
  1038. }
  1039. },
  1040. _supports_condition_in_parens: function() {
  1041. /*
  1042. * supports_condition_in_parens
  1043. * : ( '(' S* supports_condition S* ')' ) | supports_declaration_condition |
  1044. * general_enclosed
  1045. * ;
  1046. */
  1047. var tokenStream = this._tokenStream,
  1048. ident;
  1049. if (tokenStream.match(Tokens.LPAREN)) {
  1050. this._readWhitespace();
  1051. if (tokenStream.match(Tokens.IDENT)) {
  1052. // look ahead for not keyword, if not given, continue with declaration condition.
  1053. ident = tokenStream.token().value.toLowerCase();
  1054. if (ident === "not") {
  1055. this._readWhitespace();
  1056. this._supports_condition();
  1057. this._readWhitespace();
  1058. tokenStream.mustMatch(Tokens.RPAREN);
  1059. } else {
  1060. tokenStream.unget();
  1061. this._supports_declaration_condition(false);
  1062. }
  1063. } else {
  1064. this._supports_condition();
  1065. this._readWhitespace();
  1066. tokenStream.mustMatch(Tokens.RPAREN);
  1067. }
  1068. } else {
  1069. this._supports_declaration_condition();
  1070. }
  1071. },
  1072. _supports_declaration_condition: function(requireStartParen) {
  1073. /*
  1074. * supports_declaration_condition
  1075. * : '(' S* declaration ')'
  1076. * ;
  1077. */
  1078. var tokenStream = this._tokenStream;
  1079. if (requireStartParen !== false) {
  1080. tokenStream.mustMatch(Tokens.LPAREN);
  1081. }
  1082. this._readWhitespace();
  1083. this._declaration();
  1084. tokenStream.mustMatch(Tokens.RPAREN);
  1085. },
  1086. _media: function() {
  1087. /*
  1088. * media
  1089. * : MEDIA_SYM S* media_query_list S* '{' S* ruleset* '}' S*
  1090. * ;
  1091. */
  1092. var tokenStream = this._tokenStream,
  1093. line,
  1094. col,
  1095. mediaList;// = [];
  1096. //look for @media
  1097. tokenStream.mustMatch(Tokens.MEDIA_SYM);
  1098. line = tokenStream.token().startLine;
  1099. col = tokenStream.token().startCol;
  1100. this._readWhitespace();
  1101. mediaList = this._media_query_list();
  1102. tokenStream.mustMatch(Tokens.LBRACE);
  1103. this._readWhitespace();
  1104. this.fire({
  1105. type: "startmedia",
  1106. media: mediaList,
  1107. line: line,
  1108. col: col
  1109. });
  1110. while (true) {
  1111. if (tokenStream.peek() === Tokens.PAGE_SYM) {
  1112. this._page();
  1113. } else if (tokenStream.peek() === Tokens.FONT_FACE_SYM) {
  1114. this._font_face();
  1115. } else if (tokenStream.peek() === Tokens.VIEWPORT_SYM) {
  1116. this._viewport();
  1117. } else if (tokenStream.peek() === Tokens.DOCUMENT_SYM) {
  1118. this._document();
  1119. } else if (tokenStream.peek() === Tokens.SUPPORTS_SYM) {
  1120. this._supports();
  1121. } else if (tokenStream.peek() === Tokens.MEDIA_SYM) {
  1122. this._media();
  1123. } else if (!this._ruleset()) {
  1124. break;
  1125. }
  1126. }
  1127. tokenStream.mustMatch(Tokens.RBRACE);
  1128. this._readWhitespace();
  1129. this.fire({
  1130. type: "endmedia",
  1131. media: mediaList,
  1132. line: line,
  1133. col: col
  1134. });
  1135. },
  1136. //CSS3 Media Queries
  1137. _media_query_list: function() {
  1138. /*
  1139. * media_query_list
  1140. * : S* [media_query [ ',' S* media_query ]* ]?
  1141. * ;
  1142. */
  1143. var tokenStream = this._tokenStream,
  1144. mediaList = [];
  1145. this._readWhitespace();
  1146. if (tokenStream.peek() === Tokens.IDENT || tokenStream.peek() === Tokens.LPAREN) {
  1147. mediaList.push(this._media_query());
  1148. }
  1149. while (tokenStream.match(Tokens.COMMA)) {
  1150. this._readWhitespace();
  1151. mediaList.push(this._media_query());
  1152. }
  1153. return mediaList;
  1154. },
  1155. /*
  1156. * Note: "expression" in the grammar maps to the _media_expression
  1157. * method.
  1158. */
  1159. _media_query: function() {
  1160. /*
  1161. * media_query
  1162. * : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
  1163. * | expression [ AND S* expression ]*
  1164. * ;
  1165. */
  1166. var tokenStream = this._tokenStream,
  1167. type = null,
  1168. ident = null,
  1169. token = null,
  1170. expressions = [];
  1171. if (tokenStream.match(Tokens.IDENT)) {
  1172. ident = tokenStream.token().value.toLowerCase();
  1173. //since there's no custom tokens for these, need to manually check
  1174. if (ident !== "only" && ident !== "not") {
  1175. tokenStream.unget();
  1176. ident = null;
  1177. } else {
  1178. token = tokenStream.token();
  1179. }
  1180. }
  1181. this._readWhitespace();
  1182. if (tokenStream.peek() === Tokens.IDENT) {
  1183. type = this._media_type();
  1184. if (token === null) {
  1185. token = tokenStream.token();
  1186. }
  1187. } else if (tokenStream.peek() === Tokens.LPAREN) {
  1188. if (token === null) {
  1189. token = tokenStream.LT(1);
  1190. }
  1191. expressions.push(this._media_expression());
  1192. }
  1193. if (type === null && expressions.length === 0) {
  1194. return null;
  1195. } else {
  1196. this._readWhitespace();
  1197. while (tokenStream.match(Tokens.IDENT)) {
  1198. if (tokenStream.token().value.toLowerCase() !== "and") {
  1199. this._unexpectedToken(tokenStream.token());
  1200. }
  1201. this._readWhitespace();
  1202. expressions.push(this._media_expression());
  1203. }
  1204. }
  1205. return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
  1206. },
  1207. //CSS3 Media Queries
  1208. _media_type: function() {
  1209. /*
  1210. * media_type
  1211. * : IDENT
  1212. * ;
  1213. */
  1214. return this._media_feature();
  1215. },
  1216. /**
  1217. * Note: in CSS3 Media Queries, this is called "expression".
  1218. * Renamed here to avoid conflict with CSS3 Selectors
  1219. * definition of "expression". Also note that "expr" in the
  1220. * grammar now maps to "expression" from CSS3 selectors.
  1221. * @method _media_expression
  1222. * @private
  1223. */
  1224. _media_expression: function() {
  1225. /*
  1226. * expression
  1227. * : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
  1228. * ;
  1229. */
  1230. var tokenStream = this._tokenStream,
  1231. feature = null,
  1232. token,
  1233. expression = null;
  1234. tokenStream.mustMatch(Tokens.LPAREN);
  1235. feature = this._media_feature();
  1236. this._readWhitespace();
  1237. if (tokenStream.match(Tokens.COLON)) {
  1238. this._readWhitespace();
  1239. token = tokenStream.LT(1);
  1240. expression = this._expression();
  1241. }
  1242. tokenStream.mustMatch(Tokens.RPAREN);
  1243. this._readWhitespace();
  1244. return new MediaFeature(feature, expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null);
  1245. },
  1246. //CSS3 Media Queries
  1247. _media_feature: function() {
  1248. /*
  1249. * media_feature
  1250. * : IDENT
  1251. * ;
  1252. */
  1253. var tokenStream = this._tokenStream;
  1254. this._readWhitespace();
  1255. tokenStream.mustMatch(Tokens.IDENT);
  1256. return SyntaxUnit.fromToken(tokenStream.token());
  1257. },
  1258. //CSS3 Paged Media
  1259. _page: function() {
  1260. /*
  1261. * page:
  1262. * PAGE_SYM S* IDENT? pseudo_page? S*
  1263. * '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
  1264. * ;
  1265. */
  1266. var tokenStream = this._tokenStream,
  1267. line,
  1268. col,
  1269. identifier = null,
  1270. pseudoPage = null;
  1271. //look for @page
  1272. tokenStream.mustMatch(Tokens.PAGE_SYM);
  1273. line = tokenStream.token().startLine;
  1274. col = tokenStream.token().startCol;
  1275. this._readWhitespace();
  1276. if (tokenStream.match(Tokens.IDENT)) {
  1277. identifier = tokenStream.token().value;
  1278. //The value 'auto' may not be used as a page name and MUST be treated as a syntax error.
  1279. if (identifier.toLowerCase() === "auto") {
  1280. this._unexpectedToken(tokenStream.token());
  1281. }
  1282. }
  1283. //see if there's a colon upcoming
  1284. if (tokenStream.peek() === Tokens.COLON) {
  1285. pseudoPage = this._pseudo_page();
  1286. }
  1287. this._readWhitespace();
  1288. this.fire({
  1289. type: "startpage",
  1290. id: identifier,
  1291. pseudo: pseudoPage,
  1292. line: line,
  1293. col: col
  1294. });
  1295. this._readDeclarations(true, true);
  1296. this.fire({
  1297. type: "endpage",
  1298. id: identifier,
  1299. pseudo: pseudoPage,
  1300. line: line,
  1301. col: col
  1302. });
  1303. },
  1304. //CSS3 Paged Media
  1305. _margin: function() {
  1306. /*
  1307. * margin :
  1308. * margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
  1309. * ;
  1310. */
  1311. var tokenStream = this._tokenStream,
  1312. line,
  1313. col,
  1314. marginSym = this._margin_sym();
  1315. if (marginSym) {
  1316. line = tokenStream.token().startLine;
  1317. col = tokenStream.token().startCol;
  1318. this.fire({
  1319. type: "startpagemargin",
  1320. margin: marginSym,
  1321. line: line,
  1322. col: col
  1323. });
  1324. this._readDeclarations(true);
  1325. this.fire({
  1326. type: "endpagemargin",
  1327. margin: marginSym,
  1328. line: line,
  1329. col: col
  1330. });
  1331. return true;
  1332. } else {
  1333. return false;
  1334. }
  1335. },
  1336. //CSS3 Paged Media
  1337. _margin_sym: function() {
  1338. /*
  1339. * margin_sym :
  1340. * TOPLEFTCORNER_SYM |
  1341. * TOPLEFT_SYM |
  1342. * TOPCENTER_SYM |
  1343. * TOPRIGHT_SYM |
  1344. * TOPRIGHTCORNER_SYM |
  1345. * BOTTOMLEFTCORNER_SYM |
  1346. * BOTTOMLEFT_SYM |
  1347. * BOTTOMCENTER_SYM |
  1348. * BOTTOMRIGHT_SYM |
  1349. * BOTTOMRIGHTCORNER_SYM |
  1350. * LEFTTOP_SYM |
  1351. * LEFTMIDDLE_SYM |
  1352. * LEFTBOTTOM_SYM |
  1353. * RIGHTTOP_SYM |
  1354. * RIGHTMIDDLE_SYM |
  1355. * RIGHTBOTTOM_SYM
  1356. * ;
  1357. */
  1358. var tokenStream = this._tokenStream;
  1359. if (tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
  1360. Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
  1361. Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
  1362. Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
  1363. Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
  1364. Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
  1365. Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM])) {
  1366. return SyntaxUnit.fromToken(tokenStream.token());
  1367. } else {
  1368. return null;
  1369. }
  1370. },
  1371. _pseudo_page: function() {
  1372. /*
  1373. * pseudo_page
  1374. * : ':' IDENT
  1375. * ;
  1376. */
  1377. var tokenStream = this._tokenStream;
  1378. tokenStream.mustMatch(Tokens.COLON);
  1379. tokenStream.mustMatch(Tokens.IDENT);
  1380. //TODO: CSS3 Paged Media says only "left", "center", and "right" are allowed
  1381. return tokenStream.token().value;
  1382. },
  1383. _font_face: function() {
  1384. /*
  1385. * font_face
  1386. * : FONT_FACE_SYM S*
  1387. * '{' S* declaration [ ';' S* declaration ]* '}' S*
  1388. * ;
  1389. */
  1390. var tokenStream = this._tokenStream,
  1391. line,
  1392. col;
  1393. //look for @page
  1394. tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
  1395. line = tokenStream.token().startLine;
  1396. col = tokenStream.token().startCol;
  1397. this._readWhitespace();
  1398. this.fire({
  1399. type: "startfontface",
  1400. line: line,
  1401. col: col
  1402. });
  1403. this._readDeclarations(true);
  1404. this.fire({
  1405. type: "endfontface",
  1406. line: line,
  1407. col: col
  1408. });
  1409. },
  1410. _viewport: function() {
  1411. /*
  1412. * viewport
  1413. * : VIEWPORT_SYM S*
  1414. * '{' S* declaration? [ ';' S* declaration? ]* '}' S*
  1415. * ;
  1416. */
  1417. var tokenStream = this._tokenStream,
  1418. line,
  1419. col;
  1420. tokenStream.mustMatch(Tokens.VIEWPORT_SYM);
  1421. line = tokenStream.token().startLine;
  1422. col = tokenStream.token().startCol;
  1423. this._readWhitespace();
  1424. this.fire({
  1425. type: "startviewport",
  1426. line: line,
  1427. col: col
  1428. });
  1429. this._readDeclarations(true);
  1430. this.fire({
  1431. type: "endviewport",
  1432. line: line,
  1433. col: col
  1434. });
  1435. },
  1436. _document: function() {
  1437. /*
  1438. * document
  1439. * : DOCUMENT_SYM S*
  1440. * _document_function [ ',' S* _document_function ]* S*
  1441. * '{' S* ruleset* '}'
  1442. * ;
  1443. */
  1444. var tokenStream = this._tokenStream,
  1445. token,
  1446. functions = [],
  1447. prefix = "";
  1448. tokenStream.mustMatch(Tokens.DOCUMENT_SYM);
  1449. token = tokenStream.token();
  1450. if (/^@\-([^\-]+)\-/.test(token.value)) {
  1451. prefix = RegExp.$1;
  1452. }
  1453. this._readWhitespace();
  1454. functions.push(this._document_function());
  1455. while (tokenStream.match(Tokens.COMMA)) {
  1456. this._readWhitespace();
  1457. functions.push(this._document_function());
  1458. }
  1459. tokenStream.mustMatch(Tokens.LBRACE);
  1460. this.fire({
  1461. type: "startdocument",
  1462. functions: functions,
  1463. prefix: prefix,
  1464. line: token.startLine,
  1465. col: token.startCol
  1466. });
  1467. this._readWhitespace(); // Stylus hack
  1468. var ok = true;
  1469. while (ok) {
  1470. switch (tokenStream.peek()) {
  1471. case Tokens.PAGE_SYM:
  1472. this._page();
  1473. break;
  1474. case Tokens.FONT_FACE_SYM:
  1475. this._font_face();
  1476. break;
  1477. case Tokens.VIEWPORT_SYM:
  1478. this._viewport();
  1479. break;
  1480. case Tokens.MEDIA_SYM:
  1481. this._media();
  1482. break;
  1483. case Tokens.KEYFRAMES_SYM:
  1484. this._keyframes();
  1485. break;
  1486. case Tokens.DOCUMENT_SYM:
  1487. this._document();
  1488. break;
  1489. case Tokens.SUPPORTS_SYM:
  1490. this._supports();
  1491. break;
  1492. default:
  1493. ok = Boolean(this._ruleset());
  1494. }
  1495. }
  1496. tokenStream.mustMatch(Tokens.RBRACE);
  1497. token = tokenStream.token();
  1498. this.fire({
  1499. type: "enddocument",
  1500. functions: functions,
  1501. prefix: prefix,
  1502. line: token.startLine,
  1503. col: token.startCol
  1504. });
  1505. this._readWhitespace();
  1506. },
  1507. _document_function: function() {
  1508. /*
  1509. * document_function
  1510. * : function | URI S*
  1511. * ;
  1512. */
  1513. var tokenStream = this._tokenStream,
  1514. value;
  1515. if (tokenStream.match(Tokens.URI)) {
  1516. value = tokenStream.token().value;
  1517. this._readWhitespace();
  1518. } else {
  1519. value = this._function();
  1520. }
  1521. return value;
  1522. },
  1523. _operator: function(inFunction) {
  1524. /*
  1525. * operator (outside function)
  1526. * : '/' S* | ',' S* | /( empty )/
  1527. * operator (inside function)
  1528. * : '/' S* | '+' S* | '*' S* | '-' S* /( empty )/
  1529. * ;
  1530. */
  1531. var tokenStream = this._tokenStream,
  1532. token = null;
  1533. if (tokenStream.match([Tokens.SLASH, Tokens.COMMA]) ||
  1534. (inFunction && tokenStream.match([Tokens.PLUS, Tokens.STAR, Tokens.MINUS]))) {
  1535. token = tokenStream.token();
  1536. this._readWhitespace();
  1537. }
  1538. return token ? PropertyValuePart.fromToken(token) : null;
  1539. },
  1540. _combinator: function() {
  1541. /*
  1542. * combinator
  1543. * : PLUS S* | GREATER S* | TILDE S* | S+
  1544. * ;
  1545. */
  1546. var tokenStream = this._tokenStream,
  1547. value = null,
  1548. token;
  1549. if (tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])) {
  1550. token = tokenStream.token();
  1551. value = new Combinator(token.value, token.startLine, token.startCol);
  1552. this._readWhitespace();
  1553. }
  1554. return value;
  1555. },
  1556. _unary_operator: function() {
  1557. /*
  1558. * unary_operator
  1559. * : '-' | '+'
  1560. * ;
  1561. */
  1562. var tokenStream = this._tokenStream;
  1563. if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])) {
  1564. return tokenStream.token().value;
  1565. } else {
  1566. return null;
  1567. }
  1568. },
  1569. _property: function() {
  1570. /*
  1571. * property
  1572. * : IDENT S*
  1573. * ;
  1574. */
  1575. var tokenStream = this._tokenStream,
  1576. value = null,
  1577. hack = null,
  1578. tokenValue,
  1579. token,
  1580. line,
  1581. col;
  1582. //check for star hack - throws error if not allowed
  1583. if (tokenStream.peek() === Tokens.STAR && this.options.starHack) {
  1584. tokenStream.get();
  1585. token = tokenStream.token();
  1586. hack = token.value;
  1587. line = token.startLine;
  1588. col = token.startCol;
  1589. }
  1590. if (tokenStream.match(Tokens.IDENT)) {
  1591. token = tokenStream.token();
  1592. tokenValue = token.value;
  1593. //check for underscore hack - no error if not allowed because it's valid CSS syntax
  1594. if (tokenValue.charAt(0) === "_" && this.options.underscoreHack) {
  1595. hack = "_";
  1596. tokenValue = tokenValue.substring(1);
  1597. }
  1598. value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol));
  1599. this._readWhitespace();
  1600. }
  1601. return value;
  1602. },
  1603. //Augmented with CSS3 Selectors
  1604. _ruleset: function() {
  1605. /*
  1606. * ruleset
  1607. * : selectors_group
  1608. * '{' S* declaration? [ ';' S* declaration? ]* '}' S*
  1609. * ;
  1610. */
  1611. var tokenStream = this._tokenStream,
  1612. tt,
  1613. selectors;
  1614. /*
  1615. * Error Recovery: If even a single selector fails to parse,
  1616. * then the entire ruleset should be thrown away.
  1617. */
  1618. try {
  1619. selectors = this._selectors_group();
  1620. } catch (ex) {
  1621. if (ex instanceof SyntaxError && !this.options.strict) {
  1622. //fire error event
  1623. this.fire({
  1624. type: "error",
  1625. error: ex,
  1626. message: ex.message,
  1627. line: ex.line,
  1628. col: ex.col
  1629. });
  1630. //skip over everything until closing brace
  1631. tt = tokenStream.advance([Tokens.RBRACE]);
  1632. if (tt === Tokens.RBRACE) {
  1633. //if there's a right brace, the rule is finished so don't do anything
  1634. } else {
  1635. //otherwise, rethrow the error because it wasn't handled properly
  1636. throw ex;
  1637. }
  1638. } else {
  1639. //not a syntax error, rethrow it
  1640. throw ex;
  1641. }
  1642. //trigger parser to continue
  1643. return true;
  1644. }
  1645. //if it got here, all selectors parsed
  1646. if (selectors) {
  1647. this.fire({
  1648. type: "startrule",
  1649. selectors: selectors,
  1650. line: selectors[0].line,
  1651. col: selectors[0].col
  1652. });
  1653. this._readDeclarations(true);
  1654. this.fire({
  1655. type: "endrule",
  1656. selectors: selectors,
  1657. line: selectors[0].line,
  1658. col: selectors[0].col
  1659. });
  1660. }
  1661. return selectors;
  1662. },
  1663. //CSS3 Selectors
  1664. _selectors_group: function() {
  1665. /*
  1666. * selectors_group
  1667. * : selector [ COMMA S* selector ]*
  1668. * ;
  1669. */
  1670. var tokenStream = this._tokenStream,
  1671. selectors = [],
  1672. selector;
  1673. selector = this._selector();
  1674. if (selector !== null) {
  1675. selectors.push(selector);
  1676. while (tokenStream.match(Tokens.COMMA)) {
  1677. this._readWhitespace();
  1678. selector = this._selector();
  1679. if (selector !== null) {
  1680. selectors.push(selector);
  1681. } else {
  1682. this._unexpectedToken(tokenStream.LT(1));
  1683. }
  1684. }
  1685. }
  1686. return selectors.length ? selectors : null;
  1687. },
  1688. //CSS3 Selectors
  1689. _selector: function() {
  1690. /*
  1691. * selector
  1692. * : simple_selector_sequence [ combinator simple_selector_sequence ]*
  1693. * ;
  1694. */
  1695. var tokenStream = this._tokenStream,
  1696. selector = [],
  1697. nextSelector = null,
  1698. combinator = null,
  1699. ws = null;
  1700. //if there's no simple selector, then there's no selector
  1701. nextSelector = this._simple_selector_sequence();
  1702. if (nextSelector === null) {
  1703. return null;
  1704. }
  1705. selector.push(nextSelector);
  1706. do {
  1707. //look for a combinator
  1708. combinator = this._combinator();
  1709. if (combinator !== null) {
  1710. selector.push(combinator);
  1711. nextSelector = this._simple_selector_sequence();
  1712. //there must be a next selector
  1713. if (nextSelector === null) {
  1714. this._unexpectedToken(tokenStream.LT(1));
  1715. } else {
  1716. //nextSelector is an instance of SelectorPart
  1717. selector.push(nextSelector);
  1718. }
  1719. } else {
  1720. //if there's not whitespace, we're done
  1721. if (this._readWhitespace()) {
  1722. //add whitespace separator
  1723. ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol);
  1724. //combinator is not required
  1725. combinator = this._combinator();
  1726. //selector is required if there's a combinator
  1727. nextSelector = this._simple_selector_sequence();
  1728. if (nextSelector === null) {
  1729. if (combinator !== null) {
  1730. this._unexpectedToken(tokenStream.LT(1));
  1731. }
  1732. } else {
  1733. if (combinator !== null) {
  1734. selector.push(combinator);
  1735. } else {
  1736. selector.push(ws);
  1737. }
  1738. selector.push(nextSelector);
  1739. }
  1740. } else {
  1741. break;
  1742. }
  1743. }
  1744. } while (true);
  1745. return new Selector(selector, selector[0].line, selector[0].col);
  1746. },
  1747. //CSS3 Selectors
  1748. _simple_selector_sequence: function() {
  1749. /*
  1750. * simple_selector_sequence
  1751. * : [ type_selector | universal ]
  1752. * [ HASH | class | attrib | pseudo | any | negation ]*
  1753. * | [ HASH | class | attrib | pseudo | any | negation ]+
  1754. * ;
  1755. */
  1756. var tokenStream = this._tokenStream,
  1757. //parts of a simple selector
  1758. elementName = null,
  1759. modifiers = [],
  1760. //complete selector text
  1761. selectorText= "",
  1762. //the different parts after the element name to search for
  1763. components = [
  1764. //HASH
  1765. function() {
  1766. return tokenStream.match(Tokens.HASH) ?
  1767. new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
  1768. null;
  1769. },
  1770. this._class,
  1771. this._attrib,
  1772. this._pseudo,
  1773. this._any,
  1774. this._negation
  1775. ],
  1776. i = 0,
  1777. len = components.length,
  1778. component = null,
  1779. line,
  1780. col;
  1781. //get starting line and column for the selector
  1782. line = tokenStream.LT(1).startLine;
  1783. col = tokenStream.LT(1).startCol;
  1784. elementName = this._type_selector();
  1785. if (!elementName) {
  1786. elementName = this._universal();
  1787. }
  1788. if (elementName !== null) {
  1789. selectorText += elementName;
  1790. }
  1791. while (true) {
  1792. //whitespace means we're done
  1793. if (tokenStream.peek() === Tokens.S) {
  1794. break;
  1795. }
  1796. //check for each component
  1797. while (i < len && component === null) {
  1798. component = components[i++].call(this);
  1799. }
  1800. if (component === null) {
  1801. //we don't have a selector
  1802. if (selectorText === "") {
  1803. return null;
  1804. } else {
  1805. break;
  1806. }
  1807. } else {
  1808. i = 0;
  1809. modifiers.push(component);
  1810. selectorText += component.toString();
  1811. component = null;
  1812. }
  1813. }
  1814. return selectorText !== "" ?
  1815. new SelectorPart(elementName, modifiers, selectorText, line, col) :
  1816. null;
  1817. },
  1818. //CSS3 Selectors
  1819. _type_selector: function() {
  1820. /*
  1821. * type_selector
  1822. * : [ namespace_prefix ]? element_name
  1823. * ;
  1824. */
  1825. var tokenStream = this._tokenStream,
  1826. ns = this._namespace_prefix(),
  1827. elementName = this._element_name();
  1828. if (!elementName) {
  1829. /*
  1830. * Need to back out the namespace that was read due to both
  1831. * type_selector and universal reading namespace_prefix
  1832. * first. Kind of hacky, but only way I can figure out
  1833. * right now how to not change the grammar.
  1834. */
  1835. if (ns) {
  1836. tokenStream.unget();
  1837. if (ns.length > 1) {
  1838. tokenStream.unget();
  1839. }
  1840. }
  1841. return null;
  1842. } else {
  1843. if (ns) {
  1844. elementName.text = ns + elementName.text;
  1845. elementName.col -= ns.length;
  1846. }
  1847. return elementName;
  1848. }
  1849. },
  1850. //CSS3 Selectors
  1851. _class: function() {
  1852. /*
  1853. * class
  1854. * : '.' IDENT
  1855. * ;
  1856. */
  1857. var tokenStream = this._tokenStream,
  1858. token;
  1859. if (tokenStream.match(Tokens.DOT)) {
  1860. tokenStream.mustMatch(Tokens.IDENT);
  1861. token = tokenStream.token();
  1862. return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);
  1863. } else {
  1864. return null;
  1865. }
  1866. },
  1867. //CSS3 Selectors
  1868. _element_name: function() {
  1869. /*
  1870. * element_name
  1871. * : IDENT
  1872. * ;
  1873. */
  1874. var tokenStream = this._tokenStream,
  1875. token;
  1876. if (tokenStream.match(Tokens.IDENT)) {
  1877. token = tokenStream.token();
  1878. return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol);
  1879. } else {
  1880. return null;
  1881. }
  1882. },
  1883. //CSS3 Selectors
  1884. _namespace_prefix: function() {
  1885. /*
  1886. * namespace_prefix
  1887. * : [ IDENT | '*' ]? '|'
  1888. * ;
  1889. */
  1890. var tokenStream = this._tokenStream,
  1891. value = "";
  1892. //verify that this is a namespace prefix
  1893. if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE) {
  1894. if (tokenStream.match([Tokens.IDENT, Tokens.STAR])) {
  1895. value += tokenStream.token().value;
  1896. }
  1897. tokenStream.mustMatch(Tokens.PIPE);
  1898. value += "|";
  1899. }
  1900. return value.length ? value : null;
  1901. },
  1902. //CSS3 Selectors
  1903. _universal: function() {
  1904. /*
  1905. * universal
  1906. * : [ namespace_prefix ]? '*'
  1907. * ;
  1908. */
  1909. var tokenStream = this._tokenStream,
  1910. value = "",
  1911. ns;
  1912. ns = this._namespace_prefix();
  1913. if (ns) {
  1914. value += ns;
  1915. }
  1916. if (tokenStream.match(Tokens.STAR)) {
  1917. value += "*";
  1918. }
  1919. return value.length ? value : null;
  1920. },
  1921. //CSS3 Selectors
  1922. _attrib: function() {
  1923. /*
  1924. * attrib
  1925. * : '[' S* [ namespace_prefix ]? IDENT S*
  1926. * [ [ PREFIXMATCH |
  1927. * SUFFIXMATCH |
  1928. * SUBSTRINGMATCH |
  1929. * '=' |
  1930. * INCLUDES |
  1931. * DASHMATCH ] S* [ IDENT | STRING ] S*
  1932. * ]? ']'
  1933. * ;
  1934. */
  1935. var tokenStream = this._tokenStream,
  1936. value = null,
  1937. ns,
  1938. token;
  1939. if (tokenStream.match(Tokens.LBRACKET)) {
  1940. token = tokenStream.token();
  1941. value = token.value;
  1942. value += this._readWhitespace();
  1943. ns = this._namespace_prefix();
  1944. if (ns) {
  1945. value += ns;
  1946. }
  1947. tokenStream.mustMatch(Tokens.IDENT);
  1948. value += tokenStream.token().value;
  1949. value += this._readWhitespace();
  1950. if (tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH,
  1951. Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])) {
  1952. value += tokenStream.token().value;
  1953. value += this._readWhitespace();
  1954. tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
  1955. value += tokenStream.token().value;
  1956. value += this._readWhitespace();
  1957. if (tokenStream.match([Tokens.IDENT])) {
  1958. if (tokenStream.token().value.toLowerCase() == 'i') {
  1959. value += tokenStream.token().value;
  1960. value += this._readWhitespace();
  1961. } else {
  1962. tokenStream.unget();
  1963. }
  1964. }
  1965. }
  1966. tokenStream.mustMatch(Tokens.RBRACKET);
  1967. return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol);
  1968. } else {
  1969. return null;
  1970. }
  1971. },
  1972. //CSS3 Selectors
  1973. _pseudo: function() {
  1974. /*
  1975. * pseudo
  1976. * : ':' ':'? [ IDENT | functional_pseudo ]
  1977. * ;
  1978. */
  1979. var tokenStream = this._tokenStream,
  1980. pseudo = null,
  1981. colons = ":",
  1982. line,
  1983. col;
  1984. if (tokenStream.match(Tokens.COLON)) {
  1985. if (tokenStream.match(Tokens.COLON)) {
  1986. colons += ":";
  1987. }
  1988. if (tokenStream.match(Tokens.IDENT)) {
  1989. pseudo = tokenStream.token().value;
  1990. line = tokenStream.token().startLine;
  1991. col = tokenStream.token().startCol - colons.length;
  1992. } else if (tokenStream.peek() === Tokens.FUNCTION) {
  1993. line = tokenStream.LT(1).startLine;
  1994. col = tokenStream.LT(1).startCol - colons.length;
  1995. pseudo = this._functional_pseudo();
  1996. }
  1997. if (pseudo) {
  1998. pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col);
  1999. } else {
  2000. var startLine = tokenStream.LT(1).startLine,
  2001. startCol = tokenStream.LT(0).startCol;
  2002. throw new SyntaxError("Expected a `FUNCTION` or `IDENT` after colon at line " + startLine + ", col " + startCol + ".", startLine, startCol);
  2003. }
  2004. }
  2005. return pseudo;
  2006. },
  2007. //CSS3 Selectors
  2008. _functional_pseudo: function() {
  2009. /*
  2010. * functional_pseudo
  2011. * : FUNCTION S* expression ')'
  2012. * ;
  2013. */
  2014. var tokenStream = this._tokenStream,
  2015. value = null;
  2016. if (tokenStream.match(Tokens.FUNCTION)) {
  2017. value = tokenStream.token().value;
  2018. value += this._readWhitespace();
  2019. value += this._expression();
  2020. tokenStream.mustMatch(Tokens.RPAREN);
  2021. value += ")";
  2022. }
  2023. return value;
  2024. },
  2025. //CSS3 Selectors
  2026. _expression: function() {
  2027. /*
  2028. * expression
  2029. * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
  2030. * ;
  2031. */
  2032. var tokenStream = this._tokenStream,
  2033. value = "";
  2034. while (tokenStream.match([Tokens.PLUS, Tokens.MINUS, Tokens.DIMENSION,
  2035. Tokens.NUMBER, Tokens.STRING, Tokens.IDENT, Tokens.LENGTH,
  2036. Tokens.FREQ, Tokens.ANGLE, Tokens.TIME,
  2037. Tokens.RESOLUTION, Tokens.SLASH])) {
  2038. value += tokenStream.token().value;
  2039. value += this._readWhitespace();
  2040. }
  2041. return value.length ? value : null;
  2042. },
  2043. //CSS3 Selectors
  2044. _any: function() {
  2045. /*
  2046. * any
  2047. * : ANY S* any_arg S* ')'
  2048. * ;
  2049. */
  2050. var tokenStream = this._tokenStream,
  2051. line,
  2052. col,
  2053. value = "",
  2054. arg,
  2055. subpart = null;
  2056. if (tokenStream.match(Tokens.ANY)) {
  2057. value = tokenStream.token().value;
  2058. line = tokenStream.token().startLine;
  2059. col = tokenStream.token().startCol;
  2060. value += this._readWhitespace();
  2061. arg = this._selectors_group();
  2062. value += arg;
  2063. value += this._readWhitespace();
  2064. tokenStream.match(Tokens.RPAREN);
  2065. value += tokenStream.token().value;
  2066. subpart = new SelectorSubPart(value, "any", line, col);
  2067. subpart.args.push(arg);
  2068. }
  2069. return subpart;
  2070. },
  2071. //CSS3 Selectors
  2072. _negation: function() {
  2073. /*
  2074. * negation
  2075. * : NOT S* negation_arg S* ')'
  2076. * ;
  2077. */
  2078. var tokenStream = this._tokenStream,
  2079. line,
  2080. col,
  2081. value = "",
  2082. arg,
  2083. subpart = null;
  2084. if (tokenStream.match(Tokens.NOT)) {
  2085. value = tokenStream.token().value;
  2086. line = tokenStream.token().startLine;
  2087. col = tokenStream.token().startCol;
  2088. value += this._readWhitespace();
  2089. arg = this._negation_arg();
  2090. value += arg;
  2091. value += this._readWhitespace();
  2092. tokenStream.match(Tokens.RPAREN);
  2093. value += tokenStream.token().value;
  2094. subpart = new SelectorSubPart(value, "not", line, col);
  2095. subpart.args.push(arg);
  2096. }
  2097. return subpart;
  2098. },
  2099. //CSS3 Selectors
  2100. _negation_arg: function() {
  2101. /*
  2102. * negation_arg
  2103. * : type_selector | universal | HASH | class | attrib | pseudo
  2104. * ;
  2105. */
  2106. var tokenStream = this._tokenStream,
  2107. args = [
  2108. this._type_selector,
  2109. this._universal,
  2110. function() {
  2111. return tokenStream.match(Tokens.HASH) ?
  2112. new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
  2113. null;
  2114. },
  2115. this._class,
  2116. this._attrib,
  2117. this._pseudo
  2118. ],
  2119. arg = null,
  2120. i = 0,
  2121. len = args.length,
  2122. line,
  2123. col,
  2124. part;
  2125. line = tokenStream.LT(1).startLine;
  2126. col = tokenStream.LT(1).startCol;
  2127. while (i < len && arg === null) {
  2128. arg = args[i].call(this);
  2129. i++;
  2130. }
  2131. //must be a negation arg
  2132. if (arg === null) {
  2133. this._unexpectedToken(tokenStream.LT(1));
  2134. }
  2135. //it's an element name
  2136. if (arg.type === "elementName") {
  2137. part = new SelectorPart(arg, [], arg.toString(), line, col);
  2138. } else {
  2139. part = new SelectorPart(null, [arg], arg.toString(), line, col);
  2140. }
  2141. return part;
  2142. },
  2143. _declaration: function() {
  2144. /*
  2145. * declaration
  2146. * : property ':' S* expr prio?
  2147. * | /( empty )/
  2148. * ;
  2149. */
  2150. var tokenStream = this._tokenStream,
  2151. property = null,
  2152. expr = null,
  2153. prio = null,
  2154. invalid = null,
  2155. propertyName= "";
  2156. property = this._property();
  2157. if (property !== null) {
  2158. tokenStream.mustMatch(Tokens.COLON);
  2159. this._readWhitespace();
  2160. expr = this._expr();
  2161. //if there's no parts for the value, it's an error
  2162. if (!expr || expr.length === 0) {
  2163. this._unexpectedToken(tokenStream.LT(1));
  2164. }
  2165. prio = this._prio();
  2166. /*
  2167. * If hacks should be allowed, then only check the root
  2168. * property. If hacks should not be allowed, treat
  2169. * _property or *property as invalid properties.
  2170. */
  2171. propertyName = property.toString();
  2172. if (this.options.starHack && property.hack === "*" ||
  2173. this.options.underscoreHack && property.hack === "_") {
  2174. propertyName = property.text;
  2175. }
  2176. try {
  2177. this._validateProperty(propertyName, expr);
  2178. } catch (ex) {
  2179. invalid = ex;
  2180. }
  2181. this.fire({
  2182. type: "property",
  2183. property: property,
  2184. value: expr,
  2185. important: prio,
  2186. line: property.line,
  2187. col: property.col,
  2188. invalid: invalid
  2189. });
  2190. return true;
  2191. } else {
  2192. return false;
  2193. }
  2194. },
  2195. _prio: function() {
  2196. /*
  2197. * prio
  2198. * : IMPORTANT_SYM S*
  2199. * ;
  2200. */
  2201. var tokenStream = this._tokenStream,
  2202. result = tokenStream.match(Tokens.IMPORTANT_SYM);
  2203. this._readWhitespace();
  2204. return result;
  2205. },
  2206. _expr: function(inFunction) {
  2207. /*
  2208. * expr
  2209. * : term [ operator term ]*
  2210. * ;
  2211. */
  2212. var values = [],
  2213. //valueParts = [],
  2214. value = null,
  2215. operator = null;
  2216. value = this._term(inFunction);
  2217. if (value !== null) {
  2218. values.push(value);
  2219. do {
  2220. operator = this._operator(inFunction);
  2221. //if there's an operator, keep building up the value parts
  2222. if (operator) {
  2223. values.push(operator);
  2224. } /*else {
  2225. //if there's not an operator, you have a full value
  2226. values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
  2227. valueParts = [];
  2228. }*/
  2229. value = this._term(inFunction);
  2230. if (value === null) {
  2231. break;
  2232. } else {
  2233. values.push(value);
  2234. }
  2235. } while (true);
  2236. }
  2237. //cleanup
  2238. /*if (valueParts.length) {
  2239. values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
  2240. }*/
  2241. return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null;
  2242. },
  2243. _term: function(inFunction) {
  2244. /*
  2245. * term
  2246. * : unary_operator?
  2247. * [ NUMBER S* | PERCENTAGE S* | LENGTH S* | ANGLE S* |
  2248. * TIME S* | FREQ S* | function | ie_function ]
  2249. * | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor
  2250. * ;
  2251. */
  2252. var tokenStream = this._tokenStream,
  2253. unary = null,
  2254. value = null,
  2255. endChar = null,
  2256. part = null,
  2257. token,
  2258. line,
  2259. col;
  2260. //returns the operator or null
  2261. unary = this._unary_operator();
  2262. if (unary !== null) {
  2263. line = tokenStream.token().startLine;
  2264. col = tokenStream.token().startCol;
  2265. }
  2266. //exception for IE filters
  2267. if (tokenStream.peek() === Tokens.IE_FUNCTION && this.options.ieFilters) {
  2268. value = this._ie_function();
  2269. if (unary === null) {
  2270. line = tokenStream.token().startLine;
  2271. col = tokenStream.token().startCol;
  2272. }
  2273. //see if it's a simple block
  2274. } else if (inFunction && tokenStream.match([Tokens.LPAREN, Tokens.LBRACE, Tokens.LBRACKET])) {
  2275. token = tokenStream.token();
  2276. endChar = token.endChar;
  2277. value = token.value + this._expr(inFunction).text;
  2278. if (unary === null) {
  2279. line = tokenStream.token().startLine;
  2280. col = tokenStream.token().startCol;
  2281. }
  2282. tokenStream.mustMatch(Tokens.type(endChar));
  2283. value += endChar;
  2284. this._readWhitespace();
  2285. //see if there's a simple match
  2286. } else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH,
  2287. Tokens.ANGLE, Tokens.TIME,
  2288. Tokens.FREQ, Tokens.STRING, Tokens.IDENT, Tokens.URI, Tokens.UNICODE_RANGE])) {
  2289. value = tokenStream.token().value;
  2290. if (unary === null) {
  2291. line = tokenStream.token().startLine;
  2292. col = tokenStream.token().startCol;
  2293. // Correct potentially-inaccurate IDENT parsing in
  2294. // PropertyValuePart constructor.
  2295. part = PropertyValuePart.fromToken(tokenStream.token());
  2296. }
  2297. this._readWhitespace();
  2298. } else {
  2299. //see if it's a color
  2300. token = this._hexcolor();
  2301. if (token === null) {
  2302. //if there's no unary, get the start of the next token for line/col info
  2303. if (unary === null) {
  2304. line = tokenStream.LT(1).startLine;
  2305. col = tokenStream.LT(1).startCol;
  2306. }
  2307. //has to be a function
  2308. if (value === null) {
  2309. /*
  2310. * This checks for alpha(opacity=0) style of IE
  2311. * functions. IE_FUNCTION only presents progid: style.
  2312. */
  2313. if (tokenStream.LA(3) === Tokens.EQUALS && this.options.ieFilters) {
  2314. value = this._ie_function();
  2315. } else {
  2316. value = this._function();
  2317. }
  2318. }
  2319. /*if (value === null) {
  2320. return null;
  2321. //throw new Error("Expected identifier at line " + tokenStream.token().startLine + ", character " + tokenStream.token().startCol + ".");
  2322. }*/
  2323. } else {
  2324. value = token.value;
  2325. if (unary === null) {
  2326. line = token.startLine;
  2327. col = token.startCol;
  2328. }
  2329. }
  2330. }
  2331. return part !== null ? part : value !== null ?
  2332. new PropertyValuePart(unary !== null ? unary + value : value, line, col) :
  2333. null;
  2334. },
  2335. _function: function() {
  2336. /*
  2337. * function
  2338. * : FUNCTION S* expr ')' S*
  2339. * ;
  2340. */
  2341. var tokenStream = this._tokenStream,
  2342. functionText = null,
  2343. expr = null,
  2344. lt;
  2345. if (tokenStream.match(Tokens.FUNCTION)) {
  2346. functionText = tokenStream.token().value;
  2347. this._readWhitespace();
  2348. expr = this._expr(true);
  2349. functionText += expr;
  2350. //START: Horrible hack in case it's an IE filter
  2351. if (this.options.ieFilters && tokenStream.peek() === Tokens.EQUALS) {
  2352. do {
  2353. if (this._readWhitespace()) {
  2354. functionText += tokenStream.token().value;
  2355. }
  2356. //might be second time in the loop
  2357. if (tokenStream.LA(0) === Tokens.COMMA) {
  2358. functionText += tokenStream.token().value;
  2359. }
  2360. tokenStream.match(Tokens.IDENT);
  2361. functionText += tokenStream.token().value;
  2362. tokenStream.match(Tokens.EQUALS);
  2363. functionText += tokenStream.token().value;
  2364. //functionText += this._term();
  2365. lt = tokenStream.peek();
  2366. while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN && lt !== Tokens.EOF) {
  2367. tokenStream.get();
  2368. functionText += tokenStream.token().value;
  2369. lt = tokenStream.peek();
  2370. }
  2371. } while (tokenStream.match([Tokens.COMMA, Tokens.S]));
  2372. }
  2373. //END: Horrible Hack
  2374. tokenStream.match(Tokens.RPAREN);
  2375. functionText += ")";
  2376. this._readWhitespace();
  2377. }
  2378. return functionText;
  2379. },
  2380. _ie_function: function() {
  2381. /* (My own extension)
  2382. * ie_function
  2383. * : IE_FUNCTION S* IDENT '=' term [S* ','? IDENT '=' term]+ ')' S*
  2384. * ;
  2385. */
  2386. var tokenStream = this._tokenStream,
  2387. functionText = null,
  2388. lt;
  2389. //IE function can begin like a regular function, too
  2390. if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])) {
  2391. functionText = tokenStream.token().value;
  2392. do {
  2393. if (this._readWhitespace()) {
  2394. functionText += tokenStream.token().value;
  2395. }
  2396. //might be second time in the loop
  2397. if (tokenStream.LA(0) === Tokens.COMMA) {
  2398. functionText += tokenStream.token().value;
  2399. }
  2400. tokenStream.match(Tokens.IDENT);
  2401. functionText += tokenStream.token().value;
  2402. tokenStream.match(Tokens.EQUALS);
  2403. functionText += tokenStream.token().value;
  2404. //functionText += this._term();
  2405. lt = tokenStream.peek();
  2406. while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN) {
  2407. tokenStream.get();
  2408. functionText += tokenStream.token().value;
  2409. lt = tokenStream.peek();
  2410. }
  2411. } while (tokenStream.match([Tokens.COMMA, Tokens.S]));
  2412. tokenStream.match(Tokens.RPAREN);
  2413. functionText += ")";
  2414. this._readWhitespace();
  2415. }
  2416. return functionText;
  2417. },
  2418. _hexcolor: function() {
  2419. /*
  2420. * There is a constraint on the color that it must
  2421. * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
  2422. * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
  2423. *
  2424. * hexcolor
  2425. * : HASH S*
  2426. * ;
  2427. */
  2428. var tokenStream = this._tokenStream,
  2429. token = null,
  2430. color;
  2431. if (tokenStream.match(Tokens.HASH)) {
  2432. //need to do some validation here
  2433. token = tokenStream.token();
  2434. color = token.value;
  2435. if (!/#[a-f0-9]{3,6}/i.test(color)) {
  2436. throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
  2437. }
  2438. this._readWhitespace();
  2439. }
  2440. return token;
  2441. },
  2442. //-----------------------------------------------------------------
  2443. // Animations methods
  2444. //-----------------------------------------------------------------
  2445. _keyframes: function() {
  2446. /*
  2447. * keyframes:
  2448. * : KEYFRAMES_SYM S* keyframe_name S* '{' S* keyframe_rule* '}' {
  2449. * ;
  2450. */
  2451. var tokenStream = this._tokenStream,
  2452. token,
  2453. tt,
  2454. name,
  2455. prefix = "";
  2456. tokenStream.mustMatch(Tokens.KEYFRAMES_SYM);
  2457. token = tokenStream.token();
  2458. if (/^@\-([^\-]+)\-/.test(token.value)) {
  2459. prefix = RegExp.$1;
  2460. }
  2461. this._readWhitespace();
  2462. name = this._keyframe_name();
  2463. this._readWhitespace();
  2464. tokenStream.mustMatch(Tokens.LBRACE);
  2465. this.fire({
  2466. type: "startkeyframes",
  2467. name: name,
  2468. prefix: prefix,
  2469. line: token.startLine,
  2470. col: token.startCol
  2471. });
  2472. this._readWhitespace();
  2473. tt = tokenStream.peek();
  2474. //check for key
  2475. while (tt === Tokens.IDENT || tt === Tokens.PERCENTAGE) {
  2476. this._keyframe_rule();
  2477. this._readWhitespace();
  2478. tt = tokenStream.peek();
  2479. }
  2480. this.fire({
  2481. type: "endkeyframes",
  2482. name: name,
  2483. prefix: prefix,
  2484. line: token.startLine,
  2485. col: token.startCol
  2486. });
  2487. this._readWhitespace();
  2488. tokenStream.mustMatch(Tokens.RBRACE);
  2489. this._readWhitespace();
  2490. },
  2491. _keyframe_name: function() {
  2492. /*
  2493. * keyframe_name:
  2494. * : IDENT
  2495. * | STRING
  2496. * ;
  2497. */
  2498. var tokenStream = this._tokenStream;
  2499. tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
  2500. return SyntaxUnit.fromToken(tokenStream.token());
  2501. },
  2502. _keyframe_rule: function() {
  2503. /*
  2504. * keyframe_rule:
  2505. * : key_list S*
  2506. * '{' S* declaration [ ';' S* declaration ]* '}' S*
  2507. * ;
  2508. */
  2509. var keyList = this._key_list();
  2510. this.fire({
  2511. type: "startkeyframerule",
  2512. keys: keyList,
  2513. line: keyList[0].line,
  2514. col: keyList[0].col
  2515. });
  2516. this._readDeclarations(true);
  2517. this.fire({
  2518. type: "endkeyframerule",
  2519. keys: keyList,
  2520. line: keyList[0].line,
  2521. col: keyList[0].col
  2522. });
  2523. },
  2524. _key_list: function() {
  2525. /*
  2526. * key_list:
  2527. * : key [ S* ',' S* key]*
  2528. * ;
  2529. */
  2530. var tokenStream = this._tokenStream,
  2531. keyList = [];
  2532. //must be least one key
  2533. keyList.push(this._key());
  2534. this._readWhitespace();
  2535. while (tokenStream.match(Tokens.COMMA)) {
  2536. this._readWhitespace();
  2537. keyList.push(this._key());
  2538. this._readWhitespace();
  2539. }
  2540. return keyList;
  2541. },
  2542. _key: function() {
  2543. /*
  2544. * There is a restriction that IDENT can be only "from" or "to".
  2545. *
  2546. * key
  2547. * : PERCENTAGE
  2548. * | IDENT
  2549. * ;
  2550. */
  2551. var tokenStream = this._tokenStream,
  2552. token;
  2553. if (tokenStream.match(Tokens.PERCENTAGE)) {
  2554. return SyntaxUnit.fromToken(tokenStream.token());
  2555. } else if (tokenStream.match(Tokens.IDENT)) {
  2556. token = tokenStream.token();
  2557. if (/from|to/i.test(token.value)) {
  2558. return SyntaxUnit.fromToken(token);
  2559. }
  2560. tokenStream.unget();
  2561. }
  2562. //if it gets here, there wasn't a valid token, so time to explode
  2563. this._unexpectedToken(tokenStream.LT(1));
  2564. },
  2565. //-----------------------------------------------------------------
  2566. // Helper methods
  2567. //-----------------------------------------------------------------
  2568. /**
  2569. * Not part of CSS grammar, but useful for skipping over
  2570. * combination of white space and HTML-style comments.
  2571. * @return {void}
  2572. * @method _skipCruft
  2573. * @private
  2574. */
  2575. _skipCruft: function() {
  2576. while (this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])) {
  2577. //noop
  2578. }
  2579. },
  2580. /**
  2581. * Not part of CSS grammar, but this pattern occurs frequently
  2582. * in the official CSS grammar. Split out here to eliminate
  2583. * duplicate code.
  2584. * @param {Boolean} checkStart Indicates if the rule should check
  2585. * for the left brace at the beginning.
  2586. * @param {Boolean} readMargins Indicates if the rule should check
  2587. * for margin patterns.
  2588. * @return {void}
  2589. * @method _readDeclarations
  2590. * @private
  2591. */
  2592. _readDeclarations: function(checkStart, readMargins) {
  2593. /*
  2594. * Reads the pattern
  2595. * S* '{' S* declaration [ ';' S* declaration ]* '}' S*
  2596. * or
  2597. * S* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
  2598. * Note that this is how it is described in CSS3 Paged Media, but is actually incorrect.
  2599. * A semicolon is only necessary following a declaration if there's another declaration
  2600. * or margin afterwards.
  2601. */
  2602. var tokenStream = this._tokenStream,
  2603. tt;
  2604. this._readWhitespace();
  2605. if (checkStart) {
  2606. tokenStream.mustMatch(Tokens.LBRACE);
  2607. }
  2608. this._readWhitespace();
  2609. try {
  2610. while (true) {
  2611. if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())) {
  2612. //noop
  2613. } else if (this._declaration()) {
  2614. if (!tokenStream.match(Tokens.SEMICOLON)) {
  2615. break;
  2616. }
  2617. } else {
  2618. break;
  2619. }
  2620. //if ((!this._margin() && !this._declaration()) || !tokenStream.match(Tokens.SEMICOLON)){
  2621. // break;
  2622. //}
  2623. this._readWhitespace();
  2624. }
  2625. tokenStream.mustMatch(Tokens.RBRACE);
  2626. this._readWhitespace();
  2627. } catch (ex) {
  2628. if (ex instanceof SyntaxError && !this.options.strict) {
  2629. //fire error event
  2630. this.fire({
  2631. type: "error",
  2632. error: ex,
  2633. message: ex.message,
  2634. line: ex.line,
  2635. col: ex.col
  2636. });
  2637. //see if there's another declaration
  2638. tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]);
  2639. if (tt === Tokens.SEMICOLON) {
  2640. //if there's a semicolon, then there might be another declaration
  2641. this._readDeclarations(false, readMargins);
  2642. } else if (tt !== Tokens.RBRACE) {
  2643. //if there's a right brace, the rule is finished so don't do anything
  2644. //otherwise, rethrow the error because it wasn't handled properly
  2645. throw ex;
  2646. }
  2647. } else {
  2648. //not a syntax error, rethrow it
  2649. throw ex;
  2650. }
  2651. }
  2652. },
  2653. /**
  2654. * In some cases, you can end up with two white space tokens in a
  2655. * row. Instead of making a change in every function that looks for
  2656. * white space, this function is used to match as much white space
  2657. * as necessary.
  2658. * @method _readWhitespace
  2659. * @return {String} The white space if found, empty string if not.
  2660. * @private
  2661. */
  2662. _readWhitespace: function() {
  2663. var tokenStream = this._tokenStream,
  2664. ws = "";
  2665. while (tokenStream.match(Tokens.S)) {
  2666. ws += tokenStream.token().value;
  2667. }
  2668. return ws;
  2669. },
  2670. /**
  2671. * Throws an error when an unexpected token is found.
  2672. * @param {Object} token The token that was found.
  2673. * @method _unexpectedToken
  2674. * @return {void}
  2675. * @private
  2676. */
  2677. _unexpectedToken: function(token) {
  2678. throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
  2679. },
  2680. /**
  2681. * Helper method used for parsing subparts of a style sheet.
  2682. * @return {void}
  2683. * @method _verifyEnd
  2684. * @private
  2685. */
  2686. _verifyEnd: function() {
  2687. if (this._tokenStream.LA(1) !== Tokens.EOF) {
  2688. this._unexpectedToken(this._tokenStream.LT(1));
  2689. }
  2690. },
  2691. //-----------------------------------------------------------------
  2692. // Validation methods
  2693. //-----------------------------------------------------------------
  2694. _validateProperty: function(property, value) {
  2695. Validation.validate(property, value);
  2696. },
  2697. //-----------------------------------------------------------------
  2698. // Parsing methods
  2699. //-----------------------------------------------------------------
  2700. parse: function(input) {
  2701. this._tokenStream = new TokenStream(input, Tokens);
  2702. this._stylesheet();
  2703. },
  2704. parseStyleSheet: function(input) {
  2705. //just passthrough
  2706. return this.parse(input);
  2707. },
  2708. parseMediaQuery: function(input) {
  2709. this._tokenStream = new TokenStream(input, Tokens);
  2710. var result = this._media_query();
  2711. //if there's anything more, then it's an invalid selector
  2712. this._verifyEnd();
  2713. //otherwise return result
  2714. return result;
  2715. },
  2716. /**
  2717. * Parses a property value (everything after the semicolon).
  2718. * @return {parserlib.css.PropertyValue} The property value.
  2719. * @throws parserlib.util.SyntaxError If an unexpected token is found.
  2720. * @method parserPropertyValue
  2721. */
  2722. parsePropertyValue: function(input) {
  2723. this._tokenStream = new TokenStream(input, Tokens);
  2724. this._readWhitespace();
  2725. var result = this._expr();
  2726. //okay to have a trailing white space
  2727. this._readWhitespace();
  2728. //if there's anything more, then it's an invalid selector
  2729. this._verifyEnd();
  2730. //otherwise return result
  2731. return result;
  2732. },
  2733. /**
  2734. * Parses a complete CSS rule, including selectors and
  2735. * properties.
  2736. * @param {String} input The text to parser.
  2737. * @return {Boolean} True if the parse completed successfully, false if not.
  2738. * @method parseRule
  2739. */
  2740. parseRule: function(input) {
  2741. this._tokenStream = new TokenStream(input, Tokens);
  2742. //skip any leading white space
  2743. this._readWhitespace();
  2744. var result = this._ruleset();
  2745. //skip any trailing white space
  2746. this._readWhitespace();
  2747. //if there's anything more, then it's an invalid selector
  2748. this._verifyEnd();
  2749. //otherwise return result
  2750. return result;
  2751. },
  2752. /**
  2753. * Parses a single CSS selector (no comma)
  2754. * @param {String} input The text to parse as a CSS selector.
  2755. * @return {Selector} An object representing the selector.
  2756. * @throws parserlib.util.SyntaxError If an unexpected token is found.
  2757. * @method parseSelector
  2758. */
  2759. parseSelector: function(input) {
  2760. this._tokenStream = new TokenStream(input, Tokens);
  2761. //skip any leading white space
  2762. this._readWhitespace();
  2763. var result = this._selector();
  2764. //skip any trailing white space
  2765. this._readWhitespace();
  2766. //if there's anything more, then it's an invalid selector
  2767. this._verifyEnd();
  2768. //otherwise return result
  2769. return result;
  2770. },
  2771. /**
  2772. * Parses an HTML style attribute: a set of CSS declarations
  2773. * separated by semicolons.
  2774. * @param {String} input The text to parse as a style attribute
  2775. * @return {void}
  2776. * @method parseStyleAttribute
  2777. */
  2778. parseStyleAttribute: function(input) {
  2779. input += "}"; // for error recovery in _readDeclarations()
  2780. this._tokenStream = new TokenStream(input, Tokens);
  2781. this._readDeclarations();
  2782. }
  2783. };
  2784. //copy over onto prototype
  2785. for (prop in additions) {
  2786. if (Object.prototype.hasOwnProperty.call(additions, prop)) {
  2787. proto[prop] = additions[prop];
  2788. }
  2789. }
  2790. return proto;
  2791. }();
  2792. /*
  2793. nth
  2794. : S* [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]? |
  2795. ['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S*
  2796. ;
  2797. */
  2798. },{"../util/EventTarget":23,"../util/SyntaxError":25,"../util/SyntaxUnit":26,"./Combinator":2,"./MediaFeature":4,"./MediaQuery":5,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./TokenStream":17,"./Tokens":18,"./Validation":19}],7:[function(require,module,exports){
  2799. "use strict";
  2800. /* exported Properties */
  2801. var Properties = module.exports = {
  2802. __proto__: null,
  2803. //A
  2804. "align-items" : "flex-start | flex-end | center | baseline | stretch",
  2805. "align-content" : "flex-start | flex-end | center | space-between | space-around | stretch",
  2806. "align-self" : "auto | flex-start | flex-end | center | baseline | stretch",
  2807. "all" : "initial | inherit | unset",
  2808. "-webkit-align-items" : "flex-start | flex-end | center | baseline | stretch",
  2809. "-webkit-align-content" : "flex-start | flex-end | center | space-between | space-around | stretch",
  2810. "-webkit-align-self" : "auto | flex-start | flex-end | center | baseline | stretch",
  2811. "alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>",
  2812. "alignment-baseline" : "auto | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
  2813. "animation" : 1,
  2814. "animation-delay" : "<time>#",
  2815. "animation-direction" : "<single-animation-direction>#",
  2816. "animation-duration" : "<time>#",
  2817. "animation-fill-mode" : "[ none | forwards | backwards | both ]#",
  2818. "animation-iteration-count" : "[ <number> | infinite ]#",
  2819. "animation-name" : "[ none | <single-animation-name> ]#",
  2820. "animation-play-state" : "[ running | paused ]#",
  2821. "animation-timing-function" : 1,
  2822. //vendor prefixed
  2823. "-moz-animation-delay" : "<time>#",
  2824. "-moz-animation-direction" : "[ normal | alternate ]#",
  2825. "-moz-animation-duration" : "<time>#",
  2826. "-moz-animation-iteration-count" : "[ <number> | infinite ]#",
  2827. "-moz-animation-name" : "[ none | <single-animation-name> ]#",
  2828. "-moz-animation-play-state" : "[ running | paused ]#",
  2829. "-ms-animation-delay" : "<time>#",
  2830. "-ms-animation-direction" : "[ normal | alternate ]#",
  2831. "-ms-animation-duration" : "<time>#",
  2832. "-ms-animation-iteration-count" : "[ <number> | infinite ]#",
  2833. "-ms-animation-name" : "[ none | <single-animation-name> ]#",
  2834. "-ms-animation-play-state" : "[ running | paused ]#",
  2835. "-webkit-animation-delay" : "<time>#",
  2836. "-webkit-animation-direction" : "[ normal | alternate ]#",
  2837. "-webkit-animation-duration" : "<time>#",
  2838. "-webkit-animation-fill-mode" : "[ none | forwards | backwards | both ]#",
  2839. "-webkit-animation-iteration-count" : "[ <number> | infinite ]#",
  2840. "-webkit-animation-name" : "[ none | <single-animation-name> ]#",
  2841. "-webkit-animation-play-state" : "[ running | paused ]#",
  2842. "-o-animation-delay" : "<time>#",
  2843. "-o-animation-direction" : "[ normal | alternate ]#",
  2844. "-o-animation-duration" : "<time>#",
  2845. "-o-animation-iteration-count" : "[ <number> | infinite ]#",
  2846. "-o-animation-name" : "[ none | <single-animation-name> ]#",
  2847. "-o-animation-play-state" : "[ running | paused ]#",
  2848. "appearance" : "none | auto",
  2849. "-moz-appearance" : "none | button | button-arrow-down | button-arrow-next | button-arrow-previous | button-arrow-up | button-bevel | button-focus | caret | checkbox | checkbox-container | checkbox-label | checkmenuitem | dualbutton | groupbox | listbox | listitem | menuarrow | menubar | menucheckbox | menuimage | menuitem | menuitemtext | menulist | menulist-button | menulist-text | menulist-textfield | menupopup | menuradio | menuseparator | meterbar | meterchunk | progressbar | progressbar-vertical | progresschunk | progresschunk-vertical | radio | radio-container | radio-label | radiomenuitem | range | range-thumb | resizer | resizerpanel | scale-horizontal | scalethumbend | scalethumb-horizontal | scalethumbstart | scalethumbtick | scalethumb-vertical | scale-vertical | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical | searchfield | separator | sheet | spinner | spinner-downbutton | spinner-textfield | spinner-upbutton | splitter | statusbar | statusbarpanel | tab | tabpanel | tabpanels | tab-scroll-arrow-back | tab-scroll-arrow-forward | textfield | textfield-multiline | toolbar | toolbarbutton | toolbarbutton-dropdown | toolbargripper | toolbox | tooltip | treeheader | treeheadercell | treeheadersortarrow | treeitem | treeline | treetwisty | treetwistyopen | treeview | -moz-mac-unified-toolbar | -moz-win-borderless-glass | -moz-win-browsertabbar-toolbox | -moz-win-communicationstext | -moz-win-communications-toolbox | -moz-win-exclude-glass | -moz-win-glass | -moz-win-mediatext | -moz-win-media-toolbox | -moz-window-button-box | -moz-window-button-box-maximized | -moz-window-button-close | -moz-window-button-maximize | -moz-window-button-minimize | -moz-window-button-restore | -moz-window-frame-bottom | -moz-window-frame-left | -moz-window-frame-right | -moz-window-titlebar | -moz-window-titlebar-maximized",
  2850. "-ms-appearance" : "none | icon | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal",
  2851. "-webkit-appearance" : "none | button | button-bevel | caps-lock-indicator | caret | checkbox | default-button | listbox | listitem | media-fullscreen-button | media-mute-button | media-play-button | media-seek-back-button | media-seek-forward-button | media-slider | media-sliderthumb | menulist | menulist-button | menulist-text | menulist-textfield | push-button | radio | searchfield | searchfield-cancel-button | searchfield-decoration | searchfield-results-button | searchfield-results-decoration | slider-horizontal | slider-vertical | sliderthumb-horizontal | sliderthumb-vertical | square-button | textarea | textfield | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbargripper-horizontal | scrollbargripper-vertical | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical",
  2852. "-o-appearance" : "none | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal",
  2853. "azimuth" : "<azimuth>",
  2854. //B
  2855. "backface-visibility" : "visible | hidden",
  2856. "background" : 1,
  2857. "background-attachment" : "<attachment>#",
  2858. "background-blend-mode" : "<blend-mode>",
  2859. "background-clip" : "<box>#",
  2860. "background-color" : "<color>",
  2861. "background-image" : "<bg-image>#",
  2862. "background-origin" : "<box>#",
  2863. "background-position" : "<bg-position>",
  2864. "background-repeat" : "<repeat-style>#",
  2865. "background-size" : "<bg-size>#",
  2866. "baseline-shift" : "baseline | sub | super | <percentage> | <length>",
  2867. "behavior" : 1,
  2868. "binding" : 1,
  2869. "bleed" : "<length>",
  2870. "bookmark-label" : "<content> | <attr> | <string>",
  2871. "bookmark-level" : "none | <integer>",
  2872. "bookmark-state" : "open | closed",
  2873. "bookmark-target" : "none | <uri> | <attr>",
  2874. "border" : "<border-width> || <border-style> || <color>",
  2875. "border-bottom" : "<border-width> || <border-style> || <color>",
  2876. "border-bottom-color" : "<color>",
  2877. "border-bottom-left-radius" : "<x-one-radius>",
  2878. "border-bottom-right-radius" : "<x-one-radius>",
  2879. "border-bottom-style" : "<border-style>",
  2880. "border-bottom-width" : "<border-width>",
  2881. "border-collapse" : "collapse | separate",
  2882. "border-color" : "<color>{1,4}",
  2883. "border-image" : 1,
  2884. "border-image-outset" : "[ <length> | <number> ]{1,4}",
  2885. "border-image-repeat" : "[ stretch | repeat | round | space ]{1,2}",
  2886. "border-image-slice" : "<border-image-slice>",
  2887. "border-image-source" : "<image> | none",
  2888. "border-image-width" : "[ <length> | <percentage> | <number> | auto ]{1,4}",
  2889. "border-left" : "<border-width> || <border-style> || <color>",
  2890. "border-left-color" : "<color>",
  2891. "border-left-style" : "<border-style>",
  2892. "border-left-width" : "<border-width>",
  2893. "border-radius" : "<border-radius>",
  2894. "border-right" : "<border-width> || <border-style> || <color>",
  2895. "border-right-color" : "<color>",
  2896. "border-right-style" : "<border-style>",
  2897. "border-right-width" : "<border-width>",
  2898. "border-spacing" : "<length>{1,2}",
  2899. "border-style" : "<border-style>{1,4}",
  2900. "border-top" : "<border-width> || <border-style> || <color>",
  2901. "border-top-color" : "<color>",
  2902. "border-top-left-radius" : "<x-one-radius>",
  2903. "border-top-right-radius" : "<x-one-radius>",
  2904. "border-top-style" : "<border-style>",
  2905. "border-top-width" : "<border-width>",
  2906. "border-width" : "<border-width>{1,4}",
  2907. "bottom" : "<margin-width>",
  2908. "-moz-box-align" : "start | end | center | baseline | stretch",
  2909. "-moz-box-decoration-break" : "slice | clone",
  2910. "-moz-box-direction" : "normal | reverse",
  2911. "-moz-box-flex" : "<number>",
  2912. "-moz-box-flex-group" : "<integer>",
  2913. "-moz-box-lines" : "single | multiple",
  2914. "-moz-box-ordinal-group" : "<integer>",
  2915. "-moz-box-orient" : "horizontal | vertical | inline-axis | block-axis",
  2916. "-moz-box-pack" : "start | end | center | justify",
  2917. "-o-box-decoration-break" : "slice | clone",
  2918. "-webkit-box-align" : "start | end | center | baseline | stretch",
  2919. "-webkit-box-decoration-break" : "slice | clone",
  2920. "-webkit-box-direction" : "normal | reverse",
  2921. "-webkit-box-flex" : "<number>",
  2922. "-webkit-box-flex-group" : "<integer>",
  2923. "-webkit-box-lines" : "single | multiple",
  2924. "-webkit-box-ordinal-group" : "<integer>",
  2925. "-webkit-box-orient" : "horizontal | vertical | inline-axis | block-axis",
  2926. "-webkit-box-pack" : "start | end | center | justify",
  2927. "box-decoration-break" : "slice | clone",
  2928. "box-shadow" : "<box-shadow>",
  2929. "box-sizing" : "content-box | border-box",
  2930. "break-after" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
  2931. "break-before" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
  2932. "break-inside" : "auto | avoid | avoid-page | avoid-column",
  2933. //C
  2934. "caption-side" : "top | bottom",
  2935. "clear" : "none | right | left | both",
  2936. "clip" : "<shape> | auto",
  2937. "-webkit-clip-path" : "<clip-source> | <clip-path> | none",
  2938. "clip-path" : "<clip-source> | <clip-path> | none",
  2939. "clip-rule" : "nonzero | evenodd",
  2940. "color" : "<color>",
  2941. "color-interpolation" : "auto | sRGB | linearRGB",
  2942. "color-interpolation-filters" : "auto | sRGB | linearRGB",
  2943. "color-profile" : 1,
  2944. "color-rendering" : "auto | optimizeSpeed | optimizeQuality",
  2945. "column-count" : "<integer> | auto", //https://www.w3.org/TR/css3-multicol/
  2946. "column-fill" : "auto | balance",
  2947. "column-gap" : "<length> | normal",
  2948. "column-rule" : "<border-width> || <border-style> || <color>",
  2949. "column-rule-color" : "<color>",
  2950. "column-rule-style" : "<border-style>",
  2951. "column-rule-width" : "<border-width>",
  2952. "column-span" : "none | all",
  2953. "column-width" : "<length> | auto",
  2954. "columns" : 1,
  2955. "content" : 1,
  2956. "counter-increment" : 1,
  2957. "counter-reset" : 1,
  2958. "crop" : "<shape> | auto",
  2959. "cue" : "cue-after | cue-before",
  2960. "cue-after" : 1,
  2961. "cue-before" : 1,
  2962. "cursor" : 1,
  2963. //D
  2964. "direction" : "ltr | rtl",
  2965. "display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | run-in | ruby | ruby-base | ruby-text | ruby-base-container | ruby-text-container | contents | none | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex",
  2966. "dominant-baseline" : "auto | use-script | no-change | reset-size | ideographic | alphabetic | hanging | mathematical | central | middle | text-after-edge | text-before-edge",
  2967. "drop-initial-after-adjust" : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>",
  2968. "drop-initial-after-align" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
  2969. "drop-initial-before-adjust" : "before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>",
  2970. "drop-initial-before-align" : "caps-height | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
  2971. "drop-initial-size" : "auto | line | <length> | <percentage>",
  2972. "drop-initial-value" : "<integer>",
  2973. //E
  2974. "elevation" : "<angle> | below | level | above | higher | lower",
  2975. "empty-cells" : "show | hide",
  2976. "enable-background" : 1,
  2977. //F
  2978. "fill" : "<paint>",
  2979. "fill-opacity" : "<opacity-value>",
  2980. "fill-rule" : "nonzero | evenodd",
  2981. "filter" : "<filter-function-list> | none",
  2982. "fit" : "fill | hidden | meet | slice",
  2983. "fit-position" : 1,
  2984. "flex" : "<flex>",
  2985. "flex-basis" : "<width>",
  2986. "flex-direction" : "row | row-reverse | column | column-reverse",
  2987. "flex-flow" : "<flex-direction> || <flex-wrap>",
  2988. "flex-grow" : "<number>",
  2989. "flex-shrink" : "<number>",
  2990. "flex-wrap" : "nowrap | wrap | wrap-reverse",
  2991. "-webkit-flex" : "<flex>",
  2992. "-webkit-flex-basis" : "<width>",
  2993. "-webkit-flex-direction" : "row | row-reverse | column | column-reverse",
  2994. "-webkit-flex-flow" : "<flex-direction> || <flex-wrap>",
  2995. "-webkit-flex-grow" : "<number>",
  2996. "-webkit-flex-shrink" : "<number>",
  2997. "-webkit-flex-wrap" : "nowrap | wrap | wrap-reverse",
  2998. "-ms-flex" : "<flex>",
  2999. "-ms-flex-align" : "start | end | center | stretch | baseline",
  3000. "-ms-flex-direction" : "row | row-reverse | column | column-reverse",
  3001. "-ms-flex-order" : "<number>",
  3002. "-ms-flex-pack" : "start | end | center | justify",
  3003. "-ms-flex-wrap" : "nowrap | wrap | wrap-reverse",
  3004. "float" : "left | right | none",
  3005. "float-offset" : 1,
  3006. "flood-color" : 1,
  3007. "flood-opacity" : "<opacity-value>",
  3008. "font" : "<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar",
  3009. "font-family" : "<font-family>",
  3010. "font-feature-settings" : "<feature-tag-value> | normal",
  3011. "font-kerning" : "auto | normal | none",
  3012. "font-size" : "<font-size>",
  3013. "font-size-adjust" : "<number> | none",
  3014. "font-stretch" : "<font-stretch>",
  3015. "font-style" : "<font-style>",
  3016. "font-variant" : "<font-variant> | normal | none",
  3017. "font-variant-alternates" : "<font-variant-alternates> | normal",
  3018. "font-variant-caps" : "<font-variant-caps> | normal",
  3019. "font-variant-east-asian" : "<font-variant-east-asian> | normal",
  3020. "font-variant-ligatures" : "<font-variant-ligatures> | normal | none",
  3021. "font-variant-numeric" : "<font-variant-numeric> | normal",
  3022. "font-variant-position" : "normal | sub | super",
  3023. "font-weight" : "<font-weight>",
  3024. //G
  3025. "glyph-orientation-horizontal" : "<glyph-angle>",
  3026. "glyph-orientation-vertical" : "auto | <glyph-angle>",
  3027. "grid" : 1,
  3028. "grid-area" : 1,
  3029. "grid-auto-columns" : 1,
  3030. "grid-auto-flow" : 1,
  3031. "grid-auto-position" : 1,
  3032. "grid-auto-rows" : 1,
  3033. "grid-cell-stacking" : "columns | rows | layer",
  3034. "grid-column" : 1,
  3035. "grid-columns" : 1,
  3036. "grid-column-align" : "start | end | center | stretch",
  3037. "grid-column-sizing" : 1,
  3038. "grid-column-start" : 1,
  3039. "grid-column-end" : 1,
  3040. "grid-column-span" : "<integer>",
  3041. "grid-flow" : "none | rows | columns",
  3042. "grid-layer" : "<integer>",
  3043. "grid-row" : 1,
  3044. "grid-rows" : 1,
  3045. "grid-row-align" : "start | end | center | stretch",
  3046. "grid-row-start" : 1,
  3047. "grid-row-end" : 1,
  3048. "grid-row-span" : "<integer>",
  3049. "grid-row-sizing" : 1,
  3050. "grid-template" : 1,
  3051. "grid-template-areas" : 1,
  3052. "grid-template-columns" : 1,
  3053. "grid-template-rows" : 1,
  3054. //H
  3055. "hanging-punctuation" : 1,
  3056. "height" : "<margin-width> | <content-sizing>",
  3057. "hyphenate-after" : "<integer> | auto",
  3058. "hyphenate-before" : "<integer> | auto",
  3059. "hyphenate-character" : "<string> | auto",
  3060. "hyphenate-lines" : "no-limit | <integer>",
  3061. "hyphenate-resource" : 1,
  3062. "hyphens" : "none | manual | auto",
  3063. //I
  3064. "icon" : 1,
  3065. "image-orientation" : "angle | auto",
  3066. "image-rendering" : "auto | optimizeSpeed | optimizeQuality",
  3067. "image-resolution" : 1,
  3068. "ime-mode" : "auto | normal | active | inactive | disabled",
  3069. "inline-box-align" : "last | <integer>",
  3070. "isolation" : "auto | isolate",
  3071. //J
  3072. "justify-content" : "flex-start | flex-end | center | space-between | space-around",
  3073. "-webkit-justify-content" : "flex-start | flex-end | center | space-between | space-around",
  3074. //K
  3075. "kerning" : "auto | <length>",
  3076. //L
  3077. "left" : "<margin-width>",
  3078. "letter-spacing" : "<length> | normal",
  3079. "line-height" : "<line-height>",
  3080. "line-break" : "auto | loose | normal | strict",
  3081. "line-stacking" : 1,
  3082. "line-stacking-ruby" : "exclude-ruby | include-ruby",
  3083. "line-stacking-shift" : "consider-shifts | disregard-shifts",
  3084. "line-stacking-strategy" : "inline-line-height | block-line-height | max-height | grid-height",
  3085. "list-style" : 1,
  3086. "list-style-image" : "<uri> | none",
  3087. "list-style-position" : "inside | outside",
  3088. "list-style-type" : "disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-greek | lower-latin | upper-latin | armenian | georgian | lower-alpha | upper-alpha | none",
  3089. //M
  3090. "margin" : "<margin-width>{1,4}",
  3091. "margin-bottom" : "<margin-width>",
  3092. "margin-left" : "<margin-width>",
  3093. "margin-right" : "<margin-width>",
  3094. "margin-top" : "<margin-width>",
  3095. "mark" : 1,
  3096. "mark-after" : 1,
  3097. "mark-before" : 1,
  3098. "marker" : 1,
  3099. "marker-end" : 1,
  3100. "marker-mid" : 1,
  3101. "marker-start" : 1,
  3102. "marks" : 1,
  3103. "marquee-direction" : 1,
  3104. "marquee-play-count" : 1,
  3105. "marquee-speed" : 1,
  3106. "marquee-style" : 1,
  3107. "mask" : 1,
  3108. "max-height" : "<length> | <percentage> | <content-sizing> | none",
  3109. "max-width" : "<length> | <percentage> | <content-sizing> | none",
  3110. "min-height" : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
  3111. "min-width" : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
  3112. "mix-blend-mode" : "<blend-mode>",
  3113. "move-to" : 1,
  3114. //N
  3115. "nav-down" : 1,
  3116. "nav-index" : 1,
  3117. "nav-left" : 1,
  3118. "nav-right" : 1,
  3119. "nav-up" : 1,
  3120. //O
  3121. "object-fit" : "fill | contain | cover | none | scale-down",
  3122. "object-position" : "<position>",
  3123. "opacity" : "<opacity-value>",
  3124. "order" : "<integer>",
  3125. "-webkit-order" : "<integer>",
  3126. "orphans" : "<integer>",
  3127. "outline" : 1,
  3128. "outline-color" : "<color> | invert",
  3129. "outline-offset" : 1,
  3130. "outline-style" : "<border-style>",
  3131. "outline-width" : "<border-width>",
  3132. "overflow" : "visible | hidden | scroll | auto",
  3133. "overflow-style" : 1,
  3134. "overflow-wrap" : "normal | break-word",
  3135. "overflow-x" : 1,
  3136. "overflow-y" : 1,
  3137. //P
  3138. "padding" : "<padding-width>{1,4}",
  3139. "padding-bottom" : "<padding-width>",
  3140. "padding-left" : "<padding-width>",
  3141. "padding-right" : "<padding-width>",
  3142. "padding-top" : "<padding-width>",
  3143. "page" : 1,
  3144. "page-break-after" : "auto | always | avoid | left | right",
  3145. "page-break-before" : "auto | always | avoid | left | right",
  3146. "page-break-inside" : "auto | avoid",
  3147. "page-policy" : 1,
  3148. "pause" : 1,
  3149. "pause-after" : 1,
  3150. "pause-before" : 1,
  3151. "perspective" : 1,
  3152. "perspective-origin" : 1,
  3153. "phonemes" : 1,
  3154. "pitch" : 1,
  3155. "pitch-range" : 1,
  3156. "play-during" : 1,
  3157. "pointer-events" : "auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all",
  3158. "position" : "static | relative | absolute | fixed | sticky | -webkit-sticky",
  3159. "presentation-level" : 1,
  3160. "punctuation-trim" : 1,
  3161. //Q
  3162. "quotes" : 1,
  3163. //R
  3164. "rendering-intent" : 1,
  3165. "resize" : 1,
  3166. "rest" : 1,
  3167. "rest-after" : 1,
  3168. "rest-before" : 1,
  3169. "richness" : 1,
  3170. "right" : "<margin-width>",
  3171. "rotation" : 1,
  3172. "rotation-point" : 1,
  3173. "ruby-align" : 1,
  3174. "ruby-overhang" : 1,
  3175. "ruby-position" : 1,
  3176. "ruby-span" : 1,
  3177. //S
  3178. "shape-rendering" : "auto | optimizeSpeed | crispEdges | geometricPrecision",
  3179. "size" : 1,
  3180. "speak" : "normal | none | spell-out",
  3181. "speak-header" : "once | always",
  3182. "speak-numeral" : "digits | continuous",
  3183. "speak-punctuation" : "code | none",
  3184. "speech-rate" : 1,
  3185. "src" : 1,
  3186. "stop-color" : 1,
  3187. "stop-opacity" : "<opacity-value>",
  3188. "stress" : 1,
  3189. "string-set" : 1,
  3190. "stroke" : "<paint>",
  3191. "stroke-dasharray" : "none | <dasharray>",
  3192. "stroke-dashoffset" : "<percentage> | <length>",
  3193. "stroke-linecap" : "butt | round | square",
  3194. "stroke-linejoin" : "miter | round | bevel",
  3195. "stroke-miterlimit" : "<miterlimit>",
  3196. "stroke-opacity" : "<opacity-value>",
  3197. "stroke-width" : "<percentage> | <length>",
  3198. "table-layout" : "auto | fixed",
  3199. "tab-size" : "<integer> | <length>",
  3200. "target" : 1,
  3201. "target-name" : 1,
  3202. "target-new" : 1,
  3203. "target-position" : 1,
  3204. "text-align" : "left | right | center | justify | match-parent | start | end",
  3205. "text-align-last" : 1,
  3206. "text-anchor" : "start | middle | end",
  3207. "text-decoration" : "<text-decoration-line> || <text-decoration-style> || <text-decoration-color>",
  3208. "text-decoration-color" : "<text-decoration-color>",
  3209. "text-decoration-line" : "<text-decoration-line>",
  3210. "text-decoration-style" : "<text-decoration-style>",
  3211. "text-emphasis" : 1,
  3212. "text-height" : 1,
  3213. "text-indent" : "<length> | <percentage>",
  3214. "text-justify" : "auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida",
  3215. "text-outline" : 1,
  3216. "text-overflow" : 1,
  3217. "text-rendering" : "auto | optimizeSpeed | optimizeLegibility | geometricPrecision",
  3218. "text-shadow" : 1,
  3219. "text-transform" : "capitalize | uppercase | lowercase | none",
  3220. "text-wrap" : "normal | none | avoid",
  3221. "top" : "<margin-width>",
  3222. "-ms-touch-action" : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
  3223. "touch-action" : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
  3224. "transform" : 1,
  3225. "transform-origin" : 1,
  3226. "transform-style" : 1,
  3227. "transition" : 1,
  3228. "transition-delay" : 1,
  3229. "transition-duration" : 1,
  3230. "transition-property" : 1,
  3231. "transition-timing-function" : 1,
  3232. //U
  3233. "unicode-range" : "<unicode-range>#",
  3234. "unicode-bidi" : "normal | embed | isolate | bidi-override | isolate-override | plaintext",
  3235. "user-modify" : "read-only | read-write | write-only",
  3236. "user-select" : "auto | text | none | contain | all",
  3237. //V
  3238. "vertical-align" : "auto | use-script | baseline | sub | super | top | text-top | central | middle | bottom | text-bottom | <percentage> | <length>",
  3239. "visibility" : "visible | hidden | collapse",
  3240. "voice-balance" : 1,
  3241. "voice-duration" : 1,
  3242. "voice-family" : 1,
  3243. "voice-pitch" : 1,
  3244. "voice-pitch-range" : 1,
  3245. "voice-rate" : 1,
  3246. "voice-stress" : 1,
  3247. "voice-volume" : 1,
  3248. "volume" : 1,
  3249. //W
  3250. "white-space" : "normal | pre | nowrap | pre-wrap | pre-line | -pre-wrap | -o-pre-wrap | -moz-pre-wrap | -hp-pre-wrap", // https://perishablepress.com/wrapping-content/
  3251. "white-space-collapse" : 1,
  3252. "widows" : "<integer>",
  3253. "width" : "<length> | <percentage> | <content-sizing> | auto",
  3254. "will-change" : "<will-change>",
  3255. "word-break" : "normal | keep-all | break-all",
  3256. "word-spacing" : "<length> | normal",
  3257. "word-wrap" : "normal | break-word",
  3258. "writing-mode" : "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb",
  3259. //Z
  3260. "z-index" : "<integer> | auto",
  3261. "zoom" : "<number> | <percentage> | normal"
  3262. };
  3263. },{}],8:[function(require,module,exports){
  3264. "use strict";
  3265. module.exports = PropertyName;
  3266. var SyntaxUnit = require("../util/SyntaxUnit");
  3267. var Parser = require("./Parser");
  3268. /**
  3269. * Represents a selector combinator (whitespace, +, >).
  3270. * @namespace parserlib.css
  3271. * @class PropertyName
  3272. * @extends parserlib.util.SyntaxUnit
  3273. * @constructor
  3274. * @param {String} text The text representation of the unit.
  3275. * @param {String} hack The type of IE hack applied ("*", "_", or null).
  3276. * @param {int} line The line of text on which the unit resides.
  3277. * @param {int} col The column of text on which the unit resides.
  3278. */
  3279. function PropertyName(text, hack, line, col) {
  3280. SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_NAME_TYPE);
  3281. /**
  3282. * The type of IE hack applied ("*", "_", or null).
  3283. * @type String
  3284. * @property hack
  3285. */
  3286. this.hack = hack;
  3287. }
  3288. PropertyName.prototype = new SyntaxUnit();
  3289. PropertyName.prototype.constructor = PropertyName;
  3290. PropertyName.prototype.toString = function() {
  3291. return (this.hack ? this.hack : "") + this.text;
  3292. };
  3293. },{"../util/SyntaxUnit":26,"./Parser":6}],9:[function(require,module,exports){
  3294. "use strict";
  3295. module.exports = PropertyValue;
  3296. var SyntaxUnit = require("../util/SyntaxUnit");
  3297. var Parser = require("./Parser");
  3298. /**
  3299. * Represents a single part of a CSS property value, meaning that it represents
  3300. * just everything single part between ":" and ";". If there are multiple values
  3301. * separated by commas, this type represents just one of the values.
  3302. * @param {String[]} parts An array of value parts making up this value.
  3303. * @param {int} line The line of text on which the unit resides.
  3304. * @param {int} col The column of text on which the unit resides.
  3305. * @namespace parserlib.css
  3306. * @class PropertyValue
  3307. * @extends parserlib.util.SyntaxUnit
  3308. * @constructor
  3309. */
  3310. function PropertyValue(parts, line, col) {
  3311. SyntaxUnit.call(this, parts.join(" "), line, col, Parser.PROPERTY_VALUE_TYPE);
  3312. /**
  3313. * The parts that make up the selector.
  3314. * @type Array
  3315. * @property parts
  3316. */
  3317. this.parts = parts;
  3318. }
  3319. PropertyValue.prototype = new SyntaxUnit();
  3320. PropertyValue.prototype.constructor = PropertyValue;
  3321. },{"../util/SyntaxUnit":26,"./Parser":6}],10:[function(require,module,exports){
  3322. "use strict";
  3323. module.exports = PropertyValueIterator;
  3324. /**
  3325. * A utility class that allows for easy iteration over the various parts of a
  3326. * property value.
  3327. * @param {parserlib.css.PropertyValue} value The property value to iterate over.
  3328. * @namespace parserlib.css
  3329. * @class PropertyValueIterator
  3330. * @constructor
  3331. */
  3332. function PropertyValueIterator(value) {
  3333. /**
  3334. * Iterator value
  3335. * @type int
  3336. * @property _i
  3337. * @private
  3338. */
  3339. this._i = 0;
  3340. /**
  3341. * The parts that make up the value.
  3342. * @type Array
  3343. * @property _parts
  3344. * @private
  3345. */
  3346. this._parts = value.parts;
  3347. /**
  3348. * Keeps track of bookmarks along the way.
  3349. * @type Array
  3350. * @property _marks
  3351. * @private
  3352. */
  3353. this._marks = [];
  3354. /**
  3355. * Holds the original property value.
  3356. * @type parserlib.css.PropertyValue
  3357. * @property value
  3358. */
  3359. this.value = value;
  3360. }
  3361. /**
  3362. * Returns the total number of parts in the value.
  3363. * @return {int} The total number of parts in the value.
  3364. * @method count
  3365. */
  3366. PropertyValueIterator.prototype.count = function() {
  3367. return this._parts.length;
  3368. };
  3369. /**
  3370. * Indicates if the iterator is positioned at the first item.
  3371. * @return {Boolean} True if positioned at first item, false if not.
  3372. * @method isFirst
  3373. */
  3374. PropertyValueIterator.prototype.isFirst = function() {
  3375. return this._i === 0;
  3376. };
  3377. /**
  3378. * Indicates if there are more parts of the property value.
  3379. * @return {Boolean} True if there are more parts, false if not.
  3380. * @method hasNext
  3381. */
  3382. PropertyValueIterator.prototype.hasNext = function() {
  3383. return this._i < this._parts.length;
  3384. };
  3385. /**
  3386. * Marks the current spot in the iteration so it can be restored to
  3387. * later on.
  3388. * @return {void}
  3389. * @method mark
  3390. */
  3391. PropertyValueIterator.prototype.mark = function() {
  3392. this._marks.push(this._i);
  3393. };
  3394. /**
  3395. * Returns the next part of the property value or null if there is no next
  3396. * part. Does not move the internal counter forward.
  3397. * @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
  3398. * part.
  3399. * @method peek
  3400. */
  3401. PropertyValueIterator.prototype.peek = function(count) {
  3402. return this.hasNext() ? this._parts[this._i + (count || 0)] : null;
  3403. };
  3404. /**
  3405. * Returns the next part of the property value or null if there is no next
  3406. * part.
  3407. * @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
  3408. * part.
  3409. * @method next
  3410. */
  3411. PropertyValueIterator.prototype.next = function() {
  3412. return this.hasNext() ? this._parts[this._i++] : null;
  3413. };
  3414. /**
  3415. * Returns the previous part of the property value or null if there is no
  3416. * previous part.
  3417. * @return {parserlib.css.PropertyValuePart} The previous part of the
  3418. * property value or null if there is no previous part.
  3419. * @method previous
  3420. */
  3421. PropertyValueIterator.prototype.previous = function() {
  3422. return this._i > 0 ? this._parts[--this._i] : null;
  3423. };
  3424. /**
  3425. * Restores the last saved bookmark.
  3426. * @return {void}
  3427. * @method restore
  3428. */
  3429. PropertyValueIterator.prototype.restore = function() {
  3430. if (this._marks.length) {
  3431. this._i = this._marks.pop();
  3432. }
  3433. };
  3434. /**
  3435. * Drops the last saved bookmark.
  3436. * @return {void}
  3437. * @method drop
  3438. */
  3439. PropertyValueIterator.prototype.drop = function() {
  3440. this._marks.pop();
  3441. };
  3442. },{}],11:[function(require,module,exports){
  3443. "use strict";
  3444. module.exports = PropertyValuePart;
  3445. var SyntaxUnit = require("../util/SyntaxUnit");
  3446. var Colors = require("./Colors");
  3447. var Parser = require("./Parser");
  3448. var Tokens = require("./Tokens");
  3449. /**
  3450. * Represents a single part of a CSS property value, meaning that it represents
  3451. * just one part of the data between ":" and ";".
  3452. * @param {String} text The text representation of the unit.
  3453. * @param {int} line The line of text on which the unit resides.
  3454. * @param {int} col The column of text on which the unit resides.
  3455. * @namespace parserlib.css
  3456. * @class PropertyValuePart
  3457. * @extends parserlib.util.SyntaxUnit
  3458. * @constructor
  3459. */
  3460. function PropertyValuePart(text, line, col, optionalHint) {
  3461. var hint = optionalHint || {};
  3462. SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_VALUE_PART_TYPE);
  3463. /**
  3464. * Indicates the type of value unit.
  3465. * @type String
  3466. * @property type
  3467. */
  3468. this.type = "unknown";
  3469. //figure out what type of data it is
  3470. var temp;
  3471. //it is a measurement?
  3472. if (/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)) { //dimension
  3473. this.type = "dimension";
  3474. this.value = +RegExp.$1;
  3475. this.units = RegExp.$2;
  3476. //try to narrow down
  3477. switch (this.units.toLowerCase()) {
  3478. case "em":
  3479. case "rem":
  3480. case "ex":
  3481. case "px":
  3482. case "cm":
  3483. case "mm":
  3484. case "in":
  3485. case "pt":
  3486. case "pc":
  3487. case "ch":
  3488. case "vh":
  3489. case "vw":
  3490. case "vmax":
  3491. case "vmin":
  3492. this.type = "length";
  3493. break;
  3494. case "fr":
  3495. this.type = "grid";
  3496. break;
  3497. case "deg":
  3498. case "rad":
  3499. case "grad":
  3500. case "turn":
  3501. this.type = "angle";
  3502. break;
  3503. case "ms":
  3504. case "s":
  3505. this.type = "time";
  3506. break;
  3507. case "hz":
  3508. case "khz":
  3509. this.type = "frequency";
  3510. break;
  3511. case "dpi":
  3512. case "dpcm":
  3513. this.type = "resolution";
  3514. break;
  3515. //default
  3516. }
  3517. } else if (/^([+\-]?[\d\.]+)%$/i.test(text)) { //percentage
  3518. this.type = "percentage";
  3519. this.value = +RegExp.$1;
  3520. } else if (/^([+\-]?\d+)$/i.test(text)) { //integer
  3521. this.type = "integer";
  3522. this.value = +RegExp.$1;
  3523. } else if (/^([+\-]?[\d\.]+)$/i.test(text)) { //number
  3524. this.type = "number";
  3525. this.value = +RegExp.$1;
  3526. } else if (/^#([a-f0-9]{3,6})/i.test(text)) { //hexcolor
  3527. this.type = "color";
  3528. temp = RegExp.$1;
  3529. if (temp.length === 3) {
  3530. this.red = parseInt(temp.charAt(0)+temp.charAt(0), 16);
  3531. this.green = parseInt(temp.charAt(1)+temp.charAt(1), 16);
  3532. this.blue = parseInt(temp.charAt(2)+temp.charAt(2), 16);
  3533. } else {
  3534. this.red = parseInt(temp.substring(0, 2), 16);
  3535. this.green = parseInt(temp.substring(2, 4), 16);
  3536. this.blue = parseInt(temp.substring(4, 6), 16);
  3537. }
  3538. } else if (/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)) { //rgb() color with absolute numbers
  3539. this.type = "color";
  3540. this.red = +RegExp.$1;
  3541. this.green = +RegExp.$2;
  3542. this.blue = +RegExp.$3;
  3543. } else if (/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //rgb() color with percentages
  3544. this.type = "color";
  3545. this.red = +RegExp.$1 * 255 / 100;
  3546. this.green = +RegExp.$2 * 255 / 100;
  3547. this.blue = +RegExp.$3 * 255 / 100;
  3548. } else if (/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with absolute numbers
  3549. this.type = "color";
  3550. this.red = +RegExp.$1;
  3551. this.green = +RegExp.$2;
  3552. this.blue = +RegExp.$3;
  3553. this.alpha = +RegExp.$4;
  3554. } else if (/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with percentages
  3555. this.type = "color";
  3556. this.red = +RegExp.$1 * 255 / 100;
  3557. this.green = +RegExp.$2 * 255 / 100;
  3558. this.blue = +RegExp.$3 * 255 / 100;
  3559. this.alpha = +RegExp.$4;
  3560. } else if (/hsla?\(\s*(-?\d+|-?\d*\.\d+)(%|deg|g?rad|turn|)\s*,\s*(-?\d+|-?\d*\.\d+)%\s*,\s*(-?\d+|-?\d*\.\d+)%(?:\s*,\s*(-?\d+|-?\d*\.\d+))?\s*\)/i.test(text)) { //hsla() color
  3561. this.type = "color";
  3562. this.hue = +RegExp.$1;
  3563. this.hueUnit = +RegExp.$2;
  3564. this.saturation = +RegExp.$3 / 100;
  3565. this.lightness = +RegExp.$4 / 100;
  3566. this.alpha = +RegExp.$4;
  3567. } else if (/^url\(("([^\\"]|\\.)*")\)/i.test(text)) { //URI
  3568. // generated by TokenStream.readURI, so always double-quoted.
  3569. this.type = "uri";
  3570. this.uri = PropertyValuePart.parseString(RegExp.$1);
  3571. } else if (/^([^\(]+)\(/i.test(text)) {
  3572. this.type = "function";
  3573. this.name = RegExp.$1;
  3574. this.value = text;
  3575. } else if (/^"([^\n\r\f\\"]|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*"/i.test(text)) { //double-quoted string
  3576. this.type = "string";
  3577. this.value = PropertyValuePart.parseString(text);
  3578. } else if (/^'([^\n\r\f\\']|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*'/i.test(text)) { //single-quoted string
  3579. this.type = "string";
  3580. this.value = PropertyValuePart.parseString(text);
  3581. } else if (Colors[text.toLowerCase()]) { //named color
  3582. this.type = "color";
  3583. temp = Colors[text.toLowerCase()].substring(1);
  3584. this.red = parseInt(temp.substring(0, 2), 16);
  3585. this.green = parseInt(temp.substring(2, 4), 16);
  3586. this.blue = parseInt(temp.substring(4, 6), 16);
  3587. } else if (/^[,\/]$/.test(text)) {
  3588. this.type = "operator";
  3589. this.value = text;
  3590. } else if (/^-?[a-z_\u00A0-\uFFFF][a-z0-9\-_\u00A0-\uFFFF]*$/i.test(text)) {
  3591. this.type = "identifier";
  3592. this.value = text;
  3593. }
  3594. // There can be ambiguity with escape sequences in identifiers, as
  3595. // well as with "color" parts which are also "identifiers", so record
  3596. // an explicit hint when the token generating this PropertyValuePart
  3597. // was an identifier.
  3598. this.wasIdent = Boolean(hint.ident);
  3599. }
  3600. PropertyValuePart.prototype = new SyntaxUnit();
  3601. PropertyValuePart.prototype.constructor = PropertyValuePart;
  3602. /**
  3603. * Helper method to parse a CSS string.
  3604. */
  3605. PropertyValuePart.parseString = function(str) {
  3606. str = str.slice(1, -1); // Strip surrounding single/double quotes
  3607. var replacer = function(match, esc) {
  3608. if (/^(\n|\r\n|\r|\f)$/.test(esc)) {
  3609. return "";
  3610. }
  3611. var m = /^[0-9a-f]{1,6}/i.exec(esc);
  3612. if (m) {
  3613. var codePoint = parseInt(m[0], 16);
  3614. if (String.fromCodePoint) {
  3615. return String.fromCodePoint(codePoint);
  3616. } else {
  3617. // XXX No support for surrogates on old JavaScript engines.
  3618. return String.fromCharCode(codePoint);
  3619. }
  3620. }
  3621. return esc;
  3622. };
  3623. return str.replace(/\\(\r\n|[^\r0-9a-f]|[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)/ig,
  3624. replacer);
  3625. };
  3626. /**
  3627. * Helper method to serialize a CSS string.
  3628. */
  3629. PropertyValuePart.serializeString = function(value) {
  3630. var replacer = function(c) {
  3631. if (c === "\"") {
  3632. return "\\" + c;
  3633. }
  3634. var cp = String.codePointAt ? c.codePointAt(0) :
  3635. // We only escape non-surrogate chars, so using charCodeAt
  3636. // is harmless here.
  3637. c.charCodeAt(0);
  3638. return "\\" + cp.toString(16) + " ";
  3639. };
  3640. return "\"" + value.replace(/["\r\n\f]/g, replacer) + "\"";
  3641. };
  3642. /**
  3643. * Create a new syntax unit based solely on the given token.
  3644. * Convenience method for creating a new syntax unit when
  3645. * it represents a single token instead of multiple.
  3646. * @param {Object} token The token object to represent.
  3647. * @return {parserlib.css.PropertyValuePart} The object representing the token.
  3648. * @static
  3649. * @method fromToken
  3650. */
  3651. PropertyValuePart.fromToken = function(token) {
  3652. var part = new PropertyValuePart(token.value, token.startLine, token.startCol, {
  3653. // Tokens can have escaped characters that would fool the type
  3654. // identification in the PropertyValuePart constructor, so pass
  3655. // in a hint if this was an identifier.
  3656. ident: token.type === Tokens.IDENT
  3657. });
  3658. return part;
  3659. };
  3660. },{"../util/SyntaxUnit":26,"./Colors":1,"./Parser":6,"./Tokens":18}],12:[function(require,module,exports){
  3661. "use strict";
  3662. var Pseudos = module.exports = {
  3663. __proto__: null,
  3664. ":first-letter": 1,
  3665. ":first-line": 1,
  3666. ":before": 1,
  3667. ":after": 1
  3668. };
  3669. Pseudos.ELEMENT = 1;
  3670. Pseudos.CLASS = 2;
  3671. Pseudos.isElement = function(pseudo) {
  3672. return pseudo.indexOf("::") === 0 || Pseudos[pseudo.toLowerCase()] === Pseudos.ELEMENT;
  3673. };
  3674. },{}],13:[function(require,module,exports){
  3675. "use strict";
  3676. module.exports = Selector;
  3677. var SyntaxUnit = require("../util/SyntaxUnit");
  3678. var Parser = require("./Parser");
  3679. var Specificity = require("./Specificity");
  3680. /**
  3681. * Represents an entire single selector, including all parts but not
  3682. * including multiple selectors (those separated by commas).
  3683. * @namespace parserlib.css
  3684. * @class Selector
  3685. * @extends parserlib.util.SyntaxUnit
  3686. * @constructor
  3687. * @param {Array} parts Array of selectors parts making up this selector.
  3688. * @param {int} line The line of text on which the unit resides.
  3689. * @param {int} col The column of text on which the unit resides.
  3690. */
  3691. function Selector(parts, line, col) {
  3692. SyntaxUnit.call(this, parts.join(" "), line, col, Parser.SELECTOR_TYPE);
  3693. /**
  3694. * The parts that make up the selector.
  3695. * @type Array
  3696. * @property parts
  3697. */
  3698. this.parts = parts;
  3699. /**
  3700. * The specificity of the selector.
  3701. * @type parserlib.css.Specificity
  3702. * @property specificity
  3703. */
  3704. this.specificity = Specificity.calculate(this);
  3705. }
  3706. Selector.prototype = new SyntaxUnit();
  3707. Selector.prototype.constructor = Selector;
  3708. },{"../util/SyntaxUnit":26,"./Parser":6,"./Specificity":16}],14:[function(require,module,exports){
  3709. "use strict";
  3710. module.exports = SelectorPart;
  3711. var SyntaxUnit = require("../util/SyntaxUnit");
  3712. var Parser = require("./Parser");
  3713. /**
  3714. * Represents a single part of a selector string, meaning a single set of
  3715. * element name and modifiers. This does not include combinators such as
  3716. * spaces, +, >, etc.
  3717. * @namespace parserlib.css
  3718. * @class SelectorPart
  3719. * @extends parserlib.util.SyntaxUnit
  3720. * @constructor
  3721. * @param {String} elementName The element name in the selector or null
  3722. * if there is no element name.
  3723. * @param {Array} modifiers Array of individual modifiers for the element.
  3724. * May be empty if there are none.
  3725. * @param {String} text The text representation of the unit.
  3726. * @param {int} line The line of text on which the unit resides.
  3727. * @param {int} col The column of text on which the unit resides.
  3728. */
  3729. function SelectorPart(elementName, modifiers, text, line, col) {
  3730. SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_PART_TYPE);
  3731. /**
  3732. * The tag name of the element to which this part
  3733. * of the selector affects.
  3734. * @type String
  3735. * @property elementName
  3736. */
  3737. this.elementName = elementName;
  3738. /**
  3739. * The parts that come after the element name, such as class names, IDs,
  3740. * pseudo classes/elements, etc.
  3741. * @type Array
  3742. * @property modifiers
  3743. */
  3744. this.modifiers = modifiers;
  3745. }
  3746. SelectorPart.prototype = new SyntaxUnit();
  3747. SelectorPart.prototype.constructor = SelectorPart;
  3748. },{"../util/SyntaxUnit":26,"./Parser":6}],15:[function(require,module,exports){
  3749. "use strict";
  3750. module.exports = SelectorSubPart;
  3751. var SyntaxUnit = require("../util/SyntaxUnit");
  3752. var Parser = require("./Parser");
  3753. /**
  3754. * Represents a selector modifier string, meaning a class name, element name,
  3755. * element ID, pseudo rule, etc.
  3756. * @namespace parserlib.css
  3757. * @class SelectorSubPart
  3758. * @extends parserlib.util.SyntaxUnit
  3759. * @constructor
  3760. * @param {String} text The text representation of the unit.
  3761. * @param {String} type The type of selector modifier.
  3762. * @param {int} line The line of text on which the unit resides.
  3763. * @param {int} col The column of text on which the unit resides.
  3764. */
  3765. function SelectorSubPart(text, type, line, col) {
  3766. SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_SUB_PART_TYPE);
  3767. /**
  3768. * The type of modifier.
  3769. * @type String
  3770. * @property type
  3771. */
  3772. this.type = type;
  3773. /**
  3774. * Some subparts have arguments, this represents them.
  3775. * @type Array
  3776. * @property args
  3777. */
  3778. this.args = [];
  3779. }
  3780. SelectorSubPart.prototype = new SyntaxUnit();
  3781. SelectorSubPart.prototype.constructor = SelectorSubPart;
  3782. },{"../util/SyntaxUnit":26,"./Parser":6}],16:[function(require,module,exports){
  3783. "use strict";
  3784. module.exports = Specificity;
  3785. var Pseudos = require("./Pseudos");
  3786. var SelectorPart = require("./SelectorPart");
  3787. /**
  3788. * Represents a selector's specificity.
  3789. * @namespace parserlib.css
  3790. * @class Specificity
  3791. * @constructor
  3792. * @param {int} a Should be 1 for inline styles, zero for stylesheet styles
  3793. * @param {int} b Number of ID selectors
  3794. * @param {int} c Number of classes and pseudo classes
  3795. * @param {int} d Number of element names and pseudo elements
  3796. */
  3797. function Specificity(a, b, c, d) {
  3798. this.a = a;
  3799. this.b = b;
  3800. this.c = c;
  3801. this.d = d;
  3802. }
  3803. Specificity.prototype = {
  3804. constructor: Specificity,
  3805. /**
  3806. * Compare this specificity to another.
  3807. * @param {Specificity} other The other specificity to compare to.
  3808. * @return {int} -1 if the other specificity is larger, 1 if smaller, 0 if equal.
  3809. * @method compare
  3810. */
  3811. compare: function(other) {
  3812. var comps = ["a", "b", "c", "d"],
  3813. i, len;
  3814. for (i=0, len=comps.length; i < len; i++) {
  3815. if (this[comps[i]] < other[comps[i]]) {
  3816. return -1;
  3817. } else if (this[comps[i]] > other[comps[i]]) {
  3818. return 1;
  3819. }
  3820. }
  3821. return 0;
  3822. },
  3823. /**
  3824. * Creates a numeric value for the specificity.
  3825. * @return {int} The numeric value for the specificity.
  3826. * @method valueOf
  3827. */
  3828. valueOf: function() {
  3829. return (this.a * 1000) + (this.b * 100) + (this.c * 10) + this.d;
  3830. },
  3831. /**
  3832. * Returns a string representation for specificity.
  3833. * @return {String} The string representation of specificity.
  3834. * @method toString
  3835. */
  3836. toString: function() {
  3837. return this.a + "," + this.b + "," + this.c + "," + this.d;
  3838. }
  3839. };
  3840. /**
  3841. * Calculates the specificity of the given selector.
  3842. * @param {parserlib.css.Selector} The selector to calculate specificity for.
  3843. * @return {parserlib.css.Specificity} The specificity of the selector.
  3844. * @static
  3845. * @method calculate
  3846. */
  3847. Specificity.calculate = function(selector) {
  3848. var i, len,
  3849. part,
  3850. b=0, c=0, d=0;
  3851. function updateValues(part) {
  3852. var i, j, len, num,
  3853. elementName = part.elementName ? part.elementName.text : "",
  3854. modifier;
  3855. if (elementName && elementName.charAt(elementName.length-1) !== "*") {
  3856. d++;
  3857. }
  3858. for (i=0, len=part.modifiers.length; i < len; i++) {
  3859. modifier = part.modifiers[i];
  3860. switch (modifier.type) {
  3861. case "class":
  3862. case "attribute":
  3863. c++;
  3864. break;
  3865. case "id":
  3866. b++;
  3867. break;
  3868. case "pseudo":
  3869. if (Pseudos.isElement(modifier.text)) {
  3870. d++;
  3871. } else {
  3872. c++;
  3873. }
  3874. break;
  3875. case "not":
  3876. for (j=0, num=modifier.args.length; j < num; j++) {
  3877. updateValues(modifier.args[j]);
  3878. }
  3879. }
  3880. }
  3881. }
  3882. for (i=0, len=selector.parts.length; i < len; i++) {
  3883. part = selector.parts[i];
  3884. if (part instanceof SelectorPart) {
  3885. updateValues(part);
  3886. }
  3887. }
  3888. return new Specificity(0, b, c, d);
  3889. };
  3890. },{"./Pseudos":12,"./SelectorPart":14}],17:[function(require,module,exports){
  3891. "use strict";
  3892. module.exports = TokenStream;
  3893. var TokenStreamBase = require("../util/TokenStreamBase");
  3894. var PropertyValuePart = require("./PropertyValuePart");
  3895. var Tokens = require("./Tokens");
  3896. var h = /^[0-9a-fA-F]$/,
  3897. nonascii = /^[\u00A0-\uFFFF]$/,
  3898. nl = /\n|\r\n|\r|\f/,
  3899. whitespace = /\u0009|\u000a|\u000c|\u000d|\u0020/;
  3900. //-----------------------------------------------------------------------------
  3901. // Helper functions
  3902. //-----------------------------------------------------------------------------
  3903. function isHexDigit(c) {
  3904. return c !== null && h.test(c);
  3905. }
  3906. function isDigit(c) {
  3907. return c !== null && /\d/.test(c);
  3908. }
  3909. function isWhitespace(c) {
  3910. return c !== null && whitespace.test(c);
  3911. }
  3912. function isNewLine(c) {
  3913. return c !== null && nl.test(c);
  3914. }
  3915. function isNameStart(c) {
  3916. return c !== null && /[a-z_\u00A0-\uFFFF\\]/i.test(c);
  3917. }
  3918. function isNameChar(c) {
  3919. return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
  3920. }
  3921. function isIdentStart(c) {
  3922. return c !== null && (isNameStart(c) || /\-\\/.test(c));
  3923. }
  3924. function mix(receiver, supplier) {
  3925. for (var prop in supplier) {
  3926. if (Object.prototype.hasOwnProperty.call(supplier, prop)) {
  3927. receiver[prop] = supplier[prop];
  3928. }
  3929. }
  3930. return receiver;
  3931. }
  3932. //-----------------------------------------------------------------------------
  3933. // CSS Token Stream
  3934. //-----------------------------------------------------------------------------
  3935. /**
  3936. * A token stream that produces CSS tokens.
  3937. * @param {String|Reader} input The source of text to tokenize.
  3938. * @constructor
  3939. * @class TokenStream
  3940. * @namespace parserlib.css
  3941. */
  3942. function TokenStream(input) {
  3943. TokenStreamBase.call(this, input, Tokens);
  3944. }
  3945. TokenStream.prototype = mix(new TokenStreamBase(), {
  3946. /**
  3947. * Overrides the TokenStreamBase method of the same name
  3948. * to produce CSS tokens.
  3949. * @return {Object} A token object representing the next token.
  3950. * @method _getToken
  3951. * @private
  3952. */
  3953. _getToken: function() {
  3954. var c,
  3955. reader = this._reader,
  3956. token = null,
  3957. startLine = reader.getLine(),
  3958. startCol = reader.getCol();
  3959. c = reader.read();
  3960. while (c) {
  3961. switch (c) {
  3962. /*
  3963. * Potential tokens:
  3964. * - COMMENT
  3965. * - SLASH
  3966. * - CHAR
  3967. */
  3968. case "/":
  3969. if (reader.peek() === "*") {
  3970. token = this.commentToken(c, startLine, startCol);
  3971. } else {
  3972. token = this.charToken(c, startLine, startCol);
  3973. }
  3974. break;
  3975. /*
  3976. * Potential tokens:
  3977. * - DASHMATCH
  3978. * - INCLUDES
  3979. * - PREFIXMATCH
  3980. * - SUFFIXMATCH
  3981. * - SUBSTRINGMATCH
  3982. * - CHAR
  3983. */
  3984. case "|":
  3985. case "~":
  3986. case "^":
  3987. case "$":
  3988. case "*":
  3989. if (reader.peek() === "=") {
  3990. token = this.comparisonToken(c, startLine, startCol);
  3991. } else {
  3992. token = this.charToken(c, startLine, startCol);
  3993. }
  3994. break;
  3995. /*
  3996. * Potential tokens:
  3997. * - STRING
  3998. * - INVALID
  3999. */
  4000. case "\"":
  4001. case "'":
  4002. token = this.stringToken(c, startLine, startCol);
  4003. break;
  4004. /*
  4005. * Potential tokens:
  4006. * - HASH
  4007. * - CHAR
  4008. */
  4009. case "#":
  4010. if (isNameChar(reader.peek())) {
  4011. token = this.hashToken(c, startLine, startCol);
  4012. } else {
  4013. token = this.charToken(c, startLine, startCol);
  4014. }
  4015. break;
  4016. /*
  4017. * Potential tokens:
  4018. * - DOT
  4019. * - NUMBER
  4020. * - DIMENSION
  4021. * - PERCENTAGE
  4022. */
  4023. case ".":
  4024. if (isDigit(reader.peek())) {
  4025. token = this.numberToken(c, startLine, startCol);
  4026. } else {
  4027. token = this.charToken(c, startLine, startCol);
  4028. }
  4029. break;
  4030. /*
  4031. * Potential tokens:
  4032. * - CDC
  4033. * - MINUS
  4034. * - NUMBER
  4035. * - DIMENSION
  4036. * - PERCENTAGE
  4037. */
  4038. case "-":
  4039. if (reader.peek() === "-") { //could be closing HTML-style comment
  4040. //or CSS variable
  4041. if (/\w/.test(reader.peek(2))) {
  4042. token = this.identOrFunctionToken(c, startLine, startCol);
  4043. } else {
  4044. token = this.htmlCommentEndToken(c, startLine, startCol);
  4045. }
  4046. } else if (isNameStart(reader.peek())) {
  4047. token = this.identOrFunctionToken(c, startLine, startCol);
  4048. } else {
  4049. token = this.charToken(c, startLine, startCol);
  4050. }
  4051. break;
  4052. /*
  4053. * Potential tokens:
  4054. * - IMPORTANT_SYM
  4055. * - CHAR
  4056. */
  4057. case "!":
  4058. token = this.importantToken(c, startLine, startCol);
  4059. break;
  4060. /*
  4061. * Any at-keyword or CHAR
  4062. */
  4063. case "@":
  4064. token = this.atRuleToken(c, startLine, startCol);
  4065. break;
  4066. /*
  4067. * Potential tokens:
  4068. * - ANY
  4069. * - NOT
  4070. * - CHAR
  4071. */
  4072. case ":":
  4073. token = this.notOrAnyToken(c, startLine, startCol);
  4074. break;
  4075. /*
  4076. * Potential tokens:
  4077. * - CDO
  4078. * - CHAR
  4079. */
  4080. case "<":
  4081. token = this.htmlCommentStartToken(c, startLine, startCol);
  4082. break;
  4083. /*
  4084. * Potential tokens:
  4085. * - IDENT
  4086. * - CHAR
  4087. */
  4088. case "\\":
  4089. if (/[^\r\n\f]/.test(reader.peek())) {
  4090. token = this.identOrFunctionToken(this.readEscape(c, true), startLine, startCol);
  4091. } else {
  4092. token = this.charToken(c, startLine, startCol);
  4093. }
  4094. break;
  4095. /*
  4096. * Potential tokens:
  4097. * - UNICODE_RANGE
  4098. * - URL
  4099. * - CHAR
  4100. */
  4101. case "U":
  4102. case "u":
  4103. if (reader.peek() === "+") {
  4104. token = this.unicodeRangeToken(c, startLine, startCol);
  4105. break;
  4106. }
  4107. /* falls through */
  4108. default:
  4109. /*
  4110. * Potential tokens:
  4111. * - NUMBER
  4112. * - DIMENSION
  4113. * - LENGTH
  4114. * - FREQ
  4115. * - TIME
  4116. * - EMS
  4117. * - EXS
  4118. * - ANGLE
  4119. */
  4120. if (isDigit(c)) {
  4121. token = this.numberToken(c, startLine, startCol);
  4122. } else
  4123. /*
  4124. * Potential tokens:
  4125. * - S
  4126. */
  4127. if (isWhitespace(c)) {
  4128. token = this.whitespaceToken(c, startLine, startCol);
  4129. } else
  4130. /*
  4131. * Potential tokens:
  4132. * - IDENT
  4133. */
  4134. if (isIdentStart(c)) {
  4135. token = this.identOrFunctionToken(c, startLine, startCol);
  4136. } else {
  4137. /*
  4138. * Potential tokens:
  4139. * - CHAR
  4140. * - PLUS
  4141. */
  4142. token = this.charToken(c, startLine, startCol);
  4143. }
  4144. }
  4145. //make sure this token is wanted
  4146. //TODO: check channel
  4147. break;
  4148. }
  4149. if (!token && c === null) {
  4150. token = this.createToken(Tokens.EOF, null, startLine, startCol);
  4151. }
  4152. return token;
  4153. },
  4154. //-------------------------------------------------------------------------
  4155. // Methods to create tokens
  4156. //-------------------------------------------------------------------------
  4157. /**
  4158. * Produces a token based on available data and the current
  4159. * reader position information. This method is called by other
  4160. * private methods to create tokens and is never called directly.
  4161. * @param {int} tt The token type.
  4162. * @param {String} value The text value of the token.
  4163. * @param {int} startLine The beginning line for the character.
  4164. * @param {int} startCol The beginning column for the character.
  4165. * @param {Object} options (Optional) Specifies a channel property
  4166. * to indicate that a different channel should be scanned
  4167. * and/or a hide property indicating that the token should
  4168. * be hidden.
  4169. * @return {Object} A token object.
  4170. * @method createToken
  4171. */
  4172. createToken: function(tt, value, startLine, startCol, options) {
  4173. var reader = this._reader;
  4174. options = options || {};
  4175. return {
  4176. value: value,
  4177. type: tt,
  4178. channel: options.channel,
  4179. endChar: options.endChar,
  4180. hide: options.hide || false,
  4181. startLine: startLine,
  4182. startCol: startCol,
  4183. endLine: reader.getLine(),
  4184. endCol: reader.getCol()
  4185. };
  4186. },
  4187. //-------------------------------------------------------------------------
  4188. // Methods to create specific tokens
  4189. //-------------------------------------------------------------------------
  4190. /**
  4191. * Produces a token for any at-rule. If the at-rule is unknown, then
  4192. * the token is for a single "@" character.
  4193. * @param {String} first The first character for the token.
  4194. * @param {int} startLine The beginning line for the character.
  4195. * @param {int} startCol The beginning column for the character.
  4196. * @return {Object} A token object.
  4197. * @method atRuleToken
  4198. */
  4199. atRuleToken: function(first, startLine, startCol) {
  4200. var rule = first,
  4201. reader = this._reader,
  4202. tt = Tokens.CHAR,
  4203. ident;
  4204. /*
  4205. * First, mark where we are. There are only four @ rules,
  4206. * so anything else is really just an invalid token.
  4207. * Basically, if this doesn't match one of the known @
  4208. * rules, just return '@' as an unknown token and allow
  4209. * parsing to continue after that point.
  4210. */
  4211. reader.mark();
  4212. //try to find the at-keyword
  4213. ident = this.readName();
  4214. rule = first + ident;
  4215. tt = Tokens.type(rule.toLowerCase());
  4216. //if it's not valid, use the first character only and reset the reader
  4217. if (tt === Tokens.CHAR || tt === Tokens.UNKNOWN) {
  4218. if (rule.length > 1) {
  4219. tt = Tokens.UNKNOWN_SYM;
  4220. } else {
  4221. tt = Tokens.CHAR;
  4222. rule = first;
  4223. reader.reset();
  4224. }
  4225. }
  4226. return this.createToken(tt, rule, startLine, startCol);
  4227. },
  4228. /**
  4229. * Produces a character token based on the given character
  4230. * and location in the stream. If there's a special (non-standard)
  4231. * token name, this is used; otherwise CHAR is used.
  4232. * @param {String} c The character for the token.
  4233. * @param {int} startLine The beginning line for the character.
  4234. * @param {int} startCol The beginning column for the character.
  4235. * @return {Object} A token object.
  4236. * @method charToken
  4237. */
  4238. charToken: function(c, startLine, startCol) {
  4239. var tt = Tokens.type(c);
  4240. var opts = {};
  4241. if (tt === -1) {
  4242. tt = Tokens.CHAR;
  4243. } else {
  4244. opts.endChar = Tokens[tt].endChar;
  4245. }
  4246. return this.createToken(tt, c, startLine, startCol, opts);
  4247. },
  4248. /**
  4249. * Produces a character token based on the given character
  4250. * and location in the stream. If there's a special (non-standard)
  4251. * token name, this is used; otherwise CHAR is used.
  4252. * @param {String} first The first character for the token.
  4253. * @param {int} startLine The beginning line for the character.
  4254. * @param {int} startCol The beginning column for the character.
  4255. * @return {Object} A token object.
  4256. * @method commentToken
  4257. */
  4258. commentToken: function(first, startLine, startCol) {
  4259. var comment = this.readComment(first);
  4260. return this.createToken(Tokens.COMMENT, comment, startLine, startCol);
  4261. },
  4262. /**
  4263. * Produces a comparison token based on the given character
  4264. * and location in the stream. The next character must be
  4265. * read and is already known to be an equals sign.
  4266. * @param {String} c The character for the token.
  4267. * @param {int} startLine The beginning line for the character.
  4268. * @param {int} startCol The beginning column for the character.
  4269. * @return {Object} A token object.
  4270. * @method comparisonToken
  4271. */
  4272. comparisonToken: function(c, startLine, startCol) {
  4273. var reader = this._reader,
  4274. comparison = c + reader.read(),
  4275. tt = Tokens.type(comparison) || Tokens.CHAR;
  4276. return this.createToken(tt, comparison, startLine, startCol);
  4277. },
  4278. /**
  4279. * Produces a hash token based on the specified information. The
  4280. * first character provided is the pound sign (#) and then this
  4281. * method reads a name afterward.
  4282. * @param {String} first The first character (#) in the hash name.
  4283. * @param {int} startLine The beginning line for the character.
  4284. * @param {int} startCol The beginning column for the character.
  4285. * @return {Object} A token object.
  4286. * @method hashToken
  4287. */
  4288. hashToken: function(first, startLine, startCol) {
  4289. var name = this.readName(first);
  4290. return this.createToken(Tokens.HASH, name, startLine, startCol);
  4291. },
  4292. /**
  4293. * Produces a CDO or CHAR token based on the specified information. The
  4294. * first character is provided and the rest is read by the function to determine
  4295. * the correct token to create.
  4296. * @param {String} first The first character in the token.
  4297. * @param {int} startLine The beginning line for the character.
  4298. * @param {int} startCol The beginning column for the character.
  4299. * @return {Object} A token object.
  4300. * @method htmlCommentStartToken
  4301. */
  4302. htmlCommentStartToken: function(first, startLine, startCol) {
  4303. var reader = this._reader,
  4304. text = first;
  4305. reader.mark();
  4306. text += reader.readCount(3);
  4307. if (text === "<!--") {
  4308. return this.createToken(Tokens.CDO, text, startLine, startCol);
  4309. } else {
  4310. reader.reset();
  4311. return this.charToken(first, startLine, startCol);
  4312. }
  4313. },
  4314. /**
  4315. * Produces a CDC or CHAR token based on the specified information. The
  4316. * first character is provided and the rest is read by the function to determine
  4317. * the correct token to create.
  4318. * @param {String} first The first character in the token.
  4319. * @param {int} startLine The beginning line for the character.
  4320. * @param {int} startCol The beginning column for the character.
  4321. * @return {Object} A token object.
  4322. * @method htmlCommentEndToken
  4323. */
  4324. htmlCommentEndToken: function(first, startLine, startCol) {
  4325. var reader = this._reader,
  4326. text = first;
  4327. reader.mark();
  4328. text += reader.readCount(2);
  4329. if (text === "-->") {
  4330. return this.createToken(Tokens.CDC, text, startLine, startCol);
  4331. } else {
  4332. reader.reset();
  4333. return this.charToken(first, startLine, startCol);
  4334. }
  4335. },
  4336. /**
  4337. * Produces an IDENT or FUNCTION token based on the specified information. The
  4338. * first character is provided and the rest is read by the function to determine
  4339. * the correct token to create.
  4340. * @param {String} first The first character in the identifier.
  4341. * @param {int} startLine The beginning line for the character.
  4342. * @param {int} startCol The beginning column for the character.
  4343. * @return {Object} A token object.
  4344. * @method identOrFunctionToken
  4345. */
  4346. identOrFunctionToken: function(first, startLine, startCol) {
  4347. var reader = this._reader,
  4348. ident = this.readName(first),
  4349. tt = Tokens.IDENT,
  4350. uriFns = ["url(", "url-prefix(", "domain("],
  4351. uri;
  4352. //if there's a left paren immediately after, it's a URI or function
  4353. if (reader.peek() === "(") {
  4354. ident += reader.read();
  4355. if (uriFns.indexOf(ident.toLowerCase()) > -1) {
  4356. reader.mark();
  4357. uri = this.readURI(ident);
  4358. if (uri === null) {
  4359. //didn't find a valid URL or there's no closing paren
  4360. reader.reset();
  4361. tt = Tokens.FUNCTION;
  4362. } else {
  4363. tt = Tokens.URI;
  4364. ident = uri;
  4365. }
  4366. } else {
  4367. tt = Tokens.FUNCTION;
  4368. }
  4369. } else if (reader.peek() === ":") { //might be an IE function
  4370. //IE-specific functions always being with progid:
  4371. if (ident.toLowerCase() === "progid") {
  4372. ident += reader.readTo("(");
  4373. tt = Tokens.IE_FUNCTION;
  4374. }
  4375. }
  4376. return this.createToken(tt, ident, startLine, startCol);
  4377. },
  4378. /**
  4379. * Produces an IMPORTANT_SYM or CHAR token based on the specified information. The
  4380. * first character is provided and the rest is read by the function to determine
  4381. * the correct token to create.
  4382. * @param {String} first The first character in the token.
  4383. * @param {int} startLine The beginning line for the character.
  4384. * @param {int} startCol The beginning column for the character.
  4385. * @return {Object} A token object.
  4386. * @method importantToken
  4387. */
  4388. importantToken: function(first, startLine, startCol) {
  4389. var reader = this._reader,
  4390. important = first,
  4391. tt = Tokens.CHAR,
  4392. temp,
  4393. c;
  4394. reader.mark();
  4395. c = reader.read();
  4396. while (c) {
  4397. //there can be a comment in here
  4398. if (c === "/") {
  4399. //if the next character isn't a star, then this isn't a valid !important token
  4400. if (reader.peek() !== "*") {
  4401. break;
  4402. } else {
  4403. temp = this.readComment(c);
  4404. if (temp === "") { //broken!
  4405. break;
  4406. }
  4407. }
  4408. } else if (isWhitespace(c)) {
  4409. important += c + this.readWhitespace();
  4410. } else if (/i/i.test(c)) {
  4411. temp = reader.readCount(8);
  4412. if (/mportant/i.test(temp)) {
  4413. important += c + temp;
  4414. tt = Tokens.IMPORTANT_SYM;
  4415. }
  4416. break; //we're done
  4417. } else {
  4418. break;
  4419. }
  4420. c = reader.read();
  4421. }
  4422. if (tt === Tokens.CHAR) {
  4423. reader.reset();
  4424. return this.charToken(first, startLine, startCol);
  4425. } else {
  4426. return this.createToken(tt, important, startLine, startCol);
  4427. }
  4428. },
  4429. /**
  4430. * Produces a NOT or ANY or CHAR token based on the specified information. The
  4431. * first character is provided and the rest is read by the function to determine
  4432. * the correct token to create.
  4433. * @param {String} first The first character in the token.
  4434. * @param {int} startLine The beginning line for the character.
  4435. * @param {int} startCol The beginning column for the character.
  4436. * @return {Object} A token object.
  4437. * @method notToken
  4438. */
  4439. notOrAnyToken: function(first, startLine, startCol) {
  4440. var reader = this._reader,
  4441. text = first;
  4442. reader.mark();
  4443. text += reader.readCount(4);
  4444. if (text.toLowerCase() === ":not(") {
  4445. return this.createToken(Tokens.NOT, text, startLine, startCol);
  4446. }
  4447. if (text.toLowerCase() === ":any(") {
  4448. return this.createToken(Tokens.ANY, text, startLine, startCol);
  4449. }
  4450. text += reader.readCount(5);
  4451. if (text.toLowerCase() === ":-moz-any(") {
  4452. return this.createToken(Tokens.ANY, text, startLine, startCol);
  4453. }
  4454. text += reader.readCount(3);
  4455. if (text.toLowerCase() === ":-webkit-any(") {
  4456. return this.createToken(Tokens.ANY, text, startLine, startCol);
  4457. }
  4458. reader.reset();
  4459. return this.charToken(first, startLine, startCol);
  4460. },
  4461. /**
  4462. * Produces a number token based on the given character
  4463. * and location in the stream. This may return a token of
  4464. * NUMBER, EMS, EXS, LENGTH, ANGLE, TIME, FREQ, DIMENSION,
  4465. * or PERCENTAGE.
  4466. * @param {String} first The first character for the token.
  4467. * @param {int} startLine The beginning line for the character.
  4468. * @param {int} startCol The beginning column for the character.
  4469. * @return {Object} A token object.
  4470. * @method numberToken
  4471. */
  4472. numberToken: function(first, startLine, startCol) {
  4473. var reader = this._reader,
  4474. value = this.readNumber(first),
  4475. ident,
  4476. tt = Tokens.NUMBER,
  4477. c = reader.peek();
  4478. if (isIdentStart(c)) {
  4479. ident = this.readName(reader.read());
  4480. value += ident;
  4481. if (/^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^vmax$|^vmin$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i.test(ident)) {
  4482. tt = Tokens.LENGTH;
  4483. } else if (/^deg|^rad$|^grad$|^turn$/i.test(ident)) {
  4484. tt = Tokens.ANGLE;
  4485. } else if (/^ms$|^s$/i.test(ident)) {
  4486. tt = Tokens.TIME;
  4487. } else if (/^hz$|^khz$/i.test(ident)) {
  4488. tt = Tokens.FREQ;
  4489. } else if (/^dpi$|^dpcm$/i.test(ident)) {
  4490. tt = Tokens.RESOLUTION;
  4491. } else {
  4492. tt = Tokens.DIMENSION;
  4493. }
  4494. } else if (c === "%") {
  4495. value += reader.read();
  4496. tt = Tokens.PERCENTAGE;
  4497. }
  4498. return this.createToken(tt, value, startLine, startCol);
  4499. },
  4500. /**
  4501. * Produces a string token based on the given character
  4502. * and location in the stream. Since strings may be indicated
  4503. * by single or double quotes, a failure to match starting
  4504. * and ending quotes results in an INVALID token being generated.
  4505. * The first character in the string is passed in and then
  4506. * the rest are read up to and including the final quotation mark.
  4507. * @param {String} first The first character in the string.
  4508. * @param {int} startLine The beginning line for the character.
  4509. * @param {int} startCol The beginning column for the character.
  4510. * @return {Object} A token object.
  4511. * @method stringToken
  4512. */
  4513. stringToken: function(first, startLine, startCol) {
  4514. var delim = first,
  4515. string = first,
  4516. reader = this._reader,
  4517. tt = Tokens.STRING,
  4518. c = reader.read(),
  4519. i;
  4520. while (c) {
  4521. string += c;
  4522. if (c === "\\") {
  4523. c = reader.read();
  4524. if (c === null) {
  4525. break; // premature EOF after backslash
  4526. } else if (/[^\r\n\f0-9a-f]/i.test(c)) {
  4527. // single-character escape
  4528. string += c;
  4529. } else {
  4530. // read up to six hex digits
  4531. for (i=0; isHexDigit(c) && i<6; i++) {
  4532. string += c;
  4533. c = reader.read();
  4534. }
  4535. // swallow trailing newline or space
  4536. if (c === "\r" && reader.peek() === "\n") {
  4537. string += c;
  4538. c = reader.read();
  4539. }
  4540. if (isWhitespace(c)) {
  4541. string += c;
  4542. } else {
  4543. // This character is null or not part of the escape;
  4544. // jump back to the top to process it.
  4545. continue;
  4546. }
  4547. }
  4548. } else if (c === delim) {
  4549. break; // delimiter found.
  4550. } else if (isNewLine(reader.peek())) {
  4551. // newline without an escapement: it's an invalid string
  4552. tt = Tokens.INVALID;
  4553. break;
  4554. }
  4555. c = reader.read();
  4556. }
  4557. //if c is null, that means we're out of input and the string was never closed
  4558. if (c === null) {
  4559. tt = Tokens.INVALID;
  4560. }
  4561. return this.createToken(tt, string, startLine, startCol);
  4562. },
  4563. unicodeRangeToken: function(first, startLine, startCol) {
  4564. var reader = this._reader,
  4565. value = first,
  4566. temp,
  4567. tt = Tokens.CHAR;
  4568. //then it should be a unicode range
  4569. if (reader.peek() === "+") {
  4570. reader.mark();
  4571. value += reader.read();
  4572. value += this.readUnicodeRangePart(true);
  4573. //ensure there's an actual unicode range here
  4574. if (value.length === 2) {
  4575. reader.reset();
  4576. } else {
  4577. tt = Tokens.UNICODE_RANGE;
  4578. //if there's a ? in the first part, there can't be a second part
  4579. if (value.indexOf("?") === -1) {
  4580. if (reader.peek() === "-") {
  4581. reader.mark();
  4582. temp = reader.read();
  4583. temp += this.readUnicodeRangePart(false);
  4584. //if there's not another value, back up and just take the first
  4585. if (temp.length === 1) {
  4586. reader.reset();
  4587. } else {
  4588. value += temp;
  4589. }
  4590. }
  4591. }
  4592. }
  4593. }
  4594. return this.createToken(tt, value, startLine, startCol);
  4595. },
  4596. /**
  4597. * Produces a S token based on the specified information. Since whitespace
  4598. * may have multiple characters, this consumes all whitespace characters
  4599. * into a single token.
  4600. * @param {String} first The first character in the token.
  4601. * @param {int} startLine The beginning line for the character.
  4602. * @param {int} startCol The beginning column for the character.
  4603. * @return {Object} A token object.
  4604. * @method whitespaceToken
  4605. */
  4606. whitespaceToken: function(first, startLine, startCol) {
  4607. var value = first + this.readWhitespace();
  4608. return this.createToken(Tokens.S, value, startLine, startCol);
  4609. },
  4610. //-------------------------------------------------------------------------
  4611. // Methods to read values from the string stream
  4612. //-------------------------------------------------------------------------
  4613. readUnicodeRangePart: function(allowQuestionMark) {
  4614. var reader = this._reader,
  4615. part = "",
  4616. c = reader.peek();
  4617. //first read hex digits
  4618. while (isHexDigit(c) && part.length < 6) {
  4619. reader.read();
  4620. part += c;
  4621. c = reader.peek();
  4622. }
  4623. //then read question marks if allowed
  4624. if (allowQuestionMark) {
  4625. while (c === "?" && part.length < 6) {
  4626. reader.read();
  4627. part += c;
  4628. c = reader.peek();
  4629. }
  4630. }
  4631. //there can't be any other characters after this point
  4632. return part;
  4633. },
  4634. readWhitespace: function() {
  4635. var reader = this._reader,
  4636. whitespace = "",
  4637. c = reader.peek();
  4638. while (isWhitespace(c)) {
  4639. reader.read();
  4640. whitespace += c;
  4641. c = reader.peek();
  4642. }
  4643. return whitespace;
  4644. },
  4645. readNumber: function(first) {
  4646. var reader = this._reader,
  4647. number = first,
  4648. hasDot = (first === "."),
  4649. c = reader.peek();
  4650. while (c) {
  4651. if (isDigit(c)) {
  4652. number += reader.read();
  4653. } else if (c === ".") {
  4654. if (hasDot) {
  4655. break;
  4656. } else {
  4657. hasDot = true;
  4658. number += reader.read();
  4659. }
  4660. } else {
  4661. break;
  4662. }
  4663. c = reader.peek();
  4664. }
  4665. return number;
  4666. },
  4667. // returns null w/o resetting reader if string is invalid.
  4668. readString: function() {
  4669. var token = this.stringToken(this._reader.read(), 0, 0);
  4670. return token.type === Tokens.INVALID ? null : token.value;
  4671. },
  4672. // returns null w/o resetting reader if URI is invalid.
  4673. readURI: function(first) {
  4674. var reader = this._reader,
  4675. uri = first,
  4676. inner = "",
  4677. c = reader.peek();
  4678. //skip whitespace before
  4679. while (c && isWhitespace(c)) {
  4680. reader.read();
  4681. c = reader.peek();
  4682. }
  4683. //it's a string
  4684. if (c === "'" || c === "\"") {
  4685. inner = this.readString();
  4686. if (inner !== null) {
  4687. inner = PropertyValuePart.parseString(inner);
  4688. }
  4689. } else {
  4690. inner = this.readUnquotedURL();
  4691. }
  4692. c = reader.peek();
  4693. //skip whitespace after
  4694. while (c && isWhitespace(c)) {
  4695. reader.read();
  4696. c = reader.peek();
  4697. }
  4698. //if there was no inner value or the next character isn't closing paren, it's not a URI
  4699. if (inner === null || c !== ")") {
  4700. uri = null;
  4701. } else {
  4702. // Ensure argument to URL is always double-quoted
  4703. // (This simplifies later processing in PropertyValuePart.)
  4704. uri += PropertyValuePart.serializeString(inner) + reader.read();
  4705. }
  4706. return uri;
  4707. },
  4708. // This method never fails, although it may return an empty string.
  4709. readUnquotedURL: function(first) {
  4710. var reader = this._reader,
  4711. url = first || "",
  4712. c;
  4713. for (c = reader.peek(); c; c = reader.peek()) {
  4714. // Note that the grammar at
  4715. // https://www.w3.org/TR/CSS2/grammar.html#scanner
  4716. // incorrectly includes the backslash character in the
  4717. // `url` production, although it is correctly omitted in
  4718. // the `baduri1` production.
  4719. if (nonascii.test(c) || /^[\-!#$%&*-\[\]-~]$/.test(c)) {
  4720. url += c;
  4721. reader.read();
  4722. } else if (c === "\\") {
  4723. if (/^[^\r\n\f]$/.test(reader.peek(2))) {
  4724. url += this.readEscape(reader.read(), true);
  4725. } else {
  4726. break; // bad escape sequence.
  4727. }
  4728. } else {
  4729. break; // bad character
  4730. }
  4731. }
  4732. return url;
  4733. },
  4734. readName: function(first) {
  4735. var reader = this._reader,
  4736. ident = first || "",
  4737. c;
  4738. for (c = reader.peek(); c; c = reader.peek()) {
  4739. if (c === "\\") {
  4740. if (/^[^\r\n\f]$/.test(reader.peek(2))) {
  4741. ident += this.readEscape(reader.read(), true);
  4742. } else {
  4743. // Bad escape sequence.
  4744. break;
  4745. }
  4746. } else if (isNameChar(c)) {
  4747. ident += reader.read();
  4748. } else {
  4749. break;
  4750. }
  4751. }
  4752. return ident;
  4753. },
  4754. readEscape: function(first, unescape) {
  4755. var reader = this._reader,
  4756. cssEscape = first || "",
  4757. i = 0,
  4758. c = reader.peek();
  4759. if (isHexDigit(c)) {
  4760. do {
  4761. cssEscape += reader.read();
  4762. c = reader.peek();
  4763. } while (c && isHexDigit(c) && ++i < 6);
  4764. }
  4765. if (cssEscape.length === 1) {
  4766. if (/^[^\r\n\f0-9a-f]$/.test(c)) {
  4767. reader.read();
  4768. if (unescape) {
  4769. return c;
  4770. }
  4771. } else {
  4772. // We should never get here (readName won't call readEscape
  4773. // if the escape sequence is bad).
  4774. throw new Error("Bad escape sequence.");
  4775. }
  4776. } else if (c === "\r") {
  4777. reader.read();
  4778. if (reader.peek() === "\n") {
  4779. c += reader.read();
  4780. }
  4781. } else if (/^[ \t\n\f]$/.test(c)) {
  4782. reader.read();
  4783. } else {
  4784. c = "";
  4785. }
  4786. if (unescape) {
  4787. var cp = parseInt(cssEscape.slice(first.length), 16);
  4788. return String.fromCodePoint ? String.fromCodePoint(cp) :
  4789. String.fromCharCode(cp);
  4790. }
  4791. return cssEscape + c;
  4792. },
  4793. readComment: function(first) {
  4794. var reader = this._reader,
  4795. comment = first || "",
  4796. c = reader.read();
  4797. if (c === "*") {
  4798. while (c) {
  4799. comment += c;
  4800. //look for end of comment
  4801. if (comment.length > 2 && c === "*" && reader.peek() === "/") {
  4802. comment += reader.read();
  4803. break;
  4804. }
  4805. c = reader.read();
  4806. }
  4807. return comment;
  4808. } else {
  4809. return "";
  4810. }
  4811. }
  4812. });
  4813. },{"../util/TokenStreamBase":27,"./PropertyValuePart":11,"./Tokens":18}],18:[function(require,module,exports){
  4814. "use strict";
  4815. var Tokens = module.exports = [
  4816. /*
  4817. * The following token names are defined in CSS3 Grammar: https://www.w3.org/TR/css3-syntax/#lexical
  4818. */
  4819. // HTML-style comments
  4820. { name: "CDO" },
  4821. { name: "CDC" },
  4822. // ignorables
  4823. { name: "S", whitespace: true/*, channel: "ws"*/ },
  4824. { name: "COMMENT", whitespace: true, comment: true, hide: true/*, channel: "comment"*/ },
  4825. // attribute equality
  4826. { name: "INCLUDES", text: "~=" },
  4827. { name: "DASHMATCH", text: "|=" },
  4828. { name: "PREFIXMATCH", text: "^=" },
  4829. { name: "SUFFIXMATCH", text: "$=" },
  4830. { name: "SUBSTRINGMATCH", text: "*=" },
  4831. // identifier types
  4832. { name: "STRING" },
  4833. { name: "IDENT" },
  4834. { name: "HASH" },
  4835. // at-keywords
  4836. { name: "IMPORT_SYM", text: "@import" },
  4837. { name: "PAGE_SYM", text: "@page" },
  4838. { name: "MEDIA_SYM", text: "@media" },
  4839. { name: "FONT_FACE_SYM", text: "@font-face" },
  4840. { name: "CHARSET_SYM", text: "@charset" },
  4841. { name: "NAMESPACE_SYM", text: "@namespace" },
  4842. { name: "SUPPORTS_SYM", text: "@supports" },
  4843. { name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport", "@-o-viewport"] },
  4844. { name: "DOCUMENT_SYM", text: ["@document", "@-moz-document"] },
  4845. { name: "UNKNOWN_SYM" },
  4846. //{ name: "ATKEYWORD"},
  4847. // CSS3 animations
  4848. { name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-o-keyframes" ] },
  4849. // important symbol
  4850. { name: "IMPORTANT_SYM" },
  4851. // measurements
  4852. { name: "LENGTH" },
  4853. { name: "ANGLE" },
  4854. { name: "TIME" },
  4855. { name: "FREQ" },
  4856. { name: "DIMENSION" },
  4857. { name: "PERCENTAGE" },
  4858. { name: "NUMBER" },
  4859. // functions
  4860. { name: "URI" },
  4861. { name: "FUNCTION" },
  4862. // Unicode ranges
  4863. { name: "UNICODE_RANGE" },
  4864. /*
  4865. * The following token names are defined in CSS3 Selectors: https://www.w3.org/TR/css3-selectors/#selector-syntax
  4866. */
  4867. // invalid string
  4868. { name: "INVALID" },
  4869. // combinators
  4870. { name: "PLUS", text: "+" },
  4871. { name: "GREATER", text: ">" },
  4872. { name: "COMMA", text: "," },
  4873. { name: "TILDE", text: "~" },
  4874. // modifier
  4875. { name: "NOT" },
  4876. { name: "ANY", text: ["any", "-webkit-any", "-moz-any"] },
  4877. /*
  4878. * Defined in CSS3 Paged Media
  4879. */
  4880. { name: "TOPLEFTCORNER_SYM", text: "@top-left-corner" },
  4881. { name: "TOPLEFT_SYM", text: "@top-left" },
  4882. { name: "TOPCENTER_SYM", text: "@top-center" },
  4883. { name: "TOPRIGHT_SYM", text: "@top-right" },
  4884. { name: "TOPRIGHTCORNER_SYM", text: "@top-right-corner" },
  4885. { name: "BOTTOMLEFTCORNER_SYM", text: "@bottom-left-corner" },
  4886. { name: "BOTTOMLEFT_SYM", text: "@bottom-left" },
  4887. { name: "BOTTOMCENTER_SYM", text: "@bottom-center" },
  4888. { name: "BOTTOMRIGHT_SYM", text: "@bottom-right" },
  4889. { name: "BOTTOMRIGHTCORNER_SYM", text: "@bottom-right-corner" },
  4890. { name: "LEFTTOP_SYM", text: "@left-top" },
  4891. { name: "LEFTMIDDLE_SYM", text: "@left-middle" },
  4892. { name: "LEFTBOTTOM_SYM", text: "@left-bottom" },
  4893. { name: "RIGHTTOP_SYM", text: "@right-top" },
  4894. { name: "RIGHTMIDDLE_SYM", text: "@right-middle" },
  4895. { name: "RIGHTBOTTOM_SYM", text: "@right-bottom" },
  4896. /*
  4897. * The following token names are defined in CSS3 Media Queries: https://www.w3.org/TR/css3-mediaqueries/#syntax
  4898. */
  4899. /*{ name: "MEDIA_ONLY", state: "media"},
  4900. { name: "MEDIA_NOT", state: "media"},
  4901. { name: "MEDIA_AND", state: "media"},*/
  4902. { name: "RESOLUTION", state: "media" },
  4903. /*
  4904. * The following token names are not defined in any CSS specification but are used by the lexer.
  4905. */
  4906. // not a real token, but useful for stupid IE filters
  4907. { name: "IE_FUNCTION" },
  4908. // part of CSS3 grammar but not the Flex code
  4909. { name: "CHAR" },
  4910. // TODO: Needed?
  4911. // Not defined as tokens, but might as well be
  4912. {
  4913. name: "PIPE",
  4914. text: "|"
  4915. },
  4916. {
  4917. name: "SLASH",
  4918. text: "/"
  4919. },
  4920. {
  4921. name: "MINUS",
  4922. text: "-"
  4923. },
  4924. {
  4925. name: "STAR",
  4926. text: "*"
  4927. },
  4928. {
  4929. name: "LBRACE",
  4930. endChar: "}",
  4931. text: "{"
  4932. },
  4933. {
  4934. name: "RBRACE",
  4935. text: "}"
  4936. },
  4937. {
  4938. name: "LBRACKET",
  4939. endChar: "]",
  4940. text: "["
  4941. },
  4942. {
  4943. name: "RBRACKET",
  4944. text: "]"
  4945. },
  4946. {
  4947. name: "EQUALS",
  4948. text: "="
  4949. },
  4950. {
  4951. name: "COLON",
  4952. text: ":"
  4953. },
  4954. {
  4955. name: "SEMICOLON",
  4956. text: ";"
  4957. },
  4958. {
  4959. name: "LPAREN",
  4960. endChar: ")",
  4961. text: "("
  4962. },
  4963. {
  4964. name: "RPAREN",
  4965. text: ")"
  4966. },
  4967. {
  4968. name: "DOT",
  4969. text: "."
  4970. }
  4971. ];
  4972. (function() {
  4973. var nameMap = [],
  4974. typeMap = Object.create(null);
  4975. Tokens.UNKNOWN = -1;
  4976. Tokens.unshift({ name:"EOF" });
  4977. for (var i=0, len = Tokens.length; i < len; i++) {
  4978. nameMap.push(Tokens[i].name);
  4979. Tokens[Tokens[i].name] = i;
  4980. if (Tokens[i].text) {
  4981. if (Tokens[i].text instanceof Array) {
  4982. for (var j=0; j < Tokens[i].text.length; j++) {
  4983. typeMap[Tokens[i].text[j]] = i;
  4984. }
  4985. } else {
  4986. typeMap[Tokens[i].text] = i;
  4987. }
  4988. }
  4989. }
  4990. Tokens.name = function(tt) {
  4991. return nameMap[tt];
  4992. };
  4993. Tokens.type = function(c) {
  4994. return typeMap[c] || -1;
  4995. };
  4996. })();
  4997. },{}],19:[function(require,module,exports){
  4998. "use strict";
  4999. /* exported Validation */
  5000. var Matcher = require("./Matcher");
  5001. var Properties = require("./Properties");
  5002. var ValidationTypes = require("./ValidationTypes");
  5003. var ValidationError = require("./ValidationError");
  5004. var PropertyValueIterator = require("./PropertyValueIterator");
  5005. var Validation = module.exports = {
  5006. validate: function(property, value) {
  5007. //normalize name
  5008. var name = property.toString().toLowerCase(),
  5009. expression = new PropertyValueIterator(value),
  5010. spec = Properties[name],
  5011. part;
  5012. if (!spec) {
  5013. if (name.indexOf("-") !== 0) { //vendor prefixed are ok
  5014. throw new ValidationError("Unknown property '" + property + "'.", property.line, property.col);
  5015. }
  5016. } else if (typeof spec !== "number") {
  5017. // All properties accept some CSS-wide values.
  5018. // https://drafts.csswg.org/css-values-3/#common-keywords
  5019. if (ValidationTypes.isAny(expression, "inherit | initial | unset")) {
  5020. if (expression.hasNext()) {
  5021. part = expression.next();
  5022. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  5023. }
  5024. return;
  5025. }
  5026. // Property-specific validation.
  5027. this.singleProperty(spec, expression);
  5028. }
  5029. },
  5030. singleProperty: function(types, expression) {
  5031. var result = false,
  5032. value = expression.value,
  5033. part;
  5034. result = Matcher.parse(types + ' | <var>').match(expression);
  5035. if (!result) {
  5036. if (expression.hasNext() && !expression.isFirst()) {
  5037. part = expression.peek();
  5038. if (part.name !== 'var' || part.type !== 'function') {
  5039. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  5040. }
  5041. } else {
  5042. throw new ValidationError("Expected (" + ValidationTypes.describe(types) + ") but found '" + value + "'.", value.line, value.col);
  5043. }
  5044. } else if (expression.hasNext()) {
  5045. part = expression.next();
  5046. if (part.name !== 'var' || part.type !== 'function') {
  5047. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  5048. }
  5049. }
  5050. }
  5051. };
  5052. },{"./Matcher":3,"./Properties":7,"./PropertyValueIterator":10,"./ValidationError":20,"./ValidationTypes":21}],20:[function(require,module,exports){
  5053. "use strict";
  5054. module.exports = ValidationError;
  5055. /**
  5056. * Type to use when a validation error occurs.
  5057. * @class ValidationError
  5058. * @namespace parserlib.util
  5059. * @constructor
  5060. * @param {String} message The error message.
  5061. * @param {int} line The line at which the error occurred.
  5062. * @param {int} col The column at which the error occurred.
  5063. */
  5064. function ValidationError(message, line, col) {
  5065. /**
  5066. * The column at which the error occurred.
  5067. * @type int
  5068. * @property col
  5069. */
  5070. this.col = col;
  5071. /**
  5072. * The line at which the error occurred.
  5073. * @type int
  5074. * @property line
  5075. */
  5076. this.line = line;
  5077. /**
  5078. * The text representation of the unit.
  5079. * @type String
  5080. * @property text
  5081. */
  5082. this.message = message;
  5083. }
  5084. //inherit from Error
  5085. ValidationError.prototype = new Error();
  5086. },{}],21:[function(require,module,exports){
  5087. "use strict";
  5088. var ValidationTypes = module.exports;
  5089. var Matcher = require("./Matcher");
  5090. function copy(to, from) {
  5091. Object.keys(from).forEach(function(prop) {
  5092. to[prop] = from[prop];
  5093. });
  5094. }
  5095. copy(ValidationTypes, {
  5096. isLiteral: function (part, literals) {
  5097. var text = part.text.toString().toLowerCase(),
  5098. args = literals.split(" | "),
  5099. i, len, found = false;
  5100. for (i=0, len=args.length; i < len && !found; i++) {
  5101. if (args[i].charAt(0) === "<") {
  5102. found = this.simple[args[i]](part);
  5103. } else if (args[i].slice(-2) === "()") {
  5104. found = (part.type === "function" &&
  5105. part.name === args[i].slice(0, -2));
  5106. } else if (text === args[i].toLowerCase()) {
  5107. found = true;
  5108. }
  5109. }
  5110. return found;
  5111. },
  5112. isSimple: function(type) {
  5113. return Boolean(this.simple[type]);
  5114. },
  5115. isComplex: function(type) {
  5116. return Boolean(this.complex[type]);
  5117. },
  5118. describe: function(type) {
  5119. if (this.complex[type] instanceof Matcher) {
  5120. return this.complex[type].toString(0);
  5121. }
  5122. return type;
  5123. },
  5124. /**
  5125. * Determines if the next part(s) of the given expression
  5126. * are any of the given types.
  5127. */
  5128. isAny: function (expression, types) {
  5129. var args = types.split(" | "),
  5130. i, len, found = false;
  5131. for (i=0, len=args.length; i < len && !found && expression.hasNext(); i++) {
  5132. found = this.isType(expression, args[i]);
  5133. }
  5134. return found;
  5135. },
  5136. /**
  5137. * Determines if the next part(s) of the given expression
  5138. * are one of a group.
  5139. */
  5140. isAnyOfGroup: function(expression, types) {
  5141. var args = types.split(" || "),
  5142. i, len, found = false;
  5143. for (i=0, len=args.length; i < len && !found; i++) {
  5144. found = this.isType(expression, args[i]);
  5145. }
  5146. return found ? args[i-1] : false;
  5147. },
  5148. /**
  5149. * Determines if the next part(s) of the given expression
  5150. * are of a given type.
  5151. */
  5152. isType: function (expression, type) {
  5153. var part = expression.peek(),
  5154. result = false;
  5155. if (type.charAt(0) !== "<") {
  5156. result = this.isLiteral(part, type);
  5157. if (result) {
  5158. expression.next();
  5159. }
  5160. } else if (this.simple[type]) {
  5161. result = this.simple[type](part);
  5162. if (result) {
  5163. expression.next();
  5164. }
  5165. } else if (this.complex[type] instanceof Matcher) {
  5166. result = this.complex[type].match(expression);
  5167. } else {
  5168. result = this.complex[type](expression);
  5169. }
  5170. return result;
  5171. },
  5172. simple: {
  5173. __proto__: null,
  5174. "<absolute-size>":
  5175. "xx-small | x-small | small | medium | large | x-large | xx-large",
  5176. "<animateable-feature>":
  5177. "scroll-position | contents | <animateable-feature-name>",
  5178. "<animateable-feature-name>": function(part) {
  5179. return this["<ident>"](part) &&
  5180. !/^(unset|initial|inherit|will-change|auto|scroll-position|contents)$/i.test(part);
  5181. },
  5182. "<angle>": function(part) {
  5183. return part.type === "angle";
  5184. },
  5185. "<attachment>": "scroll | fixed | local",
  5186. "<attr>": "attr()",
  5187. // inset() = inset( <shape-arg>{1,4} [round <border-radius>]? )
  5188. // circle() = circle( [<shape-radius>]? [at <position>]? )
  5189. // ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? )
  5190. // polygon() = polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )
  5191. "<basic-shape>": "inset() | circle() | ellipse() | polygon()",
  5192. "<bg-image>": "<image> | <gradient> | none",
  5193. "<blend-mode>": "normal | multiply | screen | overlay | darken | lighten | color-dodge | color-burn | hard-light | soft-light | difference | exclusion | hue | saturation | color | luminosity",
  5194. "<border-style>":
  5195. "none | hidden | dotted | dashed | solid | double | groove | " +
  5196. "ridge | inset | outset",
  5197. "<border-width>": "<length> | thin | medium | thick",
  5198. "<box>": "padding-box | border-box | content-box",
  5199. "<clip-source>": "<uri>",
  5200. "<color>": function(part) {
  5201. return part.type === "color" || String(part) === "transparent" || String(part) === "currentColor";
  5202. },
  5203. // The SVG <color> spec doesn't include "currentColor" or "transparent" as a color.
  5204. "<color-svg>": function(part) {
  5205. return part.type === "color";
  5206. },
  5207. "<content>": "content()",
  5208. // https://www.w3.org/TR/css3-sizing/#width-height-keywords
  5209. "<content-sizing>":
  5210. "fill-available | -moz-available | -webkit-fill-available | " +
  5211. "max-content | -moz-max-content | -webkit-max-content | " +
  5212. "min-content | -moz-min-content | -webkit-min-content | " +
  5213. "fit-content | -moz-fit-content | -webkit-fit-content",
  5214. "<feature-tag-value>": function(part) {
  5215. return part.type === "function" && /^[A-Z0-9]{4}$/i.test(part);
  5216. },
  5217. // custom() isn't actually in the spec
  5218. "<filter-function>":
  5219. "blur() | brightness() | contrast() | custom() | " +
  5220. "drop-shadow() | grayscale() | hue-rotate() | invert() | " +
  5221. "opacity() | saturate() | sepia()",
  5222. "<flex-basis>": "<width>",
  5223. "<flex-direction>": "row | row-reverse | column | column-reverse",
  5224. "<flex-grow>": "<number>",
  5225. "<flex-shrink>": "<number>",
  5226. "<flex-wrap>": "nowrap | wrap | wrap-reverse",
  5227. "<font-size>":
  5228. "<absolute-size> | <relative-size> | <length> | <percentage>",
  5229. "<font-stretch>":
  5230. "normal | ultra-condensed | extra-condensed | condensed | " +
  5231. "semi-condensed | semi-expanded | expanded | extra-expanded | " +
  5232. "ultra-expanded",
  5233. "<font-style>": "normal | italic | oblique",
  5234. "<font-variant-caps>":
  5235. "small-caps | all-small-caps | petite-caps | all-petite-caps | " +
  5236. "unicase | titling-caps",
  5237. "<font-variant-css21>": "normal | small-caps",
  5238. "<font-weight>":
  5239. "normal | bold | bolder | lighter | " +
  5240. "100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900",
  5241. "<generic-family>":
  5242. "serif | sans-serif | cursive | fantasy | monospace",
  5243. "<geometry-box>": "<shape-box> | fill-box | stroke-box | view-box",
  5244. "<glyph-angle>": function(part) {
  5245. return part.type === "angle" && part.units === "deg";
  5246. },
  5247. "<gradient>": function(part) {
  5248. return part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial\-|linear\-)?gradient/i.test(part);
  5249. },
  5250. "<icccolor>":
  5251. "cielab() | cielch() | cielchab() | " +
  5252. "icc-color() | icc-named-color()",
  5253. //any identifier
  5254. "<ident>": function(part) {
  5255. return part.type === "identifier" || part.wasIdent;
  5256. },
  5257. "<ident-not-generic-family>": function(part) {
  5258. return this["<ident>"](part) && !this["<generic-family>"](part);
  5259. },
  5260. "<image>": "<uri>",
  5261. "<integer>": function(part) {
  5262. return part.type === "integer";
  5263. },
  5264. "<length>": function(part) {
  5265. if (part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?calc/i.test(part)) {
  5266. return true;
  5267. } else {
  5268. return part.type === "length" || part.type === "number" || part.type === "integer" || String(part) === "0";
  5269. }
  5270. },
  5271. "<line>": function(part) {
  5272. return part.type === "integer";
  5273. },
  5274. "<line-height>": "<number> | <length> | <percentage> | normal",
  5275. "<margin-width>": "<length> | <percentage> | auto",
  5276. "<miterlimit>": function(part) {
  5277. return this["<number>"](part) && part.value >= 1;
  5278. },
  5279. "<nonnegative-length-or-percentage>": function(part) {
  5280. return (this["<length>"](part) || this["<percentage>"](part)) &&
  5281. (String(part) === "0" || part.type === "function" || (part.value) >= 0);
  5282. },
  5283. "<nonnegative-number-or-percentage>": function(part) {
  5284. return (this["<number>"](part) || this["<percentage>"](part)) &&
  5285. (String(part) === "0" || part.type === "function" || (part.value) >= 0);
  5286. },
  5287. "<number>": function(part) {
  5288. return part.type === "number" || this["<integer>"](part);
  5289. },
  5290. "<opacity-value>": function(part) {
  5291. return this["<number>"](part) && part.value >= 0 && part.value <= 1;
  5292. },
  5293. "<padding-width>": "<nonnegative-length-or-percentage>",
  5294. "<percentage>": function(part) {
  5295. return part.type === "percentage" || String(part) === "0";
  5296. },
  5297. "<relative-size>": "smaller | larger",
  5298. "<shape>": "rect() | inset-rect()",
  5299. "<shape-box>": "<box> | margin-box",
  5300. "<single-animation-direction>":
  5301. "normal | reverse | alternate | alternate-reverse",
  5302. "<single-animation-name>": function(part) {
  5303. return this["<ident>"](part) &&
  5304. /^-?[a-z_][-a-z0-9_]+$/i.test(part) &&
  5305. !/^(none|unset|initial|inherit)$/i.test(part);
  5306. },
  5307. "<string>": function(part) {
  5308. return part.type === "string";
  5309. },
  5310. "<time>": function(part) {
  5311. return part.type === "time";
  5312. },
  5313. "<unicode-range>": function(part) {
  5314. return /^U\+[0-9a-f?]{1,6}(-[0-9a-f?]{1,6})?\s*$/i.test(part);
  5315. },
  5316. "<uri>": function(part) {
  5317. return part.type === "uri";
  5318. },
  5319. "<var>": "var()",
  5320. "<width>": "<margin-width>"
  5321. },
  5322. complex: {
  5323. __proto__: null,
  5324. "<azimuth>":
  5325. "<angle>" +
  5326. " | " +
  5327. "[ [ left-side | far-left | left | center-left | center | " +
  5328. "center-right | right | far-right | right-side ] || behind ]" +
  5329. " | "+
  5330. "leftwards | rightwards",
  5331. "<bg-position>": "<position>#",
  5332. "<bg-size>":
  5333. "[ <length> | <percentage> | auto ]{1,2} | cover | contain",
  5334. "<border-image-slice>":
  5335. // [<number> | <percentage>]{1,4} && fill?
  5336. // *but* fill can appear between any of the numbers
  5337. Matcher.many([true /* first element is required */],
  5338. Matcher.cast("<nonnegative-number-or-percentage>"),
  5339. Matcher.cast("<nonnegative-number-or-percentage>"),
  5340. Matcher.cast("<nonnegative-number-or-percentage>"),
  5341. Matcher.cast("<nonnegative-number-or-percentage>"),
  5342. "fill"),
  5343. "<border-radius>":
  5344. "<nonnegative-length-or-percentage>{1,4} " +
  5345. "[ / <nonnegative-length-or-percentage>{1,4} ]?",
  5346. "<box-shadow>": "none | <shadow>#",
  5347. "<clip-path>": "<basic-shape> || <geometry-box>",
  5348. "<dasharray>":
  5349. // "list of comma and/or white space separated <length>s and
  5350. // <percentage>s". There is a non-negative constraint.
  5351. Matcher.cast("<nonnegative-length-or-percentage>")
  5352. .braces(1, Infinity, "#", Matcher.cast(",").question()),
  5353. "<family-name>":
  5354. // <string> | <IDENT>+
  5355. "<string> | <ident-not-generic-family> <ident>*",
  5356. "<filter-function-list>": "[ <filter-function> | <uri> ]+",
  5357. // https://www.w3.org/TR/2014/WD-css-flexbox-1-20140325/#flex-property
  5358. "<flex>":
  5359. "none | [ <flex-grow> <flex-shrink>? || <flex-basis> ]",
  5360. "<font-family>": "[ <generic-family> | <family-name> ]#",
  5361. "<font-shorthand>":
  5362. "[ <font-style> || <font-variant-css21> || " +
  5363. "<font-weight> || <font-stretch> ]? <font-size> " +
  5364. "[ / <line-height> ]? <font-family>",
  5365. "<font-variant-alternates>":
  5366. // stylistic(<feature-value-name>)
  5367. "stylistic() || " +
  5368. "historical-forms || " +
  5369. // styleset(<feature-value-name> #)
  5370. "styleset() || " +
  5371. // character-variant(<feature-value-name> #)
  5372. "character-variant() || " +
  5373. // swash(<feature-value-name>)
  5374. "swash() || " +
  5375. // ornaments(<feature-value-name>)
  5376. "ornaments() || " +
  5377. // annotation(<feature-value-name>)
  5378. "annotation()",
  5379. "<font-variant-ligatures>":
  5380. // <common-lig-values>
  5381. "[ common-ligatures | no-common-ligatures ] || " +
  5382. // <discretionary-lig-values>
  5383. "[ discretionary-ligatures | no-discretionary-ligatures ] || " +
  5384. // <historical-lig-values>
  5385. "[ historical-ligatures | no-historical-ligatures ] || " +
  5386. // <contextual-alt-values>
  5387. "[ contextual | no-contextual ]",
  5388. "<font-variant-numeric>":
  5389. // <numeric-figure-values>
  5390. "[ lining-nums | oldstyle-nums ] || " +
  5391. // <numeric-spacing-values>
  5392. "[ proportional-nums | tabular-nums ] || " +
  5393. // <numeric-fraction-values>
  5394. "[ diagonal-fractions | stacked-fractions ] || " +
  5395. "ordinal || slashed-zero",
  5396. "<font-variant-east-asian>":
  5397. // <east-asian-variant-values>
  5398. "[ jis78 | jis83 | jis90 | jis04 | simplified | traditional ] || " +
  5399. // <east-asian-width-values>
  5400. "[ full-width | proportional-width ] || " +
  5401. "ruby",
  5402. // Note that <color> here is "as defined in the SVG spec", which
  5403. // is more restrictive that the <color> defined in the CSS spec.
  5404. // none | currentColor | <color> [<icccolor>]? |
  5405. // <funciri> [ none | currentColor | <color> [<icccolor>]? ]?
  5406. "<paint>": "<paint-basic> | <uri> <paint-basic>?",
  5407. // Helper definition for <paint> above.
  5408. "<paint-basic>": "none | currentColor | <color-svg> <icccolor>?",
  5409. "<position>":
  5410. // Because our `alt` combinator is ordered, we need to test these
  5411. // in order from longest possible match to shortest.
  5412. "[ center | [ left | right ] [ <percentage> | <length> ]? ] && " +
  5413. "[ center | [ top | bottom ] [ <percentage> | <length> ]? ]" +
  5414. " | " +
  5415. "[ left | center | right | <percentage> | <length> ] " +
  5416. "[ top | center | bottom | <percentage> | <length> ]" +
  5417. " | " +
  5418. "[ left | center | right | top | bottom | <percentage> | <length> ]",
  5419. "<repeat-style>":
  5420. "repeat-x | repeat-y | [ repeat | space | round | no-repeat ]{1,2}",
  5421. "<shadow>":
  5422. //inset? && [ <length>{2,4} && <color>? ]
  5423. Matcher.many([true /* length is required */],
  5424. Matcher.cast("<length>").braces(2, 4), "inset", "<color>"),
  5425. "<text-decoration-color>":
  5426. "<color>",
  5427. "<text-decoration-line>":
  5428. "none | [ underline || overline || line-through || blink ]",
  5429. "<text-decoration-style>":
  5430. "solid | double | dotted | dashed | wavy",
  5431. "<will-change>":
  5432. "auto | <animateable-feature>#",
  5433. "<x-one-radius>":
  5434. //[ <length> | <percentage> ] [ <length> | <percentage> ]?
  5435. "[ <length> | <percentage> ]{1,2}"
  5436. }
  5437. });
  5438. Object.keys(ValidationTypes.simple).forEach(function(nt) {
  5439. var rule = ValidationTypes.simple[nt];
  5440. if (typeof rule === "string") {
  5441. ValidationTypes.simple[nt] = function(part) {
  5442. return ValidationTypes.isLiteral(part, rule);
  5443. };
  5444. }
  5445. });
  5446. Object.keys(ValidationTypes.complex).forEach(function(nt) {
  5447. var rule = ValidationTypes.complex[nt];
  5448. if (typeof rule === "string") {
  5449. ValidationTypes.complex[nt] = Matcher.parse(rule);
  5450. }
  5451. });
  5452. // Because this is defined relative to other complex validation types,
  5453. // we need to define it *after* the rest of the types are initialized.
  5454. ValidationTypes.complex["<font-variant>"] =
  5455. Matcher.oror({ expand: "<font-variant-ligatures>" },
  5456. { expand: "<font-variant-alternates>" },
  5457. "<font-variant-caps>",
  5458. { expand: "<font-variant-numeric>" },
  5459. { expand: "<font-variant-east-asian>" });
  5460. },{"./Matcher":3}],22:[function(require,module,exports){
  5461. "use strict";
  5462. module.exports = {
  5463. Colors : require("./Colors"),
  5464. Combinator : require("./Combinator"),
  5465. Parser : require("./Parser"),
  5466. PropertyName : require("./PropertyName"),
  5467. PropertyValue : require("./PropertyValue"),
  5468. PropertyValuePart : require("./PropertyValuePart"),
  5469. Matcher : require("./Matcher"),
  5470. MediaFeature : require("./MediaFeature"),
  5471. MediaQuery : require("./MediaQuery"),
  5472. Selector : require("./Selector"),
  5473. SelectorPart : require("./SelectorPart"),
  5474. SelectorSubPart : require("./SelectorSubPart"),
  5475. Specificity : require("./Specificity"),
  5476. TokenStream : require("./TokenStream"),
  5477. Tokens : require("./Tokens"),
  5478. ValidationError : require("./ValidationError")
  5479. };
  5480. },{"./Colors":1,"./Combinator":2,"./Matcher":3,"./MediaFeature":4,"./MediaQuery":5,"./Parser":6,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./Specificity":16,"./TokenStream":17,"./Tokens":18,"./ValidationError":20}],23:[function(require,module,exports){
  5481. "use strict";
  5482. module.exports = EventTarget;
  5483. /**
  5484. * A generic base to inherit from for any object
  5485. * that needs event handling.
  5486. * @class EventTarget
  5487. * @constructor
  5488. */
  5489. function EventTarget() {
  5490. /**
  5491. * The array of listeners for various events.
  5492. * @type Object
  5493. * @property _listeners
  5494. * @private
  5495. */
  5496. this._listeners = Object.create(null);
  5497. }
  5498. EventTarget.prototype = {
  5499. //restore constructor
  5500. constructor: EventTarget,
  5501. /**
  5502. * Adds a listener for a given event type.
  5503. * @param {String} type The type of event to add a listener for.
  5504. * @param {Function} listener The function to call when the event occurs.
  5505. * @return {void}
  5506. * @method addListener
  5507. */
  5508. addListener: function(type, listener) {
  5509. if (!this._listeners[type]) {
  5510. this._listeners[type] = [];
  5511. }
  5512. this._listeners[type].push(listener);
  5513. },
  5514. /**
  5515. * Fires an event based on the passed-in object.
  5516. * @param {Object|String} event An object with at least a 'type' attribute
  5517. * or a string indicating the event name.
  5518. * @return {void}
  5519. * @method fire
  5520. */
  5521. fire: function(event) {
  5522. if (typeof event === "string") {
  5523. event = { type: event };
  5524. }
  5525. if (typeof event.target !== "undefined") {
  5526. event.target = this;
  5527. }
  5528. if (typeof event.type === "undefined") {
  5529. throw new Error("Event object missing 'type' property.");
  5530. }
  5531. if (this._listeners[event.type]) {
  5532. //create a copy of the array and use that so listeners can't chane
  5533. var listeners = this._listeners[event.type].concat();
  5534. for (var i=0, len=listeners.length; i < len; i++) {
  5535. listeners[i].call(this, event);
  5536. }
  5537. }
  5538. },
  5539. /**
  5540. * Removes a listener for a given event type.
  5541. * @param {String} type The type of event to remove a listener from.
  5542. * @param {Function} listener The function to remove from the event.
  5543. * @return {void}
  5544. * @method removeListener
  5545. */
  5546. removeListener: function(type, listener) {
  5547. if (this._listeners[type]) {
  5548. var listeners = this._listeners[type];
  5549. for (var i=0, len=listeners.length; i < len; i++) {
  5550. if (listeners[i] === listener) {
  5551. listeners.splice(i, 1);
  5552. break;
  5553. }
  5554. }
  5555. }
  5556. }
  5557. };
  5558. },{}],24:[function(require,module,exports){
  5559. "use strict";
  5560. module.exports = StringReader;
  5561. /**
  5562. * Convenient way to read through strings.
  5563. * @namespace parserlib.util
  5564. * @class StringReader
  5565. * @constructor
  5566. * @param {String} text The text to read.
  5567. */
  5568. function StringReader(text) {
  5569. /**
  5570. * The input text with line endings normalized.
  5571. * @property _input
  5572. * @type String
  5573. * @private
  5574. */
  5575. this._input = text.replace(/(\r\n?|\n)/g, "\n");
  5576. /**
  5577. * The row for the character to be read next.
  5578. * @property _line
  5579. * @type int
  5580. * @private
  5581. */
  5582. this._line = 1;
  5583. /**
  5584. * The column for the character to be read next.
  5585. * @property _col
  5586. * @type int
  5587. * @private
  5588. */
  5589. this._col = 1;
  5590. /**
  5591. * The index of the character in the input to be read next.
  5592. * @property _cursor
  5593. * @type int
  5594. * @private
  5595. */
  5596. this._cursor = 0;
  5597. }
  5598. StringReader.prototype = {
  5599. // restore constructor
  5600. constructor: StringReader,
  5601. //-------------------------------------------------------------------------
  5602. // Position info
  5603. //-------------------------------------------------------------------------
  5604. /**
  5605. * Returns the column of the character to be read next.
  5606. * @return {int} The column of the character to be read next.
  5607. * @method getCol
  5608. */
  5609. getCol: function() {
  5610. return this._col;
  5611. },
  5612. /**
  5613. * Returns the row of the character to be read next.
  5614. * @return {int} The row of the character to be read next.
  5615. * @method getLine
  5616. */
  5617. getLine: function() {
  5618. return this._line;
  5619. },
  5620. /**
  5621. * Determines if you're at the end of the input.
  5622. * @return {Boolean} True if there's no more input, false otherwise.
  5623. * @method eof
  5624. */
  5625. eof: function() {
  5626. return this._cursor === this._input.length;
  5627. },
  5628. //-------------------------------------------------------------------------
  5629. // Basic reading
  5630. //-------------------------------------------------------------------------
  5631. /**
  5632. * Reads the next character without advancing the cursor.
  5633. * @param {int} count How many characters to look ahead (default is 1).
  5634. * @return {String} The next character or null if there is no next character.
  5635. * @method peek
  5636. */
  5637. peek: function(count) {
  5638. var c = null;
  5639. count = typeof count === "undefined" ? 1 : count;
  5640. // if we're not at the end of the input...
  5641. if (this._cursor < this._input.length) {
  5642. // get character and increment cursor and column
  5643. c = this._input.charAt(this._cursor + count - 1);
  5644. }
  5645. return c;
  5646. },
  5647. /**
  5648. * Reads the next character from the input and adjusts the row and column
  5649. * accordingly.
  5650. * @return {String} The next character or null if there is no next character.
  5651. * @method read
  5652. */
  5653. read: function() {
  5654. var c = null;
  5655. // if we're not at the end of the input...
  5656. if (this._cursor < this._input.length) {
  5657. // if the last character was a newline, increment row count
  5658. // and reset column count
  5659. if (this._input.charAt(this._cursor) === "\n") {
  5660. this._line++;
  5661. this._col=1;
  5662. } else {
  5663. this._col++;
  5664. }
  5665. // get character and increment cursor and column
  5666. c = this._input.charAt(this._cursor++);
  5667. }
  5668. return c;
  5669. },
  5670. //-------------------------------------------------------------------------
  5671. // Misc
  5672. //-------------------------------------------------------------------------
  5673. /**
  5674. * Saves the current location so it can be returned to later.
  5675. * @method mark
  5676. * @return {void}
  5677. */
  5678. mark: function() {
  5679. this._bookmark = {
  5680. cursor: this._cursor,
  5681. line: this._line,
  5682. col: this._col
  5683. };
  5684. },
  5685. reset: function() {
  5686. if (this._bookmark) {
  5687. this._cursor = this._bookmark.cursor;
  5688. this._line = this._bookmark.line;
  5689. this._col = this._bookmark.col;
  5690. delete this._bookmark;
  5691. }
  5692. },
  5693. //-------------------------------------------------------------------------
  5694. // Advanced reading
  5695. //-------------------------------------------------------------------------
  5696. /**
  5697. * Reads up to and including the given string. Throws an error if that
  5698. * string is not found.
  5699. * @param {String} pattern The string to read.
  5700. * @return {String} The string when it is found.
  5701. * @throws Error when the string pattern is not found.
  5702. * @method readTo
  5703. */
  5704. readTo: function(pattern) {
  5705. var buffer = "",
  5706. c;
  5707. /*
  5708. * First, buffer must be the same length as the pattern.
  5709. * Then, buffer must end with the pattern or else reach the
  5710. * end of the input.
  5711. */
  5712. while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) !== buffer.length - pattern.length) {
  5713. c = this.read();
  5714. if (c) {
  5715. buffer += c;
  5716. } else {
  5717. throw new Error("Expected \"" + pattern + "\" at line " + this._line + ", col " + this._col + ".");
  5718. }
  5719. }
  5720. return buffer;
  5721. },
  5722. /**
  5723. * Reads characters while each character causes the given
  5724. * filter function to return true. The function is passed
  5725. * in each character and either returns true to continue
  5726. * reading or false to stop.
  5727. * @param {Function} filter The function to read on each character.
  5728. * @return {String} The string made up of all characters that passed the
  5729. * filter check.
  5730. * @method readWhile
  5731. */
  5732. readWhile: function(filter) {
  5733. var buffer = "",
  5734. c = this.peek();
  5735. while (c !== null && filter(c)) {
  5736. buffer += this.read();
  5737. c = this.peek();
  5738. }
  5739. return buffer;
  5740. },
  5741. /**
  5742. * Reads characters that match either text or a regular expression and
  5743. * returns those characters. If a match is found, the row and column
  5744. * are adjusted; if no match is found, the reader's state is unchanged.
  5745. * reading or false to stop.
  5746. * @param {String|RegExp} matcher If a string, then the literal string
  5747. * value is searched for. If a regular expression, then any string
  5748. * matching the pattern is search for.
  5749. * @return {String} The string made up of all characters that matched or
  5750. * null if there was no match.
  5751. * @method readMatch
  5752. */
  5753. readMatch: function(matcher) {
  5754. var source = this._input.substring(this._cursor),
  5755. value = null;
  5756. // if it's a string, just do a straight match
  5757. if (typeof matcher === "string") {
  5758. if (source.slice(0, matcher.length) === matcher) {
  5759. value = this.readCount(matcher.length);
  5760. }
  5761. } else if (matcher instanceof RegExp) {
  5762. if (matcher.test(source)) {
  5763. value = this.readCount(RegExp.lastMatch.length);
  5764. }
  5765. }
  5766. return value;
  5767. },
  5768. /**
  5769. * Reads a given number of characters. If the end of the input is reached,
  5770. * it reads only the remaining characters and does not throw an error.
  5771. * @param {int} count The number of characters to read.
  5772. * @return {String} The string made up the read characters.
  5773. * @method readCount
  5774. */
  5775. readCount: function(count) {
  5776. var buffer = "";
  5777. while (count--) {
  5778. buffer += this.read();
  5779. }
  5780. return buffer;
  5781. }
  5782. };
  5783. },{}],25:[function(require,module,exports){
  5784. "use strict";
  5785. module.exports = SyntaxError;
  5786. /**
  5787. * Type to use when a syntax error occurs.
  5788. * @class SyntaxError
  5789. * @namespace parserlib.util
  5790. * @constructor
  5791. * @param {String} message The error message.
  5792. * @param {int} line The line at which the error occurred.
  5793. * @param {int} col The column at which the error occurred.
  5794. */
  5795. function SyntaxError(message, line, col) {
  5796. Error.call(this);
  5797. this.name = this.constructor.name;
  5798. /**
  5799. * The column at which the error occurred.
  5800. * @type int
  5801. * @property col
  5802. */
  5803. this.col = col;
  5804. /**
  5805. * The line at which the error occurred.
  5806. * @type int
  5807. * @property line
  5808. */
  5809. this.line = line;
  5810. /**
  5811. * The text representation of the unit.
  5812. * @type String
  5813. * @property text
  5814. */
  5815. this.message = message;
  5816. }
  5817. //inherit from Error
  5818. SyntaxError.prototype = Object.create(Error.prototype); // jshint ignore:line
  5819. SyntaxError.prototype.constructor = SyntaxError; // jshint ignore:line
  5820. },{}],26:[function(require,module,exports){
  5821. "use strict";
  5822. module.exports = SyntaxUnit;
  5823. /**
  5824. * Base type to represent a single syntactic unit.
  5825. * @class SyntaxUnit
  5826. * @namespace parserlib.util
  5827. * @constructor
  5828. * @param {String} text The text of the unit.
  5829. * @param {int} line The line of text on which the unit resides.
  5830. * @param {int} col The column of text on which the unit resides.
  5831. */
  5832. function SyntaxUnit(text, line, col, type) {
  5833. /**
  5834. * The column of text on which the unit resides.
  5835. * @type int
  5836. * @property col
  5837. */
  5838. this.col = col;
  5839. /**
  5840. * The line of text on which the unit resides.
  5841. * @type int
  5842. * @property line
  5843. */
  5844. this.line = line;
  5845. /**
  5846. * The text representation of the unit.
  5847. * @type String
  5848. * @property text
  5849. */
  5850. this.text = text;
  5851. /**
  5852. * The type of syntax unit.
  5853. * @type int
  5854. * @property type
  5855. */
  5856. this.type = type;
  5857. }
  5858. /**
  5859. * Create a new syntax unit based solely on the given token.
  5860. * Convenience method for creating a new syntax unit when
  5861. * it represents a single token instead of multiple.
  5862. * @param {Object} token The token object to represent.
  5863. * @return {parserlib.util.SyntaxUnit} The object representing the token.
  5864. * @static
  5865. * @method fromToken
  5866. */
  5867. SyntaxUnit.fromToken = function(token) {
  5868. return new SyntaxUnit(token.value, token.startLine, token.startCol);
  5869. };
  5870. SyntaxUnit.prototype = {
  5871. //restore constructor
  5872. constructor: SyntaxUnit,
  5873. /**
  5874. * Returns the text representation of the unit.
  5875. * @return {String} The text representation of the unit.
  5876. * @method valueOf
  5877. */
  5878. valueOf: function() {
  5879. return this.toString();
  5880. },
  5881. /**
  5882. * Returns the text representation of the unit.
  5883. * @return {String} The text representation of the unit.
  5884. * @method toString
  5885. */
  5886. toString: function() {
  5887. return this.text;
  5888. }
  5889. };
  5890. },{}],27:[function(require,module,exports){
  5891. "use strict";
  5892. module.exports = TokenStreamBase;
  5893. var StringReader = require("./StringReader");
  5894. var SyntaxError = require("./SyntaxError");
  5895. /**
  5896. * Generic TokenStream providing base functionality.
  5897. * @class TokenStreamBase
  5898. * @namespace parserlib.util
  5899. * @constructor
  5900. * @param {String|StringReader} input The text to tokenize or a reader from
  5901. * which to read the input.
  5902. */
  5903. function TokenStreamBase(input, tokenData) {
  5904. /**
  5905. * The string reader for easy access to the text.
  5906. * @type StringReader
  5907. * @property _reader
  5908. * @private
  5909. */
  5910. this._reader = new StringReader(input ? input.toString() : "");
  5911. /**
  5912. * Token object for the last consumed token.
  5913. * @type Token
  5914. * @property _token
  5915. * @private
  5916. */
  5917. this._token = null;
  5918. /**
  5919. * The array of token information.
  5920. * @type Array
  5921. * @property _tokenData
  5922. * @private
  5923. */
  5924. this._tokenData = tokenData;
  5925. /**
  5926. * Lookahead token buffer.
  5927. * @type Array
  5928. * @property _lt
  5929. * @private
  5930. */
  5931. this._lt = [];
  5932. /**
  5933. * Lookahead token buffer index.
  5934. * @type int
  5935. * @property _ltIndex
  5936. * @private
  5937. */
  5938. this._ltIndex = 0;
  5939. this._ltIndexCache = [];
  5940. }
  5941. /**
  5942. * Accepts an array of token information and outputs
  5943. * an array of token data containing key-value mappings
  5944. * and matching functions that the TokenStream needs.
  5945. * @param {Array} tokens An array of token descriptors.
  5946. * @return {Array} An array of processed token data.
  5947. * @method createTokenData
  5948. * @static
  5949. */
  5950. TokenStreamBase.createTokenData = function(tokens) {
  5951. var nameMap = [],
  5952. typeMap = Object.create(null),
  5953. tokenData = tokens.concat([]),
  5954. i = 0,
  5955. len = tokenData.length+1;
  5956. tokenData.UNKNOWN = -1;
  5957. tokenData.unshift({ name:"EOF" });
  5958. for (; i < len; i++) {
  5959. nameMap.push(tokenData[i].name);
  5960. tokenData[tokenData[i].name] = i;
  5961. if (tokenData[i].text) {
  5962. typeMap[tokenData[i].text] = i;
  5963. }
  5964. }
  5965. tokenData.name = function(tt) {
  5966. return nameMap[tt];
  5967. };
  5968. tokenData.type = function(c) {
  5969. return typeMap[c];
  5970. };
  5971. return tokenData;
  5972. };
  5973. TokenStreamBase.prototype = {
  5974. //restore constructor
  5975. constructor: TokenStreamBase,
  5976. //-------------------------------------------------------------------------
  5977. // Matching methods
  5978. //-------------------------------------------------------------------------
  5979. /**
  5980. * Determines if the next token matches the given token type.
  5981. * If so, that token is consumed; if not, the token is placed
  5982. * back onto the token stream. You can pass in any number of
  5983. * token types and this will return true if any of the token
  5984. * types is found.
  5985. * @param {int|int[]} tokenTypes Either a single token type or an array of
  5986. * token types that the next token might be. If an array is passed,
  5987. * it's assumed that the token can be any of these.
  5988. * @param {variant} channel (Optional) The channel to read from. If not
  5989. * provided, reads from the default (unnamed) channel.
  5990. * @return {Boolean} True if the token type matches, false if not.
  5991. * @method match
  5992. */
  5993. match: function(tokenTypes, channel) {
  5994. //always convert to an array, makes things easier
  5995. if (!(tokenTypes instanceof Array)) {
  5996. tokenTypes = [tokenTypes];
  5997. }
  5998. do {
  5999. var tt = this.get(channel);
  6000. if (tokenTypes.includes(tt)) {
  6001. return true;
  6002. }
  6003. } while (tt === 4 && this.LA(0) !== 0);
  6004. //no match found, put the token back
  6005. this.unget();
  6006. return false;
  6007. },
  6008. /**
  6009. * Determines if the next token matches the given token type.
  6010. * If so, that token is consumed; if not, an error is thrown.
  6011. * @param {int|int[]} tokenTypes Either a single token type or an array of
  6012. * token types that the next token should be. If an array is passed,
  6013. * it's assumed that the token must be one of these.
  6014. * @return {void}
  6015. * @method mustMatch
  6016. */
  6017. mustMatch: function(tokenTypes) {
  6018. var token;
  6019. //always convert to an array, makes things easier
  6020. if (!(tokenTypes instanceof Array)) {
  6021. tokenTypes = [tokenTypes];
  6022. }
  6023. if (!this.match(tokenTypes)) {
  6024. token = this.LT(1);
  6025. throw new SyntaxError("Expected " + this._tokenData[tokenTypes[0]].name +
  6026. " at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
  6027. }
  6028. },
  6029. //-------------------------------------------------------------------------
  6030. // Consuming methods
  6031. //-------------------------------------------------------------------------
  6032. /**
  6033. * Keeps reading from the token stream until either one of the specified
  6034. * token types is found or until the end of the input is reached.
  6035. * @param {int|int[]} tokenTypes Either a single token type or an array of
  6036. * token types that the next token should be. If an array is passed,
  6037. * it's assumed that the token must be one of these.
  6038. * @param {variant} channel (Optional) The channel to read from. If not
  6039. * provided, reads from the default (unnamed) channel.
  6040. * @return {void}
  6041. * @method advance
  6042. */
  6043. advance: function(tokenTypes, channel) {
  6044. while (this.LA(0) !== 0 && !this.match(tokenTypes, channel)) {
  6045. this.get();
  6046. }
  6047. return this.LA(0);
  6048. },
  6049. /**
  6050. * Consumes the next token from the token stream.
  6051. * @return {int} The token type of the token that was just consumed.
  6052. * @method get
  6053. */
  6054. get: function(channel) {
  6055. var tokenInfo = this._tokenData,
  6056. i =0,
  6057. token,
  6058. info;
  6059. //check the lookahead buffer first
  6060. if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length) {
  6061. i++;
  6062. this._token = this._lt[this._ltIndex++];
  6063. info = tokenInfo[this._token.type];
  6064. //obey channels logic
  6065. while ((info.channel !== undefined && channel !== info.channel) &&
  6066. this._ltIndex < this._lt.length) {
  6067. this._token = this._lt[this._ltIndex++];
  6068. info = tokenInfo[this._token.type];
  6069. i++;
  6070. }
  6071. //here be dragons
  6072. if ((info.channel === undefined || channel === info.channel) &&
  6073. this._ltIndex <= this._lt.length) {
  6074. this._ltIndexCache.push(i);
  6075. return this._token.type;
  6076. }
  6077. }
  6078. //call token retriever method
  6079. token = this._getToken();
  6080. //if it should be hidden, don't save a token
  6081. if (token.type > -1 && !tokenInfo[token.type].hide) {
  6082. //apply token channel
  6083. token.channel = tokenInfo[token.type].channel;
  6084. //save for later
  6085. this._token = token;
  6086. this._lt.push(token);
  6087. //save space that will be moved (must be done before array is truncated)
  6088. this._ltIndexCache.push(this._lt.length - this._ltIndex + i);
  6089. //keep the buffer under 5 items
  6090. if (this._lt.length > 5) {
  6091. this._lt.shift();
  6092. }
  6093. //also keep the shift buffer under 5 items
  6094. if (this._ltIndexCache.length > 5) {
  6095. this._ltIndexCache.shift();
  6096. }
  6097. //update lookahead index
  6098. this._ltIndex = this._lt.length;
  6099. }
  6100. /*
  6101. * Skip to the next token if:
  6102. * 1. The token type is marked as hidden.
  6103. * 2. The token type has a channel specified and it isn't the current channel.
  6104. */
  6105. info = tokenInfo[token.type];
  6106. if (info &&
  6107. (info.hide ||
  6108. (info.channel !== undefined && channel !== info.channel))) {
  6109. return this.get(channel);
  6110. } else {
  6111. //return just the type
  6112. return token.type;
  6113. }
  6114. },
  6115. /**
  6116. * Looks ahead a certain number of tokens and returns the token type at
  6117. * that position. This will throw an error if you lookahead past the
  6118. * end of input, past the size of the lookahead buffer, or back past
  6119. * the first token in the lookahead buffer.
  6120. * @param {int} The index of the token type to retrieve. 0 for the
  6121. * current token, 1 for the next, -1 for the previous, etc.
  6122. * @return {int} The token type of the token in the given position.
  6123. * @method LA
  6124. */
  6125. LA: function(index) {
  6126. var total = index,
  6127. tt;
  6128. if (index > 0) {
  6129. //TODO: Store 5 somewhere
  6130. if (index > 5) {
  6131. throw new Error("Too much lookahead.");
  6132. }
  6133. //get all those tokens
  6134. while (total) {
  6135. tt = this.get();
  6136. total--;
  6137. }
  6138. //unget all those tokens
  6139. while (total < index) {
  6140. this.unget();
  6141. total++;
  6142. }
  6143. } else if (index < 0) {
  6144. if (this._lt[this._ltIndex+index]) {
  6145. tt = this._lt[this._ltIndex+index].type;
  6146. } else {
  6147. throw new Error("Too much lookbehind.");
  6148. }
  6149. } else {
  6150. tt = this._token.type;
  6151. }
  6152. return tt;
  6153. },
  6154. /**
  6155. * Looks ahead a certain number of tokens and returns the token at
  6156. * that position. This will throw an error if you lookahead past the
  6157. * end of input, past the size of the lookahead buffer, or back past
  6158. * the first token in the lookahead buffer.
  6159. * @param {int} The index of the token type to retrieve. 0 for the
  6160. * current token, 1 for the next, -1 for the previous, etc.
  6161. * @return {Object} The token of the token in the given position.
  6162. * @method LA
  6163. */
  6164. LT: function(index) {
  6165. //lookahead first to prime the token buffer
  6166. this.LA(index);
  6167. //now find the token, subtract one because _ltIndex is already at the next index
  6168. return this._lt[this._ltIndex+index-1];
  6169. },
  6170. /**
  6171. * Returns the token type for the next token in the stream without
  6172. * consuming it.
  6173. * @return {int} The token type of the next token in the stream.
  6174. * @method peek
  6175. */
  6176. peek: function() {
  6177. return this.LA(1);
  6178. },
  6179. /**
  6180. * Returns the actual token object for the last consumed token.
  6181. * @return {Token} The token object for the last consumed token.
  6182. * @method token
  6183. */
  6184. token: function() {
  6185. return this._token;
  6186. },
  6187. /**
  6188. * Returns the name of the token for the given token type.
  6189. * @param {int} tokenType The type of token to get the name of.
  6190. * @return {String} The name of the token or "UNKNOWN_TOKEN" for any
  6191. * invalid token type.
  6192. * @method tokenName
  6193. */
  6194. tokenName: function(tokenType) {
  6195. if (tokenType < 0 || tokenType > this._tokenData.length) {
  6196. return "UNKNOWN_TOKEN";
  6197. } else {
  6198. return this._tokenData[tokenType].name;
  6199. }
  6200. },
  6201. /**
  6202. * Returns the token type value for the given token name.
  6203. * @param {String} tokenName The name of the token whose value should be returned.
  6204. * @return {int} The token type value for the given token name or -1
  6205. * for an unknown token.
  6206. * @method tokenName
  6207. */
  6208. tokenType: function(tokenName) {
  6209. return this._tokenData[tokenName] || -1;
  6210. },
  6211. /**
  6212. * Returns the last consumed token to the token stream.
  6213. * @method unget
  6214. */
  6215. unget: function() {
  6216. //if (this._ltIndex > -1) {
  6217. if (this._ltIndexCache.length) {
  6218. this._ltIndex -= this._ltIndexCache.pop();//--;
  6219. this._token = this._lt[this._ltIndex - 1];
  6220. } else {
  6221. throw new Error("Too much lookahead.");
  6222. }
  6223. }
  6224. };
  6225. },{"./StringReader":24,"./SyntaxError":25}],28:[function(require,module,exports){
  6226. "use strict";
  6227. module.exports = {
  6228. StringReader : require("./StringReader"),
  6229. SyntaxError : require("./SyntaxError"),
  6230. SyntaxUnit : require("./SyntaxUnit"),
  6231. EventTarget : require("./EventTarget"),
  6232. TokenStreamBase : require("./TokenStreamBase")
  6233. };
  6234. },{"./EventTarget":23,"./StringReader":24,"./SyntaxError":25,"./SyntaxUnit":26,"./TokenStreamBase":27}],"parserlib":[function(require,module,exports){
  6235. "use strict";
  6236. module.exports = {
  6237. css : require("./css"),
  6238. util : require("./util")
  6239. };
  6240. },{"./css":22,"./util":28}]},{},[]);
  6241. return require('parserlib');
  6242. })();
  6243. var clone = (function() {
  6244. 'use strict';
  6245. var nativeMap;
  6246. try {
  6247. nativeMap = Map;
  6248. } catch(_) {
  6249. // maybe a reference error because no `Map`. Give it a dummy value that no
  6250. // value will ever be an instanceof.
  6251. nativeMap = function() {};
  6252. }
  6253. var nativeSet;
  6254. try {
  6255. nativeSet = Set;
  6256. } catch(_) {
  6257. nativeSet = function() {};
  6258. }
  6259. var nativePromise;
  6260. try {
  6261. nativePromise = Promise;
  6262. } catch(_) {
  6263. nativePromise = function() {};
  6264. }
  6265. /**
  6266. * Clones (copies) an Object using deep copying.
  6267. *
  6268. * This function supports circular references by default, but if you are certain
  6269. * there are no circular references in your object, you can save some CPU time
  6270. * by calling clone(obj, false).
  6271. *
  6272. * Caution: if `circular` is false and `parent` contains circular references,
  6273. * your program may enter an infinite loop and crash.
  6274. *
  6275. * @param `parent` - the object to be cloned
  6276. * @param `circular` - set to true if the object to be cloned may contain
  6277. * circular references. (optional - true by default)
  6278. * @param `depth` - set to a number if the object is only to be cloned to
  6279. * a particular depth. (optional - defaults to Infinity)
  6280. * @param `prototype` - sets the prototype to be used when cloning an object.
  6281. * (optional - defaults to parent prototype).
  6282. * @param `includeNonEnumerable` - set to true if the non-enumerable properties
  6283. * should be cloned as well. Non-enumerable properties on the prototype
  6284. * chain will be ignored. (optional - false by default)
  6285. */
  6286. function clone(parent, circular, depth, prototype, includeNonEnumerable) {
  6287. if (typeof circular === 'object') {
  6288. depth = circular.depth;
  6289. prototype = circular.prototype;
  6290. includeNonEnumerable = circular.includeNonEnumerable;
  6291. circular = circular.circular;
  6292. }
  6293. // maintain two arrays for circular references, where corresponding parents
  6294. // and children have the same index
  6295. var allParents = [];
  6296. var allChildren = [];
  6297. var useBuffer = typeof Buffer != 'undefined';
  6298. if (typeof circular == 'undefined')
  6299. circular = true;
  6300. if (typeof depth == 'undefined')
  6301. depth = Infinity;
  6302. // recurse this function so we don't reset allParents and allChildren
  6303. function _clone(parent, depth) {
  6304. // cloning null always returns null
  6305. if (parent === null)
  6306. return null;
  6307. if (depth === 0)
  6308. return parent;
  6309. var child;
  6310. var proto;
  6311. if (typeof parent != 'object') {
  6312. return parent;
  6313. }
  6314. if (parent instanceof nativeMap) {
  6315. child = new nativeMap();
  6316. } else if (parent instanceof nativeSet) {
  6317. child = new nativeSet();
  6318. } else if (parent instanceof nativePromise) {
  6319. child = new nativePromise(function (resolve, reject) {
  6320. parent.then(function(value) {
  6321. resolve(_clone(value, depth - 1));
  6322. }, function(err) {
  6323. reject(_clone(err, depth - 1));
  6324. });
  6325. });
  6326. } else if (clone.__isArray(parent)) {
  6327. child = [];
  6328. } else if (clone.__isRegExp(parent)) {
  6329. child = new RegExp(parent.source, __getRegExpFlags(parent));
  6330. if (parent.lastIndex) child.lastIndex = parent.lastIndex;
  6331. } else if (clone.__isDate(parent)) {
  6332. child = new Date(parent.getTime());
  6333. } else if (useBuffer && Buffer.isBuffer(parent)) {
  6334. child = new Buffer(parent.length);
  6335. parent.copy(child);
  6336. return child;
  6337. } else if (parent instanceof Error) {
  6338. child = Object.create(parent);
  6339. } else {
  6340. if (typeof prototype == 'undefined') {
  6341. proto = Object.getPrototypeOf(parent);
  6342. child = Object.create(proto);
  6343. }
  6344. else {
  6345. child = Object.create(prototype);
  6346. proto = prototype;
  6347. }
  6348. }
  6349. if (circular) {
  6350. var index = allParents.indexOf(parent);
  6351. if (index != -1) {
  6352. return allChildren[index];
  6353. }
  6354. allParents.push(parent);
  6355. allChildren.push(child);
  6356. }
  6357. if (parent instanceof nativeMap) {
  6358. var keyIterator = parent.keys();
  6359. while(true) {
  6360. var next = keyIterator.next();
  6361. if (next.done) {
  6362. break;
  6363. }
  6364. var keyChild = _clone(next.value, depth - 1);
  6365. var valueChild = _clone(parent.get(next.value), depth - 1);
  6366. child.set(keyChild, valueChild);
  6367. }
  6368. }
  6369. if (parent instanceof nativeSet) {
  6370. var iterator = parent.keys();
  6371. while(true) {
  6372. var next = iterator.next();
  6373. if (next.done) {
  6374. break;
  6375. }
  6376. var entryChild = _clone(next.value, depth - 1);
  6377. child.add(entryChild);
  6378. }
  6379. }
  6380. for (var i in parent) {
  6381. var attrs;
  6382. if (proto) {
  6383. attrs = Object.getOwnPropertyDescriptor(proto, i);
  6384. }
  6385. if (attrs && attrs.set == null) {
  6386. continue;
  6387. }
  6388. child[i] = _clone(parent[i], depth - 1);
  6389. }
  6390. if (Object.getOwnPropertySymbols) {
  6391. var symbols = Object.getOwnPropertySymbols(parent);
  6392. for (var i = 0; i < symbols.length; i++) {
  6393. // Don't need to worry about cloning a symbol because it is a primitive,
  6394. // like a number or string.
  6395. var symbol = symbols[i];
  6396. var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
  6397. if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
  6398. continue;
  6399. }
  6400. child[symbol] = _clone(parent[symbol], depth - 1);
  6401. if (!descriptor.enumerable) {
  6402. Object.defineProperty(child, symbol, {
  6403. enumerable: false
  6404. });
  6405. }
  6406. }
  6407. }
  6408. if (includeNonEnumerable) {
  6409. var allPropertyNames = Object.getOwnPropertyNames(parent);
  6410. for (var i = 0; i < allPropertyNames.length; i++) {
  6411. var propertyName = allPropertyNames[i];
  6412. var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
  6413. if (descriptor && descriptor.enumerable) {
  6414. continue;
  6415. }
  6416. child[propertyName] = _clone(parent[propertyName], depth - 1);
  6417. Object.defineProperty(child, propertyName, {
  6418. enumerable: false
  6419. });
  6420. }
  6421. }
  6422. return child;
  6423. }
  6424. return _clone(parent, depth);
  6425. }
  6426. /**
  6427. * Simple flat clone using prototype, accepts only objects, usefull for property
  6428. * override on FLAT configuration object (no nested props).
  6429. *
  6430. * USE WITH CAUTION! This may not behave as you wish if you do not know how this
  6431. * works.
  6432. */
  6433. clone.clonePrototype = function clonePrototype(parent) {
  6434. if (parent === null)
  6435. return null;
  6436. var c = function () {};
  6437. c.prototype = parent;
  6438. return new c();
  6439. };
  6440. // private utility functions
  6441. function __objToStr(o) {
  6442. return Object.prototype.toString.call(o);
  6443. }
  6444. clone.__objToStr = __objToStr;
  6445. function __isDate(o) {
  6446. return typeof o === 'object' && __objToStr(o) === '[object Date]';
  6447. }
  6448. clone.__isDate = __isDate;
  6449. function __isArray(o) {
  6450. return typeof o === 'object' && __objToStr(o) === '[object Array]';
  6451. }
  6452. clone.__isArray = __isArray;
  6453. function __isRegExp(o) {
  6454. return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
  6455. }
  6456. clone.__isRegExp = __isRegExp;
  6457. function __getRegExpFlags(re) {
  6458. var flags = '';
  6459. if (re.global) flags += 'g';
  6460. if (re.ignoreCase) flags += 'i';
  6461. if (re.multiline) flags += 'm';
  6462. return flags;
  6463. }
  6464. clone.__getRegExpFlags = __getRegExpFlags;
  6465. return clone;
  6466. })();
  6467. if (typeof module === 'object' && module.exports) {
  6468. module.exports = clone;
  6469. }
  6470. /**
  6471. * Main CSSLint object.
  6472. * @class CSSLint
  6473. * @static
  6474. * @extends parserlib.util.EventTarget
  6475. */
  6476. /* global parserlib, clone, Reporter */
  6477. /* exported CSSLint */
  6478. var CSSLint = (function() {
  6479. "use strict";
  6480. var rules = [],
  6481. formatters = [],
  6482. embeddedRuleset = /\/\*\s*csslint([^\*]*)\*\//,
  6483. api = new parserlib.util.EventTarget();
  6484. api.version = "1.0.4";
  6485. //-------------------------------------------------------------------------
  6486. // Rule Management
  6487. //-------------------------------------------------------------------------
  6488. /**
  6489. * Adds a new rule to the engine.
  6490. * @param {Object} rule The rule to add.
  6491. * @method addRule
  6492. */
  6493. api.addRule = function(rule) {
  6494. rules.push(rule);
  6495. rules[rule.id] = rule;
  6496. };
  6497. /**
  6498. * Clears all rule from the engine.
  6499. * @method clearRules
  6500. */
  6501. api.clearRules = function() {
  6502. rules = [];
  6503. };
  6504. /**
  6505. * Returns the rule objects.
  6506. * @return An array of rule objects.
  6507. * @method getRules
  6508. */
  6509. api.getRules = function() {
  6510. return [].concat(rules).sort(function(a, b) {
  6511. return a.id > b.id ? 1 : 0;
  6512. });
  6513. };
  6514. /**
  6515. * Returns a ruleset configuration object with all current rules.
  6516. * @return A ruleset object.
  6517. * @method getRuleset
  6518. */
  6519. api.getRuleset = function() {
  6520. var ruleset = {},
  6521. i = 0,
  6522. len = rules.length;
  6523. while (i < len) {
  6524. ruleset[rules[i++].id] = 1; // by default, everything is a warning
  6525. }
  6526. return ruleset;
  6527. };
  6528. /**
  6529. * Returns a ruleset object based on embedded rules.
  6530. * @param {String} text A string of css containing embedded rules.
  6531. * @param {Object} ruleset A ruleset object to modify.
  6532. * @return {Object} A ruleset object.
  6533. * @method getEmbeddedRuleset
  6534. */
  6535. function applyEmbeddedRuleset(text, ruleset) {
  6536. var valueMap,
  6537. embedded = text && text.match(embeddedRuleset),
  6538. rules = embedded && embedded[1];
  6539. if (rules) {
  6540. valueMap = {
  6541. "true": 2, // true is error
  6542. "": 1, // blank is warning
  6543. "false": 0, // false is ignore
  6544. "2": 2, // explicit error
  6545. "1": 1, // explicit warning
  6546. "0": 0 // explicit ignore
  6547. };
  6548. rules.toLowerCase().split(",").forEach(function(rule) {
  6549. var pair = rule.split(":"),
  6550. property = pair[0] || "",
  6551. value = pair[1] || "";
  6552. ruleset[property.trim()] = valueMap[value.trim()];
  6553. });
  6554. }
  6555. return ruleset;
  6556. }
  6557. //-------------------------------------------------------------------------
  6558. // Formatters
  6559. //-------------------------------------------------------------------------
  6560. /**
  6561. * Adds a new formatter to the engine.
  6562. * @param {Object} formatter The formatter to add.
  6563. * @method addFormatter
  6564. */
  6565. api.addFormatter = function(formatter) {
  6566. // formatters.push(formatter);
  6567. formatters[formatter.id] = formatter;
  6568. };
  6569. /**
  6570. * Retrieves a formatter for use.
  6571. * @param {String} formatId The name of the format to retrieve.
  6572. * @return {Object} The formatter or undefined.
  6573. * @method getFormatter
  6574. */
  6575. api.getFormatter = function(formatId) {
  6576. return formatters[formatId];
  6577. };
  6578. /**
  6579. * Formats the results in a particular format for a single file.
  6580. * @param {Object} result The results returned from CSSLint.verify().
  6581. * @param {String} filename The filename for which the results apply.
  6582. * @param {String} formatId The name of the formatter to use.
  6583. * @param {Object} options (Optional) for special output handling.
  6584. * @return {String} A formatted string for the results.
  6585. * @method format
  6586. */
  6587. api.format = function(results, filename, formatId, options) {
  6588. var formatter = this.getFormatter(formatId),
  6589. result = null;
  6590. if (formatter) {
  6591. result = formatter.startFormat();
  6592. result += formatter.formatResults(results, filename, options || {});
  6593. result += formatter.endFormat();
  6594. }
  6595. return result;
  6596. };
  6597. /**
  6598. * Indicates if the given format is supported.
  6599. * @param {String} formatId The ID of the format to check.
  6600. * @return {Boolean} True if the format exists, false if not.
  6601. * @method hasFormat
  6602. */
  6603. api.hasFormat = function(formatId) {
  6604. return formatters.hasOwnProperty(formatId);
  6605. };
  6606. //-------------------------------------------------------------------------
  6607. // Verification
  6608. //-------------------------------------------------------------------------
  6609. /**
  6610. * Starts the verification process for the given CSS text.
  6611. * @param {String} text The CSS text to verify.
  6612. * @param {Object} ruleset (Optional) List of rules to apply. If null, then
  6613. * all rules are used. If a rule has a value of 1 then it's a warning,
  6614. * a value of 2 means it's an error.
  6615. * @return {Object} Results of the verification.
  6616. * @method verify
  6617. */
  6618. api.verify = function(text, ruleset) {
  6619. var i = 0,
  6620. reporter,
  6621. lines,
  6622. allow = {},
  6623. ignore = [],
  6624. report,
  6625. parser = new parserlib.css.Parser({
  6626. starHack: true,
  6627. ieFilters: true,
  6628. underscoreHack: true,
  6629. strict: false
  6630. });
  6631. // normalize line endings
  6632. lines = text.replace(/\n\r?/g, "$split$").split("$split$");
  6633. // find 'allow' comments
  6634. CSSLint.Util.forEach(lines, function (line, lineno) {
  6635. var allowLine = line && line.match(/\/\*[ \t]*csslint[ \t]+allow:[ \t]*([^\*]*)\*\//i),
  6636. allowRules = allowLine && allowLine[1],
  6637. allowRuleset = {};
  6638. if (allowRules) {
  6639. allowRules.toLowerCase().split(",").forEach(function(allowRule) {
  6640. allowRuleset[allowRule.trim()] = true;
  6641. });
  6642. if (Object.keys(allowRuleset).length > 0) {
  6643. allow[lineno + 1] = allowRuleset;
  6644. }
  6645. }
  6646. });
  6647. var ignoreStart = null,
  6648. ignoreEnd = null;
  6649. CSSLint.Util.forEach(lines, function (line, lineno) {
  6650. // Keep oldest, "unclosest" ignore:start
  6651. if (ignoreStart === null && line.match(/\/\*[ \t]*csslint[ \t]+ignore:start[ \t]*\*\//i)) {
  6652. ignoreStart = lineno;
  6653. }
  6654. if (line.match(/\/\*[ \t]*csslint[ \t]+ignore:end[ \t]*\*\//i)) {
  6655. ignoreEnd = lineno;
  6656. }
  6657. if (ignoreStart !== null && ignoreEnd !== null) {
  6658. ignore.push([ignoreStart, ignoreEnd]);
  6659. ignoreStart = ignoreEnd = null;
  6660. }
  6661. });
  6662. // Close remaining ignore block, if any
  6663. if (ignoreStart !== null) {
  6664. ignore.push([ignoreStart, lines.length]);
  6665. }
  6666. if (!ruleset) {
  6667. ruleset = this.getRuleset();
  6668. }
  6669. if (embeddedRuleset.test(text)) {
  6670. // defensively copy so that caller's version does not get modified
  6671. ruleset = clone(ruleset);
  6672. ruleset = applyEmbeddedRuleset(text, ruleset);
  6673. }
  6674. reporter = new Reporter(lines, ruleset, allow, ignore);
  6675. ruleset.errors = 2; // always report parsing errors as errors
  6676. for (i in ruleset) {
  6677. if (ruleset.hasOwnProperty(i) && ruleset[i]) {
  6678. if (rules[i]) {
  6679. rules[i].init(parser, reporter);
  6680. }
  6681. }
  6682. }
  6683. // capture most horrible error type
  6684. try {
  6685. parser.parse(text);
  6686. } catch (ex) {
  6687. reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
  6688. }
  6689. report = {
  6690. messages : reporter.messages,
  6691. stats : reporter.stats,
  6692. ruleset : reporter.ruleset,
  6693. allow : reporter.allow,
  6694. ignore : reporter.ignore
  6695. };
  6696. // sort by line numbers, rollups at the bottom
  6697. report.messages.sort(function (a, b) {
  6698. if (a.rollup && !b.rollup) {
  6699. return 1;
  6700. } else if (!a.rollup && b.rollup) {
  6701. return -1;
  6702. } else {
  6703. return a.line - b.line;
  6704. }
  6705. });
  6706. return report;
  6707. };
  6708. //-------------------------------------------------------------------------
  6709. // Publish the API
  6710. //-------------------------------------------------------------------------
  6711. return api;
  6712. })();
  6713. /**
  6714. * An instance of Report is used to report results of the
  6715. * verification back to the main API.
  6716. * @class Reporter
  6717. * @constructor
  6718. * @param {String[]} lines The text lines of the source.
  6719. * @param {Object} ruleset The set of rules to work with, including if
  6720. * they are errors or warnings.
  6721. * @param {Object} explicitly allowed lines
  6722. * @param {[][]} ingore list of line ranges to be ignored
  6723. */
  6724. function Reporter(lines, ruleset, allow, ignore) {
  6725. "use strict";
  6726. /**
  6727. * List of messages being reported.
  6728. * @property messages
  6729. * @type String[]
  6730. */
  6731. this.messages = [];
  6732. /**
  6733. * List of statistics being reported.
  6734. * @property stats
  6735. * @type String[]
  6736. */
  6737. this.stats = [];
  6738. /**
  6739. * Lines of code being reported on. Used to provide contextual information
  6740. * for messages.
  6741. * @property lines
  6742. * @type String[]
  6743. */
  6744. this.lines = lines;
  6745. /**
  6746. * Information about the rules. Used to determine whether an issue is an
  6747. * error or warning.
  6748. * @property ruleset
  6749. * @type Object
  6750. */
  6751. this.ruleset = ruleset;
  6752. /**
  6753. * Lines with specific rule messages to leave out of the report.
  6754. * @property allow
  6755. * @type Object
  6756. */
  6757. this.allow = allow;
  6758. if (!this.allow) {
  6759. this.allow = {};
  6760. }
  6761. /**
  6762. * Linesets not to include in the report.
  6763. * @property ignore
  6764. * @type [][]
  6765. */
  6766. this.ignore = ignore;
  6767. if (!this.ignore) {
  6768. this.ignore = [];
  6769. }
  6770. }
  6771. Reporter.prototype = {
  6772. // restore constructor
  6773. constructor: Reporter,
  6774. /**
  6775. * Report an error.
  6776. * @param {String} message The message to store.
  6777. * @param {int} line The line number.
  6778. * @param {int} col The column number.
  6779. * @param {Object} rule The rule this message relates to.
  6780. * @method error
  6781. */
  6782. error: function(message, line, col, rule) {
  6783. "use strict";
  6784. this.messages.push({
  6785. type : "error",
  6786. line : line,
  6787. col : col,
  6788. message : message,
  6789. evidence: this.lines[line-1],
  6790. rule : rule || {}
  6791. });
  6792. },
  6793. /**
  6794. * Report an warning.
  6795. * @param {String} message The message to store.
  6796. * @param {int} line The line number.
  6797. * @param {int} col The column number.
  6798. * @param {Object} rule The rule this message relates to.
  6799. * @method warn
  6800. * @deprecated Use report instead.
  6801. */
  6802. warn: function(message, line, col, rule) {
  6803. "use strict";
  6804. this.report(message, line, col, rule);
  6805. },
  6806. /**
  6807. * Report an issue.
  6808. * @param {String} message The message to store.
  6809. * @param {int} line The line number.
  6810. * @param {int} col The column number.
  6811. * @param {Object} rule The rule this message relates to.
  6812. * @method report
  6813. */
  6814. report: function(message, line, col, rule) {
  6815. "use strict";
  6816. // Check if rule violation should be allowed
  6817. if (this.allow.hasOwnProperty(line) && this.allow[line].hasOwnProperty(rule.id)) {
  6818. return;
  6819. }
  6820. var ignore = false;
  6821. CSSLint.Util.forEach(this.ignore, function (range) {
  6822. if (range[0] <= line && line <= range[1]) {
  6823. ignore = true;
  6824. }
  6825. });
  6826. if (ignore) {
  6827. return;
  6828. }
  6829. this.messages.push({
  6830. type : this.ruleset[rule.id] === 2 ? "error" : "warning",
  6831. line : line,
  6832. col : col,
  6833. message : message,
  6834. evidence: this.lines[line-1],
  6835. rule : rule
  6836. });
  6837. },
  6838. /**
  6839. * Report some informational text.
  6840. * @param {String} message The message to store.
  6841. * @param {int} line The line number.
  6842. * @param {int} col The column number.
  6843. * @param {Object} rule The rule this message relates to.
  6844. * @method info
  6845. */
  6846. info: function(message, line, col, rule) {
  6847. "use strict";
  6848. this.messages.push({
  6849. type : "info",
  6850. line : line,
  6851. col : col,
  6852. message : message,
  6853. evidence: this.lines[line-1],
  6854. rule : rule
  6855. });
  6856. },
  6857. /**
  6858. * Report some rollup error information.
  6859. * @param {String} message The message to store.
  6860. * @param {Object} rule The rule this message relates to.
  6861. * @method rollupError
  6862. */
  6863. rollupError: function(message, rule) {
  6864. "use strict";
  6865. this.messages.push({
  6866. type : "error",
  6867. rollup : true,
  6868. message : message,
  6869. rule : rule
  6870. });
  6871. },
  6872. /**
  6873. * Report some rollup warning information.
  6874. * @param {String} message The message to store.
  6875. * @param {Object} rule The rule this message relates to.
  6876. * @method rollupWarn
  6877. */
  6878. rollupWarn: function(message, rule) {
  6879. "use strict";
  6880. this.messages.push({
  6881. type : "warning",
  6882. rollup : true,
  6883. message : message,
  6884. rule : rule
  6885. });
  6886. },
  6887. /**
  6888. * Report a statistic.
  6889. * @param {String} name The name of the stat to store.
  6890. * @param {Variant} value The value of the stat.
  6891. * @method stat
  6892. */
  6893. stat: function(name, value) {
  6894. "use strict";
  6895. this.stats[name] = value;
  6896. }
  6897. };
  6898. // expose for testing purposes
  6899. CSSLint._Reporter = Reporter;
  6900. /*
  6901. * Utility functions that make life easier.
  6902. */
  6903. CSSLint.Util = {
  6904. /*
  6905. * Adds all properties from supplier onto receiver,
  6906. * overwriting if the same name already exists on
  6907. * receiver.
  6908. * @param {Object} The object to receive the properties.
  6909. * @param {Object} The object to provide the properties.
  6910. * @return {Object} The receiver
  6911. */
  6912. mix: function(receiver, supplier) {
  6913. "use strict";
  6914. var prop;
  6915. for (prop in supplier) {
  6916. if (supplier.hasOwnProperty(prop)) {
  6917. receiver[prop] = supplier[prop];
  6918. }
  6919. }
  6920. return prop;
  6921. },
  6922. /*
  6923. * Polyfill for array indexOf() method.
  6924. * @param {Array} values The array to search.
  6925. * @param {Variant} value The value to search for.
  6926. * @return {int} The index of the value if found, -1 if not.
  6927. */
  6928. indexOf: function(values, value) {
  6929. "use strict";
  6930. if (values.indexOf) {
  6931. return values.indexOf(value);
  6932. } else {
  6933. for (var i=0, len=values.length; i < len; i++) {
  6934. if (values[i] === value) {
  6935. return i;
  6936. }
  6937. }
  6938. return -1;
  6939. }
  6940. },
  6941. /*
  6942. * Polyfill for array forEach() method.
  6943. * @param {Array} values The array to operate on.
  6944. * @param {Function} func The function to call on each item.
  6945. * @return {void}
  6946. */
  6947. forEach: function(values, func) {
  6948. "use strict";
  6949. if (values.forEach) {
  6950. return values.forEach(func);
  6951. } else {
  6952. for (var i=0, len=values.length; i < len; i++) {
  6953. func(values[i], i, values);
  6954. }
  6955. }
  6956. }
  6957. };
  6958. /*
  6959. * Rule: Don't use adjoining classes (.foo.bar).
  6960. */
  6961. CSSLint.addRule({
  6962. // rule information
  6963. id: "adjoining-classes",
  6964. name: "Disallow adjoining classes",
  6965. desc: "Don't use adjoining classes.",
  6966. url: "https://github.com/CSSLint/csslint/wiki/Disallow-adjoining-classes",
  6967. browsers: "IE6",
  6968. // initialization
  6969. init: function(parser, reporter) {
  6970. "use strict";
  6971. var rule = this;
  6972. parser.addListener("startrule", function(event) {
  6973. var selectors = event.selectors,
  6974. selector,
  6975. part,
  6976. modifier,
  6977. classCount,
  6978. i, j, k;
  6979. for (i=0; i < selectors.length; i++) {
  6980. selector = selectors[i];
  6981. for (j=0; j < selector.parts.length; j++) {
  6982. part = selector.parts[j];
  6983. if (part.type === parser.SELECTOR_PART_TYPE) {
  6984. classCount = 0;
  6985. for (k=0; k < part.modifiers.length; k++) {
  6986. modifier = part.modifiers[k];
  6987. if (modifier.type === "class") {
  6988. classCount++;
  6989. }
  6990. if (classCount > 1){
  6991. reporter.report("Adjoining classes: "+selectors[i].text, part.line, part.col, rule);
  6992. }
  6993. }
  6994. }
  6995. }
  6996. }
  6997. });
  6998. }
  6999. });
  7000. /*
  7001. * Rule: Don't use width or height when using padding or border.
  7002. */
  7003. CSSLint.addRule({
  7004. // rule information
  7005. id: "box-model",
  7006. name: "Beware of broken box size",
  7007. desc: "Don't use width or height when using padding or border.",
  7008. url: "https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size",
  7009. browsers: "All",
  7010. // initialization
  7011. init: function(parser, reporter) {
  7012. "use strict";
  7013. var rule = this,
  7014. widthProperties = {
  7015. border: 1,
  7016. "border-left": 1,
  7017. "border-right": 1,
  7018. padding: 1,
  7019. "padding-left": 1,
  7020. "padding-right": 1
  7021. },
  7022. heightProperties = {
  7023. border: 1,
  7024. "border-bottom": 1,
  7025. "border-top": 1,
  7026. padding: 1,
  7027. "padding-bottom": 1,
  7028. "padding-top": 1
  7029. },
  7030. properties,
  7031. boxSizing = false;
  7032. function startRule() {
  7033. properties = {};
  7034. boxSizing = false;
  7035. }
  7036. function endRule() {
  7037. var prop, value;
  7038. if (!boxSizing) {
  7039. if (properties.height) {
  7040. for (prop in heightProperties) {
  7041. if (heightProperties.hasOwnProperty(prop) && properties[prop]) {
  7042. value = properties[prop].value;
  7043. // special case for padding
  7044. if (!(prop === "padding" && value.parts.length === 2 && value.parts[0].value === 0)) {
  7045. reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
  7046. }
  7047. }
  7048. }
  7049. }
  7050. if (properties.width) {
  7051. for (prop in widthProperties) {
  7052. if (widthProperties.hasOwnProperty(prop) && properties[prop]) {
  7053. value = properties[prop].value;
  7054. if (!(prop === "padding" && value.parts.length === 2 && value.parts[1].value === 0)) {
  7055. reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
  7056. }
  7057. }
  7058. }
  7059. }
  7060. }
  7061. }
  7062. parser.addListener("startrule", startRule);
  7063. parser.addListener("startfontface", startRule);
  7064. parser.addListener("startpage", startRule);
  7065. parser.addListener("startpagemargin", startRule);
  7066. parser.addListener("startkeyframerule", startRule);
  7067. parser.addListener("startviewport", startRule);
  7068. parser.addListener("property", function(event) {
  7069. var name = event.property.text.toLowerCase();
  7070. if (heightProperties[name] || widthProperties[name]) {
  7071. if (!/^0\S*$/.test(event.value) && !(name === "border" && event.value.toString() === "none")) {
  7072. properties[name] = {
  7073. line: event.property.line,
  7074. col: event.property.col,
  7075. value: event.value
  7076. };
  7077. }
  7078. } else {
  7079. if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)) {
  7080. properties[name] = 1;
  7081. } else if (name === "box-sizing") {
  7082. boxSizing = true;
  7083. }
  7084. }
  7085. });
  7086. parser.addListener("endrule", endRule);
  7087. parser.addListener("endfontface", endRule);
  7088. parser.addListener("endpage", endRule);
  7089. parser.addListener("endpagemargin", endRule);
  7090. parser.addListener("endkeyframerule", endRule);
  7091. parser.addListener("endviewport", endRule);
  7092. }
  7093. });
  7094. /*
  7095. * Rule: box-sizing doesn't work in IE6 and IE7.
  7096. */
  7097. CSSLint.addRule({
  7098. // rule information
  7099. id: "box-sizing",
  7100. name: "Disallow use of box-sizing",
  7101. desc: "The box-sizing properties isn't supported in IE6 and IE7.",
  7102. url: "https://github.com/CSSLint/csslint/wiki/Disallow-box-sizing",
  7103. browsers: "IE6, IE7",
  7104. tags: ["Compatibility"],
  7105. // initialization
  7106. init: function(parser, reporter) {
  7107. "use strict";
  7108. var rule = this;
  7109. parser.addListener("property", function(event) {
  7110. var name = event.property.text.toLowerCase();
  7111. if (name === "box-sizing") {
  7112. reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
  7113. }
  7114. });
  7115. }
  7116. });
  7117. /*
  7118. * Rule: Use the bulletproof @font-face syntax to avoid 404's in old IE
  7119. * (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
  7120. */
  7121. CSSLint.addRule({
  7122. // rule information
  7123. id: "bulletproof-font-face",
  7124. name: "Use the bulletproof @font-face syntax",
  7125. desc: "Use the bulletproof @font-face syntax to avoid 404's in old IE (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax).",
  7126. url: "https://github.com/CSSLint/csslint/wiki/Bulletproof-font-face",
  7127. browsers: "All",
  7128. // initialization
  7129. init: function(parser, reporter) {
  7130. "use strict";
  7131. var rule = this,
  7132. fontFaceRule = false,
  7133. firstSrc = true,
  7134. ruleFailed = false,
  7135. line, col;
  7136. // Mark the start of a @font-face declaration so we only test properties inside it
  7137. parser.addListener("startfontface", function() {
  7138. fontFaceRule = true;
  7139. });
  7140. parser.addListener("property", function(event) {
  7141. // If we aren't inside an @font-face declaration then just return
  7142. if (!fontFaceRule) {
  7143. return;
  7144. }
  7145. var propertyName = event.property.toString().toLowerCase(),
  7146. value = event.value.toString();
  7147. // Set the line and col numbers for use in the endfontface listener
  7148. line = event.line;
  7149. col = event.col;
  7150. // This is the property that we care about, we can ignore the rest
  7151. if (propertyName === "src") {
  7152. var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
  7153. // We need to handle the advanced syntax with two src properties
  7154. if (!value.match(regex) && firstSrc) {
  7155. ruleFailed = true;
  7156. firstSrc = false;
  7157. } else if (value.match(regex) && !firstSrc) {
  7158. ruleFailed = false;
  7159. }
  7160. }
  7161. });
  7162. // Back to normal rules that we don't need to test
  7163. parser.addListener("endfontface", function() {
  7164. fontFaceRule = false;
  7165. if (ruleFailed) {
  7166. reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
  7167. }
  7168. });
  7169. }
  7170. });
  7171. /*
  7172. * Rule: Include all compatible vendor prefixes to reach a wider
  7173. * range of users.
  7174. */
  7175. CSSLint.addRule({
  7176. // rule information
  7177. id: "compatible-vendor-prefixes",
  7178. name: "Require compatible vendor prefixes",
  7179. desc: "Include all compatible vendor prefixes to reach a wider range of users.",
  7180. url: "https://github.com/CSSLint/csslint/wiki/Require-compatible-vendor-prefixes",
  7181. browsers: "All",
  7182. // initialization
  7183. init: function (parser, reporter) {
  7184. "use strict";
  7185. var rule = this,
  7186. compatiblePrefixes,
  7187. properties,
  7188. prop,
  7189. variations,
  7190. prefixed,
  7191. i,
  7192. len,
  7193. inKeyFrame = false,
  7194. arrayPush = Array.prototype.push,
  7195. applyTo = [];
  7196. // See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
  7197. compatiblePrefixes = {
  7198. "animation" : "webkit",
  7199. "animation-delay" : "webkit",
  7200. "animation-direction" : "webkit",
  7201. "animation-duration" : "webkit",
  7202. "animation-fill-mode" : "webkit",
  7203. "animation-iteration-count" : "webkit",
  7204. "animation-name" : "webkit",
  7205. "animation-play-state" : "webkit",
  7206. "animation-timing-function" : "webkit",
  7207. "appearance" : "webkit moz",
  7208. "border-end" : "webkit moz",
  7209. "border-end-color" : "webkit moz",
  7210. "border-end-style" : "webkit moz",
  7211. "border-end-width" : "webkit moz",
  7212. "border-image" : "webkit moz o",
  7213. "border-radius" : "webkit",
  7214. "border-start" : "webkit moz",
  7215. "border-start-color" : "webkit moz",
  7216. "border-start-style" : "webkit moz",
  7217. "border-start-width" : "webkit moz",
  7218. "box-align" : "webkit moz",
  7219. "box-direction" : "webkit moz",
  7220. "box-flex" : "webkit moz",
  7221. "box-lines" : "webkit",
  7222. "box-ordinal-group" : "webkit moz",
  7223. "box-orient" : "webkit moz",
  7224. "box-pack" : "webkit moz",
  7225. "box-sizing" : "",
  7226. "box-shadow" : "",
  7227. "column-count" : "webkit moz ms",
  7228. "column-gap" : "webkit moz ms",
  7229. "column-rule" : "webkit moz ms",
  7230. "column-rule-color" : "webkit moz ms",
  7231. "column-rule-style" : "webkit moz ms",
  7232. "column-rule-width" : "webkit moz ms",
  7233. "column-width" : "webkit moz ms",
  7234. "flex" : "webkit ms",
  7235. "flex-basis" : "webkit",
  7236. "flex-direction" : "webkit ms",
  7237. "flex-flow" : "webkit",
  7238. "flex-grow" : "webkit",
  7239. "flex-shrink" : "webkit",
  7240. "hyphens" : "epub moz",
  7241. "line-break" : "webkit ms",
  7242. "margin-end" : "webkit moz",
  7243. "margin-start" : "webkit moz",
  7244. "marquee-speed" : "webkit wap",
  7245. "marquee-style" : "webkit wap",
  7246. "padding-end" : "webkit moz",
  7247. "padding-start" : "webkit moz",
  7248. "tab-size" : "moz o",
  7249. "text-size-adjust" : "webkit ms",
  7250. "transform" : "webkit ms",
  7251. "transform-origin" : "webkit ms",
  7252. "transition" : "",
  7253. "transition-delay" : "",
  7254. "transition-duration" : "",
  7255. "transition-property" : "",
  7256. "transition-timing-function" : "",
  7257. "user-modify" : "webkit moz",
  7258. "user-select" : "webkit moz ms",
  7259. "word-break" : "epub ms",
  7260. "writing-mode" : "epub ms"
  7261. };
  7262. for (prop in compatiblePrefixes) {
  7263. if (compatiblePrefixes.hasOwnProperty(prop)) {
  7264. variations = [];
  7265. prefixed = compatiblePrefixes[prop].split(" ");
  7266. for (i = 0, len = prefixed.length; i < len; i++) {
  7267. variations.push("-" + prefixed[i] + "-" + prop);
  7268. }
  7269. compatiblePrefixes[prop] = variations;
  7270. arrayPush.apply(applyTo, variations);
  7271. }
  7272. }
  7273. parser.addListener("startrule", function () {
  7274. properties = [];
  7275. });
  7276. parser.addListener("startkeyframes", function (event) {
  7277. inKeyFrame = event.prefix || true;
  7278. });
  7279. parser.addListener("endkeyframes", function () {
  7280. inKeyFrame = false;
  7281. });
  7282. parser.addListener("property", function (event) {
  7283. var name = event.property;
  7284. if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
  7285. // e.g., -moz-transform is okay to be alone in @-moz-keyframes
  7286. if (!inKeyFrame || typeof inKeyFrame !== "string" ||
  7287. name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
  7288. properties.push(name);
  7289. }
  7290. }
  7291. });
  7292. parser.addListener("endrule", function () {
  7293. if (!properties.length) {
  7294. return;
  7295. }
  7296. var propertyGroups = {},
  7297. i,
  7298. len,
  7299. name,
  7300. prop,
  7301. variations,
  7302. value,
  7303. full,
  7304. actual,
  7305. item,
  7306. propertiesSpecified;
  7307. for (i = 0, len = properties.length; i < len; i++) {
  7308. name = properties[i];
  7309. for (prop in compatiblePrefixes) {
  7310. if (compatiblePrefixes.hasOwnProperty(prop)) {
  7311. variations = compatiblePrefixes[prop];
  7312. if (CSSLint.Util.indexOf(variations, name.text) > -1) {
  7313. if (!propertyGroups[prop]) {
  7314. propertyGroups[prop] = {
  7315. full: variations.slice(0),
  7316. actual: [],
  7317. actualNodes: []
  7318. };
  7319. }
  7320. if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
  7321. propertyGroups[prop].actual.push(name.text);
  7322. propertyGroups[prop].actualNodes.push(name);
  7323. }
  7324. }
  7325. }
  7326. }
  7327. }
  7328. for (prop in propertyGroups) {
  7329. if (propertyGroups.hasOwnProperty(prop)) {
  7330. value = propertyGroups[prop];
  7331. full = value.full;
  7332. actual = value.actual;
  7333. if (full.length > actual.length) {
  7334. for (i = 0, len = full.length; i < len; i++) {
  7335. item = full[i];
  7336. if (CSSLint.Util.indexOf(actual, item) === -1) {
  7337. propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length === 2) ? actual.join(" and ") : actual.join(", ");
  7338. reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
  7339. }
  7340. }
  7341. }
  7342. }
  7343. }
  7344. });
  7345. }
  7346. });
  7347. /*
  7348. * Rule: Certain properties don't play well with certain display values.
  7349. * - float should not be used with inline-block
  7350. * - height, width, margin-top, margin-bottom, float should not be used with inline
  7351. * - vertical-align should not be used with block
  7352. * - margin, float should not be used with table-*
  7353. */
  7354. CSSLint.addRule({
  7355. // rule information
  7356. id: "display-property-grouping",
  7357. name: "Require properties appropriate for display",
  7358. desc: "Certain properties shouldn't be used with certain display property values.",
  7359. url: "https://github.com/CSSLint/csslint/wiki/Require-properties-appropriate-for-display",
  7360. browsers: "All",
  7361. // initialization
  7362. init: function(parser, reporter) {
  7363. "use strict";
  7364. var rule = this;
  7365. var propertiesToCheck = {
  7366. display: 1,
  7367. "float": "none",
  7368. height: 1,
  7369. width: 1,
  7370. margin: 1,
  7371. "margin-left": 1,
  7372. "margin-right": 1,
  7373. "margin-bottom": 1,
  7374. "margin-top": 1,
  7375. padding: 1,
  7376. "padding-left": 1,
  7377. "padding-right": 1,
  7378. "padding-bottom": 1,
  7379. "padding-top": 1,
  7380. "vertical-align": 1
  7381. },
  7382. properties;
  7383. function reportProperty(name, display, msg) {
  7384. if (properties[name]) {
  7385. if (typeof propertiesToCheck[name] !== "string" || properties[name].value.toLowerCase() !== propertiesToCheck[name]) {
  7386. reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
  7387. }
  7388. }
  7389. }
  7390. function startRule() {
  7391. properties = {};
  7392. }
  7393. function endRule() {
  7394. var display = properties.display ? properties.display.value : null;
  7395. if (display) {
  7396. switch (display) {
  7397. case "inline":
  7398. // height, width, margin-top, margin-bottom, float should not be used with inline
  7399. reportProperty("height", display);
  7400. reportProperty("width", display);
  7401. reportProperty("margin", display);
  7402. reportProperty("margin-top", display);
  7403. reportProperty("margin-bottom", display);
  7404. reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
  7405. break;
  7406. case "block":
  7407. // vertical-align should not be used with block
  7408. reportProperty("vertical-align", display);
  7409. break;
  7410. case "inline-block":
  7411. // float should not be used with inline-block
  7412. reportProperty("float", display);
  7413. break;
  7414. default:
  7415. // margin, float should not be used with table
  7416. if (display.indexOf("table-") === 0) {
  7417. reportProperty("margin", display);
  7418. reportProperty("margin-left", display);
  7419. reportProperty("margin-right", display);
  7420. reportProperty("margin-top", display);
  7421. reportProperty("margin-bottom", display);
  7422. reportProperty("float", display);
  7423. }
  7424. // otherwise do nothing
  7425. }
  7426. }
  7427. }
  7428. parser.addListener("startrule", startRule);
  7429. parser.addListener("startfontface", startRule);
  7430. parser.addListener("startkeyframerule", startRule);
  7431. parser.addListener("startpagemargin", startRule);
  7432. parser.addListener("startpage", startRule);
  7433. parser.addListener("startviewport", startRule);
  7434. parser.addListener("property", function(event) {
  7435. var name = event.property.text.toLowerCase();
  7436. if (propertiesToCheck[name]) {
  7437. properties[name] = {
  7438. value: event.value.text,
  7439. line: event.property.line,
  7440. col: event.property.col
  7441. };
  7442. }
  7443. });
  7444. parser.addListener("endrule", endRule);
  7445. parser.addListener("endfontface", endRule);
  7446. parser.addListener("endkeyframerule", endRule);
  7447. parser.addListener("endpagemargin", endRule);
  7448. parser.addListener("endpage", endRule);
  7449. parser.addListener("endviewport", endRule);
  7450. }
  7451. });
  7452. /*
  7453. * Rule: Disallow duplicate background-images (using url).
  7454. */
  7455. CSSLint.addRule({
  7456. // rule information
  7457. id: "duplicate-background-images",
  7458. name: "Disallow duplicate background images",
  7459. desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
  7460. url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images",
  7461. browsers: "All",
  7462. // initialization
  7463. init: function(parser, reporter) {
  7464. "use strict";
  7465. var rule = this,
  7466. stack = {};
  7467. parser.addListener("property", function(event) {
  7468. var name = event.property.text,
  7469. value = event.value,
  7470. i, len;
  7471. if (name.match(/background/i)) {
  7472. for (i=0, len=value.parts.length; i < len; i++) {
  7473. if (value.parts[i].type === "uri") {
  7474. if (typeof stack[value.parts[i].uri] === "undefined") {
  7475. stack[value.parts[i].uri] = event;
  7476. } else {
  7477. reporter.report("Background image '" + value.parts[i].uri + "' was used multiple times, first declared at line " + stack[value.parts[i].uri].line + ", col " + stack[value.parts[i].uri].col + ".", event.line, event.col, rule);
  7478. }
  7479. }
  7480. }
  7481. }
  7482. });
  7483. }
  7484. });
  7485. /*
  7486. * Rule: Duplicate properties must appear one after the other. If an already-defined
  7487. * property appears somewhere else in the rule, then it's likely an error.
  7488. */
  7489. CSSLint.addRule({
  7490. // rule information
  7491. id: "duplicate-properties",
  7492. name: "Disallow duplicate properties",
  7493. desc: "Duplicate properties must appear one after the other.",
  7494. url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties",
  7495. browsers: "All",
  7496. // initialization
  7497. init: function(parser, reporter) {
  7498. "use strict";
  7499. var rule = this,
  7500. properties,
  7501. lastProperty;
  7502. function startRule() {
  7503. properties = {};
  7504. }
  7505. parser.addListener("startrule", startRule);
  7506. parser.addListener("startfontface", startRule);
  7507. parser.addListener("startpage", startRule);
  7508. parser.addListener("startpagemargin", startRule);
  7509. parser.addListener("startkeyframerule", startRule);
  7510. parser.addListener("startviewport", startRule);
  7511. parser.addListener("property", function(event) {
  7512. var property = event.property,
  7513. name = property.text.toLowerCase();
  7514. if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)) {
  7515. reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
  7516. }
  7517. properties[name] = event.value.text;
  7518. lastProperty = name;
  7519. });
  7520. }
  7521. });
  7522. /*
  7523. * Rule: Style rules without any properties defined should be removed.
  7524. */
  7525. CSSLint.addRule({
  7526. // rule information
  7527. id: "empty-rules",
  7528. name: "Disallow empty rules",
  7529. desc: "Rules without any properties specified should be removed.",
  7530. url: "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules",
  7531. browsers: "All",
  7532. // initialization
  7533. init: function(parser, reporter) {
  7534. "use strict";
  7535. var rule = this,
  7536. count = 0;
  7537. parser.addListener("startrule", function() {
  7538. count=0;
  7539. });
  7540. parser.addListener("property", function() {
  7541. count++;
  7542. });
  7543. parser.addListener("endrule", function(event) {
  7544. var selectors = event.selectors;
  7545. if (count === 0) {
  7546. reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
  7547. }
  7548. });
  7549. }
  7550. });
  7551. /*
  7552. * Rule: There should be no syntax errors. (Duh.)
  7553. */
  7554. CSSLint.addRule({
  7555. // rule information
  7556. id: "errors",
  7557. name: "Parsing Errors",
  7558. desc: "This rule looks for recoverable syntax errors.",
  7559. browsers: "All",
  7560. // initialization
  7561. init: function(parser, reporter) {
  7562. "use strict";
  7563. var rule = this;
  7564. parser.addListener("error", function(event) {
  7565. reporter.error(event.message, event.line, event.col, rule);
  7566. });
  7567. }
  7568. });
  7569. CSSLint.addRule({
  7570. // rule information
  7571. id: "fallback-colors",
  7572. name: "Require fallback colors",
  7573. desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
  7574. url: "https://github.com/CSSLint/csslint/wiki/Require-fallback-colors",
  7575. browsers: "IE6,IE7,IE8",
  7576. // initialization
  7577. init: function(parser, reporter) {
  7578. "use strict";
  7579. var rule = this,
  7580. lastProperty,
  7581. propertiesToCheck = {
  7582. color: 1,
  7583. background: 1,
  7584. "border-color": 1,
  7585. "border-top-color": 1,
  7586. "border-right-color": 1,
  7587. "border-bottom-color": 1,
  7588. "border-left-color": 1,
  7589. border: 1,
  7590. "border-top": 1,
  7591. "border-right": 1,
  7592. "border-bottom": 1,
  7593. "border-left": 1,
  7594. "background-color": 1
  7595. };
  7596. function startRule() {
  7597. lastProperty = null;
  7598. }
  7599. parser.addListener("startrule", startRule);
  7600. parser.addListener("startfontface", startRule);
  7601. parser.addListener("startpage", startRule);
  7602. parser.addListener("startpagemargin", startRule);
  7603. parser.addListener("startkeyframerule", startRule);
  7604. parser.addListener("startviewport", startRule);
  7605. parser.addListener("property", function(event) {
  7606. var property = event.property,
  7607. name = property.text.toLowerCase(),
  7608. parts = event.value.parts,
  7609. i = 0,
  7610. colorType = "",
  7611. len = parts.length;
  7612. if (propertiesToCheck[name]) {
  7613. while (i < len) {
  7614. if (parts[i].type === "color") {
  7615. if ("alpha" in parts[i] || "hue" in parts[i]) {
  7616. if (/([^\)]+)\(/.test(parts[i])) {
  7617. colorType = RegExp.$1.toUpperCase();
  7618. }
  7619. if (!lastProperty || (lastProperty.property.text.toLowerCase() !== name || lastProperty.colorType !== "compat")) {
  7620. reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
  7621. }
  7622. } else {
  7623. event.colorType = "compat";
  7624. }
  7625. }
  7626. i++;
  7627. }
  7628. }
  7629. lastProperty = event;
  7630. });
  7631. }
  7632. });
  7633. /*
  7634. * Rule: You shouldn't use more than 10 floats. If you do, there's probably
  7635. * room for some abstraction.
  7636. */
  7637. CSSLint.addRule({
  7638. // rule information
  7639. id: "floats",
  7640. name: "Disallow too many floats",
  7641. desc: "This rule tests if the float property is used too many times",
  7642. url: "https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats",
  7643. browsers: "All",
  7644. // initialization
  7645. init: function(parser, reporter) {
  7646. "use strict";
  7647. var rule = this;
  7648. var count = 0;
  7649. // count how many times "float" is used
  7650. parser.addListener("property", function(event) {
  7651. if (event.property.text.toLowerCase() === "float" &&
  7652. event.value.text.toLowerCase() !== "none") {
  7653. count++;
  7654. }
  7655. });
  7656. // report the results
  7657. parser.addListener("endstylesheet", function() {
  7658. reporter.stat("floats", count);
  7659. if (count >= 10) {
  7660. reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
  7661. }
  7662. });
  7663. }
  7664. });
  7665. /*
  7666. * Rule: Avoid too many @font-face declarations in the same stylesheet.
  7667. */
  7668. CSSLint.addRule({
  7669. // rule information
  7670. id: "font-faces",
  7671. name: "Don't use too many web fonts",
  7672. desc: "Too many different web fonts in the same stylesheet.",
  7673. url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts",
  7674. browsers: "All",
  7675. // initialization
  7676. init: function(parser, reporter) {
  7677. "use strict";
  7678. var rule = this,
  7679. count = 0;
  7680. parser.addListener("startfontface", function() {
  7681. count++;
  7682. });
  7683. parser.addListener("endstylesheet", function() {
  7684. if (count > 5) {
  7685. reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
  7686. }
  7687. });
  7688. }
  7689. });
  7690. /*
  7691. * Rule: You shouldn't need more than 9 font-size declarations.
  7692. */
  7693. CSSLint.addRule({
  7694. // rule information
  7695. id: "font-sizes",
  7696. name: "Disallow too many font sizes",
  7697. desc: "Checks the number of font-size declarations.",
  7698. url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-font-size-declarations",
  7699. browsers: "All",
  7700. // initialization
  7701. init: function(parser, reporter) {
  7702. "use strict";
  7703. var rule = this,
  7704. count = 0;
  7705. // check for use of "font-size"
  7706. parser.addListener("property", function(event) {
  7707. if (event.property.toString() === "font-size") {
  7708. count++;
  7709. }
  7710. });
  7711. // report the results
  7712. parser.addListener("endstylesheet", function() {
  7713. reporter.stat("font-sizes", count);
  7714. if (count >= 10) {
  7715. reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
  7716. }
  7717. });
  7718. }
  7719. });
  7720. /*
  7721. * Rule: When using a vendor-prefixed gradient, make sure to use them all.
  7722. */
  7723. CSSLint.addRule({
  7724. // rule information
  7725. id: "gradients",
  7726. name: "Require all gradient definitions",
  7727. desc: "When using a vendor-prefixed gradient, make sure to use them all.",
  7728. url: "https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions",
  7729. browsers: "All",
  7730. // initialization
  7731. init: function(parser, reporter) {
  7732. "use strict";
  7733. var rule = this,
  7734. gradients;
  7735. parser.addListener("startrule", function() {
  7736. gradients = {
  7737. moz: 0,
  7738. webkit: 0,
  7739. oldWebkit: 0,
  7740. o: 0
  7741. };
  7742. });
  7743. parser.addListener("property", function(event) {
  7744. if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)) {
  7745. gradients[RegExp.$1] = 1;
  7746. } else if (/\-webkit\-gradient/i.test(event.value)) {
  7747. gradients.oldWebkit = 1;
  7748. }
  7749. });
  7750. parser.addListener("endrule", function(event) {
  7751. var missing = [];
  7752. if (!gradients.moz) {
  7753. missing.push("Firefox 3.6+");
  7754. }
  7755. if (!gradients.webkit) {
  7756. missing.push("Webkit (Safari 5+, Chrome)");
  7757. }
  7758. if (!gradients.oldWebkit) {
  7759. missing.push("Old Webkit (Safari 4+, Chrome)");
  7760. }
  7761. if (!gradients.o) {
  7762. missing.push("Opera 11.1+");
  7763. }
  7764. if (missing.length && missing.length < 4) {
  7765. reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
  7766. }
  7767. });
  7768. }
  7769. });
  7770. /*
  7771. * Rule: Don't use IDs for selectors.
  7772. */
  7773. CSSLint.addRule({
  7774. // rule information
  7775. id: "ids",
  7776. name: "Disallow IDs in selectors",
  7777. desc: "Selectors should not contain IDs.",
  7778. url: "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors",
  7779. browsers: "All",
  7780. // initialization
  7781. init: function(parser, reporter) {
  7782. "use strict";
  7783. var rule = this;
  7784. parser.addListener("startrule", function(event) {
  7785. var selectors = event.selectors,
  7786. selector,
  7787. part,
  7788. modifier,
  7789. idCount,
  7790. i, j, k;
  7791. for (i=0; i < selectors.length; i++) {
  7792. selector = selectors[i];
  7793. idCount = 0;
  7794. for (j=0; j < selector.parts.length; j++) {
  7795. part = selector.parts[j];
  7796. if (part.type === parser.SELECTOR_PART_TYPE) {
  7797. for (k=0; k < part.modifiers.length; k++) {
  7798. modifier = part.modifiers[k];
  7799. if (modifier.type === "id") {
  7800. idCount++;
  7801. }
  7802. }
  7803. }
  7804. }
  7805. if (idCount === 1) {
  7806. reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
  7807. } else if (idCount > 1) {
  7808. reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
  7809. }
  7810. }
  7811. });
  7812. }
  7813. });
  7814. /*
  7815. * Rule: IE6-9 supports up to 31 stylesheet import.
  7816. * Reference:
  7817. * http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
  7818. */
  7819. CSSLint.addRule({
  7820. // rule information
  7821. id: "import-ie-limit",
  7822. name: "@import limit on IE6-IE9",
  7823. desc: "IE6-9 supports up to 31 @import per stylesheet",
  7824. browsers: "IE6, IE7, IE8, IE9",
  7825. // initialization
  7826. init: function(parser, reporter) {
  7827. "use strict";
  7828. var rule = this,
  7829. MAX_IMPORT_COUNT = 31,
  7830. count = 0;
  7831. function startPage() {
  7832. count = 0;
  7833. }
  7834. parser.addListener("startpage", startPage);
  7835. parser.addListener("import", function() {
  7836. count++;
  7837. });
  7838. parser.addListener("endstylesheet", function() {
  7839. if (count > MAX_IMPORT_COUNT) {
  7840. reporter.rollupError(
  7841. "Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet.",
  7842. rule
  7843. );
  7844. }
  7845. });
  7846. }
  7847. });
  7848. /*
  7849. * Rule: Don't use @import, use <link> instead.
  7850. */
  7851. CSSLint.addRule({
  7852. // rule information
  7853. id: "import",
  7854. name: "Disallow @import",
  7855. desc: "Don't use @import, use <link> instead.",
  7856. url: "https://github.com/CSSLint/csslint/wiki/Disallow-%40import",
  7857. browsers: "All",
  7858. // initialization
  7859. init: function(parser, reporter) {
  7860. "use strict";
  7861. var rule = this;
  7862. parser.addListener("import", function(event) {
  7863. reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
  7864. });
  7865. }
  7866. });
  7867. /*
  7868. * Rule: Make sure !important is not overused, this could lead to specificity
  7869. * war. Display a warning on !important declarations, an error if it's
  7870. * used more at least 10 times.
  7871. */
  7872. CSSLint.addRule({
  7873. // rule information
  7874. id: "important",
  7875. name: "Disallow !important",
  7876. desc: "Be careful when using !important declaration",
  7877. url: "https://github.com/CSSLint/csslint/wiki/Disallow-%21important",
  7878. browsers: "All",
  7879. // initialization
  7880. init: function(parser, reporter) {
  7881. "use strict";
  7882. var rule = this,
  7883. count = 0;
  7884. // warn that important is used and increment the declaration counter
  7885. parser.addListener("property", function(event) {
  7886. if (event.important === true) {
  7887. count++;
  7888. reporter.report("Use of !important", event.line, event.col, rule);
  7889. }
  7890. });
  7891. // if there are more than 10, show an error
  7892. parser.addListener("endstylesheet", function() {
  7893. reporter.stat("important", count);
  7894. if (count >= 10) {
  7895. reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
  7896. }
  7897. });
  7898. }
  7899. });
  7900. /*
  7901. * Rule: Properties should be known (listed in CSS3 specification) or
  7902. * be a vendor-prefixed property.
  7903. */
  7904. CSSLint.addRule({
  7905. // rule information
  7906. id: "known-properties",
  7907. name: "Require use of known properties",
  7908. desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
  7909. url: "https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties",
  7910. browsers: "All",
  7911. // initialization
  7912. init: function(parser, reporter) {
  7913. "use strict";
  7914. var rule = this;
  7915. parser.addListener("property", function(event) {
  7916. // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
  7917. if (event.invalid) {
  7918. reporter.report(event.invalid.message, event.line, event.col, rule);
  7919. }
  7920. });
  7921. }
  7922. });
  7923. /*
  7924. * Rule: All properties should be in alphabetical order.
  7925. */
  7926. CSSLint.addRule({
  7927. // rule information
  7928. id: "order-alphabetical",
  7929. name: "Alphabetical order",
  7930. desc: "Assure properties are in alphabetical order",
  7931. browsers: "All",
  7932. // initialization
  7933. init: function(parser, reporter) {
  7934. "use strict";
  7935. var rule = this,
  7936. properties;
  7937. var startRule = function () {
  7938. properties = [];
  7939. };
  7940. var endRule = function(event) {
  7941. var currentProperties = properties.join(","),
  7942. expectedProperties = properties.sort().join(",");
  7943. if (currentProperties !== expectedProperties) {
  7944. reporter.report("Rule doesn't have all its properties in alphabetical order.", event.line, event.col, rule);
  7945. }
  7946. };
  7947. parser.addListener("startrule", startRule);
  7948. parser.addListener("startfontface", startRule);
  7949. parser.addListener("startpage", startRule);
  7950. parser.addListener("startpagemargin", startRule);
  7951. parser.addListener("startkeyframerule", startRule);
  7952. parser.addListener("startviewport", startRule);
  7953. parser.addListener("property", function(event) {
  7954. var name = event.property.text,
  7955. lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, "");
  7956. properties.push(lowerCasePrefixLessName);
  7957. });
  7958. parser.addListener("endrule", endRule);
  7959. parser.addListener("endfontface", endRule);
  7960. parser.addListener("endpage", endRule);
  7961. parser.addListener("endpagemargin", endRule);
  7962. parser.addListener("endkeyframerule", endRule);
  7963. parser.addListener("endviewport", endRule);
  7964. }
  7965. });
  7966. /*
  7967. * Rule: outline: none or outline: 0 should only be used in a :focus rule
  7968. * and only if there are other properties in the same rule.
  7969. */
  7970. CSSLint.addRule({
  7971. // rule information
  7972. id: "outline-none",
  7973. name: "Disallow outline: none",
  7974. desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
  7975. url: "https://github.com/CSSLint/csslint/wiki/Disallow-outline%3Anone",
  7976. browsers: "All",
  7977. tags: ["Accessibility"],
  7978. // initialization
  7979. init: function(parser, reporter) {
  7980. "use strict";
  7981. var rule = this,
  7982. lastRule;
  7983. function startRule(event) {
  7984. if (event.selectors) {
  7985. lastRule = {
  7986. line: event.line,
  7987. col: event.col,
  7988. selectors: event.selectors,
  7989. propCount: 0,
  7990. outline: false
  7991. };
  7992. } else {
  7993. lastRule = null;
  7994. }
  7995. }
  7996. function endRule() {
  7997. if (lastRule) {
  7998. if (lastRule.outline) {
  7999. if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1) {
  8000. reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
  8001. } else if (lastRule.propCount === 1) {
  8002. reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
  8003. }
  8004. }
  8005. }
  8006. }
  8007. parser.addListener("startrule", startRule);
  8008. parser.addListener("startfontface", startRule);
  8009. parser.addListener("startpage", startRule);
  8010. parser.addListener("startpagemargin", startRule);
  8011. parser.addListener("startkeyframerule", startRule);
  8012. parser.addListener("startviewport", startRule);
  8013. parser.addListener("property", function(event) {
  8014. var name = event.property.text.toLowerCase(),
  8015. value = event.value;
  8016. if (lastRule) {
  8017. lastRule.propCount++;
  8018. if (name === "outline" && (value.toString() === "none" || value.toString() === "0")) {
  8019. lastRule.outline = true;
  8020. }
  8021. }
  8022. });
  8023. parser.addListener("endrule", endRule);
  8024. parser.addListener("endfontface", endRule);
  8025. parser.addListener("endpage", endRule);
  8026. parser.addListener("endpagemargin", endRule);
  8027. parser.addListener("endkeyframerule", endRule);
  8028. parser.addListener("endviewport", endRule);
  8029. }
  8030. });
  8031. /*
  8032. * Rule: Don't use classes or IDs with elements (a.foo or a#foo).
  8033. */
  8034. CSSLint.addRule({
  8035. // rule information
  8036. id: "overqualified-elements",
  8037. name: "Disallow overqualified elements",
  8038. desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
  8039. url: "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements",
  8040. browsers: "All",
  8041. // initialization
  8042. init: function(parser, reporter) {
  8043. "use strict";
  8044. var rule = this,
  8045. classes = {};
  8046. parser.addListener("startrule", function(event) {
  8047. var selectors = event.selectors,
  8048. selector,
  8049. part,
  8050. modifier,
  8051. i, j, k;
  8052. for (i=0; i < selectors.length; i++) {
  8053. selector = selectors[i];
  8054. for (j=0; j < selector.parts.length; j++) {
  8055. part = selector.parts[j];
  8056. if (part.type === parser.SELECTOR_PART_TYPE) {
  8057. for (k=0; k < part.modifiers.length; k++) {
  8058. modifier = part.modifiers[k];
  8059. if (part.elementName && modifier.type === "id") {
  8060. reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
  8061. } else if (modifier.type === "class") {
  8062. if (!classes[modifier]) {
  8063. classes[modifier] = [];
  8064. }
  8065. classes[modifier].push({
  8066. modifier: modifier,
  8067. part: part
  8068. });
  8069. }
  8070. }
  8071. }
  8072. }
  8073. }
  8074. });
  8075. parser.addListener("endstylesheet", function() {
  8076. var prop;
  8077. for (prop in classes) {
  8078. if (classes.hasOwnProperty(prop)) {
  8079. // one use means that this is overqualified
  8080. if (classes[prop].length === 1 && classes[prop][0].part.elementName) {
  8081. reporter.report("Element (" + classes[prop][0].part + ") is overqualified, just use " + classes[prop][0].modifier + " without element name.", classes[prop][0].part.line, classes[prop][0].part.col, rule);
  8082. }
  8083. }
  8084. }
  8085. });
  8086. }
  8087. });
  8088. /*
  8089. * Rule: Headings (h1-h6) should not be qualified (namespaced).
  8090. */
  8091. CSSLint.addRule({
  8092. // rule information
  8093. id: "qualified-headings",
  8094. name: "Disallow qualified headings",
  8095. desc: "Headings should not be qualified (namespaced).",
  8096. url: "https://github.com/CSSLint/csslint/wiki/Disallow-qualified-headings",
  8097. browsers: "All",
  8098. // initialization
  8099. init: function(parser, reporter) {
  8100. "use strict";
  8101. var rule = this;
  8102. parser.addListener("startrule", function(event) {
  8103. var selectors = event.selectors,
  8104. selector,
  8105. part,
  8106. i, j;
  8107. for (i=0; i < selectors.length; i++) {
  8108. selector = selectors[i];
  8109. for (j=0; j < selector.parts.length; j++) {
  8110. part = selector.parts[j];
  8111. if (part.type === parser.SELECTOR_PART_TYPE) {
  8112. if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0) {
  8113. reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
  8114. }
  8115. }
  8116. }
  8117. }
  8118. });
  8119. }
  8120. });
  8121. /*
  8122. * Rule: Selectors that look like regular expressions are slow and should be avoided.
  8123. */
  8124. CSSLint.addRule({
  8125. // rule information
  8126. id: "regex-selectors",
  8127. name: "Disallow selectors that look like regexs",
  8128. desc: "Selectors that look like regular expressions are slow and should be avoided.",
  8129. url: "https://github.com/CSSLint/csslint/wiki/Disallow-selectors-that-look-like-regular-expressions",
  8130. browsers: "All",
  8131. // initialization
  8132. init: function(parser, reporter) {
  8133. "use strict";
  8134. var rule = this;
  8135. parser.addListener("startrule", function(event) {
  8136. var selectors = event.selectors,
  8137. selector,
  8138. part,
  8139. modifier,
  8140. i, j, k;
  8141. for (i=0; i < selectors.length; i++) {
  8142. selector = selectors[i];
  8143. for (j=0; j < selector.parts.length; j++) {
  8144. part = selector.parts[j];
  8145. if (part.type === parser.SELECTOR_PART_TYPE) {
  8146. for (k=0; k < part.modifiers.length; k++) {
  8147. modifier = part.modifiers[k];
  8148. if (modifier.type === "attribute") {
  8149. if (/([~\|\^\$\*]=)/.test(modifier)) {
  8150. reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
  8151. }
  8152. }
  8153. }
  8154. }
  8155. }
  8156. }
  8157. });
  8158. }
  8159. });
  8160. /*
  8161. * Rule: Total number of rules should not exceed x.
  8162. */
  8163. CSSLint.addRule({
  8164. // rule information
  8165. id: "rules-count",
  8166. name: "Rules Count",
  8167. desc: "Track how many rules there are.",
  8168. browsers: "All",
  8169. // initialization
  8170. init: function(parser, reporter) {
  8171. "use strict";
  8172. var count = 0;
  8173. // count each rule
  8174. parser.addListener("startrule", function() {
  8175. count++;
  8176. });
  8177. parser.addListener("endstylesheet", function() {
  8178. reporter.stat("rule-count", count);
  8179. });
  8180. }
  8181. });
  8182. /*
  8183. * Rule: Warn people with approaching the IE 4095 limit
  8184. */
  8185. CSSLint.addRule({
  8186. // rule information
  8187. id: "selector-max-approaching",
  8188. name: "Warn when approaching the 4095 selector limit for IE",
  8189. desc: "Will warn when selector count is >= 3800 selectors.",
  8190. browsers: "IE",
  8191. // initialization
  8192. init: function(parser, reporter) {
  8193. "use strict";
  8194. var rule = this, count = 0;
  8195. parser.addListener("startrule", function(event) {
  8196. count += event.selectors.length;
  8197. });
  8198. parser.addListener("endstylesheet", function() {
  8199. if (count >= 3800) {
  8200. reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
  8201. }
  8202. });
  8203. }
  8204. });
  8205. /*
  8206. * Rule: Warn people past the IE 4095 limit
  8207. */
  8208. CSSLint.addRule({
  8209. // rule information
  8210. id: "selector-max",
  8211. name: "Error when past the 4095 selector limit for IE",
  8212. desc: "Will error when selector count is > 4095.",
  8213. browsers: "IE",
  8214. // initialization
  8215. init: function(parser, reporter) {
  8216. "use strict";
  8217. var rule = this, count = 0;
  8218. parser.addListener("startrule", function(event) {
  8219. count += event.selectors.length;
  8220. });
  8221. parser.addListener("endstylesheet", function() {
  8222. if (count > 4095) {
  8223. reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
  8224. }
  8225. });
  8226. }
  8227. });
  8228. /*
  8229. * Rule: Avoid new-line characters in selectors.
  8230. */
  8231. CSSLint.addRule({
  8232. // rule information
  8233. id: "selector-newline",
  8234. name: "Disallow new-line characters in selectors",
  8235. desc: "New-line characters in selectors are usually a forgotten comma and not a descendant combinator.",
  8236. browsers: "All",
  8237. // initialization
  8238. init: function(parser, reporter) {
  8239. "use strict";
  8240. var rule = this;
  8241. function startRule(event) {
  8242. var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine,
  8243. selectors = event.selectors;
  8244. for (i = 0, len = selectors.length; i < len; i++) {
  8245. selector = selectors[i];
  8246. for (p = 0, pLen = selector.parts.length; p < pLen; p++) {
  8247. for (n = p + 1; n < pLen; n++) {
  8248. part = selector.parts[p];
  8249. part2 = selector.parts[n];
  8250. type = part.type;
  8251. currentLine = part.line;
  8252. nextLine = part2.line;
  8253. if (type === "descendant" && nextLine > currentLine) {
  8254. reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule);
  8255. }
  8256. }
  8257. }
  8258. }
  8259. }
  8260. parser.addListener("startrule", startRule);
  8261. }
  8262. });
  8263. /*
  8264. * Rule: Use shorthand properties where possible.
  8265. *
  8266. */
  8267. CSSLint.addRule({
  8268. // rule information
  8269. id: "shorthand",
  8270. name: "Require shorthand properties",
  8271. desc: "Use shorthand properties where possible.",
  8272. url: "https://github.com/CSSLint/csslint/wiki/Require-shorthand-properties",
  8273. browsers: "All",
  8274. // initialization
  8275. init: function(parser, reporter) {
  8276. "use strict";
  8277. var rule = this,
  8278. prop, i, len,
  8279. propertiesToCheck = {},
  8280. properties,
  8281. mapping = {
  8282. "margin": [
  8283. "margin-top",
  8284. "margin-bottom",
  8285. "margin-left",
  8286. "margin-right"
  8287. ],
  8288. "padding": [
  8289. "padding-top",
  8290. "padding-bottom",
  8291. "padding-left",
  8292. "padding-right"
  8293. ]
  8294. };
  8295. // initialize propertiesToCheck
  8296. for (prop in mapping) {
  8297. if (mapping.hasOwnProperty(prop)) {
  8298. for (i=0, len=mapping[prop].length; i < len; i++) {
  8299. propertiesToCheck[mapping[prop][i]] = prop;
  8300. }
  8301. }
  8302. }
  8303. function startRule() {
  8304. properties = {};
  8305. }
  8306. // event handler for end of rules
  8307. function endRule(event) {
  8308. var prop, i, len, total;
  8309. // check which properties this rule has
  8310. for (prop in mapping) {
  8311. if (mapping.hasOwnProperty(prop)) {
  8312. total=0;
  8313. for (i=0, len=mapping[prop].length; i < len; i++) {
  8314. total += properties[mapping[prop][i]] ? 1 : 0;
  8315. }
  8316. if (total === mapping[prop].length) {
  8317. reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
  8318. }
  8319. }
  8320. }
  8321. }
  8322. parser.addListener("startrule", startRule);
  8323. parser.addListener("startfontface", startRule);
  8324. // check for use of "font-size"
  8325. parser.addListener("property", function(event) {
  8326. var name = event.property.toString().toLowerCase();
  8327. if (propertiesToCheck[name]) {
  8328. properties[name] = 1;
  8329. }
  8330. });
  8331. parser.addListener("endrule", endRule);
  8332. parser.addListener("endfontface", endRule);
  8333. }
  8334. });
  8335. /*
  8336. * Rule: Don't use properties with a star prefix.
  8337. *
  8338. */
  8339. CSSLint.addRule({
  8340. // rule information
  8341. id: "star-property-hack",
  8342. name: "Disallow properties with a star prefix",
  8343. desc: "Checks for the star property hack (targets IE6/7)",
  8344. url: "https://github.com/CSSLint/csslint/wiki/Disallow-star-hack",
  8345. browsers: "All",
  8346. // initialization
  8347. init: function(parser, reporter) {
  8348. "use strict";
  8349. var rule = this;
  8350. // check if property name starts with "*"
  8351. parser.addListener("property", function(event) {
  8352. var property = event.property;
  8353. if (property.hack === "*") {
  8354. reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
  8355. }
  8356. });
  8357. }
  8358. });
  8359. /*
  8360. * Rule: Don't use text-indent for image replacement if you need to support rtl.
  8361. *
  8362. */
  8363. CSSLint.addRule({
  8364. // rule information
  8365. id: "text-indent",
  8366. name: "Disallow negative text-indent",
  8367. desc: "Checks for text indent less than -99px",
  8368. url: "https://github.com/CSSLint/csslint/wiki/Disallow-negative-text-indent",
  8369. browsers: "All",
  8370. // initialization
  8371. init: function(parser, reporter) {
  8372. "use strict";
  8373. var rule = this,
  8374. textIndent,
  8375. direction;
  8376. function startRule() {
  8377. textIndent = false;
  8378. direction = "inherit";
  8379. }
  8380. // event handler for end of rules
  8381. function endRule() {
  8382. if (textIndent && direction !== "ltr") {
  8383. reporter.report("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.", textIndent.line, textIndent.col, rule);
  8384. }
  8385. }
  8386. parser.addListener("startrule", startRule);
  8387. parser.addListener("startfontface", startRule);
  8388. // check for use of "font-size"
  8389. parser.addListener("property", function(event) {
  8390. var name = event.property.toString().toLowerCase(),
  8391. value = event.value;
  8392. if (name === "text-indent" && value.parts[0].value < -99) {
  8393. textIndent = event.property;
  8394. } else if (name === "direction" && value.toString() === "ltr") {
  8395. direction = "ltr";
  8396. }
  8397. });
  8398. parser.addListener("endrule", endRule);
  8399. parser.addListener("endfontface", endRule);
  8400. }
  8401. });
  8402. /*
  8403. * Rule: Don't use properties with a underscore prefix.
  8404. *
  8405. */
  8406. CSSLint.addRule({
  8407. // rule information
  8408. id: "underscore-property-hack",
  8409. name: "Disallow properties with an underscore prefix",
  8410. desc: "Checks for the underscore property hack (targets IE6)",
  8411. url: "https://github.com/CSSLint/csslint/wiki/Disallow-underscore-hack",
  8412. browsers: "All",
  8413. // initialization
  8414. init: function(parser, reporter) {
  8415. "use strict";
  8416. var rule = this;
  8417. // check if property name starts with "_"
  8418. parser.addListener("property", function(event) {
  8419. var property = event.property;
  8420. if (property.hack === "_") {
  8421. reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
  8422. }
  8423. });
  8424. }
  8425. });
  8426. /*
  8427. * Rule: Headings (h1-h6) should be defined only once.
  8428. */
  8429. CSSLint.addRule({
  8430. // rule information
  8431. id: "unique-headings",
  8432. name: "Headings should only be defined once",
  8433. desc: "Headings should be defined only once.",
  8434. url: "https://github.com/CSSLint/csslint/wiki/Headings-should-only-be-defined-once",
  8435. browsers: "All",
  8436. // initialization
  8437. init: function(parser, reporter) {
  8438. "use strict";
  8439. var rule = this;
  8440. var headings = {
  8441. h1: 0,
  8442. h2: 0,
  8443. h3: 0,
  8444. h4: 0,
  8445. h5: 0,
  8446. h6: 0
  8447. };
  8448. parser.addListener("startrule", function(event) {
  8449. var selectors = event.selectors,
  8450. selector,
  8451. part,
  8452. pseudo,
  8453. i, j;
  8454. for (i=0; i < selectors.length; i++) {
  8455. selector = selectors[i];
  8456. part = selector.parts[selector.parts.length-1];
  8457. if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())) {
  8458. for (j=0; j < part.modifiers.length; j++) {
  8459. if (part.modifiers[j].type === "pseudo") {
  8460. pseudo = true;
  8461. break;
  8462. }
  8463. }
  8464. if (!pseudo) {
  8465. headings[RegExp.$1]++;
  8466. if (headings[RegExp.$1] > 1) {
  8467. reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
  8468. }
  8469. }
  8470. }
  8471. }
  8472. });
  8473. parser.addListener("endstylesheet", function() {
  8474. var prop,
  8475. messages = [];
  8476. for (prop in headings) {
  8477. if (headings.hasOwnProperty(prop)) {
  8478. if (headings[prop] > 1) {
  8479. messages.push(headings[prop] + " " + prop + "s");
  8480. }
  8481. }
  8482. }
  8483. if (messages.length) {
  8484. reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
  8485. }
  8486. });
  8487. }
  8488. });
  8489. /*
  8490. * Rule: Don't use universal selector because it's slow.
  8491. */
  8492. CSSLint.addRule({
  8493. // rule information
  8494. id: "universal-selector",
  8495. name: "Disallow universal selector",
  8496. desc: "The universal selector (*) is known to be slow.",
  8497. url: "https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector",
  8498. browsers: "All",
  8499. // initialization
  8500. init: function(parser, reporter) {
  8501. "use strict";
  8502. var rule = this;
  8503. parser.addListener("startrule", function(event) {
  8504. var selectors = event.selectors,
  8505. selector,
  8506. part,
  8507. i;
  8508. for (i=0; i < selectors.length; i++) {
  8509. selector = selectors[i];
  8510. part = selector.parts[selector.parts.length-1];
  8511. if (part.elementName === "*") {
  8512. reporter.report(rule.desc, part.line, part.col, rule);
  8513. }
  8514. }
  8515. });
  8516. }
  8517. });
  8518. /*
  8519. * Rule: Don't use unqualified attribute selectors because they're just like universal selectors.
  8520. */
  8521. CSSLint.addRule({
  8522. // rule information
  8523. id: "unqualified-attributes",
  8524. name: "Disallow unqualified attribute selectors",
  8525. desc: "Unqualified attribute selectors are known to be slow.",
  8526. url: "https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors",
  8527. browsers: "All",
  8528. // initialization
  8529. init: function(parser, reporter) {
  8530. "use strict";
  8531. var rule = this;
  8532. parser.addListener("startrule", function(event) {
  8533. var selectors = event.selectors,
  8534. selectorContainsClassOrId = false,
  8535. selector,
  8536. part,
  8537. modifier,
  8538. i, k;
  8539. for (i=0; i < selectors.length; i++) {
  8540. selector = selectors[i];
  8541. part = selector.parts[selector.parts.length-1];
  8542. if (part.type === parser.SELECTOR_PART_TYPE) {
  8543. for (k=0; k < part.modifiers.length; k++) {
  8544. modifier = part.modifiers[k];
  8545. if (modifier.type === "class" || modifier.type === "id") {
  8546. selectorContainsClassOrId = true;
  8547. break;
  8548. }
  8549. }
  8550. if (!selectorContainsClassOrId) {
  8551. for (k=0; k < part.modifiers.length; k++) {
  8552. modifier = part.modifiers[k];
  8553. if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")) {
  8554. reporter.report(rule.desc, part.line, part.col, rule);
  8555. }
  8556. }
  8557. }
  8558. }
  8559. }
  8560. });
  8561. }
  8562. });
  8563. /*
  8564. * Rule: When using a vendor-prefixed property, make sure to
  8565. * include the standard one.
  8566. */
  8567. CSSLint.addRule({
  8568. // rule information
  8569. id: "vendor-prefix",
  8570. name: "Require standard property with vendor prefix",
  8571. desc: "When using a vendor-prefixed property, make sure to include the standard one.",
  8572. url: "https://github.com/CSSLint/csslint/wiki/Require-standard-property-with-vendor-prefix",
  8573. browsers: "All",
  8574. // initialization
  8575. init: function(parser, reporter) {
  8576. "use strict";
  8577. var rule = this,
  8578. properties,
  8579. num,
  8580. propertiesToCheck = {
  8581. "-webkit-border-radius": "border-radius",
  8582. "-webkit-border-top-left-radius": "border-top-left-radius",
  8583. "-webkit-border-top-right-radius": "border-top-right-radius",
  8584. "-webkit-border-bottom-left-radius": "border-bottom-left-radius",
  8585. "-webkit-border-bottom-right-radius": "border-bottom-right-radius",
  8586. "-o-border-radius": "border-radius",
  8587. "-o-border-top-left-radius": "border-top-left-radius",
  8588. "-o-border-top-right-radius": "border-top-right-radius",
  8589. "-o-border-bottom-left-radius": "border-bottom-left-radius",
  8590. "-o-border-bottom-right-radius": "border-bottom-right-radius",
  8591. "-moz-border-radius": "border-radius",
  8592. "-moz-border-radius-topleft": "border-top-left-radius",
  8593. "-moz-border-radius-topright": "border-top-right-radius",
  8594. "-moz-border-radius-bottomleft": "border-bottom-left-radius",
  8595. "-moz-border-radius-bottomright": "border-bottom-right-radius",
  8596. "-moz-column-count": "column-count",
  8597. "-webkit-column-count": "column-count",
  8598. "-moz-column-gap": "column-gap",
  8599. "-webkit-column-gap": "column-gap",
  8600. "-moz-column-rule": "column-rule",
  8601. "-webkit-column-rule": "column-rule",
  8602. "-moz-column-rule-style": "column-rule-style",
  8603. "-webkit-column-rule-style": "column-rule-style",
  8604. "-moz-column-rule-color": "column-rule-color",
  8605. "-webkit-column-rule-color": "column-rule-color",
  8606. "-moz-column-rule-width": "column-rule-width",
  8607. "-webkit-column-rule-width": "column-rule-width",
  8608. "-moz-column-width": "column-width",
  8609. "-webkit-column-width": "column-width",
  8610. "-webkit-column-span": "column-span",
  8611. "-webkit-columns": "columns",
  8612. "-moz-box-shadow": "box-shadow",
  8613. "-webkit-box-shadow": "box-shadow",
  8614. "-moz-transform": "transform",
  8615. "-webkit-transform": "transform",
  8616. "-o-transform": "transform",
  8617. "-ms-transform": "transform",
  8618. "-moz-transform-origin": "transform-origin",
  8619. "-webkit-transform-origin": "transform-origin",
  8620. "-o-transform-origin": "transform-origin",
  8621. "-ms-transform-origin": "transform-origin",
  8622. "-moz-box-sizing": "box-sizing",
  8623. "-webkit-box-sizing": "box-sizing"
  8624. };
  8625. // event handler for beginning of rules
  8626. function startRule() {
  8627. properties = {};
  8628. num = 1;
  8629. }
  8630. // event handler for end of rules
  8631. function endRule() {
  8632. var prop,
  8633. i,
  8634. len,
  8635. needed,
  8636. actual,
  8637. needsStandard = [];
  8638. for (prop in properties) {
  8639. if (propertiesToCheck[prop]) {
  8640. needsStandard.push({
  8641. actual: prop,
  8642. needed: propertiesToCheck[prop]
  8643. });
  8644. }
  8645. }
  8646. for (i=0, len=needsStandard.length; i < len; i++) {
  8647. needed = needsStandard[i].needed;
  8648. actual = needsStandard[i].actual;
  8649. if (!properties[needed]) {
  8650. reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
  8651. } else {
  8652. // make sure standard property is last
  8653. if (properties[needed][0].pos < properties[actual][0].pos) {
  8654. reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
  8655. }
  8656. }
  8657. }
  8658. }
  8659. parser.addListener("startrule", startRule);
  8660. parser.addListener("startfontface", startRule);
  8661. parser.addListener("startpage", startRule);
  8662. parser.addListener("startpagemargin", startRule);
  8663. parser.addListener("startkeyframerule", startRule);
  8664. parser.addListener("startviewport", startRule);
  8665. parser.addListener("property", function(event) {
  8666. var name = event.property.text.toLowerCase();
  8667. if (!properties[name]) {
  8668. properties[name] = [];
  8669. }
  8670. properties[name].push({
  8671. name: event.property,
  8672. value: event.value,
  8673. pos: num++
  8674. });
  8675. });
  8676. parser.addListener("endrule", endRule);
  8677. parser.addListener("endfontface", endRule);
  8678. parser.addListener("endpage", endRule);
  8679. parser.addListener("endpagemargin", endRule);
  8680. parser.addListener("endkeyframerule", endRule);
  8681. parser.addListener("endviewport", endRule);
  8682. }
  8683. });
  8684. /*
  8685. * Rule: You don't need to specify units when a value is 0.
  8686. */
  8687. CSSLint.addRule({
  8688. // rule information
  8689. id: "zero-units",
  8690. name: "Disallow units for 0 values",
  8691. desc: "You don't need to specify units when a value is 0.",
  8692. url: "https://github.com/CSSLint/csslint/wiki/Disallow-units-for-zero-values",
  8693. browsers: "All",
  8694. // initialization
  8695. init: function(parser, reporter) {
  8696. "use strict";
  8697. var rule = this;
  8698. // count how many times "float" is used
  8699. parser.addListener("property", function(event) {
  8700. var parts = event.value.parts,
  8701. i = 0,
  8702. len = parts.length;
  8703. while (i < len) {
  8704. if ((parts[i].units || parts[i].type === "percentage") && parts[i].value === 0 && parts[i].type !== "time") {
  8705. reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
  8706. }
  8707. i++;
  8708. }
  8709. });
  8710. }
  8711. });
  8712. (function() {
  8713. "use strict";
  8714. /**
  8715. * Replace special characters before write to output.
  8716. *
  8717. * Rules:
  8718. * - single quotes is the escape sequence for double-quotes
  8719. * - &amp; is the escape sequence for &
  8720. * - &lt; is the escape sequence for <
  8721. * - &gt; is the escape sequence for >
  8722. *
  8723. * @param {String} message to escape
  8724. * @return escaped message as {String}
  8725. */
  8726. var xmlEscape = function(str) {
  8727. if (!str || str.constructor !== String) {
  8728. return "";
  8729. }
  8730. return str.replace(/["&><]/g, function(match) {
  8731. switch (match) {
  8732. case "\"":
  8733. return "&quot;";
  8734. case "&":
  8735. return "&amp;";
  8736. case "<":
  8737. return "&lt;";
  8738. case ">":
  8739. return "&gt;";
  8740. }
  8741. });
  8742. };
  8743. CSSLint.addFormatter({
  8744. // format information
  8745. id: "checkstyle-xml",
  8746. name: "Checkstyle XML format",
  8747. /**
  8748. * Return opening root XML tag.
  8749. * @return {String} to prepend before all results
  8750. */
  8751. startFormat: function() {
  8752. return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
  8753. },
  8754. /**
  8755. * Return closing root XML tag.
  8756. * @return {String} to append after all results
  8757. */
  8758. endFormat: function() {
  8759. return "</checkstyle>";
  8760. },
  8761. /**
  8762. * Returns message when there is a file read error.
  8763. * @param {String} filename The name of the file that caused the error.
  8764. * @param {String} message The error message
  8765. * @return {String} The error message.
  8766. */
  8767. readError: function(filename, message) {
  8768. return "<file name=\"" + xmlEscape(filename) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape(message) + "\"></error></file>";
  8769. },
  8770. /**
  8771. * Given CSS Lint results for a file, return output for this format.
  8772. * @param results {Object} with error and warning messages
  8773. * @param filename {String} relative file path
  8774. * @param options {Object} (UNUSED for now) specifies special handling of output
  8775. * @return {String} output for results
  8776. */
  8777. formatResults: function(results, filename/*, options*/) {
  8778. var messages = results.messages,
  8779. output = [];
  8780. /**
  8781. * Generate a source string for a rule.
  8782. * Checkstyle source strings usually resemble Java class names e.g
  8783. * net.csslint.SomeRuleName
  8784. * @param {Object} rule
  8785. * @return rule source as {String}
  8786. */
  8787. var generateSource = function(rule) {
  8788. if (!rule || !("name" in rule)) {
  8789. return "";
  8790. }
  8791. return "net.csslint." + rule.name.replace(/\s/g, "");
  8792. };
  8793. if (messages.length > 0) {
  8794. output.push("<file name=\""+filename+"\">");
  8795. CSSLint.Util.forEach(messages, function (message) {
  8796. // ignore rollups for now
  8797. if (!message.rollup) {
  8798. output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
  8799. " message=\"" + xmlEscape(message.message) + "\" source=\"" + generateSource(message.rule) +"\"/>");
  8800. }
  8801. });
  8802. output.push("</file>");
  8803. }
  8804. return output.join("");
  8805. }
  8806. });
  8807. }());
  8808. CSSLint.addFormatter({
  8809. // format information
  8810. id: "compact",
  8811. name: "Compact, 'porcelain' format",
  8812. /**
  8813. * Return content to be printed before all file results.
  8814. * @return {String} to prepend before all results
  8815. */
  8816. startFormat: function() {
  8817. "use strict";
  8818. return "";
  8819. },
  8820. /**
  8821. * Return content to be printed after all file results.
  8822. * @return {String} to append after all results
  8823. */
  8824. endFormat: function() {
  8825. "use strict";
  8826. return "";
  8827. },
  8828. /**
  8829. * Given CSS Lint results for a file, return output for this format.
  8830. * @param results {Object} with error and warning messages
  8831. * @param filename {String} relative file path
  8832. * @param options {Object} (Optional) specifies special handling of output
  8833. * @return {String} output for results
  8834. */
  8835. formatResults: function(results, filename, options) {
  8836. "use strict";
  8837. var messages = results.messages,
  8838. output = "";
  8839. options = options || {};
  8840. /**
  8841. * Capitalize and return given string.
  8842. * @param str {String} to capitalize
  8843. * @return {String} capitalized
  8844. */
  8845. var capitalize = function(str) {
  8846. return str.charAt(0).toUpperCase() + str.slice(1);
  8847. };
  8848. if (messages.length === 0) {
  8849. return options.quiet ? "" : filename + ": Lint Free!";
  8850. }
  8851. CSSLint.Util.forEach(messages, function(message) {
  8852. if (message.rollup) {
  8853. output += filename + ": " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
  8854. } else {
  8855. output += filename + ": line " + message.line +
  8856. ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
  8857. }
  8858. });
  8859. return output;
  8860. }
  8861. });
  8862. CSSLint.addFormatter({
  8863. // format information
  8864. id: "csslint-xml",
  8865. name: "CSSLint XML format",
  8866. /**
  8867. * Return opening root XML tag.
  8868. * @return {String} to prepend before all results
  8869. */
  8870. startFormat: function() {
  8871. "use strict";
  8872. return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
  8873. },
  8874. /**
  8875. * Return closing root XML tag.
  8876. * @return {String} to append after all results
  8877. */
  8878. endFormat: function() {
  8879. "use strict";
  8880. return "</csslint>";
  8881. },
  8882. /**
  8883. * Given CSS Lint results for a file, return output for this format.
  8884. * @param results {Object} with error and warning messages
  8885. * @param filename {String} relative file path
  8886. * @param options {Object} (UNUSED for now) specifies special handling of output
  8887. * @return {String} output for results
  8888. */
  8889. formatResults: function(results, filename/*, options*/) {
  8890. "use strict";
  8891. var messages = results.messages,
  8892. output = [];
  8893. /**
  8894. * Replace special characters before write to output.
  8895. *
  8896. * Rules:
  8897. * - single quotes is the escape sequence for double-quotes
  8898. * - &amp; is the escape sequence for &
  8899. * - &lt; is the escape sequence for <
  8900. * - &gt; is the escape sequence for >
  8901. *
  8902. * @param {String} message to escape
  8903. * @return escaped message as {String}
  8904. */
  8905. var escapeSpecialCharacters = function(str) {
  8906. if (!str || str.constructor !== String) {
  8907. return "";
  8908. }
  8909. return str.replace(/"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  8910. };
  8911. if (messages.length > 0) {
  8912. output.push("<file name=\""+filename+"\">");
  8913. CSSLint.Util.forEach(messages, function (message) {
  8914. if (message.rollup) {
  8915. output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
  8916. } else {
  8917. output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
  8918. " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
  8919. }
  8920. });
  8921. output.push("</file>");
  8922. }
  8923. return output.join("");
  8924. }
  8925. });
  8926. /* globals JSON: true */
  8927. CSSLint.addFormatter({
  8928. // format information
  8929. id: "json",
  8930. name: "JSON",
  8931. /**
  8932. * Return content to be printed before all file results.
  8933. * @return {String} to prepend before all results
  8934. */
  8935. startFormat: function() {
  8936. "use strict";
  8937. this.json = [];
  8938. return "";
  8939. },
  8940. /**
  8941. * Return content to be printed after all file results.
  8942. * @return {String} to append after all results
  8943. */
  8944. endFormat: function() {
  8945. "use strict";
  8946. var ret = "";
  8947. if (this.json.length > 0) {
  8948. if (this.json.length === 1) {
  8949. ret = JSON.stringify(this.json[0]);
  8950. } else {
  8951. ret = JSON.stringify(this.json);
  8952. }
  8953. }
  8954. return ret;
  8955. },
  8956. /**
  8957. * Given CSS Lint results for a file, return output for this format.
  8958. * @param results {Object} with error and warning messages
  8959. * @param filename {String} relative file path (Unused)
  8960. * @return {String} output for results
  8961. */
  8962. formatResults: function(results, filename, options) {
  8963. "use strict";
  8964. if (results.messages.length > 0 || !options.quiet) {
  8965. this.json.push({
  8966. filename: filename,
  8967. messages: results.messages,
  8968. stats: results.stats
  8969. });
  8970. }
  8971. return "";
  8972. }
  8973. });
  8974. CSSLint.addFormatter({
  8975. // format information
  8976. id: "junit-xml",
  8977. name: "JUNIT XML format",
  8978. /**
  8979. * Return opening root XML tag.
  8980. * @return {String} to prepend before all results
  8981. */
  8982. startFormat: function() {
  8983. "use strict";
  8984. return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>";
  8985. },
  8986. /**
  8987. * Return closing root XML tag.
  8988. * @return {String} to append after all results
  8989. */
  8990. endFormat: function() {
  8991. "use strict";
  8992. return "</testsuites>";
  8993. },
  8994. /**
  8995. * Given CSS Lint results for a file, return output for this format.
  8996. * @param results {Object} with error and warning messages
  8997. * @param filename {String} relative file path
  8998. * @param options {Object} (UNUSED for now) specifies special handling of output
  8999. * @return {String} output for results
  9000. */
  9001. formatResults: function(results, filename/*, options*/) {
  9002. "use strict";
  9003. var messages = results.messages,
  9004. output = [],
  9005. tests = {
  9006. "error": 0,
  9007. "failure": 0
  9008. };
  9009. /**
  9010. * Generate a source string for a rule.
  9011. * JUNIT source strings usually resemble Java class names e.g
  9012. * net.csslint.SomeRuleName
  9013. * @param {Object} rule
  9014. * @return rule source as {String}
  9015. */
  9016. var generateSource = function(rule) {
  9017. if (!rule || !("name" in rule)) {
  9018. return "";
  9019. }
  9020. return "net.csslint." + rule.name.replace(/\s/g, "");
  9021. };
  9022. /**
  9023. * Replace special characters before write to output.
  9024. *
  9025. * Rules:
  9026. * - single quotes is the escape sequence for double-quotes
  9027. * - &lt; is the escape sequence for <
  9028. * - &gt; is the escape sequence for >
  9029. *
  9030. * @param {String} message to escape
  9031. * @return escaped message as {String}
  9032. */
  9033. var escapeSpecialCharacters = function(str) {
  9034. if (!str || str.constructor !== String) {
  9035. return "";
  9036. }
  9037. return str.replace(/"/g, "'").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  9038. };
  9039. if (messages.length > 0) {
  9040. messages.forEach(function (message) {
  9041. // since junit has no warning class
  9042. // all issues as errors
  9043. var type = message.type === "warning" ? "error" : message.type;
  9044. // ignore rollups for now
  9045. if (!message.rollup) {
  9046. // build the test case separately, once joined
  9047. // we'll add it to a custom array filtered by type
  9048. output.push("<testcase time=\"0\" name=\"" + generateSource(message.rule) + "\">");
  9049. output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\"><![CDATA[" + message.line + ":" + message.col + ":" + escapeSpecialCharacters(message.evidence) + "]]></" + type + ">");
  9050. output.push("</testcase>");
  9051. tests[type] += 1;
  9052. }
  9053. });
  9054. output.unshift("<testsuite time=\"0\" tests=\"" + messages.length + "\" skipped=\"0\" errors=\"" + tests.error + "\" failures=\"" + tests.failure + "\" package=\"net.csslint\" name=\"" + filename + "\">");
  9055. output.push("</testsuite>");
  9056. }
  9057. return output.join("");
  9058. }
  9059. });
  9060. CSSLint.addFormatter({
  9061. // format information
  9062. id: "lint-xml",
  9063. name: "Lint XML format",
  9064. /**
  9065. * Return opening root XML tag.
  9066. * @return {String} to prepend before all results
  9067. */
  9068. startFormat: function() {
  9069. "use strict";
  9070. return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
  9071. },
  9072. /**
  9073. * Return closing root XML tag.
  9074. * @return {String} to append after all results
  9075. */
  9076. endFormat: function() {
  9077. "use strict";
  9078. return "</lint>";
  9079. },
  9080. /**
  9081. * Given CSS Lint results for a file, return output for this format.
  9082. * @param results {Object} with error and warning messages
  9083. * @param filename {String} relative file path
  9084. * @param options {Object} (UNUSED for now) specifies special handling of output
  9085. * @return {String} output for results
  9086. */
  9087. formatResults: function(results, filename/*, options*/) {
  9088. "use strict";
  9089. var messages = results.messages,
  9090. output = [];
  9091. /**
  9092. * Replace special characters before write to output.
  9093. *
  9094. * Rules:
  9095. * - single quotes is the escape sequence for double-quotes
  9096. * - &amp; is the escape sequence for &
  9097. * - &lt; is the escape sequence for <
  9098. * - &gt; is the escape sequence for >
  9099. *
  9100. * @param {String} message to escape
  9101. * @return escaped message as {String}
  9102. */
  9103. var escapeSpecialCharacters = function(str) {
  9104. if (!str || str.constructor !== String) {
  9105. return "";
  9106. }
  9107. return str.replace(/"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  9108. };
  9109. if (messages.length > 0) {
  9110. output.push("<file name=\""+filename+"\">");
  9111. CSSLint.Util.forEach(messages, function (message) {
  9112. if (message.rollup) {
  9113. output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
  9114. } else {
  9115. var rule = "";
  9116. if (message.rule && message.rule.id) {
  9117. rule = "rule=\"" + escapeSpecialCharacters(message.rule.id) + "\" ";
  9118. }
  9119. output.push("<issue " + rule + "line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
  9120. " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
  9121. }
  9122. });
  9123. output.push("</file>");
  9124. }
  9125. return output.join("");
  9126. }
  9127. });
  9128. CSSLint.addFormatter({
  9129. // format information
  9130. id: "text",
  9131. name: "Plain Text",
  9132. /**
  9133. * Return content to be printed before all file results.
  9134. * @return {String} to prepend before all results
  9135. */
  9136. startFormat: function() {
  9137. "use strict";
  9138. return "";
  9139. },
  9140. /**
  9141. * Return content to be printed after all file results.
  9142. * @return {String} to append after all results
  9143. */
  9144. endFormat: function() {
  9145. "use strict";
  9146. return "";
  9147. },
  9148. /**
  9149. * Given CSS Lint results for a file, return output for this format.
  9150. * @param results {Object} with error and warning messages
  9151. * @param filename {String} relative file path
  9152. * @param options {Object} (Optional) specifies special handling of output
  9153. * @return {String} output for results
  9154. */
  9155. formatResults: function(results, filename, options) {
  9156. "use strict";
  9157. var messages = results.messages,
  9158. output = "";
  9159. options = options || {};
  9160. if (messages.length === 0) {
  9161. return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
  9162. }
  9163. output = "\n\ncsslint: There ";
  9164. if (messages.length === 1) {
  9165. output += "is 1 problem";
  9166. } else {
  9167. output += "are " + messages.length + " problems";
  9168. }
  9169. output += " in " + filename + ".";
  9170. var pos = filename.lastIndexOf("/"),
  9171. shortFilename = filename;
  9172. if (pos === -1) {
  9173. pos = filename.lastIndexOf("\\");
  9174. }
  9175. if (pos > -1) {
  9176. shortFilename = filename.substring(pos+1);
  9177. }
  9178. CSSLint.Util.forEach(messages, function (message, i) {
  9179. output = output + "\n\n" + shortFilename;
  9180. if (message.rollup) {
  9181. output += "\n" + (i+1) + ": " + message.type;
  9182. output += "\n" + message.message;
  9183. } else {
  9184. output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
  9185. output += "\n" + message.message;
  9186. output += "\n" + message.evidence;
  9187. }
  9188. });
  9189. return output;
  9190. }
  9191. });
  9192. /*
  9193. * Web worker for CSSLint
  9194. */
  9195. /* global self, JSON */
  9196. // message indicates to start linting
  9197. self.onmessage = function(event) {
  9198. "use strict";
  9199. var data = event.data,
  9200. message,
  9201. text,
  9202. ruleset,
  9203. results;
  9204. try {
  9205. message = JSON.parse(data);
  9206. text = message.text;
  9207. ruleset = message.ruleset;
  9208. } catch (ex) {
  9209. text = data;
  9210. }
  9211. results = CSSLint.verify(text, ruleset);
  9212. // Not all browsers support structured clone, so JSON stringify results
  9213. self.postMessage(JSON.stringify(results));
  9214. };