diff --git a/cps/constants.py b/cps/constants.py
index ccb0d8a8..ed127136 100644
--- a/cps/constants.py
+++ b/cps/constants.py
@@ -81,10 +81,10 @@ SIDEBAR_PUBLISHER = 1 << 12
SIDEBAR_RATING = 1 << 13
SIDEBAR_FORMAT = 1 << 14
SIDEBAR_ARCHIVED = 1 << 15
-# SIDEBAR_LIST = 1 << 16
+SIDEBAR_LIST = 1 << 16
ADMIN_USER_ROLES = sum(r for r in ALL_ROLES.values()) & ~ROLE_ANONYMOUS
-ADMIN_USER_SIDEBAR = (SIDEBAR_ARCHIVED << 1) - 1
+ADMIN_USER_SIDEBAR = (SIDEBAR_LIST << 1) - 1
UPDATE_STABLE = 0 << 0
AUTO_UPDATE_STABLE = 1 << 0
diff --git a/cps/db.py b/cps/db.py
index 9eb8985d..a5a01aca 100644
--- a/cps/db.py
+++ b/cps/db.py
@@ -29,7 +29,8 @@ from sqlalchemy import create_engine
from sqlalchemy import Table, Column, ForeignKey, CheckConstraint
from sqlalchemy import String, Integer, Boolean, TIMESTAMP, Float
from sqlalchemy.orm import relationship, sessionmaker, scoped_session
-from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm.collections import InstrumentedList
+from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
from sqlalchemy.pool import StaticPool
from flask_login import current_user
from sqlalchemy.sql.expression import and_, true, false, text, func, or_
@@ -97,6 +98,9 @@ class Identifiers(Base):
self.type = id_type
self.book = book
+ #def get(self):
+ # return {self.type: self.val}
+
def formatType(self):
if self.type == "amazon":
return u"Amazon"
@@ -149,6 +153,9 @@ class Comments(Base):
self.text = text
self.book = book
+ def get(self):
+ return self.text
+
def __repr__(self):
return u"".format(self.text)
@@ -162,6 +169,9 @@ class Tags(Base):
def __init__(self, name):
self.name = name
+ def get(self):
+ return self.name
+
def __repr__(self):
return u"".format(self.name)
@@ -179,6 +189,9 @@ class Authors(Base):
self.sort = sort
self.link = link
+ def get(self):
+ return self.name
+
def __repr__(self):
return u"".format(self.name, self.sort, self.link)
@@ -194,6 +207,9 @@ class Series(Base):
self.name = name
self.sort = sort
+ def get(self):
+ return self.name
+
def __repr__(self):
return u"".format(self.name, self.sort)
@@ -207,6 +223,9 @@ class Ratings(Base):
def __init__(self, rating):
self.rating = rating
+ def get(self):
+ return self.rating
+
def __repr__(self):
return u"".format(self.rating)
@@ -220,6 +239,12 @@ class Languages(Base):
def __init__(self, lang_code):
self.lang_code = lang_code
+ def get(self):
+ if self.language_name:
+ return self.language_name
+ else:
+ return self.lang_code
+
def __repr__(self):
return u"".format(self.lang_code)
@@ -235,6 +260,9 @@ class Publishers(Base):
self.name = name
self.sort = sort
+ def get(self):
+ return self.name
+
def __repr__(self):
return u"".format(self.name, self.sort)
@@ -255,6 +283,10 @@ class Data(Base):
self.uncompressed_size = uncompressed_size
self.name = name
+ # ToDo: Check
+ def get(self):
+ return self.name
+
def __repr__(self):
return u"".format(self.book, self.format, self.uncompressed_size, self.name)
@@ -262,14 +294,14 @@ class Data(Base):
class Books(Base):
__tablename__ = 'books'
- DEFAULT_PUBDATE = "0101-01-01 00:00:00+00:00"
+ DEFAULT_PUBDATE = datetime(101, 1, 1, 0, 0, 0, 0) # ("0101-01-01 00:00:00+00:00")
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(collation='NOCASE'), nullable=False, default='Unknown')
sort = Column(String(collation='NOCASE'))
author_sort = Column(String(collation='NOCASE'))
timestamp = Column(TIMESTAMP, default=datetime.utcnow)
- pubdate = Column(String) # , default=datetime.utcnow)
+ pubdate = Column(TIMESTAMP, default=DEFAULT_PUBDATE)
series_index = Column(String, nullable=False, default="1.0")
last_modified = Column(TIMESTAMP, default=datetime.utcnow)
path = Column(String, default="", nullable=False)
@@ -301,6 +333,9 @@ class Books(Base):
self.path = path
self.has_cover = has_cover
+ #def as_dict(self):
+ # return {c.name: getattr(self, c.name) for c in self.__table__.columns}
+
def __repr__(self):
return u"".format(self.title, self.sort, self.author_sort,
self.timestamp, self.pubdate, self.series_index,
@@ -329,6 +364,39 @@ class Custom_Columns(Base):
display_dict['enum_values'] = [x.decode('unicode_escape') for x in display_dict['enum_values']]
return display_dict
+class AlchemyEncoder(json.JSONEncoder):
+
+ def default(self, obj):
+ if isinstance(obj.__class__, DeclarativeMeta):
+ # an SQLAlchemy class
+ fields = {}
+ for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
+ if field == 'books':
+ continue
+ data = obj.__getattribute__(field)
+ try:
+ if isinstance(data, str):
+ data = data.replace("'","\'")
+ elif isinstance(data, InstrumentedList):
+ el =list()
+ for ele in data:
+ if ele.get:
+ el.append(ele.get())
+ else:
+ el.append(json.dumps(ele, cls=AlchemyEncoder))
+ data =",".join(el)
+ if data == '[]':
+ data = ""
+ else:
+ json.dumps(data)
+ fields[field] = data
+ except:
+ fields[field] = ""
+ # a json-encodable dict
+ return fields
+
+ return json.JSONEncoder.default(self, obj)
+
class CalibreDB():
@@ -494,10 +562,11 @@ class CalibreDB():
pos_content_cc_filter, ~neg_content_cc_filter, archived_filter)
# Fill indexpage with all requested data from database
- def fill_indexpage(self, page, database, db_filter, order, *join):
- return self.fill_indexpage_with_archived_books(page, database, db_filter, order, False, *join)
+ def fill_indexpage(self, page, pagesize, database, db_filter, order, *join):
+ return self.fill_indexpage_with_archived_books(page, pagesize, database, db_filter, order, False, *join)
- def fill_indexpage_with_archived_books(self, page, database, db_filter, order, allow_show_archived, *join):
+ def fill_indexpage_with_archived_books(self, page, pagesize, database, db_filter, order, allow_show_archived, *join):
+ pagesize = pagesize or self.config.config_books_per_page
if current_user.show_detail_random():
randm = self.session.query(Books) \
.filter(self.common_filters(allow_show_archived)) \
@@ -505,14 +574,14 @@ class CalibreDB():
.limit(self.config.config_random_books)
else:
randm = false()
- off = int(int(self.config.config_books_per_page) * (page - 1))
+ off = int(int(pagesize) * (page - 1))
query = self.session.query(database) \
.join(*join, isouter=True) \
.filter(db_filter) \
.filter(self.common_filters(allow_show_archived))
- pagination = Pagination(page, self.config.config_books_per_page,
+ pagination = Pagination(page, pagesize,
len(query.all()))
- entries = query.order_by(*order).offset(off).limit(self.config.config_books_per_page).all()
+ entries = query.order_by(*order).offset(off).limit(pagesize).all()
for book in entries:
book = self.order_authors(book)
return entries, randm, pagination
@@ -552,20 +621,26 @@ class CalibreDB():
.filter(and_(Books.authors.any(and_(*q)), func.lower(Books.title).ilike("%" + title + "%"))).first()
# read search results from calibre-database and return it (function is used for feed and simple search
- def get_search_results(self, term):
+ def get_search_results(self, term, offset=None, order=None, limit=None):
+ order = order or [Books.sort]
+ if offset != None and limit != None:
+ offset = int(offset)
+ limit = offset + int(limit)
term.strip().lower()
self.session.connection().connection.connection.create_function("lower", 1, lcase)
q = list()
authorterms = re.split("[, ]+", term)
for authorterm in authorterms:
q.append(Books.authors.any(func.lower(Authors.name).ilike("%" + authorterm + "%")))
- return self.session.query(Books).filter(self.common_filters(True)).filter(
+ result = self.session.query(Books).filter(self.common_filters(True)).filter(
or_(Books.tags.any(func.lower(Tags.name).ilike("%" + term + "%")),
Books.series.any(func.lower(Series.name).ilike("%" + term + "%")),
Books.authors.any(and_(*q)),
Books.publishers.any(func.lower(Publishers.name).ilike("%" + term + "%")),
func.lower(Books.title).ilike("%" + term + "%")
- )).order_by(Books.sort).all()
+ )).order_by(*order).all()
+ result_count = len(result)
+ return result[offset:limit], result_count
# Creates for all stored languages a translated speaking name in the array for the UI
def speaking_language(self, languages=None):
diff --git a/cps/editbooks.py b/cps/editbooks.py
index 83b4afd1..1de44d98 100644
--- a/cps/editbooks.py
+++ b/cps/editbooks.py
@@ -27,6 +27,7 @@ import json
from shutil import copyfile
from uuid import uuid4
+from babel import Locale as LC
from flask import Blueprint, request, flash, redirect, url_for, abort, Markup, Response
from flask_babel import gettext as _
from flask_login import current_user, login_required
@@ -171,21 +172,42 @@ def modify_identifiers(input_identifiers, db_identifiers, db_session):
changed = True
return changed
+@editbook.route("/ajax/delete/")
+@login_required
+def delete_book_from_details(book_id):
+ return Response(delete_book(book_id,"", True), mimetype='application/json')
+
-@editbook.route("/delete//", defaults={'book_format': ""})
-@editbook.route("/delete///")
+@editbook.route("/delete/", defaults={'book_format': ""})
+@editbook.route("/delete//")
@login_required
-def delete_book(book_id, book_format):
+def delete_book_ajax(book_id, book_format):
+ return delete_book(book_id,book_format, False)
+
+def delete_book(book_id, book_format, jsonResponse):
+ warning = {}
if current_user.role_delete_books():
book = calibre_db.get_book(book_id)
if book:
try:
result, error = helper.delete_book(book, config.config_calibre_dir, book_format=book_format.upper())
if not result:
- flash(error, category="error")
- return redirect(url_for('editbook.edit_book', book_id=book_id))
+ if jsonResponse:
+ return json.dumps({"location": url_for("editbook.edit_book"),
+ "type": "alert",
+ "format": "",
+ "error": error}),
+ else:
+ flash(error, category="error")
+ return redirect(url_for('editbook.edit_book', book_id=book_id))
if error:
- flash(error, category="warning")
+ if jsonResponse:
+ warning = {"location": url_for("editbook.edit_book"),
+ "type": "warning",
+ "format": "",
+ "error": error}
+ else:
+ flash(error, category="warning")
if not book_format:
# delete book from Shelfs, Downloads, Read list
ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).delete()
@@ -241,11 +263,23 @@ def delete_book(book_id, book_format):
# book not found
log.error('Book with id "%s" could not be deleted: not found', book_id)
if book_format:
- flash(_('Book Format Successfully Deleted'), category="success")
- return redirect(url_for('editbook.edit_book', book_id=book_id))
+ if jsonResponse:
+ return json.dumps([warning, {"location": url_for("editbook.edit_book", book_id=book_id),
+ "type": "success",
+ "format": book_format,
+ "message": _('Book Format Successfully Deleted')}])
+ else:
+ flash(_('Book Format Successfully Deleted'), category="success")
+ return redirect(url_for('editbook.edit_book', book_id=book_id))
else:
- flash(_('Book Successfully Deleted'), category="success")
- return redirect(url_for('web.index'))
+ if jsonResponse:
+ return json.dumps([warning, {"location": url_for('web.index'),
+ "type": "success",
+ "format": book_format,
+ "message": _('Book Successfully Deleted')}])
+ else:
+ flash(_('Book Successfully Deleted'), category="success")
+ return redirect(url_for('web.index'))
def render_edit_book(book_id):
@@ -896,3 +930,112 @@ def convert_bookformat(book_id):
else:
flash(_(u"There was an error converting this book: %(res)s", res=rtn), category="error")
return redirect(url_for('editbook.edit_book', book_id=book_id))
+
+@editbook.route("/ajax/editbooks/", methods=['POST'])
+@login_required_if_no_ano
+def edit_list_book(param):
+ vals = request.form.to_dict()
+ # calibre_db.update_title_sort(config)
+ #calibre_db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
+ book = calibre_db.get_book(vals['pk'])
+ if param =='series_index':
+ edit_book_series_index(vals['value'], book)
+ elif param =='tags':
+ edit_book_tags(vals['value'], book)
+ elif param =='series':
+ edit_book_series(vals['value'], book)
+ elif param =='publishers':
+ vals['publisher'] = vals['value']
+ edit_book_publisher(vals, book)
+ elif param =='languages':
+ edit_book_languages(vals['value'], book)
+ elif param =='author_sort':
+ book.author_sort = vals['value']
+ elif param =='title':
+ book.title = vals['value']
+ helper.update_dir_stucture(book.id, config.config_calibre_dir)
+ elif param =='sort':
+ book.sort = vals['value']
+ # ToDo: edit books
+ elif param =='authors':
+ input_authors = vals['value'].split('&')
+ input_authors = list(map(lambda it: it.strip().replace(',', '|'), input_authors))
+ modify_database_object(input_authors, book.authors, db.Authors, calibre_db.session, 'author')
+ sort_authors_list = list()
+ for inp in input_authors:
+ stored_author = calibre_db.session.query(db.Authors).filter(db.Authors.name == inp).first()
+ if not stored_author:
+ stored_author = helper.get_sorted_author(inp)
+ else:
+ stored_author = stored_author.sort
+ sort_authors_list.append(helper.get_sorted_author(stored_author))
+ sort_authors = ' & '.join(sort_authors_list)
+ if book.author_sort != sort_authors:
+ book.author_sort = sort_authors
+ helper.update_dir_stucture(book.id, config.config_calibre_dir, input_authors[0])
+ book.last_modified = datetime.utcnow()
+ calibre_db.session.commit()
+ return ""
+
+@editbook.route("/ajax/sort_value//")
+@login_required
+def get_sorted_entry(field, bookid):
+ if field == 'title' or field == 'authors':
+ book = calibre_db.get_filtered_book(bookid)
+ if book:
+ if field == 'title':
+ return json.dumps({'sort': book.sort})
+ elif field == 'authors':
+ return json.dumps({'author_sort': book.author_sort})
+ return ""
+
+
+@editbook.route("/ajax/simulatemerge", methods=['POST'])
+@login_required
+def simulate_merge_list_book():
+ vals = request.get_json().get('Merge_books')
+ if vals:
+ to_book = calibre_db.get_book(vals[0]).title
+ vals.pop(0)
+ if to_book:
+ for book_id in vals:
+ from_book = []
+ from_book.append(calibre_db.get_book(book_id).title)
+ return json.dumps({'to': to_book, 'from': from_book})
+ return ""
+
+
+@editbook.route("/ajax/mergebooks", methods=['POST'])
+@login_required
+def merge_list_book():
+ vals = request.get_json().get('Merge_books')
+ to_file = list()
+ if vals:
+ # load all formats from target book
+ to_book = calibre_db.get_book(vals[0])
+ vals.pop(0)
+ if to_book:
+ for file in to_book.data:
+ to_file.append(file.format)
+ to_name = helper.get_valid_filename(to_book.title) + ' - ' + \
+ helper.get_valid_filename(to_book.authors[0].name)
+ for book_id in vals:
+ from_book = calibre_db.get_book(book_id)
+ if from_book:
+ for element in from_book.data:
+ if element.format not in to_file:
+ # create new data entry with: book_id, book_format, uncompressed_size, name
+ filepath_new = os.path.normpath(os.path.join(config.config_calibre_dir,
+ to_book.path,
+ to_name + "." + element.format.lower()))
+ filepath_old = os.path.normpath(os.path.join(config.config_calibre_dir,
+ from_book.path,
+ element.name + "." + element.format.lower()))
+ copyfile(filepath_old, filepath_new)
+ to_book.data.append(db.Data(to_book.id,
+ element.format,
+ element.uncompressed_size,
+ to_name))
+ delete_book(from_book.id,"", True) # json_resp =
+ return json.dumps({'success': True})
+ return ""
diff --git a/cps/helper.py b/cps/helper.py
index d11af912..4f05c48a 100644
--- a/cps/helper.py
+++ b/cps/helper.py
@@ -137,7 +137,7 @@ def send_registration_mail(e_mail, user_name, default_password, resend=False):
taskMessage=_(u"Registration e-mail for user: %(name)s", name=user_name),
text=text
))
-
+
return
diff --git a/cps/jinjia.py b/cps/jinjia.py
index c91534eb..a6df156d 100644
--- a/cps/jinjia.py
+++ b/cps/jinjia.py
@@ -76,22 +76,18 @@ def mimetype_filter(val):
@jinjia.app_template_filter('formatdate')
def formatdate_filter(val):
try:
- conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val)
- formatdate = datetime.datetime.strptime(conformed_timestamp[:15], "%Y%m%d %H%M%S")
- return format_date(formatdate, format='medium', locale=get_locale())
+ return format_date(val, format='medium', locale=get_locale())
except AttributeError as e:
log.error('Babel error: %s, Current user locale: %s, Current User: %s', e,
current_user.locale,
current_user.nickname
)
- return formatdate
+ return val
@jinjia.app_template_filter('formatdateinput')
def format_date_input(val):
- conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val)
- date_obj = datetime.datetime.strptime(conformed_timestamp[:15], "%Y%m%d %H%M%S")
- input_date = date_obj.isoformat().split('T', 1)[0] # Hack to support dates <1900
+ input_date = val.isoformat().split('T', 1)[0] # Hack to support dates <1900
return '' if input_date == "0101-01-01" else input_date
diff --git a/cps/opds.py b/cps/opds.py
index 78d5d8ed..ac0e103b 100644
--- a/cps/opds.py
+++ b/cps/opds.py
@@ -100,7 +100,7 @@ def feed_normal_search():
@requires_basic_auth_if_no_ano
def feed_new():
off = request.args.get("offset") or 0
- entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
+ entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books, True, [db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@@ -118,7 +118,7 @@ def feed_discover():
@requires_basic_auth_if_no_ano
def feed_best_rated():
off = request.args.get("offset") or 0
- entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
+ entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books, db.Books.ratings.any(db.Ratings.rating > 9),
[db.Books.timestamp.desc()])
return render_xml_template('feed.xml', entries=entries, pagination=pagination)
@@ -164,7 +164,7 @@ def feed_authorindex():
@requires_basic_auth_if_no_ano
def feed_author(book_id):
off = request.args.get("offset") or 0
- entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
+ entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books,
db.Books.authors.any(db.Authors.id == book_id),
[db.Books.timestamp.desc()])
@@ -190,7 +190,7 @@ def feed_publisherindex():
@requires_basic_auth_if_no_ano
def feed_publisher(book_id):
off = request.args.get("offset") or 0
- entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
+ entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books,
db.Books.publishers.any(db.Publishers.id == book_id),
[db.Books.timestamp.desc()])
@@ -218,7 +218,7 @@ def feed_categoryindex():
@requires_basic_auth_if_no_ano
def feed_category(book_id):
off = request.args.get("offset") or 0
- entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
+ entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books,
db.Books.tags.any(db.Tags.id == book_id),
[db.Books.timestamp.desc()])
@@ -245,7 +245,7 @@ def feed_seriesindex():
@requires_basic_auth_if_no_ano
def feed_series(book_id):
off = request.args.get("offset") or 0
- entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
+ entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books,
db.Books.series.any(db.Series.id == book_id),
[db.Books.series_index])
@@ -276,7 +276,7 @@ def feed_ratingindex():
@requires_basic_auth_if_no_ano
def feed_ratings(book_id):
off = request.args.get("offset") or 0
- entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
+ entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books,
db.Books.ratings.any(db.Ratings.id == book_id),
[db.Books.timestamp.desc()])
@@ -304,7 +304,7 @@ def feed_formatindex():
@requires_basic_auth_if_no_ano
def feed_format(book_id):
off = request.args.get("offset") or 0
- entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
+ entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books,
db.Books.data.any(db.Data.format == book_id.upper()),
[db.Books.timestamp.desc()])
@@ -338,7 +338,7 @@ def feed_languagesindex():
@requires_basic_auth_if_no_ano
def feed_languages(book_id):
off = request.args.get("offset") or 0
- entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
+ entries, __, pagination = calibre_db.fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1), 0,
db.Books,
db.Books.languages.any(db.Languages.id == book_id),
[db.Books.timestamp.desc()])
@@ -408,7 +408,7 @@ def get_metadata_calibre_companion(uuid, library):
def feed_search(term):
if term:
- entries = calibre_db.get_search_results(term)
+ entries, __ = calibre_db.get_search_results(term)
entriescount = len(entries) if len(entries) > 0 else 1
pagination = Pagination(1, entriescount, entriescount)
return render_xml_template('feed.xml', searchterm=term, entries=entries, pagination=pagination)
diff --git a/cps/static/css/caliBlur_override.css b/cps/static/css/caliBlur_override.css
index 05a7c0d8..7f940212 100644
--- a/cps/static/css/caliBlur_override.css
+++ b/cps/static/css/caliBlur_override.css
@@ -5,7 +5,7 @@ body.serieslist.grid-view div.container-fluid>div>div.col-sm-10:before{
.cover .badge{
position: absolute;
top: 0;
- right: 0;
+ left: 0;
background-color: #cc7b19;
border-radius: 0;
padding: 0 8px;
diff --git a/cps/static/css/libs/bootstrap-table.min.css b/cps/static/css/libs/bootstrap-table.min.css
index 770b6728..72a8f74f 100644
--- a/cps/static/css/libs/bootstrap-table.min.css
+++ b/cps/static/css/libs/bootstrap-table.min.css
@@ -1 +1,10 @@
-.fixed-table-container .bs-checkbox,.fixed-table-container .no-records-found{text-align:center}.fixed-table-body thead th .th-inner,.table td,.table th{box-sizing:border-box}.bootstrap-table .table{margin-bottom:0!important;border-bottom:1px solid #ddd;border-collapse:collapse!important;border-radius:1px}.bootstrap-table .table:not(.table-condensed),.bootstrap-table .table:not(.table-condensed)>tbody>tr>td,.bootstrap-table .table:not(.table-condensed)>tbody>tr>th,.bootstrap-table .table:not(.table-condensed)>tfoot>tr>td,.bootstrap-table .table:not(.table-condensed)>tfoot>tr>th,.bootstrap-table .table:not(.table-condensed)>thead>tr>td{padding:8px}.bootstrap-table .table.table-no-bordered>tbody>tr>td,.bootstrap-table .table.table-no-bordered>thead>tr>th{border-right:2px solid transparent}.bootstrap-table .table.table-no-bordered>tbody>tr>td:last-child{border-right:none}.fixed-table-container{position:relative;clear:both;border:1px solid #ddd;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px}.fixed-table-container.table-no-bordered{border:1px solid transparent}.fixed-table-footer,.fixed-table-header{overflow:hidden}.fixed-table-footer{border-top:1px solid #ddd}.fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.fixed-table-container table{width:100%}.fixed-table-container thead th{height:0;padding:0;margin:0;border-left:1px solid #ddd}.fixed-table-container thead th:focus{outline:transparent solid 0}.fixed-table-container thead th:first-child:not([data-not-first-th]){border-left:none;border-top-left-radius:4px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px}.fixed-table-container tbody td .th-inner,.fixed-table-container thead th .th-inner{padding:8px;line-height:24px;vertical-align:top;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fixed-table-container thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px}.fixed-table-container thead th .both{background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC')}.fixed-table-container thead th .asc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==)}.fixed-table-container thead th .desc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII=)}.fixed-table-container th.detail{width:30px}.fixed-table-container tbody td{border-left:1px solid #ddd}.fixed-table-container tbody tr:first-child td{border-top:none}.fixed-table-container tbody td:first-child{border-left:none}.fixed-table-container tbody .selected td{background-color:#f5f5f5}.fixed-table-container input[type=radio],.fixed-table-container input[type=checkbox]{margin:0 auto!important}.fixed-table-pagination .pagination-detail,.fixed-table-pagination div.pagination{margin-top:10px;margin-bottom:10px}.fixed-table-pagination div.pagination .pagination{margin:0}.fixed-table-pagination .pagination a{padding:6px 12px;line-height:1.428571429}.fixed-table-pagination .pagination-info{line-height:34px;margin-right:5px}.fixed-table-pagination .btn-group{position:relative;display:inline-block;vertical-align:middle}.fixed-table-pagination .dropup .dropdown-menu{margin-bottom:0}.fixed-table-pagination .page-list{display:inline-block}.fixed-table-toolbar .columns-left{margin-right:5px}.fixed-table-toolbar .columns-right{margin-left:5px}.fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.428571429}.fixed-table-toolbar .bs-bars,.fixed-table-toolbar .columns,.fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px;line-height:34px}.fixed-table-pagination li.disabled a{pointer-events:none;cursor:default}.fixed-table-loading{display:none;position:absolute;top:42px;right:0;bottom:0;left:0;z-index:99;background-color:#fff;text-align:center}.fixed-table-body .card-view .title{font-weight:700;display:inline-block;min-width:30%;text-align:left!important}.table td,.table th{vertical-align:middle}.fixed-table-toolbar .dropdown-menu{text-align:left;max-height:300px;overflow:auto}.fixed-table-toolbar .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.fixed-table-toolbar .btn-group>.btn-group>.btn{border-radius:0}.fixed-table-toolbar .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.fixed-table-toolbar .btn-group>.btn-group:last-child>.btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.bootstrap-table .table>thead>tr>th{vertical-align:bottom;border-bottom:1px solid #ddd}.bootstrap-table .table thead>tr>th{padding:0;margin:0}.bootstrap-table .fixed-table-footer tbody>tr>td{padding:0!important}.bootstrap-table .fixed-table-footer .table{border-bottom:none;border-radius:0;padding:0!important}.bootstrap-table .pull-right .dropdown-menu{right:0;left:auto}p.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden}.fixed-table-pagination:after,.fixed-table-toolbar:after{content:"";display:block;clear:both}.fullscreen{position:fixed;top:0;left:0;z-index:1050;width:100%!important;background:#FFF}
\ No newline at end of file
+/**
+ * bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
+ *
+ * @version v1.16.0
+ * @homepage https://bootstrap-table.com
+ * @author wenzhixin (http://wenzhixin.net.cn/)
+ * @license MIT
+ */
+
+.bootstrap-table .fixed-table-toolbar::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-toolbar .bs-bars,.bootstrap-table .fixed-table-toolbar .columns,.bootstrap-table .fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group>.btn{border-radius:0}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:last-child>.btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu{text-align:left;max-height:300px;overflow:auto;-ms-overflow-style:scrollbar;z-index:1001}.bootstrap-table .fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.428571429}.bootstrap-table .fixed-table-toolbar .columns-left{margin-right:5px}.bootstrap-table .fixed-table-toolbar .columns-right{margin-left:5px}.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu{right:0;left:auto}.bootstrap-table .fixed-table-container{position:relative;clear:both}.bootstrap-table .fixed-table-container .table{width:100%;margin-bottom:0!important}.bootstrap-table .fixed-table-container .table td,.bootstrap-table .fixed-table-container .table th{vertical-align:middle;box-sizing:border-box}.bootstrap-table .fixed-table-container .table thead th{vertical-align:bottom;padding:0;margin:0}.bootstrap-table .fixed-table-container .table thead th:focus{outline:0 solid transparent}.bootstrap-table .fixed-table-container .table thead th.detail{width:30px}.bootstrap-table .fixed-table-container .table thead th .th-inner{padding:.75rem;vertical-align:bottom;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bootstrap-table .fixed-table-container .table thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px!important}.bootstrap-table .fixed-table-container .table thead th .both{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC")}.bootstrap-table .fixed-table-container .table thead th .asc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==)}.bootstrap-table .fixed-table-container .table thead th .desc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII=)}.bootstrap-table .fixed-table-container .table tbody tr.selected td{background-color:rgba(0,0,0,.075)}.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td{text-align:center}.bootstrap-table .fixed-table-container .table tbody tr .card-view{display:flex}.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title{font-weight:700;display:inline-block;min-width:30%;text-align:left!important}.bootstrap-table .fixed-table-container .table .bs-checkbox{text-align:center}.bootstrap-table .fixed-table-container .table .bs-checkbox label{margin-bottom:0}.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=checkbox],.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=radio]{margin:0 auto!important}.bootstrap-table .fixed-table-container .table.table-sm .th-inner{padding:.3rem}.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer){border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height.has-card-view{border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border{border-left:1px solid #dee2e6;border-right:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table thead th{border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th{border-bottom:1px solid #32383e}.bootstrap-table .fixed-table-container .fixed-table-header{overflow:hidden}.bootstrap-table .fixed-table-container .fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading{align-items:center;background:#fff;display:none;justify-content:center;position:absolute;bottom:0;width:100%;z-index:1000}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap{align-items:baseline;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text{font-size:2rem;margin-right:6px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap{align-items:center;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before{content:"";animation-duration:1.5s;animation-iteration-count:infinite;animation-name:LOADING;background:#212529;border-radius:50%;display:block;height:5px;margin:0 4px;opacity:0;width:5px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot{animation-delay:.3s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after{animation-delay:.6s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark{background:#212529}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before{background:#fff}.bootstrap-table .fixed-table-container .fixed-table-footer{overflow:hidden}.bootstrap-table .fixed-table-pagination::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-pagination>.pagination,.bootstrap-table .fixed-table-pagination>.pagination-detail{margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-pagination>.pagination-detail .pagination-info{line-height:34px;margin-right:5px}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list{display:inline-block}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group{position:relative;display:inline-block;vertical-align:middle}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group .dropdown-menu{margin-bottom:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination{margin:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination a{padding:6px 12px;line-height:1.428571429}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a{color:#c8c8c8}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::before{content:'\2B05'}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::after{content:'\27A1'}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.disabled a{pointer-events:none;cursor:default}.bootstrap-table.fullscreen{position:fixed;top:0;left:0;z-index:1050;width:100%!important;background:#fff;height:calc(100vh);overflow-y:scroll}div.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden}@keyframes LOADING{0%{opacity:0}50%{opacity:1}to{opacity:0}}
\ No newline at end of file
diff --git a/cps/static/css/style.css b/cps/static/css/style.css
index 4d8b4805..e2cd4ec7 100644
--- a/cps/static/css/style.css
+++ b/cps/static/css/style.css
@@ -51,7 +51,22 @@ body h2 {
color:#444;
}
-a { color: #45b29d; }
+a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d; }
+
+.book-remove:hover { color: #23527c; }
+
+.btn-default a { color: #444; }
+
+.btn-default a:hover {
+ color: #45b29d;
+ text-decoration: None;
+}
+
+.btn-default:hover {
+ color: #45b29d;
+}
+
+.editable-click, a.editable-click, a.editable-click:hover { border-bottom: None; }
.navigation .nav-head {
text-transform: uppercase;
@@ -63,6 +78,7 @@ a { color: #45b29d; }
border-top: 1px solid #ccc;
padding-top: 20px;
}
+
.navigation li a {
color: #444;
text-decoration: none;
diff --git a/cps/static/js/archive/archive.js b/cps/static/js/archive/archive.js
index cb76321f..06c05624 100644
--- a/cps/static/js/archive/archive.js
+++ b/cps/static/js/archive/archive.js
@@ -411,6 +411,19 @@ bitjs.archive = bitjs.archive || {};
return "unrar.js";
};
+ /**
+ * Unrarrer5
+ * @extends {bitjs.archive.Unarchiver}
+ * @constructor
+ */
+ bitjs.archive.Unrarrer5 = function(arrayBuffer, optPathToBitJS) {
+ bitjs.base(this, arrayBuffer, optPathToBitJS);
+ };
+ bitjs.inherits(bitjs.archive.Unrarrer5, bitjs.archive.Unarchiver);
+ bitjs.archive.Unrarrer5.prototype.getScriptFileName = function() {
+ return "unrar5.js";
+ };
+
/**
* Untarrer
* @extends {bitjs.archive.Unarchiver}
diff --git a/cps/static/js/archive/unrar.js b/cps/static/js/archive/unrar.js
index fadb791e..3e2a45af 100644
--- a/cps/static/js/archive/unrar.js
+++ b/cps/static/js/archive/unrar.js
@@ -14,10 +14,10 @@
/* global VM_FIXEDGLOBALSIZE, VM_GLOBALMEMSIZE, MAXWINMASK, VM_GLOBALMEMADDR, MAXWINSIZE */
// This file expects to be invoked as a Worker (see onmessage below).
-importScripts("../io/bitstream.js");
+/*importScripts("../io/bitstream.js");
importScripts("../io/bytebuffer.js");
importScripts("archive.js");
-importScripts("rarvm.js");
+importScripts("rarvm.js");*/
// Progress variables.
var currentFilename = "";
@@ -29,19 +29,21 @@ var totalFilesInArchive = 0;
// Helper functions.
var info = function(str) {
- postMessage(new bitjs.archive.UnarchiveInfoEvent(str));
+ console.log(str);
+ // postMessage(new bitjs.archive.UnarchiveInfoEvent(str));
};
var err = function(str) {
- postMessage(new bitjs.archive.UnarchiveErrorEvent(str));
+ console.log(str);
+ // postMessage(new bitjs.archive.UnarchiveErrorEvent(str));
};
var postProgress = function() {
- postMessage(new bitjs.archive.UnarchiveProgressEvent(
+ /*postMessage(new bitjs.archive.UnarchiveProgressEvent(
currentFilename,
currentFileNumber,
currentBytesUnarchivedInFile,
currentBytesUnarchived,
totalUncompressedBytesInArchive,
- totalFilesInArchive));
+ totalFilesInArchive));*/
};
// shows a byte value as its hex representation
@@ -1298,7 +1300,7 @@ var unrar = function(arrayBuffer) {
totalUncompressedBytesInArchive = 0;
totalFilesInArchive = 0;
- postMessage(new bitjs.archive.UnarchiveStartEvent());
+ //postMessage(new bitjs.archive.UnarchiveStartEvent());
var bstream = new bitjs.io.BitStream(arrayBuffer, false /* rtl */);
var header = new RarVolumeHeader(bstream);
@@ -1348,7 +1350,7 @@ var unrar = function(arrayBuffer) {
localfile.unrar();
if (localfile.isValid) {
- postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
+ // postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
postProgress();
}
}
@@ -1358,7 +1360,7 @@ var unrar = function(arrayBuffer) {
} else {
err("Invalid RAR file");
}
- postMessage(new bitjs.archive.UnarchiveFinishEvent());
+ // postMessage(new bitjs.archive.UnarchiveFinishEvent());
};
// event.data.file has the ArrayBuffer.
diff --git a/cps/static/js/archive/unrar5.js b/cps/static/js/archive/unrar5.js
new file mode 100644
index 00000000..452989c0
--- /dev/null
+++ b/cps/static/js/archive/unrar5.js
@@ -0,0 +1,1371 @@
+/**
+ * unrar.js
+ *
+ * Licensed under the MIT License
+ *
+ * Copyright(c) 2011 Google Inc.
+ * Copyright(c) 2011 antimatter15
+ *
+ * Reference Documentation:
+ *
+ * http://kthoom.googlecode.com/hg/docs/unrar.html
+ */
+/* global bitjs, importScripts, RarVM, Uint8Array, UnpackFilter */
+/* global VM_FIXEDGLOBALSIZE, VM_GLOBALMEMSIZE, MAXWINMASK, VM_GLOBALMEMADDR, MAXWINSIZE */
+
+// This file expects to be invoked as a Worker (see onmessage below).
+/*importScripts("../io/bitstream.js");
+importScripts("../io/bytebuffer.js");
+importScripts("archive.js");
+importScripts("rarvm.js");*/
+
+// Progress variables.
+var currentFilename = "";
+var currentFileNumber = 0;
+var currentBytesUnarchivedInFile = 0;
+var currentBytesUnarchived = 0;
+var totalUncompressedBytesInArchive = 0;
+var totalFilesInArchive = 0;
+
+// Helper functions.
+var info = function(str) {
+ console.log(str)
+ //postMessage(new bitjs.archive.UnarchiveInfoEvent(str));
+};
+var err = function(str) {
+ console.log(str)
+ //postMessage(new bitjs.archive.UnarchiveErrorEvent(str));
+};
+var postProgress = function() {
+ /*postMessage(new bitjs.archive.UnarchiveProgressEvent(
+ currentFilename,
+ currentFileNumber,
+ currentBytesUnarchivedInFile,
+ currentBytesUnarchived,
+ totalUncompressedBytesInArchive,
+ totalFilesInArchive));*/
+};
+
+// shows a byte value as its hex representation
+var nibble = "0123456789ABCDEF";
+var byteValueToHexString = function(num) {
+ return nibble[num >> 4] + nibble[num & 0xF];
+};
+var twoByteValueToHexString = function(num) {
+ return nibble[(num >> 12) & 0xF] + nibble[(num >> 8) & 0xF] + nibble[(num >> 4) & 0xF] + nibble[num & 0xF];
+};
+
+
+// Volume Types
+var MAIN_HEAD = 0x01,
+ ENCRYPT_HEAD = 0x04,
+ FILE_HEAD = 0x02,
+ SERVICE_HEAD = 0x03,
+ // COMM_HEAD = 0x75,
+ // AV_HEAD = 0x76,
+ // SUB_HEAD = 0x77,
+ // PROTECT_HEAD = 0x78,
+ // SIGN_HEAD = 0x79,
+ // NEWSUB_HEAD = 0x7a,
+ ENDARC_HEAD = 0x05;
+
+// ============================================================================================== //
+
+var RarMainVolumeHeader = function(bstream) {
+ var headPos = bstream.bytePtr;
+ // byte 1,2
+ info("Rar Volume Header @" + bstream.bytePtr);
+
+ this.crc = bstream.readBits(16);
+ info(" crc=" + this.crc);
+
+ // byte 3
+ this.headType = bstream.readBits(8);
+ info(" headType=" + this.headType);
+
+ // Get flags
+ // bytes 4,5
+ this.flags = {};
+ this.flags.value = bstream.readBits(16);
+
+ // byte 6
+ this.headSize = bstream.readBits(8);
+ // byte 7
+ if (bstream.readBits(8) === 1) {
+ info(" RarVersion=5");
+ }
+ // byte 8
+ bstream.readBits(8);
+}
+
+var vint = function(bstream) {
+ var size = 0;
+ var result = 0;
+ var loop = 0 ;
+ do {
+ size = bstream.readBits(8);
+ result |= (size & 0x7F) << (loop * 7);
+ loop++;
+ } while (size & 0x80 )
+ return result;
+}
+
+/**
+ * @param {bitjs.io.BitStream} bstream
+ * @constructor
+ */
+var RarVolumeHeader = function(bstream) {
+ var headPos = bstream.bytePtr;
+ // byte 1,2
+ info("Rar Volume Header @" + bstream.bytePtr);
+
+ this.crc = bstream.readBits(32);
+ info(" crc=" + this.crc);
+
+ // byte 3 + x Header size
+ this.headSize = vint(bstream);
+ info(" Header Size=" + this.headSize);
+
+ // byte 4
+ this.headType = bstream.readBits(8);
+ info(" headType=" + this.headType);
+
+ // Get Header flags
+ this.headFlags = {};
+ this.headFlags.value = bstream.peekBits(8);
+
+ info(" Header flags=" + byteValueToHexString(this.headFlags.value));
+ this.headFlags.EXTRA_AREA = !!bstream.readBits(1);
+ this.headFlags.DATA_AREA = !!bstream.readBits(1);
+ this.headFlags.UNKNOWN = !!bstream.readBits(1);
+ this.headFlags.CONTINUE_FROM = !!bstream.readBits(1);
+ this.headFlags.CONTNUE_TO = !!bstream.readBits(1);
+ this.headFlags.DEPENDS = !!bstream.readBits(1);
+ this.headFlags.CHILDBLOCK = !!bstream.readBits(1);
+ bstream.readBits(1); // unused*/
+
+ // Get extra AreaSize
+ if (this.headFlags.EXTRA_AREA) {
+ this.extraSize = vint(bstream);
+ } else {
+ this.extraSize = 0;
+ }
+ if (this.headFlags.DATA_AREA && (this.headType == FILE_HEAD || this.headType == SERVICE_HEAD)) {
+ this.packSize = vint(bstream);
+ // this.packSize = bstream.readBits(32);
+ }
+ this.flags = {};
+ this.flags.value = bstream.peekBits(8);
+
+ switch (this.headType) {
+ case MAIN_HEAD:
+ // this.flags = {};
+ // this.flags.value = bstream.peekBits(16);
+ this.flags.MHD_VOLUME = !!bstream.readBits(1);
+ this.flags.MHD_VOLUMNE_NO = !!bstream.readBits(1);
+ this.flags.MHD_SOLID = !!bstream.readBits(1);
+ this.flags.MHD_RECOVERY = !!bstream.readBits(1);
+ this.flags.MHD_LOCKED = !!bstream.readBits(1);
+ bstream.readBits(3); // unused*/
+ if (this.flags.MHD_VOLUMNE_NO) {
+ this.volumeNumber = vint(bstream);
+ }
+ bstream.readBytes(this.extraSize);
+ // break;
+ return; // Main Header finally parsed
+ // ------------
+ case FILE_HEAD:
+ case SERVICE_HEAD:
+ this.flags.DIRECTORY = !!bstream.readBits(1);
+ this.flags.TIME = !!bstream.readBits(1);
+ this.flags.CRC = !!bstream.readBits(1);
+ this.flags.UNPACK_UNKNOWN = !!bstream.readBits(1);
+ bstream.readBits(4);
+
+ if (this.flags.UNPACK_UNKNOWN) {
+ vint(bstream);
+ } else {
+ this.unpackedSize = vint(bstream);
+ }
+ this.fileAttr = vint(bstream);
+ if (this.flags.TIME) {
+ this.fileTime = bstream.readBits(32);
+ }
+ if (this.flags.CRC) {
+ this.fileCRC = bstream.readBits(32);
+ }
+ // var compInfo = vint(bstream);
+ this.unpVer = bstream.readBits(6);
+ this.solid = bstream.readBits(1);
+ bstream.readBits(1);
+ this.method = bstream.readBits(3);
+ this.dictSize = bstream.readBits(4);
+ bstream.readBits(1);
+ this.hostOS = vint(bstream);
+ this.nameSize = vint(bstream);
+
+ this.filename = bstream.readBytes(this.nameSize);
+ var _s = "";
+ for (var _i = 0; _i < this.filename.length ; _i++) {
+ _s += String.fromCharCode(this.filename[_i]);
+ }
+
+ this.filename = _s;
+ bstream.readBytes(this.extraSize);
+ break;
+
+ default:
+ info("Found a header of type 0x" + byteValueToHexString(this.headType));
+ // skip the rest of the header bytes (for now)
+ bstream.readBytes(this.headSize - 7);
+ break;
+ }
+};
+
+//var BLOCK_LZ = 0;
+
+var rLDecode = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224],
+ rLBits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5],
+ rDBitLengthCounts = [4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 14, 0, 12],
+ rSDDecode = [0, 4, 8, 16, 32, 64, 128, 192],
+ rSDBits = [2, 2, 3, 4, 5, 6, 6, 6];
+
+var rDDecode = [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32,
+ 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072,
+ 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304,
+ 131072, 196608, 262144, 327680, 393216, 458752, 524288, 589824,
+ 655360, 720896, 786432, 851968, 917504, 983040
+];
+
+var rDBits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5,
+ 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,
+ 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
+];
+
+var rLowDistRepCount = 16;
+
+var rNC = 299,
+ rDC = 60,
+ rLDC = 17,
+ rRC = 28,
+ rBC = 20,
+ rHuffTableSize = (rNC + rDC + rRC + rLDC);
+
+//var UnpBlockType = BLOCK_LZ;
+var UnpOldTable = new Array(rHuffTableSize);
+
+var BD = { //bitdecode
+ DecodeLen: new Array(16),
+ DecodePos: new Array(16),
+ DecodeNum: new Array(rBC)
+};
+var LD = { //litdecode
+ DecodeLen: new Array(16),
+ DecodePos: new Array(16),
+ DecodeNum: new Array(rNC)
+};
+var DD = { //distdecode
+ DecodeLen: new Array(16),
+ DecodePos: new Array(16),
+ DecodeNum: new Array(rDC)
+};
+var LDD = { //low dist decode
+ DecodeLen: new Array(16),
+ DecodePos: new Array(16),
+ DecodeNum: new Array(rLDC)
+};
+var RD = { //rep decode
+ DecodeLen: new Array(16),
+ DecodePos: new Array(16),
+ DecodeNum: new Array(rRC)
+};
+
+/**
+ * @type {Array}
+ */
+var rOldBuffers = [];
+
+/**
+ * The current buffer we are unpacking to.
+ * @type {bitjs.io.ByteBuffer}
+ */
+var rBuffer;
+
+/**
+ * The buffer of the final bytes after filtering (only used in Unpack29).
+ * @type {bitjs.io.ByteBuffer}
+ */
+var wBuffer;
+
+var lowDistRepCount = 0;
+var prevLowDist = 0;
+
+var rOldDist = [0, 0, 0, 0];
+var lastDist;
+var lastLength;
+
+/**
+ * In unpack.cpp, UnpPtr keeps track of what bytes have been unpacked
+ * into the Window buffer and WrPtr keeps track of what bytes have been
+ * actually written to disk after the unpacking and optional filtering
+ * has been done.
+ *
+ * In our case, rBuffer is the buffer for the unpacked bytes and wBuffer is
+ * the final output bytes.
+ */
+
+
+/**
+ * Read in Huffman tables for RAR
+ * @param {bitjs.io.BitStream} bstream
+ */
+function rarReadTables(bstream) {
+ var BitLength = new Array(rBC);
+ var Table = new Array(rHuffTableSize);
+ var i;
+ // before we start anything we need to get byte-aligned
+ bstream.readBits((8 - bstream.bitPtr) & 0x7);
+
+ if (bstream.readBits(1)) {
+ info("Error! PPM not implemented yet");
+ return;
+ }
+
+ if (!bstream.readBits(1)) { //discard old table
+ for (i = UnpOldTable.length; i--;) {
+ UnpOldTable[i] = 0;
+ }
+ }
+
+ // read in bit lengths
+ for (var I = 0; I < rBC; ++I) {
+ var Length = bstream.readBits(4);
+ if (Length === 15) {
+ var ZeroCount = bstream.readBits(4);
+ if (ZeroCount === 0) {
+ BitLength[I] = 15;
+ } else {
+ ZeroCount += 2;
+ while (ZeroCount-- > 0 && I < rBC) {
+ BitLength[I++] = 0;
+ }
+ --I;
+ }
+ } else {
+ BitLength[I] = Length;
+ }
+ }
+
+ // now all 20 bit lengths are obtained, we construct the Huffman Table:
+
+ rarMakeDecodeTables(BitLength, 0, BD, rBC);
+
+ var TableSize = rHuffTableSize;
+ //console.log(DecodeLen, DecodePos, DecodeNum);
+ for (i = 0; i < TableSize;) {
+ var N;
+ var num = rarDecodeNumber(bstream, BD);
+ if (num < 16) {
+ Table[i] = (num + UnpOldTable[i]) & 0xf;
+ i++;
+ } else if (num < 18) {
+ N = (num === 16) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11);
+
+ while (N-- > 0 && i < TableSize) {
+ Table[i] = Table[i - 1];
+ i++;
+ }
+ } else {
+ N = (num === 18) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11);
+
+ while (N-- > 0 && i < TableSize) {
+ Table[i++] = 0;
+ }
+ }
+ }
+
+ rarMakeDecodeTables(Table, 0, LD, rNC);
+ rarMakeDecodeTables(Table, rNC, DD, rDC);
+ rarMakeDecodeTables(Table, rNC + rDC, LDD, rLDC);
+ rarMakeDecodeTables(Table, rNC + rDC + rLDC, RD, rRC);
+
+ for (i = UnpOldTable.length; i--;) {
+ UnpOldTable[i] = Table[i];
+ }
+ return true;
+}
+
+
+function rarDecodeNumber(bstream, dec) {
+ var DecodeLen = dec.DecodeLen,
+ DecodePos = dec.DecodePos,
+ DecodeNum = dec.DecodeNum;
+ var bitField = bstream.getBits() & 0xfffe;
+ //some sort of rolled out binary search
+ var bits = ((bitField < DecodeLen[8]) ?
+ ((bitField < DecodeLen[4]) ?
+ ((bitField < DecodeLen[2]) ?
+ ((bitField < DecodeLen[1]) ? 1 : 2) :
+ ((bitField < DecodeLen[3]) ? 3 : 4)) :
+ (bitField < DecodeLen[6]) ?
+ ((bitField < DecodeLen[5]) ? 5 : 6) :
+ ((bitField < DecodeLen[7]) ? 7 : 8)) :
+ ((bitField < DecodeLen[12]) ?
+ ((bitField < DecodeLen[10]) ?
+ ((bitField < DecodeLen[9]) ? 9 : 10) :
+ ((bitField < DecodeLen[11]) ? 11 : 12)) :
+ (bitField < DecodeLen[14]) ?
+ ((bitField < DecodeLen[13]) ? 13 : 14) :
+ 15));
+ bstream.readBits(bits);
+ var N = DecodePos[bits] + ((bitField - DecodeLen[bits - 1]) >>> (16 - bits));
+
+ return DecodeNum[N];
+}
+
+
+function rarMakeDecodeTables(BitLength, offset, dec, size) {
+ var DecodeLen = dec.DecodeLen;
+ var DecodePos = dec.DecodePos;
+ var DecodeNum = dec.DecodeNum;
+ var LenCount = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ var TmpPos = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ var N = 0;
+ var M = 0;
+ var i;
+ for (i = DecodeNum.length; i--;) {
+ DecodeNum[i] = 0;
+ }
+ for (i = 0; i < size; i++) {
+ LenCount[BitLength[i + offset] & 0xF]++;
+ }
+ LenCount[0] = 0;
+ TmpPos[0] = 0;
+ DecodePos[0] = 0;
+ DecodeLen[0] = 0;
+
+ var I;
+ for (I = 1; I < 16; ++I) {
+ N = 2 * (N + LenCount[I]);
+ M = (N << (15 - I));
+ if (M > 0xFFFF) {
+ M = 0xFFFF;
+ }
+ DecodeLen[I] = M;
+ DecodePos[I] = DecodePos[I - 1] + LenCount[I - 1];
+ TmpPos[I] = DecodePos[I];
+ }
+ for (I = 0; I < size; ++I) {
+ if (BitLength[I + offset] !== 0) {
+ DecodeNum[TmpPos[BitLength[offset + I] & 0xF]++] = I;
+ }
+ }
+
+}
+
+// TODO: implement
+/**
+ * @param {bitjs.io.BitStream} bstream
+ * @param {boolean} Solid
+ */
+function unpack15() { //bstream, Solid) {
+ info("ERROR! RAR 1.5 compression not supported");
+}
+
+/**
+ * Unpacks the bit stream into rBuffer using the Unpack20 algorithm.
+ * @param {bitjs.io.BitStream} bstream
+ * @param {boolean} Solid
+ */
+function unpack20(bstream) { //, Solid) {
+ var destUnpSize = rBuffer.data.length;
+ var oldDistPtr = 0;
+ var Length;
+ var Distance;
+ rarReadTables20(bstream);
+ while (destUnpSize > rBuffer.ptr) {
+ var num = rarDecodeNumber(bstream, LD);
+ var Bits;
+ if (num < 256) {
+ rBuffer.insertByte(num);
+ continue;
+ }
+ if (num > 269) {
+ Length = rLDecode[num -= 270] + 3;
+ if ((Bits = rLBits[num]) > 0) {
+ Length += bstream.readBits(Bits);
+ }
+ var DistNumber = rarDecodeNumber(bstream, DD);
+ Distance = rDDecode[DistNumber] + 1;
+ if ((Bits = rDBits[DistNumber]) > 0) {
+ Distance += bstream.readBits(Bits);
+ }
+ if (Distance >= 0x2000) {
+ Length++;
+ if (Distance >= 0x40000) {
+ Length++;
+ }
+ }
+ lastLength = Length;
+ lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
+ rarCopyString(Length, Distance);
+ continue;
+ }
+ if (num === 269) {
+ rarReadTables20(bstream);
+ rarUpdateProgress();
+ continue;
+ }
+ if (num === 256) {
+ lastDist = rOldDist[oldDistPtr++ & 3] = lastDist;
+ rarCopyString(lastLength, lastDist);
+ continue;
+ }
+ if (num < 261) {
+ Distance = rOldDist[(oldDistPtr - (num - 256)) & 3];
+ var LengthNumber = rarDecodeNumber(bstream, RD);
+ Length = rLDecode[LengthNumber] + 2;
+ if ((Bits = rLBits[LengthNumber]) > 0) {
+ Length += bstream.readBits(Bits);
+ }
+ if (Distance >= 0x101) {
+ Length++;
+ if (Distance >= 0x2000) {
+ Length++;
+ if (Distance >= 0x40000) {
+ Length++;
+ }
+ }
+ }
+ lastLength = Length;
+ lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
+ rarCopyString(Length, Distance);
+ continue;
+ }
+ if (num < 270) {
+ Distance = rSDDecode[num -= 261] + 1;
+ if ((Bits = rSDBits[num]) > 0) {
+ Distance += bstream.readBits(Bits);
+ }
+ lastLength = 2;
+ lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
+ rarCopyString(2, Distance);
+ continue;
+ }
+ }
+ rarUpdateProgress();
+}
+
+function rarUpdateProgress() {
+ var change = rBuffer.ptr - currentBytesUnarchivedInFile;
+ currentBytesUnarchivedInFile = rBuffer.ptr;
+ currentBytesUnarchived += change;
+ postProgress();
+}
+
+var rNC20 = 298,
+ rDC20 = 48,
+ rRC20 = 28,
+ rBC20 = 19,
+ rMC20 = 257;
+
+var UnpOldTable20 = new Array(rMC20 * 4);
+
+function rarReadTables20(bstream) {
+ var BitLength = new Array(rBC20);
+ var Table = new Array(rMC20 * 4);
+ var TableSize, N, I;
+ var i;
+ bstream.readBits(1);
+ if (!bstream.readBits(1)) {
+ for (i = UnpOldTable20.length; i--;) {
+ UnpOldTable20[i] = 0;
+ }
+ }
+ TableSize = rNC20 + rDC20 + rRC20;
+ for (I = 0; I < rBC20; I++) {
+ BitLength[I] = bstream.readBits(4);
+ }
+ rarMakeDecodeTables(BitLength, 0, BD, rBC20);
+ I = 0;
+ while (I < TableSize) {
+ var num = rarDecodeNumber(bstream, BD);
+ if (num < 16) {
+ Table[I] = num + UnpOldTable20[I] & 0xf;
+ I++;
+ } else if (num === 16) {
+ N = bstream.readBits(2) + 3;
+ while (N-- > 0 && I < TableSize) {
+ Table[I] = Table[I - 1];
+ I++;
+ }
+ } else {
+ if (num === 17) {
+ N = bstream.readBits(3) + 3;
+ } else {
+ N = bstream.readBits(7) + 11;
+ }
+ while (N-- > 0 && I < TableSize) {
+ Table[I++] = 0;
+ }
+ }
+ }
+ rarMakeDecodeTables(Table, 0, LD, rNC20);
+ rarMakeDecodeTables(Table, rNC20, DD, rDC20);
+ rarMakeDecodeTables(Table, rNC20 + rDC20, RD, rRC20);
+ for (i = UnpOldTable20.length; i--;) {
+ UnpOldTable20[i] = Table[i];
+ }
+}
+
+// ============================================================================================== //
+
+// Unpack code specific to RarVM
+var VM = new RarVM();
+
+/**
+ * Filters code, one entry per filter.
+ * @type {Array}
+ */
+var Filters = [];
+
+/**
+ * Filters stack, several entrances of same filter are possible.
+ * @type {Array}
+ */
+var PrgStack = [];
+
+/**
+ * Lengths of preceding blocks, one length per filter. Used to reduce
+ * size required to write block length if lengths are repeating.
+ * @type {Array}
+ */
+var OldFilterLengths = [];
+
+var LastFilter = 0;
+
+function initFilters() {
+ OldFilterLengths = [];
+ LastFilter = 0;
+ Filters = [];
+ PrgStack = [];
+}
+
+
+/**
+ * @param {number} firstByte The first byte (flags).
+ * @param {Uint8Array} vmCode An array of bytes.
+ */
+function rarAddVMCode(firstByte, vmCode) {
+ VM.init();
+ var i;
+ var bstream = new bitjs.io.BitStream(vmCode.buffer, true /* rtl */ );
+
+ var filtPos;
+ if (firstByte & 0x80) {
+ filtPos = RarVM.readData(bstream);
+ if (filtPos === 0) {
+ initFilters();
+ } else {
+ filtPos--;
+ }
+ } else {
+ filtPos = LastFilter;
+ }
+
+ if (filtPos > Filters.length || filtPos > OldFilterLengths.length) {
+ return false;
+ }
+
+ LastFilter = filtPos;
+ var newFilter = (filtPos === Filters.length);
+
+ // new filter for PrgStack
+ var stackFilter = new UnpackFilter();
+ var filter = null;
+ // new filter code, never used before since VM reset
+ if (newFilter) {
+ // too many different filters, corrupt archive
+ if (filtPos > 1024) {
+ return false;
+ }
+
+ filter = new UnpackFilter();
+ Filters.push(filter);
+ stackFilter.ParentFilter = (Filters.length - 1);
+ OldFilterLengths.push(0); // OldFilterLengths.Add(1)
+ filter.ExecCount = 0;
+ } else { // filter was used in the past
+ filter = Filters[filtPos];
+ stackFilter.ParentFilter = filtPos;
+ filter.ExecCount++;
+ }
+
+ var emptyCount = 0;
+ for (i = 0; i < PrgStack.length; ++i) {
+ PrgStack[i - emptyCount] = PrgStack[i];
+
+ if (PrgStack[i] === null) {
+ emptyCount++;
+ }
+ if (emptyCount > 0) {
+ PrgStack[i] = null;
+ }
+ }
+
+ if (emptyCount === 0) {
+ PrgStack.push(null); //PrgStack.Add(1);
+ emptyCount = 1;
+ }
+
+ var stackPos = PrgStack.length - emptyCount;
+ PrgStack[stackPos] = stackFilter;
+ stackFilter.ExecCount = filter.ExecCount;
+
+ var blockStart = RarVM.readData(bstream);
+ if (firstByte & 0x40) {
+ blockStart += 258;
+ }
+ stackFilter.BlockStart = (blockStart + rBuffer.ptr) & MAXWINMASK;
+
+ if (firstByte & 0x20) {
+ stackFilter.BlockLength = RarVM.readData(bstream);
+ } else {
+ stackFilter.BlockLength = filtPos < OldFilterLengths.length ?
+ OldFilterLengths[filtPos] :
+ 0;
+ }
+ stackFilter.NextWindow = (wBuffer.ptr !== rBuffer.ptr) &&
+ (((wBuffer.ptr - rBuffer.ptr) & MAXWINMASK) <= blockStart);
+
+ OldFilterLengths[filtPos] = stackFilter.BlockLength;
+
+ for (i = 0; i < 7; ++i) {
+ stackFilter.Prg.InitR[i] = 0;
+ }
+ stackFilter.Prg.InitR[3] = VM_GLOBALMEMADDR;
+ stackFilter.Prg.InitR[4] = stackFilter.BlockLength;
+ stackFilter.Prg.InitR[5] = stackFilter.ExecCount;
+
+ // set registers to optional parameters if any
+ if (firstByte & 0x10) {
+ var initMask = bstream.readBits(7);
+ for (i = 0; i < 7; ++i) {
+ if (initMask & (1 << i)) {
+ stackFilter.Prg.InitR[i] = RarVM.readData(bstream);
+ }
+ }
+ }
+
+ if (newFilter) {
+ var vmCodeSize = RarVM.readData(bstream);
+ if (vmCodeSize >= 0x10000 || vmCodeSize === 0) {
+ return false;
+ }
+ vmCode = new Uint8Array(vmCodeSize);
+ for (i = 0; i < vmCodeSize; ++i) {
+ //if (Inp.Overflow(3))
+ // return(false);
+ vmCode[i] = bstream.readBits(8);
+ }
+ VM.prepare(vmCode, filter.Prg);
+ }
+ stackFilter.Prg.Cmd = filter.Prg.Cmd;
+ stackFilter.Prg.AltCmd = filter.Prg.Cmd;
+
+ var staticDataSize = filter.Prg.StaticData.length;
+ if (staticDataSize > 0 && staticDataSize < VM_GLOBALMEMSIZE) {
+ // read statically defined data contained in DB commands
+ for (i = 0; i < staticDataSize; ++i) {
+ stackFilter.Prg.StaticData[i] = filter.Prg.StaticData[i];
+ }
+ }
+
+ if (stackFilter.Prg.GlobalData.length < VM_FIXEDGLOBALSIZE) {
+ stackFilter.Prg.GlobalData = new Uint8Array(VM_FIXEDGLOBALSIZE);
+ }
+
+ var globalData = stackFilter.Prg.GlobalData;
+ for (i = 0; i < 7; ++i) {
+ VM.setLowEndianValue(globalData, stackFilter.Prg.InitR[i], i * 4);
+ }
+
+ VM.setLowEndianValue(globalData, stackFilter.BlockLength, 0x1c);
+ VM.setLowEndianValue(globalData, 0, 0x20);
+ VM.setLowEndianValue(globalData, stackFilter.ExecCount, 0x2c);
+ for (i = 0; i < 16; ++i) {
+ globalData[0x30 + i] = 0;
+ }
+
+ // put data block passed as parameter if any
+ if (firstByte & 8) {
+ //if (Inp.Overflow(3))
+ // return(false);
+ var dataSize = RarVM.readData(bstream);
+ if (dataSize > (VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE)) {
+ return (false);
+ }
+
+ var curSize = stackFilter.Prg.GlobalData.length;
+ if (curSize < dataSize + VM_FIXEDGLOBALSIZE) {
+ // Resize global data and update the stackFilter and local variable.
+ var numBytesToAdd = dataSize + VM_FIXEDGLOBALSIZE - curSize;
+ var newGlobalData = new Uint8Array(globalData.length + numBytesToAdd);
+ newGlobalData.set(globalData);
+
+ stackFilter.Prg.GlobalData = newGlobalData;
+ globalData = newGlobalData;
+ }
+ //byte *GlobalData=&StackFilter->Prg.GlobalData[VM_FIXEDGLOBALSIZE];
+ for (i = 0; i < dataSize; ++i) {
+ //if (Inp.Overflow(3))
+ // return(false);
+ globalData[VM_FIXEDGLOBALSIZE + i] = bstream.readBits(8);
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * @param {!bitjs.io.BitStream} bstream
+ */
+function rarReadVMCode(bstream) {
+ var firstByte = bstream.readBits(8);
+ var length = (firstByte & 7) + 1;
+ if (length === 7) {
+ length = bstream.readBits(8) + 7;
+ } else if (length === 8) {
+ length = bstream.readBits(16);
+ }
+
+ // Read all bytes of VM code into an array.
+ var vmCode = new Uint8Array(length);
+ for (var i = 0; i < length; i++) {
+ // Do something here with checking readbuf.
+ vmCode[i] = bstream.readBits(8);
+ }
+ return rarAddVMCode(firstByte, vmCode);
+}
+
+/**
+ * Unpacks the bit stream into rBuffer using the Unpack29 algorithm.
+ * @param {bitjs.io.BitStream} bstream
+ * @param {boolean} Solid
+ */
+function unpack29(bstream) {
+ // lazy initialize rDDecode and rDBits
+
+ var DDecode = new Array(rDC);
+ var DBits = new Array(rDC);
+ var Distance = 0;
+ var Length = 0;
+ var Dist = 0, BitLength = 0, Slot = 0;
+ var I;
+ for (I = 0; I < rDBitLengthCounts.length; I++, BitLength++) {
+ for (var J = 0; J < rDBitLengthCounts[I]; J++, Slot++, Dist += (1 << BitLength)) {
+ DDecode[Slot] = Dist;
+ DBits[Slot] = BitLength;
+ }
+ }
+
+ var Bits;
+ //tablesRead = false;
+
+ rOldDist = [0, 0, 0, 0];
+
+ lastDist = 0;
+ lastLength = 0;
+ var i;
+ for (i = UnpOldTable.length; i--;) {
+ UnpOldTable[i] = 0;
+ }
+
+ // read in Huffman tables
+ rarReadTables(bstream);
+
+ while (true) {
+ var num = rarDecodeNumber(bstream, LD);
+
+ if (num < 256) {
+ rBuffer.insertByte(num);
+ continue;
+ }
+ if (num >= 271) {
+ Length = rLDecode[num -= 271] + 3;
+ if ((Bits = rLBits[num]) > 0) {
+ Length += bstream.readBits(Bits);
+ }
+ var DistNumber = rarDecodeNumber(bstream, DD);
+ Distance = DDecode[DistNumber] + 1;
+ if ((Bits = DBits[DistNumber]) > 0) {
+ if (DistNumber > 9) {
+ if (Bits > 4) {
+ Distance += ((bstream.getBits() >>> (20 - Bits)) << 4);
+ bstream.readBits(Bits - 4);
+ //todo: check this
+ }
+ if (lowDistRepCount > 0) {
+ lowDistRepCount--;
+ Distance += prevLowDist;
+ } else {
+ var LowDist = rarDecodeNumber(bstream, LDD);
+ if (LowDist === 16) {
+ lowDistRepCount = rLowDistRepCount - 1;
+ Distance += prevLowDist;
+ } else {
+ Distance += LowDist;
+ prevLowDist = LowDist;
+ }
+ }
+ } else {
+ Distance += bstream.readBits(Bits);
+ }
+ }
+ if (Distance >= 0x2000) {
+ Length++;
+ if (Distance >= 0x40000) {
+ Length++;
+ }
+ }
+ rarInsertOldDist(Distance);
+ rarInsertLastMatch(Length, Distance);
+ rarCopyString(Length, Distance);
+ continue;
+ }
+ if (num === 256) {
+ if (!rarReadEndOfBlock(bstream)) {
+ break;
+ }
+ continue;
+ }
+ if (num === 257) {
+ if (!rarReadVMCode(bstream)) {
+ break;
+ }
+ continue;
+ }
+ if (num === 258) {
+ if (lastLength !== 0) {
+ rarCopyString(lastLength, lastDist);
+ }
+ continue;
+ }
+ if (num < 263) {
+ var DistNum = num - 259;
+ Distance = rOldDist[DistNum];
+
+ for (var I2 = DistNum; I2 > 0; I2--) {
+ rOldDist[I2] = rOldDist[I2 - 1];
+ }
+ rOldDist[0] = Distance;
+
+ var LengthNumber = rarDecodeNumber(bstream, RD);
+ Length = rLDecode[LengthNumber] + 2;
+ if ((Bits = rLBits[LengthNumber]) > 0) {
+ Length += bstream.readBits(Bits);
+ }
+ rarInsertLastMatch(Length, Distance);
+ rarCopyString(Length, Distance);
+ continue;
+ }
+ if (num < 272) {
+ Distance = rSDDecode[num -= 263] + 1;
+ if ((Bits = rSDBits[num]) > 0) {
+ Distance += bstream.readBits(Bits);
+ }
+ rarInsertOldDist(Distance);
+ rarInsertLastMatch(2, Distance);
+ rarCopyString(2, Distance);
+ continue;
+ }
+ } // while (true)
+ rarUpdateProgress();
+ rarWriteBuf();
+}
+
+/**
+ * Does stuff to the current byte buffer (rBuffer) based on
+ * the filters loaded into the RarVM and writes out to wBuffer.
+ */
+function rarWriteBuf() {
+ var writeSize = (rBuffer.ptr & MAXWINMASK);
+ var j;
+ var flt;
+ for (var i = 0; i < PrgStack.length; ++i) {
+ flt = PrgStack[i];
+ if (flt === null) {
+ continue;
+ }
+
+ if (flt.NextWindow) {
+ flt.NextWindow = false;
+ continue;
+ }
+
+ var blockStart = flt.BlockStart;
+ var blockLength = flt.BlockLength;
+ var parentPrg;
+
+ // WrittenBorder = wBuffer.ptr
+ if (((blockStart - wBuffer.ptr) & MAXWINMASK) < writeSize) {
+ if (wBuffer.ptr !== blockStart) {
+ // Copy blockStart bytes from rBuffer into wBuffer.
+ rarWriteArea(wBuffer.ptr, blockStart);
+ writeSize = (rBuffer.ptr - wBuffer.ptr) & MAXWINMASK;
+ }
+ if (blockLength <= writeSize) {
+ var blockEnd = (blockStart + blockLength) & MAXWINMASK;
+ if (blockStart < blockEnd || blockEnd === 0) {
+ VM.setMemory(0, rBuffer.data.subarray(blockStart, blockStart + blockLength), blockLength);
+ } else {
+ var firstPartLength = MAXWINSIZE - blockStart;
+ VM.setMemory(0, rBuffer.data.subarray(blockStart, blockStart + firstPartLength), firstPartLength);
+ VM.setMemory(firstPartLength, rBuffer.data, blockEnd);
+ }
+
+ parentPrg = Filters[flt.ParentFilter].Prg;
+ var prg = flt.Prg;
+
+ if (parentPrg.GlobalData.length > VM_FIXEDGLOBALSIZE) {
+ // Copy global data from previous script execution if any.
+ prg.GlobalData = new Uint8Array(parentPrg.GlobalData);
+ }
+
+ rarExecuteCode(prg);
+ var globalDataLen;
+
+ if (prg.GlobalData.length > VM_FIXEDGLOBALSIZE) {
+ // Save global data for next script execution.
+ globalDataLen = prg.GlobalData.length;
+ if (parentPrg.GlobalData.length < globalDataLen) {
+ parentPrg.GlobalData = new Uint8Array(globalDataLen);
+ }
+ parentPrg.GlobalData.set(
+ this.mem_.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen),
+ VM_FIXEDGLOBALSIZE);
+ } else {
+ parentPrg.GlobalData = new Uint8Array(0);
+ }
+
+ var filteredData = prg.FilteredData;
+
+ PrgStack[i] = null;
+ while (i + 1 < PrgStack.length) {
+ var nextFilter = PrgStack[i + 1];
+ if (nextFilter === null || nextFilter.BlockStart !== blockStart ||
+ nextFilter.BlockLength !== filteredData.length || nextFilter.NextWindow) {
+ break;
+ }
+
+ // Apply several filters to same data block.
+
+ VM.setMemory(0, filteredData, filteredData.length);
+
+ parentPrg = Filters[nextFilter.ParentFilter].Prg;
+ var nextPrg = nextFilter.Prg;
+
+ globalDataLen = parentPrg.GlobalData.length;
+ if (globalDataLen > VM_FIXEDGLOBALSIZE) {
+ // Copy global data from previous script execution if any.
+ nextPrg.GlobalData = new Uint8Array(globalDataLen);
+ nextPrg.GlobalData.set(parentPrg.GlobalData.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen), VM_FIXEDGLOBALSIZE);
+ }
+
+ rarExecuteCode(nextPrg);
+
+ if (nextPrg.GlobalData.length > VM_GLOBALMEMSIZE) {
+ // Save global data for next script execution.
+ globalDataLen = nextPrg.GlobalData.length;
+ if (parentPrg.GlobalData.length < globalDataLen) {
+ parentPrg.GlobalData = new Uint8Array(globalDataLen);
+ }
+ parentPrg.GlobalData.set(
+ this.mem_.subarray(VM_FIXEDGLOBALSIZE, VM_FIXEDGLOBALSIZE + globalDataLen),
+ VM_FIXEDGLOBALSIZE);
+ } else {
+ parentPrg.GlobalData = new Uint8Array(0);
+ }
+
+ filteredData = nextPrg.FilteredData;
+ i++;
+ PrgStack[i] = null;
+ } // while (i + 1 < PrgStack.length)
+
+ for (j = 0; j < filteredData.length; ++j) {
+ wBuffer.insertByte(filteredData[j]);
+ }
+ writeSize = (rBuffer.ptr - wBuffer.ptr) & MAXWINMASK;
+ } else { // if (blockLength <= writeSize)
+ for (j = i; j < PrgStack.length; ++j) {
+ flt = PrgStack[j];
+ if (flt !== null && flt.NextWindow) {
+ flt.NextWindow = false;
+ }
+ }
+ //WrPtr=WrittenBorder;
+ return;
+ }
+ } // if (((blockStart - wBuffer.ptr) & MAXWINMASK) < writeSize)
+ } // for (var i = 0; i < PrgStack.length; ++i)
+
+ // Write any remaining bytes from rBuffer to wBuffer;
+ rarWriteArea(wBuffer.ptr, rBuffer.ptr);
+
+ // Now that the filtered buffer has been written, swap it back to rBuffer.
+ rBuffer = wBuffer;
+}
+
+/**
+ * Copy bytes from rBuffer to wBuffer.
+ * @param {number} startPtr The starting point to copy from rBuffer.
+ * @param {number} endPtr The ending point to copy from rBuffer.
+ */
+function rarWriteArea(startPtr, endPtr) {
+ if (endPtr < startPtr) {
+ console.error("endPtr < startPtr, endPtr=" + endPtr + ", startPtr=" + startPtr);
+ // rarWriteData(startPtr, -(int)StartPtr & MAXWINMASK);
+ // RarWriteData(0, endPtr);
+ return;
+ } else if (startPtr < endPtr) {
+ rarWriteData(startPtr, endPtr - startPtr);
+ }
+}
+
+/**
+ * Writes bytes into wBuffer from rBuffer.
+ * @param {number} offset The starting point to copy bytes from rBuffer.
+ * @param {number} numBytes The number of bytes to copy.
+ */
+function rarWriteData(offset, numBytes) {
+ if (wBuffer.ptr >= rBuffer.data.length) {
+ return;
+ }
+ var leftToWrite = rBuffer.data.length - wBuffer.ptr;
+ if (numBytes > leftToWrite) {
+ numBytes = leftToWrite;
+ }
+ for (var i = 0; i < numBytes; ++i) {
+ wBuffer.insertByte(rBuffer.data[offset + i]);
+ }
+}
+
+/**
+ * @param {VM_PreparedProgram} prg
+ */
+function rarExecuteCode(prg) {
+ if (prg.GlobalData.length > 0) {
+ var writtenFileSize = wBuffer.ptr;
+ prg.InitR[6] = writtenFileSize;
+ VM.setLowEndianValue(prg.GlobalData, writtenFileSize, 0x24);
+ VM.setLowEndianValue(prg.GlobalData, (writtenFileSize >>> 32) >> 0, 0x28);
+ VM.execute(prg);
+ }
+}
+
+function rarReadEndOfBlock(bstream) {
+ rarUpdateProgress();
+
+ var NewTable = false,
+ NewFile = false;
+ if (bstream.readBits(1)) {
+ NewTable = true;
+ } else {
+ NewFile = true;
+ NewTable = !!bstream.readBits(1);
+ }
+ //tablesRead = !NewTable;
+ return !(NewFile || (NewTable && !rarReadTables(bstream)));
+}
+
+function rarInsertLastMatch(length, distance) {
+ lastDist = distance;
+ lastLength = length;
+}
+
+function rarInsertOldDist(distance) {
+ rOldDist.splice(3, 1);
+ rOldDist.splice(0, 0, distance);
+}
+
+/**
+ * Copies len bytes from distance bytes ago in the buffer to the end of the
+ * current byte buffer.
+ * @param {number} length How many bytes to copy.
+ * @param {number} distance How far back in the buffer from the current write
+ * pointer to start copying from.
+ */
+function rarCopyString(length, distance) {
+ var srcPtr = rBuffer.ptr - distance;
+ if (srcPtr < 0) {
+ var l = rOldBuffers.length;
+ while (srcPtr < 0) {
+ srcPtr = rOldBuffers[--l].data.length + srcPtr;
+ }
+ // TODO: lets hope that it never needs to read beyond file boundaries
+ while (length--) {
+ rBuffer.insertByte(rOldBuffers[l].data[srcPtr++]);
+ }
+ }
+ if (length > distance) {
+ while (length--) {
+ rBuffer.insertByte(rBuffer.data[srcPtr++]);
+ }
+ } else {
+ rBuffer.insertBytes(rBuffer.data.subarray(srcPtr, srcPtr + length));
+ }
+}
+
+/**
+ * @param {RarLocalFile} v
+ */
+function unpack(v) {
+ // TODO: implement what happens when unpVer is < 15
+ var Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer;
+ // var Solid = v.header.LHD_SOLID;
+ var bstream = new bitjs.io.BitStream(v.fileData.buffer, true /* rtl */, v.fileData.byteOffset, v.fileData.byteLength);
+
+ rBuffer = new bitjs.io.ByteBuffer(v.header.unpackedSize);
+
+ info("Unpacking " + v.filename + " RAR v" + Ver);
+
+ switch (Ver) {
+ case 15: // rar 1.5 compression
+ unpack15(); //(bstream, Solid);
+ break;
+ case 20: // rar 2.x compression
+ case 26: // files larger than 2GB
+ unpack20(bstream); //, Solid);
+ break;
+ case 29: // rar 3.x compression
+ case 36: // alternative hash
+ wBuffer = new bitjs.io.ByteBuffer(rBuffer.data.length);
+ unpack29(bstream);
+ break;
+ } // switch(method)
+
+ rOldBuffers.push(rBuffer);
+ // TODO: clear these old buffers when there's over 4MB of history
+ return rBuffer.data;
+}
+
+// bstream is a bit stream
+var RarLocalFile = function(bstream) {
+ this.header = new RarVolumeHeader(bstream);
+ this.filename = this.header.filename;
+
+ if (this.header.headType !== FILE_HEAD && this.header.headType !== ENDARC_HEAD && this.header.headType !== SERVICE_HEAD) {
+ this.isValid = false;
+ info("Error! RAR Volume did not include a FILE_HEAD header ");
+ } else {
+ // read in the compressed data
+ this.fileData = null;
+ if (this.header.packSize > 0) {
+ this.fileData = bstream.readBytes(this.header.packSize);
+ if (this.header.headType === FILE_HEAD) {
+ this.isValid = true;
+ }
+ }
+ }
+};
+
+RarLocalFile.prototype.unrar5 = function() {
+ //if (!this.header.flags.LHD_SPLIT_BEFORE) {
+ // unstore file
+ // No compression
+ if (this.header.method === 0x00) {
+ info("Unstore " + this.filename);
+ this.isValid = true;
+
+ currentBytesUnarchivedInFile += this.fileData.length;
+ currentBytesUnarchived += this.fileData.length;
+
+ // Create a new buffer and copy it over.
+ var len = this.header.packSize;
+ var newBuffer = new bitjs.io.ByteBuffer(len);
+ newBuffer.insertBytes(this.fileData);
+ this.fileData = newBuffer.data;
+ } else {
+ this.isValid = true;
+ this.fileData = unpack(this);
+ }
+ //}
+};
+
+var unrar5 = function(arrayBuffer) {
+ currentFilename = "";
+ currentFileNumber = 0;
+ currentBytesUnarchivedInFile = 0;
+ currentBytesUnarchived = 0;
+ totalUncompressedBytesInArchive = 0;
+ totalFilesInArchive = 0;
+
+ // postMessage(new bitjs.archive.UnarchiveStartEvent());
+ var bstream = new bitjs.io.BitStream(arrayBuffer, false /* rtl */);
+
+ var header = new RarMainVolumeHeader(bstream);
+ if (header.crc === 0x6152 &&
+ header.headType === 0x72 &&
+ header.flags.value === 0x1A21 &&
+ header.headSize === 7) {
+ info("Found RAR signature");
+
+ var mhead = new RarVolumeHeader(bstream);
+ if (mhead.headType !== MAIN_HEAD) {
+ info("Error! RAR did not include a MAIN_HEAD header");
+ } else {
+ var localFiles = [];
+ var localFile = null;
+ do {
+ try {
+ localFile = new RarLocalFile(bstream);
+ info("RAR localFile isValid=" + localFile.isValid + ", volume packSize=" + localFile.header.packSize);
+ if (localFile && localFile.isValid && localFile.header.packSize > 0) {
+ totalUncompressedBytesInArchive += localFile.header.unpackedSize;
+ localFiles.push(localFile);
+ } else if (localFile.header.packSize === 0 && localFile.header.unpackedSize === 0) {
+ localFile.isValid = true;
+ }
+ } catch (err) {
+ break;
+ }
+ //info("bstream" + bstream.bytePtr+"/"+bstream.bytes.length);
+ } while (localFile.isValid);
+ totalFilesInArchive = localFiles.length;
+
+ // now we have all information but things are unpacked
+ localFiles.sort(alphanumCase);
+
+ info(localFiles.map(function(a) {
+ return a.filename;
+ }).join(", "));
+ for (var i = 0; i < localFiles.length; ++i) {
+ var localfile = localFiles[i];
+
+ // update progress
+ currentFilename = localfile.header.filename;
+ currentBytesUnarchivedInFile = 0;
+
+ // actually do the unzipping
+ localfile.unrar5();
+
+ if (localfile.isValid) {
+ postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
+ postProgress();
+ }
+ }
+
+ postProgress();
+ }
+ } else {
+ err("Invalid RAR file");
+ }
+ // postMessage(new bitjs.archive.UnarchiveFinishEvent());
+};
+
+// event.data.file has the ArrayBuffer.
+onmessage = function(event) {
+ var ab = event.data.file;
+ unrar5(ab, true);
+};
diff --git a/cps/static/js/filter_list.js b/cps/static/js/filter_list.js
index 8291f0ac..b138d3b6 100644
--- a/cps/static/js/filter_list.js
+++ b/cps/static/js/filter_list.js
@@ -19,6 +19,17 @@ var direction = 0; // Descending order
var sort = 0; // Show sorted entries
$("#sort_name").click(function() {
+ var class_name = $("h1").attr('Class') + "_sort_name";
+ var obj = {};
+ obj[class_name] = sort;
+ $.ajax({
+ method:"post",
+ contentType: "application/json; charset=utf-8",
+ dataType: "json",
+ url: window.location.pathname + "/../../ajax/view",
+ data: JSON.stringify({obj}),
+ });
+
var count = 0;
var index = 0;
var store;
@@ -40,9 +51,7 @@ $("#sort_name").click(function() {
count++;
}
});
- /*listItems.sort(function(a,b){
- return $(a).children()[1].innerText.localeCompare($(b).children()[1].innerText)
- });*/
+
// Find count of middle element
if (count > 20) {
var middle = parseInt(count / 2, 10) + (count % 2);
diff --git a/cps/static/js/kthoom.js b/cps/static/js/kthoom.js
index 33a2ac0e..bbb3fead 100644
--- a/cps/static/js/kthoom.js
+++ b/cps/static/js/kthoom.js
@@ -162,10 +162,15 @@ function initProgressClick() {
function loadFromArrayBuffer(ab) {
var start = (new Date).getTime();
var h = new Uint8Array(ab, 0, 10);
+ unrar5(ab);
var pathToBitJS = "../../static/js/archive/";
var lastCompletion = 0;
- if (h[0] === 0x52 && h[1] === 0x61 && h[2] === 0x72 && h[3] === 0x21) { //Rar!
- unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS);
+ /*if (h[0] === 0x52 && h[1] === 0x61 && h[2] === 0x72 && h[3] === 0x21) { //Rar!
+ if (h[7] === 0x01) {
+ unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS);
+ } else {
+ unarchiver = new bitjs.archive.Unrarrer5(ab, pathToBitJS);
+ }
} else if (h[0] === 80 && h[1] === 75) { //PK (Zip)
unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS);
} else if (h[0] === 255 && h[1] === 216) { // JPEG
@@ -229,7 +234,7 @@ function loadFromArrayBuffer(ab) {
unarchiver.start();
} else {
alert("Some error");
- }
+ }*/
}
function scrollTocToActive() {
diff --git a/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js b/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js
index e93e8246..cd411037 100644
--- a/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js
+++ b/cps/static/js/libs/bootstrap-table/bootstrap-table-editable.min.js
@@ -1,7 +1,10 @@
-/*
-* bootstrap-table - v1.12.1 - 2018-03-12
-* https://github.com/wenzhixin/bootstrap-table
-* Copyright (c) 2018 zhixin wen
-* Licensed MIT License
-*/
-!function(a){"use strict";a.extend(a.fn.bootstrapTable.defaults,{editable:!0,onEditableInit:function(){return!1},onEditableSave:function(){return!1},onEditableShown:function(){return!1},onEditableHidden:function(){return!1}}),a.extend(a.fn.bootstrapTable.Constructor.EVENTS,{"editable-init.bs.table":"onEditableInit","editable-save.bs.table":"onEditableSave","editable-shown.bs.table":"onEditableShown","editable-hidden.bs.table":"onEditableHidden"});var b=a.fn.bootstrapTable.Constructor,c=b.prototype.initTable,d=b.prototype.initBody;b.prototype.initTable=function(){var b=this;c.apply(this,Array.prototype.slice.apply(arguments)),this.options.editable&&a.each(this.columns,function(c,d){if(d.editable){var e={},f=[],g="editable-",h=function(a,b){var c=a.replace(/([A-Z])/g,function(a){return"-"+a.toLowerCase()});if(c.slice(0,g.length)==g){var d=c.replace(g,"data-");e[d]=b}};a.each(b.options,h),d.formatter=d.formatter||function(a){return a},d._formatter=d._formatter?d._formatter:d.formatter,d.formatter=function(c,g,i){var j=d._formatter?d._formatter(c,g,i):c;a.each(d,h),a.each(e,function(a,b){f.push(" "+a+'="'+b+'"')});var k=!1;return d.editable.hasOwnProperty("noeditFormatter")&&(k=d.editable.noeditFormatter(c,g,i)),k===!1?['"].join(""):k}}})},b.prototype.initBody=function(){var b=this;d.apply(this,Array.prototype.slice.apply(arguments)),this.options.editable&&(a.each(this.columns,function(c,d){d.editable&&(b.$body.find('a[data-name="'+d.field+'"]').editable(d.editable).off("save").on("save",function(c,e){var f=b.getData(),g=a(this).parents("tr[data-index]").data("index"),h=f[g],i=h[d.field];a(this).data("value",e.submitValue),h[d.field]=e.submitValue,b.trigger("editable-save",d.field,h,i,a(this)),b.resetFooter()}),b.$body.find('a[data-name="'+d.field+'"]').editable(d.editable).off("shown").on("shown",function(c,e){var f=b.getData(),g=a(this).parents("tr[data-index]").data("index"),h=f[g];b.trigger("editable-shown",d.field,h,a(this),e)}),b.$body.find('a[data-name="'+d.field+'"]').editable(d.editable).off("hidden").on("hidden",function(c,e){var f=b.getData(),g=a(this).parents("tr[data-index]").data("index"),h=f[g];b.trigger("editable-hidden",d.field,h,a(this),e)}))}),this.trigger("editable-init"))}}(jQuery);
\ No newline at end of file
+/**
+ * bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
+ *
+ * @version v1.16.0
+ * @homepage https://bootstrap-table.com
+ * @author wenzhixin (http://wenzhixin.net.cn/)
+ * @license MIT
+ */
+
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],e):e((t=t||self).jQuery)}(this,(function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function n(t,e){return t(e={exports:{}},e.exports),e.exports}var r=function(t){return t&&t.Math==Math&&t},o=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof e&&e)||Function("return this")(),i=function(t){try{return!!t()}catch(t){return!0}},a=!i((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})),c={}.propertyIsEnumerable,u=Object.getOwnPropertyDescriptor,f={f:u&&!c.call({1:2},1)?function(t){var e=u(this,t);return!!e&&e.enumerable}:c},l=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},s={}.toString,d=function(t){return s.call(t).slice(8,-1)},p="".split,h=i((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==d(t)?p.call(t,""):Object(t)}:Object,v=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},y=function(t){return h(v(t))},g=function(t){return"object"==typeof t?null!==t:"function"==typeof t},b=function(t,e){if(!g(t))return t;var n,r;if(e&&"function"==typeof(n=t.toString)&&!g(r=n.call(t)))return r;if("function"==typeof(n=t.valueOf)&&!g(r=n.call(t)))return r;if(!e&&"function"==typeof(n=t.toString)&&!g(r=n.call(t)))return r;throw TypeError("Can't convert object to primitive value")},m={}.hasOwnProperty,x=function(t,e){return m.call(t,e)},O=o.document,w=g(O)&&g(O.createElement),E=function(t){return w?O.createElement(t):{}},j=!a&&!i((function(){return 7!=Object.defineProperty(E("div"),"a",{get:function(){return 7}}).a})),S=Object.getOwnPropertyDescriptor,T={f:a?S:function(t,e){if(t=y(t),e=b(e,!0),j)try{return S(t,e)}catch(t){}if(x(t,e))return l(!f.f.call(t,e),t[e])}},P=function(t){if(!g(t))throw TypeError(String(t)+" is not an object");return t},A=Object.defineProperty,_={f:a?A:function(t,e,n){if(P(t),e=b(e,!0),P(n),j)try{return A(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported");return"value"in n&&(t[e]=n.value),t}},I=a?function(t,e,n){return _.f(t,e,l(1,n))}:function(t,e,n){return t[e]=n,t},R=function(t,e){try{I(o,t,e)}catch(n){o[t]=e}return e},C=o["__core-js_shared__"]||R("__core-js_shared__",{}),k=Function.toString;"function"!=typeof C.inspectSource&&(C.inspectSource=function(t){return k.call(t)});var M,$,F,D=C.inspectSource,N=o.WeakMap,q="function"==typeof N&&/native code/.test(D(N)),B=n((function(t){(t.exports=function(t,e){return C[t]||(C[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.6.0",mode:"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})})),L=0,K=Math.random(),V=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++L+K).toString(36)},U=B("keys"),W=function(t){return U[t]||(U[t]=V(t))},z={},Y=o.WeakMap;if(q){var G=new Y,H=G.get,Q=G.has,X=G.set;M=function(t,e){return X.call(G,t,e),e},$=function(t){return H.call(G,t)||{}},F=function(t){return Q.call(G,t)}}else{var Z=W("state");z[Z]=!0,M=function(t,e){return I(t,Z,e),e},$=function(t){return x(t,Z)?t[Z]:{}},F=function(t){return x(t,Z)}}var J,tt,et={set:M,get:$,has:F,enforce:function(t){return F(t)?$(t):M(t,{})},getterFor:function(t){return function(e){var n;if(!g(e)||(n=$(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return n}}},nt=n((function(t){var e=et.get,n=et.enforce,r=String(String).split("String");(t.exports=function(t,e,i,a){var c=!!a&&!!a.unsafe,u=!!a&&!!a.enumerable,f=!!a&&!!a.noTargetGet;"function"==typeof i&&("string"!=typeof e||x(i,"name")||I(i,"name",e),n(i).source=r.join("string"==typeof e?e:"")),t!==o?(c?!f&&t[e]&&(u=!0):delete t[e],u?t[e]=i:I(t,e,i)):u?t[e]=i:R(e,i)})(Function.prototype,"toString",(function(){return"function"==typeof this&&e(this).source||D(this)}))})),rt=o,ot=function(t){return"function"==typeof t?t:void 0},it=function(t,e){return arguments.length<2?ot(rt[t])||ot(o[t]):rt[t]&&rt[t][e]||o[t]&&o[t][e]},at=Math.ceil,ct=Math.floor,ut=function(t){return isNaN(t=+t)?0:(t>0?ct:at)(t)},ft=Math.min,lt=function(t){return t>0?ft(ut(t),9007199254740991):0},st=Math.max,dt=Math.min,pt=function(t){return function(e,n,r){var o,i=y(e),a=lt(i.length),c=function(t,e){var n=ut(t);return n<0?st(n+e,0):dt(n,e)}(r,a);if(t&&n!=n){for(;a>c;)if((o=i[c++])!=o)return!0}else for(;a>c;c++)if((t||c in i)&&i[c]===n)return t||c||0;return!t&&-1}},ht={includes:pt(!0),indexOf:pt(!1)},vt=ht.indexOf,yt=function(t,e){var n,r=y(t),o=0,i=[];for(n in r)!x(z,n)&&x(r,n)&&i.push(n);for(;e.length>o;)x(r,n=e[o++])&&(~vt(i,n)||i.push(n));return i},gt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],bt=gt.concat("length","prototype"),mt={f:Object.getOwnPropertyNames||function(t){return yt(t,bt)}},xt={f:Object.getOwnPropertySymbols},Ot=it("Reflect","ownKeys")||function(t){var e=mt.f(P(t)),n=xt.f;return n?e.concat(n(t)):e},wt=function(t,e){for(var n=Ot(e),r=_.f,o=T.f,i=0;i=74)&&(J=Vt.match(/Chrome\/(\d+)/))&&(tt=J[1]);var Yt,Gt=tt&&+tt,Ht=Bt("species"),Qt=Bt("isConcatSpreadable"),Xt=Gt>=51||!i((function(){var t=[];return t[Qt]=!1,t.concat()[0]!==t})),Zt=(Yt="concat",Gt>=51||!i((function(){var t=[];return(t.constructor={})[Ht]=function(){return{foo:1}},1!==t[Yt](Boolean).foo}))),Jt=function(t){if(!g(t))return!1;var e=t[Qt];return void 0!==e?!!e:Ct(t)};Rt({target:"Array",proto:!0,forced:!Xt||!Zt},{concat:function(t){var e,n,r,o,i,a=kt(this),c=Kt(a,0),u=0;for(e=-1,r=arguments.length;e9007199254740991)throw TypeError("Maximum allowed index exceeded");for(n=0;n=9007199254740991)throw TypeError("Maximum allowed index exceeded");Mt(c,u++,i)}return c.length=u,c}});var te,ee=function(t,e,n){if(function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function")}(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}},ne=[].push,re=function(t){var e=1==t,n=2==t,r=3==t,o=4==t,i=6==t,a=5==t||i;return function(c,u,f,l){for(var s,d,p=kt(c),v=h(p),y=ee(u,f,3),g=lt(v.length),b=0,m=l||Kt,x=e?m(c,g):n?m(c,0):void 0;g>b;b++)if((a||b in v)&&(d=y(s=v[b],b,p),t))if(e)x[b]=d;else if(d)switch(t){case 3:return!0;case 5:return s;case 6:return b;case 2:ne.call(x,s)}else if(o)return!1;return i?-1:r||o?o:x}},oe={forEach:re(0),map:re(1),filter:re(2),some:re(3),every:re(4),find:re(5),findIndex:re(6)},ie=Object.keys||function(t){return yt(t,gt)},ae=a?Object.defineProperties:function(t,e){P(t);for(var n,r=ie(e),o=r.length,i=0;o>i;)_.f(t,n=r[i++],e[n]);return t},ce=it("document","documentElement"),ue=W("IE_PROTO"),fe=function(){},le=function(t){return"
+
+
+
+
+
{% endblock %}
diff --git a/cps/templates/detail.html b/cps/templates/detail.html
index 25dbb6fd..a0943122 100644
--- a/cps/templates/detail.html
+++ b/cps/templates/detail.html
@@ -92,7 +92,7 @@
{{entry.title|shortentitle(40)}}
{% for author in entry.authors %}
- {{author.name.replace('|',',')}}
+ {{author.name.replace('|',',')}}
{% if not loop.last %}
&
{% endif %}
@@ -114,7 +114,7 @@
{% endif %}
{% if entry.series|length > 0 %}
-
{{_('Book')}} {{entry.series_index}} {{_('of')}} {{entry.series[0].name}}
+ {{_('Book')}} {{entry.series_index}} {{_('of')}} {{entry.series[0].name}}
{% endif %}
{% if entry.languages.__len__() > 0 %}
@@ -143,7 +143,7 @@
{% for tag in entry.tags %}
- {{tag.name}}
+ {{tag.name}}
{%endfor%}
@@ -154,13 +154,13 @@
{% endif %}
- {% if entry.pubdate[:10] != '0101-01-01' %}
+ {% if (entry.pubdate|string)[:10] != '0101-01-01' %}
{{_('Published')}}: {{entry.pubdate|formatdate}}
@@ -281,7 +281,7 @@
{% if g.user.role_edit() %}
{% endif %}
diff --git a/cps/templates/discover.html b/cps/templates/discover.html
index 1326f9a9..7e343d79 100644
--- a/cps/templates/discover.html
+++ b/cps/templates/discover.html
@@ -22,7 +22,7 @@
{% if not loop.first %}
&
{% endif %}
- {{author.name.replace('|',',')|shortentitle(30)}}
+ {{author.name.replace('|',',')|shortentitle(30)}}
{% if loop.last %}
(...)
{% endif %}
@@ -30,7 +30,7 @@
{% if not loop.first %}
&
{% endif %}
- {{author.name.replace('|',',')|shortentitle(30)}}
+ {{author.name.replace('|',',')|shortentitle(30)}}
{% endif %}
{% endfor %}
diff --git a/cps/templates/grid.html b/cps/templates/grid.html
index 8fb084fd..16b1608f 100644
--- a/cps/templates/grid.html
+++ b/cps/templates/grid.html
@@ -27,13 +27,13 @@
{% for entry in entries %}
diff --git a/cps/templates/index.html b/cps/templates/index.html
index 3498791c..13105008 100644
--- a/cps/templates/index.html
+++ b/cps/templates/index.html
@@ -21,7 +21,7 @@
{% if not loop.first %}
&
{% endif %}
-
{{author.name.replace('|',',')|shortentitle(30)}}
+
{{author.name.replace('|',',')|shortentitle(30)}}
{% if loop.last %}
(...)
{% endif %}
@@ -29,7 +29,7 @@
{% if not loop.first %}
&
{% endif %}
-
{{author.name.replace('|',',')|shortentitle(30)}}
+
{{author.name.replace('|',',')|shortentitle(30)}}
{% endif %}
{% endfor %}
@@ -54,14 +54,14 @@
{{_(title)}}
@@ -84,7 +84,7 @@
{% if not loop.first %}
&
{% endif %}
-
{{author.name.replace('|',',')|shortentitle(30)}}
+
{{author.name.replace('|',',')|shortentitle(30)}}
{% if loop.last %}
(...)
{% endif %}
@@ -92,7 +92,7 @@
{% if not loop.first %}
&
{% endif %}
-
{{author.name.replace('|',',')|shortentitle(30)}}
+
{{author.name.replace('|',',')|shortentitle(30)}}
{% endif %}
{% endfor %}
{% for format in entry.data %}
diff --git a/cps/templates/languages.html b/cps/templates/languages.html
index 3c30e4d0..2b482e19 100644
--- a/cps/templates/languages.html
+++ b/cps/templates/languages.html
@@ -10,7 +10,7 @@
{% endif %}
{{lang_counter[loop.index0].bookcount}}
-
+
{% endfor %}
diff --git a/cps/templates/layout.html b/cps/templates/layout.html
index 66fffda4..55d6adbc 100644
--- a/cps/templates/layout.html
+++ b/cps/templates/layout.html
@@ -1,4 +1,4 @@
-{% from 'modal_restriction.html' import restrict_modal %}
+{% from 'modal_dialogs.html' import restrict_modal, delete_book %}
@@ -128,7 +128,7 @@
{{_('Browse')}}
{% for element in sidebar %}
{% if g.user.check_visibility(element['visibility']) and element['public'] %}
-
{{_(element['text'])}}
+
{{_(element['text'])}}
{% endif %}
{% endfor %}
{% if g.user.is_authenticated or g.allow_anonymous %}
@@ -136,10 +136,6 @@
{% for shelf in g.shelves_access %}
{{shelf.name|shortentitle(40)}}{% if shelf.is_public == 1 %} {{_('(Public)')}}{% endif %}
{% endfor %}
-
{% if not g.user.is_anonymous %}
{{_('Create a Shelf')}}
{{_('About')}}
@@ -155,29 +151,29 @@
{% if pagination and (pagination.has_next or pagination.has_prev) %}
{% endif %}
-
+
@@ -196,7 +192,6 @@
-
@@ -227,9 +222,11 @@
});
$(document).ready(function() {
var inp = $('#query').first()
- var val = inp.val()
- if (val !== "undefined") {
+ if (inp.length) {
+ var val = inp.val()
+ if (val.length) {
inp.val('').blur().focus().val(val)
+ }
}
});
});
diff --git a/cps/templates/list.html b/cps/templates/list.html
index a2938be1..13a818d8 100644
--- a/cps/templates/list.html
+++ b/cps/templates/list.html
@@ -32,7 +32,7 @@
{% endif %}