Angularjs HTML5 Video onended - html

I am loading html5 mp4 video and I want to trigger function from angular scope when video end. I tried below simple code but onended event cannot find the function in angular scope.
HTML
<video controls="controls" autoplay="true" ng-show="showVideo" ng-src="{{vidSrc}}" vid-dir onended="vidEnded()">
Angularjs function added in main controller. onended event triggered but function is undefined
$scope.vidEnded = function(){
console.log('vid ended')
}
Also tried adding function in directory like this but the function is not triggered.
.directive('vidDir', [function () {
return {
restrict: 'A',
link: function (scope, elem, attr) {
console.log(elem)
elem.onended = function(){
console.log('vid ended')
}
}
};
}]);

I believe the following code would achieve what you desire.
<video controls="controls" autoplay="true" ng-show="showVideo" ng-src="{{vidSrc}}" vid-dir (ended)="vidEnded()">

Add the following in front of your angular function:
angular.element(this).scope().vidEnded()
The result:
<video controls="controls" autoplay="true" ng-show="showVideo" ng-src="{{vidSrc}}" vid-dir onended="angular.element(this).scope().vidEnded()">

Another option would be to add a separate directive on-ended instead of the actual onended, like this:
/**
* Handles onended event for video tags.
* #example <video on-ended="awesomeFn()">
*/
.directive('onEnded', function () {
return {
scope: {
onEnded: '&'
},
link: function (scope, element) {
element.on('ended', scope.onEnded);
}
};
});

use this.vidEnded();
.directive('vidDir', [function () {
return {
restrict: 'A',
link: function (scope, elem, attr) {
console.log(elem)
this.vidEnded= function(){
console.log('vid ended')
}
}
};
}]);

Worked at this issue for a while and found a hack to the same problem that worked for me. Uses jQuery $timeOut function (you could also use the vanilla JS timeout, both are a typical hack with angular I find), and the html5 video events: "duration", "currentTime", and "timeupdate", which you can learn more about here. Using those event values you can calculate the end of the video and simulate the html5 video "ended" event. Here's the code
myCtrl.myVideo = document.getElementbyId("my-video-id"); //you need to have an id for your html5 video
//function to define in the controller
myCtrl.videoListener = function()
{
//javascript event listener function
myCtrl.myVideo.addEventListener('timeupdate',
function()
{
myCtrl.myVideo = this; //define "this"
var videoDuration = this.duration;
var videoCurrentTime = this.currentTime;
if (videoCurrentTime >= videoDuration)
{
//wrapping in a $timeOut function is the only way this works!
$timeout(function(){
//do something when the video ends, set variables, etc.
}, 0);
}
else if (videoCurrentTime < videoDuration)
{
//wrapping in a $timeOut function is the only way this works.
$timeout(function(){
//do something while the video is playing, set variables, etc.
}, 0);
}
});
}

You actually do not need a directive or plugin to run the onended event.(if jquery is integrated) Here is some logic to add in your controller. Make sure the video tag has an Id.remove the on ended attribute if you already have one in there. Then within your controller add this
var jq = $;
jq('#IdOfVideo').on('ended', function(){
//Whatever you want to happen after it has ended
});

Related

How to play/pause HTML video via click (or spacebar) when controls are hidden? [duplicate]

