Dynamic about: URLs using JavaScript Code Modules (XPCOMUtils.jsm)? - javascript

I'm trying to dynamically register custom about: URLs for a Firefox extension, relative to a variable.
Example:
- If var = 1, then create... about:123
- If var = 2, then create... about:abc
- If var = 3, then create... about:xxx
JavaScript code modules was suggested to me, specifically XPCOMUtils.jsm, but how would one go about implementing this in code?
I have gone through these pages to no avail:
https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules
https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/XPCOMUtils.jsm
I have already implemented a custom static about: URL using the chrome.manifest file. But I don't need static about: URLs. I need dynamic about: URLs, relative to a variable inside the Firefox extension.
Thanks for your help!

I wrote this addon-sdk module called, about-what, which will allow you to dynamically add/remove about:what uris to firefox.
This is the current source code for about-what:
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
const { Cr, Cu, Ci, Cc, Cm } = require('chrome');
const { when: unload } = require('sdk/system/unload');
const { validateOptions : validate } = require('sdk/deprecated/api-utils');
const { uuid } = require('sdk/util/uuid');
const { URL, isValidURI } = require('sdk/url');
const tabs = require('sdk/tabs');
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const validOptions = {
what: {
is: ['string'],
ok: function(what) {
if (what.match(/^[a-z0-9-]+$/i))
return true;
return false;
},
map: function(url) url.toLowerCase()
},
url: {
is: ['string', 'undefined']
},
content: {
is: ['string', 'undefined']
},
useChrome: {
is: ['undefined', 'null', 'boolean'],
map: function(use) !!use
}
};
function add(options) {
let { what, url, content, useChrome } = validate(options, validOptions);
let baseURL = url;
if (content) {
url = encodeURI('data:text/html;charset=utf-8,' + content.replace(/\{\s*page\.baseurl\s*\}/, baseURL));
}
let classID = uuid();
let aboutModule = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
newChannel: function (aURI) {
let chan = Services.io.newChannel(url, null, null);
if (useChrome)
chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
return chan;
},
getURIFlags: function () Ci.nsIAboutModule.ALLOW_SCRIPT
};
let factory = {
createInstance: function(aOuter, aIID) {
if (aOuter)
throw Cr.NS_ERROR_NO_AGGREGATION;
return aboutModule.QueryInterface(aIID);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
};
// register about:what
Cm.QueryInterface(Ci.nsIComponentRegistrar).
registerFactory(classID, '', '#mozilla.org/network/protocol/about;1?what='+what, factory);
let remover = unloader.bind(null, what, factory, classID);
unload(remover);
return undefined;
}
exports.add = add;
function unloader(what, factory, classID) {
// unregister about:what
Cm.QueryInterface(Ci.nsIComponentRegistrar).unregisterFactory(classID, factory);
let regEx = new RegExp('^' + what, 'i');
// AMO policy, see http://maglione-k.users.sourceforge.net/bootstrapped.xhtml
// close about:what tabs
for each (let tab in tabs) {
let url = URL(tab.url);
if (url.scheme === 'about' && url.path.match(regEx)) {
tab.close();
}
}
}

Related

node.js edit variable from another file

Hello i have just started to learn node.js today and i am having a problem getting a variable from another file and editing it.
I'm trying to get the variable bots from server.js and edit it in bots.js.
server.js:
var bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(bots); }, 5000);
module.exports.bots = bots;
bots.js:
var request = require("../server.js");
var unique_id = '16f11103';
if (request.bots.some(e => e.botID === unique_id)) {
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
}
One way of doing this is to create a function that sets the variable bots.
Note: Instead of using seperate variables and functions i suggest to use something like a class to group all the functionality surrounding the bots.
server.js
var bots = [
{ botID: "16f11103", userID: "12345" },
{ botID: "5657d5e9", userID: "54321" },
];
var setBots = (newBots) => (bots = newBots);
setInterval(() => {
console.log(bots);
}, 5000);
module.exports = { bots, setBots };
bots.js
var { bots, setBots } = require("./server.js");
var unique_id = "16f11103";
if (bots.some((e) => e.botID === unique_id)) {
var newBots = bots.filter(function (e) {
return e.botID != unique_id;
});
setBots(newBots);
}
It's not working because of how variables are modified in JavaScript (Think copy by reference vs value).
When you first export the bots variable in server.js:
module.exports.bots = bots;
module.exports.bots is pointing to the bots variable by reference. If you modify the array directly like module.exports.bots[0] = 1 it will be reflected in the original bots variable as well since the reference hasn't changed.
However, what you're doing is reassigning the module.exports.bots value completely. The Array.prototype.filter() returns a new array without modifying the original.
When you do:
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
You're making it so that the module.exports.bots variable points to a new array and no longer points to the original bots array. That's why your console.log call is always printing the same array because the original bots variable is still pointing to that array and it was never modified.
If you don't want to modify the array directly you can try this.
server.js
var request = {};
request.bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(request.bots); }, 5000);
module.exports = request;
bots.js can now be kept exactly the same
var request = require("../server.js");
var unique_id = '16f11103';
if (request.bots.some(e => e.botID === unique_id)) {
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
}
You should do
var bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(bots); }, 5000);
module.exports = bots;
instead of
module.exports.bots = bots;
You can also do
exports.bots = bots;
Then use Like this
var bots = require("./server.js");
var unique_id = '16f11103';
if (bots.some(e => e.botID === unique_id)) {
bots = bots.filter(function(e) {
return e.botID != unique_id;
});
}

Matching a string to one of many patterns and extracting data

I have a problem I want to solve with RegEx, or any other method if there is a better one. I've tried several ways to achieve the goal, but nothing really worked.
I have an array with endpoints:
const endpoints = [
{
id: 1,
url: "/api/items/:itemId"
},
{
id: 2,
url: "/api/users/:userName/delete"
},
{
id: 3,
url: "/api/users/:userName/edit"
}
];
And a request URL:
const url = "/api/users/max/edit";
Now what I want is to have a function which acts like this:
const rewrite = (url, endpoints) => {
// What is the best way to achieve the following return value:
return {
endpointId: 3,
values: {
userName: "max"
}
};
};
Explanation: The function should find the appropriate endpoint for the url. All parts of the endpoint url which start with a colon are not static, but should rather be replaced with values from the request url. In this case :userName should be replaced with max.
I've been in web development for some time now, but to be honest I've almost no clue how to solve such a problem.
const rewrite = (url, endpoints) => {
var doubledArray = Array.prototype.map.call(endpoints, function(el) {
return {
id: el.id,
url: el.url.split('/')
};
});
var parts = url.split('/');
var i = 0;
parts.forEach(function(element) {
doubledArray = doubledArray.filter(el => (element == el.url[i] || el.url[i].startsWith(':')));
i++;
});
return {
endpointId: doubledArray[0].id,
values: {
[`${doubledArray[0].url.filter(el => el.startsWith(':'))[0].substring(1)}`]: parts[doubledArray[0].url.findIndex(function (el) { return el.startsWith(':'); } )],
}
};
};
You can go through the endpoints making each .url into a RegExp to test the url against.
When a matching one is found, it is just a matter of extracting the needed part and making up an Object with the property name:
<script>
const myEndpoints = [
{
id: 1,
url: "/api/items/:itemId"
},
{
id: 2,
url: "/api/users/:userName/delete"
},
{
id: 3,
url: "/api/users/:userName/edit"
}
];
const myUrl = "/api/users/nermal/edit";
const rewrite = (url, endpoints) => {
for (let i = 0; i < endpoints.length; i++) {
var rep = new RegExp(":(\\w+)", "m");
var propName = rep.exec(endpoints[i].url);
var reu = new RegExp(endpoints[i].url.replace(propName[0], "(.*)"));
var a = reu.exec(url);
if (a !== null) {
var x = new Object;
x["endpointId"] = endpoints[i].id;
var y = new Object;
y[propName[1]] = a[1];
x["values"] = y;
return x;
}
}
return null;
};
var q = rewrite(myUrl, myEndpoints);
console.log(q);
console.log(q.values);
</script>
Outputs:
Object { endpointId: 3, values: {…} }
Object { userName: "nermal" }

gulp-minify is replacing javascript variable name with there value in function call

I have code like:
define('identity', function () {
// LOT OF CONSTANTS AND USAGE HAS BEEN REMOVED FOR BREVITY
'use strict';
var CREATE_ACCOUNT = 'CreateAccount';
var ACCOUNT = 'Account';
var CONNECT = 'Connect';
var SWITCH = 'Switch';
var FB = 'FB';
var PIPE_SEPARATOR = ' | ';
var DOT_SEPARATOR = '.';
var COMMA_SEPARATOR = ', ';
function concatenateWithDotSeparater(array) {
return concatenateWithSeparator(array,DOT_SEPARATOR);
}
function concatenateWithPipeSeparater(array) {
return concatenateWithSeparator(array,PIPE_SEPARATOR);
}
function concatenateWithCommaSeparater(array) {
return concatenateWithSeparator(array, COMMA_SEPARATOR);
}
function concatenateWithSeparator(array, separator) {
return array.join(separator);
}
return {
signUp: {
facebookConnect : concatenateWithDotSeparater([CREATE_ACCOUNT, FB, CONNECT]),
}
};
});
Essentially, I had lots of constants which were repeating and need to be concatenated to produce actual value. So, I created constants to hold repeating values and then concatenated them via function.
But when I tried to minify the JS using gulp-minify version 1.0.0 the result was like:
return{signUp:{facebookConnect:e(["CreateAccount","FB","Connect"])}}
It injected actual values. How can I prevent this?
I like to get an output like:
return{signUp:{facebookConnect:e([s,d,e])}}
I am using minify like:
.pipe(minify({
ext: {
src: '.js',
min: '-min.js'
}
}))
Please help.
You can avoid renaming some variables if you set option mangle where you should to set list of names you want to save unchangeble. Under the hood the gulp-minify packet uses the uglify-es packet. Look at it for detailed documentation.
Please try with this options:
.pipe(minify({
ext: {
src: '.js',
min: '-min.js'
},
mangle: { reserved: ['CREATE_ACCOUNT', 'ACCOUNT', 'CONNECT'] // etc
}))

Object properties are undefined, the object itself shows all the data though

I'm building an Single Page application for a minor project. I've been trying to save data from API call's to the Movie Database in an object. If i console.log the object, I can see all its properties and values. If I console.log the object.property, it returns 'undefined'. This is the code:
(() => {
"use strict"
/* Saving sections to variables
--------------------------------------------------------------*/
const movieList = document.getElementsByClassName('movie_list')[0];
const movieSingle = document.getElementsByClassName('movie_single')[0];
/* All standard filters for displaying movies
--------------------------------------------------------------*/
const allFilters = {
trending: 'movie/popular',
toplist: 'movie/top_rated',
latest: 'movie/now_playing',
upcoming: 'movie/upcoming'
};
const allData = {};
/* Initialize app - Get al standard data and save it in object
--------------------------------------------------------------*/
const app = {
init() {
getData(allFilters.trending, 'popular');
getData(allFilters.toplist, 'toplist');
getData(allFilters.latest, 'latest');
getData(allFilters.upcoming, 'upcoming');
this.startPage();
},
startPage() {
window.location.hash = "trending";
}
}
/* Function for getting data from the API
--------------------------------------------------------------*/
const getData = (filter, key) => {
const request = new XMLHttpRequest();
const apiKey = '?api_key=xxx';
const getUrl = `https://api.themoviedb.org/3/${filter}${apiKey}`;
request.open('GET', getUrl, true);
request.onload = () => {
if (request.status >= 200 && request.status < 400) {
let data = JSON.parse(request.responseText);
data.filter = key;
cleanData.init(data);
} else {
window.location.hash = 'random';
}
};
request.onerror = () => {
console.error('Error');
};
request.send();
};
/* Check if the data is list or single, and clean up
--------------------------------------------------------------*/
const cleanData = {
init(originalData) {
if (!originalData.results) {
this.single(originalData);
} else {
allData[originalData.filter] = originalData;
}
},
list(data) {
data.results.map(function(el) {
el.backdrop_path = `https://image.tmdb.org/t/p/w500/${el.backdrop_path}`;
});
let attributes = {
movie_image: {
src: function() {
return this.backdrop_path;
},
alt: function() {
return this.title;
}
},
title_url: {
href: function() {
return `#movie/${this.id}/${this.title}`;
}
}
}
showList(data.results, attributes);
},
single(data) {
data.poster_path = `https://image.tmdb.org/t/p/w500/${data.poster_path}`;
data.budget = formatCurrency(data.budget);
data.revenue = formatCurrency(data.revenue);
data.runtime = `${(data.runtime / 60).toFixed(1)} uur`;
data.imdb_id = `http://www.imdb.com/title/${data.imdb_id}`;
let attributes = {
movie_image: {
src: function() {
return this.poster_path;
},
alt: function() {
return this.title;
}
},
imdb_url: {
href: function() {
return this.imdb_id
}
},
similar_url: {
href: function() {
return `#movie/${this.id}/${this.title}/similar`
}
}
};
showSingle(data, attributes);
}
};
const showList = (cleanedData, attributes) => {
movieList.classList.remove('hidden');
movieSingle.classList.add('hidden');
Transparency.render(movieList, cleanedData, attributes);
};
const showSingle = (cleanedData, attributes) => {
movieSingle.classList.remove('hidden');
movieList.classList.add('hidden');
Transparency.render(movieSingle, cleanedData, attributes);
}
const formatCurrency = amount => {
amount = amount.toFixed(0).replace(/./g, function(c, i, a) {
return i && c !== "." && ((a.length - i) % 3 === 0) ? '.' + c : c;
});
return `€${amount},-`;
};
app.init();
console.log(allData); // Returns object with 4 properties: trending, toplist, latest & upcoming. Each property is filled with 20 results (movies with data) from the API.
console.log(allData.trending) // Returns 'undefined' (each property I've tried).
console.log(allData['trending']) // Returns 'undefined'
Object.keys(allData); // Returns an empty Array []
})();
When I use console.log(allData) I see 4 properties, all filled with the results from the API. But when i do console.log(allData.trending) or console.log(allData['trending']) it returns 'Undefined' in the console. Has anyone an idea how to fix this?
When you call app.init() it fires the init and sends the api call(s) to fetch data.
The call to fetch data is Asynchronous, which means it doesn't wait for the response to continue execution. So it goes ahead and executes next lines of code, which are your console.logs. At this time the API calls haven't responded with the data, so when you try to access data.property it fails as the data is not yet here.
When you do log(data) it creates a log of the reference to data, which gets updated in the log when the reference is filled with a value later. To get the value of data at that instance and prevent updating later you can try log(JSON.stringify(data)). When you do that you get a consistent result, none of your logs work, which is the actual behaviour.
To get your logs work, look into the success/load callback of you A(synchronous)JAX request, log it from there. Or if you constructed allData later in cleanData then call the logs after cleanData function.
So to answer your question, none of your logs should work as it is an asynchronous call. You are getting the allData logged due to the way console.log works with references that are updated later, use console.log(JSON.stringify(allData)) to get the actual snapshot of Object

Ember.js deprecation of registerImplementation in favour of App.initializer

I am using an extension of HashLocation to implement a hashbang url type for Ember.js.
Here is the code snippet:
(function() {
var get = Ember.get, set = Ember.set;
Ember.Location.registerImplementation('hashbang', Ember.HashLocation.extend({
getURL: function() {
return get(this, 'location').hash.substr(2);
},
setURL: function(path) {
get(this, 'location').hash = "!"+path;
set(this, 'lastSetURL', "!"+path);
},
onUpdateURL: function(callback) {
var self = this;
var guid = Ember.guidFor(this);
Ember.$(window).bind('hashchange.ember-location-'+guid, function() {
Ember.run(function() {
var path = location.hash.substr(2);
if (get(self, 'lastSetURL') === path) { return; }
set(self, 'lastSetURL', null);
callback(location.hash.substr(2));
});
});
},
formatURL: function(url) {
return '#!'+url;
}
}));
})();
I use this by reopening the Router:
App.Router.reopen({
location: 'hashbang'
});
However, on running the application, i'm hitting the following deprecation:
DEPRECATION: Using the Ember.Location.registerImplementation is no longer supported. Register your custom location implementation with the container instead.
I can't find any information on how to do this. Does anyone have any implementation snippets on what I would have to do?
According to deprecation message. use container instead.
(function() {
var get = Ember.get, set = Ember.set;
var hashbangLocation = Ember.HashLocation.extend({
getURL: function() {
return get(this, 'location').hash.substr(2);
},
setURL: function(path) {
get(this, 'location').hash = "!"+path;
set(this, 'lastSetURL', "!"+path);
},
onUpdateURL: function(callback) {
var self = this;
var guid = Ember.guidFor(this);
Ember.$(window).bind('hashchange.ember-location-'+guid, function() {
Ember.run(function() {
var path = location.hash.substr(2);
if (get(self, 'lastSetURL') === path) { return; }
set(self, 'lastSetURL', null);
callback(location.hash.substr(2));
});
});
},
formatURL: function(url) {
return '#!'+url;
}
});
App.register('location:hashbang', hashbangLocation);
})();
Reopen it as usual
App.Router.reopen({
location: 'hashbang'
});

Categories

Resources