/* var ceshi = '';
// var ceshi = 'http://192.168.0.135:8105';
//  var ceshi = 'http://47.107.252.54:8105';

// var token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOm51bGwsIm1hbmFnZXIiOm51bGwsImlkIjoxLCJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNTk2NzAxNjAyLCJpYXQiOjE1OTY2MTUyMDIsImp0aSI6Ijk0YjA5YTg1LWViY2ItNDE1Yy04OGVmLWU0MDM1M2UzYmIxYyJ9.Uzi95n0kYXp7bdpY77MdLtTpPL_Fq-jn0tE7gjg6AzA"
var token = window.localStorage.dcj_token
 


 */
var confirmSnap = $(".confirmSnap.innerBtn");
var snapshotGui = $(".snapshotGui.overlayGui");
var _musicMaxWeight = 8
//M
var cmp = getQueryVariable('cmp') || ''

var _hotNum = 0

function reData(data, type) {
    return JSON.stringify(data) == type ? null : data
}

let strictInputNum = function(e, precision, min=0,max){//precision：保留小数位数
    var value = e.target.value.trim(); 
    var lastOne = value[value.length-1];
    var preContent = value.substr(0,value.length-1)
    var hasPoint = precision > 0 && lastOne == '.' ; 
     
    var a = Math.pow(10, precision)
    value = parseFloat(value) || 0;
    if(isNaN(value)){
        e.target.value = preContent //还原
        return
    }
    e.target.value =  Math.max(min, parseInt(value * a) / a )
    
    hasPoint && (e.target.value += '.')//补小数点
}

//新编辑界面
var EditTools = function(n) {
    //main2018 里面的主要对象
    this.n = n;
    //当前状态	
    this.atPanel = null;
    //场景信息
    this.sceneInformation = new SceneInformation();
    //热点
    this.hotpoint = new Hotpoint();
    //导览
    this.editGuide = new EditGuide();
    //音乐
    this.EditBGM = new EditBGM();
    //初始化界面
    //this.init();
    this.data = {}
    
    this.editLabel = new EditLabel();
}


EditTools.prototype.loadDone = function(name, data){
    this.data[name] = data
    if('data' in this.data && 'data2' in this.data){
        this.init();
    }
}


EditTools.prototype.init = function() {
    
    
    this.ifLogin();
    
    this.hotpoint.init(/* this.n */);
    var that = this;
    this.initSaveAll();
    this.active();
    
    if(player.mode != 'panorama'){
        $(".toolLeft li[data-name=panoVisible]").addClass('unable')
        $(".toolLeft li[data-name=routeArrow]").addClass('unable')
        player.once("pano.chosen",()=>{//playerAndModelReady
            $(".toolLeft li[data-name=panoVisible]").removeClass('unable')
            $(".toolLeft li[data-name=routeArrow]").removeClass('unable')
        })
    }
    
    
    
    //创建导览
    that.EditBGM.init(this.data.data); 
    that.sceneInformation.init(this.data.data,  this.data.data2)
    // fyz 初始化导览需要两部分数据, 其中data2为data.js中的数据
    that.editGuide.init(this.data.data,  this.data.data2);
    
    that.editLabel.init()
    
    $("body").on("click", function() {
        $(".DelConfirm").removeClass("active");
    })
    
    
    player.on("mode.changing",(currentMode, mode, pano)=>{ //起飞
        if(this.atPanel != "screen" && !editTool.hotpoint.gettingCameraData) return
        
        if(currentMode == "panorama") confirmSnap.addClass("unable")
        
          
    })
    player.on("flying.ended",(/* toPos, fromPos, pano */)=>{ //飞结束
        if(this.atPanel != "screen" && !editTool.hotpoint.gettingCameraData) return
        if(player.mode == "panorama") confirmSnap.removeClass("unable")
    })
    
    
    
    
    
    
   /*  $(" .MenuOptions li").on("click", (e)=>{ 
        var $elem = $(e.target);
        $elem.closest('.MenuOptions').find('li').removeClass("chosen");
        $elem.addClass("chosen"); 
    }) */
    
    //fyz 拖拽以改变顺序 
    {
        let tourListNode = $("#tourList>ul")[0];
        let tourItemListNode = $("#tourItemList>ul")[0];
        let callback = function(ul){
            reIndexTourList(ul)
            
        }
         
        //导览拖拽
        setDraggable({ul:tourListNode,  dragItemClassName:['guideItem', 'listItem'], callback})
        setDraggable({ul:tourItemListNode,  dragItemClassName:['guideItem'], callback })
        
        
        //热点媒体拖拽
        setDraggable({ul:$('#hotpointDetail [name="video"] .list')[0],  dragItemClassName:['mediaItem'] })
        setDraggable({ul:$('#hotpointDetail [name="photo"] .list')[0],  dragItemClassName:['mediaItem'] })
    
    
        //热点列表拖拽 
        setDraggable({ul:$('.toolRight .spotList')[0],  dragItemClassName:['listItem']})
        
        //roomLabel列表拖拽 
        setDraggable({ul:$('.toolRight .roomLabels [name="list"]>ul')[0],  dragItemClassName:['listItem']})
    }
    
    
    $(".toolRight .panoVisible [name=autoCompute]").on('click',()=>{
        VisiSet.resetPanosVisiByModel()
        
    }) 
    
    
    
    //平面图方向固定
    this.fpAngleOptions = new MenuOptions({
        dom: $(".toolRight .information [data-name=floorPlanAngle]  .MenuOptions"),
        uiCallBack : (o)=>{    
            var name = o.name == void 0 ?  o.$li.attr('index') : o.name
            
            _settings.floorPlanAngle = parseFloat(name) * Math.PI/2  //设置好方向 (0是无效的，将不设置)
             
            
            if(name != 0 && player.ready){  
                if(player.mode != 'floorplan' && player.modeTran.split('-')!= 'floorplan'){
                    player.flyToMode('floorplan')
                }else{
                    player.flyToMode('floorplan',()=>{ 
                        player.cameraControls.controls.floorplan.rotateToAngle(_settings.floorPlanAngle)
                    })
                }
                
            } 
            
        }, 
        callbackWhenChose:(o)=>{
            var name = o.$li.attr('index')
             
        }
    })
    this.fpAngleOptions.updateChoseAtUI({name: Math.round(_settings.floorPlanAngle/(Math.PI/2))})

}
//点击的时候激活状态
EditTools.prototype.active = function() {
    var that = this;
     
    $('.toolBottom .toolLeft li').click(async function() {
        var name = $(this).data("name");
        $('.toolLeft li[data-name=' + name + ']').addClass("active").siblings().removeClass("active");
        $('.toolRight .' + name).removeClass("hide").siblings().addClass("hide");
        if (name == that.atPanel)
            return;

        console.log(`atPanel ${that.atPanel} name ${name}`)
        switch (that.atPanel) {
            //退出
            case "panoVisible":
                await VisiSet.unsaveWarn()
             
                VisiSet.finishSetPanoVisible()
                break;
            case "panoLog":
                VisiSet.finishSetPanoLog()
                break;    
            case "routeArrow":
                VisiSet.finishSetRoute()
                break;        
            case "screen":
                confirmSnap.addClass("hide").removeClass("unable");
                snapshotGui.hide();
                break;
            /* case "overlay":
                EditOverlay.leave()
                break; */
            case "hotpoint": 
                await that.hotpoint.unsaveWarn()
                $("#hotpointDetail").hasClass("atRight") || $("#hotpointDetail a.close").click();
                VisiSet.finishSetTagVisible()
                that.hotpoint.wireframeModel.visible = false
                break;
                
            case 'roomLabels': 
                that.editLabel.leave() 
                break;
                
        }
        
        
        
        switch (name) {
            case "hotpoint": 
                that.hotpoint.wireframeModel.visible = that.hotpoint.wireframeModel.shouldShow
                break;
            case "panoVisible":
                $(".toolLeft").addClass("unable")
                VisiSet.enterSet(VisiSet.beginSetPanoVisible.bind(VisiSet))

                break;
            case "panoLog":
                $(".toolLeft").addClass("unable")
                VisiSet.enterSet(VisiSet.beginSetPanoLog.bind(VisiSet))

                break;  
            case 'routeArrow':
                VisiSet.enterSet(VisiSet.beginSetRoute.bind(VisiSet))
                break;
            case "screen":
                confirmSnap.removeClass("hide");
                $('#camera-start').text("点此设置为初始画面")
                snapshotGui.show();
                if (player.mode != "panorama")
                    confirmSnap.addClass("unable")
                break;
            /* case "overlay":
                EditOverlay.enter()
                break; */
            case 'snapTour':
                that.editGuide.scroller.tourBlackSpeed.InitOffset()
                that.editGuide.scroller.tourWalkSpeed.InitOffset()     
                break;

            case 'roomLabels': 
                that.editLabel.enter() 
                break;
        }

        that.atPanel = name;

        /* if(name != "hotpoint"){
        $("#hotpointDetail").hasClass("atRight") || $("#hotpointDetail a.close").click()
    } */

    })

    //滚动阻止事件
    $(".toolRight div.content").on("mousewheel", function(event) {
        event.stopPropagation()
    });
    // 输入框阻止事件
    $('.toolRight').on('keydown', function(ev) {
        ev.stopPropagation();
    })

 
    //加空格和回车字符
    $(".buttons[name='addSign'] button").on('click',function(e){ 
        let forAttr = $(this).parent().attr('for') 
        let textarea = forAttr ? $('#'+forAttr) : $(this).parent().parent().find('textarea')
        let sign = $(this).attr('name') == 'space' ? '&nbsp;' : '<br>'
        var v = textarea.val();
        textarea.val(v + sign) 
    })
    

        
}
//保存按钮
EditTools.prototype.initSaveAll = function() {
    var that = this;

    $('#save').on('click', function() {
        $(".edit-loading").removeClass("hide");
 

        var $shareImages = $(".information .edit-fun-images a.result");

        new Promise(function(resolve, reject) {//分享图
            upload($shareImages, 'images', resolve)
        }).then(function(imgUrls) {
            
            //导览的数据
            var guides = that.editGuide.getSavingInfo()/* Array.from($('.tourFolderList ul li')).map(function(dom) {
                return dom.data
            }) */
            
            let tourRotTime = parseFloat($('.toolRight .snapTour [name="rotTime"] input').val()) 
            isNaN(tourRotTime) && (tourRotTime = '') 

            var info = {
                name: $('#pjtName').val(),
                summary: $('#info-summary').val(),
                weixinDesc: $('#weixin-summary').val(),
                shareImgUrl: imgUrls[0] || '',
                backgroundMusic: that.EditBGM.musicBox.getSrc() || '', 
                bgName: that.EditBGM.musicBox.getName(), 
                hoticon: JSON.parse($(".hotStyle-item li.active").attr("data-val")),
                camera_start: $(".screen .shotImg.innerBtn")[0].cameraData,
                loadlogo: $("#loadlogo").is(':checked'),
                special: $("#g_specialScene").is(':checked'),
                vision_version: $("#twoData").is(':checked') ? "1.1.562.17209" : false,
                roomLabels: that.editLabel.getSavingInfo(),
                /************************************** 方奕卓 场景可配置项 ******************************************/
                hotImageScale: $("#hotImgScale").is(':checked'),
                // 热点图片可放大
                
                autoAdjustHotScale : $("#autoAdjustHotScale").is(':checked'),
                
                hideFloorMarker: $("#hideFloorMarker").is(':checked'),
                // 地面标记
                hideMouseMarker: $("#hideMouseMarker").is(':checked'),
              
                showingLabels: $("#showingLabels").is(':checked'),
                // 鼠标标记
                floorMarkerColor: $('#floorMarkerColor').val(),
                // 地面/导览标志颜色
                mouseMarkerColor: $('#mouseMarkerColor').val(),
                // 鼠标标志颜色
                momentTour: $('#tourSwitch input').is(':checked') ? 'black' : 'walk',
                tourBlackSpeed : that.editGuide.scroller.tourBlackSpeed.value,
                tourWalkSpeed : that.editGuide.scroller.tourWalkSpeed.value,
                tourRotTime,
                
                momentTourBlackNewType :  $('#newBlack').is(':checked'),
                
                showHotListSta: $('#hotListSwitch').is(':checked'),
                // 开启热点列表
                hotIconScale: DATA.hotIconScale,
                // 热点缩放
                supportsVR: $('#VRSwitch input').is(':checked'),
                // 开启VR功能


                /**************** 陈志广 小地图可配置项   *********** */
                cadSignColor: $('#cadSignColor').val(),
                cadBorderColor: $('#cadBorderColor').val(),
                cadBorderWidth: $('#cad-size').val(),
                showCad: $('input[name="show-cad"]').is(':checked'),
                /**********/
                route: VisiSet.saveRoute(),
                floorPlanAngle: _settings.floorPlanAngle || 0 //俯视图旋转角
            }
 
             
            var data =  { 
                sceneCode: number,
                hots: reData(that.hotpoint.getSavingInfo(), '{}'),
                //tourAudio: reData(that.editGuide.tourAudio,'{}'),//for 旧版 
                name: number,
                guides: reData(guides, '[]'),
                info: info, 
            } 
            console.log(data)
            let url = cmp ? ('/api/scene/edit/' + cmp) : '/manage/scene/edit'
            $.ajax({
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    token: token
                },
                dataType: 'json',
                contentType: 'application/json',
                url: ceshi + url,
                data: JSON.stringify(data),
                success: function(data) {
                    if (data.code === 0) {//成功
                        //manage.showInfo("保存成功")
                        location.reload()
                    }else if (data.code === 5001) {
                        alert('请重新登录')
                        localStorage.dcj_token = ''
                        location.reload()
                    }else{
                        alert(`saveAll  (url:${url})  code : ${data.code}  \n${data.msg}`) 
                    }
                    
                }

            })
        });
    })
}


EditTools.prototype.ifLogin = function() {//先登录
    if(window.location.href.includes('&localTest'))return //本地 不登录

    let url = cmp ? ('/api/scene/roamViable/' + cmp) : '/manage/scene/roamViable'
    //借用漫游可行保存来判断
    $.ajax({
        method: 'POST',
        url: ceshi + url,
        headers: {
            'Content-Type': 'application/json',
            token: token
        },
        contentType: 'application/json',
        data: JSON.stringify({
            data: '[]',
            sceneCode: window.number
        }),
        success: (data)=>{ 
            if (data.code === 5001) {
                alert('请重新登录')
                localStorage.dcj_token = ''
                location.reload()
            }
        }
        ,
        fail: function() {
            console.log('ifLogin failed')
        }
    })
}


class EditLabel{
    constructor(){
        
        
        
    }
    
    init(){
        let reverse = player.model.roomLabels.slice(0).reverse()
        reverse.forEach(e=>this.addLebelCallback(e))
        
        
        $('.toolRight .roomLabels .addBtn button').on('click',()=>{//准备添加
            if(this.editingLabel){
                this.setEditLabel(false, this.editingLabel)
            }
            this.setAddState(true) 
        })  
        $('.toolRight .roomLabels [name="title"] input').on('input',(e)=>{//标题
            this.editingLabel.setTitle(e.target.value) 
            this.editingLabel.$li.find('.title').text(e.target.value)
        })
        /* $('.toolRight .roomLabels [name="delete"] button').on('click',(e)=>{//删除 
            this.deleteLabel(this.editingLabel)
            this.setEditLabel(false,this.editingLabel)            
        }) */
        
        
        
        player.on('getIntersect',(intersect, consume)=>{//添加 
            if(this.adding){
                var position = intersect.point.clone();
                position.y += 0.5
                //var floorIndex = player.model.allFloorsVisible ? null : player.model.currentFloor.floorIndex
                var floorIndex = intersect.object.parent.parent.floorIndex 
                var label = new RoomLabel({position, title:'' ,  floorIndex  })
                player.model.roomLabels.push(label)
                this.setAddState(false) 
                this.addLebelCallback(label)
                this.setEditLabel(true, label) //开始编辑
                    
                    
                this.adding = false
                label.update()
                consume(true)
            }
        })
        
        
        
        
        
        //点击列表弹出按钮
        $('.toolRight .roomLabels [name="list"]>ul').on("click", (e)=>{
            var target = $(e.target); 
            let li = searchParent(e.target, { className: 'listItem' }, 7);
            if(!li)return;
            
            let label = li.label
              
          
            if (target.hasClass("del")) {
                e.stopPropagation();
                target.siblings(".DelConfirm").addClass("active"); 
            } else {
                if (target.hasClass("DelConfirm")) {
                    e.stopPropagation();
                    this.deleteLabel(label);
                }else{  
                    this.setEditLabel(true, label) //开始编辑
                }
            }
        });
    } 
    
    deleteLabel(label){
        let index = player.model.roomLabels.indexOf(label)
        index > -1 && player.model.roomLabels.splice(index,1) 
        
        label.dispose()
        
        label.$li.remove()
        
        if(this.editingLabel == label){
            this.setEditLabel(false,this.editingLabel)
        }
    }
    
    addLebelCallback(label){
        this.addLebelEvent(label)
        this.addToList(label)
    }
    
    addToList(label){
        var $li = $("<li class='listItem' draggable='true'><div class='icon'></div><div class=title >" + label.title + "</div><div class=DelConfirm title='删除'>确定删除</div><div class=del></div></li>");
        $li[0].label = label  
        label.$li = $li;
        
        $('.toolRight .roomLabels [name="list"]>ul').prepend($li)
        
    }
    setAddState(state){
        state ? CursorDeal.add('addLabel') : CursorDeal.remove('addLabel')
        this.adding = state 
    }
    
    setEditLabel(state, label){
        let pannel = $('.toolRight .roomLabels li[name="prop"]') 
         
        if(state){  
            if(this.editingLabel){
                let old = this.editingLabel
                this.setEditLabel(false, this.editingLabel)
                if(old == label){
                    return  //点击已打开的就关闭
                }
            }
            label.elem.addClass('selected')
            pannel.removeClass('hide')
            pannel.find('[name="title"] input').val(label.title)
            label.setEditSelect(true)
            pannel.insertAfter(label.$li)     
            this.editingLabel = label 
            player.focusPoint({aim: label.position, radius: label.position.distanceTo(player.position)}) 
        }else{
            label.elem.removeClass('selected')
            pannel.addClass('hide')
            label.setEditSelect(false)
            this.editingLabel = null
        } 
    }
    
    addLebelEvent(label){
        //拖拽 
        let dragElemShift = {}
        let dragging = false
        const startDrag = (evt) => {
            if(!this.editing )return
            dragging = true
            $('#player').on("mousemove", drag); 
            label.elem.find('a').css('pointer-events','none')
            /* if(player.mode != 'floorplan'){ //换算到elem的底部
                let lebelWidth = label.elem.children(0).width() 
                let lebelHeight = label.elem.children(0).height() * (68+24) / 24 ;//
                dragElemShift = {x: -evt.offsetX + lebelWidth/2, y: 100  } //y向不准确，因为原本添加时提高了0.5，而0.5对应的像素需要根据视角，那就算了吧
            }else{
                dragElemShift = {x:0,y:0}
            } */
            
            CursorDeal.add('moveLabel')
        };

        const endDrag = (evt) => {
            dragging = false

            $('#player').off("mousemove", drag);
            label.elem.find('a').css('pointer-events','')
            CursorDeal.remove('moveLabel')
        };

        const drag = (evt) => {
            if (dragging) {
                
                evt.preventDefault(); 
                let mouse = new THREE.Vector3()   
                let meshes = player.model.allFloorsVisible ? player.model.colliders : player.model.currentFloor.collider.children
                math.convertScreenPositionToNDC(evt.offsetX/* +dragElemShift.x */, evt.offsetY/* +dragElemShift.y */, mouse);
                var intersect = convertTool.getMouseIntersect(player.camera, meshes, mouse)
                if(intersect){
                    let position = intersect.point.clone()
                    position.y += 0.5
                    label.setPos(position)
                    var floorIndex = intersect.object.parent.parent.floorIndex
                    label.floorIndex = floorIndex
                }  
                //this.dispatchEvent({type:'dragged', position: this.position})
                
            }
        };

        label.elem.on('mousedown', startDrag);
        $(document).on('mouseup', endDrag);
        $("#player").on('mouseup', endDrag);    
   
        //点击编辑：
        label.elem.on('click',(e)=>{
            if(this.editing){
                this.setEditLabel(true,label)
            }
            e.stopPropagation()
        })
        
    }
    
    enter(){
        this.editing = true
        player.roomLebelClickUnabled = true
        
    }
    leave(){ 
        this.editing = false
        this.editingLabel && this.setEditLabel(false, this.editingLabel)
        player.roomLebelClickUnabled = false
        this.setAddState(false)
    }
    
    getSavingInfo(){ 
        var info = Array.from($('.toolRight .roomLabels [name="list"] .listItem')).map((li,index)=>{
            let label = li.label
            return {
                //order: index,
                title : label.title,
                position : toPrecision(label.position.toArray(), 4),
                floorIndex: label.floorIndex
            } 
        }) 
        console.log(info)
        return info
        
    }
    
}












//场景信息编辑
var SceneInformation = function() {}
SceneInformation.prototype.init = function(data, data2) {
    
     
    
    var $options = $('.hotStyle-item li');
    var $loadlogo = $("#loadlogo");
    var $hotImageScale = $("#hotImgScale");
    var $hideFloorMarker = $("#hideFloorMarker");
    var $hideMouseMarker = $("#hideMouseMarker");
    var $showingLabels = $("#showingLabels");
    var $g_specialScene = $("#g_specialScene");
    var $twoData = $("#twoData");

    /*******************方奕卓 其他项需要用到data来初始化, 故一并放到这里 ******************/
    let momentTour = $('#tourSwitch input');
    // 导览瞬间过渡开关
    let hotListSwitch = $('#hotListSwitch');
     
    // 热点图标缩放
    let VRSwitch = $('#VRSwitch input');
    // VR功能开启
    /***********************************************************************************/

    $(".model-title2").text(data.name);
    $('#pjtName').val(data.name);
    $('#info-summary').val(data.summary);
    //if (data2) $('#weixin-summary').val(data2.weixinDesc);
    $('#weixin-summary').val(data.weixinDesc);

    for (var i = 0; i < $options.length; i++) {
        if ($options.eq(i).attr('data-val') === JSON.stringify(data.hoticon)) {
            $options.eq(i).addClass("active").siblings().removeClass("active");
        }
    }
    
    let hasCameraStart = !!data.camera_start && data.camera_start.thumbImg  //最初始的数据camera_start是默认值，但无thumbImg
    $(".screen .shotImg.innerBtn")[0].cameraData = hasCameraStart && data.camera_start; 
    if(hasCameraStart) $(".screen .itemTitle button").removeClass('hide')
    
    $(".shotImg.innerBtn").on("click", function(){ 
        var EntryInfo = this.cameraData
        if (EntryInfo) {
            var to = player.model.panos.get(EntryInfo.pano.uuid);
            var q = new THREE.Quaternion().fromArray(EntryInfo.camera.quaternion);
            var lookAtPoint = new THREE.Vector3(0,0,-1).applyQuaternion(q).add(to.position);
            player.flyToPano({
                pano: to,
                lookAtPoint: lookAtPoint
            })
        }
    })
    $(".screen .itemTitle button").on('click',()=>{
        $(".screen .itemTitle button").addClass('hide')
        $(".screen .shotImg.innerBtn")[0].cameraData = {};
        $(".screen .shotImg.innerBtn").addClass('blank').css("background-image", "")
    })

    
    if(data.shareImgUrl){
        var imagesHTML = '<div><a class=" result success"><span></span><img src="' + data.shareImgUrl + '"></a></div>'
        $(".information .edit-fun-images.list ").html(imagesHTML);
        $("#shareImgUpload").addClass("hide")
    }
       
    if(data.spotImgUrl){   
    
    }


    /*************方奕卓 其他项的初始化操作 ************/
    initColorElem(data);
     
    // 给其他元素绑定事件
    if (data.momentTour === 'black') {
        momentTour.prop('checked', true);
    }
    if (data.showHotListSta) {
        hotListSwitch.prop('checked', true);
    }
    if (data.momentTourBlackNewType) {
        $('#newBlack').prop('checked', true);
    }
    
    /* if (data.hotIconScale) {
        $('#hotIconScale input').val(parseFloat(data.hotIconScale));
    } */
    
    /* $('#hotIconScale_2 input').on('change',(e)=>{
        var s = THREE.Math.clamp(parseFloat(e.target.value), 0.1, 100)
        e.target.value = s
        editTool.hotpoint.editSpot.mesh.scale.set(s,s,s)  
    }) */
    
    
    
    if (data.supportsVR) {
        VRSwitch.prop('checked', true);
    }
    /************************************************/

    //隐藏公司logo
    if (data.loadlogo) {
        $loadlogo[0].checked = true
        showLogo();
    }

    // 热点图片放大
    if (data.hotImageScale) {
        $hotImageScale[0].checked = true
    }

    // 隐藏地面标记
    if (data.hideFloorMarker) {
        $hideFloorMarker[0].checked = true
    }

    // 隐藏鼠标标记 
    if (data.hideMouseMarker) {
        $hideMouseMarker[0].checked = true
    }
     
    if (data.showingLabels) {
        $showingLabels[0].checked = true
    }
    //特殊大场景
    if (data.special) {
        $g_specialScene[0].checked = true
    }

    //启动二代的数据
    if (data.vision_version === "1.1.562.17209" || data.vision_version) {
        $twoData[0].checked = true
    }

}

/**
 * 初始化颜色选择的html元素
 * @param {*} data 
 * @param {*} elem 
 */
function initColorElem(data, elem) {
    let $floorMarkerColor = $('#floorMarkerColor');
    let $mouseMarkerColor = $('#mouseMarkerColor');
    let $floorMarkerColorTex = $('#floorMarkerColorTex');
    let $mouseMarkerColorTex = $('#mouseMarkerColorTex');
    let $cadSignColor = $('#cadSignColor');
    let $cadSignColorTex = $('#cadSignColorTex');
    let $cadBorderColor = $('#cadBorderColor');
    let $cadBorderColorTex = $('#cadBorderColorTex');

    var colorRe = /^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/;

    // 事件监听
    $floorMarkerColor.on('change', function(e) {
        $('#floorMarkerColorTex').val(e.target.value)
    })

    $floorMarkerColorTex.on('blur keydown', function(e) {
        (e.type === 'blur' || e.type === 'keydown' && e.keyCode === 13) && setColor(e, $floorMarkerColor, this);
    })
    $mouseMarkerColor.on('change', function(e) {
        $('#mouseMarkerColorTex').val(e.target.value)

    })
    $mouseMarkerColorTex.on('blur keydown', function(e) {
        (e.type === 'blur' || e.type === 'keydown' && e.keyCode === 13) && setColor(e, $mouseMarkerColor, this);
    })
    
    // 事件监听
    $cadSignColor.on('change', function(e) {
        $('#cadSignColorTex').val( e.target.value)
    })
    
    $cadSignColorTex.on('blur keydown', function(e) {
        (e.type === 'blur' || e.type === 'keydown' && e.keyCode === 13) && setColor(e, $cadSignColor, this);
    })
    
    // 事件监听
    $cadBorderColor.on('change', function(e) {
        $('#cadBorderColorTex').val( e.target.value)
    })
    
    $cadBorderColorTex.on('blur keydown', function(e) {
        (e.type === 'blur' || e.type === 'keydown' && e.keyCode === 13) && setColor(e, $cadBorderColor, this);
    })

    // 设置颜色值
    function setColor(e, colorElem, inputElem) {
        var isColor = colorRe.test(e.target.value);
        isColor && colorElem.val(e.target.value);
        // isColor ? colorElem.val(e.target.value): (inputElem.placeholder = colorElem.val());
    }

    
    // 判断data中是否有存在的颜色值, 否则设置默认值
    data.floorMarkerColor ? $floorMarkerColor.val(data.floorMarkerColor) : $floorMarkerColor.val('#4bcdfc');
    data.mouseMarkerColor ? $mouseMarkerColor.val(data.mouseMarkerColor) : $mouseMarkerColor.val('#4bcdfc');
    
    // 判断data中是否有存在的颜色值, 否则设置默认值
    data.cadSignColor ? $cadSignColor.val(data.cadSignColor) : $cadSignColor.val('#00c8af');
    data.cadBorderColor ? $cadBorderColor.val(data.cadBorderColor) : $cadBorderColor.val('#ffffff');


    $('input[name="show-cad"]')[0].checked = !!data.showCad

    $('#cad-size > option[value="'+(data.cadBorderWidth ? data.cadBorderWidth : '0.01')+'"]').attr("selected",true);

    if (data.showCad) {
        $('input[name="show-cad"]').trigger('change')
    }
    
    
      
    $('#panoIdColor').on('change', function(e) {
        $('#panoIdColorTex').val(e.target.value)
        VisiSet.changePanoIdColor(e.target.value)
    })
    $('#panoIdColorTex').on('blur keydown', function(e) {
        (e.type === 'blur' || e.type === 'keydown' && e.keyCode === 13) && setColor(e, $('#panoIdColor'), this);
        VisiSet.changePanoIdColor(e.target.value)
    })
    $('#panoIdColor').val('#c13e3e')//初始颜色
    $('#panoIdColorTex').val('#c13e3e')
}


