Merge pull request #104 from codex-team/youtube-plugin-added

Youtube plugin added
This commit is contained in:
khaydarov 2017-01-26 18:21:48 +03:00 committed by GitHub
commit 67ecaf3932
16 changed files with 2622 additions and 2350 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -42,7 +42,11 @@
<script src="plugins/twitter/twitter.js"></script>
<link rel="stylesheet" href="plugins/twitter/twitter.css">
<script src="plugins/embed/embed.js"></script>
<link rel="stylesheet" href="plugins/embed/embed.css">
<script src="plugins/paste/paste.js"></script>
<script src="plugins/paste/patterns.js"></script>
<link rel="stylesheet" href="plugins/paste/paste.css">
<script>
@ -71,16 +75,11 @@
},
paste: {
type: 'paste',
iconClassname: '',
prepare: pasteTool.prepare,
make: pasteTool.make,
appendCallback: null,
settings: null,
render: null,
save: pasteTool.save,
prepare: paste.prepare,
make: paste.make,
save: paste.save,
enableLineBreaks: false,
callbacks: pasteTool.callbacks,
allowedToPaste: false
callbacks: paste.pasted
},
code: {
type: 'code',
@ -166,15 +165,19 @@
type: 'twitter',
iconClassname: 'ce-icon-twitter',
prepare: twitter.prepare,
make: twitter.make,
appendCallback: null,
settings: null,
render: twitter.render,
validate: twitter.validate,
save: twitter.save,
displayInToolbox: false,
enableLineBreaks: false,
allowedToPaste: false
config : {
fetchUrl : ''
}
},
embed: {
type: 'embed',
make: embed.make,
render: embed.render,
save: embed.save,
validate: embed.validate,
}
},
data : {

View file

@ -263,6 +263,9 @@ var content = (function(content) {
} else {
if (currentInputIndex === codex.state.inputs.length - 1)
return;
/** Timeout for browsers execution */
setTimeout(function () {

View file

@ -121,7 +121,7 @@ var toolbox = (function(toolbox) {
/**
* UNREPLACEBLE_TOOLS this types of tools are forbidden to replace even they are empty
*/
var UNREPLACEBLE_TOOLS = ['image', 'link', 'list', 'instagram', 'twitter'],
var UNREPLACEBLE_TOOLS = ['image', 'link', 'list', 'instagram', 'twitter', 'embed'],
tool = codex.tools[codex.toolbar.current],
workingNode = codex.content.currentNode,
currentInputIndex = codex.caret.inputIndex,

12
plugins/embed/embed.css Normal file
View file

@ -0,0 +1,12 @@
.ce-redactor .embed {
max-width: 600px;
margin: 15px 0;
background: #fff;
}
.embed__loader {
background-image: url("loading.gif") !important;
background-repeat: no-repeat;
background-position: center center;
opacity: 0.5;
}

208
plugins/embed/embed.js Normal file
View file

@ -0,0 +1,208 @@
/**
* Embed plugin by gohabereg
* @version 1.0.0
*/
var embed = function(embed){
var methods = {
addInternal: function (content) {
codex.content.switchBlock(codex.content.currentNode, content, 'video_extended');
var blockContent = codex.content.currentNode.childNodes[0];
blockContent.classList.add('embed__loader');
setTimeout(function(){
blockContent.classList.remove('embed__loader');
}, 1000);
},
getHtmlWithEmbedId: function (type, id) {
return services[type].html.replace(/<\%\= remote\_id \%\>/g, id);
},
makeElementFromHtml: function(html) {
var wrapper = document.createElement('DIV');
wrapper.innerHTML = html;
return wrapper;
},
getRemoteId: function(source, execArray) {
switch(source) {
case 'yandex-music-track':
id = execArray[2]+'/'+execArray[1];
break;
case 'yandex-music-playlist':
id = execArray[1]+'/'+execArray[2];
break;
default:
id = execArray[1];
}
return id;
}
};
var services = {
vimeo: {
regex: /(?:http[s]?:\/\/)?(?:www.)?vimeo\.co(?:.+\/([^\/]\d+)(?:#t=[\d]+)?s?$)/,
html: "<iframe src=\"https://player.vimeo.com/video/<%= remote_id %>?title=0&byline=0\" width=\"580\" height=\"320\" frameborder=\"0\"></iframe>",
height: 320,
width: 580
},
youtube: {
regex: /^.*(?:(?:youtu\.be\/)|(?:youtube\.com)\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*)(?:[\?\&]t\=(\d*)|)/,
html: "<iframe src=\"https://www.youtube.com/embed/<%= remote_id %>\" width=\"580\" height=\"320\" frameborder=\"0\" allowfullscreen></iframe>",
height: 320,
width: 580
},
vk : {
regex: /^https?.+vk?.com\/feed\?w=wall\d+_\d+/,
html: "<iframe src=\"https://tjournal.ru/proxy/video/<%= remote_id %>?rel=0&showinfo=0&enablejsapi=1&autoplay=1\" width=\"580\" height=\"320\" frameborder=\"0\" allowfullscreen></iframe>"
},
coub: {
regex: /https?:\/\/coub\.com\/view\/([^\/\?\&]+)/,
html: "<iframe src=\"//coub.com/embed/<%= remote_id %>\" width=\"580\" height=\"320\" frameborder=\"0\" allowfullscreen></iframe>",
height: 320,
width: 580
},
vine: {
regex: /https?:\/\/vine\.co\/v\/([^\/\?\&]+)/,
html: "<iframe src=\"https://vine.co/v/<%= remote_id %>/embed/simple/\" width=\"580\" height=\"320\" frameborder=\"0\" allowfullscreen></iframe>",
height: 320,
width: 580
},
imgur: {
regex: /https?:\/\/(?:i\.)?imgur\.com.*\/([a-zA-Z0-9]+)(?:\.gifv)?/,
html: "<iframe allowfullscreen=\"true\" scrolling=\"no\" src=\"http://imgur.com/<%= remote_id %>/embed\" id=\"imgur-embed-iframe-pub-<%= remote_id %>\" class=\"imgur-embed-iframe-pub\" style=\"height: 500px; width: 540px; border: 1px solid #000\"></iframe>",
height: 500,
width: 540
},
gfycat: {
regex: /https?:\/\/gfycat\.com(?:\/detail)?\/([a-zA-Z]+)/,
html: "<iframe src='https://gfycat.com/ifr/<%= remote_id %>' frameborder='0' scrolling='no' width='580' height='436' allowfullscreen ></iframe>",
height: 436,
width: 580
},
'twitch-channel': {
regex: /https?:\/\/twitch.tv\/([^\/\?\&]*)/,
html: "<iframe src=\"https://player.twitch.tv/?channel=<%= remote_id %>\" frameborder=\"0\" allowfullscreen=\"true\" scrolling=\"no\" height=\"366\" width=\"600\"></iframe>",
height: 366,
width: 600
},
'twitch-video': {
regex: /https?:\/\/www.twitch.tv\/[^\/\?\&]*\/v\/([0-9]*)/,
html: "<iframe src=\"https://player.twitch.tv/?video=v<%= remote_id %>\" frameborder=\"0\" allowfullscreen=\"true\" scrolling=\"no\" height=\"366\" width=\"600\"></iframe>",
height: 366,
width: 600
},
'yandex-music-album': {
regex: /https?:\/\/music.yandex.ru\/album\/([0-9]*)/,
html: "<iframe frameborder=\"0\" style=\"border:none;width:540px;height:400px;\" width=\"540\" height=\"400\" src=\"https://music.yandex.ru/iframe/#album/<%= remote_id %>/\"></iframe>",
height: 400,
width: 540
},
'yandex-music-track': {
regex: /https?:\/\/music.yandex.ru\/album\/([0-9]*)\/track\/([0-9]*)/,
html: "<iframe frameborder=\"0\" style=\"border:none;width:540px;height:100px;\" width=\"540\" height=\"100\" src=\"https://music.yandex.ru/iframe/#track/<%= remote_id %>/\"></iframe>",
height: 100,
width: 540
},
'yandex-music-playlist': {
regex: /https?:\/\/music.yandex.ru\/users\/([^\/\?\&]*)\/playlists\/([0-9]*)/,
html: "<iframe frameborder=\"0\" style=\"border:none;width:540px;height:400px;\" width=\"540\" height=\"400\" src=\"https://music.yandex.ru/iframe/#playlist/<%= remote_id %>/show/cover/description/\"></iframe>",
height: 400,
width: 540
}
};
embed.make = function(data, isInternal) {
if (!data.remote_id)
return;
var html = methods.getHtmlWithEmbedId(data.source, data.remote_id),
block = methods.makeElementFromHtml(html);
block.dataset.remoteId = data.remote_id;
block.dataset.source = data.source;
block.dataset.thumbnailUrl = data.thumbnailUrl;
block.classList.add('embed');
var sidePadding = (600 - services[data.source].width) / 2 + 'px';
block.style.padding = '30px ' + sidePadding;
if (isInternal) {
methods.addInternal(block);
}
return block;
};
/**
* Saving JSON output.
* Upload data via ajax
*/
embed.save = function(blockContent) {
if (!blockContent)
return;
var data,
source = blockContent.dataset.source;
data = {
source: source,
remote_id: blockContent.dataset.remoteId,
thumbnailUrl: blockContent.dataset.thumbnailUrl,
height: services[source].height,
width: services[source].width
};
return data;
};
/**
* Render data
*/
embed.render = function(data) {
return embed.make(data);
};
embed.urlPastedCallback = function(url, pattern) {
var execArray = pattern.regex.exec(url),
id = methods.getRemoteId(pattern.type, execArray);
var data = {
source: pattern.type,
remote_id: id,
thumbnailUrl: url
};
embed.make(data, true);
};
embed.validate = function(savedData) {
var source = savedData.source,
execArray = services[source].regex.exec(savedData.thumbnailUrl),
remoteId = methods.getRemoteId(source, execArray);
return remoteId == savedData.remote_id;
};
return embed;
}({});

BIN
plugins/embed/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -623,6 +623,85 @@ var image = (function(image) {
*/
image.uploadImageFromUri = uploadingCallbacks_.ByPaste.uploadImageFromUrl;
image.urlPastedCallbacks = {
/**
* Upload image by URL
*
* @uses codex Image tool
* @param filename
* @returns {Element}
*/
uploadedImage : function(filename) {
var data = {
background: false,
border: false,
isStretch: false,
file: {
url: "upload/redactor_images/" + filename,
bigUrl: "upload/redactor_images/" + filename,
width: null,
height: null,
additionalData: "null"
},
caption: '',
cover: null
};
/** Using Image plugin make method */
var image = ceImage.make(data);
return image;
},
/**
* Direct upload from pasted path
* @param path
*/
uploadImage : function(path) {
var ajaxUrl = location.protocol + '//' + location.hostname + ':32769',
file,
image,
current = codex.content.currentNode,
beforeSend,
success_callback;
/** When image is uploaded to redactors folder */
success_callback = function(data) {
console.log(data);
return;
var file = JSON.parse(data);
image = ceImage.urlPastedCallbacks.uploadedImage(file.filename);
codex.content.switchBlock(current, image, 'image');
};
/** Before sending XMLHTTP request */
beforeSend = function() {
var content = current.querySelector('.ce-block__content');
content.classList.add('ce-plugin-image__loader');
};
/** Preparing data for XMLHTTP */
var data = {
url: '/club/fetchImage',
type: "POST",
data : {
url: path
},
beforeSend : beforeSend,
success : success_callback
};
codex.core.ajax(data);
}
};
return image;
})({});
})({});

View file

@ -9,15 +9,11 @@ var instagram = (function(instagram) {
render : function(content) {
codex.content.switchBlock(codex.content.currentNode, content, 'instagram');
setTimeout(function() {
window.instgrm.Embeds.process();
}, 200);
var blockContent = codex.content.currentNode.childNodes[0];
blockContent.classList.add('instagram__loader');
window.instgrm.Embeds.process();
setTimeout(function(){
blockContent.classList.remove('instagram__loader');
}, 500);
},
/**
@ -59,9 +55,11 @@ var instagram = (function(instagram) {
};
/**
* @private
*
* Make instagram embed via Widgets method
*/
instagram.make = function(data, isInternal) {
var make_ = function(data, isInternal) {
if (!data.instagram_url)
return;
@ -123,9 +121,24 @@ var instagram = (function(instagram) {
* Render data
*/
instagram.render = function(data) {
return instagram.make(data);
return make_(data);
};
/**
* callback for instagram url's coming from pasteTool
* Using instagram Embed Widgete to render
* @param url
*/
instagram.urlPastedCallback = function(url) {
var data = {
instagram_url: url
};
make_(data, true);
};
return instagram;
})({});

View file

@ -30,7 +30,7 @@ var paragraph = (function(paragraph) {
* If pasted URL from instagram, twitter or Image
* it renders via Social widgets content or uploads image and uses Image tool to render
*/
tag.addEventListener('paste', codex.tools.paste.callbacks.pasted, false);
tag.addEventListener('paste', codex.tools.paste.callbacks, false);
return tag;

View file

@ -1,14 +1,7 @@
/**
* Paste plugin.
*
* Listens on paste event and pastes content from:
* - Instagram
* - Twitter
* - VK
* - Facebook
* - Image
* - External Link
*
* Listen to clipboard paste event and analize pasted text whit patterns in pattern.js
*/
/**
@ -16,212 +9,46 @@
*
* Main tool settings.
*/
var pasteTool = {
};
/**
* Make elements to insert or switch
*
* @uses Core codex.draw module
*/
pasteTool.ui = {
/**
* Upload image by URL
*
* @uses codex Image tool
* @param filename
* @returns {Element}
*/
uploadedImage : function(filename) {
var data = {
background: false,
border: false,
isStretch: false,
file: {
url: "upload/redactor_images/" + filename,
bigUrl: "upload/redactor_images/" + filename,
width: null,
height: null,
additionalData: "null"
},
caption: '',
cover: null
};
/** Using Image plugin make method */
var image = codex.tools.image.make(data);
return image;
}
};
/**
*
* Callbacks
*/
pasteTool.callbacks = {
var paste = function(paste){
/**
* Saves data
* @param event
*/
pasted : function(event) {
paste.pasted = function(event) {
var clipBoardData = event.clipboardData || window.clipboardData,
content = clipBoardData.getData('Text');
var result = pasteTool.callbacks.analize(content);
var result = analize(content);
if (result) {
event.preventDefault();
event.stopImmediatePropagation();
}
},
};
/**
* Analizes pated string and calls necessary method
*/
analize : function(string) {
var regexTemplates = {
image : /(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*\.(?:jpe?g|gif|png))(?:\?([^#]*))?(?:#(.*))?/i,
instagram : new RegExp("http?.+instagram.com\/p?."),
twitter : new RegExp("http?.+twitter.com?.+\/"),
facebook : /https?.+facebook.+\/\d+\?/,
vk : /https?.+vk?.com\/feed\?w=wall\d+_\d+/,
},
var analize = function(string) {
image = regexTemplates.image.test(string),
instagram = regexTemplates.instagram.exec(string),
twitter = regexTemplates.twitter.exec(string),
facebook = regexTemplates.facebook.test(string),
vk = regexTemplates.vk.test(string);
var result = false;
if (image) {
paste.patterns.map(function(pattern, i){
if (pattern.regex.test(string)) {
pattern.callback.call(null, string, pattern);
result = true;
}
});
pasteTool.callbacks.uploadImage(string);
return true;
return result;
} else if (instagram) {
pasteTool.callbacks.instagramMedia(instagram);
return true;
} else if (twitter) {
pasteTool.callbacks.twitterMedia(twitter);
return true;
} else if (facebook) {
pasteTool.callbacks.facebookMedia(string);
return true;
} else if (vk) {
pasteTool.callbacks.vkMedia(string);
return true;
}
return false;
},
/**
* Direct upload
* @param url
*/
uploadImage : function(path) {
var ajaxUrl = location.protocol + '//' + location.hostname + ':32769',
file,
image,
current = codex.content.currentNode,
beforeSend,
success_callback;
/** When image is uploaded to redactors folder */
success_callback = function(data) {
console.log(data);
return;
var file = JSON.parse(data);
image = pasteTool.ui.uploadedImage(file.filename);
codex.content.switchBlock(current, image, 'image');
};
/** Before sending XMLHTTP request */
beforeSend = function() {
var content = current.querySelector('.ce-block__content');
content.classList.add('ce-plugin-image__loader');
};
/** Preparing data for XMLHTTP */
var data = {
url: '/club/fetchImage',
type: "POST",
data : {
url: path
},
beforeSend : beforeSend,
success : success_callback
};
codex.core.ajax(data);
},
/**
* callback for instagram url's
* Using instagram Embed Widgete to render
* @uses Instagram tool
* @param url
*/
instagramMedia : function(url) {
var fullUrl = url.input,
data;
};
data = {
instagram_url: fullUrl
};
return paste;
codex.tools.instagram.make(data, true);
}(paste || {});
},
/**
* callback for tweets
* Using Twittter Widget to render
* @uses Twitter tool
* @param url
*/
twitterMedia : function(url) {
var fullUrl = Array.isArray(url) ? url.input : url,
tweetId,
arr,
data;
arr = fullUrl.split('/');
tweetId = arr.pop();
/** Example */
data = {
id: tweetId,
id_str: tweetId,
status_url: fullUrl
};
codex.tools.twitter.make(data);
}
};

97
plugins/paste/patterns.js Normal file
View file

@ -0,0 +1,97 @@
/**
* Patterns
*
* To add plugin create callback function in one and add here this object:
* {
* type : '', - type of pasted text (for example, 'image', 'url' or your plugin name)
* regex : /regex/, - regex for pasted text
* callback : yourPlugin.smthPastedCallback - callback function in your plugin which is called when pasted text matches regex
* }
*
*/
var paste = paste || {};
paste.patterns = [
{
type: 'image',
regex: /(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*\.(?:jpe?g|gif|png))(?:\?([^#]*))?(?:#(.*))?/i,
callback: image.urlPastedCallbacks.uploadImage
},
{
type: 'instagram',
regex: /http?.+instagram.com\/p\/([a-zA-Z0-9]*)/,
callback: instagram.urlPastedCallback
},
{
type: 'twitter',
regex: /http?.+twitter.com?.+\//,
callback: twitter.urlPastedCallback
},
{
type: 'facebook',
regex: /https?.+facebook.+\/\d+\?/,
callback: ''//pasteTool.callbacks.facebookMedia
},
{
type: 'vk',
regex: /https?.+vk?.com\/feed\?w=wall\d+_\d+/,
callback: ''//pasteTool.callbacks.vkMedia
},
{
type: 'youtube',
regex: /(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/,
callback: embed.urlPastedCallback
},
{
type: 'vimeo',
regex: /(?:http[s]?:\/\/)?(?:www.)?vimeo\.co(?:.+\/([^\/]\d+)(?:#t=[\d]+)?s?$)/,
callback: embed.urlPastedCallback
},
{
type: 'coub',
regex: /https?:\/\/coub\.com\/view\/([^\/\?\&]+)/,
callback: embed.urlPastedCallback
},
{
type: 'vine',
regex: /https?:\/\/vine\.co\/v\/([^\/\?\&]+)/,
callback: embed.urlPastedCallback
},
{
type: 'imgur',
regex: /https?:\/\/(?:i\.)?imgur\.com.*\/([a-zA-Z0-9]+)(?:\.gifv)?/,
callback: embed.urlPastedCallback
},
{
type: 'gfycat',
regex: /https?:\/\/gfycat\.com(?:\/detail)?\/([a-zA-Z]+)/,
callback: embed.urlPastedCallback
},
{
type: 'twitch-channel',
regex: /https?:\/\/www.twitch.tv\/([^\/\?\&]*)/,
callback: embed.urlPastedCallback
},
{
type: 'twitch-video',
regex: /https?:\/\/www.twitch.tv\/[^\/\?\&]*\/v\/([0-9]*)/,
callback: embed.urlPastedCallback
},
{
type: 'yandex-music-album',
regex: /https?:\/\/music.yandex.ru\/album\/([0-9]*)/,
callback: embed.urlPastedCallback
},
{
type: 'yandex-music-track',
regex: /https?:\/\/music.yandex.ru\/album\/([0-9]*)\/track\/([0-9]*)/,
callback: embed.urlPastedCallback
},
{
type: 'yandex-music-playlist',
regex: /https?:\/\/music.yandex.ru\/users\/([^\/\?\&]*)\/playlists\/([0-9]*)/,
callback: embed.urlPastedCallback
}
];

View file

@ -21,16 +21,16 @@ var twitter = (function(twitter) {
if (codex.content.currentNode) {
tweet.dataset.statusUrl = data.status_url;
codex.content.switchBlock(codex.content.currentNode, tweet, 'twitter');
codex.content.switchBlock(codex.content.currentNode, tweet, 'tweet');
}
/**
* in case if we need extra data
*/
if (!data.user) {
if ( !data.user ) {
codex.core.ajax({
url : '/writing/tweetInfo?tweetId=' + data.id_str,
url : twitter.config.fetchUrl + '?tweetId=' + data.id_str,
type: "GET",
success: function(result) {
methods.saveTwitterData(result, tweet);
@ -43,7 +43,7 @@ var twitter = (function(twitter) {
tweet.dataset.profileImageUrlHttps = data.user.profile_image_url_https;
tweet.dataset.screenName = data.user.screen_name;
tweet.dataset.name = data.user.name;
tweet.dataset.id = data.id;
tweet.dataset.id = +data.id;
tweet.dataset.idStr = data.id_str;
tweet.dataset.text = data.text;
tweet.dataset.createdAt = data.created_at;
@ -62,9 +62,10 @@ var twitter = (function(twitter) {
},
saveTwitterData : function(result, tweet) {
var data = JSON.parse(result),
twitterContent = tweet;
setTimeout(function() {
/**
@ -74,7 +75,7 @@ var twitter = (function(twitter) {
twitterContent.dataset.profileImageUrlHttps = data.user.profile_image_url_https;
twitterContent.dataset.screenName = data.user.screen_name;
twitterContent.dataset.name = data.user.name;
twitterContent.dataset.id = data.id;
twitterContent.dataset.id = +data.id;
twitterContent.dataset.idStr = data.id_str;
twitterContent.dataset.text = data.text;
twitterContent.dataset.createdAt = data.created_at;
@ -90,10 +91,15 @@ var twitter = (function(twitter) {
/**
* Prepare twitter scripts
*/
twitter.prepare = function() {
twitter.prepare = function(config) {
var script = "//platform.twitter.com/widgets.js";
/**
* Save configs
*/
twitter.config = config;
/**
* Load script
*/
@ -101,12 +107,18 @@ var twitter = (function(twitter) {
};
twitter.make = function(data) {
/**
* @private
*
* @param data
* @returns {*}
*/
make_ = function(data) {
if (!data.id || !data.status_url)
return;
if (!data.id_str && typeof(data.id) === 'number') {
if (!data.id_str) {
data.id_str = data.status_url.match(/[^\/]+$/)[0];
}
@ -146,7 +158,31 @@ var twitter = (function(twitter) {
};
twitter.render = function(data) {
return twitter.make(data);
return make_(data);
};
twitter.urlPastedCallback = function(url) {
var tweetId,
arr,
data;
arr = url.split('/');
tweetId = arr.pop();
/** Example */
data = {
"media" : true,
"conversation" : false,
"user" : null,
"id" : +tweetId,
"text" : null,
"created_at" : null,
"status_url" : url,
"caption" : null
};
make_(data);
};
return twitter;

View file

@ -76,7 +76,7 @@
},
{
"type":"video_extended",
"data":{
"data":{
"source":"youtube",
"remote_id":"SqmAjcAPBpA",
"thumbnailUrl":"http:\/\/tj.local\/preview\/youtube\/SqmAjcAPBpA",

File diff suppressed because one or more lines are too long