Site: http://blieque.comli.com/motion
Before I start, I know there are many alternatives to Lightbox2 (Lokesh Dhakar's) to display videos but I want to avoid using three different JavaScript things, already using MooTools and JQuery as I'd like to keep HTTP requests to a minimum, as well as disk usage.
As it comes, Lightbox2 does not support videos, full stop. However, I noticed that the JavaScript was essentially taking the contents of an a's href attribute and placing it in an img's src attribute when the light-box was open. As far as I can see, changing this img to an iframe (done) and setting the anchor tag's href to youtube.com/embed/*video-id* should generate an iframe containing that YouTube video (replacing watch?id= with embed/ presents a full screen version of the video.
I then also added JavaScript for width, height and frameborder attributes on top of the default class="lb-image". Now when the page is loaded and the Lightbox called it creates an empty window. If you inspect the code you can see all the attributes are there but the page in the frame frame isn't loaded, just creating an empty head and body tag.
I was just wondering if it was a server problem or a code problem and if so, how to fix it. If there any way to make it work?
Thanks
Note: I'm NOT using Drupal so the lightvideo option is not available.
Maybe someone still is looking for solution.
I had same problem in my project. Not with youtube iframe but it’s not difficult to implenet it. Lightbox2 cannot be extended so i wrote simple class whose adding listener and observer. For proper displaing require is that video have a poster with the same sizes. That’s the fastest way to keep corret sizes of popup.
In href required is to add dataset href with image url
<a href="POSTER_URL" data-href="VIDEO_URL" data-lightbox="Videos">
Open Lightbox
</a>
SCSS to cover image in popup and set fade effect while loading
.lightbox {
.lb {
&-outerContainer {
video {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 9999;
width: 100%;
height: auto;
opacity: 1;
transition: opacity 300ms ease-in-out;
border: none;
outline: none;
&:hover, &:focus {
border: none;
outline: none;
}
}
&.animating {
video {
opacity: 0;
}
}
}
&-container {
position: relative;
.lb-image {
border: none;
}
}
}
}
And JS class which one create and set video into popup. Maybe little messy but i don't care. It's only quick solution.
class LightBoxVideo {
constructor() {
this.videos = {};
this.lightBoxVideo();
}
lightBoxVideo = () => {
this.setEvents();
this.setMutationObserver();
}
setMutationObserver = () => {
const observer = new MutationObserver(mutation => {
const imageMutations = mutation.filter((m) => {
return m.attributeName === "src" && m.target.className === 'lb-image'
});
const overlayDisplay = window.getComputedStyle(document.querySelector('.lightboxOverlay'), null).display;
if("none" === overlayDisplay) {
this.removeVideoElement();
}
if(imageMutations.length > 0) {
if(this.videos[imageMutations[0].target.src]) {
this.removeVideoElement();
this.setVideoElement(this.videos[imageMutations[0].target.src]);
}
}
});
observer.observe(document.body, {
childList: false,
attributes: true,
subtree: true,
characterData: false
});
}
setEvents = () => {
const videoLinks = this.findVideoLinks();
videoLinks.forEach((link) => {
this.videos[link.href] = link;
link.addEventListener('click', (e) => {
this.removeVideoElement();
this.setVideoElement(e.target);
});
});
}
setVideoElement = (element) => {
const lightbox = document.querySelector('.lightbox')
const container = lightbox.querySelector('.lb-container');
const videoElement = this.createVideoElement(element);
container.prepend(videoElement);
}
removeVideoElement = () => {
const lightbox = document.querySelector('.lightbox')
const container = lightbox.querySelector('.lb-container');
const video = container.querySelector('video');
if(video) {
container.removeChild(video);
}
}
createVideoElement = (element) => {
const video = document.createElement('video');
video.setAttribute('poster', element.href);
video.setAttribute('controls', 'true');
const source = document.createElement('source');
source.setAttribute('src', element.dataset.href);
source.setAttribute('type', 'video/mp4');
video.append(source);
return video;
}
findVideoLinks = () => {
const hrefs = document.querySelectorAll('a[data-lightbox]');
const regex = /\.(mp4|mov|flv|wmv)$/;
if(0 === hrefs.length) {
return [];
}
return Array.from(hrefs).filter((href) => {
return !! href.dataset.href.match(regex);
});
}
}
To preview how it's work - codepen here: https://codepen.io/PatrykN/pen/RwKpwMe
Enabling video support
By default, support for videos is disabled. However, this can be easily enabled by checking the enable video support option on admin/settings/lightbox2.
Basic Example
This Video
Reference
Related
First, please note that I'm using Elementor Pro for this project, then I can't use css easily because the html structure is complex. That's why I'm trying to do this with Javascript.
I'd like to move my image above my h1 title for tablets and mobile, and that it stays above the description on larger screens.
Here is my code (pure javascript, no jquery)
let title = document.getElementById("product-title");
let image = document.getElementById("product-image");
let resizeProductPage = function () {
if(typeof(title) != 'undefined' && title != null){
let width = window.innerWidth;
if (width<1025)
title.parentNode.insertBefore(image, title);
else
image.parentNode.insertBefore(title, image);
}
};
resizeProductPage();
window.addEventListener("resize", resizeProductPage);
but it doesn't work, and I find the code rather complex. would you have an idea?
Your code seems to be fine, but here is what you should do.
Under edit with elementor, at the component editor you
should be able to specify the ID for the title element to: product-title & the image element to: product-image
Your conditional checking if the title element exists should be adapted to just if (title) as it will return false if that element does not exist.
Other than that your code should be working fine, I made a working solution as a snippet.
let title = document.getElementById("product-title");
let image = document.getElementById("product-image");
let resizeProductPage = function() {
if (title) {
if (window.innerWidth < 1025)
title.parentNode.insertBefore(image, title);
else
image.parentNode.insertBefore(title, image);
}
};
resizeProductPage();
window.addEventListener("resize", resizeProductPage);
#product-image {
background: black;
text-align: center;
height: 200px;
width: 400px;
}
#product-image p {
color: #fff;
}
<h1 id="product-title">Product Title</h1>
<div id="product-image">
<p>Image Here</p>
</div>
I have a chat bot that I am working on that is using websdk and the images are added through a tool from Oracle which will not accept image attributes, you just put the image urls in the correct fields. However the images are resized to the tiny chatbot iframe and I would like to figure out how to enlarge them on click or hover. Outside of the tool I can apply Javascript and CSS, I just cant edit the HTML as far as I can tell because it's rendered by the tool.
Here is what the tool looks like, see how its not using HTML, you just input the links?
Oracle Tool
I used hover CSS and that words to enlarge the images but the image is still stuck inside the iframe sized so its not a real solution.
.oda-chat-card-image:hover {
-ms-transform: scale(1.5); /* IE 9 */
-webkit-transform: scale(2); /* Safari 3-8 */
transform: scale(2);
Hover Example
I found some code that puts the image into a target div which is the whole background iframe. I use this iframe to open links in the background of the chat bit that the bot suggests so the used stays in the same window. Which I like, but it requires the images have inline attributes that I cannot add.
I thought if I could use some Javascript to add the attributes to all images that I could get what I want but that seems to require adding unique IDs to the images but that is something I cannot do. I wondered what other possibilities anyone else could suggest.
The images do have a class so perhaps I can access them that way somehow. That's how I was able to apply the hover CSS class=oda-chat-card-image
This is the code I tried to use for opening the image on click in the background iframe which I named name="mainPageIframe" id="mainPageIframe"
const change = src => {
document.getElementById('mainPageIframe').src = src
If I inspect the image in the browser and manually add
onclick="change(this.src)"
to the end of an image,
Example code rendered by the chatbot tool but edited where I added the onclick attribute manually while in f12 inspecting the image.
<img dir="auto" class="oda-chat-card-image" src="https://eservice.xactware.com/esc/showme/images/Annotated/1379/Bot/X1ForgotPassword.png" onclick="change(this.src)" alt="Card image">
Example of adding the attribute manually to produce the result I want
then when I click the image it opens in the background iframe. Doing this is the best thing I could think of but I would rather have the image open as an enlarged overlay over the whole screen instead of trapped inside the iframe. That's why I thought copying it to the main page iframe would be a solution. Any ideas on how I can apply
onclick="change(this.src)"
to all images in my webpage without unique id's or perhaps apply a different solution all together?
This is the HTML file I am working on that I can edit. It calls the bot contents by the URI and channelID. I am able to apply CSS to the items in the bot and Javascript, I just don't know how to get the images in the bnot to pop up or out side of the chatbot iframe and enlarge.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>ODA Live Chat Example</title>
</head>
<body>
<iframe name="mainPageIframe" id="mainPageIframe" src="https://docs.oracle.com/en/cloud/paas/digital-assistant/index.html" style="position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:0;">
Your browser doesn't support iframes
</iframe>
<script src="https://polyfill.io/v3/polyfill.js?features=Number.isInteger%2Cdefault"></script>
<style>
.oda-chat-card-image:hover {
-ms-transform: scale(1.5); /* IE 9 */
-webkit-transform: scale(2); /* Safari 3-8 */
transform: scale(2);
}
.more
{
display: none;
}
img {
max-width: 100%;
height: auto;
}
.oda-chat-conversation {
background-color: #b3b8bd;
}
.oda-chat-left .oda-chat-message-bubble, .oda-chat-left .oda-chat-message-header, .oda-chat-left .oda-chat-message-footer, .oda-chat-card, .oda-chat-message-global-actions .oda-chat-action-postback {
background-color: #2e5a81;
}
.oda-chat-message-actions {
background-color: #1f3b54;
}
.oda-chat-message-global-actions .oda-chat-action-postback, .oda-chat-message-actions .oda-chat-action-postback {
background-color: #1f3b54;
margin: 0px;
font-weight: normal;
}
.oda-chat-action-postback {
background-color: #1f3b54;
}
.oda-chat-header {
background-color: #1f3b54;
}
.oda-chat-footer {
background-color: #1f3b54;
}
.oda-chat-card-image {
max-width: 100%;
height: auto;
}
.oda-chat-button {
height: 20px;
width: 100px;
outline: none;
background-color: transparent;
box-shadow: none;
}
#media screen and (min-width: 769px){
.oda-chat-button {
height: 96px;
width: 96px;
}
</style>
<script>
const change = src => {
document.getElementById('mainPageIframe').src = src
}
//Function to toggle full pargraph showing
function showHideParagraphs(lidx) {
var dots = document.getElementById("dots"+lidx);
var moreText = document.getElementById("more"+lidx);
var btnText = document.getElementById("myBtn"+lidx);
btnText.blur(); //loose focus
if (dots.style.display === "none") {
dots.style.display = "inline";
btnText.innerHTML = "Read more";
moreText.style.display = "none";
} else {
dots.style.display = "none";
btnText.innerHTML = "Show less";
moreText.style.display = "inline";
}
}
//Function to split paragraphs with show more:
var gidx = 0;
function splitParagraph(txt) {
var paragraphs = txt.split("\n\n");
if (paragraphs.length > 1) {
var html = "<p>"+paragraphs[0]+'<span id="dots'+gidx+'">...</span></p><span id="more'+gidx+'" class="more">';
for (var idx = 1; idx < paragraphs.length; idx++) {
html += "<p>"+paragraphs[idx]+"</p>";
}
html += '</span><div class="oda-chat-message-actions"><button class="oda-chat-action-postback" onclick="showHideParagraphs('+gidx+')" id="myBtn'+gidx+'">Read more</button></div>';
gidx++;
return html;
}
else {
return txt;
}
}
//For typing indicator for keepTurn messages
var moreToCome = 0;
var typingIndicatorSafeguard=null;
function increaseMoreToCome(n){
moreToCome += n;
if (typingIndicatorSafeguard!=null){
clearTimeout(typingIndicatorSafeguard);
}
typingIndicatorSafeguard = setTimeout(function(){moreToCome=0;}, 30000); //In any case only keep generating typing indicators for 30 seconds
}
function decreaseMoreToCome(){
if (moreToCome>0){
moreToCome--;
setTimeout(function() {Bots.showTypingIndicator(30);}, 0);
}
}
//End of for typing indicator for keepTurn messages
var delegate = {
beforeDisplay: function(message) {
if (message.messagePayload.type=='text'){
//Split paragrahps with show-more
message.messagePayload.text = splitParagraph(message.messagePayload.text);
}
//Typing indicator for keepTurn messages
//Solution pending: https://bug.oraclecorp.com/pls/bug/webbug_edit.edit_info_top?rptno=30944113
if (message.messagePayload.text && message.messagePayload.text.match((/^((Greetings)|(Salutations)).*I am Artie/))){
increaseMoreToCome(2);
}
decreaseMoreToCome();
//End of typing indicator for keepTurn messages
return message;
},
beforeSend: function(message) {
//For typing indicator for keepTurn messages
moreToCome=0;
//End of for typing indicator for keepTurn messages
return message;
}
};
var chatWidgetWebSettings = {
URI: 'oda-12e973c924a245abbd61f357fbbe926a-da2.data.digitalassistant.oci.oraclecloud.com',
channelId: 'a61c229b-5469-4ea0-b62c-686bc3c1ad76',
enableClearMessage: true,
enableLocalConversationHistory: true,
enableTimestamp: true,
enableAttachment: false,
showTypingIndicator: true,
enableSpeech: true,
enableBotAudioResponse: true,
//openLinksInNewWindow: true, //Disabled as it's not really needed and is superseeding link onclick handler added through delegate.
enableAutocomplete: true,
disablePastActions: 'none',
delegate: delegate,
botButtonIcon: 'images/RbotIcon.png',
botIcon: 'images/RbotIcon.png',
logoIcon: 'images/RbotIcon.png',
personIcon: 'images/personIcon.png',
agentAvatar: 'images/woman2.png',
i18n: { "en": { chatTitle: 'Oracle Digital Assistant',
chatSubtitle: "Help on digital assistant learning"
}
},
typingIndicatorTimeout: 30,
messagePadding: '10px',
//theme: WebSDK.THEME.redwood-dark,
theme: 'redwood-dark',
linkHandler: { target: 'mainPageIframe' },
enableDraggableButton: true
};
function initSdk(name) {
// Default name is Bots
if (!name) {
name = 'Bots';
}
setTimeout(function (){
const Bots = new WebSDK(chatWidgetWebSettings); // Initiate library with configuration
Bots.on('widget:opened', function() {
console.log('Widget opened!');
if (Bots.getConversationHistory().messagesCount < 1){
console.log("Starting the conversation");
Bots.sendMessage('Hello', { hidden: true });
}
});
Bots.connect() // Connect to server
.then(function (){
console.log("Connection Successful");
})
.catch(function (reason){
console.log("Connection failed");
console.log(reason);
});
window[name] = Bots;
});
}
</script>
<script src="files/web-sdk_21.02.js" onload="initSdk()"></script>
</body>
</html>
I am finishing up a beat/sample pad project using web audio and JavaScript. When I click on a sample pad, I want it to sound instantly, and this works fine on the desktop version, however, on mobile, the sample will only sound once I have lifted my finger away from the screen. This then creates a lag of sorts, and you definitely wouldn't be able to play along in time with any music for example. Is there a way to get the sample to sound instantly as soon as it's pressed on a mobile device? I've found that the "mousedown" event seems to work best for desktop, but I can't get the same result with anything that I have tried for mobile.
Please feel free to take a look at the Beat pad project here: http://beatpad.dwcreate.co.uk/
I'll post code later if needed.
Many thanks to anyone who can help me with this problem!
Audio element seems to have latency issues on Apple touch devices...not sure about the others. AudioBuffer will provide more reliable playback immediacy across browsers. After some fiddling, it seems the "touchstart" event must be bound on the document and wasn't working when bound on the element.
// samples identified by keyboard key code
const samples = [49,50,51,52,53,54,55,56,57,48,45,61,113,119,101,114,116,121,117,105,111,112,91,93,97,115,100,102,103,104,106,107,108,59,39,122,120,99,118,98,110,109,44,46,47]
// audio contect for decoding and playback
const ctx = new (window.AudioContext || window.webkitAudioContext)({ latencyHint: 'playback' })
// decoded AudioBuffers we'll create for each sample
audioBuffers = {}
// keyboard keys to render
keys = {}
// track download/decode progress
let totalLoaded = 0
init()
function init() {
samples.forEach(async keyCode => {
const url = `https://cøder.com/static/61881209/${keyCode}.mp3`
const buffer = await (await fetch(url)).arrayBuffer()
ctx.decodeAudioData(buffer, decoded => {
audioBuffers[keyCode] = decoded
fileLoaded()
})
})
}
// keyboard press
document.addEventListener('keypress', playSample)
function fileLoaded() {
totalLoaded++
document.querySelector('#progress').innerText = `Samples Loaded: ${totalLoaded} / ${samples.length}`
if (totalLoaded === samples.length) {
initKeyboard()
}
}
function playSample({ keyCode }) {
// play audio sample from beginning
const [audioBuffer, key] = [audioBuffers[keyCode], keys[keyCode]]
if (audioBuffer) {
const bufferSource = ctx.createBufferSource()
bufferSource.buffer = audioBuffer;
bufferSource.connect(ctx.destination);
bufferSource.start(0);
requestAnimationFrame(_ => {
key.classList.remove('pressed')
requestAnimationFrame(_ => {
key.classList.add('pressed')
})
})
}
}
// draw keyboard on screen
function initKeyboard() {
const keyboard = document.createElement('div')
keyboard.className = 'keyboard'
let row = 0 // keyboard row
const breakOn = ['q', 'a', 'z'] // start new row
// render each keyboard key
samples.forEach(keyCode => {
const char = String.fromCharCode(keyCode)
const key = document.createElement('div')
key.className = 'key'
key.innerText = char
key.dataset.code = keyCode
// start new row
if (breakOn.includes(char)) {
row++
key.style.clear = 'both'
key.style.marginLeft = `${20*row}px`
}
keys[keyCode] = key
keyboard.append(key)
})
// bind click or touch if it's a tablet
if ('ontouchstart' in window) {
document.addEventListener('touchstart', handleClick)
} else {
document.addEventListener('mousedown', handleClick)
}
function handleClick(e) {
const {dataset: {code}} = e.target
if (code) {
playSample({ keyCode: code })
}
}
document.body.innerHTML = ''
document.body.append(keyboard)
}
body {
font-family: 'open sans light', arial, sans-serif;
background: #f9f9f9;
}
.keyboard {
width: 620px;
overflow: hidden;
white-space: nowrap;
}
.key {
background: #fff;
cursor: pointer;
height: 40px;
width: 40px;
margin: 2px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #ccc;
float: left;
border-radius: 6px;
user-select: none;
}
.key.pressed {
animation: keyPress .5s ease-out;
}
#keyframes keyPress {
0% {
background: lime;
}
100% {
background: #fff;
}
}
<div id="progress">Loading...</div>
According to the Mozilla MDN, you might be looking for the touchstart event.
function onTouchQuick() {
// ...
}
element.addEventListener("touchstart", onTouchQuick);
I'm trying to create a function in my React project that will create a new element that plays one loop of a gif, then deletes the element. So far I have been semi-successful using this:
function playGif() {
var gif = document.createElement('img')
gif.className = "gif-play"
document.body.appendChild(gif)
setTimeout(() => { gif.parentNode.removeChild(gif); }, 600)
}
With the following CSS:
.gif-play {
width: 256px;
height: 256px;
position: absolute;
background-image: url(./assets/images/animations/atk1.gif);
background-size: cover;
}
However, no matter what I set the timeout to delete the element (even the precise duration of the gif in milliseconds) the gif will still start clipping and playing from the middle after several button clicks.
I have also tried setting the image src directly like this:
function playGif() {
var gif = document.createElement('img')
gif.src = require('./assets/images/animations/atk1.gif')
document.body.appendChild(gif)
setTimeout(() => { gif.parentNode.removeChild(gif); }, 600)
}
But with this the gif never shows up at all, just a broken image. I thought using require() in React was supposed to fix that, but it still doesn't work for me.
So any tips on how to get my gif to play from the beginning when I call the function would be great.
Thanks!
not sure exactly what you want but I have prepared one animation for you. You can check it. Hope it helps you.
PlayGifExample Component
import React, {useEffect} from "react";
import PlayGif from './PlayGif.css'
function PlayGifExample() {
function animate(i) {
if(i > 1000)
return;
let gif = document.createElement('img');
gif.className = "gif-play";
gif.style.cssText = `left: ${i}px`;
console.log(i);
document.body.appendChild(gif);
setTimeout(() => {
gif.parentNode.removeChild(gif);
animate(i+1);
}, 10)
}
function playGif() {
let gif = document.createElement('img');
gif.className = "gif-play";
gif.style.cssText = `left: 1px`;
document.body.appendChild(gif);
setTimeout(() => {
gif.parentNode.removeChild(gif);
animate(2);
}, 10)
}
return (
<>
<button onClick={playGif}>Click me</button>
</>
);
}
export default PlayGifExample;
PlayGif.css
.gif-play {
width: 256px;
height: 256px;
position: absolute;
background-image: url(./images/4.png);
background-size: cover;
}
I am trying to Lazy load some images onto a website. The problem that I am encountering is that I have to remove the class for the lazy loading to work but I still want the CSS properties to be used by the image. Can anyone help?
// JavaScript Document
document.addEventListener("DOMContentLoaded", function() {
var lazyImages = [].slice.call(document.querySelectorAll("img.Images"));
if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("Images");
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// Possibly fall back to a more compatible method here
}
});
.Images {
width: 190px;
height: 190px;
padding: 10px;
object-fit: cover;
}
<img class="Images" src="https://i.imgur.com/mf5YDAy.jpg" data-src="https://i.imgur.com/mf5YDAy.jpg" data-srcset="https://i.imgur.com/mf5YDAy.jpg, https://i.imgur.com/mf5YDAy.jpg" />