Code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#################################################### | |
#### code.gs ######### | |
################################################### | |
const favIcon = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('config').getRange('B3').getValues() | |
function doGet() { | |
return HtmlService.createTemplateFromFile('index') | |
.evaluate() | |
.addMetaTag('viewport', 'width=device-width, initial-scale=1') | |
.setTitle('Web Apps ระบบโหวต SUPERSTAR') | |
.setFaviconUrl(favIcon) | |
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL) | |
} | |
/** @Include Files */ | |
function include(filename) { | |
return HtmlService.createHtmlOutputFromFile(filename).getContent(); | |
} | |
/** @getURL */ | |
function getScriptURL() { | |
return ScriptApp.getService().getUrl(); | |
} | |
/** @อ่านค่า config จากตาราง **/ | |
function getConfig(){ | |
const ws = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("config") | |
const dataf = ws.getRange(1,2,ws.getLastRow()).getDisplayValues(); | |
Logger. log(dataf) | |
return dataf | |
} | |
/** @อ่านค่าคะแนนผลโหวต */ | |
function getDataStar(){ | |
const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("supstar") | |
const data = ss.getDataRange().getDisplayValues().slice(1) | |
Logger.log(data) | |
return data | |
} | |
/** @ตรวจเบอร์โทร */ | |
function identity(phonein){ | |
const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("data") | |
const data = ss.getRange(1,2,ss.getLastRow()).getDisplayValues() | |
let out = '' | |
Logger.log(data) | |
data.forEach(function(val){ | |
Logger.log(val) | |
if(val == phonein){ | |
out= 'true' | |
} | |
}) | |
if (out == ''){ | |
out = 'null' | |
} | |
Logger.log(out) | |
return out; | |
} | |
/** @บันทึกลงตาราง */ | |
function submitForm(obj){ | |
const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("data") | |
Logger.log(obj) | |
ss.appendRow([ | |
new Date(), | |
obj.phone, | |
obj.img1, | |
obj.img2, | |
obj.img3, | |
obj.img4, | |
obj.img5 | |
]) | |
} | |
############################################## | |
##### index.html ######### | |
############################################## | |
<!DOCTYPE html> | |
<html lang="th"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta name="description" content="Superstar voting system created by Guruchian"> | |
<title>Web Apps ระบบโหวต SUPERSTAR</title> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"/> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/> | |
<!-- Include the library files chart--> | |
<script src="https://www.rgraph.net/libraries/RGraph.svg.common.core.js"></script> | |
<!-- Include the library files hbar chart--> | |
<script src="https://www.rgraph.net/libraries/RGraph.svg.hbar.js"></script> | |
<!-- Include the library files pie chart--> | |
<script src="https://www.rgraph.net/libraries/RGraph.svg.pie.js"></script> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=K2D&family=Sarabun&display=swap'); | |
body, html { | |
height: 100%; | |
margin:0; | |
background-image: url("https://img.freepik.com/free-vector/floral-background-with-leaves_23-2148877190.jpg?size=626&ext=jpg"); | |
background-position: center center; | |
background-repeat: no-repeat; | |
background-size: cover; | |
background-attachment: fixed; | |
font-family: K2D; | |
} | |
.imgbtn { | |
background-color: #4CAF5000; | |
border: none; | |
padding: 5px; | |
text-align: center; | |
text-decoration: none; | |
display: inline-block; | |
font-size: 16px; | |
} | |
.customer{ | |
width: 400px; | |
max-width: 100%; | |
margin: 0 auto 30px; | |
font-size: 1.2em; | |
} | |
h2 { | |
font-size: 2rem; | |
text-align: center; | |
font-weight: 600; | |
} | |
h3 { | |
font-size: 1.5rem; | |
color: #fff; | |
font-weight: 600; | |
text-align: center; | |
font-size: 1.5rem; | |
} | |
/** text-stroke */ | |
.outline{ | |
color: #fff; | |
text-shadow: -1px 1px 2px #000, | |
1px 1px 2px #000, | |
1px -1px 0 #000, | |
-1px -1px 0 #000; | |
font-weight: 400; | |
text-align: center; | |
} | |
/** loader page */ | |
#loader{ | |
position: fixed; | |
width: 100%; | |
height: 100vh; | |
//background: #ffed8a url('https://giffiles.alphacoders.com/153/15357.gif') no-repeat center; | |
background-size: 100vw auto; | |
background: #fff url('https://i.pinimg.com/originals/7d/a7/c8/7da7c8c153dece39bc993839b192c94e.gif') no-repeat center; | |
background-size: 300px auto; | |
z-index: 999; | |
} | |
</style> | |
</head> | |
<body> | |
<!-- loader page --> | |
<div id="loader"></div> | |
<div id="content" class="d-flex justify-content-center"> | |
<div class="container"> | |
<div class="text-center my-4 outline"> | |
<div id="logo" class="mb-3" ><img src="https://semicon.github.io/img/logo2.png" width="80px"></div> | |
<div id ="title"></div> | |
</div> | |
<div id="phoneinput" class="form-label text-center" for="tel" style ="display:block"> | |
<div id = "chart" class="my-4 text-center"> | |
<img src="https://semicon.github.io/img/btn.png" width="100px"> | |
</div> | |
กรอกหมายเลขโทรศัพท์ผู้โหวต 1 เบอร์โหวตได้ครั้งเดียว | |
<!-- phone number input field --> | |
<div class="row gs-3 d-flex justify-content-center mt-2"> | |
<div class="customer"> | |
<div class="input-group"> | |
<div class="input-group-text"><i class="fa-solid fa-mobile-screen fa-2x"></i></div> | |
<input type="tel" class="form-control" id="phone" placeholder="0xxxxxxxxx" maxlength="10"> | |
<button class="btn btn-secondary px-3" style="font-size: 18px;" onclick="submitPhone()">ตกลง</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- voting form --> | |
<div class ="row gs-3 d-flex justify-content-center"> | |
<form id="myForm" onsubmit="submit(this)" style="display:none"> | |
<div class="text-center text-light my-4 p-4 bg-info bg-opacity-10 outline"><h4>คลิกที่รูปเพื่อโหวต</h4></div> | |
<div id="vote_detail"></div> | |
</form> | |
</div> | |
<!-- The canvas tag (this is where the chart shows up) --> | |
<div id="graph" class ="text-center" style="display:block"> | |
<div class="text-center text-light my-4 p-4 bg-info bg-opacity-10 outline"><h4>สรุปผลโหวต</h4></div> | |
<!-- This is where the bar chart shows up --> | |
<!-- /** #### start bar chart code ### **/ --> | |
<div id="graphbar" class ="text-center mt-4"> | |
<div class ="text-center"> | |
<div style="width: 500px; height: 250px; display: inline-block; background: #00000000;" id="chart-bar"> | |
</div> | |
</div> | |
</div> | |
<!-- /** #### end bar chart code ### **/ --> | |
<!-- This is where the donut chart shows up --> | |
<!-- /** #### start pie chart code ### **/ --> | |
<div id="graphpie" class ="text-center mt-4"> | |
<div class ="text-center"> | |
<div style="width: 350px; height: 350px; display: inline-block; background: #00000000; border-radius: 5px" id="chart-pie"></div> | |
</div> | |
</div> | |
<!-- /** #### end pie chart code ### **/ --> | |
</div> | |
</div> | |
</div> | |
<p class="mt-4 text-center"> | |
รูปภาพและข้อมูลบนเว็บไซต์ใช้ในการทดสอบและเป็นตัวอย่างเท่านั้น | |
</p> | |
<div class="bg-info text-dark bg-opacity-10 mt-4 p-4 text-center"> | |
Copyleft <img src="https://mirrors.creativecommons.org/presskit/icons/heart.red.png" width="25" height="25" alt="cc" /> 2022, <a href="https://guruchian.blogspot.com" target="_blank">guruchian.blogspot.com</a> Created by <a href="https://web.facebook.com/ph.wichian" target="_blank"> Dr.KruChian</a> | |
</div> | |
<?!=include('js')?> | |
<!-- Include the library bootstrap.js files --> | |
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script> | |
<!-- Include the library sweetalert2 files --> | |
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script> | |
</body> | |
</html> | |
############################### | |
###### js.html ####### | |
############################### | |
<script> | |
window.onload = function() { | |
google.script.run.withSuccessHandler(config).getConfig() | |
} | |
// These variables are global values that apply to all generated charts. | |
let barData = [] //ข้อมูลของกราฟแท่ง -------------****** | |
let chartLabels = [] //แถบรายการของกราฟ ------------****** | |
let pieData = [] //ข้อมูลของกราฟโดนัท ------------****** | |
let maxValue = 0 //ตั้งค่าสูงสุดของประชากร ----------****** | |
let logo ='' | |
/** //Add Config ***/ | |
function config(dataf){ | |
if(dataf.length > 0){ | |
maxValue = dataf[1] | |
const logo = "<img src='"+dataf[3]+"' width='"+dataf[4]+"'>"; | |
const title = "<h2>"+dataf[5]+"</h2>"; | |
document.getElementById('logo').innerHTML = logo; | |
document.getElementById('title').innerHTML = title; | |
google.script.run.withSuccessHandler(createTable).getDataStar() | |
} | |
} | |
/** //CREATE PAGE VOTE ***/ | |
function createTable(datas) { | |
//console.log(datas) | |
if(datas.length > 0){ | |
let result = ""; | |
for(let i = 0; i < datas.length; i++) { | |
let n = i+1 | |
let labelx = datas[i][1] | |
let datax = datas[i][3] | |
barData.push(datax) | |
chartLabels.push(labelx) | |
pieData.push([datax,maxValue-datax]) | |
result += "<div class='customer text-center mt-4'>"; | |
result += "<button typr='submit' class='btn imgbtn my-4' onclick='vote("+n+")'><img src ='"+datas[i][2]+"' width='250px'></button>"+ | |
"<div class ='outline'> <h3>"+datas[i][1]+"</h3></div>"+ | |
"<input type='hidden' id='img"+n+"' name='img"+ n +" value=''>"; | |
result += "</div>"; | |
} | |
document.getElementById('vote_detail').innerHTML = result; | |
createGraph() | |
} | |
} | |
/** //CREATE PAGE VOTE ***/ | |
function createGraph(){ | |
displayContent() | |
/** ################# BAR CHART ###################### **/ | |
/** #### start bar chart code ### **/ | |
//// // A black and orange SVG Horizontal Bar chart ///// | |
//The data for the chart | |
data = barData; | |
// labelsAbove labels. | |
labels = chartLabels; | |
bar_bg = new RGraph.SVG.HBar({ | |
id: 'chart-bar', | |
data: [1,1,1,1,1], | |
options: { | |
colors: ['gray'], | |
textColor: 'black', | |
textFont:'K2D, Verdana, sans-serif', | |
xaxisScale: false, | |
backgroundGrid: false, | |
xaxis: false, | |
yaxis: false, | |
marginInner: 3, | |
yaxisLabels: labels, | |
textColor: 'rgba(0,0,0,0)', | |
labelsAboveColor: 'black', | |
labelsAboveSpecific: data | |
} | |
}).draw().responsive([ | |
{maxWidth: null, width: 500, height: 250, options: {textSize: 12},parentCss:{'_float':'center', textAlign:'center'}}, | |
{maxWidth: 800, width: 350, height: 250, options: {textSize: 10},parentCss:{'_float':'center', textAlign:'center'}} | |
]); | |
bar_fg = new RGraph.SVG.HBar({ | |
id: 'chart-bar', | |
data: data, | |
options: { | |
colors: ['orange'], | |
textColor: 'black', | |
textFont:'K2D, Verdana, sans-serif', | |
yaxisLabels: labels, | |
xaxisScaleMax: Number(maxValue), | |
xaxisScale: false, | |
xaxis: false, | |
yaxis: false, | |
backgroundGrid: false, | |
marginInner: 3 | |
} | |
// The orange chart uses the wave() effect. | |
}).grow({callback: function () | |
{ | |
bar_bg.set('labelsAbove', true); | |
RGraph.SVG.redraw(); | |
} | |
}).responsive([ | |
{maxWidth: null, width: 500, height: 250, options: {textSize: 12},parentCss:{'_float':'center', textAlign:'center'}}, | |
{maxWidth: 700, width: 350, height: 250, options: {textSize: 10},parentCss:{'_float':'center', textAlign:'center'}} | |
]); | |
/** #### end bar chart code ### **/ | |
/** ################# PIE CHART ###################### **/ | |
/** #### start pie chart code ### **/ | |
/** // A Meter-esque SVG Pie chart **/ | |
RGraph.SVG.GLOBALS = { | |
colors: ['#00000002'], | |
donut: true, | |
donutWidth: 15, | |
marginLeft: 5, | |
marginRight: 5, | |
marginTop: 5, | |
marginBottom: 5 | |
}; | |
// Create the outer Donuts background | |
pie1a = new RGraph.SVG.Pie({ | |
id: 'chart-pie', | |
data: [maxValue], | |
options: { | |
} | |
}).draw().responsive([ | |
{maxWidth:null,parentCss:{'_float':'center',textAlign:'center'},parentCss:{'_float':'center', textAlign:'center'}}, | |
{maxWidth:700,parentCss:{'_float':'center',textAlign:'center'},parentCss:{'_float':'center', textAlign:'center'}} | |
]); | |
// Create the outer Donut. This chart shows the real values. | |
pie1b = new RGraph.SVG.Pie({ | |
id: 'chart-pie', | |
data: pieData[0], | |
options: { | |
colors: ['#9AC17E','transparent'] | |
} | |
}).roundRobin(); | |
// Create the second Donuts background. Note the reduction in the | |
// radius setting. | |
pie2a = new RGraph.SVG.Pie({ | |
id: 'chart-pie', | |
data: [maxValue], | |
options: { | |
radius: pie1a.radius - 18 | |
} | |
}).draw(); | |
// Create the second Donuts foreground. It uses the same radius | |
// as the above Donut. | |
pie2b = new RGraph.SVG.Pie({ | |
id: 'chart-pie', | |
data: pieData[1], | |
options: { | |
radius: pie2a.radius, | |
colors: ['#FADA31','transparent'] | |
} | |
}).roundRobin(); | |
// Create the background for the third Donut chart. The radius has | |
// been reduced again from the second Donut chart. | |
pie3a = new RGraph.SVG.Pie({ | |
id: 'chart-pie', | |
data: [maxValue], | |
options: { | |
radius: pie2a.radius - 18 | |
} | |
}).draw(); | |
// The foreground for the third donut chart. | |
pie3b = new RGraph.SVG.Pie({ | |
id: 'chart-pie', | |
data: pieData[2], | |
options: { | |
radius: pie3a.radius, | |
colors: ['#D3696B','transparent'] | |
} | |
}).roundRobin(); | |
// The background for the fourth Donut chart. Again, | |
// the radius has been reduced from the previous | |
// Donut chart. | |
pie4a = new RGraph.SVG.Pie({ | |
id: 'chart-pie', | |
data: [maxValue], | |
options: { | |
radius: pie3a.radius - 18 | |
} | |
}).draw(); | |
// The foreground for the the fourth Donut chart. | |
pie4b = new RGraph.SVG.Pie({ | |
id: 'chart-pie', | |
data: pieData[3], | |
options: { | |
radius: pie4a.radius, | |
colors: ['#82C1E0','transparent'] | |
} | |
}).roundRobin(); | |
// The background for the fifth Donut chart. Again, | |
// the radius has been reduced from the previous | |
// Donut chart. | |
pie5a = new RGraph.SVG.Pie({ | |
id: 'chart-pie', | |
data: [maxValue], | |
options: { | |
radius: pie4a.radius - 18 | |
} | |
}).draw(); | |
// The foreground for the the fifth Donut chart. | |
pie5b = new RGraph.SVG.Pie({ | |
id: 'chart-pie', | |
data: pieData[4], | |
options: { | |
radius: pie5a.radius, | |
colors: ['#EFC1E0','transparent'] | |
} | |
}).roundRobin(); | |
// Add some custom text - the label - for the outer Donut chart. | |
RGraph.SVG.text({ | |
object: pie1a, | |
font:'K2D, Verdana, sans-serif', | |
text: labels[0] +" -> "+ pieData[0][0], | |
x: pie1a.centerx - 5, | |
y: pie1a.centery - pie1a.radius + (RGraph.SVG.GLOBALS.donutWidth / 2), | |
color: 'black', | |
bold: true, | |
halign: 'right', | |
valign: 'center', | |
//background: 'rgba(255,255,255,0.35)', | |
padding: 2 | |
}); | |
// The label for the second Donut. | |
RGraph.SVG.text({ | |
object: pie1a, | |
font:'K2D, Verdana, sans-serif', | |
text: labels[1] +" -> "+ pieData[1][0], | |
x: pie2a.centerx - 5, | |
y: pie2a.centery - pie2a.radius + (RGraph.SVG.GLOBALS.donutWidth / 2), | |
color: 'black', | |
bold: true, | |
halign: 'right', | |
valign: 'center', | |
//background: 'rgba(255,255,255,0.35)', | |
padding: 2 | |
}); | |
// The label for the third Donut. | |
RGraph.SVG.text({ | |
object: pie1a, | |
font:'K2D, Verdana, sans-serif', | |
text: labels[2] +" -> "+ pieData[2][0], | |
x: pie3a.centerx - 5, | |
y: pie3a.centery - pie3a.radius + (RGraph.SVG.GLOBALS.donutWidth / 2), | |
color: 'black', | |
bold: true, | |
halign: 'right', | |
valign: 'center', | |
//background: 'rgba(255,255,255,0.35)', | |
padding: 2 | |
}); | |
// The label for the fourth Donut - the innermost one. | |
RGraph.SVG.text({ | |
object: pie1a, | |
font:'K2D, Verdana, sans-serif', | |
text: labels[3] +" -> "+ pieData[3][0], | |
x: pie4a.centerx - 5, | |
y: pie4a.centery - pie4a.radius + (RGraph.SVG.GLOBALS.donutWidth / 2), | |
color: 'black', | |
bold: true, | |
halign: 'right', | |
valign: 'center', | |
//background: 'rgba(255,255,255,0.35)', | |
padding: 2 | |
}); | |
// The label for the fourth Donut - the innermost one. | |
RGraph.SVG.text({ | |
object: pie1a, | |
font:'K2D, Verdana, sans-serif', | |
text: labels[4] +" -> "+ pieData[4][0], | |
x: pie5a.centerx - 5, | |
y: pie5a.centery - pie5a.radius + (RGraph.SVG.GLOBALS.donutWidth / 2), | |
color: 'black', | |
bold: true, | |
halign: 'right', | |
valign: 'center', | |
//background: 'rgba(255,255,255,0.35)', | |
padding: 2 | |
}); | |
/** #### end pie chart code ### **/ | |
} | |
/** // function to handle click on submit button **/ | |
function submitPhone(){ | |
var input = document.getElementById("phone").value; | |
// format for the phone number | |
var format = /^\(?([0]{1})\)?[-]?([1-9]{1})[-]?([0-9]{8})$/; | |
// conditional statements | |
if(input === ""){ | |
//alert("Please Enter Phone Number") | |
Swal.fire({ | |
title: 'ขออภัย !', | |
html: '<h5 class="text-danger"> กรุณาใส่หมายเลขโทรศัพท์ </h5>', | |
showClass: { | |
popup: 'animate__animated animate__fadeInDown' | |
}, | |
hideClass: { | |
popup: 'animate__animated animate__fadeOutUp' | |
} | |
}) | |
}else if(input.match(format)){ | |
checkPhoneNumber(input); | |
}else{ | |
//alert("Please Enter Phone Number") | |
Swal.fire({ | |
title: 'ขออภัย !', | |
html: '<h5 class="text-danger"> รูปแบบหมายเลขโทรศัพท์ไม่ถูกต้อง </h5>', | |
showClass: { | |
popup: 'animate__animated animate__fadeInDown' | |
}, | |
hideClass: { | |
popup: 'animate__animated animate__fadeOutUp' | |
} | |
}) | |
} | |
} | |
/** ###### Submit phone number to GS.##### **/ | |
function checkPhoneNumber(input) { | |
phonein = "`"+input | |
//console.log(phonein) | |
google.script.run.withSuccessHandler(function(out){ | |
//console.log(out) | |
if(out == 'null'){ | |
document.getElementById("myForm").style.display = "block"; | |
document.getElementById("graph").style.display = "none"; | |
document.getElementById('phoneinput').style.display = "none"; | |
document.getElementById('phone').value = "" | |
}else{ | |
document.getElementById('phone').value = "" | |
Swal.fire({ | |
title: 'ขออภัย !', | |
html: '<h5 class="text-danger"> 1 เบอร์ โหวต 1 ครั้ง </h5>', | |
showClass: { | |
popup: 'animate__animated animate__fadeInDown' | |
}, | |
hideClass: { | |
popup: 'animate__animated animate__fadeOutUp' | |
} | |
}); | |
} | |
}).identity(phonein) | |
} | |
/** ###### Click Vote ##### **/ | |
function vote(e){ | |
event.preventDefault(); | |
//console.log("eee : "+e) | |
document.getElementById("myForm").style.display = "none"; | |
document.getElementById('img'+e).value = 1 | |
let obj = {} | |
obj.phone = phonein | |
obj.img1 = document.getElementById('img1').value | |
obj.img2 = document.getElementById('img2').value | |
obj.img3 = document.getElementById('img3').value | |
obj.img4 = document.getElementById('img4').value | |
obj.img5 = document.getElementById('img5').value | |
google.script.run.withSuccessHandler(function(){ | |
document.getElementById('phone').value = ""; | |
document.getElementById("myForm").reset(); | |
google.script.run.withSuccessHandler(config).getConfig() | |
reLoad() | |
}).submitForm(obj) | |
} | |
/** ##### reLoad page ##### **/ | |
function reLoad() { | |
google.script.run.withSuccessHandler(function(url){ | |
window.open(url,'_top'); | |
}).getScriptURL(); | |
} | |
/** ##### script loader page ##### **/ | |
let loader; | |
/** Hide the content section and show the loader**/ | |
function loadNow(opacity) { | |
if (opacity <= 0) { | |
displayContent(); | |
} else { | |
loader.style.opacity = opacity; | |
window.setTimeout(function() { | |
loadNow(opacity - 0.005); | |
}, 50); | |
} | |
} | |
/** Hide the loader and show its contents. **/ | |
function displayContent() { | |
loader.style.display = 'none'; | |
document.getElementById('content').style.display = 'block'; | |
} | |
/** loader **/ | |
document.addEventListener("DOMContentLoaded", function() { | |
loader = document.getElementById('loader'); | |
loadNow(1); | |
}); | |
</script> | |