diff --git a/core/files.js b/core/files.js
index d762e512..45badada 100644
--- a/core/files.js
+++ b/core/files.js
@@ -153,7 +153,7 @@ var files = {
'resources/jquery-jeditable/1.8.0/jquery.jeditable.min.jsm': true,
// jQuery URLive
- 'resources/urlive/latest/jquery.urlive.min.jsm': true,
+ 'resources/urlive/1.1.1/jquery.urlive.min.jsm': true,
// JavaScript Cookie
'resources/js-cookie/2.2.1/js.cookie.min.jsm': true,
@@ -232,6 +232,9 @@ var files = {
// Underscore.js
'resources/underscore.js/1.8.3/underscore-min.jsm': true,
+ // urlize
+ 'resources/urlize/latest/urlize.jsm': true,
+
// Vue.js
'resources/vue/1.0.28/vue.min.jsm': true,
'resources/vue/2.6.11/vue.min.jsm': true,
diff --git a/core/mappings.js b/core/mappings.js
index e6ce0293..cd4aadbd 100644
--- a/core/mappings.js
+++ b/core/mappings.js
@@ -130,6 +130,7 @@ var mappings = {
'twitter-bootstrap/{version}/css/bootstrap.': resources.twitterBootstrapCSS,
'underscore.js/{version}/underscore.': resources.underscore,
'underscore.js/{version}/underscore-min.': resources.underscore,
+ 'urlive/{version}/jquery.urlive.': resources.jqueryURLive,
'vue/{version}/vue.min.js': resources.vueJs,
'webfont/{version}/webfont.js': resources.webfont,
'webfont/{version}/webfontloader.js': resources.webfont,
@@ -198,7 +199,7 @@ var mappings = {
'scriptaculous-js@{version}/scriptaculous.': resources.scriptaculous,
'underscore@{version}/underscore.': resources.underscore,
'underscore@{version}/underscore-min.': resources.underscore,
- 'urlize.js/urlize.js': resources.jqueryURLive,
+ 'urlize.js/urlize.js': resources.urlize,
'@webcomponents/webcomponentsjs/webcomponents-loader.js': resources.webcomponentsJS,
'webfontloader@{version}/webfontloader.': resources.webfont
},
diff --git a/core/resources.js b/core/resources.js
index f4b1a5ff..2b264171 100644
--- a/core/resources.js
+++ b/core/resources.js
@@ -352,6 +352,11 @@ var resources = {
'path': 'resources/underscore.js/{version}/underscore-min.jsm',
'type': 'application/javascript'
},
+ // urlize
+ 'urlize': {
+ 'path': 'resources/urlize/{version}/urlize.jsm',
+ 'type': 'application/javascript'
+ },
// Vue.js
'vueJs': {
'path': 'resources/vue/{version}/vue.min.jsm',
diff --git a/modules/internal/helpers.js b/modules/internal/helpers.js
index 73b9dc9c..a364a3de 100644
--- a/modules/internal/helpers.js
+++ b/modules/internal/helpers.js
@@ -346,6 +346,8 @@ helpers.determineResourceName = function (filename) {
return 'WebRTC adapter';
case 'vue.jsm':
return 'Vue.js';
+ case 'urlize.jsm':
+ return 'urlize';
case 'wow.min.jsm':
return 'WOW';
case 'jsdelivr-combine-jquery-hogan-algoliasearch-autocomplete.jsm':
@@ -489,6 +491,8 @@ helpers.setLastVersion = function (type, version) {
version = '1.19.1';
} else if (type.includes('/jquery-jeditable/1.')) {
version = '1.8.0';
+ } else if (type.includes('/urlive/1.')) {
+ version = '1.1.1';
} else if (type.includes('/js-cookie/2.')) {
version = '2.2.1';
} else if (type.includes('/lazysizes/4.')) {
@@ -535,6 +539,8 @@ helpers.setLastVersion = function (type, version) {
version = '2.1.4';
} else if (type.includes('/underscore.js/1.')) {
version = '1.9.1';
+ } else if (type.includes('/urlive/1.')) {
+ version = '1.1.1';
} else if (type.includes('/vue/1.')) {
version = '1.0.28';
} else if (type.includes('/vue/2.')) {
diff --git a/pages/updates/updates.html b/pages/updates/updates.html
index eb1390db..6c15b715 100644
--- a/pages/updates/updates.html
+++ b/pages/updates/updates.html
@@ -24,7 +24,8 @@
- Fixed #55
- - Added WebComponents.js and jQuery URLive #56
+ - Added WebComponents.js and urlize #56
+ - Added jQuery URLive v1.1.1
Please update your uBlock/uMatrix rules
diff --git a/resources/urlive/latest/jquery.urlive.min.jsm b/resources/urlive/1.1.1/jquery.urlive.min.jsm
similarity index 100%
rename from resources/urlive/latest/jquery.urlive.min.jsm
rename to resources/urlive/1.1.1/jquery.urlive.min.jsm
diff --git a/resources/urlize.js/latest/urlize.jsm b/resources/urlize.js/latest/urlize.jsm
new file mode 100644
index 00000000..fefea899
--- /dev/null
+++ b/resources/urlize.js/latest/urlize.jsm
@@ -0,0 +1,318 @@
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define('urlize', [], factory);
+ } else if (typeof exports === 'object') {
+ // Node. Does not work with strict CommonJS, but
+ module.exports = factory();
+ } else {
+ // Browser globals (root is window)
+ root.urlize = factory(root.b);
+ }
+}(this, function () {
+ // From http://blog.stevenlevithan.com/archives/cross-browser-split
+ // modified to not add itself to String.prototype.
+
+ /*!
+ * Cross-Browser Split 1.1.1
+ * Copyright 2007-2012 Steven Levithan
+ * Available under the MIT License
+ * ECMAScript compliant, uniform cross-browser split method
+ */
+
+ /**
+ * Splits a string into an array of strings using a regex or string separator. Matches of the
+ * separator are not included in the result array. However, if `separator` is a regex that contains
+ * capturing groups, backreferences are spliced into the result each time `separator` is matched.
+ * Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably
+ * cross-browser.
+ * @param {String} str String to split.
+ * @param {RegExp|String} separator Regex or string to use for separating the string.
+ * @param {Number} [limit] Maximum number of items to include in the result array.
+ * @returns {Array} Array of substrings.
+ * @example
+ *
+ * // Basic use
+ * split('a b c d', ' ');
+ * // -> ['a', 'b', 'c', 'd']
+ *
+ * // With limit
+ * split('a b c d', ' ', 2);
+ * // -> ['a', 'b']
+ *
+ * // Backreferences in result array
+ * split('..word1 word2..', /([a-z]+)(\d+)/i);
+ * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
+ */
+ var split;
+
+ // Avoid running twice; that would break the `nativeSplit` reference
+ split = split || function (undef) {
+
+ var nativeSplit = String.prototype.split,
+ compliantExecNpcg = /()??/.exec("")[1] === undef, // NPCG: nonparticipating capturing group
+ self;
+
+ self = function (str, separator, limit) {
+ // If `separator` is not a regex, use `nativeSplit`
+ if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
+ return nativeSplit.call(str, separator, limit);
+ }
+
+ var output = [],
+ flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.extended ? "x" : "") + // Proposed for ES6
+ (separator.sticky ? "y" : ""), // Firefox 3+
+ lastLastIndex = 0, // Make `global` and avoid `lastIndex` issues by working with a copy
+ separator = new RegExp(separator.source, flags + "g"),
+ separator2, match, lastIndex, lastLength;
+
+ str += ""; // Type-convert
+
+ if (!compliantExecNpcg) {
+ // Doesn't need flags gy, but they don't hurt
+ separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
+ }
+
+ /* Values for `limit`, per the spec:
+ * If undefined: 4294967295 // Math.pow(2, 32) - 1
+ * If 0, Infinity, or NaN: 0
+ * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
+ * If negative number: 4294967296 - Math.floor(Math.abs(limit))
+ * If other: Type-convert, then use the above rules
+ */
+ limit = limit === undef ?
+ -1 >>> 0 : // Math.pow(2, 32) - 1
+ limit >>> 0; // ToUint32(limit)
+
+ while (match = separator.exec(str)) {
+ // `separator.lastIndex` is not reliable cross-browser
+ lastIndex = match.index + match[0].length;
+ if (lastIndex > lastLastIndex) {
+ output.push(str.slice(lastLastIndex, match.index));
+ // Fix browsers whose `exec` methods don't consistently return `undefined` for
+ // nonparticipating capturing groups
+ if (!compliantExecNpcg && match.length > 1) {
+ match[0].replace(separator2, function () {
+ for (var i = 1; i < arguments.length - 2; i++) {
+ if (arguments[i] === undef) {
+ match[i] = undef;
+ }
+ }
+ });
+ }
+ if (match.length > 1 && match.index < str.length) {
+ Array.prototype.push.apply(output, match.slice(1));
+ }
+ lastLength = match[0].length;
+ lastLastIndex = lastIndex;
+ if (output.length >= limit) {
+ break;
+ }
+ }
+ if (separator.lastIndex === match.index) {
+ separator.lastIndex++; // Avoid an infinite loop
+ }
+ }
+ if (lastLastIndex === str.length) {
+ if (lastLength || !separator.test("")) {
+ output.push("");
+ }
+ } else {
+ output.push(str.slice(lastLastIndex));
+ }
+ return output.length > limit ? output.slice(0, limit) : output;
+ };
+
+ return self;
+ }();
+
+
+ function startswith(string, prefix) {
+ return string.substr(0, prefix.length) == prefix;
+ }
+
+ function endswith(string, suffix) {
+ return string.substr(string.length - suffix.length, suffix.length) == suffix;
+ }
+
+ // http://stackoverflow.com/a/7924240/17498
+ function occurrences(string, substring) {
+ var n = 0;
+ var pos = 0;
+ while (true) {
+ pos = string.indexOf(substring, pos);
+ if (pos != -1) {
+ n++;
+ pos += substring.length;
+ } else {
+ break;
+ }
+ }
+ return n;
+ }
+
+ var unquoted_percents_re = /%(?![0-9A-Fa-f]{2})/;
+
+ // Quotes a URL if it isn't already quoted.
+ function smart_urlquote(url) {
+ // XXX: Not handling IDN.
+ //
+ // Convert protocol to lowercase.
+ var colonIndex = url.indexOf(':');
+ url = url.substring(0, colonIndex).toLowerCase() + url.substring(colonIndex);
+ //
+ // An URL is considered unquoted if it contains no % characters or
+ // contains a % not followed by two hexadecimal digits.
+ if (url.indexOf('%') == -1 || url.match(unquoted_percents_re)) {
+ return encodeURI(url);
+ } else {
+ return url;
+ }
+ }
+
+ var trailing_punctuation_django = ['.', ',', ':', ';'];
+ var trailing_punctuation_improved = ['.', ',', ':', ';', '.)'];
+ var wrapping_punctuation_django = [['(', ')'], ['<', '>'], ['<', '>']];
+ var wrapping_punctuation_improved = [['(', ')'], ['<', '>'], ['<', '>'],
+ ['\u201c', '\u201d'], ['\u2018', '\u2019']];
+ var word_split_re_django = /(\s+)/;
+ var word_split_re_improved = /([\s<>"]+)/;
+ var simple_url_re = /^https?:\/\/\w/i;
+
+ var django_top_level_domains = ['com', 'edu', 'gov', 'int', 'mil', 'net', 'org'];
+ var simple_email_re = /^\S+@\S+\.\S+$/;
+
+ function htmlescape(html, options) {
+ var escaped = html
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+ if (options && !options.django_compatible) { // only on django_compatible because => https://github.com/ljosa/urlize.js/pull/9
+ escaped = escaped.replace(/\//g, "/");
+ }
+ return escaped;
+ }
+
+ function urlescape(url) {
+ return url // Do not escape slash, because is used for the http:// part
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """);
+ }
+
+ function convert_arguments(args) {
+ var options;
+ if (args.length == 2 && typeof (args[1]) == 'object') {
+ options = args[1];
+ } else {
+ options = {
+ nofollow: args[1],
+ autoescape: args[2],
+ trim_url_limit: args[3],
+ target: args[4]
+ };
+ }
+ if (!('django_compatible' in options)) options.django_compatible = true;
+ return options;
+ }
+
+ function urlize(text, options) {
+ options = convert_arguments(arguments);
+
+ function trim_url(x, limit) {
+ if (options.trim === "http" || options.trim === "www")
+ x = x.replace(/^https?:\/\//i, '');
+ if (options.trim === "www")
+ x = x.replace(/^www\./i, '');
+ if (limit === undefined) limit = options.trim_url_limit;
+ if (limit && x.length > limit) return x.substr(0, limit - 3) + '...';
+ return x;
+ }
+ var safe_input = false;
+ var word_split_re = options.django_compatible ? word_split_re_django : word_split_re_improved;
+ var trailing_punctuation = options.django_compatible ? trailing_punctuation_django : trailing_punctuation_improved;
+ var wrapping_punctuation = options.django_compatible ? wrapping_punctuation_django : wrapping_punctuation_improved;
+ var simple_url_2_re = new RegExp('^www\\.|^(?!http)\\w[^@' + (options.django_compatible ? '' : '.') + ']+\\.(' +
+ (options.top_level_domains || django_top_level_domains).join('|') +
+ ')$',
+ "i");
+ var words = split(text, word_split_re);
+ for (var i = 0; i < words.length; i++) {
+ var word = words[i];
+ var match = undefined;
+ if (word.indexOf('.') != -1 || word.indexOf('@') != -1 || word.indexOf(':') != -1) {
+ // Deal with punctuation.
+ var lead = '';
+ var middle = word;
+ var trail = '';
+ for (var j = 0; j < trailing_punctuation.length; j++) {
+ var punctuation = trailing_punctuation[j];
+ if (endswith(middle, punctuation)) {
+ middle = middle.substr(0, middle.length - punctuation.length);
+ trail = punctuation + trail;
+ }
+ }
+ for (var j = 0; j < wrapping_punctuation.length; j++) {
+ var opening = wrapping_punctuation[j][0];
+ var closing = wrapping_punctuation[j][1];
+ if (startswith(middle, opening)) {
+ middle = middle.substr(opening.length);
+ lead = lead + opening;
+ }
+ // Keep parentheses at the end only if they're balanced.
+ if (endswith(middle, closing) && occurrences(middle, closing) == occurrences(middle, opening) + 1) {
+ middle = middle.substr(0, middle.length - closing.length);
+ trail = closing + trail;
+ }
+ }
+
+ // Make URL we want to point to.
+ var url = undefined;
+ var nofollow_attr = options.nofollow ? ' rel="nofollow"' : '';
+ var target_attr = options.target ? ' target="' + options.target + '"' : '';
+
+ if (middle.match(simple_url_re)) url = smart_urlquote(middle);
+ else if (middle.match(simple_url_2_re)) url = smart_urlquote('http://' + middle);
+ else if (middle.indexOf(':') == -1 && middle.match(simple_email_re)) {
+ // XXX: Not handling IDN.
+ url = 'mailto:' + middle;
+ nofollow_attr = '';
+ }
+
+ // Make link.
+ if (url) {
+ var trimmed = trim_url(middle);
+ if (options.autoescape) {
+ // XXX: Assuming autoscape == false
+ lead = htmlescape(lead, options);
+ trail = htmlescape(trail, options);
+ url = urlescape(url);
+ trimmed = htmlescape(trimmed, options);
+ }
+ middle = '' + trimmed + '';
+ words[i] = lead + middle + trail;
+ } else {
+ if (safe_input) {
+ // Do nothing, as we have no mark_safe.
+ } else if (options.autoescape) {
+ words[i] = htmlescape(word, options);
+ }
+ }
+ } else if (safe_input) {
+ // Do nothing, as we have no mark_safe.
+ } else if (options.autoescape) {
+ words[i] = htmlescape(word, options);
+ }
+ }
+ return words.join('');
+ }
+
+ urlize.test = {};
+ urlize.test.split = split;
+ urlize.test.convert_arguments = convert_arguments;
+
+ return urlize;
+}));