mirror of
				https://github.com/janeczku/calibre-web
				synced 2025-10-31 07:13:02 +00:00 
			
		
		
		
	
							
								
								
									
										37
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								README.md
									
									
									
									
									
								
							| @@ -33,8 +33,8 @@ Calibre-Web is a web app providing a clean interface for browsing, reading and d | |||||||
| 1. Install dependencies by running `pip install --target vendor -r requirements.txt`. | 1. Install dependencies by running `pip install --target vendor -r requirements.txt`. | ||||||
| 2. Execute the command: `python cps.py` (or `nohup python cps.py` - recommended if you want to exit the terminal window) | 2. Execute the command: `python cps.py` (or `nohup python cps.py` - recommended if you want to exit the terminal window) | ||||||
| 3. Point your browser to `http://localhost:8083` or `http://localhost:8083/opds` for the OPDS catalog | 3. Point your browser to `http://localhost:8083` or `http://localhost:8083/opds` for the OPDS catalog | ||||||
| 4. Set `Location of Calibre database` to the path of the folder where your Calibre library (metadata.db) lives, push "submit" button | 4. Set `Location of Calibre database` to the path of the folder where your Calibre library (metadata.db) lives, push "submit" button\ | ||||||
|    optionally a google drive can be used to host the calibre library (-> Using Google Drive integration) |    Optionally a Google Drive can be used to host the calibre library [-> Using Google Drive integration](https://github.com/janeczku/calibre-web/wiki/Configuration#using-google-drive-integration) | ||||||
| 5. Go to Login page | 5. Go to Login page | ||||||
|  |  | ||||||
| **Default admin login:**\ | **Default admin login:**\ | ||||||
| @@ -50,26 +50,35 @@ Python 2.7+, python 3.x+ | |||||||
|  |  | ||||||
| Optionally, to enable on-the-fly conversion from one ebook format to another when using the send-to-kindle feature, or during editing of ebooks metadata: | Optionally, to enable on-the-fly conversion from one ebook format to another when using the send-to-kindle feature, or during editing of ebooks metadata: | ||||||
|  |  | ||||||
| [Download and install](https://calibre-ebook.com/download) the Calibre desktop program for your platform and enter the folder including programm name (normally /opt/calibre/ebook-convert, or c:\prgogram files\calibre\ebook-convert.exe) in the field "calibre's converter tool" on the setup page. | [Download and install](https://calibre-ebook.com/download) the Calibre desktop program for your platform and enter the folder including programm name (normally /opt/calibre/ebook-convert, or C:\Program Files\calibre\ebook-convert.exe) in the field "calibre's converter tool" on the setup page. | ||||||
|  |  | ||||||
| \*** DEPRECATED \*** Support will be removed in future releases | \*** DEPRECATED \*** Support will be removed in future releases | ||||||
| Optionally, to enable on-the-fly conversion from EPUB to MOBI when using the send-to-kindle feature: |  | ||||||
|  |  | ||||||
| [Download Kindlegen](http://www.amazon.com/gp/feature.html?docId=1000765211) Amazon's KindleGen tool for your platform and place the binary named as `kindlegen` in the `vendor` folder. | [Download](http://www.amazon.com/gp/feature.html?docId=1000765211) Amazon's KindleGen tool for your platform and place the binary named as `kindlegen` in the `vendor` folder. | ||||||
|  |  | ||||||
| ## Docker images | ## Docker Images | ||||||
|  |  | ||||||
| Pre-built Docker images based on Alpine Linux are available in these Docker Hub repositories: | Pre-built Docker images are available in these Docker Hub repositories: | ||||||
|  |  | ||||||
| **x64** | #### **Technosoft2000 - x64** | ||||||
| + **technosoft2000** at [technosoft2000/calibre-web](https://hub.docker.com/r/technosoft2000/calibre-web/). If you want the option to convert/download ebooks in multiple formats, use this image as it includes Calibre's ebook-convert binary. The "path to convertertool" should be set to /opt/calibre/ebook-convert. | + Docker Hub - [https://hub.docker.com/r/technosoft2000/calibre-web/](https://hub.docker.com/r/technosoft2000/calibre-web/) | ||||||
| + **linuxserver.io** at [linuxserver/calibre-web](https://hub.docker.com/r/linuxserver/calibre-web/). Cannot convert between ebook formats. | + Github - [https://github.com/Technosoft2000/docker-calibre-web](https://github.com/Technosoft2000/docker-calibre-web)  | ||||||
|  |  | ||||||
| **armhf** |     Includes the Calibre `ebook-convert` binary. | ||||||
| + **linuxserver.io** at [lsioarmhf/calibre-web](https://hub.docker.com/r/lsioarmhf/calibre-web/) |     + The "path to convertertool" should be set to `/opt/calibre/ebook-convert` | ||||||
|  |  | ||||||
| **aarch64** | #### **LinuxServer - x64, armhf, aarch64** | ||||||
| + **linuxserver.io** at [lsioarmhf/calibre-web-aarch64](https://hub.docker.com/r/lsioarmhf/calibre-web-aarch64) | + Docker Hub - [https://hub.docker.com/r/linuxserver/calibre-web/](https://hub.docker.com/r/linuxserver/calibre-web/) | ||||||
|  | + Github - [https://github.com/linuxserver/docker-calibre-web](https://github.com/linuxserver/docker-calibre-web) | ||||||
|  | + Github - (Optional Calibre layer) - [https://github.com/linuxserver/docker-calibre-web/tree/calibre](https://github.com/linuxserver/docker-calibre-web/tree/calibre)  | ||||||
|  |  | ||||||
|  |    This image has the option to pull in an extra docker manifest layer to include the Calibre `ebook-convert` binary.  Just include the environmental variable `DOCKER_MODS=linuxserver/calibre-web:calibre` in your docker run/docker compose file. **(x64 only)** | ||||||
|  |    | ||||||
|  |    If you do not need this functionality then this can be omitted, keeping the image as lightweight as possible. | ||||||
|  |      | ||||||
|  |    Both the Calibre-Web and Calibre-Mod images are rebuilt automatically on new releases of Calibre-Web and Calibre respectively, and on updates to any included base image packages on a weekly basis if required. | ||||||
|  |    + The "path to convertertool" should be set to `/usr/bin/ebook-convert` | ||||||
|  |    + The "path to unrar" should be set to `/usr/bin/unrar` | ||||||
|  |  | ||||||
| # Wiki | # Wiki | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										27
									
								
								cps/admin.py
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								cps/admin.py
									
									
									
									
									
								
							| @@ -26,6 +26,8 @@ import os | |||||||
| import json | import json | ||||||
| import time | import time | ||||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||||
|  | # import uuid | ||||||
|  | import random | ||||||
| try: | try: | ||||||
|     from imp import reload |     from imp import reload | ||||||
| except ImportError: | except ImportError: | ||||||
| @@ -33,7 +35,7 @@ except ImportError: | |||||||
|  |  | ||||||
| from babel import Locale as LC | from babel import Locale as LC | ||||||
| from babel.dates import format_datetime | from babel.dates import format_datetime | ||||||
| from flask import Blueprint, flash, redirect, url_for, abort, request, make_response | from flask import Blueprint, flash, redirect, url_for, abort, request, make_response, send_from_directory | ||||||
| from flask_login import login_required, current_user, logout_user | from flask_login import login_required, current_user, logout_user | ||||||
| from flask_babel import gettext as _ | from flask_babel import gettext as _ | ||||||
| from sqlalchemy import and_ | from sqlalchemy import and_ | ||||||
| @@ -542,6 +544,9 @@ def new_user(): | |||||||
|         to_save = request.form.to_dict() |         to_save = request.form.to_dict() | ||||||
|         content.default_language = to_save["default_language"] |         content.default_language = to_save["default_language"] | ||||||
|         content.mature_content = "Show_mature_content" in to_save |         content.mature_content = "Show_mature_content" in to_save | ||||||
|  |         dat = datetime.strptime("1.1.2019", "%d.%m.%Y") | ||||||
|  |         content.id = int(time.time()*100) | ||||||
|  |         # val= int(uuid.uuid4()) | ||||||
|         if "locale" in to_save: |         if "locale" in to_save: | ||||||
|             content.locale = to_save["locale"] |             content.locale = to_save["locale"] | ||||||
|  |  | ||||||
| @@ -551,6 +556,7 @@ def new_user(): | |||||||
|                 val += int(key[5:]) |                 val += int(key[5:]) | ||||||
|         content.sidebar_view = val |         content.sidebar_view = val | ||||||
|  |  | ||||||
|  |  | ||||||
|         if "show_detail_random" in to_save: |         if "show_detail_random" in to_save: | ||||||
|             content.sidebar_view |= constants.DETAIL_RANDOM |             content.sidebar_view |= constants.DETAIL_RANDOM | ||||||
|  |  | ||||||
| @@ -758,6 +764,25 @@ def reset_password(user_id): | |||||||
|     return redirect(url_for('admin.admin')) |     return redirect(url_for('admin.admin')) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admi.route("/admin/logfile") | ||||||
|  | @login_required | ||||||
|  | @admin_required | ||||||
|  | def view_logfile(): | ||||||
|  |     perpage_p = {0:"30",1:"40",2:"100"} | ||||||
|  |     for key, value in perpage_p.items(): | ||||||
|  |         print(key) | ||||||
|  |         print(value) | ||||||
|  |     return render_title_template("logviewer.html",title=_(u"Logfile viewer"), perpage_p=perpage_p, perpage = 30, | ||||||
|  |                                  page="logfile") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @admi.route("/ajax/accesslog") | ||||||
|  | @login_required | ||||||
|  | @admin_required | ||||||
|  | def send_logfile(): | ||||||
|  |     return send_from_directory(constants.BASE_DIR,"access.log") | ||||||
|  |  | ||||||
|  |  | ||||||
| @admi.route("/get_update_status", methods=['GET']) | @admi.route("/get_update_status", methods=['GET']) | ||||||
| @login_required_if_no_ano | @login_required_if_no_ano | ||||||
| def get_update_status(): | def get_update_status(): | ||||||
|   | |||||||
| @@ -88,8 +88,8 @@ def setup(log_file, log_level=None, logger=None): | |||||||
|             log_file = os.path.join(_BASE_DIR, log_file) |             log_file = os.path.join(_BASE_DIR, log_file) | ||||||
|         log_file = os.path.abspath(log_file) |         log_file = os.path.abspath(log_file) | ||||||
|     else: |     else: | ||||||
|         # log_file = LOG_TO_STDERR |         log_file = LOG_TO_STDERR | ||||||
|         log_file = default_file |         # log_file = default_file | ||||||
|  |  | ||||||
|     # print ('%r -- %r' % (log_level, log_file)) |     # print ('%r -- %r' % (log_level, log_file)) | ||||||
|     if logger != "access" and logger != "tornado.access": |     if logger != "access" and logger != "tornado.access": | ||||||
|   | |||||||
| Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB | 
| Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB | 
| Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB | 
| @@ -8,6 +8,9 @@ | |||||||
|  * Copyright(c) 2011 Google Inc. |  * Copyright(c) 2011 Google Inc. | ||||||
|  * Copyright(c) 2011 antimatter15 |  * Copyright(c) 2011 antimatter15 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | /* global bitjs, Uint8Array */ | ||||||
|  |  | ||||||
| var bitjs = bitjs || {}; | var bitjs = bitjs || {}; | ||||||
| bitjs.io = bitjs.io || {}; | bitjs.io = bitjs.io || {}; | ||||||
|  |  | ||||||
| @@ -30,20 +33,20 @@ bitjs.io = bitjs.io || {}; | |||||||
|      * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. |      * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. | ||||||
|      * @param {boolean} rtl Whether the stream reads bits from the byte starting |      * @param {boolean} rtl Whether the stream reads bits from the byte starting | ||||||
|      *     from bit 7 to 0 (true) or bit 0 to 7 (false). |      *     from bit 7 to 0 (true) or bit 0 to 7 (false). | ||||||
|      * @param {Number} opt_offset The offset into the ArrayBuffer |      * @param {Number} optOffset The offset into the ArrayBuffer | ||||||
|      * @param {Number} opt_length The length of this BitStream |      * @param {Number} optLength The length of this BitStream | ||||||
|      */ |      */ | ||||||
|     bitjs.io.BitStream = function(ab, rtl, opt_offset, opt_length) { |     bitjs.io.BitStream = function(ab, rtl, optOffset, optLength) { | ||||||
|         if (!ab || !ab.toString || ab.toString() !== "[object ArrayBuffer]") { |         if (!ab || !ab.toString || ab.toString() !== "[object ArrayBuffer]") { | ||||||
|             throw "Error! BitArray constructed with an invalid ArrayBuffer object"; |             throw "Error! BitArray constructed with an invalid ArrayBuffer object"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         var offset = opt_offset || 0; |         var offset = optOffset || 0; | ||||||
|         var length = opt_length || ab.byteLength; |         var length = optLength || ab.byteLength; | ||||||
|         this.bytes = new Uint8Array(ab, offset, length); |         this.bytes = new Uint8Array(ab, offset, length); | ||||||
|         this.bytePtr = 0; // tracks which byte we are on |         this.bytePtr = 0; // tracks which byte we are on | ||||||
|         this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) |         this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) | ||||||
|         this.peekBits = rtl ? this.peekBits_rtl : this.peekBits_ltr; |         this.peekBits = rtl ? this.peekBitsRtl : this.peekBitsLtr; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -57,8 +60,8 @@ bitjs.io = bitjs.io || {}; | |||||||
|      * @param {boolean=} movePointers Whether to move the pointer, defaults false. |      * @param {boolean=} movePointers Whether to move the pointer, defaults false. | ||||||
|      * @return {number} The peeked bits, as an unsigned number. |      * @return {number} The peeked bits, as an unsigned number. | ||||||
|      */ |      */ | ||||||
|     bitjs.io.BitStream.prototype.peekBits_ltr = function(n, movePointers) { |     bitjs.io.BitStream.prototype.peekBitsLtr = function(n, movePointers) { | ||||||
|         if (n <= 0 || typeof n != typeof 1) { |         if (n <= 0 || typeof n !== typeof 1) { | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -77,12 +80,13 @@ bitjs.io = bitjs.io || {}; | |||||||
|             if (bytePtr >= bytes.length) { |             if (bytePtr >= bytes.length) { | ||||||
|                 throw "Error!  Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + |                 throw "Error!  Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + | ||||||
|                     bytes.length + ", bitPtr=" + bitPtr; |                     bytes.length + ", bitPtr=" + bitPtr; | ||||||
|                 return -1; |                 // return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             var numBitsLeftInThisByte = (8 - bitPtr); |             var numBitsLeftInThisByte = (8 - bitPtr); | ||||||
|  |             var mask; | ||||||
|             if (n >= numBitsLeftInThisByte) { |             if (n >= numBitsLeftInThisByte) { | ||||||
|                 var mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); |                 mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); | ||||||
|                 result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); |                 result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); | ||||||
|  |  | ||||||
|                 bytePtr++; |                 bytePtr++; | ||||||
| @@ -90,7 +94,7 @@ bitjs.io = bitjs.io || {}; | |||||||
|                 bitsIn += numBitsLeftInThisByte; |                 bitsIn += numBitsLeftInThisByte; | ||||||
|                 n -= numBitsLeftInThisByte; |                 n -= numBitsLeftInThisByte; | ||||||
|             } else { |             } else { | ||||||
|                 var mask = (BITMASK[n] << bitPtr); |                 mask = (BITMASK[n] << bitPtr); | ||||||
|                 result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); |                 result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); | ||||||
|  |  | ||||||
|                 bitPtr += n; |                 bitPtr += n; | ||||||
| @@ -118,8 +122,8 @@ bitjs.io = bitjs.io || {}; | |||||||
|      * @param {boolean=} movePointers Whether to move the pointer, defaults false. |      * @param {boolean=} movePointers Whether to move the pointer, defaults false. | ||||||
|      * @return {number} The peeked bits, as an unsigned number. |      * @return {number} The peeked bits, as an unsigned number. | ||||||
|      */ |      */ | ||||||
|     bitjs.io.BitStream.prototype.peekBits_rtl = function(n, movePointers) { |     bitjs.io.BitStream.prototype.peekBitsRtl = function(n, movePointers) { | ||||||
|         if (n <= 0 || typeof n != typeof 1) { |         if (n <= 0 || typeof n !== typeof 1) { | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -138,7 +142,7 @@ bitjs.io = bitjs.io || {}; | |||||||
|             if (bytePtr >= bytes.length) { |             if (bytePtr >= bytes.length) { | ||||||
|                 throw "Error!  Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + |                 throw "Error!  Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + | ||||||
|                     bytes.length + ", bitPtr=" + bitPtr; |                     bytes.length + ", bitPtr=" + bitPtr; | ||||||
|                 return -1; |                 // return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             var numBitsLeftInThisByte = (8 - bitPtr); |             var numBitsLeftInThisByte = (8 - bitPtr); | ||||||
| @@ -198,19 +202,19 @@ bitjs.io = bitjs.io || {}; | |||||||
|      * @return {Uint8Array} The subarray. |      * @return {Uint8Array} The subarray. | ||||||
|      */ |      */ | ||||||
|     bitjs.io.BitStream.prototype.peekBytes = function(n, movePointers) { |     bitjs.io.BitStream.prototype.peekBytes = function(n, movePointers) { | ||||||
|         if (n <= 0 || typeof n != typeof 1) { |         if (n <= 0 || typeof n !== typeof 1) { | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // from http://tools.ietf.org/html/rfc1951#page-11 |         // from http://tools.ietf.org/html/rfc1951#page-11 | ||||||
|         // "Any bits of input up to the next byte boundary are ignored." |         // "Any bits of input up to the next byte boundary are ignored." | ||||||
|         while (this.bitPtr != 0) { |         while (this.bitPtr !== 0) { | ||||||
|             this.readBits(1); |             this.readBits(1); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         var movePointers = movePointers || false; |         var movePointers = movePointers || false; | ||||||
|         var bytePtr = this.bytePtr, |         var bytePtr = this.bytePtr; | ||||||
|             bitPtr = this.bitPtr; |         // bitPtr = this.bitPtr; | ||||||
|  |  | ||||||
|         var result = this.bytes.subarray(bytePtr, bytePtr + n); |         var result = this.bytes.subarray(bytePtr, bytePtr + n); | ||||||
|  |  | ||||||
| @@ -230,4 +234,4 @@ bitjs.io = bitjs.io || {}; | |||||||
|         return this.peekBytes(n, true); |         return this.peekBytes(n, true); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| })(); | })(); | ||||||
|   | |||||||
| @@ -8,6 +8,9 @@ | |||||||
|  * Copyright(c) 2011 Google Inc. |  * Copyright(c) 2011 Google Inc. | ||||||
|  * Copyright(c) 2011 antimatter15 |  * Copyright(c) 2011 antimatter15 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | /* global bitjs, Uint8Array */ | ||||||
|  |  | ||||||
| var bitjs = bitjs || {}; | var bitjs = bitjs || {}; | ||||||
| bitjs.io = bitjs.io || {}; | bitjs.io = bitjs.io || {}; | ||||||
|  |  | ||||||
| @@ -20,7 +23,7 @@ bitjs.io = bitjs.io || {}; | |||||||
|      * @constructor |      * @constructor | ||||||
|      */ |      */ | ||||||
|     bitjs.io.ByteBuffer = function(numBytes) { |     bitjs.io.ByteBuffer = function(numBytes) { | ||||||
|         if (typeof numBytes != typeof 1 || numBytes <= 0) { |         if (typeof numBytes !== typeof 1 || numBytes <= 0) { | ||||||
|             throw "Error! ByteBuffer initialized with '" + numBytes + "'"; |             throw "Error! ByteBuffer initialized with '" + numBytes + "'"; | ||||||
|         } |         } | ||||||
|         this.data = new Uint8Array(numBytes); |         this.data = new Uint8Array(numBytes); | ||||||
| @@ -55,14 +58,14 @@ bitjs.io = bitjs.io || {}; | |||||||
|      */ |      */ | ||||||
|     bitjs.io.ByteBuffer.prototype.writeNumber = function(num, numBytes) { |     bitjs.io.ByteBuffer.prototype.writeNumber = function(num, numBytes) { | ||||||
|         if (numBytes < 1) { |         if (numBytes < 1) { | ||||||
|             throw 'Trying to write into too few bytes: ' + numBytes; |             throw "Trying to write into too few bytes: " + numBytes; | ||||||
|         } |         } | ||||||
|         if (num < 0) { |         if (num < 0) { | ||||||
|             throw 'Trying to write a negative number (' + num + |             throw "Trying to write a negative number (" + num + | ||||||
|                 ') as an unsigned number to an ArrayBuffer'; |                 ") as an unsigned number to an ArrayBuffer"; | ||||||
|         } |         } | ||||||
|         if (num > (Math.pow(2, numBytes * 8) - 1)) { |         if (num > (Math.pow(2, numBytes * 8) - 1)) { | ||||||
|             throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; |             throw "Trying to write " + num + " into only " + numBytes + " bytes"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Roll 8-bits at a time into an array of bytes. |         // Roll 8-bits at a time into an array of bytes. | ||||||
| @@ -85,12 +88,12 @@ bitjs.io = bitjs.io || {}; | |||||||
|      */ |      */ | ||||||
|     bitjs.io.ByteBuffer.prototype.writeSignedNumber = function(num, numBytes) { |     bitjs.io.ByteBuffer.prototype.writeSignedNumber = function(num, numBytes) { | ||||||
|         if (numBytes < 1) { |         if (numBytes < 1) { | ||||||
|             throw 'Trying to write into too few bytes: ' + numBytes; |             throw "Trying to write into too few bytes: " + numBytes; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         var HALF = Math.pow(2, (numBytes * 8) - 1); |         var HALF = Math.pow(2, (numBytes * 8) - 1); | ||||||
|         if (num >= HALF || num < -HALF) { |         if (num >= HALF || num < -HALF) { | ||||||
|             throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; |             throw "Trying to write " + num + " into only " + numBytes + " bytes"; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // Roll 8-bits at a time into an array of bytes. |         // Roll 8-bits at a time into an array of bytes. | ||||||
| @@ -112,10 +115,10 @@ bitjs.io = bitjs.io || {}; | |||||||
|         for (var i = 0; i < str.length; ++i) { |         for (var i = 0; i < str.length; ++i) { | ||||||
|             var curByte = str.charCodeAt(i); |             var curByte = str.charCodeAt(i); | ||||||
|             if (curByte < 0 || curByte > 255) { |             if (curByte < 0 || curByte > 255) { | ||||||
|                 throw 'Trying to write a non-ASCII string!'; |                 throw "Trying to write a non-ASCII string!"; | ||||||
|             } |             } | ||||||
|             this.insertByte(curByte); |             this.insertByte(curByte); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| })(); | })(); | ||||||
|   | |||||||
| @@ -8,6 +8,9 @@ | |||||||
|  * Copyright(c) 2011 Google Inc. |  * Copyright(c) 2011 Google Inc. | ||||||
|  * Copyright(c) 2011 antimatter15 |  * Copyright(c) 2011 antimatter15 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | /* global bitjs, Uint8Array */ | ||||||
|  |  | ||||||
| var bitjs = bitjs || {}; | var bitjs = bitjs || {}; | ||||||
| bitjs.io = bitjs.io || {}; | bitjs.io = bitjs.io || {}; | ||||||
|  |  | ||||||
| @@ -19,13 +22,13 @@ bitjs.io = bitjs.io || {}; | |||||||
|      * out of an ArrayBuffer.  In this buffer, everything must be byte-aligned. |      * out of an ArrayBuffer.  In this buffer, everything must be byte-aligned. | ||||||
|      * |      * | ||||||
|      * @param {ArrayBuffer} ab The ArrayBuffer object. |      * @param {ArrayBuffer} ab The ArrayBuffer object. | ||||||
|      * @param {number=} opt_offset The offset into the ArrayBuffer |      * @param {number=} optOffset The offset into the ArrayBuffer | ||||||
|      * @param {number=} opt_length The length of this BitStream |      * @param {number=} optLength The length of this BitStream | ||||||
|      * @constructor |      * @constructor | ||||||
|      */ |      */ | ||||||
|     bitjs.io.ByteStream = function(ab, opt_offset, opt_length) { |     bitjs.io.ByteStream = function(ab, optOffset, optLength) { | ||||||
|         var offset = opt_offset || 0; |         var offset = optOffset || 0; | ||||||
|         var length = opt_length || ab.byteLength; |         var length = optLength || ab.byteLength; | ||||||
|         this.bytes = new Uint8Array(ab, offset, length); |         this.bytes = new Uint8Array(ab, offset, length); | ||||||
|         this.ptr = 0; |         this.ptr = 0; | ||||||
|     }; |     }; | ||||||
| @@ -40,8 +43,9 @@ bitjs.io = bitjs.io || {}; | |||||||
|      */ |      */ | ||||||
|     bitjs.io.ByteStream.prototype.peekNumber = function(n) { |     bitjs.io.ByteStream.prototype.peekNumber = function(n) { | ||||||
|         // TODO: return error if n would go past the end of the stream? |         // TODO: return error if n would go past the end of the stream? | ||||||
|         if (n <= 0 || typeof n != typeof 1) |         if (n <= 0 || typeof n !== typeof 1) { | ||||||
|             return -1; |             return -1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         var result = 0; |         var result = 0; | ||||||
|         // read from last byte to first byte and roll them in |         // read from last byte to first byte and roll them in | ||||||
| @@ -105,7 +109,7 @@ bitjs.io = bitjs.io || {}; | |||||||
|      * @return {Uint8Array} The subarray. |      * @return {Uint8Array} The subarray. | ||||||
|      */ |      */ | ||||||
|     bitjs.io.ByteStream.prototype.peekBytes = function(n, movePointers) { |     bitjs.io.ByteStream.prototype.peekBytes = function(n, movePointers) { | ||||||
|         if (n <= 0 || typeof n != typeof 1) { |         if (n <= 0 || typeof n !== typeof 1) { | ||||||
|             return null; |             return null; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -135,7 +139,7 @@ bitjs.io = bitjs.io || {}; | |||||||
|      * @return {string} The next n bytes as a string. |      * @return {string} The next n bytes as a string. | ||||||
|      */ |      */ | ||||||
|     bitjs.io.ByteStream.prototype.peekString = function(n) { |     bitjs.io.ByteStream.prototype.peekString = function(n) { | ||||||
|         if (n <= 0 || typeof n != typeof 1) { |         if (n <= 0 || typeof n !== typeof 1) { | ||||||
|             return ""; |             return ""; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										609
									
								
								cps/static/js/logviewer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										609
									
								
								cps/static/js/logviewer.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,609 @@ | |||||||
|  | var threadregexp = /(?:^| - )(\[[^\]]*\]):/; | ||||||
|  |  | ||||||
|  | var colors = ["#ffa", "#aaf", "#afa", "#aff", "#faf", "#aaa", "#fd8", "#f80", "#4df", "#4fc", "#76973c", "#7e56d8", "#99593d", "#37778a", "#4068fc"]; | ||||||
|  | var screenlines = 1; | ||||||
|  |  | ||||||
|  | var file = null; | ||||||
|  | var text; | ||||||
|  |  | ||||||
|  | var current, nextFilterId = 1; | ||||||
|  | var shl = null; | ||||||
|  | var hl = []; | ||||||
|  | var groupwith = false; | ||||||
|  | var filterswitch = true; | ||||||
|  |  | ||||||
|  | var selectedlineid = -1; | ||||||
|  | var selectedthread = null; | ||||||
|  | var reachedbottom = false; | ||||||
|  | var reachedtop = false; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function wheelscroll(event) | ||||||
|  | { | ||||||
|  |   renderincremental(event.deltaY); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function keypress(event) | ||||||
|  | { | ||||||
|  |   if (event.key == "PageDown") { | ||||||
|  |     _render(screenlines - 1); | ||||||
|  |     event.preventDefault(); | ||||||
|  |   } | ||||||
|  |   if (event.key == "PageUp") { | ||||||
|  |     _render(-(screenlines - 1)); | ||||||
|  |     event.preventDefault(); | ||||||
|  |   } | ||||||
|  |   if (event.key == "Home" && event.ctrlKey) { | ||||||
|  |     selectedlineid = 0; | ||||||
|  |     render(); | ||||||
|  |     event.preventDefault(); | ||||||
|  |   } | ||||||
|  |   if (event.key == "End" && event.ctrlKey) { | ||||||
|  |     selectedlineid = text.length - 1; | ||||||
|  |     render(); | ||||||
|  |     event.preventDefault(); | ||||||
|  |   } | ||||||
|  |   if (event.key == "ArrowUp") { | ||||||
|  |     renderincremental(-1); | ||||||
|  |     event.preventDefault(); | ||||||
|  |   } | ||||||
|  |   if (event.key == "ArrowDown") { | ||||||
|  |     renderincremental(1); | ||||||
|  |     event.preventDefault(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function init(filename) { | ||||||
|  |   document.addEventListener("wheel", wheelscroll, false); | ||||||
|  |   document.addEventListener("keypress", keypress, false); | ||||||
|  |   window.addEventListener("resize", resize, false); | ||||||
|  |  | ||||||
|  |   _resize(); | ||||||
|  |  | ||||||
|  |   var s = document.getElementById("search"); | ||||||
|  |   s.value = ""; | ||||||
|  |   selectfilter(0); | ||||||
|  |   reload(filename); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _resize() | ||||||
|  | { | ||||||
|  |   var d = document.getElementById("renderer"); | ||||||
|  |   var t = document.getElementById("toobar"); | ||||||
|  |   screenlines = Math.floor((window.innerHeight - t.offsetHeight) / d.firstChild.offsetHeight) - 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function resize() | ||||||
|  | { | ||||||
|  |   _resize(); | ||||||
|  |   repaint(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function reload(filename) | ||||||
|  | { | ||||||
|  |   if (shl) shl.cache = {}; | ||||||
|  |   for (_hl of hl) _hl.cache = {}; | ||||||
|  |  | ||||||
|  |   var q = filename; | ||||||
|  |   document.title = "Log: " + (q || "none loaded"); | ||||||
|  |   if (!q) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   var d = document.getElementById("renderer"); | ||||||
|  |   d.innerHTML = "loading " + q + "..."; | ||||||
|  |  | ||||||
|  |   var r = new XMLHttpRequest(); | ||||||
|  |   r.open("GET", "/ajax/accesslog", true); | ||||||
|  |   r.responseType = 'text'; | ||||||
|  |   r.onload = function() { | ||||||
|  |     console.log("prepare"); | ||||||
|  |     prepare(r.responseText); | ||||||
|  |     if (selectedlineid > text.length) { | ||||||
|  |       selectedlineid = -1; | ||||||
|  |     } | ||||||
|  |     console.log("render"); | ||||||
|  |     render(); | ||||||
|  |   }; | ||||||
|  |   r.send(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _sanitize(t) | ||||||
|  | { | ||||||
|  |   t = t | ||||||
|  |     .replace(/&/g, "&") | ||||||
|  |     .replace(/ /g, " ") | ||||||
|  |     .replace(/</g, "<") | ||||||
|  |     .replace(/>/g, ">"); | ||||||
|  |  | ||||||
|  |   return t; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _prepare(t) | ||||||
|  | { | ||||||
|  |   // sanitization happens in render, since otherwise it eats enormous amount of memory. | ||||||
|  |   /* | ||||||
|  |   var t = t.split('\n'); | ||||||
|  |   for (var i in t) { | ||||||
|  |     t[i] = _sanitize(t[i]); | ||||||
|  |   } | ||||||
|  |   return t; | ||||||
|  |   */ | ||||||
|  |   return /*_sanitize*/(t).split('\n'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function prepare(t) | ||||||
|  | { | ||||||
|  |   text = _prepare(t); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function render() | ||||||
|  | { | ||||||
|  |   _render(0, false); // completely redraws the view from the current scroll position | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function repaint() | ||||||
|  | { | ||||||
|  |   _render(0, true); // completely redraws the view, but centers the selected line | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function renderincremental(difference) | ||||||
|  | { | ||||||
|  |   _render(difference);  // "scrolls" the view | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _render(increment, repaintonly) | ||||||
|  | { | ||||||
|  |   var epoch  = new Date(); | ||||||
|  |  | ||||||
|  |   var d = document.getElementById("renderer"); | ||||||
|  |   var filter = _gfilteron(); | ||||||
|  |  | ||||||
|  |   function process(i, append) | ||||||
|  |   { | ||||||
|  |     var t = _sanitize(text[i]); | ||||||
|  |  | ||||||
|  |     var lhl = false; | ||||||
|  |     function dohl(_hl) | ||||||
|  |     { | ||||||
|  |       if (_hl.cache[i] === false) { | ||||||
|  |         // lhl is here unaffected | ||||||
|  |         return t; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       var t2 = t.replace(new RegExp("(" + _hl.text_r + ")", "g"), "<span style='background-color:" + _hl.color + ";'>$1</span>"); | ||||||
|  |       var affecting = (t != t2); | ||||||
|  |       _hl.cache[i] = affecting; | ||||||
|  |       lhl = lhl || (affecting && (!filter || _hl.filter)); | ||||||
|  |       return t2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (var h in hl) | ||||||
|  |       t = dohl(hl[h]); | ||||||
|  |     if (shl) | ||||||
|  |       t = dohl(shl); | ||||||
|  |  | ||||||
|  |     if (filter && !lhl && i != selectedlineid) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lhl = lhl && !filter; | ||||||
|  |     var div = document.createElement("div"); | ||||||
|  |     div.id = i; | ||||||
|  |     if (lhl) div.className = 'lhl'; | ||||||
|  |     div.onclick = function() { selectline(this); }; | ||||||
|  |     div.innerHTML = t; | ||||||
|  |     if (t.match(new RegExp(selectedthread, "g"))) div.className += ' thread'; | ||||||
|  |  | ||||||
|  |     if (append) | ||||||
|  |       d.appendChild(div); | ||||||
|  |     else | ||||||
|  |       d.insertBefore(div, d.firstChild); | ||||||
|  |  | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   var lefttodraw = Math.floor(Math.abs(increment)); | ||||||
|  |  | ||||||
|  |   if (increment < 0) { | ||||||
|  |     // scroll up | ||||||
|  |     reachedbottom = false; | ||||||
|  |     if (reachedtop) { | ||||||
|  |       _hint("reached top of the file"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     for (var i = parseInt(d.firstChild.id) - 1; lefttodraw && i >= 0 && i < text.length; --i) { | ||||||
|  |       if (process(i, false)) { | ||||||
|  |         --lefttodraw; | ||||||
|  |         if (d.childNodes.length > screenlines) | ||||||
|  |           d.removeChild(d.lastChild); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (lefttodraw) { | ||||||
|  |       _hint("reached top of the file"); | ||||||
|  |       reachedtop = true; | ||||||
|  |     } | ||||||
|  |   } else if (increment > 0) { | ||||||
|  |     // scroll down | ||||||
|  |     reachedtop = false; | ||||||
|  |     if (reachedbottom) { | ||||||
|  |       _hint("reached bottom of the file"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     for (var i = parseInt(d.lastChild.id) + 1; lefttodraw && i < text.length; ++i) { | ||||||
|  |       if (process(i, true)) { | ||||||
|  |         --lefttodraw; | ||||||
|  |         if (d.childNodes.length > screenlines) | ||||||
|  |           d.removeChild(d.firstChild); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (lefttodraw) { | ||||||
|  |       _hint("reached bottom of the file"); | ||||||
|  |       reachedbottom = true; | ||||||
|  |     } | ||||||
|  |   } else { // == 0 | ||||||
|  |     // redraw all | ||||||
|  |     reachedbottom = false; | ||||||
|  |     reachedtop = false; | ||||||
|  |     lefttodraw = screenlines; | ||||||
|  |     var i = repaintonly ? parseInt(d.firstChild.id) : selectedlineid; | ||||||
|  |     if (i < 0) i = 0; | ||||||
|  |  | ||||||
|  |     d.innerHTML = ""; | ||||||
|  |     for (; lefttodraw && i < text.length; ++i) { | ||||||
|  |       if (process(i, true)) { | ||||||
|  |         --lefttodraw; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!repaintonly && selectedlineid > -1) { | ||||||
|  |       // center the selected line in the middle of screen! | ||||||
|  |       _render(-(screenlines / 2)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   selectline(selectedlineid); | ||||||
|  |  | ||||||
|  |   var now = new Date(); | ||||||
|  |   console.log("rendered in " + (now.getTime() - epoch.getTime()) + "ms"); | ||||||
|  |  | ||||||
|  |   var pos = document.getElementById("position"); | ||||||
|  |   pos.textContent = Math.round((parseInt(d.firstChild.id) / text.length) * 1000) / 10 + "%"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _hint(h) | ||||||
|  | { | ||||||
|  |   document.getElementById("hint").innerHTML = h; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _gfilteron() | ||||||
|  | { | ||||||
|  |   if (!filterswitch) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   if (shl && shl.filter) | ||||||
|  |     return true; | ||||||
|  |  | ||||||
|  |   for (var h in hl) | ||||||
|  |   { | ||||||
|  |     if (hl[h].filter) | ||||||
|  |       return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _getfilterelement(filter) | ||||||
|  | { | ||||||
|  |   if (filter == 0) | ||||||
|  |     return document.getElementById("search"); | ||||||
|  |  | ||||||
|  |   return document.getElementById("filter" + filter); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _setfilterelementstate(p0, _hl) | ||||||
|  | { | ||||||
|  |   p0.style.textDecoration = _hl.filter ? "underline" : ""; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _triminput(t) | ||||||
|  | { | ||||||
|  |   t = t | ||||||
|  |     .replace(/^\s+/, "") | ||||||
|  |     .replace(/\s+$/, "") | ||||||
|  |   ; | ||||||
|  |   return t; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _regexpescape(t) | ||||||
|  | { | ||||||
|  |   t = t | ||||||
|  |     .replace(/\\/g, "\\\\") | ||||||
|  |     .replace(/\?/g, "\\?") | ||||||
|  |     .replace(/\./g, "\\.") | ||||||
|  |     .replace(/\+/g, "\\+") | ||||||
|  |     .replace(/\*/g, "\\*") | ||||||
|  |     .replace(/\^/g, "\\^") | ||||||
|  |     .replace(/\$/g, "\\$") | ||||||
|  |     .replace(/\(/g, "\\(") | ||||||
|  |     .replace(/\)/g, "\\)") | ||||||
|  |     .replace(/\[/g, "\\[") | ||||||
|  |     .replace(/\]/g, "\\]") | ||||||
|  |     .replace(/\|/g, "\\|") | ||||||
|  |   ; | ||||||
|  |   return t; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function resetuistate() | ||||||
|  | { | ||||||
|  |   groupwith = false; | ||||||
|  |   _hint(""); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function newhl(t, p, persistent) | ||||||
|  | { | ||||||
|  |   return { | ||||||
|  |     id: persistent ? nextFilterId++ : 0, | ||||||
|  |     text: t, | ||||||
|  |     text_r: _sanitize(_regexpescape(t)), | ||||||
|  |     color: p ? p.color : colors[0], | ||||||
|  |     filter: p ? p.filter : false, | ||||||
|  |     cache: {} | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function selectline(id) | ||||||
|  | { | ||||||
|  |   var l0 = document.getElementById(selectedlineid); | ||||||
|  |   if (l0) | ||||||
|  |     l0.style.backgroundColor = ""; | ||||||
|  |  | ||||||
|  |   var l1 = null; | ||||||
|  |   if (typeof(id) == "object") { | ||||||
|  |     l1 = id; | ||||||
|  |     id = parseInt(l1.id); | ||||||
|  |   } else { | ||||||
|  |     l1 = document.getElementById(id); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   selectedlineid = id; | ||||||
|  |   if (selectedlineid > -1) | ||||||
|  |     _hint("line # " + (selectedlineid + 1)); | ||||||
|  |  | ||||||
|  |   if (l1) { | ||||||
|  |     l1.style.background = "#faa"; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   var thread = null; | ||||||
|  |   var m = text[selectedlineid].match(threadregexp); | ||||||
|  |   if (m) thread = _regexpescape(_sanitize(m[1])); | ||||||
|  |   if (thread != selectedthread) { | ||||||
|  |     selectedthread = thread; | ||||||
|  |     repaint(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return l1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function mouseup(event) | ||||||
|  | { | ||||||
|  |   if (event.ctrlKey) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   resetuistate(); | ||||||
|  |  | ||||||
|  |   var s = window.getSelection(); | ||||||
|  |   var t = _triminput(s.toString()); | ||||||
|  |   if (!t) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   s = document.getElementById("search"); | ||||||
|  |   s.value = t; | ||||||
|  |  | ||||||
|  |   t = _prepare(t)[0]; | ||||||
|  |  | ||||||
|  |   shl = newhl(t, shl); | ||||||
|  |   selectfilter(0); | ||||||
|  |   repaint(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function persist() | ||||||
|  | { | ||||||
|  |   resetuistate(); | ||||||
|  |  | ||||||
|  |   var dorender = false; | ||||||
|  |   if (!shl) | ||||||
|  |   { | ||||||
|  |     _apply(); | ||||||
|  |     dorender = true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (!shl) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   selectfilter(0); | ||||||
|  |  | ||||||
|  |   var _hl = newhl(shl.text, shl, true); | ||||||
|  |   _hl.cache = shl.cache; // hope this is right, shl is updated in _apply, that always creates an empty cache | ||||||
|  |   hl.push(_hl); | ||||||
|  |  | ||||||
|  |   var p = document.getElementById("persistents"); | ||||||
|  |   var p0 = document.createElement("div"); | ||||||
|  |   p0.id = "filter" + _hl.id; | ||||||
|  |   p0.className = "persistent"; | ||||||
|  |   p0.style.backgroundColor = _hl.color; | ||||||
|  |   p0.innerHTML = _hl.text; | ||||||
|  |   p0.onclick = function() {selectfilter(_hl.id)}; | ||||||
|  |   _setfilterelementstate(p0, _hl); | ||||||
|  |   p.appendChild(p0); | ||||||
|  |  | ||||||
|  |   _restartshl(); | ||||||
|  |   selectfilter(_hl.id); | ||||||
|  |   if (dorender) | ||||||
|  |     render(); | ||||||
|  |  | ||||||
|  |   colors.push(colors.shift()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _apply() | ||||||
|  | { | ||||||
|  |   s = document.getElementById("search"); | ||||||
|  |   var t = _triminput(s.value); | ||||||
|  |  | ||||||
|  |   if (!t) | ||||||
|  |   { | ||||||
|  |     shl = null; | ||||||
|  |   } | ||||||
|  |   else | ||||||
|  |   { | ||||||
|  |     t = _prepare(t)[0]; | ||||||
|  |     shl = newhl(t, shl); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function highlight() | ||||||
|  | { | ||||||
|  |   resetuistate(); | ||||||
|  |  | ||||||
|  |   _apply(); | ||||||
|  |  | ||||||
|  |   repaint(); | ||||||
|  |   selectfilter(0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function filter() | ||||||
|  | { | ||||||
|  |   resetuistate(); | ||||||
|  |  | ||||||
|  |   if ((!shl && !hl.length) || (current == shl)) | ||||||
|  |   { | ||||||
|  |     _apply(); | ||||||
|  |     selectfilter(0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (current) { | ||||||
|  |     current.filter = !current.filter; | ||||||
|  |     _setfilterelementstate(_getfilterelement(current.id), current); | ||||||
|  |     if (filterswitch) | ||||||
|  |       render(); | ||||||
|  |     else | ||||||
|  |       repaint(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function group() | ||||||
|  | { | ||||||
|  |   resetuistate(); | ||||||
|  |  | ||||||
|  |   // the code it self happens in selectfilter() function | ||||||
|  |  | ||||||
|  |   if (!shl) | ||||||
|  |   { | ||||||
|  |     _hint("press higlight or filter first"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (hl.length) | ||||||
|  |   { | ||||||
|  |     groupwith = true; | ||||||
|  |     _hint("-> now select a pinned filter to group the current highlight with"); | ||||||
|  |   } | ||||||
|  |   else | ||||||
|  |   { | ||||||
|  |     _hint("you have to pin a filter with the 'pin' button first"); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function _restartshl() | ||||||
|  | { | ||||||
|  |   var s = document.getElementById("search"); | ||||||
|  |   s.value = ""; | ||||||
|  |   s.style.backgroundColor = ""; | ||||||
|  |   s.style.textDecoration = ""; | ||||||
|  |   current = shl = null; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function restart() | ||||||
|  | { | ||||||
|  |   resetuistate(); | ||||||
|  |  | ||||||
|  |   var filtered = _gfilteron(); // was: = current && current.filter | ||||||
|  |  | ||||||
|  |   if (current == shl) | ||||||
|  |   { | ||||||
|  |     _restartshl(); | ||||||
|  |   } | ||||||
|  |   else | ||||||
|  |   { | ||||||
|  |     var p0 = _getfilterelement(current.id); | ||||||
|  |  | ||||||
|  |     for (var h in hl) | ||||||
|  |       if (hl[h].id == current.id) { | ||||||
|  |         hl.splice(h, 1); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |     if (current.text) { | ||||||
|  |       shl = newhl(current.text, current); | ||||||
|  |       var s = document.getElementById("search"); | ||||||
|  |       s.value = current.text; | ||||||
|  |       _setfilterelementstate(s, shl); | ||||||
|  |     } | ||||||
|  |     selectfilter(0); | ||||||
|  |  | ||||||
|  |     var p = document.getElementById("persistents"); | ||||||
|  |     p.removeChild(p0); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (!shl) // means: filter could not be switched back to shl or directly shl was reset | ||||||
|  |     render(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function selectfilter(filter) | ||||||
|  | { | ||||||
|  |   var el0 = _getfilterelement(current ? current.id : 0); | ||||||
|  |   var el1 = _getfilterelement(filter); | ||||||
|  |  | ||||||
|  |   el0.style.border = ""; | ||||||
|  |   el0.style.margin = ""; | ||||||
|  |   el1.style.border = "solid 2px #3ad"; | ||||||
|  |   el1.style.margin = "0px"; | ||||||
|  |  | ||||||
|  |   function filterbyid(id) | ||||||
|  |   { | ||||||
|  |     for (var h in hl) | ||||||
|  |       if (hl[h].id == id) | ||||||
|  |         return hl[h]; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (groupwith && filter) | ||||||
|  |   { | ||||||
|  |     el1.innerHTML += "+" + shl.text; | ||||||
|  |     var _hl = filterbyid(filter); | ||||||
|  |     _hl.text = ""; // not backward compatible | ||||||
|  |     _hl.text_r += "|" + shl.text_r; | ||||||
|  |     _hl.cache = {}; | ||||||
|  |     resetuistate(); | ||||||
|  |     _restartshl(); | ||||||
|  |     render(); | ||||||
|  |   } | ||||||
|  |   else | ||||||
|  |     resetuistate(); | ||||||
|  |  | ||||||
|  |   current = (filter == 0) ? shl : filterbyid(filter); | ||||||
|  |  | ||||||
|  |   // A bit hacky redraw of the search box color :) | ||||||
|  |   if (filter === 0 && shl) | ||||||
|  |   { | ||||||
|  |     var s = document.getElementById("search"); | ||||||
|  |     s.style.backgroundColor = shl.color; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function flipfilter(event) | ||||||
|  | { | ||||||
|  |   filterswitch = !filterswitch; | ||||||
|  |   event.target.innerHTML = (filterswitch ? "filters on" : "filters off"); | ||||||
|  |   render(); | ||||||
|  |  | ||||||
|  |   event.stopPropagation(); | ||||||
|  | } | ||||||
| @@ -106,6 +106,7 @@ | |||||||
|   <div class="row"> |   <div class="row"> | ||||||
|     <div class="col"> |     <div class="col"> | ||||||
|       <h2>{{_('Administration')}}</h2> |       <h2>{{_('Administration')}}</h2> | ||||||
|  |       <div class="btn btn-default"><a id="logfile" href="{{url_for('admin.view_logfile')}}">{{_('View Logfile')}}</a></div> | ||||||
|       <div class="btn btn-default" id="restart_database">{{_('Reconnect to Calibre DB')}}</div> |       <div class="btn btn-default" id="restart_database">{{_('Reconnect to Calibre DB')}}</div> | ||||||
|       <div class="btn btn-default" id="admin_restart" data-toggle="modal" data-target="#RestartDialog">{{_('Restart Calibre-Web')}}</div> |       <div class="btn btn-default" id="admin_restart" data-toggle="modal" data-target="#RestartDialog">{{_('Restart Calibre-Web')}}</div> | ||||||
|       <div class="btn btn-default" id="admin_stop" data-toggle="modal" data-target="#ShutdownDialog">{{_('Stop Calibre-Web')}}</div> |       <div class="btn btn-default" id="admin_stop" data-toggle="modal" data-target="#ShutdownDialog">{{_('Stop Calibre-Web')}}</div> | ||||||
|   | |||||||
							
								
								
									
										141
									
								
								cps/templates/logviewer.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								cps/templates/logviewer.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | |||||||
|  | {% extends "layout.html" %} | ||||||
|  | {% block body %} | ||||||
|  | {% block header %} | ||||||
|  | <style> | ||||||
|  |  | ||||||
|  | html, body { | ||||||
|  |   margin: 0; | ||||||
|  |   height: 100%; | ||||||
|  |   overflow-y: hidden | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div.log { | ||||||
|  |   font-family: Courier New; | ||||||
|  |   font-size: 12px; | ||||||
|  |   box-sizing: border-box; | ||||||
|  |   height: 500px; | ||||||
|  |   overflow-y: scroll; | ||||||
|  |   border-style: solid; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div.log { | ||||||
|  |   white-space: nowrap; | ||||||
|  |   padding: 0.5em; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div.lhl { | ||||||
|  |   background-color: #ffd; | ||||||
|  | } | ||||||
|  | div.thread { | ||||||
|  |   background: #fdd; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div.persistent, | ||||||
|  | div.persistents, | ||||||
|  | div.hint { | ||||||
|  |   display: inline; | ||||||
|  |   cursor: pointer; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div.hint, | ||||||
|  | div.persistent { | ||||||
|  |   font-family: Arial; | ||||||
|  |   font-size: 12px; | ||||||
|  |   padding: 2px; | ||||||
|  |   margin: 1px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div.hint { | ||||||
|  |   color: gray; | ||||||
|  |   font-style: italic; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div.button, | ||||||
|  | div.button2, | ||||||
|  | div.button_right { | ||||||
|  |   display: inline; | ||||||
|  |   font-weight: bold; | ||||||
|  |   font-family: Arial; | ||||||
|  |   font-size: 12px; | ||||||
|  |   padding: 2px; | ||||||
|  |   cursor: pointer; | ||||||
|  |   color: #3ad; | ||||||
|  |   _text-decoration: underline; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div.button2 { | ||||||
|  |   border: solid 1px gray; | ||||||
|  |   border-radius: 4px; | ||||||
|  |   color: black; | ||||||
|  |   text-decoration: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div.button_right { | ||||||
|  |   margin-right: 1em; | ||||||
|  |   float: right; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div.nounder { | ||||||
|  |   text-decoration: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | input.filebtn { | ||||||
|  |   float: right; | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | </style> | ||||||
|  | {% endblock %} | ||||||
|  | <!--body onload="load()"--> | ||||||
|  | <div id="toobar" class="toobar"> | ||||||
|  |   <input class="search" id="search" type="text" onmousedown="selectfilter(0)"></input> | ||||||
|  |   <div class="button2" onclick="highlight()" title="apply changes in the search box">apply</div> | ||||||
|  |   <div class="button_right" onclick="flipfilter(event)" title="disable/enable selected filters">filters on</div> | ||||||
|  |   <br/> | ||||||
|  |   <div class="button" onclick="filter()" title="show only lines containing the phrase">filter</div> | ||||||
|  |   <div class="hint"   onclick="repaint()" id="hint"></div> | ||||||
|  |   <div class="hint"   onclick="repaint()" id="position"></div> | ||||||
|  | </div> | ||||||
|  | <div class="clear"></div> | ||||||
|  |  | ||||||
|  | <div class="logpaginator"><a href="{{'/logs/1'}}"><span class="glyphicon glyphicon-fast-backward"></span></a>  <a href="{{ "/logs/"}}"><span class="glyphicon glyphicon-step-backward"></span></a>  <a href="{{ '/logs/' }}"><span class="glyphicon glyphicon-step-forward"></span></a> <a href="{{'/logs/'}}"><span class="glyphicon glyphicon-fast-forward"></span></a></div> | ||||||
|  | <div class="logperpage"> | ||||||
|  |   <form id="logform1" action="" method="POST"> | ||||||
|  |     <label for="reversed">{{_('Reversed')}}:</label> | ||||||
|  |     <input type="checkbox" name="reversed" id="reversed" onchange="this.form.submit();" {% if reversed %} checked="checked" {% endif %} />  | ||||||
|  |     <label for="perpage">{{_('Lines per page')}}:</label> | ||||||
|  |     <select name="perpage" id ="perpage" onchange="this.form.submit();"> | ||||||
|  |       {% for key, value in  perpage_p.items() %} | ||||||
|  |         <option value="{{key}}"{% if value== perpage %} selected="selected" {% endif %}>{{value}}</option> | ||||||
|  |       {% endfor %} | ||||||
|  |     </select> | ||||||
|  |   </form> | ||||||
|  | </div> | ||||||
|  | <div class="logwarn">{{warning}}</div> | ||||||
|  | <div class="clear"></div> | ||||||
|  | <div class="logdiv"> | ||||||
|  |   <table class="logtable" cellpadding="0" cellspacing="0"> | ||||||
|  |    <div id="renderer" class="log" onmouseup="mouseup(event)"><div>nothing loaded</div></div> | ||||||
|  |   {% for line in log %} | ||||||
|  |     <tr><td class="logline">{{line.line}}</td><td>{{line.date}}</td><td class="loglevel">{{line.level}}</td><td>{{line.message}}</td></tr> | ||||||
|  |   {% endfor %} | ||||||
|  |   </table> | ||||||
|  | </div> | ||||||
|  | <div class="logform"> | ||||||
|  |   <form id="logform2" action="" method="POST" style="width: auto"> | ||||||
|  |     <label for="from" style="display: inline-block; float: left; margin-right: 5px; height: 34px; line-height: 34px;">{{_('Jump to time:')}}</label> | ||||||
|  |     <input style="display: inline-block; text-align: center; float: left; width: 155px;" class="form-control" type="text" name="from" id="from" size="15" value="{{from}}"/> | ||||||
|  |     <input style="display: inline-block; float: left; margin-left: 5px;" class="btn btn-default" type="submit" value="{{_('Go')}}" /> | ||||||
|  |   </form> | ||||||
|  | </div> | ||||||
|  | {% endblock %} | ||||||
|  | {% block js %} | ||||||
|  | <script src="{{ url_for('static', filename='js/logviewer.js') }}"></script> | ||||||
|  | <script> | ||||||
|  | $(function(){ | ||||||
|  |   init('access.log'); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | {% endblock %} | ||||||
| @@ -3,7 +3,6 @@ universal = 1 | |||||||
|  |  | ||||||
| [metadata] | [metadata] | ||||||
| name = calibreweb | name = calibreweb | ||||||
| version=  0.6.3 |  | ||||||
| url = https://github.com/janeczku/calibre-web | url = https://github.com/janeczku/calibre-web | ||||||
| project_urls = | project_urls = | ||||||
|   Bug Tracker = https://github.com/janeczku/calibre-web/issues |   Bug Tracker = https://github.com/janeczku/calibre-web/issues | ||||||
| @@ -38,8 +37,6 @@ console_scripts = | |||||||
|   cps = calibreweb:main |   cps = calibreweb:main | ||||||
| [options] | [options] | ||||||
| python_requires = >=2.6 | python_requires = >=2.6 | ||||||
| package_dir= |  | ||||||
|     =src |  | ||||||
| include_package_data = True | include_package_data = True | ||||||
| zip_safe = False | zip_safe = False | ||||||
| install_requires = | install_requires = | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								setup.py
									
									
									
									
									
								
							| @@ -20,6 +20,25 @@ | |||||||
| #  """Calibre-web distribution package setuptools installer.""" | #  """Calibre-web distribution package setuptools installer.""" | ||||||
|  |  | ||||||
| from setuptools import setup | from setuptools import setup | ||||||
|  | import os | ||||||
|  | import re | ||||||
|  | import codecs | ||||||
|  |  | ||||||
|  | here = os.path.abspath(os.path.dirname(__file__)) | ||||||
|  |  | ||||||
|  | def read(*parts): | ||||||
|  |     with codecs.open(os.path.join(here, *parts), 'r') as fp: | ||||||
|  |         return fp.read() | ||||||
|  |  | ||||||
|  | def find_version(*file_paths): | ||||||
|  |     version_file = read(*file_paths) | ||||||
|  |     version_match = re.search(r"^STABLE_VERSION\s+=\s+{['\"]version['\"]:\s*['\"](.*)['\"]}", | ||||||
|  |                               version_file, re.M) | ||||||
|  |     if version_match: | ||||||
|  |         return version_match.group(1) | ||||||
|  |     raise RuntimeError("Unable to find version string.") | ||||||
|  |  | ||||||
| setup( | setup( | ||||||
|     package_dir = {'': 'src'}) |     package_dir = {'': 'src'}, | ||||||
|  |     version=find_version("src", "calibreweb", "cps", "constants.py") | ||||||
|  | ) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Ozzieisaacs
					Ozzieisaacs