2015-06-05 03:59:28 -04:00
|
|
|
var FFZ = window.FrankerFaceZ,
|
|
|
|
utils = require("./utils"),
|
2015-07-04 17:06:36 -04:00
|
|
|
constants = require("./constants"),
|
2015-06-05 03:59:28 -04:00
|
|
|
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
|
|
|
|
helpers,
|
|
|
|
|
2015-07-29 01:03:10 -04:00
|
|
|
SRCSETS = {};
|
|
|
|
build_srcset = function(id) {
|
|
|
|
if ( SRCSETS[id] )
|
|
|
|
return SRCSETS[id];
|
|
|
|
var out = SRCSETS[id] = TWITCH_BASE + id + "/1.0 1x, " + TWITCH_BASE + id + "/2.0 2x, " + TWITCH_BASE + id + "/3.0 4x";
|
|
|
|
return out;
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
data_to_tooltip = function(data) {
|
|
|
|
var set = data.set,
|
|
|
|
set_type = data.set_type,
|
|
|
|
owner = data.owner;
|
|
|
|
|
|
|
|
if ( set_type === undefined )
|
|
|
|
set_type = "Channel";
|
|
|
|
|
|
|
|
if ( ! set )
|
|
|
|
return data.code;
|
|
|
|
|
|
|
|
else if ( set == "--twitch-turbo--" || set == "turbo" ) {
|
|
|
|
set = "Twitch Turbo";
|
|
|
|
set_type = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "Emoticon: " + data.code + "\n" + (set_type ? set_type + ": " : "") + set + (owner ? "\nBy: " + owner.display_name : "");
|
|
|
|
},
|
|
|
|
|
|
|
|
build_tooltip = function(id) {
|
|
|
|
var emote_data = this._twitch_emotes[id],
|
|
|
|
set = emote_data ? emote_data.set : null;
|
|
|
|
|
|
|
|
if ( ! emote_data )
|
|
|
|
return "???";
|
|
|
|
|
|
|
|
if ( typeof emote_data == "string" )
|
|
|
|
return emote_data;
|
|
|
|
|
|
|
|
if ( emote_data.tooltip )
|
|
|
|
return emote_data.tooltip;
|
|
|
|
|
|
|
|
return emote_data.tooltip = data_to_tooltip(emote_data);
|
|
|
|
},
|
|
|
|
|
|
|
|
load_emote_data = function(id, code, success, data) {
|
|
|
|
if ( ! success )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( code )
|
|
|
|
data.code = code;
|
|
|
|
|
|
|
|
this._twitch_emotes[id] = data;
|
|
|
|
var tooltip = build_tooltip.bind(this)(id);
|
|
|
|
|
|
|
|
var images = document.querySelectorAll('img[data-emote="' + id + '"]');
|
|
|
|
for(var x=0; x < images.length; x++)
|
|
|
|
images[x].title = tooltip;
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2015-06-05 03:59:28 -04:00
|
|
|
reg_escape = function(str) {
|
|
|
|
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
|
|
|
},
|
|
|
|
|
2015-07-18 21:10:27 -04:00
|
|
|
LINK = /(?:https?:\/\/)?(?:[-a-zA-Z0-9@:%_\+~#=]+\.)+[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#!?&//=]*)/g,
|
|
|
|
|
2015-06-05 03:59:28 -04:00
|
|
|
SEPARATORS = "[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]",
|
2015-07-29 01:03:10 -04:00
|
|
|
SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*"),
|
|
|
|
|
|
|
|
|
|
|
|
LINK_SPLIT = /^(?:(https?):\/\/)?(?:(.*?)@)?([^\/:]+)(?::(\d+))?(.*?)(?:\?(.*?))?(?:\#(.*?))?$/,
|
|
|
|
YOUTUBE_CHECK = /^(?:https?:\/\/)?(?:m\.|www\.)?youtu(?:be\.com|\.be)\/(?:v\/|watch\/|.*?(?:embed|watch).*?v=)?([a-zA-Z0-9\-_]+)$/,
|
|
|
|
IMGUR_PATH = /^\/(?:gallery\/)?[A-Za-z0-9]+(?:\.(?:png|jpg|jpeg|gif|gifv|bmp))?$/,
|
|
|
|
IMAGE_EXT = /\.(?:png|jpg|jpeg|gif|bmp)$/i,
|
|
|
|
IMAGE_DOMAINS = [],
|
|
|
|
|
|
|
|
is_image = function(href, any_domain) {
|
|
|
|
var match = href.match(LINK_SPLIT);
|
|
|
|
if ( ! match )
|
|
|
|
return;
|
|
|
|
|
|
|
|
var domain = match[3].toLowerCase(), port = match[4],
|
|
|
|
path = match[5];
|
|
|
|
|
|
|
|
// Don't allow non-standard ports.
|
|
|
|
if ( port && port !== '80' && port !== '443' )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// imgur-specific checks.
|
|
|
|
if ( domain === 'i.imgur.com' || domain === 'imgur.com' || domain === 'www.imgur.com' || domain === 'm.imgur.com' )
|
|
|
|
return IMGUR_PATH.test(path);
|
|
|
|
|
|
|
|
return any_domain ? IMAGE_EXT.test(path) : IMAGE_DOMAINS.indexOf(domain) !== -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
image_iframe = function(href, extra_class) {
|
|
|
|
return '<iframe class="ffz-image-hover' + (extra_class ? ' ' + extra_class : '') + '" allowtransparency="true" src="' + constants.SERVER + 'script/image-proxy.html?' + utils.quote_attr(href) + '"></iframe>';
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
build_link_tooltip = function(href) {
|
|
|
|
var link_data = this._link_data[href],
|
|
|
|
tooltip;
|
|
|
|
|
|
|
|
if ( ! link_data )
|
|
|
|
return "";
|
|
|
|
|
|
|
|
if ( link_data.tooltip )
|
|
|
|
return link_data.tooltip;
|
|
|
|
|
|
|
|
if ( link_data.type == "youtube" ) {
|
|
|
|
tooltip = this.settings.link_image_hover ? image_iframe(link_data.full || href, 'ffz-yt-thumb') : '';
|
|
|
|
tooltip += "<b>YouTube: " + utils.sanitize(link_data.title) + "</b><hr>";
|
|
|
|
tooltip += "Channel: " + utils.sanitize(link_data.channel) + " | " + utils.time_to_string(link_data.duration) + "<br>";
|
|
|
|
tooltip += utils.number_commas(link_data.views||0) + " Views | 👍 " + utils.number_commas(link_data.likes||0) + " 👎 " + utils.number_commas(link_data.dislikes||0);
|
|
|
|
|
|
|
|
} else if ( link_data.type == "strawpoll" ) {
|
|
|
|
tooltip = "<b>Strawpoll: " + utils.sanitize(link_data.title) + "</b><hr><table><tbody>";
|
|
|
|
for(var key in link_data.items) {
|
|
|
|
var votes = link_data.items[key],
|
|
|
|
percentage = Math.floor((votes / link_data.total) * 100);
|
|
|
|
tooltip += '<tr><td style="text-align:left">' + utils.sanitize(key) + '</td><td style="text-align:right">' + utils.number_commas(votes) + "</td></tr>";
|
|
|
|
}
|
|
|
|
tooltip += "</tbody></table><hr>Total: " + utils.number_commas(link_data.total);
|
|
|
|
var fetched = utils.parse_date(link_data.fetched);
|
|
|
|
if ( fetched ) {
|
|
|
|
var age = Math.floor((fetched.getTime() - Date.now()) / 1000);
|
|
|
|
if ( age > 60 )
|
|
|
|
tooltip += "<br><small>Data was cached " + utils.time_to_string(age) + " ago.</small>";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} else if ( link_data.type == "twitch" ) {
|
|
|
|
tooltip = "<b>Twitch: " + utils.sanitize(link_data.display_name) + "</b><hr>";
|
|
|
|
var since = utils.parse_date(link_data.since);
|
|
|
|
if ( since )
|
|
|
|
tooltip += "Member Since: " + utils.date_string(since) + "<br>";
|
|
|
|
tooltip += "<nobr>Views: " + utils.number_commas(link_data.views) + "</nobr> | <nobr>Followers: " + utils.number_commas(link_data.followers) + "</nobr>";
|
|
|
|
|
|
|
|
|
|
|
|
} else if ( link_data.type == "twitch_vod" ) {
|
|
|
|
tooltip = "<b>Twitch " + (link_data.broadcast_type == "highlight" ? "Highlight" : "Broadcast") + ": " + utils.sanitize(link_data.title) + "</b><hr>";
|
|
|
|
tooltip += "By: " + utils.sanitize(link_data.display_name) + (link_data.game ? " | Playing: " + utils.sanitize(link_data.game) : " | Not Playing") + "<br>";
|
|
|
|
tooltip += "Views: " + utils.number_commas(link_data.views) + " | " + utils.time_to_string(link_data.length);
|
|
|
|
|
|
|
|
|
|
|
|
} else if ( link_data.type == "twitter" ) {
|
|
|
|
tooltip = "<b>Tweet By: " + utils.sanitize(link_data.user) + "</b><hr>";
|
|
|
|
tooltip += utils.sanitize(link_data.tweet);
|
|
|
|
|
|
|
|
|
|
|
|
} else if ( link_data.type == "reputation" ) {
|
|
|
|
tooltip = (this.settings.link_image_hover && is_image(link_data.full || href, this.settings.image_hover_all_domains)) ? image_iframe(link_data.full || href) : '';
|
|
|
|
tooltip += '<span style="word-wrap: break-word">' + utils.sanitize(link_data.full.toLowerCase()) + '</span>';
|
|
|
|
if ( link_data.trust < 50 || link_data.safety < 50 || (link_data.tags && link_data.tags.length > 0) ) {
|
|
|
|
tooltip += "<hr>";
|
|
|
|
var had_extra = false;
|
|
|
|
if ( link_data.trust < 50 || link_data.safety < 50 ) {
|
|
|
|
link_data.unsafe = true;
|
|
|
|
tooltip += "<b>Potentially Unsafe Link</b><br>";
|
|
|
|
tooltip += "Trust: " + link_data.trust + "% | Child Safety: " + link_data.safety + "%";
|
|
|
|
had_extra = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( link_data.tags && link_data.tags.length > 0 )
|
|
|
|
tooltip += (had_extra ? "<br>" : "") + "Tags: " + link_data.tags.join(", ");
|
|
|
|
|
|
|
|
tooltip += "<br>Data Source: WOT";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} else if ( link_data.full ) {
|
|
|
|
tooltip = (this.settings.link_image_hover && is_image(link_data.full || href, this.settings.image_hover_all_domains)) ? image_iframe(link_data.full || href) : '';
|
|
|
|
tooltip += '<span style="word-wrap: break-word">' + utils.sanitize(link_data.full.toLowerCase()) + '</span>';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! tooltip )
|
|
|
|
tooltip = '<span style="word-wrap: break-word">' + utils.sanitize(href.toLowerCase()) + '</span>';
|
|
|
|
|
|
|
|
link_data.tooltip = tooltip;
|
|
|
|
return tooltip;
|
|
|
|
},
|
|
|
|
|
|
|
|
load_link_data = function(href, success, data) {
|
|
|
|
if ( ! success )
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._link_data[href] = data;
|
|
|
|
data.unsafe = false;
|
|
|
|
|
|
|
|
var tooltip = build_link_tooltip.bind(this)(href), links,
|
|
|
|
no_trail = href.charAt(href.length-1) == "/" ? href.substr(0, href.length-1) : null;
|
|
|
|
|
|
|
|
if ( no_trail )
|
|
|
|
links = document.querySelectorAll('span.message a[href="' + href + '"], span.message a[href="' + no_trail + '"], span.message a[data-url="' + href + '"], span.message a[data-url="' + no_trail + '"]');
|
|
|
|
else
|
|
|
|
links = document.querySelectorAll('span.message a[href="' + href + '"], span.message a[data-url="' + href + '"]');
|
|
|
|
|
|
|
|
if ( ! this.settings.link_info )
|
|
|
|
return;
|
|
|
|
|
|
|
|
for(var x=0; x < links.length; x++) {
|
|
|
|
if ( data.unsafe )
|
|
|
|
links[x].classList.add('unsafe-link');
|
|
|
|
|
|
|
|
if ( ! links[x].classList.contains('deleted-link') )
|
|
|
|
links[x].title = tooltip;
|
|
|
|
}
|
|
|
|
};
|
2015-06-05 03:59:28 -04:00
|
|
|
|
2015-07-04 17:06:36 -04:00
|
|
|
|
|
|
|
FFZ.SRC_IDS = {},
|
|
|
|
FFZ.src_to_id = function(src) {
|
|
|
|
if ( FFZ.SRC_IDS.hasOwnProperty(src) )
|
|
|
|
return FFZ.SRC_IDS[src];
|
|
|
|
|
|
|
|
var match = /\/emoticons\/v1\/(\d+)\/1\.0/.exec(src),
|
|
|
|
id = match ? parseInt(match[1]) : null;
|
|
|
|
|
|
|
|
if ( id === NaN )
|
|
|
|
id = null;
|
|
|
|
|
|
|
|
FFZ.SRC_IDS[src] = id;
|
|
|
|
return id;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-07-13 21:52:44 -04:00
|
|
|
// ---------------------
|
2015-07-18 21:10:27 -04:00
|
|
|
// Settings
|
2015-07-13 21:52:44 -04:00
|
|
|
// ---------------------
|
|
|
|
|
|
|
|
var ts = new Date(0).toLocaleTimeString().toUpperCase();
|
|
|
|
|
|
|
|
FFZ.settings_info.twenty_four_timestamps = {
|
|
|
|
type: "boolean",
|
|
|
|
value: ts.lastIndexOf('PM') === -1 && ts.lastIndexOf('AM') === -1,
|
|
|
|
|
|
|
|
category: "Chat Appearance",
|
|
|
|
no_bttv: true,
|
|
|
|
|
|
|
|
name: "24hr Timestamps",
|
|
|
|
help: "Display timestamps in chat in the 24 hour format rather than 12 hour."
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-07-18 21:10:27 -04:00
|
|
|
FFZ.settings_info.show_deleted_links = {
|
|
|
|
type: "boolean",
|
|
|
|
value: false,
|
|
|
|
|
|
|
|
category: "Chat Moderation",
|
|
|
|
no_bttv: true,
|
|
|
|
|
|
|
|
name: "Show Deleted Links",
|
|
|
|
help: "Do not delete links based on room settings or link length."
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------
|
|
|
|
// Setup
|
|
|
|
// ---------------------
|
|
|
|
|
|
|
|
FFZ.prototype.setup_tokenization = function() {
|
|
|
|
helpers = window.require && window.require("ember-twitch-chat/helpers/chat-line-helpers");
|
|
|
|
if ( ! helpers )
|
|
|
|
return this.log("Unable to get chat helper functions.");
|
|
|
|
|
|
|
|
this.log("Hooking Ember chat line helpers.");
|
|
|
|
|
|
|
|
var f = this;
|
|
|
|
|
|
|
|
// Timestamp Display
|
2015-07-13 21:52:44 -04:00
|
|
|
helpers.getTime = function(e) {
|
2015-07-18 21:10:27 -04:00
|
|
|
if ( e === undefined || e === null )
|
|
|
|
return '?:??';
|
|
|
|
|
2015-07-13 21:52:44 -04:00
|
|
|
var hours = e.getHours(),
|
|
|
|
minutes = e.getMinutes();
|
2015-07-18 21:10:27 -04:00
|
|
|
|
|
|
|
if ( hours > 12 && ! f.settings.twenty_four_timestamps )
|
2015-07-13 21:52:44 -04:00
|
|
|
hours -= 12;
|
2015-07-18 21:10:27 -04:00
|
|
|
else if ( hours === 0 && ! f.settings.twenty_four_timestamps )
|
2015-07-13 21:52:44 -04:00
|
|
|
hours = 12;
|
2015-07-18 21:10:27 -04:00
|
|
|
|
2015-07-13 21:52:44 -04:00
|
|
|
return hours + ':' + (minutes < 10 ? '0' : '') + minutes;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-07-18 21:10:27 -04:00
|
|
|
// Linkify Messages
|
|
|
|
helpers.linkifyMessage = function(tokens, delete_links) {
|
|
|
|
var show_deleted = f.settings.show_deleted_links;
|
|
|
|
|
|
|
|
return _.chain(tokens).map(function(token) {
|
|
|
|
if ( ! _.isString(token) )
|
|
|
|
return token;
|
|
|
|
|
|
|
|
var matches = token.match(LINK);
|
|
|
|
if ( ! matches || ! matches.length )
|
|
|
|
return [token];
|
|
|
|
|
|
|
|
return _.zip(
|
|
|
|
token.split(LINK),
|
|
|
|
_.map(matches, function(e) {
|
2015-07-29 01:03:10 -04:00
|
|
|
var long = e.length > 255;
|
|
|
|
if ( ! show_deleted && (delete_links || long) )
|
|
|
|
return {isLink: true, isDeleted: true, isLong: long, href: e};
|
|
|
|
//return {mentionedUser: '</span><a class="deleted-link" title="' + utils.quote_attr(e) + '" data-url="' + utils.quote_attr(e) + '" href="#"><' + (e.length > 255 ? 'long link' : 'deleted link') + '></a><span class="mentioning">', own: true}
|
2015-07-18 21:10:27 -04:00
|
|
|
return {isLink: true, href: e};
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}).flatten().compact().value();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-05 03:59:28 -04:00
|
|
|
// ---------------------
|
|
|
|
// Tokenization
|
|
|
|
// ---------------------
|
|
|
|
|
2015-07-29 01:03:10 -04:00
|
|
|
FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, delete_links) {
|
2015-06-05 03:59:28 -04:00
|
|
|
if ( msgObject.cachedTokens )
|
|
|
|
return msgObject.cachedTokens;
|
|
|
|
|
|
|
|
var msg = msgObject.message,
|
|
|
|
user = this.get_user(),
|
|
|
|
room_id = msgObject.room,
|
|
|
|
from_me = user && msgObject.from === user.login,
|
|
|
|
emotes = msgObject.tags && msgObject.tags.emotes,
|
|
|
|
|
|
|
|
tokens = [msg];
|
|
|
|
|
|
|
|
// Standard tokenization
|
2015-07-29 01:03:10 -04:00
|
|
|
if ( helpers && helpers.linkifyMessage ) {
|
|
|
|
var labels = msg.labels || [],
|
|
|
|
mod_or_higher = labels.indexOf("owner") !== -1 ||
|
|
|
|
labels.indexOf("staff") !== -1 ||
|
|
|
|
labels.indexOf("admin") !== -1 ||
|
|
|
|
labels.indexOf("global_mod") !== -1 ||
|
|
|
|
labels.indexOf("mod") !== -1 ||
|
|
|
|
msg.style === 'admin';
|
|
|
|
|
|
|
|
tokens = helpers.linkifyMessage(tokens, delete_links && !mod_or_higher);
|
|
|
|
}
|
|
|
|
|
2015-07-18 21:10:27 -04:00
|
|
|
|
|
|
|
if ( user && user.login && helpers && helpers.mentionizeMessage )
|
2015-06-05 03:59:28 -04:00
|
|
|
tokens = helpers.mentionizeMessage(tokens, user.login, from_me);
|
2015-07-06 00:09:21 -04:00
|
|
|
|
2015-07-18 21:10:27 -04:00
|
|
|
if ( helpers && helpers.emoticonizeMessage )
|
|
|
|
tokens = helpers.emoticonizeMessage(tokens, emotes);
|
|
|
|
|
2015-07-06 00:09:21 -04:00
|
|
|
if ( this.settings.replace_bad_emotes )
|
|
|
|
tokens = this.tokenize_replace_emotes(tokens);
|
2015-06-05 03:59:28 -04:00
|
|
|
|
|
|
|
// FrankerFaceZ Extras
|
|
|
|
tokens = this._remove_banned(tokens);
|
|
|
|
tokens = this.tokenize_emotes(msgObject.from, room_id, tokens, from_me);
|
|
|
|
|
2015-07-04 17:06:36 -04:00
|
|
|
if ( this.settings.parse_emoji )
|
|
|
|
tokens = this.tokenize_emoji(tokens);
|
|
|
|
|
2015-06-05 03:59:28 -04:00
|
|
|
// Capitalization
|
|
|
|
var display = msgObject.tags && msgObject.tags['display-name'];
|
|
|
|
if ( display && display.length )
|
|
|
|
FFZ.capitalization[msgObject.from] = [display.trim(), Date.now()];
|
|
|
|
|
|
|
|
|
|
|
|
// Mentions!
|
|
|
|
if ( ! from_me ) {
|
|
|
|
tokens = this.tokenize_mentions(tokens);
|
|
|
|
|
|
|
|
for(var i=0; i < tokens.length; i++) {
|
|
|
|
var token = tokens[i];
|
2015-07-04 17:06:36 -04:00
|
|
|
if ( msgObject.style !== 'whisper' && (_.isString(token) || ! token.mentionedUser || token.own) )
|
2015-06-05 03:59:28 -04:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// We have a mention!
|
|
|
|
msgObject.ffz_has_mention = true;
|
|
|
|
|
|
|
|
// If we have chat tabs, update the status.
|
2015-06-10 18:46:04 -04:00
|
|
|
if ( room_id && ! this.has_bttv && this.settings.group_tabs && this._chatv && this._chatv._ffz_tabs ) {
|
2015-06-05 03:59:28 -04:00
|
|
|
var el = this._chatv._ffz_tabs.querySelector('.ffz-chat-tab[data-room="' + room_id + '"]');
|
|
|
|
if ( el && ! el.classList.contains('active') )
|
|
|
|
el.classList.add('tab-mentioned');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display notifications if that setting is enabled. Also make sure
|
|
|
|
// that we have a chat view because showing a notification when we
|
|
|
|
// can't actually go to it is a bad thing.
|
2015-07-04 17:06:36 -04:00
|
|
|
if ( this._chatv && this.settings.highlight_notifications && ! this.embed_in_dash && ! document.hasFocus() && ! prevent_notification ) {
|
2015-06-05 03:59:28 -04:00
|
|
|
var room = this.rooms[room_id] && this.rooms[room_id].room,
|
|
|
|
room_name;
|
|
|
|
|
2015-07-13 21:52:44 -04:00
|
|
|
// Make sure we have UI for this channel.
|
|
|
|
if ( (this.settings.group_tabs && (this.settings.pinned_rooms.indexOf(room_id) !== -1 || this._chatv._ffz_host )) || room.get('isGroupRoom') || room === this._chatv.get('controller.currentChannelRoom') ) {
|
|
|
|
if ( room && room.get('isGroupRoom') )
|
|
|
|
room_name = room.get('tmiRoom.displayName');
|
|
|
|
else
|
|
|
|
room_name = FFZ.get_capitalization(room_id);
|
|
|
|
|
|
|
|
display = display || Twitch.display.capitalize(msgObject.from);
|
|
|
|
|
|
|
|
if ( msgObject.style === 'action' )
|
|
|
|
msg = '* ' + display + ' ' + msg;
|
|
|
|
else
|
|
|
|
msg = display + ': ' + msg;
|
|
|
|
|
|
|
|
var f = this;
|
|
|
|
if ( msgObject.style === 'whisper' )
|
|
|
|
this.show_notification(
|
|
|
|
msg,
|
|
|
|
"Twitch Chat Whisper",
|
|
|
|
"ffz_whisper_notice",
|
2015-07-18 21:10:27 -04:00
|
|
|
(this.settings.notification_timeout*1000),
|
2015-07-13 21:52:44 -04:00
|
|
|
function() {
|
|
|
|
window.focus();
|
|
|
|
}
|
2015-07-04 17:06:36 -04:00
|
|
|
);
|
2015-07-13 21:52:44 -04:00
|
|
|
else
|
|
|
|
this.show_notification(
|
|
|
|
msg,
|
|
|
|
"Twitch Chat Mention in " + room_name,
|
|
|
|
room_id,
|
2015-07-18 21:10:27 -04:00
|
|
|
(this.settings.notification_timeout*1000),
|
2015-07-13 21:52:44 -04:00
|
|
|
function() {
|
|
|
|
window.focus();
|
|
|
|
var cont = App.__container__.lookup('controller:chat');
|
|
|
|
room && cont && cont.focusRoom(room);
|
|
|
|
}
|
2015-07-04 17:06:36 -04:00
|
|
|
);
|
2015-07-13 21:52:44 -04:00
|
|
|
}
|
2015-06-05 03:59:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
msgObject.cachedTokens = tokens;
|
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-06 00:09:21 -04:00
|
|
|
FFZ.prototype.tokenize_line = function(user, room, message, no_emotes, no_emoji) {
|
2015-06-05 03:59:28 -04:00
|
|
|
if ( typeof message === "string" )
|
|
|
|
message = [message];
|
|
|
|
|
|
|
|
if ( helpers && helpers.linkifyMessage )
|
|
|
|
message = helpers.linkifyMessage(message);
|
|
|
|
|
|
|
|
if ( helpers && helpers.mentionizeMessage ) {
|
|
|
|
var u = this.get_user();
|
|
|
|
if ( u && u.login )
|
|
|
|
message = helpers.mentionizeMessage(message, u.login, user === u.login);
|
|
|
|
}
|
|
|
|
|
2015-07-06 00:09:21 -04:00
|
|
|
if ( ! no_emotes ) {
|
2015-06-05 03:59:28 -04:00
|
|
|
message = this.tokenize_emotes(user, room, message);
|
2015-07-06 00:09:21 -04:00
|
|
|
if ( this.settings.replace_bad_emotes )
|
|
|
|
message = this.tokenize_replace_emotes(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( this.settings.parse_emoji && ! no_emoji )
|
|
|
|
message = this.tokenize_emoji(message);
|
2015-06-05 03:59:28 -04:00
|
|
|
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FFZ.prototype.render_tokens = function(tokens, render_links) {
|
2015-07-04 17:06:36 -04:00
|
|
|
var f = this;
|
2015-06-05 03:59:28 -04:00
|
|
|
return _.map(tokens, function(token) {
|
2015-07-04 17:06:36 -04:00
|
|
|
if ( token.emoticonSrc ) {
|
2015-07-29 01:03:10 -04:00
|
|
|
var tooltip, srcset, extra;
|
2015-07-04 17:06:36 -04:00
|
|
|
if ( token.ffzEmote ) {
|
|
|
|
var emote_set = f.emote_sets && f.emote_sets[token.ffzEmoteSet],
|
|
|
|
emote = emote_set && emote_set.emoticons && emote_set.emoticons[token.ffzEmote];
|
|
|
|
|
|
|
|
tooltip = emote ? utils.sanitize(f._emote_tooltip(emote)) : token.altText;
|
2015-07-29 01:03:10 -04:00
|
|
|
srcset = emote ? emote.srcSet : token.srcSet;
|
|
|
|
extra = ' data-ffz-emote="' + emote.id + '"';
|
2015-07-04 17:06:36 -04:00
|
|
|
|
|
|
|
} else if ( token.ffzEmoji ) {
|
|
|
|
var eid = token.ffzEmoji,
|
|
|
|
emoji = f.emoji_data && f.emoji_data[eid];
|
|
|
|
|
2015-07-06 00:09:21 -04:00
|
|
|
tooltip = emoji ? "Emoji: " + token.altText + "\nName: :" + emoji.short_name + ":" : token.altText;
|
2015-07-29 01:03:10 -04:00
|
|
|
srcset = emoji ? emoji.srcSet : token.srcSet;
|
|
|
|
extra = ' data-ffz-emoji="' + eid + '"';
|
2015-07-04 17:06:36 -04:00
|
|
|
|
|
|
|
} else {
|
2015-07-29 01:03:10 -04:00
|
|
|
var id = token.replacedId || FFZ.src_to_id(token.emoticonSrc),
|
2015-07-04 17:06:36 -04:00
|
|
|
data = id && f._twitch_emotes && f._twitch_emotes[id];
|
|
|
|
|
2015-07-29 01:03:10 -04:00
|
|
|
if ( data )
|
|
|
|
tooltip = data.tooltip ? data.tooltip : token.altText;
|
|
|
|
else {
|
|
|
|
tooltip = f._twitch_emotes[id] = token.altText;
|
|
|
|
f.ws_send("twitch_emote", id, load_emote_data.bind(f, id, token.altText));
|
|
|
|
}
|
|
|
|
|
|
|
|
extra = ' data-emote="' + id + '"';
|
|
|
|
|
|
|
|
if ( ! constants.EMOTE_REPLACEMENTS[id] )
|
|
|
|
srcset = build_srcset(id);
|
2015-07-04 17:06:36 -04:00
|
|
|
}
|
|
|
|
|
2015-07-29 01:03:10 -04:00
|
|
|
return '<img class="emoticon tooltip"' + (extra||"") + ' src="' + utils.quote_attr(token.emoticonSrc) + '" ' + (srcset ? 'srcset="' + utils.quote_attr(srcset) + '" ' : '') + 'alt="' + utils.quote_attr(token.altText) + '" title="' + utils.quote_attr(tooltip) + '">';
|
2015-07-04 17:06:36 -04:00
|
|
|
}
|
2015-06-05 03:59:28 -04:00
|
|
|
|
|
|
|
if ( token.isLink ) {
|
2015-07-29 01:03:10 -04:00
|
|
|
var text = token.title || (token.isLong && '<long link>') || (token.isDeleted && '<deleted link>') || token.href;
|
|
|
|
|
2015-06-05 03:59:28 -04:00
|
|
|
if ( ! render_links && render_links !== undefined )
|
2015-07-29 01:03:10 -04:00
|
|
|
return utils.sanitize(text);
|
2015-06-05 03:59:28 -04:00
|
|
|
|
2015-07-29 01:03:10 -04:00
|
|
|
var href = token.href,
|
|
|
|
tooltip, cls = '',
|
|
|
|
|
|
|
|
ind_at = href.indexOf("@"),
|
|
|
|
ind_sl = href.indexOf("/");
|
|
|
|
|
|
|
|
if ( ind_at !== -1 && (ind_sl === -1 || ind_at < ind_sl) ) {
|
|
|
|
// E-Mail Link
|
|
|
|
cls = 'email-link';
|
|
|
|
|
|
|
|
if ( f.settings.link_info ) {
|
|
|
|
cls += ' tooltip';
|
|
|
|
tooltip = 'E-Mail ' + href;
|
|
|
|
}
|
|
|
|
|
|
|
|
href = 'mailto:' + href;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Web Link
|
|
|
|
if ( ! href.match(/^https?:\/\//) )
|
|
|
|
href = 'http://' + href;
|
|
|
|
|
|
|
|
if ( f.settings.link_info ) {
|
|
|
|
cls = 'html-tooltip';
|
|
|
|
|
|
|
|
var data = f._link_data && f._link_data[href];
|
|
|
|
if ( data ) {
|
|
|
|
tooltip = data.tooltip;
|
|
|
|
if ( data.unsafe )
|
|
|
|
cls += ' unsafe-link';
|
|
|
|
|
|
|
|
} else {
|
|
|
|
f._link_data = f._link_data || {};
|
|
|
|
f._link_data[href] = true;
|
|
|
|
f.ws_send("get_link", href, load_link_data.bind(f, href));
|
|
|
|
if ( f.settings.link_image_hover && is_image(href, f.settings.image_hover_all_domains) )
|
|
|
|
tooltip = image_iframe(href);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if ( f.settings.link_image_hover ) {
|
|
|
|
cls = 'html-tooltip';
|
|
|
|
if ( is_image(href, f.settings.image_hover_all_domains) )
|
|
|
|
tooltip = image_iframe(href);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-04 17:06:36 -04:00
|
|
|
|
2015-07-29 01:03:10 -04:00
|
|
|
// Deleted Links
|
|
|
|
var actual_href = href;
|
|
|
|
if ( token.isDeleted ) {
|
|
|
|
cls = 'deleted-link ' + cls;
|
|
|
|
tooltip = utils.sanitize(token.censoredHref || token.href);
|
|
|
|
href = '#';
|
|
|
|
}
|
|
|
|
|
|
|
|
return '<a class="' + cls + '" data-url="' + utils.quote_attr(actual_href) + '" href="' + utils.quote_attr(href || '#') + '" title="' + utils.quote_attr(tooltip || '') + '" target="_blank">' + utils.sanitize(text) + '</a>';
|
2015-06-05 03:59:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( token.mentionedUser )
|
2015-07-29 01:03:10 -04:00
|
|
|
return '<span class="' + (token.own ? "mentioning" : "mentioned") + '">' + utils.sanitize(token.mentionedUser) + "</span>";
|
2015-06-05 03:59:28 -04:00
|
|
|
|
|
|
|
if ( token.deletedLink )
|
|
|
|
return utils.sanitize(token.text);
|
|
|
|
|
|
|
|
return utils.sanitize(token);
|
|
|
|
}).join("");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------
|
|
|
|
// Emoticon Processing
|
|
|
|
// ---------------------
|
|
|
|
|
2015-07-06 00:09:21 -04:00
|
|
|
FFZ.prototype.tokenize_replace_emotes = function(tokens) {
|
|
|
|
// Replace bad Twitch emoticons with custom emoticons.
|
|
|
|
var f = this;
|
|
|
|
|
|
|
|
if ( _.isString(tokens) )
|
|
|
|
tokens = [tokens];
|
|
|
|
|
|
|
|
for(var i=0; i < tokens.length; i++) {
|
|
|
|
var token = tokens[i];
|
|
|
|
if ( ! token || ! token.emoticonSrc || token.ffzEmote )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Check for a few specific emoticon IDs.
|
|
|
|
var emote_id = FFZ.src_to_id(token.emoticonSrc);
|
|
|
|
if ( constants.EMOTE_REPLACEMENTS.hasOwnProperty(emote_id) ) {
|
2015-07-29 01:03:10 -04:00
|
|
|
token.replacedId = emote_id;
|
|
|
|
token.emoticonSrc = constants.EMOTE_REPLACEMENT_BASE + constants.EMOTE_REPLACEMENTS[emote_id];
|
2015-07-06 00:09:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-05 03:59:28 -04:00
|
|
|
FFZ.prototype.tokenize_title_emotes = function(tokens) {
|
|
|
|
var f = this,
|
|
|
|
Channel = App.__container__.lookup('controller:channel'),
|
|
|
|
possible = Channel && Channel.get('product.emoticons'),
|
|
|
|
emotes = [];
|
|
|
|
|
|
|
|
if ( _.isString(tokens) )
|
|
|
|
tokens = [tokens];
|
|
|
|
|
|
|
|
// Build a list of emotes that match.
|
|
|
|
_.each(_.union(f.__twitch_global_emotes||[], possible), function(emote) {
|
|
|
|
if ( ! emote || emote.state === "inactive" )
|
|
|
|
return;
|
|
|
|
|
|
|
|
var r = new RegExp("\\b" + emote.regex + "\\b");
|
|
|
|
|
|
|
|
_.any(tokens, function(token) {
|
|
|
|
return _.isString(token) && token.match(r);
|
|
|
|
}) && emotes.push(emote);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Include Global Emotes~!
|
|
|
|
if ( f.__twitch_global_emotes === undefined || f.__twitch_global_emotes === null ) {
|
|
|
|
f.__twitch_global_emotes = false;
|
|
|
|
Twitch.api.get("chat/emoticon_images", {emotesets:"0,42"}).done(function(data) {
|
|
|
|
if ( ! data || ! data.emoticon_sets || ! data.emoticon_sets[0] ) {
|
|
|
|
f.__twitch_global_emotes = [];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var emotes = f.__twitch_global_emotes = [];
|
|
|
|
data = data.emoticon_sets[0];
|
|
|
|
for(var i=0; i < data.length; i++) {
|
|
|
|
var em = data[i];
|
|
|
|
emotes.push({regex: em.code, url: TWITCH_BASE + em.id + "/1.0"});
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( f._cindex )
|
|
|
|
f._cindex.ffzFixTitle();
|
|
|
|
}).fail(function() {
|
|
|
|
setTimeout(function(){f.__twitch_global_emotes = null;},5000);
|
|
|
|
});;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! emotes.length )
|
|
|
|
return tokens;
|
|
|
|
|
|
|
|
if ( typeof tokens === "string" )
|
|
|
|
tokens = [tokens];
|
|
|
|
|
|
|
|
_.each(emotes, function(emote) {
|
|
|
|
var eo = {isEmoticon:true, srcSet: emote.url + ' 1x', emoticonSrc: emote.url, altText: emote.regex};
|
|
|
|
var r = new RegExp("\\b" + emote.regex + "\\b");
|
|
|
|
|
|
|
|
tokens = _.compact(_.flatten(_.map(tokens, function(token) {
|
|
|
|
if ( _.isObject(token) )
|
|
|
|
return token;
|
|
|
|
|
|
|
|
var tbits = token.split(r), bits = [];
|
|
|
|
tbits.forEach(function(val, ind) {
|
|
|
|
bits.push(val);
|
|
|
|
if ( ind !== tbits.length - 1 )
|
|
|
|
bits.push(eo);
|
|
|
|
});
|
|
|
|
return bits;
|
|
|
|
})));
|
|
|
|
});
|
|
|
|
|
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FFZ.prototype.tokenize_emotes = function(user, room, tokens, do_report) {
|
|
|
|
var f = this;
|
|
|
|
|
|
|
|
// Get our sets.
|
|
|
|
var sets = this.getEmotes(user, room),
|
|
|
|
emotes = [];
|
|
|
|
|
|
|
|
// Build a list of emotes that match.
|
|
|
|
_.each(sets, function(set_id) {
|
|
|
|
var set = f.emote_sets[set_id];
|
|
|
|
if ( ! set )
|
|
|
|
return;
|
|
|
|
|
|
|
|
_.each(set.emoticons, function(emote) {
|
|
|
|
_.any(tokens, function(token) {
|
|
|
|
return _.isString(token) && token.match(emote.regex);
|
|
|
|
}) && emotes.push(emote);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// Don't bother proceeding if we have no emotes.
|
|
|
|
if ( ! emotes.length )
|
|
|
|
return tokens;
|
|
|
|
|
|
|
|
// Now that we have all the matching tokens, do crazy stuff.
|
2015-07-04 17:06:36 -04:00
|
|
|
if ( typeof tokens === "string" )
|
2015-06-05 03:59:28 -04:00
|
|
|
tokens = [tokens];
|
|
|
|
|
|
|
|
// This is weird stuff I basically copied from the old Twitch code.
|
|
|
|
// Here, for each emote, we split apart every text token and we
|
|
|
|
// put it back together with the matching bits of text replaced
|
|
|
|
// with an object telling Twitch's line template how to render the
|
|
|
|
// emoticon.
|
|
|
|
_.each(emotes, function(emote) {
|
|
|
|
var eo = {
|
|
|
|
srcSet: emote.srcSet,
|
|
|
|
emoticonSrc: emote.urls[1] + '" data-ffz-emote="' + encodeURIComponent(JSON.stringify([emote.id, emote.set_id])),
|
2015-07-04 17:06:36 -04:00
|
|
|
ffzEmote: emote.id,
|
|
|
|
ffzEmoteSet: emote.set_id,
|
2015-06-05 03:59:28 -04:00
|
|
|
altText: (emote.hidden ? "???" : emote.name)
|
|
|
|
};
|
|
|
|
|
|
|
|
tokens = _.compact(_.flatten(_.map(tokens, function(token) {
|
|
|
|
if ( _.isObject(token) )
|
|
|
|
return token;
|
|
|
|
|
|
|
|
var tbits = token.split(emote.regex), bits = [];
|
|
|
|
while(tbits.length) {
|
|
|
|
var bit = tbits.shift();
|
|
|
|
if ( tbits.length ) {
|
|
|
|
bit += tbits.shift();
|
|
|
|
if ( bit )
|
|
|
|
bits.push(bit);
|
|
|
|
|
|
|
|
tbits.shift();
|
|
|
|
bits.push(eo);
|
|
|
|
|
|
|
|
if ( do_report && room )
|
|
|
|
f.add_usage(room, emote.id);
|
|
|
|
|
|
|
|
} else
|
|
|
|
bits.push(bit);
|
|
|
|
}
|
|
|
|
return bits;
|
|
|
|
})));
|
|
|
|
});
|
|
|
|
|
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-04 17:06:36 -04:00
|
|
|
// ---------------------
|
|
|
|
// Emoji Processing
|
|
|
|
// ---------------------
|
|
|
|
|
|
|
|
FFZ.prototype.tokenize_emoji = function(tokens) {
|
|
|
|
if ( typeof tokens === "string" )
|
|
|
|
tokens = [tokens];
|
|
|
|
|
|
|
|
if ( ! this.emoji_data )
|
|
|
|
return tokens;
|
|
|
|
|
|
|
|
var f = this;
|
|
|
|
|
|
|
|
return _.compact(_.flatten(_.map(tokens, function(token) {
|
|
|
|
if ( _.isObject(token) )
|
|
|
|
return token;
|
|
|
|
|
|
|
|
var tbits = token.split(constants.EMOJI_REGEX), bits = [];
|
|
|
|
while(tbits.length) {
|
|
|
|
// Deal with the unmatched string first.
|
|
|
|
var bit = tbits.shift();
|
|
|
|
bit && bits.push(bit);
|
|
|
|
|
|
|
|
if ( tbits.length ) {
|
|
|
|
// We have an emoji too, so let's handle that.
|
|
|
|
var match = tbits.shift(),
|
|
|
|
variant = tbits.shift();
|
|
|
|
|
|
|
|
if ( variant === '\uFE0E' ) {
|
|
|
|
// Text Variant
|
|
|
|
bits.push(match);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Find the right image~!
|
|
|
|
var eid = utils.emoji_to_codepoint(match, variant),
|
2015-07-06 00:09:21 -04:00
|
|
|
data = f.emoji_data[eid];
|
2015-07-04 17:06:36 -04:00
|
|
|
|
2015-07-06 00:09:21 -04:00
|
|
|
if ( data )
|
2015-07-04 17:06:36 -04:00
|
|
|
bits.push(data.token);
|
2015-07-06 00:09:21 -04:00
|
|
|
else
|
|
|
|
bits.push(match + (variant || ""));
|
2015-07-04 17:06:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bits;
|
|
|
|
})));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-05 03:59:28 -04:00
|
|
|
// ---------------------
|
|
|
|
// Mention Parsing
|
|
|
|
// ---------------------
|
|
|
|
|
|
|
|
FFZ._regex_cache = {};
|
|
|
|
|
|
|
|
FFZ._get_regex = function(word) {
|
|
|
|
return FFZ._regex_cache[word] = FFZ._regex_cache[word] || RegExp("\\b" + reg_escape(word) + "\\b", "ig");
|
|
|
|
}
|
|
|
|
|
|
|
|
FFZ._words_to_regex = function(list) {
|
|
|
|
var regex = FFZ._regex_cache[list];
|
|
|
|
if ( ! regex ) {
|
|
|
|
var reg = "";
|
|
|
|
for(var i=0; i < list.length; i++) {
|
|
|
|
if ( ! list[i] )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
reg += (reg ? "|" : "") + reg_escape(list[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
regex = FFZ._regex_cache[list] = new RegExp("(^|.*?" + SEPARATORS + ")(" + reg + ")(?=$|" + SEPARATORS + ")", "ig");
|
|
|
|
}
|
|
|
|
|
|
|
|
return regex;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FFZ.prototype.tokenize_mentions = function(tokens) {
|
|
|
|
var mention_words = this.settings.keywords;
|
|
|
|
if ( ! mention_words || ! mention_words.length )
|
|
|
|
return tokens;
|
|
|
|
|
|
|
|
if ( typeof tokens === "string" )
|
|
|
|
tokens = [tokens];
|
|
|
|
|
|
|
|
var regex = FFZ._words_to_regex(mention_words),
|
|
|
|
new_tokens = [];
|
|
|
|
|
|
|
|
for(var i=0; i < tokens.length; i++) {
|
|
|
|
var token = tokens[i];
|
|
|
|
if ( ! _.isString(token) ) {
|
|
|
|
new_tokens.push(token);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! token.match(regex) ) {
|
|
|
|
new_tokens.push(token);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
token = token.replace(regex, function(all, prefix, match) {
|
|
|
|
new_tokens.push(prefix);
|
|
|
|
new_tokens.push({
|
|
|
|
mentionedUser: match,
|
|
|
|
own: false
|
|
|
|
});
|
|
|
|
|
|
|
|
return "";
|
|
|
|
});
|
|
|
|
|
|
|
|
if ( token )
|
|
|
|
new_tokens.push(token);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_tokens;
|
2015-07-04 17:06:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------
|
|
|
|
// Handling Bad Stuff
|
|
|
|
// ---------------------
|
|
|
|
|
|
|
|
FFZ.prototype._deleted_link_click = function(e) {
|
|
|
|
if ( ! this.classList.contains("deleted-link") )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Get the URL
|
|
|
|
var href = this.getAttribute('data-url'),
|
|
|
|
link = href,
|
|
|
|
f = FrankerFaceZ.get();
|
|
|
|
|
|
|
|
// Delete Old Stuff
|
|
|
|
this.classList.remove('deleted-link');
|
|
|
|
this.removeAttribute("data-url");
|
|
|
|
this.removeAttribute("title");
|
|
|
|
this.removeAttribute("original-title");
|
|
|
|
|
|
|
|
// Process URL
|
|
|
|
if ( href.indexOf("@") > -1 && (-1 === href.indexOf("/") || href.indexOf("@") < href.indexOf("/")) )
|
|
|
|
href = "mailto:" + href;
|
|
|
|
else if ( ! href.match(/^https?:\/\//) )
|
|
|
|
href = "http://" + href;
|
|
|
|
|
|
|
|
// Set up the Link
|
|
|
|
this.href = href;
|
|
|
|
this.target = "_new";
|
|
|
|
this.textContent = link;
|
|
|
|
|
|
|
|
// Now, check for a tooltip.
|
|
|
|
var link_data = f._link_data[link];
|
|
|
|
if ( link_data && typeof link_data != "boolean" ) {
|
|
|
|
this.title = link_data.tooltip;
|
|
|
|
if ( link_data.unsafe )
|
|
|
|
this.classList.add('unsafe-link');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop from Navigating
|
|
|
|
e.preventDefault();
|
2015-06-05 03:59:28 -04:00
|
|
|
}
|