  
var g_ProjectName = window.location.pathname.substring(window.location.pathname.indexOf("/")+1,window.location.pathname.lastIndexOf("/"));
var s = window.location.href.split('/');
    s.pop();
var g_index=null;
var g_modeldata=null;

var g_weixinTitle=null;

var g_Hots=null;
var g_HotMeshes=[];
var g_HotMeshSize = {
    g_HotMeshWidth: 0.3,
    g_HotMeshHeight:0.3
};

//add表示添加，delete表示删除
var g_HotStatus=null;
var g_newHot = [];//存储新加热点
var g_HotImage= {
	"point":"https://super.4dage.com/images/4dagePoint2.png",
	"point2":"https://super.4dage.com/images/4dagePoint.png"
};	
var g_saveHot=false;

var g_TextColor=0x7777ff;
var g_Text=null;
var g_TextPlaneMesh=[];
var g_TextIconMesh=[];
var g_TextIcon="./images/text.png";
var g_SelectTextIndex=null;
var g_TextShow=null;
var g_audioPlay=false;
var g_background=null; 
var g_roof=null;

var g_data2 = null;//加载的data2.js的内容
var g_bgAudio=null;//背景音乐
var g_tourAudio=null;//导览音乐
var g_play = 1;//表示播放图标状态
var g_playAudio = null;//当前在播放或当继续播放时应该播放的，是g_bgAudio或g_tourAudio 
var g_currentHot = null;//当前打开的热点 
//var g_Texture=null;
//var g_ChunknameTexture={};   //chunkname和贴图名称对应
var g_NormalTexture=false;
var g_SpecularTexture=false;
var g_DirectionalLight=null;  
  
var g_snapShotWidth = 200; //截图下载图片的大小
var g_snapShotHeight = 140;

//微信分享
var g_weixinObj = {
  "title": document.querySelector("head title").innerHTML,
  "lineLink" : window.location.href,
  "imgUrl" : "https://www.4dmodel.com/SuperPanoramic/images/weixintitle.jpg",
  "desc" : "四维时代提供技术支持",
}

var settings = {
    hotClickEvent:{
        video:{
            playAndPause:true,
            examine:false,
            openHot:false 
        },
        photo:{
            examine:false,
            openHot:false 
        },
        shine:{
            examine:true,
            openHot:true  
        }
    },
    //默认的：
    teleportTime:  1500,//瞬间过渡的时间 
    /* flytimeDistanceMultiplier:150, 
    flyTime:750,  */
    tourRotTime:2,  //默认停留2秒
    //dontExamHot:true  
    transparentBg: false,
    bgImg:  null 
}
if(window.number == '725'||window.number == '724'){
    settings.mobileNavHigh = true
}


//共用函数：


window.common = null;  
window.MathLight = null;
window.math = null
window.easing = null
window.lerp = null
window.transitions = null
window.browser = null
window.momentTourBlackNewType = 0//=  number == 'TEST'//true
 









var dealMap = (map)=>{//使不resize  when   image is not power of two
    map.wrapS = map.wrapT = THREE.ClampToEdgeWrapping;
    map.minFilter = THREE.LinearFilter;
    map.magFilter = THREE.LinearFilter;
    map.generateMipmaps = true;  
    
}  
 
 
  
var dom = { 
	getOffset: function(type, element, parent) { 
		left = (type == "left") ? element.offsetLeft : element.offsetTop;
		if (!parent) parent = $("body")[0];
		while (element = element.offsetParent) { 
			if (element == parent) break;
			left += (type == "left") ? element.offsetLeft : element.offsetTop;
		}
		return left;
	}  
};


