Adding Custom Video.js Control Bar buttons - javascript

I've been working on video.js for like a day so I'm a real newbie when it comes to this stuff Today I just wanted to add a button that will switch between two videos. I did it in jQuery quick and easy. But I would rather do it in javascript to better understand video.js as a whole.
Done so far:
1. I have downloaded the most recent version of video.js from github.
2. Player is working great.
3. Read through the guides.
4. Tried their example code.
Their Code for button creation:
var myButton = video.controlBar.addChild('MyButton', {
text: 'Press Me',
children: {
buttonChildExample: {
buttonChildOption: true
}
}
});
Error from console : Uncaught TypeError: undefined is not a function
So no addChild() is defined which is odd because its in their docs/api.
Does anyone know how to add buttons to their controlbar ?
Any help would be appreciated and if you need any more info let me know. Thanks.
UPDATE:
1) I have wrapped the above code in a videojs.ready() as the documentation suggests. But still to no avail.
2) component = new window['videojs'][componentClass](this.player_ || this, options);in video.dev.js (line 1655) throws the error "uncaught TypeError: undefined is not a function"
3) Evaluating new window['videojs'] in console gave me the error " TypeError: The element or ID Supplied is not valid. (videojs).
Again thanks for your help in adavanced.

The undefined is coming from the fact that MyButton is not a vjs.Component. The documentation around this is a little unclear and it took me a while to understand what is going on.
The documentation states that the first argument is the "class name or instance of a child to add" - it's referring to a JavaScript class rather than a CSS class. The correct way to add a button is as follows:
var myButton = video.controlBar.addChild('button', {
text: "Press me",
// other options
});
myButton.addClass("html-classname");
This will add the following to your controlbar:
<div class="vjs-control html-classname" aria-live="polite" tabindex="0">
<div class='vjs-control-content">
<span class="vjs-control-text">Press me</span>
</div>
</div>

Starting with v5:
var videoJsButtonClass = videojs.getComponent('Button');
var concreteButtonClass = videojs.extend(videoJsButtonClass, {
// The `init()` method will also work for constructor logic here, but it is
// deprecated. If you provide an `init()` method, it will override the
// `constructor()` method!
constructor: function() {
videoJsButtonClass.call(this, videojsInstance);
}, // notice the comma
handleClick: function(){
// Do your stuff
}
});
var concreteButtonInstance = videojsInstance.controlBar.addChild(new concreteButtonClass());
concreteButtonInstance.addClass("vjs-" + name);
PROFIT!

(This answer is relevant to videojs 4.9.1)
To add a custom button to videojs's control bar, you need to create a vjs component and add it to the controlbar's children in the player setup options. Here's a setup where I add 3 custom components to the control bar (playebackSpeedPopoverMenu, selectBitrateControlButton, and verticalVolumeMenuButton):
var setup = {
'techOrder' : ['html5', 'flash'],
'controls' : true,
'preload' : 'auto',
'children':{
'controlBar': {
'children': {
'playbackSpeedPopoverMenu': {},
'selectBitrateControlButton': {src:videosrc},
'verticalVolumeMenuButton': {},
'volumeControl': false,
'muteToggle':false,
'liveDisplay':false,
}
}
};
var player = new vjs.Player(vjs.el("id_of_my_video_element_note_that_theres_no_poundsign"),
setup,
function() {
//this is your ready function
}));
The reason the controlbar compnents are declared like 'selectBitrateControlButton': {src:videosrc} is because whatever is included in the object literal will get injected into the components init() function as the options parameter. This works great working with the videojs.dev.js file, not the closure-compiled cdn version (that's a whole 'nother story) These are some examples of defining vjs components that appear on the controlbar:
https://github.com/videojs/video.js/blob/master/src/js/control-bar/play-toggle.js#L8
https://github.com/videojs/video.js/blob/master/src/js/control-bar/fullscreen-toggle.js#L8
I fully recommend getting the latest code from the videojs git repo on git hub, which will make it easier to look up the various sources in your IDE. The ui component framework isn't terribly complicated and can be mastered in a couple days with some patient sourcecode drilling.

