2024-01-14, update for chrome extensions

master
Your Name 2024-01-19 17:31:09 +08:00
parent 85444e6c41
commit 29db3dcea9
19 changed files with 1585 additions and 468 deletions

View File

@ -41,7 +41,7 @@ try:
except Exception as exc:
pass
CONST_APP_VERSION = "MaxBot (2024.01.13)"
CONST_APP_VERSION = "MaxBot (2024.01.14)"
CONST_MAXBOT_ANSWER_ONLINE_FILE = "MAXBOT_ONLINE_ANSWER.txt"
CONST_MAXBOT_CONFIG_FILE = "settings.json"
@ -50,6 +50,39 @@ CONST_MAXBOT_INT28_FILE = "MAXBOT_INT28_IDLE.txt"
CONST_MAXBOT_LAST_URL_FILE = "MAXBOT_LAST_URL.txt"
CONST_MAXBOT_QUESTION_FILE = "MAXBOT_QUESTION.txt"
CONST_MAXBLOCK_EXTENSION_NAME = "Maxblockplus_1.0.0"
CONST_MAXBLOCK_EXTENSION_FILTER =[
"*google-analytics.com/*",
"*googletagmanager.com/*",
"*googletagservices.com/*",
"*lndata.com/*",
"*a.amnet.tw/*",
"*adx.c.appier.net/*",
"*clarity.ms/*",
"*cloudfront.com/*",
"*cms.analytics.yahoo.com/*",
"*doubleclick.net/*",
"*e2elog.fetnet.net/*",
"*fundingchoicesmessages.google.com/*",
"*ghtinc.com/*",
"*match.adsrvr.org/*",
"*onead.onevision.com.tw/*",
"*popin.cc/*",
"*rollbar.com/*",
"*sb.scorecardresearch.com/*",
"*tagtoo.co/*",
"*.ssp.hinet.net/*",
"*ticketmaster.sg/js/adblock*",
"*.googlesyndication.com/*",
"*treasuredata.com/*",
"*play.google.com/log?*",
"*www.youtube.com/youtubei/v1/player/heartbeat*",
"*tixcraft.com/js/analytics.js*",
"*ticketmaster.sg/js/adblock.js*",
"*img.uniicreative.com/*",
"*cdn.cookielaw.org/*",
"*tixcraft.com/js/custom.js*",
"*tixcraft.com/js/common.js*",
"*cdnjs.cloudflare.com/ajax/libs/clipboard.js/*"]
CONST_CHROME_VERSION_NOT_MATCH_EN="Please download the WebDriver version to match your browser version."
CONST_CHROME_VERSION_NOT_MATCH_TW="請下載與您瀏覽器相同版本的WebDriver版本或更新您的瀏覽器版本。"
@ -567,7 +600,7 @@ def clean_uc_exe_cache():
return is_cache_exist
def dump_settins_to_maxbot_plus_extension(ext, config_dict):
def dump_settings_to_maxbot_plus_extension(ext, config_dict):
# sync config.
target_path = ext
target_path = os.path.join(target_path, "data")
@ -613,6 +646,20 @@ def dump_settins_to_maxbot_plus_extension(ext, config_dict):
with open(target_path, 'w') as outfile:
outfile.write(json_str)
def dump_settings_to_maxblock_plus_extension(ext, config_dict):
# sync config.
target_path = ext
target_path = os.path.join(target_path, "data")
target_path = os.path.join(target_path, CONST_MAXBOT_CONFIG_FILE)
#print("save as to:", target_path)
try:
os.unlink(target_path)
except Exception as exc:
pass
with open(target_path, 'w') as outfile:
config_dict["domain_filter"]=CONST_MAXBLOCK_EXTENSION_FILTER
json.dump(config_dict, outfile)
def get_uc_options(uc, config_dict, webdriver_path):
options = uc.ChromeOptions()
options.page_load_strategy = 'eager'
@ -637,7 +684,9 @@ def get_uc_options(uc, config_dict, webdriver_path):
if os.path.exists(ext):
# sync config.
if CONST_MAXBOT_EXTENSION_NAME in ext:
dump_settins_to_maxbot_plus_extension(ext, config_dict)
dump_settings_to_maxbot_plus_extension(ext, config_dict)
if CONST_MAXBLOCK_EXTENSION_NAME in ext:
dump_settings_to_maxblock_plus_extension(ext, config_dict)
load_extension_path += ("," + os.path.abspath(ext))
if len(load_extension_path) > 0:

