Last active
August 29, 2015 14:04
-
-
Save switer/238f0c00a84dab5545e8 to your computer and use it in GitHub Desktop.
clouda web component parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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