Issue using angularjs/ngDialog modal with Bitmovin player - javascript

I am trying to use an angularjs/ngDialog modal to display a Bitmovin video player dialog. The sample code below loads and plays the video fine. The issue comes when I close the dialog using either the close button or clicking the background area and then try to reopen the dialog/player. The player doesn't load and play. Ideally I would like the player to pickup where it left off. When I use JWPlayer with the same angularjs/ngDialog code the video plays the 2nd, 3rd, ... times around.
I'm new to angularjs. Any help is greatly appreciated.
<!DOCTYPE html>
<html lang="en">
<head>
<title>** Bitmovin AngularJS Modal Player</title>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel='stylesheet' href='css/ngDialog.css' type='text/css' media='all' />
<link rel='stylesheet' href='css/ngDialog-theme-default.css' type='text/css' media='all' />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js?ver=1.5.8'></script>
<script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/ng-dialog/0.6.4/js/ngDialog.min.js?ver=0.6.4'></script>
<script type="text/javascript" src="https://bitmovin-a.akamaihd.net/bitmovin-player/stable/6.1.7/bitmovinplayer.min.js"></script>
</head>
<body>
<div class="container">
<div class="content">
<div ng-app="myApp" ng-controller="myModalController">
<button ng-click="showVideoPlayerPopup()" class="btmv-button">WATCH</button>
</div>
</div>
</div>
<script type="text/javascript">
function playVideo() {
//var createPlayer = function () {
var config = {
key: "0b3b8038-7b11-4aa0-8717-1f848c76e436",
source: {
dash: 'https://bitmovin-a.akamaihd.net/content/MI201109210084_1/mpds/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd'
},
style: {
width: '50vw',
aspectratio: '16/9'
},
playback : {
autoplay : true,
muted : false
},
events: {
onReady: function(data) {
console.log('Video duration: ' + this.getDuration() + "s" );
console.log(data);
},
onPlay : function(data) {
console.log(data);
},
onPause : function(data) {
console.log(data);
},
onSeek : function(data) {
//updateTime();
console.log(data);
},
onPlaybackFinished: function(data) {
console.log('Video onPlaybackFinished: ' + this.getCurrentTime() + "s", data );
player.seek(0);
console.log('Video onPlaybackFinished seek(0) time: ' + this.getCurrentTime() + "s" );
// player.destroy();
// console.log('TGC Video onPlaybackFinished player destroyed! ');
},
onError : function(data) {
console.error("Bitmovin Player error: " + data.code + ": " + data.message);
}
} // end events
};
var player = bitmovin.player('btmv-player');
player.setup(config).then(function(value) {
console.log('Successfully created Bitmovin Player instance');
}, function(reason) {
console.log('Error while creating Bitmovin Player instance: ${error.message}');
});
function updateTime(time) {
document.getElementById("event").innerHTML ="The video has been seeked to "+JSON.stringify(player.getCurrentTime())+"s";
}
player.addEventHandler('onSeeked', function(timestamp) {
updateTime( JSON.stringify( player.getCurrentTime() ) );
});
};
function playbackFinished() {
console.log('playbackFinished fired! ');
}
function killPlayer() {
if ( confirm('Close the player?') ) {
player.destroy();
return true;
}
return false;
}
var app = angular.module('myApp',['ngDialog']);
app.controller('myModalController', function($scope, ngDialog) {
$scope.ngDialog = ngDialog;
$scope.showVideoPlayerPopup = function(video_path) {
ngDialog.open({
//animation : true,
disableAnimation: true,
scope : $scope,
template: '<div id="btmv-player"></div>' +
'<br />' +
'<div id="event"></div>',
plain : true,
className : 'ngdialog-theme-default',
closeByDocument: true,
width : 670,
height : 390,
preCloseCallback: function(value) {
//killPlayer();
return true;
}
});
$scope.$on('ngDialog.opened', function (e, $dialog) {
playVideo();
//createPlayer();
console.log('ngDialog opened: ' + $dialog.attr('id'));
});
$scope.$on('ngDialog.closing', function (e, $dialog) {
//killPlayer();
console.log('ngDialog closing: ' + $dialog.attr('id'));
});
$scope.$on('ngDialog.closed', function (e, $dialog) {
//killPlayer();
console.log('ngDialog closed: ' + $dialog.attr('id'));
});
}
});
</script>
</body>
</html>

It seems that the problem is that the player <div> does not exist any more when calling bitmovin.player("btmv-player") in killPlayer.
The following code should work. It uses the short cut bitmovin.player() instead of accessing via ID, which returns the last created player.
<html lang="en">
<head>
<title>** Bitmovin AngularJS Modal Player</title>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/ng-dialog/0.6.4/css/ngDialog.css' type='text/css' media='all' />
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/ng-dialog/0.6.4/css/ngDialog-theme-default.css' type='text/css' media='all' />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js?ver=1.5.8'></script>
<script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/ng-dialog/0.6.4/js/ngDialog.min.js?ver=0.6.4'></script>
<script type="text/javascript" src="https://bitmovin-a.akamaihd.net/bitmovin-player/stable/7/bitmovinplayer.js"></script>
</head>
<body>
<div class="container">
<div class="content">
<div ng-app="myApp" ng-controller="myModalController">
<button ng-click="showVideoPlayerPopup()" class="btmv-button">WATCH</button>
</div>
</div>
</div>
<script type="text/javascript">
function playVideo() {
var config = {
key: "YOUR-PLAYER-KEY",
source: {
dash: 'https://bitmovin-a.akamaihd.net/content/MI201109210084_1/mpds/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd'
},
style: {
width: '50vw',
aspectratio: '16/9'
},
playback : {
autoplay : true,
muted : false
},
events: {
onReady: function(data) {
console.log('Video duration: ' + this.getDuration() + "s" );
console.log(data);
},
onPlay: function(data) {
console.log(data);
},
onPaused: function(data) {
console.log(data);
},
onSeek : function(data) {
console.log(data);
},
onPlaybackFinished: function(data) {
console.log('Video onPlaybackFinished: ' + this.getCurrentTime() + "s", data );
player.seek(0);
console.log('Video onPlaybackFinished seek(0) time: ' + this.getCurrentTime() + "s" );
},
onError : function(data) {
console.error("Bitmovin Player error: " + data.code + ": " + data.message);
}
} // end events
};
var player = bitmovin.player('btmv-player');
player.setup(config).then(function(value) {
console.log('Successfully created Bitmovin Player instance');
}, function(reason) {
console.log('Error while creating Bitmovin Player instance: ${error.message}');
});
function updateTime(time) {
document.getElementById("event").innerHTML ="The video has been seeked to "+JSON.stringify(player.getCurrentTime())+"s";
}
player.addEventHandler('onSeeked', function(timestamp) {
updateTime( JSON.stringify( player.getCurrentTime() ) );
});
};
function playbackFinished() {
console.log('playbackFinished fired! ');
}
function killPlayer() {
if ( confirm('Close the player?') ) {
var player = bitmovin.player();
if (player) {
player.destroy();
}
return true;
}
return false;
}
var app = angular.module('myApp',['ngDialog']);
app.controller('myModalController', function($scope, ngDialog) {
$scope.ngDialog = ngDialog;
$scope.showVideoPlayerPopup = function(video_path) {
ngDialog.open({
disableAnimation: true,
scope : $scope,
template: '<div id="btmv-player"></div>' +
'<br />' +
'<div id="event"></div>',
plain : true,
className : 'ngdialog-theme-default',
closeByDocument: true,
width : 670,
height : 390
});
}
$scope.$on('ngDialog.opened', function (e, $dialog) {
playVideo();
console.log('ngDialog opened: ' + $dialog.attr('id'));
});
$scope.$on('ngDialog.closed', function (e, $dialog) {
killPlayer();
console.log('ngDialog closed: ' + $dialog.attr('id'));
});
});
</script>
</body>
</html>
Furthermore I suggest not mixing AngularJS and plain JavaScript as weird things can happen, especially if the project grows. You should consider to add all the JavaScript code into the app / controllers.
There's also a Github project to use the Bitmovin Player in Angular (maintained by MovingImage24 and not by Bitmovin): https://github.com/MovingImage24/mi-angular-bitdash-player
I'm not sure how up to date it is kept, though.

Related

Filter by value in checkbox popup in Mapbox GL

I have changed my mind about how I want to use the Checkbox filter in Mapbox. I am using CSV2Geojson to map Google Sheets data in my map and then filter by values in one column. I know there is this https://labs.mapbox.com/education/impact-tools/finder-with-filters/ but I want to stick to my layout instead of using a config file to define options. My original spreadsheet had a function that looked at column headers and only returned cells with "Y" in them when checked. Now, I have 1 column where I need to look for 4 specific values. Below is the code and codepen for debugging. The code in question is at the bottom of the code block.
https://codepen.io/bearcats6001/project/editor/AjrPPP
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>CodePen - project map 2</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.7.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.7.0/mapbox-gl.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.0/jquery.min.js"></script>
<script src='https://npmcdn.com/csv2geojson#latest/csv2geojson.js'></script>
<script src='https://npmcdn.com/#turf/turf/turf.min.js'></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.7.2/mapbox-gl-geocoder.min.js"></script>
<link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.7.2/mapbox-gl-geocoder.css" type="text/css">-->
<div id="map">
<div id="filterMenu">
<h3>Programs</h3>
<input type="checkbox" id="Nathan" name="srBox" value="SustainableRecreation">
<label for="Nathan"> Nathan Shock Centers of Excellence in the Basic Biology of Aging</label><br>
<input type="checkbox" id="Pepper" name="hlBox" value="HealthyLandscapes">
<label for="Pepper"> Claude D. Pepper Older Americans Independence Centers</label><br>
<input type="checkbox" id="Aging" name="ovBox" value="Overnight">
<label for="Aging"> Centers on the Demography and Economics of Aging</label><br>
<input type="checkbox" id="Minority" name="ovBox" value="Overnight">
<label for="Minority"> Resource Centers for Minority Aging Research</label><br>
</div>
</div>
<!-- partial -->
<script src="./script.js"></script>
</body>
</html>
var transformRequest = (url, resourceType) => {
var isMapboxRequest =
url.slice(8, 22) === "api.mapbox.com" ||
url.slice(10, 26) === "tiles.mapbox.com";
return {
url: isMapboxRequest
? url.replace("?", "?pluginName=sheetMapper&")
: url,
};
};
mapboxgl.accessToken = 'pk.eyJ1IjoiYWZhcm9yZyIsImEiOiJjbDIwajV3YTUwMGc3M2xwNDdiYWJiMjUzIn0.Pjt9AndPk1Axv99wez-5TA';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v10',
center: [-102.182698, 37.131563],
zoom: 3,
//left, bottom, right, top
});
var nav = new mapboxgl.NavigationControl({
showCompass: true,
showZoom: true,
visualizePitch: true
});
map.addControl(nav, "bottom-right");
$(document).ready(function () {
$.ajax({
type: "GET",
url: 'https://docs.google.com/spreadsheets/d/1umfhXq5WEPLEABV81-tZUayAw7WZrmqe/gviz/tq?tqx=out:csv&sheet=Sheet1',
dataType: "text",
success: function (csvData) { makeGeoJSON(csvData); }
});
function makeGeoJSON(csvData) {
csv2geojson.csv2geojson(csvData, {
latfield: 'Latitude',
lonfield: 'Longitude',
delimiter: ','
}, function (err, data) {
map.on('load', function () {
map.addLayer({
'id': 'rfovProjects',
'type': 'circle',
'source': {
'type': 'geojson',
'data': data
},
'paint': {
'circle-radius': 7,
'circle-color': "blue",
'circle-opacity': 0.5,
}
});
});
});
};
});
map.on('mouseenter', 'rfovProjects', function () {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'rfovProjects', function () {
map.getCanvas().style.cursor = '';
});
map.on('click', function(e) {
if(!e.originalEvent.defaultPrevented) {
e.originalEvent.preventDefault();
}
var features = map.queryRenderedFeatures(e.point, {
layers: ['rfovProjects']
});
if (!features.length) {
return;
}
var feature = features[0];
var popupContent = '<h3 style="display:inline">' + feature.properties.Name + '</h3><br><p style="display:inline"><b>'
/*popupContent += feature.properties.time ? '</b>, ' + feature.properties.time : '</b>'*/
popupContent += '<br> Website Link </p>'
var popup = new mapboxgl.Popup({ offset: [0, 0] })
.setLngLat(e.lngLat)
.setHTML(popupContent)
.addTo(map);
});
//basemap toggles
/*
var tRadio = document.getElementById('topoRadio');
tRadio.addEventListener('change', function() {
if (this.checked) {
map.setLayoutProperty('mapbox-satellite', 'visibility', 'none');
}
});
var iRadio = document.getElementById('imageryRadio');
iRadio.addEventListener('change', function() {
if (this.checked) {
map.setLayoutProperty('mapbox-satellite', 'visibility', 'visible');
}
});*/
//
map.doubleClickZoom.enable();
map.on('dblclick', function(e) {
map.getZoom() +10;
});
map.addControl(
new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
mapboxgl: mapboxgl,
flyTo: {
zoom: 12,
easing: function(t) {
return t;
}
},
marker: false
})
);
map.addControl(new mapboxgl.NavigationControl());
var filters = {};
function updateFilters() {
var compositeFilter = ['all'];
for (let filterValue in filters) {
if (filters[filterValue]) {
compositeFilter.push(['==', ['get', filterValue], 'Y']);
}
}
if (compositeFilter.length > 1)
map.setFilter('rfovProjects', compositeFilter);
else {
map.setFilter('rfovProjects', null);
}
}
var checkbox = document.getElementById('Nathan');
checkbox.addEventListener('change', function() {
filters['Program'] = this.checked;
updateFilters();
});
var checkbox = document.getElementById('Pepper');
checkbox.addEventListener('change', function() {
filters['Program'] = this.checked;
updateFilters();
});
var checkbox = document.getElementById('Aging');
checkbox.addEventListener('change', function() {
filters['Program'] = this.checked;
updateFilters();
});
var checkbox = document.getElementById('Minority');
checkbox.addEventListener('change', function() {
filters['Program'] = this.checked;
updateFilters();
});

Load inserted text in dialog

I am using summernote 0.8 and jquery 3.5.
I have created a dialog that inputs synonyms for example, when inputing test1, test2, test3 in the dialog a special tag is filled into the editor like the following:
<span data-function="addSynonym" data-options="[test2, test3]"><span style="background-color: yellow;">test1</span></span>
I would like to load the dialog with these values, edit them and add the updated values to the editor's text field.
Find below my minimum viable example:
$(document).ready(function() {
$('.summernote').summernote({
height: 300,
tabsize: 2,
toolbar: [
['insert', ['synonym', 'codeview']]
],
});
});
(function(factory) {
/* global define */
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node/CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory(window.jQuery);
}
}(function($) {
$.extend($.summernote.plugins, {
'synonym': function(context) {
var self = this;
var ui = $.summernote.ui;
var $editor = context.layoutInfo.editor;
var options = context.options;
context.memo('button.synonym', function() {
return ui.button({
contents: '<i class="fa fa-snowflake-o">',
tooltip: 'Create Synonym',
click: context.createInvokeHandler('synonym.showDialog')
}).render();
});
self.initialize = function() {
var $container = options.dialogsInBody ? $(document.body) : $editor;
var body = '<div class="form-group">' +
'<label>Add Synonyms (comma - , - seperated</label>' +
'<input id="input-synonym" class="form-control" type="text" placeholder="Insert your synonym" />'
'</div>'
var footer = '<button href="#" class="btn btn-primary ext-synonym-btn">OK</button>';
self.$dialog = ui.dialog({
title: 'Create Synonym',
fade: options.dialogsFade,
body: body,
footer: footer
}).render().appendTo($container);
};
// You should remove elements on `initialize`.
self.destroy = function() {
self.$dialog.remove();
self.$dialog = null;
};
self.showDialog = function() {
self
.openDialog()
.then(function(data) {
ui.hideDialog(self.$dialog);
context.invoke('editor.restoreRange');
self.insertToEditor(data);
console.log("dialog returned: ", data)
})
.fail(function() {
context.invoke('editor.restoreRange');
});
};
self.openDialog = function() {
return $.Deferred(function(deferred) {
var $dialogBtn = self.$dialog.find('.ext-synonym-btn');
var $synonymInput = self.$dialog.find('#input-synonym')[0];
ui.onDialogShown(self.$dialog, function() {
context.triggerEvent('dialog.shown');
$dialogBtn
.click(function(event) {
event.preventDefault();
deferred.resolve({
synonym: $synonymInput.value
});
});
});
ui.onDialogHidden(self.$dialog, function() {
$dialogBtn.off('click');
if (deferred.state() === 'pending') {
deferred.reject();
}
});
ui.showDialog(self.$dialog);
});
};
this.insertToEditor = function(data) {
console.log("synonym: " + data.synonym)
var dataArr = data.synonym.split(',');
var restArr = dataArr.slice(1);
var $elem = $('<span>', {
'data-function': "addSynonym",
'data-options': '[' + restArr.join(',').trim() + ']',
'html': $('<span>', {
'text': dataArr[0],
'css': {
backgroundColor: 'yellow'
}
})
});
context.invoke('editor.insertNode', $elem[0]);
};
}
});
}));
<head>
<meta charset="UTF-8">
<title>Summernote with Bootstrap 4</title>
<script src="https://code.jquery.com/jquery-3.4.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/summernote#0.8.15/dist/summernote-bs4.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/summernote#0.8.15/dist/summernote-bs4.min.js"></script>
</head>
<body style="
padding-top: 50px;
border-left-width: 50px;
padding-left: 50px;
border-right-width: 50px;
padding-right: 150px;
">
<div class="container">
<div class="summernote">
<p>Hello World!</p>
This text should be replaced by the dialog. </div>
</div>
</body>
Any suggestions how to do add this update functionality to my yellow text?
I appreciate your replies!
Using the oninit callback, we can easily use jquery methods to select that embedded text and trigger a click on that button you added in your plugin.
It's the first time I use Summernote. So to bring a clear code and a similar syntax in the [UPDATE], I added jquery-ui dialogBox that would be used to update the clicked span.
And for this I used updateSpan() function that receives the (targeted) current span object and it's new value as arguments.
var i=0;
function updateSpan(object,value){
object.text(value.split(',', 1));
object.attr('data-options',value.split(',', 1));
object.attr('data-all','['+value+']');
object.css('backgroundColor','yellow');
object.parent().append(" ");
}
$(document).ready(function() {
$('.summernote').summernote({
height: 300,
tabsize: 2,
toolbar: [
['insert', ['synonym', 'codeview']]
],
callbacks: {
onInit: function() {
$(".note-editable").on('click','span[data-function="addSynonym"]', function (e) {
var spanvalue=($(this).attr('data-all')).replace(/[\[\]']+/g,'');
var targetSpan=$(this);
//console.log(spanvalue);
$('#upDialog').dialog({
open : function (event, ui) {
$('#upDialog #input-synonym').empty().val(spanvalue);
//console.log(spanvalue);
},
modal: true,
title: 'Dialog',
show: {
effect: "scale",
duration: 200
},
resizable: false,
buttons: [{
text: "ok",
click: function () {
updateSpan(targetSpan,$('#upDialog #input-synonym').val());
$(this).dialog("close");
targetSpan.focus();
}
}]
});
});
}
}
});
});
(function(factory) {
/* global define */
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node/CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory(window.jQuery);
}
}(function($) {
$.extend($.summernote.plugins, {
'synonym': function(context) {
var self = this;
var ui = $.summernote.ui;
var $editor = context.layoutInfo.editor;
var options = context.options;
context.memo('button.synonym', function() {
return ui.button({
contents: '<i class="fa fa-snowflake-o">',
tooltip: 'Create Synonym',
click: context.createInvokeHandler('synonym.showDialog')
}).render();
});
self.initialize = function() {
var $container = options.dialogsInBody ? $(document.body) : $editor;
var body = '<div class="form-group">' +
'<label>Add Synonyms (comma - , - seperated</label>' +
'<input id="input-synonym" class="form-control" type="text" placeholder="Insert your synonym" />'
'</div>'
var footer = '<button href="#" class="btn btn-primary ext-synonym-btn">OK</button>';
self.$dialog = ui.dialog({
title: 'Create Synonym',
fade: options.dialogsFade,
body: body,
footer: footer
}).render().appendTo($container);
};
// You should remove elements on `initialize`.
self.destroy = function() {
self.$dialog.remove();
self.$dialog = null;
};
self.showDialog = function() {
self
.openDialog()
.then(function(data) {
ui.hideDialog(self.$dialog);
context.invoke('editor.restoreRange');
self.insertToEditor(data);
//console.log("dialog returned: ", data)
})
.fail(function() {
context.invoke('editor.restoreRange');
});
};
self.openDialog = function() {
return $.Deferred(function(deferred) {
var $dialogBtn = self.$dialog.find('.ext-synonym-btn');
var $synonymInput = self.$dialog.find('#input-synonym')[0];
ui.onDialogShown(self.$dialog, function() {
context.triggerEvent('dialog.shown');
$dialogBtn
.click(function(event) {
event.preventDefault();
deferred.resolve({
synonym: $synonymInput.value
});
});
});
ui.onDialogHidden(self.$dialog, function() {
$dialogBtn.off('click');
if (deferred.state() === 'pending') {
deferred.reject();
}
});
ui.showDialog(self.$dialog);
});
};
this.insertToEditor = function(data) {
i++;
//console.log("synonym: " + data.synonym)
var dataArr = data.synonym.split(',');
var restArr = dataArr.slice(1);
var $elem = $('<span>', {
'data-function': "addSynonym",
'data-id': i,
'data-options': '[' + restArr.join(',').trim() + ']',
'data-all': '[' + dataArr.join(',').trim() + ']',
'html': $('<span>', {
'text': dataArr[0],
'css': {
backgroundColor: 'yellow'
}
})
});
context.invoke('editor.insertNode', $elem[0]);
context.invoke('editor.insertText', ' ');
//context.invoke('editor.restoreRange');
//Still a bug : https://github.com/summernote/summernote/issues/3249
$('.summernote').summernote('editor.insertText', ' ');
context.invoke('editor.focus');
}
}
});
}));
#upDialog{
display:none;
}
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js" integrity="sha256-KM512VNnjElC30ehFwehXjx1YCHPiQkOPmqnrWtpccM=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/summernote#0.8.15/dist/summernote-bs4.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/summernote#0.8.15/dist/summernote-bs4.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css" integrity="sha256-rByPlHULObEjJ6XQxW/flG2r+22R5dKiAoef+aXWfik=" crossorigin="anonymous" />
<body style="
padding-top: 50px;
border-left-width: 50px;
padding-left: 50px;
border-right-width: 50px;
padding-right: 150px;
">
<div class="container">
<div class="summernote">
<p>Hello World!</p>
This text should be replaced by the dialog.
</div>
<div id="upDialog" title="Update Value"><input id="input-synonym" class="form-control" type="text" placeholder="Insert your synonym" /></div>
</div>
</body>
You can replace this dialog by a modal to look identical or adapt the Dialog design to the old one.

