
/*=================================================================================================
 * MARI HOME   ---------------"--<@
 *-------------------------------------------------------------------------------------------------
 * @코드 명칭 : 마리 멀티 업로더 ( 가제 ) 
 * @요약 정보 : 파일 개체를 드래그하거나 그와 관련된 행위에 사용
 * @파일 이름 : multi_uploader_dragAction.js
 * @웹용 주소 : 글쓰기등 업로드가 필요한 곳, 파일 탐색기와 같이 파일 미리보기가 필요한 곳에서 외부 파일(js파일)로 호출됨 
 * @호출 파일 : multi_uploader_code.js에 스프레드 연산자로 등록되어 호출됨
 * @서버 위치 : 마리홈 ( ),  그누보드 ( plugin/multi_uploader/ )
 * @소스 제작 : repter ( 예뜨락 ), 홈페이지 ( https://marihome.net ) 
 * @제작 날짜 : 2023.08.01 ( 최초 제작 시작일 )
 * @소스 설명 : 마우스 움직임이나 이벤트에 대한 기능을 중요 부분이라 따로 메서드로 만들어 등록한다
 * @기타 사항 : 당분간 필요한 업그레이드를 통해서 소스 변경이 자주 일어날 수 있습니다 
 *           자바스크립트 형태 : 객체 리터럴 ( Object Literal )
 *           변수 => 프로퍼티 (Property)   : 프로퍼티 값
 *           함수 => 메서드 (Method)
 * @최종 수정 : 2025.03.26
 * @버전 명시 : 0.9
 *-------------------------------------------------------------------------------------------------
 * Copyright 2023. repter All Rights Reserved.
**===============================================================================================*/ 

// 글로벌 변수 지정
if(typeof(window.MARI) === "undefined") { (function(global) { global.MARI = {}; })(window || global); }
if(typeof(window.MARI.file_uploder) === "undefined") { (function(global) { global.MARI.file_uploder = {}; })(window || global); }

/*=========================================================================================
 * 1. 파일 드래그에 관련된 
**----------------------------------------------------------------------------------------*/
if(typeof(window.MARI.file_uploder.dragAction) === "undefined") {
	MARI.file_uploder.dragAction  = {
        
		/*=========================================================================================
		 * 1. 드래그 관련 이벤트   init 메소드에서 불려짐
		**---------------------------------------------------------------------------------------*/ 
		drag_selection : function(){

			let obj_this = MARI.file_uploder.code; 
			if(obj_this.use_selection === 'No'){ // 드래그 선택 사용 안함이면 리턴
				return false;
			}
 			obj_this.drag_selection_mouse_down();   // 17 마우스 다운
			obj_this.drag_selection_mouse_up();     // 18 마우스 업 
			obj_this.drag_selection_mouse_over();   // 19 마우스 오버
			obj_this.drag_selection_mouse_out();    // 20 마우스 아웃
			obj_this.drag_selection_mouse_move();   // 21 마우스 무브
			obj_this.drag_selection_key_down();     // 22 키보드 key down  
			obj_this.drag_selection_key_up();       // 23 키보드 key up
			obj_this.drag_selection_mouse_scroll(); // 24 스크롤 위치 이벤트
			obj_this.drag_selection_etc();          // 25 관련 기타 함수
		},


		/*=========================================================================================
		 * 2. 마우스 다운
		**---------------------------------------------------------------------------------------*/  
		drag_selection_mouse_down : function(event){

			let obj_this          = MARI.file_uploder.code;
			let drop_zone_cls     = obj_this.drop_zone_cls;
			let file_list_cls     = obj_this.file_list_cls;    // UL 파일을 담는 그룹
			let resize_handle_cls = obj_this.resize_handle_cls;
			let object_dom_cls    = drop_zone_cls+', '+file_list_cls+', '+resize_handle_cls;
			let one_file_cls      = obj_this.one_file_cls;     // li 개별 파일

            document.addEventListener('mousedown', function(event) {
                obj_this.drag_selection_mouse_down2(event);
            });
        },      


		drag_selection_mouse_down2 : function(event){

			let obj_this            = MARI.file_uploder.code;
			let drop_zone_cls       = obj_this.drop_zone_cls;
			let file_list_cls       = obj_this.file_list_cls;    // UL 파일을 담는 그룹
			let resize_handle_cls   = obj_this.resize_handle_cls;
			let resizeHt_handle_cls = obj_this.resizeHt_handle_cls;
			let resizeWd_handle_cls = obj_this.resizeWd_handle_cls;
			let object_dom_cls    = drop_zone_cls+', '+file_list_cls+', '+resize_handle_cls+', '+resizeHt_handle_cls+', '+resizeWd_handle_cls;
			let one_file_cls      = obj_this.one_file_cls;     // li 개별 파일

                // 조건에 만족하지 않으면 리턴해서 리소스 낭비를 막음
                if ( !( event.target.closest(object_dom_cls) ||  
                    event.target.matches('.drag_bar, .drag_bar_cover, .resize_handle, .resizeWd_handle, .resizeHt_handle') )  ) { 
                    return;
                }
                /**==============================================
                ** 변수 지정
                listHeader => shape6 자세히 보기시에 내부에 헤더가 생기므로 더해줌
                **---------------------------------------------*/
			    let cls            = event && event.target ? event.target.className : ''; 
                let eq_idx         = obj_this.event_eq(event); 
                let evt_idx        = obj_this.event_index(event, obj_this.one_file_cls); // 클릭, 드래그시 선택된 idx 
                let loaderMain     = obj_this.loader_main_obj(eq_idx);                        // 사용 가능하게 설정된 업로더에서의 인덱스 
                let listHeader     = loaderMain.querySelector('.file_list_header'); //리스트 헤더
                let focus_panel    = obj_this.getFocusPanel(eq_idx); // 하일라이트 판넬
                let front_panel    = loaderMain.querySelector(obj_this.front_panel_cls); // 프론트 판넬
                let file_zone_obj  = loaderMain.querySelector(obj_this.file_zone_cls);   // 기능별 div가 들어가는
                let drop_zone_obj  = obj_this.getDropZone(eq_idx); 
                let fileMain       = loaderMain.querySelector('.file_main'); //드래그 이벤트를  drop_zone에서 file_main으로 변경 2024-11-27(수)
                let moduleDom      = obj_this.module_obj(eq_idx);
                let moduleRect     = moduleDom.getBoundingClientRect();  
                let ul_obj         = loaderMain.querySelector(obj_this.file_list_cls);
                let li_obj         = obj_this.li_obj(eq_idx);
                let resizeBar      = loaderMain.querySelector(obj_this.resize_handle_cls);// 업로더 모듈 리사이즈 바
                let resizeBarRect  = resizeBar.getBoundingClientRect();
                let drag_bar       = loaderMain.querySelector(obj_this.drag_bar_cls);    // 프론트 판넬
                let dropRect       = drag_bar.getBoundingClientRect();
                let bar_left       = typeof(dropRect) !== 'undefined' ? dropRect.left + window.pageXOffset : 0; 
 			    let target_type    = obj_this.getTargetType(event);//썸네일 선택 혹은 주변 공간 선택 구분
                let add_width      = event.clientX - moduleRect.left + window.pageXOffset + 0;
                let add_height     = event.clientY - moduleRect.top  + window.pageYOffset + 0;
                let select_cnt     = obj_this.selectCnt(eq_idx);  // 드래그 선택중인 갯수 리턴
                let aryIdx         = obj_this.getListDataAryIdx(eq_idx, evt_idx) - 1; //데이터 코드에는 1부터 증가이므로 빼준다
                let totalCnt       = obj_this.file_count_return(eq_idx, 'totalCnt'); // 파일 업로더 전체 갯수
                let drag_focus_dom = document.createElement('div');
                let frontPanelDiv  = document.createElement('div');  
                let dropZoneRect   = drop_zone_obj.getBoundingClientRect(); // drop_zone
                let fileMainRect   = fileMain.getBoundingClientRect();      // drop_zone 에서 이벤트 대상을 바꿈 
                let objType        = obj_this.getMakeType(eq_idx, evt_idx); 
                let file_view_dp   = loaderMain.querySelector(obj_this.file_view_dp_cls); // 프론트 판넬  
                let objScrollTop   = drop_zone_obj.scrollTop;  // Y 스크롤이 되었다면
                let objScrollLeft  = drop_zone_obj.scrollLeft; // X 스크롤이 되었다면

                obj_this.historyEvent(eq_idx,  '현재의 eq_idx => '+eq_idx, 'pink');
 
                if (event.target.closest(object_dom_cls)) { // 마웃 다운이 드롭존, 파일리스트, 리사이즈 핸들 돔 안에서 이뤄졌다면 
                    // 현재 선택된 업로더 인덱스
                    obj_this.eq_idx = eq_idx; 
                    /**==============================================
                    ** 변수값 새로 갱신
                    **---------------------------------------------*/
                    // 리스트의 클래스를 변경할 때 사용
                    obj_this.search_chk1     = ul_obj.querySelectorAll('.thumb_chk1');
                    obj_this.search_chk2     = ul_obj.querySelectorAll('.thumb_chk2');
                    obj_this.search_chk3     = ul_obj.querySelectorAll('.thumb_chk3');
                    obj_this.search_chk4     = ul_obj.querySelectorAll('.thumb_chk4');
                    obj_this.search_off1     = ul_obj.querySelectorAll('.thumb_off1');
                    obj_this.search_off2     = ul_obj.querySelectorAll('.thumb_off2');
                    obj_this.search_off3     = ul_obj.querySelectorAll('.thumb_off3');
                    obj_this.search_off4     = ul_obj.querySelectorAll('.thumb_off4');
                    obj_this.drag_eq_idx     = eq_idx;     // click_eq와 같으면 지울것
                    obj_this.click_eq        = eq_idx;
                    obj_this.mouse_down_flag = true;       // 마우스 다운시 플래그
                    obj_this.click_target    = target_type;   // 클릭한 곳이 파일인지 배경인지( 'around' : 'thumbnail' ) 
                    obj_this.startX          = event.clientX + objScrollLeft; // 처음 클릭한 위치  
                    obj_this.startY          = event.clientY + objScrollTop - listHeader.offsetHeight;// 처음 클릭한 위치
                    obj_this.startX5         = add_width;
                    obj_this.startY5         = add_height;

                    // 히스토리 입력 
                    let msgAry = ['현재의 선택된 배열', MARI.file_uploder.code.drag_select_ary[eq_idx]];
                    obj_this.historyEvent(eq_idx,  msgAry, 'pink');
 
                    // getMakeTypeAry 배열에 타입 넣기
                    if (!Array.isArray(obj_this.getMakeTypeAry[eq_idx])) {
                        obj_this.getMakeTypeAry[eq_idx] = []; // resize_flag 배열 초기화후 false를 넣어줌
                    }
                    for(let i=0; i<totalCnt; i++){
                        let objType = obj_this.getMakeType(eq_idx, i);
                        obj_this.getMakeTypeAry[eq_idx][i] = objType;
                    }
        
                    // 드래그 다운 스크롤시 이용 값 갱신 
                    obj_this.before_list_top  = ul_obj.getBoundingClientRect().top; // 처음 클릭시 파일 리스트 위치
                    obj_this.before_list_left = ul_obj.getBoundingClientRect().left; // 처음 클릭시 파일 리스트 위치
                    obj_this.eq_idx           = eq_idx;     // 드래그시 처음 클릭한 idx 현재 eq_idx 갱신

                    /**==============================================
                    ** 마우스 다운시 ghost_move, moving에 대한 결정
                    ** 마우스 무브시 결정한 변수를 가지고 움직임을 결정 
                    **
                    ** 고스트 무브 조건
                    ** 업로드 없는 상태( 리스트 )에서도 드래그가 가능하도록
                    ** 개체가 선택되어져 있음
                    ** 개체 선택전 이미지, 파일 네임을 선택 했을 때
                    **---------------------------------------------*/
                    // 조건에 만족하면 고스트 무브 ( 개체를 이동할 수 있게 한다 ) 
                    if (obj_this.validClasses.includes(cls)) {
                        obj_this.drag_ready_flag = 'ghost_move';
                    } 

                    /*===============================================
                     * ※ 클릭한 곳의 클릭 인덱스
                    **---------------------------------------------*/
                    if( evt_idx >-1 && target_type === 'thumbnail'){
                        // 선택한 이미지의 원래 사이즈 넣기            
                        let objType    = obj_this.getMakeType(eq_idx, evt_idx); 
                        let getListIdx = obj_this.getListDataAryIdx(eq_idx, evt_idx) - 1;      // 데이터 코드에는 1부터 증가이므로 빼준다  
                        let getDatas   = obj_this.getFileInfo2(eq_idx, objType, getListIdx, 'All', '195'); // 데이터 
 
                        obj_this.click_idx[eq_idx] = evt_idx;    // 클릭하면서 선택한 인덱스
                        obj_this.click_extention = (getDatas && getDatas.dataExt) ? getDatas.dataExt : '';

                        if ( getDatas.dataThumbnail){ // 썸네일이 존재한다면   
                            // ※ 선택시 리사이징 계산등에 사용하기 위한 원본의 넓이와 높이를 넣어준다
                            obj_this.nowImgNaturalHeight[eq_idx] = getDatas.dataImgHeight;
                            obj_this.nowImgNaturalWidth[eq_idx]  = getDatas.dataImgWidth;
                        }
   
                        // 미리보기에서 사용 ( 이미지 리사이징에 대한 처리를 위해서 공간의 현재 넓이와 높이가 필요 )
                        //let viewDp = loaderMain.querySelector(obj_this.view_preview_cls);
                        //obj_this.resize_viewWidth[eq_idx]  = viewDp.offsetWidth - obj_this.PAD;  // 리사이즈 하고자 하는 미리보기 공간 클릭시 넓이
                        //obj_this.resize_viewHeight[eq_idx] = viewDp.offsetHeight - obj_this.PAD; // 업로도 리사이즈 하는 미리보기 공간 클릭시 높이

                    }

                    /*===============================================
                     * 드래그 영역 하일라이트 돔 추가, 하일라이트 CSS 위치 지정
                     * 각 개체에 대한 반전 선택을 위한 CSS 설정
                    **---------------------------------------------*/
                    if (focus_panel === null) {
                        drag_focus_dom.className = obj_this.focus_pannel_cls.substr(1);
                        loaderMain.querySelector(obj_this.file_main_cls).appendChild(drag_focus_dom); // ul과 같은 선상에 놓기 위해
                        drag_focus_dom.style.display = 'none';
                        drag_focus_dom.style.left   = '0px';
                        drag_focus_dom.style.top    = '0px';
                        drag_focus_dom.style.width  = '0px';
                        drag_focus_dom.style.height = '0px';
                    }
                    
 
                    /*===========================================
                    ** 파일의 위치 정보
                    ** 파일 미리보기 안에 들어 있는 파일들의 위치 정보를 기록한다
                    ** 마우스 다운시 마다 정보가 입력되니 파일 추가 등 변화가 있을 때마다 
                    ** 업데이트될 수 있도록 한다
                    ** 파일 추가 삭제 시에만 갱신되도록 한다
                    **-----------------------------------------*/
                    obj_this.position_info();

                    /*===============================================
                     * 쉬프트 동작 시 시작점과 끝점을 구분하기 위한 
                     * 쉬프트 배열에 시작점과 끝점을 넣는다
                     * 클릭 시마다 배열의 0번과 1번에 입력되고 리셋되기 전까지
                     * 1번 배열에 다시 쓰인다
                    **---------------------------------------------*/   
                    if(evt_idx > -1){
                        // 일반 클릭이면 초기화
                        if ( obj_this.shift_key_flag === false && obj_this.ctrl_key_flag === false || obj_this.ctrl_key_flag === true ) {
                            obj_this.shift_start_end_ary[eq_idx] = []; // 초기화
                        }

                        let startEndAry = obj_this.shift_start_end_ary[eq_idx]; 
                        if (startEndAry.length === 0) {
                            // 첫 번째 배열이 비어 있으면 첫 번째로 값 추가
                            startEndAry[0] = evt_idx;
                        }

                        if ( obj_this.shift_key_flag === true ) {
                            if (startEndAry.length > 0) { 
                                // 두 번째부터는 두 번째 값만 계속 변경
                                startEndAry[1] = evt_idx;
                            }
                        }

                    } 
 
                    /*===============================================
                     * 선택된 항목을 배열에 넣고 고스트 이미지를 만든다
                    **---------------------------------------------*/
                    //control shift면
                    if( obj_this.shift_key_flag === true  || obj_this.ctrl_key_flag === true ){
                        obj_this.set_drag_select_ary(event, objType, 'add', eq_idx, aryIdx, 'thumbnail', 'dragAct1');

                    } else { // 기능키 누름 없이 개별 클릭이 되었다면
                       if (typeof obj_this.drag_select_ary[eq_idx] !== 'undefined' 
                            && Array.isArray(obj_this.drag_select_ary[eq_idx]['idx']) 
                            && obj_this.drag_select_ary[eq_idx]['idx'].length <= 1) {
                            obj_this.set_drag_select_ary(event, objType, 'one', eq_idx, aryIdx, 'thumbnail', 'dragAct2');
                        }
                    }



                    // ※ 고스트 이미지:: 만들기
                    //if( obj_this.drag_flag === 'ghost_move'){ // 드래그중이라면 
                        obj_this.ghost_image_make(event, eq_idx); // 고스트 이미지를 만든다
                    //}
 


                    /*===============================
                     * ※ 미리보기
                     * 마우스 다운시 파일 미리보기를 실행한다 
                     * 선택한것 배열에 넣은 다음 
                     * 실행이 이뤄져야 한다
                    **-----------------------------*/
                    if( obj_this.view_img_idx[eq_idx] !== evt_idx ){//이전 선택과 같지 않다면
                        if( obj_this.drag_flag === false){ // 드래그중이 아니라면 
                            if( obj_this.shift_key_flag === false && obj_this.ctrl_key_flag === false ){

                                // 선택한 파일의 기본 정보를 하단 표시줄에 넣어준다
                                if(target_type === 'thumbnail'){
                                    // 하단 선택한 파일의 기본 정보 
                                    let selectFileInfo = loaderMain.querySelector('.status_bar');                
                                    let objType      = obj_this.getMakeType(eq_idx, evt_idx);
                                    let getListIdx   = obj_this.getListDataAryIdx(eq_idx, evt_idx) - 1;   // 데이터 코드에는 1부터 증가이므로 빼준다  
                                    let getDatas     = obj_this.getFileInfo2(eq_idx, objType, getListIdx, 'All', '1983'); // 데이터
                                    let datas        = getDatas;  
                                    let dataWd_Ht = datas.dataImgWidth + ' x ' + datas.dataImgHeight;   
                                    let dataSize  = datas.dataFileSize; 
                                    let dataName  = datas.dataFileName; 
                                    let shortInfo = ''; 

                                    if (!datas.dataType.startsWith('image/')) {
                                    shortInfo += '<div>' + '' + '</div><div class="smallbar"></div>';  // 파일 타입 utf-8, ansi
                                    } else { 
                                    shortInfo += '<div>' + dataWd_Ht + '</div><div class="smallbar"></div>'; // 이미지의 넓이 높이
                                    }
                                    shortInfo += '<div>' + dataSize  + '</div><div class="smallbar"></div>';
                                    shortInfo += '<div class="file_name_container"><span class="file_name_content">' + dataName  + '</span></div><div class="smallbar"></div>'; 
                                    selectFileInfo.innerHTML = shortInfo; 

                                    let nameContainer = loaderMain.querySelector('.status_bar .file_name_container'); 
                                    let nameContent   = loaderMain.querySelector('.status_bar .file_name_content'); 
                                    // 파일명이 길다면 마퀴 클래스를 붙여준다
                                    if(nameContent.offsetWidth > 100){
                                        nameContent.classList.add("marquee_on"); // 실행

                                        // 애니메이션이 끝났을 때 마지막 애니메이션 종료 후 클래스 삭제
                                        nameContent.addEventListener("animationend", function(event) {
                                            // 모든 애니메이션이 끝났을 때만 클래스를 제거
                                            if (event.animationName === "marquee3") {
                                                nameContent.classList.remove("marquee_on");

                                                // 마우스를 올렸을 때의 동작
                                                nameContainer.addEventListener("mouseenter", function() {
                                                    // 애니메이션이 끝날 때까지 클래스 추가/삭제가 되지 않도록
                                                    if (!nameContent.classList.contains("marquee_on")) {
                                                        nameContent.classList.add("marquee_on");
                                                    }
                                                });

                                            }
                                        }); 
                                    }
                                }
                                
                                // ※ 미리 보기 선택, 상세 보기 선택
                                if(obj_this.preview_mode[eq_idx]==='open'|| obj_this.detail_mode[eq_idx]==='open'){
                                    obj_this.file_preview_act(event, eq_idx, evt_idx, 'one');  
                                } 
                                
                                obj_this.aryClassSetting(event, objType, eq_idx, 'no_shift_ctrl_down', target_type, evt_idx, 'call4');
                                 
                                // 미리보기 이미지 네비게이션  ::: imgControllerAct > 2140 라인즈음
                                let naviImg  = loaderMain.querySelector('.naviImg');     // 미리보기 썸네일 가이드 이미지    
                                let getDatas = obj_this.getFileInfo2(eq_idx, objType, aryIdx, 'All', '195'); // 데이터

                                /*===================
                                 * 네비게이션 이미지
                                **-----------------*/
                                if(naviImg && getDatas  && getDatas.dataThumbnail !== null){
       console.log( getDatas.dataThumbnail );   
                                    
                                    
                                    naviImg.src  = getDatas.dataThumbnail// 미리보기 이미지    
                                } 

                            }
                        }
                    }

                    if(evt_idx > 0){
                        obj_this.view_img_idx[eq_idx] = evt_idx;
                    }

                    /*===============================================
                     *  SHIFT, ctrl키에 따른 동작 분류
                    **---------------------------------------------*/  
                    if( obj_this.shift_key_flag === true && obj_this.ctrl_key_flag === true ){ 
                        /**======================
                        ** SHIFT + ctrl 선택
                        ** shift선택과 동일
                        **---------------------*/
                        obj_this.aryClassSetting(event, objType, eq_idx, 'only_shift_down', target_type, evt_idx, 'call1');
                    }
                    else
                    if( obj_this.shift_key_flag === true && obj_this.ctrl_key_flag === false ){ 
                        /**======================
                        ** SHIFT만 선택
                        **---------------------*/
                        obj_this.aryClassSetting(event, objType, eq_idx, 'only_shift_down', target_type, evt_idx, 'call2');
                    }
                    else
                    if( obj_this.ctrl_key_flag === true && obj_this.shift_key_flag === false ){ 
                        /**======================
                        ** ctrl만 선택
                        **---------------------*/ 
                        obj_this.aryClassSetting(event, objType, eq_idx, 'only_ctrl_down', target_type, evt_idx, 'call3');
                    }
                    else
                    if( obj_this.shift_key_flag === false && obj_this.ctrl_key_flag === false ){
                        /**======================
                        ** SHIFT, ctrl 둘다 안함
                        **---------------------*/
                        // 마지막 클릭한 곳  
                        // 썸네일을 클릭했다면 바로 선택 
                        // 주변을 클릭했다면 이전 선택한 것은 풀지 않고 클릭한 곳은 보더 변경 없이 바탕만 색을 변경한다 
                        obj_this.aryClassSetting(event, objType, eq_idx, 'no_shift_ctrl_down', target_type, evt_idx, 'call4');
                    }

                    // 선택한 n개 파일 삭제 버튼에 갯수를 갱신
                    // obj_this.select_name_list();

                    // 미리보기 드래그바 보이기 또는 감추기 활성 여부
                    obj_this.drag_bar_line_dp(event, eq_idx);
 
                } // 마웃 다운이 드롭존, 파일리스트, 리사이즈 핸들 돔 안에서 이뤄졌다면

 
                /*===============================================
                 * 파일 미리보기 드래그 바로 이동
                **---------------------------------------------*/
                if (event.target.matches('.drag_bar, .drag_bar_cover, .resize_handle, .resizeWd_handle, .resizeHt_handle')) { 

                    if(focus_panel)
                    focus_panel.style.display = 'none';  // 드래그 영역 감춤
                    obj_this.click_eq         = eq_idx; // 현재의 모듈 인덱스번호를 기록한다 마우스 무브등에서 사용
                    obj_this.mouse_down_flag  = true;

                    if (!front_panel) {
                        frontPanelDiv.className = 'front_panel';
                        file_zone_obj.appendChild(frontPanelDiv);
                        frontPanelDiv.style.display = 'block';
                    }
                    else{
                        front_panel.style.display = 'block';
                    }

                    let cursorType = null;
                    // 미리보기 드래그바 상태 플래그 설정 
                    if (event.target.matches('.drag_bar, .drag_bar_cover')){
                        obj_this.drag_bar_flag = true;  
                        cursorType = 'w-resize';
                        let viewDp       = loaderMain.querySelector(obj_this.view_preview_cls); 
                        obj_this.resize_viewWidth[eq_idx]  = viewDp.offsetWidth - obj_this.PAD;  // 리사이즈 하고자 하는 미리보기 공간 클릭시 넓이
                        obj_this.resize_viewHeight[eq_idx] = viewDp.offsetHeight - obj_this.PAD; // 업로도 리사이즈 하는 미리보기 공간 클릭시 높이
                    }


                    // 리사이즈 바들에 대한 플래그 설정 
                    if( obj_this.resize_bar_flag   === false &&
                        obj_this.resizeWd_bar_flag === false &&
                        obj_this.resizeHt_bar_flag === false){
                        if (event.target.matches('.resize_handle')){
                            obj_this.resize_bar_flag   = true;   // 넓이, 높이 리사이즈 상태 플래그
                            document.body.style.cursor = 'se-resize !important';
                            cursorType = 'se-resize';
                        }
                        else
                        if (event.target.matches('.resizeWd_handle')){
                            obj_this.resizeWd_bar_flag = true;   // 넓이 리사이즈 상태 플래그
                            document.body.style.cursor = 'w-resize !important';
                            cursorType = 'w-resize';
                        }
                        else
                        if (event.target.matches('.resizeHt_handle')){
                            obj_this.resizeHt_bar_flag = true;   // 높이 리사이즈 상태 플래그
                            document.body.style.cursor = 's-resize !important';
                            cursorType = 's-resize';
                        }  
                    } 


                    // 드래그, 리사이즈등에 사용되는 커버 판넬
                    if (cursorType !== null) {
                        obj_this.helperDiv_create(event, cursorType);
                    }

                    /*=============================================== 
                    * 파일 업로더 영역 넓히고 줄이기 오른쪽 하단 리사이즈 바로 이동
                    **---------------------------------------------*/
                    // 포커스 판넬 리셋  
                    obj_this.focusPanelHide(); 
                    obj_this.click_eq             = eq_idx; //현재의 모듈 인덱스번호를 기록한다 마우스 무브등에서 사용
                    obj_this.resize_flag[eq_idx]  = true;   // 드래그할 수 있도록 플래그를 변경한다
                    obj_this.drag_flag            = false;  // 무빙할 수 없도록 
                    obj_this.mouse_down_flag      = true;
                    obj_this.click_target         = false; 

                    // 모듈 리사이즈 무빙바 
                    obj_this.resize_DomWidth[eq_idx]  = moduleRect.width;    // 리사이즈하는 돔의 높이( 돔의 H )  
                    obj_this.resize_DomHeight[eq_idx] = moduleRect.height;    // 리사이즈하는 돔의 높이( 돔의 H )  
                    obj_this.resize_positionX[eq_idx] = resizeBarRect.left; // 리사이즈바의 하단 위치( 돔의 하단 Y )
                    obj_this.resize_positionY[eq_idx] = resizeBarRect.bottom; // 리사이즈바의 하단 위치( 돔의 하단 Y )
 
                    // 미리보기 좌우 무빙바, 보여지는 곳의 공간을 재 셋팅
                    obj_this.viewBarPos_left    = bar_left;// + 150;//  - window.pageXOffset; // 미리보기 무빙바의 위치 X스크롤이 있으면 보정
                    if( obj_this.preview_mode[eq_idx] === 'open'){
                        obj_this.view_dp_wd[eq_idx] = parseInt(file_view_dp.offsetWidth);
                    }

                    if (front_panel === null || front_panel.length === 0) {
                        frontPanelDiv.className = 'front_panel';
                        file_zone_obj.appendChild(frontPanelDiv);
                        frontPanelDiv.style.display = 'block'; 
                    }
                    else{
                        front_panel.style.display  = 'block'; 
                    }

                    // 미리보기 드래그바 보이기 또는 감추기 활성 여부
                    obj_this.drag_bar_line_dp(event, eq_idx);

                    // 추가 보강 필요 ::2024-11-26(화)
                    // shape8에 대한 설정 
                    const positions = obj_this.fileInfoClass.map(cls =>  
                        Array.from(loaderMain.querySelectorAll('.'+cls)).map(el => {
                            const rect = el.getBoundingClientRect(); // 요소의 위치와 크기 정보 가져오기
                            return rect.left + rect.width; // left + width = 오른쪽 변의 위치
                        })
                    );
                    obj_this.shape8TextPositions = positions; 
                }

        }, // end drag_selection_mouse_down2 


		/*=========================================================================================
		 * 3. 마우스 업
		**---------------------------------------------------------------------------------------*/  
		drag_selection_mouse_up : function(){

            document.addEventListener('mouseup', function(event) {
				/*===============================================
				 * 파일업로더에서 마우스 다운 이벤트 동작이 있었다면
				**---------------------------------------------*/                    
 			    let obj_this = MARI.file_uploder.code;
                let eq_idx   = obj_this.event_eq(event); //사용 가능하게 설정된 업로더에서의 인덱스

                if( eq_idx === null ||  eq_idx < 0 ) return false;

                let loaderMain = obj_this.loader_main_obj(eq_idx);    
				if( obj_this.mouse_down_flag === true ){
			        let cls          = event && event.target ? event.target.className : '';  
                    let front_panel  = loaderMain.querySelector(obj_this.front_panel_cls); // 프론트 판넬  
                    let focus_panel  = obj_this.getFocusPanel(eq_idx); // 하일라이트 판넬
                    let ul_obj       = loaderMain.querySelector(obj_this.file_list_cls);
                    let li_obj       = obj_this.li_obj(eq_idx); 
                    let ghostImg     = loaderMain.querySelector('.ghost_img'); 
                    let select_cnt   = obj_this.selectCnt(eq_idx);  // 드래그 선택중인 갯수 리턴
                    let click_idx    = obj_this.click_idx[eq_idx];      // 마우스 다운시 선택된 idx 
                    let fileLoader   = document.querySelectorAll(obj_this.loader_cls);     
                    let loader_cnt   = fileLoader.length; 
 			        let target_type  = obj_this.getTargetType(event);//썸네일 선택 혹은 주변 공간 선택 구분
                    let evt_idx      = obj_this.event_index(event, obj_this.one_file_cls);   // 클릭, 드래그시 선택된 idx
                    let aryIdx       = obj_this.getListDataAryIdx(eq_idx, evt_idx) - 1; //데이터 코드에는 1부터 증가이므로 빼준다
                    let objType      = obj_this.getMakeType(eq_idx, evt_idx);
                    
                    // file_list 요소의 스타일에서 'left', 'top', 'width', 'height' 값을 가져오기 
                    let fileMain     = loaderMain.querySelector('.file_main');
                    let file_list    = loaderMain.querySelector(obj_this.file_list_cls);
                    let rect         = fileMain.getBoundingClientRect();
                    let obj_left     = rect.left + window.pageXOffset;
                    let obj_top      = rect.top + window.pageYOffset;
                    let obj_width    = fileMain.offsetWidth;
                    let obj_height   = fileMain.offsetHeight;

                    // 드랍존 영역 위치 계산
                    let file_list_left   = obj_left - window.pageXOffset;
                    let file_list_right  = obj_left - window.pageXOffset + obj_width;
                    let file_list_top    = obj_top  - window.pageYOffset;
                    let file_list_bottom = obj_top  - window.pageYOffset + obj_height;

					// 고스트 이미지 영역 위치( ghostImg 요소의 스타일에서 'left'와 'top' 값을 가져오고, 'outerWidth'와 'outerHeight'를 계산하여 변환 )
                    let ghost_left   = '';
                    let ghost_top    = '';
                    let ghost_right  = '';
                    let ghost_bottom = '';
                    if( ghostImg ){
                        ghost_left   = parseInt(getComputedStyle(ghostImg).left, 10);
                        ghost_top    = parseInt(getComputedStyle(ghostImg).top, 10); 
                        ghost_right  = ghost_left + ghostImg.offsetWidth;
                        ghost_bottom = ghost_top  + ghostImg.offsetHeight;
                    }

					let in_flag1     = false;
					let in_flag2     = false;

					//마우스업 안에서만 지역 변수로 사용
					let up_drag_flag = false; 
					if( obj_this.drag_flag === true || obj_this.drag_flag === 'ghost_move' ){
						up_drag_flag = obj_this.drag_flag;
					}

					let _mouse_down_flag = obj_this.mouse_down_flag;
					let _drag_flag       = obj_this.drag_flag; 
                    let select_ary       = MARI.file_uploder.code.drag_select_ary;

                    // 리스트의 클래스를 변경할 때 사용
                    obj_this.search_chk1   = ul_obj.querySelectorAll('.thumb_chk1');
                    obj_this.search_chk2   = ul_obj.querySelectorAll('.thumb_chk2');
                    obj_this.search_chk3   = ul_obj.querySelectorAll('.thumb_chk3');
                    obj_this.search_chk4   = ul_obj.querySelectorAll('.thumb_chk4');
                    obj_this.search_off1   = ul_obj.querySelectorAll('.thumb_off1');
                    obj_this.search_off2   = ul_obj.querySelectorAll('.thumb_off2');
                    obj_this.search_off3   = ul_obj.querySelectorAll('.thumb_off3');
                    obj_this.search_off4   = ul_obj.querySelectorAll('.thumb_off4'); 

					/*===============================================
					** 선택 개체 css 색 변경
					**-----------------------------------------------
					** SHIFT, ctrl키에 따른 동작 분류를 통해 css 변경
					**---------------------------------------------*/
					// 동작이 파일 업로더 위에서 이뤄진다면
					if( loader_cnt > 0 && eq_idx > -1 ){
						if( obj_this.shift_key_flag === true && obj_this.ctrl_key_flag === true ){ 
							//SHIFT + CTRL 선택 shift만 선택과 동일
							// 아무 변화 없음 ( 구색 맞추기 위해)
							obj_this.aryClassSetting(event, objType, eq_idx, 'only_shift_up', target_type, evt_idx, 'call5'); 
						}
						else
						if( obj_this.shift_key_flag === true && obj_this.ctrl_key_flag === false ){ 
							//SHIFT만 선택
							obj_this.aryClassSetting(event, objType, eq_idx, 'only_shift_up', target_type, evt_idx, 'call6'); 
						}
						else
						if( obj_this.ctrl_key_flag === true && obj_this.shift_key_flag === false ){
							//ctrl만 선택 :: 마우스 업때 보더 강조된 걸로
				 			obj_this.aryClassSetting(event, objType, eq_idx, 'only_ctrl_up', target_type, evt_idx, 'call7'); 
						}
						else
						if( obj_this.shift_key_flag === false && obj_this.ctrl_key_flag === false ){ 
							//SHIFT,ctrl 둘다 안함
                            // 마우스업시 리셋도 포함:: 아래의 리셋 (선택된거 풀기)과 같은
		 			        obj_this.aryClassSetting(event, objType, eq_idx, 'no_shift_ctrl_up', target_type, evt_idx, 'call8');
						}
					}

                    //===============================
                    // 선택된 것에 대한 디버깅을 알고자 할 때
                    //===============================
                    // 디버깅 
                    // obj_this.log('선택된 갯수', select_cnt);
                    // obj_this.log('선택한 것에 대한 배열', MARI.file_uploder.code.drag_select_ary[0]['idx'])  
                    
                    let select_cnt2   = obj_this.selectCnt(eq_idx);  // 드래그 선택중인 갯수 리턴
                    //고스트 이미지:: 이미지의 선택된 돔 누적 :: 리셋되기 이전에 있어야 함
                    let list_dom = '';
                    for (let i = 0; i <= select_cnt; i++) {
                        let loop_idx = select_ary[eq_idx]['idx'][i];
                        if (li_obj.length > loop_idx) {
                            list_dom += li_obj[loop_idx].outerHTML; 
                        }
                    }

					/*===============================
					** mouseup 초기화 
					** 배열 초기화는 마우스 다운시
					**-----------------------------*/
					// 포커스 판넬 리셋 :: 감추지 않고 디버깅이 필요할때 사용하기 위해서 걸어 둠
                    if(obj_this.focusPanelHideAct === true){
                        obj_this.focusPanelHide();
                    }
 
					obj_this.resize_flag[eq_idx] = false; //리사이즈 플래그 
					obj_this.drag_flag           = false;
					obj_this.mouse_down_flag     = false;
					obj_this.click_target        = false; 
                    obj_this.drag_idx            = null;  // 드래그중 선택되고 있는 현재의 인덱스 (선 택되고 있는 미리보기 인덱스 초기화 )
					obj_this.drag_ready_flag     = false; // 현재 클릭한 곳이 선택되어진 개체거나 썸네일 파일명일 경우 고스트 무브 그외는 무빙을 위한 플래그 
					obj_this.click_eq            = null;
					obj_this.scroll_add_num      = null;  // 스크롤을 위해 비워둔다
					clearTimeout(obj_this.set_time_var3);

					// 개체 이동시 안내 가이드 없애기 
                    document.querySelectorAll('.one_file_before_bar').forEach(function(element) {
                        element.removeAttribute('style');
                    });
                    document.querySelectorAll('.one_file_after_bar').forEach(function(element) {
                        element.removeAttribute('style');
                    });

					/*===============================
					 * 선택 리스트 해제시 미리보기 초기화 
					 * 미리보기 해제
					**-----------------------------*/   
                    if( select_cnt2 == 0 ){ 
                            obj_this.reset_preview(event); 
                    }

					/*===============================
					 * 파일 이동과 삭제
					 * ------------------------------
					 * 고스트 이미지를 파일 드랍존 밖으로 꺼냈다면 
					 * - 선택된 파일 삭제
					 * 드롭존 안에서 다른 파일 앞 뒤로 이동 했다면 
					 * - 순서 바꾸기
					 * ghost_image_make()에서 돔 생성
					**-----------------------------*/   
 					if( up_drag_flag === true || up_drag_flag === 'ghost_move' ){
						// 고스트 이미지가 드랍존 좌우 넓이 내에 있다면 
						if( ghost_left  < file_list_right && ghost_right > file_list_left){
							in_flag1 = true; // 안 나감 
						}
						// 고스트 이미지가 드랍존 상하 높이 내에 있다면 
						if( ghost_top  < file_list_bottom && ghost_bottom >file_list_top){
							in_flag2 = true; // 안 나감 
						}
 
						// 두 조건을 만족해서 드랍존에 모서리라도 걸쳐 있다면 안나감 그외 밖으로 모두 나가 있다면 나감 
						if( in_flag1 === true && in_flag2 === true ){ // 드롭존 안쪽에 있음 :: 선택된 파일이 있다면 이동  
							/*===============================
							 * 이동 허락된 개체가 있다면 이동 시킨다
							**-----------------------------*/                            
						 	if(up_drag_flag === 'ghost_move'){  
								// 파일 위치 이동이 있다면
								if(obj_this.insert_pos === 'before' || obj_this.insert_pos === 'after'){

                                    // ※ 선택 리스트 선택한 곳으로 순서 이동:::고스트 이동후 선택된 곳에 리스트를 만들어 넣고 원본 지움
                                    obj_this.ghostMoveAfter_AddDom(event, eq_idx,list_dom);

								}// 파일 위치 이동이 있다면
							}
							/*-------------------------------
							 * 이동 허락된 개체가 있다면 이동 시킨다
							**=============================*/
						}
						else{// 드롭존 밖으로 고스트 무브로 나감 :: 선택된 파일이 있다면 삭제
							if(up_drag_flag === 'ghost_move'){ // 고스트 무브로 드롭존을 벗어 났다면 삭제
								if(select_cnt > 0){
                                    obj_this.select_files_del(event);
								}
							}
						}
					}
					else
					if(up_drag_flag === false){ // 드래그 고스트 없는 클릭
					 // 연관된 동작 없음 
					}

					/*===============================
					* 고스트 이미지 삭제( 감춤 )
					* 현재 : 마우스 업 상태
					**-----------------------------*/
					//고스트 이미지 없애기 :: 위치 정보를 계산하는 곳이 있기에 최후에 삭제해 줌
                    if( ghostImg ) ghostImg.remove();

                    //프론트 판넬 감추기
                    if(front_panel){
                        front_panel.style.display  = 'none';
                    }

				}// 파일업로더에서 마우스 다운 이벤트 동작이 있었다면

                /*===============================================
                 * 리사이즈 상태 플래그 초기화
                **---------------------------------------------*/ 
                obj_this.drag_bar_flag     = false;   // 미리보기 드래그바 상태 플래그
                obj_this.resize_bar_flag   = false;   // 넓이, 높이 리사이즈 상태 플래그
                obj_this.resizeWd_bar_flag = false;   // 넓이 리사이즈 상태 플래그
                obj_this.resizeHt_bar_flag = false;   // 높이 리사이즈 상태 플래그
 
                /*=============================================== 
                 * 마우스업일 때 리사이즈가 있었다면 리스트의 높이를 보정해 준다
                **---------------------------------------------*/ 
                let helperDiv = document.getElementById(obj_this.helperDomeId);
                let target_class = helperDiv ? helperDiv.getAttribute('target_class') : null;  
                if (target_class && (target_class.includes('resize_handle') || 
                    target_class.includes('resizeHt_handle') || 
                    target_class.includes('resizeWd_handle'))) { 
                    
                    obj_this.click_eq = null; //현재의 모듈 인덱스번호를 기록한다 마우스 무브등에서 사용  
                    // 현재 라인에서 가장 높은 이미지의 높이에 맞춰 나머지 높이를 맞춘다 :: 파일 업로더 영역 리사이즈
                    let list_shape   = obj_this.list_shape[eq_idx];   
                    obj_this.equalizeImageHeights(eq_idx, 'resizeMouseUp', list_shape);
                } 

                // 드래그, 리사이즈등에 사용되는 커버 판넬
                const helperDivDome = document.getElementById(obj_this.helperDomeId);
                if (helperDivDome) {
                     helperDivDome.remove(); // 요소 제거
                }


                /*===============================================
                 *  마우스 다운 안에 귀속 시킬것
                 * 파일 미리보기 드래그 바로 이동
                **---------------------------------------------*/
                // 이벤트 타겟이 '.drag_bar' 또는 '.front_panel'인지 확인 
                if (event.target.matches('.helperDiv, .drag_bar, .drag_bar_cover, .front_panel')) {  
                    let preview_mode = String(obj_this.preview_mode[eq_idx]); // 파일 미리 보기 창 열려 있는지에 대한 플래그  
                    let detail_mode  = String(obj_this.detail_mode[eq_idx]);  // 파일 상세 보기 창 열려 있는지에 대한 플래그
                    let file_view_dp = loaderMain.querySelector(obj_this.file_view_dp_cls); // 프론트 판넬  

                    /*===============================
                    * 미리보기창 넓이를 넣어준다 드롭바를 건들지
                    * 않을 수 있으므로
                    * ( 오픈 되어 있을 경우만 갱신 )
                    **-----------------------------*/ 
                    if (preview_mode === 'open' || detail_mode === 'open') {
                        obj_this.view_dp_wd[eq_idx] = parseInt(file_view_dp.offsetWidth); 
                    }

                    /*===============================
                    * 현재의 모듈 넓이를 갱신한다 
                    **-----------------------------*/  
                    let moduleDom = obj_this.module_obj(eq_idx);
                    MARI.file_uploder.code.module_width[eq_idx] = moduleDom.offsetWidth; 
                       
                    //프론트 판넬 감추기
                    let front_panel = loaderMain.querySelector(obj_this.front_panel_cls); // 프론트 판넬    
                    if(front_panel){
                        front_panel.style.display  = 'none';
                        obj_this.resize_flag[obj_this.click_eq] = false;  // 리사이즈 플래그 
                    } 
                }
 

                /*===============================================
                 * 마우스 다운 안에 귀속 시킬것
                 * 파일 업로더 영역 넓히고 줄이기 드래그 바로 이동
                **---------------------------------------------*/  
                obj_this.resize_flag[eq_idx] = false;
 
                // 히스토리 입력
                let msgAry = ['마우스 업:::선택된 리스트', MARI.file_uploder.code.drag_select_ary[0]['name']];
                obj_this.historyEvent(eq_idx,  msgAry, 'pink'); 
                obj_this.log( '마우스 업:::선택된 리스트 => ', obj_this.nowDatas(MARI.file_uploder.code.drag_select_ary[0]['name']),'No' );

            });
            
		}, // end drag_selection_mouse_up


		/*=========================================================================================
		 * 4. 마우스 무브
		**---------------------------------------------------------------------------------------*/ 
		drag_selection_mouse_move : function(){

 			let obj_util     = MARI.file_uploder.util;
			let obj_this     = MARI.file_uploder.code;
            let eq_idx       = null, loaderMain = null, fileMain   = null, front_panel  = null;
            let select_ary   = null, startX     = null, startY     = null, evt_idx      = null;
            let aryIdx       = null, li_obj     = null, resizeFlag = null, before_click = null;
            let objType      = null, getListIdx = null, getDatas   = null, panel        = null;
            let drop_zone    = null, rect       = null, pan_width  = null, pan_height   = null;
            let fileMainRect = null, ghostDom   = null;

            // 마우스업시 초기화
            document.addEventListener('mouseup', function(event) {                
                eq_idx        = loaderMain   = fileMain  = front_panel = select_ary   = null;
                startX        = startY       = evt_idx   = aryIdx      = li_obj       = null;
                resizeFlag    = before_click = objType   = getListIdx  = getDatas     = null; 
                panel         = drop_zone    = rect      = pan_width   = pan_height   = null;
                fileMainRect  = ghostDom     = null;
            });

            // 마우스 무브에 필요한 소스
            document.addEventListener('mousemove', function(event) {

                // 마우스 이동시 마다 이벤트 발생을 막기 위해 드래그를 위한 마우스 다운이 없다면 리턴
 				if( !( obj_this.mouse_down_flag ||
                       obj_this.drag_bar_flag || obj_this.resize_bar_flag||
                       obj_this.resizeWd_bar_flag||obj_this.resizeHt_bar_flag ) ){ 
                    return false;
                }

                // 공통 변수:: 호출수를 줄이기 위해서 여러번 반복 호출이 필요 없는 부분은 따로 빼서 정의해 준다
                if( eq_idx === null ){
                    eq_idx       = obj_this.event_eq(event);
                    loaderMain   = obj_this.loader_main_obj(eq_idx);
                    fileMain     = loaderMain.querySelector('.file_main');             //드래그 이벤트를  drop_zone에서 file_main으로 변경 2024-11-27(수)
                    drop_zone    = loaderMain.querySelector(obj_this.drop_zone_cls);
                    front_panel  = loaderMain.querySelector(obj_this.front_panel_cls); // 리사이즈바 선택시 프론트 판넬
                    li_obj       = obj_this.li_obj(eq_idx);
                    fileMainRect = fileMain.getBoundingClientRect();
                    before_click = obj_this.before_click_idx[eq_idx]; // 이전 클릭 했던 곳 
                    select_ary   = MARI.file_uploder.code.drag_select_ary;
                    startX       = obj_this.startX;
                    startY       = obj_this.startY;   
                    evt_idx      = obj_this.event_index(event, obj_this.one_file_cls); // 클릭, 드래그시 선택된 idx
                    objType      = obj_this.getMakeType(eq_idx, evt_idx); 
                    aryIdx       = obj_this.getListDataAryIdx(eq_idx, evt_idx) - 1;    //데이터 코드에는 1부터 증가이므로 빼준다
                    getListIdx   = obj_this.getListDataAryIdx(eq_idx, evt_idx) - 1;      // 데이터 코드에는 1부터 증가이므로 빼준다  
                    getDatas     = obj_this.getFileInfo2(eq_idx, objType, getListIdx, 'All', '935'); // 데이터  
                    panel        = obj_this.getFocusPanel(eq_idx); // 하일라이트 포커스 판넬 
                    if(panel){
                    rect         = panel.getBoundingClientRect(); 
                    pan_width    = rect.width;
                    pan_height   = rect.height;
                    }

                    resizeFlag   = false;

                    // 리사이즈 관련 플래그
                    if (front_panel?.style.display === 'block' && 
                       (obj_this.resize_bar_flag || obj_this.resizeWd_bar_flag || obj_this.resizeHt_bar_flag)) {
                        resizeFlag = true;
                    }

                }

                // 공통 변수:: 무빙에 따라 변화가 있어야 하는 변수들
                let x              = event.clientX;
                let y              = event.clientY;
                const pan_top      = y - fileMainRect.top + window.scrollY;
                const pan_left     = x - fileMainRect.left + window.scrollX;  
                const pan_right    = pan_left + pan_width;
                const pan_bottom   = pan_top + pan_height;

                // 고스트 무브, 일반 무빙 플래그 :: 리사이즈를 위한 front_panel 판넬이 없거나 또는 display가 none일 때
                if (!front_panel || front_panel.style.display === 'none') {                    
                    if (obj_this.drag_ready_flag === 'ghost_move' && (Math.abs(startX - x) > 3 || Math.abs(startY - y) > 3)) {
                        obj_this.drag_flag = 'ghost_move'; // 무빙을 위해 플래그 설정
                    } else {
                        obj_this.drag_flag = true; // 드래그 설정
                    }
                }

                /**==============================================
                 * 1::썸네일 이미지 또는 파일명을 드래그 했을 때, 
                 *    선택이 되어 있는 개체라면 => ghost_move 
                **---------------------------------------------*/
                if( obj_this.drag_flag === 'ghost_move' ){

                    // 쉬프트키를 통한 고스트 이미지 이동시 개체 복사
                    if( obj_this.shift_key_flag === true ){ // 마우스업 때마다 일어나지 않도록 범위를 좁혀서 
                        return false
                    }

                    ghostDom = ghostDom || loaderMain.querySelector('.ghost_img'); // 고스트 돔이 null이라면

                    // 고스트 이미지를 띄운다 
                    if (ghostDom && (Math.abs(startX - x) > 3 || Math.abs(startY - y) > 3)) {
                        // 포커스 판넬 숨김
                        obj_this.focusPanelHide();
                        
                        // 고스트 이미지 설정
                        Object.assign(ghostDom.style, {
                            left: `${event.clientX - 52.5}px`, // 105의 반절
                            top: `${event.clientY - 95}px`,    // 105에서 여유분 10픽셀을 뺀
                            display: 'block',
                        });
 
                        // 필터링 및 스타일 적용
                        obj_this.drag_position_ary[eq_idx].forEach((pos, index) => {
                            if (pos.left <= pan_right && pos.right >= pan_left && pos.top <= pan_bottom && pos.bottom >= pan_top) {
                                const obj      = obj_this.li_obj(eq_idx)[index];
                                const isBefore = pan_right < pos.left + (pos.right - pos.left) / 2;  // true false < 기준으로 좌측값이 우측 값보다 작은지

                                document.querySelectorAll('.one_file_before_bar, .one_file_after_bar')
                                    .forEach(bar => bar.style.display = '');

                                if (!obj_this.drag_select_ary[eq_idx].idx.includes(index)) {
                                    obj.querySelector(isBefore ? '.one_file_before_bar' : '.one_file_after_bar').style.display = 'block';
                                    Object.assign(obj_this, { insert_idx: index, insert_pos: isBefore ? 'before' : 'after' });
                                }
                            }
                        });
                    }

                    // 선택한 n개 파일 삭제 버튼에 갯수를 갱신 마우스 오버시 오픈 레이어에 선택한 파일명 넣기
                    // 미리보기 드래그바 보이기 또는 감추기 활성 여부
                    obj_this.drag_bar_line_dp(event, eq_idx);
                    
                    return false; // 여기서 더 진행되지 않도록 리턴
                } // end if

 
                /**==============================================
                 * 2::썸네일 이미지 또는 파일명을 드래그 했을 때,  
                 *    그외 => true ( 개체 선택, 영역 드래그 )
                **---------------------------------------------*/
                // ※ 일반적인 드래그
                if( obj_this.drag_flag === true || obj_this.mouse_down_flag=== true ){ 
  
                    //여러개의 파일 선택 사용 유무( 드래그 행위 ) 
                    if(obj_this.multiSelect[eq_idx] !== 'Y'){
         //               return false;
                    }

                    //하일라이트 판넬 동작 ( 사용 금지가 아니라면 )  ghost_move 제외
                    if(obj_this.use_focus_pannel_act[eq_idx] === 'Y' && obj_this.drag_flag === true){
                        obj_this.focus_heighlight_act(event);
                    }

                    /**==============================================
                    ** ※ 마우스 클릭한곳, 무빙에 따른 css 추가 삭제를 통한
                    *    개체 선택과 해제 ( 최소 3픽셀 이상의 움직임이 있을 경우 )
                    **---------------------------------------------*/
                    if( Math.abs(startX - x) > 3 || Math.abs(startY - y) > 3 ){ 
                        // ※ 선택된게 없다면 선택 영역 배열 초기화:: 641 라인에 비슷한게 있음
                        if( obj_this.ctrl_key_flag === false && obj_this.shift_key_flag === false ){     
                            if( event.target.closest(obj_this.file_main_cls + ', ' + obj_this.file_list_cls) ){
                                obj_this.select_ary_reset(event, eq_idx);       // 배열을 비운다
                                // 우선 모든걸 초기화 후 선택된 것만 넣어줄 수 있도록 한다
                                li_obj.forEach(el => el.className = 'one_file'); // 모든 걸 초기화
                                //let before_click  = obj_this.before_click_idx[eq_idx]; // 이전 클릭 했던 곳

                                if(before_click && li_obj[before_click])
                                li_obj[before_click].className = 'one_file thumb_off1'; 
                            } 
                        }

                        // ※ 영역 드래그로 개체 선택하기
                        obj_this.rangeSelect(event, aryIdx);  
                    }

                } // end if 
 

                /**==============================================
                 * 3::미리보기 이동바( 수직바 좌우로 공간 조절 )를 선택해서 움직인다면 
                 *    공간 넓이 조절
                **---------------------------------------------*/
                // ※ 미리보기 이동바 드래그
                if( obj_this.drag_bar_flag === true ){
 
                    let loaderMain  = obj_this.loader_main_obj(eq_idx); 
                    let viewDp      = loaderMain.querySelector(obj_this.view_preview_cls); 
                    let detailDp    = loaderMain.querySelector(obj_this.view_detail_cls);
                    let viewDpWd    = obj_this.view_dp_wd[eq_idx]; //이전 미리보기 공간의 넓이
                //  let moduleWidth = MARI.file_uploder.code.module_width[eq_idx];
                    
                    
                    /*===============================
                    * 현재의 모듈 넓이를 갱신한다 
                    **-----------------------------*/              
                    let moduleWidth = parseInt(loaderMain.offsetWidth) - 20;
                    let bigSizeWd   = parseInt(moduleWidth) - parseInt(obj_this.max_preview_wd) + 12;//전체크기에서 max값을 뺀 크기(모듈 자체가 가변형이라 이렇게 처리함)
                    let minSizeWd   = parseInt(obj_this.min_preview_wd);
                    let moveLeftWd  = parseInt(viewDpWd - (event.clientX - obj_this.viewBarPos_left) + 2);//2 픽셀 왼쪽으로 이동 보정:: 미리보기 창의 넓이와도 같다
                    let objWd       = Math.min(Math.max(moveLeftWd, minSizeWd), bigSizeWd);  
                    let fileMainWd  = loaderMain.querySelector('.file_main').offsetWidth;
                    
                    // 멕스 사이즈 이상되지 않도록
                    //if (objWd > bigSizeWd -16) return false; //16 스크롤바 넓이
                    if (fileMainWd < parseInt(obj_this.max_preview_wd) ) return false; //190
 
                    // 미리보기, 상세보기 영역 넓이 조절
                    [viewDp, detailDp].forEach(el => el.style.width = `${objWd}px`);
 
                    let sliderDom    = loaderMain.querySelector('.slider');
                    // 미리보기 넓이에 따라 이미지 배율용 슬라이더 보이기 감추기 
                    if(sliderDom){
                        if(objWd <= 450){
                            sliderDom.style.display = 'none';
                        } else {
                            sliderDom.style.display = 'inline-block';
                        }
                        
                        let imgCtrL_on  = loaderMain.querySelector('.imgCtrL_on');
                        let imgCtrL_off = loaderMain.querySelector('.imgCtrL_on');
                        let imgCtrR_on  = loaderMain.querySelector('.imgCtrR_on');
                        let imgCtrR_off = loaderMain.querySelector('.imgCtrR_off');
                         
                        if(objWd <= 300){
                            if(imgCtrL_on)  imgCtrL_on.style.display  = 'none';
                            if(imgCtrL_off) imgCtrL_off.style.display = 'none';
                            if(imgCtrR_on)  imgCtrR_on.style.display  = 'none';
                            if(imgCtrR_off) imgCtrR_off.style.display = 'none';
                        } else {
                            if(imgCtrL_on)  imgCtrL_on.style.display  = 'inline-block';
                            if(imgCtrL_off) imgCtrL_off.style.display = 'inline-block';
                            if(imgCtrR_on)  imgCtrR_on.style.display  = 'inline-block';
                            if(imgCtrR_off) imgCtrR_off.style.display = 'inline-block';
                        }
                    }

                    // 하단 이미지 배율용 셀렉트 박스에서 해당 값이 있는 옵션 찾기 
                    let selectInput = loaderMain ? loaderMain.querySelector('.selectInput') : null; 
                    let selectMenu  = loaderMain ? loaderMain.querySelector('.selectMenu') : null;  
                    let inputValue  = selectInput ? selectInput.value : null;// 인풋에 들어간 값  
                    if(selectMenu){
                        selectMenu.selectedIndex = -1; // 일치하는 옵션이 없을 경우는 선택을 하지 않도록
                        for (let i = 0; i < selectMenu.options.length; i++) {
                            if (selectMenu.options[i].text.trim() === inputValue.trim()) {
                                selectMenu.selectedIndex = i; // 일치하는 옵션 선택
                                break; // 옵션을 찾으면 반복 종료
                            }
                        }
                    }
 
                    let focusPanel = obj_this.getFocusPanel(eq_idx);
                    if (focusPanel) focusPanel.style.display = 'none';

                    obj_this.drag_bar_line_dp(event, eq_idx);

                    // 모듈 리사이즈, 미리보기 리사이즈 내용 공유:: 시작  
                    if (obj_this.preview_mode[eq_idx] === 'open') {
                        let previewImg = loaderMain.querySelector('.preview_upload_thumb2');
                        if (previewImg) {
                            // 이미지 리사이즈및 위치를 가운데로:: 리사이즈시 시간차이로 이전 사이즈를 가져오므로 
                            //리사이즈된 리턴된 값을 가지고 아래에서 한번 더 재 측정이 이뤄진다 
                            let preview     = loaderMain.querySelector('.view_preview');
                            let afterImgWd  = preview.offsetWidth - obj_this.PAD;         // 미리보기에서의 이미지 크기 
                            let originalWd  = obj_this.nowImgNaturalWidth[eq_idx];       // 원본 이미지 크기  
                            let originalHt  = obj_this.nowImgNaturalHeight[eq_idx];      // 원본 이미지 크기  
                            let imgPercent  = Math.round(afterImgWd / originalWd * 100); // 퍼센트로 바꾼 비율  
 
                          
                            // 미리보기 리사이즈 바에 의한 좌우 이동에 따른 미리보기 이미지 확대 축소
                            let reResize    = obj_this.imgView_imgResize(eq_idx, 'viewResize', imgPercent, '1389 Line');  
                            let imgPercent2 = Math.round(reResize.newWidth / originalWd * 100);    // 리사이즈 이후 반환된 리사이즈 크기 퍼센트 이미지 비율
                            // 미리보기 트랙볼의 위치 업데이트
                            obj_this.imgViewTrackPosition(event, 'viewResize', eq_idx, imgPercent2, '1284 Line'); 
                            // 이미지 확대,축소 버튼의 모양 변경
                            obj_this.imgView_imgCtrlOnOff(eq_idx, imgPercent2);
                             
                        }
                    }
                    // 모듈 리사이즈, 미리보기 리사이즈 내용 공유:: 끝

                } // end if
                
 
                /*=============================================== 
                 * 4::파일 업로더 영역 넓히고 줄이기 영역 리사이즈
                **---------------------------------------------*/ 
                // ※ 업로더 모듈 리사이즈 드래그
                if (resizeFlag === true){ // 넓이, 높이 리사이즈 상태 플래그 :: 영역 열리고 닫음은 multi_uploader_code.js 1141 라인 즈음

                    let moduleDom      = obj_this.module_obj(obj_this.click_eq);
			        let drop_zone      = loaderMain.querySelector(obj_this.drop_zone_cls); // 파일 로더를 기준으로 접근함
                    let objWd          = obj_this.resize_DomWidth[obj_this.click_eq];  // 업로도 리사이즈 하고자 하는 돔의 클릭시 넓이
                    let objHt          = obj_this.resize_DomHeight[obj_this.click_eq]; // 업로도 리사이즈 하고자 하는 돔의 클릭시 높이
                    let clickX         = obj_this.resize_positionX[obj_this.click_eq]; // 기준점 (예를 들어 특정 요소의 X 좌표)
                    let clickY         = obj_this.resize_positionY[obj_this.click_eq]; // 기준점 (예를 들어 특정 요소의 Y 좌표)
                    let add_width      = objWd - obj_this.PAD + (event.clientX - clickX);// - window.scrollX;
                    let add_height     = objHt + (event.clientY - clickY);// - window.scrollY; 
                    let fileLoader     = document.querySelectorAll(obj_this.loader_cls);
                    let file_main      = loaderMain.querySelector('.file_main');

                    // 일정 부분 움직임이 있다면 업로더의 크기를 키운다
                    if( Math.abs(obj_this.startX5 - add_width ) > 2 || 
                        Math.abs(obj_this.startY5 - add_height) > 2 ){ 
                        let dropZoneHt = add_height - (moduleDom.offsetHeight - file_main.offsetHeight); 
 
                        // 현재 라인에서 가장 높은 이미지의 높이에 맞춰 나머지 높이를 맞춘다 :: 파일 업로더 영역 리사이즈
                        let list_shape   = obj_this.list_shape[eq_idx];  
                        obj_this.equalizeImageHeights(eq_idx, 'resizeDom', list_shape);

                        // 추가 보강 필요 ::2024-11-26(화)
                        // shape8에 대한 설정 
                        if(list_shape === 'shape8'){    
                            const rect2 = moduleDom.getBoundingClientRect(); // 요소의 위치와 크기 정보 가져오기
                            let domWd = parseInt( rect2.left + rect2.width) - obj_this.PAD;  
                            let aryLen = obj_this.shape8TextPositions.length; 
                            for(let i=0; i<aryLen; i++){
                                let aryLen2 = obj_this.shape8TextPositions[i].length; 
                                for(let j=0; j<aryLen2; j++){ 
                                    if(i === 22 || i === 33){// modified_date_in,file_size_in 특정 사이즈 이하일때 작동되도록...
                                    } 
                                    else {
                                        if(domWd <  MARI.file_uploder.code.shape8TextPositions[i][j]){   
                                            loaderMain.querySelectorAll('.'+obj_this.fileInfoClass[i])[j].style.display = 'none'; 
                                        } else {  
                                             loaderMain.querySelectorAll('.'+obj_this.fileInfoClass[i])[j].removeAttribute('style'); 
                                        } 
                                    }
                                }
                            }
                        }
                        // 추가 보강 필요 ::2024-11-26(화)
 
                        //리사이즈 바들에 대한 플래그 설정
                        if (obj_this.resize_bar_flag === true){ // 넓이, 높이 리사이즈 상태 플래그 
                            if(obj_this.resizeWidthAct === true){
                                moduleDom.style.width = add_width+'px';
                            }
                            if(obj_this.resizeHeightAct === true){
                                if(add_height <= 152) return false;
                                moduleDom.style.height = add_height+'px';     
                            }
                        }
                        else
                        if (obj_this.resizeWd_bar_flag === true){ // 넓이 리사이즈 상태 플래그
                            if(obj_this.resizeWidthAct === true){
                                moduleDom.style.width = add_width+'px'; 
                            }
                        }
                        else
                        if (obj_this.resizeHt_bar_flag === true){ // 높이 리사이즈 상태 플래그
                            if(obj_this.resizeHeightAct === true){
                                if(add_height <= 152 ) return false;
                                moduleDom.style.height = parseInt(add_height)+ 13 + 'px'; 
                            } 
                        }

                        // 모듈 리사이즈, 미리보기 리사이즈 내용 공유:: 시작  
                        if (obj_this.preview_mode[eq_idx] === 'open') {
                            let previewImg = loaderMain.querySelector('.preview_upload_thumb2');
                            if (previewImg) { 
                                
                                // 이미지 리사이즈및 위치를 가운데로:: 리사이즈시 시간차이로 이전 사이즈를 가져오므로 
                                //리사이즈된 리턴된 값을 가지고 아래에서 한번 더 재 측정이 이뤄진다 
                                let preview     = loaderMain.querySelector('.view_preview');
                                let afterImgWd  = preview.offsetWidth - obj_this.PAD;         // 미리보기에서의 공간을 기준으로 한 이미지 크기 
                                let originalWd  = obj_this.nowImgNaturalWidth[eq_idx];       // 원본 이미지 크기  
                                let imgPercent  = Math.round(afterImgWd / originalWd * 100); // 퍼센트로 바꾼 비율  
                                let reResize    = obj_this.imgView_imgResize(eq_idx, 'viewResize', imgPercent, '1492 Line');  
                                let imgPercent2 = Math.round(reResize.newWidth / originalWd * 100);    // 리사이즈 이후 반환된 리사이즈 크기 퍼센트 이미지 비율
                                // 미리보기 트랙볼의 위치 업데이트
                                obj_this.imgViewTrackPosition(event, 'viewResize', eq_idx, imgPercent2, '1387 Line'); 
                                // 이미지 확대,축소 버튼의 모양 변경
                                obj_this.imgView_imgCtrlOnOff(eq_idx, imgPercent2); 
                                
                            }
                        }
                        // 모듈 리사이즈, 미리보기 리사이즈 내용 공유:: 끝
                    }

                    // 포커스 판넬 안보이게
                    let focus_panel  = obj_this.getFocusPanel(eq_idx); // 하일라이트 판넬  

                    // 포커스 판넬 리셋 
                    obj_this.focusPanelHide();

                    // 미리보기 드래그바 보이기 또는 감추기 활성 여부
                    obj_this.drag_bar_line_dp(event, eq_idx);

                } // end if
			});
            
        }, // end drag_selection_mouse_move 


		/*===================================
		 * 5. 마우스 오버
		**---------------------------------*/ 
		drag_selection_mouse_over : function(){   
			let obj_this      = MARI.file_uploder.code; 
			let drop_zone_cls = obj_this.drop_zone_cls;
            
            document.addEventListener('mouseover', function(event) {

                // 현재 마우스가 위치한 곳의 eq_idx를 기록해서 사용할 수 있도록 마우스 다운, 업, 오버, 아웃등 다양한 이벤트에 대응하도록 하려고 했으나
                // 간혹 원하는 결과 값이 나오지 않아서 eq_idx의 값이 올바르지 않을 때가 있다 그에 대한 보완책으로 사용한다ㅑ::2025-01-29(수)
                if (event.target.closest(obj_this.loader_cls) !== null) { 
                    obj_this.eq_idx = event.target.closest(obj_this.loader_cls).getAttribute('obj_eq');
                }

                if (event.target.matches(drop_zone_cls)) {
                    // file_list에서의 마우스 다운으로 이벤트가 버블링 되지 않도록
                    event.stopPropagation(); // 상위 개체로 이벤트 전파됨을 막아줌
                    event.preventDefault();  // 창 이동이나 새로 고침등을 막아주는 이벤트 

                    /**==============================
                    ** 마우스 오버시 eq_idx 변경
                    **-----------------------------*/
                    let elements = document.querySelectorAll(drop_zone_cls);
                    obj_this.eq_idx = Array.prototype.indexOf.call(elements, event.target);                    
                    obj_this.before_scrollTop = event.target.scrollTop;
                }
            });
		},
 
		/*===================================
		 * 6. 마우스 아웃
		**---------------------------------*/
		drag_selection_mouse_out : function(){ 
			let obj_this     = MARI.file_uploder.code; 
			let loader_cls   = obj_this.loader_cls; 
			let one_file_cls = obj_this.one_file_cls;
 
            document.addEventListener('mouseout', function(event) {
                if (event.target.matches(one_file_cls)) {
                    if (!event.target.classList.contains('thumb_chk1')) {
                        document.querySelectorAll(one_file_cls).forEach(function(el) {
                            el.removeAttribute('style');
                        });
                    }

                    // 마우스 오버시 해당 파일의 정보 닫기
                    clearTimeout(obj_this.set_time_var2);

                    // 파일 정보 레이어 플래그 설정
                    let target_type = obj_this.is_thumbnail(event); // 썸네일 선택 혹은 주변 공간 선택 구분 
                    if (target_type !== 'thumbnail') {
                        obj_this.over_flag = false;
                    }
                }
            });

		},


		/*===================================
		 * 7. 키보드 key down  
		**---------------------------------*/
		drag_selection_key_down : function(){
			let obj_this        = MARI.file_uploder.code;
            window.addEventListener('keydown', function(event) {  
                let mouse_down_flag = obj_this.mouse_down_flag;

                // SHIFT 16
                if (event.keyCode === 16) { 
                    obj_this.shift_key_flag = true;
                    document.querySelector('.help_key1').innerHTML = '<span class="blue">Shift</span>';
                    event.preventDefault();
                    return false;
                }

                // Ctrl 17
                if (event.keyCode === 17) { 
                    obj_this.ctrl_key_flag = true;
                    document.querySelector('.help_key2').innerHTML = '<span class="blue">Ctrl</span>';
                    event.preventDefault();
                    return false;
                }
            });

		},

        /*===================================
         * 8. 키보드 key up
        **---------------------------------*/ 
		drag_selection_key_up : function(){
			let obj_this      = MARI.file_uploder.code; 
            window.addEventListener('keyup', function(event) {  
                let mouse_down_flag = obj_this.mouse_down_flag;

                // SHIFT 16
                if (event.keyCode === 16) { 
                    obj_this.shift_key_flag = false;
                    document.querySelector('.help_key1').innerHTML = '<span class="gray">Shift</span>';
                    event.preventDefault();
                    return false;
                }

                // Ctrl 17
                if (event.keyCode === 17) { 
                    obj_this.ctrl_key_flag = false;
                    document.querySelector('.help_key2').innerHTML = '<span class="gray">Ctrl</span>';
                    event.preventDefault();
                    return false;
                }
            });

		}, 

		/*===================================
		 * 9. 스크롤 위치 이벤트
		**---------------------------------*/ 
		drag_selection_mouse_scroll : function(){
			let obj_this      = MARI.file_uploder.code;
			let drop_zone_cls = obj_this.drop_zone_cls;  
            let eq_idx        = obj_this.event_eq(event);    //사용 가능하게 설정된 업로더에서의 인덱스 
            let loaderMain    = obj_this.loader_main_obj(eq_idx);
            if (loaderMain && loaderMain.querySelector) {
                let focusPanel    = loaderMain.querySelector(obj_this.focus_pannel_cls); // 미리보기 공간 넓이 조정  
                if(!focusPanel) return false;
                let panel_ht      = parseInt(obj_this.panel_ht); // 마우스 휠 이동시 사용

                loaderMain.addEventListener('scroll', function(event) { 
                    let before     = obj_this.scroll_top;
                    let after      = loaderMain.scrollTop;
                    let gap_scroll = after - before;
                    let css_top    = null;
                    let css_height = null;

                    if (gap_scroll >= 0) {
                        css_top    = parseInt(window.getComputedStyle(focusPanel).top, 10); 
                        css_height = Math.abs(panel_ht + gap_scroll); 
                    } else {
                        css_top    = obj_this.panel_top - Math.abs(panel_ht + gap_scroll); 
                        css_height = Math.abs(panel_ht + gap_scroll);
                    }

                    focusPanel.style.top    = css_top + 'px';
                    focusPanel.style.height = css_height + 'px';
                }); 
            }
		},

		/*===================================
		 * 10. 관련 기타 함수
		 *
		 * 10-1. 삭제 버튼 마우스 오버  ( 선택한 n개 파일 삭제 )
		 * 드래그로 선택된 개체 리스트명 팝업 열기( mouseover )
		 * 드래그로 선택된 개체 리스트명 팝업 닫기( mouseout )
		**---------------------------------*/ 
        // 나중에 다시 손을 볼것 
		drag_selection_etc : function(){
			let obj_this = MARI.file_uploder.code;
			let obj_util = MARI.file_uploder.util;
            document.querySelectorAll('.n_del_bt').forEach(function(btn, idx) {
                btn.addEventListener('mouseenter', function(event) {
                    // let idx = obj_this.drag_eq_idx;
                    if (obj_this.shift_key_flag === false) {
                        let nDelBtWrap = document.querySelectorAll('.n_del_bt_wrap')[idx];
                        if (getComputedStyle(nDelBtWrap).display === 'none') { 
                            obj_util.fadeIn(nDelBtWrap,  200);
                        }
                    }
                });

                btn.addEventListener('mouseleave', function(event) {
                    // let idx = obj_this.drag_eq_idx;
                    let nDelBtWrap = document.querySelectorAll('.n_del_bt_wrap')[idx];
                    if (getComputedStyle(nDelBtWrap).display === 'block') {
                        setTimeout(function() {
                            if (!event.target.closest('.n_del_bt_wrap, .n_del_bt_alt, .n_del_bt_alt_cont')) {  
                               if( event.target.className){ 
                                 obj_util.fadeOut(nDelBtWrap,  200);
                               }
                            }
                        }, 50);
                    }
                });

                document.querySelectorAll('.n_del_bt_alt_cont').forEach(function(bt_alt, idx) {
                    bt_alt.addEventListener('mouseleave', function(event) { 
                        if( event.target.className){
                            let nDelBtWrap = document.querySelectorAll('.n_del_bt_wrap')[idx];
                            obj_util.fadeOut(nDelBtWrap,  200);
                        }
                    }); 
                });
            });
		},


		/*=========================================================================================
		 * 11. 리셋 관련 
		**----------------------------------------------------------------------------------------*/
		select_ary_reset : function(event, eq_idx){  
			let obj_this = MARI.file_uploder.code; 
			let f_len    = document.querySelectorAll(obj_this.loader_main_cls).length; 
			if(eq_idx){// 선택된 업로더만  
                MARI.file_uploder.code.drag_select_ary[eq_idx] = { objType:[], name: [], idx: [], code: [] };// 배열을 비운다
                // 히스토리 입력 
                obj_this.historyEvent(eq_idx,  '선택된 업로더 리셋', 'red');
			}
			else{ // 업로더 전부 
				for(let i=0; i < f_len; i++){
                    MARI.file_uploder.code.drag_select_ary[i] = { objType:[], name: [], idx: [], code: [] };// 배열을 비운다
				} 
                // 히스토리 입력 
                obj_this.historyEvent(eq_idx,  '모든 업로더 리셋', 'red'); 
			}

			MARI.file_uploder.code.shift_start_num = null; // SHIFT키를 사용할 때 시작 개체
			MARI.file_uploder.code.shift_end_num   = null; // SHIFT키를 사용할 때 끝 개체 
            // 선택한 n개 파일 삭제에 마우스 오버시 사용되는 레이어 기능
            obj_this.select_name_list(event);

		},


		/*=========================================================================================
		 * 12. 클릭한 곳의 인덱스를 선택 배열에 등록한다   css 입혀주는 곳으로 옮김
         *     드래그를 했는데도 불구하고 배열에 누적 저장이 되지 않았다면 getDatas가 되지 않았는지 살펴 볼 것
		**----------------------------------------------------------------------------------------*/
		set_drag_select_ary : function(event, objType, addType, eq_idx2, click_idx, click_type, print_num){
			let obj_this   = MARI.file_uploder.code;
            let eq_idx     = obj_this.event_eq(event);    //사용 가능하게 설정된 업로더에서의 인덱스   
            let select_ary = MARI.file_uploder.code.drag_select_ary;
            let getListIdx = obj_this.getListDataAryIdx(eq_idx, click_idx) - 1;    // 데이터 코드에는 1부터 증가이므로 빼준다  click_idx와 같음
            if( getListIdx < 0) return false;

            let getDatas   = obj_this.getFileInfo2(eq_idx, objType, getListIdx, 'All', objType + ' - 1463 Line set_drag_select_ary'); // 데이터 

            if(!getDatas || getDatas === null) { 
                obj_this.historyEvent(eq_idx,  'set_drag_select_ary 메서드에서 getDatas 값이 null 입니다','yellow'); 
            }

			if( getDatas && click_idx > -1 ){ 
                let dataCode   = getDatas.dataCode;
                let sel_name   = getDatas.dataFileName;

				if( addType === 'one' || typeof(select_ary[eq_idx]) === 'undefined'){ 
				 	obj_this.select_ary_reset(event, eq_idx); // 처음 시작시 공간을 확보한다
				}

				/*===============================
				 * ※ 드래그로 선택된 것을 배열에 차곡 차곡 쌓는다
				**-----------------------------*/
				if(addType === 'add' || addType === 'one'){ // 배열에 차곡 차곡 쌓는다
                    if (!select_ary[eq_idx]['idx'].includes(click_idx)) { 
                        select_ary[eq_idx]['objType'].push(objType); 
                        select_ary[eq_idx]['code'].push(dataCode); 
                        select_ary[eq_idx]['idx'].push(click_idx);
                        select_ary[eq_idx]['name'].push(sel_name);   
                    }
				}
				if(addType === 'minus'){ // 배열에서 선택된 항목을 삭제한다
                    // 해당 인덱스가 존재하면 'idx', 'code', 'name'에서 삭제
                    let removeIndex = select_ary[eq_idx]['idx'].indexOf(click_idx); 
                    if (removeIndex !== -1) {
                        select_ary[eq_idx]['idx'].splice(removeIndex, 1);
                        select_ary[eq_idx]['code'].splice(removeIndex, 1);
                        select_ary[eq_idx]['name'].splice(removeIndex, 1);
                    }
				}
			}

            // 선택한 n개 파일 삭제에 마우스 오버시 사용되는 레이어 기능
            obj_this.select_name_list(event);

        },


		/*=========================================================================================
		 * 13. 필요 단품 기능 : 시작
		**----------------------------------------------------------------------------------------*/
        // 선택한 n개 파일 삭제에 마우스 오버시 사용되는 레이어 기능
 		select_name_list : function(event){
			let obj_this   = MARI.file_uploder.code; 
            let eq_idx     = obj_this.event_eq(event);    //사용 가능하게 설정된 업로더에서의 인덱스
			let select_ary = obj_this.drag_select_ary;
            let loaderMain = obj_this.loader_main_obj(eq_idx); 
            let select_cnt = obj_this.selectCnt(eq_idx);  // 드래그 선택중인 갯수 리턴
            let name_list  = '';
 
			for(let j=0; j < select_cnt; j++){  //select_cnt
				let file_name = select_ary[eq_idx]['name'][j];
				if(typeof(file_name) !=='undefined'){
					file_name = file_name.replace(/^\s+|\s+$/g, ""); //이름 다음에 공백이 들어가서 제거
					if(j < select_cnt-1){ 
						name_list += '<span>'+file_name+',</span>';
					}
					else{
						name_list += '<span>'+file_name+'</span>';
					} 
				}
			}

			if(name_list === ''){
				name_list = '선택된 파일이 없습니다.';
			}
            
            // 오픈 레이어에 선택된 파일명 넣기
            if(loaderMain.querySelector('.n_del_bt_alt_cont'))
            loaderMain.querySelector('.n_del_bt_alt_cont').innerHTML = name_list;

            // 선택한 n개 파일 삭제에 갯수를 갱신 
            if(loaderMain.querySelector('.del_file_cnt'))
            loaderMain.querySelector('.del_file_cnt').innerHTML = select_cnt;

        },


		/*=========================================================================================
		 * 14.레이아웃 관련 : 시작
		**----------------------------------------------------------------------------------------*/
		file_info_layer_process : function(event){
			let obj_this     = MARI.file_uploder.code;   
			let one_file_cls = obj_this.one_file_cls; 
            document.addEventListener('mouseenter', function(event) {//mouseover
                if (event.target instanceof Element && event.target.matches(one_file_cls)) {
                    let eq_idx    = obj_this.event_eq(event);    //사용 가능하게 설정된 업로더에서의 인덱스  
                    let evt_idx   = obj_this.event_index(event, obj_this.one_file_cls);   // 클릭, 드래그시 선택된 evt_idx
                    let objType   = obj_this.getMakeType(eq_idx, evt_idx);
                    let getListIdx = obj_this.getListDataAryIdx(eq_idx, evt_idx) - 1;    // 데이터 코드에는 1부터 증가이므로 빼준다  
                //  let getDatas  = obj_this.getFileInfo2(eq_idx, objType, getListIdx, 'All', '1559'); // 데이터
                    
                    // 불러온 데이터를 기반으로 팝업 내용을 넣어준다
                    let titleMsg ='';
                    titleMsg += '항목유형:image jpeg';
                    titleMsg += '사진크기:image jpeg';
                    event.target.alt = 'Your alt text here'+titleMsg; // alt 속성 설정 
                }
            }, true); // 캡처링 모드를 활성화해 `mouseenter`를 유사하게 처리

        },
            
		file_info_layer_process2 : function(event){ // 다시 만들것
			let obj_this     = MARI.file_uploder.code;   
			let one_file_cls = obj_this.one_file_cls; 

			/**==============================
			** 마우스 오버시 파일 정보 레이어 만들고 오픈
			**-----------------------------*/ 
            document.addEventListener('mouseenter', function(event) {//mouseover

                // event.target이 Element인지 확인
                if (event.target instanceof Element && event.target.matches(one_file_cls)) { 
                    // 여기에 원하는 코드를 작성합니다.
                    let obj_util   = MARI.file_uploder.util;
                    let obj_this   = MARI.file_uploder.code; 
                    let eq_idx     = obj_this.event_eq(event);    //사용 가능하게 설정된 업로더에서의 인덱스  
                    let loaderMain = obj_this.loader_main_obj(eq_idx);
                    let li_obj     = obj_this.li_obj(eq_idx);
                    let evt_idx    = obj_this.event_index(event, obj_this.one_file_cls);   // 클릭, 드래그시 선택된 evt_idx
                    let objType    = obj_this.getMakeType(eq_idx, evt_idx); 
                    let getListIdx = obj_this.getListDataAryIdx(eq_idx, evt_idx) - 1;
                    let getDatas   = obj_this.getFileInfo2(eq_idx, objType, getListIdx, 'All', '1591'); // 데이터

                    //리스트의 정보를 가져온다
                    let msgInfo = {
                        'altmsg1' : getDatas.dataFileName,
                        'altmsg2' : getDatas.dataFileType2,
                        'altmsg3' : getDatas.dataFileSize+' ( ' + getDatas.dataMbSize + ' 바이트)',
                        'altmsg4' : getDatas.lastModifiedDate,
                        'altmsg5' : getDatas.dataImgWidth +'픽셀',
                        'altmsg6' : getDatas.dataImgHeight +'픽셀', 
                    };

                    // 돔을 생성한다
                    obj_this.info_layer_make(event, msgInfo);  

                    // 모든 .file_info_div 요소를 선택
                    const fileInfoDivs = document.querySelectorAll('.file_info_div');

                    // 애니메이션을 중지하고 최종 스타일을 적용하는 함수
                    function stopAnimationAndApplyStyles55(index) {
                        const fileInfoDiv = fileInfoDivs[index];
                        if (fileInfoDiv) {
                            // CSS transition 제거
                            fileInfoDiv.style.transition = 'none';

                            // 현재 스타일을 즉시 적용
                            const computedStyle = window.getComputedStyle(fileInfoDiv);
                            fileInfoDiv.style.opacity = computedStyle.opacity;
                            fileInfoDiv.style.display = computedStyle.display;

                            // 스타일을 적용 후, 다시 transition 설정 (필요시)
                            requestAnimationFrame(() => {
                                fileInfoDiv.style.transition = 'opacity 0.5s ease-in-out';
                            });
                        }
                    }
                    // 애니메이션 실행
                    function animateFileInfoDiv(index) {
                        if (fileInfoDivs[index]) {
                            const fileInfoDiv = fileInfoDivs[index];
                            // 초기 스타일 설정
                            fileInfoDiv.style.display = 'block';
                            fileInfoDiv.style.opacity = '0';
                            fileInfoDiv.style.transition = 'opacity 0.5s';

                            // 애니메이션 타이밍
                            setTimeout(function() {
                                fileInfoDiv.style.opacity = '1'; // Fade in
                            }, 1000); // 1초 지연

                            setTimeout(function() {
                                fileInfoDiv.style.opacity = '0'; // Fade out
                            }, 6000); // 6초 후 Fade out 시작

                            setTimeout(function() { 
                                fileInfoDiv.style.display = 'none'; // 0.5초 후 숨김
                            }, 6500); // Fade out 완료 후 숨김 
                        }
                    } 

                    if( obj_this.drag_flag === false && obj_this.mouse_down_flag === false){ // 드래그와 선택중이 아니라면 
                        // 호출 예시
                        animateFileInfoDiv(eq_idx); // eq_idx는 사용자의 이벤트 코드에 의해 결정됨
                    }
                }
            }, true); // 캡처링 모드를 활성화해 `mouseenter`를 유사하게 처리
 
			// 정보창을 마우스 위치에서 오픈 시킨다 
            document.addEventListener('mousemove', function(event) {
                
                if ( !event.target.matches(one_file_cls)) {
                    return false;//추가
                }
                // `one_file_cls` 요소가 이벤트의 대상인지 확인
                if (event.target.matches(one_file_cls)) {
                    let obj_this = MARI.file_uploder.code; 
                    let eq_idx   = obj_this.event_eq(event);    //사용 가능하게 설정된 업로더에서의 인덱스  

                    // 정보창의 위치를 마우스 위치에 맞게 조정
                    let obj_top = event.clientY + 18;
                    let obj_left = event.clientX;
                    const fileInfoDivs = document.querySelectorAll('.file_info_div');
                    if (fileInfoDivs[eq_idx]) {
                        fileInfoDivs[eq_idx].style.top = `${obj_top}px`;
                        fileInfoDivs[eq_idx].style.left = `${obj_left}px`;

                        if (obj_this.drag_flag === false && obj_this.mouse_down_flag === false) {
                            fileInfoDivs[eq_idx].style.display = 'block';
                            fileInfoDivs[eq_idx].style.opacity = '0';
                            fileInfoDivs[eq_idx].style.transition = 'opacity 0.5s';

                            // 애니메이션
                            setTimeout(function() {
                                fileInfoDivs[eq_idx].style.opacity = '1';
                            }, 1000);

                            setTimeout(function() {
                                fileInfoDivs[eq_idx].style.opacity = '0';
                            }, 6000);

                            setTimeout(function() {
                                fileInfoDivs[eq_idx].style.display = 'none';
                            }, 6500);
                        }
                    }
                }
            }); 

            // 개체 위에서 클릭이 이뤄졌다면 정보창을 즉시 닫는다
            document.addEventListener('mousedown', function(event) {
                // `one_file_cls` 요소가 이벤트의 대상인지 확인
                if (event.target.matches(one_file_cls)) {
                    let obj_this = MARI.file_uploder.code; 
                    let eq_idx   = obj_this.event_eq(event);    //사용 가능하게 설정된 업로더에서의 인덱스  

                    // 모든 file_info_div 요소를 숨깁니다
                    const fileInfoDivs = document.querySelectorAll('.file_info_div');
                    if (fileInfoDivs[eq_idx]) {
                        fileInfoDivs[eq_idx].style.display = 'none';
                    } 
                }
            });
 
            // 개체 위에서 마우스 아웃이면 정보창을 닫는다
            document.addEventListener('mouseleave', function(event) {
                if (!event.relatedTarget || event.relatedTarget.nodeName === "HTML") {
                    // 마우스가 창 밖으로 나갔을 때 실행할 코드
                    return; // 창 밖으로 나갔으면 함수를 종료
                }
                
                let obj_this = MARI.file_uploder.code;
                // one_file_cls 요소가 이벤트의 대상인지 확인
                if (event.target.matches('.'+ obj_this.one_file_cls)) { 
                    let obj_this = MARI.file_uploder.code; 
                    let eq_idx   = obj_this.event_eq(event);    //사용 가능하게 설정된 업로더에서의 인덱스  

                    // 모든 file_info_div 요소를 감춤
                    const fileInfoDivs = document.querySelectorAll('.file_info_div');
                    if (fileInfoDivs[eq_idx]) {
                        fileInfoDivs[eq_idx].style.display = 'none';
                    } 
                }
            });  
		},


		/*=========================================================================================
		 * 15. 파일의 위치 정보
		 *  파일 미리보기 안에 들어 있는 파일들의 위치 정보를 기록한다
		 *  마우스 다운시 마다 정보가 입력되니 파일 추가등 변황 있을때 마다 
		 *  넣을수 있도록	 변화 시킬 것
		 *  obj_this.code.drag_position_ary
		**---------------------------------------------------------------------------------------*/   
		position_info : function(){

			/*===========================================
			** 파일의 위치 정보
			** 파일 미리보기 안에 들어 있는 파일들의 위치 정보를 기록한다
			** 마우스 다운시 마다 정보가 입력되니 파일 추가등 변황 있을때 마다 
			** 넣을수 있도록	 변화 시킬 것
			**  MARI.file_uploder.code.drag_position_ary
			**-----------------------------------------*/  
			let obj_this      = MARI.file_uploder.code;  
            let eq_idx        = obj_this.event_eq(event);    // 사용 가능하게 설정된 업로더에서의 인덱스 
			let loader_cls    = obj_this.loader_cls;  
			let drop_zone_cls = obj_this.drop_zone_cls;
			let file_list_cls = obj_this.file_list_cls;      // 드랍 영역 클래스 명
			let one_file_cls  = obj_this.one_file_cls;

            if(eq_idx < 0) return false;
            let loaderMain    = obj_this.loader_main_obj(eq_idx); 
            let fileLoader    = document.querySelectorAll(obj_this.loader_cls);     
			let drop_zone     = loaderMain.querySelector(obj_this.drop_zone_cls); // 파일 로더를 기준으로 접근함
            let loader_cnt    = fileLoader.length;
            let fileListElms  = loaderMain.querySelectorAll(one_file_cls);
			let positionAry   = []; // 셀렉션을 통해 필터를 사용 선택해 주기 위해서
 
            let listCnt = fileListElms.length;
            for (let j = 0; j < listCnt; j++) {
                let fileNameElm  = fileListElms[j].querySelector('.file_name');
                let fileName     = fileNameElm ? fileNameElm.textContent.trim() : '';
                let rect         = fileListElms[j].getBoundingClientRect();
                let dropZoneRect = drop_zone.getBoundingClientRect();
                let scrollTop    = window.pageYOffset || document.documentElement.scrollTop;
                let scrollLeft   = window.pageXOffset || document.documentElement.scrollLeft;
                let domTop       = rect.top - dropZoneRect.top + scrollTop;
                let domLeft      = rect.left - dropZoneRect.left + scrollLeft;
                let domWidth     = rect.width - 2;
                let domHeight    = rect.height - 2;
                let domRight     = domLeft + domWidth;
                let domBottom    = domTop + domHeight;
                let dataCodes  = fileListElms[j].getAttribute('data-code');
                let parts      = dataCodes.split('_');
                let dataIdx    = parseInt(parts[parts.length - 1]) - 1;
                positionAry[j] = {
                    'index'    : j,
                    'dataCode' : fileListElms[j].getAttribute('data-code'),
                    'dataIdx'  : dataIdx,
                    'name'     : fileName,
                    'left'     : domLeft,
                    'right'    : domRight,
                    'top'      : domTop,
                    'bottom'   : domBottom,
                };
            } 

		    // 영역 설정을 위해서 추가해 준 부분 2023.11.20 
			obj_this.drag_position_ary[eq_idx] = positionAry; // 배열 정보를 넣어준다
            
            // 히스토리 입력
            let msgAry = ['파일의 위치 정보', positionAry];
            obj_this.historyEvent(eq_idx,  msgAry, 'pink'); 
 
		},


        // 특정 배열을 오름차순으로 만들고 지정한 숫자 이후의 숫자를 먼저, 그리고 그 숫자보다 작은 숫자를 나중에 배치하여 정렬
        reorderArray : function (arr, target) {
            // 배열을 오름차순으로 정렬
            const sorted = [...arr].sort((a, b) => a - b);

            // target 기준으로 분리
            const greaterOrEqual = sorted.filter(num => num >= target);
            const less = sorted.filter(num => num < target);

            // 합쳐서 결과 반환
            return [...greaterOrEqual, ...less];
        },


		/*=================================================
		 * 16. 고스트 이미지를 만들어 준비 시킨다 
		**-----------------------------------------------*/ 
		ghost_image_make : function(event, eq_idx){
			let obj_this    = MARI.file_uploder.code; 
            let loaderMain  = obj_this.loader_main_obj(eq_idx); 
            let evt_idx     = obj_this.event_index(event, obj_this.one_file_cls); // 클릭, 드래그시 선택된 idx 
            let selectArray = MARI.file_uploder.code.drag_select_ary[eq_idx]['idx'];
            let ghostDom    = loaderMain.querySelector('.ghost_img');
			let li_obj      = obj_this.li_obj(eq_idx);
            let select_cnt  = selectArray.length;  // 드래그 선택중인 갯수 리턴
            let drag_cnt    = (select_cnt > 5 ) ? 5 : select_cnt;
            let reIdxAry    = obj_this.reorderArray(selectArray, evt_idx);
            let selectCnt   = reIdxAry.length;
            let addCls      = (selectCnt === 1) ? ' one' : '';
            let ghostImgs   = '<div class="ghost_thumb_panel"></div>';
            if (selectCnt > 1) {
                ghostImgs += `<div class="ghost_img_cnt" data-cnt="${select_cnt}">${select_cnt}</div>`;
            }
 
            for(let i=0; i<drag_cnt; i++){
                let idx = reIdxAry[i];
                let uploadThumb = li_obj[idx].querySelector('.upload_thumb');
                if(uploadThumb){
                    ghostSrc    = uploadThumb.getAttribute('src');
                    ghostImgs += `
                        <div class="ghost_img_wrap${addCls}">
                            <div class="ghost_thumb_wrap">
                                <img src="${ghostSrc}" class="upload_thumb">
                            </div>
                        </div>`;
                }
            }

            // ghostDom 요소가 존재하지 않으면 생성
            if (ghostDom === null) {
 			     let click_idx = obj_this.click_idx[eq_idx];
                 if( li_obj[click_idx] ){
                    let liRect    = li_obj[click_idx].getBoundingClientRect(); 
                    let imgDom = '<div class="ghost_img" style="top:'+liRect.top+'px;left:'+liRect.left+'px;">' + ghostImgs + '</div>';
                    let fileListObj   = loaderMain.querySelector(obj_this.file_list_cls);  
                    fileListObj.insertAdjacentHTML('afterend', imgDom);
                 }
            }
            
            // 히스토리 입력 
            obj_this.historyEvent(eq_idx,  '고스트 이미지를 생성 한다'); 
        },



// 다른 파일로 옮길것 드래그에 관련된 것만 들어갈 수 있도록
		/*=========================================================================================
		 * 17. 대표 썸네일 선택과 에니메이션 선택
		**---------------------------------------------------------------------------------------*/ 
		thumb_choice : function(eq_idx, numIdx){ // 업로드시 클릭등의 이벤트가 없으므로 직접 코드를 할당한다
			let obj_util     = MARI.file_uploder.util; 
			let obj_this     = MARI.file_uploder.code;
            let li_obj       = obj_this.li_obj(eq_idx); 
            let loaderMain   = obj_this.loader_main_obj(eq_idx);
            let inputElems   = document.querySelectorAll('input[name="list_thumbnail"]');
            let allChoiceDiv = loaderMain.querySelectorAll(obj_this.choice_div_cls);
            let totalCnt     = obj_this.file_count_return(eq_idx, 'totalCnt');  // 업로드 전체 갯수 

			// 대표 썸네일 선택
            if(totalCnt <= 0 ){ return false; }
            
            /* 대표 썸네일이 지정되어 있다면 지정번호로 비어 있다면 첫번째를 선택 선택이 들어오면 선택을 최우선으로*/  
            let thumb_val = (numIdx !== undefined && numIdx !== null) ? numIdx : (inputElems[eq_idx].value || 0); 
            
            // ※value값과 리스트의 갯수가 맞지 않으면 0으로 설정
            if( typeof(li_obj[numIdx]) === 'undefined' ){
                numIdx    = 0;
                thumb_val = 0;
            } //numIdx 변경이 있을 수 있으므로 choiceDiv는 아래에 기재

            if(li_obj.length <= 0)return false;

            let choiceDiv = li_obj[numIdx].querySelector(obj_this.choice_div_cls);

            // 끝나는 시점에 에니메이션
            obj_this.totalInFileAni(eq_idx, numIdx);

			//선택한 썸네일 인덱스를 인풋에 넣어준다
            document.querySelectorAll('input[name="list_thumbnail"]')[eq_idx].value = thumb_val; //바뀐 인덱스 번호 
			/*===============================
			 * 선택된 리스트에 대표 버튼 보이게 하기
			**-----------------------------*/   
            let allThumb_bt = loaderMain.querySelectorAll(obj_this.thumb_bt_cls);  
            let thumbButtons = loaderMain.querySelectorAll('.thumb_bt'); 
            thumbButtons.forEach(el => el.classList.remove('on')); 

            // 각 요소에 대해 'on' 클래스를 제거
            allThumb_bt.forEach(function(elem) {
                elem.classList.remove('on');
            });

            li_obj[numIdx].classList.remove('thumb_off1'); 
            li_obj[numIdx].querySelector(obj_this.thumb_bt_cls).classList.add('on'); 

            // 선택된 choice_div에 대해 작업을 수행
            if (choiceDiv) {
                // 두 번째 loader_main의 모든 one_file의 choice_div를 display: none으로 설정
                allChoiceDiv.forEach(el => el.style.display = 'none');   
                choiceDiv.style.display = 'block'; // 예를 들어 display를 block으로 설정
            } 
            // 히스토리 입력 
            obj_this.historyEvent(eq_idx,  (parseInt(thumb_val)+1)+'번 파일 대표 썸네일로 선택','orange');

        },

		scroll_add_x_num  : 0,
		scroll_add_y_num  : 0,
		scroll_add_x_num2 : 0,
		scroll_add_y_num2 : 0,
		wheel_move        : null,
		panel_wd          : 0, // 포커스 하이라이트의 넓이
        panel_left        : 0, // 포커스 하이라이트의 x 
		panel_ht          : 0, // 포커스 하이라이트의 높이
		panel_top         : 0, // 포커스 하이라이트의 y
		scroll_top        : 0, // 포커스 하이라이트의 높이
		scroll_left       : 0, // 포커스 하이라이트의 넓높이
		/*=================================================
		 * 18. 포커스 하일라이트 판넬 스크롤링
		**-----------------------------------------------*/
		scroll_act : function(event){
			/**==============================================
			** 포커스 하일라이트 판넬 스크롤링
			**---------------------------------------------*/ 
 			let obj_this       = MARI.file_uploder.code;   
			let drop_zone_cls  = obj_this.drop_zone_cls;  
            let eq_idx         = obj_this.event_eq(event); // 사용 가능하게 설정된 업로더에서의 인덱스
            let ul_obj         = obj_this.ul_obj(eq_idx);                        // .file_list 클래스의 ul 
            let drop_zone_elem = document.querySelector(obj_this.drop_zone_cls); // drop_zone_cls 선택자에 맞는 요소  
            let loaderMain     = obj_this.loader_main_obj(eq_idx); 
            let fileMain       = loaderMain.querySelector('.file_list_header'); //드래그 이벤트를  drop_zone에서 file_main으로 변경 2024-11-27(수)
			let drop_zone_wd   = drop_zone_elem.offsetWidth;                     // - 2;  padding과 border 포함한 너비
			let drop_zone_ht   = drop_zone_elem.offsetHeight;                    // - 2; 
			let drop_zone_pl   = (event.pageX - drop_zone_elem.getBoundingClientRect().left + window.scrollX); // 페이지의 X 위치와 비교  window.scrollX 추가되었음
			let drop_zone_pt   = (event.pageY - drop_zone_elem.getBoundingClientRect().top  + window.scrollY);  // 페이지의 Y 위치와 비교  window.scrollY 추가되었음
			let file_list_wd   = ul_obj.offsetWidth;  // padding과 border 포함한 높이
			let file_list_ht   = ul_obj.offsetHeight; // padding과 border 포함한 높이 
			let add_rate       = 0;
            let dropZoneElems  = document.querySelectorAll(drop_zone_cls);

			/**==============================================
			**  좌우로 스크롤링
			**---------------------------------------------*/
			// 스크롤이 필요없는 범위 안이라면  리셋  null 
			if(drop_zone_pl > 5 && drop_zone_wd - drop_zone_pl > 5){ 
		 		MARI.file_uploder.code.scroll_add_x_num = null;
			}

			// 처음 시도시, 스크롤이 필요없는 범위 안이라면 =>스크롤의 현재 높이를 넣어준다
			if(obj_this.scroll_add_x_num === null){ 
                obj_this.scroll_add_x_num = document.querySelectorAll(drop_zone_cls)[eq_idx].scrollLeft;
			}

			/**==================================
			** 좌측단으로 드래그를 벗어나게 했을 때
			**---------------------------------*/
			if(drop_zone_pl <= 5){
				let wd_range = Math.abs(0 - drop_zone_pl);
				add_rate     = wd_range * 10;
				if( parseInt(document.querySelectorAll(drop_zone_cls)[eq_idx].scrollLeft) > 0){
				 	parseInt(document.querySelectorAll(drop_zone_cls)[eq_idx].scrollLeft) -= add_rate; // ※ 스크롤 이동 
					obj_this.scroll_add_x_num  = document.querySelectorAll(drop_zone_cls)[eq_idx].scrollLeft;
				}
				else{ 
					 document.querySelectorAll(drop_zone_cls)[eq_idx].scrollLeft = 0; 
				}
			}

			/**==================================
			** 우측단으로 드래그를 벗어나게 했을 때
			**---------------------------------*/
            if (drop_zone_wd - drop_zone_pl <= 5) {
                let wd_range = Math.abs(drop_zone_wd - drop_zone_pl);
                add_rate     = wd_range * 10;

                if (dropZoneElems[eq_idx].scrollLeft < file_list_wd) {
                    dropZoneElems[eq_idx].scrollLeft += add_rate; // ※ 스크롤 이동 
                    obj_this.scroll_add_x_num = dropZoneElems[eq_idx].scrollLeft;
                } else {
                    clearTimeout(obj_this.set_time_var3);
                    dropZoneElems[eq_idx].scrollLeft = file_list_wd; 
                }
            }

			/**==============================================
			**  상하단으로 스크롤링
			**---------------------------------------------*/
 			// 스크롤이 필요없는 범위 안이라면  리셋  null 
			if(drop_zone_pt > 5 && drop_zone_ht - drop_zone_pt > 5){ 
		 		MARI.file_uploder.code.scroll_add_y_num = null;
			}

			// 처음 시도시, 스크롤이 필요없는 범위 안이라면 =>스크롤의 현재 높이를 넣어준다
			if(obj_this.scroll_add_y_num === null){ 
                obj_this.scroll_add_y_num = document.querySelectorAll(drop_zone_cls)[eq_idx].scrollTop;

			}
			/**==================================
			**  상단으로 드래그를 벗어나게 했을 때 
			**---------------------------------*/ 
            if (drop_zone_pt <= 5) {
                let ht_range = Math.abs(0 - drop_zone_pt);
                let add_rate = ht_range * 10; 
                if (dropZoneElems[eq_idx].scrollTop > 0) {
                    dropZoneElems[eq_idx].scrollTop -= add_rate; // ※ 스크롤 이동 
                    obj_this.scroll_add_y_num = dropZoneElems[eq_idx].scrollTop;
                } else {
                    clearTimeout(obj_this.set_time_var3);
                    dropZoneElems[eq_idx].scrollTop = 0;
                }
            }

			/**==================================
			** 하단으로 드래그를 벗어나게 했을 때
			**---------------------------------*/ 
            if (drop_zone_ht - drop_zone_pt <= 5) {
                let ht_range = Math.abs(drop_zone_ht - drop_zone_pt);
                let add_rate = 1;
                if (dropZoneElems[eq_idx].scrollTop  < file_list_ht - 50) {
                     dropZoneElems[eq_idx].scrollTop += add_rate; // ※ 스크롤 이동 
                    obj_this.scroll_add_y_num = dropZoneElems[eq_idx].scrollTop;
                } else {
                    clearTimeout(obj_this.set_time_var3);
                    dropZoneElems[eq_idx].scrollTop = file_list_ht; 
                }
            }

		},

		/*=================================================
		 * 19. 포커스 하일라이트 판넬 스크롤링
		**-----------------------------------------------*/
		scroll_act2 : function(event){
			let obj_this       = MARI.file_uploder.code;   
            let eq_idx         = obj_this.event_eq(event); // 사용 가능하게 설정된 업로더에서의 인덱스 
            let drop_zone_obj  = obj_this.getDropZone(eq_idx); 
            let get_main_obj   = obj_this.file_main_obj(eq_idx); 
            let main_objRect   = get_main_obj.getBoundingClientRect();
			let main_obj_pt    = (event.pageY - main_objRect.top  + window.scrollY);  // 페이지의 Y 위치와 비교  window.scrollY 추가되었음
            let ht_range       = Math.abs(0 - main_obj_pt);
            let add_rate       = ht_range * 10;
            const scrollSpeed  = 5; // 스크롤 속도

            // 마우스가 dropZone의 위 또는 아래를 벗어났는지 확인
            if (event.clientY < main_objRect.top + 71) {
                // 위로 스크롤
               drop_zone_obj.scrollTop -= scrollSpeed;
            } else if (event.clientY > main_objRect.bottom + 1) {
                // 아래로 스크롤
                drop_zone_obj.scrollTop += scrollSpeed;
            }

        },
            
        
		/*=================================================
		 * 20. 포커스 하일라이트 판넬 동작
		**-----------------------------------------------*/
		focus_heighlight_act : function(event){ 
			let obj_this        = MARI.file_uploder.code;   
            let eq_idx          = obj_this.event_eq(event); // 사용 가능하게 설정된 업로더에서의 인덱스  
            let focus_panel     = obj_this.getFocusPanel(eq_idx); // 하일라이트 판넬
            let loaderMain      = obj_this.loader_main_obj(eq_idx); 
            let drop_zone_obj   = obj_this.getDropZone(eq_idx); 
            let listHeader      = loaderMain.querySelector('.file_list_header');
            let fileMain        = loaderMain.querySelector('.file_main'); //드래그 이벤트를  drop_zone에서 file_main으로 변경 2024-11-27(수)

            // 첫 클릭한 곳과 마우스 오버된 업로더가 틀리면 교정 (  드래그 후 다른 업로더 영역에 올려졌을 때 어떻게 되는지 확인할 것)
            if( obj_this.click_eq !== eq_idx || obj_this.click_eq === null ) {
                eq_idx = Number(obj_this.click_eq);
            }
            if( obj_this.mouse_down_flag === false ){
                obj_this.log('마우스 다운 플래그 사용 금지');
                return false; 
            }
            // 포커스 하일라이트 판넬 사용을 하지 않음
            if(obj_this.use_focus_pannel_act[eq_idx] === 'N'){
                obj_this.log('포커스 하일라이트 사용 금지');
                return false; 
            }
            if(!focus_panel) return false;

            // 드롭존 정보
            let drop_zoneRect = drop_zone_obj.getBoundingClientRect();
            let drop_zoneTop  = drop_zoneRect.top  + window.scrollY;
            let drop_zoneLeft = drop_zoneRect.left + window.scrollX;
            let drop_zoneHt   = drop_zone_obj.offsetHeight;
            let drop_zoneWd   = drop_zone_obj.offsetWidth;
            let objBottom     = drop_zoneTop  + drop_zoneHt; // 드롭존의 하단 경계
            let objRight      = drop_zoneLeft + drop_zoneWd; // 드롭존의 우측 경계
            let objScrollTop  = drop_zone_obj.scrollTop;     // 드롭존 내부의 Y 스크롤 위치
            let objScrollLeft = drop_zone_obj.scrollLeft;    // 드롭존 내부의 X 스크롤 위치 
            let has_xscroll   = obj_this.has_xscroll(event, eq_idx);// 스크롤 상태
            let has_yscroll   = obj_this.has_yscroll(event, eq_idx); 
            let evtX          = event.clientX;// 이벤트 및 초기 클릭 위치
            let evtY          = event.clientY;
            let startX        = obj_this.startX;
            let startY        = obj_this.startY;
            // 현재 위치 계산
            let moveX         = evtX + objScrollLeft + drop_zone_obj.offsetLeft;                         // 드롭존 내 X 좌표
            let moveY         = evtY - listHeader.offsetHeight + objScrollTop + drop_zone_obj.offsetTop; // 드롭존 내 Y 좌표
            // 이동 방향 계산
            let leftMove      = startX + window.scrollX - drop_zoneLeft + drop_zone_obj.offsetLeft;      // 기준점을 기준으로 좌우 이동
            let rightMove     = leftMove - (startX - evtX);                                              // 오른쪽 이동
            let bottomMove    = startY + window.scrollY - drop_zoneTop + drop_zone_obj.offsetTop;        // 기준점을 기준으로 상하 이동
            let topMove       = moveY - drop_zoneRect.top;                                               // 위쪽 이동

 			/**==================================
			** 개체의 css 값
			**---------------------------------*/
 		 	let css_left      = ( moveX - startX  < 0 ) ? rightMove : leftMove;   // 좌우로 이동 : 우로 이동 
		 	let css_width     = Math.abs(startX - moveX); // 넓이는 절대값  
 		  	let css_top       = ( moveY - startY  < 0 ) ? topMove   : bottomMove; // 위로이동 : 아래로 이동
            let css_height    = Math.abs( startY - moveY ); // 높이는 절대값(처음 클릭한 위치에서 부터 현재의 위치)
 
			/**==================================
			** 상하 3픽셀 이상의 움직임이 있다면 
			** 하일라이트 영역 보이기
			**---------------------------------*/ 
            if(Math.abs(startX - evtX) > 3 || Math.abs(startY - evtY) > 3){ 
                focus_panel.style.display = 'block';// 드래그 영역 보이도록
            } 

			if(css_width < 0 || css_height < 0) return false;

			/**==================================
			** 하일라이트 포커스 판넬 시작점이 
			** 파일 리스트 시작점 보다 작지 않도록 조절한다
			**---------------------------------*/
			if(css_left < 0){
				css_left  = 0;
		 	  	css_width = focus_panel.offsetWidth;
			}
			if(css_top < 0) {
	  			css_top    = 0; 
		 	  	css_height = focus_panel.offsetHeight;
			}

			/**==================================
			** 스크롤이 있다면 조절
            // obj_this.scroll_act2(event); // 스크롤이 있다면 
			**---------------------------------*/
            obj_this.scroll_act2(event); // 스크롤이 있다면

			/**==================================  
			** ※ 하일라이트 포커스 판넬 ※ 
			** ※ css 위치점 지정 ※ 
			**---------------------------------*/ 
            focus_panel.style.left = css_left + 'px';
            focus_panel.style.top  = css_top  + 'px'; // 휠 이동이 있으면

			/**==================================  
			** ※ 스크롤바가 있다면 ※ 
			**---------------------------------*/
			if(has_xscroll ){ 
               objBottom -= 18; 
            }
		 	if(has_yscroll ){
                objRight -= 18;
            }

			/**==================================  
			** ※ 하일라이트 포커스 판넬 ※ 
			** ※ 높이 넓이 제한 ※ 
			**---------------------------------*/          
            // 내부 문서의 마지막 지점을 넘기지 않도록 
            if( evtX < objRight - window.scrollX - 2) { 
               focus_panel.style.width  = css_width + 'px';
            }

			if( evtY < objBottom - window.scrollY  - 2 ) {
               focus_panel.style.height = css_height + 'px';
            }


			/**==================================
			** 개체를 선택하기 위한 현재의 위치 저장 ( 중요 )
			**---------------------------------*/
			obj_this.drag_moveX  = evtX;       // rangeSelect함수 안에서 위치값을 
			obj_this.drag_moveY  = evtY;       // rangeSelect함수 안에서 위치값을
			obj_this.move_x      = evtX;       // 좌우 이동을 알기 위한 플래그용
			obj_this.move_y      = evtY;       // 상하 이동을 알기 위한 플래그용
			obj_this.panel_wd    = css_width;  // 마우스 휠 이동시 사용
			obj_this.panel_ht    = css_height; // 마우스 휠 이동시 사용
			obj_this.panel_left  = parseInt(window.getComputedStyle(focus_panel).left); // 마우스 휠 이동시 사용 
			obj_this.panel_top   = parseInt(window.getComputedStyle(focus_panel).top);  // 마우스 휠 이동시 사용 
            obj_this.scroll_left = document.querySelectorAll(obj_this.file_main_cls)[eq_idx].scrollLeft; // file_main 의 현재 스크롤 위치를 기록한다
            obj_this.scroll_top  = document.querySelectorAll(obj_this.file_main_cls)[eq_idx].scrollTop;  // file_main 의 현재 스크롤 위치를 기록한다

		},


		/*=================================================
		 * 21. 셀렉션을 통한 선택 사항을 가름한다
		**-----------------------------------------------*/ 
		rangeSelect : function(event, evt_idx2){ 
			/**==============================================
			** 변수값 지정 ( 약 980라인 ) 
			**---------------------------------------------*/  
			let obj_this      = MARI.file_uploder.code;  
            let eq_idx        = obj_this.event_eq(event);   // 사용 가능하게 설정된 업로더에서의 인덱스 

            if(eq_idx < 0) return false;

            //첫 클릭한 곳과 마우스 오버된 업로더가 틀리면 리턴 (  드래그 후 다른 업로더 영역에 올려졌을 때 어떻게 되는지 확인할 것)
            if(obj_this.click_eq !== eq_idx) {
                eq_idx = obj_this.click_eq;
                return false;
            }

            // 개체를 셀렉팅하기 위한 영역 드래그가 아닌 미리보기 영역 늘리는 중이라면 제외 
            let helperDiv = document.getElementById(obj_this.helperDomeId); 
            if( obj_this.drag_bar_flag === true && helperDiv) { // 미리보기 영역 크기 늘리는 중이라면 리턴 
                 return false;
            }

 			let target_type   = obj_this.getTargetType(event);//썸네일 선택 혹은 주변 공간 선택 구분
            let evt_idx       = evt_idx2;
            let focus_panel   = obj_this.getFocusPanel(eq_idx); // 하일라이트 판넬
            let drop_zone_obj = obj_this.getDropZone(eq_idx);  

			/*===============================
			 * 셀렉션을 통한 선택 사항을 가름한다
			**-----------------------------*/  
            if ( focus_panel === null) return false;

			let rect         = focus_panel.getBoundingClientRect();  
            let pan_top      = rect.top.toFixed() -  drop_zone_obj.getBoundingClientRect().top + window.scrollY;
            let pan_left     = rect.left.toFixed() - drop_zone_obj.getBoundingClientRect().left + window.scrollX; 
            let loaderMain   = obj_this.loader_main_obj(eq_idx); 
            let file_main    = loaderMain.querySelector('.file_main');
			let pan_right    = pan_left + rect.width;
			let pan_bottom   = pan_top  + rect.height; 
			let position_Ary = obj_this.drag_position_ary[eq_idx]; 
            let li_len       = obj_this.li_obj(eq_idx).length;     // .one_file  클래스의 li  

			/**========================================================
			** 필터링해서 선택 범위를 선정 css를 입히거나 걷어낸다
            ** 마우스 다운시 전체 개체를 돌면서 
            ** 최대한 가볍게 할려면 objType관련 데이터를 배열로 받아서 바로 쓸수 있게하면 된다
			**-------------------------------------------------------*/  
			if(typeof(position_Ary) !== 'undefined'){
                // null 배열을 빼고 다시 담아서 사용한다
				position_Ary.filter((pos, index) =>{
                    let dataIdx = index;
                    let objType =  obj_this.getMakeTypeAry[eq_idx][index]; 

					// 범위내에 들어온 개체들( 넓은 범위에서 드래그로 그물에 담듯 사용할려면 위치값으로 접근 필요 ) 
					if( pos.left < pan_right   && pos.right  > pan_left && 
						pos.top  < pan_bottom  && pos.bottom > pan_top  ){ 
						if( obj_this.ctrl_key_flag === true && obj_this.shift_key_flag === false ){ 
							// ctrl만 선택
							obj_this.aryClassSetting2(event, objType, eq_idx, 'only_ctrl_dragin', target_type, dataIdx);
						}
						else
						if( obj_this.shift_key_flag === false && obj_this.ctrl_key_flag === false ){ 
							// SHIFT,ctrl 둘다 안함
							obj_this.aryClassSetting2(event, objType, eq_idx, 'no_shift_ctrl_dragin', target_type, dataIdx); 
						}
					} else {// 범위를 벗어난 개체들 
						if( obj_this.ctrl_key_flag === true && obj_this.shift_key_flag === false ){ 
							// ctrl만 선택 
				 			obj_this.aryClassSetting2(event, objType, eq_idx, 'only_ctrl_dragout', target_type, dataIdx);
						}
						else
						if( obj_this.shift_key_flag === false && obj_this.ctrl_key_flag === false ){
							// SHIFT,ctrl 둘다 안함
							obj_this.aryClassSetting2(event, objType, eq_idx, 'no_shift_ctrl_dragout', target_type, dataIdx); 
						} 
					}
				});
			}

		},


		/*=================================================
		 * 22. 콘트롤 클릭 선택시 반전을 위한 css 설정및 선택한 값 배열 저장
         call = > 어디서 호출했는지 확인할 경우 콘솔등에서 확인용으로
		**-----------------------------------------------*/ 
		aryClassSetting : function(event, objType, eq_idx, obj_type, click_type, target_idx, call=''){ 

			let target_type   = click_type;//썸네일 선택 혹은 주변 공간 선택 구분 
			let obj_util      = MARI.file_uploder.util;
			let obj_this      = MARI.file_uploder.code; 

            if(obj_this.click_eq != eq_idx) return false;

			let select_ary    = MARI.file_uploder.code.drag_select_ary;
			let click_idx     = obj_this.click_idx[eq_idx];  // 마우스 다운시 선택된 idx target_idx와 같음
            let evt_idx       = obj_this.event_index(event, obj_this.one_file_cls);   // 클릭, 드래그시 선택된 idx
            let loader_obj    = obj_this.loader_main_obj(eq_idx); // 업로더 메인  
            let ul_obj        = obj_this.ul_obj(eq_idx);     // .file_list 클래스의 ul 
            let li_obj        = obj_this.li_obj(eq_idx);     // .one_file  클래스의 li 
            let aryIdx        = target_idx;
            let list_cnt      = li_obj != null ? li_obj.length : 0;     // 리스트의 전체 갯수 
 		  	let sel_obj       = li_obj[target_idx]; // 선택된 리스트 
            let sel_code      = '';
            let sel_name      = '';
            let choice_cls    = '';
            if(sel_obj){
                sel_code      = sel_obj.getAttribute('data-code') !== null ? sel_obj.getAttribute('data-code') : null;
                sel_name      = (typeof sel_obj !== 'undefined' && sel_obj.parentElement) ? sel_obj.parentElement.querySelector('.file_name')?.textContent : null;
                choice_cls    = sel_obj.className;       // 넘어온 선택된 개체의 클래스 (cls와 틀림 cls =>마우스가 올려진 현재의 클래스)
            }
 
            let search_chk1   = obj_this.search_chk1;
            let search_chk2   = obj_this.search_chk2;
            let search_chk3   = obj_this.search_chk3;
            let search_chk4   = obj_this.search_chk4;
            let search_off1   = obj_this.search_off1;
            let search_off2   = obj_this.search_off2;
            let search_off3   = obj_this.search_off3;
            let search_off4   = obj_this.search_off4;

			if(obj_type === 'only_shift_down'){ // 쉬프트 키를 누른 상태에서 마우스 다운 
               /**======================
                ** SHIFT만 선택
                **---------------------*/
                let start_num = obj_this.shift_start_end_ary[eq_idx][0];
                let end_num   = obj_this.shift_start_end_ary[eq_idx][1];

                obj_this.log('쉬프트 키 누름에 따른 시작번호와 끝번호 인덱스',start_num,end_num);
                if(start_num && end_num){

                    obj_this.select_ary_reset(event, eq_idx); // 배열을 비운다

                    // 클릭한 곳 부터 쉬프트키를 통해 최종 클릭한 곳 까지 연쇄 선택
                    li_obj.forEach(el => el.className = 'one_file'); // 클래스 일괄 변경
                    if(end_num > start_num){
                        for (let i = start_num; i <= end_num; i++) {  // 시작 부터 끝까지 선택한 곳 보더 없는 선택으로 정방향 변경 
                            li_obj[i].classList.add('thumb_chk3'); 
                            obj_this.set_drag_select_ary(event, objType, 'add', eq_idx, i, 'thumbnail', 'dragAct3');
                        }

                    } else {
                        for (let i = start_num; i >= end_num; i--) { // 시작 부터 끝까지 선택한 곳 보더 없는 선택으로 역방향 변경
                            li_obj[i].classList.add('thumb_chk3');
                            obj_this.set_drag_select_ary(event, objType, 'add', eq_idx, i, 'thumbnail', 'dragAct4');
                        }
                    }

                    if(end_num > -1){ 
                        li_obj[end_num].className = 'one_file thumb_chk1';// 마지막 선택 보더 선택으로 변경 
                    }

                    // 히스토리 입력 
                    let msgAry = ['[ SHIFT ('+(start_num+1)+'번 파일 ~ '+(end_num+1)+'번 파일) ] 연속 선택된 배열', MARI.file_uploder.code.drag_select_ary[eq_idx]];
                    obj_this.historyEvent(eq_idx,  msgAry, 'pink');
                }
            }
			else
			if(obj_type === 'only_shift_up'){ // 쉬프트 키 사용시 마우스업일 때 변화 없음
                /*===============================================
                * ※ 이전 클릭한 곳을 알기 위해 사용 
                **---------------------------------------------*/ 
                obj_this.before_click_idx[eq_idx] = Number(target_idx); 
                // 히스토리 입력 
                //obj_this.historyEvent(eq_idx,  '쉬프트 키 사용시 마우스업일 때');
                return false;
            }
			else
			if(obj_type === 'only_ctrl_down'){ // 콘트롤 사용 마우스 다운시  
				if( target_type === 'thumbnail'){ 
					// 비어 있다면 선택
					if(choice_cls === 'one_file'){
                        search_off1.forEach(el => el.classList.remove('thumb_off1'));              // □ -> delete 
                        search_chk1.forEach(el => el.className = 'one_file thumb_chk3');           // ▣ -> ■ ( .thumb_chk1으로 된 기존 선택된 것을 보더 없는 선택으로 변경 )
                        li_obj[target_idx].classList.add('bgsoft');                                // ▦
						obj_this.set_drag_select_ary(event, objType, 'add', eq_idx, aryIdx, click_type, 'dragAct5');

                        // 히스토리 입력 
                        obj_this.historyEvent(eq_idx,  '콘트롤 사용 마우스 다운시1');
					}
					else
					if(choice_cls === 'one_file thumb_chk3'){ 
                        search_off1.forEach(el => el.classList.remove('thumb_off1'));              // □ -> delete 
                        search_chk1.forEach(el => el.className = 'one_file thumb_chk3');           // ▣ -> ■ ( .thumb_chk1으로 된 기존 선택된 것을 보더 없는 선택으로 변경 )
                        li_obj[target_idx].className = 'one_file thumb_chk1';                      // 클래스 변경

						obj_this.set_drag_select_ary(event, objType, 'add', eq_idx, aryIdx, click_type, 'dragAct6');
                        
                        // 히스토리 입력 
                        obj_this.historyEvent(eq_idx,  '콘트롤 사용 마우스 다운시2');
					} 
                    /*===============================================
                     * ※ 이전 클릭한 곳을 알기 위해 사용 
                    **---------------------------------------------*/ 
                    obj_this.before_click_idx[eq_idx] = Number(target_idx); 
				}
			}
			else
			if(obj_type === 'only_ctrl_up'){
				/*===================================
				 * 콘트롤만 사용 마우스 업시
				**---------------------------------*/
                // 선택에 의한 반전
                search_chk2.forEach(el => el.className = 'one_file thumb_chk1');// 클래스 일괄 변경
                search_chk4.forEach(el => el.className = 'one_file thumb_chk3');// 클래스 일괄 변경 ( ■ -> ■ )
                search_off2.forEach(el => el.className = 'one_file thumb_off1');// 클래스 일괄 변경 ( □ -> □ )
                search_off4.forEach(el => el.className = 'one_file');           // 클래스 일괄 변경 ( ▩ -> delete )
                // 선택에 의한 반전

				if( target_type === 'thumbnail'){
                    li_obj[target_idx].classList.remove('bgsoft'); // 클래스명 삭제
					if( choice_cls  === 'one_file thumb_off4'){ // 개체 클래스 변경 
                        li_obj[target_idx].className = 'one_file'; 
						obj_this.set_drag_select_ary(event, objType, 'add', eq_idx, aryIdx, click_type, 'dragAct7'); 
                        // 히스토리 입력 
                        obj_this.historyEvent(eq_idx,  '콘트롤만 사용 썸네일 위에서 마우스 업시1');
					}
					else
					if(choice_cls === 'one_file thumb_chk1'){ // ▣ -> □  
                        li_obj[target_idx].className = 'one_file thumb_off1'; 
						obj_this.set_drag_select_ary(event, objType, 'minus', eq_idx, aryIdx, click_type, 'dragAct8');
                        // 히스토리 입력 
                        obj_this.historyEvent(eq_idx,  '콘트롤만 사용 썸네일 위에서 마우스 업시2');
					}
					else
					if(choice_cls === 'one_file thumb_chk3'){ // ■ -> ▩ 
                        li_obj[target_idx].className = 'one_file thumb_off4'; 
						obj_this.set_drag_select_ary(event, objType, 'minus', eq_idx, aryIdx, click_type, 'dragAct9');
                        // 히스토리 입력 
                        obj_this.historyEvent(eq_idx,  '콘트롤만 사용 썸네일 위에서 마우스 업시3');
					}
					else
					if(choice_cls === 'one_file thumb_off1'){ // □ ->▣ 
                        li_obj[target_idx].className = 'one_file thumb_chk1'; 
						obj_this.set_drag_select_ary(event, objType, 'add', eq_idx, aryIdx, click_type, 'dragAct10');
                        // 히스토리 입력 
                        obj_this.historyEvent(eq_idx,  '콘트롤만 사용 썸네일 위에서 마우스 업시4');
					}
					else
					if(choice_cls === 'one_file bgsoft'){ 
                        li_obj[target_idx].classList.remove('thumb_off1');  
                        li_obj[target_idx].classList.add('thumb_chk1'); 
                        // 히스토리 입력 
                        obj_this.historyEvent(eq_idx,  '콘트롤만 사용 썸네일 위에서 마우스 업5');
					} 
				}
            }
			/*=====================================================================================
			 * 쉬프트 키를 이용한 클릭, 드래그인, 드래그 아웃
			 *-------------------------------------------------------------------------------------
			 * 동작 설명

			**-----------------------------------------------------------------------------------*/ 

			/*=====================================================================================
			 * 콘트롤 사용 없이 개별적 선택, 개별 드래그시
			 *-------------------------------------------------------------------------------------
			 * 동작 설명
			 * 콘트롤 없이 영역 지정후 바탕 마우스 다운시 관련된 동작 없고 마우스 업시 영역 지정을 해제한다 선택된 항목만 테두리로 표시
			 * 썸네일을 마우스 다운시 
			 *    기존 선택된 테두리는 없애고 선택된 썸네일에 테두리 선택형으로 바꾼다 
			 *    기존 선택된 테두리형 선택 없애고 선택된 썸네일에 테두리 선택형으로 바꾼다 
			 *    고스트 이미지 드래그라면 다른 드래그된 개체를 풀지 않는다
			 * 바탕화면 마우스 다운시
			 *    기존 선택된 것들은 다 풀고 선택된 썸네일만 테두리 선택형으로 바꾼다
			**-----------------------------------------------------------------------------------*/
			if(obj_type === 'no_shift_ctrl_down'){ // 쉬프트,콘트롤 사용 안하고 마우스 다운시  
                if( target_type === 'thumbnail'){
                    let select_cnt = obj_this.selectCnt(eq_idx);  // 드래그 선택중인 갯수 리턴 
                    if(select_cnt == 1){ //선택된게 하나일 때
                        li_obj.forEach(el => el.className = 'one_file');        // 모든 걸 초기화
                        li_obj[target_idx].className = 'one_file thumb_chk1';   // ▣
                        obj_this.set_drag_select_ary(event, objType, 'one', eq_idx, aryIdx, click_type, 'dragAct11'); 

                        // 히스토리 입력 
                        obj_this.historyEvent(eq_idx,  '쉬프트,콘트롤 사용 안하고 마우스 다운시1');
                    } 
                    else
                    if(select_cnt > 1){ //선택된게 하나 이상일 때  
                        // 선택한 인덱스가 그룹에 속해 있지 않다면
                        let isIncluded = select_ary[eq_idx]['idx'].includes(aryIdx);
                        if(!isIncluded){
                            li_obj.forEach(el => el.className = 'one_file');        // 모든 걸 초기화
                            li_obj[target_idx].className = 'one_file thumb_chk1';   // ▣
                            obj_this.set_drag_select_ary(event, objType, 'one', eq_idx, aryIdx, click_type, 'dragAct12'); 
                            // 히스토리 입력 
                            obj_this.historyEvent(eq_idx,  '쉬프트,콘트롤 사용 안하고 마우스 다운시2');
                        }
                    }
                    /*===============================================
                     * ※ 이전 클릭한 곳을 알기 위해 사용 
                    **---------------------------------------------*/ 
                    obj_this.before_click_idx[eq_idx] = Number(target_idx);
                }
			}
			else
			if(obj_type === 'no_shift_ctrl_up'){ // 쉬프트,콘트롤 사용 안하고 마우스 업시 

                let targetClass = event.target.className;
                //드래그된 것이 없고 바탕 화면을 클릭 했다면 리셋  
                if( obj_this.drag_flag === false && targetClass !=='helperDiv' ) { // 영역 드래그일 경우
                    obj_this.select_ary_reset(event, eq_idx); // 배열을 비운다
                }
                
                // 파일 이름 변경에 관련된 곳 빼고 마우스업을 했다면
                if(!( targetClass === 'file_name' || targetClass === 'file_icon')){
                    search_chk4.forEach(el => el.className = 'one_file thumb_chk3');// 클래스 일괄 변경 ( ■ -> ■ )
                    search_off2.forEach(el => el.className = 'one_file thumb_off1');// 클래스 일괄 변경 ( □ -> □ )

                    if(obj_this.startX  ===  event.clientX  ){
                        // 우선 모든걸 초기화 후 선택된 것만 넣어줄 수 있도록 한다
                        li_obj.forEach(el => el.className = 'one_file');        // 모든 걸 초기화
                        if(target_idx >= 0)
                        li_obj[target_idx].className = 'one_file thumb_chk1';   // ▣
                        obj_this.set_drag_select_ary(event, objType, 'one', eq_idx, aryIdx, click_type, 'dragAct13'); 
                        // 히스토리 입력 
                        obj_this.historyEvent(eq_idx,  '쉬프트,콘트롤 사용 안하고 마우스 업시1');
                    }  
                } 
            }

		},// end aryClassSetting 
 

		/*=================================================
		 * 23.  aryClassSetting에 넣어 관리했으나
         * 많은 개체를 담을 때 드래그인 아웃에 따른 원활한 흐름을 위해서
         * 따로 빼서 관리를 한다 추후 다시 합칠수 있음
         * 866라인즈음의 마우스 무브랑 연관
		**-----------------------------------------------*/ 
		aryClassSetting2 : function(event, objType, eq_idx, obj_type, click_type, target_idx, call=''){ 

			let obj_this   = MARI.file_uploder.code;
            let li_obj     = obj_this.li_obj(eq_idx);       // .one_file  클래스의 li  
 		  	let sel_obj    = li_obj[target_idx];            // 선택된 리스트 
            let choice_cls = sel_obj ? sel_obj.className : ''; // 넘어온 선택된 개체의 클래스 (cls와 틀림 cls =>마우스가 올려진 현재의 클래스)
            let aryIdx     = target_idx;

			if(obj_type === 'no_shift_ctrl_dragin'){ // 일반적인 드래그 영역 지정
                if (choice_cls === 'one_file thumb_off1' ||  choice_cls === 'one_file' || choice_cls === 'one_file thumb_off3') {
                    li_obj[target_idx].className = 'one_file thumb_chk4'; // 클래스명 변경 마우스 범위에 들어온 선택된거

                    // 미리보기, 상세보기
                    if(obj_this.preview_mode[eq_idx]==='open'){
                        obj_this.file_preview_act(event, eq_idx, target_idx, 'select_range');
                    }
			 	    obj_this.set_drag_select_ary(event, objType, 'add', eq_idx, target_idx, click_type, 'dragAct14');
                    // 히스토리 입력 
                    // obj_this.historyEvent(eq_idx,  '일반적인 드래그 영역 지정3');
                }
			}
			else
			if(obj_type === 'no_shift_ctrl_dragout'){// 일반적인 드래그 영역 해제 
                if (choice_cls === 'one_file thumb_chk3' || choice_cls === 'one_file thumb_chk4') { 

                    // 미리보기, 상세보기
                    if(obj_this.preview_mode[eq_idx]==='open'){
                        obj_this.file_preview_act(event, eq_idx, target_idx, 'select_range');
                    }
			      	obj_this.set_drag_select_ary(event, objType, 'minus', eq_idx, target_idx, click_type, 'dragAct15');
                    
                    // 히스토리 입력 
                    // obj_this.historyEvent(eq_idx,  '일반적인 드래그 영역 해제2');
                }
			}

            /*========================================================
             * 배경색에 따른 분류
             *--------------------------------------------------------
             * thumb_off1, thumb_off2( □ :: 테두리 )
             * thumb_off3, thumb_off4( ▩ :: 선택되지 않은 )
             * thumb_chk1, thumb_chk2( ▣ :: 선택 되어 배경색과 테두리가 있는 )
             * thumb_chk3, thumb_chk4( ■ :: 선택 되어 배경색이 있는 )
            **------------------------------------------------------*/
			if(obj_type === 'only_ctrl_dragin'){ //콘트롤 드래그 영역 지정

				if( choice_cls === 'one_file'){
                    li_obj[target_idx].className = 'one_file thumb_chk4'; // 마우스업에서 thumb_chk4 -> thumb_chk3로 변경
					obj_this.set_drag_select_ary(event, objType, 'add', eq_idx, aryIdx, click_type, 'dragAct16');
				}
				else
				if( choice_cls === 'one_file thumb_chk1'){ 
                    li_obj[target_idx].className = 'one_file thumb_off2'; 
					obj_this.set_drag_select_ary(event, objType, 'minus', eq_idx, aryIdx, click_type, 'dragAct17');
				}
				else
				if( choice_cls === 'one_file thumb_chk3'){ 
                    li_obj[target_idx].className = 'one_file thumb_off4'; 
					obj_this.set_drag_select_ary(event, objType, 'minus', eq_idx, aryIdx, click_type, 'dragAct18');  
				} 
				else
				if( choice_cls === 'one_file thumb_off1'){ 
                    li_obj[target_idx].className = 'one_file thumb_chk2'; 
					obj_this.set_drag_select_ary(event, objType, 'add', eq_idx, aryIdx, click_type, 'dragAct19');
				}
				else
				if( choice_cls === 'one_file thumb_off3'){  
                    li_obj[target_idx].className = 'one_file thumb_chk4';
					obj_this.set_drag_select_ary(event, objType, 'minus', eq_idx, aryIdx, click_type, 'dragAct20');
				}
            }
			else
			if(obj_type === 'only_ctrl_dragout'){//콘트롤 드래그 영역 해제
                return false;
            }

        }, // end aryClassSetting2

 
		/*=========================================================================================
		 * 24. 선택 리스트 해제시 미리보기 초기화
		**---------------------------------------------------------------------------------------*/ 
		reset_preview : function(event){
            let obj_util     = MARI.file_uploder.util;
            let obj_this     = MARI.file_uploder.code;
            let eq_idx       = obj_this.event_eq(event); // 사용 가능하게 설정된 업로더에서의 인덱스 
			let viewTypeFlag = obj_this.viewTypeFlag[eq_idx]; // 미리보기창  preview( 미리보기 ),detail( 상세보기 ) 
            let loaderMain   = obj_this.loader_main_obj(eq_idx); 
            let file_view_dp = loaderMain.querySelector(obj_this.file_view_dp_cls); // 미리보기 공간 넓이 조정
            let viewDp       = loaderMain.querySelector(obj_this.view_preview_cls);
            let detailDp     = loaderMain.querySelector(obj_this.view_detail_cls);

			// 미리 보기, 상세보기 초기화
			let view_html   =  '<span class="msg">미리 볼 파일을 선택하십시오.</span>'; 
			let detail_html =  '<span class="msg">상세 정보를 볼 파일을 선택하십시오.</span>'; 

			//미리보기에 넣기  view_preview
            viewDp.innerHTML   = view_html;
            detailDp.innerHTML = detail_html;
            MARI.file_uploder.code.view_img_idx[eq_idx] = null; //초기화

            // 미리보기, 상세보기 메세지 보이기
            let dropMsg    = viewDp.querySelector('.msg'); 
            let viewMsg    = detailDp.querySelector('.msg'); 
            obj_util.fadeIn(viewMsg,  200, 0);
            obj_util.fadeIn(dropMsg,  200, 0);

            let up_drag_flag = false; 
            if( obj_this.drag_flag === true || obj_this.drag_flag === 'ghost_move' ){
                up_drag_flag = obj_this.drag_flag;
            }

            // 다음 것을 받기 위해 선택한 이미지의 원래 크기 초기화
            if(up_drag_flag === true){ // 미리보기 이미지 리셋시 
                obj_this.nowImgNaturalHeight[eq_idx] = null;
                obj_this.nowImgNaturalWidth[eq_idx]  = null;
            }

            let selectFileInfo = loaderMain.querySelector('.status_bar');   
            selectFileInfo.innerHTML = ''; // 선택된 파일의 기본 정보 하단 창에 보이기 

            // 히스토리 입력 
            obj_this.historyEvent(eq_idx,  '미리보기 초기화 한다');

		},
 
        
		/*=================================================
		 * 25. ※ 선택한 파일 미리 보기 선택, 상세 보기 선택
		 *		obj_this.file_preview_act(event, evt_idx, target_idx, 'select_range');
		 *		obj_this.file_preview_act(event, evt_idx, target_idx, 'one');
         *
         *      obj_this.preview_mode[eq_idx]==='open'|| 
         *      obj_this.detail_mode[eq_idx]==='open'
		**-----------------------------------------------*/
        // ☆ mousemove 안에서 실행되기에 리소스를 많이 차지하고 있음
        // ☆ 흐름 또한 복잡하기에 추후 무조건 정리를 한번 더 할 것
        file_preview_act : function(event, eq_idx, target_idx, act_type){  
            if(target_idx < 0) return false;
   
			/*===============================
			 * 파일 미리보기 공간을 만들고 
			 * 리스트에서 선택된 이미지를 넣는다 
			 * act_type => one or select_range
			**-----------------------------*/
			let obj_util     = MARI.file_uploder.util;
            let getLocal     = obj_util.getFromLocalStorage('uploaderData'); 
			let obj_this     = MARI.file_uploder.code; 
            let evt_idx      = obj_this.event_index(event, obj_this.one_file_cls);   // 클릭, 드래그시 선택된 target_idx
            let loaderMain   = obj_this.loader_main_obj(eq_idx);                        // 사용 가능하게 설정된 업로더에서의 인덱스 
            let dTaEqAry     = obj_this.dTa_Ary[eq_idx] || null;  // 새로운 파일이 파일 업로더 기준으로 배열로 담김 ( 수정용 파일 안담김 )
			let data_file    = (dTaEqAry !== null ) ? dTaEqAry.new_files.files : null;      
            let li_obj       = obj_this.li_obj(eq_idx);     // .one_file  클래스의 li  
 		  	let sel_obj      = li_obj[target_idx];   // 리스트중 몇번째 ( 선택된 리스트 개체 )
 			let file_name    = sel_obj.querySelector('.file_name').innerText; //파일명 
			let extention    = file_name.substring(file_name.lastIndexOf(".")+1).toLowerCase();//파일 확장자(파일명 소문자로)
			let viewTypeFlag = obj_this.viewTypeFlag[eq_idx]; // 미리보기창  preview( 미리보기 ),detail( 상세보기 )
            let select_cnt   = obj_this.selectCnt(eq_idx);    // 드래그 선택중인 갯수 리턴 
            let drag_len2    = (select_cnt >7 ) ? 7 : select_cnt;
            let file_view_dp = loaderMain.querySelector(obj_this.file_view_dp_cls); // 2번 ( 순번대로 안에 포함된 )
            let view_preview = loaderMain.querySelector('.view_preview');           // 3번
            let view_detail  = loaderMain.querySelector('.view_detail');            // 3번
            let preview_obj  = loaderMain.querySelectorAll('.preview_in_div');  // 4번
            let thumbZone    = loaderMain.querySelector('.preview_upload_thumb');   // 5번 이 아래 이미지
            let objType      = obj_this.getMakeType(eq_idx, target_idx);
            let getListIdx   = obj_this.getListDataAryIdx(eq_idx, target_idx) - 1;   // 데이터 코드에는 1부터 증가이므로 빼준다  
            let getDatas     = obj_this.getFileInfo2(eq_idx, objType, getListIdx, 'All', '1983'); // 데이터
            let datas        = getDatas;  
            if(datas === null) return false; 

		//	let obj_src      = datas.dataThumbnail;
		// 	let preview_src  = getDatas.dataPicture;
            let ext          = obj_util.getFileExtension(datas.dataPicture);// 확장자만 가져온다
            let isImg        = obj_util.isImageFile(ext); 
            let preview_src  = getDatas.dataPicture; // 사진 주소를 넣어준다  
            if(isImg === false){  
                if(datas.dataMakeType === "Modify"){ 
 			        obj_src     = obj_this.moduleUrl +'/images/file_type1/'+ext+'.png';  // 확장자 썸네일 이미지
                    preview_src = obj_this.moduleUrl +'/images/file_type1/'+ext+'.png';  // 저장된 확장자 썸네일 이미지
                } 
                if(datas.dataMakeType === "New"){
 			        obj_src     = datas.dataThumbnail;  // 확장자 썸네일 이미지
                    preview_src = datas.dataFileSrc;  // 새로운 업로드일 때 확장자 썸네일 이미지
                }
            }

			let file_ext     = file_ext_list[obj_this.country][extention];
			let file_type    = ''; 
			let view_obj     = ''; // 미리보기 개체를 담는다
			let detail_info  = ''; // 상세보기 개체들
			let detail_div   = ''; // 상세보기 영역 
			let preview_div  = ''; // 미리보기 영역
			let dataFileSrc  = datas.dataFileSrc; // 데이터
			// 바탕 클릭시 인덱스가 null 이 들어오는것 있으면 금지
			if(target_idx  === null || target_idx < 0){
				//alert('target_idx 값이 맞지 않습니다'); 
                alert(objLang.txt101);
				return false;
			}

			// MIME 타입과 파일 확장자를 비교하여 파일 유형 결정
			file_type = (datas?.dataType === mime_types[extention]) 
				? `${file_ext} ( .${extention} )` : `${file_ext?.toUpperCase() || '알수 없음'} ( .${extention} )`;


			/*=====================================================================================
			 * 미리 보기 내용 : Start
			**-----------------------------------------------------------------------------------*/  
            let originalWidth, originalHeight,aspectRatio; 
            let imgWd, imgHt, imgTop, imgLeft, setWidth, newSize; 
            let zoneWd = parseInt(view_preview.offsetWidth)  - 40;  // 40( 패딩값 제함 )
            let zoneHt = parseInt(view_preview.offsetHeight) - 40;  // 40( 패딩값 제함 )
 
            // 공통부 
            if(datas.dataThumbnail){ // 썸네일등 이미지가 있어서 미리보기시 리사이즈등에 의해 비율이 필요할 때
                originalWidth  = parseInt(datas.dataImgWidth);
                originalHeight = parseInt(datas.dataImgHeight);  
                obj_this.nowImgNaturalWidth[eq_idx]  = parseInt(datas.dataImgWidth);
                obj_this.nowImgNaturalHeight[eq_idx] = parseInt(datas.dataImgHeight);  
                aspectRatio    = originalWidth / originalHeight; // 비율 계산 
            } 

			/*=====================================================================================
			 * 0. 이미지와 그외의 파일로 나눠서 처리
			**-----------------------------------------------------------------------------------*/
            /*===============================
             * 이미지
            **-----------------------------*/
            if(datas.dataType.match("image.*")){  
                if(datas.dataExt !='mp4'){
                    view_obj +='<div class="preview_upload_thumb">';  
                    view_obj +='    <img src="'+ preview_src +'" class="preview_upload_thumb2">';  
                    view_obj +='</div>'; 
                }
			}

            /*===============================
             * 이미지 그외
             * 
            **-----------------------------*/
            switch (datas.dataExt) {
                
                case 'text': // (ok) :: 잘 안쓰는 타입이지만 넣어준다
                case 'txt' : // (ok) 텍스트 확장자
                    const reader = new FileReader();
                    reader.addEventListener("load", () => {
                        let file       = new Uint8Array(reader.result);  
                        let isUTF8     = obj_util.checkUTF8(file);  // 추가 검사 (ANSI와 UTF-8 구별)  
                        let fileEncode = isUTF8 ? 'UTF-8' : 'ANSI';
                        let decoder    = new TextDecoder(isUTF8 ? "utf-8" : "euc-kr"); // 자동으로 UTF-8 또는 EUC-KR 디코딩
                        let txt        = decoder.decode(file); 
                        txt = txt.replace(/(?:\r\n|\r|\n)/g, '<br />');

                        let viewObj    = loaderMain.querySelectorAll('.preview_in_div');
                        let firstDiv   = viewObj[0];
                        let secondDiv  = viewObj[1] || null;
                        if (preview_obj.length < 1) {
                            firstDiv.innerHTML = '<div class="txt_preview">' + txt + '</div>'; 
                        } else {
                            secondDiv.innerHTML = '<div class="txt_preview">' + txt + '</div>';
                        }

                        // 파일의 기본 정보 하단 창에 보이기에 파일 형식을 넣어준다 
                        let firstSpan = loaderMain.querySelector('.status_bar div');
                        firstSpan.innerHTML = fileEncode;

                    }, false);

                    // 파일을 ArrayBuffer로 읽기
                    if (datas) {
                        reader.readAsArrayBuffer(datas);
                    }

                break;

                case 'html': // (ok)
                    const reader3 = new FileReader(); 
                    reader3.addEventListener("load", (evnet) => { 
                        let sanitizedHtml = reader3.result
                            .replace(/<script.*?>.*?<\/script>/gis, "")  // <script> 태그 제거
                            .replace(/on\w+=".*?"/g, "")                 // onclick 등 이벤트 속성 제거
                            .replace(/<iframe.*?>.*?<\/iframe>/gis, "")  // iframe 태그 제거
                            .replace(/<meta.*?http-equiv="refresh".*?>/gis, ""); // meta refresh 제거
                        const blob = new Blob([sanitizedHtml], { type: "text/html" });
                        const url = URL.createObjectURL(blob); 
                        
                        let file       = new Uint8Array(reader3.result);  
                        let isUTF8     = obj_util.checkUTF8(file);  // 추가 검사 (ANSI와 UTF-8 구별)  
                        let fileEncode = isUTF8 ? 'UTF-8' : 'ANSI';
                        let viewObj    = loaderMain.querySelectorAll('.preview_in_div');
                        let firstDiv   = viewObj[0];
                        let secondDiv  = viewObj[1] || null;
                        if (preview_obj.length < 1) {
                            firstDiv.innerHTML  = '<iframe class="previewFrame" sandbox="allow-same-origin"></iframe>'; 
                        } else {
                            secondDiv.innerHTML = '<iframe class="previewFrame" sandbox="allow-same-origin"></iframe>';
                        }

                        const previewFrame = loaderMain.querySelector('.previewFrame');
                        previewFrame.src = url; 


                        // 파일의 기본 정보 하단 창에 보이기에 파일 형식을 넣어준다 
                        let firstSpan = loaderMain.querySelector('.status_bar div');
                        firstSpan.innerHTML = fileEncode;
                        
                        
                    });
                    reader3.readAsText(datas); 
                    
                break; 
                    
                case 'avi': // avi일때 아래 asf와 같이 해서 asf에서 정의 한다
                    view_obj +='<video src="'+dataFileSrc+'" type="video/asf" class="obj_cls"></video>';
                    break;

                case 'flac': // (ok)
                case 'mp3' : // (ok)  
                    view_obj +='    <div class="preview_upload_thumb">';  
                    view_obj +='        <img src="'+ preview_src +'" class="preview_upload_thumb2">';  
                    view_obj +='    </div>'; 
                    view_obj +='	<div class="preview_audio_control">'; 
                    view_obj +='		<audio controls class="audio_player">'; 
                    view_obj +='			<source src="'+dataFileSrc+'">';
                    view_obj +='			이 문장은 여러분의 브라우저가 audio 태그를 지원하지 않을 때 화면에 표시됩니다!';
                    view_obj +='		</audio>';
                    view_obj +='	</div>';
                    break;
                case 'webm': // (ok) video로 미리보기
                case 'mp4':  // (ok) 
                    view_obj +='	<div class="preview_video_control">'; 
                    view_obj +='		<video controls class="video_player">';
                    view_obj +='			<source src="'+dataFileSrc+'" type="video/mp4">';
                    view_obj +='			이 문장은 여러분의 브라우저가 video 태그를 지원하지 않을 때 화면에 표시됩니다!';
                    view_obj +='		</video>';
                    view_obj +='	</div>';
                break;
                    
                case 'zip__': // (ok)
                    const reader5 = new FileReader(); 
                    reader5.addEventListener("load", (evnet) => {  
                        const data    = new Uint8Array(reader5.result); 
                        let code      = obj_util.parseZIP(data); 
                        let viewObj   = loaderMain.querySelectorAll('.preview_in_div');
                        let firstDiv  = viewObj[0];
                        let secondDiv = viewObj[1] || null; 
                        if (preview_obj.length < 1) {
                            firstDiv.innerHTML  = '<div class="txt_preview">' + code + '</div>'; 
                        } else {
                            secondDiv.innerHTML = '<div class="txt_preview">' + code + '</div>';
                        } 
                    });
                    reader5.readAsArrayBuffer(datas);
                break;

                case 'pdf' : // (ok)
                    view_obj +='<div class="objet_preview"><object data="'+dataFileSrc+'" type="'+datas.type+'"  encoding="euc-kr" class="obj_cls">';
                    view_obj +='</object></div>';
                break;
 
                default:  // 위에 캐이스로 지정이 되어 있지 않다면 지원 불가(ok)
                    //xlsx, hwp, hwpx, tar, 7z 
                    view_obj +='<div class="notReadableMsg">미리보기가 지원되지 않습니다</div>';

            } //end switch
			/*-----------------------------------
			 * 미리 보기 내용 : End
			**=================================*/

			/*===================================
			** 상세 보기 내용 : Start
			**---------------------------------*/
			// 선택된 파일 선택 갯수 이름,타입 간단 정보
			detail_info  +='	<div class="preview_info1">';
			if( drag_len2 >1 ){
				detail_info  +='	<p class="preview_img_cnt">'+select_cnt+'개 항목을 선택했습니다.</p>';
			} else {
				detail_info  +='	<p class="preview_img_file_name">'+file_name+'</p>';
				detail_info  +='	<p class="preview_img_file_type">'+file_type+'</p>';
			}
			detail_info  +='	</div>';

			// 선택된 파일 이미지들
			detail_info  +='	<div class="preview_info2">';

			for(let i = 0; i < drag_len2; i++){ 
				let img_idx = MARI.file_uploder.code.drag_select_ary[eq_idx]['idx'][i];
		 	    let obj_src = li_obj[i].querySelector('.upload_thumb').getAttribute('src');  
				view_obj2   = '<img src="'+ obj_src +'" class="preview_upload_thumb2">'; 
				detail_info += view_obj2; // 위에서 정의 해준 형태로 미리보기를 넣어준다 
			}
			detail_info  +='	</div>';

			if( datas.dataExt === 'mp345' ){
			 	let file_type2         = data_file[aryIndex].file_type2;
				let file_type3         = data_file[aryIndex].file_type3;
				let file_pic_size      = data_file[aryIndex].file_pic_size;
				let lastModifiedDate   = data_file[aryIndex].lastModifiedDate;
				let file_size          = data_file[aryIndex].file_size;
				let mp3_album          = data_file[aryIndex].mp3[0].mp3_album;   
				let mp3_albumArtist    = data_file[aryIndex].mp3[0].mp3_albumArtist; 
				let mp3_albumCover     = data_file[aryIndex].mp3[0].mp3_albumCover;
				let mp3_artist         = data_file[aryIndex].mp3[0].mp3_artist; 
				let mp3_comment        = data_file[aryIndex].mp3[0].mp3_comment;  
				let mp3_composer       = data_file[aryIndex].mp3[0].mp3_composer; 
				let mp3_date           = data_file[aryIndex].mp3[0].mp3_date; 
				let mp3_disc           = data_file[aryIndex].mp3[0].mp3_disc; 
				let mp3_genre          = data_file[aryIndex].mp3[0].mp3_genre; 
				let mp3_lyrics         = data_file[aryIndex].mp3[0].mp3_lyrics; 
				let mp3_tagType        = data_file[aryIndex].mp3[0].mp3_tagType; 
				let mp3_title          = data_file[aryIndex].mp3[0].mp3_title; 
				let mp3_track          = data_file[aryIndex].mp3[0].mp3_track; 
				let mp3_year           = data_file[aryIndex].mp3[0].mp3_year; 
				let file_naturalHeight = data_file[aryIndex].mp3[0].file_naturalHeight; 
				let file_naturalWidth  = data_file[aryIndex].mp3[0].file_naturalWidth; 

				/* 오디오 재생 시간을 가져온다 */
				let audio_src = URL.createObjectURL(datas); // 업로더에 올라온 mp3주소
				let audio     = new Audio(audio_src);
				let audioDuration = '0';

                // audio 객체의 loadedmetadata 이벤트 리스너를 추가
                audio.addEventListener('loadedmetadata', () => {
                    const duration = audio.duration; // 재생 시간 (초 단위)
                    audioDuration = obj_util.convertSecondsToTime(duration); // "00 시 03분 15초"

                    // preview_content 클래스의 4번째 요소를 찾아서 내용을 설정
                    const previewContentElements = document.querySelectorAll(`${loaderMain} .preview_content`);
                    if (previewContentElements.length > 3) {
                        previewContentElements[3].innerHTML = audioDuration;
                    }

                    // 상세 보기가 선택되었을 때
                    if (viewTypeFlag === 'detail') {
                        const viewPreviewElement = document.querySelector(view_preview);
                        const viewDetailElement = document.querySelector(view_detail);

                        if (viewPreviewElement) {
                            viewPreviewElement.style.display = 'none';
                        }

                        if (viewDetailElement) {
                            viewDetailElement.style.display = 'block';
                        }
                    }
                });
                
				let loopAry = [
					{ key: '참여 음악가1', value: mp3_albumArtist },  //1
					{ key: '앨범', value: mp3_album },             //2
					{ key: '장르', value: mp3_genre },             //3
					{ key: '길이', value: audioDuration },         //4
					{ key: '연도', value: mp3_year },              //6
					{ key: '디스크', value: mp3_disc },             //8
					{ key: '앨범 음악가', value: mp3_artist },       //9
					{ key: '제목', value: mp3_title },             //10
					{ key: '앨범 커버', value: mp3_albumCover },    //
					{ key: '코멘트', value: mp3_comment },         //
					{ key: '작곡가', value: mp3_composer },        //
					{ key: '출시일', value: mp3_date },            //
					{ key: '가사', value: mp3_lyrics },            //
					{ key: '트랙', value: mp3_track },             //
					{ key: '썸네일 높이', value: file_naturalHeight },    //
					{ key: '썸네일 넓이', value: file_naturalWidth },     //
					{ key: '태그 타입', value: mp3_tagType }        //
				];
				 
				loopAry.forEach(item => {
					if (item.value) {
						detail_info += ' <div class="preview_info3">';
						detail_info += `    <span class="preview_title">${item.key} : </span>`;
						detail_info += `    <span class="preview_content" title="${item.value}">${item.value}</span>`;
						detail_info += ' </div>';
					}
				});

			}// end ( datas.dataExt === 'mp3' )
			/*-----------------------------------
			 * 상세 보기 내용 : End
			**=================================*/
            
            
// 이 아래에서 리소스를 엄청 찾이함   
			/*=====================================================================================
			 * 미리보기 출력 : Start
			**-----------------------------------------------------------------------------------*/ 
			if(preview_obj.length > 0){  
                // 두번째 선택한 이미지를 첫번째와 같이 넣음
                // preview_obj의 첫 번째 요소의 outerHTML을 가져온다
                let firstDom = preview_obj[0].outerHTML;

                // 새로운 미리보기 div를 정의
                let newPreviewDiv = ''; 
                newPreviewDiv += '    <div class="preview_in_div">';
                newPreviewDiv +=         view_obj;  // 위에서 정의해준 개체들을 미리 볼 수 있는 형태로 넣는다
                newPreviewDiv += '    </div>';

 			    if(datas.dataType.match("image.*")){  // 이미지 타입일 경우 
                    let cssDsp = 'block';
                    newPreviewDiv += '<div class="preview_img_tools" style="display:'+cssDsp+'">'; // 이미지에 관련된 콘트롤러들을 담음 
                    newPreviewDiv +=    obj_this.rulerDom(eq_idx);       // 미리보기 눈금자 :: 픽셀 잣대
                    newPreviewDiv +=    obj_this.gridLinesDom(eq_idx);   // 미리보기 눈금선 :: 격자.모눈자
                    newPreviewDiv +=    obj_this.guidesLineDome(eq_idx); // 미리보기 안내선 :: 십자선
                    newPreviewDiv +=    obj_this.imgController(eq_idx);  // 미리보기 이미지 확대,축소 콘트롤러 
                    newPreviewDiv += '</div>';  //이미지에 관련된 콘트롤러들을 담음 
                }

                // 드래그 중이 아닌 경우에만 미리보기를 업데이트
                // 미리보기와 새로 만들어진 미리보기를 넣어준다 
                let file_view_dp = loaderMain.querySelector(obj_this.file_view_dp_cls); 
                file_view_dp.querySelector('.view_preview').innerHTML = firstDom + newPreviewDiv; // newPreviewDiv만으로도 되지만 교차 페이드 아웃을 시키기 위해서 두개를 사용
                file_view_dp.querySelector('.view_detail').innerHTML  = detail_info;

			} else { 
				//미리 보기( 첫 이미지를 넣음)
				preview_div +='	<div class="preview_in_div">';  
				preview_div +=     view_obj;  // 위에서 정의해준 개체들을 미리 볼수 있는 형태로 넣는다
				preview_div +='	</div>'; 

 			    if(datas.dataType.match("image.*")){  // 이미지 타입일 경우 
                    let cssDsp = 'block'; 
                    preview_div += '<div class="preview_img_tools" style="display:'+cssDsp+'">'; // 이미지에 관련된 콘트롤러들을 담음
                    preview_div +=     obj_this.rulerDom(eq_idx);       // 미리보기 눈금자 :: 픽셀 잣대
                    preview_div +=     obj_this.gridLinesDom(eq_idx);   // 미리보기 눈금선 :: 격자.모눈자
                    preview_div +=     obj_this.guidesLineDome(eq_idx); // 미리보기 안내선 :: 십자선
                    preview_div +=     obj_this.imgController(eq_idx);  // 미리보기 이미지 확대,축소 콘트롤러
                    preview_div += '</div>';  //이미지에 관련된 콘트롤러들을 담음
                }

				//상세 보기 
				detail_div  +='	<div class="view_detail_in_div">';  
				detail_div  +=     detail_info;
				detail_div  +='	</div>'; 

				/*===================================
				** 클릭시 처음
				** 드래그 중이라해도 비어 있다면 첫 미리보기는
				** 넣는다   == 비교문으로 막지 않은 이유
				** (obj_this.drag_flag === false )
				**---------------------------------*/
                // preview_div와 detail_div를 연결하여 HTML 내용 설정 
                let file_view_dp = loaderMain.querySelector(obj_this.file_view_dp_cls);  

                if(file_view_dp.querySelector('.view_preview')){
                    file_view_dp.querySelector('.view_preview').innerHTML = preview_div;
                }
                if(file_view_dp.querySelector('.view_detail')){
                    file_view_dp.querySelector('.view_detail').innerHTML  = detail_div;
                }
			}

            // 미리보기 또는 상세보기 선택 및 미리 보기 시 페이드 인 처리 
		    chg_view(event, eq_idx);// 미리보기 혹은 상세보기 선택 및 미리 보기시 페이드 인  

            // 미리보기 이미지 콘트롤러 액션
            let preview_img_control = loaderMain.querySelector('.preview_img_control');
            if(preview_img_control){ 
                obj_this.imgControllerAct(event, eq_idx, target_idx);
            }

			MARI.file_uploder.code.view_img_idx[eq_idx] = target_idx;    

            // 현재의 미리보기 이미지 크기를 넣어둔다
            let previewImg = loaderMain.querySelector('.preview_upload_thumb2'); 
            if(previewImg){
                obj_this.resize_imgWidth[eq_idx]  = previewImg.offsetWidth; // 클릭후 현재의 미리보기 이미지 크기
                obj_this.resize_imgHeight[eq_idx] = previewImg.offsetHeight; // 클릭후 현재의 미리보기 이미지 크기  
            } else {
                obj_this.resize_imgWidth[eq_idx]  = 184; // 클릭후 현재의 미리보기 이미지 크기
                obj_this.resize_imgHeight[eq_idx] = 160; // 클릭후 현재의 미리보기 이미지 크기  
            }
			/*-------------------------------------------------------------------------------------
			 * 출력 : End
			**===================================================================================*/

			/*=====================================================================================
			** 기타 함수
			**-----------------------------------------------------------------------------------*/ 

			/*===================================
			** 선택된 영역을 보여준다,  페이드인 아웃 효과
			**---------------------------------*/ 
			function chg_view(event, eq_idx){
				let obj_this     = MARI.file_uploder.code;   
			    let viewTypeFlag = obj_this.viewTypeFlag[eq_idx]; // 미리보기창  preview( 미리보기 ),detail( 상세보기 ) 
                let loaderMain   = obj_this.loader_main_obj(eq_idx); 
                let preview_obj  = loaderMain.querySelectorAll('.preview_in_div');  // 미리보기 개체  
                let view_preview = loaderMain.querySelector('.view_preview'); 
                let view_detail  = loaderMain.querySelector('.view_detail');

                // 선택된 요소들 가져오기 
                let firstDiv  = preview_obj[0];
                let secondDiv = preview_obj[1]; 

                // 선택된 영역을 보여준다
                if (viewTypeFlag === 'preview') { // 미리보기가 선택되었다면 (열려져서 닫기를 기다리는)
                    view_detail.style.display  = 'none';
                    view_preview.style.display = 'block';
                } else if (viewTypeFlag === 'detail') { // 상세 보기가 선택되었다면
                    view_preview.style.display = 'none';
                    view_detail.style.display  = 'block';
                }

				if(act_type === 'one'){ // 하나씩 클릭일 경우 지연을 가지고 에니메이션 추가
					if(preview_obj.length > 1){
                        // 미리보기에서의 선택시 대상 에니메이션으로 페이드 인 페이드 아웃
                        obj_util.fadeOut(firstDiv, 500, 150, 'block', 0, 10, function() {
                            // 끝난 이후 실행 함수 
                            obj_util.audioAutoPlay(event, eq_idx); // 미리보기 대상이 오디오 형식일때만
                        });

 			            if(datas.dataPicture){  // 이미지 타입일 경우
                            // 이미지 첫 클릭 이후 위치 및 트랙 포인터 위치 설정
                            obj_this.setPositionImg(eq_idx, 'second');
                        }
                        
                        obj_util.fadeIn(secondDiv, 500, 150, 'block', 1, 10, function() { 
                            // 끝난 이후 실행 함수
                            firstDiv.remove(); 
                            obj_util.audioAutoPlay(event, eq_idx); // 미리보기 대상이 오디오 형식일때만 
                        }); 

                        // 히스토리 입력 
                        obj_this.historyEvent(eq_idx,  '선택한 파일 미리보기'); 
					}
					else{
                        // 이미지 첫 클릭시 위치 및 트랙 포인터 위치 설정
 			            if(datas.dataType.match("image.*")){  // 이미지 타입일 경우 
                           obj_this.setPositionImg(eq_idx, 'first');
                        }
                        obj_util.fadeIn(firstDiv, 500, 0, 'block', 1, 10, function() {
                            // 첫 실행이기에 firstDiv.remove()가 필요 없음 
							obj_util.audioAutoPlay(event, eq_idx);  // 미리보기 대상이 오디오 형식이라면  
                        }); 
					}
				}
				else
				if(act_type === 'select_range'){ // 드래그 선택일 경우 지연없이 바로 보여주기
                    // preview_obj의 요소가 1개 이상인지 확인
					if(preview_obj.length > 1){
                         firstDiv.remove();
                         secondDiv.style.opacity = '1';
					}
					else{  
                        obj_util.fadeIn(firstDiv, 0, 0, 'block', 1, 0, function() { 
                            // 첫 실행이기에 firstDiv.remove()가 필요 없음
                        });  
					}

                    // 히스토리 입력 
                    obj_this.historyEvent(eq_idx,  '선택한 파일 연속 미리보기'); 
				}
			}

		}, // file_preview_act
 


        /**
         * 선택한 곳으로 리스트 이동:::고스트 이동후 선택된 곳에 리스트를 만들어 넣고 원본 지움
         * 돔 추가 
         * drag_selection_mouse_up -> ghostMoveAfter_AddDom 
        **/
        ghostMoveAfter_AddDom : function (event, eq_idx, addListDom) {
            let obj_this      = MARI.file_uploder.code;
            let li_obj        = obj_this.li_obj(eq_idx);
            let select_cnt    = obj_this.selectCnt(eq_idx); // 드래그 선택 중인 갯수
            let select_ary    = obj_this.drag_select_ary;
            let chg_click_idx = obj_this.click_idx[eq_idx]; // 첫 클릭된 곳의 인덱스
            let targetIndex   = null;                       // 가져다 놓아서 들어갈 곳의 인덱스
            let objType       = obj_this.getMakeType(eq_idx, obj_this.click_idx[eq_idx]);

            // 위치 계산 로직 (공통 처리)
            const calculateChgIndex = (insertPos) => {
                return insertPos === 'before'
                    ? (chg_click_idx < obj_this.insert_idx ? obj_this.insert_idx - 1 : obj_this.insert_idx)
                    : (chg_click_idx < obj_this.insert_idx ? obj_this.insert_idx : obj_this.insert_idx + 1);
            };
            
            // 1.DOM 삽입 로직
            //원본
            if (['before', 'after'].includes(obj_this.insert_pos)) {
                targetIndex = calculateChgIndex(obj_this.insert_pos);
                let insertMethod = obj_this.insert_pos === 'before' ? 'beforebegin' : 'afterend';
                li_obj[obj_this.insert_idx].insertAdjacentHTML(insertMethod, addListDom); 
            }

            // 선택 파일 정렬 및 삭제
            select_ary[eq_idx] = obj_this.sortByIdx(select_ary[eq_idx]);

            // 2.데이터 변경
            let targetIndex2 = null;
            if(obj_this.insert_pos === 'after'){
                // 리스트에서 데이터 코드를 분리해서 인덱스로 사용한다
                if(objType === 'Modify'){
                    if(chg_click_idx < targetIndex){
                        targetIndex2 = parseInt(obj_this.getListDataAryIdx(eq_idx, targetIndex)) - 0;
                    } else {
                        targetIndex2 = parseInt(targetIndex) - 0;
                    }
                }
                if(objType === 'New'){ 
                    if(chg_click_idx < targetIndex){
                        targetIndex2 = parseInt(obj_this.getListDataAryIdx(eq_idx, targetIndex)) - 1;
                    } else {
                        targetIndex2 = parseInt(targetIndex) - 0;
                    }
                } 
            } else {
                if(objType === 'Modify'){
                    if(chg_click_idx < targetIndex){
                        targetIndex2 = targetIndex + 1;
                    } else {
                        targetIndex2 = targetIndex +0;
                    }
                }
                
                if(objType === 'New'){ 
                    if(chg_click_idx < targetIndex){
                        targetIndex2 = targetIndex + 0; 
                    } else {
                        targetIndex2 = targetIndex +0; 
                    }
                }
            }

            // 3. 기존 돔을 삭제해서 옮겨진 형태를 취한다
            for (let i = select_cnt - 1; i >= 0; i--) { 
                let del_idx = select_ary[eq_idx]['idx'][i];
                li_obj[del_idx].remove(); 
            }

            // 4.원본 배열 순서 변경:::배열의 순번을 옮겨진 형태로 변경한다
            obj_this.moveMultipleItemsInArray(event, eq_idx, select_ary[eq_idx], targetIndex2);
 

            // 5.대표 썸네일 갱신
            let chg_thumb_idx = obj_this.event_index(event, '.thumb_bt.on');
            document.querySelectorAll("input[name=list_thumbnail]")[eq_idx].value = chg_thumb_idx;

            let mFile  = MARI.file_uploder.code.dTa_Ary[eq_idx].modify_files;
            let mLen   = mFile.length;
            let rstAry1 = [];
            let rstAry2 = [];
            for(let i=0; i<mLen; i++){
                rstAry1[i] = mFile[i].name;
                rstAry2[i] = mFile[i].dataCode;
            }
        },



	};// end property  
}// end if(typeof(window.MARI.file_uploder.dragAction) === "undefined") {