View File

@ -22,7 +22,7 @@ import sys
import threading
import webbrowser
CONST_APP_VERSION = "MaxBot (2024.01.13)"
CONST_APP_VERSION = "MaxBot (2024.01.14)"
CONST_MAXBOT_LAUNCHER_FILE = "config_launcher.json"
CONST_MAXBOT_CONFIG_FILE = "settings.json"

View File

@ -39,7 +39,7 @@ try:
except Exception as exc:
pass
CONST_APP_VERSION = "MaxBot (2024.01.13)"
CONST_APP_VERSION = "MaxBot (2024.01.14)"
CONST_MAXBOT_ANSWER_ONLINE_FILE = "MAXBOT_ONLINE_ANSWER.txt"
CONST_MAXBOT_CONFIG_FILE = "settings.json"

View File

@ -0,0 +1,90 @@
/*
* extension process crashes or your extension is manually stopped at
* chrome://serviceworker-internals
*/
'use strict';
chrome.declarativeNetRequest.onRuleMatchedDebug.addListener((e) => {
const msg = `Navigation blocked to ${e.request.url} on tab ${e.request.tabId}.`;
//console.log(msg);
});
function reload_rule_from_setting(settings)
{
if(!settings.hasOwnProperty("domain_filter")) {
settings.domain_filter = [];
}
initializeDynamicRules(settings.domain_filter);
}
chrome.runtime.onInstalled.addListener(function(){
fetch("data/settings.json")
.then((resp) => resp.json())
.then((settings) =>
{
reload_rule_from_setting(settings);
chrome.storage.local.set(
{
settings: settings,
});
console.log("dump settings.json to extension storage");
}
).catch(error =>
{
console.log('error is', error)
}
);
});
function initializeDynamicRules(domain_filter)
{
const base_rule = {
"id" : 1,
"priority": 1,
"action" : { "type" : "block" },
"condition" : {
"urlFilter": "",
"resourceTypes" : ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}};
let formated_rules = [];
for(let i=0; i<domain_filter.length; i++) {
let new_rule = {};
Object.assign(new_rule, base_rule);
new_rule.condition.urlFilter = domain_filter[i];
new_rule.id = i+1;
formated_rules.push(new_rule);
}
let roles = {
removeRuleIds:[],
addRules: formated_rules
};
const oldRules = chrome.declarativeNetRequest.getDynamicRules();
//console.log(oldRules);
oldRules.then(function(result) {
// here you can use the result of promiseB
//console.log(result);
let removeRuleIds=[]
for(let i=0; i<result.length; i++) {
removeRuleIds.push(result[i].id);
}
roles.removeRuleIds = removeRuleIds;
//console.log(roles);
chrome.declarativeNetRequest.updateDynamicRules(roles,function(){});
});
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
let request_json = request;
let result_json={"answer": "pong from background"};
if(request_json.action=="update_role") {
reload_rule_from_setting(request_json.data.settings);
result_json={"answer": "updating"};
sendResponse(result_json);
}
});

View File

@ -0,0 +1,13 @@
body {
margin: 36px;
}
textarea {
font-family: monospace;
}
.message {
height: 20px;
background: #eee;
padding: 5px;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -10,19 +10,29 @@
"128": "icons/maxblock-128.png"
},
"manifest_version": 3,
"declarative_net_request": {
"rule_resources": [
{
"id": "ruleset_1",
"enabled": true,
"path": "rules_1.json"
}
]
"background": {
"service_worker": "background.js",
"type": "module"
},
"permissions": [
"storage",
"declarativeNetRequest",
"declarativeNetRequestFeedback"
],
"options_page": "options.html",
"web_accessible_resources": [
{
"resources": [
"data/*.json"
],
"extension_ids": [
"*"
],
"matches": [
"*://*/*"
]
}
],
"action": {
"default_icon": "icons/maxblock-128.png",
"default_title": "MaxBlock"

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Extension Options</title>
<meta charset="UTF-8" />
<script src="jquery.min.js"></script>
<link href="dist/bootstrap/bootstrap.min.css" rel="stylesheet" />
<script src="dist/bootstrap/bootstrap.min.js"></script>
<link href="dist/glyphicon/glyphicon.css" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="css/options.css" />
</head>
<body>
<div id="message"></div>
<h2>MaxBlock Settings</h2>
<div class="row g-3">
<label class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
Input example: *.example.com/*
</div>
</div>
<div class="row g-3">
<label for="new_domain" class="col-sm-2 col-form-label">New Domain Filter</label>
<div class="col-sm-8">
<input class="form-control" id="new_domain" value="" />
</div><button class="btn btn-primary col-sm-2" id="add_btn">Add</button>
</div>
<div class="row g-3">
<label class="col-sm-2 col-form-label">Block Domain List</label>
<div class="col-sm-10">
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</th>
<!--th>#</th-->
<th class="domain">Domain Filter</th>
</tr>
</thead>
<tbody id="block_domain_list">
</tbody>
</table>
<button class="btn btn-primary" id="clear_all_btn">Clear All</button>
</div>
</div>
<div class="row g-3">
<div class="col-12">
<button class="btn btn-primary" id="save_btn">Save</button>
</div>
</div>
<script src="options.js"></script>
</body>
</html>

View File

@ -0,0 +1,140 @@
const storage = chrome.storage.local;
const submitButton = document.querySelector('#save_btn');
const addButton = document.querySelector('#add_btn');
const clearAllButton = document.querySelector('#clear_all_btn');
const new_domain = document.querySelector('#new_domain');
var settings = null;
loadChanges();
submitButton.addEventListener('click', saveChanges);
addButton.addEventListener('click', addDomainClick);
clearAllButton.addEventListener('click', clearAllClick);
async function saveChanges()
{
if(settings) {
let domain_filter =[];
$("#block_domain_list a").each(function ()
{
domain_filter.push($(this).attr('data-domain'));
});
//console.log(domain_filter);
settings.domain_filter = domain_filter;
let bundle = {
action: 'update_role',
data: {
'settings': settings
}
};
const return_answer = await chrome.runtime.sendMessage(bundle);
console.log(return_answer);
await storage.set(
{
settings: settings
}
);
}
message('Settings saved');
}
function loadChanges()
{
storage.get('settings', function (items)
{
//console.log(items);
if (items.settings)
{
settings = items.settings
//message('Loaded saved settings.');
if(!settings.hasOwnProperty("domain_filter")) {
settings.domain_filter = [];
}
for (let i = 0; i < settings.domain_filter.length; i++) {
addDomainRow(settings.domain_filter[i]);
}
bind_remove_onclick_event();
} else {
console.log('no settings found');
settings = {};
settings.domain_filter = [];
}
}
);
}
function format_domain_to_tr(user_input_domain)
{
let domain_item = "";
domain_item += '<tr><td>';
domain_item += '<a href="#" data-domain="'+ user_input_domain +'" data-action="remove">';
domain_item += '<span class="glyphicon glyphicon-remove add" aria-hidden="true"></span>';
domain_item += '</a>';
domain_item += '</td>';
//domain_item += '<td>';
//domain_item += '</td>';
domain_item += '<td>';
domain_item += user_input_domain;
domain_item += '</td>';
domain_item += '</tr>';
return domain_item;
}
function addDomainRow(user_input_domain)
{
if(user_input_domain.length > 0) {
let domain_item = format_domain_to_tr(user_input_domain);
$(domain_item).appendTo($("#block_domain_list"));
}
}
function clearAllClick()
{
$("#block_domain_list").html("");
}
function bind_remove_onclick_event()
{
$("#block_domain_list a").click(function(event)
{
//console.log($(this).html());
$(this).parent().parent().remove();
});
}
function addDomainClick()
{
let text = new_domain.value;
if(text.length > 0) {
let eachLine = text.split('\n');
for(let i = 0, l = eachLine.length; i < l; i++) {
addDomainRow(eachLine[i]);
}
// reset
new_domain.value = "";
} else {
new_domain.focus();
}
bind_remove_onclick_event();
}
let messageClearTimer;
function message(msg)
{
clearTimeout(messageClearTimer);
const message = document.querySelector('#message');
message.innerText = msg;
messageClearTimer = setTimeout(function ()
{
message.innerText = '';
}, 3000);
}

View File

@ -1,318 +0,0 @@
[
{
"id": 1,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*google-analytics.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 2,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*googletagmanager.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 3,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*googletagservices.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 4,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*lndata.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 5,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*a.amnet.tw/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 6,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*ad.setn.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 7,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*adx.c.appier.net/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 8,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*cbcapi.setn.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media"]
}
},
{
"id": 9,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*clarity.ms/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 10,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*cloudfront.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media"]
}
},
{
"id": 11,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*cms.analytics.yahoo.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media"]
}
},
{
"id": 12,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*doubleclick.net/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 13,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*e2elog.fetnet.net/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 14,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*fundingchoicesmessages.google.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 15,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*ghtinc.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 16,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*match.adsrvr.org/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 17,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*onead.onevision.com.tw/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 18,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*popin.cc/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 19,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*rollbar.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 20,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*sb.scorecardresearch.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 22,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*tagtoo.co/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 23,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*.ssp.hinet.net/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 24,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*ticketmaster.sg/js/adblock*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media"]
}
},
{
"id": 25,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*.googlesyndication.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 26,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*treasuredata.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 27,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*ubas.setn.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 28,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*play.google.com/log?*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media"]
}
},
{
"id": 29,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*www.youtube.com/youtubei/v1/player/heartbeat*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media"]
}
},
{
"id": 30,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*tixcraft.com/js/analytics.js*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media"]
}
},
{
"id": 31,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*ticketmaster.sg/js/adblock.js*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media"]
}
},
{
"id": 32,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*img.uniicreative.com/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media"]
}
},
{
"id": 33,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*cdn.cookielaw.org/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
},
{
"id": 34,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*tixcraft.com/js/custom.js*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media"]
}
},
{
"id": 35,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*tixcraft.com/js/common.js*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media"]
}
},
{
"id": 36,
"priority": 1,
"action": { "type": "block"},
"condition": {
"urlFilter": "*cdnjs.cloudflare.com/ajax/libs/clipboard.js/*",
"resourceTypes": ["main_frame", "sub_frame", "script", "image", "font", "xmlhttprequest", "media", "stylesheet"]
}
}
]

View File

@ -21,12 +21,14 @@ chrome.runtime.onInstalled.addListener(function(){
chrome.storage.local.set(
{
settings: settings,
status: default_status,
webserver_runing: default_webserver_runing
}
);
console.log("dump settings.json to extension storage");
}
).catch(error =>
{
console.log('error is', error)
}
);
});

View File

@ -174,7 +174,6 @@ async function tixcraft_get_ocr_answer(api_url, image_data)
}
};
let bundle_string = JSON.stringify(bundle);
const return_answer = await chrome.runtime.sendMessage(bundle);
//console.log(return_answer);

View File

@ -1,146 +1,156 @@
<!DOCTYPE html>
<html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Extension Options</title>
<meta charset="UTF-8">
<link href="dist/bootstrap/bootstrap.min.css" rel="stylesheet">
<meta charset="UTF-8" />
<link href="dist/bootstrap/bootstrap.min.css" rel="stylesheet" />
<script src="dist/bootstrap/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/options.css" />
</head>
<body>
<div id="message"></div>
<h2>MaxBot Settings</h2>
<div class="row g-3">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#home-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="true">Perference</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="advanced-tab" data-bs-toggle="tab" data-bs-target="#advanced-tab-pane" type="button" role="tab" aria-controls="advanced-tab-pane" aria-selected="false">Advanced</button>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div id="message"></div>
<h2>MaxBot Settings</h2>
<div class="row g-3">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation"><button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#home-tab-pane" type="button" role="tab" aria-controls="home-tab-pane" aria-selected="true">Perference</button></li>
<li class="nav-item" role="presentation"><button class="nav-link" id="advanced-tab" data-bs-toggle="tab" data-bs-target="#advanced-tab-pane" type="button" role="tab" aria-controls="advanced-tab-pane" aria-selected="false">Advanced</button></li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home-tab-pane" role="tabpanel" aria-labelledby="home-tab" tabindex="0">
<div class="row mb-3">
<label for="ticket_number" class="col-sm-2 col-form-label">Ticket Number</label>
<div class="col-sm-10">
<select id="ticket_number" class="form-select" aria-label="Default select">
<option selected>Ticket Number</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option selected="selected">
Ticket Number
</option>
<option value="1">
1
</option>
<option value="2">
2
</option>
<option value="3">
3
</option>
<option value="4">
4
</option>
<option value="5">
5
</option>
<option value="6">
6
</option>
<option value="7">
7
</option>
<option value="8">
8
</option>
<option value="9">
9
</option>
<option value="10">
10
</option>
</select>
</div>
</div>
<div class="row mb-3">
<label for="date_select_mode" class="col-sm-2 col-form-label">Date Select Order</label>
<div class="col-sm-10">
<select id="date_select_mode" class="form-select" aria-label="Default select">
<option value="from top to bottom">from top to bottom</option>
<option value="from bottom to top">from bottom to top</option>
<option value="center">center</option>
<option value="random">random</option>
<option value="from top to bottom">
from top to bottom
</option>
<option value="from bottom to top">
from bottom to top
</option>
<option value="center">
center
</option>
<option value="random">
random
</option>
</select>
</div>
</div>
<div class="row mb-3">
<label for="date_keyword" class="col-sm-2 col-form-label">Date Keywords</label>
<div class="col-sm-10">
<textarea class="form-control" id="date_keyword" rows="3"></textarea>
</div>
</div>
<div class="row mb-3">
<label for="area_select_mode" class="col-sm-2 col-form-label">Area Select Order</label>
<div class="col-sm-10">
<select id="area_select_mode" class="form-select" aria-label="Default select">
<option value="from top to bottom">from top to bottom</option>
<option value="from bottom to top">from bottom to top</option>
<option value="center">center</option>
<option value="random">random</option>
<option value="from top to bottom">
from top to bottom
</option>
<option value="from bottom to top">
from bottom to top
</option>
<option value="center">
center
</option>
<option value="random">
random
</option>
</select>
</div>
</div>
<div class="row mb-3">
<label for="area_keyword" class="col-sm-2 col-form-label">Area Keywords</label>
<div class="col-sm-10">
<textarea class="form-control" id="area_keyword" rows="3"></textarea>
</div>
</div>
<div class="row mb-3">
<label for="keyword_exclude" class="col-sm-2 col-form-label">Exclude Keywords</label>
<div class="col-sm-10">
<textarea class="form-control" id="keyword_exclude" rows="3"></textarea>
</div>
</div>
</div>
<div class="tab-pane fade" id="advanced-tab-pane" role="tabpanel" aria-labelledby="advanced-tab" tabindex="0">
<div class="row mb-3">
<label for="auto_reload_page_interval" class="col-sm-2 col-form-label">Reload page interval(sec.)</label>
<div class="col-sm-10">
<input class="form-control" id="auto_reload_page_interval" value="" />
</div>
</div>
<div class="row mb-3">
<label class="col-sm-2 col-form-label form-check-label" for="disable_adjacent_seat">
Disable Adjacent Seat
</label>
<label class="col-sm-2 col-form-label form-check-label" for="disable_adjacent_seat">Disable Adjacent Seat</label>
<div class="col-sm-10">
<input class="form-check-input" type="checkbox" id="disable_adjacent_seat">
<input class="form-check-input" type="checkbox" id="disable_adjacent_seat" />
</div>
</div>
<div class="row mb-3">
<label class="col-sm-2 col-form-label form-check-label" for="ocr_captcha_enable">
OCR
</label>
<label class="col-sm-2 col-form-label form-check-label" for="ocr_captcha_enable">OCR</label>
<div class="col-sm-10">
<input class="form-check-input" type="checkbox" id="ocr_captcha_enable">
<input class="form-check-input" type="checkbox" id="ocr_captcha_enable" />
</div>
</div>
<div class="row mb-3">
<label for="remote_url" class="col-sm-2 col-form-label">OCR Server URL</label>
<div class="col-sm-10">
<input class="form-control" id="remote_url" value="" />
</div>
</div>
<div class="row mb-3">
<label class="col-sm-2 col-form-label form-check-label" for="ocr_captcha_use_public_server">
MaxBot Public OCR Server
</label>
<label class="col-sm-2 col-form-label form-check-label" for="ocr_captcha_use_public_server">MaxBot Public OCR Server</label>
<div class="col-sm-10">
<input class="form-check-input" type="checkbox" id="ocr_captcha_use_public_server">
<input class="form-check-input" type="checkbox" id="ocr_captcha_use_public_server" />
</div>
</div>
</div>
</div>
</div>
<div class="col-12">
<button class="btn btn-primary" id="save_btn">Save</button>
</div>
</div>
<script src="options.js"></script>
</div>
<script src="options.js"></script>
</body>
</html>