You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

worker-css.js 368KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898
  1. "no use strict";
  2. var console = {
  3. log: function(msg) {
  4. postMessage({type: "log", data: msg});
  5. }
  6. };
  7. var window = {
  8. console: console
  9. };
  10. var normalizeModule = function(parentId, moduleName) {
  11. // normalize plugin requires
  12. if (moduleName.indexOf("!") !== -1) {
  13. var chunks = moduleName.split("!");
  14. return normalizeModule(parentId, chunks[0]) + "!" + normalizeModule(parentId, chunks[1]);
  15. }
  16. // normalize relative requires
  17. if (moduleName.charAt(0) == ".") {
  18. var base = parentId.split("/").slice(0, -1).join("/");
  19. var moduleName = base + "/" + moduleName;
  20. while(moduleName.indexOf(".") !== -1 && previous != moduleName) {
  21. var previous = moduleName;
  22. var moduleName = moduleName.replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, "");
  23. }
  24. }
  25. return moduleName;
  26. };
  27. var require = function(parentId, id) {
  28. var id = normalizeModule(parentId, id);
  29. var module = require.modules[id];
  30. if (module) {
  31. if (!module.initialized) {
  32. module.exports = module.factory().exports;
  33. module.initialized = true;
  34. }
  35. return module.exports;
  36. }
  37. var chunks = id.split("/");
  38. chunks[0] = require.tlns[chunks[0]] || chunks[0];
  39. var path = chunks.join("/") + ".js";
  40. require.id = id;
  41. importScripts(path);
  42. return require(parentId, id);
  43. };
  44. require.modules = {};
  45. require.tlns = {};
  46. var define = function(id, deps, factory) {
  47. if (arguments.length == 2) {
  48. factory = deps;
  49. } else if (arguments.length == 1) {
  50. factory = id;
  51. id = require.id;
  52. }
  53. if (id.indexOf("text!") === 0)
  54. return;
  55. var req = function(deps, factory) {
  56. return require(id, deps, factory);
  57. };
  58. require.modules[id] = {
  59. factory: function() {
  60. var module = {
  61. exports: {}
  62. };
  63. var returnExports = factory(req, module.exports, module);
  64. if (returnExports)
  65. module.exports = returnExports;
  66. return module;
  67. }
  68. };
  69. };
  70. function initBaseUrls(topLevelNamespaces) {
  71. require.tlns = topLevelNamespaces;
  72. }
  73. function initSender() {
  74. var EventEmitter = require(null, "ace/lib/event_emitter").EventEmitter;
  75. var oop = require(null, "ace/lib/oop");
  76. var Sender = function() {};
  77. (function() {
  78. oop.implement(this, EventEmitter);
  79. this.callback = function(data, callbackId) {
  80. postMessage({
  81. type: "call",
  82. id: callbackId,
  83. data: data
  84. });
  85. };
  86. this.emit = function(name, data) {
  87. postMessage({
  88. type: "event",
  89. name: name,
  90. data: data
  91. });
  92. };
  93. }).call(Sender.prototype);
  94. return new Sender();
  95. }
  96. var main;
  97. var sender;
  98. onmessage = function(e) {
  99. var msg = e.data;
  100. if (msg.command) {
  101. main[msg.command].apply(main, msg.args);
  102. }
  103. else if (msg.init) {
  104. initBaseUrls(msg.tlns);
  105. require(null, "ace/lib/fixoldbrowsers");
  106. sender = initSender();
  107. var clazz = require(null, msg.module)[msg.classname];
  108. main = new clazz(sender);
  109. }
  110. else if (msg.event && sender) {
  111. sender._emit(msg.event, msg.data);
  112. }
  113. };
  114. // vim:set ts=4 sts=4 sw=4 st:
  115. // -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
  116. // -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project)
  117. // -- dantman Daniel Friesen Copyright(C) 2010 XXX No License Specified
  118. // -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License
  119. // -- Irakli Gozalishvili Copyright (C) 2010 MIT License
  120. /*!
  121. Copyright (c) 2009, 280 North Inc. http://280north.com/
  122. MIT License. http://github.com/280north/narwhal/blob/master/README.md
  123. */
  124. define('ace/lib/fixoldbrowsers', ['require', 'exports', 'module' , 'ace/lib/regexp', 'ace/lib/es5-shim'], function(require, exports, module) {
  125. require("./regexp");
  126. require("./es5-shim");
  127. });
  128. define('ace/lib/regexp', ['require', 'exports', 'module' ], function(require, exports, module) {
  129. //---------------------------------
  130. // Private variables
  131. //---------------------------------
  132. var real = {
  133. exec: RegExp.prototype.exec,
  134. test: RegExp.prototype.test,
  135. match: String.prototype.match,
  136. replace: String.prototype.replace,
  137. split: String.prototype.split
  138. },
  139. compliantExecNpcg = real.exec.call(/()??/, "")[1] === undefined, // check `exec` handling of nonparticipating capturing groups
  140. compliantLastIndexIncrement = function () {
  141. var x = /^/g;
  142. real.test.call(x, "");
  143. return !x.lastIndex;
  144. }();
  145. if (compliantLastIndexIncrement && compliantExecNpcg)
  146. return;
  147. //---------------------------------
  148. // Overriden native methods
  149. //---------------------------------
  150. // Adds named capture support (with backreferences returned as `result.name`), and fixes two
  151. // cross-browser issues per ES3:
  152. // - Captured values for nonparticipating capturing groups should be returned as `undefined`,
  153. // rather than the empty string.
  154. // - `lastIndex` should not be incremented after zero-length matches.
  155. RegExp.prototype.exec = function (str) {
  156. var match = real.exec.apply(this, arguments),
  157. name, r2;
  158. if ( typeof(str) == 'string' && match) {
  159. // Fix browsers whose `exec` methods don't consistently return `undefined` for
  160. // nonparticipating capturing groups
  161. if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) {
  162. r2 = RegExp(this.source, real.replace.call(getNativeFlags(this), "g", ""));
  163. // Using `str.slice(match.index)` rather than `match[0]` in case lookahead allowed
  164. // matching due to characters outside the match
  165. real.replace.call(str.slice(match.index), r2, function () {
  166. for (var i = 1; i < arguments.length - 2; i++) {
  167. if (arguments[i] === undefined)
  168. match[i] = undefined;
  169. }
  170. });
  171. }
  172. // Attach named capture properties
  173. if (this._xregexp && this._xregexp.captureNames) {
  174. for (var i = 1; i < match.length; i++) {
  175. name = this._xregexp.captureNames[i - 1];
  176. if (name)
  177. match[name] = match[i];
  178. }
  179. }
  180. // Fix browsers that increment `lastIndex` after zero-length matches
  181. if (!compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index))
  182. this.lastIndex--;
  183. }
  184. return match;
  185. };
  186. // Don't override `test` if it won't change anything
  187. if (!compliantLastIndexIncrement) {
  188. // Fix browser bug in native method
  189. RegExp.prototype.test = function (str) {
  190. // Use the native `exec` to skip some processing overhead, even though the overriden
  191. // `exec` would take care of the `lastIndex` fix
  192. var match = real.exec.call(this, str);
  193. // Fix browsers that increment `lastIndex` after zero-length matches
  194. if (match && this.global && !match[0].length && (this.lastIndex > match.index))
  195. this.lastIndex--;
  196. return !!match;
  197. };
  198. }
  199. //---------------------------------
  200. // Private helper functions
  201. //---------------------------------
  202. function getNativeFlags (regex) {
  203. return (regex.global ? "g" : "") +
  204. (regex.ignoreCase ? "i" : "") +
  205. (regex.multiline ? "m" : "") +
  206. (regex.extended ? "x" : "") + // Proposed for ES4; included in AS3
  207. (regex.sticky ? "y" : "");
  208. };
  209. function indexOf (array, item, from) {
  210. if (Array.prototype.indexOf) // Use the native array method if available
  211. return array.indexOf(item, from);
  212. for (var i = from || 0; i < array.length; i++) {
  213. if (array[i] === item)
  214. return i;
  215. }
  216. return -1;
  217. };
  218. });
  219. // vim: ts=4 sts=4 sw=4 expandtab
  220. // -- kriskowal Kris Kowal Copyright (C) 2009-2011 MIT License
  221. // -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project)
  222. // -- dantman Daniel Friesen Copyright (C) 2010 XXX TODO License or CLA
  223. // -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License
  224. // -- Gozala Irakli Gozalishvili Copyright (C) 2010 MIT License
  225. // -- kitcambridge Kit Cambridge Copyright (C) 2011 MIT License
  226. // -- kossnocorp Sasha Koss XXX TODO License or CLA
  227. // -- bryanforbes Bryan Forbes XXX TODO License or CLA
  228. // -- killdream Quildreen Motta Copyright (C) 2011 MIT Licence
  229. // -- michaelficarra Michael Ficarra Copyright (C) 2011 3-clause BSD License
  230. // -- sharkbrainguy Gerard Paapu Copyright (C) 2011 MIT License
  231. // -- bbqsrc Brendan Molloy (C) 2011 Creative Commons Zero (public domain)
  232. // -- iwyg XXX TODO License or CLA
  233. // -- DomenicDenicola Domenic Denicola Copyright (C) 2011 MIT License
  234. // -- xavierm02 Montillet Xavier XXX TODO License or CLA
  235. // -- Raynos Raynos XXX TODO License or CLA
  236. // -- samsonjs Sami Samhuri Copyright (C) 2010 MIT License
  237. // -- rwldrn Rick Waldron Copyright (C) 2011 MIT License
  238. // -- lexer Alexey Zakharov XXX TODO License or CLA
  239. /*!
  240. Copyright (c) 2009, 280 North Inc. http://280north.com/
  241. MIT License. http://github.com/280north/narwhal/blob/master/README.md
  242. */
  243. define('ace/lib/es5-shim', ['require', 'exports', 'module' ], function(require, exports, module) {
  244. /*
  245. * Brings an environment as close to ECMAScript 5 compliance
  246. * as is possible with the facilities of erstwhile engines.
  247. *
  248. * Annotated ES5: http://es5.github.com/ (specific links below)
  249. * ES5 Spec: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
  250. *
  251. * @module
  252. */
  253. /*whatsupdoc*/
  254. //
  255. // Function
  256. // ========
  257. //
  258. // ES-5 15.3.4.5
  259. // http://es5.github.com/#x15.3.4.5
  260. if (!Function.prototype.bind) {
  261. Function.prototype.bind = function bind(that) { // .length is 1
  262. // 1. Let Target be the this value.
  263. var target = this;
  264. // 2. If IsCallable(Target) is false, throw a TypeError exception.
  265. if (typeof target != "function")
  266. throw new TypeError(); // TODO message
  267. // 3. Let A be a new (possibly empty) internal list of all of the
  268. // argument values provided after thisArg (arg1, arg2 etc), in order.
  269. // XXX slicedArgs will stand in for "A" if used
  270. var args = slice.call(arguments, 1); // for normal call
  271. // 4. Let F be a new native ECMAScript object.
  272. // 11. Set the [[Prototype]] internal property of F to the standard
  273. // built-in Function prototype object as specified in 15.3.3.1.
  274. // 12. Set the [[Call]] internal property of F as described in
  275. // 15.3.4.5.1.
  276. // 13. Set the [[Construct]] internal property of F as described in
  277. // 15.3.4.5.2.
  278. // 14. Set the [[HasInstance]] internal property of F as described in
  279. // 15.3.4.5.3.
  280. var bound = function () {
  281. if (this instanceof bound) {
  282. // 15.3.4.5.2 [[Construct]]
  283. // When the [[Construct]] internal method of a function object,
  284. // F that was created using the bind function is called with a
  285. // list of arguments ExtraArgs, the following steps are taken:
  286. // 1. Let target be the value of F's [[TargetFunction]]
  287. // internal property.
  288. // 2. If target has no [[Construct]] internal method, a
  289. // TypeError exception is thrown.
  290. // 3. Let boundArgs be the value of F's [[BoundArgs]] internal
  291. // property.
  292. // 4. Let args be a new list containing the same values as the
  293. // list boundArgs in the same order followed by the same
  294. // values as the list ExtraArgs in the same order.
  295. // 5. Return the result of calling the [[Construct]] internal
  296. // method of target providing args as the arguments.
  297. var F = function(){};
  298. F.prototype = target.prototype;
  299. var self = new F;
  300. var result = target.apply(
  301. self,
  302. args.concat(slice.call(arguments))
  303. );
  304. if (result !== null && Object(result) === result)
  305. return result;
  306. return self;
  307. } else {
  308. // 15.3.4.5.1 [[Call]]
  309. // When the [[Call]] internal method of a function object, F,
  310. // which was created using the bind function is called with a
  311. // this value and a list of arguments ExtraArgs, the following
  312. // steps are taken:
  313. // 1. Let boundArgs be the value of F's [[BoundArgs]] internal
  314. // property.
  315. // 2. Let boundThis be the value of F's [[BoundThis]] internal
  316. // property.
  317. // 3. Let target be the value of F's [[TargetFunction]] internal
  318. // property.
  319. // 4. Let args be a new list containing the same values as the
  320. // list boundArgs in the same order followed by the same
  321. // values as the list ExtraArgs in the same order.
  322. // 5. Return the result of calling the [[Call]] internal method
  323. // of target providing boundThis as the this value and
  324. // providing args as the arguments.
  325. // equiv: target.call(this, ...boundArgs, ...args)
  326. return target.apply(
  327. that,
  328. args.concat(slice.call(arguments))
  329. );
  330. }
  331. };
  332. // XXX bound.length is never writable, so don't even try
  333. //
  334. // 15. If the [[Class]] internal property of Target is "Function", then
  335. // a. Let L be the length property of Target minus the length of A.
  336. // b. Set the length own property of F to either 0 or L, whichever is
  337. // larger.
  338. // 16. Else set the length own property of F to 0.
  339. // 17. Set the attributes of the length own property of F to the values
  340. // specified in 15.3.5.1.
  341. // TODO
  342. // 18. Set the [[Extensible]] internal property of F to true.
  343. // TODO
  344. // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
  345. // 20. Call the [[DefineOwnProperty]] internal method of F with
  346. // arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
  347. // thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
  348. // false.
  349. // 21. Call the [[DefineOwnProperty]] internal method of F with
  350. // arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
  351. // [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
  352. // and false.
  353. // TODO
  354. // NOTE Function objects created using Function.prototype.bind do not
  355. // have a prototype property or the [[Code]], [[FormalParameters]], and
  356. // [[Scope]] internal properties.
  357. // XXX can't delete prototype in pure-js.
  358. // 22. Return F.
  359. return bound;
  360. };
  361. }
  362. // Shortcut to an often accessed properties, in order to avoid multiple
  363. // dereference that costs universally.
  364. // _Please note: Shortcuts are defined after `Function.prototype.bind` as we
  365. // us it in defining shortcuts.
  366. var call = Function.prototype.call;
  367. var prototypeOfArray = Array.prototype;
  368. var prototypeOfObject = Object.prototype;
  369. var slice = prototypeOfArray.slice;
  370. var toString = call.bind(prototypeOfObject.toString);
  371. var owns = call.bind(prototypeOfObject.hasOwnProperty);
  372. // If JS engine supports accessors creating shortcuts.
  373. var defineGetter;
  374. var defineSetter;
  375. var lookupGetter;
  376. var lookupSetter;
  377. var supportsAccessors;
  378. if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) {
  379. defineGetter = call.bind(prototypeOfObject.__defineGetter__);
  380. defineSetter = call.bind(prototypeOfObject.__defineSetter__);
  381. lookupGetter = call.bind(prototypeOfObject.__lookupGetter__);
  382. lookupSetter = call.bind(prototypeOfObject.__lookupSetter__);
  383. }
  384. //
  385. // Array
  386. // =====
  387. //
  388. // ES5 15.4.3.2
  389. // http://es5.github.com/#x15.4.3.2
  390. // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
  391. if (!Array.isArray) {
  392. Array.isArray = function isArray(obj) {
  393. return toString(obj) == "[object Array]";
  394. };
  395. }
  396. // The IsCallable() check in the Array functions
  397. // has been replaced with a strict check on the
  398. // internal class of the object to trap cases where
  399. // the provided function was actually a regular
  400. // expression literal, which in V8 and
  401. // JavaScriptCore is a typeof "function". Only in
  402. // V8 are regular expression literals permitted as
  403. // reduce parameters, so it is desirable in the
  404. // general case for the shim to match the more
  405. // strict and common behavior of rejecting regular
  406. // expressions.
  407. // ES5 15.4.4.18
  408. // http://es5.github.com/#x15.4.4.18
  409. // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/forEach
  410. if (!Array.prototype.forEach) {
  411. Array.prototype.forEach = function forEach(fun /*, thisp*/) {
  412. var self = toObject(this),
  413. thisp = arguments[1],
  414. i = 0,
  415. length = self.length >>> 0;
  416. // If no callback function or if callback is not a callable function
  417. if (toString(fun) != "[object Function]") {
  418. throw new TypeError(); // TODO message
  419. }
  420. while (i < length) {
  421. if (i in self) {
  422. // Invoke the callback function with call, passing arguments:
  423. // context, property value, property key, thisArg object context
  424. fun.call(thisp, self[i], i, self);
  425. }
  426. i++;
  427. }
  428. };
  429. }
  430. // ES5 15.4.4.19
  431. // http://es5.github.com/#x15.4.4.19
  432. // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
  433. if (!Array.prototype.map) {
  434. Array.prototype.map = function map(fun /*, thisp*/) {
  435. var self = toObject(this),
  436. length = self.length >>> 0,
  437. result = Array(length),
  438. thisp = arguments[1];
  439. // If no callback function or if callback is not a callable function
  440. if (toString(fun) != "[object Function]") {
  441. throw new TypeError(); // TODO message
  442. }
  443. for (var i = 0; i < length; i++) {
  444. if (i in self)
  445. result[i] = fun.call(thisp, self[i], i, self);
  446. }
  447. return result;
  448. };
  449. }
  450. // ES5 15.4.4.20
  451. // http://es5.github.com/#x15.4.4.20
  452. // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
  453. if (!Array.prototype.filter) {
  454. Array.prototype.filter = function filter(fun /*, thisp */) {
  455. var self = toObject(this),
  456. length = self.length >>> 0,
  457. result = [],
  458. thisp = arguments[1];
  459. // If no callback function or if callback is not a callable function
  460. if (toString(fun) != "[object Function]") {
  461. throw new TypeError(); // TODO message
  462. }
  463. for (var i = 0; i < length; i++) {
  464. if (i in self && fun.call(thisp, self[i], i, self))
  465. result.push(self[i]);
  466. }
  467. return result;
  468. };
  469. }
  470. // ES5 15.4.4.16
  471. // http://es5.github.com/#x15.4.4.16
  472. // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
  473. if (!Array.prototype.every) {
  474. Array.prototype.every = function every(fun /*, thisp */) {
  475. var self = toObject(this),
  476. length = self.length >>> 0,
  477. thisp = arguments[1];
  478. // If no callback function or if callback is not a callable function
  479. if (toString(fun) != "[object Function]") {
  480. throw new TypeError(); // TODO message
  481. }
  482. for (var i = 0; i < length; i++) {
  483. if (i in self && !fun.call(thisp, self[i], i, self))
  484. return false;
  485. }
  486. return true;
  487. };
  488. }
  489. // ES5 15.4.4.17
  490. // http://es5.github.com/#x15.4.4.17
  491. // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
  492. if (!Array.prototype.some) {
  493. Array.prototype.some = function some(fun /*, thisp */) {
  494. var self = toObject(this),
  495. length = self.length >>> 0,
  496. thisp = arguments[1];
  497. // If no callback function or if callback is not a callable function
  498. if (toString(fun) != "[object Function]") {
  499. throw new TypeError(); // TODO message
  500. }
  501. for (var i = 0; i < length; i++) {
  502. if (i in self && fun.call(thisp, self[i], i, self))
  503. return true;
  504. }
  505. return false;
  506. };
  507. }
  508. // ES5 15.4.4.21
  509. // http://es5.github.com/#x15.4.4.21
  510. // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
  511. if (!Array.prototype.reduce) {
  512. Array.prototype.reduce = function reduce(fun /*, initial*/) {
  513. var self = toObject(this),
  514. length = self.length >>> 0;
  515. // If no callback function or if callback is not a callable function
  516. if (toString(fun) != "[object Function]") {
  517. throw new TypeError(); // TODO message
  518. }
  519. // no value to return if no initial value and an empty array
  520. if (!length && arguments.length == 1)
  521. throw new TypeError(); // TODO message
  522. var i = 0;
  523. var result;
  524. if (arguments.length >= 2) {
  525. result = arguments[1];
  526. } else {
  527. do {
  528. if (i in self) {
  529. result = self[i++];
  530. break;
  531. }
  532. // if array contains no values, no initial value to return
  533. if (++i >= length)
  534. throw new TypeError(); // TODO message
  535. } while (true);
  536. }
  537. for (; i < length; i++) {
  538. if (i in self)
  539. result = fun.call(void 0, result, self[i], i, self);
  540. }
  541. return result;
  542. };
  543. }
  544. // ES5 15.4.4.22
  545. // http://es5.github.com/#x15.4.4.22
  546. // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
  547. if (!Array.prototype.reduceRight) {
  548. Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) {
  549. var self = toObject(this),
  550. length = self.length >>> 0;
  551. // If no callback function or if callback is not a callable function
  552. if (toString(fun) != "[object Function]") {
  553. throw new TypeError(); // TODO message
  554. }
  555. // no value to return if no initial value, empty array
  556. if (!length && arguments.length == 1)
  557. throw new TypeError(); // TODO message
  558. var result, i = length - 1;
  559. if (arguments.length >= 2) {
  560. result = arguments[1];
  561. } else {
  562. do {
  563. if (i in self) {
  564. result = self[i--];
  565. break;
  566. }
  567. // if array contains no values, no initial value to return
  568. if (--i < 0)
  569. throw new TypeError(); // TODO message
  570. } while (true);
  571. }
  572. do {
  573. if (i in this)
  574. result = fun.call(void 0, result, self[i], i, self);
  575. } while (i--);
  576. return result;
  577. };
  578. }
  579. // ES5 15.4.4.14
  580. // http://es5.github.com/#x15.4.4.14
  581. // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
  582. if (!Array.prototype.indexOf) {
  583. Array.prototype.indexOf = function indexOf(sought /*, fromIndex */ ) {
  584. var self = toObject(this),
  585. length = self.length >>> 0;
  586. if (!length)
  587. return -1;
  588. var i = 0;
  589. if (arguments.length > 1)
  590. i = toInteger(arguments[1]);
  591. // handle negative indices
  592. i = i >= 0 ? i : Math.max(0, length + i);
  593. for (; i < length; i++) {
  594. if (i in self && self[i] === sought) {
  595. return i;
  596. }
  597. }
  598. return -1;
  599. };
  600. }
  601. // ES5 15.4.4.15
  602. // http://es5.github.com/#x15.4.4.15
  603. // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
  604. if (!Array.prototype.lastIndexOf) {
  605. Array.prototype.lastIndexOf = function lastIndexOf(sought /*, fromIndex */) {
  606. var self = toObject(this),
  607. length = self.length >>> 0;
  608. if (!length)
  609. return -1;
  610. var i = length - 1;
  611. if (arguments.length > 1)
  612. i = Math.min(i, toInteger(arguments[1]));
  613. // handle negative indices
  614. i = i >= 0 ? i : length - Math.abs(i);
  615. for (; i >= 0; i--) {
  616. if (i in self && sought === self[i])
  617. return i;
  618. }
  619. return -1;
  620. };
  621. }
  622. //
  623. // Object
  624. // ======
  625. //
  626. // ES5 15.2.3.2
  627. // http://es5.github.com/#x15.2.3.2
  628. if (!Object.getPrototypeOf) {
  629. // https://github.com/kriskowal/es5-shim/issues#issue/2
  630. // http://ejohn.org/blog/objectgetprototypeof/
  631. // recommended by fschaefer on github
  632. Object.getPrototypeOf = function getPrototypeOf(object) {
  633. return object.__proto__ || (
  634. object.constructor ?
  635. object.constructor.prototype :
  636. prototypeOfObject
  637. );
  638. };
  639. }
  640. // ES5 15.2.3.3
  641. // http://es5.github.com/#x15.2.3.3
  642. if (!Object.getOwnPropertyDescriptor) {
  643. var ERR_NON_OBJECT = "Object.getOwnPropertyDescriptor called on a " +
  644. "non-object: ";
  645. Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) {
  646. if ((typeof object != "object" && typeof object != "function") || object === null)
  647. throw new TypeError(ERR_NON_OBJECT + object);
  648. // If object does not owns property return undefined immediately.
  649. if (!owns(object, property))
  650. return;
  651. var descriptor, getter, setter;
  652. // If object has a property then it's for sure both `enumerable` and
  653. // `configurable`.
  654. descriptor = { enumerable: true, configurable: true };
  655. // If JS engine supports accessor properties then property may be a
  656. // getter or setter.
  657. if (supportsAccessors) {
  658. // Unfortunately `__lookupGetter__` will return a getter even
  659. // if object has own non getter property along with a same named
  660. // inherited getter. To avoid misbehavior we temporary remove
  661. // `__proto__` so that `__lookupGetter__` will return getter only
  662. // if it's owned by an object.
  663. var prototype = object.__proto__;
  664. object.__proto__ = prototypeOfObject;
  665. var getter = lookupGetter(object, property);
  666. var setter = lookupSetter(object, property);
  667. // Once we have getter and setter we can put values back.
  668. object.__proto__ = prototype;
  669. if (getter || setter) {
  670. if (getter) descriptor.get = getter;
  671. if (setter) descriptor.set = setter;
  672. // If it was accessor property we're done and return here
  673. // in order to avoid adding `value` to the descriptor.
  674. return descriptor;
  675. }
  676. }
  677. // If we got this far we know that object has an own property that is
  678. // not an accessor so we set it as a value and return descriptor.
  679. descriptor.value = object[property];
  680. return descriptor;
  681. };
  682. }
  683. // ES5 15.2.3.4
  684. // http://es5.github.com/#x15.2.3.4
  685. if (!Object.getOwnPropertyNames) {
  686. Object.getOwnPropertyNames = function getOwnPropertyNames(object) {
  687. return Object.keys(object);
  688. };
  689. }
  690. // ES5 15.2.3.5
  691. // http://es5.github.com/#x15.2.3.5
  692. if (!Object.create) {
  693. Object.create = function create(prototype, properties) {
  694. var object;
  695. if (prototype === null) {
  696. object = { "__proto__": null };
  697. } else {
  698. if (typeof prototype != "object")
  699. throw new TypeError("typeof prototype["+(typeof prototype)+"] != 'object'");
  700. var Type = function () {};
  701. Type.prototype = prototype;
  702. object = new Type();
  703. // IE has no built-in implementation of `Object.getPrototypeOf`
  704. // neither `__proto__`, but this manually setting `__proto__` will
  705. // guarantee that `Object.getPrototypeOf` will work as expected with
  706. // objects created using `Object.create`
  707. object.__proto__ = prototype;
  708. }
  709. if (properties !== void 0)
  710. Object.defineProperties(object, properties);
  711. return object;
  712. };
  713. }
  714. // ES5 15.2.3.6
  715. // http://es5.github.com/#x15.2.3.6
  716. // Patch for WebKit and IE8 standard mode
  717. // Designed by hax <hax.github.com>
  718. // related issue: https://github.com/kriskowal/es5-shim/issues#issue/5
  719. // IE8 Reference:
  720. // http://msdn.microsoft.com/en-us/library/dd282900.aspx
  721. // http://msdn.microsoft.com/en-us/library/dd229916.aspx
  722. // WebKit Bugs:
  723. // https://bugs.webkit.org/show_bug.cgi?id=36423
  724. function doesDefinePropertyWork(object) {
  725. try {
  726. Object.defineProperty(object, "sentinel", {});
  727. return "sentinel" in object;
  728. } catch (exception) {
  729. // returns falsy
  730. }
  731. }
  732. // check whether defineProperty works if it's given. Otherwise,
  733. // shim partially.
  734. if (Object.defineProperty) {
  735. var definePropertyWorksOnObject = doesDefinePropertyWork({});
  736. var definePropertyWorksOnDom = typeof document == "undefined" ||
  737. doesDefinePropertyWork(document.createElement("div"));
  738. if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) {
  739. var definePropertyFallback = Object.defineProperty;
  740. }
  741. }
  742. if (!Object.defineProperty || definePropertyFallback) {
  743. var ERR_NON_OBJECT_DESCRIPTOR = "Property description must be an object: ";
  744. var ERR_NON_OBJECT_TARGET = "Object.defineProperty called on non-object: "
  745. var ERR_ACCESSORS_NOT_SUPPORTED = "getters & setters can not be defined " +
  746. "on this javascript engine";
  747. Object.defineProperty = function defineProperty(object, property, descriptor) {
  748. if ((typeof object != "object" && typeof object != "function") || object === null)
  749. throw new TypeError(ERR_NON_OBJECT_TARGET + object);
  750. if ((typeof descriptor != "object" && typeof descriptor != "function") || descriptor === null)
  751. throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor);
  752. // make a valiant attempt to use the real defineProperty
  753. // for I8's DOM elements.
  754. if (definePropertyFallback) {
  755. try {
  756. return definePropertyFallback.call(Object, object, property, descriptor);
  757. } catch (exception) {
  758. // try the shim if the real one doesn't work
  759. }
  760. }
  761. // If it's a data property.
  762. if (owns(descriptor, "value")) {
  763. // fail silently if "writable", "enumerable", or "configurable"
  764. // are requested but not supported
  765. /*
  766. // alternate approach:
  767. if ( // can't implement these features; allow false but not true
  768. !(owns(descriptor, "writable") ? descriptor.writable : true) ||
  769. !(owns(descriptor, "enumerable") ? descriptor.enumerable : true) ||
  770. !(owns(descriptor, "configurable") ? descriptor.configurable : true)
  771. )
  772. throw new RangeError(
  773. "This implementation of Object.defineProperty does not " +
  774. "support configurable, enumerable, or writable."
  775. );
  776. */
  777. if (supportsAccessors && (lookupGetter(object, property) ||
  778. lookupSetter(object, property)))
  779. {
  780. // As accessors are supported only on engines implementing
  781. // `__proto__` we can safely override `__proto__` while defining
  782. // a property to make sure that we don't hit an inherited
  783. // accessor.
  784. var prototype = object.__proto__;
  785. object.__proto__ = prototypeOfObject;
  786. // Deleting a property anyway since getter / setter may be
  787. // defined on object itself.
  788. delete object[property];
  789. object[property] = descriptor.value;
  790. // Setting original `__proto__` back now.
  791. object.__proto__ = prototype;
  792. } else {
  793. object[property] = descriptor.value;
  794. }
  795. } else {
  796. if (!supportsAccessors)
  797. throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
  798. // If we got that far then getters and setters can be defined !!
  799. if (owns(descriptor, "get"))
  800. defineGetter(object, property, descriptor.get);
  801. if (owns(descriptor, "set"))
  802. defineSetter(object, property, descriptor.set);
  803. }
  804. return object;
  805. };
  806. }
  807. // ES5 15.2.3.7
  808. // http://es5.github.com/#x15.2.3.7
  809. if (!Object.defineProperties) {
  810. Object.defineProperties = function defineProperties(object, properties) {
  811. for (var property in properties) {
  812. if (owns(properties, property))
  813. Object.defineProperty(object, property, properties[property]);
  814. }
  815. return object;
  816. };
  817. }
  818. // ES5 15.2.3.8
  819. // http://es5.github.com/#x15.2.3.8
  820. if (!Object.seal) {
  821. Object.seal = function seal(object) {
  822. // this is misleading and breaks feature-detection, but
  823. // allows "securable" code to "gracefully" degrade to working
  824. // but insecure code.
  825. return object;
  826. };
  827. }
  828. // ES5 15.2.3.9
  829. // http://es5.github.com/#x15.2.3.9
  830. if (!Object.freeze) {
  831. Object.freeze = function freeze(object) {
  832. // this is misleading and breaks feature-detection, but
  833. // allows "securable" code to "gracefully" degrade to working
  834. // but insecure code.
  835. return object;
  836. };
  837. }
  838. // detect a Rhino bug and patch it
  839. try {
  840. Object.freeze(function () {});
  841. } catch (exception) {
  842. Object.freeze = (function freeze(freezeObject) {
  843. return function freeze(object) {
  844. if (typeof object == "function") {
  845. return object;
  846. } else {
  847. return freezeObject(object);
  848. }
  849. };
  850. })(Object.freeze);
  851. }
  852. // ES5 15.2.3.10
  853. // http://es5.github.com/#x15.2.3.10
  854. if (!Object.preventExtensions) {
  855. Object.preventExtensions = function preventExtensions(object) {
  856. // this is misleading and breaks feature-detection, but
  857. // allows "securable" code to "gracefully" degrade to working
  858. // but insecure code.
  859. return object;
  860. };
  861. }
  862. // ES5 15.2.3.11
  863. // http://es5.github.com/#x15.2.3.11
  864. if (!Object.isSealed) {
  865. Object.isSealed = function isSealed(object) {
  866. return false;
  867. };
  868. }
  869. // ES5 15.2.3.12
  870. // http://es5.github.com/#x15.2.3.12
  871. if (!Object.isFrozen) {
  872. Object.isFrozen = function isFrozen(object) {
  873. return false;
  874. };
  875. }
  876. // ES5 15.2.3.13
  877. // http://es5.github.com/#x15.2.3.13
  878. if (!Object.isExtensible) {
  879. Object.isExtensible = function isExtensible(object) {
  880. // 1. If Type(O) is not Object throw a TypeError exception.
  881. if (Object(object) === object) {
  882. throw new TypeError(); // TODO message
  883. }
  884. // 2. Return the Boolean value of the [[Extensible]] internal property of O.
  885. var name = '';
  886. while (owns(object, name)) {
  887. name += '?';
  888. }
  889. object[name] = true;
  890. var returnValue = owns(object, name);
  891. delete object[name];
  892. return returnValue;
  893. };
  894. }
  895. // ES5 15.2.3.14
  896. // http://es5.github.com/#x15.2.3.14
  897. if (!Object.keys) {
  898. // http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
  899. var hasDontEnumBug = true,
  900. dontEnums = [
  901. "toString",
  902. "toLocaleString",
  903. "valueOf",
  904. "hasOwnProperty",
  905. "isPrototypeOf",
  906. "propertyIsEnumerable",
  907. "constructor"
  908. ],
  909. dontEnumsLength = dontEnums.length;
  910. for (var key in {"toString": null})
  911. hasDontEnumBug = false;
  912. Object.keys = function keys(object) {
  913. if ((typeof object != "object" && typeof object != "function") || object === null)
  914. throw new TypeError("Object.keys called on a non-object");
  915. var keys = [];
  916. for (var name in object) {
  917. if (owns(object, name)) {
  918. keys.push(name);
  919. }
  920. }
  921. if (hasDontEnumBug) {
  922. for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
  923. var dontEnum = dontEnums[i];
  924. if (owns(object, dontEnum)) {
  925. keys.push(dontEnum);
  926. }
  927. }
  928. }
  929. return keys;
  930. };
  931. }
  932. //
  933. // Date
  934. // ====
  935. //
  936. // ES5 15.9.5.43
  937. // http://es5.github.com/#x15.9.5.43
  938. // This function returns a String value represent the instance in time
  939. // represented by this Date object. The format of the String is the Date Time
  940. // string format defined in 15.9.1.15. All fields are present in the String.
  941. // The time zone is always UTC, denoted by the suffix Z. If the time value of
  942. // this object is not a finite Number a RangeError exception is thrown.
  943. if (!Date.prototype.toISOString || (new Date(-62198755200000).toISOString().indexOf('-000001') === -1)) {
  944. Date.prototype.toISOString = function toISOString() {
  945. var result, length, value, year;
  946. if (!isFinite(this))
  947. throw new RangeError;
  948. // the date time string format is specified in 15.9.1.15.
  949. result = [this.getUTCMonth() + 1, this.getUTCDate(),
  950. this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds()];
  951. year = this.getUTCFullYear();
  952. year = (year < 0 ? '-' : (year > 9999 ? '+' : '')) + ('00000' + Math.abs(year)).slice(0 <= year && year <= 9999 ? -4 : -6);
  953. length = result.length;
  954. while (length--) {
  955. value = result[length];
  956. // pad months, days, hours, minutes, and seconds to have two digits.
  957. if (value < 10)
  958. result[length] = "0" + value;
  959. }
  960. // pad milliseconds to have three digits.
  961. return year + "-" + result.slice(0, 2).join("-") + "T" + result.slice(2).join(":") + "." +
  962. ("000" + this.getUTCMilliseconds()).slice(-3) + "Z";
  963. }
  964. }
  965. // ES5 15.9.4.4
  966. // http://es5.github.com/#x15.9.4.4
  967. if (!Date.now) {
  968. Date.now = function now() {
  969. return new Date().getTime();
  970. };
  971. }
  972. // ES5 15.9.5.44
  973. // http://es5.github.com/#x15.9.5.44
  974. // This function provides a String representation of a Date object for use by
  975. // JSON.stringify (15.12.3).
  976. if (!Date.prototype.toJSON) {
  977. Date.prototype.toJSON = function toJSON(key) {
  978. // When the toJSON method is called with argument key, the following
  979. // steps are taken:
  980. // 1. Let O be the result of calling ToObject, giving it the this
  981. // value as its argument.
  982. // 2. Let tv be ToPrimitive(O, hint Number).
  983. // 3. If tv is a Number and is not finite, return null.
  984. // XXX
  985. // 4. Let toISO be the result of calling the [[Get]] internal method of
  986. // O with argument "toISOString".
  987. // 5. If IsCallable(toISO) is false, throw a TypeError exception.
  988. if (typeof this.toISOString != "function")
  989. throw new TypeError(); // TODO message
  990. // 6. Return the result of calling the [[Call]] internal method of
  991. // toISO with O as the this value and an empty argument list.
  992. return this.toISOString();
  993. // NOTE 1 The argument is ignored.
  994. // NOTE 2 The toJSON function is intentionally generic; it does not
  995. // require that its this value be a Date object. Therefore, it can be
  996. // transferred to other kinds of objects for use as a method. However,
  997. // it does require that any such object have a toISOString method. An
  998. // object is free to use the argument key to filter its
  999. // stringification.
  1000. };
  1001. }
  1002. // ES5 15.9.4.2
  1003. // http://es5.github.com/#x15.9.4.2
  1004. // based on work shared by Daniel Friesen (dantman)
  1005. // http://gist.github.com/303249
  1006. if (Date.parse("+275760-09-13T00:00:00.000Z") !== 8.64e15) {
  1007. // XXX global assignment won't work in embeddings that use
  1008. // an alternate object for the context.
  1009. Date = (function(NativeDate) {
  1010. // Date.length === 7
  1011. var Date = function Date(Y, M, D, h, m, s, ms) {
  1012. var length = arguments.length;
  1013. if (this instanceof NativeDate) {
  1014. var date = length == 1 && String(Y) === Y ? // isString(Y)
  1015. // We explicitly pass it through parse:
  1016. new NativeDate(Date.parse(Y)) :
  1017. // We have to manually make calls depending on argument
  1018. // length here
  1019. length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) :
  1020. length >= 6 ? new NativeDate(Y, M, D, h, m, s) :
  1021. length >= 5 ? new NativeDate(Y, M, D, h, m) :
  1022. length >= 4 ? new NativeDate(Y, M, D, h) :
  1023. length >= 3 ? new NativeDate(Y, M, D) :
  1024. length >= 2 ? new NativeDate(Y, M) :
  1025. length >= 1 ? new NativeDate(Y) :
  1026. new NativeDate();
  1027. // Prevent mixups with unfixed Date object
  1028. date.constructor = Date;
  1029. return date;
  1030. }
  1031. return NativeDate.apply(this, arguments);
  1032. };
  1033. // 15.9.1.15 Date Time String Format.
  1034. var isoDateExpression = new RegExp("^" +
  1035. "(\\d{4}|[\+\-]\\d{6})" + // four-digit year capture or sign + 6-digit extended year
  1036. "(?:-(\\d{2})" + // optional month capture
  1037. "(?:-(\\d{2})" + // optional day capture
  1038. "(?:" + // capture hours:minutes:seconds.milliseconds
  1039. "T(\\d{2})" + // hours capture
  1040. ":(\\d{2})" + // minutes capture
  1041. "(?:" + // optional :seconds.milliseconds
  1042. ":(\\d{2})" + // seconds capture
  1043. "(?:\\.(\\d{3}))?" + // milliseconds capture
  1044. ")?" +
  1045. "(?:" + // capture UTC offset component
  1046. "Z|" + // UTC capture
  1047. "(?:" + // offset specifier +/-hours:minutes
  1048. "([-+])" + // sign capture
  1049. "(\\d{2})" + // hours offset capture
  1050. ":(\\d{2})" + // minutes offset capture
  1051. ")" +
  1052. ")?)?)?)?" +
  1053. "$");
  1054. // Copy any custom methods a 3rd party library may have added
  1055. for (var key in NativeDate)
  1056. Date[key] = NativeDate[key];
  1057. // Copy "native" methods explicitly; they may be non-enumerable
  1058. Date.now = NativeDate.now;
  1059. Date.UTC = NativeDate.UTC;
  1060. Date.prototype = NativeDate.prototype;
  1061. Date.prototype.constructor = Date;
  1062. // Upgrade Date.parse to handle simplified ISO 8601 strings
  1063. Date.parse = function parse(string) {
  1064. var match = isoDateExpression.exec(string);
  1065. if (match) {
  1066. match.shift(); // kill match[0], the full match
  1067. // parse months, days, hours, minutes, seconds, and milliseconds
  1068. for (var i = 1; i < 7; i++) {
  1069. // provide default values if necessary
  1070. match[i] = +(match[i] || (i < 3 ? 1 : 0));
  1071. // match[1] is the month. Months are 0-11 in JavaScript
  1072. // `Date` objects, but 1-12 in ISO notation, so we
  1073. // decrement.
  1074. if (i == 1)
  1075. match[i]--;
  1076. }
  1077. // parse the UTC offset component
  1078. var minuteOffset = +match.pop(), hourOffset = +match.pop(), sign = match.pop();
  1079. // compute the explicit time zone offset if specified
  1080. var offset = 0;
  1081. if (sign) {
  1082. // detect invalid offsets and return early
  1083. if (hourOffset > 23 || minuteOffset > 59)
  1084. return NaN;
  1085. // express the provided time zone offset in minutes. The offset is
  1086. // negative for time zones west of UTC; positive otherwise.
  1087. offset = (hourOffset * 60 + minuteOffset) * 6e4 * (sign == "+" ? -1 : 1);
  1088. }
  1089. // Date.UTC for years between 0 and 99 converts year to 1900 + year
  1090. // The Gregorian calendar has a 400-year cycle, so
  1091. // to Date.UTC(year + 400, .... ) - 12622780800000 == Date.UTC(year, ...),
  1092. // where 12622780800000 - number of milliseconds in Gregorian calendar 400 years
  1093. var year = +match[0];
  1094. if (0 <= year && year <= 99) {
  1095. match[0] = year + 400;
  1096. return NativeDate.UTC.apply(this, match) + offset - 12622780800000;
  1097. }
  1098. // compute a new UTC date value, accounting for the optional offset
  1099. return NativeDate.UTC.apply(this, match) + offset;
  1100. }
  1101. return NativeDate.parse.apply(this, arguments);
  1102. };
  1103. return Date;
  1104. })(Date);
  1105. }
  1106. //
  1107. // String
  1108. // ======
  1109. //
  1110. // ES5 15.5.4.20
  1111. // http://es5.github.com/#x15.5.4.20
  1112. var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" +
  1113. "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" +
  1114. "\u2029\uFEFF";
  1115. if (!String.prototype.trim || ws.trim()) {
  1116. // http://blog.stevenlevithan.com/archives/faster-trim-javascript
  1117. // http://perfectionkills.com/whitespace-deviations/
  1118. ws = "[" + ws + "]";
  1119. var trimBeginRegexp = new RegExp("^" + ws + ws + "*"),
  1120. trimEndRegexp = new RegExp(ws + ws + "*$");
  1121. String.prototype.trim = function trim() {
  1122. return String(this).replace(trimBeginRegexp, "").replace(trimEndRegexp, "");
  1123. };
  1124. }
  1125. //
  1126. // Util
  1127. // ======
  1128. //
  1129. // ES5 9.4
  1130. // http://es5.github.com/#x9.4
  1131. // http://jsperf.com/to-integer
  1132. var toInteger = function (n) {
  1133. n = +n;
  1134. if (n !== n) // isNaN
  1135. n = 0;
  1136. else if (n !== 0 && n !== (1/0) && n !== -(1/0))
  1137. n = (n > 0 || -1) * Math.floor(Math.abs(n));
  1138. return n;
  1139. };
  1140. var prepareString = "a"[0] != "a",
  1141. // ES5 9.9
  1142. // http://es5.github.com/#x9.9
  1143. toObject = function (o) {
  1144. if (o == null) { // this matches both null and undefined
  1145. throw new TypeError(); // TODO message
  1146. }
  1147. // If the implementation doesn't support by-index access of
  1148. // string characters (ex. IE < 7), split the string
  1149. if (prepareString && typeof o == "string" && o) {
  1150. return o.split("");
  1151. }
  1152. return Object(o);
  1153. };
  1154. });
  1155. define('ace/lib/event_emitter', ['require', 'exports', 'module' ], function(require, exports, module) {
  1156. var EventEmitter = {};
  1157. EventEmitter._emit =
  1158. EventEmitter._dispatchEvent = function(eventName, e) {
  1159. this._eventRegistry = this._eventRegistry || {};
  1160. this._defaultHandlers = this._defaultHandlers || {};
  1161. var listeners = this._eventRegistry[eventName] || [];
  1162. var defaultHandler = this._defaultHandlers[eventName];
  1163. if (!listeners.length && !defaultHandler)
  1164. return;
  1165. e = e || {};
  1166. e.type = eventName;
  1167. if (!e.stopPropagation) {
  1168. e.stopPropagation = function() {
  1169. this.propagationStopped = true;
  1170. };
  1171. }
  1172. if (!e.preventDefault) {
  1173. e.preventDefault = function() {
  1174. this.defaultPrevented = true;
  1175. };
  1176. }
  1177. for (var i=0; i<listeners.length; i++) {
  1178. listeners[i](e);
  1179. if (e.propagationStopped)
  1180. break;
  1181. }
  1182. if (defaultHandler && !e.defaultPrevented)
  1183. return defaultHandler(e);
  1184. };
  1185. EventEmitter.setDefaultHandler = function(eventName, callback) {
  1186. this._defaultHandlers = this._defaultHandlers || {};
  1187. if (this._defaultHandlers[eventName])
  1188. throw new Error("The default handler for '" + eventName + "' is already set");
  1189. this._defaultHandlers[eventName] = callback;
  1190. };
  1191. EventEmitter.on =
  1192. EventEmitter.addEventListener = function(eventName, callback) {
  1193. this._eventRegistry = this._eventRegistry || {};
  1194. var listeners = this._eventRegistry[eventName];
  1195. if (!listeners)
  1196. var listeners = this._eventRegistry[eventName] = [];
  1197. if (listeners.indexOf(callback) == -1)
  1198. listeners.push(callback);
  1199. };
  1200. EventEmitter.removeListener =
  1201. EventEmitter.removeEventListener = function(eventName, callback) {
  1202. this._eventRegistry = this._eventRegistry || {};
  1203. var listeners = this._eventRegistry[eventName];
  1204. if (!listeners)
  1205. return;
  1206. var index = listeners.indexOf(callback);
  1207. if (index !== -1)
  1208. listeners.splice(index, 1);
  1209. };
  1210. EventEmitter.removeAllListeners = function(eventName) {
  1211. if (this._eventRegistry) this._eventRegistry[eventName] = [];
  1212. };
  1213. exports.EventEmitter = EventEmitter;
  1214. });
  1215. define('ace/lib/oop', ['require', 'exports', 'module' ], function(require, exports, module) {
  1216. exports.inherits = (function() {
  1217. var tempCtor = function() {};
  1218. return function(ctor, superCtor) {
  1219. tempCtor.prototype = superCtor.prototype;
  1220. ctor.super_ = superCtor.prototype;
  1221. ctor.prototype = new tempCtor();
  1222. ctor.prototype.constructor = ctor;
  1223. };
  1224. }());
  1225. exports.mixin = function(obj, mixin) {
  1226. for (var key in mixin) {
  1227. obj[key] = mixin[key];
  1228. }
  1229. };
  1230. exports.implement = function(proto, mixin) {
  1231. exports.mixin(proto, mixin);
  1232. };
  1233. });
  1234. define('ace/mode/css_worker', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/worker/mirror', 'ace/mode/css/csslint'], function(require, exports, module) {
  1235. var oop = require("../lib/oop");
  1236. var Mirror = require("../worker/mirror").Mirror;
  1237. var CSSLint = require("./css/csslint").CSSLint;
  1238. var Worker = exports.Worker = function(sender) {
  1239. Mirror.call(this, sender);
  1240. this.setTimeout(200);
  1241. };
  1242. oop.inherits(Worker, Mirror);
  1243. (function() {
  1244. this.onUpdate = function() {
  1245. var value = this.doc.getValue();
  1246. var result = CSSLint.verify(value);
  1247. this.sender.emit("csslint", result.messages.map(function(msg) {
  1248. delete msg.rule;
  1249. return msg;
  1250. }));
  1251. };
  1252. }).call(Worker.prototype);
  1253. });
  1254. define('ace/worker/mirror', ['require', 'exports', 'module' , 'ace/document', 'ace/lib/lang'], function(require, exports, module) {
  1255. var Document = require("../document").Document;
  1256. var lang = require("../lib/lang");
  1257. var Mirror = exports.Mirror = function(sender) {
  1258. this.sender = sender;
  1259. var doc = this.doc = new Document("");
  1260. var deferredUpdate = this.deferredUpdate = lang.deferredCall(this.onUpdate.bind(this));
  1261. var _self = this;
  1262. sender.on("change", function(e) {
  1263. doc.applyDeltas([e.data]);
  1264. deferredUpdate.schedule(_self.$timeout);
  1265. });
  1266. };
  1267. (function() {
  1268. this.$timeout = 500;
  1269. this.setTimeout = function(timeout) {
  1270. this.$timeout = timeout;
  1271. };
  1272. this.setValue = function(value) {
  1273. this.doc.setValue(value);
  1274. this.deferredUpdate.schedule(this.$timeout);
  1275. };
  1276. this.getValue = function(callbackId) {
  1277. this.sender.callback(this.doc.getValue(), callbackId);
  1278. };
  1279. this.onUpdate = function() {
  1280. // abstract method
  1281. };
  1282. }).call(Mirror.prototype);
  1283. });
  1284. define('ace/document', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter', 'ace/range', 'ace/anchor'], function(require, exports, module) {
  1285. var oop = require("./lib/oop");
  1286. var EventEmitter = require("./lib/event_emitter").EventEmitter;
  1287. var Range = require("./range").Range;
  1288. var Anchor = require("./anchor").Anchor;
  1289. /**
  1290. * new Document([text])
  1291. * - text (String | Array): The starting text
  1292. *
  1293. * Creates a new `Document`. If `text` is included, the `Document` contains those strings; otherwise, it's empty.
  1294. *
  1295. **/
  1296. var Document = function(text) {
  1297. this.$lines = [];
  1298. // There has to be one line at least in the document. If you pass an empty
  1299. // string to the insert function, nothing will happen. Workaround.
  1300. if (text.length == 0) {
  1301. this.$lines = [""];
  1302. } else if (Array.isArray(text)) {
  1303. this.insertLines(0, text);
  1304. } else {
  1305. this.insert({row: 0, column:0}, text);
  1306. }
  1307. };
  1308. (function() {
  1309. oop.implement(this, EventEmitter);
  1310. this.setValue = function(text) {
  1311. var len = this.getLength();
  1312. this.remove(new Range(0, 0, len, this.getLine(len-1).length));
  1313. this.insert({row: 0, column:0}, text);
  1314. };
  1315. this.getValue = function() {
  1316. return this.getAllLines().join(this.getNewLineCharacter());
  1317. };
  1318. this.createAnchor = function(row, column) {
  1319. return new Anchor(this, row, column);
  1320. };
  1321. // check for IE split bug
  1322. if ("aaa".split(/a/).length == 0)
  1323. this.$split = function(text) {
  1324. return text.replace(/\r\n|\r/g, "\n").split("\n");
  1325. }
  1326. else
  1327. this.$split = function(text) {
  1328. return text.split(/\r\n|\r|\n/);
  1329. };
  1330. this.$detectNewLine = function(text) {
  1331. var match = text.match(/^.*?(\r\n|\r|\n)/m);
  1332. if (match) {
  1333. this.$autoNewLine = match[1];
  1334. } else {
  1335. this.$autoNewLine = "\n";
  1336. }
  1337. };
  1338. this.getNewLineCharacter = function() {
  1339. switch (this.$newLineMode) {
  1340. case "windows":
  1341. return "\r\n";
  1342. case "unix":
  1343. return "\n";
  1344. case "auto":
  1345. return this.$autoNewLine;
  1346. }
  1347. };
  1348. this.$autoNewLine = "\n";
  1349. this.$newLineMode = "auto";
  1350. this.setNewLineMode = function(newLineMode) {
  1351. if (this.$newLineMode === newLineMode)
  1352. return;
  1353. this.$newLineMode = newLineMode;
  1354. };
  1355. this.getNewLineMode = function() {
  1356. return this.$newLineMode;
  1357. };
  1358. this.isNewLine = function(text) {
  1359. return (text == "\r\n" || text == "\r" || text == "\n");
  1360. };
  1361. this.getLine = function(row) {
  1362. return this.$lines[row] || "";
  1363. };
  1364. this.getLines = function(firstRow, lastRow) {
  1365. return this.$lines.slice(firstRow, lastRow + 1);
  1366. };
  1367. this.getAllLines = function() {
  1368. return this.getLines(0, this.getLength());
  1369. };
  1370. this.getLength = function() {
  1371. return this.$lines.length;
  1372. };
  1373. this.getTextRange = function(range) {
  1374. if (range.start.row == range.end.row) {
  1375. return this.$lines[range.start.row].substring(range.start.column,
  1376. range.end.column);
  1377. }
  1378. else {
  1379. var lines = this.getLines(range.start.row+1, range.end.row-1);
  1380. lines.unshift((this.$lines[range.start.row] || "").substring(range.start.column));
  1381. lines.push((this.$lines[range.end.row] || "").substring(0, range.end.column));
  1382. return lines.join(this.getNewLineCharacter());
  1383. }
  1384. };
  1385. this.$clipPosition = function(position) {
  1386. var length = this.getLength();
  1387. if (position.row >= length) {
  1388. position.row = Math.max(0, length - 1);
  1389. position.column = this.getLine(length-1).length;
  1390. }
  1391. return position;
  1392. };
  1393. this.insert = function(position, text) {
  1394. if (!text || text.length === 0)
  1395. return position;
  1396. position = this.$clipPosition(position);
  1397. // only detect new lines if the document has no line break yet
  1398. if (this.getLength() <= 1)
  1399. this.$detectNewLine(text);
  1400. var lines = this.$split(text);
  1401. var firstLine = lines.splice(0, 1)[0];
  1402. var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0];
  1403. position = this.insertInLine(position, firstLine);
  1404. if (lastLine !== null) {
  1405. position = this.insertNewLine(position); // terminate first line
  1406. position = this.insertLines(position.row, lines);
  1407. position = this.insertInLine(position, lastLine || "");
  1408. }
  1409. return position;
  1410. };
  1411. this.insertLines = function(row, lines) {
  1412. if (lines.length == 0)
  1413. return {row: row, column: 0};
  1414. // apply doesn't work for big arrays (smallest threshold is on safari 0xFFFF)
  1415. // to circumvent that we have to break huge inserts into smaller chunks here
  1416. if (lines.length > 0xFFFF) {
  1417. var end = this.insertLines(row, lines.slice(0xFFFF));
  1418. lines = lines.slice(0, 0xFFFF);
  1419. }
  1420. var args = [row, 0];
  1421. args.push.apply(args, lines);
  1422. this.$lines.splice.apply(this.$lines, args);
  1423. var range = new Range(row, 0, row + lines.length, 0);
  1424. var delta = {
  1425. action: "insertLines",
  1426. range: range,
  1427. lines: lines
  1428. };
  1429. this._emit("change", { data: delta });
  1430. return end || range.end;
  1431. };
  1432. this.insertNewLine = function(position) {
  1433. position = this.$clipPosition(position);
  1434. var line = this.$lines[position.row] || "";
  1435. this.$lines[position.row] = line.substring(0, position.column);
  1436. this.$lines.splice(position.row + 1, 0, line.substring(position.column, line.length));
  1437. var end = {
  1438. row : position.row + 1,
  1439. column : 0
  1440. };
  1441. var delta = {
  1442. action: "insertText",
  1443. range: Range.fromPoints(position, end),
  1444. text: this.getNewLineCharacter()
  1445. };
  1446. this._emit("change", { data: delta });
  1447. return end;
  1448. };
  1449. this.insertInLine = function(position, text) {
  1450. if (text.length == 0)
  1451. return position;
  1452. var line = this.$lines[position.row] || "";
  1453. this.$lines[position.row] = line.substring(0, position.column) + text
  1454. + line.substring(position.column);
  1455. var end = {
  1456. row : position.row,
  1457. column : position.column + text.length
  1458. };
  1459. var delta = {
  1460. action: "insertText",
  1461. range: Range.fromPoints(position, end),
  1462. text: text
  1463. };
  1464. this._emit("change", { data: delta });
  1465. return end;
  1466. };
  1467. this.remove = function(range) {
  1468. // clip to document
  1469. range.start = this.$clipPosition(range.start);
  1470. range.end = this.$clipPosition(range.end);
  1471. if (range.isEmpty())
  1472. return range.start;
  1473. var firstRow = range.start.row;
  1474. var lastRow = range.end.row;
  1475. if (range.isMultiLine()) {
  1476. var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1;
  1477. var lastFullRow = lastRow - 1;
  1478. if (range.end.column > 0)
  1479. this.removeInLine(lastRow, 0, range.end.column);
  1480. if (lastFullRow >= firstFullRow)
  1481. this.removeLines(firstFullRow, lastFullRow);
  1482. if (firstFullRow != firstRow) {
  1483. this.removeInLine(firstRow, range.start.column, this.getLine(firstRow).length);
  1484. this.removeNewLine(range.start.row);
  1485. }
  1486. }
  1487. else {
  1488. this.removeInLine(firstRow, range.start.column, range.end.column);
  1489. }
  1490. return range.start;
  1491. };
  1492. this.removeInLine = function(row, startColumn, endColumn) {
  1493. if (startColumn == endColumn)
  1494. return;
  1495. var range = new Range(row, startColumn, row, endColumn);
  1496. var line = this.getLine(row);
  1497. var removed = line.substring(startColumn, endColumn);
  1498. var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length);
  1499. this.$lines.splice(row, 1, newLine);
  1500. var delta = {
  1501. action: "removeText",
  1502. range: range,
  1503. text: removed
  1504. };
  1505. this._emit("change", { data: delta });
  1506. return range.start;
  1507. };
  1508. this.removeLines = function(firstRow, lastRow) {
  1509. var range = new Range(firstRow, 0, lastRow + 1, 0);
  1510. var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1);
  1511. var delta = {
  1512. action: "removeLines",
  1513. range: range,
  1514. nl: this.getNewLineCharacter(),
  1515. lines: removed
  1516. };
  1517. this._emit("change", { data: delta });
  1518. return removed;
  1519. };
  1520. this.removeNewLine = function(row) {
  1521. var firstLine = this.getLine(row);
  1522. var secondLine = this.getLine(row+1);
  1523. var range = new Range(row, firstLine.length, row+1, 0);
  1524. var line = firstLine + secondLine;
  1525. this.$lines.splice(row, 2, line);
  1526. var delta = {
  1527. action: "removeText",
  1528. range: range,
  1529. text: this.getNewLineCharacter()
  1530. };
  1531. this._emit("change", { data: delta });
  1532. };
  1533. this.replace = function(range, text) {
  1534. if (text.length == 0 && range.isEmpty())
  1535. return range.start;
  1536. // Shortcut: If the text we want to insert is the same as it is already
  1537. // in the document, we don't have to replace anything.
  1538. if (text == this.getTextRange(range))
  1539. return range.end;
  1540. this.remove(range);
  1541. if (text) {
  1542. var end = this.insert(range.start, text);
  1543. }
  1544. else {
  1545. end = range.start;
  1546. }
  1547. return end;
  1548. };
  1549. this.applyDeltas = function(deltas) {
  1550. for (var i=0; i<deltas.length; i++) {
  1551. var delta = deltas[i];
  1552. var range = Range.fromPoints(delta.range.start, delta.range.end);
  1553. if (delta.action == "insertLines")
  1554. this.insertLines(range.start.row, delta.lines);
  1555. else if (delta.action == "insertText")
  1556. this.insert(range.start, delta.text);
  1557. else if (delta.action == "removeLines")
  1558. this.removeLines(range.start.row, range.end.row - 1);
  1559. else if (delta.action == "removeText")
  1560. this.remove(range);
  1561. }
  1562. };
  1563. this.revertDeltas = function(deltas) {
  1564. for (var i=deltas.length-1; i>=0; i--) {
  1565. var delta = deltas[i];
  1566. var range = Range.fromPoints(delta.range.start, delta.range.end);
  1567. if (delta.action == "insertLines")
  1568. this.removeLines(range.start.row, range.end.row - 1);
  1569. else if (delta.action == "insertText")
  1570. this.remove(range);
  1571. else if (delta.action == "removeLines")
  1572. this.insertLines(range.start.row, delta.lines);
  1573. else if (delta.action == "removeText")
  1574. this.insert(range.start, delta.text);
  1575. }
  1576. };
  1577. }).call(Document.prototype);
  1578. exports.Document = Document;
  1579. });
  1580. define('ace/range', ['require', 'exports', 'module' ], function(require, exports, module) {
  1581. /**
  1582. * class Range
  1583. *
  1584. * This object is used in various places to indicate a region within the editor. To better visualize how this works, imagine a rectangle. Each quadrant of the rectangle is analogus to a range, as ranges contain a starting row and starting column, and an ending row, and ending column.
  1585. *
  1586. **/
  1587. /**
  1588. * new Range(startRow, startColumn, endRow, endColumn)
  1589. * - startRow (Number): The starting row
  1590. * - startColumn (Number): The starting column
  1591. * - endRow (Number): The ending row
  1592. * - endColumn (Number): The ending column
  1593. *
  1594. * Creates a new `Range` object with the given starting and ending row and column points.
  1595. *
  1596. **/
  1597. var Range = function(startRow, startColumn, endRow, endColumn) {
  1598. this.start = {
  1599. row: startRow,
  1600. column: startColumn
  1601. };
  1602. this.end = {
  1603. row: endRow,
  1604. column: endColumn
  1605. };
  1606. };
  1607. (function() {
  1608. /**
  1609. * Range.isEqual(range) -> Boolean
  1610. * - range (Range): A range to check against
  1611. *
  1612. * Returns `true` if and only if the starting row and column, and ending tow and column, are equivalent to those given by `range`.
  1613. *
  1614. **/
  1615. this.isEqual = function(range) {
  1616. return this.start.row == range.start.row &&
  1617. this.end.row == range.end.row &&
  1618. this.start.column == range.start.column &&
  1619. this.end.column == range.end.column
  1620. };
  1621. this.toString = function() {
  1622. return ("Range: [" + this.start.row + "/" + this.start.column +
  1623. "] -> [" + this.end.row + "/" + this.end.column + "]");
  1624. };
  1625. this.contains = function(row, column) {
  1626. return this.compare(row, column) == 0;
  1627. };
  1628. this.compareRange = function(range) {
  1629. var cmp,
  1630. end = range.end,
  1631. start = range.start;
  1632. cmp = this.compare(end.row, end.column);
  1633. if (cmp == 1) {
  1634. cmp = this.compare(start.row, start.column);
  1635. if (cmp == 1) {
  1636. return 2;
  1637. } else if (cmp == 0) {
  1638. return 1;
  1639. } else {
  1640. return 0;
  1641. }
  1642. } else if (cmp == -1) {
  1643. return -2;
  1644. } else {
  1645. cmp = this.compare(start.row, start.column);
  1646. if (cmp == -1) {
  1647. return -1;
  1648. } else if (cmp == 1) {
  1649. return 42;
  1650. } else {
  1651. return 0;
  1652. }
  1653. }
  1654. }
  1655. /** related to: Range.compare
  1656. * Range.comparePoint(p) -> Number
  1657. * - p (Range): A point to compare with
  1658. * + (Number): This method returns one of the following numbers:<br/>
  1659. * * `0` if the two points are exactly equal<br/>
  1660. * * `-1` if `p.row` is less then the calling range<br/>
  1661. * * `1` if `p.row` is greater than the calling range<br/>
  1662. * <br/>
  1663. * If the starting row of the calling range is equal to `p.row`, and:<br/>
  1664. * * `p.column` is greater than or equal to the calling range's starting column, this returns `0`<br/>
  1665. * * Otherwise, it returns -1<br/>
  1666. *<br/>
  1667. * If the ending row of the calling range is equal to `p.row`, and:<br/>
  1668. * * `p.column` is less than or equal to the calling range's ending column, this returns `0`<br/>
  1669. * * Otherwise, it returns 1<br/>
  1670. *
  1671. * Checks the row and column points of `p` with the row and column points of the calling range.
  1672. *
  1673. *
  1674. *
  1675. **/
  1676. this.comparePoint = function(p) {
  1677. return this.compare(p.row, p.column);
  1678. }
  1679. /** related to: Range.comparePoint
  1680. * Range.containsRange(range) -> Boolean
  1681. * - range (Range): A range to compare with
  1682. *
  1683. * Checks the start and end points of `range` and compares them to the calling range. Returns `true` if the `range` is contained within the caller's range.
  1684. *
  1685. **/
  1686. this.containsRange = function(range) {
  1687. return this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0;
  1688. }
  1689. /**
  1690. * Range.intersects(range) -> Boolean
  1691. * - range (Range): A range to compare with
  1692. *
  1693. * Returns `true` if passed in `range` intersects with the one calling this method.
  1694. *
  1695. **/
  1696. this.intersects = function(range) {
  1697. var cmp = this.compareRange(range);
  1698. return (cmp == -1 || cmp == 0 || cmp == 1);
  1699. }
  1700. /**
  1701. * Range.isEnd(row, column) -> Boolean
  1702. * - row (Number): A row point to compare with
  1703. * - column (Number): A column point to compare with
  1704. *
  1705. * Returns `true` if the caller's ending row point is the same as `row`, and if the caller's ending column is the same as `column`.
  1706. *
  1707. **/
  1708. this.isEnd = function(row, column) {
  1709. return this.end.row == row && this.end.column == column;
  1710. }
  1711. /**
  1712. * Range.isStart(row, column) -> Boolean
  1713. * - row (Number): A row point to compare with
  1714. * - column (Number): A column point to compare with
  1715. *
  1716. * Returns `true` if the caller's starting row point is the same as `row`, and if the caller's starting column is the same as `column`.
  1717. *
  1718. **/
  1719. this.isStart = function(row, column) {
  1720. return this.start.row == row && this.start.column == column;
  1721. }
  1722. /**
  1723. * Range.setStart(row, column)
  1724. * - row (Number): A row point to set
  1725. * - column (Number): A column point to set
  1726. *
  1727. * Sets the starting row and column for the range.
  1728. *
  1729. **/
  1730. this.setStart = function(row, column) {
  1731. if (typeof row == "object") {
  1732. this.start.column = row.column;
  1733. this.start.row = row.row;
  1734. } else {
  1735. this.start.row = row;
  1736. this.start.column = column;
  1737. }
  1738. }
  1739. /**
  1740. * Range.setEnd(row, column)
  1741. * - row (Number): A row point to set
  1742. * - column (Number): A column point to set
  1743. *
  1744. * Sets the starting row and column for the range.
  1745. *
  1746. **/
  1747. this.setEnd = function(row, column) {
  1748. if (typeof row == "object") {
  1749. this.end.column = row.column;
  1750. this.end.row = row.row;
  1751. } else {
  1752. this.end.row = row;
  1753. this.end.column = column;
  1754. }
  1755. }
  1756. /** related to: Range.compare
  1757. * Range.inside(row, column) -> Boolean
  1758. * - row (Number): A row point to compare with
  1759. * - column (Number): A column point to compare with
  1760. *
  1761. * Returns `true` if the `row` and `column` are within the given range.
  1762. *
  1763. **/
  1764. this.inside = function(row, column) {
  1765. if (this.compare(row, column) == 0) {
  1766. if (this.isEnd(row, column) || this.isStart(row, column)) {
  1767. return false;
  1768. } else {
  1769. return true;
  1770. }
  1771. }
  1772. return false;
  1773. }
  1774. /** related to: Range.compare
  1775. * Range.insideStart(row, column) -> Boolean
  1776. * - row (Number): A row point to compare with
  1777. * - column (Number): A column point to compare with
  1778. *
  1779. * Returns `true` if the `row` and `column` are within the given range's starting points.
  1780. *
  1781. **/
  1782. this.insideStart = function(row, column) {
  1783. if (this.compare(row, column) == 0) {
  1784. if (this.isEnd(row, column)) {
  1785. return false;
  1786. } else {
  1787. return true;
  1788. }
  1789. }
  1790. return false;
  1791. }
  1792. /** related to: Range.compare
  1793. * Range.insideEnd(row, column) -> Boolean
  1794. * - row (Number): A row point to compare with
  1795. * - column (Number): A column point to compare with
  1796. *
  1797. * Returns `true` if the `row` and `column` are within the given range's ending points.
  1798. *
  1799. **/
  1800. this.insideEnd = function(row, column) {
  1801. if (this.compare(row, column) == 0) {
  1802. if (this.isStart(row, column)) {
  1803. return false;
  1804. } else {
  1805. return true;
  1806. }
  1807. }
  1808. return false;
  1809. }
  1810. /**
  1811. * Range.compare(row, column) -> Number
  1812. * - row (Number): A row point to compare with
  1813. * - column (Number): A column point to compare with
  1814. * + (Number): This method returns one of the following numbers:<br/>
  1815. * * `0` if the two points are exactly equal <br/>
  1816. * * `-1` if `p.row` is less then the calling range <br/>
  1817. * * `1` if `p.row` is greater than the calling range <br/>
  1818. * <br/>
  1819. * If the starting row of the calling range is equal to `p.row`, and: <br/>
  1820. * * `p.column` is greater than or equal to the calling range's starting column, this returns `0`<br/>
  1821. * * Otherwise, it returns -1<br/>
  1822. * <br/>
  1823. * If the ending row of the calling range is equal to `p.row`, and: <br/>
  1824. * * `p.column` is less than or equal to the calling range's ending column, this returns `0` <br/>
  1825. * * Otherwise, it returns 1
  1826. *
  1827. * Checks the row and column points with the row and column points of the calling range.
  1828. *
  1829. *
  1830. **/
  1831. this.compare = function(row, column) {
  1832. if (!this.isMultiLine()) {
  1833. if (row === this.start.row) {
  1834. return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0);
  1835. };
  1836. }
  1837. if (row < this.start.row)
  1838. return -1;
  1839. if (row > this.end.row)
  1840. return 1;
  1841. if (this.start.row === row)
  1842. return column >= this.start.column ? 0 : -1;
  1843. if (this.end.row === row)
  1844. return column <= this.end.column ? 0 : 1;
  1845. return 0;
  1846. };
  1847. this.compareStart = function(row, column) {
  1848. if (this.start.row == row && this.start.column == column) {
  1849. return -1;
  1850. } else {
  1851. return this.compare(row, column);
  1852. }
  1853. }
  1854. /**
  1855. * Range.compareEnd(row, column) -> Number
  1856. * - row (Number): A row point to compare with
  1857. * - column (Number): A column point to compare with
  1858. * + (Number): This method returns one of the following numbers:<br/>
  1859. * * `0` if the two points are exactly equal<br/>
  1860. * * `-1` if `p.row` is less then the calling range<br/>
  1861. * * `1` if `p.row` is greater than the calling range, or if `isEnd` is `true.<br/>
  1862. * <br/>
  1863. * If the starting row of the calling range is equal to `p.row`, and:<br/>
  1864. * * `p.column` is greater than or equal to the calling range's starting column, this returns `0`<br/>
  1865. * * Otherwise, it returns -1<br/>
  1866. *<br/>
  1867. * If the ending row of the calling range is equal to `p.row`, and:<br/>
  1868. * * `p.column` is less than or equal to the calling range's ending column, this returns `0`<br/>
  1869. * * Otherwise, it returns 1
  1870. *
  1871. * Checks the row and column points with the row and column points of the calling range.
  1872. *
  1873. *
  1874. **/
  1875. this.compareEnd = function(row, column) {
  1876. if (this.end.row == row && this.end.column == column) {
  1877. return 1;
  1878. } else {
  1879. return this.compare(row, column);
  1880. }
  1881. }
  1882. /**
  1883. * Range.compareInside(row, column) -> Number
  1884. * - row (Number): A row point to compare with
  1885. * - column (Number): A column point to compare with
  1886. * + (Number): This method returns one of the following numbers:<br/>
  1887. * * `1` if the ending row of the calling range is equal to `row`, and the ending column of the calling range is equal to `column`<br/>
  1888. * * `-1` if the starting row of the calling range is equal to `row`, and the starting column of the calling range is equal to `column`<br/>
  1889. * <br/>
  1890. * Otherwise, it returns the value after calling [[Range.compare `compare()`]].
  1891. *
  1892. * Checks the row and column points with the row and column points of the calling range.
  1893. *
  1894. *
  1895. *
  1896. **/
  1897. this.compareInside = function(row, column) {
  1898. if (this.end.row == row && this.end.column == column) {
  1899. return 1;
  1900. } else if (this.start.row == row && this.start.column == column) {
  1901. return -1;
  1902. } else {
  1903. return this.compare(row, column);
  1904. }
  1905. }
  1906. /**
  1907. * Range.clipRows(firstRow, lastRow) -> Range
  1908. * - firstRow (Number): The starting row
  1909. * - lastRow (Number): The ending row
  1910. *
  1911. * Returns the part of the current `Range` that occurs within the boundaries of `firstRow` and `lastRow` as a new `Range` object.
  1912. *
  1913. **/
  1914. this.clipRows = function(firstRow, lastRow) {
  1915. if (this.end.row > lastRow) {
  1916. var end = {
  1917. row: lastRow+1,
  1918. column: 0
  1919. };
  1920. }
  1921. if (this.start.row > lastRow) {
  1922. var start = {
  1923. row: lastRow+1,
  1924. column: 0
  1925. };
  1926. }
  1927. if (this.start.row < firstRow) {
  1928. var start = {
  1929. row: firstRow,
  1930. column: 0
  1931. };
  1932. }
  1933. if (this.end.row < firstRow) {
  1934. var end = {
  1935. row: firstRow,
  1936. column: 0
  1937. };
  1938. }
  1939. return Range.fromPoints(start || this.start, end || this.end);
  1940. };
  1941. this.extend = function(row, column) {
  1942. var cmp = this.compare(row, column);
  1943. if (cmp == 0)
  1944. return this;
  1945. else if (cmp == -1)
  1946. var start = {row: row, column: column};
  1947. else
  1948. var end = {row: row, column: column};
  1949. return Range.fromPoints(start || this.start, end || this.end);
  1950. };
  1951. this.isEmpty = function() {
  1952. return (this.start.row == this.end.row && this.start.column == this.end.column);
  1953. };
  1954. this.isMultiLine = function() {
  1955. return (this.start.row !== this.end.row);
  1956. };
  1957. this.clone = function() {
  1958. return Range.fromPoints(this.start, this.end);
  1959. };
  1960. this.collapseRows = function() {
  1961. if (this.end.column == 0)
  1962. return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row-1), 0)
  1963. else
  1964. return new Range(this.start.row, 0, this.end.row, 0)
  1965. };
  1966. this.toScreenRange = function(session) {
  1967. var screenPosStart =
  1968. session.documentToScreenPosition(this.start);
  1969. var screenPosEnd =
  1970. session.documentToScreenPosition(this.end);
  1971. return new Range(
  1972. screenPosStart.row, screenPosStart.column,
  1973. screenPosEnd.row, screenPosEnd.column
  1974. );
  1975. };
  1976. }).call(Range.prototype);
  1977. Range.fromPoints = function(start, end) {
  1978. return new Range(start.row, start.column, end.row, end.column);
  1979. };
  1980. exports.Range = Range;
  1981. });
  1982. define('ace/anchor', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter'], function(require, exports, module) {
  1983. var oop = require("./lib/oop");
  1984. var EventEmitter = require("./lib/event_emitter").EventEmitter;
  1985. /**
  1986. * new Anchor(doc, row, column)
  1987. * - doc (Document): The document to associate with the anchor
  1988. * - row (Number): The starting row position
  1989. * - column (Number): The starting column position
  1990. *
  1991. * Creates a new `Anchor` and associates it with a document.
  1992. *
  1993. **/
  1994. var Anchor = exports.Anchor = function(doc, row, column) {
  1995. this.document = doc;
  1996. if (typeof column == "undefined")
  1997. this.setPosition(row.row, row.column);
  1998. else
  1999. this.setPosition(row, column);
  2000. this.$onChange = this.onChange.bind(this);
  2001. doc.on("change", this.$onChange);
  2002. };
  2003. (function() {
  2004. oop.implement(this, EventEmitter);
  2005. this.getPosition = function() {
  2006. return this.$clipPositionToDocument(this.row, this.column);
  2007. };
  2008. this.getDocument = function() {
  2009. return this.document;
  2010. };
  2011. this.onChange = function(e) {
  2012. var delta = e.data;
  2013. var range = delta.range;
  2014. if (range.start.row == range.end.row && range.start.row != this.row)
  2015. return;
  2016. if (range.start.row > this.row)
  2017. return;
  2018. if (range.start.row == this.row && range.start.column > this.column)
  2019. return;
  2020. var row = this.row;
  2021. var column = this.column;
  2022. if (delta.action === "insertText") {
  2023. if (range.start.row === row && range.start.column <= column) {
  2024. if (range.start.row === range.end.row) {
  2025. column += range.end.column - range.start.column;
  2026. }
  2027. else {
  2028. column -= range.start.column;
  2029. row += range.end.row - range.start.row;
  2030. }
  2031. }
  2032. else if (range.start.row !== range.end.row && range.start.row < row) {
  2033. row += range.end.row - range.start.row;
  2034. }
  2035. } else if (delta.action === "insertLines") {
  2036. if (range.start.row <= row) {
  2037. row += range.end.row - range.start.row;
  2038. }
  2039. }
  2040. else if (delta.action == "removeText") {
  2041. if (range.start.row == row && range.start.column < column) {
  2042. if (range.end.column >= column)
  2043. column = range.start.column;
  2044. else
  2045. column = Math.max(0, column - (range.end.column - range.start.column));
  2046. } else if (range.start.row !== range.end.row && range.start.row < row) {
  2047. if (range.end.row == row) {
  2048. column = Math.max(0, column - range.end.column) + range.start.column;
  2049. }
  2050. row -= (range.end.row - range.start.row);
  2051. }
  2052. else if (range.end.row == row) {
  2053. row -= range.end.row - range.start.row;
  2054. column = Math.max(0, column - range.end.column) + range.start.column;
  2055. }
  2056. } else if (delta.action == "removeLines") {
  2057. if (range.start.row <= row) {
  2058. if (range.end.row <= row)
  2059. row -= range.end.row - range.start.row;
  2060. else {
  2061. row = range.start.row;
  2062. column = 0;
  2063. }
  2064. }
  2065. }
  2066. this.setPosition(row, column, true);
  2067. };
  2068. this.setPosition = function(row, column, noClip) {
  2069. var pos;
  2070. if (noClip) {
  2071. pos = {
  2072. row: row,
  2073. column: column
  2074. };
  2075. }
  2076. else {
  2077. pos = this.$clipPositionToDocument(row, column);
  2078. }
  2079. if (this.row == pos.row && this.column == pos.column)
  2080. return;
  2081. var old = {
  2082. row: this.row,
  2083. column: this.column
  2084. };
  2085. this.row = pos.row;
  2086. this.column = pos.column;
  2087. this._emit("change", {
  2088. old: old,
  2089. value: pos
  2090. });
  2091. };
  2092. this.detach = function() {
  2093. this.document.removeEventListener("change", this.$onChange);
  2094. };
  2095. this.$clipPositionToDocument = function(row, column) {
  2096. var pos = {};
  2097. if (row >= this.document.getLength()) {
  2098. pos.row = Math.max(0, this.document.getLength() - 1);
  2099. pos.column = this.document.getLine(pos.row).length;
  2100. }
  2101. else if (row < 0) {
  2102. pos.row = 0;
  2103. pos.column = 0;
  2104. }
  2105. else {
  2106. pos.row = row;
  2107. pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column));
  2108. }
  2109. if (column < 0)
  2110. pos.column = 0;
  2111. return pos;
  2112. };
  2113. }).call(Anchor.prototype);
  2114. });
  2115. define('ace/lib/lang', ['require', 'exports', 'module' ], function(require, exports, module) {
  2116. exports.stringReverse = function(string) {
  2117. return string.split("").reverse().join("");
  2118. };
  2119. exports.stringRepeat = function (string, count) {
  2120. return new Array(count + 1).join(string);
  2121. };
  2122. var trimBeginRegexp = /^\s\s*/;
  2123. var trimEndRegexp = /\s\s*$/;
  2124. exports.stringTrimLeft = function (string) {
  2125. return string.replace(trimBeginRegexp, '');
  2126. };
  2127. exports.stringTrimRight = function (string) {
  2128. return string.replace(trimEndRegexp, '');
  2129. };
  2130. exports.copyObject = function(obj) {
  2131. var copy = {};
  2132. for (var key in obj) {
  2133. copy[key] = obj[key];
  2134. }
  2135. return copy;
  2136. };
  2137. exports.copyArray = function(array){
  2138. var copy = [];
  2139. for (var i=0, l=array.length; i<l; i++) {
  2140. if (array[i] && typeof array[i] == "object")
  2141. copy[i] = this.copyObject( array[i] );
  2142. else
  2143. copy[i] = array[i];
  2144. }
  2145. return copy;
  2146. };
  2147. exports.deepCopy = function (obj) {
  2148. if (typeof obj != "object") {
  2149. return obj;
  2150. }
  2151. var copy = obj.constructor();
  2152. for (var key in obj) {
  2153. if (typeof obj[key] == "object") {
  2154. copy[key] = this.deepCopy(obj[key]);
  2155. } else {
  2156. copy[key] = obj[key];
  2157. }
  2158. }
  2159. return copy;
  2160. };
  2161. exports.arrayToMap = function(arr) {
  2162. var map = {};
  2163. for (var i=0; i<arr.length; i++) {
  2164. map[arr[i]] = 1;
  2165. }
  2166. return map;
  2167. };
  2168. exports.arrayRemove = function(array, value) {
  2169. for (var i = 0; i <= array.length; i++) {
  2170. if (value === array[i]) {
  2171. array.splice(i, 1);
  2172. }
  2173. }
  2174. };
  2175. exports.escapeRegExp = function(str) {
  2176. return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
  2177. };
  2178. exports.getMatchOffsets = function(string, regExp) {
  2179. var matches = [];
  2180. string.replace(regExp, function(str) {
  2181. matches.push({
  2182. offset: arguments[arguments.length-2],
  2183. length: str.length
  2184. });
  2185. });
  2186. return matches;
  2187. };
  2188. exports.deferredCall = function(fcn) {
  2189. var timer = null;
  2190. var callback = function() {
  2191. timer = null;
  2192. fcn();
  2193. };
  2194. var deferred = function(timeout) {
  2195. deferred.cancel();
  2196. timer = setTimeout(callback, timeout || 0);
  2197. return deferred;
  2198. };
  2199. deferred.schedule = deferred;
  2200. deferred.call = function() {
  2201. this.cancel();
  2202. fcn();
  2203. return deferred;
  2204. };
  2205. deferred.cancel = function() {
  2206. clearTimeout(timer);
  2207. timer = null;
  2208. return deferred;
  2209. };
  2210. return deferred;
  2211. };
  2212. });
  2213. define('ace/mode/css/csslint', ['require', 'exports', 'module' ], function(require, exports, module) {
  2214. /*!
  2215. CSSLint
  2216. Copyright (c) 2011 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
  2217. Permission is hereby granted, free of charge, to any person obtaining a copy
  2218. of this software and associated documentation files (the "Software"), to deal
  2219. in the Software without restriction, including without limitation the rights
  2220. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  2221. copies of the Software, and to permit persons to whom the Software is
  2222. furnished to do so, subject to the following conditions:
  2223. The above copyright notice and this permission notice shall be included in
  2224. all copies or substantial portions of the Software.
  2225. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  2226. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  2227. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  2228. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  2229. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  2230. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  2231. THE SOFTWARE.
  2232. */
  2233. /* Build time: 2-March-2012 02:47:11 */
  2234. /*!
  2235. Parser-Lib
  2236. Copyright (c) 2009-2011 Nicholas C. Zakas. All rights reserved.
  2237. Permission is hereby granted, free of charge, to any person obtaining a copy
  2238. of this software and associated documentation files (the "Software"), to deal
  2239. in the Software without restriction, including without limitation the rights
  2240. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  2241. copies of the Software, and to permit persons to whom the Software is
  2242. furnished to do so, subject to the following conditions:
  2243. The above copyright notice and this permission notice shall be included in
  2244. all copies or substantial portions of the Software.
  2245. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  2246. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  2247. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  2248. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  2249. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  2250. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  2251. THE SOFTWARE.
  2252. */
  2253. /* Version v0.1.6, Build time: 2-March-2012 02:44:32 */
  2254. var parserlib = {};
  2255. (function(){
  2256. /**
  2257. * A generic base to inherit from for any object
  2258. * that needs event handling.
  2259. * @class EventTarget
  2260. * @constructor
  2261. */
  2262. function EventTarget(){
  2263. /**
  2264. * The array of listeners for various events.
  2265. * @type Object
  2266. * @property _listeners
  2267. * @private
  2268. */
  2269. this._listeners = {};
  2270. }
  2271. EventTarget.prototype = {
  2272. //restore constructor
  2273. constructor: EventTarget,
  2274. /**
  2275. * Adds a listener for a given event type.
  2276. * @param {String} type The type of event to add a listener for.
  2277. * @param {Function} listener The function to call when the event occurs.
  2278. * @return {void}
  2279. * @method addListener
  2280. */
  2281. addListener: function(type, listener){
  2282. if (!this._listeners[type]){
  2283. this._listeners[type] = [];
  2284. }
  2285. this._listeners[type].push(listener);
  2286. },
  2287. /**
  2288. * Fires an event based on the passed-in object.
  2289. * @param {Object|String} event An object with at least a 'type' attribute
  2290. * or a string indicating the event name.
  2291. * @return {void}
  2292. * @method fire
  2293. */
  2294. fire: function(event){
  2295. if (typeof event == "string"){
  2296. event = { type: event };
  2297. }
  2298. if (typeof event.target != "undefined"){
  2299. event.target = this;
  2300. }
  2301. if (typeof event.type == "undefined"){
  2302. throw new Error("Event object missing 'type' property.");
  2303. }
  2304. if (this._listeners[event.type]){
  2305. //create a copy of the array and use that so listeners can't chane
  2306. var listeners = this._listeners[event.type].concat();
  2307. for (var i=0, len=listeners.length; i < len; i++){
  2308. listeners[i].call(this, event);
  2309. }
  2310. }
  2311. },
  2312. /**
  2313. * Removes a listener for a given event type.
  2314. * @param {String} type The type of event to remove a listener from.
  2315. * @param {Function} listener The function to remove from the event.
  2316. * @return {void}
  2317. * @method removeListener
  2318. */
  2319. removeListener: function(type, listener){
  2320. if (this._listeners[type]){
  2321. var listeners = this._listeners[type];
  2322. for (var i=0, len=listeners.length; i < len; i++){
  2323. if (listeners[i] === listener){
  2324. listeners.splice(i, 1);
  2325. break;
  2326. }
  2327. }
  2328. }
  2329. }
  2330. };
  2331. function StringReader(text){
  2332. /**
  2333. * The input text with line endings normalized.
  2334. * @property _input
  2335. * @type String
  2336. * @private
  2337. */
  2338. this._input = text.replace(/\n\r?/g, "\n");
  2339. this._line = 1;
  2340. this._col = 1;
  2341. this._cursor = 0;
  2342. }
  2343. StringReader.prototype = {
  2344. //restore constructor
  2345. constructor: StringReader,
  2346. //-------------------------------------------------------------------------
  2347. // Position info
  2348. //-------------------------------------------------------------------------
  2349. /**
  2350. * Returns the column of the character to be read next.
  2351. * @return {int} The column of the character to be read next.
  2352. * @method getCol
  2353. */
  2354. getCol: function(){
  2355. return this._col;
  2356. },
  2357. /**
  2358. * Returns the row of the character to be read next.
  2359. * @return {int} The row of the character to be read next.
  2360. * @method getLine
  2361. */
  2362. getLine: function(){
  2363. return this._line ;
  2364. },
  2365. /**
  2366. * Determines if you're at the end of the input.
  2367. * @return {Boolean} True if there's no more input, false otherwise.
  2368. * @method eof
  2369. */
  2370. eof: function(){
  2371. return (this._cursor == this._input.length);
  2372. },
  2373. //-------------------------------------------------------------------------
  2374. // Basic reading
  2375. //-------------------------------------------------------------------------
  2376. /**
  2377. * Reads the next character without advancing the cursor.
  2378. * @param {int} count How many characters to look ahead (default is 1).
  2379. * @return {String} The next character or null if there is no next character.
  2380. * @method peek
  2381. */
  2382. peek: function(count){
  2383. var c = null;
  2384. count = (typeof count == "undefined" ? 1 : count);
  2385. //if we're not at the end of the input...
  2386. if (this._cursor < this._input.length){
  2387. //get character and increment cursor and column
  2388. c = this._input.charAt(this._cursor + count - 1);
  2389. }
  2390. return c;
  2391. },
  2392. /**
  2393. * Reads the next character from the input and adjusts the row and column
  2394. * accordingly.
  2395. * @return {String} The next character or null if there is no next character.
  2396. * @method read
  2397. */
  2398. read: function(){
  2399. var c = null;
  2400. //if we're not at the end of the input...
  2401. if (this._cursor < this._input.length){
  2402. //if the last character was a newline, increment row count
  2403. //and reset column count
  2404. if (this._input.charAt(this._cursor) == "\n"){
  2405. this._line++;
  2406. this._col=1;
  2407. } else {
  2408. this._col++;
  2409. }
  2410. //get character and increment cursor and column
  2411. c = this._input.charAt(this._cursor++);
  2412. }
  2413. return c;
  2414. },
  2415. //-------------------------------------------------------------------------
  2416. // Misc
  2417. //-------------------------------------------------------------------------
  2418. /**
  2419. * Saves the current location so it can be returned to later.
  2420. * @method mark
  2421. * @return {void}
  2422. */
  2423. mark: function(){
  2424. this._bookmark = {
  2425. cursor: this._cursor,
  2426. line: this._line,
  2427. col: this._col
  2428. };
  2429. },
  2430. reset: function(){
  2431. if (this._bookmark){
  2432. this._cursor = this._bookmark.cursor;
  2433. this._line = this._bookmark.line;
  2434. this._col = this._bookmark.col;
  2435. delete this._bookmark;
  2436. }
  2437. },
  2438. //-------------------------------------------------------------------------
  2439. // Advanced reading
  2440. //-------------------------------------------------------------------------
  2441. /**
  2442. * Reads up to and including the given string. Throws an error if that
  2443. * string is not found.
  2444. * @param {String} pattern The string to read.
  2445. * @return {String} The string when it is found.
  2446. * @throws Error when the string pattern is not found.
  2447. * @method readTo
  2448. */
  2449. readTo: function(pattern){
  2450. var buffer = "",
  2451. c;
  2452. while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) != buffer.length - pattern.length){
  2453. c = this.read();
  2454. if (c){
  2455. buffer += c;
  2456. } else {
  2457. throw new Error("Expected \"" + pattern + "\" at line " + this._line + ", col " + this._col + ".");
  2458. }
  2459. }
  2460. return buffer;
  2461. },
  2462. /**
  2463. * Reads characters while each character causes the given
  2464. * filter function to return true. The function is passed
  2465. * in each character and either returns true to continue
  2466. * reading or false to stop.
  2467. * @param {Function} filter The function to read on each character.
  2468. * @return {String} The string made up of all characters that passed the
  2469. * filter check.
  2470. * @method readWhile
  2471. */
  2472. readWhile: function(filter){
  2473. var buffer = "",
  2474. c = this.read();
  2475. while(c !== null && filter(c)){
  2476. buffer += c;
  2477. c = this.read();
  2478. }
  2479. return buffer;
  2480. },
  2481. /**
  2482. * Reads characters that match either text or a regular expression and
  2483. * returns those characters. If a match is found, the row and column
  2484. * are adjusted; if no match is found, the reader's state is unchanged.
  2485. * reading or false to stop.
  2486. * @param {String|RegExp} matchter If a string, then the literal string
  2487. * value is searched for. If a regular expression, then any string
  2488. * matching the pattern is search for.
  2489. * @return {String} The string made up of all characters that matched or
  2490. * null if there was no match.
  2491. * @method readMatch
  2492. */
  2493. readMatch: function(matcher){
  2494. var source = this._input.substring(this._cursor),
  2495. value = null;
  2496. //if it's a string, just do a straight match
  2497. if (typeof matcher == "string"){
  2498. if (source.indexOf(matcher) === 0){
  2499. value = this.readCount(matcher.length);
  2500. }
  2501. } else if (matcher instanceof RegExp){
  2502. if (matcher.test(source)){
  2503. value = this.readCount(RegExp.lastMatch.length);
  2504. }
  2505. }
  2506. return value;
  2507. },
  2508. /**
  2509. * Reads a given number of characters. If the end of the input is reached,
  2510. * it reads only the remaining characters and does not throw an error.
  2511. * @param {int} count The number of characters to read.
  2512. * @return {String} The string made up the read characters.
  2513. * @method readCount
  2514. */
  2515. readCount: function(count){
  2516. var buffer = "";
  2517. while(count--){
  2518. buffer += this.read();
  2519. }
  2520. return buffer;
  2521. }
  2522. };
  2523. function SyntaxError(message, line, col){
  2524. /**
  2525. * The column at which the error occurred.
  2526. * @type int
  2527. * @property col
  2528. */
  2529. this.col = col;
  2530. this.line = line;
  2531. this.message = message;
  2532. }
  2533. //inherit from Error
  2534. SyntaxError.prototype = new Error();
  2535. function SyntaxUnit(text, line, col, type){
  2536. /**
  2537. * The column of text on which the unit resides.
  2538. * @type int
  2539. * @property col
  2540. */
  2541. this.col = col;
  2542. this.line = line;
  2543. this.text = text;
  2544. this.type = type;
  2545. }
  2546. /**
  2547. * Create a new syntax unit based solely on the given token.
  2548. * Convenience method for creating a new syntax unit when
  2549. * it represents a single token instead of multiple.
  2550. * @param {Object} token The token object to represent.
  2551. * @return {parserlib.util.SyntaxUnit} The object representing the token.
  2552. * @static
  2553. * @method fromToken
  2554. */
  2555. SyntaxUnit.fromToken = function(token){
  2556. return new SyntaxUnit(token.value, token.startLine, token.startCol);
  2557. };
  2558. SyntaxUnit.prototype = {
  2559. //restore constructor
  2560. constructor: SyntaxUnit,
  2561. /**
  2562. * Returns the text representation of the unit.
  2563. * @return {String} The text representation of the unit.
  2564. * @method valueOf
  2565. */
  2566. valueOf: function(){
  2567. return this.toString();
  2568. },
  2569. /**
  2570. * Returns the text representation of the unit.
  2571. * @return {String} The text representation of the unit.
  2572. * @method toString
  2573. */
  2574. toString: function(){
  2575. return this.text;
  2576. }
  2577. };
  2578. /**
  2579. * Generic TokenStream providing base functionality.
  2580. * @class TokenStreamBase
  2581. * @namespace parserlib.util
  2582. * @constructor
  2583. * @param {String|StringReader} input The text to tokenize or a reader from
  2584. * which to read the input.
  2585. */
  2586. function TokenStreamBase(input, tokenData){
  2587. /**
  2588. * The string reader for easy access to the text.
  2589. * @type StringReader
  2590. * @property _reader
  2591. * @private
  2592. */
  2593. this._reader = input ? new StringReader(input.toString()) : null;
  2594. this._token = null;
  2595. this._tokenData = tokenData;
  2596. this._lt = [];
  2597. this._ltIndex = 0;
  2598. this._ltIndexCache = [];
  2599. }
  2600. /**
  2601. * Accepts an array of token information and outputs
  2602. * an array of token data containing key-value mappings
  2603. * and matching functions that the TokenStream needs.
  2604. * @param {Array} tokens An array of token descriptors.
  2605. * @return {Array} An array of processed token data.
  2606. * @method createTokenData
  2607. * @static
  2608. */
  2609. TokenStreamBase.createTokenData = function(tokens){
  2610. var nameMap = [],
  2611. typeMap = {},
  2612. tokenData = tokens.concat([]),
  2613. i = 0,
  2614. len = tokenData.length+1;
  2615. tokenData.UNKNOWN = -1;
  2616. tokenData.unshift({name:"EOF"});
  2617. for (; i < len; i++){
  2618. nameMap.push(tokenData[i].name);
  2619. tokenData[tokenData[i].name] = i;
  2620. if (tokenData[i].text){
  2621. typeMap[tokenData[i].text] = i;
  2622. }
  2623. }
  2624. tokenData.name = function(tt){
  2625. return nameMap[tt];
  2626. };
  2627. tokenData.type = function(c){
  2628. return typeMap[c];
  2629. };
  2630. return tokenData;
  2631. };
  2632. TokenStreamBase.prototype = {
  2633. //restore constructor
  2634. constructor: TokenStreamBase,
  2635. //-------------------------------------------------------------------------
  2636. // Matching methods
  2637. //-------------------------------------------------------------------------
  2638. /**
  2639. * Determines if the next token matches the given token type.
  2640. * If so, that token is consumed; if not, the token is placed
  2641. * back onto the token stream. You can pass in any number of
  2642. * token types and this will return true if any of the token
  2643. * types is found.
  2644. * @param {int|int[]} tokenTypes Either a single token type or an array of
  2645. * token types that the next token might be. If an array is passed,
  2646. * it's assumed that the token can be any of these.
  2647. * @param {variant} channel (Optional) The channel to read from. If not
  2648. * provided, reads from the default (unnamed) channel.
  2649. * @return {Boolean} True if the token type matches, false if not.
  2650. * @method match
  2651. */
  2652. match: function(tokenTypes, channel){
  2653. //always convert to an array, makes things easier
  2654. if (!(tokenTypes instanceof Array)){
  2655. tokenTypes = [tokenTypes];
  2656. }
  2657. var tt = this.get(channel),
  2658. i = 0,
  2659. len = tokenTypes.length;
  2660. while(i < len){
  2661. if (tt == tokenTypes[i++]){
  2662. return true;
  2663. }
  2664. }
  2665. //no match found, put the token back
  2666. this.unget();
  2667. return false;
  2668. },
  2669. /**
  2670. * Determines if the next token matches the given token type.
  2671. * If so, that token is consumed; if not, an error is thrown.
  2672. * @param {int|int[]} tokenTypes Either a single token type or an array of
  2673. * token types that the next token should be. If an array is passed,
  2674. * it's assumed that the token must be one of these.
  2675. * @param {variant} channel (Optional) The channel to read from. If not
  2676. * provided, reads from the default (unnamed) channel.
  2677. * @return {void}
  2678. * @method mustMatch
  2679. */
  2680. mustMatch: function(tokenTypes, channel){
  2681. var token;
  2682. //always convert to an array, makes things easier
  2683. if (!(tokenTypes instanceof Array)){
  2684. tokenTypes = [tokenTypes];
  2685. }
  2686. if (!this.match.apply(this, arguments)){
  2687. token = this.LT(1);
  2688. throw new SyntaxError("Expected " + this._tokenData[tokenTypes[0]].name +
  2689. " at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
  2690. }
  2691. },
  2692. //-------------------------------------------------------------------------
  2693. // Consuming methods
  2694. //-------------------------------------------------------------------------
  2695. /**
  2696. * Keeps reading from the token stream until either one of the specified
  2697. * token types is found or until the end of the input is reached.
  2698. * @param {int|int[]} tokenTypes Either a single token type or an array of
  2699. * token types that the next token should be. If an array is passed,
  2700. * it's assumed that the token must be one of these.
  2701. * @param {variant} channel (Optional) The channel to read from. If not
  2702. * provided, reads from the default (unnamed) channel.
  2703. * @return {void}
  2704. * @method advance
  2705. */
  2706. advance: function(tokenTypes, channel){
  2707. while(this.LA(0) !== 0 && !this.match(tokenTypes, channel)){
  2708. this.get();
  2709. }
  2710. return this.LA(0);
  2711. },
  2712. /**
  2713. * Consumes the next token from the token stream.
  2714. * @return {int} The token type of the token that was just consumed.
  2715. * @method get
  2716. */
  2717. get: function(channel){
  2718. var tokenInfo = this._tokenData,
  2719. reader = this._reader,
  2720. value,
  2721. i =0,
  2722. len = tokenInfo.length,
  2723. found = false,
  2724. token,
  2725. info;
  2726. //check the lookahead buffer first
  2727. if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length){
  2728. i++;
  2729. this._token = this._lt[this._ltIndex++];
  2730. info = tokenInfo[this._token.type];
  2731. //obey channels logic
  2732. while((info.channel !== undefined && channel !== info.channel) &&
  2733. this._ltIndex < this._lt.length){
  2734. this._token = this._lt[this._ltIndex++];
  2735. info = tokenInfo[this._token.type];
  2736. i++;
  2737. }
  2738. //here be dragons
  2739. if ((info.channel === undefined || channel === info.channel) &&
  2740. this._ltIndex <= this._lt.length){
  2741. this._ltIndexCache.push(i);
  2742. return this._token.type;
  2743. }
  2744. }
  2745. //call token retriever method
  2746. token = this._getToken();
  2747. //if it should be hidden, don't save a token
  2748. if (token.type > -1 && !tokenInfo[token.type].hide){
  2749. //apply token channel
  2750. token.channel = tokenInfo[token.type].channel;
  2751. //save for later
  2752. this._token = token;
  2753. this._lt.push(token);
  2754. //save space that will be moved (must be done before array is truncated)
  2755. this._ltIndexCache.push(this._lt.length - this._ltIndex + i);
  2756. //keep the buffer under 5 items
  2757. if (this._lt.length > 5){
  2758. this._lt.shift();
  2759. }
  2760. //also keep the shift buffer under 5 items
  2761. if (this._ltIndexCache.length > 5){
  2762. this._ltIndexCache.shift();
  2763. }
  2764. //update lookahead index
  2765. this._ltIndex = this._lt.length;
  2766. }
  2767. /*
  2768. * Skip to the next token if:
  2769. * 1. The token type is marked as hidden.
  2770. * 2. The token type has a channel specified and it isn't the current channel.
  2771. */
  2772. info = tokenInfo[token.type];
  2773. if (info &&
  2774. (info.hide ||
  2775. (info.channel !== undefined && channel !== info.channel))){
  2776. return this.get(channel);
  2777. } else {
  2778. //return just the type
  2779. return token.type;
  2780. }
  2781. },
  2782. /**
  2783. * Looks ahead a certain number of tokens and returns the token type at
  2784. * that position. This will throw an error if you lookahead past the
  2785. * end of input, past the size of the lookahead buffer, or back past
  2786. * the first token in the lookahead buffer.
  2787. * @param {int} The index of the token type to retrieve. 0 for the
  2788. * current token, 1 for the next, -1 for the previous, etc.
  2789. * @return {int} The token type of the token in the given position.
  2790. * @method LA
  2791. */
  2792. LA: function(index){
  2793. var total = index,
  2794. tt;
  2795. if (index > 0){
  2796. //TODO: Store 5 somewhere
  2797. if (index > 5){
  2798. throw new Error("Too much lookahead.");
  2799. }
  2800. //get all those tokens
  2801. while(total){
  2802. tt = this.get();
  2803. total--;
  2804. }
  2805. //unget all those tokens
  2806. while(total < index){
  2807. this.unget();
  2808. total++;
  2809. }
  2810. } else if (index < 0){
  2811. if(this._lt[this._ltIndex+index]){
  2812. tt = this._lt[this._ltIndex+index].type;
  2813. } else {
  2814. throw new Error("Too much lookbehind.");
  2815. }
  2816. } else {
  2817. tt = this._token.type;
  2818. }
  2819. return tt;
  2820. },
  2821. /**
  2822. * Looks ahead a certain number of tokens and returns the token at
  2823. * that position. This will throw an error if you lookahead past the
  2824. * end of input, past the size of the lookahead buffer, or back past
  2825. * the first token in the lookahead buffer.
  2826. * @param {int} The index of the token type to retrieve. 0 for the
  2827. * current token, 1 for the next, -1 for the previous, etc.
  2828. * @return {Object} The token of the token in the given position.
  2829. * @method LA
  2830. */
  2831. LT: function(index){
  2832. //lookahead first to prime the token buffer
  2833. this.LA(index);
  2834. //now find the token, subtract one because _ltIndex is already at the next index
  2835. return this._lt[this._ltIndex+index-1];
  2836. },
  2837. /**
  2838. * Returns the token type for the next token in the stream without
  2839. * consuming it.
  2840. * @return {int} The token type of the next token in the stream.
  2841. * @method peek
  2842. */
  2843. peek: function(){
  2844. return this.LA(1);
  2845. },
  2846. /**
  2847. * Returns the actual token object for the last consumed token.
  2848. * @return {Token} The token object for the last consumed token.
  2849. * @method token
  2850. */
  2851. token: function(){
  2852. return this._token;
  2853. },
  2854. /**
  2855. * Returns the name of the token for the given token type.
  2856. * @param {int} tokenType The type of token to get the name of.
  2857. * @return {String} The name of the token or "UNKNOWN_TOKEN" for any
  2858. * invalid token type.
  2859. * @method tokenName
  2860. */
  2861. tokenName: function(tokenType){
  2862. if (tokenType < 0 || tokenType > this._tokenData.length){
  2863. return "UNKNOWN_TOKEN";
  2864. } else {
  2865. return this._tokenData[tokenType].name;
  2866. }
  2867. },
  2868. /**
  2869. * Returns the token type value for the given token name.
  2870. * @param {String} tokenName The name of the token whose value should be returned.
  2871. * @return {int} The token type value for the given token name or -1
  2872. * for an unknown token.
  2873. * @method tokenName
  2874. */
  2875. tokenType: function(tokenName){
  2876. return this._tokenData[tokenName] || -1;
  2877. },
  2878. /**
  2879. * Returns the last consumed token to the token stream.
  2880. * @method unget
  2881. */
  2882. unget: function(){
  2883. //if (this._ltIndex > -1){
  2884. if (this._ltIndexCache.length){
  2885. this._ltIndex -= this._ltIndexCache.pop();//--;
  2886. this._token = this._lt[this._ltIndex - 1];
  2887. } else {
  2888. throw new Error("Too much lookahead.");
  2889. }
  2890. }
  2891. };
  2892. parserlib.util = {
  2893. StringReader: StringReader,
  2894. SyntaxError : SyntaxError,
  2895. SyntaxUnit : SyntaxUnit,
  2896. EventTarget : EventTarget,
  2897. TokenStreamBase : TokenStreamBase
  2898. };
  2899. })();
  2900. /* Version v0.1.6, Build time: 2-March-2012 02:44:32 */
  2901. (function(){
  2902. var EventTarget = parserlib.util.EventTarget,
  2903. TokenStreamBase = parserlib.util.TokenStreamBase,
  2904. StringReader = parserlib.util.StringReader,
  2905. SyntaxError = parserlib.util.SyntaxError,
  2906. SyntaxUnit = parserlib.util.SyntaxUnit;
  2907. var Colors = {
  2908. aliceblue :"#f0f8ff",
  2909. antiquewhite :"#faebd7",
  2910. aqua :"#00ffff",
  2911. aquamarine :"#7fffd4",
  2912. azure :"#f0ffff",
  2913. beige :"#f5f5dc",
  2914. bisque :"#ffe4c4",
  2915. black :"#000000",
  2916. blanchedalmond :"#ffebcd",
  2917. blue :"#0000ff",
  2918. blueviolet :"#8a2be2",
  2919. brown :"#a52a2a",
  2920. burlywood :"#deb887",
  2921. cadetblue :"#5f9ea0",
  2922. chartreuse :"#7fff00",
  2923. chocolate :"#d2691e",
  2924. coral :"#ff7f50",
  2925. cornflowerblue :"#6495ed",
  2926. cornsilk :"#fff8dc",
  2927. crimson :"#dc143c",
  2928. cyan :"#00ffff",
  2929. darkblue :"#00008b",
  2930. darkcyan :"#008b8b",
  2931. darkgoldenrod :"#b8860b",
  2932. darkgray :"#a9a9a9",
  2933. darkgreen :"#006400",
  2934. darkkhaki :"#bdb76b",
  2935. darkmagenta :"#8b008b",
  2936. darkolivegreen :"#556b2f",
  2937. darkorange :"#ff8c00",
  2938. darkorchid :"#9932cc",
  2939. darkred :"#8b0000",
  2940. darksalmon :"#e9967a",
  2941. darkseagreen :"#8fbc8f",
  2942. darkslateblue :"#483d8b",
  2943. darkslategray :"#2f4f4f",
  2944. darkturquoise :"#00ced1",
  2945. darkviolet :"#9400d3",
  2946. deeppink :"#ff1493",
  2947. deepskyblue :"#00bfff",
  2948. dimgray :"#696969",
  2949. dodgerblue :"#1e90ff",
  2950. firebrick :"#b22222",
  2951. floralwhite :"#fffaf0",
  2952. forestgreen :"#228b22",
  2953. fuchsia :"#ff00ff",
  2954. gainsboro :"#dcdcdc",
  2955. ghostwhite :"#f8f8ff",
  2956. gold :"#ffd700",
  2957. goldenrod :"#daa520",
  2958. gray :"#808080",
  2959. green :"#008000",
  2960. greenyellow :"#adff2f",
  2961. honeydew :"#f0fff0",
  2962. hotpink :"#ff69b4",
  2963. indianred :"#cd5c5c",
  2964. indigo :"#4b0082",
  2965. ivory :"#fffff0",
  2966. khaki :"#f0e68c",
  2967. lavender :"#e6e6fa",
  2968. lavenderblush :"#fff0f5",
  2969. lawngreen :"#7cfc00",
  2970. lemonchiffon :"#fffacd",
  2971. lightblue :"#add8e6",
  2972. lightcoral :"#f08080",
  2973. lightcyan :"#e0ffff",
  2974. lightgoldenrodyellow :"#fafad2",
  2975. lightgray :"#d3d3d3",
  2976. lightgreen :"#90ee90",
  2977. lightpink :"#ffb6c1",
  2978. lightsalmon :"#ffa07a",
  2979. lightseagreen :"#20b2aa",
  2980. lightskyblue :"#87cefa",
  2981. lightslategray :"#778899",
  2982. lightsteelblue :"#b0c4de",
  2983. lightyellow :"#ffffe0",
  2984. lime :"#00ff00",
  2985. limegreen :"#32cd32",
  2986. linen :"#faf0e6",
  2987. magenta :"#ff00ff",
  2988. maroon :"#800000",
  2989. mediumaquamarine:"#66cdaa",
  2990. mediumblue :"#0000cd",
  2991. mediumorchid :"#ba55d3",
  2992. mediumpurple :"#9370d8",
  2993. mediumseagreen :"#3cb371",
  2994. mediumslateblue :"#7b68ee",
  2995. mediumspringgreen :"#00fa9a",
  2996. mediumturquoise :"#48d1cc",
  2997. mediumvioletred :"#c71585",
  2998. midnightblue :"#191970",
  2999. mintcream :"#f5fffa",
  3000. mistyrose :"#ffe4e1",
  3001. moccasin :"#ffe4b5",
  3002. navajowhite :"#ffdead",
  3003. navy :"#000080",
  3004. oldlace :"#fdf5e6",
  3005. olive :"#808000",
  3006. olivedrab :"#6b8e23",
  3007. orange :"#ffa500",
  3008. orangered :"#ff4500",
  3009. orchid :"#da70d6",
  3010. palegoldenrod :"#eee8aa",
  3011. palegreen :"#98fb98",
  3012. paleturquoise :"#afeeee",
  3013. palevioletred :"#d87093",
  3014. papayawhip :"#ffefd5",
  3015. peachpuff :"#ffdab9",
  3016. peru :"#cd853f",
  3017. pink :"#ffc0cb",
  3018. plum :"#dda0dd",
  3019. powderblue :"#b0e0e6",
  3020. purple :"#800080",
  3021. red :"#ff0000",
  3022. rosybrown :"#bc8f8f",
  3023. royalblue :"#4169e1",
  3024. saddlebrown :"#8b4513",
  3025. salmon :"#fa8072",
  3026. sandybrown :"#f4a460",
  3027. seagreen :"#2e8b57",
  3028. seashell :"#fff5ee",
  3029. sienna :"#a0522d",
  3030. silver :"#c0c0c0",
  3031. skyblue :"#87ceeb",
  3032. slateblue :"#6a5acd",
  3033. slategray :"#708090",
  3034. snow :"#fffafa",
  3035. springgreen :"#00ff7f",
  3036. steelblue :"#4682b4",
  3037. tan :"#d2b48c",
  3038. teal :"#008080",
  3039. thistle :"#d8bfd8",
  3040. tomato :"#ff6347",
  3041. turquoise :"#40e0d0",
  3042. violet :"#ee82ee",
  3043. wheat :"#f5deb3",
  3044. white :"#ffffff",
  3045. whitesmoke :"#f5f5f5",
  3046. yellow :"#ffff00",
  3047. yellowgreen :"#9acd32"
  3048. };
  3049. /**
  3050. * Represents a selector combinator (whitespace, +, >).
  3051. * @namespace parserlib.css
  3052. * @class Combinator
  3053. * @extends parserlib.util.SyntaxUnit
  3054. * @constructor
  3055. * @param {String} text The text representation of the unit.
  3056. * @param {int} line The line of text on which the unit resides.
  3057. * @param {int} col The column of text on which the unit resides.
  3058. */
  3059. function Combinator(text, line, col){
  3060. SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE);
  3061. this.type = "unknown";
  3062. //pretty simple
  3063. if (/^\s+$/.test(text)){
  3064. this.type = "descendant";
  3065. } else if (text == ">"){
  3066. this.type = "child";
  3067. } else if (text == "+"){
  3068. this.type = "adjacent-sibling";
  3069. } else if (text == "~"){
  3070. this.type = "sibling";
  3071. }
  3072. }
  3073. Combinator.prototype = new SyntaxUnit();
  3074. Combinator.prototype.constructor = Combinator;
  3075. /**
  3076. * Represents a media feature, such as max-width:500.
  3077. * @namespace parserlib.css
  3078. * @class MediaFeature
  3079. * @extends parserlib.util.SyntaxUnit
  3080. * @constructor
  3081. * @param {SyntaxUnit} name The name of the feature.
  3082. * @param {SyntaxUnit} value The value of the feature or null if none.
  3083. */
  3084. function MediaFeature(name, value){
  3085. SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE);
  3086. this.name = name;
  3087. this.value = value;
  3088. }
  3089. MediaFeature.prototype = new SyntaxUnit();
  3090. MediaFeature.prototype.constructor = MediaFeature;
  3091. /**
  3092. * Represents an individual media query.
  3093. * @namespace parserlib.css
  3094. * @class MediaQuery
  3095. * @extends parserlib.util.SyntaxUnit
  3096. * @constructor
  3097. * @param {String} modifier The modifier "not" or "only" (or null).
  3098. * @param {String} mediaType The type of media (i.e., "print").
  3099. * @param {Array} parts Array of selectors parts making up this selector.
  3100. * @param {int} line The line of text on which the unit resides.
  3101. * @param {int} col The column of text on which the unit resides.
  3102. */
  3103. function MediaQuery(modifier, mediaType, features, line, col){
  3104. SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType + " " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE);
  3105. this.modifier = modifier;
  3106. this.mediaType = mediaType;
  3107. this.features = features;
  3108. }
  3109. MediaQuery.prototype = new SyntaxUnit();
  3110. MediaQuery.prototype.constructor = MediaQuery;
  3111. /**
  3112. * A CSS3 parser.
  3113. * @namespace parserlib.css
  3114. * @class Parser
  3115. * @constructor
  3116. * @param {Object} options (Optional) Various options for the parser:
  3117. * starHack (true|false) to allow IE6 star hack as valid,
  3118. * underscoreHack (true|false) to interpret leading underscores
  3119. * as IE6-7 targeting for known properties, ieFilters (true|false)
  3120. * to indicate that IE < 8 filters should be accepted and not throw
  3121. * syntax errors.
  3122. */
  3123. function Parser(options){
  3124. //inherit event functionality
  3125. EventTarget.call(this);
  3126. this.options = options || {};
  3127. this._tokenStream = null;
  3128. }
  3129. //Static constants
  3130. Parser.DEFAULT_TYPE = 0;
  3131. Parser.COMBINATOR_TYPE = 1;
  3132. Parser.MEDIA_FEATURE_TYPE = 2;
  3133. Parser.MEDIA_QUERY_TYPE = 3;
  3134. Parser.PROPERTY_NAME_TYPE = 4;
  3135. Parser.PROPERTY_VALUE_TYPE = 5;
  3136. Parser.PROPERTY_VALUE_PART_TYPE = 6;
  3137. Parser.SELECTOR_TYPE = 7;
  3138. Parser.SELECTOR_PART_TYPE = 8;
  3139. Parser.SELECTOR_SUB_PART_TYPE = 9;
  3140. Parser.prototype = function(){
  3141. var proto = new EventTarget(), //new prototype
  3142. prop,
  3143. additions = {
  3144. //restore constructor
  3145. constructor: Parser,
  3146. //instance constants - yuck
  3147. DEFAULT_TYPE : 0,
  3148. COMBINATOR_TYPE : 1,
  3149. MEDIA_FEATURE_TYPE : 2,
  3150. MEDIA_QUERY_TYPE : 3,
  3151. PROPERTY_NAME_TYPE : 4,
  3152. PROPERTY_VALUE_TYPE : 5,
  3153. PROPERTY_VALUE_PART_TYPE : 6,
  3154. SELECTOR_TYPE : 7,
  3155. SELECTOR_PART_TYPE : 8,
  3156. SELECTOR_SUB_PART_TYPE : 9,
  3157. //-----------------------------------------------------------------
  3158. // Grammar
  3159. //-----------------------------------------------------------------
  3160. _stylesheet: function(){
  3161. /*
  3162. * stylesheet
  3163. * : [ CHARSET_SYM S* STRING S* ';' ]?
  3164. * [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
  3165. * [ namespace [S|CDO|CDC]* ]*
  3166. * [ [ ruleset | media | page | font_face | keyframes ] [S|CDO|CDC]* ]*
  3167. * ;
  3168. */
  3169. var tokenStream = this._tokenStream,
  3170. charset = null,
  3171. count,
  3172. token,
  3173. tt;
  3174. this.fire("startstylesheet");
  3175. //try to read character set
  3176. this._charset();
  3177. this._skipCruft();
  3178. //try to read imports - may be more than one
  3179. while (tokenStream.peek() == Tokens.IMPORT_SYM){
  3180. this._import();
  3181. this._skipCruft();
  3182. }
  3183. //try to read namespaces - may be more than one
  3184. while (tokenStream.peek() == Tokens.NAMESPACE_SYM){
  3185. this._namespace();
  3186. this._skipCruft();
  3187. }
  3188. //get the next token
  3189. tt = tokenStream.peek();
  3190. //try to read the rest
  3191. while(tt > Tokens.EOF){
  3192. try {
  3193. switch(tt){
  3194. case Tokens.MEDIA_SYM:
  3195. this._media();
  3196. this._skipCruft();
  3197. break;
  3198. case Tokens.PAGE_SYM:
  3199. this._page();
  3200. this._skipCruft();
  3201. break;
  3202. case Tokens.FONT_FACE_SYM:
  3203. this._font_face();
  3204. this._skipCruft();
  3205. break;
  3206. case Tokens.KEYFRAMES_SYM:
  3207. this._keyframes();
  3208. this._skipCruft();
  3209. break;
  3210. case Tokens.UNKNOWN_SYM: //unknown @ rule
  3211. tokenStream.get();
  3212. if (!this.options.strict){
  3213. //fire error event
  3214. this.fire({
  3215. type: "error",
  3216. error: null,
  3217. message: "Unknown @ rule: " + tokenStream.LT(0).value + ".",
  3218. line: tokenStream.LT(0).startLine,
  3219. col: tokenStream.LT(0).startCol
  3220. });
  3221. //skip braces
  3222. count=0;
  3223. while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) == Tokens.LBRACE){
  3224. count++; //keep track of nesting depth
  3225. }
  3226. while(count){
  3227. tokenStream.advance([Tokens.RBRACE]);
  3228. count--;
  3229. }
  3230. } else {
  3231. //not a syntax error, rethrow it
  3232. throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
  3233. }
  3234. break;
  3235. case Tokens.S:
  3236. this._readWhitespace();
  3237. break;
  3238. default:
  3239. if(!this._ruleset()){
  3240. //error handling for known issues
  3241. switch(tt){
  3242. case Tokens.CHARSET_SYM:
  3243. token = tokenStream.LT(1);
  3244. this._charset(false);
  3245. throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
  3246. case Tokens.IMPORT_SYM:
  3247. token = tokenStream.LT(1);
  3248. this._import(false);
  3249. throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
  3250. case Tokens.NAMESPACE_SYM:
  3251. token = tokenStream.LT(1);
  3252. this._namespace(false);
  3253. throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
  3254. default:
  3255. tokenStream.get(); //get the last token
  3256. this._unexpectedToken(tokenStream.token());
  3257. }
  3258. }
  3259. }
  3260. } catch(ex) {
  3261. if (ex instanceof SyntaxError && !this.options.strict){
  3262. this.fire({
  3263. type: "error",
  3264. error: ex,
  3265. message: ex.message,
  3266. line: ex.line,
  3267. col: ex.col
  3268. });
  3269. } else {
  3270. throw ex;
  3271. }
  3272. }
  3273. tt = tokenStream.peek();
  3274. }
  3275. if (tt != Tokens.EOF){
  3276. this._unexpectedToken(tokenStream.token());
  3277. }
  3278. this.fire("endstylesheet");
  3279. },
  3280. _charset: function(emit){
  3281. var tokenStream = this._tokenStream,
  3282. charset,
  3283. token,
  3284. line,
  3285. col;
  3286. if (tokenStream.match(Tokens.CHARSET_SYM)){
  3287. line = tokenStream.token().startLine;
  3288. col = tokenStream.token().startCol;
  3289. this._readWhitespace();
  3290. tokenStream.mustMatch(Tokens.STRING);
  3291. token = tokenStream.token();
  3292. charset = token.value;
  3293. this._readWhitespace();
  3294. tokenStream.mustMatch(Tokens.SEMICOLON);
  3295. if (emit !== false){
  3296. this.fire({
  3297. type: "charset",
  3298. charset:charset,
  3299. line: line,
  3300. col: col
  3301. });
  3302. }
  3303. }
  3304. },
  3305. _import: function(emit){
  3306. /*
  3307. * import
  3308. * : IMPORT_SYM S*
  3309. * [STRING|URI] S* media_query_list? ';' S*
  3310. */
  3311. var tokenStream = this._tokenStream,
  3312. tt,
  3313. uri,
  3314. importToken,
  3315. mediaList = [];
  3316. //read import symbol
  3317. tokenStream.mustMatch(Tokens.IMPORT_SYM);
  3318. importToken = tokenStream.token();
  3319. this._readWhitespace();
  3320. tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
  3321. //grab the URI value
  3322. uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
  3323. this._readWhitespace();
  3324. mediaList = this._media_query_list();
  3325. //must end with a semicolon
  3326. tokenStream.mustMatch(Tokens.SEMICOLON);
  3327. this._readWhitespace();
  3328. if (emit !== false){
  3329. this.fire({
  3330. type: "import",
  3331. uri: uri,
  3332. media: mediaList,
  3333. line: importToken.startLine,
  3334. col: importToken.startCol
  3335. });
  3336. }
  3337. },
  3338. _namespace: function(emit){
  3339. /*
  3340. * namespace
  3341. * : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
  3342. */
  3343. var tokenStream = this._tokenStream,
  3344. line,
  3345. col,
  3346. prefix,
  3347. uri;
  3348. //read import symbol
  3349. tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
  3350. line = tokenStream.token().startLine;
  3351. col = tokenStream.token().startCol;
  3352. this._readWhitespace();
  3353. //it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT
  3354. if (tokenStream.match(Tokens.IDENT)){
  3355. prefix = tokenStream.token().value;
  3356. this._readWhitespace();
  3357. }
  3358. tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
  3359. //grab the URI value
  3360. uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
  3361. this._readWhitespace();
  3362. //must end with a semicolon
  3363. tokenStream.mustMatch(Tokens.SEMICOLON);
  3364. this._readWhitespace();
  3365. if (emit !== false){
  3366. this.fire({
  3367. type: "namespace",
  3368. prefix: prefix,
  3369. uri: uri,
  3370. line: line,
  3371. col: col
  3372. });
  3373. }
  3374. },
  3375. _media: function(){
  3376. /*
  3377. * media
  3378. * : MEDIA_SYM S* media_query_list S* '{' S* ruleset* '}' S*
  3379. * ;
  3380. */
  3381. var tokenStream = this._tokenStream,
  3382. line,
  3383. col,
  3384. mediaList;// = [];
  3385. //look for @media
  3386. tokenStream.mustMatch(Tokens.MEDIA_SYM);
  3387. line = tokenStream.token().startLine;
  3388. col = tokenStream.token().startCol;
  3389. this._readWhitespace();
  3390. mediaList = this._media_query_list();
  3391. tokenStream.mustMatch(Tokens.LBRACE);
  3392. this._readWhitespace();
  3393. this.fire({
  3394. type: "startmedia",
  3395. media: mediaList,
  3396. line: line,
  3397. col: col
  3398. });
  3399. while(true) {
  3400. if (tokenStream.peek() == Tokens.PAGE_SYM){
  3401. this._page();
  3402. } else if (!this._ruleset()){
  3403. break;
  3404. }
  3405. }
  3406. tokenStream.mustMatch(Tokens.RBRACE);
  3407. this._readWhitespace();
  3408. this.fire({
  3409. type: "endmedia",
  3410. media: mediaList,
  3411. line: line,
  3412. col: col
  3413. });
  3414. },
  3415. //CSS3 Media Queries
  3416. _media_query_list: function(){
  3417. /*
  3418. * media_query_list
  3419. * : S* [media_query [ ',' S* media_query ]* ]?
  3420. * ;
  3421. */
  3422. var tokenStream = this._tokenStream,
  3423. mediaList = [];
  3424. this._readWhitespace();
  3425. if (tokenStream.peek() == Tokens.IDENT || tokenStream.peek() == Tokens.LPAREN){
  3426. mediaList.push(this._media_query());
  3427. }
  3428. while(tokenStream.match(Tokens.COMMA)){
  3429. this._readWhitespace();
  3430. mediaList.push(this._media_query());
  3431. }
  3432. return mediaList;
  3433. },
  3434. /*
  3435. * Note: "expression" in the grammar maps to the _media_expression
  3436. * method.
  3437. */
  3438. _media_query: function(){
  3439. /*
  3440. * media_query
  3441. * : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
  3442. * | expression [ AND S* expression ]*
  3443. * ;
  3444. */
  3445. var tokenStream = this._tokenStream,
  3446. type = null,
  3447. ident = null,
  3448. token = null,
  3449. expressions = [];
  3450. if (tokenStream.match(Tokens.IDENT)){
  3451. ident = tokenStream.token().value.toLowerCase();
  3452. //since there's no custom tokens for these, need to manually check
  3453. if (ident != "only" && ident != "not"){
  3454. tokenStream.unget();
  3455. ident = null;
  3456. } else {
  3457. token = tokenStream.token();
  3458. }
  3459. }
  3460. this._readWhitespace();
  3461. if (tokenStream.peek() == Tokens.IDENT){
  3462. type = this._media_type();
  3463. if (token === null){
  3464. token = tokenStream.token();
  3465. }
  3466. } else if (tokenStream.peek() == Tokens.LPAREN){
  3467. if (token === null){
  3468. token = tokenStream.LT(1);
  3469. }
  3470. expressions.push(this._media_expression());
  3471. }
  3472. if (type === null && expressions.length === 0){
  3473. return null;
  3474. } else {
  3475. this._readWhitespace();
  3476. while (tokenStream.match(Tokens.IDENT)){
  3477. if (tokenStream.token().value.toLowerCase() != "and"){
  3478. this._unexpectedToken(tokenStream.token());
  3479. }
  3480. this._readWhitespace();
  3481. expressions.push(this._media_expression());
  3482. }
  3483. }
  3484. return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
  3485. },
  3486. //CSS3 Media Queries
  3487. _media_type: function(){
  3488. /*
  3489. * media_type
  3490. * : IDENT
  3491. * ;
  3492. */
  3493. return this._media_feature();
  3494. },
  3495. /**
  3496. * Note: in CSS3 Media Queries, this is called "expression".
  3497. * Renamed here to avoid conflict with CSS3 Selectors
  3498. * definition of "expression". Also note that "expr" in the
  3499. * grammar now maps to "expression" from CSS3 selectors.
  3500. * @method _media_expression
  3501. * @private
  3502. */
  3503. _media_expression: function(){
  3504. /*
  3505. * expression
  3506. * : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
  3507. * ;
  3508. */
  3509. var tokenStream = this._tokenStream,
  3510. feature = null,
  3511. token,
  3512. expression = null;
  3513. tokenStream.mustMatch(Tokens.LPAREN);
  3514. feature = this._media_feature();
  3515. this._readWhitespace();
  3516. if (tokenStream.match(Tokens.COLON)){
  3517. this._readWhitespace();
  3518. token = tokenStream.LT(1);
  3519. expression = this._expression();
  3520. }
  3521. tokenStream.mustMatch(Tokens.RPAREN);
  3522. this._readWhitespace();
  3523. return new MediaFeature(feature, (expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null));
  3524. },
  3525. //CSS3 Media Queries
  3526. _media_feature: function(){
  3527. /*
  3528. * media_feature
  3529. * : IDENT
  3530. * ;
  3531. */
  3532. var tokenStream = this._tokenStream;
  3533. tokenStream.mustMatch(Tokens.IDENT);
  3534. return SyntaxUnit.fromToken(tokenStream.token());
  3535. },
  3536. //CSS3 Paged Media
  3537. _page: function(){
  3538. /*
  3539. * page:
  3540. * PAGE_SYM S* IDENT? pseudo_page? S*
  3541. * '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
  3542. * ;
  3543. */
  3544. var tokenStream = this._tokenStream,
  3545. line,
  3546. col,
  3547. identifier = null,
  3548. pseudoPage = null;
  3549. //look for @page
  3550. tokenStream.mustMatch(Tokens.PAGE_SYM);
  3551. line = tokenStream.token().startLine;
  3552. col = tokenStream.token().startCol;
  3553. this._readWhitespace();
  3554. if (tokenStream.match(Tokens.IDENT)){
  3555. identifier = tokenStream.token().value;
  3556. //The value 'auto' may not be used as a page name and MUST be treated as a syntax error.
  3557. if (identifier.toLowerCase() === "auto"){
  3558. this._unexpectedToken(tokenStream.token());
  3559. }
  3560. }
  3561. //see if there's a colon upcoming
  3562. if (tokenStream.peek() == Tokens.COLON){
  3563. pseudoPage = this._pseudo_page();
  3564. }
  3565. this._readWhitespace();
  3566. this.fire({
  3567. type: "startpage",
  3568. id: identifier,
  3569. pseudo: pseudoPage,
  3570. line: line,
  3571. col: col
  3572. });
  3573. this._readDeclarations(true, true);
  3574. this.fire({
  3575. type: "endpage",
  3576. id: identifier,
  3577. pseudo: pseudoPage,
  3578. line: line,
  3579. col: col
  3580. });
  3581. },
  3582. //CSS3 Paged Media
  3583. _margin: function(){
  3584. /*
  3585. * margin :
  3586. * margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
  3587. * ;
  3588. */
  3589. var tokenStream = this._tokenStream,
  3590. line,
  3591. col,
  3592. marginSym = this._margin_sym();
  3593. if (marginSym){
  3594. line = tokenStream.token().startLine;
  3595. col = tokenStream.token().startCol;
  3596. this.fire({
  3597. type: "startpagemargin",
  3598. margin: marginSym,
  3599. line: line,
  3600. col: col
  3601. });
  3602. this._readDeclarations(true);
  3603. this.fire({
  3604. type: "endpagemargin",
  3605. margin: marginSym,
  3606. line: line,
  3607. col: col
  3608. });
  3609. return true;
  3610. } else {
  3611. return false;
  3612. }
  3613. },
  3614. //CSS3 Paged Media
  3615. _margin_sym: function(){
  3616. /*
  3617. * margin_sym :
  3618. * TOPLEFTCORNER_SYM |
  3619. * TOPLEFT_SYM |
  3620. * TOPCENTER_SYM |
  3621. * TOPRIGHT_SYM |
  3622. * TOPRIGHTCORNER_SYM |
  3623. * BOTTOMLEFTCORNER_SYM |
  3624. * BOTTOMLEFT_SYM |
  3625. * BOTTOMCENTER_SYM |
  3626. * BOTTOMRIGHT_SYM |
  3627. * BOTTOMRIGHTCORNER_SYM |
  3628. * LEFTTOP_SYM |
  3629. * LEFTMIDDLE_SYM |
  3630. * LEFTBOTTOM_SYM |
  3631. * RIGHTTOP_SYM |
  3632. * RIGHTMIDDLE_SYM |
  3633. * RIGHTBOTTOM_SYM
  3634. * ;
  3635. */
  3636. var tokenStream = this._tokenStream;
  3637. if(tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
  3638. Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
  3639. Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
  3640. Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
  3641. Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
  3642. Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
  3643. Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM]))
  3644. {
  3645. return SyntaxUnit.fromToken(tokenStream.token());
  3646. } else {
  3647. return null;
  3648. }
  3649. },
  3650. _pseudo_page: function(){
  3651. /*
  3652. * pseudo_page
  3653. * : ':' IDENT
  3654. * ;
  3655. */
  3656. var tokenStream = this._tokenStream;
  3657. tokenStream.mustMatch(Tokens.COLON);
  3658. tokenStream.mustMatch(Tokens.IDENT);
  3659. //TODO: CSS3 Paged Media says only "left", "center", and "right" are allowed
  3660. return tokenStream.token().value;
  3661. },
  3662. _font_face: function(){
  3663. /*
  3664. * font_face
  3665. * : FONT_FACE_SYM S*
  3666. * '{' S* declaration [ ';' S* declaration ]* '}' S*
  3667. * ;
  3668. */
  3669. var tokenStream = this._tokenStream,
  3670. line,
  3671. col;
  3672. //look for @page
  3673. tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
  3674. line = tokenStream.token().startLine;
  3675. col = tokenStream.token().startCol;
  3676. this._readWhitespace();
  3677. this.fire({
  3678. type: "startfontface",
  3679. line: line,
  3680. col: col
  3681. });
  3682. this._readDeclarations(true);
  3683. this.fire({
  3684. type: "endfontface",
  3685. line: line,
  3686. col: col
  3687. });
  3688. },
  3689. _operator: function(){
  3690. /*
  3691. * operator
  3692. * : '/' S* | ',' S* | /( empty )/
  3693. * ;
  3694. */
  3695. var tokenStream = this._tokenStream,
  3696. token = null;
  3697. if (tokenStream.match([Tokens.SLASH, Tokens.COMMA])){
  3698. token = tokenStream.token();
  3699. this._readWhitespace();
  3700. }
  3701. return token ? PropertyValuePart.fromToken(token) : null;
  3702. },
  3703. _combinator: function(){
  3704. /*
  3705. * combinator
  3706. * : PLUS S* | GREATER S* | TILDE S* | S+
  3707. * ;
  3708. */
  3709. var tokenStream = this._tokenStream,
  3710. value = null,
  3711. token;
  3712. if(tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])){
  3713. token = tokenStream.token();
  3714. value = new Combinator(token.value, token.startLine, token.startCol);
  3715. this._readWhitespace();
  3716. }
  3717. return value;
  3718. },
  3719. _unary_operator: function(){
  3720. /*
  3721. * unary_operator
  3722. * : '-' | '+'
  3723. * ;
  3724. */
  3725. var tokenStream = this._tokenStream;
  3726. if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])){
  3727. return tokenStream.token().value;
  3728. } else {
  3729. return null;
  3730. }
  3731. },
  3732. _property: function(){
  3733. /*
  3734. * property
  3735. * : IDENT S*
  3736. * ;
  3737. */
  3738. var tokenStream = this._tokenStream,
  3739. value = null,
  3740. hack = null,
  3741. tokenValue,
  3742. token,
  3743. line,
  3744. col;
  3745. //check for star hack - throws error if not allowed
  3746. if (tokenStream.peek() == Tokens.STAR && this.options.starHack){
  3747. tokenStream.get();
  3748. token = tokenStream.token();
  3749. hack = token.value;
  3750. line = token.startLine;
  3751. col = token.startCol;
  3752. }
  3753. if(tokenStream.match(Tokens.IDENT)){
  3754. token = tokenStream.token();
  3755. tokenValue = token.value;
  3756. //check for underscore hack - no error if not allowed because it's valid CSS syntax
  3757. if (tokenValue.charAt(0) == "_" && this.options.underscoreHack){
  3758. hack = "_";
  3759. tokenValue = tokenValue.substring(1);
  3760. }
  3761. value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol));
  3762. this._readWhitespace();
  3763. }
  3764. return value;
  3765. },
  3766. //Augmented with CSS3 Selectors
  3767. _ruleset: function(){
  3768. /*
  3769. * ruleset
  3770. * : selectors_group
  3771. * '{' S* declaration? [ ';' S* declaration? ]* '}' S*
  3772. * ;
  3773. */
  3774. var tokenStream = this._tokenStream,
  3775. tt,
  3776. selectors;
  3777. try {
  3778. selectors = this._selectors_group();
  3779. } catch (ex){
  3780. if (ex instanceof SyntaxError && !this.options.strict){
  3781. //fire error event
  3782. this.fire({
  3783. type: "error",
  3784. error: ex,
  3785. message: ex.message,
  3786. line: ex.line,
  3787. col: ex.col
  3788. });
  3789. //skip over everything until closing brace
  3790. tt = tokenStream.advance([Tokens.RBRACE]);
  3791. if (tt == Tokens.RBRACE){
  3792. //if there's a right brace, the rule is finished so don't do anything
  3793. } else {
  3794. //otherwise, rethrow the error because it wasn't handled properly
  3795. throw ex;
  3796. }
  3797. } else {
  3798. //not a syntax error, rethrow it
  3799. throw ex;
  3800. }
  3801. //trigger parser to continue
  3802. return true;
  3803. }
  3804. //if it got here, all selectors parsed
  3805. if (selectors){
  3806. this.fire({
  3807. type: "startrule",
  3808. selectors: selectors,
  3809. line: selectors[0].line,
  3810. col: selectors[0].col
  3811. });
  3812. this._readDeclarations(true);
  3813. this.fire({
  3814. type: "endrule",
  3815. selectors: selectors,
  3816. line: selectors[0].line,
  3817. col: selectors[0].col
  3818. });
  3819. }
  3820. return selectors;
  3821. },
  3822. //CSS3 Selectors
  3823. _selectors_group: function(){
  3824. /*
  3825. * selectors_group
  3826. * : selector [ COMMA S* selector ]*
  3827. * ;
  3828. */
  3829. var tokenStream = this._tokenStream,
  3830. selectors = [],
  3831. selector;
  3832. selector = this._selector();
  3833. if (selector !== null){
  3834. selectors.push(selector);
  3835. while(tokenStream.match(Tokens.COMMA)){
  3836. this._readWhitespace();
  3837. selector = this._selector();
  3838. if (selector !== null){
  3839. selectors.push(selector);
  3840. } else {
  3841. this._unexpectedToken(tokenStream.LT(1));
  3842. }
  3843. }
  3844. }
  3845. return selectors.length ? selectors : null;
  3846. },
  3847. //CSS3 Selectors
  3848. _selector: function(){
  3849. /*
  3850. * selector
  3851. * : simple_selector_sequence [ combinator simple_selector_sequence ]*
  3852. * ;
  3853. */
  3854. var tokenStream = this._tokenStream,
  3855. selector = [],
  3856. nextSelector = null,
  3857. combinator = null,
  3858. ws = null;
  3859. //if there's no simple selector, then there's no selector
  3860. nextSelector = this._simple_selector_sequence();
  3861. if (nextSelector === null){
  3862. return null;
  3863. }
  3864. selector.push(nextSelector);
  3865. do {
  3866. //look for a combinator
  3867. combinator = this._combinator();
  3868. if (combinator !== null){
  3869. selector.push(combinator);
  3870. nextSelector = this._simple_selector_sequence();
  3871. //there must be a next selector
  3872. if (nextSelector === null){
  3873. this._unexpectedToken(this.LT(1));
  3874. } else {
  3875. //nextSelector is an instance of SelectorPart
  3876. selector.push(nextSelector);
  3877. }
  3878. } else {
  3879. //if there's not whitespace, we're done
  3880. if (this._readWhitespace()){
  3881. //add whitespace separator
  3882. ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol);
  3883. //combinator is not required
  3884. combinator = this._combinator();
  3885. //selector is required if there's a combinator
  3886. nextSelector = this._simple_selector_sequence();
  3887. if (nextSelector === null){
  3888. if (combinator !== null){
  3889. this._unexpectedToken(tokenStream.LT(1));
  3890. }
  3891. } else {
  3892. if (combinator !== null){
  3893. selector.push(combinator);
  3894. } else {
  3895. selector.push(ws);
  3896. }
  3897. selector.push(nextSelector);
  3898. }
  3899. } else {
  3900. break;
  3901. }
  3902. }
  3903. } while(true);
  3904. return new Selector(selector, selector[0].line, selector[0].col);
  3905. },
  3906. //CSS3 Selectors
  3907. _simple_selector_sequence: function(){
  3908. /*
  3909. * simple_selector_sequence
  3910. * : [ type_selector | universal ]
  3911. * [ HASH | class | attrib | pseudo | negation ]*
  3912. * | [ HASH | class | attrib | pseudo | negation ]+
  3913. * ;
  3914. */
  3915. var tokenStream = this._tokenStream,
  3916. //parts of a simple selector
  3917. elementName = null,
  3918. modifiers = [],
  3919. //complete selector text
  3920. selectorText= "",
  3921. //the different parts after the element name to search for
  3922. components = [
  3923. //HASH
  3924. function(){
  3925. return tokenStream.match(Tokens.HASH) ?
  3926. new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
  3927. null;
  3928. },
  3929. this._class,
  3930. this._attrib,
  3931. this._pseudo,
  3932. this._negation
  3933. ],
  3934. i = 0,
  3935. len = components.length,
  3936. component = null,
  3937. found = false,
  3938. line,
  3939. col;
  3940. //get starting line and column for the selector
  3941. line = tokenStream.LT(1).startLine;
  3942. col = tokenStream.LT(1).startCol;
  3943. elementName = this._type_selector();
  3944. if (!elementName){
  3945. elementName = this._universal();
  3946. }
  3947. if (elementName !== null){
  3948. selectorText += elementName;
  3949. }
  3950. while(true){
  3951. //whitespace means we're done
  3952. if (tokenStream.peek() === Tokens.S){
  3953. break;
  3954. }
  3955. //check for each component
  3956. while(i < len && component === null){
  3957. component = components[i++].call(this);
  3958. }
  3959. if (component === null){
  3960. //we don't have a selector
  3961. if (selectorText === ""){
  3962. return null;
  3963. } else {
  3964. break;
  3965. }
  3966. } else {
  3967. i = 0;
  3968. modifiers.push(component);
  3969. selectorText += component.toString();
  3970. component = null;
  3971. }
  3972. }
  3973. return selectorText !== "" ?
  3974. new SelectorPart(elementName, modifiers, selectorText, line, col) :
  3975. null;
  3976. },
  3977. //CSS3 Selectors
  3978. _type_selector: function(){
  3979. /*
  3980. * type_selector
  3981. * : [ namespace_prefix ]? element_name
  3982. * ;
  3983. */
  3984. var tokenStream = this._tokenStream,
  3985. ns = this._namespace_prefix(),
  3986. elementName = this._element_name();
  3987. if (!elementName){
  3988. /*
  3989. * Need to back out the namespace that was read due to both
  3990. * type_selector and universal reading namespace_prefix
  3991. * first. Kind of hacky, but only way I can figure out
  3992. * right now how to not change the grammar.
  3993. */
  3994. if (ns){
  3995. tokenStream.unget();
  3996. if (ns.length > 1){
  3997. tokenStream.unget();
  3998. }
  3999. }
  4000. return null;
  4001. } else {
  4002. if (ns){
  4003. elementName.text = ns + elementName.text;
  4004. elementName.col -= ns.length;
  4005. }
  4006. return elementName;
  4007. }
  4008. },
  4009. //CSS3 Selectors
  4010. _class: function(){
  4011. /*
  4012. * class
  4013. * : '.' IDENT
  4014. * ;
  4015. */
  4016. var tokenStream = this._tokenStream,
  4017. token;
  4018. if (tokenStream.match(Tokens.DOT)){
  4019. tokenStream.mustMatch(Tokens.IDENT);
  4020. token = tokenStream.token();
  4021. return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);
  4022. } else {
  4023. return null;
  4024. }
  4025. },
  4026. //CSS3 Selectors
  4027. _element_name: function(){
  4028. /*
  4029. * element_name
  4030. * : IDENT
  4031. * ;
  4032. */
  4033. var tokenStream = this._tokenStream,
  4034. token;
  4035. if (tokenStream.match(Tokens.IDENT)){
  4036. token = tokenStream.token();
  4037. return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol);
  4038. } else {
  4039. return null;
  4040. }
  4041. },
  4042. //CSS3 Selectors
  4043. _namespace_prefix: function(){
  4044. /*
  4045. * namespace_prefix
  4046. * : [ IDENT | '*' ]? '|'
  4047. * ;
  4048. */
  4049. var tokenStream = this._tokenStream,
  4050. value = "";
  4051. //verify that this is a namespace prefix
  4052. if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE){
  4053. if(tokenStream.match([Tokens.IDENT, Tokens.STAR])){
  4054. value += tokenStream.token().value;
  4055. }
  4056. tokenStream.mustMatch(Tokens.PIPE);
  4057. value += "|";
  4058. }
  4059. return value.length ? value : null;
  4060. },
  4061. //CSS3 Selectors
  4062. _universal: function(){
  4063. /*
  4064. * universal
  4065. * : [ namespace_prefix ]? '*'
  4066. * ;
  4067. */
  4068. var tokenStream = this._tokenStream,
  4069. value = "",
  4070. ns;
  4071. ns = this._namespace_prefix();
  4072. if(ns){
  4073. value += ns;
  4074. }
  4075. if(tokenStream.match(Tokens.STAR)){
  4076. value += "*";
  4077. }
  4078. return value.length ? value : null;
  4079. },
  4080. //CSS3 Selectors
  4081. _attrib: function(){
  4082. /*
  4083. * attrib
  4084. * : '[' S* [ namespace_prefix ]? IDENT S*
  4085. * [ [ PREFIXMATCH |
  4086. * SUFFIXMATCH |
  4087. * SUBSTRINGMATCH |
  4088. * '=' |
  4089. * INCLUDES |
  4090. * DASHMATCH ] S* [ IDENT | STRING ] S*
  4091. * ]? ']'
  4092. * ;
  4093. */
  4094. var tokenStream = this._tokenStream,
  4095. value = null,
  4096. ns,
  4097. token;
  4098. if (tokenStream.match(Tokens.LBRACKET)){
  4099. token = tokenStream.token();
  4100. value = token.value;
  4101. value += this._readWhitespace();
  4102. ns = this._namespace_prefix();
  4103. if (ns){
  4104. value += ns;
  4105. }
  4106. tokenStream.mustMatch(Tokens.IDENT);
  4107. value += tokenStream.token().value;
  4108. value += this._readWhitespace();
  4109. if(tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH,
  4110. Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])){
  4111. value += tokenStream.token().value;
  4112. value += this._readWhitespace();
  4113. tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
  4114. value += tokenStream.token().value;
  4115. value += this._readWhitespace();
  4116. }
  4117. tokenStream.mustMatch(Tokens.RBRACKET);
  4118. return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol);
  4119. } else {
  4120. return null;
  4121. }
  4122. },
  4123. //CSS3 Selectors
  4124. _pseudo: function(){
  4125. /*
  4126. * pseudo
  4127. * : ':' ':'? [ IDENT | functional_pseudo ]
  4128. * ;
  4129. */
  4130. var tokenStream = this._tokenStream,
  4131. pseudo = null,
  4132. colons = ":",
  4133. line,
  4134. col;
  4135. if (tokenStream.match(Tokens.COLON)){
  4136. if (tokenStream.match(Tokens.COLON)){
  4137. colons += ":";
  4138. }
  4139. if (tokenStream.match(Tokens.IDENT)){
  4140. pseudo = tokenStream.token().value;
  4141. line = tokenStream.token().startLine;
  4142. col = tokenStream.token().startCol - colons.length;
  4143. } else if (tokenStream.peek() == Tokens.FUNCTION){
  4144. line = tokenStream.LT(1).startLine;
  4145. col = tokenStream.LT(1).startCol - colons.length;
  4146. pseudo = this._functional_pseudo();
  4147. }
  4148. if (pseudo){
  4149. pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col);
  4150. }
  4151. }
  4152. return pseudo;
  4153. },
  4154. //CSS3 Selectors
  4155. _functional_pseudo: function(){
  4156. /*
  4157. * functional_pseudo
  4158. * : FUNCTION S* expression ')'
  4159. * ;
  4160. */
  4161. var tokenStream = this._tokenStream,
  4162. value = null;
  4163. if(tokenStream.match(Tokens.FUNCTION)){
  4164. value = tokenStream.token().value;
  4165. value += this._readWhitespace();
  4166. value += this._expression();
  4167. tokenStream.mustMatch(Tokens.RPAREN);
  4168. value += ")";
  4169. }
  4170. return value;
  4171. },
  4172. //CSS3 Selectors
  4173. _expression: function(){
  4174. /*
  4175. * expression
  4176. * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
  4177. * ;
  4178. */
  4179. var tokenStream = this._tokenStream,
  4180. value = "";
  4181. while(tokenStream.match([Tokens.PLUS, Tokens.MINUS, Tokens.DIMENSION,
  4182. Tokens.NUMBER, Tokens.STRING, Tokens.IDENT, Tokens.LENGTH,
  4183. Tokens.FREQ, Tokens.ANGLE, Tokens.TIME,
  4184. Tokens.RESOLUTION])){
  4185. value += tokenStream.token().value;
  4186. value += this._readWhitespace();
  4187. }
  4188. return value.length ? value : null;
  4189. },
  4190. //CSS3 Selectors
  4191. _negation: function(){
  4192. /*
  4193. * negation
  4194. * : NOT S* negation_arg S* ')'
  4195. * ;
  4196. */
  4197. var tokenStream = this._tokenStream,
  4198. line,
  4199. col,
  4200. value = "",
  4201. arg,
  4202. subpart = null;
  4203. if (tokenStream.match(Tokens.NOT)){
  4204. value = tokenStream.token().value;
  4205. line = tokenStream.token().startLine;
  4206. col = tokenStream.token().startCol;
  4207. value += this._readWhitespace();
  4208. arg = this._negation_arg();
  4209. value += arg;
  4210. value += this._readWhitespace();
  4211. tokenStream.match(Tokens.RPAREN);
  4212. value += tokenStream.token().value;
  4213. subpart = new SelectorSubPart(value, "not", line, col);
  4214. subpart.args.push(arg);
  4215. }
  4216. return subpart;
  4217. },
  4218. //CSS3 Selectors
  4219. _negation_arg: function(){
  4220. /*
  4221. * negation_arg
  4222. * : type_selector | universal | HASH | class | attrib | pseudo
  4223. * ;
  4224. */
  4225. var tokenStream = this._tokenStream,
  4226. args = [
  4227. this._type_selector,
  4228. this._universal,
  4229. function(){
  4230. return tokenStream.match(Tokens.HASH) ?
  4231. new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
  4232. null;
  4233. },
  4234. this._class,
  4235. this._attrib,
  4236. this._pseudo
  4237. ],
  4238. arg = null,
  4239. i = 0,
  4240. len = args.length,
  4241. elementName,
  4242. line,
  4243. col,
  4244. part;
  4245. line = tokenStream.LT(1).startLine;
  4246. col = tokenStream.LT(1).startCol;
  4247. while(i < len && arg === null){
  4248. arg = args[i].call(this);
  4249. i++;
  4250. }
  4251. //must be a negation arg
  4252. if (arg === null){
  4253. this._unexpectedToken(tokenStream.LT(1));
  4254. }
  4255. //it's an element name
  4256. if (arg.type == "elementName"){
  4257. part = new SelectorPart(arg, [], arg.toString(), line, col);
  4258. } else {
  4259. part = new SelectorPart(null, [arg], arg.toString(), line, col);
  4260. }
  4261. return part;
  4262. },
  4263. _declaration: function(){
  4264. /*
  4265. * declaration
  4266. * : property ':' S* expr prio?
  4267. * | /( empty )/
  4268. * ;
  4269. */
  4270. var tokenStream = this._tokenStream,
  4271. property = null,
  4272. expr = null,
  4273. prio = null,
  4274. error = null,
  4275. invalid = null;
  4276. property = this._property();
  4277. if (property !== null){
  4278. tokenStream.mustMatch(Tokens.COLON);
  4279. this._readWhitespace();
  4280. expr = this._expr();
  4281. //if there's no parts for the value, it's an error
  4282. if (!expr || expr.length === 0){
  4283. this._unexpectedToken(tokenStream.LT(1));
  4284. }
  4285. prio = this._prio();
  4286. try {
  4287. this._validateProperty(property, expr);
  4288. } catch (ex) {
  4289. invalid = ex;
  4290. }
  4291. this.fire({
  4292. type: "property",
  4293. property: property,
  4294. value: expr,
  4295. important: prio,
  4296. line: property.line,
  4297. col: property.col,
  4298. invalid: invalid
  4299. });
  4300. return true;
  4301. } else {
  4302. return false;
  4303. }
  4304. },
  4305. _prio: function(){
  4306. /*
  4307. * prio
  4308. * : IMPORTANT_SYM S*
  4309. * ;
  4310. */
  4311. var tokenStream = this._tokenStream,
  4312. result = tokenStream.match(Tokens.IMPORTANT_SYM);
  4313. this._readWhitespace();
  4314. return result;
  4315. },
  4316. _expr: function(){
  4317. /*
  4318. * expr
  4319. * : term [ operator term ]*
  4320. * ;
  4321. */
  4322. var tokenStream = this._tokenStream,
  4323. values = [],
  4324. //valueParts = [],
  4325. value = null,
  4326. operator = null;
  4327. value = this._term();
  4328. if (value !== null){
  4329. values.push(value);
  4330. do {
  4331. operator = this._operator();
  4332. //if there's an operator, keep building up the value parts
  4333. if (operator){
  4334. values.push(operator);
  4335. } /*else {
  4336. //if there's not an operator, you have a full value
  4337. values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
  4338. valueParts = [];
  4339. }*/
  4340. value = this._term();
  4341. if (value === null){
  4342. break;
  4343. } else {
  4344. values.push(value);
  4345. }
  4346. } while(true);
  4347. }
  4348. //cleanup
  4349. /*if (valueParts.length){
  4350. values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
  4351. }*/
  4352. return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null;
  4353. },
  4354. _term: function(){
  4355. /*
  4356. * term
  4357. * : unary_operator?
  4358. * [ NUMBER S* | PERCENTAGE S* | LENGTH S* | ANGLE S* |
  4359. * TIME S* | FREQ S* | function | ie_function ]
  4360. * | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor
  4361. * ;
  4362. */
  4363. var tokenStream = this._tokenStream,
  4364. unary = null,
  4365. value = null,
  4366. token,
  4367. line,
  4368. col;
  4369. //returns the operator or null
  4370. unary = this._unary_operator();
  4371. if (unary !== null){
  4372. line = tokenStream.token().startLine;
  4373. col = tokenStream.token().startCol;
  4374. }
  4375. //exception for IE filters
  4376. if (tokenStream.peek() == Tokens.IE_FUNCTION && this.options.ieFilters){
  4377. value = this._ie_function();
  4378. if (unary === null){
  4379. line = tokenStream.token().startLine;
  4380. col = tokenStream.token().startCol;
  4381. }
  4382. //see if there's a simple match
  4383. } else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH,
  4384. Tokens.ANGLE, Tokens.TIME,
  4385. Tokens.FREQ, Tokens.STRING, Tokens.IDENT, Tokens.URI, Tokens.UNICODE_RANGE])){
  4386. value = tokenStream.token().value;
  4387. if (unary === null){
  4388. line = tokenStream.token().startLine;
  4389. col = tokenStream.token().startCol;
  4390. }
  4391. this._readWhitespace();
  4392. } else {
  4393. //see if it's a color
  4394. token = this._hexcolor();
  4395. if (token === null){
  4396. //if there's no unary, get the start of the next token for line/col info
  4397. if (unary === null){
  4398. line = tokenStream.LT(1).startLine;
  4399. col = tokenStream.LT(1).startCol;
  4400. }
  4401. //has to be a function
  4402. if (value === null){
  4403. /*
  4404. * This checks for alpha(opacity=0) style of IE
  4405. * functions. IE_FUNCTION only presents progid: style.
  4406. */
  4407. if (tokenStream.LA(3) == Tokens.EQUALS && this.options.ieFilters){
  4408. value = this._ie_function();
  4409. } else {
  4410. value = this._function();
  4411. }
  4412. }
  4413. /*if (value === null){
  4414. return null;
  4415. //throw new Error("Expected identifier at line " + tokenStream.token().startLine + ", character " + tokenStream.token().startCol + ".");
  4416. }*/
  4417. } else {
  4418. value = token.value;
  4419. if (unary === null){
  4420. line = token.startLine;
  4421. col = token.startCol;
  4422. }
  4423. }
  4424. }
  4425. return value !== null ?
  4426. new PropertyValuePart(unary !== null ? unary + value : value, line, col) :
  4427. null;
  4428. },
  4429. _function: function(){
  4430. /*
  4431. * function
  4432. * : FUNCTION S* expr ')' S*
  4433. * ;
  4434. */
  4435. var tokenStream = this._tokenStream,
  4436. functionText = null,
  4437. expr = null,
  4438. lt;
  4439. if (tokenStream.match(Tokens.FUNCTION)){
  4440. functionText = tokenStream.token().value;
  4441. this._readWhitespace();
  4442. expr = this._expr();
  4443. functionText += expr;
  4444. //START: Horrible hack in case it's an IE filter
  4445. if (this.options.ieFilters && tokenStream.peek() == Tokens.EQUALS){
  4446. do {
  4447. if (this._readWhitespace()){
  4448. functionText += tokenStream.token().value;
  4449. }
  4450. //might be second time in the loop
  4451. if (tokenStream.LA(0) == Tokens.COMMA){
  4452. functionText += tokenStream.token().value;
  4453. }
  4454. tokenStream.match(Tokens.IDENT);
  4455. functionText += tokenStream.token().value;
  4456. tokenStream.match(Tokens.EQUALS);
  4457. functionText += tokenStream.token().value;
  4458. //functionText += this._term();
  4459. lt = tokenStream.peek();
  4460. while(lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN){
  4461. tokenStream.get();
  4462. functionText += tokenStream.token().value;
  4463. lt = tokenStream.peek();
  4464. }
  4465. } while(tokenStream.match([Tokens.COMMA, Tokens.S]));
  4466. }
  4467. //END: Horrible Hack
  4468. tokenStream.match(Tokens.RPAREN);
  4469. functionText += ")";
  4470. this._readWhitespace();
  4471. }
  4472. return functionText;
  4473. },
  4474. _ie_function: function(){
  4475. /* (My own extension)
  4476. * ie_function
  4477. * : IE_FUNCTION S* IDENT '=' term [S* ','? IDENT '=' term]+ ')' S*
  4478. * ;
  4479. */
  4480. var tokenStream = this._tokenStream,
  4481. functionText = null,
  4482. expr = null,
  4483. lt;
  4484. //IE function can begin like a regular function, too
  4485. if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])){
  4486. functionText = tokenStream.token().value;
  4487. do {
  4488. if (this._readWhitespace()){
  4489. functionText += tokenStream.token().value;
  4490. }
  4491. //might be second time in the loop
  4492. if (tokenStream.LA(0) == Tokens.COMMA){
  4493. functionText += tokenStream.token().value;
  4494. }
  4495. tokenStream.match(Tokens.IDENT);
  4496. functionText += tokenStream.token().value;
  4497. tokenStream.match(Tokens.EQUALS);
  4498. functionText += tokenStream.token().value;
  4499. //functionText += this._term();
  4500. lt = tokenStream.peek();
  4501. while(lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN){
  4502. tokenStream.get();
  4503. functionText += tokenStream.token().value;
  4504. lt = tokenStream.peek();
  4505. }
  4506. } while(tokenStream.match([Tokens.COMMA, Tokens.S]));
  4507. tokenStream.match(Tokens.RPAREN);
  4508. functionText += ")";
  4509. this._readWhitespace();
  4510. }
  4511. return functionText;
  4512. },
  4513. _hexcolor: function(){
  4514. /*
  4515. * There is a constraint on the color that it must
  4516. * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
  4517. * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
  4518. *
  4519. * hexcolor
  4520. * : HASH S*
  4521. * ;
  4522. */
  4523. var tokenStream = this._tokenStream,
  4524. token = null,
  4525. color;
  4526. if(tokenStream.match(Tokens.HASH)){
  4527. //need to do some validation here
  4528. token = tokenStream.token();
  4529. color = token.value;
  4530. if (!/#[a-f0-9]{3,6}/i.test(color)){
  4531. throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
  4532. }
  4533. this._readWhitespace();
  4534. }
  4535. return token;
  4536. },
  4537. //-----------------------------------------------------------------
  4538. // Animations methods
  4539. //-----------------------------------------------------------------
  4540. _keyframes: function(){
  4541. /*
  4542. * keyframes:
  4543. * : KEYFRAMES_SYM S* keyframe_name S* '{' S* keyframe_rule* '}' {
  4544. * ;
  4545. */
  4546. var tokenStream = this._tokenStream,
  4547. token,
  4548. tt,
  4549. name;
  4550. tokenStream.mustMatch(Tokens.KEYFRAMES_SYM);
  4551. this._readWhitespace();
  4552. name = this._keyframe_name();
  4553. this._readWhitespace();
  4554. tokenStream.mustMatch(Tokens.LBRACE);
  4555. this.fire({
  4556. type: "startkeyframes",
  4557. name: name,
  4558. line: name.line,
  4559. col: name.col
  4560. });
  4561. this._readWhitespace();
  4562. tt = tokenStream.peek();
  4563. //check for key
  4564. while(tt == Tokens.IDENT || tt == Tokens.PERCENTAGE) {
  4565. this._keyframe_rule();
  4566. this._readWhitespace();
  4567. tt = tokenStream.peek();
  4568. }
  4569. this.fire({
  4570. type: "endkeyframes",
  4571. name: name,
  4572. line: name.line,
  4573. col: name.col
  4574. });
  4575. this._readWhitespace();
  4576. tokenStream.mustMatch(Tokens.RBRACE);
  4577. },
  4578. _keyframe_name: function(){
  4579. /*
  4580. * keyframe_name:
  4581. * : IDENT
  4582. * | STRING
  4583. * ;
  4584. */
  4585. var tokenStream = this._tokenStream,
  4586. token;
  4587. tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
  4588. return SyntaxUnit.fromToken(tokenStream.token());
  4589. },
  4590. _keyframe_rule: function(){
  4591. /*
  4592. * keyframe_rule:
  4593. * : key_list S*
  4594. * '{' S* declaration [ ';' S* declaration ]* '}' S*
  4595. * ;
  4596. */
  4597. var tokenStream = this._tokenStream,
  4598. token,
  4599. keyList = this._key_list();
  4600. this.fire({
  4601. type: "startkeyframerule",
  4602. keys: keyList,
  4603. line: keyList[0].line,
  4604. col: keyList[0].col
  4605. });
  4606. this._readDeclarations(true);
  4607. this.fire({
  4608. type: "endkeyframerule",
  4609. keys: keyList,
  4610. line: keyList[0].line,
  4611. col: keyList[0].col
  4612. });
  4613. },
  4614. _key_list: function(){
  4615. /*
  4616. * key_list:
  4617. * : key [ S* ',' S* key]*
  4618. * ;
  4619. */
  4620. var tokenStream = this._tokenStream,
  4621. token,
  4622. key,
  4623. keyList = [];
  4624. //must be least one key
  4625. keyList.push(this._key());
  4626. this._readWhitespace();
  4627. while(tokenStream.match(Tokens.COMMA)){
  4628. this._readWhitespace();
  4629. keyList.push(this._key());
  4630. this._readWhitespace();
  4631. }
  4632. return keyList;
  4633. },
  4634. _key: function(){
  4635. /*
  4636. * There is a restriction that IDENT can be only "from" or "to".
  4637. *
  4638. * key
  4639. * : PERCENTAGE
  4640. * | IDENT
  4641. * ;
  4642. */
  4643. var tokenStream = this._tokenStream,
  4644. token;
  4645. if (tokenStream.match(Tokens.PERCENTAGE)){
  4646. return SyntaxUnit.fromToken(tokenStream.token());
  4647. } else if (tokenStream.match(Tokens.IDENT)){
  4648. token = tokenStream.token();
  4649. if (/from|to/i.test(token.value)){
  4650. return SyntaxUnit.fromToken(token);
  4651. }
  4652. tokenStream.unget();
  4653. }
  4654. //if it gets here, there wasn't a valid token, so time to explode
  4655. this._unexpectedToken(tokenStream.LT(1));
  4656. },
  4657. //-----------------------------------------------------------------
  4658. // Helper methods
  4659. //-----------------------------------------------------------------
  4660. /**
  4661. * Not part of CSS grammar, but useful for skipping over
  4662. * combination of white space and HTML-style comments.
  4663. * @return {void}
  4664. * @method _skipCruft
  4665. * @private
  4666. */
  4667. _skipCruft: function(){
  4668. while(this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])){
  4669. //noop
  4670. }
  4671. },
  4672. /**
  4673. * Not part of CSS grammar, but this pattern occurs frequently
  4674. * in the official CSS grammar. Split out here to eliminate
  4675. * duplicate code.
  4676. * @param {Boolean} checkStart Indicates if the rule should check
  4677. * for the left brace at the beginning.
  4678. * @param {Boolean} readMargins Indicates if the rule should check
  4679. * for margin patterns.
  4680. * @return {void}
  4681. * @method _readDeclarations
  4682. * @private
  4683. */
  4684. _readDeclarations: function(checkStart, readMargins){
  4685. /*
  4686. * Reads the pattern
  4687. * S* '{' S* declaration [ ';' S* declaration ]* '}' S*
  4688. * or
  4689. * S* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
  4690. * Note that this is how it is described in CSS3 Paged Media, but is actually incorrect.
  4691. * A semicolon is only necessary following a delcaration is there's another declaration
  4692. * or margin afterwards.
  4693. */
  4694. var tokenStream = this._tokenStream,
  4695. tt;
  4696. this._readWhitespace();
  4697. if (checkStart){
  4698. tokenStream.mustMatch(Tokens.LBRACE);
  4699. }
  4700. this._readWhitespace();
  4701. try {
  4702. while(true){
  4703. if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())){
  4704. //noop
  4705. } else if (this._declaration()){
  4706. if (!tokenStream.match(Tokens.SEMICOLON)){
  4707. break;
  4708. }
  4709. } else {
  4710. break;
  4711. }
  4712. //if ((!this._margin() && !this._declaration()) || !tokenStream.match(Tokens.SEMICOLON)){
  4713. // break;
  4714. //}
  4715. this._readWhitespace();
  4716. }
  4717. tokenStream.mustMatch(Tokens.RBRACE);
  4718. this._readWhitespace();
  4719. } catch (ex) {
  4720. if (ex instanceof SyntaxError && !this.options.strict){
  4721. //fire error event
  4722. this.fire({
  4723. type: "error",
  4724. error: ex,
  4725. message: ex.message,
  4726. line: ex.line,
  4727. col: ex.col
  4728. });
  4729. //see if there's another declaration
  4730. tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]);
  4731. if (tt == Tokens.SEMICOLON){
  4732. //if there's a semicolon, then there might be another declaration
  4733. this._readDeclarations(false, readMargins);
  4734. } else if (tt != Tokens.RBRACE){
  4735. //if there's a right brace, the rule is finished so don't do anything
  4736. //otherwise, rethrow the error because it wasn't handled properly
  4737. throw ex;
  4738. }
  4739. } else {
  4740. //not a syntax error, rethrow it
  4741. throw ex;
  4742. }
  4743. }
  4744. },
  4745. /**
  4746. * In some cases, you can end up with two white space tokens in a
  4747. * row. Instead of making a change in every function that looks for
  4748. * white space, this function is used to match as much white space
  4749. * as necessary.
  4750. * @method _readWhitespace
  4751. * @return {String} The white space if found, empty string if not.
  4752. * @private
  4753. */
  4754. _readWhitespace: function(){
  4755. var tokenStream = this._tokenStream,
  4756. ws = "";
  4757. while(tokenStream.match(Tokens.S)){
  4758. ws += tokenStream.token().value;
  4759. }
  4760. return ws;
  4761. },
  4762. /**
  4763. * Throws an error when an unexpected token is found.
  4764. * @param {Object} token The token that was found.
  4765. * @method _unexpectedToken
  4766. * @return {void}
  4767. * @private
  4768. */
  4769. _unexpectedToken: function(token){
  4770. throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
  4771. },
  4772. /**
  4773. * Helper method used for parsing subparts of a style sheet.
  4774. * @return {void}
  4775. * @method _verifyEnd
  4776. * @private
  4777. */
  4778. _verifyEnd: function(){
  4779. if (this._tokenStream.LA(1) != Tokens.EOF){
  4780. this._unexpectedToken(this._tokenStream.LT(1));
  4781. }
  4782. },
  4783. //-----------------------------------------------------------------
  4784. // Validation methods
  4785. //-----------------------------------------------------------------
  4786. _validateProperty: function(property, value){
  4787. Validation.validate(property, value);
  4788. },
  4789. //-----------------------------------------------------------------
  4790. // Parsing methods
  4791. //-----------------------------------------------------------------
  4792. parse: function(input){
  4793. this._tokenStream = new TokenStream(input, Tokens);
  4794. this._stylesheet();
  4795. },
  4796. parseStyleSheet: function(input){
  4797. //just passthrough
  4798. return this.parse(input);
  4799. },
  4800. parseMediaQuery: function(input){
  4801. this._tokenStream = new TokenStream(input, Tokens);
  4802. var result = this._media_query();
  4803. //if there's anything more, then it's an invalid selector
  4804. this._verifyEnd();
  4805. //otherwise return result
  4806. return result;
  4807. },
  4808. /**
  4809. * Parses a property value (everything after the semicolon).
  4810. * @return {parserlib.css.PropertyValue} The property value.
  4811. * @throws parserlib.util.SyntaxError If an unexpected token is found.
  4812. * @method parserPropertyValue
  4813. */
  4814. parsePropertyValue: function(input){
  4815. this._tokenStream = new TokenStream(input, Tokens);
  4816. this._readWhitespace();
  4817. var result = this._expr();
  4818. //okay to have a trailing white space
  4819. this._readWhitespace();
  4820. //if there's anything more, then it's an invalid selector
  4821. this._verifyEnd();
  4822. //otherwise return result
  4823. return result;
  4824. },
  4825. /**
  4826. * Parses a complete CSS rule, including selectors and
  4827. * properties.
  4828. * @param {String} input The text to parser.
  4829. * @return {Boolean} True if the parse completed successfully, false if not.
  4830. * @method parseRule
  4831. */
  4832. parseRule: function(input){
  4833. this._tokenStream = new TokenStream(input, Tokens);
  4834. //skip any leading white space
  4835. this._readWhitespace();
  4836. var result = this._ruleset();
  4837. //skip any trailing white space
  4838. this._readWhitespace();
  4839. //if there's anything more, then it's an invalid selector
  4840. this._verifyEnd();
  4841. //otherwise return result
  4842. return result;
  4843. },
  4844. /**
  4845. * Parses a single CSS selector (no comma)
  4846. * @param {String} input The text to parse as a CSS selector.
  4847. * @return {Selector} An object representing the selector.
  4848. * @throws parserlib.util.SyntaxError If an unexpected token is found.
  4849. * @method parseSelector
  4850. */
  4851. parseSelector: function(input){
  4852. this._tokenStream = new TokenStream(input, Tokens);
  4853. //skip any leading white space
  4854. this._readWhitespace();
  4855. var result = this._selector();
  4856. //skip any trailing white space
  4857. this._readWhitespace();
  4858. //if there's anything more, then it's an invalid selector
  4859. this._verifyEnd();
  4860. //otherwise return result
  4861. return result;
  4862. },
  4863. /**
  4864. * Parses an HTML style attribute: a set of CSS declarations
  4865. * separated by semicolons.
  4866. * @param {String} input The text to parse as a style attribute
  4867. * @return {void}
  4868. * @method parseStyleAttribute
  4869. */
  4870. parseStyleAttribute: function(input){
  4871. input += "}"; // for error recovery in _readDeclarations()
  4872. this._tokenStream = new TokenStream(input, Tokens);
  4873. this._readDeclarations();
  4874. }
  4875. };
  4876. //copy over onto prototype
  4877. for (prop in additions){
  4878. if (additions.hasOwnProperty(prop)){
  4879. proto[prop] = additions[prop];
  4880. }
  4881. }
  4882. return proto;
  4883. }();
  4884. /*global Validation, ValidationTypes, ValidationError*/
  4885. var Properties = {
  4886. //A
  4887. "alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>",
  4888. "alignment-baseline" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
  4889. "animation" : 1,
  4890. "animation-delay" : { multi: "<time>", comma: true },
  4891. "animation-direction" : { multi: "normal | alternate", comma: true },
  4892. "animation-duration" : { multi: "<time>", comma: true },
  4893. "animation-iteration-count" : { multi: "<number> | infinite", comma: true },
  4894. "animation-name" : { multi: "none | <ident>", comma: true },
  4895. "animation-play-state" : { multi: "running | paused", comma: true },
  4896. "animation-timing-function" : 1,
  4897. //vendor prefixed
  4898. "-moz-animation-delay" : { multi: "<time>", comma: true },
  4899. "-moz-animation-direction" : { multi: "normal | alternate", comma: true },
  4900. "-moz-animation-duration" : { multi: "<time>", comma: true },
  4901. "-moz-animation-iteration-count" : { multi: "<number> | infinite", comma: true },
  4902. "-moz-animation-name" : { multi: "none | <ident>", comma: true },
  4903. "-moz-animation-play-state" : { multi: "running | paused", comma: true },
  4904. "-ms-animation-delay" : { multi: "<time>", comma: true },
  4905. "-ms-animation-direction" : { multi: "normal | alternate", comma: true },
  4906. "-ms-animation-duration" : { multi: "<time>", comma: true },
  4907. "-ms-animation-iteration-count" : { multi: "<number> | infinite", comma: true },
  4908. "-ms-animation-name" : { multi: "none | <ident>", comma: true },
  4909. "-ms-animation-play-state" : { multi: "running | paused", comma: true },
  4910. "-webkit-animation-delay" : { multi: "<time>", comma: true },
  4911. "-webkit-animation-direction" : { multi: "normal | alternate", comma: true },
  4912. "-webkit-animation-duration" : { multi: "<time>", comma: true },
  4913. "-webkit-animation-iteration-count" : { multi: "<number> | infinite", comma: true },
  4914. "-webkit-animation-name" : { multi: "none | <ident>", comma: true },
  4915. "-webkit-animation-play-state" : { multi: "running | paused", comma: true },
  4916. "-o-animation-delay" : { multi: "<time>", comma: true },
  4917. "-o-animation-direction" : { multi: "normal | alternate", comma: true },
  4918. "-o-animation-duration" : { multi: "<time>", comma: true },
  4919. "-o-animation-iteration-count" : { multi: "<number> | infinite", comma: true },
  4920. "-o-animation-name" : { multi: "none | <ident>", comma: true },
  4921. "-o-animation-play-state" : { multi: "running | paused", comma: true },
  4922. "appearance" : "icon | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | 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 | inherit",
  4923. "azimuth" : function (expression) {
  4924. var simple = "<angle> | leftwards | rightwards | inherit",
  4925. direction = "left-side | far-left | left | center-left | center | center-right | right | far-right | right-side",
  4926. behind = false,
  4927. valid = false,
  4928. part;
  4929. if (!ValidationTypes.isAny(expression, simple)) {
  4930. if (ValidationTypes.isAny(expression, "behind")) {
  4931. behind = true;
  4932. valid = true;
  4933. }
  4934. if (ValidationTypes.isAny(expression, direction)) {
  4935. valid = true;
  4936. if (!behind) {
  4937. ValidationTypes.isAny(expression, "behind");
  4938. }
  4939. }
  4940. }
  4941. if (expression.hasNext()) {
  4942. part = expression.next();
  4943. if (valid) {
  4944. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  4945. } else {
  4946. throw new ValidationError("Expected (<'azimuth'>) but found '" + part + "'.", part.line, part.col);
  4947. }
  4948. }
  4949. },
  4950. //B
  4951. "backface-visibility" : "visible | hidden",
  4952. "background" : 1,
  4953. "background-attachment" : { multi: "<attachment>", comma: true },
  4954. "background-clip" : { multi: "<box>", comma: true },
  4955. "background-color" : "<color> | inherit",
  4956. "background-image" : { multi: "<bg-image>", comma: true },
  4957. "background-origin" : { multi: "<box>", comma: true },
  4958. "background-position" : { multi: "<bg-position>", comma: true },
  4959. "background-repeat" : { multi: "<repeat-style>" },
  4960. "background-size" : { multi: "<bg-size>", comma: true },
  4961. "baseline-shift" : "baseline | sub | super | <percentage> | <length>",
  4962. "binding" : 1,
  4963. "bleed" : "<length>",
  4964. "bookmark-label" : "<content> | <attr> | <string>",
  4965. "bookmark-level" : "none | <integer>",
  4966. "bookmark-state" : "open | closed",
  4967. "bookmark-target" : "none | <uri> | <attr>",
  4968. "border" : "<border-width> || <border-style> || <color>",
  4969. "border-bottom" : "<border-width> || <border-style> || <color>",
  4970. "border-bottom-color" : "<color>",
  4971. "border-bottom-left-radius" : "<x-one-radius>",
  4972. "border-bottom-right-radius" : "<x-one-radius>",
  4973. "border-bottom-style" : "<border-style>",
  4974. "border-bottom-width" : "<border-width>",
  4975. "border-collapse" : "collapse | separate | inherit",
  4976. "border-color" : { multi: "<color> | inherit", max: 4 },
  4977. "border-image" : 1,
  4978. "border-image-outset" : { multi: "<length> | <number>", max: 4 },
  4979. "border-image-repeat" : { multi: "stretch | repeat | round", max: 2 },
  4980. "border-image-slice" : function(expression) {
  4981. var valid = false,
  4982. numeric = "<number> | <percentage>",
  4983. fill = false,
  4984. count = 0,
  4985. max = 4,
  4986. part;
  4987. if (ValidationTypes.isAny(expression, "fill")) {
  4988. fill = true;
  4989. valid = true;
  4990. }
  4991. while (expression.hasNext() && count < max) {
  4992. valid = ValidationTypes.isAny(expression, numeric);
  4993. if (!valid) {
  4994. break;
  4995. }
  4996. count++;
  4997. }
  4998. if (!fill) {
  4999. ValidationTypes.isAny(expression, "fill");
  5000. } else {
  5001. valid = true;
  5002. }
  5003. if (expression.hasNext()) {
  5004. part = expression.next();
  5005. if (valid) {
  5006. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  5007. } else {
  5008. throw new ValidationError("Expected ([<number> | <percentage>]{1,4} && fill?) but found '" + part + "'.", part.line, part.col);
  5009. }
  5010. }
  5011. },
  5012. "border-image-source" : "<image> | none",
  5013. "border-image-width" : { multi: "<length> | <percentage> | <number> | auto", max: 4 },
  5014. "border-left" : "<border-width> || <border-style> || <color>",
  5015. "border-left-color" : "<color> | inherit",
  5016. "border-left-style" : "<border-style>",
  5017. "border-left-width" : "<border-width>",
  5018. "border-radius" : function(expression) {
  5019. var valid = false,
  5020. numeric = "<length> | <percentage>",
  5021. slash = false,
  5022. fill = false,
  5023. count = 0,
  5024. max = 8,
  5025. part;
  5026. while (expression.hasNext() && count < max) {
  5027. valid = ValidationTypes.isAny(expression, numeric);
  5028. if (!valid) {
  5029. if (expression.peek() == "/" && count > 1 && !slash) {
  5030. slash = true;
  5031. max = count + 5;
  5032. expression.next();
  5033. } else {
  5034. break;
  5035. }
  5036. }
  5037. count++;
  5038. }
  5039. if (expression.hasNext()) {
  5040. part = expression.next();
  5041. if (valid) {
  5042. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  5043. } else {
  5044. throw new ValidationError("Expected (<'border-radius'>) but found '" + part + "'.", part.line, part.col);
  5045. }
  5046. }
  5047. },
  5048. "border-right" : "<border-width> || <border-style> || <color>",
  5049. "border-right-color" : "<color> | inherit",
  5050. "border-right-style" : "<border-style>",
  5051. "border-right-width" : "<border-width>",
  5052. "border-spacing" : { multi: "<length> | inherit", max: 2 },
  5053. "border-style" : { multi: "<border-style>", max: 4 },
  5054. "border-top" : "<border-width> || <border-style> || <color>",
  5055. "border-top-color" : "<color> | inherit",
  5056. "border-top-left-radius" : "<x-one-radius>",
  5057. "border-top-right-radius" : "<x-one-radius>",
  5058. "border-top-style" : "<border-style>",
  5059. "border-top-width" : "<border-width>",
  5060. "border-width" : { multi: "<border-width>", max: 4 },
  5061. "bottom" : "<margin-width> | inherit",
  5062. "box-align" : "start | end | center | baseline | stretch", //http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/
  5063. "box-decoration-break" : "slice |clone",
  5064. "box-direction" : "normal | reverse | inherit",
  5065. "box-flex" : "<number>",
  5066. "box-flex-group" : "<integer>",
  5067. "box-lines" : "single | multiple",
  5068. "box-ordinal-group" : "<integer>",
  5069. "box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit",
  5070. "box-pack" : "start | end | center | justify",
  5071. "box-shadow" : function (expression) {
  5072. var result = false,
  5073. part;
  5074. if (!ValidationTypes.isAny(expression, "none")) {
  5075. Validation.multiProperty("<shadow>", expression, true, Infinity);
  5076. } else {
  5077. if (expression.hasNext()) {
  5078. part = expression.next();
  5079. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  5080. }
  5081. }
  5082. },
  5083. "box-sizing" : "content-box | border-box | inherit",
  5084. "break-after" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
  5085. "break-before" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
  5086. "break-inside" : "auto | avoid | avoid-page | avoid-column",
  5087. //C
  5088. "caption-side" : "top | bottom | inherit",
  5089. "clear" : "none | right | left | both | inherit",
  5090. "clip" : 1,
  5091. "color" : "<color> | inherit",
  5092. "color-profile" : 1,
  5093. "column-count" : "<integer> | auto", //http://www.w3.org/TR/css3-multicol/
  5094. "column-fill" : "auto | balance",
  5095. "column-gap" : "<length> | normal",
  5096. "column-rule" : "<border-width> || <border-style> || <color>",
  5097. "column-rule-color" : "<color>",
  5098. "column-rule-style" : "<border-style>",
  5099. "column-rule-width" : "<border-width>",
  5100. "column-span" : "none | all",
  5101. "column-width" : "<length> | auto",
  5102. "columns" : 1,
  5103. "content" : 1,
  5104. "counter-increment" : 1,
  5105. "counter-reset" : 1,
  5106. "crop" : "<shape> | auto",
  5107. "cue" : "cue-after | cue-before | inherit",
  5108. "cue-after" : 1,
  5109. "cue-before" : 1,
  5110. "cursor" : 1,
  5111. //D
  5112. "direction" : "ltr | rtl | inherit",
  5113. "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 | box | inline-box | grid | inline-grid | none | inherit",
  5114. "dominant-baseline" : 1,
  5115. "drop-initial-after-adjust" : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>",
  5116. "drop-initial-after-align" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
  5117. "drop-initial-before-adjust" : "before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>",
  5118. "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",
  5119. "drop-initial-size" : "auto | line | <length> | <percentage>",
  5120. "drop-initial-value" : "initial | <integer>",
  5121. //E
  5122. "elevation" : "<angle> | below | level | above | higher | lower | inherit",
  5123. "empty-cells" : "show | hide | inherit",
  5124. //F
  5125. "filter" : 1,
  5126. "fit" : "fill | hidden | meet | slice",
  5127. "fit-position" : 1,
  5128. "float" : "left | right | none | inherit",
  5129. "float-offset" : 1,
  5130. "font" : 1,
  5131. "font-family" : 1,
  5132. "font-size" : "<absolute-size> | <relative-size> | <length> | <percentage> | inherit",
  5133. "font-size-adjust" : "<number> | none | inherit",
  5134. "font-stretch" : "normal | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded | inherit",
  5135. "font-style" : "normal | italic | oblique | inherit",
  5136. "font-variant" : "normal | small-caps | inherit",
  5137. "font-weight" : "normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit",
  5138. //G
  5139. "grid-cell-stacking" : "columns | rows | layer",
  5140. "grid-column" : 1,
  5141. "grid-columns" : 1,
  5142. "grid-column-align" : "start | end | center | stretch",
  5143. "grid-column-sizing" : 1,
  5144. "grid-column-span" : "<integer>",
  5145. "grid-flow" : "none | rows | columns",
  5146. "grid-layer" : "<integer>",
  5147. "grid-row" : 1,
  5148. "grid-rows" : 1,
  5149. "grid-row-align" : "start | end | center | stretch",
  5150. "grid-row-span" : "<integer>",
  5151. "grid-row-sizing" : 1,
  5152. //H
  5153. "hanging-punctuation" : 1,
  5154. "height" : "<margin-width> | inherit",
  5155. "hyphenate-after" : "<integer> | auto",
  5156. "hyphenate-before" : "<integer> | auto",
  5157. "hyphenate-character" : "<string> | auto",
  5158. "hyphenate-lines" : "no-limit | <integer>",
  5159. "hyphenate-resource" : 1,
  5160. "hyphens" : "none | manual | auto",
  5161. //I
  5162. "icon" : 1,
  5163. "image-orientation" : "angle | auto",
  5164. "image-rendering" : 1,
  5165. "image-resolution" : 1,
  5166. "inline-box-align" : "initial | last | <integer>",
  5167. //L
  5168. "left" : "<margin-width> | inherit",
  5169. "letter-spacing" : "<length> | normal | inherit",
  5170. "line-height" : "<number> | <length> | <percentage> | normal | inherit",
  5171. "line-break" : "auto | loose | normal | strict",
  5172. "line-stacking" : 1,
  5173. "line-stacking-ruby" : "exclude-ruby | include-ruby",
  5174. "line-stacking-shift" : "consider-shifts | disregard-shifts",
  5175. "line-stacking-strategy" : "inline-line-height | block-line-height | max-height | grid-height",
  5176. "list-style" : 1,
  5177. "list-style-image" : "<uri> | none | inherit",
  5178. "list-style-position" : "inside | outside | inherit",
  5179. "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 | inherit",
  5180. //M
  5181. "margin" : { multi: "<margin-width> | inherit", max: 4 },
  5182. "margin-bottom" : "<margin-width> | inherit",
  5183. "margin-left" : "<margin-width> | inherit",
  5184. "margin-right" : "<margin-width> | inherit",
  5185. "margin-top" : "<margin-width> | inherit",
  5186. "mark" : 1,
  5187. "mark-after" : 1,
  5188. "mark-before" : 1,
  5189. "marks" : 1,
  5190. "marquee-direction" : 1,
  5191. "marquee-play-count" : 1,
  5192. "marquee-speed" : 1,
  5193. "marquee-style" : 1,
  5194. "max-height" : "<length> | <percentage> | none | inherit",
  5195. "max-width" : "<length> | <percentage> | none | inherit",
  5196. "min-height" : "<length> | <percentage> | inherit",
  5197. "min-width" : "<length> | <percentage> | inherit",
  5198. "move-to" : 1,
  5199. //N
  5200. "nav-down" : 1,
  5201. "nav-index" : 1,
  5202. "nav-left" : 1,
  5203. "nav-right" : 1,
  5204. "nav-up" : 1,
  5205. //O
  5206. "opacity" : "<number> | inherit",
  5207. "orphans" : "<integer> | inherit",
  5208. "outline" : 1,
  5209. "outline-color" : "<color> | invert | inherit",
  5210. "outline-offset" : 1,
  5211. "outline-style" : "<border-style> | inherit",
  5212. "outline-width" : "<border-width> | inherit",
  5213. "overflow" : "visible | hidden | scroll | auto | inherit",
  5214. "overflow-style" : 1,
  5215. "overflow-x" : 1,
  5216. "overflow-y" : 1,
  5217. //P
  5218. "padding" : { multi: "<padding-width> | inherit", max: 4 },
  5219. "padding-bottom" : "<padding-width> | inherit",
  5220. "padding-left" : "<padding-width> | inherit",
  5221. "padding-right" : "<padding-width> | inherit",
  5222. "padding-top" : "<padding-width> | inherit",
  5223. "page" : 1,
  5224. "page-break-after" : "auto | always | avoid | left | right | inherit",
  5225. "page-break-before" : "auto | always | avoid | left | right | inherit",
  5226. "page-break-inside" : "auto | avoid | inherit",
  5227. "page-policy" : 1,
  5228. "pause" : 1,
  5229. "pause-after" : 1,
  5230. "pause-before" : 1,
  5231. "perspective" : 1,
  5232. "perspective-origin" : 1,
  5233. "phonemes" : 1,
  5234. "pitch" : 1,
  5235. "pitch-range" : 1,
  5236. "play-during" : 1,
  5237. "position" : "static | relative | absolute | fixed | inherit",
  5238. "presentation-level" : 1,
  5239. "punctuation-trim" : 1,
  5240. //Q
  5241. "quotes" : 1,
  5242. //R
  5243. "rendering-intent" : 1,
  5244. "resize" : 1,
  5245. "rest" : 1,
  5246. "rest-after" : 1,
  5247. "rest-before" : 1,
  5248. "richness" : 1,
  5249. "right" : "<margin-width> | inherit",
  5250. "rotation" : 1,
  5251. "rotation-point" : 1,
  5252. "ruby-align" : 1,
  5253. "ruby-overhang" : 1,
  5254. "ruby-position" : 1,
  5255. "ruby-span" : 1,
  5256. //S
  5257. "size" : 1,
  5258. "speak" : "normal | none | spell-out | inherit",
  5259. "speak-header" : "once | always | inherit",
  5260. "speak-numeral" : "digits | continuous | inherit",
  5261. "speak-punctuation" : "code | none | inherit",
  5262. "speech-rate" : 1,
  5263. "src" : 1,
  5264. "stress" : 1,
  5265. "string-set" : 1,
  5266. "table-layout" : "auto | fixed | inherit",
  5267. "tab-size" : "<integer> | <length>",
  5268. "target" : 1,
  5269. "target-name" : 1,
  5270. "target-new" : 1,
  5271. "target-position" : 1,
  5272. "text-align" : "left | right | center | justify | inherit" ,
  5273. "text-align-last" : 1,
  5274. "text-decoration" : 1,
  5275. "text-emphasis" : 1,
  5276. "text-height" : 1,
  5277. "text-indent" : "<length> | <percentage> | inherit",
  5278. "text-justify" : "auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida",
  5279. "text-outline" : 1,
  5280. "text-overflow" : 1,
  5281. "text-shadow" : 1,
  5282. "text-transform" : "capitalize | uppercase | lowercase | none | inherit",
  5283. "text-wrap" : "normal | none | avoid",
  5284. "top" : "<margin-width> | inherit",
  5285. "transform" : 1,
  5286. "transform-origin" : 1,
  5287. "transform-style" : 1,
  5288. "transition" : 1,
  5289. "transition-delay" : 1,
  5290. "transition-duration" : 1,
  5291. "transition-property" : 1,
  5292. "transition-timing-function" : 1,
  5293. //U
  5294. "unicode-bidi" : "normal | embed | bidi-override | inherit",
  5295. "user-modify" : "read-only | read-write | write-only | inherit",
  5296. "user-select" : "none | text | toggle | element | elements | all | inherit",
  5297. //V
  5298. "vertical-align" : "<percentage> | <length> | baseline | sub | super | top | text-top | middle | bottom | text-bottom | inherit",
  5299. "visibility" : "visible | hidden | collapse | inherit",
  5300. "voice-balance" : 1,
  5301. "voice-duration" : 1,
  5302. "voice-family" : 1,
  5303. "voice-pitch" : 1,
  5304. "voice-pitch-range" : 1,
  5305. "voice-rate" : 1,
  5306. "voice-stress" : 1,
  5307. "voice-volume" : 1,
  5308. "volume" : 1,
  5309. //W
  5310. "white-space" : "normal | pre | nowrap | pre-wrap | pre-line | inherit",
  5311. "white-space-collapse" : 1,
  5312. "widows" : "<integer> | inherit",
  5313. "width" : "<length> | <percentage> | auto | inherit" ,
  5314. "word-break" : "normal | keep-all | break-all",
  5315. "word-spacing" : "<length> | normal | inherit",
  5316. "word-wrap" : 1,
  5317. //Z
  5318. "z-index" : "<integer> | auto | inherit",
  5319. "zoom" : "<number> | <percentage> | normal"
  5320. };
  5321. /**
  5322. * Represents a selector combinator (whitespace, +, >).
  5323. * @namespace parserlib.css
  5324. * @class PropertyName
  5325. * @extends parserlib.util.SyntaxUnit
  5326. * @constructor
  5327. * @param {String} text The text representation of the unit.
  5328. * @param {String} hack The type of IE hack applied ("*", "_", or null).
  5329. * @param {int} line The line of text on which the unit resides.
  5330. * @param {int} col The column of text on which the unit resides.
  5331. */
  5332. function PropertyName(text, hack, line, col){
  5333. SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_NAME_TYPE);
  5334. this.hack = hack;
  5335. }
  5336. PropertyName.prototype = new SyntaxUnit();
  5337. PropertyName.prototype.constructor = PropertyName;
  5338. PropertyName.prototype.toString = function(){
  5339. return (this.hack ? this.hack : "") + this.text;
  5340. };
  5341. /**
  5342. * Represents a single part of a CSS property value, meaning that it represents
  5343. * just everything single part between ":" and ";". If there are multiple values
  5344. * separated by commas, this type represents just one of the values.
  5345. * @param {String[]} parts An array of value parts making up this value.
  5346. * @param {int} line The line of text on which the unit resides.
  5347. * @param {int} col The column of text on which the unit resides.
  5348. * @namespace parserlib.css
  5349. * @class PropertyValue
  5350. * @extends parserlib.util.SyntaxUnit
  5351. * @constructor
  5352. */
  5353. function PropertyValue(parts, line, col){
  5354. SyntaxUnit.call(this, parts.join(" "), line, col, Parser.PROPERTY_VALUE_TYPE);
  5355. this.parts = parts;
  5356. }
  5357. PropertyValue.prototype = new SyntaxUnit();
  5358. PropertyValue.prototype.constructor = PropertyValue;
  5359. /**
  5360. * A utility class that allows for easy iteration over the various parts of a
  5361. * property value.
  5362. * @param {parserlib.css.PropertyValue} value The property value to iterate over.
  5363. * @namespace parserlib.css
  5364. * @class PropertyValueIterator
  5365. * @constructor
  5366. */
  5367. function PropertyValueIterator(value){
  5368. /**
  5369. * Iterator value
  5370. * @type int
  5371. * @property _i
  5372. * @private
  5373. */
  5374. this._i = 0;
  5375. this._parts = value.parts;
  5376. this._marks = [];
  5377. this.value = value;
  5378. }
  5379. /**
  5380. * Returns the total number of parts in the value.
  5381. * @return {int} The total number of parts in the value.
  5382. * @method count
  5383. */
  5384. PropertyValueIterator.prototype.count = function(){
  5385. return this._parts.length;
  5386. };
  5387. PropertyValueIterator.prototype.isFirst = function(){
  5388. return this._i === 0;
  5389. };
  5390. PropertyValueIterator.prototype.hasNext = function(){
  5391. return (this._i < this._parts.length);
  5392. };
  5393. PropertyValueIterator.prototype.mark = function(){
  5394. this._marks.push(this._i);
  5395. };
  5396. PropertyValueIterator.prototype.peek = function(count){
  5397. return this.hasNext() ? this._parts[this._i + (count || 0)] : null;
  5398. };
  5399. PropertyValueIterator.prototype.next = function(){
  5400. return this.hasNext() ? this._parts[this._i++] : null;
  5401. };
  5402. PropertyValueIterator.prototype.previous = function(){
  5403. return this._i > 0 ? this._parts[--this._i] : null;
  5404. };
  5405. PropertyValueIterator.prototype.restore = function(){
  5406. if (this._marks.length){
  5407. this._i = this._marks.pop();
  5408. }
  5409. };
  5410. /**
  5411. * Represents a single part of a CSS property value, meaning that it represents
  5412. * just one part of the data between ":" and ";".
  5413. * @param {String} text The text representation of the unit.
  5414. * @param {int} line The line of text on which the unit resides.
  5415. * @param {int} col The column of text on which the unit resides.
  5416. * @namespace parserlib.css
  5417. * @class PropertyValuePart
  5418. * @extends parserlib.util.SyntaxUnit
  5419. * @constructor
  5420. */
  5421. function PropertyValuePart(text, line, col){
  5422. SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_VALUE_PART_TYPE);
  5423. this.type = "unknown";
  5424. //figure out what type of data it is
  5425. var temp;
  5426. //it is a measurement?
  5427. if (/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)){ //dimension
  5428. this.type = "dimension";
  5429. this.value = +RegExp.$1;
  5430. this.units = RegExp.$2;
  5431. //try to narrow down
  5432. switch(this.units.toLowerCase()){
  5433. case "em":
  5434. case "rem":
  5435. case "ex":
  5436. case "px":
  5437. case "cm":
  5438. case "mm":
  5439. case "in":
  5440. case "pt":
  5441. case "pc":
  5442. case "ch":
  5443. this.type = "length";
  5444. break;
  5445. case "deg":
  5446. case "rad":
  5447. case "grad":
  5448. this.type = "angle";
  5449. break;
  5450. case "ms":
  5451. case "s":
  5452. this.type = "time";
  5453. break;
  5454. case "hz":
  5455. case "khz":
  5456. this.type = "frequency";
  5457. break;
  5458. case "dpi":
  5459. case "dpcm":
  5460. this.type = "resolution";
  5461. break;
  5462. //default
  5463. }
  5464. } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage
  5465. this.type = "percentage";
  5466. this.value = +RegExp.$1;
  5467. } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage
  5468. this.type = "percentage";
  5469. this.value = +RegExp.$1;
  5470. } else if (/^([+\-]?\d+)$/i.test(text)){ //integer
  5471. this.type = "integer";
  5472. this.value = +RegExp.$1;
  5473. } else if (/^([+\-]?[\d\.]+)$/i.test(text)){ //number
  5474. this.type = "number";
  5475. this.value = +RegExp.$1;
  5476. } else if (/^#([a-f0-9]{3,6})/i.test(text)){ //hexcolor
  5477. this.type = "color";
  5478. temp = RegExp.$1;
  5479. if (temp.length == 3){
  5480. this.red = parseInt(temp.charAt(0)+temp.charAt(0),16);
  5481. this.green = parseInt(temp.charAt(1)+temp.charAt(1),16);
  5482. this.blue = parseInt(temp.charAt(2)+temp.charAt(2),16);
  5483. } else {
  5484. this.red = parseInt(temp.substring(0,2),16);
  5485. this.green = parseInt(temp.substring(2,4),16);
  5486. this.blue = parseInt(temp.substring(4,6),16);
  5487. }
  5488. } else if (/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)){ //rgb() color with absolute numbers
  5489. this.type = "color";
  5490. this.red = +RegExp.$1;
  5491. this.green = +RegExp.$2;
  5492. this.blue = +RegExp.$3;
  5493. } else if (/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)){ //rgb() color with percentages
  5494. this.type = "color";
  5495. this.red = +RegExp.$1 * 255 / 100;
  5496. this.green = +RegExp.$2 * 255 / 100;
  5497. this.blue = +RegExp.$3 * 255 / 100;
  5498. } else if (/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)){ //rgba() color with absolute numbers
  5499. this.type = "color";
  5500. this.red = +RegExp.$1;
  5501. this.green = +RegExp.$2;
  5502. this.blue = +RegExp.$3;
  5503. this.alpha = +RegExp.$4;
  5504. } else if (/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)){ //rgba() color with percentages
  5505. this.type = "color";
  5506. this.red = +RegExp.$1 * 255 / 100;
  5507. this.green = +RegExp.$2 * 255 / 100;
  5508. this.blue = +RegExp.$3 * 255 / 100;
  5509. this.alpha = +RegExp.$4;
  5510. } else if (/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)){ //hsl()
  5511. this.type = "color";
  5512. this.hue = +RegExp.$1;
  5513. this.saturation = +RegExp.$2 / 100;
  5514. this.lightness = +RegExp.$3 / 100;
  5515. } else if (/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)){ //hsla() color with percentages
  5516. this.type = "color";
  5517. this.hue = +RegExp.$1;
  5518. this.saturation = +RegExp.$2 / 100;
  5519. this.lightness = +RegExp.$3 / 100;
  5520. this.alpha = +RegExp.$4;
  5521. } else if (/^url\(["']?([^\)"']+)["']?\)/i.test(text)){ //URI
  5522. this.type = "uri";
  5523. this.uri = RegExp.$1;
  5524. } else if (/^([^\(]+)\(/i.test(text)){
  5525. this.type = "function";
  5526. this.name = RegExp.$1;
  5527. this.value = text;
  5528. } else if (/^["'][^"']*["']/.test(text)){ //string
  5529. this.type = "string";
  5530. this.value = eval(text);
  5531. } else if (Colors[text.toLowerCase()]){ //named color
  5532. this.type = "color";
  5533. temp = Colors[text.toLowerCase()].substring(1);
  5534. this.red = parseInt(temp.substring(0,2),16);
  5535. this.green = parseInt(temp.substring(2,4),16);
  5536. this.blue = parseInt(temp.substring(4,6),16);
  5537. } else if (/^[\,\/]$/.test(text)){
  5538. this.type = "operator";
  5539. this.value = text;
  5540. } else if (/^[a-z\-\u0080-\uFFFF][a-z0-9\-\u0080-\uFFFF]*$/i.test(text)){
  5541. this.type = "identifier";
  5542. this.value = text;
  5543. }
  5544. }
  5545. PropertyValuePart.prototype = new SyntaxUnit();
  5546. PropertyValuePart.prototype.constructor = PropertyValuePart;
  5547. PropertyValuePart.fromToken = function(token){
  5548. return new PropertyValuePart(token.value, token.startLine, token.startCol);
  5549. };
  5550. var Pseudos = {
  5551. ":first-letter": 1,
  5552. ":first-line": 1,
  5553. ":before": 1,
  5554. ":after": 1
  5555. };
  5556. Pseudos.ELEMENT = 1;
  5557. Pseudos.CLASS = 2;
  5558. Pseudos.isElement = function(pseudo){
  5559. return pseudo.indexOf("::") === 0 || Pseudos[pseudo.toLowerCase()] == Pseudos.ELEMENT;
  5560. };
  5561. /**
  5562. * Represents an entire single selector, including all parts but not
  5563. * including multiple selectors (those separated by commas).
  5564. * @namespace parserlib.css
  5565. * @class Selector
  5566. * @extends parserlib.util.SyntaxUnit
  5567. * @constructor
  5568. * @param {Array} parts Array of selectors parts making up this selector.
  5569. * @param {int} line The line of text on which the unit resides.
  5570. * @param {int} col The column of text on which the unit resides.
  5571. */
  5572. function Selector(parts, line, col){
  5573. SyntaxUnit.call(this, parts.join(" "), line, col, Parser.SELECTOR_TYPE);
  5574. this.parts = parts;
  5575. this.specificity = Specificity.calculate(this);
  5576. }
  5577. Selector.prototype = new SyntaxUnit();
  5578. Selector.prototype.constructor = Selector;
  5579. /**
  5580. * Represents a single part of a selector string, meaning a single set of
  5581. * element name and modifiers. This does not include combinators such as
  5582. * spaces, +, >, etc.
  5583. * @namespace parserlib.css
  5584. * @class SelectorPart
  5585. * @extends parserlib.util.SyntaxUnit
  5586. * @constructor
  5587. * @param {String} elementName The element name in the selector or null
  5588. * if there is no element name.
  5589. * @param {Array} modifiers Array of individual modifiers for the element.
  5590. * May be empty if there are none.
  5591. * @param {String} text The text representation of the unit.
  5592. * @param {int} line The line of text on which the unit resides.
  5593. * @param {int} col The column of text on which the unit resides.
  5594. */
  5595. function SelectorPart(elementName, modifiers, text, line, col){
  5596. SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_PART_TYPE);
  5597. this.elementName = elementName;
  5598. this.modifiers = modifiers;
  5599. }
  5600. SelectorPart.prototype = new SyntaxUnit();
  5601. SelectorPart.prototype.constructor = SelectorPart;
  5602. /**
  5603. * Represents a selector modifier string, meaning a class name, element name,
  5604. * element ID, pseudo rule, etc.
  5605. * @namespace parserlib.css
  5606. * @class SelectorSubPart
  5607. * @extends parserlib.util.SyntaxUnit
  5608. * @constructor
  5609. * @param {String} text The text representation of the unit.
  5610. * @param {String} type The type of selector modifier.
  5611. * @param {int} line The line of text on which the unit resides.
  5612. * @param {int} col The column of text on which the unit resides.
  5613. */
  5614. function SelectorSubPart(text, type, line, col){
  5615. SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_SUB_PART_TYPE);
  5616. this.type = type;
  5617. this.args = [];
  5618. }
  5619. SelectorSubPart.prototype = new SyntaxUnit();
  5620. SelectorSubPart.prototype.constructor = SelectorSubPart;
  5621. /**
  5622. * Represents a selector's specificity.
  5623. * @namespace parserlib.css
  5624. * @class Specificity
  5625. * @constructor
  5626. * @param {int} a Should be 1 for inline styles, zero for stylesheet styles
  5627. * @param {int} b Number of ID selectors
  5628. * @param {int} c Number of classes and pseudo classes
  5629. * @param {int} d Number of element names and pseudo elements
  5630. */
  5631. function Specificity(a, b, c, d){
  5632. this.a = a;
  5633. this.b = b;
  5634. this.c = c;
  5635. this.d = d;
  5636. }
  5637. Specificity.prototype = {
  5638. constructor: Specificity,
  5639. /**
  5640. * Compare this specificity to another.
  5641. * @param {Specificity} other The other specificity to compare to.
  5642. * @return {int} -1 if the other specificity is larger, 1 if smaller, 0 if equal.
  5643. * @method compare
  5644. */
  5645. compare: function(other){
  5646. var comps = ["a", "b", "c", "d"],
  5647. i, len;
  5648. for (i=0, len=comps.length; i < len; i++){
  5649. if (this[comps[i]] < other[comps[i]]){
  5650. return -1;
  5651. } else if (this[comps[i]] > other[comps[i]]){
  5652. return 1;
  5653. }
  5654. }
  5655. return 0;
  5656. },
  5657. /**
  5658. * Creates a numeric value for the specificity.
  5659. * @return {int} The numeric value for the specificity.
  5660. * @method valueOf
  5661. */
  5662. valueOf: function(){
  5663. return (this.a * 1000) + (this.b * 100) + (this.c * 10) + this.d;
  5664. },
  5665. /**
  5666. * Returns a string representation for specificity.
  5667. * @return {String} The string representation of specificity.
  5668. * @method toString
  5669. */
  5670. toString: function(){
  5671. return this.a + "," + this.b + "," + this.c + "," + this.d;
  5672. }
  5673. };
  5674. Specificity.calculate = function(selector){
  5675. var i, len,
  5676. part,
  5677. b=0, c=0, d=0;
  5678. function updateValues(part){
  5679. var i, j, len, num,
  5680. elementName = part.elementName ? part.elementName.text : "",
  5681. modifier;
  5682. if (elementName && elementName.charAt(elementName.length-1) != "*") {
  5683. d++;
  5684. }
  5685. for (i=0, len=part.modifiers.length; i < len; i++){
  5686. modifier = part.modifiers[i];
  5687. switch(modifier.type){
  5688. case "class":
  5689. case "attribute":
  5690. c++;
  5691. break;
  5692. case "id":
  5693. b++;
  5694. break;
  5695. case "pseudo":
  5696. if (Pseudos.isElement(modifier.text)){
  5697. d++;
  5698. } else {
  5699. c++;
  5700. }
  5701. break;
  5702. case "not":
  5703. for (j=0, num=modifier.args.length; j < num; j++){
  5704. updateValues(modifier.args[j]);
  5705. }
  5706. }
  5707. }
  5708. }
  5709. for (i=0, len=selector.parts.length; i < len; i++){
  5710. part = selector.parts[i];
  5711. if (part instanceof SelectorPart){
  5712. updateValues(part);
  5713. }
  5714. }
  5715. return new Specificity(0, b, c, d);
  5716. };
  5717. var h = /^[0-9a-fA-F]$/,
  5718. nonascii = /^[\u0080-\uFFFF]$/,
  5719. nl = /\n|\r\n|\r|\f/;
  5720. //-----------------------------------------------------------------------------
  5721. // Helper functions
  5722. //-----------------------------------------------------------------------------
  5723. function isHexDigit(c){
  5724. return c !== null && h.test(c);
  5725. }
  5726. function isDigit(c){
  5727. return c !== null && /\d/.test(c);
  5728. }
  5729. function isWhitespace(c){
  5730. return c !== null && /\s/.test(c);
  5731. }
  5732. function isNewLine(c){
  5733. return c !== null && nl.test(c);
  5734. }
  5735. function isNameStart(c){
  5736. return c !== null && (/[a-z_\u0080-\uFFFF\\]/i.test(c));
  5737. }
  5738. function isNameChar(c){
  5739. return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
  5740. }
  5741. function isIdentStart(c){
  5742. return c !== null && (isNameStart(c) || /\-\\/.test(c));
  5743. }
  5744. function mix(receiver, supplier){
  5745. for (var prop in supplier){
  5746. if (supplier.hasOwnProperty(prop)){
  5747. receiver[prop] = supplier[prop];
  5748. }
  5749. }
  5750. return receiver;
  5751. }
  5752. //-----------------------------------------------------------------------------
  5753. // CSS Token Stream
  5754. //-----------------------------------------------------------------------------
  5755. /**
  5756. * A token stream that produces CSS tokens.
  5757. * @param {String|Reader} input The source of text to tokenize.
  5758. * @constructor
  5759. * @class TokenStream
  5760. * @namespace parserlib.css
  5761. */
  5762. function TokenStream(input){
  5763. TokenStreamBase.call(this, input, Tokens);
  5764. }
  5765. TokenStream.prototype = mix(new TokenStreamBase(), {
  5766. /**
  5767. * Overrides the TokenStreamBase method of the same name
  5768. * to produce CSS tokens.
  5769. * @param {variant} channel The name of the channel to use
  5770. * for the next token.
  5771. * @return {Object} A token object representing the next token.
  5772. * @method _getToken
  5773. * @private
  5774. */
  5775. _getToken: function(channel){
  5776. var c,
  5777. reader = this._reader,
  5778. token = null,
  5779. startLine = reader.getLine(),
  5780. startCol = reader.getCol();
  5781. c = reader.read();
  5782. while(c){
  5783. switch(c){
  5784. /*
  5785. * Potential tokens:
  5786. * - COMMENT
  5787. * - SLASH
  5788. * - CHAR
  5789. */
  5790. case "/":
  5791. if(reader.peek() == "*"){
  5792. token = this.commentToken(c, startLine, startCol);
  5793. } else {
  5794. token = this.charToken(c, startLine, startCol);
  5795. }
  5796. break;
  5797. case "|":
  5798. case "~":
  5799. case "^":
  5800. case "$":
  5801. case "*":
  5802. if(reader.peek() == "="){
  5803. token = this.comparisonToken(c, startLine, startCol);
  5804. } else {
  5805. token = this.charToken(c, startLine, startCol);
  5806. }
  5807. break;
  5808. case "\"":
  5809. case "'":
  5810. token = this.stringToken(c, startLine, startCol);
  5811. break;
  5812. case "#":
  5813. if (isNameChar(reader.peek())){
  5814. token = this.hashToken(c, startLine, startCol);
  5815. } else {
  5816. token = this.charToken(c, startLine, startCol);
  5817. }
  5818. break;
  5819. case ".":
  5820. if (isDigit(reader.peek())){
  5821. token = this.numberToken(c, startLine, startCol);
  5822. } else {
  5823. token = this.charToken(c, startLine, startCol);
  5824. }
  5825. break;
  5826. case "-":
  5827. if (reader.peek() == "-"){ //could be closing HTML-style comment
  5828. token = this.htmlCommentEndToken(c, startLine, startCol);
  5829. } else if (isNameStart(reader.peek())){
  5830. token = this.identOrFunctionToken(c, startLine, startCol);
  5831. } else {
  5832. token = this.charToken(c, startLine, startCol);
  5833. }
  5834. break;
  5835. case "!":
  5836. token = this.importantToken(c, startLine, startCol);
  5837. break;
  5838. case "@":
  5839. token = this.atRuleToken(c, startLine, startCol);
  5840. break;
  5841. case ":":
  5842. token = this.notToken(c, startLine, startCol);
  5843. break;
  5844. case "<":
  5845. token = this.htmlCommentStartToken(c, startLine, startCol);
  5846. break;
  5847. case "U":
  5848. case "u":
  5849. if (reader.peek() == "+"){
  5850. token = this.unicodeRangeToken(c, startLine, startCol);
  5851. break;
  5852. }
  5853. /* falls through */
  5854. default:
  5855. /*
  5856. * Potential tokens:
  5857. * - NUMBER
  5858. * - DIMENSION
  5859. * - LENGTH
  5860. * - FREQ
  5861. * - TIME
  5862. * - EMS
  5863. * - EXS
  5864. * - ANGLE
  5865. */
  5866. if (isDigit(c)){
  5867. token = this.numberToken(c, startLine, startCol);
  5868. } else
  5869. /*
  5870. * Potential tokens:
  5871. * - S
  5872. */
  5873. if (isWhitespace(c)){
  5874. token = this.whitespaceToken(c, startLine, startCol);
  5875. } else
  5876. /*
  5877. * Potential tokens:
  5878. * - IDENT
  5879. */
  5880. if (isIdentStart(c)){
  5881. token = this.identOrFunctionToken(c, startLine, startCol);
  5882. } else
  5883. /*
  5884. * Potential tokens:
  5885. * - CHAR
  5886. * - PLUS
  5887. */
  5888. {
  5889. token = this.charToken(c, startLine, startCol);
  5890. }
  5891. }
  5892. //make sure this token is wanted
  5893. //TODO: check channel
  5894. break;
  5895. }
  5896. if (!token && c === null){
  5897. token = this.createToken(Tokens.EOF,null,startLine,startCol);
  5898. }
  5899. return token;
  5900. },
  5901. //-------------------------------------------------------------------------
  5902. // Methods to create tokens
  5903. //-------------------------------------------------------------------------
  5904. /**
  5905. * Produces a token based on available data and the current
  5906. * reader position information. This method is called by other
  5907. * private methods to create tokens and is never called directly.
  5908. * @param {int} tt The token type.
  5909. * @param {String} value The text value of the token.
  5910. * @param {int} startLine The beginning line for the character.
  5911. * @param {int} startCol The beginning column for the character.
  5912. * @param {Object} options (Optional) Specifies a channel property
  5913. * to indicate that a different channel should be scanned
  5914. * and/or a hide property indicating that the token should
  5915. * be hidden.
  5916. * @return {Object} A token object.
  5917. * @method createToken
  5918. */
  5919. createToken: function(tt, value, startLine, startCol, options){
  5920. var reader = this._reader;
  5921. options = options || {};
  5922. return {
  5923. value: value,
  5924. type: tt,
  5925. channel: options.channel,
  5926. hide: options.hide || false,
  5927. startLine: startLine,
  5928. startCol: startCol,
  5929. endLine: reader.getLine(),
  5930. endCol: reader.getCol()
  5931. };
  5932. },
  5933. //-------------------------------------------------------------------------
  5934. // Methods to create specific tokens
  5935. //-------------------------------------------------------------------------
  5936. /**
  5937. * Produces a token for any at-rule. If the at-rule is unknown, then
  5938. * the token is for a single "@" character.
  5939. * @param {String} first The first character for the token.
  5940. * @param {int} startLine The beginning line for the character.
  5941. * @param {int} startCol The beginning column for the character.
  5942. * @return {Object} A token object.
  5943. * @method atRuleToken
  5944. */
  5945. atRuleToken: function(first, startLine, startCol){
  5946. var rule = first,
  5947. reader = this._reader,
  5948. tt = Tokens.CHAR,
  5949. valid = false,
  5950. ident,
  5951. c;
  5952. reader.mark();
  5953. //try to find the at-keyword
  5954. ident = this.readName();
  5955. rule = first + ident;
  5956. tt = Tokens.type(rule.toLowerCase());
  5957. //if it's not valid, use the first character only and reset the reader
  5958. if (tt == Tokens.CHAR || tt == Tokens.UNKNOWN){
  5959. if (rule.length > 1){
  5960. tt = Tokens.UNKNOWN_SYM;
  5961. } else {
  5962. tt = Tokens.CHAR;
  5963. rule = first;
  5964. reader.reset();
  5965. }
  5966. }
  5967. return this.createToken(tt, rule, startLine, startCol);
  5968. },
  5969. /**
  5970. * Produces a character token based on the given character
  5971. * and location in the stream. If there's a special (non-standard)
  5972. * token name, this is used; otherwise CHAR is used.
  5973. * @param {String} c The character for the token.
  5974. * @param {int} startLine The beginning line for the character.
  5975. * @param {int} startCol The beginning column for the character.
  5976. * @return {Object} A token object.
  5977. * @method charToken
  5978. */
  5979. charToken: function(c, startLine, startCol){
  5980. var tt = Tokens.type(c);
  5981. if (tt == -1){
  5982. tt = Tokens.CHAR;
  5983. }
  5984. return this.createToken(tt, c, startLine, startCol);
  5985. },
  5986. /**
  5987. * Produces a character token based on the given character
  5988. * and location in the stream. If there's a special (non-standard)
  5989. * token name, this is used; otherwise CHAR is used.
  5990. * @param {String} first The first character for the token.
  5991. * @param {int} startLine The beginning line for the character.
  5992. * @param {int} startCol The beginning column for the character.
  5993. * @return {Object} A token object.
  5994. * @method commentToken
  5995. */
  5996. commentToken: function(first, startLine, startCol){
  5997. var reader = this._reader,
  5998. comment = this.readComment(first);
  5999. return this.createToken(Tokens.COMMENT, comment, startLine, startCol);
  6000. },
  6001. /**
  6002. * Produces a comparison token based on the given character
  6003. * and location in the stream. The next character must be
  6004. * read and is already known to be an equals sign.
  6005. * @param {String} c The character for the token.
  6006. * @param {int} startLine The beginning line for the character.
  6007. * @param {int} startCol The beginning column for the character.
  6008. * @return {Object} A token object.
  6009. * @method comparisonToken
  6010. */
  6011. comparisonToken: function(c, startLine, startCol){
  6012. var reader = this._reader,
  6013. comparison = c + reader.read(),
  6014. tt = Tokens.type(comparison) || Tokens.CHAR;
  6015. return this.createToken(tt, comparison, startLine, startCol);
  6016. },
  6017. /**
  6018. * Produces a hash token based on the specified information. The
  6019. * first character provided is the pound sign (#) and then this
  6020. * method reads a name afterward.
  6021. * @param {String} first The first character (#) in the hash name.
  6022. * @param {int} startLine The beginning line for the character.
  6023. * @param {int} startCol The beginning column for the character.
  6024. * @return {Object} A token object.
  6025. * @method hashToken
  6026. */
  6027. hashToken: function(first, startLine, startCol){
  6028. var reader = this._reader,
  6029. name = this.readName(first);
  6030. return this.createToken(Tokens.HASH, name, startLine, startCol);
  6031. },
  6032. /**
  6033. * Produces a CDO or CHAR token based on the specified information. The
  6034. * first character is provided and the rest is read by the function to determine
  6035. * the correct token to create.
  6036. * @param {String} first The first character in the token.
  6037. * @param {int} startLine The beginning line for the character.
  6038. * @param {int} startCol The beginning column for the character.
  6039. * @return {Object} A token object.
  6040. * @method htmlCommentStartToken
  6041. */
  6042. htmlCommentStartToken: function(first, startLine, startCol){
  6043. var reader = this._reader,
  6044. text = first;
  6045. reader.mark();
  6046. text += reader.readCount(3);
  6047. if (text == "<!--"){
  6048. return this.createToken(Tokens.CDO, text, startLine, startCol);
  6049. } else {
  6050. reader.reset();
  6051. return this.charToken(first, startLine, startCol);
  6052. }
  6053. },
  6054. /**
  6055. * Produces a CDC or CHAR token based on the specified information. The
  6056. * first character is provided and the rest is read by the function to determine
  6057. * the correct token to create.
  6058. * @param {String} first The first character in the token.
  6059. * @param {int} startLine The beginning line for the character.
  6060. * @param {int} startCol The beginning column for the character.
  6061. * @return {Object} A token object.
  6062. * @method htmlCommentEndToken
  6063. */
  6064. htmlCommentEndToken: function(first, startLine, startCol){
  6065. var reader = this._reader,
  6066. text = first;
  6067. reader.mark();
  6068. text += reader.readCount(2);
  6069. if (text == "-->"){
  6070. return this.createToken(Tokens.CDC, text, startLine, startCol);
  6071. } else {
  6072. reader.reset();
  6073. return this.charToken(first, startLine, startCol);
  6074. }
  6075. },
  6076. /**
  6077. * Produces an IDENT or FUNCTION token based on the specified information. The
  6078. * first character is provided and the rest is read by the function to determine
  6079. * the correct token to create.
  6080. * @param {String} first The first character in the identifier.
  6081. * @param {int} startLine The beginning line for the character.
  6082. * @param {int} startCol The beginning column for the character.
  6083. * @return {Object} A token object.
  6084. * @method identOrFunctionToken
  6085. */
  6086. identOrFunctionToken: function(first, startLine, startCol){
  6087. var reader = this._reader,
  6088. ident = this.readName(first),
  6089. tt = Tokens.IDENT;
  6090. //if there's a left paren immediately after, it's a URI or function
  6091. if (reader.peek() == "("){
  6092. ident += reader.read();
  6093. if (ident.toLowerCase() == "url("){
  6094. tt = Tokens.URI;
  6095. ident = this.readURI(ident);
  6096. //didn't find a valid URL or there's no closing paren
  6097. if (ident.toLowerCase() == "url("){
  6098. tt = Tokens.FUNCTION;
  6099. }
  6100. } else {
  6101. tt = Tokens.FUNCTION;
  6102. }
  6103. } else if (reader.peek() == ":"){ //might be an IE function
  6104. //IE-specific functions always being with progid:
  6105. if (ident.toLowerCase() == "progid"){
  6106. ident += reader.readTo("(");
  6107. tt = Tokens.IE_FUNCTION;
  6108. }
  6109. }
  6110. return this.createToken(tt, ident, startLine, startCol);
  6111. },
  6112. /**
  6113. * Produces an IMPORTANT_SYM or CHAR token based on the specified information. The
  6114. * first character is provided and the rest is read by the function to determine
  6115. * the correct token to create.
  6116. * @param {String} first The first character in the token.
  6117. * @param {int} startLine The beginning line for the character.
  6118. * @param {int} startCol The beginning column for the character.
  6119. * @return {Object} A token object.
  6120. * @method importantToken
  6121. */
  6122. importantToken: function(first, startLine, startCol){
  6123. var reader = this._reader,
  6124. important = first,
  6125. tt = Tokens.CHAR,
  6126. temp,
  6127. c;
  6128. reader.mark();
  6129. c = reader.read();
  6130. while(c){
  6131. //there can be a comment in here
  6132. if (c == "/"){
  6133. //if the next character isn't a star, then this isn't a valid !important token
  6134. if (reader.peek() != "*"){
  6135. break;
  6136. } else {
  6137. temp = this.readComment(c);
  6138. if (temp === ""){ //broken!
  6139. break;
  6140. }
  6141. }
  6142. } else if (isWhitespace(c)){
  6143. important += c + this.readWhitespace();
  6144. } else if (/i/i.test(c)){
  6145. temp = reader.readCount(8);
  6146. if (/mportant/i.test(temp)){
  6147. important += c + temp;
  6148. tt = Tokens.IMPORTANT_SYM;
  6149. }
  6150. break; //we're done
  6151. } else {
  6152. break;
  6153. }
  6154. c = reader.read();
  6155. }
  6156. if (tt == Tokens.CHAR){
  6157. reader.reset();
  6158. return this.charToken(first, startLine, startCol);
  6159. } else {
  6160. return this.createToken(tt, important, startLine, startCol);
  6161. }
  6162. },
  6163. /**
  6164. * Produces a NOT or CHAR token based on the specified information. The
  6165. * first character is provided and the rest is read by the function to determine
  6166. * the correct token to create.
  6167. * @param {String} first The first character in the token.
  6168. * @param {int} startLine The beginning line for the character.
  6169. * @param {int} startCol The beginning column for the character.
  6170. * @return {Object} A token object.
  6171. * @method notToken
  6172. */
  6173. notToken: function(first, startLine, startCol){
  6174. var reader = this._reader,
  6175. text = first;
  6176. reader.mark();
  6177. text += reader.readCount(4);
  6178. if (text.toLowerCase() == ":not("){
  6179. return this.createToken(Tokens.NOT, text, startLine, startCol);
  6180. } else {
  6181. reader.reset();
  6182. return this.charToken(first, startLine, startCol);
  6183. }
  6184. },
  6185. /**
  6186. * Produces a number token based on the given character
  6187. * and location in the stream. This may return a token of
  6188. * NUMBER, EMS, EXS, LENGTH, ANGLE, TIME, FREQ, DIMENSION,
  6189. * or PERCENTAGE.
  6190. * @param {String} first The first character for the token.
  6191. * @param {int} startLine The beginning line for the character.
  6192. * @param {int} startCol The beginning column for the character.
  6193. * @return {Object} A token object.
  6194. * @method numberToken
  6195. */
  6196. numberToken: function(first, startLine, startCol){
  6197. var reader = this._reader,
  6198. value = this.readNumber(first),
  6199. ident,
  6200. tt = Tokens.NUMBER,
  6201. c = reader.peek();
  6202. if (isIdentStart(c)){
  6203. ident = this.readName(reader.read());
  6204. value += ident;
  6205. if (/^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^vm$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i.test(ident)){
  6206. tt = Tokens.LENGTH;
  6207. } else if (/^deg|^rad$|^grad$/i.test(ident)){
  6208. tt = Tokens.ANGLE;
  6209. } else if (/^ms$|^s$/i.test(ident)){
  6210. tt = Tokens.TIME;
  6211. } else if (/^hz$|^khz$/i.test(ident)){
  6212. tt = Tokens.FREQ;
  6213. } else if (/^dpi$|^dpcm$/i.test(ident)){
  6214. tt = Tokens.RESOLUTION;
  6215. } else {
  6216. tt = Tokens.DIMENSION;
  6217. }
  6218. } else if (c == "%"){
  6219. value += reader.read();
  6220. tt = Tokens.PERCENTAGE;
  6221. }
  6222. return this.createToken(tt, value, startLine, startCol);
  6223. },
  6224. /**
  6225. * Produces a string token based on the given character
  6226. * and location in the stream. Since strings may be indicated
  6227. * by single or double quotes, a failure to match starting
  6228. * and ending quotes results in an INVALID token being generated.
  6229. * The first character in the string is passed in and then
  6230. * the rest are read up to and including the final quotation mark.
  6231. * @param {String} first The first character in the string.
  6232. * @param {int} startLine The beginning line for the character.
  6233. * @param {int} startCol The beginning column for the character.
  6234. * @return {Object} A token object.
  6235. * @method stringToken
  6236. */
  6237. stringToken: function(first, startLine, startCol){
  6238. var delim = first,
  6239. string = first,
  6240. reader = this._reader,
  6241. prev = first,
  6242. tt = Tokens.STRING,
  6243. c = reader.read();
  6244. while(c){
  6245. string += c;
  6246. //if the delimiter is found with an escapement, we're done.
  6247. if (c == delim && prev != "\\"){
  6248. break;
  6249. }
  6250. //if there's a newline without an escapement, it's an invalid string
  6251. if (isNewLine(reader.peek()) && c != "\\"){
  6252. tt = Tokens.INVALID;
  6253. break;
  6254. }
  6255. //save previous and get next
  6256. prev = c;
  6257. c = reader.read();
  6258. }
  6259. //if c is null, that means we're out of input and the string was never closed
  6260. if (c === null){
  6261. tt = Tokens.INVALID;
  6262. }
  6263. return this.createToken(tt, string, startLine, startCol);
  6264. },
  6265. unicodeRangeToken: function(first, startLine, startCol){
  6266. var reader = this._reader,
  6267. value = first,
  6268. temp,
  6269. tt = Tokens.CHAR;
  6270. //then it should be a unicode range
  6271. if (reader.peek() == "+"){
  6272. reader.mark();
  6273. value += reader.read();
  6274. value += this.readUnicodeRangePart(true);
  6275. //ensure there's an actual unicode range here
  6276. if (value.length == 2){
  6277. reader.reset();
  6278. } else {
  6279. tt = Tokens.UNICODE_RANGE;
  6280. //if there's a ? in the first part, there can't be a second part
  6281. if (value.indexOf("?") == -1){
  6282. if (reader.peek() == "-"){
  6283. reader.mark();
  6284. temp = reader.read();
  6285. temp += this.readUnicodeRangePart(false);
  6286. //if there's not another value, back up and just take the first
  6287. if (temp.length == 1){
  6288. reader.reset();
  6289. } else {
  6290. value += temp;
  6291. }
  6292. }
  6293. }
  6294. }
  6295. }
  6296. return this.createToken(tt, value, startLine, startCol);
  6297. },
  6298. /**
  6299. * Produces a S token based on the specified information. Since whitespace
  6300. * may have multiple characters, this consumes all whitespace characters
  6301. * into a single token.
  6302. * @param {String} first The first character in the token.
  6303. * @param {int} startLine The beginning line for the character.
  6304. * @param {int} startCol The beginning column for the character.
  6305. * @return {Object} A token object.
  6306. * @method whitespaceToken
  6307. */
  6308. whitespaceToken: function(first, startLine, startCol){
  6309. var reader = this._reader,
  6310. value = first + this.readWhitespace();
  6311. return this.createToken(Tokens.S, value, startLine, startCol);
  6312. },
  6313. //-------------------------------------------------------------------------
  6314. // Methods to read values from the string stream
  6315. //-------------------------------------------------------------------------
  6316. readUnicodeRangePart: function(allowQuestionMark){
  6317. var reader = this._reader,
  6318. part = "",
  6319. c = reader.peek();
  6320. //first read hex digits
  6321. while(isHexDigit(c) && part.length < 6){
  6322. reader.read();
  6323. part += c;
  6324. c = reader.peek();
  6325. }
  6326. //then read question marks if allowed
  6327. if (allowQuestionMark){
  6328. while(c == "?" && part.length < 6){
  6329. reader.read();
  6330. part += c;
  6331. c = reader.peek();
  6332. }
  6333. }
  6334. //there can't be any other characters after this point
  6335. return part;
  6336. },
  6337. readWhitespace: function(){
  6338. var reader = this._reader,
  6339. whitespace = "",
  6340. c = reader.peek();
  6341. while(isWhitespace(c)){
  6342. reader.read();
  6343. whitespace += c;
  6344. c = reader.peek();
  6345. }
  6346. return whitespace;
  6347. },
  6348. readNumber: function(first){
  6349. var reader = this._reader,
  6350. number = first,
  6351. hasDot = (first == "."),
  6352. c = reader.peek();
  6353. while(c){
  6354. if (isDigit(c)){
  6355. number += reader.read();
  6356. } else if (c == "."){
  6357. if (hasDot){
  6358. break;
  6359. } else {
  6360. hasDot = true;
  6361. number += reader.read();
  6362. }
  6363. } else {
  6364. break;
  6365. }
  6366. c = reader.peek();
  6367. }
  6368. return number;
  6369. },
  6370. readString: function(){
  6371. var reader = this._reader,
  6372. delim = reader.read(),
  6373. string = delim,
  6374. prev = delim,
  6375. c = reader.peek();
  6376. while(c){
  6377. c = reader.read();
  6378. string += c;
  6379. //if the delimiter is found with an escapement, we're done.
  6380. if (c == delim && prev != "\\"){
  6381. break;
  6382. }
  6383. //if there's a newline without an escapement, it's an invalid string
  6384. if (isNewLine(reader.peek()) && c != "\\"){
  6385. string = "";
  6386. break;
  6387. }
  6388. //save previous and get next
  6389. prev = c;
  6390. c = reader.peek();
  6391. }
  6392. //if c is null, that means we're out of input and the string was never closed
  6393. if (c === null){
  6394. string = "";
  6395. }
  6396. return string;
  6397. },
  6398. readURI: function(first){
  6399. var reader = this._reader,
  6400. uri = first,
  6401. inner = "",
  6402. c = reader.peek();
  6403. reader.mark();
  6404. //skip whitespace before
  6405. while(c && isWhitespace(c)){
  6406. reader.read();
  6407. c = reader.peek();
  6408. }
  6409. //it's a string
  6410. if (c == "'" || c == "\""){
  6411. inner = this.readString();
  6412. } else {
  6413. inner = this.readURL();
  6414. }
  6415. c = reader.peek();
  6416. //skip whitespace after
  6417. while(c && isWhitespace(c)){
  6418. reader.read();
  6419. c = reader.peek();
  6420. }
  6421. //if there was no inner value or the next character isn't closing paren, it's not a URI
  6422. if (inner === "" || c != ")"){
  6423. uri = first;
  6424. reader.reset();
  6425. } else {
  6426. uri += inner + reader.read();
  6427. }
  6428. return uri;
  6429. },
  6430. readURL: function(){
  6431. var reader = this._reader,
  6432. url = "",
  6433. c = reader.peek();
  6434. //TODO: Check for escape and nonascii
  6435. while (/^[!#$%&\\*-~]$/.test(c)){
  6436. url += reader.read();
  6437. c = reader.peek();
  6438. }
  6439. return url;
  6440. },
  6441. readName: function(first){
  6442. var reader = this._reader,
  6443. ident = first || "",
  6444. c = reader.peek();
  6445. while(true){
  6446. if (c == "\\"){
  6447. ident += this.readEscape(reader.read());
  6448. c = reader.peek();
  6449. } else if(c && isNameChar(c)){
  6450. ident += reader.read();
  6451. c = reader.peek();
  6452. } else {
  6453. break;
  6454. }
  6455. }
  6456. return ident;
  6457. },
  6458. readEscape: function(first){
  6459. var reader = this._reader,
  6460. cssEscape = first || "",
  6461. i = 0,
  6462. c = reader.peek();
  6463. if (isHexDigit(c)){
  6464. do {
  6465. cssEscape += reader.read();
  6466. c = reader.peek();
  6467. } while(c && isHexDigit(c) && ++i < 6);
  6468. }
  6469. if (cssEscape.length == 3 && /\s/.test(c) ||
  6470. cssEscape.length == 7 || cssEscape.length == 1){
  6471. reader.read();
  6472. } else {
  6473. c = "";
  6474. }
  6475. return cssEscape + c;
  6476. },
  6477. readComment: function(first){
  6478. var reader = this._reader,
  6479. comment = first || "",
  6480. c = reader.read();
  6481. if (c == "*"){
  6482. while(c){
  6483. comment += c;
  6484. //look for end of comment
  6485. if (comment.length > 2 && c == "*" && reader.peek() == "/"){
  6486. comment += reader.read();
  6487. break;
  6488. }
  6489. c = reader.read();
  6490. }
  6491. return comment;
  6492. } else {
  6493. return "";
  6494. }
  6495. }
  6496. });
  6497. var Tokens = [
  6498. /*
  6499. * The following token names are defined in CSS3 Grammar: http://www.w3.org/TR/css3-syntax/#lexical
  6500. */
  6501. //HTML-style comments
  6502. { name: "CDO"},
  6503. { name: "CDC"},
  6504. //ignorables
  6505. { name: "S", whitespace: true/*, channel: "ws"*/},
  6506. { name: "COMMENT", comment: true, hide: true, channel: "comment" },
  6507. //attribute equality
  6508. { name: "INCLUDES", text: "~="},
  6509. { name: "DASHMATCH", text: "|="},
  6510. { name: "PREFIXMATCH", text: "^="},
  6511. { name: "SUFFIXMATCH", text: "$="},
  6512. { name: "SUBSTRINGMATCH", text: "*="},
  6513. //identifier types
  6514. { name: "STRING"},
  6515. { name: "IDENT"},
  6516. { name: "HASH"},
  6517. //at-keywords
  6518. { name: "IMPORT_SYM", text: "@import"},
  6519. { name: "PAGE_SYM", text: "@page"},
  6520. { name: "MEDIA_SYM", text: "@media"},
  6521. { name: "FONT_FACE_SYM", text: "@font-face"},
  6522. { name: "CHARSET_SYM", text: "@charset"},
  6523. { name: "NAMESPACE_SYM", text: "@namespace"},
  6524. { name: "UNKNOWN_SYM" },
  6525. //{ name: "ATKEYWORD"},
  6526. //CSS3 animations
  6527. { name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-ms-keyframes" ] },
  6528. //important symbol
  6529. { name: "IMPORTANT_SYM"},
  6530. //measurements
  6531. { name: "LENGTH"},
  6532. { name: "ANGLE"},
  6533. { name: "TIME"},
  6534. { name: "FREQ"},
  6535. { name: "DIMENSION"},
  6536. { name: "PERCENTAGE"},
  6537. { name: "NUMBER"},
  6538. //functions
  6539. { name: "URI"},
  6540. { name: "FUNCTION"},
  6541. //Unicode ranges
  6542. { name: "UNICODE_RANGE"},
  6543. /*
  6544. * The following token names are defined in CSS3 Selectors: http://www.w3.org/TR/css3-selectors/#selector-syntax
  6545. */
  6546. //invalid string
  6547. { name: "INVALID"},
  6548. //combinators
  6549. { name: "PLUS", text: "+" },
  6550. { name: "GREATER", text: ">"},
  6551. { name: "COMMA", text: ","},
  6552. { name: "TILDE", text: "~"},
  6553. //modifier
  6554. { name: "NOT"},
  6555. /*
  6556. * Defined in CSS3 Paged Media
  6557. */
  6558. { name: "TOPLEFTCORNER_SYM", text: "@top-left-corner"},
  6559. { name: "TOPLEFT_SYM", text: "@top-left"},
  6560. { name: "TOPCENTER_SYM", text: "@top-center"},
  6561. { name: "TOPRIGHT_SYM", text: "@top-right"},
  6562. { name: "TOPRIGHTCORNER_SYM", text: "@top-right-corner"},
  6563. { name: "BOTTOMLEFTCORNER_SYM", text: "@bottom-left-corner"},
  6564. { name: "BOTTOMLEFT_SYM", text: "@bottom-left"},
  6565. { name: "BOTTOMCENTER_SYM", text: "@bottom-center"},
  6566. { name: "BOTTOMRIGHT_SYM", text: "@bottom-right"},
  6567. { name: "BOTTOMRIGHTCORNER_SYM", text: "@bottom-right-corner"},
  6568. { name: "LEFTTOP_SYM", text: "@left-top"},
  6569. { name: "LEFTMIDDLE_SYM", text: "@left-middle"},
  6570. { name: "LEFTBOTTOM_SYM", text: "@left-bottom"},
  6571. { name: "RIGHTTOP_SYM", text: "@right-top"},
  6572. { name: "RIGHTMIDDLE_SYM", text: "@right-middle"},
  6573. { name: "RIGHTBOTTOM_SYM", text: "@right-bottom"},
  6574. /*
  6575. * The following token names are defined in CSS3 Media Queries: http://www.w3.org/TR/css3-mediaqueries/#syntax
  6576. */
  6577. /*{ name: "MEDIA_ONLY", state: "media"},
  6578. { name: "MEDIA_NOT", state: "media"},
  6579. { name: "MEDIA_AND", state: "media"},*/
  6580. { name: "RESOLUTION", state: "media"},
  6581. /*
  6582. * The following token names are not defined in any CSS specification but are used by the lexer.
  6583. */
  6584. //not a real token, but useful for stupid IE filters
  6585. { name: "IE_FUNCTION" },
  6586. //part of CSS3 grammar but not the Flex code
  6587. { name: "CHAR" },
  6588. //TODO: Needed?
  6589. //Not defined as tokens, but might as well be
  6590. {
  6591. name: "PIPE",
  6592. text: "|"
  6593. },
  6594. {
  6595. name: "SLASH",
  6596. text: "/"
  6597. },
  6598. {
  6599. name: "MINUS",
  6600. text: "-"
  6601. },
  6602. {
  6603. name: "STAR",
  6604. text: "*"
  6605. },
  6606. {
  6607. name: "LBRACE",
  6608. text: "{"
  6609. },
  6610. {
  6611. name: "RBRACE",
  6612. text: "}"
  6613. },
  6614. {
  6615. name: "LBRACKET",
  6616. text: "["
  6617. },
  6618. {
  6619. name: "RBRACKET",
  6620. text: "]"
  6621. },
  6622. {
  6623. name: "EQUALS",
  6624. text: "="
  6625. },
  6626. {
  6627. name: "COLON",
  6628. text: ":"
  6629. },
  6630. {
  6631. name: "SEMICOLON",
  6632. text: ";"
  6633. },
  6634. {
  6635. name: "LPAREN",
  6636. text: "("
  6637. },
  6638. {
  6639. name: "RPAREN",
  6640. text: ")"
  6641. },
  6642. {
  6643. name: "DOT",
  6644. text: "."
  6645. }
  6646. ];
  6647. (function(){
  6648. var nameMap = [],
  6649. typeMap = {};
  6650. Tokens.UNKNOWN = -1;
  6651. Tokens.unshift({name:"EOF"});
  6652. for (var i=0, len = Tokens.length; i < len; i++){
  6653. nameMap.push(Tokens[i].name);
  6654. Tokens[Tokens[i].name] = i;
  6655. if (Tokens[i].text){
  6656. if (Tokens[i].text instanceof Array){
  6657. for (var j=0; j < Tokens[i].text.length; j++){
  6658. typeMap[Tokens[i].text[j]] = i;
  6659. }
  6660. } else {
  6661. typeMap[Tokens[i].text] = i;
  6662. }
  6663. }
  6664. }
  6665. Tokens.name = function(tt){
  6666. return nameMap[tt];
  6667. };
  6668. Tokens.type = function(c){
  6669. return typeMap[c] || -1;
  6670. };
  6671. })();
  6672. //This file will likely change a lot! Very experimental!
  6673. /*global Properties, ValidationTypes, ValidationError, PropertyValueIterator */
  6674. var Validation = {
  6675. validate: function(property, value){
  6676. //normalize name
  6677. var name = property.toString().toLowerCase(),
  6678. parts = value.parts,
  6679. expression = new PropertyValueIterator(value),
  6680. spec = Properties[name],
  6681. part,
  6682. valid,
  6683. j, count,
  6684. msg,
  6685. types,
  6686. last,
  6687. literals,
  6688. max, multi, group;
  6689. if (!spec) {
  6690. if (name.indexOf("-") !== 0){ //vendor prefixed are ok
  6691. throw new ValidationError("Unknown property '" + property + "'.", property.line, property.col);
  6692. }
  6693. } else if (typeof spec != "number"){
  6694. //initialization
  6695. if (typeof spec == "string"){
  6696. if (spec.indexOf("||") > -1) {
  6697. this.groupProperty(spec, expression);
  6698. } else {
  6699. this.singleProperty(spec, expression, 1);
  6700. }
  6701. } else if (spec.multi) {
  6702. this.multiProperty(spec.multi, expression, spec.comma, spec.max || Infinity);
  6703. } else if (typeof spec == "function") {
  6704. spec(expression);
  6705. }
  6706. }
  6707. },
  6708. singleProperty: function(types, expression, max, partial) {
  6709. var result = false,
  6710. value = expression.value,
  6711. count = 0,
  6712. part;
  6713. while (expression.hasNext() && count < max) {
  6714. result = ValidationTypes.isAny(expression, types);
  6715. if (!result) {
  6716. break;
  6717. }
  6718. count++;
  6719. }
  6720. if (!result) {
  6721. if (expression.hasNext() && !expression.isFirst()) {
  6722. part = expression.peek();
  6723. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  6724. } else {
  6725. throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
  6726. }
  6727. } else if (expression.hasNext()) {
  6728. part = expression.next();
  6729. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  6730. }
  6731. },
  6732. multiProperty: function (types, expression, comma, max) {
  6733. var result = false,
  6734. value = expression.value,
  6735. count = 0,
  6736. sep = false,
  6737. part;
  6738. while(expression.hasNext() && !result && count < max) {
  6739. if (ValidationTypes.isAny(expression, types)) {
  6740. count++;
  6741. if (!expression.hasNext()) {
  6742. result = true;
  6743. } else if (comma) {
  6744. if (expression.peek() == ",") {
  6745. part = expression.next();
  6746. } else {
  6747. break;
  6748. }
  6749. }
  6750. } else {
  6751. break;
  6752. }
  6753. }
  6754. if (!result) {
  6755. if (expression.hasNext() && !expression.isFirst()) {
  6756. part = expression.peek();
  6757. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  6758. } else {
  6759. part = expression.previous();
  6760. if (comma && part == ",") {
  6761. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  6762. } else {
  6763. throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
  6764. }
  6765. }
  6766. } else if (expression.hasNext()) {
  6767. part = expression.next();
  6768. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  6769. }
  6770. },
  6771. groupProperty: function (types, expression, comma) {
  6772. var result = false,
  6773. value = expression.value,
  6774. typeCount = types.split("||").length,
  6775. groups = { count: 0 },
  6776. partial = false,
  6777. name,
  6778. part;
  6779. while(expression.hasNext() && !result) {
  6780. name = ValidationTypes.isAnyOfGroup(expression, types);
  6781. if (name) {
  6782. //no dupes
  6783. if (groups[name]) {
  6784. break;
  6785. } else {
  6786. groups[name] = 1;
  6787. groups.count++;
  6788. partial = true;
  6789. if (groups.count == typeCount || !expression.hasNext()) {
  6790. result = true;
  6791. }
  6792. }
  6793. } else {
  6794. break;
  6795. }
  6796. }
  6797. if (!result) {
  6798. if (partial && expression.hasNext()) {
  6799. part = expression.peek();
  6800. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  6801. } else {
  6802. throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
  6803. }
  6804. } else if (expression.hasNext()) {
  6805. part = expression.next();
  6806. throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
  6807. }
  6808. }
  6809. };
  6810. function ValidationError(message, line, col){
  6811. /**
  6812. * The column at which the error occurred.
  6813. * @type int
  6814. * @property col
  6815. */
  6816. this.col = col;
  6817. this.line = line;
  6818. this.message = message;
  6819. }
  6820. //inherit from Error
  6821. ValidationError.prototype = new Error();
  6822. //This file will likely change a lot! Very experimental!
  6823. /*global Properties, Validation, ValidationError, PropertyValueIterator, console*/
  6824. var ValidationTypes = {
  6825. isLiteral: function (part, literals) {
  6826. var text = part.text.toString().toLowerCase(),
  6827. args = literals.split(" | "),
  6828. i, len, found = false;
  6829. for (i=0,len=args.length; i < len && !found; i++){
  6830. if (text == args[i]){
  6831. found = true;
  6832. }
  6833. }
  6834. return found;
  6835. },
  6836. isSimple: function(type) {
  6837. return !!this.simple[type];
  6838. },
  6839. isComplex: function(type) {
  6840. return !!this.complex[type];
  6841. },
  6842. /**
  6843. * Determines if the next part(s) of the given expression
  6844. * are any of the given types.
  6845. */
  6846. isAny: function (expression, types) {
  6847. var args = types.split(" | "),
  6848. i, len, found = false;
  6849. for (i=0,len=args.length; i < len && !found && expression.hasNext(); i++){
  6850. found = this.isType(expression, args[i]);
  6851. }
  6852. return found;
  6853. },
  6854. /**
  6855. * Determines if the next part(s) of the given expresion
  6856. * are one of a group.
  6857. */
  6858. isAnyOfGroup: function(expression, types) {
  6859. var args = types.split(" || "),
  6860. i, len, found = false;
  6861. for (i=0,len=args.length; i < len && !found; i++){
  6862. found = this.isType(expression, args[i]);
  6863. }
  6864. return found ? args[i-1] : false;
  6865. },
  6866. /**
  6867. * Determines if the next part(s) of the given expression
  6868. * are of a given type.
  6869. */
  6870. isType: function (expression, type) {
  6871. var part = expression.peek(),
  6872. result = false;
  6873. if (type.charAt(0) != "<") {
  6874. result = this.isLiteral(part, type);
  6875. if (result) {
  6876. expression.next();
  6877. }
  6878. } else if (this.simple[type]) {
  6879. result = this.simple[type](part);
  6880. if (result) {
  6881. expression.next();
  6882. }
  6883. } else {
  6884. result = this.complex[type](expression);
  6885. }
  6886. return result;
  6887. },
  6888. simple: {
  6889. "<absolute-size>": function(part){
  6890. return ValidationTypes.isLiteral(part, "xx-small | x-small | small | medium | large | x-large | xx-large");
  6891. },
  6892. "<attachment>": function(part){
  6893. return ValidationTypes.isLiteral(part, "scroll | fixed | local");
  6894. },
  6895. "<attr>": function(part){
  6896. return part.type == "function" && part.name == "attr";
  6897. },
  6898. "<bg-image>": function(part){
  6899. return this["<image>"](part) || this["<gradient>"](part) || part == "none";
  6900. },
  6901. "<gradient>": function(part) {
  6902. return part.type == "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial|linear)\-gradient/i.test(part);
  6903. },
  6904. "<box>": function(part){
  6905. return ValidationTypes.isLiteral(part, "padding-box | border-box | content-box");
  6906. },
  6907. "<content>": function(part){
  6908. return part.type == "function" && part.name == "content";
  6909. },
  6910. "<relative-size>": function(part){
  6911. return ValidationTypes.isLiteral(part, "smaller | larger");
  6912. },
  6913. //any identifier
  6914. "<ident>": function(part){
  6915. return part.type == "identifier";
  6916. },
  6917. "<length>": function(part){
  6918. return part.type == "length" || part.type == "number" || part.type == "integer" || part == "0";
  6919. },
  6920. "<color>": function(part){
  6921. return part.type == "color" || part == "transparent";
  6922. },
  6923. "<number>": function(part){
  6924. return part.type == "number" || this["<integer>"](part);
  6925. },
  6926. "<integer>": function(part){
  6927. return part.type == "integer";
  6928. },
  6929. "<line>": function(part){
  6930. return part.type == "integer";
  6931. },
  6932. "<angle>": function(part){
  6933. return part.type == "angle";
  6934. },
  6935. "<uri>": function(part){
  6936. return part.type == "uri";
  6937. },
  6938. "<image>": function(part){
  6939. return this["<uri>"](part);
  6940. },
  6941. "<percentage>": function(part){
  6942. return part.type == "percentage" || part == "0";
  6943. },
  6944. "<border-width>": function(part){
  6945. return this["<length>"](part) || ValidationTypes.isLiteral(part, "thin | medium | thick");
  6946. },
  6947. "<border-style>": function(part){
  6948. return ValidationTypes.isLiteral(part, "none | hidden | dotted | dashed | solid | double | groove | ridge | inset | outset");
  6949. },
  6950. "<margin-width>": function(part){
  6951. return this["<length>"](part) || this["<percentage>"](part) || ValidationTypes.isLiteral(part, "auto");
  6952. },
  6953. "<padding-width>": function(part){
  6954. return this["<length>"](part) || this["<percentage>"](part);
  6955. },
  6956. "<shape>": function(part){
  6957. return part.type == "function" && (part.name == "rect" || part.name == "inset-rect");
  6958. },
  6959. "<time>": function(part) {
  6960. return part.type == "time";
  6961. }
  6962. },
  6963. complex: {
  6964. "<bg-position>": function(expression){
  6965. var types = this,
  6966. result = false,
  6967. numeric = "<percentage> | <length>",
  6968. xDir = "left | center | right",
  6969. yDir = "top | center | bottom",
  6970. part,
  6971. i, len;
  6972. if (ValidationTypes.isAny(expression, "top | bottom")) {
  6973. result = true;
  6974. } else {
  6975. //must be two-part
  6976. if (ValidationTypes.isAny(expression, numeric)){
  6977. if (expression.hasNext()){
  6978. result = ValidationTypes.isAny(expression, numeric + " | " + yDir);
  6979. }
  6980. } else if (ValidationTypes.isAny(expression, xDir)){
  6981. if (expression.hasNext()){
  6982. //two- or three-part
  6983. if (ValidationTypes.isAny(expression, yDir)){
  6984. result = true;
  6985. ValidationTypes.isAny(expression, numeric);
  6986. } else if (ValidationTypes.isAny(expression, numeric)){
  6987. //could also be two-part, so check the next part
  6988. if (ValidationTypes.isAny(expression, yDir)){
  6989. ValidationTypes.isAny(expression, numeric);
  6990. }
  6991. result = true;
  6992. }
  6993. }
  6994. }
  6995. }
  6996. return result;
  6997. },
  6998. "<bg-size>": function(expression){
  6999. //<bg-size> = [ <length> | <percentage> | auto ]{1,2} | cover | contain
  7000. var types = this,
  7001. result = false,
  7002. numeric = "<percentage> | <length> | auto",
  7003. part,
  7004. i, len;
  7005. if (ValidationTypes.isAny(expression, "cover | contain")) {
  7006. result = true;
  7007. } else if (ValidationTypes.isAny(expression, numeric)) {
  7008. result = true;
  7009. ValidationTypes.isAny(expression, numeric);
  7010. }
  7011. return result;
  7012. },
  7013. "<repeat-style>": function(expression){
  7014. //repeat-x | repeat-y | [repeat | space | round | no-repeat]{1,2}
  7015. var result = false,
  7016. values = "repeat | space | round | no-repeat",
  7017. part;
  7018. if (expression.hasNext()){
  7019. part = expression.next();
  7020. if (ValidationTypes.isLiteral(part, "repeat-x | repeat-y")) {
  7021. result = true;
  7022. } else if (ValidationTypes.isLiteral(part, values)) {
  7023. result = true;
  7024. if (expression.hasNext() && ValidationTypes.isLiteral(expression.peek(), values)) {
  7025. expression.next();
  7026. }
  7027. }
  7028. }
  7029. return result;
  7030. },
  7031. "<shadow>": function(expression) {
  7032. //inset? && [ <length>{2,4} && <color>? ]
  7033. var result = false,
  7034. count = 0,
  7035. inset = false,
  7036. color = false,
  7037. part;
  7038. if (expression.hasNext()) {
  7039. if (ValidationTypes.isAny(expression, "inset")){
  7040. inset = true;
  7041. }
  7042. if (ValidationTypes.isAny(expression, "<color>")) {
  7043. color = true;
  7044. }
  7045. while (ValidationTypes.isAny(expression, "<length>") && count < 4) {
  7046. count++;
  7047. }
  7048. if (expression.hasNext()) {
  7049. if (!color) {
  7050. ValidationTypes.isAny(expression, "<color>");
  7051. }
  7052. if (!inset) {
  7053. ValidationTypes.isAny(expression, "inset");
  7054. }
  7055. }
  7056. result = (count >= 2 && count <= 4);
  7057. }
  7058. return result;
  7059. },
  7060. "<x-one-radius>": function(expression) {
  7061. //[ <length> | <percentage> ] [ <length> | <percentage> ]?
  7062. var result = false,
  7063. count = 0,
  7064. numeric = "<length> | <percentage>",
  7065. part;
  7066. if (ValidationTypes.isAny(expression, numeric)){
  7067. result = true;
  7068. ValidationTypes.isAny(expression, numeric);
  7069. }
  7070. return result;
  7071. }
  7072. }
  7073. };
  7074. parserlib.css = {
  7075. Colors :Colors,
  7076. Combinator :Combinator,
  7077. Parser :Parser,
  7078. PropertyName :PropertyName,
  7079. PropertyValue :PropertyValue,
  7080. PropertyValuePart :PropertyValuePart,
  7081. MediaFeature :MediaFeature,
  7082. MediaQuery :MediaQuery,
  7083. Selector :Selector,
  7084. SelectorPart :SelectorPart,
  7085. SelectorSubPart :SelectorSubPart,
  7086. Specificity :Specificity,
  7087. TokenStream :TokenStream,
  7088. Tokens :Tokens,
  7089. ValidationError :ValidationError
  7090. };
  7091. })();
  7092. /*global parserlib, Reporter*/
  7093. var CSSLint = (function(){
  7094. var rules = [],
  7095. formatters = [],
  7096. api = new parserlib.util.EventTarget();
  7097. api.version = "0.9.7";
  7098. //-------------------------------------------------------------------------
  7099. // Rule Management
  7100. //-------------------------------------------------------------------------
  7101. /**
  7102. * Adds a new rule to the engine.
  7103. * @param {Object} rule The rule to add.
  7104. * @method addRule
  7105. */
  7106. api.addRule = function(rule){
  7107. rules.push(rule);
  7108. rules[rule.id] = rule;
  7109. };
  7110. api.clearRules = function(){
  7111. rules = [];
  7112. };
  7113. api.getRules = function(){
  7114. return [].concat(rules).sort(function(a,b){
  7115. return a.id > b.id ? 1 : 0;
  7116. });
  7117. };
  7118. //-------------------------------------------------------------------------
  7119. // Formatters
  7120. //-------------------------------------------------------------------------
  7121. /**
  7122. * Adds a new formatter to the engine.
  7123. * @param {Object} formatter The formatter to add.
  7124. * @method addFormatter
  7125. */
  7126. api.addFormatter = function(formatter) {
  7127. // formatters.push(formatter);
  7128. formatters[formatter.id] = formatter;
  7129. };
  7130. api.getFormatter = function(formatId){
  7131. return formatters[formatId];
  7132. };
  7133. api.format = function(results, filename, formatId, options) {
  7134. var formatter = this.getFormatter(formatId),
  7135. result = null;
  7136. if (formatter){
  7137. result = formatter.startFormat();
  7138. result += formatter.formatResults(results, filename, options || {});
  7139. result += formatter.endFormat();
  7140. }
  7141. return result;
  7142. };
  7143. api.hasFormat = function(formatId){
  7144. return formatters.hasOwnProperty(formatId);
  7145. };
  7146. //-------------------------------------------------------------------------
  7147. // Verification
  7148. //-------------------------------------------------------------------------
  7149. /**
  7150. * Starts the verification process for the given CSS text.
  7151. * @param {String} text The CSS text to verify.
  7152. * @param {Object} ruleset (Optional) List of rules to apply. If null, then
  7153. * all rules are used. If a rule has a value of 1 then it's a warning,
  7154. * a value of 2 means it's an error.
  7155. * @return {Object} Results of the verification.
  7156. * @method verify
  7157. */
  7158. api.verify = function(text, ruleset){
  7159. var i = 0,
  7160. len = rules.length,
  7161. reporter,
  7162. lines,
  7163. report,
  7164. parser = new parserlib.css.Parser({ starHack: true, ieFilters: true,
  7165. underscoreHack: true, strict: false });
  7166. lines = text.replace(/\n\r?/g, "$split$").split('$split$');
  7167. if (!ruleset){
  7168. ruleset = {};
  7169. while (i < len){
  7170. ruleset[rules[i++].id] = 1; //by default, everything is a warning
  7171. }
  7172. }
  7173. reporter = new Reporter(lines, ruleset);
  7174. ruleset.errors = 2; //always report parsing errors as errors
  7175. for (i in ruleset){
  7176. if(ruleset.hasOwnProperty(i)){
  7177. if (rules[i]){
  7178. rules[i].init(parser, reporter);
  7179. }
  7180. }
  7181. }
  7182. //capture most horrible error type
  7183. try {
  7184. parser.parse(text);
  7185. } catch (ex) {
  7186. reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
  7187. }
  7188. report = {
  7189. messages : reporter.messages,
  7190. stats : reporter.stats
  7191. };
  7192. //sort by line numbers, rollups at the bottom
  7193. report.messages.sort(function (a, b){
  7194. if (a.rollup && !b.rollup){
  7195. return 1;
  7196. } else if (!a.rollup && b.rollup){
  7197. return -1;
  7198. } else {
  7199. return a.line - b.line;
  7200. }
  7201. });
  7202. return report;
  7203. };
  7204. //-------------------------------------------------------------------------
  7205. // Publish the API
  7206. //-------------------------------------------------------------------------
  7207. return api;
  7208. })();
  7209. /**
  7210. * An instance of Report is used to report results of the
  7211. * verification back to the main API.
  7212. * @class Reporter
  7213. * @constructor
  7214. * @param {String[]} lines The text lines of the source.
  7215. * @param {Object} ruleset The set of rules to work with, including if
  7216. * they are errors or warnings.
  7217. */
  7218. function Reporter(lines, ruleset){
  7219. /**
  7220. * List of messages being reported.
  7221. * @property messages
  7222. * @type String[]
  7223. */
  7224. this.messages = [];
  7225. this.stats = [];
  7226. this.lines = lines;
  7227. this.ruleset = ruleset;
  7228. }
  7229. Reporter.prototype = {
  7230. //restore constructor
  7231. constructor: Reporter,
  7232. /**
  7233. * Report an error.
  7234. * @param {String} message The message to store.
  7235. * @param {int} line The line number.
  7236. * @param {int} col The column number.
  7237. * @param {Object} rule The rule this message relates to.
  7238. * @method error
  7239. */
  7240. error: function(message, line, col, rule){
  7241. this.messages.push({
  7242. type : "error",
  7243. line : line,
  7244. col : col,
  7245. message : message,
  7246. evidence: this.lines[line-1],
  7247. rule : rule || {}
  7248. });
  7249. },
  7250. /**
  7251. * Report an warning.
  7252. * @param {String} message The message to store.
  7253. * @param {int} line The line number.
  7254. * @param {int} col The column number.
  7255. * @param {Object} rule The rule this message relates to.
  7256. * @method warn
  7257. * @deprecated Use report instead.
  7258. */
  7259. warn: function(message, line, col, rule){
  7260. this.report(message, line, col, rule);
  7261. },
  7262. /**
  7263. * Report an issue.
  7264. * @param {String} message The message to store.
  7265. * @param {int} line The line number.
  7266. * @param {int} col The column number.
  7267. * @param {Object} rule The rule this message relates to.
  7268. * @method report
  7269. */
  7270. report: function(message, line, col, rule){
  7271. this.messages.push({
  7272. type : this.ruleset[rule.id] == 2 ? "error" : "warning",
  7273. line : line,
  7274. col : col,
  7275. message : message,
  7276. evidence: this.lines[line-1],
  7277. rule : rule
  7278. });
  7279. },
  7280. /**
  7281. * Report some informational text.
  7282. * @param {String} message The message to store.
  7283. * @param {int} line The line number.
  7284. * @param {int} col The column number.
  7285. * @param {Object} rule The rule this message relates to.
  7286. * @method info
  7287. */
  7288. info: function(message, line, col, rule){
  7289. this.messages.push({
  7290. type : "info",
  7291. line : line,
  7292. col : col,
  7293. message : message,
  7294. evidence: this.lines[line-1],
  7295. rule : rule
  7296. });
  7297. },
  7298. /**
  7299. * Report some rollup error information.
  7300. * @param {String} message The message to store.
  7301. * @param {Object} rule The rule this message relates to.
  7302. * @method rollupError
  7303. */
  7304. rollupError: function(message, rule){
  7305. this.messages.push({
  7306. type : "error",
  7307. rollup : true,
  7308. message : message,
  7309. rule : rule
  7310. });
  7311. },
  7312. /**
  7313. * Report some rollup warning information.
  7314. * @param {String} message The message to store.
  7315. * @param {Object} rule The rule this message relates to.
  7316. * @method rollupWarn
  7317. */
  7318. rollupWarn: function(message, rule){
  7319. this.messages.push({
  7320. type : "warning",
  7321. rollup : true,
  7322. message : message,
  7323. rule : rule
  7324. });
  7325. },
  7326. /**
  7327. * Report a statistic.
  7328. * @param {String} name The name of the stat to store.
  7329. * @param {Variant} value The value of the stat.
  7330. * @method stat
  7331. */
  7332. stat: function(name, value){
  7333. this.stats[name] = value;
  7334. }
  7335. };
  7336. //expose for testing purposes
  7337. CSSLint._Reporter = Reporter;
  7338. /*
  7339. * Utility functions that make life easier.
  7340. */
  7341. CSSLint.Util = {
  7342. /*
  7343. * Adds all properties from supplier onto receiver,
  7344. * overwriting if the same name already exists on
  7345. * reciever.
  7346. * @param {Object} The object to receive the properties.
  7347. * @param {Object} The object to provide the properties.
  7348. * @return {Object} The receiver
  7349. */
  7350. mix: function(receiver, supplier){
  7351. var prop;
  7352. for (prop in supplier){
  7353. if (supplier.hasOwnProperty(prop)){
  7354. receiver[prop] = supplier[prop];
  7355. }
  7356. }
  7357. return prop;
  7358. },
  7359. /*
  7360. * Polyfill for array indexOf() method.
  7361. * @param {Array} values The array to search.
  7362. * @param {Variant} value The value to search for.
  7363. * @return {int} The index of the value if found, -1 if not.
  7364. */
  7365. indexOf: function(values, value){
  7366. if (values.indexOf){
  7367. return values.indexOf(value);
  7368. } else {
  7369. for (var i=0, len=values.length; i < len; i++){
  7370. if (values[i] === value){
  7371. return i;
  7372. }
  7373. }
  7374. return -1;
  7375. }
  7376. },
  7377. /*
  7378. * Polyfill for array forEach() method.
  7379. * @param {Array} values The array to operate on.
  7380. * @param {Function} func The function to call on each item.
  7381. * @return {void}
  7382. */
  7383. forEach: function(values, func) {
  7384. if (values.forEach){
  7385. return values.forEach(func);
  7386. } else {
  7387. for (var i=0, len=values.length; i < len; i++){
  7388. func(values[i], i, values);
  7389. }
  7390. }
  7391. }
  7392. };
  7393. /*
  7394. * Rule: Don't use adjoining classes (.foo.bar).
  7395. */
  7396. CSSLint.addRule({
  7397. //rule information
  7398. id: "adjoining-classes",
  7399. name: "Disallow adjoining classes",
  7400. desc: "Don't use adjoining classes.",
  7401. browsers: "IE6",
  7402. //initialization
  7403. init: function(parser, reporter){
  7404. var rule = this;
  7405. parser.addListener("startrule", function(event){
  7406. var selectors = event.selectors,
  7407. selector,
  7408. part,
  7409. modifier,
  7410. classCount,
  7411. i, j, k;
  7412. for (i=0; i < selectors.length; i++){
  7413. selector = selectors[i];
  7414. for (j=0; j < selector.parts.length; j++){
  7415. part = selector.parts[j];
  7416. if (part.type == parser.SELECTOR_PART_TYPE){
  7417. classCount = 0;
  7418. for (k=0; k < part.modifiers.length; k++){
  7419. modifier = part.modifiers[k];
  7420. if (modifier.type == "class"){
  7421. classCount++;
  7422. }
  7423. if (classCount > 1){
  7424. reporter.report("Don't use adjoining classes.", part.line, part.col, rule);
  7425. }
  7426. }
  7427. }
  7428. }
  7429. }
  7430. });
  7431. }
  7432. });
  7433. /*
  7434. * Rule: Don't use width or height when using padding or border.
  7435. */
  7436. CSSLint.addRule({
  7437. //rule information
  7438. id: "box-model",
  7439. name: "Beware of broken box size",
  7440. desc: "Don't use width or height when using padding or border.",
  7441. browsers: "All",
  7442. //initialization
  7443. init: function(parser, reporter){
  7444. var rule = this,
  7445. widthProperties = {
  7446. border: 1,
  7447. "border-left": 1,
  7448. "border-right": 1,
  7449. padding: 1,
  7450. "padding-left": 1,
  7451. "padding-right": 1
  7452. },
  7453. heightProperties = {
  7454. border: 1,
  7455. "border-bottom": 1,
  7456. "border-top": 1,
  7457. padding: 1,
  7458. "padding-bottom": 1,
  7459. "padding-top": 1
  7460. },
  7461. properties;
  7462. function startRule(){
  7463. properties = {};
  7464. }
  7465. function endRule(){
  7466. var prop;
  7467. if (properties.height){
  7468. for (prop in heightProperties){
  7469. if (heightProperties.hasOwnProperty(prop) && properties[prop]){
  7470. //special case for padding
  7471. if (!(prop == "padding" && properties[prop].value.parts.length === 2 && properties[prop].value.parts[0].value === 0)){
  7472. reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
  7473. }
  7474. }
  7475. }
  7476. }
  7477. if (properties.width){
  7478. for (prop in widthProperties){
  7479. if (widthProperties.hasOwnProperty(prop) && properties[prop]){
  7480. if (!(prop == "padding" && properties[prop].value.parts.length === 2 && properties[prop].value.parts[1].value === 0)){
  7481. reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
  7482. }
  7483. }
  7484. }
  7485. }
  7486. }
  7487. parser.addListener("startrule", startRule);
  7488. parser.addListener("startfontface", startRule);
  7489. parser.addListener("startpage", startRule);
  7490. parser.addListener("startpagemargin", startRule);
  7491. parser.addListener("startkeyframerule", startRule);
  7492. parser.addListener("property", function(event){
  7493. var name = event.property.text.toLowerCase();
  7494. if (heightProperties[name] || widthProperties[name]){
  7495. if (!/^0\S*$/.test(event.value) && !(name == "border" && event.value == "none")){
  7496. properties[name] = { line: event.property.line, col: event.property.col, value: event.value };
  7497. }
  7498. } else {
  7499. if (name == "width" || name == "height"){
  7500. properties[name] = 1;
  7501. }
  7502. }
  7503. });
  7504. parser.addListener("endrule", endRule);
  7505. parser.addListener("endfontface", endRule);
  7506. parser.addListener("endpage", endRule);
  7507. parser.addListener("endpagemargin", endRule);
  7508. parser.addListener("endkeyframerule", endRule);
  7509. }
  7510. });
  7511. /*
  7512. * Rule: box-sizing doesn't work in IE6 and IE7.
  7513. */
  7514. CSSLint.addRule({
  7515. //rule information
  7516. id: "box-sizing",
  7517. name: "Disallow use of box-sizing",
  7518. desc: "The box-sizing properties isn't supported in IE6 and IE7.",
  7519. browsers: "IE6, IE7",
  7520. tags: ["Compatibility"],
  7521. //initialization
  7522. init: function(parser, reporter){
  7523. var rule = this;
  7524. parser.addListener("property", function(event){
  7525. var name = event.property.text.toLowerCase();
  7526. if (name == "box-sizing"){
  7527. reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
  7528. }
  7529. });
  7530. }
  7531. });
  7532. /*global CSSLint*/
  7533. CSSLint.addRule({
  7534. //rule information
  7535. id: "compatible-vendor-prefixes",
  7536. name: "Require compatible vendor prefixes",
  7537. desc: "Include all compatible vendor prefixes to reach a wider range of users.",
  7538. browsers: "All",
  7539. //initialization
  7540. init: function (parser, reporter) {
  7541. var rule = this,
  7542. compatiblePrefixes,
  7543. properties,
  7544. prop,
  7545. variations,
  7546. prefixed,
  7547. i,
  7548. len,
  7549. arrayPush = Array.prototype.push,
  7550. applyTo = [];
  7551. // See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
  7552. compatiblePrefixes = {
  7553. "animation" : "webkit moz ms",
  7554. "animation-delay" : "webkit moz ms",
  7555. "animation-direction" : "webkit moz ms",
  7556. "animation-duration" : "webkit moz ms",
  7557. "animation-fill-mode" : "webkit moz ms",
  7558. "animation-iteration-count" : "webkit moz ms",
  7559. "animation-name" : "webkit moz ms",
  7560. "animation-play-state" : "webkit moz ms",
  7561. "animation-timing-function" : "webkit moz ms",
  7562. "appearance" : "webkit moz",
  7563. "border-end" : "webkit moz",
  7564. "border-end-color" : "webkit moz",
  7565. "border-end-style" : "webkit moz",
  7566. "border-end-width" : "webkit moz",
  7567. "border-image" : "webkit moz o",
  7568. "border-radius" : "webkit moz",
  7569. "border-start" : "webkit moz",
  7570. "border-start-color" : "webkit moz",
  7571. "border-start-style" : "webkit moz",
  7572. "border-start-width" : "webkit moz",
  7573. "box-align" : "webkit moz ms",
  7574. "box-direction" : "webkit moz ms",
  7575. "box-flex" : "webkit moz ms",
  7576. "box-lines" : "webkit ms",
  7577. "box-ordinal-group" : "webkit moz ms",
  7578. "box-orient" : "webkit moz ms",
  7579. "box-pack" : "webkit moz ms",
  7580. "box-sizing" : "webkit moz",
  7581. "box-shadow" : "webkit moz",
  7582. "column-count" : "webkit moz ms",
  7583. "column-gap" : "webkit moz ms",
  7584. "column-rule" : "webkit moz ms",
  7585. "column-rule-color" : "webkit moz ms",
  7586. "column-rule-style" : "webkit moz ms",
  7587. "column-rule-width" : "webkit moz ms",
  7588. "column-width" : "webkit moz ms",
  7589. "hyphens" : "epub moz",
  7590. "line-break" : "webkit ms",
  7591. "margin-end" : "webkit moz",
  7592. "margin-start" : "webkit moz",
  7593. "marquee-speed" : "webkit wap",
  7594. "marquee-style" : "webkit wap",
  7595. "padding-end" : "webkit moz",
  7596. "padding-start" : "webkit moz",
  7597. "tab-size" : "moz o",
  7598. "text-size-adjust" : "webkit ms",
  7599. "transform" : "webkit moz ms o",
  7600. "transform-origin" : "webkit moz ms o",
  7601. "transition" : "webkit moz o ms",
  7602. "transition-delay" : "webkit moz o ms",
  7603. "transition-duration" : "webkit moz o ms",
  7604. "transition-property" : "webkit moz o ms",
  7605. "transition-timing-function" : "webkit moz o ms",
  7606. "user-modify" : "webkit moz",
  7607. "user-select" : "webkit moz ms",
  7608. "word-break" : "epub ms",
  7609. "writing-mode" : "epub ms"
  7610. };
  7611. for (prop in compatiblePrefixes) {
  7612. if (compatiblePrefixes.hasOwnProperty(prop)) {
  7613. variations = [];
  7614. prefixed = compatiblePrefixes[prop].split(' ');
  7615. for (i = 0, len = prefixed.length; i < len; i++) {
  7616. variations.push('-' + prefixed[i] + '-' + prop);
  7617. }
  7618. compatiblePrefixes[prop] = variations;
  7619. arrayPush.apply(applyTo, variations);
  7620. }
  7621. }
  7622. parser.addListener("startrule", function () {
  7623. properties = [];
  7624. });
  7625. parser.addListener("property", function (event) {
  7626. var name = event.property;
  7627. if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
  7628. properties.push(name);
  7629. }
  7630. });
  7631. parser.addListener("endrule", function (event) {
  7632. if (!properties.length) {
  7633. return;
  7634. }
  7635. var propertyGroups = {},
  7636. i,
  7637. len,
  7638. name,
  7639. prop,
  7640. variations,
  7641. value,
  7642. full,
  7643. actual,
  7644. item,
  7645. propertiesSpecified;
  7646. for (i = 0, len = properties.length; i < len; i++) {
  7647. name = properties[i];
  7648. for (prop in compatiblePrefixes) {
  7649. if (compatiblePrefixes.hasOwnProperty(prop)) {
  7650. variations = compatiblePrefixes[prop];
  7651. if (CSSLint.Util.indexOf(variations, name.text) > -1) {
  7652. if (!propertyGroups[prop]) {
  7653. propertyGroups[prop] = {
  7654. full : variations.slice(0),
  7655. actual : [],
  7656. actualNodes: []
  7657. };
  7658. }
  7659. if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
  7660. propertyGroups[prop].actual.push(name.text);
  7661. propertyGroups[prop].actualNodes.push(name);
  7662. }
  7663. }
  7664. }
  7665. }
  7666. }
  7667. for (prop in propertyGroups) {
  7668. if (propertyGroups.hasOwnProperty(prop)) {
  7669. value = propertyGroups[prop];
  7670. full = value.full;
  7671. actual = value.actual;
  7672. if (full.length > actual.length) {
  7673. for (i = 0, len = full.length; i < len; i++) {
  7674. item = full[i];
  7675. if (CSSLint.Util.indexOf(actual, item) === -1) {
  7676. propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length == 2) ? actual.join(" and ") : actual.join(", ");
  7677. reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
  7678. }
  7679. }
  7680. }
  7681. }
  7682. }
  7683. });
  7684. }
  7685. });
  7686. /*global CSSLint*/
  7687. CSSLint.addRule({
  7688. //rule information
  7689. id: "display-property-grouping",
  7690. name: "Require properties appropriate for display",
  7691. desc: "Certain properties shouldn't be used with certain display property values.",
  7692. browsers: "All",
  7693. //initialization
  7694. init: function(parser, reporter){
  7695. var rule = this;
  7696. var propertiesToCheck = {
  7697. display: 1,
  7698. "float": "none",
  7699. height: 1,
  7700. width: 1,
  7701. margin: 1,
  7702. "margin-left": 1,
  7703. "margin-right": 1,
  7704. "margin-bottom": 1,
  7705. "margin-top": 1,
  7706. padding: 1,
  7707. "padding-left": 1,
  7708. "padding-right": 1,
  7709. "padding-bottom": 1,
  7710. "padding-top": 1,
  7711. "vertical-align": 1
  7712. },
  7713. properties;
  7714. function reportProperty(name, display, msg){
  7715. if (properties[name]){
  7716. if (typeof propertiesToCheck[name] != "string" || properties[name].value.toLowerCase() != propertiesToCheck[name]){
  7717. reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
  7718. }
  7719. }
  7720. }
  7721. function startRule(){
  7722. properties = {};
  7723. }
  7724. function endRule(){
  7725. var display = properties.display ? properties.display.value : null;
  7726. if (display){
  7727. switch(display){
  7728. case "inline":
  7729. //height, width, margin-top, margin-bottom, float should not be used with inline
  7730. reportProperty("height", display);
  7731. reportProperty("width", display);
  7732. reportProperty("margin", display);
  7733. reportProperty("margin-top", display);
  7734. reportProperty("margin-bottom", display);
  7735. reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
  7736. break;
  7737. case "block":
  7738. //vertical-align should not be used with block
  7739. reportProperty("vertical-align", display);
  7740. break;
  7741. case "inline-block":
  7742. //float should not be used with inline-block
  7743. reportProperty("float", display);
  7744. break;
  7745. default:
  7746. //margin, float should not be used with table
  7747. if (display.indexOf("table-") === 0){
  7748. reportProperty("margin", display);
  7749. reportProperty("margin-left", display);
  7750. reportProperty("margin-right", display);
  7751. reportProperty("margin-top", display);
  7752. reportProperty("margin-bottom", display);
  7753. reportProperty("float", display);
  7754. }
  7755. //otherwise do nothing
  7756. }
  7757. }
  7758. }
  7759. parser.addListener("startrule", startRule);
  7760. parser.addListener("startfontface", startRule);
  7761. parser.addListener("startkeyframerule", startRule);
  7762. parser.addListener("startpagemargin", startRule);
  7763. parser.addListener("startpage", startRule);
  7764. parser.addListener("property", function(event){
  7765. var name = event.property.text.toLowerCase();
  7766. if (propertiesToCheck[name]){
  7767. properties[name] = { value: event.value.text, line: event.property.line, col: event.property.col };
  7768. }
  7769. });
  7770. parser.addListener("endrule", endRule);
  7771. parser.addListener("endfontface", endRule);
  7772. parser.addListener("endkeyframerule", endRule);
  7773. parser.addListener("endpagemargin", endRule);
  7774. parser.addListener("endpage", endRule);
  7775. }
  7776. });
  7777. /*global CSSLint*/
  7778. CSSLint.addRule({
  7779. //rule information
  7780. id: "duplicate-background-images",
  7781. name: "Disallow duplicate background images",
  7782. desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
  7783. browsers: "All",
  7784. //initialization
  7785. init: function(parser, reporter){
  7786. var rule = this,
  7787. stack = {};
  7788. parser.addListener("property", function(event){
  7789. var name = event.property.text,
  7790. value = event.value,
  7791. i, len;
  7792. if (name.match(/background/i)) {
  7793. for (i=0, len=value.parts.length; i < len; i++) {
  7794. if (value.parts[i].type == 'uri') {
  7795. if (typeof stack[value.parts[i].uri] === 'undefined') {
  7796. stack[value.parts[i].uri] = event;
  7797. }
  7798. else {
  7799. 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);
  7800. }
  7801. }
  7802. }
  7803. }
  7804. });
  7805. }
  7806. });
  7807. /*global CSSLint*/
  7808. CSSLint.addRule({
  7809. //rule information
  7810. id: "duplicate-properties",
  7811. name: "Disallow duplicate properties",
  7812. desc: "Duplicate properties must appear one after the other.",
  7813. browsers: "All",
  7814. //initialization
  7815. init: function(parser, reporter){
  7816. var rule = this,
  7817. properties,
  7818. lastProperty;
  7819. function startRule(event){
  7820. properties = {};
  7821. }
  7822. parser.addListener("startrule", startRule);
  7823. parser.addListener("startfontface", startRule);
  7824. parser.addListener("startpage", startRule);
  7825. parser.addListener("startpagemargin", startRule);
  7826. parser.addListener("startkeyframerule", startRule);
  7827. parser.addListener("property", function(event){
  7828. var property = event.property,
  7829. name = property.text.toLowerCase();
  7830. if (properties[name] && (lastProperty != name || properties[name] == event.value.text)){
  7831. reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
  7832. }
  7833. properties[name] = event.value.text;
  7834. lastProperty = name;
  7835. });
  7836. }
  7837. });
  7838. /*global CSSLint*/
  7839. CSSLint.addRule({
  7840. //rule information
  7841. id: "empty-rules",
  7842. name: "Disallow empty rules",
  7843. desc: "Rules without any properties specified should be removed.",
  7844. browsers: "All",
  7845. //initialization
  7846. init: function(parser, reporter){
  7847. var rule = this,
  7848. count = 0;
  7849. parser.addListener("startrule", function(){
  7850. count=0;
  7851. });
  7852. parser.addListener("property", function(){
  7853. count++;
  7854. });
  7855. parser.addListener("endrule", function(event){
  7856. var selectors = event.selectors;
  7857. if (count === 0){
  7858. reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
  7859. }
  7860. });
  7861. }
  7862. });
  7863. /*global CSSLint*/
  7864. CSSLint.addRule({
  7865. //rule information
  7866. id: "errors",
  7867. name: "Parsing Errors",
  7868. desc: "This rule looks for recoverable syntax errors.",
  7869. browsers: "All",
  7870. //initialization
  7871. init: function(parser, reporter){
  7872. var rule = this;
  7873. parser.addListener("error", function(event){
  7874. reporter.error(event.message, event.line, event.col, rule);
  7875. });
  7876. }
  7877. });
  7878. CSSLint.addRule({
  7879. //rule information
  7880. id: "fallback-colors",
  7881. name: "Require fallback colors",
  7882. desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
  7883. browsers: "IE6,IE7,IE8",
  7884. //initialization
  7885. init: function(parser, reporter){
  7886. var rule = this,
  7887. lastProperty,
  7888. propertiesToCheck = {
  7889. color: 1,
  7890. background: 1,
  7891. "background-color": 1
  7892. },
  7893. properties;
  7894. function startRule(event){
  7895. properties = {};
  7896. lastProperty = null;
  7897. }
  7898. parser.addListener("startrule", startRule);
  7899. parser.addListener("startfontface", startRule);
  7900. parser.addListener("startpage", startRule);
  7901. parser.addListener("startpagemargin", startRule);
  7902. parser.addListener("startkeyframerule", startRule);
  7903. parser.addListener("property", function(event){
  7904. var property = event.property,
  7905. name = property.text.toLowerCase(),
  7906. parts = event.value.parts,
  7907. i = 0,
  7908. colorType = "",
  7909. len = parts.length;
  7910. if(propertiesToCheck[name]){
  7911. while(i < len){
  7912. if (parts[i].type == "color"){
  7913. if ("alpha" in parts[i] || "hue" in parts[i]){
  7914. if (/([^\)]+)\(/.test(parts[i])){
  7915. colorType = RegExp.$1.toUpperCase();
  7916. }
  7917. if (!lastProperty || (lastProperty.property.text.toLowerCase() != name || lastProperty.colorType != "compat")){
  7918. reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
  7919. }
  7920. } else {
  7921. event.colorType = "compat";
  7922. }
  7923. }
  7924. i++;
  7925. }
  7926. }
  7927. lastProperty = event;
  7928. });
  7929. }
  7930. });
  7931. /*global CSSLint*/
  7932. CSSLint.addRule({
  7933. //rule information
  7934. id: "floats",
  7935. name: "Disallow too many floats",
  7936. desc: "This rule tests if the float property is used too many times",
  7937. browsers: "All",
  7938. //initialization
  7939. init: function(parser, reporter){
  7940. var rule = this;
  7941. var count = 0;
  7942. //count how many times "float" is used
  7943. parser.addListener("property", function(event){
  7944. if (event.property.text.toLowerCase() == "float" &&
  7945. event.value.text.toLowerCase() != "none"){
  7946. count++;
  7947. }
  7948. });
  7949. //report the results
  7950. parser.addListener("endstylesheet", function(){
  7951. reporter.stat("floats", count);
  7952. if (count >= 10){
  7953. reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
  7954. }
  7955. });
  7956. }
  7957. });
  7958. /*global CSSLint*/
  7959. CSSLint.addRule({
  7960. //rule information
  7961. id: "font-faces",
  7962. name: "Don't use too many web fonts",
  7963. desc: "Too many different web fonts in the same stylesheet.",
  7964. browsers: "All",
  7965. //initialization
  7966. init: function(parser, reporter){
  7967. var rule = this,
  7968. count = 0;
  7969. parser.addListener("startfontface", function(){
  7970. count++;
  7971. });
  7972. parser.addListener("endstylesheet", function(){
  7973. if (count > 5){
  7974. reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
  7975. }
  7976. });
  7977. }
  7978. });
  7979. /*global CSSLint*/
  7980. CSSLint.addRule({
  7981. //rule information
  7982. id: "font-sizes",
  7983. name: "Disallow too many font sizes",
  7984. desc: "Checks the number of font-size declarations.",
  7985. browsers: "All",
  7986. //initialization
  7987. init: function(parser, reporter){
  7988. var rule = this,
  7989. count = 0;
  7990. //check for use of "font-size"
  7991. parser.addListener("property", function(event){
  7992. if (event.property == "font-size"){
  7993. count++;
  7994. }
  7995. });
  7996. //report the results
  7997. parser.addListener("endstylesheet", function(){
  7998. reporter.stat("font-sizes", count);
  7999. if (count >= 10){
  8000. reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
  8001. }
  8002. });
  8003. }
  8004. });
  8005. /*global CSSLint*/
  8006. CSSLint.addRule({
  8007. //rule information
  8008. id: "gradients",
  8009. name: "Require all gradient definitions",
  8010. desc: "When using a vendor-prefixed gradient, make sure to use them all.",
  8011. browsers: "All",
  8012. //initialization
  8013. init: function(parser, reporter){
  8014. var rule = this,
  8015. gradients;
  8016. parser.addListener("startrule", function(){
  8017. gradients = {
  8018. moz: 0,
  8019. webkit: 0,
  8020. oldWebkit: 0,
  8021. ms: 0,
  8022. o: 0
  8023. };
  8024. });
  8025. parser.addListener("property", function(event){
  8026. if (/\-(moz|ms|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)){
  8027. gradients[RegExp.$1] = 1;
  8028. } else if (/\-webkit\-gradient/i.test(event.value)){
  8029. gradients.oldWebkit = 1;
  8030. }
  8031. });
  8032. parser.addListener("endrule", function(event){
  8033. var missing = [];
  8034. if (!gradients.moz){
  8035. missing.push("Firefox 3.6+");
  8036. }
  8037. if (!gradients.webkit){
  8038. missing.push("Webkit (Safari 5+, Chrome)");
  8039. }
  8040. if (!gradients.oldWebkit){
  8041. missing.push("Old Webkit (Safari 4+, Chrome)");
  8042. }
  8043. if (!gradients.ms){
  8044. missing.push("Internet Explorer 10+");
  8045. }
  8046. if (!gradients.o){
  8047. missing.push("Opera 11.1+");
  8048. }
  8049. if (missing.length && missing.length < 5){
  8050. reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
  8051. }
  8052. });
  8053. }
  8054. });
  8055. /*global CSSLint*/
  8056. CSSLint.addRule({
  8057. //rule information
  8058. id: "ids",
  8059. name: "Disallow IDs in selectors",
  8060. desc: "Selectors should not contain IDs.",
  8061. browsers: "All",
  8062. //initialization
  8063. init: function(parser, reporter){
  8064. var rule = this;
  8065. parser.addListener("startrule", function(event){
  8066. var selectors = event.selectors,
  8067. selector,
  8068. part,
  8069. modifier,
  8070. idCount,
  8071. i, j, k;
  8072. for (i=0; i < selectors.length; i++){
  8073. selector = selectors[i];
  8074. idCount = 0;
  8075. for (j=0; j < selector.parts.length; j++){
  8076. part = selector.parts[j];
  8077. if (part.type == parser.SELECTOR_PART_TYPE){
  8078. for (k=0; k < part.modifiers.length; k++){
  8079. modifier = part.modifiers[k];
  8080. if (modifier.type == "id"){
  8081. idCount++;
  8082. }
  8083. }
  8084. }
  8085. }
  8086. if (idCount == 1){
  8087. reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
  8088. } else if (idCount > 1){
  8089. reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
  8090. }
  8091. }
  8092. });
  8093. }
  8094. });
  8095. /*global CSSLint*/
  8096. CSSLint.addRule({
  8097. //rule information
  8098. id: "import",
  8099. name: "Disallow @import",
  8100. desc: "Don't use @import, use <link> instead.",
  8101. browsers: "All",
  8102. //initialization
  8103. init: function(parser, reporter){
  8104. var rule = this;
  8105. parser.addListener("import", function(event){
  8106. reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
  8107. });
  8108. }
  8109. });
  8110. /*global CSSLint*/
  8111. CSSLint.addRule({
  8112. //rule information
  8113. id: "important",
  8114. name: "Disallow !important",
  8115. desc: "Be careful when using !important declaration",
  8116. browsers: "All",
  8117. //initialization
  8118. init: function(parser, reporter){
  8119. var rule = this,
  8120. count = 0;
  8121. //warn that important is used and increment the declaration counter
  8122. parser.addListener("property", function(event){
  8123. if (event.important === true){
  8124. count++;
  8125. reporter.report("Use of !important", event.line, event.col, rule);
  8126. }
  8127. });
  8128. //if there are more than 10, show an error
  8129. parser.addListener("endstylesheet", function(){
  8130. reporter.stat("important", count);
  8131. if (count >= 10){
  8132. reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specifity issues.", rule);
  8133. }
  8134. });
  8135. }
  8136. });
  8137. /*global CSSLint*/
  8138. CSSLint.addRule({
  8139. //rule information
  8140. id: "known-properties",
  8141. name: "Require use of known properties",
  8142. desc: "Properties should be known (listed in CSS specification) or be a vendor-prefixed property.",
  8143. browsers: "All",
  8144. //initialization
  8145. init: function(parser, reporter){
  8146. var rule = this,
  8147. properties = {
  8148. "alignment-adjust": 1,
  8149. "alignment-baseline": 1,
  8150. "animation": 1,
  8151. "animation-delay": 1,
  8152. "animation-direction": 1,
  8153. "animation-duration": 1,
  8154. "animation-fill-mode": 1,
  8155. "animation-iteration-count": 1,
  8156. "animation-name": 1,
  8157. "animation-play-state": 1,
  8158. "animation-timing-function": 1,
  8159. "appearance": 1,
  8160. "azimuth": 1,
  8161. "backface-visibility": 1,
  8162. "background": 1,
  8163. "background-attachment": 1,
  8164. "background-break": 1,
  8165. "background-clip": 1,
  8166. "background-color": 1,
  8167. "background-image": 1,
  8168. "background-origin": 1,
  8169. "background-position": 1,
  8170. "background-repeat": 1,
  8171. "background-size": 1,
  8172. "baseline-shift": 1,
  8173. "binding": 1,
  8174. "bleed": 1,
  8175. "bookmark-label": 1,
  8176. "bookmark-level": 1,
  8177. "bookmark-state": 1,
  8178. "bookmark-target": 1,
  8179. "border": 1,
  8180. "border-bottom": 1,
  8181. "border-bottom-color": 1,
  8182. "border-bottom-left-radius": 1,
  8183. "border-bottom-right-radius": 1,
  8184. "border-bottom-style": 1,
  8185. "border-bottom-width": 1,
  8186. "border-collapse": 1,
  8187. "border-color": 1,
  8188. "border-image": 1,
  8189. "border-image-outset": 1,
  8190. "border-image-repeat": 1,
  8191. "border-image-slice": 1,
  8192. "border-image-source": 1,
  8193. "border-image-width": 1,
  8194. "border-left": 1,
  8195. "border-left-color": 1,
  8196. "border-left-style": 1,
  8197. "border-left-width": 1,
  8198. "border-radius": 1,
  8199. "border-right": 1,
  8200. "border-right-color": 1,
  8201. "border-right-style": 1,
  8202. "border-right-width": 1,
  8203. "border-spacing": 1,
  8204. "border-style": 1,
  8205. "border-top": 1,
  8206. "border-top-color": 1,
  8207. "border-top-left-radius": 1,
  8208. "border-top-right-radius": 1,
  8209. "border-top-style": 1,
  8210. "border-top-width": 1,
  8211. "border-width": 1,
  8212. "bottom": 1,
  8213. "box-align": 1,
  8214. "box-decoration-break": 1,
  8215. "box-direction": 1,
  8216. "box-flex": 1,
  8217. "box-flex-group": 1,
  8218. "box-lines": 1,
  8219. "box-ordinal-group": 1,
  8220. "box-orient": 1,
  8221. "box-pack": 1,
  8222. "box-shadow": 1,
  8223. "box-sizing": 1,
  8224. "break-after": 1,
  8225. "break-before": 1,
  8226. "break-inside": 1,
  8227. "caption-side": 1,
  8228. "clear": 1,
  8229. "clip": 1,
  8230. "color": 1,
  8231. "color-profile": 1,
  8232. "column-count": 1,
  8233. "column-fill": 1,
  8234. "column-gap": 1,
  8235. "column-rule": 1,
  8236. "column-rule-color": 1,
  8237. "column-rule-style": 1,
  8238. "column-rule-width": 1,
  8239. "column-span": 1,
  8240. "column-width": 1,
  8241. "columns": 1,
  8242. "content": 1,
  8243. "counter-increment": 1,
  8244. "counter-reset": 1,
  8245. "crop": 1,
  8246. "cue": 1,
  8247. "cue-after": 1,
  8248. "cue-before": 1,
  8249. "cursor": 1,
  8250. "direction": 1,
  8251. "display": 1,
  8252. "dominant-baseline": 1,
  8253. "drop-initial-after-adjust": 1,
  8254. "drop-initial-after-align": 1,
  8255. "drop-initial-before-adjust": 1,
  8256. "drop-initial-before-align": 1,
  8257. "drop-initial-size": 1,
  8258. "drop-initial-value": 1,
  8259. "elevation": 1,
  8260. "empty-cells": 1,
  8261. "fit": 1,
  8262. "fit-position": 1,
  8263. "float": 1,
  8264. "float-offset": 1,
  8265. "font": 1,
  8266. "font-family": 1,
  8267. "font-size": 1,
  8268. "font-size-adjust": 1,
  8269. "font-stretch": 1,
  8270. "font-style": 1,
  8271. "font-variant": 1,
  8272. "font-weight": 1,
  8273. "grid-columns": 1,
  8274. "grid-rows": 1,
  8275. "hanging-punctuation": 1,
  8276. "height": 1,
  8277. "hyphenate-after": 1,
  8278. "hyphenate-before": 1,
  8279. "hyphenate-character": 1,
  8280. "hyphenate-lines": 1,
  8281. "hyphenate-resource": 1,
  8282. "hyphens": 1,
  8283. "icon": 1,
  8284. "image-orientation": 1,
  8285. "image-rendering": 1,
  8286. "image-resolution": 1,
  8287. "inline-box-align": 1,
  8288. "left": 1,
  8289. "letter-spacing": 1,
  8290. "line-height": 1,
  8291. "line-stacking": 1,
  8292. "line-stacking-ruby": 1,
  8293. "line-stacking-shift": 1,
  8294. "line-stacking-strategy": 1,
  8295. "list-style": 1,
  8296. "list-style-image": 1,
  8297. "list-style-position": 1,
  8298. "list-style-type": 1,
  8299. "margin": 1,
  8300. "margin-bottom": 1,
  8301. "margin-left": 1,
  8302. "margin-right": 1,
  8303. "margin-top": 1,
  8304. "mark": 1,
  8305. "mark-after": 1,
  8306. "mark-before": 1,
  8307. "marks": 1,
  8308. "marquee-direction": 1,
  8309. "marquee-play-count": 1,
  8310. "marquee-speed": 1,
  8311. "marquee-style": 1,
  8312. "max-height": 1,
  8313. "max-width": 1,
  8314. "min-height": 1,
  8315. "min-width": 1,
  8316. "move-to": 1,
  8317. "nav-down": 1,
  8318. "nav-index": 1,
  8319. "nav-left": 1,
  8320. "nav-right": 1,
  8321. "nav-up": 1,
  8322. "opacity": 1,
  8323. "orphans": 1,
  8324. "outline": 1,
  8325. "outline-color": 1,
  8326. "outline-offset": 1,
  8327. "outline-style": 1,
  8328. "outline-width": 1,
  8329. "overflow": 1,
  8330. "overflow-style": 1,
  8331. "overflow-x": 1,
  8332. "overflow-y": 1,
  8333. "padding": 1,
  8334. "padding-bottom": 1,
  8335. "padding-left": 1,
  8336. "padding-right": 1,
  8337. "padding-top": 1,
  8338. "page": 1,
  8339. "page-break-after": 1,
  8340. "page-break-before": 1,
  8341. "page-break-inside": 1,
  8342. "page-policy": 1,
  8343. "pause": 1,
  8344. "pause-after": 1,
  8345. "pause-before": 1,
  8346. "perspective": 1,
  8347. "perspective-origin": 1,
  8348. "phonemes": 1,
  8349. "pitch": 1,
  8350. "pitch-range": 1,
  8351. "play-during": 1,
  8352. "position": 1,
  8353. "presentation-level": 1,
  8354. "punctuation-trim": 1,
  8355. "quotes": 1,
  8356. "rendering-intent": 1,
  8357. "resize": 1,
  8358. "rest": 1,
  8359. "rest-after": 1,
  8360. "rest-before": 1,
  8361. "richness": 1,
  8362. "right": 1,
  8363. "rotation": 1,
  8364. "rotation-point": 1,
  8365. "ruby-align": 1,
  8366. "ruby-overhang": 1,
  8367. "ruby-position": 1,
  8368. "ruby-span": 1,
  8369. "size": 1,
  8370. "speak": 1,
  8371. "speak-header": 1,
  8372. "speak-numeral": 1,
  8373. "speak-punctuation": 1,
  8374. "speech-rate": 1,
  8375. "stress": 1,
  8376. "string-set": 1,
  8377. "table-layout": 1,
  8378. "target": 1,
  8379. "target-name": 1,
  8380. "target-new": 1,
  8381. "target-position": 1,
  8382. "text-align": 1,
  8383. "text-align-last": 1,
  8384. "text-decoration": 1,
  8385. "text-emphasis": 1,
  8386. "text-height": 1,
  8387. "text-indent": 1,
  8388. "text-justify": 1,
  8389. "text-outline": 1,
  8390. "text-shadow": 1,
  8391. "text-transform": 1,
  8392. "text-wrap": 1,
  8393. "top": 1,
  8394. "transform": 1,
  8395. "transform-origin": 1,
  8396. "transform-style": 1,
  8397. "transition": 1,
  8398. "transition-delay": 1,
  8399. "transition-duration": 1,
  8400. "transition-property": 1,
  8401. "transition-timing-function": 1,
  8402. "unicode-bidi": 1,
  8403. "user-modify": 1,
  8404. "user-select": 1,
  8405. "vertical-align": 1,
  8406. "visibility": 1,
  8407. "voice-balance": 1,
  8408. "voice-duration": 1,
  8409. "voice-family": 1,
  8410. "voice-pitch": 1,
  8411. "voice-pitch-range": 1,
  8412. "voice-rate": 1,
  8413. "voice-stress": 1,
  8414. "voice-volume": 1,
  8415. "volume": 1,
  8416. "white-space": 1,
  8417. "white-space-collapse": 1,
  8418. "widows": 1,
  8419. "width": 1,
  8420. "word-break": 1,
  8421. "word-spacing": 1,
  8422. "word-wrap": 1,
  8423. "z-index": 1,
  8424. //IE
  8425. "filter": 1,
  8426. "zoom": 1,
  8427. //@font-face
  8428. "src": 1
  8429. };
  8430. parser.addListener("property", function(event){
  8431. var name = event.property.text.toLowerCase();
  8432. if (event.invalid) {
  8433. reporter.report(event.invalid.message, event.line, event.col, rule);
  8434. }
  8435. //if (!properties[name] && name.charAt(0) != "-"){
  8436. // reporter.error("Unknown property '" + event.property + "'.", event.line, event.col, rule);
  8437. //}
  8438. });
  8439. }
  8440. });
  8441. /*global CSSLint*/
  8442. CSSLint.addRule({
  8443. //rule information
  8444. id: "outline-none",
  8445. name: "Disallow outline: none",
  8446. desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
  8447. browsers: "All",
  8448. tags: ["Accessibility"],
  8449. //initialization
  8450. init: function(parser, reporter){
  8451. var rule = this,
  8452. lastRule;
  8453. function startRule(event){
  8454. if (event.selectors){
  8455. lastRule = {
  8456. line: event.line,
  8457. col: event.col,
  8458. selectors: event.selectors,
  8459. propCount: 0,
  8460. outline: false
  8461. };
  8462. } else {
  8463. lastRule = null;
  8464. }
  8465. }
  8466. function endRule(event){
  8467. if (lastRule){
  8468. if (lastRule.outline){
  8469. if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") == -1){
  8470. reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
  8471. } else if (lastRule.propCount == 1) {
  8472. reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
  8473. }
  8474. }
  8475. }
  8476. }
  8477. parser.addListener("startrule", startRule);
  8478. parser.addListener("startfontface", startRule);
  8479. parser.addListener("startpage", startRule);
  8480. parser.addListener("startpagemargin", startRule);
  8481. parser.addListener("startkeyframerule", startRule);
  8482. parser.addListener("property", function(event){
  8483. var name = event.property.text.toLowerCase(),
  8484. value = event.value;
  8485. if (lastRule){
  8486. lastRule.propCount++;
  8487. if (name == "outline" && (value == "none" || value == "0")){
  8488. lastRule.outline = true;
  8489. }
  8490. }
  8491. });
  8492. parser.addListener("endrule", endRule);
  8493. parser.addListener("endfontface", endRule);
  8494. parser.addListener("endpage", endRule);
  8495. parser.addListener("endpagemargin", endRule);
  8496. parser.addListener("endkeyframerule", endRule);
  8497. }
  8498. });
  8499. /*global CSSLint*/
  8500. CSSLint.addRule({
  8501. //rule information
  8502. id: "overqualified-elements",
  8503. name: "Disallow overqualified elements",
  8504. desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
  8505. browsers: "All",
  8506. //initialization
  8507. init: function(parser, reporter){
  8508. var rule = this,
  8509. classes = {};
  8510. parser.addListener("startrule", function(event){
  8511. var selectors = event.selectors,
  8512. selector,
  8513. part,
  8514. modifier,
  8515. i, j, k;
  8516. for (i=0; i < selectors.length; i++){
  8517. selector = selectors[i];
  8518. for (j=0; j < selector.parts.length; j++){
  8519. part = selector.parts[j];
  8520. if (part.type == parser.SELECTOR_PART_TYPE){
  8521. for (k=0; k < part.modifiers.length; k++){
  8522. modifier = part.modifiers[k];
  8523. if (part.elementName && modifier.type == "id"){
  8524. reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
  8525. } else if (modifier.type == "class"){
  8526. if (!classes[modifier]){
  8527. classes[modifier] = [];
  8528. }
  8529. classes[modifier].push({ modifier: modifier, part: part });
  8530. }
  8531. }
  8532. }
  8533. }
  8534. }
  8535. });
  8536. parser.addListener("endstylesheet", function(){
  8537. var prop;
  8538. for (prop in classes){
  8539. if (classes.hasOwnProperty(prop)){
  8540. //one use means that this is overqualified
  8541. if (classes[prop].length == 1 && classes[prop][0].part.elementName){
  8542. 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);
  8543. }
  8544. }
  8545. }
  8546. });
  8547. }
  8548. });
  8549. /*global CSSLint*/
  8550. CSSLint.addRule({
  8551. //rule information
  8552. id: "qualified-headings",
  8553. name: "Disallow qualified headings",
  8554. desc: "Headings should not be qualified (namespaced).",
  8555. browsers: "All",
  8556. //initialization
  8557. init: function(parser, reporter){
  8558. var rule = this;
  8559. parser.addListener("startrule", function(event){
  8560. var selectors = event.selectors,
  8561. selector,
  8562. part,
  8563. i, j;
  8564. for (i=0; i < selectors.length; i++){
  8565. selector = selectors[i];
  8566. for (j=0; j < selector.parts.length; j++){
  8567. part = selector.parts[j];
  8568. if (part.type == parser.SELECTOR_PART_TYPE){
  8569. if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0){
  8570. reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
  8571. }
  8572. }
  8573. }
  8574. }
  8575. });
  8576. }
  8577. });
  8578. /*global CSSLint*/
  8579. CSSLint.addRule({
  8580. //rule information
  8581. id: "regex-selectors",
  8582. name: "Disallow selectors that look like regexs",
  8583. desc: "Selectors that look like regular expressions are slow and should be avoided.",
  8584. browsers: "All",
  8585. //initialization
  8586. init: function(parser, reporter){
  8587. var rule = this;
  8588. parser.addListener("startrule", function(event){
  8589. var selectors = event.selectors,
  8590. selector,
  8591. part,
  8592. modifier,
  8593. i, j, k;
  8594. for (i=0; i < selectors.length; i++){
  8595. selector = selectors[i];
  8596. for (j=0; j < selector.parts.length; j++){
  8597. part = selector.parts[j];
  8598. if (part.type == parser.SELECTOR_PART_TYPE){
  8599. for (k=0; k < part.modifiers.length; k++){
  8600. modifier = part.modifiers[k];
  8601. if (modifier.type == "attribute"){
  8602. if (/([\~\|\^\$\*]=)/.test(modifier)){
  8603. reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
  8604. }
  8605. }
  8606. }
  8607. }
  8608. }
  8609. }
  8610. });
  8611. }
  8612. });
  8613. /*global CSSLint*/
  8614. CSSLint.addRule({
  8615. //rule information
  8616. id: "rules-count",
  8617. name: "Rules Count",
  8618. desc: "Track how many rules there are.",
  8619. browsers: "All",
  8620. //initialization
  8621. init: function(parser, reporter){
  8622. var rule = this,
  8623. count = 0;
  8624. //count each rule
  8625. parser.addListener("startrule", function(){
  8626. count++;
  8627. });
  8628. parser.addListener("endstylesheet", function(){
  8629. reporter.stat("rule-count", count);
  8630. });
  8631. }
  8632. });
  8633. /*global CSSLint*/
  8634. CSSLint.addRule({
  8635. //rule information
  8636. id: "shorthand",
  8637. name: "Require shorthand properties",
  8638. desc: "Use shorthand properties where possible.",
  8639. browsers: "All",
  8640. //initialization
  8641. init: function(parser, reporter){
  8642. var rule = this,
  8643. prop, i, len,
  8644. propertiesToCheck = {},
  8645. properties,
  8646. mapping = {
  8647. "margin": [
  8648. "margin-top",
  8649. "margin-bottom",
  8650. "margin-left",
  8651. "margin-right"
  8652. ],
  8653. "padding": [
  8654. "padding-top",
  8655. "padding-bottom",
  8656. "padding-left",
  8657. "padding-right"
  8658. ]
  8659. };
  8660. //initialize propertiesToCheck
  8661. for (prop in mapping){
  8662. if (mapping.hasOwnProperty(prop)){
  8663. for (i=0, len=mapping[prop].length; i < len; i++){
  8664. propertiesToCheck[mapping[prop][i]] = prop;
  8665. }
  8666. }
  8667. }
  8668. function startRule(event){
  8669. properties = {};
  8670. }
  8671. //event handler for end of rules
  8672. function endRule(event){
  8673. var prop, i, len, total;
  8674. //check which properties this rule has
  8675. for (prop in mapping){
  8676. if (mapping.hasOwnProperty(prop)){
  8677. total=0;
  8678. for (i=0, len=mapping[prop].length; i < len; i++){
  8679. total += properties[mapping[prop][i]] ? 1 : 0;
  8680. }
  8681. if (total == mapping[prop].length){
  8682. reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
  8683. }
  8684. }
  8685. }
  8686. }
  8687. parser.addListener("startrule", startRule);
  8688. parser.addListener("startfontface", startRule);
  8689. //check for use of "font-size"
  8690. parser.addListener("property", function(event){
  8691. var name = event.property.toString().toLowerCase(),
  8692. value = event.value.parts[0].value;
  8693. if (propertiesToCheck[name]){
  8694. properties[name] = 1;
  8695. }
  8696. });
  8697. parser.addListener("endrule", endRule);
  8698. parser.addListener("endfontface", endRule);
  8699. }
  8700. });
  8701. /*global CSSLint*/
  8702. CSSLint.addRule({
  8703. //rule information
  8704. id: "text-indent",
  8705. name: "Disallow negative text-indent",
  8706. desc: "Checks for text indent less than -99px",
  8707. browsers: "All",
  8708. //initialization
  8709. init: function(parser, reporter){
  8710. var rule = this,
  8711. textIndent = false;
  8712. function startRule(event){
  8713. textIndent = false;
  8714. }
  8715. //event handler for end of rules
  8716. function endRule(event){
  8717. if (textIndent){
  8718. 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);
  8719. }
  8720. }
  8721. parser.addListener("startrule", startRule);
  8722. parser.addListener("startfontface", startRule);
  8723. //check for use of "font-size"
  8724. parser.addListener("property", function(event){
  8725. var name = event.property.toString().toLowerCase(),
  8726. value = event.value;
  8727. if (name == "text-indent" && value.parts[0].value < -99){
  8728. textIndent = event.property;
  8729. } else if (name == "direction" && value == "ltr"){
  8730. textIndent = false;
  8731. }
  8732. });
  8733. parser.addListener("endrule", endRule);
  8734. parser.addListener("endfontface", endRule);
  8735. }
  8736. });
  8737. /*global CSSLint*/
  8738. CSSLint.addRule({
  8739. //rule information
  8740. id: "unique-headings",
  8741. name: "Headings should only be defined once",
  8742. desc: "Headings should be defined only once.",
  8743. browsers: "All",
  8744. //initialization
  8745. init: function(parser, reporter){
  8746. var rule = this;
  8747. var headings = {
  8748. h1: 0,
  8749. h2: 0,
  8750. h3: 0,
  8751. h4: 0,
  8752. h5: 0,
  8753. h6: 0
  8754. };
  8755. parser.addListener("startrule", function(event){
  8756. var selectors = event.selectors,
  8757. selector,
  8758. part,
  8759. pseudo,
  8760. i, j;
  8761. for (i=0; i < selectors.length; i++){
  8762. selector = selectors[i];
  8763. part = selector.parts[selector.parts.length-1];
  8764. if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())){
  8765. for (j=0; j < part.modifiers.length; j++){
  8766. if (part.modifiers[j].type == "pseudo"){
  8767. pseudo = true;
  8768. break;
  8769. }
  8770. }
  8771. if (!pseudo){
  8772. headings[RegExp.$1]++;
  8773. if (headings[RegExp.$1] > 1) {
  8774. reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
  8775. }
  8776. }
  8777. }
  8778. }
  8779. });
  8780. parser.addListener("endstylesheet", function(event){
  8781. var prop,
  8782. messages = [];
  8783. for (prop in headings){
  8784. if (headings.hasOwnProperty(prop)){
  8785. if (headings[prop] > 1){
  8786. messages.push(headings[prop] + " " + prop + "s");
  8787. }
  8788. }
  8789. }
  8790. if (messages.length){
  8791. reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
  8792. }
  8793. });
  8794. }
  8795. });
  8796. /*global CSSLint*/
  8797. CSSLint.addRule({
  8798. //rule information
  8799. id: "universal-selector",
  8800. name: "Disallow universal selector",
  8801. desc: "The universal selector (*) is known to be slow.",
  8802. browsers: "All",
  8803. //initialization
  8804. init: function(parser, reporter){
  8805. var rule = this;
  8806. parser.addListener("startrule", function(event){
  8807. var selectors = event.selectors,
  8808. selector,
  8809. part,
  8810. modifier,
  8811. i, j, k;
  8812. for (i=0; i < selectors.length; i++){
  8813. selector = selectors[i];
  8814. part = selector.parts[selector.parts.length-1];
  8815. if (part.elementName == "*"){
  8816. reporter.report(rule.desc, part.line, part.col, rule);
  8817. }
  8818. }
  8819. });
  8820. }
  8821. });
  8822. /*global CSSLint*/
  8823. CSSLint.addRule({
  8824. //rule information
  8825. id: "unqualified-attributes",
  8826. name: "Disallow unqualified attribute selectors",
  8827. desc: "Unqualified attribute selectors are known to be slow.",
  8828. browsers: "All",
  8829. //initialization
  8830. init: function(parser, reporter){
  8831. var rule = this;
  8832. parser.addListener("startrule", function(event){
  8833. var selectors = event.selectors,
  8834. selector,
  8835. part,
  8836. modifier,
  8837. i, j, k;
  8838. for (i=0; i < selectors.length; i++){
  8839. selector = selectors[i];
  8840. part = selector.parts[selector.parts.length-1];
  8841. if (part.type == parser.SELECTOR_PART_TYPE){
  8842. for (k=0; k < part.modifiers.length; k++){
  8843. modifier = part.modifiers[k];
  8844. if (modifier.type == "attribute" && (!part.elementName || part.elementName == "*")){
  8845. reporter.report(rule.desc, part.line, part.col, rule);
  8846. }
  8847. }
  8848. }
  8849. }
  8850. });
  8851. }
  8852. });
  8853. /*global CSSLint*/
  8854. CSSLint.addRule({
  8855. //rule information
  8856. id: "vendor-prefix",
  8857. name: "Require standard property with vendor prefix",
  8858. desc: "When using a vendor-prefixed property, make sure to include the standard one.",
  8859. browsers: "All",
  8860. //initialization
  8861. init: function(parser, reporter){
  8862. var rule = this,
  8863. properties,
  8864. num,
  8865. propertiesToCheck = {
  8866. "-webkit-border-radius": "border-radius",
  8867. "-webkit-border-top-left-radius": "border-top-left-radius",
  8868. "-webkit-border-top-right-radius": "border-top-right-radius",
  8869. "-webkit-border-bottom-left-radius": "border-bottom-left-radius",
  8870. "-webkit-border-bottom-right-radius": "border-bottom-right-radius",
  8871. "-o-border-radius": "border-radius",
  8872. "-o-border-top-left-radius": "border-top-left-radius",
  8873. "-o-border-top-right-radius": "border-top-right-radius",
  8874. "-o-border-bottom-left-radius": "border-bottom-left-radius",
  8875. "-o-border-bottom-right-radius": "border-bottom-right-radius",
  8876. "-moz-border-radius": "border-radius",
  8877. "-moz-border-radius-topleft": "border-top-left-radius",
  8878. "-moz-border-radius-topright": "border-top-right-radius",
  8879. "-moz-border-radius-bottomleft": "border-bottom-left-radius",
  8880. "-moz-border-radius-bottomright": "border-bottom-right-radius",
  8881. "-moz-column-count": "column-count",
  8882. "-webkit-column-count": "column-count",
  8883. "-moz-column-gap": "column-gap",
  8884. "-webkit-column-gap": "column-gap",
  8885. "-moz-column-rule": "column-rule",
  8886. "-webkit-column-rule": "column-rule",
  8887. "-moz-column-rule-style": "column-rule-style",
  8888. "-webkit-column-rule-style": "column-rule-style",
  8889. "-moz-column-rule-color": "column-rule-color",
  8890. "-webkit-column-rule-color": "column-rule-color",
  8891. "-moz-column-rule-width": "column-rule-width",
  8892. "-webkit-column-rule-width": "column-rule-width",
  8893. "-moz-column-width": "column-width",
  8894. "-webkit-column-width": "column-width",
  8895. "-webkit-column-span": "column-span",
  8896. "-webkit-columns": "columns",
  8897. "-moz-box-shadow": "box-shadow",
  8898. "-webkit-box-shadow": "box-shadow",
  8899. "-moz-transform" : "transform",
  8900. "-webkit-transform" : "transform",
  8901. "-o-transform" : "transform",
  8902. "-ms-transform" : "transform",
  8903. "-moz-transform-origin" : "transform-origin",
  8904. "-webkit-transform-origin" : "transform-origin",
  8905. "-o-transform-origin" : "transform-origin",
  8906. "-ms-transform-origin" : "transform-origin",
  8907. "-moz-box-sizing" : "box-sizing",
  8908. "-webkit-box-sizing" : "box-sizing",
  8909. "-moz-user-select" : "user-select",
  8910. "-khtml-user-select" : "user-select",
  8911. "-webkit-user-select" : "user-select"
  8912. };
  8913. //event handler for beginning of rules
  8914. function startRule(){
  8915. properties = {};
  8916. num=1;
  8917. }
  8918. //event handler for end of rules
  8919. function endRule(event){
  8920. var prop,
  8921. i, len,
  8922. standard,
  8923. needed,
  8924. actual,
  8925. needsStandard = [];
  8926. for (prop in properties){
  8927. if (propertiesToCheck[prop]){
  8928. needsStandard.push({ actual: prop, needed: propertiesToCheck[prop]});
  8929. }
  8930. }
  8931. for (i=0, len=needsStandard.length; i < len; i++){
  8932. needed = needsStandard[i].needed;
  8933. actual = needsStandard[i].actual;
  8934. if (!properties[needed]){
  8935. reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
  8936. } else {
  8937. //make sure standard property is last
  8938. if (properties[needed][0].pos < properties[actual][0].pos){
  8939. reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
  8940. }
  8941. }
  8942. }
  8943. }
  8944. parser.addListener("startrule", startRule);
  8945. parser.addListener("startfontface", startRule);
  8946. parser.addListener("startpage", startRule);
  8947. parser.addListener("startpagemargin", startRule);
  8948. parser.addListener("startkeyframerule", startRule);
  8949. parser.addListener("property", function(event){
  8950. var name = event.property.text.toLowerCase();
  8951. if (!properties[name]){
  8952. properties[name] = [];
  8953. }
  8954. properties[name].push({ name: event.property, value : event.value, pos:num++ });
  8955. });
  8956. parser.addListener("endrule", endRule);
  8957. parser.addListener("endfontface", endRule);
  8958. parser.addListener("endpage", endRule);
  8959. parser.addListener("endpagemargin", endRule);
  8960. parser.addListener("endkeyframerule", endRule);
  8961. }
  8962. });
  8963. /*global CSSLint*/
  8964. CSSLint.addRule({
  8965. //rule information
  8966. id: "zero-units",
  8967. name: "Disallow units for 0 values",
  8968. desc: "You don't need to specify units when a value is 0.",
  8969. browsers: "All",
  8970. //initialization
  8971. init: function(parser, reporter){
  8972. var rule = this;
  8973. //count how many times "float" is used
  8974. parser.addListener("property", function(event){
  8975. var parts = event.value.parts,
  8976. i = 0,
  8977. len = parts.length;
  8978. while(i < len){
  8979. if ((parts[i].units || parts[i].type == "percentage") && parts[i].value === 0 && parts[i].type != "time"){
  8980. reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
  8981. }
  8982. i++;
  8983. }
  8984. });
  8985. }
  8986. });
  8987. CSSLint.addFormatter({
  8988. //format information
  8989. id: "checkstyle-xml",
  8990. name: "Checkstyle XML format",
  8991. /**
  8992. * Return opening root XML tag.
  8993. * @return {String} to prepend before all results
  8994. */
  8995. startFormat: function(){
  8996. return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
  8997. },
  8998. /**
  8999. * Return closing root XML tag.
  9000. * @return {String} to append after all results
  9001. */
  9002. endFormat: function(){
  9003. return "</checkstyle>";
  9004. },
  9005. /**
  9006. * Given CSS Lint results for a file, return output for this format.
  9007. * @param results {Object} with error and warning messages
  9008. * @param filename {String} relative file path
  9009. * @param options {Object} (UNUSED for now) specifies special handling of output
  9010. * @return {String} output for results
  9011. */
  9012. formatResults: function(results, filename, options) {
  9013. var messages = results.messages,
  9014. output = [];
  9015. var generateSource = function(rule) {
  9016. if (!rule || !('name' in rule)) {
  9017. return "";
  9018. }
  9019. return 'net.csslint.' + rule.name.replace(/\s/g,'');
  9020. };
  9021. var escapeSpecialCharacters = function(str) {
  9022. if (!str || str.constructor !== String) {
  9023. return "";
  9024. }
  9025. return str.replace(/\"/g, "'").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  9026. };
  9027. if (messages.length > 0) {
  9028. output.push("<file name=\""+filename+"\">");
  9029. CSSLint.Util.forEach(messages, function (message, i) {
  9030. //ignore rollups for now
  9031. if (!message.rollup) {
  9032. output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
  9033. " message=\"" + escapeSpecialCharacters(message.message) + "\" source=\"" + generateSource(message.rule) +"\"/>");
  9034. }
  9035. });
  9036. output.push("</file>");
  9037. }
  9038. return output.join("");
  9039. }
  9040. });
  9041. CSSLint.addFormatter({
  9042. //format information
  9043. id: "compact",
  9044. name: "Compact, 'porcelain' format",
  9045. /**
  9046. * Return content to be printed before all file results.
  9047. * @return {String} to prepend before all results
  9048. */
  9049. startFormat: function() {
  9050. return "";
  9051. },
  9052. /**
  9053. * Return content to be printed after all file results.
  9054. * @return {String} to append after all results
  9055. */
  9056. endFormat: function() {
  9057. return "";
  9058. },
  9059. /**
  9060. * Given CSS Lint results for a file, return output for this format.
  9061. * @param results {Object} with error and warning messages
  9062. * @param filename {String} relative file path
  9063. * @param options {Object} (Optional) specifies special handling of output
  9064. * @return {String} output for results
  9065. */
  9066. formatResults: function(results, filename, options) {
  9067. var messages = results.messages,
  9068. output = "";
  9069. options = options || {};
  9070. var capitalize = function(str) {
  9071. return str.charAt(0).toUpperCase() + str.slice(1);
  9072. };
  9073. if (messages.length === 0) {
  9074. return options.quiet ? "" : filename + ": Lint Free!";
  9075. }
  9076. CSSLint.Util.forEach(messages, function(message, i) {
  9077. if (message.rollup) {
  9078. output += filename + ": " + capitalize(message.type) + " - " + message.message + "\n";
  9079. } else {
  9080. output += filename + ": " + "line " + message.line +
  9081. ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + "\n";
  9082. }
  9083. });
  9084. return output;
  9085. }
  9086. });
  9087. CSSLint.addFormatter({
  9088. //format information
  9089. id: "csslint-xml",
  9090. name: "CSSLint XML format",
  9091. /**
  9092. * Return opening root XML tag.
  9093. * @return {String} to prepend before all results
  9094. */
  9095. startFormat: function(){
  9096. return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
  9097. },
  9098. /**
  9099. * Return closing root XML tag.
  9100. * @return {String} to append after all results
  9101. */
  9102. endFormat: function(){
  9103. return "</csslint>";
  9104. },
  9105. /**
  9106. * Given CSS Lint results for a file, return output for this format.
  9107. * @param results {Object} with error and warning messages
  9108. * @param filename {String} relative file path
  9109. * @param options {Object} (UNUSED for now) specifies special handling of output
  9110. * @return {String} output for results
  9111. */
  9112. formatResults: function(results, filename, options) {
  9113. var messages = results.messages,
  9114. output = [];
  9115. var escapeSpecialCharacters = function(str) {
  9116. if (!str || str.constructor !== String) {
  9117. return "";
  9118. }
  9119. return str.replace(/\"/g, "'").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  9120. };
  9121. if (messages.length > 0) {
  9122. output.push("<file name=\""+filename+"\">");
  9123. CSSLint.Util.forEach(messages, function (message, i) {
  9124. if (message.rollup) {
  9125. output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
  9126. } else {
  9127. output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
  9128. " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
  9129. }
  9130. });
  9131. output.push("</file>");
  9132. }
  9133. return output.join("");
  9134. }
  9135. });
  9136. CSSLint.addFormatter({
  9137. //format information
  9138. id: "lint-xml",
  9139. name: "Lint XML format",
  9140. /**
  9141. * Return opening root XML tag.
  9142. * @return {String} to prepend before all results
  9143. */
  9144. startFormat: function(){
  9145. return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
  9146. },
  9147. /**
  9148. * Return closing root XML tag.
  9149. * @return {String} to append after all results
  9150. */
  9151. endFormat: function(){
  9152. return "</lint>";
  9153. },
  9154. /**
  9155. * Given CSS Lint results for a file, return output for this format.
  9156. * @param results {Object} with error and warning messages
  9157. * @param filename {String} relative file path
  9158. * @param options {Object} (UNUSED for now) specifies special handling of output
  9159. * @return {String} output for results
  9160. */
  9161. formatResults: function(results, filename, options) {
  9162. var messages = results.messages,
  9163. output = [];
  9164. var escapeSpecialCharacters = function(str) {
  9165. if (!str || str.constructor !== String) {
  9166. return "";
  9167. }
  9168. return str.replace(/\"/g, "'").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  9169. };
  9170. if (messages.length > 0) {
  9171. output.push("<file name=\""+filename+"\">");
  9172. CSSLint.Util.forEach(messages, function (message, i) {
  9173. if (message.rollup) {
  9174. output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
  9175. } else {
  9176. output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
  9177. " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
  9178. }
  9179. });
  9180. output.push("</file>");
  9181. }
  9182. return output.join("");
  9183. }
  9184. });
  9185. CSSLint.addFormatter({
  9186. //format information
  9187. id: "text",
  9188. name: "Plain Text",
  9189. /**
  9190. * Return content to be printed before all file results.
  9191. * @return {String} to prepend before all results
  9192. */
  9193. startFormat: function() {
  9194. return "";
  9195. },
  9196. /**
  9197. * Return content to be printed after all file results.
  9198. * @return {String} to append after all results
  9199. */
  9200. endFormat: function() {
  9201. return "";
  9202. },
  9203. /**
  9204. * Given CSS Lint results for a file, return output for this format.
  9205. * @param results {Object} with error and warning messages
  9206. * @param filename {String} relative file path
  9207. * @param options {Object} (Optional) specifies special handling of output
  9208. * @return {String} output for results
  9209. */
  9210. formatResults: function(results, filename, options) {
  9211. var messages = results.messages,
  9212. output = "";
  9213. options = options || {};
  9214. if (messages.length === 0) {
  9215. return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
  9216. }
  9217. output = "\n\ncsslint: There are " + messages.length + " problems in " + filename + ".";
  9218. var pos = filename.lastIndexOf("/"),
  9219. shortFilename = filename;
  9220. if (pos === -1){
  9221. pos = filename.lastIndexOf("\\");
  9222. }
  9223. if (pos > -1){
  9224. shortFilename = filename.substring(pos+1);
  9225. }
  9226. CSSLint.Util.forEach(messages, function (message, i) {
  9227. output = output + "\n\n" + shortFilename;
  9228. if (message.rollup) {
  9229. output += "\n" + (i+1) + ": " + message.type;
  9230. output += "\n" + message.message;
  9231. } else {
  9232. output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
  9233. output += "\n" + message.message;
  9234. output += "\n" + message.evidence;
  9235. }
  9236. });
  9237. return output;
  9238. }
  9239. });
  9240. exports.CSSLint = CSSLint;
  9241. });