var getTransformSid = function(){
    var name
    if(player.mode == 'panorama'){
        name = player.currentPano ? player.currentPano.id : 'outside'
    }else{
        name = 'outside'
    }
    return name
} 
 


 
var convertTool = { 
	getPos2d : function(point, camera, dom){//获取一个三维坐标对应屏幕中的二维坐标
		var camera = camera || player.camera;
		var dom = dom || player.domElement;
		var pos = point.clone().project(camera)	//比之前hotspot的计算方式写得简单  project用于3转2(求法同shader)； unproject用于2转3 :new r.Vector3(e.x, e.y, -1).unproject(this.camera);
		
		var x,y;
		x = (pos.x + 1) / 2 * dom.clientWidth;
		y = (1 - (pos.y + 1) / 2) * dom.clientHeight; 
  
		var inSight = x <= dom.clientWidth &&  x >= 0    //是否在屏幕中   
					&& y <= dom.clientHeight &&  y >= 0 
	 
	
		return {
			pos: new THREE.Vector2(x,y),  // 屏幕像素坐标
			vector:  pos,   //（范围 -1 ~ 1）
			trueSide : pos.z<1, //trueSide为false时，即使在屏幕范围内可见，也是反方向的另一个不可以被渲染的点   参见Tag.update
			inSight : inSight	//在屏幕范围内可见
		};
	},

	ifShelter: function(pos3d){//检测某点在视线中是否被mesh遮挡 
		var ori = player.position
        var dir = pos3d.clone().sub(ori).normalize()
        var ray = new THREE.Raycaster(ori, dir) //由外向里 因为模型从内侧是可见的所以从外侧
	
		/* if(config.isEdit && publicObjectSet.editor.mainDesign.editing){
			var o = ray.intersectObjects(publicObjectSet.editor.mainDesign.wallMeshes);
		}else{ */
			var o = ray.intersectObjects(player.model.colliders);
		//} 
		var len = pos3d.distanceTo(ori);
		if (o && o.length) {
			for(var i=0;i<o.length;i++){
				if(o[i].distance < len){  return true;  }//有遮挡
			} 
		} 
	},
    
    
    /* 
        拖拽时，获取鼠标在拖拽面上的位置（需要借助另一个intersectPlane面来计算，即和相机方向一样的面，可保证铺满屏幕）
        但是不一定能获取到，比如鼠标射线不朝向拖拽面时，即使获取也会是一个意外的反方向的交点。
	 */
	getPosAtPlane : function(pos, info/* , mouse, camera */){ //pos:与intersectPlane的交点 见笔记
		var A = pos; 
        var player = player;      
        var mouse = player.mouse;
        var O = new THREE.Vector3(mouse.x, mouse.y, -1).unproject(player.camera);
		
		
		if(info.y != void 0){//地面线的
        
            var y = info.y; 
            
            if(player.mode == "floorplan"/*  ||  Math.abs(O.x-pos.x)<0.0001 && Math.abs(O.z-pos.z)<0.0001) */){
            //intersectPlane和地面平行，无交点
                var x = pos.x, z = pos.z;
            
            }else{
             
                if(y<player.camera.position.y && O.y <= A.y /* || y>player.camera.position.y && O.y >= A.y  */)return null;  //鼠标射线向上。因为相机一定位于地面以上（地面不会抬高到相机上吧？），所以无交点。
                if(O.y == A.y){console.log('一样？？');return;}
                if(A.y == y){console.log('一样2？？');return;}
                var r = (O.y-y)/(A.y-y);
                var x = (r*A.x-O.x)/(r-1);
                var z = (r*A.z-O.z)/(r-1); 
			}
		}else{//垂直的也有越过消失点以后反向变化的情况，但使用时影响不大
			var N = info.normalVec;
			var P = info.pullPos;
			if(N.y != 0 ){console.log('N.y != 0');return;} //仅仅支持垂直于地面的的墙壁，目前都是
			if(O.z==A.z){console.log('O.z==A.z？');return;}
			if(N.z!=0 && N.x != 0){//直接用这个通用的也可以，支持斜线的墙
				//console.log('N.z==0 && N.x == 0？');  
				var c = ( N.x*(A.x-O.x) + N.y*(A.y-O.y) + N.z*(A.z-O.z));
				if(c == 0){console.log("分母为0？？ return;");return;} 
				var t = -((N.x*O.x + N.y*O.y + N.z*O.z) - (P.x*N.x + P.y*N.y + P.z*N.z) ) / c
				var x = t * (A.x - O.x) + O.x;
				var y = t * (A.y - O.y) + O.y;
				var z = t * (A.z - O.z) + O.z;
				/*原理： 已知空间直线L：(x-a)/m=(x-b)/n=(z-c)/p和空间平面π：Ax+By+Cz+D=0;
				求直线L与平面π的交点的坐标。
				把直线方程改写成参数形式：设(x-a)/m=(x-b)/n=(z-c)/p=t；
				则x=mt+a；y=nt+b；z=pt+c；代入平面π的方程得：
				A(mt+a)+B(nt+b)+C(pt+c)+D=0
				由此解得t=-(Aa+Bb+Cc+D)/(Am+Bn+Cp)
				再代入参数方程即得交点的坐标(x，y，z). */
			}else if(N.x ==0 ){ //z与pullPos相等
				var z = P.z;
				if(O.y == A.y){console.log('一样？？');return;}
				if(A.y == y){console.log('一样2？？');return;}
                if(A.z == z){console.log('一样3？？');return;}
				var r = (O.z-z)/(A.z-z);
				var x = (r*A.x-O.x)/(r-1);
				var y = (r*A.y-O.y)/(r-1);
			}else if(N.z == 0){//x与pullPos相等
				var x = P.x;
				if(O.y == A.y){console.log('一样？？');return;}
				if(A.y == y){console.log('一样2？？');return;}
                if(A.x == x){console.log('一样3？？');return;}
				var r = (O.x-x)/(A.x-x);
				var y = (r*A.y-O.y)/(r-1);
				var z = (r*A.z-O.z)/(r-1);
			}
		}   
		
		return new THREE.Vector3(x,y,z);
	},


	
	getMouseIntersect : function(camera, meshes, mouse){//获取鼠标和meshes交点
		var raycaster = new THREE.Raycaster; 
		camera.updateMatrixWorld(); 
		var origin = new THREE.Vector3(mouse.x,mouse.y,-1).unproject(camera)
		  , end = new THREE.Vector3(mouse.x,mouse.y,1).unproject(camera); 
		var dir = end.sub(origin).normalize() 
		raycaster.set(origin, dir);
		var n = raycaster.intersectObjects(meshes);
		if (0 === n.length)
			return null;
		return n[0];
		
	},
	ifIntersectChunks : function(A,B,options={}){//获取某个线段/射线和meshes的交点 
		var dir = B.clone().sub(A).normalize();
		var len = options.InfinityLen ? Infinity :  A.distanceTo(B) + (options.extLen||0);
		var ray = new THREE.Raycaster(A.clone(), dir, 0, len);
          
		var o = ray.intersectObjects(options.model || player.model.colliders);
		if (o && o.length)return o;
		  
		if(options.throughWidth){ //允许最小宽度，防止穿过极小的缝隙导致撞墙感
			var normal = math.getNormal({points:[{x:A.x, y:A.z},{x:B.x, y:B.z}]});//线段法线
			normal.multiplyScalar(options.throughWidth)
			var normalVec3 = new THREE.Vector3(normal.x, 0, normal.y);
			
			var A2 = A.clone().add(normalVec3)
			ray.set(A2, dir);  
			var o2 = ray.intersectObjects(options.model || player.model.colliders);
			ray.set(A.clone().add(normalVec3.negate()), dir);
			if (o2 && o2.length)return o2;
			var o3 = ray.intersectObjects(options.model || player.model.colliders);
			if (o3 && o3.length)return o3; 
		} 
		return null;
	},
    
	getPosAtSphere : function(pos3d, toPanoPos){
		var dir = pos3d.clone().sub(toPanoPos); 
		dir.normalize();//然后计算在球中
		dir.multiplyScalar(Constants.skyRadius);   
		dir.add(toPanoPos); 
		return dir;
	} ,
    
    getScaleForConstantSize : function(op={}){ //获得规定二维大小的mesh的scale值。可以避免因camera的projection造成的mesh视觉大小改变。  来源：tag.updateDisc
        var w;  
        var i = new THREE.Vector3, o = new THREE.Vector3, l = new THREE.Vector3, c = new THREE.Vector3, h = new THREE.Vector3
         
        if(op.width2d) w = op.width2d //如果恒定二维宽度
        else{//否则考虑上距离，加一丢丢近大远小的效果
            var currentDis, nearBound, farBound
            if(op.camera.type == "OrthographicCamera"){
                currentDis = (op.camera.right - op.camera.left) / op.camera.zoom
            }else{
                currentDis = op.position.distanceTo(op.camera.position);
            } 
            w = op.maxSize - ( op.maxSize -  op.minSize) * THREE.Math.smoothstep(currentDis,  op.nearBound,  op.farBound);
            //maxSize ： mesh要表现的最大像素宽度；   nearBound： 最近距离，若比nearBound近，则使用maxSize
        }
        i.copy(op.position).project(op.camera),  //tag中心在屏幕上的二维坐标
        o.set(op.resolution.x / 2, op.resolution.y / 2, 1).multiply(i), //转化成px   -w/2 到 w/2的范围
        l.set(w / 2, 0, 0).add(o),  //加上tag宽度的一半
        c.set(2 / op.resolution.x, 2 / op.resolution.y, 1).multiply(l), //再转回  -1 到 1的范围
        h.copy(c).unproject(op.camera);//再转成三维坐标，求得tag边缘的位置
        var g = h.distanceTo(op.position)//就能得到tag的三维半径
    
        return g  //可能NAN  当相机和position重叠时
         
    } 
    
    
    
}








