lite
Version:
A cross platform template engine base on xml/html and javascript expression.
1,550 lines (1,426 loc) • 478 kB
JavaScript
if(typeof console == 'undefined'){console = {};'log,warn,error,info,debug'.replace(/\w+/g,function(a){console[a] = print});console.dir = function(o){for(var n in o){console.log(n,o[n]);}};
console.time = console.time || function(l){this['#'+l] = +new Date};
console.timeEnd = console.timeEnd || function(l){console.log(l + (new Date-this['#'+l]));};
console.assert = console.assert || function(l){if(!l){console.error('Assert Failed!!!')}};
}
function require(id){
id = id.replace(/(\/.*?)(?:\.js)?$/,'$1.js')
if(id in require.module){
return require.module[id].exports;
}
var module = require.module[id] = {exports:{},id:id};
var dir = id.replace(/[^\/\\]+$/,'');
//console.log(id)
require.cached[id].call(null,function(id2){
if(id2.charAt() == '.'){
id2 = dir+id2;
while(id2 != (id2 =id2.replace( /[^\/]+\/\.\.\/|(\/)?\.\//,'$1')));
}
return require(id2);
},module.exports,module,id,dir);
return module.exports;
}
require.cached={}
require.module={}
if(typeof module == "object"){module.exports = require};
require.cached["fs"]=function (require,exports,m){
var isJavaEnv = typeof window == 'undefined' && typeof java == 'object' && typeof (java.io && java.io.File) == 'function';
if(isJavaEnv) {
exports.readFileSync = function(path){
var ins = new java.io.FileInputStream(path);
//Buffer
return {
toString:function(encoding){
var s = new java.io.InputStreamReader(ins,encoding||'utf-8')
var buf = [],c;
while((c = s.read())>=0){
//console.log(c)
buf.push(c)
}
//console.log(String.fromCharCode.apply(String,buf))
return String.fromCharCode.apply(String,buf)
}
}
};
exports.existsSync =function(path){
return new java.io.File(path).exists();
};
exports.realpathSync = function(path){
return new java.io.File(path).getAbsolutePath()
}
}else{
m.exports = module.require('fs');
}
};
require.cached["path"]=function (require,exports,m,__filename,__dirname){
var isJavaEnv = typeof window == 'undefined' && typeof java == 'object' && typeof (java.io && java.io.File) == 'function';
if(isJavaEnv) {
exports.resolve = function () {
var i = 0;
var file = new java.io.File(arguments[i++]);
while (i < arguments.length) {
file = new java.io.File(file, arguments[i++]);
}
return file.getAbsoluteFile().getCanonicalPath();
}
}else{
m.exports = module.require('path');
}
};
require.cached["lite/src/main/js/compiler.js"]=function (require,exports,module,__filename,__dirname){var ParseConfig = require('./parse/config').ParseConfig;
var ParseContext = require('./parse/parse-context').ParseContext;
var JSTranslator = require('./parse/js-translator').JSTranslator;
var loadLiteXML = require('./parse/xml').loadLiteXML;
var buildURIMatcher = require('./parse/resource').buildURIMatcher
exports.getTemplateId = getTemplateId
exports.LiteCompiler = LiteCompiler;
function getTemplateId(path){
////path.replace(/[^\w\_]/g,'_')
return path.slice(1).replace(/[^\w\_]/g,'_');
}
exports.execute = function(args){
var options = {};
var key = '';
for(var i=2;i<args.length;i++){
var arg = args[i];
if(arg.charAt() == '-'){
key = arg.substr(1)
options[key] = [];
}else{
options[key].push(arg)
}
}
//console.log(options)
var root = options.root && options.root[0];
var output = options.output && options.output[0];
compile(root,output,options.translator,
options.includes,options.excludes)
}
function LiteCompiler(root,options){
options = options || {};
var path = require('path');
var root =String(path.resolve(root || './')).replace(/\\/g,'/');
var compileDir = options.compileDir;
var configPath = options.configPath || path.resolve(root,'lite.xml');
if(require('fs').existsSync(configPath)){
var dom = loadLiteXML(configPath);
//console.log(dom+'')
this.config = new ParseConfig(root,dom);
}else if(!options.configPath){
configPath = path.resolve(root,'WEB-INF/lite.xml');
if(require('fs').existsSync(configPath)){
var dom = loadLiteXML(configPath);
//console.log(dom+'')
this.config = new ParseConfig(root,dom);
}
}
this.config = this.config || new ParseConfig(root,null);
var waitPromise = 'waitPromise' in options?options.waitPromise:true;
this.translator = new JSTranslator({
//liteImpl:liteImpl,
waitPromise:waitPromise
});
console.info("LiteCompiler root:",root);
}
LiteCompiler.prototype.createParseContext = function(path){
return new ParseContext(this.config,path);
}
LiteCompiler.prototype.compile=function(path){
var root = this.config.root;
var context = this.createParseContext(path);
var uri = context.createURI(path);
context.parse(uri);
//console.log("&&&",path)
//console.log(context.getConfigMap(path))
var litecode = context.toList();
if(litecode.length){
//translator.liteImpl = 'liteImpl';//avoid inline jslib
var functionName = getTemplateId(path);
var jscode = this.translator && this.translator.translate(litecode,{name:functionName});//,params:null,defaults:null
}else{//纯静态内容
var jscode = "function(){}";
}
var res = context.getResources();
var config = context.getConfigMap();
var i = res.length;
while(i--){
res[i] = res[i].path
}
return {resources:res,litecode:litecode,code:jscode,config:config,path:path};
};
//exports.compile = compile;
function compile(root,output,translator,includes,excludes){
var fs = require('fs');
var path = require('path');
root = fs.realpathSync(root || './');
output = output || path.join(root,'.litecode');
if(!fs.existsSync(output))fs.mkdirSync(output);
//console.log('compile lite @'+root,{})
var compiler = new LiteCompiler(root);
includes = includes && includes.length && new RegExp(includes.map(buildURIMatcher).join('|'));
excludes = excludes && excludes.length && new RegExp(excludes.map(buildURIMatcher).join('|'));
function loadFile(dir){
var files = fs.readdirSync(dir);
for(var i=0;i<files.length;i++){
var n = files[i];
var file = dir+'/'+n;
//console.warn(file)
var stat = fs.statSync(file);
if(stat.isFile()){
var p = path.relative(root,file).replace(/^[\/\\]?|\\/g,'/');
if(excludes && excludes.test(p)){
continue;
}
if(includes ? includes.test(p):/\.xhtml$/.test(p)){
console.log('compile:',path.join(output,p))
var result = compiler.compile(p);
var source = ['exports.template=',result.jscode,';\nexports.config = ',JSON.stringify(result.config)].join('');
var id = getTemplateId(p);
fs.writeFileSync(path.join(output,id)+'.js',source);
//dest.writeFile(path.join(dest,p))
}
//console.log(p)
}else if(n.charAt() != '.' &&stat.isDirectory() ){
loadFile(file)
}
}
}
loadFile(root);
}
};
require.cached["lite/src/main/js/parse/config.js"]=function (require,exports,module,__filename,__dirname){/*
* List Template
* License LGPL(您可以在任何地方免费使用,但请不要吝啬您对框架本身的改进)
* http://www.xidea.org/project/lite/
* @author jindw
* @version $Id: template.js,v 1.4 2008/02/28 14:39:06 jindw Exp $
*/
//add as default
/**
* 模板解析上下文对象实现
* [
* {
* "includes":["/example\\/*.xhtml"],
* "excludes":[],
* "config":{
* "encoding":"utf-8",
* "contentType":"text/html;charset=UTF-8"
* },
* "extensions":[
* {
* "namespace":"http://www.xidea.org/lite/core",
* "package":"lite/parse/i18n"
* }
* ]
* }
* ]
*/
function ParseConfig(root,dom){
this.root = new URI(root && root.replace(/[\\\/]*$/,'/') || 'lite:///');
var json = dom && parseConfig(dom);
if(json){
var result = [];
var i = json.length
while(i--){
var item = {};
copy(json[i],item);
item.includes = new RegExp(item.includes||"^$");
item.excludes = new RegExp(item.excludes||"^$");
result[i] = item;
}
this._groups = result;
}else{
this._groups = defaultConfig;
}
}
function copy(source,dest){
for(var n in source){
dest[n] = source[n];
}
}
function findGroup(groups,path,require){
for(var i=0,len = groups.length;i<len;i++){
var g = groups[i];
if(g.includes.test(path)){
if(!g.excludes.test(path)){
return g;
}
}
}
return require && groups[groups.length-1];
}
ParseConfig.prototype = {
// getDecotatorPage:function(path){
// var g = findGroup(this.config,path,null)
// return g && g.config['layout'];
// },
getConfig:function(path){
var result = {}
var g = findGroup(this._groups,path,null);
if(g){
copy(g.config,result);
}
return result;
},
getExtensionMap:function(path){
var g = findGroup(this._groups,path,null);
if(g){
return g.extensionMap;
}
return {};
}
}
var defaultConfig = {
"includes":/./,//"/example\\/*.xhtml"
"excludes":/^$/,
"config":{
//必要属性(控制xml编译)
"encoding":"utf-8",
//必要属性(控制xml编译)
"contentType":"text/html;charset=UTF-8"
},
"extensionMap":{
////xhtml 编译不是自带的,需要自己定义
//"http://www.w3.org/1999/xhtml":["org.xidea.lite.xhtml"],
//core 自行编译
//"http://firekylin.my.baidu.com/ns/2010":["org.xidea.lite.xhtml"],
//"http://firekylin.my.baidu.com/ns/2010":["org.xidea.lite.xhtml"]
}
}
if(typeof require == 'function'){
exports.ParseConfig=ParseConfig;
var parseConfig = require('./config-parser').parseConfig;
var URI=require('./resource').URI;
}
};
require.cached["lite/src/main/js/parse/config-parser.js"]=function (require,exports,module,__filename,__dirname){/*
* List Template
* License LGPL(您可以在任何地方免费使用,但请不要吝啬您对框架本身的改进)
* http://www.xidea.org/project/lite/
* @author jindw
* @version $Id: template.js,v 1.4 2008/02/28 14:39:06 jindw Exp $
*/
/**
* 模板解析上下文对象实现
* <lite>
* <extension namespace="http://www.w3.org/1999/xhtml"
* package="org.xidea.lite.xhtml"/>
* <include>**.xhtml</include>
* <group layout="/layout.xhtml">
* <config name="javascriptCompressor"
* package="org.jside.jsi.tools.JSACompressor"/>
* <include>/example/*.xhtml</include>
* </group>
* </lite>
* ==>
* [
* {
* "includes":"^[\\\\/]example[\\\\/][^\\\\/]*\.xhtml$",
* "excludes":"",
* "config":{
* "layout":"/layout.xhtml",
* "encoding":"utf-8",
* "contentType":"text/html;charset=UTF-8",
* "javascriptCompressor":"org.jside.jsi.tools.JSACompressor"
* },
* "extensionMap":[
* {
* "namespace":"http://www.w3.org/1999/xhtml",
* "package":"org.xidea.lite.xhtml"
* }
* ]
* },
* {
* "includes":"^.*\.xhtml$",
* "excludes":"",
* "config":{
* "encoding":"utf-8",
* "contentType":"text/html;charset=UTF-8"
* },
* "extensionMap":{
* "http://www.w3.org/1999/xhtml":["org.xidea.lite.xhtml"]
* ]
* }
* ]
*/
function parseConfig(doc){
if(typeof doc == 'string'){doc = loadLiteXML(doc)}
var lites = doc.getElementsByTagName("lite");
var len = lites.length;
//console.log(new (require('xmldom').XMLSerializer)().serializeToString(doc))
if(len >= 1){
var root = new LiteGroup(lites.item(0))
if(len>1){
console.error("配置文件只允许一个lite节点","您的文档中包含"+len+"个节点,后续节点将作为第一个节点子节点解析。");
for(var i=1;i<len;i++){
root.children.push(new LiteGroup(lites[i],this));
}
}
var json = root.toJSON();
return json;
}
return null
}
function LiteGroup(node,parentConfig){
this.parentConfig = parentConfig || null
this.config = {}
this.encoding= findXMLAttribute(node,'encoding','charset');
this.type = findXMLAttribute(node,'type',"mime-type",'mimeType');
this.contentType = findXMLAttribute(node,'contentType','contextType');
this.layout = findXMLAttribute(node,'layout');
this.extensionMap = {};
this.children = [];
this.includes = [];
this.excludes = [];
var child = node.firstChild;
while(child){
if(child.nodeType == 1){
switch(child.nodeName){
case 'feature':
case 'attribute':
case 'config':
this.config[findXMLAttribute(child,'name','key','uri','url')] =
findXMLAttribute(child,'value','#text')
break;
case 'extension':
var ns = findXMLAttribute(child,'namespace','name','key','uri','url');
var p = findXMLAttribute(child,'package','impl','value','#text');
var ps = this.extensionMap[ns];
if(ps && ps instanceof Array){
appendAfter(ps,p);
}else{
this.extensionMap[ns] = [p];
}
break;
case 'include':
this.includes.push(findXMLAttribute(child,'value','#text','pattern'));
break;
case 'exclude':
this.excludes.push(findXMLAttribute(child,'value','#text','pattern'));
break;
case 'group':
this.children.push(new LiteGroup(child,this))
break;
default:
console.warn("unknow nodeName:"+child.nodeName);
}
}
child = child.nextSibling;
}
}
LiteGroup.prototype.toJSON = function(){
var result = [];
var len = this.children.length;
var json = {}
this.initialize();
for(var i=0;i<len;i++){
result.push.apply(result,this.children[i].toJSON());
}
json.includes = this.includes;
json.excludes = this.excludes;
json.config = this.config;
json.extensionMap = this.extensionMap;
result.push(json);
return result;
}
LiteGroup.prototype.initialize = function(){
this.initialize = Function.prototype;
var parentConfig = this.parentConfig
if(parentConfig){
var config = {};
copy(parentConfig.config,config);
copy(this.config,config);
this.config=config;
this.extensionMap = margeExtensionMap(parentConfig.extensionMap,this.extensionMap);
}
this.includes = compilePatterns(this.includes)
this.excludes = compilePatterns(this.excludes)
mergeContentType(this,parentConfig);
this.config["encoding"] = this.encoding;
this.config["contentType"] = this.contentType;
if(this.layout != null){
if(!this.layout || this.layout.charAt() == '/'){
this.config["layout"] = this.layout;
}else{
console.error("layout 必须为绝对地址('/'开始),你的设置为:"+this.layout);
}
}
}
function mergeContentType(thiz,parentConfig){
var type=thiz.type;
var encoding = thiz.encoding;
var contentType = thiz.contentType;//不从parent继承
/*========= init 3 vars==========*/
if(contentType!=null){
console.info("contentType 用于同时指定 type 和charset 属性,如此需求更推荐您采用type和encoding代替")
var p = contentType.indexOf('charset=');
if(p>0){
var charset = contentType.substring(p+8);
if(encoding){
if(charset.toUpperCase() != encoding.toUpperCase()){
console.info('encoding 与 contentType 不一致'+encoding+','+contentType
+"; ");
}
}else{
encoding = charset;
}
}
var contentType0 = contentType.replace(/\s*;.*$/,'');
if(type){
if(type.toUpperCase() != contentType0.toUpperCase()){
console.error('type 与 contentType 不一致'+type+','+contentType0
+';type 设置将被忽略');
}
}
type = contentType0
}
/*========== init from parent ==============*/
if(encoding == null){
encoding = parentConfig && parentConfig.encoding || 'UTF-8';
}
if(type == null){
type = parentConfig && parentConfig.type;
}
if(contentType == null){//不继承
if(type){
contentType = type+";charset="+encoding;
}
}else{
var p = contentType.indexOf('charset=');
if(p<0){
contentType +=";charset="+encoding;
}
}
thiz.type = type;
thiz.encoding = encoding;
thiz.contentType = contentType;
}
function copy(source,dest){
for(var n in source){
dest[n] = source[n];
}
}
function margeExtensionMap(parentExtMap,thisExtMap){
var result = {};
for(var n in thisExtMap){
result[n] = [].concat(thisExtMap[n]);
}
for(var n in parentExtMap){
var list = [].concat(parentExtMap[n]);
var thisExt = result[n] ;
if(thisExt){
var i = thisExt.length;
while(i--){
appendAfter(list,thisExt[i]);
}
}
result[n] = list;
}
return result;
}
function appendAfter(ps,p){
var i = ps.length;
while(i--){
if(ps[i] == p){
ps.splice(i,1)
}
}
ps.push(p);
}
function compilePatterns(ps){
var i = ps.length;
while(i--){
ps[i] = buildURIMatcher(ps[i]);
}
return ps.join('|')||null;
}
if(typeof require == 'function'){
exports.parseConfig=parseConfig;
var buildURIMatcher = require('./resource').buildURIMatcher
var loadLiteXML = require('./xml').loadLiteXML;
var findXMLAttribute=require('./xml').findXMLAttribute;
}
};
require.cached["lite/src/main/js/parse/resource.js"]=function (require,exports,module,__filename,__dirname){/*
* foo://example.com:8042/over/there?name=ferret#nose
* \_/ \______________/\_________/ \_________/ \__/
* | | | | |
* scheme authority path query fragment
* | _____________________|__
* / \ / \
* urn:example:animal:ferret:nose
*/
var uriPattern = /^([a-zA-Z][\w\.]*)\:(?:(\/\/[^\/]*))?(\/?[^?#]*)(\?[^#]*)?(#[\s\S]*)?$/;
var absURIPattern = /^[a-zA-Z][\w\.]*\:/;
var uriChars = /\\|[\x22\x3c\x3e\x5c\x5e\x60\u1680\u180e\u202f\u205f\u3000]|[\x00-\x20]|[\x7b-\x7d]|[\x7f-\xa0]|[\u2000-\u200b]|[\u2028-\u2029]/g;
var allEncodes = /[\x2f\x60]|[\x00-\x29]|[\x2b-\x2c]|[\x3a-\x40]|[\x5b-\x5e]|[\x7b-\uffff]/g;
///[\x22\x25\x3c\x3e\x5c\x5e\x60\u1680\u180e\u202f\u205f\u3000]|[\x00-\x20]|[\x7b-\x7d]|[\x7f-\xa0]|[\u2000-\u200b]|[\u2028-\u2029]/g;
function encodeChar(i){
return "%"+(0x100+i).toString(16).substring(1)
}
function decodeChar(c){
var n = c.charCodeAt();
if (n < 0x80){
return encodeChar(n);
}else if (n < 0x800){
return encodeChar(0xc0 | (n >>> 6))+encodeChar(0x80 | (n & 0x3f))
}else{
return encodeChar( 0xe0 | ((n >>> 12) & 0x0f))+
encodeChar(0x80 | ((n >>> 6) & 0x3f))+
encodeChar(0x80 | (n & 0x3f))
}
}
function uriDecode(source){
//192,224,240
for(var result = [], i=1;i<source.length;i+=3){
var c = parseInt(source.substr(i,2),16);
if(c>=240){//其实无效,js无法处理超出2字节的字符
c = (c & 0x07)<<18;
c += (parseInt(source.substr(i+=3,2),16) &0x3f)<<12;
c += (parseInt(source.substr(i+=3,2),16) &0x3f)<<6;
c += (parseInt(source.substr(i+=3,2),16) &0x3f);
}else if(c>=224){
c = (c & 0x0f)<<12;
c += (parseInt(source.substr(i+=3,2),16) &0x3f)<<6;
c += (parseInt(source.substr(i+=3,2),16) &0x3f);
}else if(c>=192){
c = (c & 0x1f)<<6;
c += (parseInt(source.substr(i+=3,2),16) &0x3f);
}
result.push(String.fromCharCode(c))
}
return result.join('');
}
function uriReplace(c){
if(c == '\\'){
return '/';
}else{
return decodeChar(c);
}
}
function URI(path){
if(path instanceof URI){
return path;
}
if(/^\s*[<]/i.test(path)){
path = String(path).replace(uriChars,decodeChar)
return new URI("data:text/xml,"+path);
}else{
path = String(path).replace(uriChars,uriReplace)
}
//normalize
path = path.replace(/\/\.\/|\\\.\\|\\/g,'/');
if(/^\/|^[a-z]\:\//i.test(path)){
path = 'file://'+path;
}
while(path != (path = path.replace(/[^\/]+\/\.\.\//g,'')));
var match = path.match(uriPattern);
if(match){
setupURI(this,match);
}else{
console.error("url must be absolute,"+path)
}
}
function setupURI(uri,match){
uri.value = match[0];
uri.scheme = match[1];
uri.authority = match[2];
uri.path = match[3];
uri.query = match[4];
uri.fragment = match[5];
if('data' == uri.scheme){
match = uri.value
uri.source = decodeURIComponent(match.substring(match.indexOf(',')+1));
}
}
URI.prototype = {
resolve:function(path){
path = String(path);
if( /^\s*[#<]/.test(path) ||absURIPattern.test(path)){
path = new URI(path.replace(/^\s+/,''));
return path;
}
path = path.replace(uriChars,uriReplace)
if(path.charAt() != '/'){
var p = this.path;
path = p.replace(/[^\/]*$/,path);
}
return new URI(this.scheme + ':'+(this.authority||'') + path);
},
toString:function(){
return this.value;
}
}
var btoa = this.btoa || function(bs){
var b64 = [];
var bi = 0;
var len = bs.length;
while (bi <len) {
var b0 = bs.charCodeAt(bi++);
var b1 = bs.charCodeAt(bi++);
var b2 = bs.charCodeAt(bi++);
var data = (b0 << 16) + (b1 << 8) + (b2||0);
b64.push(
b64codes[(data >> 18) & 0x3F ],
b64codes[(data >> 12) & 0x3F],
b64codes[isNaN(b1) ? 64 : (data >> 6) & 0x3F],
b64codes[isNaN(b2) ? 64 : data & 0x3F]) ;
}
return b64.join('');
}
var b64codes = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split('');
function utf8Replacer(c){
var n = c.charCodeAt();
if (n < 0x800){
return String.fromCharCode(
(0xc0 | (n >>> 6)),
(0x80|(n & 0x3f)));
}else{
return String.fromCharCode(
(0xe0 | ((n >>> 12) & 0x0f)),
(0x80 | ((n >>> 6) & 0x3f)),
(0x80 | (n & 0x3f)));
}
}
function base64Encode(data){
data = data && data.replace(/[\u0080-\uFFFF]/g,utf8Replacer)||''
data = btoa(data) ;
return encodeURIComponent(data);
}
function buildURIMatcher(pattern){
var matcher = /\*+|[^\*\\\/]+?|[\\\/]/g;
var buf = ["^"];
var m
matcher.lastIndex = 0;
while (m = matcher.exec(pattern)) {
var item = m[0];
var len = item.length;
var c = item.charAt(0);
if (c == '*') {
if (len > 1) {
buf.push(".*");
} else {
buf.push("[^\\\\/]*");
}
} else if(len == 1 && c == '/' || c == '\\') {
buf.push("[\\\\/]");
}else{
buf.push(item.replace(/[^\w]/g,quteReqExp));
}
}
buf.push("$");
return buf.join('');
}
function quteReqExp(x){
switch(x){
case '.':
return '\\.';
case '\\':
return '\\\\';
default:
return '\\x'+(0x100 + x.charCodeAt()).toString(16).substring(1);
}
}
if(typeof require == 'function'){
exports.URI=URI;
exports.buildURIMatcher = buildURIMatcher;
exports.base64Encode=base64Encode;
}
};
require.cached["lite/src/main/js/parse/xml.js"]=function (require,exports,module,__filename,__dirname){function loadLiteXML(uri,root,options){
try{
if(uri instanceof URI){
if(uri.source){
return parseXMLByText(uri.source.replace(/^[\s\ufeff]*/,uri,options))
}else if(uri.scheme == 'lite'){
var path = uri.path+(uri.query||'')+(uri.fragment || '');
path = root.resolve(path.replace(/^\//,'./'))+'';
}else{
var path = String(uri);
}
}else{
var path = String(uri);
}
if(/^[\s\ufeff]*[<#]/.test(path)){
return parseXMLByText(path.replace(/^[\s\ufeff]*/,''),root,options)
}else{
//console.log(path,/^(?:\w+\:\/\/|\w\:\\|\/).*$/.test(path))
if(/^(?:\w+\:\/\/|\w\:\\|\/).*$/.test(path)){
var pos = path.indexOf('#')+1;
var xpath = pos && path.substr(pos);
var path = pos?path.substr(0,pos-1):path;
var source = loadTextByPath(path.replace(/^file\:\/\/?/,''));
var doc = parseXMLByText(source,uri,options);
if(xpath && doc.nodeType){
doc = selectByXPath(doc,xpath);
}
return doc;
}else{
//文本看待
return parseXMLByText(path,options);
}
}
}catch(e){
console.error("文档解析失败:"+uri,e)
throw e;
}
}
function txt2xml(source){
return "<c:out c:xmlns='http://www.xidea.org/lite/core'><![CDATA["+
source.replace(/^\ufeff?#.*[\r\n]*/, "").replace(/]]>/, "]]]]><![CDATA[>")+
"]]></c:out>";
}
function addInst(xml,s){
var p = /^\s*<\?(\w+)\s+(.*)\?>/;
var m;
var first = xml.firstChild;
while(m = s.match(p)){
if(m[1] == 'xml' && first.nodeType != 7){
var pi = xml.createProcessingInstruction(m[1], m[2]);
xml.insertBefore(pi, first);
}
s = s.substring(m[0].length);
}
return xml;
}
/**
* @private
*/
function parseXMLByText(text,path,options){
text = String(text)
if(!/^[\s\ufeff]*</.test(text)){
text = txt2xml(text);
}
try{
options = Object.create(options||{});//errorHandler
options.locator={systemId:path}
options.xmlns=defaultNSMap
var doc = new DOMParser(options).parseFromString(text,"text/html")
if(!doc.querySelectorAll){//init querySelector
var elp = doc.documentElement.constructor.prototype;
elp.querySelector = querySelector;
elp.querySelectorAll = querySelectorAll;
elp = doc.constructor.prototype;
elp.querySelector = querySelector;
elp.querySelectorAll = querySelectorAll;
}
//return doc;
return addInst(doc,text);
}catch(e){
console.error("解析xml失败:",e,text);
}
}
function loadTextByPath(path){
var fs = require('fs');
try{
var text = fs.readFileSync(path,'utf-8');
}catch(e){
return fs.readFileSync(decodeURI(path),'utf-8');
}
return text;
}
function selectorInit(node){
var doc = node.ownerDocument||node;
var nw = doc.nwmatcher;
if(!nw){
nw = doc.nwmatcher = nwmatcher({document:doc});
nw.configure( { USE_QSAPI: false, VERBOSITY: true } );
}
return nw;
}
function querySelectorAll(selector){
var nodes = selectorInit(this).select(selector,this);
nodes.item = nodeListItem;
return nodes;
}
function querySelector(selector){
return selectorInit(this).first(selector,this)
}
function selectByXPath(currentNode,xpath){
var nodes = xpathSelectNodes(currentNode,xpath);
nodes.item = nodeListItem;
return nodes;
}
function nodeListItem(i){
return this[i];
}
function findXMLAttribute(el,key){
if(el.nodeType == 2){
return el.value;
}
try{
//el
var required = key.charAt() == '*';
if(required){
key = key.substr(1);
}
for(var i=1,len = arguments.length;i<len;i++){
var an = arguments[i];
if(an == '#text'){
return el.textContent||el.text;
}else{
var v = el.getAttribute(an);//ie bug: no hasAttribute
if(v || (typeof el.hasAttribute != 'undefined') && el.hasAttribute(an)){//ie bug
if(i>1 && key.charAt(0) != '#'){
console.warn(el.tagName+" 标准属性名为:"+key +'; 您采用的是:'+an);
}
return v;
}
}
}
if(required){
console.error("标记:"+el.tagName+"属性:'"+key +"' 为必要属性。");
}
}catch(e){
console.error('findXMLAttribute error:',e)
}
return null;
}
function findXMLAttributeAsEL(el){
el = findXMLAttribute.apply(null,arguments);
if(el != null){
var el2 = el.replace(/^\s*\$\{([\s\S]*)\}\s*$/,"$1")
if(el == el2){
if(el2){
console.warn("缺少表达式括弧,文本将直接按表达式返回",el);
}
}else{
el2 = el2.replace(/^\s+|\s+$/g,'');
if(!el2){
console.warn("表达式内容为空:",el);
}
el = el2;
}
}
return el;
}
function getLiteTagInfo(node){
return node.lineNumber + ','+ node.columnNumber+'@'+node.ownerDocument.documentURI;
}
var defaultNSMap = {c:'http://www.xidea.org/lite/core',h:'http://www.xidea.org/lite/html-ext'}
var nwmatcher = require('./nwmatcher');
var URI=require('./resource').URI;
var DOMParser = require('xmldom').DOMParser;
var xpathSelectNodes = require('xpath.js');
exports.defaultNSMap = defaultNSMap;
exports.querySelector = querySelector;
exports.querySelectorAll = querySelectorAll;
exports.loadLiteXML=loadLiteXML;
exports.selectByXPath=selectByXPath;
exports.findXMLAttribute=findXMLAttribute;
exports.findXMLAttributeAsEL=findXMLAttributeAsEL;
exports.getLiteTagInfo = getLiteTagInfo;
};
require.cached["lite/src/main/js/parse/nwmatcher.js"]=function (require,exports,module,__filename,__dirname){/*
* Copyright (C) 2007-2015 Diego Perini
* All rights reserved.
*
* nwmatcher.js - A fast CSS selector engine and matcher
*
* Author: Diego Perini <diego.perini at gmail com>
* Version: 1.3.6
* Created: 20070722
* Release: 20150710
*
* License:
* http://javascript.nwbox.com/NWMatcher/MIT-LICENSE
* Download:
* http://javascript.nwbox.com/NWMatcher/nwmatcher.js
*/
(function(global, factory) {
if (typeof module == 'object' && typeof exports == 'object') {
// in a Node.js environment, the nwmatcher functions will operate on
// the passed "browserGlobal" and will be returned in an object
module.exports = function (browserGlobal) {
// passed global does not contain
// references to native objects
browserGlobal.console = console;
browserGlobal.parseInt = parseInt;
browserGlobal.Function = Function;
browserGlobal.Boolean = Boolean;
browserGlobal.Number = Number;
browserGlobal.RegExp = RegExp;
browserGlobal.String = String;
browserGlobal.Object = Object;
browserGlobal.Array = Array;
browserGlobal.Error = Error;
browserGlobal.Date = Date;
browserGlobal.Math = Math;
var exports = browserGlobal.Object();
factory(browserGlobal, exports);
return exports;
};
module.factory = factory;
} else {
// in a browser environment, the nwmatcher functions will operate on
// the "global" loading them and be attached to "global.NW.Dom"
factory(global,
(global.NW || (global.NW = global.Object())) &&
(global.NW.Dom || (global.NW.Dom = global.Object())));
global.NW.Dom.factory = factory;
}
})(this, function(global, exports) {
var version = 'nwmatcher-1.3.6',
Dom = exports,
// processing context & root element
doc = global.document,
root = doc.documentElement,
// save utility methods references
slice = global.Array.prototype.slice,
string = global.Object.prototype.toString,
// persist previous parsed data
isSingleMatch,
isSingleSelect,
lastSlice,
lastContext,
lastPosition,
lastMatcher,
lastSelector,
lastPartsMatch,
lastPartsSelect,
// accepted prefix identifiers
// (id, class & pseudo-class)
prefixes = '[#.:]?',
// accepted attribute operators
operators = '([~*^$|!]?={1})',
// accepted whitespace characters
whitespace = '[\\x20\\t\\n\\r\\f]*',
// 4 combinators F E, F>E, F+E, F~E
combinators = '[\\x20]|[>+~][^>+~]',
// an+b format params for pseudo-classes
pseudoparms = '(?:[-+]?\\d*n)?[-+]?\\d*',
// CSS quoted string values
quotedvalue = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"' + "|'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'",
// skip round brackets groups
skipround = '\\([^()]+\\)|\\(.*\\)',
// skip curly brackets groups
skipcurly = '\\{[^{}]+\\}|\\{.*\\}',
// skip square brackets groups
skipsquare = '\\[[^[\\]]*\\]|\\[.*\\]',
// skip [ ], ( ), { } brackets groups
skipgroup = '\\[.*\\]|\\(.*\\)|\\{.*\\}',
// http://www.w3.org/TR/css3-syntax/#characters
// unicode/ISO 10646 characters 161 and higher
// NOTE: Safari 2.0.x crashes with escaped (\\)
// Unicode ranges in regular expressions so we
// use a negated character range class instead
encoding = '(?:[-\\w]|[^\\x00-\\xa0]|\\\\.)',
// CSS identifier syntax
identifier = '(?:-?[_a-zA-Z]{1}[-\\w]*|[^\\x00-\\xa0]+|\\\\.+)+',
// build attribute string
attrcheck = '(' + quotedvalue + '|' + identifier + ')',
attributes = whitespace + '(' + encoding + '*:?' + encoding + '+)' +
whitespace + '(?:' + operators + whitespace + attrcheck + ')?' + whitespace,
attrmatcher = attributes.replace(attrcheck, '([\\x22\\x27]*)((?:\\\\?.)*?)\\3'),
// build pseudoclass string
pseudoclass = '((?:' +
// an+b parameters or quoted string
pseudoparms + '|' + quotedvalue + '|' +
// id, class, pseudo-class selector
prefixes + '|' + encoding + '+|' +
// nested HTML attribute selector
'\\[' + attributes + '\\]|' +
// nested pseudo-class selector
'\\(.+\\)|' + whitespace + '|' +
// nested pseudos/separators
',)+)',
// placeholder for extensions
extensions = '.+',
// CSS3: syntax scanner and
// one pass validation only
// using regular expression
standardValidator =
// discard start
'(?=[\\x20\\t\\n\\r\\f]*[^>+~(){}<>])' +
// open match group
'(' +
//universal selector
'\\*' +
// id/class/tag/pseudo-class identifier
'|(?:' + prefixes + identifier + ')' +
// combinator selector
'|' + combinators +
// HTML attribute selector
'|\\[' + attributes + '\\]' +
// pseudo-classes parameters
'|\\(' + pseudoclass + '\\)' +
// dom properties selector (extension)
'|\\{' + extensions + '\\}' +
// selector group separator (comma)
'|(?:,|' + whitespace + ')' +
// close match group
')+',
// validator for complex selectors in ':not()' pseudo-classes
extendedValidator = standardValidator.replace(pseudoclass, '.*'),
// validator for standard selectors as default
reValidator = new global.RegExp(standardValidator, 'g'),
// whitespace is any combination of these 5 character [\x20\t\n\r\f]
// http://www.w3.org/TR/css3-selectors/#selector-syntax
reTrimSpaces = new global.RegExp('^' +
whitespace + '|' + whitespace + '$', 'g'),
// only allow simple selectors nested in ':not()' pseudo-classes
reSimpleNot = new global.RegExp('^(' +
'(?!:not)' +
'(' + prefixes +
'|' + identifier +
'|\\([^()]*\\))+' +
'|\\[' + attributes + '\\]' +
')$'),
// split comma groups, exclude commas from
// quotes '' "" and from brackets () [] {}
reSplitGroup = new global.RegExp('(' +
'[^,\\\\()[\\]]+' +
'|' + skipsquare +
'|' + skipround +
'|' + skipcurly +
'|\\\\.' +
')+', 'g'),
// split last, right most, selector group token
reSplitToken = new global.RegExp('(' +
'\\[' + attributes + '\\]|' +
'\\(' + pseudoclass + '\\)|' +
'\\\\.|[^\\x20\\t\\r\\n\\f>+~])+', 'g'),
// for in excess whitespace removal
reWhiteSpace = /[\x20\t\n\r\f]+/g,
reOptimizeSelector = new global.RegExp(identifier + '|^$'),
/*----------------------------- FEATURE TESTING ----------------------------*/
// detect native methods
isNative = (function() {
var re = / \w+\(/,
isnative = String(Object.prototype.toString).replace(re, ' (');
return function(method) {
return method && typeof method != 'string' &&
isnative == String(method).replace(re, ' (');
};
})(),
// NATIVE_XXXXX true if method exist and is callable
// detect if DOM methods are native in browsers
NATIVE_FOCUS = isNative(doc.hasFocus),
NATIVE_QSAPI = isNative(doc.querySelector),
NATIVE_GEBID = isNative(doc.getElementById),
NATIVE_GEBTN = isNative(root.getElementsByTagName),
NATIVE_GEBCN = isNative(root.getElementsByClassName),
// detect native getAttribute/hasAttribute methods,
// frameworks extend these to elements, but it seems
// this does not work for XML namespaced attributes,
// used to check both getAttribute/hasAttribute in IE
NATIVE_GET_ATTRIBUTE = isNative(root.getAttribute),
NATIVE_HAS_ATTRIBUTE = isNative(root.hasAttribute),
// check if slice() can convert nodelist to array
// see http://yura.thinkweb2.com/cft/
NATIVE_SLICE_PROTO =
(function() {
var isBuggy = false;
try {
isBuggy = !!slice.call(doc.childNodes, 0)[0];
} catch(e) { }
return isBuggy;
})(),
// supports the new traversal API
NATIVE_TRAVERSAL_API =
'nextElementSibling' in root && 'previousElementSibling' in root,
// BUGGY_XXXXX true if method is feature tested and has known bugs
// detect buggy gEBID
BUGGY_GEBID = NATIVE_GEBID ?
(function() {
var isBuggy = true, x = 'x' + global.String(+new global.Date),
a = doc.createElementNS ? 'a' : '<a name="' + x + '">';
(a = doc.createElement(a)).name = x;
root.insertBefore(a, root.firstChild);
isBuggy = !!doc.getElementById(x);
root.removeChild(a);
return isBuggy;
})() :
true,
// detect IE gEBTN comment nodes bug
BUGGY_GEBTN = NATIVE_GEBTN ?
(function() {
var div = doc.createElement('div');
div.appendChild(doc.createComment(''));
return !!div.getElementsByTagName('*')[0];
})() :
true,
// detect Opera gEBCN second class and/or UTF8 bugs as well as Safari 3.2
// caching class name results and not detecting when changed,
// tests are based on the jQuery selector test suite
BUGGY_GEBCN = NATIVE_GEBCN ?
(function() {
var isBuggy, div = doc.createElement('div'), test = '\u53f0\u5317';
// Opera tests
div.appendChild(doc.createElement('span')).
setAttribute('class', test + 'abc ' + test);
div.appendChild(doc.createElement('span')).
setAttribute('class', 'x');
isBuggy = !div.getElementsByClassName(test)[0];
// Safari test
div.lastChild.className = test;
return isBuggy || div.getElementsByClassName(test).length != 2;
})() :
true,
// detect IE bug with dynamic attributes
BUGGY_GET_ATTRIBUTE = NATIVE_GET_ATTRIBUTE ?
(function() {
var input = doc.createElement('input');
input.setAttribute('value', 5);
return input.defaultValue != 5;
})() :
true,
// detect IE bug with non-standard boolean attributes
BUGGY_HAS_ATTRIBUTE = NATIVE_HAS_ATTRIBUTE ?
(function() {
var option = doc.createElement('option');
option.setAttribute('selected', 'selected');
return !option.hasAttribute('selected');
})() :
true,
// detect Safari bug with selected option elements
BUGGY_SELECTED =
(function() {
var select = doc.createElement('select');
select.appendChild(doc.createElement('option'));
return !select.firstChild.selected;
})(),
// initialized with the loading context
// and reset for each different context
BUGGY_QUIRKS_GEBCN,
BUGGY_QUIRKS_QSAPI,
QUIRKS_MODE,
XML_DOCUMENT,
// detect Opera browser
OPERA = /opera/i.test(string.call(global.opera)),
// skip simple selector optimizations for Opera >= 11
OPERA_QSAPI = OPERA && global.parseFloat(global.opera.version()) >= 11,
// check Selector API implementations
RE_BUGGY_QSAPI = NATIVE_QSAPI ?
(function() {
var pattern = new global.Array(), context, element,
expect = function(selector, element, n) {
var result = false;
context.appendChild(element);
try { result = context.querySelectorAll(selector).length == n; } catch(e) { }
while (context.firstChild) { context.removeChild(context.firstChild); }
return result;
};
// certain bugs can only be detected in standard documents
// to avoid writing a live loading document create a fake one
if (doc.implementation && doc.implementation.createDocument) {
// use a shadow document body as context
context = doc.implementation.createDocument('', '', null).
appendChild(doc.createElement('html')).
appendChild(doc.createElement('head')).parentNode.
appendChild(doc.createElement('body'));
} else {
// use an unattached div node as context
context = doc.createElement('div');
}
// fix for Safari 8.x and other engines that
// fail querying filtered sibling combinators
element = doc.createElement('div');
element.innerHTML = '<p id="a"></p><br>';
expect('p#a+*', element, 0) &&
pattern.push('\\w+#\\w+.*[+~]');
// ^= $= *= operators bugs with empty values (Opera 10 / IE8)
element = doc.createElement('p');
element.setAttribute('class', '');
expect('[class^=""]', element, 1) &&
pattern.push('[*^$]=[\\x20\\t\\n\\r\\f]*(?:""|' + "'')");
// :checked bug with option elements (Firefox 3.6.x)
// it wrongly includes 'selected' options elements
// HTML5 rules says selected options also match
element = doc.createElement('option');
element.setAttribute('selected', 'selected');
expect(':checked', element, 0) &&
pattern.push(':checked');
// :enabled :disabled bugs with hidden fields (Firefox 3.5)
// http://www.w3.org/TR/html5/links.html#selector-enabled
// http://www.w3.org/TR/css3-selectors/#enableddisabled
// not supported by IE8 Query Selector
element = doc.createElement('input');
element.setAttribute('type', 'hidden');
expect(':enabled', element, 0) &&
pattern.push(':enabled', ':disabled');
// :link bugs with hyperlinks matching (Firefox/Safari)
element = doc.createElement('link');
element.setAttribute('href', 'x');
expect(':link', element, 1) ||
pattern.push(':link');
// avoid attribute selectors for IE QSA
if (BUGGY_HAS_ATTRIBUTE) {
// IE fails in reading:
// - original values for input/textarea
// - original boolean values for controls
pattern.push('\\[[\\x20\\t\\n\\r\\f]*(?:checked|disabled|ismap|multiple|readonly|selected|value)');
}
return pattern.length ?
new global.RegExp(pattern.join('|')) :
{ 'test': function() { return false; } };
})() :
true,
// matches class selectors
RE_CLASS = new global.RegExp('(?:\\[[\\x20\\t\\n\\r\\f]*class\\b|\\.' + identifier + ')'),
// matches simple id, tag & class selectors
RE_SIMPLE_SELECTOR = new global.RegExp(
BUGGY_GEBTN && BUGGY_GEBCN || OPERA ?
'^#?-?[_a-zA-Z]{1}' + encoding + '*$' : BUGGY_GEBTN ?
'^[.#]?-?[_a-zA-Z]{1}' + encoding + '*$' : BUGGY_GEBCN ?
'^(?:\\*|#-?[_a-zA-Z]{1}' + encoding + '*)$' :
'^(?:\\*|[.#]?-?[_a-zA-Z]{1}' + encoding + '*)$'),
/*----------------------------- LOOKUP OBJECTS -----------------------------*/
LINK_NODES = new global.Object({ 'a': 1, 'A': 1, 'area': 1, 'AREA': 1, 'link': 1, 'LINK': 1 }),
// boolean attributes should return attribute name instead of true/false
ATTR_BOOLEAN = new global.Object({
'checked': 1, 'disabled': 1, 'ismap': 1,
'multiple': 1, 'readonly': 1, 'selected': 1
}),
// dynamic attributes that needs to be checked against original HTML value
ATTR_DEFAULT = new global.Object({
'value': 'defaultValue',
'checked': 'defaultChecked',
'selected': 'defaultSelected'
}),
// attributes referencing URI data values need special treatment in IE
ATTR_URIDATA = new global.Object({
'action': 2, 'cite': 2, 'codebase': 2, 'data': 2, 'href': 2,
'longdesc': 2, 'lowsrc': 2, 'src': 2, 'usemap': 2
}),
// HTML 5 draft specifications
// http://www.whatwg.org/specs/web-apps/current-work/#selectors
HTML_TABLE = new global.Object({
// class attribute must be treated case-insensitive in HTML quirks mode
// initialized by default to Standard Mode (case-sensitive),
// set dynamically by the attribute resolver
'class': 0,
'accept': 1, 'accept-charset': 1, 'align': 1, 'alink': 1, 'axis': 1,
'bgcolor': 1, 'charset': 1, 'checked': 1, 'clear': 1, 'codetype': 1, 'color': 1,
'compact': 1, 'declare': 1, 'defer': 1, 'dir': 1, 'direction': 1, 'disabled': 1,
'enctype': 1, 'face': 1, 'frame': 1, 'hreflang': 1, 'http-equiv': 1, 'lang': 1,
'language': 1, 'link': 1, 'media': 1, 'method': 1, 'multiple': 1, 'nohref': 1,
'noresize': 1, 'noshade': 1, 'nowrap': 1, 'readonly': 1, 'rel': 1, 'rev': 1,
'rules': 1, 'scope': 1, 'scrolling': 1, 'selected': 1, 'shape': 1, 'target': 1,
'text': 1, 'type': 1, 'valign': 1, 'valuetype': 1, 'vlink': 1
}),
// the following attributes must be treated case-insensitive in XHTML mode
// Niels Leenheer http://rakaz.nl/item/css_selector_bugs_case_sensitivity
XHTML_TABLE = new global.Object({
'accept': 1, 'accept-charset': 1, 'alink': 1, 'axis': 1,
'bgcolor': 1, 'charset': 1, 'codetype': 1, 'color': 1,
'enctype': 1, 'face': 1, 'hreflang': 1, 'http-equiv': 1,
'lang': 1, 'language': 1, 'link': 1, 'media': 1, 'rel': 1,
'rev': 1, 'target': 1, 'text': 1, 'type': 1, 'vlink': 1
}),
/*-------------------------- REGULAR EXPRESSIONS ---------------------------*/
// placeholder to add functionalities
Selectors = new global.Object({
// as a simple example this will check
// for chars not in standard ascii table
//
// 'mySpecialSelector': {
// 'Expression': /\u0080-\uffff/,
// 'Callback': mySelectorCallback
// }
//
// 'mySelectorCallback' will be invoked
// only after passing all other standard
// checks and only if none of them worked
}),
// attribute operators
Operators = new global.Object({
'=': "n=='%m'",
'^=': "n.indexOf('%m')==0",
'*=': "n.indexOf('%m')>-1",
'|=': "(n+'-').indexOf('%m-')==0",
'~=': "(' '+n+' ').indexOf(' %m ')>-1",
'$=': "n.substr(n.length-'%m'.length)=='%m'"
}),
// optimization expressions
Optimize = new global.Object({
ID: new global.RegExp('^\\*?#(' + encoding + '+)|' + skipgroup),
TAG: new global.RegExp('^(' + encoding + '+)|' + skipgroup),
CLASS: new global.RegExp('^\\*?\\.(' + encoding + '+$)|' + skipgroup)
}),
// precompiled Regular Expressions
Patterns = new global.Object({
// structural pseudo-classes and child selectors
spseudos: /^\:(root|empty|(?:first|last|only)(?:-child|-of-type)|nth(?:-last)?(?:-child|-of-type)\(\s*(even|odd|(?:[-+]{0,1}\d*n\s*)?[-+]{0,1}\s*\d*)\s*\))?(.*)/i,
// uistates + dynamic + negation pseudo-classes
dpseudos: /^\:(link|visited|target|active|focus|hover|checked|disabled|enabled|selected|lang\(([-\w]{2,})\)|not\(([^()]*|.*)\))?(.*)/i,
// element attribute matcher
attribute: new global.RegExp('^\\[' + attrmatcher + '\\](.*)'),
// E > F
children: /^[\x20\t\n\r\f]*\>[\x20\t\n\r\f]*(.*)/,
// E + F
adjacent: /^[\x20\t\n\r\f]*\+[\x20\t\n\r\f]*(.*)/,
// E ~ F
relative: /^[\x20\t\n\r\f]*\~[\x20\t\n\r\f]*(.*)/,
// E F
ancestor: /^[\x20\t\n\r\f]+(.*)/,
// all
universal: /^\*(.*)/,
// id
id: new global.RegExp('^#(' + encoding + '+)(.*)'),
// tag
tagName: new global.RegExp('^(' + encoding + '+)(.*)'),
// class
className: new global.RegExp('^\\.(' + encoding + '+)(.*)')
}),
/*------------------------------ UTIL METHODS ------------------------------*/
// concat elements to data
concatList =
function(data, elements) {
var i = -1, element;
if (!data.length && global.Array.slice)
return global.Array.slice(elements);
while ((element = elements[++i]))
data[data.length] = element;
return data;
},
// concat elements to data and callback
concatCall =
function(data, elements, callback) {
var i = -1, element;
while ((element = elements[++i])) {
if (false === callback(data[data.length] = element)) { break; }
}
return data;
},
// change context specific variables
switchContext =
function(from, force) {
var div, oldDoc = doc;
// save passed context
lastContext = from;
// set new context document
doc = from.ownerDocument || from;
if (force || oldDoc !== doc) {
// set document root
root = doc.documentElement;
// set host environment flags
XML_DOCUMENT = doc.createElement('DiV').nodeName == 'DiV';
// In quirks mode css class names are case insensitive.
// In standards mode they are case sensitive. See docs:
// https://developer.mozilla.org/en/Mozilla_Quirks_Mode_Behavior
// http://www.whatwg.org/specs/web-apps/current-work/#selectors
QUIRKS_MODE = !XML_DOCUMENT &&
typeof doc.compatMode == 'string' ?
doc.compatMode.indexOf('CSS') < 0 :
(function() {
var style = doc.createElement('div').style;
return style && (style.width = 1) && style.width == '1px';
})();
div = doc.createElement('div');
div.appendChild(doc.createElement('p')).setAttribute('class', 'xXx');
div.appendChild(doc.createElement('p')).setAttribute('class', 'xxx');
// GEBCN buggy in quirks mode, match count is:
// Firefox 3.0+ [xxx = 1, xXx = 1]
// Opera 10.63+ [xxx = 0, xXx = 2]
BUGGY_QUIRKS_GEBCN =
!XML_DOCUMENT && NATIVE_GEBCN && QUIRKS_MODE &&
(div.getElementsByClassName('xxx').length != 2 ||
div.getElementsByClassName('xXx').length != 2);
// QSAPI buggy in quirks mode, match count is:
// At least Chrome 4+, Firefox 3.5+, Opera 10.x+, Safari 4+ [xxx = 1, xXx = 2]
// Safari 3.2 QSA doesn't work with mixedcase in quirksmode [xxx = 1, xXx = 0]
// https://bugs.webkit.org/show_bug.cgi?id=19047
// must test the attribute selector '[class~=xxx]'
// before '.xXx' or the bug may not present itself
BUGGY_QUI