1// Copyright 1999-2023. Parallels IP Holdings GmbH. All Rights Reserved. 2// ExportList global.js 3 4(() => { 5class ModuleExportLists { 6 constructor() { 7 this.eventList = []; 8 } 9 10 on(event, callback) { 11 if (!this.eventList[event]) { 12 this.eventList[event] = []; 13 } 14 15 this.eventList[event].push(callback); 16 return this; 17 } 18 19 emit(event) { 20 if (!this.eventList[event]) return; 21 22 this.eventList[event].forEach(cb => cb.call(null)) 23 } 24} 25 26const elementObserver = selector => { 27 const event = new ModuleExportLists(); 28 29 let elementExistBefore = false; 30 31 const observer = new MutationObserver(() => { 32 const el = document.querySelector(selector) 33 34 if (!elementExistBefore && el) { 35 elementExistBefore = true; 36 event.emit('added'); 37 } else if (elementExistBefore && !el) { 38 elementExistBefore = false; 39 event.emit('removed'); 40 } 41 }); 42 43 const container = document.documentElement || document.body; 44 observer.observe(container, { 45 childList: true, 46 subtree: true, 47 }); 48 49 return event; 50} 51 52const addListToCsvButton = () => { 53 54 var extName = 'ExportLists'; 55 56 Jsw.namespace('PleskExt.' + extName); 57 let ext = PleskExt[extName]; 58 59 60 function escapeRegExp(str) { 61 return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); 62 } 63 64 function replaceAll(str, find, replace) { 65 return str.replace(new RegExp(escapeRegExp(find), 'g'), replace); 66 } 67 68 function lmsg(message, data) { 69 var locale = ext.Config.locale || {}; 70 message = locale[message] || message || ''; 71 data = data || {}; 72 for (var key in data) { 73 if (data.hasOwnProperty(key)) { 74 message = replaceAll(message, '%%' + key + '%%', data[key]); 75 } 76 } 77 78 return message; 79 } 80 81 function displayWarning(message) { 82 Jsw.addStatusMessage('warning', message, { 83 title: lmsg('exportToCsv'), 84 closable: true, 85 onClose: function () { 86 this.up('.msg-box').remove(); 87 } 88 }); 89 } 90 91 function addCvsExportButton(parentElement) { 92 new Jsw.SmallButton({ 93 renderTo: parentElement, 94 renderMode: 'top', 95 cls: 's-btn sb-csv-export btn-icon-only', 96 title: lmsg('exportToCsv'), 97 description: lmsg('exportToCsvDescription'), 98 href: ext.Config.link, 99 handler: exportListToCsv 100 }); 101 } 102 103 var listElementSelector = 'div:has(> div > div.list), div.pul-list'; 104 105 ext.init = function() { 106 function addCsvExportButtons(list) { 107 list._componentElement.select('div.paging .paging-view').forEach(addCvsExportButton); 108 } 109 110 var listElements = $$(listElementSelector); 111 listElements.forEach(function (listElement) { 112 var listComponent = Jsw.getComponent(listElement.id); 113 if (listComponent) { 114 addCsvExportButtons(listComponent); 115 116 var oldRedrawHandler = listComponent._onRedraw; 117 listComponent._onRedraw = function () { 118 if (oldRedrawHandler) { 119 oldRedrawHandler.apply(this, Array.prototype.slice.call(arguments)); 120 } 121 addCsvExportButtons(listComponent); 122 } 123 } else { 124 addJswButtonToPleskUiList(listElement); 125 } 126 }); 127 }; 128 129 function addJswButtonToPleskUiList(listElement) { 130 const exportListContainer = 'export-lists-container'; 131 132 function createButtonContainer(targetElement) { 133 const buttonContainer = document.createElement('span'); 134 buttonContainer.classList.add(exportListContainer); 135 buttonContainer.style.marginRight = '10px'; 136 targetElement.prepend(buttonContainer); 137 138 return buttonContainer; 139 } 140 141 const targetElement = listElement.querySelector('div.pul-list div.pul-list__header > span'); 142 143 if (!targetElement) { 144 return; 145 } 146 addCvsExportButton(createButtonContainer(targetElement)); 147 148 const observer = new MutationObserver(() => { 149 if (targetElement && !targetElement.querySelector('span.' + exp
149ortListContainer)) { 150 addCvsExportButton(createButtonContainer(targetElement)); 151 } 152 }); 153 154 observer.observe(document.body, { childList: true, subtree: true }); 155 } 156 157 function getUpDownElement(element, upSelector, downSelector) { 158 var sucessorElement; 159 160 var ancestorElement = element.up(upSelector); 161 if (ancestorElement) { 162 sucessorElement = ancestorElement.down(downSelector); 163 } 164 165 return sucessorElement; 166 } 167 168 function getElementText(element) { 169 var rejectedElementClasses = [ 170 'r-visible', 171 'hint', 172 'hint-info', 173 'tooltipData' 174 ]; 175 var rejectedElementNames = [ 176 'svg' 177 ]; 178 var treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, { 179 acceptNode: function (node) { 180 let result; 181 182 if (rejectedElementNames.includes(node.nodeName.toLowerCase())) { 183 return NodeFilter.FILTER_REJECT; 184 } 185 186 switch(node.nodeType){ 187 case Node.ELEMENT_NODE: 188 result = rejectedElementClasses.some(node.hasClassName.bind(node)) ? 189 NodeFilter.FILTER_REJECT : 190 NodeFilter.FILTER_ACCEPT; 191 break; 192 193 case Node.TEXT_NODE: 194 result = NodeFilter.FILTER_ACCEPT; 195 break; 196 197 default: 198 result = NodeFilter.FILTER_REJECT; 199 } 200 201 return result; 202 } 203 }, false); 204 205 var textData = []; 206 while(treeWalker.nextNode()) { 207 var node = treeWalker.currentNode; 208 209 switch(node.nodeType){ 210 case Node.ELEMENT_NODE: 211 switch (node.nodeName.toLowerCase()) { 212 case 'br': 213 textData.push('\n'); 214 break; 215 216 case 'img': 217 var text = 218 (node.readAttribute('alt') || '').trim() || 219 (node.readAttribute('title') || '').trim(); 220 if (text) { 221 textData.push(text); 222 } 223 break; 224 225 default: 226 break; 227 } 228 break; 229 230 case Node.TEXT_NODE: 231 textData.push(node.nodeValue); 232 break; 233 234 default: 235 break; 236 } 237 } 238 239 return textData.join('') 240 .replace(/\u00A0/g, ' ') // non-breaking space 241 .replace(/[\u200B\u00AD]/g, '') // zero-width space, soft hyphen 242 ; 243 } 244 245 function getRowData(tableDataElement, selector) { 246 var result = []; 247 while (tableDataElement) { 248 result.push(getElementText(tableDataElement)); 249 tableDataElement = tableDataElement.next(selector); 250 } 251 252 return result; 253 } 254 255 function getTableHeaders(tableElement) { 256 return getRowData(tableElement.down('thead > tr > th'), 'th'); 257 } 258 259 function getTableData(tableElement, selected) { 260 var result = []; 261 var rowsFilter = selected ? 'tbody > tr.selected, tbody > tr.is-selected' : 'tbody > tr'; 262 var rowFilter = selected ? 'tr.selected, tr.is-selected' : 'tr'; 263 var trElement = tableElement.down(rowsFilter); 264 while (trElement) { 265 result.push(getRowData(trElement.down('td'), 'td')); 266 trElement = trElement.next(rowFilter); 267 } 268 269 return result; 270 } 271 272 function indexFilter(data, index) { 273 return this[index]; 274 } 275 276 function getHeadingName() { 277 var headings = $$('#page .heading-area h2'); 278 279 return (headings.length > 0) ? getElementText(headings[0]).trim() : null; 280 } 281 282 function getActiveTabNames(element) { 283 var tabNames = []; 284 285 var prevTabElement = null; 286 var tabedElement; 287 while (tabedElement = (element.up('div.tabed') || element.up('div.b-content')) ) { 288 var activeTabElement = tabedElement.down('div.tabs-area ul > li.active'); 289 if (activeTabElement && activeTabElement !== prevTabElement) { 290 tabNames.push(getElementText(activeTabElement).trim()); 291 prevTabElement = activeTabElement; 292 } 293 element = tabedElement; 294 } 295 296 return tabNames.reverse(); 297 } 298 299 function exportListToCsv(event) { 300 event.stop(); 301 302 var tableElement = getUpDownElement(this, listElementSelector, 'div.list > table, table.pul-list__table'); 303 if (!tableElement) { 304 displayWarning(lmsg('unableToFetchData')); 305 306 return; 307 } 308 309 var headers = getTableHeaders(tableElement); 310 311 var headersIndexFilter = headers.map(function (header) { 312 return header.length > 0; 313 }); 314 315 var headersFiltered = headers.filter(indexFilter, headersIndexFilter); 316 if (headersFiltered.length === 0) { 317 displayWarning(lmsg('noContent')); 318 319 return; 320 } 321 322 var rows = getTableData(tableElement, true); 323 if (rows.length === 0) { 324 rows = getTableData(tableElement); 325 } 326 if (rows.length === 0) { 327 displayWarning(lmsg('noContent')); 328 329 return; 330 } 331 332 var rowsFiltered = rows.map(function (row) { 333 return row.filter(indexFilter, headersIndexFilter); 334 }); 335 336 var tabNames = getActiveTabNames(this); 337 tabNames.unshift(getHeadingName()); 338 339 var postData = { 340 exportHeaders: JSON.stringify(headersFiltered), 341 exportData: JSON.stringify(rowsFiltered), 342 exportDataTitles: tabNames 343 }; 344 345 Jsw.redirectPost(this.href, postData); 346 } 347}; 348 349addListToCsvButton(); 350[ 351 '.domain-list table.pul-list__table', 352 '#subscriptions-list-table', 353 '#customer-service-plan-list-table', 354 '#customers-list-table', 355 '#resellers-list-table', 356 '#backup-list-table', 357 '#statistics-list', 358 '#email-addresses-list-table table.pul-list__table',
359 '#mail-settings-list-table', 360 '#database-list-table', 361 '#database-users-list-table', 362 '#admin-alias-list-table', 363 '#active-plesk-sessions-list-table', 364 '#admin-control-panel-access-list-table', 365 '#restricted-domains-list-table', 366 '#configurations-list-table', 367 'div[data-type="ip-address-list"] table.pul-list__table', 368 '#scheduler-tasks-table', 369 '#admin-event-handlers-list-table', 370 '#ssl-certificate-list-table', 371 '#dns-template-acl-records-table', 372 '#php-handler-list-table', 373 '#performance-booster table.pul-list__table', 374 '#webmails-list-table', 375 '#admin-app-usage-list-table', 376 '#admin-app-uploaded-list-table', 377 '#languages-list-table', 378 '#custom-buttons-list-table', 379 '#users-list-table', 380 '#roles-list-table', 381 '#modules-exportlists-list-demo-table' 382].forEach(selector => { 383 elementObserver(selector) 384 .on('added', PleskExt.ExportLists.init); 385}); 386 387})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.