diff --git a/jade/page-contents/autocomplete_content.html b/jade/page-contents/autocomplete_content.html index 3a52495db8..7682ae651c 100644 --- a/jade/page-contents/autocomplete_content.html +++ b/jade/page-contents/autocomplete_content.html @@ -39,6 +39,8 @@

Initialization

The data is a json object where the key is the matching string and the value is an optional image url.

+

The key must be a text string. If you trust your data, or have properly sanitized your user input, you may + use HTML by setting the option allowUnsafeHTML: true.


   document.addEventListener('DOMContentLoaded', function() {
     var elems = document.querySelectorAll('.autocomplete');
@@ -104,6 +106,12 @@ 

Options

Sort function that defines the order of the list of autocomplete options. + + allowUnsafeHTML + Boolean + false + If true will render the key from each item directly as HTML. User input MUST be properly sanitized first. + diff --git a/jade/page-contents/toasts_content.html b/jade/page-contents/toasts_content.html index 66aa04553e..72200dd82c 100644 --- a/jade/page-contents/toasts_content.html +++ b/jade/page-contents/toasts_content.html @@ -4,14 +4,14 @@

Materialize provides an easy way for you to send unobtrusive alerts to your users through toasts. These toasts are also placed and sized responsively, try it out by clicking the button below on different device sizes.

- Toast! -

To do this, call the M.toast() function programatically in JavaScript.

+ Toast! +

To do this, call the M.toast() function programmatically in JavaScript.


-  M.toast({html: 'I am a toast!'})
+  M.toast({text: 'I am a toast!'})
         

One way to add this into your application is to add this as an onclick event to a button.


-  <a onclick="M.toast({html: 'I am a toast'})" class="btn">Toast!</a>
+  <a onclick="M.toast({text: 'I am a toast'})" class="btn">Toast!</a>
         
@@ -30,11 +30,37 @@

Options

+ + text + String + '' + The content of the Toast. + + + unsafeHTML + String, HTMLElement + '' + + HTML content that will be appended to to text. + Only use properly sanitized or otherwise trusted data for unsafeHTML. + + html String '' - The HTML content of the Toast. + +

+ (DEPRECATED): will be removed in a later release. +

+

+ HTML content that will be appended to text. + Only use properly sanitized or otherwise trusted data for html. +

+

+ Will be ignored if unsafeHTML is set. +

+ displayLength @@ -117,11 +143,16 @@

Properties

Custom HTML

-

You can pass in an HTML String as the first argument as well. Take a look at the example below, where we pass in text as well as a flat button. If you call an external function instead of in-line JavaScript, you will not need to escape quotation marks.

+

You can pass in an HTML String as the first argument as well. Take a look at the example below, where we pass + in text as well as a flat button. If you call an external function instead of in-line JavaScript, you will not + need to escape quotation marks.

+

+ Only use a properly sanitized or otherwise trusted HTML string. +

Toast with Action

   var toastHTML = '<span>I am toast content</span><button class="btn-flat toast-action">Undo</button>';
-  M.toast({html: toastHTML});
+  M.toast({unsafeHTML: toastHTML});
         
@@ -129,9 +160,9 @@

Custom HTML

Callback

You can have the toast callback a function when it has been dismissed.

- Toast! + Toast!

-  <a class="btn" onclick="M.toast({html: 'I am a toast', completeCallback: function(){alert('Your toast was dismissed')}})">Toast!</a>
+  <a class="btn" onclick="M.toast({text: 'I am a toast', completeCallback: function(){alert('Your toast was dismissed')}})">Toast!</a>
         
@@ -140,11 +171,11 @@

Callback

Styling Toasts

We've added the ability to customize your toasts easily. You can pass in classes as an optional parameter into the toast function. We've added a rounded class for you, but you can create your own CSS classes and apply them to toasts. Checkout out our full example below.

- Round Toast! + Round Toast!

   // 'rounded' is the class I'm applying to the toast
-  M.toast({html: 'I am a toast!', classes: 'rounded'});
+  M.toast({text: 'I am a toast!', classes: 'rounded'});
         
diff --git a/jade/page-contents/tooltips_content.html b/jade/page-contents/tooltips_content.html index 90c935a7b3..08b68e056a 100644 --- a/jade/page-contents/tooltips_content.html +++ b/jade/page-contents/tooltips_content.html @@ -66,11 +66,35 @@

Options

0 Delay time before tooltip appears. + + text + String + + Text string for the tooltip. + + + unsafeHTML + String + null + HTML content that will be appended to to text. + Only use properly sanitized or otherwise trusted data for unsafeHTML. + html String null - Can take regular text or HTML strings. + +

+ (DEPRECATED): will be removed in a later release. +

+

+ HTML content that will be appended to text. + Only use properly sanitized or otherwise trusted data for html. +

+

+ Will be ignored if unsafeHTML is set. +

+ margin diff --git a/js/autocomplete.js b/js/autocomplete.js index 1bed2301a2..5523c7522d 100644 --- a/js/autocomplete.js +++ b/js/autocomplete.js @@ -9,7 +9,8 @@ sortFunction: function(a, b, inputString) { // Sort function for sorting autocomplete results return a.indexOf(inputString) - b.indexOf(inputString); - } + }, + allowUnsafeHTML: false }; /** @@ -282,22 +283,14 @@ /** * Highlight partial match */ - _highlight(string, $el) { - let img = $el.find('img'); - let matchStart = $el - .text() - .toLowerCase() - .indexOf('' + string.toLowerCase() + ''), - matchEnd = matchStart + string.length - 1, - beforeMatch = $el.text().slice(0, matchStart), - matchText = $el.text().slice(matchStart, matchEnd + 1), - afterMatch = $el.text().slice(matchEnd + 1); - $el.html( - `${beforeMatch}${matchText}${afterMatch}` - ); - if (img.length) { - $el.prepend(img); - } + _highlight(input, label) { + const start = label.toLowerCase().indexOf('' + input.toLowerCase() + ''); + const end = start + input.length - 1; + //custom filters may return results where the string does not match any part + if (start == -1 || end == -1) { + return [label, '', '']; + } + return [label.slice(0, start), label.slice(start, end + 1), label.slice(end + 1)]; } /** @@ -376,18 +369,32 @@ // Render for (let i = 0; i < matchingData.length; i++) { - let entry = matchingData[i]; - let $autocompleteOption = $('
  • '); + const entry = matchingData[i]; + const item = document.createElement('li'); if (!!entry.data) { - $autocompleteOption.append( - `${entry.key}` - ); + const img = document.createElement('img'); + img.classList.add("right", "circle"); + img.src = entry.data; + item.appendChild(img); + } + + const parts = this._highlight(val, entry.key); + const s = document.createElement('span'); + if (this.options.allowUnsafeHTML) { + s.innerHTML = parts[0] + '' + parts[1] + '' + parts[2]; } else { - $autocompleteOption.append('' + entry.key + ''); + s.appendChild(document.createTextNode(parts[0])) + if (!!parts[1]){ + const highlight = document.createElement('span'); + highlight.textContent = parts[1]; + highlight.classList.add("highlight"); + s.appendChild(highlight); + s.appendChild(document.createTextNode(parts[2])); + } } + item.appendChild(s); - $(this.container).append($autocompleteOption); - this._highlight(val, $autocompleteOption); + $(this.container).append(item); } } diff --git a/js/toasts.js b/js/toasts.js index b0e4b83aa2..7500b285dd 100644 --- a/js/toasts.js +++ b/js/toasts.js @@ -3,6 +3,8 @@ let _defaults = { html: '', + unsafeHTML: '', + text: '', displayLength: 4000, inDuration: 300, outDuration: 375, @@ -18,7 +20,12 @@ * @member Toast#options */ this.options = $.extend({}, Toast.defaults, options); - this.message = this.options.html; + this.htmlMessage = this.options.html; + // If the new unsafeHTML is used, prefer that + if (!!this.options.unsafeHTML){ + this.htmlMessage = this.options.unsafeHTML; + } + this.message = this.options.text; /** * Describes current pan state toast @@ -188,28 +195,26 @@ $(toast).addClass(this.options.classes); } - // Set content + // Set safe text content + toast.textContent = this.message; if ( typeof HTMLElement === 'object' - ? this.message instanceof HTMLElement - : this.message && - typeof this.message === 'object' && - this.message !== null && - this.message.nodeType === 1 && - typeof this.message.nodeName === 'string' - ) { - toast.appendChild(this.message); - - // Check if it is jQuery object - } else if (!!this.message.jquery) { - $(toast).append(this.message[0]); - - // Insert as html; + ? this.htmlMessage instanceof HTMLElement + : this.htmlMessage && + typeof this.htmlMessage === 'object' && + this.htmlMessage !== null && + this.htmlMessage.nodeType === 1 && + typeof this.htmlMessage.nodeName === 'string' + ) { //if the htmlMessage is an HTML node, append it directly + toast.appendChild(this.htmlMessage); + } else if (!!this.htmlMessage.jquery) { // Check if it is jQuery object, append the node + $(toast).append(this.htmlMessage[0]); } else { - toast.innerHTML = this.message; + // Append as unsanitized html; + $(toast).append(this.htmlMessage); } - // Append toasft + // Append toast Toast._container.appendChild(toast); return toast; } diff --git a/js/tooltip.js b/js/tooltip.js index b30dce2b82..f5afafa420 100644 --- a/js/tooltip.js +++ b/js/tooltip.js @@ -5,6 +5,8 @@ exitDelay: 200, enterDelay: 0, html: null, + text: '', + unsafeHTML: null, margin: 5, inDuration: 250, outDuration: 200, @@ -68,13 +70,24 @@ let tooltipContentEl = document.createElement('div'); tooltipContentEl.classList.add('tooltip-content'); - tooltipContentEl.innerHTML = this.options.html; + this._setTooltipContent(tooltipContentEl); + tooltipEl.appendChild(tooltipContentEl); document.body.appendChild(tooltipEl); } + _setTooltipContent(tooltipContentEl) { + tooltipContentEl.textContent = this.options.text; + if (!!this.options.html){ + $(tooltipContentEl).append(this.options.html); + } + if (!!this.options.unsafeHTML){ + $(tooltipContentEl).append(this.options.unsafeHTML); + } + } + _updateTooltipContent() { - this.tooltipEl.querySelector('.tooltip-content').innerHTML = this.options.html; + this._setTooltipContent(this.tooltipEl.querySelector('.tooltip-content')); } _setupEventHandlers() { @@ -285,7 +298,7 @@ let positionOption = this.el.getAttribute('data-position'); if (tooltipTextOption) { - attributeOptions.html = tooltipTextOption; + attributeOptions.text = tooltipTextOption; } if (positionOption) { diff --git a/test/html/autocomplete.html b/test/html/autocomplete.html new file mode 100644 index 0000000000..ba33043b27 --- /dev/null +++ b/test/html/autocomplete.html @@ -0,0 +1,195 @@ + + + + + + + Materialize - Documentation + + + + + + + + + + + + + + + + + + + +
    + +
    Default autocomplete example
    +
    +
    +
    +
    + textsms + + +
    +
    +
    +
    + +
    Custom filter function
    +
    +
    +
    +
    + textsms + + +
    +
    +
    +
    + +
    Limit Set
    +
    +
    +
    +
    + textsms + + +
    +
    +
    +
    + +
    allowUnsafeHTML: false
    +
    +
    +
    +
    + textsms + + +
    +
    +
    +
    + +
    allowUnsafeHTML: true
    +
    +
    +
    +
    + textsms + + +
    +
    +
    +
    + + + +
    + + + + + + + + \ No newline at end of file diff --git a/test/html/toast.html b/test/html/toast.html new file mode 100644 index 0000000000..2fdf2c073c --- /dev/null +++ b/test/html/toast.html @@ -0,0 +1,58 @@ + + + + + + + Materialize - Documentation + + + + + + + + + + + + + + + + + + + +
    + Toast with text! + Toast with html! + Toast with unsafeHTML! + Toast with unsafeHTML and text! + + +
    + + + + + + + \ No newline at end of file diff --git a/test/html/tooltip.html b/test/html/tooltip.html new file mode 100644 index 0000000000..19767e7cba --- /dev/null +++ b/test/html/tooltip.html @@ -0,0 +1,42 @@ + + + + + + + Materialize - Documentation + + + + + + + + + + + + + + + + + + + +
    + Hover me! + Hover me! +
    + + + + + + + \ No newline at end of file