handle APlayer through angular js - play music dynamically

I am new to angular js - trying to build an audio play using Aplayer
Task:-
1. Play music dynamically
2. On click of album get json data and add to aplayer
(function() {
'use strict';
angular.module('app', []);
angular
.module('app')
.directive('aplayer', aplayer);
function aplayer() {
return {
restrict: 'AC',
link: function(scope, element, attrs) {
// `element` is the angular element the directive is attached to
// APlayer need the native one
var nativeElement = element[0];
var ap1 = new APlayer({
element: nativeElement,
narrow: false,
autoplay: true,
showlrc: false,
mutex: true,
theme: '#e6d0b2',
preload: 'metadata',
mode: 'circulation',
music: {
title: attrs["playerTitle"],
author: attrs["playerAuthor"],
url: attrs["playerUrl"],
pic: attrs["playerPic"]
}
});
ap1.on('play', function() {
console.log('play');
});
ap1.on('play', function() {
console.log('play play');
});
ap1.on('pause', function() {
console.log('pause');
});
ap1.on('canplay', function() {
console.log('canplay');
});
ap1.on('playing', function() {
console.log('playing');
});
ap1.on('ended', function() {
console.log('ended');
});
ap1.on('error', function() {
console.log('error');
});
}
};
}
})();
<!doctype html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/aplayer/1.6.0/APlayer.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div class="aplayer"
data-player-title="Preparation"
data-player-author="Hans Zimmer/Richard Harvey"
data-player-url="http://devtest.qiniudn.com/Preparation.mp3"
data-player-pic="http://devtest.qiniudn.com/Preparation.jpg"></div>
</body>
</html>
How to pass music files dynamically to Aplayer on click of an album.
According to Aplayer source code, it's not possible to replace music track or playlist dynamically. API only expose setMusic method which allow to choose track index in music playlist.
To display your albums, you need to load them first using a service which will call your tracks API.
Once loaded, you can inject them to your aplayer directive and choose the index of the track to play.
Here is a sample snippet with :
a musicService to handle your music library
a TrackController to load and display your music library
a modified aplayer directive to handle tracks and current track index
Note: I'm using free music from Free Music Archive for this demo.
(function() {
'use strict';
angular.module('app', []);
angular
.module('app')
.factory('musicService', ['$timeout', musicService])
.controller('TracksController', ['$scope', 'musicService', TracksController])
.directive('aplayer', aplayer);
function musicService($timeout) {
return {
loadTracks: function() {
// Simulate a 1500 ms $http api call
return $timeout(function() {
return [{
title: 'Procrastinating in the Sun',
author: 'The Spin Wires',
url: 'https://freemusicarchive.org/music/download/e7fee95c2d7f7b1ea8d4260850a6128842eb85a4',
pic: 'https://freemusicarchive.org/file/images/artists/The_Spin_Wires_-_20170510154106040.jpg?width=290&height=290'
},
{
title: 'осоле',
author: 'Kosta T',
url: 'https://freemusicarchive.org/music/download/0e4d722be7bd7ca334970b5407b3e5654b95f7a2',
pic: 'https://freemusicarchive.org/file/images/tracks/Track_-_2017050264944176?method=crop&width=290&height=290'
}
];
}, 1500);
}
}
}
function TracksController($scope, musicService) {
$scope.loadingTracks = true;
$scope.showPlayer = false;
musicService.loadTracks()
.then(function(tracks) {
// Once tracks are loaded, update scope
$scope.tracks = tracks;
$scope.loadingTracks = false;
});
$scope.play = function(trackIndex) {
$scope.trackIndex = trackIndex;
$scope.showPlayer = true;
}
}
function aplayer() {
return {
restrict: 'AC',
scope: {
tracks: '=',
trackIndex: '='
},
link: link
};
function link(scope, element, attrs) {
var player = new APlayer({
narrow: true,
mode: "order",
music: scope.tracks
});
// Watch for trackIndex changes
scope.$watch(
function(scope) {
// Return the "result" of the watch expression as it's more efficient
// #see http://www.bennadel.com/blog/2852-understanding-how-to-use-scope-watch-with-controller-as-in-angularjs.htm
return scope.trackIndex;
},
function(currentIndex, previousIndex) {
// As currentIndex is an integer, if(0) equals if(false)
// But we can use a regular expression to check it's an integer
if (/\d/.test(currentIndex)) {
player.setMusic(parseInt(currentIndex));
player.play();
}
});
}
}
})();
<!doctype html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/aplayer/1.6.0/APlayer.min.js"></script>
<style>
ul li {
list-style-type: none;
}
ul li * {
vertical-align: middle
}
</style>
</head>
<body ng-controller="TracksController">
<div ng-if="loadingTracks">Loading tracks, please wait...</div>
<ul>
<li ng-repeat="track in tracks">
<img ng-src="{{track.pic}}" width="64" height="64">
<button ng-click="play($index)">Play</button> {{track.author}} / {{track.title}}
</li>
</ul>
<div ng-if="tracks && showPlayer">
<h2>Now playing</h2>
<div>{{tracks[trackIndex].author}} / {{tracks[trackIndex].title}}</div>
<div class="aplayer" data-tracks="tracks" data-track-index="trackIndex"></div>
</div>
</body>
</html>

jquery change svg fill color twice

I'm trying to change the color of an SVG object with a jQuery, but after that i want to reset all fill attribute in class st24.
But after reset setAttribute doesn't work.
//Автокомплит
$(function() {
$( "#users" ).autocomplete({
source: function( request, response ) {
$.ajax({
cache: true,
url: "http://127.0.0.1/json.php",
dataType: "json",
success: function(data) {
response($.map(data, function(item){
return {
label: item.username,
placeId: item.LocationInfo.placeId,
}}));
}
});
},
minLength: 2,
select: function(event,ui) {
$('#users').val(ui.item.username);
$('#placeId').val(ui.item.placeId);
console.log(ui.item.placeId);
console.log(svgdom);
var svgElement = svgdom.getElementById(ui.item.placeId);
//Если id елемента есть в svg файле - меняем аттрибур fill, если нет генерируем алерт
if (svgElement) {
var st24 = svgdom.getElementsByClassName("st24");
$.each(st24, function( i, val) {
val.setAttribute("fill", "none");
});
svgElement.setAttribute("fill", "#008000");
} else {
generate('information');
}
}
});
});
});
If i try this:
$.each(st24, function( i, val) {
val.setAttribute("fill", "#008000");
});
work perfect - all elements have this attribute, but when i change setAttribute fill to none, and add this line: svgElement.setAttribute("fill", "#008000"); after this code, it doesn't work - why ?
Update:
This all my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="libs/jquery-2.1.1.min.js"></script>
<script src="libs/jquery-ui-1.10.4.js" type="text/javascript"></script>
<script src="libs/jquery.panzoom.js"></script>
<script src="libs/jquery.mousewheel.js"></script>
<script src="libs/noty/packaged/jquery.noty.packaged.min.js" type="text/javascript"></script>
<script src="libs/svg-pan-zoom.min.js"></script>
<link href="css/style.css" rel="stylesheet">
<link type="text/css" href="css/jquery-ui-1.10.4.custom.css" rel="stylesheet" />
</head>
<body>
<a id="link" href="admin.html">Admin</a>
<div class="ui-widget">
<label for="users">users: </label>
<input id="users" type="text" />
<input readonly="readonly" id="placeId" name="placeId" />
</div>
<embed src="svg/5.svg" width="900" height="900" id="imap"/>
<script>
//Всплывающие сообщения
function generate(type) {
var n = noty({
text : "Sorry place not found",
type : type,
dismissQueue: true,
layout : 'topCenter',
theme : 'defaultTheme',
timeout: 2000,
});
}
function generateInf() {
generate('information');
}
// Начинаем работу когда страница полностью загружена (включая графику)
$(window).load(function () {
// Получаем доступ к SVG DOM
var svgdom = document.getElementById('imap').getSVGDocument();
svgPanZoom('#imap', {
zoomEnabled: true,
controlIconsEnabled: true
});
function clearSvg() {
var st24 = svgdom.getElementsByClassName("st24");
$.each(st24, function(i, val) {
val.removeAttribute("fill");
});
}
function setColor(elem) {
elem.setAttribute("fill", "#008000");
}
//Автокомплит
$(function() {
$( "#users" ).autocomplete({
source: function( request, response ) {
$.ajax({
cache: true,
url: "http://127.0.0.1/json.php",
dataType: "json",
success: function(data) {
response($.map(data, function(item){
return {
label: item.username,
placeId: item.LocationInfo.placeId,
}}));
}
});
},
minLength: 2,
select: function(event,ui) {
$('#users').val(ui.item.username);
$('#placeId').val(ui.item.placeId);
console.log(ui.item.placeId);
console.log(svgdom);
var svgElement = svgdom.getElementById(ui.item.placeId);
//Если id елемента есть в svg файле - меняем аттрибур fill, если нет генерируем алерт
if (svgElement) {
clearSvg();
setColor(svgElement);
} else {
generate('information');
}
}
});
});
});
</script>
</body>
</html>
Setting fill to none doesn't delete the fill. It is saying "this element has no fill". So setting fill on the <svg> parent doesn't override that. The value on the child takes precendence.
If you want to remove the fill setting from the children use
val.removeAttribute('fill');
Update
Try something like this:
if (svgElement) {
var st24 = svgdom.getElementsByClassName("st24");
$.each(st24, function( i, val) {
val.removeAttribute("fill");
});
svgElement.setAttribute("fill", "#008000");
} else ...
Update 2
Have a look at this fiddle. Hopefully it should help explain what I mean.
http://jsfiddle.net/gt7nd/1/
Use this
$(val).css('fill',"#008000");
or this
val.style.fill = "#000800"
My SVG code:
<g id="505Б-1" place="place" v:mID="1937" v:groupContext="shape" transform="translate(206.929,-334.488)" class="test">
<title>Circle, ellipse.1937</title>
<v:userDefs>
<v:ud v:nameU="visVersion" v:val="VT0(14):26"/>
</v:userDefs>
<path transform="" d="M0 837.64 A4.25197 4.25197 0 0 1 8.5 837.64 A4.25197 4.25197 0 1 1 0 837.64 Z" class="st24"/>
</g>
I use class "test" instead "st24", and everything works now!

How to tell what has been destroyed by the destroy command in a kendo UI grid?

I'm using Kendo UI 2013.2.716 and JQuery 2.0.3 and I am placing a grid on my page, and my question is:
Does anyone know how to tell what has been destroyed by the destroy command from the grid?
For example:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<link href="kendo.common.min.css" rel="stylesheet" />
<link href="kendo.default.min.css" rel="stylesheet" />
<script type="text/javascript" src="jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="kendo.all.min.js"></script>
</head>
<body>
<div id="grid"></div>
<script type="text/javascript">
$(document).ready(function() {
var products = [];
for(var i = 1; i <= 40; i++) {
products.push({
ProductId: i,
ProductName: "Product " + i
});
}
$("#grid").kendoGrid({
dataSource: {
data: products,
schema: {
model: {
id: "ProductId",
fields: {
ProductName: { type: "string" },
}
}
},
requestEnd: function (e) {
if (e.type === "destroy") {
alert("OK, so something got destroyed, but what??");
}
}
},
editable: "inline",
columns: [
"ProductName",
{ command: "destroy", title: " ", width: "100px" }
]
});
});
</script>
</body>
</html>
I found the requestEnd callback in the documentation but I am totally flummoxed as to know where the item that was destroyed would be. I just need the ID of the item really so that I can update other parts of my page appropriately.
I am wondering if I need to capture it somehow beforehand.
You need to configure the transport object on your datasource. In your current configuration, does anything really get destroyed? Sure, the item may disappear from your grid, but I wonder if it's still there in the datasource. Maybe that's what you intended.
http://docs.kendoui.com/api/framework/datasource#configuration-transport.destroy
If you're just looking for a way to get at the data that's being destroyed, hook into the parameterMap() function of the transport object. In there you can manipulate the object being deleted before the operation is executed.
http://docs.kendoui.com/api/framework/datasource#configuration-transport.parameterMap
Thanks to #Brett for pointing out the destroy property on the transport. This code does the trick - allowing me to capture what was being destroyed (see the transport.destroy part):
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<link href="kendo.common.min.css" rel="stylesheet" />
<link href="kendo.default.min.css" rel="stylesheet" />
<script type="text/javascript" src="jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="kendo.all.min.js"></script>
</head>
<body>
<div id="grid"></div>
<script type="text/javascript">
$(document).ready(function() {
$("#grid").kendoGrid({
dataSource: {
schema: {
model: {
id: "ProductId",
fields: {
ProductName: { type: "string" },
}
}
},
transport: {
read: function (options) {
var products = [];
for(var i = 1; i <= 40; i++) {
products.push({
ProductId: i,
ProductName: "Product " + i
});
}
options.success(products);
},
destroy: function (options) {
var data = $("#grid")
.data("kendoGrid").dataSource.data();
var products = [];
for(var i = 0; i < data.length; i++) {
if (data[i].ProductId !== options.data.ProductId) {
products.push(data[i])
}
}
options.success(products);
alert("Woo hoo - the product with the ID: "
+ options.data.ProductId + " was destroyed!");
}
}
},
editable: "inline",
columns: [
"ProductName",
{ command: "destroy", title: " ", width: "100px" }
]
});
});
</script>
</body>
</html>

Categories