This question already has answers here:
play pause html5 video javascript
(8 answers)
Closed 10 months ago.
Here is my video code:
<video id="select-plan-vid" autoplay="" controlslist="nodownload" src="myvideo.mp4"></video>
The controls are hidden (correct), BUT I would still like users to be able to pause/play the video by clicking on it (or by pressing space bar, as I'm used to that method personally). I don't like that users can't pause it if they want to do so.
EDIT:
I have attempted Zayadur's answer; here is my javascript (put in the header of my page):
<script>
window.onload = function () {
var clickPlay = document.getElementById("select-plan-vid");
clickPlay.addEventListener("click", function () {
if (video.paused == true) {
video.play();
} else {
video.pause();
}
});
}
</script>
(Currently not working)
You can add a click listener and play / pause using javascript.
Check this answer for reference: https://stackoverflow.com/a/18855793/11578154
You can use JS to accomplish this.
This function targets id="video" and adds an event listener that tracks clicks.
window.onload = function () {
var clickPlay = document.getElementById("video");
clickPlay.addEventListener("click", function () {
if (video.paused == true) {
video.play();
} else {
video.pause();
}
});
}
And then you just add id="tag" to your video. I suggest customizing the id's to avoid redundancy. Your exercise now would be to see how you could capture the spacebar event.

"Show" button doesn't work with JSON file data [duplicate]

This question's answers are a community effort. Edit existing answers to improve this post. It is not currently accepting new answers or interactions.
I have a bit of code where I am looping through all the select boxes on a page and binding a .hover event to them to do a bit of twiddling with their width on mouse on/off.
This happens on page ready and works just fine.
The problem I have is that any select boxes I add via Ajax or DOM after the initial loop won't have the event bound.
I have found this plugin (jQuery Live Query Plugin), but before I add another 5k to my pages with a plugin, I want to see if anyone knows a way to do this, either with jQuery directly or by another option.
As of jQuery 1.7 you should use jQuery.fn.on with the selector parameter filled:
$(staticAncestors).on(eventName, dynamicChild, function() {});
Explanation:
This is called event delegation and works as followed. The event is attached to a static parent (staticAncestors) of the element that should be handled. This jQuery handler is triggered every time the event triggers on this element or one of the descendant elements. The handler then checks if the element that triggered the event matches your selector (dynamicChild). When there is a match then your custom handler function is executed.
Prior to this, the recommended approach was to use live():
$(selector).live( eventName, function(){} );
However, live() was deprecated in 1.7 in favour of on(), and completely removed in 1.9. The live() signature:
$(selector).live( eventName, function(){} );
... can be replaced with the following on() signature:
$(document).on( eventName, selector, function(){} );
For example, if your page was dynamically creating elements with the class name dosomething you would bind the event to a parent which already exists (this is the nub of the problem here, you need something that exists to bind to, don't bind to the dynamic content), this can be (and the easiest option) is document. Though bear in mind document may not be the most efficient option.
$(document).on('mouseover mouseout', '.dosomething', function(){
// what you want to happen when mouseover and mouseout
// occurs on elements that match '.dosomething'
});
Any parent that exists at the time the event is bound is fine. For example
$('.buttons').on('click', 'button', function(){
// do something here
});
would apply to
<div class="buttons">
<!-- <button>s that are generated dynamically and added here -->
</div>
There is a good explanation in the documentation of jQuery.fn.on.
In short:
Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on().
Thus in the following example #dataTable tbody tr must exist before the code is generated.
$("#dataTable tbody tr").on("click", function(event){
console.log($(this).text());
});
If new HTML is being injected into the page, it is preferable to use delegated events to attach an event handler, as described next.
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. For example, if the table exists, but the rows are added dynamically using code, the following will handle it:
$("#dataTable tbody").on("click", "tr", function(event){
console.log($(this).text());
});
In addition to their ability to handle events on descendant elements which are not yet created, another advantage of delegated events is their potential for much lower overhead when many elements must be monitored. On a data table with 1,000 rows in its tbody, the first code example attaches a handler to 1,000 elements.
A delegated-events approach (the second code example) attaches an event handler to only one element, the tbody, and the event only needs to bubble up one level (from the clicked tr to tbody).
Note: Delegated events do not work for SVG.
This is a pure JavaScript solution without any libraries or plugins:
document.addEventListener('click', function (e) {
if (hasClass(e.target, 'bu')) {
// .bu clicked
// Do your thing
} else if (hasClass(e.target, 'test')) {
// .test clicked
// Do your other thing
}
}, false);
where hasClass is
function hasClass(elem, className) {
return elem.className.split(' ').indexOf(className) > -1;
}
Live demo
Credit goes to Dave and Sime Vidas
Using more modern JS, hasClass can be implemented as:
function hasClass(elem, className) {
return elem.classList.contains(className);
}
The same jsfiddle Live demo embeded below:
function hasClass(elem, className) {
return elem.classList.contains(className);
}
document.addEventListener('click', function(e) {
if (hasClass(e.target, 'bu')) {
alert('bu');
document.querySelector('.bu').innerHTML = '<div class="bu">Bu<div class="tu">Tu</div></div>';
} else if (hasClass(e.target, 'test')) {
alert('test');
} else if (hasClass(e.target, 'tu')) {
alert('tu');
}
}, false);
.test,
.bu,
.tu {
border: 1px solid gray;
padding: 10px;
margin: 10px;
}
<div class="test">Test
<div class="bu">Bu</div>test
</div>
You can add events to objects when you create them. If you are adding the same events to multiple objects at different times, creating a named function might be the way to go.
var mouseOverHandler = function() {
// Do stuff
};
var mouseOutHandler = function () {
// Do stuff
};
$(function() {
// On the document load, apply to existing elements
$('select').hover(mouseOverHandler, mouseOutHandler);
});
// This next part would be in the callback from your Ajax call
$("<select></select>")
.append( /* Your <option>s */ )
.hover(mouseOverHandler, mouseOutHandler)
.appendTo( /* Wherever you need the select box */ )
;
You could simply wrap your event binding call up into a function and then invoke it twice: once on document ready and once after your event that adds the new DOM elements. If you do that you'll want to avoid binding the same event twice on the existing elements so you'll need either unbind the existing events or (better) only bind to the DOM elements that are newly created. The code would look something like this:
function addCallbacks(eles){
eles.hover(function(){alert("gotcha!")});
}
$(document).ready(function(){
addCallbacks($(".myEles"))
});
// ... add elements ...
addCallbacks($(".myNewElements"))
Try to use .live() instead of .bind(); the .live() will bind .hover to your checkbox after the Ajax request executes.
This is done by event delegation. Event will get bind on wrapper-class element but will be delegated to selector-class element. This is how it works.
$('.wrapper-class').on("click", '.selector-class', function() {
// Your code here
});
And HTML
<div class="wrapper-class">
<button class="selector-class">
Click Me!
</button>
</div>
#Note:
wrapper-class element can be anything ex. document, body or your wrapper. Wrapper should already exist. However, selector doesn't necessarily needs to be presented at page loading time. It may come later and the event will bind on selector without fail.
Event binding on dynamically created elements
Single element:
$(document.body).on('click','.element', function(e) { });
Child Element:
$(document.body).on('click','.element *', function(e) { });
Notice the added *. An event will be triggered for all children of that element.
I have noticed that:
$(document.body).on('click','.#element_id > element', function(e) { });
It is not working any more, but it was working before. I have been using jQuery from Google CDN, but I don't know if they changed it.
I prefer using the selector and I apply it on the document.
This binds itself on the document and will be applicable to the elements that will be rendered after page load.
For example:
$(document).on("click", 'selector', function() {
// Your code here
});
You can use the live() method to bind elements (even newly created ones) to events and handlers, like the onclick event.
Here is a sample code I have written, where you can see how the live() method binds chosen elements, even newly created ones, to events:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.16/jquery-ui.min.js"></script>
<input type="button" id="theButton" value="Click" />
<script type="text/javascript">
$(document).ready(function()
{
$('.FOO').live("click", function (){alert("It Works!")});
var $dialog = $('<div></div>').html('<div id="container"><input type ="button" id="CUSTOM" value="click"/>This dialog will show every time!</div>').dialog({
autoOpen: false,
tite: 'Basic Dialog'
});
$('#theButton').click(function()
{
$dialog.dialog('open');
return('false');
});
$('#CUSTOM').click(function(){
//$('#container').append('<input type="button" value="clickmee" class="FOO" /></br>');
var button = document.createElement("input");
button.setAttribute('class','FOO');
button.setAttribute('type','button');
button.setAttribute('value','CLICKMEE');
$('#container').append(button);
});
/* $('#FOO').click(function(){
alert("It Works!");
}); */
});
</script>
</body>
</html>
Another solution is to add the listener when creating the element. Instead of put the listener in the body, you put the listener in the element in the moment that you create it:
var myElement = $('<button/>', {
text: 'Go to Google!'
});
myElement.bind( 'click', goToGoogle);
myElement.append('body');
function goToGoogle(event){
window.location.replace("http://www.google.com");
}
Try like this way -
$(document).on( 'click', '.click-activity', function () { ... });
Take note of "MAIN" class the element is placed, for example,
<div class="container">
<ul class="select">
<li> First</li>
<li>Second</li>
</ul>
</div>
In the above scenario, the MAIN object the jQuery will watch is "container".
Then you will basically have elements names under container such as ul, li, and select:
$(document).ready(function(e) {
$('.container').on( 'click',".select", function(e) {
alert("CLICKED");
});
});
You can attach event to element when dynamically created using jQuery(html, attributes).
As of jQuery 1.8, any jQuery instance method (a method of jQuery.fn) can be used as a property of the object passed to the
second parameter:
function handleDynamicElementEvent(event) {
console.log(event.type, this.value)
}
// create and attach event to dynamic element
jQuery("<select>", {
html: $.map(Array(3), function(_, index) {
return new Option(index, index)
}),
on: {
change: handleDynamicElementEvent
}
})
.appendTo("body");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
you could use
$('.buttons').on('click', 'button', function(){
// your magic goes here
});
or
$('.buttons').delegate('button', 'click', function() {
// your magic goes here
});
these two methods are equivalent but have a different order of parameters.
see: jQuery Delegate Event
Here is why dynamically created elements do not respond to clicks :
var body = $("body");
var btns = $("button");
var btnB = $("<button>B</button>");
// `<button>B</button>` is not yet in the document.
// Thus, `$("button")` gives `[<button>A</button>]`.
// Only `<button>A</button>` gets a click listener.
btns.on("click", function () {
console.log(this);
});
// Too late for `<button>B</button>`...
body.append(btnB);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>
As a workaround, you have to listen to all clicks and check the source element :
var body = $("body");
var btnB = $("<button>B</button>");
var btnC = $("<button>C</button>");
// Listen to all clicks and
// check if the source element
// is a `<button></button>`.
body.on("click", function (ev) {
if ($(ev.target).is("button")) {
console.log(ev.target);
}
});
// Now you can add any number
// of `<button></button>`.
body.append(btnB);
body.append(btnC);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>
This is called "Event Delegation". Good news, it's a builtin feature in jQuery :-)
var i = 11;
var body = $("body");
body.on("click", "button", function () {
var letter = (i++).toString(36).toUpperCase();
body.append($("<button>" + letter + "</button>"));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>A</button>
Any parent that exists at the time the event is bound and if your page was dynamically creating elements with the class name button you would bind the event to a parent which already exists
$(document).ready(function(){
//Particular Parent chield click
$(".buttons").on("click","button",function(){
alert("Clicked");
});
//Dynamic event bind on button class
$(document).on("click",".button",function(){
alert("Dymamic Clicked");
});
$("input").addClass("button");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="buttons">
<input type="button" value="1">
<button>2</button>
<input type="text">
<button>3</button>
<input type="button" value="5">
</div>
<button>6</button>
Bind the event to a parent which already exists:
$(document).on("click", "selector", function() {
// Your code here
});
Another flexible solution to create elements and bind events (source)
// creating a dynamic element (container div)
var $div = $("<div>", {id: 'myid1', class: 'myclass'});
//creating a dynamic button
var $btn = $("<button>", { type: 'button', text: 'Click me', class: 'btn' });
// binding the event
$btn.click(function () { //for mouseover--> $btn.on('mouseover', function () {
console.log('clicked');
});
// append dynamic button to the dynamic container
$div.append($btn);
// add the dynamically created element(s) to a static element
$("#box").append($div);
Note: This will create an event handler instance for each element (may affect performance when used in loops)
Use the .on() method of jQuery http://api.jquery.com/on/ to attach event handlers to live element.
Also as of version 1.9 .live() method is removed.
I prefer to have event listeners deployed in a modular function fashion rather than scripting a document level event listener. So, I do like below. Note, you can't oversubscribe an element with the same event listener so don't worry about attaching a listener more than once - only one sticks.
var iterations = 4;
var button;
var body = document.querySelector("body");
for (var i = 0; i < iterations; i++) {
button = document.createElement("button");
button.classList.add("my-button");
button.appendChild(document.createTextNode(i));
button.addEventListener("click", myButtonWasClicked);
body.appendChild(button);
}
function myButtonWasClicked(e) {
console.log(e.target); //access to this specific button
}
<html>
<head>
<title>HTML Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
</head>
<body>
<div id="hover-id">
Hello World
</div>
<script>
jQuery(document).ready(function($){
$(document).on('mouseover', '#hover-id', function(){
$(this).css('color','yellowgreen');
});
$(document).on('mouseout', '#hover-id', function(){
$(this).css('color','black');
});
});
</script>
</body>
</html>
I was looking a solution to get $.bind and $.unbind working without problems in dynamically added elements.
As on() makes the trick to attach events, in order to create a fake unbind on those I came to:
const sendAction = function(e){ ... }
// bind the click
$('body').on('click', 'button.send', sendAction );
// unbind the click
$('body').on('click', 'button.send', function(){} );

Angular - append html through different frames with one module per frame

My code have the main windonw and one iframe and each one with your module. A button in main window fires click event that should append html into iframe, the new html when appended into that should apply interceptors and directives properly, but it doesn't work!
Angular javascript:
angular.module('module1',[]).controller('Controller1', function ($scope) {
$scope.get = function(){
$http.jsonp("some_url_here").success(function(html){
$scope.content = html;
});
}
}).directive('click', function($compile) {
return {
link: function link(scope, element, attrs) {
element.bind('click',function(){
var unbind = scope.$watch(scope.content, function() {
var div=document.getElementById("frame").contentWindow.angular.element("divId");
div.append($compile(scope.content)(div.scope()));
unbind();
});
});
}
}
});
angular.module('module2',[]).directive('a', function() {
return {
restrict:'E',
link: function(scope, element, attrs) {
console.log('ping!');
console.log(attrs.href);
}
};
});
Html code:
<html ng-app="modile1">
<div ng-controller="Controller1">
<button type="button", ng-click="get('any_value')", click:""/> Load frame
</div>
<iframe id="frame" src="/please/ignore/this">
<!-- considere the html as appended from iframe-src and contains ng-app="module2" -->
<html ng-app="module2">
<div id="divId">
<!-- code should be inject here -->
</div>
</html>
</iframe>
</html>
Please, considere that angularjs, jquery if applicable, modules-declaration as well as headers are loaded properly.
I'd like to load the html content from main-frame/window into iframe and run interceptors and directives properly. Is it possible? If yes, how can I do it?
Thanks for advancing!
I've tried this code and it seems work fine! I found it here: http://www.snip2code.com/Snippet/50430/Angular-Bootstrap
var $rootElement = angular.element(document.getElementById("frame").contentWindow.document);
var modules = [
'ng',
'module2',
function($provide) {
$provide.value('$rootElement', $rootElement)
}
];
var $injector = angular.injector(modules);
var $compile = $injector.get('$compile');
$rootElement.find("div#divId").append(scope.content);
var compositeLinkFn = $compile($rootElement);
var $rootScope = $injector.get('$rootScope');
compositeLinkFn($rootScope);
$rootScope.$apply();

How to capture onstop in marquee

I know that Chrome don't support onfinish event on marquee tag.
I decide to use JQuery plugin here: http://remysharp.com/2008/09/10/the-silky-smooth-marquee/
In this post, the author say it can capture stop event.
How ever, I cannot use it. Please help me out.
you should call .bind() for the the stop event. please see my example code below. hope it helps.
$(document).ready(
function(){
$('div.demo').marquee('pointer')
.bind('stop', function() {alert($(this).text())})
.mouseover(function () {
$(this).trigger('stop');
//alert($(this).html());
}).mouseout(function () {
$(this).trigger('start');
}).mousemove(function (event) {
if ($(this).data('drag') == true) {
this.scrollLeft = $(this).data('scrollX') + ($(this).data('x') - event.clientX);
}
}).mousedown(function (event) {
$(this).data('drag', true).data('x', event.clientX).data('scrollX', this.scrollLeft);
}).mouseup(function () {
$(this).data('drag', false);
})
});

HTML5 video error handling

I need to tell, whether video cannot be played ("x" sign is shown in browser).
This code does't works. "onerror" event will never be fired under Firefox
var v = document.getElementsByTagName("video")[0];
if ( v != undefined )
v.onerror = function(e) {
if ( v.networkState == v.NETWORK_NO_SOURCE )
{
// handle error
}
}
What's wrong here ?
"onerror" is not a valid event type for <video>
Use "error" instead.
document.getElementsByTagName('video')[0].addEventListener('error', function(event) { ... }, true);
For a complete list of events for <video> go here: https://developer.mozilla.org/En/Using_audio_and_video_in_Firefox
From Firefox 4 onwards, the 'error' event is dispatched on the <source> element.
And you should add an error handler on the only/last source:
HTML
<video id="vid" controls>
<source src="dynamicsearch.mp4" type="video/mp4"></source>
<source src="otherdynamicsearch.avi" type="video/avi"></source>
</video>
JS
var v = document.querySelector('video#vid');
var sources = v.querySelectorAll('source');
if (sources.length !== 0) {
var lastSource = sources[sources.length-1];
lastSource.addEventListener('error', function() {
alert('uh oh');
});
}
JQuery
$('video source').last().on('error', function() {
alert('uh oh');
});
AngularJS
You can create an error handling directive (or just use ng-error):
<video id="vid" controls>
<source src="dynamicsearch.mp4" type="video/mp4"></source>
<source src="otherdynamicsearch.avi" type="video/avi" ng-error="handleError()"></source>
</video>
Where the error handling directive's link function should do (copied from ng-error):
element.on('error', function(event) {
scope.$apply(function() {
fn(scope, {$event:event});
});
});
It's good to know that Chrome and Firefox have different onerror callbacks. The error must therefore be mapped. Mozilla uses error.originalTarget.
Here is a sample on how to do it with pure JavaScript:
const file = 'https://samples.ffmpeg.org/MPEG-4/MPEGSolution_jurassic.mp4';
window.fetch(file, {mode: 'no-cors'})
.then((response) => response.blob())
.then((blob) => {
const url = window.URL.createObjectURL(blob);
const video = document.createElement('video');
video.addEventListener('error', (event) => {
let error = event;
// Chrome v60
if (event.path && event.path[0]) {
error = event.path[0].error;
}
// Firefox v55
if (event.originalTarget) {
error = error.originalTarget.error;
}
// Here comes the error message
alert(`Video error: ${error.message}`);
window.URL.revokeObjectURL(url);
}, true);
video.src = url;
document.body.appendChild(video);
});
The above example maps an incoming error event into a MediaError which can be used to display an error playback message.
To catch error event, you should use video.addEventListener():
var video = document.createElement('video');
var onError = function() { // your handler};
video.addEventListener('error', onError, true);
...
// remove listener eventually
video.removeEventListener('error', onError, true);
Note that the 3rd parameter of addEventListener (on capture) should be set to true. Error event is typically fired from descendants of video element ( tags).
Anyway, relying on video tag to fire an error event is not the best strategy to detect if video has played. This event is not fired on some android and iOS devices.
The most reliable method, I can think of, is to listen to timeupdate and ended events. If video was playing, you'll get at least 3 timeupdate events. In the case of error, ended will be triggered more reliably than error.
Try adding the event listener to the tag instead - I think the onerror attribute ("error" event) works on the source tag now, not the video tag.
Pug example
video(src= encodeURI(item.urlVideo), type='video/mp4' onerror="myFunction('param',this)")
script(src='/javascripts/onerror.js')
function myFunction(param, me) {
console.log(me);
me.poster = './images/placeholder.jpg'; }