├── nodemon.json ├── favicon.ico ├── docs └── layout.png ├── frontend ├── icon.png ├── favicon.ico ├── manifest.json ├── css │ ├── fullscreen.svg │ ├── filebrowser.css │ ├── index.css │ ├── folder.svg │ └── vendor │ │ └── jquery.modal.min.css ├── index.html ├── filebrowser.html └── js │ ├── vendor │ ├── jquery.modal.min.js │ └── hammer.min.js │ └── filebrowser.js ├── .gitignore ├── .github └── workflows │ ├── call_issues_cron.yml │ └── call_issue_pr_tracker.yml ├── package.json ├── setup_dev.sh ├── metadata ├── doom.json ├── vb.json ├── vectrex.json ├── atari5200.json └── jaguar.json ├── public ├── js │ ├── vendor │ │ ├── theme-chrome.js │ │ ├── mode-json.js │ │ └── worker-json.js │ └── index.js ├── css │ └── index.css └── index.html ├── profile.js ├── README.md └── has_files.sh /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": "frontend/user" 3 | } 4 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxserver/emulatorjs/HEAD/favicon.ico -------------------------------------------------------------------------------- /docs/layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxserver/emulatorjs/HEAD/docs/layout.png -------------------------------------------------------------------------------- /frontend/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxserver/emulatorjs/HEAD/frontend/icon.png -------------------------------------------------------------------------------- /frontend/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxserver/emulatorjs/HEAD/frontend/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | package-lock.json 3 | node_modules/ 4 | frontend/README.md 5 | frontend/user 6 | frontend/data/ 7 | frontend/decrypt\ tools/ 8 | -------------------------------------------------------------------------------- /.github/workflows/call_issues_cron.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | on: 3 | schedule: 4 | - cron: '35 15 * * *' 5 | workflow_dispatch: 6 | 7 | jobs: 8 | stale: 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | uses: linuxserver/github-workflows/.github/workflows/issues-cron.yml@v1 13 | secrets: inherit 14 | -------------------------------------------------------------------------------- /frontend/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "EmulatorJS", 3 | "short_name": "EmulatorJS", 4 | "manifest_version": 2, 5 | "version": "1.9.2", 6 | "display": "standalone", 7 | "background_color": "#000000", 8 | "theme_color": "#000000", 9 | "icons": [ 10 | { 11 | "src": "icon.png", 12 | "type": "image/png", 13 | "sizes": "500x500" 14 | } 15 | ], 16 | "start_url": "/" 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "emulatorjs", 3 | "version": "1.9.2", 4 | "description": "Rom and art management utility for generating configuration files for use with the EmulatorJS frontend", 5 | "main": "index.js", 6 | "author": "thelamer", 7 | "dependencies": { 8 | "cloudcmd": "^15.9.14", 9 | "deepmerge": "^4.2.2", 10 | "express": "^4.17.2", 11 | "ipfs-http-client": "51.0.0", 12 | "jszip": "^3.7.1", 13 | "socket.io": "^4.4.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/call_issue_pr_tracker.yml: -------------------------------------------------------------------------------- 1 | name: Issue & PR Tracker 2 | 3 | on: 4 | issues: 5 | types: [opened,reopened,labeled,unlabeled,closed] 6 | pull_request_target: 7 | types: [opened,reopened,review_requested,review_request_removed,labeled,unlabeled,closed] 8 | pull_request_review: 9 | types: [submitted,edited,dismissed] 10 | 11 | jobs: 12 | manage-project: 13 | permissions: 14 | issues: write 15 | uses: linuxserver/github-workflows/.github/workflows/issue-pr-tracker.yml@v1 16 | secrets: inherit 17 | -------------------------------------------------------------------------------- /setup_dev.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | IFS=$'\n' 4 | rootdir=$(pwd) 5 | # Install modules 6 | npm install 7 | ## Grab frontend blobs 8 | # EmulatorJS 9 | curl -o \ 10 | /tmp/emulatorjs-blob.tar.gz -L \ 11 | "https://github.com/thelamer/emulatorjs/archive/main.tar.gz" 12 | tar xf \ 13 | /tmp/emulatorjs-blob.tar.gz -C \ 14 | frontend/ --strip-components=1 15 | # Custom cores 16 | curl -o \ 17 | /tmp/custom-cores.tar.gz -L \ 18 | "https://github.com/linuxserver/libretro-cores/archive/master.tar.gz" && \ 19 | tar xf \ 20 | /tmp/custom-cores.tar.gz -C \ 21 | frontend/ --strip-components=1 22 | rm frontend/README.md 23 | # Default folders 24 | if [ ! -e 'fontend/user' ]; then 25 | if [ -d '/data' ]; then 26 | ln -s /data frontend/user 27 | else 28 | mkdir -p frontend/user 29 | fi 30 | fi 31 | -------------------------------------------------------------------------------- /frontend/css/fullscreen.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /metadata/doom.json: -------------------------------------------------------------------------------- 1 | { 2 | "2C8212631B37F21AD06D18B5638C733A75E179FF ": { 3 | "logo": "QmSNo8VrwLg1NRWAMhm2qc3zWFF7HK8563sAeU4Hu6DQ8u", 4 | "name": "Doom", 5 | "vid": "QmTuyvkgZ7H4kxLN5ws7k71uGsa2pc2rEBKCHTJY6mzvTs" 6 | }, 7 | "7EC7652FCFCE8DDC6E801839291F0E28EF1D5AE7": { 8 | "logo": "QmPZaGnVHshwHropCW95HtNTUQSDUbfK9vFa2oQjVqcVkZ", 9 | "name": "Doom II", 10 | "vid": "Qmamv4bz1HVQWHRKbrbEWPUuAtUEhEVXJxqDYz2ui4gCaD" 11 | }, 12 | "90361E2A538D2388506657252AE41ACEEB1BA360": { 13 | "logo": "QmQEN7NGWPw4YbvS49RoQBgKNbRT9V3EynijAFcxAiJKCE", 14 | "name": "Doom The Plutonia Experiment" 15 | }, 16 | "9B07B02AB3C275A6A7570C3F73CC20D63A0E3833": { 17 | "logo": "QmNyBkiShukMmib9qndrZbovqX9ycdQE3UzZSLHSFtXdU7", 18 | "name": "The Ultimate Doom" 19 | }, 20 | "9FBC66AEDEF7FE3BAE0986CDB9323D2B8DB4C9D3": { 21 | "logo": "Qma4PUdncsFtsWnFbeS1qVSyRpqsLD2J7JUaz37da2DtjQ", 22 | "name": "Doom TNT: Evilution" 23 | }, 24 | "C6612AC5A8AC2E2A1D707F9B2869AF820EFB7C50": { 25 | "name": "Doom (Demo)", 26 | "ref": "2C8212631B37F21AD06D18B5638C733A75E179FF" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /frontend/css/filebrowser.css: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none; 3 | } 4 | 5 | .right { 6 | float: right; 7 | margin-right: 5px; 8 | } 9 | 10 | .directory, .file { 11 | cursor: pointer; 12 | } 13 | 14 | button { 15 | border: 2px solid #000; 16 | background: transparent; 17 | cursor: pointer; 18 | margin: 5px; 19 | } 20 | 21 | .deleteButton { 22 | margin: 0px !important; 23 | float: right; 24 | } 25 | 26 | .fileTable { 27 | font-family: Arial, Helvetica, sans-serif; 28 | border-collapse: collapse; 29 | width: 100%; 30 | margin-top: 10px; 31 | } 32 | 33 | td, th { 34 | border: 2px solid #ddd; 35 | padding: 8px; 36 | } 37 | 38 | tr:nth-child(even){ 39 | background-color: #f2f2f2; 40 | } 41 | 42 | tr:hover, button:hover { 43 | background-color: #ddd; 44 | } 45 | 46 | #dropzone { 47 | position: fixed; top: 0; left: 0; 48 | z-index: 9999999999; 49 | width: 100%; height: 100%; 50 | background-color: rgba(0,0,0,0.5); 51 | transition: visibility 175ms, opacity 175ms; 52 | } 53 | 54 | #loading { 55 | display: inline-block; 56 | width: 50px; 57 | height: 50px; 58 | border: 3px solid rgba(0,0,0,.3); 59 | border-radius: 50%; 60 | border-top-color: black; 61 | animation: spin 1s ease-in-out infinite; 62 | -webkit-animation: spin 1s ease-in-out infinite; 63 | } 64 | 65 | @keyframes spin { 66 | to { -webkit-transform: rotate(360deg); } 67 | } 68 | @-webkit-keyframes spin { 69 | to { -webkit-transform: rotate(360deg); } 70 | } 71 | -------------------------------------------------------------------------------- /frontend/css/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | background-color: black; 3 | overflow: hidden; 4 | } 5 | 6 | canvas { 7 | border: none; 8 | outline: none; 9 | } 10 | 11 | #menu { 12 | visibility: visible; 13 | position: fixed; 14 | left: 0; 15 | top: 0; 16 | width: 100vw; 17 | height: 100vh; 18 | } 19 | 20 | .page-container { 21 | height: auto; 22 | } 23 | 24 | .games-list { 25 | position:fixed; 26 | top: 5px; 27 | right: 20px; 28 | height: 100vh; 29 | } 30 | 31 | .artwork { 32 | width: auto; 33 | } 34 | 35 | .hidden { 36 | visibility: hidden; 37 | } 38 | 39 | .menu-wrap { 40 | display:flex; 41 | justify-content: center; 42 | align-items: center; 43 | height: 100%; 44 | } 45 | 46 | .menu-img { 47 | color: white; 48 | font-family: Arial, Helvetica, sans-serif; 49 | font-size: 4vh; 50 | } 51 | 52 | #background { 53 | position: fixed; 54 | left: 0; 55 | top: 0; 56 | width: 100vw; 57 | height: 100vh; 58 | } 59 | 60 | #corner { 61 | position: fixed; 62 | left: 0; 63 | bottom: 0; 64 | height: 50vh; 65 | } 66 | 67 | #game { 68 | text-align: center; 69 | color: white; 70 | } 71 | 72 | #startEmu { 73 | position: absolute; 74 | top: 50%; 75 | left: 50%; 76 | transform: translate(-50%, -50%); 77 | color: white; 78 | background: transparent; 79 | cursor: pointer; 80 | font-size: 5vw; 81 | border: 3px solid; 82 | border-radius: 1vw; 83 | padding: 1vw; 84 | } 85 | 86 | .file-button { 87 | position: absolute; 88 | top: 1vh; 89 | left: 1vw; 90 | cursor: pointer; 91 | border: none; 92 | width: 1.7vw; 93 | z-index: 999999; 94 | filter: invert(100%) sepia(0%) saturate(0%) hue-rotate(82deg) brightness(105%) contrast(105%); 95 | } 96 | 97 | .full-main { 98 | position: absolute; 99 | top: 1vh; 100 | left: 4vw; 101 | cursor: pointer; 102 | border: none; 103 | width: 1.7vw; 104 | z-index: 999999; 105 | filter: invert(100%) sepia(0%) saturate(0%) hue-rotate(82deg) brightness(105%) contrast(105%); 106 | } 107 | 108 | @keyframes growonce { 109 | 0% { 110 | transform: scale(.5,.5) translate(15vw,0); 111 | } 112 | 100% { 113 | transform: scale(1.0,1.0) translate(0,0); 114 | } 115 | } 116 | 117 | .grow { 118 | animation: growonce .3s forwards; 119 | } 120 | 121 | .shrink { 122 | transform: scale(.5,.5) translate(15vw,0); 123 | } 124 | 125 | @keyframes growonce-mobile { 126 | 0% { 127 | transform: scale(.5,.5); 128 | } 129 | 100% { 130 | transform: scale(1.0,1.0); 131 | } 132 | } 133 | 134 | .grow-mobile { 135 | animation: growonce-mobile .3s forwards; 136 | } 137 | 138 | .shrink-mobile { 139 | transform: scale(.5,.5); 140 | } 141 | -------------------------------------------------------------------------------- /frontend/filebrowser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |TouchScreen control type: 41 | 42 |
43 |File browser:
46 | 55 | 56 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /public/js/vendor/theme-chrome.js: -------------------------------------------------------------------------------- 1 | define("ace/theme/chrome",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-chrome",t.cssText='.ace-chrome .ace_gutter {background: #ebebeb;color: #333;overflow : hidden;}.ace-chrome .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-chrome {background-color: #FFFFFF;color: black;}.ace-chrome .ace_cursor {color: black;}.ace-chrome .ace_invisible {color: rgb(191, 191, 191);}.ace-chrome .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-chrome .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-chrome .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-chrome .ace_invalid {background-color: rgb(153, 0, 0);color: white;}.ace-chrome .ace_fold {}.ace-chrome .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-chrome .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-chrome .ace_support.ace_type,.ace-chrome .ace_support.ace_class.ace-chrome .ace_support.ace_other {color: rgb(109, 121, 222);}.ace-chrome .ace_variable.ace_parameter {font-style:italic;color:#FD971F;}.ace-chrome .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-chrome .ace_comment {color: #236e24;}.ace-chrome .ace_comment.ace_doc {color: #236e24;}.ace-chrome .ace_comment.ace_doc.ace_tag {color: #236e24;}.ace-chrome .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-chrome .ace_variable {color: rgb(49, 132, 149);}.ace-chrome .ace_xml-pe {color: rgb(104, 104, 91);}.ace-chrome .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-chrome .ace_heading {color: rgb(12, 7, 255);}.ace-chrome .ace_list {color:rgb(185, 6, 144);}.ace-chrome .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-chrome .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-chrome .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-chrome .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-chrome .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-chrome .ace_gutter-active-line {background-color : #dcdcdc;}.ace-chrome .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-chrome .ace_storage,.ace-chrome .ace_keyword,.ace-chrome .ace_meta.ace_tag {color: rgb(147, 15, 128);}.ace-chrome .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-chrome .ace_string {color: #1A1AA6;}.ace-chrome .ace_entity.ace_other.ace_attribute-name {color: #994409;}.ace-chrome .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass,!1)}); (function() { 2 | window.require(["ace/theme/chrome"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /frontend/css/folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/css/vendor/jquery.modal.min.css: -------------------------------------------------------------------------------- 1 | .blocker{position:fixed;top:0;right:0;bottom:0;left:0;width:100%;height:100%;overflow:auto;z-index:1;padding:20px;box-sizing:border-box;background-color:#000;background-color:rgba(0,0,0,0.75);text-align:center}.blocker:before{content:"";display:inline-block;height:100%;vertical-align:middle;margin-right:-0.05em}.blocker.behind{background-color:transparent}.modal{display:none;vertical-align:middle;position:relative;z-index:2;max-width:500px;box-sizing:border-box;width:90%;background:#fff;padding:15px 30px;-webkit-border-radius:8px;-moz-border-radius:8px;-o-border-radius:8px;-ms-border-radius:8px;border-radius:8px;-webkit-box-shadow:0 0 10px #000;-moz-box-shadow:0 0 10px #000;-o-box-shadow:0 0 10px #000;-ms-box-shadow:0 0 10px #000;box-shadow:0 0 10px #000;text-align:left}.modal a.close-modal{position:absolute;top:-12.5px;right:-12.5px;display:block;width:30px;height:30px;text-indent:-9999px;background-size:contain;background-repeat:no-repeat;background-position:center center;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAAAXNSR0IArs4c6QAAA3hJREFUaAXlm8+K00Acx7MiCIJH/yw+gA9g25O49SL4AO3Bp1jw5NvktC+wF88qevK4BU97EmzxUBCEolK/n5gp3W6TTJPfpNPNF37MNsl85/vN/DaTmU6PknC4K+pniqeKJ3k8UnkvDxXJzzy+q/yaxxeVHxW/FNHjgRSeKt4rFoplzaAuHHDBGR2eS9G54reirsmienDCTRt7xwsp+KAoEmt9nLaGitZxrBbPFNaGfPloGw2t4JVamSt8xYW6Dg1oCYo3Yv+rCGViV160oMkcd8SYKnYV1Nb1aEOjCe6L5ZOiLfF120EjWhuBu3YIZt1NQmujnk5F4MgOpURzLfAwOBSTmzp3fpDxuI/pabxpqOoz2r2HLAb0GMbZKlNV5/Hg9XJypguryA7lPF5KMdTZQzHjqxNPhWhzIuAruOl1eNqKEx1tSh5rfbxdw7mOxCq4qS68ZTjKS1YVvilu559vWvFHhh4rZrdyZ69Vmpgdj8fJbDZLJpNJ0uv1cnr/gjrUhQMuI+ANjyuwftQ0bbL6Erp0mM/ny8Fg4M3LtdRxgMtKl3jwmIHVxYXChFy94/Rmpa/pTbNUhstKV+4Rr8lLQ9KlUvJKLyG8yvQ2s9SBy1Jb7jV5a0yapfF6apaZLjLLcWtd4sNrmJUMHyM+1xibTjH82Zh01TNlhsrOhdKTe00uAzZQmN6+KW+sDa/JD2PSVQ873m29yf+1Q9VDzfEYlHi1G5LKBBWZbtEsHbFwb1oYDwr1ZiF/2bnCSg1OBE/pfr9/bWx26UxJL3ONPISOLKUvQza0LZUxSKyjpdTGa/vDEr25rddbMM0Q3O6Lx3rqFvU+x6UrRKQY7tyrZecmD9FODy8uLizTmilwNj0kraNcAJhOp5aGVwsAGD5VmJBrWWbJSgWT9zrzWepQF47RaGSiKfeGx6Szi3gzmX/HHbihwBser4B9UJYpFBNX4R6vTn3VQnez0SymnrHQMsRYGTr1dSk34ljRqS/EMd2pLQ8YBp3a1PLfcqCpo8gtHkZFHKkTX6fs3MY0blKnth66rKCnU0VRGu37ONrQaA4eZDFtWAu2fXj9zjFkxTBOo8F7t926gTp/83Kyzzcy2kZD6xiqxTYnHLRFm3vHiRSwNSjkz3hoIzo8lCKWUlg/YtGs7tObunDAZfpDLbfEI15zsEIY3U/x/gHHc/G1zltnAgAAAABJRU5ErkJggg==')}.modal-spinner{display:none;position:fixed;top:50%;left:50%;transform:translateY(-50%) translateX(-50%);padding:12px 16px;border-radius:5px;background-color:#111;height:20px}.modal-spinner>div{border-radius:100px;background-color:#fff;height:20px;width:2px;margin:0 1px;display:inline-block;-webkit-animation:sk-stretchdelay 1.2s infinite ease-in-out;animation:sk-stretchdelay 1.2s infinite ease-in-out}.modal-spinner .rect2{-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.modal-spinner .rect3{-webkit-animation-delay:-1.0s;animation-delay:-1.0s}.modal-spinner .rect4{-webkit-animation-delay:-0.9s;animation-delay:-0.9s}@-webkit-keyframes sk-stretchdelay{0%,40%,100%{-webkit-transform:scaleY(0.5)}20%{-webkit-transform:scaleY(1.0)}}@keyframes sk-stretchdelay{0%,40%,100%{transform:scaleY(0.5);-webkit-transform:scaleY(0.5)}20%{transform:scaleY(1.0);-webkit-transform:scaleY(1.0)}} -------------------------------------------------------------------------------- /frontend/js/vendor/jquery.modal.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | A simple jQuery modal (http://github.com/kylefox/jquery-modal) 3 | Version 0.9.1 4 | */ 5 | !function(o){"object"==typeof module&&"object"==typeof module.exports?o(require("jquery"),window,document):o(jQuery,window,document)}(function(o,t,i,e){var s=[],l=function(){return s.length?s[s.length-1]:null},n=function(){var o,t=!1;for(o=s.length-1;o>=0;o--)s[o].$blocker&&(s[o].$blocker.toggleClass("current",!t).toggleClass("behind",t),t=!0)};o.modal=function(t,i){var e,n;if(this.$body=o("body"),this.options=o.extend({},o.modal.defaults,i),this.options.doFade=!isNaN(parseInt(this.options.fadeDuration,10)),this.$blocker=null,this.options.closeExisting)for(;o.modal.isActive();)o.modal.close();if(s.push(this),t.is("a"))if(n=t.attr("href"),this.anchor=t,/^#/.test(n)){if(this.$elm=o(n),1!==this.$elm.length)return null;this.$body.append(this.$elm),this.open()}else this.$elm=o("| ').text(name)); 32 | } 33 | let parentRow = $(' | ||
|---|---|---|
| ').text('Parent'), $(' | ')]) { 35 | parentRow.append(item); 36 | } 37 | table.append(tableHeader,parentRow); 38 | $('#filebrowser').append(table); 39 | items.sort(); 40 | if (items.length > 0) { 41 | let dirs = []; 42 | let files = []; 43 | for await (let item of items) { 44 | if (fs.lstatSync(directory + '/' + item).isDirectory()) { 45 | dirs.push(item) 46 | } else { 47 | files.push(item) 48 | } 49 | } 50 | if (dirs.length > 0) { 51 | for await (let dir of dirs) { 52 | let tableRow = $(' | |
| ').addClass('directory').attr('onclick', 'renderFiles(\'' + directoryClean + '/' + dirClean + '\');').text(dir); 55 | let type = $(' | ').text('Dir'); 56 | let del = $(' | ').append($(' |