From 06fc726d7a0a74ae4ba26be74da578686992d413 Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Sat, 30 Dec 2017 14:02:32 +0100
Subject: web: Use working online service for barcodes
Replace webservice for generating Code 39 barcodes with a
working one.
---
templates/users/entry.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/templates/users/entry.html b/templates/users/entry.html
index 60d2dd3..065c4e7 100644
--- a/templates/users/entry.html
+++ b/templates/users/entry.html
@@ -11,7 +11,7 @@
Download
- Get HighRes Version (external)
+ Get HighRes Version (external)
Firstname
{{{FIRSTNAME}}}
--
cgit v1.2.3
From 9c91c7390dde605424e0867ef18deb49473a0f0e Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Thu, 28 Dec 2017 14:06:27 +0100
Subject: README: Fix SQL syntax error
---
README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README b/README
index 91a2ae3..e3e2984 100644
--- a/README
+++ b/README
@@ -76,6 +76,6 @@ but you need to modify a few things.
=== Database ===
* Create user
- `sqlite3 shop.db "INSERT INTO users (id, email, firstname, lastname) VALUES (1, "test@tester", "Firstname", "Lastname");`
+ `sqlite3 shop.db "INSERT INTO users (id, email, firstname, lastname) VALUES (1, 'test@tester', 'Firstname', 'Lastname');"`
* Setup user password
`mdbus2 io.mainframe.shopsystem.Database /io/mainframe/shopsystem/database io.mainframe.shopsystem.Database.SetUserPassword 1 "password"`
--
cgit v1.2.3
From 467607a8c6aca7bb97278007d43d704053a41401 Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Thu, 28 Dec 2017 14:14:10 +0100
Subject: README: Add missing dependencies
---
README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README b/README
index e3e2984..7fafe6d 100644
--- a/README
+++ b/README
@@ -33,7 +33,7 @@ The system consists of multiple daemons written in Vala, which communicate
with each other using DBus.
Build Dependencies:
- * apt install build-essential valac libesmtp-dev libgpgme11-dev libncursesw5-dev libncurses5-dev libgee-0.8-dev libgmime-2.6-dev libarchive-dev libgstreamer1.0-dev libgtk2.0-dev librsvg2-dev libsoup2.4-dev libsqlite3-dev libpango1.0-dev libssl-dev dbus-x11
+ * apt install build-essential valac libesmtp-dev libgpgme11-dev libncursesw5-dev libncurses5-dev libgee-0.8-dev libgmime-2.6-dev libarchive-dev libgstreamer1.0-dev libgtk2.0-dev librsvg2-dev libsoup2.4-dev libsqlite3-dev libpango1.0-dev libssl-dev dbus-x11 mdbus2 policykit-1
Additional runtime dependencies:
* apt install fonts-lmodern gstreamer1.0-alsa gstreamer1.0-plugins-base
--
cgit v1.2.3
From 7effa097b0cb2878ef012aea71b6b013498538a7 Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Fri, 29 Dec 2017 18:12:48 +0100
Subject: README: Use system DBus instead of session DBus
---
README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README b/README
index 7fafe6d..29c498c 100644
--- a/README
+++ b/README
@@ -78,4 +78,4 @@ but you need to modify a few things.
* Create user
`sqlite3 shop.db "INSERT INTO users (id, email, firstname, lastname) VALUES (1, 'test@tester', 'Firstname', 'Lastname');"`
* Setup user password
- `mdbus2 io.mainframe.shopsystem.Database /io/mainframe/shopsystem/database io.mainframe.shopsystem.Database.SetUserPassword 1 "password"`
+ `mdbus2 -s io.mainframe.shopsystem.Database /io/mainframe/shopsystem/database io.mainframe.shopsystem.Database.SetUserPassword 1 "password"`
--
cgit v1.2.3
From 16e73b8157e1bd1929d0e78d1347be14ab894a8d Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Fri, 29 Dec 2017 18:56:32 +0100
Subject: curses-ui: Load logo from file
This loads the ASCII art logo shown in the curses UI
from a file instead of having it hard-coded in the binary.
---
README | 5 +++++
logo.txt | 6 ++++++
src/curses-ui/curses-ui.vala | 16 ++++++++--------
src/curses-ui/logo.vala | 27 ++++++++++++++++++++-------
src/curses-ui/main.vala | 4 +++-
5 files changed, 42 insertions(+), 16 deletions(-)
create mode 100644 logo.txt
diff --git a/README b/README
index 29c498c..fdd141a 100644
--- a/README
+++ b/README
@@ -79,3 +79,8 @@ but you need to modify a few things.
`sqlite3 shop.db "INSERT INTO users (id, email, firstname, lastname) VALUES (1, 'test@tester', 'Firstname', 'Lastname');"`
* Setup user password
`mdbus2 -s io.mainframe.shopsystem.Database /io/mainframe/shopsystem/database io.mainframe.shopsystem.Database.SetUserPassword 1 "password"`
+
+== Customize Your Shop ==
+
+Edit the Logo in the logo.txt File.
+A helpful tool you will found here [http://patorjk.com/software/taag/](http://patorjk.com/software/taag/)
diff --git a/logo.txt b/logo.txt
new file mode 100644
index 0000000..ee57aa1
--- /dev/null
+++ b/logo.txt
@@ -0,0 +1,6 @@
+ _ ___ _____ ____ _
+ | |/ / ||_ _| / ___|| |__ ___ _ __
+ | ' /| __|| | \___ \| '_ \ / _ \| '_ \
+ | . \| |_ | | ___) | | | | (_) | |_) )
+ |_|\_\\__||_| |____/|_| |_|\___/| .__/
+ |_|
diff --git a/src/curses-ui/curses-ui.vala b/src/curses-ui/curses-ui.vala
index ab34787..f7f6239 100644
--- a/src/curses-ui/curses-ui.vala
+++ b/src/curses-ui/curses-ui.vala
@@ -21,7 +21,7 @@ public class CursesUI {
//StatusPanel statuswin;
MessageBoxOverlay mbOverlay;
- public CursesUI() {
+ public CursesUI(string binarylocation) {
/* unicode support */
Intl.setlocale(LocaleCategory.CTYPE, "");
@@ -37,8 +37,8 @@ public class CursesUI {
Curses.init_pair(1, Curses.Color.GREEN, Curses.Color.BLACK);
Curses.init_pair(2, Curses.Color.WHITE, Curses.Color.RED);
- /* initialize widgets */
- banner = new Logo();
+ /* initialize widgets */
+ banner = new Logo(binarylocation);
//statuswin = new StatusPanel();
messages = new MessageBox();
clkwin = new ClockWindow();
@@ -68,18 +68,18 @@ public class CursesUI {
//}
public void log(MessageType type, string message) {
- switch (type) {
+ switch (type) {
case MessageType.WARNING:
messages.add(message, MessageBox.WARN_COLOR);
break;
- case MessageType.ERROR:
+ case MessageType.ERROR:
messages.add(message, MessageBox.ERROR_COLOR);
break;
default:
messages.add(message, MessageBox.INFO_COLOR);
break;
}
-
+
}
public void log_overlay(string title, string message, int closeAfter) {
@@ -87,7 +87,7 @@ public class CursesUI {
Timeout.add_seconds(closeAfter, closeMbOverlay);
}
- public void dialog_open(string title, string message, int closeAfter=0) {
+ public void dialog_open(string title, string message, int closeAfter=0) {
dialog = new Dialog(message, title, closeAfter);
if (closeAfter > 0) {
Timeout.add_seconds(closeAfter, close);
@@ -102,7 +102,7 @@ public class CursesUI {
return false;
}
- bool close() {
+ bool close() {
dialog_close();
// just call me once
return false;
diff --git a/src/curses-ui/logo.vala b/src/curses-ui/logo.vala
index dbc716d..c795981 100644
--- a/src/curses-ui/logo.vala
+++ b/src/curses-ui/logo.vala
@@ -18,17 +18,30 @@ using Curses;
public class Logo {
Window win;
- public Logo() {
+ public Logo(string binarylocation) {
win = new Window(8, COLS - 2, 0, 1);
win.bkgdset(COLOR_PAIR(1) | Attribute.BOLD);
win.addstr("\n");
- win.addstr(" _ ___ _____ ____ _ \n");
- win.addstr(" | |/ / ||_ _| / ___|| |__ ___ _ __ \n");
- win.addstr(" | ' /| __|| | \\___ \\| '_ \\ / _ \\| '_ \\ \n");
- win.addstr(" | . \\| |_ | | ___) | | | | (_) | |_) )\n");
- win.addstr(" |_|\\_\\\\__||_| |____/|_| |_|\\___/| .__/ \n");
- win.addstr(" |_| \n");
+
+ var file = File.new_for_path (binarylocation + "/../../logo.txt");
+
+ if (!file.query_exists ()) {
+ stderr.printf ("File '%s' doesn't exist.\n", file.get_path ());
+ }
+
+ try {
+ // Open file for reading and wrap returned FileInputStream into a
+ // DataInputStream, so we can read line by line
+ var dis = new DataInputStream (file.read ());
+ string line;
+ // Read lines until end of file (null) is reached
+ while ((line = dis.read_line (null)) != null) {
+ win.addstr(line+"\n");
+ }
+ } catch (Error e) {
+ error ("%s", e.message);
+ }
win.clrtobot();
diff --git a/src/curses-ui/main.vala b/src/curses-ui/main.vala
index da822a9..7020586 100644
--- a/src/curses-ui/main.vala
+++ b/src/curses-ui/main.vala
@@ -53,7 +53,9 @@ public static int main(string[] args) {
error("IOError: %s\n", e.message);
}
- ui = new CursesUI();
+ string binarylocation = File.new_for_path(args[0]).get_parent().get_path();
+
+ ui = new CursesUI(binarylocation);
Log.set_default_handler(log_handler);
--
cgit v1.2.3
From 14c4ab56c2a2aa12b316854a5765d075b9c2801a Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Fri, 29 Dec 2017 22:19:51 +0100
Subject: README: Add demo data
---
README | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/README b/README
index fdd141a..8081e65 100644
--- a/README
+++ b/README
@@ -78,7 +78,10 @@ but you need to modify a few things.
* Create user
`sqlite3 shop.db "INSERT INTO users (id, email, firstname, lastname) VALUES (1, 'test@tester', 'Firstname', 'Lastname');"`
* Setup user password
- `mdbus2 -s io.mainframe.shopsystem.Database /io/mainframe/shopsystem/database io.mainframe.shopsystem.Database.SetUserPassword 1 "password"`
+ `mdbus2 -s io.mainframe.shopsystem.Database /io/mainframe/shopsystem/database io.mainframe.shopsystem.Database.SetUserPassword 1 "password"`
+ * Demo Data
+ `sqlite3 shop.db "INSERT INTO categories (name) VALUES ('Getränke')";`
+ `sqlite3 shop.db "INSERT INTO supplier (name,city,postal_code,street,phone,website) VALUES ('Demo Lieferant','Musterstadt','12345','Musterstraße 5','+49 1234 56789','https://www.ktt.de');"`
== Customize Your Shop ==
--
cgit v1.2.3
From f07b648ce9b6f06200bdd0090d1fdac15fd3351d Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Sat, 30 Dec 2017 12:41:55 +0100
Subject: README: add superuser permission to example user
---
README | 1 +
1 file changed, 1 insertion(+)
diff --git a/README b/README
index 8081e65..830ae5b 100644
--- a/README
+++ b/README
@@ -79,6 +79,7 @@ but you need to modify a few things.
`sqlite3 shop.db "INSERT INTO users (id, email, firstname, lastname) VALUES (1, 'test@tester', 'Firstname', 'Lastname');"`
* Setup user password
`mdbus2 -s io.mainframe.shopsystem.Database /io/mainframe/shopsystem/database io.mainframe.shopsystem.Database.SetUserPassword 1 "password"`
+ `sqlite3 shop.db "UPDATE authentication set superuser = 1, auth_users = 1, auth_products = 1, auth_cashbox = 1 where user = 1";`
* Demo Data
`sqlite3 shop.db "INSERT INTO categories (name) VALUES ('Getränke')";`
`sqlite3 shop.db "INSERT INTO supplier (name,city,postal_code,street,phone,website) VALUES ('Demo Lieferant','Musterstadt','12345','Musterstraße 5','+49 1234 56789','https://www.ktt.de');"`
--
cgit v1.2.3
From 207e77c7cbab87f3bf537dd11ca391f12df01129 Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Tue, 2 Jan 2018 13:58:11 +0100
Subject: all: replace hard-coded KtT branding with dynamic text
* extend config file with general details
* extend curves-ui makefile add config
* Use name from config
* replace static text with dynamic text
* read short name and long name from config
* replace SHORTNAME tags with the shortname
* replace mail related branding information
* add missing Replacement
* make VAT (Umsatzsteuer) optional
* make space name dynamic
* make short name in treasuremail text dynamic
* make pdf text more dynamic
---
example.cfg | 11 +++++
invoice/invoice.final.html | 7 +---
invoice/invoice.final.txt | 6 +--
invoice/invoice.temporary.html | 7 +---
invoice/invoice.temporary.txt | 6 +--
invoice/pdf-template.txt | 4 +-
invoice/treasurer.mail.txt | 2 +-
invoice/vat.html | 4 ++
invoice/vat.txt | 3 ++
src/curses-ui/Makefile | 2 +-
src/curses-ui/main.vala | 9 +++++
src/invoice/invoice.vala | 41 ++++++++++++++++---
src/pdf-invoice/pdf-invoice.vala | 37 ++++++++++++-----
src/web/web.vala | 87 ++++++++++++++++++++++++++++------------
templates/index.html | 2 +-
templates/menu.html | 2 +-
16 files changed, 165 insertions(+), 65 deletions(-)
create mode 100644 invoice/vat.html
create mode 100644 invoice/vat.txt
diff --git a/example.cfg b/example.cfg
index 1dbede7..628d73f 100644
--- a/example.cfg
+++ b/example.cfg
@@ -1,3 +1,7 @@
+[GENERAL]
+longname = Kreativität trifft Technik e.V.
+shortname = KTT
+spacename = Mainframe
[DATABASE]
file = /path/to/shop.db
[INPUT]
@@ -9,6 +13,8 @@ port = 587
username = shop-system@server.example.com
password = my_top_secret_password
starttls = true
+mailfromaddress = shop@kreativitaet-trifft-technik.de
+treasurermailaddress = shop-einzug@kreativitaet-trifft-technik.de
[AUDIO]
path = /path/to/sounds/
[PGP]
@@ -19,3 +25,8 @@ filepath = /path/to/web/templates/
port = 8080
[INVOICE]
datadir = /path/to/invoice/
+vat = no
+addressrow = Kreativität trifft Technik e.V., Bahnhofsplatz 10, 26122 Oldenburg
+footer1 = Kreativität trifft Technik e.V.\nAmtsgericht Oldenburg VR 201044\n\nHackspace „Mainframe“\nFabLab „Fab-O-Lab“\nSchnittstelle „Schnittstelle“\n\nBahnhofsplatz 10 • 26122 Oldenburg
+footer2 = Raiffeisenbank Oldenburg\nIBAN: DE34 2806 0228 0037 0185 00\nBIC: GENODEF1OL2\n\n\nFinanzamt Oldenburg\nAls gemeinnützig anerkannt.\nSteuer Nr.: 64/220/18413
+footer3 = Mail: vorstand@kreativitaet-trifft-technik.de\nWeb: www.kreativitaet-trifft-technik.de\n\n\n\nBGB-Vorstand:\nPatrick Günther, Jan Janssen, Andre Schäfer, Lars Hüsemann
diff --git a/invoice/invoice.final.html b/invoice/invoice.final.html
index 006321f..3856231 100644
--- a/invoice/invoice.final.html
+++ b/invoice/invoice.final.html
@@ -5,10 +5,7 @@ berechnen:
{{{INVOICE_TABLE}}}
-
Umsatzsteuer wird nicht erhoben, da der Verein Kreativität trifft Technik e.V.
-als Kleinunternehmen unter die Regelung des
-§ 19 Abs. 1 UStG
-fällt.
+{{{VAT}}}
-
Grüße aus dem Mainframe,
+
Grüße aus dem {{{SPACENAME}}},
das Shop-System
diff --git a/invoice/invoice.final.txt b/invoice/invoice.final.txt
index 80272f3..80e60b8 100644
--- a/invoice/invoice.final.txt
+++ b/invoice/invoice.final.txt
@@ -5,9 +5,7 @@ wie folgt zu berechnen:
{{{INVOICE_TABLE}}}
-Umsatzsteuer wird nicht erhoben, da der Verein Kreativität trifft
-Technik e.V. als Kleinunternehmen unter die Regelung des § 19
-Abs. 1 UStG fällt.
+{{{VAT}}}
-Grüße aus dem Mainframe,
+Grüße aus dem {{{SPACENAME}}},
das Shop-System
diff --git a/invoice/invoice.temporary.html b/invoice/invoice.temporary.html
index eb56443..26dc216 100644
--- a/invoice/invoice.temporary.html
+++ b/invoice/invoice.temporary.html
@@ -5,10 +5,7 @@ berechnen:
{{{INVOICE_TABLE}}}
-
Umsatzsteuer wird nicht erhoben, da der Verein Kreativität trifft Technik e.V.
-als Kleinunternehmen unter die Regelung des
-§ 19 Abs. 1 UStG
-fällt.
+{{{VAT}}}
Bei dieser Abrechnung handelt es sich lediglich um einen Zwischenstand. Die
Hauptrechnung wird einmal monatlich getrennt zugestellt und der Gesamtbetrag
@@ -16,5 +13,5 @@ wird dann vom angegebenen Bankkonto eingezogen.
Der Gesamtbetrag für den aktuellen Monat beträgt bisher:
{{{SUM_MONTH}}} €
-
Grüße aus dem Mainframe,
+
Grüße aus dem {{{SPACENAME}}},
das Shop-System
diff --git a/invoice/invoice.temporary.txt b/invoice/invoice.temporary.txt
index fcb8f25..bf21c51 100644
--- a/invoice/invoice.temporary.txt
+++ b/invoice/invoice.temporary.txt
@@ -5,9 +5,7 @@ wie folgt zu berechnen:
{{{INVOICE_TABLE}}}
-Umsatzsteuer wird nicht erhoben, da der Verein Kreativität trifft
-Technik e.V. als Kleinunternehmen unter die Regelung des § 19
-Abs. 1 UStG fällt.
+{{{VAT}}}
Bei dieser Abrechnung handelt es sich lediglich um einen Zwischenstand.
Die Hauptrechnung wird einmal monatlich getrennt zugestellt und der
@@ -15,5 +13,5 @@ Gesamtbetrag wird dann vom angegebenen Bankkonto eingezogen.
Der Gesamtbetrag für den aktuellen Monat beträgt bisher: {{{SUM_MONTH}}} €
-Grüße aus dem Mainframe,
+Grüße aus dem {{{SPACENAME}}},
das Shop-System
diff --git a/invoice/pdf-template.txt b/invoice/pdf-template.txt
index bb603b9..9da8a9a 100644
--- a/invoice/pdf-template.txt
+++ b/invoice/pdf-template.txt
@@ -4,9 +4,9 @@ wir erlauben uns, Ihnen für den Verzehr von Speisen und Getränken {{{SUM}}}
Eine detaillierte Auflistung der einzelnen Posten befindet sich, mit genauer Zeitangabe des Einkaufs, auf den folgenden Seiten.
-Umsatzsteuer wird nicht erhoben, da der Verein Kreativität trifft Technik e.V. als Kleinunternehmen unter die Regelung des § 19 Abs. 1 UStG fällt.
+{{{VAT}}}
Der Gesamtbetrag wird in 10 Tagen von dem angegebenen Bankkonto eingezogen.
Mit freundlichen Grüßen
-Kreativität trifft Technik e.V.
+{{{ORGANIZATION}}}
diff --git a/invoice/treasurer.mail.txt b/invoice/treasurer.mail.txt
index 1e89991..6c9c6ac 100644
--- a/invoice/treasurer.mail.txt
+++ b/invoice/treasurer.mail.txt
@@ -5,4 +5,4 @@ and an additional csv-file in the attachment. The members'
invoices are for your files and the csv-file can be used
for automatic money collection.
--- KtT Shopsystem
+-- {{{SHORTNAME}}} Shopsystem
diff --git a/invoice/vat.html b/invoice/vat.html
new file mode 100644
index 0000000..245ec27
--- /dev/null
+++ b/invoice/vat.html
@@ -0,0 +1,4 @@
+
Umsatzsteuer wird nicht erhoben, da der Verein Kreativität trifft Technik e.V.
+als Kleinunternehmen unter die Regelung des
+§ 19 Abs. 1 UStG
+fällt.
diff --git a/invoice/vat.txt b/invoice/vat.txt
new file mode 100644
index 0000000..d1ee957
--- /dev/null
+++ b/invoice/vat.txt
@@ -0,0 +1,3 @@
+Umsatzsteuer wird nicht erhoben, da der Verein Kreativität trifft
+Technik e.V. als Kleinunternehmen unter die Regelung des § 19
+Abs. 1 UStG fällt.
diff --git a/src/curses-ui/Makefile b/src/curses-ui/Makefile
index 1ca68cf..bfdea51 100644
--- a/src/curses-ui/Makefile
+++ b/src/curses-ui/Makefile
@@ -1,7 +1,7 @@
all: curses-ui
@echo > /dev/null
-curses-ui: *.vala ../audio/audio-interface.vala ../scanner-session/scannersession-interface.vala
+curses-ui: *.vala ../audio/audio-interface.vala ../scanner-session/scannersession-interface.vala ../config/config-interface.vala
valac -X -w -o $@ --pkg curses -X -lncursesw --pkg posix --pkg gio-2.0 $^
clean:
diff --git a/src/curses-ui/main.vala b/src/curses-ui/main.vala
index 7020586..47be40a 100644
--- a/src/curses-ui/main.vala
+++ b/src/curses-ui/main.vala
@@ -62,6 +62,15 @@ public static int main(string[] args) {
scanner.msg.connect(msg_handler);
scanner.msg_overlay.connect(msg_overlay_handler);
+ /* get configuration */
+ Config config = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
+ var shopname = "--SHOPNAME--";
+ try {
+ shopname = config.get_string("GENERAL", "longname");
+ } catch(KeyFileError e) {
+ shopname = "Missing in Config";
+ }
+
ui.log(MessageType.INFO, "KtT Shop System has been started");
play("startup.ogg");
diff --git a/src/invoice/invoice.vala b/src/invoice/invoice.vala
index 1e6dd58..68710ca 100644
--- a/src/invoice/invoice.vala
+++ b/src/invoice/invoice.vala
@@ -32,6 +32,11 @@ public class InvoiceImplementation {
Database db;
PDFInvoice pdf;
string datadir;
+ string mailfromaddress;
+ string treasurermailaddress;
+ string shortname;
+ string spacename;
+ string vat;
public InvoiceImplementation() throws IOError, KeyFileError {
mailer = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", "/io/mainframe/shopsystem/mailer");
@@ -39,6 +44,11 @@ public class InvoiceImplementation {
pdf = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.InvoicePDF", "/io/mainframe/shopsystem/invoicepdf");
Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
datadir = cfg.get_string("INVOICE", "datadir");
+ mailfromaddress = cfg.get_string("MAIL", "mailfromaddress");
+ treasurermailaddress = cfg.get_string("MAIL", "treasurermailaddress");
+ shortname = cfg.get_string("GENERAL", "shortname");
+ spacename = cfg.get_string("GENERAL", "spacename");
+ vat = cfg.get_string("INVOICE", "vat");
}
public void send_invoice(bool temporary, int64 timestamp, int user) throws IOError, InvoicePDFError, DatabaseError {
@@ -67,9 +77,9 @@ public class InvoiceImplementation {
string treasurer_path = mailer.create_mail();
Mail treasurer_mail = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", treasurer_path);
- treasurer_mail.from = {"KtT Shopsystem", "shop@kreativitaet-trifft-technik.de"};
+ treasurer_mail.from = {shortname + " Shopsystem", mailfromaddress};
treasurer_mail.subject = mailtitle;
- treasurer_mail.add_recipient({"Schatzmeister", "shop-einzug@kreativitaet-trifft-technik.de"}, RecipientType.TO);
+ treasurer_mail.add_recipient({"Schatzmeister", treasurermailaddress}, RecipientType.TO);
var csvinvoicedata = "";
foreach(var userid in users) {
@@ -82,7 +92,7 @@ public class InvoiceImplementation {
var invoicedata = generate_invoice(temporary, timestamp, userid, invoiceid);
string mail_path = mailer.create_mail();
Mail mail = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", mail_path);
- mail.from = {"KtT Shopsystem", "shop@kreativitaet-trifft-technik.de"};
+ mail.from = {shortname + " Shopsystem", mailfromaddress};
mail.subject = mailtitle;
mail.add_recipient({@"$(userdata.firstname) $(userdata.lastname)", userdata.email}, RecipientType.TO);
@@ -135,9 +145,9 @@ public class InvoiceImplementation {
string treasurer_path = mailer.create_mail();
Mail treasurer_mail = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", treasurer_path);
- treasurer_mail.from = {"KtT Shopsystem", "shop@kreativitaet-trifft-technik.de"};
+ treasurer_mail.from = {shortname + " Shopsystem", mailfromaddress};
treasurer_mail.subject = mailtitle;
- treasurer_mail.add_recipient({"Schatzmeister", "shop-einzug@kreativitaet-trifft-technik.de"}, RecipientType.TO);
+ treasurer_mail.add_recipient({"Schatzmeister", treasurermailaddress}, RecipientType.TO);
var csvinvoicedata = "";
foreach(var userid in users) {
@@ -149,7 +159,7 @@ public class InvoiceImplementation {
string mail_path = mailer.create_mail();
Mail mail = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", mail_path);
- mail.from = {"KtT Shopsystem", "shop@kreativitaet-trifft-technik.de"};
+ mail.from = {shortname + " Shopsystem", mailfromaddress};
mail.subject = mailtitle;
mail.add_recipient({@"$(userdata.firstname) $(userdata.lastname)", userdata.email}, RecipientType.TO);
@@ -227,6 +237,8 @@ public class InvoiceImplementation {
throw new IOError.FAILED("Could not open invoice template: %s", e.message);
}
+ text = text.replace("{{{SHORTNAME}}}", shortname);
+
return text;
}
@@ -294,9 +306,26 @@ public class InvoiceImplementation {
text = text.replace("{{{ADDRESS}}}", address);
text = text.replace("{{{LASTNAME}}}", name);
+ text = text.replace("{{{SPACENAME}}}", spacename);
text = text.replace("{{{INVOICE_TABLE}}}", table);
text = text.replace("{{{SUM_MONTH}}}", "%d,%02d".printf(total_sum / 100, total_sum % 100));
+ if(vat == "yes") {
+ text = text.replace("{{{VAT}}}", "");
+ } else {
+ string vattext;
+ string vattextfilename;
+ vattextfilename = (type == MessageType.HTML) ? "vat.html" : "vat.txt";
+
+ try {
+ FileUtils.get_contents(datadir + "/" + vattextfilename, out vattext);
+ } catch(GLib.FileError e) {
+ throw new IOError.FAILED("Could not open VAT template: %s", e.message);
+ }
+
+ text = text.replace("{{{VAT}}}", vattext);
+ }
+
return text;
}
diff --git a/src/pdf-invoice/pdf-invoice.vala b/src/pdf-invoice/pdf-invoice.vala
index c50fe6c..6fd4383 100644
--- a/src/pdf-invoice/pdf-invoice.vala
+++ b/src/pdf-invoice/pdf-invoice.vala
@@ -15,6 +15,8 @@
[DBus (name = "io.mainframe.shopsystem.InvoicePDF")]
public class InvoicePDF {
+ Config cfg;
+
/* A4 sizes (in points, 72 DPI) */
private const double width = 595.27559; /* 210mm */
private const double height = 841.88976; /* 297mm */
@@ -60,8 +62,14 @@ public class InvoicePDF {
"Dezember"
};
+ string longname;
+ string vat;
+
public InvoicePDF(string datadir) {
this.datadir = datadir;
+ cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
+ longname = cfg.get_string("GENERAL", "longname");
+ vat = cfg.get_string("INVOICE", "vat");
}
private void render_svg(Cairo.Context ctx, string file) {
@@ -104,8 +112,7 @@ public class InvoicePDF {
ctx.set_font_size(8.45);
ctx.move_to(56.5, 142);
- /* TODO: get string from config file */
- ctx.show_text("Kreativität trifft Technik e.V., Bahnhofsplatz 10, 26122 Oldenburg");
+ ctx.show_text(cfg.get_string("INVOICE", "addressrow"));
/* actually LMRoman12 */
ctx.select_font_face("LMSans10", Cairo.FontSlant.NORMAL, Cairo.FontWeight.NORMAL);
@@ -194,7 +201,6 @@ public class InvoicePDF {
ctx.move_to(56.5, 323);
- /* TODO: get text from config file */
ctx.show_text(@"Rechnung Nr. $invoice_id");
ctx.restore();
@@ -224,8 +230,7 @@ public class InvoicePDF {
/* set page width */
layout.set_width((int) 140 * Pango.SCALE);
- /* TODO: get text from config file */
- var text = "Kreativität trifft Technik e.V.\nAmtsgericht Oldenburg VR 201044\n\nHackspace „Mainframe“\nFabLab „Fab-O-Lab“\nSchnittstelle „Schnittstelle“\n\nBahnhofsplatz 10 • 26122 Oldenburg";
+ var text = cfg.get_string("INVOICE", "footer1");
/* write invoice date */
layout.set_markup(text, text.length);
@@ -261,8 +266,7 @@ public class InvoicePDF {
/* set page width */
layout.set_width((int) 190 * Pango.SCALE);
- /* TODO: get text from config file */
- var text = "Mail: vorstand@kreativitaet-trifft-technik.de\nWeb: www.kreativitaet-trifft-technik.de\n\n\n\nBGB-Vorstand:\nPatrick Günther, Michael Pensler, Jan Janssen";
+ var text = cfg.get_string("INVOICE", "footer2");
/* write invoice date */
layout.set_markup(text, text.length);
@@ -298,8 +302,7 @@ public class InvoicePDF {
/* set page width */
layout.set_width((int) 150 * Pango.SCALE);
- /* TODO: get text from config file */
- var text = "Raiffeisenbank Oldenburg\nIBAN: DE34 2806 0228 0037 0185 00\nBIC: GENODEF1OL2\n\n\nFinanzamt Oldenburg\nAls gemeinnützig anerkannt.\nSteuer Nr.: 64/220/18413";
+ var text = cfg.get_string("INVOICE", "footer3");
/* write invoice date */
layout.set_markup(text, text.length);
@@ -362,6 +365,22 @@ public class InvoicePDF {
text = text.replace("{{{ADDRESS}}}", address);
text = text.replace("{{{LASTNAME}}}", invoice_recipient.lastname);
text = text.replace("{{{SUM}}}", @"$sum");
+ text = text.replace("{{{ORGANIZATION}}}", longname);
+
+ if(vat == "yes") {
+ text = text.replace("{{{VAT}}}", "");
+ } else {
+ string vattext;
+
+ try {
+ FileUtils.get_contents(datadir + "/" + "vat.txt", out vattext);
+ } catch(GLib.FileError e) {
+ throw new IOError.FAILED("Could not open VAT template: %s", e.message);
+ }
+
+ text = text.replace("{{{VAT}}}", vattext);
+ }
+
layout.set_markup(text, text.length);
} catch(GLib.FileError e) {
error("File Error: %s\n", e.message);
diff --git a/src/web/web.vala b/src/web/web.vala
index ea0c667..62cfb04 100644
--- a/src/web/web.vala
+++ b/src/web/web.vala
@@ -15,12 +15,15 @@
public class WebServer {
private Soup.Server srv;
+ private string longname;
+ private string shortname;
void handler_default(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) {
try {
var l = new WebSession(server, msg, path, query, client);
var t = new WebTemplate("index.html", l);
- t.replace("TITLE", "KtT Shop System");
+ t.replace("TITLE", shortname + " Shop System");
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("home");
msg.set_response("text/html", Soup.MemoryUse.COPY, t.data);
msg.set_status(200);
@@ -39,7 +42,8 @@ public class WebServer {
var l = new WebSession(server, msg, path, query, client);
l.logout();
var t = new WebTemplate("logout.html", l);
- t.replace("TITLE", "KtT Shop System");
+ t.replace("TITLE", shortname + " Shop System");
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("home");
msg.set_response("text/html", Soup.MemoryUse.COPY, t.data);
msg.set_status(200);
@@ -96,7 +100,8 @@ public class WebServer {
}
var t = new WebTemplate("users/index.html", session);
- t.replace("TITLE", "KtT Shop System: User");
+ t.replace("TITLE", shortname + " Shop System: User");
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("users");
var data = "";
foreach(var m in db.get_member_ids()) {
@@ -130,7 +135,8 @@ public class WebServer {
}
var t = new WebTemplate("users/import-pgp.html", session);
- t.replace("TITLE", "KtT Shop System: PGP Key Import");
+ t.replace("TITLE", shortname + " Shop System: PGP Key Import");
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("users");
Soup.Buffer filedata;
@@ -182,7 +188,8 @@ public class WebServer {
return;
}
var t = new WebTemplate("users/import.html", session);
- t.replace("TITLE", "KtT Shop System: User Import");
+ t.replace("TITLE", shortname + " Shop System: User Import");
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("users");
Soup.Buffer filedata;
@@ -327,7 +334,8 @@ public class WebServer {
return;
}
var t = new WebTemplate("users/entry.html", session);
- t.replace("TITLE", "KtT Shop System: User Info %llu".printf(id));
+ t.replace("TITLE", shortname + " Shop System: User Info %llu".printf(id));
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("users");
var userinfo = db.get_user_info(id);
@@ -431,7 +439,8 @@ public class WebServer {
return;
}
var t = new WebTemplate("users/invoice.html", l);
- t.replace("TITLE", "KtT Shop System: User Invoice %llu".printf(id));
+ t.replace("TITLE", shortname + " Shop System: User Invoice %llu".printf(id));
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("users");
/* years, in which something has been purchased by the user */
@@ -553,7 +562,8 @@ public class WebServer {
try {
var l = new WebSession(server, msg, path, query, client);
var t = new WebTemplate("products/index.html", l);
- t.replace("TITLE", "KtT Shop System: Product List");
+ t.replace("TITLE", shortname + " Shop System: Product List");
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("products");
string table = "";
@@ -590,7 +600,8 @@ public class WebServer {
try {
var l = new WebSession(server, msg, path, query, client);
var t = new WebTemplate("products/bestbefore.html", l);
- t.replace("TITLE", "KtT Shop System: Best Before List");
+ t.replace("TITLE", shortname + " Shop System: Best Before List");
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("products");
string table = "";
@@ -645,7 +656,8 @@ public class WebServer {
try {
var l = new WebSession(server, msg, path, query, client);
var t = new WebTemplate("products/entry.html", l);
- t.replace("TITLE", "KtT Shop System: Product %llu".printf(id));
+ t.replace("TITLE", shortname + " Shop System: Product %llu".printf(id));
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("products");
/* ean */
@@ -725,7 +737,8 @@ public class WebServer {
try {
var session = new WebSession(server, msg, path, query, client);
var template = new WebTemplate("products/new.html", session);
- template.replace("TITLE", "KtT Shop System: New Product");
+ template.replace("TITLE", shortname + " Shop System: New Product");
+ template.replace("SHORTNAME", shortname);
template.menu_set_active("products");
if(!session.superuser && !session.auth_products) {
@@ -781,7 +794,8 @@ public class WebServer {
}
var template = new WebTemplate("products/restock.html", session);
- template.replace("TITLE", "KtT Shop System: Restock Product %llu".printf(id));
+ template.replace("TITLE", shortname + " Shop System: Restock Product %llu".printf(id));
+ template.replace("SHORTNAME", shortname);
template.replace("NAME", db.get_product_name(id));
template.menu_set_active("products");
@@ -839,7 +853,7 @@ public class WebServer {
}
var template = new WebTemplate("products/newprice.html", session);
- template.replace("TITLE", "KtT Shop System: New Price for Product %llu".printf(id));
+ template.replace("TITLE", shortname + " Shop System: New Price for Product %llu".printf(id));
template.replace("NAME", db.get_product_name(id));
template.menu_set_active("products");
@@ -878,7 +892,8 @@ public class WebServer {
try {
var l = new WebSession(server, msg, path, query, client);
var t = new WebTemplate("aliases/index.html", l);
- t.replace("TITLE", "KtT Shop System: Alias List");
+ t.replace("TITLE", shortname + " Shop System: Alias List");
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("aliases");
string table = "";
@@ -910,7 +925,8 @@ public class WebServer {
try {
var session = new WebSession(server, msg, path, query, client);
var template = new WebTemplate("aliases/new.html", session);
- template.replace("TITLE", "KtT Shop System: New Alias");
+ template.replace("TITLE", shortname + " Shop System: New Alias");
+ template.replace("SHORTNAME", shortname);
template.menu_set_active("aliases");
if(!session.superuser && !session.auth_products) {
@@ -958,7 +974,8 @@ public class WebServer {
try {
var l = new WebSession(server, msg, path, query, client);
var t = new WebTemplate("stats/index.html", l);
- t.replace("TITLE", "KtT Shop System: Statistics");
+ t.replace("TITLE", shortname + " Shop System: Statistics");
+ t.replace("SHORTNAME", shortname);
t.menu_set_active("stats");
var stats = db.get_stats_info();
@@ -991,7 +1008,8 @@ public class WebServer {
var t = new WebTemplate("stats/stock.html", l);
string data = db.get_stats_stock().json;
t.replace("DATA", data);
- t.replace("TITLE", "KtT Shop System: Statistics: Stock");
+ t.replace("SHORTNAME", shortname);
+ t.replace("TITLE", shortname + " Shop System: Statistics: Stock");
t.menu_set_active("stats");
msg.set_response("text/html", Soup.MemoryUse.COPY, t.data);
msg.set_status(200);
@@ -1007,7 +1025,8 @@ public class WebServer {
var t = new WebTemplate("stats/profit_per_day.html", l);
string data = db.get_stats_profit_per_day().json;
t.replace("DATA", data);
- t.replace("TITLE", "KtT Shop System: Statistics: Profit");
+ t.replace("SHORTNAME", shortname);
+ t.replace("TITLE", shortname + " Shop System: Statistics: Profit");
t.menu_set_active("stats");
msg.set_response("text/html", Soup.MemoryUse.COPY, t.data);
msg.set_status(200);
@@ -1023,7 +1042,8 @@ public class WebServer {
var t = new WebTemplate("stats/profit_per_weekday.html", l);
string data = db.get_stats_profit_per_weekday().json;
t.replace("DATA", data);
- t.replace("TITLE", "KtT Shop System: Statistics: Profit/Weekday");
+ t.replace("SHORTNAME", shortname);
+ t.replace("TITLE", shortname + " Shop System: Statistics: Profit/Weekday");
t.menu_set_active("stats");
msg.set_response("text/html", Soup.MemoryUse.COPY, t.data);
msg.set_status(200);
@@ -1039,7 +1059,8 @@ public class WebServer {
var t = new WebTemplate("stats/profit_per_product.html", l);
string data = db.get_stats_profit_per_products().json;
t.replace("DATA", data);
- t.replace("TITLE", "KtT Shop System: Statistics: Profit/Product");
+ t.replace("SHORTNAME", shortname);
+ t.replace("TITLE", shortname + " Shop System: Statistics: Profit/Product");
t.menu_set_active("stats");
msg.set_response("text/html", Soup.MemoryUse.COPY, t.data);
msg.set_status(200);
@@ -1148,7 +1169,8 @@ public class WebServer {
try {
var session = new WebSession(server, msg, path, query, client);
var template = new WebTemplate("errors/todo.html", session);
- template.replace("TITLE", "KtT Shop System: ToDo");
+ template.replace("TITLE", shortname + " Shop System: ToDo");
+ template.replace("SHORTNAME", shortname);
template.menu_set_active("");
msg.set_response("text/html", Soup.MemoryUse.COPY, template.data);
msg.set_status(200);
@@ -1193,7 +1215,8 @@ public class WebServer {
hist += "\n";
}
- template.replace("TITLE", "KtT Shop System: Cashbox");
+ template.replace("TITLE", shortname + " Shop System: Cashbox");
+ template.replace("SHORTNAME", shortname);
template.replace("CASHBOX_STATUS", status);
template.replace("CASHBOX_HISTORY", hist);
template.menu_set_active("cashbox");
@@ -1219,7 +1242,8 @@ public class WebServer {
}
var template = new WebTemplate("cashbox/add.html", session);
- template.replace("TITLE", "KtT Shop System: Cashbox Balance");
+ template.replace("TITLE", shortname + " Shop System: Cashbox Balance");
+ template.replace("SHORTNAME", shortname);
template.menu_set_active("cashbox");
bool error = false;
@@ -1291,7 +1315,8 @@ public class WebServer {
try {
var session = new WebSession(server, msg, path, query, client);
var template = new WebTemplate("cashbox/selection.html", session);
- template.replace("TITLE", "KtT Shop System: Cashbox Detail");
+ template.replace("TITLE", shortname + " Shop System: Cashbox Detail");
+ template.replace("SHORTNAME", shortname);
template.menu_set_active("cashbox");
msg.set_response("text/html", Soup.MemoryUse.COPY, template.data);
msg.set_status(200);
@@ -1361,9 +1386,9 @@ public class WebServer {
}
var template = new WebTemplate("cashbox/detail.html", session);
- template.replace("TITLE", "KtT Shop System: Cashbox Detail");
+ template.replace("TITLE", shortname + " Shop System: Cashbox Detail");
template.menu_set_active("cashbox");
-
+ template.replace("SHORTNAME", shortname);
template.replace("DATE", start.format("%B %Y"));
template.replace("DEBIT", debit.to_string());
template.replace("LOSS", loss.to_string());
@@ -1387,6 +1412,16 @@ public class WebServer {
}
public WebServer(uint port = 8080, TlsCertificate? cert = null) throws Error {
+ /* get configuration */
+ Config config = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
+ try {
+ longname = config.get_string("GENERAL", "longname");
+ shortname = config.get_string("GENERAL", "shortname");
+ } catch(KeyFileError e) {
+ longname = "Logname Missing in Config";
+ shortname = "Shortname Missing in Config";
+ }
+
srv = new Soup.Server("tls-certificate", cert);
Soup.ServerListenOptions options = 0;
diff --git a/templates/index.html b/templates/index.html
index c7782bd..bee2d95 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1 +1 @@
-
";
}
t.replace("DATA", table);
--
cgit v1.2.3
From fe7729489370800a1a24ad4a0b6adf7d95979daf Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Sat, 13 Jan 2018 14:26:09 +0100
Subject: scanner-session: add play_audio()
---
src/audio/audio-interface.vala | 8 ++++++++
src/scanner-session/scannersession.vala | 20 ++++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/src/audio/audio-interface.vala b/src/audio/audio-interface.vala
index fe13af5..12bfae4 100644
--- a/src/audio/audio-interface.vala
+++ b/src/audio/audio-interface.vala
@@ -22,3 +22,11 @@ public interface AudioPlayer : Object {
public abstract string[] get_user_themes() throws IOError;
public abstract void play_user(string theme, string type) throws IOError;
}
+
+public enum AudioType {
+ ERROR,
+ LOGIN,
+ LOGOUT,
+ PURCHASE,
+ INFO
+}
diff --git a/src/scanner-session/scannersession.vala b/src/scanner-session/scannersession.vala
index 9638ef5..eeb02e3 100644
--- a/src/scanner-session/scannersession.vala
+++ b/src/scanner-session/scannersession.vala
@@ -78,6 +78,26 @@ public class ScannerSessionImplementation {
return true;
}
+ private void play_audio(AudioType audioType){
+ switch (audioType) {
+ case AudioType.ERROR:
+ audio.play_system("error.ogg");
+ break;
+ case AudioType.LOGIN:
+ audio.play_user(theme, "login");
+ break;
+ case AudioType.LOGOUT:
+ audio.play_user(theme, "logout");
+ break;
+ case AudioType.PURCHASE:
+ audio.play_user(theme, "purchase");
+ break;
+ case AudioType.INFO:
+ audio.play_user(theme, "login");
+ break;
+ }
+ }
+
private void handle_barcode(string scannerdata) {
try {
stdout.printf("scannerdata: %s\n", scannerdata);
--
cgit v1.2.3
From c2bc2327238fc6833be861379e214661bfb391ae Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Sat, 13 Jan 2018 14:28:02 +0100
Subject: scanner-session: add getCodeType()
---
src/scanner-session/scannersession-interface.vala | 9 +++++++++
src/scanner-session/scannersession.vala | 22 ++++++++++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/src/scanner-session/scannersession-interface.vala b/src/scanner-session/scannersession-interface.vala
index 89498df..9f989e7 100644
--- a/src/scanner-session/scannersession-interface.vala
+++ b/src/scanner-session/scannersession-interface.vala
@@ -24,3 +24,12 @@ public enum MessageType {
WARNING,
ERROR
}
+
+public enum ScannerSessionCodeType {
+ USER,
+ GUEST,
+ UNDO,
+ LOGOUT,
+ EAN,
+ UNKNOWN
+}
diff --git a/src/scanner-session/scannersession.vala b/src/scanner-session/scannersession.vala
index eeb02e3..23db939 100644
--- a/src/scanner-session/scannersession.vala
+++ b/src/scanner-session/scannersession.vala
@@ -78,6 +78,28 @@ public class ScannerSessionImplementation {
return true;
}
+ private ScannerSessionCodeType getCodeType(string scannerdata){
+ if(scannerdata.has_prefix("USER ")){
+ return ScannerSessionCodeType.USER;
+ } else if(scannerdata == "GUEST") {
+ return ScannerSessionCodeType.GUEST;
+ } else if(scannerdata == "UNDO") {
+ return ScannerSessionCodeType.UNDO;
+ } else if(scannerdata == "LOGOUT") {
+ return ScannerSessionCodeType.LOGOUT;
+ } else {
+ //Handle EAN Code
+ uint64 id = 0;
+ scannerdata.scanf("%llu", out id);
+
+ /* check if scannerdata has valid format */
+ if(scannerdata != "%llu".printf(id) && scannerdata != "%08llu".printf(id) && scannerdata != "%013llu".printf(id)) {
+ return ScannerSessionCodeType.UNKNOWN;
+ }
+ return ScannerSessionCodeType.EAN;
+ }
+ }
+
private void play_audio(AudioType audioType){
switch (audioType) {
case AudioType.ERROR:
--
cgit v1.2.3
From 297d9ca5b7476a6a51f9837741b8cebec484de0d Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Fri, 5 Jan 2018 16:57:47 +0100
Subject: scanner-session: initial state machine implementation
---
src/scanner-session/scannersession-interface.vala | 12 +
src/scanner-session/scannersession.vala | 311 +++++++++++++---------
2 files changed, 199 insertions(+), 124 deletions(-)
diff --git a/src/scanner-session/scannersession-interface.vala b/src/scanner-session/scannersession-interface.vala
index 9f989e7..ff2c2e9 100644
--- a/src/scanner-session/scannersession-interface.vala
+++ b/src/scanner-session/scannersession-interface.vala
@@ -33,3 +33,15 @@ public enum ScannerSessionCodeType {
EAN,
UNKNOWN
}
+
+public enum ScannerSessionState {
+ READY,
+ USER
+}
+
+public struct ScannerResult {
+ public MessageType type;
+ public string message;
+ public AudioType audioType;
+ public string nextScannerdata;
+}
diff --git a/src/scanner-session/scannersession.vala b/src/scanner-session/scannersession.vala
index 23db939..f19d94c 100644
--- a/src/scanner-session/scannersession.vala
+++ b/src/scanner-session/scannersession.vala
@@ -26,6 +26,8 @@ public class ScannerSessionImplementation {
private InputDevice dev;
private Cli cli;
+ private ScannerSessionState state = ScannerSessionState.READY;
+ private DetailedProduct[] shoppingCard = {};
public signal void msg(MessageType type, string message);
public signal void msg_overlay(string title, string message);
@@ -51,10 +53,6 @@ public class ScannerSessionImplementation {
msg(type, message);
}
- private void logout() {
- logged_in = false;
- }
-
private bool login(int user) throws IOError {
this.user = user;
try {
@@ -120,141 +118,206 @@ public class ScannerSessionImplementation {
}
}
- private void handle_barcode(string scannerdata) {
- try {
- stdout.printf("scannerdata: %s\n", scannerdata);
- if(interpret(scannerdata))
- dev.blink(1000);
- } catch(IOError e) {
- send_message(MessageType.ERROR, "IOError: %s", e.message);
- } catch(DatabaseError e) {
- send_message(MessageType.ERROR, "DatabaseError: %s", e.message);
+ private ScannerResult handleReadyState(string scannerdata) throws DatabaseError, IOError{
+ ScannerSessionCodeType codeType = getCodeType(scannerdata);
+ ScannerResult scannerResult = ScannerResult();
+ switch (codeType) {
+ case ScannerSessionCodeType.USER:
+ int32 userid = int.parse(scannerdata.substring(5));
+ if(login(userid)) {
+ scannerResult.type = MessageType.INFO;
+ scannerResult.message = "Login: %s (%d)".printf(name, user);
+ scannerResult.audioType = AudioType.LOGIN;
+ shoppingCard = {};
+ state = ScannerSessionState.USER;
+ } else {
+ scannerResult.type = MessageType.ERROR;
+ scannerResult.message = "Login failed (User ID = %d)".printf(userid);
+ scannerResult.audioType = AudioType.ERROR;
+ state = ScannerSessionState.READY;
+ }
+ return scannerResult;
+ case ScannerSessionCodeType.GUEST:
+ if(login(0)) {
+ scannerResult.type = MessageType.INFO;
+ scannerResult.message = "Login as GUEST";
+ scannerResult.audioType = AudioType.LOGIN;
+ shoppingCard = {};
+ state = ScannerSessionState.USER;
+ } else {
+ scannerResult.type = MessageType.ERROR;
+ scannerResult.message = "Login failed as GUEST";
+ scannerResult.audioType = AudioType.ERROR;
+ state = ScannerSessionState.READY;
+ }
+ return scannerResult;
+ case ScannerSessionCodeType.EAN:
+ uint64 ean = 0;
+ scannerdata.scanf("%llu", out ean);
+ var p = DetailedProduct();
+ try {
+ p = db.get_product_for_ean(ean);
+ } catch(IOError e) {
+ scannerResult.type = MessageType.ERROR;
+ scannerResult.message = "Internal Error!";
+ scannerResult.audioType = AudioType.ERROR;
+ return scannerResult;
+ } catch(DatabaseError e) {
+ if(e is DatabaseError.PRODUCT_NOT_FOUND) {
+ scannerResult.type = MessageType.ERROR;
+ scannerResult.message = "Error: unknown product: %llu".printf(ean);
+ scannerResult.audioType = AudioType.ERROR;
+ } else {
+ scannerResult.type = MessageType.ERROR;
+ scannerResult.message = "Error: %s".printf(e.message);
+ scannerResult.audioType = AudioType.ERROR;
+ }
+ return scannerResult;
+ }
+
+ var mprice = p.memberprice;
+ var gprice = p.guestprice;
+ var pname = p.name;
+
+ scannerResult.type = MessageType.INFO;
+ scannerResult.message = @"article info: $pname (Member: $mprice €, Guest: $gprice €)";
+ scannerResult.audioType = AudioType.ERROR;
+ state = ScannerSessionState.READY;
+ return scannerResult;
+ default:
+ state = ScannerSessionState.READY;
+ return scannerResult;
}
}
- private bool interpret(string scannerdata) throws DatabaseError, IOError {
- if(scannerdata.has_prefix("USER ")) {
- string str_id = scannerdata.substring(5);
- int32 id = int.parse(str_id);
-
- /* check if scannerdata has valid format */
- if(scannerdata != "USER %d".printf(id)) {
- audio.play_system("error.ogg");
- send_message(MessageType.ERROR, "Invalid User ID: %s", scannerdata);
- return false;
- }
+ private ScannerResult handleUserState(string scannerdata) throws DatabaseError, IOError {
+ ScannerSessionCodeType codeType = getCodeType(scannerdata);
+ ScannerResult scannerResult = ScannerResult();
+ switch (codeType) {
+ case ScannerSessionCodeType.EAN:
+ uint64 ean = 0;
+ scannerdata.scanf("%llu", out ean);
+ var p = DetailedProduct();
+ try {
+ p = db.get_product_for_ean(ean);
+ } catch(IOError e) {
+ scannerResult.type = MessageType.ERROR;
+ scannerResult.message = "Internal Error!";
+ scannerResult.audioType = AudioType.ERROR;
+ return scannerResult;
+ } catch(DatabaseError e) {
+ if(e is DatabaseError.PRODUCT_NOT_FOUND) {
+ scannerResult.type = MessageType.ERROR;
+ scannerResult.message = "Error: unknown product: %llu".printf(ean);
+ scannerResult.audioType = AudioType.ERROR;
+ } else {
+ scannerResult.type = MessageType.ERROR;
+ scannerResult.message = "Error: %s".printf(e.message);
+ scannerResult.audioType = AudioType.ERROR;
+ }
+ return scannerResult;
+ }
- if(logged_in) {
- send_message(MessageType.WARNING, "Last user forgot to logout");
- logout();
- }
+ shoppingCard += p;
- if(login(id)) {
- audio.play_user(theme, "login");
- send_message(MessageType.INFO, "Login: %s (%d)", name, user);
- return true;
- } else {
- audio.play_system("error.ogg");
- send_message(MessageType.ERROR, "Login failed (User ID = %d)", id);
- return false;
- }
- } else if(scannerdata == "GUEST") {
- if(logged_in) {
- send_message(MessageType.WARNING, "Last user forgot to logout");
- logout();
- }
+ Price price = p.memberprice;
- if(login(0)) {
- audio.play_user(theme, "login");
- send_message(MessageType.INFO, "Login: %s (%d)", name, user);
- return true;
- } else {
- audio.play_system("error.ogg");
- send_message(MessageType.ERROR, "Login failed (User ID = 0)");
- return false;
- }
- } else if(scannerdata == "UNDO") {
- if(!logged_in) {
- audio.play_system("error.ogg");
- send_message(MessageType.ERROR, "Can't undo if not logged in!");
- return false;
- } else {
- string product = db.undo(user);
+ if(user == 0){
+ price = p.guestprice;
+ }
- if(product != "") {
- audio.play_user(theme, "purchase");
- send_message(MessageType.INFO, "Removed purchase of %s", product);
- return true;
+ scannerResult.type = MessageType.INFO;
+ scannerResult.message = @"article added to shopping card: $(p.name) ($price €)";
+ scannerResult.audioType = AudioType.PURCHASE;
+ state = ScannerSessionState.USER;
+ break;
+ case ScannerSessionCodeType.UNDO:
+ if(shoppingCard.length > 0){
+ var removedProduct = shoppingCard[shoppingCard.length-1];
+ shoppingCard = shoppingCard[0:shoppingCard.length-1];
+ scannerResult.type = MessageType.INFO;
+ scannerResult.message = @"removed last Item from Shopping Cart: $(removedProduct.name)";
+ scannerResult.audioType = AudioType.INFO;
} else {
- audio.play_user(theme, "error");
- send_message(MessageType.ERROR, "Couldn't undo last purchase!");
- return false;
+ scannerResult.type = MessageType.INFO;
+ scannerResult.message = @"No more Items on your Shopping Cart";
+ scannerResult.audioType = AudioType.ERROR;
}
- }
- } else if(scannerdata == "LOGOUT") {
- if(logged_in) {
- audio.play_user(theme, "logout");
- send_message(MessageType.INFO, "Logout!");
- logout();
- return true;
- }
-
- return false;
- } else {
- uint64 id = 0;
- scannerdata.scanf("%llu", out id);
-
- /* check if scannerdata has valid format */
- if(scannerdata != "%llu".printf(id) && scannerdata != "%08llu".printf(id) && scannerdata != "%013llu".printf(id)) {
- audio.play_user(theme, "error");
- send_message(MessageType.ERROR, "invalid product: %s", scannerdata);
- return false;
- }
+ break;
+ case ScannerSessionCodeType.LOGOUT:
+ scannerResult = logout();
+ break;
+ case ScannerSessionCodeType.USER:
+ case ScannerSessionCodeType.GUEST:
+ //Logout alten User und akrtikel kaufen
+ scannerResult = logout();
+ scannerResult.nextScannerdata = scannerdata;
+ break;
+ }
- string name = "unknown product";
+ return scannerResult;
+ }
- try {
- id = db.ean_alias_get(id);
- name = db.get_product_name(id);
- } catch(IOError e) {
- audio.play_user(theme, "error");
- send_message(MessageType.ERROR, "Internal Error!");
- return false;
- } catch(DatabaseError e) {
- if(e is DatabaseError.PRODUCT_NOT_FOUND) {
- audio.play_user(theme, "error");
- var msg = "Error: unknown product: %llu".printf(id);
- send_message(MessageType.ERROR, msg);
- msg_overlay("Attention", msg);
- } else {
- audio.play_user(theme, "error");
- send_message(MessageType.ERROR, "Error: %s", e.message);
- }
- return false;
+ private ScannerResult buyShoppingCard() {
+ ScannerResult scannerResult = ScannerResult();
+ uint8 amountOfItems = 0;
+ Price totalPrice = 0;
+ uint8 i = 0;
+ DetailedProduct p = DetailedProduct();
+ for(i = 0; i < shoppingCard.length; i++) {
+ p = shoppingCard[i];
+ db.buy(user, p.ean);
+ amountOfItems++;
+ Price price = p.memberprice;
+ if(user == 0) {
+ price = p.guestprice;
}
+ totalPrice += price;
+ }
+ scannerResult.type = MessageType.INFO;
+ scannerResult.message = @"$name bought $amountOfItems items for $totalPrice €";
+ scannerResult.audioType = AudioType.INFO;
+ return scannerResult;
+ }
- if(!logged_in) {
- var mprice = db.get_product_price(1, id);
- var gprice = db.get_product_price(0, id);
- var msg = @"article info: $name (Member: $mprice €, Guest: $gprice €)";
- audio.play_system("error.ogg");
- send_message(MessageType.INFO, msg);
- send_message(MessageType.ERROR, "Login required for purchase!");
- msg_overlay("Attention", "%s\nLogin required for purchase!".printf(msg));
+ private void handle_barcode(string scannerdata) {
+ try {
+ stdout.printf("scannerdata: %s\n", scannerdata);
+ if(interpret(scannerdata))
+ dev.blink(1000);
+ } catch(IOError e) {
+ send_message(MessageType.ERROR, "IOError: %s", e.message);
+ } catch(DatabaseError e) {
+ send_message(MessageType.ERROR, "DatabaseError: %s", e.message);
+ }
+ }
- return false;
- }
+ private bool interpret(string scannerdata) throws DatabaseError, IOError {
+ ScannerResult scannerResult = ScannerResult();
+ switch (state) {
+ case ScannerSessionState.READY:
+ scannerResult = handleReadyState(scannerdata);
+ break;
+ case ScannerSessionState.USER:
+ scannerResult = handleUserState(scannerdata);
+ break;
+ }
- if(db.buy(user, id)) {
- var price = db.get_product_price(user, id);
- audio.play_user(theme, "purchase");
- send_message(MessageType.INFO, @"article bought: $name ($price €)");
- return true;
- } else {
- audio.play_user(theme, "error");
- send_message(MessageType.ERROR, "purchase failed!");
- return false;
- }
+ play_audio(scannerResult.audioType);
+ send_message(scannerResult.type, scannerResult.message);
+ if(scannerResult.nextScannerdata != null){
+ interpret(scannerResult.nextScannerdata);
}
+ return true;
}
+
+ private ScannerResult logout() {
+ ScannerResult scannerResult = ScannerResult();
+ scannerResult = buyShoppingCard();
+ logged_in = false;
+ state = ScannerSessionState.READY;
+ return scannerResult;
+ }
+
+
}
--
cgit v1.2.3
From 0f7e3ef2d635b9561a917a95570207a15495a7a4 Mon Sep 17 00:00:00 2001
From: Sebastian Reichel
Date: Wed, 27 Jun 2018 21:49:51 +0200
Subject: database: remove correct binary on clean
---
src/database/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/database/Makefile b/src/database/Makefile
index fac0f08..986f1f6 100644
--- a/src/database/Makefile
+++ b/src/database/Makefile
@@ -5,6 +5,6 @@ database: main.vala database.vala db-interface.vala ../config/config-interface.v
valac -X -w -o $@ --pkg sqlite3 --pkg gee-0.8 --pkg gio-2.0 $^
clean:
- rm -rf db
+ rm -rf database
.PHONY: all clean
--
cgit v1.2.3
From ab2ea719ee4a86e082f8d205c1d33a048e1cfe57 Mon Sep 17 00:00:00 2001
From: Sebastian Reichel
Date: Wed, 27 Jun 2018 21:56:42 +0200
Subject: configure: drop gdk dependency
Gdk is needed for building rsvg. We are fine without having all gdk
headers available.
---
configure | 1 -
1 file changed, 1 deletion(-)
diff --git a/configure b/configure
index 4fde129..cdadf59 100755
--- a/configure
+++ b/configure
@@ -15,7 +15,6 @@ check_dependencies() {
echo "Checking library dependencies:"
check_pkg_version ncursesw 5.9 "force"
- check_pkg_version gdk-2.0 2.24 "force"
check_pkg_version gee-0.8 0.6 "force"
check_pkg_version gio-2.0 2.36 "force"
check_pkg_version gmime-3.0 3.0 "force"
--
cgit v1.2.3
From 764c39f4facfe42d926cd952dcf0f7e2768097a8 Mon Sep 17 00:00:00 2001
From: mm-chen
Date: Mon, 9 Apr 2018 21:29:04 +0200
Subject: Display on/off
Turns the display off and on via MQTT
---
README.md | 4 ++++
example.cfg | 5 +++++
src/display-on-off/display-on-off.sh | 26 ++++++++++++++++++++++++++
systemd/shopsystem-display-on-off.service | 12 ++++++++++++
4 files changed, 47 insertions(+)
create mode 100755 src/display-on-off/display-on-off.sh
create mode 100644 systemd/shopsystem-display-on-off.service
diff --git a/README.md b/README.md
index 830ae5b..9a0dbf6 100644
--- a/README.md
+++ b/README.md
@@ -84,6 +84,10 @@ but you need to modify a few things.
`sqlite3 shop.db "INSERT INTO categories (name) VALUES ('Getränke')";`
`sqlite3 shop.db "INSERT INTO supplier (name,city,postal_code,street,phone,website) VALUES ('Demo Lieferant','Musterstadt','12345','Musterstraße 5','+49 1234 56789','https://www.ktt.de');"`
+=== Display on / off via MQTT ===
+
+You can control display power via MQTT by configuring the MQTT settings (i.e. BROKER, TOPIC) in the config file.
+
== Customize Your Shop ==
Edit the Logo in the logo.txt File.
diff --git a/example.cfg b/example.cfg
index 27172b1..65056d8 100644
--- a/example.cfg
+++ b/example.cfg
@@ -32,3 +32,8 @@ footer2 = Raiffeisenbank Oldenburg\nIBAN: DE34 2806 0228 0037 0185 00\nBI
footer3 = Mail: vorstand@kreativitaet-trifft-technik.de\nWeb: www.kreativitaet-trifft-technik.de\n\n\n\nBGB-Vorstand:\nPatrick Günther, Jan Janssen, Andre Schäfer, Lars Hüsemann
[JVEREIN]
membership_number = intern
+[MQTT]
+broker = mqtt.server.exaple.com
+topic = /exaple/topic
+displayOn = open
+displayOff = close
diff --git a/src/display-on-off/display-on-off.sh b/src/display-on-off/display-on-off.sh
new file mode 100755
index 0000000..23ab10a
--- /dev/null
+++ b/src/display-on-off/display-on-off.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+function getMqttConfig
+{
+ echo `busctl --system call io.mainframe.shopsystem.Config /io/mainframe/shopsystem/config io.mainframe.shopsystem.Config GetString ss MQTT $1 | sed -s "s;s ;;"`
+}
+
+BROKER=$(getMqttConfig broker)
+TOPIC=$(getMqttConfig topic)
+ON=$(getMqttConfig displayOn)
+OFF=$(getMqttConfig displayOff)
+
+mosquitto_sub -h $BROKER -t $TOPIC | while read RAW_DATA
+do
+ case $RAW_DATA in
+ $ON)
+ vbetool dpms on
+ ;;
+ $OFF)
+ vbetool dpms off
+ ;;
+ *)
+ #vbetool dpms on
+ ;;
+ esac
+done
diff --git a/systemd/shopsystem-display-on-off.service b/systemd/shopsystem-display-on-off.service
new file mode 100644
index 0000000..dfa6f0c
--- /dev/null
+++ b/systemd/shopsystem-display-on-off.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Display on-off via MQTT
+After=systemd-user-sessions.service
+
+[Service]
+Type=simple
+ExecStart=/home/shop/serial-barcode-scanner/src/display-on-off/display-on-off.sh
+Restart=always
+RestartSec=10
+
+[Install]
+WantedBy=multi-user.target
--
cgit v1.2.3
From c70d626e473c8cbdbd87125a256ca221c0c3fa23 Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Mon, 12 Feb 2018 23:16:11 +0100
Subject: Add RFID support
---
example.cfg | 3 +-
sql/tables.sql | 1 +
src/database/database.vala | 46 +++++++++++++++++++++++
src/database/db-interface.vala | 6 +++
src/input-device/main.vala | 13 +++++--
src/scanner-session/scannersession-interface.vala | 1 +
src/scanner-session/scannersession.vala | 20 +++++++---
src/serial-device/main.vala | 6 +--
src/web/csv.vala | 10 +++++
src/web/web.vala | 1 +
systemd/shopsystem-input-device-rfid.rules | 1 +
templates/users/entry.html | 1 +
12 files changed, 97 insertions(+), 12 deletions(-)
create mode 100644 systemd/shopsystem-input-device-rfid.rules
diff --git a/example.cfg b/example.cfg
index 65056d8..a56e074 100644
--- a/example.cfg
+++ b/example.cfg
@@ -6,7 +6,8 @@ spacename = Mainframe
file = /path/to/shop.db
[INPUT]
# use ignore if you have no device (this skips the input feature)
-device = /dev/input/by-id/path-to-barcode-scanner
+barcodescanner = /dev/barcodescanner
+rfidreader = /dev/rfidreader
[MAIL]
server = mail.server.example.com
port = 587
diff --git a/sql/tables.sql b/sql/tables.sql
index fbe4d0e..1518ef2 100644
--- a/sql/tables.sql
+++ b/sql/tables.sql
@@ -9,5 +9,6 @@ CREATE TABLE IF NOT EXISTS supplier(id INTEGER PRIMARY KEY AUTOINCREMENT, name T
CREATE TABLE IF NOT EXISTS cashbox_diff(id INTEGER PRIMARY KEY AUTOINCREMENT, user INTEGER NOT NULL REFERENCES users, amount INTEGER NOT NULL, timestamp INTEGER NOT NULL DEFAULT 0);
CREATE TABLE IF NOT EXISTS ean_aliases (id INTEGER PRIMARY KEY NOT NULL, real_ean INTEGER NOT NULL REFERENCES products);
CREATE TABLE IF NOT EXISTS categories (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);
+CREATE TABLE IF NOT EXISTS rfid_users (rfid TEXT, user INTEGER NOT NULL REFERENCES users);
CREATE INDEX IF NOT EXISTS invoiceindex ON sales (user ASC, timestamp DESC);
COMMIT;
diff --git a/src/database/database.vala b/src/database/database.vala
index dc636b8..0ce3f87 100644
--- a/src/database/database.vala
+++ b/src/database/database.vala
@@ -138,6 +138,10 @@ public class DataBase : Object {
queries["alias_ean_add"] = "INSERT OR IGNORE INTO ean_aliases (id, real_ean) VALUES (?, ?)";
queries["alias_ean_get"] = "SELECT real_ean FROM ean_aliases WHERE id = ?";
queries["alias_ean_list"] = "SELECT id, real_ean FROM ean_aliases ORDER BY id ASC";
+ queries["userid_rfid"] = "SELECT user FROM rfid_users WHERE rfid = ?";
+ queries["rfid_userid"] = "SELECT rfid FROM rfid_users WHERE user = ?";
+ queries["rfid_insert"] = "INSERT OR REPLACE INTO rfid_users ('user','rfid') VALUES (?,?)";
+ queries["rfid_delete_user"] = "DELETE FROM rfid_users WHERE user = ?";
/* compile queries into statements */
foreach(var entry in queries.entries) {
@@ -632,6 +636,17 @@ public class DataBase : Object {
throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
}
+ statements["rfid_userid"].reset();
+ statements["rfid_userid"].bind_int(1, user);
+ rc = statements["rfid_userid"].step();
+
+ string[] rfid = {};
+ while(rc == Sqlite.ROW) {
+ rfid += statements["rfid_userid"].column_text(0);
+ rc = statements["rfid_userid"].step();
+ }
+ result.rfid = rfid;
+
return result;
}
@@ -894,6 +909,21 @@ public class DataBase : Object {
int rc = statements["user_replace"].step();
if(rc != Sqlite.DONE)
throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+
+ statements["rfid_delete_user"].reset();
+ statements["rfid_delete_user"].bind_int(1, u.id);
+ rc = statements["rfid_delete_user"].step();
+ if(rc != Sqlite.DONE)
+ throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+
+ foreach (string rfid in u.rfid) {
+ statements["rfid_insert"].reset();
+ statements["rfid_insert"].bind_int(1, u.id);
+ statements["rfid_insert"].bind_text(2, rfid);
+ rc = statements["rfid_insert"].step();
+ if(rc != Sqlite.DONE)
+ throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ }
}
public bool user_is_disabled(int user) throws DatabaseError {
@@ -1164,4 +1194,20 @@ public class DataBase : Object {
return bbdlist.data;
}
+
+ public int get_userid_for_rfid(string rfid) throws IOError, DatabaseError {
+ statements["userid_rfid"].reset();
+ statements["userid_rfid"].bind_text(1, rfid);
+
+ int rc = statements["userid_rfid"].step();
+
+ switch(rc) {
+ case Sqlite.ROW:
+ return statements["userid_rfid"].column_int(0);
+ case Sqlite.DONE:
+ throw new DatabaseError.RFID_NOT_FOUND("unknown rfid: %s", rfid);
+ default:
+ throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ }
+ }
}
diff --git a/src/database/db-interface.vala b/src/database/db-interface.vala
index de5bbc4..b6d1395 100644
--- a/src/database/db-interface.vala
+++ b/src/database/db-interface.vala
@@ -65,6 +65,9 @@ public interface Database : Object {
public abstract uint64 ean_alias_get(uint64 ean) throws IOError;
public abstract EanAlias[] ean_alias_list() throws IOError;
public abstract BestBeforeEntry[] bestbeforelist() throws IOError;
+ public abstract int get_userid_for_rfid(string rfid) throws IOError, DatabaseError;
+ public abstract void addrfid(string rfid, int user) throws IOError, DatabaseError;
+ public abstract void delete_rfid_for_user(int user) throws IOError, DatabaseError;
}
public struct Category {
@@ -126,6 +129,7 @@ public struct UserInfo {
public bool disabled;
public bool hidden;
public string soundTheme;
+ public string[] rfid;
public bool equals(UserInfo x) {
if(id != x.id) return false;
@@ -140,6 +144,7 @@ public struct UserInfo {
if(joined_at != x.joined_at) return false;
if(disabled != x.disabled) return false;
if(hidden != x.hidden) return false;
+ if(rfid != x.rfid) return false;
return true;
}
@@ -197,4 +202,5 @@ public errordomain DatabaseError {
SESSION_NOT_FOUND,
USER_NOT_FOUND,
CONSTRAINT_FAILED,
+ RFID_NOT_FOUND,
}
diff --git a/src/input-device/main.vala b/src/input-device/main.vala
index 8578033..30119b0 100644
--- a/src/input-device/main.vala
+++ b/src/input-device/main.vala
@@ -13,12 +13,14 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-Device dev;
+Device devScanner;
+Device devRfid;
public static int main(string[] args) {
try {
Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
- dev = new Device(cfg.get_string("INPUT", "device"));
+ devScanner = new Device(cfg.get_string("INPUT", "barcodescanner"));
+ devRfid = new Device(cfg.get_string("INPUT", "rfidreader"));
} catch(IOError e) {
error("IOError: %s\n", e.message);
} catch(KeyFileError e) {
@@ -40,7 +42,12 @@ public static int main(string[] args) {
void on_bus_aquired(DBusConnection con) {
try {
- con.register_object("/io/mainframe/shopsystem/device", dev);
+ con.register_object("/io/mainframe/shopsystem/device/scanner", devScanner);
+ } catch(IOError e) {
+ stderr.printf("Could not register service\n");
+ }
+ try {
+ con.register_object("/io/mainframe/shopsystem/device/rfid", devRfid);
} catch(IOError e) {
stderr.printf("Could not register service\n");
}
diff --git a/src/scanner-session/scannersession-interface.vala b/src/scanner-session/scannersession-interface.vala
index ff2c2e9..c19a434 100644
--- a/src/scanner-session/scannersession-interface.vala
+++ b/src/scanner-session/scannersession-interface.vala
@@ -31,6 +31,7 @@ public enum ScannerSessionCodeType {
UNDO,
LOGOUT,
EAN,
+ RFIDEM4100,
UNKNOWN
}
diff --git a/src/scanner-session/scannersession.vala b/src/scanner-session/scannersession.vala
index f19d94c..7cc9cc4 100644
--- a/src/scanner-session/scannersession.vala
+++ b/src/scanner-session/scannersession.vala
@@ -23,7 +23,8 @@ public class ScannerSessionImplementation {
private Database db;
private AudioPlayer audio;
- private InputDevice dev;
+ private InputDevice devScanner;
+ private InputDevice devRfid;
private Cli cli;
private ScannerSessionState state = ScannerSessionState.READY;
@@ -35,11 +36,13 @@ public class ScannerSessionImplementation {
public ScannerSessionImplementation() {
try {
db = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Database", "/io/mainframe/shopsystem/database");
- dev = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.InputDevice", "/io/mainframe/shopsystem/device");
+ devScanner = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.InputDevice", "/io/mainframe/shopsystem/device/scanner");
+ devRfid = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.InputDevice", "/io/mainframe/shopsystem/device/rfid");
cli = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Cli", "/io/mainframe/shopsystem/cli");
audio = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.AudioPlayer", "/io/mainframe/shopsystem/audio");
- dev.received_barcode.connect(handle_barcode);
+ devScanner.received_barcode.connect(handle_barcode);
+ devRfid.received_barcode.connect(handle_barcode);
cli.received_barcode.connect(handle_barcode);
} catch(IOError e) {
error("IOError: %s\n", e.message);
@@ -85,6 +88,8 @@ public class ScannerSessionImplementation {
return ScannerSessionCodeType.UNDO;
} else if(scannerdata == "LOGOUT") {
return ScannerSessionCodeType.LOGOUT;
+ } else if(scannerdata.length == 10) {
+ return ScannerSessionCodeType.RFIDEM4100;
} else {
//Handle EAN Code
uint64 id = 0;
@@ -184,6 +189,10 @@ public class ScannerSessionImplementation {
scannerResult.audioType = AudioType.ERROR;
state = ScannerSessionState.READY;
return scannerResult;
+ case ScannerSessionCodeType.RFIDEM4100:
+ int user = db.get_userid_for_rfid(scannerdata);
+ scannerResult.nextScannerdata =@"USER $user";
+ return scannerResult;
default:
state = ScannerSessionState.READY;
return scannerResult;
@@ -249,7 +258,8 @@ public class ScannerSessionImplementation {
break;
case ScannerSessionCodeType.USER:
case ScannerSessionCodeType.GUEST:
- //Logout alten User und akrtikel kaufen
+ case ScannerSessionCodeType.RFIDEM4100:
+ /* Logout old user session (and buy articles) */
scannerResult = logout();
scannerResult.nextScannerdata = scannerdata;
break;
@@ -284,7 +294,7 @@ public class ScannerSessionImplementation {
try {
stdout.printf("scannerdata: %s\n", scannerdata);
if(interpret(scannerdata))
- dev.blink(1000);
+ devScanner.blink(1000);
} catch(IOError e) {
send_message(MessageType.ERROR, "IOError: %s", e.message);
} catch(DatabaseError e) {
diff --git a/src/serial-device/main.vala b/src/serial-device/main.vala
index 0dea907..70d50e2 100644
--- a/src/serial-device/main.vala
+++ b/src/serial-device/main.vala
@@ -13,12 +13,12 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-Device dev;
+Device scanner;
public static int main(string[] args) {
try {
Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
- dev = new Device(cfg.get_string("INPUT", "device"), 9600, 8, 1);
+ scanner = new Device(cfg.get_string("INPUT", "barcodescanner"), 9600, 8, 1);
} catch(IOError e) {
error("IOError: %s\n", e.message);
} catch(KeyFileError e) {
@@ -40,7 +40,7 @@ public static int main(string[] args) {
void on_bus_aquired(DBusConnection con) {
try {
- con.register_object("/io/mainframe/shopsystem/device", dev);
+ con.register_object("/io/mainframe/shopsystem/devicescanner", scanner);
} catch(IOError e) {
stderr.printf("Could not register service\n");
}
diff --git a/src/web/csv.vala b/src/web/csv.vala
index 299af8d..5d4daba 100644
--- a/src/web/csv.vala
+++ b/src/web/csv.vala
@@ -1,4 +1,5 @@
/* Copyright 2012, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -66,6 +67,15 @@ public class CSVMemberFile {
m.pgp = csv_value(linedata[9]);
m.hidden = int.parse(csv_value(linedata[10])) != 0;
m.disabled = int.parse(csv_value(linedata[11])) != 0;
+ string[] rfid = {};
+ if(csv_value(linedata[12]) != "")
+ rfid += csv_value(linedata[12]);
+ if(csv_value(linedata[13]) != "")
+ rfid += csv_value(linedata[13]);
+ if(csv_value(linedata[14]) != "")
+ rfid += csv_value(linedata[14]);
+ m.rfid = rfid;
+
m.soundTheme = "";
if(csv_value(linedata[0]) != "EXTERNEMITGLIEDSNUMMER")
members += m;
diff --git a/src/web/web.vala b/src/web/web.vala
index 37e6edc..f4207ad 100644
--- a/src/web/web.vala
+++ b/src/web/web.vala
@@ -351,6 +351,7 @@ public class WebServer {
t.replace("PGPKEYID", userinfo.pgp);
t.replace("DISABLED", userinfo.disabled ? "true" : "false");
t.replace("HIDDEN", userinfo.hidden ? "true" : "false");
+ t.replace("RFID", string.joinv(" ",userinfo.rfid));
var userauth = db.get_user_auth(id);
t.replace("ISSUPERUSER", userauth.superuser ? "true" : "false");
diff --git a/systemd/shopsystem-input-device-rfid.rules b/systemd/shopsystem-input-device-rfid.rules
new file mode 100644
index 0000000..aebfbea
--- /dev/null
+++ b/systemd/shopsystem-input-device-rfid.rules
@@ -0,0 +1 @@
+ACTION=="add", ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="0035", MODE="0666", SYMLINK+="rfidreader" TAG+="systemd", ENV{SYSTEMD_WANTS}="shopsystem-input-device"
diff --git a/templates/users/entry.html b/templates/users/entry.html
index 065c4e7..b1477c7 100644
--- a/templates/users/entry.html
+++ b/templates/users/entry.html
@@ -22,6 +22,7 @@
Street
{{{STREET}}}
PLZ
{{{POSTALCODE}}}
City
{{{CITY}}}
+
RFID
{{{RFID}}}
Settings
Sound theme
--
cgit v1.2.3
From f466538a3527cd539c732d3abee481fa7a34fc2f Mon Sep 17 00:00:00 2001
From: Sebastian Reichel
Date: Thu, 28 Jun 2018 01:03:02 +0200
Subject: all: Add DBusError to avoid warnings with valac 0.40
---
src/audio/audio-interface.vala | 8 +-
src/audio/audio.vala | 10 +--
src/audio/main.vala | 2 +
src/cli/cli.vala | 2 +-
src/cli/main.vala | 8 +-
src/config/config-interface.vala | 16 ++--
src/config/config.vala | 16 ++--
src/curses-ui/main.vala | 17 ++---
src/database/database.vala | 107 ++++++++++++++-------------
src/database/db-interface.vala | 106 +++++++++++++-------------
src/database/main.vala | 2 +
src/input-device/input-device-interface.vala | 2 +-
src/input-device/input-device.vala | 2 +-
src/input-device/main.vala | 2 +
src/invoice/invoice.vala | 8 +-
src/mail/mail.vala | 6 +-
src/mail/mailer-interface.vala | 12 +--
src/mail/mailer.vala | 10 +--
src/mail/main.vala | 2 +-
src/pdf-invoice/main.vala | 6 +-
src/pdf-invoice/pdf-invoice-interface.vala | 4 +-
src/pdf-invoice/pdf-invoice.vala | 16 ++--
src/pdf-stock/pdf-stock-interface.vala | 2 +-
src/pdf-stock/pdf-stock.vala | 2 +-
src/pgp/main.vala | 2 +
src/pgp/pgp-interface.vala | 6 +-
src/pgp/pgp.vala | 6 +-
src/scanner-session/scannersession.vala | 18 +++--
src/serial-device/main.vala | 2 +
src/serial-device/serial-device.vala | 2 +-
src/web/csv.vala | 2 +-
src/web/main.vala | 2 +
src/web/web.vala | 48 ++++++++++++
src/web/websession.vala | 6 +-
34 files changed, 262 insertions(+), 200 deletions(-)
diff --git a/src/audio/audio-interface.vala b/src/audio/audio-interface.vala
index 12bfae4..721a484 100644
--- a/src/audio/audio-interface.vala
+++ b/src/audio/audio-interface.vala
@@ -17,10 +17,10 @@
public interface AudioPlayer : Object {
public abstract signal void end_of_stream();
- public abstract void play_system(string file) throws IOError;
- public abstract string get_random_user_theme() throws IOError;
- public abstract string[] get_user_themes() throws IOError;
- public abstract void play_user(string theme, string type) throws IOError;
+ public abstract void play_system(string file) throws DBusError, IOError;
+ public abstract string get_random_user_theme() throws DBusError, IOError;
+ public abstract string[] get_user_themes() throws DBusError, IOError;
+ public abstract void play_user(string theme, string type) throws DBusError, IOError;
}
public enum AudioType {
diff --git a/src/audio/audio.vala b/src/audio/audio.vala
index 9afb840..ccced20 100644
--- a/src/audio/audio.vala
+++ b/src/audio/audio.vala
@@ -30,7 +30,7 @@ public class AudioPlayerImplementation {
return true;
}
- public AudioPlayerImplementation(string path) throws IOError {
+ public AudioPlayerImplementation(string path) throws IOError, DBusError {
this.path = path;
var alsa = Gst.ElementFactory.make("alsasink", "alsa");
@@ -45,7 +45,7 @@ public class AudioPlayerImplementation {
p.get_bus().add_watch(Priority.DEFAULT, bus_callback);
}
- public void play_system(string file) {
+ public void play_system(string file) throws IOError, DBusError {
p.set_state(Gst.State.NULL);
p.uri = "file://" + path + "system/" + file;
p.set_state(Gst.State.PLAYING);
@@ -76,15 +76,15 @@ public class AudioPlayerImplementation {
return files[index];
}
- public string get_random_user_theme() {
+ public string get_random_user_theme() throws IOError, DBusError {
return get_random_file(path + "user/");
}
- public string[] get_user_themes() {
+ public string[] get_user_themes() throws IOError, DBusError {
return get_files(path + "user/");
}
- public void play_user(string theme, string type) {
+ public void play_user(string theme, string type) throws IOError, DBusError {
p.set_state(Gst.State.NULL);
var file = get_random_file(path + "user/" + theme+ "/" + type);
p.uri = "file://" + path + "user/" + theme+ "/" + type + "/" + file;
diff --git a/src/audio/main.vala b/src/audio/main.vala
index ac64f52..2a7d918 100644
--- a/src/audio/main.vala
+++ b/src/audio/main.vala
@@ -34,6 +34,8 @@ public static int main(string[] args) {
error("IOError: %s\n", e.message);
} catch(KeyFileError e) {
error("Config Error: %s\n", e.message);
+ } catch(DBusError e) {
+ error("DBus Error: %s\n", e.message);
}
new MainLoop().run();
diff --git a/src/cli/cli.vala b/src/cli/cli.vala
index bc6fe9d..c0ba61e 100644
--- a/src/cli/cli.vala
+++ b/src/cli/cli.vala
@@ -21,7 +21,7 @@ public class CliImpl {
public CliImpl() {
}
- public void send(string msg) {
+ public void send(string msg) throws IOError, DBusError {
stdout.printf("Sending: %s\n", msg);
received_barcode(msg);
}
diff --git a/src/cli/main.vala b/src/cli/main.vala
index a3208dd..377efa1 100644
--- a/src/cli/main.vala
+++ b/src/cli/main.vala
@@ -42,7 +42,11 @@ public static int main(string[] args) {
void on_name_aquired() {
foreach (string cmd in commands) {
- cli.send(cmd);
+ try {
+ cli.send(cmd);
+ } catch (Error e) {
+ stderr.printf("Error sending command: %s", e.message);
+ }
}
// wait a minimal amount of time, to ensure the event was sent
@@ -62,4 +66,4 @@ void on_bus_aquired(DBusConnection con) {
stderr.printf("Could not register service\n");
}
-}
\ No newline at end of file
+}
diff --git a/src/config/config-interface.vala b/src/config/config-interface.vala
index c34e3a7..369071e 100644
--- a/src/config/config-interface.vala
+++ b/src/config/config-interface.vala
@@ -15,12 +15,12 @@
[DBus (name = "io.mainframe.shopsystem.Config")]
public interface Config : Object {
- public abstract bool has_group(string group_name) throws IOError, KeyFileError;
- public abstract bool has_key(string group_name, string key) throws IOError, KeyFileError;
- public abstract string get_string(string group_name, string key) throws IOError, KeyFileError;
- public abstract bool get_boolean(string group_name, string key) throws IOError, KeyFileError;
- public abstract int get_integer(string group_name, string key) throws IOError, KeyFileError;
- public abstract int64 get_int64(string group_name, string key) throws IOError, KeyFileError;
- public abstract uint64 get_uint64(string group_name, string key) throws IOError, KeyFileError;
- public abstract double get_double(string group_name, string key) throws IOError, KeyFileError;
+ public abstract bool has_group(string group_name) throws DBusError, IOError, KeyFileError;
+ public abstract bool has_key(string group_name, string key) throws DBusError, IOError, KeyFileError;
+ public abstract string get_string(string group_name, string key) throws DBusError, IOError, KeyFileError;
+ public abstract bool get_boolean(string group_name, string key) throws DBusError, IOError, KeyFileError;
+ public abstract int get_integer(string group_name, string key) throws DBusError, IOError, KeyFileError;
+ public abstract int64 get_int64(string group_name, string key) throws DBusError, IOError, KeyFileError;
+ public abstract uint64 get_uint64(string group_name, string key) throws DBusError, IOError, KeyFileError;
+ public abstract double get_double(string group_name, string key) throws DBusError, IOError, KeyFileError;
}
diff --git a/src/config/config.vala b/src/config/config.vala
index 60fd835..b25b667 100644
--- a/src/config/config.vala
+++ b/src/config/config.vala
@@ -27,35 +27,35 @@ public class Cfg {
}
}
- public bool has_group(string group_name) throws KeyFileError {
+ public bool has_group(string group_name) throws DBusError, IOError, KeyFileError {
return file.has_group(group_name);
}
- public bool has_key(string group_name, string key) throws KeyFileError {
+ public bool has_key(string group_name, string key) throws DBusError, IOError, KeyFileError {
return file.has_key(group_name, key);
}
- public string get_string(string group_name, string key) throws KeyFileError {
+ public string get_string(string group_name, string key) throws DBusError, IOError, KeyFileError {
return file.get_string(group_name, key);
}
- public bool get_boolean(string group_name, string key) throws KeyFileError {
+ public bool get_boolean(string group_name, string key) throws DBusError, IOError, KeyFileError {
return file.get_boolean(group_name, key);
}
- public int get_integer(string group_name, string key) throws KeyFileError {
+ public int get_integer(string group_name, string key) throws DBusError, IOError, KeyFileError {
return file.get_integer(group_name, key);
}
- public int64 get_int64(string group_name, string key) throws KeyFileError {
+ public int64 get_int64(string group_name, string key) throws DBusError, IOError, KeyFileError {
return file.get_int64(group_name, key);
}
- public uint64 get_uint64(string group_name, string key) throws KeyFileError {
+ public uint64 get_uint64(string group_name, string key) throws DBusError, IOError, KeyFileError {
return file.get_uint64(group_name, key);
}
- public double get_double(string group_name, string key) throws KeyFileError {
+ public double get_double(string group_name, string key) throws DBusError, IOError, KeyFileError {
return file.get_double(group_name, key);
}
diff --git a/src/curses-ui/main.vala b/src/curses-ui/main.vala
index 47be40a..7c2e762 100644
--- a/src/curses-ui/main.vala
+++ b/src/curses-ui/main.vala
@@ -23,6 +23,8 @@ private static void play(string file) {
audio.play_system(file);
} catch(IOError e) {
ui.log(MessageType.WARNING, "could not play audio: %s".printf(e.message));
+ } catch(DBusError e) {
+ ui.log(MessageType.WARNING, "could not play audio: %s".printf(e.message));
}
}
@@ -43,8 +45,8 @@ public static int main(string[] args) {
loop = new MainLoop();
/* handle unix signals */
- Unix.signal_add(Posix.SIGTERM, handle_signals);
- Unix.signal_add(Posix.SIGINT, handle_signals);
+ Unix.signal_add(Posix.Signal.TERM, handle_signals);
+ Unix.signal_add(Posix.Signal.INT, handle_signals);
try {
audio = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.AudioPlayer", "/io/mainframe/shopsystem/audio");
@@ -62,16 +64,7 @@ public static int main(string[] args) {
scanner.msg.connect(msg_handler);
scanner.msg_overlay.connect(msg_overlay_handler);
- /* get configuration */
- Config config = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
- var shopname = "--SHOPNAME--";
- try {
- shopname = config.get_string("GENERAL", "longname");
- } catch(KeyFileError e) {
- shopname = "Missing in Config";
- }
-
- ui.log(MessageType.INFO, "KtT Shop System has been started");
+ ui.log(MessageType.INFO, "Shop System has been started");
play("startup.ogg");
/* run mainloop */
diff --git a/src/database/database.vala b/src/database/database.vala
index 0ce3f87..8106742 100644
--- a/src/database/database.vala
+++ b/src/database/database.vala
@@ -154,7 +154,7 @@ public class DataBase : Object {
#endif
}
- public GLib.HashTable get_products() {
+ public GLib.HashTable get_products() throws DBusError, IOError, DatabaseError {
var result = new GLib.HashTable(null, null);
statements["products"].reset();
@@ -277,7 +277,7 @@ public class DataBase : Object {
}
#endif
- public DetailedProduct[] get_stock() {
+ public DetailedProduct[] get_stock() throws DBusError, IOError, DatabaseError {
DetailedProduct[] result = {};
statements["stock_status"].reset();
@@ -297,8 +297,9 @@ public class DataBase : Object {
return result;
}
- public DetailedProduct get_product_for_ean(uint64 ean) throws DatabaseError {
- var p = new DetailedProduct();
+ public DetailedProduct get_product_for_ean(uint64 ean) throws DBusError, IOError, DatabaseError {
+ DetailedProduct p = {};
+
try {
p.ean = ean_alias_get(ean);
p.name = get_product_name(ean);
@@ -312,7 +313,7 @@ public class DataBase : Object {
}
}
- public PriceEntry[] get_prices(uint64 product) {
+ public PriceEntry[] get_prices(uint64 product) throws DBusError, IOError, DatabaseError {
PriceEntry[] result = {};
statements["prices"].reset();
@@ -330,7 +331,7 @@ public class DataBase : Object {
return result;
}
- public RestockEntry[] get_restocks(uint64 product, bool descending) {
+ public RestockEntry[] get_restocks(uint64 product, bool descending) throws DBusError, IOError, DatabaseError {
RestockEntry[] result = {};
var statement = statements[descending ? "restocks_desc" : "restocks_asc"];
@@ -354,7 +355,7 @@ public class DataBase : Object {
return result;
}
- public bool buy(int32 user, uint64 article) throws DatabaseError {
+ public bool buy(int32 user, uint64 article) throws DBusError, IOError, DatabaseError {
int rc = 0;
int64 timestamp = (new DateTime.now_utc()).to_unix();
@@ -370,7 +371,7 @@ public class DataBase : Object {
return true;
}
- public string get_product_name(uint64 article) throws DatabaseError {
+ public string get_product_name(uint64 article) throws DBusError, IOError, DatabaseError {
statements["product_name"].reset();
statements["product_name"].bind_text(1, "%llu".printf(article));
@@ -386,7 +387,7 @@ public class DataBase : Object {
}
}
- public string get_product_category(uint64 article) throws DatabaseError {
+ public string get_product_category(uint64 article) throws DBusError, IOError, DatabaseError {
statements["product_category"].reset();
statements["product_category"].bind_text(1, "%llu".printf(article));
@@ -402,7 +403,7 @@ public class DataBase : Object {
}
}
- public int get_product_amount(uint64 article) throws DatabaseError {
+ public int get_product_amount(uint64 article) throws DBusError, IOError, DatabaseError {
statements["product_amount"].reset();
statements["product_amount"].bind_text(1, "%llu".printf(article));
@@ -418,7 +419,7 @@ public class DataBase : Object {
}
}
- public bool get_product_deprecated(uint64 article) throws DatabaseError {
+ public bool get_product_deprecated(uint64 article) throws DBusError, IOError, DatabaseError {
statements["product_deprecated"].reset();
statements["product_deprecated"].bind_text(1, "%llu".printf(article));
@@ -434,7 +435,7 @@ public class DataBase : Object {
}
}
- public void product_deprecate(uint64 article, bool value) throws DatabaseError {
+ public void product_deprecate(uint64 article, bool value) throws DBusError, IOError, DatabaseError {
int rc;
statements["product_set_deprecated"].reset();
@@ -446,7 +447,7 @@ public class DataBase : Object {
throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
}
- public Price get_product_price(int user, uint64 article) throws DatabaseError {
+ public Price get_product_price(int user, uint64 article) throws DBusError, IOError, DatabaseError {
int64 timestamp = (new DateTime.now_utc()).to_unix();
bool member = user != 0;
@@ -469,7 +470,7 @@ public class DataBase : Object {
}
}
- public string undo(int32 user) throws DatabaseError {
+ public string undo(int32 user) throws DBusError, IOError, DatabaseError {
uint64 pid = 0;
int rc = 0;
string pname;
@@ -500,7 +501,7 @@ public class DataBase : Object {
return pname;
}
- public void restock(int user, uint64 product, uint amount, uint price, int supplier, int64 best_before_date) throws DatabaseError {
+ public void restock(int user, uint64 product, uint amount, uint price, int supplier, int64 best_before_date) throws DBusError, IOError, DatabaseError {
int rc = 0;
int64 timestamp = (new DateTime.now_utc()).to_unix();
@@ -525,7 +526,7 @@ public class DataBase : Object {
throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
}
- public void new_product(uint64 id, string name, int category, int memberprice, int guestprice) throws DatabaseError {
+ public void new_product(uint64 id, string name, int category, int memberprice, int guestprice) throws DBusError, IOError, DatabaseError {
statements["product_create"].reset();
statements["product_create"].bind_text(1, @"$id");
statements["product_create"].bind_text(2, name);
@@ -542,7 +543,7 @@ public class DataBase : Object {
new_price(id, 0, memberprice, guestprice);
}
- public void new_price(uint64 product, int64 timestamp, int memberprice, int guestprice) throws DatabaseError {
+ public void new_price(uint64 product, int64 timestamp, int memberprice, int guestprice) throws DBusError, IOError, DatabaseError {
statements["price_create"].reset();
statements["price_create"].bind_text(1, @"$product");
statements["price_create"].bind_int64(2, timestamp);
@@ -555,7 +556,7 @@ public class DataBase : Object {
}
}
- public bool check_user_password(int32 user, string password) {
+ public bool check_user_password(int32 user, string password) throws DBusError, IOError, DatabaseError {
statements["password_get"].reset();
statements["password_get"].bind_int(1, user);
@@ -569,7 +570,7 @@ public class DataBase : Object {
}
}
- public void set_user_password(int32 user, string password) throws DatabaseError {
+ public void set_user_password(int32 user, string password) throws DBusError, IOError, DatabaseError {
var pwhash = Checksum.compute_for_string(ChecksumType.SHA256, password);
int rc;
@@ -589,7 +590,7 @@ public class DataBase : Object {
throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
}
- public void set_sessionid(int user, string sessionid) throws DatabaseError {
+ public void set_sessionid(int user, string sessionid) throws DBusError, IOError, DatabaseError {
statements["session_set"].reset();
statements["session_set"].bind_text(1, sessionid);
statements["session_set"].bind_int(2, user);
@@ -599,7 +600,7 @@ public class DataBase : Object {
throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
}
- public int get_user_by_sessionid(string sessionid) throws DatabaseError {
+ public int get_user_by_sessionid(string sessionid) throws DBusError, IOError, DatabaseError {
statements["session_get"].reset();
statements["session_get"].bind_text(1, sessionid);
@@ -610,7 +611,7 @@ public class DataBase : Object {
}
}
- public UserInfo get_user_info(int user) throws DatabaseError {
+ public UserInfo get_user_info(int user) throws DBusError, IOError, DatabaseError {
var result = UserInfo();
statements["userinfo"].reset();
statements["userinfo"].bind_int(1, user);
@@ -650,7 +651,7 @@ public class DataBase : Object {
return result;
}
- public UserAuth get_user_auth(int user) throws DatabaseError {
+ public UserAuth get_user_auth(int user) throws DBusError, IOError, DatabaseError {
var result = UserAuth();
result.id = user;
result.superuser = false;
@@ -676,7 +677,7 @@ public class DataBase : Object {
return result;
}
- public void set_user_auth(UserAuth auth) throws DatabaseError {
+ public void set_user_auth(UserAuth auth) throws DBusError, IOError, DatabaseError {
int rc;
/* create user auth line if not existing */
@@ -698,7 +699,7 @@ public class DataBase : Object {
throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
}
- public string get_username(int user) throws DatabaseError {
+ public string get_username(int user) throws DBusError, IOError, DatabaseError {
statements["username"].reset();
statements["username"].bind_int(1, user);
@@ -709,7 +710,7 @@ public class DataBase : Object {
}
}
- public string get_user_theme(int user, string fallback) throws DatabaseError {
+ public string get_user_theme(int user, string fallback) throws DBusError, IOError, DatabaseError {
statements["user_theme_get"].reset();
statements["user_theme_get"].bind_text(1, fallback);
statements["user_theme_get"].bind_int(2, user);
@@ -721,7 +722,7 @@ public class DataBase : Object {
}
}
- public void set_userTheme(int user, string userTheme) throws DatabaseError {
+ public void set_userTheme(int user, string userTheme) throws DBusError, IOError, DatabaseError {
statements["user_theme_set"].reset();
if (userTheme == "") {
statements["user_theme_set"].bind_null(1);
@@ -735,7 +736,7 @@ public class DataBase : Object {
throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
}
- public InvoiceEntry[] get_invoice(int user, int64 from=0, int64 to=-1) throws DatabaseError {
+ public InvoiceEntry[] get_invoice(int user, int64 from=0, int64 to=-1) throws DBusError, IOError, DatabaseError {
InvoiceEntry[] result = {};
if(to == -1) {
@@ -766,7 +767,7 @@ public class DataBase : Object {
return result;
}
- public int64 get_first_purchase(int user) {
+ public int64 get_first_purchase(int user) throws DBusError, IOError, DatabaseError {
statements["purchase_first"].reset();
statements["purchase_first"].bind_int(1, user);
@@ -776,7 +777,7 @@ public class DataBase : Object {
return 0;
}
- public int64 get_last_purchase(int user) {
+ public int64 get_last_purchase(int user) throws DBusError, IOError, DatabaseError {
statements["purchase_last"].reset();
statements["purchase_last"].bind_int(1, user);
@@ -786,7 +787,7 @@ public class DataBase : Object {
return 0;
}
- public StatsInfo get_stats_info() {
+ public StatsInfo get_stats_info() throws DBusError, IOError, DatabaseError {
var result = StatsInfo();
DateTime now = new DateTime.now_local();
@@ -861,7 +862,7 @@ public class DataBase : Object {
return result;
}
- public int[] get_member_ids() {
+ public int[] get_member_ids() throws DBusError, IOError, DatabaseError {
int[] result = {};
statements["user_get_ids"].reset();
@@ -871,7 +872,7 @@ public class DataBase : Object {
return result;
}
- public void user_disable(int user, bool value) throws DatabaseError {
+ public void user_disable(int user, bool value) throws DBusError, IOError, DatabaseError {
int rc;
/* create user auth line if not existing */
@@ -890,7 +891,7 @@ public class DataBase : Object {
throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
}
- public void user_replace(UserInfo u) throws DatabaseError {
+ public void user_replace(UserInfo u) throws DBusError, IOError, DatabaseError {
statements["user_replace"].reset();
statements["user_replace"].bind_int(1, u.id);
statements["user_replace"].bind_text(2, u.email);
@@ -926,29 +927,29 @@ public class DataBase : Object {
}
}
- public bool user_is_disabled(int user) throws DatabaseError {
+ public bool user_is_disabled(int user) throws DBusError, IOError, DatabaseError {
return get_user_info(user).disabled;
}
- public bool user_exists(int user) throws DatabaseError {
+ public bool user_exists(int user) throws DBusError, IOError, DatabaseError {
if(user in get_member_ids())
return true;
return false;
}
- public bool user_equals(UserInfo u) throws DatabaseError {
+ public bool user_equals(UserInfo u) throws DBusError, IOError, DatabaseError {
var dbu = get_user_info(u.id);
return u.equals(dbu);
}
- public int64 get_timestamp_of_last_purchase() {
+ public int64 get_timestamp_of_last_purchase() throws DBusError, IOError, DatabaseError {
statements["last_timestamp"].reset();
if(statements["last_timestamp"].step() != Sqlite.ROW)
return 0;
return statements["last_timestamp"].column_int64(0);
}
- public Category[] get_category_list() {
+ public Category[] get_category_list() throws DBusError, IOError, DatabaseError {
Category[] result = {};
statements["category_list"].reset();
@@ -964,7 +965,7 @@ public class DataBase : Object {
return result;
}
- public Supplier[] get_supplier_list() {
+ public Supplier[] get_supplier_list() throws DBusError, IOError, DatabaseError {
Supplier[] result = {};
statements["supplier_list"].reset();
@@ -985,7 +986,7 @@ public class DataBase : Object {
return result;
}
- public Supplier get_supplier(int id) {
+ public Supplier get_supplier(int id) throws DBusError, IOError, DatabaseError {
Supplier result = Supplier();
statements["supplier_get"].reset();
@@ -1012,7 +1013,7 @@ public class DataBase : Object {
return result;
}
- public void add_supplier(string name, string postal_code, string city, string street, string phone, string website) throws DatabaseError {
+ public void add_supplier(string name, string postal_code, string city, string street, string phone, string website) throws DBusError, IOError, DatabaseError {
statements["supplier_add"].reset();
statements["supplier_add"].bind_text(1, name);
statements["supplier_add"].bind_text(2, postal_code);
@@ -1027,7 +1028,7 @@ public class DataBase : Object {
}
}
- public int[] get_users_with_sales(int64 timestamp_from, int64 timestamp_to) {
+ public int[] get_users_with_sales(int64 timestamp_from, int64 timestamp_to) throws DBusError, IOError, DatabaseError {
var result = new int[0];
statements["users_with_sales"].reset();
statements["users_with_sales"].bind_int64(1, timestamp_from);
@@ -1040,7 +1041,7 @@ public class DataBase : Object {
return result;
}
- public Price get_user_invoice_sum(int user, int64 timestamp_from, int64 timestamp_to) {
+ public Price get_user_invoice_sum(int user, int64 timestamp_from, int64 timestamp_to) throws DBusError, IOError, DatabaseError {
Price result = 0;
statements["user_invoice_sum"].reset();
@@ -1054,7 +1055,7 @@ public class DataBase : Object {
return result;
}
- public Price cashbox_status() {
+ public Price cashbox_status() throws DBusError, IOError, DatabaseError {
Price result = 0;
statements["cashbox_status"].reset();
@@ -1065,7 +1066,7 @@ public class DataBase : Object {
return result;
}
- public void cashbox_add(int user, Price amount, int64 timestamp) throws DatabaseError {
+ public void cashbox_add(int user, Price amount, int64 timestamp) throws DBusError, IOError, DatabaseError {
statements["cashbox_add"].reset();
statements["cashbox_add"].bind_int(1, user);
statements["cashbox_add"].bind_int(2, amount);
@@ -1078,7 +1079,7 @@ public class DataBase : Object {
}
}
- public CashboxDiff[] cashbox_history() {
+ public CashboxDiff[] cashbox_history() throws DBusError, IOError, DatabaseError {
CashboxDiff[] result = {};
statements["cashbox_history"].reset();
@@ -1096,7 +1097,7 @@ public class DataBase : Object {
return result;
}
- public CashboxDiff[] cashbox_changes(int64 start, int64 stop) {
+ public CashboxDiff[] cashbox_changes(int64 start, int64 stop) throws DBusError, IOError, DatabaseError {
CashboxDiff[] result = {};
statements["cashbox_changes"].reset();
@@ -1116,7 +1117,7 @@ public class DataBase : Object {
return result;
}
- public void ean_alias_add(uint64 ean, uint64 real_ean) throws DatabaseError {
+ public void ean_alias_add(uint64 ean, uint64 real_ean) throws DBusError, IOError, DatabaseError {
statements["alias_ean_add"].reset();
statements["alias_ean_add"].bind_text(1, "%llu".printf(ean));
statements["alias_ean_add"].bind_text(2, "%llu".printf(real_ean));
@@ -1128,7 +1129,7 @@ public class DataBase : Object {
}
}
- public uint64 ean_alias_get(uint64 ean) {
+ public uint64 ean_alias_get(uint64 ean) throws DBusError, IOError, DatabaseError {
uint64 result = ean;
statements["alias_ean_get"].reset();
@@ -1140,7 +1141,7 @@ public class DataBase : Object {
return result;
}
- public EanAlias[] ean_alias_list() {
+ public EanAlias[] ean_alias_list() throws DBusError, IOError, DatabaseError {
EanAlias[] result = {};
statements["alias_ean_list"].reset();
@@ -1165,7 +1166,7 @@ public class DataBase : Object {
return 1;
}
- public BestBeforeEntry?[] bestbeforelist() {
+ public BestBeforeEntry?[] bestbeforelist() throws DBusError, IOError, DatabaseError {
var bbdlist = new GLib.GenericArray();
foreach(var product in get_stock()) {
@@ -1195,7 +1196,7 @@ public class DataBase : Object {
return bbdlist.data;
}
- public int get_userid_for_rfid(string rfid) throws IOError, DatabaseError {
+ public int get_userid_for_rfid(string rfid) throws DBusError, IOError, DatabaseError {
statements["userid_rfid"].reset();
statements["userid_rfid"].bind_text(1, rfid);
diff --git a/src/database/db-interface.vala b/src/database/db-interface.vala
index b6d1395..5d0fdfb 100644
--- a/src/database/db-interface.vala
+++ b/src/database/db-interface.vala
@@ -15,59 +15,59 @@
[DBus (name = "io.mainframe.shopsystem.Database")]
public interface Database : Object {
- public abstract DetailedProduct[] get_stock() throws IOError;
- public abstract DetailedProduct get_product_for_ean(uint64 ean) throws IOError, DatabaseError;
- public abstract PriceEntry[] get_prices(uint64 product) throws IOError;
- public abstract RestockEntry[] get_restocks(uint64 product, bool descending) throws IOError;
- public abstract bool buy(int32 user, uint64 article) throws IOError, DatabaseError;
- public abstract string get_product_name(uint64 article) throws IOError, DatabaseError;
- public abstract string get_product_category(uint64 article) throws IOError, DatabaseError;
- public abstract int get_product_amount(uint64 article) throws IOError, DatabaseError;
- public abstract bool get_product_deprecated(uint64 article) throws IOError, DatabaseError;
- public abstract void product_deprecate(uint64 article, bool value) throws IOError, DatabaseError;
- public abstract Price get_product_price(int user, uint64 article) throws IOError, DatabaseError;
- public abstract string undo(int32 user) throws IOError, DatabaseError;
- public abstract void restock(int user, uint64 product, uint amount, uint price, int supplier, int64 best_before_date) throws IOError, DatabaseError;
- public abstract void new_product(uint64 id, string name, int category, int memberprice, int guestprice) throws IOError, DatabaseError;
- public abstract void new_price(uint64 product, int64 timestamp, int memberprice, int guestprice) throws IOError, DatabaseError;
- public abstract bool check_user_password(int32 user, string password) throws IOError;
- public abstract void set_user_password(int32 user, string password) throws IOError, DatabaseError;
- public abstract void set_sessionid(int user, string sessionid) throws IOError, DatabaseError;
- public abstract void set_userTheme(int user, string userTheme) throws IOError, DatabaseError;
- public abstract int get_user_by_sessionid(string sessionid) throws IOError, DatabaseError;
- public abstract UserInfo get_user_info(int user) throws IOError, DatabaseError;
- public abstract UserAuth get_user_auth(int user) throws IOError, DatabaseError;
- public abstract void set_user_auth(UserAuth auth) throws IOError, DatabaseError;
- public abstract string get_username(int user) throws IOError, DatabaseError;
- public abstract string get_user_theme(int user, string fallback) throws IOError, DatabaseError;
- public abstract InvoiceEntry[] get_invoice(int user, int64 from=0, int64 to=-1) throws IOError, DatabaseError;
- public abstract int64 get_first_purchase(int user) throws IOError;
- public abstract int64 get_last_purchase(int user) throws IOError;
- public abstract StatsInfo get_stats_info() throws IOError;
- public abstract int[] get_member_ids() throws IOError;
- public abstract void user_disable(int user, bool value) throws IOError, DatabaseError;
- public abstract void user_replace(UserInfo u) throws IOError, DatabaseError;
- public abstract bool user_is_disabled(int user) throws IOError, DatabaseError;
- public abstract bool user_exists(int user) throws IOError, DatabaseError;
- public abstract bool user_equals(UserInfo u) throws IOError, DatabaseError;
- public abstract int64 get_timestamp_of_last_purchase() throws IOError;
- public abstract Category[] get_category_list() throws IOError;
- public abstract Supplier[] get_supplier_list() throws IOError;
- public abstract Supplier get_supplier(int id) throws IOError;
- public abstract void add_supplier(string name, string postal_code, string city, string street, string phone, string website) throws IOError, DatabaseError;
- public abstract int[] get_users_with_sales(int64 timestamp_from, int64 timestamp_to) throws IOError;
- public abstract Price get_user_invoice_sum(int user, int64 timestamp_from, int64 timestamp_to) throws IOError;
- public abstract Price cashbox_status() throws IOError;
- public abstract void cashbox_add(int user, Price amount, int64 timestamp) throws IOError, DatabaseError;
- public abstract CashboxDiff[] cashbox_history() throws IOError;
- public abstract CashboxDiff[] cashbox_changes(int64 start, int64 stop) throws IOError;
- public abstract void ean_alias_add(uint64 ean, uint64 real_ean) throws IOError, DatabaseError;
- public abstract uint64 ean_alias_get(uint64 ean) throws IOError;
- public abstract EanAlias[] ean_alias_list() throws IOError;
- public abstract BestBeforeEntry[] bestbeforelist() throws IOError;
- public abstract int get_userid_for_rfid(string rfid) throws IOError, DatabaseError;
- public abstract void addrfid(string rfid, int user) throws IOError, DatabaseError;
- public abstract void delete_rfid_for_user(int user) throws IOError, DatabaseError;
+ public abstract DetailedProduct[] get_stock() throws DBusError, IOError;
+ public abstract DetailedProduct get_product_for_ean(uint64 ean) throws DBusError, IOError, DatabaseError;
+ public abstract PriceEntry[] get_prices(uint64 product) throws DBusError, IOError;
+ public abstract RestockEntry[] get_restocks(uint64 product, bool descending) throws DBusError, IOError;
+ public abstract bool buy(int32 user, uint64 article) throws DBusError, IOError, DatabaseError;
+ public abstract string get_product_name(uint64 article) throws DBusError, IOError, DatabaseError;
+ public abstract string get_product_category(uint64 article) throws DBusError, IOError, DatabaseError;
+ public abstract int get_product_amount(uint64 article) throws DBusError, IOError, DatabaseError;
+ public abstract bool get_product_deprecated(uint64 article) throws DBusError, IOError, DatabaseError;
+ public abstract void product_deprecate(uint64 article, bool value) throws DBusError, IOError, DatabaseError;
+ public abstract Price get_product_price(int user, uint64 article) throws DBusError, IOError, DatabaseError;
+ public abstract string undo(int32 user) throws DBusError, IOError, DatabaseError;
+ public abstract void restock(int user, uint64 product, uint amount, uint price, int supplier, int64 best_before_date) throws DBusError, IOError, DatabaseError;
+ public abstract void new_product(uint64 id, string name, int category, int memberprice, int guestprice) throws DBusError, IOError, DatabaseError;
+ public abstract void new_price(uint64 product, int64 timestamp, int memberprice, int guestprice) throws DBusError, IOError, DatabaseError;
+ public abstract bool check_user_password(int32 user, string password) throws DBusError, IOError;
+ public abstract void set_user_password(int32 user, string password) throws DBusError, IOError, DatabaseError;
+ public abstract void set_sessionid(int user, string sessionid) throws DBusError, IOError, DatabaseError;
+ public abstract void set_userTheme(int user, string userTheme) throws DBusError, IOError, DatabaseError;
+ public abstract int get_user_by_sessionid(string sessionid) throws DBusError, IOError, DatabaseError;
+ public abstract UserInfo get_user_info(int user) throws DBusError, IOError, DatabaseError;
+ public abstract UserAuth get_user_auth(int user) throws DBusError, IOError, DatabaseError;
+ public abstract void set_user_auth(UserAuth auth) throws DBusError, IOError, DatabaseError;
+ public abstract string get_username(int user) throws DBusError, IOError, DatabaseError;
+ public abstract string get_user_theme(int user, string fallback) throws DBusError, IOError, DatabaseError;
+ public abstract InvoiceEntry[] get_invoice(int user, int64 from=0, int64 to=-1) throws DBusError, IOError, DatabaseError;
+ public abstract int64 get_first_purchase(int user) throws DBusError, IOError;
+ public abstract int64 get_last_purchase(int user) throws DBusError, IOError;
+ public abstract StatsInfo get_stats_info() throws DBusError, IOError;
+ public abstract int[] get_member_ids() throws DBusError, IOError;
+ public abstract void user_disable(int user, bool value) throws DBusError, IOError, DatabaseError;
+ public abstract void user_replace(UserInfo u) throws DBusError, IOError, DatabaseError;
+ public abstract bool user_is_disabled(int user) throws DBusError, IOError, DatabaseError;
+ public abstract bool user_exists(int user) throws DBusError, IOError, DatabaseError;
+ public abstract bool user_equals(UserInfo u) throws DBusError, IOError, DatabaseError;
+ public abstract int64 get_timestamp_of_last_purchase() throws DBusError, IOError;
+ public abstract Category[] get_category_list() throws DBusError, IOError;
+ public abstract Supplier[] get_supplier_list() throws DBusError, IOError;
+ public abstract Supplier get_supplier(int id) throws DBusError, IOError;
+ public abstract void add_supplier(string name, string postal_code, string city, string street, string phone, string website) throws DBusError, IOError, DatabaseError;
+ public abstract int[] get_users_with_sales(int64 timestamp_from, int64 timestamp_to) throws DBusError, IOError;
+ public abstract Price get_user_invoice_sum(int user, int64 timestamp_from, int64 timestamp_to) throws DBusError, IOError;
+ public abstract Price cashbox_status() throws DBusError, IOError;
+ public abstract void cashbox_add(int user, Price amount, int64 timestamp) throws DBusError, IOError, DatabaseError;
+ public abstract CashboxDiff[] cashbox_history() throws DBusError, IOError;
+ public abstract CashboxDiff[] cashbox_changes(int64 start, int64 stop) throws DBusError, IOError;
+ public abstract void ean_alias_add(uint64 ean, uint64 real_ean) throws DBusError, IOError, DatabaseError;
+ public abstract uint64 ean_alias_get(uint64 ean) throws DBusError, IOError;
+ public abstract EanAlias[] ean_alias_list() throws DBusError, IOError;
+ public abstract BestBeforeEntry[] bestbeforelist() throws DBusError, IOError;
+ public abstract int get_userid_for_rfid(string rfid) throws DBusError, IOError, DatabaseError;
+ public abstract void addrfid(string rfid, int user) throws DBusError, IOError, DatabaseError;
+ public abstract void delete_rfid_for_user(int user) throws DBusError, IOError, DatabaseError;
}
public struct Category {
diff --git a/src/database/main.vala b/src/database/main.vala
index 72e3813..f65707b 100644
--- a/src/database/main.vala
+++ b/src/database/main.vala
@@ -28,6 +28,8 @@ public static int main(string[] args) {
error("IOError: %s\n", e.message);
} catch(KeyFileError e) {
error("Config Error: %s\n", e.message);
+ } catch(DBusError e) {
+ error("DBusError: %s\n", e.message);
}
Bus.own_name(
diff --git a/src/input-device/input-device-interface.vala b/src/input-device/input-device-interface.vala
index 067b827..9b16da4 100644
--- a/src/input-device/input-device-interface.vala
+++ b/src/input-device/input-device-interface.vala
@@ -16,5 +16,5 @@
[DBus (name = "io.mainframe.shopsystem.InputDevice")]
public interface InputDevice : Object {
public abstract signal void received_barcode(string barcode);
- public abstract void blink(uint duration) throws IOError;
+ public abstract void blink(uint duration) throws IOError, DBusError;
}
diff --git a/src/input-device/input-device.vala b/src/input-device/input-device.vala
index 6988c6d..d3a7691 100644
--- a/src/input-device/input-device.vala
+++ b/src/input-device/input-device.vala
@@ -315,7 +315,7 @@ public class Device {
/**
* @param duration duration of the blink in 0.1 seconds
*/
- public void blink(uint duration) {
+ public void blink(uint duration) throws IOError, DBusError {
/* not supported */
}
}
diff --git a/src/input-device/main.vala b/src/input-device/main.vala
index 30119b0..757de46 100644
--- a/src/input-device/main.vala
+++ b/src/input-device/main.vala
@@ -25,6 +25,8 @@ public static int main(string[] args) {
error("IOError: %s\n", e.message);
} catch(KeyFileError e) {
error("Config Error: %s\n", e.message);
+ } catch(DBusError e) {
+ error("DBus Error: %s\n", e.message);
}
Bus.own_name(
diff --git a/src/invoice/invoice.vala b/src/invoice/invoice.vala
index ce47c7b..c0a162c 100644
--- a/src/invoice/invoice.vala
+++ b/src/invoice/invoice.vala
@@ -39,7 +39,7 @@ public class InvoiceImplementation {
string vat;
string jverein_membership_number;
- public InvoiceImplementation() throws IOError, KeyFileError {
+ public InvoiceImplementation() throws DBusError, IOError, KeyFileError {
mailer = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", "/io/mainframe/shopsystem/mailer");
db = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Database", "/io/mainframe/shopsystem/database");
pdf = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.InvoicePDF", "/io/mainframe/shopsystem/invoicepdf");
@@ -53,7 +53,7 @@ public class InvoiceImplementation {
jverein_membership_number = cfg.get_string("JVEREIN", "membership_number");
}
- public void send_invoice(bool temporary, int64 timestamp, int user) throws IOError, InvoicePDFError, DatabaseError {
+ public void send_invoice(bool temporary, int64 timestamp, int user) throws DBusError, IOError, InvoicePDFError, DatabaseError {
int64 prevtimestamp = timestamp - day_in_seconds;
if(!temporary)
@@ -121,7 +121,7 @@ public class InvoiceImplementation {
}
}
- public void send_invoices(bool temporary, int64 timestamp) throws IOError, InvoicePDFError, DatabaseError {
+ public void send_invoices(bool temporary, int64 timestamp) throws DBusError, IOError, InvoicePDFError, DatabaseError {
int64 prevtimestamp = timestamp - day_in_seconds;
string due_date_string = "";
@@ -200,7 +200,7 @@ public class InvoiceImplementation {
}
}
- public InvoiceData generate_invoice(bool temporary, int64 timestamp, int userid, string invoiceid) throws IOError, InvoicePDFError, DatabaseError {
+ public InvoiceData generate_invoice(bool temporary, int64 timestamp, int userid, string invoiceid) throws DBusError, IOError, InvoicePDFError, DatabaseError {
int64 prevtimestamp = timestamp - day_in_seconds;
if(!temporary)
prevtimestamp = new DateTime.from_unix_local(timestamp).add_months(-1).to_unix();
diff --git a/src/mail/mail.vala b/src/mail/mail.vala
index 63e9362..e1eec1d 100644
--- a/src/mail/mail.vala
+++ b/src/mail/mail.vala
@@ -101,12 +101,12 @@ public class MailImplementation {
}
#endif
- public void add_recipient(MailContact contact, GMime.AddressType type) {
+ public void add_recipient(MailContact contact, GMime.AddressType type) throws DBusError, IOError {
m.add_mailbox(type, contact.name, contact.email);
recipients += contact.email;
}
- public void set_main_part(string text, MessageType type) {
+ public void set_main_part(string text, MessageType type) throws DBusError, IOError {
GMime.DataWrapper content = new GMime.DataWrapper.with_stream(
new GMime.StreamMem.with_buffer(text.data),
GMime.ContentEncoding.DEFAULT);
@@ -129,7 +129,7 @@ public class MailImplementation {
}
}
- public void add_attachment(string filename, string content_type, uint8[] data) {
+ public void add_attachment(string filename, string content_type, uint8[] data) throws DBusError, IOError {
GMime.Part part = new GMime.Part();
GMime.DataWrapper content = new GMime.DataWrapper.with_stream(
diff --git a/src/mail/mailer-interface.vala b/src/mail/mailer-interface.vala
index 1b819d5..019585b 100644
--- a/src/mail/mailer-interface.vala
+++ b/src/mail/mailer-interface.vala
@@ -15,9 +15,9 @@
[DBus (name = "io.mainframe.shopsystem.Mailer")]
public interface Mailer : Object {
- public abstract string create_mail() throws IOError;
- public abstract void delete_mail(string path) throws IOError;
- public abstract void send_mail(string path) throws IOError;
+ public abstract string create_mail() throws IOError, DBusError;
+ public abstract void delete_mail(string path) throws IOError, DBusError;
+ public abstract void send_mail(string path) throws IOError, DBusError;
}
[DBus (name = "io.mainframe.shopsystem.Mail")]
@@ -28,9 +28,9 @@ public interface Mail : Object {
public abstract string reply_to { owned get; set; }
public abstract MailDate date { owned get; set; }
- public abstract void add_recipient(MailContact contact, RecipientType type = RecipientType.TO) throws IOError;
- public abstract void set_main_part(string text, MessageType type = MessageType.PLAIN) throws IOError;
- public abstract void add_attachment(string filename, string content_type, uint8[] data) throws IOError;
+ public abstract void add_recipient(MailContact contact, RecipientType type = RecipientType.TO) throws IOError, DBusError;
+ public abstract void set_main_part(string text, MessageType type = MessageType.PLAIN) throws IOError, DBusError;
+ public abstract void add_attachment(string filename, string content_type, uint8[] data) throws IOError, DBusError;
}
public struct MailAttachment {
diff --git a/src/mail/mailer.vala b/src/mail/mailer.vala
index d83e4b0..c8d609c 100644
--- a/src/mail/mailer.vala
+++ b/src/mail/mailer.vala
@@ -56,7 +56,7 @@ public class MailerImplementation {
return 1;
}
- public MailerImplementation() throws IOError {
+ public MailerImplementation() throws DBusError, IOError {
int result;
GMime.init();
@@ -67,7 +67,7 @@ public class MailerImplementation {
send_queue = new Queue();
/* ignore SIGPIPE, as suggested by libESMTP */
- Posix.signal(Posix.SIGPIPE, Posix.SIG_IGN);
+ Posix.signal(Posix.Signal.PIPE, Posix.SIG_IGN);
/* get configuration */
Config config = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
@@ -120,7 +120,7 @@ public class MailerImplementation {
GMime.shutdown();
}
- public string create_mail() throws IOError {
+ public string create_mail() throws DBusError, IOError {
string path = @"/io/mainframe/shopsystem/mail/$mailcounter";
var mail = new MailImplementation();
@@ -136,7 +136,7 @@ public class MailerImplementation {
return path;
}
- public void delete_mail(string path) throws IOError {
+ public void delete_mail(string path) throws DBusError, IOError {
if(!(path in mails))
throw new IOError.NOT_FOUND("No such mail");
@@ -144,7 +144,7 @@ public class MailerImplementation {
mails.remove(path);
}
- public void send_mail(string path) throws IOError {
+ public void send_mail(string path) throws DBusError, IOError {
if(!(path in mails))
throw new IOError.NOT_FOUND("No such mail");
diff --git a/src/mail/main.vala b/src/mail/main.vala
index 0c36f6b..989c4f1 100644
--- a/src/mail/main.vala
+++ b/src/mail/main.vala
@@ -19,7 +19,7 @@ DBusConnection mail_bus;
public static int main(string[] args) {
try {
m = new MailerImplementation();
- } catch(IOError e) {
+ } catch(Error e) {
stderr.printf("Error: %s\n", e.message);
}
diff --git a/src/pdf-invoice/main.vala b/src/pdf-invoice/main.vala
index 3d8e298..dc95bde 100644
--- a/src/pdf-invoice/main.vala
+++ b/src/pdf-invoice/main.vala
@@ -19,6 +19,8 @@ public static int main(string[] args) {
try {
Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
datadir = cfg.get_string("INVOICE", "datadir");
+ } catch(DBusError e) {
+ error("DBusError: %s\n", e.message);
} catch(IOError e) {
error("IOError: %s\n", e.message);
} catch(KeyFileError e) {
@@ -41,7 +43,7 @@ public static int main(string[] args) {
void on_bus_aquired(DBusConnection conn) {
try {
conn.register_object("/io/mainframe/shopsystem/invoicepdf", new InvoicePDF(datadir));
- } catch(IOError e) {
- stderr.printf("Could not register service\n");
+ } catch(Error e) {
+ stderr.printf("Could not register service: %s\n", e.message);
}
}
diff --git a/src/pdf-invoice/pdf-invoice-interface.vala b/src/pdf-invoice/pdf-invoice-interface.vala
index 8fccbf0..41abc6d 100644
--- a/src/pdf-invoice/pdf-invoice-interface.vala
+++ b/src/pdf-invoice/pdf-invoice-interface.vala
@@ -43,6 +43,6 @@ public interface PDFInvoice : Object {
public abstract InvoiceRecipient invoice_recipient { set; owned get; }
public abstract InvoiceEntry[] invoice_entries { set; owned get; }
- public abstract uint8[] generate() throws IOError, InvoicePDFError;
- public abstract void clear() throws IOError;
+ public abstract uint8[] generate() throws DBusError, IOError, InvoicePDFError;
+ public abstract void clear() throws DBusError, IOError;
}
diff --git a/src/pdf-invoice/pdf-invoice.vala b/src/pdf-invoice/pdf-invoice.vala
index 6fd4383..a5696b1 100644
--- a/src/pdf-invoice/pdf-invoice.vala
+++ b/src/pdf-invoice/pdf-invoice.vala
@@ -65,7 +65,7 @@ public class InvoicePDF {
string longname;
string vat;
- public InvoicePDF(string datadir) {
+ public InvoicePDF(string datadir) throws DBusError, IOError, KeyFileError {
this.datadir = datadir;
cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
longname = cfg.get_string("GENERAL", "longname");
@@ -96,7 +96,7 @@ public class InvoicePDF {
ctx.restore();
}
- private void draw_address(Cairo.Context ctx) {
+ private void draw_address(Cairo.Context ctx) throws DBusError, IOError, KeyFileError {
ctx.save();
ctx.set_source_rgb(0, 0, 0);
ctx.set_line_width(1.0);
@@ -206,7 +206,7 @@ public class InvoicePDF {
ctx.restore();
}
- private void draw_footer_text_left(Cairo.Context ctx) {
+ private void draw_footer_text_left(Cairo.Context ctx) throws DBusError, IOError, KeyFileError {
ctx.save();
ctx.move_to(64.0, 742.0);
ctx.set_source_rgb(0, 0, 0);
@@ -242,7 +242,7 @@ public class InvoicePDF {
ctx.restore();
}
- private void draw_footer_text_middle(Cairo.Context ctx) {
+ private void draw_footer_text_middle(Cairo.Context ctx) throws DBusError, IOError, KeyFileError {
ctx.save();
ctx.move_to(216.5, 742.0);
ctx.set_source_rgb(0, 0, 0);
@@ -278,7 +278,7 @@ public class InvoicePDF {
ctx.restore();
}
- private void draw_footer_text_right(Cairo.Context ctx) {
+ private void draw_footer_text_right(Cairo.Context ctx) throws DBusError, IOError, KeyFileError {
ctx.save();
ctx.move_to(410.0, 742.0);
ctx.set_source_rgb(0, 0, 0);
@@ -331,7 +331,7 @@ public class InvoicePDF {
return "Moin";
}
- private void draw_first_page_text(Cairo.Context ctx) {
+ private void draw_first_page_text(Cairo.Context ctx) throws IOError {
ctx.save();
ctx.move_to(56.5, 352.5);
ctx.set_source_rgb(0, 0, 0);
@@ -616,7 +616,7 @@ public class InvoicePDF {
return Cairo.Status.SUCCESS;
}
- public uint8[] generate() throws InvoicePDFError {
+ public uint8[] generate() throws DBusError, IOError, InvoicePDFError, KeyFileError {
data = null;
var document = new Cairo.PdfSurface.for_stream(pdf_write, width, height);
@@ -657,7 +657,7 @@ public class InvoicePDF {
return data;
}
- public void clear() {
+ public void clear() throws DBusError, IOError {
invoice_date = 0;
invoice_id = "";
invoice_recipient.firstname = "";
diff --git a/src/pdf-stock/pdf-stock-interface.vala b/src/pdf-stock/pdf-stock-interface.vala
index 415916f..5168250 100644
--- a/src/pdf-stock/pdf-stock-interface.vala
+++ b/src/pdf-stock/pdf-stock-interface.vala
@@ -15,5 +15,5 @@
[DBus (name = "io.mainframe.shopsystem.StockPDF")]
public interface PDFStock : Object {
- public abstract uint8[] generate() throws IOError;
+ public abstract uint8[] generate() throws DBusError, IOError;
}
diff --git a/src/pdf-stock/pdf-stock.vala b/src/pdf-stock/pdf-stock.vala
index 1d16e4a..2edba88 100644
--- a/src/pdf-stock/pdf-stock.vala
+++ b/src/pdf-stock/pdf-stock.vala
@@ -154,7 +154,7 @@ public class StockPDF {
return Cairo.Status.SUCCESS;
}
- public uint8[] generate() {
+ public uint8[] generate() throws DBusError, IOError {
data = null;
var surface = new Cairo.PdfSurface.for_stream(pdf_write, a4w, a4h);
diff --git a/src/pgp/main.vala b/src/pgp/main.vala
index c866c7b..332bcd5 100644
--- a/src/pgp/main.vala
+++ b/src/pgp/main.vala
@@ -24,6 +24,8 @@ public static int main(string[] args) {
try {
cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
pgp = new PGPKeyArchive(cfg.get_string("PGP", "keyring"));
+ } catch(DBusError e) {
+ error("DBusError: %s\n", e.message);
} catch(IOError e) {
error("IOError: %s\n", e.message);
} catch(KeyFileError e) {
diff --git a/src/pgp/pgp-interface.vala b/src/pgp/pgp-interface.vala
index 62bfe67..2621fae 100644
--- a/src/pgp/pgp-interface.vala
+++ b/src/pgp/pgp-interface.vala
@@ -15,7 +15,7 @@
[DBus (name = "io.mainframe.shopsystem.PGP")]
public interface PGP : Object {
- public abstract string[] import_archive(uint8[] data) throws IOError;
- public abstract string[] list_keys() throws IOError;
- public abstract string get_key(string fingerprint) throws IOError;
+ public abstract string[] import_archive(uint8[] data) throws IOError, DBusError;
+ public abstract string[] list_keys() throws IOError, DBusError;
+ public abstract string get_key(string fingerprint) throws IOError, DBusError;
}
diff --git a/src/pgp/pgp.vala b/src/pgp/pgp.vala
index bb48c61..560b00f 100644
--- a/src/pgp/pgp.vala
+++ b/src/pgp/pgp.vala
@@ -42,7 +42,7 @@ public class PGPKeyArchive {
gpg.set_armor(true);
}
- public string[] import_archive(uint8[] data) {
+ public string[] import_archive(uint8[] data) throws DBusError, IOError {
string[] result = {};
unowned Archive.Entry entry;
var archive = new Archive.Read();
@@ -93,7 +93,7 @@ public class PGPKeyArchive {
return result;
}
- public string[] list_keys() {
+ public string[] list_keys() throws DBusError, IOError {
string[] result = {};
GPG.Key key;
@@ -108,7 +108,7 @@ public class PGPKeyArchive {
return result;
}
- public string get_key(string fingerprint) {
+ public string get_key(string fingerprint) throws DBusError, IOError {
GPG.Data keydata;
GPG.Data.create(out keydata);
diff --git a/src/scanner-session/scannersession.vala b/src/scanner-session/scannersession.vala
index 7cc9cc4..c1b513e 100644
--- a/src/scanner-session/scannersession.vala
+++ b/src/scanner-session/scannersession.vala
@@ -56,7 +56,7 @@ public class ScannerSessionImplementation {
msg(type, message);
}
- private bool login(int user) throws IOError {
+ private bool login(int user) throws DBusError, IOError {
this.user = user;
try {
this.name = db.get_username(user);
@@ -79,7 +79,7 @@ public class ScannerSessionImplementation {
return true;
}
- private ScannerSessionCodeType getCodeType(string scannerdata){
+ private ScannerSessionCodeType getCodeType(string scannerdata) {
if(scannerdata.has_prefix("USER ")){
return ScannerSessionCodeType.USER;
} else if(scannerdata == "GUEST") {
@@ -103,7 +103,7 @@ public class ScannerSessionImplementation {
}
}
- private void play_audio(AudioType audioType){
+ private void play_audio(AudioType audioType) throws DBusError, IOError {
switch (audioType) {
case AudioType.ERROR:
audio.play_system("error.ogg");
@@ -123,7 +123,7 @@ public class ScannerSessionImplementation {
}
}
- private ScannerResult handleReadyState(string scannerdata) throws DatabaseError, IOError{
+ private ScannerResult handleReadyState(string scannerdata) throws DatabaseError, DBusError, IOError {
ScannerSessionCodeType codeType = getCodeType(scannerdata);
ScannerResult scannerResult = ScannerResult();
switch (codeType) {
@@ -199,7 +199,7 @@ public class ScannerSessionImplementation {
}
}
- private ScannerResult handleUserState(string scannerdata) throws DatabaseError, IOError {
+ private ScannerResult handleUserState(string scannerdata) throws DatabaseError, DBusError, IOError {
ScannerSessionCodeType codeType = getCodeType(scannerdata);
ScannerResult scannerResult = ScannerResult();
switch (codeType) {
@@ -268,7 +268,7 @@ public class ScannerSessionImplementation {
return scannerResult;
}
- private ScannerResult buyShoppingCard() {
+ private ScannerResult buyShoppingCard() throws DatabaseError, DBusError, IOError {
ScannerResult scannerResult = ScannerResult();
uint8 amountOfItems = 0;
Price totalPrice = 0;
@@ -295,6 +295,8 @@ public class ScannerSessionImplementation {
stdout.printf("scannerdata: %s\n", scannerdata);
if(interpret(scannerdata))
devScanner.blink(1000);
+ } catch(DBusError e) {
+ send_message(MessageType.ERROR, "DBusError: %s", e.message);
} catch(IOError e) {
send_message(MessageType.ERROR, "IOError: %s", e.message);
} catch(DatabaseError e) {
@@ -302,7 +304,7 @@ public class ScannerSessionImplementation {
}
}
- private bool interpret(string scannerdata) throws DatabaseError, IOError {
+ private bool interpret(string scannerdata) throws DatabaseError, DBusError, IOError {
ScannerResult scannerResult = ScannerResult();
switch (state) {
case ScannerSessionState.READY:
@@ -321,7 +323,7 @@ public class ScannerSessionImplementation {
return true;
}
- private ScannerResult logout() {
+ private ScannerResult logout() throws DatabaseError, DBusError, IOError {
ScannerResult scannerResult = ScannerResult();
scannerResult = buyShoppingCard();
logged_in = false;
diff --git a/src/serial-device/main.vala b/src/serial-device/main.vala
index 70d50e2..d8dadf3 100644
--- a/src/serial-device/main.vala
+++ b/src/serial-device/main.vala
@@ -23,6 +23,8 @@ public static int main(string[] args) {
error("IOError: %s\n", e.message);
} catch(KeyFileError e) {
error("Config Error: %s\n", e.message);
+ } catch(DBusError e) {
+ error("DBus Error: %s\n", e.message);
}
Bus.own_name(
diff --git a/src/serial-device/serial-device.vala b/src/serial-device/serial-device.vala
index 549cd74..ae7480b 100644
--- a/src/serial-device/serial-device.vala
+++ b/src/serial-device/serial-device.vala
@@ -272,7 +272,7 @@ public class Device {
/**
* @param duration duration of the blink in 0.1 seconds
*/
- public void blink(uint duration) {
+ public void blink(uint duration) throws IOError, DBusError {
uint size = (byterate/1000) * duration;
var msg = new uint8[size];
Posix.memset(msg, 0xFF, msg.length);
diff --git a/src/web/csv.vala b/src/web/csv.vala
index 5d4daba..6356f77 100644
--- a/src/web/csv.vala
+++ b/src/web/csv.vala
@@ -17,7 +17,7 @@
public class CSVMemberFile {
private UserInfo[] members;
- public Gee.List missing_unblocked_members() throws DatabaseError, IOError {
+ public Gee.List missing_unblocked_members() throws DatabaseError, IOError, DBusError {
var result = new Gee.ArrayList();
var dbusers = db.get_member_ids();
diff --git a/src/web/main.vala b/src/web/main.vala
index 7070e66..e249d81 100644
--- a/src/web/main.vala
+++ b/src/web/main.vala
@@ -44,6 +44,8 @@ public static int main(string[] args) {
error("IOError: %s\n", e.message);
} catch(KeyFileError e) {
error("KeyFileError: %s\n", e.message);
+ } catch(DBusError e) {
+ error("DBusError: %s\n", e.message);
}
stdout.printf("Web Server Port: %u\n", port);
diff --git a/src/web/web.vala b/src/web/web.vala
index f4207ad..9280fef 100644
--- a/src/web/web.vala
+++ b/src/web/web.vala
@@ -34,6 +34,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -54,6 +56,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -123,6 +127,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -177,6 +183,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -282,6 +290,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -322,6 +332,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -409,6 +421,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -527,6 +541,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -594,6 +610,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -627,6 +645,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -650,6 +670,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -731,6 +753,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -782,6 +806,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -840,6 +866,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -886,6 +914,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -919,6 +949,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -967,6 +999,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -1139,6 +1173,8 @@ public class WebServer {
} catch(IOError e) {
stderr.printf(e.message+"\n");
handler_400_fallback(server, msg, path, query, client);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -1163,6 +1199,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -1182,6 +1220,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -1230,6 +1270,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -1302,6 +1344,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
@@ -1328,6 +1372,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
}
@@ -1409,6 +1455,8 @@ public class WebServer {
handler_400(server, msg, path, query, client, e.message);
} catch(IOError e) {
handler_400(server, msg, path, query, client, e.message);
+ } catch(DBusError e) {
+ handler_400(server, msg, path, query, client, e.message);
}
}
diff --git a/src/web/websession.vala b/src/web/websession.vala
index ae3cafc..839d0a2 100644
--- a/src/web/websession.vala
+++ b/src/web/websession.vala
@@ -82,7 +82,7 @@ public class WebSession {
return result;
}
- private void setup_auth(int user) throws DatabaseError, IOError {
+ private void setup_auth(int user) throws DatabaseError, IOError, DBusError {
var auth = db.get_user_auth(user);
this.disabled = db.user_is_disabled(user);
this.superuser = auth.superuser;
@@ -92,7 +92,7 @@ public class WebSession {
this.logged_in = true;
}
- public void logout() throws DatabaseError, IOError {
+ public void logout() throws DatabaseError, IOError, DBusError {
if(logged_in) {
db.set_sessionid(user, "");
superuser = false;
@@ -103,7 +103,7 @@ public class WebSession {
}
}
- public WebSession(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) throws DatabaseError, IOError {
+ public WebSession(Soup.Server server, Soup.Message msg, string path, GLib.HashTable? query, Soup.ClientContext client) throws DatabaseError, IOError, DBusError {
var cookies = Soup.cookies_from_request(msg);
/* Check for existing session */
--
cgit v1.2.3
From 9503b54a12c228dc686f630ca97172662c18c58f Mon Sep 17 00:00:00 2001
From: Johannes Rudolph
Date: Wed, 18 Apr 2018 22:09:52 +0200
Subject: Update Copyright
---
src/curses-ui/clock.vala | 1 +
src/curses-ui/curses-ui.vala | 2 ++
src/curses-ui/logo.vala | 1 +
src/curses-ui/main.vala | 2 ++
src/curses-ui/numbers.vala | 2 ++
src/database/database.vala | 1 +
src/database/db-interface.vala | 1 +
src/input-device/input-device.vala | 1 +
src/input-device/main.vala | 1 +
src/pdf-invoice/pdf-invoice.vala | 1 +
src/scanner-session/scannersession.vala | 1 +
src/serial-device/main.vala | 1 +
src/web/web.vala | 1 +
13 files changed, 16 insertions(+)
diff --git a/src/curses-ui/clock.vala b/src/curses-ui/clock.vala
index dd3ddcd..43ca48b 100644
--- a/src/curses-ui/clock.vala
+++ b/src/curses-ui/clock.vala
@@ -1,4 +1,5 @@
/* Copyright 2013, Sebastian Reichel
+ * Copyright 2018, Malte Modler
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/curses-ui/curses-ui.vala b/src/curses-ui/curses-ui.vala
index f7f6239..0cd6281 100644
--- a/src/curses-ui/curses-ui.vala
+++ b/src/curses-ui/curses-ui.vala
@@ -1,4 +1,6 @@
/* Copyright 2013, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
+ * Copyright 2018, Malte Modler
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/curses-ui/logo.vala b/src/curses-ui/logo.vala
index c795981..6a75ba1 100644
--- a/src/curses-ui/logo.vala
+++ b/src/curses-ui/logo.vala
@@ -1,4 +1,5 @@
/* Copyright 2013, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/curses-ui/main.vala b/src/curses-ui/main.vala
index 7c2e762..8a46db8 100644
--- a/src/curses-ui/main.vala
+++ b/src/curses-ui/main.vala
@@ -1,4 +1,6 @@
/* Copyright 2013, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
+ * Copyright 2018, Malte Modler
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/curses-ui/numbers.vala b/src/curses-ui/numbers.vala
index 200cf63..5cbcd23 100644
--- a/src/curses-ui/numbers.vala
+++ b/src/curses-ui/numbers.vala
@@ -1,4 +1,6 @@
/* Copyright 2013, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
+ * Copyright 2018, Malte Modler
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/database/database.vala b/src/database/database.vala
index 8106742..121c793 100644
--- a/src/database/database.vala
+++ b/src/database/database.vala
@@ -1,4 +1,5 @@
/* Copyright 2012-2013, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/database/db-interface.vala b/src/database/db-interface.vala
index 5d0fdfb..2a6603f 100644
--- a/src/database/db-interface.vala
+++ b/src/database/db-interface.vala
@@ -1,4 +1,5 @@
/* Copyright 2013, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/input-device/input-device.vala b/src/input-device/input-device.vala
index d3a7691..1c558d0 100644
--- a/src/input-device/input-device.vala
+++ b/src/input-device/input-device.vala
@@ -1,4 +1,5 @@
/* Copyright 2015, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/input-device/main.vala b/src/input-device/main.vala
index 757de46..372418e 100644
--- a/src/input-device/main.vala
+++ b/src/input-device/main.vala
@@ -1,4 +1,5 @@
/* Copyright 2015, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/pdf-invoice/pdf-invoice.vala b/src/pdf-invoice/pdf-invoice.vala
index a5696b1..d92c93f 100644
--- a/src/pdf-invoice/pdf-invoice.vala
+++ b/src/pdf-invoice/pdf-invoice.vala
@@ -1,4 +1,5 @@
/* Copyright 2013, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/scanner-session/scannersession.vala b/src/scanner-session/scannersession.vala
index c1b513e..fc385f6 100644
--- a/src/scanner-session/scannersession.vala
+++ b/src/scanner-session/scannersession.vala
@@ -1,4 +1,5 @@
/* Copyright 2012-2013, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/serial-device/main.vala b/src/serial-device/main.vala
index d8dadf3..5d5a107 100644
--- a/src/serial-device/main.vala
+++ b/src/serial-device/main.vala
@@ -1,4 +1,5 @@
/* Copyright 2013, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/src/web/web.vala b/src/web/web.vala
index 9280fef..695ec70 100644
--- a/src/web/web.vala
+++ b/src/web/web.vala
@@ -1,4 +1,5 @@
/* Copyright 2012, Sebastian Reichel
+ * Copyright 2017-2018, Johannes Rudolph
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
--
cgit v1.2.3
From fb75ceb1c5a415cc6667043b1123e42586b1d9b1 Mon Sep 17 00:00:00 2001
From: Sebastian Reichel
Date: Thu, 28 Jun 2018 01:29:41 +0200
Subject: pgp: replace deprecated Archive.Read.support_compression_all with
Archive.Read.support_filter_all
---
src/pgp/pgp.vala | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/pgp/pgp.vala b/src/pgp/pgp.vala
index 560b00f..2a5b160 100644
--- a/src/pgp/pgp.vala
+++ b/src/pgp/pgp.vala
@@ -48,7 +48,7 @@ public class PGPKeyArchive {
var archive = new Archive.Read();
/* support all formats & compression types */
- archive.support_compression_all();
+ archive.support_filter_all();
archive.support_format_all();
/* load test archive for now */
--
cgit v1.2.3
From 8e60fa48a8535da9eaa34ef35b5040c2eb68fc3b Mon Sep 17 00:00:00 2001
From: Sebastian Reichel
Date: Thu, 28 Jun 2018 21:35:23 +0200
Subject: all: I18N support
This adds I18N support using standard gettext system together
with an initial German translation. This can be used to search
locales in some directory:
Intl.bindtextdomain("shopsystem", "/home/sre/src/serial-barcode-scanner");
And this can be used to switch language at runtime:
Intl.setlocale(LocaleCategory.ALL, "");
---
.gitignore | 2 +
Makefile | 14 +-
locale/de.po | 481 ++++++++++++++++++++++++++++++++
src/audio/Makefile | 2 +-
src/audio/audio.vala | 4 +-
src/audio/main.vala | 17 +-
src/backup/Makefile | 2 +-
src/backup/main.vala | 5 +-
src/cli/Makefile | 3 +-
src/cli/cli.vala | 11 +-
src/cli/main.vala | 26 +-
src/config/Makefile | 2 +-
src/config/config.vala | 2 +-
src/config/main.vala | 11 +-
src/curses-ui/Makefile | 2 +-
src/curses-ui/dialog.vala | 6 +-
src/curses-ui/logo.vala | 4 +-
src/curses-ui/main.vala | 13 +-
src/curses-ui/message_box.vala | 4 +-
src/curses-ui/message_box_overlay.vala | 6 +-
src/database/Makefile | 2 +-
src/database/database.vala | 88 +++---
src/database/main.vala | 21 +-
src/input-device/Makefile | 2 +-
src/input-device/input-device.vala | 12 +-
src/input-device/main.vala | 19 +-
src/invoice/Makefile | 4 +-
src/invoice/invoice.vala | 8 +-
src/invoice/main.vala | 11 +-
src/invoice/single.vala | 4 +-
src/mail/Makefile | 2 +-
src/mail/mailer.vala | 14 +-
src/mail/main.vala | 13 +-
src/pdf-invoice/Makefile | 4 +-
src/pdf-invoice/main.vala | 17 +-
src/pdf-invoice/pdf-invoice.vala | 20 +-
src/pdf-stock/Makefile | 4 +-
src/pdf-stock/main.vala | 11 +-
src/pdf-stock/test.vala | 6 +-
src/pgp/Makefile | 2 +-
src/pgp/main.vala | 18 +-
src/pgp/pgp.vala | 2 +-
src/scanner-session/Makefile | 2 +-
src/scanner-session/main.vala | 11 +-
src/scanner-session/scannersession.vala | 46 ++-
src/serial-device/Makefile | 2 +-
src/serial-device/main.vala | 17 +-
src/serial-device/serial-device.vala | 20 +-
src/web/Makefile | 2 +-
src/web/main.vala | 21 +-
src/web/template.vala | 24 +-
src/web/web.vala | 4 +-
src/web/websession.vala | 8 +-
53 files changed, 795 insertions(+), 263 deletions(-)
create mode 100644 locale/de.po
diff --git a/.gitignore b/.gitignore
index 359056d..026f4b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ ktt-shopsystem.cfg
config.mk
config.h
.DS_Store
+locale/de.mo
+locale/messages.pot
diff --git a/Makefile b/Makefile
index d551b6b..ee08cef 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,18 @@
all:
@cd src && make --no-print-directory all
+gettext: locale/de/LC_MESSAGES/shopsystem.mo
+
+locale/de/LC_MESSAGES/shopsystem.mo: locale/de.po
+ install -d locale/de/LC_MESSAGES/
+ msgfmt -o $@ $<
+
+locale/%.po: locale/messages.pot
+ msgmerge -N --backup=off --update $@ $<
+
+locale/messages.pot: */*/*.vala
+ xgettext --language=vala --from-code=utf-8 --keyword=_ --escape --sort-output -o $@ */*/*.vala
+
clean:
@cd src && make --no-print-directory clean
@@ -14,4 +26,4 @@ shop.db: sql/tables.sql sql/views.sql sql/trigger.sql
sqlite3 shop.db < $$file; \
done
-.PHONY: all clean install
+.PHONY: all clean install gettext
diff --git a/locale/de.po b/locale/de.po
new file mode 100644
index 0000000..6aca21f
--- /dev/null
+++ b/locale/de.po
@@ -0,0 +1,481 @@
+# Copyright (C) 2018 Sebastian Reichel
+# This file is distributed under the same license as the main package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: shopsystem 0.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-06-28 23:13+0200\n"
+"PO-Revision-Date: 2018-06-28 21:14+0200\n"
+"Last-Translator: Sebastian Reichel \n"
+"Language-Team: German\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/curses-ui/message_box.vala:52
+msgid ""
+"\n"
+"Date Changed: "
+msgstr ""
+"\n"
+"Datumswechsel: "
+
+#: src/scanner-session/scannersession.vala:289
+#, c-format
+msgid "%s bought %d items for %s €"
+msgstr "%s hat gerade %d Posten für %s € gekauft"
+
+#: src/web/template.vala:53 src/web/template.vala:55 src/web/template.vala:57
+#: src/web/template.vala:59 src/web/template.vala:82
+#, c-format
+msgid "%s could not be loaded!"
+msgstr "%s konnte nicht geladen werden!"
+
+#: src/web/template.vala:40 src/web/template.vala:43 src/web/template.vala:46
+#: src/web/template.vala:49 src/web/template.vala:78
+#, c-format
+msgid "%s not found!"
+msgstr "%s wurde nicht gefunden!"
+
+#: src/pdf-invoice/pdf-invoice.vala:591
+#, c-format
+msgid "Article name \"%s\" does not fit on a single page!"
+msgstr "Artikelname \"%s\" passt nicht auf eine Seite"
+
+#: src/serial-device/serial-device.vala:39
+msgid "Can't read lock file!\n"
+msgstr "Konnte Lock-Datei nicht lesen\n"
+
+#: src/audio/audio.vala:38
+msgid "Cannot find alsa GStreamer plugin"
+msgstr "GStreamer Plugin für ALSA konnte nicht gefunden werden"
+
+#: src/audio/audio.vala:42
+msgid "Cannot find playbin2 GStreamer plugin"
+msgstr "GStreamer Plugin playbin2 konnte nicht gefunden werden"
+
+#: src/audio/main.vala:39 src/database/main.vala:29
+#: src/input-device/main.vala:31 src/pdf-invoice/main.vala:30
+#: src/pgp/main.vala:35 src/serial-device/main.vala:29
+#, c-format
+msgid "Config Error: %s\n"
+msgstr "Konfigurationsfehler: %s\n"
+
+#: src/serial-device/serial-device.vala:211
+#, c-format
+msgid "Convert Error: %s"
+msgstr "Konvertierungsfehler"
+
+#: src/audio/main.vala:30 src/cli/main.vala:37 src/config/main.vala:32
+#: src/database/main.vala:40 src/input-device/main.vala:42
+#: src/pdf-invoice/main.vala:39 src/pdf-stock/main.vala:26 src/pgp/main.vala:44
+#: src/scanner-session/main.vala:28 src/serial-device/main.vala:40
+msgid "Could not acquire name\n"
+msgstr "Konnte DBus Namen nicht erlangen\n"
+
+#: src/input-device/input-device.vala:40
+#: src/serial-device/serial-device.vala:165
+msgid "Could not bind IOChannel"
+msgstr "IOChannel Verbindung konnte nicht hergestellt werden"
+
+#: src/serial-device/serial-device.vala:59
+#, c-format
+msgid "Could not create lock file: %s!\n"
+msgstr "Konnte Lock-Datei nicht erstellen: %s!\n"
+
+#: src/pdf-invoice/pdf-invoice.vala:81
+#, c-format
+msgid "Could not load SVG: %s\n"
+msgstr "Konnte SVG nicht laden: %s\n"
+
+#: src/config/config.vala:26
+#, c-format
+msgid "Could not load configuration file: %s"
+msgstr "Konnte Konfigurationsdatei nicht laden: %s"
+
+#: src/invoice/invoice.vala:338 src/pdf-invoice/pdf-invoice.vala:379
+#, c-format
+msgid "Could not open VAT template: %s"
+msgstr "Konnte VAT-Template nicht öffnen: %s"
+
+#: src/serial-device/serial-device.vala:55
+msgid "Could not open device!\n"
+msgstr "Konnte Gerätedatei nicht öffnen!\n"
+
+#: src/invoice/invoice.vala:252 src/invoice/invoice.vala:319
+#, c-format
+msgid "Could not open invoice template: %s"
+msgstr "Konnte Rechnungs-Template nicht öffnen: %s"
+
+#: src/audio/main.vala:53 src/cli/main.vala:68 src/config/main.vala:43
+#: src/database/main.vala:51 src/input-device/main.vala:53
+#: src/input-device/main.vala:58 src/pdf-stock/main.vala:37
+#: src/pgp/main.vala:55 src/scanner-session/main.vala:41
+#: src/serial-device/main.vala:51
+msgid "Could not register service\n"
+msgstr "Konnte DBus-Service nicht registrieren\n"
+
+#: src/pdf-invoice/main.vala:50
+#, c-format
+msgid "Could not register service: %s\n"
+msgstr "Konnte DBus-Servicenicht registieren: %s\n"
+
+#: src/web/web.vala:1482
+msgid "Could not setup webserver!"
+msgstr "Konnte Webserver nicht konfigurieren!"
+
+#: src/web/main.vala:64
+#, c-format
+msgid "Could not start Webserver: %s\n"
+msgstr "Konnte Webserver nicht starten: %s\n"
+
+#: src/scanner-session/scannersession.vala:300
+#, c-format
+msgid "DBus Error: %s"
+msgstr "DBus Fehler: %s"
+
+#: src/audio/main.vala:41 src/database/main.vala:31
+#: src/input-device/main.vala:33 src/pdf-invoice/main.vala:26
+#: src/pgp/main.vala:31 src/serial-device/main.vala:31 src/web/main.vala:51
+#, c-format
+msgid "DBus Error: %s\n"
+msgstr "DBus Fehler: %s\n"
+
+#: src/scanner-session/scannersession.vala:304
+#, c-format
+msgid "Database Error: %s"
+msgstr "Datenbank Fehler: %s"
+
+#: src/scanner-session/scannersession.vala:66
+#, c-format
+msgid "Error (user=%d): %s"
+msgstr "Fehler (Nutzer %d): %s"
+
+#: src/cli/main.vala:51
+#, c-format
+msgid "Error sending command: %s"
+msgstr "Fehler beim senden des Kommandos: %s"
+
+#: src/pgp/pgp.vala:123
+msgid "Error!\n"
+msgstr "Fehler!\n"
+
+#: src/curses-ui/logo.vala:44 src/scanner-session/scannersession.vala:178
+#: src/scanner-session/scannersession.vala:225
+#, c-format
+msgid "Error: %s"
+msgstr "Fehler: %s"
+
+#: src/backup/main.vala:41 src/invoice/main.vala:51 src/invoice/main.vala:58
+#: src/invoice/single.vala:51 src/invoice/single.vala:58 src/mail/main.vala:26
+#: src/web/web.vala:1143
+#, c-format
+msgid "Error: %s\n"
+msgstr "Fehler: %s\n"
+
+#: src/mail/main.vala:35
+msgid "Error: Could not acquire name\n"
+msgstr "Fehler: Konnte DBus-Namen nicht bekommen\n"
+
+#: src/mail/main.vala:47
+msgid "Error: Could not register service\n"
+msgstr "Fehler: Konnte DBus-Service nicht registrieren\n"
+
+#: src/database/database.vala:81
+msgid "Error: could not open database!"
+msgstr "Fehler: Konnte Datenbank nicht öffnen!"
+
+#: src/database/database.vala:28
+#, c-format
+msgid "Error: could not prepare statement: %s"
+msgstr "Fehler: Konnte SQL-Anweisung nicht erstellen: %s"
+
+#: src/scanner-session/scannersession.vala:174
+#: src/scanner-session/scannersession.vala:221
+#, c-format
+msgid "Error: unknown product: %llu"
+msgstr "Fehler: unbekanntes Produkt: %llu"
+
+#: src/serial-device/serial-device.vala:163
+msgid "Failed to set encoding"
+msgstr "Fehler beim setzten der Zeichenkodierung"
+
+#: src/curses-ui/logo.vala:31
+#, c-format
+msgid "File '%s' doesn't exist.\n"
+msgstr "Datei '%s' existiert nicht.\n"
+
+#: src/input-device/input-device.vala:43 src/pdf-stock/test.vala:24
+#, c-format
+msgid "File Error: %s"
+msgstr "Datei Fehler: %s"
+
+#: src/pdf-invoice/pdf-invoice.vala:387
+#, c-format
+msgid "File Error: %s\n"
+msgstr "Datei Fehler: %s\n"
+
+#: src/scanner-session/scannersession.vala:20
+msgid "Guest"
+msgstr "Gast"
+
+#: src/pdf-stock/test.vala:22 src/scanner-session/scannersession.vala:302
+#, c-format
+msgid "IO Error: %s"
+msgstr "EA Fehler: %s"
+
+#: src/audio/main.vala:37 src/curses-ui/main.vala:60 src/database/main.vala:27
+#: src/input-device/main.vala:29 src/pdf-invoice/main.vala:28
+#: src/pgp/main.vala:33 src/scanner-session/scannersession.vala:49
+#: src/serial-device/main.vala:27 src/web/main.vala:47
+#, c-format
+msgid "IO Error: %s\n"
+msgstr "EA Fehler: %s\n"
+
+#: src/serial-device/serial-device.vala:207
+#, c-format
+msgid "IOChannel Error: %s"
+msgstr "IOChannel Fehler: %s"
+
+#: src/serial-device/serial-device.vala:168
+#, c-format
+msgid "IOChannel: %s"
+msgstr "IOChannel: %s"
+
+#: src/input-device/input-device.vala:27
+msgid "Ignoring InputDevice!\n"
+msgstr "Eingabegerät wird ignoriert\n"
+
+#: src/scanner-session/scannersession.vala:168
+#: src/scanner-session/scannersession.vala:215
+msgid "Internal Error!"
+msgstr "Interner Fehler!"
+
+#: src/web/main.vala:44 src/web/main.vala:49
+#, c-format
+msgid "KeyFile Error: %s\n"
+msgstr "Schlüsseldatei Fehler: %s\n"
+
+#: src/scanner-session/scannersession.vala:149
+msgid "Login as GUEST"
+msgstr "Login als Gast"
+
+#: src/scanner-session/scannersession.vala:141
+#, c-format
+msgid "Login failed (User ID = %d)"
+msgstr "Login fehlgeschlagen (User ID = %d)"
+
+#: src/scanner-session/scannersession.vala:155
+msgid "Login failed as GUEST"
+msgstr "Gast-Login fehlgeschlagen"
+
+#: src/web/websession.vala:165
+#, c-format
+msgid "Login for user id %d failed\n"
+msgstr "Login für Benutzer %d fehlgeschlagen\n"
+
+#: src/scanner-session/scannersession.vala:135
+#, c-format
+msgid "Login: %s (%d)"
+msgstr "Login: %s (%d)"
+
+#: src/input-device/input-device.vala:215
+#: src/serial-device/serial-device.vala:189
+msgid "Lost device"
+msgstr "Gerät verloren"
+
+#: src/pdf-invoice/pdf-invoice.vala:628
+msgid "No invoice ID given!"
+msgstr "Keine Rechnungsnummer übergeben!"
+
+#: src/pdf-invoice/pdf-invoice.vala:631
+msgid "No invoice data given!"
+msgstr "Keine Rechnungsdaten übergeben!"
+
+#: src/pdf-invoice/pdf-invoice.vala:634
+msgid "No invoice date given!"
+msgstr "Kein Rechnungsdatum übergeben!"
+
+#: src/pdf-invoice/pdf-invoice.vala:637
+msgid "No invoice recipient given!"
+msgstr "Kein Rechnungsempfänger übergeben!"
+
+#: src/scanner-session/scannersession.vala:253
+msgid "No more Items on your Shopping Cart"
+msgstr "Keine weiteren Gegenstände im Einkaufswagen"
+
+#: src/mail/mailer.vala:141 src/mail/mailer.vala:149
+msgid "No such mail"
+msgstr "Mail wurde nicht gefunden"
+
+#: src/database/database.vala:611
+msgid "No such session available in database!"
+msgstr "Keine solche Session in der Datenbank verfügbar!"
+
+#: src/database/database.vala:710 src/database/database.vala:722
+msgid "No such user available in database!"
+msgstr "Kein solcher Nutzer in der Datenbank verfügbar!"
+
+#: src/cli/main.vala:25
+#, c-format
+msgid ""
+"Nothing to send.\n"
+"Usage: %s \n"
+"Example: %s \"USER 1\" \"LOGOUT\"\n"
+msgstr ""
+"Nichts zu senden.\n"
+"Benutzung: %s \n"
+"Beispiel: %s \"USER 1\" \"LOGOUT\"\n"
+
+#: src/invoice/main.vala:20
+msgid "Possible values for : temporary, final\n"
+msgstr "Mögliche Werte für : temporary, final\n"
+
+#: src/pdf-invoice/pdf-invoice.vala:471
+msgid "Prices > 9999.99€ are not supported!"
+msgstr "Preise > 9999,99€ werden nicht unterstützt!"
+
+#: src/database/database.vala:487
+#, c-format
+msgid "Remove purchase of %s"
+msgstr "Einkaufsposten %s wurde entfernt"
+
+#: src/mail/mailer.vala:180
+#, c-format
+msgid "Reply from SMTP-Server: %s"
+msgstr "Antwort vom SMTP-Server: %s"
+
+#: src/cli/cli.vala:24
+#, c-format
+msgid "Sending: %s\n"
+msgstr "Sende: %s\n"
+
+#: src/curses-ui/main.vala:72
+msgid "Shop System has been started"
+msgstr "Shop System wurde gestartet"
+
+#: src/curses-ui/main.vala:78
+msgid "Stopping Shop System"
+msgstr "Shop System wird gestoppt"
+
+#: src/web/main.vala:55
+#, c-format
+msgid "TLS certificate: %s\n"
+msgstr "TLS Zertifikat: %s\n"
+
+#: src/web/main.vala:56
+#, c-format
+msgid "TLS private key: %s\n"
+msgstr "TLS Privater Schlüssel: %s\n"
+
+#: src/invoice/invoice.vala:314
+msgid "Unknown MessageType"
+msgstr "Unbekannter Nachrichten Typ"
+
+#: src/invoice/main.vala:19
+#, c-format
+msgid "Usage: %s [timestamp]\n"
+msgstr "Benutzung: %s [timestamp]\n"
+
+#: src/web/main.vala:54
+#, c-format
+msgid "Web Server Port: %u\n"
+msgstr "Webserver Port: %u\n"
+
+#: src/pdf-invoice/pdf-invoice.vala:475
+msgid "Years after 9999 are not supported!"
+msgstr "Jahre nach 9999 werden nicht unterstützt!"
+
+#: src/scanner-session/scannersession.vala:240
+#, c-format
+msgid "article added to shopping card: %s (%s €)"
+msgstr "Artikel %s (%s €) wurde zum Warenkorb hinzugefügt"
+
+#: src/scanner-session/scannersession.vala:189
+#, c-format
+msgid "article info: %s (Member: %s €, Guest: %s €"
+msgstr "Artikelinfo: %s (Mitglied: %s €, Gast: %s €"
+
+#: src/input-device/input-device.vala:249
+#, c-format
+msgid "barcode: %s\n"
+msgstr "Barcode: %s\n"
+
+#: src/mail/mailer.vala:107
+msgid "could not configure STARTTLS"
+msgstr "Konnte STARTTLS nicht konfigurieren"
+
+#: src/web/template.vala:61 src/web/template.vala:84
+msgid "could not load templates!"
+msgstr "Konnte Templates nicht laden!"
+
+#: src/curses-ui/main.vala:27 src/curses-ui/main.vala:29
+#, c-format
+msgid "could not play audio: %s"
+msgstr "Konnte Audio Datei nicht abspielen: %s"
+
+#: src/mail/mailer.vala:99
+msgid "could not setup server"
+msgstr "Konnte Server nicht konfigurieren"
+
+#: src/mail/mailer.vala:176
+msgid "eSMTP: Start Session failed!"
+msgstr "eSMTP: Session Start fehlgeschlagen!"
+
+#: src/database/database.vala:370 src/database/database.vala:387
+#: src/database/database.vala:403 src/database/database.vala:419
+#: src/database/database.vala:435 src/database/database.vala:448
+#: src/database/database.vala:470 src/database/database.vala:492
+#: src/database/database.vala:500 src/database/database.vala:527
+#: src/database/database.vala:541 src/database/database.vala:556
+#: src/database/database.vala:583 src/database/database.vala:591
+#: src/database/database.vala:601 src/database/database.vala:638
+#: src/database/database.vala:675 src/database/database.vala:689
+#: src/database/database.vala:700 src/database/database.vala:737
+#: src/database/database.vala:765 src/database/database.vala:884
+#: src/database/database.vala:892 src/database/database.vala:913
+#: src/database/database.vala:919 src/database/database.vala:927
+#: src/database/database.vala:1028 src/database/database.vala:1079
+#: src/database/database.vala:1129 src/database/database.vala:1212
+#, c-format
+msgid "internal error: %d"
+msgstr "Interner Fehler: %d"
+
+#: src/scanner-session/scannersession.vala:249
+#, c-format
+msgid "removed last Item from Shopping Cart: %s"
+msgstr "Letzter Posten wurde aus dem Warenkorb entfernt: %s"
+
+#: src/serial-device/serial-device.vala:43
+msgid "serial device is locked!\n"
+msgstr "Serielles Gerät ist blockiert!\n"
+
+#: src/mail/mailer.vala:79
+msgid "server or port configuration is missing"
+msgstr "Server oder Port Konfiguration fehlt"
+
+#: src/input-device/input-device.vala:224
+msgid "short read!\n"
+msgstr "Unvollständige Daten aus Gerätedatei gelesen\n"
+
+#: src/database/database.vala:490
+msgid "undo not possible without purchases"
+msgstr "Rückgängig nicht möglich ohne Einkäufe"
+
+#: src/database/database.vala:385 src/database/database.vala:401
+#: src/database/database.vala:417 src/database/database.vala:433
+#: src/database/database.vala:468
+#, c-format
+msgid "unknown product: %llu"
+msgstr "Unbekanntes Produkt: %llu"
+
+#: src/database/database.vala:1210
+#, c-format
+msgid "unknown rfid: %s"
+msgstr "Unbekannter RFID: %s"
+
+#: src/database/database.vala:636
+msgid "user not found"
+msgstr "User nicht gefunden"
diff --git a/src/audio/Makefile b/src/audio/Makefile
index 950287a..31d373d 100644
--- a/src/audio/Makefile
+++ b/src/audio/Makefile
@@ -2,7 +2,7 @@ all: audio
@echo > /dev/null
audio: main.vala audio.vala audio-interface.vala ../config/config-interface.vala
- valac -X -w -o $@ --pkg gstreamer-1.0 --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg gstreamer-1.0 --pkg gio-2.0 $^
clean:
rm -rf audio
diff --git a/src/audio/audio.vala b/src/audio/audio.vala
index ccced20..679e5c7 100644
--- a/src/audio/audio.vala
+++ b/src/audio/audio.vala
@@ -35,11 +35,11 @@ public class AudioPlayerImplementation {
var alsa = Gst.ElementFactory.make("alsasink", "alsa");
if (alsa == null)
- throw new GLib.IOError.FAILED("Cannot find alsa GStreamer plugin");
+ throw new GLib.IOError.FAILED(_("Cannot find alsa GStreamer plugin"));
p = Gst.ElementFactory.make("playbin", "player");
if (p == null)
- throw new GLib.IOError.FAILED("Cannot find playbin2 GStreamer plugin");
+ throw new GLib.IOError.FAILED(_("Cannot find playbin2 GStreamer plugin"));
p.set("audio-sink", alsa);
p.get_bus().add_watch(Priority.DEFAULT, bus_callback);
diff --git a/src/audio/main.vala b/src/audio/main.vala
index 2a7d918..4a1da2f 100644
--- a/src/audio/main.vala
+++ b/src/audio/main.vala
@@ -18,24 +18,27 @@ AudioPlayerImplementation player;
public static int main(string[] args) {
Gst.init(ref args);
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
Bus.own_name(
BusType.SYSTEM,
"io.mainframe.shopsystem.AudioPlayer",
BusNameOwnerFlags.NONE,
- on_bus_aquired,
+ on_bus_acquired,
() => {},
- () => stderr.printf("Could not aquire name\n"));
+ () => stderr.printf(_("Could not acquire name\n")));
try {
Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
var path = cfg.get_string("AUDIO", "path");
player = new AudioPlayerImplementation(path);
} catch(IOError e) {
- error("IOError: %s\n", e.message);
+ error(_("IO Error: %s\n"), e.message);
} catch(KeyFileError e) {
- error("Config Error: %s\n", e.message);
+ error(_("Config Error: %s\n"), e.message);
} catch(DBusError e) {
- error("DBus Error: %s\n", e.message);
+ error(_("DBus Error: %s\n"), e.message);
}
new MainLoop().run();
@@ -43,10 +46,10 @@ public static int main(string[] args) {
return 0;
}
-void on_bus_aquired(DBusConnection con) {
+void on_bus_acquired(DBusConnection con) {
try {
con.register_object("/io/mainframe/shopsystem/audio", player);
} catch(IOError e) {
- stderr.printf("Could not register service\n");
+ stderr.printf(_("Could not register service\n"));
}
}
diff --git a/src/backup/Makefile b/src/backup/Makefile
index de8eee3..f2594c2 100644
--- a/src/backup/Makefile
+++ b/src/backup/Makefile
@@ -2,7 +2,7 @@ all: backup
@echo > /dev/null
backup: main.vala ../mail/mailer-interface.vala ../config/config-interface.vala
- valac -X -w -o $@ --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg gio-2.0 $^
clean:
rm -f backup
diff --git a/src/backup/main.vala b/src/backup/main.vala
index 4aed71f..670e56a 100644
--- a/src/backup/main.vala
+++ b/src/backup/main.vala
@@ -14,6 +14,9 @@
*/
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
try {
Mailer mailer = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Mail", "/io/mainframe/shopsystem/mailer");
Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
@@ -35,7 +38,7 @@ public static int main(string[] args) {
mailer.send_mail(mailpath);
} catch(Error e) {
- stderr.printf("Error: %s\n", e.message);
+ stderr.printf(_("Error: %s\n"), e.message);
}
return 0;
diff --git a/src/cli/Makefile b/src/cli/Makefile
index f042ca8..a66ce8f 100644
--- a/src/cli/Makefile
+++ b/src/cli/Makefile
@@ -2,8 +2,7 @@ all: cli
@echo > /dev/null
cli: main.vala cli.vala cli-interface.vala ../config/config-interface.vala
- valac -X -w -o $@ --pkg linux --pkg posix --pkg gio-2.0 $^
-
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg linux --pkg posix --pkg gio-2.0 $^
clean:
rm -rf cli
diff --git a/src/cli/cli.vala b/src/cli/cli.vala
index c0ba61e..0d0e9d4 100644
--- a/src/cli/cli.vala
+++ b/src/cli/cli.vala
@@ -15,14 +15,13 @@
[DBus (name = "io.mainframe.shopsystem.Cli")]
public class CliImpl {
-
public signal void received_barcode(string barcode);
public CliImpl() {
- }
+ }
- public void send(string msg) throws IOError, DBusError {
- stdout.printf("Sending: %s\n", msg);
- received_barcode(msg);
- }
+ public void send(string msg) throws IOError, DBusError {
+ stdout.printf(_("Sending: %s\n"), msg);
+ received_barcode(msg);
+ }
}
diff --git a/src/cli/main.vala b/src/cli/main.vala
index 377efa1..aacb3ba 100644
--- a/src/cli/main.vala
+++ b/src/cli/main.vala
@@ -18,20 +18,23 @@ MainLoop ml;
string[] commands;
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
if (args.length == 1) {
- stdout.printf("Nothing to send.\nUsage: %s \nExample: %s \"USER 1\" \"LOGOUT\"\n", args[0], args[0]);
+ stdout.printf(_("Nothing to send.\nUsage: %s \nExample: %s \"USER 1\" \"LOGOUT\"\n"), args[0], args[0]);
return 0;
}
commands = args[1:args.length];
-
+
cli = new CliImpl();
Bus.own_name(
BusType.SYSTEM,
"io.mainframe.shopsystem.Cli",
BusNameOwnerFlags.NONE,
- on_bus_aquired,
- on_name_aquired,
- () => stderr.printf("Could not aquire name\n"));
+ on_bus_acquired,
+ on_name_acquired,
+ () => stderr.printf(_("Could not acquire name\n")));
ml = new MainLoop();
@@ -40,12 +43,12 @@ public static int main(string[] args) {
return 0;
}
-void on_name_aquired() {
+void on_name_acquired() {
foreach (string cmd in commands) {
try {
cli.send(cmd);
} catch (Error e) {
- stderr.printf("Error sending command: %s", e.message);
+ stderr.printf(_("Error sending command: %s"), e.message);
}
}
@@ -55,15 +58,14 @@ void on_name_aquired() {
ml.quit ();
return false;
});
- time.attach (ml.get_context ());
-
+ time.attach (ml.get_context ());
}
-void on_bus_aquired(DBusConnection con) {
+void on_bus_acquired(DBusConnection con) {
try {
- con.register_object("/io/mainframe/shopsystem/cli", cli);
+ con.register_object("/io/mainframe/shopsystem/cli", cli);
} catch(IOError e) {
- stderr.printf("Could not register service\n");
+ stderr.printf(_("Could not register service\n"));
}
}
diff --git a/src/config/Makefile b/src/config/Makefile
index d93d5df..bda0cbb 100644
--- a/src/config/Makefile
+++ b/src/config/Makefile
@@ -2,7 +2,7 @@ all: config
@echo > /dev/null
config: main.vala config.vala config-interface.vala
- valac -X -w -o $@ --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg gio-2.0 $^
clean:
rm -rf config
diff --git a/src/config/config.vala b/src/config/config.vala
index b25b667..bc476f4 100644
--- a/src/config/config.vala
+++ b/src/config/config.vala
@@ -23,7 +23,7 @@ public class Cfg {
this.file = new KeyFile();
this.file.load_from_file(file, KeyFileFlags.NONE);
} catch(Error e) {
- error("Could not load configuration file: %s", e.message);
+ error(_("Could not load configuration file: %s"), e.message);
}
}
diff --git a/src/config/main.vala b/src/config/main.vala
index 5ae15be..c24b572 100644
--- a/src/config/main.vala
+++ b/src/config/main.vala
@@ -16,6 +16,9 @@
Cfg cfg;
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
string binarylocation = File.new_for_path(args[0]).get_parent().get_path();
cfg = new Cfg(binarylocation + "/../../ktt-shopsystem.cfg");
@@ -24,19 +27,19 @@ public static int main(string[] args) {
BusType.SYSTEM,
"io.mainframe.shopsystem.Config",
BusNameOwnerFlags.NONE,
- on_bus_aquired,
+ on_bus_acquired,
() => {},
- () => stderr.printf("Could not aquire name\n"));
+ () => stderr.printf(_("Could not acquire name\n")));
new MainLoop().run();
return 0;
}
-void on_bus_aquired(DBusConnection con) {
+void on_bus_acquired(DBusConnection con) {
try {
con.register_object("/io/mainframe/shopsystem/config", cfg);
} catch(IOError e) {
- stderr.printf("Could not register service\n");
+ stderr.printf(_("Could not register service\n"));
}
}
diff --git a/src/curses-ui/Makefile b/src/curses-ui/Makefile
index bfdea51..2fa44b3 100644
--- a/src/curses-ui/Makefile
+++ b/src/curses-ui/Makefile
@@ -2,7 +2,7 @@ all: curses-ui
@echo > /dev/null
curses-ui: *.vala ../audio/audio-interface.vala ../scanner-session/scannersession-interface.vala ../config/config-interface.vala
- valac -X -w -o $@ --pkg curses -X -lncursesw --pkg posix --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg curses -X -lncursesw --pkg posix --pkg gio-2.0 $^
clean:
rm -rf curses-ui
diff --git a/src/curses-ui/dialog.vala b/src/curses-ui/dialog.vala
index 7d2902b..e7ae0b8 100644
--- a/src/curses-ui/dialog.vala
+++ b/src/curses-ui/dialog.vala
@@ -51,7 +51,7 @@ public class Dialog {
if (countdownValue > 0) {
Timeout.add_seconds(1, decrementTitleCountdown);
- }
+ }
}
private void setTitle() {
@@ -61,7 +61,7 @@ public class Dialog {
}
int title_x = (dialogWidth-title.length)/2;
win.mvaddstr(0, title_x, title);
- win.mvaddch(0, title_x-2, Acs.RTEE);
+ win.mvaddch(0, title_x-2, Acs.RTEE);
win.mvaddch(0, title_x-1, ' ');
win.mvaddch(0, title_x+title.length, ' ');
win.mvaddch(0, title_x+title.length+1, Acs.LTEE);
@@ -70,7 +70,7 @@ public class Dialog {
win.mvaddch(0, title_x+title.length+3, Acs.HLINE);
}
- private bool decrementTitleCountdown() {
+ private bool decrementTitleCountdown() {
countdownValue--;
setTitle();
win.refresh();
diff --git a/src/curses-ui/logo.vala b/src/curses-ui/logo.vala
index 6a75ba1..e7f6fb8 100644
--- a/src/curses-ui/logo.vala
+++ b/src/curses-ui/logo.vala
@@ -28,7 +28,7 @@ public class Logo {
var file = File.new_for_path (binarylocation + "/../../logo.txt");
if (!file.query_exists ()) {
- stderr.printf ("File '%s' doesn't exist.\n", file.get_path ());
+ stderr.printf (_("File '%s' doesn't exist.\n"), file.get_path ());
}
try {
@@ -41,7 +41,7 @@ public class Logo {
win.addstr(line+"\n");
}
} catch (Error e) {
- error ("%s", e.message);
+ error (_("Error: %s"), e.message);
}
win.clrtobot();
diff --git a/src/curses-ui/main.vala b/src/curses-ui/main.vala
index 8a46db8..e5ad4c8 100644
--- a/src/curses-ui/main.vala
+++ b/src/curses-ui/main.vala
@@ -24,9 +24,9 @@ private static void play(string file) {
try {
audio.play_system(file);
} catch(IOError e) {
- ui.log(MessageType.WARNING, "could not play audio: %s".printf(e.message));
+ ui.log(MessageType.WARNING, _("could not play audio: %s").printf(e.message));
} catch(DBusError e) {
- ui.log(MessageType.WARNING, "could not play audio: %s".printf(e.message));
+ ui.log(MessageType.WARNING, _("could not play audio: %s").printf(e.message));
}
}
@@ -46,6 +46,9 @@ public void log_handler(string? log_domain, LogLevelFlags flags, string message)
public static int main(string[] args) {
loop = new MainLoop();
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
/* handle unix signals */
Unix.signal_add(Posix.Signal.TERM, handle_signals);
Unix.signal_add(Posix.Signal.INT, handle_signals);
@@ -54,7 +57,7 @@ public static int main(string[] args) {
audio = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.AudioPlayer", "/io/mainframe/shopsystem/audio");
scanner = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.ScannerSession", "/io/mainframe/shopsystem/scanner_session");
} catch(IOError e) {
- error("IOError: %s\n", e.message);
+ error(_("IO Error: %s\n"), e.message);
}
string binarylocation = File.new_for_path(args[0]).get_parent().get_path();
@@ -66,13 +69,13 @@ public static int main(string[] args) {
scanner.msg.connect(msg_handler);
scanner.msg_overlay.connect(msg_overlay_handler);
- ui.log(MessageType.INFO, "Shop System has been started");
+ ui.log(MessageType.INFO, _("Shop System has been started"));
play("startup.ogg");
/* run mainloop */
loop.run();
- ui.log(MessageType.INFO, "Stopping Shop System");
+ ui.log(MessageType.INFO, _("Stopping Shop System"));
play("shutdown.ogg");
/* leave curses mode */
diff --git a/src/curses-ui/message_box.vala b/src/curses-ui/message_box.vala
index d6823fe..70c4d10 100644
--- a/src/curses-ui/message_box.vala
+++ b/src/curses-ui/message_box.vala
@@ -41,7 +41,7 @@ public class MessageBox {
init_pair (INFO_COLOR, Color.WHITE, Color.BLACK);
init_pair (WARN_COLOR, Color.YELLOW, Color.BLACK);
- init_pair (ERROR_COLOR, Color.RED, Color.BLACK);
+ init_pair (ERROR_COLOR, Color.RED, Color.BLACK);
}
public void add(string msg, short color_pair = MessageBox.INFO_COLOR) {
@@ -49,7 +49,7 @@ public class MessageBox {
if(now.get_day_of_year() != last.get_day_of_year() || now.get_year() != last.get_year()) {
string curtime = now.format("%Y-%m-%d");
- subwin.addstr("\nDate Changed: " + curtime);
+ subwin.addstr(_("\nDate Changed: ") + curtime);
}
last = now;
diff --git a/src/curses-ui/message_box_overlay.vala b/src/curses-ui/message_box_overlay.vala
index 721ab06..7b5a09b 100644
--- a/src/curses-ui/message_box_overlay.vala
+++ b/src/curses-ui/message_box_overlay.vala
@@ -37,13 +37,13 @@ public class MessageBoxOverlay {
Timeout.add_seconds(1, decrementTitleCountdown);
}
- private void setTitle() {
+ private void setTitle() {
var title = " === %s (%d) === ".printf(dialogTitle, countdownValue);
int title_x = (COLS - title.length)/2;
- win.mvaddstr(0, title_x, title);
+ win.mvaddstr(0, title_x, title);
}
- private bool decrementTitleCountdown() {
+ private bool decrementTitleCountdown() {
countdownValue--;
setTitle();
win.refresh();
diff --git a/src/database/Makefile b/src/database/Makefile
index 986f1f6..9e2956e 100644
--- a/src/database/Makefile
+++ b/src/database/Makefile
@@ -2,7 +2,7 @@ all: database
@echo > /dev/null
database: main.vala database.vala db-interface.vala ../config/config-interface.vala ../price.vapi
- valac -X -w -o $@ --pkg sqlite3 --pkg gee-0.8 --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg sqlite3 --pkg gee-0.8 --pkg gio-2.0 $^
clean:
rm -rf database
diff --git a/src/database/database.vala b/src/database/database.vala
index 121c793..cd2b947 100644
--- a/src/database/database.vala
+++ b/src/database/database.vala
@@ -25,7 +25,7 @@ public class DataBase : Object {
int rc = db.prepare_v2(query, -1, out stmt);
if(rc != Sqlite.OK) {
- error("could not prepare statement: %s", query);
+ error(_("Error: could not prepare statement: %s"), query);
}
}
@@ -78,7 +78,7 @@ public class DataBase : Object {
rc = Sqlite.Database.open(file, out db);
if(rc != Sqlite.OK) {
- error("could not open database!");
+ error(_("Error: could not open database!"));
}
/* setup queries */
@@ -367,7 +367,7 @@ public class DataBase : Object {
rc = statements["purchase"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
return true;
}
@@ -382,9 +382,9 @@ public class DataBase : Object {
case Sqlite.ROW:
return statements["product_name"].column_text(0);
case Sqlite.DONE:
- throw new DatabaseError.PRODUCT_NOT_FOUND("unknown product: %llu", article);
+ throw new DatabaseError.PRODUCT_NOT_FOUND(_("unknown product: %llu"), article);
default:
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
}
@@ -398,9 +398,9 @@ public class DataBase : Object {
case Sqlite.ROW:
return statements["product_category"].column_text(0);
case Sqlite.DONE:
- throw new DatabaseError.PRODUCT_NOT_FOUND("unknown product: %llu", article);
+ throw new DatabaseError.PRODUCT_NOT_FOUND(_("unknown product: %llu"), article);
default:
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
}
@@ -414,9 +414,9 @@ public class DataBase : Object {
case Sqlite.ROW:
return statements["product_amount"].column_int(0);
case Sqlite.DONE:
- throw new DatabaseError.PRODUCT_NOT_FOUND("unknown product: %llu", article);
+ throw new DatabaseError.PRODUCT_NOT_FOUND(_("unknown product: %llu"), article);
default:
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
}
@@ -430,9 +430,9 @@ public class DataBase : Object {
case Sqlite.ROW:
return statements["product_deprecated"].column_int(0) == 1;
case Sqlite.DONE:
- throw new DatabaseError.PRODUCT_NOT_FOUND("unknown product: %llu", article);
+ throw new DatabaseError.PRODUCT_NOT_FOUND(_("unknown product: %llu"), article);
default:
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
}
@@ -445,7 +445,7 @@ public class DataBase : Object {
rc = statements["product_set_deprecated"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
public Price get_product_price(int user, uint64 article) throws DBusError, IOError, DatabaseError {
@@ -465,9 +465,9 @@ public class DataBase : Object {
else
return statements["price"].column_int(1);
case Sqlite.DONE:
- throw new DatabaseError.PRODUCT_NOT_FOUND("unknown product: %llu", article);
+ throw new DatabaseError.PRODUCT_NOT_FOUND(_("unknown product: %llu"), article);
default:
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
}
@@ -484,12 +484,12 @@ public class DataBase : Object {
case Sqlite.ROW:
pid = uint64.parse(statements["last_purchase"].column_text(0));
pname = get_product_name(pid);
- write_to_log("Remove purchase of %s", pname);
+ stderr.printf(_("Remove purchase of %s"), pname);
break;
case Sqlite.DONE:
- throw new DatabaseError.PRODUCT_NOT_FOUND("undo not possible without purchases");
+ throw new DatabaseError.PRODUCT_NOT_FOUND(_("undo not possible without purchases"));
default:
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
statements["undo"].reset();
@@ -497,7 +497,7 @@ public class DataBase : Object {
rc = statements["undo"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
return pname;
}
@@ -524,7 +524,7 @@ public class DataBase : Object {
rc = statements["stock"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
public void new_product(uint64 id, string name, int category, int memberprice, int guestprice) throws DBusError, IOError, DatabaseError {
@@ -538,7 +538,7 @@ public class DataBase : Object {
if(rc == Sqlite.CONSTRAINT) {
throw new DatabaseError.CONSTRAINT_FAILED(db.errmsg());
} else if(rc != Sqlite.DONE) {
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
new_price(id, 0, memberprice, guestprice);
@@ -553,7 +553,7 @@ public class DataBase : Object {
int rc = statements["price_create"].step();
if(rc != Sqlite.DONE) {
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
}
@@ -580,7 +580,7 @@ public class DataBase : Object {
statements["user_auth_create"].bind_int(1, user);
rc = statements["user_auth_create"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
/* set password */
statements["password_set"].reset();
@@ -588,7 +588,7 @@ public class DataBase : Object {
statements["password_set"].bind_int(2, user);
rc = statements["password_set"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
public void set_sessionid(int user, string sessionid) throws DBusError, IOError, DatabaseError {
@@ -598,7 +598,7 @@ public class DataBase : Object {
int rc = statements["session_set"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
public int get_user_by_sessionid(string sessionid) throws DBusError, IOError, DatabaseError {
@@ -608,7 +608,7 @@ public class DataBase : Object {
if(statements["session_get"].step() == Sqlite.ROW) {
return statements["session_get"].column_int(0);
} else {
- throw new DatabaseError.SESSION_NOT_FOUND("No such session available in database!");
+ throw new DatabaseError.SESSION_NOT_FOUND(_("No such session available in database!"));
}
}
@@ -633,9 +633,9 @@ public class DataBase : Object {
result.soundTheme = statements["userinfo"].column_text(10);
result.joined_at = statements["userinfo"].column_int64(11);
} else if(rc == Sqlite.DONE) {
- throw new DatabaseError.USER_NOT_FOUND("user not found");
+ throw new DatabaseError.USER_NOT_FOUND(_("user not found"));
} else {
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
statements["rfid_userid"].reset();
@@ -672,7 +672,7 @@ public class DataBase : Object {
} else if(rc == Sqlite.DONE) {
/* entry not found, we return defaults */
} else {
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
return result;
@@ -686,7 +686,7 @@ public class DataBase : Object {
statements["user_auth_create"].bind_int(1, auth.id);
rc = statements["user_auth_create"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
/* set authentication */
statements["userauth_set"].reset();
@@ -697,7 +697,7 @@ public class DataBase : Object {
rc = statements["userauth_set"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
public string get_username(int user) throws DBusError, IOError, DatabaseError {
@@ -707,7 +707,7 @@ public class DataBase : Object {
if(statements["username"].step() == Sqlite.ROW) {
return statements["username"].column_text(0)+" "+statements["username"].column_text(1);
} else {
- throw new DatabaseError.USER_NOT_FOUND("No such user available in database!");
+ throw new DatabaseError.USER_NOT_FOUND(_("No such user available in database!"));
}
}
@@ -719,7 +719,7 @@ public class DataBase : Object {
if(statements["user_theme_get"].step() == Sqlite.ROW) {
return statements["user_theme_get"].column_text(0);
} else {
- throw new DatabaseError.USER_NOT_FOUND("No such user available in database!");
+ throw new DatabaseError.USER_NOT_FOUND(_("No such user available in database!"));
}
}
@@ -734,7 +734,7 @@ public class DataBase : Object {
int rc = statements["user_theme_set"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
public InvoiceEntry[] get_invoice(int user, int64 from=0, int64 to=-1) throws DBusError, IOError, DatabaseError {
@@ -762,7 +762,7 @@ public class DataBase : Object {
}
if(rc != Sqlite.DONE) {
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
return result;
@@ -881,7 +881,7 @@ public class DataBase : Object {
statements["user_auth_create"].bind_int(1, user);
rc = statements["user_auth_create"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
/* set disabled flag */
statements["user_disable"].reset();
@@ -889,7 +889,7 @@ public class DataBase : Object {
statements["user_disable"].bind_int(2, user);
rc = statements["user_disable"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
public void user_replace(UserInfo u) throws DBusError, IOError, DatabaseError {
@@ -910,13 +910,13 @@ public class DataBase : Object {
int rc = statements["user_replace"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
statements["rfid_delete_user"].reset();
statements["rfid_delete_user"].bind_int(1, u.id);
rc = statements["rfid_delete_user"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
foreach (string rfid in u.rfid) {
statements["rfid_insert"].reset();
@@ -924,7 +924,7 @@ public class DataBase : Object {
statements["rfid_insert"].bind_text(2, rfid);
rc = statements["rfid_insert"].step();
if(rc != Sqlite.DONE)
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
}
@@ -1025,7 +1025,7 @@ public class DataBase : Object {
int rc = statements["supplier_add"].step();
if(rc != Sqlite.DONE) {
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
}
@@ -1076,7 +1076,7 @@ public class DataBase : Object {
int rc = statements["cashbox_add"].step();
if(rc != Sqlite.DONE) {
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
}
@@ -1126,7 +1126,7 @@ public class DataBase : Object {
int rc = statements["alias_ean_add"].step();
if(rc != Sqlite.DONE) {
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
}
@@ -1207,9 +1207,9 @@ public class DataBase : Object {
case Sqlite.ROW:
return statements["userid_rfid"].column_int(0);
case Sqlite.DONE:
- throw new DatabaseError.RFID_NOT_FOUND("unknown rfid: %s", rfid);
+ throw new DatabaseError.RFID_NOT_FOUND(_("unknown rfid: %s"), rfid);
default:
- throw new DatabaseError.INTERNAL_ERROR("internal error: %d", rc);
+ throw new DatabaseError.INTERNAL_ERROR(_("internal error: %d"), rc);
}
}
}
diff --git a/src/database/main.vala b/src/database/main.vala
index f65707b..bd4c7dc 100644
--- a/src/database/main.vala
+++ b/src/database/main.vala
@@ -13,42 +13,41 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-public static void write_to_log(string message, ...) {
- /* TODO: send message via DBus? Replace some write_to_log by throwing an error? */
-}
-
DataBase db;
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
try {
Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
var dbfile = cfg.get_string("DATABASE", "file");
db = new DataBase(dbfile);
} catch(IOError e) {
- error("IOError: %s\n", e.message);
+ error(_("IO Error: %s\n"), e.message);
} catch(KeyFileError e) {
- error("Config Error: %s\n", e.message);
+ error(_("Config Error: %s\n"), e.message);
} catch(DBusError e) {
- error("DBusError: %s\n", e.message);
+ error(_("DBus Error: %s\n"), e.message);
}
Bus.own_name(
BusType.SYSTEM,
"io.mainframe.shopsystem.Database",
BusNameOwnerFlags.NONE,
- on_bus_aquired,
+ on_bus_acquired,
() => {},
- () => stderr.printf("Could not aquire name\n"));
+ () => stderr.printf(_("Could not acquire name\n")));
new MainLoop().run();
return 0;
}
-void on_bus_aquired(DBusConnection con) {
+void on_bus_acquired(DBusConnection con) {
try {
con.register_object("/io/mainframe/shopsystem/database", db);
} catch(IOError e) {
- stderr.printf("Could not register service\n");
+ stderr.printf(_("Could not register service\n"));
}
}
diff --git a/src/input-device/Makefile b/src/input-device/Makefile
index aba6c73..5e501df 100644
--- a/src/input-device/Makefile
+++ b/src/input-device/Makefile
@@ -2,7 +2,7 @@ all: input-device
@echo > /dev/null
input-device: main.vala input-device.vala input-device-interface.vala ../config/config-interface.vala
- valac -X -w -o $@ --pkg linux --pkg posix --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg linux --pkg posix --pkg gio-2.0 $^
clean:
rm -rf input-device
diff --git a/src/input-device/input-device.vala b/src/input-device/input-device.vala
index 1c558d0..1dba213 100644
--- a/src/input-device/input-device.vala
+++ b/src/input-device/input-device.vala
@@ -24,7 +24,7 @@ public class Device {
public Device(string device) {
if (device == "ignore") {
- stdout.printf("Ignoring InputDevice!\n");
+ stdout.printf(_("Ignoring InputDevice!\n"));
return;
}
try {
@@ -37,10 +37,10 @@ public class Device {
Posix.fcntl(fd, Posix.F_SETFL, flags | Posix.O_NONBLOCK);
if(!(io_read.add_watch(IOCondition.IN | IOCondition.HUP, device_read) != 0)) {
- error("Could not bind IOChannel");
+ error(_("Could not bind IOChannel"));
}
} catch(FileError e) {
- error("FileError: %s", e.message);
+ error(_("File Error: %s"), e.message);
}
}
@@ -212,7 +212,7 @@ public class Device {
char key = '\0';
if((cond & IOCondition.HUP) == IOCondition.HUP)
- error("Lost device");
+ error(_("Lost device"));
do {
int fd = source.unix_get_fd();
@@ -221,7 +221,7 @@ public class Device {
/* short read */
if (s != sizeof(Linux.Input.Event)) {
if(s > 0)
- stdout.printf("short read!\n");
+ stdout.printf(_("short read!\n"));
return true;
}
@@ -246,7 +246,7 @@ public class Device {
buffer += "%c".printf(key);
} while(key != '\n');
- stdout.printf("barcode: %s\n", buffer);
+ stdout.printf(_("barcode: %s\n"), buffer);
if(buffer.has_prefix("USER ") || buffer.has_prefix("STOCK") || buffer.has_prefix("AMOUNT ")) {
if(!check_code39_checksum(buffer))
diff --git a/src/input-device/main.vala b/src/input-device/main.vala
index 372418e..487c028 100644
--- a/src/input-device/main.vala
+++ b/src/input-device/main.vala
@@ -18,40 +18,43 @@ Device devScanner;
Device devRfid;
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
try {
Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
devScanner = new Device(cfg.get_string("INPUT", "barcodescanner"));
devRfid = new Device(cfg.get_string("INPUT", "rfidreader"));
} catch(IOError e) {
- error("IOError: %s\n", e.message);
+ error(_("IO Error: %s\n"), e.message);
} catch(KeyFileError e) {
- error("Config Error: %s\n", e.message);
+ error(_("Config Error: %s\n"), e.message);
} catch(DBusError e) {
- error("DBus Error: %s\n", e.message);
+ error(_("DBus Error: %s\n"), e.message);
}
Bus.own_name(
BusType.SYSTEM,
"io.mainframe.shopsystem.InputDevice",
BusNameOwnerFlags.NONE,
- on_bus_aquired,
+ on_bus_acquired,
() => {},
- () => stderr.printf("Could not aquire name\n"));
+ () => stderr.printf(_("Could not acquire name\n")));
new MainLoop().run();
return 0;
}
-void on_bus_aquired(DBusConnection con) {
+void on_bus_acquired(DBusConnection con) {
try {
con.register_object("/io/mainframe/shopsystem/device/scanner", devScanner);
} catch(IOError e) {
- stderr.printf("Could not register service\n");
+ stderr.printf(_("Could not register service\n"));
}
try {
con.register_object("/io/mainframe/shopsystem/device/rfid", devRfid);
} catch(IOError e) {
- stderr.printf("Could not register service\n");
+ stderr.printf(_("Could not register service\n"));
}
}
diff --git a/src/invoice/Makefile b/src/invoice/Makefile
index 73eaf48..d8d72c0 100644
--- a/src/invoice/Makefile
+++ b/src/invoice/Makefile
@@ -2,10 +2,10 @@ all: invoice single-invoice
@echo > /dev/null
invoice: main.vala invoice.vala ../mail/mailer-interface.vala ../pdf-invoice/pdf-invoice-interface.vala ../database/db-interface.vala ../config/config-interface.vala ../price.vapi
- valac -X -w -o $@ --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg gio-2.0 $^
single-invoice: single.vala invoice.vala ../mail/mailer-interface.vala ../pdf-invoice/pdf-invoice-interface.vala ../database/db-interface.vala ../config/config-interface.vala ../price.vapi
- valac -X -w -o $@ --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg gio-2.0 $^
clean:
rm -f invoice single-invoice
diff --git a/src/invoice/invoice.vala b/src/invoice/invoice.vala
index c0a162c..f899cb5 100644
--- a/src/invoice/invoice.vala
+++ b/src/invoice/invoice.vala
@@ -249,7 +249,7 @@ public class InvoiceImplementation {
try {
FileUtils.get_contents(datadir + "/treasurer.mail.txt", out text);
} catch(GLib.FileError e) {
- throw new IOError.FAILED("Could not open invoice template: %s", e.message);
+ throw new IOError.FAILED(_("Could not open invoice template: %s"), e.message);
}
text = text.replace("{{{SHORTNAME}}}", shortname);
@@ -311,12 +311,12 @@ public class InvoiceImplementation {
table = generate_invoice_table_html(entries);
if(filename == "")
- throw new IOError.FAILED("Unknown MessageType");
+ throw new IOError.FAILED(_("Unknown MessageType"));
try {
FileUtils.get_contents(datadir + "/" + filename, out text);
} catch(GLib.FileError e) {
- throw new IOError.FAILED("Could not open invoice template: %s", e.message);
+ throw new IOError.FAILED(_("Could not open invoice template: %s"), e.message);
}
text = text.replace("{{{ADDRESS}}}", address);
@@ -335,7 +335,7 @@ public class InvoiceImplementation {
try {
FileUtils.get_contents(datadir + "/" + vattextfilename, out vattext);
} catch(GLib.FileError e) {
- throw new IOError.FAILED("Could not open VAT template: %s", e.message);
+ throw new IOError.FAILED(_("Could not open VAT template: %s"), e.message);
}
text = text.replace("{{{VAT}}}", vattext);
diff --git a/src/invoice/main.vala b/src/invoice/main.vala
index b341adb..f5ec69b 100644
--- a/src/invoice/main.vala
+++ b/src/invoice/main.vala
@@ -16,11 +16,14 @@
InvoiceImplementation invoice;
public static void help(string name) {
- stderr.printf("Usage: %s [timestamp]\n", name);
- stderr.printf("Possible values for : temporary, final\n");
+ stderr.printf(_("Usage: %s [timestamp]\n"), name);
+ stderr.printf(_("Possible values for : temporary, final\n"));
}
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
bool temporary = false;
int64 timestamp = new DateTime.now_local().to_unix();
@@ -45,14 +48,14 @@ public static int main(string[] args) {
try {
invoice = new InvoiceImplementation();
} catch(Error e) {
- stderr.printf("Error: %s\n", e.message);
+ stderr.printf(_("Error: %s\n"), e.message);
return 1;
}
try {
invoice.send_invoices(temporary, timestamp);
} catch(Error e) {
- stderr.printf("Error: %s\n", e.message);
+ stderr.printf(_("Error: %s\n"), e.message);
return 1;
}
diff --git a/src/invoice/single.vala b/src/invoice/single.vala
index 54cba26..6f54afe 100644
--- a/src/invoice/single.vala
+++ b/src/invoice/single.vala
@@ -48,14 +48,14 @@ public static int main(string[] args) {
try {
invoice = new InvoiceImplementation();
} catch(Error e) {
- stderr.printf("Error: %s\n", e.message);
+ stderr.printf(_("Error: %s\n"), e.message);
return 1;
}
try {
invoice.send_invoice(temporary, timestamp, user);
} catch(Error e) {
- stderr.printf("Error: %s\n", e.message);
+ stderr.printf(_("Error: %s\n"), e.message);
return 1;
}
diff --git a/src/mail/Makefile b/src/mail/Makefile
index 47819dd..70e9b5a 100644
--- a/src/mail/Makefile
+++ b/src/mail/Makefile
@@ -2,7 +2,7 @@ all: mailer
@echo > /dev/null
mailer: main.vala mailer.vala mail.vala mailer-interface.vala ../config/config-interface.vala
- valac -X -w -o $@ --vapidir=../../vapi --pkg posix --pkg libesmtp --pkg gio-2.0 --pkg gmime-3.0 -X -D_GNU_SOURCE -X -lesmtp -X -lssl -X -lcrypto -X -ldl -X -pthread $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --vapidir=../../vapi --pkg posix --pkg libesmtp --pkg gio-2.0 --pkg gmime-3.0 -X -D_GNU_SOURCE -X -lesmtp -X -lssl -X -lcrypto -X -ldl -X -pthread $^
clean:
rm -f mailer
diff --git a/src/mail/mailer.vala b/src/mail/mailer.vala
index c8d609c..3d7a996 100644
--- a/src/mail/mailer.vala
+++ b/src/mail/mailer.vala
@@ -76,7 +76,7 @@ public class MailerImplementation {
var cfgport = config.get_integer("MAIL", "port");
server = @"$cfgserv:$cfgport";
} catch(KeyFileError e) {
- throw new IOError.FAILED("server or port configuration is missing");
+ throw new IOError.FAILED(_("server or port configuration is missing"));
}
try {
@@ -96,7 +96,7 @@ public class MailerImplementation {
/* setup server */
result = session.set_server(server);
if(result == 0)
- throw new IOError.FAILED("could not setup server");
+ throw new IOError.FAILED(_("could not setup server"));
/* Use TLS if possible */
if (starttls)
@@ -104,7 +104,7 @@ public class MailerImplementation {
else
result = session.starttls_enable(Smtp.StartTlsOption.DISABLED);
if(result == 0)
- throw new IOError.FAILED("could not configure STARTTLS");
+ throw new IOError.FAILED(_("could not configure STARTTLS"));
/* setup authentication */
if(username != "") {
@@ -138,7 +138,7 @@ public class MailerImplementation {
public void delete_mail(string path) throws DBusError, IOError {
if(!(path in mails))
- throw new IOError.NOT_FOUND("No such mail");
+ throw new IOError.NOT_FOUND(_("No such mail"));
mail_bus.unregister_object(mails[path].registration_id);
mails.remove(path);
@@ -146,7 +146,7 @@ public class MailerImplementation {
public void send_mail(string path) throws DBusError, IOError {
if(!(path in mails))
- throw new IOError.NOT_FOUND("No such mail");
+ throw new IOError.NOT_FOUND(_("No such mail"));
send_queue.push_tail(mails[path].mail);
delete_mail(path);
@@ -173,11 +173,11 @@ public class MailerImplementation {
int result = session.start_session();
if(result == 0)
- throw new IOError.FAILED("eSMTP: Start Session failed!");
+ throw new IOError.FAILED(_("eSMTP: Start Session failed!"));
unowned Smtp.Status status = message.transfer_status();
if(status.code < 200 || status.code >= 300)
- throw new IOError.FAILED("Reply from SMTP-Server: %s", status.text);
+ throw new IOError.FAILED(_("Reply from SMTP-Server: %s"), status.text);
current_mail = null;
diff --git a/src/mail/main.vala b/src/mail/main.vala
index 989c4f1..b3a7088 100644
--- a/src/mail/main.vala
+++ b/src/mail/main.vala
@@ -17,30 +17,33 @@ MailerImplementation m;
DBusConnection mail_bus;
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
try {
m = new MailerImplementation();
} catch(Error e) {
- stderr.printf("Error: %s\n", e.message);
+ stderr.printf(_("Error: %s\n"), e.message);
}
Bus.own_name(
BusType.SYSTEM,
"io.mainframe.shopsystem.Mail",
BusNameOwnerFlags.NONE,
- on_mail_bus_aquired,
+ on_mail_bus_acquired,
() => {},
- () => stderr.printf("Error: Could not aquire name\n"));
+ () => stderr.printf(_("Error: Could not acquire name\n")));
new MainLoop().run();
return 0;
}
-void on_mail_bus_aquired(DBusConnection con) {
+void on_mail_bus_acquired(DBusConnection con) {
try {
mail_bus = con;
con.register_object("/io/mainframe/shopsystem/mailer", m);
} catch(IOError e) {
- stderr.printf("Error: Could not register service\n");
+ stderr.printf(_("Error: Could not register service\n"));
}
}
diff --git a/src/pdf-invoice/Makefile b/src/pdf-invoice/Makefile
index 9805f6e..91b8522 100644
--- a/src/pdf-invoice/Makefile
+++ b/src/pdf-invoice/Makefile
@@ -2,10 +2,10 @@ all: pdf-invoice
@echo > /dev/null
pdf-invoice: main.vala pdf-invoice.vala pdf-invoice-interface.vala ../config/config-interface.vala ../database/db-interface.vala ../price.vapi
- valac -X -w -g -o $@ --pkg pangocairo --pkg librsvg-2.0 --pkg posix --pkg gdk-2.0 --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -g -o $@ --pkg pangocairo --pkg librsvg-2.0 --pkg posix --pkg gdk-2.0 --pkg gio-2.0 $^
test: pdf-invoice-interface.vala ../database/db-interface.vala test.vala ../price.vapi
- valac -X -w -o $@ --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg gio-2.0 $^
clean:
rm -rf pdf-invoice test
diff --git a/src/pdf-invoice/main.vala b/src/pdf-invoice/main.vala
index dc95bde..1848852 100644
--- a/src/pdf-invoice/main.vala
+++ b/src/pdf-invoice/main.vala
@@ -16,34 +16,37 @@
private string datadir;
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
try {
Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
datadir = cfg.get_string("INVOICE", "datadir");
} catch(DBusError e) {
- error("DBusError: %s\n", e.message);
+ error(_("DBus Error: %s\n"), e.message);
} catch(IOError e) {
- error("IOError: %s\n", e.message);
+ error(_("IO Error: %s\n"), e.message);
} catch(KeyFileError e) {
- error("Config Error: %s\n", e.message);
+ error(_("Config Error: %s\n"), e.message);
}
Bus.own_name(
BusType.SYSTEM,
"io.mainframe.shopsystem.InvoicePDF",
BusNameOwnerFlags.NONE,
- on_bus_aquired,
+ on_bus_acquired,
() => {},
- () => stderr.printf("Could not aquire name\n"));
+ () => stderr.printf(_("Could not acquire name\n")));
new MainLoop().run();
return 0;
}
-void on_bus_aquired(DBusConnection conn) {
+void on_bus_acquired(DBusConnection conn) {
try {
conn.register_object("/io/mainframe/shopsystem/invoicepdf", new InvoicePDF(datadir));
} catch(Error e) {
- stderr.printf("Could not register service: %s\n", e.message);
+ stderr.printf(_("Could not register service: %s\n"), e.message);
}
}
diff --git a/src/pdf-invoice/pdf-invoice.vala b/src/pdf-invoice/pdf-invoice.vala
index d92c93f..b41817d 100644
--- a/src/pdf-invoice/pdf-invoice.vala
+++ b/src/pdf-invoice/pdf-invoice.vala
@@ -78,7 +78,7 @@ public class InvoicePDF {
var svg = new Rsvg.Handle.from_file(file);
svg.render_cairo(ctx);
} catch(Error e) {
- error("Could not load SVG: %s\n", e.message);
+ error(_("Could not load SVG: %s\n"), e.message);
}
}
@@ -376,7 +376,7 @@ public class InvoicePDF {
try {
FileUtils.get_contents(datadir + "/" + "vat.txt", out vattext);
} catch(GLib.FileError e) {
- throw new IOError.FAILED("Could not open VAT template: %s", e.message);
+ throw new IOError.FAILED(_("Could not open VAT template: %s"), e.message);
}
text = text.replace("{{{VAT}}}", vattext);
@@ -384,7 +384,7 @@ public class InvoicePDF {
layout.set_markup(text, text.length);
} catch(GLib.FileError e) {
- error("File Error: %s\n", e.message);
+ error(_("File Error: %s\n"), e.message);
}
/* render text */
@@ -468,11 +468,11 @@ public class InvoicePDF {
var price = @"$(e.price)€".replace(".", ",");
if(e.price > 999999) {
- throw new InvoicePDFError.PRICE_TOO_HIGH("Prices > 9999.99€ are not supported!");
+ throw new InvoicePDFError.PRICE_TOO_HIGH(_("Prices > 9999.99€ are not supported!"));
}
if(tm.get_year() > 9999) {
- throw new InvoicePDFError.TOO_FAR_IN_THE_FUTURE("Years after 9999 are not supported!");
+ throw new InvoicePDFError.TOO_FAR_IN_THE_FUTURE(_("Years after 9999 are not supported!"));
}
/* if date remains the same do not add it again */
@@ -588,7 +588,7 @@ public class InvoicePDF {
/* retry adding the entry */
if(!draw_invoice_table_entry(ctx, y, entry, out y)) {
- throw new InvoicePDFError.ARTICLE_NAME_TOO_LONG("Article name \"%s\" does not fit on a single page!", entry.product.name);
+ throw new InvoicePDFError.ARTICLE_NAME_TOO_LONG(_("Article name \"%s\" does not fit on a single page!"), entry.product.name);
}
}
}
@@ -625,16 +625,16 @@ public class InvoicePDF {
var ctx = new Cairo.Context(document);
if(invoice_id == "")
- throw new InvoicePDFError.NO_INVOICE_ID("No invoice ID given!");
+ throw new InvoicePDFError.NO_INVOICE_ID(_("No invoice ID given!"));
if(invoice_entries == null)
- throw new InvoicePDFError.NO_INVOICE_DATA("No invoice data given!");
+ throw new InvoicePDFError.NO_INVOICE_DATA(_("No invoice data given!"));
if(invoice_date == 0)
- throw new InvoicePDFError.NO_INVOICE_DATE("No invoice date given!");
+ throw new InvoicePDFError.NO_INVOICE_DATE(_("No invoice date given!"));
if(invoice_recipient.firstname == "" && invoice_recipient.lastname == "")
- throw new InvoicePDFError.NO_INVOICE_RECIPIENT("No invoice recipient given!");
+ throw new InvoicePDFError.NO_INVOICE_RECIPIENT(_("No invoice recipient given!"));
/* first page */
draw_logo(ctx);
diff --git a/src/pdf-stock/Makefile b/src/pdf-stock/Makefile
index 2c8be58..c310c65 100644
--- a/src/pdf-stock/Makefile
+++ b/src/pdf-stock/Makefile
@@ -6,10 +6,10 @@ all: pdf-stock
@echo > /dev/null
pdf-stock: main.vala pdf-stock.vala ../database/db-interface.vala ../price.vapi ../../libcairobarcode/libcairobarcode.vapi
- ${VALAC} -X -w ${LIBCAIROBARCODE} -o $@ --pkg cairo --pkg pangocairo --pkg gio-2.0 --pkg posix $^
+ ${VALAC} -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w ${LIBCAIROBARCODE} -o $@ --pkg cairo --pkg pangocairo --pkg gio-2.0 --pkg posix $^
test: test.vala pdf-stock-interface.vala
- ${VALAC} -X -w -o $@ --pkg gio-2.0 $^
+ ${VALAC} -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg gio-2.0 $^
run: pdf-stock
LD_LIBRARY_PATH=../../libcairobarcode ./pdf-stock
diff --git a/src/pdf-stock/main.vala b/src/pdf-stock/main.vala
index 49613f4..10aef60 100644
--- a/src/pdf-stock/main.vala
+++ b/src/pdf-stock/main.vala
@@ -14,23 +14,26 @@
*/
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
Bus.own_name(
BusType.SYSTEM,
"io.mainframe.shopsystem.StockPDF",
BusNameOwnerFlags.NONE,
- on_bus_aquired,
+ on_bus_acquired,
() => {},
- () => stderr.printf("Could not aquire name\n"));
+ () => stderr.printf(_("Could not acquire name\n")));
new MainLoop().run();
return 0;
}
-void on_bus_aquired(DBusConnection conn) {
+void on_bus_acquired(DBusConnection conn) {
try {
conn.register_object("/io/mainframe/shopsystem/stockpdf", new StockPDF());
} catch(IOError e) {
- stderr.printf("Could not register service\n");
+ stderr.printf(_("Could not register service\n"));
}
}
diff --git a/src/pdf-stock/test.vala b/src/pdf-stock/test.vala
index cc3da4f..2e35f9c 100644
--- a/src/pdf-stock/test.vala
+++ b/src/pdf-stock/test.vala
@@ -19,9 +19,11 @@ public static int main(string args[]) {
var pdfdata = stock.generate();
FileUtils.set_contents("test.pdf", (string) pdfdata, pdfdata.length);
} catch(IOError e) {
- error("IOError: %s", e.message);
+ error(_("IO Error: %s"), e.message);
} catch(FileError e) {
- error("FileError: %s", e.message);
+ error(_("File Error: %s"), e.message);
+ } catch(DBusError e) {
+ error(_("DBus Error: %s"), e.message);
}
return 0;
diff --git a/src/pgp/Makefile b/src/pgp/Makefile
index 6292111..5faa80f 100644
--- a/src/pgp/Makefile
+++ b/src/pgp/Makefile
@@ -2,7 +2,7 @@ all: pgp
@echo > /dev/null
pgp: main.vala pgp.vala pgp-interface.vala ../config/config-interface.vala
- valac --Xcc="-D_FILE_OFFSET_BITS=64" -X -w -o $@ --vapidir ../../vapi -X -lgpgme --pkg gpgme --pkg gio-2.0 --pkg libarchive $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' --Xcc="-D_FILE_OFFSET_BITS=64" -X -w -o $@ --vapidir ../../vapi -X -lgpgme --pkg gpgme --pkg gio-2.0 --pkg libarchive $^
clean:
rm -rf pgp
diff --git a/src/pgp/main.vala b/src/pgp/main.vala
index 332bcd5..74bcf0e 100644
--- a/src/pgp/main.vala
+++ b/src/pgp/main.vala
@@ -21,35 +21,37 @@ PGPKeyArchive pgp;
Config cfg;
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
try {
cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
pgp = new PGPKeyArchive(cfg.get_string("PGP", "keyring"));
} catch(DBusError e) {
- error("DBusError: %s\n", e.message);
+ error(_("DBus Error: %s\n"), e.message);
} catch(IOError e) {
- error("IOError: %s\n", e.message);
+ error(_("IO Error: %s\n"), e.message);
} catch(KeyFileError e) {
- error("Config Error: %s\n", e.message);
+ error(_("Config Error: %s\n"), e.message);
}
-
Bus.own_name(
BusType.SYSTEM,
"io.mainframe.shopsystem.PGP",
BusNameOwnerFlags.NONE,
- on_bus_aquired,
+ on_bus_acquired,
() => {},
- () => stderr.printf("Could not aquire name\n"));
+ () => stderr.printf(_("Could not acquire name\n")));
new MainLoop().run();
return 0;
}
-void on_bus_aquired(DBusConnection con) {
+void on_bus_acquired(DBusConnection con) {
try {
con.register_object("/io/mainframe/shopsystem/pgp", pgp);
} catch(IOError e) {
- stderr.printf("Could not register service\n");
+ stderr.printf(_("Could not register service\n"));
}
}
diff --git a/src/pgp/pgp.vala b/src/pgp/pgp.vala
index 2a5b160..6d9bed0 100644
--- a/src/pgp/pgp.vala
+++ b/src/pgp/pgp.vala
@@ -120,7 +120,7 @@ public class PGPKeyArchive {
keydata.read(data);
return (string) data;
} else {
- stdout.printf("error!\n");
+ stdout.printf(_("Error!\n"));
return "";
}
}
diff --git a/src/scanner-session/Makefile b/src/scanner-session/Makefile
index 5320025..28f0ea8 100644
--- a/src/scanner-session/Makefile
+++ b/src/scanner-session/Makefile
@@ -2,7 +2,7 @@ all: scanner-session
@echo > /dev/null
scanner-session: main.vala scannersession.vala scannersession-interface.vala ../database/db-interface.vala ../input-device/input-device-interface.vala ../cli/cli-interface.vala ../audio/audio-interface.vala ../price.vapi
- valac -X -w -o $@ --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg gio-2.0 $^
clean:
rm -rf scanner-session
diff --git a/src/scanner-session/main.vala b/src/scanner-session/main.vala
index 2ed94f9..3b876c5 100644
--- a/src/scanner-session/main.vala
+++ b/src/scanner-session/main.vala
@@ -16,13 +16,16 @@
ScannerSessionImplementation session;
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
Bus.own_name(
BusType.SYSTEM,
"io.mainframe.shopsystem.ScannerSession",
BusNameOwnerFlags.NONE,
- on_bus_aquired,
+ on_bus_acquired,
() => {},
- () => stderr.printf("Could not aquire name\n"));
+ () => stderr.printf(_("Could not acquire name\n")));
session = new ScannerSessionImplementation();
@@ -31,10 +34,10 @@ public static int main(string[] args) {
return 0;
}
-void on_bus_aquired(DBusConnection con) {
+void on_bus_acquired(DBusConnection con) {
try {
con.register_object("/io/mainframe/shopsystem/scanner_session", session);
} catch(IOError e) {
- stderr.printf("Could not register service\n");
+ stderr.printf(_("Could not register service\n"));
}
}
diff --git a/src/scanner-session/scannersession.vala b/src/scanner-session/scannersession.vala
index fc385f6..cf308ce 100644
--- a/src/scanner-session/scannersession.vala
+++ b/src/scanner-session/scannersession.vala
@@ -17,7 +17,7 @@
[DBus (name = "io.mainframe.shopsystem.ScannerSession")]
public class ScannerSessionImplementation {
private int user = 0;
- private string name = "Guest";
+ private string name = _("Guest");
private bool logged_in = false;
private bool disabled = false;
private string theme = "beep";
@@ -46,7 +46,7 @@ public class ScannerSessionImplementation {
devRfid.received_barcode.connect(handle_barcode);
cli.received_barcode.connect(handle_barcode);
} catch(IOError e) {
- error("IOError: %s\n", e.message);
+ error(_("IO Error: %s\n"), e.message);
}
}
@@ -63,7 +63,7 @@ public class ScannerSessionImplementation {
this.name = db.get_username(user);
this.disabled = db.user_is_disabled(user);
} catch(DatabaseError e) {
- send_message(MessageType.ERROR, "Error (user=%d): %s", user, e.message);
+ send_message(MessageType.ERROR, _("Error (user=%d): %s"), user, e.message);
return false;
}
this.logged_in = true;
@@ -132,13 +132,13 @@ public class ScannerSessionImplementation {
int32 userid = int.parse(scannerdata.substring(5));
if(login(userid)) {
scannerResult.type = MessageType.INFO;
- scannerResult.message = "Login: %s (%d)".printf(name, user);
+ scannerResult.message = _("Login: %s (%d)").printf(name, user);
scannerResult.audioType = AudioType.LOGIN;
shoppingCard = {};
state = ScannerSessionState.USER;
} else {
scannerResult.type = MessageType.ERROR;
- scannerResult.message = "Login failed (User ID = %d)".printf(userid);
+ scannerResult.message = _("Login failed (User ID = %d)").printf(userid);
scannerResult.audioType = AudioType.ERROR;
state = ScannerSessionState.READY;
}
@@ -146,13 +146,13 @@ public class ScannerSessionImplementation {
case ScannerSessionCodeType.GUEST:
if(login(0)) {
scannerResult.type = MessageType.INFO;
- scannerResult.message = "Login as GUEST";
+ scannerResult.message = _("Login as GUEST");
scannerResult.audioType = AudioType.LOGIN;
shoppingCard = {};
state = ScannerSessionState.USER;
} else {
scannerResult.type = MessageType.ERROR;
- scannerResult.message = "Login failed as GUEST";
+ scannerResult.message = _("Login failed as GUEST");
scannerResult.audioType = AudioType.ERROR;
state = ScannerSessionState.READY;
}
@@ -165,17 +165,17 @@ public class ScannerSessionImplementation {
p = db.get_product_for_ean(ean);
} catch(IOError e) {
scannerResult.type = MessageType.ERROR;
- scannerResult.message = "Internal Error!";
+ scannerResult.message = _("Internal Error!");
scannerResult.audioType = AudioType.ERROR;
return scannerResult;
} catch(DatabaseError e) {
if(e is DatabaseError.PRODUCT_NOT_FOUND) {
scannerResult.type = MessageType.ERROR;
- scannerResult.message = "Error: unknown product: %llu".printf(ean);
+ scannerResult.message = _("Error: unknown product: %llu").printf(ean);
scannerResult.audioType = AudioType.ERROR;
} else {
scannerResult.type = MessageType.ERROR;
- scannerResult.message = "Error: %s".printf(e.message);
+ scannerResult.message = _("Error: %s").printf(e.message);
scannerResult.audioType = AudioType.ERROR;
}
return scannerResult;
@@ -186,13 +186,13 @@ public class ScannerSessionImplementation {
var pname = p.name;
scannerResult.type = MessageType.INFO;
- scannerResult.message = @"article info: $pname (Member: $mprice €, Guest: $gprice €)";
+ scannerResult.message = _("article info: %s (Member: %s €, Guest: %s €").printf(@"$pname", @"$mprice", @"$gprice");
scannerResult.audioType = AudioType.ERROR;
state = ScannerSessionState.READY;
return scannerResult;
case ScannerSessionCodeType.RFIDEM4100:
int user = db.get_userid_for_rfid(scannerdata);
- scannerResult.nextScannerdata =@"USER $user";
+ scannerResult.nextScannerdata = @"USER $user";
return scannerResult;
default:
state = ScannerSessionState.READY;
@@ -212,17 +212,17 @@ public class ScannerSessionImplementation {
p = db.get_product_for_ean(ean);
} catch(IOError e) {
scannerResult.type = MessageType.ERROR;
- scannerResult.message = "Internal Error!";
+ scannerResult.message = _("Internal Error!");
scannerResult.audioType = AudioType.ERROR;
return scannerResult;
} catch(DatabaseError e) {
if(e is DatabaseError.PRODUCT_NOT_FOUND) {
scannerResult.type = MessageType.ERROR;
- scannerResult.message = "Error: unknown product: %llu".printf(ean);
+ scannerResult.message = _("Error: unknown product: %llu").printf(ean);
scannerResult.audioType = AudioType.ERROR;
} else {
scannerResult.type = MessageType.ERROR;
- scannerResult.message = "Error: %s".printf(e.message);
+ scannerResult.message = _("Error: %s").printf(e.message);
scannerResult.audioType = AudioType.ERROR;
}
return scannerResult;
@@ -237,7 +237,7 @@ public class ScannerSessionImplementation {
}
scannerResult.type = MessageType.INFO;
- scannerResult.message = @"article added to shopping card: $(p.name) ($price €)";
+ scannerResult.message = _("article added to shopping card: %s (%s €)").printf(@"$(p.name)", @"$price");
scannerResult.audioType = AudioType.PURCHASE;
state = ScannerSessionState.USER;
break;
@@ -246,11 +246,11 @@ public class ScannerSessionImplementation {
var removedProduct = shoppingCard[shoppingCard.length-1];
shoppingCard = shoppingCard[0:shoppingCard.length-1];
scannerResult.type = MessageType.INFO;
- scannerResult.message = @"removed last Item from Shopping Cart: $(removedProduct.name)";
+ scannerResult.message = _("removed last Item from Shopping Cart: %s").printf(@"$(removedProduct.name)");
scannerResult.audioType = AudioType.INFO;
} else {
scannerResult.type = MessageType.INFO;
- scannerResult.message = @"No more Items on your Shopping Cart";
+ scannerResult.message = _("No more Items on your Shopping Cart");
scannerResult.audioType = AudioType.ERROR;
}
break;
@@ -286,7 +286,7 @@ public class ScannerSessionImplementation {
totalPrice += price;
}
scannerResult.type = MessageType.INFO;
- scannerResult.message = @"$name bought $amountOfItems items for $totalPrice €";
+ scannerResult.message = @_("%s bought %d items for %s €").printf(@"$name", amountOfItems, @"$totalPrice");
scannerResult.audioType = AudioType.INFO;
return scannerResult;
}
@@ -297,11 +297,11 @@ public class ScannerSessionImplementation {
if(interpret(scannerdata))
devScanner.blink(1000);
} catch(DBusError e) {
- send_message(MessageType.ERROR, "DBusError: %s", e.message);
+ send_message(MessageType.ERROR, _("DBus Error: %s"), e.message);
} catch(IOError e) {
- send_message(MessageType.ERROR, "IOError: %s", e.message);
+ send_message(MessageType.ERROR, _("IO Error: %s"), e.message);
} catch(DatabaseError e) {
- send_message(MessageType.ERROR, "DatabaseError: %s", e.message);
+ send_message(MessageType.ERROR, _("Database Error: %s"), e.message);
}
}
@@ -331,6 +331,4 @@ public class ScannerSessionImplementation {
state = ScannerSessionState.READY;
return scannerResult;
}
-
-
}
diff --git a/src/serial-device/Makefile b/src/serial-device/Makefile
index d80279d..9c810ea 100644
--- a/src/serial-device/Makefile
+++ b/src/serial-device/Makefile
@@ -2,7 +2,7 @@ all: serial-device
@echo > /dev/null
serial-device: main.vala serial-device.vala ../input-device/input-device-interface.vala ../config/config-interface.vala
- valac -X -w -o $@ --pkg linux --pkg posix --pkg gio-2.0 $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg linux --pkg posix --pkg gio-2.0 $^
clean:
rm -rf serial-device
diff --git a/src/serial-device/main.vala b/src/serial-device/main.vala
index 5d5a107..95926b5 100644
--- a/src/serial-device/main.vala
+++ b/src/serial-device/main.vala
@@ -17,34 +17,37 @@
Device scanner;
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
try {
Config cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
scanner = new Device(cfg.get_string("INPUT", "barcodescanner"), 9600, 8, 1);
} catch(IOError e) {
- error("IOError: %s\n", e.message);
+ error(_("IO Error: %s\n"), e.message);
} catch(KeyFileError e) {
- error("Config Error: %s\n", e.message);
+ error(_("Config Error: %s\n"), e.message);
} catch(DBusError e) {
- error("DBus Error: %s\n", e.message);
+ error(_("DBus Error: %s\n"), e.message);
}
Bus.own_name(
BusType.SYSTEM,
"io.mainframe.shopsystem.InputDevice",
BusNameOwnerFlags.NONE,
- on_bus_aquired,
+ on_bus_acquired,
() => {},
- () => stderr.printf("Could not aquire name\n"));
+ () => stderr.printf(_("Could not acquire name\n")));
new MainLoop().run();
return 0;
}
-void on_bus_aquired(DBusConnection con) {
+void on_bus_acquired(DBusConnection con) {
try {
con.register_object("/io/mainframe/shopsystem/devicescanner", scanner);
} catch(IOError e) {
- stderr.printf("Could not register service\n");
+ stderr.printf(_("Could not register service\n"));
}
}
diff --git a/src/serial-device/serial-device.vala b/src/serial-device/serial-device.vala
index ae7480b..3900128 100644
--- a/src/serial-device/serial-device.vala
+++ b/src/serial-device/serial-device.vala
@@ -36,11 +36,11 @@ public class Device {
if(lockfile.load_contents(null, out data, null)) {
pid = int.parse((string) data);
} else {
- error("Can't read lock file!\n");
+ error(_("Can't read lock file!\n"));
}
if(Posix.kill(pid, 0) == 0) {
- error("serial device is locked!\n");
+ error(_("serial device is locked!\n"));
}
}
@@ -52,11 +52,11 @@ public class Device {
if(fd < 0) {
fd = -1;
lockfile.delete();
- error("Could not open device!\n");
+ error(_("Could not open device!\n"));
}
} catch(Error e) {
- error("Could not create lock file: %s!\n", e.message);
+ error(_("Could not create lock file: %s!\n"), e.message);
}
@@ -160,12 +160,12 @@ public class Device {
io_read = new IOChannel.unix_new(fd);
io_read.set_line_term("\r\n", 2);
if(io_read.set_encoding(null) != IOStatus.NORMAL)
- error("Failed to set encoding");
+ error(_("Failed to set encoding"));
if(!(io_read.add_watch(IOCondition.IN | IOCondition.HUP, device_read) != 0)) {
- error("Could not bind IOChannel");
+ error(_("Could not bind IOChannel"));
}
} catch(IOChannelError e) {
- error("IOChannel: %s", e.message);
+ error(_("IOChannel: %s"), e.message);
}
}
@@ -186,7 +186,7 @@ public class Device {
size_t len, term_char;
if((cond & IOCondition.HUP) == IOCondition.HUP)
- error("Lost device");
+ error(_("Lost device"));
try {
ret = gio.read_line(out msg, out len, out term_char);
@@ -204,11 +204,11 @@ public class Device {
received_barcode(msg);
}
catch(IOChannelError e) {
- stderr.printf("IOChannel Error: %s", e.message);
+ stderr.printf(_("IOChannel Error: %s"), e.message);
return false;
}
catch(ConvertError e) {
- stderr.printf("Convert Error: %s", e.message);
+ stderr.printf(_("Convert Error: %s"), e.message);
return false;
}
return true;
diff --git a/src/web/Makefile b/src/web/Makefile
index e6094f6..22d7bbc 100644
--- a/src/web/Makefile
+++ b/src/web/Makefile
@@ -2,7 +2,7 @@ all: web
@echo > /dev/null
web: main.vala web.vala websession.vala csv.vala template.vala ../database/db-interface.vala ../pgp/pgp-interface.vala ../price.vapi ../config/config-interface.vala ../audio/audio-interface.vala
- valac -X -w -o $@ --vapidir=../../vapi --enable-experimental --pkg gee-0.8 --pkg gio-2.0 --pkg libsoup-2.4 --pkg posix $^
+ valac -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --vapidir=../../vapi --enable-experimental --pkg gee-0.8 --pkg gio-2.0 --pkg libsoup-2.4 --pkg posix $^
clean:
rm -rf web
diff --git a/src/web/main.vala b/src/web/main.vala
index e249d81..f7b0c30 100644
--- a/src/web/main.vala
+++ b/src/web/main.vala
@@ -21,6 +21,9 @@ public AudioPlayer audio;
string templatedir;
public static int main(string[] args) {
+ Intl.setlocale(LocaleCategory.ALL, "");
+ Intl.textdomain("shopsystem");
+
TlsCertificate? cert = null;
string certificate = "";
string privatekey = "";
@@ -30,7 +33,7 @@ public static int main(string[] args) {
db = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Database", "/io/mainframe/shopsystem/database");
pgp = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.PGP", "/io/mainframe/shopsystem/pgp");
cfg = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.Config", "/io/mainframe/shopsystem/config");
- audio = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.AudioPlayer", "/io/mainframe/shopsystem/audio");
+ audio = Bus.get_proxy_sync(BusType.SYSTEM, "io.mainframe.shopsystem.AudioPlayer", "/io/mainframe/shopsystem/audio");
templatedir = cfg.get_string("WEB", "filepath");
port = cfg.get_integer("WEB", "port");
@@ -38,19 +41,19 @@ public static int main(string[] args) {
certificate = cfg.get_string("WEB", "cert");
privatekey = cfg.get_string("WEB", "key");
} catch(KeyFileError e) {
- warning("KeyFileError: %s\n", e.message);
+ warning(_("KeyFile Error: %s\n"), e.message);
}
} catch(IOError e) {
- error("IOError: %s\n", e.message);
+ error(_("IO Error: %s\n"), e.message);
} catch(KeyFileError e) {
- error("KeyFileError: %s\n", e.message);
+ error(_("KeyFile Error: %s\n"), e.message);
} catch(DBusError e) {
- error("DBusError: %s\n", e.message);
+ error(_("DBus Error: %s\n"), e.message);
}
- stdout.printf("Web Server Port: %u\n", port);
- stdout.printf("TLS certificate: %s\n", certificate);
- stdout.printf("TLS private key: %s\n", privatekey);
+ stdout.printf(_("Web Server Port: %u\n"), port);
+ stdout.printf(_("TLS certificate: %s\n"), certificate);
+ stdout.printf(_("TLS private key: %s\n"), privatekey);
/* attach WebServer to MainLoop */
try {
@@ -58,7 +61,7 @@ public static int main(string[] args) {
cert = new TlsCertificate.from_files(certificate, privatekey);
new WebServer(port, cert);
} catch(Error e) {
- error("Could not start Webserver: %s\n", e.message);
+ error(_("Could not start Webserver: %s\n"), e.message);
}
/* start MainLoop */
diff --git a/src/web/template.vala b/src/web/template.vala
index b5265d8..f5c6d75 100644
--- a/src/web/template.vala
+++ b/src/web/template.vala
@@ -37,28 +37,28 @@ public class WebTemplate {
uint8[] basis, menu, template, auth;
if(!b.query_exists())
- throw new TemplateError.NOT_FOUND(templatedir+"base.html not found!");
+ throw new TemplateError.NOT_FOUND(_("%s not found!").printf(templatedir+"base.html"));
if(!m.query_exists())
- throw new TemplateError.NOT_FOUND(templatedir+"menu.html not found!");
+ throw new TemplateError.NOT_FOUND(_("%s not found!").printf(templatedir+"menu.html"));
if(!fauth.query_exists())
- throw new TemplateError.NOT_FOUND(fauth.get_path()+" not found!");
+ throw new TemplateError.NOT_FOUND(_("%s not found!").printf(fauth.get_path()));
if(!f.query_exists())
- throw new TemplateError.NOT_FOUND(templatedir+file+" not found!");
+ throw new TemplateError.NOT_FOUND(_("%s not found!").printf(templatedir+file));
try {
if(!b.load_contents(null, out basis, null))
- throw new TemplateError.NOT_LOADABLE(templatedir+"base.html could not be loaded!");
+ throw new TemplateError.NOT_LOADABLE(_("%s could not be loaded!").printf(templatedir+"base.html"));
if(!m.load_contents(null, out menu, null))
- throw new TemplateError.NOT_LOADABLE(templatedir+"menu.html could not be loaded!");
+ throw new TemplateError.NOT_LOADABLE(_("%s could not be loaded!").printf(templatedir+"menu.html"));
if(!fauth.load_contents(null, out auth, null))
- throw new TemplateError.NOT_LOADABLE(fauth.get_path()+" could not be loaded!");
+ throw new TemplateError.NOT_LOADABLE(_("%s could not be loaded!").printf(fauth.get_path()));
if(!f.load_contents(null, out template, null))
- throw new TemplateError.NOT_LOADABLE(templatedir+file+" could not be loaded!");
+ throw new TemplateError.NOT_LOADABLE(_("%s could not be loaded!").printf(templatedir+file));
} catch(Error e) {
- throw new TemplateError.NOT_LOADABLE("could not load templates!");
+ throw new TemplateError.NOT_LOADABLE(_("could not load templates!"));
}
this.template = ((string) basis).replace("{{{NAVBAR}}}", ((string) menu));
@@ -75,13 +75,13 @@ public class WebTemplate {
uint8[] template;
if(!f.query_exists())
- throw new TemplateError.NOT_FOUND(templatedir+file+" not found!");
+ throw new TemplateError.NOT_FOUND(_("%s not found!").printf(templatedir+file));
try {
if(!f.load_contents(null, out template, null))
- throw new TemplateError.NOT_LOADABLE(templatedir+file+" could not be loaded!");
+ throw new TemplateError.NOT_LOADABLE(_("%s could not be loaded!").printf(templatedir+file));
} catch(Error e) {
- throw new TemplateError.NOT_LOADABLE("could not load templates!");
+ throw new TemplateError.NOT_LOADABLE(_("could not load templates!"));
}
this.template = (string) template;
diff --git a/src/web/web.vala b/src/web/web.vala
index 695ec70..5524a7e 100644
--- a/src/web/web.vala
+++ b/src/web/web.vala
@@ -1140,7 +1140,7 @@ public class WebServer {
return;
}
} catch(Error e) {
- error("there has been some error: %s!\n", e.message);
+ error(_("Error: %s\n"), e.message);
}
handler_404(server, msg, path, query, client);
@@ -1479,7 +1479,7 @@ public class WebServer {
options |= Soup.ServerListenOptions.HTTPS;
if(!srv.listen_all(port, options)) {
- throw new GLib.IOError.FAILED("Could not setup webserver!");
+ throw new GLib.IOError.FAILED(_("Could not setup webserver!"));
}
/* index */
diff --git a/src/web/websession.vala b/src/web/websession.vala
index 839d0a2..85fd516 100644
--- a/src/web/websession.vala
+++ b/src/web/websession.vala
@@ -128,9 +128,9 @@ public class WebSession {
return;
}
var form_data = Soup.Form.decode((string) msg.request_body.data);
- if (form_data == null || !form_data.contains("user") || !form_data.contains("password")) {
- return;
- }
+ if (form_data == null || !form_data.contains("user") || !form_data.contains("password")) {
+ return;
+ }
/* get credentials */
@@ -162,7 +162,7 @@ public class WebSession {
setup_auth(user);
} else {
- stderr.printf("Login for user id %d failed\n", userid);
+ stderr.printf(_("Login for user id %d failed\n"), userid);
/* login failed */
failed=true;
}
--
cgit v1.2.3
From a5d597f4f67349195a6508b15097d1f1d5181c8d Mon Sep 17 00:00:00 2001
From: Sebastian Reichel
Date: Fri, 29 Jun 2018 14:10:35 +0200
Subject: README: use busctl instead of mdbus2
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 9a0dbf6..8c8dd87 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@ The system consists of multiple daemons written in Vala, which communicate
with each other using DBus.
Build Dependencies:
- * apt install build-essential valac libesmtp-dev libgpgme11-dev libncursesw5-dev libncurses5-dev libgee-0.8-dev libgmime-2.6-dev libarchive-dev libgstreamer1.0-dev libgtk2.0-dev librsvg2-dev libsoup2.4-dev libsqlite3-dev libpango1.0-dev libssl-dev dbus-x11 mdbus2 policykit-1
+ * apt install build-essential valac libesmtp-dev libgpgme11-dev libncursesw5-dev libncurses5-dev libgee-0.8-dev libgmime-2.6-dev libarchive-dev libgstreamer1.0-dev libgtk2.0-dev librsvg2-dev libsoup2.4-dev libsqlite3-dev libpango1.0-dev libssl-dev dbus-x11 policykit-1
Additional runtime dependencies:
* apt install fonts-lmodern gstreamer1.0-alsa gstreamer1.0-plugins-base
@@ -78,7 +78,7 @@ but you need to modify a few things.
* Create user
`sqlite3 shop.db "INSERT INTO users (id, email, firstname, lastname) VALUES (1, 'test@tester', 'Firstname', 'Lastname');"`
* Setup user password
- `mdbus2 -s io.mainframe.shopsystem.Database /io/mainframe/shopsystem/database io.mainframe.shopsystem.Database.SetUserPassword 1 "password"`
+ `busctl --system call io.mainframe.shopsystem.Database /io/mainframe/shopsystem/database io.mainframe.shopsystem.Database SetUserPassword id 1 "password"`
`sqlite3 shop.db "UPDATE authentication set superuser = 1, auth_users = 1, auth_products = 1, auth_cashbox = 1 where user = 1";`
* Demo Data
`sqlite3 shop.db "INSERT INTO categories (name) VALUES ('Getränke')";`
--
cgit v1.2.3
From 17560f8bf49a0be2e0b490159e29f4894fe12cba Mon Sep 17 00:00:00 2001
From: Sebastian Reichel
Date: Fri, 29 Jun 2018 00:56:47 +0200
Subject: libcairobarcode: move to src
---
libcairobarcode/.gitignore | 3 -
libcairobarcode/Makefile | 11 ----
libcairobarcode/code39.vala | 128 ------------------------------------
libcairobarcode/ean.vala | 142 ----------------------------------------
libcairobarcode/error.vala | 19 ------
src/libcairobarcode/.gitignore | 3 +
src/libcairobarcode/Makefile | 11 ++++
src/libcairobarcode/code39.vala | 128 ++++++++++++++++++++++++++++++++++++
src/libcairobarcode/ean.vala | 142 ++++++++++++++++++++++++++++++++++++++++
src/libcairobarcode/error.vala | 19 ++++++
src/pdf-stock/Makefile | 6 +-
11 files changed, 306 insertions(+), 306 deletions(-)
delete mode 100644 libcairobarcode/.gitignore
delete mode 100644 libcairobarcode/Makefile
delete mode 100644 libcairobarcode/code39.vala
delete mode 100644 libcairobarcode/ean.vala
delete mode 100644 libcairobarcode/error.vala
create mode 100644 src/libcairobarcode/.gitignore
create mode 100644 src/libcairobarcode/Makefile
create mode 100644 src/libcairobarcode/code39.vala
create mode 100644 src/libcairobarcode/ean.vala
create mode 100644 src/libcairobarcode/error.vala
diff --git a/libcairobarcode/.gitignore b/libcairobarcode/.gitignore
deleted file mode 100644
index 8721023..0000000
--- a/libcairobarcode/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-libcairobarcode.h
-libcairobarcode.so
-libcairobarcode.vapi
diff --git a/libcairobarcode/Makefile b/libcairobarcode/Makefile
deleted file mode 100644
index b51cf30..0000000
--- a/libcairobarcode/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-include ../config.mk
-
-all: libcairobarcode.so
-
-libcairobarcode.so: ean.vala code39.vala error.vala
- ${VALAC} -o $@ -H libcairobarcode.h --library libcairobarcode -X -fPIC -X -shared --pkg cairo $^
-
-clean:
- rm -f libcairobarcode.h libcairobarcode.so libcairobarcode.vapi
-
-.PHONY: all clean
diff --git a/libcairobarcode/code39.vala b/libcairobarcode/code39.vala
deleted file mode 100644
index dd3d8c5..0000000
--- a/libcairobarcode/code39.vala
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Copyright 2014, Sebastian Reichel
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-public class Code39 {
- Cairo.Context ctx;
- double width;
- double height;
-
- /* 0 = wide black, 1 = narrow black, 2 = wide white, 3 = narrow white */
- static const uint32[] lookup_table = {
- 0x1d8cd, 0xd9dc, 0x1c9dc, 0xc9dd, 0x1d8dc, 0xd8dd, 0x1c8dd, 0x1d9cc,
- 0xd9cd, 0x1c9cd, 0xdd9c, 0x1cd9c, 0xcd9d, 0x1dc9c, 0xdc9d, 0x1cc9d,
- 0x1dd8c, 0xdd8d, 0x1cd8d, 0x1dc8d, 0xddd8, 0x1cdd8, 0xcdd9, 0x1dcd8,
- 0xdcd9, 0x1ccd9, 0x1ddc8, 0xddc9, 0x1cdc9, 0x1dcc9, 0x9ddc, 0x18ddc,
- 0x8ddd, 0x19cdc, 0x9cdd, 0x18cdd, 0x19dcc, 0x9dcd, 0x18dcd, 0x1999d,
- 0x199d9, 0x19d99, 0x1d999, 0x19ccd
- };
-
- public Code39(Cairo.Context ctx, double width, double height) {
- this.ctx = ctx;
- this.width = width;
- this.height = height;
- }
-
- private int lookup_index(char c) throws BarcodeError {
- if(c >= '0' && c <= '9')
- return c - '0';
- if(c >= 'A' && c <= 'Z')
- return c - 'A' + 10;
- switch (c) {
- case '-':
- return 36;
- case '.':
- return 37;
- case ' ':
- return 38;
- case '$':
- return 39;
- case '/':
- return 40;
- case '+':
- return 41;
- case '%':
- return 42;
- case '*':
- return 43;
- default:
- throw new BarcodeError.UNEXPECTED_CHARACTER("Character '%c' is not allowed in Code 39".printf(c));
- }
- }
-
- private uint32 lookup(char c) throws BarcodeError {
- return lookup_table[lookup_index(c)];
- }
-
- private void draw_line(bool black, double linewidth) {
- double x,y;
-
- if(black)
- ctx.set_source_rgb(0, 0, 0);
- else
- ctx.set_source_rgb(1, 1, 1);
-
- ctx.rel_line_to(0,height);
- ctx.rel_move_to(linewidth,-height);
-
- ctx.get_current_point(out x, out y);
- ctx.stroke();
- ctx.move_to(x,y);
- }
-
- public void draw(string code) throws BarcodeError {
- string mycode = code;
-
- if(!mycode.has_prefix("*"))
- mycode = "*" + mycode;
- if(!mycode.has_suffix("*"))
- mycode = mycode + "*";
-
- double linewidth = width / (mycode.length * 13.0);
-
- ctx.save();
- ctx.set_line_width(linewidth);
- ctx.move_to(0,0);
- ctx.rel_move_to(0.5*linewidth,0);
-
- for(int i=0; i=0; j--) {
- var line = (format >> (2*j)) & 0x3;
-
- switch(line) {
- case 0:
- draw_line(true, linewidth);
- draw_line(true, linewidth);
- break;
- case 1:
- draw_line(true, linewidth);
- break;
- case 2:
- draw_line(false, linewidth);
- draw_line(false, linewidth);
- break;
- default:
- draw_line(false, linewidth);
- break;
- }
- }
-
- draw_line(false, linewidth);
- }
-
- ctx.restore();
- }
-}
diff --git a/libcairobarcode/ean.vala b/libcairobarcode/ean.vala
deleted file mode 100644
index 9709987..0000000
--- a/libcairobarcode/ean.vala
+++ /dev/null
@@ -1,142 +0,0 @@
-/* Copyright 2014, Sebastian Reichel
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-public class EAN {
- Cairo.Context ctx;
- double width;
- double height;
- const double marker_length_diff = 10.0;
- double basex;
- double basey;
-
- static const uint8[] C = { 0x00, 0x0b, 0x0d, 0x0e, 0x13, 0x19, 0x1c, 0x15, 0x16, 0x1a };
-
- static uint8[,] lookup = {
- { 0x0d, 0x19, 0x13, 0x3d, 0x23, 0x31, 0x2f, 0x3b, 0x37, 0x0b }, /* L */
- { 0x27, 0x33, 0x1b, 0x21, 0x1d, 0x39, 0x05, 0x11, 0x09, 0x17 }, /* G */
- { 0x72, 0x66, 0x6c, 0x42, 0x5c, 0x4e, 0x50, 0x44, 0x48, 0x74 } /* R */
- };
-
- public EAN(Cairo.Context ctx, double width, double height) {
- this.ctx = ctx;
- this.width = width / 95.0;
- this.height = height;
- }
-
- private void draw_line(bool black) {
- double x,y;
-
- if(black)
- ctx.set_source_rgb(0, 0, 0);
- else
- ctx.set_source_rgb(1, 1, 1);
-
- ctx.rel_line_to(0,height);
- ctx.rel_move_to(width,-height);
-
- ctx.get_current_point(out x, out y);
- ctx.stroke();
- ctx.move_to(x,y);
- }
-
- private void draw_startstop() {
- draw_line(true);
- draw_line(false);
- draw_line(true);
- }
-
- private void draw_middle() {
- draw_line(false);
- draw_line(true);
- draw_line(false);
- draw_line(true);
- draw_line(false);
- }
-
- private void draw_number_text(uint8 number) {
- double x, y;
- ctx.get_current_point(out x, out y);
- ctx.set_source_rgb(0, 0, 0);
-
- ctx.stroke();
- ctx.move_to(x,y);
- }
-
- private void draw_number(uint8 number, uint8 type) {
- draw_number_text(number);
-
- for(int i=6; i>=0; i--) {
- var bit = (lookup[type,number] >> i) & 1;
- draw_line(bit == 1);
- }
- }
-
- private uint8 get_number(string ean, int pos) {
- return (uint8) int.parse("%c".printf(ean[pos]));
- }
-
- private void draw13(string ean) {
- /* need some extra space for the checksum */
- width = (width * 95.0) / 102.0;
-
- uint8 LG = C[get_number(ean, 0)];
-
- draw_startstop();
- for(int i=1, x = 5; i<7; i++, x--) {
- uint8 type = (uint8) (LG >> x) & 1;
- draw_number(get_number(ean,i), type);
- }
- draw_middle();
- for(int i=7; i<13; i++)
- draw_number(get_number(ean,i), 2);
- draw_startstop();
-
- /* remove extra space for the checksum */
- width = (width * 102.0) / 95.0;
-
- }
-
- private void draw8(string ean) {
- draw_startstop();
- for(int i=0; i<4; i++)
- draw_number(get_number(ean,i), 0);
- draw_middle();
- for(int i=4; i<8; i++)
- draw_number(get_number(ean,i), 2);
- draw_startstop();
- }
-
- public void draw(string ean) throws BarcodeError {
- ctx.save();
- ctx.set_line_width(width);
- ctx.get_current_point(out basex, out basey);
- ctx.rel_move_to(0.5*width,0);
-
- for(int i=0; i '9') {
- throw new BarcodeError.UNEXPECTED_CHARACTER("Character '%c' is not allowed in EAN".printf(ean[i]));
- }
- }
-
- if(ean.length == 13)
- draw13(ean);
- else if(ean.length == 8)
- draw8(ean);
- else
- throw new BarcodeError.UNEXPECTED_LENGTH("length of EAN is incorrect (must be 8 or 13)");
-
- ctx.restore();
- }
-}
diff --git a/libcairobarcode/error.vala b/libcairobarcode/error.vala
deleted file mode 100644
index f81df91..0000000
--- a/libcairobarcode/error.vala
+++ /dev/null
@@ -1,19 +0,0 @@
-/* Copyright 2014, Sebastian Reichel
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-public errordomain BarcodeError {
- UNEXPECTED_CHARACTER,
- UNEXPECTED_LENGTH
-}
diff --git a/src/libcairobarcode/.gitignore b/src/libcairobarcode/.gitignore
new file mode 100644
index 0000000..8721023
--- /dev/null
+++ b/src/libcairobarcode/.gitignore
@@ -0,0 +1,3 @@
+libcairobarcode.h
+libcairobarcode.so
+libcairobarcode.vapi
diff --git a/src/libcairobarcode/Makefile b/src/libcairobarcode/Makefile
new file mode 100644
index 0000000..b51cf30
--- /dev/null
+++ b/src/libcairobarcode/Makefile
@@ -0,0 +1,11 @@
+include ../config.mk
+
+all: libcairobarcode.so
+
+libcairobarcode.so: ean.vala code39.vala error.vala
+ ${VALAC} -o $@ -H libcairobarcode.h --library libcairobarcode -X -fPIC -X -shared --pkg cairo $^
+
+clean:
+ rm -f libcairobarcode.h libcairobarcode.so libcairobarcode.vapi
+
+.PHONY: all clean
diff --git a/src/libcairobarcode/code39.vala b/src/libcairobarcode/code39.vala
new file mode 100644
index 0000000..ca5451c
--- /dev/null
+++ b/src/libcairobarcode/code39.vala
@@ -0,0 +1,128 @@
+/* Copyright 2014, Sebastian Reichel
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+public class Code39 {
+ Cairo.Context ctx;
+ double width;
+ double height;
+
+ /* 0 = wide black, 1 = narrow black, 2 = wide white, 3 = narrow white */
+ const uint32[] lookup_table = {
+ 0x1d8cd, 0xd9dc, 0x1c9dc, 0xc9dd, 0x1d8dc, 0xd8dd, 0x1c8dd, 0x1d9cc,
+ 0xd9cd, 0x1c9cd, 0xdd9c, 0x1cd9c, 0xcd9d, 0x1dc9c, 0xdc9d, 0x1cc9d,
+ 0x1dd8c, 0xdd8d, 0x1cd8d, 0x1dc8d, 0xddd8, 0x1cdd8, 0xcdd9, 0x1dcd8,
+ 0xdcd9, 0x1ccd9, 0x1ddc8, 0xddc9, 0x1cdc9, 0x1dcc9, 0x9ddc, 0x18ddc,
+ 0x8ddd, 0x19cdc, 0x9cdd, 0x18cdd, 0x19dcc, 0x9dcd, 0x18dcd, 0x1999d,
+ 0x199d9, 0x19d99, 0x1d999, 0x19ccd
+ };
+
+ public Code39(Cairo.Context ctx, double width, double height) {
+ this.ctx = ctx;
+ this.width = width;
+ this.height = height;
+ }
+
+ private int lookup_index(char c) throws BarcodeError {
+ if(c >= '0' && c <= '9')
+ return c - '0';
+ if(c >= 'A' && c <= 'Z')
+ return c - 'A' + 10;
+ switch (c) {
+ case '-':
+ return 36;
+ case '.':
+ return 37;
+ case ' ':
+ return 38;
+ case '$':
+ return 39;
+ case '/':
+ return 40;
+ case '+':
+ return 41;
+ case '%':
+ return 42;
+ case '*':
+ return 43;
+ default:
+ throw new BarcodeError.UNEXPECTED_CHARACTER("Character '%c' is not allowed in Code 39".printf(c));
+ }
+ }
+
+ private uint32 lookup(char c) throws BarcodeError {
+ return lookup_table[lookup_index(c)];
+ }
+
+ private void draw_line(bool black, double linewidth) {
+ double x,y;
+
+ if(black)
+ ctx.set_source_rgb(0, 0, 0);
+ else
+ ctx.set_source_rgb(1, 1, 1);
+
+ ctx.rel_line_to(0,height);
+ ctx.rel_move_to(linewidth,-height);
+
+ ctx.get_current_point(out x, out y);
+ ctx.stroke();
+ ctx.move_to(x,y);
+ }
+
+ public void draw(string code) throws BarcodeError {
+ string mycode = code;
+
+ if(!mycode.has_prefix("*"))
+ mycode = "*" + mycode;
+ if(!mycode.has_suffix("*"))
+ mycode = mycode + "*";
+
+ double linewidth = width / (mycode.length * 13.0);
+
+ ctx.save();
+ ctx.set_line_width(linewidth);
+ ctx.move_to(0,0);
+ ctx.rel_move_to(0.5*linewidth,0);
+
+ for(int i=0; i=0; j--) {
+ var line = (format >> (2*j)) & 0x3;
+
+ switch(line) {
+ case 0:
+ draw_line(true, linewidth);
+ draw_line(true, linewidth);
+ break;
+ case 1:
+ draw_line(true, linewidth);
+ break;
+ case 2:
+ draw_line(false, linewidth);
+ draw_line(false, linewidth);
+ break;
+ default:
+ draw_line(false, linewidth);
+ break;
+ }
+ }
+
+ draw_line(false, linewidth);
+ }
+
+ ctx.restore();
+ }
+}
diff --git a/src/libcairobarcode/ean.vala b/src/libcairobarcode/ean.vala
new file mode 100644
index 0000000..4a4f621
--- /dev/null
+++ b/src/libcairobarcode/ean.vala
@@ -0,0 +1,142 @@
+/* Copyright 2014, Sebastian Reichel
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+public class EAN {
+ Cairo.Context ctx;
+ double width;
+ double height;
+ const double marker_length_diff = 10.0;
+ double basex;
+ double basey;
+
+ const uint8[] C = { 0x00, 0x0b, 0x0d, 0x0e, 0x13, 0x19, 0x1c, 0x15, 0x16, 0x1a };
+
+ static uint8[,] lookup = {
+ { 0x0d, 0x19, 0x13, 0x3d, 0x23, 0x31, 0x2f, 0x3b, 0x37, 0x0b }, /* L */
+ { 0x27, 0x33, 0x1b, 0x21, 0x1d, 0x39, 0x05, 0x11, 0x09, 0x17 }, /* G */
+ { 0x72, 0x66, 0x6c, 0x42, 0x5c, 0x4e, 0x50, 0x44, 0x48, 0x74 } /* R */
+ };
+
+ public EAN(Cairo.Context ctx, double width, double height) {
+ this.ctx = ctx;
+ this.width = width / 95.0;
+ this.height = height;
+ }
+
+ private void draw_line(bool black) {
+ double x,y;
+
+ if(black)
+ ctx.set_source_rgb(0, 0, 0);
+ else
+ ctx.set_source_rgb(1, 1, 1);
+
+ ctx.rel_line_to(0,height);
+ ctx.rel_move_to(width,-height);
+
+ ctx.get_current_point(out x, out y);
+ ctx.stroke();
+ ctx.move_to(x,y);
+ }
+
+ private void draw_startstop() {
+ draw_line(true);
+ draw_line(false);
+ draw_line(true);
+ }
+
+ private void draw_middle() {
+ draw_line(false);
+ draw_line(true);
+ draw_line(false);
+ draw_line(true);
+ draw_line(false);
+ }
+
+ private void draw_number_text(uint8 number) {
+ double x, y;
+ ctx.get_current_point(out x, out y);
+ ctx.set_source_rgb(0, 0, 0);
+
+ ctx.stroke();
+ ctx.move_to(x,y);
+ }
+
+ private void draw_number(uint8 number, uint8 type) {
+ draw_number_text(number);
+
+ for(int i=6; i>=0; i--) {
+ var bit = (lookup[type,number] >> i) & 1;
+ draw_line(bit == 1);
+ }
+ }
+
+ private uint8 get_number(string ean, int pos) {
+ return (uint8) int.parse("%c".printf(ean[pos]));
+ }
+
+ private void draw13(string ean) {
+ /* need some extra space for the checksum */
+ width = (width * 95.0) / 102.0;
+
+ uint8 LG = C[get_number(ean, 0)];
+
+ draw_startstop();
+ for(int i=1, x = 5; i<7; i++, x--) {
+ uint8 type = (uint8) (LG >> x) & 1;
+ draw_number(get_number(ean,i), type);
+ }
+ draw_middle();
+ for(int i=7; i<13; i++)
+ draw_number(get_number(ean,i), 2);
+ draw_startstop();
+
+ /* remove extra space for the checksum */
+ width = (width * 102.0) / 95.0;
+
+ }
+
+ private void draw8(string ean) {
+ draw_startstop();
+ for(int i=0; i<4; i++)
+ draw_number(get_number(ean,i), 0);
+ draw_middle();
+ for(int i=4; i<8; i++)
+ draw_number(get_number(ean,i), 2);
+ draw_startstop();
+ }
+
+ public void draw(string ean) throws BarcodeError {
+ ctx.save();
+ ctx.set_line_width(width);
+ ctx.get_current_point(out basex, out basey);
+ ctx.rel_move_to(0.5*width,0);
+
+ for(int i=0; i '9') {
+ throw new BarcodeError.UNEXPECTED_CHARACTER("Character '%c' is not allowed in EAN".printf(ean[i]));
+ }
+ }
+
+ if(ean.length == 13)
+ draw13(ean);
+ else if(ean.length == 8)
+ draw8(ean);
+ else
+ throw new BarcodeError.UNEXPECTED_LENGTH("length of EAN is incorrect (must be 8 or 13)");
+
+ ctx.restore();
+ }
+}
diff --git a/src/libcairobarcode/error.vala b/src/libcairobarcode/error.vala
new file mode 100644
index 0000000..f81df91
--- /dev/null
+++ b/src/libcairobarcode/error.vala
@@ -0,0 +1,19 @@
+/* Copyright 2014, Sebastian Reichel
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+public errordomain BarcodeError {
+ UNEXPECTED_CHARACTER,
+ UNEXPECTED_LENGTH
+}
diff --git a/src/pdf-stock/Makefile b/src/pdf-stock/Makefile
index c310c65..4e8f472 100644
--- a/src/pdf-stock/Makefile
+++ b/src/pdf-stock/Makefile
@@ -1,18 +1,18 @@
include ../../config.mk
-LIBCAIROBARCODE=-X -I../../libcairobarcode -X -L../../libcairobarcode -X -lcairobarcode
+LIBCAIROBARCODE=-X -I../libcairobarcode -X -L../libcairobarcode -X -lcairobarcode
all: pdf-stock
@echo > /dev/null
-pdf-stock: main.vala pdf-stock.vala ../database/db-interface.vala ../price.vapi ../../libcairobarcode/libcairobarcode.vapi
+pdf-stock: main.vala pdf-stock.vala ../database/db-interface.vala ../price.vapi ../libcairobarcode/libcairobarcode.vapi
${VALAC} -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w ${LIBCAIROBARCODE} -o $@ --pkg cairo --pkg pangocairo --pkg gio-2.0 --pkg posix $^
test: test.vala pdf-stock-interface.vala
${VALAC} -X -D'GETTEXT_PACKAGE="shopsystem"' -X -w -o $@ --pkg gio-2.0 $^
run: pdf-stock
- LD_LIBRARY_PATH=../../libcairobarcode ./pdf-stock
+ LD_LIBRARY_PATH=../libcairobarcode ./pdf-stock
clean:
rm -rf pdf-stock test
--
cgit v1.2.3
From 22df08c29f6e4566d418e2a80a8279303406a733 Mon Sep 17 00:00:00 2001
From: Sebastian Reichel
Date: Fri, 29 Jun 2018 00:58:19 +0200
Subject: build: switch to meson buildsystem
We build the project with 'meson' now instead of using custom
configure script + GNU Makefiles. To build the project, go to
the project root directory and use the following commands:
Building:
meson build
cd build
ninja
Update Language Files:
ninja shopsystem-pot
ninja shopsystem-update-po
Install:
DESTDIR=./tmp ninja install
---
.gitignore | 1 +
LICENSE | 22 +-
Makefile | 26 +-
README.md | 15 +-
configure | 56 -
configure-functions.sh | 351 -
data/dbus/.gitignore | 1 +
.../io.mainframe.shopsystem.AudioPlayer.conf | 17 +
.../config/io.mainframe.shopsystem.Config.conf | 17 +
.../config/io.mainframe.shopsystem.Database.conf | 17 +
.../io.mainframe.shopsystem.InputDevice.conf | 17 +
.../config/io.mainframe.shopsystem.InvoicePDF.conf | 17 +
data/dbus/config/io.mainframe.shopsystem.Mail.conf | 17 +
data/dbus/config/io.mainframe.shopsystem.PGP.conf | 17 +
.../io.mainframe.shopsystem.ScannerSession.conf | 17 +
.../io.mainframe.shopsystem.SerialDevice.conf | 17 +
.../config/io.mainframe.shopsystem.StockPDF.conf | 17 +
.../io.mainframe.shopsystem.AudioPlayer.service.in | 4 +
.../dbus/io.mainframe.shopsystem.Config.service.in | 4 +
.../io.mainframe.shopsystem.Database.service.in | 4 +
.../io.mainframe.shopsystem.InputDevice.service.in | 4 +
.../io.mainframe.shopsystem.InvoicePDF.service.in | 4 +
data/dbus/io.mainframe.shopsystem.Mail.service.in | 4 +
data/dbus/io.mainframe.shopsystem.PGP.service.in | 4 +
....mainframe.shopsystem.ScannerSession.service.in | 4 +
...io.mainframe.shopsystem.SerialDevice.service.in | 4 +
.../io.mainframe.shopsystem.StockPDF.service.in | 4 +
data/dbus/meson.build | 28 +
data/invoice/footer-line.svg | 115 +
data/invoice/invoice.final.html | 11 +
data/invoice/invoice.final.txt | 11 +
data/invoice/invoice.temporary.html | 17 +
data/invoice/invoice.temporary.txt | 17 +
data/invoice/logo.svg | 198 +
data/invoice/pdf-template.txt | 12 +
data/invoice/treasurer.mail.txt | 8 +
data/invoice/vat.html | 4 +
data/invoice/vat.txt | 3 +
data/logo.txt | 6 +
data/meson.build | 10 +
data/po/LINGUAS | 1 +
data/po/POTFILES | 47 +
data/po/de.po | 477 +
data/po/meson.build | 2 +
data/po/shopsystem.pot | 475 +
data/sounds/system/LICENSE | 14 +
data/sounds/system/error.ogg | Bin 0 -> 15582 bytes
data/sounds/system/shutdown.ogg | Bin 0 -> 35991 bytes
data/sounds/system/startup.ogg | Bin 0 -> 23405 bytes
data/sounds/user/beep/LICENSE | 24 +
data/sounds/user/beep/error/buzzer.ogg | Bin 0 -> 27153 bytes
data/sounds/user/beep/login/toycaralarm.ogg | Bin 0 -> 8737 bytes
data/sounds/user/beep/logout/cha ching.ogg | Bin 0 -> 31476 bytes
data/sounds/user/beep/purchase/beep.ogg | Bin 0 -> 5665 bytes
data/sounds/user/beep/purchase/camera.ogg | Bin 0 -> 14605 bytes
data/sounds/user/dude/LICENSE | 19 +
data/sounds/user/dude/error/I broke something.ogg | Bin 0 -> 76434 bytes
.../user/dude/login/hey dude, whats up man.ogg | Bin 0 -> 57447 bytes
data/sounds/user/dude/logout/bye.ogg | Bin 0 -> 27610 bytes
data/sounds/user/dude/purchase/yeah man.ogg | Bin 0 -> 38743 bytes
data/sounds/user/girlfriend/LICENSE | 39 +
.../user/girlfriend/error/no, no, forget it.ogg | Bin 0 -> 26872 bytes
data/sounds/user/girlfriend/login/hello.ogg | Bin 0 -> 9918 bytes
.../login/we need to talk about this.ogg | Bin 0 -> 37918 bytes
.../girlfriend/logout/get out, get outta here.ogg | Bin 0 -> 29444 bytes
.../logout/i dont have time for this.ogg | Bin 0 -> 34138 bytes
.../logout/why dont you just leave then.ogg | Bin 0 -> 35918 bytes
.../user/girlfriend/purchase/code accepted.ogg | Bin 0 -> 17678 bytes
.../user/girlfriend/purchase/yeah so what.ogg | Bin 0 -> 25896 bytes
data/sounds/user/japanese/LICENSE | 29 +
data/sounds/user/japanese/error/no, iie.ogg | Bin 0 -> 13226 bytes
data/sounds/user/japanese/error/sumimasen.ogg | Bin 0 -> 19912 bytes
data/sounds/user/japanese/login/hello.ogg | Bin 0 -> 20760 bytes
data/sounds/user/japanese/logout/goodbye.ogg | Bin 0 -> 20223 bytes
.../sounds/user/japanese/purchase/domo-arigato.ogg | Bin 0 -> 12280 bytes
data/sounds/user/japanese/purchase/domo.ogg | Bin 0 -> 8290 bytes
data/sounds/user/robot/LICENSE | 19 +
data/sounds/user/robot/error/critical error.ogg | Bin 0 -> 21345 bytes
data/sounds/user/robot/login/login accepted.ogg | Bin 0 -> 32165 bytes
data/sounds/user/robot/logout/terminating.ogg | Bin 0 -> 18148 bytes
data/sounds/user/robot/purchase/accepted.ogg | Bin 0 -> 16324 bytes
data/sounds/user/sexy/LICENSE | 39 +
data/sounds/user/sexy/error/error.ogg | Bin 0 -> 15582 bytes
.../user/sexy/login/welcome back sexy bitch.ogg | Bin 0 -> 56041 bytes
.../sexy/logout/well if thats how you feel.ogg | Bin 0 -> 36308 bytes
data/sounds/user/sexy/logout/what about me.ogg | Bin 0 -> 26986 bytes
data/sounds/user/sexy/purchase/do it again.ogg | Bin 0 -> 65485 bytes
data/sounds/user/sexy/purchase/what is next.ogg | Bin 0 -> 19297 bytes
data/sounds/user/sexy/purchase/yes, master.ogg | Bin 0 -> 24812 bytes
.../user/sexy/purchase/your will is my command.ogg | Bin 0 -> 41467 bytes
data/systemd/meson.build | 18 +
data/systemd/shopsystem-display-on-off.service.in | 12 +
data/systemd/shopsystem-input-device.service.in | 20 +
data/systemd/shopsystem-mail-backup.service.in | 12 +
data/systemd/shopsystem-mail-backup.timer | 9 +
.../shopsystem-mail-daily-invoice.service.in | 13 +
data/systemd/shopsystem-mail-daily-invoice.timer | 9 +
.../shopsystem-mail-monthly-invoice.service.in | 13 +
data/systemd/shopsystem-mail-monthly-invoice.timer | 9 +
data/systemd/shopsystem-ui.service.in | 17 +
data/systemd/shopsystem-web.service.in | 13 +
data/templates/aliases/index.html | 17 +
data/templates/aliases/new.html | 14 +
data/templates/base.html | 26 +
data/templates/cashbox/add.html | 17 +
data/templates/cashbox/detail.html | 26 +
data/templates/cashbox/index.html | 21 +
data/templates/cashbox/selection.html | 17 +
data/templates/css/base.css | 91 +
data/templates/css/bootstrap.css | 6167 +++++++++++
data/templates/errors/400.html | 5 +
data/templates/errors/403.html | 1 +
data/templates/errors/todo.html | 1 +
data/templates/img/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes
data/templates/img/glyphicons-halflings.png | Bin 0 -> 12799 bytes
data/templates/index.html | 1 +
data/templates/js/bootstrap.js | 2280 ++++
data/templates/js/code39.js | 148 +
data/templates/js/jquery.flot.js | 2599 +++++
data/templates/js/jquery.flot.navigate.js | 321 +
data/templates/js/jquery.flot.pie.js | 750 ++
data/templates/js/jquery.flot.selection.js | 344 +
data/templates/js/jquery.js | 10346 +++++++++++++++++++
data/templates/js/jquery.mousewheel.js | 84 +
data/templates/js/sorttable.js | 40 +
data/templates/logout.html | 1 +
data/templates/menu.html | 32 +
data/templates/menu_login.html | 5 +
data/templates/menu_logout.html | 12 +
data/templates/products/bestbefore.html | 8 +
data/templates/products/entry.html | 64 +
data/templates/products/index.html | 24 +
data/templates/products/new.html | 22 +
data/templates/products/newprice.html | 18 +
data/templates/products/restock.html | 22 +
data/templates/stats/index.html | 19 +
data/templates/stats/profit_per_day.html | 80 +
data/templates/stats/profit_per_product.html | 42 +
data/templates/stats/profit_per_weekday.html | 30 +
data/templates/stats/stock.html | 83 +
data/templates/users/entry.html | 123 +
data/templates/users/import-pgp.html | 22 +
data/templates/users/import.html | 39 +
data/templates/users/index.html | 8 +
data/templates/users/invoice.html | 39 +
data/udev/meson.build | 6 +
data/udev/shopsystem-input-device-rfid.rules | 1 +
data/udev/shopsystem-input-device.rules | 1 +
dbus/.gitignore | 1 -
dbus/Makefile | 27 -
dbus/config/Makefile | 15 -
.../io.mainframe.shopsystem.AudioPlayer.conf | 17 -
dbus/config/io.mainframe.shopsystem.Config.conf | 17 -
dbus/config/io.mainframe.shopsystem.Database.conf | 17 -
.../io.mainframe.shopsystem.InputDevice.conf | 17 -
.../config/io.mainframe.shopsystem.InvoicePDF.conf | 17 -
dbus/config/io.mainframe.shopsystem.Mail.conf | 17 -
dbus/config/io.mainframe.shopsystem.PGP.conf | 17 -
.../io.mainframe.shopsystem.ScannerSession.conf | 17 -
.../io.mainframe.shopsystem.SerialDevice.conf | 17 -
dbus/config/io.mainframe.shopsystem.StockPDF.conf | 17 -
.../io.mainframe.shopsystem.AudioPlayer.service.in | 4 -
dbus/io.mainframe.shopsystem.Config.service.in | 4 -
dbus/io.mainframe.shopsystem.Database.service.in | 4 -
.../io.mainframe.shopsystem.InputDevice.service.in | 4 -
dbus/io.mainframe.shopsystem.InvoicePDF.service.in | 4 -
dbus/io.mainframe.shopsystem.Mail.service.in | 4 -
dbus/io.mainframe.shopsystem.PGP.service.in | 4 -
....mainframe.shopsystem.ScannerSession.service.in | 4 -
...io.mainframe.shopsystem.SerialDevice.service.in | 4 -
dbus/io.mainframe.shopsystem.StockPDF.service.in | 4 -
invoice/footer-line.svg | 115 -
invoice/invoice.final.html | 11 -
invoice/invoice.final.txt | 11 -
invoice/invoice.temporary.html | 17 -
invoice/invoice.temporary.txt | 17 -
invoice/logo.svg | 198 -
invoice/pdf-template.txt | 12 -
invoice/treasurer.mail.txt | 8 -
invoice/vat.html | 4 -
invoice/vat.txt | 3 -
locale/de.po | 481 -
logo.txt | 6 -
meson.build | 3 +
sounds/system/LICENSE | 14 -
sounds/system/error.ogg | Bin 15582 -> 0 bytes
sounds/system/shutdown.ogg | Bin 35991 -> 0 bytes
sounds/system/startup.ogg | Bin 23405 -> 0 bytes
sounds/user/beep/LICENSE | 24 -
sounds/user/beep/error/buzzer.ogg | Bin 27153 -> 0 bytes
sounds/user/beep/login/toycaralarm.ogg | Bin 8737 -> 0 bytes
sounds/user/beep/logout/cha ching.ogg | Bin 31476 -> 0 bytes
sounds/user/beep/purchase/beep.ogg | Bin 5665 -> 0 bytes
sounds/user/beep/purchase/camera.ogg | Bin 14605 -> 0 bytes
sounds/user/dude/LICENSE | 19 -
sounds/user/dude/error/I broke something.ogg | Bin 76434 -> 0 bytes
sounds/user/dude/login/hey dude, whats up man.ogg | Bin 57447 -> 0 bytes
sounds/user/dude/logout/bye.ogg | Bin 27610 -> 0 bytes
sounds/user/dude/purchase/yeah man.ogg | Bin 38743 -> 0 bytes
sounds/user/girlfriend/LICENSE | 39 -
sounds/user/girlfriend/error/no, no, forget it.ogg | Bin 26872 -> 0 bytes
sounds/user/girlfriend/login/hello.ogg | Bin 9918 -> 0 bytes
.../login/we need to talk about this.ogg | Bin 37918 -> 0 bytes
.../girlfriend/logout/get out, get outta here.ogg | Bin 29444 -> 0 bytes
.../logout/i dont have time for this.ogg | Bin 34138 -> 0 bytes
.../logout/why dont you just leave then.ogg | Bin 35918 -> 0 bytes
sounds/user/girlfriend/purchase/code accepted.ogg | Bin 17678 -> 0 bytes
sounds/user/girlfriend/purchase/yeah so what.ogg | Bin 25896 -> 0 bytes
sounds/user/japanese/LICENSE | 29 -
sounds/user/japanese/error/no, iie.ogg | Bin 13226 -> 0 bytes
sounds/user/japanese/error/sumimasen.ogg | Bin 19912 -> 0 bytes
sounds/user/japanese/login/hello.ogg | Bin 20760 -> 0 bytes
sounds/user/japanese/logout/goodbye.ogg | Bin 20223 -> 0 bytes
sounds/user/japanese/purchase/domo-arigato.ogg | Bin 12280 -> 0 bytes
sounds/user/japanese/purchase/domo.ogg | Bin 8290 -> 0 bytes
sounds/user/robot/LICENSE | 19 -
sounds/user/robot/error/critical error.ogg | Bin 21345 -> 0 bytes
sounds/user/robot/login/login accepted.ogg | Bin 32165 -> 0 bytes
sounds/user/robot/logout/terminating.ogg | Bin 18148 -> 0 bytes
sounds/user/robot/purchase/accepted.ogg | Bin 16324 -> 0 bytes
sounds/user/sexy/LICENSE | 39 -
sounds/user/sexy/error/error.ogg | Bin 15582 -> 0 bytes
sounds/user/sexy/login/welcome back sexy bitch.ogg | Bin 56041 -> 0 bytes
.../sexy/logout/well if thats how you feel.ogg | Bin 36308 -> 0 bytes
sounds/user/sexy/logout/what about me.ogg | Bin 26986 -> 0 bytes
sounds/user/sexy/purchase/do it again.ogg | Bin 65485 -> 0 bytes
sounds/user/sexy/purchase/what is next.ogg | Bin 19297 -> 0 bytes
sounds/user/sexy/purchase/yes, master.ogg | Bin 24812 -> 0 bytes
.../user/sexy/purchase/your will is my command.ogg | Bin 41467 -> 0 bytes
src/Makefile | 9 -
src/audio/Makefile | 10 -
src/backup/Makefile | 10 -
src/cli/Makefile | 10 -
src/config/Makefile | 10 -
src/curses-ui/Makefile | 10 -
src/database/Makefile | 10 -
src/input-device/Makefile | 10 -
src/invoice/Makefile | 13 -
src/libcairobarcode/Makefile | 11 -
src/mail/Makefile | 10 -
src/meson.build | 52 +
src/pdf-invoice/Makefile | 13 -
src/pdf-stock/Makefile | 20 -
src/pgp/Makefile | 10 -
src/scanner-session/Makefile | 10 -
src/serial-device/Makefile | 10 -
src/web/Makefile | 10 -
systemd/Makefile | 23 -
systemd/shopsystem-display-on-off.service | 12 -
systemd/shopsystem-input-device-rfid.rules | 1 -
systemd/shopsystem-input-device.rules | 1 -
systemd/shopsystem-input-device.service | 20 -
systemd/shopsystem-mail-backup.service | 12 -
systemd/shopsystem-mail-backup.timer | 9 -
systemd/shopsystem-mail-daily-invoice.service | 13 -
systemd/shopsystem-mail-daily-invoice.timer | 9 -
systemd/shopsystem-mail-monthly-invoice.service | 13 -
systemd/shopsystem-mail-monthly-invoice.timer | 9 -
systemd/shopsystem-ui.service | 17 -
systemd/shopsystem-web.service | 13 -
templates/aliases/index.html | 17 -
templates/aliases/new.html | 14 -
templates/base.html | 26 -
templates/cashbox/add.html | 17 -
templates/cashbox/detail.html | 26 -
templates/cashbox/index.html | 21 -
templates/cashbox/selection.html | 17 -
templates/css/base.css | 91 -
templates/css/bootstrap.css | 6167 -----------
templates/errors/400.html | 5 -
templates/errors/403.html | 1 -
templates/errors/todo.html | 1 -
templates/img/glyphicons-halflings-white.png | Bin 8777 -> 0 bytes
templates/img/glyphicons-halflings.png | Bin 12799 -> 0 bytes
templates/index.html | 1 -
templates/js/bootstrap.js | 2280 ----
templates/js/code39.js | 148 -
templates/js/jquery.flot.js | 2599 -----
templates/js/jquery.flot.navigate.js | 321 -
templates/js/jquery.flot.pie.js | 750 --
templates/js/jquery.flot.selection.js | 344 -
templates/js/jquery.js | 10346 -------------------
templates/js/jquery.mousewheel.js | 84 -
templates/js/sorttable.js | 40 -
templates/logout.html | 1 -
templates/menu.html | 32 -
templates/menu_login.html | 5 -
templates/menu_logout.html | 12 -
templates/products/bestbefore.html | 8 -
templates/products/entry.html | 64 -
templates/products/index.html | 24 -
templates/products/new.html | 22 -
templates/products/newprice.html | 18 -
templates/products/restock.html | 22 -
templates/stats/index.html | 19 -
templates/stats/profit_per_day.html | 80 -
templates/stats/profit_per_product.html | 42 -
templates/stats/profit_per_weekday.html | 30 -
templates/stats/stock.html | 83 -
templates/users/entry.html | 123 -
templates/users/import-pgp.html | 22 -
templates/users/import.html | 39 -
templates/users/index.html | 8 -
templates/users/invoice.html | 39 -
304 files changed, 26081 insertions(+), 26109 deletions(-)
delete mode 100755 configure
delete mode 100755 configure-functions.sh
create mode 100644 data/dbus/.gitignore
create mode 100644 data/dbus/config/io.mainframe.shopsystem.AudioPlayer.conf
create mode 100644 data/dbus/config/io.mainframe.shopsystem.Config.conf
create mode 100644 data/dbus/config/io.mainframe.shopsystem.Database.conf
create mode 100644 data/dbus/config/io.mainframe.shopsystem.InputDevice.conf
create mode 100644 data/dbus/config/io.mainframe.shopsystem.InvoicePDF.conf
create mode 100644 data/dbus/config/io.mainframe.shopsystem.Mail.conf
create mode 100644 data/dbus/config/io.mainframe.shopsystem.PGP.conf
create mode 100644 data/dbus/config/io.mainframe.shopsystem.ScannerSession.conf
create mode 100644 data/dbus/config/io.mainframe.shopsystem.SerialDevice.conf
create mode 100644 data/dbus/config/io.mainframe.shopsystem.StockPDF.conf
create mode 100644 data/dbus/io.mainframe.shopsystem.AudioPlayer.service.in
create mode 100644 data/dbus/io.mainframe.shopsystem.Config.service.in
create mode 100644 data/dbus/io.mainframe.shopsystem.Database.service.in
create mode 100644 data/dbus/io.mainframe.shopsystem.InputDevice.service.in
create mode 100644 data/dbus/io.mainframe.shopsystem.InvoicePDF.service.in
create mode 100644 data/dbus/io.mainframe.shopsystem.Mail.service.in
create mode 100644 data/dbus/io.mainframe.shopsystem.PGP.service.in
create mode 100644 data/dbus/io.mainframe.shopsystem.ScannerSession.service.in
create mode 100644 data/dbus/io.mainframe.shopsystem.SerialDevice.service.in
create mode 100644 data/dbus/io.mainframe.shopsystem.StockPDF.service.in
create mode 100644 data/dbus/meson.build
create mode 100644 data/invoice/footer-line.svg
create mode 100644 data/invoice/invoice.final.html
create mode 100644 data/invoice/invoice.final.txt
create mode 100644 data/invoice/invoice.temporary.html
create mode 100644 data/invoice/invoice.temporary.txt
create mode 100644 data/invoice/logo.svg
create mode 100644 data/invoice/pdf-template.txt
create mode 100644 data/invoice/treasurer.mail.txt
create mode 100644 data/invoice/vat.html
create mode 100644 data/invoice/vat.txt
create mode 100644 data/logo.txt
create mode 100644 data/meson.build
create mode 100644 data/po/LINGUAS
create mode 100644 data/po/POTFILES
create mode 100644 data/po/de.po
create mode 100644 data/po/meson.build
create mode 100644 data/po/shopsystem.pot
create mode 100644 data/sounds/system/LICENSE
create mode 100644 data/sounds/system/error.ogg
create mode 100644 data/sounds/system/shutdown.ogg
create mode 100644 data/sounds/system/startup.ogg
create mode 100644 data/sounds/user/beep/LICENSE
create mode 100644 data/sounds/user/beep/error/buzzer.ogg
create mode 100644 data/sounds/user/beep/login/toycaralarm.ogg
create mode 100644 data/sounds/user/beep/logout/cha ching.ogg
create mode 100644 data/sounds/user/beep/purchase/beep.ogg
create mode 100644 data/sounds/user/beep/purchase/camera.ogg
create mode 100644 data/sounds/user/dude/LICENSE
create mode 100644 data/sounds/user/dude/error/I broke something.ogg
create mode 100644 data/sounds/user/dude/login/hey dude, whats up man.ogg
create mode 100644 data/sounds/user/dude/logout/bye.ogg
create mode 100644 data/sounds/user/dude/purchase/yeah man.ogg
create mode 100644 data/sounds/user/girlfriend/LICENSE
create mode 100644 data/sounds/user/girlfriend/error/no, no, forget it.ogg
create mode 100644 data/sounds/user/girlfriend/login/hello.ogg
create mode 100644 data/sounds/user/girlfriend/login/we need to talk about this.ogg
create mode 100644 data/sounds/user/girlfriend/logout/get out, get outta here.ogg
create mode 100644 data/sounds/user/girlfriend/logout/i dont have time for this.ogg
create mode 100644 data/sounds/user/girlfriend/logout/why dont you just leave then.ogg
create mode 100644 data/sounds/user/girlfriend/purchase/code accepted.ogg
create mode 100644 data/sounds/user/girlfriend/purchase/yeah so what.ogg
create mode 100644 data/sounds/user/japanese/LICENSE
create mode 100644 data/sounds/user/japanese/error/no, iie.ogg
create mode 100644 data/sounds/user/japanese/error/sumimasen.ogg
create mode 100644 data/sounds/user/japanese/login/hello.ogg
create mode 100644 data/sounds/user/japanese/logout/goodbye.ogg
create mode 100644 data/sounds/user/japanese/purchase/domo-arigato.ogg
create mode 100644 data/sounds/user/japanese/purchase/domo.ogg
create mode 100644 data/sounds/user/robot/LICENSE
create mode 100644 data/sounds/user/robot/error/critical error.ogg
create mode 100644 data/sounds/user/robot/login/login accepted.ogg
create mode 100644 data/sounds/user/robot/logout/terminating.ogg
create mode 100644 data/sounds/user/robot/purchase/accepted.ogg
create mode 100644 data/sounds/user/sexy/LICENSE
create mode 100644 data/sounds/user/sexy/error/error.ogg
create mode 100644 data/sounds/user/sexy/login/welcome back sexy bitch.ogg
create mode 100644 data/sounds/user/sexy/logout/well if thats how you feel.ogg
create mode 100644 data/sounds/user/sexy/logout/what about me.ogg
create mode 100644 data/sounds/user/sexy/purchase/do it again.ogg
create mode 100644 data/sounds/user/sexy/purchase/what is next.ogg
create mode 100644 data/sounds/user/sexy/purchase/yes, master.ogg
create mode 100644 data/sounds/user/sexy/purchase/your will is my command.ogg
create mode 100644 data/systemd/meson.build
create mode 100644 data/systemd/shopsystem-display-on-off.service.in
create mode 100644 data/systemd/shopsystem-input-device.service.in
create mode 100644 data/systemd/shopsystem-mail-backup.service.in
create mode 100644 data/systemd/shopsystem-mail-backup.timer
create mode 100644 data/systemd/shopsystem-mail-daily-invoice.service.in
create mode 100644 data/systemd/shopsystem-mail-daily-invoice.timer
create mode 100644 data/systemd/shopsystem-mail-monthly-invoice.service.in
create mode 100644 data/systemd/shopsystem-mail-monthly-invoice.timer
create mode 100644 data/systemd/shopsystem-ui.service.in
create mode 100644 data/systemd/shopsystem-web.service.in
create mode 100644 data/templates/aliases/index.html
create mode 100644 data/templates/aliases/new.html
create mode 100644 data/templates/base.html
create mode 100644 data/templates/cashbox/add.html
create mode 100644 data/templates/cashbox/detail.html
create mode 100644 data/templates/cashbox/index.html
create mode 100644 data/templates/cashbox/selection.html
create mode 100644 data/templates/css/base.css
create mode 100644 data/templates/css/bootstrap.css
create mode 100644 data/templates/errors/400.html
create mode 100644 data/templates/errors/403.html
create mode 100644 data/templates/errors/todo.html
create mode 100644 data/templates/img/glyphicons-halflings-white.png
create mode 100644 data/templates/img/glyphicons-halflings.png
create mode 100644 data/templates/index.html
create mode 100644 data/templates/js/bootstrap.js
create mode 100644 data/templates/js/code39.js
create mode 100644 data/templates/js/jquery.flot.js
create mode 100644 data/templates/js/jquery.flot.navigate.js
create mode 100644 data/templates/js/jquery.flot.pie.js
create mode 100644 data/templates/js/jquery.flot.selection.js
create mode 100644 data/templates/js/jquery.js
create mode 100644 data/templates/js/jquery.mousewheel.js
create mode 100644 data/templates/js/sorttable.js
create mode 100644 data/templates/logout.html
create mode 100644 data/templates/menu.html
create mode 100644 data/templates/menu_login.html
create mode 100644 data/templates/menu_logout.html
create mode 100644 data/templates/products/bestbefore.html
create mode 100644 data/templates/products/entry.html
create mode 100644 data/templates/products/index.html
create mode 100644 data/templates/products/new.html
create mode 100644 data/templates/products/newprice.html
create mode 100644 data/templates/products/restock.html
create mode 100644 data/templates/stats/index.html
create mode 100644 data/templates/stats/profit_per_day.html
create mode 100644 data/templates/stats/profit_per_product.html
create mode 100644 data/templates/stats/profit_per_weekday.html
create mode 100644 data/templates/stats/stock.html
create mode 100644 data/templates/users/entry.html
create mode 100644 data/templates/users/import-pgp.html
create mode 100644 data/templates/users/import.html
create mode 100644 data/templates/users/index.html
create mode 100644 data/templates/users/invoice.html
create mode 100644 data/udev/meson.build
create mode 100644 data/udev/shopsystem-input-device-rfid.rules
create mode 100644 data/udev/shopsystem-input-device.rules
delete mode 100644 dbus/.gitignore
delete mode 100644 dbus/Makefile
delete mode 100644 dbus/config/Makefile
delete mode 100644 dbus/config/io.mainframe.shopsystem.AudioPlayer.conf
delete mode 100644 dbus/config/io.mainframe.shopsystem.Config.conf
delete mode 100644 dbus/config/io.mainframe.shopsystem.Database.conf
delete mode 100644 dbus/config/io.mainframe.shopsystem.InputDevice.conf
delete mode 100644 dbus/config/io.mainframe.shopsystem.InvoicePDF.conf
delete mode 100644 dbus/config/io.mainframe.shopsystem.Mail.conf
delete mode 100644 dbus/config/io.mainframe.shopsystem.PGP.conf
delete mode 100644 dbus/config/io.mainframe.shopsystem.ScannerSession.conf
delete mode 100644 dbus/config/io.mainframe.shopsystem.SerialDevice.conf
delete mode 100644 dbus/config/io.mainframe.shopsystem.StockPDF.conf
delete mode 100644 dbus/io.mainframe.shopsystem.AudioPlayer.service.in
delete mode 100644 dbus/io.mainframe.shopsystem.Config.service.in
delete mode 100644 dbus/io.mainframe.shopsystem.Database.service.in
delete mode 100644 dbus/io.mainframe.shopsystem.InputDevice.service.in
delete mode 100644 dbus/io.mainframe.shopsystem.InvoicePDF.service.in
delete mode 100644 dbus/io.mainframe.shopsystem.Mail.service.in
delete mode 100644 dbus/io.mainframe.shopsystem.PGP.service.in
delete mode 100644 dbus/io.mainframe.shopsystem.ScannerSession.service.in
delete mode 100644 dbus/io.mainframe.shopsystem.SerialDevice.service.in
delete mode 100644 dbus/io.mainframe.shopsystem.StockPDF.service.in
delete mode 100644 invoice/footer-line.svg
delete mode 100644 invoice/invoice.final.html
delete mode 100644 invoice/invoice.final.txt
delete mode 100644 invoice/invoice.temporary.html
delete mode 100644 invoice/invoice.temporary.txt
delete mode 100644 invoice/logo.svg
delete mode 100644 invoice/pdf-template.txt
delete mode 100644 invoice/treasurer.mail.txt
delete mode 100644 invoice/vat.html
delete mode 100644 invoice/vat.txt
delete mode 100644 locale/de.po
delete mode 100644 logo.txt
create mode 100644 meson.build
delete mode 100644 sounds/system/LICENSE
delete mode 100644 sounds/system/error.ogg
delete mode 100644 sounds/system/shutdown.ogg
delete mode 100644 sounds/system/startup.ogg
delete mode 100644 sounds/user/beep/LICENSE
delete mode 100644 sounds/user/beep/error/buzzer.ogg
delete mode 100644 sounds/user/beep/login/toycaralarm.ogg
delete mode 100644 sounds/user/beep/logout/cha ching.ogg
delete mode 100644 sounds/user/beep/purchase/beep.ogg
delete mode 100644 sounds/user/beep/purchase/camera.ogg
delete mode 100644 sounds/user/dude/LICENSE
delete mode 100644 sounds/user/dude/error/I broke something.ogg
delete mode 100644 sounds/user/dude/login/hey dude, whats up man.ogg
delete mode 100644 sounds/user/dude/logout/bye.ogg
delete mode 100644 sounds/user/dude/purchase/yeah man.ogg
delete mode 100644 sounds/user/girlfriend/LICENSE
delete mode 100644 sounds/user/girlfriend/error/no, no, forget it.ogg
delete mode 100644 sounds/user/girlfriend/login/hello.ogg
delete mode 100644 sounds/user/girlfriend/login/we need to talk about this.ogg
delete mode 100644 sounds/user/girlfriend/logout/get out, get outta here.ogg
delete mode 100644 sounds/user/girlfriend/logout/i dont have time for this.ogg
delete mode 100644 sounds/user/girlfriend/logout/why dont you just leave then.ogg
delete mode 100644 sounds/user/girlfriend/purchase/code accepted.ogg
delete mode 100644 sounds/user/girlfriend/purchase/yeah so what.ogg
delete mode 100644 sounds/user/japanese/LICENSE
delete mode 100644 sounds/user/japanese/error/no, iie.ogg
delete mode 100644 sounds/user/japanese/error/sumimasen.ogg
delete mode 100644 sounds/user/japanese/login/hello.ogg
delete mode 100644 sounds/user/japanese/logout/goodbye.ogg
delete mode 100644 sounds/user/japanese/purchase/domo-arigato.ogg
delete mode 100644 sounds/user/japanese/purchase/domo.ogg
delete mode 100644 sounds/user/robot/LICENSE
delete mode 100644 sounds/user/robot/error/critical error.ogg
delete mode 100644 sounds/user/robot/login/login accepted.ogg
delete mode 100644 sounds/user/robot/logout/terminating.ogg
delete mode 100644 sounds/user/robot/purchase/accepted.ogg
delete mode 100644 sounds/user/sexy/LICENSE
delete mode 100644 sounds/user/sexy/error/error.ogg
delete mode 100644 sounds/user/sexy/login/welcome back sexy bitch.ogg
delete mode 100644 sounds/user/sexy/logout/well if thats how you feel.ogg
delete mode 100644 sounds/user/sexy/logout/what about me.ogg
delete mode 100644 sounds/user/sexy/purchase/do it again.ogg
delete mode 100644 sounds/user/sexy/purchase/what is next.ogg
delete mode 100644 sounds/user/sexy/purchase/yes, master.ogg
delete mode 100644 sounds/user/sexy/purchase/your will is my command.ogg
delete mode 100644 src/Makefile
delete mode 100644 src/audio/Makefile
delete mode 100644 src/backup/Makefile
delete mode 100644 src/cli/Makefile
delete mode 100644 src/config/Makefile
delete mode 100644 src/curses-ui/Makefile
delete mode 100644 src/database/Makefile
delete mode 100644 src/input-device/Makefile
delete mode 100644 src/invoice/Makefile
delete mode 100644 src/libcairobarcode/Makefile
delete mode 100644 src/mail/Makefile
create mode 100644 src/meson.build
delete mode 100644 src/pdf-invoice/Makefile
delete mode 100644 src/pdf-stock/Makefile
delete mode 100644 src/pgp/Makefile
delete mode 100644 src/scanner-session/Makefile
delete mode 100644 src/serial-device/Makefile
delete mode 100644 src/web/Makefile
delete mode 100644 systemd/Makefile
delete mode 100644 systemd/shopsystem-display-on-off.service
delete mode 100644 systemd/shopsystem-input-device-rfid.rules
delete mode 100644 systemd/shopsystem-input-device.rules
delete mode 100644 systemd/shopsystem-input-device.service
delete mode 100644 systemd/shopsystem-mail-backup.service
delete mode 100644 systemd/shopsystem-mail-backup.timer
delete mode 100644 systemd/shopsystem-mail-daily-invoice.service
delete mode 100644 systemd/shopsystem-mail-daily-invoice.timer
delete mode 100644 systemd/shopsystem-mail-monthly-invoice.service
delete mode 100644 systemd/shopsystem-mail-monthly-invoice.timer
delete mode 100644 systemd/shopsystem-ui.service
delete mode 100644 systemd/shopsystem-web.service
delete mode 100644 templates/aliases/index.html
delete mode 100644 templates/aliases/new.html
delete mode 100644 templates/base.html
delete mode 100644 templates/cashbox/add.html
delete mode 100644 templates/cashbox/detail.html
delete mode 100644 templates/cashbox/index.html
delete mode 100644 templates/cashbox/selection.html
delete mode 100644 templates/css/base.css
delete mode 100644 templates/css/bootstrap.css
delete mode 100644 templates/errors/400.html
delete mode 100644 templates/errors/403.html
delete mode 100644 templates/errors/todo.html
delete mode 100644 templates/img/glyphicons-halflings-white.png
delete mode 100644 templates/img/glyphicons-halflings.png
delete mode 100644 templates/index.html
delete mode 100644 templates/js/bootstrap.js
delete mode 100644 templates/js/code39.js
delete mode 100644 templates/js/jquery.flot.js
delete mode 100644 templates/js/jquery.flot.navigate.js
delete mode 100644 templates/js/jquery.flot.pie.js
delete mode 100644 templates/js/jquery.flot.selection.js
delete mode 100644 templates/js/jquery.js
delete mode 100644 templates/js/jquery.mousewheel.js
delete mode 100644 templates/js/sorttable.js
delete mode 100644 templates/logout.html
delete mode 100644 templates/menu.html
delete mode 100644 templates/menu_login.html
delete mode 100644 templates/menu_logout.html
delete mode 100644 templates/products/bestbefore.html
delete mode 100644 templates/products/entry.html
delete mode 100644 templates/products/index.html
delete mode 100644 templates/products/new.html
delete mode 100644 templates/products/newprice.html
delete mode 100644 templates/products/restock.html
delete mode 100644 templates/stats/index.html
delete mode 100644 templates/stats/profit_per_day.html
delete mode 100644 templates/stats/profit_per_product.html
delete mode 100644 templates/stats/profit_per_weekday.html
delete mode 100644 templates/stats/stock.html
delete mode 100644 templates/users/entry.html
delete mode 100644 templates/users/import-pgp.html
delete mode 100644 templates/users/import.html
delete mode 100644 templates/users/index.html
delete mode 100644 templates/users/invoice.html
diff --git a/.gitignore b/.gitignore
index 026f4b8..af5ca8c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+build/
gnupg/
*.swp
shop.db
diff --git a/LICENSE b/LICENSE
index 1f0b9a1..400c4a1 100644
--- a/LICENSE
+++ b/LICENSE
@@ -2,40 +2,40 @@ Files: *
Copyright: 2012-2013, Sebastian Reichel
License: ISC
-Files: templates/js/sorttable.js
+Files: data/templates/js/sorttable.js
Copyright: 2007, Stuart Langridge
License: Expat
-Files: templates/js/jquery.js
+Files: data/templates/js/jquery.js
Copyright: 2012, jQuery Foundation and contributors
License: Expat
-Files: templates/js/jquery.mousewheel.js
+Files: data/templates/js/jquery.mousewheel.js
Copyright: 2011, Brandon Aaron (http://brandonaaron.net)
License: Expat
-Files: templates/js/jquery.flot.js
- templates/js/jquery.flot.navigate.js
- templates/js/jquery.flot.selection.js
+Files: data/templates/js/jquery.flot.js
+ data/templates/js/jquery.flot.navigate.js
+ data/templates/js/jquery.flot.selection.js
Copyright: 2007-2012, Ole Laursen
2007-2012, IOLA
2008, Three Dub Media (http://threedubmedia.com)
License: Expat
-Files: templates/js/jquery.flot.pie.js
+Files: data/templates/js/jquery.flot.pie.js
Copyright: 2009, Brian Medendorp
2009, btburnett3
2009, Anthony Aragues
2009, Xavi Ivars
License: Expat
-Files: templates/css/bootstrap.css
- templates/js/bootstrap.js
+Files: data/templates/css/bootstrap.css
+ data/templates/js/bootstrap.js
Copyright: Bootstrap
License: Apache-2.0
-Files: templates/img/glyphicons-halflings.png
- templates/img/glyphicons-halflings-white.png
+Files: data/templates/img/glyphicons-halflings.png
+ data/templates/img/glyphicons-halflings-white.png
Copyright: Glyphicons
License: CC-BY-3.0
diff --git a/Makefile b/Makefile
index ee08cef..2b0da03 100644
--- a/Makefile
+++ b/Makefile
@@ -1,24 +1,20 @@
-all:
- @cd src && make --no-print-directory all
+all: compile gettext
-gettext: locale/de/LC_MESSAGES/shopsystem.mo
+build:
+ meson build
-locale/de/LC_MESSAGES/shopsystem.mo: locale/de.po
- install -d locale/de/LC_MESSAGES/
- msgfmt -o $@ $<
+compile: build
+ cd build && ninja
-locale/%.po: locale/messages.pot
- msgmerge -N --backup=off --update $@ $<
+gettext: build
+ cd build && ninja shopsystem-pot
+ cd build && ninja shopsystem-update-po
-locale/messages.pot: */*/*.vala
- xgettext --language=vala --from-code=utf-8 --keyword=_ --escape --sort-output -o $@ */*/*.vala
+install: build
+ cd build && DESTDIR=`pwd`/tmpinst ninja install
clean:
- @cd src && make --no-print-directory clean
-
-install:
- @cd src && make --no-print-directory install
- @cd dbus && make --no-print-directory install
+ rm -rf build
shop.db: sql/tables.sql sql/views.sql sql/trigger.sql
@for file in $^ ; do \
diff --git a/README.md b/README.md
index 8c8dd87..fa67d0b 100644
--- a/README.md
+++ b/README.md
@@ -51,17 +51,12 @@ but you need to modify a few things.
* adduser "shop" with homedir in /home/shop
* clone git repository into /home/shop/serial-barcode-scanner
-=== Build the Software ===
+=== Build and Install the Software ===
- * ./configure
- * make shop.db
- * make
-
-=== DBus Configuration ===
-
- * cd dbus
- * make
- * sudo make install
+ * meson build --prefix /usr
+ * cd build
+ * ninja
+ * ninja install
=== Systemd ===
diff --git a/configure b/configure
deleted file mode 100755
index cdadf59..0000000
--- a/configure
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/sh
-. ./configure-functions.sh
-
-NAME="serial-barcode-scanner"
-AUTHOR="Sebastian Reichel "
-VERSION="0.1"
-
-check_dependencies() {
- VALA_VERSION=`valac --api-version`
- GPGME_VERSION=`gpgme-config --version`
- ESMTP_VERSION=`libesmtp-config --version`
-
- echo "Checking software dependencies:"
- check_prg_version valac 0.16 $VALA_VERSION "force"
-
- echo "Checking library dependencies:"
- check_pkg_version ncursesw 5.9 "force"
- check_pkg_version gee-0.8 0.6 "force"
- check_pkg_version gio-2.0 2.36 "force"
- check_pkg_version gmime-3.0 3.0 "force"
- check_prg_version gpgme 1.2 $GPGME_VERSION "force"
- check_pkg_version gstreamer-1.0 1.0 "force"
- check_pkg_version libarchive 3.0 "force"
- check_prg_version libesmtp 0.1 $ESMTP_VERSION "force"
- check_pkg_version librsvg-2.0 2.36 "force"
- check_pkg_version libsoup-2.4 2.48 "force"
- check_pkg_version pangocairo 1.32 "force"
- check_pkg_version sqlite3 3.7 "force"
- check_pkg_version libssl 1.0.0 "force"
-
- echo "Checking font dependencies:"
- check_font LMSans10 "force"
- check_font LMRoman10 "force"
-}
-
-extra_options() {
- case $1 in
- *)
- DESC="not available"
- CMD="not-available"
- esac
-}
-
-BASE=`dirname $0`
-
-check_parameters $@
-check_compiler
-check_install
-check_pkgconfig
-
-check_dependencies
-
-create_config $BASE/config.mk VALAC
-create_header $BASE/config.h VERSION
-
-final_report CC PREFIX
diff --git a/configure-functions.sh b/configure-functions.sh
deleted file mode 100755
index e003e5d..0000000
--- a/configure-functions.sh
+++ /dev/null
@@ -1,351 +0,0 @@
-#!/bin/sh
-# MIT license
-# Copyright 2009-2010 Sebastian Reichel
-
-# These are default values for some configurable things
-if [ "x$CC" = "x" ] ; then
- CC=gcc
-fi;
-
-if [ "x$VALAC" = "x" ] ; then
- VALAC=valac
-fi;
-
-if [ "x$INSTALL" = "x" ] ; then
- INSTALL=install
-fi;
-
-if [ "x$PKGCONFIG" = "x" ] ; then
- PKGCONFIG=pkg-config
-fi;
-
-PREFIX=/usr/local
-INSTALL_DATA=`echo ${INSTALL} -m 644`
-INSTALL_PROGRAM=`echo ${INSTALL} -m 755 -s`
-INSTALL_DIR=`echo ${INSTALL} -d`
-
-# This awk script is derived from autotools
-awk_verscmp='
- # Use only awk features that work with 7th edition Unix awk (1978).
- # My, what an old awk you have, Mr. Solaris!
- END {
- while (length(v1) && length(v2)) {
- # Set d1 to be the next thing to compare from v1, and likewise for d2.
- # Normally this is a single character, but if v1 and v2 contain digits,
- # compare them as integers and fractions as strverscmp does.
- if (v1 ~ /^[0-9]/ && v2 ~ /^[0-9]/) {
- # Split v1 and v2 into their leading digit string components d1 and d2,
- # and advance v1 and v2 past the leading digit strings.
- for (len1 = 1; substr(v1, len1 + 1) ~ /^[0-9]/; len1++) continue
- for (len2 = 1; substr(v2, len2 + 1) ~ /^[0-9]/; len2++) continue
- d1 = substr(v1, 1, len1); v1 = substr(v1, len1 + 1)
- d2 = substr(v2, 1, len2); v2 = substr(v2, len2 + 1)
- if (d1 ~ /^0/) {
- if (d2 ~ /^0/) {
- # Compare two fractions.
- while (d1 ~ /^0/ && d2 ~ /^0/) {
- d1 = substr(d1, 2); len1--
- d2 = substr(d2, 2); len2--
- }
- if (len1 != len2 && ! (len1 && len2 && substr(d1, 1, 1) == substr(d2, 1, 1))) {
- # The two components differ in length, and the common prefix
- # contains only leading zeros. Consider the longer to be less.
- d1 = -len1
- d2 = -len2
- } else {
- # Otherwise, compare as strings.
- d1 = "x" d1
- d2 = "x" d2
- }
- } else {
- # A fraction is less than an integer.
- exit 1
- }
- } else {
- if (d2 ~ /^0/) {
- # An integer is greater than a fraction.
- exit 2
- } else {
- # Compare two integers.
- d1 += 0
- d2 += 0
- }
- }
- } else {
- # The normal case, without worrying about digits.
- d1 = substr(v1, 1, 1); v1 = substr(v1, 2)
- d2 = substr(v2, 1, 1); v2 = substr(v2, 2)
- }
- if (d1 < d2) exit 1
- if (d1 > d2) exit 2
- }
- # Beware Solaris /usr/xgp4/bin/awk (at least through Solaris 10),
- # which mishandles some comparisons of empty strings to integers.
- if (length(v2)) exit 1
- if (length(v1)) exit 2
- }
-'
-
-show_usage() {
- echo "'configure' configures $1 to adapt to many kinds of systems."
- echo "Usage: ./configure [OPTION]... [VAR=VALUE]..."
- echo
- echo "To assign environment variables (e.g. CC, CFLAGS, ...), specify them"
- echo "as VAR=VALUE. See below for descriptions of some of the useful variables."
- echo
- echo "Defaults for the options are specified in brackets."
- echo
- echo "Options:"
-
- echo " -h, --help display this help and exit"
- echo " -v, --version display version information and exit"
- echo " --prefix=PREFIX install files in PREFIX [/usr/local]"
- echo " --debug enable debug build (-g flag)"
- echo " --colored enable colored build"
-
- for parameter in `extra_options list-all` ; do
- extra_options $parameter
- printf " %-27s $DESC\n" $parameter
- done
-
- echo
- echo "Some influential environment variables:"
- echo " CC C compiler command"
- echo " VALAC Vala compiler"
- echo " CFLAGS C compiler flags, e.g. -I"
- echo " LDFLAGS linker flags, e.g. -L"
- echo " INSTALL install binary"
- echo " PKGCONFIG pkg-config binary"
- echo
- echo "Report bugs to: $2"
- exit 0
-}
-
-show_version() {
- echo "$1 version $2"
- echo "Originally written by $3"
- exit 0
-}
-
-check_parameters() {
- while : ; do
- [ -z "$1" ] && break
- parse_options $1
- shift
- done
-
- if [ "$UNKNOWN_FLAG" = "1" ] ; then
- echo
- fi
-}
-
-parse_options() {
- flag=`echo $1 | cut -d = -f 1`
- value=`echo $1 | awk 'BEGIN{FS="=";}{print $2}'`
-
- case $flag in
- "-h"|"--help")
- show_usage "$NAME" "$AUTHOR"; ;;
- "-v"|"--version")
- show_version "$NAME" "$VERSION" "$AUTHOR"; ;;
- "--debug")
- DEBUG="yes"; ;;
- "--colored")
- COLOR="yes"; ;;
- "--prefix")
- PREFIX="$value"; ;;
- "CC")
- CC="$value"; ;;
- "CFLAGS")
- CFLAGS="$value"; ;;
- "LDFLAGS")
- LDFLAGS="$value"; ;;
- "INSTALL")
- INSTALL="$value"; ;;
- "PKGCONFIG")
- PKGCONFIG="$value"; ;;
- "VALAC")
- VALAC="$value"; ;;
- *)
- extra_options $flag $value
- if [ "$CMD" != "not-available" ] ; then
- eval "$CMD"
- else
- echo "WARNING: unknown flag '$1'." >&2
- UNKNOWN_FLAG=1
- fi;;
- esac
-}
-
-check_prg_version() {
- printf " %-34s" "$1..."
-
- if [ "$4" = "disable" ] ; then
- echo "disabled"
- return 0
- fi
-
- if [ "$3" = "" ] ; then
- echo "missing"
- if [ "$4" != "force" ] ; then
- return 0
- else
- exit 1
- fi
- fi
-
- echo "$3"
-
- awk "$awk_verscmp" v1="$2" v2="$3" /dev/null
- if [ "$?" = "2" ] ; then
- echo "Please install at least $2 to use this feature"
- if [ "$4" != "force" ] ; then
- return 0
- else
- exit 1
- fi
- fi
-
- return 1
-}
-
-check_pkg_version() {
- printf " %-34s" "$1..."
-
- if [ "$3" = "disable" ] ; then
- echo "disabled"
- return 0
- fi
-
- pkg-config --exists $1
- if [ "$?" = "1" ] ; then
- echo "missing"
- if [ "$3" != "force" ] ; then
- return 0
- else
- exit 1
- fi
- fi
-
- echo `pkg-config --modversion $1`
-
- pkg-config --atleast-version "$2" "$1"
- if [ "$?" = "1" ] ; then
- echo "Please install at least $2"
- if [ "$3" != "force" ] ; then
- return 0
- else
- exit 1
- fi
- fi
-
- return 1
-}
-
-check_font() {
- name="$1"
-
- printf " %-34s" "$1..."
-
- for file in /etc/fonts/conf.avail/*; do
- grep -q "$name" "$file"
- if [ $? -eq 0 ] ; then
- echo "available"
- return 1
- fi
- done
-
- echo "MISSING"
-
- if [ "$2" != "force" ] ; then
- return 0
- else
- exit 1
- fi
-}
-
-check_compiler() {
- printf "Checking compiler... "
- echo 'main(){}' > test.c
- ${CC} ${CFLAGS} ${LDFLAGS} -o a.out test.c >/dev/null 2>&1
- if [ ! $? = 0 ]; then
- echo failed
- exit 1
- else
- echo works
- fi
- rm -f a.out test.c
-}
-
-check_install() {
- printf "Checking install... "
- if [ -x `which ${INSTALL}` ] ; then
- echo available
- else
- echo missing
- exit 1
- fi
-}
-
-check_pkgconfig() {
- printf "Checking pkg-config... "
- if [ -x `which ${PKGCONFIG}` ] ; then
- echo `${PKGCONFIG} --version`
- else
- echo missing
- exit 1
- fi
-}
-
-create_config() {
- FILE=$1
- shift
- printf "Creating %-26s" $FILE...
- rm -f $FILE && touch $FILE
-
- # Some standard variables
- echo DEBUG=$DEBUG >> $FILE
- echo COLOR=$COLOR >> $FILE
- echo CC=$CC >> $FILE
- echo CFLAGS=$CFLAGS >> $FILE
- echo LDFLAGS=$LDFLAGS >> $FILE
- echo PREFIX=$PREFIX >> $FILE
- echo PKGCONFIG=$PKGCONFIG >> $FILE
- echo INSTALL=$INSTALL >> $FILE
- echo INSTALL_DATA=$INSTALL_DATA >> $FILE
- echo INSTALL_PROGRAM=$INSTALL_PROGRAM >> $FILE
- echo INSTALL_DIR=$INSTALL_DIR >> $FILE
-
- for option in $@ ; do
- value=`echo \\$$option`
- value=`eval echo $value`
- echo $option=$value >> $FILE
- done
-
- echo "done"
-}
-
-create_header() {
- FILE=$1
- shift
- printf "Creating %-26s" $FILE...
- rm -f $FILE && touch $FILE
-
- for option in $@ ; do
- value=`echo \\$$option`
- value=`eval echo $value`
- echo "#define $option \"$value\"" >> $FILE
- done
-
- echo "done"
-}
-
-final_report() {
- echo
- echo "Final Report:"
- for option in $@ ; do
- value=`echo \\$$option`
- value=`eval echo $value`
- printf " %-10s = %s\n" $option $value
- done
-}
diff --git a/data/dbus/.gitignore b/data/dbus/.gitignore
new file mode 100644
index 0000000..0e3ad1b
--- /dev/null
+++ b/data/dbus/.gitignore
@@ -0,0 +1 @@
+*.service
diff --git a/data/dbus/config/io.mainframe.shopsystem.AudioPlayer.conf b/data/dbus/config/io.mainframe.shopsystem.AudioPlayer.conf
new file mode 100644
index 0000000..b8c11c0
--- /dev/null
+++ b/data/dbus/config/io.mainframe.shopsystem.AudioPlayer.conf
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/dbus/config/io.mainframe.shopsystem.Config.conf b/data/dbus/config/io.mainframe.shopsystem.Config.conf
new file mode 100644
index 0000000..41e5cb6
--- /dev/null
+++ b/data/dbus/config/io.mainframe.shopsystem.Config.conf
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/dbus/config/io.mainframe.shopsystem.Database.conf b/data/dbus/config/io.mainframe.shopsystem.Database.conf
new file mode 100644
index 0000000..61bde19
--- /dev/null
+++ b/data/dbus/config/io.mainframe.shopsystem.Database.conf
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/dbus/config/io.mainframe.shopsystem.InputDevice.conf b/data/dbus/config/io.mainframe.shopsystem.InputDevice.conf
new file mode 100644
index 0000000..15ad43d
--- /dev/null
+++ b/data/dbus/config/io.mainframe.shopsystem.InputDevice.conf
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/dbus/config/io.mainframe.shopsystem.InvoicePDF.conf b/data/dbus/config/io.mainframe.shopsystem.InvoicePDF.conf
new file mode 100644
index 0000000..6ec4610
--- /dev/null
+++ b/data/dbus/config/io.mainframe.shopsystem.InvoicePDF.conf
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/dbus/config/io.mainframe.shopsystem.Mail.conf b/data/dbus/config/io.mainframe.shopsystem.Mail.conf
new file mode 100644
index 0000000..e48e264
--- /dev/null
+++ b/data/dbus/config/io.mainframe.shopsystem.Mail.conf
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/dbus/config/io.mainframe.shopsystem.PGP.conf b/data/dbus/config/io.mainframe.shopsystem.PGP.conf
new file mode 100644
index 0000000..ab2f8b2
--- /dev/null
+++ b/data/dbus/config/io.mainframe.shopsystem.PGP.conf
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/dbus/config/io.mainframe.shopsystem.ScannerSession.conf b/data/dbus/config/io.mainframe.shopsystem.ScannerSession.conf
new file mode 100644
index 0000000..49c4eb1
--- /dev/null
+++ b/data/dbus/config/io.mainframe.shopsystem.ScannerSession.conf
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/dbus/config/io.mainframe.shopsystem.SerialDevice.conf b/data/dbus/config/io.mainframe.shopsystem.SerialDevice.conf
new file mode 100644
index 0000000..d849ef4
--- /dev/null
+++ b/data/dbus/config/io.mainframe.shopsystem.SerialDevice.conf
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/dbus/config/io.mainframe.shopsystem.StockPDF.conf b/data/dbus/config/io.mainframe.shopsystem.StockPDF.conf
new file mode 100644
index 0000000..8cf0fdc
--- /dev/null
+++ b/data/dbus/config/io.mainframe.shopsystem.StockPDF.conf
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/dbus/io.mainframe.shopsystem.AudioPlayer.service.in b/data/dbus/io.mainframe.shopsystem.AudioPlayer.service.in
new file mode 100644
index 0000000..6aa6a6f
--- /dev/null
+++ b/data/dbus/io.mainframe.shopsystem.AudioPlayer.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=io.mainframe.shopsystem.AudioPlayer
+Exec=@PATH@/shop-audio
+User=shop
diff --git a/data/dbus/io.mainframe.shopsystem.Config.service.in b/data/dbus/io.mainframe.shopsystem.Config.service.in
new file mode 100644
index 0000000..52e4cf7
--- /dev/null
+++ b/data/dbus/io.mainframe.shopsystem.Config.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=io.mainframe.shopsystem.Config
+Exec=@PATH@/shop-config
+User=shop
diff --git a/data/dbus/io.mainframe.shopsystem.Database.service.in b/data/dbus/io.mainframe.shopsystem.Database.service.in
new file mode 100644
index 0000000..89112dc
--- /dev/null
+++ b/data/dbus/io.mainframe.shopsystem.Database.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=io.mainframe.shopsystem.Database
+Exec=@PATH@/shop-database
+User=shop
diff --git a/data/dbus/io.mainframe.shopsystem.InputDevice.service.in b/data/dbus/io.mainframe.shopsystem.InputDevice.service.in
new file mode 100644
index 0000000..1584d8d
--- /dev/null
+++ b/data/dbus/io.mainframe.shopsystem.InputDevice.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=io.mainframe.shopsystem.InputDevice
+Exec=@PATH@/shop-input-device
+User=shop
diff --git a/data/dbus/io.mainframe.shopsystem.InvoicePDF.service.in b/data/dbus/io.mainframe.shopsystem.InvoicePDF.service.in
new file mode 100644
index 0000000..2516d4e
--- /dev/null
+++ b/data/dbus/io.mainframe.shopsystem.InvoicePDF.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=io.mainframe.shopsystem.InvoicePDF
+Exec=@PATH@/shop-pdf-invoice
+User=shop
diff --git a/data/dbus/io.mainframe.shopsystem.Mail.service.in b/data/dbus/io.mainframe.shopsystem.Mail.service.in
new file mode 100644
index 0000000..694f579
--- /dev/null
+++ b/data/dbus/io.mainframe.shopsystem.Mail.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=io.mainframe.shopsystem.Mail
+Exec=@PATH@/shop-mailer
+User=shop
diff --git a/data/dbus/io.mainframe.shopsystem.PGP.service.in b/data/dbus/io.mainframe.shopsystem.PGP.service.in
new file mode 100644
index 0000000..7c61f63
--- /dev/null
+++ b/data/dbus/io.mainframe.shopsystem.PGP.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=io.mainframe.shopsystem.PGP
+Exec=@PATH@/shop-pgp
+User=shop
diff --git a/data/dbus/io.mainframe.shopsystem.ScannerSession.service.in b/data/dbus/io.mainframe.shopsystem.ScannerSession.service.in
new file mode 100644
index 0000000..231ae21
--- /dev/null
+++ b/data/dbus/io.mainframe.shopsystem.ScannerSession.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=io.mainframe.shopsystem.ScannerSession
+Exec=@PATH@/shop-scanner-session
+User=shop
diff --git a/data/dbus/io.mainframe.shopsystem.SerialDevice.service.in b/data/dbus/io.mainframe.shopsystem.SerialDevice.service.in
new file mode 100644
index 0000000..7433606
--- /dev/null
+++ b/data/dbus/io.mainframe.shopsystem.SerialDevice.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=io.mainframe.shopsystem.SerialDevice
+Exec=@PATH@/shop-serial-device
+User=shop
diff --git a/data/dbus/io.mainframe.shopsystem.StockPDF.service.in b/data/dbus/io.mainframe.shopsystem.StockPDF.service.in
new file mode 100644
index 0000000..d1a3b5e
--- /dev/null
+++ b/data/dbus/io.mainframe.shopsystem.StockPDF.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=io.mainframe.shopsystem.StockPDF
+Exec=@PATH@/shop-pdf-stock
+User=shop
diff --git a/data/dbus/meson.build b/data/dbus/meson.build
new file mode 100644
index 0000000..e8c529e
--- /dev/null
+++ b/data/dbus/meson.build
@@ -0,0 +1,28 @@
+bindir = join_paths(get_option('prefix'), get_option('bindir'))
+dbusdir = join_paths(get_option('prefix'), get_option('datadir'), 'dbus-1', 'system-services')
+dbuscfgdir = join_paths(get_option('sysconfdir'), 'dbus-1', 'system.d')
+
+cfg = configuration_data()
+cfg.set('PATH', bindir)
+
+configure_file(input: 'io.mainframe.shopsystem.AudioPlayer.service.in', output: 'io.mainframe.shopsystem.AudioPlayer.service', configuration: cfg, install_dir: dbusdir)
+configure_file(input: 'io.mainframe.shopsystem.Config.service.in', output: 'io.mainframe.shopsystem.Config.service', configuration: cfg, install_dir: dbusdir)
+configure_file(input: 'io.mainframe.shopsystem.Database.service.in', output: 'io.mainframe.shopsystem.Database.service', configuration: cfg, install_dir: dbusdir)
+configure_file(input: 'io.mainframe.shopsystem.InputDevice.service.in', output: 'io.mainframe.shopsystem.InputDevice.service', configuration: cfg, install_dir: dbusdir)
+configure_file(input: 'io.mainframe.shopsystem.InvoicePDF.service.in', output: 'io.mainframe.shopsystem.InvoicePDF.service', configuration: cfg, install_dir: dbusdir)
+configure_file(input: 'io.mainframe.shopsystem.Mail.service.in', output: 'io.mainframe.shopsystem.Mail.service', configuration: cfg, install_dir: dbusdir)
+configure_file(input: 'io.mainframe.shopsystem.PGP.service.in', output: 'io.mainframe.shopsystem.PGP.service', configuration: cfg, install_dir: dbusdir)
+configure_file(input: 'io.mainframe.shopsystem.ScannerSession.service.in', output: 'io.mainframe.shopsystem.ScannerSession.service', configuration: cfg, install_dir: dbusdir)
+configure_file(input: 'io.mainframe.shopsystem.SerialDevice.service.in', output: 'io.mainframe.shopsystem.SerialDevice.service', configuration: cfg, install_dir: dbusdir)
+configure_file(input: 'io.mainframe.shopsystem.StockPDF.service.in', output: 'io.mainframe.shopsystem.StockPDF.service', configuration: cfg, install_dir: dbusdir)
+
+install_data('config/io.mainframe.shopsystem.AudioPlayer.conf', install_dir: dbuscfgdir)
+install_data('config/io.mainframe.shopsystem.Config.conf', install_dir: dbuscfgdir)
+install_data('config/io.mainframe.shopsystem.Database.conf', install_dir: dbuscfgdir)
+install_data('config/io.mainframe.shopsystem.InputDevice.conf', install_dir: dbuscfgdir)
+install_data('config/io.mainframe.shopsystem.InvoicePDF.conf', install_dir: dbuscfgdir)
+install_data('config/io.mainframe.shopsystem.Mail.conf', install_dir: dbuscfgdir)
+install_data('config/io.mainframe.shopsystem.PGP.conf', install_dir: dbuscfgdir)
+install_data('config/io.mainframe.shopsystem.ScannerSession.conf', install_dir: dbuscfgdir)
+install_data('config/io.mainframe.shopsystem.SerialDevice.conf', install_dir: dbuscfgdir)
+install_data('config/io.mainframe.shopsystem.StockPDF.conf', install_dir: dbuscfgdir)
diff --git a/data/invoice/footer-line.svg b/data/invoice/footer-line.svg
new file mode 100644
index 0000000..eafdffe
--- /dev/null
+++ b/data/invoice/footer-line.svg
@@ -0,0 +1,115 @@
+
+
+
+
\ No newline at end of file
diff --git a/data/invoice/invoice.final.html b/data/invoice/invoice.final.html
new file mode 100644
index 0000000..3856231
--- /dev/null
+++ b/data/invoice/invoice.final.html
@@ -0,0 +1,11 @@
+
{{{ADDRESS}}} {{{LASTNAME}}},
+
+
wir erlauben uns, Ihnen für den Verzehr von Speisen und Getränken wie folgt zu
+berechnen:
+
+{{{INVOICE_TABLE}}}
+
+{{{VAT}}}
+
+
Grüße aus dem {{{SPACENAME}}},
+das Shop-System
diff --git a/data/invoice/invoice.final.txt b/data/invoice/invoice.final.txt
new file mode 100644
index 0000000..80e60b8
--- /dev/null
+++ b/data/invoice/invoice.final.txt
@@ -0,0 +1,11 @@
+{{{ADDRESS}}} {{{LASTNAME}}},
+
+wir erlauben uns, Ihnen für den Verzehr von Speisen und Getränken
+wie folgt zu berechnen:
+
+{{{INVOICE_TABLE}}}
+
+{{{VAT}}}
+
+Grüße aus dem {{{SPACENAME}}},
+das Shop-System
diff --git a/data/invoice/invoice.temporary.html b/data/invoice/invoice.temporary.html
new file mode 100644
index 0000000..26dc216
--- /dev/null
+++ b/data/invoice/invoice.temporary.html
@@ -0,0 +1,17 @@
+
{{{ADDRESS}}} {{{LASTNAME}}},
+
+
wir erlauben uns, Ihnen für den Verzehr von Speisen und Getränken wie folgt zu
+berechnen:
+
+{{{INVOICE_TABLE}}}
+
+{{{VAT}}}
+
+
Bei dieser Abrechnung handelt es sich lediglich um einen Zwischenstand. Die
+Hauptrechnung wird einmal monatlich getrennt zugestellt und der Gesamtbetrag
+wird dann vom angegebenen Bankkonto eingezogen.
+
+
Der Gesamtbetrag für den aktuellen Monat beträgt bisher:
{{{SUM_MONTH}}} €
+
+
Grüße aus dem {{{SPACENAME}}},
+das Shop-System
diff --git a/data/invoice/invoice.temporary.txt b/data/invoice/invoice.temporary.txt
new file mode 100644
index 0000000..bf21c51
--- /dev/null
+++ b/data/invoice/invoice.temporary.txt
@@ -0,0 +1,17 @@
+{{{ADDRESS}}} {{{LASTNAME}}},
+
+wir erlauben uns, Ihnen für den Verzehr von Speisen und Getränken
+wie folgt zu berechnen:
+
+{{{INVOICE_TABLE}}}
+
+{{{VAT}}}
+
+Bei dieser Abrechnung handelt es sich lediglich um einen Zwischenstand.
+Die Hauptrechnung wird einmal monatlich getrennt zugestellt und der
+Gesamtbetrag wird dann vom angegebenen Bankkonto eingezogen.
+
+Der Gesamtbetrag für den aktuellen Monat beträgt bisher: {{{SUM_MONTH}}} €
+
+Grüße aus dem {{{SPACENAME}}},
+das Shop-System
diff --git a/data/invoice/logo.svg b/data/invoice/logo.svg
new file mode 100644
index 0000000..81e97a9
--- /dev/null
+++ b/data/invoice/logo.svg
@@ -0,0 +1,198 @@
+
+
+
+
\ No newline at end of file
diff --git a/data/invoice/pdf-template.txt b/data/invoice/pdf-template.txt
new file mode 100644
index 0000000..9da8a9a
--- /dev/null
+++ b/data/invoice/pdf-template.txt
@@ -0,0 +1,12 @@
+{{{ADDRESS}}} {{{LASTNAME}}},
+
+wir erlauben uns, Ihnen für den Verzehr von Speisen und Getränken {{{SUM}}}€ in Rechnung zu stellen.
+
+Eine detaillierte Auflistung der einzelnen Posten befindet sich, mit genauer Zeitangabe des Einkaufs, auf den folgenden Seiten.
+
+{{{VAT}}}
+
+Der Gesamtbetrag wird in 10 Tagen von dem angegebenen Bankkonto eingezogen.
+Mit freundlichen Grüßen
+
+{{{ORGANIZATION}}}
diff --git a/data/invoice/treasurer.mail.txt b/data/invoice/treasurer.mail.txt
new file mode 100644
index 0000000..6c9c6ac
--- /dev/null
+++ b/data/invoice/treasurer.mail.txt
@@ -0,0 +1,8 @@
+Hi Treasurer,
+
+This mail has the members' invoices for the current month
+and an additional csv-file in the attachment. The members'
+invoices are for your files and the csv-file can be used
+for automatic money collection.
+
+-- {{{SHORTNAME}}} Shopsystem
diff --git a/data/invoice/vat.html b/data/invoice/vat.html
new file mode 100644
index 0000000..245ec27
--- /dev/null
+++ b/data/invoice/vat.html
@@ -0,0 +1,4 @@
+
Umsatzsteuer wird nicht erhoben, da der Verein Kreativität trifft Technik e.V.
+als Kleinunternehmen unter die Regelung des
+§ 19 Abs. 1 UStG
+fällt.
diff --git a/data/invoice/vat.txt b/data/invoice/vat.txt
new file mode 100644
index 0000000..d1ee957
--- /dev/null
+++ b/data/invoice/vat.txt
@@ -0,0 +1,3 @@
+Umsatzsteuer wird nicht erhoben, da der Verein Kreativität trifft
+Technik e.V. als Kleinunternehmen unter die Regelung des § 19
+Abs. 1 UStG fällt.
diff --git a/data/logo.txt b/data/logo.txt
new file mode 100644
index 0000000..ee57aa1
--- /dev/null
+++ b/data/logo.txt
@@ -0,0 +1,6 @@
+ _ ___ _____ ____ _
+ | |/ / ||_ _| / ___|| |__ ___ _ __
+ | ' /| __|| | \___ \| '_ \ / _ \| '_ \
+ | . \| |_ | | ___) | | | | (_) | |_) )
+ |_|\_\\__||_| |____/|_| |_|\___/| .__/
+ |_|
diff --git a/data/meson.build b/data/meson.build
new file mode 100644
index 0000000..a1441b4
--- /dev/null
+++ b/data/meson.build
@@ -0,0 +1,10 @@
+datadir = join_paths(get_option('prefix'), get_option('datadir'), 'shopsystem')
+
+install_data('logo.txt', install_dir: datadir)
+install_subdir('sounds', install_dir: datadir)
+install_subdir('templates', install_dir: datadir)
+install_subdir('invoice', install_dir: datadir)
+subdir('dbus')
+subdir('po')
+subdir('systemd')
+subdir('udev')
diff --git a/data/po/LINGUAS b/data/po/LINGUAS
new file mode 100644
index 0000000..7673daa
--- /dev/null
+++ b/data/po/LINGUAS
@@ -0,0 +1 @@
+de
diff --git a/data/po/POTFILES b/data/po/POTFILES
new file mode 100644
index 0000000..33e1f6a
--- /dev/null
+++ b/data/po/POTFILES
@@ -0,0 +1,47 @@
+audio/audio.vala
+audio/main.vala
+backup/main.vala
+cli/cli.vala
+cli/main.vala
+config/config.vala
+config/main.vala
+curses-ui/clock.vala
+curses-ui/curses-ui.vala
+curses-ui/dialog.vala
+curses-ui/logo.vala
+curses-ui/main.vala
+curses-ui/message_box.vala
+curses-ui/message_box_overlay.vala
+curses-ui/numbers.vala
+curses-ui/status.vala
+database/database.vala
+database/main.vala
+graph-data.vala
+input-device/input-device.vala
+input-device/main.vala
+invoice/invoice.vala
+invoice/main.vala
+invoice/single.vala
+libcairobarcode/code39.vala
+libcairobarcode/ean.vala
+libcairobarcode/error.vala
+mail/mail.vala
+mail/mailer.vala
+mail/main.vala
+pdf-invoice/main.vala
+pdf-invoice/pdf-invoice.vala
+pdf-invoice/test.vala
+pdf-stock/main.vala
+pdf-stock/pdf-stock.vala
+pdf-stock/test.vala
+pgp/main.vala
+pgp/pgp.vala
+scanner-session/main.vala
+scanner-session/scannersession.vala
+serial-device/main.vala
+serial-device/serial-device.vala
+web/csv.vala
+web/main.vala
+web/template.vala
+web/web.vala
+web/websession.vala
diff --git a/data/po/de.po b/data/po/de.po
new file mode 100644
index 0000000..f70e4ce
--- /dev/null
+++ b/data/po/de.po
@@ -0,0 +1,477 @@
+# Copyright (C) 2018 Sebastian Reichel
+# This file is distributed under the same license as the main package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: shopsystem 0.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-06-29 01:20+0200\n"
+"PO-Revision-Date: 2018-06-28 21:14+0200\n"
+"Last-Translator: Sebastian Reichel \n"
+"Language-Team: German\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: curses-ui/message_box.vala:52
+msgid ""
+"\n"
+"Date Changed: "
+msgstr ""
+"\n"
+"Datumswechsel: "
+
+#: scanner-session/scannersession.vala:289
+#, c-format
+msgid "%s bought %d items for %s €"
+msgstr "%s hat gerade %d Posten für %s € gekauft"
+
+#: web/template.vala:53 web/template.vala:55 web/template.vala:57
+#: web/template.vala:59 web/template.vala:82
+#, c-format
+msgid "%s could not be loaded!"
+msgstr "%s konnte nicht geladen werden!"
+
+#: web/template.vala:40 web/template.vala:43 web/template.vala:46
+#: web/template.vala:49 web/template.vala:78
+#, c-format
+msgid "%s not found!"
+msgstr "%s wurde nicht gefunden!"
+
+#: pdf-invoice/pdf-invoice.vala:591
+#, c-format
+msgid "Article name \"%s\" does not fit on a single page!"
+msgstr "Artikelname \"%s\" passt nicht auf eine Seite"
+
+#: serial-device/serial-device.vala:39
+msgid "Can't read lock file!\n"
+msgstr "Konnte Lock-Datei nicht lesen\n"
+
+#: audio/audio.vala:38
+msgid "Cannot find alsa GStreamer plugin"
+msgstr "GStreamer Plugin für ALSA konnte nicht gefunden werden"
+
+#: audio/audio.vala:42
+msgid "Cannot find playbin2 GStreamer plugin"
+msgstr "GStreamer Plugin playbin2 konnte nicht gefunden werden"
+
+#: audio/main.vala:39 database/main.vala:29 input-device/main.vala:31
+#: pdf-invoice/main.vala:30 pgp/main.vala:35 serial-device/main.vala:29
+#, c-format
+msgid "Config Error: %s\n"
+msgstr "Konfigurationsfehler: %s\n"
+
+#: serial-device/serial-device.vala:211
+#, c-format
+msgid "Convert Error: %s"
+msgstr "Konvertierungsfehler"
+
+#: audio/main.vala:30 cli/main.vala:37 config/main.vala:32
+#: database/main.vala:40 input-device/main.vala:42 pdf-invoice/main.vala:39
+#: pdf-stock/main.vala:26 pgp/main.vala:44 scanner-session/main.vala:28
+#: serial-device/main.vala:40
+msgid "Could not acquire name\n"
+msgstr "Konnte DBus Namen nicht erlangen\n"
+
+#: input-device/input-device.vala:40 serial-device/serial-device.vala:165
+msgid "Could not bind IOChannel"
+msgstr "IOChannel Verbindung konnte nicht hergestellt werden"
+
+#: serial-device/serial-device.vala:59
+#, c-format
+msgid "Could not create lock file: %s!\n"
+msgstr "Konnte Lock-Datei nicht erstellen: %s!\n"
+
+#: pdf-invoice/pdf-invoice.vala:81
+#, c-format
+msgid "Could not load SVG: %s\n"
+msgstr "Konnte SVG nicht laden: %s\n"
+
+#: config/config.vala:26
+#, c-format
+msgid "Could not load configuration file: %s"
+msgstr "Konnte Konfigurationsdatei nicht laden: %s"
+
+#: invoice/invoice.vala:338 pdf-invoice/pdf-invoice.vala:379
+#, c-format
+msgid "Could not open VAT template: %s"
+msgstr "Konnte VAT-Template nicht öffnen: %s"
+
+#: serial-device/serial-device.vala:55
+msgid "Could not open device!\n"
+msgstr "Konnte Gerätedatei nicht öffnen!\n"
+
+#: invoice/invoice.vala:252 invoice/invoice.vala:319
+#, c-format
+msgid "Could not open invoice template: %s"
+msgstr "Konnte Rechnungs-Template nicht öffnen: %s"
+
+#: audio/main.vala:53 cli/main.vala:68 config/main.vala:43
+#: database/main.vala:51 input-device/main.vala:53 input-device/main.vala:58
+#: pdf-stock/main.vala:37 pgp/main.vala:55 scanner-session/main.vala:41
+#: serial-device/main.vala:51
+msgid "Could not register service\n"
+msgstr "Konnte DBus-Service nicht registrieren\n"
+
+#: pdf-invoice/main.vala:50
+#, c-format
+msgid "Could not register service: %s\n"
+msgstr "Konnte DBus-Servicenicht registieren: %s\n"
+
+#: web/web.vala:1482
+msgid "Could not setup webserver!"
+msgstr "Konnte Webserver nicht konfigurieren!"
+
+#: web/main.vala:64
+#, c-format
+msgid "Could not start Webserver: %s\n"
+msgstr "Konnte Webserver nicht starten: %s\n"
+
+#: pdf-stock/test.vala:26 scanner-session/scannersession.vala:300
+#, c-format
+msgid "DBus Error: %s"
+msgstr "DBus Fehler: %s"
+
+#: audio/main.vala:41 database/main.vala:31 input-device/main.vala:33
+#: pdf-invoice/main.vala:26 pgp/main.vala:31 serial-device/main.vala:31
+#: web/main.vala:51
+#, c-format
+msgid "DBus Error: %s\n"
+msgstr "DBus Fehler: %s\n"
+
+#: scanner-session/scannersession.vala:304
+#, c-format
+msgid "Database Error: %s"
+msgstr "Datenbank Fehler: %s"
+
+#: scanner-session/scannersession.vala:66
+#, c-format
+msgid "Error (user=%d): %s"
+msgstr "Fehler (Nutzer %d): %s"
+
+#: cli/main.vala:51
+#, c-format
+msgid "Error sending command: %s"
+msgstr "Fehler beim senden des Kommandos: %s"
+
+#: pgp/pgp.vala:123
+msgid "Error!\n"
+msgstr "Fehler!\n"
+
+#: curses-ui/logo.vala:44 scanner-session/scannersession.vala:178
+#: scanner-session/scannersession.vala:225
+#, c-format
+msgid "Error: %s"
+msgstr "Fehler: %s"
+
+#: backup/main.vala:41 invoice/main.vala:51 invoice/main.vala:58
+#: invoice/single.vala:51 invoice/single.vala:58 mail/main.vala:26
+#: web/web.vala:1143
+#, c-format
+msgid "Error: %s\n"
+msgstr "Fehler: %s\n"
+
+#: mail/main.vala:35
+msgid "Error: Could not acquire name\n"
+msgstr "Fehler: Konnte DBus-Namen nicht bekommen\n"
+
+#: mail/main.vala:47
+msgid "Error: Could not register service\n"
+msgstr "Fehler: Konnte DBus-Service nicht registrieren\n"
+
+#: database/database.vala:81
+msgid "Error: could not open database!"
+msgstr "Fehler: Konnte Datenbank nicht öffnen!"
+
+#: database/database.vala:28
+#, c-format
+msgid "Error: could not prepare statement: %s"
+msgstr "Fehler: Konnte SQL-Anweisung nicht erstellen: %s"
+
+#: scanner-session/scannersession.vala:174
+#: scanner-session/scannersession.vala:221
+#, c-format
+msgid "Error: unknown product: %llu"
+msgstr "Fehler: unbekanntes Produkt: %llu"
+
+#: serial-device/serial-device.vala:163
+msgid "Failed to set encoding"
+msgstr "Fehler beim setzten der Zeichenkodierung"
+
+#: curses-ui/logo.vala:31
+#, c-format
+msgid "File '%s' doesn't exist.\n"
+msgstr "Datei '%s' existiert nicht.\n"
+
+#: input-device/input-device.vala:43 pdf-stock/test.vala:24
+#, c-format
+msgid "File Error: %s"
+msgstr "Datei Fehler: %s"
+
+#: pdf-invoice/pdf-invoice.vala:387
+#, c-format
+msgid "File Error: %s\n"
+msgstr "Datei Fehler: %s\n"
+
+#: scanner-session/scannersession.vala:20
+msgid "Guest"
+msgstr "Gast"
+
+#: pdf-stock/test.vala:22 scanner-session/scannersession.vala:302
+#, c-format
+msgid "IO Error: %s"
+msgstr "EA Fehler: %s"
+
+#: audio/main.vala:37 curses-ui/main.vala:60 database/main.vala:27
+#: input-device/main.vala:29 pdf-invoice/main.vala:28 pgp/main.vala:33
+#: scanner-session/scannersession.vala:49 serial-device/main.vala:27
+#: web/main.vala:47
+#, c-format
+msgid "IO Error: %s\n"
+msgstr "EA Fehler: %s\n"
+
+#: serial-device/serial-device.vala:207
+#, c-format
+msgid "IOChannel Error: %s"
+msgstr "IOChannel Fehler: %s"
+
+#: serial-device/serial-device.vala:168
+#, c-format
+msgid "IOChannel: %s"
+msgstr "IOChannel: %s"
+
+#: input-device/input-device.vala:27
+msgid "Ignoring InputDevice!\n"
+msgstr "Eingabegerät wird ignoriert\n"
+
+#: scanner-session/scannersession.vala:168
+#: scanner-session/scannersession.vala:215
+msgid "Internal Error!"
+msgstr "Interner Fehler!"
+
+#: web/main.vala:44 web/main.vala:49
+#, c-format
+msgid "KeyFile Error: %s\n"
+msgstr "Schlüsseldatei Fehler: %s\n"
+
+#: scanner-session/scannersession.vala:149
+msgid "Login as GUEST"
+msgstr "Login als Gast"
+
+#: scanner-session/scannersession.vala:141
+#, c-format
+msgid "Login failed (User ID = %d)"
+msgstr "Login fehlgeschlagen (User ID = %d)"
+
+#: scanner-session/scannersession.vala:155
+msgid "Login failed as GUEST"
+msgstr "Gast-Login fehlgeschlagen"
+
+#: web/websession.vala:165
+#, c-format
+msgid "Login for user id %d failed\n"
+msgstr "Login für Benutzer %d fehlgeschlagen\n"
+
+#: scanner-session/scannersession.vala:135
+#, c-format
+msgid "Login: %s (%d)"
+msgstr "Login: %s (%d)"
+
+#: input-device/input-device.vala:215 serial-device/serial-device.vala:189
+msgid "Lost device"
+msgstr "Gerät verloren"
+
+#: pdf-invoice/pdf-invoice.vala:628
+msgid "No invoice ID given!"
+msgstr "Keine Rechnungsnummer übergeben!"
+
+#: pdf-invoice/pdf-invoice.vala:631
+msgid "No invoice data given!"
+msgstr "Keine Rechnungsdaten übergeben!"
+
+#: pdf-invoice/pdf-invoice.vala:634
+msgid "No invoice date given!"
+msgstr "Kein Rechnungsdatum übergeben!"
+
+#: pdf-invoice/pdf-invoice.vala:637
+msgid "No invoice recipient given!"
+msgstr "Kein Rechnungsempfänger übergeben!"
+
+#: scanner-session/scannersession.vala:253
+msgid "No more Items on your Shopping Cart"
+msgstr "Keine weiteren Gegenstände im Einkaufswagen"
+
+#: mail/mailer.vala:141 mail/mailer.vala:149
+msgid "No such mail"
+msgstr "Mail wurde nicht gefunden"
+
+#: database/database.vala:611
+msgid "No such session available in database!"
+msgstr "Keine solche Session in der Datenbank verfügbar!"
+
+#: database/database.vala:710 database/database.vala:722
+msgid "No such user available in database!"
+msgstr "Kein solcher Nutzer in der Datenbank verfügbar!"
+
+#: cli/main.vala:25
+#, c-format
+msgid ""
+"Nothing to send.\n"
+"Usage: %s \n"
+"Example: %s \"USER 1\" \"LOGOUT\"\n"
+msgstr ""
+"Nichts zu senden.\n"
+"Benutzung: %s \n"
+"Beispiel: %s \"USER 1\" \"LOGOUT\"\n"
+
+#: invoice/main.vala:20
+msgid "Possible values for : temporary, final\n"
+msgstr "Mögliche Werte für : temporary, final\n"
+
+#: pdf-invoice/pdf-invoice.vala:471
+msgid "Prices > 9999.99€ are not supported!"
+msgstr "Preise > 9999,99€ werden nicht unterstützt!"
+
+#: database/database.vala:487
+#, c-format
+msgid "Remove purchase of %s"
+msgstr "Einkaufsposten %s wurde entfernt"
+
+#: mail/mailer.vala:180
+#, c-format
+msgid "Reply from SMTP-Server: %s"
+msgstr "Antwort vom SMTP-Server: %s"
+
+#: cli/cli.vala:24
+#, c-format
+msgid "Sending: %s\n"
+msgstr "Sende: %s\n"
+
+#: curses-ui/main.vala:72
+msgid "Shop System has been started"
+msgstr "Shop System wurde gestartet"
+
+#: curses-ui/main.vala:78
+msgid "Stopping Shop System"
+msgstr "Shop System wird gestoppt"
+
+#: web/main.vala:55
+#, c-format
+msgid "TLS certificate: %s\n"
+msgstr "TLS Zertifikat: %s\n"
+
+#: web/main.vala:56
+#, c-format
+msgid "TLS private key: %s\n"
+msgstr "TLS Privater Schlüssel: %s\n"
+
+#: invoice/invoice.vala:314
+msgid "Unknown MessageType"
+msgstr "Unbekannter Nachrichten Typ"
+
+#: invoice/main.vala:19
+#, c-format
+msgid "Usage: %s [timestamp]\n"
+msgstr "Benutzung: %s [timestamp]\n"
+
+#: web/main.vala:54
+#, c-format
+msgid "Web Server Port: %u\n"
+msgstr "Webserver Port: %u\n"
+
+#: pdf-invoice/pdf-invoice.vala:475
+msgid "Years after 9999 are not supported!"
+msgstr "Jahre nach 9999 werden nicht unterstützt!"
+
+#: scanner-session/scannersession.vala:240
+#, c-format
+msgid "article added to shopping card: %s (%s €)"
+msgstr "Artikel %s (%s €) wurde zum Warenkorb hinzugefügt"
+
+#: scanner-session/scannersession.vala:189
+#, c-format
+msgid "article info: %s (Member: %s €, Guest: %s €"
+msgstr "Artikelinfo: %s (Mitglied: %s €, Gast: %s €"
+
+#: input-device/input-device.vala:249
+#, c-format
+msgid "barcode: %s\n"
+msgstr "Barcode: %s\n"
+
+#: mail/mailer.vala:107
+msgid "could not configure STARTTLS"
+msgstr "Konnte STARTTLS nicht konfigurieren"
+
+#: web/template.vala:61 web/template.vala:84
+msgid "could not load templates!"
+msgstr "Konnte Templates nicht laden!"
+
+#: curses-ui/main.vala:27 curses-ui/main.vala:29
+#, c-format
+msgid "could not play audio: %s"
+msgstr "Konnte Audio Datei nicht abspielen: %s"
+
+#: mail/mailer.vala:99
+msgid "could not setup server"
+msgstr "Konnte Server nicht konfigurieren"
+
+#: mail/mailer.vala:176
+msgid "eSMTP: Start Session failed!"
+msgstr "eSMTP: Session Start fehlgeschlagen!"
+
+#: database/database.vala:370 database/database.vala:387
+#: database/database.vala:403 database/database.vala:419
+#: database/database.vala:435 database/database.vala:448
+#: database/database.vala:470 database/database.vala:492
+#: database/database.vala:500 database/database.vala:527
+#: database/database.vala:541 database/database.vala:556
+#: database/database.vala:583 database/database.vala:591
+#: database/database.vala:601 database/database.vala:638
+#: database/database.vala:675 database/database.vala:689
+#: database/database.vala:700 database/database.vala:737
+#: database/database.vala:765 database/database.vala:884
+#: database/database.vala:892 database/database.vala:913
+#: database/database.vala:919 database/database.vala:927
+#: database/database.vala:1028 database/database.vala:1079
+#: database/database.vala:1129 database/database.vala:1212
+#, c-format
+msgid "internal error: %d"
+msgstr "Interner Fehler: %d"
+
+#: scanner-session/scannersession.vala:249
+#, c-format
+msgid "removed last Item from Shopping Cart: %s"
+msgstr "Letzter Posten wurde aus dem Warenkorb entfernt: %s"
+
+#: serial-device/serial-device.vala:43
+msgid "serial device is locked!\n"
+msgstr "Serielles Gerät ist blockiert!\n"
+
+#: mail/mailer.vala:79
+msgid "server or port configuration is missing"
+msgstr "Server oder Port Konfiguration fehlt"
+
+#: input-device/input-device.vala:224
+msgid "short read!\n"
+msgstr "Unvollständige Daten aus Gerätedatei gelesen\n"
+
+#: database/database.vala:490
+msgid "undo not possible without purchases"
+msgstr "Rückgängig nicht möglich ohne Einkäufe"
+
+#: database/database.vala:385 database/database.vala:401
+#: database/database.vala:417 database/database.vala:433
+#: database/database.vala:468
+#, c-format
+msgid "unknown product: %llu"
+msgstr "Unbekanntes Produkt: %llu"
+
+#: database/database.vala:1210
+#, c-format
+msgid "unknown rfid: %s"
+msgstr "Unbekannter RFID: %s"
+
+#: database/database.vala:636
+msgid "user not found"
+msgstr "User nicht gefunden"
diff --git a/data/po/meson.build b/data/po/meson.build
new file mode 100644
index 0000000..387ab1d
--- /dev/null
+++ b/data/po/meson.build
@@ -0,0 +1,2 @@
+i18n = import('i18n')
+i18n.gettext(meson.project_name(), args: ['--directory='+join_paths(meson.source_root(), 'src'), '--language=vala', '--from-code=utf-8', '--keyword=_', '--escape', '--sort-output'])
diff --git a/data/po/shopsystem.pot b/data/po/shopsystem.pot
new file mode 100644
index 0000000..abab319
--- /dev/null
+++ b/data/po/shopsystem.pot
@@ -0,0 +1,475 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the shopsystem package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: shopsystem\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-06-29 01:20+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: curses-ui/message_box.vala:52
+msgid ""
+"\n"
+"Date Changed: "
+msgstr ""
+
+#: scanner-session/scannersession.vala:289
+#, c-format
+msgid "%s bought %d items for %s \342\202\254"
+msgstr ""
+
+#: web/template.vala:53 web/template.vala:55 web/template.vala:57
+#: web/template.vala:59 web/template.vala:82
+#, c-format
+msgid "%s could not be loaded!"
+msgstr ""
+
+#: web/template.vala:40 web/template.vala:43 web/template.vala:46
+#: web/template.vala:49 web/template.vala:78
+#, c-format
+msgid "%s not found!"
+msgstr ""
+
+#: pdf-invoice/pdf-invoice.vala:591
+#, c-format
+msgid "Article name \"%s\" does not fit on a single page!"
+msgstr ""
+
+#: serial-device/serial-device.vala:39
+msgid "Can't read lock file!\n"
+msgstr ""
+
+#: audio/audio.vala:38
+msgid "Cannot find alsa GStreamer plugin"
+msgstr ""
+
+#: audio/audio.vala:42
+msgid "Cannot find playbin2 GStreamer plugin"
+msgstr ""
+
+#: audio/main.vala:39 database/main.vala:29 input-device/main.vala:31
+#: pdf-invoice/main.vala:30 pgp/main.vala:35 serial-device/main.vala:29
+#, c-format
+msgid "Config Error: %s\n"
+msgstr ""
+
+#: serial-device/serial-device.vala:211
+#, c-format
+msgid "Convert Error: %s"
+msgstr ""
+
+#: audio/main.vala:30 cli/main.vala:37 config/main.vala:32
+#: database/main.vala:40 input-device/main.vala:42 pdf-invoice/main.vala:39
+#: pdf-stock/main.vala:26 pgp/main.vala:44 scanner-session/main.vala:28
+#: serial-device/main.vala:40
+msgid "Could not acquire name\n"
+msgstr ""
+
+#: input-device/input-device.vala:40 serial-device/serial-device.vala:165
+msgid "Could not bind IOChannel"
+msgstr ""
+
+#: serial-device/serial-device.vala:59
+#, c-format
+msgid "Could not create lock file: %s!\n"
+msgstr ""
+
+#: pdf-invoice/pdf-invoice.vala:81
+#, c-format
+msgid "Could not load SVG: %s\n"
+msgstr ""
+
+#: config/config.vala:26
+#, c-format
+msgid "Could not load configuration file: %s"
+msgstr ""
+
+#: invoice/invoice.vala:338 pdf-invoice/pdf-invoice.vala:379
+#, c-format
+msgid "Could not open VAT template: %s"
+msgstr ""
+
+#: serial-device/serial-device.vala:55
+msgid "Could not open device!\n"
+msgstr ""
+
+#: invoice/invoice.vala:252 invoice/invoice.vala:319
+#, c-format
+msgid "Could not open invoice template: %s"
+msgstr ""
+
+#: audio/main.vala:53 cli/main.vala:68 config/main.vala:43
+#: database/main.vala:51 input-device/main.vala:53 input-device/main.vala:58
+#: pdf-stock/main.vala:37 pgp/main.vala:55 scanner-session/main.vala:41
+#: serial-device/main.vala:51
+msgid "Could not register service\n"
+msgstr ""
+
+#: pdf-invoice/main.vala:50
+#, c-format
+msgid "Could not register service: %s\n"
+msgstr ""
+
+#: web/web.vala:1482
+msgid "Could not setup webserver!"
+msgstr ""
+
+#: web/main.vala:64
+#, c-format
+msgid "Could not start Webserver: %s\n"
+msgstr ""
+
+#: pdf-stock/test.vala:26 scanner-session/scannersession.vala:300
+#, c-format
+msgid "DBus Error: %s"
+msgstr ""
+
+#: audio/main.vala:41 database/main.vala:31 input-device/main.vala:33
+#: pdf-invoice/main.vala:26 pgp/main.vala:31 serial-device/main.vala:31
+#: web/main.vala:51
+#, c-format
+msgid "DBus Error: %s\n"
+msgstr ""
+
+#: scanner-session/scannersession.vala:304
+#, c-format
+msgid "Database Error: %s"
+msgstr ""
+
+#: scanner-session/scannersession.vala:66
+#, c-format
+msgid "Error (user=%d): %s"
+msgstr ""
+
+#: cli/main.vala:51
+#, c-format
+msgid "Error sending command: %s"
+msgstr ""
+
+#: pgp/pgp.vala:123
+msgid "Error!\n"
+msgstr ""
+
+#: curses-ui/logo.vala:44 scanner-session/scannersession.vala:178
+#: scanner-session/scannersession.vala:225
+#, c-format
+msgid "Error: %s"
+msgstr ""
+
+#: backup/main.vala:41 invoice/main.vala:51 invoice/main.vala:58
+#: invoice/single.vala:51 invoice/single.vala:58 mail/main.vala:26
+#: web/web.vala:1143
+#, c-format
+msgid "Error: %s\n"
+msgstr ""
+
+#: mail/main.vala:35
+msgid "Error: Could not acquire name\n"
+msgstr ""
+
+#: mail/main.vala:47
+msgid "Error: Could not register service\n"
+msgstr ""
+
+#: database/database.vala:81
+msgid "Error: could not open database!"
+msgstr ""
+
+#: database/database.vala:28
+#, c-format
+msgid "Error: could not prepare statement: %s"
+msgstr ""
+
+#: scanner-session/scannersession.vala:174
+#: scanner-session/scannersession.vala:221
+#, c-format
+msgid "Error: unknown product: %llu"
+msgstr ""
+
+#: serial-device/serial-device.vala:163
+msgid "Failed to set encoding"
+msgstr ""
+
+#: curses-ui/logo.vala:31
+#, c-format
+msgid "File '%s' doesn't exist.\n"
+msgstr ""
+
+#: input-device/input-device.vala:43 pdf-stock/test.vala:24
+#, c-format
+msgid "File Error: %s"
+msgstr ""
+
+#: pdf-invoice/pdf-invoice.vala:387
+#, c-format
+msgid "File Error: %s\n"
+msgstr ""
+
+#: scanner-session/scannersession.vala:20
+msgid "Guest"
+msgstr ""
+
+#: pdf-stock/test.vala:22 scanner-session/scannersession.vala:302
+#, c-format
+msgid "IO Error: %s"
+msgstr ""
+
+#: audio/main.vala:37 curses-ui/main.vala:60 database/main.vala:27
+#: input-device/main.vala:29 pdf-invoice/main.vala:28 pgp/main.vala:33
+#: scanner-session/scannersession.vala:49 serial-device/main.vala:27
+#: web/main.vala:47
+#, c-format
+msgid "IO Error: %s\n"
+msgstr ""
+
+#: serial-device/serial-device.vala:207
+#, c-format
+msgid "IOChannel Error: %s"
+msgstr ""
+
+#: serial-device/serial-device.vala:168
+#, c-format
+msgid "IOChannel: %s"
+msgstr ""
+
+#: input-device/input-device.vala:27
+msgid "Ignoring InputDevice!\n"
+msgstr ""
+
+#: scanner-session/scannersession.vala:168
+#: scanner-session/scannersession.vala:215
+msgid "Internal Error!"
+msgstr ""
+
+#: web/main.vala:44 web/main.vala:49
+#, c-format
+msgid "KeyFile Error: %s\n"
+msgstr ""
+
+#: scanner-session/scannersession.vala:149
+msgid "Login as GUEST"
+msgstr ""
+
+#: scanner-session/scannersession.vala:141
+#, c-format
+msgid "Login failed (User ID = %d)"
+msgstr ""
+
+#: scanner-session/scannersession.vala:155
+msgid "Login failed as GUEST"
+msgstr ""
+
+#: web/websession.vala:165
+#, c-format
+msgid "Login for user id %d failed\n"
+msgstr ""
+
+#: scanner-session/scannersession.vala:135
+#, c-format
+msgid "Login: %s (%d)"
+msgstr ""
+
+#: input-device/input-device.vala:215 serial-device/serial-device.vala:189
+msgid "Lost device"
+msgstr ""
+
+#: pdf-invoice/pdf-invoice.vala:628
+msgid "No invoice ID given!"
+msgstr ""
+
+#: pdf-invoice/pdf-invoice.vala:631
+msgid "No invoice data given!"
+msgstr ""
+
+#: pdf-invoice/pdf-invoice.vala:634
+msgid "No invoice date given!"
+msgstr ""
+
+#: pdf-invoice/pdf-invoice.vala:637
+msgid "No invoice recipient given!"
+msgstr ""
+
+#: scanner-session/scannersession.vala:253
+msgid "No more Items on your Shopping Cart"
+msgstr ""
+
+#: mail/mailer.vala:141 mail/mailer.vala:149
+msgid "No such mail"
+msgstr ""
+
+#: database/database.vala:611
+msgid "No such session available in database!"
+msgstr ""
+
+#: database/database.vala:710 database/database.vala:722
+msgid "No such user available in database!"
+msgstr ""
+
+#: cli/main.vala:25
+#, c-format
+msgid ""
+"Nothing to send.\n"
+"Usage: %s \n"
+"Example: %s \"USER 1\" \"LOGOUT\"\n"
+msgstr ""
+
+#: invoice/main.vala:20
+msgid "Possible values for : temporary, final\n"
+msgstr ""
+
+#: pdf-invoice/pdf-invoice.vala:471
+msgid "Prices > 9999.99\342\202\254 are not supported!"
+msgstr ""
+
+#: database/database.vala:487
+#, c-format
+msgid "Remove purchase of %s"
+msgstr ""
+
+#: mail/mailer.vala:180
+#, c-format
+msgid "Reply from SMTP-Server: %s"
+msgstr ""
+
+#: cli/cli.vala:24
+#, c-format
+msgid "Sending: %s\n"
+msgstr ""
+
+#: curses-ui/main.vala:72
+msgid "Shop System has been started"
+msgstr ""
+
+#: curses-ui/main.vala:78
+msgid "Stopping Shop System"
+msgstr ""
+
+#: web/main.vala:55
+#, c-format
+msgid "TLS certificate: %s\n"
+msgstr ""
+
+#: web/main.vala:56
+#, c-format
+msgid "TLS private key: %s\n"
+msgstr ""
+
+#: invoice/invoice.vala:314
+msgid "Unknown MessageType"
+msgstr ""
+
+#: invoice/main.vala:19
+#, c-format
+msgid "Usage: %s [timestamp]\n"
+msgstr ""
+
+#: web/main.vala:54
+#, c-format
+msgid "Web Server Port: %u\n"
+msgstr ""
+
+#: pdf-invoice/pdf-invoice.vala:475
+msgid "Years after 9999 are not supported!"
+msgstr ""
+
+#: scanner-session/scannersession.vala:240
+#, c-format
+msgid "article added to shopping card: %s (%s \342\202\254)"
+msgstr ""
+
+#: scanner-session/scannersession.vala:189
+#, c-format
+msgid "article info: %s (Member: %s \342\202\254, Guest: %s \342\202\254"
+msgstr ""
+
+#: input-device/input-device.vala:249
+#, c-format
+msgid "barcode: %s\n"
+msgstr ""
+
+#: mail/mailer.vala:107
+msgid "could not configure STARTTLS"
+msgstr ""
+
+#: web/template.vala:61 web/template.vala:84
+msgid "could not load templates!"
+msgstr ""
+
+#: curses-ui/main.vala:27 curses-ui/main.vala:29
+#, c-format
+msgid "could not play audio: %s"
+msgstr ""
+
+#: mail/mailer.vala:99
+msgid "could not setup server"
+msgstr ""
+
+#: mail/mailer.vala:176
+msgid "eSMTP: Start Session failed!"
+msgstr ""
+
+#: database/database.vala:370 database/database.vala:387
+#: database/database.vala:403 database/database.vala:419
+#: database/database.vala:435 database/database.vala:448
+#: database/database.vala:470 database/database.vala:492
+#: database/database.vala:500 database/database.vala:527
+#: database/database.vala:541 database/database.vala:556
+#: database/database.vala:583 database/database.vala:591
+#: database/database.vala:601 database/database.vala:638
+#: database/database.vala:675 database/database.vala:689
+#: database/database.vala:700 database/database.vala:737
+#: database/database.vala:765 database/database.vala:884
+#: database/database.vala:892 database/database.vala:913
+#: database/database.vala:919 database/database.vala:927
+#: database/database.vala:1028 database/database.vala:1079
+#: database/database.vala:1129 database/database.vala:1212
+#, c-format
+msgid "internal error: %d"
+msgstr ""
+
+#: scanner-session/scannersession.vala:249
+#, c-format
+msgid "removed last Item from Shopping Cart: %s"
+msgstr ""
+
+#: serial-device/serial-device.vala:43
+msgid "serial device is locked!\n"
+msgstr ""
+
+#: mail/mailer.vala:79
+msgid "server or port configuration is missing"
+msgstr ""
+
+#: input-device/input-device.vala:224
+msgid "short read!\n"
+msgstr ""
+
+#: database/database.vala:490
+msgid "undo not possible without purchases"
+msgstr ""
+
+#: database/database.vala:385 database/database.vala:401
+#: database/database.vala:417 database/database.vala:433
+#: database/database.vala:468
+#, c-format
+msgid "unknown product: %llu"
+msgstr ""
+
+#: database/database.vala:1210
+#, c-format
+msgid "unknown rfid: %s"
+msgstr ""
+
+#: database/database.vala:636
+msgid "user not found"
+msgstr ""
diff --git a/data/sounds/system/LICENSE b/data/sounds/system/LICENSE
new file mode 100644
index 0000000..db764a9
--- /dev/null
+++ b/data/sounds/system/LICENSE
@@ -0,0 +1,14 @@
+Files: error.ogg
+Source: http://www.freesound.org/people/Corsica_S/sounds/56097/
+Copyright: 2008, Corsica_S
+License: CC-BY-3.0
+
+Files: shutdown.ogg
+Source: http://www.freesound.org/people/Corsica_S/sounds/104719/
+Copyright: 2010, Corsica_S
+License: CC-BY-3.0
+
+Files: startup.ogg
+Source: http://www.freesound.org/people/Corsica_S/sounds/83342/
+Copyright: 2010, Corsica_S
+License: CC-BY-3.0
diff --git a/data/sounds/system/error.ogg b/data/sounds/system/error.ogg
new file mode 100644
index 0000000..f34f375
Binary files /dev/null and b/data/sounds/system/error.ogg differ
diff --git a/data/sounds/system/shutdown.ogg b/data/sounds/system/shutdown.ogg
new file mode 100644
index 0000000..2560efd
Binary files /dev/null and b/data/sounds/system/shutdown.ogg differ
diff --git a/data/sounds/system/startup.ogg b/data/sounds/system/startup.ogg
new file mode 100644
index 0000000..31649ff
Binary files /dev/null and b/data/sounds/system/startup.ogg differ
diff --git a/data/sounds/user/beep/LICENSE b/data/sounds/user/beep/LICENSE
new file mode 100644
index 0000000..a0acd3d
--- /dev/null
+++ b/data/sounds/user/beep/LICENSE
@@ -0,0 +1,24 @@
+Files: error/buzzer.ogg
+Source: http://www.freesound.org/people/guitarguy1985/sounds/54047/
+Copyright: 2008, guitarguy1985
+License: CC-0
+
+Files: login/toycaralarm.ogg
+Source: http://www.freesound.org/people/AMPUL/sounds/29723/
+Copyright: 2007, AMPUL
+License: Sampling+
+
+Files: logout/cha ching.ogg
+Source: http://www.freesound.org/people/creek23/sounds/75235/
+Copyright: 2009, creek23
+License: CC-BY-3.0
+
+Files: purchase/camera.ogg
+Source: http://www.freesound.org/people/FreqMan/sounds/42899/
+Copyright: 2007, FreqMan
+License: CC-BY-3.0
+
+Files: purchase/beep.ogg
+Source: http://www.freesound.org/people/KorgMS2000B/sounds/54415/
+Copyright: 2008, KorgMS2000B
+License: Sampling+
diff --git a/data/sounds/user/beep/error/buzzer.ogg b/data/sounds/user/beep/error/buzzer.ogg
new file mode 100644
index 0000000..eebd2fa
Binary files /dev/null and b/data/sounds/user/beep/error/buzzer.ogg differ
diff --git a/data/sounds/user/beep/login/toycaralarm.ogg b/data/sounds/user/beep/login/toycaralarm.ogg
new file mode 100644
index 0000000..817a4c9
Binary files /dev/null and b/data/sounds/user/beep/login/toycaralarm.ogg differ
diff --git a/data/sounds/user/beep/logout/cha ching.ogg b/data/sounds/user/beep/logout/cha ching.ogg
new file mode 100644
index 0000000..4c6d88d
Binary files /dev/null and b/data/sounds/user/beep/logout/cha ching.ogg differ
diff --git a/data/sounds/user/beep/purchase/beep.ogg b/data/sounds/user/beep/purchase/beep.ogg
new file mode 100644
index 0000000..e3dbad4
Binary files /dev/null and b/data/sounds/user/beep/purchase/beep.ogg differ
diff --git a/data/sounds/user/beep/purchase/camera.ogg b/data/sounds/user/beep/purchase/camera.ogg
new file mode 100644
index 0000000..41373d4
Binary files /dev/null and b/data/sounds/user/beep/purchase/camera.ogg differ
diff --git a/data/sounds/user/dude/LICENSE b/data/sounds/user/dude/LICENSE
new file mode 100644
index 0000000..3b65ddd
--- /dev/null
+++ b/data/sounds/user/dude/LICENSE
@@ -0,0 +1,19 @@
+Files: error/I broke something.ogg
+Source: http://www.freesound.org/people/ecfike/sounds/154574/
+Copyright: ecfike
+License: CC-0
+
+Files: login/hey dude, whats up man.ogg
+Source: http://www.freesound.org/people/ecfike/sounds/154619/
+Copyright: ecfike
+License: CC-0
+
+Files: logout/bye.ogg
+Source: http://www.freesound.org/people/ecfike/sounds/154546/
+Copyright: ecfike
+License: CC-0
+
+Files: purchase/yeah man.ogg
+Source: http://www.freesound.org/people/ecfike/sounds/154582/
+Copyright: ecfike
+License: CC-0
diff --git a/data/sounds/user/dude/error/I broke something.ogg b/data/sounds/user/dude/error/I broke something.ogg
new file mode 100644
index 0000000..ae8c298
Binary files /dev/null and b/data/sounds/user/dude/error/I broke something.ogg differ
diff --git a/data/sounds/user/dude/login/hey dude, whats up man.ogg b/data/sounds/user/dude/login/hey dude, whats up man.ogg
new file mode 100644
index 0000000..8b6d7b5
Binary files /dev/null and b/data/sounds/user/dude/login/hey dude, whats up man.ogg differ
diff --git a/data/sounds/user/dude/logout/bye.ogg b/data/sounds/user/dude/logout/bye.ogg
new file mode 100644
index 0000000..de62d3b
Binary files /dev/null and b/data/sounds/user/dude/logout/bye.ogg differ
diff --git a/data/sounds/user/dude/purchase/yeah man.ogg b/data/sounds/user/dude/purchase/yeah man.ogg
new file mode 100644
index 0000000..b3c2d09
Binary files /dev/null and b/data/sounds/user/dude/purchase/yeah man.ogg differ
diff --git a/data/sounds/user/girlfriend/LICENSE b/data/sounds/user/girlfriend/LICENSE
new file mode 100644
index 0000000..9153012
--- /dev/null
+++ b/data/sounds/user/girlfriend/LICENSE
@@ -0,0 +1,39 @@
+Files: error/no, no, forget it.ogg
+Source: http://www.freesound.org/people/epanody/sounds/86427/
+Copyright: 2009, epanody
+License: CC-BY-3.0
+
+Files: login/hello.ogg
+Source: http://www.freesound.org/people/epanody/sounds/97830/
+Copyright: 2010, epanody
+License: CC-BY-3.0
+
+Files: login/we need to talk about this.ogg
+Source: http://www.freesound.org/people/epanody/sounds/86454/
+Copyright: 2009, epanody
+License: CC-BY-3.0
+
+Files: logout/get out, get outta here.ogg
+Source: http://www.freesound.org/people/epanody/sounds/86409/
+Copyrigt: 2009, epanody
+License: CC-BY-3.0
+
+Files: logout/why dont you just leave then.ogg
+Source: http://www.freesound.org/people/epanody/sounds/86463/
+Copyrigt: 2009, epanody
+License: CC-BY-3.0
+
+Files: logout/i dont have time for this.ogg
+Source: http://www.freesound.org/people/epanody/sounds/86386/
+Copyrigt: 2009, epanody
+License: CC-BY-3.0
+
+Files: purchase/code accepted.ogg
+Source: http://www.freesound.org/people/epanody/sounds/97827/
+Copyright: 2010, epanody
+License: CC-BY-3.0
+
+Files: purchase/yeah so what.ogg
+Source: http://www.freesound.org/people/epanody/sounds/86468/
+Copyright: 2010, epanody
+License: CC-BY-3.0
diff --git a/data/sounds/user/girlfriend/error/no, no, forget it.ogg b/data/sounds/user/girlfriend/error/no, no, forget it.ogg
new file mode 100644
index 0000000..825f24b
Binary files /dev/null and b/data/sounds/user/girlfriend/error/no, no, forget it.ogg differ
diff --git a/data/sounds/user/girlfriend/login/hello.ogg b/data/sounds/user/girlfriend/login/hello.ogg
new file mode 100644
index 0000000..7c76e4d
Binary files /dev/null and b/data/sounds/user/girlfriend/login/hello.ogg differ
diff --git a/data/sounds/user/girlfriend/login/we need to talk about this.ogg b/data/sounds/user/girlfriend/login/we need to talk about this.ogg
new file mode 100644
index 0000000..2df048f
Binary files /dev/null and b/data/sounds/user/girlfriend/login/we need to talk about this.ogg differ
diff --git a/data/sounds/user/girlfriend/logout/get out, get outta here.ogg b/data/sounds/user/girlfriend/logout/get out, get outta here.ogg
new file mode 100644
index 0000000..c305d26
Binary files /dev/null and b/data/sounds/user/girlfriend/logout/get out, get outta here.ogg differ
diff --git a/data/sounds/user/girlfriend/logout/i dont have time for this.ogg b/data/sounds/user/girlfriend/logout/i dont have time for this.ogg
new file mode 100644
index 0000000..77b0fce
Binary files /dev/null and b/data/sounds/user/girlfriend/logout/i dont have time for this.ogg differ
diff --git a/data/sounds/user/girlfriend/logout/why dont you just leave then.ogg b/data/sounds/user/girlfriend/logout/why dont you just leave then.ogg
new file mode 100644
index 0000000..eac83c8
Binary files /dev/null and b/data/sounds/user/girlfriend/logout/why dont you just leave then.ogg differ
diff --git a/data/sounds/user/girlfriend/purchase/code accepted.ogg b/data/sounds/user/girlfriend/purchase/code accepted.ogg
new file mode 100644
index 0000000..34dc3d4
Binary files /dev/null and b/data/sounds/user/girlfriend/purchase/code accepted.ogg differ
diff --git a/data/sounds/user/girlfriend/purchase/yeah so what.ogg b/data/sounds/user/girlfriend/purchase/yeah so what.ogg
new file mode 100644
index 0000000..c488a26
Binary files /dev/null and b/data/sounds/user/girlfriend/purchase/yeah so what.ogg differ
diff --git a/data/sounds/user/japanese/LICENSE b/data/sounds/user/japanese/LICENSE
new file mode 100644
index 0000000..d0a0fbe
--- /dev/null
+++ b/data/sounds/user/japanese/LICENSE
@@ -0,0 +1,29 @@
+Files: error/no, iie.ogg
+Source: https://www.freesound.org/people/yugi16dm/sounds/323211/
+Copyright: 2015, yugi16dm
+License: CC-0
+
+Files: error/sumimasen.ogg
+Source: https://www.freesound.org/people/yugi16dm/sounds/323200/
+Copyright: 2015, yugi16dm
+License: CC-0
+
+Files: login/hello.ogg
+Source: https://www.freesound.org/people/yugi16dm/sounds/323204/
+Copyright: 2015, yugi16dm
+License: CC-0
+
+Files: logout/goodbye.ogg
+Source: https://www.freesound.org/people/yugi16dm/sounds/323212/
+Copyright: 2015, yugi16dm
+License: CC-0
+
+Files: purchase/domo.ogg
+Source: https://www.freesound.org/people/Reitanna/sounds/234967/
+Copyright: 2014, Reitanna
+License: CC-0
+
+purchase/domo-arigato.ogg
+Source: https://www.freesound.org/people/Reitanna/sounds/234997/
+Copyright: 2014, Reitanna
+License: CC-0
diff --git a/data/sounds/user/japanese/error/no, iie.ogg b/data/sounds/user/japanese/error/no, iie.ogg
new file mode 100644
index 0000000..286d0fd
Binary files /dev/null and b/data/sounds/user/japanese/error/no, iie.ogg differ
diff --git a/data/sounds/user/japanese/error/sumimasen.ogg b/data/sounds/user/japanese/error/sumimasen.ogg
new file mode 100644
index 0000000..0ae16ee
Binary files /dev/null and b/data/sounds/user/japanese/error/sumimasen.ogg differ
diff --git a/data/sounds/user/japanese/login/hello.ogg b/data/sounds/user/japanese/login/hello.ogg
new file mode 100644
index 0000000..951eb5a
Binary files /dev/null and b/data/sounds/user/japanese/login/hello.ogg differ
diff --git a/data/sounds/user/japanese/logout/goodbye.ogg b/data/sounds/user/japanese/logout/goodbye.ogg
new file mode 100644
index 0000000..270d838
Binary files /dev/null and b/data/sounds/user/japanese/logout/goodbye.ogg differ
diff --git a/data/sounds/user/japanese/purchase/domo-arigato.ogg b/data/sounds/user/japanese/purchase/domo-arigato.ogg
new file mode 100644
index 0000000..bd74be5
Binary files /dev/null and b/data/sounds/user/japanese/purchase/domo-arigato.ogg differ
diff --git a/data/sounds/user/japanese/purchase/domo.ogg b/data/sounds/user/japanese/purchase/domo.ogg
new file mode 100644
index 0000000..24724c7
Binary files /dev/null and b/data/sounds/user/japanese/purchase/domo.ogg differ
diff --git a/data/sounds/user/robot/LICENSE b/data/sounds/user/robot/LICENSE
new file mode 100644
index 0000000..ac119af
--- /dev/null
+++ b/data/sounds/user/robot/LICENSE
@@ -0,0 +1,19 @@
+Files: error/critical error.ogg
+Source: http://www.freesound.org/people/liquidhot/sounds/23514/
+Copyright: 2009, liquidhot
+License: CC-BY-3.0
+
+Files: login/login accepted.ogg
+Source: http://www.freesound.org/people/liquidhot/sounds/23516/
+Copyright: 2006, liquidhot
+License: CC-BY-3.0
+
+Files: logout/terminating.ogg
+Source: http://www.freesound.org/people/liquidhot/sounds/23513/
+Copyright: 2006, liquidhot
+License: CC-BY-3.0
+
+Files: purchase/code accepted.ogg
+Source: http://www.freesound.org/people/epanody/sounds/97827/
+Copyright: 2010, epanody
+License: CC-BY-3.0
diff --git a/data/sounds/user/robot/error/critical error.ogg b/data/sounds/user/robot/error/critical error.ogg
new file mode 100644
index 0000000..f3abfcd
Binary files /dev/null and b/data/sounds/user/robot/error/critical error.ogg differ
diff --git a/data/sounds/user/robot/login/login accepted.ogg b/data/sounds/user/robot/login/login accepted.ogg
new file mode 100644
index 0000000..90e0e48
Binary files /dev/null and b/data/sounds/user/robot/login/login accepted.ogg differ
diff --git a/data/sounds/user/robot/logout/terminating.ogg b/data/sounds/user/robot/logout/terminating.ogg
new file mode 100644
index 0000000..dccb6d6
Binary files /dev/null and b/data/sounds/user/robot/logout/terminating.ogg differ
diff --git a/data/sounds/user/robot/purchase/accepted.ogg b/data/sounds/user/robot/purchase/accepted.ogg
new file mode 100644
index 0000000..b980230
Binary files /dev/null and b/data/sounds/user/robot/purchase/accepted.ogg differ
diff --git a/data/sounds/user/sexy/LICENSE b/data/sounds/user/sexy/LICENSE
new file mode 100644
index 0000000..99238dc
--- /dev/null
+++ b/data/sounds/user/sexy/LICENSE
@@ -0,0 +1,39 @@
+Files: error/error.ogg
+Source: http://www.freesound.org/people/Corsica_S/sounds/56097/
+Copyright: 2008, Corsica_S
+License: CC-BY-3.0
+
+Files: login/welcome back sexy bitch.ogg
+Source: http://www.freesound.org/people/Corsica_S/sounds/99889/
+Copyright: 2010, Corsica_S
+License: CC-BY-3.0
+
+Files: logout/what about me.ogg
+Source: http://www.freesound.org/people/epanody/sounds/86457/
+Copyright: 2010, Corsica_S
+License: CC-BY-3.0
+
+Files: logout/well if thats how you feel.ogg
+Source: http://www.freesound.org/people/epanody/sounds/86456/
+Copyright: 2010, Corsica_S
+License: CC-BY-3.0
+
+Files: purchase/yes, master.ogg
+Source: http://www.freesound.org/people/Corsica_S/sounds/107555/
+Copyright: 2010, Corsica_S
+License: CC-BY-3.0
+
+Files: purchase/your will is my command.ogg
+Source: http://www.freesound.org/people/Corsica_S/sounds/107556/
+Copyright: 2012, Corsica_S
+License: CC-BY-3.0
+
+Files: purchase/do it again.ogg
+Source: http://www.freesound.org/people/Corsica_S/sounds/95235/
+Copyright: 2010, Corsica_S
+License: CC-BY-3.0
+
+Files: purchase/what is next.ogg
+Source: http://www.freesound.org/people/Corsica_S/sounds/72257/
+Copyright: 2009, Corsica_S
+License: CC-BY-3.0
diff --git a/data/sounds/user/sexy/error/error.ogg b/data/sounds/user/sexy/error/error.ogg
new file mode 100644
index 0000000..bd0f41d
Binary files /dev/null and b/data/sounds/user/sexy/error/error.ogg differ
diff --git a/data/sounds/user/sexy/login/welcome back sexy bitch.ogg b/data/sounds/user/sexy/login/welcome back sexy bitch.ogg
new file mode 100644
index 0000000..77fc7ac
Binary files /dev/null and b/data/sounds/user/sexy/login/welcome back sexy bitch.ogg differ
diff --git a/data/sounds/user/sexy/logout/well if thats how you feel.ogg b/data/sounds/user/sexy/logout/well if thats how you feel.ogg
new file mode 100644
index 0000000..30fb42c
Binary files /dev/null and b/data/sounds/user/sexy/logout/well if thats how you feel.ogg differ
diff --git a/data/sounds/user/sexy/logout/what about me.ogg b/data/sounds/user/sexy/logout/what about me.ogg
new file mode 100644
index 0000000..9ed10db
Binary files /dev/null and b/data/sounds/user/sexy/logout/what about me.ogg differ
diff --git a/data/sounds/user/sexy/purchase/do it again.ogg b/data/sounds/user/sexy/purchase/do it again.ogg
new file mode 100644
index 0000000..6465c60
Binary files /dev/null and b/data/sounds/user/sexy/purchase/do it again.ogg differ
diff --git a/data/sounds/user/sexy/purchase/what is next.ogg b/data/sounds/user/sexy/purchase/what is next.ogg
new file mode 100644
index 0000000..e3d3927
Binary files /dev/null and b/data/sounds/user/sexy/purchase/what is next.ogg differ
diff --git a/data/sounds/user/sexy/purchase/yes, master.ogg b/data/sounds/user/sexy/purchase/yes, master.ogg
new file mode 100644
index 0000000..f0723a1
Binary files /dev/null and b/data/sounds/user/sexy/purchase/yes, master.ogg differ
diff --git a/data/sounds/user/sexy/purchase/your will is my command.ogg b/data/sounds/user/sexy/purchase/your will is my command.ogg
new file mode 100644
index 0000000..3f07616
Binary files /dev/null and b/data/sounds/user/sexy/purchase/your will is my command.ogg differ
diff --git a/data/systemd/meson.build b/data/systemd/meson.build
new file mode 100644
index 0000000..cd68b1b
--- /dev/null
+++ b/data/systemd/meson.build
@@ -0,0 +1,18 @@
+systemd = dependency('systemd')
+systemdsystemunitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir')
+bindir = join_paths(get_option('prefix'), get_option('bindir'))
+
+cfg = configuration_data()
+cfg.set('PATH', bindir)
+
+install_data('shopsystem-mail-daily-invoice.timer', install_dir: systemdsystemunitdir)
+install_data('shopsystem-mail-monthly-invoice.timer', install_dir: systemdsystemunitdir)
+install_data('shopsystem-mail-backup.timer', install_dir: systemdsystemunitdir)
+
+configure_file(input: 'shopsystem-input-device.service.in', output: 'shopsystem-input-device.service', configuration: cfg, install_dir: systemdsystemunitdir)
+configure_file(input: 'shopsystem-mail-backup.service.in', output: 'shopsystem-mail-backup.service', configuration: cfg, install_dir: systemdsystemunitdir)
+configure_file(input: 'shopsystem-mail-daily-invoice.service.in', output: 'shopsystem-mail-daily-invoice.service', configuration: cfg, install_dir: systemdsystemunitdir)
+configure_file(input: 'shopsystem-mail-monthly-invoice.service.in', output: 'shopsystem-mail-monthly-invoice.service', configuration: cfg, install_dir: systemdsystemunitdir)
+configure_file(input: 'shopsystem-ui.service.in', output: 'shopsystem-ui.service', configuration: cfg, install_dir: systemdsystemunitdir)
+configure_file(input: 'shopsystem-web.service.in', output: 'shopsystem-web.service', configuration: cfg, install_dir: systemdsystemunitdir)
+configure_file(input: 'shopsystem-display-on-off.service.in', output: 'shopsystem-display-on-off.service', configuration: cfg, install_dir: systemdsystemunitdir)
diff --git a/data/systemd/shopsystem-display-on-off.service.in b/data/systemd/shopsystem-display-on-off.service.in
new file mode 100644
index 0000000..05787a8
--- /dev/null
+++ b/data/systemd/shopsystem-display-on-off.service.in
@@ -0,0 +1,12 @@
+[Unit]
+Description=Display on-off via MQTT
+After=systemd-user-sessions.service
+
+[Service]
+Type=simple
+ExecStart=@PATH@/display-on-off.sh
+Restart=always
+RestartSec=10
+
+[Install]
+WantedBy=multi-user.target
diff --git a/data/systemd/shopsystem-input-device.service.in b/data/systemd/shopsystem-input-device.service.in
new file mode 100644
index 0000000..d343318
--- /dev/null
+++ b/data/systemd/shopsystem-input-device.service.in
@@ -0,0 +1,20 @@
+[Unit]
+Description=Shop System Input Device Handler
+BindsTo=dev-barcodescanner.device
+Requires=dev-barcodescanner.device
+After=dev-barcodescanner.device
+
+[Service]
+Type=dbus
+BusName=io.mainframe.shopsystem.InputDevice
+User=shop
+Group=shop
+Restart=always
+RestartSec=10
+ExecStart=@PATH@/shop-input-device
+StandardInput=tty
+StandardOutput=tty
+TTYPath=/dev/tty3
+
+[Install]
+WantedBy=multi-user.target
diff --git a/data/systemd/shopsystem-mail-backup.service.in b/data/systemd/shopsystem-mail-backup.service.in
new file mode 100644
index 0000000..1f55a17
--- /dev/null
+++ b/data/systemd/shopsystem-mail-backup.service.in
@@ -0,0 +1,12 @@
+[Unit]
+Description=Send shopsystem backup mail
+DefaultDependencies=no
+Conflicts=shutdown.target
+Before=shutdown.target
+
+[Service]
+Type=oneshot
+User=shop
+Group=shop
+ExecStart=@PATH@/shop-backup
+IOSchedulingClass=idle
diff --git a/data/systemd/shopsystem-mail-backup.timer b/data/systemd/shopsystem-mail-backup.timer
new file mode 100644
index 0000000..b0c18a2
--- /dev/null
+++ b/data/systemd/shopsystem-mail-backup.timer
@@ -0,0 +1,9 @@
+[Unit]
+Description=Run shopsystem backup mail daily
+
+[Timer]
+OnCalendar=*-*-* 08:00:00
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/data/systemd/shopsystem-mail-daily-invoice.service.in b/data/systemd/shopsystem-mail-daily-invoice.service.in
new file mode 100644
index 0000000..a14918d
--- /dev/null
+++ b/data/systemd/shopsystem-mail-daily-invoice.service.in
@@ -0,0 +1,13 @@
+[Unit]
+Description=Send shopsystem daily invoice mail
+DefaultDependencies=no
+Conflicts=shutdown.target
+
+[Service]
+Type=oneshot
+User=shop
+Group=shop
+ExecStart=@PATH@/shop-invoice temporary
+StandardOutput=journal
+StandardError=journal
+IOSchedulingClass=idle
diff --git a/data/systemd/shopsystem-mail-daily-invoice.timer b/data/systemd/shopsystem-mail-daily-invoice.timer
new file mode 100644
index 0000000..8688d03
--- /dev/null
+++ b/data/systemd/shopsystem-mail-daily-invoice.timer
@@ -0,0 +1,9 @@
+[Unit]
+Description=Send shopsystem daily invoice
+
+[Timer]
+OnCalendar=*-*-* 08:15:00
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/data/systemd/shopsystem-mail-monthly-invoice.service.in b/data/systemd/shopsystem-mail-monthly-invoice.service.in
new file mode 100644
index 0000000..8e0321c
--- /dev/null
+++ b/data/systemd/shopsystem-mail-monthly-invoice.service.in
@@ -0,0 +1,13 @@
+[Unit]
+Description=Send shopsystem monthly invoice mail
+DefaultDependencies=no
+Conflicts=shutdown.target
+
+[Service]
+Type=oneshot
+User=shop
+Group=shop
+ExecStart=@PATH@/shop-invoice final
+StandardOutput=journal
+StandardError=journal
+IOSchedulingClass=idle
diff --git a/data/systemd/shopsystem-mail-monthly-invoice.timer b/data/systemd/shopsystem-mail-monthly-invoice.timer
new file mode 100644
index 0000000..a29d964
--- /dev/null
+++ b/data/systemd/shopsystem-mail-monthly-invoice.timer
@@ -0,0 +1,9 @@
+[Unit]
+Description=Send shopsystem monthly invoice
+
+[Timer]
+OnCalendar=*-*-01 09:00:00
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/data/systemd/shopsystem-ui.service.in b/data/systemd/shopsystem-ui.service.in
new file mode 100644
index 0000000..c4bd3a7
--- /dev/null
+++ b/data/systemd/shopsystem-ui.service.in
@@ -0,0 +1,17 @@
+[Unit]
+Description=Shop System UI
+
+[Service]
+User=shop
+Group=shop
+Type=simple
+ExecStart=@PATH@/shop-curses-ui
+StandardInput=tty
+StandardOutput=tty
+TTYPath=/dev/tty3
+TTYReset=yes
+TTYVHangup=yes
+TTYVTDisallocate=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/data/systemd/shopsystem-web.service.in b/data/systemd/shopsystem-web.service.in
new file mode 100644
index 0000000..5707a02
--- /dev/null
+++ b/data/systemd/shopsystem-web.service.in
@@ -0,0 +1,13 @@
+[Unit]
+Description=Shop System Web Interface
+
+[Service]
+User=shop
+Group=shop
+Type=simple
+ExecStart=@PATH@/shop-web
+StandardInput=null
+StandardOutput=journal
+
+[Install]
+WantedBy=multi-user.target
diff --git a/data/templates/aliases/index.html b/data/templates/aliases/index.html
new file mode 100644
index 0000000..c914968
--- /dev/null
+++ b/data/templates/aliases/index.html
@@ -0,0 +1,17 @@
+
+
+
EAN
Real EAN
Name
+
+
+ {{{DATA}}}
+
+
+
+
+
+
diff --git a/data/templates/aliases/new.html b/data/templates/aliases/new.html
new file mode 100644
index 0000000..34b610e
--- /dev/null
+++ b/data/templates/aliases/new.html
@@ -0,0 +1,14 @@
+
Add new product alias
+
+
+
Successfully created new alias: {{{EAN}}} ➜ {{{REAL_EAN}}}
+
+
+
+
Error
+ Creating new product alias failed. Please make sure
+ you have sufficient permissions and gave valid values
+ for EAN and real EAN.
+
+
+Back to alias list
diff --git a/data/templates/base.html b/data/templates/base.html
new file mode 100644
index 0000000..1870d0f
--- /dev/null
+++ b/data/templates/base.html
@@ -0,0 +1,26 @@
+
+
+
+
+ {{{TITLE}}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/templates/errors/403.html b/data/templates/errors/403.html
new file mode 100644
index 0000000..94763d7
--- /dev/null
+++ b/data/templates/errors/403.html
@@ -0,0 +1 @@
+Access denied!
diff --git a/data/templates/errors/todo.html b/data/templates/errors/todo.html
new file mode 100644
index 0000000..a494f55
--- /dev/null
+++ b/data/templates/errors/todo.html
@@ -0,0 +1 @@
+This page is Work in Progress.
diff --git a/data/templates/img/glyphicons-halflings-white.png b/data/templates/img/glyphicons-halflings-white.png
new file mode 100644
index 0000000..3bf6484
Binary files /dev/null and b/data/templates/img/glyphicons-halflings-white.png differ
diff --git a/data/templates/img/glyphicons-halflings.png b/data/templates/img/glyphicons-halflings.png
new file mode 100644
index 0000000..a996999
Binary files /dev/null and b/data/templates/img/glyphicons-halflings.png differ
diff --git a/data/templates/index.html b/data/templates/index.html
new file mode 100644
index 0000000..bee2d95
--- /dev/null
+++ b/data/templates/index.html
@@ -0,0 +1 @@
+
Welcome to the new {{{SHORTNAME}}} Shop System.
diff --git a/data/templates/js/bootstrap.js b/data/templates/js/bootstrap.js
new file mode 100644
index 0000000..44109f6
--- /dev/null
+++ b/data/templates/js/bootstrap.js
@@ -0,0 +1,2280 @@
+/* ===================================================
+ * bootstrap-transition.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#transitions
+ * ===================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
+ * ======================================================= */
+
+ $(function () {
+
+ $.support.transition = (function () {
+
+ var transitionEnd = (function () {
+
+ var el = document.createElement('bootstrap')
+ , transEndEventNames = {
+ 'WebkitTransition' : 'webkitTransitionEnd'
+ , 'MozTransition' : 'transitionend'
+ , 'OTransition' : 'oTransitionEnd otransitionend'
+ , 'transition' : 'transitionend'
+ }
+ , name
+
+ for (name in transEndEventNames){
+ if (el.style[name] !== undefined) {
+ return transEndEventNames[name]
+ }
+ }
+
+ }())
+
+ return transitionEnd && {
+ end: transitionEnd
+ }
+
+ })()
+
+ })
+
+}(window.jQuery);/* ==========================================================
+ * bootstrap-alert.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#alerts
+ * ==========================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* ALERT CLASS DEFINITION
+ * ====================== */
+
+ var dismiss = '[data-dismiss="alert"]'
+ , Alert = function (el) {
+ $(el).on('click', dismiss, this.close)
+ }
+
+ Alert.prototype.close = function (e) {
+ var $this = $(this)
+ , selector = $this.attr('data-target')
+ , $parent
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ $parent = $(selector)
+
+ e && e.preventDefault()
+
+ $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
+
+ $parent.trigger(e = $.Event('close'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent.removeClass('in')
+
+ function removeElement() {
+ $parent
+ .trigger('closed')
+ .remove()
+ }
+
+ $.support.transition && $parent.hasClass('fade') ?
+ $parent.on($.support.transition.end, removeElement) :
+ removeElement()
+ }
+
+
+ /* ALERT PLUGIN DEFINITION
+ * ======================= */
+
+ var old = $.fn.alert
+
+ $.fn.alert = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('alert')
+ if (!data) $this.data('alert', (data = new Alert(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.alert.Constructor = Alert
+
+
+ /* ALERT NO CONFLICT
+ * ================= */
+
+ $.fn.alert.noConflict = function () {
+ $.fn.alert = old
+ return this
+ }
+
+
+ /* ALERT DATA-API
+ * ============== */
+
+ $(document).on('click.alert.data-api', dismiss, Alert.prototype.close)
+
+}(window.jQuery);/* ============================================================
+ * bootstrap-button.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#buttons
+ * ============================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* BUTTON PUBLIC CLASS DEFINITION
+ * ============================== */
+
+ var Button = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, $.fn.button.defaults, options)
+ }
+
+ Button.prototype.setState = function (state) {
+ var d = 'disabled'
+ , $el = this.$element
+ , data = $el.data()
+ , val = $el.is('input') ? 'val' : 'html'
+
+ state = state + 'Text'
+ data.resetText || $el.data('resetText', $el[val]())
+
+ $el[val](data[state] || this.options[state])
+
+ // push to event loop to allow forms to submit
+ setTimeout(function () {
+ state == 'loadingText' ?
+ $el.addClass(d).attr(d, d) :
+ $el.removeClass(d).removeAttr(d)
+ }, 0)
+ }
+
+ Button.prototype.toggle = function () {
+ var $parent = this.$element.closest('[data-toggle="buttons-radio"]')
+
+ $parent && $parent
+ .find('.active')
+ .removeClass('active')
+
+ this.$element.toggleClass('active')
+ }
+
+
+ /* BUTTON PLUGIN DEFINITION
+ * ======================== */
+
+ var old = $.fn.button
+
+ $.fn.button = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('button')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('button', (data = new Button(this, options)))
+ if (option == 'toggle') data.toggle()
+ else if (option) data.setState(option)
+ })
+ }
+
+ $.fn.button.defaults = {
+ loadingText: 'loading...'
+ }
+
+ $.fn.button.Constructor = Button
+
+
+ /* BUTTON NO CONFLICT
+ * ================== */
+
+ $.fn.button.noConflict = function () {
+ $.fn.button = old
+ return this
+ }
+
+
+ /* BUTTON DATA-API
+ * =============== */
+
+ $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) {
+ var $btn = $(e.target)
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+ $btn.button('toggle')
+ })
+
+}(window.jQuery);/* ==========================================================
+ * bootstrap-carousel.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#carousel
+ * ==========================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* CAROUSEL CLASS DEFINITION
+ * ========================= */
+
+ var Carousel = function (element, options) {
+ this.$element = $(element)
+ this.$indicators = this.$element.find('.carousel-indicators')
+ this.options = options
+ this.options.pause == 'hover' && this.$element
+ .on('mouseenter', $.proxy(this.pause, this))
+ .on('mouseleave', $.proxy(this.cycle, this))
+ }
+
+ Carousel.prototype = {
+
+ cycle: function (e) {
+ if (!e) this.paused = false
+ if (this.interval) clearInterval(this.interval);
+ this.options.interval
+ && !this.paused
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+ return this
+ }
+
+ , getActiveIndex: function () {
+ this.$active = this.$element.find('.item.active')
+ this.$items = this.$active.parent().children()
+ return this.$items.index(this.$active)
+ }
+
+ , to: function (pos) {
+ var activeIndex = this.getActiveIndex()
+ , that = this
+
+ if (pos > (this.$items.length - 1) || pos < 0) return
+
+ if (this.sliding) {
+ return this.$element.one('slid', function () {
+ that.to(pos)
+ })
+ }
+
+ if (activeIndex == pos) {
+ return this.pause().cycle()
+ }
+
+ return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
+ }
+
+ , pause: function (e) {
+ if (!e) this.paused = true
+ if (this.$element.find('.next, .prev').length && $.support.transition.end) {
+ this.$element.trigger($.support.transition.end)
+ this.cycle(true)
+ }
+ clearInterval(this.interval)
+ this.interval = null
+ return this
+ }
+
+ , next: function () {
+ if (this.sliding) return
+ return this.slide('next')
+ }
+
+ , prev: function () {
+ if (this.sliding) return
+ return this.slide('prev')
+ }
+
+ , slide: function (type, next) {
+ var $active = this.$element.find('.item.active')
+ , $next = next || $active[type]()
+ , isCycling = this.interval
+ , direction = type == 'next' ? 'left' : 'right'
+ , fallback = type == 'next' ? 'first' : 'last'
+ , that = this
+ , e
+
+ this.sliding = true
+
+ isCycling && this.pause()
+
+ $next = $next.length ? $next : this.$element.find('.item')[fallback]()
+
+ e = $.Event('slide', {
+ relatedTarget: $next[0]
+ , direction: direction
+ })
+
+ if ($next.hasClass('active')) return
+
+ if (this.$indicators.length) {
+ this.$indicators.find('.active').removeClass('active')
+ this.$element.one('slid', function () {
+ var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
+ $nextIndicator && $nextIndicator.addClass('active')
+ })
+ }
+
+ if ($.support.transition && this.$element.hasClass('slide')) {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $next.addClass(type)
+ $next[0].offsetWidth // force reflow
+ $active.addClass(direction)
+ $next.addClass(direction)
+ this.$element.one($.support.transition.end, function () {
+ $next.removeClass([type, direction].join(' ')).addClass('active')
+ $active.removeClass(['active', direction].join(' '))
+ that.sliding = false
+ setTimeout(function () { that.$element.trigger('slid') }, 0)
+ })
+ } else {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $active.removeClass('active')
+ $next.addClass('active')
+ this.sliding = false
+ this.$element.trigger('slid')
+ }
+
+ isCycling && this.cycle()
+
+ return this
+ }
+
+ }
+
+
+ /* CAROUSEL PLUGIN DEFINITION
+ * ========================== */
+
+ var old = $.fn.carousel
+
+ $.fn.carousel = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('carousel')
+ , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
+ , action = typeof option == 'string' ? option : options.slide
+ if (!data) $this.data('carousel', (data = new Carousel(this, options)))
+ if (typeof option == 'number') data.to(option)
+ else if (action) data[action]()
+ else if (options.interval) data.pause().cycle()
+ })
+ }
+
+ $.fn.carousel.defaults = {
+ interval: 5000
+ , pause: 'hover'
+ }
+
+ $.fn.carousel.Constructor = Carousel
+
+
+ /* CAROUSEL NO CONFLICT
+ * ==================== */
+
+ $.fn.carousel.noConflict = function () {
+ $.fn.carousel = old
+ return this
+ }
+
+ /* CAROUSEL DATA-API
+ * ================= */
+
+ $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
+ var $this = $(this), href
+ , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ , options = $.extend({}, $target.data(), $this.data())
+ , slideIndex
+
+ $target.carousel(options)
+
+ if (slideIndex = $this.attr('data-slide-to')) {
+ $target.data('carousel').pause().to(slideIndex).cycle()
+ }
+
+ e.preventDefault()
+ })
+
+}(window.jQuery);/* =============================================================
+ * bootstrap-collapse.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#collapse
+ * =============================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* COLLAPSE PUBLIC CLASS DEFINITION
+ * ================================ */
+
+ var Collapse = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, $.fn.collapse.defaults, options)
+
+ if (this.options.parent) {
+ this.$parent = $(this.options.parent)
+ }
+
+ this.options.toggle && this.toggle()
+ }
+
+ Collapse.prototype = {
+
+ constructor: Collapse
+
+ , dimension: function () {
+ var hasWidth = this.$element.hasClass('width')
+ return hasWidth ? 'width' : 'height'
+ }
+
+ , show: function () {
+ var dimension
+ , scroll
+ , actives
+ , hasData
+
+ if (this.transitioning || this.$element.hasClass('in')) return
+
+ dimension = this.dimension()
+ scroll = $.camelCase(['scroll', dimension].join('-'))
+ actives = this.$parent && this.$parent.find('> .accordion-group > .in')
+
+ if (actives && actives.length) {
+ hasData = actives.data('collapse')
+ if (hasData && hasData.transitioning) return
+ actives.collapse('hide')
+ hasData || actives.data('collapse', null)
+ }
+
+ this.$element[dimension](0)
+ this.transition('addClass', $.Event('show'), 'shown')
+ $.support.transition && this.$element[dimension](this.$element[0][scroll])
+ }
+
+ , hide: function () {
+ var dimension
+ if (this.transitioning || !this.$element.hasClass('in')) return
+ dimension = this.dimension()
+ this.reset(this.$element[dimension]())
+ this.transition('removeClass', $.Event('hide'), 'hidden')
+ this.$element[dimension](0)
+ }
+
+ , reset: function (size) {
+ var dimension = this.dimension()
+
+ this.$element
+ .removeClass('collapse')
+ [dimension](size || 'auto')
+ [0].offsetWidth
+
+ this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
+
+ return this
+ }
+
+ , transition: function (method, startEvent, completeEvent) {
+ var that = this
+ , complete = function () {
+ if (startEvent.type == 'show') that.reset()
+ that.transitioning = 0
+ that.$element.trigger(completeEvent)
+ }
+
+ this.$element.trigger(startEvent)
+
+ if (startEvent.isDefaultPrevented()) return
+
+ this.transitioning = 1
+
+ this.$element[method]('in')
+
+ $.support.transition && this.$element.hasClass('collapse') ?
+ this.$element.one($.support.transition.end, complete) :
+ complete()
+ }
+
+ , toggle: function () {
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
+ }
+
+ }
+
+
+ /* COLLAPSE PLUGIN DEFINITION
+ * ========================== */
+
+ var old = $.fn.collapse
+
+ $.fn.collapse = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('collapse')
+ , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option)
+ if (!data) $this.data('collapse', (data = new Collapse(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.collapse.defaults = {
+ toggle: true
+ }
+
+ $.fn.collapse.Constructor = Collapse
+
+
+ /* COLLAPSE NO CONFLICT
+ * ==================== */
+
+ $.fn.collapse.noConflict = function () {
+ $.fn.collapse = old
+ return this
+ }
+
+
+ /* COLLAPSE DATA-API
+ * ================= */
+
+ $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) {
+ var $this = $(this), href
+ , target = $this.attr('data-target')
+ || e.preventDefault()
+ || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
+ , option = $(target).data('collapse') ? 'toggle' : $this.data()
+ $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
+ $(target).collapse(option)
+ })
+
+}(window.jQuery);/* ============================================================
+ * bootstrap-dropdown.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#dropdowns
+ * ============================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* DROPDOWN CLASS DEFINITION
+ * ========================= */
+
+ var toggle = '[data-toggle=dropdown]'
+ , Dropdown = function (element) {
+ var $el = $(element).on('click.dropdown.data-api', this.toggle)
+ $('html').on('click.dropdown.data-api', function () {
+ $el.parent().removeClass('open')
+ })
+ }
+
+ Dropdown.prototype = {
+
+ constructor: Dropdown
+
+ , toggle: function (e) {
+ var $this = $(this)
+ , $parent
+ , isActive
+
+ if ($this.is('.disabled, :disabled')) return
+
+ $parent = getParent($this)
+
+ isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ if ('ontouchstart' in document.documentElement) {
+ // if mobile we we use a backdrop because click events don't delegate
+ $('').insertBefore($(this)).on('click', clearMenus)
+ }
+ $parent.toggleClass('open')
+ }
+
+ $this.focus()
+
+ return false
+ }
+
+ , keydown: function (e) {
+ var $this
+ , $items
+ , $active
+ , $parent
+ , isActive
+ , index
+
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ $parent = getParent($this)
+
+ isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) {
+ if (e.which == 27) $parent.find(toggle).focus()
+ return $this.click()
+ }
+
+ $items = $('[role=menu] li:not(.divider):visible a', $parent)
+
+ if (!$items.length) return
+
+ index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index = 0
+
+ $items
+ .eq(index)
+ .focus()
+ }
+
+ }
+
+ function clearMenus() {
+ $('.dropdown-backdrop').remove()
+ $(toggle).each(function () {
+ getParent($(this)).removeClass('open')
+ })
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+ , $parent
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ $parent = selector && $(selector)
+
+ if (!$parent || !$parent.length) $parent = $this.parent()
+
+ return $parent
+ }
+
+
+ /* DROPDOWN PLUGIN DEFINITION
+ * ========================== */
+
+ var old = $.fn.dropdown
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('dropdown')
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ /* DROPDOWN NO CONFLICT
+ * ==================== */
+
+ $.fn.dropdown.noConflict = function () {
+ $.fn.dropdown = old
+ return this
+ }
+
+
+ /* APPLY TO STANDARD DROPDOWN ELEMENTS
+ * =================================== */
+
+ $(document)
+ .on('click.dropdown.data-api', clearMenus)
+ .on('click.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+ .on('click.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+
+}(window.jQuery);
+/* =========================================================
+ * bootstrap-modal.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#modals
+ * =========================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================= */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* MODAL CLASS DEFINITION
+ * ====================== */
+
+ var Modal = function (element, options) {
+ this.options = options
+ this.$element = $(element)
+ .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
+ this.options.remote && this.$element.find('.modal-body').load(this.options.remote)
+ }
+
+ Modal.prototype = {
+
+ constructor: Modal
+
+ , toggle: function () {
+ return this[!this.isShown ? 'show' : 'hide']()
+ }
+
+ , show: function () {
+ var that = this
+ , e = $.Event('show')
+
+ this.$element.trigger(e)
+
+ if (this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = true
+
+ this.escape()
+
+ this.backdrop(function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
+
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(document.body) //don't move modals dom position
+ }
+
+ that.$element.show()
+
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
+
+ that.$element
+ .addClass('in')
+ .attr('aria-hidden', false)
+
+ that.enforceFocus()
+
+ transition ?
+ that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) :
+ that.$element.focus().trigger('shown')
+
+ })
+ }
+
+ , hide: function (e) {
+ e && e.preventDefault()
+
+ var that = this
+
+ e = $.Event('hide')
+
+ this.$element.trigger(e)
+
+ if (!this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = false
+
+ this.escape()
+
+ $(document).off('focusin.modal')
+
+ this.$element
+ .removeClass('in')
+ .attr('aria-hidden', true)
+
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.hideWithTransition() :
+ this.hideModal()
+ }
+
+ , enforceFocus: function () {
+ var that = this
+ $(document).on('focusin.modal', function (e) {
+ if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
+ that.$element.focus()
+ }
+ })
+ }
+
+ , escape: function () {
+ var that = this
+ if (this.isShown && this.options.keyboard) {
+ this.$element.on('keyup.dismiss.modal', function ( e ) {
+ e.which == 27 && that.hide()
+ })
+ } else if (!this.isShown) {
+ this.$element.off('keyup.dismiss.modal')
+ }
+ }
+
+ , hideWithTransition: function () {
+ var that = this
+ , timeout = setTimeout(function () {
+ that.$element.off($.support.transition.end)
+ that.hideModal()
+ }, 500)
+
+ this.$element.one($.support.transition.end, function () {
+ clearTimeout(timeout)
+ that.hideModal()
+ })
+ }
+
+ , hideModal: function () {
+ var that = this
+ this.$element.hide()
+ this.backdrop(function () {
+ that.removeBackdrop()
+ that.$element.trigger('hidden')
+ })
+ }
+
+ , removeBackdrop: function () {
+ this.$backdrop && this.$backdrop.remove()
+ this.$backdrop = null
+ }
+
+ , backdrop: function (callback) {
+ var that = this
+ , animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
+
+ this.$backdrop = $('')
+ .appendTo(document.body)
+
+ this.$backdrop.click(
+ this.options.backdrop == 'static' ?
+ $.proxy(this.$element[0].focus, this.$element[0])
+ : $.proxy(this.hide, this)
+ )
+
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+ this.$backdrop.addClass('in')
+
+ if (!callback) return
+
+ doAnimate ?
+ this.$backdrop.one($.support.transition.end, callback) :
+ callback()
+
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
+
+ $.support.transition && this.$element.hasClass('fade')?
+ this.$backdrop.one($.support.transition.end, callback) :
+ callback()
+
+ } else if (callback) {
+ callback()
+ }
+ }
+ }
+
+
+ /* MODAL PLUGIN DEFINITION
+ * ======================= */
+
+ var old = $.fn.modal
+
+ $.fn.modal = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('modal')
+ , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
+ if (!data) $this.data('modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option]()
+ else if (options.show) data.show()
+ })
+ }
+
+ $.fn.modal.defaults = {
+ backdrop: true
+ , keyboard: true
+ , show: true
+ }
+
+ $.fn.modal.Constructor = Modal
+
+
+ /* MODAL NO CONFLICT
+ * ================= */
+
+ $.fn.modal.noConflict = function () {
+ $.fn.modal = old
+ return this
+ }
+
+
+ /* MODAL DATA-API
+ * ============== */
+
+ $(document).on('click.modal.data-api', '[data-toggle="modal"]', function (e) {
+ var $this = $(this)
+ , href = $this.attr('href')
+ , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
+ , option = $target.data('modal') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $target.data(), $this.data())
+
+ e.preventDefault()
+
+ $target
+ .modal(option)
+ .one('hide', function () {
+ $this.focus()
+ })
+ })
+
+}(window.jQuery);
+/* ===========================================================
+ * bootstrap-tooltip.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#tooltips
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ===========================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* TOOLTIP PUBLIC CLASS DEFINITION
+ * =============================== */
+
+ var Tooltip = function (element, options) {
+ this.init('tooltip', element, options)
+ }
+
+ Tooltip.prototype = {
+
+ constructor: Tooltip
+
+ , init: function (type, element, options) {
+ var eventIn
+ , eventOut
+ , triggers
+ , trigger
+ , i
+
+ this.type = type
+ this.$element = $(element)
+ this.options = this.getOptions(options)
+ this.enabled = true
+
+ triggers = this.options.trigger.split(' ')
+
+ for (i = triggers.length; i--;) {
+ trigger = triggers[i]
+ if (trigger == 'click') {
+ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+ } else if (trigger != 'manual') {
+ eventIn = trigger == 'hover' ? 'mouseenter' : 'focus'
+ eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
+ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+ }
+ }
+
+ this.options.selector ?
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+ this.fixTitle()
+ }
+
+ , getOptions: function (options) {
+ options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options)
+
+ if (options.delay && typeof options.delay == 'number') {
+ options.delay = {
+ show: options.delay
+ , hide: options.delay
+ }
+ }
+
+ return options
+ }
+
+ , enter: function (e) {
+ var defaults = $.fn[this.type].defaults
+ , options = {}
+ , self
+
+ this._options && $.each(this._options, function (key, value) {
+ if (defaults[key] != value) options[key] = value
+ }, this)
+
+ self = $(e.currentTarget)[this.type](options).data(this.type)
+
+ if (!self.options.delay || !self.options.delay.show) return self.show()
+
+ clearTimeout(this.timeout)
+ self.hoverState = 'in'
+ this.timeout = setTimeout(function() {
+ if (self.hoverState == 'in') self.show()
+ }, self.options.delay.show)
+ }
+
+ , leave: function (e) {
+ var self = $(e.currentTarget)[this.type](this._options).data(this.type)
+
+ if (this.timeout) clearTimeout(this.timeout)
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+ self.hoverState = 'out'
+ this.timeout = setTimeout(function() {
+ if (self.hoverState == 'out') self.hide()
+ }, self.options.delay.hide)
+ }
+
+ , show: function () {
+ var $tip
+ , pos
+ , actualWidth
+ , actualHeight
+ , placement
+ , tp
+ , e = $.Event('show')
+
+ if (this.hasContent() && this.enabled) {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $tip = this.tip()
+ this.setContent()
+
+ if (this.options.animation) {
+ $tip.addClass('fade')
+ }
+
+ placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement
+
+ $tip
+ .detach()
+ .css({ top: 0, left: 0, display: 'block' })
+
+ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
+
+ pos = this.getPosition()
+
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+
+ switch (placement) {
+ case 'bottom':
+ tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
+ break
+ case 'top':
+ tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
+ break
+ case 'left':
+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
+ break
+ case 'right':
+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
+ break
+ }
+
+ this.applyPlacement(tp, placement)
+ this.$element.trigger('shown')
+ }
+ }
+
+ , applyPlacement: function(offset, placement){
+ var $tip = this.tip()
+ , width = $tip[0].offsetWidth
+ , height = $tip[0].offsetHeight
+ , actualWidth
+ , actualHeight
+ , delta
+ , replace
+
+ $tip
+ .offset(offset)
+ .addClass(placement)
+ .addClass('in')
+
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+
+ if (placement == 'top' && actualHeight != height) {
+ offset.top = offset.top + height - actualHeight
+ replace = true
+ }
+
+ if (placement == 'bottom' || placement == 'top') {
+ delta = 0
+
+ if (offset.left < 0){
+ delta = offset.left * -2
+ offset.left = 0
+ $tip.offset(offset)
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+ }
+
+ this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
+ } else {
+ this.replaceArrow(actualHeight - height, actualHeight, 'top')
+ }
+
+ if (replace) $tip.offset(offset)
+ }
+
+ , replaceArrow: function(delta, dimension, position){
+ this
+ .arrow()
+ .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
+ }
+
+ , setContent: function () {
+ var $tip = this.tip()
+ , title = this.getTitle()
+
+ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+ $tip.removeClass('fade in top bottom left right')
+ }
+
+ , hide: function () {
+ var that = this
+ , $tip = this.tip()
+ , e = $.Event('hide')
+
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+
+ $tip.removeClass('in')
+
+ function removeWithAnimation() {
+ var timeout = setTimeout(function () {
+ $tip.off($.support.transition.end).detach()
+ }, 500)
+
+ $tip.one($.support.transition.end, function () {
+ clearTimeout(timeout)
+ $tip.detach()
+ })
+ }
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ removeWithAnimation() :
+ $tip.detach()
+
+ this.$element.trigger('hidden')
+
+ return this
+ }
+
+ , fixTitle: function () {
+ var $e = this.$element
+ if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
+ $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+ }
+ }
+
+ , hasContent: function () {
+ return this.getTitle()
+ }
+
+ , getPosition: function () {
+ var el = this.$element[0]
+ return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
+ width: el.offsetWidth
+ , height: el.offsetHeight
+ }, this.$element.offset())
+ }
+
+ , getTitle: function () {
+ var title
+ , $e = this.$element
+ , o = this.options
+
+ title = $e.attr('data-original-title')
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
+
+ return title
+ }
+
+ , tip: function () {
+ return this.$tip = this.$tip || $(this.options.template)
+ }
+
+ , arrow: function(){
+ return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow")
+ }
+
+ , validate: function () {
+ if (!this.$element[0].parentNode) {
+ this.hide()
+ this.$element = null
+ this.options = null
+ }
+ }
+
+ , enable: function () {
+ this.enabled = true
+ }
+
+ , disable: function () {
+ this.enabled = false
+ }
+
+ , toggleEnabled: function () {
+ this.enabled = !this.enabled
+ }
+
+ , toggle: function (e) {
+ var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this
+ self.tip().hasClass('in') ? self.hide() : self.show()
+ }
+
+ , destroy: function () {
+ this.hide().$element.off('.' + this.type).removeData(this.type)
+ }
+
+ }
+
+
+ /* TOOLTIP PLUGIN DEFINITION
+ * ========================= */
+
+ var old = $.fn.tooltip
+
+ $.fn.tooltip = function ( option ) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('tooltip')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.tooltip.Constructor = Tooltip
+
+ $.fn.tooltip.defaults = {
+ animation: true
+ , placement: 'top'
+ , selector: false
+ , template: '
'
+ , trigger: 'hover focus'
+ , title: ''
+ , delay: 0
+ , html: false
+ , container: false
+ }
+
+
+ /* TOOLTIP NO CONFLICT
+ * =================== */
+
+ $.fn.tooltip.noConflict = function () {
+ $.fn.tooltip = old
+ return this
+ }
+
+}(window.jQuery);
+/* ===========================================================
+ * bootstrap-popover.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#popovers
+ * ===========================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * =========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* POPOVER PUBLIC CLASS DEFINITION
+ * =============================== */
+
+ var Popover = function (element, options) {
+ this.init('popover', element, options)
+ }
+
+
+ /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
+ ========================================== */
+
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
+
+ constructor: Popover
+
+ , setContent: function () {
+ var $tip = this.tip()
+ , title = this.getTitle()
+ , content = this.getContent()
+
+ $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
+ $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
+
+ $tip.removeClass('fade top bottom left right in')
+ }
+
+ , hasContent: function () {
+ return this.getTitle() || this.getContent()
+ }
+
+ , getContent: function () {
+ var content
+ , $e = this.$element
+ , o = this.options
+
+ content = (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
+ || $e.attr('data-content')
+
+ return content
+ }
+
+ , tip: function () {
+ if (!this.$tip) {
+ this.$tip = $(this.options.template)
+ }
+ return this.$tip
+ }
+
+ , destroy: function () {
+ this.hide().$element.off('.' + this.type).removeData(this.type)
+ }
+
+ })
+
+
+ /* POPOVER PLUGIN DEFINITION
+ * ======================= */
+
+ var old = $.fn.popover
+
+ $.fn.popover = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('popover')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('popover', (data = new Popover(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.popover.Constructor = Popover
+
+ $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
+ placement: 'right'
+ , trigger: 'click'
+ , content: ''
+ , template: '
'
+ })
+
+
+ /* POPOVER NO CONFLICT
+ * =================== */
+
+ $.fn.popover.noConflict = function () {
+ $.fn.popover = old
+ return this
+ }
+
+}(window.jQuery);
+/* =============================================================
+ * bootstrap-scrollspy.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#scrollspy
+ * =============================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* SCROLLSPY CLASS DEFINITION
+ * ========================== */
+
+ function ScrollSpy(element, options) {
+ var process = $.proxy(this.process, this)
+ , $element = $(element).is('body') ? $(window) : $(element)
+ , href
+ this.options = $.extend({}, $.fn.scrollspy.defaults, options)
+ this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process)
+ this.selector = (this.options.target
+ || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ || '') + ' .nav li > a'
+ this.$body = $('body')
+ this.refresh()
+ this.process()
+ }
+
+ ScrollSpy.prototype = {
+
+ constructor: ScrollSpy
+
+ , refresh: function () {
+ var self = this
+ , $targets
+
+ this.offsets = $([])
+ this.targets = $([])
+
+ $targets = this.$body
+ .find(this.selector)
+ .map(function () {
+ var $el = $(this)
+ , href = $el.data('target') || $el.attr('href')
+ , $href = /^#\w/.test(href) && $(href)
+ return ( $href
+ && $href.length
+ && [[ $href.position().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]] ) || null
+ })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .each(function () {
+ self.offsets.push(this[0])
+ self.targets.push(this[1])
+ })
+ }
+
+ , process: function () {
+ var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
+ , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
+ , maxScroll = scrollHeight - this.$scrollElement.height()
+ , offsets = this.offsets
+ , targets = this.targets
+ , activeTarget = this.activeTarget
+ , i
+
+ if (scrollTop >= maxScroll) {
+ return activeTarget != (i = targets.last()[0])
+ && this.activate ( i )
+ }
+
+ for (i = offsets.length; i--;) {
+ activeTarget != targets[i]
+ && scrollTop >= offsets[i]
+ && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
+ && this.activate( targets[i] )
+ }
+ }
+
+ , activate: function (target) {
+ var active
+ , selector
+
+ this.activeTarget = target
+
+ $(this.selector)
+ .parent('.active')
+ .removeClass('active')
+
+ selector = this.selector
+ + '[data-target="' + target + '"],'
+ + this.selector + '[href="' + target + '"]'
+
+ active = $(selector)
+ .parent('li')
+ .addClass('active')
+
+ if (active.parent('.dropdown-menu').length) {
+ active = active.closest('li.dropdown').addClass('active')
+ }
+
+ active.trigger('activate')
+ }
+
+ }
+
+
+ /* SCROLLSPY PLUGIN DEFINITION
+ * =========================== */
+
+ var old = $.fn.scrollspy
+
+ $.fn.scrollspy = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('scrollspy')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.scrollspy.Constructor = ScrollSpy
+
+ $.fn.scrollspy.defaults = {
+ offset: 10
+ }
+
+
+ /* SCROLLSPY NO CONFLICT
+ * ===================== */
+
+ $.fn.scrollspy.noConflict = function () {
+ $.fn.scrollspy = old
+ return this
+ }
+
+
+ /* SCROLLSPY DATA-API
+ * ================== */
+
+ $(window).on('load', function () {
+ $('[data-spy="scroll"]').each(function () {
+ var $spy = $(this)
+ $spy.scrollspy($spy.data())
+ })
+ })
+
+}(window.jQuery);/* ========================================================
+ * bootstrap-tab.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#tabs
+ * ========================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* TAB CLASS DEFINITION
+ * ==================== */
+
+ var Tab = function (element) {
+ this.element = $(element)
+ }
+
+ Tab.prototype = {
+
+ constructor: Tab
+
+ , show: function () {
+ var $this = this.element
+ , $ul = $this.closest('ul:not(.dropdown-menu)')
+ , selector = $this.attr('data-target')
+ , previous
+ , $target
+ , e
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ if ( $this.parent('li').hasClass('active') ) return
+
+ previous = $ul.find('.active:last a')[0]
+
+ e = $.Event('show', {
+ relatedTarget: previous
+ })
+
+ $this.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ $target = $(selector)
+
+ this.activate($this.parent('li'), $ul)
+ this.activate($target, $target.parent(), function () {
+ $this.trigger({
+ type: 'shown'
+ , relatedTarget: previous
+ })
+ })
+ }
+
+ , activate: function ( element, container, callback) {
+ var $active = container.find('> .active')
+ , transition = callback
+ && $.support.transition
+ && $active.hasClass('fade')
+
+ function next() {
+ $active
+ .removeClass('active')
+ .find('> .dropdown-menu > .active')
+ .removeClass('active')
+
+ element.addClass('active')
+
+ if (transition) {
+ element[0].offsetWidth // reflow for transition
+ element.addClass('in')
+ } else {
+ element.removeClass('fade')
+ }
+
+ if ( element.parent('.dropdown-menu') ) {
+ element.closest('li.dropdown').addClass('active')
+ }
+
+ callback && callback()
+ }
+
+ transition ?
+ $active.one($.support.transition.end, next) :
+ next()
+
+ $active.removeClass('in')
+ }
+ }
+
+
+ /* TAB PLUGIN DEFINITION
+ * ===================== */
+
+ var old = $.fn.tab
+
+ $.fn.tab = function ( option ) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('tab')
+ if (!data) $this.data('tab', (data = new Tab(this)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.tab.Constructor = Tab
+
+
+ /* TAB NO CONFLICT
+ * =============== */
+
+ $.fn.tab.noConflict = function () {
+ $.fn.tab = old
+ return this
+ }
+
+
+ /* TAB DATA-API
+ * ============ */
+
+ $(document).on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
+ e.preventDefault()
+ $(this).tab('show')
+ })
+
+}(window.jQuery);/* =============================================================
+ * bootstrap-typeahead.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#typeahead
+ * =============================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+
+!function($){
+
+ "use strict"; // jshint ;_;
+
+
+ /* TYPEAHEAD PUBLIC CLASS DEFINITION
+ * ================================= */
+
+ var Typeahead = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, $.fn.typeahead.defaults, options)
+ this.matcher = this.options.matcher || this.matcher
+ this.sorter = this.options.sorter || this.sorter
+ this.highlighter = this.options.highlighter || this.highlighter
+ this.updater = this.options.updater || this.updater
+ this.source = this.options.source
+ this.$menu = $(this.options.menu)
+ this.shown = false
+ this.listen()
+ }
+
+ Typeahead.prototype = {
+
+ constructor: Typeahead
+
+ , select: function () {
+ var val = this.$menu.find('.active').attr('data-value')
+ this.$element
+ .val(this.updater(val))
+ .change()
+ return this.hide()
+ }
+
+ , updater: function (item) {
+ return item
+ }
+
+ , show: function () {
+ var pos = $.extend({}, this.$element.position(), {
+ height: this.$element[0].offsetHeight
+ })
+
+ this.$menu
+ .insertAfter(this.$element)
+ .css({
+ top: pos.top + pos.height
+ , left: pos.left
+ })
+ .show()
+
+ this.shown = true
+ return this
+ }
+
+ , hide: function () {
+ this.$menu.hide()
+ this.shown = false
+ return this
+ }
+
+ , lookup: function (event) {
+ var items
+
+ this.query = this.$element.val()
+
+ if (!this.query || this.query.length < this.options.minLength) {
+ return this.shown ? this.hide() : this
+ }
+
+ items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
+
+ return items ? this.process(items) : this
+ }
+
+ , process: function (items) {
+ var that = this
+
+ items = $.grep(items, function (item) {
+ return that.matcher(item)
+ })
+
+ items = this.sorter(items)
+
+ if (!items.length) {
+ return this.shown ? this.hide() : this
+ }
+
+ return this.render(items.slice(0, this.options.items)).show()
+ }
+
+ , matcher: function (item) {
+ return ~item.toLowerCase().indexOf(this.query.toLowerCase())
+ }
+
+ , sorter: function (items) {
+ var beginswith = []
+ , caseSensitive = []
+ , caseInsensitive = []
+ , item
+
+ while (item = items.shift()) {
+ if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
+ else if (~item.indexOf(this.query)) caseSensitive.push(item)
+ else caseInsensitive.push(item)
+ }
+
+ return beginswith.concat(caseSensitive, caseInsensitive)
+ }
+
+ , highlighter: function (item) {
+ var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
+ return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
+ return '' + match + ''
+ })
+ }
+
+ , render: function (items) {
+ var that = this
+
+ items = $(items).map(function (i, item) {
+ i = $(that.options.item).attr('data-value', item)
+ i.find('a').html(that.highlighter(item))
+ return i[0]
+ })
+
+ items.first().addClass('active')
+ this.$menu.html(items)
+ return this
+ }
+
+ , next: function (event) {
+ var active = this.$menu.find('.active').removeClass('active')
+ , next = active.next()
+
+ if (!next.length) {
+ next = $(this.$menu.find('li')[0])
+ }
+
+ next.addClass('active')
+ }
+
+ , prev: function (event) {
+ var active = this.$menu.find('.active').removeClass('active')
+ , prev = active.prev()
+
+ if (!prev.length) {
+ prev = this.$menu.find('li').last()
+ }
+
+ prev.addClass('active')
+ }
+
+ , listen: function () {
+ this.$element
+ .on('focus', $.proxy(this.focus, this))
+ .on('blur', $.proxy(this.blur, this))
+ .on('keypress', $.proxy(this.keypress, this))
+ .on('keyup', $.proxy(this.keyup, this))
+
+ if (this.eventSupported('keydown')) {
+ this.$element.on('keydown', $.proxy(this.keydown, this))
+ }
+
+ this.$menu
+ .on('click', $.proxy(this.click, this))
+ .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
+ .on('mouseleave', 'li', $.proxy(this.mouseleave, this))
+ }
+
+ , eventSupported: function(eventName) {
+ var isSupported = eventName in this.$element
+ if (!isSupported) {
+ this.$element.setAttribute(eventName, 'return;')
+ isSupported = typeof this.$element[eventName] === 'function'
+ }
+ return isSupported
+ }
+
+ , move: function (e) {
+ if (!this.shown) return
+
+ switch(e.keyCode) {
+ case 9: // tab
+ case 13: // enter
+ case 27: // escape
+ e.preventDefault()
+ break
+
+ case 38: // up arrow
+ e.preventDefault()
+ this.prev()
+ break
+
+ case 40: // down arrow
+ e.preventDefault()
+ this.next()
+ break
+ }
+
+ e.stopPropagation()
+ }
+
+ , keydown: function (e) {
+ this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27])
+ this.move(e)
+ }
+
+ , keypress: function (e) {
+ if (this.suppressKeyPressRepeat) return
+ this.move(e)
+ }
+
+ , keyup: function (e) {
+ switch(e.keyCode) {
+ case 40: // down arrow
+ case 38: // up arrow
+ case 16: // shift
+ case 17: // ctrl
+ case 18: // alt
+ break
+
+ case 9: // tab
+ case 13: // enter
+ if (!this.shown) return
+ this.select()
+ break
+
+ case 27: // escape
+ if (!this.shown) return
+ this.hide()
+ break
+
+ default:
+ this.lookup()
+ }
+
+ e.stopPropagation()
+ e.preventDefault()
+ }
+
+ , focus: function (e) {
+ this.focused = true
+ }
+
+ , blur: function (e) {
+ this.focused = false
+ if (!this.mousedover && this.shown) this.hide()
+ }
+
+ , click: function (e) {
+ e.stopPropagation()
+ e.preventDefault()
+ this.select()
+ this.$element.focus()
+ }
+
+ , mouseenter: function (e) {
+ this.mousedover = true
+ this.$menu.find('.active').removeClass('active')
+ $(e.currentTarget).addClass('active')
+ }
+
+ , mouseleave: function (e) {
+ this.mousedover = false
+ if (!this.focused && this.shown) this.hide()
+ }
+
+ }
+
+
+ /* TYPEAHEAD PLUGIN DEFINITION
+ * =========================== */
+
+ var old = $.fn.typeahead
+
+ $.fn.typeahead = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('typeahead')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.typeahead.defaults = {
+ source: []
+ , items: 8
+ , menu: '
'
+ , item: '
'
+ , minLength: 1
+ }
+
+ $.fn.typeahead.Constructor = Typeahead
+
+
+ /* TYPEAHEAD NO CONFLICT
+ * =================== */
+
+ $.fn.typeahead.noConflict = function () {
+ $.fn.typeahead = old
+ return this
+ }
+
+
+ /* TYPEAHEAD DATA-API
+ * ================== */
+
+ $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
+ var $this = $(this)
+ if ($this.data('typeahead')) return
+ $this.typeahead($this.data())
+ })
+
+}(window.jQuery);
+/* ==========================================================
+ * bootstrap-affix.js v2.3.2
+ * http://getbootstrap.com/2.3.2/javascript.html#affix
+ * ==========================================================
+ * Copyright 2013 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* AFFIX CLASS DEFINITION
+ * ====================== */
+
+ var Affix = function (element, options) {
+ this.options = $.extend({}, $.fn.affix.defaults, options)
+ this.$window = $(window)
+ .on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
+ .on('click.affix.data-api', $.proxy(function () { setTimeout($.proxy(this.checkPosition, this), 1) }, this))
+ this.$element = $(element)
+ this.checkPosition()
+ }
+
+ Affix.prototype.checkPosition = function () {
+ if (!this.$element.is(':visible')) return
+
+ var scrollHeight = $(document).height()
+ , scrollTop = this.$window.scrollTop()
+ , position = this.$element.offset()
+ , offset = this.options.offset
+ , offsetBottom = offset.bottom
+ , offsetTop = offset.top
+ , reset = 'affix affix-top affix-bottom'
+ , affix
+
+ if (typeof offset != 'object') offsetBottom = offsetTop = offset
+ if (typeof offsetTop == 'function') offsetTop = offset.top()
+ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
+
+ affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
+ false : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
+ 'bottom' : offsetTop != null && scrollTop <= offsetTop ?
+ 'top' : false
+
+ if (this.affixed === affix) return
+
+ this.affixed = affix
+ this.unpin = affix == 'bottom' ? position.top - scrollTop : null
+
+ this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
+ }
+
+
+ /* AFFIX PLUGIN DEFINITION
+ * ======================= */
+
+ var old = $.fn.affix
+
+ $.fn.affix = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('affix')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('affix', (data = new Affix(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.affix.Constructor = Affix
+
+ $.fn.affix.defaults = {
+ offset: 0
+ }
+
+
+ /* AFFIX NO CONFLICT
+ * ================= */
+
+ $.fn.affix.noConflict = function () {
+ $.fn.affix = old
+ return this
+ }
+
+
+ /* AFFIX DATA-API
+ * ============== */
+
+ $(window).on('load', function () {
+ $('[data-spy="affix"]').each(function () {
+ var $spy = $(this)
+ , data = $spy.data()
+
+ data.offset = data.offset || {}
+
+ data.offsetBottom && (data.offset.bottom = data.offsetBottom)
+ data.offsetTop && (data.offset.top = data.offsetTop)
+
+ $spy.affix(data)
+ })
+ })
+
+
+}(window.jQuery);
\ No newline at end of file
diff --git a/data/templates/js/code39.js b/data/templates/js/code39.js
new file mode 100644
index 0000000..6028854
--- /dev/null
+++ b/data/templates/js/code39.js
@@ -0,0 +1,148 @@
+var encodings = {
+ '0':'bwbWBwBwb',
+ '1':'BwbWbwbwB',
+ '2':'bwBWbwbwB',
+ '3':'BwBWbwbwb',
+ '4':'bwbWBwbwB',
+ '5':'BwbWBwbwb',
+ '6':'bwBWBwbwb',
+ '7':'bwbWbwBwB',
+ '8':'BwbWbwBwb',
+ '9':'bwBWbwBwb',
+ 'A':'BwbwbWbwB',
+ 'B':'bwBwbWbwB',
+ 'C':'BwBwbWbwb',
+ 'D':'bwbwBWbwB',
+ 'E':'BwbwBWbwb',
+ 'F':'bwBwBWbwb',
+ 'G':'bwbwbWBwB',
+ 'H':'BwbwbWBwb',
+ 'I':'bwBwbWBwb',
+ 'J':'bwbwBWBwb',
+ 'K':'BwbwbwbWB',
+ 'L':'bwBwbwbWB',
+ 'M':'BwBwbwbWb',
+ 'N':'bwbwBwbWB',
+ 'O':'BwbwBwbWb',
+ 'P':'bwBwBwbWb',
+ 'Q':'bwbwbwBWB',
+ 'R':'BwbwbwBWb',
+ 'S':'bwBwbwBWb',
+ 'T':'bwbwBwBWb',
+ 'U':'BWbwbwbwB',
+ 'V':'bWBwbwbwB',
+ 'W':'BWBwbwbwb',
+ 'X':'bWbwBwbwB',
+ 'Y':'BWbwBwbwb',
+ 'Z':'bWBwBwbwb',
+ '-':'bWbwbwBwB',
+ '.':'BWbwbwBwb',
+ ' ':'bWBwbwBwb',
+ '$':'bWbWbWbwb',
+ '/':'bWbWbwbWb',
+ '+':'bWbwbWbWb',
+ '%':'bwbWbWbWb',
+ '*':'bWbwBwBwb'
+}
+
+var height = 100;
+var paintText = true;
+var canvas;
+var ctx;
+
+var code39_init = function() {
+ canvas = $('#barcode')[0];
+ ctx = canvas.getContext("2d");
+}
+
+var code39_checksum = function(barcode) {
+ var charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%";
+ var subtotal = 0;
+ var c;
+
+ for (c in barcode) {
+ subtotal += charset.indexOf(barcode[c]);
+ }
+
+ return charset[subtotal%43];
+}
+
+var code39_draw = function(text, add_checksum) {
+ var showtext = text;
+
+ if(add_checksum) {
+ text += code39_checksum(text);
+ showtext += " ";
+ }
+
+ text = "*" + text + "*";
+ showtext = " " + showtext + " ";
+
+ var txtLength = text.length;
+ var totalWidth = txtLength*15 +txtLength - 1;
+ cwidth = totalWidth+30;
+
+ ctx.clearRect(0,0,canvas.width,canvas.height);
+
+ canvas.style.height = canvas.height = height;
+ canvas.style.width = canvas.width = cwidth ;
+ ctx.fillStyle = "rgb(255,255,255)";
+ ctx.fillRect(0,0,canvas.width,canvas.height);
+
+ var i,j;
+
+ /* Rounding to prevent antialising */
+ var currentx = Math.round(cwidth/2-totalWidth/2), currenty = 20;
+
+ /* wides are 3x width of narrow */
+ var widewidth = 3;
+
+ var barheight = 80;
+
+ if(paintText) {
+ barheight -= 20;
+ }
+
+ for(i=0;i=1){return"rgb("+[G.r,G.g,G.b].join(",")+")"}else{return"rgba("+[G.r,G.g,G.b,G.a].join(",")+")"}};G.normalize=function(){function H(J,K,I){return KI?I:K)}G.r=H(0,parseInt(G.r),255);G.g=H(0,parseInt(G.g),255);G.b=H(0,parseInt(G.b),255);G.a=H(0,G.a,1);return G};G.clone=function(){return B.color.make(G.r,G.b,G.g,G.a)};return G.normalize()};B.color.extract=function(D,C){var E;do{E=D.css(C).toLowerCase();if(E!=""&&E!="transparent"){break}D=D.parent()}while(!B.nodeName(D.get(0),"body"));if(E=="rgba(0, 0, 0, 0)"){E="transparent"}return B.color.parse(E)};B.color.parse=function(F){var E,C=B.color.make;if(E=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10))}if(E=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10),parseFloat(E[4]))}if(E=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55)}if(E=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55,parseFloat(E[4]))}if(E=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(F)){return C(parseInt(E[1],16),parseInt(E[2],16),parseInt(E[3],16))}if(E=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(F)){return C(parseInt(E[1]+E[1],16),parseInt(E[2]+E[2],16),parseInt(E[3]+E[3],16))}var D=B.trim(F).toLowerCase();if(D=="transparent"){return C(255,255,255,0)}else{E=A[D]||[0,0,0];return C(E[0],E[1],E[2])}};var A={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);
+
+// the actual Flot code
+(function($) {
+ function Plot(placeholder, data_, options_, plugins) {
+ // data is on the form:
+ // [ series1, series2 ... ]
+ // where series is either just the data as [ [x1, y1], [x2, y2], ... ]
+ // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... }
+
+ var series = [],
+ options = {
+ // the color theme used for graphs
+ colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
+ legend: {
+ show: true,
+ noColumns: 1, // number of colums in legend table
+ labelFormatter: null, // fn: string -> string
+ labelBoxBorderColor: "#ccc", // border color for the little label boxes
+ container: null, // container (as jQuery object) to put legend in, null means default on top of graph
+ position: "ne", // position of default legend container within plot
+ margin: 5, // distance from grid edge to default legend container within plot
+ backgroundColor: null, // null means auto-detect
+ backgroundOpacity: 0.85 // set to 0 to avoid background
+ },
+ xaxis: {
+ show: null, // null = auto-detect, true = always, false = never
+ position: "bottom", // or "top"
+ mode: null, // null or "time"
+ color: null, // base color, labels, ticks
+ tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)"
+ transform: null, // null or f: number -> number to transform axis
+ inverseTransform: null, // if transform is set, this should be the inverse function
+ min: null, // min. value to show, null means set automatically
+ max: null, // max. value to show, null means set automatically
+ autoscaleMargin: null, // margin in % to add if auto-setting min/max
+ ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
+ tickFormatter: null, // fn: number -> string
+ labelWidth: null, // size of tick labels in pixels
+ labelHeight: null,
+ reserveSpace: null, // whether to reserve space even if axis isn't shown
+ tickLength: null, // size in pixels of ticks, or "full" for whole line
+ alignTicksWithAxis: null, // axis number or null for no sync
+
+ // mode specific options
+ tickDecimals: null, // no. of decimals, null means auto
+ tickSize: null, // number or [number, "unit"]
+ minTickSize: null, // number or [number, "unit"]
+ monthNames: null, // list of names of months
+ timeformat: null, // format string to use
+ twelveHourClock: false // 12 or 24 time in time mode
+ },
+ yaxis: {
+ autoscaleMargin: 0.02,
+ position: "left" // or "right"
+ },
+ xaxes: [],
+ yaxes: [],
+ series: {
+ points: {
+ show: false,
+ radius: 3,
+ lineWidth: 2, // in pixels
+ fill: true,
+ fillColor: "#ffffff",
+ symbol: "circle" // or callback
+ },
+ lines: {
+ // we don't put in show: false so we can see
+ // whether lines were actively disabled
+ lineWidth: 2, // in pixels
+ fill: false,
+ fillColor: null,
+ steps: false
+ },
+ bars: {
+ show: false,
+ lineWidth: 2, // in pixels
+ barWidth: 1, // in units of the x axis
+ fill: true,
+ fillColor: null,
+ align: "left", // or "center"
+ horizontal: false
+ },
+ shadowSize: 3
+ },
+ grid: {
+ show: true,
+ aboveData: false,
+ color: "#545454", // primary color used for outline and labels
+ backgroundColor: null, // null for transparent, else color
+ borderColor: null, // set if different from the grid color
+ tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)"
+ labelMargin: 5, // in pixels
+ axisMargin: 8, // in pixels
+ borderWidth: 2, // in pixels
+ minBorderMargin: null, // in pixels, null means taken from points radius
+ markings: null, // array of ranges or fn: axes -> array of ranges
+ markingsColor: "#f4f4f4",
+ markingsLineWidth: 2,
+ // interactive stuff
+ clickable: false,
+ hoverable: false,
+ autoHighlight: true, // highlight in case mouse is near
+ mouseActiveRadius: 10 // how far the mouse can be away to activate an item
+ },
+ hooks: {}
+ },
+ canvas = null, // the canvas for the plot itself
+ overlay = null, // canvas for interactive stuff on top of plot
+ eventHolder = null, // jQuery object that events should be bound to
+ ctx = null, octx = null,
+ xaxes = [], yaxes = [],
+ plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
+ canvasWidth = 0, canvasHeight = 0,
+ plotWidth = 0, plotHeight = 0,
+ hooks = {
+ processOptions: [],
+ processRawData: [],
+ processDatapoints: [],
+ drawSeries: [],
+ draw: [],
+ bindEvents: [],
+ drawOverlay: [],
+ shutdown: []
+ },
+ plot = this;
+
+ // public functions
+ plot.setData = setData;
+ plot.setupGrid = setupGrid;
+ plot.draw = draw;
+ plot.getPlaceholder = function() { return placeholder; };
+ plot.getCanvas = function() { return canvas; };
+ plot.getPlotOffset = function() { return plotOffset; };
+ plot.width = function () { return plotWidth; };
+ plot.height = function () { return plotHeight; };
+ plot.offset = function () {
+ var o = eventHolder.offset();
+ o.left += plotOffset.left;
+ o.top += plotOffset.top;
+ return o;
+ };
+ plot.getData = function () { return series; };
+ plot.getAxes = function () {
+ var res = {}, i;
+ $.each(xaxes.concat(yaxes), function (_, axis) {
+ if (axis)
+ res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis;
+ });
+ return res;
+ };
+ plot.getXAxes = function () { return xaxes; };
+ plot.getYAxes = function () { return yaxes; };
+ plot.c2p = canvasToAxisCoords;
+ plot.p2c = axisToCanvasCoords;
+ plot.getOptions = function () { return options; };
+ plot.highlight = highlight;
+ plot.unhighlight = unhighlight;
+ plot.triggerRedrawOverlay = triggerRedrawOverlay;
+ plot.pointOffset = function(point) {
+ return {
+ left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left),
+ top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top)
+ };
+ };
+ plot.shutdown = shutdown;
+ plot.resize = function () {
+ getCanvasDimensions();
+ resizeCanvas(canvas);
+ resizeCanvas(overlay);
+ };
+
+ // public attributes
+ plot.hooks = hooks;
+
+ // initialize
+ initPlugins(plot);
+ parseOptions(options_);
+ setupCanvases();
+ setData(data_);
+ setupGrid();
+ draw();
+ bindEvents();
+
+
+ function executeHooks(hook, args) {
+ args = [plot].concat(args);
+ for (var i = 0; i < hook.length; ++i)
+ hook[i].apply(this, args);
+ }
+
+ function initPlugins() {
+ for (var i = 0; i < plugins.length; ++i) {
+ var p = plugins[i];
+ p.init(plot);
+ if (p.options)
+ $.extend(true, options, p.options);
+ }
+ }
+
+ function parseOptions(opts) {
+ var i;
+
+ $.extend(true, options, opts);
+
+ if (options.xaxis.color == null)
+ options.xaxis.color = options.grid.color;
+ if (options.yaxis.color == null)
+ options.yaxis.color = options.grid.color;
+
+ if (options.xaxis.tickColor == null) // backwards-compatibility
+ options.xaxis.tickColor = options.grid.tickColor;
+ if (options.yaxis.tickColor == null) // backwards-compatibility
+ options.yaxis.tickColor = options.grid.tickColor;
+
+ if (options.grid.borderColor == null)
+ options.grid.borderColor = options.grid.color;
+ if (options.grid.tickColor == null)
+ options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString();
+
+ // fill in defaults in axes, copy at least always the
+ // first as the rest of the code assumes it'll be there
+ for (i = 0; i < Math.max(1, options.xaxes.length); ++i)
+ options.xaxes[i] = $.extend(true, {}, options.xaxis, options.xaxes[i]);
+ for (i = 0; i < Math.max(1, options.yaxes.length); ++i)
+ options.yaxes[i] = $.extend(true, {}, options.yaxis, options.yaxes[i]);
+
+ // backwards compatibility, to be removed in future
+ if (options.xaxis.noTicks && options.xaxis.ticks == null)
+ options.xaxis.ticks = options.xaxis.noTicks;
+ if (options.yaxis.noTicks && options.yaxis.ticks == null)
+ options.yaxis.ticks = options.yaxis.noTicks;
+ if (options.x2axis) {
+ options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis);
+ options.xaxes[1].position = "top";
+ }
+ if (options.y2axis) {
+ options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis);
+ options.yaxes[1].position = "right";
+ }
+ if (options.grid.coloredAreas)
+ options.grid.markings = options.grid.coloredAreas;
+ if (options.grid.coloredAreasColor)
+ options.grid.markingsColor = options.grid.coloredAreasColor;
+ if (options.lines)
+ $.extend(true, options.series.lines, options.lines);
+ if (options.points)
+ $.extend(true, options.series.points, options.points);
+ if (options.bars)
+ $.extend(true, options.series.bars, options.bars);
+ if (options.shadowSize != null)
+ options.series.shadowSize = options.shadowSize;
+
+ // save options on axes for future reference
+ for (i = 0; i < options.xaxes.length; ++i)
+ getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i];
+ for (i = 0; i < options.yaxes.length; ++i)
+ getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i];
+
+ // add hooks from options
+ for (var n in hooks)
+ if (options.hooks[n] && options.hooks[n].length)
+ hooks[n] = hooks[n].concat(options.hooks[n]);
+
+ executeHooks(hooks.processOptions, [options]);
+ }
+
+ function setData(d) {
+ series = parseData(d);
+ fillInSeriesOptions();
+ processData();
+ }
+
+ function parseData(d) {
+ var res = [];
+ for (var i = 0; i < d.length; ++i) {
+ var s = $.extend(true, {}, options.series);
+
+ if (d[i].data != null) {
+ s.data = d[i].data; // move the data instead of deep-copy
+ delete d[i].data;
+
+ $.extend(true, s, d[i]);
+
+ d[i].data = s.data;
+ }
+ else
+ s.data = d[i];
+ res.push(s);
+ }
+
+ return res;
+ }
+
+ function axisNumber(obj, coord) {
+ var a = obj[coord + "axis"];
+ if (typeof a == "object") // if we got a real axis, extract number
+ a = a.n;
+ if (typeof a != "number")
+ a = 1; // default to first axis
+ return a;
+ }
+
+ function allAxes() {
+ // return flat array without annoying null entries
+ return $.grep(xaxes.concat(yaxes), function (a) { return a; });
+ }
+
+ function canvasToAxisCoords(pos) {
+ // return an object with x/y corresponding to all used axes
+ var res = {}, i, axis;
+ for (i = 0; i < xaxes.length; ++i) {
+ axis = xaxes[i];
+ if (axis && axis.used)
+ res["x" + axis.n] = axis.c2p(pos.left);
+ }
+
+ for (i = 0; i < yaxes.length; ++i) {
+ axis = yaxes[i];
+ if (axis && axis.used)
+ res["y" + axis.n] = axis.c2p(pos.top);
+ }
+
+ if (res.x1 !== undefined)
+ res.x = res.x1;
+ if (res.y1 !== undefined)
+ res.y = res.y1;
+
+ return res;
+ }
+
+ function axisToCanvasCoords(pos) {
+ // get canvas coords from the first pair of x/y found in pos
+ var res = {}, i, axis, key;
+
+ for (i = 0; i < xaxes.length; ++i) {
+ axis = xaxes[i];
+ if (axis && axis.used) {
+ key = "x" + axis.n;
+ if (pos[key] == null && axis.n == 1)
+ key = "x";
+
+ if (pos[key] != null) {
+ res.left = axis.p2c(pos[key]);
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < yaxes.length; ++i) {
+ axis = yaxes[i];
+ if (axis && axis.used) {
+ key = "y" + axis.n;
+ if (pos[key] == null && axis.n == 1)
+ key = "y";
+
+ if (pos[key] != null) {
+ res.top = axis.p2c(pos[key]);
+ break;
+ }
+ }
+ }
+
+ return res;
+ }
+
+ function getOrCreateAxis(axes, number) {
+ if (!axes[number - 1])
+ axes[number - 1] = {
+ n: number, // save the number for future reference
+ direction: axes == xaxes ? "x" : "y",
+ options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis)
+ };
+
+ return axes[number - 1];
+ }
+
+ function fillInSeriesOptions() {
+ var i;
+
+ // collect what we already got of colors
+ var neededColors = series.length,
+ usedColors = [],
+ assignedColors = [];
+ for (i = 0; i < series.length; ++i) {
+ var sc = series[i].color;
+ if (sc != null) {
+ --neededColors;
+ if (typeof sc == "number")
+ assignedColors.push(sc);
+ else
+ usedColors.push($.color.parse(series[i].color));
+ }
+ }
+
+ // we might need to generate more colors if higher indices
+ // are assigned
+ for (i = 0; i < assignedColors.length; ++i) {
+ neededColors = Math.max(neededColors, assignedColors[i] + 1);
+ }
+
+ // produce colors as needed
+ var colors = [], variation = 0;
+ i = 0;
+ while (colors.length < neededColors) {
+ var c;
+ if (options.colors.length == i) // check degenerate case
+ c = $.color.make(100, 100, 100);
+ else
+ c = $.color.parse(options.colors[i]);
+
+ // vary color if needed
+ var sign = variation % 2 == 1 ? -1 : 1;
+ c.scale('rgb', 1 + sign * Math.ceil(variation / 2) * 0.2)
+
+ // FIXME: if we're getting to close to something else,
+ // we should probably skip this one
+ colors.push(c);
+
+ ++i;
+ if (i >= options.colors.length) {
+ i = 0;
+ ++variation;
+ }
+ }
+
+ // fill in the options
+ var colori = 0, s;
+ for (i = 0; i < series.length; ++i) {
+ s = series[i];
+
+ // assign colors
+ if (s.color == null) {
+ s.color = colors[colori].toString();
+ ++colori;
+ }
+ else if (typeof s.color == "number")
+ s.color = colors[s.color].toString();
+
+ // turn on lines automatically in case nothing is set
+ if (s.lines.show == null) {
+ var v, show = true;
+ for (v in s)
+ if (s[v] && s[v].show) {
+ show = false;
+ break;
+ }
+ if (show)
+ s.lines.show = true;
+ }
+
+ // setup axes
+ s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x"));
+ s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y"));
+ }
+ }
+
+ function processData() {
+ var topSentry = Number.POSITIVE_INFINITY,
+ bottomSentry = Number.NEGATIVE_INFINITY,
+ fakeInfinity = Number.MAX_VALUE,
+ i, j, k, m, length,
+ s, points, ps, x, y, axis, val, f, p;
+
+ function updateAxis(axis, min, max) {
+ if (min < axis.datamin && min != -fakeInfinity)
+ axis.datamin = min;
+ if (max > axis.datamax && max != fakeInfinity)
+ axis.datamax = max;
+ }
+
+ $.each(allAxes(), function (_, axis) {
+ // init axis
+ axis.datamin = topSentry;
+ axis.datamax = bottomSentry;
+ axis.used = false;
+ });
+
+ for (i = 0; i < series.length; ++i) {
+ s = series[i];
+ s.datapoints = { points: [] };
+
+ executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]);
+ }
+
+ // first pass: clean and copy data
+ for (i = 0; i < series.length; ++i) {
+ s = series[i];
+
+ var data = s.data, format = s.datapoints.format;
+
+ if (!format) {
+ format = [];
+ // find out how to copy
+ format.push({ x: true, number: true, required: true });
+ format.push({ y: true, number: true, required: true });
+
+ if (s.bars.show || (s.lines.show && s.lines.fill)) {
+ format.push({ y: true, number: true, required: false, defaultValue: 0 });
+ if (s.bars.horizontal) {
+ delete format[format.length - 1].y;
+ format[format.length - 1].x = true;
+ }
+ }
+
+ s.datapoints.format = format;
+ }
+
+ if (s.datapoints.pointsize != null)
+ continue; // already filled in
+
+ s.datapoints.pointsize = format.length;
+
+ ps = s.datapoints.pointsize;
+ points = s.datapoints.points;
+
+ insertSteps = s.lines.show && s.lines.steps;
+ s.xaxis.used = s.yaxis.used = true;
+
+ for (j = k = 0; j < data.length; ++j, k += ps) {
+ p = data[j];
+
+ var nullify = p == null;
+ if (!nullify) {
+ for (m = 0; m < ps; ++m) {
+ val = p[m];
+ f = format[m];
+
+ if (f) {
+ if (f.number && val != null) {
+ val = +val; // convert to number
+ if (isNaN(val))
+ val = null;
+ else if (val == Infinity)
+ val = fakeInfinity;
+ else if (val == -Infinity)
+ val = -fakeInfinity;
+ }
+
+ if (val == null) {
+ if (f.required)
+ nullify = true;
+
+ if (f.defaultValue != null)
+ val = f.defaultValue;
+ }
+ }
+
+ points[k + m] = val;
+ }
+ }
+
+ if (nullify) {
+ for (m = 0; m < ps; ++m) {
+ val = points[k + m];
+ if (val != null) {
+ f = format[m];
+ // extract min/max info
+ if (f.x)
+ updateAxis(s.xaxis, val, val);
+ if (f.y)
+ updateAxis(s.yaxis, val, val);
+ }
+ points[k + m] = null;
+ }
+ }
+ else {
+ // a little bit of line specific stuff that
+ // perhaps shouldn't be here, but lacking
+ // better means...
+ if (insertSteps && k > 0
+ && points[k - ps] != null
+ && points[k - ps] != points[k]
+ && points[k - ps + 1] != points[k + 1]) {
+ // copy the point to make room for a middle point
+ for (m = 0; m < ps; ++m)
+ points[k + ps + m] = points[k + m];
+
+ // middle point has same y
+ points[k + 1] = points[k - ps + 1];
+
+ // we've added a point, better reflect that
+ k += ps;
+ }
+ }
+ }
+ }
+
+ // give the hooks a chance to run
+ for (i = 0; i < series.length; ++i) {
+ s = series[i];
+
+ executeHooks(hooks.processDatapoints, [ s, s.datapoints]);
+ }
+
+ // second pass: find datamax/datamin for auto-scaling
+ for (i = 0; i < series.length; ++i) {
+ s = series[i];
+ points = s.datapoints.points,
+ ps = s.datapoints.pointsize;
+
+ var xmin = topSentry, ymin = topSentry,
+ xmax = bottomSentry, ymax = bottomSentry;
+
+ for (j = 0; j < points.length; j += ps) {
+ if (points[j] == null)
+ continue;
+
+ for (m = 0; m < ps; ++m) {
+ val = points[j + m];
+ f = format[m];
+ if (!f || val == fakeInfinity || val == -fakeInfinity)
+ continue;
+
+ if (f.x) {
+ if (val < xmin)
+ xmin = val;
+ if (val > xmax)
+ xmax = val;
+ }
+ if (f.y) {
+ if (val < ymin)
+ ymin = val;
+ if (val > ymax)
+ ymax = val;
+ }
+ }
+ }
+
+ if (s.bars.show) {
+ // make sure we got room for the bar on the dancing floor
+ var delta = s.bars.align == "left" ? 0 : -s.bars.barWidth/2;
+ if (s.bars.horizontal) {
+ ymin += delta;
+ ymax += delta + s.bars.barWidth;
+ }
+ else {
+ xmin += delta;
+ xmax += delta + s.bars.barWidth;
+ }
+ }
+
+ updateAxis(s.xaxis, xmin, xmax);
+ updateAxis(s.yaxis, ymin, ymax);
+ }
+
+ $.each(allAxes(), function (_, axis) {
+ if (axis.datamin == topSentry)
+ axis.datamin = null;
+ if (axis.datamax == bottomSentry)
+ axis.datamax = null;
+ });
+ }
+
+ function makeCanvas(skipPositioning, cls) {
+ var c = document.createElement('canvas');
+ c.className = cls;
+ c.width = canvasWidth;
+ c.height = canvasHeight;
+
+ if (!skipPositioning)
+ $(c).css({ position: 'absolute', left: 0, top: 0 });
+
+ $(c).appendTo(placeholder);
+
+ if (!c.getContext) // excanvas hack
+ c = window.G_vmlCanvasManager.initElement(c);
+
+ // used for resetting in case we get replotted
+ c.getContext("2d").save();
+
+ return c;
+ }
+
+ function getCanvasDimensions() {
+ canvasWidth = placeholder.width();
+ canvasHeight = placeholder.height();
+
+ if (canvasWidth <= 0 || canvasHeight <= 0)
+ throw "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight;
+ }
+
+ function resizeCanvas(c) {
+ // resizing should reset the state (excanvas seems to be
+ // buggy though)
+ if (c.width != canvasWidth)
+ c.width = canvasWidth;
+
+ if (c.height != canvasHeight)
+ c.height = canvasHeight;
+
+ // so try to get back to the initial state (even if it's
+ // gone now, this should be safe according to the spec)
+ var cctx = c.getContext("2d");
+ cctx.restore();
+
+ // and save again
+ cctx.save();
+ }
+
+ function setupCanvases() {
+ var reused,
+ existingCanvas = placeholder.children("canvas.base"),
+ existingOverlay = placeholder.children("canvas.overlay");
+
+ if (existingCanvas.length == 0 || existingOverlay == 0) {
+ // init everything
+
+ placeholder.html(""); // make sure placeholder is clear
+
+ placeholder.css({ padding: 0 }); // padding messes up the positioning
+
+ if (placeholder.css("position") == 'static')
+ placeholder.css("position", "relative"); // for positioning labels and overlay
+
+ getCanvasDimensions();
+
+ canvas = makeCanvas(true, "base");
+ overlay = makeCanvas(false, "overlay"); // overlay canvas for interactive features
+
+ reused = false;
+ }
+ else {
+ // reuse existing elements
+
+ canvas = existingCanvas.get(0);
+ overlay = existingOverlay.get(0);
+
+ reused = true;
+ }
+
+ ctx = canvas.getContext("2d");
+ octx = overlay.getContext("2d");
+
+ // we include the canvas in the event holder too, because IE 7
+ // sometimes has trouble with the stacking order
+ eventHolder = $([overlay, canvas]);
+
+ if (reused) {
+ // run shutdown in the old plot object
+ placeholder.data("plot").shutdown();
+
+ // reset reused canvases
+ plot.resize();
+
+ // make sure overlay pixels are cleared (canvas is cleared when we redraw)
+ octx.clearRect(0, 0, canvasWidth, canvasHeight);
+
+ // then whack any remaining obvious garbage left
+ eventHolder.unbind();
+ placeholder.children().not([canvas, overlay]).remove();
+ }
+
+ // save in case we get replotted
+ placeholder.data("plot", plot);
+ }
+
+ function bindEvents() {
+ // bind events
+ if (options.grid.hoverable) {
+ eventHolder.mousemove(onMouseMove);
+ eventHolder.mouseleave(onMouseLeave);
+ }
+
+ if (options.grid.clickable)
+ eventHolder.click(onClick);
+
+ executeHooks(hooks.bindEvents, [eventHolder]);
+ }
+
+ function shutdown() {
+ if (redrawTimeout)
+ clearTimeout(redrawTimeout);
+
+ eventHolder.unbind("mousemove", onMouseMove);
+ eventHolder.unbind("mouseleave", onMouseLeave);
+ eventHolder.unbind("click", onClick);
+
+ executeHooks(hooks.shutdown, [eventHolder]);
+ }
+
+ function setTransformationHelpers(axis) {
+ // set helper functions on the axis, assumes plot area
+ // has been computed already
+
+ function identity(x) { return x; }
+
+ var s, m, t = axis.options.transform || identity,
+ it = axis.options.inverseTransform;
+
+ // precompute how much the axis is scaling a point
+ // in canvas space
+ if (axis.direction == "x") {
+ s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min));
+ m = Math.min(t(axis.max), t(axis.min));
+ }
+ else {
+ s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min));
+ s = -s;
+ m = Math.max(t(axis.max), t(axis.min));
+ }
+
+ // data point to canvas coordinate
+ if (t == identity) // slight optimization
+ axis.p2c = function (p) { return (p - m) * s; };
+ else
+ axis.p2c = function (p) { return (t(p) - m) * s; };
+ // canvas coordinate to data point
+ if (!it)
+ axis.c2p = function (c) { return m + c / s; };
+ else
+ axis.c2p = function (c) { return it(m + c / s); };
+ }
+
+ function measureTickLabels(axis) {
+ var opts = axis.options, i, ticks = axis.ticks || [], labels = [],
+ l, w = opts.labelWidth, h = opts.labelHeight, dummyDiv;
+
+ function makeDummyDiv(labels, width) {
+ return $('
' +
+ '
'
+ + labels.join("") + '
')
+ .appendTo(placeholder);
+ }
+
+ if (axis.direction == "x") {
+ // to avoid measuring the widths of the labels (it's slow), we
+ // construct fixed-size boxes and put the labels inside
+ // them, we don't need the exact figures and the
+ // fixed-size box content is easy to center
+ if (w == null)
+ w = Math.floor(canvasWidth / (ticks.length > 0 ? ticks.length : 1));
+
+ // measure x label heights
+ if (h == null) {
+ labels = [];
+ for (i = 0; i < ticks.length; ++i) {
+ l = ticks[i].label;
+ if (l)
+ labels.push('
' + l + '
');
+ }
+
+ if (labels.length > 0) {
+ // stick them all in the same div and measure
+ // collective height
+ labels.push('');
+ dummyDiv = makeDummyDiv(labels, "width:10000px;");
+ h = dummyDiv.height();
+ dummyDiv.remove();
+ }
+ }
+ }
+ else if (w == null || h == null) {
+ // calculate y label dimensions
+ for (i = 0; i < ticks.length; ++i) {
+ l = ticks[i].label;
+ if (l)
+ labels.push('
' + l + '
');
+ }
+
+ if (labels.length > 0) {
+ dummyDiv = makeDummyDiv(labels, "");
+ if (w == null)
+ w = dummyDiv.children().width();
+ if (h == null)
+ h = dummyDiv.find("div.tickLabel").height();
+ dummyDiv.remove();
+ }
+ }
+
+ if (w == null)
+ w = 0;
+ if (h == null)
+ h = 0;
+
+ axis.labelWidth = w;
+ axis.labelHeight = h;
+ }
+
+ function allocateAxisBoxFirstPhase(axis) {
+ // find the bounding box of the axis by looking at label
+ // widths/heights and ticks, make room by diminishing the
+ // plotOffset
+
+ var lw = axis.labelWidth,
+ lh = axis.labelHeight,
+ pos = axis.options.position,
+ tickLength = axis.options.tickLength,
+ axismargin = options.grid.axisMargin,
+ padding = options.grid.labelMargin,
+ all = axis.direction == "x" ? xaxes : yaxes,
+ index;
+
+ // determine axis margin
+ var samePosition = $.grep(all, function (a) {
+ return a && a.options.position == pos && a.reserveSpace;
+ });
+ if ($.inArray(axis, samePosition) == samePosition.length - 1)
+ axismargin = 0; // outermost
+
+ // determine tick length - if we're innermost, we can use "full"
+ if (tickLength == null)
+ tickLength = "full";
+
+ var sameDirection = $.grep(all, function (a) {
+ return a && a.reserveSpace;
+ });
+
+ var innermost = $.inArray(axis, sameDirection) == 0;
+ if (!innermost && tickLength == "full")
+ tickLength = 5;
+
+ if (!isNaN(+tickLength))
+ padding += +tickLength;
+
+ // compute box
+ if (axis.direction == "x") {
+ lh += padding;
+
+ if (pos == "bottom") {
+ plotOffset.bottom += lh + axismargin;
+ axis.box = { top: canvasHeight - plotOffset.bottom, height: lh };
+ }
+ else {
+ axis.box = { top: plotOffset.top + axismargin, height: lh };
+ plotOffset.top += lh + axismargin;
+ }
+ }
+ else {
+ lw += padding;
+
+ if (pos == "left") {
+ axis.box = { left: plotOffset.left + axismargin, width: lw };
+ plotOffset.left += lw + axismargin;
+ }
+ else {
+ plotOffset.right += lw + axismargin;
+ axis.box = { left: canvasWidth - plotOffset.right, width: lw };
+ }
+ }
+
+ // save for future reference
+ axis.position = pos;
+ axis.tickLength = tickLength;
+ axis.box.padding = padding;
+ axis.innermost = innermost;
+ }
+
+ function allocateAxisBoxSecondPhase(axis) {
+ // set remaining bounding box coordinates
+ if (axis.direction == "x") {
+ axis.box.left = plotOffset.left;
+ axis.box.width = plotWidth;
+ }
+ else {
+ axis.box.top = plotOffset.top;
+ axis.box.height = plotHeight;
+ }
+ }
+
+ function setupGrid() {
+ var i, axes = allAxes();
+
+ // first calculate the plot and axis box dimensions
+
+ $.each(axes, function (_, axis) {
+ axis.show = axis.options.show;
+ if (axis.show == null)
+ axis.show = axis.used; // by default an axis is visible if it's got data
+
+ axis.reserveSpace = axis.show || axis.options.reserveSpace;
+
+ setRange(axis);
+ });
+
+ allocatedAxes = $.grep(axes, function (axis) { return axis.reserveSpace; });
+
+ plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = 0;
+ if (options.grid.show) {
+ $.each(allocatedAxes, function (_, axis) {
+ // make the ticks
+ setupTickGeneration(axis);
+ setTicks(axis);
+ snapRangeToTicks(axis, axis.ticks);
+
+ // find labelWidth/Height for axis
+ measureTickLabels(axis);
+ });
+
+ // with all dimensions in house, we can compute the
+ // axis boxes, start from the outside (reverse order)
+ for (i = allocatedAxes.length - 1; i >= 0; --i)
+ allocateAxisBoxFirstPhase(allocatedAxes[i]);
+
+ // make sure we've got enough space for things that
+ // might stick out
+ var minMargin = options.grid.minBorderMargin;
+ if (minMargin == null) {
+ minMargin = 0;
+ for (i = 0; i < series.length; ++i)
+ minMargin = Math.max(minMargin, series[i].points.radius + series[i].points.lineWidth/2);
+ }
+
+ for (var a in plotOffset) {
+ plotOffset[a] += options.grid.borderWidth;
+ plotOffset[a] = Math.max(minMargin, plotOffset[a]);
+ }
+ }
+
+ plotWidth = canvasWidth - plotOffset.left - plotOffset.right;
+ plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top;
+
+ // now we got the proper plotWidth/Height, we can compute the scaling
+ $.each(axes, function (_, axis) {
+ setTransformationHelpers(axis);
+ });
+
+ if (options.grid.show) {
+ $.each(allocatedAxes, function (_, axis) {
+ allocateAxisBoxSecondPhase(axis);
+ });
+
+ insertAxisLabels();
+ }
+
+ insertLegend();
+ }
+
+ function setRange(axis) {
+ var opts = axis.options,
+ min = +(opts.min != null ? opts.min : axis.datamin),
+ max = +(opts.max != null ? opts.max : axis.datamax),
+ delta = max - min;
+
+ if (delta == 0.0) {
+ // degenerate case
+ var widen = max == 0 ? 1 : 0.01;
+
+ if (opts.min == null)
+ min -= widen;
+ // always widen max if we couldn't widen min to ensure we
+ // don't fall into min == max which doesn't work
+ if (opts.max == null || opts.min != null)
+ max += widen;
+ }
+ else {
+ // consider autoscaling
+ var margin = opts.autoscaleMargin;
+ if (margin != null) {
+ if (opts.min == null) {
+ min -= delta * margin;
+ // make sure we don't go below zero if all values
+ // are positive
+ if (min < 0 && axis.datamin != null && axis.datamin >= 0)
+ min = 0;
+ }
+ if (opts.max == null) {
+ max += delta * margin;
+ if (max > 0 && axis.datamax != null && axis.datamax <= 0)
+ max = 0;
+ }
+ }
+ }
+ axis.min = min;
+ axis.max = max;
+ }
+
+ function setupTickGeneration(axis) {
+ var opts = axis.options;
+
+ // estimate number of ticks
+ var noTicks;
+ if (typeof opts.ticks == "number" && opts.ticks > 0)
+ noTicks = opts.ticks;
+ else
+ // heuristic based on the model a*sqrt(x) fitted to
+ // some data points that seemed reasonable
+ noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? canvasWidth : canvasHeight);
+
+ var delta = (axis.max - axis.min) / noTicks,
+ size, generator, unit, formatter, i, magn, norm;
+
+ if (opts.mode == "time") {
+ // pretty handling of time
+
+ // map of app. size of time units in milliseconds
+ var timeUnitSize = {
+ "second": 1000,
+ "minute": 60 * 1000,
+ "hour": 60 * 60 * 1000,
+ "day": 24 * 60 * 60 * 1000,
+ "month": 30 * 24 * 60 * 60 * 1000,
+ "year": 365.2425 * 24 * 60 * 60 * 1000
+ };
+
+
+ // the allowed tick sizes, after 1 year we use
+ // an integer algorithm
+ var spec = [
+ [1, "second"], [2, "second"], [5, "second"], [10, "second"],
+ [30, "second"],
+ [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
+ [30, "minute"],
+ [1, "hour"], [2, "hour"], [4, "hour"],
+ [8, "hour"], [12, "hour"],
+ [1, "day"], [2, "day"], [3, "day"],
+ [0.25, "month"], [0.5, "month"], [1, "month"],
+ [2, "month"], [3, "month"], [6, "month"],
+ [1, "year"]
+ ];
+
+ var minSize = 0;
+ if (opts.minTickSize != null) {
+ if (typeof opts.tickSize == "number")
+ minSize = opts.tickSize;
+ else
+ minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
+ }
+
+ for (var i = 0; i < spec.length - 1; ++i)
+ if (delta < (spec[i][0] * timeUnitSize[spec[i][1]]
+ + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
+ && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize)
+ break;
+ size = spec[i][0];
+ unit = spec[i][1];
+
+ // special-case the possibility of several years
+ if (unit == "year") {
+ magn = Math.pow(10, Math.floor(Math.log(delta / timeUnitSize.year) / Math.LN10));
+ norm = (delta / timeUnitSize.year) / magn;
+ if (norm < 1.5)
+ size = 1;
+ else if (norm < 3)
+ size = 2;
+ else if (norm < 7.5)
+ size = 5;
+ else
+ size = 10;
+
+ size *= magn;
+ }
+
+ axis.tickSize = opts.tickSize || [size, unit];
+
+ generator = function(axis) {
+ var ticks = [],
+ tickSize = axis.tickSize[0], unit = axis.tickSize[1],
+ d = new Date(axis.min);
+
+ var step = tickSize * timeUnitSize[unit];
+
+ if (unit == "second")
+ d.setUTCSeconds(floorInBase(d.getUTCSeconds(), tickSize));
+ if (unit == "minute")
+ d.setUTCMinutes(floorInBase(d.getUTCMinutes(), tickSize));
+ if (unit == "hour")
+ d.setUTCHours(floorInBase(d.getUTCHours(), tickSize));
+ if (unit == "month")
+ d.setUTCMonth(floorInBase(d.getUTCMonth(), tickSize));
+ if (unit == "year")
+ d.setUTCFullYear(floorInBase(d.getUTCFullYear(), tickSize));
+
+ // reset smaller components
+ d.setUTCMilliseconds(0);
+ if (step >= timeUnitSize.minute)
+ d.setUTCSeconds(0);
+ if (step >= timeUnitSize.hour)
+ d.setUTCMinutes(0);
+ if (step >= timeUnitSize.day)
+ d.setUTCHours(0);
+ if (step >= timeUnitSize.day * 4)
+ d.setUTCDate(1);
+ if (step >= timeUnitSize.year)
+ d.setUTCMonth(0);
+
+
+ var carry = 0, v = Number.NaN, prev;
+ do {
+ prev = v;
+ v = d.getTime();
+ ticks.push(v);
+ if (unit == "month") {
+ if (tickSize < 1) {
+ // a bit complicated - we'll divide the month
+ // up but we need to take care of fractions
+ // so we don't end up in the middle of a day
+ d.setUTCDate(1);
+ var start = d.getTime();
+ d.setUTCMonth(d.getUTCMonth() + 1);
+ var end = d.getTime();
+ d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
+ carry = d.getUTCHours();
+ d.setUTCHours(0);
+ }
+ else
+ d.setUTCMonth(d.getUTCMonth() + tickSize);
+ }
+ else if (unit == "year") {
+ d.setUTCFullYear(d.getUTCFullYear() + tickSize);
+ }
+ else
+ d.setTime(v + step);
+ } while (v < axis.max && v != prev);
+
+ return ticks;
+ };
+
+ formatter = function (v, axis) {
+ var d = new Date(v);
+
+ // first check global format
+ if (opts.timeformat != null)
+ return $.plot.formatDate(d, opts.timeformat, opts.monthNames);
+
+ var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
+ var span = axis.max - axis.min;
+ var suffix = (opts.twelveHourClock) ? " %p" : "";
+
+ if (t < timeUnitSize.minute)
+ fmt = "%h:%M:%S" + suffix;
+ else if (t < timeUnitSize.day) {
+ if (span < 2 * timeUnitSize.day)
+ fmt = "%h:%M" + suffix;
+ else
+ fmt = "%b %d %h:%M" + suffix;
+ }
+ else if (t < timeUnitSize.month)
+ fmt = "%b %d";
+ else if (t < timeUnitSize.year) {
+ if (span < timeUnitSize.year)
+ fmt = "%b";
+ else
+ fmt = "%b %y";
+ }
+ else
+ fmt = "%y";
+
+ return $.plot.formatDate(d, fmt, opts.monthNames);
+ };
+ }
+ else {
+ // pretty rounding of base-10 numbers
+ var maxDec = opts.tickDecimals;
+ var dec = -Math.floor(Math.log(delta) / Math.LN10);
+ if (maxDec != null && dec > maxDec)
+ dec = maxDec;
+
+ magn = Math.pow(10, -dec);
+ norm = delta / magn; // norm is between 1.0 and 10.0
+
+ if (norm < 1.5)
+ size = 1;
+ else if (norm < 3) {
+ size = 2;
+ // special case for 2.5, requires an extra decimal
+ if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
+ size = 2.5;
+ ++dec;
+ }
+ }
+ else if (norm < 7.5)
+ size = 5;
+ else
+ size = 10;
+
+ size *= magn;
+
+ if (opts.minTickSize != null && size < opts.minTickSize)
+ size = opts.minTickSize;
+
+ axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
+ axis.tickSize = opts.tickSize || size;
+
+ generator = function (axis) {
+ var ticks = [];
+
+ // spew out all possible ticks
+ var start = floorInBase(axis.min, axis.tickSize),
+ i = 0, v = Number.NaN, prev;
+ do {
+ prev = v;
+ v = start + i * axis.tickSize;
+ ticks.push(v);
+ ++i;
+ } while (v < axis.max && v != prev);
+ return ticks;
+ };
+
+ formatter = function (v, axis) {
+ return v.toFixed(axis.tickDecimals);
+ };
+ }
+
+ if (opts.alignTicksWithAxis != null) {
+ var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1];
+ if (otherAxis && otherAxis.used && otherAxis != axis) {
+ // consider snapping min/max to outermost nice ticks
+ var niceTicks = generator(axis);
+ if (niceTicks.length > 0) {
+ if (opts.min == null)
+ axis.min = Math.min(axis.min, niceTicks[0]);
+ if (opts.max == null && niceTicks.length > 1)
+ axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]);
+ }
+
+ generator = function (axis) {
+ // copy ticks, scaled to this axis
+ var ticks = [], v, i;
+ for (i = 0; i < otherAxis.ticks.length; ++i) {
+ v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min);
+ v = axis.min + v * (axis.max - axis.min);
+ ticks.push(v);
+ }
+ return ticks;
+ };
+
+ // we might need an extra decimal since forced
+ // ticks don't necessarily fit naturally
+ if (axis.mode != "time" && opts.tickDecimals == null) {
+ var extraDec = Math.max(0, -Math.floor(Math.log(delta) / Math.LN10) + 1),
+ ts = generator(axis);
+
+ // only proceed if the tick interval rounded
+ // with an extra decimal doesn't give us a
+ // zero at end
+ if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec))))
+ axis.tickDecimals = extraDec;
+ }
+ }
+ }
+
+ axis.tickGenerator = generator;
+ if ($.isFunction(opts.tickFormatter))
+ axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); };
+ else
+ axis.tickFormatter = formatter;
+ }
+
+ function setTicks(axis) {
+ var oticks = axis.options.ticks, ticks = [];
+ if (oticks == null || (typeof oticks == "number" && oticks > 0))
+ ticks = axis.tickGenerator(axis);
+ else if (oticks) {
+ if ($.isFunction(oticks))
+ // generate the ticks
+ ticks = oticks({ min: axis.min, max: axis.max });
+ else
+ ticks = oticks;
+ }
+
+ // clean up/labelify the supplied ticks, copy them over
+ var i, v;
+ axis.ticks = [];
+ for (i = 0; i < ticks.length; ++i) {
+ var label = null;
+ var t = ticks[i];
+ if (typeof t == "object") {
+ v = +t[0];
+ if (t.length > 1)
+ label = t[1];
+ }
+ else
+ v = +t;
+ if (label == null)
+ label = axis.tickFormatter(v, axis);
+ if (!isNaN(v))
+ axis.ticks.push({ v: v, label: label });
+ }
+ }
+
+ function snapRangeToTicks(axis, ticks) {
+ if (axis.options.autoscaleMargin && ticks.length > 0) {
+ // snap to ticks
+ if (axis.options.min == null)
+ axis.min = Math.min(axis.min, ticks[0].v);
+ if (axis.options.max == null && ticks.length > 1)
+ axis.max = Math.max(axis.max, ticks[ticks.length - 1].v);
+ }
+ }
+
+ function draw() {
+ ctx.clearRect(0, 0, canvasWidth, canvasHeight);
+
+ var grid = options.grid;
+
+ // draw background, if any
+ if (grid.show && grid.backgroundColor)
+ drawBackground();
+
+ if (grid.show && !grid.aboveData)
+ drawGrid();
+
+ for (var i = 0; i < series.length; ++i) {
+ executeHooks(hooks.drawSeries, [ctx, series[i]]);
+ drawSeries(series[i]);
+ }
+
+ executeHooks(hooks.draw, [ctx]);
+
+ if (grid.show && grid.aboveData)
+ drawGrid();
+ }
+
+ function extractRange(ranges, coord) {
+ var axis, from, to, key, axes = allAxes();
+
+ for (i = 0; i < axes.length; ++i) {
+ axis = axes[i];
+ if (axis.direction == coord) {
+ key = coord + axis.n + "axis";
+ if (!ranges[key] && axis.n == 1)
+ key = coord + "axis"; // support x1axis as xaxis
+ if (ranges[key]) {
+ from = ranges[key].from;
+ to = ranges[key].to;
+ break;
+ }
+ }
+ }
+
+ // backwards-compat stuff - to be removed in future
+ if (!ranges[key]) {
+ axis = coord == "x" ? xaxes[0] : yaxes[0];
+ from = ranges[coord + "1"];
+ to = ranges[coord + "2"];
+ }
+
+ // auto-reverse as an added bonus
+ if (from != null && to != null && from > to) {
+ var tmp = from;
+ from = to;
+ to = tmp;
+ }
+
+ return { from: from, to: to, axis: axis };
+ }
+
+ function drawBackground() {
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+
+ ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)");
+ ctx.fillRect(0, 0, plotWidth, plotHeight);
+ ctx.restore();
+ }
+
+ function drawGrid() {
+ var i;
+
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+
+ // draw markings
+ var markings = options.grid.markings;
+ if (markings) {
+ if ($.isFunction(markings)) {
+ var axes = plot.getAxes();
+ // xmin etc. is backwards compatibility, to be
+ // removed in the future
+ axes.xmin = axes.xaxis.min;
+ axes.xmax = axes.xaxis.max;
+ axes.ymin = axes.yaxis.min;
+ axes.ymax = axes.yaxis.max;
+
+ markings = markings(axes);
+ }
+
+ for (i = 0; i < markings.length; ++i) {
+ var m = markings[i],
+ xrange = extractRange(m, "x"),
+ yrange = extractRange(m, "y");
+
+ // fill in missing
+ if (xrange.from == null)
+ xrange.from = xrange.axis.min;
+ if (xrange.to == null)
+ xrange.to = xrange.axis.max;
+ if (yrange.from == null)
+ yrange.from = yrange.axis.min;
+ if (yrange.to == null)
+ yrange.to = yrange.axis.max;
+
+ // clip
+ if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
+ yrange.to < yrange.axis.min || yrange.from > yrange.axis.max)
+ continue;
+
+ xrange.from = Math.max(xrange.from, xrange.axis.min);
+ xrange.to = Math.min(xrange.to, xrange.axis.max);
+ yrange.from = Math.max(yrange.from, yrange.axis.min);
+ yrange.to = Math.min(yrange.to, yrange.axis.max);
+
+ if (xrange.from == xrange.to && yrange.from == yrange.to)
+ continue;
+
+ // then draw
+ xrange.from = xrange.axis.p2c(xrange.from);
+ xrange.to = xrange.axis.p2c(xrange.to);
+ yrange.from = yrange.axis.p2c(yrange.from);
+ yrange.to = yrange.axis.p2c(yrange.to);
+
+ if (xrange.from == xrange.to || yrange.from == yrange.to) {
+ // draw line
+ ctx.beginPath();
+ ctx.strokeStyle = m.color || options.grid.markingsColor;
+ ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
+ ctx.moveTo(xrange.from, yrange.from);
+ ctx.lineTo(xrange.to, yrange.to);
+ ctx.stroke();
+ }
+ else {
+ // fill area
+ ctx.fillStyle = m.color || options.grid.markingsColor;
+ ctx.fillRect(xrange.from, yrange.to,
+ xrange.to - xrange.from,
+ yrange.from - yrange.to);
+ }
+ }
+ }
+
+ // draw the ticks
+ var axes = allAxes(), bw = options.grid.borderWidth;
+
+ for (var j = 0; j < axes.length; ++j) {
+ var axis = axes[j], box = axis.box,
+ t = axis.tickLength, x, y, xoff, yoff;
+ if (!axis.show || axis.ticks.length == 0)
+ continue
+
+ ctx.strokeStyle = axis.options.tickColor || $.color.parse(axis.options.color).scale('a', 0.22).toString();
+ ctx.lineWidth = 1;
+
+ // find the edges
+ if (axis.direction == "x") {
+ x = 0;
+ if (t == "full")
+ y = (axis.position == "top" ? 0 : plotHeight);
+ else
+ y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0);
+ }
+ else {
+ y = 0;
+ if (t == "full")
+ x = (axis.position == "left" ? 0 : plotWidth);
+ else
+ x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0);
+ }
+
+ // draw tick bar
+ if (!axis.innermost) {
+ ctx.beginPath();
+ xoff = yoff = 0;
+ if (axis.direction == "x")
+ xoff = plotWidth;
+ else
+ yoff = plotHeight;
+
+ if (ctx.lineWidth == 1) {
+ x = Math.floor(x) + 0.5;
+ y = Math.floor(y) + 0.5;
+ }
+
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + xoff, y + yoff);
+ ctx.stroke();
+ }
+
+ // draw ticks
+ ctx.beginPath();
+ for (i = 0; i < axis.ticks.length; ++i) {
+ var v = axis.ticks[i].v;
+
+ xoff = yoff = 0;
+
+ if (v < axis.min || v > axis.max
+ // skip those lying on the axes if we got a border
+ || (t == "full" && bw > 0
+ && (v == axis.min || v == axis.max)))
+ continue;
+
+ if (axis.direction == "x") {
+ x = axis.p2c(v);
+ yoff = t == "full" ? -plotHeight : t;
+
+ if (axis.position == "top")
+ yoff = -yoff;
+ }
+ else {
+ y = axis.p2c(v);
+ xoff = t == "full" ? -plotWidth : t;
+
+ if (axis.position == "left")
+ xoff = -xoff;
+ }
+
+ if (ctx.lineWidth == 1) {
+ if (axis.direction == "x")
+ x = Math.floor(x) + 0.5;
+ else
+ y = Math.floor(y) + 0.5;
+ }
+
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + xoff, y + yoff);
+ }
+
+ ctx.stroke();
+ }
+
+
+ // draw border
+ if (bw) {
+ ctx.lineWidth = bw;
+ ctx.strokeStyle = options.grid.borderColor;
+ ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw);
+ }
+
+ ctx.restore();
+ }
+
+ function insertAxisLabels() {
+ placeholder.find(".tickLabels").remove();
+
+ var html = ['
'];
+
+ var axes = allAxes();
+ for (var j = 0; j < axes.length; ++j) {
+ var axis = axes[j], box = axis.box;
+ if (!axis.show)
+ continue;
+ //debug: html.push('')
+ html.push('
').appendTo(placeholder);
+ if (options.legend.backgroundOpacity != 0.0) {
+ // put in the transparent background
+ // separately to avoid blended labels and
+ // label boxes
+ var c = options.legend.backgroundColor;
+ if (c == null) {
+ c = options.grid.backgroundColor;
+ if (c && typeof c == "string")
+ c = $.color.parse(c);
+ else
+ c = $.color.extract(legend, 'background-color');
+ c.a = 1;
+ c = c.toString();
+ }
+ var div = legend.children();
+ $('
').prependTo(legend).css('opacity', options.legend.backgroundOpacity);
+ }
+ }
+ }
+
+
+ // interactive features
+
+ var highlights = [],
+ redrawTimeout = null;
+
+ // returns the data item the mouse is over, or null if none is found
+ function findNearbyItem(mouseX, mouseY, seriesFilter) {
+ var maxDistance = options.grid.mouseActiveRadius,
+ smallestDistance = maxDistance * maxDistance + 1,
+ item = null, foundPoint = false, i, j;
+
+ for (i = series.length - 1; i >= 0; --i) {
+ if (!seriesFilter(series[i]))
+ continue;
+
+ var s = series[i],
+ axisx = s.xaxis,
+ axisy = s.yaxis,
+ points = s.datapoints.points,
+ ps = s.datapoints.pointsize,
+ mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster
+ my = axisy.c2p(mouseY),
+ maxx = maxDistance / axisx.scale,
+ maxy = maxDistance / axisy.scale;
+
+ // with inverse transforms, we can't use the maxx/maxy
+ // optimization, sadly
+ if (axisx.options.inverseTransform)
+ maxx = Number.MAX_VALUE;
+ if (axisy.options.inverseTransform)
+ maxy = Number.MAX_VALUE;
+
+ if (s.lines.show || s.points.show) {
+ for (j = 0; j < points.length; j += ps) {
+ var x = points[j], y = points[j + 1];
+ if (x == null)
+ continue;
+
+ // For points and lines, the cursor must be within a
+ // certain distance to the data point
+ if (x - mx > maxx || x - mx < -maxx ||
+ y - my > maxy || y - my < -maxy)
+ continue;
+
+ // We have to calculate distances in pixels, not in
+ // data units, because the scales of the axes may be different
+ var dx = Math.abs(axisx.p2c(x) - mouseX),
+ dy = Math.abs(axisy.p2c(y) - mouseY),
+ dist = dx * dx + dy * dy; // we save the sqrt
+
+ // use <= to ensure last point takes precedence
+ // (last generally means on top of)
+ if (dist < smallestDistance) {
+ smallestDistance = dist;
+ item = [i, j / ps];
+ }
+ }
+ }
+
+ if (s.bars.show && !item) { // no other point can be nearby
+ var barLeft = s.bars.align == "left" ? 0 : -s.bars.barWidth/2,
+ barRight = barLeft + s.bars.barWidth;
+
+ for (j = 0; j < points.length; j += ps) {
+ var x = points[j], y = points[j + 1], b = points[j + 2];
+ if (x == null)
+ continue;
+
+ // for a bar graph, the cursor must be inside the bar
+ if (series[i].bars.horizontal ?
+ (mx <= Math.max(b, x) && mx >= Math.min(b, x) &&
+ my >= y + barLeft && my <= y + barRight) :
+ (mx >= x + barLeft && mx <= x + barRight &&
+ my >= Math.min(b, y) && my <= Math.max(b, y)))
+ item = [i, j / ps];
+ }
+ }
+ }
+
+ if (item) {
+ i = item[0];
+ j = item[1];
+ ps = series[i].datapoints.pointsize;
+
+ return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps),
+ dataIndex: j,
+ series: series[i],
+ seriesIndex: i };
+ }
+
+ return null;
+ }
+
+ function onMouseMove(e) {
+ if (options.grid.hoverable)
+ triggerClickHoverEvent("plothover", e,
+ function (s) { return s["hoverable"] != false; });
+ }
+
+ function onMouseLeave(e) {
+ if (options.grid.hoverable)
+ triggerClickHoverEvent("plothover", e,
+ function (s) { return false; });
+ }
+
+ function onClick(e) {
+ triggerClickHoverEvent("plotclick", e,
+ function (s) { return s["clickable"] != false; });
+ }
+
+ // trigger click or hover event (they send the same parameters
+ // so we share their code)
+ function triggerClickHoverEvent(eventname, event, seriesFilter) {
+ var offset = eventHolder.offset(),
+ canvasX = event.pageX - offset.left - plotOffset.left,
+ canvasY = event.pageY - offset.top - plotOffset.top,
+ pos = canvasToAxisCoords({ left: canvasX, top: canvasY });
+
+ pos.pageX = event.pageX;
+ pos.pageY = event.pageY;
+
+ var item = findNearbyItem(canvasX, canvasY, seriesFilter);
+
+ if (item) {
+ // fill in mouse pos for any listeners out there
+ item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left);
+ item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top);
+ }
+
+ if (options.grid.autoHighlight) {
+ // clear auto-highlights
+ for (var i = 0; i < highlights.length; ++i) {
+ var h = highlights[i];
+ if (h.auto == eventname &&
+ !(item && h.series == item.series &&
+ h.point[0] == item.datapoint[0] &&
+ h.point[1] == item.datapoint[1]))
+ unhighlight(h.series, h.point);
+ }
+
+ if (item)
+ highlight(item.series, item.datapoint, eventname);
+ }
+
+ placeholder.trigger(eventname, [ pos, item ]);
+ }
+
+ function triggerRedrawOverlay() {
+ if (!redrawTimeout)
+ redrawTimeout = setTimeout(drawOverlay, 30);
+ }
+
+ function drawOverlay() {
+ redrawTimeout = null;
+
+ // draw highlights
+ octx.save();
+ octx.clearRect(0, 0, canvasWidth, canvasHeight);
+ octx.translate(plotOffset.left, plotOffset.top);
+
+ var i, hi;
+ for (i = 0; i < highlights.length; ++i) {
+ hi = highlights[i];
+
+ if (hi.series.bars.show)
+ drawBarHighlight(hi.series, hi.point);
+ else
+ drawPointHighlight(hi.series, hi.point);
+ }
+ octx.restore();
+
+ executeHooks(hooks.drawOverlay, [octx]);
+ }
+
+ function highlight(s, point, auto) {
+ if (typeof s == "number")
+ s = series[s];
+
+ if (typeof point == "number") {
+ var ps = s.datapoints.pointsize;
+ point = s.datapoints.points.slice(ps * point, ps * (point + 1));
+ }
+
+ var i = indexOfHighlight(s, point);
+ if (i == -1) {
+ highlights.push({ series: s, point: point, auto: auto });
+
+ triggerRedrawOverlay();
+ }
+ else if (!auto)
+ highlights[i].auto = false;
+ }
+
+ function unhighlight(s, point) {
+ if (s == null && point == null) {
+ highlights = [];
+ triggerRedrawOverlay();
+ }
+
+ if (typeof s == "number")
+ s = series[s];
+
+ if (typeof point == "number")
+ point = s.data[point];
+
+ var i = indexOfHighlight(s, point);
+ if (i != -1) {
+ highlights.splice(i, 1);
+
+ triggerRedrawOverlay();
+ }
+ }
+
+ function indexOfHighlight(s, p) {
+ for (var i = 0; i < highlights.length; ++i) {
+ var h = highlights[i];
+ if (h.series == s && h.point[0] == p[0]
+ && h.point[1] == p[1])
+ return i;
+ }
+ return -1;
+ }
+
+ function drawPointHighlight(series, point) {
+ var x = point[0], y = point[1],
+ axisx = series.xaxis, axisy = series.yaxis;
+
+ if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
+ return;
+
+ var pointRadius = series.points.radius + series.points.lineWidth / 2;
+ octx.lineWidth = pointRadius;
+ octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();
+ var radius = 1.5 * pointRadius,
+ x = axisx.p2c(x),
+ y = axisy.p2c(y);
+
+ octx.beginPath();
+ if (series.points.symbol == "circle")
+ octx.arc(x, y, radius, 0, 2 * Math.PI, false);
+ else
+ series.points.symbol(octx, x, y, radius, false);
+ octx.closePath();
+ octx.stroke();
+ }
+
+ function drawBarHighlight(series, point) {
+ octx.lineWidth = series.bars.lineWidth;
+ octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();
+ var fillStyle = $.color.parse(series.color).scale('a', 0.5).toString();
+ var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
+ drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth,
+ 0, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth);
+ }
+
+ function getColorOrGradient(spec, bottom, top, defaultColor) {
+ if (typeof spec == "string")
+ return spec;
+ else {
+ // assume this is a gradient spec; IE currently only
+ // supports a simple vertical gradient properly, so that's
+ // what we support too
+ var gradient = ctx.createLinearGradient(0, top, 0, bottom);
+
+ for (var i = 0, l = spec.colors.length; i < l; ++i) {
+ var c = spec.colors[i];
+ if (typeof c != "string") {
+ var co = $.color.parse(defaultColor);
+ if (c.brightness != null)
+ co = co.scale('rgb', c.brightness)
+ if (c.opacity != null)
+ co.a *= c.opacity;
+ c = co.toString();
+ }
+ gradient.addColorStop(i / (l - 1), c);
+ }
+
+ return gradient;
+ }
+ }
+ }
+
+ $.plot = function(placeholder, data, options) {
+ //var t0 = new Date();
+ var plot = new Plot($(placeholder), data, options, $.plot.plugins);
+ //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime()));
+ return plot;
+ };
+
+ $.plot.version = "0.7";
+
+ $.plot.plugins = [];
+
+ // returns a string with the date d formatted according to fmt
+ $.plot.formatDate = function(d, fmt, monthNames) {
+ var leftPad = function(n) {
+ n = "" + n;
+ return n.length == 1 ? "0" + n : n;
+ };
+
+ var r = [];
+ var escape = false, padNext = false;
+ var hours = d.getUTCHours();
+ var isAM = hours < 12;
+ if (monthNames == null)
+ monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+
+ if (fmt.search(/%p|%P/) != -1) {
+ if (hours > 12) {
+ hours = hours - 12;
+ } else if (hours == 0) {
+ hours = 12;
+ }
+ }
+ for (var i = 0; i < fmt.length; ++i) {
+ var c = fmt.charAt(i);
+
+ if (escape) {
+ switch (c) {
+ case 'h': c = "" + hours; break;
+ case 'H': c = leftPad(hours); break;
+ case 'M': c = leftPad(d.getUTCMinutes()); break;
+ case 'S': c = leftPad(d.getUTCSeconds()); break;
+ case 'd': c = "" + d.getUTCDate(); break;
+ case 'm': c = "" + (d.getUTCMonth() + 1); break;
+ case 'y': c = "" + d.getUTCFullYear(); break;
+ case 'b': c = "" + monthNames[d.getUTCMonth()]; break;
+ case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
+ case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
+ case '0': c = ""; padNext = true; break;
+ }
+ if (c && padNext) {
+ c = leftPad(c);
+ padNext = false;
+ }
+ r.push(c);
+ if (!padNext)
+ escape = false;
+ }
+ else {
+ if (c == "%")
+ escape = true;
+ else
+ r.push(c);
+ }
+ }
+ return r.join("");
+ };
+
+ // round to nearby lower multiple of base
+ function floorInBase(n, base) {
+ return base * Math.floor(n / base);
+ }
+
+})(jQuery);
diff --git a/data/templates/js/jquery.flot.navigate.js b/data/templates/js/jquery.flot.navigate.js
new file mode 100644
index 0000000..34d799d
--- /dev/null
+++ b/data/templates/js/jquery.flot.navigate.js
@@ -0,0 +1,321 @@
+/*
+Flot plugin for adding panning and zooming capabilities to a plot.
+
+The default behaviour is double click and scrollwheel up/down to zoom
+in, drag to pan. The plugin defines plot.zoom({ center }),
+plot.zoomOut() and plot.pan(offset) so you easily can add custom
+controls. It also fires a "plotpan" and "plotzoom" event when
+something happens, useful for synchronizing plots.
+
+Options:
+
+ zoom: {
+ interactive: false
+ trigger: "dblclick" // or "click" for single click
+ amount: 1.5 // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
+ }
+
+ pan: {
+ interactive: false
+ cursor: "move" // CSS mouse cursor value used when dragging, e.g. "pointer"
+ frameRate: 20
+ }
+
+ xaxis, yaxis, x2axis, y2axis: {
+ zoomRange: null // or [number, number] (min range, max range) or false
+ panRange: null // or [number, number] (min, max) or false
+ }
+
+"interactive" enables the built-in drag/click behaviour. If you enable
+interactive for pan, then you'll have a basic plot that supports
+moving around; the same for zoom.
+
+"amount" specifies the default amount to zoom in (so 1.5 = 150%)
+relative to the current viewport.
+
+"cursor" is a standard CSS mouse cursor string used for visual
+feedback to the user when dragging.
+
+"frameRate" specifies the maximum number of times per second the plot
+will update itself while the user is panning around on it (set to null
+to disable intermediate pans, the plot will then not update until the
+mouse button is released).
+
+"zoomRange" is the interval in which zooming can happen, e.g. with
+zoomRange: [1, 100] the zoom will never scale the axis so that the
+difference between min and max is smaller than 1 or larger than 100.
+You can set either end to null to ignore, e.g. [1, null]. If you set
+zoomRange to false, zooming on that axis will be disabled.
+
+"panRange" confines the panning to stay within a range, e.g. with
+panRange: [-10, 20] panning stops at -10 in one end and at 20 in the
+other. Either can be null, e.g. [-10, null]. If you set
+panRange to false, panning on that axis will be disabled.
+
+Example API usage:
+
+ plot = $.plot(...);
+
+ // zoom default amount in on the pixel (10, 20)
+ plot.zoom({ center: { left: 10, top: 20 } });
+
+ // zoom out again
+ plot.zoomOut({ center: { left: 10, top: 20 } });
+
+ // zoom 200% in on the pixel (10, 20)
+ plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
+
+ // pan 100 pixels to the left and 20 down
+ plot.pan({ left: -100, top: 20 })
+
+Here, "center" specifies where the center of the zooming should
+happen. Note that this is defined in pixel space, not the space of the
+data points (you can use the p2c helpers on the axes in Flot to help
+you convert between these).
+
+"amount" is the amount to zoom the viewport relative to the current
+range, so 1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is
+70% (zoom out). You can set the default in the options.
+
+*/
+
+
+// First two dependencies, jquery.event.drag.js and
+// jquery.mousewheel.js, we put them inline here to save people the
+// effort of downloading them.
+
+/*
+jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
+Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt
+*/
+(function(E){E.fn.drag=function(L,K,J){if(K){this.bind("dragstart",L)}if(J){this.bind("dragend",J)}return !L?this.trigger("drag"):this.bind("drag",K?K:L)};var A=E.event,B=A.special,F=B.drag={not:":input",distance:0,which:1,dragging:false,setup:function(J){J=E.extend({distance:F.distance,which:F.which,not:F.not},J||{});J.distance=I(J.distance);A.add(this,"mousedown",H,J);if(this.attachEvent){this.attachEvent("ondragstart",D)}},teardown:function(){A.remove(this,"mousedown",H);if(this===F.dragging){F.dragging=F.proxy=false}G(this,true);if(this.detachEvent){this.detachEvent("ondragstart",D)}}};B.dragstart=B.dragend={setup:function(){},teardown:function(){}};function H(L){var K=this,J,M=L.data||{};if(M.elem){K=L.dragTarget=M.elem;L.dragProxy=F.proxy||K;L.cursorOffsetX=M.pageX-M.left;L.cursorOffsetY=M.pageY-M.top;L.offsetX=L.pageX-L.cursorOffsetX;L.offsetY=L.pageY-L.cursorOffsetY}else{if(F.dragging||(M.which>0&&L.which!=M.which)||E(L.target).is(M.not)){return }}switch(L.type){case"mousedown":E.extend(M,E(K).offset(),{elem:K,target:L.target,pageX:L.pageX,pageY:L.pageY});A.add(document,"mousemove mouseup",H,M);G(K,false);F.dragging=null;return false;case !F.dragging&&"mousemove":if(I(L.pageX-M.pageX)+I(L.pageY-M.pageY) max) {
+ // make sure min < max
+ var tmp = min;
+ min = max;
+ max = tmp;
+ }
+
+ var range = max - min;
+ if (zr &&
+ ((zr[0] != null && range < zr[0]) ||
+ (zr[1] != null && range > zr[1])))
+ return;
+
+ opts.min = min;
+ opts.max = max;
+ });
+
+ plot.setupGrid();
+ plot.draw();
+
+ if (!args.preventEvent)
+ plot.getPlaceholder().trigger("plotzoom", [ plot ]);
+ }
+
+ plot.pan = function (args) {
+ var delta = {
+ x: +args.left,
+ y: +args.top
+ };
+
+ if (isNaN(delta.x))
+ delta.x = 0;
+ if (isNaN(delta.y))
+ delta.y = 0;
+
+ $.each(plot.getAxes(), function (_, axis) {
+ var opts = axis.options,
+ min, max, d = delta[axis.direction];
+
+ min = axis.c2p(axis.p2c(axis.min) + d),
+ max = axis.c2p(axis.p2c(axis.max) + d);
+
+ var pr = opts.panRange;
+ if (pr === false) // no panning on this axis
+ return;
+
+ if (pr) {
+ // check whether we hit the wall
+ if (pr[0] != null && pr[0] > min) {
+ d = pr[0] - min;
+ min += d;
+ max += d;
+ }
+
+ if (pr[1] != null && pr[1] < max) {
+ d = pr[1] - max;
+ min += d;
+ max += d;
+ }
+ }
+
+ opts.min = min;
+ opts.max = max;
+ });
+
+ plot.setupGrid();
+ plot.draw();
+
+ if (!args.preventEvent)
+ plot.getPlaceholder().trigger("plotpan", [ plot ]);
+ }
+
+ function shutdown(plot, eventHolder) {
+ eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick);
+ eventHolder.unbind("mousewheel", onMouseWheel);
+ eventHolder.unbind("dragstart", onDragStart);
+ eventHolder.unbind("drag", onDrag);
+ eventHolder.unbind("dragend", onDragEnd);
+ if (panTimeout)
+ clearTimeout(panTimeout);
+ }
+
+ plot.hooks.bindEvents.push(bindEvents);
+ plot.hooks.shutdown.push(shutdown);
+ }
+
+ $.plot.plugins.push({
+ init: init,
+ options: options,
+ name: 'navigate',
+ version: '1.3'
+ });
+})(jQuery);
diff --git a/data/templates/js/jquery.flot.pie.js b/data/templates/js/jquery.flot.pie.js
new file mode 100644
index 0000000..b46c03c
--- /dev/null
+++ b/data/templates/js/jquery.flot.pie.js
@@ -0,0 +1,750 @@
+/*
+Flot plugin for rendering pie charts. The plugin assumes the data is
+coming is as a single data value for each series, and each of those
+values is a positive value or zero (negative numbers don't make
+any sense and will cause strange effects). The data values do
+NOT need to be passed in as percentage values because it
+internally calculates the total and percentages.
+
+* Created by Brian Medendorp, June 2009
+* Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars
+
+* Changes:
+ 2009-10-22: lineJoin set to round
+ 2009-10-23: IE full circle fix, donut
+ 2009-11-11: Added basic hover from btburnett3 - does not work in IE, and center is off in Chrome and Opera
+ 2009-11-17: Added IE hover capability submitted by Anthony Aragues
+ 2009-11-18: Added bug fix submitted by Xavi Ivars (issues with arrays when other JS libraries are included as well)
+
+
+Available options are:
+series: {
+ pie: {
+ show: true/false
+ radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'
+ innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect
+ startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result
+ tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)
+ offset: {
+ top: integer value to move the pie up or down
+ left: integer value to move the pie left or right, or 'auto'
+ },
+ stroke: {
+ color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')
+ width: integer pixel width of the stroke
+ },
+ label: {
+ show: true/false, or 'auto'
+ formatter: a user-defined function that modifies the text/style of the label text
+ radius: 0-1 for percentage of fullsize, or a specified pixel length
+ background: {
+ color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')
+ opacity: 0-1
+ },
+ threshold: 0-1 for the percentage value at which to hide labels (if they're too small)
+ },
+ combine: {
+ threshold: 0-1 for the percentage value at which to combine slices (if they're too small)
+ color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined
+ label: any text value of what the combined slice should be labeled
+ }
+ highlight: {
+ opacity: 0-1
+ }
+ }
+}
+
+More detail and specific examples can be found in the included HTML file.
+
+*/
+
+(function ($)
+{
+ function init(plot) // this is the "body" of the plugin
+ {
+ var canvas = null;
+ var target = null;
+ var maxRadius = null;
+ var centerLeft = null;
+ var centerTop = null;
+ var total = 0;
+ var redraw = true;
+ var redrawAttempts = 10;
+ var shrink = 0.95;
+ var legendWidth = 0;
+ var processed = false;
+ var raw = false;
+
+ // interactive variables
+ var highlights = [];
+
+ // add hook to determine if pie plugin in enabled, and then perform necessary operations
+ plot.hooks.processOptions.push(checkPieEnabled);
+ plot.hooks.bindEvents.push(bindEvents);
+
+ // check to see if the pie plugin is enabled
+ function checkPieEnabled(plot, options)
+ {
+ if (options.series.pie.show)
+ {
+ //disable grid
+ options.grid.show = false;
+
+ // set labels.show
+ if (options.series.pie.label.show=='auto')
+ if (options.legend.show)
+ options.series.pie.label.show = false;
+ else
+ options.series.pie.label.show = true;
+
+ // set radius
+ if (options.series.pie.radius=='auto')
+ if (options.series.pie.label.show)
+ options.series.pie.radius = 3/4;
+ else
+ options.series.pie.radius = 1;
+
+ // ensure sane tilt
+ if (options.series.pie.tilt>1)
+ options.series.pie.tilt=1;
+ if (options.series.pie.tilt<0)
+ options.series.pie.tilt=0;
+
+ // add processData hook to do transformations on the data
+ plot.hooks.processDatapoints.push(processDatapoints);
+ plot.hooks.drawOverlay.push(drawOverlay);
+
+ // add draw hook
+ plot.hooks.draw.push(draw);
+ }
+ }
+
+ // bind hoverable events
+ function bindEvents(plot, eventHolder)
+ {
+ var options = plot.getOptions();
+
+ if (options.series.pie.show && options.grid.hoverable)
+ eventHolder.unbind('mousemove').mousemove(onMouseMove);
+
+ if (options.series.pie.show && options.grid.clickable)
+ eventHolder.unbind('click').click(onClick);
+ }
+
+
+ // debugging function that prints out an object
+ function alertObject(obj)
+ {
+ var msg = '';
+ function traverse(obj, depth)
+ {
+ if (!depth)
+ depth = 0;
+ for (var i = 0; i < obj.length; ++i)
+ {
+ for (var j=0; jcanvas.width-maxRadius)
+ centerLeft = canvas.width-maxRadius;
+ }
+
+ function fixData(data)
+ {
+ for (var i = 0; i < data.length; ++i)
+ {
+ if (typeof(data[i].data)=='number')
+ data[i].data = [[1,data[i].data]];
+ else if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined')
+ {
+ if (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined')
+ data[i].label = data[i].data.label; // fix weirdness coming from flot
+ data[i].data = [[1,0]];
+
+ }
+ }
+ return data;
+ }
+
+ function combine(data)
+ {
+ data = fixData(data);
+ calcTotal(data);
+ var combined = 0;
+ var numCombined = 0;
+ var color = options.series.pie.combine.color;
+
+ var newdata = [];
+ for (var i = 0; i < data.length; ++i)
+ {
+ // make sure its a number
+ data[i].data[0][1] = parseFloat(data[i].data[0][1]);
+ if (!data[i].data[0][1])
+ data[i].data[0][1] = 0;
+
+ if (data[i].data[0][1]/total<=options.series.pie.combine.threshold)
+ {
+ combined += data[i].data[0][1];
+ numCombined++;
+ if (!color)
+ color = data[i].color;
+ }
+ else
+ {
+ newdata.push({
+ data: [[1,data[i].data[0][1]]],
+ color: data[i].color,
+ label: data[i].label,
+ angle: (data[i].data[0][1]*(Math.PI*2))/total,
+ percent: (data[i].data[0][1]/total*100)
+ });
+ }
+ }
+ if (numCombined>0)
+ newdata.push({
+ data: [[1,combined]],
+ color: color,
+ label: options.series.pie.combine.label,
+ angle: (combined*(Math.PI*2))/total,
+ percent: (combined/total*100)
+ });
+ return newdata;
+ }
+
+ function draw(plot, newCtx)
+ {
+ if (!target) return; // if no series were passed
+ ctx = newCtx;
+
+ setupPie();
+ var slices = plot.getData();
+
+ var attempts = 0;
+ while (redraw && attempts0)
+ maxRadius *= shrink;
+ attempts += 1;
+ clear();
+ if (options.series.pie.tilt<=0.8)
+ drawShadow();
+ drawPie();
+ }
+ if (attempts >= redrawAttempts) {
+ clear();
+ target.prepend('
Could not draw pie with labels contained inside canvas
');
+ }
+
+ if ( plot.setSeries && plot.insertLegend )
+ {
+ plot.setSeries(slices);
+ plot.insertLegend();
+ }
+
+ // we're actually done at this point, just defining internal functions at this point
+
+ function clear()
+ {
+ ctx.clearRect(0,0,canvas.width,canvas.height);
+ target.children().filter('.pieLabel, .pieLabelBackground').remove();
+ }
+
+ function drawShadow()
+ {
+ var shadowLeft = 5;
+ var shadowTop = 15;
+ var edge = 10;
+ var alpha = 0.02;
+
+ // set radius
+ if (options.series.pie.radius>1)
+ var radius = options.series.pie.radius;
+ else
+ var radius = maxRadius * options.series.pie.radius;
+
+ if (radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/2)-shadowTop || radius<=edge)
+ return; // shadow would be outside canvas, so don't draw it
+
+ ctx.save();
+ ctx.translate(shadowLeft,shadowTop);
+ ctx.globalAlpha = alpha;
+ ctx.fillStyle = '#000';
+
+ // center and rotate to starting position
+ ctx.translate(centerLeft,centerTop);
+ ctx.scale(1, options.series.pie.tilt);
+
+ //radius -= edge;
+ for (var i=1; i<=edge; i++)
+ {
+ ctx.beginPath();
+ ctx.arc(0,0,radius,0,Math.PI*2,false);
+ ctx.fill();
+ radius -= i;
+ }
+
+ ctx.restore();
+ }
+
+ function drawPie()
+ {
+ startAngle = Math.PI*options.series.pie.startAngle;
+
+ // set radius
+ if (options.series.pie.radius>1)
+ var radius = options.series.pie.radius;
+ else
+ var radius = maxRadius * options.series.pie.radius;
+
+ // center and rotate to starting position
+ ctx.save();
+ ctx.translate(centerLeft,centerTop);
+ ctx.scale(1, options.series.pie.tilt);
+ //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera
+
+ // draw slices
+ ctx.save();
+ var currentAngle = startAngle;
+ for (var i = 0; i < slices.length; ++i)
+ {
+ slices[i].startAngle = currentAngle;
+ drawSlice(slices[i].angle, slices[i].color, true);
+ }
+ ctx.restore();
+
+ // draw slice outlines
+ ctx.save();
+ ctx.lineWidth = options.series.pie.stroke.width;
+ currentAngle = startAngle;
+ for (var i = 0; i < slices.length; ++i)
+ drawSlice(slices[i].angle, options.series.pie.stroke.color, false);
+ ctx.restore();
+
+ // draw donut hole
+ drawDonutHole(ctx);
+
+ // draw labels
+ if (options.series.pie.label.show)
+ drawLabels();
+
+ // restore to original state
+ ctx.restore();
+
+ function drawSlice(angle, color, fill)
+ {
+ if (angle<=0)
+ return;
+
+ if (fill)
+ ctx.fillStyle = color;
+ else
+ {
+ ctx.strokeStyle = color;
+ ctx.lineJoin = 'round';
+ }
+
+ ctx.beginPath();
+ if (Math.abs(angle - Math.PI*2) > 0.000000001)
+ ctx.moveTo(0,0); // Center of the pie
+ else if ($.browser.msie)
+ angle -= 0.0001;
+ //ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera
+ ctx.arc(0,0,radius,currentAngle,currentAngle+angle,false);
+ ctx.closePath();
+ //ctx.rotate(angle); // This doesn't work properly in Opera
+ currentAngle += angle;
+
+ if (fill)
+ ctx.fill();
+ else
+ ctx.stroke();
+ }
+
+ function drawLabels()
+ {
+ var currentAngle = startAngle;
+
+ // set radius
+ if (options.series.pie.label.radius>1)
+ var radius = options.series.pie.label.radius;
+ else
+ var radius = maxRadius * options.series.pie.label.radius;
+
+ for (var i = 0; i < slices.length; ++i)
+ {
+ if (slices[i].percent >= options.series.pie.label.threshold*100)
+ drawLabel(slices[i], currentAngle, i);
+ currentAngle += slices[i].angle;
+ }
+
+ function drawLabel(slice, startAngle, index)
+ {
+ if (slice.data[0][1]==0)
+ return;
+
+ // format label text
+ var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;
+ if (lf)
+ text = lf(slice.label, slice);
+ else
+ text = slice.label;
+ if (plf)
+ text = plf(text, slice);
+
+ var halfAngle = ((startAngle+slice.angle) + startAngle)/2;
+ var x = centerLeft + Math.round(Math.cos(halfAngle) * radius);
+ var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;
+
+ var html = '' + text + "";
+ target.append(html);
+ var label = target.children('#pieLabel'+index);
+ var labelTop = (y - label.height()/2);
+ var labelLeft = (x - label.width()/2);
+ label.css('top', labelTop);
+ label.css('left', labelLeft);
+
+ // check to make sure that the label is not outside the canvas
+ if (0-labelTop>0 || 0-labelLeft>0 || canvas.height-(labelTop+label.height())<0 || canvas.width-(labelLeft+label.width())<0)
+ redraw = true;
+
+ if (options.series.pie.label.background.opacity != 0) {
+ // put in the transparent background separately to avoid blended labels and label boxes
+ var c = options.series.pie.label.background.color;
+ if (c == null) {
+ c = slice.color;
+ }
+ var pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;';
+ $('
').insertBefore(label).css('opacity', options.series.pie.label.background.opacity);
+ }
+ } // end individual label function
+ } // end drawLabels function
+ } // end drawPie function
+ } // end draw function
+
+ // Placed here because it needs to be accessed from multiple locations
+ function drawDonutHole(layer)
+ {
+ // draw donut hole
+ if(options.series.pie.innerRadius > 0)
+ {
+ // subtract the center
+ layer.save();
+ innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;
+ layer.globalCompositeOperation = 'destination-out'; // this does not work with excanvas, but it will fall back to using the stroke color
+ layer.beginPath();
+ layer.fillStyle = options.series.pie.stroke.color;
+ layer.arc(0,0,innerRadius,0,Math.PI*2,false);
+ layer.fill();
+ layer.closePath();
+ layer.restore();
+
+ // add inner stroke
+ layer.save();
+ layer.beginPath();
+ layer.strokeStyle = options.series.pie.stroke.color;
+ layer.arc(0,0,innerRadius,0,Math.PI*2,false);
+ layer.stroke();
+ layer.closePath();
+ layer.restore();
+ // TODO: add extra shadow inside hole (with a mask) if the pie is tilted.
+ }
+ }
+
+ //-- Additional Interactive related functions --
+
+ function isPointInPoly(poly, pt)
+ {
+ for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
+ ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))
+ && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])
+ && (c = !c);
+ return c;
+ }
+
+ function findNearbySlice(mouseX, mouseY)
+ {
+ var slices = plot.getData(),
+ options = plot.getOptions(),
+ radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
+
+ for (var i = 0; i < slices.length; ++i)
+ {
+ var s = slices[i];
+
+ if(s.pie.show)
+ {
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(0,0); // Center of the pie
+ //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here.
+ ctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false);
+ ctx.closePath();
+ x = mouseX-centerLeft;
+ y = mouseY-centerTop;
+ if(ctx.isPointInPath)
+ {
+ if (ctx.isPointInPath(mouseX-centerLeft, mouseY-centerTop))
+ {
+ //alert('found slice!');
+ ctx.restore();
+ return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};
+ }
+ }
+ else
+ {
+ // excanvas for IE doesn;t support isPointInPath, this is a workaround.
+ p1X = (radius * Math.cos(s.startAngle));
+ p1Y = (radius * Math.sin(s.startAngle));
+ p2X = (radius * Math.cos(s.startAngle+(s.angle/4)));
+ p2Y = (radius * Math.sin(s.startAngle+(s.angle/4)));
+ p3X = (radius * Math.cos(s.startAngle+(s.angle/2)));
+ p3Y = (radius * Math.sin(s.startAngle+(s.angle/2)));
+ p4X = (radius * Math.cos(s.startAngle+(s.angle/1.5)));
+ p4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5)));
+ p5X = (radius * Math.cos(s.startAngle+s.angle));
+ p5Y = (radius * Math.sin(s.startAngle+s.angle));
+ arrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]];
+ arrPoint = [x,y];
+ // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?
+ if(isPointInPoly(arrPoly, arrPoint))
+ {
+ ctx.restore();
+ return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};
+ }
+ }
+ ctx.restore();
+ }
+ }
+
+ return null;
+ }
+
+ function onMouseMove(e)
+ {
+ triggerClickHoverEvent('plothover', e);
+ }
+
+ function onClick(e)
+ {
+ triggerClickHoverEvent('plotclick', e);
+ }
+
+ // trigger click or hover event (they send the same parameters so we share their code)
+ function triggerClickHoverEvent(eventname, e)
+ {
+ var offset = plot.offset(),
+ canvasX = parseInt(e.pageX - offset.left),
+ canvasY = parseInt(e.pageY - offset.top),
+ item = findNearbySlice(canvasX, canvasY);
+
+ if (options.grid.autoHighlight)
+ {
+ // clear auto-highlights
+ for (var i = 0; i < highlights.length; ++i)
+ {
+ var h = highlights[i];
+ if (h.auto == eventname && !(item && h.series == item.series))
+ unhighlight(h.series);
+ }
+ }
+
+ // highlight the slice
+ if (item)
+ highlight(item.series, eventname);
+
+ // trigger any hover bind events
+ var pos = { pageX: e.pageX, pageY: e.pageY };
+ target.trigger(eventname, [ pos, item ]);
+ }
+
+ function highlight(s, auto)
+ {
+ if (typeof s == "number")
+ s = series[s];
+
+ var i = indexOfHighlight(s);
+ if (i == -1)
+ {
+ highlights.push({ series: s, auto: auto });
+ plot.triggerRedrawOverlay();
+ }
+ else if (!auto)
+ highlights[i].auto = false;
+ }
+
+ function unhighlight(s)
+ {
+ if (s == null)
+ {
+ highlights = [];
+ plot.triggerRedrawOverlay();
+ }
+
+ if (typeof s == "number")
+ s = series[s];
+
+ var i = indexOfHighlight(s);
+ if (i != -1)
+ {
+ highlights.splice(i, 1);
+ plot.triggerRedrawOverlay();
+ }
+ }
+
+ function indexOfHighlight(s)
+ {
+ for (var i = 0; i < highlights.length; ++i)
+ {
+ var h = highlights[i];
+ if (h.series == s)
+ return i;
+ }
+ return -1;
+ }
+
+ function drawOverlay(plot, octx)
+ {
+ //alert(options.series.pie.radius);
+ var options = plot.getOptions();
+ //alert(options.series.pie.radius);
+
+ var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
+
+ octx.save();
+ octx.translate(centerLeft, centerTop);
+ octx.scale(1, options.series.pie.tilt);
+
+ for (i = 0; i < highlights.length; ++i)
+ drawHighlight(highlights[i].series);
+
+ drawDonutHole(octx);
+
+ octx.restore();
+
+ function drawHighlight(series)
+ {
+ if (series.angle < 0) return;
+
+ //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();
+ octx.fillStyle = "rgba(255, 255, 255, "+options.series.pie.highlight.opacity+")"; // this is temporary until we have access to parseColor
+
+ octx.beginPath();
+ if (Math.abs(series.angle - Math.PI*2) > 0.000000001)
+ octx.moveTo(0,0); // Center of the pie
+ octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false);
+ octx.closePath();
+ octx.fill();
+ }
+
+ }
+
+ } // end init (plugin body)
+
+ // define pie specific options and their default values
+ var options = {
+ series: {
+ pie: {
+ show: false,
+ radius: 'auto', // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
+ innerRadius:0, /* for donut */
+ startAngle: 3/2,
+ tilt: 1,
+ offset: {
+ top: 0,
+ left: 'auto'
+ },
+ stroke: {
+ color: '#FFF',
+ width: 1
+ },
+ label: {
+ show: 'auto',
+ formatter: function(label, slice){
+ return '
'+label+' '+Math.round(slice.percent)+'%
';
+ }, // formatter function
+ radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)
+ background: {
+ color: null,
+ opacity: 0
+ },
+ threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow)
+ },
+ combine: {
+ threshold: -1, // percentage at which to combine little slices into one larger slice
+ color: null, // color to give the new slice (auto-generated if null)
+ label: 'Other' // label to give the new slice
+ },
+ highlight: {
+ //color: '#FFF', // will add this functionality once parseColor is available
+ opacity: 0.5
+ }
+ }
+ }
+ };
+
+ $.plot.plugins.push({
+ init: init,
+ options: options,
+ name: "pie",
+ version: "1.0"
+ });
+})(jQuery);
diff --git a/data/templates/js/jquery.flot.selection.js b/data/templates/js/jquery.flot.selection.js
new file mode 100644
index 0000000..7f7b326
--- /dev/null
+++ b/data/templates/js/jquery.flot.selection.js
@@ -0,0 +1,344 @@
+/*
+Flot plugin for selecting regions.
+
+The plugin defines the following options:
+
+ selection: {
+ mode: null or "x" or "y" or "xy",
+ color: color
+ }
+
+Selection support is enabled by setting the mode to one of "x", "y" or
+"xy". In "x" mode, the user will only be able to specify the x range,
+similarly for "y" mode. For "xy", the selection becomes a rectangle
+where both ranges can be specified. "color" is color of the selection
+(if you need to change the color later on, you can get to it with
+plot.getOptions().selection.color).
+
+When selection support is enabled, a "plotselected" event will be
+emitted on the DOM element you passed into the plot function. The
+event handler gets a parameter with the ranges selected on the axes,
+like this:
+
+ placeholder.bind("plotselected", function(event, ranges) {
+ alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
+ // similar for yaxis - with multiple axes, the extra ones are in
+ // x2axis, x3axis, ...
+ });
+
+The "plotselected" event is only fired when the user has finished
+making the selection. A "plotselecting" event is fired during the
+process with the same parameters as the "plotselected" event, in case
+you want to know what's happening while it's happening,
+
+A "plotunselected" event with no arguments is emitted when the user
+clicks the mouse to remove the selection.
+
+The plugin allso adds the following methods to the plot object:
+
+- setSelection(ranges, preventEvent)
+
+ Set the selection rectangle. The passed in ranges is on the same
+ form as returned in the "plotselected" event. If the selection mode
+ is "x", you should put in either an xaxis range, if the mode is "y"
+ you need to put in an yaxis range and both xaxis and yaxis if the
+ selection mode is "xy", like this:
+
+ setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });
+
+ setSelection will trigger the "plotselected" event when called. If
+ you don't want that to happen, e.g. if you're inside a
+ "plotselected" handler, pass true as the second parameter. If you
+ are using multiple axes, you can specify the ranges on any of those,
+ e.g. as x2axis/x3axis/... instead of xaxis, the plugin picks the
+ first one it sees.
+
+- clearSelection(preventEvent)
+
+ Clear the selection rectangle. Pass in true to avoid getting a
+ "plotunselected" event.
+
+- getSelection()
+
+ Returns the current selection in the same format as the
+ "plotselected" event. If there's currently no selection, the
+ function returns null.
+
+*/
+
+(function ($) {
+ function init(plot) {
+ var selection = {
+ first: { x: -1, y: -1}, second: { x: -1, y: -1},
+ show: false,
+ active: false
+ };
+
+ // FIXME: The drag handling implemented here should be
+ // abstracted out, there's some similar code from a library in
+ // the navigation plugin, this should be massaged a bit to fit
+ // the Flot cases here better and reused. Doing this would
+ // make this plugin much slimmer.
+ var savedhandlers = {};
+
+ var mouseUpHandler = null;
+
+ function onMouseMove(e) {
+ if (selection.active) {
+ updateSelection(e);
+
+ plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]);
+ }
+ }
+
+ function onMouseDown(e) {
+ if (e.which != 1) // only accept left-click
+ return;
+
+ // cancel out any text selections
+ document.body.focus();
+
+ // prevent text selection and drag in old-school browsers
+ if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) {
+ savedhandlers.onselectstart = document.onselectstart;
+ document.onselectstart = function () { return false; };
+ }
+ if (document.ondrag !== undefined && savedhandlers.ondrag == null) {
+ savedhandlers.ondrag = document.ondrag;
+ document.ondrag = function () { return false; };
+ }
+
+ setSelectionPos(selection.first, e);
+
+ selection.active = true;
+
+ // this is a bit silly, but we have to use a closure to be
+ // able to whack the same handler again
+ mouseUpHandler = function (e) { onMouseUp(e); };
+
+ $(document).one("mouseup", mouseUpHandler);
+ }
+
+ function onMouseUp(e) {
+ mouseUpHandler = null;
+
+ // revert drag stuff for old-school browsers
+ if (document.onselectstart !== undefined)
+ document.onselectstart = savedhandlers.onselectstart;
+ if (document.ondrag !== undefined)
+ document.ondrag = savedhandlers.ondrag;
+
+ // no more dragging
+ selection.active = false;
+ updateSelection(e);
+
+ if (selectionIsSane())
+ triggerSelectedEvent();
+ else {
+ // this counts as a clear
+ plot.getPlaceholder().trigger("plotunselected", [ ]);
+ plot.getPlaceholder().trigger("plotselecting", [ null ]);
+ }
+
+ return false;
+ }
+
+ function getSelection() {
+ if (!selectionIsSane())
+ return null;
+
+ var r = {}, c1 = selection.first, c2 = selection.second;
+ $.each(plot.getAxes(), function (name, axis) {
+ if (axis.used) {
+ var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]);
+ r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) };
+ }
+ });
+ return r;
+ }
+
+ function triggerSelectedEvent() {
+ var r = getSelection();
+
+ plot.getPlaceholder().trigger("plotselected", [ r ]);
+
+ // backwards-compat stuff, to be removed in future
+ if (r.xaxis && r.yaxis)
+ plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
+ }
+
+ function clamp(min, value, max) {
+ return value < min ? min: (value > max ? max: value);
+ }
+
+ function setSelectionPos(pos, e) {
+ var o = plot.getOptions();
+ var offset = plot.getPlaceholder().offset();
+ var plotOffset = plot.getPlotOffset();
+ pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width());
+ pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height());
+
+ if (o.selection.mode == "y")
+ pos.x = pos == selection.first ? 0 : plot.width();
+
+ if (o.selection.mode == "x")
+ pos.y = pos == selection.first ? 0 : plot.height();
+ }
+
+ function updateSelection(pos) {
+ if (pos.pageX == null)
+ return;
+
+ setSelectionPos(selection.second, pos);
+ if (selectionIsSane()) {
+ selection.show = true;
+ plot.triggerRedrawOverlay();
+ }
+ else
+ clearSelection(true);
+ }
+
+ function clearSelection(preventEvent) {
+ if (selection.show) {
+ selection.show = false;
+ plot.triggerRedrawOverlay();
+ if (!preventEvent)
+ plot.getPlaceholder().trigger("plotunselected", [ ]);
+ }
+ }
+
+ // function taken from markings support in Flot
+ function extractRange(ranges, coord) {
+ var axis, from, to, key, axes = plot.getAxes();
+
+ for (var k in axes) {
+ axis = axes[k];
+ if (axis.direction == coord) {
+ key = coord + axis.n + "axis";
+ if (!ranges[key] && axis.n == 1)
+ key = coord + "axis"; // support x1axis as xaxis
+ if (ranges[key]) {
+ from = ranges[key].from;
+ to = ranges[key].to;
+ break;
+ }
+ }
+ }
+
+ // backwards-compat stuff - to be removed in future
+ if (!ranges[key]) {
+ axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0];
+ from = ranges[coord + "1"];
+ to = ranges[coord + "2"];
+ }
+
+ // auto-reverse as an added bonus
+ if (from != null && to != null && from > to) {
+ var tmp = from;
+ from = to;
+ to = tmp;
+ }
+
+ return { from: from, to: to, axis: axis };
+ }
+
+ function setSelection(ranges, preventEvent) {
+ var axis, range, o = plot.getOptions();
+
+ if (o.selection.mode == "y") {
+ selection.first.x = 0;
+ selection.second.x = plot.width();
+ }
+ else {
+ range = extractRange(ranges, "x");
+
+ selection.first.x = range.axis.p2c(range.from);
+ selection.second.x = range.axis.p2c(range.to);
+ }
+
+ if (o.selection.mode == "x") {
+ selection.first.y = 0;
+ selection.second.y = plot.height();
+ }
+ else {
+ range = extractRange(ranges, "y");
+
+ selection.first.y = range.axis.p2c(range.from);
+ selection.second.y = range.axis.p2c(range.to);
+ }
+
+ selection.show = true;
+ plot.triggerRedrawOverlay();
+ if (!preventEvent && selectionIsSane())
+ triggerSelectedEvent();
+ }
+
+ function selectionIsSane() {
+ var minSize = 5;
+ return Math.abs(selection.second.x - selection.first.x) >= minSize &&
+ Math.abs(selection.second.y - selection.first.y) >= minSize;
+ }
+
+ plot.clearSelection = clearSelection;
+ plot.setSelection = setSelection;
+ plot.getSelection = getSelection;
+
+ plot.hooks.bindEvents.push(function(plot, eventHolder) {
+ var o = plot.getOptions();
+ if (o.selection.mode != null) {
+ eventHolder.mousemove(onMouseMove);
+ eventHolder.mousedown(onMouseDown);
+ }
+ });
+
+
+ plot.hooks.drawOverlay.push(function (plot, ctx) {
+ // draw selection
+ if (selection.show && selectionIsSane()) {
+ var plotOffset = plot.getPlotOffset();
+ var o = plot.getOptions();
+
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+
+ var c = $.color.parse(o.selection.color);
+
+ ctx.strokeStyle = c.scale('a', 0.8).toString();
+ ctx.lineWidth = 1;
+ ctx.lineJoin = "round";
+ ctx.fillStyle = c.scale('a', 0.4).toString();
+
+ var x = Math.min(selection.first.x, selection.second.x),
+ y = Math.min(selection.first.y, selection.second.y),
+ w = Math.abs(selection.second.x - selection.first.x),
+ h = Math.abs(selection.second.y - selection.first.y);
+
+ ctx.fillRect(x, y, w, h);
+ ctx.strokeRect(x, y, w, h);
+
+ ctx.restore();
+ }
+ });
+
+ plot.hooks.shutdown.push(function (plot, eventHolder) {
+ eventHolder.unbind("mousemove", onMouseMove);
+ eventHolder.unbind("mousedown", onMouseDown);
+
+ if (mouseUpHandler)
+ $(document).unbind("mouseup", mouseUpHandler);
+ });
+
+ }
+
+ $.plot.plugins.push({
+ init: init,
+ options: {
+ selection: {
+ mode: null, // one of null, "x", "y" or "xy"
+ color: "#e8cfac"
+ }
+ },
+ name: 'selection',
+ version: '1.1'
+ });
+})(jQuery);
diff --git a/data/templates/js/jquery.js b/data/templates/js/jquery.js
new file mode 100644
index 0000000..1c3aa82
--- /dev/null
+++ b/data/templates/js/jquery.js
@@ -0,0 +1,10346 @@
+/*!
+ * jQuery JavaScript Library v1.11.2
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-12-17T15:27Z
+ */
+
+(function( global, factory ) {
+
+ if ( typeof module === "object" && typeof module.exports === "object" ) {
+ // For CommonJS and CommonJS-like environments where a proper window is present,
+ // execute the factory and get jQuery
+ // For environments that do not inherently posses a window with a document
+ // (such as Node.js), expose a jQuery-making factory as module.exports
+ // This accentuates the need for the creation of a real window
+ // e.g. var jQuery = require("jquery")(window);
+ // See ticket #14549 for more info
+ module.exports = global.document ?
+ factory( global, true ) :
+ function( w ) {
+ if ( !w.document ) {
+ throw new Error( "jQuery requires a window with a document" );
+ }
+ return factory( w );
+ };
+ } else {
+ factory( global );
+ }
+
+// Pass this if window is not defined yet
+}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+
+// Can't do this because several apps including ASP.NET trace
+// the stack via arguments.caller.callee and Firefox dies if
+// you try to trace through "use strict" call chains. (#13335)
+// Support: Firefox 18+
+//
+
+var deletedIds = [];
+
+var slice = deletedIds.slice;
+
+var concat = deletedIds.concat;
+
+var push = deletedIds.push;
+
+var indexOf = deletedIds.indexOf;
+
+var class2type = {};
+
+var toString = class2type.toString;
+
+var hasOwn = class2type.hasOwnProperty;
+
+var support = {};
+
+
+
+var
+ version = "1.11.2",
+
+ // Define a local copy of jQuery
+ jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ // Need init if jQuery is called (just allow error to be thrown if not included)
+ return new jQuery.fn.init( selector, context );
+ },
+
+ // Support: Android<4.1, IE<9
+ // Make sure we trim BOM and NBSP
+ rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+ // Matches dashed string for camelizing
+ rmsPrefix = /^-ms-/,
+ rdashAlpha = /-([\da-z])/gi,
+
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ };
+
+jQuery.fn = jQuery.prototype = {
+ // The current version of jQuery being used
+ jquery: version,
+
+ constructor: jQuery,
+
+ // Start with an empty selector
+ selector: "",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ toArray: function() {
+ return slice.call( this );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num != null ?
+
+ // Return just the one element from the set
+ ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
+
+ // Return all the elements in a clean array
+ slice.call( this );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems ) {
+
+ // Build a new jQuery matched element set
+ var ret = jQuery.merge( this.constructor(), elems );
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+ ret.context = this.context;
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ) );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ eq: function( i ) {
+ var len = this.length,
+ j = +i + ( i < 0 ? len : 0 );
+ return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: deletedIds.sort,
+ splice: deletedIds.splice
+};
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var src, copyIsArray, copy, name, options, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+
+ // skip the boolean and the target
+ target = arguments[ i ] || {};
+ i++;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( i === length ) {
+ target = this;
+ i--;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ // Unique for each copy of jQuery on the page
+ expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
+
+ // Assume jQuery is ready without the ready module
+ isReady: true,
+
+ error: function( msg ) {
+ throw new Error( msg );
+ },
+
+ noop: function() {},
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ isWindow: function( obj ) {
+ /* jshint eqeqeq: false */
+ return obj != null && obj == obj.window;
+ },
+
+ isNumeric: function( obj ) {
+ // parseFloat NaNs numeric-cast false positives (null|true|false|"")
+ // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
+ // subtraction forces infinities to NaN
+ // adding 1 corrects loss of precision from parseFloat (#15100)
+ return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0;
+ },
+
+ isEmptyObject: function( obj ) {
+ var name;
+ for ( name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ isPlainObject: function( obj ) {
+ var key;
+
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call(obj, "constructor") &&
+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+ } catch ( e ) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Support: IE<9
+ // Handle iteration over inherited properties before own properties.
+ if ( support.ownLast ) {
+ for ( key in obj ) {
+ return hasOwn.call( obj, key );
+ }
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ },
+
+ type: function( obj ) {
+ if ( obj == null ) {
+ return obj + "";
+ }
+ return typeof obj === "object" || typeof obj === "function" ?
+ class2type[ toString.call(obj) ] || "object" :
+ typeof obj;
+ },
+
+ // Evaluates a script in a global context
+ // Workarounds based on findings by Jim Driscoll
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+ globalEval: function( data ) {
+ if ( data && jQuery.trim( data ) ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ // Convert dashed to camelCase; used by the css and data modules
+ // Microsoft forgot to hump their vendor prefix (#9572)
+ camelCase: function( string ) {
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+ },
+
+ // args is for internal usage only
+ each: function( obj, callback, args ) {
+ var value,
+ i = 0,
+ length = obj.length,
+ isArray = isArraylike( obj );
+
+ if ( args ) {
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback.apply( obj[ i ], args );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( i in obj ) {
+ value = callback.apply( obj[ i ], args );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback.call( obj[ i ], i, obj[ i ] );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( i in obj ) {
+ value = callback.call( obj[ i ], i, obj[ i ] );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return obj;
+ },
+
+ // Support: Android<4.1, IE<9
+ trim: function( text ) {
+ return text == null ?
+ "" :
+ ( text + "" ).replace( rtrim, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( arr, results ) {
+ var ret = results || [];
+
+ if ( arr != null ) {
+ if ( isArraylike( Object(arr) ) ) {
+ jQuery.merge( ret,
+ typeof arr === "string" ?
+ [ arr ] : arr
+ );
+ } else {
+ push.call( ret, arr );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, arr, i ) {
+ var len;
+
+ if ( arr ) {
+ if ( indexOf ) {
+ return indexOf.call( arr, elem, i );
+ }
+
+ len = arr.length;
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+ for ( ; i < len; i++ ) {
+ // Skip accessing in sparse arrays
+ if ( i in arr && arr[ i ] === elem ) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var len = +second.length,
+ j = 0,
+ i = first.length;
+
+ while ( j < len ) {
+ first[ i++ ] = second[ j++ ];
+ }
+
+ // Support: IE<9
+ // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
+ if ( len !== len ) {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, invert ) {
+ var callbackInverse,
+ matches = [],
+ i = 0,
+ length = elems.length,
+ callbackExpect = !invert;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( ; i < length; i++ ) {
+ callbackInverse = !callback( elems[ i ], i );
+ if ( callbackInverse !== callbackExpect ) {
+ matches.push( elems[ i ] );
+ }
+ }
+
+ return matches;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value,
+ i = 0,
+ length = elems.length,
+ isArray = isArraylike( elems ),
+ ret = [];
+
+ // Go through the array, translating each of the items to their new values
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret.push( value );
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( i in elems ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret.push( value );
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ var args, proxy, tmp;
+
+ if ( typeof context === "string" ) {
+ tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ args = slice.call( arguments, 2 );
+ proxy = function() {
+ return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ now: function() {
+ return +( new Date() );
+ },
+
+ // jQuery.support is not used in Core but other projects attach their
+ // properties to it so it needs to exist.
+ support: support
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+function isArraylike( obj ) {
+ var length = obj.length,
+ type = jQuery.type( obj );
+
+ if ( type === "function" || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ if ( obj.nodeType === 1 && length ) {
+ return true;
+ }
+
+ return type === "array" || length === 0 ||
+ typeof length === "number" && length > 0 && ( length - 1 ) in obj;
+}
+var Sizzle =
+/*!
+ * Sizzle CSS Selector Engine v2.2.0-pre
+ * http://sizzlejs.com/
+ *
+ * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-12-16
+ */
+(function( window ) {
+
+var i,
+ support,
+ Expr,
+ getText,
+ isXML,
+ tokenize,
+ compile,
+ select,
+ outermostContext,
+ sortInput,
+ hasDuplicate,
+
+ // Local document vars
+ setDocument,
+ document,
+ docElem,
+ documentIsHTML,
+ rbuggyQSA,
+ rbuggyMatches,
+ matches,
+ contains,
+
+ // Instance-specific data
+ expando = "sizzle" + 1 * new Date(),
+ preferredDoc = window.document,
+ dirruns = 0,
+ done = 0,
+ classCache = createCache(),
+ tokenCache = createCache(),
+ compilerCache = createCache(),
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ }
+ return 0;
+ },
+
+ // General-purpose constants
+ MAX_NEGATIVE = 1 << 31,
+
+ // Instance methods
+ hasOwn = ({}).hasOwnProperty,
+ arr = [],
+ pop = arr.pop,
+ push_native = arr.push,
+ push = arr.push,
+ slice = arr.slice,
+ // Use a stripped-down indexOf as it's faster than native
+ // http://jsperf.com/thor-indexof-vs-for/5
+ indexOf = function( list, elem ) {
+ var i = 0,
+ len = list.length;
+ for ( ; i < len; i++ ) {
+ if ( list[i] === elem ) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+
+ // Regular expressions
+
+ // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+ whitespace = "[\\x20\\t\\r\\n\\f]",
+ // http://www.w3.org/TR/css3-syntax/#characters
+ characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+
+ // Loosely modeled on CSS identifier characters
+ // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
+ // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+ identifier = characterEncoding.replace( "w", "w#" ),
+
+ // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
+ attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
+ // Operator (capture 2)
+ "*([*^$|!~]?=)" + whitespace +
+ // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
+ "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
+ "*\\]",
+
+ pseudos = ":(" + characterEncoding + ")(?:\\((" +
+ // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
+ // 1. quoted (capture 3; capture 4 or capture 5)
+ "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
+ // 2. simple (capture 6)
+ "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
+ // 3. anything else (capture 2)
+ ".*" +
+ ")\\)|)",
+
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+ rwhitespace = new RegExp( whitespace + "+", "g" ),
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+ rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+
+ rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
+
+ rpseudo = new RegExp( pseudos ),
+ ridentifier = new RegExp( "^" + identifier + "$" ),
+
+ matchExpr = {
+ "ID": new RegExp( "^#(" + characterEncoding + ")" ),
+ "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+ "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+ "ATTR": new RegExp( "^" + attributes ),
+ "PSEUDO": new RegExp( "^" + pseudos ),
+ "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+ "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+ // For use in libraries implementing .is()
+ // We use this for POS matching in `select`
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+ whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+ },
+
+ rinputs = /^(?:input|select|textarea|button)$/i,
+ rheader = /^h\d$/i,
+
+ rnative = /^[^{]+\{\s*\[native \w/,
+
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors
+ rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+ rsibling = /[+~]/,
+ rescape = /'|\\/g,
+
+ // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+ runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+ funescape = function( _, escaped, escapedWhitespace ) {
+ var high = "0x" + escaped - 0x10000;
+ // NaN means non-codepoint
+ // Support: Firefox<24
+ // Workaround erroneous numeric interpretation of +"0x"
+ return high !== high || escapedWhitespace ?
+ escaped :
+ high < 0 ?
+ // BMP codepoint
+ String.fromCharCode( high + 0x10000 ) :
+ // Supplemental Plane codepoint (surrogate pair)
+ String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+ },
+
+ // Used for iframes
+ // See setDocument()
+ // Removing the function wrapper causes a "Permission Denied"
+ // error in IE
+ unloadHandler = function() {
+ setDocument();
+ };
+
+// Optimize for push.apply( _, NodeList )
+try {
+ push.apply(
+ (arr = slice.call( preferredDoc.childNodes )),
+ preferredDoc.childNodes
+ );
+ // Support: Android<4.0
+ // Detect silently failing push.apply
+ arr[ preferredDoc.childNodes.length ].nodeType;
+} catch ( e ) {
+ push = { apply: arr.length ?
+
+ // Leverage slice if possible
+ function( target, els ) {
+ push_native.apply( target, slice.call(els) );
+ } :
+
+ // Support: IE<9
+ // Otherwise append directly
+ function( target, els ) {
+ var j = target.length,
+ i = 0;
+ // Can't trust NodeList.length
+ while ( (target[j++] = els[i++]) ) {}
+ target.length = j - 1;
+ }
+ };
+}
+
+function Sizzle( selector, context, results, seed ) {
+ var match, elem, m, nodeType,
+ // QSA vars
+ i, groups, old, nid, newContext, newSelector;
+
+ if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+ setDocument( context );
+ }
+
+ context = context || document;
+ results = results || [];
+ nodeType = context.nodeType;
+
+ if ( typeof selector !== "string" || !selector ||
+ nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
+
+ return results;
+ }
+
+ if ( !seed && documentIsHTML ) {
+
+ // Try to shortcut find operations when possible (e.g., not under DocumentFragment)
+ if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
+ // Speed-up: Sizzle("#ID")
+ if ( (m = match[1]) ) {
+ if ( nodeType === 9 ) {
+ elem = context.getElementById( m );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document (jQuery #6963)
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE, Opera, and Webkit return items
+ // by name instead of ID
+ if ( elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ } else {
+ return results;
+ }
+ } else {
+ // Context is not a document
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+ contains( context, elem ) && elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ }
+
+ // Speed-up: Sizzle("TAG")
+ } else if ( match[2] ) {
+ push.apply( results, context.getElementsByTagName( selector ) );
+ return results;
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( (m = match[3]) && support.getElementsByClassName ) {
+ push.apply( results, context.getElementsByClassName( m ) );
+ return results;
+ }
+ }
+
+ // QSA path
+ if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+ nid = old = expando;
+ newContext = context;
+ newSelector = nodeType !== 1 && selector;
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ groups = tokenize( selector );
+
+ if ( (old = context.getAttribute("id")) ) {
+ nid = old.replace( rescape, "\\$&" );
+ } else {
+ context.setAttribute( "id", nid );
+ }
+ nid = "[id='" + nid + "'] ";
+
+ i = groups.length;
+ while ( i-- ) {
+ groups[i] = nid + toSelector( groups[i] );
+ }
+ newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
+ newSelector = groups.join(",");
+ }
+
+ if ( newSelector ) {
+ try {
+ push.apply( results,
+ newContext.querySelectorAll( newSelector )
+ );
+ return results;
+ } catch(qsaError) {
+ } finally {
+ if ( !old ) {
+ context.removeAttribute("id");
+ }
+ }
+ }
+ }
+ }
+
+ // All others
+ return select( selector.replace( rtrim, "$1" ), context, results, seed );
+}
+
+/**
+ * Create key-value caches of limited size
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
+ * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ * deleting the oldest entry
+ */
+function createCache() {
+ var keys = [];
+
+ function cache( key, value ) {
+ // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+ if ( keys.push( key + " " ) > Expr.cacheLength ) {
+ // Only keep the most recent entries
+ delete cache[ keys.shift() ];
+ }
+ return (cache[ key + " " ] = value);
+ }
+ return cache;
+}
+
+/**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+function markFunction( fn ) {
+ fn[ expando ] = true;
+ return fn;
+}
+
+/**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+function assert( fn ) {
+ var div = document.createElement("div");
+
+ try {
+ return !!fn( div );
+ } catch (e) {
+ return false;
+ } finally {
+ // Remove from its parent by default
+ if ( div.parentNode ) {
+ div.parentNode.removeChild( div );
+ }
+ // release memory in IE
+ div = null;
+ }
+}
+
+/**
+ * Adds the same handler for all of the specified attrs
+ * @param {String} attrs Pipe-separated list of attributes
+ * @param {Function} handler The method that will be applied
+ */
+function addHandle( attrs, handler ) {
+ var arr = attrs.split("|"),
+ i = attrs.length;
+
+ while ( i-- ) {
+ Expr.attrHandle[ arr[i] ] = handler;
+ }
+}
+
+/**
+ * Checks document order of two siblings
+ * @param {Element} a
+ * @param {Element} b
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
+ */
+function siblingCheck( a, b ) {
+ var cur = b && a,
+ diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+ ( ~b.sourceIndex || MAX_NEGATIVE ) -
+ ( ~a.sourceIndex || MAX_NEGATIVE );
+
+ // Use IE sourceIndex if available on both nodes
+ if ( diff ) {
+ return diff;
+ }
+
+ // Check if b follows a
+ if ( cur ) {
+ while ( (cur = cur.nextSibling) ) {
+ if ( cur === b ) {
+ return -1;
+ }
+ }
+ }
+
+ return a ? 1 : -1;
+}
+
+/**
+ * Returns a function to use in pseudos for input types
+ * @param {String} type
+ */
+function createInputPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === type;
+ };
+}
+
+/**
+ * Returns a function to use in pseudos for buttons
+ * @param {String} type
+ */
+function createButtonPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && elem.type === type;
+ };
+}
+
+/**
+ * Returns a function to use in pseudos for positionals
+ * @param {Function} fn
+ */
+function createPositionalPseudo( fn ) {
+ return markFunction(function( argument ) {
+ argument = +argument;
+ return markFunction(function( seed, matches ) {
+ var j,
+ matchIndexes = fn( [], seed.length, argument ),
+ i = matchIndexes.length;
+
+ // Match elements found at the specified indexes
+ while ( i-- ) {
+ if ( seed[ (j = matchIndexes[i]) ] ) {
+ seed[j] = !(matches[j] = seed[j]);
+ }
+ }
+ });
+ });
+}
+
+/**
+ * Checks a node for validity as a Sizzle context
+ * @param {Element|Object=} context
+ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
+ */
+function testContext( context ) {
+ return context && typeof context.getElementsByTagName !== "undefined" && context;
+}
+
+// Expose support vars for convenience
+support = Sizzle.support = {};
+
+/**
+ * Detects XML nodes
+ * @param {Element|Object} elem An element or a document
+ * @returns {Boolean} True iff elem is a non-HTML XML node
+ */
+isXML = Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+/**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+setDocument = Sizzle.setDocument = function( node ) {
+ var hasCompare, parent,
+ doc = node ? node.ownerDocument || node : preferredDoc;
+
+ // If no document and documentElement is available, return
+ if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+ return document;
+ }
+
+ // Set our document
+ document = doc;
+ docElem = doc.documentElement;
+ parent = doc.defaultView;
+
+ // Support: IE>8
+ // If iframe document is assigned to "document" variable and if iframe has been reloaded,
+ // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
+ // IE6-8 do not support the defaultView property so parent will be undefined
+ if ( parent && parent !== parent.top ) {
+ // IE11 does not have attachEvent, so all must suffer
+ if ( parent.addEventListener ) {
+ parent.addEventListener( "unload", unloadHandler, false );
+ } else if ( parent.attachEvent ) {
+ parent.attachEvent( "onunload", unloadHandler );
+ }
+ }
+
+ /* Support tests
+ ---------------------------------------------------------------------- */
+ documentIsHTML = !isXML( doc );
+
+ /* Attributes
+ ---------------------------------------------------------------------- */
+
+ // Support: IE<8
+ // Verify that getAttribute really returns attributes and not properties
+ // (excepting IE8 booleans)
+ support.attributes = assert(function( div ) {
+ div.className = "i";
+ return !div.getAttribute("className");
+ });
+
+ /* getElement(s)By*
+ ---------------------------------------------------------------------- */
+
+ // Check if getElementsByTagName("*") returns only elements
+ support.getElementsByTagName = assert(function( div ) {
+ div.appendChild( doc.createComment("") );
+ return !div.getElementsByTagName("*").length;
+ });
+
+ // Support: IE<9
+ support.getElementsByClassName = rnative.test( doc.getElementsByClassName );
+
+ // Support: IE<10
+ // Check if getElementById returns elements by name
+ // The broken getElementById methods don't pick up programatically-set names,
+ // so use a roundabout getElementsByName test
+ support.getById = assert(function( div ) {
+ docElem.appendChild( div ).id = expando;
+ return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
+ });
+
+ // ID find and filter
+ if ( support.getById ) {
+ Expr.find["ID"] = function( id, context ) {
+ if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
+ var m = context.getElementById( id );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [ m ] : [];
+ }
+ };
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ return elem.getAttribute("id") === attrId;
+ };
+ };
+ } else {
+ // Support: IE6/7
+ // getElementById is not reliable as a find shortcut
+ delete Expr.find["ID"];
+
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+ return node && node.value === attrId;
+ };
+ };
+ }
+
+ // Tag
+ Expr.find["TAG"] = support.getElementsByTagName ?
+ function( tag, context ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( tag );
+
+ // DocumentFragment nodes don't have gEBTN
+ } else if ( support.qsa ) {
+ return context.querySelectorAll( tag );
+ }
+ } :
+
+ function( tag, context ) {
+ var elem,
+ tmp = [],
+ i = 0,
+ // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
+ results = context.getElementsByTagName( tag );
+
+ // Filter out possible comments
+ if ( tag === "*" ) {
+ while ( (elem = results[i++]) ) {
+ if ( elem.nodeType === 1 ) {
+ tmp.push( elem );
+ }
+ }
+
+ return tmp;
+ }
+ return results;
+ };
+
+ // Class
+ Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+ if ( documentIsHTML ) {
+ return context.getElementsByClassName( className );
+ }
+ };
+
+ /* QSA/matchesSelector
+ ---------------------------------------------------------------------- */
+
+ // QSA and matchesSelector support
+
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+ rbuggyMatches = [];
+
+ // qSa(:focus) reports false when true (Chrome 21)
+ // We allow this because of a bug in IE8/9 that throws an error
+ // whenever `document.activeElement` is accessed on an iframe
+ // So, we allow :focus to pass through QSA all the time to avoid the IE error
+ // See http://bugs.jquery.com/ticket/13378
+ rbuggyQSA = [];
+
+ if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
+ // Build QSA regex
+ // Regex strategy adopted from Diego Perini
+ assert(function( div ) {
+ // Select is set to empty string on purpose
+ // This is to test IE's treatment of not explicitly
+ // setting a boolean content attribute,
+ // since its presence should be enough
+ // http://bugs.jquery.com/ticket/12359
+ docElem.appendChild( div ).innerHTML = "" +
+ "";
+
+ // Support: IE8, Opera 11-12.16
+ // Nothing should be selected when empty strings follow ^= or $= or *=
+ // The test attribute must be unknown in Opera but "safe" for WinRT
+ // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
+ if ( div.querySelectorAll("[msallowcapture^='']").length ) {
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+ }
+
+ // Support: IE8
+ // Boolean attributes and "value" are not treated correctly
+ if ( !div.querySelectorAll("[selected]").length ) {
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+ }
+
+ // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+
+ if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
+ rbuggyQSA.push("~=");
+ }
+
+ // Webkit/Opera - :checked should return selected option elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":checked").length ) {
+ rbuggyQSA.push(":checked");
+ }
+
+ // Support: Safari 8+, iOS 8+
+ // https://bugs.webkit.org/show_bug.cgi?id=136851
+ // In-page `selector#id sibing-combinator selector` fails
+ if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) {
+ rbuggyQSA.push(".#.+[+~]");
+ }
+ });
+
+ assert(function( div ) {
+ // Support: Windows 8 Native Apps
+ // The type and name attributes are restricted during .innerHTML assignment
+ var input = doc.createElement("input");
+ input.setAttribute( "type", "hidden" );
+ div.appendChild( input ).setAttribute( "name", "D" );
+
+ // Support: IE8
+ // Enforce case-sensitivity of name attribute
+ if ( div.querySelectorAll("[name=d]").length ) {
+ rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
+ }
+
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":enabled").length ) {
+ rbuggyQSA.push( ":enabled", ":disabled" );
+ }
+
+ // Opera 10-11 does not throw on post-comma invalid pseudos
+ div.querySelectorAll("*,:x");
+ rbuggyQSA.push(",.*:");
+ });
+ }
+
+ if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
+ docElem.webkitMatchesSelector ||
+ docElem.mozMatchesSelector ||
+ docElem.oMatchesSelector ||
+ docElem.msMatchesSelector) )) ) {
+
+ assert(function( div ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9)
+ support.disconnectedMatch = matches.call( div, "div" );
+
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( div, "[s!='']:x" );
+ rbuggyMatches.push( "!=", pseudos );
+ });
+ }
+
+ rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+ rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
+
+ /* Contains
+ ---------------------------------------------------------------------- */
+ hasCompare = rnative.test( docElem.compareDocumentPosition );
+
+ // Element contains another
+ // Purposefully does not implement inclusive descendent
+ // As in, an element does not contain itself
+ contains = hasCompare || rnative.test( docElem.contains ) ?
+ function( a, b ) {
+ var adown = a.nodeType === 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return a === bup || !!( bup && bup.nodeType === 1 && (
+ adown.contains ?
+ adown.contains( bup ) :
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+ ));
+ } :
+ function( a, b ) {
+ if ( b ) {
+ while ( (b = b.parentNode) ) {
+ if ( b === a ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ /* Sorting
+ ---------------------------------------------------------------------- */
+
+ // Document order sorting
+ sortOrder = hasCompare ?
+ function( a, b ) {
+
+ // Flag for duplicate removal
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ // Sort on method existence if only one input has compareDocumentPosition
+ var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
+ if ( compare ) {
+ return compare;
+ }
+
+ // Calculate position if both inputs belong to the same document
+ compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
+ a.compareDocumentPosition( b ) :
+
+ // Otherwise we know they are disconnected
+ 1;
+
+ // Disconnected nodes
+ if ( compare & 1 ||
+ (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
+
+ // Choose the first element that is related to our preferred document
+ if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
+ return -1;
+ }
+ if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
+ return 1;
+ }
+
+ // Maintain original order
+ return sortInput ?
+ ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
+ 0;
+ }
+
+ return compare & 4 ? -1 : 1;
+ } :
+ function( a, b ) {
+ // Exit early if the nodes are identical
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ var cur,
+ i = 0,
+ aup = a.parentNode,
+ bup = b.parentNode,
+ ap = [ a ],
+ bp = [ b ];
+
+ // Parentless nodes are either documents or disconnected
+ if ( !aup || !bup ) {
+ return a === doc ? -1 :
+ b === doc ? 1 :
+ aup ? -1 :
+ bup ? 1 :
+ sortInput ?
+ ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
+ 0;
+
+ // If the nodes are siblings, we can do a quick check
+ } else if ( aup === bup ) {
+ return siblingCheck( a, b );
+ }
+
+ // Otherwise we need full lists of their ancestors for comparison
+ cur = a;
+ while ( (cur = cur.parentNode) ) {
+ ap.unshift( cur );
+ }
+ cur = b;
+ while ( (cur = cur.parentNode) ) {
+ bp.unshift( cur );
+ }
+
+ // Walk down the tree looking for a discrepancy
+ while ( ap[i] === bp[i] ) {
+ i++;
+ }
+
+ return i ?
+ // Do a sibling check if the nodes have a common ancestor
+ siblingCheck( ap[i], bp[i] ) :
+
+ // Otherwise nodes in our document sort first
+ ap[i] === preferredDoc ? -1 :
+ bp[i] === preferredDoc ? 1 :
+ 0;
+ };
+
+ return doc;
+};
+
+Sizzle.matches = function( expr, elements ) {
+ return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
+
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace( rattributeQuotes, "='$1']" );
+
+ if ( support.matchesSelector && documentIsHTML &&
+ ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+ ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
+
+ try {
+ var ret = matches.call( elem, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || support.disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9
+ elem.document && elem.document.nodeType !== 11 ) {
+ return ret;
+ }
+ } catch (e) {}
+ }
+
+ return Sizzle( expr, document, null, [ elem ] ).length > 0;
+};
+
+Sizzle.contains = function( context, elem ) {
+ // Set document vars if needed
+ if ( ( context.ownerDocument || context ) !== document ) {
+ setDocument( context );
+ }
+ return contains( context, elem );
+};
+
+Sizzle.attr = function( elem, name ) {
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
+
+ var fn = Expr.attrHandle[ name.toLowerCase() ],
+ // Don't get fooled by Object.prototype properties (jQuery #13807)
+ val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+ fn( elem, name, !documentIsHTML ) :
+ undefined;
+
+ return val !== undefined ?
+ val :
+ support.attributes || !documentIsHTML ?
+ elem.getAttribute( name ) :
+ (val = elem.getAttributeNode(name)) && val.specified ?
+ val.value :
+ null;
+};
+
+Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Document sorting and removing duplicates
+ * @param {ArrayLike} results
+ */
+Sizzle.uniqueSort = function( results ) {
+ var elem,
+ duplicates = [],
+ j = 0,
+ i = 0;
+
+ // Unless we *know* we can detect duplicates, assume their presence
+ hasDuplicate = !support.detectDuplicates;
+ sortInput = !support.sortStable && results.slice( 0 );
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ while ( (elem = results[i++]) ) {
+ if ( elem === results[ i ] ) {
+ j = duplicates.push( i );
+ }
+ }
+ while ( j-- ) {
+ results.splice( duplicates[ j ], 1 );
+ }
+ }
+
+ // Clear input after sorting to release objects
+ // See https://github.com/jquery/sizzle/pull/225
+ sortInput = null;
+
+ return results;
+};
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+ var node,
+ ret = "",
+ i = 0,
+ nodeType = elem.nodeType;
+
+ if ( !nodeType ) {
+ // If no nodeType, this is expected to be an array
+ while ( (node = elem[i++]) ) {
+ // Do not traverse comment nodes
+ ret += getText( node );
+ }
+ } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent for elements
+ // innerText usage removed for consistency of new lines (jQuery #11153)
+ if ( typeof elem.textContent === "string" ) {
+ return elem.textContent;
+ } else {
+ // Traverse its children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ // Do not include comment or processing instruction nodes
+
+ return ret;
+};
+
+Expr = Sizzle.selectors = {
+
+ // Can be adjusted by the user
+ cacheLength: 50,
+
+ createPseudo: markFunction,
+
+ match: matchExpr,
+
+ attrHandle: {},
+
+ find: {},
+
+ relative: {
+ ">": { dir: "parentNode", first: true },
+ " ": { dir: "parentNode" },
+ "+": { dir: "previousSibling", first: true },
+ "~": { dir: "previousSibling" }
+ },
+
+ preFilter: {
+ "ATTR": function( match ) {
+ match[1] = match[1].replace( runescape, funescape );
+
+ // Move the given value to match[3] whether quoted or unquoted
+ match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
+
+ if ( match[2] === "~=" ) {
+ match[3] = " " + match[3] + " ";
+ }
+
+ return match.slice( 0, 4 );
+ },
+
+ "CHILD": function( match ) {
+ /* matches from matchExpr["CHILD"]
+ 1 type (only|nth|...)
+ 2 what (child|of-type)
+ 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+ 4 xn-component of xn+y argument ([+-]?\d*n|)
+ 5 sign of xn-component
+ 6 x of xn-component
+ 7 sign of y-component
+ 8 y of y-component
+ */
+ match[1] = match[1].toLowerCase();
+
+ if ( match[1].slice( 0, 3 ) === "nth" ) {
+ // nth-* requires argument
+ if ( !match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // numeric x and y parameters for Expr.filter.CHILD
+ // remember that false/true cast respectively to 0/1
+ match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+ match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+ // other types prohibit arguments
+ } else if ( match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ return match;
+ },
+
+ "PSEUDO": function( match ) {
+ var excess,
+ unquoted = !match[6] && match[2];
+
+ if ( matchExpr["CHILD"].test( match[0] ) ) {
+ return null;
+ }
+
+ // Accept quoted arguments as-is
+ if ( match[3] ) {
+ match[2] = match[4] || match[5] || "";
+
+ // Strip excess characters from unquoted arguments
+ } else if ( unquoted && rpseudo.test( unquoted ) &&
+ // Get excess from tokenize (recursively)
+ (excess = tokenize( unquoted, true )) &&
+ // advance to the next closing parenthesis
+ (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+ // excess is a negative index
+ match[0] = match[0].slice( 0, excess );
+ match[2] = unquoted.slice( 0, excess );
+ }
+
+ // Return only captures needed by the pseudo filter method (type and argument)
+ return match.slice( 0, 3 );
+ }
+ },
+
+ filter: {
+
+ "TAG": function( nodeNameSelector ) {
+ var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+ return nodeNameSelector === "*" ?
+ function() { return true; } :
+ function( elem ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+ };
+ },
+
+ "CLASS": function( className ) {
+ var pattern = classCache[ className + " " ];
+
+ return pattern ||
+ (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+ classCache( className, function( elem ) {
+ return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
+ });
+ },
+
+ "ATTR": function( name, operator, check ) {
+ return function( elem ) {
+ var result = Sizzle.attr( elem, name );
+
+ if ( result == null ) {
+ return operator === "!=";
+ }
+ if ( !operator ) {
+ return true;
+ }
+
+ result += "";
+
+ return operator === "=" ? result === check :
+ operator === "!=" ? result !== check :
+ operator === "^=" ? check && result.indexOf( check ) === 0 :
+ operator === "*=" ? check && result.indexOf( check ) > -1 :
+ operator === "$=" ? check && result.slice( -check.length ) === check :
+ operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
+ operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+ false;
+ };
+ },
+
+ "CHILD": function( type, what, argument, first, last ) {
+ var simple = type.slice( 0, 3 ) !== "nth",
+ forward = type.slice( -4 ) !== "last",
+ ofType = what === "of-type";
+
+ return first === 1 && last === 0 ?
+
+ // Shortcut for :nth-*(n)
+ function( elem ) {
+ return !!elem.parentNode;
+ } :
+
+ function( elem, context, xml ) {
+ var cache, outerCache, node, diff, nodeIndex, start,
+ dir = simple !== forward ? "nextSibling" : "previousSibling",
+ parent = elem.parentNode,
+ name = ofType && elem.nodeName.toLowerCase(),
+ useCache = !xml && !ofType;
+
+ if ( parent ) {
+
+ // :(first|last|only)-(child|of-type)
+ if ( simple ) {
+ while ( dir ) {
+ node = elem;
+ while ( (node = node[ dir ]) ) {
+ if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
+ return false;
+ }
+ }
+ // Reverse direction for :only-* (if we haven't yet done so)
+ start = dir = type === "only" && !start && "nextSibling";
+ }
+ return true;
+ }
+
+ start = [ forward ? parent.firstChild : parent.lastChild ];
+
+ // non-xml :nth-child(...) stores cache data on `parent`
+ if ( forward && useCache ) {
+ // Seek `elem` from a previously-cached index
+ outerCache = parent[ expando ] || (parent[ expando ] = {});
+ cache = outerCache[ type ] || [];
+ nodeIndex = cache[0] === dirruns && cache[1];
+ diff = cache[0] === dirruns && cache[2];
+ node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+ // Fallback to seeking `elem` from the start
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ // When found, cache indexes on `parent` and break
+ if ( node.nodeType === 1 && ++diff && node === elem ) {
+ outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+ break;
+ }
+ }
+
+ // Use previously-cached element index if available
+ } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+ diff = cache[1];
+
+ // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+ } else {
+ // Use the same loop as above to seek `elem` from the start
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+ // Cache the index of each encountered element
+ if ( useCache ) {
+ (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+ }
+
+ if ( node === elem ) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Incorporate the offset, then check against cycle size
+ diff -= last;
+ return diff === first || ( diff % first === 0 && diff / first >= 0 );
+ }
+ };
+ },
+
+ "PSEUDO": function( pseudo, argument ) {
+ // pseudo-class names are case-insensitive
+ // http://www.w3.org/TR/selectors/#pseudo-classes
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+ // Remember that setFilters inherits from pseudos
+ var args,
+ fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+ Sizzle.error( "unsupported pseudo: " + pseudo );
+
+ // The user may use createPseudo to indicate that
+ // arguments are needed to create the filter function
+ // just as Sizzle does
+ if ( fn[ expando ] ) {
+ return fn( argument );
+ }
+
+ // But maintain support for old signatures
+ if ( fn.length > 1 ) {
+ args = [ pseudo, pseudo, "", argument ];
+ return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+ markFunction(function( seed, matches ) {
+ var idx,
+ matched = fn( seed, argument ),
+ i = matched.length;
+ while ( i-- ) {
+ idx = indexOf( seed, matched[i] );
+ seed[ idx ] = !( matches[ idx ] = matched[i] );
+ }
+ }) :
+ function( elem ) {
+ return fn( elem, 0, args );
+ };
+ }
+
+ return fn;
+ }
+ },
+
+ pseudos: {
+ // Potentially complex pseudos
+ "not": markFunction(function( selector ) {
+ // Trim the selector passed to compile
+ // to avoid treating leading and trailing
+ // spaces as combinators
+ var input = [],
+ results = [],
+ matcher = compile( selector.replace( rtrim, "$1" ) );
+
+ return matcher[ expando ] ?
+ markFunction(function( seed, matches, context, xml ) {
+ var elem,
+ unmatched = matcher( seed, null, xml, [] ),
+ i = seed.length;
+
+ // Match elements unmatched by `matcher`
+ while ( i-- ) {
+ if ( (elem = unmatched[i]) ) {
+ seed[i] = !(matches[i] = elem);
+ }
+ }
+ }) :
+ function( elem, context, xml ) {
+ input[0] = elem;
+ matcher( input, null, xml, results );
+ // Don't keep the element (issue #299)
+ input[0] = null;
+ return !results.pop();
+ };
+ }),
+
+ "has": markFunction(function( selector ) {
+ return function( elem ) {
+ return Sizzle( selector, elem ).length > 0;
+ };
+ }),
+
+ "contains": markFunction(function( text ) {
+ text = text.replace( runescape, funescape );
+ return function( elem ) {
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+ };
+ }),
+
+ // "Whether an element is represented by a :lang() selector
+ // is based solely on the element's language value
+ // being equal to the identifier C,
+ // or beginning with the identifier C immediately followed by "-".
+ // The matching of C against the element's language value is performed case-insensitively.
+ // The identifier C does not have to be a valid language name."
+ // http://www.w3.org/TR/selectors/#lang-pseudo
+ "lang": markFunction( function( lang ) {
+ // lang value must be a valid identifier
+ if ( !ridentifier.test(lang || "") ) {
+ Sizzle.error( "unsupported lang: " + lang );
+ }
+ lang = lang.replace( runescape, funescape ).toLowerCase();
+ return function( elem ) {
+ var elemLang;
+ do {
+ if ( (elemLang = documentIsHTML ?
+ elem.lang :
+ elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
+
+ elemLang = elemLang.toLowerCase();
+ return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+ }
+ } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+ return false;
+ };
+ }),
+
+ // Miscellaneous
+ "target": function( elem ) {
+ var hash = window.location && window.location.hash;
+ return hash && hash.slice( 1 ) === elem.id;
+ },
+
+ "root": function( elem ) {
+ return elem === docElem;
+ },
+
+ "focus": function( elem ) {
+ return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+ },
+
+ // Boolean properties
+ "enabled": function( elem ) {
+ return elem.disabled === false;
+ },
+
+ "disabled": function( elem ) {
+ return elem.disabled === true;
+ },
+
+ "checked": function( elem ) {
+ // In CSS3, :checked should return both checked and selected elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ var nodeName = elem.nodeName.toLowerCase();
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+ },
+
+ "selected": function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ // Contents
+ "empty": function( elem ) {
+ // http://www.w3.org/TR/selectors/#empty-pseudo
+ // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
+ // but not by others (comment: 8; processing instruction: 7; etc.)
+ // nodeType < 6 works because attributes (2) do not appear as children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ if ( elem.nodeType < 6 ) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ "parent": function( elem ) {
+ return !Expr.pseudos["empty"]( elem );
+ },
+
+ // Element/input types
+ "header": function( elem ) {
+ return rheader.test( elem.nodeName );
+ },
+
+ "input": function( elem ) {
+ return rinputs.test( elem.nodeName );
+ },
+
+ "button": function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === "button" || name === "button";
+ },
+
+ "text": function( elem ) {
+ var attr;
+ return elem.nodeName.toLowerCase() === "input" &&
+ elem.type === "text" &&
+
+ // Support: IE<8
+ // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
+ },
+
+ // Position-in-collection
+ "first": createPositionalPseudo(function() {
+ return [ 0 ];
+ }),
+
+ "last": createPositionalPseudo(function( matchIndexes, length ) {
+ return [ length - 1 ];
+ }),
+
+ "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ return [ argument < 0 ? argument + length : argument ];
+ }),
+
+ "even": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 0;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "odd": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 1;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; --i >= 0; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; ++i < length; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ })
+ }
+};
+
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Add button/input type pseudos
+for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+ Expr.pseudos[ i ] = createInputPseudo( i );
+}
+for ( i in { submit: true, reset: true } ) {
+ Expr.pseudos[ i ] = createButtonPseudo( i );
+}
+
+// Easy API for creating new setFilters
+function setFilters() {}
+setFilters.prototype = Expr.filters = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
+ var matched, match, tokens, type,
+ soFar, groups, preFilters,
+ cached = tokenCache[ selector + " " ];
+
+ if ( cached ) {
+ return parseOnly ? 0 : cached.slice( 0 );
+ }
+
+ soFar = selector;
+ groups = [];
+ preFilters = Expr.preFilter;
+
+ while ( soFar ) {
+
+ // Comma and first run
+ if ( !matched || (match = rcomma.exec( soFar )) ) {
+ if ( match ) {
+ // Don't consume trailing commas as valid
+ soFar = soFar.slice( match[0].length ) || soFar;
+ }
+ groups.push( (tokens = []) );
+ }
+
+ matched = false;
+
+ // Combinators
+ if ( (match = rcombinators.exec( soFar )) ) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ // Cast descendant combinators to space
+ type: match[0].replace( rtrim, " " )
+ });
+ soFar = soFar.slice( matched.length );
+ }
+
+ // Filters
+ for ( type in Expr.filter ) {
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+ (match = preFilters[ type ]( match ))) ) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ type: type,
+ matches: match
+ });
+ soFar = soFar.slice( matched.length );
+ }
+ }
+
+ if ( !matched ) {
+ break;
+ }
+ }
+
+ // Return the length of the invalid excess
+ // if we're just parsing
+ // Otherwise, throw an error or return tokens
+ return parseOnly ?
+ soFar.length :
+ soFar ?
+ Sizzle.error( selector ) :
+ // Cache the tokens
+ tokenCache( selector, groups ).slice( 0 );
+};
+
+function toSelector( tokens ) {
+ var i = 0,
+ len = tokens.length,
+ selector = "";
+ for ( ; i < len; i++ ) {
+ selector += tokens[i].value;
+ }
+ return selector;
+}
+
+function addCombinator( matcher, combinator, base ) {
+ var dir = combinator.dir,
+ checkNonElements = base && dir === "parentNode",
+ doneName = done++;
+
+ return combinator.first ?
+ // Check against closest ancestor/preceding element
+ function( elem, context, xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ return matcher( elem, context, xml );
+ }
+ }
+ } :
+
+ // Check against all ancestor/preceding elements
+ function( elem, context, xml ) {
+ var oldCache, outerCache,
+ newCache = [ dirruns, doneName ];
+
+ // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+ if ( xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ if ( matcher( elem, context, xml ) ) {
+ return true;
+ }
+ }
+ }
+ } else {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ outerCache = elem[ expando ] || (elem[ expando ] = {});
+ if ( (oldCache = outerCache[ dir ]) &&
+ oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
+
+ // Assign to newCache so results back-propagate to previous elements
+ return (newCache[ 2 ] = oldCache[ 2 ]);
+ } else {
+ // Reuse newcache so results back-propagate to previous elements
+ outerCache[ dir ] = newCache;
+
+ // A match means we're done; a fail means we have to keep checking
+ if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ };
+}
+
+function elementMatcher( matchers ) {
+ return matchers.length > 1 ?
+ function( elem, context, xml ) {
+ var i = matchers.length;
+ while ( i-- ) {
+ if ( !matchers[i]( elem, context, xml ) ) {
+ return false;
+ }
+ }
+ return true;
+ } :
+ matchers[0];
+}
+
+function multipleContexts( selector, contexts, results ) {
+ var i = 0,
+ len = contexts.length;
+ for ( ; i < len; i++ ) {
+ Sizzle( selector, contexts[i], results );
+ }
+ return results;
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+ var elem,
+ newUnmatched = [],
+ i = 0,
+ len = unmatched.length,
+ mapped = map != null;
+
+ for ( ; i < len; i++ ) {
+ if ( (elem = unmatched[i]) ) {
+ if ( !filter || filter( elem, context, xml ) ) {
+ newUnmatched.push( elem );
+ if ( mapped ) {
+ map.push( i );
+ }
+ }
+ }
+ }
+
+ return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+ if ( postFilter && !postFilter[ expando ] ) {
+ postFilter = setMatcher( postFilter );
+ }
+ if ( postFinder && !postFinder[ expando ] ) {
+ postFinder = setMatcher( postFinder, postSelector );
+ }
+ return markFunction(function( seed, results, context, xml ) {
+ var temp, i, elem,
+ preMap = [],
+ postMap = [],
+ preexisting = results.length,
+
+ // Get initial elements from seed or context
+ elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+ // Prefilter to get matcher input, preserving a map for seed-results synchronization
+ matcherIn = preFilter && ( seed || !selector ) ?
+ condense( elems, preMap, preFilter, context, xml ) :
+ elems,
+
+ matcherOut = matcher ?
+ // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+ postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+ // ...intermediate processing is necessary
+ [] :
+
+ // ...otherwise use results directly
+ results :
+ matcherIn;
+
+ // Find primary matches
+ if ( matcher ) {
+ matcher( matcherIn, matcherOut, context, xml );
+ }
+
+ // Apply postFilter
+ if ( postFilter ) {
+ temp = condense( matcherOut, postMap );
+ postFilter( temp, [], context, xml );
+
+ // Un-match failing elements by moving them back to matcherIn
+ i = temp.length;
+ while ( i-- ) {
+ if ( (elem = temp[i]) ) {
+ matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+ }
+ }
+ }
+
+ if ( seed ) {
+ if ( postFinder || preFilter ) {
+ if ( postFinder ) {
+ // Get the final matcherOut by condensing this intermediate into postFinder contexts
+ temp = [];
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) ) {
+ // Restore matcherIn since elem is not yet a final match
+ temp.push( (matcherIn[i] = elem) );
+ }
+ }
+ postFinder( null, (matcherOut = []), temp, xml );
+ }
+
+ // Move matched elements from seed to results to keep them synchronized
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) &&
+ (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
+
+ seed[temp] = !(results[temp] = elem);
+ }
+ }
+ }
+
+ // Add elements to results, through postFinder if defined
+ } else {
+ matcherOut = condense(
+ matcherOut === results ?
+ matcherOut.splice( preexisting, matcherOut.length ) :
+ matcherOut
+ );
+ if ( postFinder ) {
+ postFinder( null, results, matcherOut, xml );
+ } else {
+ push.apply( results, matcherOut );
+ }
+ }
+ });
+}
+
+function matcherFromTokens( tokens ) {
+ var checkContext, matcher, j,
+ len = tokens.length,
+ leadingRelative = Expr.relative[ tokens[0].type ],
+ implicitRelative = leadingRelative || Expr.relative[" "],
+ i = leadingRelative ? 1 : 0,
+
+ // The foundational matcher ensures that elements are reachable from top-level context(s)
+ matchContext = addCombinator( function( elem ) {
+ return elem === checkContext;
+ }, implicitRelative, true ),
+ matchAnyContext = addCombinator( function( elem ) {
+ return indexOf( checkContext, elem ) > -1;
+ }, implicitRelative, true ),
+ matchers = [ function( elem, context, xml ) {
+ var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ (checkContext = context).nodeType ?
+ matchContext( elem, context, xml ) :
+ matchAnyContext( elem, context, xml ) );
+ // Avoid hanging onto element (issue #299)
+ checkContext = null;
+ return ret;
+ } ];
+
+ for ( ; i < len; i++ ) {
+ if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+ matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+ } else {
+ matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+ // Return special upon seeing a positional matcher
+ if ( matcher[ expando ] ) {
+ // Find the next relative operator (if any) for proper handling
+ j = ++i;
+ for ( ; j < len; j++ ) {
+ if ( Expr.relative[ tokens[j].type ] ) {
+ break;
+ }
+ }
+ return setMatcher(
+ i > 1 && elementMatcher( matchers ),
+ i > 1 && toSelector(
+ // If the preceding token was a descendant combinator, insert an implicit any-element `*`
+ tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
+ ).replace( rtrim, "$1" ),
+ matcher,
+ i < j && matcherFromTokens( tokens.slice( i, j ) ),
+ j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+ j < len && toSelector( tokens )
+ );
+ }
+ matchers.push( matcher );
+ }
+ }
+
+ return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ var bySet = setMatchers.length > 0,
+ byElement = elementMatchers.length > 0,
+ superMatcher = function( seed, context, xml, results, outermost ) {
+ var elem, j, matcher,
+ matchedCount = 0,
+ i = "0",
+ unmatched = seed && [],
+ setMatched = [],
+ contextBackup = outermostContext,
+ // We must always have either seed elements or outermost context
+ elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
+ // Use integer dirruns iff this is the outermost matcher
+ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
+ len = elems.length;
+
+ if ( outermost ) {
+ outermostContext = context !== document && context;
+ }
+
+ // Add elements passing elementMatchers directly to results
+ // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+ // Support: IE<9, Safari
+ // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id
+ for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
+ if ( byElement && elem ) {
+ j = 0;
+ while ( (matcher = elementMatchers[j++]) ) {
+ if ( matcher( elem, context, xml ) ) {
+ results.push( elem );
+ break;
+ }
+ }
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ }
+ }
+
+ // Track unmatched elements for set filters
+ if ( bySet ) {
+ // They will have gone through all possible matchers
+ if ( (elem = !matcher && elem) ) {
+ matchedCount--;
+ }
+
+ // Lengthen the array for every element, matched or not
+ if ( seed ) {
+ unmatched.push( elem );
+ }
+ }
+ }
+
+ // Apply set filters to unmatched elements
+ matchedCount += i;
+ if ( bySet && i !== matchedCount ) {
+ j = 0;
+ while ( (matcher = setMatchers[j++]) ) {
+ matcher( unmatched, setMatched, context, xml );
+ }
+
+ if ( seed ) {
+ // Reintegrate element matches to eliminate the need for sorting
+ if ( matchedCount > 0 ) {
+ while ( i-- ) {
+ if ( !(unmatched[i] || setMatched[i]) ) {
+ setMatched[i] = pop.call( results );
+ }
+ }
+ }
+
+ // Discard index placeholder values to get only actual matches
+ setMatched = condense( setMatched );
+ }
+
+ // Add matches to results
+ push.apply( results, setMatched );
+
+ // Seedless set matches succeeding multiple successful matchers stipulate sorting
+ if ( outermost && !seed && setMatched.length > 0 &&
+ ( matchedCount + setMatchers.length ) > 1 ) {
+
+ Sizzle.uniqueSort( results );
+ }
+ }
+
+ // Override manipulation of globals by nested matchers
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ outermostContext = contextBackup;
+ }
+
+ return unmatched;
+ };
+
+ return bySet ?
+ markFunction( superMatcher ) :
+ superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
+ var i,
+ setMatchers = [],
+ elementMatchers = [],
+ cached = compilerCache[ selector + " " ];
+
+ if ( !cached ) {
+ // Generate a function of recursive functions that can be used to check each element
+ if ( !match ) {
+ match = tokenize( selector );
+ }
+ i = match.length;
+ while ( i-- ) {
+ cached = matcherFromTokens( match[i] );
+ if ( cached[ expando ] ) {
+ setMatchers.push( cached );
+ } else {
+ elementMatchers.push( cached );
+ }
+ }
+
+ // Cache the compiled function
+ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+
+ // Save selector and tokenization
+ cached.selector = selector;
+ }
+ return cached;
+};
+
+/**
+ * A low-level selection function that works with Sizzle's compiled
+ * selector functions
+ * @param {String|Function} selector A selector or a pre-compiled
+ * selector function built with Sizzle.compile
+ * @param {Element} context
+ * @param {Array} [results]
+ * @param {Array} [seed] A set of elements to match against
+ */
+select = Sizzle.select = function( selector, context, results, seed ) {
+ var i, tokens, token, type, find,
+ compiled = typeof selector === "function" && selector,
+ match = !seed && tokenize( (selector = compiled.selector || selector) );
+
+ results = results || [];
+
+ // Try to minimize operations if there is no seed and only one group
+ if ( match.length === 1 ) {
+
+ // Take a shortcut and set the context if the root selector is an ID
+ tokens = match[0] = match[0].slice( 0 );
+ if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+ support.getById && context.nodeType === 9 && documentIsHTML &&
+ Expr.relative[ tokens[1].type ] ) {
+
+ context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+ if ( !context ) {
+ return results;
+
+ // Precompiled matchers will still verify ancestry, so step up a level
+ } else if ( compiled ) {
+ context = context.parentNode;
+ }
+
+ selector = selector.slice( tokens.shift().value.length );
+ }
+
+ // Fetch a seed set for right-to-left matching
+ i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+ while ( i-- ) {
+ token = tokens[i];
+
+ // Abort if we hit a combinator
+ if ( Expr.relative[ (type = token.type) ] ) {
+ break;
+ }
+ if ( (find = Expr.find[ type ]) ) {
+ // Search, expanding context for leading sibling combinators
+ if ( (seed = find(
+ token.matches[0].replace( runescape, funescape ),
+ rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
+ )) ) {
+
+ // If seed is empty or no tokens remain, we can return early
+ tokens.splice( i, 1 );
+ selector = seed.length && toSelector( tokens );
+ if ( !selector ) {
+ push.apply( results, seed );
+ return results;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ // Compile and execute a filtering function if one is not provided
+ // Provide `match` to avoid retokenization if we modified the selector above
+ ( compiled || compile( selector, match ) )(
+ seed,
+ context,
+ !documentIsHTML,
+ results,
+ rsibling.test( selector ) && testContext( context.parentNode ) || context
+ );
+ return results;
+};
+
+// One-time assignments
+
+// Sort stability
+support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
+
+// Support: Chrome 14-35+
+// Always assume duplicates if they aren't passed to the comparison function
+support.detectDuplicates = !!hasDuplicate;
+
+// Initialize against the default document
+setDocument();
+
+// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+// Detached nodes confoundingly follow *each other*
+support.sortDetached = assert(function( div1 ) {
+ // Should return 1, but returns 4 (following)
+ return div1.compareDocumentPosition( document.createElement("div") ) & 1;
+});
+
+// Support: IE<8
+// Prevent attribute/property "interpolation"
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !assert(function( div ) {
+ div.innerHTML = "";
+ return div.firstChild.getAttribute("href") === "#" ;
+}) ) {
+ addHandle( "type|href|height|width", function( elem, name, isXML ) {
+ if ( !isXML ) {
+ return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+ }
+ });
+}
+
+// Support: IE<9
+// Use defaultValue in place of getAttribute("value")
+if ( !support.attributes || !assert(function( div ) {
+ div.innerHTML = "";
+ div.firstChild.setAttribute( "value", "" );
+ return div.firstChild.getAttribute( "value" ) === "";
+}) ) {
+ addHandle( "value", function( elem, name, isXML ) {
+ if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+ return elem.defaultValue;
+ }
+ });
+}
+
+// Support: IE<9
+// Use getAttributeNode to fetch booleans when getAttribute lies
+if ( !assert(function( div ) {
+ return div.getAttribute("disabled") == null;
+}) ) {
+ addHandle( booleans, function( elem, name, isXML ) {
+ var val;
+ if ( !isXML ) {
+ return elem[ name ] === true ? name.toLowerCase() :
+ (val = elem.getAttributeNode( name )) && val.specified ?
+ val.value :
+ null;
+ }
+ });
+}
+
+return Sizzle;
+
+})( window );
+
+
+
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+
+var rneedsContext = jQuery.expr.match.needsContext;
+
+var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
+
+
+
+var risSimple = /^.[^:#\[\.,]*$/;
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, not ) {
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep( elements, function( elem, i ) {
+ /* jshint -W018 */
+ return !!qualifier.call( elem, i, elem ) !== not;
+ });
+
+ }
+
+ if ( qualifier.nodeType ) {
+ return jQuery.grep( elements, function( elem ) {
+ return ( elem === qualifier ) !== not;
+ });
+
+ }
+
+ if ( typeof qualifier === "string" ) {
+ if ( risSimple.test( qualifier ) ) {
+ return jQuery.filter( qualifier, elements, not );
+ }
+
+ qualifier = jQuery.filter( qualifier, elements );
+ }
+
+ return jQuery.grep( elements, function( elem ) {
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
+ });
+}
+
+jQuery.filter = function( expr, elems, not ) {
+ var elem = elems[ 0 ];
+
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 && elem.nodeType === 1 ?
+ jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
+ jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+ return elem.nodeType === 1;
+ }));
+};
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var i,
+ ret = [],
+ self = this,
+ len = self.length;
+
+ if ( typeof selector !== "string" ) {
+ return this.pushStack( jQuery( selector ).filter(function() {
+ for ( i = 0; i < len; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ }) );
+ }
+
+ for ( i = 0; i < len; i++ ) {
+ jQuery.find( selector, self[ i ], ret );
+ }
+
+ // Needed because $( selector, context ) becomes $( context ).find( selector )
+ ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
+ ret.selector = this.selector ? this.selector + " " + selector : selector;
+ return ret;
+ },
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector || [], false) );
+ },
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector || [], true) );
+ },
+ is: function( selector ) {
+ return !!winnow(
+ this,
+
+ // If this is a positional/relative selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ typeof selector === "string" && rneedsContext.test( selector ) ?
+ jQuery( selector ) :
+ selector || [],
+ false
+ ).length;
+ }
+});
+
+
+// Initialize a jQuery object
+
+
+// A central reference to the root jQuery(document)
+var rootjQuery,
+
+ // Use the correct document accordingly with window argument (sandbox)
+ document = window.document,
+
+ // A simple way to check for HTML strings
+ // Prioritize #id over to avoid XSS via location.hash (#9521)
+ // Strict HTML recognition (#11290: must start with <)
+ rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+
+ init = jQuery.fn.init = function( selector, context ) {
+ var match, elem;
+
+ // HANDLE: $(""), $(null), $(undefined), $(false)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = rquickExpr.exec( selector );
+ }
+
+ // Match html or make sure no context is specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+
+ // scripts is true for back-compat
+ // Intentionally let the error be thrown if parseHTML is not present
+ jQuery.merge( this, jQuery.parseHTML(
+ match[1],
+ context && context.nodeType ? context.ownerDocument || context : document,
+ true
+ ) );
+
+ // HANDLE: $(html, props)
+ if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+ for ( match in context ) {
+ // Properties of context are called as methods if possible
+ if ( jQuery.isFunction( this[ match ] ) ) {
+ this[ match ]( context[ match ] );
+
+ // ...and otherwise set as attributes
+ } else {
+ this.attr( match, context[ match ] );
+ }
+ }
+ }
+
+ return this;
+
+ // HANDLE: $(#id)
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return ( context || rootjQuery ).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(DOMElement)
+ } else if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return typeof rootjQuery.ready !== "undefined" ?
+ rootjQuery.ready( selector ) :
+ // Execute immediately if ready is not present
+ selector( jQuery );
+ }
+
+ if ( selector.selector !== undefined ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ };
+
+// Give the init function the jQuery prototype for later instantiation
+init.prototype = jQuery.fn;
+
+// Initialize central reference
+rootjQuery = jQuery( document );
+
+
+var rparentsprev = /^(?:parents|prev(?:Until|All))/,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.extend({
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+jQuery.fn.extend({
+ has: function( target ) {
+ var i,
+ targets = jQuery( target, this ),
+ len = targets.length;
+
+ return this.filter(function() {
+ for ( i = 0; i < len; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ closest: function( selectors, context ) {
+ var cur,
+ i = 0,
+ l = this.length,
+ matched = [],
+ pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( ; i < l; i++ ) {
+ for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
+ // Always skip document fragments
+ if ( cur.nodeType < 11 && (pos ?
+ pos.index(cur) > -1 :
+
+ // Don't pass non-elements to Sizzle
+ cur.nodeType === 1 &&
+ jQuery.find.matchesSelector(cur, selectors)) ) {
+
+ matched.push( cur );
+ break;
+ }
+ }
+ }
+
+ return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+
+ // No argument, return index in parent
+ if ( !elem ) {
+ return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
+ }
+
+ // index in selector
+ if ( typeof elem === "string" ) {
+ return jQuery.inArray( this[0], jQuery( elem ) );
+ }
+
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ return this.pushStack(
+ jQuery.unique(
+ jQuery.merge( this.get(), jQuery( selector, context ) )
+ )
+ );
+ },
+
+ addBack: function( selector ) {
+ return this.add( selector == null ?
+ this.prevObject : this.prevObject.filter(selector)
+ );
+ }
+});
+
+function sibling( cur, dir ) {
+ do {
+ cur = cur[ dir ];
+ } while ( cur && cur.nodeType !== 1 );
+
+ return cur;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return sibling( elem, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return sibling( elem, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.merge( [], elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( name.slice( -5 ) !== "Until" ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ if ( this.length > 1 ) {
+ // Remove duplicates
+ if ( !guaranteedUnique[ name ] ) {
+ ret = jQuery.unique( ret );
+ }
+
+ // Reverse order for parents* and prev-derivatives
+ if ( rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+ }
+
+ return this.pushStack( ret );
+ };
+});
+var rnotwhite = (/\S+/g);
+
+
+
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+ var object = optionsCache[ options ] = {};
+ jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
+ object[ flag ] = true;
+ });
+ return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ * options: an optional list of space-separated options that will change how
+ * the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+ // Convert options from String-formatted to Object-formatted if needed
+ // (we check in cache first)
+ options = typeof options === "string" ?
+ ( optionsCache[ options ] || createOptions( options ) ) :
+ jQuery.extend( {}, options );
+
+ var // Flag to know if list is currently firing
+ firing,
+ // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list was already fired
+ fired,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = !options.once && [],
+ // Fire callbacks
+ fire = function( data ) {
+ memory = options.memory && data;
+ fired = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ firing = true;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+ memory = false; // To prevent further calls using add
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( stack ) {
+ if ( stack.length ) {
+ fire( stack.shift() );
+ }
+ } else if ( memory ) {
+ list = [];
+ } else {
+ self.disable();
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ // First, we save the current length
+ var start = list.length;
+ (function add( args ) {
+ jQuery.each( args, function( _, arg ) {
+ var type = jQuery.type( arg );
+ if ( type === "function" ) {
+ if ( !options.unique || !self.has( arg ) ) {
+ list.push( arg );
+ }
+ } else if ( arg && arg.length && type !== "string" ) {
+ // Inspect recursively
+ add( arg );
+ }
+ });
+ })( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away
+ } else if ( memory ) {
+ firingStart = start;
+ fire( memory );
+ }
+ }
+ return this;
+ },
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ jQuery.each( arguments, function( _, arg ) {
+ var index;
+ while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+ list.splice( index, 1 );
+ // Handle firing indexes
+ if ( firing ) {
+ if ( index <= firingLength ) {
+ firingLength--;
+ }
+ if ( index <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ });
+ }
+ return this;
+ },
+ // Check if a given callback is in the list.
+ // If no argument is given, return whether or not list has callbacks attached.
+ has: function( fn ) {
+ return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ firingLength = 0;
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
+ },
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ if ( list && ( !fired || stack ) ) {
+ args = args || [];
+ args = [ context, args.slice ? args.slice() : args ];
+ if ( firing ) {
+ stack.push( args );
+ } else {
+ fire( args );
+ }
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!fired;
+ }
+ };
+
+ return self;
+};
+
+
+jQuery.extend({
+
+ Deferred: function( func ) {
+ var tuples = [
+ // action, add listener, listener list, final state
+ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+ [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+ [ "notify", "progress", jQuery.Callbacks("memory") ]
+ ],
+ state = "pending",
+ promise = {
+ state: function() {
+ return state;
+ },
+ always: function() {
+ deferred.done( arguments ).fail( arguments );
+ return this;
+ },
+ then: function( /* fnDone, fnFail, fnProgress */ ) {
+ var fns = arguments;
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( tuples, function( i, tuple ) {
+ var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+ // deferred[ done | fail | progress ] for forwarding actions to newDefer
+ deferred[ tuple[1] ](function() {
+ var returned = fn && fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise()
+ .done( newDefer.resolve )
+ .fail( newDefer.reject )
+ .progress( newDefer.notify );
+ } else {
+ newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
+ }
+ });
+ });
+ fns = null;
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ return obj != null ? jQuery.extend( obj, promise ) : promise;
+ }
+ },
+ deferred = {};
+
+ // Keep pipe for back-compat
+ promise.pipe = promise.then;
+
+ // Add list-specific methods
+ jQuery.each( tuples, function( i, tuple ) {
+ var list = tuple[ 2 ],
+ stateString = tuple[ 3 ];
+
+ // promise[ done | fail | progress ] = list.add
+ promise[ tuple[1] ] = list.add;
+
+ // Handle state
+ if ( stateString ) {
+ list.add(function() {
+ // state = [ resolved | rejected ]
+ state = stateString;
+
+ // [ reject_list | resolve_list ].disable; progress_list.lock
+ }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+ }
+
+ // deferred[ resolve | reject | notify ]
+ deferred[ tuple[0] ] = function() {
+ deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
+ return this;
+ };
+ deferred[ tuple[0] + "With" ] = list.fireWith;
+ });
+
+ // Make the deferred a promise
+ promise.promise( deferred );
+
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+
+ // All done!
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( subordinate /* , ..., subordinateN */ ) {
+ var i = 0,
+ resolveValues = slice.call( arguments ),
+ length = resolveValues.length,
+
+ // the count of uncompleted subordinates
+ remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+ // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+ deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+ // Update function for both resolve and progress values
+ updateFunc = function( i, contexts, values ) {
+ return function( value ) {
+ contexts[ i ] = this;
+ values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
+ if ( values === progressValues ) {
+ deferred.notifyWith( contexts, values );
+
+ } else if ( !(--remaining) ) {
+ deferred.resolveWith( contexts, values );
+ }
+ };
+ },
+
+ progressValues, progressContexts, resolveContexts;
+
+ // add listeners to Deferred subordinates; treat others as resolved
+ if ( length > 1 ) {
+ progressValues = new Array( length );
+ progressContexts = new Array( length );
+ resolveContexts = new Array( length );
+ for ( ; i < length; i++ ) {
+ if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+ resolveValues[ i ].promise()
+ .done( updateFunc( i, resolveContexts, resolveValues ) )
+ .fail( deferred.reject )
+ .progress( updateFunc( i, progressContexts, progressValues ) );
+ } else {
+ --remaining;
+ }
+ }
+ }
+
+ // if we're not waiting on anything, resolve the master
+ if ( !remaining ) {
+ deferred.resolveWith( resolveContexts, resolveValues );
+ }
+
+ return deferred.promise();
+ }
+});
+
+
+// The deferred used on DOM ready
+var readyList;
+
+jQuery.fn.ready = function( fn ) {
+ // Add the callback
+ jQuery.ready.promise().done( fn );
+
+ return this;
+};
+
+jQuery.extend({
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+
+ // Abort if there are pending holds or we're already ready
+ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+ return;
+ }
+
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.triggerHandler ) {
+ jQuery( document ).triggerHandler( "ready" );
+ jQuery( document ).off( "ready" );
+ }
+ }
+});
+
+/**
+ * Clean-up method for dom ready events
+ */
+function detach() {
+ if ( document.addEventListener ) {
+ document.removeEventListener( "DOMContentLoaded", completed, false );
+ window.removeEventListener( "load", completed, false );
+
+ } else {
+ document.detachEvent( "onreadystatechange", completed );
+ window.detachEvent( "onload", completed );
+ }
+}
+
+/**
+ * The ready event handler and self cleanup method
+ */
+function completed() {
+ // readyState === "complete" is good enough for us to call the dom ready in oldIE
+ if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
+ detach();
+ jQuery.ready();
+ }
+}
+
+jQuery.ready.promise = function( obj ) {
+ if ( !readyList ) {
+
+ readyList = jQuery.Deferred();
+
+ // Catch cases where $(document).ready() is called after the browser event has already occurred.
+ // we once tried to use readyState "interactive" here, but it caused issues like the one
+ // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ setTimeout( jQuery.ready );
+
+ // Standards-based browsers support DOMContentLoaded
+ } else if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", completed, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", completed, false );
+
+ // If IE event model is used
+ } else {
+ // Ensure firing before onload, maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", completed );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", completed );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var top = false;
+
+ try {
+ top = window.frameElement == null && document.documentElement;
+ } catch(e) {}
+
+ if ( top && top.doScroll ) {
+ (function doScrollCheck() {
+ if ( !jQuery.isReady ) {
+
+ try {
+ // Use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ top.doScroll("left");
+ } catch(e) {
+ return setTimeout( doScrollCheck, 50 );
+ }
+
+ // detach all dom ready events
+ detach();
+
+ // and execute any waiting functions
+ jQuery.ready();
+ }
+ })();
+ }
+ }
+ }
+ return readyList.promise( obj );
+};
+
+
+var strundefined = typeof undefined;
+
+
+
+// Support: IE<9
+// Iteration over object's inherited properties before its own
+var i;
+for ( i in jQuery( support ) ) {
+ break;
+}
+support.ownLast = i !== "0";
+
+// Note: most support tests are defined in their respective modules.
+// false until the test is run
+support.inlineBlockNeedsLayout = false;
+
+// Execute ASAP in case we need to set body.style.zoom
+jQuery(function() {
+ // Minified: var a,b,c,d
+ var val, div, body, container;
+
+ body = document.getElementsByTagName( "body" )[ 0 ];
+ if ( !body || !body.style ) {
+ // Return for frameset docs that don't have a body
+ return;
+ }
+
+ // Setup
+ div = document.createElement( "div" );
+ container = document.createElement( "div" );
+ container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
+ body.appendChild( container ).appendChild( div );
+
+ if ( typeof div.style.zoom !== strundefined ) {
+ // Support: IE<8
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";
+
+ support.inlineBlockNeedsLayout = val = div.offsetWidth === 3;
+ if ( val ) {
+ // Prevent IE 6 from affecting layout for positioned elements #11048
+ // Prevent IE from shrinking the body in IE 7 mode #12869
+ // Support: IE<8
+ body.style.zoom = 1;
+ }
+ }
+
+ body.removeChild( container );
+});
+
+
+
+
+(function() {
+ var div = document.createElement( "div" );
+
+ // Execute the test only if not already executed in another module.
+ if (support.deleteExpando == null) {
+ // Support: IE<9
+ support.deleteExpando = true;
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+ }
+
+ // Null elements to avoid leaks in IE.
+ div = null;
+})();
+
+
+/**
+ * Determines whether an object can have data
+ */
+jQuery.acceptData = function( elem ) {
+ var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ],
+ nodeType = +elem.nodeType || 1;
+
+ // Do not set data on non-element DOM nodes because it will not be cleared (#8335).
+ return nodeType !== 1 && nodeType !== 9 ?
+ false :
+
+ // Nodes accept data unless otherwise specified; rejection can be conditional
+ !noData || noData !== true && elem.getAttribute("classid") === noData;
+};
+
+
+var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
+ rmultiDash = /([A-Z])/g;
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+
+ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ // Only convert to a number if it doesn't change the string
+ +data + "" === data ? +data :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+ var name;
+ for ( name in obj ) {
+
+ // if the public data object is empty, the private is still empty
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+ continue;
+ }
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var ret, thisCache,
+ internalKey = jQuery.expando,
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++;
+ } else {
+ id = internalKey;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ // Avoid exposing jQuery metadata on plain JS objects when the object
+ // is serialized using JSON.stringify
+ cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ] = jQuery.extend( cache[ id ], name );
+ } else {
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+ }
+ }
+
+ thisCache = cache[ id ];
+
+ // jQuery data() is stored in a separate object inside the object's internal data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data.
+ if ( !pvt ) {
+ if ( !thisCache.data ) {
+ thisCache.data = {};
+ }
+
+ thisCache = thisCache.data;
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // Check for both converted-to-camel and non-converted data property names
+ // If a data property was specified
+ if ( typeof name === "string" ) {
+
+ // First Try to find as-is property data
+ ret = thisCache[ name ];
+
+ // Test for null|undefined property data
+ if ( ret == null ) {
+
+ // Try to find the camelCased property
+ ret = thisCache[ jQuery.camelCase( name ) ];
+ }
+ } else {
+ ret = thisCache;
+ }
+
+ return ret;
+}
+
+function internalRemoveData( elem, name, pvt ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, i,
+ isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+ if ( thisCache ) {
+
+ // Support array or space separated string names for data keys
+ if ( !jQuery.isArray( name ) ) {
+
+ // try the string as a key before any manipulation
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+
+ // split the camel cased version by spaces unless a key with the spaces exists
+ name = jQuery.camelCase( name );
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+ name = name.split(" ");
+ }
+ }
+ } else {
+ // If "name" is an array of keys...
+ // When data is initially created, via ("key", "val") signature,
+ // keys will be converted to camelCase.
+ // Since there is no way to tell _how_ a key was added, remove
+ // both plain key and camelCase key. #12786
+ // This will only penalize the array argument path.
+ name = name.concat( jQuery.map( name, jQuery.camelCase ) );
+ }
+
+ i = name.length;
+ while ( i-- ) {
+ delete thisCache[ name[i] ];
+ }
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( !pvt ) {
+ delete cache[ id ].data;
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject( cache[ id ] ) ) {
+ return;
+ }
+ }
+
+ // Destroy the cache
+ if ( isNode ) {
+ jQuery.cleanData( [ elem ], true );
+
+ // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+ /* jshint eqeqeq: false */
+ } else if ( support.deleteExpando || cache != cache.window ) {
+ /* jshint eqeqeq: true */
+ delete cache[ id ];
+
+ // When all else fails, null
+ } else {
+ cache[ id ] = null;
+ }
+}
+
+jQuery.extend({
+ cache: {},
+
+ // The following elements (space-suffixed to avoid Object.prototype collisions)
+ // throw uncatchable exceptions if you attempt to set expando properties
+ noData: {
+ "applet ": true,
+ "embed ": true,
+ // ...but Flash objects (which have this classid) *can* handle expandos
+ "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data ) {
+ return internalData( elem, name, data );
+ },
+
+ removeData: function( elem, name ) {
+ return internalRemoveData( elem, name );
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return internalData( elem, name, data, true );
+ },
+
+ _removeData: function( elem, name ) {
+ return internalRemoveData( elem, name, true );
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var i, name, data,
+ elem = this[0],
+ attrs = elem && elem.attributes;
+
+ // Special expections of .data basically thwart jQuery.access,
+ // so implement the relevant behavior ourselves
+
+ // Gets all values
+ if ( key === undefined ) {
+ if ( this.length ) {
+ data = jQuery.data( elem );
+
+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+ i = attrs.length;
+ while ( i-- ) {
+
+ // Support: IE11+
+ // The attrs elements can be null (#14894)
+ if ( attrs[ i ] ) {
+ name = attrs[ i ].name;
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = jQuery.camelCase( name.slice(5) );
+ dataAttr( elem, name, data[ name ] );
+ }
+ }
+ }
+ jQuery._data( elem, "parsedAttrs", true );
+ }
+ }
+
+ return data;
+ }
+
+ // Sets multiple values
+ if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ return arguments.length > 1 ?
+
+ // Sets one value
+ this.each(function() {
+ jQuery.data( this, key, value );
+ }) :
+
+ // Gets one value
+ // Try to fetch any internally stored data first
+ elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined;
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+
+jQuery.extend({
+ queue: function( elem, type, data ) {
+ var queue;
+
+ if ( elem ) {
+ type = ( type || "fx" ) + "queue";
+ queue = jQuery._data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !queue || jQuery.isArray(data) ) {
+ queue = jQuery._data( elem, type, jQuery.makeArray(data) );
+ } else {
+ queue.push( data );
+ }
+ }
+ return queue || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ startLength = queue.length,
+ fn = queue.shift(),
+ hooks = jQuery._queueHooks( elem, type ),
+ next = function() {
+ jQuery.dequeue( elem, type );
+ };
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ startLength--;
+ }
+
+ if ( fn ) {
+
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift( "inprogress" );
+ }
+
+ // clear up the last queue stop function
+ delete hooks.stop;
+ fn.call( elem, next, hooks );
+ }
+
+ if ( !startLength && hooks ) {
+ hooks.empty.fire();
+ }
+ },
+
+ // not intended for public consumption - generates a queueHooks object, or returns the current one
+ _queueHooks: function( elem, type ) {
+ var key = type + "queueHooks";
+ return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+ empty: jQuery.Callbacks("once memory").add(function() {
+ jQuery._removeData( elem, type + "queue" );
+ jQuery._removeData( elem, key );
+ })
+ });
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ var setter = 2;
+
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ setter--;
+ }
+
+ if ( arguments.length < setter ) {
+ return jQuery.queue( this[0], type );
+ }
+
+ return data === undefined ?
+ this :
+ this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ // ensure a hooks for this queue
+ jQuery._queueHooks( this, type );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, obj ) {
+ var tmp,
+ count = 1,
+ defer = jQuery.Deferred(),
+ elements = this,
+ i = this.length,
+ resolve = function() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ };
+
+ if ( typeof type !== "string" ) {
+ obj = type;
+ type = undefined;
+ }
+ type = type || "fx";
+
+ while ( i-- ) {
+ tmp = jQuery._data( elements[ i ], type + "queueHooks" );
+ if ( tmp && tmp.empty ) {
+ count++;
+ tmp.empty.add( resolve );
+ }
+ }
+ resolve();
+ return defer.promise( obj );
+ }
+});
+var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
+
+var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
+
+var isHidden = function( elem, el ) {
+ // isHidden might be called from jQuery#filter function;
+ // in that case, element will be second argument
+ elem = el || elem;
+ return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+ };
+
+
+
+// Multifunctional method to get and set values of a collection
+// The value/s can optionally be executed if it's a function
+var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
+ var i = 0,
+ length = elems.length,
+ bulk = key == null;
+
+ // Sets many values
+ if ( jQuery.type( key ) === "object" ) {
+ chainable = true;
+ for ( i in key ) {
+ jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
+ }
+
+ // Sets one value
+ } else if ( value !== undefined ) {
+ chainable = true;
+
+ if ( !jQuery.isFunction( value ) ) {
+ raw = true;
+ }
+
+ if ( bulk ) {
+ // Bulk operations run against the entire set
+ if ( raw ) {
+ fn.call( elems, value );
+ fn = null;
+
+ // ...except when executing function values
+ } else {
+ bulk = fn;
+ fn = function( elem, key, value ) {
+ return bulk.call( jQuery( elem ), value );
+ };
+ }
+ }
+
+ if ( fn ) {
+ for ( ; i < length; i++ ) {
+ fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+ }
+ }
+ }
+
+ return chainable ?
+ elems :
+
+ // Gets
+ bulk ?
+ fn.call( elems ) :
+ length ? fn( elems[0], key ) : emptyGet;
+};
+var rcheckableType = (/^(?:checkbox|radio)$/i);
+
+
+
+(function() {
+ // Minified: var a,b,c
+ var input = document.createElement( "input" ),
+ div = document.createElement( "div" ),
+ fragment = document.createDocumentFragment();
+
+ // Setup
+ div.innerHTML = "
a";
+
+ // IE strips leading whitespace when .innerHTML is used
+ support.leadingWhitespace = div.firstChild.nodeType === 3;
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ support.tbody = !div.getElementsByTagName( "tbody" ).length;
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ support.htmlSerialize = !!div.getElementsByTagName( "link" ).length;
+
+ // Makes sure cloning an html5 element does not cause problems
+ // Where outerHTML is undefined, this still works
+ support.html5Clone =
+ document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>";
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ input.type = "checkbox";
+ input.checked = true;
+ fragment.appendChild( input );
+ support.appendChecked = input.checked;
+
+ // Make sure textarea (and checkbox) defaultValue is properly cloned
+ // Support: IE6-IE11+
+ div.innerHTML = "";
+ support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
+
+ // #11217 - WebKit loses check when the name is after the checked attribute
+ fragment.appendChild( div );
+ div.innerHTML = "";
+
+ // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
+ // old WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Support: IE<9
+ // Opera does not clone events (and typeof div.attachEvent === undefined).
+ // IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
+ support.noCloneEvent = true;
+ if ( div.attachEvent ) {
+ div.attachEvent( "onclick", function() {
+ support.noCloneEvent = false;
+ });
+
+ div.cloneNode( true ).click();
+ }
+
+ // Execute the test only if not already executed in another module.
+ if (support.deleteExpando == null) {
+ // Support: IE<9
+ support.deleteExpando = true;
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+ }
+})();
+
+
+(function() {
+ var i, eventName,
+ div = document.createElement( "div" );
+
+ // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event)
+ for ( i in { submit: true, change: true, focusin: true }) {
+ eventName = "on" + i;
+
+ if ( !(support[ i + "Bubbles" ] = eventName in window) ) {
+ // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
+ div.setAttribute( eventName, "t" );
+ support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false;
+ }
+ }
+
+ // Null elements to avoid leaks in IE.
+ div = null;
+})();
+
+
+var rformElems = /^(?:input|select|textarea)$/i,
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+
+function returnTrue() {
+ return true;
+}
+
+function returnFalse() {
+ return false;
+}
+
+function safeActiveElement() {
+ try {
+ return document.activeElement;
+ } catch ( err ) { }
+}
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+ global: {},
+
+ add: function( elem, types, handler, data, selector ) {
+ var tmp, events, t, handleObjIn,
+ special, eventHandle, handleObj,
+ handlers, type, namespaces, origType,
+ elemData = jQuery._data( elem );
+
+ // Don't attach events to noData or text/comment nodes (but allow plain objects)
+ if ( !elemData ) {
+ return;
+ }
+
+ // Caller can pass in an object of custom data in lieu of the handler
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ selector = handleObjIn.selector;
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure and main handler, if this is the first
+ if ( !(events = elemData.events) ) {
+ events = elemData.events = {};
+ }
+ if ( !(eventHandle = elemData.handle) ) {
+ eventHandle = elemData.handle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ?
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+ eventHandle.elem = elem;
+ }
+
+ // Handle multiple events separated by a space
+ types = ( types || "" ).match( rnotwhite ) || [ "" ];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tmp[1];
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+ // There *must* be a type, no attaching namespace-only handlers
+ if ( !type ) {
+ continue;
+ }
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend({
+ type: type,
+ origType: origType,
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+ namespace: namespaces.join(".")
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ if ( !(handlers = events[ type ]) ) {
+ handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
+
+ // Only use addEventListener/attachEvent if the special events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+ var j, handleObj, tmp,
+ origCount, t, events,
+ special, handlers, type,
+ namespaces, origType,
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem );
+
+ if ( !elemData || !(events = elemData.events) ) {
+ return;
+ }
+
+ // Once for each type.namespace in types; type may be omitted
+ types = ( types || "" ).match( rnotwhite ) || [ "" ];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tmp[1];
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+ handlers = events[ type ] || [];
+ tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
+
+ // Remove matching events
+ origCount = j = handlers.length;
+ while ( j-- ) {
+ handleObj = handlers[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !tmp || tmp.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+ handlers.splice( j, 1 );
+
+ if ( handleObj.selector ) {
+ handlers.delegateCount--;
+ }
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+ }
+
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( origCount && !handlers.length ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ delete elemData.handle;
+
+ // removeData also checks for emptiness and clears the expando if empty
+ // so use it instead of delete
+ jQuery._removeData( elem, "events" );
+ }
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ var handle, ontype, cur,
+ bubbleType, special, tmp, i,
+ eventPath = [ elem || document ],
+ type = hasOwn.call( event, "type" ) ? event.type : event,
+ namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
+
+ cur = tmp = elem = elem || document;
+
+ // Don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
+ }
+
+ if ( type.indexOf(".") >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+ ontype = type.indexOf(":") < 0 && "on" + type;
+
+ // Caller can pass in a jQuery.Event object, Object, or just an event type string
+ event = event[ jQuery.expando ] ?
+ event :
+ new jQuery.Event( type, typeof event === "object" && event );
+
+ // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
+ event.isTrigger = onlyHandlers ? 2 : 3;
+ event.namespace = namespaces.join(".");
+ event.namespace_re = event.namespace ?
+ new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
+ null;
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ if ( !event.target ) {
+ event.target = elem;
+ }
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data == null ?
+ [ event ] :
+ jQuery.makeArray( data, [ event ] );
+
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+ bubbleType = special.delegateType || type;
+ if ( !rfocusMorph.test( bubbleType + type ) ) {
+ cur = cur.parentNode;
+ }
+ for ( ; cur; cur = cur.parentNode ) {
+ eventPath.push( cur );
+ tmp = cur;
+ }
+
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( tmp === (elem.ownerDocument || document) ) {
+ eventPath.push( tmp.defaultView || tmp.parentWindow || window );
+ }
+ }
+
+ // Fire handlers on the event path
+ i = 0;
+ while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
+
+ event.type = i > 1 ?
+ bubbleType :
+ special.bindType || type;
+
+ // jQuery handler
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+
+ // Native handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
+ event.result = handle.apply( cur, data );
+ if ( event.result === false ) {
+ event.preventDefault();
+ }
+ }
+ }
+ event.type = type;
+
+ // If nobody prevented the default action, do it now
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+ if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
+ jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
+ // Don't do default actions on window, that's where global variables be (#6170)
+ if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
+
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ tmp = elem[ ontype ];
+
+ if ( tmp ) {
+ elem[ ontype ] = null;
+ }
+
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ try {
+ elem[ type ]();
+ } catch ( e ) {
+ // IE<9 dies on focus/blur to hidden element (#1486,#12518)
+ // only reproducible on winXP IE8 native, not IE9 in IE8 mode
+ }
+ jQuery.event.triggered = undefined;
+
+ if ( tmp ) {
+ elem[ ontype ] = tmp;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( event );
+
+ var i, ret, handleObj, matched, j,
+ handlerQueue = [],
+ args = slice.call( arguments ),
+ handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
+ special = jQuery.event.special[ event.type ] || {};
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
+ args[0] = event;
+ event.delegateTarget = this;
+
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+ return;
+ }
+
+ // Determine handlers
+ handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+
+ // Run delegates first; they may want to stop propagation beneath us
+ i = 0;
+ while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
+ event.currentTarget = matched.elem;
+
+ j = 0;
+ while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
+
+ // Triggered event must either 1) have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+ if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
+
+ event.handleObj = handleObj;
+ event.data = handleObj.data;
+
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+ .apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ if ( (event.result = ret) === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+ }
+
+ // Call the postDispatch hook for the mapped type
+ if ( special.postDispatch ) {
+ special.postDispatch.call( this, event );
+ }
+
+ return event.result;
+ },
+
+ handlers: function( event, handlers ) {
+ var sel, handleObj, matches, i,
+ handlerQueue = [],
+ delegateCount = handlers.delegateCount,
+ cur = event.target;
+
+ // Find delegate handlers
+ // Black-hole SVG