//--------------------------------------
//管理js文件 获取modeldata.js 判断是否有特殊的字段，如果有就先加载SpecialScene.js 里面有对特殊场景处理的代码 否则就直接加载main



var Manage = function(){
    this.weixinURL = "https://res.wx.qq.com/open/js/jweixin-1.2.0.js",
    this.time = "?"+new Date().getTime();
    this.loadAudio();
    this.loadWeixin();
}
//动态加载js文件
Manage.prototype.LoadJs = function(_files, succes){
	/* 已加载文件缓存列表,用于判断文件是否已加载过，若已加载则不再次加载*/
	var classcodes = [];
	var FileArray = [];
    if (typeof _files === "object") {
        FileArray = _files;
    } else {
        /*如果文件列表是字符串，则用,切分成数组*/
        if (typeof _files === "string") {
            FileArray = _files.split(",");
        }
    }
    if (FileArray != null && FileArray.length > 0) {
        var LoadedCount = 0;
        for (var i = 0; i < FileArray.length; i++) {
            loadFile(FileArray[i], function() {
                LoadedCount++;
                if (LoadedCount == FileArray.length) {
                	try {
                		succes();
                	}
                    catch(err) {
					    console.log("err: 您未定义回调");
					}
                }
            })
        }
    }
    /*加载JS文件,url:文件路径,success:加载成功回调函数*/
    function loadFile(url, success) {
        if (!FileIsExt(classcodes, url)) {
            var _ThisType = GetFileType(url);
            var ThisType = _ThisType.indexOf("?") == -1 ? _ThisType  :  _ThisType.substring(0,_ThisType.indexOf("?"));
            var fileObj = null;
            if (ThisType == ".js") {
                fileObj = document.createElement('script');
                fileObj.src = url;
            } else if (ThisType == ".css") {
                fileObj = document.createElement('link');
                fileObj.href = url;
                fileObj.type = "text/css";
                fileObj.rel = "stylesheet";
            } else if (ThisType == ".less") {
                fileObj = document.createElement('link');
                fileObj.href = url;
                fileObj.type = "text/css";
                fileObj.rel = "stylesheet/less";
            }
            success = success || function() {};
            fileObj.onload = fileObj.onreadystatechange = function() {
                if (!this.readyState || 'loaded' === this.readyState || 'complete' === this.readyState) {
                    success();
                    classcodes.push(url)
                }
            }
            document.getElementsByTagName('head')[0].appendChild(fileObj);
        } else {
            success();
        }
    }
    /*获取文件类型,后缀名，小写*/
    function GetFileType(url) {
        if (url != null && url.length > 0) {
            return url.substr(url.lastIndexOf(".")).toLowerCase();
        }
        return "";
    }
    /*文件是否已加载*/
    function FileIsExt(FileArray, _url) {
        if (FileArray != null && FileArray.length > 0) {
            var len = FileArray.length;
            for (var i = 0; i < len; i++) {
                if (FileArray[i] == _url) {
                    return true;
                }
            }
        }
        return false;
    }
	
};

