shjung 3 éve
szülő
commit
609189da0e
22 módosított fájl, 4311 hozzáadás és 305 törlés
  1. 2 1
      src/main/resources/static/application/op/00.main/main-func.js
  2. 2 1
      src/main/resources/static/application/op/06.vms/01.system/01.monitoring/05.camera/camera.js
  3. 4 0
      src/main/resources/static/application/op/06.vms/01.system/02.control/control.css
  4. 5 2
      src/main/resources/static/application/op/06.vms/01.system/02.control/control.js
  5. 9 1
      src/main/resources/static/application/op/06.vms/02.manager/01.info/info.css
  6. 15 8
      src/main/resources/static/application/op/06.vms/02.manager/01.info/info.html
  7. 28 7
      src/main/resources/static/application/op/06.vms/02.manager/01.info/info.js
  8. 167 87
      src/main/resources/static/application/op/06.vms/03.form/manage.css
  9. 6 2
      src/main/resources/static/application/op/06.vms/03.form/manage.html
  10. 633 194
      src/main/resources/static/application/op/06.vms/03.form/manage.js
  11. 128 0
      src/main/resources/static/application/op/06.vms/03.form/parse.css
  12. 53 0
      src/main/resources/static/application/op/06.vms/03.form/parse.html
  13. 346 0
      src/main/resources/static/application/op/06.vms/03.form/parse.js
  14. 2 2
      src/main/resources/static/application/op/06.vms/04.schedule/01.auto/auto.js
  15. 2 0
      src/main/resources/static/js/vworld/obj-facility.js
  16. 1580 0
      src/main/resources/static/libs/dx-21.2.6/css/dx.yiits.css
  17. 0 0
      src/main/resources/static/libs/dx-21.2.6/js/dx-quill.min.js
  18. 1 0
      src/main/resources/static/libs/dx-21.2.6/js/showdown.min.js
  19. 924 0
      src/main/resources/static/libs/dx-21.2.6/js/turndown.js
  20. BIN
      src/main/resources/static/libs/fonts/batang.ttf
  21. BIN
      src/main/resources/static/libs/fonts/gulim.ttf
  22. 404 0
      src/main/resources/static/libs/html2.canvas/html2.canvas-1.4.1.js

+ 2 - 1
src/main/resources/static/application/op/00.main/main-func.js

