Embedding = init: -> return unless Conf['Embedding'] or Conf['Link Title'] @types = {} @types[type.key] = type for type in @ordered_types if Conf['Floating Embeds'] @dialog = UI.dialog 'embedding', 'top: 50px; right: 0px;', <%= importHTML('Linkification/Embed') %> @media = $ '#media-embed', @dialog $.one d, '4chanXInitFinished', @ready if Conf['Link Title'] $.on d, '4chanXInitFinished PostsInserted', -> for key, service of Embedding.types when service.title?.batchSize Embedding.flushTitles service.title return events: (post) -> return unless Conf['Embedding'] i = 0 items = $$ '.embedder', post.nodes.comment while el = items[i++] $.on el, 'click', Embedding.cb.toggle Embedding.cb.toggle.call el if $.hasClass el, 'embedded' return process: (link, post) -> return unless Conf['Embedding'] or Conf['Link Title'] return if $.x 'ancestor::pre', link if data = Embedding.services link data.post = post Embedding.embed data if Conf['Embedding'] Embedding.title data if Conf['Link Title'] services: (link) -> {href} = link for type in Embedding.ordered_types when match = type.regExp.exec href return if type.dummy return {key: type.key, uid: match[1], options: match[2], link} return embed: (data) -> {key, uid, options, link, post} = data {href} = link return if Embedding.types[key].httpOnly and location.protocol isnt 'http:' $.addClass link, key.toLowerCase() embed = $.el 'a', className: 'embedder' href: 'javascript:;' textContent: '(embed)' embed.dataset[name] = value for name, value of {key, uid, options, href} $.on embed, 'click', Embedding.cb.toggle $.after link, [$.tn(' '), embed] if Conf['Auto-embed'] and !Conf['Floating Embeds'] and !post.isFetchedQuote and key isnt 'TwitchTV' # XXX https://github.com/justintv/Twitch-API/issues/289 $.asap (-> doc.contains embed), -> Embedding.cb.toggle.call embed ready: -> $.addClass Embedding.dialog, 'empty' $.on $('.close', Embedding.dialog), 'click', Embedding.closeFloat $.on $('.move', Embedding.dialog), 'mousedown', Embedding.dragEmbed $.on $('.jump', Embedding.dialog), 'click', -> Header.scrollTo Embedding.lastEmbed if doc.contains Embedding.lastEmbed $.add d.body, Embedding.dialog closeFloat: -> delete Embedding.lastEmbed $.addClass Embedding.dialog, 'empty' $.replace Embedding.media.firstChild, $.el 'div' dragEmbed: -> # only webkit can handle a blocking div {style} = Embedding.media if Embedding.dragEmbed.mouseup $.off d, 'mouseup', Embedding.dragEmbed Embedding.dragEmbed.mouseup = false style.visibility = '' return $.on d, 'mouseup', Embedding.dragEmbed Embedding.dragEmbed.mouseup = true style.visibility = 'hidden' title: (data) -> {key, uid, options, link, post} = data return unless service = Embedding.types[key].title $.addClass link, key.toLowerCase() if service.batchSize (service.queue or= []).push data if service.queue.length >= service.batchSize Embedding.flushTitles service else unless $.cache service.api(uid), (-> Embedding.cb.title @, data), {responseType: 'json'} $.extend link, <%= html('[${key}] Title Link Blocked (are you using NoScript?)') %> flushTitles: (service) -> {queue} = service return unless queue?.length service.queue = [] cb = -> Embedding.cb.title @, data for data in queue return unless $.cache service.api(data.uid for data in queue), cb, {responseType: 'json'} for data in queue $.extend data.link, <%= html('[${data.key}] Title Link Blocked (are you using NoScript?)') %> return cb: toggle: (e) -> e?.preventDefault() if Conf['Floating Embeds'] return unless div = Embedding.media.firstChild $.replace div, Embedding.cb.embed @ Embedding.lastEmbed = Get.postFromNode(@).nodes.root $.rmClass Embedding.dialog, 'empty' return if $.hasClass @, "embedded" $.rm @nextElementSibling @textContent = '(embed)' else $.after @, Embedding.cb.embed @ @textContent = '(unembed)' $.toggleClass @, 'embedded' embed: (a) -> # We create an element to embed container = $.el 'div' $.add container, el = (type = Embedding.types[a.dataset.key]).el a # Set style values. el.style.cssText = if type.style? type.style else "border:0;width:640px;height:390px" return container title: (req, data) -> {key, uid, options, link, post} = data {status} = req service = Embedding.types[key].title text = "[#{key}] #{switch status when 200, 304 service.text req.response, uid when 404 "Not Found" when 403 "Forbidden or Private" else "#{status}'d" }" link.dataset.original = link.textContent link.textContent = text for post2 in post.clones for link2 in $$ 'a.linkify', post2.nodes.comment when link2.href is link.href link2.dataset.original ?= link2.textContent link2.textContent = text return ordered_types: [ key: 'audio' regExp: /\.(?:mp3|ogg|wav)(?:\?|$)/i style: '' el: (a) -> $.el 'audio', controls: true preload: 'auto' src: a.dataset.href , key: 'Gist' regExp: /^\w+:\/\/gist\.github\.com\/(?:[\w\-]+\/)?(\w+)/ el: (a) -> el = $.el 'iframe' el.setAttribute 'sandbox', 'allow-scripts' content = <%= html('${a.dataset.uid}') %> el.src = E.url content el title: api: (uid) -> "https://api.github.com/gists/#{uid}" text: ({files}) -> return file for file of files when files.hasOwnProperty file , key: 'image' regExp: /\.(?:gif|png|jpg|jpeg|bmp)(?:\?|$)/i style: '' el: (a) -> $.el 'div', <%= html('') %> , key: 'InstallGentoo' regExp: /^\w+:\/\/paste\.installgentoo\.com\/view\/(?:raw\/|download\/|embed\/)?(\w+)/ el: (a) -> $.el 'iframe', src: "https://paste.installgentoo.com/view/embed/#{a.dataset.uid}" , key: 'Twitter' regExp: /^\w+:\/\/(?:www\.)?twitter\.com\/(\w+\/status\/\d+)/ el: (a) -> $.el 'iframe', src: "https://twitframe.com/show?url=https://twitter.com/#{a.dataset.uid}" , key: 'LiveLeak' regExp: /^\w+:\/\/(?:\w+\.)?liveleak\.com\/.*\?.*i=(\w+)/ httpOnly: true style: 'border: none; width: 640px; height: 360px;' el: (a) -> el = $.el 'iframe', src: "http://www.liveleak.com/ll_embed?i=#{a.dataset.uid}", el.setAttribute "allowfullscreen", "true" el , key: 'Pastebin' regExp: /^\w+:\/\/(?:\w+\.)?pastebin\.com\/(?!u\/)(?:[\w\.]+\?i\=)?(\w+)/ httpOnly: true el: (a) -> div = $.el 'iframe', src: "http://pastebin.com/embed_iframe.php?i=#{a.dataset.uid}" , key: 'Gfycat' regExp: /^\w+:\/\/(?:www\.)?gfycat\.com\/(?:iframe\/)?(\w+)/ el: (a) -> div = $.el 'iframe', src: "//gfycat.com/iframe/#{a.dataset.uid}" , key: 'SoundCloud' regExp: /^\w+:\/\/(?:www\.)?(?:soundcloud\.com\/|snd\.sc\/)([\w\-\/]+)/ style: 'border: 0; width: 500px; height: 400px;' el: (a) -> $.el 'iframe', src: "https://w.soundcloud.com/player/?visual=true&show_comments=false&url=https%3A%2F%2Fsoundcloud.com%2F#{encodeURIComponent a.dataset.uid}" title: api: (uid) -> "//soundcloud.com/oembed?format=json&url=https%3A%2F%2Fsoundcloud.com%2F#{encodeURIComponent uid}" text: (_) -> _.title , key: 'StrawPoll' regExp: /^\w+:\/\/(?:www\.)?strawpoll\.me\/(?:embed_\d+\/)?(\d+(?:\/r)?)/ style: 'border: 0; width: 600px; height: 406px;' el: (a) -> $.el 'iframe', src: "//strawpoll.me/embed_1/#{a.dataset.uid}" , key: 'TwitchTV' regExp: /^\w+:\/\/(?:www\.)?twitch\.tv\/(\w[^#\&\?]*)/ style: "border: none; width: 620px; height: 378px;" el: (a) -> if result = /(\w+)\/([bcv])\/(\d+)/i.exec a.dataset.uid [_, channel, type, id] = result idprefix = if type is 'b' then 'a' else type flashvars = "channel=#{channel}&start_volume=25&auto_play=false&videoId=#{idprefix}#{id}" if start = a.dataset.href.match /\bt=(\w+)/ seconds = 0 for part in start[1].match /\d+[hms]/g seconds += +part[...-1] * {'h': 3600, 'm': 60, 's': 1}[part[-1..]] flashvars += "&initial_time=#{seconds}" else channel = (/(\w+)/.exec a.dataset.uid)[0] flashvars = "channel=#{channel}&start_volume=25&auto_play=false" obj = $.el 'object', data: '//www-cdn.jtvnw.net/swflibs/TwitchPlayer.swf' $.extend obj, <%= html('') %> obj.children[1].value = flashvars obj , key: 'Vocaroo' regExp: /^\w+:\/\/(?:www\.)?vocaroo\.com\/i\/(\w+)/ style: '' el: (a) -> el = $.el 'audio', controls: true preload: 'auto' type = if el.canPlayType 'audio/webm' then 'webm' else 'mp3' el.src = "http://vocaroo.com/media_command.php?media=#{a.dataset.uid}&command=download_#{type}" el , key: 'Vimeo' regExp: /^\w+:\/\/(?:www\.)?vimeo\.com\/(\d+)/ el: (a) -> $.el 'iframe', src: "//player.vimeo.com/video/#{a.dataset.uid}?wmode=opaque" title: api: (uid) -> "https://vimeo.com/api/oembed.json?url=https://vimeo.com/#{uid}" text: (_) -> _.title , key: 'Vine' regExp: /^\w+:\/\/(?:www\.)?vine\.co\/v\/(\w+)/ style: 'border: none; width: 500px; height: 500px;' el: (a) -> $.el 'iframe', src: "https://vine.co/v/#{a.dataset.uid}/card" , key: 'YouTube' regExp: /^\w+:\/\/(?:youtu.be\/|[\w.]*youtube[\w.]*\/.*(?:v=|\bembed\/|\bv\/))([\w\-]{11})(.*)/ el: (a) -> start = a.dataset.options.match /\b(?:star)?t\=(\w+)/ start = start[1] if start if start and !/^\d+$/.test start start += ' 0h0m0s' start = 3600 * start.match(/(\d+)h/)[1] + 60 * start.match(/(\d+)m/)[1] + 1 * start.match(/(\d+)s/)[1] el = $.el 'iframe', src: "//www.youtube.com/embed/#{a.dataset.uid}?wmode=opaque#{if start then '&start=' + start else ''}" el.setAttribute "allowfullscreen", "true" el title: batchSize: 50 api: (uids) -> ids = encodeURIComponent uids.join(',') key = '<%= meta.youtubeAPIKey %>' "https://www.googleapis.com/youtube/v3/videos?part=snippet&id=#{ids}&fields=items%28id%2Csnippet%28title%29%29&key=#{key}" text: (data, uid) -> for item in data.items when item.id is uid return item.snippet.title 'Not Found' , key: 'Loopvid' regExp: /^\w+:\/\/(?:www\.)?loopvid.appspot.com\/#?((?:pf|kd|lv|gd|gh|db|dx|nn|cp|wu|ig|ky|mf|pc|gc)\/[\w\-\/]+(,[\w\-\/]+)*|fc\/\w+\/\d+)/ style: 'max-width: 80vw; max-height: 80vh;' el: (a) -> el = $.el 'video', controls: true preload: 'auto' loop: true [_, host, names] = a.dataset.uid.match /(\w+)\/(.*)/ types = switch host when 'gd', 'wu', 'fc' then [''] when 'gc' then ['giant', 'fat', 'zippy'] else ['.webm', '.mp4'] for name in names.split ',' for type in types base = "#{name}#{type}" url = switch host # list from src/common.py at http://loopvid.appspot.com/source.html when 'pf' then "https://web.archive.org/web/2/http://a.pomf.se/#{base}" when 'kd' then "http://kastden.org/loopvid/#{base}" when 'lv' then "http://kastden.org/_loopvid_media/lv/#{base}" when 'gd' then "https://docs.google.com/uc?export=download&id=#{base}" when 'gh' then "https://googledrive.com/host/#{base}" when 'db' then "https://dl.dropboxusercontent.com/u/#{base}" when 'dx' then "https://dl.dropboxusercontent.com/#{base}" when 'nn' then "http://naenara.eu/loopvids/#{base}" when 'cp' then "https://copy.com/#{base}" when 'wu' then "http://webmup.com/#{base}/vid.webm" when 'ig' then "https://i.imgur.com/#{base}" when 'ky' then "https://kiyo.me/#{base}" when 'mf' then "https://d.maxfile.ro/#{base}" when 'pc' then "http://a.pomf.cat/#{base}" when 'fc' then "//i.4cdn.org/#{base}.webm" when 'gc' then "https://#{type}.gfycat.com/#{name}.webm" $.add el, $.el 'source', src: url el , key: 'Clyp' regExp: /^\w+:\/\/(?:www\.)?clyp\.it\/(\w+)/ style: '' el: (a) -> el = $.el 'audio', controls: true preload: 'auto' type = if el.canPlayType 'audio/ogg' then 'ogg' else 'mp3' el.src = "https://clyp.it/#{a.dataset.uid}.#{type}" el , # dummy entries: not implemented but included to prevent them being wrongly embedded as a subsequent type key: 'Loopvid-dummy' regExp: /^\w+:\/\/(?:www\.)?loopvid.appspot.com\// dummy: true , key: 'MediaFire-dummy' regExp: /^\w+:\/\/(?:www\.)?mediafire.com\// dummy: true , key: 'video' regExp: /\.(?:ogv|webm|mp4)(?:\?|$)/i style: 'max-width: 80vw; max-height: 80vh;' el: (a) -> $.el 'video', controls: true preload: 'auto' src: a.dataset.href loop: /^https?:\/\/i\.4cdn\.org\//.test a.dataset.href ]