//获取页面url后面的参数
Manage.prototype.number = function(variable) {
	var query = window.location.search.substring(1);
   	var vars = query.split("&");
   	for (var i=0;i<vars.length;i++) {
           	var pair = vars[i].split("=");
           	if(pair[0] == variable){return pair[1];}
   	}
   	return(false);
};

Manage.prototype.loadWeixin = function() {
    var that = this;
    this.LoadJs(that.weixinURL+that.time,function(){ });

}


 


Manage.prototype.weixinShare = function() {
    console.log("weixinShare")
    $.ajax({    
        url:'https://www.4dage.com/wechat/jssdk/', 
        type: "post",
        data : {
            'url' : location.href.split('#')[0]
        }, 
        dataType:"jsonp",     
        jsonpCallback:"success_jsonp",         
        success:function(data,textStatus){
            console.log("weixinShare success")
            console.log(data.appId)
            
            wx.config({
                // debug : true,
                appId : data.appId,
                timestamp : data.timestamp,
                nonceStr : data.nonceStr,
                signature : data.signature,
                jsApiList : [ 'checkJsApi', 'onMenuShareTimeline',
                        'onMenuShareAppMessage', 'onMenuShareQQ',
                        'onMenuShareWeibo', 'hideMenuItems',
                        'showMenuItems', 'hideAllNonBaseMenuItem',
                        'showAllNonBaseMenuItem', 'translateVoice',
                        'startRecord', 'stopRecord', 'onRecordEnd',
                        'playVoice', 'pauseVoice', 'stopVoice',
                        'uploadVoice', 'downloadVoice', 'chooseImage',
                        'previewImage', 'uploadImage', 'downloadImage',
                        'getNetworkType', 'openLocation', 'getLocation',
                        'hideOptionMenu', 'showOptionMenu', 'closeWindow',
                        'scanQRCode', 'chooseWXPay',
                        'openProductSpecificView', 'addCard', 'chooseCard',
                        'openCard' ]
            });
        },    
        error:function(XMLHttpRequest,textStatus,errorThrown){    
            console.log("jsonp.error:"+textStatus);    
        }    
    }); 
    
    var success_jsonp = function(json){
        console.log(json);
    };


    wx.ready(function(){ 
        // config信息验证后会执行ready方法，所有接口调用都必须在config接口获得结果之后，config是一个客户端的异步操作，所以如果需要在页面加载时就调用相关接口，则须把相关接口放在ready函数中调用来确保正确执行〿
        //对于用户触发时才调用的接口，则可以直接调用，不需要放在ready函数中〿
        //分享到朋友圈 
        console.log(g_weixinObj)
        wx.onMenuShareTimeline({ 
            title: g_weixinObj.title, // 分享标题 
            link: g_weixinObj.lineLink, // 分享链接 
            imgUrl: g_weixinObj.imgUrl, // 分享图标 
            desc: g_weixinObj.desc
        }); 

        //获取“分享给朋友”按钮点击状态及自定义分享内容接叿
        wx.onMenuShareAppMessage({ 
            title: g_weixinObj.title, // 分享标题 
            desc: g_weixinObj.desc,  // 分享描述 
            link: g_weixinObj.lineLink, // 分享链接 
            imgUrl: g_weixinObj.imgUrl, // 分享图标 
            type: '', // 分享类型,music、video或link，不填默认为link 
            dataUrl: '' // 如果type是music或video，则要提供数据链接，默认为空 
        });
        
        wx.onMenuShareWeibo({
            title: g_weixinObj.title, // 分享标题
            desc: g_weixinObj.desc, // 分享描述
            link: g_weixinObj.lineLink, // 分享链接
            imgUrl: g_weixinObj.imgUrl, // 分享图标
            success: function () { 
                // 用户确认分享后执行的回调函数
            },
            cancel: function () { 
                // 用户取消分享后执行的回调函数
            }
        });

        wx.onMenuShareQZone({
            title: g_weixinObj.title, // 分享标题
            desc: g_weixinObj.desc, // 分享描述
            link: g_weixinObj.lineLink, // 分享链接
            imgUrl: g_weixinObj.imgUrl, // 分享图标
            success: function () { 
                // 用户确认分享后执行的回调函数
            },
            cancel: function () { 
                // 用户取消分享后执行的回调函数
            }
        });

        wx.onMenuShareQQ({
            title: g_weixinObj.title, // 分享标题
            desc:  g_weixinObj.desc, // 分享描述
            link: g_weixinObj.lineLink, // 分享链接
            imgUrl: g_weixinObj.imgUrl, // 分享图标
            success: function () { 
                // 用户确认分享后执行的回调函数
            },
            cancel: function () { 
                // 用户取消分享后执行的回调函数
            }
        });

        wx.error(function(res){ 
        // config信息验证失败会执行error函数，如签名过期导致验证失败，具体错误信息可以打开config的debug模式查看，也可以在返回的res参数中查看，对于SPA可以在这里更新签名〿

        });
    });

    
}