$('input[name="show-cad"]').on('change', function(ev) {
    if (!ev.target.checked) {
        return $('.cad').hide()
    }
    
    let taht = this
    if (!taht.floorJSON) {
        $.ajax({
            url: '//super.4dage.com/data/'+ window.number +'/floor.json', 
            method: 'GET',
            success(res) {
                taht.floorJSON = res
                loadCAD(taht.floorJSON)
                
                $('#cadSignColor').on('change', function(ev) {
                    loadCAD()
                })

                $('#cadBorderColor').on('change', function(ev) {
                    loadCAD()
                })

                $('#cad-size').on('change', function(ev) {
                    loadCAD()
                })
            },
            error() {
                alert('当前场景没有小地图floor.json文件，请联系三维部门添加')
                ev.target.checked = false
            }
        })
    } else {
        console.log('0000000000000000000')
        $('.cad').show()
    }
})


function loadCAD(data) {
    grendCAD(data, $('#player')[0], $('#cadSignColor').val(), $('#cadBorderColor').val(), $('#cad-size').val())
}

 

//设置初始界面
function getSeft(seft) {
    $('#camera-start').on('click', function(e) {

        if (VisiSet.setPanoVisible) {
            return VisiSet.savePanoVisibles()
        } else if (VisiSet.setTagVisible) {
            return VisiSet.saveTagVisibles()
        }

        var snapType = editTool.hotpoint.editSpot ?  'hot' : 'screen'
        var preview = snapType == 'screen' ? $(".screen .shotImg.innerBtn")[0] : $('#hotpointDetail .shotImg.innerBtn')[0]
        var snap = JSON.parse("{" + seft.getSnapAngleInfo() + "}") 
         
        preview.cameraData = {
            camera: {
                position: snap.metadata.camera_position,
                quaternion: snap.metadata.camera_quaternion// 相机朝向
            },
            pano: {
                uuid: snap.metadata.scan_id // 起始点位的id 
            }
        }

        var ev = document.createEvent("MouseEvent");//截取视图 
        g_snapShotWidth = snapType == 'screen' ? 240 * 4 : 240;
        g_snapShotHeight = snapType == 'screen' ? 118 * 4 : 118;
        //window.screenSta = 'startScreen';
        ev.initMouseEvent("snapshotBegin", true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null);

        ev.__callback = (imgData,info)=>{
            uploadImg(imgData, (rs)=>{
                if (rs.code === 0) { 
                    preview.cameraData.thumbImg = rs.data;
                    preview.style['background-image'] = "url(" + rs.data + ")"
                }
            }
            , "thumbImg.jpg")

        }
        $(".screen .itemTitle button").removeClass('hide')
        window.dispatchEvent(ev);
    });

}

//热点编辑
var Hotpoint = function() {
    this.spotList = $(".spotList");
    this.hotpointDetail = $("#hotpointDetail");
    this._scale = {}
    this.scroller = {}
    this.scroller.boxDepth = new SlideBar({
        root: $('#boxDepth').eq(0),
        value: 0,
        min: 0 * 100,
        max: 1 * 100,
        name: "boxDepth",
        unitStr: "cm",
        avoidCrash: true,
        onchange: (s)=>{
            if (s == 0) {
                this.editSpot.addBox(false)
            } else {
                this.editSpot.addBox(true) 
                this.editSpot.scale.z = s / 100 ;
                this.updateTransform("scale")
            }
        },
        dragEndEvent: function() {
        }
    })
    // fyz 导览编辑页面同样采取热点编辑的样式, 这里确保只查找到热点编辑
}
 
Hotpoint.prototype.init = function() {
    var that = this;
    

  
    $(".hotStyle-item li").on('click', function() {//默认样式
        $(this).addClass('active').siblings().removeClass('active');
    })
    
    // 添加多媒体
    this.musicBox = musicPlayBoxBind(this.hotpointDetail.find("[name=audio].mediaUpload"), null )
  
    this.addmediaInit();
   
    // 添加音乐
    this.addModel();
    // 添加模型
    this.addwebPack();
    // 添加外链
    this.wireframeModel = new THREE.Object3D;
    
    if(Hot.allPhotoLoaded && Hot.allModelLoaded)  this.initListSelect()
    else Hot.whenAllFileLoaded = this.initListSelect.bind(this) //等全部photo加载完
    
    
    player.model.hotGroup.children.forEach(e=>this.addHotList(e) )//创建列表
   
  
    this.hotpointDetail.find(".tail .submit").on('click', this.saveHot.bind(this))
    
    
    this.hotpointDetail.find("[name=info] input").on('change',(e)=>{//标题
        this.editSpot.info.title = e.target.value
        this.editSpot.setTitleElem()
    })
    
    
    
    //media简介
    {
        let textarea = this.hotpointDetail.find('li[name="photo"] textarea, li[name="video"] textarea');
        textarea.on('change',(e)=>{ 
            if(!e.target.currentBtn)return
            e.target.currentBtn.desc = e.target.value;
            this.descBtnUpdate(e.target.currentBtn ) 
        })
        
        //应用到所有 
        let applyAll = (type)=>{
            var choice = confirm(`确定将文字应用到此热点的所有${type == 'photo' ? '图片' : '视频'}吗？`); 
            if (choice) { 
                Array.from(that.hotpointDetail.find(`[name=${type}] .list .descBtn`)).forEach(btn=>{
                    btn.desc = this.hotpointDetail.find(`li[name=${type}] textarea`)[0].value
                    this.descBtnUpdate(btn) 
                })
            }
        }
        this.hotpointDetail.find('li[name="photo"] button').on('click',()=>{
            applyAll("photo")
        })
        this.hotpointDetail.find('li[name="video"] button').on('click',()=>{
            applyAll("video")
        })
    }
    
    /* {
        let ui = $('#hotIconScale input')
        let min = parseFloat(ui.attr('min'));
        let max = parseFloat(ui.attr('max'));
        ui.on("change", function(e) {  
            var s = THREE.Math.clamp(parseFloat(e.target.value), min, max)
            if(isNaN(s))s = 1
            e.target.value = s   
            DATA.hotIconScale = s
        })
        
    } */
    
    
    $(".hotpoint [name='adjustSize'] button").on('click',(e)=>{
        let type = $(e.target).attr('index')
        var c = 1.1 
        if(type == '-'){
            s = 1/c
        }else{
            s = c
        }
        
        s = Math.max(0.001, s) 
        
        let sid = player.mode == 'panorama' ? player.currentPano.id : "outside";
        
        
        player.model.hotGroup.children.forEach(hot=>{
            if(hot.texType == 'shine'){
                hot.info.scale.multiplyScalar(s) 
                let s1 = hot.info.transformAtPanos[sid] ?.scale   //跳过独立调整的
                s1 || hot.scale.multiplyScalar(s) 
            }
        })
        
        
        DATA.hotIconScale *= s //没用，兼容旧版代码
        
    })
    
    
    
    
    
    
    
    
    this.isSpriteCheckBox = new CheckBox({dom:  $("#isSprite"),
        uiCallBack : (checked)=>{ 
             
        },
        callbackWhenChose:(checked)=>{
            that.editSpot.info.isSprite = checked;
            if(checked) {
                that.editSpot.update(player)
            }else{  
                that.editSpot.quaternion.copy(that.editSpot.info.quaternion)
            }  
        }
    })
      
    //clickEvent
    this.actionCheckBox = new CheckBox({dom:  this.hotpointDetail.find("li[name='clickEvent'] li.editCheckbox input"),
        uiCallBack : (checked, name)=>{ 
            if(name == 'openHot'){
                var content =  that.hotpointDetail.find("li[name='content']") 
                checked ? content.removeClass('hide') : content.addClass('hide')
            }else if(name == 'fastTran'){//瞬间过渡
                var fastTran = that.hotpointDetail.find("li[name='fastTran']")
                checked ? fastTran.removeClass('hide') : fastTran.addClass('hide')
            }
        },
        callbackWhenChose:(checked, name)=>{ 
            that.editSpot.info.actionType[name] = !!checked; 
            
        }
    })
 
    this.imgDescCheckBox = new CheckBox({dom:  this.hotpointDetail.find("#addImgDesc"),
        uiCallBack : (checked, name)=>{ 
            var textarea = this.hotpointDetail.find('li[name="photo"] textarea');
            var title = this.hotpointDetail.find('li[name="photo"] [name="descTitle"]')
            if(!checked){  
                textarea.addClass('hide'), title.addClass('hide')
                /* textarea.val('');
                textarea[0].currentBtn = null */
            }else{
                textarea.removeClass('hide'), title.removeClass('hide') 
            }
            var imgArea = this.hotpointDetail.find('li[name="photo"] .edit-fun-images');
            checked ? imgArea.addClass('hasDescBtn') : imgArea.removeClass('hasDescBtn')
                
        },
        callbackWhenChose:(checked, name)=>{ 
            that.editSpot.info.useImgDesc = !!checked; 
            
        }
    })
    
    this.videoDescCheckBox = new CheckBox({dom:  this.hotpointDetail.find("#addVideoDesc"),
        uiCallBack : (checked, name)=>{ 
            var textarea = this.hotpointDetail.find('li[name="video"] textarea');
            var title = this.hotpointDetail.find('li[name="video"] [name="descTitle"]')
            if(!checked){  
                textarea.addClass('hide'), title.addClass('hide')
                /* textarea.val('');
                textarea[0].currentBtn = null */
            }else{
                textarea.removeClass('hide'), title.removeClass('hide') 
            }
            var Area = this.hotpointDetail.find('li[name="video"] .edit-fun-images');
            checked ? Area.addClass('hasDescBtn') : Area.removeClass('hasDescBtn')
                
        },
        callbackWhenChose:(checked, name)=>{ 
            that.editSpot.info.useVideoDesc = !!checked;
        }
    })
     
     
    //点击添加热点按钮
    $('.hotpoint .addBtn button').on("click", function(e) { 
        g_HotStatus = $(e.target).attr('data-type');
        //$("#player").css("cursor", "cell");
        CursorDeal.add('addHot' )
        
    });
    
    
    
    //点击热点列表弹出按钮
    this.spotList.on("click", function(e) {
        var target = $(e.target); 
        let li = searchParent(e.target, { className: 'listItem' }, 7);
        if(!li)return;
        var hotId = $(li).attr("data-spid");
        var hot = player.model.hots[hotId] 
        
        if(VisiSet.setTagVisible){
            return VisiSet.SetOneTagVisible(hot)
        }
        
        if (target.hasClass("del")) {
            e.stopPropagation();
            target.siblings(".DelConfirm").addClass("active");
            that.spotList.find("i").text(that.spotList.find("ul li").length);
        } else {
            if (target.hasClass("DelConfirm")) {
                e.stopPropagation();
                that.removeHot(hot);
            } else {
                //点击热点列表弹出编辑热点窗口 
                that.editHot(hot, $(li))
                 
            }
        }
    });


    this.hotpointDetail.find(".tail button.delete").on("click", ()=>{ 
        var choice = confirm(`确定删除热点？`); 
        if (choice) { 
            that.removeHot(this.editSpot);
            cancelEdit(this.editSpot)
        } 
    })
    
    let cancelEdit = (hot)=>{ 
        this.tempInfos && player.model.hotGroup.children.forEach(hot=>{//还原所有同时修改了的贴图和模型
            var info = this.tempInfos[hot.sid] 
            if(hot.texType == 'shine'){
                hot.changeTexType(hot.texType,   info.styleImg  )
                hot.info.styleImg = info.styleImg.map(e=>e.src)
            }else{  
                hot.changeTexType(hot.texType,  info.texMedia )
                hot.info.texSrc = info.texMedia.src;
                if(hot.texType == 'photo' && hot.info.animateInfo){
                    GifTexDeal.remove(hot.animation)
                    hot.animation = GifTexDeal.addAnimation(hot.material_.map, hot, hot.info.animateInfo )
                    GifTexDeal.start(hot.animation)
                }
                
            }  
            if(hot.objObject != info.objObject){
                this.switchModel({add:info.objObject, spot:hot})
                hot.info.scale.copy(info.scale)
                hot.scale.copy(info.scale)
            }
        })
        
        
        if(this.texLists){
            for(let i in this.texLists){this.texLists[i].recover()}
            this.modelList.recover()
        }
        this.editDone();
          
    }
    
    
    //关闭、取消
    this.hotpointDetail.find(".head a.close, .tail button.cancel").on("click", ()=>{
        var hot = this.editSpot
        if(hot.isNew){
            this.removeHot(hot)
        }else{
            this.recoverInfo(hot) 
            hot.updateVisible([player.currentPano]) 
        }
        
        cancelEdit(hot) 
    });

      
 
    
    this.linkTypeMenuOptions = new MenuOptions({ //链接打开方式切换 
        dom: this.hotpointDetail.find(" .MenuOptions[name=linkType] "),
        uiCallBack : (o)=>{ 
            
        },
        callbackWhenChose:(o)=>{
            this.editSpot.info.linkType = o.$li.attr("index")
        }
    })
   
    
    this.panoTransformCheckBox = new CheckBox({dom: $('#setPosForPano input') ,
        uiCallBack : (checked, name)=>{ 
               
        },
        callbackWhenChose:(checked, name)=>{ 
            if(!checked){
                delete this.editSpot.info.transformAtPanos[getTransformSid()]  
                this.editSpot.position.copy(this.editSpot.info.position)
                this.editSpot.quaternion.copy(this.editSpot.info.quaternion) 
                this.editSpot.scale.copy(this.editSpot.info.scale)
            }else{
                this.getTransformAtPano(['position']) 
            } 
            
        }
    })
    
    /* $('#setPosForPano input').on("change", ()=>{
        var on = $('#setPosForPano input').is(':checked') 
        if(!on){
            delete this.editSpot.info.transformAtPanos[getTransformSid()]  
            this.editSpot.position.copy(this.editSpot.info.position)
            //this.editSpot.quaternion.copy(this.editSpot.quaternion) 
        }else{
            this.getTransformAtPano(['position']) 
        }
    })  */ 
    
    
    //是否显示模型线框
    this.wireframeModel = new THREE.Object3D;
     
    
    $('#wireframeSwitch input').on('change',(e)=>{
        var visi = e.target.checked;
        if(visi && this.wireframeModel.children.length == 0){
            var mat = new THREE.MeshBasicMaterial({
                wireframe: true,
                opacity: 0.5,
                color: "#57e4f3",
                transparent: true
            })
            
            this.wireframeModel.name = 'wireframeModel'
            player.model.chunks.forEach((mesh)=>{
                var chunk = mesh.clone();
                chunk.material = mat;
                this.wireframeModel.add(chunk)
            })
            this.wireframeModel.visible = false
            player.model.add(this.wireframeModel) 
        }
        this.wireframeModel.visible = visi
        this.wireframeModel.shouldShow = visi
        
    })
    
    
    $("#autoAdjustHotScale")[0].checked = !!DATA.autoAdjustHotScale;
    
    
    //动画
    
    this.gifCheckBox = new CheckBox({dom: $("#gifSwitch input"),
        uiCallBack : (checked )=>{  
            $("#gifInfoEdit").css({display: checked ? 'block' : 'none'})
            
        },
        callbackWhenChose:(checked )=>{ 
            if(checked){
                that.editSpot.info.animateInfo = {
                    cellXcount : parseInt($("#gifXCount").val()),
                    cellYcount : parseInt($("#gifYCount").val()),
                    voidCount : parseInt($("#voidCount").val()), 
                    loop : true,
                    duration : parseFloat($("#gifDuration").val()) * 1000
                }  
                that.editSpot.animation = GifTexDeal.addAnimation(that.editSpot.material_.map, that.editSpot, this.editSpot.info.animateInfo, this.editSpot.sid)       
                GifTexDeal.start(that.editSpot.animation)
            }else{
                GifTexDeal.remove(that.editSpot.animation) 
                that.editSpot.info.animateInfo = null
            }
            
        }
    })

    
   

    
    {//gif动画  input
        
        let changeAnimation = function(){
            GifTexDeal.setRepeart(that.editSpot.animation.animation)
            that.useSuitableRatio()
        }
        let restartAnimation = function(){
            GifTexDeal.stop(that.editSpot.animation)
            GifTexDeal.start(that.editSpot.animation)
        }
        let changeFrameCount = function(){
            var a = that.editSpot.info.animateInfo
            var frameCount = a.cellXcount * a.cellYcount - (a.voidCount||0)
            $("#gifFrameCount").text(frameCount)
            var fps = parseFloat($("#gifFps").val())
            a.duration = frameCount / fps * 1000
            $("#gifDuration").val(toPrecision(a.duration/1000, 2)) 
        }
        
        let gifXCountFun = function(value){
            that.editSpot.info.animateInfo.cellXcount = value 
            var gifImgWidth = parseFloat($("#gifImgWidth").text())
            var gifCellWidth = toPrecision(gifImgWidth / that.editSpot.info.animateInfo.cellXcount,1);
            $("#gifCellWidth").text(gifCellWidth)
            changeFrameCount()
            changeAnimation()
        }
       
        $("#gifXCount").on('input',(e)=>{
            strictInputNum(e, 0, 1) 
            gifXCountFun(parseFloat(e.target.value))
            
        })
        $("#gifXCount").on('change',(e)=>{
            strictInputNum(e, 0, 1) 
            gifXCountFun(parseFloat(e.target.value))
            restartAnimation()
        })
        
        
        let gifYCountFun = function(value){
            that.editSpot.info.animateInfo.cellYcount = value 
            var gifImgHeight = parseFloat($("#gifImgHeight").text())
            var gifCellHeight = toPrecision(gifImgHeight / that.editSpot.info.animateInfo.cellYcount,1);
            $("#gifCellHeight").text(gifCellHeight)
            changeFrameCount()
            changeAnimation()
        }
        $("#gifYCount").on('input',(e)=>{
            strictInputNum(e, 0, 1)
            gifYCountFun(parseFloat(e.target.value))
        })
        $("#gifYCount").on('change',(e)=>{
            strictInputNum(e, 0, 1)
            gifYCountFun(parseFloat(e.target.value))
            changeAnimation()
            restartAnimation()
        })
      
      
        /* let gifCellWidthFun = function(value){
            var gifCellWidth = value;
            var gifImgWidth = parseFloat($("#gifImgWidth").text())
            var cellXcount = toPrecision(gifImgWidth / gifCellWidth,2);
            $("#gifXCount").val(cellXcount)
            changeAnimation()
        }
        $("#gifCellWidth").on('input',(e)=>{
            strictInputNum(e, 0)
            gifCellWidthFun(parseFloat(e.target.value))
            
        })
        $("#gifCellWidth").on('change',(e)=>{
            strictInputNum(e, 0, 1)
            gifCellWidthFun(parseFloat(e.target.value))
            var cellXcount = $("#gifXCount").val();
            var cellXcountInt = Math.round(cellXcount)
            if(cellXcountInt != cellXcount){
                $("#gifXCount").val(cellXcountInt)
                gifXCountFun(cellXcountInt)
                changeAnimation()
            }
            restartAnimation()
        })  
        
        
        let gifCellHeightFun = function(value){
            var gifCellHeight = parseFloat(e.target.value);
            var gifImgHeight = parseFloat($("#gifImgHeight").text())
            var cellYcount = toPrecision(gifImgHeight / gifCellHeight,2);
            $("#gifYCount").val(cellYcount) 
            
        } 
        $("#gifCellHeight").on('input',(e)=>{
            strictInputNum(e, 0)
            gifCellHeightFun(parseFloat(e.target.value))
            
        })
        $("#gifCellHeight").on('change',(e)=>{
            strictInputNum(e, 0, 1)
            gifCellHeightFun(parseFloat(e.target.value))
            var cellYcount = $("#gifYCount").val();
            var cellYcountInt = Math.round(cellYcount)
            if(cellYcountInt != cellYcount){
                $("#gifYCount").val(cellYcountInt)
                gifYCountFun(cellYcountInt)
                changeAnimation()
            }  
            restartAnimation()        
        })*/
         
        $("#voidCount").on('change',(e)=>{
            strictInputNum(e, 0);
            that.editSpot.info.animateInfo.voidCount = parseInt(e.target.value)
            changeFrameCount() 
            restartAnimation()
        }) 
         
         
         
        let gifDurationFun = function(value){
            var a = that.editSpot.info.animateInfo 
            let frameCount = a.cellXcount * a.cellYcount
            a.duration = value
            $("#gifFps").val(toPrecision(frameCount / a.duration * 1000, 1))
        } 
        $("#gifDuration").on('input',(e)=>{
            strictInputNum(e, 1);
            gifDurationFun(parseFloat(e.target.value) * 1000)
        })
        $("#gifDuration").on('change',(e)=>{
            strictInputNum(e, 1, 0.01);
            gifDurationFun(parseFloat(e.target.value) * 1000)
            restartAnimation()
        })
        
        var gifFpsFun = function(e){
            strictInputNum(e, 0, 1);
            var fps = parseFloat(e.target.value)
            var a = that.editSpot.info.animateInfo 
            let frameCount = a.cellXcount * a.cellYcount - (a.voidCount||0)
            a.duration = frameCount / fps * 1000
            $("#gifDuration").val(toPrecision(a.duration / 1000, 2))
        }
        $("#gifFps").on('input',(e)=>{
            gifFpsFun(e)
            
        })
        $("#gifFps").on('change',(e)=>{
            gifFpsFun(e)
            restartAnimation()
        })
    }
    
    
    this.modelMenuOptions = new MenuOptions({
        dom: this.hotpointDetail.find(".MenuOptions[name=modelType]"),
        uiCallBack : (o)=>{ 
            var name = o.name == void 0 ?  o.$li.attr('index') : o.name
                      
            if(name == "default"){ 
                this.hotpointDetail.find('[name=boxDepthPanel]').removeClass('hide')
                this.updateBoxDepthPanel()
                this.hotpointDetail.find('[name=uploadObj]').addClass('hide')
                transformControls.unableScaleZ() 
                
            }else{
                this.hotpointDetail.find('[name=boxDepthPanel]').addClass('hide')
                this.hotpointDetail.find('[name=uploadObj]').removeClass('hide')
                  
                
            }
            
        }, 
        callbackWhenChose:(o)=>{
            var name = o.$li.attr('index')
            
            if(name == "default"){ 
                /* transformControls.unableScaleZ()
                this.editSpot.removeModel() */
                this.switchModel({remove:true, spot:this.editSpot})
                 
                if(this._scale[this.editSpot.texType]){//恢复大小
                    this.editSpot.scale.copy(this._scale[this.editSpot.texType]);
                    this.editSpot.info.scale.copy(this._scale[this.editSpot.texType]);
                } 
            }else{
                this.modelList.options.selectFun()
                //this._scale[this.editSpot.texType] = this.editSpot.scale.clone()
            }
            
        }
    })
    
    
    this.titleShowOptions = new MenuOptions({ //何时显示
        dom:  this.hotpointDetail.find(".MenuOptions[name=titleShowType]"),
        uiCallBack : (o)=>{ 
             
        },
        callbackWhenChose:(o)=>{
            var name = o.name == void 0 ?  o.$li.attr('index') : o.name            
            this.editSpot.info.titleShowType = name
            this.editSpot.setHoverState(false) //update
        }
         
    })
    
    this.titlePosOptions = new MenuOptions({ //相对热点的位置
        dom:  this.hotpointDetail.find(".MenuOptions[name=titlePos]"),
        uiCallBack : (o)=>{
            
        },
        callbackWhenChose:(o)=>{
            var name = o.name == void 0 ?  o.$li.attr('index') : o.name            
            this.editSpot.info.titlePos = name
            this.editSpot.setTitleDir();
            this.latestTitlePos = this.editSpot.info.titlePos//最近一次设置的，运用到新的热点中
        }
         
    })
    
    
    
    
    this.hotpointDetail.find("li[name='fastTran'] button").on('click',(e)=>{  
        this.setCameraBtn(!this.gettingCameraData)  
    })
    
    {
        let pos = new THREE.Vector3(Infinity,Infinity,Infinity)
        player.on("view.changed",(e)=>{ 
             if(e.cameraChanged && !pos.equals(player.position)){
                 this.updateDis();
                 pos.copy(player.position)
             } 
        })
    }
}


Hotpoint.prototype.setCameraBtn = function(state){
    let btn = this.hotpointDetail.find("li[name='fastTran'] button")
    if(!state){
        confirmSnap.addClass("hide");
        snapshotGui.hide();
        btn.text('设置')
        this.gettingCameraData = false
    }else{
        confirmSnap.removeClass("hide");
        $('#camera-start').text("点此设置跳转到此方位")
        snapshotGui.show();
        if (player.mode != "panorama"){ 
            $(".confirmSnap").addClass("unable")
        }else{
            $(".confirmSnap").removeClass("unable")
        }
        btn.text('结束设置')
        this.gettingCameraData = true
    }
}

Hotpoint.prototype.updateBoxDepthPanel = function(){
    var hot = this.editSpot
    if (hot.info.hasBox) {
        this.scroller.boxDepth.setValue(hot.scale.z * 100, true)
    } else {
        this.scroller.boxDepth.setValue(0, true)
    }
             
    setTimeout(()=>{
        this.scroller.boxDepth.InitOffset()
    }, 201)
    
}
/**
 * 显示编辑热点的窗口和初始化热点窗口的数据
 */
Hotpoint.prototype.editHot = function(hot, $li) {
    try {
        transformControls.attach(hot)
        if(player.mode == 'panorama'){
            hot.examine( {
                dontOpen: true, examine:true,
                dontFastTran: true,
                aimDuration : 600,
                duration : 600
            })
        }else{
            player.focusPoint({aim: hot.position, radius:hot.position.distanceTo(player.position)}) 
        }  
    } catch (e) {}
    
    
    if(this.editSpot == hot)return
    
    this.hotpointDetail.removeClass("atRight");
    $("#hotpointDetail .audio.mediaUpload").find("input").val('');
  
    var playIcon = 'images/play.png';
    this.editSpot = hot 
    
    
    transformControls.transformMenuOptions.updateChoseAtUI({name:'translate'})    //$(".MenuOptions[name='transform'] li[index='translate']").click()//transformControls.transCtlChangeMode("translate")

    this.getTempInfo(hot) 
    convertTool.updateVisible(hot,'visi',true)   
         
     
    this.updatePano() 
    this.updateListSelect(hot);
     
      
    //$("#isSprite")[0].checked = this.editSpot.info.isSprite
    this.isSpriteCheckBox.updateChoseAtUI(this.editSpot.info.isSprite)
   
    this.imgDescCheckBox.updateChoseAtUI(this.editSpot.info.useImgDesc)
    this.videoDescCheckBox.updateChoseAtUI(this.editSpot.info.useVideoDesc)
    
    this.linkTypeMenuOptions.updateChoseAtUI({name:this.editSpot.info.linkType})
     
     
    this.updateClickEventUI();//根据actionType更新ui
    
     
      
    var iframesHTML = hot.info.iframe.map(function(iframe) {
        return this.inputList("请填写网页链接", iframe)
    }.bind(this))

    var modulesHTML = hot.info.model.map(function(module) {
        return this.inputList("请填写网页链接", module)
    }.bind(this))

    var imagesHTML = hot.info.images.map(function(image,index) { 
        return '<div class="mediaItem" draggable="true" ><a class=" result success" data-type="photo"><span></span><img src="' + image + '" class="bg"><div class="descBtn" ></div></a></div>'
    })

 

    var videosHTML = hot.info.video.map(function(ly) {
        var thumb = (!ly.img || ly.img === "undefined") ? '' : '<img src='+ ly.img +' class="bg"></img>'
        var thumbResult = (!ly.img || ly.img === "undefined") ? '' : 'success';
        var dom = '<div class="mediaItem" draggable="true"><a class=" result success" data-type="video"><span></span>' + thumb + ' <img class="play-video" videoURL="' + ly.url + '" src="' + playIcon + '"><div class="descBtn" ></div></a>  <span class="upload-thum ' + thumbResult + ' result" attr-thum="' + ly.img + '">上传封面<i><input type="file" accept="image/*"></i></span></div>'
        return dom
    })

    this.hotpointDetail.find(".name > input").val(hot.info.title);
    this.hotpointDetail.find('[name="intro"]  .editText').val(hot.info.content);
    this.hotpointDetail.find("[name=photo] .list").html(imagesHTML);
    this.hotpointDetail.find("[name=video] .list").html(videosHTML);
    this.hotpointDetail.find("[name=modelSrc] .list").html(modulesHTML);
    this.hotpointDetail.find("[name=webPage] .list").html(iframesHTML);
    Array.from(this.hotpointDetail.find("[name=photo] .list .descBtn")).forEach((btn,index)=>{ 
        this.descBtnBind(btn, 'photo', this.editSpot.info.imagesDesc[index]) 
        this.descBtnUpdate(btn,index) 
    })
    Array.from(this.hotpointDetail.find("[name=video] .list .descBtn")).forEach((btn,index)=>{ 
        this.descBtnBind(btn, 'video', this.editSpot.info.videosDesc[index]) 
        this.descBtnUpdate(btn,index) 
    })
    
    
    this.descBtnChose(null, 'photo')
    this.descBtnChose(null, 'video')
    
    
    let bgName = hot.info.bgName || '热点背景音乐';
    if (hot.info.backgroundMusic) { 
        this.musicBox.show(/* this.hotpointDetail.find(".audio.mediaUpload"),  */bgName, hot.info.backgroundMusic)
    } else {
        this.musicBox.hide() 
    }

    
    

    {//跳转方位设置 
        var preview = $('#hotpointDetail .shotImg.innerBtn')[0]
        preview.cameraData = hot.info.cameraData   
        if(hot.info.cameraData){
            preview.style['background-image'] = "url(" + hot.info.cameraData.thumbImg + ")"
        }else{
            preview.style['background-image'] = ''
        } 
    }

    {//标题显示
        
        this.titleShowOptions.updateChoseAtUI({name:this.editSpot.info.titleShowType})
        this.titlePosOptions.updateChoseAtUI({name:this.editSpot.info.titlePos})
        
    }

    
    $('#setPosForPano [name="dis"]').removeClass('hide');
    this.updateDis()
    

}

