JavaScript 'Roll-back' to previous case? - javascript

Again, I am making a game but yet I ran into another problem. I have a set of actions a player can do, until they run out of energy. The problem I have is this: When they choose an action, it prompts for another action. Yet, when they type anything into the prompt, it closes itself. I know one solution: Copy and paste the code and basically make the code 6x larger by repeating code. But I wanna know if it's possible to: How can I make it so I can rollback a case in a switch-statement to repeat itself, but keep the current stats? Here's my JavaScript (currently not finished)
var merchants = [{
name : 'Weapons Merchant',
items : [
{type: "weapon", cost: 250, name: "Claymore"},
{type: "weapon", cost: 75, name: "Dagger"},
{type: "weapon", cost: 350, name: "Magic Staff"},
{type: "weapon", cost: 150, name: "Sword"},
{type: "weapon", cost: 125, name: "Bow"},
{type: "weapon", cost: 125, name: "Crossbow"},
{type: "weapon", cost: 5, name: "Arrow"},
{type: "weapon", cost: 15, name: "Bolt"}
]
}, {
name : 'Armor Merchant',
items : [
{type: "clothing", slot: "head", name: "Helmet"},
{type: "clothing", slot: "head", name: "Hood"},
{type: "clothing", slot: "chest", name: "Chestplate"},
{type: "clothing", slot: "chest", name: "Tunic"},
{type: "clothing", slot: "chest", name: "Robe"},
{type: "clothing", slot: "leggings", name: "Legplates"},
{type: "clothing", slot: "leggings", name: "Leggings"},
{type: "clothing", slot: "leggings", name: "Undergarments"},
{type: "clothing", slot: "feet", name: "Boots"},
{type: "clothing", slot: "feet", name: "Armored Boots"}
]
}, {
name: 'Material Merchant',
items: [
{type: "material", name: "Leather", cost: 25},
{type: "material", name: "Iron", cost: 50},
{type: "material", name: "Steel", cost: 75},
{type: "material", name: "Mythril", cost: 100},
{type: "material", name: "Dragonbone", cost: 200}
]
}];
function findMerchant(merchants, name) {
return find(merchants, function(merchant) {
return merchant.name === name;
});
}
function findItem(merchant, type, name) {
return find(merchant.items, function(item) {
return item.type === type && item.name === name;
});
}
function findWithParams(arr, parameters) {
return find(arr, function(item) {
for (var parameter in parameters) {
if (item[parameter] !== parameters[parameter]) {
return false;
}
return true;
}
});
}
function find(arr, predicateFn) {
var found = null;
arr.forEach(function(item, index, items) {
if (predicateFn.apply(undefined, arguments)) {
found = item;
return true;
}
return false;
});
return found;
}
function fight() {
var armorMerchant = findMerchant(merchants, 'Armor Merchant');
var weaponMerchant = findMerchant(merchants, 'Weapons Merchant');
var materialMerchant = findMerchant(merchants, 'Material Merchant');
var energy = 100;
var day = 3;
var money = 1000;
var armor = ['Mythril Chestplate', 'Mythril Legplates', 'Leather Hood'];
var weapon = [];
var intro = prompt("You are a hero who saved your town from a dragon attack years ago. You had fun murdering that dragon, but sadly no dragon has attacked since. Just when all hope is lo, you hear the sirens ring through the city. You know what that means. Do you PREPARE, or IGNORE THE SIRENS?").toUpperCase();
switch(intro) {
case 'PREPARE':
if(intro === "PREPARE") {
var decision1 = prompt("You decided to " + intro + ". You need to choose what you will do. Keep in mind, the more activities you do, the less energy you have! You only have 3 days to prepare! What do you do? Do you SEARCH ARMOR STAND, SEARCH WEAPON STAND, GO TO MERCHANT, FIGHT DRAGON, TRAIN, or SLEEP?").toUpperCase();
if(decision1 === "SEARCH ARMOR STAND" && energy >= 5) {
energy -= 5;
prompt("You check your armor rack, but happen to stub your toe on the way. You now have " + energy + " energy. You manage to check your armor rack, and you currently have: " + armor + " on your rack. What do you do now? SEARCH ARMOR STAND, SEARCH WEAPON STAND, GO TO MERCHANT, FIGHT DRAGON, TRAIN, or SLEEP?")
}
else {
alert("You don't have enough Energy!");
}
if(decision1 === "SEARCH WEAPON STAND" && energy >= 10) {
energy -= 10;
prompt("You go downstairs to search your weapon stand. Your weapon stand includes: " + weapon + ". On your way back up, you cut your finger on the broken piece of glass. You now have " + energy + " energy. What do you do now? SEARCH ARMOR STAND, SEARCH WEAPON STAND, GO TO MERCHANT, FIGHT DRAGON, TRAIN, or SLEEP?")
}
}
}
}
#import url(http://fonts.googleapis.com/css?family=Permanent+Marker);
html, body {
background: #000;
margin: 0;
padding: 0;
}
#wrap {
width: 760px;
margin-left: auto;
margin-right: auto;
}
.container {
position: relative;
top: 50px;
margin-left: auto;
margin-right: auto;
width: 570px;
height: 350px;
border: 6px ridge orange;
padding: 0;
}
.container img {
position: absolute;
bottom: 0px;
width: 570px;
height: 350px;
z-index: -1;
}
p.intro {
color: black;
text-shadow:
-1px -1px 0 #FFF,
1px -1px 0 #FFF,
-1px 1px 0 #FFF,
1px 1px 0 #FFF;
}
h2.header {
text-shadow:
-1px -1px 0 #FFA500,
1px -1px 0 #FFA500,
-1px 1px 0 #FFA500,
1px 1px 0 #FFA500;
}
.box {
float: left;
min-width: 567px;
min-height: 350px;
}
.box h2 {
font-family: 'Permanent Marker', cursive;
font-size: 200%;
text-align: center;
}
.box p {
font-family: 'Permanent Marker', arial;
text-align: center;
}
.box a {
position: absolute;
left: 165px;
display: inline-block;
border: 3px groove #000;
border-radius: 5px;
background: red;
margin-left: auto;
margin-right: auto;
width: 225px;
height: 75px;
font-family: 'Permanent Marker', cursive;
color: #FFA500;
text-shadow:
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;
text-align: center;
}
.battles img {
}
<html>
<body>
<div id="wrap">
<div class="box">
<div class="container">
<h2 class="header">Dragon Slayer - REBORN!</h2>
<p class="intro">You are a hero who saved your town from a dragon attack years ago. You had fun murdering that dragon, but sadly no dragon has attacked since. Just when all hope is lost, you hear the sirens ring through the city. You know what that means.</p>
<br>BEGIN!
<img class="scenario" src="http://www.thegaminghideout.com/school/stage1.png">
<div class="battles">
</div>
</div>
</div>
</div>
</body>
</html>
THINGS TO NOTE
If possible, when the case rolls back to the previous prompt/step, it should keep the current energy/day variable, too.
If possible, can it just go back to the previous prompt?
If not possible, how can I shorten the code as much as possible to make it meet my requirement?

Related

Count the subtotal from list select option, based on clicked (' active ') element

This is my goal:
Before anything clicked || After user select the item that they want
After i done some research, i was able to make this with vue.js
https://jsfiddle.net/Hanstopz/Lcnxtg51/10/
But, when i trid to create this with Plain javascript, i was stuck
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Form pesanan</title>
<style>
#import url(https://fonts.googleapis.com/css?family=Cookie);
[v-cloak] {
display: none;
}
*{
margin:0;
padding:0;
}
body{
font:15px/1.3 'Open Sans', sans-serif;
color: #5e5b64;
text-align:center;
}
a, a:visited {
outline:none;
color:#389dc1;
}
a:hover{
text-decoration:none;
}
section, footer, header, aside, nav{
display: block;
}
form{
background-color: #61a1bc;
border-radius: 2px;
box-shadow: 0 1px 1px #ccc;
width: 400px;
padding: 35px 60px;
margin: 50px auto;
}
form h1{
color:#fff;
font-size:64px;
font-family:'Cookie', cursive;
font-weight: normal;
line-height:1;
text-shadow:0 3px 0 rgba(0,0,0,0.1);
}
form ul{
list-style:none;
font-size:20px;
font-weight:bold;
text-align: left;
margin:20px 0 15px;
}
form ul li{
padding:20px 30px;
background-color:#f0f0f0;
margin-bottom:8px;
box-shadow:0 1px 1px rgba(0,0,0,0.1);
cursor:pointer;
}
form ul li span{
float:right;
}
/* ini bagian v-bind 'active' (menambahkan class ini ke li) */
.active{
color: white;
background-color: darkgreen;
}
div.total{
border-top:1px solid rgba(255,255,255,0.5);
padding:15px 30px;
font-size:20px;
font-weight:bold;
text-align: left;
color:#fff;
}
div.total span{
float:right;
}
</style>
</head>
<body>
<form id="main">
<h1>Menu kami</h1>
<ul id="menu">
<!-- {{service.name}} <span>{{service.price | currency}}</span> -->
</ul>
<div class="total">
Total harga:
<!-- Total: <span>{{total() | currency}}</span> -->
</div>
</form>
<script>
const menu = [
{ name: 'Cappucino', price: 35000, active: true },
{ name: 'Green tea latte', price: 40000, active: false },
{ name: 'Fish and chips', price: 50000, active: false },
{ name: 'Tuna sandwich', price: 30000, active: false },
{ name: 'Mineral water', price: 8000, active: false },
{ name: 'French fries', price: 18000, active: false }
]
menu.forEach((menu, price) => {
document.getElementById('menu').innerHTML += "<li>" + menu.name + "<span>" + "Rp " + menu.price + "</span>" + "</li>"
});
</script>
</body>
</html>
i managed to create the display from array, but i get confused on how do i add an active class when i click on the list and a function to calculate all active element... Anyone please help me.
{UPDATE}
after i combined the answer from Nabir Abbas, i can select and toggle between active and not active element. But i wanna make a total that can be counted whenever it has an active element. The price would be taken from the array based on active link... so i tried this
function totalCount() {
if ($("li").hasClass("active")) {
document.getElementById("subtotal").innerHTML = 0 + $(menu.price);
}}
but it does not seems right, can anyone help me?
This should work:
const menu = [{
id: 1,
name: 'Cappucino',
price: 35000,
active: true
},
{
id: 2,
name: 'Green tea latte',
price: 40000,
active: false
},
{
id: 3,
name: 'Fish and chips',
price: 50000,
active: false
},
{
id: 4,
name: 'Tuna sandwich',
price: 30000,
active: false
},
{
id: 5,
name: 'Mineral water',
price: 8000,
active: false
},
{
id: 6,
name: 'French fries',
price: 18000,
active: false
}
]
menu.forEach((menu, price) => {
const li = document.createElement('li')
li.innerHTML = `${menu.name}<span>Rp ${menu.price}</span>`
li.addEventListener('click', e => {
li.classList.toggle('active')
console.log('Active lements', getActiveCount())
})
document.getElementById('menu').append(li)
});
function getActiveCount() {
const activeListItems = document.querySelectorAll('#menu li.active')
return activeListItems.length
}
Here we are adding the event listener to per element on the go before appending them to the parent. Also, notice we are using classList.toggle which removes the active class if it exists on the list item and adds it if it doesn't
Edit 10/7/2020
To get the total price of the active elements, first you have to add the item price as an attribute to per li, you can use the following function:
so the above code becomes:
menu.forEach(item => {
const li = document.createElement('li')
li.innerHTML = `${menu.name}<span>Rp ${menu.price}</span>`
li.setAttribute('data-price', item.price)
li.addEventListener('click', e => {
li.classList.toggle('active')
console.log('Active lements', getActiveCount())
})
document.getElementById('menu').append(li)
});
Then:
function getTotalPrice() {
const activeItems = document.querySelectorAll('#menu li.active')
return activeItems.length ? Array.from(activeItems).reduce((acc, elem) => acc+=elem.getAttribute('data-price'), 0): 0
}

How to call method on click as opposed to on v-for in Vuejs

I'm trying to display this array of objects based on highest to lowest rating of each item. It works fine using the method on the v-for, but I would really like to display the list of objects in the order they appear initially and THEN call a function that displays them by highest to lowest rating.
When I have it set as v-for="item in items" and then try to call the method on a button, such as #click="rated(items)", nothing happens. Why would I be able to display the array initially on the v-for with the method attached, but not on a click event?
const items = [
{
name: "Bert",
rating: 2.25
},
{
name: "Ernie",
rating: 4.6
},
{
name: "Elmo",
rating: 8.75
},
{
name: "Rosita",
rating: 2.75
},
{
name: "Abby",
rating: 9.5
},
{
name: "Cookie Monster",
rating: 5.75
},
{
name: "Oscar",
rating: 6.75
}
]
new Vue({
el: "#app",
data: {
items: items
},
methods: {
rated: function(items) {
return items.slice().sort(function(a, b) {
return b.rating - a.rating;
});
},
sortByRating: function(items) {
return items.slice().sort(function(a, b) {
return b.rating - a.rating;
});
}
}
});
#app {
display: flex;
flex-flow: row wrap;
margin-top: 3rem;
}
.item {
flex: 1;
margin: .5rem;
background: #eee;
box-shadow: 0px 2px 4px rgba(0,0,0,.5);
padding: 1rem;
min-width: 20vw;
}
.toggle {
position: absolute;
top: 10px;
left: 10px;
padding: .5rem 1rem;
background: DarkOrchid;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="item in rated(items)"
class="item">
<p>{{item.name}}</p>
<p>{{item.rating}}</p>
</div>
</div>
Try to rewrite the array for the result returned by the method, like this.
#click="items = rated(items)
and, inside v-for you can keep using items.

DataTables conditional formatting bars

I develop the app that uses datatables to visualize certain statistics data.
I receive from API the data in the following format:
{
"data": [{
"companyName": "company1",
"growth": 15
},
{
"companyName": "company2",
"growth": -8
},
{
"companyName": "company3",
"growth": 23
}]
}
Which is essentially a company name and its year over year revenue growth figure (%).
What I would like to implement is an MS Excel-like conditional formatting feature that displays colored bars inside percentage cells (color of the bar is red for negative and green for positive values and its size is normalized to minimal/maximal value and column width).
So far, my HTML is:
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js" charset="utf8"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.js"></script>
<table id="revenue-growth"></table>
And jQuery:
$('#revenue-growth').DataTable({
ajax: {
url: 'https://192.168.0.1/revenue',
method: 'GET',
dataSrc: 'data'
}
dom: 'Bfrtip',
pageLength: 50,
order: [0, 'asc']
columDefs: [{
targets: 0,
data: 'companyName',
title: 'Company Name'
}, {
targets: 1,
data: 'growth',
title: 'YoY revenue growth, %'
}
]
})
So, my question is: is there a way to implement such feature from the ground up, or, maybe, there's already plug-in that does exactly this?
I didn't find anything matching my request here, at stackoverflow, nor I was able to identify DataTables API methods that may solve my problem, so I would really appreciate that if you could point me to the right direction.
You could use the render-callback to create bars inside the cells in the growth-column.
You have to find the maximum growth inside the dataset.
In the render-callback:
2.1. check if the growth is negative or positive.
2.2. calculate bar width-% based on the growth and the maximum.
2.3. create and append the elements based on these information.
Here is an simple example:
$(document).ready(function() {
const dataSet = [
{ companyName: "Company A", growth: -12 },
{ companyName: "Company B", growth: 31 },
{ companyName: "Company C", growth: 7 },
{ companyName: "Company D", growth: 0 },
{ companyName: "Company E", growth: -29 },
{ companyName: "Company F", growth: 23 },
];
// Get absolute maximum value of grwoth from dataset
const maxGrowthValue = Math.max.apply(Math, dataSet.map(function(item) { return Math.abs(item.growth); }));
const table = $('#example').DataTable({
data: dataSet,
columns: [
{
data: 'companyName',
title: "Company Name",
},
{
data: 'growth',
title: "YoY revenue growth, %",
// Custom render the cell of the growth-column
render: (data, type, row, meta) => {
const isPositive = (Number(data) > 0);
const barWidth = 100 / maxGrowthValue * Math.abs(Number(data)) * 0.5;
const $growthBarContainer = $('<div>', {
class: "growth-bar",
});
const $growthBar = $('<div>', {
class: "bar bar-" + (isPositive ? 'positive' : 'negative'),
});
$growthBar.css({
width: barWidth.toFixed(2) + '%',
});
$growthBarContainer.append($growthBar);
$growthNumber = $('<div>', {
class: "growth-number",
});
$growthNumber.text(data + '%');
$growthBarContainer.append($growthNumber);
return $growthBarContainer.prop("outerHTML");
},
},
],
});
});
.growth-bar {
display: inline-block;
width: 120px;
height: 12px;
position: relative;
background-color: #eee;
border: 1px solid #424242;
}
.growth-bar > .bar {
width: 0%;
height: 100%;
position: absolute;
}
.growth-bar > .bar.bar-negative {
right: 50%;
background-color: red;
}
.growth-bar > .bar.bar-positive {
left: 50%;
background-color: green;
}
.growth-bar > .growth-number {
position: absolute;
top: 1px;
right: 2px;
color: #fff;
/* shadow for better readability */
text-shadow: 0px -1px 0px rgba(0,0,0,.5), 0px 1px 0px rgba(0,0,0,.5), -1px 0px 0px rgba(0,0,0,.5), 1px 0px 0px rgba(0,0,0,.5), 0px 0px 1px rgba(0,0,0,.25), 0px 0px 2px rgba(0,0,0,.5), 0px 0px 3px rgba(0,0,0,.75);
font-size: 10px;
line-height: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css">
<script src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<table id="example" class="display" style="width:100%"></table>

Google map fails to load if async

whenever I try to load the google map asynchronously it fails to load until I reload the page multiple times.after Removing async from the the code runs well.
I have used knockout.js and Foursquare API for the work.
//strict
//JavaScript file
$(document).ready(function() {
//initialize map
var map;
//if no error occours
if (typeof google != "undefined") {
map = new google.maps.Map(document.getElementById('display-area'), {
center: new google.maps.LatLng(30.4100, 77.98000),
zoom: 11,
});
} //else the error message will be displayed
else {
$('#error').css('display', 'block'); //error handling if map loading fails
$('#error').css('color', 'red'); //error handling color set to red
$('#display_area').css('display', 'none'); //map loading fails
}
//display list of places
$('#view-list-toggle').click(function() {
var list = $('#view-list');
//view-list is initially set to display:none, when any marker is clicked it has to be displayed
list.css('display', 'block');
});
//array of locations where the markers are to be created
//loc is the location
var locDetails = [{
loc: new google.maps.LatLng(30.416333, 77.968585),
title: "UPES Dehradun",
details: '<b>University of Petroleum and Energy Studies, Dehradun. My place!:)</b><br><img src="images/upes.jpg" width=100%>',
venues: "4e61e0d1e4cdf1e2bf0ddd66",
searchOptions: ['college', 'petroleum']
}, //venues are used in foursquareURL to get the location and rating of the place.
//searchOptions provide a easier search,
{
loc: new google.maps.LatLng(30.34266547538908, 78.0602590943995),
title: "Silver City Multiplex",
details: '<b>Silver City Multiplex</b><br>A perfect shopping and movie hall for fun<br><img src="images/sc.jpg" width=100%>',
venues: "4efad823469012b8c3fcad8c",
searchOptions: ['mall', 'shopping']
},
{
loc: new google.maps.LatLng(30.33308670383727, 77.99534667810693),
title: "Forest research institute",
details: '<b>The Forest Research Institute</b><br> It is an institute of the Indian Council of Forestry Research and Education and is a premier institution in the field of forestry research in India.<br><img src="images/fri.jpg" width=100%>',
venues: "56c95d90cd10173286d99b0c",
searchOptions: ['institute', 'places']
},
{
loc: new google.maps.LatLng(30.3572660, 78.0166512),
title: "Tapkeshwar Mandir",
details: '<b>Tapkeshwar Mandir</b><br>Water drips from the cave walls of this famous, tranquil Hindu temple honoring Lord Shiva<br><img src="images/tap.jpg" width=100%>',
venues: "516fc256e4b03bca88777ab4",
searchOptions: ['places', 'temple']
},
{
loc: new google.maps.LatLng(30.334597469681178, 77.98139035061027),
title: "IMA",
details: '<b>The Indian Military Academy</b><br> Dehradun is the officer training Academy of the Indian Army. IMA was established in 1932<br><img src="images/ima.jpg" width=100%>',
venues: "4d8f0723835b530cbf6fa5b6",
searchOptions: ['acedemy', 'army']
},
{
loc: new google.maps.LatLng(30.340292, 77.952410),
title: "Uttaranchal University",
details: '<b>Uttaranchal University</b><br>It is a State Private university established by the Government of Uttarakhand and approverd by UGC.<br><img src="images/uti.jpg" width=100%>',
venues: "57f4b07d498e1f07ac71068e",
searchOptions: ['university', 'study']
},
{
loc: new google.maps.LatLng(30.322101132963066, 78.00289561203648),
title: "Big Cinemas",
details: '<b>Carnival Cinemas-Vikas mall</b><br>A mall, where you can easily find fun stuffs like food and movies<br><img src="images/car.png" width=100%>',
venues: "50cc6304e4b0af445c617e6f",
searchOptions: ['movie', 'shop']
},
{
loc: new google.maps.LatLng(30.409601848936052, 77.97009795321478),
title: "Sai Mandir",
details: '<b>Temple</b><br>A hindu temple near university of petroleum and energy studies',
venues: "513886f1e4b019e1c4745c8b",
searchOptions: ['temple', 'temples']
},
{
loc: new google.maps.LatLng(30.488112761304816, 78.03671992371376),
title: "Kempty Fall",
details: '<b>Kempty Fall</b><br>A very Calm and Soothing place as a family picnic destination.<br><img src="images/kempty.jpg" width=100%>',
venues: "4e8fe25d02d5ee38b690c60c",
searchOptions: ['water', 'fall']
},
{
loc: new google.maps.LatLng(30.459536, 78.020681),
title: "George everest",
details: '<b>George everest</b><br>Beautiful location peace of mind relaxing place worth a visit<br><img src="images/ge.jpg" width=100%>',
venues: "4fba04bce4b09a2fd4f5b957",
searchOptions: ['hill', 'mountains']
}
];
function MVM() {
var temp = []; //will be used to store the markers
var self = this;
for (var j = 0; j < locDetails.length; j++)
temp.push(new finalMarker(locDetails[j], self)); //
self.query = ko.observable('');
self.markers = ko.observableArray(temp);
self.fName = ko.observable('');
self.fRating = ko.observable('');
self.kill = function() {
for (var i = 0; i < self.markers().length; i++)
self.markers()[i].startState();
};
self.searchMarkers = function() {
// find a list of markers that match the string
var search_matches = [];
//a list of markers that do not matches and needs to be hidden
var search_not_match = [];
var query = self.query();
for (var k = 0; k < self.markers().length; k++) {
var mrkr = self.markers()[k];
if (mrkr.matches(query))
search_matches.push(mrkr);
else
search_not_match.push(mrkr);
}
for (i = 0; i < search_matches.length; i++)
search_matches[i].matchesSearch();
//if search matches then the list is set to be visible;
for (i = 0; i < search_not_match.length; i++)
search_not_match[i].doNotMatch();
//all the remaining is set to be hidden
};
self.selectItem = function(item) {
item.selected();
};
}
function finalMarker(marker_info, model) {
var self = this;
self.model = model;
self.title = marker_info.title;
self.venues = marker_info.venues;
self.text = ko.observable(marker_info.details);
self.searchOptions = marker_info.searchOptions;
var infoParts = self.text().split(" "); //splits the infopart and add it to searchOptions, it will help in making the search more easier to users.
for (var i = 0; i < infoParts.length; i++)
self.searchOptions.push(infoParts[i].toLowerCase());
var titleParts = self.title.split(" "); //splits the title and add it to searchOptions, it will help in making the search more easier to users.
for (i = 0; i < titleParts.length; i++)
self.searchOptions.push(titleParts[i].toLowerCase());
self.infowindow = new google.maps.InfoWindow({
content: self.text()
});
self.googleMarker = new google.maps.Marker({
position: marker_info.loc,
title: marker_info.title,
map: map,
animation: google.maps.Animation.DROP
});
//when any one of the markers on the map is clicked then this function is called.
google.maps.event.addListener(self.googleMarker, 'click', function() {
self.selected();
self.toggleBounce();
//animate the markers when loaded.
function toggleBounce() {
if (marker.getAnimation() !== null) {
marker.setAnimation(null);
} else {
marker.setAnimation(google.maps.Animation.BOUNCE);
}
}
});
self.selected = function() {
// kills/closes all the other window popups
model.kill();
self.clicked();
$('#four-view').css('display', 'block');
//url for foursquare
var foursquareUrl = 'https://api.foursquare.com/v2/venues/' + self.venues + '?v=20170503&' + 'client_id=' + 'I5E3UJKIUR03YMFPIC0DEML402JJLEH2ZBN2MJMVY1CERFZA' + '&client_secret=' + 'J10CMGOLXC24EJTBBMW5DUD3O5DDXJ5E5Y134MAFQG15AYNQ';
model.fRating('');
//foursquare api settingz
//id = "I5E3UJKIUR03YMFPIC0DEML402JJLEH2ZBN2MJMVY1CERFZA";
//Secret = "J10CMGOLXC24EJTBBMW5DUD3O5DDXJ5E5Y134MAFQG15AYNQ";
model.fName('');
//ajax async. loads the data when the marker is clicked
$.ajax({
url: foursquareUrl
}).done(
function(response) {
var res = response.response.venue;
model.fName(res.name);
model.fRating(res.rating);
if (!res.rating) {
model.fRating('not yet rated');
} else
model.fRating('rated as ' + res.rating);
})
.error(function() {
model.fName('error');
model.fRating('error');
});
};
self.matches = function(q) {
return $.inArray(q.toLowerCase(), self.searchOptions) != -1;
};
self.matchesSearch = function() {
self.isHidden(false);
self.googleMarker.setVisible(true);
self.infowindow.open(map, self.googleMarker);
$('#four-view').css('display', 'none');
};
self.startState = function() {
self.isHidden(false);
self.googleMarker.setVisible(true);
self.infowindow.close();
$('#four-view').css('display', 'none');
};
self.doNotMatch = function() {
self.isHidden(true);
self.googleMarker.setVisible(false);
self.infowindow.close();
$('#four-view').css('display', 'none');
};
self.clicked = function() {
self.isHidden(false);
self.googleMarker.setVisible(true);
self.googleMarker.setAnimation(google.maps.Animation.BOUNCE);
setTimeout(function() {
self.googleMarker.setAnimation(null);
}, 1400);
self.infowindow.open(map, self.googleMarker);
};
self.isHidden = ko.observable(false);
}
ko.applyBindings(new MVM());
});
html,
body {
height: 99%;
margin: 0px;
padding-left: 10px;
padding-right: 10px;
background: linear-gradient(27deg, #151515 5px, transparent 5px) 0 5px, linear-gradient(207deg, #151515 5px, transparent 5px) 10px 0px, linear-gradient(27deg, #222 5px, transparent 5px) 0px 10px, linear-gradient(207deg, #222 5px, transparent 5px) 10px 5px, linear-gradient(90deg, #1b1b1b 10px, transparent 10px), linear-gradient(#1d1d1d 25%, #1a1a1a 25%, #1a1a1a 50%, transparent 50%, transparent 75%, #242424 75%, #242424);
background-color: #131313;
background-size: 20px 20px;
}
input {
width: 20%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45), 0 1px 3px rgba(0, 0, 0, 0.44);
border: 1px solid black;
position: relative;
padding-bottom: 2px;
margin-bottom: 2px;
}
h1 {
color: white;
}
#media only screen and (min-width: 506px) {
#view-list {
display: block !important;
}
}
#display-area {
height: 100%;
width: 100%;
margin: 5px;
padding: 5px;
}
#view-list {
top: 30%;
right: 0px;
position: absolute;
font-weight: bold;
color: white;
background: linear-gradient(27deg, #151515 5px, transparent 5px) 0 5px, linear-gradient(207deg, #151515 5px, transparent 5px) 10px 0px, linear-gradient(27deg, #222 5px, transparent 5px) 0px 10px, linear-gradient(207deg, #222 5px, transparent 5px) 10px 5px, linear-gradient(90deg, #1b1b1b 10px, transparent 10px), linear-gradient(#1d1d1d 25%, #1a1a1a 25%, #1a1a1a 50%, transparent 50%, transparent 75%, #242424 75%, #242424);
background-color: #131313;
background-size: 20px 20px;
box-shadow: 10px 4px 13px rgba(0, 0, 0, 0.45), 5px 10px 10px rgba(0, 0, 0, 0.64);
border: 1px solid black;
}
#four-view {
padding: 5px;
color: grey;
display: none;
text-align: center;
-ms-transform: translateY(-50%);
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.hidden {
display: none;
}
.list-item:hover {
background-color: grey;
}
#view-list-toggle {
display: none;
background-color: grey;
padding: 5px;
}
#media only screen and (max-width: 528px) {
h1 {
display: none;
}
#display-area {
height: 100%;
}
#four-view {
padding-top: 10px;
margin-top: 10px;
}
#view-list {
font-size: 10px;
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>Neighbourhood</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="css/main.css" />
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDy2Gh9s53u0nitvdqECa5cevawqiBwkP0&callback=initMap"></script>
</head>
<body bgcolor="grey">
<h1 align="center">Neighbourhood Project - Udacity</h1>
<form action="/" align="right">
<input data-bind="value: query" maxlength="100" />
<button data-bind="click: searchMarkers">Search List</button>
</form>
<div id="four-view" class="rating">
<strong data-bind="text: fName"></strong> is <strong data-bind="text: fRating" style="color: white;"></strong> by foursquare.
</div>
<div id="view-list-toggle">List View</div>
<div id="display-area"></div>
<div id="view-list">
<table>
<tbody data-bind="foreach: markers()">
<tr data-bind="click: $root.selectItem">
<td data-bind="text: title, css : { hidden : isHidden() }" class='list-item'></td>
</tr>
</tbody>
</table>
</div>
<div id="error" style="display : none; color : green">Oops!! The map could not be loaded. Please check your connection.</div>
<script type='text/javascript' src='js/knockout-3.3.0.js'></script>
<script type='text/javascript' src='js/jquery-2.1.3.min.js'></script>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>
If you are using jQuery, you can asynchronously load google maps using jQuery's $.getScript() method.
Here is an example of how you would do this:
function initMap() {
// logic for creating map
}
var mapsUrl = "https://maps.googleapis.com/maps/api/js?key=" + "Your_API_KEY" + "&v=3";
$.getScript(mapsUrl)
.done(function() {
initMap();
})
.fail(function() {
console.log('Error: Map failed to load.');
});

Why is ngDraggable only working half the time in angular ng-repeat

For the life of me i can't figure out why my images are only sometimes drag/droppable. It seems like when i am unable to drop, clicking on the image once before starting to drag it works (sometimes). I had thought that it was a focus issue so i tried using jQuery to focus on the ng-drag div in the call to the drop-success function.
I also kept changing the css, and i have a feeling that may have something to do with it, but then again im unsure if its some type of broadcasting problem.
I didn't touch the ngDraggable.js file at all......
Mobile seems to be fine, but on desktop this is very strange and seems to be intermittent. Please help. Link to my github repo:
https://github.com/TransientCW/BabyBox-Angular
html:
<div class="row chest" style="text-align:center;margin: 0 auto;width:95%;">
<div style="text-align:center;" class=".col-lg-1 imageChest">
<ul style="list-style:none; width:100%; text-align:center;margin:0 auto;padding:0;" class="draggable-objects" id="theList">
<li ng-repeat="image in gameCont.images track by $index"
class="item"
ng-drag="true"
ng-drag-data="image"
ng-drag-success="gameCont.onDragSuccess($data, $event, $index)"
style="height:110px;">
<img class="thumbnail"
ng-src="{{image.imgSrc}}"
width="75" height="75"/>
<span class="itemName"><h5>{{image.name}}</h5></span>
</li>
</ul>
</div>
<div class="innerChest" style="text-align:center;margin: 0 auto;width:90%;">
<span style="color:white;"><h4>Please place your 5 preferred images below</h4></span>
<div ng-drop="true"
ng-drop-success="gameCont.onDropComplete($data, $event, $index)"
style="width: 100%; height: 100%;
border:1px solid black;position:relative;bottom:21px;">
<ul style="list-style:none;">
<li ng-repeat="image in gameCont.selectedImages track by $index"
class="item">
<img class="thumbnail"
ng-src="{{image.imgSrc}}"
width="65" height="65">
</li>
</ul>
</div>
</div>
</div>
</br>
<div class="row" style="text-align:center;position:relative;bottom:95px;">
<button type="submit" class="btn btn-default" ng-click="gameCont.submitImages()">Submit</button>
<button type="submit" class="btn btn-default" ng-click="gameCont.reset()">Reset</button>
</div>
css:
body {
padding: 0px;
margin: 0px;
}
.item {
display:inline-block;
width: 75px;
height:75px;
margin: 15px;
vertical-align:top;
}
.headline {
color: black;
}
.row.chest {
position:relative;
background-image: url('../img/Empty-Box.png');
background-repeat: no-repeat;
background-size:contain;
background-position:center;
height: 705px;
width: 534px;
position:relative;
text-align:center;
}
.col-lg-1.imageChest {
position:absolute;
top:150px;
width: 100%;
text-align:center;
}
.innerChest {
position: absolute;
top: 480px;
left:60px;
width: 200px;
height: 120px;
padding: 5px;
text-align:center;
}
.itemName {
color:purple;
text-shadow:1px 1px 10px white;
}
*{
-moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;
}
[ng-drag]{
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
[ng-drag]{
width: 100px;
height: 100px;
color:white;
text-align: center;
display: inline-block;
margin:0 10px;
cursor: move;
}
ul.draggable-objects:after{
display: block;
content:"";
clear:both;
}
.draggable-objects li{
float:left;
display: block;
width: 75px;
height:75px;
}
[ng-drag].drag-over{
border:0;
}
[ng-drag].dragging{
opacity: 0.5;
}
[ng-drop]{
text-align: center;
display: block;
margin: 20px auto;
position: relative;
width: 600px;
height: 200px;
display: block;
margin:20px auto;
position: relative;
}
[ng-drop].drag-enter{
border:0;
}
[ng-drop] span.title{
display: block;
position: absolute;
top:50%;
left:50%;
width: 200px;
height: 20px;
margin-left: -100px;
margin-top: -10px;
}
[ng-drop] div{
position: relative;
z-index: 2;
}
angular controller:
/**
* Created by cdub on 9/10/2016.
*/
angular.module('gameBox', ['ngRoute', 'ngDraggable'])
.controller('gameBoxController', [
'$window',
'$location',
'imageFactory',
gbControllerFunc
]);
function gbControllerFunc($window, $location, imageFactory) {
console.log('game controller set');
var gameCont = this;
gameCont.selectedFull = false;
gameCont.imageNames = [];
gameCont.errorRefresh = imageFactory.errorRefresh;
gameCont.submitImages = imageFactory.submit;
//This is the list of selectable, draggable objects
gameCont.images = [];
loadAllFactoryImages();
//This is the empty list, which will have dragged objects pushed into it
gameCont.selectedImages = imageFactory.selectedImages;
/**
* Function for loading/reloading copy of original factory array
*/
function loadAllFactoryImages() {
for(var i = 0; i < imageFactory.images.length; i++) {
gameCont.images.push(imageFactory.images[i]);
}
}
/**
* Find the object in the array, return its index for splicing
* #param obj
* #param arr
* #returns {number}
*/
function searchImages(obj) {
console.log('looking for name: ', obj.name);
console.log('in array: ', gameCont.images);
for(var i = 0; i < gameCont.images.length; i++) {
console.log(gameCont.images[i].name);
if(gameCont.images[i].name === obj.name) {
return i;
}
}
return -1;
}
/**
* Function to send email with the images that the user selected
*/
gameCont.submitImages = function() {
console.log('images submitted');
$location.path('/success');
};
/**
* Reset all arrays and reload home view
*/
gameCont.reset = function() {
console.log('resetting');
gameCont.images = [];
loadAllFactoryImages();
gameCont.selectedImages = [];
$window.location.reload();
};
gameCont.centerAnchor = true;
gameCont.toggleCenterAnchor = function () {gameCont.centerAnchor = !gameCont.centerAnchor};
gameCont.onDropComplete=function(data,evt){
if(gameCont.selectedImages.length < 5 ) {
var index = searchImages(data, gameCont.images);
console.log('index returned is: ', index);
gameCont.images.splice(index, 1);
gameCont.selectedImages.push(data);
gameCont.imageNames.push(data.name);
console.log('images up top: ', gameCont.images);
} else {
console.log('selected images full');
}
angular.element('#theList').focus();
};
gameCont.onDragSuccess=function(data, evt, $index){
console.log("133","$scope","onDragSuccess", "", evt);
delete($index);
};
}
angular image factory:
/**
* Created by cdub on 9/10/2016.
*/
angular.module('gameBox')
.factory('imageFactory', [
'$location',
imgFactoryFunc
]);
function imgFactoryFunc($location) {
console.log('factory loaded up');
var fact = this;
//Image object array with URL's for ng-repeat
fact.imgArray = [
{
name: 'Bib',
imgSrc: 'img/Composite/Accessory - Bib.jpg'
},
{
name: 'Hat',
imgSrc: 'img/Composite/Accessory - Hat.jpg'
},
{
name: 'Mittens',
imgSrc: 'img/Composite/Accessory - Mittens.jpg'
},
{
name: 'Socks',
imgSrc: 'img/Composite/Accessory - Socks.gif'
},
{
name: 'Toy',
imgSrc: 'img/Composite/Accessory - Toy.jpg'
},
{
name: 'Short Onesie 1',
imgSrc: 'img/Composite/Onesies - Short.jpg'
},
{
name: 'Short Onesie 2',
imgSrc: 'img/Composite/Onesies - Short 2.jpg'
},
{
name: 'Long Onesie',
imgSrc: 'img/Composite/Onesies - Long.jpg'
},
{
name: 'Booties',
imgSrc: 'img/Composite/Outerwear - Booties.jpeg'
},
{
name: 'Cardigan',
imgSrc: 'img/Composite/Outerwear - Cardigan.jpg'
},
{
name: 'Jacket',
imgSrc: 'img/Composite/Outerwear - Jacket.jpg'
},
{
name: 'Romper 1',
imgSrc: 'img/Composite/Outfits - Romper 1.jpg'
},
{
name: 'Romper2',
imgSrc: 'img/Composite/Outfits - Romper 2.jpg'
},
{
name: 'Gown',
imgSrc: 'img/Composite/Outfits - Gown.jpg'
},
{
name: 'Long Sleeve Dress',
imgSrc: 'img/Composite/Outfits - Long Sleeve Dress.jpg'
},
{
name: 'Top & Bottom 1',
imgSrc: 'img/Composite/Outfits - Top & Bottom.jpg'
},
{
name: 'Top & Bottom 2',
imgSrc: 'img/Composite/Outfits - Top & Bottom 2.jpg'
},
{
name: 'Tutu',
imgSrc: 'img/Composite/Outfits - Tutu.jpg'
},
{
name: 'Tank Dress',
imgSrc: 'img/Composite/Outfits - Tank dress.jpg'
},
{
name: 'Cotton Long Pants',
imgSrc: 'img/Composite/Pants - Cotton Long.jpg'
},
{
name: 'Denim Pants',
imgSrc: 'img/Composite/Pants - Denim.jpg'
},
{
name: 'Long Pants',
imgSrc: 'img/Composite/Pants - Long.jpg'
},
{
name: 'Short Pants',
imgSrc: 'img/Composite/Pants - Shorts.jpg'
},
{
name: 'Button Down Shirt',
imgSrc: 'img/Composite/Shirts - Button Down.png'
},
{
name: 'Graphic Tee Shirt',
imgSrc: 'img/Composite/Shirts - Graphic Tee.jpg'
},
{
name: 'Kimono Shirt',
imgSrc: 'img/Composite/Shirts - Kimono.jpg'
},
{
name: 'Polo Shirt',
imgSrc: 'img/Composite/Shirts - Polo Shirt.jpg'
},
{
name: 'Tank Shirt',
imgSrc: 'img/Composite/Shirts - Tank.jpg'
}
];
// Empty array to hold dropped image objects
fact.selectedImages = [];
return {
images : fact.imgArray,
selectedImages: fact.selectedImages,
errorRefresh: function() {
console.log('Refresh button clicked after error');
$location.path('/');
}
}
}
unfortunately, it was working fine. I was on a touchscreen laptop and getting weird behavior. On a regular laptop, the drag functionality worked fine, and on a desktop, it woked fine as well.....

Categories

Resources