Sales Tax and Tip calculation script. Enjoy it!

You can copy or modify this code and recommend improvements. Enjoy it!

Lines 252 and 253:
Modify this two lines of code to adapt sales tax script to your city tax.

<option href="#⑦" value=7>7%</option>
<option href="#⑪.⑤" value=11.5>11.5%</option>

Copy and paste the following code on a new Scriptable script:


// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: red; icon-glyph: calendar-alt;

// Adapted from calendar widget created by Max Zeryck @mzeryck

// Store current datetime
const date = new Date()
const filename = Script.name() + "https://www.icloud.com/iclouddrive/#ImageName"
const files = FileManager.local()
const path = files.joinPath(files.documentsDirectory(), filename)

// If we're running the script normally, go to the Calendar.
if (!config.runsInWidget) {

// Otherwise, create the widget.  
} else {

  let widget = new ListWidget()
  widget.backgroundColor = new Color("435c55")

widget.backgroundImage = files.readImage(path)

    
let title = widget.addText("Sales Tax");
title.font = Font.semiboldSystemFont(63);
title.textColor  = Color.white();
title.shadowColor = Color.black();
title.shadowOffset = new Point(1,1);
title.shadowRadius = 1;

let sub = widget.addText("type your City");
sub.font = Font.semiboldSystemFont(33);
sub.textColor  = Color.yellow();

let author = widget.addText("Created by yourname");
author.font = Font.semiboldSystemFont(18);
author.textColor  = Color.lightGray();

 // Finalize widget settings
 widget.setPadding(16,16,16,0)
 widget.spacing = -3
 
 Script.setWidget(widget)
 widget.presentSmall()
 Script.complete() 
}


// JS code for Tax calculation.
// You can copy or modify this code and recommend improvements.  Enjoy it!
function ticket(ivuPercent='11.5', tipPercent='15', subTotal='1000') {
  this.ivuPercent = ivuPercent;
  this.tipPercent = tipPercent;
  this.subTotal = subTotal;
}

// I want to get the parameters from Shortcuts, not inizialization.
var t1 = new ticket(args.ivuPercent, args.tipPercent, args.subTotal);

console.log(t1.ivuPercent + " "               + t1.tipPercent + " " + t1.subTotal);

/*
// Alert
var alert = new Alert();
alert.title = "Sales Tax";
alert.message = "Type Percents and ticket amount";
alert.addTextField("IvuPercent", t1.ivuPercent);
alert.addTextField("TipPercent", t1.tipPercent);
alert.addTextField("Subtotal", t1.subTotal);
alert.addAction("OK");
await alert.present();
var per = alert.textFieldValue(0);
var tip = alert.textFieldValue(1);
var sub = alert.textFieldValue(2);
*/

var per = t1.ivuPercent;
var tip = t1.tipPercent;
var sub = t1.subTotal;

const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  minimumFractionDigits: 2
})

var sub = formatter.format(1000) // "$1,000.00"

console.log("ivuPercent " + per + "\ntipPercent " + tip + "\nsubTotal " + sub);

// HTML
var html=`
<!DOCTYPE html>
<html>
<!-- head -->
<head>

<h2>Sales Tax</h2>

<meta name="viewport" content="width=device-width, initial-scale=1">

<!-- style del head CSS -->
<style>
h1, h2 {
  text-align: center;
  font-family: avenir;
}

/* botones verde obscuro */
.drpbtni, .drpbtnt {
  background-color: #435c55;
  color: white;
  padding: 16px;
  font-size: 20px;
  font-family: avenir;
  border: none;
  cursor: pointer;
    overflow: auto;
  white-space: nowrap;
  -webkit-overflow-scrolling: touch;
  -ms-overflow-style: -ms-autohiding-scrollbar;
  display: inline-block;
  text-align: center;
  text-decoration: none;
  width: 177px;
  font-size:100%;
}

/* color azul del hover */
.drpbtni:hover, .drpbtni:focus, 
.drpbtnt:hover, .drpbtnt:focus {
  background-color: #2980B9;
}

.dropdown {
  position: relative;
  display: inline-block;
}

/* dropdown white sub menu content*/
.drpdwni-content, .drpdwnt-content {
  display: none;
  position: sticky;

  min-width: 10%;
  overflow: auto;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  z-index: 1;
  
  color: #0066ff;
  padding: 16px;
  font-family: avenir;
  border: none;
  cursor: pointer;
  white-space: nowrap;
  -webkit-overflow-scrolling: touch;
  -ms-overflow-style: -ms-autohiding-scrollbar;
  display: inline-block;
  text-align: center;
  text-decoration: none;
  width: 177px;
  font-size:100%;
}

.drpdwni-content a, 
.drpdwnt-content a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: inline-block;
}

.dropdown a:hover {background-color: #ddd;}

body {
  background-color:DarkSeaGreen;
  background-image: linear-gradient(#afcfaf, DarkSeaGreen);
  text-align: left;
  border-spacing: 0px;
  padding: 0px;
}
#container {
  display: table;
  width: 100%;
  height: 100%;
}
.calculate {
  color: #2980B9;
  padding: 5px;
  font-family: avenir;
  border: none;
  cursor: pointer;
  white-space: nowrap;
  -webkit-overflow-scrolling: touch;
  -ms-overflow-style: -ms-autohiding-scrollbar;
  display: inline-block;
  text-align: center;
  text-decoration: none;
  width: 96%;
  font-size:100%;
}

input[type=text], select {
  width: 100%;
  padding: 12px 20px;
  margin: 1px 0;
  display: block;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
}

input[type=submit] {
  width: 100%;
  background-color: #2980B9;
  color: #2980B9; azul
  padding: 14px 20px;
  margin: 8px 0;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

input[type=submit]:hover {
  background-color: #45a049;
  color: #435c55; verde obscuro
}

div.subContainer {
  border-radius: 2px;
  background-color: #82bce3;
  padding: 21px;
}
</style>
</head>

<body>
<div class="dropdown">
  <button onclick="myFunctioni()" class="drpbtni">Tax</button>
  <button onclick="myFunctiont()" class="drpbtnt">Tip</button>
</div>
  
<table class="tl"; align=center>
<tbody>
  <tr>
    <td>
  <select type="number" id="ivu" name="ivu" class="drpdwni-content">
  
  
<!-- Modify this two lines of code to adapt sales tax to your city tax.  -->
    <option href="#⑦" value=7>7%</option>
    <option href="#⑪.⑤" value=11.5>11.5%</option>
    
    
  </select></td>
  <td>
  <select type="number" id="tip" name="tip" class="drpdwnt-content">
    <option href="#none" value=0>none</option>
    <option href="#⑫" value=12>⑫</option>
    <option href="#⑮" value=15>⑮</option>
    <option href="#⑱" value=18>⑱</option>
    <option href="#⑳" value=20>⑳</option>
  </select></td>
    </tr>
  </tbody>
</table>

<!-- container -->
<div id="container">
<!-- style body de la tabla CSS -->
<style type="text/css">
.tl {
  border-color: black;
  border-style: solid;
  border-width: 0px;
  font-family: avenir;
  font-size: 100%;
  overflow: hidden;
  padding: 0px 0px;
  word-break: normal;
}
.tg  {
  border-collapse:collapse;
  border-spacing: 0px;
  }
.tg td{
  border-color: black;
  border-style: solid;
  border-width: 0px;
  font-family: avenir;
  font-size: 150%;
  overflow: hidden;
  padding: 10px 20px;
  word-break: normal;
  }
.tg .tg-0lax{
  width: 1%; 
  text-align: left;
  vertical-align: top;
  }
.tg .tg-lqy6{
  text-align: right;
  vertical-align: top;
  }
</style>

<!-- tabla -->
<table class="tg"; align=center>
<tbody>
  <tr>
    <td class="tg-0lax"></td>
    <td class="tg-lqy6">
      <p id="outlef" ></p>
    </td>
    <td class="tg-lqy6">
      <p id="outrig"></p>
    </td>
    <td class="tg-0lax"></td>
  </tr>
</tbody>
</table>
</div>

<div class=subContainer>
<form action="/action_page.php">
  <label for="subTotal">SubTotal</label>
  <input type="number" step="0.01" min=0 id="sub" name="sub" class="calculate" placeholder="SubTotal...">
  
<input type="submit" value="Calculate" onclick="myFunction()">
</form>
</div>

<!-- script -->
<script type="text/javascript">
<!-- variables de alert al HTML -->
<!-- let per = ${per} -->
<!-- let tip = ${tip} -->
<!-- let sub = (${sub}).toFixed(2) -->
function myFunction() {
var subTotal = document.getElementById("sub").value
var per = document.getElementById("ivu").value
var tip = document.getElementById("tip").value

<!-- Fórmulas -->
var sub = Number(subTotal)
var ivu = per / 100
var pro = tip / 100

var totalIvu = ivu * sub
var totalTip = pro * sub

var totalAmount = sub + totalIvu + totalTip
var totalAmount = (per / 100 * sub) + (tip / 100 * sub) + sub
var totalAmtNoRnd = (per / 100 * sub) + (tip / 100 * sub) + sub


const formatoEU = { style: 'currency', currency: 'USD' };
const numFormat = new Intl.NumberFormat('en-US', formatoEU);

// expected output: "$##,###,###.##"
var sub = numFormat.format(sub);
var totalIvu = numFormat.format(totalIvu);
var totalTip = numFormat.format(totalTip);
var totalAmount = numFormat.format(totalAmount);



<!-- labels -->
document.getElementById("outlef").innerHTML = "Tax %" + "<br>" + "Tip %" + "<br><br>" + "Subtotal" + "<br>" + "Tax" +  "<br>" + "Tip" +  "<br>" + "<b>Total</b>"

<!-- valores -->
document.getElementById("outrig").innerHTML = per + "%<br>" + tip + "%<br><br>" + sub + "<br>" + totalIvu + "<br>" + totalTip + "<br>" + "<b>" + totalAmount + "</b>" + "<br><br>" + totalAmtNoRnd
}

/* When the user clicks on the button, toggle between hiding and showing the dropdown content */

function myFunctioni() {
var optivu = document.getElementById("ivu").classList.toggle("show");
}
function myFunctiont() {
var opttip = document.getElementById("tip").classList.toggle("show");
}

// Close the dropdown if the user clicks outside of it
window.onclick = function(event) {
  if (!event.target.matches('.drpbtni')) {
    var dropdowns = document.getElementsByClassName("drpdwni-content");
    var i;
    for (i = 0; i < dropdowns.length; i++) {
      var openDropdown = dropdowns[i];
      if (openDropdown.classList.contains('show')) {
        openDropdown.classList.remove('show');
      }
    }
  }
}

// Close the dropdown if the user clicks outside of it
window.onclick = function(event) {
  if (!event.target.matches('.drpbtnt')) {
    var dropdowns = document.getElementsByClassName("drpdwnt-content");
    var i;
    for (i = 0; i < dropdowns.length; i++) {
      var openDropdown = dropdowns[i];
      if (openDropdown.classList.contains('show')) {
        openDropdown.classList.contains('show');
      }
    }
  }
}
</script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript" src="IVU Script desa.js"></script>
</body>
</html>
`

// WebViewe
WebView.loadHTML(html, null, new Size(0, 100));
Script.complete();



async function createWidget(items) {
  let item = items[0]
  let authors = item.authors.map(a => {
    return a.name
  }).join(", ")
  let imgURL = extractImageURL(item)
  let rawDate = item["date_published"]
  let date = new Date(Date.parse(rawDate))
  let dateFormatter = new DateFormatter()
  dateFormatter.useFullDateStyle()
  dateFormatter.useShortTimeStyle()
  let strDate = dateFormatter.string(date)
  let gradient = new LinearGradient()
  gradient.locations = [0, 1]
  gradient.colors = [
    new Color("#b00a0fe6"),
    new Color("#b00a0fb3")
  ]
  let w = new ListWidget()
  if (imgURL != null) {
    let imgReq = new Request(imgURL)
    let img = await imgReq.loadImage()
    w.backgroundImage = img
  }
  w.backgroundColor = new Color("#b00a0f")
  w.backgroundGradient = gradient
  // Add spacer above content to center it vertically.
  w.addSpacer()
  // Show article headline.
  let titleTxt = w.addText(item.title)
  titleTxt.font = Font.boldSystemFont(16)
  titleTxt.textColor = Color.white()
  // Add spacing below headline.
  w.addSpacer(8)
  // Show authors.
  let authorsTxt = w.addText("by " + authors)
  authorsTxt.font = Font.mediumSystemFont(12)
  authorsTxt.textColor = Color.white()
  authorsTxt.textOpacity = 0.9
  // Add spacing below authors.
  w.addSpacer(2)
  // Show date.
  let dateTxt = w.addText(strDate)
  dateTxt.font = Font.mediumSystemFont(12)
  dateTxt.textColor = Color.white()
  dateTxt.textOpacity = 0.9
  // Add spacing below content to center it vertically.
  w.addSpacer()
  return w
}


async function loadItems() {
  let url = "https://macstories.net/feed/json"
  let req = new Request(url)
  let json = await req.loadJSON()
  return json.items
}

function extractImageURL(item) {
  let regex = /<img src="(.*)" alt="/
  let html = item["content_html"]
  let matches = html.match(regex)
  if (matches && matches.length >= 2) {
    return matches[1]
  } else {
    return null
  }
}

Widget:

Output: