I want to use jwplayer on a Single Page App built using Durandal framework. The idea is to create a persistent audio player, unaffected by the application's navigation. But I haven't been able to load jwplayer in Durandal.
I've successfully loaded jwplayer on a simple html file using Require.js. But this method doesn't seem to work on Durandal (which also uses Require.js).
This is my code on shell.js:
define(['jwplayer/jwplayer','durandal/plugins/router','durandal/app'], function (jwplayer,router,app) {
jwplayer.api('player').setup({
width: '320',
height: '40',
sources: [{
file: "rtmp://127.0.0.1:1935/vod/mp3:sample_1.mp3"
},{
file: "http://127.0.0.1:1935/vod/sample_1.mp3/playlist.m3u8"
}]
});
return {
router: router,
activate: function () {
...
}
};
});
And inside the shell.html I have this code: <div id="player">loading player...</div>
This is the error message I get:
Uncaught TypeError: Cannot read property 'id' of null jwplayer.js:37
It seems that the player element is unrecognizable inside a Durandal module. What caused this problem? How do I add a jwplayer inside a Durandal module?
As you already figured out, it looks like the player element isn't recognized when the code is executing. When the module is loading for the first time, the DOM definitely isn't ready. It may not even be ready during the activate() callback function. The right time to setup the jwplayer is probably in the viewAttached() callback.
You can read more about the viewAttached callback here in the Composition Callbacks section: http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks/
Something like this:
define(['jwplayer/jwplayer','durandal/plugins/router','durandal/app'], function (jwplayer,router,app) {
return {
router: router,
activate: function () {
...
},
viewAttached: function(){
jwplayer.api('player').setup({
width: '320',
height: '40',
sources: [{
file: "rtmp://127.0.0.1:1935/vod/mp3:sample_1.mp3"
},{
file: "http://127.0.0.1:1935/vod/sample_1.mp3/playlist.m3u8"
}]
});
}
};
});
Related
I am writing a new Electron app and to make things a bit more organized I created an object that will encapsulate my display code. My target was to connect the display renderer object with the backend code and keep things really happy.
But, I create the renderer object in the main.js module and I don't know how to get that instance into the preload.js file because all the renderer functions are being called from preload.
What is the right way to tackle this? Global? Is there a way to expose the object instance in main.cs in the preload for each instance I create? (yes, I have multiple renderers created from the same class).
Look at this code which I found on a blog. It's got a similar structure, but I don't see how to access the main.cs renderer object from the preload.
// main.js
app.whenReady().then(() => {`
let mainWindow = new BrowserWindow({`
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true
},
width:640,
height: 480,
resizable: false
})
// ... rest of code
//preload.js
const { contextBridge, ipcRenderer} = require('electron')
contextBridge.exposeInMainWorld(
'electron',
{
sendMessage: () => ipcRenderer.send('countdown-start')
}
)
// renderer.js
document.getElementById('start').addEventListener('click', _ => {
window.electron.sendMessage()
})
Basically this question is technically incorrect as I have discovered.
The object I create are in the main process, and the renderer process can't access them at all.
What I ended up doing was exposing methods in the main process to work with the object. I called these methods from Preload and things are working fine.
Sorry for a confusing question.
Am getting the above error in console on loading my application. My controller looks as below.Am new to angular so any help would be appreciated
Am trying to export the datatable into excel here. I think the issue is here angular.module('myApp','datatables', 'datatables.buttons'])
What is the correct way to include them in the module?
ViewCtrl.js
angular.module('myApp',['datatables', 'datatables.buttons'])
.controller('ViewCtrl', function($scope, DTOptionsBuilder, DTColumnDefBuilder) {
$scope.dtOptions = DTOptionsBuilder.newOptions()
.withPaginationType('full_numbers')
.withDisplayLength(2)
.withDOM('pitrfl')
.withButtons([{
extend: 'excelHtml5',
}
]);
vm.dtColumnDefs = [
DTColumnDefBuilder.newColumnDef(0),
DTColumnDefBuilder.newColumnDef(1).notVisible(),
DTColumnDefBuilder.newColumnDef(2).notSortable()
];
});
Verify the console loads. If the console.log does not show and you still get the inject error, then your problem is likely the datatables and datatables.buttons. Verify that these files are added to your index.html file prior to this file.
angular.module('myApp', ['datatables', 'datatables.buttons'])
.controller('ViewCtrl', function ($scope) {
console.log("Code of awesomeness");
});
If you find that you do see the console.log("Code of awesomeness"); Then your $inject issue is DTOptionsBuilder, DTColumnDefBuilder. Verify spelling.
I am trying to insert youtube videos with the iframe API in to an existing page with the help of a chrome extension content script. But I cannot get the onYouTubeIframeAPIReady to trigger.
manifest.json
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*", "file://*/*", "*://*/*"],
"js": ["content-script.js"]
}
],
content-script.js
const appEl = document.createElement('div');
appEl.id = 'my-app';
appEl.innerHTML = `<div id="youtube-iframe"></div>`;
const bodyEl = document.querySelector('body');
bodyEl.insertBefore(appEl, bodyEl.firstChild);
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
document.querySelector('body').appendChild(tag);
window.onYouTubeIframeAPIReady = () => {
this.player = new YT.Player('youtube-iframe', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
}
});
}
function onPlayerReady(event) {
console.log('player ready');
event.target.playVideo();
};
In a chrome-app I was able to make it work with a webview but this does not seem to be available in extensions.
I solved the problem, here is the solution.
I tried all variants of the code injection method but the problem was the the YouTube API script was defining an anonymous function that expected the window as an input argument. So even after following the advice of not loading external scripts (chrome web store might remove your extension) and having a local file that I included with different means I was not able to get the onYouTubeIframeAPIReady to be triggered by the YouTube API script. Only after pasting the script into the same file where I defined onYouTubeIframeAPIReady I was able to see the video. However to organize the code better, so it works with ES6 imports (via Webpack) I did the following steps.
Download the YouTube API script (https://www.youtube.com/iframe_api see https://developers.google.com/youtube/iframe_api_reference) to a local file.
Adopt the script to work as module by changing the the script from
(function(){var g,k=this;function l(a){a=a.split(".");
...
Ub=l("onYouTubePlayerAPIReady");Ub&&Ub();})();
to
export default function(){var g,k=window;function l(a){a=a.split(".")
...
Ub=l("onYouTubePlayerAPIReady");Ub&&Ub();}
This changes the anonymous function call to a function that is exported in a ES6 module style and the this object in the anonymous function is exchanged with the window. I saved it in the file as youtube-iframe-api.js
Now I was able to use the YouTube API in another module with the following code
import youtubeApi from './youtube-iframe-api';
function onPlayerReady(event) {
event.target.playVideo();
},
window.onYouTubeIframeAPIReady = () => {
this.player = new YT.Player('youtube-iframe', {
height: '100',
width: '100',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady,
}
});
}
youtubeApi();
In my web site, I have bunch of JavaScript files, all for different use but the login page only needs the jquery and loginValidate files. When I attach my main.js, it is suppose to load only these two files by checking the conditions. How to do that?
My config file:
requirejs.config({
baseUrl:"scripts",
paths:{
//libraries
jquery :"lib/jquery-1.9.0.min",
jqueryUI :"lib/jquery-ui-1.9.2.custom.min",
underScore :"lib/underscore-min",
backBone :"lib/backbone-min",
//scripts
appInit :"js/tasklist",
loginValidate :"js/loginValidate"
},
shim:{
"underScore":{
exports: "_"
},
"backBone":{
exports:"Backbone",
deps:["underScore"]
},
"appInit" : {
deps:["jquery","jqueryUI","underScore","backBone"]
},
"jqueryUI":{
deps:["jquery"]
},
"loginValidate":{
deps:['jquery']
}
}
});
It is only needed on login page:
require(["jquery","loginValidate"], function($,validate){
how can i call the loginValidate function?
});
The loginValidate function:
define(function(){
var tasklistHandler = function (params) {
//params take care.. to validate
};
$(function(){ // calling internally the function
var paramsLoginForm = {
loginForm : $('#tlLoginForm')
}
tasklistHandler(paramsLoginForm);
});
})
Is this the correct way to do? I am using also Backbone.js to utilise some other page; how can I proceed for those pages?
The problem you have is the lack of "return" in the "factory" function (the callback function you pass to the define call in the last snippet).
Instead you need to have something like this there:
define(function(){
return function (params) {
//params take care.. to validate
};
})
And in your require call you use run the function that is returned to you by factory function:
require(["jquery","loginValidate"], function($, validate){
var paramsLoginForm = {
loginForm : $('#tlLoginForm')
}
// \|/ that is what is returned by `define`'s callback function.
validate(paramsLoginForm);
});
Say I want to use jquery together with a standard, non-amd enabled jquery plugin that has been defined using standard closure: (function($))( $.fn.myplugin = { ... } )(jQuery); and it all sits inside of a js/libs/jquery/jquery.myplugin.js.
I use this config:
require.config({
baseUrl: 'js/',
paths: {
'jquery': 'libs/jquery/jquery-noconflict',
'underscore': 'libs/underscore/underscore',
'backbone': 'libs/backbone/backbone',
'jquery-myplugin': 'libs/jquery/jquery.myplugin'
},
shim: {
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'jquery-myplugin': {
deps: ['jquery']
}
});
I load jQuery in no-conflict mode in libs/jquery/jquery-noconflict.js, becase I don't want to pollute global namespace:
define(['libs/jquery'], function () {
return jQuery.noConflict(true);
});
and this is how I load my main app.js:
define([
'jquery',
'underscore',
'backbone',
'jquery-myplugin'],
function($, _, Backbone, MyPlugin){
//MyPlugin is always undefined, not even sure if
//I should be passing it here if it only extends jQuery?
});
Now, here is the problem I am experiencing - while I can use all libraries defined above without any problems, I could not work out the correct shim configuration to load non-AMD enabled jquery plugins.
I've tried setting up jquery-myplugin as deps of the jquery (and other way around) but I could never get it working.
It seems like I'm having problem with the following scenario:
jQuery loads in no-conflict mode.
plugin code runs, extending the instance of the jQuery above
I can use $ within my application, extended by the plugin code, so $.myplugin is available.
I have seen similar questions floating around but none of them actually resolves this issue giving only vague suggestions such as "use shim config"...
Edit
I also tried using
"jquery-myplugin": {
deps: ["jquery"],
exports: "jQuery.fn.myplugin"
}
And whilst plugin methods are available once loaded as AMD module this way, I still can't access: $('.class').myplugin() as default $ object hasn't been extended with myplugin code.
Using jQuery.noConflict(true) removes the jQuery global variable. When your plugin loads, it tries to access jQuery, but can't, causing this failure.
If your plugin was a module, it could get access to jQuery as a dependency. Or you could leave jQuery available as a global.
First, insure that "path/to/jquery-myplugin" actually extends window.jQuery and not $
noConflict() leaves window.jQuery object defined but unbinds itself from window.$ On some new browsers window.$ is built in alias for native document.querySelectorAll function.
Second, your myplugin does NOT need to return itself, as it cannot be used by itself. Since it extends jQuery, return jQuery from myplugin call.
Lastly, "path/to/jquery-myplugin" is NOT a module. It's a plain JS file. It's possible RequireJS tries to load it like a module and does not find define() call, which leads to mess. Try actually adding ".js" file extension to the reference to signal to RequireJS that it needs to use "js!" plugin to load the resource.
require.config({
paths: {
"jquery": "path/to/jquery",
"jquery-myplugin": "path/to/jquery-myplugin.js"
},
shim: {
"jquery": {
init: function() {
return window.jQuery.noConflict();
},
"jquery-myplugin": {
deps: ['jquery']
init: function($) {
return $;
},
}
}
});
I had the same problem as you today. Here is how I could fix it :
requirejs.config({
"baseUrl": "js/lib",
"paths": {
"app": "../app"
}
});
// Final version of jQuery with all needed plugins.
define("jquery", ["jquery-core", "myplugin"], function(jqCore) {
return jqCore;
});
// Define core jQuery module.
define("jquery-core", ["jquery-1.9.1.min"], function() {
return jQuery.noConflict(true);
});
// This module exposes jquery module to the global scope and returns previous defined version.
define("jq-global", ["jquery-core"], function(jqCore) {
var tmp = jQuery;
jQuery = jqCore;
return tmp;
});
// Define your plugin here, and don't forget to reset previous jQuery version.
define("myplugin", ["jq-global", "jquery.myplugin"], function(jqGlobal, jqPlugin) {
jQuery = jqGlobal;
});
// Load the main app module to start the app
requirejs(["app/main"]);
Now in my app/main.js file I can do the following :
define(["jquery"], function($) {
$('body').myplugin();
});
The idea here is to expose jQuery temporary before plugin code is executed. So far I didn't test the solution in a larger environment with a lot more modules to load, so I can't guarantee it will work in the long term ;)
Edit
This solution won't work!! Since requirejs doesn't load the scripts sequentially, it is possible the plugin js file loads before jquery which will cause the execution to fail. Sorry for this.
If someone has another idea...