@@ -218,7 +218,8 @@ function onContextMenuFunc(ALyrIdx, ALyrName, ANmbr, ACoordX, ACoordY, X, Y) {
             // 카메라 영상 클릭 이벤트
             $(div).children().eq(0).children().eq(1).on('click',function(){
                 $('.vms-right-btn-click').remove();
-                let src = vmsObj.strmSesnNm;
+                console.log(vmsObj);
+                let src = vmsObj.strmHttpAddr;
                 camCtrl(_cctvMap, createVideoDiv, vmsObj, src, X, Y)
             });
 

+ 2 - 1
src/main/resources/static/application/op/06.vms/01.system/01.monitoring/05.camera/camera.js

@@ -158,9 +158,10 @@ function montSection(data){
     $(".mont-box").append(divLineArr);
 
     _videoMap.get('info').forEach( (obj, key)=> {
+        console.log(obj);
         let player = videojs('video' + obj.monitoring_seq, { 
                         sources : [ 
-                            { src : obj.strm_sesn_nm, type : "application/x-mpegURL"} 
+                            { src : obj.strm_http_addr, type : "application/x-mpegURL"} 
                         ], 
                         poster : "test-poster.png",
                         controls : true, 

+ 4 - 0
src/main/resources/static/application/op/06.vms/01.system/02.control/control.css

@@ -27,6 +27,7 @@ body{
 }
 
 .CMS0,.CMS1,.CMS2,.PAS0,.HTS0,
+.PWS0,.PWS1,.PWS2,
 .MOS0,.PAS1,.HTS1,.MOS1,.PAS2,
 .HTS2,.MOS2,.gray,.CDS0,.CDS1,
 .CDS2,.green,.red,.yellow
@@ -43,6 +44,7 @@ body{
 .HTS0,
 .CDS0,
 .MOS0,
+.PWS0,
 .green{
     background-color: green;
     width:100%;
@@ -54,6 +56,7 @@ body{
 .HTS1,
 .CDS1,
 .MOS1,
+.PWS1,
 .red{
     background-color: red;
 }
@@ -67,6 +70,7 @@ body{
 .HTS2,
 .MOS2,
 .CDS2,
+.PWS2,
 .gray{
     background-color: gray;
 }

+ 5 - 2
src/main/resources/static/application/op/06.vms/01.system/02.control/control.js

@@ -361,8 +361,11 @@ function fetchBaseData(){
                 _listMap.set(item.vms_ctlr_nmbr, listData);
             }
         });
+        console.log(baseData);
+        console.log(data);
         let listData = Array.from(_listMap.values());
         _listTable.option('dataSource', listData);
+        console.log(listData);
     }
 
     let controlArr = controlDiv();
@@ -989,7 +992,7 @@ function listTableDblClick(info){
     if(!_selectedVideo){
         _selectedVideo = videojs('video', {
             sources  : [
-                { src : selectedData.strm_sesn_nm, type : "application/x-mpegURL"} 
+                { src : selectedData.strm_http_addr, type : "application/x-mpegURL"} 
             ],
             controls : true, 
             playsinline : true, 
@@ -1005,7 +1008,7 @@ function listTableDblClick(info){
 
     _selectedVideo.src([
         {
-            src: selectedData.strm_sesn_nm,
+            src: selectedData.strm_http_addr,
         }
     ]);
     _selectedVideo.play();

+ 9 - 1
src/main/resources/static/application/op/06.vms/02.manager/01.info/info.css

@@ -65,7 +65,7 @@ body{
 }
 
 .input-line > div:nth-child(1){
-    width: 120px;
+    width: 130px;
     height: 100%;
     display: flex;
     align-items: center;
@@ -139,4 +139,12 @@ body{
     opacity: 1;
     white-space: nowrap;
     font: bold 10pt sans-serif;
+}
+
+.ml-10{
+    margin-left: 10px;
+}
+
+.ml-8{
+    margin-left: 8px;
 }

+ 15 - 8
src/main/resources/static/application/op/06.vms/02.manager/01.info/info.html

@@ -46,10 +46,8 @@
                             <div>
                                 <div class="vms_ctlr_nmbr"></div>
                             </div>
-                        </div>
-                        <div class="input-line">
-                            <div>VMS ID (*)</div>
-                            <div>
+                            <div class="ml-8">VMS ID (*)</div>
+                            <div class="ml-10">
                                 <div class="vms_ctlr_id"></div>
                             </div>
                         </div>
@@ -72,13 +70,10 @@
                             </div>
                         </div>
                         <div class="input-line">
-                            <div>위치좌표 X (*)</div>
+                            <div>위치좌표 X / Y(*)</div>
                             <div>
                                 <div class="x_crdn"></div>
                             </div>
-                        </div>
-                        <div class="input-line">
-                            <div>위치좌표 Y (*)</div>
                             <div>
                                 <div class="y_crdn"></div>
                             </div>
@@ -116,6 +111,18 @@
                                 <div class="strm_sesn_nm"></div>
                             </div>
                         </div>
+                        <div class="input-line">
+                            <div>스트리밍 HTTP 주소</div>
+                            <div>
+                                <div class="strm_http_addr"></div>
+                            </div>
+                        </div>
+                        <div class="input-line">
+                            <div>스트리밍 RSTP 주소</div>
+                            <div>
+                                <div class="strm_rtsp_addr"></div>
+                            </div>
+                        </div>
                         <div class="input-line">
                             <div>표출폼최대갯수</div>
                             <div>

+ 28 - 7
src/main/resources/static/application/op/06.vms/02.manager/01.info/info.js

@@ -66,7 +66,9 @@ const _updateColumns  = {
     web_cmra_pwd : null,
     wthrinfr_cnct_yn : "N",
     x_crdn : 0,
-    y_crdn : 0
+    y_crdn : 0,
+    strm_http_addr: null,
+    strm_rtsp_addr: null,
 }
 
 const _btnClasses     = [
@@ -97,6 +99,8 @@ const _inputClasses   = [
     'vms_max_phse_num',
     'panl_on_time',
     'panl_off_time',
+    'strm_http_addr',
+    'strm_rtsp_addr',
 ];
 
 const panlTimeColumns = [
@@ -224,6 +228,7 @@ async function fetchBaseData(){
 function fetchInfoTableList(jsonData){
     _vmsTableList.option('dataSource', jsonData);
     _managerData.push(jsonData);
+    console.log(jsonData);
     doMap();
     updateIcon();
 }
@@ -305,11 +310,11 @@ function fetchVmsInputArray(){
         inputBox.set('input', null);
         switch(inputClass){
             case 'vms_ctlr_nmbr' :
-                setVmsInputOption(inputClass, 'text', 170, null, 10);
+                setVmsInputOption(inputClass, 'text', 60, null, 10);
                 inputBox.set('must', '제어기 번호');
                 break
             case 'vms_ctlr_id':
-                setVmsInputOption(inputClass, 'text', 170, null, 30);
+                setVmsInputOption(inputClass, 'text', 100, null, 30);
                 inputBox.set('must', 'VMS ID');
                 break
             case 'vms_nm' :
@@ -325,12 +330,12 @@ function fetchVmsInputArray(){
                 inputBox.set('must', 'VMS 크기');
                 break
             case 'x_crdn' :
-                setVmsInputOption(inputClass, 'text', 170, '0.00000000', 11);
+                setVmsInputOption(inputClass, 'text', 120, '0.00000000', 11);
                 inputBox.set('must', '위치좌표 X');
                 inputBox.get('input').on('valueChanged', (text) => crdnChanges(text, 3));
                 break
             case 'y_crdn' :
-                setVmsInputOption(inputClass, 'text', 170, '0.00000000', 11);
+                setVmsInputOption(inputClass, 'text', 120, '0.00000000', 11);
                 inputBox.set('must', '위치좌표 Y');
                 inputBox.get('input').on('valueChanged', (text) => crdnChanges(text, 2));
                 break
@@ -357,6 +362,12 @@ function fetchVmsInputArray(){
             case 'strm_sesn_nm' :
                 setVmsInputOption(inputClass, 'text', 250, null, 200);
                 break
+            case 'strm_http_addr' :
+                setVmsInputOption(inputClass, 'text', 250, null, 200);
+                break
+            case 'strm_rtsp_addr' :
+                setVmsInputOption(inputClass, 'text', 250, null, 200);
+                break
             case 'vms_max_phse_num' :
                 setVmsInputOption(inputClass, 'number', 70, 10, 16);
                 break
@@ -575,13 +586,23 @@ function saveEvent(type){
     if(!/^[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3}$/.test(updateData.vms_ctlr_ip)){
         const edIp = getInput('vms_ctlr_ip');
         edIp.focus();
-        return alertWarning("IP 주소를 정확히 입력해주세요.", null, edIp);
+        return alertWarning("스트리밍 주소 IP를 형식에 맞게 입력해주세요.", null, edIp);
     };
 
     if( updateData.web_cmra_ip && !/^[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3}$/.test(updateData.web_cmra_ip)){
         const edIp = getInput('web_cmra_ip');
         edIp.focus();
-        return alertWarning("IP 주소를 정확히 입력해주세요.", null, edIp);
+        return alertWarning("웹카메라 IP를 형식에 맞게 입력해주세요.", null, edIp);
+    };
+    if( updateData.strm_http_addr && !/^[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3}$/.test(updateData.strm_http_addr)){
+        const edIp = getInput('strm_http_addr');
+        edIp.focus();
+        return alertWarning("스트리밍 HTTP 주소 IP를 형식에 맞게 입력해주세요.", null, edIp);
+    };
+    if( updateData.strm_rtsp_addr && !/^[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3}$/.test(updateData.strm_rtsp_addr)){
+        const edIp = getInput('strm_rtsp_addr');
+        edIp.focus();
+        return alertWarning("스트리밍 RSTP 주소 IP를 형식에 맞게 입력해주세요.", null, edIp);
     };
     
     for( let panlTimeColumn of panlTimeColumns){

+ 167 - 87
src/main/resources/static/application/op/06.vms/03.form/manage.css

@@ -1,100 +1,101 @@
-* {
+*{
     padding: 0;
-    margin: 0;
+    margin: 0;   
 }
-html {
+html{
     width: 100%;
     height: 100%;
 }
-body {
+body{
     padding: 10px;
     width: calc(100% - 20px);
     height: calc(100% - 20px);
 }
 
-.left-section {
+
+.left-section{
     width: 500px;
     height: 100%;
 }
-.table-container {
+.table-container{
     height: calc(100% - 81px);
 }
-.left-section > div:nth-child(2) {
+.left-section > div:nth-child(2){
     display: flex;
     align-items: center;
     width: 100%;
     height: 50px;
 }
 
-.right-section {
+.right-section{
     width: calc(100% - 501px);
     height: 100%;
 }
-.form-info-edit-sect {
+.form-info-edit-sect{
     display: flex;
     width: 100%;
     height: 100%;
 }
-.form-info-edit-sect > div:nth-child(1) {
+.form-info-edit-sect > div:nth-child(1){
     width: 496px;
     height: 100%;
     padding: 2px;
 }
-.form-info-edit-sect > div:nth-child(1) > div:nth-child(2) {
+.form-info-edit-sect > div:nth-child(1) > div:nth-child(2){
     width: 100%;
     height: 40px;
     display: flex;
     align-items: center;
 }
-.form-info-edit-sect > div:nth-child(1) > div:nth-child(1) {
+.form-info-edit-sect > div:nth-child(1) > div:nth-child(1){
     padding: 10px;
     width: calc(100% - 22px);
     height: calc(100% - 40px);
 }
-.form-info-edit-sect > div:nth-child(2) {
+.form-info-edit-sect > div:nth-child(2){
     width: 530px;
     height: calc(100% - 20px);
     padding-top: 10px;
     padding-bottom: 10px;
 }
-.form-info-edit-sect > div:nth-child(2) > div {
+.form-info-edit-sect > div:nth-child(2) > div{
     width: 100%;
     height: 40px;
     display: flex;
     align-items: center;
 }
-.label {
+.label{
     width: 80px;
     height: 100%;
     display: flex;
     align-items: center;
     justify-content: right;
 }
-.form-obj-info-sect {
+.form-obj-info-sect{
     display: flex;
     width: 100%;
     height: calc(100% - 42px);
 }
-.form-obj-info-sect > div:nth-child(2) {
+.form-obj-info-sect > div:nth-child(2){
     width: calc(100% - 451px);
     height: 100%;
 }
-.form-obj-info-sect > div:nth-child(1) {
+.form-obj-info-sect > div:nth-child(1){
     width: 450px;
     height: 100%;
 }
-.form-obj-info {
+.form-obj-info{
     width: 100%;
     height: 100%;
 }
-.form-obj-info > div:nth-child(1) {
+.form-obj-info > div:nth-child(1){
     width: 100%;
     height: 40px;
     display: flex;
     align-items: center;
 }
 
-.tab-serve-title {
+.tab-serve-title{
     background-color: #2f2f38;
     display: flex;
     align-items: center;
@@ -103,176 +104,181 @@ body {
     height: 25px;
 }
 
-.button-box {
+.button-box{
     width: 100%;
     height: 40px;
     display: flex;
     align-items: center;
 }
-.button-box > :first-child {
+.button-box > :first-child{
     padding-left: 10px;
 }
-.left-section > div:nth-child(2) > div:nth-child(1) {
+.left-section > div:nth-child(2) > div:nth-child(1){
     padding-left: 20px;
 }
-.left-section > div:nth-child(2) > div:nth-child(2) {
+.left-section > div:nth-child(2) > div:nth-child(2){
     padding-left: 10px;
 }
 
-.no-padding {
-    padding: 5px !important;
+.no-padding{
+    padding : 5px !important;
 }
 .obj-btn-box,
-.obj-text-box {
+.obj-text-box{
     width: 100%;
     height: 35px;
     display: flex;
     align-items: center;
 }
-.obj-table-box {
+.obj-table-box{
     width: 100%;
     height: calc(100% - 95px);
     padding: 2px;
 }
 
-.obj-btn-box > :first-child {
+.obj-btn-box > :first-child{
     margin-left: 10px;
 }
 .obj-btn-box > div:nth-child(2),
-.obj-text-box > div:nth-child(3) {
+.obj-text-box > div:nth-child(3){
     margin-left: auto;
     margin-right: 5px;
 }
 
-.obj-btn-box > div:nth-child(3) {
+.obj-btn-box > div:nth-child(3){
     margin-right: 10px;
 }
 
-.ml-5 {
+.ml-5{
     margin-left: 5px;
 }
-.obj-edit-btn {
+.obj-edit-btn{
     width: 100%;
     height: 30px;
     display: flex;
     align-items: center;
 }
 
-.obj-edit-btn > div:nth-child(1) {
+.obj-edit-btn > div:nth-child(1){
     margin-left: 10px;
 }
-.obj-edit-btn > div:nth-child(5),
+.obj-edit-btn > div:nth-child(5), 
 .obj-edit-btn > div:nth-child(9),
-.obj-edit-btn > div:nth-child(11) {
-    margin-left: 15px;
+.obj-edit-btn > div:nth-child(11){
+    margin-left : 15px;
 }
 
-.obj-edit-text {
+.obj-edit-text{
     width: 100%;
     height: 290px;
     padding-top: 10px;
     padding-bottom: 10px;
 }
 
-.obj-edit-text > div {
+.obj-edit-text > div{
     display: flex;
     align-items: center;
     height: 40px;
     width: 100%;
 }
 
-.obj-edit-img {
+.obj-edit-img{
     width: 100%;
     height: calc(100% - 310px);
     padding: 2px;
 }
-.obj-edit-img > div:nth-child(1) {
+.obj-edit-img > div:nth-child(1){
     width: 100%;
     height: 100%;
     padding: 10px;
 }
-.padding-box {
+.padding-box{
     width: 100%;
     height: 100%;
     padding: 2px;
 }
-.obj-edit-sect {
+.obj-edit-sect{
     width: 100%;
     height: 100%;
 }
 
-.direct-sect {
+.direct-sect{
     width: 100%;
     height: 100%;
-    padding: 2px;
 }
 
-.direct-sect > div {
+.direct-sect > div{
     width: 100%;
     height: 50%;
     display: flex;
 }
 
-.direct-sect > div:nth-child(1) > div:nth-child(1),
-.direct-sect > div:nth-child(2) > div:nth-child(1) {
-    width: calc(100% - 130px);
+.direct-sect > div:nth-child(1) > div:nth-child(1){
+    width: 100%;
     height: 100%;
 }
+.direct-sect > div:nth-child(2) > div:nth-child(1){
+    width: calc(100% - 150px);
+    height: 100%;
+}
+.direct-sect > div:nth-child(2) > div:nth-child(1){
+    padding: 2px;
+}
 
 .direct-sect > div:nth-child(1) > div:nth-child(2),
-.direct-sect > div:nth-child(2) > div:nth-child(2) {
-    width: 130px;
+.direct-sect > div:nth-child(2) > div:nth-child(2){
+    width: 150px;
     height: 100%;
     padding: 10px;
 }
-.direct-sect > div:nth-child(1) > div:nth-child(2) > div:nth-child(1) {
+.direct-sect > div:nth-child(1) > div:nth-child(2) > div:nth-child(1){
     display: flex;
 }
 
 .dx-icon-align-bottom {
-    background-image: url("/images/op/icon/align-bottom.bmp");
+    background-image: url('/images/op/icon/align-bottom.bmp');
     background-repeat: no-repeat;
     background-position: 0px 0px;
 }
 .dx-icon-align-top {
-    background-image: url("/images/op/icon/align-top.bmp");
+    background-image: url('/images/op/icon/align-top.bmp');
     background-repeat: no-repeat;
     background-position: 0px 0px;
 }
 .dx-icon-align-center {
-    background-image: url("/images/op/icon/align-center.bmp");
+    background-image: url('/images/op/icon/align-center.bmp');
     background-repeat: no-repeat;
     background-position: 0px 0px;
 }
 .dx-icon-align-wide {
-    background-image: url("/images/op/icon/align-wide.bmp");
+    background-image: url('/images/op/icon/align-wide.bmp');
     background-repeat: no-repeat;
     background-position: 0px 0px;
 }
 .dx-icon-align-left {
-    background-image: url("/images/op/icon/align-left.bmp");
+    background-image: url('/images/op/icon/align-left.bmp');
     background-repeat: no-repeat;
     background-position: 0px 0px;
 }
 .dx-icon-align-right {
-    background-image: url("/images/op/icon/align-right.bmp");
+    background-image: url('/images/op/icon/align-right.bmp');
     background-repeat: no-repeat;
     background-position: 0px 0px;
 }
 
-.mb-5 {
+.mb-5{
     margin-bottom: 5px;
 }
 
-.char-map {
-    margin-left: 185px;
+.char-map{
+    margin-left:  185px;
 }
 
-.dp-none {
+.dp-none{
     display: none;
 }
 
-.img-text {
+.img-text{
     width: 320px;
     height: 64px;
     display: flex;
@@ -280,29 +286,30 @@ body {
     justify-content: center;
 }
 
-.vms-img {
+.vms-img{
     display: none;
 }
 
-.left-auto {
+.left-auto{
     margin-left: auto;
     margin-right: 10px;
 }
-.form-tab {
+.form-tab{
     width: 100%;
     height: calc(100% - 40px);
 }
-.canvas {
-    width: 320px;
+.canvas{
+    width : 320px;
     height: 64px;
     position: relative;
+    overflow: hidden;
 }
 
-.drag-div {
+.drag-div{
     position: absolute;
 }
 
-.modal-screen {
+.modal-screen{
     z-index: 200;
     background: #363640;
     position: absolute;
@@ -311,7 +318,7 @@ body {
     display: flex;
     flex-direction: column;
 }
-.modal-background {
+.modal-background{
     position: absolute;
     display: flex;
     align-items: center;
@@ -324,47 +331,47 @@ body {
     background-color: rgba(75, 73, 73, 0.4);
 }
 
-.modal-title {
+.modal-title{
     height: 30px;
 }
 
-.modal-content {
+.modal-content{
     width: 100%;
     height: 65%;
-    display: flex;
-    align-items: center;
+    display: flex; 
+    align-items: center; 
     justify-content: center;
 }
-.modal-button {
+.modal-button{
     width: 100%;
     height: 20%;
     display: flex;
     align-items: center;
     justify-content: right;
 }
-.modal-button > div {
+.modal-button > div{
     margin-right: 10px;
 }
-.modal-container {
+.modal-container{
     width: calc(100% - 20px);
     height: calc(100% - 52px);
     padding: 10px;
 }
-.modal-container > div:nth-child(1) {
+.modal-container > div:nth-child(1){
     width: 100%;
     height: 100%;
 }
-.modal-table-box {
+.modal-table-box{
     width: calc(100% - 4px);
     height: calc(100% - 69px);
     padding: 2px;
 }
 
-.modal-table-box > div:nth-child(1) {
+.modal-table-box > div:nth-child(1){
     width: 100%;
     height: 100%;
 }
-.modal-container > div:nth-child(1) > div:nth-child(3) {
+.modal-container > div:nth-child(1) > div:nth-child(3){
     width: 100%;
     height: 40px;
     display: flex;
@@ -373,21 +380,94 @@ body {
 .modal-x-btn,
 .img-x-btn,
 .simbol-x-btn,
-.modal-container > div:nth-child(1) > div:nth-child(3) > div:nth-child(1) {
+.name-x-btn,
+.modal-container > div:nth-child(1) > div:nth-child(3) > div:nth-child(1){
     margin-left: auto;
     margin-right: 10px;
 }
 
-.input-file {
+.input-file{
     display: none;
 }
 
-.canvas-content {
+.canvas-content{
     width: auto;
     height: auto;
     font-size: 14pt;
-    font-family: "맑은 고딕";
+    font-family: 맑은 고딕;
     position: absolute;
     color: #00ff00;
     font-weight: bold;
 }
+
+
+.name-modal-screen {
+    z-index: 200;
+    background: #363640;
+    position: absolute;
+    width: 310px;
+    height: 150px;
+    display: flex;
+    flex-direction: column;
+}
+.name-modal-btn-box{
+    width: 100%;
+    height: 40px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+
+.name-modal-container{
+    width: calc(100% - 20px);
+    height: calc(100% - 68px);
+    justify-content: center;
+    display: flex;
+    padding-left: 20px;
+    flex-direction: column;
+}
+
+.name-modal-container > div:nth-child(1) {
+    height: 25px;
+}
+
+.name-modal-btn-box > div:nth-child(2) > div:nth-child(1){
+    margin-left: 10px;
+}
+
+.name-modal-btn-box > div:nth-child(1) > div:nth-child(1) {
+    width: 85px;
+}
+
+.dr-textarea{
+    width: 100%;
+    height: 100%;
+    text-align: center;
+    overflow: auto;
+    background-color: rgba(0,0,0);
+    color: rgb(255, 255, 0);
+    font-size: 18pt;
+    border: 0px solid black;
+    resize: none;
+    white-space: pre;
+}
+
+.dr-textarea:focus{
+    outline:none;
+}
+
+.dr-canvas-box{
+    width: calc(100% - 2px);
+    height: calc(100%  - 2px);
+}
+
+.dr-canvas{
+    width: 320px;
+    height: 64px;
+    background-color: black;
+}
+.dx-htmleditor-content{
+    background-color: rgba(0,0,0) !important;
+    margin: 1px !important;
+}

+ 6 - 2
src/main/resources/static/application/op/06.vms/03.form/manage.html

@@ -1,14 +1,18 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="kor">
 <head>
     <meta charset="UTF-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta http-equiv="X-UA-Compatible" content="chrom">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <script src="/libs/dx-21.2.6/js/dx-quill.min.js"></script>
     <script src="/libs/include-common.js"></script>
     <script src="/libs/include-excel.js"></script>
     <link rel="stylesheet" href="/application/op/99.common/common.css">
+    <script src="/libs/dx-21.2.6/js/showdown.min.js"></script>
     <link rel="stylesheet" href="./manage.css">
     <script src="/libs/floodfill/floodfill.js"></script>
+    <script src="/libs/html2.canvas/html2.canvas-1.4.1.js"></script>
+ 
     <script src="./manage.js"></script>
     <title>VMS 폼 관리</title>
 </head>

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 633 - 194
src/main/resources/static/application/op/06.vms/03.form/manage.js


+ 128 - 0
src/main/resources/static/application/op/06.vms/03.form/parse.css

@@ -0,0 +1,128 @@
+#log {
+    background-color: rgb(147, 194, 149);
+    width: 400px;
+}
+#previewDiv {
+    margin-left: 0px;
+    margin-top: 0px;
+    margin-right: 0px;
+    background-color: #000000;
+    border: 1px solid #ff0000;
+    width: 386px;
+    height: 65px;
+}
+#previewCanvas {
+    margin-left: 1px;
+    margin-top: 1px;
+    margin-right: 1px;
+    background-color: #000000;
+    width: 384px;
+    height: 64px;
+}
+
+@font-face {
+    font-family:'나눔고딕';
+    src: url('/libs/font_korean/나눔고딕.ttf');
+    }
+    @font-face {
+    font-family:'나눔명조';
+    src: url('/libs/font_korean/나눔명조.ttf');
+    }
+    @font-face {
+    font-family:'굴림';
+    src: url('/libs/font_korean/굴림.ttc');
+    }
+    @font-face {
+    font-family:'굴림체';
+    src: url('/libs/font_korean/굴림체.ttf');
+    }
+    @font-face {
+    font-family:'궁서체';
+    src: url('/libs/font_korean/궁서체.ttf');
+    }
+    @font-face {
+    font-family:'hy궁서';
+    src: url('/libs/font_korean/hy궁서.ttf');
+    }
+    @font-face {
+    font-family:'HS봄바람체';
+    src: url('/libs/font_korean/HS봄바람체2.0.ttf');
+    }
+    @font-face {
+    font-family:'돋움';
+    src: url('/libs/font_korean/돋움.ttc');
+    }
+    @font-face {
+    font-family:'돋움체';
+    src: url('/libs/font_korean/돋움체.ttf');
+    }
+    @font-face {
+    font-family:'맑은고딕';
+    src: url('/libs/font_korean/맑은고딕.ttf');
+    }
+    @font-face {
+    font-family:'바탕';
+    src: url('/libs/font_korean/바탕.ttc');
+    }
+    @font-face {
+    font-family:'바탕체';
+    src: url('/libs/font_korean/바탕체.ttf');
+    }
+    @font-face {
+    font-family:'새굴림';
+    src: url('/libs/font_korean/새굴림.ttf');
+    }
+      /* .testFont {
+      color: black;
+      font-family: '나눔고딕(Regular)', sans-serif;
+      font-size: 100px;
+    } */
+    .htmlCon {
+      display: grid;
+	    grid-template-columns: 420px 0px;
+	    /* grid-template-rows: 800px 100px; */
+    }
+    .htmlEditor {
+        position: grid;
+        background-color: black;
+        width: 400px;
+        height: 400px !important;
+        /* color: springgreen;
+        font-size: 30px;
+        font-family:'궁서체' !important; */
+    }
+
+    .htmlToolBar {
+      position: grid;
+      background-color: black;
+      color: aliceblue;
+      width: 181px;
+      height: 155px !important;
+    }
+    .dx-htmleditor-content img {
+      vertical-align: middle;
+      padding-right: 10px;
+    }
+
+    .value-content {
+      margin-top: 20px;
+      overflow: auto;
+      height: 110px;
+      width: 100%;
+      white-space: pre-wrap;
+    }
+
+    .options {
+      margin-top: 20px;
+      padding: 20px;
+      background-color: rgba(0, 0, 0, 0.685);
+      box-sizing: border-box;
+      width: 400px;
+    }
+
+    .value-content {
+        background-color: black;
+    }
+    /* .dx-button-text {
+      visibility: g;
+    } */

+ 53 - 0
src/main/resources/static/application/op/06.vms/03.form/parse.html

@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>DevExtreme Demo</title>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
+    
+    <script src="/libs/jquery/jquery-3.6.0.min.js"></script>
+    <script src="/libs/dx-21.2.6/js/dx-quill.min.js"></script>
+    <script src="/libs/dx-21.2.6/js/dx.all.js"></script>
+    <!-- <script src="/libs/dx-21.2.6/js/turndown.js"></script> -->
+    
+    <link rel="stylesheet" type="text/css" href="/libs/dx-21.2.6/css/dx.yiits.css" />
+  
+    <script src="/libs/dx-21.2.6/js/showdown.min.js"></script>
+    <script src="./parse.js"></script>
+    <link rel="stylesheet" href="./parse.css">
+
+  </head>
+
+  <body class="dx-viewport">
+    <h3 style="color: red;">Content :</h3>
+    <div id="log"></div>
+    
+    <h3  style="color: red;">Preview :</h3>
+    <canvas id="previewCanvas" class="previewCanvas" width="384" height="64" color="red"> 
+      VMS Form Preview 
+    </canvas>
+    
+    <h3  style="color: red;">Html Editor :</h3>
+    <div class="dx-viewport">
+      <div class="demo-container">
+        <div class="htmlCon">
+          <div class="htmlEditor"></div>
+           <!-- <div class="htmlToolBar"></div> -->
+        </div>
+
+        <div class="options">
+          <div class="value-types"></div>
+          <div id="value-content" class="value-content"></div>
+        </div>
+      </div>
+    </div>
+    <script>
+      htmlEditor()
+      
+       
+    
+    </script>
+
+  </body>
+</html>

+ 346 - 0
src/main/resources/static/application/op/06.vms/03.form/parse.js

@@ -0,0 +1,346 @@
+function htmlEditor() {
+    const fontArr = []
+    for(var ii = 9; ii < 41; ii++){
+        fontArr.push(ii+'pt')
+    }
+
+          $('.htmlEditor').dxHtmlEditor({
+              onContentReady: function(e) {
+                e.component.format("font", "맑은고딕");
+                e.component.format("size", "18pt");
+                e.component.format("color", "springgreen");
+                e.component.getQuillInstance().blur();
+                e.component.getQuillInstance().focus();
+              },
+              toolbar: {
+                  items: [
+                    'alignLeft', 'alignCenter','alignRight',
+                    'separator',
+                    ,
+                    {
+                      name: 'font',
+                      acceptedValues: ['맑은고딕','굴림체', '궁서체', '돋움체', '바탕체'],
+                      options: {
+                        //value:'맑은 고딕',
+                        //width: 170
+                      },
+                    },
+                    'separator',
+                    {
+                      name: 'color',
+                      acceptedValues: ['red', 'springgreen','yellow'],
+                      options: {
+                        //value:"springgreen",
+                        //width: 170,
+                        // icon: "color"
+                      }
+                    },
+                    'separator',     
+                    {
+                      name: 'size',
+                      acceptedValues: fontArr,
+                      options: {
+                        //value:"18pt",
+                        //width: 170,
+                      }
+                    },
+                    'separator',
+                    'bold' 
+                    
+                  ],
+                  //container: ".htmlToolBar"
+              },
+              onValueChanged(e) {
+                    let color  = $(".dx-htmleditor-content").find("span").css("color")
+                    if(color == null) {
+                            e.component.format("font", "맑은고딕");
+                            e.component.format("size", "18pt");
+                            e.component.format("color", "springgreen");
+                            e.component.getQuillInstance().blur();
+                            e.component.getQuillInstance().focus();
+                    }
+
+                    $('.value-content').text(e.component.option('value'));
+                    let htmlTextVal = $('.value-content').text()
+                    parsHtml(htmlTextVal)
+              },
+          })
+}
+
+function parsHtml(htmlTextVal) {
+    // 돋움 : Dotum
+    // 돋움체 : DotumChe
+    // 굴림 : Gulim
+    // 굴림체 : GulimChe
+    // 바탕 : Batang
+    // 바탕체 : BatangChe
+    // 궁서 : Gungsuh
+    // 궁서체 : GungsuhChe
+    // 새굴림: New Gulim
+    // 맑은 고딕 : Malgun Gothic, ==> Default Font
+
+    const fontNameMap = new Map();
+    fontNameMap.set("맑은 고딕", 1);
+    fontNameMap.set("HY견명조", 2);
+    fontNameMap.set("굴림체", 3);
+    fontNameMap.set("궁서체", 4);
+    fontNameMap.set("돋움체", 5);
+    fontNameMap.set("바탕체", 6);
+
+    const fontCodeMap = new Map();
+    fontCodeMap.set(1, "맑은 고딕");
+    fontCodeMap.set(2, "HY견명조");
+    fontCodeMap.set(3, "굴림체");
+    fontCodeMap.set(4, "궁서체");
+    fontCodeMap.set(5, "돋움체");
+    fontCodeMap.set(6, "바탕체");
+
+    class Text {
+        constructor(name, size, color, bold, width, height, htmlText, text) {
+            this.name = name;
+            this.size = size;
+            this.color = color;
+            this.bold = bold;
+            this.width = width;
+            this.height = height;
+            this.htmlText = htmlText;
+            this.text = text;
+            this.x = 0;
+            this.y = 0;
+        }
+    }
+    class Line {
+        constructor(align) {
+            this.align = align;
+            this.width = 0;
+            this.height = 0;
+            this.texts = new Array();
+        }
+    }
+    class Form {
+        constructor(color, width, height) {
+            this.color = color;
+            this.width = width;
+            this.height = height;
+            this.textHeight = 0;
+            this.lines = new Array();
+        }
+    }
+
+    // Map 으로 저장해서 처리하는 것으로 수정하는 것이 좋음.
+    function getTextAlignFromData(align) {
+        //(0:left, 1:right, 2:center
+        if (align === "center") {
+            return 2;
+        }
+        if (align === "right") {
+            return 1;
+        }
+        return 0;
+    }
+    function getTextAlignFromCode(align) {
+        //(0:left, 1:right, 2:center
+        if (align === 2) {
+            return "center";
+        }
+        if (align === 1) {
+            return "right";
+        }
+        return "left";
+    }
+
+    var $log = $("#log");
+    var str = "";
+    // str += '<p style="text-align: center;">';
+    // str += '   <strong style="color: rgb(255, 0, 0); font-size: 18pt; font-family: 맑은 고딕;">맑은 고딕</strong>';
+    // str += '   <strong style="color: rgb(0, 255, 0); font-size: 18pt; font-family: 맑은 고딕;">굴림체</strong>';
+    // str += '   <strong style="color: rgb(0, 0, 255); font-size: 18pt; font-family: 맑은 고딕;">HY견명조</strong></p>';
+    // str += '<p style="text-align: center;">';
+    // str += '   <strong style="color: rgb(255, 0, 0); font-size: 18pt; font-family: 궁서체;">궁서체</strong>';
+    // str += '   <strong style="color: rgb(0, 255, 0); font-size: 18pt; font-family: 궁서체;">돋움체</strong>';
+    // str += '   <strong style="color: rgb(0, 0, 255); font-size: 18pt; font-family: 궁서체;">바탕체</strong></p>';
+    // str += '<p style="text-align: left;">';
+    // str += '   <strong style="color: rgb(255, 0, 0); font-size: 18pt; font-family: 맑은 고딕;">맑은 afdsa 고딕</strong>';
+    // str += '   <strong style="color: rgb(0, 255, 0); font-size: 18pt; font-family: 굴림체;">굴림체</strong>';
+    // str += '   <strong style="color: rgb(0, 0, 255); font-size: 18pt; font-family: HY견명조;">HY견명조</strong></p>';
+    // str += '<p style="text-align: right;">';
+    // str += '   <strong style="color: rgb(255, 0, 0); font-size: 18pt; font-family: 궁서체;">궁서체</strong>';
+    // str += '   <strong style="color: rgb(0, 255, 0); font-size: 18pt; font-family: 돋움체;">돋움체</strong>';
+    // str += '   <strong style="color: rgb(0, 0, 255); font-size: 18pt; font-family: 바탕체;">바탕체</strong></p>';
+
+    str = htmlTextVal;
+    // '<p style="text-align: left;"><span style="color: red; font-size: 12pt;">동해물과 백두산이</span> <span style="font-family: 굴림체; color: springgreen; font-size: 12pt;">마르고 닳도록</span></p><p style="text-align: right;"><span style="font-family: 궁서체; font-size: 12pt; color: yellow;">하나님이 보우하사</span> <span style="font-family: 돋움체; font-size: 12pt; color: red;">우리나라 만세</span></p>';
+    //    '<p><span style="color: red; font-family: 나눔고딕;">기본폰트 </span><span style="font-family: Sunflower; color: springgreen;">나눔고딕 </span><span style="font-family: 나눔명조;">나눔명조</span></p><p><span style="font-family: 나눔고딕;">썬플라워</span> <span style="font-family: 나눔고딕;">햄릿</span><span style="font-family: Hahmlet;">  </span><span style="font-family: 나눔고딕;">노토산즈</span></p><p><br></p>';
+    var html = $.parseHTML(str);
+    var nodeNames = [];
+
+    let line = 0;
+
+    ////console.log("=====================================================");
+
+    // HTML TAG Parsing....................
+    const el = document.createElement("html");
+    el.innerHTML = str;
+    const pTags = el.getElementsByTagName("p");
+    //console.log(el);
+    //console.log(pTags);
+
+    const objForm = new Form("rgb(0,0,0)", 384, 64);
+
+    for (let ii = 0; ii < pTags.length; ii++) {
+        const p = pTags[ii];
+        const textAlign = getTextAlignFromData(p.style.textAlign);
+        const children = p.children;
+        //console.log("----------------------------------------------------------------");
+        if (children.length == 0) {
+            console.warn("object not found....");
+            continue;
+        }
+        const objLine = new Line(textAlign);
+        for (let jj = 0; jj < children.length; jj++) {
+            const obj = children[jj];
+            const fontName = obj.style.fontFamily;
+            const fontSize = Number(obj.style.fontSize.replace("pt", "").replace("px", ""));
+            const fontBold = obj.nodeName === "STRONG" ? true : obj.style.fontWeight === "bold";
+            let fontColor = obj.style.color.replaceAll(" ", "");
+            if (fontColor === "") {
+                fontColor = "rgb(0,0,0)";
+            }
+            const htmlText = obj.innerText;
+            const text = htmlText.trim();
+            const result = getTextInfo(fontName, fontSize, fontBold, htmlText);
+            const textWidth = result.width;
+            const textHeight = result.height;
+            objLine.width += textWidth;
+            if (textHeight > objLine.height) {
+                objLine.height = textHeight;
+            }
+
+            const objText = new Text(fontName, fontSize, fontColor, fontBold, textWidth, textHeight, htmlText, text);
+            objLine.texts.push(objText);
+
+            //console.log(
+            //     `line ${line}, objects ${jj}, font-Name: ${fontName}, bold: ${fontBold}, size: ${fontSize}, color: ${fontColor}, text: ${text}, [${htmlText}], text-height: ${textHeight}, text-width: ${textWidth}`
+            // );
+        }
+        //console.log(
+        //     `line ${line}, objects: [${children.length}], text-align: [${textAlign}]${getTextAlignFromCode(textAlign)}, line-width: ${objLine.width}, line-height: ${
+        //         objLine.height
+        //     }`
+        // );
+        objForm.textHeight += objLine.height;
+        objForm.lines.push(objLine);
+        line++;
+    }
+
+    // 텍스트 위치 계산
+    let textTop = objForm.height - objForm.textHeight;
+    if (textTop <= 0) {
+        textTop = 0;
+    } else {
+        textTop /= 2;
+    }
+    objForm.lines.forEach((line, idx) => {
+        let left = 0; // left
+        if (line.align == 1) {
+            // right
+            left = objForm.width - line.width;
+            if (left <= 0) {
+                left = 0;
+            }
+        } else if (line.align == 2) {
+            // center
+            left = objForm.width - line.width;
+            if (left <= 0) {
+                left = 0;
+            } else {
+                left /= 2;
+            }
+        }
+        line.texts.forEach((text, seq) => {
+            text.x = left;
+            text.y = textTop + (line.height - text.height);
+            left += text.width;
+        });
+        textTop += line.height;
+    });
+    //console.log(objForm);
+    // 미리보기 폼을 캔버스에 그린다.
+    drawPreviewForm(objForm);
+
+    $log.html(html);
+    //$("#previewCanvas").append("<h3>Preview:</h3>");
+    // $log.append("<h3>Preview:</h3>");
+
+    function getTextInfo2(fontName, fontSize, fontBold, htmlText) {
+        var text = document.createElement("span");
+        document.body.appendChild(text);
+
+        //text.style.backgroundColor = "#FF0000";
+        //text.style.lineHeight = "100%";
+        text.style.border = "none";
+        text.style.margin = "0px";
+        text.style.padding = "0px";
+        text.style.font = fontName;
+        if (fontBold) {
+            text.style.fontWeight = "bold";
+        }
+        text.style.fontFamily = fontName;
+        text.style.fontSize = fontSize + "pt";
+        text.style.height = "auto";
+        text.style.width = "auto";
+        text.style.position = "absolute";
+        text.style.whiteSpace = "pre"; //"nowrap";
+        text.innerHTML = htmlText;
+        ////console.log("getTextInfo: ", text.getBoundingClientRect());
+        const width = Math.ceil(text.clientWidth);
+        const height = Math.ceil(text.clientHeight);
+        document.body.removeChild(text);
+        //console.log("getTextInfo: ", width, height);
+        return {
+            width: width,
+            height: height,
+        };
+    }
+
+    function getTextInfo(fontName, fontSize, fontBold, htmlText) {
+        const canvas = document.getElementById("previewCanvas");
+        const ctx = canvas.getContext("2d");
+        ctx.fillStyle = "rgb(0,0,0)";
+        ctx.fillRect(0, 0, canvas.width, canvas.height);
+        const bold = fontBold ? "bold" : "";
+        ctx.font = `${bold} ${fontSize}pt ${fontName}`;
+        ctx.textBaseline = "hanging";
+        ctx.fillStyle = "rgb(255,0,0)";
+        ctx.fillText(htmlText, 0, 0);
+        const metrics = ctx.measureText(htmlText);
+        const textWidth = metrics.width;
+        const fontHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
+        const actualHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
+        //console.log(metrics);
+        // //console.log(fontHeight, actualHeight);
+        const width = Math.ceil(textWidth);
+        const height = Math.ceil(fontHeight);
+        return {
+            width: width,
+            height: height,
+        };
+    }
+
+    function drawPreviewForm(form) {
+        const canvas = document.getElementById("previewCanvas");
+        const ctx = canvas.getContext("2d");
+        ctx.fillStyle = form.color;
+        ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+        form.lines.forEach((line, idx) => {
+            line.texts.forEach((text, seq) => {
+                const bold = text.bold ? "bold" : "";
+                ctx.font = `${bold} ${text.size}pt ${text.name}`;
+                ctx.textBaseline = "hanging";
+                ctx.fillStyle = text.color;
+                ctx.fillText(text.text, text.x, text.y);
+            });
+        });
+    }
+}

+ 2 - 2
src/main/resources/static/application/op/06.vms/04.schedule/01.auto/auto.js

@@ -573,7 +573,7 @@ function delEvent(scheduleType){
         });
         return 
     }
-    retrun alertWarning(_contentMap.get(scheduleType).get('name') + ' 스케줄 목록에서 스케줄 정보를 선택해주세요');
+    return alertWarning(_contentMap.get(scheduleType).get('name') + ' 스케줄 목록에서 스케줄 정보를 선택해주세요');
 }
 
 //전체 VMS 등록 버튼 이벤트
@@ -592,7 +592,7 @@ function allSavePopup(scheduleType, trfcSituTypeCd){
         }
         return
     }
-    retrun alertWarning('VMS 스케줄 일괄 관리<br>등록되어 있는 스케줄 폼을 목록에서 먼저 선택하세요');
+    return alertWarning('VMS 스케줄 일괄 관리<br>등록되어 있는 스케줄 폼을 목록에서 먼저 선택하세요');
 }
 
 //전체 VMS 편집 버튼 이벤트

+ 2 - 0
src/main/resources/static/js/vworld/obj-facility.js

@@ -228,6 +228,8 @@ export class TVms extends TPoi {
         this.webCmraId = ADto.web_cmra_id;
         this.webCmraPwd = ADto.web_cmra_pwd;
         this.strmSesnNm = ADto.strm_sesn_nm;
+        this.strmHttpAddr = ADto.strm_http_addr;
+        this.strmRsptAddr = ADto.strm_rstp_addr;
     }
 }
 

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1580 - 0
src/main/resources/static/libs/dx-21.2.6/css/dx.yiits.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
src/main/resources/static/libs/dx-21.2.6/js/dx-quill.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
src/main/resources/static/libs/dx-21.2.6/js/showdown.min.js


+ 924 - 0
src/main/resources/static/libs/dx-21.2.6/js/turndown.js

@@ -0,0 +1,924 @@
+var TurndownService = (function () {
+    'use strict';
+  
+    function extend (destination) {
+      for (var i = 1; i < arguments.length; i++) {
+        var source = arguments[i];
+        for (var key in source) {
+          if (source.hasOwnProperty(key)) destination[key] = source[key];
+        }
+      }
+      return destination
+    }
+  
+    function repeat (character, count) {
+      return Array(count + 1).join(character)
+    }
+  
+    var blockElements = [
+      'address', 'article', 'aside', 'audio', 'blockquote', 'body', 'canvas',
+      'center', 'dd', 'dir', 'div', 'dl', 'dt', 'fieldset', 'figcaption',
+      'figure', 'footer', 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
+      'header', 'hgroup', 'hr', 'html', 'isindex', 'li', 'main', 'menu', 'nav',
+      'noframes', 'noscript', 'ol', 'output', 'p', 'pre', 'section', 'table',
+      'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'ul'
+    ];
+  
+    function isBlock (node) {
+      return blockElements.indexOf(node.nodeName.toLowerCase()) !== -1
+    }
+  
+    var voidElements = [
+      'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input',
+      'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'
+    ];
+  
+    function isVoid (node) {
+      return voidElements.indexOf(node.nodeName.toLowerCase()) !== -1
+    }
+  
+    var voidSelector = voidElements.join();
+    function hasVoid (node) {
+      return node.querySelector && node.querySelector(voidSelector)
+    }
+  
+    var rules = {};
+  
+    rules.paragraph = {
+      filter: 'p',
+  
+      replacement: function (content) {
+        return '\n\n' + content + '\n\n'
+      }
+    };
+  
+    rules.lineBreak = {
+      filter: 'br',
+  
+      replacement: function (content, node, options) {
+        return options.br + '\n'
+      }
+    };
+  
+    rules.heading = {
+      filter: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
+  
+      replacement: function (content, node, options) {
+        var hLevel = Number(node.nodeName.charAt(1));
+  
+        if (options.headingStyle === 'setext' && hLevel < 3) {
+          var underline = repeat((hLevel === 1 ? '=' : '-'), content.length);
+          return (
+            '\n\n' + content + '\n' + underline + '\n\n'
+          )
+        } else {
+          return '\n\n' + repeat('#', hLevel) + ' ' + content + '\n\n'
+        }
+      }
+    };
+  
+    rules.blockquote = {
+      filter: 'blockquote',
+  
+      replacement: function (content) {
+        content = content.replace(/^\n+|\n+$/g, '');
+        content = content.replace(/^/gm, '> ');
+        return '\n\n' + content + '\n\n'
+      }
+    };
+  
+    rules.list = {
+      filter: ['ul', 'ol'],
+  
+      replacement: function (content, node) {
+        var parent = node.parentNode;
+        if (parent.nodeName === 'LI' && parent.lastElementChild === node) {
+          return '\n' + content
+        } else {
+          return '\n\n' + content + '\n\n'
+        }
+      }
+    };
+  
+    rules.listItem = {
+      filter: 'li',
+  
+      replacement: function (content, node, options) {
+        content = content
+          .replace(/^\n+/, '') // remove leading newlines
+          .replace(/\n+$/, '\n') // replace trailing newlines with just a single one
+          .replace(/\n/gm, '\n    '); // indent
+        var prefix = options.bulletListMarker + '   ';
+        var parent = node.parentNode;
+        if (parent.nodeName === 'OL') {
+          var start = parent.getAttribute('start');
+          var index = Array.prototype.indexOf.call(parent.children, node);
+          prefix = (start ? Number(start) + index : index + 1) + '.  ';
+        }
+        return (
+          prefix + content + (node.nextSibling && !/\n$/.test(content) ? '\n' : '')
+        )
+      }
+    };
+  
+    rules.indentedCodeBlock = {
+      filter: function (node, options) {
+        return (
+          options.codeBlockStyle === 'indented' &&
+          node.nodeName === 'PRE' &&
+          node.firstChild &&
+          node.firstChild.nodeName === 'CODE'
+        )
+      },
+  
+      replacement: function (content, node, options) {
+        return (
+          '\n\n    ' +
+          node.firstChild.textContent.replace(/\n/g, '\n    ') +
+          '\n\n'
+        )
+      }
+    };
+  
+    rules.fencedCodeBlock = {
+      filter: function (node, options) {
+        return (
+          options.codeBlockStyle === 'fenced' &&
+          node.nodeName === 'PRE' &&
+          node.firstChild &&
+          node.firstChild.nodeName === 'CODE'
+        )
+      },
+  
+      replacement: function (content, node, options) {
+        var className = node.firstChild.className || '';
+        var language = (className.match(/language-(\S+)/) || [null, ''])[1];
+        var code = node.firstChild.textContent;
+  
+        var fenceChar = options.fence.charAt(0);
+        var fenceSize = 3;
+        var fenceInCodeRegex = new RegExp('^' + fenceChar + '{3,}', 'gm');
+  
+        var match;
+        while ((match = fenceInCodeRegex.exec(code))) {
+          if (match[0].length >= fenceSize) {
+            fenceSize = match[0].length + 1;
+          }
+        }
+  
+        var fence = repeat(fenceChar, fenceSize);
+  
+        return (
+          '\n\n' + fence + language + '\n' +
+          code.replace(/\n$/, '') +
+          '\n' + fence + '\n\n'
+        )
+      }
+    };
+  
+    rules.horizontalRule = {
+      filter: 'hr',
+  
+      replacement: function (content, node, options) {
+        return '\n\n' + options.hr + '\n\n'
+      }
+    };
+  
+    rules.inlineLink = {
+      filter: function (node, options) {
+        return (
+          options.linkStyle === 'inlined' &&
+          node.nodeName === 'A' &&
+          node.getAttribute('href')
+        )
+      },
+  
+      replacement: function (content, node) {
+        var href = node.getAttribute('href');
+        var title = node.title ? ' "' + node.title + '"' : '';
+        return '[' + content + '](' + href + title + ')'
+      }
+    };
+  
+    rules.referenceLink = {
+      filter: function (node, options) {
+        return (
+          options.linkStyle === 'referenced' &&
+          node.nodeName === 'A' &&
+          node.getAttribute('href')
+        )
+      },
+  
+      replacement: function (content, node, options) {
+        var href = node.getAttribute('href');
+        var title = node.title ? ' "' + node.title + '"' : '';
+        var replacement;
+        var reference;
+  
+        switch (options.linkReferenceStyle) {
+          case 'collapsed':
+            replacement = '[' + content + '][]';
+            reference = '[' + content + ']: ' + href + title;
+            break
+          case 'shortcut':
+            replacement = '[' + content + ']';
+            reference = '[' + content + ']: ' + href + title;
+            break
+          default:
+            var id = this.references.length + 1;
+            replacement = '[' + content + '][' + id + ']';
+            reference = '[' + id + ']: ' + href + title;
+        }
+  
+        this.references.push(reference);
+        return replacement
+      },
+  
+      references: [],
+  
+      append: function (options) {
+        var references = '';
+        if (this.references.length) {
+          references = '\n\n' + this.references.join('\n') + '\n\n';
+          this.references = []; // Reset references
+        }
+        return references
+      }
+    };
+  
+    rules.emphasis = {
+      filter: ['em', 'i'],
+  
+      replacement: function (content, node, options) {
+        if (!content.trim()) return ''
+        return options.emDelimiter + content + options.emDelimiter
+      }
+    };
+  
+    rules.strong = {
+      filter: ['strong', 'b'],
+  
+      replacement: function (content, node, options) {
+        if (!content.trim()) return ''
+        return options.strongDelimiter + content + options.strongDelimiter
+      }
+    };
+  
+    rules.code = {
+      filter: function (node) {
+        var hasSiblings = node.previousSibling || node.nextSibling;
+        var isCodeBlock = node.parentNode.nodeName === 'PRE' && !hasSiblings;
+  
+        return node.nodeName === 'CODE' && !isCodeBlock
+      },
+  
+      replacement: function (content) {
+        if (!content.trim()) return ''
+  
+        var delimiter = '`';
+        var leadingSpace = '';
+        var trailingSpace = '';
+        var matches = content.match(/`+/gm);
+        if (matches) {
+          if (/^`/.test(content)) leadingSpace = ' ';
+          if (/`$/.test(content)) trailingSpace = ' ';
+          while (matches.indexOf(delimiter) !== -1) delimiter = delimiter + '`';
+        }
+  
+        return delimiter + leadingSpace + content + trailingSpace + delimiter
+      }
+    };
+  
+    rules.image = {
+      filter: 'img',
+  
+      replacement: function (content, node) {
+        var alt = node.alt || '';
+        var src = node.getAttribute('src') || '';
+        var title = node.title || '';
+        var titlePart = title ? ' "' + title + '"' : '';
+        return src ? '![' + alt + ']' + '(' + src + titlePart + ')' : ''
+      }
+    };
+  
+    /**
+     * Manages a collection of rules used to convert HTML to Markdown
+     */
+  
+    function Rules (options) {
+      this.options = options;
+      this._keep = [];
+      this._remove = [];
+  
+      this.blankRule = {
+        replacement: options.blankReplacement
+      };
+  
+      this.keepReplacement = options.keepReplacement;
+  
+      this.defaultRule = {
+        replacement: options.defaultReplacement
+      };
+  
+      this.array = [];
+      for (var key in options.rules) this.array.push(options.rules[key]);
+    }
+  
+    Rules.prototype = {
+      add: function (key, rule) {
+        this.array.unshift(rule);
+      },
+  
+      keep: function (filter) {
+        this._keep.unshift({
+          filter: filter,
+          replacement: this.keepReplacement
+        });
+      },
+  
+      remove: function (filter) {
+        this._remove.unshift({
+          filter: filter,
+          replacement: function () {
+            return ''
+          }
+        });
+      },
+  
+      forNode: function (node) {
+        if (node.isBlank) return this.blankRule
+        var rule;
+  
+        if ((rule = findRule(this.array, node, this.options))) return rule
+        if ((rule = findRule(this._keep, node, this.options))) return rule
+        if ((rule = findRule(this._remove, node, this.options))) return rule
+  
+        return this.defaultRule
+      },
+  
+      forEach: function (fn) {
+        for (var i = 0; i < this.array.length; i++) fn(this.array[i], i);
+      }
+    };
+  
+    function findRule (rules, node, options) {
+      for (var i = 0; i < rules.length; i++) {
+        var rule = rules[i];
+        if (filterValue(rule, node, options)) return rule
+      }
+      return void 0
+    }
+  
+    function filterValue (rule, node, options) {
+      var filter = rule.filter;
+      if (typeof filter === 'string') {
+        if (filter === node.nodeName.toLowerCase()) return true
+      } else if (Array.isArray(filter)) {
+        if (filter.indexOf(node.nodeName.toLowerCase()) > -1) return true
+      } else if (typeof filter === 'function') {
+        if (filter.call(rule, node, options)) return true
+      } else {
+        throw new TypeError('`filter` needs to be a string, array, or function')
+      }
+    }
+  
+    /**
+     * The collapseWhitespace function is adapted from collapse-whitespace
+     * by Luc Thevenard.
+     *
+     * The MIT License (MIT)
+     *
+     * Copyright (c) 2014 Luc Thevenard <lucthevenard@gmail.com>
+     *
+     * Permission is hereby granted, free of charge, to any person obtaining a copy
+     * of this software and associated documentation files (the "Software"), to deal
+     * in the Software without restriction, including without limitation the rights
+     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+     * copies of the Software, and to permit persons to whom the Software is
+     * furnished to do so, subject to the following conditions:
+     *
+     * The above copyright notice and this permission notice shall be included in
+     * all copies or substantial portions of the Software.
+     *
+     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+     * THE SOFTWARE.
+     */
+  
+    /**
+     * collapseWhitespace(options) removes extraneous whitespace from an the given element.
+     *
+     * @param {Object} options
+     */
+    function collapseWhitespace (options) {
+      var element = options.element;
+      var isBlock = options.isBlock;
+      var isVoid = options.isVoid;
+      var isPre = options.isPre || function (node) {
+        return node.nodeName === 'PRE'
+      };
+  
+      if (!element.firstChild || isPre(element)) return
+  
+      var prevText = null;
+      var prevVoid = false;
+  
+      var prev = null;
+      var node = next(prev, element, isPre);
+  
+      while (node !== element) {
+        if (node.nodeType === 3 || node.nodeType === 4) { // Node.TEXT_NODE or Node.CDATA_SECTION_NODE
+          var text = node.data.replace(/[ \r\n\t]+/g, ' ');
+  
+          if ((!prevText || / $/.test(prevText.data)) &&
+              !prevVoid && text[0] === ' ') {
+            text = text.substr(1);
+          }
+  
+          // `text` might be empty at this point.
+          if (!text) {
+            node = remove(node);
+            continue
+          }
+  
+          node.data = text;
+  
+          prevText = node;
+        } else if (node.nodeType === 1) { // Node.ELEMENT_NODE
+          if (isBlock(node) || node.nodeName === 'BR') {
+            if (prevText) {
+              prevText.data = prevText.data.replace(/ $/, '');
+            }
+  
+            prevText = null;
+            prevVoid = false;
+          } else if (isVoid(node)) {
+            // Avoid trimming space around non-block, non-BR void elements.
+            prevText = null;
+            prevVoid = true;
+          }
+        } else {
+          node = remove(node);
+          continue
+        }
+  
+        var nextNode = next(prev, node, isPre);
+        prev = node;
+        node = nextNode;
+      }
+  
+      if (prevText) {
+        prevText.data = prevText.data.replace(/ $/, '');
+        if (!prevText.data) {
+          remove(prevText);
+        }
+      }
+    }
+  
+    /**
+     * remove(node) removes the given node from the DOM and returns the
+     * next node in the sequence.
+     *
+     * @param {Node} node
+     * @return {Node} node
+     */
+    function remove (node) {
+      var next = node.nextSibling || node.parentNode;
+  
+      node.parentNode.removeChild(node);
+  
+      return next
+    }
+  
+    /**
+     * next(prev, current, isPre) returns the next node in the sequence, given the
+     * current and previous nodes.
+     *
+     * @param {Node} prev
+     * @param {Node} current
+     * @param {Function} isPre
+     * @return {Node}
+     */
+    function next (prev, current, isPre) {
+      if ((prev && prev.parentNode === current) || isPre(current)) {
+        return current.nextSibling || current.parentNode
+      }
+  
+      return current.firstChild || current.nextSibling || current.parentNode
+    }
+  
+    /*
+     * Set up window for Node.js
+     */
+  
+    var root = (typeof window !== 'undefined' ? window : {});
+  
+    /*
+     * Parsing HTML strings
+     */
+  
+    function canParseHTMLNatively () {
+      var Parser = root.DOMParser;
+      var canParse = false;
+  
+      // Adapted from https://gist.github.com/1129031
+      // Firefox/Opera/IE throw errors on unsupported types
+      try {
+        // WebKit returns null on unsupported types
+        if (new Parser().parseFromString('', 'text/html')) {
+          canParse = true;
+        }
+      } catch (e) {}
+  
+      return canParse
+    }
+  
+    function createHTMLParser () {
+      var Parser = function () {};
+  
+      {
+        if (shouldUseActiveX()) {
+          Parser.prototype.parseFromString = function (string) {
+            var doc = new window.ActiveXObject('htmlfile');
+            doc.designMode = 'on'; // disable on-page scripts
+            doc.open();
+            doc.write(string);
+            doc.close();
+            return doc
+          };
+        } else {
+          Parser.prototype.parseFromString = function (string) {
+            var doc = document.implementation.createHTMLDocument('');
+            doc.open();
+            doc.write(string);
+            doc.close();
+            return doc
+          };
+        }
+      }
+      return Parser
+    }
+  
+    function shouldUseActiveX () {
+      var useActiveX = false;
+      try {
+        document.implementation.createHTMLDocument('').open();
+      } catch (e) {
+        if (window.ActiveXObject) useActiveX = true;
+      }
+      return useActiveX
+    }
+  
+    var HTMLParser = canParseHTMLNatively() ? root.DOMParser : createHTMLParser();
+  
+    function RootNode (input) {
+      var root;
+      if (typeof input === 'string') {
+        var doc = htmlParser().parseFromString(
+          // DOM parsers arrange elements in the <head> and <body>.
+          // Wrapping in a custom element ensures elements are reliably arranged in
+          // a single element.
+          '<x-turndown id="turndown-root">' + input + '</x-turndown>',
+          'text/html'
+        );
+        root = doc.getElementById('turndown-root');
+      } else {
+        root = input.cloneNode(true);
+      }
+      collapseWhitespace({
+        element: root,
+        isBlock: isBlock,
+        isVoid: isVoid
+      });
+  
+      return root
+    }
+  
+    var _htmlParser;
+    function htmlParser () {
+      _htmlParser = _htmlParser || new HTMLParser();
+      return _htmlParser
+    }
+  
+    function Node (node) {
+      node.isBlock = isBlock(node);
+      node.isCode = node.nodeName.toLowerCase() === 'code' || node.parentNode.isCode;
+      node.isBlank = isBlank(node);
+      node.flankingWhitespace = flankingWhitespace(node);
+      return node
+    }
+  
+    function isBlank (node) {
+      return (
+        ['A', 'TH', 'TD', 'IFRAME', 'SCRIPT', 'AUDIO', 'VIDEO'].indexOf(node.nodeName) === -1 &&
+        /^\s*$/i.test(node.textContent) &&
+        !isVoid(node) &&
+        !hasVoid(node)
+      )
+    }
+  
+    function flankingWhitespace (node) {
+      var leading = '';
+      var trailing = '';
+  
+      if (!node.isBlock) {
+        var hasLeading = /^\s/.test(node.textContent);
+        var hasTrailing = /\s$/.test(node.textContent);
+        var blankWithSpaces = node.isBlank && hasLeading && hasTrailing;
+  
+        if (hasLeading && !isFlankedByWhitespace('left', node)) {
+          leading = ' ';
+        }
+  
+        if (!blankWithSpaces && hasTrailing && !isFlankedByWhitespace('right', node)) {
+          trailing = ' ';
+        }
+      }
+  
+      return { leading: leading, trailing: trailing }
+    }
+  
+    function isFlankedByWhitespace (side, node) {
+      var sibling;
+      var regExp;
+      var isFlanked;
+  
+      if (side === 'left') {
+        sibling = node.previousSibling;
+        regExp = / $/;
+      } else {
+        sibling = node.nextSibling;
+        regExp = /^ /;
+      }
+  
+      if (sibling) {
+        if (sibling.nodeType === 3) {
+          isFlanked = regExp.test(sibling.nodeValue);
+        } else if (sibling.nodeType === 1 && !isBlock(sibling)) {
+          isFlanked = regExp.test(sibling.textContent);
+        }
+      }
+      return isFlanked
+    }
+  
+    var reduce = Array.prototype.reduce;
+    var leadingNewLinesRegExp = /^\n*/;
+    var trailingNewLinesRegExp = /\n*$/;
+    var escapes = [
+      [/\\/g, '\\\\'],
+      [/\*/g, '\\*'],
+      [/^-/g, '\\-'],
+      [/^\+ /g, '\\+ '],
+      [/^(=+)/g, '\\$1'],
+      [/^(#{1,6}) /g, '\\$1 '],
+      [/`/g, '\\`'],
+      [/^~~~/g, '\\~~~'],
+      [/\[/g, '\\['],
+      [/\]/g, '\\]'],
+      [/^>/g, '\\>'],
+      [/_/g, '\\_'],
+      [/^(\d+)\. /g, '$1\\. ']
+    ];
+  
+    function TurndownService (options) {
+      if (!(this instanceof TurndownService)) return new TurndownService(options)
+  
+      var defaults = {
+        rules: rules,
+        headingStyle: 'setext',
+        hr: '* * *',
+        bulletListMarker: '*',
+        codeBlockStyle: 'indented',
+        fence: '```',
+        emDelimiter: '_',
+        strongDelimiter: '**',
+        linkStyle: 'inlined',
+        linkReferenceStyle: 'full',
+        br: '  ',
+        blankReplacement: function (content, node) {
+          return node.isBlock ? '\n\n' : ''
+        },
+        keepReplacement: function (content, node) {
+          return node.isBlock ? '\n\n' + node.outerHTML + '\n\n' : node.outerHTML
+        },
+        defaultReplacement: function (content, node) {
+          return node.isBlock ? '\n\n' + content + '\n\n' : content
+        }
+      };
+      this.options = extend({}, defaults, options);
+      this.rules = new Rules(this.options);
+    }
+  
+    TurndownService.prototype = {
+      /**
+       * The entry point for converting a string or DOM node to Markdown
+       * @public
+       * @param {String|HTMLElement} input The string or DOM node to convert
+       * @returns A Markdown representation of the input
+       * @type String
+       */
+  
+      turndown: function (input) {
+        if (!canConvert(input)) {
+          throw new TypeError(
+            input + ' is not a string, or an element/document/fragment node.'
+          )
+        }
+  
+        if (input === '') return ''
+  
+        var output = process.call(this, new RootNode(input));
+        return postProcess.call(this, output)
+      },
+  
+      /**
+       * Add one or more plugins
+       * @public
+       * @param {Function|Array} plugin The plugin or array of plugins to add
+       * @returns The Turndown instance for chaining
+       * @type Object
+       */
+  
+      use: function (plugin) {
+        if (Array.isArray(plugin)) {
+          for (var i = 0; i < plugin.length; i++) this.use(plugin[i]);
+        } else if (typeof plugin === 'function') {
+          plugin(this);
+        } else {
+          throw new TypeError('plugin must be a Function or an Array of Functions')
+        }
+        return this
+      },
+  
+      /**
+       * Adds a rule
+       * @public
+       * @param {String} key The unique key of the rule
+       * @param {Object} rule The rule
+       * @returns The Turndown instance for chaining
+       * @type Object
+       */
+  
+      addRule: function (key, rule) {
+        this.rules.add(key, rule);
+        return this
+      },
+  
+      /**
+       * Keep a node (as HTML) that matches the filter
+       * @public
+       * @param {String|Array|Function} filter The unique key of the rule
+       * @returns The Turndown instance for chaining
+       * @type Object
+       */
+  
+      keep: function (filter) {
+        this.rules.keep(filter);
+        return this
+      },
+  
+      /**
+       * Remove a node that matches the filter
+       * @public
+       * @param {String|Array|Function} filter The unique key of the rule
+       * @returns The Turndown instance for chaining
+       * @type Object
+       */
+  
+      remove: function (filter) {
+        this.rules.remove(filter);
+        return this
+      },
+  
+      /**
+       * Escapes Markdown syntax
+       * @public
+       * @param {String} string The string to escape
+       * @returns A string with Markdown syntax escaped
+       * @type String
+       */
+  
+      escape: function (string) {
+        return escapes.reduce(function (accumulator, escape) {
+          return accumulator.replace(escape[0], escape[1])
+        }, string)
+      }
+    };
+  
+    /**
+     * Reduces a DOM node down to its Markdown string equivalent
+     * @private
+     * @param {HTMLElement} parentNode The node to convert
+     * @returns A Markdown representation of the node
+     * @type String
+     */
+  
+    function process (parentNode) {
+      var self = this;
+      return reduce.call(parentNode.childNodes, function (output, node) {
+        node = new Node(node);
+  
+        var replacement = '';
+        if (node.nodeType === 3) {
+          replacement = node.isCode ? node.nodeValue : self.escape(node.nodeValue);
+        } else if (node.nodeType === 1) {
+          replacement = replacementForNode.call(self, node);
+        }
+  
+        return join(output, replacement)
+      }, '')
+    }
+  
+    /**
+     * Appends strings as each rule requires and trims the output
+     * @private
+     * @param {String} output The conversion output
+     * @returns A trimmed version of the ouput
+     * @type String
+     */
+  
+    function postProcess (output) {
+      var self = this;
+      this.rules.forEach(function (rule) {
+        if (typeof rule.append === 'function') {
+          output = join(output, rule.append(self.options));
+        }
+      });
+  
+      return output.replace(/^[\t\r\n]+/, '').replace(/[\t\r\n\s]+$/, '')
+    }
+  
+    /**
+     * Converts an element node to its Markdown equivalent
+     * @private
+     * @param {HTMLElement} node The node to convert
+     * @returns A Markdown representation of the node
+     * @type String
+     */
+  
+    function replacementForNode (node) {
+      var rule = this.rules.forNode(node);
+      var content = process.call(this, node);
+      var whitespace = node.flankingWhitespace;
+      if (whitespace.leading || whitespace.trailing) content = content.trim();
+      return (
+        whitespace.leading +
+        rule.replacement(content, node, this.options) +
+        whitespace.trailing
+      )
+    }
+  
+    /**
+     * Determines the new lines between the current output and the replacement
+     * @private
+     * @param {String} output The current conversion output
+     * @param {String} replacement The string to append to the output
+     * @returns The whitespace to separate the current output and the replacement
+     * @type String
+     */
+  
+    function separatingNewlines (output, replacement) {
+      var newlines = [
+        output.match(trailingNewLinesRegExp)[0],
+        replacement.match(leadingNewLinesRegExp)[0]
+      ].sort();
+      var maxNewlines = newlines[newlines.length - 1];
+      return maxNewlines.length < 2 ? maxNewlines : '\n\n'
+    }
+  
+    function join (string1, string2) {
+      var separator = separatingNewlines(string1, string2);
+  
+      // Remove trailing/leading newlines and replace with separator
+      string1 = string1.replace(trailingNewLinesRegExp, '');
+      string2 = string2.replace(leadingNewLinesRegExp, '');
+  
+      return string1 + separator + string2
+    }
+  
+    /**
+     * Determines whether an input can be converted
+     * @private
+     * @param {String|HTMLElement} input Describe this parameter
+     * @returns Describe what it returns
+     * @type String|Object|Array|Boolean|Number
+     */
+  
+    function canConvert (input) {
+      return (
+        input != null && (
+          typeof input === 'string' ||
+          (input.nodeType && (
+            input.nodeType === 1 || input.nodeType === 9 || input.nodeType === 11
+          ))
+        )
+      )
+    }
+  
+    return TurndownService;
+  
+  }());

BIN
src/main/resources/static/libs/fonts/batang.ttf


BIN
src/main/resources/static/libs/fonts/gulim.ttf


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 404 - 0
src/main/resources/static/libs/html2.canvas/html2.canvas-1.4.1.js


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott