Array callback for every item apart from one - javascript

I have the following array:
var videos = [];
$('.video').each(function(){
videos.push($(this).attr('id'));
});
and inside here:
events: {
onPlay: function() {
var id = vid.attr('id');
var index = videos.indexOf(id);
videos.splice(index, 1);
console.log(videos);
}
}
I have also tried something like this:
// if($(this).attr('id') != $('.video').attr('id') ){
// jwplayer( $('.video').attr('id')).stop();
// }
I want to run a function for every array item (videos) apart from the array value equal to:
vid.attr('id')
I am using splice but that removes the item completely and since I have multiple videos I will end up with an empty array. I basically want only one video to play at the time so I need to stop the others.
Full function code:
$(function(){
//add attributes to your ".video" DIV placeholder to customimize output
//example: data-autostart="true" - see customSettings Object below.
if (typeof(jwplayer) !== 'undefined'){
var videos = [];
$('.video').each(function(){
videos.push($(this).attr('id'));
});
$('.video').each(function(){
var vid = $(this),
videoSettings = {},
settings = {
autostart: false,
width: '100%',
aspectratio: '16:9',
image: ''
},
customSettings = {
autostart: vid.attr('data-autostart'),
width: vid.attr('data-width'),
aspectratio: vid.attr('data-aspectratio'),
image: vid.attr('data-image')
};
$.extend(videoSettings, settings, customSettings);
var playerInstance = jwplayer( vid.attr('id') ).setup({
primary: "flash",
file: Drupal.settings.basePath + $(this).attr('data-src'),
autostart: videoSettings.autostart,
aspectratio: videoSettings.aspectratio,
image: Drupal.settings.basePath + videoSettings.image,
skin: "glow",
stretching: "exactfit",
width: videoSettings.width,
events: {
onPlay: function() {
var id = vid.attr('id');
var index = videos.indexOf(id);
videos.splice(index, 1);
console.log(videos);
// if($(this).attr('id') != $('.video').attr('id') ){
// jwplayer( $('.video').attr('id')).stop();
// }
}
}
// ga:{idstring: videoSettings.videotitle,
// trackingobject: "pageTracker"
// }
});
});
}
});

What about a simple forEach?
onPlay: function() {
var id = vid.attr('id');
videos.forEach(function(videoId) {
if(videoId != id) {
//You may want to check first if video is running at all
jwplayer(videoId).stop();
}
});
}

onPlay: function() {
var id = vid.attr('id');
for (var v in videos) {
if(id != v[i]) {
jwplayer(v[i].stop();
}
}
}

Related

Videojs - current index as variable

I have a VideoJs player that plays from a playlist.
I want to display which video number is currently playing from the playlist. Since I need the total number of videos anyway to create the playlist, I can use the defined variable there.
e.g. "Video 3 of 10".
For this I try by means of:
var test = player.currentIndex(videoList);
To pass the current number of the video into the variable.
Unfortunately without success.
var lerneinheit = "E1";
var videocounter = 26;
var videonumber= 0;
const videoList = Array.from({ length: videocounter }, (_, i) => {
return { sources: [{ src: `https://www.XXX.de/player/${lerneinheit}_${videonumber + i + 1}.mp4`, type: 'video/mp4' }], };
});
var player = videojs(document.querySelector('video'), {
inactivityTimeout: 0
});
try {
player.volume(1);
} catch (e) {}
player.playlist(videoList);
document.querySelector('.previous').addEventListener('click', function() {
player.playlist.previous();
});
document.querySelector('.next').addEventListener('click', function() {
player.playlist.next();
});
player.playlist.autoadvance(0); // play all
Array.prototype.forEach.call(document.querySelectorAll('[name=autoadvance]'), function(el) {
el.addEventListener('click', function() {
var value = document.querySelector('[name=autoadvance]:checked').value;
//alert(value);
player.playlist.autoadvance(JSON.parse(value));
});
});
/* ADD PREVIOUS */
var Button = videojs.getComponent('Button');
// Extend default
var PrevButton = videojs.extend(Button, {
//constructor: function(player, options) {
constructor: function() {
Button.apply(this, arguments);
//this.addClass('vjs-chapters-button');
this.addClass('icon-angle-left');
this.controlText("Previous");
},
handleClick: function() {
console.log('click');
player.playlist.previous();
}
});
/* ADD BUTTON */
//var Button = videojs.getComponent('Button');
// Extend default
var NextButton = videojs.extend(Button, {
//constructor: function(player, options) {
constructor: function() {
Button.apply(this, arguments);
//this.addClass('vjs-chapters-button');
this.addClass('icon-angle-right');
this.controlText("Next");
},
handleClick: function() {
console.log('click');
player.playlist.next();
}
});
videojs.registerComponent('NextButton', NextButton);
videojs.registerComponent('PrevButton', PrevButton);
player.getChild('controlBar').addChild('PrevButton', {}, 0);
player.getChild('controlBar').addChild('NextButton', {}, 2);
document.getElementById("current").innerHTML = "Von " + videocounter;
Check the API documentation:
player.curtentItem() returns the index of the current item. You'll probably want to increase this by one for display, as it is 0-based. The playlistitem event is triggered when the item has changed.

Refactoring repeated code in javascript prototype constructor

I have a open function that once triggered, simply plays video in a dedicated panel.
This function can be triggered in two ways - one with a click and another one with a page load (window load) with url that contains a valid anchor tag.
They all work fine but some codes of the window load handler are repetitive and I'm not too sure how I can keep this DRY.
Please take a look and point me in some directions on how I can write this better.
I commented in open function which is for which.
$.videoWatch.prototype = {
init: function() {
this.$openLinks = this.$element.find(".open");
this.$closeLinks = this.$element.find(".close");
this.open();
this.close();
},
_getContent: function(element) {
var $parent = element.parent(),
id = element.attr('href').substring(1),
title = $parent.data('title'),
desc = $parent.data('desc');
return {
title: title,
desc: desc,
id: id
}
},
open: function() {
var self = this;
//open theatre with window load with #hash id
window.onload = function() {
var hash = location.hash;
var $a = $('a[href="' + hash + '"]'),
content = self._getContent($a),
$li = $a.parents("li"),
$theatreVideo = $(".playing"),
$theatreTitle = $(".theatre-title"),
$theatreText = $(".theatre-text");
$(".theatre").attr('id', content.id);
$theatreTitle.text(content.title);
$theatreText.text(content.desc);
if ($theatreText.text().length >= 90) {
$(".theatre-text").css({
'overflow': 'hidden',
'max-height': '90px',
});
$moreButton.insertAfter($theatreText);
}
$a.parent().addClass("active");
$(".theatre").insertAfter($li);
$(".theatre").slideDown('fast', scrollToTheatre);
oldIndex = $li.index();
}
//open theatre with click event
self.$openLinks.on("click", function(e) {
// e.preventDefault();
if (curID == $(this).parent().attr("id")) {
$("figure").removeClass("active");
$("button.more").remove();
$(".theatre").slideUp('fast');
$('.playing').attr("src", "");
removeHash();
oldIndex = -1;
curID = "";
return false
} else {
curID = $(this).parent().attr("id");
}
var $a = $(this),
content = self._getContent($a),
$li = $a.parents("li"),
$theatreVideo = $(".playing"),
$theatreTitle = $(".theatre-title"),
$theatreText = $(".theatre-text");
$(".theatre").attr('id', content.id);
$theatreTitle.text(content.title);
$theatreText.text(content.desc);
if ($theatreText.text().length >= 90) {
$(".theatre-text").css({
'overflow': 'hidden',
'max-height': '90px',
});
$moreButton.insertAfter($theatreText);
}
if (!($li.index() == oldIndex)) {
$("figure").removeClass("active");
$(".theatre").hide(function(){
$a.parent().addClass("active");
$(".theatre").insertAfter($li);
$(".theatre").slideDown('fast', scrollToTheatre);
oldIndex = $li.index();
});
} else {
$(".theatre").insertAfter($li);
scrollToTheatre();
$("figure").removeClass("active");
$a.parent().addClass("active");
}
});
},
...
Simplified and refactored open method:
open: function() {
var self = this;
var serviceObj = {
theatreVideo : $(".playing"),
theatre: $(".theatre"),
theatreTitle : $(".theatre-title"),
theatreText : $(".theatre-text"),
setTheatreContent: function(content){
this.theatre.attr('id', content.id);
this.theatreTitle.text(content.title);
this.theatreText.text(content.desc);
if (this.theatreText.text().length >= 90) {
this.theatreText.css({
'overflow': 'hidden',
'max-height': '90px',
});
$moreButton.insertAfter(this.theatreText);
}
},
activateTeatre: function(a, li){
a.parent().addClass("active");
this.theatre.insertAfter(li);
this.theatre.slideDown('fast', scrollToTheatre);
oldIndex = li.index();
}
};
//open theatre with window load with #hash id
window.onload = function() {
var hash = location.hash;
var $a = $('a[href="' + hash + '"]'),
content = self._getContent($a),
$li = $a.parents("li");
serviceObj.setTheatreContent(content);
serviceObj.activateTeatre($a, $li);
}
//open theatre with click event
self.$openLinks.on("click", function(e) {
// e.preventDefault();
if (curID == $(this).parent().attr("id")) {
$("figure").removeClass("active");
$("button.more").remove();
$(".theatre").slideUp('fast');
$('.playing').attr("src", "");
removeHash();
oldIndex = -1;
curID = "";
return false
} else {
curID = $(this).parent().attr("id");
}
var $a = $(this),
content = self._getContent($a),
$li = $a.parents("li");
serviceObj.setTheatreContent(content);
if (!($li.index() == oldIndex)) {
$("figure").removeClass("active");
$(".theatre").hide(function(){
serviceObj.activateTeatre($a, $li);
});
} else {
$(".theatre").insertAfter($li);
scrollToTheatre();
$("figure").removeClass("active");
$a.parent().addClass("active");
}
});
},
1st of all there are variables that don't depend on the input, you could pull them to the class (I'll show just one example, as you asked for directions):
init: function() {
this.$theatreVideo = $(".playing");
All the variables that do depend on the input, like $li could be moved to a function:
var $a = $(this),
$dependsOnA = self.dependsOnA($a);
self.actionDependsOnA($dependsOnA); // see below
function dependsOnA($a) {
return {
a: $a,
li: $a.parents("li"),
content: self._getContent($a)
}
}
Also the code that "repeats" can be moved to a function:
function actionDependsOnA($dependsOnA)
$(".theatre").attr('id', $dependsOnA.content.id);
$theatreTitle.text($dependsOnA.content.title);
$theatreText.text($dependsOnA.content.desc);
}

Attaching multiple events to single function in jQuery

I'm looking for the opposite of that everyone's been looking for. I have this anonymous jQuery function that I want to keep that way, but I want to attach multiple events handlers to it on different events (two events exactly).
When the textarea has text inside it changed (keyup)
When document is clicke (click).
I know that grabbing the callback function and putting it in a function declaration, then using the function on both cases would do the job, but is there something around that wont require me to put the callback function in a normal function and just leave it as is?
This is the code I currently have:
urls.keyup(function(){
delay('remote', function(){
if (urls.val().length >= char_start)
{
var has_lbrs = /\r|\n/i.test(urls.val());
if (has_lbrs)
{
urls_array = urls.val().split("\n");
for (var i = 0; i < urls_array.length; i++)
{
if (!validate_url(urls_array[i]))
{
urls_array.splice(i, 1);
continue;
}
}
}
else
{
if (!validate_url(urls.val()))
{
display_alert('You might have inserted an invalid URL.', 'danger', 3000, 'top');
return;
}
}
final_send = (has_lbrs && urls_array.length > 0) ? urls_array : urls.val();
if (!final_send)
{
display_alert('There went something wrong while uploading your images.', 'danger', 3000, 'top');
return;
}
var template = '<input type="text" class="remote-progress" value="0" />';
$('.pre-upload').text('').append(template);
$('.remote-progress').val(0).trigger('change').delay(2000);
$('.remote-progress').knob({
'min':0,
'max': 100,
'readOnly': true,
'width': 128,
'height': 128,
'fgColor': '#ad3b3b',
'bgColor': '#e2e2e2',
'displayInput': false,
'cursor': true,
'dynamicDraw': true,
'thickness': 0.3,
'tickColorizeValues': true,
});
var tmr = self.setInterval(function(){myDelay()}, 50);
var m = 0;
function myDelay(){
m++;
$('.remote-progress').val(m).trigger('change');
if (m == 100)
{
// window.clearInterval(tmr);
m = 0;
}
}
$.ajax({
type: 'POST',
url: 'upload.php',
data: {
upload_type: 'remote',
urls: JSON.stringify(final_send),
thumbnail_width: $('.options input[checked]').val(),
resize_width: $('.options .resize_width').val(),
album_id: $('#album_id').val(),
usid: $('#usid').val(),
},
success: function(data) {
// console.log(data); return;
var response = $.parseJSON(data);
if (response)
{
$('.middle').hide();
$('.remote-area').hide();
window.clearInterval(tmr);
}
if ('error' in response)
{
//console.log(response.error);
if (!$('.top-alert').is(':visible'))
{
display_alert(response.error, 'danger', 3000, 'top');
}
return;
}
if (!target.is(':visible'))
{
target.show().addClass('inner');
}
else
{
target.addClass('inner');
}
for (var key in response)
{
var image_url = response[key];
var thumb_uri = image_url.replace('/i/', '/t/');
var forum_uri = '[img]' + image_url + '[/img]';
var html_uri = '<a href="' + image_url + '">' + image_url + '</a>';
var view_url = image_url.replace(/store\/i\/([A-Za-z0-9]+)(?=\.)/i, "image/$1");
view_url = strstr(view_url, '.', true);
// Append the upload box
target.append(upload_box(key));
// Hide knob
$('.knobber').hide();
// Put the image box
putImage($('.' + key), view_url, image_url, thumb_uri, forum_uri, html_uri);
}
},
});
}
}, 2000); // Delay
}); // Remote upload
How do I make this code run on document click as well?
Thank you.
As you yourself has said in you question, the answer is to create an external named reference to the callback function and use it as the callback.
Like
jQuery(function () {
function callback(e) {
console.log('event2', e.type);
}
$('input').keyup(callback);
$(document).click(callback)
})
But since you have asked for a different style have a look at, it essentially does the same as the above one
jQuery(function () {
var callback;
$('input').keyup(callback = function (e) {
console.log('event', e.type);
});
$(document).click(callback)
})
Demo: Fiddle

Filter DOM elements via JavaScript

I have elements that get created like this:
function appendCalendarEventToList(className, event) {
var eventObject = {
title: event.title,
id: event.id,
type: event.type,
color: event.color,
description: event.description,
canReoccur: event.canReoccur,
reoccurVal: event.reoccurVal,
estHours: event.estHours,
project: event.project
};
var div = document.createElement("div");
div.className = 'external-event';
div.style.background = event.color;
div.innerHTML = event.title;
// store the Event Object in the DOM element so we can get to it later
$(div).data('eventObject', eventObject);
$(div).draggable({
helper: function () {
$copy = $(this).clone();
$copy.data('eventObject', eventObject);
$copy.css({ "list-style": "none", "width": $(this).outerWidth() }); return $copy;
},
appendTo: 'body',
zIndex: 999,
revert: true, // will cause the event to go back to its
revertDuration: 0 // original position after the drag
});
$(className).append(div);
$(div).qtip({
content: event.title,
position:
{
target: 'mouse'
},
// show: { event: 'click' },
hide: { event: 'mousedown mouseleave' },
style: {
classes: 'custSideTip',
width: 200,
padding: 5,
color: 'black',
textAlign: 'left',
border: {
width: 1,
radius: 3
}
}
});
return div;
}
Every event has a project attribute as you see.
However there are projects and tasks (and cases that can be ignored):
function refetchUnscheduled() {
$.ajax({
type: "POST",
url: "CalendarServices.aspx/GetUnscheduled",
data: '',
async:false,
success: function (data) {
var projects = '.project-container';
var tasks = '.task-container';
var cases = '.case-container';
$(projects).html('');
$(tasks).html('');
$(cases).html('');
var numProjects = 0;
var numTasks = 0;
var numCases = 0;
$.each(data, function () {
var className = '';
var url = '';
var typeName = '';
var crm = this.crm;
switch(this.type)
{
case 'p':
className = projects;
url = 'ProjectViewFrame.aspx?id=' + this.id;
typeName = 'Project';
numProjects++;
break;
case 't':
className = tasks;
url = 'TaskViewFrame.aspx?id=' + this.id;
typeName = 'Task';
numTasks++;
break;
case 'c':
className = cases;
url = 'CaseViewFrame.aspx?id=' + this.id;
typeName = 'Case';
numCases++;
break;
default:
}
var curDiv = appendCalendarEventToList(className, this);
var curEvent = this;
$(curDiv).bind('contextmenu', function (e) {
$('#contextMenuContainer').html('');
var btn1 = createLink('Edit ' + typeName, 'context-elem-name');
$('#contextMenuContainer').append(btn1);
$(btn1).on('click', function () {
if (crm == '')
showCalendarModal('Edit ' + typeName, url);
else
showCRM(crm);
})
prepContextMenu(e);
return false;
});
});
changeUnscheduledHeaders();
}
,
error: function () {
}
});
}
Here is what I need that I am unsure how to do:
I need an OR based filter:
Given the following:
function filter(criteria,projects-div,tasks-div)
{
var words = criteria.split(' ');
}
I need to first, hide all projects whos
obj.data('eventObject').title contains one or more words.
Then, once these are filtered:
I need to first show all tasks whos project is visible,
So:
obj.data('eventObject').project is == to one of the visible project's project property.
Then, I need to also show any tasks that have any of the words.
All comparing must be case insensitive too.
So say my criteria is 'hello world'
I show all projects with hello or world in the title.
I show all tasks whos project attribute is visible after the first step
I show all tasks whos title has hello or world
Thanks
I'm on mobile, but at first glance, what about [].filter.call (NodeList, filterFn)?

jQuery plugin data mixup with multiple instances

I'm trying to develop a jQuery plugin to display adaptive image galleries. I'm using .data() to store the information for each slide of the gallery.
Everything appears to be working okay when displaying a single gallery, but gets mixed up when trying to have multiple gallery instances on a single page. A rough demo can be viewed here.
Here is the code that is being run:
(function($) {
$.Gallery = function(element, options) {
this.$el = $(element);
this._init(options);
};
$.Gallery.defaults = {
showCarousel : true,
};
$.Gallery.prototype = {
_init : function(options) {
var $this = $(this);
var instance = this;
// Get settings from stored instance
var settings = $this.data('settings');
var slides = $this.data('slides');
// Create or update the settings of the current gallery instance
if (typeof(settings) == 'undefined') {
settings = $.extend(true, {}, $.Gallery.defaults, options);
}
else {
settings = $.extend(true, {}, settings, options);
}
$this.data('settings', settings);
if (typeof(slides) === 'undefined') {
slides = [];
}
$this.data('slides', slides);
instance.updateCarousel();
},
addItems : function(newItems) {
var $this = $(this),
slides = $this.data('slides');
for (var i=0; i<newItems.length; i++) {
slides.push(newItems[i]);
};
},
updateCarousel : function() {
var instance = this,
$this = $(this);
slides = $(instance).data('slides');
var gallery = instance.$el.attr('id');
/* Create carousel if it doesn't exist */
if (instance.$el.children('.carousel').length == 0) {
$('<div class="carousel"><ul></ul></div>').appendTo(instance.$el);
var image = instance.$el.find('img');
};
var carouselList = instance.$el.find('.carousel > ul'),
slideCount = slides.length,
displayCount = carouselList.find('li').length;
if (displayCount < slideCount) {
for (var i=displayCount; i<slides.length; i++) {
slides[i].id = gallery + '-slide-' + i;
slide = $('<img>').attr({src : slides[i].thumb}).appendTo(carouselList).wrap(function() {
return '<li id="' + slides[i].id + '" />'
});
slide.on({
click : function() {
instance.slideTo($(this).parent().attr('id'));
},
});
};
};
},
slideTo : function(slideID) {
var $this = $(this);
var slides = $this.data('slides');
var image;
var $instance = $(this.$el);
$(slides).each( function() {
if (this.id == slideID){
image = this.img;
};
});
$('<img/>').load( function() {
$instance.find('div.image').empty().append('<img src="' + image + '"/>');
}).attr( 'src', image );
},
};
$.fn.gallery = function(options) {
args = Array.prototype.slice.call(arguments, 1);
this.each(function() {
var instance = $(this).data('gallery');
if (typeof(options) === 'string') {
if (!instance) {
console.error('Methods cannot be called until jquery.responsiveGallery has been initialized.');
}
else if (!$.isFunction(instance[options])) {
console.error('The method "' + options + '" does not exist in jquery.responsiveGallery.');
}
else {
instance[options].apply(instance, args);
}
}
else {
if (!instance) {
$(this).data('gallery', new $.Gallery(this, options));
}
}
});
return this;
};
})(jQuery);
$(window).load(function() {
$('#gallery2').gallery();
$('#gallery3').gallery();
$('#gallery2').gallery('addItems', [
{
img: 'images/image2.jpg',
thumb: 'images/image2_thumb.jpg',
desc: 'Image of number 2'
},
{
img: 'images/image3.jpg',
thumb: 'images/image3_thumb.jpg',
desc: 'Image of number 3'
},
{
img: 'images/image4.jpg',
thumb: 'images/image4_thumb.jpg',
desc: 'Image of number 4'
},
{
img: 'images/image5.jpg',
thumb: 'images/image5_thumb.jpg',
desc: 'Image of number 5'
}
]);
$('.pGallery').gallery('addItems', [
{
img: 'images/2.jpg',
thumb: 'images/2_thumb.jpg',
desc: 'Image of number 0'
},
{
img: 'images/image1.jpg',
thumb: 'images/image1_thumb.jpg',
desc: 'The second image'
}
]);
$('.pGallery').gallery('updateCarousel');
});
On the surface it appears to create two galleries with the proper slide structure of:
gallery2
gallery2-slide-0
gallery2-slide-1
gallery2-slide-2
gallery2-slide-3
gallery2-slide-4
gallery2-slide-5
gallery3
gallery3-slide-0
gallery3-slide-1
However, the onclick action doesn't work for the last two slides of Gallery2 (the slides duplicated in both galleries). Upon closer inspection of the DOM, I can see that the slides stored in data for gallery2, have the following ids:
gallery2
gallery2-slide-0
gallery2-slide-1
gallery2-slide-2
gallery2-slide-3
gallery3-slide-0
gallery3-slide-1
I'm not sure what is causing the mix-up or how to fix, so any help would be greatly appreciated.
Thanks
Your problem is that calling $('.pGallery').gallery('addItems'... cause the same objects to be added to the internal structure of both galleries, when you call $('.pGallery').gallery('updateCarousel'); the ids stored in those objects are overwritten.
You need to put a copy of the original objects in there. Do:
addItems : function(newItems) {
var $this = $(this),
slides = $this.data('slides');
for (var i=0; i<newItems.length; i++) {
//slides.push(newItems[i]);
slides.push(jQuery.extend({}, newItems[i]));
};
},
With jQuery.extend({}, oldObject) you can perform a shallow copy;

Categories