1class AlertElement extends HTMLElement { 2 constructor() { 3 super(); 4 5 // Bindings 6 this.close = this.close.bind(this); 7 this.destroyCloseButton = this.destroyCloseButton.bind(this); 8 this.createCloseButton = this.createCloseButton.bind(this); 9 this.onMutation = this.onMutation.bind(this); 10 this.observer = new MutationObserver(this.onMutation); 11 this.observer.observe(this, { 12 attributes: false, 13 childList: true, 14 subtree: true 15 }); 16 17 // Handle the fade in animation 18 this.addEventListener('animationend', event => { 19 if (event.animationName === 'joomla-alert-fade-in' && event.target === this) { 20 this.dispatchEvent(new CustomEvent('joomla.alert.shown')); 21 this.style.removeProperty('animationName'); 22 } 23 }); 24 25 // Handle the fade out animation 26 this.addEventListener('animationend', event => { 27 if (event.animationName === 'joomla-alert-fade-out' && event.target === this) { 28 this.dispatchEvent(new CustomEvent('joomla.alert.closed')); 29 this.remove(); 30 } 31 }); 32 } 33 34 /* Attributes to monitor */ 35 static get observedAttributes() { 36 return ['type', 'role', 'dismiss', 'auto-dismiss', 'close-text']; 37 } 38 get type() { 39 return this.getAttribute('type'); 40 } 41 set type(value) { 42 this.setAttribute('type', value); 43 } 44 get role() { 45 return this.getAttribute('role'); 46 } 47 set role(value) { 48 this.setAttribute('role', value); 49 } 50 get closeText() { 51 return this.getAttribute('close-text'); 52 } 53 set closeText(value) { 54 this.setAttribute('close-text', value); 55 } 56 get dismiss() { 57 return this.getAttribute('dismiss'); 58 } 59 set dismiss(value) { 60 this.setAttribute('dismiss', value); 61 } 62 get autodismiss() { 63 return this.getAttribute('auto-dismiss'); 64 } 65 set autodismiss(value) { 66 this.setAttribute('auto-dismiss', value); 67 } 68 69 /* Lifecycle, element appended to the DOM */ 70 connectedCallback() { 71 this.dispatchEvent(new CustomEvent('joomla.alert.show')); 72 this.style.animationName = 'joomla-alert-fade-in'; 73 74 // Default to info 75 if (!this.type || !['info', 'warning', 'danger', 'success'].includes(this.type)) { 76 this.setAttribute('type', 'info'); 77 } 78 // Default to alert 79 if (!this.role || !['alert', 'alertdialog'].includes(this.role)) { 80 this.setAttribute('role', 'alert'); 81 } 82 83 // Hydrate the button 84 if (this.firstElementChild && this.firstElementChild.tagName === 'BUTTON') { 85 this.button = this.firstElementChild; 86 if (this.button.classList.contains('joomla-alert--close')) { 87 this.button.classList.add('joomla-alert--close'); 88 } 89 if (this.button.innerHTML === '') { 90 this.button.innerHTML = '<span aria-hidden="true">×</span>'; 91 } 92 if (!this.button.hasAttribute('aria-label')) { 93 this.button.setAttribute('aria-label', this.closeText); 94 } 95 } 96 97 // Append button 98 if (this.hasAttribute('dismiss') && !this.button) { 99 this.createCloseButton(); 100 } 101 if (this.hasAttribute('auto-dismiss')) { 102 this.autoDismiss(); 103 } 104 } 105 106 /* Lifecycle, element removed from the DOM */ 107 disconnectedCallback() { 108 if (this.button) { 109 this.button.removeEventListener('click', this.close); 110 } 111 this.observer.disconnect(); 112 } 113 114 /* Respond to attribute changes */ 115 attributeChangedCallback(attr, oldValue, newValue) { 116 switch (attr) { 117 case 'type': 118 if (!newValue || newValue && ['info', 'warning', 'danger', 'success'].indexOf(newValue) === -1) { 119 this.type = 'info'; 120 } 121 break; 122 case 'role': 123 if (!newValue || newValue && ['alert', 'alertdialog'].indexOf(newValue) === -1) { 124 this.role = 'alert'; 125 } 126 break; 127 case 'dismiss': 128 if ((!newValue || newValue === '') && (!oldValue || oldValue === '')) { 129 if (this.button && !this.hasAttribute('dismiss')) { 130 this.destroyCloseButton(); 131 } else if (!this.button && this.hasAttribute('dismiss')) { 132 this.createCloseButton(); 133 } 134 } else if (this.button && newValue === 'false') { 135 this.destroyCloseButton(); 136 } else if (!this.button && newValue !== 'false') { 137 this.createCloseButton(); 138 } 139 break; 140 case 'close-text': 141 if (!newValue || newValue !== oldValue) { 142 if (this.button) { 143 this.button.setAttribute('aria-label', newValue); 144 } 145 } 146 break; 147 case 'auto-dismiss': 148 this.autoDismiss(); 149 break; 150 } 151 } 152 153 /* Observe added elements */ 154 onMutation(mutationsList) { 155 // eslint-disable-next-line no-restricted-syntax 156 for (const mutation of mutationsList) { 157 if (mutation.type === 'childList') { 158 if (mutation.addedNodes.length) { 159 // Make sure that the button is always the first element 160 if (this.button && this.firstElementChild !== this.button) { 161 this.prepend(this.button); 162 } 163 } 164 } 165 } 166 } 167 168 /* Method to close the alert */ 169 close() { 170 this.dispatchEvent(new CustomEvent('joomla.alert.close')); 171 this.style.animationName = 'joomla-alert-fade-out'; 172 } 173 174 /* Method to create the close button */ 175 createCloseButton() { 176 this.button = document.createElement('button'); 177 this.button.setAttribute('type', 'button'); 178 this.button.classList.add('joomla-alert--close'); 179 this.button.innerHTML = '<span aria-hidden="true">×</span>'; 180 this.button.setAttribute('aria-label', this.closeText); 181 this.insertAdjacentElement('afterbegin', this.button); 182 183 /* Add the required listener */ 184 this.button.addEventListener('click', this.close); 185 } 186 187 /* Method to remove the close button */ 188 destroyCloseButton() { 189 if (this.button) { 190 this.button.removeEventListener('click', this.close); 191 this.button.parentNode.removeChild(this.button); 192 this.button = null; 193 } 194 } 195 196 /* Method to auto-dismiss */ 197 autoDismiss() { 198 const timer = parseInt(this.getAttribute('auto-dismiss'), 10); 199 setTimeout(this.close, timer >= 10 ? timer : 3000); 200 } 201} 202if (!customElements.get('joomla-alert')) { 203 customElements.define('joomla-alert', AlertElement); 204} 205 206/** 207 * @copyright (C) 2020 Open Source Matters, Inc. <https://www.joomla.org> 208 * @license GNU General Public License version 2 or later; see LICENSE.txt 209 */ 210 211 212/** 213 * Returns the container of the Messages 214 * 215 * @param {string|HTMLElement} container The container 216 * 217 * @returns {HTMLElement} 218 */ 219const getMessageContainer = container => { 220 let messageContainer; 221 if (container instanceof HTMLElement) { 222 return container; 223 } 224 if (typeof container === 'undefined' || container && container === '#system-message-container') { 225 messageContainer = document.getElementById('system-message-container'); 226 } else { 227 messageContainer = document.querySelector(container); 228 } 229 return messageContainer; 230}; 231 232/** 233 * Render messages send via JSON 234 * Used by some javascripts such as validate.js 235 * 236 * @param {object}
236 messages JavaScript object containing the messages to render. 237 * Example: 238 * const messages = { 239 * "message": ["This will be a green message", "So will this"], 240 * "error": ["This will be a red message", "So will this"], 241 * "info": ["This will be a blue message", "So will this"], 242 * "notice": ["This will be same as info message", "So will this"], 243 * "warning": ["This will be a orange message", "So will this"], 244 * "my_custom_type": ["This will be same as info message", "So will this"] 245 * }; 246 * @param {string} selector The selector of the container where the message will be rendered 247 * @param {bool} keepOld If we shall discard old messages 248 * @param {int} timeout The milliseconds before the message self destruct 249 * @return void 250 */ 251Joomla.renderMessages = (messages, selector, keepOld, timeout) => { 252 const messageContainer = getMessageContainer(selector); 253 if (typeof keepOld === 'undefined' || keepOld && keepOld === false) { 254 Joomla.removeMessages(messageContainer); 255 } 256 Object.keys(messages).forEach(type => { 257 let alertClass = type; 258 259 // Array of messages of this type 260 const typeMessages = messages[type]; 261 const messagesBox = document.createElement('joomla-alert');
262 if (['success', 'info', 'danger', 'warning'].indexOf(type) < 0) { 263 alertClass = type === 'notice' ? 'info' : type; 264 alertClass = type === 'message' ? 'success' : alertClass; 265 alertClass = type === 'error' ? 'danger' : alertClass; 266 alertClass = type === 'warning' ? 'warning' : alertClass; 267 } 268 messagesBox.setAttribute('type', alertClass); 269 messagesBox.setAttribute('close-text', Joomla.Text._('JCLOSE')); 270 messagesBox.setAttribute('dismiss', true); 271 if (timeout && parseInt(timeout, 10) > 0) { 272 messagesBox.setAttribute('auto-dismiss', timeout); 273 } 274 275 // Title 276 const title = Joomla.Text._(type); 277 278 // Skip titles with untranslated strings 279 if (typeof title !== 'undefined') { 280 const titleWrapper = document.createElement('div'); 281 titleWrapper.className = 'alert-heading'; 282 titleWrapper.innerHTML = Joomla.sanitizeHtml(`<span class="${type}"></span><span class="visually-hidden">${Joomla.Text._(type) ? Joomla.Text._(type) : type}</span>`); 283 messagesBox.appendChild(titleWrapper); 284 } 285 286 // Add messages to the message box 287 const messageWrapper = document.createElement('div'); 288 messageWrapper.className = 'alert-wrapper'; 289 typeMessages.forEach(typeMessage => { 290 messageWrapper.innerHTML += Joomla.sanitizeHtml(`<div class="alert-message">${typeMessage}</div>`); 291 }); 292 messagesBox.appendChild(messageWrapper); 293 messageContainer.appendChild(messagesBox); 294 }); 295}; 296 297/** 298 * Remove messages 299 * 300 * @param {element} container The element of the container of the message 301 * to be removed 302 * 303 * @return {void} 304 */ 305Joomla.removeMessages = container => { 306 const messageContainer = getMessageContainer(container); 307 messageContainer.querySelectorAll('joomla-alert').forEach(alert => alert.close()); 308}; 309document.addEventListener('DOMContentLoaded', () => { 310 const messages = Joomla.getOptions('joomla.messages'); 311 if (messages) { 312 Object.keys(messages).map(message => Joomla.renderMessages(messages[message], undefined, true, undefined)); 313 } 314});
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.