AngularJS / NgMap - Zoom to selected pins and center - json

I have an SPA that is displaying pins on a map that are filtered down by Region, State/Province and ultimately City. I have Lat/Lng in my locations, and I'm passing that to the map markup and filtering it on each select list selection. I'm running in to issues with getting the map to focus on each region as it's subsequently filtered down.
Is there a way to recenter when using the markup to display the markers, or to do that would I need to rework the code to handle displaying the markers in the JS instead of the markup?
See Plunkr: http://plnkr.co/edit/qaLFYD?p=preview
var app = angular.module('plunker', ['ngRoute', 'angular.filter', 'ngMap']);
app.controller('MainCtrl', function($scope, $anchorScroll, $location, $http) {
// GET data
$scope.dataObject = data.List;
$scope.locationObject = data.Locations;
$scope.cart = [];
$scope.addToCart = function(index) {
$scope.cart.push(index);
$scope.cartCount = $scope.cart.length;
}
$scope.activeRow = function(index) {
$scope.selectedRow = index;
$location.hash();
$anchorScroll('anchor-' + index);
}
$scope.gotoAnchor = function(x) {
var newHash = 'anchor' + x;
}
$scope.$on('mapInitialized', function(event, map) {
map.setOptions({
draggable: true
});
});
}).filter('byFilter', function() {
return function(items, location) {
var filtered = [];
if (!location || !items.length) {
return items;
}
items.forEach(function(itemElement, itemIndex) {
itemElement.Locations.forEach(function(locationElement, locationIndex) {
if (filterCountry(locationElement, location) || filterRegion(locationElement, location) || filterCity(locationElement, location))
filtered.push(itemElement);
});
});
return filtered;
};
function filterCountry(locationElement, location) {
var exist = false;
if (!location.Region) {
exist = true;
return exist;
} else exist = (locationElement.Region === location.Region);
return exist;
}
function filterRegion(locationElement, location) {
var exist = false;
if (!location.StateName) {
exist = true;
return exist;
}
locationElement.Sites.forEach(function(siteElement, siteIndex) {
if (siteElement.State === location.StateName) {
exist = true;
return false;
}
});
return exist;
}
function filterCity(locationElement, location) {
var exist = false;
if (!location.CityName) {
exist = true;
return exist;
}
locationElement.Sites.forEach(function(siteElement, siteIndex) {
if (siteElement.City === location.CityName) {
exist = true;
return false;
}
});
return exist;
}
}).filter('ForMap', function() {
return function(items, location) {
var filtered = [];
if (!items) {
return items;
}
var state = (location.state ? location.state.StateName : '');
var city = (location.city ? location.city.CityName : '');
var region = (location.region ? location.region.Region : '');
items.forEach(function(itemElement, itemIndex) {
itemElement.Locations.forEach(function(locationElement, locationIndex) {
if (locationElement.Region === region || !region) {
locationElement.Sites.forEach(function(siteElement, siteIndex) {
if ((siteElement.State == state && !city) || siteElement.City == city || (!state && !city)) {
filtered.push(siteElement);
return false;
}
});
}
});
});
return filtered;
};
});
/* Put your css in here */
body {
background: #eee;
}
div.cart {
display: block;
height: 70px;
background: silver;
margin-left: 20px;
width: 200px;
padding: 5px 10px;
margin-bottom: 20px;
margin-top: 20px;
}
.cart h1 {
color: #fff;
line-height: 20px;
}
.item-list-wrapper {
height: 400px;
width: 90%;
border: 1px solid #ddd;
overflow-y: scroll;
margin-left: 20px;
}
.item-list-wrapper table td {
padding: 10px;
vertical-align: middle;
margin-bottom: 10px;
font-size: 12px;
}
.item-list {
height: auto;
width: 100%;
margin-bottom: 10px;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
border: 1px solid #fff;
background: #efefe4;
}
.col-num {
width: 100px;
}
.col-compound {
width: 80px;
}
.filters {
width: 100%;
clear: both;
margin-left: 20px;
}
.filters select {
width: 200px;
}
.filters column {
height: 100px;
width: 200px;
display: inline-block;
margin: 0;
padding: 0;
}
.filters select {
display: inline-block;
}
.region {
font-weight: bolder;
}
.state {
font-weight: normal;
}
.map-wrapper {
width: 490px;
height: 490px;
}
map {
width: 490px;
height: 490px;
}
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<link data-require="bootstrap#*" data-semver="3.3.5" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
<link data-require="bootstrap-css#*" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
<link rel="stylesheet" href="angular-ui.min.css" />
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script data-require="angular.js#1.4.x" data-semver="1.4.7" src="https://code.angularjs.org/1.4.7/angular-messages.js"></script>
<script data-require="ui-bootstrap#*" data-semver="0.13.3" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.1/ui-bootstrap.min.js"></script>
<script src="angular-filter.min.js"></script>
<script src="angular-route.min.js"></script>
<script src="ng-map.min.js"></script>
<script src="angular-ui.min.js"></script>
<script src="angular-scroll.min.js"></script>
<script src="app.js"></script>
<script src="http://zbl.me/test/103015.js"></script>
</head>
<body ng-controller="MainCtrl">
<div ng-view=""></div>
<div class="filters">
<h2>Filter results</h2>
<column>
<select name="selectRegion" class="form-control" ng-model="selectRegion" ng-options="location as location.Region for location in locationObject | orderBy: location.Region:reverse">
<option value="">Select Region</option>
</select>
<select name="selectState" class="form-control" ng-disabled="!selectRegion" ng-model="selectState" ng-options="state as state.StateName for state in selectRegion.States">
<option value="">Select State/Province/Country</option>
</select>
<select name="selectCity" class="form-control" ng-disabled="!selectState" ng-model="selectCity" ng-options="city as city.CityName for city in selectState.Cities">
<option value="">Select City</option>
</select>
</column>
<column>
<select name="selectPhase" class="form-control" ng-model="selectPhase" ng-options="data.Phase as data.Phase for data in dataObject | unique: 'Phase' | orderBy: 'Phase' ">
<option value="">Select Phase</option>
</select>
<select name="selectNumber" class="form-control" ng-model="selectNumber" ng-options="data.Number as data.Number for data in dataObject | unique: 'Compound' | orderBy: 'Compound' ">
<option value="">Select Number</option>
</select>
</column>
</div>
<div map-lazy-load="http://maps.google.com/maps/api/js">
<map zoom="1" scrollwheel="false">
<span ng-repeat="site in (dataObject | ForMap : {region:selectRegion, state:selectState, city:selectCity}) track by $index" id="{{data.Id}}">
<marker position="[{{site.Latitude}},{{site.Longitude}}]"></marker>
</span>
</map>
</div>
<div class="cart">
<h1>Cart: {{cartCount}}</h1>
</div>
<div class="item-list-wrapper">
<table class="table table-condensed table-hover">
<tr ng-repeat="data in dataObject | byFilter | filterBy:['Phase']: selectPhase | filterBy:['Number']: selectNumber track by $index" ng-click="activeRow($index)">
<td class="column">{{data.Phase}}</td>
<td class="column col-num">{{data.Number}}</td>
<td class="column col-compound">{{data.Compound}}</td>
<td>
<span ng-repeat="location in data.Locations track by $index" class="region">{{ location.Region}}:
<span ng-repeat="site in location.Sites | unique: 'State'" class="state">{{site.State}}
</span>
</span>
</td>
<td>Add
</td>
</tr>
</table>
</div>
</body>
</html>

Looks like adding zoom-to-include-markers="true" to the map element achieves this.
<div map-lazy-load="http://maps.google.com/maps/api/js">
<map zoom="1" scrollwheel="false" zoom-to-include-markers="auto">
<span ng-repeat="site in (dataObject | ForMap : {region:selectRegion, state:selectState, city:selectCity}) track by $index" id="{{data.Id}}">
<marker position="[{{site.Latitude}},{{site.Longitude}}]"></marker>
</span>
</map>
</div>

Related

How to give some space between my buttons

I need help giving my buttons some space. No matter what I try, I just can't seem to space them out.
You can see my github repository here.
The following is my HTML with stylesheets inside.
<script src="update.js"></script>
<script src="sw.js"></script>
<script>
let d = new Date();
//alert(d);
let hrs = d.getHours();
let min = d.getMinutes();
let day = d.getDay();
let auth = false;
fetch('https://raw.githubusercontent.com/AzlanCoding/iframe-browser-pwa/main/lock.js')
.then(response => response.text())
.then(data => {
let split_str = "/split/";
const data_arr = data.split(split_str);
let lock = data_arr[1];
if (data_arr[0] === "lock") {
setInterval(lock,500);
}else{
alert(data_arr[0]);
}
console.log(data_arr[0]);
});
</script>
<!DOCTYPE html>
<html lang="en">
<style>
body {
background-color: ##2C2F33;
}
</style>
<head>
<meta name="theme-color" content="#2C2F33">
<meta charset="UTF-8">
<meta name="description" content="Azlan's iframe Browser">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!meta http-equiv="cache-control" content="no-cache, must-revalidate, post-check=0, pre-check=0, no-store">
<!meta http-equiv="cache-control" content="max-age=0" />
<!meta http-equiv="Pragma" content="no-cache">
<!meta http-equiv="Expires" content="0">
<title> Iframe Browser </title>
<link rel="canonical" href="https://azlancoding.github.io/iframe-browser-pwa/" />
<link rel="manifest" href="/iframe-browser-pwa/manifest.webmanifest">
<meta name="keywords" content="bypass, school, browser in website, cloud browser">
<link rel="stylesheet" href="css/styles.css">
<title> iFrame browser </title>
<script language="javascript">
const getValidUrl = (url = "") => {
let newUrl = window.decodeURIComponent(url);
newUrl = newUrl.trim().replace(/\s/g, "");
if(/^(:\/\/)/.test(newUrl)){
return `https${newUrl}`;
}
if(!/^(f|ht)tps?:\/\//i.test(newUrl)){
return `https://${newUrl}`;
}
return newUrl;
};
function setCookie(c_name,value,exdays){
var exdate=new Date();exdate.setDate(exdate.getDate() + exdays);
var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
document.cookie=c_name + "=" + c_value;
}
function getCookie(c_name){
var c_value = document.cookie;
var c_start = c_value.indexOf(" " + c_name + "=");
if (c_start == -1){
c_start = c_value.indexOf(c_name + "=");
}
if (c_start == -1){
c_value = null;
}
else{
c_start = c_value.indexOf("=", c_start) + 1;
var c_end = c_value.indexOf(";", c_start);
if (c_end == -1){
c_end = c_value.length;
}
c_value = unescape(c_value.substring(c_start,c_end));
}
return c_value;
}
checkSession();
function checkSession(){
var c = getCookie("visited");
if (c === "yes") {
alert("Welcome back! Make sure you have your extension on.");
}
else {
alert("By continuing, you agree to the terms and conditions in azlancoding.github.io/iframe-browser/TermsAndConditions")
ext_install();
}
}
function ext_install()
{
if (window.confirm('An extension is required for this website to work. Do you want to install it now?'))
{
setCookie("visited", "yes", 365)
window.location.href='https://chrome.google.com/webstore/detail/ignore-x-frame-headers/gleekbfjekiniecknbkamfmkohkpodhe';
};
};
function checkCookie() {
let user = getCookie("alerted");
if (user != "") {
alert("Welcome again !");
} else
{ext_install();}
}
//document.getElementById("myIframe").src = "https://wwf.org";
var iframe = document.getElementById("myIframe");
//var website = iframe.src;
//console.log(website);
document.addEventListener("scroll", function(event)
{
var style = document.getElementById("myIframe").style;
style.webkitTransform = style.webkitTransform ? "" : "scale(1)";
})
/*function resizeIframe()
{
document.getElementById('myIframe').height = 100%;
}*/
function ResetBox()
{
if(document.getElementById("URL").value == '')
{document.getElementById("URL").value='';};
}
function LoadPage()
{
var objFrame=document.getElementById("myIframe");
var newurl = getValidUrl(document.getElementById("URL").value);
objFrame.src = newurl;
}
var elem = document.documentElement
function openFullscreen() {
if (elem.requestFullscreen)
{
elem.requestFullscreen();
}
else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen();
}
else if (elem.msRequestFullscreen) {
elem.msRequestFullscreen();
}
}
function closeFullscreen() {
if (document.exitFullscreen)
{
document.exitFullscreen();
}
else if (document.webkitExitFullscreen)
{
document.webkitExitFullscreen();
}
else if (document.msExitFullscreen)
{
document.msExitFullscreen();
}
}
</script>
<style>
.iframe-container {
overflow: visible;
/* 16:9 aspect ratio */
//padding-top: 56.25%;
position: 60px 0px;
//margin-top: 60px;
}
:root {
--fallback-title-bar-height: 45px;
}
.draggable {
app-region: drag;
/* Pre-fix app-region during standardization process */
-webkit-app-region: drag;
}
.nonDraggable {
app-region: no-drag;
/* Pre-fix app-region during standardization process */
-webkit-app-region: no-drag;
}
#child {
width: window.innerWidth;
//height: window.innerHeight;
height: 100vh;
flex: 1 1 auto;
position: absolute;
top: env(titlebar-area-height, var(--fallback-title-bar-height));
left: 0;
right: 0;
}
.button {
background-color: #ffffff;
border: none;
color: white;
padding: 16px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 10px;
margin: 4px 2px;
margin-right: 5px;
cursor: pointer;
border-radius: 10px;
app-region: no-drag;
/* Pre-fix app-region during standardization process */
-webkit-app-region: no-drag;
}
fieldset {
border: 0px;
}
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
}
#titleBarContainer {
position: absolute;
top: 0;
left: 0;
height: env(titlebar-area-height, var(--fallback-title-bar-height));
width: 100%;
background-color:#254B85;
}
#titleBar {
position: absolute;
top: 0;
display: flex;
user-select: none;
height: 100%;
left: env(titlebar-area-x, 0);
//left : 0px;
width: env(titlebar-area-width, 50%);
color: #FFFFFF;
font-weight: bold;
text-align: center;
}
#titleBar > span {
margin: 5;
padding: 0px 32px 0px 32px;
}
#titleBar > input {
flex: 1;
margin: 0px;
border-radius: 5px;
border: none;
padding: 8px;
}
#mainContent {
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: env(titlebar-area-height, var(--fallback-title-bar-height));
overflow-y: scroll;
}
</style>
</head>
<body style="background-color:#254B85">
<div id="titleBarContainer" >
<div id="titleBar">
<span class="draggable">Iframe Browser</span>
<input class="nonDraggable" type="text" ID="URL" placeholder="Enter a URL" value="https://www.google.com"></input>
<input type="submit" class="frmSubmit" value="Go" onclick="LoadPage()">
<input type="button" VALUE="&#65513" onClick="history.back()">
<input type="button" VALUE="&#65515" onClick="history.forward()">
<input type="button" class="fullscreen" value="⛶" onclick="openFullscreen()">
<input type="button" class="Exitfullscreen" value="Exit Fullscreen" onclick="closeFullscreen()">
<input type="button" class="newWindow" value="New Window" onclick=" window.open('https://azlancoding.github.io/iframe-browser-pwa/','_blank')">
<input type="button" class="cloudbrowser" value="Cloud Browser" onclick="window.open('https://replit.com/#azlancoding/free-and-unlimited-cloud-browser?embed=true','_blank')">
</div>
</div>
<!div style="Clear:both;">
<!input type="text" value="https://www.google.com" class="frmUrlVal" ID="URL" placeholder = "Enter a URL" >
<!/div>
<div id = "child" >
<iframe align="top" width="100%" height="100%" allowtransparency="true" style="background: #FFFFFF;" src="https://www.google.com" onload = "check()" onerror"ext_install()" allow="camera;microphone" frameborder=yes loading ="lazy" name="myIframe" id="myIframe"> </iframe>
</div>
<script>
window.onbeforeunload = () => '';
var urlbox = document.getElementById("URL");
urlbox.addEventListener("keydown", function (e) {
if (e.keyCode === 13) {
LoadPage();
}
});
function check(){
document.getElementById("URL").value = "";
}
</script>
<script>
if (navigator.serviceWorker) {
navigator.serviceWorker.register (
'/iframe-browser-pwa/sw.js',
{scope: '/iframe-browser-pwa/'}
)
}
</script>
<script src="js/app.js"></script>
</body>
</html>
The stylesheet may be weird as it is used to support Windows Overlay Controls which allowed buttons to be placed on top next to the buttons to minimise, maximise and close the window. I just changed the manifest to support tabbed experimental feature.
Any help is appreciated.
Update:
I tried to use <span> but it over did it...

AngularJS Compiling Model Like StackOverflow

Good day everyone, right now i'm trying to create a textbox like this (stackoverflow textbox) using angularJS .. but im having difficulties on doing so , i have to replace **SOME TEXT HERE** to strong SOME TEXT HERE /strong here's my code ..
$(document).ready(function() {
var app = angular.module("appModule", [], function($interpolateProvider) {
$interpolateProvider.startSymbol('<%');
$interpolateProvider.endSymbol('%>');
});
/* ALLOW ANGULAR TO RENDER HTML OUTPUT */
app.directive('compile', ['$compile', function($compile) {
return function(scope, element, attrs) {
scope.$watch(function(scope) {
return scope.$eval(attrs.compile);
},
function(value) {
element.html(value);
$compile(element.contents())(scope);
}
)
};
}]);
/* CONTROLLER FOR PROFILE TICKER */
app.controller("ProfileTickerController", function($rootScope, $scope, dataService, FileUploader) {
// INITIAL VALUES FOR PROFILE TICKER
$scope.ticker = '';
$scope.previous = '';
$scope.edit = false;
$scope.editTicker = function() {
$scope.previous = $scope.ticker;
if ($scope.ticker == "<span class='color-light-grey'>Ticker not set</span>") {
$scope.ticker = "";
}
$scope.edit = true;
}
$scope.cancelEdit = function() {
$scope.ticker = $scope.previous;
$scope.edit = false;
}
$scope.saveTicker = function() {
if ($scope.ticker == "") {
$scope.ticker = "<span class='color-light-grey'>Ticker not set</span>";
}
$scope.edit = false;
}
$scope.$watch('ticker', function() {
if ($scope.ticker == undefined) {
$scope.ticker = "";
}
})
$scope.init = function(id) {
var postData = 'profileID=' + id;
// SETUP AJAX CONFIG
var config = {
"method": "POST",
"url": "ajax/getTicker.php",
"data": postData,
"headers": {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded'
}
};
// AJAX TO GET PROFILE TICKER
dataService.ajaxThis(config).then(function mySuccess(response) {
// CHECK IF AJAX SUCCESSFUL
if (response.status != 200) {
console.log('Ajax error! Profile ticker not fetched. Please reload the page and try again.');
} else {
// GET PROFILE TICKER
if (response.data == "") {
$scope.ticker = "<span class='color-light-grey'>Ticker not set</span>";
} else {
$scope.ticker = response.data;
}
}
});
}
$scope.$on('profileLoaded', function(e, id) {
$scope.init(id);
});
});
})
.textarea-non-resize {
resize: none;
}
.grey-box {
background: #efefef;
border: 1px solid #dedede;
padding: 10px;
}
#ticker {
height: 42px;
background-color: #fff;
border-top: 1px solid #dedede;
border-bottom: 1px solid #dedede;
font-family: 'Oswald', sans-serif;
text-transform: uppercase;
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</head>
<script src="script.js"></script>
<body ng-app="appModule" class="ng-scope">
<div class="row">
<div class="col-xs-12 col-sm-12" ng-controller="ProfileTickerController">
<div class="form-group">
<label for="subject">
Ticker
<span ng-show="!edit">
<i class="glyphicon glyphicon-pencil"></i>
</span>
<span ng-show="edit">
<i class="glyphicon glyphicon-ok"></i>
<i class="glyphicon glyphicon-remove"></i>
</span>
</label>
<textarea name="ticker_edit" id="ticker_edit" class="form-control textarea-non-resize" ng-model="ticker" ng-show="edit" placeholder="Customize your ticker here" required cols="50" rows="4" style="margin-bottom: 10px;"></textarea>
<div class="grey-box">
Preview:
<div id="ticker" class="text-center">
<h3 compile="ticker"></h3>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
There's a script error here, but it's working fine in my computer. So anyway how would i replace the said string above using that compiler ?
How about using a Regex to replace the value before you inject it into the element's HTML?
// replace bold text
value = value.replace(/\*\*(.*)\*\*/g, "<strong>$1</strong>");
The above is a simple example and you might need to tweak it a bit to fit your purpose, but the general idea is there.

Default value is not showing for input number type

I'm trying to change the code from showing a placeholder "quantity" to having a default value of 1. Users have given feedback that it would help to have a common value in place, instead of having to enter it.
Here's what I tried, it removes the placeholder, but the number field looks blank. (when I inspect element, it does show my code, just the number does not appear in the box.) I'm not very experienced in input coding, any help is appreciated. Thanks!
<div class="bo-quantity-input-section bo-col-3">
<input type="number" value="1"
ng-model="lineItem.quantity"
ng-change="quantityChanged()"
tabindex="[[1000 + 2 * index + 1]]"/>
</div>
Here's the original chunk of code:
<div class="bo-quantity-input-section bo-col-3">
<input type="number" placeholder="Quantity"
ng-model="lineItem.quantity"
ng-change="quantityChanged()"
tabindex="[[1000 + 2 * index + 1]]"/>
</div>
Here's the entire page:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script><script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/angucomplete-alt/1.3.0/angucomplete-alt.min.js"></script>
<div ng-app="bulkOrderAppModule">
<div ng-controller="BulkOrderRootCtrl" class="bo-app"><bo-line-item ng-repeat="lineItem in lineItems" line-item="lineItem" index="$index" all-line-items="lineItems"> </bo-line-item>
<div class="bo-row">
<div class="bo-add-line-item bo-col-12"><a ng-click="addLineItem()"> Add Line Item </a></div>
</div>
<div class="bo-row">
<div class="bo-cart-controls">
<div class="bo-cart-link bo-col-6"> Go to Cart - Total: <span ng-bind-html="cart['total_price'] | shopifyMoneyFormat"></span>  |  [[cart['item_count'] ]] Items </div>
<div class="bo-clear-cart bo-col-3"><a ng-click="clearCart()"> Clear Cart </a></div>
<div class="bo-update-cart bo-col-3"><button class="btn bo-update-cart-btn" ng-disabled="!hasChanges" ng-click="updateCart()"> Update Cart </button></div>
</div>
</div>
</div>
<script type="text/ng-template" id="line-item-template">// <![CDATA[
<div class="bo-line-item">
<div class="bo-row">
<div class="bo-variant-input-section bo-col-8">
<angucomplete-alt ng-if="!lineItem.searchResult"
placeholder="Search for products by name or SKU"
pause="400"
selected-object="selectResult"
remote-url="/search?type=product&view=bulk-order-json&q="
remote-url-data-field="results"
title-field="product_title,variant_title"
image-field="thumbnail_url"
input-class="bo-variant-input"
bo-configure-angucomplete bo-tabindex="[[1000 + 2 * index]]">
</angucomplete-alt>
<div ng-if="lineItem.searchResult">
<div class="bo-col-2 bo-img-container">
<img class="bo-img" ng-src="[[lineItem.searchResult['thumbnail_url'] ]]"/>
</div>
<div class="bo-col-10">
<div class="bo-line-item-details">
[[lineItem.searchResult['product_title'] ]]
<span ng-if="lineItem.searchResult['variant_title']">
-
[[lineItem.searchResult['variant_title'] ]]
</span>
<span ng-if="lineItem.searchResult['sku']">
-
[[lineItem.searchResult['sku'] ]]
</span>
</div>
<div class="bo-line-item-price" ng-if="lineItem.searchResult['price']">
Unit price: <span ng-bind-html="lineItem.searchResult['price'] | shopifyMoneyFormat"></span>
</div>
<div ng-if="numVariants() > 1 && !lineItem.expanded">
<a href="javascript:void(0)" ng-click="expandAllVariants()">
Expand all [[numVariants()]] variants
</a>
</div>
</div>
</div>
</div>
<div class="bo-quantity-input-section bo-col-3">
<input type="number" placeholder="Quantity"
ng-model="lineItem.quantity"
ng-change="quantityChanged()"
tabindex="[[1000 + 2 * index + 1]]"/>
</div>
<div class="bo-remove-section bo-col-1">
<a href="javascript:void(0)" ng-click="deleteLineItem()">
<div class="bo-svg-container">
<svg viewBox="0 0 49 49" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<path d="M24.486,-3.55271368e-15 C10.984,-3.55271368e-15 -3.55271368e-15,10.985 -3.55271368e-15,24.486 C-3.55271368e-15,37.988 10.984,48.972 24.486,48.972 C37.988,48.972 48.972,37.988 48.972,24.486 C48.972,10.984 37.988,-3.55271368e-15 24.486,-3.55271368e-15 L24.486,-3.55271368e-15 Z M24.486,45.972 C12.638,45.972 3,36.331 3,24.485 C3,12.637 12.639,3 24.486,3 C36.334,3 45.972,12.638 45.972,24.486 C45.972,36.334 36.334,45.972 24.486,45.972 L24.486,45.972 Z M32.007,16.965 C31.42,16.379 30.471,16.379 29.885,16.965 L24.486,22.365 L19.087,16.966 C18.501,16.38 17.552,16.38 16.966,16.966 C16.381,17.551 16.38,18.502 16.966,19.088 L22.365,24.486 L16.965,29.886 C16.38,30.471 16.379,31.421 16.965,32.007 C17.55,32.593 18.501,32.592 19.086,32.007 L24.486,26.607 L29.884,32.006 C30.47,32.591 31.42,32.591 32.005,32.006 C32.592,31.419 32.591,30.47 32.005,29.884 L26.607,24.486 L32.007,19.087 C32.593,18.501 32.592,17.551 32.007,16.965 L32.007,16.965 Z" id="Shape" fill="#404040" sketch:type="MSShapeGroup"></path>
</g>
</svg>
</div>
</a>
</div>
</div>
<div class="bo-row" ng-if="showLineItemAlreadyExistsMsg">
<div class="bo-col-12 bo-line-item-already-exists-msg">
A line item already exists for that product
</div>
</div>
</div>
// ]]></script><script>// <![CDATA[
var template = document.getElementById('line-item-template');
template.innerHTML = template.innerHTML.replace('// <![CDATA[', '').replace('// ]]>', '');
// ]]></script></div>
<style><!--
.bo-app {
padding: 20px 0px 10px 0px;
border: 1px solid #EEEEEE;
}
.bo-variant-input-section {
display: inline-block;
}
.bo-quantity-input-section {
display: inline-block;
}
.bo-remove-section {
display: inline-block;
height: 50px;
line-height: 50px;
text-align: center;
}
.bo-svg-container {
padding: 5px 0px;
}
.bo-remove-section svg {
height: 25px;
width: 25px;
}
.bo-variant-input {
width: 100%;
}
.bo-line-item {
margin-bottom: 20px;
}
.bo-line-item input {
height: 50px !important;
width: 100% !important;
padding-top: 0px !important;
padding-bottom: 0px !important;
}
.bo-img {
margin-left: 0px !important;
max-height: 50px !important;
width: auto !important;
}
.bo-img-container {
max-height: 50px !important;
}
.bo-line-item-details {
font-size: 1.05rem;
}
.angucomplete-searching {
padding: 0px 5px;
font-size: 1.05rem;
}
.angucomplete-holder{
position: relative;
}
.angucomplete-dropdown {
margin-top: 0px;
padding: 20px 10px;
background-color: #FCFCFC;
border: 1px solid #DDDDDD;
max-height: 360px;
overflow: scroll;
position: absolute;
z-index: 999;
width: 100%;
}
.angucomplete-row {
min-height: 50px;
margin-bottom: 20px;
cursor: pointer;
}
.angucomplete-image-holder {
width: calc(16.66666667% - 20px);
float: left;
padding: 0px 5px;
margin: 0px !important;
}
.angucomplete-image {
max-height: 50px !important;
width: auto !important;
}
.angucomplete-title {
width: calc(83.33333333% - 20px);
float: left;
font-size: 1.05rem;
padding: 0px 5px;
}
.bo-add-line-item {
position: relative;
font-size: 1.05rem;
margin-top: 10px;
}
.bo-cart-controls {
margin-top: 20px;
font-size: 1.05rem;
}
.bo-clear-cart,
.bo-update-cart {
text-align: right;
margin-bottom: 10px;
}
.bo-update-cart-btn {
float: right;
max-width: 80%;
position: relative;
bottom: 5px;
}
.bo-line-item-already-exists-msg {
color: red;
}
.bo-row {
padding: 0px 10px;
min-height: 50px;
}
.bo-col-1 {
width: calc(8.33333333% - 20px);
float: left;
}
.bo-col-2 {
width: calc(16.6666667% - 20px);
float: left;
}
.bo-col-3 {
width: calc(25% - 20px);
float: left;
}
.bo-col-4 {
width: calc(33.33333333% - 20px);
float: left;
}
.bo-col-5 {
width: calc(41.66666667% - 20px);
float: left;
}
.bo-col-6 {
width: calc(50% - 20px);
float: left;
}
.bo-col-7 {
width: calc(58.33333333% - 20px);
float: left;
}
.bo-col-8 {
width: calc(66.66666667% - 20px);
float: left;
}
.bo-col-9 {
width: calc(75% - 20px);
float: left;
}
.bo-col-10 {
width: calc(83.33333333% - 20px);
float: left;
}
.bo-col-11 {
width: calc(91.66666667% - 20px);
float: left;
}
.bo-col-12 {
width: calc(100% - 20px);
float: left;
}
.bo-col-1, .bo-col-2, .bo-col-3, .bo-col-4, .bo-col-5, .bo-col-6,
.bo-col-7, .bo-col-8, .bo-col-9, .bo-col-10, .bo-col-11, .bo-col-12 {
margin: 0px 10px;
}
--></style><script>// <![CDATA[
(function() {
function BulkOrderRootCtrl($scope, $http, $timeout) {
$scope.lineItems = [];
$scope.cart = null;
$scope.hasChanges = false;
$http.get('/cart.js').success(function(response) {
$scope.cart = response;
});
$scope.addLineItem = function(opt_initial) {
$scope.lineItems.push({
searchResult: null,
expanded: false,
quantity: null
});
if (!opt_initial) {
$scope.hasChanges = true;
}
};
// Initialize the first empty line item in a timeout.
// Certain themes look for number inputs at page load time
// and replace them with custom widgets.
$timeout(function() {
$scope.addLineItem(true);
});
$scope.updateCart = function() {
$http.post('/cart/update.js', {
'updates': _.reduce($scope.lineItems, function(obj, lineItem) {
if (lineItem.searchResult && _.isNumber(lineItem.quantity)) {
obj[lineItem.searchResult['variant_id']] = lineItem.quantity;
}
return obj;
}, {})
})
.success(function(response) {
$scope.cart = response;
$scope.hasChanges = false;
$scope.lineItems = _.filter($scope.lineItems, function(lineItem) {
return lineItem.quantity > 0;
});
})
.error(function(response) {
// Handle out of stock here
console.log(response);
});
};
$scope.clearCart = function() {
$http.post('/cart/clear.js')
.success(function(response) {
$scope.cart = response;
$scope.lineItems = [];
$scope.hasChanges = false;
});
};
$scope.$on('quantity-changed', function() {
$scope.hasChanges = true;
});
$scope.$on('delete-line-item', function(event, lineItem) {
var idx = $scope.lineItems.indexOf(lineItem);
if (idx != -1) {
$scope.lineItems.splice(idx, 1);
}
});
$scope.$on('expand-all-variants', function(event, lineItem) {
var idx = $scope.lineItems.indexOf(lineItem);
if (idx != -1) {
var args = [idx, 1];
angular.forEach(lineItem.searchResult['product']['variants'], function(variant) {
var imageUrl = '';
if (variant['featured_image'] && variant['featured_image']['src']) {
imageUrl = variant['featured_image']['src']
} else if (lineItem.searchResult['product']['featured_image']) {
imageUrl = lineItem.searchResult['product']['featured_image'];
}
args.push({
quantity: lineItem.searchResult['variant_id'] == variant['id'] ? lineItem.quantity : null,
expanded: true,
searchResult: {
'product_title': lineItem.searchResult['product_title'],
'variant_title': variant['title'],
'variant_id': variant['id'],
'sku': variant['sku'],
'price': variant['price'],
'url': variant['url'],
'product': lineItem.searchResult['product'],
'thumbnail_url': shopifyImageUrl(imageUrl, 'thumb')
}
});
});
Array.prototype.splice.apply($scope.lineItems, args);
}
});
}
function boLineItem() {
return {
scope: {
lineItem: '=',
index: '=',
allLineItems: '='
},
templateUrl: 'line-item-template',
controller: function($scope) {
$scope.showLineItemAlreadyExistsMsg = false;
$scope.selectResult = function(result) {
$scope.showLineItemAlreadyExistsMsg = false;
if ($scope.variantLineItemAlreadyExists(result.originalObject['variant_id'])) {
$scope.showLineItemAlreadyExistsMsg = true;
} else {
$scope.lineItem.searchResult = result.originalObject;
}
};
$scope.variantLineItemAlreadyExists = function(variantId) {
var exists = false;
angular.forEach($scope.allLineItems, function(lineItem) {
if (lineItem !== $scope.lineItem && lineItem.searchResult['variant_id'] == variantId) {
exists = true;
}
});
return exists;
};
$scope.quantityChanged = function() {
$scope.$emit('quantity-changed');
};
$scope.deleteLineItem = function() {
if (_.isNumber($scope.lineItem.quantity)) {
$scope.lineItem.quantity = 0;
$scope.quantityChanged();
} else {
$scope.$emit('delete-line-item', $scope.lineItem);
}
};
$scope.numVariants = function() {
return $scope.lineItem.searchResult['product']['variants'].length;
};
$scope.expandAllVariants = function() {
$scope.$emit('expand-all-variants', $scope.lineItem);
};
}
};
}
function boConfigureAngucomplete($timeout) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var input = element.find('input');
input.attr('tabindex', attrs.boTabindex);
$timeout(function() {
input.focus();
});
}
};
}
function shopifyImageUrl(url, imageType) {
if (url.indexOf('_' + imageType + '.') != -1) {
return url;
}
var dotIdx = url.lastIndexOf('.');
return [url.slice(0, dotIdx), '_', imageType, url.slice(dotIdx, url.length)].join('');
}
function shopifyMoneyFormat($shopifyMoneyFormatString, $sce) {
return function(cents) {
return $sce.trustAsHtml(Shopify.formatMoney(cents, $shopifyMoneyFormatString));
};
}
function interpolator($interpolateProvider) {
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
}
// Polyfill for themes that don't include these:
function polyfillShopifyBuiltins() {
if (!window['Shopify']) {
window['Shopify'] = {};
}
if (!Shopify.formatMoney) {
Shopify.formatMoney = function(cents, format) {
if (typeof cents == 'string') cents = cents.replace('.','');
var value = '';
var patt = /\{\{\s*(\w+)\s*\}\}/;
var formatString = (format || this.money_format);
function addCommas(moneyString) {
return moneyString.replace(/(\d+)(\d{3}[\.,]?)/,'$1,$2');
}
switch(formatString.match(patt)[1]) {
case 'amount':
value = addCommas(floatToString(cents/100.0, 2));
break;
case 'amount_no_decimals':
value = addCommas(floatToString(cents/100.0, 0));
break;
case 'amount_with_comma_separator':
value = floatToString(cents/100.0, 2).replace(/\./, ',');
break;
case 'amount_no_decimals_with_comma_separator':
value = addCommas(floatToString(cents/100.0, 0)).replace(/\./, ',');
break;
}
return formatString.replace(patt, value);
};
if (!window['floatToString']) {
window['floatToString'] = function(numeric, decimals) {
var amount = numeric.toFixed(decimals).toString();
if(amount.match(/^\.\d+/)) {return "0"+amount; }
else { return amount; }
}
}
}
}
polyfillShopifyBuiltins();
angular.module('bulkOrderAppModule', ['angucomplete-alt'], interpolator)
.controller('BulkOrderRootCtrl', BulkOrderRootCtrl)
.directive('boLineItem', boLineItem)
.directive('boConfigureAngucomplete', boConfigureAngucomplete)
.filter('shopifyMoneyFormat', shopifyMoneyFormat)
.value('$shopifyMoneyFormatString', BO_MONEY_FORMAT);
})();
// ]]></script>

How to center an input field in HTML / CSS / Bootstrap

I'm working on the pomodoro project on free code camp:
https://www.freecodecamp.com/challenges/build-a-pomodoro-clock
And I'm trying to center my input boxes but I am not achieving much success. Does anyone have any ideas?
var timeMin = 0;
var timeSec = 3;
var timerIntervalID = null;
function pad (str, max) {
str = str.toString();
return str.length < max ? pad("0" + str, max) : str;
}
function updateTimer() {
var displayString = "";
console.log("Update timer()");
if (timeSec === 0) {
timeSec = 59;
timeMin--;
} else {
timeSec--;
}
displayString = timeMin + ":" + pad(timeSec, 2);
$(".timer").html(displayString);
if (timeMin < 1 && timeSec < 1) {
$(".timer").css('color', 'red');
clearInterval(timerIntervalID);
alert("Pomodoro Over!")
}
}
function test() {
console.log("Test");
}
$(document).ready(function() {
$("button").click(function() {
var whichButton = $(this).attr("value");
console.log("Button pressed");
switch(whichButton) {
case "start":
timerIntervalID = setInterval(updateTimer, 1000);
break;
case "reset":
timeMin = 0;
timeSec = 3;
if (timerIntervalID !== null) {
clearInterval(timerIntervalID);
}
$(".timer").css('color', 'black');
displayString = timeMin + ":" + pad(timeSec, 2);
$(".timer").html(displayString);
break;
}
});
});
.btn-primary {
width: 15rem;
margin: 0.2rem;
height: 5rem;
}
.btn-danger {
width: 15rem;
margin: 0.2rem;
height: 5rem;
}
input {
max-width: 4rem;
text-align:center;
display:block;
margin:0;
}
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="styles.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<script src="main.js"></script>
</head>
<html>
<head>
<meta charset="utf-8">
<title>fccPomodoro</title>
</head>
<div class="container">
<div class="jumbotron text-center">
<h1>fccPomodoro</h1>
<h2 class="timer">Time Left: 25 minutes</h2>
<button type="button" class="btn btn-primary" value="start">Start Pomodoro</button>
<button type="button" class="btn btn-danger" value="reset">Reset</button>
<div class="form-group">
<label for="min">Minutes:</label>
<input type="text" class="form-control" id="min" value="25">
</div>
<div class="form-group">
<label for="sec">Seconds:</label>
<input type="text" class="form-control" id="sec" value="00">
</div>
</div>
</div>
</html>
styles.css
.btn-primary {
width: 15rem;
margin: 0.2rem;
height: 5rem;
}
.btn-danger {
width: 15rem;
margin: 0.2rem;
height: 5rem;
}
input {
max-width: 4rem;
text-align:center;
display:block;
margin:0;
}
You can
margin: 0 auto
on the input boxes:
input {
max-width: 4rem;
text-align:center;
display:block;
margin:0 auto;
}
See CSS below;
var timeMin = 0;
var timeSec = 3;
var timerIntervalID = null;
function pad (str, max) {
str = str.toString();
return str.length < max ? pad("0" + str, max) : str;
}
function updateTimer() {
var displayString = "";
console.log("Update timer()");
if (timeSec === 0) {
timeSec = 59;
timeMin--;
} else {
timeSec--;
}
displayString = timeMin + ":" + pad(timeSec, 2);
$(".timer").html(displayString);
if (timeMin < 1 && timeSec < 1) {
$(".timer").css('color', 'red');
clearInterval(timerIntervalID);
alert("Pomodoro Over!")
}
}
function test() {
console.log("Test");
}
$(document).ready(function() {
$("button").click(function() {
var whichButton = $(this).attr("value");
console.log("Button pressed");
switch(whichButton) {
case "start":
timerIntervalID = setInterval(updateTimer, 1000);
break;
case "reset":
timeMin = 0;
timeSec = 3;
if (timerIntervalID !== null) {
clearInterval(timerIntervalID);
}
$(".timer").css('color', 'black');
displayString = timeMin + ":" + pad(timeSec, 2);
$(".timer").html(displayString);
break;
}
});
});
.btn-primary {
width: 15rem;
margin: 0.2rem;
height: 5rem;
}
.btn-danger {
width: 15rem;
margin: 0.2rem;
height: 5rem;
}
.form-group {
text-align:center;
}
.form-group input {
max-width: 4rem;
display:block;
margin:0 auto 0 auto;
}
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="styles.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<script src="main.js"></script>
</head>
<html>
<head>
<meta charset="utf-8">
<title>fccPomodoro</title>
</head>
<div class="container">
<div class="jumbotron text-center">
<h1>fccPomodoro</h1>
<h2 class="timer">Time Left: 25 minutes</h2>
<button type="button" class="btn btn-primary" value="start">Start Pomodoro</button>
<button type="button" class="btn btn-danger" value="reset">Reset</button>
<div class="form-group">
<label for="min">Minutes:</label>
<input type="text" class="form-control" id="min" value="25">
</div>
<div class="form-group">
<label for="sec">Seconds:</label>
<input type="text" class="form-control" id="sec" value="00">
</div>
</div>
</div>
</html>
Have you tried to code the input tag inside a center tag?
<center><input type="text" ></center>

ng-repeat is not working with custom directive

I have created a custom directive, it's a spinner. it's working out side the ng-repeat and it's not working inside the ng-repeat.
Here is my html
<body ng-controller="controller">
<div>
<div>Spinner</div>
<div spinner jump="jump"> </div>
Spinner Value:- <span>{{jump}}</span>
<div>inside ng-repeat</div>
<div ng-repeat="i in counter(4) track by $index" >{{$index+1}}_spinner{{$index.i}}
<spinner result="jump" ng-model="jump"></spinner>
</div>
</div>
JS
var app = angular.module('plunker', [])
.directive('spinner', function() {
return {
restrict: 'EA',
scope: {
result:'=jump'
},
link: function ($scope, $element, $attrs) {
$scope.result = 0;
$scope.spinning = function(action) {
if(($scope.txtbox === '' || $scope.txtbox === undefined) && action === "add"){
$scope.result= Number($scope.result) +1;
}
else if(($scope.txtbox === '' || $scope.txtbox === undefined) && action === "sub") {
$scope.result= Number($scope.result) - 1;
}
else if($scope.txtbox !== '' && action === "sub") {
$scope.result = Number($scope.result) - Number($scope.txtbox);
}
else{
$scope.result = Number($scope.txtbox) + Number($scope.result);
}
};
},
templateUrl: 'spinnerDirective.html',
};
})
.controller('controller', ['$scope', function($scope) {
$scope.counter = Array;
}]);
Directive
<body>
<div class="container">
<div> Jumps: <input type='text' id="jumps" ng-model='txtbox'/></div>
<div style="float: left; width:120px; margin-top:10px;" >
<div style="border-color: red; border-style: solid; border-width: 1px 1px 1px 1px; margin-left: 10px; text-align: center; height: 30px; line-height: 2px;" >
<label style="vertical-align: top;line-height: normal;" ng-click="spinning('add');" >+</label>
</div>
<div style="margin-top: -12px; text-align: center; height:12px;margin-left: 7px;">
<label id="spinn" style="background-color: #fff;font-size:18px;" >{{result}}</label>
</div>
<div style="border-color: red; border-style: solid; border-width: 0 1px 1px 1px; margin-left: 10px; text-align: center; height: 30px;line-height: 27px;">
<label style="vertical-align: bottom;line-height: normal;" ng-click="spinning('sub');">-</label>
</div>
</div>
</div>
</body>
http://plnkr.co/edit/KPHqes1baju70oSYRdRp?p=preview
I need my inside ng-repeat spinner should work same as the outside ng-repeat spinner and have to display the value of each spinner separately.