Jsdom not save generated html after javascript proccessing - javascript

I have html with tables and in html I included js script bom.js:
$(document).ready(function() {
setTimeout(function() {
var nums = [];
$('#table_Serv tr td.serv-nomer').each(function (elem, ind) {
nums[parseInt($(this).text())] = elem + 1;
// nums.newNum[elem] = elem + 1;
// nums.oldNum[elem] = parseInt($(this).text());
$(this).text(elem + 1);
});
$('#test1234 tr td:nth-child(2)').each(function() {
// extract each number in an array
const numbers = $(this).html().split(',').map(x => Number(x));
// var sortednum = [];
var arrs = []
for(var i = 0; i <= numbers.length; i++) {
arrs[numbers[i]] = nums[numbers[i]];
}
// console.log(sortednum);
// // Sort the numbers
const sorted = arrs.sort((a, b) => a > b);
;
const stringArray = sorted.reduce((tmp, x) => `${tmp},${x}`);
// Insert the string back in the td
$(this).text(stringArray);
});
}, 1000);
});
When I go to html page, script working and get me table with changes by javascript. When jsdom get this html, jsdom is not executing javascript and get me clean html.
My index.js:
'use strict';
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
var fs = require('fs');
const options = {
resources: 'usable',
runScripts: 'dangerously',
};
var url = process.argv[2] !== undefined ? process.argv[2] : 'http://google.ru';
JSDOM.fromURL(url, options).then(dom => {
setTimeout(function() {
var cont = dom.window.document.getElementsByTagName('html')[0].innerHTML;
console.log(cont);
var file = url.split('/');
var filename = file[file.length - 1];
fs.writeFile("file.html" + filename, cont, function(err) {
if(err) {
return console.log(err);
}
// console.log("The file was saved!");
});
}, 1000)
});
How I can resolve this solutions? I need save html after javascript processing. Get html by javascript. How I can do it?
I tryied this:
JSDOM.fromURL(url, options).then(dom => {
dom.window.onload = function() {
var cont = dom.window.document.getElementsByTagName('html').[0].innerHTML;
console.log(cont);
var file = url.split('/');
var filename = file[file.length - 1];
fs.writeFile("file.html" + filename, cont, function(err) {
if(err) {
return console.log(err);
}
// console.log("The file was saved!");
});
};
});
Not working :( window.onload method is not want working..

Related

Function B returns null when called from function A, but returns correct value when logged within function B

please refer to the code below:
function getGdriveLinks() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('Test');
const fileNames = sheet.getRange('C2:C').getValues();
const mainFolder = DriveApp.getFolderById('Folder ID');
for (var i = 0; i < fileNames.filter(String).length; i++){
var fileName = fileNames[i][0].toString();
var fileLink = getGdriveLink(fileName,mainFolder);
Logger.log(fileLink);
}
}
function getGdriveLink(searchFileName,folder){
var mainFiles = folder.getFiles();
while (mainFiles.hasNext()){
var file = mainFiles.next();
if(file.getName().indexOf(searchFileName) != -1){
Logger.log(file.getUrl());
return file.getUrl();
}
}
var subFolders = folder.getFolders();
while (subFolders.hasNext()){
var subFolder = subFolders.next();
getGdriveLink(searchFileName,subFolder);
}
}
Basically, the aim is to get the filenames in C2:C of Test sheet, find each of these in the 'Folder ID', and then return their URL. The URL is returned correctly when I log it from getGdriveLink(searchFileName,folder) using Logger.log(file.getUrl()). However, it returns null via the Logger.log(fileLink) when the result goes back to getGdriveLinks() function. Is there anything I'm missing? We're using a corporate domain if that's something to consider. Hoping for your guidance. Thanks!
You could also do it this way and avoid recursion altogether
function getPathAllDrivesFromId(fileid) {
var ids = [{id:fileid,name:DriveApp.getFileById(fileid).getName()}];
let r;
do {
r = Drive.Files.get(fileid,{supportsAllDrives:true,supportsTeamDrives:true});
if(r.parents.length > 0) {
//Logger.log(JSON.stringify(r.parents[0]));
ids.push({id:r.parents[0].id,name:DriveApp.getFolderById(r.parents[0].id).getName()});
fileid = r.parents[0].id
}
}while (r.parents.length > 0);
if(ids[ids.length - 1].name == "Drive") {
ids[ids.length - 1].name = Drive.Drives.get(ids[ids.length - 1].id).name;
}
//Logger.log(JSON.stringify(ids));
let path = ids.map(obj => obj.name).flat().reverse().join(' / ')
//Logger.log(path);
return path;
}
function getGdriveLinks() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Test');
const osh = ss.getSheetByName("Results");
const ns = sh.getRange('C2:C' + sh.getLastRow()).getValues();
const folder = DriveApp.getFolderById('Folder ID');
const result = [["Filename", "Id", "Url", "path"]];
ns.forEach(n => {
let files = DriveApp.getFilesByName(n);
while (files.hasNext()) {
let file = files.next();
let path = getPathAllDrivesFromId(file.getId());
if (~path.indexOf(folder.getName())) {
result.push([file.getName(), file.getId(), file.getUrl(), path]);
}
}
})
osh.clearContents();
osh.getRange(1, 1, result.length, result[0].length).setValues(result);
}

How is it possible two following lines "see" an array differently?

async postList(arr){
console.log(arr);
console.log(arr[0]);
Debugger + console picture: https://prnt.sc/23q4jra
as seen in the picture of the debugger console.log(arr) returns an array with a value in the [0] position, the very next line arr[0] returns "undefined" and arr.length returns "0"
how is it possible?
the function that calls this function:
async mountedCall(){
var composedArray = await this.createList();
document.getElementById('listWrap_players').appendChild(await this.postList(composedArray));
},
createList():
async createList(){
var composedArray = [];
const id = document.getElementById('joinCode').innerHTML;
var player_count = null;
await firebase.database().ref('lobbies/' + id + '/playerCount/').once('value', (snapshot) => {
const data = snapshot.val();
player_count = data;
}).then(function() {
for(var i = 1; i <= player_count; i++){
var iStr = String(i);
const player_names_snapshot = firebase.database().ref('lobbies/' + id + '/players/' + iStr);
player_names_snapshot.once('value', (snapshot) => {
const data = snapshot.val();
composedArray.push(data);
}).then(function(){return;});
}
});
this.isLeader(id);
return composedArray;
},
UPDATE:
Tried to replace console.log with console.log(JSON.stringify(arr)) as suggested below
console.log(JSON.stringify(arr)) returns an empty array so I think it means I have synchronization problem in createList() or in mountedCall(), yet I cant seem to find it. I've used await and .then() in every location possible...
here are all the functions together:
async mountedCall(){
var composedArray = await this.createList();
document.getElementById('listWrap_players').appendChild(await this.postList(composedArray));
},
async removeAllChildNodes(list) {
while(list.firstChild){
list.removeChild(list.firstChild);
}
},
async postList(arr){
console.log(JSON.stringify(arr));
console.log(arr[0]);
var list = document.createElement('ul');
for(let i = 0; i < arr.length; i++){
var item = document.createElement('li');
item.appendChild(document.createTextNode(arr[i]));
list.appendChild(item);
}
const listContainer = document.getElementById('listWrap_players');
this.removeAllChildNodes(listContainer);
return list;
},
async createList(){
var composedArray = [];
const id = document.getElementById('joinCode').innerHTML;
var player_count = null;
await firebase.database().ref('lobbies/' + id + '/playerCount/').once('value', (snapshot) => {
const data = snapshot.val();
player_count = data;
}).then(function() {
for(var i = 1; i <= player_count; i++){
var iStr = String(i);
const player_names_snapshot = firebase.database().ref('lobbies/' + id + '/players/' + iStr);
player_names_snapshot.once('value', (snapshot) => {
const data = snapshot.val();
composedArray.push(data);
}).then(function(){return;});
}
});
return composedArray;
},

Parse Cloud Code Promises in a for loop

As the title states, I'm having trouble with Promises in Parse.
I'm struggling to firstly understand exactly how Promises themselves work, especially in Parse.
I have been stuck on this for about three weeks and the closest I've come to a solution is having an empty array returned.
What I'm trying to do is scrape a site and then create objects from the table (this is working)
Where there trouble comes in, is I am then running a for loop on the results and querying each Dam name to get the resulting objectid from the database.
Here is my code:
var c = new Crawler({
maxConnections: 10,
// This will be called for each crawled page
callback: function(err, res, done) {
if (err) {
console.log(err);
} else {
var $ = res.$;
// $ is Cheerio by default
//a lean implementation of core jQuery designed specifically for the server
console.log($("title").text());
}
done();
}
});
The Function which Creates objects from the Dom and adds them to an array:
function getDamObjects(Dom) {
var dom = Dom;
var LevelObjects = [];
for (i = 1; i < dom.length - 1; i++) {
var TableRow = dom.eq(i);
var NameString = TableRow.children().eq(0).text();
var RiverString = TableRow.children().eq(1).text();
var FSCString = TableRow.children().eq(4).text();
var ThisWeekString = TableRow.children().eq(5).text();
var LastWeekString = TableRow.children().eq(6).text();
var LastYearString = TableRow.children().eq(7).text();
NameString = NameString.replace('#', '');
NameString = NameString.replace('$', '');
NameString = NameString.replace('&', '');
NameString = NameString.replace('#', '');
ThisWeekString = ThisWeekString.replace('#', '');
ThisWeekString = ThisWeekString.replace('$', '');
ThisWeekString = ThisWeekString.replace('&', '');
ThisWeekString = ThisWeekString.replace('#', '');
LastWeekString = LastWeekString.replace('#', '');
LastWeekString = LastWeekString.replace('$', '');
LastWeekString = LastWeekString.replace('&', '');
LastWeekString = LastWeekString.replace('#', '');
LastYearString = LastYearString.replace('#', '');
LastYearString = LastYearString.replace('$', '');
LastYearString = LastYearString.replace('&', '');
LastYearString = LastYearString.replace('#', '');
var level = {};
/*
getDamObject(NameString).then(function(DamObject){
let DamID = DamObject.id;
*/
level['Dam'] = NameString; //DamID;
level['ThisWeek'] = ThisWeekString;
level['LastWeek'] = LastWeekString;
level['LastYear'] = LastYearString;
LevelObjects.push(level);
};
return LevelObjects;
};
The Get Dam Object Code:
function getDamObject(Dam) {
var promise = new Parse.Promise();
var query = new Parse.Query("DayZeroDams");
query.equalTo("Name", Dam);
query.first().then(function(DamObject) {
promise.resolve(DamObject);
}, function(error) {
promise.reject(error);
});
return promise;
}
The Cloud Code Called:
Parse.Cloud.define('jsdom', function(request, response) {
c.queue([{
uri: 'xxxxxx',
// The global callback won't be called
callback: function(err, res, done) {
if (err) {
response.error(err);
} else {
var $ = res.$;
var ResultsArray = [];
var dom = res.$('#mainContent_tw').children('tr');
return Parse.Promise.as().then(function() {
var promise = Parse.Promise.as();
var LevelObjects = getDamObjects(dom);
_.each(LevelObjects, function(DamLevel) {
promise = promise.then(function() {
var Name = DamLevel["Dam"];
var query = new Parse.Query("DayZeroDams");
query.equalTo("Name", Name);
return query.first().then(function(result) {
let damID = result.id;
ResultsArray.push(damID);
return Parse.Promise.as();
}, function(error) {
response.error(error);
});
});
});
return promise;
}).then(function() {
response.success(ResultsArray);
}, function(error) {
response.error(error);
});
//response.success(LevelObjects);
}
done();
}
}]);
});
Please take note that I am fairly novice when it comes to Javascript, I have only recently started learning it in order to work with my server code.
Convert getDamObjects into an async function and then await the result of each row, pushing it to the array:
function replaceSymbols(input) {
return input.replace(/[#\$&#]/g, '');
}
async function getDamObjects(Dom) {
const dom = Dom;
const levelObjects = [];
for (let i = 1; i < dom.length - 1; i++) {
const children = dom.eq(i).children();
const NameString = replaceSymbols(children.eq(0).text());
const RiverString = children.eq(1).text();
const FSCString = children.eq(4).text();
const ThisWeek = replaceSymbols(children.eq(5).text());
const LastWeek = replaceSymbols(children.eq(6).text());
const LastYear = replaceSymbols(children.eq(7).text());
const Dam = await getDamObject(NameString);
levelObjects.push({
Dam,
ThisWeek,
LastWeek,
LastYear,
});
}
return levelObjects;
}
Remember that now that getDamObjects is an async function, it will return a Promise that resolves to the array once iterations are complete. Consume it using await getDamObjects in another async function (or use .then)

Scrape part of web page html and test it against condition

This is part of the html code from "view page source" I want to scrape
<span>Loyalty cijena</span>
<strong>863,84 KN</strong>
This is my code to scrape part which is now "863" and start my function if that number is under 863. But now the function start no matter condition is set [0-862] or [0-864]. Where is my mistake?
function createBackup() {
var folder = getFolder(FOLDER_NAME);
var exportUrl = RESOURCE_URL;
var response = UrlFetchApp.fetch(exportUrl);
var htmlBody = response.getContentText();
var scraped = htmlBody.match(/Loyalty cijena<\/span>\s*<strong>(\d+),(\d+) KN<\/strong>/m)[1];
if(scraped.match(/[0-862]/)); {
createBackupFile(folder, FILE_NAME, fetchData());
}
}
Edit: This is full code
var RESOURCE_URL = 'https://www...',
BACKUP_FOLDER_ID = 'xxx',
FOLDER_NAME_FORMAT = 'yyyy-MM-dd',
FILE_NAME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss",
FILE_EXT = '.pdf',
now = new Date(),
FOLDER_NAME = Utilities.formatDate(now, 'CET', FOLDER_NAME_FORMAT),
FILE_NAME = Utilities.formatDate(now, 'CET', FILE_NAME_FORMAT) + FILE_EXT;
function createBackup() {
var folder = getFolder(FOLDER_NAME);
var exportUrl = RESOURCE_URL;
var response = UrlFetchApp.fetch(exportUrl);
var htmlBody = response.getContentText();
var scraped = htmlBody.match(/Loyalty cijena<\/span>\s*<strong>(\d+),(\d+) KN<\/strong>/m)[1];
console.log(scraped,parseInt(scraped) > 0 && parseInt(scraped) < 863); {
createBackupFile(folder, FILE_NAME, fetchData());
}
}
function getFolder(name) {
var backupFolder = getBackupFolder(),
folders = backupFolder.getFoldersByName(name);
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = backupFolder.createFolder(name);
}
return folder;
}
function getBackupFolder() {
return DriveApp.getFolderById(BACKUP_FOLDER_ID);
}
function createBackupFile(folder, filename, data, overwrite) {
if (overwrite) {
var existingFiles = folder.getFilesByName(filename);
while (existingFiles.hasNext()) {
var file = existingFiles.next();
folder.removeFile(file);
}
}
}
function fetchData() {
var exportUrl = RESOURCE_URL;
var response = UrlFetchApp.fetch(exportUrl);
var htmlBody = response.getContentText();
var blob = Utilities.newBlob(htmlBody, 'text/html').getAs('application/pdf').setName(FILE_NAME);
return folder.createFile(blob);
}
The pattern [0-862] matches text that ends with 62 and where the first symbol is 0 to 8, so for example 562 but not 561. In your case you should compare the number represented by those characters, instead of matching against a regular expression.
Don't use regex to match a value:
var htmlBody = `<span>Loyalty cijena</span>
<strong>863,84 KN</strong>`
var scraped = htmlBody.match(/Loyalty cijena<\/span>\s*<strong>(\d+),(\d+) KN<\/strong>/m)[1];
console.log(scraped,
parseInt(scraped) > 0 && parseInt(scraped) <= 863); // false if you change to < 863

jquery html(array) doesn't insert all items in array

When I run the javascript code below, it load specified amount of images from Flickr.
By var photos = photoGroup.getPhotos(10) code, I get 10 images from cache.
Then, I can see the object has exactly 10 items by checking console.log(photos);
But actual image appeared on the page is less than 10 items...
I have no idea why this work this way..
Thank you in advance.
<html>
<head>
<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
<script>
var PhotoGroup = function(nativePhotos, callback) {
var _cache = new Array();
var numberOfPhotosLoaded = 0;
var containerWidth = $("#contents").css('max-width');
var containerHeight = $("#contents").css('max-height');
$(nativePhotos).each(function(key, photo) {
$("<img src='"+"http://farm" + photo["farm"] + ".staticflickr.com/" + photo["server"] + "/" + photo["id"] + "_" + photo["secret"] + "_b.jpg"+"'/>")
.attr("alt", photo['title'])
.attr("data-cycle-title", photo['ownername'])
.load(function() {
if(this.naturalWidth >= this.naturalHeight) {
$(this).attr("width", containerWidth);
} else {
$(this).attr("height", containerHeight);
}
_cache.push(this);
if(nativePhotos.length == ++numberOfPhotosLoaded)
callback();
})
});
var getRandom = function(max) {
return Math.floor((Math.random()*max)+1);
}
this.getPhotos = function(numberOfPhotos) {
var photoPool = new Array();
var maxRandomNumber = _cache.length-1;
while(photoPool.length != numberOfPhotos) {
var index = getRandom(maxRandomNumber);
if($.inArray(_cache[index], photoPool))
photoPool.push(_cache[index]);
}
return photoPool;
}
}
var Contents = function() {
var self = this;
var contentTypes = ["#slideShowWrapper", "#video"];
var switchTo = function(nameOfContent) {
$(contentTypes).each(function(contentType) {
$(contentType).hide();
});
switch(nameOfContent) {
case("EHTV") :
$("#video").show();
break;
case("slideShow") :
$("#slideShowWrapper").show();
break;
default :
break;
}
}
this.startEHTV = function() {
switchTo("EHTV");
document._video = document.getElementById("video");
document._video.addEventListener("loadstart", function() {
document._video.playbackRate = 0.3;
}, false);
document._video.addEventListener("ended", startSlideShow, false);
document._video.play();
}
this.startSlideShow = function() {
switchTo("slideShow");
var photos = photoGroup.getPhotos(10)
console.log(photos);
$('#slideShow').html(photos);
}
var api_key = '6242dcd053cd0ad8d791edd975217606';
var group_id = '2359176#N25';
var flickerAPI = 'http://api.flickr.com/services/rest/?jsoncallback=?';
var photoGroup;
$.getJSON(flickerAPI, {
api_key: api_key,
group_id: group_id,
format: "json",
method: "flickr.groups.pools.getPhotos",
}).done(function(data) {
photoGroup = new PhotoGroup(data['photos']['photo'], self.startSlideShow);
});
}
var contents = new Contents();
</script>
</head>
<body>
<div id="slideShow"></div>
</body>
</html>
I fix your method getRandom() according to this article, and completely re-write method getPhotos():
this.getPhotos = function(numberOfPhotos) {
var available = _cache.length;
if (numberOfPhotos >= available) {
// just clone existing array
return _cache.slice(0);
}
var result = [];
var indices = [];
while (result.length != numberOfPhotos) {
var r = getRandom(available);
if ($.inArray(r, indices) == -1) {
indices.push(r);
result.push(_cache[r]);
}
}
return result;
}
Check full solution here: http://jsfiddle.net/JtDzZ/
But this method still slow, because loop may be quite long to execute due to same random numbers occurred.
If you care about performance, you need to create other stable solution. For ex., randomize only first index of your images sequence.

Categories