Add calculator widget (#956)
This adds a simple calculator widget, somewhat similar to the one presented when searching calculator on Google. Also, it adds somewhat of a template for making the addition of new widgets easier via the app/utils/widgets.py file. My eventual plan is to use this to create more widgets that appear in Google, such as a color picker, timer, etc. --------- Co-authored-by: Ben Busby <contact@benbusby.com>main
parent
da53db2a81
commit
7ca69e752d
|
@ -19,7 +19,8 @@ from app.utils.misc import get_proxy_host_url
|
||||||
from app.filter import Filter
|
from app.filter import Filter
|
||||||
from app.utils.misc import read_config_bool, get_client_ip, get_request_url, \
|
from app.utils.misc import read_config_bool, get_client_ip, get_request_url, \
|
||||||
check_for_update
|
check_for_update
|
||||||
from app.utils.results import add_ip_card, bold_search_terms,\
|
from app.utils.widgets import *
|
||||||
|
from app.utils.results import bold_search_terms,\
|
||||||
add_currency_card, check_currency, get_tabs_content
|
add_currency_card, check_currency, get_tabs_content
|
||||||
from app.utils.search import Search, needs_https, has_captcha
|
from app.utils.search import Search, needs_https, has_captcha
|
||||||
from app.utils.session import valid_user_session
|
from app.utils.session import valid_user_session
|
||||||
|
@ -340,10 +341,15 @@ def search():
|
||||||
|
|
||||||
response = bold_search_terms(response, query)
|
response = bold_search_terms(response, query)
|
||||||
|
|
||||||
# Feature to display IP address
|
# check for widgets and add if requested
|
||||||
if search_util.check_kw_ip():
|
if search_util.widget != '':
|
||||||
html_soup = bsoup(str(response), 'html.parser')
|
html_soup = bsoup(str(response), 'html.parser')
|
||||||
response = add_ip_card(html_soup, get_client_ip(request))
|
match search_util.widget:
|
||||||
|
case 'ip':
|
||||||
|
response = add_ip_card(html_soup, get_client_ip(request))
|
||||||
|
case 'calculator':
|
||||||
|
if not 'nojs' in request.args:
|
||||||
|
response = add_calculator_card(html_soup)
|
||||||
|
|
||||||
# Update tabs content
|
# Update tabs content
|
||||||
tabs = get_tabs_content(app.config['HEADER_TABS'],
|
tabs = get_tabs_content(app.config['HEADER_TABS'],
|
||||||
|
@ -353,6 +359,8 @@ def search():
|
||||||
translation)
|
translation)
|
||||||
|
|
||||||
# Feature to display currency_card
|
# Feature to display currency_card
|
||||||
|
# Since this is determined by more than just the
|
||||||
|
# query is it not defined as a standard widget
|
||||||
conversion = check_currency(str(response))
|
conversion = check_currency(str(response))
|
||||||
if conversion:
|
if conversion:
|
||||||
html_soup = bsoup(str(response), 'html.parser')
|
html_soup = bsoup(str(response), 'html.parser')
|
||||||
|
@ -395,7 +403,7 @@ def search():
|
||||||
query=urlparse.unquote(query),
|
query=urlparse.unquote(query),
|
||||||
search_type=search_util.search_type,
|
search_type=search_util.search_type,
|
||||||
mobile=g.user_request.mobile,
|
mobile=g.user_request.mobile,
|
||||||
tabs=tabs))
|
tabs=tabs)).replace(" ", "")
|
||||||
|
|
||||||
|
|
||||||
@app.route(f'/{Endpoint.config}', methods=['GET', 'POST', 'PUT'])
|
@app.route(f'/{Endpoint.config}', methods=['GET', 'POST', 'PUT'])
|
||||||
|
|
|
@ -52,6 +52,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function focusSearch () {
|
function focusSearch () {
|
||||||
|
if (window.usingCalculator) {
|
||||||
|
// if this function exists, it means the calculator widget has been displayed
|
||||||
|
if (usingCalculator()) return;
|
||||||
|
}
|
||||||
activeIdx = -1;
|
activeIdx = -1;
|
||||||
searchBar.focus();
|
searchBar.focus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,260 @@
|
||||||
|
<!--
|
||||||
|
Calculator widget.
|
||||||
|
This file should contain all required
|
||||||
|
CSS, HTML, and JS for it.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#calc-text {
|
||||||
|
background: var(--whoogle-dark-page-bg);
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: right;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--whoogle-dark-text);
|
||||||
|
}
|
||||||
|
#prev-equation {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.error-border {
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#calc-btns {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(6, 1fr);
|
||||||
|
grid-template-rows: repeat(5, 1fr);
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
#calc-btns button {
|
||||||
|
background: #313141;
|
||||||
|
color: var(--whoogle-dark-text);
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#calc-btns button:hover {
|
||||||
|
background: #414151;
|
||||||
|
}
|
||||||
|
#calc-btns .common {
|
||||||
|
background: #51516a;
|
||||||
|
}
|
||||||
|
#calc-btns .common:hover {
|
||||||
|
background: #61617a;
|
||||||
|
}
|
||||||
|
#calc-btn-0 { grid-row: 5; grid-column: 3; }
|
||||||
|
#calc-btn-1 { grid-row: 4; grid-column: 3; }
|
||||||
|
#calc-btn-2 { grid-row: 4; grid-column: 4; }
|
||||||
|
#calc-btn-3 { grid-row: 4; grid-column: 5; }
|
||||||
|
#calc-btn-4 { grid-row: 3; grid-column: 3; }
|
||||||
|
#calc-btn-5 { grid-row: 3; grid-column: 4; }
|
||||||
|
#calc-btn-6 { grid-row: 3; grid-column: 5; }
|
||||||
|
#calc-btn-7 { grid-row: 2; grid-column: 3; }
|
||||||
|
#calc-btn-8 { grid-row: 2; grid-column: 4; }
|
||||||
|
#calc-btn-9 { grid-row: 2; grid-column: 5; }
|
||||||
|
#calc-btn-EQ { grid-row: 5; grid-column: 5; }
|
||||||
|
#calc-btn-PT { grid-row: 5; grid-column: 4; }
|
||||||
|
#calc-btn-BCK { grid-row: 5; grid-column: 6; }
|
||||||
|
#calc-btn-ADD { grid-row: 4; grid-column: 6; }
|
||||||
|
#calc-btn-SUB { grid-row: 3; grid-column: 6; }
|
||||||
|
#calc-btn-MLT { grid-row: 2; grid-column: 6; }
|
||||||
|
#calc-btn-DIV { grid-row: 1; grid-column: 6; }
|
||||||
|
#calc-btn-CLR { grid-row: 1; grid-column: 5; }
|
||||||
|
#calc-btn-PRC{ grid-row: 1; grid-column: 4; }
|
||||||
|
#calc-btn-RP { grid-row: 1; grid-column: 3; }
|
||||||
|
#calc-btn-LP { grid-row: 1; grid-column: 2; }
|
||||||
|
#calc-btn-ABS { grid-row: 1; grid-column: 1; }
|
||||||
|
#calc-btn-SIN { grid-row: 2; grid-column: 2; }
|
||||||
|
#calc-btn-COS { grid-row: 3; grid-column: 2; }
|
||||||
|
#calc-btn-TAN { grid-row: 4; grid-column: 2; }
|
||||||
|
#calc-btn-SQR { grid-row: 5; grid-column: 2; }
|
||||||
|
#calc-btn-EXP { grid-row: 2; grid-column: 1; }
|
||||||
|
#calc-btn-E { grid-row: 3; grid-column: 1; }
|
||||||
|
#calc-btn-PI { grid-row: 4; grid-column: 1; }
|
||||||
|
#calc-btn-LOG { grid-row: 5; grid-column: 1; }
|
||||||
|
</style>
|
||||||
|
<p id="prev-equation"></p>
|
||||||
|
<div id="calculator-widget">
|
||||||
|
<p id="calc-text">0</p>
|
||||||
|
<div id="calc-btns">
|
||||||
|
<button id="calc-btn-0" class="common">0</button>
|
||||||
|
<button id="calc-btn-1" class="common">1</button>
|
||||||
|
<button id="calc-btn-2" class="common">2</button>
|
||||||
|
<button id="calc-btn-3" class="common">3</button>
|
||||||
|
<button id="calc-btn-4" class="common">4</button>
|
||||||
|
<button id="calc-btn-5" class="common">5</button>
|
||||||
|
<button id="calc-btn-6" class="common">6</button>
|
||||||
|
<button id="calc-btn-7" class="common">7</button>
|
||||||
|
<button id="calc-btn-8" class="common">8</button>
|
||||||
|
<button id="calc-btn-9" class="common">9</button>
|
||||||
|
<button id="calc-btn-EQ" class="common">=</button>
|
||||||
|
<button id="calc-btn-PT" class="common">.</button>
|
||||||
|
<button id="calc-btn-BCK">⬅</button>
|
||||||
|
<button id="calc-btn-ADD">+</button>
|
||||||
|
<button id="calc-btn-SUB">-</button>
|
||||||
|
<button id="calc-btn-MLT">x</button>
|
||||||
|
<button id="calc-btn-DIV">/</button>
|
||||||
|
<button id="calc-btn-CLR">C</button>
|
||||||
|
<button id="calc-btn-PRC">%</button>
|
||||||
|
<button id="calc-btn-RP">)</button>
|
||||||
|
<button id="calc-btn-LP">(</button>
|
||||||
|
<button id="calc-btn-ABS">|x|</button>
|
||||||
|
<button id="calc-btn-SIN">sin</button>
|
||||||
|
<button id="calc-btn-COS">cos</button>
|
||||||
|
<button id="calc-btn-TAN">tan</button>
|
||||||
|
<button id="calc-btn-SQR">√</button>
|
||||||
|
<button id="calc-btn-EXP">^</button>
|
||||||
|
<button id="calc-btn-E">ℇ</button>
|
||||||
|
<button id="calc-btn-PI">π</button>
|
||||||
|
<button id="calc-btn-LOG">log</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
// JS does not have this by default.
|
||||||
|
// from https://www.freecodecamp.org/news/how-to-factorialize-a-number-in-javascript-9263c89a4b38/
|
||||||
|
function factorial(num) {
|
||||||
|
if (num < 0)
|
||||||
|
return -1;
|
||||||
|
else if (num === 0)
|
||||||
|
return 1;
|
||||||
|
else {
|
||||||
|
return (num * factorial(num - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// returns true if the user is currently focused on the calculator widget
|
||||||
|
function usingCalculator() {
|
||||||
|
let activeElement = document.activeElement;
|
||||||
|
while (true) {
|
||||||
|
if (!activeElement) return false;
|
||||||
|
if (activeElement.id === "calculator-wrapper") return true;
|
||||||
|
activeElement = activeElement.parentElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const $ = q => document.querySelectorAll(q);
|
||||||
|
// key bindings for commonly used buttons
|
||||||
|
const keybindings = {
|
||||||
|
"0": "0",
|
||||||
|
"1": "1",
|
||||||
|
"2": "2",
|
||||||
|
"3": "3",
|
||||||
|
"4": "4",
|
||||||
|
"5": "5",
|
||||||
|
"6": "6",
|
||||||
|
"7": "7",
|
||||||
|
"8": "8",
|
||||||
|
"9": "9",
|
||||||
|
"Enter": "EQ",
|
||||||
|
".": "PT",
|
||||||
|
"+": "ADD",
|
||||||
|
"-": "SUB",
|
||||||
|
"*": "MLT",
|
||||||
|
"/": "DIV",
|
||||||
|
"%": "PRC",
|
||||||
|
"c": "CLR",
|
||||||
|
"(": "LP",
|
||||||
|
")": "RP",
|
||||||
|
"Backspace": "BCK",
|
||||||
|
}
|
||||||
|
window.addEventListener("keydown", event => {
|
||||||
|
if (!usingCalculator()) return;
|
||||||
|
if (event.key === "Enter" && document.activeElement.id !== "search-bar")
|
||||||
|
event.preventDefault();
|
||||||
|
if (keybindings[event.key])
|
||||||
|
document.getElementById("calc-btn-" + keybindings[event.key]).click();
|
||||||
|
})
|
||||||
|
// calculates the string
|
||||||
|
const calc = () => {
|
||||||
|
var mathtext = document.getElementById("calc-text");
|
||||||
|
var statement = mathtext.innerHTML
|
||||||
|
// remove empty ()
|
||||||
|
.replace("()", "")
|
||||||
|
// special constants
|
||||||
|
.replace("π", "(Math.PI)")
|
||||||
|
.replace("ℇ", "(Math.E)")
|
||||||
|
// turns 3(1+2) into 3*(1+2) (for example)
|
||||||
|
.replace(/(?<=[0-9\)])(?<=[^+\-x*\/%^])\(/, "x(")
|
||||||
|
// same except reversed
|
||||||
|
.replace(/\)(?=[0-9\(])(?=[^+\-x*\/%^])/, ")x")
|
||||||
|
// replace human friendly x with JS *
|
||||||
|
.replace("x", "*")
|
||||||
|
// trig & misc functions
|
||||||
|
.replace("sin", "Math.sin")
|
||||||
|
.replace("cos", "Math.cos")
|
||||||
|
.replace("tan", "Math.tan")
|
||||||
|
.replace("√", "Math.sqrt")
|
||||||
|
.replace("^", "**")
|
||||||
|
.replace("abs", "Math.abs")
|
||||||
|
.replace("log", "Math.log")
|
||||||
|
;
|
||||||
|
// add any missing )s to the end
|
||||||
|
while(true) if (
|
||||||
|
(statement.match(/\(/g) || []).length >
|
||||||
|
(statement.match(/\)/g) || []).length
|
||||||
|
) statement += ")"; else break;
|
||||||
|
// evaluate the expression.
|
||||||
|
console.log("calculating [" + statement + "]");
|
||||||
|
try {
|
||||||
|
var result = eval(statement);
|
||||||
|
document.getElementById("prev-equation").innerHTML = mathtext.innerHTML + " = ";
|
||||||
|
mathtext.innerHTML = result;
|
||||||
|
mathtext.classList.remove("error-border");
|
||||||
|
} catch (e) {
|
||||||
|
mathtext.classList.add("error-border");
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const updateCalc = (e) => {
|
||||||
|
// character(s) recieved from button
|
||||||
|
var c = event.target.innerHTML;
|
||||||
|
var mathtext = document.getElementById("calc-text");
|
||||||
|
if (mathtext.innerHTML === "0") mathtext.innerHTML = "";
|
||||||
|
// special cases
|
||||||
|
switch (c) {
|
||||||
|
case "C":
|
||||||
|
// Clear
|
||||||
|
mathtext.innerHTML = "0";
|
||||||
|
break;
|
||||||
|
case "⬅":
|
||||||
|
// Delete
|
||||||
|
mathtext.innerHTML = mathtext.innerHTML.slice(0, -1);
|
||||||
|
if (mathtext.innerHTML.length === 0) {
|
||||||
|
mathtext.innerHTML = "0";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "=":
|
||||||
|
calc()
|
||||||
|
break;
|
||||||
|
case "sin":
|
||||||
|
case "cos":
|
||||||
|
case "tan":
|
||||||
|
case "log":
|
||||||
|
case "√":
|
||||||
|
mathtext.innerHTML += `${c}(`;
|
||||||
|
break;
|
||||||
|
case "|x|":
|
||||||
|
mathtext.innerHTML += "abs("
|
||||||
|
break;
|
||||||
|
case "+":
|
||||||
|
case "-":
|
||||||
|
case "x":
|
||||||
|
case "/":
|
||||||
|
case "%":
|
||||||
|
case "^":
|
||||||
|
if (mathtext.innerHTML.length === 0) mathtext.innerHTML = "0";
|
||||||
|
// prevent typing 2 operators in a row
|
||||||
|
if (mathtext.innerHTML.match(/[+\-x\/%^] $/))
|
||||||
|
mathtext.innerHTML = mathtext.innerHTML.slice(0, -3);
|
||||||
|
mathtext.innerHTML += ` ${c} `;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mathtext.innerHTML += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i of $("#calc-btns button")) {
|
||||||
|
i.addEventListener('click', event => {
|
||||||
|
updateCalc(event);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -281,44 +281,6 @@ def append_anon_view(result: BeautifulSoup, config: Config) -> None:
|
||||||
av_link['class'] = 'anon-view'
|
av_link['class'] = 'anon-view'
|
||||||
result.append(av_link)
|
result.append(av_link)
|
||||||
|
|
||||||
|
|
||||||
def add_ip_card(html_soup: BeautifulSoup, ip: str) -> BeautifulSoup:
|
|
||||||
"""Adds the client's IP address to the search results
|
|
||||||
if query contains keywords
|
|
||||||
|
|
||||||
Args:
|
|
||||||
html_soup: The parsed search result containing the keywords
|
|
||||||
ip: ip address of the client
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
BeautifulSoup
|
|
||||||
|
|
||||||
"""
|
|
||||||
main_div = html_soup.select_one('#main')
|
|
||||||
if main_div:
|
|
||||||
# HTML IP card tag
|
|
||||||
ip_tag = html_soup.new_tag('div')
|
|
||||||
ip_tag['class'] = 'ZINbbc xpd O9g5cc uUPGi'
|
|
||||||
|
|
||||||
# For IP Address html tag
|
|
||||||
ip_address = html_soup.new_tag('div')
|
|
||||||
ip_address['class'] = 'kCrYT ip-address-div'
|
|
||||||
ip_address.string = ip
|
|
||||||
|
|
||||||
# Text below the IP address
|
|
||||||
ip_text = html_soup.new_tag('div')
|
|
||||||
ip_text.string = 'Your public IP address'
|
|
||||||
ip_text['class'] = 'kCrYT ip-text-div'
|
|
||||||
|
|
||||||
# Adding all the above html tags to the IP card
|
|
||||||
ip_tag.append(ip_address)
|
|
||||||
ip_tag.append(ip_text)
|
|
||||||
|
|
||||||
# Insert the element at the top of the result list
|
|
||||||
main_div.insert_before(ip_tag)
|
|
||||||
return html_soup
|
|
||||||
|
|
||||||
|
|
||||||
def check_currency(response: str) -> dict:
|
def check_currency(response: str) -> dict:
|
||||||
"""Check whether the results have currency conversion
|
"""Check whether the results have currency conversion
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@ class Search:
|
||||||
self.config = config
|
self.config = config
|
||||||
self.session_key = session_key
|
self.session_key = session_key
|
||||||
self.query = ''
|
self.query = ''
|
||||||
|
self.widget = ''
|
||||||
self.cookies_disabled = cookies_disabled
|
self.cookies_disabled = cookies_disabled
|
||||||
self.search_type = self.request_params.get(
|
self.search_type = self.request_params.get(
|
||||||
'tbm') if 'tbm' in self.request_params else ''
|
'tbm') if 'tbm' in self.request_params else ''
|
||||||
|
@ -104,6 +105,11 @@ class Search:
|
||||||
# Strip leading '! ' for "feeling lucky" queries
|
# Strip leading '! ' for "feeling lucky" queries
|
||||||
self.feeling_lucky = q.startswith('! ')
|
self.feeling_lucky = q.startswith('! ')
|
||||||
self.query = q[2:] if self.feeling_lucky else q
|
self.query = q[2:] if self.feeling_lucky else q
|
||||||
|
# Check for possible widgets
|
||||||
|
self.widget = "ip" if re.search("([^a-z0-9]|^)my *[^a-z0-9] *(ip|internet protocol)" +
|
||||||
|
"($|( *[^a-z0-9] *(((addres|address|adres|" +
|
||||||
|
"adress)|a)? *$)))", self.query.lower()) else self.widget
|
||||||
|
self.widget = 'calculator' if re.search("calculator|calc|calclator|math", self.query.lower()) else self.widget
|
||||||
return self.query
|
return self.query
|
||||||
|
|
||||||
def generate_response(self) -> str:
|
def generate_response(self) -> str:
|
||||||
|
@ -171,13 +177,3 @@ class Search:
|
||||||
|
|
||||||
return str(formatted_results)
|
return str(formatted_results)
|
||||||
|
|
||||||
def check_kw_ip(self) -> re.Match:
|
|
||||||
"""Checks for keywords related to 'my ip' in the query
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool
|
|
||||||
|
|
||||||
"""
|
|
||||||
return re.search("([^a-z0-9]|^)my *[^a-z0-9] *(ip|internet protocol)" +
|
|
||||||
"($|( *[^a-z0-9] *(((addres|address|adres|" +
|
|
||||||
"adress)|a)? *$)))", self.query.lower())
|
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
def add_ip_card(html_soup: BeautifulSoup, ip: str) -> BeautifulSoup:
|
||||||
|
"""Adds the client's IP address to the search results
|
||||||
|
if query contains keywords
|
||||||
|
|
||||||
|
Args:
|
||||||
|
html_soup: The parsed search result containing the keywords
|
||||||
|
ip: ip address of the client
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
BeautifulSoup
|
||||||
|
|
||||||
|
"""
|
||||||
|
main_div = html_soup.select_one('#main')
|
||||||
|
if main_div:
|
||||||
|
# HTML IP card tag
|
||||||
|
ip_tag = html_soup.new_tag('div')
|
||||||
|
ip_tag['class'] = 'ZINbbc xpd O9g5cc uUPGi'
|
||||||
|
|
||||||
|
# For IP Address html tag
|
||||||
|
ip_address = html_soup.new_tag('div')
|
||||||
|
ip_address['class'] = 'kCrYT ip-address-div'
|
||||||
|
ip_address.string = ip
|
||||||
|
|
||||||
|
# Text below the IP address
|
||||||
|
ip_text = html_soup.new_tag('div')
|
||||||
|
ip_text.string = 'Your public IP address'
|
||||||
|
ip_text['class'] = 'kCrYT ip-text-div'
|
||||||
|
|
||||||
|
# Adding all the above html tags to the IP card
|
||||||
|
ip_tag.append(ip_address)
|
||||||
|
ip_tag.append(ip_text)
|
||||||
|
|
||||||
|
# Insert the element at the top of the result list
|
||||||
|
main_div.insert_before(ip_tag)
|
||||||
|
return html_soup
|
||||||
|
|
||||||
|
def add_calculator_card(html_soup: BeautifulSoup) -> BeautifulSoup:
|
||||||
|
"""Adds the a calculator widget to the search results
|
||||||
|
if query contains keywords
|
||||||
|
|
||||||
|
Args:
|
||||||
|
html_soup: The parsed search result containing the keywords
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
BeautifulSoup
|
||||||
|
"""
|
||||||
|
main_div = html_soup.select_one('#main')
|
||||||
|
if main_div:
|
||||||
|
widget_file = open('app/static/widgets/calculator.html')
|
||||||
|
widget_tag = html_soup.new_tag('div')
|
||||||
|
widget_tag['class'] = 'ZINbbc xpd O9g5cc uUPGi'
|
||||||
|
widget_tag['id'] = 'calculator-wrapper'
|
||||||
|
calculator_text = html_soup.new_tag('div')
|
||||||
|
calculator_text['class'] = 'kCrYT ip-address-div'
|
||||||
|
calculator_text.string = 'Calculator'
|
||||||
|
calculator_widget = html_soup.new_tag('div')
|
||||||
|
calculator_widget.append(BeautifulSoup(widget_file, 'html.parser'));
|
||||||
|
calculator_widget['class'] = 'kCrYT ip-text-div'
|
||||||
|
widget_tag.append(calculator_text)
|
||||||
|
widget_tag.append(calculator_widget)
|
||||||
|
main_div.insert_before(widget_tag)
|
||||||
|
widget_file.close()
|
||||||
|
return html_soup
|
Loading…
Reference in New Issue