aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <m.shulhan@gmail.com>2020-03-06 00:57:07 +0700
committerShulhan <m.shulhan@gmail.com>2020-03-16 01:17:35 +0700
commit23a277ba6c3513346e29d39c5b2226219981fce0 (patch)
treefa332a5486e4b2ee2e78baa57384fa40c0a346d1
parentbf9240a2b91ec855a772c5fdc98ddd89ab28b6e5 (diff)
downloadkamusku-23a277ba6c3513346e29d39c5b2226219981fce0.tar.xz
kbbi: tambah fitur pencarian definisi kata
-rw-r--r--client.go100
-rw-r--r--client_test.go21
-rw-r--r--cmd/kbbi/main.go25
-rw-r--r--definisi_kata.go43
-rw-r--r--kbbi.go21
-rw-r--r--parser.go51
-rw-r--r--testdata/entri.html396
7 files changed, 634 insertions, 23 deletions
diff --git a/client.go b/client.go
index 69401cd..7c82fee 100644
--- a/client.go
+++ b/client.go
@@ -16,6 +16,7 @@ import (
"strings"
"time"
+ "github.com/shuLhan/share/lib/debug"
"golang.org/x/net/html"
"golang.org/x/net/publicsuffix"
)
@@ -67,6 +68,35 @@ func New(cookies []*http.Cookie) (cl *Client, err error) {
}
//
+// CariDefinisi dari kata.
+//
+func (cl Client) CariDefinisi(kata string) (defKata []*DefinisiKata, err error) {
+ entriURL := baseURL + entriPath + kata
+ res, err := cl.httpc.Get(entriURL)
+ if err != nil {
+ return nil, fmt.Errorf("Cari %q: %w", kata, err)
+ }
+
+ defer res.Body.Close()
+
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return nil, fmt.Errorf("Cari %q: %w", kata, err)
+ }
+
+ if debug.Value >= 2 {
+ fmt.Printf(">>> HTML body for %s:\n%s", entriURL, body)
+ }
+
+ defKata, err = parseHTMLEntri(body)
+ if err != nil {
+ return nil, fmt.Errorf("Cari %q: %w", kata, err)
+ }
+
+ return defKata, nil
+}
+
+//
// ListKataDasar get list of kata dasar
//
func (cl Client) ListKataDasar() (kataDasar daftarKata, err error) {
@@ -174,6 +204,55 @@ func (cl *Client) SetCookies(cookies []*http.Cookie) {
}
}
+//
+// parseHTMLEntri parse HTML body from "/entri/<kata>" page to find the
+// definition of the word.
+//
+func parseHTMLEntri(htmlBody []byte) (daftarDefinisi []*DefinisiKata, err error) {
+ node, err := html.Parse(bytes.NewReader(htmlBody))
+ if err != nil {
+ return nil, err
+ }
+
+ var prev *html.Node
+
+ for {
+ switch {
+ case node.FirstChild != nil && node.FirstChild != prev &&
+ node.LastChild != prev:
+ node = node.FirstChild
+ case node.NextSibling != nil:
+ node = node.NextSibling
+ default:
+ prev = node
+ node = node.Parent
+ }
+ if node == nil {
+ break
+ }
+
+ if node.Type != html.ElementNode {
+ continue
+ }
+ if node.Data != tagNameOrderedList {
+ continue
+ }
+
+ li := getFirstChild(node)
+ for li != nil {
+ defKata := parseDefinisiKata(li)
+ if defKata == nil {
+ break
+ }
+ daftarDefinisi = append(daftarDefinisi, defKata)
+ li = getNextSibling(li)
+ }
+ node = node.NextSibling
+ }
+
+ return daftarDefinisi, nil
+}
+
func (cl Client) parseHTMLKataDasar(htmlBody []byte) (kataDasar daftarKata, err error) {
node, err := html.Parse(bytes.NewReader(htmlBody))
if err != nil {
@@ -186,7 +265,8 @@ func (cl Client) parseHTMLKataDasar(htmlBody []byte) (kataDasar daftarKata, err
for {
switch {
- case node.FirstChild != nil && node.FirstChild != prev && node.LastChild != prev:
+ case node.FirstChild != nil && node.FirstChild != prev &&
+ node.LastChild != prev:
node = node.FirstChild
case node.NextSibling != nil:
node = node.NextSibling
@@ -201,14 +281,14 @@ func (cl Client) parseHTMLKataDasar(htmlBody []byte) (kataDasar daftarKata, err
if node.Type != html.ElementNode {
continue
}
- if node.Data != elementTypeAnchor {
+ if node.Data != tagNameAnchor {
continue
}
for _, attr := range node.Attr {
if attr.Key != attrNameHref {
continue
}
- if !strings.HasPrefix(attr.Val, entriURL) {
+ if !strings.HasPrefix(attr.Val, entriPath) {
continue
}
k := strings.TrimSpace(node.FirstChild.Data)
@@ -232,7 +312,8 @@ func (cl Client) parseHTMLLogin(htmlBody []byte) (token string, err error) {
for {
switch {
- case node.FirstChild != nil && node.FirstChild != prev && node.LastChild != prev:
+ case node.FirstChild != nil && node.FirstChild != prev &&
+ node.LastChild != prev:
node = node.FirstChild
case node.NextSibling != nil:
node = node.NextSibling
@@ -247,7 +328,7 @@ func (cl Client) parseHTMLLogin(htmlBody []byte) (token string, err error) {
if node.Type != html.ElementNode {
continue
}
- if node.Data != elementTypeInput {
+ if node.Data != tagNameInput {
continue
}
for _, attr := range node.Attr {
@@ -293,12 +374,3 @@ func (cl *Client) preLogin() (token string, err error) {
return token, nil
}
-
-func getAttrValue(attrs []html.Attribute) string {
- for _, attr := range attrs {
- if attr.Key == attrNameValue {
- return attr.Val
- }
- }
- return ""
-}
diff --git a/client_test.go b/client_test.go
index 9b4e0a6..7373645 100644
--- a/client_test.go
+++ b/client_test.go
@@ -9,13 +9,32 @@ import (
"testing"
)
+func TestParseHTMLEntri(t *testing.T) {
+ htmlBody, err := ioutil.ReadFile("testdata/entri.html")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ daftarDefinisi, err := parseHTMLEntri(htmlBody)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, defKata := range daftarDefinisi {
+ t.Logf("Definisi kata: %+v", defKata)
+ }
+}
+
func TestClient_parseHTMLKataDasar(t *testing.T) {
htmlBody, err := ioutil.ReadFile("testdata/kbbi_dasar.html")
if err != nil {
t.Fatal(err)
}
- cl := NewClient()
+ cl, err := New(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
got, err := cl.parseHTMLKataDasar(htmlBody)
if err != nil {
diff --git a/cmd/kbbi/main.go b/cmd/kbbi/main.go
index 53bb107..2f7a322 100644
--- a/cmd/kbbi/main.go
+++ b/cmd/kbbi/main.go
@@ -21,8 +21,9 @@ import (
)
const (
- configDir = "kbbi"
- cookieFile = "cookie"
+ configDir = "kbbi"
+ cookieFile = "cookie"
+ errKataNotFound = "kata tidak ditemukan"
)
func main() {
@@ -69,6 +70,26 @@ func main() {
}
listKataDasar(cl)
+ return
+ }
+
+ var (
+ pesan string
+ daftarKata []string = flag.Args()
+ )
+ for _, kata := range daftarKata {
+ daftarDefinisi, err := cl.CariDefinisi(kata)
+ if err != nil {
+ log.Println(err)
+ }
+ if len(daftarDefinisi) == 0 {
+ pesan = errKataNotFound
+ }
+
+ fmt.Println("===", kata, ":", pesan)
+ for _, defKata := range daftarDefinisi {
+ fmt.Printf("\t%s: %s\n", defKata.Nomina, defKata.Isi)
+ }
}
}
diff --git a/definisi_kata.go b/definisi_kata.go
new file mode 100644
index 0000000..9bd3368
--- /dev/null
+++ b/definisi_kata.go
@@ -0,0 +1,43 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package kbbi
+
+import (
+ "golang.org/x/net/html"
+)
+
+type DefinisiKata struct {
+ Nomina []string
+ Isi string
+}
+
+func parseDefinisiKata(li *html.Node) (defKata *DefinisiKata) {
+ elFont := getFirstChild(li)
+ if elFont.Data != tagNameFont {
+ return nil
+ }
+ elItalic := getFirstChild(elFont)
+ if elItalic.Data != tagNameItalic {
+ return nil
+ }
+
+ defKata = &DefinisiKata{}
+
+ elSpan := getFirstChild(elItalic)
+ for elSpan != nil && elSpan.Data == tagNameSpan {
+ for _, attr := range elSpan.Attr {
+ if attr.Key != attrNameTitle {
+ continue
+ }
+ defKata.Nomina = append(defKata.Nomina, attr.Val)
+ }
+ elSpan = getNextSibling(elSpan)
+ }
+
+ el := getNextSibling(elFont)
+ defKata.Isi = el.Data
+
+ return defKata
+}
diff --git a/kbbi.go b/kbbi.go
index db562f1..69beb0e 100644
--- a/kbbi.go
+++ b/kbbi.go
@@ -9,20 +9,29 @@
package kbbi
const (
- hostname = "kbbi.kemdikbud.go.id"
- baseURL = "https://" + hostname
- loginURL = baseURL + "/Account/Login"
- entriURL = "/entri/"
+ hostname = "kbbi.kemdikbud.go.id"
+ baseURL = "https://" + hostname
+ loginURL = baseURL + "/Account/Login"
+ entriPath = "/entri/"
+ attrNameClass = "class"
attrNameHref = "href"
attrNameName = "name"
+ attrNameTitle = "title"
attrNameValue = "value"
+ attrValueLastListChild = "last-list-child"
+
headerNameContentType = "Content-Type"
headerValueContentType = "application/x-www-form-urlencoded"
- elementTypeAnchor = "a"
- elementTypeInput = "input"
+ tagNameAnchor = "a"
+ tagNameInput = "input"
+ tagNameOrderedList = "ol"
+ tagNameListItem = "li"
+ tagNameFont = "font"
+ tagNameItalic = "i"
+ tagNameSpan = "span"
paramNameIngatSaya = "IngatSaya"
paramNameKataSandi = "KataSandi"
diff --git a/parser.go b/parser.go
new file mode 100644
index 0000000..8a66e6f
--- /dev/null
+++ b/parser.go
@@ -0,0 +1,51 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package kbbi
+
+import (
+ "strings"
+
+ "golang.org/x/net/html"
+)
+
+func getAttrValue(attrs []html.Attribute) string {
+ for _, attr := range attrs {
+ if attr.Key == attrNameValue {
+ return attr.Val
+ }
+ }
+ return ""
+}
+
+//
+// getFirstChild get the first non-empty child.
+//
+func getFirstChild(node *html.Node) *html.Node {
+ el := node.FirstChild
+ for el != nil {
+ if el.Type == html.TextNode {
+ if len(strings.TrimSpace(el.Data)) == 0 {
+ el = el.NextSibling
+ continue
+ }
+ }
+ break
+ }
+ return el
+}
+
+func getNextSibling(node *html.Node) *html.Node {
+ el := node.NextSibling
+ for el != nil {
+ if el.Type == html.TextNode {
+ if len(strings.TrimSpace(el.Data)) == 0 {
+ el = el.NextSibling
+ continue
+ }
+ }
+ break
+ }
+ return el
+}
diff --git a/testdata/entri.html b/testdata/entri.html
new file mode 100644
index 0000000..9c53f07
--- /dev/null
+++ b/testdata/entri.html
@@ -0,0 +1,396 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <meta
+ name="viewport"
+ content="width=device-width, initial-scale=1.0"
+ />
+ <meta
+ name="keywords"
+ content="kbbi, kbbi online, kbbi daring, kbbi dalam jaringan, kbbi 5, kbbi V, kbbi online terbaru, kbbi terbaru, kbbi resmi, Kamus Besar Bahasa Indonesia, Badan Bahasa, Pusat Bahasa, kamus bahasa Indonesia, kamus daring, kamus indonesia,"
+ />
+ <link rel="icon" href="/kbbi-daring-3.ico" />
+ <title>Hasil Pencarian - KBBI Daring</title>
+ <link
+ href="/Content/css?v=DsWRYqffn1l_yiM362JpjeKWGHv3Xp66PuBRKIpyVUU1"
+ rel="stylesheet"
+ />
+
+ <script src="/bundles/modernizr?v=inCVuEFe6J4Q07A0AcRsbJic_UE5MwpRMNGcOtk94TE1"></script>
+ </head>
+ <body style="font-family:Verdana, Geneva, Tahoma, sans-serif">
+ <div
+ class="navbar navbar-inverse navbar-fixed-top"
+ style="background-color:#110063;border-color:gold"
+ >
+ <div class="container">
+ <div class="navbar-header">
+ <img
+ src="/Content/Images/Logo-Tut-Wuri-Handayani-blue.png"
+ height="40px;"
+ width="40px;"
+ style="margin:5px;"
+ />
+ <button
+ type="button"
+ class="navbar-toggle"
+ data-toggle="collapse"
+ data-target=".navbar-collapse"
+ style="background-color:#110063;border-color:gold;"
+ >
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a
+ href="/Beranda"
+ style="color:gold;text-decoration:none;margin-left:5px;margin-right:5px;font-size:larger"
+ >KBBI Daring</a
+ >
+ </div>
+ <div
+ class="navbar-collapse collapse"
+ style="background-color:#110063;color:gold;border-top-color:gold"
+ >
+ <ul class="nav navbar-nav">
+ <li><a href="/" style="color:gold">Cari</a></li>
+ <li>
+ <a href="/Beranda/SeputarLaman" style="color:gold"
+ >Seputar Laman</a
+ >
+ </li>
+ </ul>
+ <form
+ action="/Account/LogOff"
+ class="navbar-right"
+ id="logoutForm"
+ method="post"
+ >
+ <input
+ name="__RequestVerificationToken"
+ type="hidden"
+ value="CF8U6nmOAJlAY2k60AvI39TUbFkvFxPojfIX5wstWyS2o46yCeF1TmqBO6HgMak8TWbCvm9K2hN-k0egbXfWAsX9x0OC0g2c3LC2C0-dcLzFEK2_ly2KN7J4rtx8LJ4wAl6ye-QpucHX-AN6HAqo3g2"
+ />
+ <ul class="nav navbar-nav navbar-right">
+ <li>
+ <a
+ href="/Manage"
+ style="color:gold"
+ title="Lakukan Manajemen Akun"
+ >Halo Shulhan!</a
+ >
+ </li>
+ <li>
+ <a
+ href="javascript:document.getElementById('logoutForm').submit()"
+ style="color:gold"
+ >Keluar</a
+ >
+ </li>
+ </ul>
+ </form>
+ </div>
+ </div>
+ </div>
+ <div class="container body-content">
+ <script src="/bundles/jquery?v=2u0aRenDpYxArEyILB59ETSCA2cfQkSMlxb6jbMBqf81"></script>
+ <script>
+ $(function() {
+ $(".entrisButton").hover(
+ function() {
+ $(this).fadeTo(1, 1)
+ },
+ function() {
+ $(this).fadeTo(1, 0.18)
+ },
+ )
+ })
+ </script>
+ <br />
+
+ <div>
+ <h4 class="text-center">
+ <span
+ class="glyphicon glyphicon-info-sign text-primary"
+ ></span>
+ <b>Halo Shulhan!</b>
+ Sudahkah Anda mengecek
+ <a href="/Manage">halaman manajemen akun Anda</a>? Anda
+ dapat melihat cara membukanya
+ <a href="/Beranda/Bantuan#pertanyaan-1">di sini</a>. Jika
+ Anda pernah mengajukan
+ <a href="/Manage/ProposalDibuat">usulan-usulan</a>,
+ mungkin usulan-usulan tersebut telah diproses oleh redaksi
+ kami.
+ </h4>
+ <br />
+ </div>
+
+ <form
+ action="/entri/nul"
+ class="form-horizontal"
+ id="searchForm"
+ method="post"
+ onsubmit="searchText(event)"
+ role="form"
+ >
+ <div class="form-group">
+ <div class="col-md-2"></div>
+ <div class="col-md-8">
+ <div class="input-group form-control-max">
+ <input
+ id="textBoxSearch"
+ name="frasa"
+ value="informasi"
+ type="text"
+ class="form-control form-control-max"
+ style="margin-top:1px;"
+ placeholder="Pencarian..."
+ />
+ <span class="input-group-btn">
+ <span
+ class="btn btn-primary glyphicon glyphicon-search"
+ onclick="searchText(event)"
+ ></span>
+ </span>
+ </div>
+ </div>
+ </div>
+ <h3 id="errorMessageDiv"></h3>
+ <script>
+ String.prototype.contains = function(it) {
+ return this.indexOf(it) != -1
+ }
+ function searchText(ev) {
+ var val = $("#textBoxSearch").val()
+ ev.preventDefault()
+ if (!val) {
+ $("#errorMessageDiv").replaceWith(
+ '<h3 id="errorMessageDiv"><font color="red"><p class="text-center add-margin-top-5"><i>Kotak pencarian tidak boleh kosong</i></p></font></h3>',
+ )
+ } else {
+ if (
+ val.contains(".") ||
+ val.contains("?") ||
+ val.toLowerCase() == "nul" ||
+ val.toLowerCase() == "bin"
+ ) {
+ //for non-dependent respond
+ window.location.href =
+ "/" + "Cari/Hasil?frasa=" + val
+ } else {
+ window.location.href = "/" + "entri/" + val
+ }
+ }
+ }
+ </script>
+ </form>
+
+ <hr />
+ <h2 style="margin-bottom:3px">
+ in.for.ma.si
+ <small
+ ><span class="entrisButton"
+ ><a href="../DataDasarEntri/Edit?eid=31762"
+ ><span
+ title="Ubah"
+ class="glyphicon glyphicon-edit text-success"
+ ></span></a
+ ><a href="../DataDasarEntri/Copy?eid=31762"
+ ><span
+ title="Usulkan entri baru berdasarkan entri ini"
+ class="glyphicon glyphicon-duplicate"
+ style="color:darkcyan"
+ ></span></a
+ ><a href="../DataDasarEntri/Details?eid=31762"
+ ><span
+ title="Detail"
+ class="glyphicon glyphicon-list-alt text-info"
+ ></span></a
+ ><span
+ title="Sejarah redaksi - entri ini tidak memiliki sejarah redaksi"
+ class="glyphicon glyphicon-book"
+ style="color:lightgrey"
+ ></span
+ ><a href="https://www.google.com/#q=informasi"
+ ><span
+ title="Cari di Google"
+ class="glyphicon glyphicon-search text-primary"
+ ></span></a></span
+ ></small>
+ </h2>
+ <p>
+ <a
+ href="http://tesaurus.kemdikbud.go.id/tematis/lema/informasi"
+ >&#x21E2; Tesaurus</a
+ >
+ </p>
+ <ol class="last-list-child">
+ <li>
+ <font color="red"
+ ><i
+ ><span title="Nomina: kata benda">n</span>
+ </i></font
+ >penerangan
+ </li>
+ <li>
+ <font color="red"
+ ><i
+ ><span title="Nomina: kata benda">n</span>
+ </i></font
+ >pemberitahuan; kabar atau berita tentang sesuatu
+ </li>
+ <li>
+ <font color="red"
+ ><i
+ ><span title="Nomina: kata benda">n</span>
+ <span title="Linguistik: -">Ling</span>
+ </i></font
+ >keseluruhan makna yang menunjang amanat yang terlihat
+ dalam bagian-bagian amanat itu
+ </li>
+ <li style="margin -left:-19px">
+ <a
+ href="../DataDasarMakna/Create?eid=31762&number=4"
+ class="entrisButton"
+ ><span
+ title="Usulkan makna baru"
+ class="glyphicon glyphicon-plus-sign text-success"
+ ></span
+ >Usulkan makna baru</a
+ >
+ </li>
+ </ol>
+ <h4 style="padding-top:6px;">Kata Turunan</h4>
+ <ul style="list -style: none;" class="adjusted-par">
+ <li>
+ <a href="../../entri/menginformasikan"
+ >menginformasikan</a
+ >
+ </li>
+ </ul>
+ <h4 style="padding-top:6px;">Gabungan Kata</h4>
+ <ul style="list -style: none;" class="adjusted-par">
+ <li>
+ <a href="../../entri/informasi%20elektronik"
+ >informasi elektronik</a
+ >;
+ <a href="../../entri/informasi%20gizi">informasi gizi</a>;
+ <a href="../../entri/informasi%20jabatan"
+ >informasi jabatan</a
+ >;
+ <a href="../../entri/informasi%20karier"
+ >informasi karier</a
+ >;
+ <a href="../../entri/informasi%20meteorologi"
+ >informasi meteorologi</a
+ >;
+ <a href="../../entri/informasi%20pekerjaan"
+ >informasi pekerjaan</a
+ >;
+ <a href="../../entri/informasi%20pendidikan"
+ >informasi pendidikan</a
+ >;
+ <a href="../../entri/informasi%20pornografi"
+ >informasi pornografi</a
+ >
+ </li>
+ </ul>
+ <br /><br />
+ <h4>
+ <a href="/DataDasarEntri/Create" class="entrisButton"
+ ><span
+ title="Usulkan entri baru"
+ class="glyphicon glyphicon-plus-sign text-success"
+ ></span
+ >Usulkan entri baru</a
+ >
+ </h4>
+ <hr />
+ <footer>
+ <p>
+ &copy; 2016
+ <a href="http://badanbahasa.kemdikbud.go.id/"
+ >Badan Pengembangan Bahasa dan Perbukuan</a
+ >, Kementerian Pendidikan dan Kebudayaan Republik
+ Indonesia
+ </p>
+ <p>
+ Versi luring:
+ <a
+ class="btn btn-primary"
+ href="https://play.google.com/store/apps/details?id=yuku.kbbi5&hl=in"
+ >Android</a
+ >
+ |
+ <a
+ class="btn btn-primary"
+ href="https://itunes.apple.com/app/kamus-besar-bahasa-indonesia/id1173573777"
+ >iOS</a
+ >
+ ||
+ <span title="by: Ian K"
+ >Versi daring: 2.0.2.0-20191127214052</span
+ >
+ </p>
+ </footer>
+ </div>
+ <script src="/bundles/jquery?v=2u0aRenDpYxArEyILB59ETSCA2cfQkSMlxb6jbMBqf81"></script>
+
+ <script src="/bundles/bootstrap?v=7k-mK_Lw6GRA4MkvIrgrWipUHc3KUDohIwN2DDpspCI1"></script>
+
+ <!-- Global site tag (gtag.js) - Google Analytics -->
+ <script
+ async
+ src="https://www.googletagmanager.com/gtag/js?id=UA-128199158-1"
+ ></script>
+ <script>
+ window.dataLayer = window.dataLayer || []
+ function gtag() {
+ dataLayer.push(arguments)
+ }
+ gtag("js", new Date())
+
+ gtag("config", "UA-128199158-1")
+ </script>
+
+ <script>
+ function setSelectionRange(input, selectionStart, selectionEnd) {
+ if (input.setSelectionRange) {
+ input.focus()
+ input.setSelectionRange(selectionStart, selectionEnd)
+ } else if (input.createTextRange) {
+ var range = input.createTextRange()
+ range.collapse(true)
+ range.moveEnd("character", selectionEnd)
+ range.moveStart("character", selectionStart)
+ range.select()
+ }
+ }
+
+ function setCaretToPos(input, pos) {
+ setSelectionRange(input, pos, pos)
+ }
+
+ $(document).ready(function() {
+ // Catch all events related to changes http://stackoverflow.com/questions/21215049/disable-text-entry-in-input-type-number
+ $(".number-input").on("change keyup", function() {
+ var sanitized = $(this)
+ .val()
+ .replace(/[^0-9]/g, "") // Remove invalid characters
+ $(this).val(sanitized) // Update value
+ })
+
+ $(function() {
+ var tb = document.getElementById("textBoxSearch")
+ if (tb) {
+ var val = $("#textBoxSearch").val()
+ var caretPos = val.length
+ setCaretToPos(tb, caretPos)
+ }
+ })
+ })
+ </script>
+ </body>
+</html>