523 lines
20 KiB
CoffeeScript
523 lines
20 KiB
CoffeeScript
Embedding =
|
|
init: ->
|
|
return unless g.VIEW in ['index', 'thread', 'archive'] and Conf['Linkify'] and (Conf['Embedding'] or Conf['Link Title'] or Conf['Cover Preview'])
|
|
@types = $.dict()
|
|
@types[type.key] = type for type in @ordered_types
|
|
|
|
if Conf['Embedding'] and g.VIEW isnt 'archive'
|
|
@dialog = UI.dialog 'embedding',
|
|
`<%= readHTML('Embed.html') %>`
|
|
@media = $ '#media-embed', @dialog
|
|
$.one d, '4chanXInitFinished', @ready
|
|
$.on d, 'IndexRefreshInternal', ->
|
|
g.posts.forEach (post) ->
|
|
for post in [post, post.clones...]
|
|
for embed in post.nodes.embedlinks
|
|
Embedding.cb.catalogRemove.call embed
|
|
return
|
|
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 if g.VIEW is 'archive'
|
|
if Conf['Embedding']
|
|
i = 0
|
|
items = post.nodes.embedlinks = $$ '.embedder', post.nodes.comment
|
|
while el = items[i++]
|
|
$.on el, 'click', Embedding.cb.click
|
|
Embedding.cb.toggle.call el if $.hasClass el, 'embedded'
|
|
if Conf['Cover Preview']
|
|
i = 0
|
|
items = $$ '.linkify', post.nodes.comment
|
|
while el = items[i++]
|
|
if (data = Embedding.services el)
|
|
Embedding.preview data
|
|
return
|
|
|
|
process: (link, post) ->
|
|
return unless Conf['Embedding'] or Conf['Link Title'] or Conf['Cover Preview']
|
|
return if $.x 'ancestor::pre', link
|
|
if data = Embedding.services link
|
|
data.post = post
|
|
Embedding.embed data if Conf['Embedding'] and g.VIEW isnt 'archive'
|
|
Embedding.title data if Conf['Link Title']
|
|
Embedding.preview data if Conf['Cover Preview'] and g.VIEW isnt 'archive'
|
|
|
|
services: (link) ->
|
|
{href} = link
|
|
for type in Embedding.ordered_types when (match = type.regExp.exec href)
|
|
return {key: type.key, uid: match[1], options: match[2], link}
|
|
return
|
|
|
|
embed: (data) ->
|
|
{key, uid, options, link, post} = data
|
|
{href} = link
|
|
|
|
$.addClass link, key.toLowerCase()
|
|
|
|
embed = $.el 'a',
|
|
className: 'embedder'
|
|
href: 'javascript:;'
|
|
,
|
|
`<%= html('(<span>un</span>embed)') %>`
|
|
|
|
embed.dataset[name] = value for name, value of {key, uid, options, href}
|
|
|
|
$.on embed, 'click', Embedding.cb.click
|
|
$.after link, [$.tn(' '), embed]
|
|
post.nodes.embedlinks.push embed
|
|
|
|
if Conf['Auto-embed'] and !Conf['Floating Embeds'] and !post.isFetchedQuote
|
|
if $.hasClass(doc, 'catalog-mode')
|
|
$.addClass embed, 'embed-removed'
|
|
else
|
|
Embedding.cb.toggle.call embed
|
|
|
|
ready: ->
|
|
return if !Main.isThisPageLegit()
|
|
$.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.pointerEvents = ''
|
|
return
|
|
$.on d, 'mouseup', Embedding.dragEmbed
|
|
Embedding.dragEmbed.mouseup = true
|
|
style.pointerEvents = 'none'
|
|
|
|
title: (data) ->
|
|
{key, uid, options, link, post} = data
|
|
return if not (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
|
|
CrossOrigin.cache service.api(uid), (-> Embedding.cb.title @, data)
|
|
|
|
flushTitles: (service) ->
|
|
{queue} = service
|
|
return unless queue?.length
|
|
service.queue = []
|
|
cb = ->
|
|
Embedding.cb.title @, data for data in queue
|
|
return
|
|
CrossOrigin.cache service.api(data.uid for data in queue), cb
|
|
|
|
preview: (data) ->
|
|
{key, uid, link} = data
|
|
return if not (service = Embedding.types[key].preview)
|
|
$.on link, 'mouseover', (e) ->
|
|
src = service.url uid
|
|
{height} = service
|
|
el = $.el 'img',
|
|
src: src
|
|
id: 'ihover'
|
|
$.add Header.hover, el
|
|
UI.hover
|
|
root: link
|
|
el: el
|
|
latestEvent: e
|
|
endEvents: 'mouseout click'
|
|
height: height
|
|
|
|
cb:
|
|
click: (e) ->
|
|
e.preventDefault()
|
|
if not $.hasClass(@, 'embedded') and (Conf['Floating Embeds'] or $.hasClass(doc, 'catalog-mode'))
|
|
return if not (div = Embedding.media.firstChild)
|
|
$.replace div, Embedding.cb.embed @
|
|
Embedding.lastEmbed = Get.postFromNode(@).nodes.root
|
|
$.rmClass Embedding.dialog, 'empty'
|
|
else
|
|
Embedding.cb.toggle.call @
|
|
|
|
toggle: ->
|
|
if $.hasClass @, "embedded"
|
|
$.rm @nextElementSibling
|
|
else
|
|
$.after @, Embedding.cb.embed @
|
|
$.toggleClass @, 'embedded'
|
|
|
|
embed: (a) ->
|
|
# We create an element to embed
|
|
container = $.el 'div', {className: 'media-embed'}
|
|
$.add container, el = (type = Embedding.types[a.dataset.key]).el a
|
|
|
|
# Set style values.
|
|
el.style.cssText = if type.style?
|
|
type.style
|
|
else
|
|
'border: none; width: 640px; height: 360px;'
|
|
|
|
return container
|
|
|
|
catalogRemove: ->
|
|
isCatalog = $.hasClass(doc, 'catalog-mode')
|
|
if (isCatalog and $.hasClass(@, 'embedded')) or (!isCatalog and $.hasClass(@, 'embed-removed'))
|
|
Embedding.cb.toggle.call @
|
|
$.toggleClass @, 'embed-removed'
|
|
|
|
title: (req, data) ->
|
|
{key, uid, options, link, post} = data
|
|
service = Embedding.types[key].title
|
|
|
|
{status} = req
|
|
if status in [200, 304] and service.status
|
|
status = service.status(req.response)[0]
|
|
|
|
return unless status
|
|
|
|
text = "[#{key}] #{switch status
|
|
when 200, 304
|
|
text = service.text req.response, uid
|
|
if typeof text is 'string'
|
|
text
|
|
else
|
|
text = link.textContent
|
|
when 404
|
|
"Not Found"
|
|
when 403, 401
|
|
"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|m4a|oga|wav|flac)(?:[?#]|$)/i
|
|
style: ''
|
|
el: (a) ->
|
|
$.el 'audio',
|
|
controls: true
|
|
preload: 'auto'
|
|
src: a.dataset.href
|
|
,
|
|
key: 'image'
|
|
regExp: /^[^?#]+\.(?:gif|png|jpg|jpeg|bmp|webp)(?::\w+)?(?:[?#]|$)/i
|
|
style: ''
|
|
el: (a) ->
|
|
$.el 'div', `<%= html('<a target="_blank" href="${a.dataset.href}"><img src="${a.dataset.href}" style="max-width: 80vw; max-height: 80vh;"></a>') %>`
|
|
,
|
|
key: 'video'
|
|
regExp: /^[^?#]+\.(?:og[gv]|webm|mp4)(?:[?#]|$)/i
|
|
style: 'max-width: 80vw; max-height: 80vh;'
|
|
el: (a) ->
|
|
el = $.el 'video',
|
|
hidden: true
|
|
controls: true
|
|
preload: 'auto'
|
|
src: a.dataset.href
|
|
loop: ImageHost.test a.dataset.href.split('/')[2]
|
|
$.on el, 'loadedmetadata', ->
|
|
if el.videoHeight is 0 and el.parentNode
|
|
$.replace el, Embedding.types.audio.el(a)
|
|
else
|
|
el.hidden = false
|
|
el
|
|
,
|
|
key: 'PeerTube'
|
|
regExp: /^(\w+:\/\/[^\/]+\/videos\/watch\/\w{8}-\w{4}-\w{4}-\w{4}-\w{12})(.*)/
|
|
el: (a) ->
|
|
options = if (start = a.dataset.options.match /[?&](start=\w+)/) then "?#{start[1]}" else ''
|
|
el = $.el 'iframe',
|
|
src: a.dataset.uid.replace('/videos/watch/', '/videos/embed/') + options
|
|
el.setAttribute "allowfullscreen", "true"
|
|
el
|
|
,
|
|
key: 'BitChute'
|
|
regExp: /^\w+:\/\/(?:www\.)?bitchute\.com\/video\/([\w\-]+)/
|
|
el: (a) ->
|
|
el = $.el 'iframe',
|
|
src: "https://www.bitchute.com/embed/#{a.dataset.uid}/"
|
|
el.setAttribute "allowfullscreen", "true"
|
|
el
|
|
,
|
|
key: 'Clyp'
|
|
regExp: /^\w+:\/\/(?:www\.)?clyp\.it\/(\w{8})/
|
|
style: 'border: 0; width: 640px; height: 160px;'
|
|
el: (a) ->
|
|
$.el 'iframe',
|
|
src: "https://clyp.it/#{a.dataset.uid}/widget"
|
|
title:
|
|
api: (uid) -> "https://api.clyp.it/oembed?url=https://clyp.it/#{uid}"
|
|
text: (_) -> _.title
|
|
,
|
|
key: 'Dailymotion'
|
|
regExp: /^\w+:\/\/(?:(?:www\.)?dailymotion\.com\/(?:embed\/)?video|dai\.ly)\/([A-Za-z0-9]+)[^?]*(.*)/
|
|
el: (a) ->
|
|
options = if (start = a.dataset.options.match /[?&](start=\d+)/) then "?#{start[1]}" else ''
|
|
el = $.el 'iframe',
|
|
src: "//www.dailymotion.com/embed/video/#{a.dataset.uid}#{options}"
|
|
el.setAttribute "allowfullscreen", "true"
|
|
el
|
|
title:
|
|
api: (uid) -> "https://api.dailymotion.com/video/#{uid}"
|
|
text: (_) -> _.title
|
|
preview:
|
|
url: (uid) -> "https://www.dailymotion.com/thumbnail/video/#{uid}"
|
|
height: 240
|
|
,
|
|
key: 'Gfycat'
|
|
regExp: /^\w+:\/\/(?:www\.)?gfycat\.com\/(?:iframe\/)?(\w+)/
|
|
el: (a) ->
|
|
el = $.el 'iframe',
|
|
src: "//gfycat.com/ifr/#{a.dataset.uid}"
|
|
el.setAttribute "allowfullscreen", "true"
|
|
el
|
|
,
|
|
key: 'Gist'
|
|
regExp: /^\w+:\/\/gist\.github\.com\/[\w\-]+\/(\w+)/
|
|
style: ''
|
|
el: do ->
|
|
counter = 0
|
|
(a) ->
|
|
el = $.el 'pre',
|
|
hidden: true
|
|
id: "gist-embed-#{counter++}"
|
|
CrossOrigin.cache "https://api.github.com/gists/#{a.dataset.uid}", ->
|
|
el.textContent = Object.values(@response.files)[0].content
|
|
el.className = 'prettyprint'
|
|
$.global ->
|
|
window.prettyPrint? (() ->), document.getElementById(document.currentScript.dataset.id).parentNode
|
|
, id: el.id
|
|
el.hidden = false
|
|
el
|
|
title:
|
|
api: (uid) -> "https://api.github.com/gists/#{uid}"
|
|
text: ({files}) ->
|
|
return file for file of files when files.hasOwnProperty file
|
|
,
|
|
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: 'LiveLeak'
|
|
regExp: /^\w+:\/\/(?:\w+\.)?liveleak\.com\/.*\?.*[tif]=(\w+)/
|
|
el: (a) ->
|
|
el = $.el 'iframe',
|
|
src: "https://www.liveleak.com/e/#{a.dataset.uid}",
|
|
el.setAttribute "allowfullscreen", "true"
|
|
el
|
|
,
|
|
key: 'Loopvid'
|
|
regExp: /^\w+:\/\/(?:www\.)?loopvid.appspot.com\/#?((?:pf|kd|lv|gd|gh|db|dx|nn|cp|wu|ig|ky|mf|m2|pc|1c|pi|ni|wl|ko|mm|ic|gc)\/[\w\-\/]+(?:,[\w\-\/]+)*|fc\/\w+\/\d+|https?:\/\/.+)/
|
|
style: 'max-width: 80vw; max-height: 80vh;'
|
|
el: (a) ->
|
|
el = $.el 'video',
|
|
controls: true
|
|
preload: 'auto'
|
|
loop: true
|
|
if /^http/.test a.dataset.uid
|
|
$.add el, $.el 'source', src: a.dataset.uid
|
|
return el
|
|
[_, 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}"
|
|
urls = switch host
|
|
# list from src/common.py at http://loopvid.appspot.com/source.html
|
|
when 'pf' then ["https://kastden.org/_loopvid_media/pf/#{base}", "https://web.archive.org/web/2/http://a.pomf.se/#{base}"]
|
|
when 'kd' then ["https://kastden.org/loopvid/#{base}"]
|
|
when 'lv' then ["https://lv.kastden.org/#{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 ["https://kastden.org/_loopvid_media/nn/#{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://kastden.org/_loopvid_media/ky/#{base}"]
|
|
when 'mf' then ["https://kastden.org/_loopvid_media/mf/#{base}", "https://web.archive.org/web/2/https://d.maxfile.ro/#{base}"]
|
|
when 'm2' then ["https://kastden.org/_loopvid_media/m2/#{base}"]
|
|
when 'pc' then ["https://kastden.org/_loopvid_media/pc/#{base}", "https://web.archive.org/web/2/http://a.pomf.cat/#{base}"]
|
|
when '1c' then ["http://b.1339.cf/#{base}"]
|
|
when 'pi' then ["https://kastden.org/_loopvid_media/pi/#{base}", "https://web.archive.org/web/2/https://u.pomf.is/#{base}"]
|
|
when 'ni' then ["https://kastden.org/_loopvid_media/ni/#{base}", "https://web.archive.org/web/2/https://u.nya.is/#{base}"]
|
|
when 'wl' then ["http://webm.land/media/#{base}"]
|
|
when 'ko' then ["https://kordy.kastden.org/loopvid/#{base}"]
|
|
when 'mm' then ["https://kastden.org/_loopvid_media/mm/#{base}", "https://web.archive.org/web/2/https://my.mixtape.moe/#{base}"]
|
|
when 'ic' then ["https://media.8ch.net/file_store/#{base}"]
|
|
when 'fc' then ["//#{ImageHost.host()}/#{base}.webm"]
|
|
when 'gc' then ["https://#{type}.gfycat.com/#{name}.webm"]
|
|
|
|
for url in urls
|
|
$.add el, $.el 'source', src: url
|
|
el
|
|
,
|
|
key: 'Openings.moe'
|
|
regExp: /^\w+:\/\/openings.moe\/\?video=([^.&=]+)/
|
|
style: 'width: 1280px; height: 720px; max-width: 80vw; max-height: 80vh;'
|
|
el: (a) ->
|
|
el = $.el 'iframe',
|
|
src: "https://openings.moe/?video=#{a.dataset.uid}",
|
|
el.setAttribute "allowfullscreen", "true"
|
|
el
|
|
,
|
|
key: 'Pastebin'
|
|
regExp: /^\w+:\/\/(?:\w+\.)?pastebin\.com\/(?!u\/)(?:[\w.]+(?:\/|\?i\=))?(\w+)/
|
|
el: (a) ->
|
|
div = $.el 'iframe',
|
|
src: "//pastebin.com/embed_iframe.php?i=#{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) -> "#{location.protocol}//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: "https://www.strawpoll.me/embed_1/#{a.dataset.uid}"
|
|
,
|
|
key: 'Streamable'
|
|
regExp: /^\w+:\/\/(?:www\.)?streamable\.com\/(\w+)/
|
|
el: (a) ->
|
|
el = $.el 'iframe',
|
|
src: "https://streamable.com/o/#{a.dataset.uid}"
|
|
el.setAttribute "allowfullscreen", "true"
|
|
el
|
|
title:
|
|
api: (uid) -> "https://api.streamable.com/oembed?url=https://streamable.com/#{uid}"
|
|
text: (_) -> _.title
|
|
,
|
|
key: 'TwitchTV'
|
|
regExp: /^\w+:\/\/(?:www\.|secure\.)?twitch\.tv\/(\w[^#\&\?]*)/
|
|
el: (a) ->
|
|
m = a.dataset.uid.match /(\w+)(?:\/v\/(\d+))?/
|
|
url = "//player.twitch.tv/?#{if m[2] then "video=v#{m[2]}" else "channel=#{m[1]}"}&autoplay=false&parent=#{location.hostname}"
|
|
if (time = a.dataset.href.match /\bt=(\w+)/)
|
|
url += "&time=#{time[1]}"
|
|
el = $.el 'iframe',
|
|
src: url
|
|
el.setAttribute "allowfullscreen", "true"
|
|
el
|
|
,
|
|
key: 'Twitter'
|
|
regExp: /^\w+:\/\/(?:www\.|mobile\.)?twitter\.com\/(\w+\/status\/\d+)/
|
|
style: 'border: none; width: 550px; height: 250px; overflow: hidden; resize: both;'
|
|
el: (a) ->
|
|
el = $.el 'iframe'
|
|
$.on el, 'load', ->
|
|
@contentWindow.postMessage {element: 't', query: 'height'}, 'https://twitframe.com'
|
|
onMessage = (e) ->
|
|
if e.source is el.contentWindow and e.origin is 'https://twitframe.com'
|
|
$.off window, 'message', onMessage
|
|
(cont or el).style.height = "#{+$.minmax(e.data.height, 250, 0.8 * doc.clientHeight)}px"
|
|
$.on window, 'message', onMessage
|
|
el.src = "https://twitframe.com/show?url=https://twitter.com/#{a.dataset.uid}"
|
|
if $.engine is 'gecko'
|
|
# XXX https://bugzilla.mozilla.org/show_bug.cgi?id=680823
|
|
el.style.cssText = 'border: none; width: 100%; height: 100%;'
|
|
cont = $.el 'div'
|
|
$.add cont, el
|
|
cont
|
|
else
|
|
el
|
|
,
|
|
key: 'VidLii'
|
|
regExp: /^\w+:\/\/(?:www\.)?vidlii\.com\/watch\?v=(\w{11})/
|
|
style: 'border: none; width: 640px; height: 392px;'
|
|
el: (a) ->
|
|
el = $.el 'iframe',
|
|
src: "https://www.vidlii.com/embed?v=#{a.dataset.uid}&a=0"
|
|
el.setAttribute "allowfullscreen", "true"
|
|
el
|
|
,
|
|
key: 'Vimeo'
|
|
regExp: /^\w+:\/\/(?:www\.)?vimeo\.com\/(\d+)/
|
|
el: (a) ->
|
|
el = $.el 'iframe',
|
|
src: "//player.vimeo.com/video/#{a.dataset.uid}?wmode=opaque"
|
|
el.setAttribute "allowfullscreen", "true"
|
|
el
|
|
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: 'Vocaroo'
|
|
regExp: /^\w+:\/\/(?:(?:www\.|old\.)?vocaroo\.com|voca\.ro)\/((?:i\/)?\w+)/
|
|
style: ''
|
|
el: (a) ->
|
|
el = $.el 'audio',
|
|
controls: true
|
|
preload: 'auto'
|
|
el.src = if /^i\//.test(a.dataset.uid)
|
|
"https://old.vocaroo.com/media_command.php?media=#{a.dataset.uid.replace('i/', '')}&command=download_mp3"
|
|
else
|
|
"https://media.vocaroo.com/mp3/#{a.dataset.uid}"
|
|
el
|
|
,
|
|
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}?rel=0&wmode=opaque#{if start then '&start=' + start else ''}"
|
|
el.setAttribute "allowfullscreen", "true"
|
|
el
|
|
title:
|
|
api: (uid) -> "https://noembed.com/embed?url=https%3A//www.youtube.com/watch%3Fv%3D#{uid}&format=json"
|
|
text: (_) -> _.title
|
|
status: (_) ->
|
|
if _.error
|
|
m = _.error.match(/^(\d*)\s*(.*)/)
|
|
[+m[1], m[2]]
|
|
else
|
|
[200, 'OK']
|
|
preview:
|
|
url: (uid) -> "https://img.youtube.com/vi/#{uid}/0.jpg"
|
|
height: 360
|
|
]
|