In my Project, I have multiple Graphs (max 15 of different plots like Bar , Chart , Bubble, Line) that are being plotted using javascript (Open/SAP UI-5) in HTML5 below is the code for the same
press: function() {
var arrCanvas = document.getElementsByTagName("canvas");
for(var i=0; i<arrCanvas.length; i++){
var canvas = arrCanvas[i];
if(canvas.width!==0 && (typeof imageNames[i] !== 'undefined')){
var imageDetail = {};
var uint8ArrayClamped = canvas.getContext("2d").getImageData(0,0,canvas.width,canvas.height).data;
imageDetail.byteData = uint8ArrayClamped;
imageDetail.title = "graph-"+i;
imageDetail.height = canvas.height;
imageDetail.width = canvas.width;
arrImageByte.push(imageDetail);
}
}
}
When I pass this arrImageByte to Ajax call the browser (tested on chrome) crashes with "oh snap" message.
while adding below line to the code
press: function() {
var arrCanvas = document.getElementsByTagName("canvas");
for(var i=0; i<arrCanvas.length; i++){
var canvas = arrCanvas[i];
if(canvas.width!==0 && (typeof imageNames[i] !== 'undefined')){
var imageDetail = {};
var uint8ArrayClamped = canvas.getContext("2d").getImageData(0,0,canvas.width,canvas.height).data;
//I belive somwhow This does the trick
imageDetail.byteData = [].slice.call(uint8ArrayClamped);
imageDetail.title = "graph-"+i;
imageDetail.height = canvas.height;
imageDetail.width = canvas.width;
arrImageByte.push(imageDetail);
}
}
}
Copying the unit8ArrayClamped into another empty array does the trick, now the code work and REST is being called, I wonder why this is happening?
but again copying such a huge data into another takes time and for multiple graphs, it takes almost 6-7 seconds, how to reduce this to minimal?
Is there another approach besides copying the byte array into another new array?
Related
I've searched high and wide for an answer but can't seem to find it. I am trying to alter my custom function that looks up sitemap URL's and the date they were updated to accept a range of inputs.
Here is the current function that works:
function sitemap(sitemapUrl, namespace) {
var array = [];
var xml = UrlFetchApp.fetch(sitemapUrl).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement();
var sitemapNameSpace = XmlService.getNamespace("http://www.sitemaps.org/schemas/sitemap/0.9");
var urls = root.getChildren('url', sitemapNameSpace);
for (var i = 0; i < urls.length; i++) {
var loc = urls[i].getChild('loc', sitemapNameSpace).getText();
var lastmod = urls[i].getChild('lastmod', sitemapNameSpace).getText();
array.push([loc, lastmod]);
}
return array;
}
I've tried using Google's example below but doesn't seem to work however I incorporate it into my function. Any ideas?
function DOUBLE(input) {
if (input.map) { // Test whether input is an array.
return input.map(DOUBLE); // Recurse over array if so.
} else {
return input * 2;
}
}
Edit: This is how I tried to use Google's example for my function:
function sitemaps(sitemapUrl) {
var array = [];
var xml = UrlFetchApp.fetch(sitemapUrl).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement()
var sitemapNameSpace = XmlService.getNamespace("http://www.sitemaps.org/schemas/sitemap/0.9")
var urls = root.getChildren('url', sitemapNameSpace)
for (var i = 0; i < urls.length; i++) {
var loc = urls[i].getChild('loc',sitemapNameSpace).getText();
var lastmod = urls[i].getChild('lastmod',sitemapNameSpace).getText();
array.push([loc, lastmod]);
}
if (sitemapUrl.map) {
return sitemapUrl.map(sitemaps);
} else {
return array
}
You are no using the same format as the Google example. As of right now you are checking if the input is an array after actually retrieving the data.
But you using fetch with an array as input could trigger an Error and the function may no get to the point where it checks if the sitemapUrl can be used with map.
Also take into account that map will call the function in every single element of the array and return an array with a result for each of element. So in your case B3:B6 would call the function for the value at B3, B4, B5 and B6 and return an array of length 4 with the result. For your case in which you want a single list you need to flattern the array afterwards
I would change your function to be like this:
function sitemaps(sitemapUrl) {
if (sitemapUrl.map) {
return sitemapUrl.map(sitemaps).flat();
} else {
var array = [];
var xml = UrlFetchApp.fetch(sitemapUrl).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement()
var sitemapNameSpace = XmlService.getNamespace("http://www.sitemaps.org/schemas/sitemap/0.9")
var urls = root.getChildren('url', sitemapNameSpace)
for (var i = 0; i < urls.length; i++) {
var loc = urls[i].getChild('loc', sitemapNameSpace).getText();
var lastmod = urls[i].getChild('lastmod', sitemapNameSpace).getText();
array.push([loc, lastmod]);
}
return array
}
}
Although what you are doing is fine take into account that it also exists a way to retrieve all the request at the same time (
UrlFetchApp.fetch()) but for this specific case you would need to flatten a reshape the input array.
I'm using several CANVAS in a HTML file and I want to declare them in an Array. This is the (not array) declaration:
var canvas0 = document.getElementById('canvas0');
var ctxPaint0 = canvas0.getContext('2d');
var canvas1 = document.getElementById('canvas1');
var ctxPaint1 = canvas1.getContext('2d');
var canvas2 = document.getElementById('canvas2');
var ctxPaint2 = canvas2.getContext('2d');
var canvas3 = document.getElementById('canvas3');
var ctxPaint3 = canvas3.getContext('2d');
To optimize this, I'm trying to use For loops, but it doesn't work:
var canvasArray = ["canvas0","canvas1","canvas2","canvas3"];
for (var i = 0; i < canvasArray.length; i++) {
window["canvas"+i] = document.getElementById(canvasArray[i]);
window["ctxPaint"+i] = canvasArray[i].getContext('2d');
}
It outputs this error:
Uncaught TypeError: canvasArray[i].getContext is not a function
EDIT:
Thanks to RSchneyer, changing the Array of strings to an Array of Canvas works fine now:
let canvasArray = Array.from(document.getElementsByTagName("canvas"));
for (var i = 0; i < canvasArray.length; i++) {
window["canvas"+i] = document.getElementById(canvasArray[i]);
window["ctxPaint"+i] = canvasArray[i].getContext('2d');
}
You could do like
const ctxArray = [...document.querySelectorAll('[id^="canvas"]')].map(cvs => cvs.getContext('2d'));
which will result to an array holding all the canvases context - which is all you need, since to get the original canvas out of its context all you need is i.e.:
let i = 0; // or any desired index
const canvas = ctxArray[i].canvas;
canvasArray is an array of strings, which don't have a getContext() function. Try replacing the canvasArray[i] in window["ctxPaint"+i] = canvasArray[i].getContext('2d'); with window["canvas"+i], or create another array to hold your actual canvas variables
I have an array named globalArrayAllTrades as you see below. I simply like to INVERT the date in a new copy of the array. So I loop through, create a new object and add it to the new array - simple.
Then function does exactly as expected. BUT if the array contains too many objects the code fails with a "FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory".
My laptop has 8 GB of memory...When the NODEJS process crashes it uses about 1.5 GB and about 70% of of totally amount of available memory is used.
I do run the NODEJS app with the parameter: --max_old_space_size=5000 which normally fixes every thing. But not this one and i have tried MANY different ways to code the same function - BUT each and every time - it fails...unless the original array is smaller.
How can I fix this issue?
function invertTrades(){
var original = globalArrayAllTrades.slice();
globalArrayAllTrades.length = 0;
globalListAllTrades.length = 0;
for(var i = 0; i < original.length; i++){
var objS = original[i];
var objE = original[original.length-1-i];
var objInv = new TradePoint(objS.number, objS.matchdate, objE.price, objE.size, objE.issell);
globalArrayAllTrades.push(objInv);
globalListAllTrades[objInv.matchdate] = objInv;
}
}
You can save some memory by making original just contain the properties you need to invert, not the whole TradePoint object. Then you don't need to construct new TradePoint objects, you can modify them in place.
var original = globalArrayAllTrades.map(function(trade) {
return {
trade.price,
trade.size,
trade.issell
};
}).reverse();
globalArrayAllTrades.forEach(function(trade, i) {
trade.price = original[i].price;
trade.size = original[i].size;
trade.issell = original[i].issell;
});
And since all the objects were modified in place, there's no need to update globalListAllTrades.
Another way is to swap the price, size, and issell properties in place between the pairs of elements:
var midpoint = Math.floor(globalArrayAllTrade.length/2);
for (var i = 0; i < midpoint; i++) {
var objS = globalArrayAllTrades[i];
var objE = globalArrayAllTrades[globalArrayAllTrades.length-1-i];
var temp = objS.price;
objS.price = objE.price;
objE.price = temp;
temp = objS.size;
objS.size = objE.size;
objE.size = temp;
temp = objS.issell;
objS.issell = objE.issell;
objE.issell = temp;
}
Have you considered just doing this?
// Copy array and then reverse it
var newArray = [].concat(original).reverse();
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
I would suggest avoiding to copy that array:
function getInverse(i) {
var objS = globalArrayAllTrades[i];
var objE = globalArrayAllTrades[globalArrayAllTrades.length-1-i];
var objInv = new TradePoint(objS.number, objS.matchdate, objE.price, objE.size, objE.issell);
globalListAllTrades[objInv.matchdate] = objInv;
return objInv;
}
function invertTrades(){
globalListAllTrades.length = 0;
for (var i = 0, l = Math.floor(globalArrayAllTrades.length/2); i < l; i++) {
var j = globalArrayAllTrades.length-1-i;
var a = getInverse(i);
var b = getInverse(j);
globalArrayAllTrades[i] = a;
globalArrayAllTrades[j] = b;
}
}
I have to use XMLHttpRequest to grab a kml file, because I cannot directly make changes to the KML and needed to draw out polygons with their own separate infowindows with details on them that is stored in the KML but not as a description tag or anything like that so I couldn't just grab it easily. I managed to do this, and the polygons display and the infowindows work. Its a sizable program so I didn't display it here, but basically when I load the getKML function of mine, it would not work in the development environment or present issues. Whereas it would work well on my localhost.
This is the error message I keep getting:
Uncaught NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'https://someURL/polygons_live.kml'.
Heres the code, you really only need the first couple lines to look at because thats where xmlhttprequest is used, also excuse me for the messy code, still an intern and refactoring:
/** Calls using xmlhttprequest to grab the kml file
* and later prints it out as one or more polygons
*/
function getKML(kmlUrl) {
var xmlRequest = new XMLHttpRequest();
xmlRequest.open("GET", kmlUrl, false);
xmlRequest.send();
xmlDoc = xmlRequest.responseXML;
var x = xmlDoc.getElementsByTagName("Placemark");
// travels through each Placemark tag in the kml files
var outage_time, restoration_time, event_number_value, fillColour, borderColour;
var objArray = [];
for (var i = 0; i < x.length; i++) {
// uses momentjs api to create human readable dates
var date_format = "MMM DD, hh:mm a";
// gets the event number
event_number_value = x[i].getElementsByTagName("SimpleData")[2].childNodes[0].nodeValue;
// gets outage start time
var outage_time_value = x[i].getElementsByTagName("SimpleData")[3].childNodes[0].nodeValue;
var outage_time_moment = moment(outage_time_value);
outage_time = outage_time_moment.format(date_format);
// gets estimated restoration time
var restoration_time_value = x[i].getElementsByTagName("SimpleData")[5].childNodes[0].nodeValue;
// checks to see if we have a restoration time or not
if (restoration_time_value === "2001-01-01T00:00:00") {
restoration_time = "Not yet determined";
} else {
var restoration_time_moment = moment(restoration_time_value);
restoration_time = restoration_time_moment.format(date_format);
}
// gets the coordinates of the polygon
var coords = x[i].getElementsByTagName("coordinates")[0].childNodes[0].nodeValue;
var coordinate = coords.split(",0 ");
var coordJoined = coordinate.join();
var coordAgain = coordJoined.split(",");
// gets the colour of the polygon
var colour = x[i].getElementsByTagName("styleUrl")[0].childNodes[0].nodeValue;
// determines the colour out of yellow, orange and red
if (colour === "#Style1") {
fillColour = '#f1c40f';
borderColour = '#f1c40f';
} else if (colour === "#Style2") {
fillColour = '#e67e22';
borderColour = '#e67e22';
} else {
fillColour = '#c0392b';
borderColour = '#c0392b';
}
// creates objects and adds it to array to be later used as data
var obj = {
eventID : event_number_value,
offTime : outage_time,
restoreTime : restoration_time,
fill : fillColour,
borderCol : borderColour
};
objArray.push(obj);
// create a LatLng array out of the coordinate string
var polygonCoords = new Array();
var j = 0;
var z = j + 1;
//var firstCoord = new google.maps.LatLng();
while (z < coordAgain.length) {
// adds the first and last latLng to the array of polygonCoords
if ((j % 2) == 0) {
var co1 = parseFloat(coordAgain[z]);
var co2 = parseFloat(coordAgain[j]);
var newLatLng = new google.maps.LatLng(co1, co2);
polygonCoords.push(newLatLng);
} else {
var co1 = parseFloat(coordAgain[j]);
var co2 = parseFloat(coordAgain[z]);
var newLatLng = new google.maps.LatLng(co1, co2);
polygonCoords.push(newLatLng);
}
j++;
z++;
}
//removes last coordinate as its useless as its not a number
polygonCoords.pop();
/** Adds the polygon to a polygon array
* and maps it onto the map
*/
var newPoly = new google.maps.Polygon({
paths : polygonCoords,
strokeColor : objArray[i].borderCol,
strokeOpacity : 0.35,
strokeWeight : 2,
fillColor : objArray[i].fill,
fillOpacity : 0.35
})
newPoly.setMap(map);
newPoly.set("eventNum", objArray[i].eventID);
newPoly.set("offTime", objArray[i].offTime);
newPoly.set("resTime", objArray[i].restoreTime);
google.maps.event.addListener(newPoly, 'click',
attachInfoWindow(newPoly));
polyArray.push(newPoly);
}
}
Update 1: I actually found this error later on appearing in my console:
XMLHttpRequest cannot load https://someurl/polygons_live.kml. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://someurl' is therefore not allowed access.
Its a cross-domain request issue, I'm going to start using relative addresses to point to when grabbing my kml.
It resolved my issue.
Is there any way to access a pathItem's fill opacity with javascript? I can access the overall opacity, but I want to lower the opacity of the fill while keeping the stroke fully opaque.
I can't find anything in the documentation, nor can I find anyone else asking this question.
I can set the overall opacity like so:
var selection = app.activeDocument.selection;
selection[0].opacity = 50;
I've tried every variant of "fillOpacity" that I can think of, like this:
var selection = app.activeDocument.selection;
selection[0].fillOpacity = 50;
selection[0].FillOpacity = 50;
selection[0].fill.opacity = 50;
...but it doesn't work.
Am I going about this wrong, or is this just not possible?
You cannot access it, as you cannot access it normally even in illustrator. This is a Photoshop property only. I checked the documentation as well just to make sure. What you could do is this though and it would accomplish same thing:
doc = app.activeDocument;
i = 0
var selection = doc.selection[i];
var storedColor = doc.selection[i].fillColor;
//new object with only fill, we send it to back so it doesn't overlap stroke, if there is one
var newObject = app.selection[i].duplicate(doc, ElementPlacement.PLACEATEND);
//turn off fill for first object
doc.selection[i].filled = false;
i = i + 1;
newObject.stroked = false;
//apply stored color from earlier to new shape
newObject.fillColor = storedColor;
newObject.opacity = 50;
newObject.name = "50p fill";
What I did to solve the problem is to apply a spotcolor to the objects where I uses the tint property
var docRef = app.activeDocument;
var selectedObjects = docRef.selection;
var theTint;
var fillwithSwatch = function (pathItems, sname ){
for (var i=0;i< pathItems.length; i++){
pathItems[i].fill = true;
theTint = pathItems[i].fillColor.gray;
pathItems[i].fillColor = docRef.swatches.getByName ( sname ).color ;
pathItems[i].fillColor.tint = theTint;
}
}
theTint = fillTint(selectedObjects);
// the spotcolor should be in the swatchpallet already
fillwithSwatch (selectedObjects, "myBlue" );