Manage.prototype.dealURL = function(src, type){
    //music: "///super.4dage.com/data/LYW/edit/20200928_151633415.mp3"
    //"https://super.4dage.com/data/LYW/edit/20200928_165319399.jpg"]
    
   
    if(window.isLocal && settings.localPrefix!=void 0){//本地将线上的前缀替换
        var oldPrefix = 'super.4dage.com/';  //最简单的地址写一个，如果有其他完全不一样的地址就用数组逐个判断
        var index = src.indexOf(oldPrefix);
        if(index>-1){
            var wholeOldPrefix = src.slice(0, index+oldPrefix.length);
            return src.replace(wholeOldPrefix, settings.localPrefix)
        }
        
        console.error("没有找到合适的本地链接") 
        return src
    }else{
        //add https://
        var prefix = g_Prefix.replace('https://','').replace('http://','') 
        if(!src.includes('http:/') && !src.includes('https:/') && src.includes(prefix)){
            src = 'https://'+src
        }
        return src
    }
    
    
}


Manage.prototype.removeSrcPostMark = function(url){//去除texture.load时自动加上的'?'
    var index = url.indexOf('?')
    if(index>-1){
        return url.slice(0, index)
    }else return url 
}


Manage.prototype.showInfo = function (o) { // ({result:true, title:"发布成功"}); 
	 
	var box = $(".resultBox");
	
	var title = o.title || o || i18n.get('保存成功');
	box.children().eq(0).html(title) 
	
	
	//var time = o.time || THREE.Math.clamp((Config.lang=='en') ? title.length*50 : title.length*130 ,1300,5000);
	var time = o.time ||  THREE.Math.clamp(title.length*130 ,1300,  5000);
    o.time || console.log("showtime  " + time)
	
	//实际有一半的时间在渐变透明度
	this.showInfoTimer && clearTimeout(this.showInfoTimer) 
	box.removeClass("animate");//如果之后不久又要showinfo一个的话，先停止前面的animate
	setTimeout(function () {
		box.css(
			{
				'-webkit-animation-duration': time + 'ms',
				'animation-duration': time + 'ms'
			}
		)
        if(o.top){
            box.children().css('top', o.top + "%");
        }else{
            box.children().css('top', '' )
        }
		box.removeClass("hide");
		box.addClass("animate");
		if (o.dontInteract) {//遮挡对屏幕的操作
			box.css('pointer-events', 'auto')
		} else {
			box.css('pointer-events', 'none')
		}
		 

		this.showInfoTimer = setTimeout(function () {
			box.removeClass("animate");
			box.addClass("hide");
			this.showInfoTimer = null; 
		}.bind(this), time + 20)

	}.bind(this), 50)//这个数字太小的话后面触发的没有重新animate的效果 应该要比帧率大吧


}//like:  manage.showInfo({title:'a', top:20})




//公用的函数

function getQueryVariable(variable)
{
       var query = window.location.search.substring(1);
       var vars = query.split("&");
       for (var i=0;i<vars.length;i++) {
               var pair = vars[i].split("=");
               if(pair[0] == variable){return pair[1];}
       }
       return(false);
}

