").append(st.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,a||[e.responseText,t,e])}),this},st.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){st.fn[t]=function(e){return this.on(t,e)}}),st.each(["get","post"],function(e,n){st[n]=function(e,r,i,o){return st.isFunction(r)&&(o=o||i,i=r,r=t),st.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),st.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Dn,type:"GET",isLocal:Fn.test(jn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":In,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":st.parseJSON,"text xml":st.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?H(H(e,st.ajaxSettings),t):H(st.ajaxSettings,e)},ajaxPrefilter:D(Wn),ajaxTransport:D($n),ajax:function(e,n){function r(e,n,r,s){var l,f,v,b,T,N=n;2!==x&&(x=2,u&&clearTimeout(u),i=t,a=s||"",w.readyState=e>0?4:0,r&&(b=M(p,w,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=w.getResponseHeader("Last-Modified"),T&&(st.lastModified[o]=T),T=w.getResponseHeader("etag"),T&&(st.etag[o]=T)),304===e?(l=!0,N="notmodified"):(l=q(p,b),N=l.state,f=l.data,v=l.error,l=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),w.status=e,w.statusText=(n||N)+"",l?g.resolveWith(d,[f,N,w]):g.rejectWith(d,[w,N,v]),w.statusCode(y),y=t,c&&h.trigger(l?"ajaxSuccess":"ajaxError",[w,p,l?f:v]),m.fireWith(d,[w,N]),c&&(h.trigger("ajaxComplete",[w,p]),--st.active||st.event.trigger("ajaxStop")))}"object"==typeof e&&(n=e,e=t),n=n||{};var i,o,a,s,u,l,c,f,p=st.ajaxSetup({},n),d=p.context||p,h=p.context&&(d.nodeType||d.jquery)?st(d):st.event,g=st.Deferred(),m=st.Callbacks("once memory"),y=p.statusCode||{},v={},b={},x=0,T="canceled",w={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!s)for(s={};t=_n.exec(a);)s[t[1].toLowerCase()]=t[2];t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=b[n]=b[n]||e,v[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)y[t]=[y[t],e[t]];else w.always(e[w.status]);return this},abort:function(e){var t=e||T;return i&&i.abort(t),r(0,t),this}};if(g.promise(w).complete=m.add,w.success=w.done,w.error=w.fail,p.url=((e||p.url||Dn)+"").replace(Mn,"").replace(Bn,jn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=st.trim(p.dataType||"*").toLowerCase().match(lt)||[""],null==p.crossDomain&&(l=Pn.exec(p.url.toLowerCase()),p.crossDomain=!(!l||l[1]===jn[1]&&l[2]===jn[2]&&(l[3]||("http:"===l[1]?80:443))==(jn[3]||("http:"===jn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=st.param(p.data,p.traditional)),L(Wn,p,n,w),2===x)return w;c=p.global,c&&0===st.active++&&st.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!On.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(Hn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=qn.test(o)?o.replace(qn,"$1_="+Ln++):o+(Hn.test(o)?"&":"?")+"_="+Ln++)),p.ifModified&&(st.lastModified[o]&&w.setRequestHeader("If-Modified-Since",st.lastModified[o]),st.etag[o]&&w.setRequestHeader("If-None-Match",st.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&w.setRequestHeader("Content-Type",p.contentType),w.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+In+"; q=0.01":""):p.accepts["*"]);for(f in p.headers)w.setRequestHeader(f,p.headers[f]);if(p.beforeSend&&(p.beforeSend.call(d,w,p)===!1||2===x))return w.abort();T="abort";for(f in{success:1,error:1,complete:1})w[f](p[f]);if(i=L($n,p,n,w)){w.readyState=1,c&&h.trigger("ajaxSend",[w,p]),p.async&&p.timeout>0&&(u=setTimeout(function(){w.abort("timeout")},p.timeout));try{x=1,i.send(v,r)}catch(N){if(!(2>x))throw N;r(-1,N)}}else r(-1,"No Transport");return w},getScript:function(e,n){return st.get(e,t,n,"script")},getJSON:function(e,t,n){return st.get(e,t,n,"json")}}),st.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return st.globalEval(e),e}}}),st.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),st.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=V.head||st("head")[0]||V.documentElement;return{send:function(t,i){n=V.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Xn=[],Un=/(=)\?(?=&|$)|\?\?/;st.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xn.pop()||st.expando+"_"+Ln++;return this[e]=!0,e}}),st.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Un.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Un.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=st.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Un,"$1"+o):n.jsonp!==!1&&(n.url+=(Hn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||st.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Xn.push(o)),s&&st.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Vn,Yn,Jn=0,Gn=e.ActiveXObject&&function(){var e;for(e in Vn)Vn[e](t,!0)};st.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&_()||F()}:_,Yn=st.ajaxSettings.xhr(),st.support.cors=!!Yn&&"withCredentials"in Yn,Yn=st.support.ajax=!!Yn,Yn&&st.ajaxTransport(function(n){if(!n.crossDomain||st.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,f,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=st.noop,Gn&&delete Vn[a]),i)4!==u.readyState&&u.abort();else{f={},s=u.status,p=u.responseXML,c=u.getAllResponseHeaders(),p&&p.documentElement&&(f.xml=p),"string"==typeof u.responseText&&(f.text=u.responseText);try{l=u.statusText}catch(d){l=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=f.text?200:404}}catch(h){i||o(-1,h)}f&&o(s,l,f,c)},n.async?4===u.readyState?setTimeout(r):(a=++Jn,Gn&&(Vn||(Vn={},st(e).unload(Gn)),Vn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Qn,Kn,Zn=/^(?:toggle|show|hide)$/,er=RegExp("^(?:([+-])=|)("+ut+")([a-z%]*)$","i"),tr=/queueHooks$/,nr=[W],rr={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=er.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(st.cssNumber[e]?"":"px"),"px"!==r&&s){s=st.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,st.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};st.Animation=st.extend(P,{tweener:function(e,t){st.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");for(var n,r=0,i=e.length;i>r;r++)n=e[r],rr[n]=rr[n]||[],rr[n].unshift(t)},prefilter:function(e,t){t?nr.unshift(e):nr.push(e)}}),st.Tween=$,$.prototype={constructor:$,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(st.cssNumber[n]?"":"px")},cur:function(){var e=$.propHooks[this.prop];return e&&e.get?e.get(this):$.propHooks._default.get(this)},run:function(e){var t,n=$.propHooks[this.prop];return this.pos=t=this.options.duration?st.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):$.propHooks._default.set(this),this}},$.prototype.init.prototype=$.prototype,$.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=st.css(e.elem,e.prop,"auto"),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){st.fx.step[e.prop]?st.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[st.cssProps[e.prop]]||st.cssHooks[e.prop])?st.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},$.propHooks.scrollTop=$.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},st.each(["toggle","show","hide"],function(e,t){var n=st.fn[t];st.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(I(t,!0),e,r,i)}}),st.fn.extend({fadeTo:function(e,t,n,r){return this.filter(w).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=st.isEmptyObject(e),o=st.speed(t,n,r),a=function(){var t=P(this,st.extend({},e),o);a.finish=function(){t.stop(!0)},(i||st._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=st.timers,a=st._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&tr.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&st.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=st._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=st.timers,a=r?r.length:0;for(n.finish=!0,st.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),st.each({slideDown:I("show"),slideUp:I("hide"),slideToggle:I("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){st.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),st.speed=function(e,t,n){var r=e&&"object"==typeof e?st.extend({},e):{complete:n||!n&&t||st.isFunction(e)&&e,duration:e,easing:n&&t||t&&!st.isFunction(t)&&t};return r.duration=st.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in st.fx.speeds?st.fx.speeds[r.duration]:st.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){st.isFunction(r.old)&&r.old.call(this),r.queue&&st.dequeue(this,r.queue)},r},st.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},st.timers=[],st.fx=$.prototype.init,st.fx.tick=function(){var e,n=st.timers,r=0;for(Qn=st.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||st.fx.stop(),Qn=t},st.fx.timer=function(e){e()&&st.timers.push(e)&&st.fx.start()},st.fx.interval=13,st.fx.start=function(){Kn||(Kn=setInterval(st.fx.tick,st.fx.interval))},st.fx.stop=function(){clearInterval(Kn),Kn=null},st.fx.speeds={slow:600,fast:200,_default:400},st.fx.step={},st.expr&&st.expr.filters&&(st.expr.filters.animated=function(e){return st.grep(st.timers,function(t){return e===t.elem}).length}),st.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){st.offset.setOffset(this,e,t)});var n,r,i={top:0,left:0},o=this[0],a=o&&o.ownerDocument;if(a)return n=a.documentElement,st.contains(n,o)?(o.getBoundingClientRect!==t&&(i=o.getBoundingClientRect()),r=z(a),{top:i.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:i.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):i},st.offset={setOffset:function(e,t,n){var r=st.css(e,"position");"static"===r&&(e.style.position="relative");var i,o,a=st(e),s=a.offset(),u=st.css(e,"top"),l=st.css(e,"left"),c=("absolute"===r||"fixed"===r)&&st.inArray("auto",[u,l])>-1,f={},p={};c?(p=a.position(),i=p.top,o=p.left):(i=parseFloat(u)||0,o=parseFloat(l)||0),st.isFunction(t)&&(t=t.call(e,n,s)),null!=t.top&&(f.top=t.top-s.top+i),null!=t.left&&(f.left=t.left-s.left+o),"using"in t?t.using.call(e,f):a.css(f)}},st.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===st.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),st.nodeName(e[0],"html")||(n=e.offset()),n.top+=st.css(e[0],"borderTopWidth",!0),n.left+=st.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-st.css(r,"marginTop",!0),left:t.left-n.left-st.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent||V.documentElement;e&&!st.nodeName(e,"html")&&"static"===st.css(e,"position");)e=e.offsetParent;return e||V.documentElement})}}),st.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);st.fn[e]=function(i){return st.access(this,function(e,i,o){var a=z(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?st(a).scrollLeft():o,r?o:st(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}}),st.each({Height:"height",Width:"width"},function(e,n){st.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){st.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return st.access(this,function(n,r,i){var o;return st.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?st.css(n,r,s):st.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=st,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return st})})(window);
5 | //@ sourceMappingURL=jquery.min.map
6 |
--------------------------------------------------------------------------------
/demo/js.js:
--------------------------------------------------------------------------------
1 | window.jQuery(function( jQuery ) {
2 | var CSSTree = window.CSSTree,
3 | alert = window.alert,
4 | contentArea = jQuery( '.content' ),
5 | textarea = jQuery( 'textarea' ),
6 | button = jQuery( 'button' ),
7 | result = jQuery( 'pre' ),
8 | injectLinks = jQuery( 'input[name=links]' ),
9 | showPositions = jQuery( 'input[name=positions]' ),
10 | positionsCallback = function( key, value ) {
11 | return key === 'position' ? undefined : value;
12 | };
13 |
14 | // Inserts links into tree for highlighting
15 | function insertLinks( branches ) {
16 | branches.forEach(function( branch ) {
17 | if ( branch.position ) {
18 | branch.highlight = "Range [" + branch.position.range.start + "," + branch.position.range.end + "]";
19 | }
20 |
21 | if ( branch.rules && branch.rules.length ) {
22 | branch.rules.forEach(function( rule ) {
23 | if ( rule.position ) {
24 | rule.highlight = "Range [" + rule.position.range.start + "," + rule.position.range.end + "]";
25 | }
26 | });
27 | }
28 |
29 | if ( branch.branches && branch.branches.length ) {
30 | insertLinks( branch.branches );
31 | }
32 | });
33 | }
34 |
35 | // Highlighting input ranges
36 | function select( start, end ) {
37 | var element = textarea[ 0 ], range;
38 |
39 | if ( start >= end ) {
40 | alert( 'Start Must Be Less Than End' );
41 | return;
42 | }
43 | else if ( element.createTextRange ) {
44 | range = element.createTextRange();
45 | range.collapse( true );
46 | range.moveStart( 'character', start );
47 | range.moveEnd( 'character', end );
48 | range.select();
49 | }
50 | else if ( element.setSelectionRange ) {
51 | element.setSelectionRange( start, end );
52 | }
53 | else if ( element.selectionStart ) {
54 | element.selectionStart = start;
55 | element.selectionEnd = end;
56 | }
57 | }
58 |
59 | // Scroll text area to highlighted section (centered if possible)
60 | function scroll(){
61 | var current = textarea.scrollTop(),
62 | caret = textarea.textareaHelper( 'caretPos' ).top,
63 | height = textarea.height(),
64 | ypos = current + caret - parseInt( height / 2, 10 );
65 |
66 | if ( ypos < 0 ) {
67 | ypos = 0;
68 | }
69 |
70 | textarea.scrollTop( ypos );
71 | }
72 |
73 | // Watch for highlighting
74 | result.on( 'click', 'a', function(){
75 | var parts = jQuery( this ).attr( 'data-pos' ).split( ',' ),
76 | start = parseInt( parts[ 0 ] || '', 10 ),
77 | end = parseInt( parts[ 1 ] || '', 10 ) + 1;
78 |
79 | if ( end > start ) {
80 | select( start, end );
81 | scroll();
82 | }
83 |
84 | return false;
85 | });
86 |
87 | // Watch for run button
88 | button.on( 'click', function(){
89 | var tree = CSSTree( textarea.val() || '' ).branches,
90 | string;
91 |
92 | if ( injectLinks.is( ':checked' ) ) {
93 | insertLinks( tree );
94 | }
95 |
96 | string = JSON.stringify( tree, showPositions.is( ':checked' ) ? null : positionsCallback, 2 );
97 | string = string.substr( 1, string.length - 2 ).replace( /\\n/g, "\n" ).replace( /\\t/g, "\t" );
98 | string = string.replace( /"highlight": "Range \[(\d+)\,(\d+)\]"/g, '"highlight": "
Range [$1,$2]"' );
99 | result.removeClass( 'hide' ).html( string );
100 | contentArea.addClass( 'active' );
101 | });
102 | });
103 |
--------------------------------------------------------------------------------
/demo/textarea.js:
--------------------------------------------------------------------------------
1 | // Textarea Helper from Codecademy
2 | // https://github.com/Codecademy/textarea-helper
3 | (function($) {
4 | var caretClass = 'textarea-helper-caret',
5 | dataKey = 'textarea-helper'
6 |
7 | // Styles that could influence size of the mirrored element.
8 | ,
9 | mirrorStyles = [
10 | // Box Styles.
11 | 'box-sizing', 'height', 'width', 'padding-bottom', 'padding-left', 'padding-right', 'padding-top'
12 |
13 | // Font stuff.
14 | ,
15 | 'font-family', 'font-size', 'font-style', 'font-variant', 'font-weight'
16 |
17 | // Spacing etc.
18 | ,
19 | 'word-spacing', 'letter-spacing', 'line-height', 'text-decoration', 'text-indent', 'text-transform'];
20 |
21 | var TextareaHelper = function(elem) {
22 | if (elem.nodeName.toLowerCase() !== 'textarea') return;
23 | this.$text = $(elem);
24 | this.$mirror = $('
')
25 | .css({
26 | 'position': 'absolute',
27 | 'overflow': 'auto',
28 | 'white-space': 'pre-wrap',
29 | 'word-wrap': 'break-word',
30 | 'top': 0,
31 | 'left': -9999
32 | })
33 | .insertAfter(this.$text);
34 | };
35 |
36 | (function() {
37 | this.update = function() {
38 |
39 | // Copy styles.
40 | var styles = {};
41 | for (var i = 0, style; style = mirrorStyles[i]; i++) {
42 | styles[style] = this.$text.css(style);
43 | }
44 | this.$mirror.css(styles)
45 | .empty();
46 |
47 | // Update content and insert caret.
48 | var caretPos = this.getOriginalCaretPos(),
49 | str = this.$text.val(),
50 | pre = document.createTextNode(str.substring(0, caretPos)),
51 | post = document.createTextNode(str.substring(caretPos)),
52 | $car = $('
')
53 | .addClass(caretClass)
54 | .html(' ');
55 | this.$mirror.append(pre, $car, post)
56 | .scrollTop(this.$text.scrollTop());
57 | };
58 |
59 | this.destroy = function() {
60 | this.$mirror.remove();
61 | this.$text.removeData(dataKey);
62 | return null;
63 | };
64 |
65 | this.caretPos = function() {
66 | this.update();
67 | return this.$mirror.find('.' + caretClass)
68 | .position();
69 | };
70 |
71 | this.height = function() {
72 | this.update();
73 | this.$mirror.css('height', '');
74 | return this.$mirror.height();
75 | };
76 |
77 | // XBrowser caret position
78 | // Adapted from http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea
79 | this.getOriginalCaretPos = function() {
80 | var text = this.$text[0];
81 | if (text.selectionStart) {
82 | return text.selectionStart;
83 | } else if (document.selection) {
84 | text.focus();
85 | var r = document.selection.createRange();
86 | if (r == null) {
87 | return 0;
88 | }
89 | var re = text.createTextRange(),
90 | rc = re.duplicate();
91 | re.moveToBookmark(r.getBookmark());
92 | rc.setEndPoint('EndToStart', re);
93 | return rc.text.length;
94 | }
95 | return 0;
96 | };
97 |
98 | })
99 | .call(TextareaHelper.prototype);
100 |
101 | $.fn.textareaHelper = function(method) {
102 | this.each(function() {
103 | var $this = $(this),
104 | instance = $this.data(dataKey);
105 | if (!instance) {
106 | instance = new TextareaHelper(this);
107 | $this.data(dataKey, instance);
108 | }
109 | });
110 | if (method) {
111 | var instance = this.first()
112 | .data(dataKey);
113 | return instance[method]();
114 | } else {
115 | return this;
116 | }
117 | };
118 |
119 | })(jQuery);
120 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var fs = require( 'fs' ),
2 | _CSSTree = global.CSSTree,
3 | _StringIterator = global.StringIterator;
4 |
5 |
6 | // Grab CSSTree files
7 | global.StringIterator = require( "string-iterator" );
8 | global.CSSTree = module.exports = require( './lib/CSSTree.js' );
9 | require( './build/libs.js' ).forEach(function( file ) {
10 | require( './lib/' + file );
11 | });
12 |
13 |
14 | // Attach exporter for parent modules
15 | global.CSSTree.exportScript = function( callback ) {
16 | if ( callback ) {
17 | fs.readFile( __dirname + '/dist/CSSTree.js', 'utf8', callback );
18 | }
19 | else {
20 | return fs.readFileSync( __dirname + '/dist/CSSTree.js', 'utf8' );
21 | }
22 | };
23 |
24 |
25 | // Clear global case
26 | global.StringIterator = _StringIterator;
27 | global.CSSTree = _CSSTree;
28 |
--------------------------------------------------------------------------------
/lib/AtRule.js:
--------------------------------------------------------------------------------
1 | var ratruleseek = /"|'|\(/,
2 | ratrulepart = /[\, ]/;
3 |
4 |
5 | function AtRule( atrule, position ) {
6 | var self = this;
7 |
8 | if ( ! ( self instanceof AtRule ) ) {
9 | return new AtRule( atrule, position );
10 | }
11 |
12 | self.atrule = ( atrule || '' ).trim();
13 | self.position = position;
14 | self.parts = [];
15 | self.breakdown();
16 | }
17 |
18 | AtRule.prototype = {
19 |
20 | breakdown: function(){
21 | var self = this, atrule = self.atrule,
22 | parts = [], part = '',
23 | i = -1, l = atrule.length, c = '',
24 | seek;
25 |
26 | for ( ; ++i < l; ) {
27 | c = atrule[ i ];
28 |
29 | // Part separator
30 | if ( ratrulepart.exec( c ) ) {
31 | part = part.trim();
32 |
33 | if ( part.length ) {
34 | parts.push( part );
35 | }
36 |
37 | if ( c != ' ' ) {
38 | parts.push( c );
39 | }
40 |
41 | part = '';
42 | }
43 | // Read all characters in a seek sequence ((...), "...", '...', etc)
44 | else if ( ratruleseek.exec( c ) ) {
45 | part += c;
46 | seek = c == '(' ? ')' : c;
47 |
48 | // Find end char
49 | for ( ; ++i < l; ) {
50 | c = atrule[ i ];
51 | part += c;
52 |
53 | // End char found, break off and attach part
54 | if ( c == seek ) {
55 | break;
56 | }
57 | // Skip over escaped values
58 | else if ( c == "\\" && atrule[ i + 1 ] ) {
59 | part += atrule[ ++i ];
60 | }
61 | }
62 | }
63 | // Add to the current part
64 | else {
65 | part += c;
66 | }
67 | }
68 |
69 | // Catch the last part and add it
70 | if ( ( part = part.trim() ).length ) {
71 | parts.push( part );
72 | }
73 |
74 | self.parts = parts;
75 | }
76 |
77 | };
78 |
79 | global.CSSTree.AtRule = AtRule;
80 |
--------------------------------------------------------------------------------
/lib/CSSTree.js:
--------------------------------------------------------------------------------
1 | var rwhitespace = /(\s|\t|\r\n|\r|\n)/,
2 | leftTrim = /^(\r\n|\r|\n|\t|\s)*/,
3 | StringIterator = global.StringIterator;
4 |
5 |
6 | function CSSTree( css ) {
7 | var self = this, m;
8 |
9 | // Force instance of CSSTree
10 | if ( ! ( self instanceof CSSTree ) ) {
11 | return new CSSTree( css );
12 | }
13 |
14 | // Internals
15 | self.iter = new StringIterator( css );
16 | self.css = css;
17 | self.branches = [];
18 |
19 | // Begin rendering
20 | self.render();
21 | }
22 |
23 | // Methods
24 | CSSTree.prototype = {
25 |
26 | // Starts process of reading the stylesheet
27 | render: function(){
28 | var self = this,
29 | iter = self.iter;
30 |
31 | iter.each(function( c ) {
32 | // Comment
33 | if ( c == '/' && iter.next == '*' ) {
34 | iter.reverse();
35 | self.comment();
36 | }
37 | // Skip over whitespace, but assume anything else is a selector/atrule
38 | else if ( ! rwhitespace.exec( c ) ) {
39 | iter.reverse();
40 | self.selector();
41 | }
42 | });
43 |
44 | // Apply line/character information to all positions
45 | self._positions( self.branches );
46 | },
47 |
48 | // Ignore comment blocks
49 | comment: function( nested ) {
50 | var self = this,
51 | iter = self.iter,
52 | position = new CSSTree.Position( iter.index + 1 ),
53 | comment = iter.each(function( c, iter ) {
54 | if ( iter.c == '/' && iter.prev == '*' ) {
55 | return false;
56 | }
57 | });
58 |
59 | self.branches.push(
60 | new CSSTree.Comment( comment, nested || false, position.markEnd( iter.index ) )
61 | );
62 | },
63 |
64 | // Selctor looks for opening rule set or closing semicolon for oneliners
65 | selector: function(){
66 | var self = this,
67 | iter = self.iter,
68 | position = new CSSTree.Position( iter.index + 1 ),
69 | selector = '',
70 | branch;
71 |
72 | iter.each(function( c ) {
73 | // Comment block
74 | if ( c == '/' && iter.next == '*' ) {
75 | iter.reverse();
76 | position.markChunkEnd( iter.index );
77 | self.comment();
78 | position.markChunkStart( iter.index + 1 );
79 | }
80 | // Atrule
81 | else if ( c == ';' ) {
82 | return false;
83 | }
84 | // Media atrule
85 | else if ( c == '{' && selector.trim()[ 0 ] == '@' ) {
86 | branch = self.nested( selector, position );
87 | branch.position = position.markEnd( iter.index );
88 | self.branches.push( branch );
89 |
90 | selector = null;
91 | return false;
92 | }
93 | // Selector for ruleset
94 | else if ( c == '{' ) {
95 | branch = new CSSTree.Selector( selector, self.rules( position ), position );
96 | position.markEnd( iter.index );
97 | self.branches.push( branch );
98 |
99 | selector = null;
100 | return false;
101 | }
102 | // Escape string
103 | else if ( c == "\\" ) {
104 | iter.skip();
105 | selector += c + iter.c;
106 | }
107 | // Seek
108 | else if ( c == "'" || c == '"' ) {
109 | selector += c + iter.seek( c );
110 | }
111 | // Seek
112 | else if ( c == '(' ) {
113 | selector += c + iter.seek( ')' );
114 | }
115 | // Add to selector string
116 | else {
117 | selector += c;
118 | }
119 | });
120 |
121 | // Single line queries
122 | if ( selector && selector.trim().length ) {
123 | self.branches.push(
124 | new CSSTree.AtRule( selector, position.markEnd( iter.index - 1 ) )
125 | );
126 | }
127 | },
128 |
129 | // Rule Sets
130 | rules: function( parentPos ) {
131 | var self = this,
132 | iter = self.iter,
133 | rules = [],
134 | rule, position;
135 |
136 | iter.each(function( c ) {
137 | // Nested Comment block
138 | if ( c == '/' && iter.next == '*' ) {
139 | iter.reverse();
140 | parentPos.markChunkEnd( iter.index );
141 | self.comment( true );
142 | parentPos.markChunkStart( iter.index + 1 );
143 | }
144 | // End of rules
145 | else if ( c == '}' ) {
146 | return false;
147 | }
148 | // New rule
149 | else if ( ! rwhitespace.exec( c ) ) {
150 | position = new CSSTree.Position( iter.index, parentPos );
151 | iter.reverse();
152 |
153 | rule = new CSSTree.Rule(
154 | self.property( position ).trim(),
155 | self.value( position ).trim(),
156 | position
157 | );
158 |
159 | // Break down the parts of the value, and push it
160 | position.markEnd( iter.index - ( iter.c == ';' ? 1 : 0 ), false );
161 |
162 | // Only add if there is an actual property
163 | if ( rule.property.length ) {
164 | rules.push( rule );
165 | }
166 | }
167 | });
168 |
169 | return rules;
170 | },
171 |
172 | // Property Names
173 | property: function( position ) {
174 | var self = this,
175 | iter = self.iter,
176 | property = '';
177 |
178 | iter.each(function( c ) {
179 | // Nested Comment block
180 | if ( c == '/' && iter.next == '*' ) {
181 | iter.reverse();
182 | position.markChunkEnd( iter.index );
183 | self.comment( true );
184 | position.markChunkStart( iter.index + 1 );
185 | }
186 | // End of property
187 | else if ( c == ':' ) {
188 | return false;
189 | }
190 | // Invalid CSS, but still end of property
191 | else if ( c == ';' || c == '}' ) {
192 | iter.reverse();
193 | return false;
194 | }
195 | else {
196 | property += c;
197 | }
198 | });
199 |
200 | return property;
201 | },
202 |
203 | // Values
204 | value: function( position ) {
205 | var self = this,
206 | iter = self.iter,
207 | value = '';
208 |
209 | iter.each(function( c ) {
210 | // Nested Comment block
211 | if ( c == '/' && iter.next == '*' ) {
212 | iter.reverse();
213 | position.markChunkEnd( iter.index );
214 | self.comment( true );
215 | position.markChunkStart( iter.index + 1 );
216 | }
217 | // End of value
218 | else if ( c == ';' ) {
219 | return false;
220 | }
221 | // Watch for no semi-colon at end of set
222 | else if ( c == '}' ) {
223 | iter.reverse();
224 | return false;
225 | }
226 | // Seek strings
227 | else if ( c == "'" || c == '"' ) {
228 | value += c + iter.seek( c );
229 | }
230 | // Seek groupings
231 | else if ( c == '(' ) {
232 | value += c + iter.seek( ')' );
233 | }
234 | // Append
235 | else {
236 | value += c;
237 | }
238 | });
239 |
240 | return value;
241 | },
242 |
243 | // Nested atrules
244 | nested: function( atrule, position ) {
245 | var self = this,
246 | iter = self.iter,
247 | startPos = null,
248 | string = '',
249 | peek, index, rule, subPosition, character,
250 | block = atrule.trim()[ 0 ] == '@' ?
251 | new CSSTree.AtRule( atrule, position ) :
252 | new CSSTree.Selector( atrule, null, position );
253 |
254 | iter.each(function( c ) {
255 | // Nested Comment block
256 | if ( c == '/' && iter.next == '*' ) {
257 | iter.reverse();
258 | position.markChunkEnd( iter.index );
259 | self.comment( true );
260 | position.markChunkStart( iter.index );
261 | }
262 | // End of property:value
263 | else if ( c == ';' ) {
264 | subPosition = new CSSTree.Position( startPos );
265 | subPosition.markEnd( iter.index - ( iter.c == ';' ? 1 : 0 ) );
266 |
267 | // String trimming & positioning
268 | string = string.trim();
269 | index = string.indexOf( ':' );
270 |
271 | // Build up rule property
272 | rule = new CSSTree.Rule(
273 | string.substr( 0, index ).trim(),
274 | string.substr( index + 1 ).trim(),
275 | subPosition
276 | );
277 |
278 | // Atrules don't startoff with rules
279 | if ( ! block.rules ) {
280 | block.rules = [];
281 | }
282 |
283 | // Parse out parts and add to branches rules
284 | block.rules.push( rule );
285 | startPos = null;
286 | string = '';
287 | }
288 | // Nested Block
289 | else if ( c == '{' ) {
290 | if ( ! block.branches ) {
291 | block.branches = [];
292 | }
293 |
294 | // Nested branches should start tracking at the first character, not whitespace
295 | string = string.replace( leftTrim, '' );
296 |
297 | // Travel down the tree
298 | subPosition = new CSSTree.Position( startPos, position );
299 | block.branches.push( self.nested( string, subPosition ) );
300 | startPos = null;
301 | string = '';
302 | }
303 | // Seek
304 | else if ( c == "'" || c == '"' ) {
305 | string += c + iter.seek( c );
306 | }
307 | // Seek
308 | else if ( c == '(' ) {
309 | string += c + iter.seek( ')' );
310 | }
311 | // End of block
312 | else if ( c == '}' ) {
313 | subPosition = new CSSTree.Position( startPos );
314 |
315 | // Assume any string left is a property:value definition
316 | if ( ( string = string.trim() ).length ) {
317 | index = string.indexOf( ':' );
318 | rule = new CSSTree.Rule(
319 | string.substr( 0, index ).trim(),
320 | string.substr( index + 1 ).trim(),
321 | subPosition.markEnd( iter.index - 1 )
322 | );
323 |
324 | // Atrules don't startoff with rules
325 | if ( ! block.rules ) {
326 | block.rules = [];
327 | }
328 |
329 | // Parse out value parts and add to rules
330 | block.rules.push( rule );
331 | }
332 |
333 | block.position.markEnd( iter.index, false );
334 | return false;
335 | }
336 | // Append
337 | else {
338 | if ( startPos === null && ! rwhitespace.exec( c ) ) {
339 | startPos = iter.index;
340 | }
341 |
342 | string += c;
343 | }
344 | });
345 |
346 | return block;
347 | },
348 |
349 | // Crawl the tree to finish position markup
350 | _positions: function( branches ) {
351 | var self = this;
352 |
353 | // Cycle through each branch for markings
354 | branches.forEach(function( branch ) {
355 | self._markPosition( branch.position );
356 |
357 | // Markup each rule positions
358 | if ( branch.rules ) {
359 | branch.rules.forEach(function( rule ) {
360 | if ( rule.position ) {
361 | self._markPosition( rule.position );
362 | }
363 | });
364 | }
365 |
366 | // Crawl nested branches
367 | if ( branch.branches ) {
368 | self._positions( branch.branches );
369 | }
370 | });
371 | },
372 |
373 | // Marks up position
374 | _markPosition: function( position ) {
375 | var self = this;
376 |
377 | // Mark branch positions
378 | position.start = self._charPosition( position.range.start );
379 | position.end = self._charPosition( position.range.end );
380 | },
381 |
382 | // Marks the line & character for the character position passed
383 | _charPosition: function( pos ) {
384 | var self = this,
385 | iter = self.iter.goto( pos );
386 |
387 | return {
388 | line: iter.line,
389 | character: iter.character
390 | };
391 | }
392 |
393 | };
394 |
395 | // Keep Reference
396 | CSSTree.StringIterator = StringIterator;
397 |
398 | // Expose to NodeJS/Window
399 | if ( typeof module == 'object' && typeof module.exports == 'object' ) {
400 | module.exports = CSSTree;
401 | }
402 | else {
403 | global.CSSTree = CSSTree;
404 | }
405 |
--------------------------------------------------------------------------------
/lib/Comment.js:
--------------------------------------------------------------------------------
1 | function Comment( comment, nested, position ) {
2 | var self = this;
3 |
4 | if ( ! ( self instanceof Comment ) ) {
5 | return new Comment( comment, nested, position );
6 | }
7 |
8 | self.comment = ( comment || '' ).trim();
9 | self.nested = !!nested;
10 | self.position = position;
11 | }
12 |
13 | global.CSSTree.Comment = Comment;
14 |
--------------------------------------------------------------------------------
/lib/Position.js:
--------------------------------------------------------------------------------
1 | function Position( start, _parent ) {
2 | var self = this;
3 |
4 | if ( ! ( self instanceof Position ) ) {
5 | return new Position( start, _parent );
6 | }
7 |
8 | if ( _parent && _parent instanceof Position ) {
9 | self._parent = _parent;
10 | }
11 |
12 | if ( start === undefined ) {
13 | start = 0;
14 | }
15 |
16 | self.range = { start: start, end: start, length: 0 };
17 | self.start = { line: 0, character: 0 };
18 | self.end = { line: 0, character: 0 };
19 | self.chunks = [];
20 | self.markChunkStart( start, false );
21 | }
22 |
23 | Position.prototype = {
24 |
25 | markChunkStart: function( pos, propagate ) {
26 | var self = this;
27 |
28 | self._chunk = { start: pos, end: pos, length: 0 };
29 | self.chunks.push( self._chunk );
30 |
31 | if ( self._parent && propagate !== false ) {
32 | self._parent.markChunkStart( pos, propagate );
33 | }
34 | },
35 |
36 | markChunkEnd: function( pos, propagate ) {
37 | var self = this;
38 |
39 | if ( self._chunk ) {
40 | self._chunk.end = pos;
41 | self._chunk.length = pos - self._chunk.start + 1;
42 | delete self._chunk;
43 | }
44 |
45 | if ( self._parent && propagate !== false ) {
46 | self._parent.markChunkEnd( pos, propagate );
47 | }
48 |
49 | return self;
50 | },
51 |
52 | markEnd: function( pos, propagate ) {
53 | var self = this;
54 |
55 | self.markChunkEnd( pos, propagate );
56 | self.range.end = pos;
57 | self.range.length = pos - self.range.start + 1;
58 |
59 | if ( self._parent ) {
60 | delete self._parent;
61 | }
62 |
63 | return self;
64 | }
65 |
66 | };
67 |
68 | global.CSSTree.Position = Position;
69 |
--------------------------------------------------------------------------------
/lib/Rule.js:
--------------------------------------------------------------------------------
1 | var rwhitespace = /(\s|\t|\r\n|\r|\n)/,
2 | rvalueseparator = /\/|,/,
3 | rvalueseek = /"|'|\(/;
4 |
5 |
6 | function Rule( property, value, position ) {
7 | var self = this;
8 |
9 | if ( ! ( self instanceof Rule ) ) {
10 | return new Rule( property, value, position );
11 | }
12 |
13 | self.property = ( property || '' ).trim();
14 | self.value = ( value || '' ).trim();
15 | self.position = position;
16 | self.parts = [];
17 | self.breakdown();
18 | }
19 |
20 | Rule.prototype = {
21 |
22 | breakdown: function(){
23 | var self = this, value = self.value,
24 | parts = [], i = -1, l = value.length, part = '', c, seek;
25 |
26 | for ( ; ++i < l; ) {
27 | c = value[ i ];
28 |
29 | // Whitespace is a value separator
30 | if ( rwhitespace.exec( c ) ) {
31 | if ( ( part = part.trim() ).length ) {
32 | parts.push( part );
33 | }
34 |
35 | part = '';
36 | }
37 | // Value separator
38 | else if ( rvalueseparator.exec( c ) ) {
39 | if ( ( part = part.trim() ).length ) {
40 | parts.push( part );
41 | }
42 |
43 | parts.push( c );
44 | part = '';
45 | }
46 | // Read all characters in a seek sequence (url(...), "...", '...', etc)
47 | else if ( rvalueseek.exec( c ) ) {
48 | part += c;
49 | seek = c == '(' ? ')' : c;
50 |
51 | // Find end char
52 | for ( ; ++i < l; ) {
53 | c = value[ i ];
54 | part += c;
55 |
56 | // End char found, break off and attach part
57 | if ( c == seek ) {
58 | break;
59 | }
60 | // Skip over escaped values
61 | else if ( c == "\\" && value[ i + 1 ] ) {
62 | part += value[ ++i ];
63 | }
64 | }
65 |
66 | parts.push( part );
67 | part = '';
68 | }
69 | else {
70 | part += c;
71 | }
72 | }
73 |
74 | // Attach final leftover part
75 | if ( part.length ) {
76 | parts.push( part );
77 | }
78 |
79 | self.parts = parts;
80 | }
81 |
82 | };
83 |
84 | global.CSSTree.Rule = Rule;
85 |
--------------------------------------------------------------------------------
/lib/Selector.js:
--------------------------------------------------------------------------------
1 | var rselectornested = /[:#\[\.]/,
2 | rselectorpart = /[>~\+\*\,]/,
3 | rwhitespace = /(\s|\t|\r\n|\r|\n)/;
4 |
5 |
6 | function Selector( selector, rules, position ) {
7 | var self = this;
8 |
9 | if ( ! ( self instanceof Selector ) ) {
10 | return new Selector( selector, rules, position );
11 | }
12 |
13 | self.selector = ( selector || '' ).trim();
14 | self.position = position;
15 | self.parts = [];
16 | self.rules = rules || [];
17 | self.breakdown();
18 | }
19 |
20 | Selector.prototype = {
21 |
22 | breakdown: function(){
23 | var self = this, selector = self.selector,
24 | parts = [], nested = null, part = '',
25 | i = -1, l = selector.length, c = '',
26 | innerSeek = null;
27 |
28 | for ( ; ++i < l; ) {
29 | c = selector[ i ];
30 |
31 | // Selector breaker (whitespace, comma, etc...)
32 | if ( rselectorpart.exec( c ) || rwhitespace.exec( c ) ) {
33 | part = part.trim();
34 |
35 | if ( nested !== null ) {
36 | if ( part.length ) {
37 | nested.push( part );
38 | }
39 |
40 | parts.push( nested );
41 | }
42 | else if ( part.length ) {
43 | parts.push( part );
44 | }
45 |
46 | if ( ! rwhitespace.exec( c ) ) {
47 | parts.push( c );
48 | }
49 |
50 | nested = null;
51 | part = '';
52 | }
53 | // Inside selector, but still need to break apart ('span.class')
54 | else if ( part.length && rselectornested.exec( c ) ) {
55 | part = part.trim();
56 |
57 | // Catch whitespace parts and just continue on
58 | if ( ! part.length ) {
59 | part = c;
60 | continue;
61 | }
62 |
63 | // Add part to nested array
64 | if ( nested === null ) {
65 | nested = [ part ];
66 | }
67 | else {
68 | nested.push( part );
69 | }
70 |
71 | // Create new part
72 | part = c;
73 |
74 | // Seek to closing brace
75 | if ( c == '[' ) {
76 | for ( ; ++i < l; ) {
77 | c = selector[ i ];
78 | part += c;
79 |
80 | if ( c == "\\" ) {
81 | part += selector[ ++i ];
82 | }
83 | else if ( innerSeek !== null ) {
84 | if ( c == innerSeek ) {
85 | innerSeek = null;
86 | }
87 | }
88 | else if ( c == '"' || c == "'" ) {
89 | innerSeek = c;
90 | }
91 | else if ( c == ']' ) {
92 | nested.push( part );
93 | part = '';
94 | break;
95 | }
96 | }
97 | }
98 | }
99 | else {
100 | part += c;
101 | }
102 | }
103 |
104 | // Catch the last part if it exists
105 | if ( ( part = part.trim() ).length || nested !== null ) {
106 | if ( nested !== null ) {
107 | if ( part.length ) {
108 | nested.push( part );
109 | }
110 |
111 | parts.push( nested );
112 | }
113 | else {
114 | parts.push( part );
115 | }
116 | }
117 |
118 | // Apply parts back to object
119 | self.parts = parts;
120 | }
121 |
122 | };
123 |
124 | global.CSSTree.Selector = Selector;
125 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "csstree",
3 | "version": "0.0.3",
4 | "main": "./index.js",
5 | "description": "CSS AST Builder",
6 | "keywords": [ "css", "parser", "tool", "ast" ],
7 | "author": "Corey Hart
(http://www.codenothing.com)",
8 | "homepage": "http://codenothing.github.com/CSSTree/",
9 | "license": "MIT",
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/codenothing/CSSTree.git"
13 | },
14 | "bugs": {
15 | "url": "https://github.com/codenothing/CSSTree/issues",
16 | "email": "corey@codenothing.com"
17 | },
18 | "engines": {
19 | "node": ">=0.8"
20 | },
21 | "scripts": {
22 | "test": "make test",
23 | "postinstall": "node build/build.js"
24 | },
25 | "dependencies": {
26 | "string-iterator": "0.0.1"
27 | },
28 | "devDependencies": {
29 | "nlint": "0.0.6",
30 | "munit": "0.0.7",
31 | "async": "0.2.9"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/test/munit.js:
--------------------------------------------------------------------------------
1 | global.CSSTree = require( '../' );
2 |
--------------------------------------------------------------------------------
/test/positions/box-model/sheet.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Thanks to yuicompressor for this test case: http://developer.yahoo.com/yui/compressor/css.html
4 |
5 | */
6 | #elem span.color span {
7 | width: 100px; /* IE */
8 | voice-family: "\"}\"";
9 | voice-family: inherit;
10 | width: 200px; /* others */
11 | }
12 |
13 | html > body #elem {
14 | width: 200px; /* others */
15 | }
16 |
--------------------------------------------------------------------------------
/test/positions/box-model/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | nested: false,
4 | comment: "/*\n\nThanks to yuicompressor for this test case: http://developer.yahoo.com/yui/compressor/css.html\n\n*/",
5 | position: {
6 | range: {
7 | start: 0,
8 | end: 101,
9 | length: 102
10 | },
11 | start: {
12 | line: 1,
13 | character: 1
14 | },
15 | end: {
16 | line: 5,
17 | character: 2
18 | },
19 | chunks: [
20 | {
21 | start: 0,
22 | end: 101,
23 | length: 102
24 | }
25 | ]
26 | }
27 | },
28 | {
29 | nested: true,
30 | comment: "/* IE */",
31 | position: {
32 | range: {
33 | start: 142,
34 | end: 149,
35 | length: 8
36 | },
37 | start: {
38 | line: 7,
39 | character: 16
40 | },
41 | end: {
42 | line: 7,
43 | character: 23
44 | },
45 | chunks: [
46 | {
47 | start: 142,
48 | end: 149,
49 | length: 8
50 | }
51 | ]
52 | }
53 | },
54 | {
55 | comment: "/* others */",
56 | nested: true,
57 | position: {
58 | range: {
59 | start: 214,
60 | end: 225,
61 | length: 12
62 | },
63 | start: {
64 | line: 10,
65 | character: 16
66 | },
67 | end: {
68 | line: 10,
69 | character: 27
70 | },
71 | chunks: [
72 | {
73 | start: 214,
74 | end: 225,
75 | length: 12
76 | }
77 | ]
78 | }
79 | },
80 | {
81 | selector: '#elem span.color span',
82 | parts: [
83 | '#elem',
84 | [
85 | 'span',
86 | '.color'
87 | ],
88 | 'span'
89 | ],
90 | position: {
91 | range: {
92 | start: 103,
93 | end: 227,
94 | length: 125
95 | },
96 | start: {
97 | line: 6,
98 | character: 1
99 | },
100 | end: {
101 | line: 11,
102 | character: 1
103 | },
104 | chunks: [
105 | {
106 | start: 103,
107 | end: 141,
108 | length: 39
109 | },
110 | {
111 | start: 150,
112 | end: 213,
113 | length: 64
114 | },
115 | {
116 | start: 226,
117 | end: 227,
118 | length: 2
119 | }
120 | ]
121 | },
122 | rules: [
123 | {
124 | property: 'width',
125 | value: '100px',
126 | parts: [ '100px' ],
127 | position: {
128 | range: {
129 | start: 128,
130 | end: 139,
131 | length: 12
132 | },
133 | start: {
134 | line: 7,
135 | character: 2
136 | },
137 | end: {
138 | line: 7,
139 | character: 13
140 | },
141 | chunks: [
142 | {
143 | start: 128,
144 | end: 139,
145 | length: 12
146 | }
147 | ]
148 | }
149 | },
150 | {
151 | property: 'voice-family',
152 | value: "\"\\\"}\\\"\"",
153 | parts: [ "\"\\\"}\\\"\"" ],
154 | position: {
155 | range: {
156 | start: 152,
157 | end: 172,
158 | length: 21
159 | },
160 | start: {
161 | line: 8,
162 | character: 2
163 | },
164 | end: {
165 | line: 8,
166 | character: 22
167 | },
168 | chunks: [
169 | {
170 | start: 152,
171 | end: 172,
172 | length: 21
173 | }
174 | ]
175 | }
176 | },
177 | {
178 | property: 'voice-family',
179 | value: 'inherit',
180 | parts: [ 'inherit' ],
181 | position: {
182 | range: {
183 | start: 176,
184 | end: 196,
185 | length: 21
186 | },
187 | start: {
188 | line: 9,
189 | character: 2
190 | },
191 | end: {
192 | line: 9,
193 | character: 22
194 | },
195 | chunks: [
196 | {
197 | start: 176,
198 | end: 196,
199 | length: 21
200 | }
201 | ]
202 | }
203 | },
204 | {
205 | property: 'width',
206 | value: '200px',
207 | parts: [ '200px' ],
208 | position: {
209 | range: {
210 | start: 200,
211 | end: 211,
212 | length: 12
213 | },
214 | start: {
215 | line: 10,
216 | character: 2
217 | },
218 | end: {
219 | line: 10,
220 | character: 13
221 | },
222 | chunks: [
223 | {
224 | start: 200,
225 | end: 211,
226 | length: 12
227 | }
228 | ]
229 | }
230 | }
231 | ]
232 | },
233 | {
234 | nested: true,
235 | comment: "/* others */",
236 | position: {
237 | range: {
238 | start: 265,
239 | end: 276,
240 | length: 12
241 | },
242 | start: {
243 | line: 14,
244 | character: 16
245 | },
246 | end: {
247 | line: 14,
248 | character: 27
249 | },
250 | chunks: [
251 | {
252 | start: 265,
253 | end: 276,
254 | length: 12
255 | }
256 | ]
257 | }
258 | },
259 | {
260 | selector: 'html > body #elem',
261 | parts: [
262 | 'html',
263 | '>',
264 | 'body',
265 | '#elem'
266 | ],
267 | position: {
268 | range: {
269 | start: 230,
270 | end: 278,
271 | length: 49
272 | },
273 | start: {
274 | line: 13,
275 | character: 1
276 | },
277 | end: {
278 | line: 15,
279 | character: 1
280 | },
281 | chunks: [
282 | {
283 | start: 230,
284 | end: 264,
285 | length: 35
286 | },
287 | {
288 | start: 277,
289 | end: 278,
290 | length: 2
291 | }
292 | ]
293 | },
294 | rules: [
295 | {
296 | property: 'width',
297 | value: '200px',
298 | parts: [ '200px' ],
299 | position: {
300 | range: {
301 | start: 251,
302 | end: 262,
303 | length: 12
304 | },
305 | start: {
306 | line: 14,
307 | character: 2
308 | },
309 | end: {
310 | line: 14,
311 | character: 13
312 | },
313 | chunks: [
314 | {
315 | start: 251,
316 | end: 262,
317 | length: 12
318 | }
319 | ]
320 | }
321 | }
322 | ]
323 | }
324 | ];
325 |
--------------------------------------------------------------------------------
/test/positions/compressed-media/sheet.css:
--------------------------------------------------------------------------------
1 | @import url(style.css);@media print and (color), projection and (device-aspect-ratio: 16/9){margin: 10px;@page :left{color:black;@bottom-left-corner{margin:2cm}}}
2 |
--------------------------------------------------------------------------------
/test/positions/compressed-media/tree.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = [
3 | {
4 | atrule: "@import url(style.css)",
5 | parts: [ "@import", "url(style.css)" ],
6 | position: {
7 | range: {
8 | start: 0,
9 | end: 21,
10 | length: 22
11 | },
12 | start: {
13 | line: 1,
14 | character: 1
15 | },
16 | end: {
17 | line: 1,
18 | character: 22
19 | },
20 | chunks: [
21 | {
22 | start: 0,
23 | end: 21,
24 | length: 22
25 | }
26 | ]
27 | }
28 | },
29 | {
30 | atrule: "@media print and (color), projection and (device-aspect-ratio: 16/9)",
31 | parts: [ "@media", "print", "and", "(color)", ",", "projection", "and", "(device-aspect-ratio: 16/9)" ],
32 | position: {
33 | range: {
34 | start: 23,
35 | end: 161,
36 | length: 139
37 | },
38 | start: {
39 | line: 1,
40 | character: 24
41 | },
42 | end: {
43 | line: 1,
44 | character: 162
45 | },
46 | chunks: [
47 | {
48 | start: 23,
49 | end: 161,
50 | length: 139
51 | }
52 | ]
53 | },
54 | rules: [
55 | {
56 | property: 'margin',
57 | value: '10px',
58 | parts: [ '10px' ],
59 | position: {
60 | range: {
61 | start: 92,
62 | end: 103,
63 | length: 12
64 | },
65 | start: {
66 | line: 1,
67 | character: 93
68 | },
69 | end: {
70 | line: 1,
71 | character: 104
72 | },
73 | chunks: [
74 | {
75 | start: 92,
76 | end: 103,
77 | length: 12
78 | }
79 | ]
80 | }
81 | }
82 | ],
83 | branches: [
84 | {
85 | atrule: "@page :left",
86 | parts: [ "@page", ":left" ],
87 | position: {
88 | range: {
89 | start: 105,
90 | end: 160,
91 | length: 56
92 | },
93 | start: {
94 | line: 1,
95 | character: 106
96 | },
97 | end: {
98 | line: 1,
99 | character: 161
100 | },
101 | chunks: [
102 | {
103 | start: 105,
104 | end: 160,
105 | length: 56
106 | }
107 | ]
108 | },
109 | rules: [
110 | {
111 | property: 'color',
112 | value: 'black',
113 | parts: [ 'black' ],
114 | position: {
115 | range: {
116 | start: 117,
117 | end: 127,
118 | length: 11
119 | },
120 | start: {
121 | line: 1,
122 | character: 118
123 | },
124 | end: {
125 | line: 1,
126 | character: 128
127 | },
128 | chunks: [
129 | {
130 | start: 117,
131 | end: 127,
132 | length: 11
133 | }
134 | ]
135 | }
136 | }
137 | ],
138 | branches: [
139 | {
140 | atrule: "@bottom-left-corner",
141 | parts: [ "@bottom-left-corner" ],
142 | position: {
143 | range: {
144 | start: 129,
145 | end: 159,
146 | length: 31
147 | },
148 | start: {
149 | line: 1,
150 | character: 130
151 | },
152 | end: {
153 | line: 1,
154 | character: 160
155 | },
156 | chunks: [
157 | {
158 | start: 129,
159 | end: 159,
160 | length: 31
161 | }
162 | ]
163 | },
164 | rules: [
165 | {
166 | property: 'margin',
167 | value: '2cm',
168 | parts: [ '2cm' ],
169 | position: {
170 | range: {
171 | start: 149,
172 | end: 158,
173 | length: 10
174 | },
175 | start: {
176 | line: 1,
177 | character: 150
178 | },
179 | end: {
180 | line: 1,
181 | character: 159
182 | },
183 | chunks: [
184 | {
185 | start: 149,
186 | end: 158,
187 | length: 10
188 | }
189 | ]
190 | }
191 | }
192 | ]
193 | }
194 | ]
195 | }
196 | ]
197 | }
198 | ];
199 |
--------------------------------------------------------------------------------
/test/positions/compressed/sheet.css:
--------------------------------------------------------------------------------
1 | #a{color:black}#b{margin-top:1px;margin-right:1px;margin-bottom:1px;margin-left:1px}
2 |
--------------------------------------------------------------------------------
/test/positions/compressed/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | selector: '#a',
4 | parts: [ '#a' ],
5 | position: {
6 | range: {
7 | start: 0,
8 | end: 14,
9 | length: 15
10 | },
11 | start: {
12 | line: 1,
13 | character: 1
14 | },
15 | end: {
16 | line: 1,
17 | character: 15
18 | },
19 | chunks: [
20 | {
21 | start: 0,
22 | end: 14,
23 | length: 15
24 | }
25 | ]
26 | },
27 | rules: [
28 | {
29 | property: 'color',
30 | value: 'black',
31 | parts: [ 'black' ],
32 | position: {
33 | range: {
34 | start: 3,
35 | end: 13,
36 | length: 11
37 | },
38 | start: {
39 | line: 1,
40 | character: 4
41 | },
42 | end: {
43 | line: 1,
44 | character: 14
45 | },
46 | chunks: [
47 | {
48 | start: 3,
49 | end: 13,
50 | length: 11
51 | }
52 | ]
53 | },
54 | },
55 | ],
56 | },
57 | {
58 | selector: '#b',
59 | parts: [ '#b' ],
60 | position: {
61 | range: {
62 | start: 15,
63 | end: 83,
64 | length: 69
65 | },
66 | start: {
67 | line: 1,
68 | character: 16
69 | },
70 | end: {
71 | line: 1,
72 | character: 84
73 | },
74 | chunks: [
75 | {
76 | start: 15,
77 | end: 83,
78 | length: 69
79 | }
80 | ]
81 | },
82 | rules: [
83 | {
84 | property: 'margin-top',
85 | value: '1px',
86 | parts: [ '1px' ],
87 | position: {
88 | range: {
89 | start: 18,
90 | end: 31,
91 | length: 14
92 | },
93 | start: {
94 | line: 1,
95 | character: 19
96 | },
97 | end: {
98 | line: 1,
99 | character: 32
100 | },
101 | chunks: [
102 | {
103 | start: 18,
104 | end: 31,
105 | length: 14
106 | }
107 | ]
108 | },
109 | },
110 | {
111 | property: 'margin-right',
112 | value: '1px',
113 | parts: [ '1px' ],
114 | position: {
115 | range: {
116 | start: 33,
117 | end: 48,
118 | length: 16
119 | },
120 | start: {
121 | line: 1,
122 | character: 34
123 | },
124 | end: {
125 | line: 1,
126 | character: 49
127 | },
128 | chunks: [
129 | {
130 | start: 33,
131 | end: 48,
132 | length: 16
133 | }
134 | ]
135 | },
136 | },
137 | {
138 | property: 'margin-bottom',
139 | value: '1px',
140 | parts: [ '1px' ],
141 | position: {
142 | range: {
143 | start: 50,
144 | end: 66,
145 | length: 17
146 | },
147 | start: {
148 | line: 1,
149 | character: 51
150 | },
151 | end: {
152 | line: 1,
153 | character: 67
154 | },
155 | chunks: [
156 | {
157 | start: 50,
158 | end: 66,
159 | length: 17
160 | }
161 | ]
162 | },
163 | },
164 | {
165 | property: 'margin-left',
166 | value: '1px',
167 | parts: [ '1px' ],
168 | position: {
169 | range: {
170 | start: 68,
171 | end: 82,
172 | length: 15
173 | },
174 | start: {
175 | line: 1,
176 | character: 69
177 | },
178 | end: {
179 | line: 1,
180 | character: 83
181 | },
182 | chunks: [
183 | {
184 | start: 68,
185 | end: 82,
186 | length: 15
187 | }
188 | ]
189 | },
190 | },
191 | ]
192 | },
193 | ];
194 |
--------------------------------------------------------------------------------
/test/positions/media/sheet.css:
--------------------------------------------------------------------------------
1 | @import url(style.css);
2 |
3 | @media screen {
4 | margin: 20px;
5 | }
6 |
7 | @media print and (color), projection and (device-aspect-ratio: 16/9) {
8 | margin: 10px;
9 | @page :left {
10 | color: black;
11 | @bottom-left-corner {
12 | margin: 2cm;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test/positions/media/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | atrule: "@import url(style.css)",
4 | parts: [ "@import", "url(style.css)" ],
5 | position: {
6 | range: {
7 | start: 0,
8 | end: 21,
9 | length: 22
10 | },
11 | start: {
12 | line: 1,
13 | character: 1
14 | },
15 | end: {
16 | line: 1,
17 | character: 22
18 | },
19 | chunks: [
20 | {
21 | start: 0,
22 | end: 21,
23 | length: 22
24 | }
25 | ]
26 | }
27 | },
28 | {
29 | atrule: "@media screen",
30 | parts: [ "@media", "screen" ],
31 | position: {
32 | range: {
33 | start: 25,
34 | end: 56,
35 | length: 32
36 | },
37 | start: {
38 | line: 3,
39 | character: 1
40 | },
41 | end: {
42 | line: 5,
43 | character: 1
44 | },
45 | chunks: [
46 | {
47 | start: 25,
48 | end: 56,
49 | length: 32
50 | }
51 | ]
52 | },
53 | rules: [
54 | {
55 | property: 'margin',
56 | value: '20px',
57 | parts: [ '20px' ],
58 | position: {
59 | range: {
60 | start: 42,
61 | end: 53,
62 | length: 12
63 | },
64 | start: {
65 | line: 4,
66 | character: 2
67 | },
68 | end: {
69 | line: 4,
70 | character: 13
71 | },
72 | chunks: [
73 | {
74 | start: 42,
75 | end: 53,
76 | length: 12
77 | }
78 | ]
79 | }
80 | }
81 | ]
82 | },
83 | {
84 | atrule: "@media print and (color), projection and (device-aspect-ratio: 16/9)",
85 | parts: [ "@media", "print", "and", "(color)", ",", "projection", "and", "(device-aspect-ratio: 16/9)" ],
86 | position: {
87 | range: {
88 | start: 59,
89 | end: 223,
90 | length: 165
91 | },
92 | start: {
93 | line: 7,
94 | character: 1
95 | },
96 | end: {
97 | line: 15,
98 | character: 1
99 | },
100 | chunks: [
101 | {
102 | start: 59,
103 | end: 223,
104 | length: 165
105 | }
106 | ]
107 | },
108 | rules: [
109 | {
110 | property: 'margin',
111 | value: '10px',
112 | parts: [ '10px' ],
113 | position: {
114 | range: {
115 | start: 131,
116 | end: 142,
117 | length: 12
118 | },
119 | start: {
120 | line: 8,
121 | character: 2
122 | },
123 | end: {
124 | line: 8,
125 | character: 13
126 | },
127 | chunks: [
128 | {
129 | start: 131,
130 | end: 142,
131 | length: 12
132 | }
133 | ]
134 | }
135 | }
136 | ],
137 | branches: [
138 | {
139 | atrule: "@page :left",
140 | parts: [ "@page", ":left" ],
141 | position: {
142 | range: {
143 | start: 146,
144 | end: 221,
145 | length: 76
146 | },
147 | start: {
148 | line: 9,
149 | character: 2
150 | },
151 | end: {
152 | line: 14,
153 | character: 2
154 | },
155 | chunks: [
156 | {
157 | start: 146,
158 | end: 221,
159 | length: 76
160 | }
161 | ]
162 | },
163 | rules: [
164 | {
165 | property: 'color',
166 | value: 'black',
167 | parts: [ 'black' ],
168 | position: {
169 | range: {
170 | start: 162,
171 | end: 173,
172 | length: 12
173 | },
174 | start: {
175 | line: 10,
176 | character: 3
177 | },
178 | end: {
179 | line: 10,
180 | character: 14
181 | },
182 | chunks: [
183 | {
184 | start: 162,
185 | end: 173,
186 | length: 12
187 | }
188 | ]
189 | }
190 | }
191 | ],
192 | branches: [
193 | {
194 | atrule: "@bottom-left-corner",
195 | parts: [ "@bottom-left-corner" ],
196 | position: {
197 | range: {
198 | start: 178,
199 | end: 218,
200 | length: 41
201 | },
202 | start: {
203 | line: 11,
204 | character: 3
205 | },
206 | end: {
207 | line: 13,
208 | character: 3
209 | },
210 | chunks: [
211 | {
212 | start: 178,
213 | end: 218,
214 | length: 41
215 | }
216 | ]
217 | },
218 | rules: [
219 | {
220 | property: 'margin',
221 | value: '2cm',
222 | parts: [ '2cm' ],
223 | position: {
224 | range: {
225 | start: 203,
226 | end: 213,
227 | length: 11
228 | },
229 | start: {
230 | line: 12,
231 | character: 4
232 | },
233 | end: {
234 | line: 12,
235 | character: 14
236 | },
237 | chunks: [
238 | {
239 | start: 203,
240 | end: 213,
241 | length: 11
242 | }
243 | ]
244 | }
245 | }
246 | ]
247 | }
248 | ]
249 | }
250 | ]
251 | }
252 | ];
253 |
--------------------------------------------------------------------------------
/test/positions/nested-comments/sheet.css:
--------------------------------------------------------------------------------
1 | #elem span.color span {
2 | width/*prop*/:/*value*/100px; /*rules*/
3 | }
4 |
5 | @media screen {
6 | /* first level */
7 | margin: 20px;
8 |
9 | b {
10 | /* second level */
11 | color/*nested prop*/:/*nested value*/red;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/positions/nested-comments/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | nested: true,
4 | comment: "/*prop*/",
5 | position: {
6 | range: {
7 | start: 30,
8 | end: 37,
9 | length: 8
10 | },
11 | start: {
12 | line: 2,
13 | character: 7
14 | },
15 | end: {
16 | line: 2,
17 | character: 14
18 | },
19 | chunks: [
20 | {
21 | start: 30,
22 | end: 37,
23 | length: 8
24 | }
25 | ]
26 | }
27 | },
28 | {
29 | nested: true,
30 | comment: "/*value*/",
31 | position: {
32 | range: {
33 | start: 39,
34 | end: 47,
35 | length: 9
36 | },
37 | start: {
38 | line: 2,
39 | character: 16
40 | },
41 | end: {
42 | line: 2,
43 | character: 24
44 | },
45 | chunks: [
46 | {
47 | start: 39,
48 | end: 47,
49 | length: 9
50 | }
51 | ]
52 | }
53 | },
54 | {
55 | nested: true,
56 | comment: "/*rules*/",
57 | position: {
58 | range: {
59 | start: 55,
60 | end: 63,
61 | length: 9
62 | },
63 | start: {
64 | line: 2,
65 | character: 32
66 | },
67 | end: {
68 | line: 2,
69 | character: 40
70 | },
71 | chunks: [
72 | {
73 | start: 55,
74 | end: 63,
75 | length: 9
76 | }
77 | ]
78 | }
79 | },
80 | {
81 | selector: '#elem span.color span',
82 | parts: [
83 | '#elem',
84 | [
85 | 'span',
86 | '.color'
87 | ],
88 | 'span'
89 | ],
90 | position: {
91 | range: {
92 | start: 0,
93 | end: 65,
94 | length: 66
95 | },
96 | start: {
97 | line: 1,
98 | character: 1
99 | },
100 | end: {
101 | line: 3,
102 | character: 1
103 | },
104 | chunks: [
105 | {
106 | start: 0,
107 | end: 29,
108 | length: 30
109 | },
110 | {
111 | start: 38,
112 | end: 38,
113 | length: 1
114 | },
115 | {
116 | start: 48,
117 | end: 54,
118 | length: 7
119 | },
120 | {
121 | start: 64,
122 | end: 65,
123 | length: 2
124 | }
125 | ]
126 | },
127 | rules: [
128 | {
129 | property: 'width',
130 | value: '100px',
131 | parts: [ '100px' ],
132 | position: {
133 | range: {
134 | start: 25,
135 | end: 52,
136 | length: 28
137 | },
138 | start: {
139 | line: 2,
140 | character: 2
141 | },
142 | end: {
143 | line: 2,
144 | character: 29
145 | },
146 | chunks: [
147 | {
148 | start: 25,
149 | end: 29,
150 | length: 5
151 | },
152 | {
153 | start: 38,
154 | end: 38,
155 | length: 1
156 | },
157 | {
158 | start: 48,
159 | end: 52,
160 | length: 5
161 | },
162 | ]
163 | }
164 | },
165 | ]
166 | },
167 | {
168 | nested: true,
169 | comment: "/* first level */",
170 | position: {
171 | range: {
172 | start: 85,
173 | end: 101,
174 | length: 17
175 | },
176 | start: {
177 | line: 6,
178 | character: 2
179 | },
180 | end: {
181 | line: 6,
182 | character: 18
183 | },
184 | chunks: [
185 | {
186 | start: 85,
187 | end: 101,
188 | length: 17
189 | }
190 | ]
191 | }
192 | },
193 | {
194 | nested: true,
195 | comment: "/* second level */",
196 | position: {
197 | range: {
198 | start: 126,
199 | end: 143,
200 | length: 18
201 | },
202 | start: {
203 | line: 10,
204 | character: 3
205 | },
206 | end: {
207 | line: 10,
208 | character: 20
209 | },
210 | chunks: [
211 | {
212 | start: 126,
213 | end: 143,
214 | length: 18
215 | }
216 | ]
217 | }
218 | },
219 | {
220 | nested: true,
221 | comment: "/*nested prop*/",
222 | position: {
223 | range: {
224 | start: 152,
225 | end: 166,
226 | length: 15
227 | },
228 | start: {
229 | line: 11,
230 | character: 8
231 | },
232 | end: {
233 | line: 11,
234 | character: 22
235 | },
236 | chunks: [
237 | {
238 | start: 152,
239 | end: 166,
240 | length: 15
241 | }
242 | ]
243 | }
244 | },
245 | {
246 | nested: true,
247 | comment: "/*nested value*/",
248 | position: {
249 | range: {
250 | start: 168,
251 | end: 183,
252 | length: 16
253 | },
254 | start: {
255 | line: 11,
256 | character: 24
257 | },
258 | end: {
259 | line: 11,
260 | character: 39
261 | },
262 | chunks: [
263 | {
264 | start: 168,
265 | end: 183,
266 | length: 16
267 | }
268 | ]
269 | }
270 | },
271 | {
272 | atrule: '@media screen',
273 | parts: [
274 | '@media',
275 | 'screen',
276 | ],
277 | position: {
278 | range: {
279 | start: 68,
280 | end: 192,
281 | length: 125
282 | },
283 | start: {
284 | line: 5,
285 | character: 1
286 | },
287 | end: {
288 | line: 13,
289 | character: 1
290 | },
291 | chunks: [
292 | {
293 | start: 68,
294 | end: 84,
295 | length: 17
296 | },
297 | {
298 | start: 101,
299 | end: 125,
300 | length: 25
301 | },
302 | {
303 | start: 143,
304 | end: 151,
305 | length: 9
306 | },
307 | {
308 | start: 166,
309 | end: 167,
310 | length: 2
311 | },
312 | {
313 | start: 183,
314 | end: 192,
315 | length: 10
316 | },
317 | ]
318 | },
319 | rules: [
320 | {
321 | property: 'margin',
322 | value: '20px',
323 | parts: [ '20px' ],
324 | position: {
325 | range: {
326 | start: 104,
327 | end: 115,
328 | length: 12
329 | },
330 | start: {
331 | line: 7,
332 | character: 2
333 | },
334 | end: {
335 | line: 7,
336 | character: 13
337 | },
338 | chunks: [
339 | {
340 | start: 104,
341 | end: 115,
342 | length: 12
343 | }
344 | ]
345 | }
346 | }
347 | ],
348 | branches: [
349 | {
350 | selector: 'b',
351 | parts: [
352 | 'b',
353 | ],
354 | position: {
355 | range: {
356 | start: 120,
357 | end: 190,
358 | length: 71
359 | },
360 | start: {
361 | line: 9,
362 | character: 2
363 | },
364 | end: {
365 | line: 12,
366 | character: 2
367 | },
368 | chunks: [
369 | {
370 | start: 120,
371 | end: 125,
372 | length: 6
373 | },
374 | {
375 | start: 143,
376 | end: 151,
377 | length: 9
378 | },
379 | {
380 | start: 166,
381 | end: 167,
382 | length: 2
383 | },
384 | {
385 | start: 183,
386 | end: 190,
387 | length: 8
388 | },
389 | ]
390 | },
391 | rules: [
392 | {
393 | property: 'color',
394 | value: 'red',
395 | parts: [ 'red' ],
396 | position: {
397 | range: {
398 | start: 147,
399 | end: 186,
400 | length: 40
401 | },
402 | start: {
403 | line: 11,
404 | character: 3
405 | },
406 | end: {
407 | line: 11,
408 | character: 42
409 | },
410 | chunks: [
411 | {
412 | start: 147,
413 | end: 186,
414 | length: 40
415 | }
416 | ]
417 | }
418 | }
419 | ]
420 | }
421 | ]
422 | }
423 | ];
424 |
--------------------------------------------------------------------------------
/test/sheets/box-model/sheet.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Thanks to yuicompressor for this test case: http://developer.yahoo.com/yui/compressor/css.html
4 |
5 | */
6 | #elem span.color span {
7 | width: 100px; /* IE */
8 | voice-family: "\"}\"";
9 | voice-family:inherit;
10 | width: 200px; /* others */
11 | }
12 |
13 | html > body #elem {
14 | width: 200px; /* others */
15 | }
16 |
--------------------------------------------------------------------------------
/test/sheets/box-model/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | nested: false,
4 | comment: "/*\n\nThanks to yuicompressor for this test case: http://developer.yahoo.com/yui/compressor/css.html\n\n*/"
5 | },
6 | {
7 | nested: true,
8 | comment: "/* IE */"
9 | },
10 | {
11 | nested: true,
12 | comment: "/* others */"
13 | },
14 | {
15 | selector: '#elem span.color span',
16 | parts: [
17 | '#elem',
18 | [
19 | 'span',
20 | '.color'
21 | ],
22 | 'span'
23 | ],
24 | rules: [
25 | {
26 | property: 'width',
27 | value: '100px',
28 | parts: [ '100px' ]
29 | },
30 | {
31 | property: 'voice-family',
32 | value: "\"\\\"}\\\"\"",
33 | parts: [ "\"\\\"}\\\"\"" ]
34 | },
35 | {
36 | property: 'voice-family',
37 | value: 'inherit',
38 | parts: [ 'inherit' ]
39 | },
40 | {
41 | property: 'width',
42 | value: '200px',
43 | parts: [ '200px' ]
44 | }
45 | ]
46 | },
47 | {
48 | nested: true,
49 | comment: "/* others */"
50 | },
51 | {
52 | selector: 'html > body #elem',
53 | parts: [
54 | 'html',
55 | '>',
56 | 'body',
57 | '#elem'
58 | ],
59 | rules: [
60 | {
61 | property: 'width',
62 | value: '200px',
63 | parts: [ '200px' ]
64 | }
65 | ]
66 | }
67 | ];
68 |
--------------------------------------------------------------------------------
/test/sheets/comment-ie5-mac/sheet.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-size: 12pt;
3 | /* ie5 mac comment hack \*/
4 | font-size: 5pt;
5 | }
6 |
--------------------------------------------------------------------------------
/test/sheets/comment-ie5-mac/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | nested: true,
4 | comment: "/* ie5 mac comment hack \\*/"
5 | },
6 | {
7 | selector: 'body',
8 | parts: [
9 | 'body'
10 | ],
11 | rules: [
12 | {
13 | property: 'font-size',
14 | value: '12pt',
15 | parts: [ '12pt' ]
16 | },
17 | {
18 | property: 'font-size',
19 | value: '5pt',
20 | parts: [ '5pt' ]
21 | }
22 | ]
23 | }
24 | ];
25 |
--------------------------------------------------------------------------------
/test/sheets/complex-properties/sheet.css:
--------------------------------------------------------------------------------
1 | #red {
2 | border-radius: 1px 2px 3px 4px/5px 6px 7px 8px;
3 | border-radius: 1px 2px 3px 4px / 5px 6px 7px 8px;
4 | box-shadow: inset 3px 2px 6px,3px 1px 10px;
5 | box-shadow: inset 3px 2px 6px, 3px 1px 10px;
6 | }
7 |
--------------------------------------------------------------------------------
/test/sheets/complex-properties/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | selector: "#red",
4 | parts: [
5 | '#red'
6 | ],
7 | rules: [
8 | {
9 | property: 'border-radius',
10 | value: '1px 2px 3px 4px/5px 6px 7px 8px',
11 | parts: [
12 | '1px',
13 | '2px',
14 | '3px',
15 | '4px',
16 | '/',
17 | '5px',
18 | '6px',
19 | '7px',
20 | '8px'
21 | ]
22 | },
23 | {
24 | property: 'border-radius',
25 | value: '1px 2px 3px 4px / 5px 6px 7px 8px',
26 | parts: [
27 | '1px',
28 | '2px',
29 | '3px',
30 | '4px',
31 | '/',
32 | '5px',
33 | '6px',
34 | '7px',
35 | '8px'
36 | ]
37 | },
38 | {
39 | property: 'box-shadow',
40 | value: 'inset 3px 2px 6px,3px 1px 10px',
41 | parts: [
42 | 'inset',
43 | '3px',
44 | '2px',
45 | '6px',
46 | ',',
47 | '3px',
48 | '1px',
49 | '10px'
50 | ]
51 | },
52 | {
53 | property: 'box-shadow',
54 | value: 'inset 3px 2px 6px, 3px 1px 10px',
55 | parts: [
56 | 'inset',
57 | '3px',
58 | '2px',
59 | '6px',
60 | ',',
61 | '3px',
62 | '1px',
63 | '10px'
64 | ]
65 | }
66 | ]
67 | }
68 | ];
69 |
--------------------------------------------------------------------------------
/test/sheets/complex-selector/sheet.css:
--------------------------------------------------------------------------------
1 | #test span.hello,
2 | #test2 span.hello[id='yes'] ~ div
3 | {
4 | color: black;
5 | }
6 |
7 | span.a.b.c.d {
8 | color: black;
9 | }
10 |
11 | body > div[title='spacing test\'s'] + a[href$='nospace'] :first-child {
12 | color: black;
13 | }
14 |
15 | body > div.a.b[title='spacing test\'s'].c.d + a.a.b[href$="space ]"].c.d :first-child {
16 | color: black;
17 | }
18 |
--------------------------------------------------------------------------------
/test/sheets/complex-selector/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | selector: "#test span.hello,\n#test2 span.hello[id='yes'] ~ div",
4 | parts: [
5 | "#test",
6 | [
7 | "span",
8 | ".hello"
9 | ],
10 | ",",
11 | "#test2",
12 | [
13 | "span",
14 | ".hello",
15 | "[id='yes']"
16 | ],
17 | "~",
18 | "div"
19 | ],
20 | rules: [
21 | {
22 | property: 'color',
23 | value: 'black',
24 | parts: [ 'black' ]
25 | }
26 | ]
27 | },
28 | {
29 | selector: "span.a.b.c.d",
30 | parts: [
31 | [
32 | "span",
33 | ".a",
34 | ".b",
35 | ".c",
36 | ".d"
37 | ]
38 | ],
39 | rules: [
40 | {
41 | property: 'color',
42 | value: 'black',
43 | parts: [ 'black' ]
44 | }
45 | ]
46 | },
47 | {
48 | selector: "body > div[title='spacing test\\'s'] + a[href$='nospace'] :first-child",
49 | parts: [
50 | "body",
51 | ">",
52 | [
53 | "div",
54 | "[title='spacing test\\'s']"
55 | ],
56 | "+",
57 | [
58 | "a",
59 | "[href$='nospace']"
60 | ],
61 | ":first-child"
62 | ],
63 | rules: [
64 | {
65 | property: 'color',
66 | value: 'black',
67 | parts: [ 'black' ]
68 | }
69 | ]
70 | },
71 | {
72 | selector: "body > div.a.b[title='spacing test\\'s'].c.d + a.a.b[href$=\"space ]\"].c.d :first-child",
73 | parts: [
74 | "body",
75 | ">",
76 | [
77 | "div",
78 | ".a",
79 | ".b",
80 | "[title='spacing test\\'s']",
81 | ".c",
82 | ".d"
83 | ],
84 | "+",
85 | [
86 | "a",
87 | ".a",
88 | ".b",
89 | "[href$=\"space ]\"]",
90 | ".c",
91 | ".d"
92 | ],
93 | ":first-child"
94 | ],
95 | rules: [
96 | {
97 | property: 'color',
98 | value: 'black',
99 | parts: [ 'black' ]
100 | }
101 | ]
102 | }
103 | ];
104 |
--------------------------------------------------------------------------------
/test/sheets/escapables/sheet.css:
--------------------------------------------------------------------------------
1 | #one {
2 | background: transparent url(http://www.example.com/someimage1.png?add=semi;) no-repeat top center;
3 | }
4 |
--------------------------------------------------------------------------------
/test/sheets/escapables/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | selector: '#one',
4 | parts: [
5 | '#one'
6 | ],
7 | rules: [
8 | {
9 | property: 'background',
10 | value: 'transparent url(http://www.example.com/someimage1.png?add=semi;) no-repeat top center',
11 | parts: [
12 | 'transparent',
13 | 'url(http://www.example.com/someimage1.png?add=semi;)',
14 | 'no-repeat',
15 | 'top',
16 | 'center'
17 | ]
18 | }
19 | ]
20 | }
21 | ];
22 |
--------------------------------------------------------------------------------
/test/sheets/imports/sheet.css:
--------------------------------------------------------------------------------
1 | @import url("crazyurl.css?semi=yes;&email=corey@codenothing.com");
2 | @import url('crazyurl.css?semi=yes;&email=corey@codenothing.com');
3 | @import url(crazyurl.css?semi=yes;&email=corey@codenothing.com);
4 | @import url(crazyurl.css?semi=yes;&email=corey@codenothing.com)
5 |
--------------------------------------------------------------------------------
/test/sheets/imports/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | atrule: "@import url(\"crazyurl.css?semi=yes;&email=corey@codenothing.com\")",
4 | parts: [
5 | "@import",
6 | "url(\"crazyurl.css?semi=yes;&email=corey@codenothing.com\")"
7 | ]
8 | },
9 | {
10 | atrule: "@import url('crazyurl.css?semi=yes;&email=corey@codenothing.com')",
11 | parts: [
12 | "@import",
13 | "url('crazyurl.css?semi=yes;&email=corey@codenothing.com')"
14 | ]
15 | },
16 | {
17 | atrule: "@import url(crazyurl.css?semi=yes;&email=corey@codenothing.com)",
18 | parts: [
19 | "@import",
20 | "url(crazyurl.css?semi=yes;&email=corey@codenothing.com)"
21 | ]
22 | },
23 | {
24 | atrule: "@import url(crazyurl.css?semi=yes;&email=corey@codenothing.com)",
25 | parts: [
26 | "@import",
27 | "url(crazyurl.css?semi=yes;&email=corey@codenothing.com)"
28 | ]
29 | }
30 | ];
31 |
--------------------------------------------------------------------------------
/test/sheets/media-empty/sheet.css:
--------------------------------------------------------------------------------
1 | /*! preserved */
2 | emptiness {}
3 |
4 | @import "another.css";
5 | /* I'm empty - delete me */
6 | empty { ;}
7 |
8 | @media print {
9 | .noprint { display: none; }
10 | }
11 |
12 | @media screen {
13 | /* this rule should be removed, not simply minified.*/
14 | .breakme {}
15 | .printonly { display: none; }
16 | }
17 |
--------------------------------------------------------------------------------
/test/sheets/media-empty/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | nested: false,
4 | comment: "/*! preserved */"
5 | },
6 | {
7 | selector: 'emptiness',
8 | parts: [
9 | 'emptiness'
10 | ],
11 | rules: []
12 | },
13 | {
14 | atrule: '@import "another.css"',
15 | parts: [ '@import', '"another.css"' ]
16 | },
17 | {
18 | nested: false,
19 | comment: "/* I'm empty - delete me */"
20 | },
21 | {
22 | selector: 'empty',
23 | parts: [
24 | 'empty'
25 | ],
26 | rules: []
27 | },
28 | {
29 | atrule: '@media print',
30 | parts: [ '@media', 'print' ],
31 | branches: [
32 | {
33 | selector: '.noprint',
34 | parts: [ '.noprint' ],
35 | rules: [
36 | {
37 | property: 'display',
38 | value: 'none',
39 | parts: [ 'none' ]
40 | }
41 | ]
42 | }
43 | ]
44 | },
45 | {
46 | nested: true,
47 | comment: "/* this rule should be removed, not simply minified.*/"
48 | },
49 | {
50 | atrule: '@media screen',
51 | parts: [ '@media', 'screen' ],
52 | branches: [
53 | {
54 | selector: '.breakme',
55 | parts: [ '.breakme' ],
56 | rules: []
57 | },
58 | {
59 | selector: '.printonly',
60 | parts: [ '.printonly' ],
61 | rules: [
62 | {
63 | property: 'display',
64 | value: 'none',
65 | parts: [ 'none' ]
66 | }
67 | ]
68 | }
69 | ]
70 | }
71 | ];
72 |
--------------------------------------------------------------------------------
/test/sheets/media/sheet.css:
--------------------------------------------------------------------------------
1 | @media screen {
2 | margin: 20px;
3 | }
4 |
5 | @media print and (color), projection and (device-aspect-ratio: 16/9) {
6 | margin: 10px;
7 | @page :left {
8 | color: black;
9 | @bottom-left-corner {
10 | margin: 2cm;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/sheets/media/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | atrule: '@media screen',
4 | parts: [ '@media', 'screen' ],
5 | rules: [
6 | {
7 | property: 'margin',
8 | value: '20px',
9 | parts: [ '20px' ]
10 | }
11 | ]
12 | },
13 | {
14 | atrule: '@media print and (color), projection and (device-aspect-ratio: 16/9)',
15 | parts: [ '@media', 'print', 'and', '(color)', ',', 'projection', 'and', '(device-aspect-ratio: 16/9)' ],
16 | rules: [
17 | {
18 | property: 'margin',
19 | value: '10px',
20 | parts: [ '10px' ]
21 | }
22 | ],
23 | branches: [
24 | {
25 | atrule: '@page :left',
26 | parts: [ '@page', ':left' ],
27 | rules: [
28 | {
29 | property: 'color',
30 | value: 'black',
31 | parts: [ 'black' ]
32 | }
33 | ],
34 | branches: [
35 | {
36 | atrule: '@bottom-left-corner',
37 | parts: [ '@bottom-left-corner' ],
38 | rules: [
39 | {
40 | property: 'margin',
41 | value: '2cm',
42 | parts: [ '2cm' ]
43 | }
44 | ]
45 | }
46 | ]
47 | }
48 | ]
49 | }
50 | ];
51 |
--------------------------------------------------------------------------------
/test/sheets/nested-escape/sheet.css:
--------------------------------------------------------------------------------
1 | @media screen {
2 | body {
3 | content: \ntest;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/sheets/nested-escape/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | atrule: '@media screen',
4 | parts: [ '@media', 'screen' ],
5 | branches: [
6 | {
7 | selector: 'body',
8 | parts: [ 'body' ],
9 | rules: [
10 | {
11 | property: 'content',
12 | value: "\\ntest",
13 | parts: [ "\\ntest" ]
14 | }
15 | ]
16 | }
17 | ]
18 | }
19 | ];
20 |
--------------------------------------------------------------------------------
/test/sheets/opacity-hell/sheet.css:
--------------------------------------------------------------------------------
1 | /* example from https://developer.mozilla.org/en/CSS/opacity */
2 | pre {
3 | border: solid red;
4 | opacity: 0.8;
5 | -ms-filter: " progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
6 | filter: PROGID:DXImageTransform.Microsoft.Alpha(Opacity=80);
7 | zoom: 1;
8 | }
9 |
10 | code {
11 | -ms-filter: " PROGID:DXImageTransform.Microsoft.Alpha(Opacity=80)";
12 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80 ) ;
13 | }
14 |
--------------------------------------------------------------------------------
/test/sheets/opacity-hell/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | nested: false,
4 | comment: "/* example from https://developer.mozilla.org/en/CSS/opacity */"
5 | },
6 | {
7 | selector: 'pre',
8 | parts: [
9 | 'pre'
10 | ],
11 | rules: [
12 | {
13 | property: "border",
14 | value: "solid red",
15 | parts: [
16 | 'solid',
17 | 'red'
18 | ]
19 | },
20 | {
21 | property: "opacity",
22 | value: "0.8",
23 | parts: [ "0.8" ]
24 | },
25 | {
26 | property: "-ms-filter",
27 | value: '" progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"',
28 | parts: [ '" progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"' ]
29 | },
30 | {
31 | property: "filter",
32 | value: "PROGID:DXImageTransform.Microsoft.Alpha(Opacity=80)",
33 | parts: [ "PROGID:DXImageTransform.Microsoft.Alpha(Opacity=80)" ]
34 | },
35 | {
36 | property: "zoom",
37 | value: "1",
38 | parts: [ "1" ]
39 | }
40 | ]
41 | },
42 | {
43 | selector: 'code',
44 | parts: [
45 | 'code'
46 | ],
47 | rules: [
48 | {
49 | property: '-ms-filter',
50 | value: '" PROGID:DXImageTransform.Microsoft.Alpha(Opacity=80)"',
51 | parts: [ '" PROGID:DXImageTransform.Microsoft.Alpha(Opacity=80)"' ]
52 | },
53 | {
54 | property: 'filter',
55 | value: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=80 )',
56 | parts: [ 'progid:DXImageTransform.Microsoft.Alpha(Opacity=80 )' ]
57 | }
58 | ]
59 | }
60 | ];
61 |
--------------------------------------------------------------------------------
/test/sheets/preserve-newline/sheet.css:
--------------------------------------------------------------------------------
1 | #sel-o {
2 | content: "on\"ce upon \
3 | a time";
4 | content: "once upon \
5 | a ti\'me";
6 | }
7 |
--------------------------------------------------------------------------------
/test/sheets/preserve-newline/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | selector: '#sel-o',
4 | parts: [
5 | '#sel-o'
6 | ],
7 | rules: [
8 | {
9 | property: 'content',
10 | value: "\"on\\\"ce upon \\\na time\"",
11 | parts: [ "\"on\\\"ce upon \\\na time\"" ]
12 | },
13 | {
14 | property: 'content',
15 | value: "\"once upon \\\na ti\\'me\"",
16 | parts: [ "\"once upon \\\na ti\\'me\"" ]
17 | }
18 | ]
19 | }
20 | ];
21 |
--------------------------------------------------------------------------------
/test/sheets/preserve-string/sheet.css:
--------------------------------------------------------------------------------
1 | .sele {
2 | content: "\"keep \" me";
3 | something: '\\\' . . ';
4 | else: 'empty{}';
5 | content: "/* test */"; /* <---- this is not a comment, should be be kept */
6 | }
7 |
--------------------------------------------------------------------------------
/test/sheets/preserve-string/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | nested: true,
4 | comment: "/* <---- this is not a comment, should be be kept */"
5 | },
6 | {
7 | selector: '.sele',
8 | parts: [
9 | '.sele'
10 | ],
11 | rules: [
12 | {
13 | property: 'content',
14 | value: "\"\\\"keep \\\" me\"",
15 | parts: [ "\"\\\"keep \\\" me\"" ]
16 | },
17 | {
18 | property: 'something',
19 | value: "'\\\\\\' . . '",
20 | parts: [ "'\\\\\\' . . '" ]
21 | },
22 | {
23 | property: 'else',
24 | value: "'empty{}'",
25 | parts: [ "'empty{}'" ]
26 | },
27 | {
28 | property: 'content',
29 | value: "\"/* test */\"",
30 | parts: [ "\"/* test */\"" ]
31 | }
32 | ]
33 | }
34 | ];
35 |
--------------------------------------------------------------------------------
/test/sheets/semi-colons/sheet.css:
--------------------------------------------------------------------------------
1 | p :first-line {
2 | ba:zinga;;;
3 | foo: bar;;;
4 | }
5 |
--------------------------------------------------------------------------------
/test/sheets/semi-colons/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | selector: 'p :first-line',
4 | parts: [
5 | 'p',
6 | ':first-line'
7 | ],
8 | rules: [
9 | {
10 | property: 'ba',
11 | value: 'zinga',
12 | parts: [ 'zinga' ]
13 | },
14 | {
15 | property: 'foo',
16 | value: 'bar',
17 | parts: [ 'bar' ]
18 | }
19 | ]
20 | }
21 | ];
22 |
--------------------------------------------------------------------------------
/test/sheets/simple/sheet.css:
--------------------------------------------------------------------------------
1 | #test {
2 | color: black;
3 | font-size: 20px;
4 | background: red url(http://www.example.com/someimg.png);
5 | }
6 |
7 | /* Comment Block */
8 | #next {
9 | position: absolute;
10 | margin: 10px 11px 12px 0;
11 | padding: 1px 2px 3px;
12 | }
13 |
14 | /*
15 | Multi Line Comment
16 | */
17 |
18 | @import 'sheet.css';
19 | @charset 'utf-8';
20 |
--------------------------------------------------------------------------------
/test/sheets/simple/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | selector: '#test',
4 | parts: [
5 | '#test'
6 | ],
7 | rules: [
8 | {
9 | property: 'color',
10 | value: 'black',
11 | parts: [ 'black' ]
12 | },
13 | {
14 | property: 'font-size',
15 | value: '20px',
16 | parts: [ '20px' ]
17 | },
18 | {
19 | property: 'background',
20 | value: 'red url(http://www.example.com/someimg.png)',
21 | parts: [
22 | 'red',
23 | 'url(http://www.example.com/someimg.png)'
24 | ]
25 | }
26 | ]
27 | },
28 | {
29 | comment: '/* Comment Block */',
30 | nested: false
31 | },
32 | {
33 | selector: '#next',
34 | parts: [
35 | '#next'
36 | ],
37 | rules: [
38 | {
39 | property: 'position',
40 | value: 'absolute',
41 | parts: [ 'absolute' ]
42 | },
43 | {
44 | property: 'margin',
45 | value: '10px 11px 12px 0',
46 | parts: [
47 | '10px',
48 | '11px',
49 | '12px',
50 | '0'
51 | ]
52 | },
53 | {
54 | property: 'padding',
55 | value: '1px 2px 3px',
56 | parts: [
57 | '1px',
58 | '2px',
59 | '3px'
60 | ]
61 | }
62 | ]
63 | },
64 | {
65 | comment: "/*\nMulti Line Comment\n*/",
66 | nested: false
67 | },
68 | {
69 | atrule: "@import 'sheet.css'",
70 | parts: [ "@import", "'sheet.css'" ]
71 | },
72 | {
73 | atrule: "@charset 'utf-8'",
74 | parts: [ "@charset", "'utf-8'" ]
75 | }
76 | ];
77 |
--------------------------------------------------------------------------------
/test/sheets/star-underscore/sheet.css:
--------------------------------------------------------------------------------
1 | #elementarr {
2 | width: 1px;
3 | *width: 3pt;
4 | _width: 2em;
5 | }
6 |
--------------------------------------------------------------------------------
/test/sheets/star-underscore/tree.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | selector: '#elementarr',
4 | parts: [
5 | '#elementarr'
6 | ],
7 | rules: [
8 | {
9 | property: 'width',
10 | value: '1px',
11 | parts: [ '1px' ]
12 | },
13 | {
14 | property: '*width',
15 | value: '3pt',
16 | parts: [ '3pt' ]
17 | },
18 | {
19 | property: '_width',
20 | value: '2em',
21 | parts: [ '2em' ]
22 | }
23 | ]
24 | }
25 | ];
26 |
--------------------------------------------------------------------------------
/test/test-AtRule.js:
--------------------------------------------------------------------------------
1 | var munit = global.munit,
2 | CSSTree = global.CSSTree;
3 |
4 | munit( 'AtRule', {
5 |
6 | // Initialization
7 | init: function( assert ) {
8 | var position = new CSSTree.Position( 25 ),
9 | atrule = new CSSTree.AtRule( " @import 'test.css' ", position );
10 |
11 | assert.equal( 'atrule', atrule.atrule, "@import 'test.css'" );
12 | assert.equal( 'position', atrule.position, position );
13 | assert.equal( 'rules', atrule.rules, undefined );
14 | assert.equal( 'branches', atrule.branches, undefined );
15 | assert.deepEqual( 'parts', atrule.parts, [ "@import", "'test.css'" ] );
16 | },
17 |
18 | // Testing string breakdown handle
19 | breakdown: function( assert ) {
20 | var atrule = new CSSTree.AtRule();
21 |
22 | [
23 |
24 | {
25 | name: 'Basic',
26 | atrule: "@import 'test.css'",
27 | parts: [
28 | "@import",
29 | "'test.css'"
30 | ]
31 | },
32 |
33 | {
34 | name: 'Extendend',
35 | atrule: '@media print and (color), projection and (device-aspect-ratio: 16/9)',
36 | parts: [
37 | '@media',
38 | 'print',
39 | 'and',
40 | '(color)',
41 | ',',
42 | 'projection',
43 | 'and',
44 | '(device-aspect-ratio: 16/9)'
45 | ]
46 | },
47 |
48 | {
49 | name: 'URL Import',
50 | atrule: "@import url(\"crazyurl.css?semi=yes;&email=corey@codenothing.com\")",
51 | parts: [
52 | "@import",
53 | "url(\"crazyurl.css?semi=yes;&email=corey@codenothing.com\")"
54 | ]
55 | },
56 |
57 | {
58 | name: 'Escaped Quotes',
59 | atrule: "@import url('crazyurl.css?semi=yes;&email=core\\'y@codenothing.com')",
60 | parts: [
61 | "@import",
62 | "url('crazyurl.css?semi=yes;&email=core\\'y@codenothing.com')"
63 | ]
64 | }
65 |
66 | ].forEach(function( object ) {
67 | atrule.atrule = object.atrule;
68 | atrule.breakdown();
69 | assert.deepEqual( object.name, atrule.parts, object.parts );
70 | });
71 | },
72 |
73 | });
74 |
--------------------------------------------------------------------------------
/test/test-CSSTree.js:
--------------------------------------------------------------------------------
1 | var munit = global.munit,
2 | CSSTree = global.CSSTree;
3 |
4 | // Put core functionality at the highest priority
5 | munit( 'CSSTree', { priority: 1.0 }, {
6 |
7 | // Add basic existance tests to ensure api stays consistent through versions
8 | // THESE TESTS SHOULD NOT CHANGE
9 | init: function( assert ) {
10 | var css = "body { color: red; }",
11 | tree = new CSSTree( css );
12 |
13 | assert.exists( 'iter', tree.iter )
14 | .equal( 'css', tree.css, css )
15 | .isArray( 'branches', tree.branches );
16 | },
17 |
18 | // Same concept, make sure sub objects stay consistent through versions
19 | // THESE TESTS SHOULD NOT CHANGE
20 | 'static': function( assert ) {
21 | assert.isFunction( 'Position', CSSTree.Position )
22 | .isFunction( 'Selector', CSSTree.Selector )
23 | .isFunction( 'Comment', CSSTree.Comment )
24 | .isFunction( 'AtRule', CSSTree.AtRule )
25 | .isFunction( 'Rule', CSSTree.Rule );
26 | },
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/test/test-Comment.js:
--------------------------------------------------------------------------------
1 | var munit = global.munit,
2 | CSSTree = global.CSSTree;
3 |
4 | munit( 'Comment.init', function( assert ) {
5 | var position = new CSSTree.Position( 25 ),
6 | comment = new CSSTree.Comment( " /* Test Comment */ ", null, position );
7 |
8 | assert.equal( 'comment', comment.comment, "/* Test Comment */" );
9 | assert.equal( 'nested', comment.nested, false );
10 | assert.equal( 'position', comment.position, position );
11 | });
12 |
--------------------------------------------------------------------------------
/test/test-Position.js:
--------------------------------------------------------------------------------
1 | var munit = global.munit,
2 | CSSTree = global.CSSTree;
3 |
4 | munit( 'Position.init', function( assert ) {
5 | var position = new CSSTree.Position( 25 );
6 |
7 | assert.deepEqual( 'range', position.range, { start: 25, end: 25, length: 0 } );
8 | assert.deepEqual( 'start', position.start, { line: 0, character: 0 } );
9 | assert.deepEqual( 'end', position.end, { line: 0, character: 0 } );
10 | assert.deepEqual( '_chunk', position._chunk, { start: 25, end: 25, length: 0 } );
11 | assert.deepEqual( 'chunks', position.chunks, [ { start: 25, end: 25, length: 0 } ] );
12 | assert.equal( '_parent', position._parent, undefined );
13 | });
14 |
--------------------------------------------------------------------------------
/test/test-Rule.js:
--------------------------------------------------------------------------------
1 | var munit = global.munit,
2 | CSSTree = global.CSSTree;
3 |
4 | munit( 'Rule', {
5 |
6 | // Testing initialization
7 | init: function( assert ) {
8 | var position = new CSSTree.Position( 25 ),
9 | rule = new CSSTree.Rule( " color ", " red !important ", position );
10 |
11 | assert.equal( 'property', rule.property, "color" );
12 | assert.equal( 'value', rule.value, "red !important" );
13 | assert.equal( 'position', rule.position, position );
14 | assert.deepEqual( 'parts', rule.parts, [ "red", "!important" ] );
15 | },
16 |
17 | // Testing string breadown of rule values
18 | breakdown: function( assert ) {
19 | var rule = new CSSTree.Rule();
20 |
21 | [
22 |
23 | {
24 | name: 'Basic',
25 | value: 'red !important',
26 | parts: [
27 | 'red',
28 | '!important'
29 | ]
30 | },
31 |
32 | {
33 | name: 'Slash',
34 | value: '10px / 20px',
35 | parts: [
36 | '10px',
37 | '/',
38 | '20px'
39 | ]
40 | },
41 |
42 | {
43 | name: 'Comma',
44 | value: 'Sans-Seriff,Comic-Sans',
45 | parts: [
46 | 'Sans-Seriff',
47 | ',',
48 | 'Comic-Sans'
49 | ]
50 | },
51 |
52 | {
53 | name: 'Value Separators',
54 | value: '10px "Red Light" \'Green Light\' url(http://www.google.com) "Testi\\"ng Esc\\"apes"',
55 | parts: [
56 | '10px',
57 | '"Red Light"',
58 | "'Green Light'",
59 | 'url(http://www.google.com)',
60 | '"Testi\\"ng Esc\\"apes"'
61 | ]
62 | }
63 |
64 | ].forEach(function( object ) {
65 | rule.value = object.value;
66 | rule.breakdown();
67 | assert.deepEqual( object.name, rule.parts, object.parts );
68 | });
69 | },
70 |
71 | });
72 |
--------------------------------------------------------------------------------
/test/test-Selector.js:
--------------------------------------------------------------------------------
1 | var munit = global.munit,
2 | CSSTree = global.CSSTree;
3 |
4 | munit( 'Selector', {
5 |
6 | // Testing selector init
7 | init: function( assert ) {
8 | var position = new CSSTree.Position( 25 ),
9 | rules = [ 1, 2, 3 ],
10 | selector = new CSSTree.Selector( " html body ", rules, position );
11 |
12 | assert.equal( 'selector', selector.selector, "html body" );
13 | assert.equal( 'position', selector.position, position );
14 | assert.equal( 'rules', selector.rules, rules );
15 | assert.equal( 'branches', selector.branches, undefined );
16 | assert.deepEqual( 'parts', selector.parts, [ "html", "body" ] );
17 | },
18 |
19 | // Testing breakdown of selector string
20 | breakdown: function( assert ) {
21 | var selector = new CSSTree.Selector();
22 |
23 | [
24 | {
25 | name: 'Basic',
26 | selector: "html body",
27 | parts: [
28 | 'html',
29 | 'body'
30 | ]
31 | },
32 |
33 | {
34 | name: 'Nested',
35 | selector: "html body.class span",
36 | parts: [
37 | 'html',
38 | [
39 | 'body',
40 | '.class'
41 | ],
42 | 'span'
43 | ]
44 | },
45 |
46 | {
47 | name: 'Brace',
48 | selector: "div a[data-tag='testing']",
49 | parts: [
50 | 'div',
51 | [
52 | 'a',
53 | "[data-tag='testing']"
54 | ]
55 | ]
56 | },
57 |
58 | {
59 | name: 'Nested Brace Filter',
60 | selector: "div a.class[data-tag='testing']:first-child",
61 | parts: [
62 | 'div',
63 | [
64 | 'a',
65 | '.class',
66 | "[data-tag='testing']",
67 | ':first-child'
68 | ]
69 | ]
70 | },
71 |
72 | {
73 | name: 'Separator',
74 | selector: "div > p",
75 | parts: [
76 | 'div',
77 | '>',
78 | 'p'
79 | ]
80 | },
81 |
82 | {
83 | name: 'All Separators',
84 | selector: "div > p ~ a + b, *",
85 | parts: [
86 | 'div',
87 | '>',
88 | 'p',
89 | '~',
90 | 'a',
91 | '+',
92 | 'b',
93 | ',',
94 | '*'
95 | ]
96 | },
97 |
98 | {
99 | name: 'Brace Separator',
100 | selector: "div p, [data-target=alpha]",
101 | parts: [
102 | 'div',
103 | 'p',
104 | ',',
105 | '[data-target=alpha]'
106 | ]
107 | },
108 |
109 | {
110 | name: 'Brace Separator Nested',
111 | selector: "div p:first-child, [data-target=alpha]:last-child",
112 | parts: [
113 | 'div',
114 | [
115 | 'p',
116 | ':first-child'
117 | ],
118 | ',',
119 | [
120 | '[data-target=alpha]',
121 | ':last-child'
122 | ]
123 | ]
124 | }
125 |
126 | ].forEach(function( object ) {
127 | selector.selector = object.selector;
128 | selector.breakdown();
129 | assert.deepEqual( object.name, selector.parts, object.parts );
130 | });
131 | },
132 |
133 | });
134 |
--------------------------------------------------------------------------------
/test/test-files.js:
--------------------------------------------------------------------------------
1 | var munit = global.munit,
2 | CSSTree = global.CSSTree,
3 | fs = require( 'fs' ),
4 | SHEETS = __dirname + '/sheets/',
5 | POSITIONS = __dirname + '/positions/';
6 |
7 |
8 | function RemovePositions( array ) {
9 | array.forEach(function( object ) {
10 | if ( object.position ) {
11 | delete object.position;
12 | }
13 |
14 | if ( object.rules ) {
15 | RemovePositions( object.rules );
16 | }
17 |
18 | if ( object.branches ) {
19 | RemovePositions( object.branches );
20 | }
21 | });
22 |
23 | return array;
24 | }
25 |
26 | // Lower sheet rendering priority to allow focused tests to run first
27 | munit( 'Files', { priority: munit.PRIORITY_LOW }, {
28 |
29 | // Sheet testing
30 | sheets: function( assert ) {
31 | fs.readdirSync( SHEETS ).forEach(function( dir ) {
32 | if ( ! fs.statSync( SHEETS + dir ).isDirectory() ) {
33 | return;
34 | }
35 |
36 | // Sheet setup
37 | var name = 'sheet-' + dir,
38 | branch = name + '-branch-',
39 | sheet = fs.readFileSync( SHEETS + dir + '/sheet.css', 'utf8' ),
40 | branches = RemovePositions( CSSTree( sheet ).branches ),
41 | match = require( SHEETS + dir + '/tree.js' );
42 |
43 | assert.deepEqual( name, branches, match );
44 | });
45 | },
46 |
47 | // Position Testing
48 | positions: function( assert ) {
49 | fs.readdirSync( POSITIONS ).forEach(function( dir ) {
50 | if ( ! fs.statSync( POSITIONS + dir ).isDirectory() ) {
51 | return;
52 | }
53 |
54 | // Sheet setup
55 | var name = 'positions-' + dir,
56 | branch = name + '-branch-',
57 | sheet = fs.readFileSync( POSITIONS + dir + '/sheet.css', 'utf8' ),
58 | branches = CSSTree( sheet ).branches,
59 | match = require( POSITIONS + dir + '/tree.js' );
60 |
61 | assert.deepEqual( name, branches, match );
62 | });
63 | },
64 |
65 | });
66 |
--------------------------------------------------------------------------------