I tried the 2 top answers here and they both worked but didn't address other problems, such as the button not showing text or an icon and missing a title.
Here's my solution:
const customInvertFilterButton: videojs.Button = this.video.controlBar.addChild('button')
customInvertFilterButton.addClass('vjs-custom-invert-filter-button')
customInvertFilterButton.addClass('vjs-icon-spinner')
customInvertFilterButton.setAttribute('title', 'Invert')
customInvertFilterButton.on('click', () => {
this.invertFilter = !this.invertFilter
customInvertFilterButton.setAttribute('title', this.invertFilter ? 'Uninvert' : 'Invert')
})
Here, the vjs-icon-spinner class is a vjs icon. All their icons are here:
https://videojs.github.io/font
Optionally, to read more about their icons check this out (I didn't find it overly useful):
https://github.com/videojs/font
Finally, the icons were rendering too small. You can adjust their size and look with this SCSS:
.vjs-custom-invert-filter-button {
cursor: pointer;
&.vjs-icon-spinner:before {
font-size: 1.8em;
line-height: 1.67;
}
}
Hope it helps. Much love in HIM! Yes, J^e^s^u^s
OPTIONAL ADDITIONAL INFO FOR MORE ICONS
The VJS icon set is a subset of the Material Icons set.
Its possible to use any Material Icon as follows:
// https://github.com/google/material-design-icons/tree/master/font
// https://google.github.io/material-design-icons/#setup-method-2-self-hosting
#font-face {
font-family: 'VJS Material Icons';
font-style: normal;
font-weight: 400;
src: local('Material Icons'), local('MaterialIcons-Regular'), url(/assets/fonts/vjs-mat-icons/MaterialIcons-Regular.ttf) format('truetype');
}
.vjs-material-icons {
font-family: 'VJS Material Icons', sans-serif;
font-weight: normal;
font-style: normal;
font-size: 24px; /* Preferred icon size but also suggests 18px, 24px, 36px, 48px */
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased;
/* Support for Safari and Chrome. */
text-rendering: optimizeLegibility;
/* Support for Firefox. */
-moz-osx-font-smoothing: grayscale;
/* Support for IE. */
font-feature-settings: 'liga';
}
The above font file can be downloaded from https://github.com/google/material-design-icons/tree/master/font
Then to use any Material icon:
// https://fonts.google.com/icons
.vjs-custom-brightness-down-button {
cursor: pointer;
&.vjs-icon-brightness-down {
font-family: 'VJS Material Icons', serif;
font-weight: normal;
font-style: normal;
&:before {
content: "\e1ad"; // this relates to a specific icon
font-size: 1.4em;
line-height: 1.67;
}
}
}
Note that the unicode code seen in the CSS content property can be gotten from https://fonts.google.com/icons
And finally, the .ts for this:
private initCustomBrightnessDownButton() {
const customBrightnessDownButton: videojs.Button = this.video.controlBar.addChild('button')
customBrightnessDownButton.addClass('vjs-custom-brightness-down-button')
customBrightnessDownButton.addClass('vjs-material-icons')
customBrightnessDownButton.addClass('vjs-icon-brightness-down')
customBrightnessDownButton.setAttribute('title', 'Brightness -')
customBrightnessDownButton.on('click', () => {
this.brightness = this.brightness > 10 ? this.brightness - 10 : this.brightness
})
}

Related

Google Maps markerClusterer: how can I pass options at init when adding lib via unpkg?

