ClassBase.define({
init:function(){
this.bindMessage();
},
bindMessage:function(){
if(window._htmlSafeHasBindMessage){return;}
window._htmlSafeHasBindMessage = true;
// html内容安全预览(确保安全,禁用当前站点cookie;支持内部js执行);
window.addEventListener('message',function(e){
//console.error('kod-recevice-message',e);
if(!e || !_.isObject(e.data) || e.data.type != 'iframe.event' ){return;}
// 页面点击等事件; {type:'iframe.event',event:'mousedown'}
// 连接跳转: {type:'iframe.event',event:'iframeLinkHref',url:}
// 内部嵌入iframe: {type:'iframe.event',event:'iframeChildLoad',url:,uuid:} kod层接收到消息进行处理,处理完成后通知到iframe窗口;
$('iframe').each(function(){ // html文件预览,内部点击事件通知到该层(对话框置顶,右键菜单等处理)
if($(this).get(0).contentWindow != e.source) return;
$(this).trigger(e.data.event,e.data);
});
});
},
support:function(){
if(!window.fetch || !window.MutationObserver){return false;}
var frame = document.createElement("iframe");
if(!("sandbox" in frame) || !("srcdoc" in frame)){return false;}
if($.browserIS.ios){// ios webview中不支持;Safari支持;
var ua = navigator.userAgent.toLowerCase();
if(ua.indexOf('quark')){return false;}
if(ua.indexOf('weixin')){return false;}
}
return true;
},
// 禁用cookie,去除 allow-same-origin; iframe内请求无法缓存;
// https://stackoverflow.com/questions/67680940/iframe-sandbox-is-not-caching-my-js-script
iframeAttr:function(){
var allow = "midi; geolocation; camera *; microphone; camera; display-capture; encrypted-media; clipboard-read; clipboard-write;";
var sandbox = 'allow-modals allow-orientation-lock allow-forms allow-scripts allow-popups allow-pointer-lock allow-top-navigation-by-user-activation allow-downloads';
return "sandbox='"+sandbox+"' allow='"+allow+"' allowfullscreen allowpaymentrequest";
},
iframe:function(){
return '';
},
getTruePathCache:{},
fileContentCache:{},
loadContent:function($iframe,filePath,pathModel,contentSet,args){
var self = this;
var linkPre = pathModel.urlMake('fileOutBy','viewToken='+(G.kod.viewToken || ''));
var apiHostTo = window.API_HOST;// chrome xhr跨域时,发送options请求进行预检处理;需要带上index.php,否则会被nginx拦截报错;
if(apiHostTo.indexOf('/?') == apiHostTo.length - 2){
apiHostTo = apiHostTo.replace('/?','/index.php?');
}
linkPre = linkPre.replace(window.API_HOST,apiHostTo);
if(_.startsWith(filePath,G.kod.APP_HOST_LINK) &&
_.includes(filePath,'explorer/share/fileOut')){
var urlInfo = $.parseUrl(filePath); // 外链分享,文件嵌入情况;
if(urlInfo.params.path){filePath = urlDecode(urlInfo.params.path);}
}
var cacheKeyAll = '';// 缓存处理,文件变更时支持;
if(args && args._fileInfo && args._fileInfo.size){
cacheKeyAll = '[size-'+args._fileInfo.size+',time='+args._fileInfo.modifyTime+']';
}
var getTruePath = function(thePath,addPath,callback){
var cache = self.getTruePathCache,cacheKey = cacheKeyAll+thePath+'__'+(addPath || '');
if(cache[cacheKey]){return callback && callback(cache[cacheKey],addPath);}
$.ajax({
url:pathModel.urlMake('fileOutBy'),
type:'POST',dateType:'json',
data:{path:thePath,add:addPath,type:'getTruePath'},
success:function(data){
if(!_.isObject(data) || !data.code || !data.data){return;}
cache[cacheKey] = data.data;
callback && callback(data.data,addPath);
}
});
};
var fileContent = function(thePath,addPath,callback){
var cache = self.fileContentCache,cacheKey = cacheKeyAll+'__'+thePath+'__'+(addPath || '');
if(cache[cacheKey]){return callback && callback(cache[cacheKey]);}
pathModel.fileContent({path:thePath},function(content){
var newContent = self.parseContent(content,thePath,linkPre,addPath);
cache[cacheKey] = newContent;
callback && callback(newContent);
});
}
// 页面跳转,相对路径处理(先获取,再以新的作为当前文件名)
$iframe.unbind('iframeLinkHref').bind('iframeLinkHref',function(e,eventData){
var thePath = $(this).attr('data-file-path');
var addPath = eventData.url;
if(!thePath){return;}
getTruePath(thePath,addPath,function(truePath){
reloadContent(truePath,addPath);
});
});
// 页面嵌入子iframe; 数据处理,处理完成后通知到iframe的window;
$iframe.unbind('iframeChildLoad').bind('iframeChildLoad',function(e,eventData){
var thePath = $(this).attr('data-file-path');
var addPath = eventData.url;
var iframeWindow = $iframe.get(0).contentWindow;
if(!thePath || !iframeWindow){return;}
getTruePath(thePath,addPath,function(truePath){
fileContent(truePath,addPath,function(content){
iframeWindow.postMessage({
type:'parent.event',event:'iframeChildLoadSuccess',
url:truePath,uuid:eventData.uuid,content:content
},'*');
});
});
});
var loadTimeout = false;
var reloadContent = function(thePath,addPath,theContent){
$iframe.attr('data-file-path',thePath);
var setContent = function(newContent){
$iframe.attr('srcdoc',newContent).trigger('load'); // iOS-微信中无效;
$iframe.unbind('iframeMainload').bind('iframeMainload',function(){
clearTimeout(loadTimeout);
});
clearTimeout(loadTimeout);
loadTimeout = setTimeout(function(){ // 一段时间未接收到载入消息,则重新设置值(safari下偶尔白屏[未载入引起])
$iframe.attr('srcdoc',newContent);
},500);
};
setContent('');//$iframe.attr('src','about:blank');
if(theContent){return setContent(self.parseContent(contentSet,thePath,linkPre,addPath));}
fileContent(thePath,addPath,setContent);
};
reloadContent(filePath,false,contentSet);
},
parseContent:function(content,filePath,linkPre,addPath){
var isPathUrl = this.isPathUrl,pathUrlParse = this.pathUrlParse,pathTrue = this.pathTrue;
var urlFilterCurrent = eval('('+this.urlFilter.toString()+')'); // 当前环境作用域; 可以直接使用变量及方法;
var domSrcMap = [
{tag:'script',key:'src',replaceType:'script-import',typeMatch:'module'},
{tag:'link',key:'href',replaceType:'css-import'},
{tag:'img',key:'src',cros:true,name:'image'},
{tag:'source',key:'src',cros:true},
{tag:'video',key:'src',cros:true},
{tag:'audio',key:'src',cros:true}
];
var $dom = $((new DOMParser()).parseFromString(content,'text/html')); // 忽略报错
var basePath = $dom.find('base').attr('href') || '';// 相对路径处理(跳转,引用都需要追加)
var contentNew = this.htmlContentParse(content,domSrcMap,urlFilterCurrent);
// html内容展示,src相对路径处理(url处理: './test.js','test.js','../img/test.js');
var urlPage = window.location.href.replace(/\?.*/,'').replace(/#.*/,'') + (addPath || '');
var locationNow = new window.URL(urlPage); // 保留打开页面的锚点及参数; js中可能会作为逻辑处理用到
locationNow = _.pick(locationNow,'href,origin,protocol,host,hostname,port,pathname,search,hash'.split(','));
// console.log(999,{linkPre,filePath,basePath,addPath,location,contentNew},{urlPage,locationNow},JSON.stringify(locationNow));
var fileEncode = '';
if(window.kodFileOutDecode){ // 加解密处理;
//fileEncode = ';window.kodFileOutDecode = ('+kodFileOutDecode.toString()+');window.kodFileOutDecode();';
}
var script = `
(function(){
var linkPre = "${linkPre}",filePath ="${filePath}",basePath="${basePath}";
window._hash = "`+(addPath || '')+`";window._location_ = `+JSON.stringify(locationNow)+`
var domSrcMap =`+jsonEncode(domSrcMap)+`;
var urlFilter =`+this.urlFilter.toString()+`;
var isPathUrl =`+this.isPathUrl.toString()+`;
var pathTrue =`+this.pathTrue.toString()+`;
var pathUrlParse =`+this.pathUrlParse.toString()+`;
var htmlContentParse =`+this.htmlContentParse.toString()+`;
(`+this.hookScript.toString()+`)();
(`+this.hookScriptProfill.toString()+`)();
(`+this.hookScriptEvent.toString()+`)();`+fileEncode+`
})();`;
return ''+contentNew;
},
// ================================================================
// ====================以下为运行在iframe中的代码====================
// ================================================================
// 是否为绝对路径url(http:,https:,data:,blob:,#,/,javascript:), 其他则为相对路径url(../xxx,./xxx,xxx/a.html,/aa/bb.xxx);
isPathUrl:function(url){
if(!url || typeof(url) != 'string'){return false;}
var ignorePre = ['http:','https:','/','data:','blob:','#','javascript:'];
for(var i = 0; i < ignorePre.length; i++){
if(url.substr(0,ignorePre[i].length) == ignorePre[i]){return false;}
}
return true;
},
pathUrlParse:function(url,_basePath){ // 如果是路径,去除锚点及参数;
var _url = url;
if(url=='./'){url = './index.html';}
var urlClear = function(theUrl){
if(theUrl.indexOf('?') >0){theUrl = theUrl.substr(0,theUrl.indexOf('?'));}
if(theUrl.indexOf('#') >0){theUrl = theUrl.substr(0,theUrl.indexOf('#'));}
return theUrl.replace('/./','/');
};
if(url.substr(0,1) == '?'){
url = './index.html'+url;
if(window._hash){
var arr = urlClear(_hash).split('/');
url = './'+arr[arr.length - 1] + _url;
}
}
//url = urlClear(url);
if(url.substr(-1,1) == '/'){url += 'index.html';} // 目录结尾自动处理为文件;
url = url.replace(/\/+/g,'/');
if(_basePath){url = _basePath + url;}
return url;
},
pathTrue:function(thePath){
if(!thePath || typeof(thePath) != 'string') return '';
thePath = thePath.replace(/\/+/g,'/').replace(/(\/\.\/)+/g,'/');
thePath = thePath.replace(/\/+/g,'/').replace(/(\/\.\/)+/g,'/');
if(thePath.indexOf('../') === -1 ) return thePath;
var arr = thePath.split('/');
for(var i = 0; i <= arr.length; i++){
if(arr[i] !== '..'){continue;}
for(var j = i; j>=0;j--) {
if(arr[j] === '.' || arr[j] === '..' || arr[j] === -1){continue;}
if(arr[j] === ''){arr[i] = -1;break;} // 以/开头处理到根,则直接置空
arr[i] = -1;arr[j] = -1;break; // 抵消一对;
}
}
var newPathArr = [];
for(var i = 0; i < arr.length; i++){
if(arr[i] === -1){continue;}
newPathArr.push(arr[i]);
}
var newPath = newPathArr.join('/');
if(newPath.indexOf('./../') === 0){
newPath = '../'+newPath.substr('./../'.length);
}
return newPath.replace('/./','/');
},
urlFilter:function(url,replaceType){
if(!url || typeof(url) != 'string'){return url;}
var paramAdd = '',urlOld = url;// 保持之前参数;
if( url && url.substr(0,4) == 'http' &&
url.indexOf('&_s_=/') > 0 && url.indexOf('&_s_=/&') === -1){ // 根据url拼接出的url处理;
url = url.substr(url.indexOf('&_s_=/') + '&_s_=/'.length);
}
if(!isPathUrl(url)){return url;}
if(url.indexOf('?') > 0 && url.substr(0,4) != 'http'){paramAdd = '&_s_=/&'+url.substr(url.indexOf('?')+1);}
if(url.indexOf('#') > 0 && url.substr(0,4) != 'http'){paramAdd = '#'+url.substr(url.indexOf('#')+1);}
url = pathUrlParse(url,basePath);
var result = linkPre;
if(url.indexOf('.wasm.js') != -1){replaceType = 'script-wasm';} // wasm 引入; "*.wasm" 将被自动替换;
if(replaceType){result += '&replaceType='+replaceType;}
var urlPath = filePath,urlAdd = url;
// 资源引用路径,相对路径转换; 多个相同资源共同缓存(暂时无效; iframe中allow-same-origin请求无法缓存, orgin为null)
var urlPathArr = urlPath.replace(/\/*$/,'').replace(/^\/*/,'').split('/');
var urlAddArr = (urlAdd || '').split('../');
if( urlAddArr.length && urlPathArr.length && urlPathArr.length > urlAddArr.length ){
var urlAddArr = urlAdd.split('/'),lastFile = urlAddArr.pop();
urlPath = pathTrue(urlPath + '/../' + urlAddArr.join('/')) + '/' + lastFile;
urlAdd = lastFile;
}
result += '&path='+encodeURIComponent(urlPath)+"&add="+encodeURIComponent(urlAdd)+paramAdd;
// console.error('urlFilter',[urlPath,urlAdd],[urlOld,url,result,paramAdd]);
return result;
},
htmlContentParse:function(html,domSrcMap,_urlFilter){
var contentNew = html; // 匹配字符串后替换;
var doc = (new DOMParser()).parseFromString(html,'text/html'); // 忽略报错
if(!doc || !doc.getElementsByTagName){return contentNew;}
var urlFilter = _urlFilter;
var escapeRegex = function(str){return str.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');}
domSrcMap.forEach(function(item){
var nodes = doc.getElementsByTagName(item.tag);
if(!nodes || !nodes.length){return;}
var nodeArr = Array.from(nodes);
nodeArr.forEach(function(node){
var url = node.getAttribute(item.key);
if(!url){return;}
var replaceType = item.replaceType || '';
if(item.typeMatch){replaceType = node.getAttribute('type') == 'module' ? replaceType:'';}
var urlNew = urlFilter(url,replaceType);
if(url == urlNew){return;}
var reg = new RegExp(item.key+'\s*=\s*[\'"]'+escapeRegex(url)+'[\'"]','g');
contentNew = contentNew.replace(reg,item.key+'="'+urlNew+'" _src_="'+url+'"');
// if(item.cros){node.setAttribute("crossOrigin",'anonymous');}
});
});
var getElementsByAttribute = function(attr,value,fromNode,result){
if(!result){result = [];}
if(fromNode && fromNode.hasAttribute && fromNode.hasAttribute(attr) &&
(value === false || fromNode.getAttribute(attr) == value)
){result.push(fromNode);}
if(!fromNode || !fromNode.children){return result;}
var children = fromNode.children;
for(var i = children.length; i--;){
getElementsByAttribute(attr,value,children[i],result);
}
return result;
};
// html中iframe提前替换; 避免首次载入url错误的情况;
var nodes = doc.getElementsByTagName('iframe');
var nodeArr = Array.from(nodes);
nodeArr.forEach(function(node){
var url = node.getAttribute('src');
if(!url || node.getAttribute('_src_')){return;}
var reg = new RegExp('src\s*=\s*[\'"]'+escapeRegex(url)+'[\'"]','g');
contentNew = contentNew.replace(reg,'src="" _src_="'+url+'"');
});
// console.error("htmlContentParse:",html,contentNew);
// html中style background-image提前处理;
var nodes = getElementsByAttribute('style',false,doc);
nodes.forEach(function(node){
var style = node.getAttribute('style');
if(!style || !style.indexOf("url(") ){return;}
var styleNew = style.replace(/url\s*\(\s*(['"]*.*?['"]*)\s*\)/g,function(str,url){
var q = url.substr(0,1);
if(q != "'" && q != '"'){q = '';}
if(q){url = url.substr(1,url.length -2);}
return 'url('+q+urlFilter(url)+q+')';
});
contentNew = contentNew.replace(style,styleNew);
});
contentNew = contentNew.replace('window.location','window._location_');
return contentNew;
},
/**
* url相对路径引用
*
* url处理(img.src,link.href,script.src; xhr,fetch; worker.src;worker中fetch; wasm.js)
* css处理: 图片引用处理,import支持处理
* js处理: module/import支持;import内部再import自适应url;引入wasm处理;worker跨域处理;worker内部fetch url自适应处理;
*
* 跳转处理: 支持[a点击,a设置href,window.open,reload];暂不支持[hash跳转,location.href(无法覆盖,无法setter)]
* iframe处理: 支持[内置;src,attr,插入html]; 仅支持两层;
* 不支持: window.location 系列处理(hash,href,pathname,replace; 浏览器做了限制;)
*/
hookScript:function(){
// css中图片引用处理(运行时,js修改元素样式)
var urlFilterImage = function(v){
if(!v || typeof(v) != 'string'){return v;}
if(!v.match(/url\s*\(\s*['"]*(.*?)['"]*\s*\)/g)){return v;}
var result = v.replace(/url\s*\(\s*['"]*(.*?)['"]*\s*\)/g,function(str,url){
if(!url){return str;}
return 'url("'+urlFilter(url)+'")';
});
return result;
};
// import 'src.js'; import Stats from 'src.js';
var urlFilterScriptImport = function(node){
var html = node.innerText || node.innerHTML;
if(!html || html.indexOf('from') === -1){return;}
var result = html.replace(/\s+from\s+(['"].*?['"])/g,function(str,url){
var q = url.substr(0,1);
return str.replace(url,q+urlFilter(url.substr(1,url.length-2),'script-import')+q);
});
result = result.replace(/import\s+(['"].*?['"])/g,function(str,url){
var q = url.substr(0,1);
return str.replace(url,q+urlFilter(url.substr(1,url.length-2),'script-import')+q);
});
node.innerHTML = result;
};
var urlFilterScriptImportMap = function(node){
var html = node.innerText || node.innerHTML;
if(!html){return;}
var result = html.replace(/['"]\s*:\s*(['"].*?['"])/g,function(str,url){
var q = url.substr(0,1);
return str.replace(url,q+urlFilter(url.substr(1,url.length-2),'script-import')+q);
});
node.innerHTML = result;
};
var domSrcMapObj = {};
domSrcMap.forEach(function(item){domSrcMapObj[item.tag] = item;});
var nodeCheck = function(node){
if(node.getAttribute && node.getAttribute('style')){ // style中图片处理;
node.setAttribute('style',urlFilterImage(node.getAttribute('style')));
}
if(node.tagName == 'SCRIPT'){ // js代码中url引用处理;
if(node.innerText){
if(node.getAttribute('type') == 'module'){urlFilterScriptImport(node);}
if(node.getAttribute('type') == 'importmap'){urlFilterScriptImportMap(node);}
}else{
setTimeout(function(){ // 节点添加过程中,未添加完成情况,内容获取为空的情况;
if(node.getAttribute('type') == 'module'){urlFilterScriptImport(node);}
if(node.getAttribute('type') == 'importmap'){urlFilterScriptImportMap(node);}
},0);
}
}
if(node.tagName == 'IFRAME'){
iframeReset(node,['src',node.getAttribute('_src_') || node.getAttribute('src')]);
}
var mapItem = domSrcMapObj[(node.tagName || '').toLowerCase()];
if(mapItem && node && node[mapItem.key]){
var theUrl = node.getAttribute(mapItem.key);
var newUrl = urlFilter(theUrl,mapItem.replaceType || '');;
node[mapItem.key] = newUrl;
if(node.tagName == 'SCRIPT' && theUrl != newUrl && !node.getAttribute('_src_')){
node.setAttribute('_src_',theUrl); // 保留之前url; getter中重写;
}
if(mapItem.cros){node.crossOrigin = "anonymous";}
}
if(!node.childNodes || node.childNodes.length == 0){return;}
node.childNodes.forEach(nodeCheck);// 子节点处理;通过html天假多层结构;
};
// dom对象创建监听(针对初始html内容, innerHTML修改内容等情况处理)
var observer = new MutationObserver(function(mutationsList,mutationObserver){
mutationsList.forEach(function(mutation){
if(!mutation.addedNodes) return;
mutation.addedNodes.forEach(nodeCheck);
});
});
observer.observe(document,{childList:true,attributes:true,subtree:true});
functionHookSetter = function(target,key,setterFunc,getterFunc){
if(!window.Object || !Object.getOwnPropertyDescriptor){return;}
var descriptor = Object.getOwnPropertyDescriptor(target,key);
if(!descriptor){
if(!target.setAttribute && !target.setProperty){console.error(this,key);return;}
descriptor = {};
if(setterFunc){
descriptor.set = function(){
var result = setterFunc.apply(this,arguments);
if(result === false){return;} // 返回false不处理;
if(this.setProperty){return this.setProperty(key,result[0]);}
if(this.setAttribute){return this.setAttribute(key,result[0]);}
};
}
if(getterFunc){
descriptor.get = function(){
var result = getterFunc.apply(this,arguments);
if(result){return result;} //只要值,则作为返回值;
if(this.getProperty){return this.getProperty(key);}
if(this.getAttribute){return this.getAttribute(key);}
};
}
}else{
var _setter = descriptor["set"],_getter = descriptor["get"];
if(setterFunc){
descriptor["set"] = function(){
var result = setterFunc.apply(this,arguments);
if(result === false){return;} // 返回false不处理;
return _setter.apply(this,result || arguments);
}
}
if(getterFunc){
descriptor["get"] = function(){
var result = getterFunc.apply(this,arguments);
if(result){return result;} //只要值,则作为返回值;
return _getter.apply(this,arguments);
}
}
}
descriptor.configurable = true;
Object.defineProperty(target,key,descriptor);
};
functionHook = function(target,method,beforeFunc,afterFunc){
if(!target || !method || !target[method]){return;}
var _theMethod = target[method];
target[method] = function(){
var args = arguments;
if(beforeFunc){
var newArgs = beforeFunc.apply(this,args);
if( newArgs === false ) return newArgs;
args = newArgs === undefined ? args : newArgs; //没有返回值则使用结果;
}
var result = _theMethod.apply(this,args);
if( afterFunc ){
var newResult = afterFunc.apply(this,[result,args]);
result = newResult === undefined ? result : newResult;//没有返回值则使用结果
}
return result;
}
};
domSrcMap.forEach(function(mapItem){
var nodeName = mapItem.name || mapItem.tag;
nodeName = nodeName.substr(0,1).toUpperCase() + nodeName.substr(1);
var nodeElement = window['HTML'+nodeName+'Element'];
if(!nodeElement){return;}
functionHookSetter(nodeElement.prototype,mapItem.key,function(src){
var replaceType = mapItem.replaceType || '';
if(mapItem.typeMatch ){replaceType = this.getAttribute('type') == 'module' ? replaceType:'';}
if(mapItem.cros){this.crossOrigin = "anonymous";}
arguments[0] = urlFilter(arguments[0],replaceType);return arguments;
},function(){
// 获取src重写使用原始内容; // 兼容js文件内代码会通过document.currentScript获取到标签,获取url进行相关操作等;
if(this.getAttribute('_src_')){return this.getAttribute('_src_');}
});
});
functionHookSetter(CSSStyleDeclaration.prototype,'background',function(src){
arguments[0] = urlFilterImage(arguments[0]);return arguments;
});
functionHookSetter(CSSStyleDeclaration.prototype,'background-image',function(src){
arguments[0] = urlFilterImage(arguments[0]);return arguments;
});
functionHook(CSSStyleDeclaration.prototype,'setProperty',function(k,v){
arguments[1] = urlFilterImage(arguments[1]);return arguments;
});
functionHook(HTMLElement.prototype,'setAttribute',function(k,v){
if(k == 'style'){arguments[1] = urlFilterImage(arguments[1]);return arguments;}
});
functionHookSetter(HTMLElement.prototype,'style',function(src){
arguments[0] = urlFilterImage(arguments[0]);return arguments;
});
var has = function(str,find){
if(!str || typeof(str) != 'string'){return false;}
return str.indexOf(find) !== -1;
};
// 通过a标签获取url的情况支持;(aceEditor,worker; a标签设置js再获取url的情况)
functionHookSetter(HTMLAnchorElement.prototype,'href',function(href){
if(href && ( has(href,'.js') || has(href,'.css') )){
arguments[0] = urlFilter(href);
}
return arguments;
});
// 通过a标签获取host等情况处理(angular,载入html白名单处理情况)
var linkGetterHost = 'protocol,host,port,hostname'.split(',');
linkGetterHost.forEach(function(key){
functionHookSetter(HTMLAnchorElement.prototype,key,false,function(){
if(!this.parentNode){return _location_[key];} // 如果a节点未添加到dom中,则使用默认url
});
});
// iframe 处理(仅处理相对路径引用的文件; 当前页面发出消息(带上uuid,获取内容及路径信息; kod页面进行处理)
functionHook(HTMLIFrameElement.prototype,'setAttribute',function(k,v){
if(k == 'src'){arguments[1] = iframeReset(this,arguments);return arguments;}
});
functionHookSetter(HTMLIFrameElement.prototype,'src',function(src){
arguments[0] = iframeReset(this,['src',src]);return arguments;
});
var iframeReset = function(node,args){
if(!isPathUrl(args[1]) || args[1] == 'blank.html'){return args[1];}
var url = pathUrlParse(args[1],basePath);
node.setAttribute('_view_src',url);
var uuid = node.getAttribute('_view_uuid');
if(!uuid){
uuid = Math.ceil(Math.random()*1000000);
node.setAttribute('_view_uuid',uuid);
}
window.parent.postMessage({type:'iframe.event',event:'iframeChildLoad',url:url,uuid:uuid},'*');
return '';
};
var requestText = function(url,callback){
fetch(url).then(function(response){
return response.text();
}).then(function(text){
callback && callback(text);
});
};
var noop = function(){};
// worker 请求中 fetch处理;
var proxyWorker = function(url,type,name){
var url = urlFilter(url,'script-import');
var proxyMethods = 'addEventListener,removeEventListener,postMessage,terminate'.split(','),proxyMethodsCall = {};
var obj = {onmessage:noop,onerror:noop};
var _each = function(objs,method){
if(!objs){return;}
for(var k in objs){
(function(key){method(objs[key],key);})(k);
}
};
var _includes = function(arr,v){return arr.indexOf(v) !== -1;};
var _toArray = function(arr){
var res = [];
for(var i = 0; i < arr.length; i++){res.push(arr[i]);}
return res;
};
_each(proxyMethods,function(funcName){
obj[funcName] = function(){
var args = _toArray(arguments);
//console.error(222,funcName,obj.self,args,proxyMethodsCall);
if(obj.self){return obj.self[funcName].apply(obj.self,args);}
if(!proxyMethodsCall[funcName]){proxyMethodsCall[funcName] = [];}
proxyMethodsCall[funcName].push(args);
}
});
var loadAfter = function(objTarget){
obj.self = objTarget;
objTarget.onmessage = obj.onmessage;
objTarget.onerror = obj.onerror; // 设置调用覆盖临时变量
_each(proxyMethodsCall,function(calls,method){
_each(calls,function(args){
obj[method].apply(obj.self,args);
});
});
proxyMethodsCall = [];
};
var scriptAdd = `
;(function(){
var linkPre = "${linkPre}",filePath ="${filePath}",basePath="${basePath}";
var _location = `+JSON.stringify(_location_)+`
var urlFilter = `+urlFilter.toString()+`;
var isPathUrl = `+isPathUrl.toString()+`;
var pathTrue = `+pathTrue.toString()+`;
var pathUrlParse =`+pathUrlParse.toString()+`;
var htmlContentParse =`+htmlContentParse.toString()+`;
var _fetch = self.fetch,_ajaxOpen = XMLHttpRequest.prototype.open;
self.fetch = function(url){
arguments[0] = urlFilter(arguments[0]);
return _fetch.apply(this,arguments);
};
XMLHttpRequest.prototype.open = function(){
arguments[1] = urlFilter(arguments[1]);
return _ajaxOpen.apply(this,arguments);
}
})();`;
requestText(urlFilter(url,'script-import'),function(text){
var worker = false;
var blob = new Blob([scriptAdd+text],{type:'application/javascript'});
var blobUrl = URL.createObjectURL(blob);
// console.error('proxyWorker:',blobUrl,url,text,scriptAdd+text);
if(type == 'Worker'){worker = new _Worker(blobUrl);}
if(type == 'SharedWorker'){worker = new _SharedWorker(blobUrl,name);}
loadAfter(worker);
});
return obj;//返回临时变量,worker构造后需要覆盖;
};
var _Worker = window.Worker;window.Worker = function(url){return proxyWorker(url,'Worker');};
var _SharedWorker = window.SharedWorker;window.SharedWorker = function(url,name){return proxyWorker(url,'SharedWorker',name);};
var _Audio = window.Audio;window.Audio = function(url){
var node = new _Audio(urlFilter(url));
node.setAttribute("crossOrigin",'anonymous');
return node;
}
var _fetch = window.fetch; window.fetch = function(url){
if(typeof(url) == 'string' || !url){arguments[0] = urlFilter(url);} // theUrl 支持字符串,URL对象,Request对象
if(url instanceof window.URL){arguments[0] = new URL(urlFilter(url.href));}
var options = {method:url.method,mode:url.mode,cache:url.cache,headers:url.headers,body:url.body,credentials:url.credentials};
if(url instanceof window.Request){
arguments[0] = new Request(urlFilter(url.url),options);
}
return _fetch.apply(this,arguments);
};
// chrome xhr跨域options目录预检处理;需要带上index.php
var _ajaxOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(){
arguments[1] = urlFilter(arguments[1]);
if(arguments[1] === _location_.href){// 获取url位自身时,采用当前文件名(document.baseURI的情况兼容)
arguments[1] = linkPre + '&path='+encodeURIComponent(filePath+'/t.html')+'&add='; // 需要到上层;
}
var result = _ajaxOpen.apply(this,arguments);
this.withCredentials = false;
return result;
},
// functionHookSetter(document,'baseURI',false,function(){return filePath;});
functionHook(document,'write',function(code){
arguments[0] = htmlContentParse(code,domSrcMap,urlFilter);return arguments;
});
// 阻止事件冒泡调用标记处理;
functionHook(UIEvent.prototype,'stopPropagation',function(code){
this._stopEvent = true;return arguments;
});
functionHook(UIEvent.prototype,'preventDefault',function(code){
this._stopEvent = true;return arguments;
});
// 引用路径转换处理; 相对路径转换;
var pathUrlTrue = function(url,add){
if(add && !isPathUrl(add)){return add;}
if(!url || !add){return url;}
var char = add.substr(0,1);
if(char == '/'){return _location_.origin + add;}
if(char == '#' || char == '?'){return url+add;}
var urlTemp = url.replace('://','_:@@_');
if(url.substr(-1,1) == '/'){urlTemp += 'index.html/';}
var result = pathTrue(urlTemp+'/../'+add).replace('_:@@_','://');
return result;
};
var urlMain = _location_.href.replace(/#.*$/, "" );
var urlPage = pathUrlTrue(urlMain,_hash);
// console.log(444,urlPage,urlPage);
// 兼容 获取window.location.href无法覆盖的情况; 扩展到字符串替换上(jquery-ui等tab锚点处理兼容)
functionHook(String.prototype,'replace',false,function(result,args){
if(result === 'about:srcdoc'){return urlMain;}
return result;
});
Object.defineProperty(document,'URL',{get:function(){
return urlPage;
}});
functionHookSetter(HTMLAnchorElement.prototype,'href',false,function(){
var url = this.getAttribute('href') || '';
if(url.substr(0,1) == '#' || url.substr(0,1) == '?'){return urlPage+url;}
return pathUrlTrue(urlPage,this.getAttribute('href'));
});
},
hookScriptProfill:function(){
// 避免cookie,localStorage读写报错 history replaceState
Object.defineProperty(document,"cookie",{
set:function(v){return true;},
get:function(){return '';},
});
var noop = function(){};
var _localData = {getItem:noop,setItem:noop,clear:noop,removeItem:noop,length:0,getObject:noop,setObject:noop};
Object.defineProperty(window,"localStorage",{
get: function(){return _localData;},
});
Object.defineProperty(window,"sessionStorage",{
get: function(){return _localData;},
});
Object.defineProperty(window,"caches",{
get: function(){return {'delete':noop,has:noop,keys:noop,match:noop,open:noop};},
});
// sandbox中不可用,直接禁用;
Object.defineProperty(window,"indexedDB",{
get:function(){return undefined;},
});
window.history.replaceState = function(){};
},
hookScriptEvent:function(){
window.addEventListener('mousedown',function(){
window.parent.postMessage({type:'iframe.event',event:'mousedown'},'*');
});
window.addEventListener('mouseup',function(){
window.parent.postMessage({type:'iframe.event',event:'mouseup'},'*');
});
window.addEventListener('message',function(e){
if(!e || !e.data || e.data.type != 'parent.event'){return;}
// 当前窗口收到kod窗口内容处理完成消息后处理;
// console.error('iframe-message',e);
if(e.data.event == 'iframeChildLoadSuccess'){
var iframe = document.querySelector('[_view_uuid="'+e.data.uuid+'"]');
if(!iframe){return;}
iframe.setAttribute('data-file-path',e.data.url);
iframe.src="";iframe.srcdoc=e.data.content;
}
});
// url跳转拦截(a跳转,a.href修改; window.open; window.location.href==无法支持,无法setter)
var gotoPage = function(url){
var urlOld = url;
if(url && url.substr(0,1)== '#'){window.location.hash = url;return true;}
if(!isPathUrl(url)){return false;}
url = pathUrlParse(url,basePath);
window.parent.postMessage({type:'iframe.event',event:'iframeLinkHref',url:url},'*');
return true;
}
var stopPP = function(e){
var evt = e || window.event;
if(!evt){return;}
if(evt.preventDefault){evt.preventDefault();}
evt.returnValue = true;
};
var parentNodeFind = function(node,tag){
if(!node || !node.parentNode || node == window || node == document){return false;}
if(node.nodeName == tag){return node;}
return parentNodeFind(node.parentNode,tag);
};
window.addEventListener('click',function(e){
var node = parentNodeFind(e.target,'A');
if(!node){return;}
if(e && e._stopEvent){return stopPP(e);} // 已经在其他处阻止;
var url = node.getAttribute("href");
var targetType = node.getAttribute('target');
var iframes = document.getElementsByName(targetType);
if(iframes.length >= 1){// target到某个ifram存在时不处理;
iframes[0].src = url;
return stopPP(e);
}
if(gotoPage(url)){return stopPP(e);};
});
window.parent.postMessage({type:'iframe.event',event:'iframeMainload'},'*');
// location 跳转监听,暂时无解; //onhashchange,popstate,locationchange...
// https://stackoverflow.com/questions/6390341/how-to-detect-if-url-has-changed-after-hash-in-javascript
// window.addEventListener('popstate',function(e){});
var _open = window.open;
window.open = function(url){
if(gotoPage(url)){return;};
return _open.apply(window,arguments);
}
// window.location.pathname = window._hash;
window.location.origin = (new URL(linkPre)).origin; //无效; window.orgin依然为null; setter无效;
},
});