//隐藏公司Logo
function showLogo(){
    $("#myCompany").hide();
    $("#loaderCoBrandName").hide();
    $("#title-logo").hide();
    $(".title-container").css("justify-content","center")
}
//czj 添加随机的时间
function randomTime(){
    return new Date()
}
function matcher(data){
    if(!data || !g_version ) return data;
    delete data.model.vision_version;
    var _data = {
        files: {
            "templates": ["images/images{{number}}/{{filename}}"]
        },
        model :{
            sid :window.number,
            camera_start:
            data.model.images && data.model.images.length != 0  ? 
            {
                camera: {
                    zoom: "-1",
                    quaternion: [
                        JSON.parse(data.model.images[0].metadata).camera_quaternion.z,
                        JSON.parse(data.model.images[0].metadata).camera_quaternion.w,
                        JSON.parse(data.model.images[0].metadata).camera_quaternion.x,
                        JSON.parse(data.model.images[0].metadata).camera_quaternion.y
                    ]
                },
                pano: { uuid: JSON.parse(data.model.images[0].metadata).scan_id },
                mode: "0"
            }
            : ''
        },
        sid: window.number,
        hoticon: {
            default: "https://super.4dage.com/images/4dagePoint2.png",
            higt: "https://super.4dage.com/images/4dagePoint.png"
        },
        special: "false",
        weixinDesc: ""
    };
    $.extend(true,data,_data)
    return data;
}

function hotMatcher(data={}){
    //if(!data || !g_version) return data; 
    if(g_version) {
        data.tourAudio = data.audio || {};
    }else{
        data.tourAudio = {}
    }
    return data;
}


var GifTexDeal = {
    
    animateObjects : [], 
    animateTexs : [] ,
    addAnimation : function(texture, owner, info, id){
        /* if(this.animateObjects.find(e=> 
            e.texture == texture && !ifSame(info, e.info) 
        )) */ 
        
        var animation
        var tex = this.animateTexs.find(e=>e.texture == texture)
        if(tex){
            animation = tex
        }else{
            animation = {texture,info }
            this.animateTexs.push(animation)
            this.setRepeart(animation)
        }
        
        var object = {
            animation, //默认相同的texture对应的info是一样的, 对应一个animation
            owner,        
        }
         
        this.animateObjects.push(object)
        return object
    },
    
    
    remove : function(object){
        var index = this.animateObjects.indexOf(object) 
        if(index>-1){
            this.animateObjects.splice(index, 1)
            
            if(!this.animateObjects.find(e=>e.animation == object.animation)){
                let i = this.animateTexs.indexOf(object.animation)
                this.animateTexs.splice(i, 1)
                object.animation.texture.repeat.set(1,1) 
            }
            
            this.stop(object) 
            
        }
    },
    setRepeart : function(animation){
        animation.texture.repeat.set(1/animation.info.cellXcount, 1/animation.info.cellYcount)
    },
    start: function(object){ 
         
        if(!object || object.started )return;
        object.started = true
        if(object.animation.started)return;
        object.animation.started = true
        
        var info = object.animation.info
        var count = info.cellXcount * info.cellYcount - (info.voidCount || 0)
     
        if(count <= 1)return;
        
        transitions.start( (progress)=>{
            var index = Math.floor(count * progress);
            var indexX =  index % info.cellXcount
            var indexY =  info.cellYcount - Math.floor(index /info.cellXcount ) - 1;  //uv.offset.y是从下到上的
            object.animation.texture.offset.x = indexX / info.cellXcount;
            object.animation.texture.offset.y = indexY / info.cellYcount;
            
            //console.log(object.id + " : "+ object.texture.offset.toArray())
        } , info.duration * (-1), null,/* ()=>{//done  (-1):循环
            object.started = false
            object.texture.offset.x = 0;
            object.texture.offset.y = 0;
            this.start(object)
        }, */ 0 ,null, object.id, "gif_"+object.animation.texture.id); 

        

    },
    
    
    stop: function(object){ 
        if(!object || !object.started)return;
        object.started = false
        //只有该object对应的texture对应的所有object都停止了，才能真的停止动画:
        if(this.animateObjects.find(e=>e.animation == object.animation && e.started)) return;
        
         
        transitions.cancelById("gif_"+object.animation.texture.id);
        object.animation.texture.offset.set(0,0)
        object.animation.started = false     
        
    }
}