Hotpoint.prototype.updateDis = function(){
    if(!this.editSpot)return
    
    var pos = this.editSpot.position;
    var playerPos = player.position;
    
    var dis1 = pos.clone().setY(0).distanceTo(playerPos.clone().setY(0))
    var disY = pos.y - playerPos.y
    var dir =  disY>0 ? '上' : '下' 
    $('#setPosForPano [name="dis"]').html(`与相机水平距离: ${toPrecision(dis1, 2)}m , <br>在相机之${dir}: ${toPrecision(Math.abs(disY), 2)}m`) 
}

Hotpoint.prototype.recoverInfo = function(hot) { //  恢復
    hot.info = hot.tempInfo;
    
    hot.setFromInfo(hot.info, hot.tempInfo.media,  hot.tempInfo.objObject)
    delete hot.info.media
    delete hot.info.objObject
}

Hotpoint.prototype.getTempInfo = function(hot) { //  编辑前， 从当前状态获取info, 用於恢復
    var plane = hot.plane;
    
    var info = hot.info;
    
    hot.tempInfo = CloneObject(info);
    hot.tempInfo.media = hot.texMedia
    hot.tempInfo.objObject = hot.objObject
    
    
    this.getTempInfos() 
} 

Hotpoint.prototype.getTempInfos = function(){
    if(Hot.allPhotoLoaded && Hot.allModelLoaded){
        this.tempInfos = {}
        
        player.model.hotGroup.children.forEach(hot=>{
            this.tempInfos[hot.sid] = {
                styleImg :  hot.styleImg,
                texMedia : hot.texMedia,
                objObject : hot.objObject,
                scale : hot.scale.clone()
            } 
        }) 
    }
}

Hotpoint.prototype.getTransformAtPano = function(types){
    /* var name = getTransformSid()
    var info = {
        pos : this.editSpot.mesh.position.clone() 
    }
    if(!this.editSpot.isSprite){
        info.qua = this.editSpot.mesh.quaternion.clone()   
    }else{
        if(type == 'quaternion')return;   //如果恒朝向相机且调整的是quaternion，不要保存position
    }
    this.editSpot.transformAtPanos[name] = info */
    //this.editSpot.usingTransformData = true
    
    
    var name = getTransformSid()
    let info = {
        pos : this.editSpot.position.clone(),
        scale : this.editSpot.scale.clone()
    }
    hot.isSprite || (info.qua = this.editSpot.quaternion.clone())
      
    this.editSpot.info.transformAtPanos[name] = info
        
     
}  

Hotpoint.prototype.updateTransform = function(types=['position','quaternion','scale']){
    var on = this.panoTransformCheckBox.checked() //$('#setPosForPano input').is(':checked')
    //var name = getTransformSid()
    if(on ){ 
        this.getTransformAtPano(types)  
    }else{ 
        if(types.includes('position')){
            this.editSpot.info.position.copy(this.editSpot.position)
        }else if(types.includes('quaternion')){
            if(!this.editSpot.isSprite){ //如果恒朝向相机就不保存quaternion
                this.editSpot.info.quaternion.copy(this.editSpot.quaternion)
            }
        }else if(types.includes('scale')){  
            this.editSpot.info.scale.copy(this.editSpot.scale)
        }   
    }
    
    
    types.includes('position') && this.updateDis()
}

Hotpoint.prototype.updatePano = function(){
    if(!this.editSpot ) return;
    //$('#setPosForPano input').prop('checked', !!this.editSpot.info.transformAtPanos[getTransformSid()]);
    this.panoTransformCheckBox.updateChoseAtUI(!!this.editSpot.info.transformAtPanos[getTransformSid()])
}

Hotpoint.prototype.unsaveWarn = async function(done){//如果在编辑某个热点时触发了结束编辑，询问是否保存
    if(this.editSpot){
        var sure = confirm("是否保存当前热点？")
        if(sure){
            await this.saveHot()   
        } 
        done && done()
    }else if(VisiSet.setTagVisible && (VisiSet.tagVsetting && VisiSet.saveLastTagVi({getIfChange:true}) || Object.keys(VisiSet.tagVTemp).length)){//修改过
        var sure = confirm("是否保存已编辑的热点可视？")
        if(sure){
            VisiSet.saveTagVisibles()
        } 
        done && done() 
    } 
}
 
 
 
 

var ifSameTex = function(imgs0, imgs1){//是否是相同的图   （两个都是空的不算相同）
    return !!(imgs0[0] && imgs1[0] && imgs0[0].src == imgs1[0].src &&
        (imgs0[1] ? (imgs1[1] && imgs0[1].src == imgs1[1].src) :  !imgs1[1] ))
} 

Hotpoint.prototype.findSpotByContent = (media, type)=>{////使用了该贴图的所有热点 
    var spots = []
    
    if(type == 'shine'){
        media = media || []
        for(var i in player.model.hots){ 
            if(player.model.hots[i].texType != type)continue
            var styleImg = player.model.hots[i].styleImg || [];
            if(ifSameTex(styleImg,	media)){
                spots.push(player.model.hots[i])
            }
        }   
    }else if(type == 'photo' || type == 'video'){
        if(type == 'photo' && !Hot.allPhotoLoaded)return spots
        for(var i in player.model.hots){ 
            if(player.model.hots[i].texType != type)continue
            var texMedia = player.model.hots[i].texMedia 
            if(media && texMedia && media.src == texMedia.src){
                spots.push(player.model.hots[i])
            }
        } 
        
    }else if(type == 'model'){
        if(!Hot.allModelLoaded)return spots
        for(var i in player.model.hots){ 
            if(!player.model.hots[i].objObject)continue
            var objObject = player.model.hots[i].objObject
            if(media && objObject && media.src == objObject.src){
                spots.push(player.model.hots[i])
            }
        } 
        
    }        
    
    return spots
}

Hotpoint.prototype.initListSelect = function(){//热点样式图列表
    var that = this; 
    var countElem = $("<a class='showCount'></a>") 
     
    var uploadInputImage = $('<input  type="file" style="display:none" accept="image/*"></input>');
    var uploadInputVideo = $('<input  type="file" style="display:none" accept="video/*"></input>');
    var uploadInputModel = $('<input  type="file" style="display:none" accept=".obj,.OBJ"></input>');
    
    var getListItem = ()=>{
        var elem = $('<li><ul class="hotTexUpload"><li class="upload"><div></div><a class="del hasHover"></a></li><li class="upload hide"><div></div><a class="del hasHover"></a></li></ul></li>')
        elem[0]._styleImg = []
        return elem
    }
    var editItemInfo = {}
    var setImgContent = function($dom, index, src) {
        var li = $dom.find('li').eq(index)
        var div = li.find('>div');
        if (src) {
            li.addClass("uploaded")
            div.css("background-image", `url(${src})`)
        } else {
            li.removeClass("uploaded")
            div.css("background-image", '')
        }  
    }
     
    var hotIcons = [];  // 从所有热点tex中搜集 。
    player.model.hotGroup.children.forEach(e=>{// 从所有热点tex中搜集
        if(e.texType == 'shine'){
            let imgs = e.info.styleImg;
            if(imgs && imgs.length){ 
                if(!hotIcons.find(e=>e[0] == imgs[0] && e[1] == imgs[1])){ 
                    hotIcons.push(imgs)
                }
            }
        } 
    })
      
    hotIcons = hotIcons.map(imgs=>imgs = imgs.map((a)=>{return {src:a}})) 
    //hotIcons = [[], [g_HotImage.point, g_HotImage.point2], ...hotIcons] 
    hotIcons.push([{src:g_HotImage.point}, {src:g_HotImage.point2}],[] )//放末尾用来添加的  g_HotImage是默认
    
    var list = hotIcons.map(arr=>{//初始列表
        var elem = getListItem()
        elem[0]._styleImg = arr     //每个li上绑定一个 _styleImg，存储它包含的图， 和热点的styleImg对应
        var divs = elem.find("li>div")
        arr.forEach((img,i)=>{ 
            setImgContent(elem, i, img.src)
        }) 
        arr.length && elem.find("li").eq(1).removeClass('hide') 
        return elem 
    })
    list[list.length - 2].addClass('forbitEdit') 
     
     
    this.styleList = new ListBox(list,  $('#shineTexSelect'), {//列表
        selectFun:(o={})=>{//选中时要改变选中项selection缩略图 以及改变热点 
            if(this.styleList.chosenItem == void 0){
                this.styleList.listTitle.find('li').addClass('hide')
            }else{
                this.styleList.listTitle.find('li').removeClass('hide')
                var divs = this.styleList.listTitle.find('li>div')//selection
                var imgs = this.styleList.chosenItem[0]._styleImg;
                if(imgs[0]){
                    divs.eq(0).css("background-image", 'url("'+imgs[0].src+'")')
                    divs.eq(0).parent().removeClass('hide')
                }else{
                    divs.eq(0).parent().addClass('hide')
                }
                
                if(imgs[1]){ 
                    divs.eq(1).css("background-image", 'url("'+imgs[1].src+'")')
                    divs.eq(1).parent().removeClass('hide')
                }else{
                    divs.eq(1).parent().addClass('hide')
                }
                //应用到热点：
                o.dontApplyToHot || this.editSpot.changeShineTex(imgs)  
            }
            
            Array.from(this.styleList.listDom.children()).forEach(li=>{//更新一下所有的li的使用的spot
                li.sameContentSpots = this.findSpotByContent(li._styleImg, 'shine')
            })
        
        },  
        /* delFun: ()=>{
            
        }, */  
        addFun: ($li)=>{ //列表新增时对li的操作
            $li[0].sameContentSpots = this.findSpotByContent($li[0]._styleImg, 'shine')
            
            $li.find('li').bind('click', (e)=>{
                e.stopPropagation()
                var index = $(e.currentTarget).index();
                 
                if($li.hasClass('forbitEdit'))return
                if (e.target.classList.contains("del")) { //删除图片 
                    if ($li[0]._styleImg.length == 2 && index == 0) { //删除第一个 且有第二个的话
                        setImgContent($li, 0, $li[0]._styleImg[1].src);
                        setImgContent($li, 1, null);
                        $li[0]._styleImg = [$li[0]._styleImg[1]]
                    } else {
                        setImgContent($li, index, null);
                        $li[0]._styleImg.pop()
                        if ($li[0]._styleImg.length == 0) {
                            //$li.find("li").eq(1).addClass("hide");
                            this.styleList.removeItem($li)  
                        }
                    }
                    
                    $li[0].sameContentSpots.forEach(hot=>{
                        hot.changeShineTex($li[0]._styleImg)
                    })
                    
                    if($li.hasClass('selected')){//是当前spot的
                        //this.editSpot.changeTex($li[0]._styleImg)
                        this.styleList.options.selectFun()
                    } 
                    
                   
                } else {
                    //添加图片 
                    uploadInputImage.click()
                    editItemInfo = {
                        imgs : $li[0]._styleImg,  index,  $li    , type:"shine" 
                    };
                }
            })
            
            $li.bind('mouseover', (e)=>{
                /* this.showTitileSpots = $li[0].sameContentSpots; 
                this.showTitileSpots.forEach(a=>a.showTitle())  */
                countElem.text("热点个数："+$li[0].sameContentSpots.length).removeClass('hide')
                $li.append(countElem)  
                $li[0].sameContentSpots.forEach(hot=>{
                    hot.changeBoxHelperDisplay(true)
                })
                 
            
            })
            $li.bind('mouseout', (e)=>{ 
                /* this.showTitileSpots.forEach(a=>a.hideTitle())
                this.showTitileSpots = null; */
                countElem.addClass('hide') 
                $li[0].sameContentSpots.forEach(hot=>{
                    hot.changeBoxHelperDisplay(false)
                })
            }) 
        },
        saveTemp: (list)=>{//编辑热点时保存一份副本
            list._styleImgTemp =  Array.from(list.listDom.children()).map((li)=>{
                return li._styleImg.slice()
            })
        },
        recover : (list)=>{//取消编辑时恢复
            Array.from(list.listDom.children()).forEach((li,i)=>{
                li._styleImg = list._styleImgTemp[i]
                li.sameContentSpots = this.findSpotByContent(li._styleImg, 'shine')
            })
        }, 
        
    })

    this.styleList.findChosenByContent = (imgs=[])=>{//找到某图对应的li
        var list = Array.from(this.styleList.listDom.children())
        var item = list.find(e=>ifSameTex(e._styleImg, imgs )) 
        return {index: item ? list.indexOf(item) : null,  item: item  }
    }
    
    /* this.styleList.clearVoid = ()=>{//清理空的
        var length = this.styleList.listDom.children().length;
        Array.from(this.styleList.listDom.children()).forEach((e, i)=>{
            if(i > 0 ){
                if(e._styleImg.length == 0) this.styleList.removeItem($(e))
            }   
        })
    } */
    this.styleList.addNew = ()=>{//看是否在第一行增加新的 
        /* var lastOne = this.styleList.listDom.children().last();
        if(lastOne[0]._styleImg.length != 0){
            this.styleList.addItem(getListItem())
        }  */
        
        let firstOne = this.styleList.listDom.children().first();
        if(firstOne[0]._styleImg.length != 0){
            this.styleList.addItem(getListItem())
        }
    }
     
     
      
     
    //-----photo----video-----------------------
    
    
    
    
    
    
    var getPhotoListItem = ()=>{
        var elem = $('<li><div class="upload"><div></div><a class="del hasHover"></a></div></li>')
        return elem
    }
    var getVideoListItem = ()=>{
        var elem = $('<li><div class="upload"><div></div><a class="del hasHover"></a></div></li>') 
        return elem
    }
    
    var photos = [] ,videos = []
    var photoList = [], videoList = []
    
    player.model.hotGroup.children.forEach(hot=>{
        if(hot.texType == 'photo'){  
            var media = hot.texMedia
            if(!photos.find(u=>media == u)){
                photos.push(media)
                
                var elem = getPhotoListItem()
                elem[0]._media = media   
                elem[0].animateInfo = CloneObject(hot.info.animateInfo)
                elem.find(".upload>div").css("background-image", "url(" + hot.info.texSrc + ")")
                elem.find(".upload").addClass("uploaded")
                photoList.push(elem) 
                
            }else{
                //尽量使用带ani的
                if(hot.info.animateInfo){
                    let elem = photoList.find(elem=>elem[0]._media == media)
                    if(!elem[0].animateInfo) elem[0].animateInfo = CloneObject(hot.info.animateInfo)
                }
            } 
        }else if(hot.texType == 'video'){
            var media = hot.texMedia
            if(!videos.find(u=>media.src == u.src)){ //if(!videos.find(u=>media == u)){  
                videos.push(media)
                
                var elem = getVideoListItem()
                elem[0]._media = media 
                elem.attr("title", media.name) 
                elem.find(".upload>div").text(media.name)
                elem.find(".upload").addClass("uploaded")
                videoList.push(elem)
                
            } 
            
        }
    })
   
    
    
    photoList.push(getPhotoListItem())//放末尾用来添加的
    this.photoList = new ListBox(photoList,  $('#photoTexSelect'), {//列表  
        selectFun:(o={})=>{//选中时要改变选中项selection缩略图 以及改变热点 
            var div = this.photoList.listTitle.find('.uploaded')//selection
            
            var media; 
            if(this.photoList.chosenItem){ 
                media = this.photoList.chosenItem[0]._media; 
            }
            if(media){
                div.removeClass('hide')
                div.css("background-image", 'url("'+media.src+'")')
                //应用到热点：
                if(!o.dontApplyToHot){ 
                    this.editSpot.changeTexType("photo", media || 'clear' )  
                    this.updateGifPanel({animateInfo: this.photoList.chosenItem[0].animateInfo }) 
                    this.useSuitableRatio()                    
                }else{
                    this.updateGifPanel({on: !!this.photoList.chosenItem[0].animateInfo  })    
                }
                 
                $('#gifEdit').removeClass('hide');
                
            }else{
                !o.dontApplyToHot && this.editSpot.changeTexType("photo",   'clear' )  
                this.updateGifPanel({on:false})
                $('#gifEdit').addClass('hide');
                div.addClass('hide')
            }
            
            
            Array.from(this.photoList.listDom.children()).forEach(li=>{//更新一下所有的li的使用的spot
                li.sameContentSpots = this.findSpotByContent(li._media, 'photo')
            })
        
        },  
        addFun: ($li)=>{ //photoList列表新增时对li的操作
            $li[0].sameContentSpots = this.findSpotByContent($li[0]._media, 'photo')
            
            $li.find('.upload').bind('click', (e)=>{
                e.stopPropagation()
                var index = $(e.currentTarget).index();
                 
                
                if (e.target.classList.contains("del")) { //删除图片 
                     
                    /* if($li.hasClass('selected')){//(肯定)是当前spot的 
                        that.editSpot.changeTexType('photo', 'clear')  
                    }                    
                    $li[0]._media = null */
                    
                    this.photoList.removeItem($li)  
                } else {
                    //添加图片 
                    uploadInputImage.click()
                    editItemInfo = {
                        $li   , type:"photo" 
                    };
                }
            })
            
            $li.bind('mouseover', (e)=>{
                /* this.showTitileSpots = $li[0].sameContentSpots; 
                this.showTitileSpots.forEach(a=>a.showTitle())  */
                countElem.text("热点个数："+$li[0].sameContentSpots.length).removeClass('hide')
                $li.append(countElem)  
                $li[0].sameContentSpots.forEach(hot=>{
                    hot.changeBoxHelperDisplay(true)
                })
                 
            
            })
            $li.bind('mouseout', (e)=>{ 
                /* this.showTitileSpots.forEach(a=>a.hideTitle())
                this.showTitileSpots = null; */
                countElem.addClass('hide') 
                $li[0].sameContentSpots.forEach(hot=>{
                    hot.changeBoxHelperDisplay(false)
                })
            }) 
            
        },
        
        saveTemp: (list)=>{//编辑热点时保存一份副本
            list._mediaTemp =  Array.from(list.listDom.children()).map((li)=>{
                return li._media
            })
            list._animationTemp =  Array.from(list.listDom.children()).map((li)=>{
                return li.animateInfo
            })
            
        },
        recover : (list)=>{//取消编辑时恢复
            Array.from(list.listDom.children()).forEach((li,i)=>{
                li._media = list._mediaTemp[i]
                li.animateInfo = list._animationTemp[i]  
                li.sameContentSpots = this.findSpotByContent(li._media, 'photo')
            }) 
        }, 
    })
    
    this.photoList.addNew = ()=>{//看是否在第一行增加新的  
        let firstOne = this.photoList.listDom.children().first();
        if(firstOne[0]._media){
            this.photoList.addItem(getPhotoListItem())
        }
    }
    
    
    
    //---videoList-------
    
    
    videoList.push(getVideoListItem())//放末尾用来添加的
    this.videoList = new ListBox(videoList,  $('#videoTexSelect'), {//列表   
        selectFun:(o={})=>{  
            var div = this.videoList.listTitle//selection 
             
            var media; 
            if(this.videoList.chosenItem){ 
                media = this.videoList.chosenItem[0]._media; 
            }
            if(media){
                //div.removeClass('hide')
                div.text(media ? media.name : '') 
                //应用到热点：
                this.editSpot.info.texSrc = media && media.src      //视频不能共用一个，否则会一起播放暂停
                this.editSpot.info.fileName = media.name
                this.editSpot.videoWidth = media.videoWidth //记录。因为新new的话要等一会儿才不为0
                this.editSpot.videoHeight = media.videoHeight 
                //media.currentTime = 0 //触发oncanplaythrough 
                if(!o.dontApplyToHot){ 
                    this.editSpot.changeTexType("video" , media ? null : 'clear'    )   
                    this.useSuitableRatio()
                } 
            }else{
                //div.addClass('hide')
                div.text('')
                if(!o.dontApplyToHot){ 
                    this.editSpot.changeTexType("video" ,   'clear'    )  
                }
            }
             
           
            
            Array.from(this.videoList.listDom.children()).forEach(li=>{//更新一下所有的li的使用的spot
                li.sameContentSpots = this.findSpotByContent(li._media, 'video')
            })
        
        },
        addFun: ($li)=>{  
            $li[0].sameContentSpots = this.findSpotByContent($li[0]._media, 'video')
            
            $li.find('.upload').bind('click', (e)=>{
                e.stopPropagation()
                var index = $(e.currentTarget).index();
                 
                
                if (e.target.classList.contains("del")) { //删除 
                     
                    if($li.hasClass('selected')){//是当前spot的 
                        that.editSpot.changeTexType('video', 'clear')  
                    } 
                    $li[0]._media = null
                    this.videoList.removeItem($li)  
                } else {
                    //添加  
                    uploadInputVideo.click()
                    editItemInfo = {
                        $li   , type:"video" 
                    };
                }
            })
            
            $li.bind('mouseover', (e)=>{
                /* this.showTitileSpots = $li[0].sameContentSpots; 
                this.showTitileSpots.forEach(a=>a.showTitle())  */
                countElem.text("热点个数："+$li[0].sameContentSpots.length).removeClass('hide')
                $li.append(countElem)  
                $li[0].sameContentSpots.forEach(hot=>{
                    hot.changeBoxHelperDisplay(true)
                })
                 
            
            })
            $li.bind('mouseout', (e)=>{ 
                /* this.showTitileSpots.forEach(a=>a.hideTitle())
                this.showTitileSpots = null; */
                countElem.addClass('hide') 
                $li[0].sameContentSpots.forEach(hot=>{
                    hot.changeBoxHelperDisplay(false)
                })
            }) 
            
        },
        
        saveTemp: (list)=>{//编辑热点时保存一份副本
            list._mediaTemp =  Array.from(list.listDom.children()).map((li)=>{
                return li._media
            })
        },
        recover : (list)=>{//取消编辑时恢复
            Array.from(list.listDom.children()).forEach((li,i)=>{
                li._media = list._mediaTemp[i]
                li.sameContentSpots = this.findSpotByContent(li._media, 'video')
            })
        }, 
        
    
    })
    
    this.videoList.addNew = ()=>{//看是否在第一行增加新的 
        let firstOne = this.videoList.listDom.children().first();
        if(firstOne[0]._media){
            this.videoList.addItem(getVideoListItem())
        }
    }
    
    
    this.photoList.findChosenByContent = this.videoList.findChosenByContent = (list_, media)=>{//找到某图对应的li
        var list = Array.from(list_.listDom.children())
        var item = list.find(e=>e._media && media && e._media.src == media.src) 
        return {index: item ? list.indexOf(item) : null,  item: item  }
    }
    
   /*  this.photoList.clearVoid = this.videoList.clearVoid =  (list_)=>{//清理空的
        var length = list_.listDom.children().length;
        Array.from(list_.listDom.children()).forEach((e, i)=>{
            if(i > 0 ){
                if(!e._media ) list_.removeItem($(e))
            }   
        })
    } */
    
    
    
   
    //----modelList--------------------- 
    var objSrcs = []
    var modelList = []
    var getModelListItem = ()=>{
        var elem = $('<li><div class="upload"><div></div><a class="del hasHover"></a></div></li>') 
        return elem
    }
    
    player.model.hotGroup.children.forEach(e=>{  
        if(e.info.objSrc && !objSrcs.find(u=>e.info.objSrc == u)){
            objSrcs.push(e.info.objSrc)
            
            var elem = getModelListItem()
            elem[0].objObject = e.objObject 
            elem.attr("title", e.info.objName)
            elem.find(".upload>div").text(e.info.objName)
            elem.find(".upload").addClass("uploaded")
            
            
            modelList.push(elem)
            
        }     
    })
    
    modelList.push(getModelListItem())//放末尾用来添加的
    this.modelList = new ListBox(modelList,  $('#ObjSelect'), {//列表   
        selectFun:(o={})=>{  
            var div = this.modelList.listTitle//selection 
             
            var model; 
            if(this.modelList.chosenItem){ 
                model = this.modelList.chosenItem[0].objObject; 
            }
            if(model){
                //div.removeClass('hide')
                div.text(model ? model.name : '') 
                //应用到热点： 
                if(!o.dontApplyToHot){ 
                    
                    this.switchModel({add:model,spot:this.editSpot})
                } 
            }else{
                //div.addClass('hide')
                div.text('')
                this.switchModel({remove:true,spot:this.editSpot})
                
                
            }
             
           
            
            Array.from(this.modelList.listDom.children()).forEach(li=>{//更新一下所有的li的使用的spot
                li.sameContentSpots = this.findSpotByContent(li.objObject, 'model')
            })
        
        },
        addFun: ($li)=>{  
            $li[0].sameContentSpots = this.findSpotByContent($li[0].objObject, 'model')
            
            $li.find('.upload').bind('click', (e)=>{
                e.stopPropagation()
                var index = $(e.currentTarget).index();
                 
                
                if (e.target.classList.contains("del")) { //删除  
                     
                    $li[0].sameContentSpots.forEach(e=>{
                        /* e.removeModel() */
                        that.switchModel({remove:true, spot:e}) 
                    })
                    if($li.hasClass('selected')){//是当前spot的 ui..
                         
                    }
                    $li[0].objObject = null
                    this.modelList.removeItem($li)  
                } else {
                    //添加  
                    uploadInputModel.click()
                    editItemInfo = {
                        $li   , type:"model" 
                    };
                }
            })
            
            $li.bind('mouseover', (e)=>{
                /* this.showTitileSpots = $li[0].sameContentSpots; 
                this.showTitileSpots.forEach(a=>a.showTitle())  */
                countElem.text("热点个数："+$li[0].sameContentSpots.length).removeClass('hide')
                $li.append(countElem)  
                $li[0].sameContentSpots.forEach(hot=>{
                    hot.changeBoxHelperDisplay(true)
                })
                 
            
            })
            $li.bind('mouseout', (e)=>{ 
                /* this.showTitileSpots.forEach(a=>a.hideTitle())
                this.showTitileSpots = null; */
                countElem.addClass('hide') 
                $li[0].sameContentSpots.forEach(hot=>{
                    hot.changeBoxHelperDisplay(false)
                })
            }) 
            
        },
        
        saveTemp: (list)=>{//编辑热点时保存一份副本
            list._objTemp =  Array.from(list.listDom.children()).map((li)=>{
                return li.objObject
            })
        },
        recover : (list)=>{//取消编辑时恢复
            Array.from(list.listDom.children()).forEach((li,i)=>{
                li.objObject = list._objTemp[i];
                li.sameContentSpots = this.findSpotByContent(li.objObject, 'model')
            })
        }, 
        
    
    })
    
    this.modelList.addNew = ()=>{//看是否在第一行增加新的  
        let firstOne = this.modelList.listDom.children().first();
        if(firstOne[0].objObject){
            this.modelList.addItem(getModelListItem())
        }
    }
    this.modelList.findChosenByContent = (objObject)=>{ 
        var list = Array.from(this.modelList.listDom.children())
        var item = list.find(e=>e.objObject && e.objObject.src == objObject.src) 
        return {index: item ? list.indexOf(item) : null,  item: item  }
    }
    
    
    
    this.photoList.updateDelBtns = this.videoList.updateDelBtns = (list_)=>{//每次开始编辑热点时更新删除按钮可见性 
        Array.from(list_.listDom.children()).forEach(li=>{
            //li.sameContentSpots = this.findSpotByContent(li._media, 'photo')
            if(li.sameContentSpots.find(e=>e!=this.editSpot)){
                $(li).addClass('cannotDelete')
            }else{
                $(li).removeClass('cannotDelete')
            }  
        }) 
    }
    
    
    
    
    
    //-----------------------------------
    
    
    
    
    
    uploadInputImage.on("change", (e)=>{
        inputMedia({
            enableTypes: ["photo"],
            photoDone: (resultImg)=>{
                if(editItemInfo.type == 'shine'){
                    editItemInfo.imgs[editItemInfo.index] = resultImg  //{file: resultImg.file, needSave:true, src:resultImg.src}//resultImg 
                    setImgContent(editItemInfo.$li,  editItemInfo.index, resultImg.src)

                    if (editItemInfo.index == 0){
                        editItemInfo.$li.find('li').eq(1).removeClass("hide");
                    }
                        
                    resultImg.needSave = true
                   
                    editItemInfo.$li[0].sameContentSpots.forEach(hot=>{
                        hot.changeShineTex(editItemInfo.$li[0]._styleImg)
                    })
                     
                    
                }else{//"photo"
                      
                    editItemInfo.$li.find(".upload").addClass("uploaded")
                    editItemInfo.$li.find(".upload div").css("background-image", `url(${resultImg.src})`)
                    
                    editItemInfo.$li[0]._media = resultImg
                    editItemInfo.$li[0].sameContentSpots.forEach(hot=>{
                        hot.changeTexType("photo",resultImg) 
                    })                        
                    resultImg.needSave = true 
                   
                }
                 
                
                /* if(editItemInfo.$li.hasClass('selected')){//是当前spot的 
                    this.texLists[editItemInfo.type].options.selectFun({dontApplyToHot:true})//更新选中状态
                } */
                  
                this.texLists[editItemInfo.type].addNew()
                this.texLists[editItemInfo.type].selectFromOutSide(editItemInfo.$li.index()) 
                this.texLists[editItemInfo.type].listDom.addClass('hide')     
            }
        }, "photo", e);
    
    })  
    
    uploadInputVideo.on("change", (e)=>{
        inputMedia({
            enableTypes: ["video"],
            videoDone: (file,video)=>{ 
             
                editItemInfo.$li.find(".upload").addClass("uploaded")
                editItemInfo.$li.find(".upload>div").text(file.name)
                editItemInfo.$li.attr("title", file.name) 
                video.file = file
                video.name = file.name
                video.needSave = true
                
                editItemInfo.$li[0]._media = video
                editItemInfo.$li[0].sameContentSpots.forEach(hot=>{
                    hot.info.texSrc = video.src      //视频不能共用一个，否则会一起播放暂停
                    hot.info.fileName = video.name
                    hot.videoWidth = video.videoWidth //记录。因为新new的话要等一会儿才不为0
                    hot.videoHeight = video.videoHeight
                    hot.changeTexType("video" ) 
                })                        
                 

                /* if(editItemInfo.$li.hasClass('selected')){//是当前spot的 
                    this.videoList.options.selectFun({dontApplyToHot:true})//更新选中状态
                } */ 
                this.videoList.addNew()
                this.videoList.selectFromOutSide(editItemInfo.$li.index())  
                
            }
        }, "video", e);
    
    })  
     
    uploadInputModel.on("change", (e)=>{
        inputMedia({
            enableTypes: ["model"],
            modelDone: (file,object)=>{ 
             
                editItemInfo.$li.find(".upload").addClass("uploaded")
                editItemInfo.$li.find(".upload>div").text(file.name)
                editItemInfo.$li.attr("title", file.name) 
                object.file = file
                object.name = file.name
                object.src = object.uuid;//作为标识而已
                object.needSave = true
                {//adjustModelAuto
                    let bound = new THREE.Box3
                    object.traverse( function ( child ) {  
                        if(child.geometry){//会不会有child自带了一层matrix呢？如果有要乘以matrix。 
                            child.geometry.computeBoundingBox();
                            bound.union(child.geometry.boundingBox) 
                        }                      
                    });
                    
                    let standard = 1; 
                    let size = bound.max.distanceTo(bound.min);
                    let s = standard / size
                    object.scale.set(s,s,s)
                    
                    let center = bound.center()
                    object.position.copy(center).negate().multiplyScalar(s)//保证模型的中心点和hot中心点重合
                    
                    object.modelBound = {
                        bound,
                        scaleRatio : s,
                        position: object.position.toArray()
                    }    
                }    
                
                
                editItemInfo.$li[0].objObject = object
                editItemInfo.$li[0].sameContentSpots.forEach(hot=>{ 
                    //hot.addModel(object) 
                    this.switchModel({add:object, spot:hot})
                })                        
                 
                this.modelList.selectFromOutSide(editItemInfo.$li.index())

                /* if(editItemInfo.$li.hasClass('selected')){//是当前spot的 
                    this.modelList.options.selectFun({dontApplyToHot:true})//更新选中状态
                }  */
                this.modelList.addNew()
                    
                
            }
        }, "model", e);
    
    })  
     
    this.texLists = {shine: this.styleList, photo:this.photoList, video: this.videoList}
    
    this.texTypeMenuOptions = new MenuOptions({
        dom: this.hotpointDetail.find(" li[name=style]  .MenuOptions"),
        uiCallBack : (o)=>{ 
            var name = o.name == void 0 ?  o.$li.attr('index') : o.name
            for(let i in this.texLists){ this.texLists[i].listDom.parent().addClass("hide")} 
            this.texLists[name].listDom.parent().removeClass("hide")
            
            if(name == 'photo'){
                $('#gifEdit').removeClass('hide')
            }else{ 
                $('#gifEdit').addClass('hide')
                this.updateGifPanel({on:false})
            }
            
        },
        callbackWhenChose:(o)=>{
            this.changeType( o.$li.attr('index'))
        }
    })
    
    
    //编辑开放
    this.hotpointDetail.find(" li[name=style]").removeClass('unable');
    this.hotpointDetail.find(" li[name=model]").removeClass('unable');
    this.hotpointDetail.find(" li[name=style] .itemTitle").attr('data-size','') 
    if(this.editSpot){//已经在编辑了，初始化下
        this.updateListSelect(this.editSpot)
        this.getTempInfos()  
    }
    
    this.hotpointDetail.find(" li[name=setPos] button[name=setSpace] ").on('click',(e)=>{
        var space = transformControls.space == 'world'?'local':'world'
        transformControls.setSpace(space)
        e.target.innerText = space == 'world'?'切换为世界坐标方向':'切换为本地坐标方向' 
    })      
    
    this.hotpointDetail.find(" li[name=setPos] button[name=useSuitableRatio] ").on('click',()=>{
        this.useSuitableRatio() 
    })      
    
    this.hotpointDetail.find(" li[name=setPos] button[name=resetRot] ").on('click',()=>{
        this.editSpot.rotation.x = 0
        this.editSpot.rotation.z = 0
    })      
    
}


