--- /dev/null
+===========================================================================
+Other terms and conditions
+===========================================================================
+
+Portions of this software (libraries) were developed by other people,
+and are subject to the licence terms that those people chose.
+
+---------------------------------------------------------------------------
+Modernizr
+
+This JavaScript library, available at https://modernizr.com/,
+is distributed under the MIT licence:
+
+ Copyright © 2009-2017
+ 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.
-<html>
+<html class="no-js">
<head>
<title>eBook Library</title>
<link href="lib.css" rel="stylesheet" type="text/css"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
- <body>
+ <body onload="onSearch();">
<form>
<input id="search" onclick="onSearch();" type="button" value="Search"/>
<span class="term">Author: <input id="aut" type="text"/></span>
<div id="details" class="tooltip" onclick="hideDetails();">(No information available)</div>
- <script src="lib.min.js"></script>
+ <script src="modernizr-custom.js"></script>
+ <script src="all.js"></script>
</body>
</html>
--- /dev/null
+/*! modernizr 3.5.0 (Custom Build) | MIT *
+ * https://modernizr.com/download/?-fetch-setclasses !*/
+!function(n,e,s){ function o(n,e){ return typeof n===e; } function a(){ var n,e,s,a,t,l,r; for(var c in f) if(f.hasOwnProperty(c)) { if(n=[],e=f[c],e.name && (n.push(e.name.toLowerCase()),e.options && e.options.aliases && e.options.aliases.length)) for(s=0;s<e.options.aliases.length;s++) n.push(e.options.aliases[s].toLowerCase()); for(a=o(e.fn,'function')?e.fn():e.fn,t=0;t<n.length;t++) l=n[t],r=l.split('.'),1===r.length?Modernizr[r[0]]=a:(!Modernizr[r[0]]||Modernizr[r[0]]instanceof Boolean||(Modernizr[r[0]]=new Boolean(Modernizr[r[0]])),Modernizr[r[0]][r[1]]=a),i.push((a?'':'no-')+r.join('-'));}} function t(n){var e=r.className,s=Modernizr._config.classPrefix||'';if(c&&(e=e.baseVal),Modernizr._config.enableJSClass){var o=new RegExp('(^|\\s)'+s+'no-js(\\s|$)');e=e.replace(o,'$1'+s+'js$2');}Modernizr._config.enableClasses&&(e+=' '+s+n.join(' '+s),c?r.className.baseVal=e:r.className=e);}var i=[],f=[],l={_version:'3.5.0',_config:{classPrefix:'',enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(n,e){var s=this;setTimeout(function(){e(s[n]);},0);},addTest:function(n,e,s){f.push({name:n,fn:e,options:s});},addAsyncTest:function(n){f.push({name:null,fn:n});}},Modernizr=function(){};Modernizr.prototype=l,Modernizr=new Modernizr;var r=e.documentElement,c='svg'===r.nodeName.toLowerCase();Modernizr.addTest('fetch','fetch'in n),a(),t(i),delete l.addTest,delete l.addAsyncTest;for(var u=0;u<Modernizr._q.length;u++)Modernizr._q[u]();n.Modernizr=Modernizr;}(window,document);
}
fetch(url, {method:'GET', cache:'default'})
- .then(response => response.json())
- .then((jsonValue) => {
- console.log('JSON response for info: ', jsonValue);
+ .then(function(response) {return response.json();})
+ .then(function(jsonValue) {
my.cache = jsonValue;
notifyAll(); // inform all subscribers that the model has been updated
})
- .catch(err => {
+ .catch(function(err) {
var msg = 'Error fetching book details via URL: ' + url + ': ' + err;
console.log(msg, err.stack);
report(msg);
-//QuanLib: eBook Library
-//(C) 2017 by Christian Jaekl (cejaekl@yahoo.com)
+// QuanLib: eBook Library
+// Copyright (C) 2017 by Christian Jaekl (cejaekl@yahoo.com)
'use strict';
PagingController.init(BooksModel);
SearchController.init(BooksModel);
+if (Modernizr.fetch) {
+ console.log('quanweb: browser feature check: OK');
+}
+else {
+ // If we cared about supporting older browsers (at this point, IE11 and Adroid 4.x's built-in browser,
+ // neither of which is due to receive security patch support for much longer), then we would insert a
+ // shim here to implement the fetch API. But, in this case, we don't and won't.
+ alert('Sorry, this page will not work in your browser.\nPlease use a recent version of Chrome, Edge or Firefox instead.');
+}
+
// ================
// Global functions
//
var url = constructSearchUrl();
fetch(url, {method:'GET', cache:'default'})
- .then(response => response.json())
- .then((jsonValue) => {
+ .then(function(response) {return response.json();})
+ .then(function(jsonValue) {
// console.log('JSON response: ', jsonValue);
booksModel.ids = jsonValue;
booksModel.count = booksModel.ids.length;
PagingController.adjustPos(0);
})
- .catch(err => {
+ .catch(function(err) {
var msg = 'Error fetching JSON from URL: ' + url + ': ' + err + ':' + err.stack;
console.log(msg);
report(msg);
package main
+import (
+ "errors"
+ "fmt"
+ "github.com/alyu/configparser"
+ "os"
+ "strconv"
+)
+
type qwConfig struct {
- user, pass, dbName, efsBasePath string
+ user, pass, dbName string
+ basePath string
+ port int
+}
+
+func checkedSection(config *configparser.Configuration, name string) (*configparser.Section, error) {
+ section, err := config.Section(name)
+ if nil != err {
+ fmt.Println("ERROR! Could not locate section [" + name + "] in config file:", err)
+ return nil, err
+ }
+ return section, nil
+}
+
+func checkedValue(section *configparser.Section, name string) (string, error) {
+ if ! section.Exists(name) {
+ return "", errors.New("ERROR! Value named \"" + name + "\" not found in config file.")
+ }
+
+ value := section.ValueOf(name)
+ return value, nil
+}
+
+// Note: Will exit if config cannot be loaded
+func GetConfig() (*qwConfig) {
+ config, err := loadConfig()
+ if nil != err {
+ fmt.Println("FATAL! Cannot load config. Unable to continue.", err)
+ os.Exit(1)
+ }
+
+ return config
}
-func getConfig() qwConfig {
- // TODO: use a real password, and load config info from a file
- return qwConfig{user:"quanlib", pass:"quanlib", dbName:"quanlib", efsBasePath:"/arc/quanlib/efs"}
+func loadConfig() (*qwConfig, error) {
+ // TODO: Make the path to the config file configurable (environment variable?)
+ // TODO: Load the config file once, and cache the values for subsequent getConfig() calls
+
+ const configFile = "quanlib.ini"
+
+ config, err := configparser.Read(configFile)
+ if nil != err {
+ fmt.Println("ERROR! Failed to read config file:", configFile, err)
+ return nil, err
+ }
+
+ var section *configparser.Section
+
+ // ------------------
+ // Section: database
+
+ var dbName, user, pass string
+
+ section, err = checkedSection(config, "database")
+ if nil != err {
+ return nil, err
+ }
+
+ dbName, err = checkedValue(section, "name")
+ if nil != err {
+ return nil, err
+ }
+
+ user, err = checkedValue(section, "user")
+ if nil != err {
+ return nil, err
+ }
+
+ pass, err = checkedValue(section, "pass")
+ if nil != err {
+ return nil, err
+ }
+
+ // --------------------
+ // Section: filesystem
+
+ var basePath string
+
+ section, err = checkedSection(config, "filesystem")
+ if nil != err {
+ return nil, err
+ }
+
+ basePath, err = checkedValue(section, "basePath")
+ if nil != err {
+ return nil, err
+ }
+
+ // -------------
+ // Section: web
+
+ var port int
+ var portStr string
+
+ section, err = checkedSection(config, "web")
+ if nil != err {
+ return nil, err
+ }
+
+ portStr, err = checkedValue(section, "port")
+ if nil != err {
+ return nil, err
+ }
+ port, err = strconv.Atoi(portStr)
+ if nil != err {
+ return nil, err
+ }
+
+ return &qwConfig{ user:user, pass:pass, dbName:dbName,
+ basePath:basePath,
+ port:port },
+ nil
}
g_mutex.Lock()
defer g_mutex.Unlock()
if nil == g_db {
- config := getConfig()
+ config := GetConfig()
g_db = openDb(config.user, config.pass, config.dbName)
}
}
}
func efsPathForId(efsId int) string {
- config := getConfig()
+ config := GetConfig()
idStr := fmt.Sprintf("%010d", efsId)
- path := fmt.Sprintf("%s/%s/%s/%s/%s/%s.dat", config.efsBasePath, idStr[0:2], idStr[2:4], idStr[4:6], idStr[6:8], idStr)
+ path := fmt.Sprintf("%s/efs/%s/%s/%s/%s/%s.dat", config.basePath, idStr[0:2], idStr[2:4], idStr[4:6], idStr[6:8], idStr)
return path
}
import (
"database/sql"
"net/http"
+ "strconv"
)
// ============================================================================
http.HandleFunc("/", handler)
- http.ListenAndServe(":8001", nil)
+ config := GetConfig()
+ http.ListenAndServe(":" + strconv.Itoa(config.port), nil)
}