|
| 1 | +// Override for the formio bootstrap file template. |
| 2 | +// |
| 3 | +// The original template embeds ctx.component.description (which is raw HTML) directly |
| 4 | +// into a <span class="sr-only"> that lives inside an <a> tag. When the description |
| 5 | +// contains block-level elements such as <ul>/<li> the browser's HTML parser |
| 6 | +// considers the nesting invalid and hoists those elements outside every inline |
| 7 | +// ancestor, making them fully visible even though they should be hidden. |
| 8 | +// |
| 9 | +// Fix: strip all HTML tags from the description before putting it into the sr-only |
| 10 | +// span so the text remains purely inline. |
| 11 | + |
| 12 | +function stripHtml(html) { |
| 13 | + if (!html) return ''; |
| 14 | + // Use the DOM when available (browser), otherwise fall back to a simple regex. |
| 15 | + if (typeof document !== 'undefined') { |
| 16 | + const tmp = document.createElement('div'); |
| 17 | + tmp.innerHTML = html; |
| 18 | + return tmp.textContent || tmp.innerText || ''; |
| 19 | + } |
| 20 | + return html.replace(/<[^>]*>/g, ''); |
| 21 | +} |
| 22 | + |
| 23 | +export const overrideFileTemplate = (ctx) => { |
| 24 | + var __t, |
| 25 | + __p = '', |
| 26 | + __j = Array.prototype.join; |
| 27 | + function print() { |
| 28 | + __p += __j.call(arguments, ''); |
| 29 | + } |
| 30 | + |
| 31 | + if (ctx.options.vpat) { |
| 32 | + __p += |
| 33 | + '\n <span tabindex="-1" class="sr-only" id="invisible-' + |
| 34 | + ((__t = ctx.instance.id) == null ? '' : __t) + |
| 35 | + '-' + |
| 36 | + ((__t = ctx.component.key) == null ? '' : __t) + |
| 37 | + '"></span>\n'; |
| 38 | + } |
| 39 | + __p += '\n'; |
| 40 | + if (!ctx.self.imageUpload) { |
| 41 | + __p += '\n '; |
| 42 | + if (ctx.options.vpat) { |
| 43 | + __p += |
| 44 | + '\n <div>' + |
| 45 | + ((__t = |
| 46 | + !ctx.component.filePattern || ctx.component.filePattern === '*' |
| 47 | + ? 'Any file types are allowed' |
| 48 | + : ctx.t('Allowed file types: ') + ctx.component.filePattern) == null |
| 49 | + ? '' |
| 50 | + : __t) + |
| 51 | + '</div>\n '; |
| 52 | + } |
| 53 | + __p += |
| 54 | + '\n <ul class="list-group list-group-striped">\n <li class="list-group-item list-group-header hidden-xs hidden-sm">\n <div class="row">\n '; |
| 55 | + if (!ctx.disabled) { |
| 56 | + __p += '\n <div class="col-md-1"></div>\n '; |
| 57 | + } |
| 58 | + __p += '\n <div class="col-md-'; |
| 59 | + if (ctx.self.hasTypes) { |
| 60 | + __p += '7'; |
| 61 | + } else { |
| 62 | + __p += '9'; |
| 63 | + } |
| 64 | + __p += |
| 65 | + '"><strong>' + |
| 66 | + ((__t = ctx.t('File Name')) == null ? '' : __t) + |
| 67 | + '</strong></div>\n <div class="col-md-2"><strong>' + |
| 68 | + ((__t = ctx.t('Size')) == null ? '' : __t) + |
| 69 | + '</strong></div>\n '; |
| 70 | + if (ctx.self.hasTypes) { |
| 71 | + __p += |
| 72 | + '\n <div class="col-md-2"><strong>' + |
| 73 | + ((__t = ctx.t('Type')) == null ? '' : __t) + |
| 74 | + '</strong></div>\n '; |
| 75 | + } |
| 76 | + __p += '\n </div>\n </li>\n '; |
| 77 | + ctx.files.forEach(function (file) { |
| 78 | + __p += '\n <li class="list-group-item">\n <div class="row">\n '; |
| 79 | + if (!ctx.disabled) { |
| 80 | + __p += |
| 81 | + '\n <div class="col-md-1"><i tabindex="0" class="' + |
| 82 | + ((__t = ctx.iconClass('remove')) == null ? '' : __t) + |
| 83 | + '" ref="removeLink"></i></div>\n '; |
| 84 | + } |
| 85 | + __p += '\n <div class="col-md-'; |
| 86 | + if (ctx.self.hasTypes) { |
| 87 | + __p += '7'; |
| 88 | + } else { |
| 89 | + __p += '9'; |
| 90 | + } |
| 91 | + __p += '">\n '; |
| 92 | + if (ctx.component.uploadOnly) { |
| 93 | + __p += '\n ' + ((__t = file.originalName || file.name) == null ? '' : __t) + '\n '; |
| 94 | + } else { |
| 95 | + __p += |
| 96 | + '\n <a href="' + |
| 97 | + ((__t = file.url || '#') == null ? '' : __t) + |
| 98 | + '" target="_blank" ref="fileLink">\n <span class="sr-only">' + |
| 99 | + ((__t = ctx.t('Press to open ')) == null ? '' : __t) + |
| 100 | + '</span>' + |
| 101 | + ((__t = file.originalName || file.name) == null ? '' : __t) + |
| 102 | + '\n </a>\n '; |
| 103 | + } |
| 104 | + __p += |
| 105 | + '\n </div>\n <div class="col-md-2">' + |
| 106 | + ((__t = ctx.fileSize(file.size)) == null ? '' : __t) + |
| 107 | + '</div>\n '; |
| 108 | + if (ctx.self.hasTypes && !ctx.disabled) { |
| 109 | + __p += |
| 110 | + '\n <div class="col-md-2">\n <select class="file-type" ref="fileType">\n '; |
| 111 | + ctx.component.fileTypes.map(function (type) { |
| 112 | + __p += '\n <option class="test" value="' + ((__t = type.value) == null ? '' : __t) + '" '; |
| 113 | + if (type.label === file.fileType) { |
| 114 | + __p += 'selected="selected"'; |
| 115 | + } |
| 116 | + __p += '>' + ((__t = ctx.t(type.label)) == null ? '' : __t) + '</option>\n '; |
| 117 | + }); |
| 118 | + __p += '\n </select>\n </div>\n '; |
| 119 | + } |
| 120 | + if (ctx.self.hasTypes && ctx.disabled) { |
| 121 | + __p += '\n <div class="col-md-2">' + ((__t = file.fileType) == null ? '' : __t) + '</div>\n '; |
| 122 | + } |
| 123 | + __p += '\n </div>\n </li>\n '; |
| 124 | + }); |
| 125 | + __p += '\n </ul>\n'; |
| 126 | + } else { |
| 127 | + __p += '\n <div>\n '; |
| 128 | + ctx.files.forEach(function (file) { |
| 129 | + __p += |
| 130 | + '\n <div>\n <span>\n <img ref="fileImage" src="" alt="' + |
| 131 | + ((__t = file.originalName || file.name) == null ? '' : __t) + |
| 132 | + '" style="width:' + |
| 133 | + ((__t = ctx.component.imageSize) == null ? '' : __t) + |
| 134 | + 'px">\n '; |
| 135 | + if (!ctx.disabled) { |
| 136 | + __p += |
| 137 | + '\n <i tabindex="0" class="' + |
| 138 | + ((__t = ctx.iconClass('remove')) == null ? '' : __t) + |
| 139 | + '" ref="removeLink"></i>\n '; |
| 140 | + } |
| 141 | + __p += '\n </span>\n </div>\n '; |
| 142 | + }); |
| 143 | + __p += '\n </div>\n'; |
| 144 | + } |
| 145 | + __p += '\n'; |
| 146 | + if (!ctx.disabled && (ctx.component.multiple || !ctx.files.length)) { |
| 147 | + __p += '\n '; |
| 148 | + if (ctx.self.useWebViewCamera) { |
| 149 | + __p += |
| 150 | + '\n <div class="fileSelector">\n <button class="btn btn-primary" ref="galleryButton"><i class="fa fa-book"></i> ' + |
| 151 | + ((__t = ctx.t('Gallery')) == null ? '' : __t) + |
| 152 | + '</button>\n <button class="btn btn-primary" ref="cameraButton"><i class="fa fa-camera"></i> ' + |
| 153 | + ((__t = ctx.t('Camera')) == null ? '' : __t) + |
| 154 | + '</button>\n </div>\n '; |
| 155 | + } else if (!ctx.self.cameraMode) { |
| 156 | + __p += |
| 157 | + '\n <div class="fileSelector" ref="fileDrop" ' + |
| 158 | + ((__t = ctx.fileDropHidden ? 'hidden' : '') == null ? '' : __t) + |
| 159 | + '>\n <i class="' + |
| 160 | + ((__t = ctx.iconClass('cloud-upload')) == null ? '' : __t) + |
| 161 | + '"></i> ' + |
| 162 | + ((__t = ctx.t('Drop files to attach,')) == null ? '' : __t) + |
| 163 | + '\n '; |
| 164 | + if (ctx.self.imageUpload && ctx.component.webcam) { |
| 165 | + __p += |
| 166 | + '\n <a href="#" ref="toggleCameraMode"><i class="fa fa-camera"></i> ' + |
| 167 | + ((__t = ctx.t('use camera')) == null ? '' : __t) + |
| 168 | + '</a>\n '; |
| 169 | + } |
| 170 | + // --- KEY FIX: strip HTML from description before embedding in sr-only span --- |
| 171 | + const plainDescription = stripHtml(ctx.component.description); |
| 172 | + const filePatternText = |
| 173 | + !ctx.component.filePattern || ctx.component.filePattern === '*' |
| 174 | + ? 'Any file types are allowed' |
| 175 | + : ctx.t('Allowed file types: ') + ctx.component.filePattern; |
| 176 | + const srOnlyText = ctx.t( |
| 177 | + 'Browse to attach file for ' + |
| 178 | + ctx.component.label + |
| 179 | + '. ' + |
| 180 | + (plainDescription ? plainDescription + '. ' : '') + |
| 181 | + filePatternText, |
| 182 | + ); |
| 183 | + |
| 184 | + __p += |
| 185 | + '\n ' + |
| 186 | + ((__t = ctx.t('or')) == null ? '' : __t) + |
| 187 | + '\n <a href="#" ref="fileBrowse" class="browse">\n ' + |
| 188 | + ((__t = ctx.t('browse')) == null ? '' : __t) + |
| 189 | + '\n <span class="sr-only">\n ' + |
| 190 | + ((__t = srOnlyText) == null ? '' : __t) + |
| 191 | + '\n </span>\n </a>\n <div ref="fileProcessingLoader" class="loader-wrapper">\n <div class="loader text-center"></div>\n </div>\n </div>\n '; |
| 192 | + } else { |
| 193 | + __p += |
| 194 | + '\n <div class="video-container">\n <video class="video" autoplay="true" ref="videoPlayer" tabindex="-1"></video>\n </div>\n <button class="btn btn-primary" ref="takePictureButton"><i class="fa fa-camera"></i> ' + |
| 195 | + ((__t = ctx.t('Take Picture')) == null ? '' : __t) + |
| 196 | + '</button>\n <button class="btn btn-primary" ref="toggleCameraMode">' + |
| 197 | + ((__t = ctx.t('Switch to file upload')) == null ? '' : __t) + |
| 198 | + '</button>\n '; |
| 199 | + } |
| 200 | + __p += '\n'; |
| 201 | + } |
| 202 | + __p += '\n'; |
| 203 | + ctx.statuses.forEach(function (status) { |
| 204 | + __p += |
| 205 | + '\n <div class="file ' + |
| 206 | + ((__t = ctx.statuses.status === 'error' ? ' has-error' : '') == null ? '' : __t) + |
| 207 | + '">\n <div class="row">\n <div class="fileName col-form-label col-sm-10">' + |
| 208 | + ((__t = status.originalName) == null ? '' : __t) + |
| 209 | + '\n <i class="' + |
| 210 | + ((__t = ctx.iconClass('remove')) == null ? '' : __t) + |
| 211 | + '" ref="fileStatusRemove">\n <span class="sr-only">' + |
| 212 | + ((__t = ctx.t('Remove button. Press to remove ' + status.originalName || status.name + '.')) == null ? '' : __t) + |
| 213 | + '</span>\n <span class="sr-only">' + |
| 214 | + ((__t = status.message ? status.message.replace(';', '.') : '') == null ? '' : __t) + |
| 215 | + '</span>\n </i>\n </div>\n <div class="fileSize col-form-label col-sm-2 text-right">' + |
| 216 | + ((__t = ctx.fileSize(status.size)) == null ? '' : __t) + |
| 217 | + '</div>\n </div>\n <div class="row">\n <div class="col-sm-12">\n '; |
| 218 | + if (status.status === 'progress') { |
| 219 | + __p += |
| 220 | + '\n <div class="progress">\n <div class="progress-bar" role="progressbar" aria-valuenow="' + |
| 221 | + ((__t = status.progress) == null ? '' : __t) + |
| 222 | + '" aria-valuemin="0" aria-valuemax="100" style="width: ' + |
| 223 | + ((__t = status.progress) == null ? '' : __t) + |
| 224 | + '%">\n <span class="sr-only">' + |
| 225 | + ((__t = status.progress) == null ? '' : __t) + |
| 226 | + '% ' + |
| 227 | + ((__t = ctx.t('Complete')) == null ? '' : __t) + |
| 228 | + '</span>\n </div>\n </div>\n '; |
| 229 | + } else if (status.status === 'error') { |
| 230 | + __p += |
| 231 | + '\n <div class="alert alert-danger bg-' + |
| 232 | + ((__t = status.status) == null ? '' : __t) + |
| 233 | + '">' + |
| 234 | + ((__t = ctx.t(status.message)) == null ? '' : __t) + |
| 235 | + '</div>\n '; |
| 236 | + } else { |
| 237 | + __p += |
| 238 | + '\n <div class="bg-' + |
| 239 | + ((__t = status.status) == null ? '' : __t) + |
| 240 | + '">' + |
| 241 | + ((__t = ctx.t(status.message)) == null ? '' : __t) + |
| 242 | + '</div>\n '; |
| 243 | + } |
| 244 | + __p += '\n </div>\n </div>\n </div>\n'; |
| 245 | + }); |
| 246 | + __p += '\n'; |
| 247 | + if (!ctx.component.storage || ctx.support.hasWarning) { |
| 248 | + __p += '\n <div class="alert alert-warning">\n '; |
| 249 | + if (!ctx.component.storage) { |
| 250 | + __p += |
| 251 | + '\n <p>' + |
| 252 | + ((__t = ctx.t('No storage has been set for this field. File uploads are disabled until storage is set up.')) == |
| 253 | + null |
| 254 | + ? '' |
| 255 | + : __t) + |
| 256 | + '</p>\n '; |
| 257 | + } |
| 258 | + __p += '\n '; |
| 259 | + if (!ctx.support.filereader) { |
| 260 | + __p += |
| 261 | + '\n <p>' + ((__t = ctx.t('File API & FileReader API not supported.')) == null ? '' : __t) + '</p>\n '; |
| 262 | + } |
| 263 | + __p += '\n '; |
| 264 | + if (!ctx.support.formdata) { |
| 265 | + __p += '\n <p>' + ((__t = ctx.t("XHR2's FormData is not supported.")) == null ? '' : __t) + '</p>\n '; |
| 266 | + } |
| 267 | + __p += '\n '; |
| 268 | + if (!ctx.support.progress) { |
| 269 | + __p += |
| 270 | + '\n <p>' + ((__t = ctx.t("XHR2's upload progress isn't supported.")) == null ? '' : __t) + '</p>\n '; |
| 271 | + } |
| 272 | + __p += '\n </div>\n'; |
| 273 | + } |
| 274 | + __p += '\n'; |
| 275 | + return __p; |
| 276 | +}; |
0 commit comments