Chrome extension using RequireJS, Backbone (Chaplin) conflicts - backbone.js

I am creating a Google Chrome Extension that have to add content on the visited websites (like a toolbox).
I have to use RequireJS and BackboneJS (Chaplin) and everything is ok except when i'm visiting a website using RequireJS (and Backbone, but the problem seems to come from RequireJS conflicts).
(This is when I use content scripts to include a -script- tag that includes RequireJS.)
I suppose it's normal to have conflicts if I add content directly in the page so I tried the solution here : Loading multiple instances of requireJS and Backbone
It seems to work (for now), but the website is trying to reload his own RequireJS file (with his path, but in my extension) before loading mine and I'm afraid it could lead to unexpected behaviour.
Plus, I have to precise my file paths in requirejs.config or it's looking for them in Bitbucket sources (cloudfront). (Maybe it's normal though)
Example with bitbucket :
Denying load of chrome-extension://mgncmiffelpdhlbkkmmaedbodabdchea/https://d3oaxc4q5k2d6q.cloudfront.net/m/7aaf1677069c/amd/build/main.js?8uxr. Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.
<--------- This file is Bitbucket's RequireJS, Bitbucket is still working fine though
Is there another solution I didn't find yet ? Or am I doing it wrong ? I'm a beginner with RequireJS (and Chrome ext.. and Backbone...) so I might have missed something.
Here is the Content script part in manifest.json
"content_scripts": [
{
"matches": ["https://*/*", "http://*/*"],
"js": ["bower_components/requirejs/require.js",
"extension/init-app.js",
"extension/main.js"]
}],
init-app.js is Rob's script
require.load = function(context, moduleName, url) {
url = chrome.extension.getURL(url);
var x = new XMLHttpRequest();
// Append Math.random()... to bust the cache
x.open('GET', url + '?' + Math.random().toString(36).slice(-4));
x.onload = function() {
var code = x.responseText;
x += '\n//# sourceURL=' + url; // Optional, for debugging.
window.eval(code);
context.completeLoad(moduleName);
};
x.onerror = function() {
// Log error if you wish. This is usually not needed, because
// Chrome's developer tools does already log "404 Not found"
// errors for scripts to the console.
};
x.send();
};
and main.js contain requirejs.config + app
// Configure the AMD module loader
requirejs.config({
skipDataMain: true,
// The path where your JavaScripts are located
baseUrl: 'extension',
// Specify the paths of vendor libraries
paths: {
jquery: '../bower_components/jquery/jquery',
underscore: '../bower_components/lodash/dist/lodash',
backbone: '../bower_components/backbone/backbone',
handlebars: '../bower_components/handlebars/handlebars',
text: '../bower_components/requirejs-text/text',
chaplin: '../bower_components/chaplin/chaplin',
application: '/extension/application',
routes: '/extension/routes',
},
// Underscore and Backbone are not AMD-capable per default,
// so we need to use the AMD wrapping of RequireJS
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
handlebars: {
exports: 'Handlebars'
}
}
// For easier development, disable browser caching
// Of course, this should be removed in a production environment
//, urlArgs: 'bust=' + (new Date()).getTime()
});
// Bootstrap the application
require(['application', 'routes'], function(Application, routes) {
new Application({routes: routes, controllerPath: 'scripts/controllers/', controllerSuffix: '-controller'});
});
It works on gooogle.com for instance, but I get
GET chrome-extension://ccgfmmmnebacpnbdpdnphmnmicaooddg/extension/Home.js?9zfr net::ERR_FILE_NOT_FOUND
on https://www.cloud9trader.com (website using RequireJS) because it has
<script data-main="/0.2.59/scripts/Home.js" src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.14/require.min.js"></script>
in its source. To summarize I just need the script to ignore the "current" website Require file.

The skipDataMain option is synchronously checked when require.js is loaded. Setting this variable after loading require.js has no effect on the loader any more, because the data-main scan has already run at that point.
The correct way to skip data-main is to declare the configuration before loading require.js, as follows:
// extension/config.js
var require = {
skipDataMain: true
};
manifest.json:
{
...
"content_scripts": [{
"matches": ["https://*/*", "http://*/*"],
"js": [
"extension/config.js",
"bower_components/requirejs/require.js",
"extension/init-app.js",
"extension/main.js"
]
}],
...
}

Related

Unable to get react to work in requirejs environment

I have a regular ol' requirejs app, that pulls in modules in regular JS I write. I really want to start using reactjs but am not even able to get it to load with out throwing errors in the console.
My Paths
'react': '../node_modules/react/cjs/react.production.min', // there is a cjs and a umd (Universal Module Definition) version, I dont think I am UMD. we are Asynchronous Module Definition (AMD)
'reactDOM': '../node_modules/react-dom/cjs/react-dom.production.min',
'JSXTransformer': 'vendors/react/JSXTransformer-0.10.0',
'jsx': 'vendors/react/jsx',
Is it possible that there is a shim dependency that I need to setup?
'react' : {
'deps' : ['jquery']
},
'reactDOM' : {
'deps' : ['jquery']
}
I have also included them normally such as
require(["jquery", "jqueryUi", "bootstrap", "react", "reactDOM", "front", "owlCarousel", "select2", "blockui", ], function ($, jqueryUi, bootstrap, React, ReactDOM, front, owlCarousel, select2, blockui) {
$(document).ready(function(){
require([ $('#requirePageSpecificJs').val() ]); // this is set in php, result: "/require-mturk.js"
});
});
I get this error, when loading a page.
I am completely new to React, so I may be missing some things. Does the latest react require Babel to run, and if so do I need to include babel as a package, and add that as a shim? I am seeing some other posts of using requirejs with react but it is requiring different tools and stuff. completely lost here.

Bundle the js files based on requirejs structure

We have requirejs.
I have mentioned the paths and the shim in requirejs.
Here, when I load the controller in angularjs router, the files are loading separately.
require.config({
urlArgs: "bust=" + (new Date()).getTime(),
baseUrl: 'Folder',
waitSeconds: 200,
paths: {
// Jquery
'Angular': 'Angular path',
'FileA': 'FileA',
'FileB': 'FileB',
},
shim: {
'FileB'{
Deps:'FileA'
},
'FileA'{
Deps:'Angular'
},
}
When I require FileB, it will automatically retrieve fileA and angular file.
But in this case, I am requesting three http request for the three files.
Is there any solution that, based on requirejs structure bundling the files?
So that when I request the file, I will get the single file instead of multiple files
If there is any dependency between the files that you are trying to bundle then bundling them in separate files doesn't make any sense.
However if you are sure that dependencies would not be any problem you can use bundle a new feature from requirejs:
requirejs.config({
bundles: {
'shared': ['angular'],
'fileA': ['fileA'],
'fileB': ['fileB'],
// etc...
}
});
Also you can add your template to the bundle which is awesome. More info here.

r.js cannot resolve dependencies mentioned in shim

I've recently joined a project which is built using Backbonejs, (uses Marionette for view rendering) + nodejs. They also use requirejs to load the backbonejs files. Would like to add at this stage, that I've never worked with backbonejs or requirejs before and hence I'm struggling with the issue I describe later.
Some code that will help explain the issue that I run into (All this code was already written by previous dev's)
Folder Structure:
/public
/js
/collection (consists all Backbone.js collections files)
/lib
/bower_components
/backone
/marionette
/etc
/models (consists all Backbone.js models files)
/views (consists all Backbone.js view files)
/main.js
/main.build.js
/app.js
/controller.js
/router.js
Code from files that I think relate to issue:
main.js
requirejs.config({
paths: {
'async': 'lib/bower_components/requirejs-plugins/src/async',
'jquery': 'lib/bower_components/jquery/dist/jquery.min',
'underscore': 'lib/bower_components/underscore/underscore-min',
'lodash': 'lib/bower_components/lodash/dist/lodash.min',
'backbone': 'lib/bower_components/backbone/backbone',
'marionette': 'lib/bower_components/marionette/lib/backbone.marionette.min',
'markercluster':'lib/markercluster',
'jquerymobile': 'lib/jquery.mobile-1.4.0.min',
'hogan': 'lib/template-2.0.0.min',
'templates': '/templates',
'real': 'lib/mainjs',
'touch': 'lib/jquery.touchSwipe.min',
'mouse': 'lib/jquery.mousewheel',
'moment': 'lib/moment-2.5.1.min',
'humanize': 'lib/bower_components/humanize-plus/public/dist/humanize.min',
'validator': 'lib/bower_components/validator-js/validator.min',
'real': 'lib/mainfile'
},
shim: {
backbone: {
deps: ["underscore"]
},
marionette: {
deps: ["backbone"]
},
templates: {
deps: ["hogan", "jquery"]
},
real: {
deps: ["jquery", "jquerymobile", "touch", "mouse"]
},
markercluster: {
exports: "MarkerClusterer"
},
humanize: {
exports: "humanize"
}
},
waitSeconds: 0
});
define('gmaps', ['async!http://maps.google.com/maps/api/js?v=3&key=AIzaSyBiV8f88yLWJ_IMSdP1fVNO1-gt3eLVSgg&sensor=true&callback=gMapsCallback'], function(){
// define('gmaps', ['http://maps.google.com/maps/api/js?v=3&sensor=false'], function(){
return window.google.maps;
});
require(['app', 'templates', 'real'], function(app) {
app.start({
version: "0.9.9"
});
});
main.build.js
({
baseUrl: ".",
name: "main",
wrapShim: true,
out: "main-built.js"
})
app.js
define(['underscore', 'controller', 'router', 'models/Cache', 'views/RootView'], function(_, Controller, Router, Cache, RootView) {
var Application = Marionette.Application.extend({
propertyListPageSize: 3,
initialize: function() {
_.templateSettings = { interpolate : /\{\{(.+?)\}\}/g };
},
onStart: function(options){
new RootView();
this.controller = new Controller();
this.router = new Router({controller: this.controller});
this.cache = new Cache();
this.context = {};
//this.evHistory = [];//#todo remove once BB/marionette navigation is in place
if(Backbone.history) Backbone.history.start({ pushState: false });
if(Backbone.history.fragment === "") this.navigate('home');
},
navigate: function(fragment, trigger, replace){
this.router.navigate(fragment, {trigger:trigger, replace:replace});
},
back: function() {
window.history.back();
}
});
app = new Application();
return app;
});
rootView.js
define(['marionette', 'views/HomeView', 'views/HeaderView', 'views/FooterView', 'views/MenuView', 'views/VideoView', 'views/LocationSearchView', 'views/LoginView', 'views/FindView', 'views/ServicesView', 'views/ValueView', 'views/PropertyListView', 'views/SideBySideView', 'views/ConfirmRegistrationView', 'views/ForgotPasswordView', 'views/CreateAccountView', 'views/UserHomeView', 'views/MyBrokerView', 'views/GiveFeedbackView', 'views/SeeFeedbackView', 'views/ViewingScheduleView', 'views/MyViewingsSummaryView', 'views/MyAccountView', 'views/ViewingConfirmView', 'views/ValueAddressPropertyListView'],
function(Marionette, HomeView, HeaderView, FooterView, MenuView, VideoView, LocationView, LoginView, FindView, ServicesView, ValueView, PropertyListView, SideBySideView, ConfirmRegistrationView, ForgotPasswordView, CreateAccountView, UserHomeView, MyBrokerView, GiveFeedbackView, SeeFeedbackView, ViewingScheduleView, MyViewingsSummaryView, MyAccountView, ViewingConfirmView, ValueAddressPropertyListView) {
var RootView = Marionette.LayoutView.extend({
...some view code
});
Use case I'm trying to solve:
So when I access the site in the browser, I notice in the debugger that it loads all the js files right at the beginning. During the load process my site is blank and user has to wait a while before he can use the site.
So what I've been able to understand is that when app is 'started' in main.js, app.js creates an instance of rootView.js , which in turn has all the views listed as dependencies. This triggers a download request for all the other views which in turn would solve their own dependencies and download all the relevant models and collections. Hence all files being downloaded when the user accessed the site.
Solution I've been trying:
Since requirejs is being used, I'm trying to use r.js to optimize and combine all the js files to reduce the number of downloads.
Issue I'm running into:
When i run r.js. i get the following error
Tracing dependencies for: main
Error: ENOENT: no such file or directory, open '/var/node_projects/rm/rm.src.server/src/public/js/underscore.js'
In module tree:
main
app
Error: Error: ENOENT: no such file or directory, open '/var/node_projects/rm/rm.src.server/src/public/js/underscore.js'
In module tree:
main
app
at Error (native)
If I add the underscore.js files directly to the specified path in the error, then I get the same error for marionette.js. What I think is happening is that app.js is not recognizing the shim'ed dependencies and hence its trying to find the files directly at specified path in the error.
Things I've tried:
- I've added wrapShim: true in the main.build.js file but that did not help
Honestly, I've been sitting on this for a couple of days and I'm not sure what I can do next and hence this post.
Any help/direction would be appreciated.
You need to include the same shim configuration in your build file, as wrapShim is not sufficient.
If shim config is used in the app during runtime, duplicate the config here. Necessary if shim config is used, so that the shim's dependencies are included in the build. Using "mainConfigFile" is a better way to pass this information though, so that it is only listed in one place. However, if mainConfigFile is not an option, the shim config can be inlined in the build config.
https://github.com/jrburke/r.js/blob/master/build/example.build.js

Properly loading webshims / modernizr with requirejs?

I have modernizr / pollyfiller included at the top of my index.html file, and in my main.js:
require.config({
paths : {
'jquery' : 'lib/jquery-1.10.2.min',
'jqdate' : 'lib/jquery.dateFormat-1.0',
'webshims' : 'lib/polyfiller'
},
shim : {
'lib/underscore' : {
exports : '_'
},
'lib/backbone' : {
deps : ["lib/underscore", "jquery"],
exports : 'Backbone'
},
"modernizr" : {
deps : ["jquery"],
exports : "modernizr"
},
"webshims" : {
deps : [ "jquery", "lib/modernizr-custom"],
exports: "webshims"
}
}
});
var router, vent;
require(["jquery", "lib/underscore", "lib/backbone", "app", "lib/modernizr-custom", "webshims"], function($, _, Backbone, Router, modernizr, webshims) {
$(function() {
$.webshims.setOptions('forms forms-ext', {
replaceUI : false,
waitReady : false
});
$.webshims.polyfill('forms forms-ext');
router = new Router();
vent = _.extend({}, Backbone.Events);
$.expr.cacheLength = 1;
Backbone.history.start({
});
});
});
This will generally load fine, however, sometimes it looks like webshims is not defined by the time I try to call:
$.webshims.setOptions('forms forms-ext', {
replaceUI : false,
waitReady : false
});
$.webshims.polyfill('forms forms-ext');
and I get the error: TypeError: $.webshims is undefined
Is there a better way to load this?
edit
So, I updated the script like you said, and had to capitalize Webshims in the paths and shim definition. It loads fine but now I get an error:
Uncaught SyntaxError: Unexpected token <
in Chrome and
SyntaxError: syntax error
<!DOCTYPE html>
in firefox
Updated Answer
Alexander Farkas pointed out in a comment that polyfiller defines itself as "polyfiller" like this:
define('polyfiller', ['jquery'], factory);
So:
No shim should be required to load polyfiller.js.
The module defined by polyfiller.js should always be referred to as "polyfiller". So there has to be a paths setting that maps the module name polyfiller to the actual path of the polyfiller.js file.
So the original config should be modified to remove the "webshims" shim, then the paths setting "webshims": "lib/polyfiller" should become "polyfiller": "lib/polyfiller" and the require call should be:
require(["jquery", "lib/underscore", "lib/backbone", "app", "lib/modernizr-custom", "polyfiller"], function($, _, Backbone, Router, modernizr) {
I've dropped the last variable from the function's parameters because there's no need to pass the module value since the polyfiller.js file registers itself as $.webshims.
This is similar to how jQuery defines itself as "jquery" (it needs no shim and is always called "jquery").
Original Answer
Change your require call so that you require "webshims" instead of "lib/polyfiller":
require(["jquery", "lib/underscore", "lib/backbone", "app", "lib/modernizr-custom", "webshims"], ...
The code in your question shows you've set the paths configuration option so that the module name "webshims" resolves to "lib/polyfiller", and created what looks like a sensible shim for it. However, when you require the webshims module you refer to it as "lib/polyfiller". RequireJS does not do a reverse resolution to figure out that "lib/polyfiller" is "webshims".
Alternatively, you could drop the "webshims" name from paths and rename the shim so that it is set for "lib/polyfiller". However, I consider it to be a better practice to refer to 3rd party libraries by one-word names throughout an application rather than have paths for them. So "jquery", "bootstrap", "underscore", and "webshims" etc. rather than "lib/...".

jQuery File Upload doesn't work in IE9 when compressed with Requirejs

My code uses Backbone with Requirejs and jQuery File Upload and works perfectly in IE 9 when I access the development code directly (without building). As soon as I build it something goes wrong and the request is sent empty to the server.
The only difference I see in the request (using Network capturing in IE9) is that the one that works when I click in the upload button, the Initiator is click, and for the one that fails, the initiator is JS Library XMLHttpRequest.
So there must be something making the event change when the code gets compressed, but I have no idea how to get closer to the root of the problem.
Any ideas?
Are you requiring the iFrame Transport plugin?
I am using jQuery File Upload with Requirejs, and have not seen this issue. Without seeing some code, it is hard to say what is tripping up your application. I suggest updating your question with some relevant code. In the meantime, here is what is working for me.
main.js
require.config({
appDir: '../',
baseUrl: 'js/',
paths: {
underscore: '../vendor/lodash.underscore',
backbone: '../vendor/backbone',
'jquery.fileupload': '../vendor/jquery-file-upload/js/jquery.fileupload',
'jquery.iframe-transport': '../vendor/jquery-file-upload/js/jquery.iframe-transport',
'jquery.ui.widget': '../vendor/jquery-file-upload/js/jquery.ui.widget'
},
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
And in the page that houses the upload control:
UploadPage.js
define([
'views/Base',
'jquery.iframe-transport',
'jquery.fileupload'
], function(BaseView) {
return BaseView.extend({
// Do cool stuff
});
});

Resources