TEST
JavaScript for HTML Browsers: CSS Selectors and Media Queries
最終更新:
eriax
-
view
制限
- DOM-HTML 限定。名前空間を認識しない。大文字・小文字の区別はテキトー。長さは px しか認識しない。
- 構文エラーからの回復はしない(構文エラー回復を含めた CSS パーサは別所を参照)。
- 1000 行以内を目指したが現状 1050 行。パックして 20kb ぐらい。
- マッチング主体なので、ノードをかき集める速度は期待できない。
使用例
セレクタテスト
var sapi = CSS_Selectors_and_MediaQueries_for_HTML_Browsers._selectors_group;
// セレクタテストの生成。
var selector = sapi('body:lang(ja) p#HOGE:nth-child(2n+1)');
// テストしたい要素。
var target = document.getElementById('HOGE');
// テストを実行。this として all 配列を渡すと :scope にマッチする。
var result = selector.call({ all: [document.body] }, target);
戻り値は [[p, null], [body, null]] のように単純セレクタにマッチした要素の配列(または null)。セレクタとは逆順に入っており、0 番がセレクタ全体にマッチした要素。この例で null になっている部分は疑似要素を表すレンジ。例えば p::before なら、p の最初の子の直前に位置するレンジになる。
セレクタを使って要素をかき集める簡便メソッド
var applySelectors = CSS_Selectors_and_MediaQueries_for_HTML_Browsers.applySelectors;
// body 以下で非表示の要素をかき集める。戻り値は配列。
var result = applySelectors('*[aria-hidden="true"]', document.body);
メディアクエリテスト
var mql = CSS_Selectors_and_MediaQueries_for_HTML_Browsers._media_query_list;
// メディアクエリテストの生成。
var mediump = mql('only screen and (min-width: 800px), screen and (min-color: 16)');
// window をメディアグループ 'visual' としてクエリテスト。戻り値は真偽値。
if (mediump.call({ ondefault: function (e) { return true; } }, window, 'visual'))
alert('OK');
this 値として ondefault ハンドラを渡すと、未知の特徴を処理できる。
メディアグループ 'interactive' としてテストする簡便メソッド
var matchesWindow = CSS_Selectors_and_MediaQueries_for_HTML_Browsers.matchesWindow;
if (matchesWindow('(orientation: portrait)', window))
alert('OK');
ソースコード
var CSS_Selectors_and_MediaQueries_for_HTML_Browsers = new function () { /*@cc_on@*/
var h = '[0-9a-f]';
var nonascii = '[^\\u0000-\\u007F]';
var unicode = '\\\\' + h + '{1,6}(?:\\r\\n|[\x20\\n\\r\\t\\f])?';
var num = '(?:[0-9]+|[0-9]*\\.[0-9]+)';
var nl = '(?:\\n|\\r\\n|\\r|\\f)';
var w = '[\x20\\t\\r\\n\\f]*';
var s = '[\x20\\t\\r\\n\\f]+';
var comment = '\\/\\*[^\*]*\\*+(?:[^\/\*][^\*]*\\*+)*\\/';
var escape = unicode + '|\\\\[^\\n\\r\\f0-9A-Fa-f]';
var nmchar = '(?:[_0-9a-z-]|' + nonascii + '|' + escape + ')';
var name = nmchar + '+';
var nmstart = '(?:[_a-z]|' + nonascii + '|' + escape + ')';
var ident = '-?' + nmstart + nmchar + '*';
var string1 = '\"(?:[^\\n\\r\\f\\\\\"]|\\\\' + nl + '|' + nonascii + '|' + escape + ')*\"';
var string2 = '\'(?:[^\\n\\r\\f\\\\\']|\\\\' + nl + '|' + nonascii + '|' + escape + ')*\'';
var string = '(?:' + string1 + '|' + string2 + ')';
var invalid1 = '\"(?:[^\\n\\r\\f\\\\\"]|\\\\' + nl + '|' + nonascii + '|' + escape + ')*';
var invalid2 = '\'(?:[^\\n\\r\\f\\\\\']|\\\\' + nl + '|' + nonascii + '|' + escape + ')*';
var invalid = '(?:' + invalid1 + '|' + invalid2 + ')';
var url = '(?:[!#$%&*-~]|' + nonascii + '|' + escape + ')*';
//
var A = '(?:a|\\\\0{0,4}[46]1(?:\\r\\n|[ \\t\\r\\n\\f])?)';
var D = '(?:d|\\\\0{0,4}[46]4(?:\\r\\n|[ \\t\\r\\n\\f])?)';
var E = '(?:e|\\\\0{0,4}[46]5(?:\\r\\n|[ \\t\\r\\n\\f])?)';
var L = '(?:l|\\\\0{0,4}[46]c(?:\\r\\n|[ \\t\\r\\n\\f])?|\\\\l)';
var N = '(?:n|\\\\0{0,4}[46]e(?:\\r\\n|[ \\t\\r\\n\\f])?|\\\\n)';
var O = '(?:o|\\\\0{0,4}[46]f(?:\\r\\n|[ \\t\\r\\n\\f])?|\\\\o)';
var P = '(?:p|\\\\0{0,4}[57]0(?:\\r\\n|[ \\t\\r\\n\\f])?|\\\\p)';
var R = '(?:r|\\\\0{0,4}[57]2(?:\\r\\n|[ \\t\\r\\n\\f])?|\\\\r)';
var T = '(?:t|\\\\0{0,4}[57]4(?:\\r\\n|[ \\t\\r\\n\\f])?|\\\\t)';
var U = '(?:u|\\\\0{0,4}[57]5(?:\\r\\n|[ \\t\\r\\n\\f])?|\\\\u)';
var V = '(?:v|\\\\0{0,4}[57]6(?:\\r\\n|[ \\t\\r\\n\\f])?|\\\\v)';
var X = '(?:x|\\\\0{0,4}[57]8(?:\\r\\n|[ \\t\\r\\n\\f])?|\\\\x)';
var Y = '(?:y|\\\\0{0,4}[57]9(?:\\r\\n|[ \\t\\r\\n\\f])?|\\\\y)';
//
var S0 = w;
var S1 = s;
var INCLUDES = '~=';
var DASHMATCH = '\\|=';
var PREFIXMATCH = '\\^=';
var SUFFIXMATCH = '\\$=';
var SUBSTRINGMATCH = '\\*=';
var PLUS = w + '\\+';
var GREATER = w + '>';
var COMMA = w + ',';
var TILDE = w + '~(?!=)';
var NOT = ':not\\(';
var STRING = string;
var IDENT = ident;
var HASH = '#' + name;
var DIMENSION = num + ident;
var PERCENTAGE = num + '%';
var NUMBER = num;
var URI = U + R + L + '\\(' + w + string + w + '\\)|' + U + R + L + '\\(' + w + url + w + '\\)';
var FUNCTION = ident + '\\(';
var s_unary_operator = PLUS + '|-';
var s_hexcolor = HASH + S0;
//
var p_Sh = new RegExp('^' + S1);
var p_St = new RegExp(S1 + '$');
var f_trim = function (stringData) {
return stringData.replace(p_Sh, '').replace(p_St, '');
};
var p_escaped = /\\([0-9a-f]{1,6})(?:\r\n|[\x20\n\r\t\f])?|\\([^\n\r\f0-9a-f])/gi;
var f_unescape_replace = function (str, x, c) {
if (x) {
return String.fromCharCode(parseInt(x, 16)); // not accurate
}
return c;
};
var f_unescape = function (stringData) {
return stringData.replace(p_escaped, f_unescape_replace);
};
//
// css3-selectors
var p_selector_type = new RegExp('^(?:(' + IDENT + '(?=\\|)|\\*(?=\\|))?\\|)?(' + IDENT + '|\\*)', 'i');
var p_selector_subject = new RegExp;
p_selector_subject.exec = function (stringData) {
var src = stringData;
var cmp;
if ((cmp = p_selector_type.exec(src))) { // subject
var res1 = [];
var res2 = [];
var code;
var ns = cmp[1];
var ln = cmp[2];
if (!ns || ns === '*') {
if (ln === '*') {
code = 'return\x20n;';
}
else {
code = f_unescape(ln).replace(/\W/g, '\\$&');
code = 'return\x20/^' + code + '$/i.test(n.nodeName)?n:null;';
}
res2[res2.length] = code;
cmp = cmp[0];
res1[res1.length] = cmp;
}
else {
throw new Error('Namespace is not supported');
}
return [].concat(res1.join(''), res2);
}
return null;
};
//
var p_selector_ID = new RegExp('^#(' + name + ')', 'i');
var p_selector_class = new RegExp('^\\.(' + IDENT + ')', 'i');
var p_selector_attrib = new RegExp('\\[' + S0 + '((' + IDENT + '(?=\\|)|\\*(?=\\|))?\\|)?(' + IDENT + ')' + S0 + '(?:(' + PREFIXMATCH + '|' + SUFFIXMATCH + '|' + SUBSTRINGMATCH + '|' + INCLUDES + '|' + DASHMATCH + '|=)' + S0 + '(?:(' + STRING + ')|(' + IDENT + '))' + S0 + ')?' + '\\]', 'i');
var p_selector_negation_begin = new RegExp('^' + NOT, 'i');
var p_selector_negation_end = new RegExp('^' + S0 + '\\)');
var p_selector_pseudo_class = new RegExp('^(::?)(?:(' + FUNCTION + ')' + S0 + '(' + '(?:(?:' + DIMENSION + '|' + STRING + '|' + IDENT + '|' + NUMBER + '|' + PLUS + '|-)' + S0 + ')+' + ')' + '\\)' + '|(' + IDENT + '))', 'i');
var p_selector_predicate_common = new RegExp;
p_selector_predicate_common.exec = function (stringData) {
var src = stringData;
var cmp;
var res1 = [];
var res2 = [];
var code;
switch (src.charAt(0)) {
case '#':
if ((cmp = p_selector_ID.exec(src))) {
code = f_unescape(cmp[1]).replace(/[\"\\]/g, '\\$&');
code = 'return\x20(n.id==="' + code + '")?n:null;';
break;
}
return null;
case '.':
if ((cmp = p_selector_class.exec(src))) {
code = f_unescape(cmp[1]).replace(/\W/g, '\\$&');
code = 'return\x20/(?:^|' + S1 + ')' + code + '(?:' + S0 + '|$)/.test(n.className)?n:null;';
break;
}
return null;
case '[':
if ((cmp = p_selector_attrib.exec(src))) {
var ns = cmp[2];
if (ns && ns !== '*') {
throw new Error('Namespace is not supported');
}
var ln = f_unescape(cmp[3]).replace(/[\"\\]/g, '\\$&');
var opr = cmp[4];
var val;
code = 'var\x20a=n.getAttributeNode("' + ln + '"); ';
if (opr) {
val = ((val = cmp[5])) ? val.slice(1, -1) : cmp[6];
val = f_unescape(val).replace(/\W/g, '\\$&');
}
switch (opr) {
case '=':
val = '/^' + val + '$/.test(a.value)';
break;
case '^=':
val = '/^' + val + '/.test(a.value)';
break;
case '$=':
val = '/' + val + '$/.test(a.value)';
break;
case '*=':
val = '/' + val + '/.test(a.value)';
break;
case '~=':
val = '/(?:^|' + S1 + ')' + val + '(?:' + S1 + '|$)/.test(a.value)';
break;
case '|=':
val = '/^' + val + '(?=-|$)/.test(a.value)';
break;
default:
val = 'a.specified';
break;
}
code += 'return\x20(a&&' + val + ')?n:null;';
break;
}
return null;
case ':':
if ((cmp = p_selector_pseudo_class.exec(src))) {
var type = cmp[1];
var name;
var args, a, b;
if ((name = cmp[2])) {
name = name.slice(0, -1);
args = cmp[3];
}
else {
name = cmp[4];
args = '';
}
switch (type + name) {
case ':root':
code = 'return\x20(n===n.ownerDocument.documentElement)?n:null;';
break;
case ':nth-child':
args = f_parse_nth(args), a = args[0], b = args[1];
code = 'var\x20m,i;for(m=n,i=1;m=m.previousSibling;)if(m.nodeType===1)i++;';
code += (a === 0) ? 'return\x20(i===' + b + ')?n:null;' : 'var\x20j=i-' + b + ';return((j%' + a + '===0) && (j / ' + a + '>=0))?n:null;';
break;
case ':nth-last-child':
args = f_parse_nth(args), a = args[0], b = args[1];
code = 'var\x20m,i;for(m=n,i=1;m=m.nextSibling;)if(m.nodeType===1)i++;';
code += (a === 0) ? 'return\x20(i===' + b + ')?n:null;' : 'var\x20j=i-' + b + ';return((j%' + a + '===0) && (j / ' + a + '>=0))?n:null;';
break;
case ':nth-of-type':
args = f_parse_nth(args), a = args[0], b = args[1];
code = 'var\x20s=n.tagName,m,i;for(m=n,i=1;m=m.previousSibling;)if(m.nodeType===1&&m.tagName===s)i++;';
code += (a === 0) ? 'return\x20(i===' + b + ')?n:null;' : 'var\x20j=i-' + b + ';return((j%' + a + '===0)&&(j/' + a + '>=0))?n:null;';
break;
case ':nth-last-of-type':
args = f_parse_nth(args), a = args[0], b = args[1];
code = 'var\x20s=n.tagName,m,i;for(m=n,i=1;m=m.nextSibling;)if(m.nodeType===1&&m.tagName===s)i++;';
code += (a === 0) ? 'return\x20(i===' + b + ')?n:null;' : 'var\x20j=i-' + b + ';return((j%' + a + '===0)&&(j/' + a + '>=0))?n:null;';
break;
case ':first-child':
code = 'var\x20m;for(m=n;m=m.previousSibling;)if(m.nodeType===1)return\x20null;return\x20n;';
break;
case ':last-child':
code = 'var\x20m;for(m=n;m=m.nextSibling;)if(m.nodeType===1)return\x20null;return\x20n;';
break;
case ':first-of-type':
code = 'var\x20s=n.tagName,m;for(m=n;m=m.previousSibling;)if(m.nodeType===1&&m.tagName===s)return\x20null;return\x20n;';
break;
case ':last-of-type':
code = 'var\x20s=n.tagName,m;for(m=n;m=m.nextSibling;)if(m.nodeType===1&&m.tagName===s)return\x20null;return\x20n;';
break;
case ':only-child':
code = 'var\x20m;for(m=n;m=m.previousSibling;)if(m.nodeType===1)return\x20null;for(m=n;m=m.nextSibling;)if(m.nodeType===1)return\x20null;return\x20n;';
break;
case ':only-of-type':
code = 'var\x20s=n.tagName,m;for(m=n;m=m.previousSibling;)if(m.nodeType===1&&m.tagName===s)return\x20null;for(m=n;m=m.nextSibling;)if(m.nodeType===1&&m.tagName===s)return\x20null;return\x20n;';
break;
case ':empty':
code = 'var\x20m;for(m=n.firstChild;m;m=m.nextSibling)switch(m.nodeType){case\x201:return\x20null;case\x203:case\x204:if(m.length>0)return\x20null;default:continue;}return\x20n;';
break;
case ':link':
case ':visited':
case ':active':
case ':hover':
throw new Error(name + ' is not supported');
case ':focus':
code = 'var\x20d;return((d=n.ownerDocument)&&(n===d.activeElement))?n:null;';
break;
case ':target':
code = 'var\x20d,s,w;return((d=n.ownerDocument)&&(w=d.defaultView)&&(s=w.location.hash)&&(s=s.slice(1))&&(n.id===s))?n:null;';
break;
case ':lang':
args = f_trim(args).replace(/\W/g, '\\$&');
code = 'for(var\x20m=n,v;m;m=m.parentNode)if(m.nodeType===1)if((v=m.lang))return\x20/^' + args + '(?:-|$)/i.test(v)?n:null;return\x20null;';
break;
case ':enabled':
code = 'return\x20(n.disabled===false)?n:null;';
break;
case ':disabled':
code = 'return\x20(n.disabled===true)?n:null;';
break;
case ':checked':
code = 'return\x20(n.checked===true)?n:null;';
break;
case ':scope':
code = 'var\x20c=this.all;if(!c)return\x20null;var\x20I=c.length,i;for(i=0;i<I;i++)if(n===c[i])return\x20n;return\x20null;';
break;
case '::first-line':
case ':first-line':
throw new Error(name + ' is not supported');
case '::first-letter':
case ':first-letter':
code = 'var\x20d,r;if((d=n.ownerDocument)){r=d.createRange();while(n.hasChildNodes())n=n.firstChild;if(n.nodeType===3)if(n.length>0){r.setStart(n,0);r.setEnd(n,1);return\x20r;}}return\x20null;';
break;
case '::selection':
case ':selection':
code = 'var\x20d,w,s;if((d=n.ownerDocument)&&(w=d.defaultView))return\x20w.getSelection().getRangeAt(0);return\x20null';
break;
case '::before':
case ':before':
code = 'var\x20d,r;if((d=n.ownerDocument)){r=d.createRange();r.setStart(n,0);return\x20r;}return\x20null;';
break;
case '::after':
case ':after':
code = 'var\x20d,r;if((d=n.ownerDocument)){r=d.createRange();switch(n.nodeType){case\x203:case\x204:case\x207:case\x208:r.setStart(n,n.data.length);break;default:r.setStart(n,n.childNodes.length);break;}return\x20r;}return\x20null;';
break;
default:
throw new Error(name + ' is not supported');
}
break;
}
return null;
default:
return null;
}
res2[res2.length] = code;
cmp = cmp[0];
res1[res1.length] = cmp;
return [].concat(res1.join(''), res2);
};
//
var p_selector_negation_arg = new RegExp;
p_selector_negation_arg.exec = function (stringData) {
var src = stringData;
var cmp;
var res1 = [];
var res2 = [];
var code;
switch (src.charAt(0)) {
case '#':
case '.':
case '[':
case ':':
if ((cmp = p_selector_predicate_common.exec(src))) {
code = cmp.slice(1);
break;
}
return null;
default:
if ((cmp = p_selector_subject.exec(src))) {
code = cmp.slice(1);
break;
}
return null;
}
res2[res2.length] = 'return\x20!(function(n){' + code.join('') + '})(n)?n:null;';
cmp = cmp[0];
res1[res1.length] = cmp;
return [].concat(res1.join(''), res2);
};
//
var p_simple_selector_sequence = new RegExp;
p_simple_selector_sequence.exec = function (stringData) {
var src = stringData;
var cmp;
var res1 = [];
var res2 = [];
var code;
var subj = false;
if ((cmp = p_selector_subject.exec(src))) { // subject
subj = true;
res2 = res2.concat(cmp.slice(1));
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
}
A: while (src.length > 0) { // predicates
switch (src.charAt(0)) {
case '#':
case '.':
case '[':
if ((cmp = p_selector_predicate_common.exec(src))) {
code = cmp.slice(1);
break;
}
return null;
case ':':
if ((cmp = p_selector_negation_begin.exec(src))) {
cmp = cmp[0];
var zrc = src.slice(cmp.length);
var kmp;
var rez1 = [cmp];
var rez2 = [];
if ((kmp = p_selector_negation_arg.exec(zrc))) {
rez2 = rez2.concat(kmp.slice(1));
kmp = kmp[0];
rez1[rez1.length] = kmp;
zrc = zrc.slice(kmp.length);
if ((kmp = p_selector_negation_end.exec(zrc))) {
kmp = kmp[0];
rez1[rez1.length] = kmp;
zrc = zrc.slice(kmp.length);
//
src = zrc;
res1.push.apply(res1, rez1);
res2.push.apply(res2, rez2);
continue;
}
}
break A;
}
if ((cmp = p_selector_predicate_common.exec(src))) {
code = cmp.slice(1);
break;
}
break A;
default:
break A;
}
res2 = res2.concat(code);
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
}
if (res1.length > 0) {
if (!subj) {
cmp = p_selector_subject.exec('*');
res2 = [].concat(cmp.slice(1), res2);
}
return [].concat(res1.join(''), res2);
}
return null;
};
//
var p_combinator = new RegExp('^(?:(' + PLUS + ')' + S0 + '|(' + GREATER + ')' + S0 + '|(' + TILDE + ')' + S0 + '|(' + S1 + '))', 'i');
var o_combinator_table = {
'': function (node, patterns) {
A: if (node.nodeType === 1) {
var count = patterns.length;
var i, n;
for (i = 0; i < count; i++) {
n = patterns[i].call(this, node);
if (!n) {
break A;
}
}
return [node, (node !== n) ? n : null];
}
return null;
},
'\x20': function (node, patterns) {
var count = patterns.length;
var i, n;
A: while ((node = node.parentNode)) {
if (node.nodeType === 1) {
for (i = 0; i < count; i++) {
n = patterns[i].call(this, node);
if (!n) {
continue A;
}
}
return [node, (node !== n) ? n : null];
}
}
return null;
},
'>': function (node, patterns) {
var count = patterns.length;
var i, n;
A: if ((node = node.parentNode)) {
if (node.nodeType === 1) {
for (i = 0; i < count; i++) {
n = patterns[i].call(this, node);
if (!n) {
break A;
}
}
return [node, (node !== n) ? n : null];
}
}
return null;
},
'~': function (node, patterns) {
var count = patterns.length;
var i, n;
A: while ((node = node.previousSibling)) {
if (node.nodeType === 1) {
for (i = 0; i < count; i++) {
n = patterns[i].call(this, node);
if (!n) {
continue A;
}
}
return [node, (node !== n) ? n : null];
}
}
return null;
},
'+': function (node, patterns) {
var count = patterns.length;
var i, n;
A: while ((node = node.previousSibling)) {
if (node.nodeType === 1) {
for (i = 0; i < count; i++) {
n = patterns[i].call(this, node);
if (!n) {
break A;
}
}
return [node, (node !== n) ? n : null];
}
}
return null;
}
};
//
var p_selector = new RegExp;
p_selector.exec = function (stringData) {
var src = stringData;
var cmp;
if ((cmp = p_simple_selector_sequence.exec(src))) {
var res1 = [];
var res2 = [];
var sel;
var cmb;
sel = cmp.slice(1);
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
while ((cmp = p_combinator.exec(src))) {
cmb = cmp[1] ? '+' : cmp[2] ? '>' : cmp[3] ? '~' : '\x20';
res2[res2.length] = [sel, cmb];
sel = null;
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
if ((cmp = p_simple_selector_sequence.exec(src))) {
sel = cmp.slice(1);
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
continue;
}
break;
}
if (sel) {
res2[res2.length] = [sel, ''];
}
else {
var lst = res2[res2.length - 1];
var lsn = lst.length - 1;
if (lst[lsn] === '\x20') {
lst[lsn] = '';
}
else {
return null;
}
}
for (var simple, patterns, i = 0, I = res2.length; i < I; i++) {
simple = res2[i], patterns = simple[0];
res2[i] = {
patterns: patterns,
relation: o_combinator_table[simple[1]]
};
for (var j = 0, J = patterns.length; j < J; j++) {
patterns[j] = new Function('n', patterns[j]);
}
}
res2.reverse();
return [].concat(res1.join(''), res2);
}
return null;
};
//
var p_selector_separator = new RegExp('^' + COMMA + w);
var p_selectors_group = new RegExp;
p_selectors_group.exec = function (stringData) {
var src = stringData;
var cmp;
if ((cmp = p_selector.exec(src))) {
var res1 = [];
var res2 = [];
res2[res2.length] = cmp.slice(1);
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
while ((cmp = p_selector_separator.exec(src))) {
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
if ((cmp = p_selector.exec(src))) {
res2[res2.length] = cmp.slice(1);
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
continue;
}
break;
}
return [].concat(res1.join(''), res2);
}
return null;
};
//
var s_integer = '[0-9]+';
var p_nth = new RegExp(w + '(?:([-+]?(?:' + s_integer + ')?)' + N + '(?:' + w + '([-+])' + w + '(' + s_integer + '))?|([-+]?(?:' + s_integer + '))|(' + O + D + D + ')|(' + E + V + E + N + '))' + w, 'i');
var f_parse_nth = function (stringData) {
var cmp = p_nth.exec(stringData);
if (cmp) {
var i, a, b;
for (i = 0; !cmp[++i];);
switch (i) {
case 1: // '2n+1'
a = cmp[i];
b = cmp[i + 1] + cmp[i + 2];
return [a === '+' ? 1 : a === '-' ? -1 : parseInt(a, 10), parseInt(b, 10) || 0];
case 4: // '1'
return [0, parseInt(cmp[i], 10) || 0];
case 5: // 'odd'
return [2, 1];
case 6: // 'even'
return [2, 0];
}
}
return null;
};
//
var o_selectors_group_cache = { };
var f_create_selectors_group = function (stringData) {
var selectors = p_selectors_group.exec(stringData);
if (!selectors || stringData.length !== selectors[0].length) {
throw new Error('malformed CSS selectors');
}
var fn = o_selectors_group_cache[stringData];
if ('function' === typeof fn) {
return fn;
}
return o_selectors_group_cache[stringData] = function (node) {
var selectorCount = selectors.length;
var i;
A: for (i = 1; i < selectorCount; i++) {
var n = node;
var result = [];
var simples = selectors[i];
var simpleCount = simples.length;
var j;
for (j = 0; j < simpleCount; j++) {
var simple = simples[j];
var patterns = simple.patterns;
var relation = simple.relation;
if ((n = relation.call(this, n, patterns))) {
result[result.length] = n;
n = n[0];
continue;
}
continue A;
}
return result;
}
return null;
};
};
this._selectors_group = f_create_selectors_group;
//
// css3-values
var p_exprs = new RegExp('^(?:' + '(' + FUNCTION + ')' + '|(\\))' + '|(' + '(?:' + s_unary_operator + ')?' + '(?:' + PERCENTAGE + '|' + DIMENSION + '|' + NUMBER + ')' + '|' + URI + '|' + STRING + '|' + IDENT + '(?!\\()' + '|' + s_hexcolor + ')|(' + COMMA + S0 + '|' + S0 + '/' + S0 + '|' + S1 + ')' + ')', 'i');
var p_expr = new RegExp;
p_expr.exec = function (stringData) {
var src = stringData;
var cmp;
var tmp;
var res1 = [];
var res2 = [];
var depth = 0;
for (; cmp = p_exprs.exec(src); src = src.slice(tmp.length)) {
if ((tmp = cmp[1])) { // 'f('
depth++, res1[res1.length] = tmp;
res2[res2.length] = f_unescape(tmp);
continue;
}
if ((tmp = cmp[2])) { // ')'
if (depth > 0) {
depth--, res1[res1.length] = tmp;
res2[res2.length] = tmp;
continue;
}
break;
}
if ((tmp = cmp[3])) {
res1[res1.length] = tmp;
res2[res2.length] = f_unescape(tmp);
continue;
}
if ((tmp = cmp[4])) { // operator
res1[res1.length] = tmp;
res2[res2.length] = f_trim(tmp) || '\x20';
continue;
}
}
if (depth === 0) {
if (res2[res2.length - 1] === '\x20') {
res2.pop();
}
return [].concat(res1.join(''), res2);
}
return null;
};
//
// css3-mediaqueries
var p_media_expression_prefix = new RegExp('^\\(' + S0 + '(' + IDENT + ')' + S0, 'i');
var p_media_expression_infix = new RegExp('^:' + S0);
var p_media_expression_suffix = new RegExp('^\\)' + S0);
var p_val_px = new RegExp('^' + w + '(' + NUMBER + ')' + P + X + w + '$', 'i');
var p_val_ratio = new RegExp('^' + w + '(' + NUMBER + ')' + w + '/' + w + '(' + NUMBER + ')' + w + '$', 'i');
var p_val_num = new RegExp('^' + S0 + '(' + NUMBER + ')' + S0 + '$', 'i');
var p_val_pattern = new RegExp('^' + S0 + '(' + STRING + ')' + S0 + ',' + S0 + '(' + STRING + ')' + S0 + '$');
var c_media_width = 'w.innerWidth';
var c_media_height = 'w.innerHeight';
var c_media_width_div_height = 'w.innerWidth/w.innerHeight';
var c_media_device_width = 'w.screen.availWidth';
var c_media_device_height = 'w.screen.availHeight';
var c_media_device_width_div_device_height = 'w.screen.availWidth/w.screen.availHeight';
var c_media_color = 'w.screen.colorDepth';
var p_media_expression = new RegExp;
p_media_expression.exec = function (stringData) {
var src = stringData;
var cmp;
if ((cmp = p_media_expression_prefix.exec(src))) {
var res1 = [];
var res2 = [];
res2 = res2.concat(cmp.slice(1));
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
if ((cmp = p_media_expression_infix.exec(src))) {
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
if ((cmp = p_expr.exec(src))) {
res2 = res2.concat(cmp.slice(1));
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
}
}
if ((cmp = p_media_expression_suffix.exec(src))) {
cmp = cmp[0];
res1[res1.length] = cmp;
var feature = res2[0].toLowerCase();
var value = res2.slice(1).join('');
var accessor;
var operator;
var code;
/*@{
c_media_width = '(function(d){d=(d.compatMode==="CSS1Compat")?d.documentElement:d.body;return\x20d.clientWidth;})(w.document)';
c_media_height = '(function(d){d=(d.compatMode==="CSS1Compat")?d.documentElement:d.body;return\x20d.clientHeight;})(w.document)';
c_media_width_div_height = '(function(d){d=(d.compatMode==="CSS1Compat")?d.documentElement:d.body;return\x20d.clientWidth/d.clientHeight;})(w.document)';
}@*/
if (/(?:^|-)(?:width|height)$/i.test(feature)) {
switch (feature) {
case 'width':
accessor = c_media_width, operator = '===';
break;
case 'height':
accessor = c_media_height, operator = '===';
break;
case 'max-width':
accessor = c_media_width, operator = '<=';
break;
case 'min-width':
accessor = c_media_width, operator = '>=';
break;
case 'max-height':
accessor = c_media_height, operator = '<=';
break;
case 'min-height':
accessor = c_media_height, operator = '>=';
break;
case 'device-width':
accessor = c_media_device_width, operator = '===';
break;
case 'device-height':
accessor = c_media_device_height, operator = '===';
break;
case 'max-device-width':
accessor = c_media_device_width, operator = '<=';
break;
case 'min-device-width':
accessor = c_media_device_width, operator = '>=';
break;
case 'max-device-height':
accessor = c_media_device_height, operator = '<=';
break;
case 'min-device-height':
accessor = c_media_device_height, operator = '>=';
break;
default:
accessor = 'false';
}
code = 'return\x20' + accessor;
if (value && (value = p_val_px.exec(value))) {
code += operator + value[1] + ';';
}
else if (/^[<>]/.test(operator)) {
code += ';';
}
else {
code += '!==0;';
}
}
else if (/(?:^|-)aspect-ratio$/i.test(feature)) {
switch (feature) {
case 'aspect-ratio':
accessor = c_media_width_div_height, operator = '==';
break;
case 'max-aspect-ratio':
accessor = c_media_width_div_height, operator = '<=';
break;
case 'min-aspect-ratio':
accessor = c_media_width_div_height, operator = '>=';
break;
case 'device-aspect-ratio':
accessor = c_media_device_width_div_device_height, operator = '==';
break;
case 'max-device-aspect-ratio':
accessor = c_media_device_width_div_device_height, operator = '<=';
break;
case 'min-device-aspect-ratio':
accessor = c_media_device_width_div_device_height, operator = '>=';
break;
default:
accessor = 'false';
}
code = 'return\x20' + accessor;
if (value && (value = p_val_ratio.exec(value))) {
code += operator + value[1] + ';';
}
else if (/^[<>]/.test(operator)) {
code += ';';
}
else {
code += '!==0;';
}
}
else if (/(?:^|-)color$/i.test(feature)) {
switch (feature) {
case 'color':
accessor = c_media_color, operator = '===';
break;
case 'max-color':
accessor = c_media_color, operator = '<=';
break;
case 'min-color':
accessor = c_media_color, operator = '>=';
break;
default:
accessor = 'false';
}
code = 'return\x20' + accessor;
if (value && (value = p_val_num.exec(value))) {
code += operator + value[1] + ';';
}
else if (/^[<>]/.test(operator)) {
code += ';';
}
else {
code += '!==0;';
}
}
else {
switch (feature) {
case 'orientation':
if (value) {
value = f_trim(value).toLowerCase();
switch (value) {
case 'portrait':
code = 'return\x20' + c_media_width + '<=' + c_media_height + ';';
break;
case 'landscape':
code = 'return\x20' + c_media_width + '>' + c_media_height + ';';
break;
default:
code = 'return\x20false;';
break;
}
}
else {
code = 'return\x20false;';
}
break;
case '-x-domain-pattern':
value = p_val_pattern.exec(value);
if (value) {
var psrc = value[1].slice(1, -1).replace(/[\/\\]/g, '\\$&');
var flag = value[2].slice(1, -1).replace(/\\/g, '\\$&');
code = /\W/.test(flag) ? 'return\x20false;' : 'return\x20/' + psrc + '/' + flag + '.test(w.location);';
}
else {
code = 'return\x20false;';
}
break;
default:
code = 'var\x20h=this.ondefault;if(h){return\x20h.call(this,{target:w,feature:"' + feature + '",value:"' + value.replace(/[\"\\]/g, '\\$&') + '"});}return\x20false;';
break;
}
}
res2 = code;
return [].concat(res1.join(''), res2);
}
}
return null;
};
//
var o_media_groups = {
'continuous': /\s*(?:^|,)\s*(?:braille|handheld|screen|speech|tty|tv)\s*(?:,|$)\s*/i,
'paged': /\s*(?:^|,)\s*(?:handheld|embossed|print|projection|tv)\s*(?:,|$)\s*/i,
'visual': /\s*(?:^|,)\s*(?:handheld|print|projection|screen|tty|tv)\s*(?:,|$)\s*/i,
'audio': /\s*(?:^|,)\s*(?:handheld|screen|tv)\s*(?:,|$)\s*/i,
'speechg': /\s*(?:^|,)\s*(?:handheld|speech)\s*(?:,|$)\s*/i,
'tactile': /\s*(?:^|,)\s*(?:braille|embossed)\s*(?:,|$)\s*/i,
'grid': /\s*(?:^|,)\s*(?:braille|embossed|handheld|tty)\s*(?:,|$)\s*/i,
'bitmap': /\s*(?:^|,)\s*(?:handheld|print|projection|screen|tv)\s*(?:,|$)\s*/i,
'interactive': /\s*(?:^|,)\s*(?:braille|handheld|projection|screen|speech|tty|tv)\s*(?:,|$)\s*/i,
'static': /\s*(?:^|,)\s*(?:braille|embossed|handheld|print|screen|speech|tty|tv)\s*(?:,|$)\s*/i,
//
'braille': /\s*(?:^|,)\s*(?:continuous|tactile|grid|interactive|static)\s*(?:,|$)\s*/i,
'embossed': /\s*(?:^|,)\s*(?:paged|tactile|grid|static)\s*(?:,|$)\s*/i,
'handheld': /\s*(?:^|,)\s*(?:continuous|paged|visual|audio|speech|grid|bitmap|interactive|static)\s*(?:,|$)\s*/i,
'print': /\s*(?:^|,)\s*(?:paged|visual|bitmap|static)\s*(?:,|$)\s*/i,
'projection': /\s*(?:^|,)\s*(?:paged|visual|bitmap|interactive)\s*(?:,|$)\s*/i,
'screen': /\s*(?:^|,)\s*(?:continuous|visual|audio|bitmap|interactive|static)\s*(?:,|$)\s*/i,
'speech': /\s*(?:^|,)\s*(?:continuous|speech|interactive|static)\s*(?:,|$)\s*/i,
'tty': /\s*(?:^|,)\s*(?:continuous|visual|grid|interactive|static)\s*(?:,|$)\s*/i,
'tv': /\s*(?:^|,)\s*(?:continuous|paged|visual|audio|bitmap|interactive|static)\s*(?:,|$)\s*/i
};
//
var p_media_query_prefix = new RegExp('^' + S0 + '(' + O + N + L + Y + '|' + N + O + T + ')?' + S0 + '(' + IDENT + ')' + S0, 'i');
var p_media_query_and = new RegExp('^' + A + N + D + S1, 'i');
var p_media_query = new RegExp;
p_media_query.exec = function (stringData) {
var src = stringData;
var cmp;
var res1 = [];
var res2 = [];
var code1;
var code2;
A: {
if ((cmp = p_media_query_prefix.exec(src))) { // only | not
code1 = (code1 = cmp[1]) ? f_unescape(code1).toLowerCase() : 'only';
code2 = f_unescape(cmp[2]).toLowerCase();
break A;
}
if ((cmp = p_media_expression.exec(src))) {
code1 = 'only';
code2 = 'all';
res2[res2.length] = new Function('w', cmp.slice(1).join(''));
break A;
}
return null;
}
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
// only or not
code1 = (code1 === 'not') ? 'return\x20!b;' : 'return\x20b;';
// media type
if (code2 === 'all') {
code2 = 'return\x20true;';
}
else {
var pattern = o_media_groups[code2];
code2 = 'if(/(?:^|' + S0 + ',)' + S0 + '(?:' + code2.replace(/\W/g, '\\$&') + ')' + S0 + '(?:,' + S0 + '|$)/i.test(m))\x20return\x20true;';
if (pattern) {
code2 += 'return\x20' + pattern + '.test(m);'; // be careful not to contain '/'
}
else {
code2 += 'return\x20false;';
}
}
res2 = [].concat(new Function('b', code1), new Function('m', code2), res2);
// media queries
while ((cmp = p_media_query_and.exec(src))) {
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
if ((cmp = p_media_expression.exec(src))) {
res2[res2.length] = new Function('w', cmp.slice(1).join(''));
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
continue;
}
return null;
}
return [].concat(res1.join(''), res2);
};
//
var p_media_query_list_prefix = new RegExp('^' + S0);
var p_media_query_list_infix = new RegExp('^' + COMMA + S0, 'i');
var p_media_query_list = new RegExp;
p_media_query_list.exec = function (stringData) {
var src = stringData;
var cmp;
if ((cmp = p_media_query_list_prefix.exec(src))) {
var res1 = [];
var res2 = [];
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
if ((cmp = p_media_query.exec(src))) {
res2[res2.length] = cmp.slice(1);
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
while ((cmp = p_media_query_list_infix.exec(src))) {
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
if ((cmp = p_media_query.exec(src))) {
res2[res2.length] = cmp.slice(1);
cmp = cmp[0];
res1[res1.length] = cmp;
src = src.slice(cmp.length);
continue;
}
break;
}
}
return [].concat(res1.join(''), res2);
}
return null;
};
//
var o_media_query_list_cache = { };
var f_create_media_query_list = function (stringData) {
var queries = p_media_query_list.exec(stringData);
if (!queries) {
throw new Error('malformed Media Queries');
}
var fn = o_media_query_list_cache[stringData];
if ('function' === typeof fn) {
return fn;
}
return o_media_query_list_cache[stringData] = function (view, mediaType) {
var queryCount = queries.length;
var i;
for (i = 1; i < queryCount; i++) {
var exprs = queries[i];
var exprCount = exprs.length;
var j;
if (exprs[1].call(this, mediaType)) {
var bool = true;
for (j = 2; j < exprCount; j++) {
if (!exprs[j].call(this, view)) {
bool = false;
break;
}
}
if (exprs[0].call(this, bool)) {
return true;
}
}
}
return false;
};
};
this._media_query_list = f_create_media_query_list;
//
// shortcut methods
var f_matches_media_interactive = function (queries, view, thisArg) {
return f_create_media_query_list(queries).call(thisArg, view, 'interactive');
};
this.matchesWindow = f_matches_media_interactive;
var f_apply_selectors = function (selectors, root, thisArg) {
selectors = f_create_selectors_group(selectors);
if (! thisArg) {
thisArg = { };
}
thisArg.all = [ ].concat(thisArg.all || root);
var result = [ ];
var es = root.getElementsByTagName('*');
var I = es.length;
var i;
var r;
for (i = 0; i < I; i++) {
if ((r = selectors.call(thisArg, es[i]))) {
r = r[0];
result[result.length] = r[1] || r[0];
}
}
return result;
};
this.applySelectors = f_apply_selectors;
};
- 初出:2011-07-07/08/09/10/11/12、修正 2011-08-12