PageSourceSearch

https://econews.co.kr/js/Common/spectrum.js?v=20260928022845

js econews.co.kr collected 2026-10-06 10:29:59 UTC 20,995 bytes, 570 lines download raw bytes

1/**
2 * spectrum options
3 * - flat : 항상 떠있게 할지(type: flat과 동일)
4 * - showInput : input 노출 여부
5 * - allowEmpty : 공백 가능 여부
6 * - showAlpha : 투명도 노출 여부
7 * - disabled : 비활성화 여부
8 * - showPalette : 팔레트 노출 여부
9 * - palette : 기본 팔레트 설정
10 * - showPaletteOnly : 클릭 시 처음에 팔레트만 보이게 할지 여부
11 * - togglePaletteOnly : 팔레트 내 토글버튼 여부
12 * - togglePaletteLessText : 팔레트 Less 텍스트
13 * - togglePaletteMoreText : 팔레트 More 텍스트
14 * - showSelectionPalette : 팔레트 적용기록 사용 여부
15 * - localStorageKey : 팔레트 적용기록 localStorage의 Key 값
16 * - selectionPalette : 팔레트 적용기록 수동 생성
17 * - maxSelectionSize : 팔레트 최대 적용기록 (default: 8)
18 * - hideAfterPaletteSelect : 색상 선택 후 colorPicker 숨길 지 여부
19 * - clickoutFiresChange : 색상 변경 후 바깥영역 클릭 시 선택한 값 반영할지 여부
20 * - showInitial : 색상 변경 전 값을 보여줄 지 여부
21 * - chooseText : "적용" 버튼 텍스트
22 * - cancelText : "취소" 버튼 텍스트5
23 * - showButtons : 취소/적용 버튼 노출 여부
24 * - containerClassName : colorPickerSelector 클래스 이름
25 * - replacerClassName : colorPicker 클래스 이름
26 * - preferredFormat : colorPickerSelector 노출 방식(color, text, component, flat)
27 * - type : 색 적용시 실행할 함수(적용 버튼을 눌러야 함)
28 * - change : 색 적용시 실행할 함수(적용 버튼을 눌러야 함)
29 * - move : 색 적용시 실행할 함수(실시간 반영)
30 * - hide : colorPicker 창 닫을 때 실행할 함수
31 * - show : colorPicker 창 열릴 때 실행할 함수
32 * - beforeshow : colorPicker 창 열기 전 실행할 함수
33 * - dragstart : 색 드래그 시작 시 실행할 함수
34 * - dragstop : 색 드래그 종료 시 실행할 함수
35 */
36const spectrumOption = {
37    showInput: true,
38    allowEmpty: true,
39    showAlpha: false,
40    disabled: false,
41    showPalette: true,
42    showPaletteOnly: false,
43    togglePaletteOnly: false,
44    togglePaletteLessText: '숨기기',
45    togglePaletteMoreText: '더보기',
46    showSelectionPalette: true,
47    localStorageKey: "clickn.spectrum",
48    hideAfterPaletteSelect: false,
49    clickoutFiresChange: true,
50    showInitial: true,
51    chooseText: "적용",
52    cancelText: "취소",
53    showButtons: false,
54    preferredFormat: "hex",
55    type: "text",
56    useDrag: false,
57};
58
59/**
60 * element에 spectrum 실행을 위한 클릭 이벤트를 생성한다.
61 * @param {HTMLElement} el input 태그로 되어있는 element
62 * @param {function} callback 변경된 color 값을 callback 해주는 함수
63 * @param {object} option spectrum을 초기화할 때 사용할 option
64 * @returns {HTMLElement}
65 */
66function createSpectrum(el, callback = () => { }, option = {}) {
67    /*
68    el.hover(function(){
69        $(this).attr('style', `background-color: ${color}; border: 2px solid #5a626e !important; border-radius: 3px;`);
70    }, function () {
71        $(this).attr('style', `background-color: ${color}; border: 2px solid white !important; border-radius: 3px;`);
72    });*/
73
74    if ($('.sp-container').length === 0) {
75        const _this = this;
76
77        el.on('click', () => {
78            //스펙트럼 생성
79            el.spectrum({ ...spectrumOption, ...option });  
80            
81            el.on("show.spectrum", (_, color) => {
82                redesignSpectrum(el);
83                const fontColor = el.css("color");
84                const alpha = color ? color._a : 0;
85                if (alpha !== 1) {
86                    updateSpectrum(el, null, fontColor);
87                    el.spectrum('set', null);
88                } else {
89                    updateSpectrum(el, color, fontColor);
90                }
91                customInputValue();
92
93                el.attr('style', this.borderCss(color, "#000"));
94                el.off('mouseenter mouseleave');
95            });
96
97            el.spectrum('show');
98
99            // 만약 spectrum의 출력 영역 높이가 viewport를 넘어갈 때 조정
100            const spHeight = $('.sp-container').height();
101            const spOffset = $('.sp-container').offset();
102            const viewportHeight = $(window).height();
103            if (spHeight + spOffset.top > viewportHeight) {
104                $('.sp-container').css('top', `${spOffset.top - (spHeight + spOffset.top - viewportHeight)}px`);
105            }            
106           
107            //스펙트럼 색상 선택 시 이벤트           
108            el.on("move.spectrum", (_, color) => {
109                redesignSpectrum(el);
110                const fontColor = el.css("color");
111                updateSpectrum(el, color, fontColor);
112                customInputValue();
113
114                //색상 적용 시 발생 함수
115                if (callback) {
116                    const hexCode = color ? color.toHexString() : 'transparent';
117                    callback(hexCode, "move");
118                }
119            });  
120            
121            el.on("dragstop.spectrum", (_, color) => {     
122                //색상 적용 시 발생 함수
123                if (callback) {
124                    const hexCode = color ? color.toHexString() : 'transparent';
125                    callback(hexCode, "dragstop");
126                }
127            });
128
129            el.on("dragstart.spectrum", (_, color) => {     
130                //색상 적용 시 발생 함수
131                if (callback) {
132                    const hexCode = color ? color.toHexString() : 'transparent';
133                    callback(hexCode, "dragstart");
134                }
135            });
136
137            //스펙트럼 닫힐 때 이벤트
138            el.on("hide.spectrum", (_, color) => {
139                el.off("show.spectrum move.spectrum hide.spectrum");
140                el.spectrum("destroy");
141                const fontColor = el.css("color");
142                updateSpectrum(el, color, fontColor); 
143                el.attr('style', this.borderCss(color, "#FFF"));
144
145                const _this = this;
146                el.hover(function(){
147                    $(this).attr('style', _this.borderCss(color, "#5a626e"));
148                }, function () {
149                    $(this).attr('style', _this.borderCss(color, "#FFF"));
150                });
151            });
152
153            //디자인 적용
154            redesignSpectrum(el);
155
156             // 스포이드 생성
157            spoidSpectrum(el, callback);              
158           
159            return false;
160        });
161    }
162}
163
164function borderCss(color, borderColor) {
165    let style = `border: 2px solid ${borderColor} !important; border-radius: 3px;`;
166
167    if (!color || color == 'transparent') { 
168        style += ` background-repeat:no-repeat;background-size:contain;background-image:url('/images/spectrum/no_color.png');`       
169    } else {
170        style += ` background-color: ${color};`
171    }
172
173    return style
174}
175
176/**
177 * spectrum 내의 input 값을 커스텀으로 변경
178 */
179function customInputValue() {
180    // selector - input 내에 #을 before로 줬기 때문에 # 삭제
181    const input = $('div.sp-picker-container > div.sp-flex > div.sp-input-container.sp-cf > input');
182    const inputValue = input.val();
183    if (inputValue && inputValue.includes('rgba')) {
184        try {
185            const tmp = inputValue.replace('rgba', '').replaceAll(/[() ;]/g, '').split(',');
186            input.val(tmp.slice(0, 3).map((i) => Number(i).toString(16)).join(''));
187        } catch(e) {
188            input.val('');
189        }
190    } else {
191        input.val(inputValue ? inputValue.replace('#', '') : inputValue);
192    }
193}
194
195/**
196 * spectrum의 이벤트 핸들러를 통해 받은 color object를 사용하기 쉬운 데이터로 변환한다.
197 * @param {object} color spectrum 이벤트 핸들러를 통해 받은 color object
198 * @returns {object}
199 * - hex : "ff0000"
200 * - hexString : "#ff0000"
201 * - rgb : {"r":255,"g":0,"b":0}
202 * - rgbString : "rgb(255, 0, 0)"
203 * - hsv : {"h":0,"s":1,"v":1}
204 * - hsvString : "hsv(0, 100%, 100%)"
205 * - hsl : {"h":0,"s":1,"l":0.5}
206 * - hslString : "hsl(0, 100%, 50%)"
207 * - name : "red"
208 */
209function convertColor(color) {
210    if (color) {
211        return {
212            hex: color.toHex(),
213            hexString: color.toHexString(),
214            rgb: color.toRgb(),
215            rgbString: color.toRgbString(),
216            hsv: color.toHsv(),
217            hsvString: color.toHsvString(),
218            hsl: color.toHsl(),
219            hslString: color.toHslString(),
220            name: color.toName(),
221        }
222    } else {
223        return null;
224    }
225}
226
227
228/**
229 * spectrum 내 디자인을 재구성한다.
230 * - spectrum 헤더 생성
231 * - spectrum palette 내 "최근 사용 컬러" UI 생성
232 * - spectrum selector 내 initial, inputBox, spoid UI 재조합 및 수정
233 * @param {HTMLElement} el spectrum이 적용되어있는 element
234 */
235function redesignSpectrum(el) { 
236    /**
237     * color-picker-header
238     */
239    if ($('.sp-header').length === 0) {
240        $('.sp-palette-container').before($(`
241            <div id="color_picker_selector_header" class="sp-header">
242                <h2 style="font-size: 16px; color: #333333; font-weight: 700;">색상 선택</h2>
243                <button class="closeBtn" style="background: url('/images/spectrum/close.png') no-repeat center;"></button>
244            </div>
245        `));
246    }
247
248    $(".sp-header .closeBtn").on("click", () => {
249        el.spectrum("destroy");
250    })
251
252    /**
253     * color-picker-palette
254     */
255    if ($('.sp-palette.sp-thumb').find($('hr')).length === 0) {
256        const selection = $('.sp-palette-row-selection');
257        // 최근 사용한 컬러가 존재할 경우 그 중에 rgba로 입력된 버튼만 수정
258        if (selection.children().length !== 0) {
259            selection.children().map((_, el) => {
260                if ($(el).attr('data-color').includes('rgba')) {
261                    $(el).attr({
262                        'title': 'tranparent',
263                        'data-color': ''
264                    });
265                    $(el).addClass('sp-clear-display');
266                    $($(el).children()[0]).css('background-color', 'transparent');
267                }
268            });
269        }
270        // 만약 최근 사용한 컬러가 8개 미만일 경우 빈 컬러 생성
271        const selectionLength = selection.children().length;
272        for (let i = 0; i < 8 - selectionLength; i += 1) {
273            selection.append($(`
274                <span class="sp-palette-disable"></span>
275            `));
276        }
277        $('.sp-palette-row-0').before($('<p style="font-size: 14px; color: #999999; margin: 0 0 3px 3px;">최근 사용 컬러</p>'));
278        $('.sp-palette-row-0').before(selection);
279        $('.sp-palette-row-0').before($('<hr style="display: block !important; background: #e4e4e4; height: 1px; border: 0; margin: 6px 0;" />'));
280    }
281
282    /**
283     * color-picker-selector
284     */
285    // 관리자 모드일 때 투명 버튼 제거
286    if ($('.auto_keyword').length)  {
287        $('.sp-clear.sp-clear-display').remove();
288        $('.sp-hue').css({
289            'top': '0',
290            'height': '100%'
291        });
292
293        // color-picker-selector 내의 input 값에 들어가는 공백을 방지하기 위한 부분 (공백 시 투명)
294        $('.sp-input').on('focus', (e) => {
295            $('.sp-input').attr('data-before-value', e.target.value);
296        });
297        $('.sp-input').on('change', (e) => {
298            if (e.target.value === '') {
299                const color = $('.sp-input').attr('data-before-value');
300                $('.sp-input').val(color);
301                updateSpectrum(el, color);
302                el.spectrum('set', color);
303                redesignSpectrum();
304            } else {
305                $('.sp-input').attr('data-before-value', e.target.value);
306            }
307        });
308    }
309    // initial, input, spoid 재조합
310    if ($('.sp-flex').length === 0) {
311        $('.sp-top').after($('<div class="sp-flex"></div>'));
312        $('.sp-flex').append($('.sp-initial'));
313        $('.sp-flex').append($('.sp-input-container'));
314    }
315    // initial 수정
316    const arrow = $('<p class="sp-arrow" style="background: url(\'/images/spectrum/arrow.png\') no-repeat center; width: 10px; height: 10px; margin: 0 3px;"></p>');
317    const init = $('.sp-initial').find($('.sp-thumb-el.sp-thumb-active'));
318    const clear = $('.sp-initial').find($('.sp-thumb-el.sp-clear-display'));
319    const target = init.length ? init : clear;
320    if ($('.sp-palette-row-initial').find($('.sp-arrow')).length === 0 && target.length !== 0) {
321        target[target.length > 1 ? 1 : 0].before(arrow[0]);
322    }
323    const before = $('.sp-palette-row-initial').children().first();
324    if (before.attr('data-color') && before.attr('data-color').includes('rgba')) {
325        $(before).attr({
326            'title': 'tranparent',
327            'data-color': ''
328        });
329        $(before).addClass('sp-clear-display');
330        $($(before).children()[0]).css('background-color', 'transparent');
331    }
332    
333    // input 박스의 height 강제(admin 모드에서는 이미 height가 important로 선언되어있기 때문)
334    $('.sp-input').attr('style', 'height: 32px !important; padding: 0 5px 0 18px !important; text-align: start;');    
335
336    $('.sp-container').css('transform', 'translate(-40px, 8px)');
337    $('.sp-dragger').css({
338        'border': '2px solid white',
339        'border-radius': '50%'
340    });
341    $('.sp-slider').css({
342        'border': '2px solid white',
343        'background': 'transparent',
344        'height': '10px',
345        'width': '10px',
346        'left': '-1px',
347        'margin-top': '10px'
348    });
349}
350
351/**
352 * spectrum color 변경에 따라 특정 element의 배경색 등을 변경한다. (label 배경 변경 등)
353 * @param {HTMLElement} el 변경된 색상을 적용할 element
354 * @param {string | object | null | undefined} color 변경된 background 색상
355 * @param {string} fontColor 변경된 font 색상
356 */
357function updateSpectrum(el, color, fontColor) {
358    if (color && color!='transparent') { 
359        el.css({
360            "background-color": typeof color === 'string' ? color : color.toHexString(),
361            "background-repeat": "",
362            "background-size": "",
363            "background-image": "",
364            "color": fontColor,
365        });
366        el.val(color);
367    } else {
368        el.attr('style', `background-repeat:no-repeat;background-size:contain;background-image:url('/images/spectrum/no_color.png');border:2px solid white !important;`);
369        el.val('');
370    }
371}
372
373/**
374 * spectrum 내 스포이드 element 생성 후 색상 선택 시 실행할 함수도 실행한다.
375 * @param {HTMLElement} el spectrum이 적용되어있는 element
376 * @param {function} callback 적용 후 변경된 색상을 callback 해주는 함수
377 */
378function spoidSpectrum(el, callback = () => {}) {
379    if ($('.spoid').length === 0) {
380        if (window.EyeDropper) {
381            const eyeDropper = new EyeDropper(); // 브라우저에서 기본 제공하는 색깔 스포이드 객체
382            // 스포이드 버튼 생성
383            const spoid = $(`<button class="spoid" style="background: url('/images/spectrum/spoid.png') no-repeat; "></button>`);
384            spoid.on('click', () => eyeDropper.open().then((result) => {
385                el.spectrum('set', result.sRGBHex);
386                redesignSpectrum(el);
387
388                updateSpectrum(el, result.sRGBHex);
389                
390                if (callback) { 
391                    callback(result.sRGBHex, "click");
392                }
393
394            }));
395            $('.sp-flex').append(spoid);
396        } else {
397            // 브라우저에서 EyeDropper()를 지원해주지 않을 경우
398            $('.sp-input-container').css('width', '110px !important');
399        }
400    }
401}
402
403/**
404 * 색상 hex값을 HSL로 변환
405 * @param {string} hex hex 값
406 * @returns {object}
407 */
408function hexToHSL(hex) {
409    // hex값이 아닌 값이 들어올 경우
410    const regexHex = /^#?([a-fA-F\d]{2})([a-fA-F\d]{2})([a-fA-F\d]{2})$/gi;
411    const result = regexHex.exec(hex);
412    if (!result) return { h: 0, s: 0, l: 0 };
413
414    const r = parseInt(result[1], 16) / 255;
415    const g = parseInt(result[2], 16) / 255;
416    const b = parseInt(result[3], 16) / 255;
417    const max = Math.max(r, g, b);
418    const min = Math.min(r, g, b);
419    let h, s, l = (max + min) / 2;
420    if (max === min) { h = s = 0; }
421    else {
422        const d = max - min;
423        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
424        switch (max) {
425            case r: h = (g - b) / d + (g < b ? 6 : 0); break;
426            case g: h = (b - r) / d + 2; break;
427            case b: h = (r - g) / d + 4; break;
428        }
429        h /= 6;
430    }
431    return { h, s, l };
432}
433
434/**
435 * Froala Editor - Custom button의 callback 함수
436 * @param {object} Object callback 시 필요한 데이터
437 * - el : spectrum을 적용할 element
438 * - edit : froala editor edit 정보
439 * - format : froala editor format 정보
440 * - type : 'color'인지 'background-color'인지
441 * - initColor : 초기화할 색상
442 */
443function spectrumForFECallback({ el, edit, format, type, selection, initColor = '' }) {
444    edit.off();
445
446    if ($('#color_picker').length !== 0) {
447        el.spectrum('hide');
448    }
449    if ($('#color_picker_selector').length !== 0) {
450        el.spectrum('hide');
451    }
452    // $('#main_pannel_frame').contents().find('#customTextColor-1').append(el);
453    $('body').prepend(el);
454    const defaultColor = type === 'color' ? initColor : 'transparent';
455    el.spectrum({ ...spectrumOption, color: initColor === '' ? defaultColor : initColor });
456
457    redesignSpectrum(el);
458    el.on('move.spectrum show.spectrum', (_, color) => {
459        const colorString = color ? color.toHexString() : 'transparent';
460        // 스포이드 생성
461        spoidSpectrum(el, (color) => {
462            if (type === 'cell-background-color') {
463                format.setBackground(color);
464            } else {
465                format.applyStyle(type, color);
466            }
467        });
468
469        redesignSpectrum(el);
470        if (type === 'cell-background-color') {
471            format.setBackground(colorString);
472        } else {
473            format.applyStyle(type, colorString);
474        }
475        
476        // 만약 투명일 경우 투명하게 강제 전환
477        if (color && color.toRgb().a === 0) {
478            if (type === 'cell-background-color') {
479                format.setBackground('rgba(0,0,0,0)');
480            } else {
481                format.applyStyle(type, 'transparent');
482            }
483            el.spectrum('set', '');
484            redesignSpectrum(el);
485        }
486
487        customInputValue();
488    });
489    el.on('hide.spectrum', () => {
490        el.spectrum('destroy');
491        $('#color_picker').remove();
492        $('#color_picker_selector').remove();
493        $('.sp-container').remove();
494        edit.on();
495    });
496
497    el.spectrum('show');
498
499    // color_picker_selector dragable
500    if ($('.sp-container').length) {
501        $('#color_picker_selector_header').css('cursor', 'move');
502        $('.sp-container')[0].id = 'color_picker_selector';
503        dragElement($('#color_picker_selector')[0]);
504    }
505}
506
507/**
508 * Froala Editor - Custom button의 refresh 함수
509 * @param {HTMLElement} el CSS를 적용할 element
510 * @param {object} position custom button의 position 정보
511 * @param {boolean} isFirst 첫 출력인지
512 */
513function spectrumForFERefresh(el, position) {
514    let x = Math.round(position.x);
515    const y = Math.round(position.y);
516    const width = Math.round(position.width);
517    const height = Math.round(position.height);
518    el.css({
519        position: 'absolute',
520        top: `${y + 50}px`,
521        left: `${x - 200}px`,
522        transform: `translate(-${width / 2}px, -${height / 2}px)`,
523    });
524}
525
526/**
527 * params로 받는 element를 드래그 가능하게 만드는 함수
528 * @param {HTMLElement} el 드래그 가능하게 할 element
529 */
530function dragElement(el) {
531    var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
532    if ($(`#${el.id}_header`).length) {
533        $(`#${el.id}_header`).on('mousedown', dragMouseDown);
534    } else {
535        el.onmousedown = dragMouseDown;
536    }
537  
538    function dragMouseDown(e) {
539      e = e || window.event;
540      e.preventDefault();
541      
542      pos3 = e.clientX;
543      pos4 = e.clientY;
544      document.onmouseup = closeDragElement;
545      document.onmousemove = elementDrag;
546    }
547  
548    function elementDrag(e) {
549      e = e || window.event;
550      e.preventDefault();
551      
552      pos1 = pos3 - e.clientX;
553      pos2 = pos4 - e.clientY;
554      pos3 = e.clientX;
555      pos4 = e.clientY;
556      
557      el.style.top = (el.offsetTop - pos2) + "px";
558      el.style.left = (el.offsetLeft - pos1) + "px";
559    }
560  
561    function closeDragElement() {
562      document.onmouseup = null;
563      document.onmousemove = null;
564    }
565  }
566
567  //스펙트럼 삭제
568  function spectrumDestroy() {
569    $(".sp-header .closeBtn").trigger('click');   
570  }

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.