1'use strict'; 2 3/** 4 * JT javascript UI library 5 * @namespace 6 * @description UI library create to help front end developement 7 */ 8var JT = JT || {}; 9 10(function(win) { 11 12 13 14 /** 15 * Empty object will store all custom global of the site 16 * 17 * @description Sometime variables or functions need to be accessible globally. 18 * Use this object to store them, it avoid potentiel conflict with third party script. 19 * Please use this functionality with wisdom you avoid memory issue 20 * 21 * @version 1.0.0 22 * @since 2018-02-12 23 * @author STUDIO-JT (NICO) 24 * 25 * @example 26 * // Add global variable : 27 * JT.globals.myVar = 'somthing'; 28 * 29 * // Add global fucntion : 30 * JT.globals.myFunction = function(){ 31 * // alert('something') 32 * }; 33 */ 34 JT.globals = {}; 35 36 37 38 /** 39 * íë©´ì´ ê¸°ì¤ì ë³´ë¤ ììì§ íì¸í©ëë¤. 40 * Check if screen is smaller than 41 * 42 * @description CSS mediaqueries max-widthì ëì¼í©ëë¤. 43 * @version 1.0.0 44 * @since 2018-02-12 45 * @author STUDIO-JT (NICO) 46 * 47 * @example 48 * // Basic usage : 49 * JT.isScreen('768'); 50 */ 51 JT.isScreen = function( maxWidth ){ 52 53 if( win.matchMedia ) { 54 return win.matchMedia('(max-width:'+ maxWidth +'px)').matches; 55 } else { 56 return win.innerWidth <= maxWidth; 57 } 58 59 }; 60 61 62 63 /** 64 * userAgentì ë³´ì í¤ìëê° í¬í¨ëì´ ìëì§ íì¸í©ëë¤. 65 * 66 * @version 1.0.0 67 * @since 2023-03-31 68 * @author STUDIO-JT (KMS) 69 * @requires browser-selector.js 70 * 71 * @param {string} value - í¤ìë (í´ëì¤ëª ) 72 * @return {boolean} 73 * 74 * @example 75 * // Basic usage : 76 * JT.browser('mobile'); 77 */ 78 JT.browser = function( value ){ 79 80 if( document.querySelector('html').classList.contains(value) ) { 81 return true; 82 } else { 83 return false; 84 } 85 86 }; 87 88 89 90 /** 91 * JT simple confirm 92 * 93 * @description ì·¨ì/íì¸ ë²í¼ì ê°ì§ ì ìë 모ë¬ì ëìëë¤. 94 * íì¸ë²í¼ì íìë¡ ì ê³µëë©°, ì·¨ìë²í¼ì ìµì ì íµí´ íì±í ìí¬ ì ììµëë¤. 95 * 96 * @version 1.0.0 97 * @since 2023-01-18 98 * @author STUDIO-JT (KMS) 99 * 100 * @param {string|object} [args] - 모ë¬ì íìí í ì¤í¸ 문ìì´ í¹ì ìµì ì¤ë¸ì í¸ 101 * @param {string} args.message - 모ë¬ì íìí í ì¤í¸ 문ìì´ 102 * @param {boolean} [args.isChoice=false] - ì·¨ìë²í¼ ì¬ì© ì 무 103 * @param {string} [args.confirm='íì¸'] - íì¸ ë²í¼ ë¼ë²¨ 104 * @param {string} [args.cancel='ì·¨ì'] - ì·¨ì ë²í¼ ë¼ë²¨ 105 * @param {callback} [args.onConfirm] - íì¸ ë²í¼ ì½ë°± 106 * @param {callback} [args.onCancel] - ì·¨ì ë²í¼ ì½ë°± 107 * @param {callback} [cb] - ëª¨ë¬ ì ê±° í ì¤íëë ì½ë°± 108 * 109 * @example 110 * // 기본ìì : 111 * JT.confirm('Hello World'); 112 * 113 * // ì½ë°±ìì : 114 * JT.confirm('Hello World', function(){ 115 * console.log('callback'); 116 * }); 117 * 118 * // ì¤ë¸ì í¸ íì ìµì ìì : 119 * JT.confirm({ 120 * message : 'Hello World', 121 * confirm : 'íì¸', 122 * cancel : 'ì·¨ì', 123 * onConfirm : function(){ 124 * console.log('íì¸ callback'); 125 * }, 126 * onCancel : function(){ 127 * console.log('ì·¨ì callback'); 128 * } 129 * }); 130 */ 131 JT.confirm = function( args, cb ){ 132 133 if( typeof args !== 'object' && typeof args !== 'string' ) return; 134 if( typeof cb == 'undefined' ) cb = ''; 135 136 let message, isChoice, confirm, cancel, onConfirm, onCancel; 137 138 // String or object parameter 139 if( typeof args == 'object' ){ 140 message = args.message; 141 onConfirm = args.onConfirm; 142 onCancel = args.onCancel; 143 } else { 144 message = args; 145 } 146 147 // If has on_confirm callback second parameter (TODO : improve the parameter check logic) 148 if( typeof cb == 'function' && typeof args != 'object' ){ 149 onConfirm = cb; 150 } 151 152 // Set default value 153 isChoice = ( typeof args.isChoice != 'undefined' ) ? args.isChoice : false;
154 confirm = ( typeof args.confirm != 'undefined' ) ? args.confirm : 'íì¸'; 155 cancel = ( typeof args.cancel != 'undefined' ) ? args.cancel : 'ì·¨ì'; 156 157 // Get a unique id 158 const now = new Date().getTime(); 159 const uid = now / 1000 | 0; 160 const id = 'jt-confirm--' + uid; 161 162 // Defined class 163 let confirmClass = 'jt-confirm'; 164 if( isChoice ) confirmClass += ' jt-confirm--choice'; 165 166 // Add element 167 let html = document.createElement('div'); 168 html.setAttribute('id', id); 169 html.setAttribute('class', confirmClass); 170 html.setAttribute('role', 'alert'); 171 172 html.innerHTML = `<div class="jt-confirm__container"> 173 <div class="jt-confirm__content"> 174 <p>${message}</p> 175 </div> 176 <div class="jt-confirm__actions"> 177 ${isChoice ? '<button class="jt-confirm__btn jt-confirm--cancel">'+ cancel +'</button>' : ''} 178 <button class="jt-confirm__btn jt-confirm--success">${confirm}</button> 179 </div> 180 </div>`; 181 182 183 // Append element 184 document.querySelector('body').appendChild(html); 185 186 // A11y - focus 187 html.querySelector('.jt-confirm--success').setAttribute('tabindex', 0); 188 html.querySelector('.jt-confirm--success').focus(); 189 190 // Callback 191 html.querySelector('.jt-confirm--success').addEventListener('click', function(e){ 192 e.preventDefault(); 193 html.remove(); 194 if( typeof onConfirm === 'function' ) onConfirm(); 195 }); 196 197 if( isChoice ){ 198 html.querySelector('.jt-confirm--cancel').addEventListener('click', function(e){ 199 e.preventDefault(); 200 html.remove(); 201 if( typeof onCancel === 'function' ) onCancel(); 202 }); 203 } 204 205 // ESC keyevent ì°ë 206 const esc = function(e){ 207 if ( e.which == '27' ) { 208 html.remove(); 209 } 210 }; 211 document.removeEventListener('keyup', esc); 212 document.addEventListener('keyup', esc); 213 214 }; 215 216 217 218 /** 219 * Custom Alert helper 220 * 221 * @version 1.2.0 222 * @since 2018-02-12 223 * @author STUDIO-JT (NICO) 224 * 225 * @param {object} args - Alert ìµì ì¤ë¸ì í¸ 226 * @param {string} [args.theme=basic] - 미리 ì ì ë ì¤íì¼ì "basic", "classic"ì´ë©° ìíë í´ëì¤ ì´ë¦ì ì¶ê°íì¬ íì¥ í ì ììµëë¤ 227 * @param {string} [args.type=none] - 미리 ì ì ë ì¤íì¼ì "info", "success", "error"ì´ë©° ìíë í´ëì¤ ì´ë¦ì ì¶ê°íì¬ íì¥ í ì ììµëë¤ 228 * @param {boolean} [args.hasIcon=false] - ì림 ë©ì¸ì§ì ìí를 ëíë´ë ìì´ì½ì ë©ì¸ì§ ìë¨ì ì¶ê°í©ëë¤ 229 * @param {boolean} [args.isConfirm=false] - 컨í íì Alert ì¶ë ¥ (ì·¨ìë²í¼ ì¶ê°) 230 * @param {string} [args.message=false] - ì림 ë©ìì§ ë´ì© 231 * @param {string} [args.title=false] - ì림 ë©ìì§ ì 목 232 * @param {boolean} [args.titleHighlight=false] - íì¥ë ì 목 ì¤íì¼ì ì ê³µí©ëë¤ 233 * @param {boolean} [args.buttonHighlight=false] - íì¥ë ë²í¼ ì¤íì¼ì ì ê³µí©ëë¤ 234 * @param {boolean} [args.buttonIcon=false] - ë²í¼ì ê³ ì ìì´ì½ì ì¶ê°í©ëë¤ 235 * @param {string} [args.confirm=íì¸] - íì¸ ë²í¼ ë¼ë²¨ 236 * @param {string} [args.cancel=ì·¨ì] - ì·¨ì ë²í¼ ë¼ë²¨ 237 * @param {callback} [args.onConfirm] - íì¸ ë²í¼ ì½ë°± 238 * @param {callback} [args.onCancel] - ì·¨ì ë²í¼ ì½ë°± 239 * 240 * @todo create custom ui 241 * 242 * @example 243 * JT.alert({ 244 * theme : 'basic', 245 * type : 'error', 246 * hasIcon : true, 247 * isConfirm : true, 248 * message : 'ê°ì 문ì ê° ì§ìëë©´ ê´ë¦¬ììê² ì°ë½ë°ëëë¤.', 249 * title : 'ì ì¥ì ì¤í¨íìµëë¤.', 250 * titleHighlight : false, 251 * buttonHighlight : false, 252 * buttonIcon : true, 253 * confirm : 'íì¸', 254 * cancel : 'ì·¨ì', 255 * onConfirm : function(){ 256 * console.log('íì¸ callback'); 257 * }, 258 * onCancel : function(){ 259 * console.log('ì·¨ì callback'); 260 * } 261 * }); 262 */ 263 JT.alert = function( args ){ 264 265 if( typeof args !== 'object' ) return; 266 267 let theme, type, hasIcon, isConfirm, message, title, titleHighlight, buttonHighlight, buttonIcon, confirm, cancel, onConfirm, onCancel; 268 269 // Set default value 270 theme = ( typeof args.theme != 'undefined' ) ? args.theme : 'basic'; 271 type = ( typeof args.type != 'undefined' ) ? args.type : 'none'; 272 hasIcon = ( typeof args.hasIcon != 'undefined' && args.hasIcon && type != 'none' ) ? true : false;
273 isConfirm = ( typeof args.isConfirm != 'undefined' ) ? args.isConfirm : false; 274 message = ( typeof args.message != 'undefined' ) ? args.message : false; 275 title = ( typeof args.title != 'undefined' ) ? args.title : false; 276 titleHighlight = ( typeof args.titleHighlight != 'undefined' && !!title && !hasIcon ) ? args.titleHighlight : false; 277 buttonHighlight = ( typeof args.buttonHighlight != 'undefined' && !isConfirm ) ? args.buttonHighlight : false; 278 buttonIcon = ( typeof args.buttonIcon != 'undefined' ) ? args.buttonIcon : false; 279 confirm = ( typeof args.confirm != 'undefined' ) ? args.confirm : 'íì¸'; 280 cancel = ( typeof args.cancel != 'undefined' ) ? args.cancel : 'ì·¨ì'; 281 onConfirm = args.onConfirm; 282 onCancel = args.onCancel; 283 284 // Get a unique id 285 const now = new Date().getTime(); 286 const uid = now / 1000 | 0; 287 const id = 'jt-alert--' + uid; 288 289 // Defined class 290 let alertClass = 'jt-alert jt-alert--theme-' + theme + ' jt-alert--type-' + type; 291 292 if( titleHighlight ) alertClass += ' jt-alert--title-highlight'; 293 if( buttonHighlight ) alertClass += ' jt-alert--button-highlight'; 294 if( buttonIcon ) alertClass += ' jt-alert--button-icon'; 295 if( hasIcon ) alertClass += ' jt-alert--icon'; 296 if( isConfirm ) alertClass += ' jt-alert--confirm'; 297 if( buttonHighlight && !isConfirm && !hasIcon ) alertClass += ' jt-alert--reverse'; 298 299 // Add element 300 let html = document.createElement('div'); 301 html.setAttribute('id', id); 302 html.setAttribute('class', alertClass); 303 html.setAttribute('role', 'alert'); 304 305 html.innerHTML = `<div class="jt-alert__container"> 306 <div class="jt-alert__content"> 307 ${title ? '<h1>'+ title +'</h1>' : ''} 308 ${message ? '<p>'+ message +'</p>' : ''} 309 </div> 310 <div class="jt-alert__actions"> 311 ${isConfirm ? '<button class="jt-alert__btn jt-alert__btn--cancel"><span class="jt-alert__btn-label">'+ cancel +'</span></button>' : ''} 312 <button class="jt-alert__btn jt-alert__btn--confirm"><span class="jt-alert__btn-label">${confirm}</span></button> 313 </div> 314 </div>`; 315 316 if( hasIcon ) { 317 // Create SVG 318 let svg = ''; 319 320 switch ( type ) { 321 case 'info': 322 svg = '<svg width="45" height="45" viewBox="0 0 45 45" xmlns="http://www.w3.org/2000/svg"><path d="M22.5,2C33.8,2,43,11.2,43,22.5S33.8,43,22.5,43S2,33.8,2,22.5S11.2,2,22.5,2 M22.5,0C10.1,0,0,10.1,0,22.5S10.1,45,22.5,45 S45,34.9,45,22.5S34.9,0,22.5,0L22.5,0z M23.5,12.8h-2V28h2V12.8z M23.5,29.8h-2v2.4h2V29.8z"/></svg>'; 323 break; 324 case 'success': 325 svg = '<svg width="45" height="45" viewBox="0 0 45 45" xmlns="http://www.w3.org/2000/svg"><path d="M22.5,2C33.8,2,43,11.2,43,22.5S33.8,43,22.5,43S2,33.8,2,22.5S11.2,2,22.5,2 M22.5,0C10.1,0,0,10.1,0,22.5S10.1,45,22.5,45 S45,34.9,45,22.5S34.9,0,22.5,0L22.5,0z M33.2,17.5L31.9,16L19.3,26.9c-1.7-1.8-5.9-6.4-6.1-6.6l-1.4,1.4c0.2,0.2,4.1,4.5,6.7,7.3 l0.7,0.7L33.2,17.5z"/></svg>'; 326 break; 327 case 'error': 328 svg = '<svg width="45" height="45" viewBox="0 0 45 45" xmlns="http://www.w3.org/2000/svg"><path d="M30.2,16.2l-6.3,6.3l6.3,6.3l-1.4,1.4l-6.3-6.3l-6.3,6.3l-1.4-1.4l6.3-6.3l-6.3-6.3l1.4-1.4l6.3,6.3l6.3-6.3L30.2,16.2z M45,22.5C45,34.9,34.9,45,22.5,45S0,34.9,0,22.5S10.1,0,22.5,0S45,10.1,45,22.5z M43,22.5C43,11.2,33.8,2,22.5,2S2,11.2,2,22.5 S11.2,43,22.5,43S43,33.8,43,22.5z"/></svg>'; 329 break; 330 default: 331 break; 332 } 333 334 svg = new DOMParser().parseFromString(svg, 'image/svg+xml'); 335 336 // Create icon element 337 let icon = document.createElement('i'); 338 icon.setAttribute('class', 'jt-alert__icon'); 339 icon.appendChild(svg.querySelector('svg')); 340 341 html.querySelector('.jt-alert__content').prepend(icon); 342 } 343 344 if( buttonIcon ) { 345 // Confirm 346 const confirmSvg = new DOMParser().parseFromString('<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M5.1,11.1l-0.6-0.7C3.1,9,1,6.8,0.8,6.7l1.4-1.4c0.1,0.1,2,2,3.1,3.2l6.6-5.4l1.3,1.5L5.1,11.1z"/></svg>', 'image/svg+xml'); 347 348 let confirmIcon = document.createElement('i');
349 confirmIcon.setAttribute('class', 'jt-alert__btn-icon'); 350 confirmIcon.appendChild(confirmSvg.querySelector('svg')); 351 352 html.querySelector('.jt-alert__btn--confirm').prepend(confirmIcon); 353 354 // Cancel 355 if( isConfirm ) { 356 const cancelSvg = new DOMParser().parseFromString('<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M8.4,7l4.8,4.8l-1.4,1.4L7,8.4l-4.8,4.8l-1.4-1.4L5.6,7L0.8,2.2l1.4-1.4L7,5.6l4.8-4.8l1.4,1.4L8.4,7z"/></svg>', 'image/svg+xml'); 357 358 let cancelIcon = document.createElement('i'); 359 cancelIcon.setAttribute('class', 'jt-alert__btn-icon'); 360 cancelIcon.appendChild(cancelSvg.querySelector('svg')); 361 362 html.querySelector('.jt-alert__btn--cancel').prepend(cancelIcon); 363 } 364 } 365 366 // Append element 367 document.querySelector('body').appendChild(html); 368 369 // A11y - focus 370 html.querySelector('.jt-alert__btn--confirm').setAttribute('tabindex', 0); 371 html.querySelector('.jt-alert__btn--confirm').focus(); 372 373 // Callback 374 html.querySelector('.jt-alert__btn--confirm').addEventListener('click', function(e){ 375 e.preventDefault(); 376 html.remove(); 377 if( typeof onConfirm === 'function' ) onConfirm(); 378 }); 379 380 if( isConfirm ){ 381 html.querySelector('.jt-alert__btn--cancel').addEventListener('click', function(e){ 382 e.preventDefault(); 383 html.remove(); 384 if( typeof onCancel === 'function' ) onCancel(); 385 }); 386 } 387 388 // ESC keyevent ì°ë 389 const esc = function(e){ 390 if ( e.which == '27' ) { 391 html.remove(); 392 } 393 }; 394 document.removeEventListener('keyup', esc); 395 document.addEventListener('keyup', esc); 396 397 }; 398 399 400 401 /** 402 * Loading mini modal 403 * 404 * @description Loading modal component 405 * 406 * @version 1.0.0 407 * @since 2021-12-14 408 * @author STUDIO-JT (NICO) 409 * 410 * @example 411 * // ë¡ë© ì´ê¸° 412 * JT.loading.show(); 413 * 414 * // ë¡ë© ë«ê¸° 415 * JT.loading.hide(); 416 */ 417 JT.loading = { 418 show : function(message){ 419 message = typeof message === 'undefined' ? 'ë¡ë©ì¤' : message; 420 421 if( document.getElementsByClassName('jt-alert-loading').length <= 0 ) { 422 let html = document.createElement('div'); 423 html.setAttribute('class', 'jt-alert-loading'); 424 425 html.innerHTML = `<div class="jt-alert-loading__container"> 426 <div class="jt-alert-loading__content"> 427 <h1 class="jt-alert-loading__message">${message}</h1> 428 <div class="jt-alert-loading__progress"> 429 <div class="jt-alert-loading__progress-icon"></div> 430 <div class="jt-alert-loading__progress-icon"></div> 431 <div class="jt-alert-loading__progress-icon"></div> 432 </div> 433 </div> 434 </div>`; 435 436 document.querySelector('body').appendChild(html); 437 } else { 438 document.querySelector('.jt-alert-loading__message').innerText = message; 439 } 440 }, 441 remove : function(){ 442 document.querySelector('.jt-alert-loading').remove(); 443 } 444 }; 445 446 447 448 /** 449 * Smooth scroll with gsap 450 * 451 * @version 1.0.0 452 * @since 2018-02-03 453 * @author STUDIO-JT (NICO) 454 * @requires gsap.min.js 455 * @requires ScrollToPlugin.min.js 456 */ 457 JT.smoothscroll = { 458 enabled : false, 459 passive : function(){ 460 461 let supportsPassive = false; 462 463 try { 464 document.addEventListener('test', null, { get passive
464() { supportsPassive = true }}); 465 } catch(e) {} 466 467 return supportsPassive; 468 469 }, 470 init : function(){ 471 472 if( document.documentElement.classList.contains('mobile') || document.documentElement.classList.contains('mac') ) return; 473 474 if( this.passive() ){ 475 window.addEventListener('wheel', this.scrolling, { passive: false }); 476 } else { 477 window.addEventListener('mousewheel', this.scrolling); 478 window.addEventListener('DOMMouseScroll', this.scrolling); 479 } 480 481 this.enabled = true; 482 483 }, 484 destroy : function(){ 485 486 if( this.passive() ){ 487 window.removeEventListener('wheel', this.scrolling); 488 } else { 489 window.removeEventListener('mousewheel', this.scrolling); 490 window.removeEventListener('DOMMouseScroll', this.scrolling); 491 } 492 493 gsap.killTweensOf(window, { scrollTo: true }); 494 495 this.enabled = false; 496 497 }, 498 scrolling : function( event ){ 499 500 event.preventDefault(); 501 502 let scrollTime = 1; 503 let distanceOffset = 2.5; 504 let scrollDistance = ( window.innerHeight / distanceOffset ); 505 let delta = 0; 506 507 if( JT.smoothscroll.passive() ){ 508 delta = ( event.wheelDelta / 120 ) || ( -event.deltaY / 3 ); 509 } else { 510 if( typeof event.originalEvent.deltaY != 'undefined' ){ 511 delta = ( -event.originalEvent.deltaY / 120 ); 512 } else { 513 delta = ( event.originalEvent.wheelDelta / 120 ) || ( -event.originalEvent.detail / 3 ); 514 } 515 } 516 517 let scrollTop = document.documentElement.scrollTop; 518 let finalScroll = scrollTop - parseInt( delta * scrollDistance ); 519 520 gsap.to(window, { 521 duration: scrollTime, 522 scrollTo: { y: finalScroll, autoKill: true }, 523 ease: 'power3.out', 524 overwrite: 5 525 }); 526 527 } 528 529 } 530 531 532 533 /** 534 * Destroy or restore scroll mousewheel 535 * 536 * @version 1.0.0 537 * @since 2020-07-14 538 * @author STUDIO-JT (NICO, KMS) 539 * 540 * @example 541 * // ì¤í¬ë¡¤ ë¹íì±í : 542 * JT.scroll.destroy(); 543 * 544 * // ì¤í¬ë¡¤ íì±í : 545 * JT.scroll.restore(); 546 */ 547 JT.scroll = { 548 // window scroll event on/off 549 destroy : function( hasSmoothscroll ){ 550 if( typeof hasSmoothscroll != 'undefined' && hasSmoothscroll === true ) JT.smoothscroll.destroy(); 551 552 if( this.supportPassive() ){ 553 window.addEventListener('wheel', this.disabledEvent, { passive: false }); 554 } else { 555 window.addEventListener('mousewheel', this.disabledEvent); 556 window.addEventListener('DOMMouseScroll', this.disabledEvent); 557 } 558 }, 559 560 restore : function( hasSmoothscroll ){ 561 if( typeof hasSmoothscroll != 'undefined' && hasSmoothscroll === true ) JT.smoothscroll.init(); 562 563 if( this.supportPassive() ){ 564 window.removeEventListener('wheel', this.disabledEvent); 565 } else { 566 window.removeEventListener('mousewheel', this.disabledEvent); 567 window.removeEventListener('DOMMouseScroll', this.disabledEvent); 568 } 569 }, 570 571 // scroll passive mode check 572 supportPassive : function(){ 573 let supportsPassive = false; 574 575 try { 576 document.addEventListener('test', null, { get passive() { supportsPassive = true }}); 577 } catch(e) {} 578 579 return supportsPassive; 580 }, 581 582 // disabled scroll 583 disabledEvent : function( event ){ 584 event.preventDefault(); 585 } 586 }; 587 588 589 590 /** 591 * Cookies helper 592 * 593 * @Crud your cookies 594 * 595 * @version 1.0.0 596 * @since 2019-04-04 597 * @author STUDIO-JT (NICO) 598 * @see {@link https://www.quirksmode.org/js/cookies.html} 599 * 600 * @example 601 * // Cookie ì¶ê° (7ì¼) : 602 * JT.cookies.create('myCookieId','Cookie ë´ì©',7); 603 * 604 * // Cookie ì½ê¸° : 605 * JT.cookies.read('myCookieId'); 606 * 607 * // Cookie ìì : 608 * JT.cookies.destroy('myCookieId'); 609 */ 610 JT.cookies = { 611 612 create : function( name, value, days ) { 613 let expires = ''; 614 if ( days ) { 615 const date = new Date(); 616 date.setTime(date.getTime()+(days*24*60*60*1000)); 617 expires = '; expires='+date.toGMTString(); 618 } 619 document.cookie = name+'='+value+expires+'; path=/'; 620 }, 621 622 read : function( name ) { 623 const nameEQ = name + '='; 624 const ca = document.cookie.split(';'); 625 for( let i=0 ; i < ca.length ; i++ ) { 626 let c = ca[i]; 627 while ( c.charAt(0)==' ' ) c = c.substring(1,c.length); 628 if ( c.indexOf(nameEQ) == 0 ) return c.substring(nameEQ.length,c.length); 629 } 630 return null; 631 }, 632 633 destroy : function( name ) { 634 JT.cookies.create(name, '', -1); 635 } 636 637 }; 638 639 640 641 /** 642 * History manager 643 * 644 * @version 1.0.0 645 * @since 2020-07-09 646 * @author STUDIO-JT (NICO) 647 * 648 * @example 649 * // Pop up 650 * function popupInit(){ 651 * 652 * // Open event 653 * document.querySelector('.open').addEventListener('click', function(e){ 654 * 655 * e.preventDefault(); 656 * popupOpen(); 657 * JT.history.add('popup01'); 658 * 659 * }); 660 * 661 * // Close event 662 * document.querySelector('.close').addEventListener('click', function(e){ 663 * 664 * e.preventDefault(); 665 * popupClose(); 666 * JT.history.remove('popup01'); 667 * 668 * }); 669 * 670 * // Back/forward event (if history change) 671 * JT.history.listen('popup01', popupOpen, popupClose); 672 * 673 * // Open - close closures 674 * function popupOpen(){ 675 * // popup open code 676 * } 677 * 678 * function popupClose(){ 679 * // popup hide code 680 * } 681 * 682 * } 683 * 684 * // Tab example : Hash ì¬ì© 685 * function tabInit(){ 686 * 687 * JT.history.listen('tab', tabOpen, tabClose, true); 688 * 689 * if(location.hash != ''){ 690 * tabClose(); 691 * tabOpen(location.hash); 692 * } 693 * 694 * // Open - close 695 * function tabOpen(id){ 696 * // tab show code 697 * } 698 * 699 * function tabClose(){ 700 * // tab hide code 701 * } 702 * 703 * } 704 */ 705 JT.history = { 706 707 // pushstate after open the popup 708 add : function(uid){ 709 if ('history' in window && 'pushState' in history) { 710 let pushObj = {}; 711 pushObj['jt-'+uid] = 'show'; 712 history.pushState(pushObj, null, location.href); 713 } 714 }, 715 716 // Remove the current history on click close btn 717 // Todo : check only history state is popup show 718 remove : function(uid){ 719 if(history.state != null && history.state['jt-'+uid] == 'show'){ 720 history.back(); // the magic remove state by spidoche (old school functions are good) 721 } 722 }, 723 724 // Listen back/forward btn click 725 listen : function(uid,open,close,hash){ 726 727 // set default value 728 if(typeof hash == 'undefined'){ 729 hash = false;
730 } 731 732 // Hash mode 733 if(hash){ 734 735 window.addEventListener('hashchange', function(e){ 736 737 close(); 738 open(location.hash); 739 740 }); 741 window.addEventListener 742 743 // PopState mode 744 } else if ('PopStateEvent' in window) { 745 746 window.addEventListener('popstate', function(e){ 747 748 if(e.state != null && e.state['jt-'+uid] == 'show'){ 749 // forward button 750 if(typeof open === 'function'){ 751 open(); 752 } 753 }else{ 754 // back button 755 if(typeof close === 'function'){ 756 close(); 757 } 758 759 // remove forward if not need 760 if(typeof open !== 'function'){ 761 762 // Clean forwar history 763 //history.forward(); 764 765 } 766 } 767 768 }, false); 769 770 } 771 } 772 }; 773 774 775 776 /** 777 * UI Helper 778 * 779 * @description UI ê´ë ¨ í¨ì ê´ë¦¬ì© í¬í¼ 780 * 781 * @version 1.0.0 782 * @since 2018-04-12 783 * @author STUDIO-JT (201) 784 * 785 * @example 786 * // ë±ë¡ë 모ë í¨ì ì¤íí기 787 * JT.ui.init(); 788 * 789 * // í¨ì ë±ë¡í기 790 * JT.ui.add( function () { 791 * // alert('something') 792 * }; 793 * JT.ui.add( test_func ); 794 * function test_func () { 795 * // alert( 'somethid' ); 796 * }; 797 * 798 * // í¨ìëª (string) ì¼ë¡ ìì í기 799 * JT.ui.del( func_name ); 800 * JT.ui.del( 'test_func' ); 801 * 802 * // í¨ìëª (string)ì¼ë¡ í¨ì ê°ì ¸ì¤ê¸° 803 * JT.ui.get( func_name ); 804 * JT.ui.get( 'test_func' ); 805 * 806 * // í¨ìëª (string)ì¼ë¡ í¨ì ì¤íí기 807 * JT.ui.call( func_name ); 808 * JT.ui.call( 'test_func' ); 809 * 810 * // ìµëª í¨ìë func_{timestamp} ë¡ ì¶ê°ë¨ 811 */ 812 JT.ui = { 813 814 list: {}, 815 816 init: function () { 817 818 try { 819 820 for ( let func_name in this.list ) { 821 822 if ( typeof this.list[ func_name ] === 'function' ) { 823 824 this.list[ func_name ].call(); 825 826 } 827 828 } 829 830 } catch ( e ) { 831 832 console.log( e ); 833 834 } 835 836 }, 837 838 add: function ( func, exec_flag ) { 839 840 try { 841 842 if ( typeof func === 'function' ) { 843 844 const func_name = ( ! func.name ? func.toString().match(/^function\s*([^\s(]+)/)[1] : func.name ); 845 846 this.list[ func_name ] = func; 847 848 if ( typeof exec_flag !== 'undefined' && exec_flag === true ) { 849 850 func.call(); 851 852 } 853 854 } 855 856 } catch ( e ) { 857 858 console.log( e ); 859 860 } 861 862 }, 863 864 del: function ( func_name ) { 865 866 try { 867 868 delete this.list[ func_name ]; 869 870 } catch ( e ) { 871 872 console.log( e ); 873 874 } 875 876 }, 877 878 replace: function ( func_name, func ) { 879 880 try { 881 882 if ( typeof func === 'function' ) { 883 884 this.list[ func_name ] = func; 885 886 } 887 888 } catch ( e ) { 889 890 console.log( e ); 891 892 } 893 894 }, 895 896 get: function ( func_name ) { 897 898 try { 899 900 return this.list[ func_name ]; 901 902 } catch ( e ) { 903 904 console.log( e ); 905 return null; 906 907 } 908 909 }, 910 911 call: function ( func_name ) { 912 913 try { 914 915 this.list[ func_name ].call(); 916 917 } catch ( e ) { 918 919 console.log( e ); 920 921 } 922 923 } 924 925 }; 926 927 928 929 /** 930 * ì¡°ì¬ Helper 931 * 932 * @description ì/를, ì/ë ë± ì¡°ì¬ë¥¼ ê°ì ¸ì¤ë í¬í¼ 933 * 934 * @version 1.0.0 935 * @since 2021-01-20 936 * @author STUDIO-JT (201) 937 * @see https://github.com/e-/Josa.js/ 938 * 939 * @example 940 * JT.josa( 'ì¬ê³¼', 'ì/를' ); // return '를' 941 * JT.josa( 'ì¬ê³¼', 'ì' ); // return '를' 942 * JT.josa( 'ì¬ê³¼', '를' ); // return '를' 943 * JT.josa( 'ì¬ê³¼', 'ì를' ); // return '를' 944 */ 945 JT.josa = function ( word, format, join ) { 946 947 const _f = [ 948 function ( string ) { return _hasJong(string) ? 'ì' : '를'; }, //ì/를 êµ¬ë¶ 949 function ( string ) { return _hasJong(string) ? 'ì' : 'ë'; }, //ì/ë êµ¬ë¶ 950 function ( string ) { return _hasJong(string) ? 'ì´' : 'ê°'; }, //ì´/ê° êµ¬ë¶ 951 function ( string ) { return _hasJong(string) ? 'ê³¼' : 'ì'; }, //ì/ê³¼ êµ¬ë¶ 952 function ( string ) { return _hasJong(string) ? 'ì¼ë¡' : 'ë¡'; } //ì¼ë¡/ë¡ êµ¬ë¶ 953 ]; 954 955 const _formats = { 956 'ì/를' : _f[0], 957 'ì' : _f[0], 958 '를' : _f[0], 959 'ì를' : _f[0], 960 'ì/ë' : _f[1], 961 'ì' : _f[1], 962 'ë' : _f[1], 963 'ìë' : _f[1], 964 'ì´/ê°' : _f[2], 965 'ì´' : _f[2], 966 'ê°' : _f[2], 967 'ì´ê°' : _f[2], 968 'ì/ê³¼' : _f[3], 969 'ì' : _f[3], 970 'ê³¼' : _f[3], 971 'ìê³¼' : _f[3], 972 'ì¼ë¡/ë¡' : _f[4], 973 'ì¼ë¡' : _f[4], 974 'ë¡' : _f[4], 975 'ì¼ë¡ë¡' : _f[4] 976 } 977 978 979 if ( typeof _formats[ format ] === 'undefiend' ) throw 'Invalid format'; 980 981 return ( join ? word : '' ) + _formats[ format ]( word ); 982 983 function _hasJong( string ) { //stringì ë§ì§ë§ ê¸ìê° ë°ì¹¨ì ê°ì§ëì§ íì¸ 984 985 return ( string.charCodeAt( string.length - 1 ) - 0xac00 ) % 28 > 0; 986 987 } 988 989 } 990 991 992 993 /** 994 * Task Helper 995 *
996 * @description ë¹ë기 ì²ë¦¬ë¥¼ ëê¸°ë¡ ì²ë¦¬í기 ìí Task í¬í¼ 997 * 998 * @version 1.0.0 999 * @since 2021-12-10 1000 * @author STUDIO-JT (201) 1001 * @see https://medium.com/@griffinmichl/asynchronous-javascript-queue-920828f6327 1002 * 1003 * @example 1004 * const task = JT.task(); 1005 * task.push( done => { setTimeout( () => { console.log( 'test - 1' ); done(); }, 150 } ); 1006 * task.push( done => { setTimeout( () => { console.log( 'test - 2' ); done(); }, 50 } ); 1007 * task.push( done => { setTimeout( () => { console.log( 'test - 3' ); done(); }, 100 } ); 1008 * task.run(); 1009 * 1010 * // Will Return 1011 * test - 1 1012 * test - 2 1013 * test - 3 1014 */ 1015 JT.task = function () { 1016 1017 let taskQueue = []; 1018 1019 function enqueueTask( task ) { 1020 return taskQueue.push( task ); 1021 } 1022 1023 function runTask() { 1024 let task = taskQueue.shift(); 1025 task( function () { 1026 if ( taskQueue.length > 0 ) { 1027 runTask(); 1028 } 1029 } ); 1030 } 1031 1032 return { 1033 push: enqueueTask, 1034 run: runTask 1035 } 1036 1037 } 1038 1039 1040 1041 /** 1042 * Input validation 1043 * 1044 * @version 1.0.0 1045 * @since 2023-04-05 1046 * @author STUDIO-JT (KMS) 1047 * 1048 * @description form íê·¸ì novalidate ì í¸ë¦¬ë·°í¸ê° í¨ê» ì ê³µëì´ì¼ í©ëë¤. (The form tag must be provided with the novalidate attribute.) 1049 * 1050 * @param {element} element - Input ìë¦¬ë¨¼í¸ (validation target) 1051 * @param {string} wrapSelector='.jt-form__data' - Input 컨í ì´ë í´ëì¤ 1052 * @param {string} validSelector='.jt-form__valid' - Validation ë©ì¸ì§ê° ì¶ë ¥ë ìë¦¬ë¨¼í¸ 1053 * @return {boolean} 1054 * 1055 * @example 1056 * // HTML 1057 * <form novalidate> 1058 * </form> 1059 * 1060 * // Basic usage : 1061 * JT.validation(input[type="text"]); 1062 * JT.validation(input[type="text"], '.jt-form__data'. '.jt-form__valid'); 1063 */ 1064 JT.validation = function( element, wrapSelector, validSelector ) { 1065 1066 const field = element; 1067 const container = ( typeof wrapSelector == 'undefined' ) ? field.closest('.jt-form__data') : field.closest(wrapSelector); 1068 const validTip = ( typeof wrapSelector == 'undefined' ) ? container.querySelector('.jt-form__valid') : container.querySelector(validSelector); 1069 const dataset = field.dataset; 1070 let isValid = true; 1071 1072 if( !!!container || !!!validTip ) { 1073 if( !!!container ) console.warn('ì í¨ì± ê²ì¬ë¥¼ ìí ìì를 íì¸í´ì£¼ì¸ì. undefined ' + field.getAttribute('id') + ' container'); 1074 if( !!!validTip ) console.warn('ì í¨ì± ê²ì¬ë¥¼ ìí ìì를 íì¸í´ì£¼ì¸ì. undefined ' + field.getAttribute('id') + ' valid element'); 1075 return; 1076 } 1077 1078 if( !field.validity.valid ) { 1079 1080 if( field.validity.valueMissing ) { // Required message 1081 1082 if( 'msgRequired' in dataset ) { 1083 validTip.textContent = dataset.msgRequired; 1084 } else { 1085 validTip.textContent = 'íìí목ì ì ë ¥í´ ì£¼ì¸ì.'; 1086 } 1087 1088 } else if( field.validity.typeMismatch || field.validity.patternMismatch ) { // Invalid message 1089 1090 if( 'msgInvalid' in dataset ) { 1091 validTip.textContent = dataset.msgInvalid; 1092 } else { 1093 validTip.textContent = 'ì íí íìì¼ë¡ 기ì¬í´ 주ì¸ì.'; 1094 } 1095 1096 } else if( field.validity.tooLong ) { 1097 1098 validTip.textContent = field.maxlength ? `${ field.maxlength }ì ì´ë´ë¡ ì ë ¥í´ ì£¼ì¸ì.` : 'ìµë íì© ê¸¸ì´ë³´ë¤ ê¹ëë¤.'; 1099 1100 } else if( field.validity.tooShort ) { 1101 1102 validTip.textContent = field.minlength ? `${ field.minlength }ì ì´ì ì ë ¥í´ ì£¼ì¸ì.` : 'ìµì íì© ê¸¸ì´ë³´ë¤ ì§§ìµëë¤.';; 1103 1104 } 1105 1106 // Add error class 1107 container.classList.add('jt-form__data--error'); 1108 1109 // Flag 1110 isValid = false;
1111 1112 } else { 1113 1114 validTip.textContent = ''; 1115 1116 // Remove error class 1117 container.classList.remove('jt-form__data--error'); 1118 1119 } 1120 1121 return isValid; 1122 1123 } 1124 1125 1126 1127 /** 1128 * GSAP killChildTweensOf helper 1129 * Because killChildTweensOf has been removed in gsap 3 1130 * https://greensock.com/forums/topic/22033-killchildtweensof-replacement/?do=findComment&comment=103903 1131 * 1132 * @version 1.0.0 1133 * @since 2022-02-10 1134 * @author STUDIO-JT (NICO) 1135 * 1136 * @example 1137 * JT.killChildTweensOf(document.getElementById('main')); 1138 */ 1139 JT.killChildTweensOf = function( parent, complete ) { 1140 1141 let parents = gsap.utils.toArray(parent), 1142 i = parents.length, 1143 _isDescendant = function(element) { 1144 while (element) { 1145 element = element.parentNode; 1146 if (element === parent) { 1147 return true; 1148 } 1149 } 1150 }, 1151 j, tweens, targets; 1152 if (i > 1) { 1153 while (--i > -1) { 1154 killChildTweensOf(parents[i], complete); 1155 } 1156 return; 1157 } 1158 parent = parents[0]; 1159 tweens = gsap.globalTimeline.getChildren(true, true, false); 1160 for (i = 0; i < tweens.length; i++) { 1161 targets = tweens[i].targets(); 1162 j = targets.length; 1163 for (j = 0; j < targets.length; j++) { 1164 if (_isDescendant(targets[j])) { 1165 if (complete) { 1166 tweens[i].totalProgress(1); 1167 } 1168 tweens[i].kill(); 1169 } 1170 } 1171 } 1172 1173 } 1174 1175 1176 1177 /** 1178 * JT Slide Up, Down and Toggle 1179 * 1180 * @version 1.0.0 1181 * @since 2023-04-05 1182 * @author STUDIO-JT (KMS) 1183 * 1184 * @param {element} selector - ì¬ë¼ì´ë íê² ìë¦¬ë¨¼í¸ 1185 * @param {number} duration=500 - 모ì ìë 1186 * @param {callback} callback=undefined - 모ì ìë£í ì¤íëë ì½ë°± 1187 * 1188 * @example 1189 * // Basic usage : 1190 * JT.slide.up( document.querySelector('.item') ); 1191 * JT.slide.down( document.querySelector('.item') ); 1192 * JT.slide.toggle( document.querySelector('.item') ); 1193 * 1194 * // Callback : 1195 * JT.slide.toggle( document.querySelector('.item'), 500, function(){ 1196 * // console.log('callback'); 1197 * }); 1198 */ 1199 JT.slide = { 1200 1201 up: function( selector, duration=500, callback=undefined ) { 1202 1203 selector.style.transitionProperty = 'height, margin, padding'; 1204 selector.style.transitionDuration = duration + 'ms'; 1205 selector.style.boxSizing = 'border-box'; 1206 selector.style.height = selector.offsetHeight + 'px'; 1207 selector.offsetHeight; 1208 selector.style.overflow = 'hidden'; 1209 selector.style.height = 0; 1210 selector.style.paddingTop = 0; 1211 selector.style.paddingBottom = 0; 1212 selector.style.marginTop = 0; 1213 selector.style.marginBottom = 0; 1214 1215 window.setTimeout( () => { 1216 selector.style.display = 'none'; 1217 selector.style.removeProperty('height'); 1218 selector.style.removeProperty('padding-top'); 1219 selector.style.removeProperty('padding-bottom'); 1220 selector.style.removeProperty('margin-top'); 1221 selector.style.removeProperty('margin-bottom'); 1222 selector.style.removeProperty('overflow'); 1223 selector.style.removeProperty('transition-duration'); 1224 selector.style.removeProperty('transition-property'); 1225 1226 if( typeof callback === 'function' ) { 1227 callback(); 1228 } 1229 }, duration); 1230 1231 }, 1232 1233 down: function( selector, duration=500, callback=undefined ) { 1234 1235 selector.style.removeProperty('display'); 1236 let display = window.getComputedStyle(selector).display; 1237 if ( display === 'none' ) display = 'block'; 1238 selector.style.display = display; 1239 1240 const height = selector.offsetHeight; 1241 1242 selector.style.overflow = 'hidden'; 1243 selector.style.height = 0; 1244 selector.style.paddingTop = 0; 1245 selector.style.paddingBottom = 0; 1246 selector.style.marginTop = 0; 1247 selector.style.marginBottom = 0; 1248 selector.offsetHeight; 1249 selector.style.boxSizing = 'border-box'; 1250 selector.style.transitionProperty = 'height, margin, padding'; 1251 selector.style.transitionDuration = duration + 'ms'; 1252 selector.style.height = height + 'px'; 1253 selector.style.removeProperty('padding-top'); 1254 selector.style.removeProperty('padding-bottom'); 1255 selector.style.removeProperty('margin-top'); 1256 selector.style.removeProperty('margin-bottom'); 1257 1258 window.setTimeout( () => { 1259 selector.style.removeProperty('height'); 1260 selector.style.removeProperty('overflow'); 1261 selector.style.removeProperty('transition-duration'); 1262 selector.style.removeProperty('transition-property'); 1263 1264 if( typeof callback === 'function' ) { 1265 callback(); 1266 } 1267 }, duration); 1268 1269 }, 1270 1271 toggle: function( selector, duration=500, callback=undefined ) { 1272 1273 if ( window.getComputedStyle(selector).display === 'none' ) { 1274 this.down(selector, duration, callback); 1275 } else { 1276 this.up(selector, duration, callback); 1277 } 1278 1279 } 1280 1281 } 1282 1283 1284 1285 /** 1286 * JT offset 1287 * 1288 * @version 1.0.0 1289 * @since 2023-04-05 1290 * @author STUDIO-JT (KMS) 1291 * 1292 * @param {element} selector - offset ëì ìë¦¬ë¨¼í¸ 1293 * @return {number} offset ë°ì´í° 1294 * 1295 * @example 1296 * // Basic usage : 1297 * JT.offset.top('.item'); 1298 * JT.offset.left('.item'); 1299 */ 1300 JT.offset = { 1301 1302 top: function( selector ){ 1303 1304 selector = ( selector instanceof HTMLElement ) ? selector : document.querySelector( selector ); 1305 const rect = selector.getBoundingClientRect(); 1306 return rect.top + window.scrollY; 1307 1308 }, 1309 1310 left: function( selector ){ 1311 1312 selector = ( selector instanceof HTMLElement ) ? selector : document.querySelector( selector ); 1313 const rect = selector.getBoundingClientRect(); 1314 return rect.left + window.scrollX; 1315 1316 } 1317 1318 } 1319 1320 1321 1322})(window);
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.