2014-08-24 18:53:55 +02:00
|
|
|
(function() {
|
|
|
|
'use strict';
|
|
|
|
|
2014-08-28 18:25:40 +02:00
|
|
|
var weechat = angular.module('weechat');
|
|
|
|
|
|
|
|
weechat.filter('toArray', function () {
|
2014-09-03 14:21:11 +02:00
|
|
|
return function (obj, storeIdx) {
|
2014-08-28 18:25:40 +02:00
|
|
|
if (!(obj instanceof Object)) {
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2014-09-03 14:21:11 +02:00
|
|
|
if (storeIdx) {
|
|
|
|
return Object.keys(obj).map(function (key, idx) {
|
|
|
|
return Object.defineProperties(obj[key], {
|
|
|
|
'$key' : { value: key },
|
|
|
|
'$idx' : { value: idx, configurable: true }
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-08-28 18:25:40 +02:00
|
|
|
return Object.keys(obj).map(function (key) {
|
|
|
|
return Object.defineProperty(obj[key], '$key', { value: key });
|
|
|
|
});
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2015-06-04 07:14:56 +02:00
|
|
|
weechat.filter('irclinky', function() {
|
2014-11-06 16:31:31 +01:00
|
|
|
return function(text) {
|
2014-08-28 18:25:40 +02:00
|
|
|
if (!text) {
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
2014-09-28 20:45:29 +02:00
|
|
|
// This regex in no way matches all IRC channel names (they could also begin with &, + or an
|
2014-09-25 17:26:18 +02:00
|
|
|
// exclamation mark followed by 5 alphanumeric characters, and are bounded in length by 50).
|
|
|
|
// However, it matches all *common* IRC channels while trying to minimise false positives.
|
|
|
|
// "#1" is much more likely to be "number 1" than "IRC channel #1".
|
2014-09-28 20:45:29 +02:00
|
|
|
// Thus, we only match channels beginning with a # and having at least one letter in them.
|
2014-12-20 20:10:33 +01:00
|
|
|
var channelRegex = /(^|[\s,.:;?!"'()+@-\~%])(#+[^\x00\x07\r\n\s,:]*[a-z][^\x00\x07\r\n\s,:]*)/gmi;
|
2015-06-04 07:14:56 +02:00
|
|
|
// Call the method we bound to window.openBuffer when we instantiated
|
|
|
|
// the Weechat controller.
|
|
|
|
var substitute = '$1<a href="#" onclick="openBuffer(\'$2\');">$2</a>';
|
2014-11-06 13:55:33 +01:00
|
|
|
return text.replace(channelRegex, substitute);
|
2014-08-28 18:25:40 +02:00
|
|
|
};
|
2015-06-04 07:14:56 +02:00
|
|
|
});
|
2014-08-28 18:25:40 +02:00
|
|
|
|
2014-10-27 21:50:01 +01:00
|
|
|
weechat.filter('inlinecolour', function() {
|
2014-08-28 18:25:40 +02:00
|
|
|
return function(text) {
|
|
|
|
if (!text) {
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
|
|
|
// only match 6-digit colour codes, 3-digit ones have too many false positives (issue numbers, etc)
|
2014-12-30 21:06:17 +01:00
|
|
|
var hexColourRegex = /(^|[^&])(\#[0-9a-f]{6};?)(?!\w)/gmi;
|
|
|
|
var rgbColourRegex = /(.?)(rgba?\((?:\s*\d+\s*,){2}\s*\d+\s*(?:,\s*[\d.]+\s*)?\);?)/gmi;
|
|
|
|
var substitute = '$1$2 <div class="colourbox" style="background-color:$2"></div>';
|
|
|
|
text = text.replace(hexColourRegex, substitute);
|
|
|
|
text = text.replace(rgbColourRegex, substitute);
|
|
|
|
return text;
|
2014-10-27 21:50:01 +01:00
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2019-04-13 04:59:22 +02:00
|
|
|
// Calls the 'linky' filter unless the disable flag is set. Useful for things like join/quit messages,
|
|
|
|
// so you don't accidentally click a mailto: on someone's hostmask.
|
|
|
|
weechat.filter('conditionalLinkify', ['$filter', function($filter) {
|
|
|
|
return function(text, disable) {
|
|
|
|
if (!text || disable) {
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
return $filter('linky')(text, '_blank', {rel:'noopener noreferrer'});
|
|
|
|
};
|
|
|
|
}]);
|
|
|
|
|
2014-10-27 21:50:01 +01:00
|
|
|
// apply a filter to an HTML string's text nodes, and do so with not exceedingly terrible performance
|
|
|
|
weechat.filter('DOMfilter', ['$filter', '$sce', function($filter, $sce) {
|
2015-11-27 16:58:07 +01:00
|
|
|
// To prevent nested anchors, we need to know if a filter is going to create them.
|
|
|
|
// Here's a list of names. See #681 for more information.
|
|
|
|
var filtersThatCreateAnchors = ['irclinky'];
|
|
|
|
|
2014-10-27 21:50:01 +01:00
|
|
|
return function(text, filter) {
|
|
|
|
if (!text || !filter) {
|
|
|
|
return text;
|
|
|
|
}
|
2015-11-27 16:58:07 +01:00
|
|
|
var createsAnchor = filtersThatCreateAnchors.indexOf(filter) > -1;
|
2014-10-27 21:50:01 +01:00
|
|
|
|
2015-06-08 23:35:18 +02:00
|
|
|
var escape_html = function(text) {
|
|
|
|
// First, escape entities to prevent escaping issues because it's a bad idea
|
|
|
|
// to parse/modify HTML with regexes, which we do a couple of lines down...
|
|
|
|
var entities = {"<": "<", ">": ">", '"': '"', "'": ''', "&": "&", "/": '/'};
|
|
|
|
return text.replace(/[<>"'&\/]/g, function (char) {
|
|
|
|
return entities[char];
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2015-03-21 12:25:18 +01:00
|
|
|
// hacky way to pass extra arguments without using .apply, which
|
2015-01-03 18:29:59 +01:00
|
|
|
// would require assembling an argument array. PERFORMANCE!!!
|
|
|
|
var extraArgument = (arguments.length > 2) ? arguments[2] : null;
|
2015-03-21 12:25:18 +01:00
|
|
|
var thirdArgument = (arguments.length > 3) ? arguments[3] : null;
|
|
|
|
|
2014-10-27 21:50:01 +01:00
|
|
|
var filterFunction = $filter(filter);
|
|
|
|
var el = document.createElement('div');
|
|
|
|
el.innerHTML = text;
|
|
|
|
|
|
|
|
// Recursive DOM-walking function applying the filter to the text nodes
|
|
|
|
var process = function(node) {
|
|
|
|
if (node.nodeType === 3) { // text node
|
2015-06-08 23:35:18 +02:00
|
|
|
// apply the filter to *escaped* HTML, and only commit changes if
|
|
|
|
// it changed the escaped value. This is because setting the result
|
|
|
|
// as innerHTML causes it to be unescaped.
|
|
|
|
var input = escape_html(node.nodeValue);
|
|
|
|
var value = filterFunction(input, extraArgument, thirdArgument);
|
2015-11-27 16:58:07 +01:00
|
|
|
|
2015-06-08 23:35:18 +02:00
|
|
|
if (value !== input) {
|
2014-10-27 21:50:01 +01:00
|
|
|
// we changed something. create a new node to replace the current one
|
|
|
|
// we could also only add its children but that would probably incur
|
|
|
|
// more overhead than it would gain us
|
|
|
|
var newNode = document.createElement('span');
|
|
|
|
newNode.innerHTML = value;
|
|
|
|
|
|
|
|
var parent = node.parentNode;
|
|
|
|
var sibling = node.nextSibling;
|
|
|
|
parent.removeChild(node);
|
|
|
|
if (sibling) {
|
|
|
|
parent.insertBefore(newNode, sibling);
|
|
|
|
} else {
|
|
|
|
parent.appendChild(newNode);
|
|
|
|
}
|
2014-12-30 20:23:32 +01:00
|
|
|
return newNode;
|
2014-10-27 21:50:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// recurse
|
2014-12-30 20:23:32 +01:00
|
|
|
if (node === undefined || node === null) return;
|
2014-10-27 21:50:01 +01:00
|
|
|
node = node.firstChild;
|
|
|
|
while (node) {
|
2015-12-01 20:31:12 +01:00
|
|
|
var nextNode = null;
|
2015-11-27 16:58:07 +01:00
|
|
|
// do not recurse inside links if the filter would create a nested link
|
|
|
|
if (!(createsAnchor && node.tagName === 'A')) {
|
|
|
|
nextNode = process(node);
|
|
|
|
}
|
2014-12-30 20:23:32 +01:00
|
|
|
node = (nextNode ? nextNode : node).nextSibling;
|
2014-10-27 21:50:01 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
process(el);
|
|
|
|
|
|
|
|
return $sce.trustAsHtml(el.innerHTML);
|
2014-08-28 18:25:40 +02:00
|
|
|
};
|
|
|
|
}]);
|
2014-09-03 14:21:11 +02:00
|
|
|
|
2017-04-23 14:34:12 +02:00
|
|
|
// This is used by the cordova app to change link targets to "window.open(<url>, '_system')"
|
|
|
|
// so that they're opened in a browser window and don't navigate away from Glowing Bear
|
2017-03-18 06:58:15 +01:00
|
|
|
weechat.filter('linksForCordova', ['$sce', function($sce) {
|
|
|
|
return function(text) {
|
2017-04-19 14:55:48 +02:00
|
|
|
// XXX TODO this needs to be improved
|
2017-04-23 14:34:12 +02:00
|
|
|
text = text.replace(/<a (rel="[a-z ]+"\s+)?(?:target="_[a-z]+"\s+)?href="([^"]+)"/gi,
|
|
|
|
"<a $1 onClick=\"window.open('$2', '_system')\"");
|
2017-03-18 06:58:15 +01:00
|
|
|
return $sce.trustAsHtml(text);
|
|
|
|
};
|
|
|
|
}]);
|
|
|
|
|
2014-09-03 14:21:11 +02:00
|
|
|
weechat.filter('getBufferQuickKeys', function () {
|
|
|
|
return function (obj, $scope) {
|
|
|
|
if (!$scope) { return obj; }
|
|
|
|
if (($scope.search !== undefined && $scope.search.length) || $scope.onlyUnread) {
|
|
|
|
obj.forEach(function(buf, idx) {
|
|
|
|
buf.$quickKey = idx < 10 ? (idx + 1) % 10 : '';
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
_.map(obj, function(buffer, idx) {
|
|
|
|
return [buffer.number, buffer.$idx, idx];
|
|
|
|
}).sort(function(left, right) {
|
|
|
|
// By default, Array.prototype.sort() sorts alphabetically.
|
|
|
|
// Pass an ordering function to sort by first element.
|
|
|
|
return left[0] - right[0] || left[1] - right[1];
|
|
|
|
}).forEach(function(info, keyIdx) {
|
|
|
|
obj[ info[2] ].$quickKey = keyIdx < 10 ? (keyIdx + 1) % 10 : '';
|
2017-03-18 16:31:55 +01:00
|
|
|
// Don't update jump key upon filtering
|
|
|
|
if (obj[ info[2] ].$jumpKey === undefined) {
|
|
|
|
// Only assign jump keys up to 99
|
|
|
|
obj[ info[2] ].$jumpKey = (keyIdx < 99) ? keyIdx + 1 : '';
|
|
|
|
}
|
2014-09-03 14:21:11 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2015-03-21 16:43:46 +01:00
|
|
|
// Emojifis the string using https://github.com/Ranks/emojione
|
2015-01-03 18:29:59 +01:00
|
|
|
weechat.filter('emojify', function() {
|
|
|
|
return function(text, enable_JS_Emoji) {
|
2015-03-23 15:07:51 +01:00
|
|
|
if (enable_JS_Emoji === true && window.emojione !== undefined) {
|
2016-01-02 18:14:48 +01:00
|
|
|
// Emoji live in the D800-DFFF surrogate plane; only bother passing
|
|
|
|
// this range to CPU-expensive unicodeToImage();
|
|
|
|
var emojiRegex = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
|
|
|
|
if (emojiRegex.test(text)) {
|
|
|
|
return emojione.unicodeToImage(text);
|
|
|
|
} else {
|
|
|
|
return(text);
|
|
|
|
}
|
2015-01-03 18:29:59 +01:00
|
|
|
} else {
|
|
|
|
return(text);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2016-04-03 14:32:17 +02:00
|
|
|
weechat.filter('latexmath', function() {
|
2015-03-21 12:25:18 +01:00
|
|
|
return function(text, selector, enabled) {
|
2016-04-03 14:32:17 +02:00
|
|
|
if (!enabled || typeof(katex) === "undefined") {
|
2015-03-21 12:25:18 +01:00
|
|
|
return text;
|
|
|
|
}
|
2015-03-09 15:09:32 +01:00
|
|
|
if (text.indexOf("$$") != -1 || text.indexOf("\\[") != -1 || text.indexOf("\\(") != -1) {
|
2016-04-03 14:32:17 +02:00
|
|
|
// contains math -> delayed rendering
|
|
|
|
setTimeout(function() {
|
|
|
|
var math = document.querySelector(selector);
|
|
|
|
renderMathInElement(math, {
|
|
|
|
delimiters: [
|
|
|
|
{left: "$$", right: "$$", display: false},
|
|
|
|
{left: "\\[", right: "\\]", display: true},
|
|
|
|
{left: "\\(", right: "\\)", display: false}
|
|
|
|
]
|
|
|
|
});
|
|
|
|
});
|
2015-03-09 15:09:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return text;
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2015-03-26 14:41:19 +01:00
|
|
|
weechat.filter('prefixlimit', function() {
|
|
|
|
return function(input, chars) {
|
|
|
|
if (isNaN(chars)) return input;
|
|
|
|
if (chars <= 0) return '';
|
|
|
|
if (input && input.length > chars) {
|
|
|
|
input = input.substring(0, chars);
|
|
|
|
return input + '+';
|
|
|
|
}
|
|
|
|
return input;
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2019-07-03 09:01:05 +02:00
|
|
|
weechat.filter('codify', function() {
|
|
|
|
return function(text) {
|
2019-08-19 13:18:59 +02:00
|
|
|
// The groups of this regex are:
|
|
|
|
// 1. Start of line or space, to prevent codifying weird`stuff` like this
|
|
|
|
// 2. Opening single or triple backticks (not 2, not more than 3)
|
|
|
|
// 3. The code block, does not start with another backtick, non-greedy expansion
|
|
|
|
// 4. The closing backticks, identical to group 2
|
|
|
|
var re = /(^|\s)(```|`)([^`].*?)\2/g;
|
|
|
|
return text.replace(re, function(match, ws, open, code) {
|
|
|
|
var rr = ws + '<span class="hidden-bracket">' + open + '</span><code>' + code + '</code><span class="hidden-bracket">' + open + '</span>';
|
2019-07-03 09:01:05 +02:00
|
|
|
return rr;
|
2019-07-03 09:20:14 +02:00
|
|
|
});
|
2019-07-03 09:01:05 +02:00
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2014-08-24 18:53:55 +02:00
|
|
|
})();
|