commit
ce2b19ebd0
|
@ -0,0 +1,7 @@
|
||||||
|
# GoLand
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
/falseknees
|
||||||
|
/falseknees.exe
|
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2021 Etzelia
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,17 @@
|
||||||
|
GO ?= go
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
$(GO) build ./cmd/falseknees
|
||||||
|
|
||||||
|
.PHONY: vet
|
||||||
|
vet:
|
||||||
|
$(GO) vet ./...
|
||||||
|
|
||||||
|
.PHONY: fmt
|
||||||
|
fmt:
|
||||||
|
$(GO) fmt ./...
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
$(GO) test -race ./...
|
|
@ -0,0 +1,27 @@
|
||||||
|
# FalseKnees
|
||||||
|
|
||||||
|
This is definitely not real API for [FalseKnees](https://www.falseknees.com).
|
||||||
|
|
||||||
|
By that I mean to say FK does not *have* an API, so this is a glorified HTML parser.
|
||||||
|
|
||||||
|
Please be considerate of the fact that this is not a real API and so it is pulling entire HTML pages
|
||||||
|
to parse.
|
||||||
|
|
||||||
|
|
||||||
|
## Current Comic
|
||||||
|
|
||||||
|
[FalseKnees](https://www.falseknees.com) `/index.html` page contains a redirect to get the latest
|
||||||
|
comic number. This library leverages that page to get the current comic, **however** in an effort
|
||||||
|
to reduce HTTP calls against full HTML pages, the library has a built-in interval of 30 minutes.
|
||||||
|
|
||||||
|
This means that if you get the current comic at 12:00 and FK adds a new comic at 12:05, you
|
||||||
|
won't get the new current comic until 12:30.
|
||||||
|
|
||||||
|
Currently the following funcs retrieve the current comic number:
|
||||||
|
|
||||||
|
* `Random`
|
||||||
|
* `Current`
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
|
@ -0,0 +1,29 @@
|
||||||
|
package falseknees
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// Client is a FalseKnees client
|
||||||
|
type Client struct {
|
||||||
|
http *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Client
|
||||||
|
func New(opts ...ClientOption) *Client {
|
||||||
|
c := &Client{
|
||||||
|
http: http.DefaultClient,
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(c)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientOption is options for a Client
|
||||||
|
type ClientOption func(*Client)
|
||||||
|
|
||||||
|
// WithHTTP is a ClientOption for using a different http.Client
|
||||||
|
func WithHTTP(client *http.Client) ClientOption {
|
||||||
|
return func(c *Client) {
|
||||||
|
c.http = client
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.birbmc.com/Etzelia/falseknees"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
randomFlag bool
|
||||||
|
imageFlag bool
|
||||||
|
outFlag string
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.BoolVar(&randomFlag, "random", false, "get a random comic")
|
||||||
|
flag.BoolVar(&imageFlag, "image", false, "get the image response for a comic")
|
||||||
|
flag.StringVar(&outFlag, "out", "", "where to write the response (default: stdout)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
comicNum := 0
|
||||||
|
if flag.NArg() > 0 {
|
||||||
|
arg := flag.Arg(0)
|
||||||
|
i, err := strconv.Atoi(arg)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Specific comic must be a number: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
comicNum = i
|
||||||
|
}
|
||||||
|
out := os.Stdout
|
||||||
|
if outFlag != "" {
|
||||||
|
fi, err := os.Create(outFlag)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Could not create file %s: %v\n", outFlag, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fi.Close()
|
||||||
|
out = fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := run(comicNum, out); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(comicNum int, out io.WriteCloser) error {
|
||||||
|
|
||||||
|
client := falseknees.New()
|
||||||
|
var comic *falseknees.Comic
|
||||||
|
var err error
|
||||||
|
if randomFlag {
|
||||||
|
comic, err = client.Random(context.Background())
|
||||||
|
} else if comicNum == 0 {
|
||||||
|
comic, err = client.Current(context.Background())
|
||||||
|
} else {
|
||||||
|
comic, err = client.Comic(context.Background(), comicNum)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if imageFlag {
|
||||||
|
return image(comic.Img, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
format := `Comic: %d
|
||||||
|
Title: %s
|
||||||
|
Image: %s
|
||||||
|
`
|
||||||
|
if _, err := fmt.Fprintf(out, format,
|
||||||
|
comic.Num,
|
||||||
|
comic.Title,
|
||||||
|
comic.Img); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func image(img string, out io.WriteCloser) error {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, img, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(out, resp.Body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
package falseknees
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
updateInterval = time.Minute * 30
|
||||||
|
baseURL = "https://www.falseknees.com/"
|
||||||
|
currentRe = regexp.MustCompile(`window\.location\.href.+"(.+)\.html"`)
|
||||||
|
imageRe = regexp.MustCompile(`src="(imgs.+\.png)".+title="(.+)"`)
|
||||||
|
|
||||||
|
current int
|
||||||
|
lastUpdate time.Time
|
||||||
|
)
|
||||||
|
|
||||||
|
// Comic is a FalseKnees comic
|
||||||
|
type Comic struct {
|
||||||
|
Num int
|
||||||
|
Title string
|
||||||
|
Img string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comic returns a specific Comic
|
||||||
|
func (c *Client) Comic(ctx context.Context, num int) (*Comic, error) {
|
||||||
|
return c.comic(ctx, num)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current returns the current Comic
|
||||||
|
func (c *Client) Current(ctx context.Context) (*Comic, error) {
|
||||||
|
if err := c.updateCurrent(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.Comic(ctx, current)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Random returns a random Comic
|
||||||
|
func (c *Client) Random(ctx context.Context) (*Comic, error) {
|
||||||
|
if err := c.updateCurrent(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
return c.Comic(ctx, rand.Intn(current)+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) updateCurrent(ctx context.Context) error {
|
||||||
|
now := time.Now()
|
||||||
|
if !lastUpdate.IsZero() && lastUpdate.After(now.Add(-updateInterval)) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
lastUpdate = now
|
||||||
|
|
||||||
|
u := fmt.Sprintf("%sindex.html", baseURL)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.http.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("could not get page for index: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
match := currentRe.FindStringSubmatch(string(body))
|
||||||
|
if len(match) == 0 {
|
||||||
|
return errors.New("could not find current comic")
|
||||||
|
}
|
||||||
|
|
||||||
|
curr, err := strconv.Atoi(match[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
current = curr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) comic(ctx context.Context, num int) (*Comic, error) {
|
||||||
|
u := fmt.Sprintf("%s%d.html", baseURL, num)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.http.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("could not get page for comic %d: %s", num, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
match := imageRe.FindStringSubmatch(string(body))
|
||||||
|
if len(match) == 0 {
|
||||||
|
return nil, fmt.Errorf("could not find comic #%d", num)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Comic{
|
||||||
|
Num: num,
|
||||||
|
Title: match[2],
|
||||||
|
Img: fmt.Sprintf("%s%s", baseURL, match[1]),
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -0,0 +1,483 @@
|
||||||
|
package falseknees
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
server *httptest.Server
|
||||||
|
client *Client
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path == "/index.html" {
|
||||||
|
_, _ = w.Write(indexHTML)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.URL.Path == "/389.html" {
|
||||||
|
_, _ = w.Write(currentHTML)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.URL.Path == "/252.html" {
|
||||||
|
_, _ = w.Write(bunnyHTML)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
server = httptest.NewServer(http.HandlerFunc(handler))
|
||||||
|
baseURL = server.URL + "/"
|
||||||
|
currentComic.Img = fmt.Sprintf("%simgs/389.png", baseURL)
|
||||||
|
bunnyComic.Img = fmt.Sprintf("%simgs/252.png", baseURL)
|
||||||
|
client = New()
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCurrent(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
comic, err := client.Current(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("could not get current comic: %v\n", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if *comic != currentComic {
|
||||||
|
t.Log("comic does not match test data")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComic(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
comic, err := client.Comic(context.Background(), 252)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("could not get comic 252: %v\n", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if *comic != bunnyComic {
|
||||||
|
t.Log("comic does not match test data")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
currentComic = Comic{
|
||||||
|
Num: 389,
|
||||||
|
Title: "that's the good stuff",
|
||||||
|
}
|
||||||
|
bunnyComic = Comic{
|
||||||
|
Num: 252,
|
||||||
|
Title: "Spring is the fucking greatest shit",
|
||||||
|
}
|
||||||
|
|
||||||
|
indexHTML = []byte(`<!DOCTYPE html
|
||||||
|
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="description"
|
||||||
|
content="False Knees is a webcomic written by Joshua Barkman. All silly nonsense is my own." />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
|
||||||
|
<meta http-equiv="refresh" content="0; URL=389.html" />
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.location.href = "389.html"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/png" href="imgs/favicon.png" sizes="96x96">
|
||||||
|
<!-- Facebook Meta tags -->
|
||||||
|
<meta property="og:title" content="False Knees" />
|
||||||
|
<meta property="og:type" content="blog" />
|
||||||
|
<meta property="og:url" content="http://www.falseknees.com/index.html" />
|
||||||
|
<meta property="og:image" content="http://www.falseknees.com/imgs/389.png" />
|
||||||
|
<meta property="og:site_name" content="False Knees" />
|
||||||
|
<meta property="fb:admins" content="1646220005" />
|
||||||
|
|
||||||
|
<link rel="image_src" href="imgs/389.png" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="stylesheet.css" />
|
||||||
|
<title>Page Redirection</title>
|
||||||
|
|
||||||
|
<!-- Google Analytics -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var _gaq = _gaq || [];
|
||||||
|
var pluginUrl =
|
||||||
|
'//www.google-analytics.com/plugins/ga/inpage_linkid.js';
|
||||||
|
_gaq.push(['_require', 'inpage_linkid', pluginUrl]);
|
||||||
|
_gaq.push(['_setAccount', 'UA-37345913-1']);
|
||||||
|
_gaq.push(['_setDomainName', 'falseknees.com']);
|
||||||
|
_gaq.push(['_trackPageview']);
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||||
|
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||||
|
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||||
|
})();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="fb-root"></div>
|
||||||
|
<script>
|
||||||
|
(function(d, s, id) {
|
||||||
|
var js, fjs = d.getElementsByTagName(s)[0];
|
||||||
|
if (d.getElementById(id)) return;
|
||||||
|
js = d.createElement(s); js.id = id;
|
||||||
|
js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.0";
|
||||||
|
fjs.parentNode.insertBefore(js, fjs);
|
||||||
|
}(document, 'script', 'facebook-jssdk'));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Title Image -->
|
||||||
|
<table align="center">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div id="title">
|
||||||
|
<a href="index.html"><img src="imgs/falseknees.png" width="800" /></a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- New Button Placement -->
|
||||||
|
<div align="center">
|
||||||
|
<p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><a href="about.html"
|
||||||
|
title=""><img src="imgs/aboutoff.png" height="50" alt="About" onmouseover="this.src='imgs/abouton.png'" onmouseout="this.src='imgs/aboutoff.png'" /></a>
|
||||||
|
</td>
|
||||||
|
<td><a title=""><img src="imgs/stara.png" height="50" alt="" /></a></td>
|
||||||
|
<td><a href="https://false-knees.myshopify.com/"
|
||||||
|
title=""><img src="imgs/store.png" height="50" alt="Store" onmouseover="this.src='imgs/storeon.png'" onmouseout="this.src='imgs/store.png'" /></a>
|
||||||
|
</td>
|
||||||
|
<td><a title=""><img src="imgs/starb.png" height="50" alt="" /></a></td>
|
||||||
|
<td><a href="https://www.patreon.com/falseknees?ty=h"><img src="imgs/patron.png" height="50" onmouseover="this.src='imgs/patroff.png'" onmouseout="this.src='imgs/patron.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a title=""><img src="imgs/starc.png" height="50" alt="" /></a></td>
|
||||||
|
<td><a href="book.html"
|
||||||
|
title=""><img src="imgs/book.png" height="40" alt="About" onmouseover="this.src='imgs/bookon.png'" onmouseout="this.src='imgs/book.png'" /></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Comic -->
|
||||||
|
<div>
|
||||||
|
<img src="imgs/389.png" width="600" title="that's the good stuff" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Descriptive Text Box -->
|
||||||
|
<!-- Descriptive Text Box -->
|
||||||
|
<div align="center">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Navigation Buttons -->
|
||||||
|
<div align="center">
|
||||||
|
<p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><a href="1.html"
|
||||||
|
title=""><img src="imgs/first.png" height="60" alt="First" onmouseover="this.src='imgs/firston.png'" onmouseout="this.src='imgs/first.png'" /></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="388.html"
|
||||||
|
title=""><img src="imgs/previous.png" height="60" alt="Previous" onmouseover="this.src='imgs/previouson.png'" onmouseout="this.src='imgs/previous.png'" /></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="archive.html"
|
||||||
|
title=""><img src="imgs/archive.png" height="60" alt="Archive" onmouseover="this.src='imgs/archive2.png'" onmouseout="this.src='imgs/archive.png'" /></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="index.html"
|
||||||
|
title=""><img src="imgs/next.png" height="60" alt="Next" onmouseover="this.src='imgs/nexton.png'" onmouseout="this.src='imgs/next.png'" /></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="index.html"
|
||||||
|
title=""><img src="imgs/last.png" height="60" alt="Last" onmouseover="this.src='imgs/laston.png'" onmouseout="this.src='imgs/last.png'" /></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Social Media -->
|
||||||
|
<table class="social" align="center">
|
||||||
|
<tr>
|
||||||
|
<td><a href="http://falseknees.tumblr.com/"><img src="imgs/TumblrButton.png" width="60" onmouseover="this.src='imgs/TumblrButtonOn.png'" onmouseout="this.src='imgs/TumblrButton.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="https://instagram.com/FalseKnees"><img src="imgs/instagram.png" width="60" onmouseover="this.src='imgs/instagramon.png'" onmouseout="this.src='imgs/instagram.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="https://www.facebook.com/FalseKnees?ref=hl"><img src="imgs/FacebookButton.png" width="60" onmouseover="this.src='imgs/FacebookButtonOn.png'" onmouseout="this.src='imgs/FacebookButton.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="http://www.webtoons.com/en/challenge/false-knees/list?title_no=79544"><img src="imgs/webtooff.png" width="60" onmouseover="this.src='imgs/webtoon.png'" onmouseout="this.src='imgs/webtooff.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="https://tapas.io/series/FalseKnees"><img src="imgs/tap.png" width="60" onmouseover="this.src='imgs/tapon.png'" onmouseout="this.src='imgs/tap.png'"/></a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Copyright -->
|
||||||
|
<div>
|
||||||
|
<p>False Knees © 2013-whenever Joshua Barkman</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>`)
|
||||||
|
|
||||||
|
currentHTML = []byte(`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta name="description" content="False Knees is a webcomic written by Joshua Barkman. All silly nonsense is my own." />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/png" href="imgs/favicon.png" sizes="96x96">
|
||||||
|
<!-- Facebook Meta tags -->
|
||||||
|
<meta property="og:title" content="False Knees" />
|
||||||
|
<meta property="og:type" content="blog" />
|
||||||
|
<meta property="og:url" content="http://www.falseknees.com/389.html" />
|
||||||
|
<meta property="og:image" content="http://www.falseknees.com/imgs/389.png" />
|
||||||
|
<meta property="og:site_name" content="False Knees" />
|
||||||
|
<meta property="fb:admins" content="1646220005" />
|
||||||
|
|
||||||
|
<link rel="image_src" href="imgs/389.png" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="stylesheet.css" />
|
||||||
|
<title>False Knees</title>
|
||||||
|
|
||||||
|
<!-- Google Analytics -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var _gaq = _gaq || [];
|
||||||
|
var pluginUrl =
|
||||||
|
'//www.google-analytics.com/plugins/ga/inpage_linkid.js';
|
||||||
|
_gaq.push(['_require', 'inpage_linkid', pluginUrl]);
|
||||||
|
_gaq.push(['_setAccount', 'UA-37345913-1']);
|
||||||
|
_gaq.push(['_setDomainName', 'falseknees.com']);
|
||||||
|
_gaq.push(['_trackPageview']);
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||||
|
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||||
|
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||||
|
})();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="fb-root"></div>
|
||||||
|
<script>(function(d, s, id) {
|
||||||
|
var js, fjs = d.getElementsByTagName(s)[0];
|
||||||
|
if (d.getElementById(id)) return;
|
||||||
|
js = d.createElement(s); js.id = id;
|
||||||
|
js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.0";
|
||||||
|
fjs.parentNode.insertBefore(js, fjs);
|
||||||
|
}(document, 'script', 'facebook-jssdk'));</script>
|
||||||
|
|
||||||
|
<!-- Title Image -->
|
||||||
|
<table align="center">
|
||||||
|
<tr>
|
||||||
|
<td><div id="title">
|
||||||
|
<a href="index.html"><img src="imgs/falseknees.png" width="800" /></a>
|
||||||
|
</div></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- New Button Placement -->
|
||||||
|
<div align="center">
|
||||||
|
<p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><a href="about.html" title=""><img src="imgs/aboutoff.png" height="50" alt="About" onmouseover="this.src='imgs/abouton.png'" onmouseout="this.src='imgs/aboutoff.png'" /></a></td>
|
||||||
|
<td><a title=""><img src="imgs/stara.png" height="50" alt="" /></a></td>
|
||||||
|
<td><a href="https://false-knees.myshopify.com/" title=""><img src="imgs/store.png" height="50" alt="Store" onmouseover="this.src='imgs/storeon.png'" onmouseout="this.src='imgs/store.png'" /></a></td>
|
||||||
|
<td><a title=""><img src="imgs/starb.png" height="50" alt="" /></a></td>
|
||||||
|
<td><a href="https://www.patreon.com/falseknees?ty=h"><img src="imgs/patron.png" height="50" onmouseover="this.src='imgs/patroff.png'" onmouseout="this.src='imgs/patron.png'"/></a></td>
|
||||||
|
<td><a title=""><img src="imgs/starc.png" height="50" alt="" /></a></td>
|
||||||
|
<td><a href="book.html" title=""><img src="imgs/book.png" height="40" alt="About" onmouseover="this.src='imgs/bookon.png'" onmouseout="this.src='imgs/book.png'" /></a></td>
|
||||||
|
</tr>
|
||||||
|
</table></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Comic -->
|
||||||
|
<div>
|
||||||
|
<img src="imgs/389.png" width="600" title="that's the good stuff" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Descriptive Text Box -->
|
||||||
|
<!-- Descriptive Text Box -->
|
||||||
|
<div align="center">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Navigation Buttons -->
|
||||||
|
<div align="center">
|
||||||
|
<p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><a href="1.html" title=""><img src="imgs/first.png" height="60" alt="First" onmouseover="this.src='imgs/firston.png'" onmouseout="this.src='imgs/first.png'" /></a></td>
|
||||||
|
<td><a href="388.html" title=""><img src="imgs/previous.png" height="60" alt="Previous" onmouseover="this.src='imgs/previouson.png'" onmouseout="this.src='imgs/previous.png'" /></a></td>
|
||||||
|
<td><a href="archive.html" title=""><img src="imgs/archive.png" height="60" alt="Archive" onmouseover="this.src='imgs/archive2.png'" onmouseout="this.src='imgs/archive.png'" /></a></td>
|
||||||
|
<td><a href="389.html" title=""><img src="imgs/next.png" height="60" alt="Next" onmouseover="this.src='imgs/nexton.png'" onmouseout="this.src='imgs/next.png'" /></a></td>
|
||||||
|
<td><a href="index.html" title=""><img src="imgs/last.png" height="60" alt="Last" onmouseover="this.src='imgs/laston.png'" onmouseout="this.src='imgs/last.png'" /></a></td>
|
||||||
|
</tr>
|
||||||
|
</table></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Social Media -->
|
||||||
|
<table class="social" align="center">
|
||||||
|
<tr>
|
||||||
|
<td><a href="http://falseknees.tumblr.com/"><img src="imgs/TumblrButton.png" width="60" onmouseover="this.src='imgs/TumblrButtonOn.png'" onmouseout="this.src='imgs/TumblrButton.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="https://instagram.com/FalseKnees"><img src="imgs/instagram.png" width="60" onmouseover="this.src='imgs/instagramon.png'" onmouseout="this.src='imgs/instagram.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="https://www.facebook.com/FalseKnees?ref=hl"><img src="imgs/FacebookButton.png" width="60" onmouseover="this.src='imgs/FacebookButtonOn.png'" onmouseout="this.src='imgs/FacebookButton.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="http://www.webtoons.com/en/challenge/false-knees/list?title_no=79544"><img src="imgs/webtooff.png" width="60" onmouseover="this.src='imgs/webtoon.png'" onmouseout="this.src='imgs/webtooff.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="https://tapas.io/series/FalseKnees"><img src="imgs/tap.png" width="60" onmouseover="this.src='imgs/tapon.png'" onmouseout="this.src='imgs/tap.png'"/></a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Copyright -->
|
||||||
|
<div>
|
||||||
|
<p>False Knees © 2013-whenever Joshua Barkman</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
|
|
||||||
|
bunnyHTML = []byte(`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta name="description" content="False Knees is a webcomic written by Joshua Barkman. All silly nonsense is my own." />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/png" href="imgs/favicon.png" sizes="96x96">
|
||||||
|
<!-- Facebook Meta tags -->
|
||||||
|
<meta property="og:title" content="False Knees" />
|
||||||
|
<meta property="og:type" content="blog" />
|
||||||
|
<meta property="og:url" content="http://www.falseknees.com/252.html" />
|
||||||
|
<meta property="og:image" content="http://www.falseknees.com/imgs/252.png" />
|
||||||
|
<meta property="og:site_name" content="False Knees" />
|
||||||
|
<meta property="fb:admins" content="1646220005" />
|
||||||
|
|
||||||
|
<link rel="image_src" href="imgs/252.png" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="stylesheet.css" />
|
||||||
|
<title>False Knees</title>
|
||||||
|
|
||||||
|
<!-- Google Analytics -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var _gaq = _gaq || [];
|
||||||
|
var pluginUrl =
|
||||||
|
'//www.google-analytics.com/plugins/ga/inpage_linkid.js';
|
||||||
|
_gaq.push(['_require', 'inpage_linkid', pluginUrl]);
|
||||||
|
_gaq.push(['_setAccount', 'UA-37345913-1']);
|
||||||
|
_gaq.push(['_setDomainName', 'falseknees.com']);
|
||||||
|
_gaq.push(['_trackPageview']);
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||||
|
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||||
|
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||||
|
})();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="fb-root"></div>
|
||||||
|
<script>(function(d, s, id) {
|
||||||
|
var js, fjs = d.getElementsByTagName(s)[0];
|
||||||
|
if (d.getElementById(id)) return;
|
||||||
|
js = d.createElement(s); js.id = id;
|
||||||
|
js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.0";
|
||||||
|
fjs.parentNode.insertBefore(js, fjs);
|
||||||
|
}(document, 'script', 'facebook-jssdk'));</script>
|
||||||
|
|
||||||
|
<!-- Title Image -->
|
||||||
|
<table align="center">
|
||||||
|
<tr>
|
||||||
|
<td><div id="title">
|
||||||
|
<a href="index.html"><img src="imgs/falseknees.png" width="800" /></a>
|
||||||
|
</div></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- New Button Placement -->
|
||||||
|
<div align="center">
|
||||||
|
<p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><a href="about.html" title=""><img src="imgs/aboutoff.png" height="50" alt="About" onmouseover="this.src='imgs/abouton.png'" onmouseout="this.src='imgs/aboutoff.png'" /></a></td>
|
||||||
|
<td><a title=""><img src="imgs/stara.png" height="50" alt="" /></a></td>
|
||||||
|
<td><a href="https://false-knees.myshopify.com/collections/comic-prints" title=""><img src="imgs/store.png" height="50" alt="Store" onmouseover="this.src='imgs/storeon.png'" onmouseout="this.src='imgs/store.png'" /></a></td>
|
||||||
|
<td><a title=""><img src="imgs/starb.png" height="50" alt="" /></a></td>
|
||||||
|
<td><a href="https://www.patreon.com/falseknees?ty=h"><img src="imgs/patron.png" height="50" onmouseover="this.src='imgs/patroff.png'" onmouseout="this.src='imgs/patron.png'"/></a></td>
|
||||||
|
<td><a title=""><img src="imgs/starc.png" height="50" alt="" /></a></td>
|
||||||
|
<td><a href="book.html" title=""><img src="imgs/book.png" height="40" alt="About" onmouseover="this.src='imgs/bookon.png'" onmouseout="this.src='imgs/book.png'" /></a></td>
|
||||||
|
</tr>
|
||||||
|
</table></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Comic -->
|
||||||
|
<div>
|
||||||
|
<img src="imgs/252.png" width="600" title="Spring is the fucking greatest shit" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Descriptive Text Box -->
|
||||||
|
<!-- Descriptive Text Box -->
|
||||||
|
<div align="center">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Navigation Buttons -->
|
||||||
|
<div align="center">
|
||||||
|
<p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><a href="1.html" title=""><img src="imgs/first.png" height="60" alt="First" onmouseover="this.src='imgs/firston.png'" onmouseout="this.src='imgs/first.png'" /></a></td>
|
||||||
|
<td><a href="251.html" title=""><img src="imgs/previous.png" height="60" alt="Previous" onmouseover="this.src='imgs/previouson.png'" onmouseout="this.src='imgs/previous.png'" /></a></td>
|
||||||
|
<td><a href="archive.html" title=""><img src="imgs/archive.png" height="60" alt="Archive" onmouseover="this.src='imgs/archive2.png'" onmouseout="this.src='imgs/archive.png'" /></a></td>
|
||||||
|
<td><a href="253.html" title=""><img src="imgs/next.png" height="60" alt="Next" onmouseover="this.src='imgs/nexton.png'" onmouseout="this.src='imgs/next.png'" /></a></td>
|
||||||
|
<td><a href="index.html" title=""><img src="imgs/last.png" height="60" alt="Last" onmouseover="this.src='imgs/laston.png'" onmouseout="this.src='imgs/last.png'" /></a></td>
|
||||||
|
</tr>
|
||||||
|
</table></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Social Media -->
|
||||||
|
<table class="social" align="center">
|
||||||
|
<tr>
|
||||||
|
<td><a href="http://falseknees.tumblr.com/"><img src="imgs/TumblrButton.png" width="60" onmouseover="this.src='imgs/TumblrButtonOn.png'" onmouseout="this.src='imgs/TumblrButton.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="https://instagram.com/FalseKnees"><img src="imgs/instagram.png" width="60" onmouseover="this.src='imgs/instagramon.png'" onmouseout="this.src='imgs/instagram.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="https://www.facebook.com/FalseKnees?ref=hl"><img src="imgs/FacebookButton.png" width="60" onmouseover="this.src='imgs/FacebookButtonOn.png'" onmouseout="this.src='imgs/FacebookButton.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="http://www.webtoons.com/en/challenge/false-knees/list?title_no=79544"><img src="imgs/webtooff.png" width="60" onmouseover="this.src='imgs/webtoon.png'" onmouseout="this.src='imgs/webtooff.png'"/></a>
|
||||||
|
</td>
|
||||||
|
<td><a href="https://tapas.io/series/FalseKnees"><img src="imgs/tap.png" width="60" onmouseover="this.src='imgs/tapon.png'" onmouseout="this.src='imgs/tap.png'"/></a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Copyright -->
|
||||||
|
<div>
|
||||||
|
<p>False Knees © 2013-whenever Joshua Barkman</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
|
)
|
Reference in New Issue