I'm currently trying to implement Google Maps JavaScript MarkerClusterer on a Magento 2 website.
As Magento 2 front-end workflow goes, I can't use ES6+ import/export feature (unless I'm in developer mode). There's no use of NPM in Magento 2.
MarkerClusterer documentation states that when adding via unpkg (which is what I do), the MarkerClusterer can be accessed at markerClusterer.MarkerClusterer (last line of the "Install" section here).
I'm fine when declaring my cluster object the following way (clusters are showing on map with default algorithm and renderer): let cluster = new markerClusterer.MarkerClusterer({ markers, map });.
However, I'm struggling with overriding default parameters (I need to add a clusterClass and custom icons).
At first (before I realised I won't be able to use ES6+ import/export feature), It worked like a charm when declaring my cluster object the following way:
let cluster = new MarkerClusterer(
map,
markers, {
clusterClass: "marker-cluster__icon",
imagePath: mapOperator.imageBasePath + "pin-cluster-",
});
I have no clue about how to pass new options/parameters with the following syntax which I have no choice about using: let cluster = new markerClusterer.MarkerClusterer({ markers, map });.
I couldn't find any example over the internet as everyone is using NPM or any kind of other modern front-end workflows nowadays.
The following, as an example, is not working:
let config = {
markers: {
clusterClass: "marker-cluster__icon",
imagePath: mapOperator.imageBasePath + "pin-cluster-",
},
map
}
let cluster = new markerClusterer.MarkerClusterer(config);
For me, that's still an obscure part of how javascript is working. Hope someone can lighten my path...
So thanks to #geocodezip, I was finally able to reach my goal. Following thread's answer (which is not exactly a duplicate) helped breaking the deadlock: Unable to apply custom icons for clustering in google maps markerclusterer because unable to provide position data.
One remaining caveat is that I wasn't able to add a custom CSS class to the label (to the marker cluster icon HTML element), which would have helped to get a more secured focus on it (than .map [title][aria-label][role="button"]) for styling the information of the amount of markers in a cluster.
I've tried several solutions including google.maps.MarkerLabel interface className property, clusterClass option as mentioned here and cssClass option as mentioned here, but none of them seemed to be working...
let renderer = {
render: ({ count, position }) =>
new google.maps.Marker({
label: {
text: String(count),
color: "transparent",
},
position,
icon: {
url: mapOperator.imageBasePath + "pin-cluster.png",
},
zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count,
}),
};
let config = { map, markers, renderer };
let cluster = new markerClusterer.MarkerClusterer(config);
.map [title][aria-label][role="button"] {
margin-top: -5px;
margin-left: 5px;
&:before {
position: absolute;
right: 0;
top: 0;
display: block;
content: attr(aria-label);
width: 16px;
height: 16px;
text-align: center;
font-weight: 400;
font-size: 10px;
line-height: 16px;
letter-spacing: -1px;
background: #ffffff;
border: 2px solid;
border-radius: 50%;
color: #red;
border-color: #red;
}
}

Dynamically load font from file based on userinput

I built a small styling simulator for an app of our company so that designers can quickly check color options/border styles etc for our app.
I used mainly CSSvariables that change when adapting colors via a color picker / adapt values in a config JSON.
The designers should be able to also test different font styles in the JSON but I can't get it to work with local font files.
How can I achieve to load local fonts from a local folder too? I want to achieve this live so that the difference can be seen when entering a different font.
Just loading in example "Courier" works as its a system font but when I want to load it from a folder it doesn't work.
This is how I am loading the fonts into a CSS variable (the get value is just providing a safe method to access the JSON)
JS
$(":root")[0].style.setProperty("--regularFont", "/fonts/" + getValue(parsedConfig,['configs', 0, 'styleSheet', 'fonts', 'regularFont'],"") + ".tff");
$(":root")[0].style.setProperty("--boldFont", "/fonts/" + getValue(parsedConfig,['configs', 0, 'styleSheet', 'fonts', 'boldFont'],"") + ".tff");
JSON:
"fonts": {
"regularFont": "Arial",
"boldFont": "AvenirNext-Bold"
CSS
#font-face {
font-family: sans-serif;
font-family: regularFont;
src: local(var(--regularFont)),
url(var(--regularFont));
}
#font-face {
font-family: sans-serif;
font-family: boldFont;
src: local(var(--boldFont)),
url(var(--boldFont));
font-weight: bold;
}
/* example Styling */
.card {
font-family: regularFont;
}
You can create a FontFace object with the data from the File object and the add it to the document's FontFaceSet:
const fontInput = document.getElementById("font-input"),
test = document.getElementById("test");
let font;
fontInput.addEventListener("input", async () => {
if (font)
document.fonts.remove(font);
const data = await fontInput.files[0].arrayBuffer();
font = new FontFace('test-font', data);
await font.load();
document.fonts.add(font);
test.style.fontFamily = `test-font`;
});
<input type="file" id="font-input">
<p id="test">Upload a font to change me!</p>

Custom SAPUI5 icons added to the IconPool sometimes don't display on IE

I have a problem with displaying custom icons in IE11 (didn't check for older ones, neither Edge). Chrome and Firefox work properly. Pretty often custom icons that are added to the IconPool are not displayed in IE. After hitting refresh in the browser, they appear normally. Is this a known issue and is there a way to fix this?
The way we do this, is:
Fonts details are stored in a json that is hardcoded in the page source under a specific variable (I've shortened the output and simplified the names):
var myFonts = {
"iconfonts": [{
"content": "ea0f",
"key": "iconFont-icon_search"
}, {
"content": "e635",
"key": "iconFont-burger_close"
}]
}
Then the fonts are loaded by a small utility component (single .js file) method:
addMyFontsToSAPUI5(myFonts);
Code for the utility component:
addMyFontsToSAPUI5 = function(data) {
sap.ui.getCore().attachInit(function() {
setTimeout(function() {
console.log("Loading Icon Fonts");
jQuery.sap.require("sap.ui.core.IconPool");
if (data && data.iconfonts) {
var iconfonts = data.iconfonts;
for (var i = 0; i < iconfonts.length; i++) {
var iconfont = iconfonts[i];
sap.ui.core.IconPool.addIcon(iconfont.key, "myfonts", "iconfont", iconfont.content);
}
console.log("Loading Icons Finished");
var eventBus = sap.ui.getCore().getEventBus();
eventBus.publish("Icons", "IconFontsLoaded");
}
}, 0);
});
};
After the event is raised, components that use these fonts launch the rerender method. Example controller code:
sap.ui.define(
["sap/ui/core/mvc/Controller"],
function(Controller) {
"use strict";
return Controller.extend("mynamespace.controller.Main", {
onInit: function() {
var eventBus = sap.ui.getCore().getEventBus();
eventBus.subscribe("Icons", "IconFontsLoaded", this.onRerender, this);
},
onRerender: function() {
console.log("Rerender after icons are loaded");
this.getView().rerender();
}
});
});
Finally, the CSS structure:
/* My ICONFONT */
#font-face {
font-family: 'iconfont';
src:url('/Fonts/iconfont.eot?-d4jyxw&version=2.1.6');
src:url('/Fonts/iconfont.eot?#iefix-d4jyxw&version=2.1.6') format('embedded-opentype'),
url('/Fonts/iconfont.woff?-d4jyxw&version=2.1.6') format('woff'),
url('/Fonts/iconfont.ttf?-d4jyxw&version=2.1.6') format('truetype'),
url('/Fonts/iconfont.svg?-d4jyxw#iconfont&version=2.1.6') format('svg');
font-weight: normal;
font-style: normal;
}
/* My ICONFONT */
.iconFont {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'iconfont' !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconFont-icon_search:before {
content: "\ea0f";
}
.iconFont-burger_close:before {
content: "\e635";
}
Every single console.log is launching properly. Somehow firing the rerender method doesn't make the icons reappear. The CSS output is not a regular .css file - it's an output resource generated by a Java component (tho you can display this in the browser under a certain url, but it's not a file physically present on the server).
The components which launch the rerender metod are using the AppCacheBuster.
The application/portal as a whole is working on the SAP Enterprise Portal.
I simply can't determine when exactly this issue occurs, because it seems to happen in some pretty random (seemingly) circumstances. The built-in IconPool icons are always displaying properly.

how to get the font src url via webfont loader

i use webfont-loader dynamic load fonts via css files,code like this
WebFont.load({
custom: {
families: 'Lobster',
urls:'mydomain.com/lobster.css'
}
}
and in the css file,it like this
#font-face {
font-family: 'Lobster';
src: url('Lobster_1.3-webfont.woff') format('woff'),
url('Lobster_1.3-webfont.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
i wonder as if there is a way to get the font face url,like "Lobster_1.3-webfont.ttf" and "Lobster_1.3-webfont.woff" which defined in css.
in webfont-loader,there is no such api,if there is no directly way,i have to load the css as text file first,but i relly don't want do such things
do you guys have any clue?
PS:the reason why i need the font's url,is because i use fabric.js to create svg file and render it to PDF by phantom.js in node side,but looks that phantom.js did not support svg with #import url('xxx.css'). so i have to use #font-face with src.
Well, let's say that you are using a subset of fonts that you chose and not updated by users, so you know paths of the font you are currently using.
Fabricjs has an internal object that you can populate called:
fabric.fontPaths = { };
if you store there url information:
fabric.fontPaths = {Lobster: 'Lobster_1.3-webfont.woff'};
You will have font face embeeded in svg at export.
That should allow you to have a properly loaded svg in phantomjs.
First get font name with URL's.
var fontArray = [];
$.get("https://www.googleapis.com/webfonts/v1/webfonts?key={your key}&sort=popularity", {}, function (data) {
var count = 0;
$.each(data.items, function (index, value) {
count++
fontArray.push(value);
});
});
and then add to fabric.fontPaths.
$.each(fontArray, function (i, item) {
if (fontName == item.family) {
var ttfFile = item.files.regular;
var dataFile = [];
fabric.fontPaths[fontName] = "data:font/opentype;charset=utf-8;base64," + ttfFile;
}
})

Javascript Clock output text style

I have a clock on my website written in javascript which I got from the internet and modified it slightly to fit my needs better and it looks great on firefox using a mac, but when I use it in other browsers on a PC or Mac it looks terrible and I have no idea how to change it, I'm fairly new to javascript. The code i'm using is below:
var alternate=0
var standardbrowser=!document.all&&!document.getElementById
if (standardbrowser)
document.write('')
function show(){
if (!standardbrowser)
var clockobj=document.getElementById? document.getElementById("digitalclock") : document.all.digitalclock
var Digital=new Date()
var hours=Digital.getHours()
var minutes=Digital.getMinutes()
var dn="AM"
if (hours==12) dn="PM"
if (hours>12){
dn="PM"
}
if (hours==0) hours=0
if (hours.toString().length==1)
hours="0"+hours
if (minutes<=9)
minutes="0"+minutes
if (standardbrowser){
if (alternate==0)
document.tick.tock.value=hours+" : "+minutes+" "+dn
else
document.tick.tock.value=hours+" "+minutes+" "+dn
}
else{
if (alternate==0)
clockobj.innerHTML=hours+" : "+minutes
else
clockobj.innerHTML=hours+" : "+minutes
}
alternate=(alternate==0)? 1 : 0
setTimeout("show()",1000)
}
window.onload=show
I am looking to be able to set the font and size of the clock to be uniform across most browsers if not all, i'm not too bothered about IE because to site is terrible in ie anyway and will get round to sorting that out in the future.
You can use CSS to do this. Your clock is being displayed in a container with an ID of digitalclock. That means you can define style rules in an external stylesheet, inline or in the <head> of your .html file:
#digitalclock {
font-size: 1em;
font-family: arial, helvetica, sans-serif;
font-weight: normal;
color: red;
/* and whatever other rules you want */
}
why don't you just style the containing html element (#digitalclock) via css?

Categories

Resources