return isTrusted false - events

I'm writing my first web component with Stencil.
It's a pills component published here : https://github.com/reservoir-dogs/rp-pills
The component code's :
import { Component, Prop, Watch, Event, EventEmitter } from '#stencil/core';
#Component({
tag: 'rp-pills',
styleUrl: 'rp-pills.scss',
shadow: false
})
export class RpPills {
#Prop() items: any[] = [];
#Prop() displayProperty: string;
#Prop({ mutable: true }) value: any;
#Prop() class: string;
#Prop() theme: string = 'default';
#Prop() emptyMessage: string = 'No item';
classes: string;
#Event() valueChange: EventEmitter;
onClick(item: any) {
this.value = item;
this.valueChange.emit(this.value);
}
#Watch('class')
#Watch('theme')
watchHandler() {
this.classes = `nav nav-pills ${this.theme} ${this.class}`;
}
render() {
if (this.items.length > 0)
return (
<ul class={this.classes}>
{this.items.map((item) =>
<li onClick={() => this.onClick(item)} class={this.value === item ? 'active' : ''} >
<a>{item[this.displayProperty]}</a>
</li>)}
</ul>
);
else
return (
<ul class={this.classes}>
<li class="empty">
<a>{this.emptyMessage}</a>
</li>
</ul>
);
}
}
This test work fine :
it('should work with a list of items and selected item', async () => {
const cmp = new RpPills();
cmp.valueChange = {
emit: () => { }
};
const spy = jest.spyOn(cmp.valueChange, 'emit');
cmp.onClick({name:'Coucou'});
expect(spy).toHaveBeenCalledWith({ name: 'Coucou' });
});
But my HTML sample and an integration in Ionic App does not work and return the following message : {"isTrusted":false}
HTML sample :
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0">
<title>Stencil Component Starter</title>
<script src="/build/rppills.js"></script>
</head>
<style>
.orange .active a {
background-color: #ff0000;
}
.orange a {
background-color: #ff6a00;
}
div {
float: left;
clear: both;
}
</style>
<body>
<div>
<rp-pills display-property="name" class="nav-pills-bordered" theme="orange">
</rp-pills>
<span></span>
</div>
<div>
<rp-pills display-property="name" class="nav-justified">
</rp-pills>
<span></span>
</div>
<div>
<rp-pills display-property="name" class="nav-pills-bordered nav-justified">
</rp-pills>
<span></span>
</div>
<div>
<rp-pills class="nav-pills-bordered">
</rp-pills>
<span></span>
</div>
<div>
<rp-pills empty-message="Aucun message" class="nav-pills-bordered">
</rp-pills>
<span></span>
</div>
<div>
<rp-pills>
</rp-pills>
<span></span>
</div>
<script>
var cmps = document.querySelectorAll('rp-pills');
for (var i = 0; i < cmps.length; i++) {
var cmp = cmps[i];
if (cmp.attributes.length > 0 && cmp.attributes[0].name == 'display-property') {
cmp.value = { 'name': 'Coucou' };
cmp.items = [cmp.value, { 'name': 'Comment ca va ?' }, { 'name': 'Au revoir' }];
cmp.addEventListener('valueChange', (event) => { alert(JSON.stringify(event)); });
}
}
setInterval(() => {
var cmps = document.querySelectorAll('rp-pills');
var spans = document.querySelectorAll('span');
for (var i = 0; i < cmps.length; i++) {
if (cmps[i].value != undefined)
spans[i].innerText = cmps[i].value.name;
}
}, 1000);
</script>
</body>
</html>

2 errors
I tried to do that :
<rp-pills [items]="jeux" [(value)]="partie.jeu"></rp-pills>
The event of my Stencil component's :
is not compliant with angular two-way binding
contain a property 'detail' where there is my value
Use in Ionic App
HTML code
<rp-pills [items]="jeux" [value]="partie.jeu" (valueChange)="jeuChange($event.detail)"></rp-pills>
TypeScript code
jeuChange(jeu: Jeu) {
this.partie.jeu = jeu;
}

Related

Chart.js 4.0.1 Uncaught ReferenceError: Chart is not defined

I wrote an AJAX Javascript code for my ESP32 datalogger which worked so far using old versions of Chart.js. The website hosted by the ESP32 is not working anylonger if I use a more recent of Chart.js.
The error message I caught is:
Uncaught ReferenceError: Chart is not defined at datalogin.html
The line in the code involved is this one
const myChart = new Chart(ctx, config);
Something in the interpretation seems to occur at a very early stage. But I could not identify what. See code below.
I already tried different Chart.js repository in order to have it working. It had absolutly no influence. I actively looked using different browser regarding this specific problematic and I could not find any way of solving this problem.
Thanks a lot for your support
<!DOCTYPE html>
<html lang="en">
<head>
<title>EFM Meter loggin interface</title>
<link rel="icon" type="image/x-icon" href="lightning.ico">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script type="module" src="https://github.com/cdnjs/cdnjs/blob/master/ajax/libs/Chart.js/4.0.1/chart.js" ></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns#2.0.1/dist/chartjs-adapter-date-fns.bundle.js"></script>
<style>
.jumbotron {
background-color: #000;
color: #fff;
}
</style>
</head>
<body>
<div class="jumbotron text-center">
<h1>Elektrofeldmeter Data Logging Page</h1>
</div>
<div class="container">
<div class="border border-2 m-2">
<div class="row">
<div class="col-sm-12 ">
<div class="container-sm">
<canvas id="myChart" style="" height="152"></canvas>
</div>
</div>
</div>
</div>
<div class="row">
<div class="border border-2 m-2">
<div class="col-sm-12">
<h3> Parameters setup</h3>
</div>
</div>
<div class="border border-2 m-2">
<div class="row">
<div class="col-sm-12">
<label for="customRange1" class="form-label" id="labelPWM">PWM of motor: 190</label>
<input type="range" class="form-range" min="0" max="190" step="1" value="190" onchange="rangeValue(this)" id="customRange1">
</div>
<div class="col-sm-6">
<label for="customRange2" class="form-label" id="labelYmin">Y min: -5000</label>
<input type="range" class="form-range" min="-5000" max="5000" step="1" value="-5000" onchange="rangeY()" id="Ymin">
</div>
<div class="col-sm-6">
<label for="customRange2" class="form-label" id="labelYmax">Y max: +5000 0</label>
<input type="range" class="form-range" min="-5000" max="5000" step="1" value="5000" onchange="rangeY()" id="Ymax">
</div>
<div class="col-sm-12">
<button type="button" class="btn btn-primary" onclick="saveAs()">*.CSV</button>
</div>
</div>
</div>
</div>
</div>
<script>
function rangeValue(element) {
console.log(element.value);
var xhr = new XMLHttpRequest();
xhr.open("GET", "/pwm?value="+element.value, true);
xhr.send();
document.getElementById("labelPWM").innerHTML = "PWM of motor: " + element.value;
};
function rangeY() {
myChart.options.scales.y.max = document.getElementById('Ymax').value;
myChart.options.scales.y.min = document.getElementById('Ymin').value;
myChart.update();
};
function clkOrientation(element) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/clk?value="+element.checked, true);
xhr.send();
}
const animation = { };
const config = {
type: 'line',
data: {
datasets: [{
borderColor: '#fc9905',
borderWidth: 2,
radius: 0,
data: 0
}
]
},
options: {
animation,
interaction: {
intersect: false
},
plugins: {
legend: false
},
scales: {
x: {
type: 'time',
title: {
display: true,
text: 'time',
color: '#000000',
font: {
size: 20
}
},
ticks :
{
color: '#000000',
font: {
size: 12
}
}
},
y: {
type: 'linear',
title: {
display: true,
text: 'Electrical field [V/m] # PWM=190',
color: '#000000',
font: {
size: 20
}
},
ticks :
{
color: '#000000',
font: {
size: 12
}
}
}
},
plugins: {
title: {
display: true,
text: 'Electrical field value [V/m]'
}
}
}
};
Chart.defaults.font.size = 20;
const ctx = document.getElementById('myChart').getContext('2d');
const myChart = new Chart(ctx, config);
function addData(chart, data) {
for (var i = 0 ; i < data.length; i++){
chart.data.datasets[0].data.push({"x":data[i].x,"y":(data[i].y-32.671)/0.48025});
};
chart.update();
};
function getData() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var txt = this.responseText;
var array = JSON.parse(txt); //Ref: https://www.w3schools.com/js/js_json_parse.asp
addData(myChart,array);
}
};
xhttp.open("GET", "readEFM", true); //Handle readADC server on ESP32
xhttp.send();
};
setInterval(function() {
getData();
}, 5000);
txt = '[{"x":1662921606250,"y":35.71},{"x":1662921606450,"y":35.61},{"x":1662921606650,"y":35.70},{"x":1662921606850,"y":35.74},{"x":1662921607050,"y":35.78},{"x":1662921607250,"y":35.45},{"x":1662921607450,"y":35.65},{"x":1662921607650,"y":35.66},{"x":1662921607850,"y":35.78},{"x":1662921608050,"y":35.76}]';
const myArr = JSON.parse(txt);
addData(myChart,myArr);
myChart.update();
function saveAs(){
let csvContent = "data:text/csv;charset=utf-8,";
for (var i = 0 ; i < myChart.data.datasets[0].data.length; i++){
csvContent+=myChart.data.datasets[0].data[i].x + "," + myChart.data.datasets[0].data[i].y + "\n";
}
var encodedUri = encodeURI(csvContent);
var link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", "EFM_log.csv");
document.body.appendChild(link);
link.click();
};
</script>
</body>
</html>

Polymer 3 animation not working for shadow dom elements

I'm attempting to update the Polymer 2 grain-masonry project to use Polymer 3 and I'm 90% there, but I cannot get the animation for adding new elements to work correctly. The artifacts are added through the shadow dom and it seems that even though the class name is added, the animation doesnt work.
I've tried changing how the keyframes are added to the javascript file as well as putting the animate classes on the ":host" as opposed to ":host ::slotted"
index.html
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
<title>masonry-layout demo</title>
<script src="../node_modules/#webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
<script type="module">
import '#polymer/iron-demo-helpers/demo-pages-shared-styles';
import '#polymer/iron-demo-helpers/demo-snippet';
</script>
<script>
function addBelow() {
let newArticle = document.createElement('article');
newArticle.innerHTML = 'added below';
document.getElementById('masonry').appendChild(newArticle);
}
function addAbove() {
let newArticle = document.createElement('article');
let masonry = document.getElementById('masonry');
newArticle.innerHTML = 'added above';
document.getElementById('masonry').prependChild(newArticle);
}
</script>
<script type="module" src="../masonry-layout.js"></script>
<custom-style>
<style is="custom-style" include="demo-pages-shared-styles">
</style>
<style>
article {
background: #36abcc;
width: 29%;
height: 200px;
margin: 2%;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
grain-masonry {
background: #ccc;
}
</style>
</custom-style>
</head>
<body>
<div class="vertical-section-container centered">
<h3>Basic masonry-layout demo</h3>
<button onclick="addBelow();">add below</button>
<button onclick="addAbove();">add above</button>
<demo-snippet>
<template>
<masonry-layout id="masonry">
<article>
Power
</article>
<article style="height: 100px;">
To
</article>
<article style="height: 190px;">
The
</article>
<article style="height: 310px;">
People
</article>
<article>
Power
</article>
<article style="height: 140px;">
To
</article>
<article style="height: 210px;">
The
</article>
<article style="height: 230px;">
People
</article>
</masonry-layout>
</template>
</demo-snippet>
</div>
</body>
</html>
masonry-layout.js
import {html, PolymerElement} from '#polymer/polymer/polymer-element.js';
import {afterNextRender} from '#polymer/polymer/lib/utils/render-status.js';
import { FlattenedNodesObserver } from '#polymer/polymer/lib/utils/flattened-nodes-observer.js';
import './masonry.pkgd.min.js';
/**
* `masonry-layout`
* Polymer 3 wrapper for masonry javascript library
*
* #customElement
* #polymer
* #demo demo/index.html
*/
class MasonryLayout extends PolymerElement {
static get is() {
return 'masonry-layout'
}
static get properties() {
return {
itemSelector: {
type: String,
reflectToAttribute: true,
value: 'article'
},
transitionDuration: {
type: Number,
reflectToAttribute: true,
value: 0
}
};
}
static get template() {
return html`
<style>
#keyframes containerMasonryMoveToOrigin {
from {
opacity: 0;
}
to { transform: translateY(0); opacity: 1; }
}
:host {
display: block;
width: 100%;
}
:host .masonry-layout-animate-move-up) {
transform: translateY(200px);
opacity: 0;
animation: containerMasonryMoveToOrigin 5s ease forwards;
}
.masonry-layout-animate-move-down) {
transform: translateY(-200px);
opacity: 0;
animation: containerMasonryMoveToOrigin 0.65s ease forwards;
}
</style>
<slot id="slot"></slot>
`;
}
init() {
this.raw = new Masonry(this, {
itemSelector: this.itemSelector,
transitionDuration: this.transitionDuration,
initLayout: false
});
afterNextRender(this, function () {
this.layout();
});
}
connectedCallback() {
super.connectedCallback();
this.init();
this._loadObserver = function (event) {
let target = event.target;
if (target.tagName === 'IMG') {
this.layout();
}
}.bind(this);
this.addEventListener('load', this._loadObserver, true);
this.toggleSlotObsever = false;
this._slotObserver = new FlattenedNodesObserver(this.$.slot, (info) => {
if (this.toggleSlotObsever) {
let addedElements = info.addedNodes.filter((node) => {
return (node.nodeType === Node.ELEMENT_NODE && node.nodeName === this.itemSelector.toUpperCase())
});
let removedElements = info.removedNodes.filter((node) => {
return (node.nodeType === Node.ELEMENT_NODE && node.nodeName === this.itemSelector.toUpperCase())
});
if (addedElements.length > 0 || removedElements.length > 0) {
this.reInit();
}
}
this.toggleSlotObsever = true;
});
}
disconnectedCallback() {
this._slotObserver.disconnect();
this.removeEventListener('load', this._loadObserver);
}
layout() {
this.raw.layout();
}
reInit() {
this.raw.destroy();
this.init();
}
appendChild(element) {
this.toggleSlotObsever = false;
element.classList.add('masonry-layout-animate-move-up');
element.addEventListener('animationend', function (event) {
//event.target.classList.remove('masonry-layout-animate-move-up');
});
super.appendChild(element);
this.raw.addItems(element);
afterNextRender(this, function () {
this.layout();
this.toggleSlotObsever = true;
});
}
appendChildren(elements) {
for (let element of elements) {
this.appendChild(element);
}
}
prependChild(element) {
this.insertBefore(element, this.children[0]);
}
prependChildren(elements) {
for (let element of elements) {
this.prependChild(element);
}
}
insertBefore(element, before) {
if (element.nodeName === '#document-fragment') {
element = element.querySelector(this.itemSelector);
}
this.toggleSlotObsever = false;
element.classList.add('masonry-layout-animate-move-down');
element.addEventListener('animationend', function (event) {
//event.target.classList.remove('masonry-layout-animate-move-down');
});
super.insertBefore(element, before);
this.raw.prepended(element);
afterNextRender(this, function () {
this.layout();
this.toggleSlotObsever = true;
});
}
}
window.customElements.define('masonry-layout', MasonryLayout);

Robot Framework - did not match any elements even in the firepath

I try to automate the CMS using robot framework, I got the XPath using firepath, I'm able to locate the element in Firefox, if I refresh the page, I couldn't locate the same XPath,I thought XPath got changed, but I got the same XPath when I try to get after page refresh,
IN SHORT again:
1) Got the Xpath, say "A"
2) Refresh the page
3) try to locate the element using "A".but it shows no element found
4) Get the new XPath, say "B"
5) Both A and B are same if I try now with A it works,
I need to automate this scenario, Thanks for your time.
I tried Frame too.
Note:
Every time after refresh element gets hidden until the click operation takes place.
<document>
<html style="height: 100%; overflow: hidden;">
<head>
**xpath**<body id="m_bodyElem" class="Gecko Gecko54 ENUS cms-bootstrap ui-layout-container" style="position: relative; height: 100%; overflow: hidden; margin: 0px; padding: 0px; border: medium none;">
<form id="aspnetForm" method="post" action="./CMSAdministration.aspx">
<div class="aspNetHidden">**xpath**
***
</document>
AFTER DOM ACTIVATION
<document>
<html>
<head>
<body id="m_bodyElem" class="Gecko Gecko54 ENUS cms-bootstrap">
<form id="aspnetForm" method="post" action="./UIPage.aspx?elementguid=a5568855-b030-47ff-801a-d524618a2ca7&displaytitle=false" onsubmit="if (window.Loader) { window.Loader.submitForm(1000, false); }; return true;">
<div class="aspNetHidden">
<script type="text/javascript"> //<![CDATA[ var theForm = document.forms['aspnetForm']; if (!theForm) { theForm = document.aspnetForm; } function __doPostBack(eventTarget, eventArgument) { if (!theForm.onsubmit || (theForm.onsubmit() != false)) { theForm.__EVENTTARGET.value = eventTarget; theForm.__EVENTARGUMENT.value = eventArgument; theForm.submit(); } } //]]> </script>
<script src="/WebResource.axd?d=pynGkmcFUV13He1Qd6_TZBvGXq_vBGigUCFpfznup-OhZmSOTsQXuUy4Gn5GLidTPeGZMHkjMcSO21b0MZpf4w2&t=636160552680000000" type="text/javascript"/>
<script type="text/javascript"> //<![CDATA[ function CloseDialog(refreshPage) { // Check that the document content has not been changed without saving. Stop closing the dialog when user decides to save the content. if (window.CheckChanges && !CheckChanges()) { return false; } if (typeof(refreshPage) === "undefined") { refreshPage = true; } try { // IE9 fix - wopener doesn't have to be available if(refreshPage && window.wopener && window.wopener.RefreshWOpener) { wopener.RefreshWOpener(window); } } catch(err) {} var canClose = true; if (window.onCloseDialog) { canClose = window.onCloseDialog(); } if (canClose) { if(top.closeDialog && (top != window)) { setTimeout(function(){ if(top && top.closeDialog && (top != window)){ top.closeDialog(window) } }, 1); } else { top.close(); } } return false; } //]]> </script>
<script type="text/javascript"> //<![CDATA[ function GetTop(){ if(top.getTop) { return top.getTop(window); } else { return top; } } //]]> </script>
<script src="/CMSPages/GetResource.ashx?scriptfile=%2fCMSScripts%2fUI%2fUIPage.js" type="text/javascript"/>
<script type="text/javascript"> //<![CDATA[ function ToggleDiv(label){ var div = $cmsj(label).parent(); var image = div.find('.ToggleImage'); var hiddenField = image.next(); var affectedElements = div.find('.editing-form-category-caption').next(); if(hiddenField.val() == 'True') { div.removeClass('Collapsed'); hiddenField.val('False') image.attr('src', '/CMSPages/GetResource.ashx?image=%5bImages.zip%5d%2fCMSModules%2fCMS_PortalEngine%2fWebpartProperties%2fminus.png'); affectedElements.show(); } else { div.addClass('Collapsed'); hiddenField.val('True'); image.attr('src', '/CMSPages/GetResource.ashx?image=%5bImages.zip%5d%2fCMSModules%2fCMS_PortalEngine%2fWebpartProperties%2fplus.png'); affectedElements.hide(); } } //]]> </script>
<script type="text/javascript"> //<![CDATA[ function modalDialog(url, name, width, height, otherParams, noWopener, forceModal, forceNewWindow, setTitle) { // Header and footer is greater than before, increase window size accordingly if (typeof(height) === "number") { height += 66; } // Set default parameter values if (setTitle == undefined) { setTitle = true; } if (forceModal == undefined) { forceModal = true; } if (otherParams == undefined) { otherParams = 'toolbar=no,directories=no,menubar=no,modal=yes,dependent=yes,resizable=yes'; } var advanced = false; try { advanced = window.top.AdvancedModalDialogs; } catch (err) { } if (advanced && !forceNewWindow) { window.top.advancedModal(url, name, width, height, otherParams, noWopener, forceModal, setTitle, this); } else { var dHeight = height; var dWidth = width; if (width.toString().indexOf('%') != -1) { dWidth = Math.round(screen.width * parseInt(width, 10) / 100); } if (height.toString().indexOf('%') != -1) { dHeight = Math.round(screen.height * parseInt(height, 10) / 100); } var oWindow = window.open(url, name, 'width=' + dWidth + ',height=' + dHeight + ',' + otherParams); if (oWindow) { oWindow.opener = this; oWindow.focus(); } } } //]]> </script>
<script type="text/javascript"> //<![CDATA[ var applicationUrl = '/'; var imagesUrl = '/CMSPages/GetResource.ashx?image=%5bImages.zip%5d%2f'; var isRTL = false; //]]> </script>
<script src="/CMSPages/GetResource.ashx?scriptfile=%7e%2fCMSScripts%2fBootstrap%2fbootstrap.min.js" type="text/javascript"/>
<script src="/CMSPages/GetResource.ashx?scriptfile=%7e%2fCMSScripts%2fBootstrap%2fbootstrap.custom.js" type="text/javascript"/>
<script src="/CMSPages/GetResource.ashx?scriptfile=%7e%2fCMSScripts%2fControls%2fcontextmenu.js" type="text/javascript"/>
<script type="text/javascript"> //<![CDATA[ menuSettings['cg0_motheractions'] = { activecss : 'unigrid-action-menuactive', inactivecss : '', activecssoffset : 0, button : 'Both', vertical : 'Bottom', offsetx : 0, offsety : 0, horizontal : 'Left', dynamic : false, mouseover : false, level : 0, rtl : false, up : false }; CM_Init('cg0_motheractions'); function ContextMenuAsyncCloser_cg0_motheractions(sender, args) { HideContextMenu('cg0_motheractions',true); } RegisterAsyncCloser(ContextMenuAsyncCloser_cg0_motheractions); //]]> </script>
<script type="text/javascript"> //<![CDATA[ function ContextExportObject(definition, backup) { var query = ''; if (backup) { query += '&backup=true'; } modalDialog(applicationUrl + 'CMSModules/ImportExport/Pages/ExportObject.aspx?objectType=' + escape(definition[0]) + '&objectId=' + definition[1] + query, 'ExportObject', 750, 230); } function ContextRestoreObject(definition, backup) { var query = '&ug=UG_m_c_plc_lt_ctl01_ST_UI_Listing_gridElem'; if (backup) { query += '&backup=true'; } modalDialog(applicationUrl + 'CMSModules/ImportExport/Pages/RestoreObject.aspx?objectType=' + escape(definition[0]) + '&objectId=' + definition[1] + query, 'RestoreObject', 750, 400); } function ContextCloneObject(definition) { modalDialog(applicationUrl + 'CMSModules/Objects/Dialogs/CloneObjectDialog.aspx?objectType=' + escape(definition[0]) + '&objectId=' + definition[1], 'CloneObject', 750, 470); } //]]> </script>
<script type="text/javascript"> //<![CDATA[ menuSettings['cg0_mActions'] = { activecss : 'unigrid-menu-panel', inactivecss : 'unigrid-menu-panel', activecssoffset : 0, button : 'Both', vertical : 'Bottom', offsetx : 0, offsety : 0, horizontal : 'Left', dynamic : false, mouseover : false, level : 0, rtl : false, up : false }; CM_Init('cg0_mActions'); function ContextMenuAsyncCloser_cg0_mActions(sender, args) { HideContextMenu('cg0_mActions',true); } RegisterAsyncCloser(ContextMenuAsyncCloser_cg0_mActions); //]]> </script>
<script src="/CMSPages/GetResource.ashx?scriptfile=%7e%2fCMSScripts%2fjquery%2fjquery-dialog.js" type="text/javascript"/>
<script src="/ScriptResource.axd?d=x6wALODbMJK5e0eRC_p1LReXSSMeIyuHkoW0S0JbeJThyUVM1Ht4tiF1s3IQMCFhPQK_xpltR2WGldkDgz-jn22orun3s0371LlvE1PAZKjo6kObtvOl-fuKz6i7Aya00&t=7c776dc1" type="text/javascript"/>
<script src="/ScriptResource.axd?d=P5lTttoqSeZXoYRLQMIScL2FKbXixpOQbTl1EIUzR5PmuwyrWWddMW5IgYL1QiI9_cn_1H5B6OyKNQvmWuhRMgoFqxMQlu84DtMU2tQiSt_N7pheWxn0ZjjBaQY6SWz90&t=7c776dc1" type="text/javascript"/>
<script type="text/javascript"> //<![CDATA[ var CMS = CMS || {}; CMS.Application = { "applicationName": "PIM", "imagesUrl": "/CMSPages/GetResource.ashx?image=%5bImages.zip%5d%2f", "isDebuggingEnabled": false, "contexthelp": { "contextHelp": { "application": { "description": null, "helpTopics": [] }, "helpTopics": [] }, "suppressContextHelp": false }, "applicationUrl": "/", "isDialog": false, "isRTL": "false" }; //]]> </script>
<div class="aspNetHidden">
<script type="text/javascript"> //<![CDATA[ Sys.WebForms.PageRequestManager._initialize('m$manScript', 'aspnetForm', ['tm$c$plc$lt$ctl00$HeaderActions$headerElem$pnlUp','','tm$c$plc$lt$ctl01$ST_UI_Listing$gridElem$a$pnlUpdate','','tm$c$ctxM','','tm$pM$pMP','','tm$c$plc$lt$ctl01$MessagesPlaceholder$plcMess$pMP','','tm$c$plc$lt$ctl01$ST_UI_Listing$gridElem$plcMess$pMP',''], [], [], 90, 'm'); //]]> </script>
<div id="m_pnlBody" class="PageBody">
<div id="m_pM_pMP"/>
<div id="m_c_m"/>
<div id="m_c_ctxM">
<div id="UIHeader" class="UIHeader">
<div id="m_c_plc_lt_ctl00_HeaderActions_headerElem_pnlUp">
<div id="m_c_plc_lt_ctl00_HeaderActions_headerElem_pnlActions" class="header-actions-container">
<button id="m_c_plc_lt_ctl00_HeaderActions_headerElem_headerElem_HA_0" class="btn btn-primary" type="button" name="m$c$plc$lt$ctl00$HeaderActions$headerElem$headerElem_HA_0" value="New Product Type" onclick="window.open('/CMSModules/AdminControls/Pages/UIPage.aspx?elementguid=6e43429b-1086-4669-8130-9ee24ebb1d21&displaytitle=false','_self'); return false;__doPostBack('m$c$plc$lt$ctl00$HeaderActions$headerElem$headerElem_HA_0','')">New Product Type</button>
</div>
<div id="m_c_plc_lt_ctl00_HeaderActions_headerElem_pnlAdditionalControls" class="dont-check-changes "/>
</div>
<div id="m_c_plc_lt_ctl00_HeaderActions_HeaderActions_zone_PageTitle_pnlHeader" class="PageHeader SimpleHeader">
<div id="CMSHeaderDiv" class="CMSHeaderDiv"/>
<div id="CKToolbar" class="CKToolbar"/>
</div>
<div id="UIContent" class="UIContent scroll-area" style="height: auto; top: 56px; bottom: 0px;">
</div>
<script type="text/javascript"> //<![CDATA[ (function() {var fn = function() {$get("m_manScript_HiddenField").value = '';Sys.Application.remove_init(fn);};Sys.Application.add_init(fn);})();//]]> </script>
<script type="text/javascript"> //<![CDATA[ document.pageLoaded = true; //]]> </script>
<script type="text/javascript"> //<![CDATA[ cmsrequire(['CMS/UniGrid'], function(module) { new module({ "id": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem", "uniqueId": "m$c$plc$lt$ctl01$ST_UI_Listing$gridElem", "hdnCmdNameId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_hidCmdName", "hdnCmdArgId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_hidCmdArg", "hdnSelHashId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_hidSelectionHash", "hdnSelId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_hidSelection", "gridId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_v", "resetSelection": false, "allowSorting": false }); }); //]]> </script>
<script type="text/javascript"> //<![CDATA[ cmsrequire(['CMS/AdvancedExport'], function(module) { new module({ "id": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_a", "uniqueId": "m$c$plc$lt$ctl01$ST_UI_Listing$gridElem$a", "unigridId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem", "hdnParamId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_a_hdnParameter", "btnFullPostbackUniqueId": "m$c$plc$lt$ctl01$ST_UI_Listing$gridElem$a$btnFullPostback", "chlColumnsId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_a_chlColumns", "hdnDefaultSelectionId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_a_hdnDefaultSelection", "revRecordsId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_a_revRecords", "cvColumnsId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_a_cvColumns", "mdlAdvancedExportId": "m_c_plc_lt_ctl01_ST_UI_Listing_gridElem_a_mdlAdvancedExport", "fixHeight": false, "alertMessage": null }); }); //]]> </script>
The button I need to interact is
//button[#id='m_c_plc_lt_ctl00_HeaderActions_headerElem_headerElem_HA_0']

Angular ajax $http cannot post and get in same time

I'm trying to use MEAN stack store and update a simple angular to do list, I've write a restful api for server.the post record function app.addTask = function(task) working fine if I remove $http.get from html, but once put it back, it stop record the new task.
Can't figure it out why, need your guys help.
Here is the server.js
var express = require('express'),
restful = require('node-restful'),
bodyParser = require('body-Parser'),
methodOverride = require('method-override'),
mongoose = restful.mongoose;
var app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.use(methodOverride());
mongoose.connect('mongodb://localhost/transTaskDB');
//create new collection
var TaskSchema = mongoose.Schema({
contents: String,
target: String,
color: String,
status: Boolean
});
var Tasks = restful.model('tasks', TaskSchema);
// Tasks.methods(['get', 'post', 'put', 'delete']);
// Tasks.register(app, '/api/tasks');
// ROUTES FOR OUR API
//===============================================
var router = express.Router(); //get an instance of express Router
// middleware to use for all requests
router.use(function(req, res, next) {
// do logging
console.log('Shown when each request made.');
next(); // make sure we go to the next routes and don't stop here
});
//custom filter find the last entry record
//record new task
router.route('/newtask')
.post(function(req, res){
//create a new instance of the task model
var task = new Tasks();
task.contents = req.body.taskContent;
task.target = req.body.taskUser;
task.color = req.body.taskColor;
// task.contents = "have a tea";
// task.target = "tony";
// task.color = "#e74c3c";
task.status = false; //** fasle means the task still open not done
//task.save();
//throw error exception in console
task.save(function(err, tasks) {
if (err)
res.send(err);
res.json(tasks);
});
})
.get(function(req, res){
Tasks.find(function(err, tasks) {
if (err)
res.send(err);
res.json(tasks);
});
});
//REGISTER OUT ROUTES --------
//all of our route will be prefixed with /api
app.use('/api/tasks', router);
app.listen(3000);
console.log('Server is running at port 3000');
html
<!doctype html>
<html lang='en' ng-app='TaskAPP'>
<head>
<meta charset='UTF-8'>
<title>Trans Task</title>
<link rel='stylesheet' href='bower_components/bootstrap-sass/dist/css/bootstrap.min.css' type='text/css'>
<link rel='stylesheet' href='bower_components/bootstrap-sass/dist/css/bootstrap-theme.min.css' type='text/css'>
<style>
body {
background-image: url('images/swirl_pattern.png');
background-repeat: repeat-y repeat-x;
}
.done {text-decoration: line-through; color: #ccc;}
.task-list {list-style: none;}
.published-list {
padding: 10px 40px;
}
.published-list >div {
padding: 20px;
border-radius: 10px;
font-size:20px;
color: white;
}
</style>
</head>
<body>
<div class="container">
<nav class="navbar navbar-inverse" role="navigation">
<div class="navbar-header">
<a class="navbar-brand" href="#">
<span class="glyphicon glyphicon-tower"></span> Trans Task
</a>
</div>
</nav>
<div ng-controller='taskCtrl as app'>
<form role="form" name='frm2' ng-submit="app.addTask(app.task)">
<div class="form-group">
<label>Task Contains</label>
<input type='text' name='taskInput' class='form-control' ng-model="app.task.taskContent" placeholder="Enter task" required/>
</div>
<div class="form-group">
<!--need find a way to load the registed users from mongodb-->
<!--apply socket.io on each individual user to get realtime task update and update manager the roger response-->
<label>Target assign to</label>
<input type='text' name='taskInput' class='form-control' ng-model="app.task.taskUser" placeholder="Enter name" required/>
</div>
<div class="form-group">
<label>with color</label>
<select ng-model="app.task.taskColor">
<option ng-repeat="col in colors" value="{{col.color}}" >{{col.title}}</option>
</select>
</div>
<button class="btn btn-primary" ng-disabled='frm2.$invalid'>Publish</button>
</form>
<ul class='task-list'>
<li class="published-list" ng-repeat="tsk in app.task">
<div ng-style={'background-color':tsk.color}>
<input type='checkbox' ng-model='tsk.status'>
<span ng-class='{"done":tsk.status}'>{{tsk.contents}}</span>
</div>
</li>
</ul>
<!-- <button class="btn btn-danger" ng-click='clearCompleted()'>Clear Completed</button> -->
</div>
</div>
<script src='bower_components/jquery/dist/jquery.min.js'></script>
<script src='bower_components/angular/angular.min.js'></script>
<script src='bower_components/bootstrap-sass/dist/js/bootstrap.min.js'></script>
<script>
//angular parts
var app = angular.module('TaskAPP',[]);
//ajax sample
app.controller("taskCtrl", function($scope, $http) {
$scope.colors = [
{'title': 'red', 'color': '#e74c3c', 'status':true}, {'title': 'green', 'color': '#2ecc71', 'status':true}, {'title': 'amber', 'color': '#f39c12', 'status':true}
];
//you are the problem
var app = this;
$http.get("http://localhost:3000/api/tasks/newtask")
.success(function(data) {
//the return is the tasks json file
app.task = data;
})
app.addTask = function(task) {
$http.post("http://localhost:3000/api/tasks/newtask", task)
.success(function(data) {
//the return is the tasks json file
app.task = data;
})
}
})
</script>
</body>
</html>

Google Maps from JSON File

I'm trying to create a heatmap map with 2 layers using google maps API and heatmaps.js. I'm able to create my map by hard-coding my lat/lon variables, but ultimately I'd like to have my layers fed from JSON files that can easily be refreshed.
Here is my current HTML, which works great.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<link href='css/bootstrap.css' rel='stylesheet' type='text/css'></link>
<link href='css/bootstrap-responsive.css' rel='stylesheet' type='text/css'></link>
<script type='text/javascript' src='http://code.jquery.com/jquery-1.8.3.js'></script>
<script type='text/javascript' src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=visualization&.js"></script>
<style type="text/css">
body {
padding-top: 60px;
padding-bottom: 40px;
}
.sidebar-nav {
padding: 9px 0;
}
#media (max-width: 980px) {
/* Enable use of floated navbar text */
.navbar-text.pull-right {
float: none;
padding-left: 5px;
padding-right: 5px;
}
}
</style>
<script>
var map, pointarray, heatmap;
var layer1 = [
{location: new google.maps.LatLng(42.071523,-72.624257), weight:13.747727085},
{location: new google.maps.LatLng(42.37686,-72.46914), weight:6.6332495807},
{location: new google.maps.LatLng(42.40524,-72.528427), weight:5.1961524227},
{location: new google.maps.LatLng(42.383945,-72.511834), weight:1.7320508076},
{location: new google.maps.LatLng(42.433317,-72.114488), weight:5.6568542495}
];
var layer2 = [
{location: new google.maps.LatLng(41.513113,-74.37616), weight:2.4494897428},
{location: new google.maps.LatLng(41.147711,-73.941171), weight:10},
{location: new google.maps.LatLng(41.329376,-74.347207), weight:4.8989794856},
{location: new google.maps.LatLng(41.338977,-73.987248), weight:1.7320508076}
];
function initialize() {
var mapOptions = {
zoom: 4,
center: new google.maps.LatLng(38.82, -99.408660),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
pointArray1 = new google.maps.MVCArray(layer1);
pointArray2 = new google.maps.MVCArray(layer2);
heatmap1 = new google.maps.visualization.HeatmapLayer({
data: pointArray1
});
heatmap2 = new google.maps.visualization.HeatmapLayer({
data: pointArray2
});
heatmap1.setMap(map);
heatmap2.setMap(map);
}
//FUNCTION TO CHANGE LAYERS
function changeMap(layerNum) {
if (layerNum == 1) {
updateMap(heatmap1);
}
if (layerNum == 2) {
updateMap(heatmap2);
}
}
//FUNCTION TO UPDATE LAYERS
function updateMap(layer) {
var layerMap = layer.getMap();
if (layerMap) {
layer.setMap(null);
} else {
layer.setMap(map);
}
}
</script>
<link href='css/DT_bootstrap.css' rel='stylesheet' type='text/css'></link>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="../assets/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="brand" href="#">Test Page</a>
<div class="nav-collapse collapse">
<p class="navbar-text pull-right">
Logged in as ...
</p>
<ul class="nav">
<li class="active">Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
</div>
<body class="container-fluid">
<div class="row">
<div class="span12" style="float: none; margin: 0 auto;">
<body onload="initialize()">
<h3>Test Layers</h3>
<input type='checkbox' name='system_type17' value='1' onclick="changeMap(this.value);" checked="checked" />
<span style="width:100px;display:inline-block;">Layer 1</span>
<input type="checkbox" name="system_type3" value="2" onclick="changeMap(this.value);" checked="checked" />
<span style="width:100px;display:inline-block;">Layer 2</span>
<br><br/>
<div id="map-canvas" style="height: 575px; width: 1000px;"></div>
</body>
</div>
</div>
</body>
</body>
</html>
What I would like to do is replace var layer1 and layer2 with a single JSON file that I would be able to just query, I'm just unsure how to execute something like this. For example, a JSON file that would look something like this:
{
"datapoints": [
{
"lat": "38.1513366000",
"lon": "-97.4341659000",
"weight": "0",
"layer":"1"
},
{
"lat": "38.1513748000",
"lon": "-97.4341125000",
"weight": "0",
"layer":"1"
},
{
"lat": "38.1513938000",
"lon": "-97.4341125000",
"weight": "0",
"layer":"1"
},
{
"lat": "38.1493263000",
"lon": "-97.4339447000",
"weight": "0",
"layer":"1"
},
{
"lat": "38.1493339000",
"lon": "-97.4339447000",
"weight": "0",
"layer":"2"
},
{
"lat": "38.1493377000",
"lon": "-97.4339447000",
"weight": "0",
"layer":"2"
},
{
"lat": "38.1483650000",
"lon": "-97.4358291000",
"weight": "0",
"layer":"2"
},
{
"lat": "38.1484031000",
"lon": "-97.4358062000",
"weight": "0",
"layer":"2"
}
]
}
Updated code per Yehia's suggestions:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<link href='css/bootstrap.css' rel='stylesheet' type='text/css'></link>
<link href='css/bootstrap-responsive.css' rel='stylesheet' type='text/css'></link>
<script type='text/javascript' src='js/jquery-1.8.3.js'></script>
<script type='text/javascript' src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=visualization&.js"></script>
<style type="text/css">
body {
padding-top: 60px;
padding-bottom: 40px;
}
.sidebar-nav {
padding: 9px 0;
}
#media (max-width: 980px) {
/* Enable use of floated navbar text */
.navbar-text.pull-right {
float: none;
padding-left: 5px;
padding-right: 5px;
}
}
</style>
<script>
var map, pointarray, heatmap;
/*var layer1 = [
{location: new google.maps.LatLng(42.071523,-72.624257), weight:13.747727085},
{location: new google.maps.LatLng(42.37686,-72.46914), weight:6.6332495807},
{location: new google.maps.LatLng(42.40524,-72.528427), weight:5.1961524227},
{location: new google.maps.LatLng(42.383945,-72.511834), weight:1.7320508076},
{location: new google.maps.LatLng(42.433317,-72.114488), weight:5.6568542495}
];
var layer2 = [
{location: new google.maps.LatLng(41.513113,-74.37616), weight:2.4494897428},
{location: new google.maps.LatLng(41.147711,-73.941171), weight:10},
{location: new google.maps.LatLng(41.329376,-74.347207), weight:4.8989794856},
{location: new google.maps.LatLng(41.338977,-73.987248), weight:1.7320508076}
];*/
function initialize() {
var mapOptions = {
zoom: 4,
center: new google.maps.LatLng(38.82, -99.408660),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
}
//FUNCTION TO CHANGE LAYERS
function changeMap(layerNum) {
if (layerNum == 1) {
updateMap(heatmap1);
}
if (layerNum == 2) {
updateMap(heatmap2);
}
}
//FUNCTION TO UPDATE LAYERS
function updateMap(layer) {
var layerMap = layer.getMap();
if (layerMap) {
layer.setMap(null);
} else {
layer.setMap(map);
}
}
</script>
<link href='css/DT_bootstrap.css' rel='stylesheet' type='text/css'></link>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="../assets/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="brand" href="#">Test Page</a>
<div class="nav-collapse collapse">
<p class="navbar-text pull-right">
Logged in as ...
</p>
<ul class="nav">
<li class="active">Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
</div>
<body class="container-fluid">
<div class="row">
<div class="span12" style="float: none; margin: 0 auto;">
<body onload="initialize()">
<h3>Test Layers</h3>
<input type='checkbox' name='system_type17' value='1' onclick="changeMap(this.value);" checked="checked" />
<span style="width:100px;display:inline-block;">Layer 1</span>
<input type="checkbox" name="system_type3" value="2" onclick="changeMap(this.value);" checked="checked" />
<span style="width:100px;display:inline-block;">Layer 2</span>
<br><br/>
<div id="map-canvas" style="height: 575px; width: 1000px;"></div>
</body>
</div>
</div>
<script type="text/javascript">
$.getJSON("testdata.json").then(function(data) {
$.each(data, function(i,datapoints) {
$.each(datapoints, function(j,datapoint) {
layers.push({
location: new google.maps.LatLng(datapoint.lat, datapoint.lng),
weight: datapoint.weight,
layerID: datapoint.layer
});
});
});
});
<script>
</body>
</body>
</html>
Alright, almost there I think. Thanks so much for your help so far, I know this is painful. Here is my initialize function, I'm still getting Uncaught ReferenceError: layers is not defined
<script type='text/javascript'>
var map, pointarray, heatmap;
function initialize() {
function loadData() {
var layers = [ ];
$.ajax({
url: 'testdata.json',
async: false,
dataType: 'json',
success: function (data) {
for (var i=0; i<data.datapoints.length; i++) {
var datapoint = data.datapoints[i];
if (layers[datapoint.layer]===undefined) {
layers[datapoint.layer] = [];
}
layers[datapoint.layer].push({
location: new google.maps.LatLng(datapoint.lat, datapoint.lon),
weight: datapoint.weight,
layerID: datapoint.layer
});
}
}
});
return layers;
}
var mapOptions = {
zoom: 4,
center: new google.maps.LatLng(38.82, -99.408660),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
pointArray1 = new google.maps.MVCArray(layers[1]);
pointArray2 = new google.maps.MVCArray(layers[2]);
heatmap1 = new google.maps.visualization.HeatmapLayer({
data: pointArray1
});
heatmap2 = new google.maps.visualization.HeatmapLayer({
data: pointArray2
});
heatmap1.setMap(map);
heatmap2.setMap(map);
}
//FUNCTION TO CHANGE LAYERS
function changeMap(layerNum) {
if (layerNum == 1) {
updateMap(heatmap1);
}
if (layerNum == 2) {
updateMap(heatmap2);
}
}
//FUNCTION TO UPDATE LAYERS
function updateMap(layer) {
var layerMap = layer.getMap();
if (layerMap) {
layer.setMap(null);
} else {
layer.setMap(map);
}
}
</script>
You'll need a way of reading that JSON and parsing it into the variable(s) you need. Since you're using jQuery, here's a method that would work with the JSON structure you have.
$.getJSON("layers.json").then(function(data) {
$.each(data, function(i,datapoints) {
$.each(datapoints, function(j,datapoint) {
layers.push({
location: new google.maps.LatLng(datapoint.lat, datapoint.lng),
weight: datapoint.weight,
layerID: datapoint.layer
});
});
});
});
Adapt as necessary.
In response to davids12's update, here's a function that returns an array of arrays where each composite array has the datapoints from a certain layer. So, running over your example, layers would have 2 arrays in it (layers[1] and layers[2]) which are basically layer1 and layer2 that you describe in your code (with an additional layerID variable; you could remove it if you wish). This allows you to load much more layers and points just by putting them in the JSON using the format you have now.
function loadData() {
var layers = [ ];
$.ajax({
url: 'j.json',
async: false,
dataType: 'json',
success: function (data) {
for (var i=0; i<data.datapoints.length; i++) {
var datapoint = data.datapoints[i];
if (layers[datapoint.layer]===undefined) {
layers[datapoint.layer] = [];
}
layers[datapoint.layer].push({
location: new google.maps.LatLng(datapoint.lat, datapoint.lng),
weight: datapoint.weight,
layerID: datapoint.layer
});
}
}
});
return layers;
}

Resources