widgets.nosql 175 KB

1234567891011121314151617181920212223242526272829
  1. {"id":"19062510010001ohf1","name":"view-engine-partial","reference":"view-engine/partial.html","body":"<script editor>\n option('id', 'Partial page', '', 'Partial');\n\n exports.configure = function(options, el) {\n\n var text = 'not defined';\n\n if (options.id) {\n var item = this.id.findItem('value', options.id);\n item && (text = item.text);\n }\n\n el.find('.totaljs').text('Partial: ' + text);\n\n };\n\n</script>\n\n<script total>\n exports.render = function(options, html, next) {\n if (!options.id)\n return next('');\n this.CMSpartial(options.id, function(err, response) {\n if (response && response.body)\n next(html.substring(0, html.indexOf('<div class=\"totaljs')) + response.body + '</div>');\n else\n next('');\n });\n };\n</script>\n\n<div class=\"wm wp wb wc146\">\n <div class=\"totaljs\">Partial page</div>\n</div>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"view-engine","dateupdated":"2019-06-28T06:06:46.019Z"}
  2. {"id":"19062510010002ohf0","name":"basics-addon-goto-v1","reference":"basics/addon/goto/v1.html","body":"<style></style>\n<script>\n COMPONENT('basics-addon-goto-v1', function(component, config){\n component.$el = component.element;\n\n component.make = function(){\n $.HSCore.components.HSGoTo.init('.js-go-to', component.$el);\n };\n }, [\n '/libs/ui/1.0.0/js/components/hs.go-to.js',\n ]);\n</script>\n\n<div class=\"basics-addon-goto-v1\" data-jc=\"basics-addon-goto-v1\">\n <a class=\"js-go-to u-go-to-v2\" href=\"#!\" data-type=\"fixed\" data-position='{\"bottom\": 15,\"right\": 15}' data-offset-top=\"400\" data-compensation=\"#js-header\" data-show-effect=\"zoomIn\">\n <i class=\"fa fa-angle-up\"></i>\n </a>\n</div>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/addon/goto","dateupdated":"2019-06-28T06:06:46.019Z"}
  3. {"id":"19062510010003ohf1","name":"basics-footer-bottombar-v1","reference":"basics/footer/bottombar/v1.html","body":"<style></style>\n\n<footer class=\"g-bg-gray-dark-v1 g-color-white-opacity-0_8 g-py-20 basics-footer-bottombar-v1\">\n <div class=\"container\">\n <div class=\"row\">\n <div class=\"col-md-8 text-center text-md-left g-mb-10 g-mb-0--md\">\n <div class=\"d-lg-flex\">\n <small class=\"d-block g-font-size-default g-mr-10 g-mb-10 g-mb-0--md CMS_edit\">1998-2018 © All Rights Reserved.</small>\n <ul class=\"u-list-inline\">\n <li class=\"list-inline-item\"><a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit\" href=\"#!\">개인정보처리방침</a></li>\n <li class=\"list-inline-item\"><span>|</span></li>\n <li class=\"list-inline-item\"><a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit\" href=\"#!\">뷰어다운로드</a></li>\n <li class=\"list-inline-item\"><span>|</span></li>\n <li class=\"list-inline-item\"><a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit\" href=\"#!\">찾아오시는길</a></li>\n <li class=\"list-inline-item\"><span>|</span></li>\n <li class=\"list-inline-item\"><a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit\" href=\"#!\">사이트맵</a></li>\n </ul>\n </div>\n </div>\n\n <div class=\"col-md-4 align-self-center\">\n <ul class=\"list-inline text-center text-md-right mb-0\">\n <li class=\"list-inline-item g-mx-10\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"Facebook\">\n <a href=\"#!\" class=\"g-color-white-opacity-0_5 g-color-white--hover CMS_edit\"><i class=\"fab fa-facebook\"></i></a>\n </li>\n <li class=\"list-inline-item g-mx-10\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"Skype\">\n <a href=\"#!\" class=\"g-color-white-opacity-0_5 g-color-white--hover CMS_edit\"><i class=\"fab fa-skype\"></i></a>\n </li>\n <li class=\"list-inline-item g-mx-10\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"Linkedin\">\n <a href=\"#!\" class=\"g-color-white-opacity-0_5 g-color-white--hover CMS_edit\"><i class=\"fab fa-linkedin\"></i></a>\n </li>\n <li class=\"list-inline-item g-mx-10\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"Pinterest\">\n <a href=\"#!\" class=\"g-color-white-opacity-0_5 g-color-white--hover CMS_edit\"><i class=\"fab fa-pinterest\"></i></a>\n </li>\n <li class=\"list-inline-item g-mx-10\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"Twitter\">\n <a href=\"#!\" class=\"g-color-white-opacity-0_5 g-color-white--hover CMS_edit\"><i class=\"fab fa-twitter\"></i></a>\n </li>\n <li class=\"list-inline-item g-mx-10\" data-toggle=\"tooltip\" data-placement=\"top\" title=\"\" data-original-title=\"Dribbble\">\n <a href=\"#!\" class=\"g-color-white-opacity-0_5 g-color-white--hover CMS_edit\"><i class=\"fab fa-dribbble\"></i></a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n</footer>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/footer/bottombar","dateupdated":"2019-06-28T06:06:46.019Z"}
  4. {"id":"19062510010004ohf0","name":"basics-footer-contactus-v1","reference":"basics/footer/contactus/v1.html","body":"<style></style>\n\n<script editor>\n option('addressBgImage', 'Background Image for Address Area', '', 'file');\n\n exports.configure = async function(options, $el, prevOptions){\n $('address.bg-address', $el).css('background-image', `url(${options.addressBgImage})`);\n };\n</script>\n\n<div class=\"g-bg-black-opacity-0_9 g-color-white-opacity-0_8 g-py-60 basics-footer-contactus-v1\">\n <div class=\"container\">\n <div class=\"row\">\n <!-- Footer Content -->\n <div class=\"col-lg-3 col-md-6 g-mb-40 g-mb-0--lg\">\n <div class=\"u-heading-v2-3--bottom g-brd-white-opacity-0_8 g-mb-20\">\n <h2 class=\"u-heading-v2__title h6 text-uppercase mb-0 CMS_edit\">여기를 편집하세요.</h2>\n </div>\n\n <p class=\"CMS_edit\">여기를 편집하세요.</p>\n </div>\n <!-- End Footer Content -->\n\n <!-- Footer Content -->\n <div class=\"col-lg-3 col-md-6 g-mb-40 g-mb-0--lg\">\n <div class=\"u-heading-v2-3--bottom g-brd-white-opacity-0_8 g-mb-20\">\n <h2 class=\"u-heading-v2__title h6 text-uppercase mb-0 CMS_edit\">여기를 편집하세요.</h2>\n </div>\n\n <article>\n <h3 class=\"h6 g-mb-2\">\n <a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit CMS_attribute\" href=\"#!\">여기를 편집하세요.</a>\n </h3>\n <div class=\"small g-color-white-opacity-0_6 CMS_edit\">2019-01-01</div>\n </article>\n\n <hr class=\"g-brd-white-opacity-0_1 g-my-10\">\n\n <article>\n <h3 class=\"h6 g-mb-2\">\n <a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit CMS_attribute\" href=\"#!\">여기를 편집하세요.</a>\n </h3>\n <div class=\"small g-color-white-opacity-0_6 CMS_edit\">2019-01-01</div>\n </article>\n\n <hr class=\"g-brd-white-opacity-0_1 g-my-10\">\n\n <article>\n <h3 class=\"h6 g-mb-2\">\n <a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit CMS_attribute\" href=\"#!\">여기를 편집하세요.</a>\n </h3>\n <div class=\"small g-color-white-opacity-0_6 CMS_edit\">2019-01-01</div>\n </article>\n </div>\n <!-- End Footer Content -->\n\n <!-- Footer Content -->\n <div class=\"col-lg-3 col-md-6 g-mb-40 g-mb-0--lg\">\n <div class=\"u-heading-v2-3--bottom g-brd-white-opacity-0_8 g-mb-20\">\n <h2 class=\"u-heading-v2__title h6 text-uppercase mb-0 CMS_edit\">여기를 편집하세요.</h2>\n </div>\n\n <nav class=\"text-uppercase1\">\n <ul class=\"list-unstyled g-mt-minus-10 mb-0\">\n <li class=\"g-pos-rel g-brd-bottom g-brd-white-opacity-0_1 g-py-10\">\n <h4 class=\"h6 g-pr-20 mb-0\">\n <a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit CMS_attribute\" href=\"#!\">여기를 편집하세요.</a>\n <i class=\"fa fa-angle-right g-absolute-centered--y g-right-0\"></i>\n </h4>\n </li>\n <li class=\"g-pos-rel g-brd-bottom g-brd-white-opacity-0_1 g-py-10\">\n <h4 class=\"h6 g-pr-20 mb-0\">\n <a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit CMS_attribute\" href=\"#!\">여기를 편집하세요.</a>\n <i class=\"fa fa-angle-right g-absolute-centered--y g-right-0\"></i>\n </h4>\n </li>\n <li class=\"g-pos-rel g-brd-bottom g-brd-white-opacity-0_1 g-py-10\">\n <h4 class=\"h6 g-pr-20 mb-0\">\n <a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit CMS_attribute\" href=\"#!\">여기를 편집하세요.</a>\n <i class=\"fa fa-angle-right g-absolute-centered--y g-right-0\"></i>\n </h4>\n </li>\n <li class=\"g-pos-rel g-brd-bottom g-brd-white-opacity-0_1 g-py-10\">\n <h4 class=\"h6 g-pr-20 mb-0\">\n <a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit CMS_attribute\" href=\"#!\">여기를 편집하세요.</a>\n <i class=\"fa fa-angle-right g-absolute-centered--y g-right-0\"></i>\n </h4>\n </li>\n </ul>\n </nav>\n </div>\n <!-- End Footer Content -->\n\n <!-- Footer Content -->\n <div class=\"col-lg-3 col-md-6\">\n <div class=\"u-heading-v2-3--bottom g-brd-white-opacity-0_8 g-mb-20\">\n <h2 class=\"u-heading-v2__title h6 text-uppercase mb-0 CMS_edit\">여기를 편집하세요.</h2>\n </div>\n\n <address class=\"g-bg-no-repeat g-font-size-12 mb-0 bg-address\" style=\"background-image: url(//via.placeholder.com/255x154)\">\n <!-- Location -->\n <div class=\"d-flex g-mb-20\">\n <div class=\"g-mr-10\">\n <span class=\"u-icon-v3 u-icon-size--xs g-bg-white-opacity-0_1 g-color-white-opacity-0_6\">\n <i class=\"fa fa-map-marker\"></i>\n </span>\n </div>\n <p class=\"mb-0 CMS_edit CMS_multiline\">여기를 편집하세요.\n <br>\n 여러줄 입력이 가능합니다.\n </p>\n </div>\n <!-- End Location -->\n\n <!-- Phone -->\n <div class=\"d-flex g-mb-20\">\n <div class=\"g-mr-10\">\n <span class=\"u-icon-v3 u-icon-size--xs g-bg-white-opacity-0_1 g-color-white-opacity-0_6\">\n <i class=\"fa fa-phone\"></i>\n </span>\n </div>\n <p class=\"mb-0 CMS_edit CMS_multiline\">TEL : 000-000-0000\n <br>\n FAX : 000-000-0000\n </p>\n </div>\n <!-- End Phone -->\n\n <!-- Email and Website -->\n <div class=\"d-flex g-mb-20\">\n <div class=\"g-mr-10\">\n <span class=\"u-icon-v3 u-icon-size--xs g-bg-white-opacity-0_1 g-color-white-opacity-0_6\">\n <i class=\"fa fa-globe\"></i>\n </span>\n </div>\n <p class=\"mb-0\">\n <a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit CMS_attribute\" href=\"mailto:info@kice.re.kr\">example@example.com</a>\n <br>\n <a class=\"g-color-white-opacity-0_8 g-color-white--hover CMS_edit\" href=\"#!\">example.com</a>\n </p>\n </div>\n <!-- End Email and Website -->\n </address>\n </div>\n <!-- End Footer Content -->\n </div>\n </div>\n</div>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/footer/contactus","dateupdated":"2019-06-28T06:06:46.019Z"}
  5. {"id":"19062510010005ohf1","name":"basics-header-breadcrumb-v1","reference":"basics/header/breadcrumb/v1.html","body":"<style></style>\n\n<script>\n COMPONENT('basics-header-breadcrumb-v1', function(component, config){\n component.$el = component.element;\n component.$ul = $('ul.u-list-inline', component.$el);\n component.$header = $('header', component.$el);\n\n component.$ul.empty();\n\n // get mainmenu before `make()` execute\n if ( !window._TOTALCMS_MAINMENU ) component.template = '/api/v3/nav/mainmenu';\n\n component.make = function(mainmenu){\n if ( !window._TOTALCMS_MAINMENU ) window._TOTALCMS_MAINMENU = mainmenu;\n else mainmenu = window._TOTALCMS_MAINMENU;\n\n let serializedTree = serializeTree([mainmenu]);\n let node = serializedTree.find(function(item){ return item.url === location.pathname });\n\n let nodePath = getPath(serializeTree(serializedTree), node);\n\n nodePath.forEach(function(item, idx){\n let $li = $('<li class=\"list-inline-item g-mr-7\"/>');\n\n if ( item.url === location.pathname )\n $li.removeClass('g-mr-7').append(`<span>${item.name}</span>`);\n else\n $li.append(`<a class=\"u-link-v5 g-color-main\" href=\"${item.url || '/'}\">${!item.url ? 'Home' : item.name}</a>`);\n\n if ( idx < nodePath.length - 1 ) $li.append('<i class=\"fa fa-angle-right g-ml-7\"></i>');\n else $li.addClass('g-color-primary');\n\n component.$ul.append($li);\n });\n };\n\n // ██████╗██╗ ██╗███████╗████████╗ ██████╗ ███╗ ███╗ ███████╗██╗ ██╗███╗ ██╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗███████╗\n // ██╔════╝██║ ██║██╔════╝╚══██╔══╝██╔═══██╗████╗ ████║ ██╔════╝██║ ██║████╗ ██║██╔════╝╚══██╔══╝██║██╔═══██╗████╗ ██║██╔════╝\n // ██║ ██║ ██║███████╗ ██║ ██║ ██║██╔████╔██║ █████╗ ██║ ██║██╔██╗ ██║██║ ██║ ██║██║ ██║██╔██╗ ██║███████╗\n // ██║ ██║ ██║╚════██║ ██║ ██║ ██║██║╚██╔╝██║ ██╔══╝ ██║ ██║██║╚██╗██║██║ ██║ ██║██║ ██║██║╚██╗██║╚════██║\n // ╚██████╗╚██████╔╝███████║ ██║ ╚██████╔╝██║ ╚═╝ ██║ ██║ ╚██████╔╝██║ ╚████║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║███████║\n // ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝\n // ███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████╗\n // ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝\n function serializeTree(tree, parent, store = []){\n for ( var i = 0, ilen = tree.length; i < ilen; i++ )\n {\n let node = $.extend(true, {}, tree[i]);\n if ( parent ) node.parent = parent.id;\n if ( node.children && node.children.length > 0 ) serializeTree(node.children, node, store);\n delete node.children;\n store.push(node);\n }\n return store;\n };\n\n function getPath(serializedTree, node, path = []){\n if ( node.parent ) getPath(serializedTree, serializedTree.find(function(item){ return item.id === node.parent; }), path);\n path.push(node);\n return path;\n }\n\n function getPathName(serializedTree, node, path = ''){\n let nodePath = getPath(serializedTree, node);\n return nodePath.map(function(item){\n if ( nodePath.indexOf(item) !== 0 ) return ` > ${item.name}`;\n return item.name;\n });\n };\n\n function findByUrl(tree, url){\n return findByField(tree, 'url', url);\n };\n\n function findById(tree, id){\n return findByField(tree, 'id', id);\n };\n\n function findByField(tree, field, value){\n for ( let i = 0, ilen = tree.length; i < ilen; i++ )\n if ( tree[i][field] === value ) return tree[i];\n\n for ( let i = 0, ilen = tree.length; i < ilen; i++ )\n {\n if ( tree[i].children && tree[i].children.length > 0 )\n {\n let node = findByField(tree[i].children, field, value);\n if ( node ) return node;\n }\n }\n\n return null;\n };\n });\n</script>\n\n<section class=\"g-bg-gray-light-v5 g-py-50 basics-header-breadcrumb-v1\" data-jc=\"basics-header-breadcrumb-v1\">\n <div class=\"container g-bg-cover__inner\">\n <header class=\"g-mb-20\">\n <h3 class=\"h5 g-font-weight-300 g-mb-5 CMS_edit\">편집</h3>\n <h2 class=\"h1 g-font-weight-300 text-uppercase CMS_edit\">편집</h2>\n </header>\n <ul class=\"u-list-inline\"></ul>\n </div>\n</section>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/header/breadcrumb","dateupdated":"2019-06-28T06:06:46.019Z"}
  6. {"id":"19062510010006ohf0","name":"basics-layout-container-v1","reference":"basics/layout/container/v1.html","body":"<style>\n #CMS .layout-container-v1 {\n padding: 1rem;\n min-height: 2rem;\n }\n</style>\n\n<div class=\"layout-container-v1 container CMS_attribute CMS_widgets\"></div>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/layout/container","dateupdated":"2019-06-28T06:06:46.019Z"}
  7. {"id":"19062510010007ohf1","name":"basics-layout-rowcol-v1","reference":"basics/layout/rowcol/v1.html","body":"<style>\n #CMS .row {\n border-top: 6px solid #E0E0E0;\n cursor: crosshair;\n padding: .25rem 1rem;\n margin: 0;\n }\n\n #CMS .row,\n #CMS .row .basics-layout-rowcol-v1 {\n padding: 1rem;\n min-height: 2rem;\n }\n</style>\n\n<script editor>\n option('number-of-columns', 'Number of Columns', 1);\n option('breakpoint', 'Breakpoint', 'md', [\n { text: 'None', value: '' },\n { text: 'Extra Small', value: 'xs' },\n { text: 'Small', value: 'sm' },\n { text: 'Middle', value: 'md' },\n { text: 'Large', value: 'lg' },\n { text: 'Extra Large', value: 'xl' },\n ]);\n option('no-gutters', 'No Gutters', false);\n\n exports.configure = function(options, $el, prev){\n let $component = $el.find('.basics-layout-rowcol-v1');\n let $columns = $component.find('.basics-layout-rowcol-v1-col');\n\n $component.toggleClass('no-gutters', options['no-gutters']);\n\n let numberOfColumns = options['number-of-columns'];\n let eachColumnsSize = 12 / numberOfColumns;\n let columnBreakpointPrefix = options['breakpoint'] ? `${options['breakpoint']}-` : '';\n\n for ( let i = 0, ilen = numberOfColumns; i < ilen; i++ )\n {\n let $column = $columns.eq(i);\n if ( $column.length <= 0 )\n {\n $component.append(`<div class=\"basics-layout-rowcol-v1-col col-${columnBreakpointPrefix}${eachColumnsSize} CMS_widgets CMS_attribute\"/>`);\n }\n else\n {\n $column.removeAttr('class')\n .addClass(`basics-layout-rowcol-v1-col col-${columnBreakpointPrefix}${eachColumnsSize} CMS_widgets CMS_attribute`);\n }\n }\n\n for ( let i = $columns.length, ilen = numberOfColumns; i > ilen; i-- )\n {\n let $column = $columns.eq(i - 1);\n $column.children().appendTo($columns.eq(ilen - 1));\n $column.remove();\n }\n };\n</script>\n\n<div class=\"row basics-layout-rowcol-v1 CMS_attribute\">\n <div class=\"basics-layout-rowcol-v1-col col-12 CMS_widgets CMS_attribute\"></div>\n</div>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/layout/rowcol","dateupdated":"2019-06-28T06:06:46.019Z"}
  8. {"id":"19062510010008ohf0","name":"basics-main-carousel-v1","reference":"basics/main/carousel/v1.html","body":"<style>\n #CMS .basics-main-carousel-v1 { min-height: 10rem; }\n</style>\n\n<script>\n COMPONENT('basics-main-carousel-v1', function(component){\n component.$el = component.element;\n\n component.make = function(){\n $.HSCore.components.HSCarousel.init(component.$el);\n };\n }, [\n '/libs/slick-carousel/1.9.0/slick.min.css',\n '/libs/slick-carousel/1.9.0/slick.min.js',\n\n '/libs/ui/1.0.0/js/components/hs.carousel.js',\n ]);\n</script>\n\n<section class=\"js-carousel g-overflow-hidden g-max-height-100vh basics-main-carousel-v1 CMS_widgets\" data-cms-importable=\"basics-main-carousel-piece-v1\" data-autoplay=\"true\" data-infinite=\"true\" data-speed=\"15000\" data-pagi-classes=\"u-carousel-indicators-v29 container text-center text-uppercase g-absolute-centered--x g-bottom-50 g-line-height-1_2 g-font-size-12 g-color-white g-brd-white-opacity-0_2\" data-jc=\"basics-main-carousel-v1\"></section>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/main/carousel","dateupdated":"2019-06-28T06:06:46.019Z"}
  9. {"id":"19062510010009ohf1","name":"basics-main-carousel-v2","reference":"basics/main/carousel/v2.html","body":"<style>\n #CMS .basics-main-carousel-v2 { min-height: 10rem; }\n</style>\n\n<script editor>\n option('bgImage', 'Background Image', '', 'file');\n\n exports.configure = function(options, $el, prevOptions){\n $('.divimage', $el).css('background-image', `url(${options.bgImage})`);\n };\n</script>\n\n<script>\n COMPONENT('basics-main-carousel-v2', function(component, config){\n component.$el = component.element;\n component.$carousel = $('.js-carousel', component.$el);\n\n component.make = function(){\n $.HSCore.components.HSCarousel.init(component.$carousel);\n };\n }, [\n '/libs/dzsparallaxer/2.6.3/dzsparallaxer.css',\n '/libs/dzsparallaxer/2.6.3/dzsscroller/scroller.css',\n '/libs/dzsparallaxer/2.6.3/advancedscroller/plugin.css',\n\n '/libs/dzsparallaxer/2.6.3/dzsparallaxer.js',\n '/libs/dzsparallaxer/2.6.3/dzsscroller/scroller.js',\n '/libs/dzsparallaxer/2.6.3/advancedscroller/plugin.js',\n ]);\n</script>\n\n<div class=\"dzsparallaxer auto-init height-is-based-on-content use-loading g-bg-cover g-bg-black-opacity-0_7--after basics-main-carousel-v2\" data-jc=\"basics-main-carousel-v2\">\n <div class=\"divimage dzsparallaxer--target w-100\" style=\"height: 140%; background-image: url(//via.placeholder.com/1920x1280/eeeeee);\"></div>\n <div class=\"container g-bg-cover__inner g-py-120\">\n <div class=\"js-carousel g-pb-80 CMS_widgets\" data-cms-importable=\"basics-main-carousel-piece-v2\" data-infinite=\"true\" data-arrows-classes=\"u-arrow-v1 g-width-40 g-height-40 g-brd-1 g-brd-style-solid g-brd-white-opacity-0_6 g-brd-primary--hover g-color-white-opacity-0_5 g-bg-primary--hover g-color-white--hover g-absolute-centered--x g-bottom-0\" data-arrow-left-classes=\"fa fa-angle-left g-ml-minus-25\" data-arrow-right-classes=\"fa fa-angle-right g-ml-25\">\n </div>\n </div>\n</div>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/main/carousel","dateupdated":"2019-06-28T06:06:46.019Z"}
  10. {"id":"19062510010010ohf0","name":"basics-main-hero-v1","reference":"basics/main/hero/v1.html","body":"<script editor>\n option('bgImage', 'Background Image', '', 'file');\n\n exports.configure = async function(options, $el, prevOptions){\n $('.g-bg-img-hero', $el).css('background-image', `url(${options.bgImage})`)\n };\n</script>\n\n<section class=\"g-bg-img-hero basics-main-hero-v1\" data-jc=\"basics-main-hero-v1\">\n <div class=\"container g-py-100 CMS_attribute CMS_widgets\"></div>\n</section>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/main/hero","dateupdated":"2019-06-28T06:06:46.019Z"}
  11. {"id":"19062510010011ohf1","name":"basics-main-topic-v1","reference":"basics/main/topic/v1.html","body":"<style></style>\n\n<section class=\"g-brd-top g-brd-bottom g-brd-gray-light-v4 basics-main-topic-v1\">\n <div class=\"container text-center g-py-50--md g-py-20\">\n <h2 class=\"h2 text-uppercase g-font-weight-300 CMS_edit\">이곳을 편집하세요.</h2>\n <p class=\"lead g-px-100--md g-mb-20 CMS_edit CMS_multiline\">\n 이곳을 편집하세요.<br/>\n 이곳을 <strong class=\"g-color-primary\">편집</strong>하세요.\n </p>\n </div>\n</section>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/main/topic","dateupdated":"2019-06-28T06:06:46.019Z"}
  12. {"id":"19062510010012ohf0","name":"basics-main-carousel-piece-v1","reference":"basics/main/carousel/piece/v1.html","body":"<style>\n #CMS .basics-main-carousel-piece-v1 { visibility: visible; height: auto; margin-bottom: .5rem; }\n #CMS .basics-main-carousel-piece-v1 .js-slide { visibility: visible; }\n #CMS .basics-main-carousel-piece-v1 .slide-image { max-height: 500px; }\n #CMS .CMS_selected > .basics-main-carousel-piece-v1 { border: .5rem solid #1A88E0; }\n</style>\n\n<script editor>\n option('slideImage', 'Slide Image', '', 'file');\n\n exports.configure = async function(options, $el, prevOptions){\n $('.slide-image', $el).css('background-image', `url(${options.slideImage})`);\n };\n</script>\n\n<div class=\"js-slide basics-main-carousel-piece-v1\" data-jc=\"basics-main-carousel-piece-v1\" data-cms-need-parent>\n <div class=\"g-flex-centered g-height-100vh g-min-height-500--md g-bg-cover g-bg-pos-center g-bg-img-hero g-bg-black-opacity-0_5--after slide-image\" style=\"background-image: url(//via.placeholder.com/1920x1280/eeeeee);\">\n <div class=\"container text-center g-z-index-1\">\n <h2 class=\"text-uppercase g-font-weight-700 g-font-size-22 g-font-size-36--md g-color-white g-mb-20 CMS_edit\">여기를\n <span class=\"g-color-primary\">편집 </span> 해주세요.\n </h2>\n <p class=\"g-hidden-xs-down g-max-width-645 g-color-white-opacity-0_9 mx-auto g-mb-35 CMS_edit\">\n 여기를 편집해주세요.\n </p>\n <a class=\"btn btn-lg u-btn-primary g-font-weight-700 g-font-size-12 text-uppercase g-rounded-50 g-px-40 g-py-15 CMS_edit\" href=\"#!\">더 알아보기</a>\n </div>\n </div>\n</div>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/main/carousel/piece","dateupdated":"2019-06-28T06:06:46.019Z"}
  13. {"id":"19062510010013ohf1","name":"basics-main-carousel-piece-v2","reference":"basics/main/carousel/piece/v2.html","body":"<style>\n #CMS .basics-main-carousel-piece-v2 { visibility: visible; height: auto; margin-bottom: .5rem; }\n #CMS .basics-main-carousel-piece-v2 .js-slide { visibility: visible; }\n #CMS .basics-main-carousel-piece-v2 .slide-image { max-height: 500px; }\n #CMS .CMS_selected > .basics-main-carousel-piece-v2 { border: .5rem solid #1A88E0; }\n</style>\n\n<div class=\"js-slide basics-main-carousel-piece-v2\" data-jc=\"basics-main-carousel-piece-v2\">\n <div class=\"text-center g-px-100--lg\">\n <i class=\"d-block g-color-primary g-font-size-60 g-line-height-0_7 g-pos-rel g-top-20\">“</i>\n <blockquote class=\"g-color-white g-font-size-25 g-py-40 CMS_edit\">여기를 편집해주세요.</blockquote>\n <h4 class=\"h6 g-color-white-opacity-0_7 text-uppercase g-mb-0 CMS_edit\">\n 여기를 <em class=\"g-font-style-normal g-color-primary\">편집해주세요.</em>\n </h4>\n </div>\n</div>","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/main/carousel/piece","dateupdated":"2019-06-28T06:06:46.019Z"}
  14. {"id":"19062510120001wee1","name":"basics-main-info-v3","reference":"basics/main/info/v3.html","body":"<style></style>\n\n<script editor>\n option('leftImage', 'Left Image', '', 'file');\n\n exports.configure = async function(options, $el, prevOptions){\n $('.left-image', $el).css('background-image', `url(${options.leftImage})`)\n .attr('data-bg-img-src', options.leftImage)\n .data('bg-img-src', options.leftImage);\n };\n</script>\n\n<section class=\"basics-main-info-v3\" data-jc=\"basics-main-info-v3\">\n <!-- Info Blocks -->\n <div class=\"row no-gutters\">\n <!-- Info Image -->\n <div class=\"col-lg-5 g-min-height-360 g-bg-size-cover g-bg-pos-center left-image\" data-bg-img-src=\"//via.placeholder.com/1920x1280\" style=\"background-image: url(//via.placeholder.com/1920x1280);\"></div>\n <!-- End Info Image -->\n <div class=\"col-lg-7 g-bg-gray-dark-v1 g-pt-100 g-pb-80 g-px-80\">\n <header class=\"text-uppercase g-mb-35\">\n <div class=\"g-mb-30\">\n <span class=\"d-block g-color-primary g-font-weight-700 g-font-size-default g-mb-15 CMS_edit\">이곳을 편집해주세요.</span>\n <h2 class=\"h2 g-color-white g-font-weight-700 mb-0 CMS_edit\">이곳을 편집해주세요.</h2>\n </div>\n <div class=\"g-width-70 g-brd-bottom g-brd-2 g-brd-primary\"></div>\n </header>\n <p class=\"g-color-white-opacity-0_7 g-mb-20 CMS_edit CMS_multiline\">이곳을 편집해주세요.</p>\n <p class=\"g-color-white-opacity-0_7 g-mb-45 CMS_edit CMS_multiline\">이곳을 편집해주세요.</p>\n\n <div class=\"row align-items-stretch CMS_repeat\">\n <div class=\"col-sm-6 g-mb-30 CMS_remove\">\n <!-- Article -->\n <article class=\"h-100 g-flex-middle g-brd-left g-brd-3 g-brd-primary g-brd-white--hover g-bg-black-opacity-0_8 g-transition-0_3 g-pa-20\">\n <div class=\"g-flex-middle-item\">\n <h4 class=\"h6 g-color-white g-font-weight-600 text-uppercase g-mb-10 CMS_edit\">이곳을 편집해주세요.</h4>\n <p class=\"g-color-white-opacity-0_7 mb-0 CMS_edit\">이곳을 편집해주세요.</p>\n </div>\n </article>\n <!-- End Article -->\n </div>\n <div class=\"col-sm-6 g-mb-30 CMS_remove\">\n <!-- Article -->\n <article class=\"h-100 g-flex-middle g-brd-left g-brd-3 g-brd-primary g-brd-white--hover g-bg-black-opacity-0_8 g-transition-0_3 g-pa-20\">\n <div class=\"g-flex-middle-item\">\n <h4 class=\"h6 g-color-white g-font-weight-600 text-uppercase g-mb-10 CMS_edit\">이곳을 편집해주세요.</h4>\n <p class=\"g-color-white-opacity-0_7 mb-0 CMS_edit\">이곳을 편집해주세요.</p>\n </div>\n </article>\n <!-- End Article -->\n </div>\n </div>\n\n </div>\n <!-- End Info Blocks -->\n</section>","datecreated":"2019-06-25T01:12:28.882Z","picture":"","icon":"","category":"basics/main/info","dateupdated":"2019-06-28T06:06:46.019Z"}
  15. {"id":"19062510290002irk0","name":"extensions-test-v1","reference":"extensions/test/v1.html","body":"<style>\n @media (min-width: 767.98px) {\n .extensions-text-v1 .test-paper {\n display: flex;\n }\n .extensions-text-v1 .test-paper.two-col {\n flex-direction: column;\n flex-wrap: wrap;\n align-content: flex-start;\n height: 60rem;\n }\n }\n .extensions-text-v1 .test-paper article.question-answer {\n padding: 0 1rem;\n padding-bottom: 3rem;\n }\n\n .extensions-text-v1 .test-paper article.question-answer ul.answer.multiple-choice {\n list-style-type: none;\n }\n</style>\n<script>\n COMPONENT('extensions-text-v1', 'paperCol:2', function(component, config){\n component.$el = component.element;\n\n let dummy = [\n 'break-page',\n {\n _id: '5d0908a2f8c31c0000aff135',\n questionContent: `\n<article class=\"question-answer\">\n <p class=\"question-title font-weight-bold\">1. 다음 글을 읽고 물음에 답하시오.</p>\n <div class=\"question-content mb-3\">\n <div class=\"question-quotation border border-dark p-2\">\n <p>어니스트 베델은 영국의 기자였다. 그는 1904년 러일 정쟁을 취재하기 위해 우리나라에 왔다. 그 당시 우리 국민들은 일본의 침략으로 하루하루 어렵게 살고 있었다. 일본은 우리 국민들을 철저히 감시하였기 때문에 우리나라 사람들이 발행하는 시문에는 일본의 칩략 행위에 대한 기사를 실을 수 없엇다. 그래서 일본의 만행이 제대로 알려지지 않았다. 이러한 실상을 알게 된 베델은 기자로서 깊은 고민에 빠졌다. 당시 영국은 일본과 동맹국이어서 일본에 우호적인 기사를 써야 했기 때문에 그는 더욱 괴로웠다.</p>\n <p>'아! 일본 때문에 한국인들이 고통을 받고 있구나. 일본이 한국을 위해 을사조약을 맺었다는 것은 사실이 아니야. 또 한국인들이 을사조약을 환영한다는 것도 거짓이었어. 그런데도 이런 사실을 감추고 거짓 기사를 쓸 수는 없어. 기자는 사실만을 써야 해. 이 진실을 세상 사람들에게 알릴 수 있는 방법이 없을까?'</p>\n <div class=\"d-flex mb-3\">\n <span class=\"my-auto px-3\">(가)</span>\n <div class=\"w-100 mx-0 border border-dark p-2\" style=\"height: 2.5rem\"></div>\n </div>\n <p>베델은 자신이 만든 대한매일신보에 우리나라에 대한 일본의 침략행위를 사실대로 실었다. 을사조약 문서에 우리나라의 국새가 찍혀있지 않다는 것과 일본이 우리나라의 문화재를 몰래 빼돌렸다는 사실을 기사로 썼다. 또 이완용의 행동을 비판하는 신문 사설을 처음으로 싣기도 하였다.</p>\n <p>베델이 신문을 통해 일본의 침략 행위를 낱낱이 ㉠ <u>밝히자</u> 일본은 베델을 압박하고 베델의 일을 방해하기 시작하였다. 일본은 영국 정부에 베델을 추방하는 데 협력해 줄 것을 요청하기도 하고, 그의 처벌을 요구하는 소송장을 내기도 하였다. 베델은 세 차례의 재판을 거치면서 건강이 악화되어 결국 서른일곱의 나이에 우리나라에서 눈을 감았다.</p>\n <p>\"나는 죽되 대한매일신보는 길이 살아 한국 동포를 구하기를 원하노라.\"</p>\n <p>마지막 순간까지도 언론인의 신념을 지키고자 했던 어니스트 베델. 언론인으로서의 참모습을 실현하고자 했던 그의 정신은 우리 마음속에 영원히 남아 있을 것이다.</p>\n </div>\n </div>\n <p>밑줄 친 말이 위 글의 ㉠과 같은 뜻으로 쓰인 것은 어느 것입니까?</p>\n</article>`,\n type: 'multiple-choice',\n 'multiple-choice': {\n isMultipleSelection: false,\n options: [\n {\n content: '전등을 켜서 방을 <u>밝혔다.</u>'\n },\n {\n content: '그 친구는 먹을 것을 너무 <u>밝혔다.</u>'\n },\n {\n content: '책을 읽다가 뜬눈으로 밤을 <u>밝혔다.</u>'\n },\n {\n content: '어둠 속에서 길을 찾기 위해 횃불을 <u>밝혔다.</u>'\n },\n {\n content: '탐정은 방송을 통하여 사건의 비밀을 <u>밝혔다.</u>'\n },\n ]\n }\n },\n {\n _id: '5d090a2f9e35540000d47fed',\n questionContent: `\n<article class=\"question-answer\">\n <p class=\"question-title font-weight-bold\">2. 다음 글을 읽고 물음에 답하시오.</p>\n <div class=\"question-content mb-3\">\n <div class=\"question-quotation border border-dark p-2\">\n <p>옛날 아이들은 윷놀이, 자치기, 팽이치기와 같은 전래 놀이를 하면서 친구들과 즐겁게 놀았다. <span class=\"border border-dark px-2\">그러나</span> 요즘 아이들은 주로 인터넷 게임을 하거나 텔레비전을 시청하며 여가를 보낸다. 전래 놀이는 여럿이 함께하는 놀이가 많아 친구를 사귈 기회를 더 많이 갖게 하고 ㉠ <u>건강한 몸을 유지하는 데 도움을 준다.</u> 전래 놀이를 하면 다음과 같은 좋은 점이 있다.</p>\n <p>(가) 첫째, 자연을 가까이 느낄 수 있다. 대부분의 전래 놀이는 바깥에서 한다. 그래서 풀, 바람, 나무, 흙, 돌, 햇빛 등 자연을 가까이 느낄 수 있다.</p>\n <p>(나) 둘째, 공동체 의식을 기를 수 있다. 또래 아이들과 함께 윷놀이나 비사치기를 하면서 의논하고 힘을 모으기도 한다. 이러한 과정을 거치면서 아이들은 서로 돕고 위하는 태도를 배우게 된다.</p>\n <p>((다)) 셋째, 체력이 향상될 수 있다. 전래 놀이는 뛰거나 빠른 걸음으로 걷는 등 움직임이 많아서 따로 운동을 하지 않아도 운동 효과를 볼 수 있기 때문이다.</p>\n <p>((라)) 넷째, 놀이를 통해 지식을 얻을 수 있다. 자치기에 필요한 막대기를 고르면서 나무의 특성을 자연스럽게 알고, 연날리기를 하면서 바람을 이용하는 방법을 터득하게 된다.</p>\n <p>((마)) 다섯째, 조상들의 지혜를 알 수 있다. 우리 조상들은 언제 어디서나 누구든지 즐길 수 있는 다양한 놀이 방법을 만들어 냈다. 아이들은 전래 놀이를 하면서 지혜를 터득하게 된다.</p>\n <p>이와 같이 전래 놀이를 하면 좋은 점이 많다. 우리 모두 전래 놀이에 관심을 가지고 즐겨 하자.</p>\n </div>\n </div>\n <p>밑줄 친 ㉠을 뒷받침하는 근거로 가장 적절한 것은 어느 것입니까?</p>\n</article>`,\n type: 'multiple-choice',\n 'multiple-choice': {\n isMultipleSelection: false,\n options: [\n {\n content: '(가)'\n },\n {\n content: '(나)'\n },\n {\n content: '(다)'\n },\n {\n content: '(라)'\n },\n {\n content: '(마)'\n },\n ]\n }\n },\n 'break-page',\n {\n _id: '5d09111a8fcc6500006d78d0',\n questionContent: `\n<article class=\"question-answer\">\n <p class=\"question-title font-weight-bold\">3. 다음 글을 읽고 물음에 답하시오.</p>\n <div class=\"question-content mb-3\">\n <div class=\"question-quotation border border-dark p-2\">\n <p>흙 속의 미생물에서 감기약 성분을 얻는다면 믿을 수 있을까? 놀랍게도 과학자들은 흙 속의 미생물인 방선균에서 그 성분을 얻고 있다. 방선균은 실처럼 생긴 가지가 서로 연결된 형태를 띤 세균의 한 종류이다. 방선균은 흙, 식물, 동물의 몸, 하천, 바닷물 등에 사는데 그 중에서도 흙 속에 가장 많이 산다.</p>\n <p>방선균은 우리 생활에 많은 도움을 준다. 먼저 방선균은 식물이 사는 데 꼭 필요한 질소를 공급해 준다. 그래서 농사에 도움이 된다. 또한 방선균은 유기물을 분해하기 때문에 퇴비를 만드는 데에 쓰인다. 화장실, 정화조 등의 악취를 없애고 가정의 하수 등을 정화하는 데에도 이용된다. 무엇보다도 방선균의 가장 큰 특징은 곰팡이나 병원균<sup>*</sup>을 파괴하는 항생 물질을 만들어 내는 것이다.</p>\n <div class=\"d-flex mb-2\">\n <span class=\"my-auto px-3\">(가)</span>\n <div class=\"w-100 mx-0 border border-dark p-2\">\n <p>방선균이 만들어 내는 항생 물질은 의약품을 만드는 데 널리 이용된다. 우리가 사용하는 의약품 중 약 70%가 방선균이 만들어 낸 항생 물질을 원료로 한다. 감기약이나 안약, 피부 질환에 바르는 연고에서부터 암이나 결핵을 치료하는 약에 이르기까지 방선균의 쓰임은 다양하다.</p>\n <p>과학자들은 계속해서 방선균 연구에 힘쓰고 있다. 최근에는 흙 속에 있는 방선균뿐 아니라 바다에 있는 방선균에 대한 연구가 새롭게 진행되고 있다. 새로운 방선균의 발견과 그것의 활용 방안에 대한 연구는 방선균의 활용 가치를 높이는 데 기여할 것이다.</p>\n </div>\n </div>\n <p>* 병원균: 병의 원인이 되는 균.</p>\n </div>\n </div>\n <p>위 글의 내용과 일치하지 않는 것은 어느 것입니까?</p>\n</article>`,\n type: 'multiple-choice',\n 'multiple-choice': {\n isMultipleSelection: false,\n options: [\n {\n content: '방선균은 물속에서 가장 많이 발견된다.'\n },\n {\n content: '방선균은 식물이 살아가는 데 도움을 준다.'\n },\n {\n content: '방선균은 가정의 하수를 정화하는 데 활용된다.'\n },\n {\n content: '최근에 바다에 있는 방선균에 대한 연구가 이루어지고 있다.'\n },\n {\n content: '방선균이 만들어 내는 항생 물질은 의약품을 만드는 데 이용된다.'\n },\n ]\n }\n },\n {\n _id: '5d0913ce65fa31000020f2c3',\n questionContent: `\n<article class=\"question-answer\">\n <p class=\"question-title font-weight-bold\">4. 다음 글을 읽고 물음에 답하시오.</p>\n <div class=\"question-content mb-3\">\n <div class=\"question-quotation border border-dark p-2\">\n <p>우리나라 사람에게 호랑이는 특별한 동물이다. 우리 조상들은 호랑이에게 잡귀와 나쁜 기운을 쫓아낼 수 있는 능력이 있다고 믿었다. 호랑이를 단순한 동물이 아닌 산신령이나 수호신으로 생각했던 것이다. <span class=\"border border-dark px-4\">㉠</span> 사람들은 호랑이 형상을 넣은 물건을 가까이두었다.</p>\n <p>우리 조상들이 평소 사용하던 생활 용품을 살펴보면 호랑이 형상이 들어 있는 물건이 많다. 어른들은 아이들이 나쁜 꿈을 꾸지 않고 편안하게 잠들기를 바라며 아이들의 베개에 호랑이 문양을 수놓았다. 남자들이 거처하는 사랑방에는 높은 벼슬에 오르기를 기원하며 호랑이 장식이 있는 가구를 놓았다. 부엌에서는 호랑이 다리를 본떠 만든 소반<sup>*</sup>이나 호랑이 무늬가 있는 그릇 등을 사용하였다.</p>\n <p>특별한 일이 있을 때에도 사람들은 호랑이 형상을 그리거나 본뜬 물건을 사용하였다. 혼례를 치를 때에는 신부가 안전하기를 바라는 마음에서 가마에 호랑이 문양을 그려 넣었다. 또, 한 해가 시작되는 정월이 되면 사람들은 집 안으로 들어오려는 잡귀를 막기 위해 대문에 호랑이 '호(虍)'자를 써 붙였다. 그리고 전쟁을 무사히 마치고 싶어 했던 군인들은 호랑이 그림이 있는 옷을 입고, 호랑이 수염을 꽂은 모자를 썼으며, 호랑이가 그려진 깃발을 흔들면서 행군하였다. ㉡ <u>사람들은 무덤에도 호랑이 형상을 세웠다.</u> 호랑이가 죽은 사람을 지켜 준다고 믿었기 때문이다.</p>\n\n <p>* 소반: 자그마한 밥상</p>\n </div>\n </div>\n </div>\n <p>위 글의 <span class=\"border border-dark px-4\">㉠</span>에 들어갈 알맞은 낱말은 어느 것입니까?</p>\n</article>`,\n type: 'multiple-choice',\n 'multiple-choice': {\n isMultipleSelection: false,\n options: [\n {\n content: '그러나'\n },\n {\n content: '그러면'\n },\n {\n content: '그래서'\n },\n {\n content: '하지만'\n },\n {\n content: '왜냐하면'\n },\n ]\n }\n },\n 'break-page',\n {\n _id: '5d0915ee82cc1800004f79f2',\n questionContent: `\n<article class=\"question-answer\">\n <p class=\"question-title font-weight-bold\">5. 다음 글을 읽고 물음에 답하시오.</p>\n <div class=\"question-content mb-3\">\n <div class=\"question-quotation border border-dark p-2\">\n <p>우리 생활에서 한글은 없어서는 안 될 존재이다. 한글은 1443년, 세종대왕과 집현전 학자들이 심혈을 기울여 만들어낸 노력의 ⓐ <u>결정</u>이라 할 수 있다. 자음 14자와 모음 10자로 구성되어 있고, 발음기관을 본떠서 만들어 과학적이며 배우기 쉬운 문자이다. 그런데 요즘은 우리의 소중한 한글이 많이 오염되고 훼손되고 있다는 생각이 자주 들게 된다. 그렇게 생각하는 이유는 다음과 같다.</p>\n <p>첫째, 심각한 통신언어가 사용되면서 한글이 훼손되고 있다. 예를 들어 '초등학생'을 '초딩', '어서오세요.'를 '어솨요.'라고 줄여 쓰는 통신언어들이 이제는 실생활에서까지 사용되면서, 어떤 것이 올바른 말인지 모르는 사람들이 많아진다. 통신언어는 말이 짧고 쓰기 쉬워서 경제성이 뛰어나다.</p>\n <p>둘째, 버젓이 고유의 한글이 있는데도 외국어를 사용한다. '색깔'을 '칼라'로, '악단'을 '밴드'로, 건물을 '빌딩'으로 사용하는 것은 자연스러운 일이 되었다. 많은 사람들이 외국어라는 거부감 없이 일상적으로 이런 말을 사용하고 있다. 이러다가는 미래에는 우리 고유의 한글이 기억 속에서 점점 잊혀갈지도 모른다.</p>\n </div>\n </div>\n </div>\n <p>위 글의 제목으로 알맞은 것은 어느 것입니까?</p>\n</article>`,\n type: 'multiple-choice',\n 'multiple-choice': {\n isMultipleSelection: false,\n options: [\n {\n content: '한글의 우수성'\n },\n {\n content: '통신언어의 특징'\n },\n {\n content: '한글 창제 원리'\n },\n {\n content: '한글의 오염과 훼손'\n },\n {\n content: '한글과 외국어의 차이점'\n },\n ]\n }\n },\n {\n _id: '5d0916b982cc1800004f79f3',\n questionContent: `\n<article class=\"question-answer\">\n <p class=\"question-title font-weight-bold\">6. 다음 글을 읽고 물음에 답하시오.</p>\n <div class=\"question-content mb-3\">\n <div class=\"question-quotation border border-dark p-2\">\n <p>(가) 우리는 가끔 하고 싶은 말을 다 하고 산다면 얼마나 속이 시원할까 하고 생각할 때가 있다. 하지만, 우리가 하고 싶은 말이 모두 옳고 고운 말일까? 우리가 하고자 하는 말 중에는 자기중심적인 말이 많다. 자기중심적인 말은 경우에 따라 상대방의 마음에 상처를 줄 수도 있다.</p>\n <p>(나) 우리는 때때로 보고도 보지 않은 것처럼 해야 할 때가 있다. 이것은 ㉠ <u>불의</u>를 보고 무조건 모르는 척하라는 것이 아니다. 그런 상황이 일어나게 된 까닭을 이해해 보라는 것이다. 만약, 친구가 무엇인가를 훔치는 것을 보게 된다면, 어떻게 할 것인가? 도둑이라고 막 떠벌릴 것인가, 아니면 친구의 사정을 고려하여 떠벌리지 말 것인가를 한번쯤 생각해 보라는 것이다.</p>\n <p>(다) 또, 듣고도 듣지 않은 것처럼 해야 할 때가 있다. 이것은 귀로 흘러들어온 말을 함부로 입 밖으로 내서는 안 도니다는 것이다. 직접 확인해 보지 않은 사실을 다른 사람에게 전해서 그 말이 눈덩이 처럼 커진다면, 그말에 책임을 질 수 있을까? 말을 전할 때에는 들은 말이 사실인지를 확인해야 한다.</p>\n <p>(라) 우리의 얼굴엔 눈과 귀가 두 개씩 있지만, 입은 하나밖에 없다. 이것은 많이 보고 많이 듣되, 말은 조금만 하라는 뜻이다. 생각 없이 이말저말을 함부로 하는 사람은 좋은 친구를 사귈 수 없을 것이며, 좋은 친구가 될 수도 없을 것이다. 왜냐하면, 그 사람의 입을 믿을 수가 없기 떄문이다.</p>\n </div>\n </div>\n <p>위 글의 주제로 가장 적절한 것은 어느 것입니까?</p>\n</article>`,\n type: 'multiple-choice',\n 'multiple-choice': {\n isMultipleSelection: false,\n options: [\n {\n content: '고운 말만 해야 한다.'\n },\n {\n content: '말은 신중하게 해야 한다.'\n },\n {\n content: '좋은 친구를 사귀어야 한다.'\n },\n {\n content: '남에게 들은 것은 전하지 말아야한다.'\n },\n {\n content: '다른 사람의 잘못을 지적해 주어야 한다.'\n },\n ]\n }\n },\n ];\n\n component.make = function(){\n let $testPaper;\n\n dummy.forEach(function(questionAnswer, idx){\n if ( questionAnswer === 'break-page' )\n return $testPaper = $(`<div class=\"test-paper ${config.paperCol === 2 ? 'two-col' : '' } ${ idx > 0 ? 'pt-3 border-top' : '' }\"></div>`).appendTo(component.$el);\n\n let $questionAnswer = $(questionAnswer.questionContent);\n\n $questionAnswer.addClass(config.paperCol === 2 ? 'g-width-50x--md' : '');\n\n if ( questionAnswer.type === 'multiple-choice' )\n {\n let $optionList = $('<ul class=\"answer multiple-choice pl-3\"/>');\n questionAnswer['multiple-choice'].options.forEach(function(option, idx){\n $optionList.append(`\n<li><div class=\"form-check form-check-inline\">\n <input class=\"form-check-input\" type=\"radio\" name=\"${questionAnswer._id}\" id=\"${questionAnswer._id}-${idx}\" value=\"option1\">\n <label class=\"form-check-label\" for=\"${questionAnswer._id}-${idx}\">${option.content}</label>\n</div></li>`\n );\n });\n\n $questionAnswer.append($optionList);\n }\n\n $testPaper.append($questionAnswer);\n });\n\n $(window).on('resize', _.debounce(component._resizeTestPaper, 150)).trigger('resize');\n };\n\n component._resizeTestPaper = function(){\n if( window.matchMedia( '(max-width: 768px)' ).matches )\n return $('.test-paper', component.$el).height('auto');\n\n $('.test-paper', component.$el).each(function(){\n let $testPaper = $('.test-paper', component.$el);\n let maxHeight = 0;\n\n $('article.question-answer', $testPaper).each(function(){\n if ( $(this).height() > maxHeight ) maxHeight = $(this).height();\n });\n\n $testPaper.height(maxHeight + 20);\n });\n };\n });\n</script>\n<div data-jc=\"extensions-text-v1\" class=\"extensions-text-v1\"></div>","datecreated":"2019-06-25T01:29:18.956Z","picture":"","icon":"","category":"extensions/test","dateupdated":"2019-06-28T06:06:46.019Z"}
  16. {"id":"19062510010014ohf0","name":"basics-header-nav-megamenu-v1","reference":"basics/header/nav/megamenu/v1.html","body":"<style></style>\n\n<script editor>\n option('showTopbar', '상단 바 보이기', true);\n option('navTextColor', '네비게이션 글자색', '#ffffff', 'Color');\n option('navFontWeight', '네비게이션 글자 굵기', 600);\n\n exports.configure = async function(options, $el, prevOptions){\n let $component = $('.basics-header-nav-megamenu-v1', $el);\n\n $component.attr({ 'data-nav-text-color': options.navTextColor });\n\n $('ul.navbar-nav', $component).css({ 'font-weight': options.navFontWeight });\n $('.basics-header-nav-megamenu-v1-topbar', $component).toggleClass('d-none', !options.showTopbar);\n };\n</script>\n<script>\n COMPONENT('basics-header-nav-megamenu-v1', function(component, config){\n // 컴포넌트 상수 설정\n const CONSTANTS = {\n TEMPLATE: {\n // 탑 레벨 - 일반 아이템\n $NAV_LIST_ITEM: $('<li class=\"nav-item g-mx-5--lg g-mx-10--xl\"><a class=\"nav-link g-px-5 g-py-20 text-nowrap\" href=\"#!\"></a></li>'),\n // $NAV_LIST_ITEM: $('<li class=\"nav-item g-mx-2--md g-mx-5--xl g-mb-5 g-mb-0--lg\"><a href=\"#!\" class=\"nav-link\"></a></li>'),\n\n // 탑 레벨 - 서브메뉴 있는 아이템\n $HAS_SUBMENU_LIST_ITEM: $('<li class=\"nav-item hs-has-sub-menu g-mx-5--lg g-mx-10--xl\" data-animation-in=\"fadeIn\" data-animation-out=\"fadeOut\"/>'),\n // 탑 레벨 - 서브메뉴 토글러\n $SUBMENU_TOGGLER: $('<a class=\"nav-link text-uppercase g-px-5 g-py-20 text-nowrap\" href=\"#!\" aria-haspopup=\"true\" aria-expanded=\"false\"></a>'),\n // 탑 레벨 - 서브메뉴\n $SUBMENU_LIST: $('<ul class=\"hs-sub-menu list-unstyled u-shadow-v11 g-min-width-220 g-brd-top g-brd-primary g-brd-top-2 g-mt-17\">'),\n\n // 서브메뉴 - 일반 아이템\n $SUBMENU_LIST_ITEM: $('<li class=\"dropdown-item\"><a class=\"nav-link g-color-gray-dark-v4\" href=\"#!\"></a></li>'),\n\n // 서브메뉴 - 서브메뉴 있는 아이템\n $SUBMENU_HAS_SUBMENU_LIST_ITEM: $('<li class=\"dropdown-item hs-has-sub-menu\"/>'),\n // 서브메뉴 - 서브메뉴 토글러\n $SUBMENU_SUBMENU_TOGGLER: $('<a class=\"nav-link g-color-gray-dark-v4\" href=\"#!\" aria-haspopup=\"true\" aria-expanded=\"false\"></a>'),\n // 서브메뉴 - 서브메뉴 리스트\n $SUBMENU_SUBMENU_LIST: $('<ul class=\"hs-sub-menu list-unstyled u-shadow-v11 g-min-width-220 g-brd-top g-brd-primary g-brd-top-2 g-mt-minus-2\"/>'),\n // 서브메뉴 - 서브메뉴 일반 아이템\n $SUBMENU_SUBMENU_LIST_ITEM: $('<li class=\"dropdown-item\"><a class=\"nav-link g-color-gray-dark-v4\" href=\"#!\"></a></li>'),\n }\n };\n\n // 컴포넌트 객체 변수 지정\n component.$navbar = component.$el = component.element;\n\n component.$navbarToggler = $('.navbar-toggler', component.$navbar);\n\n component.$nav = $('nav.js-mega-menu', component.$navbar);\n component.$navbarCollapse = $('.navbar-collapse', component.$nav);\n component.$navbarNav = $('ul.navbar-nav', component.$navbarCollapse);\n\n component.$navContainer = $('.container', component.$nav);\n\n component.$topbar = $('.basics-header-nav-megamenu-v1-topbar', component.$el);\n component.$loginAnchor = $('.navbar-login-link', component.$el);\n\n if ( !window._TOTALCMS_MAINMENU ) component.template = '/api/v3/nav/mainmenu';\n\n component.make = function(mainmenu){\n // 공용 네비게이션 객체 초기화\n if ( window._TOTALCMS_MAINMENU ) mainmenu = window._TOTALCMS_MAINMENU;\n else if ( mainmenu ) window._TOTALCMS_MAINMENU = mainmenu;\n\n // 토글 매핑 아이디 임의화\n // 네비게이션 접기 펴기 ID 랜덤화\n let navRandomId = $.TOT.helpers.getRandomId();\n\n component.$navbarToggler\n .attr({\n 'aria-controls': navRandomId,\n 'data-target': `#${navRandomId}`,\n 'data-toggle': 'collapse',\n })\n .data('target', `#${navRandomId}`);\n\n component.$navbarCollapse.prop('id', navRandomId);\n\n\n // 네비게이션 생성\n _.each(window._TOTALCMS_MAINMENU.children, function _recursive(menuItem, index, src, $parent, depth){\n depth = depth || 0;\n menuItem.hasChild = menuItem.children && menuItem.children.length > 0;\n\n if ( menuItem.url === '/' ) return;\n\n let $cursor;\n if ( depth === 0 )\n {\n if ( menuItem.hasChild )\n {\n let $hasSubmenuListItem = CONSTANTS.TEMPLATE.$HAS_SUBMENU_LIST_ITEM.clone();\n let $toggler = CONSTANTS.TEMPLATE.$SUBMENU_TOGGLER.clone();\n let $ul = CONSTANTS.TEMPLATE.$SUBMENU_LIST.clone();\n\n // 서브메뉴 토글러 아이디 매핑\n let submenuId = $.TOT.helpers.getRandomId();\n let togglerId = $.TOT.helpers.getRandomId();\n\n $toggler\n .prop('id', togglerId)\n .attr({ 'aria-controls': submenuId })\n .css({ 'color': component.$el.data('nav-text-color') || 'inherit' })\n .text($.TOT.helpers.unescapeHtml(menuItem.name));\n $ul\n .prop('id', submenuId)\n .attr({ 'aria-labelledby': togglerId });\n\n $hasSubmenuListItem.append($toggler, $ul);\n component.$navbarNav.append($hasSubmenuListItem);\n\n $cursor = $ul;\n }\n else\n {\n let $listItem = CONSTANTS.TEMPLATE.$NAV_LIST_ITEM.clone();\n $('a.nav-link', $listItem)\n .attr('href', menuItem.url)\n .css({\n 'color': component.$el.data('nav-text-color') || 'inherit',\n })\n .text($.TOT.helpers.unescapeHtml(menuItem.name));\n component.$navbarNav.append($listItem);\n }\n }\n else if ( depth === 1 )\n {\n if ( menuItem.hasChild )\n {\n let $hasSubmenuListItem = CONSTANTS.TEMPLATE.$SUBMENU_HAS_SUBMENU_LIST_ITEM.clone();\n let $toggler = CONSTANTS.TEMPLATE.$SUBMENU_SUBMENU_TOGGLER.clone();\n let $ul = CONSTANTS.TEMPLATE.$SUBMENU_SUBMENU_LIST.clone();\n\n // 서브메뉴 토글러 아이디 매핑\n let submenuId = $.TOT.helpers.getRandomId();\n let togglerId = $.TOT.helpers.getRandomId();\n\n $toggler.prop('id', togglerId).attr({ 'aria-controls': submenuId }).text($.TOT.helpers.unescapeHtml(menuItem.name));\n $ul.prop('id', submenuId).attr({ 'aria-labelledby': togglerId });\n\n $hasSubmenuListItem.append($toggler, $ul);\n $parent.append($hasSubmenuListItem);\n\n $cursor = $ul;\n }\n else\n {\n let $listItem = CONSTANTS.TEMPLATE.$SUBMENU_LIST_ITEM.clone();\n $listItem.find('a').attr('href', menuItem.url).text($.TOT.helpers.unescapeHtml(menuItem.name));\n $parent.append($listItem);\n }\n }\n else if ( depth === 2 )\n {\n let $listItem = CONSTANTS.TEMPLATE.$SUBMENU_SUBMENU_LIST_ITEM.clone();\n $listItem.find('a').attr('href', menuItem.url).text($.TOT.helpers.unescapeHtml(menuItem.name));\n $parent.append($listItem);\n }\n\n if ( !menuItem.hasChild ) return;\n\n depth += 1;\n _.each(menuItem.children, function(menuItem, index, src){\n return _recursive(menuItem, index, src, $cursor, depth);\n });\n });\n\n // DOM 렌더링 이후 작업\n setTimeout(function(){\n // 검색바 ID 임의화\n let searchFormRandomId = $.HSCore.helpers.getRandomId();\n $('#searchform', component.$el).prop('id', searchFormRandomId);\n $('[data-dropdown-target=\"#searchform\"]', component.$el).attr({\n 'data-dropdown-target': `#${searchFormRandomId}`,\n 'aria-controls': searchFormRandomId,\n }).data('dropdown-target', `#${searchFormRandomId}`);\n\n let redirectTo = encodeURIComponent(location.href.replace(location.origin, ''));\n\n // 세션에 따른 링크 변경\n if ( _.get(TOTAL_LOCALS, '_SESSION.isLoggedIn') )\n component.$loginAnchor.text('로그아웃').attr('href', `/logout?from=${redirectTo}`);\n else\n component.$loginAnchor.attr('href', `/login?from=${redirectTo}`);\n\n component.$loginAnchor.removeClass('d-none');\n\n // 현재 URL 네비게이션 강조\n $(`a[href=\"${location.pathname}\"]`, component.$navbarNav).parents('li').addClass('active');\n\n $.HSCore.components.HSHeader.init(component.$navbar);\n $.HSCore.helpers.HSHamburgers.init($('.hamburger', component.$el));\n\n component.$nav.HSMegaMenu({\n event: 'hover',\n pageContainer: component.$navContainer,\n breakpoint: 991\n });\n\n $.HSCore.components.HSDropdown.init($('[data-dropdown-target]', component.$el), {\n afterOpen: function () {\n $(this).find('input[type=\"search\"]').focus();\n }\n });\n\n component.$nav.data('HSMegaMenu').smartPositions();\n });\n }\n });\n</script>\n\n<!-- Header -->\n<header class=\"u-header u-header--static u-shadow-v19 header-nav basics-header-nav-megamenu-v1\" data-jc=\"basics-header-nav-megamenu-v1\">\n <div class=\"u-header__section u-header__section--hidden u-header__section--dark g-bg-black-opacity-0_9 g-color-white-opacity-0_8 g-py-13 basics-header-nav-megamenu-v1-topbar\">\n <div class=\"container\">\n <div class=\"row flex-column flex-md-row align-items-center justify-content-between text-uppercase g-color-white g-font-size-11 g-mx-minus-15\">\n <div class=\"col-auto ml-auto\">\n <ul class=\"list-inline g-line-height-1 g-mt-minus-10 g-mx-minus-4 mb-0\">\n <li class=\"list-inline-item g-mx-4 g-mt-10\">\n <a href=\"/login\" class=\"navbar-login-link g-color-white g-color-primary--hover g-text-underline--none--hover d-none\">로그인</a>\n </li>\n\n <!--li class=\"list-inline-item g-mx-4 g-mt-10\">|</li>\n <li class=\"list-inline-item g-mx-4 g-mt-10\">\n <a href=\"/joinus\" class=\"g-color-white g-color-primary--hover g-text-underline--none--hover\">회원가입</a>\n </li-->\n\n <li class=\"list-inline-item g-mx-4 g-mt-10\">\n <!-- Search -->\n <div class=\"g-pos-rel\">\n <a href=\"#!\" class=\"g-font-size-14 g-color-white g-color-primary--hover g-ml-5\" aria-haspopup=\"true\" aria-expanded=\"false\" aria-controls=\"searchform\" data-dropdown-target=\"#searchform\" data-dropdown-type=\"css-animation\" data-dropdown-duration=\"300\" data-dropdown-animation-in=\"fadeInUp\" data-dropdown-animation-out=\"fadeOutDown\">\n <i class=\"fas fa-search\"></i>\n </a>\n\n <!-- Search Form -->\n <form id=\"searchform\" class=\"u-searchform-v1 g-bg-black g-box-shadow-none g-pa-10 g-mt-10 u-dropdown--css-animation u-dropdown--hidden\" style=\"animation-duration: 300ms;\">\n <div class=\"input-group g-brd-primary--focus\">\n <input class=\"form-control rounded-0 u-form-control g-brd-white\" type=\"search\" placeholder=\"Enter Your Search Here...\">\n <div class=\"input-group-addon p-0\">\n <button class=\"btn rounded-0 btn-primary btn-md g-font-size-14 g-px-18\" type=\"submit\">Go</button>\n </div>\n </div>\n </form>\n <!-- End Search Form -->\n </div>\n <!-- End Search -->\n </li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"u-header__section u-header__section--light g-bg-white g-transition-0_3 g-py-10 CMS_attribute\">\n <nav class=\"js-mega-menu navbar navbar-expand-lg\">\n <div class=\"container\">\n <!-- Responsive Toggle Button -->\n <button class=\"navbar-toggler navbar-toggler-right btn g-line-height-1 g-brd-none g-pa-0 g-pos-abs g-top-3 g-right-0\" type=\"button\"\n aria-label=\"Toggle navigation\"\n aria-expanded=\"false\"\n aria-controls=\"navBar\"\n data-toggle=\"collapse\"\n data-target=\"#navBar\">\n <span class=\"hamburger hamburger--slider g-pr-0\">\n <span class=\"hamburger-box\">\n <span class=\"hamburger-inner\"></span>\n </span>\n </span>\n </button>\n <!-- End Responsive Toggle Button -->\n\n <!-- Logo -->\n <a class=\"navbar-brand\" href=\"/\"><img class=\"CMS_edit g-width-200\" src=\"//via.placeholder.com/200x45\" alt=\"Image Description\"></a>\n <!-- End Logo -->\n\n <!-- Navigation -->\n <div class=\"collapse navbar-collapse align-items-center flex-sm-row g-pt-15 g-pt-0--lg CMS_attribute\">\n <ul class=\"navbar-nav text-uppercase g-pos-rel ml-auto\"></ul>\n </div>\n <!-- End Navigation -->\n </div>\n </nav>\n </div>\n</header>\n<!-- End Header -->","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/header/nav/megamenu","dateupdated":"2019-06-28T06:06:46.019Z"}
  17. {"id":"19062622230001tda1","name":"basics-footer-bottombar-v2","reference":"basics/footer/bottombar/v2.html","body":"<style></style>\n\n<footer class=\"g-bg-white g-py-20\">\n <div class=\"container\">\n <div class=\"row\">\n <div class=\"col-md-8 align-self-center text-center text-md-left g-mb-10 g-mb-0--md\">\n <div class=\"d-lg-flex\">\n <small class=\"d-block g-font-size-default g-mr-10 g-mb-10 g-mb-0--md CMS_edit\">2019 © All Rights Reserved.</small>\n <ul class=\"u-list-inline\">\n <li class=\"list-inline-item\">\n <a class=\"g-color-black-opacity-0_8 g-color-black--hover CMS_edit\" href=\"#!\">Privacy Policy</a>\n </li>\n <li class=\"list-inline-item\"><span>|</span></li>\n <li class=\"list-inline-item\">\n <a class=\"g-color-black-opacity-0_8 g-color-black--hover CMS_edit\" href=\"#!\">Terms of Use</a>\n </li>\n <li class=\"list-inline-item\"><span>|</span></li>\n <li class=\"list-inline-item\">\n <a class=\"g-color-black-opacity-0_8 g-color-black--hover CMS_edit\" href=\"#!\">License</a>\n </li>\n <li class=\"list-inline-item\"><span>|</span></li>\n <li class=\"list-inline-item\">\n <a class=\"g-color-black-opacity-0_8 g-color-black--hover CMS_edit\" href=\"#!\">Support</a>\n </li>\n </ul>\n </div>\n </div>\n\n <div class=\"col-md-4 align-self-center\">\n <a class=\"d-block text-center text-md-right\" href=\"/\">\n <img class=\"img-fluid CMS_edit\" src=\"img/c_logo.png\" alt=\"Logo\">\n </a>\n </div>\n </div>\n </div>\n</footer>","datecreated":"2019-06-26T13:23:54.877Z","picture":"","icon":"","category":"basics/footer/bottombar","dateupdated":"2019-06-28T06:06:46.019Z"}
  18. {"id":"19062622230003tda1","name":"basics-main-hero-v2","reference":"basics/main/hero/v2.html","body":"<style></style>\n\n<script editor>\n option('bgImage', '배경 이미지', '', 'file');\n\n exports.configure = async function(options, $el, prevOptions){\n $('.basics-main-hero-v2', $el).css('background-image', `url(${options.bgImage})`);\n };\n</script>\n\n<section class=\"g-pt-100 g-pb-40 basics-main-hero-v2\" style=\"background-image: url(//via.placeholder.com/320x265?text=pattern%20image);\">\n <div class=\"container\">\n <figure class=\"row\">\n <!-- Figure Image -->\n <div class=\"col-md-8 offset-md-2 col-lg-4 offset-lg-0 g-mb-30\">\n <img class=\"w-100 CMS_edit\" src=\"//via.placeholder.com/150?text=image\" alt=\"Image Description\">\n </div>\n <!-- End Figure Image -->\n\n <!-- Figure Body -->\n <div class=\"col-lg-8\">\n <div class=\"d-flex justify-content-between g-mb-10\">\n <div class=\"g-mb-20\">\n <h4 class=\"h5 g-mb-5 CMS_edit\">이곳을 편집하세요.</h4>\n <em class=\"d-block g-font-style-normal g-font-size-default text-uppercase g-color-primary CMS_edit\">이곳을 편집하세요.</em>\n </div>\n\n <!-- Figure Social Icons -->\n <ul class=\"list-inline\">\n <li class=\"list-inline-item g-mx-2\">\n <a class=\"u-icon-v1 u-icon-size--sm u-icon-slide-up--hover g-color-gray-light-v1 g-bg-gray-light-v5 g-color-gray-light-v1--hover rounded-circle CMS_edit\" href=\"#!\">\n <i class=\"g-font-size-default g-line-height-1 u-icon__elem-regular fab fa-facebook-f\"></i>\n <i class=\"g-font-size-default g-line-height-0_8 u-icon__elem-hover fab fa-facebook-f\"></i>\n </a>\n </li>\n <li class=\"list-inline-item g-mx-2\">\n <a class=\"u-icon-v1 u-icon-size--sm u-icon-slide-up--hover g-color-gray-light-v1 g-bg-gray-light-v5 g-color-gray-light-v1--hover rounded-circle CMS_edit\" href=\"#!\">\n <i class=\"g-font-size-default g-line-height-1 u-icon__elem-regular fab fa-twitter\"></i>\n <i class=\"g-font-size-default g-line-height-0_8 u-icon__elem-hover fab fa-twitter\"></i>\n </a>\n </li>\n </ul>\n <!-- End Figure Social Icons -->\n </div>\n\n <!-- Figure Info -->\n <div class=\"g-mb-50\">\n\n <p class=\"CMS_edit\">이곳을 편집하세요.</p>\n\n <p class=\"CMS_edit\">이곳을 편집하세요.</p>\n\n <p class=\"CMS_edit\">이곳을 편집하세요.</p>\n\n <p class=\"CMS_edit\">이곳을 편집하세요.</p>\n\n <h4 class=\"h6 g-font-weight-700 text-uppercase g-mb-0 CMS_edit\">\n <span class=\"d-inline-block g-width-40 g-height-2 g-bg-primary g-pos-rel g-top-minus-3 mr-2\"></span>\n 이곳을 편집하세요.\n </h4>\n\n </div>\n <!-- End Info -->\n </div>\n <!-- End Figure Body -->\n </figure>\n </div>\n</section>","datecreated":"2019-06-26T13:23:54.877Z","picture":"","icon":"","category":"basics/main/hero","dateupdated":"2019-06-28T06:06:46.019Z"}
  19. {"id":"19062622230004tda0","name":"basics-main-topic-v2","reference":"basics/main/topic/v2.html","body":"<style></style>\n\n<section class=\"g-bg-gray-dark-v1 g-color-white g-pa-50\">\n <div class=\"container text-center\">\n <p class=\"lead g-font-weight-400 g-mr-20--md g-mb-30 g-mb-0--md CMS_edit\">이곳을 편집하세요.</p>\n </div>\n</section>","datecreated":"2019-06-26T13:23:54.877Z","picture":"","icon":"","category":"basics/main/topic","dateupdated":"2019-06-28T06:06:46.019Z"}
  20. {"id":"19062622230005tda1","name":"basics-main-carousel-piece-v3","reference":"basics/main/carousel/piece/v3.html","body":"<style>\n #CMS .basics-main-carousel-piece-v3.js-slide { visibility: visible; min-height: 100px; max-height: 500px; margin-bottom: .5rem; }\n #CMS .CMS_selected > .basics-main-carousel-piece-v3 { border: .5rem solid #1A88E0; }\n</style>\n\n<script editor>\n option('slideImage', '이미지', '', 'file');\n\n exports.configure = async function(options, $el, prevOptions){\n $('.slide-image', $el).css('background-image', `url(${options.slideImage})`);\n };\n</script>\n\n<div class=\"js-slide slide-image g-flex-centered g-bg-cover g-bg-pos-top-center g-bg-img-hero g-bg-bluegray-opacity-0_5--after basics-main-carousel-piece-v3\" style=\"background-image: url(//via.placeholder.com/1280x768);\" data-calc-target=\".header-nav\"></div>","datecreated":"2019-06-26T13:23:54.877Z","picture":"","icon":"","category":"basics/main/carousel/piece","dateupdated":"2019-06-28T06:06:46.019Z"}
  21. {"id":"19062510290001irk1","name":"extensions-question-v1","reference":"extensions/question/v1.html","body":"<style></style>\n\n<script total>\n const Question = DB('question');\n\n ROUTE('POST /api/v3/question/paginate', ['*Question --> @paginate']);\n\n // ╔╦╗╔═╗╔═╗╦╔╗╔╔═╗ ╔═╗╔═╗╦ ╦╔═╗╔╦╗╔═╗\n // ║║║╣ ╠╣ ║║║║║╣ ╚═╗║ ╠═╣║╣ ║║║╠═╣\n // ═╩╝╚═╝╚ ╩╝╚╝╚═╝ ╚═╝╚═╝╩ ╩╚═╝╩ ╩╩ ╩o\n // ─────────────────────────────────────\n let questionSchema = GETSCHEMA('Question');\n\n if ( !questionSchema ) NEWSCHEMA('Question', makeQuestionSchema);\n else makeQuestionSchema(questionSchema);\n\n function makeQuestionSchema(schema){\n schema.define('_id', 'String(24)');\n\n schema.define('question_code', 'String');\n schema.define('question_type', 'String');\n schema.define('school_grade_name', 'String');\n schema.define('school_grade_code', 'String');\n schema.define('suject_name', 'String');\n schema.define('suject_code', 'String');\n schema.define('assessment_area_code', 'Number');\n schema.define('assessment_area_name', 'String');\n schema.define('question_img', 'String');\n schema.define('anser_img', 'String');\n schema.define('anser_guide_img', 'String');\n schema.define('achivement_code', 'String');\n schema.define('sound_file', 'String');\n schema.define('answer_choice', 'Number');\n schema.define('answer_subjective', 'String');\n schema.define('percent_total', 'Number');\n schema.define('percent_high', 'Number');\n schema.define('percent_medium', 'Number');\n schema.define('percent_standard', 'Number');\n schema.define('percent_low', 'Number');\n schema.define('achivement_standard', 'String');\n schema.define('split_score', 'Object');\n\n schema.define('createdAt', 'Date');\n schema.define('updatedAt', 'Date');\n\n schema.define('dtParams', 'Object');\n\n schema.addWorkflow('paginate', async function($){\n let model = $.model.$clean();\n\n let paginate = model.dtParams;\n\n if ( isNaN(paginate.start) )\n return $.controller.throw400(`\"start\" ${TRANSLATE($.controller.req.me.language, 'Field is Required')}`);\n if ( isNaN(paginate.length) )\n return $.controller.throw400(`\"length\" ${TRANSLATE($.controller.req.me.language, 'Field is Required')}`);\n\n paginate.start = Number(paginate.start);\n paginate.length = Number(paginate.length);\n\n let query = _.pickBy(_.omit(paginate, ['start', 'length', 'order', 'search', 'draw', 'columns']), function(param){ return !_.isNil(param) && param !== ''; });\n\n if ( paginate.search && paginate.search.value )\n {\n let searchKeyword = new RegExp(paginate.search.value);\n query.$or = [\n { title: searchKeyword },\n ];\n }\n\n let count = await Question.countDocuments(query);\n // let data = await Question.find(query).toArray();\n\n $.callback({\n draw: paginate.draw,\n recordsTotal: count,\n recordsFiltered: count,\n data: await Question.find(query).toArray(),\n });\n });\n };\n</script>\n\n<script>\n COMPONENT('extensions-question-v1', '', function(component, config){\n });\n</script>\n","datecreated":"2019-06-25T01:29:18.956Z","picture":"","icon":"","category":"extensions/question","dateupdated":"2019-06-28T06:06:46.019Z"}
  22. {"id":"19062622230002tda0","name":"basics-main-carousel-v3","reference":"basics/main/carousel/v3.html","body":"<style>\n #CMS .basics-main-carousel-v3 { background-color: #aaaaaa80; min-height: 10rem; }\n\n #CMS .basics-main-carousel-v3 .promo-box {\n position: relative !important;\n transform: initial;\n padding: 2rem 0;\n }\n\n #CMS .basics-main-carousel-v3 .js-carousel.CMS_widgets {\n min-height: 2rem;\n border: 2px dashed #aaaaaa80;\n max-width: 80%;\n margin: 0 auto;\n }\n</style>\n\n<script>\n COMPONENT('basics-main-carousel-v3', function(component){\n component.$el = component.element;\n\n component.$carousel = $('.js-carousel', component.$el);\n\n component.make = function(){\n $.HSCore.components.HSCarousel.init(component.$carousel);\n };\n }, [\n '/libs/slick-carousel/1.9.0/slick.min.css',\n '/libs/slick-carousel/1.9.0/slick.min.js',\n\n '/libs/ui/1.0.0/js/components/hs.carousel.js',\n ]);\n</script>\n\n<section class=\"g-pos-rel basics-main-carousel-v3\" data-jc=\"basics-main-carousel-v3\">\n <!-- Promo Block - Carousel -->\n <div class=\"js-carousel CMS_widgets\" data-cms-importable=\"basics-main-carousel-piece-v3\" data-autoplay=\"true\" data-infinite=\"true\" data-fade=\"true\" data-speed=\"5000\"></div>\n <!-- End Promo Block - Carousel -->\n\n <!-- Promo Block Content -->\n <div class=\"g-absolute-centered--y g-left-0 g-right-0 g-z-index-1 promo-box\">\n <div class=\"container text-center\">\n <h2 class=\"h2 g-color-white g-font-weight-600 g-font-size-40 g-font-size-70--md g-line-height-1 mb-5 CMS_edit\">여기를 편집해주세요.</h2>\n <p class=\"g-color-white g-font-weight-300 g-font-size-20 g-font-size-24--md mb-5 CMS_edit CMS_multiline\">여기를 편집해주세요.\n <br>\n 여러줄 수정이 가능합니다.\n </p>\n <a class=\"btn u-btn-outline-white g-color-white g-bg-white-opacity-0_2 g-color-black--hover g-bg-white--hover g-font-weight-600 g-font-size-12 text-uppercase g-rounded-50 g-px-25 g-py-15 CMS_edit\" href=\"#!\">여기를 수정해주세요.</a>\n </div>\n </div>\n <!-- End Promo Block Content -->\n</section>","datecreated":"2019-06-26T13:23:54.877Z","picture":"","icon":"","category":"basics/main/carousel","dateupdated":"2019-06-28T06:06:46.019Z"}
  23. {"id":"19062722420001moa1","name":"basics-main-carousel-v4","reference":"basics/main/carousel/v4.html","body":"<style>\n #CMS .basics-main-carousel-v4 { min-height: 10rem; }\n</style>\n\n<script>\n COMPONENT('basics-main-carousel-v4', function(component){\n component.$el = component.element;\n\n component.make = function(){\n $.HSCore.components.HSCarousel.init(component.$el);\n };\n }, [\n '/libs/slick-carousel/1.9.0/slick.min.css',\n '/libs/slick-carousel/1.9.0/slick.min.js',\n\n '/libs/ui/1.0.0/js/components/hs.carousel.js',\n ]);\n</script>\n\n<div class=\"js-carousel basics-main-carousel-v4 text-center g-pb-30 CMS_widgets\" data-cms-importable=\"basics-main-carousel-piece-v4\" data-infinite=\"true\" data-jc=\"basics-main-carousel-v4\" data-arrows-classes=\"u-arrow-v1 g-absolute-centered--y g-width-35 g-height-40 g-font-size-18 g-color-gray g-bg-white g-mt-minus-10\" data-arrow-left-classes=\"fa fa-angle-left g-left-0\" data-arrow-right-classes=\"fa fa-angle-right g-right-0\"></div>","datecreated":"2019-06-27T13:42:45.910Z","picture":"","icon":"","category":"basics/main/carousel","dateupdated":"2019-06-28T06:06:46.019Z"}
  24. {"id":"19062722420002moa0","name":"extensions-addon-hs-height-calc","reference":"extensions/addon/hs/height-calc.html","body":"<style></style>\n\n<script>\n COMPONENT('extensions-addon-hs-height-calc', function(component, config){\n component.make = function(){\n $.HSCore.helpers.HSHeightCalc.init();\n };\n }, [\n '/libs/ui/1.0.0/js/helpers/hs.height-calc.js',\n ]);\n</script>\n\n<div class=\"extensions-addon-hs-height-calc script-module\" data-jc=\"extensions-addon-hs-height-calc\" data-module-name=\"$.HSCore.helpers.HSHeightCalc.init\"></div>","datecreated":"2019-06-27T13:42:45.910Z","picture":"","icon":"","category":"extensions/addon/hs","dateupdated":"2019-06-28T06:06:46.019Z"}
  25. {"id":"19062722420003moa1","name":"extensions-addon-hs-onscroll-animation","reference":"extensions/addon/hs/onscroll-animation.html","body":"<style></style>\n\n<script>\n COMPONENT('extensions-addon-hs-onscroll-animation', function(component, config){\n component.make = function(){\n $.HSCore.components.HSOnScrollAnimation.init('[data-animation]');\n };\n }, [\n '/libs/ui/1.0.0/js/components/hs.onscroll-animation.js',\n ]);\n</script>\n\n<div class=\"extensions-addon-hs-onscroll-animation script-module\" data-jc=\"extensions-addon-hs-onscroll-animation\" data-module-name=\"$.HSCore.components.HSOnScrollAnimation.init\"></div>","datecreated":"2019-06-27T13:42:45.910Z","picture":"","icon":"","category":"extensions/addon/hs","dateupdated":"2019-06-28T06:06:46.019Z"}
  26. {"id":"19062722420004moa0","name":"basics-main-carousel-piece-v4","reference":"basics/main/carousel/piece/v4.html","body":"<style>\n #CMS .basics-main-carousel-piece-v4 { display: flex; }\n #CMS .basics-main-carousel-piece-v4.js-slide {\n visibility: visible;\n margin: auto;\n min-height: 100px;\n max-height: 500px;\n max-width: 600px;\n margin-bottom: .5rem;\n }\n #CMS .CMS_selected > .basics-main-carousel-piece-v4 { border: .5rem solid #1A88E0; }\n</style>\n\n<script editor>\n option('slideImage', '이미지', '', 'file');\n option('captionText', '캡션 텍스트', '');\n\n exports.configure = async function(options, $el, prevOptions){\n $('.slide-image', $el)\n .css('background-image', `url(${options.slideImage})`)\n .find('a')\n .attr({\n 'data-caption': options.captionText,\n 'data-src': options.slideImage,\n })\n .find('img')\n .attr('src', options.slideImage);\n };\n</script>\n\n<script>\n COMPONENT('basics-main-carousel-piece-v4', function(component, config){\n }, [\n '/libs/jquery-fancybox/3.5.7/jquery.fancybox.min.css',\n '/libs/jquery-fancybox/3.5.7/jquery.fancybox.min.js',\n ]);\n</script>\n\n<div class=\"js-slide slide-image basics-main-carousel-piece-v4\" data-jc=\"basics-main-carousel-piece-v4\">\n <a class=\"js-fancybox\" href=\"javascript:;\" data-fancybox=\"lightbox-gallery--07-1\" data-src=\"//via.placeholder.com/1280x768\" data-caption=\"caption\" data-animate-in=\"bounceInDown\" data-animate-out=\"bounceOutDown\" data-speed=\"1000\" data-overlay-blur-bg=\"true\">\n <img class=\"img-fluid g-rounded-6\" src=\"//via.placeholder.com/1280x768\" alt=\"Image Description\">\n </a>\n</div>\n","datecreated":"2019-06-27T13:42:45.910Z","picture":"","icon":"","category":"basics/main/carousel/piece","dateupdated":"2019-06-28T06:06:46.019Z"}
  27. {"id":"19062809270001yai1","name":"extensions-addon-hs-cubeportfolio","reference":"extensions/addon/hs/cubeportfolio.html","body":"<style></style>\n\n<script>\n COMPONENT('extensions-addon-hs-cubeportfolio', function(component, config){\n component.make = function(){\n $.HSCore.components.HSCubeportfolio.init('.cbp');\n };\n }, [\n '/libs/jquery-cubeportfolio/4.4.0/jquery.cubeportfolio.min.css',\n '/libs/jquery-cubeportfolio/4.4.0/jquery.cubeportfolio.min.js',\n\n '/libs/ui/1.0.0/js/components/hs.cubeportfolio.js',\n ]);\n</script>\n\n<div class=\"extensions-addon-hs-cubeportfolio script-module\" data-jc=\"extensions-addon-hs-cubeportfolio\" data-module-name=\"$.HSCore.components.HSCubeportfolio.init\"></div>","datecreated":"2019-06-28T00:27:48.668Z","picture":"","icon":"","category":"extensions/addon/hs","dateupdated":"2019-06-28T06:06:46.019Z"}
  28. {"id":"19062809270002yai0","name":"extensions-addon-hs-sticky-block","reference":"extensions/addon/hs/sticky-block.html","body":"<style></style>\n\n<script>\n COMPONENT('extensions-addon-hs-sticky-block', function(component, config){\n component.make = function(){\n $.HSCore.components.HSStickyBlock.init('.js-sticky-block');\n };\n }, [\n '/libs/ui/1.0.0/js/components/hs.sticky-block.js',\n ]);\n</script>\n\n<div class=\"extensions-addon-hs-sticky-block script-module\" data-jc=\"extensions-addon-hs-sticky-block\" data-module-name=\"$.HSCore.components.HSStickyBlock.init\"></div>","datecreated":"2019-06-28T00:27:48.668Z","picture":"","icon":"","category":"extensions/addon/hs","dateupdated":"2019-06-28T06:06:46.019Z"}
  29. {"id":"19062510010015ohf1","name":"basics-main-bbs-v1","reference":"basics/main/bbs/v1.html","body":"<style>\n .basics-main-bbs-v1 .component-blocker { background: #eeeeee80; }\n .basics-main-bbs-v1 .component-blocker .progress { top: 50%; left: 50%; transform: translate(-50%, -50%); }\n .basics-main-bbs-v1 .component-blocker .blocker-message { top: 60%; left: 50%; transform: translate(-50%, -50%); }\n\n .basics-main-bbs-v1 .card-body.tab-content .tab-pane .post-editor { height: 500px; }\n .basics-main-bbs-v1 .tab-post-read {\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n max-width: 15rem;\n }\n\n .basics-main-bbs-v1 ul.recent-post-list { padding-left: 0; }\n .basics-main-bbs-v1 ul.recent-post-list li.recent-post-list-item { cursor: pointer; min-height: 40px; padding: .25rem; }\n .basics-main-bbs-v1 ul.recent-post-list li.recent-post-list-item:nth-child(even) { background-color:#f7f7f7; }\n .basics-main-bbs-v1 ul.recent-post-list li.recent-post-list-item:hover { background-color:rgba(0,0,0,.075); }\n .basics-main-bbs-v1 ul.recent-post-list li.recent-post-list-item a.post-title { max-width: 70%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }\n .basics-main-bbs-v1 ul.recent-post-list li.recent-post-list-item a.post-title:hover { text-decoration: none; }\n\n @media (min-width: 768px) {\n .basics-main-bbs-v1 ul.recent-post-list li.recent-post-list-item a.post-title { max-width: 80%; }\n }\n\n /* error message style */\n .basics-main-bbs-v1 .basics-main-bbs-v1-title.with-error {\n border-top-left-radius: .25rem;\n border-top-right-radius: .25rem;\n }\n\n .basics-main-bbs-v1 .basics-main-bbs-v1-title.with-error + .card-body {\n border-bottom-left-radius: .25rem;\n border-bottom-right-radius: .25rem;\n }\n\n /* ╔╦╗╔═╗╔╦╗╔═╗╔╦╗╔═╗╔╗ ╦ ╔═╗╔═╗ ╔═╗╔╦╗╦ ╦╦ ╦╔╗╔╔═╗ */\n /* ║║╠═╣ ║ ╠═╣ ║ ╠═╣╠╩╗║ ║╣ ╚═╗ ╚═╗ ║ ╚╦╝║ ║║║║║ ╦ */\n /* ═╩╝╩ ╩ ╩ ╩ ╩ ╩ ╩ ╩╚═╝╩═╝╚═╝╚═╝ ╚═╝ ╩ ╩ ╩═╝╩╝╚╝╚═╝ */\n .basics-main-bbs-v1 .post-list-tab-content .dataTables_wrapper .dataTables_filter {\n width:100%;\n }\n\n .basics-main-bbs-v1 .post-list-tab-content table.dataTable thead tr th { white-space: nowrap; }\n\n .basics-main-bbs-v1 .post-list-tab-content table.dataTable tbody tr { cursor: pointer; }\n\n .basics-main-bbs-v1 .post-list-tab-content table.dataTable tbody tr.post-notice {\n border-bottom: 1px solid --var(primary);\n }\n\n .basics-main-bbs-v1 .post-list-tab-content table.dataTable tbody tr:hover {\n background-color:rgba(0,0,0,.075);\n }\n\n .basics-main-bbs-v1 .post-list-tab-content table.dataTable tbody tr td.post-title {\n white-space: normal;\n max-width: 100%;\n outline: 0;\n }\n\n .basics-main-bbs-v1 .post-list-tab-content table.dataTable tbody tr td.post-title div.post-title-wrapper a.post-title,\n .basics-main-bbs-v1 .post-list-tab-content table.dataTable tbody tr td.post-writer a.post-writer {\n color: unset;\n }\n\n .basics-main-bbs-v1 .post-list-tab-content table.dataTable tbody tr td.post-title div.post-title-wrapper a.post-title:hover,\n .basics-main-bbs-v1 .post-list-tab-content table.dataTable tbody tr td.post-writer a.post-writer:hover {\n text-decoration: none;\n }\n\n .basics-main-bbs-v1 .post-list-tab-content .dataTables_wrapper .dataTables_filter {text-align: center!important;}\n .basics-main-bbs-v1 .post-list-tab-content .dataTables_paginate ul.pagination {justify-content: center!important;}\n\n /* ╦═╗╔═╗╔═╗╔═╗╔═╗╔╗╔╔═╗╦╦ ╦╔═╗ ╔═╗╔═╗╦═╗╔╦╗ ╔╦╗╔═╗╔╗ ╦ ╔═╗ */\n /* ╠╦╝║╣ ╚═╗╠═╝║ ║║║║╚═╗║╚╗╔╝║╣ ║ ╠═╣╠╦╝ ║║ ║ ╠═╣╠╩╗║ ║╣ */\n /* ╩╚═╚═╝╚═╝╩ ╚═╝╝╚╝╚═╝╩ ╚╝ ╚═╝ ╚═╝╩ ╩╩╚══╩╝ ╩ ╩ ╩╚═╝╩═╝╚═╝ */\n .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable thead { display: none; }\n\n .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable tbody tr,\n .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable tbody td { display: block; }\n .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable tbody td { border: 0; }\n .basics-main-bbs-v1.cards-table .post-list-tab-content .dataTables_paginate ul.pagination li.paginate_button.page-item a.page-link {padding: .25rem .5rem;}\n\n .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable tbody tr:first-child { margin-top: 1.5rem; }\n .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable tbody tr {\n margin-bottom: 2.14286rem !important;\n box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);\n border: solid 1px #0000 !important;\n border-color: #ccc !important;\n padding: 2.85714rem !important;\n /* border-right-width: 0!important; */\n /* border-left-width: 0!important; */\n background-color: #0000;\n }\n .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable tbody tr:hover {\n background-color: #00000013;\n }\n\n/* .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable tbody tr td.post-title a { text-decoration: none; } */\n/* .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable tbody tr td.post-title { text-align: center; font-size: 1.5rem */; }\n/* .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable tbody tr td.post-posted-at { text-align: right; } */\n\n .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable tbody tr td.post-seq,\n .basics-main-bbs-v1.cards-table .post-list-tab-content table.dataTable tbody tr td.post-view-count { display: none; }\n\n /* ╔═╗ ╦ ╦╦╦ ╦ ╦╔═╗ ╔═╗╔╦╗╦ ╦╦ ╔═╗ */\n /* ║═╬╗║ ║║║ ║ ║╚═╗ ╚═╗ ║ ╚╦╝║ ║╣ */\n /* ╚═╝╚╚═╝╩╩═╝╩═╝╚╝╚═╝ ╚═╝ ╩ ╩ ╩═╝╚═╝ */\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-title { margin-bottom: .5rem; }\n .basics-main-bbs-v1 .post-editor-tab-content .ql-toolbar {\n border-bottom: 0 !important;\n }\n\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-title,\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-toolbar,\n .basics-main-bbs-v1 .ql-container {\n border-left: 0 !important;\n border-right: 0 !important;\n border-radius: 0;\n }\n\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-attachments .attachment-list .attachment-list-item {\n cursor: pointer;\n }\n\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-attachments .attachment-list .attachment-list-item span.filename {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-attachments .attachment-list .attachment-list-item:nth-child(even) {\n background-color:#f7f7f7;\n }\n\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-attachments .attachment-list .attachment-list-item:not(:last-child) {\n border-bottom: 1px solid #ccc;\n }\n\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-attachments .attachment-list .attachment-list-item:hover {\n background-color: #3398dc2d;\n }\n\n .basics-main-bbs-v1 .post-read-content .post-viewer {\n border: 0;\n }\n\n /* 본문 첨부파일 객체 스타일 */\n .basics-main-bbs-v1 .ql-container .ql-editor div.attachment {\n display: flex;\n }\n\n .basics-main-bbs-v1 .ql-container .ql-editor div.attachment div.attachment-inner {\n min-width: 200px;\n max-width: 500px;\n padding: 1.5rem;\n margin: auto;\n cursor: pointer;\n text-align: center;\n border: 1px solid lightgray;\n display: flex;\n }\n\n .basics-main-bbs-v1 .ql-container .ql-editor div.attachment div.attachment-inner:hover {\n background-color: #3398dc2d;\n }\n\n .basics-main-bbs-v1 .ql-container .ql-editor div.attachment div.attachment-inner i.attachment-icon {\n margin: auto 0 auto auto;\n }\n .basics-main-bbs-v1 .ql-container .ql-editor div.attachment div.attachment-inner span.attachment-filename {\n margin: auto auto auto .75rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n width: 100%;\n }\n\n /* 뷰어 하단 첨부파일 리스트 스타일 */\n .basics-main-bbs-v1 .post-read-content .post-viewer-attachments ul.post-viewer-attachment-list li.post-viewer-attachment-list-item:nth-child(odd),\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-attachments ul.post-editor-attachments-list li.post-editor-attachment-list-item:nth-child(odd) {\n background-color: #f7f7f7;\n }\n\n .basics-main-bbs-v1 .post-read-content .post-viewer-attachments ul.post-viewer-attachment-list li.post-viewer-attachment-list-item:hover,\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-attachments ul.post-editor-attachments-list li.post-editor-attachment-list-item:hover {\n background-color: #00000013;\n }\n\n .basics-main-bbs-v1 .post-read-content .post-viewer-attachments ul.post-viewer-attachment-list li.post-viewer-attachment-list-item a.post-viewer-attachment-download:hover,\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-attachments ul.post-editor-attachments-list li.post-editor-attachment-list-item a {\n text-decoration: none;\n }\n\n .basics-main-bbs-v1 .post-editor-tab-content .post-editor-attachments ul.post-editor-attachments-list li.post-editor-attachment-list-item i.remove-attachment {\n cursor: pointer;\n }\n</style>\n\n<script editor>\n // let postGroupId = ObjectID().toString();\n // option('post-group-name', '게시판 명', '자유게시판');\n // option('post-group-id', '게시판 ID', postGroupId);\n // option('post-group-id', '게시판 ID', '아이디는 자동생성됩니다.');\n // option('post-group-description', '게시판 설명', '');\n // option('theme', '테마', 'blue', [\n // { text: '파랑', value: 'blue' },\n // { text: '초록', value: 'green' },\n // { text: '분홍', value: 'pink' },\n // { text: '어두운 보라', value: 'darkpurple' },\n // { text: '물', value: 'teal' },\n // ]);\n\n option('showTitle', '게시판 명 보이기', false);\n\n option('showPostNumber', '글 목록 - 글 번호 보이기', true);\n option('showAuthor', '글 목록 - 글 작성자 명 보이기', true);\n option('showCreatedAt', '글 목록 - 글 작성일 보이기', true);\n option('showViewCount', '글 목록 - 글 조회수 보이기', true);\n option('showFileOnList', '글 목록 - 첨부파일 보이기', false);\n\n option('showSearchInput', '글 목록 - 검색바 보이기', true);\n option('showRowPerPage', '글 목록 - 페이지당 글수 변경 보이기', false);\n\n option('mode', '모드', 'post', [\n { text: '게시판', value: 'post' },\n { text: '최신글', value: 'recent' },\n ]);\n\n option.type = 'post';\n\n exports.configure = async function(options, $el, prevOptions){\n let $component = $('.basics-main-bbs-v1', $el);\n\n let themes = ['blue','green','pink','darkpurple','teal'];\n\n let postGroup = _.find(options._datasrc.postGroups, { _id: options.selectedPostGroup });\n\n // 설정값 data 속성 저장\n $component.attr({\n 'data-post-group-id': postGroup._id,\n 'data-post-mode': options['mode'],\n 'data-show-download-on-list': options.showFileOnList,\n 'data-show-author': options.showAuthor,\n 'data-show-created-at': options.showCreatedAt,\n 'data-show-post-number': options.showPostNumber,\n 'data-show-search-input': options.showSearchInput,\n 'data-show-row-per-page': options.showRowPerPage,\n 'data-show-view-count': options.showViewCount,\n 'data-instant-id': $component.attr('data-instant-id') || helpers.getRandomId('short'),\n });\n\n // 테마 적용\n // $component.removeClass(themes.map(function(item){ return `g-brd-${item}`; }).join(' ')).addClass(`g-brd-${options['theme']}`);\n // $('.basics-main-bbs-v1-title', $component).removeClass(themes.map(function(item){ return `g-bg-${item}`; }).join(' ')).addClass(`g-bg-${options['theme']}`);\n\n // 게시판 타이틀\n $('.basics-main-bbs-v1-title', $component)\n .toggleClass('d-editor-block', !option.showTitle)\n .find('.post-group-name')\n .text(postGroup.name.replace(/\\s\\(\\d+\\)$/, ''));\n };\n</script>\n\n<script>\n COMPONENT('basics-main-bbs-v1', 'striped:true;bordered:false;totalCount:false;hover:true;placeholder:내용을 입력해주세요...;titlePlaceHolder:제목을 입력해주세요...', function(component, config){\n\n component.compile();\n\n const CONSTANTS = {\n $RECENT_POST_LIST: $(`<ul class=\"recent-post-list\"></ul>`),\n MODES: {\n RECENT: 'recent',\n POST: 'post',\n },\n POST_GROUP: {\n FEATURES: {\n NOTICE: 'notice',\n ATTACHMENT: 'attachment',\n COMMENT: 'comment',\n }\n },\n POST_GROUP_TYPES: {\n FILE_UPLOAD: 'file',\n NORMAL: 'normal',\n },\n TEMPLATES: {\n DATATABLE: {\n $PAGINATE: $(`<nav class=\"text-center\" aria-label=\"Page Navigation\"><ul class=\"list-inline\"></ul></nav>`)\n },\n VIEWER: {\n ATTACHMENT: {\n ITEM: $('<li class=\"d-flex\"></li>'),\n }\n },\n $BROWSE_FILE_INPUT: $('<input type=\"file\" multiple/>'),\n $TOOLBAR_BTN_FILE_UPLOAD: $('<button class=\"browse-file fas fa-upload\"></button>'),\n },\n PERMISSIONS: {\n POST: {\n READABLE: 'post.read',\n WRITABLE: 'post.write',\n NOTICE_WRITABLE: 'post.notice.write',\n FILE: {\n UPLOADABLE: 'post.file.upload',\n DOWNLOADABLE: 'post.file.download',\n },\n COMMENT: {\n READABLE: 'post.comment.read',\n WRITABLE: 'post.comment.write',\n }\n },\n },\n };\n\n component.postGroupSettings = {};\n component.allowedPermission = [];\n\n component.prevScrollTop = 0;\n component.sendFileList = [];\n\n component.$el = component.element;\n component.$blocker = $('.component-blocker', component.$el);\n component.$title = $('.basics-main-bbs-v1-title span', component.$el);\n component.$cardTitle = $('.basics-main-bbs-v1-title', component.$el);\n component.$cardBody = $('.card-body.tab-content', component.$el);\n component.$initSpinner = $('.init-spinner', component.$el);\n\n component.$progress = $('.progress', component.$blocker);\n component.$progressBar = $('.progress-bar', component.$progress);\n component.$blockerMessage = $('.blocker-message', component.$blocker);\n component.$blockerMessageTxt = $('.message', component.$blockerMessage);\n\n component.notices = [];\n\n component.instantID = component.$el.data('instant-id');\n if ( !component.instantID )\n {\n component.instantID = $.HSCore.helpers.getRandomId('short');\n component.$el.attr('data-instant-id', component.instantID).data('instant-id', component.instantID);\n }\n\n component.postGroupId = component.$el.data('post-group-id') || 'dummy';\n component.mode = component.$el.data('post-mode');\n\n component.$tabNavs = $('.nav.nav-tabs [data-toggle=\"tab\"]', component.$el);\n component.$tabContents = $('[role=\"tabpanel\"]', component.$el);\n\n component.$postListTab = component.$tabNavs.filter('.tab-post-list');\n component.$postViewerTab = component.$tabNavs.filter('.tab-post-read');\n component.$postEditorTab = component.$tabNavs.filter('.tab-post-editor');\n\n component.$postListTabContent = component.$tabContents.filter('.post-list-tab-content');\n component.$postViewerTabContent = component.$tabContents.filter('.post-read-content');\n component.$postEditorTabContent = component.$tabContents.filter('.post-editor-tab-content');\n\n component.$table = $('table.post-list', component.$postListTabContent);\n component.datatable = null; // datatables.api\n component.$originalPaginate = null;\n component.columnsDefines = [];\n\n if ( component.$el.data('show-post-number') !== false && component.$el.data('show-post-number') )\n {\n component.columnsDefines.push({\n data: 'seq', title: '번호', className: 'post-seq d-none d-md-block',\n render: function(data, type, row){\n return `<p class=\"m-0 px-3\">${data}</p>`;\n }\n });\n }\n component.columnsDefines.push({\n data: 'name', title: '제목', className: 'post-title g-font-size-24 g-font-size-default--md',\n render: function(data, type, row){\n return `<div class=\"text-center text-md-left post-title-wrapper\">\n<a class=\"post-title\" href=\"#\">${ $.fn.dataTable.render.ellipsis(40)($.fn.dataTable.render.text().display(data), type, row) }</a>\n</div>`;\n }\n });\n if ( component.$el.data('show-created-at') !== false && component.$el.data('show-created-at') )\n {\n component.columnsDefines.push({\n data: 'createdAt', title: '작성일', className: 'text-right text-md-center post-posted-at g-font-size-14 g-font-size-default--md',\n render: function(data, type, row){\n let createdAt = moment(data);\n let txt = createdAt.format('YYYY-MM-DD');\n\n if ( moment().diff(createdAt, 'days') === 0 ) txt = `오늘 ${createdAt.format('HH:mm')}`;\n\n return `\n<div class=\"d-flex\">\n <span class=\"ml-auto d-md-none\">작성일:</span>\n <p class=\"ml-2 mx-md-auto my-0\">${txt}</p>\n</div>`;\n }\n });\n }\n if ( component.$el.data('show-author') !== false && component.$el.data('show-author') )\n {\n component.columnsDefines.push({\n data: 'author.name', title: '작성자', className: 'post-writer g-font-size-14 g-font-size-default--md',\n render: function(data, type, row){\n return `\n<div class=\"d-flex\">\n <span class=\"ml-auto d-md-none\">작성자:</span>\n <a class=\"post-writer ml-2 mx-md-auto\" href=\"#\">${$.fn.dataTable.render.text().display(data)}</a>\n</div>`;\n }\n });\n }\n if ( component.$el.data('show-view-count') !== false && component.$el.data('show-view-count') )\n {\n component.columnsDefines.push({ data: 'viewcount', title: '조회수', className: 'post-view-count d-none d-md-block' });\n }\n if ( component.$el.data('show-download-on-list') !== false && component.$el.data('show-download-on-list') )\n {\n component.columnsDefines.push({\n data: 'attachments', title: '파일', className: 'attachment-preview',\n render: function(data, type, row){\n if ( data && data.length > 0 )\n return `\n<div class=\"d-flex\">\n <span class=\"ml-auto d-md-none\">파일:</span>\n <a href=\"#!\" class=\"ml-2 mx-md-auto my-0 file-download\" onclick=\"javascript:void(0)\" data-file-id=\"${data[0]._id}\" title=\"${data[0].filename}\"><i class=\"fas fa-paperclip\"></i>\n</div>`;\n return ``;\n }\n });\n }\n\n component.$viewerScrollContainer = $('.post-viewer-scroll-container', component.$postViewerTabContent);\n component.$viewer = $('.post-viewer', component.$postViewerTabContent);\n component.$viewerMetadata = $('.post-viewer-metadata', component.$postViewerTabContent);\n component.$viewerControls = $('.post-viewer-controls', component.$postViewerTabContent);\n component.$viewerAttachments = $('.post-viewer-attachments', component.$postViewerTabContent);\n component.$viewerAttachmentList = $('.post-viewer-attachment-list', component.$postViewerTabContent);\n component.$viewerComments = $('.post-viewer-comments', component.$postViewerTabContent);\n component.$viewerCommentList = $('.post-viewer-comment-list', component.$postViewerTabContent);\n component.$viewerCommentForm = $('.post-viewer-comment-form', component.$postViewerTabContent);\n\n component.editor = null;\n component.$editorScrollContainer = $('.post-editor-scroll-container', component.$postEditorTabContent);\n component.$editor = $('.post-editor', component.$postEditorTabContent);\n component.$editorToolbar = $('.post-editor-toolbar', component.$postEditorTabContent);\n component.$editorPostMetadata = $('.post-editor-metadata-form', component.$postEditorTabContent);\n component.postEditForm = {};\n\n let inputTitleRandomId = $.HSCore.helpers.getRandomId();\n let inputNoticeRandomId = $.HSCore.helpers.getRandomId();\n let inputFileHideRandomId = $.HSCore.helpers.getRandomId();\n\n component.$editorPostMetadataForm = $(`\n<div class=\"form-group m-0 p-2\">\n <div class=\"form-check form-check-inline editor-post-notice-form\">\n <input class=\"form-check-input editor-post-notice\" type=\"checkbox\" id=\"${inputNoticeRandomId}\">\n <label class=\"form-check-label\" for=\"${inputNoticeRandomId}\">공지사항</label>\n </div>\n <div class=\"form-check form-check-inline editor-post-hide-attachment-form d-none\">\n <input class=\"form-check-input editor-post-hide-attachment\" type=\"checkbox\" id=\"${inputFileHideRandomId}\">\n <label class=\"form-check-label\" for=\"${inputFileHideRandomId}\">본문에서 파일 숨기기</label>\n </div>\n</div>`\n );\n component.$editorPostInputTitle = $(`<input type=\"text\" placeholder=\"${config.titlePlaceHolder}\" class=\"form-control rounded-0 border-left-0 border-right-0 border-bottom-0 border-top border-gray editor-post-title\" id=\"${inputTitleRandomId}\">`);\n component.$editorPostFormNotice = $('.editor-post-notice-form', component.$editorPostMetadataForm);\n component.$editorPostFormHideAttachment = $('.editor-post-hide-attachment-form', component.$editorPostMetadataForm);\n component.$editorPostInputNotice = $('input.editor-post-notice', component.$editorPostMetadataForm);\n component.$editorPostInputHideAttachment = $('input.editor-post-hide-attachment', component.$editorPostMetadataForm);\n\n component.$editorControls = $('.post-editor-controls', component.$postEditorTabContent);\n component.$editorAttachments = $('.post-editor-attachments', component.$postEditorTabContent);\n component.$editorAttachmentList = $('.post-editor-attachments-list', component.$postEditorTabContent);\n\n component.helpers = {};\n\n /**\n * 에러 메시지 파서\n * @param {Error} err 에러 객체\n * @return {String} 파싱된 메시지\n */\n component.helpers.parseErrorMessage = function(err){\n let errorMessageStart = '<div class=\"error\">';\n let errorMessageEnd = '</div>';\n\n if ( err.responseText )\n {\n if ( err.responseText.indexOf(errorMessageStart) < 0 ) return err.responseText;\n\n let messageStartIndex = err.responseText.indexOf(errorMessageStart) + errorMessageStart.length;\n let messageEndIndex = err.responseText.indexOf(errorMessageEnd, messageStartIndex);\n\n return err.responseText.substring(messageStartIndex, messageEndIndex);\n }\n\n return err.message;\n };\n\n component.postConfirm = function(){\n let $confirm = $('.ui-confirm', component.$el).get(0).$com;\n return $confirm || FIND('confirm');\n };\n\n component.postSnackbar = function(){\n let $snackbar = $('.ui-snackbar', component.$el).get(0).$com;\n return $snackbar || FIND('snackbar');\n };\n\n component._getFileSize = function(size){\n let _size = size;\n\n if ( _size < 1024 ) return `${_size.toFixed(2)} B`;\n _size = _size / 1024;\n\n if ( _size < 1024 ) return `${_size.toFixed(2)} KB`;\n _size = _size / 1024;\n\n if ( _size < 1024 ) return `${_size.toFixed(2)} MB`;\n _size = _size / 1024;\n\n if ( _size < 1024 ) return `${_size.toFixed(2)} GB`;\n\n return `1TB 이상`;\n };\n // ╦╔═╗╔═╗╔╦╗╔═╗╔═╗╔╗╔╔═╗╔╗╔╔╦╗ ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗\n // ║║ ║ ║║║║╠═╝║ ║║║║║╣ ║║║ ║ ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣\n // ╚╝╚═╝╚═╝╩ ╩╩ ╚═╝╝╚╝╚═╝╝╚╝ ╩ ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝o\n // ─────────────────────────────────────────────────────────\n component.configure = function(key, val, init){\n switch ( key )\n {\n case 'hover':\n case 'bordered':\n case 'striped':\n component.$table.toggleClass(`table-${key}`, val);\n break;\n case 'height':\n let height = !isNaN(val) ? `${val}px` : val;\n component.$el.css({ height: height });\n break;\n }\n };\n\n component.template = `/api/post-groups/${component.postGroupId}/include-notices`;\n component.make = function(response){\n if ( response.err )\n {\n component.$cardTitle.addClass('with-error border border-danger');\n return component.$cardBody.empty().append(`\n<div class=\"d-flex bg-danger text-white p-4\">\n <i class=\"ml-auto mr-2 my-auto fas fa-exclamation-circle fa-2x\"></i> <span class=\"mr-auto my-auto\">${component.helpers.parseErrorMessage(response)}</span>\n</div>`);\n }\n\n component.postGroup = response;\n component.notices = response.notices;\n\n // 세션 권한 계산\n if ( _.get(TOTAL_LOCALS, '_SESSION') )\n {\n component.allowedPermission =\n _.chain(TOTAL_LOCALS._SESSION.parents)\n .map('_id')\n .intersection(_.keys(component.postGroup.permission))\n .map(function(userPermission){\n return component.postGroup.permission[userPermission];\n })\n .flatten()\n .uniq()\n .value();\n\n if ( TOTAL_LOCALS._SESSION.isSysadmin )\n {\n component.allowedPermission = [CONSTANTS.PERMISSIONS.POST.READABLE,CONSTANTS.PERMISSIONS.POST.WRITABLE,CONSTANTS.PERMISSIONS.POST.NOTICE_WRITABLE,CONSTANTS.PERMISSIONS.POST.FILE.UPLOADABLE,CONSTANTS.PERMISSIONS.POST.FILE.DOWNLOADABLE,CONSTANTS.PERMISSIONS.POST.FILE.DOWNLOADABLE,CONSTANTS.PERMISSIONS.POST.COMMENT.READABLE,CONSTANTS.PERMISSIONS.POST.COMMENT.WRITABLE];\n }\n }\n // hljs 설정\n hljs.configure({ languages: ['html', 'javascript', 'css', 'bash'] });\n\n // Quilljs 모듈 확장\n const Parchment = Quill.import('parchment');\n\n const AttributeAttributorTitle = new Parchment.Attributor.Attribute('title', 'title');\n const AttributeAttributorFileId = new Parchment.Attributor.Attribute('data-file-id', 'data-file-id');\n const AttributeAttributorLastModified = new Parchment.Attributor.Attribute('data-last-modified', 'data-last-modified');\n const AttributeAttributorLastModifiedDate = new Parchment.Attributor.Attribute('data-last-modified-date', 'data-last-modified-date');\n const AttributeAttributorName = new Parchment.Attributor.Attribute('data-name', 'data-name');\n const AttributeAttributorSize = new Parchment.Attributor.Attribute('data-size', 'data-size');\n const AttributeAttributorType = new Parchment.Attributor.Attribute('data-type', 'data-type');\n const AttributeAttributorContentEditable = new Parchment.Attributor.Attribute('contenteditable', 'contenteditable');\n\n // const ClassAttributorDisplay = new Parchment.Attributor.Class('display', 'd');\n\n Parchment.register(AttributeAttributorTitle);\n Parchment.register(AttributeAttributorFileId);\n Parchment.register(AttributeAttributorLastModified);\n Parchment.register(AttributeAttributorLastModifiedDate);\n Parchment.register(AttributeAttributorName);\n Parchment.register(AttributeAttributorSize);\n Parchment.register(AttributeAttributorType);\n Parchment.register(AttributeAttributorContentEditable);\n\n // Parchment.register(ClassAttributorDisplay);\n\n Quill.register({\n 'formats/title': AttributeAttributorTitle,\n 'formats/data-file-id': AttributeAttributorFileId,\n 'formats/data-last-modified': AttributeAttributorLastModified,\n 'formats/data-last-modified-date': AttributeAttributorLastModifiedDate,\n 'formats/data-name': AttributeAttributorName,\n 'formats/data-size': AttributeAttributorSize,\n 'formats/data-type': AttributeAttributorType,\n 'formats/contenteditable': AttributeAttributorContentEditable,\n\n // 'attributors/class/display': ClassAttributorDisplay,\n });\n\n const EmbedBlock = Quill.import(\"blots/block/embed\");\n\n class AttachmentBlock extends EmbedBlock {\n static create(options = {}) {\n let node = super.create(options.filename);\n let inner = document.createElement('div');\n let icon = document.createElement('i');\n let fileName = document.createElement('span');\n\n if ( options.file instanceof File )\n {\n options = {\n fileId: options.fileId || $.HSCore.helpers.getRandomId(),\n lastModified: options.file.lastModified,\n lastModifiedDate: options.file.lastModifiedDate,\n name: options.file.name,\n size: options.file.size,\n type: options.file.type,\n };\n AttributeAttributorFileId.add(node, options.fileId);\n AttributeAttributorLastModified.add(node, options.lastModified);\n AttributeAttributorLastModifiedDate.add(node, options.lastModifiedDate);\n AttributeAttributorName.add(node, options.name);\n AttributeAttributorTitle.add(node, options.name);\n AttributeAttributorSize.add(node, options.size);\n AttributeAttributorType.add(node, options.type);\n\n fileName.textContent = node.getAttribute('data-name');\n }\n\n AttributeAttributorContentEditable.add(node, false);\n\n inner.className = 'attachment-inner';\n icon.className = 'attachment-icon fas fa-2x fa-file';\n fileName.className = 'attachment-filename';\n\n inner.appendChild(icon);\n inner.appendChild(fileName);\n\n node.appendChild(inner);\n\n return node;\n }\n\n format(name, value) {\n super.format(name, value);\n if ( name === AttributeAttributorName.attrName )\n this.domNode.querySelector('.attachment-filename').textContent = value;\n\n if ( name === AttributeAttributorSize.attrName )\n this.domNode.querySelector('.attachment-filename').textContent += ` ( ${this._getFileSize(value)} )`;\n }\n\n _getFileSize(size){\n let _size = size;\n\n if ( _size < 1024 ) return `${_size.toFixed(2)} B`;\n _size = _size / 1024;\n\n if ( _size < 1024 ) return `${_size.toFixed(2)} KB`;\n _size = _size / 1024;\n\n if ( _size < 1024 ) return `${_size.toFixed(2)} MB`;\n _size = _size / 1024;\n\n if ( _size < 1024 ) return `${_size.toFixed(2)} GB`;\n\n return `1TB 이상`;\n }\n\n static value(node) {\n return node.className;\n }\n }\n\n AttachmentBlock.blotName = 'attachment';\n AttachmentBlock.className = 'attachment';\n AttachmentBlock.tagName = 'div';\n\n Quill.register(AttachmentBlock, true);\n\n // 탭 ID 임의화\n component.$tabNavs.each(function(){\n let tabId = $.HSCore.helpers.getRandomId();\n let paneId = $.HSCore.helpers.getRandomId();\n\n let $tabNav = $(this);\n let $tabPane = component.$el.find(`.card-body.tab-content ${$tabNav.attr('href')}.tab-pane`);\n\n $tabPane.prop('id', paneId);\n $tabPane.attr('aria-labelledby', tabId);\n\n $tabNav.prop('id', tabId);\n $tabNav.attr({ 'href': `#${paneId}`, 'aria-controls': paneId });\n });\n\n // 뷰어 설정\n component.$viewerControls.on('click', '.to-list', async function($event){\n component._changeHash('#list');\n });\n\n component.$viewerControls.on('click', '.edit-post', async function($event){\n component._changeHash(`#editor-edit${component.$viewer.data('post-id')}`);\n });\n\n component.$viewerControls.on('click', '.remove-post', async function($event){\n component.postConfirm().confirm('정말로 글을 삭제하시겠습니까?', [`\"check-circle\" 네`, '아니오'], async function(buttonIndex) {\n if ( buttonIndex > 0 ) return; // cancel\n try\n {\n await $.ajax({ url: `/api/posts/${component.postGroupId}/${component.$viewer.data('post-id')}`, method: 'delete' });\n\n component._changeHash('#list');\n\n component.postSnackbar().success('삭제되었습니다.');\n }\n catch(err)\n {\n\n }\n });\n });\n\n // 목록 설정\n if ( component.mode === CONSTANTS.MODES.POST )\n {\n extendsDataTableMethods();\n\n if ( !component.datatable )\n {\n let buttons = [];\n\n if ( component.allowedPermission.indexOf(CONSTANTS.PERMISSIONS.POST.WRITABLE) >= 0 )\n {\n buttons.push({\n text: '글쓰기',\n className: 'btn-sm btn-primary post-write',\n action: function ( e, dt, node, config ) {\n component._changeHash('#editor');\n }\n });\n }\n\n component.$table.on('init.dt', async function(){\n // 커스텀 페이지네이션 초기화\n component._initCustomPaginate();\n\n let searchDelay = 1000;\n\n $('div.dataTables_filter input')\n // 자동 검색 이벤트 제거\n .off('input keyup keyup.DT input.DT')\n // 엔터 이벤트로 추가\n .on('input', function() {\n var search = $('div.dataTables_filter input').val();\n\n clearTimeout(searchDelay);\n\n searchDelay = setTimeout(function() {\n if (search != null)\n component.datatable.search(search).draw();\n }, 1000);\n });\n });\n\n component.$table.on('page.dt', async function(){\n // 페이지네이션 업데이트\n component._refreshCustomPaginate();\n });\n\n component.$table.on('draw.dt', async function(){\n _.each(component.notices, function(notice){\n let colIndex = 0;\n let createdAt = moment(notice.createdAt);\n\n $('tbody', component.$table).prepend(`<tr class=\"post-notice\" data-post-id=\"${ notice._id }\">\n <td class=\"${component.columnsDefines[colIndex++].className} text-primary\"><i class=\"fas fa-bullhorn\"></i> 공지</td>\n <td class=\"${component.columnsDefines[colIndex++].className} text-primary font-weight-bold\">\n <div class=\"text-center text-md-left post-title-wrapper\">\n ${ $.fn.dataTable.render.ellipsis(40)($.fn.dataTable.render.text().display(notice.name)) }\n </div>\n </td>\n <td class=\"${component.columnsDefines[colIndex++].className}\">\n <a class=\"post-writer\" href=\"#\">\n ${ $.fn.dataTable.render.text().display(notice.author.name) }\n </a>\n </td>\n <td class=\"${component.columnsDefines[colIndex++].className}\">\n ${\n moment().diff(createdAt, 'days') === 0 ? `오늘 ${createdAt.format('HH:mm')}`\n : `<p class=\"m-0\">${createdAt.format('YYYY-MM-DD')}</p>`\n }\n </td>\n <td class=\"${component.columnsDefines[colIndex++].className}\">${ notice.viewcount }</td>\n</tr>`);\n });\n setTimeout(function(){ component.datatable.columns.adjust(); });\n component.datatable.processing(false);\n });\n\n let isShowSearchInput = component.$el.data('show-search-input');\n if ( isShowSearchInput == null ) isShowSearchInput = true;\n\n let isShowRowPerPage = component.$el.data('show-row-per-page');\n\n component.datatable = component.$table.DataTable({\n processing: true,\n serverSide: true,\n ordering: false,\n searchDelay: 1000,\n language: { url: '/libs/jquery-datatables/1.10.19/locales/ko.json' },\n paging: true,\n pagingType: 'simple_numbers',\n lengthMenu: [[ 10, 15, 25, 50, 75, 100 ], [ 10, 15, 25, 50, 75, 100 ]],\n pageLength: 15,\n rowHeight: 40,\n rowCallback: function( row, data ) {\n $(row).addClass( 'g-color-primary--hover' );\n },\n dom: `\n<'row no-gutters'<'col-sm-12 pl-2 mt-2 col-md-4'${ isShowRowPerPage ? 'l' : '' }><'col-sm-12 mt-2 col-md-4'${ isShowSearchInput ? 'f' : '' }><'col-sm-12 mt-2 col-md-4 pr-2'>>\n<'row no-gutters table-body-wrapper'<'col-sm-12'tr>>\n<'row no-gutters'<'col-sm-12 pb-2 col-md-${buttons.length > 0 ? 8 : 12 } g-mt-30'p>${ buttons.length > 0 ? `<'col-sm-12 pb-2 col-md-4 g-mt-30 pt-1 pr-2 text-right'B>` : ''}>`,\n buttons: buttons,\n columns: component.columnsDefines,\n ajax: $.fn.dataTable.pipeline({\n url: `/api/posts/${component.postGroupId}`,\n pages: 5, // number of pages to cache\n })\n });\n }\n\n component.$table.on('click', 'tbody tr', function($event){\n $event.preventDefault();\n $event.stopPropagation();\n\n let $col = $($event.target).closest('td');\n let $row = $col.closest('tr');\n let row = component.datatable.row($row).data();\n\n if ( $col.hasClass('attachment-preview') )\n {\n if ( component.allowedPermission.indexOf(CONSTANTS.PERMISSIONS.POST.FILE.DOWNLOADABLE) < 0 )\n component.postSnackbar().failed('다운로드 권한이 없습니다.');\n else\n window.open(`/api/posts/attachment/${$col.find('a.file-download').data('file-id')}`);\n\n return false;\n }\n\n if ( $row.hasClass('post-notice') ) row = { _id: $row.data('post-id') };\n if ( !row && !$row.hasClass('post-notice') ) return;\n\n // 사용자가 클릭했을 때만 이전 스크롤값 유지,\n // 뒤로가기, 앞으로 가기는 관여하지 않음.\n component.prevScrollTop = window.scrollY;\n\n component._changeHash(row._id);\n });\n\n // 에디터 설정\n // 에디터 메타데이터 입력 폼 추가\n component.$editorPostMetadata.append(component.$editorPostMetadataForm, component.$editorPostInputTitle);\n\n // 툴바 ID 임의화\n let toolbarId = $.HSCore.helpers.getRandomId();\n component.$editorToolbar.prop('id', toolbarId);\n\n let editorScrollContainerId = $.HSCore.helpers.getRandomId();\n component.$editorScrollContainer.prop('id', editorScrollContainerId);\n\n component.$postViewerTab.on('shown.bs.tab', async function(){\n // 스크롤 이동\n let offset = component.$el.offset();\n window.scrollTo(offset.left, offset.top);\n\n // 댓글 로드\n // component.$viewerControls.append();\n // component.datatable.fixedHeader.disable();\n });\n\n component.$postListTab.on('shown.bs.tab', async function(){\n // component.datatable.clearPipeline();\n // component.datatable.ajax.reload( null, false );\n // component.datatable.fixedHeader.enable();\n component.autoSwitchingCardStyle();\n\n if ( component.prevScrollTop != null ) window.scrollTo(0, component.prevScrollTop);\n });\n\n component.$postEditorTab.on('shown.bs.tab', async function(){\n window.scrollTo(0, component.$el.offset().top);\n\n let hash = component.getPostIdFromHash();\n if ( !component.editor )\n {\n if ( component.postGroup.features.indexOf(CONSTANTS.POST_GROUP.FEATURES.NOTICE) < 0\n || component.allowedPermission.indexOf(CONSTANTS.PERMISSIONS.POST.NOTICE_WRITABLE) < 0 )\n {\n component.$editorPostFormNotice.remove();\n }\n\n if ( component.postGroup.features.indexOf(CONSTANTS.POST_GROUP.FEATURES.ATTACHMENT) >= 0 )\n {\n if ( component.allowedPermission.indexOf(CONSTANTS.PERMISSIONS.POST.FILE.UPLOADABLE) >= 0 )\n {\n component.$editorPostFormHideAttachment.removeClass('d-none');\n component.$editorPostInputHideAttachment.on('change', function($event){\n let checked = $event.currentTarget.checked;\n\n if ( checked )\n {\n component.postConfirm().confirm('하단의 파일 목록은 계속 유지되지만 본문에 위치한 파일박스를 모두 잃게됩니다.\\n계속 진행하시겠습니까?', [`\"check-circle\" 네`, '아니오'], async function(buttonIndex) {\n if ( buttonIndex > 0 ) return $event.currentTarget.checked = false; // cancel\n _.each(component.editor.getLines(), function(line){\n if ( line instanceof AttachmentBlock ) line.remove();\n });\n });\n }\n else\n {\n component._appendFileBox(component.sendFileList);\n }\n });\n\n $(`#${toolbarId} .ql-video`).after(CONSTANTS.TEMPLATES.$TOOLBAR_BTN_FILE_UPLOAD);\n\n CONSTANTS.TEMPLATES.$BROWSE_FILE_INPUT.on('change', function($event){\n component._appendFileBox(this.files);\n\n // reset file input\n this.value = '';\n\n // _.each(files, function(file){\n // var reader = new FileReader();\n\n // reader.onload = function(e) {\n // var data = e.target.result;\n // };\n // if(rABS) reader.readAsBinaryString(f);\n // else reader.readAsArrayBuffer(f);\n // });\n });\n\n CONSTANTS.TEMPLATES.$TOOLBAR_BTN_FILE_UPLOAD.on('click', function(){\n CONSTANTS.TEMPLATES.$BROWSE_FILE_INPUT.click();\n });\n\n component.$editorAttachments.removeClass('d-none');\n }\n }\n\n component.editor = new Quill(component.$editor.get(0), {\n modules: {\n syntax: true,\n toolbar: `#${toolbarId}`,\n imageResize: {},\n imageDrop: {},\n },\n theme: 'snow',\n bounds: `#${editorScrollContainerId}`,\n scrollingContainer: `#${editorScrollContainerId}`,\n placeholder: config.placeholder,\n });\n }\n\n // 에디터 초기화\n component.editor.setContents([]);\n component.$editorPostInputTitle.val('');\n component.$editorPostInputNotice.prop('checked', false);\n component.$editorAttachmentList.empty();\n component.sendFileList = [];\n component.$editorControls.empty();\n\n // 수정 모드일 경우 저장된 데이터 로드\n if ( /^#editor-edit/.test(hash) )\n {\n let postId = hash.replace('#editor-edit', '');\n\n component.postEditForm = {\n _id: component.$viewer.data('post-id'),\n content: component.$viewer.data('post-content'),\n name: component.$viewer.data('post-name'),\n isnotice: component.$viewer.data('post-isnotice'),\n attachments: component.$viewer.data('post-attachments'),\n };\n\n // 이미 로드된 뷰어 값이 없다면 ajax를 통해 받아옴\n if ( !component.postEditForm._id )\n {\n try\n {\n component.postEditForm = await $.ajax(`/api/posts/${component.postGroupId}/${postId}`);\n if ( component.postEditForm ) component.postEditForm = component.postEditForm.doc;\n }\n catch(err)\n {\n return console.error(err);\n }\n }\n\n component.editor.setContents(component.postEditForm.content);\n component.$editorPostInputTitle.val(component.postEditForm.name);\n component.$editorPostInputNotice.prop('checked', component.postEditForm.isnotice);\n\n _.each(component.postEditForm.attachments, function(attachment, idx, src){\n component.$editorAttachmentList.append(`\n<li class=\"d-flex px-3 py-1 ${ idx === src.length - 1 ? 'pb-0' : ''} post-editor-attachment-list-item\" data-file-id=\"${attachment._id}\" data-is-saved=\"true\">\n <span class=\"my-auto ml-0\">${attachment.filename} ( ${component._getFileSize(attachment.length)} )</span>\n <i class=\"text-danger my-auto ml-auto remove-attachment fas fa-times\"></i>\n</li>`\n );\n });\n\n component.$editorControls.append(`\n<button class=\"cancel-edit btn btn-sm btn-danger\"><i class=\"fas fa-times\"></i> 취소</button>\n<button class=\"save-changes btn btn-sm btn-primary ml-auto\"><i class=\"fas fa-save\"></i> 변경 저장</button>`);\n }\n // 신규 작성 모드일 경우\n else\n {\n component.$editorControls.append(`\n<button class=\"cancel-edit btn btn-sm btn-danger\"><i class=\"fas fa-times\"></i> 취소</button>\n<button class=\"create-post btn btn-sm btn-primary ml-auto\"><i class=\"fas fa-save\"></i> 저장</button>`);\n }\n });\n\n // 에디터 툴바 설정\n\n // 에디터 기능 설정\n component.$editorAttachmentList.on('click', 'i.remove-attachment', async function($event){\n let $attachmentListItem = $($event.currentTarget).closest('.post-editor-attachment-list-item');\n\n if ( $attachmentListItem.data('is-saved') )\n {\n let index = _.findIndex(component.postEditForm.attachments, _.matches({ _id: $attachmentListItem.data('file-id') }));\n if ( index > -1 ) component.postEditForm.attachments.splice(index, 1);\n }\n else\n {\n component.sendFileList[$attachmentListItem.data('file-index')] = void(0);\n }\n\n $attachmentListItem.remove();\n });\n\n component.$editorControls.on('click', '.cancel-edit', async function($event){\n component._changeHash('#list');\n });\n\n component.$editorControls.on('click', '.save-changes', async function($event){\n try\n {\n component._showBlocker();\n\n let postData = {\n isnotice: component.$editorPostInputNotice.is(':visible') ? component.$editorPostInputNotice.prop('checked') : false,\n template: 'default',\n name: component.$editorPostInputTitle.val(),\n content: component.editor.getContents(),\n attachments: component.postEditForm.attachments,\n };\n\n // 데이터 유효성 체크\n if ( !postData.name )\n {\n component._hideBlocker();\n component.postSnackbar().warning('제목을 입력해주세요.');\n return component.$editorPostInputTitle.focus();\n }\n\n postData.attachments = _.map(postData.attachments, '_id');\n\n // 첨부파일 업로드\n let savedFileInfo = await component._fileUpload(component.postGroupId, component.postEditForm._id, component.sendFileList);\n if ( savedFileInfo && savedFileInfo.info && savedFileInfo.info.length > 0 )\n {\n // 델타내에 첨부파일들에게 매겨진 임시 ID를\n // 저장된 ID로 모두 변경\n postData.content = component._replaceDeltaFileID(postData.content, savedFileInfo.info);\n\n // 변경된 델타로 에디터 재설정\n component.editor.setContents(postData.content);\n\n // 저장된 파일목록을 기존 저장된 목록에 추가\n postData.attachments = postData.attachments.concat(_.map(savedFileInfo.info, '_id'));\n }\n // 파일 전송 목록 초기화\n component.sendFileList = [];\n\n component._showBlocker('글 등록 중...');\n // 글 저장\n await $.ajax({\n url: `/api/posts/${component.postGroupId}/${component.postEditForm._id}`, method: 'patch', headers: { 'Content-Type': 'application/json' },\n data: JSON.stringify(postData),\n });\n\n component._hideBlocker();\n component.postSnackbar().success('저장되었습니다.');\n\n // 저장된 글로 포스트 읽기로 자동 탭 이동\n component._changeHash(component.postEditForm._id);\n }\n catch(err)\n {\n component._hideBlocker();\n component.postSnackbar().failed(`저장에 실패하였습니다.: ${err.message}`);\n }\n });\n\n component.$editorControls.on('click', '.create-post', async function($event){\n try\n {\n component._showBlocker();\n\n let postData = {\n isnotice: component.$editorPostInputNotice.is(':visible') ? component.$editorPostInputNotice.prop('checked') : false,\n template: 'default',\n name: component.$editorPostInputTitle.val(),\n content: component.editor.getContents(),\n attachments: [],\n };\n\n // 데이터 유효성 체크\n if ( !postData.name )\n {\n component._hideBlocker();\n component.postSnackbar().warning('제목을 입력해주세요.');\n return component.$editorPostInputTitle.focus();\n }\n\n // 첨부파일 업로드\n let savedFileInfo = await component._fileUpload(component.postGroupId, component.sendFileList);\n\n if ( savedFileInfo && savedFileInfo.info && savedFileInfo.info.length > 0 )\n {\n // 델타내에 첨부파일들에게 매겨진 임시 ID를\n // 저장된 ID로 모두 변경\n postData._id = savedFileInfo.postId;\n postData.content = component._replaceDeltaFileID(postData.content, savedFileInfo.info);\n\n // 변경된 델타로 에디터 재설정\n component.editor.setContents(postData.content);\n\n // 저장된 파일목록을 별도 필드로 저장\n postData.attachments = _.map(savedFileInfo.info, '_id');\n\n // 파일 전송 목록 초기화\n component.sendFileList = [];\n }\n\n component._showBlocker('글 등록 중...');\n\n // 글 저장\n let createdPost = await $.ajax({\n url: `/api/posts/${component.postGroupId}`, method: 'post', headers: { 'Content-Type': 'application/json' },\n data: JSON.stringify(postData),\n });\n\n component._hideBlocker();\n component.postSnackbar().success('저장되었습니다.');\n\n // 1번 페이지로 이동\n component._changeHash('#list');\n component.datatable.clearPipeline().draw();\n component.datatable.page(component.datatable.page()).draw();\n\n // 저장된 글로 포스트 읽기로 자동 탭 이동\n // component.renderPost({ _id: createdPost._id, doc: createdPost, comments: [] });\n }\n catch(err) {\n component._hideBlocker();\n component.postSnackbar().failed(`저장에 실패하였습니다.: ${err.message}`);\n }\n });\n }\n else\n {\n component.$table.before(CONSTANTS.$RECENT_POST_LIST).remove();\n component.$title.text(`${component.$title.text()} - 최근글`);\n CONSTANTS.$RECENT_POST_LIST.on('click', 'li.recent-post-list-item', component._viewPost);\n\n component._readRecentPosts();\n }\n\n // 초기화\n $(window).on('resize', _.debounce(component.autoSwitchingCardStyle, 150));\n $(window).on('hashchange', async function(){\n let postId = component.getPostIdFromHash();\n\n if ( !postId || postId === '#list' ) return component.$postListTab.tab('show');\n if ( /^#editor/.test(postId) ) return component.$postEditorTab.tab('show');\n\n try\n {\n let $post = await component.getPost(component.postGroupId, postId);\n if ( $post ) return component.renderPost($post);\n component.postSnackbar().failed('존재하지 않는 글입니다.');\n }\n catch(err)\n {\n component.postSnackbar().failed(err.message);\n }\n });\n\n $(window).trigger('resize');\n\n // 초기화 완료 컴포넌트 초기화 로딩 스피너 숨김.\n component.$initSpinner.addClass('d-none').removeClass('d-flex');\n\n // load post via url hash\n let postId = component.getPostIdFromHash();\n if ( !postId ) return;\n\n if ( postId === '#list' ) ;\n else if ( /^#editor/.test(postId) )\n component.$postEditorTab.tab('show');\n else\n component.getPost(component.postGroupId, postId)\n .then(component.renderPost)\n .catch(function(err){\n component.postSnackbar().failed(err.message);\n });\n };\n\n component._hideProgress = function(){\n component.$progress.addClass('d-none');\n };\n\n component._showProgress = function(){\n component.$progressBar.attr('aria-valuenow', 0).width(0);\n component.$progress.width(component.$blocker.width() * 85 / 100).removeClass('d-none');\n };\n\n component._hideBlocker = function(){\n component.$blockerMessage.addClass('d-none');\n component.$blocker.addClass('d-none');\n };\n\n component._showBlocker = function(message){\n let offset = component.$el.offset();\n component.$blocker.css({\n top: offset.top,\n left: offset.left,\n width: component.$el.width(),\n height: component.$el.height(),\n });\n\n component.$blocker.removeClass('d-none');\n\n if ( message )\n {\n component.$blockerMessageTxt.text(message);\n component.$blockerMessage.removeClass('d-none');\n }\n };\n\n component._replaceDeltaFileID = function(delta, savedFileInfo){\n _.each(savedFileInfo, function(info){\n delta.forEach(function(operation){\n if ( operation.insert && operation.insert.attachment && operation.insert.attachment === 'attachment' )\n {\n if ( operation.attributes['data-file-id'] === info.fileId )\n {\n operation.attributes['data-file-id'] = info._id;\n }\n }\n });\n });\n\n return delta;\n };\n\n component._appendFileBox = function(files){\n let selection = component.editor.getSelection();\n let index = 0;\n\n if ( selection ) index = selection.index;\n if ( index === 0 ) component.editor.insertText(index++, '\\n');\n\n _.each(files, function(file){\n let isExists = file.fileId ? true : false;\n let fileInstance = isExists ? file.file : file;\n\n let randomId = file.fileId || $.HSCore.helpers.getRandomId();\n\n component.editor.insertEmbed(index++, 'attachment', { fileId: randomId, file: fileInstance });\n component.editor.insertText(index++, '\\n');\n\n if ( ! isExists )\n {\n let sameFileIndex = _.findIndex( component.sendFileList, function(f){\n return f.file.lastModified === fileInstance.lastModified && f.file.lastModifiedDate === fileInstance.lastModifiedDate && f.file.name === fileInstance.name && f.file.size === fileInstance.size && f.file.type === fileInstance.type;\n });\n\n if ( sameFileIndex < 0 )\n {\n component.$editorAttachmentList.append(`\n<li class=\"d-flex px-3 py-1 post-editor-attachment-list-item\" data-file-id=\"${randomId}\" data-file-index=\"${component.sendFileList.length}\">\n <span class=\"my-auto ml-0\">${fileInstance.name} ( ${component._getFileSize(file.size)} )</span>\n <i class=\"text-danger my-auto ml-auto remove-attachment fas fa-times\"></i>\n</li>`\n );\n\n component.sendFileList.push({ fileId: randomId, file: fileInstance });\n }\n }\n });\n // move after attachments\n component.editor.setSelection(index + 1, 0);\n };\n\n component._refreshCustomPaginate = function(){\n CONSTANTS.TEMPLATES.DATATABLE.$PAGINATE.empty();\n\n let pagination = component.datatable.page.info();\n\n let per = 3;\n\n let current = pagination.page;\n\n CONSTANTS.TEMPLATES.DATATABLE.$PAGINATE.append(`\n<li class=\"list-inline-item\">\n <a class=\"u-pagination-v1__item u-pagination-v1-3 g-pa-4-13 to-previous ${ current === 0 ? 'u-pagination-v1__item--disabled' : '' }\" href=\"javascript:void(0)\" aria-label=\"이전\">\n <span aria-hidden=\"true\">\n <i class=\"fa fa-angle-left g-mr-5\"></i>\n 이전\n </span>\n <span class=\"sr-only\">이전</span>\n </a>\n</li>`);\n\n if ( current - 1 > 0 )\n {\n CONSTANTS.TEMPLATES.DATATABLE.$PAGINATE.append(`\n<li class=\"list-inline-item g-hidden-sm-down\">\n <a class=\"u-pagination-v1__item u-pagination-v1-3 g-pa-4-11 to-page\" href=\"javascript:void(0)\">${1}</a>\n</li>\n<li class=\"list-inline-item g-hidden-sm-down\">\n <span class=\"g-pa-4-11\">...</span>\n</li>`\n );\n }\n\n for ( let i = current - 1, ilen = current - 1 + per; i < ilen; i++ )\n {\n if ( i < 0 ) { ilen += 1; continue; }\n if ( i >= pagination.pages ) { break; }\n\n CONSTANTS.TEMPLATES.DATATABLE.$PAGINATE.append(`\n<li class=\"list-inline-item g-hidden-sm-down\">\n <a class=\"u-pagination-v1__item u-pagination-v1-3 g-pa-4-11 to-page ${ current === i ? 'u-pagination-v1-3--active' : '' }\" href=\"javascript:void(0)\">${i + 1}</a>\n</li>`);\n }\n\n if ( current - 1 + per <= pagination.pages - 1 )\n {\n CONSTANTS.TEMPLATES.DATATABLE.$PAGINATE.append(`\n<li class=\"list-inline-item g-hidden-sm-down\">\n <span class=\"g-pa-4-11\">...</span>\n</li>\n<li class=\"list-inline-item g-hidden-sm-down\">\n <a class=\"u-pagination-v1__item u-pagination-v1-3 g-pa-4-11 to-page\" href=\"javascript:void(0)\">${pagination.pages}</a>\n</li>`\n );\n }\n\n CONSTANTS.TEMPLATES.DATATABLE.$PAGINATE.append(`\n<li class=\"list-inline-item\">\n <a class=\"u-pagination-v1__item u-pagination-v1-3 g-pa-4-13 to-next ${ current >= pagination.pages - 1 ? 'u-pagination-v1__item--disabled' : '' }\" href=\"javascript:void(0)\" aria-label=\"다음\">\n <span aria-hidden=\"true\">\n 다음\n <i class=\"fa fa-angle-right g-ml-5\"></i>\n </span>\n <span class=\"sr-only\">다음</span>\n </a>\n</li>`);\n };\n\n component._initCustomPaginate = function(){\n component.$originalPaginate = $('.dataTables_paginate', component.$el);\n\n component.$originalPaginate.addClass('d-none');\n component.$originalPaginate.after(CONSTANTS.TEMPLATES.DATATABLE.$PAGINATE);\n\n CONSTANTS.TEMPLATES.DATATABLE.$PAGINATE.on('click', '.to-page', async function($event){\n component.datatable.page(Number($event.currentTarget.textContent) - 1).draw('page');\n });\n\n CONSTANTS.TEMPLATES.DATATABLE.$PAGINATE.on('click', '.to-next', async function($event){\n component.datatable.page('next').draw('page');\n\n if ( !component.$el.hasClass('cards-table') ) return;\n let componentOffset = component.$el.offset();\n window.scrollTo(componentOffset.left, componentOffset.top)\n });\n\n CONSTANTS.TEMPLATES.DATATABLE.$PAGINATE.on('click', '.to-previous', async function($event){\n component.datatable.page('previous').draw('page');\n\n if ( !component.$el.hasClass('cards-table') ) return;\n let componentOffset = component.$el.offset();\n window.scrollTo(componentOffset.left, componentOffset.top)\n });\n\n component._refreshCustomPaginate();\n };\n\n component._fileUpload = async function(postGroupId, postId, files){\n if ( postId instanceof Array )\n {\n files = postId;\n postId = void(0);\n }\n\n // 첨부파일 업로드\n if ( _.filter(files, Boolean).length > 0 )\n {\n let formData = new FormData();\n\n _.each(files, function(f){\n formData.append('file-id', f.fileId);\n formData.append('files', f.file);\n });\n\n component._showBlocker('파일 업로드 중...');\n component._showProgress();\n return await $.ajax({\n url: `/api/posts/${postGroupId}/${ postId ? postId + '/' : '' }attachment`, method: 'post',\n contentType: false, processData: false, data: formData,\n xhr: function() {\n var xhr = $.ajaxSettings.xhr();\n if ( xhr )\n {\n xhr.upload.addEventListener('progress', function(event){\n if ( !event.lengthComputable ) return;\n\n let percentComplete = Math.round( (event.loaded * 100) / event.total );\n\n component.$progressBar.attr('aria-valuenow', percentComplete).width(`${percentComplete}%`);\n }, false);\n }\n return xhr;\n },\n });\n }\n\n return [];\n };\n\n component.getPostIdFromHash = function(){\n let hash = location.hash.substr(1);\n\n if ( !hash ) return null;\n\n hash = hash.split('&');\n\n for ( let i = 0, ilen = hash.length; i < ilen; i++ )\n if ( hash[i].split('=')[0] === component.instantID )\n return decodeURIComponent(hash[i].split('=')[1]);\n\n return null;\n };\n\n component.getPost = async function(postGroupId, postId){\n if ( !postGroupId ) throw new Error('\"Post Group ID\" is required.');\n if ( !postId ) throw new Error('\"Post ID\" is required.');\n\n try\n {\n return await $.ajax(`/api/posts/${postGroupId}/${postId}`);\n }\n catch(err)\n {\n throw new Error(component.helpers.parseErrorMessage(err));\n }\n };\n\n component.autoSwitchingCardStyle = async function(){\n // 카드 클래스 토글링\n component.$el.toggleClass('cards-table', window.innerWidth < 768);\n // 컬럼 사이즈 재계산\n setTimeout(function(){ component.datatable.columns.adjust(); });\n };\n\n component._viewPost = async function($event){\n $event.preventDefault();\n $event.stopPropagation();\n\n let $post;\n\n try\n {\n if ( component.mode === CONSTANTS.MODES.POST )\n {\n let row = component.datatable.row($(this).closest('tr')).data();\n if ( !row ) return;\n\n $post = await component.getPost(component.postGroupId, row._id);\n }\n else if ( component.mode === CONSTANTS.MODES.RECENT )\n {\n $post = await component.getPost(component.postGroupId, $(this).data('post-id'));\n }\n\n component.renderPost($post);\n }\n catch(err)\n {\n component.postSnackbar().failed(err.message);\n }\n return false;\n };\n\n component._readRecentPosts = async function (){\n let posts = await $.ajax(`/api/posts/${component.postGroupId}/recent/5`);\n let $postListItems = _.map(posts, function(post){\n return $(`<li class=\"d-flex recent-post-list-item\" data-post-id=\"${post._id}\"><a href=\"#\" class=\"post-title my-auto\">${post.name}</a><span class=\"my-auto ml-auto\">${moment(post.createdAt).format('YYYY-MM-DD')}</span></li>`);\n });\n CONSTANTS.$RECENT_POST_LIST.append($postListItems);\n };\n\n // ╔═╗╦═╗╦╦ ╦╔═╗╔╦╗╔═╗ ╔╦╗╔═╗╔╦╗╦ ╦╔═╗╔╦╗╔═╗\n // ╠═╝╠╦╝║╚╗╔╝╠═╣ ║ ║╣ ║║║║╣ ║ ╠═╣║ ║ ║║╚═╗\n // ╩ ╩╚═╩ ╚╝ ╩ ╩ ╩ ╚═╝ ╩ ╩╚═╝ ╩ ╩ ╩╚═╝═╩╝╚═╝o\n // ────────────────────────────────────────────\n // removeHashWithoutReload()\n // extendsDataTableMethods()\n // ────────────────────────────────────────────\n function removeHashWithoutReload () {\n var scrollV, scrollH, loc = window.location;\n if ( _.indexOf(history, 'pushState') < 0 )\n {\n let componentOffset = component.$el.offset();\n loc.hash = '';\n\n // Prevent scrolling by storing the page's current scroll offset\n // Restore the scroll offset, should be flicker free\n window.scrollTo(componentOffset.left, componentOffset.top);\n }\n else\n {\n history.pushState('', document.title, loc.pathname + loc.search);\n }\n }\n\n function extendsDataTableMethods(){\n if ( $.isFunction($.fn.dataTable.pipeline) ) return;\n //\n // https://datatables.net/examples/server_side/pipeline.html\n // Pipelining function for DataTables. To be used to the `ajax` option of DataTables\n //\n $.fn.dataTable.pipeline = function ( opts ) {\n // Configuration options\n let conf = $.extend( {\n error: $.noop,\n pages: 5, // number of pages to cache\n url: '', // script url\n data: null, // function or object with parameters to send to the server\n // matching how `ajax.data` works in DataTables\n method: 'GET' // Ajax HTTP method\n }, opts );\n\n // Private variables for storing the cache\n let cacheLower = -1;\n let cacheUpper = null;\n let cacheLastRequest = null;\n let cacheLastJson = null;\n\n return function ( request, drawCallback, settings ){\n let _datatable = this.api();\n let ajax = false;\n let requestStart = request.start;\n let drawStart = request.start;\n let requestLength = request.length;\n let requestEnd = requestStart + requestLength;\n let info = _datatable.page.info();\n\n if ( settings.clearCache )\n {\n // API requested that the cache be cleared\n ajax = true;\n settings.clearCache = false;\n }\n else if ( cacheLower < 0 || requestStart < cacheLower || requestEnd > cacheUpper )\n {\n // outside cached data - need to make a request\n ajax = true;\n }\n else if (\n JSON.stringify( request.order ) !== JSON.stringify( cacheLastRequest.order )\n || JSON.stringify( request.columns ) !== JSON.stringify( cacheLastRequest.columns )\n || JSON.stringify( request.search ) !== JSON.stringify( cacheLastRequest.search )\n ) {\n // properties changed (ordering, columns, searching)\n ajax = true;\n }\n\n // Store the request for checking next time around\n cacheLastRequest = $.extend( true, {}, request );\n\n if ( ajax )\n {\n // Need data from the server\n if ( requestStart < cacheLower )\n {\n requestStart = requestStart - (requestLength*(conf.pages-1));\n\n if ( requestStart < 0 ) requestStart = 0;\n }\n\n cacheLower = requestStart;\n cacheUpper = requestStart + (requestLength * conf.pages);\n\n request.start = requestStart;\n request.length = requestLength * conf.pages;\n\n // Provide the same `data` options as DataTables.\n if ( typeof conf.data === 'function' )\n {\n // As a function it is executed with the data object as an arg\n // for manipulation. If an object is returned, it is used as the\n // data object to submit\n let d = conf.data( request );\n if ( d ) $.extend( request, d );\n }\n else if ( $.isPlainObject( conf.data ) )\n {\n // As an object, the data given extends the default\n $.extend( request, conf.data );\n }\n\n _datatable.processing(true);\n\n settings.jqXHR = $.ajax({\n type: conf.method,\n url: conf.url,\n data: request,\n dataType: 'json',\n cache: false,\n success: function ( json ) {\n cacheLastJson = $.extend(true, {}, json);\n\n if ( cacheLower != drawStart ) json.data.splice( 0, drawStart-cacheLower );\n if ( requestLength >= -1 ) json.data.splice( requestLength, json.data.length );\n\n drawCallback( json );\n\n // pipeline으로 로드할 경우, length를 잃어버리는 현상이 존재함.\n // DOM 로드가 완료된 후에 재설정하여 length가 표시되도록 하기 위함.\n setTimeout(function(){ _datatable.page.len(_datatable.page.len()); }, 100);\n },\n error: conf.error,\n });\n }\n else\n {\n json = $.extend( true, {}, cacheLastJson );\n json.draw = request.draw; // Update the echo for each response\n json.data.splice( 0, requestStart-cacheLower );\n json.data.splice( requestLength, json.data.length );\n\n drawCallback(json);\n }\n }\n };\n\n // Register an API method that will empty the pipelined data, forcing an Ajax\n // fetch on the next draw (i.e. `table.clearPipeline().draw()`)\n $.fn.dataTable.Api.register( 'clearPipeline()', function () {\n return this.iterator( 'table', function ( settings ) {\n settings.clearCache = true;\n });\n });\n };\n\n // ╔═╗╦ ╦╔╗ ╦ ╦╔═╗ ╔╦╗╔═╗╔╦╗╦ ╦╔═╗╔╦╗╔═╗\n // ╠═╝║ ║╠╩╗║ ║║ ║║║║╣ ║ ╠═╣║ ║ ║║╚═╗\n // ╩ ╚═╝╚═╝╩═╝╩╚═╝ ╩ ╩╚═╝ ╩ ╩ ╩╚═╝═╩╝╚═╝o\n // ────────────────────────────────────────\n // component.renderPost(Post post)\n // component.unescapeHtml(String string)\n // ────────────────────────────────────────\n\n component.unescapeHtml = function(string) {\n let str = '' + string;\n return str.replace(/&gt;/g, `>`)\n .replace(/&lt;/g, `<`)\n .replace(/&#39;/g, `'`)\n .replace(/&quot;/g, `\"`)\n .replace(/&amp;/g, `&`);\n };\n\n component.renderPost = function($post){\n let post = $post.doc;\n let comments = $post.comments;\n\n component.$viewer.data('post-id', post._id);\n component.$viewer.data('post-name', post.name);\n component.$viewer.data('post-content', post.content);\n component.$viewer.data('post-isnotice', post.isnotice);\n component.$viewer.data('post-attachments', post.attachments);\n\n let tabNavText = `글읽기 - ${post.name}`;\n\n component.$postViewerTab.attr('title', tabNavText).text(tabNavText).tab('show');\n\n if ( !component.viewer )\n {\n let viewerScrollConatinerId = $.HSCore.helpers.getRandomId();\n component.$viewerScrollContainer.prop('id', viewerScrollConatinerId);\n\n component.viewer = new Quill(component.$viewer.get(0), {\n modules: {\n syntax: true,\n toolbar: false,\n },\n theme: 'snow',\n bounds: `#${viewerScrollConatinerId}`,\n scrollingContainer: `#${viewerScrollConatinerId}`,\n readOnly: true,\n });\n }\n\n // 뷰어 레이아웃\n // 글 메타데이터 공간\n component.$viewerMetadata.empty().append(`\n<div class=\"post-general-info row no-gutters\">\n <div class=\"metadata-group col-sm-12 col-md-4 d-flex\">\n <div class=\"p-3 m-0 w-100 g-brd-right-0 g-brd-right-1--md border-gray border-bottom border-right text-center font-weight-bold d-flex\">\n <label class=\"mx-3 mx-md-5 my-auto text-nowrap\">작성자</label>\n <span class=\"m-auto\">${post.author.name}</span>\n </div>\n </div>\n <div class=\"metadata-group col-sm-12 col-md-4 d-flex\">\n <div class=\"p-3 m-0 w-100 g-brd-right-0 g-brd-right-1--md border-gray border-bottom border-right text-center font-weight-bold d-flex\">\n <label class=\"mx-3 mx-md-5 my-auto text-nowrap\">작성일</label>\n <span class=\"m-auto\">${moment(post.createdAt).format('YYYY-MM-DD HH:mm:ss')}</span>\n </div>\n </div>\n <div class=\"metadata-group col-sm-12 col-md-4 d-flex\">\n <div class=\"p-3 m-0 w-100 g-brd-right-0 g-brd-right-1--md border-gray border-bottom text-center font-weight-bold d-flex\">\n <label class=\"mx-3 mx-md-5 my-auto text-nowrap\">조회수</label>\n <span class=\"m-auto\">${post.viewcount}</span>\n </div>\n </div>\n</div>\n<div class=\"post-detail-info no-gutters\">\n</div>\n<div class=\"post-title-info no-gutters\">\n <div class=\"metadata-group col-12 d-flex\">\n <div class=\"p-3 m-0 w-100 g-brd-right-0 g-brd-right-1--md border-gray border-bottom text-center font-weight-bold d-flex\">\n <label class=\"mx-3 mx-md-5 my-auto text-nowrap\">제목</label>\n <span class=\"m-auto text-break\">${post.name}</span>\n </div>\n </div>\n</div>`);\n\n let $attachmentItems = $('<li class=\"d-flex py-1 px-3 post-viewer-attachment-list-item\"></li>');\n\n if ( post.attachments.length === 0 )\n $attachmentItems.text('첨부파일이 없습니다.');\n else\n $attachmentItems = _.map(post.attachments, function(attachment, idx, src){\n return `\n<li class=\"d-flex px-3 py-1 ${ idx === src.length - 1 ? 'pb-0' : ''} post-viewer-attachment-list-item\" data-file-id=\"${attachment._id}\">\n <a href=\"javascript:void(0)\" class=\"post-viewer-attachment-download\">${ attachment.filename } ( ${ component._getFileSize(attachment.length) } )</a>\n</li>`;\n }).join('');\n\n // 첨부파일 공간\n component.$viewerAttachmentList.empty().append($attachmentItems);\n\n // 첨부파일 아이템 다운로드 버튼 이벤트\n $('a.post-viewer-attachment-download', component.$viewerAttachmentList).on('click', async function($event){\n $event.preventDefault();\n $event.stopPropagation();\n\n if ( component.allowedPermission.indexOf(CONSTANTS.PERMISSIONS.POST.FILE.DOWNLOADABLE) < 0 )\n component.postSnackbar().failed('다운로드 권한이 없습니다.');\n else\n window.open(`/api/posts/attachment/${$(this).closest('.post-viewer-attachment-list-item').data('file-id')}`);\n\n return false;\n });\n\n // 본문 로드\n component.viewer.setContents(post.content);\n\n // 첨부파일 다운로드 이벤트\n $('.ql-editor .attachment .attachment-inner', component.$viewer).on('click', async function(){\n if ( component.allowedPermission.indexOf(CONSTANTS.PERMISSIONS.POST.FILE.DOWNLOADABLE) < 0 )\n return component.postSnackbar().failed('다운로드 권한이 없습니다.');\n\n window.open(`/api/posts/attachment/${$(this).closest('.attachment').data('file-id')}`);\n });\n\n // 수정, 삭제 버튼\n // 자신이 쓴 글만 가능\n component.$viewerControls.empty().append(`<button class=\"btn btn-sm btn-secondary to-list ml-2\"><i class=\"fas fa-list-ul\"></i> 목록</button>`);\n\n if ( post.author._id === TOTAL_LOCALS._SESSION._id )\n {\n component.$viewerControls.append(`\n<button class=\"btn btn-sm btn-danger remove-post ml-auto\"><i class=\"fas fa-trash-alt\"></i> 글 삭제</button>\n<button class=\"btn btn-sm btn-info edit-post mx-2\"><i class=\"fas fa-edit\"></i> 글 수정</button>`);\n }\n\n // 게시판 댓글기능 껐을 경우\n if ( component.postGroup.features.indexOf(CONSTANTS.POST_GROUP.FEATURES.COMMENT) < 0 )\n {\n component.$viewerComments.addClass('d-none');\n }\n // 댓글 로드\n else\n {\n component.$viewerCommentList.empty();\n\n let $commentItems = $('<p class=\"d-flex m-0 py-1 px-2\"></p>');\n\n // 사용자 읽기 권한 체크\n if ( component.allowedPermission.indexOf(CONSTANTS.PERMISSIONS.POST.COMMENT.READABLE) >= 0 )\n {\n if ( post.attachments.length === 0 )\n $commentItems.text('등록된 댓글이 없습니다.');\n else\n $commentItems =\n _.map(_.filter($post.comments, function(comment){ return !_.isEmpty(comment); }), function(comment){\n // 사용자 프로필에 사용된 기본값 svg는 font-awesome `fas fa-user`\n return `\n<div class=\"media g-mb-30\">\n <img class=\"d-flex g-width-50 g-height-50 rounded-circle g-mt-3 g-mr-20 p-1 border\" src=\"${post.author.photo ? post.author.photo : 'data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhcyIgZGF0YS1pY29uPSJ1c2VyIiByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDQ0OCA1MTIiIGNsYXNzPSJzdmctaW5saW5lLS1mYSBmYS11c2VyIGZhLXctMTQgZmEtM3giPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTIyNCAyNTZjNzAuNyAwIDEyOC01Ny4zIDEyOC0xMjhTMjk0LjcgMCAyMjQgMCA5NiA1Ny4zIDk2IDEyOHM1Ny4zIDEyOCAxMjggMTI4em04OS42IDMyaC0xNi43Yy0yMi4yIDEwLjItNDYuOSAxNi03Mi45IDE2cy01MC42LTUuOC03Mi45LTE2aC0xNi43QzYwLjIgMjg4IDAgMzQ4LjIgMCA0MjIuNFY0NjRjMCAyNi41IDIxLjUgNDggNDggNDhoMzUyYzI2LjUgMCA0OC0yMS41IDQ4LTQ4di00MS42YzAtNzQuMi02MC4yLTEzNC40LTEzNC40LTEzNC40eiIgY2xhc3M9IiI+PC9wYXRoPjwvc3ZnPgo=' }\" alt=\"Image Description\">\n <div class=\"media-body g-brd-around g-brd-gray-light-v4 g-pa-30 mb-3\">\n <div class=\"g-mb-15\">\n <h5 class=\"d-md-flex align-items-center h5 g-color-gray-dark-v1 mb-0\">\n <span class=\"d-block\">${$.fn.dataTable.render.text().display(comment.author.name)}</span>\n ${ post.author._id === comment.author._id ? '<span class=\"u-label g-bg-info g-rounded-3 u-label-info d-block ml-auto mt-2 mt-md-0\">글쓴이</span>' : '' }\n ${ TOTAL_LOCALS._SESSION._id === comment.author._id ? '<a class=\"u-tags-v1 g-font-size-12 g-brd-around g-brd-gray-light-v4 g-bg-red--hover g-brd-red--hover g-color-black-opacity-0_8 g-color-white--hover rounded g-py-6 g-px-15 ml-md-2 mt-2 mt-md-0 d-block text-center\" href=\"#!\">댓글 삭제</a>' : '' }\n </h5>\n <span class=\"g-color-gray-dark-v4 g-font-size-12\">${moment(comment.createdAt).format('YYYY-MM-DD HH:mm:ss')}</span>\n </div>\n\n <p>${$.fn.dataTable.render.text().display(comment.comment)}</p>\n </div>\n</div>`;\n });\n\n }\n else\n {\n $commentItems.text('댓글을 읽을 권한이 없습니다.');\n }\n\n component.$viewerCommentList.append($commentItems);\n // 댓글 입력 폼 초기화\n component.$viewerCommentForm.empty();\n\n // 사용자 쓰기 권한 체크\n if ( component.allowedPermission.indexOf(CONSTANTS.PERMISSIONS.POST.COMMENT.WRITABLE) >= 0 )\n {\n component.$viewerCommentForm.append(`\n <div class=\"mb-2\">\n <textarea class=\"form-control g-bg-secondary g-brd-gray-light-v4 g-brd-primary--focus g-resize-none rounded-3 g-py-13 g-px-15 input-comment\" rows=\"5\" placeholder=\"댓글을 입력해주세요...\"></textarea>\n </div>\n\n <div class=\"d-flex align-items-center\">\n <button class=\"btn u-btn-primary g-font-weight-600 g-font-size-12 text-uppercase my-auto ml-auto create-comment\" type=\"button\" role=\"button\">댓글 등록</button>\n </div>`);\n\n $('button.create-comment', component.$viewerCommentForm).on('click', async function($event){\n let $inputComment = $('textarea.input-comment', component.$viewerCommentForm);\n let commentData = {\n comment: $inputComment.val(),\n };\n\n // 데이터 유효성 검사\n if ( !commentData.comment )\n {\n component.postSnackbar().failed('댓글을 입력해주세요.');\n return $inputComment.focus();\n }\n\n try\n {\n let createdComment = await $.ajax({\n url: `/api/posts/${component.postGroupId}/${post._id}/comments`, method: 'post', headers: { 'Content-Type': 'application/json' },\n data: JSON.stringify(commentData),\n });\n\n component.postSnackbar().success('등록되었습니다.');\n }\n catch(err)\n {\n\n }\n });\n }\n else\n {\n component.$viewerCommentForm.addClass('d-none');\n }\n }\n };\n\n component._changeHash = function(postId){\n let hash = location.hash;\n\n if ( !hash || hash === '#!' ) return location.hash = `${component.instantID}=${encodeURIComponent(postId)}`;\n\n let appended = false;\n\n hash = hash.substr(1).split('&');\n\n for ( let i = 0, ilen = hash.length; i < ilen; i++ )\n {\n if ( hash[i].split('=')[0] === component.instantID )\n {\n hash[i] = `${component.instantID}=${encodeURIComponent(postId)}`;\n appended = true;\n }\n }\n\n if ( !appended ) hash.push(`${component.instantID}=${encodeURIComponent(postId)}`);\n\n location.hash = hash.join('&');\n };\n }, [\n '/libs/font-awesome/5.9.0/css/all.min.css',\n\n '/libs/jquery-datatables/1.10.19/css/dataTables.bootstrap4.min.css',\n '/libs/jquery-datatables/1.10.19/js/jquery.dataTables.min.js',\n '/libs/jquery-datatables/1.10.19/js/dataTables.bootstrap4.min.js',\n\n '/libs/jquery-datatables-buttons/1.5.4/css/buttons.bootstrap4.min.css',\n '/libs/jquery-datatables-buttons/1.5.4/js/dataTables.buttons.min.js',\n '/libs/jquery-datatables-buttons/1.5.4/js/buttons.bootstrap4.min.js',\n\n '/libs/jquery-datatables-renderer-ellipsis/1.10.19/ellipsis.js',\n '/libs/jquery-datatables-processing/1.10.19/processing.js',\n\n '/libs/KaTeX/0.10.0/katex.min.css',\n '/libs/KaTeX/0.10.0/katex.min.js',\n\n '/libs/highlight.js/9.14.2/styles/atom-one-dark.min.css',\n '/libs/highlight.js/9.14.2/highlight.min.js',\n\n '/libs/quill/1.3.6/quill.snow.min.css',\n '/libs/quill/1.3.6/quill.min.js',\n '/libs/quill-image-drop-module/1.0.3/image-drop.min.js',\n '/libs/quill-image-resize-module/3.0.0/image-resize.min.js',\n ]);\n</script>\n\n<!-- Hover Rows -->\n<div class=\"g-mb-30 basics-main-bbs-v1 CMS_type_post\" data-jc=\"basics-main-bbs-v1\">\n <h2 class=\"g-font-size-24 p-3 mb-0 basics-main-bbs-v1-title\">\n <i class=\"fa fa-comments-o g-mr-5\"></i>\n <span class=\"post-group-name\">게시판</span>\n </h2>\n\n <div class=\"card-header d-none\">\n <ul class=\"nav nav-tabs card-header-tabs\">\n <li class=\"nav-item\">\n <a class=\"nav-link tab-post-list active\" id=\"post-list-tab\" data-toggle=\"tab\" href=\"#post-list\" role=\"tab\" aria-controls=\"post-list\" aria-selected=\"true\">글목록</a>\n </li>\n <li class=\"nav-item\">\n <a class=\"nav-link tab-post-read\" id=\"post-read\" data-toggle=\"tab\" href=\"#post-read\" role=\"tab\" aria-controls=\"post-read\" aria-selected=\"true\">글읽기</a>\n </li>\n <li class=\"nav-item\">\n <a class=\"nav-link tab-post-editor\" id=\"post-write-tab\" data-toggle=\"tab\" href=\"#post-write\" role=\"tab\" aria-controls=\"post-write\" aria-selected=\"false\">글쓰기</a>\n </li>\n </ul>\n </div>\n\n <div class=\"p-0 card-body tab-content\">\n <div class=\"d-flex p-3 init-spinner text-center CMS_hidden\">\n <div class=\"spinner-icon g-width-80 my-auto ml-auto\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"xMidYMid\"><path fill=\"none\" d=\"M82 50A32 32 0 1 1 23.533421623214014 32.01333190873183 L21.71572875253809 21.7157287525381 L32.013331908731814 23.53342162321403 A32 32 0 0 1 82 50\" stroke-width=\"4\" stroke=\"#337ab7\"></path><circle cx=\"50\" cy=\"50\" fill=\"none\" stroke-linecap=\"round\" r=\"22\" stroke-width=\"4\" stroke=\"#337ab7\" stroke-dasharray=\"34.55751918948772 34.55751918948772\" transform=\"rotate(215.429 50 50)\"><animateTransform attributeName=\"transform\" type=\"rotate\" calcMode=\"linear\" values=\"0 50 50;360 50 50\" keyTimes=\"0;1\" dur=\"1s\" begin=\"0s\" repeatCount=\"indefinite\"></animateTransform></circle></svg>\n </div>\n <span class=\"my-auto ml-3 mr-auto g-font-size-28\">글 불러오는 중...</span>\n </div>\n\n <div class=\"post-list-tab-content tab-pane fade show active rounded\" id=\"post-list\" role=\"tabpanel\" aria-labelledby=\"post-list-tab\">\n <table class=\"table u-table--v2 table-striped text-center g-mb-50 post-list\" style=\"width:100%\"></table>\n </div>\n\n <div class=\"post-read-content tab-pane fade rounded border border-gray\" id=\"post-read\" role=\"tabpanel\" aria-labelledby=\"post-read\">\n <div class=\"post-viewer-metadata\"></div>\n <div class=\"post-viewer-scroll-container CMS_hidden\">\n <div class=\"post-viewer\"></div>\n </div>\n\n <div class=\"border-top border-bottom post-viewer-attachments\">\n <h6 class=\"m-0 p-2\"><i class=\"fas fa-paperclip\"></i> 첨부파일</h6>\n <ul class=\"m-0 px-0 py-2 post-viewer-attachment-list\"></ul>\n </div>\n\n <div class=\"post-viewer-controls border-bottom p-2 d-flex CMS_hidden\"></div>\n\n <div class=\"post-viewer-comments\">\n <h6 class=\"m-0 p-2\"><i class=\"fas fa-comments\"></i> 댓글</h6>\n <div class=\"m-0 py-2 px-4 border-bottom post-viewer-comment-list\"></div>\n <div class=\"m-0 p-2 post-viewer-comment-form\"></div>\n </div>\n </div>\n\n <div class=\"post-editor-tab-content tab-pane fade rounded border border-gray\" id=\"post-write\" role=\"tabpanel\" aria-labelledby=\"post-write-tab\">\n <div class=\"post-editor-metadata-form\"></div>\n <div class=\"post-editor-toolbar CMS_hidden\">\n <span class=\"ql-formats\">\n <select class=\"ql-font\"></select>\n <select class=\"ql-size\"></select>\n </span>\n <span class=\"ql-formats\">\n <button class=\"ql-bold\"></button>\n <button class=\"ql-italic\"></button>\n <button class=\"ql-underline\"></button>\n <button class=\"ql-strike\"></button>\n </span>\n <span class=\"ql-formats\">\n <select class=\"ql-color\"></select>\n <select class=\"ql-background\"></select>\n </span>\n <span class=\"ql-formats\">\n <button class=\"ql-script\" value=\"sub\"></button>\n <button class=\"ql-script\" value=\"super\"></button>\n </span>\n <span class=\"ql-formats\">\n <button class=\"ql-header\" value=\"1\"></button>\n <button class=\"ql-header\" value=\"2\"></button>\n <button class=\"ql-blockquote\"></button>\n <button class=\"ql-code-block\"></button>\n </span>\n <span class=\"ql-formats\">\n <button class=\"ql-list\" value=\"ordered\"></button>\n <button class=\"ql-list\" value=\"bullet\"></button>\n <button class=\"ql-indent\" value=\"-1\"></button>\n <button class=\"ql-indent\" value=\"+1\"></button>\n </span>\n <span class=\"ql-formats\">\n <button class=\"ql-direction\" value=\"rtl\"></button>\n <select class=\"ql-align\"></select>\n </span>\n <span class=\"ql-formats\">\n <button class=\"ql-link\"></button>\n <button class=\"ql-image\"></button>\n <button class=\"ql-video\"></button>\n <button class=\"ql-formula\"></button>\n </span>\n <span class=\"ql-formats\">\n <button class=\"ql-clean\"></button>\n </span>\n </div>\n\n <div class=\"post-editor-scroll-container CMS_hidden\">\n <div class=\"post-editor\"></div>\n </div>\n\n\n <div class=\"border-bottom post-editor-attachments\">\n <h6 class=\"border-bottom m-0 p-2\"><i class=\"fas fa-paperclip\"></i> 첨부파일</h6>\n <ul class=\"m-0 px-0 py-2 post-editor-attachments-list\"></ul>\n </div>\n\n <div class=\"post-editor-controls d-flex p-2 CMS_hidden\"></div>\n </div>\n\n <div data-jc=\"snackbar\" class=\"post-snackbar\"></div>\n <div data-jc=\"confirm\" class=\"post-confirm\"></div>\n </div>\n <div class=\"component-blocker position-absolute d-none\">\n <div class=\"progress g-height-25 position-absolute d-none\">\n <div class=\"progress-bar progress-bar-striped progress-bar-animated\" role=\"progressbar\" aria-valuenow=\"0\" aria-valuemin=\"0\" aria-valuemax=\"100\"></div>\n </div>\n <div class=\"blocker-message position-absolute d-flex\">\n <div class=\"my-auto ml-auto mr-3 g-width-50\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"xMidYMid\"><path fill=\"none\" d=\"M82 50A32 32 0 1 1 23.533421623214014 32.01333190873183 L21.71572875253809 21.7157287525381 L32.013331908731814 23.53342162321403 A32 32 0 0 1 82 50\" stroke-width=\"4\" stroke=\"#337ab7\"></path><circle cx=\"50\" cy=\"50\" fill=\"none\" stroke-linecap=\"round\" r=\"22\" stroke-width=\"4\" stroke=\"#337ab7\" stroke-dasharray=\"34.55751918948772 34.55751918948772\" transform=\"rotate(215.429 50 50)\"><animateTransform attributeName=\"transform\" type=\"rotate\" calcMode=\"linear\" values=\"0 50 50;360 50 50\" keyTimes=\"0;1\" dur=\"1s\" begin=\"0s\" repeatCount=\"indefinite\"></animateTransform></circle></svg>\n </div>\n <span class=\"message my-auto mr-auto g-font-size-28\"></span>\n </div>\n </div>\n</div>\n<!-- End Hover Rows -->","datecreated":"2019-06-25T01:01:47.200Z","picture":"","icon":"","category":"basics/main/bbs","dateupdated":"2019-06-28T06:06:46.019Z"}