Hotpoint.prototype.switchModel = function(o={}){
    if(o.add){
        
        if(!$("#remainModelScale")[0].checked || o.spot.plane){//恢复成等大比例，方便用户看清原始比例,否则可能拉伸严重. 但有时换obj后不想重新设置大小可以勾选保持大小。 
            var s = 1;                          //从热点转过来一定恢复比例
            o.spot.scale.set(s,s,s)
            o.spot.info.scale.set(s,s,s)
        }
        
        
        var model = o.add.clone();
        o.spot.info.objSrc = o.add.src
        o.spot.info.objName = o.add.name  
        o.spot.info.modelBound = o.add.modelBound;
        o.spot.addModel(model)
          
        if(o.spot == this.editSpot){
            transformControls.enableScaleZ()
        }  
    }else if(o.remove){
        //如果没有出现plane，很可能是因为背面朝向镜头了！
        o.spot.addPlane()
        if(o.spot == this.editSpot)  transformControls.unableScaleZ()        
    }
    
}


Hotpoint.prototype.changeType = function(type){
    var hot = this.editSpot
    var list  
    
    this._scale[hot.texType] = hot.scale.clone()
    
    if(type == 'shine'){ 
        hot.changeTexType(type, 'clear')
        if(hot.styleImg){
            hot.changeTexType(type, hot.styleImg )
        }
        list = this.styleList 
        
        list.selectFromOutSide(list.chosenIndex || 1)//触发选择函数
         
        
    }else{
        hot.changeTexType(type,'clear')
        if(type == 'photo'){
            list = this.photoList
        }else{
            list = this.videoList
        }
      
        list.selectFromOutSide(list.chosenIndex)
        
        
    }
    this.useSuitableRatio({setScaleAuto:true})  
    hot.info.actionType = CloneObject(settings.hotClickEvent[type]);//给一个默认 图和视频不打开热点
    /* if(type == 'video'){
        hot.info.actionType.playAndPause = true;
    }else{
        delete hot.info.actionType.playAndPause
    } */
    if(type != 'video'){
        delete hot.info.fileName
    }
    
    this.updateClickEventUI();//根据actionType更新ui
    
}

 

Hotpoint.prototype.updateGifPanel = function(o={}){
    var hot = this.editSpot
    
    if(!hot.material_.map)return;
    let a = o.animateInfo || hot.info.animateInfo
    //换图时根据图片的animation更改开关，如果有的话还要更改面板，如果图没有animation用自己的
    
    if(o.animateInfo){//更换animateInfo
        hot.info.animateInfo = CloneObject(o.animateInfo);
           
    }
   
    if("animateInfo" in o ){
        o.on = !!o.animateInfo
    } 
     
    if(!a){
        o.on = false
    }
    
     
    if(o.on != void 0){
        
        this.gifCheckBox.updateChoseAtUI(o.on)
        
        
        GifTexDeal.remove(hot.animation)
        if(o.on){
            hot.animation = GifTexDeal.addAnimation(hot.material_.map, hot, hot.info.animateInfo, hot.sid)
            GifTexDeal.start(hot.animation)
        }else{
            hot.animation = null
            hot.info.animateInfo = null
        }
        
    }
    
    
    
 
    let w = hot.material_.map.image.width
    let h = hot.material_.map.image.height
    
    if(a){ 
        $("#gifXCount").val(a.cellXcount);
        $("#gifYCount").val(a.cellYcount);
        $("#gifImgWidth").text(w)
        $("#gifImgHeight").text(h)
        $("#gifCellWidth").text(toPrecision(w / a.cellXcount, 2));
        $("#gifCellHeight").text(toPrecision(h / a.cellYcount, 2));
        $("#voidCount").val(a.voidCount || 0); 
        let frameCount = a.cellXcount * a.cellYcount - (a.voidCount || 0)
        $("#gifFrameCount").text(frameCount) 
        $("#gifDuration").val(toPrecision(a.duration / 1000, 2));
        $("#gifFps").val(toPrecision(frameCount / a.duration * 1000, 1) );  
    }else{
        $("#gifXCount").val(1);
        $("#gifYCount").val(1);
        $("#gifImgWidth").text(w)
        $("#gifImgHeight").text(h) 
        $("#gifCellWidth").text(w);
        $("#gifCellHeight").text(h) 
        $("#voidCount").val(0);
        $("#gifFrameCount").text(1) 
        $("#gifDuration").val(1);
        $("#gifFps").val(1);  
        
    }
    
}


Hotpoint.prototype.updateListSelect = function(hot) { //每次开始编辑热点时更新样式
 
        
    if(!Hot.allPhotoLoaded || !Hot.allModelLoaded)return
    
    
    
    var list = this.texLists[hot.texType];
    var chosenIndex = null;
    this.texTypeMenuOptions.updateChoseAtUI({name:hot.texType}) 
    
    
    for(let i in this.texLists){
        //this.texLists[i].clearVoid(this.texLists[i]); 
        this.texLists[i].saveTemp(this.texLists[i])
    }
    this.modelList.saveTemp()
    if(hot.texType == 'shine'){
        if (!hot.styleImg) hot.styleImg = [];
         
        if(hot.styleImg.length){ 
            var info = this.styleList.findChosenByContent(hot.styleImg) 
            chosenIndex = info.index
        }  
    }else{
        var info = list.findChosenByContent(list, hot.texMedia)
        chosenIndex = info.index
    }
    
    
    list.selectFromOutSide(chosenIndex,  {dontApplyToHot:true})
    
  
    this.photoList.updateDelBtns(this.photoList)
    this.videoList.updateDelBtns(this.videoList)
    
    
    
    {//model
        let hasModel = !!hot.objObject
        this.modelMenuOptions.updateChoseAtUI({name: hasModel ? 'user' : 'default'})  
        
        if(hasModel){
            var info = this.modelList.findChosenByContent(hot.objObject) 
            this.modelList.selectFromOutSide(info.index,  {dontApplyToHot:true})
            transformControls.enableScaleZ()
        }
    }
    
    
}


Hotpoint.prototype.useSuitableRatio = function(o={}) {//自适应素材比例
    var hot = this.editSpot
    var plane = this.editSpot.plane;
    var w, h, defaultS = 1, s, mediaW, mediaH
    
    if(this._scale[hot.texType] && o.setScaleAuto){
        w = this._scale[hot.texType].x;  h = this._scale[hot.texType].y; 
    }else{
        if(hot.texType == 'shine'){//默认使用热点大小，正方形
             
            if(o.setScaleAuto){
                w = h = 1 //DATA.hotIconScale * g_HotMeshSize.g_HotMeshWidth  
            }else{
                s = hot.scale.x * hot.scale.y
                w = h = Math.sqrt(Math.abs(s))
            }            
        }else{
            if(!hot.texMedia)return
            //假设不变总面积
            if(o.setScaleAuto){ 
                s = defaultS
            }else{
                s = hot.scale.x * hot.scale.y
            }
            /* if(hot.texType == 'photo'){
                mediaW = hot.texMedia.width;
                mediaH = hot.texMedia.height;
                //动画的话再变
                
                
                if(hot.info.animateInfo){
                    mediaW /= hot.info.animateInfo.cellXcount
                    mediaH /= hot.info.animateInfo.cellYcount
                }
                
                
            }else{
                mediaW = hot.texMedia.videoWidth || hot.videoWidth || 1;
                mediaH = hot.texMedia.videoHeight || hot.videoHeight || 1;
            } */
            let size = hot.getMediaSize()
           
            if(size.x == 0){
                mediaW = mediaH = 1
            }else{
                mediaW = size.x; mediaH = size.y; 
            }
            
            
            var k = Math.sqrt(Math.abs(s) / (mediaW * mediaH))    
            w = k * mediaW * (hot.scale.x < 0 ? -1 : 1);
            h = k * mediaH * (hot.scale.y < 0 ? -1 : 1);
        }
    }
    
    
    
     

    hot.scale.setX(w)
    hot.scale.setY(h)
    if(hot.isUserModel){
        hot.scale.setZ(z)
    }
    this.updateTransform('scale')
    
    
     
},


Hotpoint.prototype.updateClickEventUI = function() {//根据actionType更新ui
    var hot = this.editSpot;  
    /* var checkboxs = this.hotpointDetail.find("li[name='clickEvent'] li.editCheckbox");
    checkboxs.addClass('hide');
    
    for(let i in hot.info.actionType){
        var checkbox = checkboxs.find('[name='+ i +']')
        checkbox.parent().removeClass('hide');
        checkbox[0].checked = !!hot.info.actionType[i]
    } 
    
    var content =  this.hotpointDetail.find("li[name='content']") 
    hot.info.actionType.openHot  ? content.removeClass('hide') : content.addClass('hide') */
    
    /* for(let i in hot.info.actionType){
        this.actionCheckBox.updateChoseAtUI(hot.info.actionType[i], i)
        
    } */
    
    settings.hotClickActions.forEach(e=>{
        this.actionCheckBox.updateChoseAtUI(hot.info.actionType[e], e)
    })
    
    
}



//添加图片和视频
Hotpoint.prototype.addmediaInit = function() {
    // 视频预览控制
    var $videoLayout = $('.fun-view-video')
    var that = this
    $videoLayout.find('span').on('click', function() {
        $videoLayout.hide();
        $videoLayout.find('video').attr('src', '')[0].pause()
    });

    // 图片加载回调
    function imageSuccess(file) {
        if (!/image\/\w+/.test(file.type)) {
            alert("文件必须为图片！");
            return false;
        }
        if (!restrictedSize(file, 9)) {
            return false;
        }

        var img = new Image()
        img.src = URL.createObjectURL(file);
        img.className = 'bg'
        return img
    }

    // 视频加载回调
    function videoSuccess(file, $elayout) {
        if (!/video\/\w+/.test(file.type)) {
            alert("视频必须为map4格式！");
            return false;
        }

        $elayout.append('<span class="upload-thum">上传封面<i><input type="file" accept="image/*"></i></span>')

        return '<img src="./images/play.png" class="play-video" videoURL="' + URL.createObjectURL(file) + '">'
    }

    // 视频封面回调
    function thumbSuccess(file, $elayout, $seft) {
        if (!/image\/\w+/.test(file.type)) {
            alert("文件必须为图片！");
            return false;
        }
        if (!restrictedSize(file, 2)) {
            return false;
        }
        
        var img = $seft.closest('div').find('img.bg');
        if(!img[0]){
            img = $("<img class='bg'></img>");
            $seft.closest('div').find('a').append(img)
        }
        img.attr('src', URL.createObjectURL(file));
        $seft.closest('span').removeClass("success")
         
        
    }


     

    // 图片与视频加载
    function fileHandle(that) {
        var $seft = $(this) //input
        
         
        var file = this.files[0];
        var type = this.activeElem ? this.activeElem.attr('data-type') : $seft.parent().attr('data-type');
        
        var $din = $('<div class="mediaItem" draggable="true"></div>')  
        var fnc = type === 'photo' ? imageSuccess : type === 'video' ? videoSuccess : thumbSuccess //再次传都会是thumSuccess
        var $dom = fnc(file, $din, $seft)

        if (type === 'photo' || type === 'video') {
            if (!$dom)
                return $seft.val('');
            
            if(!this.activeElem){//创建新的item
                var $layout = $seft.closest("li"); 
                var $addLayout = $(document.createElement('a'));
                $addLayout.attr("data-type", type)
                 
                var $close = $(document.createElement('span'));
                 
                $addLayout[0].file = file;
                
                $addLayout.append($close);
                $addLayout.append($dom);
                $addLayout.addClass("result");
                $din.prepend($addLayout);
                $layout.find('.edit-fun-images').append($din);
                
                 
                var $btn = $('<div class="descBtn">添加描述</div>')
                $addLayout.append($btn)
                that.descBtnBind($btn[0], type)
                 
                    
                
                
                
            }else{//重传
                if (type === 'photo'){
                    this.activeElem.find("img.bg").remove();
                }else{
                    this.activeElem.find(".play-video").remove();
                }
                this.activeElem.removeClass("success")  
                this.activeElem.append($dom)
                this.activeElem[0].file = file;
                this.value = ''
            }  
        }

        if (searchParent($seft[0], {
            id: 'shareImgUpload'
        })) {
            //信息页面的分享图片
            $("#shareImgUpload").addClass("hide")
        }else if(searchParent($seft[0], {
            id: 'SpotStyle'
        })){
            $("#SpotStyle").addClass("hide")
        }
        
        
    }
    
    
    
    
    
    //用于重传的input
    var imgInput = $("<input accept='image/*'  type='file'></input>")
    var videoInput = $("<input accept='video/*' type='file'></input>")
    
    
    
    // 图片与视频加载
    $('.upload, .edit-fun-images').on('change', function(ev) {
        if (ev.target.tagName.toUpperCase() === 'INPUT') {
            ev.target.files.length && fileHandle.call(ev.target, that)
        } 
    })
    imgInput.on('change', (ev)=>{fileHandle.call(ev.target, that)})
    videoInput.on('change', (ev)=>{fileHandle.call(ev.target, that)})
    
    
    
    
    $('.edit-fun-images').on('click', function(ev) {//点击item时
        var $tag = $(ev.target) 
        var tagName = ev.target.tagName.toUpperCase()
       
        if (tagName === 'SPAN') { 
            if (searchParent($tag[0], {
                className: 'forShareImg'
            })) {
                //信息页面的分享图片
                $("#shareImgUpload").removeClass('hide')
            }else if(searchParent($tag[0], {
                    className: 'SpotStyle'
                })){
                $("#SpotStyle").removeClass('hide')
            }
            $tag.parent().parent().remove()
        }else if (tagName === 'IMG' && $tag.hasClass('play-video')) {
            $videoLayout.css('display', 'flex').find('video').attr('src', $tag.attr('videoURL'))[0].play()
        }else{// 替换
            if($tag.hasClass('descBtn'))return
            var a = searchParent($tag[0], {
                tagName: 'a'
            })
            if(a) {
                var input  
                if(searchParent($tag[0], {
                    className: 'video'
                })){
                    input = videoInput 
                }else{
                    input = imgInput
                    
                }
                input[0].activeElem = $(a)
                input.click()
            }  
        }
    })
    
    
}


Hotpoint.prototype.descBtnUpdate = function(btn){//图片描述按钮状态更新
    var $btn = $(btn)
    /* if(index == void 0){ 
        var btns = Array.from(this.hotpointDetail.find("[name=photo] .list .descBtn")) 
        index = btns.indexOf(btn)
    }  */
    var desc = btn.desc
    if(desc == void 0 || desc === ''){
        $btn.removeClass('hasDesc')
        $btn.text('添加描述')
    }else{
        $btn.addClass('hasDesc')
        $btn.text('查看描述')
    }  
}


Hotpoint.prototype.descBtnBind = function(btn, type, desc  ){//单个图片or视频描述按钮事件
    //console.log('descBtnBind',btn)
    var textarea = this.hotpointDetail.find(`li[name="${type}"] textarea`);
    var title = this.hotpointDetail.find(`li[name="${type}"] [name="descTitle"]`)
    btn.addEventListener('click',(e)=>{
        this.hotpointDetail.find(`[name=${type}] .list .descBtn`).removeClass('chosen')
        $(btn).addClass('chosen'); 
        textarea.removeClass('hide')
        title.removeClass('hide')
        /* textarea.val(btn.desc)
        textarea[0].currentBtn = btn  */
        this.descBtnChose(btn, type)
        e.preventDefault()
        e.stopPropagation()
    }) 
    btn.desc = desc || '' 
}

Hotpoint.prototype.descBtnChose = function(btn, type){
    var textarea = this.hotpointDetail.find(`li[name="${type}"] textarea`);
    textarea[0].currentBtn = btn
    textarea.val(btn ? btn.desc : ''); 
}


 
//当前处于的状态，比如是否是添加热点的状态
/* Hotpoint.prototype.state = function(that, Hot) {
    if (g_HotStatus === "add") {
        //console.log(Hot)
        this.addHot(that, Hot, (hot)=>{
            this.addHotList(hot);
        } );
    }
} */



Hotpoint.prototype.updateNumDisplay = function() {
    this.spotList.find("i").text(this.spotList.find("ul li").length);
}

//删除热点
Hotpoint.prototype.removeHot = function(hot ) { 
    //hot.dispose();
    hot.dispose() 
      
    hot.$li.remove();
    this.updateNumDisplay() 
}
//添加热点模型
Hotpoint.prototype.addHot = function(that) {
    if (!player.intersect || !g_HotStatus) return;// 没有正确的热点位置, return
     
    var sid = getRandomSid()
    var s = g_HotStatus == 'shine' ? Hot.getDefaulScale() :  1 
    var hot = new Hot({
        sid, 
        texType : g_HotStatus,
        position : player.intersect.point.clone() ,
        rotation : [0,0,0],
        scale: new THREE.Vector3(s,s,0.02),
        actionType : CloneObject(settings.hotClickEvent[g_HotStatus]),
        
        titlePos: this.latestTitlePos,
        titleShowType:  this.latestTitleShowType,
        
    });
    hot.photoHasRequestLoad = hot.modelHasRequestLoad = true;//防止requestDownload
    player.model.hots[sid] = hot;
    hot.hasRequestLoad = true
    hot.isNew = true
    
    if (player.getMouseDirection().angleTo(player.intersect.face.normal) < Math.PI / 2) {
        hot.lookAt(player.intersect.face.normal.clone().negate().add(player.intersect.point));
        hot.position.add(player.intersect.face.normal.clone().negate().multiplyScalar(0.01)) //avoid mesh crash with chunks 模型的精度可能和floorplan的不一样，所以chunk即使材质经过处理还是会闪烁但是wallmesh不会
    } else {
        hot.lookAt(player.intersect.face.normal.clone().add(player.intersect.point));
        hot.position.add(player.intersect.face.normal.clone().multiplyScalar(0.01))
    }
    
    
    
    g_HotStatus = false;
    player.mouseCouldBeClickToMove = false;
    //$("#player").css("cursor", "");
    CursorDeal.remove('addHot' )   
    CursorDeal.remove('noIntersect' )   
     
    this.addHotList(hot)
    this.editHot(hot)
    
    this.updateTransform('position');
    this.updateTransform('quaternion');
    
}


//添加热点列表
Hotpoint.prototype.addHotList = function(hot) {
    var li = $("<li class='listItem' draggable='true' data-spid=" + hot.sid + "><div class='icon'></div>" + /* "<div class=number>" + (++_hotNum) + "</div>" + */ "<div class=title >" + ( hot.info.title  ) + "</div>" + "<div class=DelConfirm title='删除'>确定删除</div>" + "<div class=del></div>" + "</li>");
    li.hot = hot
    hot.$li = li
    this.setListLi(hot)
    
    $(".spotList ul").append(li);
    li.on("mouseover",()=>{
       hot.changeBoxHelperDisplay(true)
    })
    li.on("mouseout",()=>{
       hot.changeBoxHelperDisplay(false)
    })
    
    this.updateNumDisplay()
}
Hotpoint.prototype.setListLi = function(hot){ //暂时用文字展示，最好有图标来表示包含gif和模型
    var name = '';
    if(hot.info.title) name = hot.info.title
    else{
        if(hot.info.objSrc)name = '包含模型的'
        if(hot.texType == 'photo' && hot.info.animateInfo)name+= 'gif'
        else name += (hot.texType == 'shine' ? '热点' : hot.texType == 'photo' ? '图片' : '视频' )
         
        
    }    
     
    hot.$li.find(".title").html(name);
    hot.$li.find('.icon').attr("type",hot.texType)
    hot.$li.attr("title", hot.info.title) //显示没显示完的标题
}
 
//添加热点模型列表dom
Hotpoint.prototype.inputList = function(text, val) {
    var _val = '';
    if (arguments[1]) {
        _val = 'value=' + val;
    }
    return '<div class="link"> <input class="text" type="text" placeholder=' + text + '  spellcheck="false" ' + _val + ' > </div>'
}
//添加热点模型
Hotpoint.prototype.addModel = function() {
    var text = this.inputList("请填写模型链接（https开头）");
    this.hotpointDetail.find("[name=modelSrc] .add").on('click', function() {
        $(this).closest("[name=modelSrc] ").find(".list").append(text)
    });
    this.hotpointDetail.find("[name=modelSrc]  .delete").on('click', function() {
        $(this).closest("[name=modelSrc] ").find(".list > div:last").remove();
    });
}
//添加网页链接
Hotpoint.prototype.addwebPack = function() {
    var text = this.inputList("请填写网页链接", "");
    this.hotpointDetail.find("[name=webPage] .add").on('click', function() {
        $(this).closest("[name=webPage]").find(".list").append(text)
    });
    this.hotpointDetail.find("[name=webPage] .delete").on('click', function() {
        $(this).closest("[name=webPage]").find(".list > div:last").remove();
    });
}

Hotpoint.prototype.editDone = function(){
    var hot = this.editSpot 
    this.editSpot = null; 
    hot.isNew = false; 
    transformControls.detach()
    this.hotpointDetail.addClass("atRight");
    $('#hotpointDetail .shotImg.innerBtn')[0].cameraData = null
    
    this.setCameraBtn(false)
    this.imgDescCheckBox.updateChoseAtUI(false)
    this.videoDescCheckBox.updateChoseAtUI(false)
     
    $('#setPosForPano [name="dis"]').addClass('hide');
 
    
    
}


// 保存热点信息
Hotpoint.prototype.saveHot = async function() {
    var $layout = $(".edit-loading");
    var hotpointDetail = this.hotpointDetail;
   
    var that = this
 
    var hot = this.editSpot
    
     
    if (hot.texType == "video" && !hot.texMedia ) {
        alert("请添加视频") 
        return;
    }else if(hot.texType == 'photo' && !hot.texMedia){
        alert("请添加图片") 
        return;
    }
    
    
     
    
    //显示loading
    $layout.removeClass('hide');
    
    var args = hot.info//{};
    //热点标题
    var $title = hotpointDetail.find("[name=info] input");
    //热点内容
    var $content = hotpointDetail.find("[name=intro]  textarea");
    //热点图片
    var $images = hotpointDetail.find("[name=content] [name=photo] .edit-fun-images a.result");
    //热点视频
    var $videos = hotpointDetail.find("[name=content] [name=video] .edit-fun-images a.result");
    //热点视频的图片
    var $thums = hotpointDetail.find("[name=content] [name=video] .edit-fun-images .upload-thum");
    //热点模型
    var $modules = hotpointDetail.find("[name=content] [name=modelSrc]  input");
    //热点内嵌网页
    var $iframes = hotpointDetail.find("[name=content] [name=webPage] input.text");
    //热点音乐
    var $miusc = hotpointDetail.find("[name=content] #upload-hotBgm");

    
    var promise = new Promise(function(resolve, reject) {
        //获取图片路径
        upload($images, 'images', resolve)
    }).then(function(imgUrls) {
        $layout.removeClass('hide');
        args.images = imgUrls
        //获取视频路径
        return new Promise(function(resolve, reject) {
            upload($videos, 'videos', resolve)
        } )
    }).then(function(videoUrls) {
        $layout.removeClass('hide');
        args.video = videoUrls
        //获取视频封面路径
        return new Promise(function(resolve, reject) {
            upload($thums, 'images', resolve)
        }
        )
    }).then(function(thums) {
        $layout.removeClass('hide');
        args.video = args.video.map(function(video, index) {//整合视频+封面
            return {
                url: video,
                img: thums[index]
            }
        })
    }).then(function() {
        //获取热点音乐所有的路径
        //console.log($miusc)
        $layout.removeClass('hide');
        if ($miusc[0].files.length > 0) {
            return new Promise(function(resolve) {
                upload($miusc, 'miusc', function(res) {
                    resolve(res[0])
                })
            })
        } else {
            return $miusc.attr('data-hotBgm')
        }
    }).then(function(src) { 
        if(that.musicBox.hasMusic && !src){
            args.backgroundMusic = hot.info.backgroundMusic
            args.bgName = hot.info.bgName
        }else{
            args.backgroundMusic = src
            args.bgName = that.musicBox.getName()
        }
        
    }).then(function() { //上传所有热点的tex file
        var a = $.Deferred();
        var doneNum = 0; 
        var filesNeedSave = [];
        var liNeedSave = [], liNeedSave2 = []
        $layout.removeClass('hide');
        
        Array.from(that.styleList.listDom.children()).forEach(li=>{
            if(li.sameContentSpots.length == 0)return;//没被热点用到就不保存
            //li.sameContentSpots.forEach(spot=>spot.styleImg = li._styleImg)//提前同步下 等下就都是线上链接了 
            
            li._styleImg.forEach(img=>{
                if(img.needSave && !filesNeedSave.includes(img)){
                    filesNeedSave.push(img)
                    
                    liNeedSave.includes(li) || liNeedSave.push(li)
                    
                } 
            })
        });
        
        [that.photoList, that.videoList].forEach(list=>{
            Array.from(list.listDom.children()).forEach(li=>{
                if(li.sameContentSpots.length == 0)return;//没被热点用到就不保存
                //li.sameContentSpots.forEach(spot=>spot.texMedia.src = li._media.src)//同步下 video不能同步，只能同步src
                
                li._media.needSave && (filesNeedSave.push(li._media), liNeedSave.push(li))  
                
            })
        })
        
        Array.from(that.modelList.listDom.children()).forEach(li=>{
            if(li.sameContentSpots.length == 0)return;//没被热点用到就不保存
            
            li.objObject.needSave && (filesNeedSave.push(li.objObject), liNeedSave2.push(li))  
        })
       
        
        
        if(filesNeedSave.length){
            filesNeedSave.forEach(o=>{
                uploadFile(o.file, 'hot', function(rs) {
                    if (rs.code === 0) {
                        o.needSave = false;
                        //o.saveURL = rs.data;
                        o.src = rs.data;  
                        if (++doneNum >= filesNeedSave.length) {
                            a.resolve();
                            
                            liNeedSave.forEach(li=>{//将blob更换成线上链接
                                li.sameContentSpots.forEach((spot)=>{
                                    if(spot.texType == 'shine'){ 
                                        spot.info.styleImg = li._styleImg.map(img=>img.src)
                                    }else{ 
                                        spot.info.texSrc = li._media.src 
                                        spot.texMedia.src = li._media.src  
                                    }
                                }) 
                            }) 
                            liNeedSave2.forEach(li=>{//将blob更换成线上链接
                                li.sameContentSpots.forEach((spot)=>{
                                    spot.info.objSrc = spot.objObject.src = li.objObject.src
                                }) 
                            }) 
                        }
                    } else {
                        alert("文件上传失败");
                        a.reject();
                    }
                })
            }) 
            return a.promise()
        }else{
            return a.resolve()
        }
        
         
    }).then(function() {
        $layout.removeClass('hide');
        //获取热点标题、内容、内嵌网页、模型网页。
        args.title = $title.val()
        args.content = $content.val()

        var iframes = []
        for (var i = 0; i < $iframes.length; i++) {
            var iframe = $iframes.eq(i).val()
            iframe && iframes.push(iframe)
        }
        args.iframe = iframes

        var modules = []
        for (var i = 0; i < $modules.length; i++) {
            var module = $modules.eq(i).val()
            module && modules.push(module)
        }
        args.model = modules
        
        /* if(!hot.isSprite){ 
            args.quaternion = hot.quaternion.clone() 
            //hot.quaternion.copy(hot.mesh.quaternion) 
        }
        args.position = hot.position.clone()
        //hot.position.copy(hot.mesh.position) */
        
          
        hot.info.texType = hot.texType
        
        if(that.ifNeedResetVisi(hot)){
            hot.getVisiblePanos();  
        }
        if(hot.texType == 'photo'){//保存时绑定animateInfo到li
            let info = that.photoList.findChosenByContent(that.photoList, hot.texMedia);
            info.item.animateInfo = CloneObject(hot.info.animateInfo)
        }
        
        if(that.actionCheckBox.checked('fastTran')){
            hot.info.cameraData = $('#hotpointDetail .shotImg.innerBtn')[0].cameraData  //快速跳转方位
        }else{
            delete hot.info.cameraData 
        }
        
        hot.info.imagesDesc = Array.from(that.hotpointDetail.find("[name=photo] .list .descBtn")).map(btn=>btn.desc)
        hot.info.videosDesc = Array.from(that.hotpointDetail.find("[name=video] .list .descBtn")).map(btn=>btn.desc)
        
        
        
        //hot.setTitleElem()//更新名称
        
        that.setListLi(hot);
         
        $layout.addClass('hide'); 
         
        console.log(args);
        
        that.editDone()
        
        that._scale[hot.texType] = hot.scale.clone()
        
        return new Promise(function(resolve, reject) {
            resolve() 
        })
    })

    return promise
}



Hotpoint.prototype.ifNeedResetVisi = function(hot){//修改过贴图或位置或模型，导致这几个数值改变，就要重新计算可见性（会覆盖用户设置）
    var need = !ifSame({
        hasBox: hot.tempInfo.hasBox,
        position: hot.tempInfo.position,
        quaternion: hot.tempInfo.quaternion,
        scale: hot.tempInfo.scale, 
        transformAtPanos: hot.tempInfo.transformAtPanos, 
        modelBound:hot.tempInfo.modelBound
    }, {
        hasBox: hot.info.hasBox,
        position: hot.info.position,
        quaternion: hot.info.quaternion,
        scale: hot.info.scale,
        transformAtPanos: hot.info.transformAtPanos,
        modelBound:hot.info.modelBound
    })  
    
    return need 
}


Hotpoint.prototype.getSavingInfo = function(){//保存全部
    var hots = {} // 热点数据
    
    /* var shineHots = player.model.hotGroup.children.filter(e=>e.texType == 'shine')
    var photoHots = player.model.hotGroup.children.filter(e=>e.texType == 'photo')
    var videoHots = player.model.hotGroup.children.filter(e=>e.texType == 'video')

    
    shineHots.concat(photoHots).concat(videoHots).forEach((hot,index)=>{     */ 
    //按列表顺序存储
    Array.from(this.spotList.find('li.listItem')).forEach((li,index)=>{
        var sid = $(li).attr("data-spid");
        var hot = player.model.hots[sid]    
        var hotData = CloneObject(hot.info) 
        if(hotData.texSrc){
            hotData.texSrc = manage.removeSrcPostMark(hotData.texSrc)
        }
        
        
        hotData.position = toPrecision(hot.info.position.toArray(), 3) 
        hotData.scale = toPrecision(hot.info.scale.toArray(), 3) 
        hotData.rotation = toPrecision(new THREE.Euler().setFromQuaternion(hot.info.quaternion).toArray().slice(0,3), 4) 
        hotData.order = index
        
        hotData.transformAtPanos = {}
        for(let i in hot.info.transformAtPanos){ 
            hotData.transformAtPanos[i] = {
                pos : toPrecision(hot.info.transformAtPanos[i].pos.toArray(), 4), 
            }
            if(hot.info.transformAtPanos[i].qua){
                hotData.transformAtPanos[i].qua = toPrecision(hot.info.transformAtPanos[i].qua.toArray(), 5)  
            } 
            if(hot.info.transformAtPanos[i].scale){
                hotData.transformAtPanos[i].scale = toPrecision(hot.info.transformAtPanos[i].scale.toArray(), 4)  
            }
        } 
        hotData.version = 'multi'
        
        if(hotData.animateInfo && hotData.animateInfo.cellXcount * hotData.animateInfo.cellYcount - (hotData.animateInfo.voidCount || 0)<=1){
            delete hotData.animateInfo
        }
        
        
       
        hotData.model = hotData.model.length ?  hotData.model : ''  
        hotData.images = hotData.images.length ?  hotData.images : ''
        hotData.video = hotData.video.length ?  hotData.video : ''
        hotData.iframe = hotData.iframe.length ?  hotData.iframe : ''
        
         
        
        hots[hot.sid] = hotData
    })

    return hots
}









//背景音乐
var EditBGM = function() {
    this.mediaUpload = $(".music  .mediaUpload");
    
    this.musicBox = musicPlayBoxBind(this.mediaUpload, (file)=>{
        uploadMusic(file, (rs, file)=>{
            if (rs.code === 0) { 
                this.musicBox.show(/* this.mediaUpload,  */file.name, rs.data) 
            }else{
                this.musicBox.hide()
            }
        })
    }, null)
   
}
EditBGM.prototype.init = function(data) {
    if (!data.backgroundMusic)
        return;
    this.musicBox.show(/* this.mediaUpload, */ data.bgName || "backgound", data.backgroundMusic);
    
}











//导览编辑
var EditGuide = function() {
    this.$list = $('#guide-list')
    //$('#tourItemEdit') = $('#tourItemEdit'); 
    this.targetTourPoint = null;
    // fyz 记录当前编辑的导览点
    // fyz 导览音乐队列
    //this.tourAudio = {};
}

/**
 * @author fyz 2019.07.31
 * @description 新增参数data2, 表示data2.js中的数据
 */
 
EditGuide.prototype.snapATourView = function(renew, insideFolder){
    var ev = document.createEvent("MouseEvent"); 
    ev.initMouseEvent("snapshotBegin", true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    g_snapShotWidth = 200;
    g_snapShotHeight = 140;
    //window.screenSta = 'tour';
    // 记录当前哪个功能截屏, 因为导览截屏需要做额外的校对调整, 初始画面截屏不需要
    ev.__callback = (imgData, info)=> {
        this.uploadGuide(imgData, info, renew,  insideFolder)
    };
    window.dispatchEvent(ev);
}


EditGuide.prototype.init = function(data, data2) {
    
    
    
    this.createDom(data, data2);
     
    //this.tourAudio = data2.tourAudio || {};
    
    
    var that = this;
 
    
    //点击添加导览
    $('.addTour .innerBtn').eq(0).on("click", function () {
        this.addFolderDom({name:"区域"+($("#tourList .listItem").length+1)  } );//点击增加一段导览
        $("#tourList ul")[0].scrollTop  = $("#tourList ul")[0].scrollHeight  //scroll to bottom
    }.bind(this));

   
    $('.addTour .innerBtn').eq(1).on("click", function () { 
        this.snapATourView()//点击截取视图
        $("#tourList ul")[0].scrollTop  = $("#tourList ul")[0].scrollHeight
    }.bind(this));
     
    
    $('.snapATourView .innerBtn').on("click", function() { 
        this.snapATourView(null,true);//folder内点击截取视图 
        $("#tourItemList ul")[0].scrollTop  = $("#tourItemList ul")[0].scrollHeight
    }.bind(this));
     
    $('#renewTourBtn').on("click", function() { // 重新截取视图 
        this.snapATourView(this, !!this.editingFolderLi);  
    }.bind(this));
    
     
    // 确认并保存导览信息
       
    $('#tourFolderEdit .tail .submit').on("click", function() {  
        this.completeFolder();
    }.bind(this));
    $('#tourItemEdit .tail .submit').on("click", function() { 
        this.completeItem();
    }.bind(this));
    
    
     
    
     //点击导览编辑窗口右上角的关闭按钮
    $("#tourItemEdit a.close, #tourItemEdit .tail .cancel").on("click", ()=>{
        /* that.targetTourPoint[0].musicSta = true;  ///??????????
        // 取消操作, 重置状态
        that.tourDetail.addClass("atRight"); */ 
        this.cancelItem()
    }); 
    $("#tourFolderEdit a.close, #tourFolderEdit .tail .cancel").on("click", ()=>{
        /* that.targetTourPoint[0].musicSta = true;  ///??????????
        // 取消操作, 重置状态
        that.tourDetail.addClass("atRight"); */ 
        this.cancelFolder()
    }); 
    

    
    //导览目录
    $("#tourList").on("click", function(e) {
        var target = $(e.target); 
        let folder = searchParent(e.target, { className: 'listItem' }, 7);
        if(!folder)return;//可能点到了item
        if (target.hasClass("del")) {
            e.stopPropagation();
            target.siblings(".DelConfirm").addClass("active"); 
        } else {
            if (target.hasClass("DelConfirm")) {
                e.stopPropagation();
                $(folder).remove()
                //that.removeFolder(folder);//删除
            } else { 
                if (target.hasClass("title") || target.hasClass("icon")) {
                    that.editFolder(folder)//编辑
                }
            }
        }
    });
    
    
    
    //编辑item
    $('.tourList ul').on('click', function(e) {
        var target = $(e.target);
        let itemDom = searchParent(e.target, { className: 'guideItem' }, 7);
        if(!itemDom)return;//可能点到了folder
        if(searchParent(e.target, { className: 'preview' })){
            var choice = confirm("你确定删除吗？"); 
            if (choice) { 
                var ul = searchParent(e.target, { className: 'tourList' })
                
                target.closest('li').remove();
                
                reIndexTourList(ul)
            } else {
                return false
            }
        }else{
            that.editItem(itemDom);
        }  
    }) 
    
  

    
     
     
    
    
    //音乐
    
    this.musicBoxFolder = musicPlayBoxBind($("#tourFolderEdit  .mediaUpload"), null, ()=>{
        (that.editingFolderLi || that.editingItemLi).tourData.musicInfo = {}//直接删除 不可逆
    })
    this.musicBoxItem = musicPlayBoxBind($("#tourItemEdit  .mediaUpload"), null, ()=>{
        (that.editingFolderLi || that.editingItemLi).tourData.musicInfo = {}//直接删除 不可逆
    })
     
    this.momentFolderMenuOptions = new MenuOptions({ //链接打开方式切换 
        dom: $(" #folderTourSwitch ") 
    })
    this.momentMenuOptions = new MenuOptions({ //链接打开方式切换 
        dom: $(" #tourSwitch_Slice ") 
    })
    
    
    
    this.rotCheckBox = new CheckBox({dom: $('#tourItemEdit [name="rotSwitch"] input') ,
        uiCallBack : (checked )=>{ 
               
        },
        callbackWhenChose:(checked )=>{  
            this.editingItemLi.tourData.dontRot = checked ? 0 : 1
        }
    })
    {
        let ui = $('#tourItemEdit [name="rotTime"] input')
        let min = parseFloat(ui.attr('min'));
        let max = parseFloat(ui.attr('max'));
        ui.on("change", function(e) {  
            var s = THREE.Math.clamp(parseFloat(e.target.value), min, max)
            if(isNaN(s))s = ''
            e.target.value = s   
            that.editingItemLi.tourData.rotTime = s
        })
    } 
    {////////////////
        let ui = $('.toolRight .snapTour [name="rotTime"] input')
        let min = parseFloat(ui.attr('min'));
        let max = parseFloat(ui.attr('max'));
        ui.on("change", function(e) {  
            var s = THREE.Math.clamp(parseFloat(e.target.value), min, max)
            if(isNaN(s))s = ''
            e.target.value = s  
        })
        ui.val(DATA.tourRotTime)
    }
    
    {
        this.scroller = { 
            tourBlackSpeed : new SlideBar({
                root: $('#tourBlackSpeed').eq(0),
                value: 100,
                min: 10,
                max: 1000,
                name: "tourBlackSpeed",
                unitStr: "%", 
                onchange: (s)=>{
                    
                },
                dragEndEvent: function() {
                    
                }
            }),
            tourWalkSpeed  : new SlideBar({
                root: $('#tourWalkSpeed').eq(0),
                value: 100,
                min: 10,
                max: 1000,
                name: "tourWalkSpeed",
                unitStr: "%",
                
                onchange: (s)=>{
                     
                },
                dragEndEvent: function() {
                    
                }
            }) 
        }
        var tourBlackSpeed = window.DATA.tourBlackSpeed;
        if(tourBlackSpeed == void 0){
            tourBlackSpeed = 100
        }
        var tourWalkSpeed = window.DATA.tourWalkSpeed;
        if(tourWalkSpeed == void 0){
            tourWalkSpeed = 100
        }
        
        this.scroller.tourBlackSpeed.setValue(tourBlackSpeed, true)
        this.scroller.tourWalkSpeed.setValue(tourWalkSpeed, true)
        
        
    }
    
    
    
}



function reIndexTourList(ul) {
    Array.from($(ul).find('li.guideItem')).forEach((li,index)=>{
        $(li).find("div:first-child span").text(index + 1);
    })
    
}

 



EditGuide.prototype.addFolderDom = function( tourData){
    var $folder = $("<li class='listItem' draggable='true'>" + "<div class=icon></div>" +    "<div class=title  >" + (tourData.name || "") + "</div>" + "<div class=DelConfirm>确定删除</div>" + "<div class=del></div>" + "</li>");
    
    $folder[0].tourItemDoms = []    //记录所有item的li dom
    $folder[0].tourData = tourData  //注：  data中的locations不会更新，保存时直接读取li
    $("#tourList ul").append($folder);
    
    return $folder 
    
}


EditGuide.prototype.getItemDom = function(title, img) {
    return '<li draggable="true" class="guideItem">' + '<div>' + '<span id="index"></span>' + '<div class="line"></div>' + '</div>' + '<div>' + '<span class="guide-name"  >' + title + '</span>' + '<input maxlength="14" class="hide" data-oper=tour-input>' + '</div>' + '<div class="preview" >' + '<div id="tourBg" style="background-image: url(' + img + ');" >' + '</div>' + '</div>' + '</li >';
}

 
 

EditGuide.prototype.createDom = function(data, data2) {
     
    var that = this;
    var tourAudio = data2.tourAudio || {}; 

  
    data.model.images.forEach((Info,index)=>{
        if(Info.locations){//是folder
            var $folder = this.addFolderDom(Info)
            // 音频链接 
            Info.locations = Info.locations.filter(e=>e)
            Info.locations.forEach(function(info, index) {
                if (info.thumbnail_signed_src) {
                    info.thumbnail_signed_src = manage.dealURL(info.thumbnail_signed_src) //旧场景有的少了https://
                    var $li = $(that.getItemDom( info.name, info.thumbnail_signed_src));
                    $li[0].tourData = info 
                    $folder[0].tourItemDoms.push($li[0])
                    
                }
            })
        }else{//是散落在外的item
            if (Info.thumbnail_signed_src) {
                Info.thumbnail_signed_src = manage.dealURL(Info.thumbnail_signed_src)
                var $li = $(that.getItemDom(Info.name, Info.thumbnail_signed_src));
                $li[0].tourData = Info
                // 音频链接
                $li[0].tourData.musicInfo = $li[0].tourData.musicInfo || tourAudio[Info.sid] || {}//新的music数据统一写在每个info下
                 
                $("#tourList ul").append($li)
                
            }
        }
    })

    reIndexTourList($("#tourList")[0])
    
    
}

 

 
EditGuide.prototype.editFolder = function(folderDom){ 
    
    this.editingFolderLi = folderDom;
    this.editingFolderLi.tempData = CloneObject(folderDom.tourData);
    $("#tourFolderEdit").removeClass('atRight');
    $("#tourFolderEdit .tourName input").val( $(folderDom).find('.title').text()||'');
   
    var momentTour = folderDom.tourData.momentTour || "default"
  
    this.momentFolderMenuOptions.updateChoseAtUI({name:momentTour})
    
    folderDom.tourItemDoms.forEach(li=>{
        $("#tourItemList>ul").append(li)
    })
    reIndexTourList($("#tourItemList>ul")[0])
    
    //判断当前点是否有导览音频 
    var musicInfo = this.editingFolderLi.tourData.musicInfo   //audio ? audio.music : null;
    if (musicInfo && musicInfo.music) {  // 判断改导览点是否已经已有音乐
        var musicName = musicInfo.name || '导览音频.mp3'  // 获取音频的文件名
        this.musicBoxFolder.show( musicName, "//" + musicInfo.music)
    } else {
        this.musicBoxFolder.hide()
    }
     
}
 

 
EditGuide.prototype.editItem = function(li) {
     
    this.editingItemLi = li
    this.editingItemLi.tempData = CloneObject(li.tourData);
    // 记录当前编辑的导览点
    var that = this;
    $('#tourItemEdit').removeClass('atRight');
    $('#tourItemEdit .tourName input').val( $(li).find('.guide-name').text() );
    
    $('#tourItemEdit [name="rotTime"] input').val(parseFloat(li.tourData.rotTime))
    
    
    var momentTour = li.tourData.momentTour || "default"
    /* $("#tourSwitch_Slice li").removeClass('chosen')
    $("#tourSwitch_Slice li[index="+ momentTour +"]").addClass('chosen'); */
    this.momentMenuOptions.updateChoseAtUI({name:momentTour})
    
    this.rotCheckBox.updateChoseAtUI(!li.tourData.dontRot)
    
    
    
    var metadata = JSON.parse(li.tourData.metadata);
    if(metadata.scan_id != 'outside'){
        var pano = player.model.panos.index[metadata.scan_id]
        if(pano){ 
            var q = new THREE.Quaternion().copy(metadata.camera_quaternion); 
            player.flyToPano({
                pano: pano, 
                quaternion: q,
                zoomLevel: metadata.zoom
            })
        }
    }
    
    if(!this.editingFolderLi){//在外的item， 可以上传音乐
        $('#tourItemEdit .mediaUpload').removeClass('hide')
        var musicInfo = li.tourData.musicInfo   //audio ? audio.music : null;
        if (musicInfo && musicInfo.music) {  // 判断改导览点是否已经已有音乐
            var musicName = musicInfo.name || '导览音频.mp3'  // 获取音频的文件名
            this.musicBoxItem.show(  musicName, "//" + musicInfo.music)
        } else {
            this.musicBoxItem.hide() 
        }  
        
    }else{
        $('#tourItemEdit .mediaUpload').addClass('hide')
    }
    
    

}

EditGuide.prototype.completeFolder = function(){//点击完成 
     
    
    var tourName = $('#tourFolderEdit .tourName input').val() //|| this.targetTourPoint.context.innerText;
    $(this.editingFolderLi).find(".title").text(tourName) 
    //this.editingFolderLi.tourData.momentTour = $("#folderTourSwitch input").is(':checked') ? "black" : 'walk' 
    
    var momentTour = this.momentFolderMenuOptions.getSelectName()//$("#folderTourSwitch li.chosen").attr("index"); 
    this.editingFolderLi.tourData.momentTour = momentTour != 'default' ?  momentTour : null;
    this.editingFolderLi.tourData.name = tourName
    
    
    this.editingFolderLi.tourItemDoms = Array.from($('#tourItemList ul li'));
     
    var done = ()=>{
        //$("#tourFolderEdit .mediaUpload").find("input").val('');  // 清空 文件
        this.musicBoxFolder.hide()// 清空 文件
        this.editingFolderLi = null
        $("#tourFolderEdit").addClass('atRight');
        $(".edit-loading").addClass('hide');
        $("#tourItemList>ul").html('')
    }
    
    //save导览音乐  
    var musicFile = this.musicBoxFolder.getFile()
    if (musicFile) { //有input文件
        $(".edit-loading").removeClass('hide');
        
        var musicName = this.musicBoxFolder.getName() 
    
        new Promise( (resolve, reject)=> {
            uploadMusic(musicFile, function(res) {
                resolve(res.data);
            })
        } ).then( (src)=>{
            $(".edit-loading").removeClass('hide'); //上传完继续显示loading
            var audio = new Audio();
            audio.src = src;
            audio.onloadedmetadata = ()=>{
                var srcArr = src.split('/');  // 对返回的src进行处理, 去掉前面域名, 尽量避免修改main.js
                srcArr[0] = '';
                src = srcArr.join('/');
                
                this.editingFolderLi.tourData.musicInfo = {    
                    "name": musicName,
                    "music": '/' + src,
                    "time": Math.round(audio.duration * 1000 + 1000)
                }
                done() 
            }
        })
    }else  done()
    
    
}



EditGuide.prototype.completeItem = function() {//点击完成
  
    var done = ()=>{
        this.musicBoxItem.hide()// 清空 文件
        this.editingItemLi = null
        $("#tourItemEdit").addClass('atRight');
        $(".edit-loading").addClass('hide');
    }
    
     
    var tourName = $('#tourItemEdit .tourName input').val()  
    $(this.editingItemLi).find('.guide-name').text(tourName)
     
     
    var momentTour = this.momentMenuOptions.getSelectName()
    this.editingItemLi.tourData.momentTour = momentTour != 'default' ?  momentTour : null;
    this.editingItemLi.tourData.name = tourName
    
    
    //save导览音乐  
    var musicFile = this.musicBoxItem.getFile()
    if (!this.editingFolderLi && musicFile) { //有input文件
        $(".edit-loading").removeClass('hide');
        
        var musicName = this.musicBoxItem.getName()  //$('#tourItemEdit .mediaUpload .title').text();
         
        new Promise( (resolve, reject)=>{
            uploadMusic(musicFile, function(res) {
                resolve(res.data);
            })
        } ).then( (src)=>{
            $(".edit-loading").removeClass('hide'); //上传完继续显示loading
            var audio = new Audio();
            audio.src = src;
            audio.onloadedmetadata = ()=>{
                var srcArr = src.split('/');  // 对返回的src进行处理, 去掉前面域名, 尽量避免修改main.js
                srcArr[0] = '';
                src = srcArr.join('/'); 
                this.editingItemLi.tourData.musicInfo = {    
                    "name": musicName,
                    "music": '/' + src,
                    "time": Math.round(audio.duration * 1000 + 1000)
                }
                done() 
            }
        })
    }else done() 

    
}

EditGuide.prototype.cancelFolder = function(){//放弃编辑
    this.musicBoxFolder.hide()// 清空 文件
     
    this.editingFolderLi.tourData = this.editingFolderLi.tempData;//还原
    this.editingFolderLi.tourItemDoms.forEach((e,i)=>{
        e.tourData = this.editingFolderLi.tourData.locations[i]
        $(e).find('.guide-name').text(e.tourData.name)
        $(e).find('#tourBg')[0].style.backgroundImage = 'url(' + e.tourData.thumbnail_signed_src + ')';
    })
    
       
    this.editingFolderLi = null
    $("#tourFolderEdit").addClass('atRight'); 
    $("#tourItemList>ul").html('')
}   
EditGuide.prototype.cancelItem = function(){//放弃编辑 
    this.musicBoxItem.hide()// 清空 文件
     
    this.editingItemLi.tourData = this.editingItemLi.tempData;//还原
    $(this.editingItemLi).find('#tourBg')[0].style.backgroundImage = 'url(' + this.editingItemLi.tourData.thumbnail_signed_src + ')';
    
    
    this.editingItemLi = null
    $("#tourItemEdit").addClass('atRight');
   
}    


 
/**
 * @author fyz 2019.07.31
 * @description uploadGuide方法新增参数renew状态码, 表示是否重新录制当前导览, 新增targetTourPoint, 记录当前的导览点元素
 */
EditGuide.prototype.uploadGuide = function(urlData, guide, renew, insideFolder) {
    var that = this;

    uploadImg(urlData, function(rs) {
        if (rs.code === 0) {
            //var editGuide = new EditGuide();
            var sid = rs.data.substring(rs.data.lastIndexOf('/') + 1, rs.data.lastIndexOf('.'));
            var thumbnail_signed_src = rs.data;
            var args = JSON.parse("{" + guide + "}");

            args.sid = sid;
            args.name = sid;
            args.thumbnail_signed_src = thumbnail_signed_src;
            args.metadata = JSON.stringify({
                camera_mode: args.metadata.camera_mode,
                camera_position: {
                    x: args.metadata.camera_position[0],
                    y: args.metadata.camera_position[1],
                    z: args.metadata.camera_position[2]
                },
                camera_quaternion: {
                    x: args.metadata.camera_quaternion[0],
                    y: args.metadata.camera_quaternion[1],
                    z: args.metadata.camera_quaternion[2],
                    w: args.metadata.camera_quaternion[3]
                },
                ortho_zoom: args.metadata.ortho_zoom,
                scan_id: args.metadata.scan_id || "outside",
                //这个"outside"不能随便改成别的
                /* final_angle: 110,
                is_ortho: false, */
                zoom: args.metadata.zoom // fyz zoom是内部计算的zoomLevel, 乘以系数1.06才是实际缩放倍数
            })

            if (renew ) { // 重新录制导览
                // 更新数据
                if(!insideFolder){
                    args.musicInfo = that.editingItemLi.tourData.musicInfo;
                }
                args.name =  that.editingItemLi.tourData.name
                $(that.editingItemLi).find('#tourBg')[0].style.backgroundImage = 'url(' + args.thumbnail_signed_src + ')';
                that.editingItemLi.tourData = args;
                
            } else { 
                var $li = $(that.getItemDom(args.name, args.thumbnail_signed_src));
 
                $li[0].tourData = args;
                
                var $ul = insideFolder ? $("#tourItemList>ul") : $("#tourList>ul")
                $ul.append($li); 
                reIndexTourList($ul[0])
                
            }
        }
    },'tour.jpg');
}




EditGuide.prototype.getSavingInfo = function(){
    var data = []
     
     
    Array.from($("#tourList ul li")).forEach(dom=>{
        if(dom.classList.contains("guideItem")){//item
            //dom.tourData.name = $(dom).find('.guide-name').text()
            data.push(dom.tourData)
        }else{//folder
            var dataFolder = dom.tourData;
            //dataFolder.name = $(dom).find('.title').text()
            dataFolder.locations = dom.tourItemDoms.map(li => li.tourData ) 
            data.push(dataFolder)
        }
    })
    
    
    return data
}























/* -- 以下是一些公用的方法  -- */
//限制大小
var restrictedSize = function(file, _size) {
    //限制大小不大于8m
    var fileSize = 0;
    var isIE = /msie/i.test(navigator.userAgent) && !window.opera;
    if (isIE && !file) {
        var fileSystem = new ActiveXObject("Scripting.FileSystemObject");
        var _file = fileSystem.GetFile(filepath);
        fileSize = _file.Size;
    } else {
        fileSize = file.size;
    }
    var size = fileSize / 1024;
    var RSize = _size * 1024;
    if (size > RSize) {
        alert("文件不能大于" + _size + "M");
        return false;
    }
    if (size <= 0) {
        alert("文件大小不能为0M！");
        return false;
    }
    return true
}

//上传音乐
var uploadMusic = function(file, callback) { 
    uploadFile(file, 'audio', callback);
}

$(".toolRight .music .itemTitle span").text(`背景音乐 (<${_musicMaxWeight}M)`)

//上传图片
function uploadImg(urlData, callback, fileName) {
    //console.log('urlData',urlData);

    var bytes = window.atob(urlData.split(',')[1]);
    //去掉url的头，并转换为byte  

    //处理异常,将ascii码小于0的转换为大于0  
    var ab = new ArrayBuffer(bytes.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i);
    }

    var blob = new Blob([ab],{
        type: 'image/jpeg',
    });

    uploadFile(blob, 'hot/images', callback, fileName);

}

//上传文件
function uploadFile(file, type, callback, fileName) {
    $(".edit-loading").removeClass("hide");
    var formData = new FormData()
    formData.append('name', number)
    formData.append('dir', type)
    formData.append('random', true)
    //formData.append('file', file) 
    formData.append("file", file, fileName);

    let url = cmp ? ('/api/scene/upload/' + number) : ('/manage/scene/upload/' + number)

    $.ajax({
        url: ceshi + url,
        data: formData,
        headers: {
            token: token
        },
        dataType: 'json',
        type: 'POST',
        cache: false,
        //上传文件无需缓存
        processData: false,
        //用于对data参数进行序列化处理 这里必须false
        contentType: false,
        //必须
        success: function(rs) {
            if(rs.code === 0){
                callback(rs, file);
                $(".edit-loading").addClass("hide");
            }else if (rs.code === 5001) {
                alert('请重新登录')
                localStorage.dcj_token = ''
                location.reload() 
            }else{
                alert(`uploadFile (url:${url}) code : ${rs.code}  \n${rs.msg}`) 
                
            }
            
        }
    })
}

function _animate(prevRect, target) {
    var ms = 300;

    if (ms) {
        var currentRect = target.getBoundingClientRect();

        if (prevRect.nodeType === 1) {
            prevRect = prevRect.getBoundingClientRect();
        }

        _css(target, 'transition', 'none');
        _css(target, 'transform', 'translate3d(' + (prevRect.left - currentRect.left) + 'px,' + (prevRect.top - currentRect.top) + 'px,0)');

        target.offsetWidth;
        // 触发重绘
        //放在timeout里面也可以
        // setTimeout(function() {
        //     _css(target, 'transition', 'all ' + ms + 'ms');
        //     _css(target, 'transform', 'translate3d(0,0,0)');
        // }, 0);
        _css(target, 'transition', 'all ' + ms + 'ms');
        _css(target, 'transform', 'translate3d(0,0,0)');

        clearTimeout(target.animated);
        target.animated = setTimeout(function() {
            _css(target, 'transition', '');
            _css(target, 'transform', '');
            target.animated = false;
        }, ms);
    }
}
//给元素添加style
function _css(el, prop, val) {
    var style = el && el.style;

    if (style) {
        if (val === void 0) {
            if (document.defaultView && document.defaultView.getComputedStyle) {
                val = document.defaultView.getComputedStyle(el, '');
            } else if (el.currentStyle) {
                val = el.currentStyle;
            }

            return prop === void 0 ? val : val[prop];
        } else {
            if (!(prop in style)) {
                prop = '-webkit-' + prop;
            }

            style[prop] = val + (typeof val === 'string' ? '' : 'px');
        }
    }
}

function upload($files, type, cb) {
    var length = $files.length
    var rcount = 0
    var result = []

    Array.from($files).forEach(function(dFile, index) {
        //a 标签的success 是用来判断是否是已经上传过的文件
        //attr-thum 属性是视频的图片
        //videoURL 是判断是否有视频
        var $file = $(dFile)
        var $image = type == 'videos' ? $file.find('img.play-video') : $file.find('img')

        if ($file.hasClass('success')) {//已经上传过，有链接
            if ($file.attr('attr-thum')) {//视频的封面
                result[index] = $file.attr('attr-thum')
            } else if ($image.attr('videoURL')) {//视频
                result[index] = $image.attr('videoURL')
            } else {
                result[index] = $image.attr('src')
            }
            return success(++rcount);
        } else {
            switch ($file[0].localName) {
            case "input":
                dFile = $file[0]
                break;
            case "span"://封面
                dFile = $file.find('input')[0];
                break;
            case "a"://重传的图or视频
                dFile = {files:[$file[0].file]}
                //dFile = $file.find('input')[0];
                break;
            default:
                dFile = $file.closest("li").find('.upload input')[0];
                break;
            }
            // dFile = $file[0].localName === "input" ? $file[0] : $file[0].localName === "span" ? $file.find('input')[0] :  $file.closest("li").find('.upload input')[0];
        }

        var file = (dFile.files && dFile.files[0]) || '';
        if (!file)
            return success(++rcount);

        uploadFile(file, 'hot/' + type, function(rs) {
            //似乎所有图保存路径都是这个hot/前缀。图地址 ："场景id/edit/重新生成的文件名.原后缀", 展示页面也是。
            if (rs.code === 0) {
                result[index] = rs.data
            }
            ;success(++rcount);
        })

    })
    //判断当前队列元素是否处理成功
    function success() {
        if (rcount === length) {
            cb(result)
        }
    }
    success()
}

function getIndex(el) {
    let index = 0;
    if (!el || !el.parentNode) {
        return -1;
    }
    while (el && (el = el.previousElementSibling)) {
        index++;
    }
    return index;
}
dataURLtoBlob = function(dataurl) {
    //将base64转换blob
    var arr = dataurl.split(',')
      , mime = arr[0].match(/:(.*?);/)[1]
      , bstr = atob(arr[1])
      , n = bstr.length
      , u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr],{
        type: mime
    });
}
;
//=========================

var eachMaxWeights = {
    //大小限制
    "photo": 10,
    "video": 1000,//50,  expand size for overlayVideo 
    //20,
    "audio": 10, //5
    "model": 5
}

var supportTypes = {
    //支持后缀
    "photo": ["jpg", "png", "jpeg", "bmp", "gif"],
    "audio": ["mp3", "aac", "ogg", "wav"/* , "m4a" */],
    "video": ["mp4", "mov" ,"webm",/* "rmvb", "wmv" */],//ios:mov
    'model':["obj"]
}
function getExt(name) {
    //后缀
    if (name.indexOf('.') > -1) {
        var a = name.split(".");
        return a.pop();
    } else {
        return '';
    }
}
function detectType(fileName) {
    //检测文件后缀类型 
    console.log("名:" + fileName)
    var ext = getExt(fileName);
    console.log("后缀:" + ext)
    //不一定所有浏览器都可以
    var type;
    //仅能根据后缀判断类型，尽管后缀可能被修改。 其他方法如file.type和base64开头的字符串均是根据后缀。
    for (var i in supportTypes) {
        if (supportTypes[i].indexOf(ext.toLowerCase()) > -1) {
            type = i;
            break;
        }
    }
    if (type) {
        return type;
    } else {
        return false;
    }

}
//手机上的相片会不会太大？
var detectWeights = function(file, type) {
    //检测大小
    var size = file.size / 1024 / 1024;
    console.log("weight" + size)
    var over;
    if (size > eachMaxWeights[type]) {
        over = Math.ceil(size * 100) / 100;
    }
    return over;
}
var exitUpload = function(input) {
    $('.waiting').removeClass('showloading');
    input.value = "";
}

var inputMedia = function(options, type, e) {

    var input = e.target;
    if (!window.FileReader) {
        alert('您的浏览器不支持上传文件');
        exitUpload(input);
        return;
    } else if (e.target.files.length === 0) {
        /* exitUpload(input); */
        return;
    }

    $('.waiting').addClass('showloading');

    var file = e.target.files[0];
    var elemType = type;

    var inputType = detectType(file.name);

    var chType = {
        "photo": '图片',
        "video": '视频',
        "audio": '音乐',
        'model': '模型'
    }

    var overWeight = detectWeights(file, inputType);

    if (elemType) {
        //有指定其中一种类型
        if (inputType != elemType) {
            alert("您选择的不是浏览器支持的" + chType[elemType] + '文件,请重新选择');
            exitUpload(e.target);

            return;
        }
    } else {
        if (!options.enableTypes.includes(inputType)) {
            var text = "";
            options.enableTypes.forEach((type,index)=>{
                text += ((index != 0 ? " / " : "") + chType[elemType])
            }
            )
            alert("您选择的不是浏览器支持的" + text + '文件,请重新选择');

            exitUpload(e.target);
            return;
        }
    }

    if (overWeight) {
        alert(`文件过大（${overWeight}兆），不能大于${eachMaxWeights[inputType]} 兆`)
        exitUpload(e.target);
        return;
    }

    var loadError = function() {
        exitUpload(e.target);
        alert(`文件出错, 无法加载此${chType[inputType]}文件。 文件可能损坏，或者浏览器不支持，或者后缀与文件不匹配。建议在IE以外的浏览器上传`)
    }

    deal(type);

    function deal(type) {
        console.log('开始deal文件')
        var reader = new FileReader();
        //reader.name = file.name;
        if(type == 'model'){
            reader.readAsText( file );
        }else{
            reader.readAsDataURL(file);//readAsDataURL
        }//readAsArrayBuffer readAsBinaryString
        reader.onerror = function(evt) {
            loadError()
        }            
        reader.onload = function(evt) {
            if(!evt.target.result){
                $('.waiting').removeClass('showloading');
                return alert('文件出错，可能文件太大')
            }
            if (inputType == "model") { 
                var object = new THREE.OBJLoader().parse( evt.target.result );
                options.modelDone(file, object) 
                
                $('.waiting').removeClass('showloading');                
                return;
            }
            
            
            
            var blob = dataURLtoBlob(evt.target.result);
            var blobSrc = window.URL.createObjectURL(blob);

            if (inputType == "photo") {
                /* EXIF.getData(file, function () {
                    //获取相片旋转 
                    EXIF.getAllTags(this);
                    var orient = EXIF.getTag(this, 'Orientation');
                    var img = new Image();
                    img.src = evt.target.result;
                    img.onload = function (e) { //这时img才有宽高，才能赋予texture宽高
                        var smallerImg = CompressImg(e.target, {
                            weight: blob.size,
                            maxWeight: 0.5 * 1024 * 1024,
                            maxSize: 1480
                        });
                        //最大500k
                        smallerImg.onload = function () {

                            var f = function (resultImg) {//处理结束回调
                                options.photoDone(resultImg)
                                $('.waiting').removeClass('showloading');
                            }

                            Rotate(smallerImg, orient, f)
                            window.URL.revokeObjectURL(blobSrc);
                        }
                    }
                        .bind(this)
                    img.onerror = loadError;
                }); */
                var img = new Image();
                img.setAttribute("crossOrigin", 'Anonymous')//要在src设置好前解决跨域
                img.src = blobSrc;
                img.base64Src = evt.target.result;
                img.file = file;
                img.onload = function(e) {
                    options.photoDone(img)
                    $('.waiting').removeClass('showloading');
                    img.onload = null  //防止src重新赋值后又触发

                }
            } else if (inputType == "video") {

                var video = $('<video controls="controls" x5-playsinline="" webkit-playsinline="true" playsinline="true" controlslist="nodownload"></video>')[0]
                video.setAttribute("crossOrigin", 'Anonymous')
                //要在src设置好前解决跨域
                $(video).on('contextmenu', function() {
                    return false;
                });
                //禁止右键点击出现选项（尤其是下载选项）

                $(video).attr('src', blobSrc);
                //华为无法使用blobSrc，否则加载不出来
                //$(video).attr('src', evt.target.result);
                video.base64Src = evt.target.result;

                var hasload = false;
                var error = false;

                var up = function() {
                    if (hasload)
                        return;

                    hasload = true;

                    options.videoDone(file, video)

                    $('.waiting').removeClass('showloading');
                }

                video.addEventListener('loadeddata', function(e) {
                    up()
                })
                //video.onerror = loadError;
                video.onerror = function(e) {
                    console.log('error')
                    error = true;

                    loadError();
                    //将mp4改成mov后缀会error

                }
            } else if (inputType == "audio") {
                //林俊波  new Howl  For IE&Safari：
                var sound = new Howl({
                    src: [blobSrc],
                    format: ["mp3"]
                });
                // Clear listener after first call.
                sound.once('load', function() {
                    console.log('loaded sound')
                    sound.unload()

                    console.log("audio.onloadeddata");
                    var audio = new Audio;
                    audio.controls = "controls";
                    audio.src = blobSrc;
                    audio.base64Src = evt.target.result;
                    options.audioDone(file, audio)

                    $('.waiting').removeClass('showloading');
                    //}

                });

                sound.once('loaderror', function() {
                    loadError.apply(this, arguments)
                    console.log('loaderror sound')

                });

                // Fires when the sound finishes playing.
                sound.on('end', function() {
                    console.log('Finished sound');
                });
            }
        }

    }
    e.target.value = "";
    //更改value会触发change

}
.bind(player)





//滑动条控件 
var SlideBar = function(o) {

    var scope = this;
    this.name = o.name;
    this.value = o.value;
    //初始值
    this.min = o.min != void 0 ? o.min : 0;
    this.max = this.oriMax = o.max != void 0 ? o.max : 100;
    this.noValue = o.noValue;
    //是否数值输入
    this.precision = o.precision;
    //精度 保留几位小数
    var div = $('<div><div class="Main"><div class="scrollBar"><div class="scroll_Track"></div><div class="scroll_Thumb"></div></div>' + (o.noValue ? '' : '<div class="BarTxt"><input class="scrollBarTxt"></div></div>'))
    div.addClass('slider')
    if (o.unitStr) {
        div.find(".BarTxt").append($('<span>' + o.unitStr + '</span>'));
    }

    o.root.append(div);
    this.line = $(".scrollBar", div);
    this.knot = $(".scroll_Thumb", div);
    o.noValue || (this.textArea = $(".scrollBarTxt", div));
    this.track = $(".scroll_Track", div);
    this.unitStr = o.unitStr;
    //unitStr是单位字符串，比如角度的°。不能是数字 
    this.onchange = o.onchange;
    this.percent = null;
    this.dragStart = false;
    this.offsetToBody = null;
    this.getOffset();
    this.checkError();
    this.percent = this.getPercent();
    o.noValue || this.displayValue();
    this.moveKnot();
    this.knotWidth = 0;
    this.lineWidth = 0;
    //this.waitValue;//等待要触发事件的值，防止崩溃
    this.avoidCrash = o.avoidCrash;
    this.realMax = !this.noValue ? o.realMax : null;
    //如果传入realMax,代表可以通过输入数字修改最大值范围，最大值不可以超过realMax。

    this.scrollUnit = (scope.max - scope.min) * 0.001;
    if (this.precision != void 0) {
        var prec = Math.pow(10, -this.precision);
        this.scrollUnit < prec && (this.scrollUnit = prec);
    } else {
        //默认化为整数
        this.scrollUnit < 1 && (this.scrollUnit = 1);
    }

    this.dragStartEvent = o.dragStartEvent;
    this.line.on("mousedown touchstart", function(event) {
        scope.dragStart = true;
        scope.dragChange(event);
        if (scope.dragStartEvent)
            scope.dragStartEvent()
        //if(o.avoidCrash && isMobile)scope.dealInterval();	
    });

    !window.isMobile && this.line.on("mousewheel DOMMouseScroll wheel", function(event) {
        event.preventDefault();
        var v = event.originalEvent.deltaY > 0 ? -scope.scrollUnit : scope.scrollUnit;
        scope.setValue(scope.value + v);
    });

    this.dragEndEvent = o.dragEndEvent;
    var stop = function() {
        if (scope.dragStart) {
            scope.dragStart = false;
            if (scope.dragEndEvent)
                scope.dragEndEvent()
        }
    }

    $(document).on("mouseup touchend", stop)
    /* isMobile ||  */
    $("#player").on("mouseup", stop)

    var lastChangeTime = 0;
    $(document).on("mousemove touchmove", function() {
        if (scope.dragStart) {
            /* if(isMobile && o.avoidCrash){ 
				scope.lastDragEvent = event;
			}else  */
            scope.dragChange(event);

        }
    })

    o.noValue || this.textArea.on("change", function() {
        var v = parseFloat(scope.textArea.val());
        if (v != v)
            //NaN
            return;
        scope.setValueFromOutside(v);
    });
    window.addEventListener("resize",()=>{
        if(this.line[0].clientWidth){
            this.getOffset() 
            this.moveKnot();
        }
    })
}

SlideBar.prototype.dealInterval = function() {
    this.interval = setInterval(function() {
        this.lastDragEvent && this.dragChange(this.lastDragEvent);
        this.lastDragEvent = null
        if (!this.dragStart)
            clearInterval(this.interval)
    }
    .bind(this), 90)
}

SlideBar.prototype.changeLimit = function(o) {
    if (o.min)
        this.min = o.min;
    if (o.max)
        this.max = o.max;
}

SlideBar.prototype.getOffset = function() {
    //为了检测鼠标位置需要获得相对body的offset
    var left = this.line[0].offsetLeft;
    var element = this.line[0];
    while (element = element.offsetParent) {
        left += element.offsetLeft;
    }
    this.offsetToBody = (left == 0) ? (this.offsetToBody || 0) : left;
    //如果left为0，很可能是它不可见，那么最好使用旧的offsetToBody

}
SlideBar.prototype.InitOffset = function() {
    //如果一开始scroller没有显示，要在显示时获取一下offset 
    this.getOffset();
    this.getWidth();
    this.moveKnot();
}

SlideBar.prototype.checkError = function() {
    if (this.min >= this.max) {
        console.log("scrollbar值有误  " + name);
        return;
    }
}

SlideBar.prototype.getPercent = function() {
    return (this.value - this.min) / (this.max - this.min);
}

SlideBar.prototype.displayValue = function(value) {
    //this.textArea.val(this.value + (this.unitStr ? " " + this.unitStr : "")); 
    if (value != void 0)
        this.value = value;
    this.textArea.val(this.value)

}
SlideBar.prototype.getWidth = function() {
    this.knotWidth = this.knot.width();
    this.lineWidth = this.line.width() - this.knotWidth

}
SlideBar.prototype.moveKnot = function() {
    //this.getWidth(); 
    var width = this.percent * this.lineWidth;
    this.knot.css('left', width + "px")
    if (this.track)
        this.track.css('width', (width + this.knotWidth / 2) + "px")

}

SlideBar.prototype.bind = function(f) {
    this.onchange = f;
}

SlideBar.prototype.setValue = function(v, noEvent, changeMax) {
    //设置数值并改变knot位置   noEvent为true的话就可以不触发回调函数，仅仅改变显示
    //if(this.value==v)return;	//因为换选材质需要执行后面onchange才会带动preview的改变，所以这里一样还是执行，而拉动滑动条的部分值不变就不执行

    if (this.precision != void 0) {
        this.value = parseFloat(v.toFixed(this.precision))
    } else {
        //默认化为整数
        this.value = Math.round(v);
    }
    
    this.value = THREE.Math.clamp(this.value, this.min, this.max)
    
    if (changeMax && this.realMax != void 0) {
        if (this.value > this.oriMax) {
            this.value = Math.min(this.value, this.realMax);
            this.changeLimit({
                max: this.value
            })
        } else {
            this.changeLimit({
                max: this.oriMax
            })
        }
    }

    this.percent = this.getPercent();

    var makeit = true;
    if (this.onchange && !noEvent) {
        var result = this.onchange(this.value);
        //如果执行的函数不允许这个值，就不能变
        if (result === false) {
            makeit = false;
        }
    }

    if (makeit) {
        this.moveKnot();
        //console.log("SlideBarV-"+this.name + " : "+this.value)
        this.noValue || this.displayValue();
    }

}

SlideBar.prototype.dragChange = function(event) {
    //拖动时触发 计算数值
    //this.getWidth();
    //if(!this.offsetToBody  || isNaN(this.offsetToBody)this.getOffset();
    if (event.clientX != void 0) {
        this.percent = (event.clientX - this.knotWidth / 2 - this.offsetToBody) / this.lineWidth;
    } else {
        if (event.touches != void 0) {
            this.percent = (event.touches[0].clientX - this.knotWidth / 2 - this.offsetToBody) / this.lineWidth;
        } else
            this.percent = (event.originalEvent.touches[0].clientX - this.knotWidth / 2 - this.offsetToBody) / this.lineWidth;

    }

    if (this.percent < 0)
        this.percent = 0;
    else if (this.percent > 1)
        this.percent = 1;
    //var v = this.percent * this.max;
    var v = this.percent * (this.max - this.min) + this.min;
    //var v = Math.round(this.percent * this.max * 10)/10;   //精度为0.1

    if (this.value == v)
        return;

    this.setValue(v);

}

SlideBar.prototype.setValueFromOutside = function(v) {
    //外面的事件触发的
    if (this.line.width() == 0)
        return;
    //不可见就不执行。
    //v = Math.round(v*10)/10;
    if (this.realMax != void 0) {
        v = THREE.Math.clamp(v, this.min, this.realMax);
        if (v > this.max)
            this.changeLimit({
                max: v
            });
    } else
        v = THREE.Math.clamp(v, this.min, this.max);
    this.setValue(v);
}

//-----------------------------------------------------   





var transformControls;
var initTransformCtl = function(THREE) {

    TransformControls.init(THREE)
    TransformControls.prototype.transCtlChangeMode = function(mode) {
        //if(!this.editing)return;
        if (mode && this.mode != mode) {
            this.mode = mode; 
            transformControls.children[0].showZ =  this.mode != 'scale' ||  transformControls.scaleShowZ
        }
    }
    ,

    transformControls = new TransformControls(player.camera,player.domElement,{
        player: player,
        dontHideWhenFaceCamera: true,
        /* scaleAxis: ["x", "y"],
        //隐藏了z轴。虽然参数没用上
        NoScaleZ: true //整体缩放时只缩放xy轴。 */ 
    });
    transformControls.space = 'local'//为了在当前方向上平移
    transformControls.setSize(1.5)
    player.model.add(transformControls)
    
    
    transformControls.addEventListener('positionChanged',()=>{
        editTool.hotpoint.updateTransform('position');
    })
    transformControls.addEventListener('quaternionChanged',()=>{
        editTool.hotpoint.updateTransform('quaternion');
    })
    transformControls.addEventListener('scaleChanged',()=>{
        editTool.hotpoint.updateTransform('scale');
    })
    
    let history = new HistoryRecord({ 
            applyData: (data)=>{ 
                if(data.object.parent && data.object == editTool.hotpoint.editSpot  ){
                    //data = Potree.Common.CloneObject(data) //避免使用后更改数据又被使用 
                    data.matrix.decompose( data.object.position, data.object.quaternion, data.object.scale );
                    editTool.hotpoint.updateTransform()
                    
                    return true
                }  
            },
            getData:(object)=>{   
                return {
                    object,
                    matrix:  object.matrixWorld.clone(),
                }
            } 
        })  
    
    transformControls.addEventListener('mouseDown', ()=>{ //dragstart
        history.beforeChange(transformControls.object)
    })  
    transformControls.addEventListener('mouseUp',()=>{
        history.afterChange(transformControls.object)
    })
    transformControls.history = history
    

    /* $(".MenuOptions[name='transform'] li").on("click", (e)=>{
        transformControls.transCtlChangeMode($(e.target).attr("index"))
    }) */
    transformControls.transformMenuOptions = new MenuOptions({  
        dom:  $(".MenuOptions[name='transform'] "),
        uiCallBack : (o)=>{ 
      
            var mode = o.name == void 0 ?  o.$li.attr('index') : o.name
            transformControls.transCtlChangeMode(mode)
            var hotpointDetail = editTool.hotpoint.hotpointDetail
            hotpointDetail.find(" li[name=setPos] button[name=setSpace] ").css('display',mode == 'scale' ? 'none' : 'block') 
            hotpointDetail.find(" li[name=setPos] button[name=useSuitableRatio] ").css('display',mode == 'scale' ? 'block' : 'none') 
            hotpointDetail.find(" li[name=setPos] button[name=resetRot] ").css('display',mode == 'rotate' ? 'block' : 'none') 
        },
        callbackWhenChose:(o)=>{
             
        }
         
    })
    
    transformControls.enableScaleZ = ()=>{
        if(transformControls.mode == 'scale'){
            transformControls.children[0].showZ = true
        }
        transformControls.scaleShowZ = true
       
    }
    transformControls.unableScaleZ = ()=>{
        if(transformControls.mode == 'scale'){
            transformControls.children[0].showZ = false
        }
        transformControls.scaleShowZ = false
    }
    editTool.transformControls = transformControls
}



 
//----------------漫游可见性---------------------------------

var VisiSet = {

    setPanoVisible: false,
    setTagVisible: false,
    setPanoLog:false,
    
    panoVLines: {},
    //线条
    panoVTemp: {},
    //修改后还没保存的临时数据

    tagVsetting: null,
    //正在设置的热点中心点
    tagsVLines: {},
    //线条

    //tagVTemp //修改后还没保存的临时数据

   
    $confirmSnap: $("#camera-start"),
    
    changeBtn : $(".toolMid .midBottom #midBtns>button").eq(1),
    
    colors: {
        green: "#00c8ae"
    },
    init: function() {
        this.footIconSizeRatio =  Math.max(player.model.size.x, player.model.size.z)  / 25; 
        
        this.meshGroup = new THREE.Object3D;
        this.meshGroup.name = "setVisible-group"
        
        player.model.add(this.meshGroup)
        $("#hotVisible button").on("click", ()=>{
            VisiSet.enterSet(VisiSet.beginSetTagVisible.bind(VisiSet)) 
        })
        
        
        /* player.on("mode.changing",(from,to)=>{
            if(to == "panorama" && (VisiSet.setPanoVisible || VisiSet.setTagVisible || VisiSet.setPanoLog)){
                VisiSet.finishSetPanoVisible()
                VisiSet.finishSetTagVisible()
                VisiSet.finishSetPanoLog()
            }
        }) */
        
        player.model.on("floor.changed",(toFloor)=>{
            if (VisiSet.setPanoVisible || VisiSet.setTagVisible || VisiSet.setPanoLog ){
                VisiSet.changePanoVisi(true, toFloor)
            } 
        }) 
        
        player.on("view.changed",(e)=>{
            if(e.cameraChanged && (VisiSet.setPanoVisible || VisiSet.setTagVisible || VisiSet.setPanoLog || VisiSet.setRoute)){
                VisiSet.updateFootIconSize();
            }
        })
        
        
        this.changeBtn.on("click",()=>{//隐藏与显示该点
            if(this.changeBtn.attr("function") == "hide"){
                for(let i in this.panoVLines){
                    this.panoVLines[i].visible && this.dealPanoVisible(i);
                } 
            }else{
                //显示该点时，显示周围一定距离内、到该点没有遮挡的pano
                //????? 
                var list = player.model.panos.sortByScore([e=>e.isAligned()], [(pano)=>{return -pano.position.distanceTo(this.panoVsetting.position)}])	
               
                 
				if(list.length == 1){
					console.log('仅有一个漫游点')
					return;
				} 
                var ifBlock = function(panoA, panoB ){
                    var A = panoA.position.clone();
                    var B = panoB.position.clone(); 
                    return convertTool.ifIntersectChunks(A, B, {})
                }
                var okList
				var s = Math.max(-list[1].score*2,   4);	//i==1的一定显示
                for(var i=1;i<list.length;i++){ 
                    if(-list[i].score < s){  
                        if(ifBlock(this.panoVsetting, list[i].item)){
                            list[i].block = true   //有阻挡 
                        }
                        list[i].good = true  
                    }else{
                        okList || (okList = list.filter(e=>e.good && !e.block));//绝对可以使用的
                          
                        if(okList.length <2 ){
                            if(!ifBlock(this.panoVsetting, list[i].item)){
                                if(okList.length == 0){
                                    okList.push(list[i])
                                }else{//1 
                                    var lastPos = okList[0].item.position.clone().sub(this.panoVsetting.position).setY(0);
                                    var thisPos = list[i].item.position.clone().sub(this.panoVsetting.position).setY(0);
                                    if(lastPos.angleTo(thisPos) > Math.PI / 2){
                                        console.log('再加一个  角度'+THREE.Math.radToDeg(lastPos.angleTo(thisPos)))
                                        break;
                                    } 
                                    
                                }
                            }
                        }else{
                            break;
                        }
                    }
                }
                if(okList.length==0){//如果length为0，至少加一个pano, 虽然是遮挡的
                    okList.push(list[0].item)
                }
                okList.forEach(e=>this.dealPanoVisible(e.item.id))
                
                console.log(okList)
                
            } 
        }) 
        
        //----route-------
        
        if(!window.DATA.route) window.DATA.route = {}
        $('#routeShowSwitch input')[0].checked = !window.DATA.route.hide   //默认是否展示。 若隐藏也可以通过函数展示
        $("#routeGradual")[0].checked = !!DATA.route.gradualShow   
        $("#routeOpaShine")[0].checked = !!DATA.route.opacityShine   
         
        
        this.routeOptions = new MenuOptions({
            dom:  $(".toolRight .routeArrow .MenuOptions[name=routeLinkTool] "), 
            uiCallBack : (o)=>{  
                $(".toolRight .routeArrow  .remark[name=ctrl]").text(o.$li.attr("index") == 'link' ? 
                  '按顺序依次点击点位绘制箭头路线，右键结束绘制。' : 
                  '点击蓝线以删除路线')  
            },
            callbackWhenChose:(o)=>{
                this.routeInfo.operation = o.$li.attr("index") == 'link' ? 'addLink' : 'removeLink' 
            }
        })
        this.routeOptions.uiCallBack({$li:this.routeOptions.dom.find('.chosen')})
       
       
        $(".toolRight .routeArrow button.delete").on("click", ()=>{//清空 
            var choice = confirm(`确定删除全部路线吗`); 
            if(choice){
                for(let id1 in this.routeInfo.lines){
                    for(let id2 in this.routeInfo.lines[id1]){ 
                        this.routeInfo.removeLink(id1,id2)
                    }
                }
            }
        })
         
        
       
    },
    enterSet: function(fun) {
        var enter = function() {
            if (player.modeTran.split('-')[1] != "floorplan") {
                setTimeout(fun, 300)
                //提前一点出现
            }
            player.flyToMode("floorplan", fun);
        }

        permitTranMode(['dollhouse','floorplan','inside'])
        player.cameraControls.controls.panorama.insideLookLimitDown = -60 
        
        if ( player.modeTran == void 0) {
            player.afterCModeFuc = ()=>{
                enter()
            }
        } else
            enter()

    },
    
    beginSetPanoLog: function() {
        player.flying || $(".toolLeft").removeClass("unable")
        if (this.setPanoLog)
            return; 
        this.setPanoLog = true
        this.panosSelect = []
        
        this.showFootIcons(null, true);
        this.updateFootIconSize()
        
        for(let i in player.model.hots){
            player.model.hots[i].visi_ = player.model.hots[i].mesh.visible;
            player.model.hots[i].mesh.visible = false
        } 
        
        this.changePanoVisi(true)
    },
    
    beginSetRoute: function(){
        if(this.setRoute)return
        this.setRoute = true
        this.showFootIcons(null, true); 
        this.updateFootIconSize()
        
        for(let i in player.model.hots){
            player.model.hots[i].visi_ = player.model.hots[i].mesh.visible;
            player.model.hots[i].mesh.visible = false
        }  
        this.changePanoVisi(true)
        //permitTranMode(['inside','dollhouse','floorplan'])
        if(!this.routeInfo){
        
            let color = '#409eff'  
            let lineMat = new THREE.LineBasicMaterial({color,depthTest:false})
            /* let arrow = new THREE.Mesh(new THREE.PlaneGeometry(0.3,0.3,1,1), new THREE.MeshBasicMaterial({
                map: Texture.load("images/introduce_open.png"),//style_jump
                transparent:true,
                depthTest:false,
                color,
                side: 2
            }))
            arrow.rotation.x = Math.PI / 2  //朝上 */
            
            
            
            let moveLine = (line,p1,p2)=>{
                LineDraw.moveLine(line, [p1,p2] )  
                //arrow:
                line.children[0].position.copy(new THREE.Vector3().addVectors(p1,p2).multiplyScalar(.5))
                //this.lineAdding.children[0].rotateOnAxis()
                //line.children[0].rotation.y = -new THREE.Vector2(p1.x - p2.x, p1.z - p2.z).angle() + Math.PI/2 
                line.children[0].lookAt(p1)
                line.children[1].position.copy(line.children[0].position)
                line.children[1].rotation.copy(line.children[0].rotation)
            }
            
            this.routeInfo = {
                lines : {},
                panoStart : null,
                plane : new THREE.Plane(),
                lineAdding : null,//用于连接的线
                lineMat, 
                operation : 'addLink',//or removeLink
                events : {
                    viewChange: (e)=>{ 
                         if(e.cameraChanged && this.routeInfo.panoStart){
                             this.routeInfo.updatePlane()
                         } 
                    },
                    pointerMove: ()=>{
                        if(!this.routeInfo.panoStart)return
                        let endPos = player.intersect ? player.intersect.point :  player.raycaster.ray.intersectPlane(this.routeInfo.plane)
                        moveLine(this.lineAdding, this.routeInfo.panoStart.floorPosition, endPos)
                        
                    },
                    click: (e)=>{//右键取消 (但拖拽后也会取消，因为右键mousedown不会触发事件，不好写算了)
                        e.button == 2 && /*player.mouseCouldBeClickToMove && */ this.routeInfo.cancel()
                    }
                    
                },
                updatePlane: ()=>{
                     let nor = player.getDirection()
                     this.routeInfo.plane.setFromNormalAndCoplanarPoint(nor, this.routeInfo.panoStart.floorPosition)
                },
                cancel: ()=>{
                    if(this.lineAdding){ 
                        this.lineAdding.parent.remove(this.lineAdding)
                        this.lineAdding = null
                        this.routeInfo.panoStart = null;
                    }
                },
                addLink: (id1,id2,line)=>{
                    this.routeInfo.lines[id1] || (this.routeInfo.lines[id1] = {})   
                    this.routeInfo.lines[id1][id2] = line
                    line.name = 'routeLine:'+ id1 + '->' +id2 
                    moveLine(line, player.model.panos.index[id1].floorPosition, player.model.panos.index[id2].floorPosition)
                },
                addLine: (startPos=new THREE.Vector3)=>{ 
                    let line = LineDraw.createLine([startPos,startPos], {mat: this.routeInfo.lineMat})  
                    //let arrow = this.routeInfo.arrow.clone()  
                    let s = 0.15
                    let arrowLeft = LineDraw.createLine([new THREE.Vector3, new THREE.Vector3(-s,0,s)], {mat: lineMat})  
                    let arrowRight = LineDraw.createLine([new THREE.Vector3, new THREE.Vector3(s,0,s)], {mat: lineMat})  
                    line.add(arrowLeft), line.add(arrowRight)
                    arrowLeft.position.copy(startPos), arrowRight.position.copy(startPos)
                    arrowLeft.renderOrder = arrowRight.renderOrder =  7
                    this.meshGroup.add(line) 
                    return line
                },
                removeLink: (id1,id2)=>{
                    this.routeInfo.lines[id1][id2].parent.remove(this.routeInfo.lines[id1][id2])
                    delete this.routeInfo.lines[id1][id2]
                }
                
            }
             
             
            
            
            //初始化绘制
            /* for(let id1 in player.model.panos.routeNextMap){
                for(let pano2 of player.model.panos.routeNextMap[id1]){
                    let line = this.routeInfo.addLine()
                    this.routeInfo.addLink(id1,pano2.id,line)
                }
            } */
            
            if(window.DATA.route){
                for(let id1 in window.DATA.route.data){
                    for(let id2 of window.DATA.route.data[id1]){
                        let line = this.routeInfo.addLine()
                        this.routeInfo.addLink(id1,id2,line)
                    }
                    
                }  
            }
             
        }else{
            for(let id1 in this.routeInfo.lines){
                for(let id2 in this.routeInfo.lines[id1]){
                    let line = this.routeInfo.lines[id1][id2] 
                    line.visible = true
                }
            }
        }
        
        player.on("view.changed",this.routeInfo.events.viewChange)
        player.domElement.addEventListener("pointermove",this.routeInfo.events.pointerMove)
        player.domElement.addEventListener("pointerup",this.routeInfo.events.click)
    },
    
    dealRouteClick: function(object){ 
        
        if(this.routeInfo.operation == 'addLink'){ 
            var id = object.name == '' ? object.parent.name : object.name  
            var panos = player.model.panos; 
            if(panos.index[id]){ 
                if(this.routeInfo.panoStart){
                    if(this.routeInfo.panoStart == panos.index[id])return
                     
                    this.routeInfo.addLink(this.routeInfo.panoStart.id, id, this.lineAdding)
                    //this.lineAdding = null
                    //this.routeInfo.panoStart = null;
                }
                //}else{
                    this.routeInfo.panoStart = panos.index[id]
                    this.lineAdding = this.routeInfo.addLine(this.routeInfo.panoStart.floorPosition)
                    this.routeInfo.updatePlane()
                       
                //} 
            }
        }else if(this.routeInfo.operation == 'removeLink'){
            let name = object.name || object.parent.name 
            if(name.slice(0,10) == 'routeLine:'){
                let id = name.slice(10).split('->');  
                this.routeInfo.removeLink(id[0], id[1])
            }
            
        }
    },
    
    finishSetRoute: function(){
        
        this.setRoute = false
        player.removeListener("view.changed",this.routeInfo.events.viewChange)
        player.domElement.removeEventListener("pointermove",this.routeInfo.events.pointerMove)
        this.changePanoVisi(false)
        permitTranMode(true)
        player.cameraControls.controls.panorama.insideLookLimitDown = null
        for(let i in player.model.hots){ 
            player.model.hots[i].mesh.visible = player.model.hots[i].visi_
        } 
         
        for(let id1 in this.routeInfo.lines){
            for(let id2 in this.routeInfo.lines[id1]){
                let line = this.routeInfo.lines[id1][id2]
                //line.parent.removeChild(line)
                line.visible = false
            }
        }
        
        this.routeInfo.cancel()
    },
    saveRoute : function(){
        
        let data = {}
        if(this.routeInfo){
            for(let id1 in this.routeInfo.lines){
                let ids = Object.keys(this.routeInfo.lines[id1])
                if(ids.length > 0)  data[id1] = Object.keys(this.routeInfo.lines[id1])
            }
        }else{
            return window.DATA.route  
        }
        
        return {
            data, 
            hide: $('#routeShowSwitch input').prop('checked') ? 0:1,
            gradualShow: $("#routeGradual").is(':checked') ? 1:0,
            opacityShine: $("#routeOpaShine").is(':checked') ? 1:0, 
        }
        //点击总保存按钮后才会保存，然后直接刷新
    },
     
    
    changePanoVisi:function(state, toFloor){
        var floor = player.model.allFloorsVisible ? 'all' : (toFloor || player.model.currentFloor)
        if(this.setPanoVisible){
            if(floor != 'all' && this.panoVsetting && floor != this.panoVsetting.floor){//如果切换到其他楼，取消选中该pano
                this.pauseSetPanoVisible('unsaved')
            }
        }
        player.model.panos.forEach(e=>{
            if(!e.footIcon)return
                
            if(state && !e.footIcon.visible && (floor == 'all' || floor == e.floor)){
                e.footIcon.visible = true
                if (this.setPanoLog)e.addTextSprite(e.id, $('#panoIdColorTex').val(), e.footIcon)
            }else if(!state || floor != 'all' && floor != e.floor){
                e.footIcon.visible = false
                if (this.setPanoLog)e.removeTextSprite()
            }
        })
    },
    
    finishSetPanoLog: function() {
        //结束 退出这个设置
        if (!this.setPanoLog)
            return;
        //否则会加多个侦听 
        this.changePanoVisi(false)
        this.setPanoLog = false;

        //this.hideFootIcons();
    
        this.recoverAllState2(); 
        this.panosSelect = null
        player.flyoutType = null
        
        permitTranMode(true)
        player.cameraControls.controls.panorama.insideLookLimitDown = null
        
        for(let i in player.model.hots){ 
            player.model.hots[i].mesh.visible = player.model.hots[i].visi_
        } 

        $("#panosIdShow").val('')
        
        
        
    },
    dealPanoLogClick: function(object) {
        var id = object.name == '' ? object.parent.name : object.name 
       
        var panos = player.model.panos; 
        
        var index = this.panosSelect.indexOf(id)
        if (index==-1) { 
            this.changeFIconState(panos.index[id].footIcon, "linked" )
            this.panosSelect.push(id)
        }else{
            this.changeFIconState(panos.index[id].footIcon,  false)
            this.panosSelect.splice(index,1)
        }
        
        
        $("#panosIdShow").val('"'+this.panosSelect.join('","')+'"');         
        
    }
    ,
    changePanoIdColor:function(color){ 
        player.model.panos.forEach(e=>{
            e.removeTextSprite();
            e.addTextSprite(e.id,color, e.footIcon)
        })  
    }
    ,
    //--------------
    beginSetPanoVisible: function() {
        player.flying || $(".toolLeft").removeClass("unable")
        if (this.setPanoVisible)
            return; 
        this.setPanoVisible = true;
        this.panoVTemp = {};

        this.SetOnePanoVisible(player.currentPano)
        //先设置currentPano 

        this.$confirmSnap.text('保存当前设置').removeClass("hide")

        //objects.tagManager.hideAllTags();

        this.setDisplay(true)
        this.changePanoVisi(true)

        this.updateFootIconSize()
        //更新一下大小，尤其是上次换了中心点然后退出又进入但是镜头没有变化的话 
        
        
        for(let i in player.model.hots){
            player.model.hots[i].visi_ = player.model.hots[i].mesh.visible;
            player.model.hots[i].mesh.visible = false
        }        
        
    },

    SetOnePanoVisible: function(pano) {
        //点击某个pano后就对该pano点进行设置 
        if (this.panoVsetting == pano)
            return;
        //if (this.panoVsetting) saveLastPanoVi(this.panoVsetting);

        this.panoVsetting = pano;
        //记录正在修改的
        this.delVisibleLines();
        //删除线
        this.showFootIcons(pano, true);
        this.createPanoVisiLines(pano);
        //创线
        
        this.changeBtn.removeClass('hide')
    },

    saveLastPanoVi: function(o={}) {
        //保存刚设置过的pano
        var change = [];
        
        /* if(o.type == 'autoCompute'){
            
        }else{ */
            for (var r in this.panoVLines) {
                var line = this.panoVLines[r];
                if (line.name.indexOf("new") > -1 && line.visible) {
                    //新设置为visible且没有取消 
                    change.push({
                        type: "add",
                        id: r
                    })

                } else if (line.name.indexOf("new") == -1 && !line.visible) {
                    //旧的且已经取消
                    change.push({
                        type: "sub",
                        id: r
                    })
                }
            }
        /* } */
        
        if(o.onlyGetData){
            return change
        }

        if (change.length) {
            //添加双向的neighbour:
            var self = this.searchNeib(this.panoVsetting.id)
            //var seeMarkers_self = self.seeMarkers;
            var neighbourUUIDs_self = self.neighbourUUIDs
            var neighbourPanos_self = self.neighbourPanos;
            for (var i = 0; i < change.length; i++) {
                var other = this.searchNeib(change[i].id)
                //var seeMarkers = other.seeMarkers;
                var neighbourUUIDs = other.neighbourUUIDs;
                var neighbourPanos = other.neighbourPanos;
                if (change[i].type == "add") {
                    //seeMarkers.push(this.panoVsetting.id);
                    neighbourUUIDs.push(this.panoVsetting.id);
                    neighbourPanos[this.panoVsetting.id] = true;

                    //seeMarkers_self.push(change[i].id);
                    neighbourUUIDs_self.push(change[i].id);
                    neighbourPanos_self[change[i].id] = true;

                } else {
                    //var index = seeMarkers.indexOf(this.panoVsetting.id);
                    //index > -1 && seeMarkers.splice(index, 1);
                    var index = neighbourUUIDs.indexOf(this.panoVsetting.id);
                    index > -1 && neighbourUUIDs.splice(index, 1);
                    neighbourPanos[this.panoVsetting.id] = false;

                    //var index = seeMarkers_self.indexOf(change[i].id);
                    //index > -1 && seeMarkers_self.splice(index, 1);
                    var index = neighbourUUIDs_self.indexOf(change[i].id);
                    index > -1 && neighbourUUIDs_self.splice(index, 1);
                    neighbourPanos_self[change[i].id] = false;
                }

                this.panoVTemp[change[i].id] = {
                    //后面两个是作为保存到后台的数据存储，临时需要用到的是第一个
                    neighbourPanos: neighbourPanos,
                    //seeMarkers: seeMarkers,
                    neighbourUUIDs: neighbourUUIDs
                }
            }

            this.panoVTemp[this.panoVsetting.id] = {
                //加上自己
                neighbourPanos: neighbourPanos_self,
                //seeMarkers: seeMarkers_self,
                neighbourUUIDs: neighbourUUIDs_self
            }

        }
    },

    pauseSetPanoVisible: function(type) {
        //暂停 因为点击了保存设置  但没有退出设置
        if (!this.setPanoVisible)
            return;
        if (type == "unsaved") {
            //中途点击pano从而停止一个热点的设置
            this.saveLastPanoVi();
        } else {
            this.panoVTemp = {};
            //清空数据 
        }
        this.delVisibleLines();
        this.showFootIcons();
        //清空选择
        var lastPanoSetting = this.panoVsetting;
        this.panoVsetting = null;
        lastPanoSetting && this.changeFIconState2(lastPanoSetting.footIcon, this.checkHasNeighbor(lastPanoSetting))
        //这句要放在this.panoVsetting = null后。 根据可见性更改透明度
        this.changeBtn.addClass('hide')
    },

    //按理说改变了neighbourPano，tag的初始visible也要改。但是这样还要考虑已经改过的tag。。很麻烦
    finishSetPanoVisible: function() {
        //结束 退出这个设置
        if (!this.setPanoVisible)
            return;
        //否则会加多个侦听 
        this.setPanoVisible = false;

        //this.hideFootIcons();
        this.delVisibleLines();

        //objects.tagManager.showAllTags();
        this.recoverAllState2();
        this.panoVsetting = null;
        this.panoVTemp = {};
        player.flyoutType = null
        this.$confirmSnap.addClass("hide")
        permitTranMode(true)
        player.cameraControls.controls.panorama.insideLookLimitDown = null
        this.setDisplay(false)
        this.changePanoVisi(false)
        for(let i in player.model.hots){ 
            player.model.hots[i].mesh.visible = player.model.hots[i].visi_
        }        
        
    },
    changeVisiBtnState: function(state){ 
        this.changeBtn.attr("function", state ? "hide" : "show")
        this.changeBtn.html(state ? "隐藏该点位置":"显示该点位置") 
    },
    recoverAllState2: function() {
        //为了热点可视恢复成pano全部可见
        if(!this.footIcons)return;
        for (var i = 0; i < this.footIcons.length; i++) {
            this.footIcons[i].material.uniforms.opacity.value = 1;
            this.footIcons[i].material.uniforms.map.value = footTex1;
        }
    },

    afterSavePanoVisibles: function() {
        //实施：

        var panos = player.model.panos;
        for (var i in this.panoVTemp) {
            var pano = panos.index[i];
            //pano.seeMarkers = this.panoVTemp[i].seeMarkers;
            pano.neighbourUUIDs = this.panoVTemp[i].neighbourUUIDs;
            pano.neighbourPanos = this.panoVTemp[i].neighbourPanos;

        }

        if (!this.checkHasNeighbor(player.currentPano)) {
            //currentPano变为孤立点  就要换一个防止飞入
            var list = panos.sortByScore([pano=>{
                return this.checkHasNeighbor(pano)
            }
            ], [function(pano) {
                return -pano.position.distanceTo(player.currentPano.position)
            }
            ])
            if (list && list.length) {
                player.currentPano = list[0].pano;
                //找最近的一非孤立点
                //this.noPanoHasNeighbor = false; //更新状态
            } else {//this.noPanoHasNeighbor = true; //更新状态
            }
        } else {//this.noPanoHasNeighbor = false; //更新状态
        }

        //dataDeal.done();
        //暂时：
        this.pauseSetPanoVisible()

        this.updateFootIconSize()
        //更新一下center大小  写在最后

    },

    //最佳推荐操作顺序： 先设置pano可见性 再创建热点   这样热点的visible正确些，否则之后再设置热点可见性会改更多
    savePanoVisibles: async function() {
        //保存

        if (this.panoVsetting)
            this.saveLastPanoVi(this.panoVsetting);
        //获取最后设置的那个热点的改动

        var PanoData = [];
        for (var i in this.panoVTemp) {
            PanoData.push({
                //希望算法部不会更改index排序，或者更改后能将visible信息一并更改
                panoID: i,
                //visibles: this.turnToPanoIndex(this.panoVTemp[i].seeMarkers),
                visibles3: this.turnToPanoIndex(this.panoVTemp[i].neighbourUUIDs)
            })
        }

        if (PanoData.length == 0) {
            //没改变
            alert("保存成功")
            return;
        }

        /* this.afterSavePanoVisibles()
        alert("保存成功")
        console.log(JSON.stringify(PanoData)) */

        let url = cmp ? ('/api/scene/roamViable/' + cmp) : '/manage/scene/roamViable'
        return new Promise((resolve,reject)=>{
            $.ajax({
                method: 'POST',
                url: ceshi + url,
                headers: {
                    'Content-Type': 'application/json',
                    token: token
                },
                contentType: 'application/json',
                data: JSON.stringify({
                    data: JSON.stringify(PanoData),
                    sceneCode: window.number
                }),
                success: (data)=>{
                    if (data.code === 0) {
                        this.afterSavePanoVisibles()
                        alert("保存漫游可行成功")
                    } else
                        alert("保存漫游可行失败")
                    if (data.code === 5001) {
                        alert('请重新登录')
                        localStorage.dcj_token = ''
                        location.reload()
                    }
                    resolve()
                }
                ,
                fail: function() {
                    alert("保存漫游可行失败")
                    reject()
                }
            })
        })
    },

    searchNeib: function(panoId) {
        //寻找某pano的相关neighbour 可能是修改过的
        var panos = player.model.panos;
        var o = {};
        if (this.panoVTemp[panoId]) {
            //o.seeMarkers = this.panoVTemp[panoId].seeMarkers;
            o.neighbourUUIDs = this.panoVTemp[panoId].neighbourUUIDs;
            o.neighbourPanos = this.panoVTemp[panoId].neighbourPanos;
        } else {
            //o.seeMarkers = panos.index[panoId].seeMarkers.slice(0);
            o.neighbourUUIDs = panos.index[panoId].neighbourUUIDs.slice(0);
            o.neighbourPanos = CloneObject(panos.index[panoId].neighbourPanos);
        }
        return o;
    },

    turnToPanoIndex: function(panoArr) { 
        var array = [];
        for (var i = 0; i < panoArr.length; i++) {
            var pano = player.model.panos.index[panoArr[i]]//可能undefined，只是存在在panoListOriData中，因在this.data.sweeps[e.uuid].enabled是false
            if(pano){
                var index = window.panoListOriData.indexOf(pano.id)
                array.push(index);
            }
        }
        return array;
    },

    setDisplay: function(state) {
        var panos = player.model.panos;
        if (state) {
            this.$confirmSnap.text('保存当前设置');
            this.$confirmSnap.removeClass('hide');
        } else {
            this.$confirmSnap.addClass('hide');
            this.changeBtn.addClass('hide')
        }
 
        player.path.currentPanoMarker.mesh.visible = !state; 
        player.reticule.visible = !state;
        
    },

    delVisibleLines: function() {
        //xzw add  所有线都删除
        for (var i in this.tagsVLines) {
            this.tagsVLines[i].geometry.dispose();
            this.tagsVLines[i].material.dispose();
            this.meshGroup.remove(this.tagsVLines[i]);
            delete this.tagsVLines[i];
        }
        for (var i in this.panoVLines) {
            this.panoVLines[i].geometry.dispose();
            this.panoVLines[i].material.dispose();
            this.meshGroup.remove(this.panoVLines[i]);
            delete this.panoVLines[i];
        }
    },
    //--------panoVisible
    createPanoVisiLines: function(pano) {
        //  pano可见性线条 
        var neighbours = this.panoVTemp[pano.id] && this.panoVTemp[pano.id].neighbourPanos || pano.neighbourPanos;

        for (var r in neighbours) {
            if (neighbours[r] && r != pano.id) {
                this.createPanoSingleLine(pano, "old", r)
            }
        }

    },

    createPanoSingleLine: function(pano, type, id) {
        //pano是中心
        var panos = player.model.panos;
        var p2 = panos.index[id].footIcon.position.clone()
        /* .sub(player.model.position) */
        var line = LineDraw.createLine([pano.footIcon.position.clone()/* .sub(this.position) */
        , p2], {
            color: this.colors.green
        });
        this.meshGroup.add(line);
        line.name = "PanoVL-" + type + "-" + id;

        this.panoVLines[id] = line;
        this.changeFIconState(panos.index[id].footIcon, "linked")
    },

    dealPanoVisible: function(id) {
        //外部调用
        var panos = player.model.panos;
        if (this.panoVsetting) {
            if (id == this.panoVsetting.id) {
                //关闭当前pano设置
                this.pauseSetPanoVisible('unsaved')
            } else {
                var link;
                //结果是否连接
                if (this.panoVLines[id]) {
                    this.panoVLines[id].visible = !this.panoVLines[id].visible;
                    link = this.panoVLines[id].visible;
                    this.changeFIconState(panos.index[id].footIcon, this.panoVLines[id].visible ? "linked" : false)
                 
                } else {
                    this.createPanoSingleLine(this.panoVsetting, "new", id)
                    link = true;
                }
                if (link) {
                    //如果连接上了，直接判断该点是可见的（有附近点），（不能通过checkHasNeighbor来判断，因为新增的线条可能不在它的neighbour中
                    this.changeFIconState2(panos.index[id].footIcon, true)
                    this.changeFIconState2(panos.index[this.panoVsetting.id].footIcon, true)
                } else {
                    //否则需要checkHasNeighbor
                    this.changeFIconState2(panos.index[id].footIcon, this.checkHasNeighbor(panos.index[id]))
                    this.changeFIconState2(panos.index[this.panoVsetting.id].footIcon, this.checkHasNeighbor(this.panoVsetting))
                }
                
                
                /* if(window.routeArray){//箭头路线
                    if(this.panoVLines[id].visible)window.routeArray.push(id)
                    else{
                        let index = window.routeArray.indexOf(id)
                        index > -1 && routeArray.splice(index, 1)  
                    }
                
                }  */
                
                
            }
        } else {
            //点击开始设置要设置的pano
            this.SetOnePanoVisible(panos.index[id])
        }
        this.updateFootIconSize()

    },

    showFootIcons: function(pano, isPanovisible) {
        if (!this.footIcons) {
            footTex1 = Texture.load("images/edit/End_128.png"); 
            footTex2 = Texture.load("images/edit/End_unable_128.png");
            footTex1.anisotropy = footTex2.anisotropy = 4
            this.footIcons = [];
            var scale = 0.4;

            scale *= 40 / Math.sqrt(Math.min($("#player").width(), $("#player").height()));
            //屏幕越小，放得越大
            scale = THREE.Math.clamp(scale, 0.3, 0.7)

            //console.log("scale"+scale)
            var geo = new THREE.PlaneGeometry(scale,scale,1,1)
            var panos = player.model.panos;
            for (var r in panos.index) {
                if (!panos.index[r].isAligned())
                    continue;
                var t = THREE.UniformsUtils.clone(shaders.waypoint.uniforms);
                t.map.value = footTex1
                t.color.value.set("#ffffff");
                var mat = new THREE.RawShaderMaterial({
                    vertexShader: shaders.waypoint.vertexShader,
                    fragmentShader: shaders.waypoint.fragmentShader,
                    uniforms: t,
                    side: THREE.DoubleSide,
                    transparent: !0,
                    depthWrite: !1,
                    depthTest: false,

                    name: "footIcon"
                })
                var foot = new THREE.Mesh(geo,mat)
                foot.position.copy( panos.index[r].floorPosition.clone()  )    /* panos.index[r].floorPosition.clone()  *///不用floorPosition是因为有出现和position的水平位置不一样的点，一般是偏移很远的不可见点，拍摄的bug
                
                foot.lookAt(foot.position.clone().add(new THREE.Vector3(0,1,0)));
                foot.name = panos.index[r].id;
                foot.visible = false;
                foot.renderOrder = 6,
                panos.index[r].footIcon = foot;
                this.meshGroup.add(foot);
                this.footIcons.push(foot)
            }
        }
        
        for (var i = 0; i < this.footIcons.length; i++) { 
            this.changeFIconState(this.footIcons[i], false)
            var panos = player.model.panos;
            if (isPanovisible) {
                this.changeFIconState2(this.footIcons[i], this.checkHasNeighbor(panos.index[this.footIcons[i].name], "showFoot"))
            }

            if (pano && (this.footIcons[i].name == pano.id)) {
                //pano为中心 或者 currentPano  所以放大一点 
                this.footIcons[i].oriScale = new THREE.Vector3(1.5,1.5,1.5)
                if (isPanovisible) {
                    //currentPano特殊些： 
                    this.changeFIconState(this.footIcons[i], "center")
                    //this.footIcons[i].Unclick = true;//不让点击和hover
                }
            } else {
                this.footIcons[i].oriScale = new THREE.Vector3(1,1,1)
            }
        }
    },

    checkHasNeighbor: function(pano, state) {
        //检查当前状态pano点是否有可通行点
        var neighbours = /* this.panoVTemp &&  */
        this.panoVTemp[pano.id] ? this.panoVTemp[pano.id].neighbourPanos : pano.neighbourPanos;

        if (state != "showFoot" && pano == this.panoVsetting) {
            //是中心点的话。state == "showFoot"代表是showFootIcon时， 这时候线还没创建，无法用线判断中心点有几个相邻点，直接用neighbourPanos
            for (var i in this.panoVLines) {
                if (this.panoVLines[i].visible) {
                    return true;
                    //有一条线即可
                }
            }
            return;
        }

        for (var i in neighbours) {
            if (i == pano.id)
                continue;
            if (neighbours[i]) {
                if (this.panoVsetting && this.panoVsetting.id == i && this.panoVLines[pano.id] && !this.panoVLines[pano.id].visible)
                    continue;
                return true;
            }
        }
        return false;
    },

    /* ifAllPanoNoNeighbor : function(){//检查是否全是孤立点
        var panos = player.model.panos;
		for(var i in panos.index){
			if(this.checkHasNeighbor(panos.index[i])){
				return false;
			}
		}
		this.noPanoHasNeighbor = true;
		return true;//是全部没有neighbour
	},  */

    changeFIconState: function(footIcon, state) {
        var color = state == "linked" ? this.colors.green : (state == "center" ? /* "#d7f244" */
        "#d5f12e" : "#ffffff");
        footIcon.material.uniforms.color.value.set(color)
    },

    changeFIconState2: function(footIcon, state) {
        //是可见点还是不可见点
        if (state) {
            footIcon.material.uniforms.map.value = footTex1;
            if (this.panoVsetting) {
                if (this.panoVsetting.id != footIcon.name) {
                    footIcon.material.uniforms.opacity.value = 1;
                } else {
                    this.changeVisiBtnState(true)
                }
            }
        } else {
            //不可见
            footIcon.material.uniforms.map.value = footTex2;
            if (!this.panoVsetting || this.panoVsetting.id != footIcon.name) {
                //非中心点时
                footIcon.material.uniforms.opacity.value = 0.5;
            } else {
                //变为中心点时
                footIcon.material.uniforms.opacity.value = 1;
                this.changeVisiBtnState(false)
            }
        }
    },

    hideFootIcons: function() {
        if (!this.footIcons)
            return;
        for (var i = 0; i < this.footIcons.length; i++) {
            this.footIcons[i].visible = false;
        }
    },

    updateFootIconSize: function() {
        //根据相机位置 改变footIcon大小，使在范围内看到的热点大小一致，防止太小点击不到
        if (!this.footIcons)  return;
        
        if(player.mode == 'floorplan'){
            var s = player.cameraControls.controls.floorplan.absoluteScale *  2.8
            s = THREE.Math.clamp(s, 0.5 * this.footIconSizeRatio, 1.4 * this.footIconSizeRatio);
            this.footIcons.forEach(function(f) {
                try { 
                    f.scale.copy(f.oriScale).multiplyScalar(s); 
                } catch (e) {
                    console.log(e)
                }
            })  
        }else{
            let maxDis = 7, maxScale = 2 * this.footIconSizeRatio,   k = maxScale / maxDis 
            this.footIcons.forEach(function(f) { 
                let s
                let dis = f.position.distanceTo(player.position)
                if(dis >= maxDis) s = maxScale
                else s = dis * k 
                f.scale.copy(f.oriScale).multiplyScalar(s);
                //f.quaternion.copy(player.camera.quaternion)
            })
        } 
    },
    
    
    
    resetPanosVisiByModel: function() {//一键计算所有漫游可行 (计算后自动连接，但不保存) 多楼层还没试过
     
        $('.waiting').addClass('showloading');
        $('.pinBottom  .allFloorBtn').click()//全部楼层显示
        let begin = ()=>{
            this.pauseSetPanoVisible("unsaved")
            
        
            var ifBlock = function(panoA, panoB ){
                var A = panoA.position.clone();
                var B = panoB.position.clone(); 
                return !!convertTool.ifIntersectChunks(A, B, {})
            }
            
                    
            player.model.panos.list.forEach(pano1=>{
                this.SetOnePanoVisible(pano1)//开始设置pano1
                
                player.model.panos.list.forEach(pano2=>{
                    if(pano1 == pano2)return
                     
                    var visiNew = !ifBlock(pano1,pano2)
                    var visiOld = !!(this.panoVLines[pano2.id] && this.panoVLines[pano2.id].visible)
                    if(visiNew != visiOld){
                        //console.log('修改 '+pano1.id+'-'+pano2.id)
                        this.dealPanoVisible(pano2.id)//修改
                    }
                    
                })
                this.pauseSetPanoVisible('unsaved')//修改pano1结束
            })
            
            $('.waiting').removeClass('showloading');
            manage.showInfo("修改成功，可以点击漫游点查看。点击保存当前设置后生效。")
        }
        
        setTimeout(()=>{
            if(this.setPanoVisible){
                begin()
            }
        },100)
    },
    
    
    
    
    
    //========热点可见性==============  
    
    beginSetTagVisible: function() {
        if (this.setTagVisible)
            return;
        $(".toolTop").addClass("unable")
        $("#hotVisible button").addClass("unable")
        $("#hotVisible label").removeClass('hide')
        
        
        this.setTagVisible = true;
        this.tagVTemp = {};

        this.$confirmSnap.text('完成设置').removeClass("hide")

        //objects.tagManager.hideAllTags();
        for (let i in player.model.hots) {
            player.model.hots[i]._isSprite = player.model.hots[i].info.isSprite;
            player.model.hots[i].info.isSprite = true;
            player.model.hots[i].material_.depthTest = false;
            
            player.model.hots[i].position.copy(player.model.hots[i].info.position);//覆盖transformAtPanos
            player.model.hots[i].scale.copy(player.model.hots[i].info.scale);//覆盖transformAtPanos
             
            player.model.hots[i].update(player)
        }
        this.setDisplay(true)
        this.updateFootIconSize()
        //更新一下大小，尤其是上次换了中心点然后退出又进入但是镜头没有变化的话 
    },
    SetOneTagVisible: function(tag) {
        //点击某个热点后就对该热点进行设置，或者在热点修改时对其进行设置
        if (this.tagVsetting == tag)
            return;
        if (this.tagVsetting) {
            this.saveLastTagVi(this.tagVsetting);
            //this.tagVsetting.setElemType(this.tagVsetting.style, this.tagVsetting.styleImageURL); 
        }

        this.tagVsetting = tag; //记录正在修改的
        this.delVisibleLines(); //删除线
        this.showFootIcons(player.currentPano);//显示、修改pano颜色
        this.changePanoVisi(true)
        this.createTagVisiLines(tag); //创线

        this.updateFootIconSize() //更新一下大小，尤其是上次换了中心点然后退出又进入但是镜头没有变化的话
    },
    saveLastTagVi: function(o={}) {
        //保存刚设置过的tag
        var change = false;
        var newVPs = this.tagVTemp[this.tagVsetting.sid] || this.tagVsetting.info.visiblePanos.slice(0);
        for (var r in this.tagsVLines) {
            var line = this.tagsVLines[r];
            if (line.name.indexOf("new") > -1 && line.visible) {
                //新设置为visible且没有取消  
                newVPs.push(r)
                change = true;
                //console.log("add: "+r)
            } else if (line.name.indexOf("new") == -1 && !line.visible) {
                //旧的且已经取消
                var i = newVPs.indexOf(r);
                if (i == -1) {
                    console.log("visiblePanos删除error");
                    continue;
                }
                newVPs.splice(i, 1);
                change = true;
                //console.log("sub: "+r)
            }
        }
        if(o.getIfChange){
            return change
        }
        if (change) {
            this.tagVTemp[this.tagVsetting.sid] = newVPs;
        }
    },
    pauseSetTagVisible: function() {
        //pc保存后删除连线 但还在继续设置 点选热点即开始
        if (!this.setTagVisible || !this.tagVsetting)
            return;
        //this.tagVsetting.setElemType(this.tagVsetting.style, this.tagVsetting.styleImageURL ); 
        this.delVisibleLines();
        //this.hideFootIcons();
        this.changePanoVisi(false)
        this.tagVTemp = {};
        this.tagVsetting = null;
    },
    finishSetTagVisible: function() {
        if (!this.setTagVisible)
            return;
        $(".toolTop").removeClass("unable")
        $("#hotVisible button").removeClass("unable")
        $("#hotVisible label").addClass('hide')
        this.pauseSetTagVisible();
        this.setTagVisible = false;

        this.setDisplay(false)
        this.$confirmSnap.addClass("hide")
        permitTranMode(true)
        player.cameraControls.controls.panorama.insideLookLimitDown = null
        /* for (var r in objects.tagManager.tags) {
            if(objects.tagManager.tags[r].state == "videoPanoFlag")continue;            
            objects.tagManager.tags[r].disc.visible = false;
			objects.tagManager.tags[r].disc.material.depthTest = true; 
		} */
        for (let i in player.model.hots) {
            player.model.hots[i].info.isSprite = player.model.hots[i]._isSprite;
            player.model.hots[i].info.isSprite || player.model.hots[i].quaternion.copy(player.model.hots[i].info.quaternion)
            player.model.hots[i].material_.depthTest = true;
        }
    },

     
    saveTagVisibles: function() {
        if (this.tagVsetting)
            this.saveLastTagVi(this.tagVsetting);
        for (var i in this.tagVTemp) {
            //保持成功于是生效   
            player.model.hots[i].setVisiblePanos(this.tagVTemp[i]) 
        }
        this.finishSetTagVisible()
        //还是保存完直接结束吧，因为现在热点可视不放在单独的设置页面了
    },
    createTagVisiLines: function(tag) {
        //  热点可见性线条
        var panos = player.model.panos; 

        var visibleList = this.tagVTemp[tag.sid] || tag.info.visiblePanos;
        //如果是刚在设置的要读取设置过的数据
        for (var r = 0; r < visibleList.length; r++) {
            var pano = panos.index[visibleList[r]];
            this.createTagSingleLine(pano, "old", tag)
        }
    },
    createTagSingleLine: function(pano, type, tag) {
        var panos = player.model.panos;
        var line = LineDraw.createLine([pano.footIcon.position.clone()/* .sub(this.position) */
        , tag.info.position.clone()], {
            color: this.colors.green
        });
        this.meshGroup.add(line);
        line.name = "tagVL-" + type + "-" + pano.id;
        this.tagsVLines[pano.id] = line;
        this.changeFIconState(panos.index[pano.id].footIcon, "linked")

    },
    dealTagVisible: function(tag, panoName) {
        //外部调用
        var panos = player.model.panos;
        if (this.tagsVLines[panoName]) {
            this.tagsVLines[panoName].visible = !this.tagsVLines[panoName].visible;
            this.changeFIconState(panos.index[panoName].footIcon, this.tagsVLines[panoName].visible ? "linked" : false)
        } else {
            this.createTagSingleLine(panos.index[panoName], "new", tag)
        }
    },
    delVisibleLines: function() {
        //xzw add  所有线都删除
        for (var i in this.tagsVLines) {
            this.tagsVLines[i].geometry.dispose();
            this.tagsVLines[i].material.dispose();
            this.meshGroup.remove(this.tagsVLines[i]);
            delete this.tagsVLines[i];
        }
        for (var i in this.panoVLines) {
            this.panoVLines[i].geometry.dispose();
            this.panoVLines[i].material.dispose();
            this.meshGroup.remove(this.panoVLines[i]);
            delete this.panoVLines[i];
        }
    },
    //------------
    unsaveWarn:async function(done){
        if(VisiSet.setPanoVisible && (VisiSet.panoVsetting && VisiSet.saveLastPanoVi({onlyGetData:true}).length || Object.keys(VisiSet.panoVTemp).length)){//修改过
            var sure = confirm("是否保存已编辑的漫游可视？")
            if(sure){
                await VisiSet.savePanoVisibles()
            } 
            done && done() 
        }
    }
    
    
    
    
    /* resetTagVisiByModel : function(){//自动计算所有热点的可视  当模型修改后所有的热点可视都会重新自动计算（用户的设置将被覆盖）
		var visiTags = [];
		for(var i in objects.tagManager.tags){
			var tag = objects.tagManager.tags[i];
			if(tag.state == "videoPanoFlag")continue;
			var visiblePanos = tag.getVisiblePanos(); 
			visiTags.push({sid:tag.sid, value:visiblePanos})
		}
        
        return visiTags;
		
        
    },
    afterResetTagVisibles : function(visiTags){
        visiTags.forEach((info)=>{
            objects.tagManager.tags[info.sid].setVisiblePanos(info.value); 
        })
        if(objects.player.mode == "panorama")objects.tagManager.updateVisible("panorama");
        
    },
	resetVisiblesByModel : function(){//自动计算所有热点和漫游点的可视
	 
		this.resetTagVisiByModel()
	 
	} */

}



 








function permitTranMode(state ) {
    if(state instanceof Array){//仅显示state包含的模式
        $(".pinBottom.left").removeClass('hide')
        $(".pinBottom.left .viewContainer>*:not(#gui-modes-map)").addClass('hide');
        $(".pinBottom.left #gui-modes-map>*").addClass('hide');
        //注：panorama需转为inside
        state.forEach(m=> $(".pinBottom.left #gui-modes-"+m).removeClass('hide'))
        player.roomLebelClickUnabled = true
    }else{
        if(state){
            player.roomLebelClickUnabled = false
            $(".pinBottom.left").removeClass('hide'),
            $(".pinBottom.left .viewContainer>*").removeClass('hide') ,
            $(".pinBottom.left #gui-modes-map>*").removeClass('hide') 
        }else{
            player.roomLebelClickUnabled = true
            $(".pinBottom.left").addClass('hide');
        } 
    }
    //一旦存在有隐藏的模式，必定会隐藏导览等其他按钮。
}



function randomWord(randomFlag, min, max) {
    //随机字符串
    var str = ""
      , range = min
      , arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];

    if (randomFlag) {
        // 随机长度
        range = Math.round(Math.random() * (max - min)) + min;
    }
    for (var i = 0; i < range; i++) {
        var pos = Math.round(Math.random() * (arr.length - 1));
        str += arr[pos];
    }
    return str;
}

function getRandomSid() {
    //5-7位随机字符串 + 6位时间    为热点准备
    var pre = randomWord(true, 5, 7);
    var post = new Date().getTime() + "";
    var len = post.length;
    post = post.substring(len - 8, len - 5) + post.substring(len - 3, len)
    //其实还是有可能重复的....
    return pre + post;
}

function searchParent(searchArea, o, maxTimes) {
    //是否是某种元素的下级，并找出该元素
    maxTimes = maxTimes || 20;
    var count = 0
    var f = function(dom) {
        if (o.id && o.id == dom.id)
            return dom
        else if (o.className && dom.classList && dom.classList.contains(o.className))
            return dom
        else if (o.tagName && dom.tagName && o.tagName.toUpperCase() == dom.tagName.toUpperCase())
            return dom
    }
    var find;
    while (searchArea && count < maxTimes) {
        if (find = f(searchArea))
            return find
        searchArea = searchArea.parentNode;
        count++;
    }

}


function musicPlayBoxBind($dom, addCallback, delCallback){
    var musicBox = {
        hasMusic:false,
        show: function(/* mediaDom,  */name, url){ 
            /* mediaDom = mediaDom || $dom
            mediaDom.find(".innerBtn").text("替换");
            mediaDom.find(".playBox").removeClass("hide");
            mediaDom.find(".title").text(name);
            mediaDom.find('a').attr('href', url); 
            musicBox.hasMusic = true */
            $dom.find(".innerBtn").text("替换");
            $dom.find(".playBox").removeClass("hide");
            $dom.find(".title").text(name);
            $dom.find('a').attr('href', url); 
            musicBox.hasMusic = true 
            
        },
        addMusic : function(e) {
            if(!e.target.files.length) return;
            var file = e.target.files[0];
            
            if (!/audio\/\w+/.test(file.type)) {
                alert("文件必须为音乐！");
                return false;
            }
            //限制大小不大于8m
            if (!restrictedSize(file, _musicMaxWeight)) {
                return false
            } 
            //var mediaDom = $(searchParent(e.target, { className: 'mediaUpload' })); 
            musicBox.show(/* mediaDom,  */file.name, URL.createObjectURL(file))
            
            addCallback && addCallback(file)
        },
        
        delMusic:function(){//手动点击删除
            musicBox.hide()
            delCallback && delCallback() 
        },
        
        hide : function(e) {//清空并隐藏
            //var mediaDom = e ? $(searchParent(e.target, { className: 'mediaUpload' })) : $dom
            
            $dom.find('a').attr('href', "");
            $dom.find(".playBox").addClass("hide");
            $dom.find(".innerBtn").text("上传");
            $dom.find("input").val("")//.removeAttr("data-hotbgm");
            musicBox.hasMusic = false
            
        },
        
        getSrc:function(){
            return this.hasMusic && $dom.find('a').attr('href'); 
        },
        getName:function(){
            return this.hasMusic && $dom.find(".title").text();
        },
        getFile:function(){//获取刚上传的文件
            return this.hasMusic && $dom.find("input")[0].files[0]
        } 
    }
    
    $dom.find("input").on("change", musicBox.addMusic)
    
    /* $dom.find("input").on("click", function() {
        mediaUpload
    });  */
    //删除音乐 
    $dom.on("click", ".delete", musicBox.delMusic)
    
    
    return musicBox;
}



class ListBox{//下拉列表
    //elemArr：初始的列表，内含每项的dom.
    constructor(elemArr, $rootDom,   options={}){
        
        this.listDom = $rootDom.find('ul.list')
        this.listTitle = $rootDom.find('.selection')
        
        this.chosenItem = null
        this.options = options;
         
        this.bindEvents()
        
        elemArr.forEach(($dom, i)=>{
            this.addItem($dom)
        })
        
    }
    updateSelectDisplay(o){
        this.listDom.children().removeClass('selected')
        this.chosenItem && this.chosenItem.addClass("selected")
        this.options.selectFun( o)
        
    }
    selectFromOutSide(index , o){ 
        if(index == null){
            var defaultItem = this.listDom.find('.forbitEdit');  //若存在默认选项
            if(defaultItem.length){
                index = defaultItem.index()
            }
        } 
             
    
        this.chosenIndex = index;
        this.chosenItem = index == void 0 ?  null : this.listDom.children().eq(index)
        this.updateSelectDisplay(o)
    }
    
    addItem($dom){ 
        this.listDom.prepend($dom)
        if(this.options.addFun){
            this.options.addFun($dom)
        } 
        
        $dom.on('click',()=>{
            this.chosenItem = $dom;
            this.chosenIndex = $dom.index();
            this.updateSelectDisplay() 
        })
    }
    
    removeItem($dom){
        $dom.remove()
        if(this.chosenItem && this.chosenItem[0] == $dom[0]){
            this.selectFromOutSide(null)
            //this.chosenItem = this.chosenIndex = null
        }
        if(this.options.delFun){
            this.options.delFun($dom)
        }  
    }
    
    saveTemp(){//编辑热点时保存一份副本
        this.temp = Array.from(this.listDom.children()).reverse().map((li)=>{
            return li.outerHTML
        })  
        this.options.saveTemp && this.options.saveTemp(this)
         
    }
    recover(){//取消编辑时恢复
       
        this.listDom.html('')
        this.temp.forEach(html=>{
            this.addItem($(html));
        })
        this.options.recover && this.options.recover(this)
        this.chosenItem = this.chosenIndex = null
        //this.selectFromOutSide(this.chosenIndex)
    }
    
    bindEvents(){
        this.listTitle.on('click',(e)=>{
            e.stopPropagation()
            this.listDom.toggleClass('hide') 
        })
        document.addEventListener('click',()=>{
            this.listDom.addClass('hide') 
        })
    }
}


class ButtonBase{
    constructor(o={}){
        this.dom = o.dom
        this.uiCallBack = o.uiCallBack  
        this.callbackWhenChose = o.callbackWhenChose //只有点击选择才会触发
    }
}


class MenuOptions extends ButtonBase{
    constructor(o={}){
        super(o)
        this.dom.find("li").on("click", (e)=>{
            var $elem = $(e.target); 
            this.updateChoseAtUI({$li:$elem}) 
            this.callbackWhenChose && this.callbackWhenChose({$li:$elem})
        })            
    }
       
    
    updateChoseAtUI(o={}){
        this.dom.find('li').removeClass("chosen");
        if(o.$li){ 
            o.$li.addClass("chosen"); 
        }else{
            this.dom.find("li[index="+o.name+"]").addClass("chosen");
             
        }
        this.uiCallBack && this.uiCallBack(o)
        
    }
    
    getSelectName(){
        return this.dom.find('li.chosen').attr('index')
    }
}








class CheckBox extends ButtonBase{ 
    constructor(o={}){
        super(o)
        this.dom.on("change",(e)=>{ 
            var name = $(e.target).attr('name')
            this.uiCallBack && this.uiCallBack(e.target.checked, name) 
            this.callbackWhenChose && this.callbackWhenChose(e.target.checked, name)
        })
    }
    
    updateChoseAtUI(checked, name){
        var dom  
        if(name) dom = this.dom.filter('[name='+name+']')[0]
        else dom = this.dom[0]
        dom.checked = checked   //this.dom.prop("checked") 
        
        this.uiCallBack && this.uiCallBack(checked, name) 
    }
    
    checked(name){
        var dom  
        if(name) dom = this.dom.filter('[name='+name+']')[0]
        else dom = this.dom[0]
        return dom.checked 
    }
}


function setDraggable(o={}){ 
    let findTarget = (event)=>{
        let target; 
        for(let name of o.dragItemClassName){
            let tar = searchParent(event.target, { className: name }, o.maxDepth || 7); 
            if(tar){
                target = tar; break;
            }
        }
        return target
    }
    
    
    let draging = null;
    let dragStart = (event)=> {
        event.dataTransfer.setData("te", event.target.innerText);  //不能使用text，firefox会打开新tab
        draging = findTarget(event);
    }
    
    
    let dragOver = (event)=>{
        event.preventDefault();
        
        
        let target = findTarget(event); 
        
 
        
        // 判断dragover是否发生在LI元素上
        if (target && target !== draging ) {
            let dragingIndex = $(draging).index()//draging.querySelector('#index');
            let targetIndex = $(target).index()//target.querySelector('#index'); 
            
            
            var targetRect = target.getBoundingClientRect();
            var dragingRect = draging.getBoundingClientRect();
            if (target && target.animated) {
                return;
            }

            let ulElem = target.parentNode;
            // 寻找到 ul节点
            if (getIndex(draging) < getIndex(target)) {
                //dragingIndex.innerText = [targetIndex.innerText, targetIndex.innerText = dragingIndex.innerText][0];
                // 交换两个节点的序号
                
                if(event.clientY<targetRect.top+targetRect.height*0.3)return
                ulElem.insertBefore(draging, target.nextSibling);
                //dragingIndex.innerText = [targetIndex.innerText, targetIndex.innerText = dragingIndex.innerText][0];// 交换两个节点的序号
                 
            } else {
                //dragingIndex.innerText = [targetIndex.innerText, targetIndex.innerText = dragingIndex.innerText][0];
                if(event.clientY>targetRect.bottom-targetRect.height*0.3)return
                ulElem.insertBefore(draging, target);
                //dragingIndex.innerText = [targetIndex.innerText, targetIndex.innerText = dragingIndex.innerText][0];
            }
            _animate(dragingRect, draging);
            _animate(targetRect, target);
            
            
            o.callback && o.callback(o.ul, target)
            //var ul = searchParent(event.target, { className: 'tourList' })
            //reIndexTourList(ul)
             
        } 
    }
    
    
    o.ul.ondragstart = dragStart
    o.ul.ondragover = dragOver 
    
    
}








function rebuildWidget(widget){//改写替换部分UI成可折叠的
    widget = $(widget)
    widget.addClass('group-widget')
    let title = widget.find('.itemTitle span')[0].innerText; 
    
    let group = $(`<div class='group-widget'> <div class='widget-wrapper'><div class='header'> <a>${title}</a> </div><div class='inner'><div class='widget-wrapper'></div></div></div></div>`)
    widget.find('.itemTitle').eq(0).remove()
    let children = Array.from(widget.children())
    children.forEach(child=>{
        group.find('.inner>div').append(child)
    })
    
    widget.append(group)
}

 

function setToggle(widget){//效果展开
    let header = $(widget).find('.header')
    let inner = $(widget).find('.inner')
	header.on("click", function(e){ 
        header.toggleClass('closed')
        inner.toggleClass('closed') 
    })  
}



 

function setWidgeOpen(widget, open){
    let header = widget.find('.header')
    let inner = widget.find('.inner')
    if(open){
        header.removeClass('closed')
        inner.removeClass('closed')
    }else{
        header.addClass('closed')
        inner.addClass('closed')
    }
}
  
function closeWidgets(classes){
    classes.forEach((cla)=>{
         let widge = $(`#hotpointDetail .content>ul>li[name=${cla}]`) 
         setWidgeOpen(widge, false)
    })
}


Array.from($("#hotpointDetail .content>ul>li")).forEach((group)=>{
    rebuildWidget(group)
    setToggle(group)
})
closeWidgets(['setPos',  'model'])






/* class OnOffSwitch extends CheckBox{
    constructor(o={}){
        super(o)
    }
}
 */

/* 
  待加功能：
  
   
   
    //boxhelper改成粗线
    
    热点可视时改成一样大小 
    热点可视insight优化
    
    hover到热点时列表滚动到这一项
    
    释放geometry内存 、texture

 */
