/*
* embed-js - v4.1.3
* A JavaScript plugin that analyses the string and embeds emojis, media, tweets, code and services.
* http://riteshkr.com/embed.js
*
* Made by Ritesh Kumar
* Under MIT License
*/
var __commonjs_global = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : this;
function __commonjs(fn, module) { return module = { exports: {} }, fn(module, module.exports, __commonjs_global), module.exports; }
/**
* Trucates the string and adds ellipsis at the end.
* @param string The string to be truncated
* @param n Length to which it should be truncated
* @returns {string} The truncated string
*/
function truncate(string, n) {
return string.substr(0, n - 1) + (string.length > n ? '...' : '');
}
/**
* Converts a string into legitimate url.
* @param string
*/
function toUrl(string) {
return (string.indexOf('//') === -1) ? ('//' + string) : string
}
/**
* Extends an Object
* @param destination
* @param source
* @returns {*}
*/
function deepExtend(destination, source) {
for (var property in source) {
if (source.hasOwnProperty(property) && source[property] && source[property].constructor === Object) {
destination[property] = destination[property] || {};
deepExtend(destination[property], source[property]);
} else {
destination[property] = source[property];
}
}
return destination;
}
function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
}
/**
* Sort an array of objects based on the index value
* @param {Array} arr Array to be sorted
* @return {Array} Sorted array
*/
function sortObject(arr) {
return arr.sort((a, b) => (a.index - b.index));
}
/**
* Creates the string of the iframes after sorting them and finally returning a string
* @param {string} str String to which the created text has to be added
* @param {object} embeds Sorted array of iframe html
* @return {string} String to be rendered
*/
function createText(str, embeds) {
let sortedEmbeds = sortObject(embeds);
for (let i = 0; i < sortedEmbeds.length; i++) {
str += ` ${sortedEmbeds[i].text}`;
}
return str;
}
/**
* Matches the string and finds the substrings matching to the provided regex pattern
* @param {object} regex Regex pattern
* @param {string} input The string to be analyzed
* @return {object} Returns the matched substring with their corresponding positions
*/
function matches(regex, input) {
return regex.exec(input);
}
/**
* Checks whether a particular service should be embedded or not based on
* the setting provided by the user
* @param {object} options The options provided by the user
* @param {string} service Name of the service for which the condition is to be analyzed
* @return {boolean} True if it should be embedded
*/
function ifEmbed(options, service) {
if (options.singleEmbed && options.served.length) return;
return ((options.excludeEmbed.indexOf(service) == -1) && !(options.excludeEmbed === 'all'));
}
function ifInline(options, service) {
return ((options.inlineEmbed.indexOf(service) >= 0) || (options.inlineEmbed === 'all'));
}
/**
* Calculates the dimensions for the elements based on a aspect ratio
* @param {object} options Plugin options
* @return {object} The width and height of the elements
*/
function setDimensions(options) {
options.videoWidth = options.videoWidth || ((options.videoHeight) / 3) * 4 || 800;
options.videoHeight = options.videoHeight || ((options.videoWidth) / 4) * 3 || 600;
return options;
}
/**
* Returns a cloned object
* @param {object} obj
* @return {object} cloned object
*/
function cloneObject(obj) {
if (obj === null || typeof obj !== 'object') return obj;
var temp = obj.constructor(); // give temp the original obj's constructor
for (var key in obj) {
temp[key] = cloneObject(obj[key])
}
return temp
}
function urlRegex() {
return /((href|src)=["']|)(\b(https?|ftp|file):\/\/[^,.\s]+[^\s]*[^,.\s]+)|(?:https?:\/\/)?(?:(?:0rz\.tw)|(?:1link\.in)|(?:1url\.com)|(?:2\.gp)|(?:2big\.at)|(?:2tu\.us)|(?:3\.ly)|(?:307\.to)|(?:4ms\.me)|(?:4sq\.com)|(?:4url\.cc)|(?:6url\.com)|(?:7\.ly)|(?:a\.gg)|(?:a\.nf)|(?:aa\.cx)|(?:abcurl\.net)|(?:ad\.vu)|(?:adf\.ly)|(?:adjix\.com)|(?:afx\.cc)|(?:all\.fuseurl.com)|(?:alturl\.com)|(?:amzn\.to)|(?:ar\.gy)|(?:arst\.ch)|(?:atu\.ca)|(?:azc\.cc)|(?:b23\.ru)|(?:b2l\.me)|(?:bacn\.me)|(?:bcool\.bz)|(?:binged\.it)|(?:bit\.ly)|(?:buff\.ly)|(?:bizj\.us)|(?:bloat\.me)|(?:bravo\.ly)|(?:bsa\.ly)|(?:budurl\.com)|(?:canurl\.com)|(?:chilp\.it)|(?:chzb\.gr)|(?:cl\.lk)|(?:cl\.ly)|(?:clck\.ru)|(?:cli\.gs)|(?:cliccami\.info)|(?:clickthru\.ca)|(?:clop\.in)|(?:conta\.cc)|(?:cort\.as)|(?:cot\.ag)|(?:crks\.me)|(?:ctvr\.us)|(?:cutt\.us)|(?:dai\.ly)|(?:decenturl\.com)|(?:dfl8\.me)|(?:digbig\.com)|(?:digg\.com)|(?:disq\.us)|(?:dld\.bz)|(?:dlvr\.it)|(?:do\.my)|(?:doiop\.com)|(?:dopen\.us)|(?:easyuri\.com)|(?:easyurl\.net)|(?:eepurl\.com)|(?:eweri\.com)|(?:fa\.by)|(?:fav\.me)|(?:fb\.me)|(?:fbshare\.me)|(?:ff\.im)|(?:fff\.to)|(?:fire\.to)|(?:firsturl\.de)|(?:firsturl\.net)|(?:flic\.kr)|(?:flq\.us)|(?:fly2\.ws)|(?:fon\.gs)|(?:freak\.to)|(?:fuseurl\.com)|(?:fuzzy\.to)|(?:fwd4\.me)|(?:fwib\.net)|(?:g\.ro.lt)|(?:gizmo\.do)|(?:gl\.am)|(?:go\.9nl.com)|(?:go\.ign.com)|(?:go\.usa.gov)|(?:goo\.gl)|(?:goshrink\.com)|(?:gurl\.es)|(?:hex\.io)|(?:hiderefer\.com)|(?:hmm\.ph)|(?:href\.in)|(?:hsblinks\.com)|(?:htxt\.it)|(?:huff\.to)|(?:hulu\.com)|(?:hurl\.me)|(?:hurl\.ws)|(?:icanhaz\.com)|(?:idek\.net)|(?:ilix\.in)|(?:is\.gd)|(?:its\.my)|(?:ix\.lt)|(?:j\.mp)|(?:jijr\.com)|(?:kl\.am)|(?:klck\.me)|(?:korta\.nu)|(?:krunchd\.com)|(?:l9k\.net)|(?:lat\.ms)|(?:liip\.to)|(?:liltext\.com)|(?:linkbee\.com)|(?:linkbun\.ch)|(?:liurl\.cn)|(?:ln-s\.net)|(?:ln-s\.ru)|(?:lnk\.gd)|(?:lnk\.ms)|(?:lnkd\.in)|(?:lnkurl\.com)|(?:lru\.jp)|(?:lt\.tl)|(?:lurl\.no)|(?:macte\.ch)|(?:mash\.to)|(?:merky\.de)|(?:migre\.me)|(?:miniurl\.com)|(?:minurl\.fr)|(?:mke\.me)|(?:moby\.to)|(?:moourl\.com)|(?:mrte\.ch)|(?:myloc\.me)|(?:myurl\.in)|(?:n\.pr)|(?:nbc\.co)|(?:nblo\.gs)|(?:nn\.nf)|(?:not\.my)|(?:notlong\.com)|(?:nsfw\.in)|(?:nutshellurl\.com)|(?:nxy\.in)|(?:nyti\.ms)|(?:o-x\.fr)|(?:oc1\.us)|(?:om\.ly)|(?:omf\.gd)|(?:omoikane\.net)|(?:on\.cnn.com)|(?:on\.mktw.net)|(?:onforb\.es)|(?:orz\.se)|(?:ow\.ly)|(?:ping\.fm)|(?:pli\.gs)|(?:pnt\.me)|(?:politi\.co)|(?:post\.ly)|(?:pp\.gg)|(?:profile\.to)|(?:ptiturl\.com)|(?:pub\.vitrue.com)|(?:qlnk\.net)|(?:qte\.me)|(?:qu\.tc)|(?:qy\.fi)|(?:r\.im)|(?:rb6\.me)|(?:read\.bi)|(?:readthis\.ca)|(?:reallytinyurl\.com)|(?:redir\.ec)|(?:redirects\.ca)|(?:redirx\.com)|(?:retwt\.me)|(?:ri\.ms)|(?:rickroll\.it)|(?:riz\.gd)|(?:rt\.nu)|(?:ru\.ly)|(?:rubyurl\.com)|(?:rurl\.org)|(?:rww\.tw)|(?:s4c\.in)|(?:s7y\.us)|(?:safe\.mn)|(?:sameurl\.com)|(?:sdut\.us)|(?:shar\.es)|(?:shink\.de)|(?:shorl\.com)|(?:short\.ie)|(?:short\.to)|(?:shortlinks\.co.uk)|(?:shorturl\.com)|(?:shout\.to)|(?:show\.my)|(?:shrinkify\.com)|(?:shrinkr\.com)|(?:shrt\.fr)|(?:shrt\.st)|(?:shrten\.com)|(?:shrunkin\.com)|(?:simurl\.com)|(?:slate\.me)|(?:smallr\.com)|(?:smsh\.me)|(?:smurl\.name)|(?:sn\.im)|(?:snipr\.com)|(?:snipurl\.com)|(?:snurl\.com)|(?:sp2\.ro)|(?:spedr\.com)|(?:srnk\.net)|(?:srs\.li)|(?:starturl\.com)|(?:su\.pr)|(?:surl\.co.uk)|(?:surl\.hu)|(?:t\.cn)|(?:t\.co)|(?:t\.lh.com)|(?:ta\.gd)|(?:tbd\.ly)|(?:tcrn\.ch)|(?:tgr\.me)|(?:tgr\.ph)|(?:tighturl\.com)|(?:tiniuri\.com)|(?:tiny\.cc)|(?:tiny\.ly)|(?:tiny\.pl)|(?:tinylink\.in)|(?:tinyuri\.ca)|(?:tinyurl\.com)|(?:tl\.gd)|(?:tmi\.me)|(?:tnij\.org)|(?:tnw\.to)|(?:tny\.com)|(?:to\.ly)|(?:togoto\.us)|(?:totc\.us)|(?:toysr\.us)|(?:tpm\.ly)|(?:tr\.im)|(?:tra\.kz)|(?:trunc\.it)|(?:twhub\.com)|(?:twirl\.at)|(?:twitclicks\.com)|(?:twitterurl\.net)|(?:twitterurl\.org)|(?:twiturl\.de)|(?:twurl\.cc)|(?:twurl\.nl)|(?:u\.mavrev.com)|(?:u\.nu)|(?:u76\.org)|(?:ub0\.cc)|(?:ulu\.lu)|(?:updating\.me)|(?:ur1\.ca)|(?:url\.az)|(?:url\.co.uk)|(?:url\.ie)|(?:url360\.me)|(?:url4\.eu)|(?:urlborg\.com)|(?:urlbrief\.com)|(?:urlcover\.com)|(?:urlcut\.com)|(?:urlenco\.de)|(?:urli\.nl)|(?:urls\.im)|(?:urlshorteningservicefortwitter\.com)|(?:urlx\.ie)|(?:urlzen\.com)|(?:usat\.ly)|(?:use\.my)|(?:vb\.ly)|(?:vgn\.am)|(?:vl\.am)|(?:vm\.lc)|(?:w55\.de)|(?:wapo\.st)|(?:wapurl\.co.uk)|(?:wipi\.es)|(?:wp\.me)|(?:x\.vu)|(?:xr\.com)|(?:xrl\.in)|(?:xrl\.us)|(?:xurl\.es)|(?:xurl\.jp)|(?:y\.ahoo.it)|(?:yatuc\.com)|(?:ye\.pe)|(?:yep\.it)|(?:yfrog\.com)|(?:yhoo\.it)|(?:yiyd\.com)|(?:youtu\.be)|(?:yuarel\.com)|(?:z0p\.de)|(?:zi\.ma)|(?:zi\.mu)|(?:zipmyurl\.com)|(?:zud\.me)|(?:zurl\.ws)|(?:zz\.gd)|(?:zzang\.kr)|(?:›\.ws)|(?:✩\.ws)|(?:✿\.ws)|(?:❥\.ws)|(?:➔\.ws)|(?:➞\.ws)|(?:➡\.ws)|(?:➨\.ws)|(?:➯\.ws)|(?:➹\.ws)|(?:➽\.ws))\/[a-z0-9]*/gi;
}
function arrayLowercase(options, property){
if(typeof options[property] !== 'string'){
options[property] = options[property].map(function(elem){
return elem.toLowerCase();
})
}
return options;
}
/**
* Sets the dimensions and converts options values' Array into lowercase.
* @param options
* @returns {Object|*}
*/
function processOptions(options){
options = setDimensions(options);
options = arrayLowercase(options, 'excludeEmbed');
options = arrayLowercase(options, 'inlineEmbed');
return arrayLowercase(options, 'openGraphExclude');
}
/**
* Get the last element of an array or string
* @param elem [String|Array]
* @returns last element of the Array or String
*/
function lastElement(elem){
return elem[elem.length - 1];
}
var renderer = {
url(match, options){
let config = options.linkOptions;
return `${match}`;
},
smiley(text, pre, code) {
return `${pre}${code}`;
},
emoji(text){
return ``;
},
audio(match){
return `
`
},
soundcloud(match, options){
let config = options.soundCloudOptions;
return `
`
},
spotify(match){
let id = lastElement(match.split('/'));
return ``
},
codepen(id, options){
return ``
},
ideone(match, options){
return ``
},
jsbin(id, options){
return ``
},
jsfiddle(id, options){
id = lastElement(id) == '/' ? id.slice(0, - 1) : id;
id = (id.indexOf('//') !== -1) ? id : `//${id}`;
return ``
},
plunker(id, options){
return ``
},
image(match){
return ``
},
flickr(match, options){
return ``
},
instagram(match, options){
return ``;
},
slideShare(html){
return `${html}
`;
},
video(match){
return ``
},
dailymotion(match, options){
const id = lastElement(match.split('/'));
return ``
},
liveleak(match, options){
return ``
},
ted(match, options){
let a = match.split('/');
const id = a[a.length - 1];
return ``
},
ustream(match, options){
let id = match.split('/');
id.splice(1, 0, 'embed');
return ``
},
detailsVimeo(data, fullData, embedUrl){
return `${data.description}
${data.views}${data.likes}
`
},
detailsYoutube(data, fullData, embedUrl){
return `${data.description}
${data.views}${data.likes}
`
},
vine(match, options){
const id = lastElement(match.split('/'));
const config = options.vineOptions;
return ``
},
vimeo(url, options){
return options.plyr ?
`` :
``
},
youtube(url, options){
return options.plyr ?
`` :
``
},
openGraph(data, options){
return ``
},
github(data, options){
return `${data.description}
${data.stargazers_count}${data.network_count}
`
},
gmap(latitude, longitude, location, options){
const config = options.mapOptions;
if (config.mode === 'place') {
return ``;
} else if (config.mode === 'streetview') {
return ``;
} else if (config.mode === 'view') {
return ``
}
}
}
const regex = {
mentions : /\B@[a-z0-9_-]+/gi,
hashtag : /\B#[a-z0-9_-]+/gi,
basicAudio : /((?:https?):\/\/\S*\.(?:wav|mp3|ogg))/gi,
soundCloud : /(soundcloud.com)\/[a-zA-Z0-9-_]+\/[a-zA-Z0-9-_]+/gi,
spotify : /spotify.com\/track\/[a-zA-Z0-9_]+/gi,
codepen : /http:\/\/codepen.io\/([A-Za-z0-9_]+)\/pen\/([A-Za-z0-9_]+)/gi,
gist : /gist.github.com\/[a-zA-Z0-9_-]+\/([a-zA-Z0-9]+)/gi,
highlightCode: /(`{3})(\s|[a-z]+)\s*([\s\S]*?[^`])\s*\1(?!`)/gm,
inlineCode : /(`)\s*([\s\S]*?[^`])\s*\1(?!`)/gm,
ideone : /ideone.com\/[a-zA-Z0-9]{6}/gi,
jsbin : /jsbin.com\/[a-zA-Z0-9_]+\/[0-9_]+/gi,
jsfiddle : /jsfiddle.net\/[a-zA-Z0-9_]+\/[a-zA-Z0-9_\/]+/gi,
plunker : /plnkr.co\/edit\/[a-zA-Z0-9\?=]+/gi,
basicImage : /((?:https?):\/\/\S*\.(?:gif|jpg|jpeg|tiff|png|svg|webp))/gi,
flickr : /flickr.com\/[a-z]+\/[a-zA-Z@_$!\d\-\]+\/[\d]+/gi,
instagram : /instagram.com\/p\/[a-zA-Z0-9_\/\?\-\=]+/gi,
slideShare : /slideshare.net\/[a-zA-Z0-9_-]*\/[a-zA-Z0-9_-]*/gi,
github : /[^\.]github.com\/([\w\.\-]+)\/([\w\.\-]+)/gi,
basicVideo : /(?:https?):\/\/\S*\.(?:ogv|webm|mp4)/gi,
dailymotion : /dailymotion.com\/video\/[a-zA-Z0-9-_]+/gi,
liveleak : /liveleak.com\/view\?i=[a-zA-Z0-9_]+/gi,
ted : /ted.com\/talks\/[a-zA-Z0-9_]+/gi,
ustream : /ustream.tv\/[a-z\/0-9]*/gi,
vimeo : /https?:\/\/(?:www\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|album\/(\d+)\/video\/|)(\d+)(?:$|\/|\?)*/gi,
vine : /vine.co\/v\/[a-zA-Z0-9]+/gi,
youtube : /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com(?:\/embed\/|\/v\/|\/watch\?v=|\/ytscreeningroom\?v=|\/feeds\/api\/videos\/|\/user\S*[^\w\-\s]|\S*[^\w\-\s]))([\w\-]{11})[?=&+%\w-]*/gi,
gmap : /@\((.+)\)/gi,
twitter : /https:\/\/twitter\.com\/\w+\/\w+\/\d+/gi,
smileys : /(\:[\+\-\w]+\:|\<[\/\\]?3|[\(\)\\\D|\*\$][\-\^]?[\:\;\=]|[\:\;\=B8][\-\^]?[3DOPp\@\$\*\\\)\(\/\|])(?=\s|[\!\.\?]|$)/gi
};
function getEmoji(match) {
return match[0] === ':' && (lastElement(match) === ':') && match.substring(1, match.length - 1)
}
function emoji (output, options) {
return output.replace(regex.smileys, function (match) {
const emoji = getEmoji(match);
if (emoji) {
return options.template.emoji(emoji, options)
}
return match;
});
}
const defaultIcons = [{
'text': ':)',
'code': ''
}, {
'text': ':D',
'code': ''
}, {
'text': ':d',
'code': ''
}, {
'text': ':(',
'code': ''
}, {
'text': ':/',
'code': ''
}, {
'text': ':P',
'code': ''
}, {
'text': ':p',
'code': ''
}, {
'text': '3:)',
'code': ''
}, {
'text': '(^)',
'code': ''
}, {
'text': ';)',
'code': ''
}, {
'text': ':o',
'code': ''
}, {
'text': '-_-',
'code': ''
}, {
'text': '(y)',
'code': ''
}, {
'text': ':*',
'code': ''
}, {
'text': '<3',
'code': ''
}, {
'text': '<3',
'code': ''
}, {
'text': '</3',
'code': ''
}, {
'text': '3',
'code': ''
}, {
'text': '^_^',
'code': ''
}, {
'text': '8-)',
'code': ''
}, {
'text': '8|',
'code': ''
}, {
'text': ':S',
'code': ''
}, {
'text': ':s',
'code': ''
}];
function smiley (input, options) {
const icons = options.customFontIcons.length ? options.customFontIcons : defaultIcons;
const escapedSymbols = icons.map((val) => escapeRegExp(val.text));
const smileyRegex = new RegExp(`(^|\\s)(${escapedSymbols.join('|')})(?=\\s|$)`, 'gi');
return input.replace(smileyRegex, (match, pre, text) => {
let index = escapedSymbols.indexOf(escapeRegExp(text));
let code = icons[index].code;
return options.template.smiley(text, pre, code, options);
});
}
function url (input, options) {
const config = options.linkOptions;
return input.replace(urlRegex(), function(match) {
console.log(arguments);
if(lastElement(match) === ')') return match; //hack for markdown image
let extension = lastElement(match.split('.'));
if ((lastElement(match) === '/'))
match = match.slice(0, -1);
if (config.exclude.indexOf(extension) === -1)
return options.template.url(match, options);
return match;
});
}
var fetchJsonp = __commonjs(function (module, exports, global) {
(function (global, factory) {
if (typeof define === 'function' && define.amd) {
define(['exports', 'module'], factory);
} else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
factory(exports, module);
} else {
var mod = {
exports: {}
};
factory(mod.exports, mod);
global.fetchJsonp = mod.exports;
}
})(__commonjs_global, function (exports, module) {
'use strict';
var defaultOptions = {
timeout: 5000,
jsonpCallback: 'callback',
jsonpCallbackFunction: null
};
function generateCallbackFunction() {
return 'jsonp_' + Date.now() + '_' + Math.ceil(Math.random() * 100000);
}
// Known issue: Will throw 'Uncaught ReferenceError: callback_*** is not defined' error if request timeout
function clearFunction(functionName) {
// IE8 throws an exception when you try to delete a property on window
// http://stackoverflow.com/a/1824228/751089
try {
delete window[functionName];
} catch (e) {
window[functionName] = undefined;
}
}
function removeScript(scriptId) {
var script = document.getElementById(scriptId);
document.getElementsByTagName('head')[0].removeChild(script);
}
var fetchJsonp = function fetchJsonp(url) {
var options = arguments[1] === undefined ? {} : arguments[1];
var timeout = options.timeout != null ? options.timeout : defaultOptions.timeout;
var jsonpCallback = options.jsonpCallback != null ? options.jsonpCallback : defaultOptions.jsonpCallback;
var timeoutId = undefined;
return new Promise(function (resolve, reject) {
var callbackFunction = options.jsonpCallbackFunction || generateCallbackFunction();
window[callbackFunction] = function (response) {
resolve({
ok: true,
// keep consistent with fetch API
json: function json() {
return Promise.resolve(response);
}
});
if (timeoutId) clearTimeout(timeoutId);
removeScript(jsonpCallback + '_' + callbackFunction);
clearFunction(callbackFunction);
};
// Check if the user set their own params, and if not add a ? to start a list of params
url += url.indexOf('?') === -1 ? '?' : '&';
var jsonpScript = document.createElement('script');
jsonpScript.setAttribute('src', url + jsonpCallback + '=' + callbackFunction);
jsonpScript.id = jsonpCallback + '_' + callbackFunction;
document.getElementsByTagName('head')[0].appendChild(jsonpScript);
timeoutId = setTimeout(function () {
reject(new Error('JSONP request to ' + url + ' timed out'));
clearFunction(callbackFunction);
removeScript(jsonpCallback + '_' + callbackFunction);
}, timeout);
});
};
// export as global function
/*
let local;
if (typeof global !== 'undefined') {
local = global;
} else if (typeof self !== 'undefined') {
local = self;
} else {
try {
local = Function('return this')();
} catch (e) {
throw new Error('polyfill failed because global object is unavailable in this environment');
}
}
local.fetchJsonp = fetchJsonp;
*/
module.exports = fetchJsonp;
});
});
var fetchJsonp$1 = (fetchJsonp && typeof fetchJsonp === 'object' && 'default' in fetchJsonp ? fetchJsonp['default'] : fetchJsonp);
/**
* Common template for vimeo and youtube iframes
* @param {string} url URL of the embedding video
* @param {object} options Options object
* @return {string} compiled template with variables replaced
*/
function template(url, options) {
return options.template.vimeo(url, options) || options.template.youtube(url, options)
}
/**
* Plays the video after clicking on the thumbnail
* @param {object} options Options object
* @return {null}
*/
function playVideo(options) {
/** Execute the customVideoClickHandler if the user wants to handle it on his own. */
if (options.customVideoClickHandler) return options.videoClickHandler(options, template);
let classes = document.getElementsByClassName(options.videoClickClass);
for (let i = 0; i < classes.length; i++) {
classes[i].onclick = function () {
options.onVideoShow();
let url = this.getAttribute('data-ejs-url') + "?autoplay=true";
this.parentNode.parentNode.innerHTML = template(url, options);
};
}
}
function getDetailsTemplate (data, fullData, embedUrl, options) {
if (data.host === 'vimeo') {
return options.template.detailsVimeo(data, fullData, embedUrl, options)
} else if (data.host === 'youtube') {
return options.template.detailsYoutube(data, fullData, embedUrl, options)
}
}
/**
* Applies video.js to all audio and video dynamically
* @param {object} options Options object
* @return {null}
*/
function applyPlyr (options) {
if (options.plyr) {
if (!options.plugins.plyr) throw new ReferenceError("You have enabled plyr but you haven't loaded the library.Find it at https://plyr.io/");
let plyr = options.plugins.plyr;
plyr.setup('.ejs-plyr', options.plyrOptions);
}
}
/**
* Applies video.js to all audio and video dynamically
* @param {object} options Options object
* @return {null}
*/
function applyVideoJS(options) {
options.videojsOptions.width = options.videoWidth;
options.videojsOptions.height = options.videoHeight;
if (options.videoJS) {
if (!options.plugins.videojs) throw new ReferenceError("You have enabled videojs but you haven't loaded the library.Find it at http://videojs.com/");
let VideoJS = options.plugins.videojs;
let elements = options.input.getElementsByClassName('ejs-video-js');
for (let i = 0; i < elements.length; i++) {
VideoJS(elements[i], options.videojsOptions, () => options.videojsCallback());
}
}
}
/**
* Destroys the onclick event for opening the video template from the details template
* @param {className} className
* @return {null}
*/
function destroyVideos (className) {
const classes = document.getElementsByClassName(className);
for (let i = 0; i < classes.length; i++) {
classes[i].onclick = null
}
}
function inlineEmbed(_){
let regexInline = _.options.link ? new RegExp(`([^>]*${_.regex.source})<\/a>`, 'gm') : new RegExp(`([^\\s]*${_.regex.source})`, 'gm');
_.output = _.output.replace(regexInline, function(match) {
let url = _.options.link ? match.slice(0, -4) : match;
if (_.options.served.indexOf(url) === -1) {
_.options.served.push(url);
if (_.options.link) {
return !_.options.inlineText ? _.template(match.slice(0, -4)) + '' : match + _.template(match.slice(0, -4))
} else {
return !_.options.inlineText ? _.template(match) : match + _.template(match)
}
} else {
return match; //TODO : check whether this should be `match`
}
});
return [_.output, _.embeds];
}
function normalEmbed(_){
let match;
while ((match = matches(_.regex, _.input)) !== null) {
let url = match[0];
if (!(_.options.served.indexOf(url) === -1) || (_.options.served.length && _.options.singleEmbed)) continue;
_.options.served.push(url);
let text = _.template(url);
_.embeds.push({
text : text,
index: match.index
})
}
return [_.output, _.embeds];
}
function embed(_){
return (ifInline(_.options, _.service)) ? inlineEmbed(_) : normalEmbed(_)
}
function base (input, output, embeds, options, regex, service) {
const args = {
input,
output,
options,
embeds,
regex,
service,
template(match){
return this.options.template[this.service](match, this.options);
}
};
return embed(args);
}
function baseEmbed(input, output, embeds, options, regex, service, flag){
return ifEmbed(options, service) || (ifEmbed(options, service) && flag) ? base(input, output, embeds, options, regex, service) : [output, embeds]
}
/**
* This is a private function which is used to get the actual text to be replaced for
* a particular url in inline embedding. This returns a promise
* @param {object} _ reference to this
* @param {function} urlToText The function that converts url to replaceable text
* @param {object} match object containing info of matching string
* @return {Promise} resolves to the text
*/
function getInlineData(_, urlToText, match) {
let url = (_.options.link ? match[0].slice(0, -4) : match[0]) || match[1];
if (_.options.served.indexOf(url) >= 0) return Promise.resolve(null);
return new Promise((resolve) => {
urlToText(_, match, url).then((text) => {
if (!text) return resolve();
_.options.served.push(url);
resolve(text);
})
})
}
/**
* A helper function for inline embedding
* @param _
* @param urlToText
* @returns Promise
*/
function inlineAsyncEmbed(_, urlToText) {
let regexInline = _.options.link ? new RegExp(`([^>]*${_.regex.source})<\/a>`, 'gi') : new RegExp(`([^\\s]*${_.regex.source})`, 'gi');
let match, promises = [];
while ((match = matches(regexInline, _.output)) !== null)
promises.push(getInlineData(_, urlToText, match));
return new Promise((resolve) => {
if (matches.length)
Promise.all(promises).then((data) => {
let i = 0;
_.output = _.output.replace(regexInline, (match) => {
if (_.options.link)
return !_.options.inlineText ? data[i] + '' : match + data[i++];
else
return !_.options.inlineText ? data[i] : match + data[i++];
});
resolve(_.output)
});
else
resolve(_.output)
})
}
function getNormalData(_, urlToText, match) {
let url = match[0];
if (_.options.served.indexOf(url) >= 0) return;
return new Promise((resolve) => {
urlToText(_, match, url, true).then(function (text) {
if (!text) resolve();
_.options.served.push(url);
_.embeds.push({
text : text,
index: match.index
});
resolve()
})
})
}
/**
* A helper function for normal embedding
* @param {object} _
* @param {function} urlToText
* @return {Promise}
*/
function normalAsyncEmbed(_, urlToText) {
let match, promises = [];
while ((match = matches(_.regex, _.input)) !== null)
promises.push(getNormalData(_, urlToText, match));
return new Promise(function (resolve) {
Promise.all(promises).then(function () {
resolve(_.embeds)
});
})
}
function asyncEmbed(_, urlToText) {
return new Promise(function (resolve) {
if (ifInline(_.options, _.service))
inlineAsyncEmbed(_, urlToText).then((output) => resolve([output, _.embeds]));
else
normalAsyncEmbed(_, urlToText).then((embeds) => resolve([_.output, embeds]))
})
}
class Twitter {
constructor(input, output, options, embeds) {
this.input = input;
this.output = output;
this.options = options;
this.embeds = embeds;
this.regex = regex.twitter;
this.service = 'twitter';
this.load = this.load.bind(this);
if(typeof this.options.input !== 'string'){
this.options.input.addEventListener('rendered', this.load, false);
}
}
/**
* Fetches the data from twitter's oEmbed API
* @param {string} url URL of the tweet
* @return {object} data containing the tweet info
*/
tweetData(url) {
let config = this.options.tweetOptions;
let apiUrl = `https://api.twitter.com/1/statuses/oembed.json?omit_script=true&url=${url}&maxwidth=${config.maxWidth}&hide_media=${config.hideMedia}&hide_thread=${config.hideThread}&align=${config.align}&lang=${config.lang}`;
return new Promise((resolve) => {
fetchJsonp$1(apiUrl, {credentials: 'include'})
.then((data)=>data.json())
.then((json)=>resolve(json))
})
}
/**
* Load twitter widgets
* @return null
*/
load() {
const twitter = this.options.plugins.twitter;
twitter.widgets.load(this.options.element); //here this refers to the element
//Execute the function after the widget is loaded
twitter.events.bind('loaded', this.options.onTweetsLoad);
}
static urlToText(_this, match, url) {
return new Promise((resolve) => _this.tweetData(url).then((data) => resolve(data.html)))
}
process() {
return new Promise((resolve) => asyncEmbed(this, Twitter.urlToText).then((data) => resolve(data)))
}
}
(function(self) {
'use strict';
if (self.fetch) {
return
}
function normalizeName(name) {
if (typeof name !== 'string') {
name = String(name)
}
if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) {
throw new TypeError('Invalid character in header field name')
}
return name.toLowerCase()
}
function normalizeValue(value) {
if (typeof value !== 'string') {
value = String(value)
}
return value
}
function Headers(headers) {
this.map = {}
if (headers instanceof Headers) {
headers.forEach(function(value, name) {
this.append(name, value)
}, this)
} else if (headers) {
Object.getOwnPropertyNames(headers).forEach(function(name) {
this.append(name, headers[name])
}, this)
}
}
Headers.prototype.append = function(name, value) {
name = normalizeName(name)
value = normalizeValue(value)
var list = this.map[name]
if (!list) {
list = []
this.map[name] = list
}
list.push(value)
}
Headers.prototype['delete'] = function(name) {
delete this.map[normalizeName(name)]
}
Headers.prototype.get = function(name) {
var values = this.map[normalizeName(name)]
return values ? values[0] : null
}
Headers.prototype.getAll = function(name) {
return this.map[normalizeName(name)] || []
}
Headers.prototype.has = function(name) {
return this.map.hasOwnProperty(normalizeName(name))
}
Headers.prototype.set = function(name, value) {
this.map[normalizeName(name)] = [normalizeValue(value)]
}
Headers.prototype.forEach = function(callback, thisArg) {
Object.getOwnPropertyNames(this.map).forEach(function(name) {
this.map[name].forEach(function(value) {
callback.call(thisArg, value, name, this)
}, this)
}, this)
}
function consumed(body) {
if (body.bodyUsed) {
return Promise.reject(new TypeError('Already read'))
}
body.bodyUsed = true
}
function fileReaderReady(reader) {
return new Promise(function(resolve, reject) {
reader.onload = function() {
resolve(reader.result)
}
reader.onerror = function() {
reject(reader.error)
}
})
}
function readBlobAsArrayBuffer(blob) {
var reader = new FileReader()
reader.readAsArrayBuffer(blob)
return fileReaderReady(reader)
}
function readBlobAsText(blob) {
var reader = new FileReader()
reader.readAsText(blob)
return fileReaderReady(reader)
}
var support = {
blob: 'FileReader' in self && 'Blob' in self && (function() {
try {
new Blob();
return true
} catch(e) {
return false
}
})(),
formData: 'FormData' in self,
arrayBuffer: 'ArrayBuffer' in self
}
function Body() {
this.bodyUsed = false
this._initBody = function(body) {
this._bodyInit = body
if (typeof body === 'string') {
this._bodyText = body
} else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
this._bodyBlob = body
} else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
this._bodyFormData = body
} else if (!body) {
this._bodyText = ''
} else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body)) {
// Only support ArrayBuffers for POST method.
// Receiving ArrayBuffers happens via Blobs, instead.
} else {
throw new Error('unsupported BodyInit type')
}
if (!this.headers.get('content-type')) {
if (typeof body === 'string') {
this.headers.set('content-type', 'text/plain;charset=UTF-8')
} else if (this._bodyBlob && this._bodyBlob.type) {
this.headers.set('content-type', this._bodyBlob.type)
}
}
}
if (support.blob) {
this.blob = function() {
var rejected = consumed(this)
if (rejected) {
return rejected
}
if (this._bodyBlob) {
return Promise.resolve(this._bodyBlob)
} else if (this._bodyFormData) {
throw new Error('could not read FormData body as blob')
} else {
return Promise.resolve(new Blob([this._bodyText]))
}
}
this.arrayBuffer = function() {
return this.blob().then(readBlobAsArrayBuffer)
}
this.text = function() {
var rejected = consumed(this)
if (rejected) {
return rejected
}
if (this._bodyBlob) {
return readBlobAsText(this._bodyBlob)
} else if (this._bodyFormData) {
throw new Error('could not read FormData body as text')
} else {
return Promise.resolve(this._bodyText)
}
}
} else {
this.text = function() {
var rejected = consumed(this)
return rejected ? rejected : Promise.resolve(this._bodyText)
}
}
if (support.formData) {
this.formData = function() {
return this.text().then(decode)
}
}
this.json = function() {
return this.text().then(JSON.parse)
}
return this
}
// HTTP methods whose capitalization should be normalized
var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']
function normalizeMethod(method) {
var upcased = method.toUpperCase()
return (methods.indexOf(upcased) > -1) ? upcased : method
}
function Request(input, options) {
options = options || {}
var body = options.body
if (Request.prototype.isPrototypeOf(input)) {
if (input.bodyUsed) {
throw new TypeError('Already read')
}
this.url = input.url
this.credentials = input.credentials
if (!options.headers) {
this.headers = new Headers(input.headers)
}
this.method = input.method
this.mode = input.mode
if (!body) {
body = input._bodyInit
input.bodyUsed = true
}
} else {
this.url = input
}
this.credentials = options.credentials || this.credentials || 'omit'
if (options.headers || !this.headers) {
this.headers = new Headers(options.headers)
}
this.method = normalizeMethod(options.method || this.method || 'GET')
this.mode = options.mode || this.mode || null
this.referrer = null
if ((this.method === 'GET' || this.method === 'HEAD') && body) {
throw new TypeError('Body not allowed for GET or HEAD requests')
}
this._initBody(body)
}
Request.prototype.clone = function() {
return new Request(this)
}
function decode(body) {
var form = new FormData()
body.trim().split('&').forEach(function(bytes) {
if (bytes) {
var split = bytes.split('=')
var name = split.shift().replace(/\+/g, ' ')
var value = split.join('=').replace(/\+/g, ' ')
form.append(decodeURIComponent(name), decodeURIComponent(value))
}
})
return form
}
function headers(xhr) {
var head = new Headers()
var pairs = xhr.getAllResponseHeaders().trim().split('\n')
pairs.forEach(function(header) {
var split = header.trim().split(':')
var key = split.shift().trim()
var value = split.join(':').trim()
head.append(key, value)
})
return head
}
Body.call(Request.prototype)
function Response(bodyInit, options) {
if (!options) {
options = {}
}
this.type = 'default'
this.status = options.status
this.ok = this.status >= 200 && this.status < 300
this.statusText = options.statusText
this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers)
this.url = options.url || ''
this._initBody(bodyInit)
}
Body.call(Response.prototype)
Response.prototype.clone = function() {
return new Response(this._bodyInit, {
status: this.status,
statusText: this.statusText,
headers: new Headers(this.headers),
url: this.url
})
}
Response.error = function() {
var response = new Response(null, {status: 0, statusText: ''})
response.type = 'error'
return response
}
var redirectStatuses = [301, 302, 303, 307, 308]
Response.redirect = function(url, status) {
if (redirectStatuses.indexOf(status) === -1) {
throw new RangeError('Invalid status code')
}
return new Response(null, {status: status, headers: {location: url}})
}
self.Headers = Headers;
self.Request = Request;
self.Response = Response;
self.fetch = function(input, init) {
return new Promise(function(resolve, reject) {
var request
if (Request.prototype.isPrototypeOf(input) && !init) {
request = input
} else {
request = new Request(input, init)
}
var xhr = new XMLHttpRequest()
function responseURL() {
if ('responseURL' in xhr) {
return xhr.responseURL
}
// Avoid security warnings on getResponseHeader when not allowed by CORS
if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) {
return xhr.getResponseHeader('X-Request-URL')
}
return;
}
xhr.onload = function() {
var status = (xhr.status === 1223) ? 204 : xhr.status
if (status < 100 || status > 599) {
reject(new TypeError('Network request failed'))
return
}
var options = {
status: status,
statusText: xhr.statusText,
headers: headers(xhr),
url: responseURL()
}
var body = 'response' in xhr ? xhr.response : xhr.responseText;
resolve(new Response(body, options))
}
xhr.onerror = function() {
reject(new TypeError('Network request failed'))
}
xhr.open(request.method, request.url, true)
if (request.credentials === 'include') {
xhr.withCredentials = true
}
if ('responseType' in xhr && support.blob) {
xhr.responseType = 'blob'
}
request.headers.forEach(function(value, name) {
xhr.setRequestHeader(name, value)
})
xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
})
}
self.fetch.polyfill = true
})(typeof self !== 'undefined' ? self : this);
/**
* Takes the location name and returns the coordinates of that location using the Google
* Map API v3. This is an async function so it will return a promise.
* @param {string} location The name of any location
* @return {array} Returns an array in the form [latitude, longitude]
*/
function getCoordinate(location) {
let url = `http://maps.googleapis.com/maps/api/geocode/json?address=${location}&sensor=false`;
return new Promise((resolve) => {
fetch(url)
.then((data) => data.json())
.then((json) => resolve([json.results[0].geometry.location.lat, json.results[0].geometry.location.lng]))
})
}
/**
* Returns the template of the Map widget. The source of the iframe is based on whether the
* mode set in options is 'place', 'streetview' or 'view'.
* @param {string} match The matching string in the form of @(location name).
* @param {number} latitude Latitude of the location
* @param {number} longitude Longitude of the location
* @param {object} options plugin options
* @return {string} Template of the map widget.
*/
function template$1(match, latitude, longitude, options) {
const location = locationText(match);
return options.template.gmap(latitude, longitude, location, options);
}
/**
* Extracts out the location name from the format @(locationName)
* @param {string} match The string in the supported format. Eg : @(Delhi)
* @return {string} Only the location name removing @ and brackets. Eg: Delhi
*/
function locationText(match) {
return match.split('(')[1].split(')')[0]
}
function gmap (input, output, options, embeds) {
let match, promises = [], allMatches = [];
const service = 'map';
while ((match = matches(regex.gmap, output)) !== null) {
options.served.push(match);
const promise = options.mapOptions.mode !== 'place' ? getCoordinate(match[0]) : Promise.resolve([null, null]);
promises.push(promise);
allMatches.push(match)
}
return new Promise((resolve) => {
Promise.all(promises).then((coordinatesArr) => {
for (var i in promises) {
let [latitude, longitude] = coordinatesArr[i];
let text = template$1((allMatches[i])[0], latitude, longitude, options);
if (ifInline(options, service)) {
output = output.replace(regex.gmap, (regexMatch) => {
return `${locationText(regexMatch)}${text}`
})
} else {
embeds.push({
text : text,
index: allMatches[i][0].index
});
output = output.replace(regex.gmap, (regexMatch) => {
return `${locationText(regexMatch)}`
});
}
}
resolve([output, embeds])
})
})
}
function markdown (output, options) {
if (!options.plugins.marked) throw new ReferenceError(`marked.js is not loaded.`);
const Marked = options.plugins.marked;
const renderer = new Marked.Renderer();
renderer.link = (href, title, text) => {
if (href.indexOf('</a') === -1) return href;
if (href.match(/>(.+)<\/a/gi)) {
if (!title) title = '';
return `${text}`
}
};
renderer.image = (href, title, text) => {
if (href.match(/>(.+)<\/a/gi)) {
if (!title) title = '';
return ``
}
return ``
};
renderer.paragraph = (text) => ` ${text}
`; //for font smiley in end.
//Fix for heading that should be actually present in marked.js
//if gfm is true the `## Heading` is acceptable but `##Heading` is not
Marked.Lexer.rules.gfm.heading = Marked.Lexer.rules.normal.heading;
Marked.Lexer.rules.tables.heading = Marked.Lexer.rules.normal.heading;
options.markedOptions.renderer = renderer;
options.markedOptions.highlight = false;
return Marked(output, options.markedOptions)
}
/**
* Encodes the characters like <, > and space and replaces them with
* <, > and > respectively.
* @param {string} code The string that has to be encoded.
* @return {string} The encoded string
*/
function encode(code) {
code = code.replace(/&/gm, '');
code = code.replace(/</g, '<');
code = code.replace(/>/g, '>');
return code;
}
/**
* removes whitespace characters
* @param {string} code The string from which the whitespace has to be removed
* @return {string}
*/
function trimSpace(code) {
code = code.replace(/^([ \t]*)/g, ''); // leading whitespace
code = code.replace(/[ \t]*$/g, ''); // trailing whitespace
return code;
}
/**
* Places the code and the language name in the required template
* @param {string} processedCode
* @param {string} language
* @return {string}
*/
function addTemplate(processedCode, language) {
return `${processedCode.value || processedCode}
`
}
/**
* Replaces the code block with the pre tags and returns a string having the code
* formatting using Highlight.js.
* => Matches the string with the regex and finds the code written in three back-ticks ```
* => Detects whether any language has been provided by the user.
* The format supported by embed.js is
* ```[language-name]
* var a = 2;
* ```
* => Trims all the unnecessary spaces and newlines from the code.
* => Passes the code to `hljs.highlightAuto(code, language)` which returns a formatted string
* having the html tags for styling. The `language` here is optional. In case we don't pass the
* language, it tries to detect the language itself.
* => Replaces the code string in the template with the formatted string
* @return {string} The string in which the code is formatted
*/
function highlight (output, options) {
output = output.replace(regex.inlineCode, function (match, group1, group2) {
return `${group2}`
});
return output.replace(regex.highlightCode, (match, group1, group2, group3) => {
let code = group3;
code = trimSpace(code);
code = encode(code);
// to prevent auto-linking. Not necessary in code
// *blocks*, but in code spans. Will be converted
// back after the auto-linker runs.
code = code.replace(/:\/\//g, '~P');
let language = group2.split('\n')[0];
let highlightedCode;
const HighlightJS = options.plugins.highlightjs;
if (language) {
highlightedCode = HighlightJS.highlightAuto(code, [language]);
} else {
highlightedCode = HighlightJS.highlightAuto(code);
language = highlightedCode.language;
}
return addTemplate(highlightedCode, language);
});
}
class Gist {
constructor(input, output, options, embeds) {
this.input = input;
this.output = output;
this.options = options;
this.embeds = embeds;
this.regex = regex.gist;
this.service = 'gist';
if(typeof this.options.input !== 'string'){
this.options.input.addEventListener('rendered', () => {
this.load()
})
}
}
template(match) {
return ``
}
load() {
let gists = this.options.input.getElementsByClassName('ejs-gist');
for (let i = 0; i < gists.length; i++) {
let gistFrame = document.createElement("iframe");
gistFrame.setAttribute("width", "100%");
gistFrame.id = `ejs-gist-${i}`;
let zone = gists[i];
zone.innerHTML = "";
zone.appendChild(gistFrame);
// Create the iframe's document
let url = gists[i].getAttribute('data-src');
url = url.indexOf('http') === -1 ? `https://${url}` : url;
let gistFrameHTML = ``;
// Set iframe's document with a trigger for this document to adjust the height
let gistFrameDoc = gistFrame.document;
if (gistFrame.contentDocument) {
gistFrameDoc = gistFrame.contentDocument;
} else if (gistFrame.contentWindow) {
gistFrameDoc = gistFrame.contentWindow.document;
}
gistFrameDoc.open();
gistFrameDoc.writeln(gistFrameHTML);
gistFrameDoc.close();
}
}
process() {
return embed(this);
}
}
function formatData(data) {
return {
title : data.snippet.title,
thumbnail : data.snippet.thumbnails.medium.url,
rawDescription: data.snippet.description,
views : data.statistics.viewCount,
likes : data.statistics.likeCount,
description : truncate(data.snippet.description, 150),
url : `https://www.youtube.com/watch?v=${data.id}`,
id : data.id,
host : 'youtube'
}
}
function data(id, options) {
let url = `https://www.googleapis.com/youtube/v3/videos?id=${id}&key=${options.googleAuthKey}&part=snippet,statistics`;
return new Promise((resolve) => {
fetch(url)
.then((data) => data.json())
.then((json) => resolve(json.items[0]))
})
}
function urlToText(args, match, url, normalEmbed) {
let id = normalEmbed ? match[1] : match[2];
let embedUrl = `https://www.youtube.com/embed/${id}`;
if (args.options.videoDetails) {
return new Promise((resolve) => {
data(id, args.options).then((data) => resolve(getDetailsTemplate(formatData(data), data, embedUrl, args.options)))
})
} else {
return new Promise((resolve) => resolve(template(embedUrl, args.options)))
}
}
function youtube (input, output, options, embeds) {
const args = {
input,
output,
options,
embeds,
regex : regex.youtube,
service: 'youtube'
};
return new Promise((resolve) => asyncEmbed(args, urlToText).then((data) => resolve(data)))
}
function formatData$1(data, truncate) {
return {
title : data.title,
thumbnail : data.thumbnail_medium,
rawDescription: data.description.replace(/\n/g, '
').replace(/
/g, '
'),
views : data.stats_number_of_plays,
likes : data.stats_number_of_likes,
description : truncate(data.description.replace(/((<|<)br\s*\/*(>|>)\r\n)/g, ' '), 150),
url : data.url,
id : data.id,
host : 'vimeo'
}
}
function data$1(id) {
let url = `https://vimeo.com/api/v2/video/${id}.json`;
return new Promise((resolve) => {
fetch(url)
.then((data) => data.json())
.then((json) => resolve(json[0]))
})
}
function urlToText$1(args, match, url, normalEmbed) {
let id;
if (!normalEmbed) {
id = args.options.link ? match[0].slice(0, -4).split('/').slice(-1).pop() : match[0].split('/').slice(-1).pop();
} else {
id = match[3]
}
if (!id) return;
let embedUrl = `https://player.vimeo.com/video/${id}`;
if (args.options.videoDetails) {
return new Promise((resolve) => {
data$1(id).then((data) => resolve(getDetailsTemplate(formatData$1(data, truncate), data, embedUrl, args.options)))
})
} else {
return new Promise((resolve) => resolve(template(embedUrl, args.options)))
}
}
function vimeo (input, output, options, embeds) {
const args = {
input,
output,
options,
embeds,
regex : regex.vimeo,
service: 'vimeo'
};
return new Promise((resolve) => asyncEmbed(args, urlToText$1).then((data) => resolve(data)))
}
function fetchData(args, url) {
let api = `http://www.slideshare.net/api/oembed/2?url=${url}&format=jsonp&maxwidth=${args.options.videoWidth}&maxheight=${args.options.videoHeight}`;
return new Promise((resolve) => {
fetchJsonp$1(api, {credentials: 'include'})
.then((data) => data.json())
.then((json) => resolve(json.html))
})
}
function urlToText$2(args, match, url) {
return new Promise((resolve) => {
fetchData(args, url).then((html) => resolve(args.template(html)))
})
}
function slideShare (input, output, options, embeds) {
const args = {
input, output, options, embeds,
regex: regex.slideShare,
service: 'slideshare',
template(html) {
return this.options.template.slideShare(html, this.options)
}
};
return new Promise((resolve) => asyncEmbed(args, urlToText$2).then((data) => resolve(data)))
}
function fetchData$1(url, _) {
url = encodeURIComponent(url);
let api = new Function('url', 'return `' + _.options.openGraphEndpoint + '`')(url);
return new Promise((resolve) => {
fetch(api)
.then((res)=>res.json())
.then((json)=>resolve(_.options.onOpenGraphFetch(json) || json))
})
}
function urlToText$3(_, match, url) {
if (url.match(_.excludeRegex)) return Promise.resolve();
return new Promise((resolve) => {
fetchData$1(url, _).then((data) => resolve(data && data.success ? _.template(data) : ''))
})
}
function openGraph (input, output, options, embeds) {
const args = {
input,
output,
options,
embeds,
service : 'opengraph',
regex : urlRegex(),
excludeRegex: new RegExp(['.mp4|.mp3|.gif|.pdf|.doc|.ppt|.docx|.jpg|.jpeg|.ogg'].concat(options.openGraphExclude).join('|'), 'gi'),
template(data) {
return this.options.template.openGraph(data, this.options)
}
};
return new Promise((resolve) => asyncEmbed(args, urlToText$3).then((data) => resolve(data)))
}
function template$2(data, options) {
return options.template.github(data, options);
}
function fetchRepo(data) {
let api = `https://api.github.com/repos/${data.user}/${data.repo}`;
return new Promise((resolve) => {
fetch(api)
.then(function (data) {
return data.json()
})
.then(function (json) {
return resolve(json)
})
})
}
function urlToText$4(_this, match, url, normalEmbed) {
let data = !normalEmbed ? ({
user: match[2],
repo: match[3]
}) : ({
user: match[1],
repo: match[2]
});
if (!data.repo) return;
return new Promise(function (resolve) {
fetchRepo(data)
.then(function (response) {
return resolve(template$2(response, _this.options))
})
})
}
function github (input, output, options, embeds) {
const args = {
input, output, options, embeds,
service: 'github',
regex : regex.github
};
return new Promise((resolve) => asyncEmbed(args, urlToText$4).then((data) => resolve(data)))
}
function mentions (input, options) {
const mRegex = regex.mentions;
return input.replace(mRegex,(match) => {
const username = match.split('@')[1];
return options.mentionsUrl(username);
})
}
function hashtag (input, options) {
const hRegex = regex.hashtag;
return input.replace(hRegex,(match)=>{
const username = match.split('#')[1];
return options.hashtagUrl(username);
})
}
var globalOptions = {};
var defaultOptions = {
marked : false,
markedOptions : {},
link : true,
linkOptions : {
target : 'self',
exclude: ['pdf'],
rel : ''
},
emoji : true,
customEmoji : [],
fontIcons : true,
customFontIcons : [],
highlightCode : false,
mentions : false,
hashtag : false,
videoJS : false,
videojsOptions : {
fluid : true,
preload: 'metadata'
},
plyr : false,
plyrOptions : {},
locationEmbed : true,
mapOptions : {
mode: 'place'
},
tweetsEmbed : false,
tweetOptions : {
maxWidth : 550,
hideMedia : false,
hideThread: false,
align : 'none',
lang : 'en'
},
singleEmbed : false,
openGraphEndpoint : null,
openGraphExclude : [],
videoEmbed : true,
videoHeight : null,
videoWidth : null,
videoDetails : true,
audioEmbed : true,
imageEmbed : true,
excludeEmbed : [],
inlineEmbed : [],
inlineText : true,
codeEmbedHeight : 500,
vineOptions : {
maxWidth : null,
type : 'postcard', //'postcard' or 'simple' embedding
responsive: true,
width : 350,
height : 460
},
plugins : {
marked : window.marked,
videojs : window.videojs,
plyr : window.plyr,
highlightjs: window.hljs,
prismjs : window.Prism,
twitter : window.twttr
},
googleAuthKey : '',
soundCloudOptions : {
height : 160,
themeColor : 'f50000', //Hex Code of the player theme color
autoPlay : false,
hideRelated : false,
showComments: true,
showUser : true,
showReposts : false,
visual : false, //Show/hide the big preview image
download : false //Show/Hide download buttons
},
videoClickClass : 'ejs-video-thumb',
customVideoClickHandler: false,
mentionsUrl : function () {
},
hashtagUrl : function () {
},
beforeEmbedJSApply : function () {
},
afterEmbedJSApply : function () {
},
onVideoShow : function () {
},
onTweetsLoad : function () {
},
videojsCallback : function () {
},
onOpenGraphFetch : function () {
},
onOpenGraphFail : function () {
},
videoClickHandler : function () {
},
served : [] //Private variable used to store processed urls so that they are not processed multiple times.
};
let instances = [];
let allInstances = [];
let promises = [];
class EmbedJS {
/**
* The constructor takes two arguements. The first one is the options object and the second one is the
* optional string . If the user wants to provide a string directly instead of the element, he can do that.
* In case the user provides both the input element and the string, the input string will be taken from the element
* and the provided string won't be processed.
*
* @param {object} options The options object
* @param template
* @return {null}
*/
constructor(options, template) {
/**
* We have created a clone of the original options to make sure that the original object
* isn't altered.
*/
let defOpts = cloneObject(defaultOptions);
let globOpts = cloneObject(globalOptions);
//merge global options with the default options
let globOptions = deepExtend(defOpts, globOpts);
//deepExtend global options with the overriding options provided by the user as an options
//object while creating a new instance of embed.js
this.options = deepExtend(globOptions, options);
this.options.template = template || renderer;
if (!this.options.input || !(typeof this.options.input === 'string' || typeof this.options.input === 'object')) throw ReferenceError("You need to pass an element or the string that needs to be processed");
this.input = (typeof this.options.input === 'object') ? this.options.input.innerHTML : this.options.input
}
/**
* Processes the string and performs all the insertions and manipulations based on
* the options and the input provided by the user. This returns a promise which is resolved once the result data is ready
* @return {Promise} The processes resulting string
*/
process() {
const input = this.input;
const options = processOptions(this.options);
let embeds = [];
let output = input;
this.options.beforeEmbedJSApply();
return new Promise((resolve) => {
if (options.link)
output = url(input, options);
const openGraphPromise = options.openGraphEndpoint ? openGraph(input, output, options, embeds) : Promise.resolve([output, embeds]);
openGraphPromise.then(function ([output, embeds]) {
if (options.highlightCode) {
output = highlight(output, options)
}
if (options.marked) {
output = markdown(output, options)
}
if (options.emoji) {
output = emoji(output, options)
}
if (options.fontIcons) {
output = smiley(output, options)
}
if (options.mentions) {
output = mentions(output, options);
}
if (options.hashtag) {
output = hashtag(output, options);
}
[output, embeds] = baseEmbed(input, output, embeds, options, regex.ideone, 'ideone');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.plunker, 'plunker');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.jsbin, 'jsbin');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.codepen, 'codepen');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.jsfiddle, 'jsfiddle');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.ted, 'ted');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.dailymotion, 'dailymotion');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.ustream, 'ustream');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.liveleak, 'liveleak');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.basicVideo, 'video', options.videoEmbed);
[output, embeds] = baseEmbed(input, output, embeds, options, regex.vine, 'vine');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.soundCloud, 'soundcloud');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.spotify, 'spotify');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.basicAudio, 'audio', options.audioEmbed);
[output, embeds] = baseEmbed(input, output, embeds, options, regex.flickr, 'flickr');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.instagram, 'instagram');
[output, embeds] = baseEmbed(input, output, embeds, options, regex.basicImage, 'image', options.imageEmbed);
if (ifEmbed(options, 'gist')) {
[output, embeds] = new Gist(input, output, options, embeds).process()
}
return ifEmbed(options, 'youtube') ? youtube(input, output, options, embeds) : Promise.resolve([output, embeds]);
}).then(function ([output, embeds]) {
return ifEmbed(options, 'vimeo') ? vimeo(input, output, options, embeds) : Promise.resolve([output, embeds]);
}).then(function ([output, embeds]) {
return ifEmbed(options, 'github') ? github(input, output, options, embeds) : Promise.resolve([output, embeds]);
}).then(function ([output, embeds]) {
return options.locationEmbed && ifEmbed(options, 'gmap') ? gmap(input, output, options, embeds) : Promise.resolve([output, embeds])
}).then(function ([output, embeds]) {
return ifEmbed(options, 'slideshare') ? slideShare(input, output, options, embeds) : Promise.resolve([output, embeds]);
}).then(([output, embeds]) => {
if (options.tweetsEmbed && ifEmbed(options, 'twitter')) {
this.twitter = new Twitter(input, output, options, embeds);
return this.twitter.process()
} else {
return Promise.resolve([output, embeds])
}
}).then(([output, embeds]) => {
this.data = {
input : options.input,
output : output,
options : options,
inputString: this.input,
/**
TODO:
- Restructure served urls structure with services name
*/
services : options.served,
template : options.template
};
resolve(createText(output, embeds))
})
})
}
/**
* First processes the data by calling the .process() and then renders the data in the div
* => Loads the twitter widgets
* => Executes the onTweetsLoad() once all the tweets have been rendered
* => Applies video.js on the media (both audio and video)
* => Triggers video loading on click of the video preview
* => Executes afterEmbedJSApply() once everything is done.
*
* @return Promise
*/
render() {
if (typeof this.options.input === 'string') throw new Error(`You cannot call render method for a string`);
if (!this.options.input) throw new Error(`You didn't pass an element while creating this instance. render() method can't work without an input element`);
return new Promise((resolve) => {
this.process().then((data) => {
this.options.input.innerHTML = data;
this.applyListeners();
resolve(this.data);
})
})
}
/**
* This method listens to all the events like click, handle
* events to be done after an element has been rendered. These
* include twitter widget rendering, gist embedding, click event listeners .
*/
applyListeners() {
applyVideoJS(this.options);
applyPlyr(this.options);
playVideo(this.options);
let event = new Event('rendered');
this.options.input.dispatchEvent(event);
this.options.afterEmbedJSApply();
}
/**
* This function updates the parametrs of the current instance
* @param options New updated options object. will be extended with the older options
* @param template [optional] the new template instance
*/
update(options, template) {
if (options)
this.options = deepExtend(this.options, options);
if (template)
this.options.template = template;
if (!this.options.input || !(typeof this.options.input === 'string' || typeof this.options.input === 'object')) throw ReferenceError("You need to pass an element or the string that needs to be processed");
this.input = (typeof this.options.input === 'object') ? this.options.input.innerHTML : this.options.input
}
/**
* returns the resulting string based on the input and the options passed by the user.
* @return Promise
*/
text(callback) {
this.process().then((data)=> callback(data, this.input))
}
/**
* The destroy method destroys all the listeners and replaces the rih text with the original text in the
* element.
* @return {null}
*/
destroy() {
if (typeof this.options.input !== 'object') throw new Error(`destroy() method only works if an element had been passed in the options object`);
destroyVideos('ejs-video-thumb');
this.options.input.removeEventListener('rendered', this.twitter.load(), false);
this.options.input.innerHTML = this.input
}
/**
* Sets options globally
* @param {object} options
*/
static setOptions(options) {
globalOptions = deepExtend(defaultOptions, options)
}
/**
* Applies embed.js to all the elements with the class name provided as option
* @return {Promise}
* @param selectorName
* @param options
* @param template
*/
static applyEmbedJS(selectorName, options = {}, template = renderer) {
let elements = document.querySelectorAll(selectorName);
for (let i = 0; i < elements.length; i++) {
options.input = elements[i];
instances[i] = new EmbedJS(options, template);
promises[i] = instances[i].render()
}
return new Promise(function (resolve) {
Promise.all(promises).then(function (val) {
resolve(val);
})
})
}
/**
* Destroys all the instances of EmbedJS created by using applyEmbedJS() method.
* @return {null}
*/
static destroyEmbedJS() {
for (let i = 0; i < instances.length; i++) {
instances[i].destroy()
}
}
/**
* Destroys all instances of EmbedJS on the page
* @return {null}
*/
static destroyAll() {
for (let i = 0; i < allInstances.length; i++) {
allInstances[i].destroy()
}
}
/**
* Creates a new instance of the Template constructor. This has been done so that multiple
* templates of a single service can be used by creating different instances of the Template.
*
* The usage of the plugin is described below.
*
* => Create a new Instance of the template by using .Template() method of EmbedJS.
*
* var template = EmbedJS.Template()
*
* => Now create different templates for different service names.
*
* template.url = function(match, options){
* return ' + match + '
* }
*
* template.instagram = function(match, dimensions, options){
* var config = options.soundCloudOptions;
* return ``;
* }
*
*/
static Template() {
return renderer;
}
}
export default EmbedJS;
//# sourceMappingURL=embed.es2015.js.map