How can I change data element on Vue.js? - javascript

<div id="SearchAddress">
<div class="main_class">
<div class="find_juso_map">
<input type="button" v-on:click="load_juso" value="주소 검색"><br>
<div id="map" style="width:300px;height:300px;margin-top:10px;"></div>
<input type="text" id="sample5_address" placeholder="메모">
<input type="button" v-on:click="add_place" value="장소 추가"><br>
</div>
<div class="set_juso_list">
<h4>요기조아</h4>
<div></div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'SearchAddress',
data() {
return {
center: new window.kakao.maps.LatLng(33.450701, 126.570667),
geocoder: new window.kakao.maps.services.Geocoder(),
joah_add: null,
joah_list: {}
}
},
props: {
},
mounted () {
var container = document.getElementById('map');
var options = {
center: this.center,
level: 3
};
new window.daum.maps.Map(container, options);
},
methods: {
load_juso() {
new window.daum.Postcode({
oncomplete: function(data) {
var addr = data.address; // 최종 주소 변수
console.log(addr,'주소')
this.joah_add = addr;
console.log(this.joah_add ,'조아조아')
// // 주소로 상세 정보를 검색
new window.kakao.maps.services.Geocoder().addressSearch(data.address, function(results, status) {
// 정상적으로 검색이 완료됐으면
if (status === window.daum.maps.services.Status.OK) {
var result = results[0]; //첫번째 결과의 값을 활용
// 해당 주소에 대한 좌표를 받아서
var coords = new window.daum.maps.LatLng(result.y, result.x);
// 지도를 보여준다.
var container = document.getElementById('map');
var map = new window.daum.maps.Map(container, {center: coords,level: 3});
container.style.display = "block";
map.relayout();
map.setCenter(coords);
new window.daum.maps.Marker({position: coords, map: map}).setPosition(coords);
}
});
}
}).open();
},
add_place() {
console.log('장소추가',this.joah_add)
}
}
}
</script>
I want to put joah_add data in the load_juso() and call it in the add_place.
first, I called load_juso() method.
I think, 'this.joah_add = addr;' <- this code can access data and set data: joah_add value.
second, I called add_place method.
why console print joah_add data -> null??
why doesn't it come out like 'this.joah_add = addr'?
please help me :'(

Javascript's this is not bound from the start
but is bound at the time of the call
So your this.joah_add = addr; of this
may be pointing to daum instead of vue
Javascript's this in normal function mode
points to whoever calls it
So
new window.daum.Postcode({... this.joah_add = addr})
could be equivalent to
new window.daum.Postcode({... daum.joah_add = addr})
and not
new window.daum.Postcode({... vue.joah_add = addr})
And your
add_place() {
console.log('장소추가',this.joah_add)
}
should be
add_place() {
console.log('장소추가',vue.joah_add)
}
So here's what you might need to do
load_juso() {
const that = this
new window.daum.Postcode({… that.joah_add = addr;...})
...
}
Hope it can help you

Related

JQuery cloned element

I am stuck on this problem. I am coding a task platform app. Whenever I try to save, the task clones itself. After each "Save Changes," there are more and more clones. I have rewritten the code so many times. But still, I am not successful. Please help me to find the error.
$("#taskSave").click(() => {
const task = {
id: Date.now(),
imageUrl: $("#imageInput").val(),
title: $("#titleInput").val(),
description: $("#descriptionInput").val(),
type: $("#typeInput").val(),
};
$("#overlay").hide();
todos.push(task);
saveStorage(todos);
// reset input values
$("#imageInput").val("");
$("#titleInput").val("");
$("#descriptionInput").val("");
$("#typeInput").val("");
});
function saveStorage(todos) {
localStorage.setItem("todos", JSON.stringify(todos));
display(todos);
};
function display(todos) {
$("#taskBoard").innerHTML = "";
// .html("");
todos.forEach(item => {
let c = document.createElement("div");
c.setAttribute("class", "card");
c.setAttribute('id', item.id);
c.innerHTML = `
<div class="cardTop">
<div class="binContainer">
<div class="binImage"></div>
</div>
</div>
<img src="${item.imageUrl}" alt="task image">
<h2>${item.title}<h2>
<p>${item.description}</p>
<div class="cardType">${item.type}</div>
`;
$("#taskBoard").append(c);
// end
});
};
I've created a minimal working example, and the problem is in the cleanup of the HTML. You cannot use innerHTML on the JQuery object, or you use its html function or you need to retrieve the javascript object with $("#taskBoard")[0].
// You can use:
$("#taskBoard").html("");
// or
// document.getElementById("taskBoard").innerHTML = "";
// or
// $("#taskBoard")[0].innerHTML = "";
// But not:
// $("#taskBoard").innerHTML = "";
The working example here on JSFiddle (on SO dont work localStorage)
let todos = [];
$("#taskSave").click(() => {
const task = {
id: Date.now()
};
todos.push(task);
saveStorage(todos);
});
function saveStorage(todos) {
localStorage.setItem("todos", JSON.stringify(todos));
display(todos);
console.log(todos);
};
function display(todos) {
$("#taskBoard").html("");
// or
// document.getElementById("taskBoard").innerHTML = "";
// or
// $("#taskBoard")[0].innerHTML = "";
// But not
// $("#taskBoard").innerHTML = "";
todos.forEach(item => {
let c = document.createElement("div");
c.innerHTML = `
<p>${item.id}</p>
`;
$("#taskBoard").append(c);
});
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="taskSave">
SAVE
</button>
<div id="taskBoard">
</div>

Why am I getting a TypeError not a function. Calling forEach on an array object

For a bit of background information: this app is supposed to load a map api and create a workout form linked to a location selected on the map by clicking. Once the map is clicked, a form is loaded to fill out info about that workout and then it's saved to the #workout variable.
Problem: I'm trying to save the #workout variable to local storage to then load all the workouts from storage whenever the page is reloaded.
I'm trying to run the _getLocalStorage() function in the constructor to load items from the local storage when the page loads, but I keep getting this TypeError code:
script.js:239 Uncaught TypeError: this[#workout].forEach is not a function
at App._getLocalStorage (script.js:239)
at new App (script.js:21)
Code:
class App {
#map;
#mapEvent;
#workout = [];
constructor() {
this._getPosition();
this._getLocalStorage();
form.addEventListener('submit', this._newWorkout.bind(this));
inputType.addEventListener('change', this._toggleElevationField);
containerWorkouts.addEventListener('click', this._panToWorkout.bind(this));
}
_panToWorkout(e) {
// find the workout
const workoutEl = e.target.closest('.workout');
if (!workoutEl) return;
const workout = this.#workout.find(
work => work.id === workoutEl.dataset.id
);
// pan to workout object with that id number
this.#map.setView(workout.coords, 13, {
animate: true,
pan: {
duration: 1,
easeLinearity: 0.8,
},
});
}
_getPosition() {
// -> check if this nagivator.geolocation object exits, then loads the map.
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
this._loadMap.bind(this),
function () {
alert("Can't get your position");
}
);
}
}
_loadMap(position) {
const { latitude, longitude } = position.coords;
// -> creating a coordinate variable because the below L.map().setView function expects an array for the coordinates.
// -> adding the map loading script from the imported library after getting coordinates
this.#map = L.map('map').setView([latitude, longitude], 13);
L.tileLayer('https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png').addTo(
this.#map
);
// -> SETUP MAP CLICK LISTENER && OPEN FORM
this.#map.on('click', this._showForm.bind(this));
}
_showForm(event) {
this.#mapEvent = event;
form.classList.remove('hidden');
inputDistance.focus();
}
_hideForm() {
// -> Clear form values when submit
inputDistance.value =
inputCadence.value =
inputDuration.value =
inputElevation.value =
'';
// -> removes the form from view to disable the slide transition while it's being removed.
form.style.display = 'none';
form.classList.add('hidden');
setTimeout(() => (form.style.display = 'grid'), 1000);
}
_toggleElevationField() {
inputElevation.closest('.form__row').classList.toggle('form__row--hidden');
inputCadence.closest('.form__row').classList.toggle('form__row--hidden');
}
_newWorkout(e) {
// -> prevent default submit function which is to refresh the page
e.preventDefault();
const validInput = (...inputs) =>
inputs.every(entry => Number.isFinite(entry));
const allPositive = (...inputs) => inputs.every(inp => inp > 0);
// -> Get data from form
const type = inputType.value;
const distance = +inputDistance.value;
const duration = +inputDuration.value;
const { lat, lng } = this.#mapEvent.latlng;
let workout;
// -> if running, create running object
if (type === 'running') {
// -> check if data is valid
const cadence = +inputCadence.value;
if (
!validInput(distance, duration, cadence) ||
!allPositive(distance, duration, cadence)
) {
return alert('Inputs have to be a positive number.');
}
workout = new Running([lat, lng], distance, duration, cadence);
}
// -> if cycling, create cycling object
if (type === 'cycling') {
const elevation = +inputElevation.value;
// -> check if data is valid
if (
!validInput(distance, duration, elevation) ||
!allPositive(distance, duration)
)
return alert('Inputs have to be a positive number.');
workout = new Cycling([lat, lng], distance, duration, elevation);
}
// -> adds workout to workout array
this.#workout.push(workout);
// -> render the workout
this._renderWorkoutMarker(workout);
// -> Render workout on list
this._renderWorkout(workout);
// -> hide the form
this._hideForm();
// -> save workouts to storage
this._setLocalStorage();
}
_renderWorkoutMarker(workout) {
// -> DISPLAY MAP MARKER ON SUBMIT
L.marker(workout.coords)
.addTo(this.#map)
.bindPopup(
L.popup({
minWidth: 250,
maxWidth: 100,
autoClose: false,
closeOnClick: false,
className: `${workout.type}-popup`,
})
)
.setPopupContent(
`${workout.type === 'cycling' ? '🚴‍♂️' : '🏃‍♂️'} ${workout.description}`
)
.openPopup();
}
_renderWorkout(workout) {
let html = `
<li class="workout workout--${workout.type}" data-id="${workout.id}">
<h2 class="workout__title">${workout.description}</h2>
<div class="workout__details">
<span class="workout__icon">${
workout.type === 'cycling' ? '🚴‍♂️' : '🏃‍♂️'
}</span>
<span class="workout__value">${workout.distance}</span>
<span class="workout__unit">km</span>
</div>
<div class="workout__details">
<span class="workout__icon">⏱</span>
<span class="workout__value">${workout.duration}</span>
<span class="workout__unit">min</span>
</div>
`;
if (workout.type === 'running') {
html += `
<div class="workout__details">
<span class="workout__icon">⚡️</span>
<span class="workout__value">${workout.pace.toFixed(1)}</span>
<span class="workout__unit">min/km</span>
</div>
<div class="workout__details">
<span class="workout__icon">🦶🏼</span>
<span class="workout__value">${workout.cadence.toFixed(1)}</span>
<span class="workout__unit">spm</span>
</div>
</li>
`;
}
if (workout.type === 'cycling') {
html += `
<div class="workout__details">
<span class="workout__icon">⚡️</span>
<span class="workout__value">${workout.speed.toFixed(1)}</span>
<span class="workout__unit">km/h</span>
</div>
<div class="workout__details">
<span class="workout__icon">⛰</span>
<span class="workout__value">${workout.elevation.toFixed(1)}</span>
<span class="workout__unit">m</span>
</div>
</li>
`;
}
form.insertAdjacentHTML('afterend', html);
}
_setLocalStorage() {
localStorage.setItem('workouts', JSON.stringify(this.#workout));
}
_getLocalStorage() {
const data = JSON.parse(localStorage.getItem('workouts'));
if (!data) return;
this.#workout = data;
console.log(typeof this.#workout);
this.#workout.forEach(work => {
this._renderWorkout(work);
});
}
}
It seems like the result of const data = JSON.parse(localStorage.getItem('workouts')); is not Array.
So you can check it as below
console.log(JSON.parse(localStorage.getItem('workouts')));

ArcGIS API for JavaScript: querying from dynamically-populated drop down list

Using ArcGIS API for Javascript 4, I want to create a drop-down list in the view that is dynamically populated based on the values available in a layer. When users select a value from this drop down and click a button, I want to query those features matching the value in the drop menu and have them appear on the map.
I've combined some elements from the following two tutorials for this:
https://developers.arcgis.com/javascript/latest/sample-code/tasks-query/index.html
https://developers.arcgis.com/javascript/latest/sample-code/featurelayer-query/index.html
I can get the attributes to populate in the drop down list. However, clicking the button to query returns the following error message: "Promise rejected: Cannot perform query. Invalid query parameters."
I'm hoping this is just something simple I've overlooked, but I can't seem to find the issue. I would appreciate any advice. (Copying + pasting the complete code in case that's easiest to drop into CodePen or elsewhere to work with.)
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"/>
<title>Project viewer</title>
<style>
html,
body,
#mainViewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#optionsDiv {
background-color: dimgray;
color: white;
padding: 10px;
width: 90%;
}
#drop-downs {
padding-bottom: 15px;
}
widget {
/* Fill in later to style drop down widget */
}
</style>
<link
rel="stylesheet"
href="https://js.arcgis.com/4.15/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.15/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/Basemap",
"esri/widgets/LayerList",
"esri/layers/FeatureLayer",
"esri/PopupTemplate",
"esri/layers/GraphicsLayer",
"esri/tasks/QueryTask",
"esri/tasks/support/Query"
], function(
Map,
MapView,
Basemap,
LayerList,
FeatureLayer,
PopupTemplate,
GraphicsLayer,
QueryTask,
Query
){
var basinUrl = "https://services.arcgis.com/v01gqwM5QqNysAAi/arcgis/rest/services/Chesapeake_Bay_major_watersheds_feature/FeatureServer/0";
//* for drop down
var basinTypeSelect = document.getElementById("MajBas");
//* Define the popup content for each result
var popupTemplate = {
title: "{MajBas}",
fieldInfos: [
{
fieldName: "MajBas",
label: "Major basin"
}
]
};
// Layer - project footprints
const basinLayer = new FeatureLayer({
url: basinUrl,
outFields: ["*"],
visible: false
});
// Layer - dark nav basemap
const basemap = Basemap.fromId("streets-night-vector");
//** Point querytask to project boundary URL
var qTask = new QueryTask({
url: basinUrl
});
//** Set the query parameters to always return geometry and all fields.
//** Returning geometry allows us to display results on the map/view
var params = new Query({
returnGeometry: true,
outFields: ["*"]
});
//* GraphicsLayer for displaying results
var resultsLayer = new GraphicsLayer();
var map = new Map({
basemap : basemap,
layers: [basinLayer]
});
var mainView = new MapView({
container: "mainViewDiv",
map: map,
popup: {
highlightEnabled: false,
dockEnabled: true,
dockOptions: {
breakpoint: false,
position: "top-right"
}
},
center: [-75.325395, 40.306275],
zoom: 5
});
// add widget with drop-down options
mainView.ui.add("optionsDiv", {
position: "bottom-left"
});
//* query all features from the basin layer
mainView
.when(function () {
return basinLayer.when(function () {
var query = basinLayer.createQuery();
return basinLayer.queryFeatures(query);
document.getElementById("doBtn").addEventListener("click", doQuery);
});
})
.then(getValues)
.then(getUniqueValues)
.then(addToSelect)
.then(doQuery)
//* return an array of all the values in the
//* basin name field
function getValues(response) {
var features = response.features;
var values = features.map(function (feature) {
return feature.attributes.MajBas;
});
return values;
}
//* return an array of unique values in
//* the MajBas field of the basin layer
function getUniqueValues(values) {
var uniqueValues = [];
values.forEach(function (item, i) {
if (
(uniqueValues.length < 1 || uniqueValues.indexOf(item) === -1) &&
item !== ""
) {
uniqueValues.push(item);
}
});
return uniqueValues;
}
//* Add the unique values to the basin type
//* select element. This will allow the user
//* to filter basin by name.
function addToSelect(values) {
values.sort();
values.forEach(function (value) {
var option = document.createElement("option");
option.text = value;
basinTypeSelect.add(option);
});
}
//** Call doQuery() each time the button is clicked
mainView.when(function () {
mainView.ui.add("optionsDiv", "bottom-left");
document.getElementById("doBtn").addEventListener("click", doQuery);
});
//**
var attributeName = document.getElementById("MajBas");
// Executes each time the button is clicked
function doQuery() {
// Clear the results from a previous query
resultsLayer.removeAll();
// Build new query
params.where =
"MajBas =" + attributeName.value;
// executes query and calls getResults() once promise is resolved
// promiseRejected() is called if the promise is rejected
qTask.execute(params).then(getResults).catch(promiseRejected);
}
// Called each time the promise is resolved
function getResults(response) {
// Loop through each results and assign a symbol and PopupTemplate
var basinResults = response.features.map(function (feature) {
// Sets the symbol of each resulting feature
feature.symbol = {
type: "simple-fill",
color: [212, 161, 87, 0.25]
};
feature.popupTemplate = popupTemplate;
return feature;
});
resultsLayer.addMany(basinResults);
// print the number of results returned to the user
document.getElementById("printResults").innerHTML =
basinResults.length + " results found!";
}
// Called each time the promise is rejected
function promiseRejected(error) {
console.error("Promise rejected: ", error.message);
}
});
</script>
</head>
<body>
<div id="mainViewDiv"></div>
<div id="optionsDiv">
<h2>Example</h2>
<div id="drop-downs">
<b>Basin</b>
<br/>
<select id="MajBas" class="widget"></select>
</div>
<br/>
<br/>
<button id="doBtn">Search</button> <br />
<p><span id="printResults"></span></p>
</div>
</body>
</html>
EDIT: To clarify, "MajBasin" is the name of the field in the attribute table containing the basin names that are visible in the drop down.
When setting params.where, the field name before the attribute value was missing. Thus, the updated code where the query works is:
params.where =
"MajBas =" + "'" + basinTypeSelect.value + "'";

Creating objects from an eventsource

I'm trying to store the date and the data from an event source to an object containing the coreid and continue to push the data and date to the correct coreid object.
As of now it's storing the wifiData to both of the coreids instead of the corresponding one. How would I push the data to the right id?
<template>
<div class="container">
<h2>Probe Diagnostics</h2>
<div class="row">
<div class="col">
<line-chart id="wifiChart" ytitle="Signal Strength" label="Wifi Strength" :colors="['#b00']" :messages="{empty: 'Waiting for data'}"
:data="wifiData" height="250px" :library="{backgroundColor: '#eee'}" :download="true" :min="-20"
:max="20"></line-chart>
<column-chart :data="wifiData" ytitle="Signal Strength" height="250px"></column-chart>
</div>
<div class="col">
<line-chart :data="psSoc" ytitle="ps-soc" height="250px"></line-chart>
<line-chart :data="psVoltage" ytitle="ps-voltage" height="250px"></line-chart>
</div>
</div>
</div>
</template>
<script>
let wifiData = [];
let psSoc = [];
let psVoltage = [];
let photons = {};
export default {
data() {
return {
wifiData,
psSoc,
psVoltage,
photons,
}
},
mounted() {
this.streamData();
},
methods: {
streamData() {
// LIVE PUSH EVENTS
if (typeof (EventSource) !== "undefined") {
var eventSource = new EventSource(
"http://10.10.10.2:8020/v1/Events/?access_token=687b5aee0b82f6536b65f");
eventSource.addEventListener('open', function (e) {
console.log("Opened connection to event stream!");
}, false);
eventSource.addEventListener('error', function (e) {
console.log("Errored!");
}, false);
eventSource.addEventListener('WiFi Signal', function (e) {
var parsedData = JSON.parse(e.data);
if (parsedData.coreid in photons) {
photons[parsedData.coreid].push([parsedData.published_at, parsedData.data])
return
} else {
photons[parsedData.coreid] =[]
}
}, false);
eventSource.addEventListener('ps-soc', function (e) {
var parsedData = JSON.parse(e.data);
psSoc.push([parsedData.published_at, parsedData.data])
}, false);
eventSource.addEventListener('ps-voltage', function (e) {
var parsedData = JSON.parse(e.data);
psVoltage.push([parsedData.published_at, parsedData.data])
}, false);
}
}
}
}
</script>
Remove wifiData completely. Instead just manage the array directly inside the lookup object:
// Initialize if needed:
if(!photons[parsedData.coreid])
photons[parsedData.coreid] = [];
// Then push directly to it:
photons[parsedData.coreid].push(/*...*/);

Why isn't my template rendering Knockout.js

So, just a little background ... I need to process data in my controller with data that is coming from the Client side. Thus, knockout.js was suggested.
This particular page has a main page, and then multiple placeholders which all have the same format. The main issue is that only one of these is appearing, and that happens to be the last one. When I inspect element, I see the data present, but not rendered.
here is the code:
First ... the markup for the parent item.
#using (Html.BeginFooterScripts())
{
<script type="text/javascript" src="//ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"></script>
<script type="text/javascript" src="/Content/Northwestern/js/_libs/knockout.mapping/knockout.mapping.2.4.1.js"></script>
<script type="text/javascript" src="~/Content/Northwestern/js/views/TabPanel/location-card.js"></script>
<script type="text/javascript">
var once = true;
$(function () {
if (once) {
initialize();
once = false;
}
});
</script>
}
<div class="resp-tabs-container tabs-narrow">
#if (Model.TabSelector.Equals("location-row"))
{
<div>
#*this div needs to be empty. The easyResponsiveTabs plugin adds classes to it and matches it with the <li> in the resp-tab-list above*#
#*Location List *#
<div class="#Model.TabSelector">
<div class="indent">
<h3>#Model.Title</h3>
</div>
#Html.Sitecore().DynamicPlaceholder("Location-Card")
</div>
</div>
}
The DynamicPlaceholder is where the problem is ... currently there are 9 identical Location-Cards
Here is the markup for the LocationCard.cshtml
#using (Html.BeginFooterScripts())
{
<script type="text/javascript" src="~/Content/Northwestern/js/views/TabPanel/location-card.js"></script>
<script>
$(function() {
geoLocate(function(location) {
var latitude = location.coords.latitude;
var longitude = location.coords.longitude;
displayLocation('#Model.LocationId', latitude, longitude);
});
});
</script>
}
<div id="detail-container">
</div>
<script type="text/html" id="location-detail-template">
<h2 class="location-title" itemprop="name" data-bind="text: ItemName">#Model.Location.ItemName</h2>
</div>
<div class="distance">
<i class="material-icons">place</i> <span data-bind="text: Distance.toFixed(1)"> Mi</span>
</div>
<div class="location-phone">
<a data-bind="attr: { 'href': clickToCallify(Phone), 'data-track-event': 'Find a Location - Detail', 'data-track-action': 'call icon' }" class="tel" itemprop="telephone">#Model.Location.Phone</a>
</div>
</div>
</div>
</script>
</div>
<div class="col lg-6 xl-7">
#(new HtmlString(Model.Body))
</div>
</div>
}
and here is the Location-card.js
var applied = false;
var geoLocateError = function onError(error) {
alert(error.message);
};
function ViewModel() {
var self = this;
self.currentLocation = {
latitude: 0,
longitude: 0
};
}
var viewModel = new ViewModel();
$(function () {
});
function initialize() {
ko.applyBindings(viewModel);
geoLocate(function(location) {
initLocation(location);
}, geoLocateError);
}
/**********************************************
* Location Functions
**********************************************/
function initLocation(location) {
viewModel.currentLocation = {
latitude: location.coords.latitude,
longitude: location.coords.longitude
};
}
function displayLocation(id, lat, lng) {
var apiUrl = '/api/northwestern/locations/getlocationbyid/' + id;
var data = {
'latitude': lat,
'longitude': lng
};
self.LocationId = id;
$.getJSON(apiUrl, data, function (response) {
var fragment = document.createDocumentFragment(),
container = document.createElement('div'),
viewModel = response;
fragment.appendChild(container);
// merge together all the display types into a commma-separated list
response.TypeListDisplay = $.map(response.Types, function (obj, t) {
return obj.ItemName;
}).join(', ');
ko.renderTemplate(
"location-detail-template",
viewModel, {
afterRender: function () {
$('#detail-container').html(container.innerHTML);
}
},
container
);
});
}
So, I am not certain what to do. I have been working on this now for several days
any help would be appreciated.

Categories