HTML5 Video Rails - ruby-on-rails

I am confuse on how to use html5 video tags with rails. I few questions
1- What is a source file .ogg?? Can't I just use a mp4 file and insert it in my video tags.
2- If i want to show video/image to all member it should be in public? Then how do i access public, and how do i access video in a different folder such as assert/video if i create it.
3- I have the following file but doesn't show anything any file any reason why?
4- Do i need to code it to encode the video first? or just insert it into a file. Is ogg a good encoder or zencoder better?
Javascript (application.js)
function init() {
if( !document.createElement('video').canPlayType ) return;
var videos = document.querySelectorAll( 'div.video' ),
videosLength = videos.length;
for( var i=0; i < videosLength; i++ ) {
var root = videos[i];
video = root.querySelector( 'video' ),
play = document.createElement( 'button' ),
mute = document.createElement( 'button' );
video.controls = false;
// Initial class name
play.className = 'video-button video-play';
play.innerHTML = play.title = 'Play';
play.onclick = function() {
if( video.paused ) {
video.play();
// Additional class names for container and button while playing
root.className += ' video-on';
play.className += ' video-play-on';
play.innerHTML = play.title = 'Pause';
} else {
video.pause();
// Remove additional class names for container and button in paused state
root.className = root.className.replace( ' video-on', '' );
play.className = play.className.replace( ' video-play-on', '' );
play.innerHTML = play.title = 'Play';
}
}
// Initial class name
mute.className = 'video-button video-mute';
mute.innerHTML = mute.title = 'Mute';
mute.onclick = function() {
if( video.muted ) {
video.muted = false;
// Remove additional class name in unmuted state
mute.className = mute.className.replace( ' video-mute-on', '' );
mute.innerHTML = mute.title = 'Mute';
} else {
video.muted = true;
// Additional class name for button in muted state
mute.className += ' video-mute-on';
mute.innerHTML = mute.title = 'Unmute';
}
}
root.appendChild( play );
root.appendChild( mute );
}
}
window.onload = init;
HTML (index.html.erb)
<video controls poster="video/video.jpg" width="854" height="480">
<source src="m/video.ogv" type="video/ogg">
<source src="video/trailer_test.mp4" type="video/mp4">
<object type="application/x-shockwave-flash" data="m/player.swf" width="854" height="504">
<param name="allowfullscreen" value="true">
<param name="allowscriptaccess" value="always">
<param name="flashvars" value="file=video.mp4">
<!--[if IE]><!--><param name="movie" value="m/player.swf"><!--<![endif]-->
<img src="m/video.jpg" width="854" height="480" alt="Video">
<p>Your browser can’t play HTML5 video. Download it instead.</p>
</object>
</video>
CSS (application.css)
body {
margin:0;
padding:50px;
background:#444;
}
/* Video */
.video {
position:relative;
overflow:hidden;
float:left;
background:#000;
color:#fff;
}
.video video {
display:block;
opacity:.4;
-webkit-transition:all .2s linear;
-moz-transition:all .2s linear;
-o-transition:all .2s linear;
transition:all .2s linear;
}
.video:hover video {
opacity:.6;
}
.video-on video,
.video-on:hover video {
opacity:1;
}
/* Button */
.video-button {
position:absolute;
z-index:1;
border:none;
background:#CCC;
text-indent:-9999px;
cursor:pointer;
-webkit-transform:scale(1.0);
-moz-transform:scale(1.0);
-o-transform:scale(1.0);
transform:scale(1.0);
-webkit-transition:all .2s linear;
-moz-transition:all .2s linear;
-o-transition:all .2s linear;
transition:all .2s linear;
}
.video-button:hover {
-webkit-transform:scale(1.1);
-moz-transform:scale(1.1);
-o-transform:scale(1.1);
transform:scale(1.1);
}
.video-button:after {
position:absolute;
background:url(i/buttons.png) no-repeat;
content:'';
}
/* Play */
.video-play {
bottom:30px;
left:30px;
-webkit-box-shadow:0 0 50px #FFF,
inset 5px 5px 20px #444,
inset 0 -20px 40px #000;
-moz-box-shadow:0 0 50px #FFF,
inset 5px 5px 20px #444,
inset 0 -20px 40px #000;
box-shadow:0 0 50px #FFF,
inset 5px 5px 20px #444,
inset 0 -20px 40px #000;
width:50px;
height:50px;
-webkit-border-radius:25px;
-moz-border-radius:25px;
border-radius:25px;
}
.video-play:after {
top:15px;
left:16px;
width:21px;
height:21px;
background-position:0 0;
}
.video-play-on:after {
background-position:-23px 0;
}
/* Mute */
.video-mute {
bottom:35px;
left:100px;
-webkit-box-shadow:0 0 30px #FFF,
inset 4px 4px 15px #444,
inset 0 -15px 35px #000;
-moz-box-shadow:0 0 30px #FFF,
inset 4px 4px 15px #444,
inset 0 -15px 35px #000;
box-shadow:0 0 30px #FFF,
inset 4px 4px 15px #444,
inset 0 -15px 35px #000;
width:38px;
height:38px;
-webkit-border-radius:19px;
-moz-border-radius:19px;
border-radius:19px;
}
.video-mute:after {
top:13px;
left:11px;
width:17px;
height:14px;
background-position:0 -21px;
}
.video-mute-on:after {
background-position:-18px -21px;
}
Any help is appreciated!

> 1- What is a source file .ogg?? Can't I just use a mp4 file and insert it in my video tags.
Yes you can just use mp4. See the Rails API on video_tag. It works with mp4. http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html#method-i-video_tag
> 2- If i want to show video/image to all member it should be in public? Then how do i access public, and how do i access video in a different folder such as assert/video if i create it.
An easy way to do this is to use the paperclip gem for handling the video uploads. Very easy to grab the url you need to play it.
> 3- I have the following file but doesn't show anything any file any reason why? > 4- Do i need to code it
If you are using mp4 in HTML5, you just need the video_tag, you don't need any of this

Related

Twilio Video Call issue on iOS Safari, After call start video freeze nodejs issue

I have created a video call application using Nodejs & Twilio CLI. And using this in my both mobile app Android & iOS. On Android is working perfectly. But on iOS, there is an issue, when users reach the video call page, it's showing preview but as the user clicks on the Join Room button, then his/her video stops and just showing a black screen. While he can talk with other users and can see the video of them. And the Second user also can see his/her video perfectly. Only the issue he/she can't see his/her video on that call.
My html code
<!DOCTYPE html>
<html>
<head>
<style>
.joinbtn {
border: none;
padding: 10px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
background-color: #2b96cc;
color: #fff;
}
.stvbtn {
border: none;
padding: 10px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
background-color: #2b96cc;
color: #fff;
}
.endbtn {
float:right;
border: none;
padding: 10px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
background-color: #dc3545;
color: #fff;
}
#media screen and (max-width: 820px) {
video {
object-fit: cover;
width: 100%;
height: 47vh;
}
}
#media screen and (min-width: 821px){
video {
object-fit: contain;
}
}
.connect_btn{
display: flex;
justify-content: center;
align-content: space-around;
margin-top: -50px;
opacity: 0.8;
padding-bottom:8px;
}
button.endbtn:disabled, button.joinbtn:disabled {
background-color: #607d8b;
color: #ffffff;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/6.4.0/adapter.js" type="text/javascript"></script>
<script src="webrtc.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Clifix Video Chat</title>
</head>
<body>
<div id="room-controls">
<video id="video" autoplay muted playsinline loop width="100%"></video>
<div class="connect_btn">
<label for="passcode"></label>
<input id="passcode" type="hidden" value="8514"/>
<!--button class="stvbtn" id="start-video" onclick="viplay()">On/Off</button-->
<button class="joinbtn" id="button-join">Join Room</button>
<button class="endbtn" id="button-leave" disabled="disabled">End Call</button>
</div>
</div>
<!-- EDIT_CODE -->
<script src="//media.twiliocdn.com/sdk/js/video/releases/2.3.0/twilio-video.min.js"></script>
<script src="index.js"></script>
</body>
</html>
My nodejs code:
'use strict';
(() => {
//const ROOM_NAME = 'demo';
var urltemp = location.search;
var array = urltemp.split('?');
var array1 = array[1];
var array2 = array1.split('=');
var id = array2[1];
const ROOM_NAME = id;
const Video = Twilio.Video;
let videoRoom, localStream;
const video = document.getElementById('video');
// preview screen
navigator.mediaDevices
.getUserMedia({ video: true, audio: true })
.then((vid) => {
video.srcObject = vid;
localStream = vid;
});
// buttons
const joinRoomButton = document.getElementById('button-join');
const leaveRoomButton = document.getElementById('button-leave');
joinRoomButton.onclick = () => {
//video.play();
// get access token
fetch(`video-token?passcode=${getPasscode()}&room=${ROOM_NAME}`)
.then((resp) => {
if (resp.ok) {
var url=window.location.href,
separator = (url.indexOf("?")===-1)?"?":"&",
newParam=separator + "join=true";
var newUrl=url.replace(newParam,"");
newUrl+=newParam;
window.history.replaceState(null,null,newUrl);
return resp.json();
} else {
console.error(resp);
if (resp.status === 401) {
throw new Error('Go Back & Join Again');
} else {
throw new Error('Unexpected error. Open dev tools for logs');
}
}
})
.then((body) => {
const token = body.token;
//console.log(token);
//connect to room
return Video.connect(token, { name: ROOM_NAME });
})
.then((room) => {
//console.log(`Connected to Room ${room.name}`);
videoRoom = room;
room.participants.forEach(participantConnected);
room.on('participantConnected', participantConnected);
room.on('participantDisconnected', participantDisconnected);
room.once('disconnected', (error) =>
room.participants.forEach(participantDisconnected)
);
joinRoomButton.disabled = true;
leaveRoomButton.disabled = false;
})
.catch((err) => {
alert(err.message);
});
};
// leave room
leaveRoomButton.onclick = () => {
var url=window.location.href,
separator = (url.indexOf("?")===-1)?"?":"&",
newParam=separator + "end=true";
var newUrl=url.replace(newParam,"");
newUrl+=newParam;
window.history.replaceState(null,null,newUrl);
videoRoom.disconnect();
//console.log(`Disconnected from Room ${videoRoom.name}`);
joinRoomButton.disabled = false;
leaveRoomButton.disabled = true;
};
})();
const getPasscode = () => {
const passcodeInput = document.getElementById('passcode') || {};
const passcode = passcodeInput.value;
passcodeInput.value = '';
return passcode;
};
// connect participant
const participantConnected = (participant) => {
//console.log(`Participant ${participant.identity} connected'`);
const div = document.createElement('div'); //create div for new participant
div.id = participant.sid;
participant.on('trackSubscribed', (track) => trackSubscribed(div, track));
participant.on('trackUnsubscribed', trackUnsubscribed);
participant.tracks.forEach((publication) => {
if (publication.isSubscribed) {
trackSubscribed(div, publication.track);
}
});
document.body.appendChild(div);
};
const participantDisconnected = (participant) => {
//console.log(`Participant ${participant.identity} disconnected.`);
document.getElementById(participant.sid).remove();
};
const trackSubscribed = (div, track) => {
div.appendChild(track.attach());
};
const trackUnsubscribed = (track) => {
track.detach().forEach((element) => element.remove());
};
As per my understanding, before this my video was not working on iOS safari then I have done modifications in my HTML video code.
From this:
<video id="video" autoplay muted width="100%"></video>
To:
<video id="video" autoplay muted playsinline loop width="100%"></video>
Then it starts working as having video freezing at the iOS User side when he/she start calling.
Twilio developer evangelist here.
When you call Video.connect the Video SDK will ask for permission to use your microphone and camera. Safari does not like giving access to the microphone and camera more than once at a time and since you also ask for media access to show the preview, it drops the preview tracks and creates new tracks for the video call. This is why the preview goes dark, but other participants can see and hear the video/audio.
Instead, you should reuse the tracks that you got for the preview by storing a reference to them and then passing them to Video.connect as the tracks property in the ConnectOptions. You already store a reference to the localStream so you can use that when you get to connect, like this:
return Video.connect(token, {
name: ROOM_NAME,
tracks: localStream.getTracks()
});
That way the tracks for the preview will be re-used for the video call and nothing should go dark.

How can I Integrate this code in .html file and .ts file?

I want to drag and drop functionality using this below URL into .html(which contains the drag and drop element) and .ts file(file contains the backend part)
https://www.npmjs.com/package/material-ui-dropzone
You don't need to use an npm package for this check out my example here
// ************************ Drag and drop ***************** //
let dropArea = document.getElementById("drop-area")
// Prevent default drag behaviors
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false)
document.body.addEventListener(eventName, preventDefaults, false)
})
// Highlight drop area when item is dragged over it
;['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false)
})
;['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false)
})
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false)
function preventDefaults (e) {
e.preventDefault()
e.stopPropagation()
}
function highlight(e) {
dropArea.classList.add('highlight')
}
function unhighlight(e) {
dropArea.classList.remove('active')
}
function handleDrop(e) {
var dt = e.dataTransfer
var files = dt.files
handleFiles(files)
}
let uploadProgress = []
let progressBar = document.getElementById('progress-bar')
function initializeProgress(numFiles) {
progressBar.value = 0
uploadProgress = []
for(let i = numFiles; i > 0; i--) {
uploadProgress.push(0)
}
}
function updateProgress(fileNumber, percent) {
uploadProgress[fileNumber] = percent
let total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length
console.debug('update', fileNumber, percent, total)
progressBar.value = total
}
function handleFiles(files) {
files = [...files]
initializeProgress(files.length)
files.forEach(uploadFile)
files.forEach(previewFile)
}
function previewFile(file) {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onloadend = function() {
let img = document.createElement('img')
img.src = reader.result
document.getElementById('gallery').appendChild(img)
}
}
function uploadFile(file, i) {
var url = 'the service url to upload the file'
var xhr = new XMLHttpRequest()
var formData = new FormData()
xhr.open('POST', url, true)
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
// Update progress (can be used to show progress indicator)
xhr.upload.addEventListener("progress", function(e) {
updateProgress(i, (e.loaded * 100.0 / e.total) || 100)
})
xhr.addEventListener('readystatechange', function(e) {
if (xhr.readyState == 4 && xhr.status == 200) {
updateProgress(i, 100) // <- Add this
}
else if (xhr.readyState == 4 && xhr.status != 200) {
// Error. Inform the user
}
})
formData.append('upload_preset', 'ujpu6gyk')
formData.append('file', file)
xhr.send(formData)
}
body {
font-family: sans-serif;
}
a {
color: #369;
}
.note {
width: 500px;
margin: 50px auto;
font-size: 1.1em;
color: #333;
text-align: justify;
}
#drop-area {
border: 2px dashed #ccc;
border-radius: 20px;
width: 480px;
margin: 50px auto;
padding: 20px;
}
#drop-area.highlight {
border-color: purple;
}
p {
margin-top: 0;
}
.my-form {
margin-bottom: 10px;
}
#gallery {
margin-top: 10px;
}
#gallery img {
width: 150px;
margin-bottom: 10px;
margin-right: 10px;
vertical-align: middle;
}
.button {
display: inline-block;
padding: 10px;
background: #ccc;
cursor: pointer;
border-radius: 5px;
border: 1px solid #ccc;
}
.button:hover {
background: #ddd;
}
#fileElem {
display: none;
}
<div id="drop-area">
<form class="my-form">
<p>Upload multiple files with the file dialog or by dragging and dropping images onto the dashed region</p>
<input type="file" id="fileElem" multiple accept="image/*" onchange="handleFiles(this.files)">
<label class="button" for="fileElem">Select some files</label>
</form>
<progress id="progress-bar" max=100 value=0></progress>
<div id="gallery" /></div>
</div>

creating tags in textarea using jquery

i want to create tags for input data.(http://textextjs.com/manual/examples/ajax-with-filter-tags-and-autocomplete.html hear they creating tags using auto complete text box, but i don't want auto complete one)
hear is my code
<!DOCTYPE html>
<html>
<head>
<script src="jquery.min.js"></script>
<script>
$(document).ready(function() {
$("#textBox").keyup(function() {
$("#message").val($(this).val());
});
});
</script>
</head>
<body>
<div>
TextBox 1 : <input type="textbox" id="textBox"></input>
TextBox 2 : <input type="textarea" id="message"></input>
</div>
</body>
</html>
hear it reflect data of textbox1 to textbox2.
now what i want is : if user enter any data(words) in textbox1 followed by space then that word should convert into tags in textbox2
First of all type=textarea is wrong. There's no such input like that. You must be using <textarea> instead of that. Secondly, why dont you use contentditable attribute? It works just like a text area but can take HTML, is supported in all browsers, and you can use it on any block element! So replace your second input with this:
TextBox 2 : <div class="target" contenteditable="true"></div>
Then, in your code,
$("#textBox").keypress(function (e) {
if (e.which === 32) {
$(".target").append("<a href='#' class='tag'>" + this.value + "</a>");
this.value = "";
}
});
(Disclaimer) I used the styles from SO's tags, like this :
body {
font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif;
}
.tag {
color: #3E6D8E;
background-color: #E0EAF1;
border-bottom: 1px solid #b3cee1;
border-right: 1px solid #b3cee1;
padding: 3px 4px 3px 4px;
margin: 2px 2px 2px 0;
text-decoration: none;
font-size: 90%;
line-height: 2.4;
white-space: nowrap;
}
.tag:hover {
background-color: #c4dae9;
border-bottom: 1px solid #c4dae9;
border-right: 1px solid #c4dae9;
text-decoration: none;
}
Demo : http://jsfiddle.net/hungerpain/Wky2Z/
To add the tags to an array, have a variable called tags outside the keypress function :
var tags = [];
Then, in the keypress, you've got this if loop right? Push the new value into the array :
if (e.which === 32) {
$(".target").append("<a href='#' class='tag'>" + this.value + "</a>");
tags.push(this.value); //push the value in array
this.value = "";
}
Then, when you need to save it to DB, just join them :
tags.join("");
Then later, when you to retrieve them from DB next time, you could wrap those with the a (what we did in the keypress function)
Demo : http://jsfiddle.net/hungerpain/Wky2Z/1/
Try This:
$(document).ready(function () {
$("#textBox").keyup(function (e) {
if (e.keyCode == 32) {
$("#message").val($(this).val());
}
if ($(this).val() == '') {
$("#message").val('');
}
});
});
JSFIDDLE DEMO

Highlight Nav item when using anchor scrolling

I'm pretty new when it comes to jQuery so I need someone with a little more experience to help me out. I have a Nav with 3 items - Work About Contact
By default I have work selected but I would like it to change the active class to which ever is clicked. I have anchor scrolling working but I would like to have it highlight to the correct nav item when clicked. Also if it is possible when scrolling down the page and getting to the next section for it to change highlight automatically.
This is the jQuery I am using for the anchor scrolling.
<script>$(function() {
var main-nav = $("#main-nav"), pos = main-nav.offset();
$(window).scroll(function() {
if($(this).scrollTop() > (pos.top + 10) && $(this).scrollTop() < 15000 && main-nav.css('position') == 'static') { main-nav.addClass('fixed'); }
else if($(this).scrollTop() <= pos.top && main-nav.hasClass('fixed')){ main-nav.removeClass('fixed'); }
else if($(this).scrollTop() > 15000 && main-nav.hasClass('fixed')){ main-nav.removeClass('fixed'); }
})
});
</script>
<script>$(function() {
$('ul.nav a').bind('click',function(event){
var $anchor = $(this);
$('html, body').stop().animate({
scrollTop: $($anchor.attr('href')).offset().top
}, 1500,'easeInOutExpo');
/*
if you don't want to use the easing effects:
$('html, body').stop().animate({
scrollTop: $($anchor.attr('href')).offset().top
}, 1000);
*/
event.preventDefault();
});
});
</script>
Here is the CSS
#main-nav{
font: bold 12px 'Bitter', serif;
width: 145px;
float: right;
}
#main-nav li{
float: left;
list-style: none;
margin: 10px 2px 0 2px;
color: #c4c5c5;
}
#main-nav li:last-child{
margin-right: 0;
}
#main-nav a{
text-decoration: none;
color: #c4c5c5;
}
#main-nav a:hover{
text-decoration: none;
color: #919292;
}
#main-nav a.active{
color: #919292;
}
Here is the HTML
<div id="main-nav" class="">
<ul class="nav">
<li><a class="active" href="#work">Work</a></li>
<li>/</li>
<li>About</li>
<li>/</li>
<li>Contact</li>
</ul>
</div>
If someone could help me out I would really really appreciate it!
Since you are using the active class to specify which anchor should be highlighted, you just need to make sure that the correct anchor has that class. Fixing that when the nav items are clicked is straightforward, in your $('ul.nav a').bind add:
$('ul.nav a').removeClass('active');
$(this).addClass('active');
Here is a jsbin.
To highlight the nav's as the page is scrolled you would just compare the scrollTop for the page with the offsets of elements in the DOM...similar to your current code in $(window).scroll.

