Skip to content

Instantly share code, notes, and snippets.

@switer
Last active August 29, 2015 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save switer/238f0c00a84dab5545e8 to your computer and use it in GitHub Desktop.
Save switer/238f0c00a84dab5545e8 to your computer and use it in GitHub Desktop.
clouda web component parser
define('ui-importer',[ 'utils', 'loader', 'event', 'tag-parser' ], function(utils, loader, Event, tagParser) {
'use strict';
var importHistory = {};
/**
* Import ui resources and cache them
*/
function UImporter(name, opts) {
UImporter.__super__.constructor.apply(this, arguments);
this.__loader = loader;
}
utils.inherit(UImporter, Event);
utils.extend(UImporter.prototype, {
getHistoryUrls : function() {
var urls = [];
for ( var url in importHistory) {
urls.push(url);
}
return urls;
},
addCache : function(options) {
var tmpDef;
for ( var name in options.caches) {
tmpDef = options.caches[name];
tmpDef.url = utils.qualifyUrl(tmpDef.url, options.base);
if (!importHistory[tmpDef.url])
importHistory[tmpDef.url] = {};
importHistory[tmpDef.url][name] = tmpDef;
}
},
import : function(urls, urlBase, callback) {
if (!urls) {
throw ('no import uri specified');
}
if (!utils.isArray(urls)) {
urls = [ urls ];
}
if (utils.isFunction(urlBase)) {
callback = urlBase;
urlBase = false;
}
var self = this, fnlResults = {};
this.__loader.loadMultiple(urls.map(function(url) {
var absUrl = utils.qualifyUrl(url, urlBase);
return absUrl;
}).filter(function(url) {
if (importHistory[url]) {
fnlResults[url] = importHistory[url];
return false;
}
utils.log.debug('import ui: ', url);
return true;
}), function(results) {
if (results) {
for ( var url in results) {
if (importHistory[url])
continue;
if (!results[url].body) {
continue;
}
//so the first time of fetching this url
//parse it
fnlResults[url] = importHistory[url] = self.__parseElDef(results[url].body, url);
}
}
if (callback)
callback(fnlResults);
});
},
//找寻元素定义的边界,并抽取其中的所有定义内容
__parseElDef : function(text, elUrl) {
if (!text) {
return false;
}
//var tagStart = /(<[\s]*clouda-component[^>]+name=['"]([^'"\s]+)['"][^>]*>)([\s\S]*)/mi,
var tagStart = /(<[\s]*clouda-component([^>]+)>)([\s\S]*)/mi, tagEnd = /<[\s]*\/[\s]*clouda-component[\s]*>/mi;
var attributeRegex = /([^'"\s]+)=['"]([^'"]+)['"]/mig, source_ = text;
var tagStartMatcher, attributes = {}, tagStartPos, tagStartLength, tagEndPos;
function replaceAttr($0, $1, $2) {
attributes[$1] = $2;
}
var eleDefines = {};
while (true) {
tagStartMatcher = source_.match(tagStart);
if (!tagStartMatcher)
break;
//用replace来查找所有的attributes
tagStartMatcher[1].replace(attributeRegex, replaceAttr);
//计算<cloud-component>标签的长度
tagStartLength = tagStartMatcher[1].length;
//计算<clouda-component>标签的index
tagStartPos = source_.indexOf(tagStartMatcher[1]);
//寻找第一个出现的结束标签
tagEndPos = source_.search(tagEnd);
if (tagEndPos == -1) {
//语法存在错误,少一个</block>的情况
tagEndPos = source_.length;
}
//取出这一段元素定义
var content = source_.substr(tagStartPos + tagStartLength, tagEndPos - (tagStartPos + tagStartLength));
var parseRes = this.__parseDefContent(attributes, content, elUrl);
if (parseRes && parseRes.name) {
eleDefines[parseRes.name] = parseRes;
}
//截取剩余模板代码,只取当前匹配的结束标签的后面(即不考虑定义的嵌套了)
source_ = source_.substr(tagEndPos);
}
return eleDefines;
},
//解析每个元素定义中的具体内容
__parseDefContent : function(attributes, text, elUrl) {
var name = attributes.name;
if (!name) {
utils.log.error('No name specified for clouda-component');
return false;
}
return utils.extend({
name : name,
attributes : attributes,
url : elUrl
}, this.__parseResource(text, utils.getUrlDir(elUrl)));
},
__parseResource : function(text, urlBase) {
var templateMatch, styleMatch, scriptMatch, self = this;
var globalResources = [];
//read link content synchronously
function readLink(src) {
return self.__loader.loadSyncly(utils.qualifyUrl(src, urlBase));
}
//just template content
templateMatch = tagParser.parseSingleTagInfo(text, 'template').content;
//remove comments
templateMatch = templateMatch.replace(/<!--[^]*?-->/gm, '');
//handle style
styleMatch = [];
tagParser.parseTagInfo(templateMatch, 'style|link', [ 'global', 'id', 'src', 'rel', 'href' ]).forEach(function(styleInfo) {
//remove style from templateMatch
templateMatch = templateMatch.replace(styleInfo.outerContent, '');
if (utils.isString(styleInfo.attributes.global)) {
var globalRes = {};
if (styleInfo.attributes.href) {
var absUrl = utils.qualifyUrl(styleInfo.attributes.href, urlBase);
utils.extend(globalRes, {
attributes : {
href : absUrl
}
});
styleInfo.outerContent = styleInfo.outerContent.replace(styleInfo.attributes.href, absUrl);
styleInfo.attributes.href = absUrl;
}
utils.extend(globalRes, {
id : styleInfo.attributes.global || styleInfo.attributes.id || styleInfo.attributes.href,
type : styleInfo.tag.toLowerCase(),
html : styleInfo.outerContent
});
if (!globalRes.id)
globalRes.id = globalRes.type + ': ' + utils.random(10);
globalResources.push(globalRes);
} else {
if (styleInfo.tag == 'LINK') {
if (styleInfo.attributes.rel == 'stylesheet') {
if (!styleInfo.attributes.href)
return;
styleMatch.push(readLink(styleInfo.attributes.href));
}
} else {
if (styleInfo.attributes.src) {
styleMatch.push(readLink(styleInfo.attributes.src));
} else {
styleMatch.push(styleInfo.content);
}
}
}
});
styleMatch = styleMatch.join('\n');
//handle script
scriptMatch = [];
tagParser.parseTagInfo(text, 'script', [ 'global', 'id', 'src' ]).forEach(function(scriptInfo) {
//remove script
templateMatch = templateMatch.replace(scriptInfo.outerContent, '');
if (utils.isString(scriptInfo.attributes.global)) {
var globalRes = {};
if (scriptInfo.attributes.src) {
var absUrl = utils.qualifyUrl(scriptInfo.attributes.src, urlBase);
utils.extend(globalRes, {
attributes : {
src : absUrl
}
});
scriptInfo.outerContent = scriptInfo.outerContent.replace(scriptInfo.attributes.src, absUrl);
scriptInfo.attributes.src = absUrl;
}
utils.extend(globalRes, {
id : scriptInfo.attributes.global || scriptInfo.attributes.id || scriptInfo.attributes.src,
type : 'script',
html : scriptInfo.outerContent
});
if (!globalRes.id)
globalRes.id = globalRes.type + ': ' + utils.random(10);
globalResources.push(globalRes);
} else {
if (scriptInfo.attributes.src) {
var scriptBody = readLink(scriptInfo.attributes.src);
if (scriptBody)
scriptMatch.push(scriptBody);
} else {
scriptMatch.push(scriptInfo.content);
}
}
}, this);
scriptMatch = scriptMatch.join('\n');
return {
template : templateMatch,
//此处合并所有Style,依赖前提是style所属位置不影响最终渲染效果
style : styleMatch,
scriptLiteral : scriptMatch,
globalResources : globalResources
};
}
});
return new UImporter('clouda');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment