initial commit
This commit is contained in:
commit
1e36c85880
41
ArchiveServer.go
Normal file
41
ArchiveServer.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SERVER_VERSION = "archive/3.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ArchiveServer struct {
|
||||||
|
timezone *time.Location
|
||||||
|
cfg *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewArchiveServer(cfg *Config) (*ArchiveServer, error) {
|
||||||
|
tz, err := time.LoadLocation(cfg.Timezone)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.LinesPerPage <= 0 {
|
||||||
|
return nil, fmt.Errorf("Invalid %d lines per page", cfg.LinesPerPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.Logs) == 0 {
|
||||||
|
return nil, fmt.Errorf("No log sources configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ls := range cfg.Logs {
|
||||||
|
if len(ls.FileLocation) == 0 {
|
||||||
|
return nil, fmt.Errorf(`No file locations for log source "%s"`, ls.Description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ArchiveServer{
|
||||||
|
timezone: tz,
|
||||||
|
cfg: cfg,
|
||||||
|
}, nil
|
||||||
|
}
|
58
Config.go
Normal file
58
Config.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type YearMonth struct {
|
||||||
|
Year int
|
||||||
|
|
||||||
|
// time.Month is a 1-based month counting system
|
||||||
|
Month time.Month
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogLocation struct {
|
||||||
|
|
||||||
|
// LogFilePath will be passed to time.Format().
|
||||||
|
LogFilePath string
|
||||||
|
|
||||||
|
StartMonth YearMonth
|
||||||
|
|
||||||
|
// Leave null to use a current/ongoing log
|
||||||
|
EndMonth *YearMonth
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogSource struct {
|
||||||
|
Description string
|
||||||
|
|
||||||
|
// Short strings by which this source can be identified. If any slugs are
|
||||||
|
// present, the first slug will be used as the canonical URL.
|
||||||
|
Slugs []string
|
||||||
|
|
||||||
|
// Disk location of logs for this source, for each partial date range
|
||||||
|
FileLocation []LogLocation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *LogSource) LatestDate() YearMonth {
|
||||||
|
// assume it's the last in the list (although it might not be)
|
||||||
|
end := this.FileLocation[len(this.FileLocation)-1].EndMonth
|
||||||
|
if end == nil {
|
||||||
|
return YearMonth{time.Now().Year(), time.Now().Month()}
|
||||||
|
} else {
|
||||||
|
return *end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Timezone string
|
||||||
|
LinesPerPage int
|
||||||
|
Logs []LogSource
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
Timezone: time.Local.String(),
|
||||||
|
LinesPerPage: 100,
|
||||||
|
Logs: make([]LogSource, 0),
|
||||||
|
}
|
||||||
|
}
|
73
Makefile
Normal file
73
Makefile
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#
|
||||||
|
# Makefile for archive
|
||||||
|
#
|
||||||
|
|
||||||
|
VERSION:=3.0.0
|
||||||
|
|
||||||
|
SOURCES:=Makefile \
|
||||||
|
static \
|
||||||
|
cmd $(wildcard cmd/archive-server/*.go) \
|
||||||
|
$(wildcard *.go)
|
||||||
|
|
||||||
|
GOFLAGS := -ldflags='-s -w' -gcflags='-trimpath=$(GOPATH)' -asmflags='-trimpath=$(GOPATH)'
|
||||||
|
|
||||||
|
#
|
||||||
|
# Phony targets
|
||||||
|
#
|
||||||
|
|
||||||
|
.PHONY: all dist clean
|
||||||
|
|
||||||
|
all: build/linux64/archive-server build/win32/archive-server.exe
|
||||||
|
|
||||||
|
dist: \
|
||||||
|
_dist/archive-$(VERSION)-linux64.tar.gz \
|
||||||
|
_dist/archive-$(VERSION)-win32.7z \
|
||||||
|
_dist/archive-$(VERSION)-src.zip
|
||||||
|
|
||||||
|
clean:
|
||||||
|
if [ -f ./staticResources.go ] ; then rm ./staticResources.go ; fi
|
||||||
|
if [ -d ./build ] ; then rm -r ./build ; fi
|
||||||
|
if [ -f ./archive ] ; then rm ./archive ; fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Generated files
|
||||||
|
#
|
||||||
|
|
||||||
|
staticResources.go: static/ static/*
|
||||||
|
go-bindata -o staticResources.go -prefix static -pkg archive static
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Release artefacts
|
||||||
|
#
|
||||||
|
|
||||||
|
build/linux64/archive-server: $(SOURCES) staticResources.go
|
||||||
|
mkdir -p build/linux64
|
||||||
|
(cd cmd/archive-server ; \
|
||||||
|
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
|
||||||
|
go build $(GOFLAGS) -o ../../build/linux64/archive-server \
|
||||||
|
)
|
||||||
|
|
||||||
|
build/win32/archive-server.exe: $(SOURCES) staticResources.go
|
||||||
|
mkdir -p build/win32
|
||||||
|
(cd cmd/archive-server ; \
|
||||||
|
PATH=/usr/lib/mxe/usr/bin:$(PATH) CC=i686-w64-mingw32.static-gcc \
|
||||||
|
CGO_ENABLED=1 GOOS=windows GOARCH=386 \
|
||||||
|
go build $(GOFLAGS) -o ../../build/win32/archive-server.exe \
|
||||||
|
)
|
||||||
|
|
||||||
|
_dist/archive-$(VERSION)-linux64.tar.gz: build/linux64/archive-server
|
||||||
|
mkdir -p _dist
|
||||||
|
tar caf _dist/archive-$(VERSION)-linux64.tar.gz -C build/linux64 archive-server --owner=0 --group=0
|
||||||
|
|
||||||
|
_dist/archive-$(VERSION)-win32.7z: build/win32/archive-server.exe
|
||||||
|
mkdir -p _dist
|
||||||
|
( cd build/win32 ; \
|
||||||
|
if [ -f dist.7z ] ; then rm dist.7z ; fi ; \
|
||||||
|
7z a dist.7z archive-server.exe ; \
|
||||||
|
mv dist.7z ../../_dist/archive-$(VERSION)-win32.7z \
|
||||||
|
)
|
||||||
|
|
||||||
|
_dist/archive-$(VERSION)-src.zip: $(SOURCES)
|
||||||
|
git archive --format=zip HEAD > _dist/archive-$(VERSION)-src.zip
|
||||||
|
|
101
Router.go
Normal file
101
Router.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package archive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (this *ArchiveServer) lookupSourceByNumericString(slug string) *LogSource {
|
||||||
|
intval, err := strconv.Atoi(slug)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if intval >= 0 && intval < len(this.cfg.Logs) {
|
||||||
|
return &this.cfg.Logs[intval]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ArchiveServer) lookupSource(slug string) *LogSource {
|
||||||
|
if src := this.lookupSourceByNumericString(slug); src != nil {
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, ls := range this.cfg.Logs {
|
||||||
|
for _, s := range ls.Slugs {
|
||||||
|
if s == slug {
|
||||||
|
return &this.cfg.Logs[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil // not found
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ArchiveServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Server", SERVER_VERSION)
|
||||||
|
|
||||||
|
if r.Method != "GET" {
|
||||||
|
http.Error(w, "Expected GET", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(r.URL.Query().Get("y")) > 0 || len(r.URL.Query().Get("q")) > 0 || len(r.URL.Query().Get("h")) > 0 {
|
||||||
|
this.legacyRoute(w, r)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ArchiveServer) legacyRoute(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
intval := func(sz string) int {
|
||||||
|
ret, _ := strconv.Atoi(sz)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
get := r.URL.Query().Get
|
||||||
|
hasGet := func(sz string) bool {
|
||||||
|
return len(get(sz)) > 0
|
||||||
|
}
|
||||||
|
redirectf := func(format string, a ...interface{}) {
|
||||||
|
http.Redirect(w, r, fmt.Sprintf(format, a...), http.StatusTemporaryRedirect)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
hubid := 0
|
||||||
|
if hasGet("h") {
|
||||||
|
hubid = intval(get("h"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasGet("q") {
|
||||||
|
if hasGet("rx") {
|
||||||
|
redirectf(`/%d/rx/%s`, hubid, url.QueryEscape(get("q")))
|
||||||
|
} else {
|
||||||
|
redirectf(`/%d/search/%s`, hubid, url.QueryEscape(get("q")))
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if hasGet("y") && hasGet("m") {
|
||||||
|
year := intval(get("y"))
|
||||||
|
month := intval(get("m"))
|
||||||
|
if hasGet("p") {
|
||||||
|
redirectf(`/%d/%d/%d/page-%d`, hubid, year, month, intval(get("p")))
|
||||||
|
} else {
|
||||||
|
redirectf(`/%d/%d/%d`, hubid, year, month)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if hi := this.lookupSourceByNumericString(get("h")); hi != nil {
|
||||||
|
currentDate := hi.LatestDate()
|
||||||
|
redirectf(`/%d/%d/%d`, hubid, currentDate.Year, currentDate.Month)
|
||||||
|
} else {
|
||||||
|
redirectf(`/`)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
44
cmd/archive-server/main.go
Normal file
44
cmd/archive-server/main.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.ivysaur.me/archive"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
bindAddr := flag.String("listen", "127.0.0.1:80", "Bind address")
|
||||||
|
configPath := flag.String("config", "config.json", "Configuration file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
cfg, err := ioutil.ReadFile(*configPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to load configuration file '%s': %s\n", *configPath, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Loading configuration from '%s'...\n", *configPath)
|
||||||
|
opts := archive.NewConfig()
|
||||||
|
err = json.Unmarshal(cfg, &opts)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to parse configuration file: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
as, err := archive.NewArchiveServer(opts)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Starting archive server on '%s'...", *bindAddr)
|
||||||
|
err = http.ListenAndServe(*bindAddr, as)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
}
|
176
static/archive.js
Executable file
176
static/archive.js
Executable file
@ -0,0 +1,176 @@
|
|||||||
|
|
||||||
|
/* mousetrap v1.4.6 craig.is/killing/mice */
|
||||||
|
(function(J,r,f){function s(a,b,d){a.addEventListener?a.addEventListener(b,d,!1):a.attachEvent("on"+b,d)}function A(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return h[a.which]?h[a.which]:B[a.which]?B[a.which]:String.fromCharCode(a.which).toLowerCase()}function t(a){a=a||{};var b=!1,d;for(d in n)a[d]?b=!0:n[d]=0;b||(u=!1)}function C(a,b,d,c,e,v){var g,k,f=[],h=d.type;if(!l[a])return[];"keyup"==h&&w(a)&&(b=[a]);for(g=0;g<l[a].length;++g)if(k=
|
||||||
|
l[a][g],!(!c&&k.seq&&n[k.seq]!=k.level||h!=k.action||("keypress"!=h||d.metaKey||d.ctrlKey)&&b.sort().join(",")!==k.modifiers.sort().join(","))){var m=c&&k.seq==c&&k.level==v;(!c&&k.combo==e||m)&&l[a].splice(g,1);f.push(k)}return f}function K(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function x(a,b,d,c){m.stopCallback(b,b.target||b.srcElement,d,c)||!1!==a(b,d)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation?
|
||||||
|
b.stopPropagation():b.cancelBubble=!0)}function y(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=A(a);b&&("keyup"==a.type&&z===b?z=!1:m.handleKey(b,K(a),a))}function w(a){return"shift"==a||"ctrl"==a||"alt"==a||"meta"==a}function L(a,b,d,c){function e(b){return function(){u=b;++n[a];clearTimeout(D);D=setTimeout(t,1E3)}}function v(b){x(d,b,a);"keyup"!==c&&(z=A(b));setTimeout(t,10)}for(var g=n[a]=0;g<b.length;++g){var f=g+1===b.length?v:e(c||E(b[g+1]).action);F(b[g],f,c,a,g)}}function E(a,b){var d,
|
||||||
|
c,e,f=[];d="+"===a?["+"]:a.split("+");for(e=0;e<d.length;++e)c=d[e],G[c]&&(c=G[c]),b&&"keypress"!=b&&H[c]&&(c=H[c],f.push("shift")),w(c)&&f.push(c);d=c;e=b;if(!e){if(!p){p={};for(var g in h)95<g&&112>g||h.hasOwnProperty(g)&&(p[h[g]]=g)}e=p[d]?"keydown":"keypress"}"keypress"==e&&f.length&&(e="keydown");return{key:c,modifiers:f,action:e}}function F(a,b,d,c,e){q[a+":"+d]=b;a=a.replace(/\s+/g," ");var f=a.split(" ");1<f.length?L(a,f,b,d):(d=E(a,d),l[d.key]=l[d.key]||[],C(d.key,d.modifiers,{type:d.action},
|
||||||
|
c,a,e),l[d.key][c?"unshift":"push"]({callback:b,modifiers:d.modifiers,action:d.action,seq:c,level:e,combo:a}))}var h={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},B={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},H={"~":"`","!":"1",
|
||||||
|
"@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},G={option:"alt",command:"meta","return":"enter",escape:"esc",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},p,l={},q={},n={},D,z=!1,I=!1,u=!1;for(f=1;20>f;++f)h[111+f]="f"+f;for(f=0;9>=f;++f)h[f+96]=f;s(r,"keypress",y);s(r,"keydown",y);s(r,"keyup",y);var m={bind:function(a,b,d){a=a instanceof Array?a:[a];for(var c=0;c<a.length;++c)F(a[c],b,d);return this},
|
||||||
|
unbind:function(a,b){return m.bind(a,function(){},b)},trigger:function(a,b){if(q[a+":"+b])q[a+":"+b]({},a);return this},reset:function(){l={};q={};return this},stopCallback:function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable},handleKey:function(a,b,d){var c=C(a,b,d),e;b={};var f=0,g=!1;for(e=0;e<c.length;++e)c[e].seq&&(f=Math.max(f,c[e].level));for(e=0;e<c.length;++e)c[e].seq?c[e].level==f&&(g=!0,
|
||||||
|
b[c[e].seq]=1,x(c[e].callback,d,c[e].combo,c[e].seq)):g||x(c[e].callback,d,c[e].combo);c="keypress"==d.type&&I;d.type!=u||w(a)||c||t(b);I=g&&"keydown"==d.type}};J.Mousetrap=m;"function"===typeof define&&define.amd&&define(m)})(window,document);
|
||||||
|
|
||||||
|
/* archive.js */
|
||||||
|
|
||||||
|
function i(s) {
|
||||||
|
return document.getElementById(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function t(e) {
|
||||||
|
e.style.display = (e.style.display == 'none') ? 'block' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function urldesc(s) {
|
||||||
|
return decodeURIComponent(s.replace(/\+/g, " "));
|
||||||
|
}
|
||||||
|
|
||||||
|
function cookie_set(key, value) {
|
||||||
|
document.cookie = (key+"="+value+"; expires=Sat, 20 Sep 2059 09:05:12; path=/");
|
||||||
|
}
|
||||||
|
|
||||||
|
function cookie_clear(key) {
|
||||||
|
document.cookie = (key+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/");
|
||||||
|
}
|
||||||
|
|
||||||
|
function cookie_get(key) {
|
||||||
|
var parts = document.cookie.split("; ").map(function(x) { return x.split("="); });
|
||||||
|
for (var i = 0, e = parts.length; i !== e; ++i) {
|
||||||
|
if (parts[i][0] == key) {
|
||||||
|
return parts[i][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlight(str) {
|
||||||
|
return str
|
||||||
|
.replace(/(\[\d\d\d\d-\d\d-\d\d\s\d\d\:\d\d\:\d\d\] )(\*.+)/g, "$1<span class=\"sys\">$2</span>")
|
||||||
|
.replace(
|
||||||
|
/\[(\d\d\d\d-\d\d-\d\d)\s(\d\d\:\d\d)\:(\d\d)\]/g,
|
||||||
|
'<span class="timestamp">[<span class="ts_split_d">$1 </span><span class="ts_split_hm">$2</span><span class="ts_split_s">:$3</span>]</span>'
|
||||||
|
)
|
||||||
|
.replace(/(\[[0-9:\-\s]*?\])/g, '<span class="timestamp">$1</span>')
|
||||||
|
.replace(/(\<\;[^\s]+?\>\;)/g, "<span class=\"chat\">$1</span>")
|
||||||
|
.replace(/(\*\*\*.+)/g, "<span class=\"sys\">$1</span>")
|
||||||
|
.replace(/(\>\;imp[^\n\r\<]*)/g, "<span class=\"gt\">$1</span>")
|
||||||
|
.replace(/(https?:\/\/.+?)([\s|<])/g, "<a href=\"$1\" rel=\"noreferrer\">$1</a>$2")
|
||||||
|
.replace(/magnet:\?.+dn=([^\< ]+)/g, function(match, m1) {
|
||||||
|
return "<a href=\"" + match + "\">[MAGNET] " + urldesc(m1) + "</a>";
|
||||||
|
})
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fontSize(change) {
|
||||||
|
var curSize = cookie_get("fontsize");
|
||||||
|
if (curSize === null) {
|
||||||
|
curSize = 12;
|
||||||
|
} else {
|
||||||
|
curSize = + curSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
curSize += change;
|
||||||
|
|
||||||
|
cookie_set("fontsize", curSize);
|
||||||
|
|
||||||
|
i("chatarea").style["fontSize"] = ""+curSize+"px";
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleMenu() {
|
||||||
|
t(i("tr1"));
|
||||||
|
t(i("tr2"));
|
||||||
|
t(i("spm"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightLine(no) {
|
||||||
|
var lines = i('chatarea').innerHTML.split('<br>');
|
||||||
|
|
||||||
|
lines[no] = '<span class="line-highlighted">' + lines[no] + '</span>';
|
||||||
|
|
||||||
|
i('chatarea').innerHTML = lines.join('<br>');
|
||||||
|
}
|
||||||
|
|
||||||
|
var alreadyLoaded = false;
|
||||||
|
|
||||||
|
function onLoad() {
|
||||||
|
|
||||||
|
if (alreadyLoaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
alreadyLoaded = true;
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
i('chatarea').innerHTML = highlight(i('chatarea').innerHTML);
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
if (
|
||||||
|
! /\/search\//.test(window.location.pathname) &&
|
||||||
|
document.location.hash.substr(0, 6) === '#line-'
|
||||||
|
) {
|
||||||
|
highlightLine( parseInt(document.location.hash.substr(6), 10) );
|
||||||
|
document.location.hash = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
fontSize(0);
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
i('selHub').onchange = function() {
|
||||||
|
if ( /\/search\//.test(window.location.pathname) ) {
|
||||||
|
window.location.pathname = i('selHub').value + "/search/" + encodeURIComponent( i('searchbox').value );
|
||||||
|
|
||||||
|
} else if ( /\/rx\//.test(window.location.pathname) ) {
|
||||||
|
window.location.pathname = i('selHub').value + "/rx/" + encodeURIComponent( i('searchbox').value )
|
||||||
|
|
||||||
|
} else {
|
||||||
|
i('frmHub').submit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
Mousetrap.bind('ctrl+alt+h', function() { i("pgprev").click(); });
|
||||||
|
Mousetrap.bind('ctrl+alt+l', function() { i("pgnext").click(); });
|
||||||
|
Mousetrap.bind('ctrl+alt+j', function() {
|
||||||
|
YMgoto( YMmod(i("seldate").value, 1) )
|
||||||
|
});
|
||||||
|
Mousetrap.bind('ctrl+alt+k', function() {
|
||||||
|
YMgoto( YMmod(i("seldate").value, -1) )
|
||||||
|
});
|
||||||
|
|
||||||
|
Mousetrap.bind('ctrl+alt+m', function() {
|
||||||
|
i("searchbox").focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function YMmod(str, change) {
|
||||||
|
var t = str.split('-').map(function(x) { return +x; });
|
||||||
|
t[1] += change;
|
||||||
|
if (t[1] == 13) {
|
||||||
|
t[0] += 1;
|
||||||
|
t[1] = 1;
|
||||||
|
}
|
||||||
|
if (t[1] == 0) {
|
||||||
|
t[0] -= 1;
|
||||||
|
t[1] = 12;
|
||||||
|
}
|
||||||
|
return t.join('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
function YMgoto(str) {
|
||||||
|
var t = str.split("-");
|
||||||
|
i("f_y").value = t[0];
|
||||||
|
i("f_m").value = t[1];
|
||||||
|
i("seldate").form.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setYM(el) {
|
||||||
|
YMgoto(el.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', onLoad);
|
16
static/index.php
Executable file
16
static/index.php
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
Chat Archives
|
||||||
|
`````````````
|
||||||
|
Requires PHP 5.4 (short-array syntax and ENT_SUBSTITUTE) with short_open_tag
|
||||||
|
URL rewriting for nginx;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri /index.php?$args;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
require __DIR__.'/../includes/bootstrap.php';
|
||||||
|
URLRouter::routeRequest();
|
147
static/style.css
Executable file
147
static/style.css
Executable file
@ -0,0 +1,147 @@
|
|||||||
|
/* Chat archives */
|
||||||
|
|
||||||
|
/* Page style */
|
||||||
|
|
||||||
|
html {
|
||||||
|
overflow-y:scroll;
|
||||||
|
}
|
||||||
|
html,body {
|
||||||
|
margin:0;padding:0;border:0;
|
||||||
|
font-family:"Segoe UI",Arial,sans-serif;
|
||||||
|
font-size:12px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
margin:0;
|
||||||
|
padding-left:20px;
|
||||||
|
}
|
||||||
|
.mini-separator {
|
||||||
|
display: inline-block;
|
||||||
|
zoom: 1;
|
||||||
|
height: 16px;
|
||||||
|
width: 0px;
|
||||||
|
border-left: 1px solid darkgrey;
|
||||||
|
border-right: 1px solid #DDD;
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
margin:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Layout */
|
||||||
|
|
||||||
|
.layout-top {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 22px;
|
||||||
|
padding: 3px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.layout-body {
|
||||||
|
margin-top:40px;
|
||||||
|
padding:3px;
|
||||||
|
}
|
||||||
|
.layout-pushdown {
|
||||||
|
position:relative;
|
||||||
|
top:3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat styling */
|
||||||
|
|
||||||
|
#chatarea {
|
||||||
|
word-break:break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timestamp {color: #BBB;}
|
||||||
|
.chat { font-weight:bold;
|
||||||
|
color: darkblue;}
|
||||||
|
.sys { font-style:italic;
|
||||||
|
color: darkgreen;}
|
||||||
|
.logo { width: 102px;
|
||||||
|
height: 37px;
|
||||||
|
display: block;
|
||||||
|
border:0;
|
||||||
|
padding-bottom: 1.0em;}
|
||||||
|
.gt { color:#0A0;font-weight:bold;}
|
||||||
|
.line-highlighted {
|
||||||
|
background:lightyellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toolbar styling */
|
||||||
|
|
||||||
|
.pad { display:inline; padding:0 8px;}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
background: #DDD;
|
||||||
|
box-shadow: 0px 4px 24px #CCC;
|
||||||
|
}
|
||||||
|
.nav form { display: inline;}
|
||||||
|
.nav .btn { background: white;
|
||||||
|
color:black;
|
||||||
|
border: 1px solid lightgrey;
|
||||||
|
padding: 0 6px;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
.nav a:hover {border-color: grey black black grey;}
|
||||||
|
|
||||||
|
|
||||||
|
.area-search {
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
#searchbox {
|
||||||
|
width:170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
display:inline-block;
|
||||||
|
width:16px;
|
||||||
|
height:16px;
|
||||||
|
content:'⌂ ';
|
||||||
|
margin-left:2px;
|
||||||
|
margin-right:4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile view */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.logo { margin: 0 auto; }
|
||||||
|
.nav {
|
||||||
|
height: 55px;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
.nav, .nav select {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height:16px;
|
||||||
|
}
|
||||||
|
.nav .btn {
|
||||||
|
padding: 2px 12px;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
.pad {
|
||||||
|
display:block;
|
||||||
|
padding:0;
|
||||||
|
height:8px;
|
||||||
|
}
|
||||||
|
.layout-body {
|
||||||
|
margin-top:75px;
|
||||||
|
}
|
||||||
|
#logo {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
.mini-separator {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.area-search {
|
||||||
|
float:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchbox {
|
||||||
|
width:auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
.ts_split_d, .ts_split_s { display:none; }
|
||||||
|
}
|
281
staticResources.go
Normal file
281
staticResources.go
Normal file
File diff suppressed because one or more lines are too long
0
tplError.go
Normal file
0
tplError.go
Normal file
Loading…
Reference in New Issue
Block a user