Google Maps API complaining in IE11 about polyfill Array.from() - google-maps-api-3

I'm trying to figure out an issue with Google Maps v3 and a polyfill we use for non-ES6 browsers (IE11 for example). The error we get is:
This site overrides Array.from() with an implementation that doesn't support iterables, which could cause Google Maps JavaScript API v3 to not work correctly.
The polyfill is: ( from https://vanillajstoolkit.com/polyfills/arrayfrom/ )
if (!Array.from) {
Array.from = (function () {
var toStr = Object.prototype.toString;
var isCallable = function (fn) {
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
};
var toInteger = function (value) {
var number = Number(value);
if (isNaN(number)) { return 0; }
if (number === 0 || !isFinite(number)) { return number; }
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
};
var maxSafeInteger = Math.pow(2, 53) - 1;
var toLength = function (value) {
var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger);
};
// The length property of the from method is 1.
return function from(arrayLike/*, mapFn, thisArg */) {
// 1. Let C be the this value.
var C = this;
// 2. Let items be ToObject(arrayLike).
var items = Object(arrayLike);
// 3. ReturnIfAbrupt(items).
if (arrayLike == null) {
throw new TypeError('Array.from requires an array-like object - not null or undefined');
}
// 4. If mapfn is undefined, then let mapping be false.
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
var T;
if (typeof mapFn !== 'undefined') {
// 5. else
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
if (!isCallable(mapFn)) {
throw new TypeError('Array.from: when provided, the second argument must be a function');
}
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 2) {
T = arguments[2];
}
}
// 10. Let lenValue be Get(items, "length").
// 11. Let len be ToLength(lenValue).
var len = toLength(items.length);
// 13. If IsConstructor(C) is true, then
// 13. a. Let A be the result of calling the [[Construct]] internal method
// of C with an argument list containing the single item len.
// 14. a. Else, Let A be ArrayCreate(len).
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
// 16. Let k be 0.
var k = 0;
// 17. Repeat, while k < len… (also steps a - h)
var kValue;
while (k < len) {
kValue = items[k];
if (mapFn) {
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
} else {
A[k] = kValue;
}
k += 1;
}
// 18. Let putStatus be Put(A, "length", len, true).
A.length = len;
// 20. Return A.
return A;
};
}());
}
This works fine on other pages - but for some reason Google Maps seems to have an issue with it!
Even more frustratingly, is that it then breaks one of my other plugins (a lazy load script), which works fine until the Google map stuff is loaded
Any ideas on what its moaning about, and how to fix it?
If you have IE11 or a VM, you can test it at: https://www.chambresdhotes.org/Detailed/1768.html (click on the map at the bottom of the page, and this will load the Google Map - but then you get this annoying error, and it breaks the lazyload scrolling after)
Thanks!

Related

Vue.js - Update computed property after async computed property gets updated

I have a computed property (filteredSyms) that depends on the asynchronous computed property (allSynonyms). I am using async-computed plugin for this:
https://www.npmjs.com/package/vue-async-computed.
However, when the data gets updated the computed property doesn't wait until the result of the async property update. Therefore, I receive not up to date information. Then after the async property actually return new value computed property doesn't run update again.
How can I make it work the way that computer property waits until there is a result from the async computed property?
The code is below:
asyncComputed: {
async allSynonyms() {
let allSyns = await this.$axios.$post('/db/sym/synonyms', this.model.syms);
return allSyns;
}
},
computed: {
filteredSyms() {
let that = this;
let allSyn = this.allSynonyms;
let exactMatch = this.symsByRating.filter(
function (v) {
let isExactMatch = v.title.toLocaleLowerCase().indexOf(that.searchString.toLocaleLowerCase()) >= 0;
return !that.idsToFilter.includes(v.id) && isExactMatch
&& (!that.currentBodyPart || v.bodyParts.indexOf(that.currentBodyPart) >= 0)
&& that.hasMoreSubsyms(v)
&& (!allSyn || !that.containsObject(v, allSyn))
&& (v.sex == that.model.sex || v.sex == 'NA');
});
let partialList = [];
exactMatch.forEach(ex => partialList.push({n: 100, sym: ex}));
for (let sym of this.symsByRating ) {
let searchWords = this.searchString.toLocaleLowerCase().split(' ');
let symWords = sym.title.toLocaleLowerCase().split(' ');
let n = 0;
let isPartialMatch = false;
symLoop:for (let symWord of symWords) {
symWord = symWord.substring(0, symWord.length - 1);
for (let searchWord of searchWords) {
// don't count last letters of the words
searchWord = searchWord.substring(0, searchWord.length - 1);
if (searchWord.length > 2 && symWord.indexOf(searchWord) >= 0) {
n++;
isPartialMatch = true;
}
}
}
if (exactMatch.indexOf(sym) < 0 && isPartialMatch
&& (!this.currentBodyPart || sym.bodyParts.indexOf(this.currentBodyPart) >= 0)
&& this.hasMoreSubsyms(sym)
&& (!allSyn || !this.containsObject(sym, allSyn))
&& (sym.sex == that.model.sex || sym.sex == 'NA')) {
partialList.push({n: n, sym: sym});
}
}
partialList.sort(function(obj1, obj2) {
return obj2.n - obj1.n;
});
if (this.searchString && this.searchString != '') {
partialList = this.filterSynonyms(partialList);
}
let fs = partialList.map(ws => ws.sym);
console.dir(fs);
return fs;
}
}
A lot of stuff is going on the filtered method, but I guess the main point here that it is using this.allSynonyms to do the check but it is not updated at the time filteredSyms is executed.
Thanks for your suggestions!
(I haven't really tested this out, but it should work.)
vue-async-computed does provide the status in this.$asyncComputed.allSynonyms.success.
try adding this.$asyncComputed.allSynonyms.success as a dependencies to filteredSyms and it should update when success state change.

[Meteor][Reactive Autorun] How to log what caused autorun to run?

My problem is that some autoruns are fired many times, and i'd like to have a way to quickly check the source.
I wanted to know if something like that was possible :
let reactive_var_1 = new ReactiveVar();
let reactive_var_2 = new ReactiveVar();
let reactive_var_3 = new ReactiveVar();
let reactive_var_4 = new ReactiveVar();
Template.test.onCreated(function () {
console.log('Template.test.onCreated...');
this.autorun(function () {
console.log('Template.test.onCreated.autorun...');
console.log('autorun source is : ');
console.log(source);
let do_something_1 = reactive_var_1.get() + 1;
let do_something_2 = reactive_var_3.get() + 2;
let do_something_3 = reactive_var_3.get() + 3;
let do_something_4 = reactive_var_4.get() + 4;
});
});
Template.test.events({
'click .something': function () {
console.log('Someone clicked !!');
reactive_var_3.set(12);
}
});
Output console should look like this :
Template.test.onCreated...
Template.test.onCreated.autorun...
autorun source is :
undefined
Someone clicked !!
Template.test.onCreated.autorun...
autorun source is :
'reactive_var_3'
This is the source function i'm looking for, which could output my 'reactive_var_3'
Thanks for your help :)
You could introduce another cache variable that keeps track of the state of the other variables:
const _cache = {};
const vars = {
reactive_var_1 : new ReactiveVar();
reactive_var_2 : new ReactiveVar();
reactive_var_3 : new ReactiveVar();
reactive_var_4 : new ReactiveVar();
};
In each autorun you could check, which of the ractive variables does not match with the cache anymore. The following function provides such a feature:
function diff() {
const diffs = [];
Object.keys(vars).forEach( key => {
const reactiveVar = vars[key];
const value = reactiveVar.get();
// if value is different from state
// it has caused an autorun
if (_cache[key] !== value) {
diffs.push(key);
}
// update cache
_cache[key] = value;
});
return diffs;
}
In your autorun you can just call the diff methods and get the keys (names) of the variables that caused the tracker to run again:
Template.test.onCreated(function () {
console.log('Template.test.onCreated...');
this.autorun(function () {
console.log('Template.test.onCreated.autorun...');
console.log('autorun source is : ');
console.log(diff());
let do_something_1 = reactive_var_1.get() + 1;
let do_something_2 = reactive_var_3.get() + 2;
let do_something_3 = reactive_var_3.get() + 3;
let do_something_4 = reactive_var_4.get() + 4;
});
});
Note that the cache is immediately updated in the diff function. If this limits your use case, you may extract it into a separate function like updateCache or something.
Also note, that _cache exists outside the template, which can be a problem when using several instances of the same template. You may then switch to another approach and keep the _cache and vars as local variables within the to the template instance:
Template.test.onCreated(function () {
console.log('Template.test.onCreated...');
const _cache = //...
const vars = //...
function diff(){ /* ... */ }
this.autorun(function () {
console.log('Template.test.onCreated.autorun...');
console.log('autorun source is : ');
console.log(diff());
let do_something_1 = reactive_var_1.get() + 1;
let do_something_2 = reactive_var_3.get() + 2;
let do_something_3 = reactive_var_3.get() + 3;
let do_something_4 = reactive_var_4.get() + 4;
});
});

Puppeteer / Node - Target.createTarget - Target Closed

I'm using Node/Puppeteer in the code below, passing in a large list of URL's for traversal and scraping. It has been difficult to do it asynchronously, though I find that I am getting closer and closer to the answer. I am currently stuck on an issue related to the following error.
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 17): Error: Protocol error (Target.createTarget): Target closed.
This error occurs once upon every iteration of the while loop. Though I'm not sure what I may be doing incorrectly.
Could someone help me do the following:
1) Diagnose the source of the error.
2) Potentially find a more effective way to traverse a large list of URLs asynchronously.
async function subProc(list, batchSize) {
let subList = null;
let i = 0;
while (list.length > 0) {
let browser = await puppeteer.launch();
subList = list.splice(0, batchSize);
console.log("Master List Size :: " + list.length);
console.log("SubList Size :: " + subList.length);
for (let j = 0; j < subList.length; j++) {
promiseArray.push(new Promise((resolve, reject) => {
resolve(pageScrape(subList[j], browser));
}));
}
Promise.all(promiseArray)
.then(response => {
procArray.concat(response);
});
promiseArray = new Array();
try {
await browser.close();
} catch(ex){
console.log(ex);
}
};
}
async function pageScrape(url, browser) {
let page = await browser.newPage();
await page.goto(url, {
timeout: 0
});
await page.waitFor(1000);
return await page.evaluate(() => {
let appTitle = document.querySelector('').innerText;
let companyName = document.querySelector('').innerText;
let dateListed = document.evaluate("", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.innerText;
let category = document.evaluate("']//a//strong", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.innerText;
/* */
return {
appTitle,
companyName,
dateListed,
category
}
}).then(response => {
let urlData = {
id: subList[j],
appName: response.appTitle,
companyName: response.companyName,
dateListed: response.dateListed,
category: response.category
}
return urlData;
});
};
I figured out the solution to the problem I was having.
Every computer is limited in its processing ability, so instead of iterating through 1000 urls simultaneously you have to break it down into smaller pieces.
By using a PromiseAll, and iterating and scraping 10 urls at a time and storing these values in an array, I was able to throttle the processing required to iterate through all 1000 urls.
processBatch(subData, 10, procArray).then((processed)=>{
for(let i = 0; i < procArray.length; i++){
for(let j = 0; j < procArray[i].length; j++){
results.push(procArray[i][j]);
}
}
function processBatch(masterList, batchSize, procArray){
return Promise.all(masterList.splice(0, batchSize).map(async url =>
{
return singleScrape(url)
})).then((results) => {
if (masterList.length < batchSize) {
console.log('done');
procArray.push(results);
return procArray;
} else {
console.log('MasterList Size :: ' + masterList.length);
procArray.push(results);
return processBatch(masterList, batchSize, procArray);
}
})
}

Exception in template helper: TypeError: _.mapObject is not a function

I have facing the below issue while using underscorejs running on meteor.
"Exception in template helper: TypeError: _.mapObject is not a
function"
Please advise.
var types = _.groupBy(areaFlatten, 'category');
console.log(types);
var result = **_.mapObject**(types, function(val, key) {
return _.reduce(val, function(memo, v) {
return memo + v.val;
}, 0) / val.length * 10;
I think you are using an older version of Underscore. _.mapObject was added in v1.8.0 (http://underscorejs.org/#changelog)
Alternative without using _.mapObject:
var types = _.groupBy(areaFlatten, 'category');
console.log(types);
var result = {};
_.each(types, function(val, key) {
result[key] = _.reduce(val, function(memo, v) {
return memo + v.val;
}, 0) / val.length * 10;
});
If you are going to use this functionality regularly, you could add a mixin to make the function available until you get a chance to upgrade, see here https://jsfiddle.net/Lradh7jd/1/
_.mixin({
mapObject: function(obj, iteratee, context) {
var output = {};
_.each(obj, function(v, k) {
output[k] = iteratee.apply(context || this, arguments);
});
return output;
}
});

MeteorJS: CALLBACKS

PROBLEM: I want to parse the elements in a page from another website, glue resulting elements in an object and insert it in a Mongo collection. Before insertion i want to check if my Mongo yet has an identical object. If it does it shall exit the running functions, otherwise i want the script to start parsing the next target.
Example:
I have a function that connects to a webpage and returns its body content
It is parsed
When <a></a> elements are met, another callback is called in which all parsed elements are merged in one object and inserted in a collection
My code :
var Cheerio = Meteor.npmRequire('cheerio');
var lastUrl;
var exit = false;
Meteor.methods({
parsing:function(){
this.unblock();
request("https://example.com/", Meteor.bindEnvironment(function(error, response, body) {
if (!error && response.statusCode == 200) {
$ = Cheerio.load(body);
var k = 1;
$("div.content").each(function() {
var name = $...//parsing
var age = $....//parsing
var url = $...//parsing <a></a> elements
var r = request("https://example.com/"+url, Meteor.bindEnvironment(function(error, response, body) {
lastUrl = response.request.uri.href;// get the last routing link
var metadata = {
name: name,
age: age
url: lastUrl
};
var postExist;
postExist = Posts.findOne(metadata); // return undefined if doesnt exist, AND every time postExist = undefined ??
if (!postExist){
Posts.insert(metadata);// if post doesnt exist (every time go here ??)
}
else {
exit = true; // if exist
}
}));
if (exit === true) return false;
});
}
}));
}
});
Problem 1 : The problem is my function works every time, but it doesn't stop even if the object exists in my collection
Problem 2 : postExist is always undefined
EDIT : The execution must stop and wait until the second request's response.
var url = $...//parsing <a></a> elements
//STOP HERE AND WAIT !!
var r = request("https://example.com/"+url, Meteor.bindEnvironment(function(error, response, body) {
Looks like you want the second request to be synchronous and not asynchronous.
To achieve this, use a future
var Cheerio = Meteor.npmRequire('cheerio');
var Future = Meteor.npmRequire('fibers/future');
var lastUrl;
var exit = false;
Meteor.methods({
parsing:function(){
this.unblock();
request("https://example.com/", Meteor.bindEnvironment(function(error, response, body) {
if (!error && response.statusCode == 200) {
$ = Cheerio.load(body);
var k = 1;
$("div.content").each(function() {
var name = $...//parsing
var age = $....//parsing
var url = $...//parsing <a></a> elements
var fut = new Future();
var r = request("https://example.com/"+url, Meteor.bindEnvironment(function(error, response, body) {
lastUrl = response.request.uri.href;// get the last routing link
var metadata = {
name: name,
age: age
url: lastUrl
};
var postExist;
postExist = Posts.findOne(metadata); // return undefined if doesnt exist
if (!postExist) {
Posts.insert(metadata);// if post doesnt exist (every time go here ??)
fut.return(true);
} else {
fut.return(false);
}
}));
var status = fut.wait();
return status;
});
}
}));
}
});
You can use futures whenever you can't utilize callback functions (e.g. you want the user to wait on the result of a callback before presenting info).
Hopefully that helps,
Elliott
This is the opposite :
postExist = Posts.findOne(metadata); // return undefined if doesnt exist > you're right
if (!postExist){ //=if NOT undefined = if it EXISTS !
Posts.insert(metadata);
}else {
exit = true; // if undefined > if it DOES NOT EXIST !
}
You need to inverse the condition or the code inside

Resources