var CloneObject = function(copyObj, result, isSimpleCopy, extraReplace) {
    //isSimpleCopy只复制最外层
    //复制json		result的可能：普通数字或字符串、普通数组、复杂对象
    if(!copyObj)return copyObj //0 null undefined ''
    result = result || {};
    if (copyObj instanceof Array) {
        /*  if (copyObj[0]instanceof Object) {
            //不支持含有 [[Object]] 这样二级数组里面还是复杂数据的，普通和复杂的数据混合可能也不支持
            console.error("不支持含有 [[Object]] 这样二级数组里面还是复杂数据的...")
        }  
        
        return copyObj.slice(0);*/ //如果是数组，直接复制返回（排除数组内是object 
        return copyObj.map(e=>{
            if(e instanceof Object){
                return CloneObject(e)
            }else return e 
        })
       
    }else{
        if(copyObj.clone instanceof Function ){ //解决一部分
            return copyObj.clone()
        }
    }
    for (var key in copyObj) {
        if (copyObj[key] instanceof Object && !isSimpleCopy)
            result[key] = CloneObject(copyObj[key]);
        else
            result[key] = copyObj[key];
        //如果是函数类同基本数据，即复制引用
    }
    return result;
}
;
var ifSame = function(object1, object2){
    if(object1 == object2  )return true // 0 != undefined  , 0 == ''
    else if(!object1 || !object2) return false
    else if(object1.constructor != object2.constructor){
        return false
    }else if(object1 instanceof Array ) {
        if(object1.length != object2.length)return false;
        var _object2 = object2.slice(0);
        
        for(let i=0;i<object1.length;i++){ 
            var u = _object2.find(e=>ifSame(object1[i], e));
            if(u == void 0 && !_object2.includes(u) && !object1.includes(u))return false;
            else{
                let index = _object2.indexOf(u);
                _object2.splice(index,1);
            }
        }
        
        return true
    }else if(object1.equals instanceof Function ){//复杂数据仅支持这种，其他的可能卡住？
        
        return object1.equals(object2)
          
    }else if(typeof object1 == 'number' ||  typeof object1 == 'string'){
        if(isNaN(object1) && isNaN(object2))return true
        else return object1 == object2
        
    }else if(typeof object1 == "object"){
        var keys1 = Object.keys(object1)
        var keys2 = Object.keys(object2)
        if(!ifSame(keys1,keys2))return false;
        
        for(let i in object1){
            var same = ifSame(object1[i], object2[i]);
            if(!same)return false
        }
        return true
    }else{
        console.log('isSame出现例外')
    }
    
    
    
    
    
}




/* var SoundManager = {//暂不支持同时播放
    currentAudio:null,//当前正在播放list中的哪一个
    list:[
        {
            name:"bg", 
            level : 0,//越大优先级越高
            canBeInterrupted : true ,
            
        },
        {
            name:"boxVideo", 
            level : 1, 
            canBeInterrupted : true,
            checkIfNeedPlay:function(){
                
                
            }
        },
        {
            name:"hot", 
            level : 2, 
            canBeInterrupted : true
        },
        {
            name:"tour", 
            level : 3, 
            canBeInterrupted : true
        },
        
        
    ],
    switchPlay:function(){
        
    },
    register:function(){
        
        
        
    } 
    
}
 */
 
 
 
//最好能知道应该播放到的currentTime
var SoundManager = {//暂不支持同时播放
    currentAudio:null,//当前正在播放list中的哪一个
    enableSound:true,//是否允许有声音
    
    
    playHistory:[],//被打断的加入播放历史
    
    list:[ ],//同一级别可以互相打断 //暂时不做多级别
    
     
    
    play:function(name, src, currentTime){
        // 点击开始自动导览的时候，手动停止背景音乐播放
        window.manage.switchBgmState(false);

        var object = this.list.find(e=>e.name == name)
        if(object){
            if(this.currentAudio){ 
                this.pause(this.currentAudio.name, false, true)
                
            }
            
            {//将当前要播放的播放历史中清除
                let index = this.playHistory.indexOf(object)
                if(index>-1)this.playHistory.splice(index,1);
                
            }
               
            
            this.currentAudio = object
            if(src){ 
                this.setSrc(name, src)
            }
            if(currentTime!=void 0){
                object.audio.currentTime = currentTime
            }
            if(object.audio){
                object.audio.load();	// iOS 9   还需要额外的 load 一下, 否则直接 play 无效 *///https://www.cnblogs.com/interdrp/p/4211883.html   部分资料
                object.audio.play();
                object.callback && object.callback(true)
            }
            Log(name + '  播放 '  )
        }
        
    },
    
    
    pause:function(name, autoReplayLast, isInterrupt){//需要能自动恢复上一个被打算的音频。恢复前判断是否还需要播放
        var object = this.list.find(e=>e.name == name)
        if(object){
            this.currentAudio = null
            
            
            if(object.audio){
                object.audio.pause()
                object.callback && object.callback(false)
                Log(name + ' 中断音频 '+  "("+common.getFileNameFromUrl(object.audio.src)+')' )
            }
            
            
            
            if(isInterrupt){//一般主动调用不需要加这个
                this.playHistory.push(object)//如果是被中断的，加入播放历史，等待恢复播放
            }
             
            
            if(autoReplayLast){ //播放之前的音频。它们是被打断过的。
                while(this.playHistory.length){ 
                    var last = this.playHistory.pop();
                    if(last.src && last.canplay(last.audio)){
                        this.play(last.name )
                    }
                    
                }
                
            }
            
            
        }
        
    },
    
 
    
    setSrc : function(name, src){//不能直接给audio赋src！一定要用这个函数！因为我要拿这里的src来判断有无src,因为貌似audio的src会自动变，''时会变成html的链接
        var object = this.list.find(e=>e.name == name)
        object.src = src
        object.audio.src = src
        Log(`${object.name} 设置src: ${src}`)
    },
    
    createAudio:function(object={}){//name, level, canplay
        if(!object.fake){
            object.audio = new Audio(); 
            object.audio.loop = !!object.loop;
            //object.audio.autoplay = true;
        
            
            object.audio.addEventListener('ended', ()=>{ 
                if(object.loop){//循环
                    Log(`${object.name} 播放完毕，重新播放`)
                    object.audio.play()
                }else{
                    this.pause(object.name, true);//停止后的后续处理
                }
            });
            
            
            object.audio.oncanplaythrough = ()=>{ 
                Log(`${object.name} canplaythrough  `) 
            }
        }
        
        this.list.push(object)
        
    },
    
    
    
    initAutoPlay:function(){  //处理设备自动播放限制
        let play = function(){  
            if(this.currentAudio && this.currentAudio.audio){
                this.currentAudio.audio.play() 
                Log(`${this.currentAudio.name} 自动播放成功`) //即使设置src在这之后好像也能成功播放
            }else{
                
            }
            document.removeEventListener("touchstart",play);
            document.removeEventListener("click",play);
            $('#player')[0].removeEventListener("touchstart", play);
        }.bind(this);
        
        
        document.addEventListener("WeixinJSBridgeReady", play, false);
        document.addEventListener("touchstart", play);//ios需要加个事件才能播放 不能自动播放；如果还有浏览器不行，换成别的交互事件
        document.addEventListener("click", play);
        $('#player')[0].addEventListener("touchstart", play); 
            
         
    }
}


function Log(value, color, fontSize){
    color = color || '#13f'
    fontSize = fontSize || 14
    console.warn(`%c${value}`, `color:${color};font-size:${fontSize}px`) 
}






Manage.prototype.loadAudio = function() { //相关：g_tourAudio \  g_playAudio
    
     
    
    
    //热点页面因为挡住了界面，所以暂时不存在别的音频阻止热点音频的可能
    //box视频都静音，所以暂时不考虑
    
    SoundManager.createAudio({
        name:'bgm',
        level:0, 
        src:'',
        loop:true,
        canplay:(audio)=>{
            return this.bgmShouldPlay
        },
        callback:(state)=>{//play或pause时随之触发的函数（即使还没开始播放）
            if(state){
                console.log('播放背景音乐');
                document.querySelector('#openMusic').style.display='none'
                document.querySelector('#closeMusic').style.display='block'
            }else{
                console.log('暂停背景音乐');
                document.querySelector('#openMusic').style.display='block'
                document.querySelector('#closeMusic').style.display='none'
            }
        }
    })  
    SoundManager.createAudio({
        name:'tour',
        level:0, 
        src:'',
        loop:false,
        canplay:(audio)=>{
            return player.director && player.director.tourIsPlaying && player.director.getAudio()
        }
    })
    SoundManager.createAudio({
        name:'hot',
        fake:true,//实际上没有audio. 只是为了来停止和续播其他音频
        level:1, 
        src:'',
        loop:false,
        canplay:(audio)=>{
            
        }
    })
     
    
    $("#volume").find("a").on("click", ()=> {  
        if($("#volume img")[0].src.indexOf("btn_on.png")>-1)
        { 
            this.switchBgmState(true); 
        }
        else if($("#volume img")[0].src.indexOf("btn_off.png")>-1)
        {
            this.switchBgmState(false);    
        }
    })  
    
    // this.switchBgmState(true);//初始设置允许播放bgm 
    // SoundManager.initAutoPlay()
    
    
}  


 
Manage.prototype.switchBgmState = function(state){//按钮的状态完全代表是否应该播放bgm，即使还没加载完
    this.bgmShouldPlay = state
    
    
    if(state){
        
        SoundManager.play('bgm')
    }else{
        
        SoundManager.pause('bgm')
    }
    
    
    /* if(!g_bgAudio || !g_bgAudio.src) return;
    
    
    var played = function(){
        console.log('begin play bgm');
        g_play = 1; 
        g_playAudio = g_bgAudio;
        
        g_tourAudio && g_tourAudio.pause()
    }
    var paused = function(){ 
        g_play = 0;
        g_playAudio == g_bgAudio && (g_playAudio =  null)
        
    }
    
    if(state ){
        g_bgAudio.play(); 
        if(g_bgAudio.paused){
            paused()
        }else{
            played()
            return true
        }            
    }else{
        g_bgAudio.pause();
        paused()
    } 
    
    
    g_bgAudio.pauseByHot = false
    g_bgAudio.pauseByTour = false */
}    
    
    
    
    
    
var manage = new Manage();

 



//兼容一代的場景
//請求地址統一管理
var g_onePregix = "https://bigscene.4dage.com/" //对应一代  http://www.4dmodel.com/SuperPanoramic/index.html?m=55
var g_version = manage.number("version");
g_version === "one" ? g_Prefix = g_onePregix : '';