can't set ckeditor toolbar location at bottom

I'm trying ckeditor, but I can't set the toolbar location to the bottom (by default it is set to the top).
I read the documentation, and I wrote this snipet in config.js file:
CKEDITOR.editorConfig = function( config )
{
config.toolbarLocation = 'bottom';
}
In config.js I have defined a toolbar and config.toolbarLocation = 'bottom' then I call ckeditor in this mode:
CKEDITOR.replace('editor', { toolbar : 'Full' });
Did I foget something else? It doesn't work. I see only textarea without toolbar (the toolbar at the top disappears).
Can you help me, please?
I guess it is priority to be covered. Try this:
CKEDITOR.replace('editor', { toolbar : 'Full', toolbarLocation : 'bottom' });
In Ckeditor 4 they have option to move toolbar to bottom but not in Ckeditor 5.
Check configuration table here
So, I did the CSS tweak.
Add a parent class to parent div of Ckeditor element and then add these:
1. Using flex-direction to flip order of toolbar and editing area:
.ck.ck-reset.ck-editor {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column-reverse;
-moz-flex-direction: column-reverse;
-ms-flex-direction: column-reverse;
flex-direction: column-reverse
}
2. Assign less height to Toolbar and more to edit area:
.ck.ck-editor__main>.ck-editor__editable {
height: 200px;
}
.ck.ck-editor .ck-editor__top .ck-sticky-panel .ck-toolbar,
.ck-sticky-panel__content {
height: 54px;
}
2. Remove shadow and outline when focusing on edit area (optional, only if you want):
.ck-focused, .ck.ck-editor__editable:not(.ck-editor__nested-editable).ck-focused {
border: none;
border: none;
outline: none !important;
-moz-outline: none !important;
-webkit-outline: none !important;
-ms-outline: none !important;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none
}
try this code :
CKEDITOR.replace('editor1', {
/*
* Ensure that htmlwriter plugin, which is required for this sample, is loaded.
*/
// extraPlugins: 'htmlwriter',
toolbarLocation: 'bottom',
height: 200,
width: '100%',
toolbar: [
['Bold', 'Italic', 'Underline', '-', 'BulletedList', '-', 'Link', 'Unlink'],
['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
['TextColor']
]});
use this :
.ck.ck-editor {
display: flex;
flex-direction: column-reverse;
}

Resources