// ==UserScript== // @name X-links Extension - Multimedia // @namespace dnsev-h // @author dnsev-h // @version 1.0.0.8 // @description Linkify and format various multimedia links // @include http://boards.4chan.org/* // @include https://boards.4chan.org/* // @include http://boards.4channel.org/* // @include https://boards.4channel.org/* // @include http://8ch.net/* // @include https://8ch.net/* // @include https://archived.moe/* // @include https://boards.fireden.net/* // @include http://desuarchive.org/* // @include https://desuarchive.org/* // @include http://fgts.jp/* // @include https://fgts.jp/* // @include http://boards.38chan.net/* // @include http://forums.e-hentai.org/* // @include https://forums.e-hentai.org/* // @include https://meguca.org/* // @homepage https://dnsev-h.github.io/x-links/ // @supportURL https://github.com/dnsev-h/x-links/issues // @updateURL https://raw.githubusercontent.com/dnsev-h/x-links/stable/builds/x-links-ext-multimedia.meta.js // @downloadURL https://raw.githubusercontent.com/dnsev-h/x-links/stable/builds/x-links-ext-multimedia.user.js // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAA4klEQVR4Ae2ZoQ7CMBRF+VIMBjGDwSAwmImZGcQUYoYPq32fAPK8LCSleZCmzb3JcUtzD+ndBDslHuVVQr0zJdCAQHoaQEggTQYj9C8ggRVCAqPBDfoUkMBq8HAs4J8vLZ2uEH/VSqC6QEZmMbg7ZgiWzu2wJQEJZGRmgwn+cNf9jxXcRn0BCZA/33VKb848OfbQioAEikqni+MMpRugdGADFQQkEL7rlN7c3QG+2EZgrPUEJPD7V+RgcHQcoGAXDQlIoLx0/kxKhwbahoAEPn5ZYwKU7ldAAvqLSQLNRlEU5Q1O5fOjZV4u4AAAAABJRU5ErkJggg== // @icon64 data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAOVBMVEUBAAAAAADmce/ml+/mje/mku/mhO/mY+/mbO/mdu/me+/mie/mXu/mm+/mpe/mf+/mqu/maO/moe9+hYmYAAAAAXRSTlMAQObYZgAAAJRJREFUeF7t1zkOAzEMBEFRe9+2//9YtzOCIOR8oEoX7GCgZEtXigWtb8qBF36ywIgD8gHcyAIHZqgHbnxwwRCPH1igEvCRCwMmZMd+cKVAjEwY0RpvgDkKAe/feANmVJxQC8TjHRssqDBHI5CPt6FihR8zjicQaD6eFW8sMEcxEI99fEG2vFrgwY4scEI/0P8X0HVf06IrwbJZHiwAAAAASUVORK5CYII= // @grant none // @run-at document-start // ==/UserScript== (function () { "use strict"; /*#{begin_debug:timing=true}#*/ var xlinks_api = (function () { "use strict"; // Private var ready = (function () { var callbacks = [], check_interval = null, check_interval_time = 250; var callback_check = function () { if ( (document.readyState === "interactive" || document.readyState === "complete") && callbacks !== null ) { var cbs = callbacks, cb_count = cbs.length, i; callbacks = null; for (i = 0; i < cb_count; ++i) { cbs[i].call(null); } window.removeEventListener("load", callback_check, false); window.removeEventListener("DOMContentLoaded", callback_check, false); document.removeEventListener("readystatechange", callback_check, false); if (check_interval !== null) { clearInterval(check_interval); check_interval = null; } return true; } return false; }; window.addEventListener("load", callback_check, false); window.addEventListener("DOMContentLoaded", callback_check, false); document.addEventListener("readystatechange", callback_check, false); return function (cb) { if (callbacks === null) { cb.call(null); } else { callbacks.push(cb); if (check_interval === null && callback_check() !== true) { check_interval = setInterval(callback_check, check_interval_time); } } }; })(); var ttl_1_hour = 60 * 60 * 1000; var ttl_1_day = 24 * ttl_1_hour; var ttl_1_year = 365 * ttl_1_day; var cache_prefix = ""; var cache_storage = window.localStorage; var cache_set = function (key, data, ttl) { cache_storage.setItem(cache_prefix + "ext-" + key, JSON.stringify({ expires: Date.now() + ttl, data: data })); }; var cache_get = function (key) { var json = parse_json(cache_storage.getItem(cache_prefix + "ext-" + key), null); if ( json !== null && typeof(json) === "object" && Date.now() < json.expires && typeof(json.data) === "object" ) { return json.data; } cache_storage.removeItem(key); return null; }; var random_string_alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; var random_string = function (count) { var alpha_len = random_string_alphabet.length, s = "", i; for (i = 0; i < count; ++i) { s += random_string_alphabet[Math.floor(Math.random() * alpha_len)]; } return s; }; var is_object = function (obj) { return (obj !== null && typeof(obj) === "object"); }; var get_regex_flags = function (regex) { var s = ""; if (regex.global) s += "g"; if (regex.ignoreCase) s += "i"; if (regex.multiline) s += "m"; return s; }; var create_temp_storage = function () { var data = {}; var fn = { length: 0, key: function (index) { return Object.keys(data)[index]; }, getItem: function (key) { if (Object.prototype.hasOwnProperty.call(data, key)) { return data[key]; } return null; }, setItem: function (key, value) { if (!Object.prototype.hasOwnProperty.call(data, key)) { ++fn.length; } data[key] = value; }, removeItem: function (key) { if (Object.prototype.hasOwnProperty.call(data, key)) { delete data[key]; --fn.length; } }, clear: function () { data = {}; fn.length = 0; } }; return fn; }; var set_shared_node = function (node) { var par = document.querySelector(".xl-extension-sharing-elements"), id = random_string(32); if (par === null) { par = document.createElement("div"); par.className = "xl-extension-sharing-elements"; par.style.setProperty("display", "none", "important"); document.body.appendChild(par); } try { node.setAttribute("data-xl-sharing-id", id); par.appendChild(node); } catch (e) { return null; } return id; }; var settings_descriptor_info_normalize = function (input) { var info = {}, opt, label, desc, a, i, ii, v; if (typeof(input.type) === "string") { info.type = input.type; } if (Array.isArray((a = input.options))) { info.options = []; for (i = 0, ii = a.length; i < ii; ++i) { v = a[i]; if ( Array.isArray(v) && v.length >= 2 && typeof((label = v[1])) === "string" ) { opt = [ v[0], v[1] ]; if (typeof((desc = v[2])) === "string") { opt.push(desc); } info.options.push(opt); } } } return info; }; var config = {}; var CommunicationChannel = function (name, key, is_extension, channel, callback) { var self = this; this.port = null; this.port_other = null; this.post = null; this.on_message = null; this.origin = null; this.name_key = null; this.is_extension = is_extension; this.name = name; this.key = key; this.callback = callback; if (channel === null) { this.name_key = name; if (key !== null) { this.name_key += "_"; this.name_key += key; } this.origin = window.location.protocol + "//" + window.location.host; this.post = this.post_window; this.on_message = function (event) { self.on_window_message(event); }; window.addEventListener("message", this.on_message, false); } else { this.port = channel.port1; this.port_other = channel.port2; this.post = this.post_channel; this.on_message = function (event) { self.on_port_message(event); }; this.port.addEventListener("message", this.on_message, false); this.port.start(); } }; CommunicationChannel.prototype.post_window = function (message, transfer) { var msg = { ext: this.is_extension, key: this.name_key, data: message }; try { window.postMessage(msg, this.origin, transfer); } catch (e) { // Tampermonkey bug try { unsafeWindow.postMessage(msg, this.origin, transfer); } catch (e2) {} } }; CommunicationChannel.prototype.post_channel = function (message, transfer) { this.port.postMessage(message, transfer); }; CommunicationChannel.prototype.post_null = function () { }; CommunicationChannel.prototype.on_window_message = function (event) { var data = event.data; if ( event.origin === this.origin && is_object(data) && data.ext === (!this.is_extension) && // jshint ignore:line data.key === this.name_key && is_object((data = data.data)) ) { this.callback(event, data, this); } }; CommunicationChannel.prototype.on_port_message = function (event) { var data = event.data; if (is_object(data)) { this.callback(event, data, this); } }; CommunicationChannel.prototype.close = function () { if (this.on_message !== null) { if (this.port === null) { window.removeEventListener("message", this.on_message, false); } else { this.port.removeEventListener("message", this.on_message, false); this.port.close(); this.port = null; } this.on_message = null; this.post = this.post_null; } }; var api = null; var API = function () { this.event = null; this.reply_callbacks = {}; this.init_state = 0; this.handlers = API.handlers_init; this.functions = {}; this.url_info_functions = {}; this.url_info_to_data_functions = {}; this.details_functions = {}; this.actions_functions = {}; var self = this; this.channel = new CommunicationChannel( "xlinks_broadcast", null, true, null, function (event, data, channel) { self.on_message(event, data, channel, {}); } ); }; API.prototype.on_message = function (event, data, channel, handlers) { var action = data.xlinks_action, action_is_null = (action === null), action_data, reply, fn, err; if ( (action_is_null || typeof(action) === "string") && is_object((action_data = data.data)) ) { reply = data.reply; if (typeof(reply) === "string") { if (Object.prototype.hasOwnProperty.call(this.reply_callbacks, reply)) { fn = this.reply_callbacks[reply]; delete this.reply_callbacks[reply]; this.event = event; fn.call(this, null, action_data); this.event = null; } else { err = "Cannot reply to extension"; } } else if (action_is_null) { err = "Missing extension action"; } else if (Object.prototype.hasOwnProperty.call(handlers, action)) { handlers[action].call(this, action_data, channel, data.id); } else { err = "Invalid extension call"; } if (err !== undefined && typeof((reply = data.id)) === "string") { this.send( channel, null, reply, { err: "Invalid extension call" } ); } } }; API.prototype.send = function (channel, action, reply_to, data, timeout_delay, on_reply) { var self = this, id = null, timeout = null, cb, i; if (on_reply !== undefined) { for (i = 0; i < 10; ++i) { id = random_string(32); if (!Object.prototype.hasOwnProperty.call(this.reply_callbacks)) break; } cb = function () { if (timeout !== null) { clearTimeout(timeout); timeout = null; } on_reply.apply(this, arguments); }; this.reply_callbacks[id] = cb; cb = null; if (timeout_delay >= 0) { timeout = setTimeout(function () { timeout = null; delete self.reply_callbacks[id]; on_reply.call(self, "Response timeout"); }, timeout_delay); } } channel.post({ xlinks_action: action, data: data, id: id, reply: reply_to }); }; API.prototype.reply_error = function (channel, reply_to, err) { channel.post({ xlinks_action: null, data: { err: err }, id: null, reply: reply_to }); }; API.prototype.post_message = function (msg) { try { window.postMessage(msg, this.origin); } catch (e) { // Tampermonkey bug try { unsafeWindow.postMessage(msg, this.origin); } catch (e2) { console.log("window.postMessage failed! Your userscript manager may need to be updated!"); console.log("window.postMessage exception:", e, e2); } } }; API.prototype.init = function (info, callback) { if (this.init_state !== 0) { if (typeof(callback) === "function") callback.call(null, this.init_state === 1 ? "Init active" : "Already started"); return; } this.init_state = 1; var self = this, de = document.documentElement, count = info.registrations, namespace = info.namespace || "", send_info = { namespace: namespace }, a, v, i; if (typeof((v = info.name)) === "string") send_info.name = v; if (typeof((v = info.author)) === "string") send_info.author = v; if (typeof((v = info.description)) === "string") send_info.description = v; if (Array.isArray((v = info.version))) { for (i = 0; i < v.length; ++i) { if (typeof(v[i]) !== "number") break; } if (i === v.length) send_info.version = v.slice(0); } if (typeof(count) !== "number" || count < 0) { count = 1; } send_info.registrations = count; send_info.main = (typeof(info.main) === "function") ? info.main.toString() : null; if (de) { a = de.getAttribute("data-xlinks-extensions-waiting"); a = (a ? (parseInt(a, 10) || 0) : 0) + count; de.setAttribute("data-xlinks-extensions-waiting", a); de = null; } ready(function () { self.send( self.channel, "init", null, send_info, 10000, function (err, data) { err = self.on_init(err, data, namespace); if (err === "Internal") { self.channel.close(); this.init_state = 3; } if (typeof(callback) === "function") callback.call(null, err); } ); }); }; API.prototype.on_init = function (err, data, namespace) { var self = this, api_key, ch, v; if (err === null) { if (!is_object(data)) { err = "Could not generate extension key"; } else if (typeof((err = data.err)) !== "string") { if (typeof((api_key = data.key)) !== "string") { err = "Could not generate extension key"; } else { // Valid err = null; if (typeof((v = data.cache_prefix)) === "string") { cache_prefix = v; } if (typeof((v = data.cache_mode)) === "string") { if (v === "session") { cache_storage = window.sessionStorage; } else if (v === "none") { cache_storage = create_temp_storage(); } } // New channel ch = (this.event.ports && this.event.ports.length === 1) ? { port1: this.event.ports[0], port2: null } : null; this.channel.close(); this.channel = new CommunicationChannel( namespace, api_key, true, ch, function (event, data, channel) { self.on_message(event, data, channel, API.handlers); } ); } } } this.init_state = (err === null) ? 2 : 0; return err; }; API.prototype.register = function (data, callback) { if (this.init_state !== 2) { if (typeof(callback) === "function") callback.call(null, "API not init'd", 0); return; } // Data var send_data = { settings: {}, request_apis: [], linkifiers: [], commands: [], create_url: null }; var request_apis_response = [], command_fns = [], array, entry, fn_map, a_data, a, i, ii, k, o, v; // Settings o = data.settings; if (is_object(o)) { for (k in o) { a = o[k]; if (Array.isArray(a)) { send_data.settings[k] = a_data = []; for (i = 0, ii = a.length; i < ii; ++i) { v = a[i]; if (Array.isArray(v) && typeof(v[0]) === "string") { entry = [ v[0] ]; if (v.length > 1) { entry.push( (v[1] === undefined ? null : v[1]), "" + (v[2] || ""), "" + (v[3] || "") ); if (v.length > 4 && is_object(v[4])) { entry.push(settings_descriptor_info_normalize(v[4])); } } a_data.push(entry); } } } } } // Request APIs array = data.request_apis; if (Array.isArray(array)) { for (i = 0, ii = array.length; i < ii; ++i) { a = array[i]; fn_map = {}; a_data = { group: "other", namespace: "other", type: "other", count: 1, concurrent: 1, delays: { okay: 200, error: 5000 }, functions: [] }; if (typeof((v = a.group)) === "string") a_data.group = v; if (typeof((v = a.namespace)) === "string") a_data.namespace = v; if (typeof((v = a.type)) === "string") a_data.type = v; if (typeof((v = a.count)) === "number") a_data.count = Math.max(1, v); if (typeof((v = a.concurrent)) === "number") a_data.concurrent = Math.max(1, v); if (typeof((v = a.delay_okay)) === "number") a_data.delay_okay = Math.max(0, v); if (typeof((v = a.delay_error)) === "number") a_data.delay_error = Math.max(0, v); if (is_object((o = a.functions))) { for (k in o) { v = o[k]; if (typeof(v) === "function") { a_data.functions.push(k); fn_map[k] = v; } } } request_apis_response.push({ functions: fn_map }); send_data.request_apis.push(a_data); } } // Linkifiers array = data.linkifiers; if (Array.isArray(array)) { for (i = 0, ii = array.length; i < ii; ++i) { a = array[i]; a_data = { regex: null, prefix_group: 0, prefix: "" }; v = a.regex; if (typeof(v) === "string") { a_data.regex = [ v ]; } else if (v instanceof RegExp) { a_data.regex = [ v.source, get_regex_flags(v) ]; } else if (Array.isArray(v)) { if (typeof(v[0]) === "string") { if (typeof(v[1]) === "string") { a_data.regex = [ v[0], v[1] ]; } else { a_data.regex = [ v[0] ]; } } } if (typeof((v = a.prefix_group)) === "number") a_data.prefix_group = v; if (typeof((v = a.prefix)) === "string") a_data.prefix = v; send_data.linkifiers.push(a_data); } } // URL info functions array = data.commands; if (Array.isArray(array)) { for (i = 0, ii = array.length; i < ii; ++i) { a = array[i]; a_data = { url_info: false, to_data: false, actions: false, details: false }; o = { url_info: null, to_data: null, actions: null, details: null }; if (typeof((v = a.url_info)) === "function") { a_data.url_info = true; o.url_info = v; } if (typeof((v = a.to_data)) === "function") { a_data.to_data = true; o.to_data = v; } if (typeof((v = a.actions)) === "function") { a_data.actions = true; o.actions = v; } if (typeof((v = a.details)) === "function") { a_data.details = true; o.details = v; } command_fns.push(o); send_data.commands.push(a_data); } } // URL create functions o = data.create_url; if (is_object(o)) { send_data.create_url = o; } // Send this.send( this.channel, "register", null, send_data, 10000, function (err, data) { var o; if (err !== null || (err = data.err) !== null) { if (typeof(callback) === "function") callback.call(null, err, 0); } else if (!is_object((o = data.response))) { if (typeof(callback) === "function") callback.call(null, "Invalid extension response", 0); } else { var okay = this.register_complete(o, request_apis_response, command_fns, send_data.settings); if (typeof(callback) === "function") callback.call(null, null, okay); } } ); }; API.prototype.register_complete = function (data, request_apis, command_fns, settings) { var reg_count = 0, setting_ns, errors, name, fn, e, o, i, ii, k, v; // Request APIs errors = []; i = 0; if (Array.isArray((o = data.request_apis))) { for (ii = o.length; i < ii; ++i) { e = o[i]; if (i >= request_apis.length) { errors.push("Invalid"); } else if (typeof(e) === "string") { errors.push(e); } else if (!is_object(e)) { errors.push("Invalid"); } else { ++reg_count; for (k in e) { if (Object.prototype.hasOwnProperty.call(e, k) && Object.prototype.hasOwnProperty.call(request_apis[i].functions, k)) { fn = request_apis[i].functions[k]; this.functions[e[k]] = fn; } } } } } for (ii = request_apis.length; i < ii; ++i) { errors.push("Invalid"); } // URL infos errors = []; i = 0; if (Array.isArray((o = data.commands))) { for (ii = o.length; i < ii; ++i) { e = o[i]; if (i >= command_fns.length) { errors.push("Invalid"); } else if (typeof(e) === "string") { errors.push(e); } else if (!is_object(e) || typeof((k = e.id)) !== "string") { errors.push("Invalid"); } else { ++reg_count; this.url_info_functions[k] = command_fns[i].url_info; this.url_info_to_data_functions[k] = command_fns[i].to_data; if (command_fns[i].actions !== null) this.actions_functions[k] = command_fns[i].actions; if (command_fns[i].details !== null) this.details_functions[k] = command_fns[i].details; } } } for (ii = command_fns.length; i < ii; ++i) { errors.push("Invalid"); } // Settings for (k in settings) { setting_ns = settings[k]; for (i = 0, ii = setting_ns.length; i < ii; ++i) { name = setting_ns[i][0]; if ( !is_object(data.settings) || !is_object((o = data.settings[k])) || (v = o[name]) === undefined ) { v = (setting_ns[i].length > 1) ? setting_ns[i][1] : false; } o = config[k]; if (o === undefined) config[k] = o = {}; o[name] = v; } } return reg_count; }; API.handlers_init = {}; API.handlers = { request_end: function (data) { var id = data.id; if (typeof(id) === "string") { // Remove request delete requests_active[id]; } }, api_function: function (data, channel, reply) { var self = this, req = null, state, id, args, fn, ret; if ( typeof((id = data.id)) !== "string" || !Array.isArray((args = data.args)) ) { // Error this.reply_error(channel, reply, "Invalid extension data"); return; } // Exists if (!Array.prototype.hasOwnProperty.call(this.functions, id)) { // Error this.reply_error(channel, reply, "Invalid extension function"); return; } fn = this.functions[id]; // State if (is_object((state = data.state))) { id = state.id; req = requests_active[id]; if (req === undefined) { requests_active[id] = req = new Request(); } load_request_state(req, state); } // Callback args = Array.prototype.slice.call(args); args.push(function () { // Err var i = 0, ii = arguments.length, arguments_copy = new Array(ii); for (; i < ii; ++i) arguments_copy[i] = arguments[i]; self.send( channel, null, reply, { err: null, args: arguments_copy } ); }); // Call ret = fn.apply(req, args); }, url_info: function (data, channel, reply) { var self = this, id, url, fn; if ( typeof((id = data.id)) !== "string" || typeof((url = data.url)) !== "string" ) { // Error this.reply_error(channel, reply, "Invalid extension data"); return; } // Exists if (!Array.prototype.hasOwnProperty.call(this.url_info_functions, id)) { // Error this.reply_error(channel, reply, "Invalid extension function"); return; } fn = this.url_info_functions[id]; // Call fn(url, function (err, data) { self.send( channel, null, reply, { err: err, data: data } ); }); }, url_info_to_data: function (data, channel, reply) { var self = this, id, url_info; if ( typeof((id = data.id)) !== "string" || !is_object((url_info = data.url)) ) { // Error this.reply_error(channel, reply, "Invalid extension data"); return; } // Exists if (!Array.prototype.hasOwnProperty.call(this.url_info_to_data_functions, id)) { // Error this.reply_error(channel, reply, "Invalid extension function"); return; } // Call this.url_info_to_data_functions[id](url_info, function (err, data) { self.send( channel, null, reply, { err: err, data: data } ); }); }, create_actions: function (data, channel, reply) { var self = this, id, fn_data, fn_info; if ( typeof((id = data.id)) !== "string" || !is_object((fn_data = data.data)) || !is_object((fn_info = data.info)) ) { // Error this.reply_error(channel, reply, "Invalid extension data"); return; } // Exists if (!Array.prototype.hasOwnProperty.call(this.actions_functions, id)) { // Error this.reply_error(channel, reply, "Invalid extension function"); return; } // Call this.actions_functions[id](fn_data, fn_info, function (err, data) { self.send( channel, null, reply, { err: err, data: data } ); }); }, create_details: function (data, channel, reply) { var self = this, id, fn_data, fn_info; if ( typeof((id = data.id)) !== "string" || !is_object((fn_data = data.data)) || !is_object((fn_info = data.info)) ) { // Error this.reply_error(channel, reply, "Invalid extension data"); return; } // Exists if (!Array.prototype.hasOwnProperty.call(this.details_functions, id)) { // Error this.reply_error(channel, reply, "Invalid extension function"); return; } // Call this.details_functions[id](fn_data, fn_info, function (err, data) { self.send( channel, null, reply, { err: err, data: set_shared_node(data) } ); }); }, }; var RequestErrorMode = { None: 0, NoCache: 1, Save: 2 }; var ImageFlags = { None: 0x0, NoLeech: 0x1 }; var requests_active = {}; var Request = function () { }; var load_request_state = function (request, state) { for (var k in state) { request[k] = state[k]; } }; // Public var init = function (info, callback) { if (api === null) api = new API(); api.init(info, callback); }; var register = function (data, callback) { if (api === null) { callback.call(null, "API not init'd", 0); return; } api.register(data, callback); }; var request = function (namespace, type, unique_id, info, callback) { if (api === null || api.init_state !== 2) { callback.call(null, "API not init'd", null); return; } api.send( api.channel, "request", null, { namespace: namespace, type: type, id: unique_id, info: info }, -1, function (err, data) { if (err !== null || (err = data.err) !== null) { data = null; } else if ((data = data.data) === null) { err = "Invalid extension data"; } callback.call(null, err, data); } ); }; var insert_styles = function (styles) { var head = document.head, n; if (head) { n = document.createElement("style"); n.textContent = styles; head.appendChild(n); } }; var parse_json = function (text, def) { try { return JSON.parse(text); } catch (e) {} return def; }; var parse_html = function (text, def) { try { return new DOMParser().parseFromString(text, "text/html"); } catch (e) {} return def; }; var parse_xml = function (text, def) { try { return new DOMParser().parseFromString(text, "text/xml"); } catch (e) {} return def; }; var get_domain = function (url) { var m = /^(?:[\w\-]+):\/*((?:[\w\-]+\.)*)([\w\-]+\.[\w\-]+)/i.exec(url); return (m === null) ? [ "", "" ] : [ m[1].toLowerCase(), m[2].toLowerCase() ]; }; var get_image = function (url, flags, callback) { if (api === null || api.init_state !== 2) { callback.call(null, "API not init'd", null); return; } // Send api.send( api.channel, "get_image", null, { url: url, flags: flags }, 10000, function (err, data) { if (err !== null) { data = null; } else if (!is_object(data)) { err = "Invalid data"; } else if (typeof((err = data.err)) !== "string" && typeof((data = data.url)) !== "string") { data = null; err = "Invalid data"; } callback.call(null, err, data); } ); }; // Exports return { RequestErrorMode: RequestErrorMode, ImageFlags: ImageFlags, init: init, config: config, register: register, request: request, get_image: get_image, insert_styles: insert_styles, parse_json: parse_json, parse_html: parse_html, parse_xml: parse_xml, get_domain: get_domain, random_string: random_string, is_object: is_object, ttl_1_hour: ttl_1_hour, ttl_1_day: ttl_1_day, ttl_1_year: ttl_1_year, cache_set: cache_set, cache_get: cache_get }; })(); var main = function main_fn(xlinks_api) { // DOM helpers var $$ = function (selector, root) { return (root || document).querySelectorAll(selector); }; var $ = (function () { var d = document; var Module = function (selector, root) { return (root || d).querySelector(selector); }; Module.add = function (parent, child) { return parent.appendChild(child); }; Module.remove = function (elem) { return elem.parentNode.removeChild(elem); }; Module.tnode = function (text) { return d.createTextNode(text); }; Module.node = function (tag, class_name, text) { var elem = d.createElement(tag); elem.className = class_name; if (text !== undefined) { elem.textContent = text; } return elem; }; Module.node_ns = function (namespace, tag, class_name) { var elem = d.createElementNS(namespace, tag); elem.setAttribute("class", class_name); return elem; }; Module.node_simple = function (tag) { return d.createElement(tag); }; Module.regex_escape = function (text) { return text.replace(/[\$\(\)\*\+\-\.\/\?\[\\\]\^\{\|\}]/g, "\\$&"); }; return Module; })(); // Helpers var re_newlines = /\r\n/g; var short_months = [ "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" ]; var full_months = [ "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" ]; var objectify = function (obj) { return (obj === null || typeof(obj) !== "object") ? {} : obj; }; var time_to_string = function (timecode) { var h = Math.floor(timecode / 3600), m = Math.floor(timecode / 60) % 60, s = Math.floor(timecode) % 60, str = ""; if (h > 0) str += (h + "h"); if (m > 0) str += ((h > 0 && m < 10 ? "0" : "") + m + "m"); if (s > 0 || str.length === 0) str += ((s < 10 ? "0" : "") + s + "s"); return str; }; var time_to_string_short = function (timecode) { var h = Math.floor(timecode / 3600), m = Math.floor(timecode / 60) % 60, s = Math.floor(timecode) % 60, str = ""; str += (h > 0) ? (h + ":" + pad(m)) : m; str += ":"; str += pad(s); return str; }; var prettify_number = function (n, label, singular, plural) { var s; if (Number.prototype.toLocaleString) { s = n.toLocaleString(); } else { s = ("" + n).replace(/\B(?=(\d{3})+(?!\d))/g, ","); } return s + label + (n === 1 ? singular : plural); }; var pad = function (n) { return (n < 10 ? "0" : "") + n; }; var format_date = function (timestamp, date_only) { var d = new Date(timestamp), s = d.getUTCFullYear() + "-" + pad(d.getUTCMonth() + 1) + "-" + pad(d.getUTCDate()); return date_only ? s : (s + " " + pad(d.getUTCHours()) + ":" + pad(d.getUTCMinutes())); }; var plaintext_to_dom = function (container, text, trim) { var lines = text.split("\n"), line, i, ii, n; for (i = 0, ii = lines.length; i < ii; ++i) { line = lines[i]; if (trim) line = line.trim(); n = $.node("p", "xl-mm-details-paragraph"); if (line.length === 0) { $.add(n, $.node_simple("br")); } else { n.textContent = line; } $.add(container, n); } }; var parse_timestamp = function (time_str) { var match = /(\d+)-(\d+)-(\d+)[T\s](\d+):(\d+):(\d+)(?:\.(\d+))?(?:Z|\s*([\+\-])(\d+)(?::(\d+))?)?/i.exec(time_str), offset = 0, ms = 0; if (match === null) return 0; if (match[7] !== undefined) { ms = parseInt(match[7], 10); if (match[7].length !== 3) { ms = Math.floor((ms / Math.pow(10, match[7].length)) * 1000); } } if (match[9] !== undefined) { offset += 3600 * 1000 * parseInt(match[9], 10); } if (match[10] !== undefined) { offset += 60 * 1000 * parseInt(match[10], 10); } if (match[8] === "-") { offset = -offset; } return Date.UTC( parseInt(match[1], 10), // year parseInt(match[2], 10) - 1, // month parseInt(match[3], 10), // day parseInt(match[4], 10), // hours parseInt(match[5], 10), // minutes parseInt(match[6], 10), // seconds ms // milliseconds ) + offset; }; var parse_long_timestamp = function (time_str) { var m = /(\d+)\s+(\w+)\s+(\d+)\s*,?\s*(\d+):(\d+):(\d+)/.exec(time_str), month; if (m === null) return 0; month = Math.max(0, short_months.indexOf(m[2].toLowerCase())); return Date.UTC( parseInt(m[3], 10), month, parseInt(m[1], 10), parseInt(m[4], 10), parseInt(m[5], 10), parseInt(m[6], 10) ); }; var parse_tagstring = function (tagstr) { var tags = [], re = /,\s*/g, pos = 0, m; // Tags while ((m = re.exec(tagstr)) !== null) { tags.push(tagstr.substr(pos, m.index - pos).toLowerCase()); pos = re.lastIndex; } if (pos < tagstr.length) { tags.push(tagstr.substr(pos).toLowerCase()); } return tags; }; var get_ordered_thumbnail = function (thumbnails, order) { var i, ii, t; for (i = 0, ii = order.length; i < ii; ++i) { t = thumbnails[order[i]]; if (t !== undefined) return t; } return null; }; get_ordered_thumbnail.order_default = [ "medium", "medium_large", "large", "small", "tiny", "huge" ]; var use_icons = false; var iconify_info = function (info) { if (use_icons) { info.icon = info.site; } }; // Generic var generic_get_data = function (info, callback) { var data = xlinks_api.cache_get(info.id); callback(null, data); }; var generic_set_data = function (data, info, callback) { xlinks_api.cache_set(info.id, data, xlinks_api.ttl_1_hour * 6); callback(null); }; var generic_create_details = function (data, info, callback) { var container = $.node("div", "xl-details-sized"), is_video = (data.subtype === "video"), is_code = (data.subtype === "gist"), uploaded_by = "Uploaded by", total, t, r, c, cc, n0, n1, n2, n3, i, thumb; $.add(container, t = $.node("div", "xl-mm-details-table")); $.add(t, r = $.node("div", "xl-mm-details-table-row")); // Small if (info.site === "twitter") { t.classList.add("xl-mm-details-table-no-height"); uploaded_by = "Posted by"; } else if (is_code) { uploaded_by = "Created by"; } // Images thumb = get_ordered_thumbnail(data.thumbnails, get_ordered_thumbnail.order_default); if (thumb !== null) { $.add(r, c = $.node("div", "xl-mm-details-table-cell-left")); $.add(c, cc = $.node("div", "xl-mm-details-table-content-left")); if (is_video) { $.add(cc, n1 = $.node("div", "xl-mm-details-stats xl-theme")); if (data.stats.views !== undefined) { $.add(n1, $.node("span", "xl-mm-details-stat-views", prettify_number(data.stats.views, " view", "", "s"))); } if (data.stats.likes !== undefined) { if (data.stats.dislikes === undefined) { $.add(n1, $.node("span", "xl-mm-details-stat", prettify_number(data.stats.likes, " like", "", "s"))); } else { total = data.stats.likes + data.stats.dislikes; if (total > 0) { $.add(n1, $.node("span", "xl-mm-details-stat", "+" + prettify_number(data.stats.likes, "", "", ""))); $.add(n1, $.node("span", "xl-mm-details-stat", "\u2212" + prettify_number(data.stats.dislikes, "", "", ""))); $.add(cc, n1 = $.node("div", "xl-mm-details-rating")); $.add(n1, n2 = $.node("div", "xl-mm-details-rating-bar")); n2.style.width = (data.stats.likes / total * 100) + "%"; } } } } $.add(cc, n0 = $.node("div", "xl-mm-details-thumbnail" + (is_video ? "" : " xl-mm-details-thumbnail-full") + " xl-theme")); generic_create_details_thumbnail(n0, thumb); if (is_video) { n1 = $.node("div", "xl-mm-details-thumbnail-row"); for (i = 1; i <= 3; ++i) { if (data.thumbnails[i] === undefined) break; $.add(n1, n2 = $.node("div", "xl-mm-details-thumbnail-small")); $.add(n2, n3 = $.node("div", "xl-mm-details-thumbnail xl-theme")); generic_create_details_thumbnail(n3, data.thumbnails[i]); } if (i > 1) { $.add(cc, n1); } else { n0.classList.add("xl-mm-details-thumbnail-large"); } // Duration if (data.video !== undefined && data.video.duration > 0) { $.add(cc, n1 = $.node("div", "xl-mm-details-duration")); n1.textContent = "Length: " + time_to_string_short(data.video.duration); } } else { $.add(cc, n1 = $.node("div", "xl-mm-details-stats xl-mm-details-stats-overlay xl-theme")); if (data.stats.views !== undefined) { $.add(n1, $.node("span", "xl-mm-details-stat-views", prettify_number(data.stats.views, " view", "", "s"))); } if (data.stats.subscribers !== undefined) { $.add(n1, $.node("span", "xl-mm-details-stat", prettify_number(data.stats.subscribers, " sub", "", "s"))); } if (data.stats.videos !== undefined) { $.add(n1, $.node("span", "xl-mm-details-stat", prettify_number(data.stats.videos, " video", "", "s"))); } } } // Content $.add(r, c = $.node("div", "xl-mm-details-table-cell-right")); $.add(c, cc = $.node("div", "xl-mm-details-table-content")); $.add(cc, n1 = $.node("div", "xl-mm-details-table-content-inner")); cc = n1; // Title $.add(cc, n1 = $.node("div", "xl-details-title-container")); $.add(n1, n2 = $.node("a", "xl-details-title")); n2.href = "#"; n2.textContent = (data.original_title !== undefined ? data.original_title : data.title); n2.setAttribute("data-xl-highlight", "title"); // Upload info if (data.channel !== undefined) { $.add(cc, n1 = $.node("div", "xl-details-upload-info xl-theme")); $.add(n1, $.tnode(uploaded_by)); $.add(n1, n2 = $.node("strong", "xl-details-uploader xl-theme xl-highlight", data.channel.title)); n2.setAttribute("data-xl-highlight", "uploader"); if (data.date_created !== undefined) { $.add(n1, $.tnode("on")); $.add(n1, $.node("strong", "xl-details-upload-date", format_date(data.date_created, data.date_only === true))); } } else if (data.date_created !== undefined) { $.add(cc, n1 = $.node("div", "xl-details-upload-info xl-theme")); $.add(n1, $.tnode("Joined on")); $.add(n1, $.node("strong", "xl-details-upload-date", format_date(data.date_created, data.date_only === true))); } // Description $.add(cc, n1 = $.node("div", "xl-mm-details-description")); plaintext_to_dom(n1, data.description, !is_code); if (is_code) { n1.classList.add("xl-mm-details-description-code"); } // Done callback(null, container); }; var generic_create_details_thumbnail = function (node, url) { xlinks_api.get_image(url, xlinks_api.ImageFlags.None, function (err, url_new) { if (err !== null) url_new = url; node.style.backgroundImage = "url('" + url_new + "')"; }); }; // YouTube API var yt_api_key = "AIzaSyBmrf_-CQnv9nePDs272dFJ9shw0f-BxQA"; var yt_url_get_info = function (url, callback) { var m = /(?:https?:\/*)?[\w\-\.]*(youtube\.com?(?:\.[a-z]{2})?|youtu\.be|y2u\.be)((?:\/[^<>()\s\'\"]*)?)/i.exec(url), data = null, tc, s, m2; if (m !== null) { s = m[1].toLowerCase(); if (s === "youtu.be" || s === "y2u.be") { m2 = /^\/([a-zA-Z0-9_-]{11})/.exec(m[2]); } else { m2 = /^\/(user|channel)\/([\w\-\.]+)/i.exec(m[2]); if (m2 !== null) { s = m2[1].toLowerCase(); data = { id: "youtube_" + s + "_" + m2[2], site: "youtube", type: s, name: m2[2], tag: "YouTube", classes_remove: [ "youtube" ], monitor: true }; m2 = null; } else { m2 = /[\?\&]v=([a-zA-Z0-9_-]{11})/.exec(m[2]); if (m2 === null) { m2 = /^\/embed\/([a-zA-Z0-9_-]{11})/.exec(m[2]); } } } if (m2 !== null) { tc = yt_parse_url_for_timecode(m[2]); data = { id: "youtube_" + m2[1], site: "youtube", type: "video", vid: m2[1], timecode: tc, tag: "YouTube", classes_remove: [ "youtube" ], monitor: true }; if (tc !== null) { data.title_extra = "(at " + time_to_string(tc) + ")"; } } } if (data !== null) iconify_info(data); callback(null, data); }; var yt_url_info_to_data = function (url_info, callback) { xlinks_api.request("youtube", url_info.type, url_info.id, url_info, callback); }; var yt_setup_xhr = function (callback) { var id_list = [], loc = window.location, i, ii; for (i = 0, ii = this.infos.length; i < ii; ++i) { id_list.push(this.infos[i].vid); } callback(null, { method: "GET", url: "https://www.googleapis.com/youtube/v3/videos?key=" + yt_api_key + "&part=snippet,contentDetails,status,statistics&maxResults=" + id_list.length + "&id=" + id_list.join(","), headers: { "Cookie": "", "Referer": loc.protocol + "//" + loc.host + "/" }, any_status: true }); }; var yt_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null), datas, items, err, i, ii; if (!xlinks_api.is_object(json)) { callback("Invalid response"); return; } if (xlinks_api.is_object(json.error)) { err = json.error.message; if (typeof(err) !== "string") err = "Unknown error"; callback(err); return; } items = json.items; if (!Array.isArray(items)) { callback("Invalid response"); return; } datas = []; for (i = 0, ii = items.length; i < ii; ++i) { datas.push(yt_parse_response_single(items[i])); } callback(null, datas); }; var yt_parse_response_single = function (response) { var snippet = objectify(response.snippet), content_details = objectify(response.contentDetails), status = objectify(response.status), statistics = objectify(response.statistics), id = response.id || "", thumbnails = {}, s_thumbnails = snippet.thumbnails, data, thumbs, i, ii, t, t2; // Create data data = { type: "youtube", subtype: "video", id: id, title: snippet.title || "", description: (snippet.description || "").replace(re_newlines, "\n"), category_id: parseInt(snippet.categoryId, 10) || 0, date_created: parse_timestamp(snippet.publishedAt || ""), thumbnails: thumbnails, channel: { id: snippet.channelId || "", title: snippet.channelTitle || "", }, video: { duration: yt_parse_duration(content_details.duration || ""), dimension: content_details.dimension || "", // 2d / 3d definition: content_details.definition || "", // hd / sd captions: (content_details.caption === "true") }, info: { upload_status: status.uploadStatus || "", // uploaded / processed privacy: status.privacyStatus || "", // public / unlisted license: status.license || "", // youtube / creativeCommon licensed: content_details.licensedContent || false, embeddable: status.embeddable || false, live_content: snippet.liveBroadcastContent, }, stats: { views: parseInt(statistics.viewCount || 0, 10) || 0, likes: parseInt(statistics.likeCount || 0, 10) || 0, dislikes: parseInt(statistics.dislikeCount || 0, 10) || 0, favorites: parseInt(statistics.favoriteCount || 0, 10) || 0, comments: parseInt(statistics.commentCount || 0, 10) || 0, } }; // Add thumbnails thumbs = xlinks_api.is_object(s_thumbnails) ? Object.keys(s_thumbnails) : []; for (i = 0, ii = thumbs.length; i < ii; ++i) { t = thumbs[i]; t2 = yt_thumbnail_names[t]; if (t2 !== undefined) { thumbnails[t2] = s_thumbnails[t].url || ""; } } for (i = 1; i <= 3; ++i) { thumbnails[i] = "https://i1.ytimg.com/vi/" + id + "/" + i + ".jpg"; } // Done return data; }; var yt_channel_setup_xhr = function (callback) { var id_list = [], loc = window.location, mode = (this.infos[0].type === "user" ? "forUsername" : "id"), i, ii; for (i = 0, ii = this.infos.length; i < ii; ++i) { id_list.push(this.infos[i].name); } callback(null, { method: "GET", url: "https://www.googleapis.com/youtube/v3/channels?key=" + yt_api_key + "&part=snippet,statistics&maxResults=" + id_list.length + "&" + mode + "=" + id_list.join(","), headers: { "Cookie": "", "Referer": loc.protocol + "//" + loc.host + "/" }, any_status: true }); }; var yt_channel_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null), datas, items, err, i, ii; if (!xlinks_api.is_object(json)) { callback("Invalid response"); return; } if (xlinks_api.is_object(json.error)) { err = json.error.message; if (typeof(err) !== "string") err = "Unknown error"; callback(err); return; } items = json.items; if (!Array.isArray(items)) { callback("Invalid response"); return; } datas = []; for (i = 0, ii = items.length; i < ii; ++i) { datas.push(yt_channel_parse_response_single(items[i])); } callback(null, datas); }; var yt_channel_parse_response_single = function (response) { var snippet = objectify(response.snippet), statistics = objectify(response.statistics), thumbnails = {}, s_thumbnails = snippet.thumbnails, data, thumbs, i, ii, t, t2; // Create data data = { type: "youtube", subtype: "channel", id: response.id || "", title: "Channel: " + (snippet.title || ""), original_title: snippet.title || "", description: (snippet.description || "").replace(re_newlines, "\n"), date_created: parse_timestamp(snippet.publishedAt || ""), thumbnails: thumbnails, stats: { views: parseInt(statistics.viewCount || 0, 10) || 0, videos: parseInt(statistics.videoCount || 0, 10) || 0, comments: parseInt(statistics.commentCount || 0, 10) || 0 } }; if (!statistics.hiddenSubscriberCount) { data.stats.subscribers = parseInt(statistics.subscriberCount || 0, 10) || 0; } // Add thumbnails thumbs = xlinks_api.is_object(s_thumbnails) ? Object.keys(s_thumbnails) : []; for (i = 0, ii = thumbs.length; i < ii; ++i) { t = thumbs[i]; t2 = yt_thumbnail_names[t]; if (t2 !== undefined) { thumbnails[t2] = s_thumbnails[t].url || ""; } } // Done return data; }; var yt_thumbnail_names = { "default": "small", "medium": "medium", "standard": "medium_large", "high": "large", "maxres": "huge" }; var yt_parse_url_for_timecode = function (url) { var m = /[\?\&\#]t=(?:([0-9\.]+)h)?(?:([0-9\.]+)m)?([0-9\.]+)s?/i.exec(url), t = null; if (m !== null) { // Add time t = 0; if (m[1] !== undefined) t += parseFloat(m[1]) * 60 * 60; if (m[2] !== undefined) t += parseFloat(m[2]) * 60; t += parseFloat(m[3]); } return t; }; var yt_parse_duration = function (time_str) { var match = /PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?/i.exec(time_str), t = 0; if (match !== null) { if (match[1] !== undefined) t += (parseInt(match[1], 10) || 0) * 60 * 60; if (match[2] !== undefined) t += (parseInt(match[2], 10) || 0) * 60; if (match[3] !== undefined) t += (parseInt(match[3], 10) || 0); } return t; }; var yt_create_actions = function (data, info, callback) { var urls = [], timecode1 = "", timecode2 = "", timecode3 = ""; if (info.timecode !== null) { timecode1 = "&t=" + info.timecode + "s"; timecode2 = "?t=" + info.timecode + "s"; timecode3 = "#t=" + info.timecode + "s"; } urls.push([ "View URL as:", "https://www.youtube.com/watch?v=" + data.id + timecode1, "youtube.com" ]); urls.push([ null, "https://youtu.be/" + data.id + timecode2, "youtu.be" ]); urls.push([ null, "http://y2u.be/" + data.id + timecode3, "y2u.be" ]); urls.push([ null, "https://www.youtube.com/embed/" + data.id, "Embed URL" ]); callback(null, urls); }; // Vimeo API var vimeo_url_get_info = function (url, callback) { var data = null, type, s, m, m2; if ( (m = /(?:https?:\/*)?[\w\-\.]*vimeo\.com((?:\/[\w\W]*)?)/i.exec(url)) !== null && (m2 = /^\/(?:m\/)?(?:(\d+)|channels\/([^\/\?\#]+)(?:\/(\d+))?|([^\/\?\#]+))/i.exec(m[1])) !== null ) { if ((s = m2[1]) !== undefined) { data = { id: "vimeo_" + s, site: "vimeo", type: "video", vid: parseInt(s, 10), tag: "Vimeo", classes_remove: [ "vimeo" ], monitor: true }; } else { s = m2[2]; type = "channel"; if (s === undefined) { s = m2[3]; type = "user"; } data = { id: "vimeo_" + type + "_" + s, site: "vimeo", type: type, name: s, tag: "Vimeo", classes_remove: [ "vimeo" ], monitor: true }; } } if (data !== null) iconify_info(data); callback(null, data); }; var vimeo_url_info_to_data = function (url_info, callback) { xlinks_api.request("vimeo", url_info.type, url_info.id, url_info, callback); }; var vimeo_setup_xhr = function (callback) { var info = this.infos[0]; callback(null, { method: "GET", url: "https://vimeo.com/api/v2/video/" + info.vid + ".json", headers: { "Cookie": "" } }); }; var vimeo_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null), item; if ( !xlinks_api.is_object(json) || !Array.isArray(json) || json.length < 1 || !xlinks_api.is_object((item = json[0])) ) { callback("Invalid response"); } else { callback(null, [ vimeo_parse_response_single(item) ]); } }; var vimeo_parse_response_single = function (response) { var tags = parse_tagstring("" + (response.tags || "")); // Create data return { type: "vimeo", subtype: "video", id: response.id || 0, title: response.title || "", description: vimeo_normalize_description(response.description || ""), date_created: parse_timestamp(response.upload_date || ""), thumbnails: { small: response.thumbnail_small, medium: response.thumbnail_medium, large: response.thumbnail_large, }, tags: tags, channel: { id: response.user_id || 0, title: response.user_name || "", thumbnails: { small: response.user_portrait_small, medium: response.user_portrait_medium, large: response.user_portrait_large, huge: response.user_portrait_huge } }, video: { duration: response.duration || 0, resolution: [ response.width || 0, response.height || 0 ] }, info: { embeddable: (response.embed_privacy === "anywhere") }, stats: { views: response.stats_number_of_plays || 0, likes: response.stats_number_of_likes || 0, comments: response.stats_number_of_comments || 0 } }; }; var vimeo_user_setup_xhr = function (callback) { var info = this.infos[0]; callback(null, { method: "GET", url: "https://vimeo.com/api/v2/" + info.name + "/info.json", headers: { "Cookie": "" } }); }; var vimeo_user_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null); if (!xlinks_api.is_object(json)) { callback("Invalid response"); } else { callback(null, [ vimeo_user_parse_response_single(json) ]); } }; var vimeo_user_parse_response_single = function (response) { // Create data return { type: "vimeo", subtype: "user", id: response.id || 0, title: "User: " + (response.display_name || ""), original_title: (response.display_name || ""), description: vimeo_normalize_description(response.bio || ""), date_created: parse_timestamp(response.created_on || ""), thumbnails: { small: response.user_portrait_small, medium: response.user_portrait_medium, large: response.user_portrait_large, huge: response.user_portrait_huge }, stats: { videos: response.total_videos_uploaded || 0 }, info: { url: response.url || "", location: response.location || "" } }; }; var vimeo_channel_setup_xhr = function (callback) { var info = this.infos[0]; callback(null, { method: "GET", url: "https://vimeo.com/api/v2/channel/" + info.name + "/info.json", headers: { "Cookie": "" } }); }; var vimeo_channel_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null); if (!xlinks_api.is_object(json)) { callback("Invalid response"); } else { callback(null, [ vimeo_channel_parse_response_single(json) ]); } }; var vimeo_channel_parse_response_single = function (response) { // Create data return { type: "vimeo", subtype: "channel", id: response.id || 0, title: "Channel: " + (response.name || ""), original_title: (response.name || ""), description: vimeo_normalize_description(response.description || ""), date_created: parse_timestamp(response.created_on || ""), author: { id: response.creator_id || 0, title: response.creator_display_name || "" }, thumbnails: { huge: response.logo }, stats: { videos: response.total_videos || 0, subscribers: response.total_subscribers || 0 } }; }; var vimeo_normalize_description = function (text) { return text.replace(//g, "").replace(/\r\n/g, "\n"); }; // SoundCloud API var sc_url_get_info = function (url, callback) { var data = null, s, u, m, m2; if ( (m = /(?:https?:\/*)?[\w\-\.]*soundcloud\.com((?:\/[\w\W]*)?)/i.exec(url)) !== null && (m2 = /^\/(?:groups\/([^\/\?\#]+)|([^\/\?\#]+)(?:\/sets\/([^\/\?\#]+)|\/([^\/\?\#]+))?)/i.exec(m[1])) !== null ) { if ((s = m2[1]) !== undefined) { data = { id: "soundcloud_group_" + s, site: "soundcloud", type: "group", name: s, tag: "SoundCloud", classes_remove: [ "soundcloud" ], monitor: true }; } else { u = m2[2]; if ((s = m2[3]) !== undefined) { data = { id: "soundcloud_playlist_" + u + "_" + s, site: "soundcloud", type: "playlist", name: u, playlist: s, tag: "SoundCloud", classes_remove: [ "soundcloud" ], monitor: true }; } else if ((s = m2[4]) !== undefined) { data = { id: "soundcloud_track_" + u + "_" + s, site: "soundcloud", type: "track", name: u, track: s, tag: "SoundCloud", classes_remove: [ "soundcloud" ], monitor: true }; } else { data = { id: "soundcloud_user_" + u, site: "soundcloud", type: "user", name: u, tag: "SoundCloud", classes_remove: [ "soundcloud" ], monitor: true }; } } } if (data !== null) iconify_info(data); callback(null, data); }; var sc_url_info_to_data = function (url_info, callback) { xlinks_api.request("soundcloud", "generic", url_info.id, url_info, callback); }; var sc_setup_xhr = function (callback) { var info = this.infos[0], type = info.type, ext = info.name; if (type === "track") { ext += "/" + info.track; } else if (type === "playlist") { ext += "/sets/" + info.playlist; } else if (type === "group") { ext += "/groups/" + info.name; } callback(null, { method: "GET", url: "https://soundcloud.com/oembed?format=json&iframe=true&url=https://soundcloud.com/" + ext, headers: { "Cookie": "" } }); }; var sc_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null); if (!xlinks_api.is_object(json)) { callback("Invalid response"); } else { callback(null, [ sc_parse_response_single(json, this.infos[0]) ]); } }; var sc_parse_response_single = function (response, info) { var type_prefix = "", type = info.type, title = (response.title || ""), author = (response.author_name || ""), data; if (type === "track") { if (author.length > 0) { title = title.replace(new RegExp("\\s+by\\s+" + $.regex_escape(author) + "$"), ""); } } else { type_prefix = type[0].toUpperCase() + type.substr(1) + ": "; } // Create data data = { type: "soundcloud", subtype: type, title: type_prefix + title, description: vimeo_normalize_description(response.description || ""), thumbnails: { large: response.thumbnail_url, }, stats: {} }; if (type !== "user") { data.channel = { title: author }; } // Done return data; }; // DailyMotion API var dm_url_get_info = function (url, callback) { var data = null, s, m, m2; if ( (m = /(?:https?:\/*)?[\w\-\.]*dailymotion\.com((?:\/[\w\W]*)?)/i.exec(url)) !== null && (m2 = /^\/(?:video\/([a-zA-Z0-9]+)|([\w\-\.]+))/i.exec(m[1])) !== null ) { if (m2[1] !== undefined) { s = m2[1]; data = { id: "dailymotion_" + s, site: "dailymotion", type: "video", vid: s, tag: "DailyMotion", classes_remove: [ "dailymotion" ] }; } else { s = m2[2]; data = { id: "dailymotion_channel_" + s, site: "dailymotion", type: "user", name: s, tag: "DailyMotion", classes_remove: [ "dailymotion" ] }; } } if (data !== null) iconify_info(data); callback(null, data); }; var dm_url_info_to_data = function (url_info, callback) { xlinks_api.request("dailymotion", url_info.type, url_info.id, url_info, callback); }; var dm_setup_xhr = function (callback) { var info = this.infos[0]; callback(null, { method: "GET", url: "https://api.dailymotion.com/video/" + info.vid + "?family_filter=false&fields=allow_embed,created_time,description,duration,id,owner.id,owner.screenname,owner.username,private,rating,ratings_total,thumbnail_120_url,thumbnail_180_url,thumbnail_240_url,thumbnail_360_url,thumbnail_480_url,thumbnail_720_url,title,views_total", headers: { "Cookie": "" } }); }; var dm_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null); if (!xlinks_api.is_object(json)) { callback("Invalid response"); } else { callback(null, [ dm_parse_response_single(json) ]); } }; var dm_parse_response_single = function (response) { var tags = (Array.isArray(response.tags) ? response.tags : []); // Create data return { type: "dailymotion", subtype: "video", id: response.id, title: response.title || "", description: dm_normalize_description(response.description || ""), date_created: (response.created_time || 0) * 1000, tags: tags, thumbnails: { tiny: response.thumbnail_120_url, small: response.thumbnail_180_url, medium: response.thumbnail_240_url, medium_large: response.thumbnail_360_url, large: response.thumbnail_480_url, huge: response.thumbnail_720_url, }, channel: { id: response["owner.id"] || "", title: response["owner.screenname"] || "", name: response["owner.username"] || "", }, video: { duration: response.duration || 0, }, info: { privacy: response.private ? "private" : "public" }, stats: { views: response.views_total || 0 } }; }; var dm_user_setup_xhr = function (callback) { var info = this.infos[0]; callback(null, { method: "GET", url: "https://api.dailymotion.com/user/" + info.name + "?family_filter=false&fields=created_time,description,id,screenname,username,avatar_120_url,avatar_190_url,avatar_240_url,avatar_360_url,avatar_480_url,avatar_720_url,videos_total,views_total", headers: { "Cookie": "" } }); }; var dm_user_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null); if (!xlinks_api.is_object(json)) { callback("Invalid response"); } else { callback(null, [ dm_user_parse_response_single(json) ]); } }; var dm_user_parse_response_single = function (response) { // Create data return { type: "dailymotion", subtype: "user", id: response.id || "", title: "User: " + (response.screenname || ""), original_title: response.screenname || "", username: response.username, description: dm_normalize_description(response.description || ""), date_created: (response.created_time || 0) * 1000, thumbnails: { tiny: response.avatar_120_url, small: response.avatar_180_url, medium: response.avatar_240_url, medium_large: response.avatar_360_url, large: response.avatar_480_url, huge: response.avatar_720_url, }, stats: { views: response.views_total || 0, videos: response.videos_total || 0 } }; }; var dm_normalize_description = function (text) { return text.replace(/\s*/g, "\n"); }; // LiveLeak API var ll_url_get_info = function (url, callback) { var data = null, s, m, m2; if ( (m = /(?:https?:\/*)?[\w\-\.]*liveleak\.com((?:\/[\w\W]*)?)/i.exec(url)) !== null && (m2 = /\/view[\w\W]*(?:[\?\&]i=([\w\_]+))/i.exec(m[1])) !== null ) { s = m2[1]; data = { id: "liveleak_" + s, site: "liveleak", vid: s, tag: "LiveLeak" }; } if (data !== null) iconify_info(data); callback(null, data); }; var ll_url_info_to_data = function (url_info, callback) { xlinks_api.request("liveleak", "video", url_info.id, url_info, callback); }; var ll_setup_xhr = function (callback) { var info = this.infos[0]; callback(null, { method: "GET", url: "http://mobile.liveleak.com/view?i=" + info.vid + "&ajax=1", headers: { "Cookie": "" } }); }; var ll_parse_response = function (xhr, callback) { var html = xlinks_api.parse_html(xhr.responseText, null); if (html === null) { callback("Invalid response"); } else { callback(null, [ ll_parse_response_single(html, this.infos[0]) ]); } }; var ll_parse_response_single = function (html, info) { var re_newlines = /\r?\n/g, re_thumbnail = /image\s*:\s*['"]([^'"\n]*)['"]/, re_colon = /:$/, data, n, n2, ns, s, m, i, ii; // Create data data = { type: "liveleak", subtype: "video", id: info.vid, title: "", description: "", date_created: 0, date_only: true, tags: [], thumbnails: {}, channel: { title: "" }, video: { duration: 0, definition: "sd" }, stats: { views: 0, plays: 0, comments: 0, } }; // Get info if ((n = $(".section_title", html)) !== null) { data.title = n.textContent.trim(); if ($("img", n) !== null) { data.video.definition = "hd"; } } if ((n = $("#body_text>p", html)) !== null) { s = ""; for (n2 = n.firstChild; n2 !== null; n2 = n2.nextSibling) { if (n2.nodeType === Node.TEXT_NODE) { s += n2.nodeValue.replace(re_newlines, " "); } else if (n2.nodeType === Node.ELEMENT_NODE) { if (n2.tagName === "BR") { s += "\n"; } else if (n2.tagName === "A") { s += n2.textContent.replace(re_newlines, " "); } } } data.description = s; } if ( (n = $("center", html)) !== null && (m = /Plays:\s*(\d+)/i.exec(n.textContent)) !== null ) { data.stats.plays = parseInt(m[1], 10); } ns = $$("script", html); for (i = 0, ii = ns.length; i < ii; ++i) { if ((m = re_thumbnail.exec(ns[i].textContent)) !== null) { data.thumbnails.large = m[1]; break; } } if ((n = $(".tab_nav_contents", html)) !== null) { ns = $$("strong", n); for (i = 0, ii = ns.length; i < ii; ++i) { n = ns[i]; s = n.textContent.trim().replace(re_colon, "").toLowerCase(); if (s === "views") { if ( (n2 = ll_get_next_tnode(n)) !== null && (m = /^\s*(\d+)/.exec(n2.nodeValue)) !== null ) { data.stats.views = parseInt(m[1], 10); } } else if (s === "comments") { if ( (n2 = ll_get_next_tnode(n)) !== null && (m = /^\s*(\d+)/.exec(n2.nodeValue)) !== null ) { data.stats.comments = parseInt(m[1], 10); } } else if (s === "by") { if ((n2 = ll_get_next_node(n, "A")) !== null) { data.channel.title = n2.textContent.trim(); } } else if (s === "tags") { n2 = n; while ((n2 = ll_get_next_node(n2, "A")) !== null) { data.tags.push(n2.textContent.trim()); } } else if (s === "added") { if ( (n2 = ll_get_next_tnode(n)) !== null && (m = /(\d+)\s*(sec|min|hour|day)s?\s+ago|(\w+)-(\d+)-(\d+)/i.exec(n2.nodeValue)) !== null ) { data.date_created = ll_parse_timestamp(m); } } } } // Done return data; }; var ll_parse_timestamp = function (match) { var v, s, d; if (match[1] !== undefined) { v = parseInt(match[1], 10); s = match[2].toLowerCase(); if (s === "min") { s *= 60 * 1000; } else if (s === "hour") { s *= 60 * 60 * 1000; } else if (s === "day") { s *= 24 * 60 * 60 * 1000; } else { // if (s === "sec") { s *= 1000; } d = new Date().getTime(); } else { s = match[3].toLowerCase(); d = Date.UTC( parseInt(match[5], 10), // year Math.max(0, short_months.indexOf(s)), parseInt(match[4], 10) // day ); } return d; }; var ll_get_next_tnode = function (node) { var n = node.nextSibling; if (n === null) { n = node.parentNode.nextSibling; } return (n.nodeType === Node.TEXT_NODE) ? n : null; }; var ll_get_next_node = function (node, tag_name) { while ((node = node.nextSibling) !== null) { if (node.nodeType === Node.ELEMENT_NODE) { if (node.tagName !== tag_name) node = null; break; } } return node; }; // Vine API var vine_url_get_info = function (url, callback) { var data = null, s, m, m2; if ( (m = /(?:https?:\/*)?[\w\-\.]*vine\.co((?:\/[\w\W]*)?)/i.exec(url)) !== null && (m2 = /^\/v\/([\w\_\-]+)/i.exec(m[1])) !== null ) { s = m2[1]; data = { id: "vine_" + s, site: "vine", vid: s, tag: "Vine" }; } if (data !== null) iconify_info(data); callback(null, data); }; var vine_url_info_to_data = function (url_info, callback) { xlinks_api.request("vine", "generic", url_info.id, url_info, callback); }; var vine_setup_xhr = function (callback) { var info = this.infos[0]; callback(null, { method: "GET", url: "https://vine.co/oembed.json?id=" + info.vid, headers: { "Cookie": "" } }); }; var vine_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null); if (!xlinks_api.is_object(json)) { callback("Invalid response"); } else { callback(null, [ vine_parse_response_single(json) ]); } }; var vine_parse_response_single = function (response) { var title = response.title || "", description = "", hashtags = /\s*((?:#[^\s\.\?\!\&\^\%\$\@\(\)\[\]\{\}<>\;\:\'\"\,\*\`\~\-\|\/\\]+\s*)+)([\.\!\?]*)$/.exec(title), t; if (hashtags !== null) { // Separate out hashtags at the end t = title.substr(0, hashtags.index).trim(); if (t.length > 0) { title = t + hashtags[2]; description = hashtags[1].trim(); } } // Create data return { type: "vine", subtype: "vinevideo", title: title, description: description, channel: { title: response.author_name || "", }, thumbnails: { large: response.thumbnail_url, }, stats: {} }; }; // Twitter API var twitter_url_get_info = function (url, callback) { var data = null, user, id, m, m2; if ( (m = /(?:https?:\/*)?[\w\-\.]*twitter\.com((?:\/[\w\W]*)?)/i.exec(url)) !== null && (m2 = /^\/([^\/]+)\/status\/(\d+)/i.exec(m[1])) !== null ) { user = m2[1]; id = m2[2]; data = { id: "twitter_" + user + "_" + id, site: "twitter", tid: id, user: user, tag: "Twitter", classes_remove: [ "twitter" ] }; } if (data !== null) iconify_info(data); callback(null, data); }; var twitter_url_info_to_data = function (url_info, callback) { xlinks_api.request("twitter", "generic", url_info.id, url_info, callback); }; var twitter_setup_xhr = function (callback) { var info = this.infos[0]; callback(null, { method: "GET", url: "https://api.twitter.com/1/statuses/oembed.json?url=https://twitter.com/" + info.user + "/status/" + info.tid, headers: { "Cookie": "", "Accept-Language": "en-us;q=0.8" } }); }; var twitter_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null); if (!xlinks_api.is_object(json)) { callback("Invalid response"); } else { callback(null, [ twitter_parse_response_single(json) ]); } }; var twitter_parse_response_single = function (response) { var author_id = (response.author_name || ""), author_ext = "", description = "", title = "Tweet by " + author_id, html = xlinks_api.parse_html(response.html || "", null), re_pic = /^pic\.twitter\.com\/(\w+)$/, date_created = null, data, m, n, t, i; if ((m = /\/([^\/]+)$/.exec(response.author_url || "")) !== null) { author_id = m[1]; author_ext = " (@" + author_id + ")"; title += author_ext; } if (html !== null && (html = $("blockquote", html)) !== null) { if ((n = $("p", html)) !== null) { // Create description for (n = n.firstChild; n !== null; n = n.nextSibling) { if (n.nodeType === Node.TEXT_NODE) { description += n.nodeValue; } else if (n.nodeType === Node.ELEMENT_NODE) { if (n.tagName === "BR") { description += "\n"; } else if (n.tagName === "A") { t = n.textContent; if ((m = re_pic.exec(t)) !== null && n.nextSibling === null) { t = ""; } description += t; } } } } if ( (n = $("p+a", html)) !== null && (m = /(\w+)\s+(\d+)\s*,?\s*(\d+)/.exec(n.textContent.trim())) !== null ) { i = Math.max(0, full_months.indexOf(m[1].toLowerCase())); date_created = Date.UTC( parseInt(m[3], 10), i, parseInt(m[2], 10) ); } } // Create data data = { type: "twitter", subtype: "tweet", title: title, description: description, channel: { id: author_id, title: (response.author_name || "") + author_ext, }, thumbnails: {}, stats: {} }; if (date_created !== null) { data.date_created = date_created; data.date_only = true; } // Done return data; }; // Livestream API var ls_url_get_info = function (url, callback) { var data = null, original, type, user, id, m, m2; if ( (m = /(?:https?:\/*)?([\w\-\.]*)livestream\.com((?:\/[\w\W]*)?)/i.exec(url)) !== null && (m2 = /^\/(?:accounts\/(\d+)(?:\/events\/(\d+))?|([^\/\?\#]+)(?:\/events\/(\d+)|\/([^\/\?\#]+))?)/i.exec(m[2])) !== null ) { original = (m[1].toLowerCase() === "original."); type = "channel"; if (m2[1] !== undefined) { user = m2[1]; if (m2[2] !== undefined) { id = m2[2]; type = "video"; original = false; } } else if (m2[3] !== undefined) { user = m2[3]; if (m2[4] !== undefined) { id = m2[4]; type = "video"; original = false; } else if (m2[5] !== undefined) { id = m2[5]; type = "video"; if ( id === "video" && (m = /[\?\&]clipId=([\w\-\_]+)/i.exec(m[2])) !== null ) { id = m[1]; original = true; } } } if (type === "video") { data = { id: "livestream_" + user + "_" + id, site: "livestream", type: type, vid: id, user: user, original: original, tag: "Livestream" }; } else { data = { id: "livestream_" + user, site: "livestream", type: type, user: user, original: original, tag: "Livestream" }; } } if (data !== null) iconify_info(data); callback(null, data); }; var ls_url_info_to_data = function (url_info, callback) { xlinks_api.request("livestream", url_info.type, url_info.id, url_info, callback); }; var ls_setup_xhr = function (callback) { var info = this.infos[0], url; if (info.original) { url = "http://x" + info.user.replace(/[^a-zA-Z0-9]/g, "-") + "x.api.channel.livestream.com/2.0/clipdetails.json?id=" + info.vid; } else { url = "https://livestream.com/api/accounts/" + info.user + "/events/" + info.vid; } callback(null, { method: "GET", url: url, headers: { "Cookie": "" } }); }; var ls_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null), info = this.infos[0]; if (!xlinks_api.is_object(json)) { callback("Invalid response"); } else { callback(null, [ info.original ? ls_parse_response_single_old(json) : ls_parse_response_single_new(json) ]); } }; var ls_parse_response_single_old = function (response) { var short_name = "", title, item, data, m, t, r; r = objectify(response.channel); if (!Array.isArray((item = r.item))) item = []; item = item[0]; if (!xlinks_api.is_object(item)) { return ls_channel_parse_response_single_old(response, true); } response = r; m = /[^\/]+$/.exec(response.link); if (m !== null) short_name = m[0]; title = item.title || ""; // Create data data = { type: "livestream", subtype: "video", id: item.guid || "", title: title, original: true, description: (item.description || "").replace(re_newlines, "\n"), date_created: parse_long_timestamp(response.pubDate || ""), thumbnails: {}, channel: { id: short_name, title: response.title || "", thumbnails: {} }, video: { duration: objectify(item.content)["@duration"] || 0, }, info: {}, stats: { views: item.viewsCount || 0 } }; if (typeof((t = objectify(response.image).url)) === "string" && t.length > 0) { data.channel.thumbnails.medium = t; } if (typeof((t = objectify(item.thumbnail)["@url"])) === "string" && t.length > 0) { data.thumbnails.medium = t; } if (typeof((t = item.thumbnailLow)) === "string" && t.length > 0) { data.thumbnails.small = t; } // Done return data; }; var ls_parse_response_single_new = function (response) { var owner = objectify(response.owner), owner_picture = objectify(owner.picture), logo = objectify(response.logo), likes = objectify(response.likes), tags = parse_tagstring("" + (response.tags || "")), data; // Create data data = { type: "livestream", subtype: "video", id: response.id || 0, title: response.full_name || "", description: (response.description || "").replace(re_newlines, "\n"), date_created: parse_timestamp(response.created_at || ""), tags: tags, thumbnails: {}, channel: { id: owner.id || 0, title: owner.full_name || "", name: owner.short_name || "", thumbnails: {} }, video: {}, info: { privacy: response.is_public ? "public" : "private", embeddable: (response.is_embeddable === true), live_content: (response.in_progress === true), }, stats: { views: response.viewer_count || 0, likes: likes.total || 0 } }; ls_get_thumbs(data.thumbnails, logo); ls_get_thumbs(data.channel.thumbnails, owner_picture); // Done return data; }; var ls_channel_setup_xhr = function (callback) { var info = this.infos[0], url; if (info.original === (this.is_retry !== true)) { url = "http://x" + info.user.replace(/[^a-zA-Z0-9]/g, "-") + "x.api.channel.livestream.com/2.0/info.json"; } else { url = "https://livestream.com/api/accounts/" + info.user; } callback(null, { method: "GET", url: url, headers: { "Cookie": "" }, any_status: true }); }; var ls_channel_parse_response = function (xhr, callback) { var info = this.infos[0], json, original, data; if (xhr.status !== 200) { if (this.is_retry === true) { callback("Invalid status"); } else { // Retry this.is_retry = true; callback(null, null, 200); } return; } json = xlinks_api.parse_json(xhr.responseText, null); if (!xlinks_api.is_object(json)) { callback("Invalid response"); } else { original = info.original; if (this.is_retry === true) original = !original; data = original ? ls_channel_parse_response_single_old(json, false) : ls_channel_parse_response_single_new(json); if (data !== null) { callback(null, [ data ]); } else if (this.is_retry === true) { callback("Invalid channel"); } else { // Retry this.is_retry = true; callback(null, null, 200); } } }; var ls_channel_parse_response_single_old = function (response, removed_video) { var short_name = "", title, original_title, data, m, t; response = objectify(response.channel); m = /[^\/]+$/.exec(response.link); if (m !== null) short_name = m[0]; title = response.title || ""; original_title = title; if (short_name.length > 0 && short_name !== original_title) { original_title += " (" + short_name + ")"; } if (removed_video) { title += " (Video unavailable)"; } // Create data data = { type: "livestream", subtype: "channel", id: short_name, title: "Channel: " + title, original_title: original_title, original: true, category: (response.category || "").toLowerCase(), description: (response.description || "").replace(re_newlines, "\n"), date_created: parse_long_timestamp(response.pubDate || ""), thumbnails: {}, stats: { views: response.viewsCount || 0 } }; if (typeof((t = objectify(response.image).url)) === "string" && t.length > 0) { data.thumbnails.medium = t; } // Done return data; }; var ls_channel_parse_response_single_new = function (response) { var short_name = response.short_name || "", title, original_title, data; title = response.full_name || ""; original_title = title; if (short_name.length > 0 && short_name !== original_title) { original_title += " (" + short_name + ")"; } // Create data data = { type: "livestream", subtype: "channel", id: response.id || 0, title: "Channel: " + title, original_title: original_title, name: short_name, description: (response.description || "").replace(re_newlines, "\n"), date_created: parse_timestamp(response.created_at || ""), thumbnails: {}, stats: {} }; ls_get_thumbs(data.thumbnails, objectify(response.picture)); // Done return data; }; var ls_get_thumbs = function (target, src) { var t; if (typeof((t = src.url)) === "string") target.huge = t; if (typeof((t = src.thumb_url)) === "string") target.tiny = t; if (typeof((t = src.small_url)) === "string") target.medium = t; if (typeof((t = src.medium_url)) === "string") target.large = t; }; // Nicovideo API var nv_url_get_info = function (url, callback) { var data = null, id, m, m2; if ( (m = /(?:https?:\/*)?([\w\-\.]*)nicovideo\.jp((?:\/[\w\W]*)?)/i.exec(url)) !== null && (m2 = /^\/watch\/((?:sm)?\d+)/i.exec(m[2])) !== null ) { id = m2[1]; data = { id: "nicovideo_" + id, site: "nicovideo", vid: id, tag: "Niconico" }; } if (data !== null) iconify_info(data); callback(null, data); }; var nv_url_info_to_data = function (url_info, callback) { xlinks_api.request("nicovideo", "video", url_info.id, url_info, callback); }; var nv_setup_xhr = function (callback) { var info = this.infos[0]; callback(null, { method: "GET", url: "http://ext.nicovideo.jp/api/getthumbinfo/" + info.vid, headers: { "Cookie": "" } }); }; var nv_parse_response = function (xhr, callback) { var xml = xlinks_api.parse_xml(xhr.responseText, null); if (xml === null) { callback("Invalid response"); } else { callback(null, [ nv_parse_response_single(xml) ]); } }; var nv_parse_response_single = function (xml) { var data, thumb, err, n1, n2; // Check for errors n1 = $("thumb", xml); if (n1 === null) { if ((n1 = $("error>code", xml)) !== null) { err = n1.textContent; if ((n2 = $("error>description", xml)) !== null) { err += " - " + n2.textContent; } } else { err = "Invalid response"; } return { error: err }; } xml = n1; // Create data data = { type: "nicovideo", subtype: "video", id: nv_select_text(xml, "video_id", ""), title: nv_select_text(xml, "title", "Unknown title"), description: nv_select_text(xml, "description", ""), date_created: parse_timestamp(nv_select_text(xml, "first_retrieve", "")), thumbnails: {}, channel: { id: nv_select_text(xml, "user_id", ""), title: nv_select_text(xml, "user_nickname", ""), thumbnails: {} }, video: { duration: nv_parse_duration(nv_select_text(xml, "length", "")) }, info: { embeddable: (nv_select_text(xml, "embeddable", "1") === "1") }, stats: { views: parseInt(nv_select_text(xml, "view_counter", "0"), 10) || 0, comments: parseInt(nv_select_text(xml, "comment_num", "0"), 10) || 0, } }; if ((thumb = nv_select_text(xml, "thumbnail_url", "")).length > 0) { data.thumbnails.medium = thumb; } if ((thumb = nv_select_text(xml, "user_icon_url", "")).length > 0) { data.channel.thumbnails.medium = thumb; } // Done return data; }; var nv_parse_duration = function (text) { var m = /(?:(\d+):)?(\d+):(\d+)/.exec(text); if (m === null) return 0; return parseInt(m[3], 10) + parseInt(m[2], 10) * 60 + (m[1] === undefined ? 0 : parseInt(m[1], 10)) * 3600; }; var nv_select_text = function (root, selector, def) { var n = $(selector, root); return (n === null) ? def : n.textContent; }; // Github Gist API var gg_url_get_info = function (url, callback) { var data = null, id, m, m2; if ( (m = /(?:https?:\/*)?([\w\-\.]*)gist\.github\.com((?:\/[\w\W]*)?)/i.exec(url)) !== null && (m2 = /^\/(?:[\w\-]+\/)?(\w+)/i.exec(m[2])) !== null ) { id = m2[1]; data = { id: "github_gist_" + id, site: "githubgist", gid: id, tag: "Gist", classes_remove: [ "gist" ] }; } if (data !== null) iconify_info(data); callback(null, data); }; var gg_url_info_to_data = function (url_info, callback) { xlinks_api.request("githubgist", "gist", url_info.id, url_info, callback); }; var gg_setup_xhr = function (callback) { var info = this.infos[0]; callback(null, { method: "GET", url: "https://api.github.com/gists/" + info.gid, headers: { "Cookie": "" } }); }; var gg_parse_response = function (xhr, callback) { var json = xlinks_api.parse_json(xhr.responseText, null); if (!xlinks_api.is_object(json)) { callback("Invalid response"); } else { callback(null, [ gg_parse_response_single(json) ]); } }; var gg_parse_response_single = function (response) { var files = objectify(response.files), owner = response.owner, owner_id = -1, owner_name = "Anonymous", data, title, description, f, fdata; // Title and description title = response.description || ""; description = ""; for (f in files) { if (!title) title = f; fdata = objectify(files[f]); if (fdata.language) f += " (" + fdata.language + ")"; description = gg_get_content(f, fdata.content || ""); break; } if (!title) title = "Unnamed Github Gist"; // Owner if (xlinks_api.is_object(owner)) { owner_id = owner.id || 0; owner_name = owner.login || ""; } // Create data data = { type: "githubgist", subtype: "gist", id: response.id || "", title: title, description: description, date_created: parse_timestamp(response.created_at || ""), thumbnails: {}, channel: { id: owner_id, title: owner_name }, info: { privacy: (response.public ? "public" : "unlisted") }, stats: { comments: response.comments || 0, changes: (Array.isArray(response.history) ? Math.max(0, response.history.length - 1) : 0) } }; if (owner && (f = owner.avatar_url)) { data.thumbnails.medium = f; } // Done return data; }; var gg_get_content = function (title, content) { var ext = "...", max_len = 2048, max_line_len = 100, i, ii, line; content = content.replace(re_newlines, "\n").split("\n"); content.splice(10, content.length - 10); for (i = 0, ii = content.length; i < ii; ++i) { line = content[i]; if (line.length > max_line_len) content[i] = line.substr(0, max_line_len - ext.length) + ext; } content = content.join("\n"); if (content.length > max_len) content = content.substr(0, max_len); return title + "\n" + content; }; // Init xlinks_api.init({ namespace: "multimedia", name: "Multimedia", author: "dnsev-h", description: "Linkify and format various multimedia links", version: [1,0,0,8], registrations: 1, main: main_fn }, function (err) { if (err === null) { xlinks_api.insert_styles(".xl-mm-details-table{display:table;width:100%;height:200px}.xl-mm-details-table-no-height{height:auto}.xl-mm-details-table-row{display:table-row;height:100%}.xl-mm-details-table-cell-left{display:table-cell;height:100%;vertical-align:top}.xl-mm-details-table-cell-right{display:table-cell;width:100%;height:100%;vertical-align:top}.xl-mm-details-table-content{width:100%;height:100%;position:relative}.xl-mm-details-table-content-left{margin-right:.5em;width:200px;height:100%;overflow:hidden;position:relative}.xl-mm-details-table-content-inner{position:absolute;left:0;top:0;bottom:0;right:0;overflow:hidden}.xl-mm-details-table-no-height .xl-mm-details-table-content-inner{position:relative;overflow:visible}.xl-mm-details-thumbnail{width:100%;padding-top:56.25%;background-color:rgba(0,0,0,.03125);background-repeat:no-repeat;background-size:cover;background-position:50% 25%}.xl-mm-details-thumbnail.xl-theme-dark{background-color:rgba(255,255,255,.03125)}.xl-mm-details-thumbnail-large{padding-top:75%}.xl-mm-details-thumbnail-full{padding-top:0;height:100%}.xl-mm-details-thumbnail-row{width:100%;text-align:center;margin-top:.25em}.xl-mm-details-thumbnail-small{display:inline-block;vertical-align:top;width:33.333%}.xl-mm-details-thumbnail-small>.xl-mm-details-thumbnail{background-position:50% 50%}.xl-mm-details-stats{text-align:right;margin-bottom:.25em;font-size:.875em!important}.xl-mm-details-stats:after{content:\"\";display:block;clear:both}.xl-mm-details-stats-overlay{position:absolute;left:0;top:0;right:0}.xl-mm-details-stats-overlay{text-shadow:0 0 .125em #fff,0 .0625em .125em #fff}.xl-mm-details-stats-overlay.xl-theme-dark{text-shadow:0 0 .125em #000,0 .0625em .125em #000}.xl-mm-details-stat-views{float:left;white-space:nowrap}.xl-mm-details-stat{display:inline-block;vertical-align:middle;white-space:nowrap;padding-left:.5em}.xl-mm-details-rating{margin-bottom:.25em;width:100%;height:.25em;position:relative;background:#f43333}.xl-mm-details-rating-bar{width:0;height:100%;background:#167ac6}.xl-mm-details-duration{margin-top:.25em;font-size:.875em!important}.xl-mm-details-paragraph{font-size:.875em!important;line-height:1.05em;margin:0;padding:0}.xl-mm-details-paragraph+.xl-mm-details-paragraph{margin:.375em 0 0}.xl-mm-details-description-code{font-family:Courier,monospace!important}.xl-mm-details-description-code>.xl-mm-details-paragraph{line-height:1.1em;white-space:pre}.xl-mm-details-description-code>.xl-mm-details-paragraph+.xl-mm-details-paragraph{margin:0}.xl-site-tag-icon[data-xl-site-tag-icon=dailymotion]{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAJ1BMVEUAZN3////m8PxSmOj2+v7D2/cogOQPcuDa6fqkyPOKuPBBjuduqO1/V4AkAAAAtklEQVR4AcXTR4KAIAwFUNML3P+8Q5seXU35y/yngsr1nzFt9tBShki7ByrMgPQEAH4N2MgD0J4R2bXVQLsgAzBG9wpoIuwwcgFs9O8pQF89u4hzBVTWMJsqBRaAcPW2cBRryDkTPdq/AZsXcd6/SQsowZc7hN1/iz4AOG3d8TtY62Ihm70X27TgKTwyw8s3SXvMI1CCc+OtKjCEvF4rXoCRluKILr2Fu0srTwtRG+9b24hdf5IXMmkFt/dqERsAAAAASUVORK5CYII=)}.xl-site-tag-icon[data-xl-site-tag-icon=githubgist]{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAA0lBMVEUAAAAUEhIUExMODg4UExMUExMTExMTExMHBwcUEhIREBAQEBAAAAAODg7///8UExMUExP///8TExMTExP///8UERESEhISEhITERETDg7///8UDg4PDw////8MDAwAAAAAAAAXFRX///+hoaEfHh77+/teXV3x8fFLSko9PDzv7+80MzMvLy/9/f3p6en4+PjNzc2Pj49iYGDY2NjPz8/ExMSpqamWlZV6eno5ODinp6crKirJyMj3+PglJCTf39+8vLykpKSenJx+fn5UU1NFRET4VMT5AAAAIXRSTlMA8t0y/farnQ3RZB4YEfPr1tLEt6Skl35zZ2NZQhQUBAHVE82ZAAABYklEQVR4AW3RBYLjMAwFUIV5mPkraco0zMv3v9JGDsNruLIl2VS78Y89DdC8Y/+G+gzbRMW0DWpzHRMtpuO2huvo0RuTXGkYoF1V4zUM0oo5Ipn/LU3QkKRvkiUi4SDzzOv4A8l0Mpkm+IjX/IyMoxJAPDLz+GHJmeXDmJkfISSJDbHhjg2ETRSaEO/c8Q5hhnQOsZhxx2wBcV5kiLknLnLsQYy5ZwyxR5bKwANUDosg5jxgDkEQCQ9IIEiDWHHPCkIjr7MMnYXwyIGYjrhjNIVw6BLKK3e8Qrkk14Jyv+KG1T0UyyU6Af48TSQk3qhEo8/fKQonRBRYMF/W/5BJ8+2u/oYVUOYUwOePhVxZeULplMTtAfCXZ/H256ixAuLglpRQB16aO42cHlIh0IHJ9tf2qxWgB1SJDiFmzYDDiBru/N12wK5/R23u2fy7SPH1PT9zqe/64mh/h3ln/+jimir/AemDbBo1GYEuAAAAAElFTkSuQmCC)}.xl-site-tag-icon[data-xl-site-tag-icon=liveleak]{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAC/VBMVEUAAAASAAA3JSVOFhZgKytmKyt4TU2jV1esT0+gMjKdLi6wHx+wIyOyISG3IiK2GxuqIiKmJiapKyudJSWgIiKkHx+fHByaHR2XJCSSIyOMJCSJIyOGJCSEJCSEIiKBIiJ9IiJ4JSV2KChyJCRxIiJvICBtHBxoGxtkFRVjERFhEBBgDg5iDAxlDg5sDw90Dg57Dg5/Dw+GDg6PDQ2XDg6eDg6jEhKoFhavEBCmCgqoBQWrBgarAACpAACpAACnAACmAACmAACjAACiAACgAACbAACYAACWAACWAACTAACTAACQAACPAACNAACLAACIAQGHAACEAACDAACCAACAAAB+AAB9AAB6AAB3AAB2AAB0AAB1AABvAABvAABuAABrAQFoAABiAABeAwNeBARcAgJbAQFZAQFYAQFWAABUAABSAABPAABNAQFYAABbBgZcBwdgBgZbCgpnGxt2HR18GxuCGxuKGxuPHByoEBCQAgJ4MDB5OjqAPDyFPj5+Q0OHT0+LVFSQUVGYTk6bUVGlU1OpVVWxVVW0WFi4Vla8UFC/T0+2RUW+Ojq8XV27ZGS2Y2OrZGSgYmKeYWGbX1+XY2OTZGSTZmaTYGCUXV2PXV2YbGyacHCdc3OcdHSeeHihfX2riYmujIyzlZW4nZ23oaG6o6O6p6e/rKzGsrLBtrbKt7fOubnKu7vPwcHTwcHWwsLaxMTdw8PhyMjmzs7rzc3t09Pf2dna2trd3Nzd3d3b4ODh5OTi4+Pj4uLn4eHj3d3k5ubm5+fo6eno6+vp7Ozn7e3q7u7q8PDq8fHr8PDv7u7w8fHw9PTy9PT28/P38fH27+/69PT39/f7+fn8+/v8/f39/v7+/f3////5/v72/v7y/f3u/Pzr+Pjt6+vv6+v15+fw5OTk6enl6+vU2dnQ1tbZ1tba1dXZ1NTUzs7Ty8vRy8vRycnKysrMz8/durriurrXr6/RqanOpaXLpKTIoKDDoKC+n5/co6PBiYm/h4e9gYHMdHTDZWWRSUmuoMMmAAAA/3RSTlMFGShArsG0ucjFw/L8/v7//f78/f3+/f39/f39+/39/f39/vv9/f/+/f/9/P3+///////////7/f7+/f7+/f/9/P/9/P38/P3///39//z9/f39/vz8/f7+/Pz8//z7//3+/f3///z9/P39+/78//38/vH0/v39/f3w+/39+//+/f39/f39/fz9/vz8/f3+/vz9/f38/f3+/f/8/f7//f37/fv8/f78/fz9/P39/vz9/fz9/f/9/fz8//39/P/9/f39/v39/P39/Pz//f37/f////7/+/39/f3+/vz8/P39//79/Pz7/P3//P79/f38/f38/fz9/Pv9/Pz//f38/f2K/3b2AAACWklEQVR4AU3RA8x0ZxDF8f+ZmbtvbRtRbdu2G9RRGath4xqxG9WOG9a2bVv73d3nOU0+/wZxMlAu1G5hGcjGSmTp1XHMVa79JwMUCKRAciSyJNb9SVp7NzoZHbJ1kzBjqUkjTtFBgwFLRpjxzOsMFsAtjy34lcICpxHZmJ174fefoUnrkLtOnjBFCIjeCQXTy78bN4BoqMdaB9y7OtHzvxOO/4fICpSf9nn7dLtPbZuuZ94JB3rvivPOFCEUg9DaJxx6/IiNTYmYK575t1KSENB107oygABVUXZDAcKAcGIkI0tDYTwBAcgQJm4/ss8LBiooIqwxbaClJBGs/fP6P8PMPRwO+h9EpJR7Jog+CNgAbNLlHDzr1UnrnbxYxgHoJxBDKhDuRkiKRHzikDFgExShtqUIugKBIPm6vhIaLFU55A9cnQRjCRLb2/K5EVGWsCUAAiGnDJ8DligiO65mgDSAhKIakEFReMfniR7GAdBLbT7rEJARJqzrM4bICiz9xyBv24GtrFQ5YVVCAE4AA4bZQFdQSHD3/ob2y5+/+oED93whvl3lS8v6WZIKZfcpAk+ffmvhsd1/Wl38/pqjB3utlVJZq378SAeT471HB+317TedyxBe1VSU19rvq6/W5C9hHVoINtwNFnvnd6yCvtaff8oGr/UvbJWZHaKHW4+g/O/kv8ELYw/omc1v7+z+poGI6QlTFUIyKhNjP+zDOlawR6DGk++PUONkXouCHj3mAb/e8G6y1L1HM7lQw1UNWSxmMZVhMgLyKq67pNx2YsssJrOMJZ120/g/MrgnATdvS1gAAAAASUVORK5CYII=)}.xl-site-tag-icon[data-xl-site-tag-icon=livestream]{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAABnUlEQVR4Ae2TNVxbURSH39pMdXfvUp3bsW64u/uEw4yMaLGJjNmyMuPu7q5xtwM3nnPjaLm/j4Sc93/3u8poHx8uhy/QPDxczgWOBaoHh8sRC6J/aFcXtbxd7da6Zm5KPdClaeGq/5VDXfH8gpsCxT0TvLvMStQvLdZADBrIuIr1b3AIOWzcoVS64UBKq29uqqUS3LG1DmtlTCrzkogY/qEKZHesEd1kNitLtZSmKM4wJjefsMhQYLfgJwpetTMJRXebeVI6PgxFjVyuuOeKACax3VRJm4T03W1TksvRFz8+xwWSWzgrHx7AuHBBWqgxxmuuJ0WRzyesH7oAJgEv4wJ2vTHGry4nRWGcL13g4iQUs1OmGZQVk+JeShguEN2kskc/TrAN+kxhFqnwcpKxToiAzsIDFjknVk2cHEwC0K8DgeC6AybfPRT3dloJ4LaTp+YC9HXHgu1rzNgVptcSqJCnsPREAOdV+OoaLvAEq4OACHhXPQLmN+X/BdYQbj6YbAOeCoDVgxXrubQPKjhcmL3Lh8sZEOxcOlTOgEAHhgJO2pZstL8AAAAASUVORK5CYII=)}.xl-site-tag-icon[data-xl-site-tag-icon=nicovideo]{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAAAAABWESUoAAAAAnRSTlMAAHaTzTgAAABJSURBVHgBY6AWMAECdD5pChCCCJp0BTglCduNBxCr4D8OQF8FQEyBAlySBBXQyJug4KVEASyKSFFAuS8oT1EQgJFQEaKEFVAOAIZtkrBYuXPiAAAAAElFTkSuQmCC)}.xl-site-tag-icon[data-xl-site-tag-icon=soundcloud]{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAABUElEQVR4Ae2RA2xEQRCGN7Ybu7bjCy+qraC2HbuKzcaNjdoKirC2Eddup3OYu83bvb1X88/3/vt33/Sftseg8WP5DQvqZS/qySkI94RqhhZ8JJ+xoPpD+YwFFR8Lg5IX0tcG93dwegzzA9CR63SeQcELwXZeG9NQ66KYN39kIRQKKIj3R6umIGp5lIYlPfgooFILKHSp2nMdNTDIUIJCxzW2LNX8gKMGBskCB2v2jBJcrpsr0/fRHKVpYxAnAICOaygLrhTu4NsYRHJgKTpK7WqtzPKdDIwcKD2u1s0V38nAIFtgcOBGfX+BrdDA8OE4WKM6pavVEMd3MggX2FlV+WQXWHR/Z+fmCsHfHds1bdwhmByho04UP4uHj4WBvwxfcoSOr4NadOJNTsHpDANPOniSa6BXhDhPQXZP6cMwf7ghFDwpKO8VaObx+Vj+FzjlGagPLrH/Q5rsAAAAAElFTkSuQmCC)}.xl-site-tag-icon[data-xl-site-tag-icon=twitter]{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAz1BMVEUAAABSo+BRod1Rod1So+BUqutVrO5UqelSpOJSpOJRod1Rot9SpONRo+BTp+dUrO1SpOJUq+1SpeNRot9TqupRo+BSpuVTpuVSpeNUq+tSo+FVrO1Ro+BRot9Ro+BUquxSpuRRo+FUqelSpOFSpuVTqOhSpOFTpuVUqelSpeNSpuVTqehSpOFRo+FSpOJVrO1VrO1Uq+xVrO1Vq+1UqelRot9Uq+xVq+1Uq+xUq+xUqepUqelTpuRVrO5Uq+1UqutUqepTpeRTp+ZSpONTqOjeC1grAAAAPXRSTlMADAJuB/r1p3NQNSQV+fnr49PFooRjKhkE/Pjv4NjOvq+mkHpnX1VMRD86LSAQ8+De2tjJxcC3sZ2FfFRG+ptkjwAAASNJREFUeAHF0VOiI0EYBtCv7di27+COUezsf0txqit6nTmPhZ/4L2yrlCS9sQHADHFQNaGLukJyvhWFMLK6EwBxUtZfxAXJKCGEbdNUlI43gRRDG0p5S8kJZby/8pdAlTPZ2+DiLUeJ4sihCQSMUPnBt3Ey5yRDvQaAepMQykXHMk4V6w+8c3EDcqpJtAZWaASMKLSCk02eHDEuRZrqJdAxjmb+n7yqmzOSYVMcBeLU2BNspRp7zjFwMiIvfMWZUSRPUV+N//vzDHW139rkE3nk4apReNZHM4Iy4+TRCJrf5MFnA7r5fSfuGndq/Zv7Be6EJb2O9t19zfqW6o0U9fhRudNKheTZvVuxceOv57DsOv+rjgfx9OeXjy5pv/+oLBv45/bAyHEKqqX6ogAAAABJRU5ErkJggg==)}.xl-site-tag-icon[data-xl-site-tag-icon=vimeo]{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAVFBMVEUqt+cqtucpt+cnt+QAAAAmsuUps+MquOf///9Mxuw0vekquOjy+/500/Hj9vye4fXI7vpnzu+Q2/PV8vuB2PKr5fZnz+9Zye1AwOq56veQ3PRZyu1aWjYSAAAAB3RSTlPwrfMnACglsLAVVgAAAUJJREFUeF59UkliAjEMc4YpzjYbe9v//7Ne5HAqgoORhSwnofmcqBlYPqgEWlA6z3QisN5mkw2ttBPa0Q0xkAgGYMN9CCZST1BmDg3HMIJxRICovVypAvD1Z98Xt62XnnO+V9MSIpdrFhxKVSkVm+9hCfjZjftW9Z4dnRkCbksGhBl1tqCkqtvgSms2AGo2h1bywEPUz9CzwLfYtgryYsvuCGQOcQTrILl54sV4P0mpNiNXJQ+UUjO2kBpBqnBXGMABhwzfo7HHueKKdISnuHsILj0WDgEewoI9vH+LmyOOGa8M4MyZETLeimUD1qptv814oLYo0Mv7bZApDY8hKEqyOTBBqIoV/z8sNWjCD6V+fX6JoZaSYhb7U+iXKgwixha4DpiaHqJ3yEHjC0IdqH3EROmzINHXRwtpz6c0/eufTvMfFlgm5bbW8/0AAAAASUVORK5CYII=)}.xl-site-tag-icon[data-xl-site-tag-icon=vine]{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAnFBMVEX///////8AAAD///8Avo/7/v3x+/ggxp0Bvo/u+vfh9/HV9OxR0rNEz6zA7+N93sYzy6UOwZXq+fbZ9e687uKt6tsFv5Gw69yQ4s4QwpYJwJJK0a/G8OZs2r5Z1Lb4/fsvyqPn+PSa5dI4zKjz/Prj+PPb9u/Q8+qD38h128Jx28Fm2Lte1rg7zagbxJopyKEVw5ih59WG4Mlg1rk6KsbXAAAAA3RSTlOU7ADyp2gSAAABVElEQVR4XoXT1ZqDMBAFYLongrrU3X3l/d9tZ6ALdKmcK5j8IUPyxfgwOngeGjbwMgbNf5lO+wN2oM08XKibaIH0UxSZ2Y+B3Re3eDWQW8dxVAlcIUyRM4jSClj83gNHRfS4h08k/66A0jRtDI4/oKkx1JDcuAIrr1oyIxDGmP4QOFZAHrjrKYMugVmM1YTAoQL4IqAdBiMC/TXmZ+qhVwPuKcoYKP7LFBtNzq9BcqXyDhxuJ5TouueJrMHUJeAqBiNhCnGi0txCDRBQVW/AYWteuwCaIOWly6KlRZF+fmoAyU0MUcRZmiU5NgD2XElKYbG+28m/43BLMOID3/mJ1QSYsNgykPy4nDNtApvBkkHGJ7nAf4CxKHfX1tyvbIOEBy4OQoLRGm2AnknCmwnKCI+AHIpbFngIEIcFMAM8AbA9BoF8CqCCwSXDfd5evbeX9+31/wWCTBqxjYyvJAAAAABJRU5ErkJggg==)}.xl-site-tag-icon[data-xl-site-tag-icon=youtube]{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACcElEQVR4Ae2WNXAbQRSGN0y9WSc+jXxyuAkzMzNzmdRh6N1Xbt2FmjAzx8zMzDfSyZv3e9ZMq5DxzXySTnvv/36zWccZndHJ96jT8qNcJsJdEOWak6epC+h5BbFesJ3YLdjXhd2C7R3uXyEy5iAT2XD0KM7zuE7ke1yPiUyiiCgnqvM1tRHQa6+A94MXdNirFllFIvsxXG1F4u228bmaehU350aq/H8AF5xwM3yKciLVQhz8T+CEm2W5nbcy3U4+EMDNMtzOhwNVAG6W5nLEEXyAiGOpLkeB7EKy085zdmzzFl++1JA2d7YX139SAG6WrDpqESRDks3C886dNQxd99d9/arnXjjfiCJ4H+eBAjdLctp9BJdCFGj2+5s5DYqUP3umZ54+7U2K8vhwLpsl8LEEh53LEmdW2gt0GG9lpVF6505T2qGDTYmaZgSSyeLsNi5JrwUweK+6sNDIjo6ujdc0XTaT/bBauSwogE939wLisxAT05i0Zo3+w2b3S2YGVuBLRPcCEJfHxjambNnWBPFXi3weYN8sVgNLMnQsYNTVtXzdIUYGznBPIGCPfTZbaz8rFi4DJKn7D+qV9+834PmLQ4VYnAcO3OyTYskPZAlS8DH8D8QCuNkHkyWO4ANEHHsTpjx8F27mAwHc7G24+eZAFYCbvQwxzX8dphQQ/D9TAHfLv2QvQ5XLdNFE8P9EE5xwM8yLENOU58Gm/S9ClDv0OoUoJMqIaqIeYImeDQC6hnY5awJit1pkFSIbX3fyHIeTdR5RJMQUSqjELHyKqNhSYjWxgdgq2IXCXdgFxPkGsbMUGcgSmaFd/y0fndH5Bf59J1OoDnY6AAAAAElFTkSuQmCC)}"); xlinks_api.register({ settings: { sites: [ // namespace [ "youtube", true, "youtube.com", "Enable link processing for YouTube" ], [ "vimeo", true, "vimeo.com", "Enable link processing for Vimeo" ], [ "soundcloud", true, "soundcloud.com", "Enable link processing for SoundCloud" ], [ "dailymotion", true, "dailymotion.com", "Enable link processing for DailyMotion" ], [ "liveleak", true, "liveleak.com", "Enable link processing for LiveLeak" ], [ "vine", true, "vine.co", "Enable link processing for Vine" ], [ "twitter", true, "twitter.com", "Enable link processing for Twitter" ], [ "livestream", true, "livestream.com", "Enable link processing for Livestream" ], [ "nicovideo", true, "nicovideo.jp", "Enable link processing for Niconico" ], [ "githubgist", true, "gist.github.com", "Enable link processing for Github Gists" ], ], multimedia: [ [ "iconify", true, "Icon site tags", "Use site-specific icons instead of [Site] tags" ], [ "youtube_api_key", "", "Custom YouTube API key", "Use a custom API key for the YouTube Data API v3 (leave blank for default)", { type: "textbox" } ] ], }, request_apis: [ // Youtube { group: "youtube", namespace: "youtube", type: "video", count: 50, concurrent: 1, delay_okay: 100, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: yt_setup_xhr, parse_response: yt_parse_response } }, { group: "youtube", namespace: "youtube", type: "channel", count: 50, concurrent: 1, delay_okay: 100, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: yt_channel_setup_xhr, parse_response: yt_channel_parse_response } }, { group: "youtube", namespace: "youtube", type: "user", count: 1, concurrent: 10, delay_okay: 100, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: yt_channel_setup_xhr, parse_response: yt_channel_parse_response } }, // Vimeo { group: "vimeo", namespace: "vimeo", type: "video", count: 1, concurrent: 1, delay_okay: 100, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: vimeo_setup_xhr, parse_response: vimeo_parse_response } }, { group: "vimeo", namespace: "vimeo", type: "user", count: 1, concurrent: 1, delay_okay: 100, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: vimeo_user_setup_xhr, parse_response: vimeo_user_parse_response } }, { group: "vimeo", namespace: "vimeo", type: "channel", count: 1, concurrent: 1, delay_okay: 100, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: vimeo_channel_setup_xhr, parse_response: vimeo_channel_parse_response } }, // SoundCloud { group: "soundcloud", namespace: "soundcloud", type: "generic", count: 1, concurrent: 1, delay_okay: 100, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: sc_setup_xhr, parse_response: sc_parse_response } }, // DailyMotion { group: "dailymotion", namespace: "dailymotion", type: "video", count: 1, concurrent: 1, delay_okay: 100, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: dm_setup_xhr, parse_response: dm_parse_response } }, { group: "dailymotion", namespace: "dailymotion", type: "user", count: 1, concurrent: 1, delay_okay: 100, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: dm_user_setup_xhr, parse_response: dm_user_parse_response } }, // LiveLeak { group: "liveleak", namespace: "liveleak", type: "video", count: 1, concurrent: 1, delay_okay: 200, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: ll_setup_xhr, parse_response: ll_parse_response } }, // Vine { group: "vine", namespace: "vine", type: "generic", count: 1, concurrent: 1, delay_okay: 100, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: vine_setup_xhr, parse_response: vine_parse_response } }, // Twitter { group: "twitter", namespace: "twitter", type: "generic", count: 1, concurrent: 1, delay_okay: 100, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: twitter_setup_xhr, parse_response: twitter_parse_response } }, // Livestream { group: "livestream", namespace: "livestream", type: "video", count: 1, concurrent: 1, delay_okay: 200, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: ls_setup_xhr, parse_response: ls_parse_response } }, { group: "livestream", namespace: "livestream", type: "channel", count: 1, concurrent: 1, delay_okay: 200, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: ls_channel_setup_xhr, parse_response: ls_channel_parse_response } }, // Nicovideo { group: "nicovideo", namespace: "nicovideo", type: "video", count: 1, concurrent: 1, delay_okay: 200, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: nv_setup_xhr, parse_response: nv_parse_response } }, // Github Gist { group: "githubgist", namespace: "githubgist", type: "gist", count: 1, concurrent: 1, delay_okay: 200, delay_error: 5000, functions: { get_data: generic_get_data, set_data: generic_set_data, setup_xhr: gg_setup_xhr, parse_response: gg_parse_response } }, ], linkifiers: [{ regex: /(https?:\/*)?[\w\-\.]*(?:youtube\.com?(?:\.[a-z]{2})?|youtu\.be|y2u\.be|vimeo\.com|soundcloud\.com|dailymotion\.com|liveleak\.com|vine\.co|twitter\.com|livestream\.com|nicovideo\.jp|gist\.github\.com)(?:\/[^<>()\s\'\"]*)?/i, prefix_group: 1, prefix: "http://", }], commands: [ { url_info: yt_url_get_info, to_data: yt_url_info_to_data, actions: yt_create_actions, details: generic_create_details }, { url_info: vimeo_url_get_info, to_data: vimeo_url_info_to_data, details: generic_create_details }, { url_info: sc_url_get_info, to_data: sc_url_info_to_data, details: generic_create_details }, { url_info: dm_url_get_info, to_data: dm_url_info_to_data, details: generic_create_details }, { url_info: ll_url_get_info, to_data: ll_url_info_to_data, details: generic_create_details }, { url_info: vine_url_get_info, to_data: vine_url_info_to_data, details: generic_create_details }, { url_info: twitter_url_get_info, to_data: twitter_url_info_to_data, details: generic_create_details }, { url_info: ls_url_get_info, to_data: ls_url_info_to_data, details: generic_create_details }, { url_info: nv_url_get_info, to_data: nv_url_info_to_data, details: generic_create_details }, { url_info: gg_url_get_info, to_data: gg_url_info_to_data, details: generic_create_details } ] }, function (err) { if (err === null) { var s = xlinks_api.config.multimedia.youtube_api_key; if (s.length === yt_api_key.length) { yt_api_key = s; } use_icons = xlinks_api.config.multimedia.iconify; } }); } }); }; main(xlinks_api); })();