mirror of
https://github.com/hkalexling/Mango.git
synced 2025-08-03 11:25:29 -04:00
Handle library/title sorting on backend (#86)
This commit is contained in:
parent
360913ee78
commit
94a1e63963
@ -1,131 +1,15 @@
|
|||||||
$(() => {
|
$(() => {
|
||||||
const titleID = $('.data').attr('data-title-id') || 'library';
|
$('#sort-select').change(() => {
|
||||||
|
|
||||||
const sortItems = () => {
|
|
||||||
const sort = $('#sort-select').find(':selected').attr('id');
|
const sort = $('#sort-select').find(':selected').attr('id');
|
||||||
localStorage.setItem(`sort-${titleID}`, sort);
|
|
||||||
|
|
||||||
const ary = sort.split('-');
|
const ary = sort.split('-');
|
||||||
const by = ary[0];
|
const by = ary[0];
|
||||||
const dir = ary[1];
|
const dir = ary[1];
|
||||||
|
|
||||||
let items = $('.item');
|
const url = `${location.protocol}//${location.host}${location.pathname}`;
|
||||||
items.remove();
|
const newURL = `${url}?${$.param({
|
||||||
|
sort: by,
|
||||||
const ctxAry = [];
|
ascend: dir === 'up' ? 1 : 0
|
||||||
const keyRange = {};
|
})}`;
|
||||||
if (by === 'auto') {
|
window.location.href = newURL;
|
||||||
// intelligent sorting
|
|
||||||
items.each((i, item) => {
|
|
||||||
const name = $(item).find('.uk-card-title').text();
|
|
||||||
const regex = /([^0-9\n\r\ ]*)[ ]*([0-9]*\.*[0-9]+)/g;
|
|
||||||
|
|
||||||
const numbers = {};
|
|
||||||
let match = regex.exec(name);
|
|
||||||
while (match) {
|
|
||||||
const key = match[1];
|
|
||||||
const num = parseFloat(match[2]);
|
|
||||||
numbers[key] = num;
|
|
||||||
|
|
||||||
if (!keyRange[key]) {
|
|
||||||
keyRange[key] = [num, num, 1];
|
|
||||||
} else {
|
|
||||||
keyRange[key][2] += 1;
|
|
||||||
if (num < keyRange[key][0]) {
|
|
||||||
keyRange[key][0] = num;
|
|
||||||
} else if (num > keyRange[key][1]) {
|
|
||||||
keyRange[key][1] = num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match = regex.exec(name);
|
|
||||||
}
|
|
||||||
ctxAry.push({
|
|
||||||
index: i,
|
|
||||||
numbers: numbers
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(keyRange);
|
|
||||||
|
|
||||||
const sortedKeys = Object.keys(keyRange).filter(k => {
|
|
||||||
return keyRange[k][2] >= items.length / 2;
|
|
||||||
});
|
|
||||||
|
|
||||||
sortedKeys.sort((a, b) => {
|
|
||||||
// sort by frequency of the key first
|
|
||||||
if (keyRange[a][2] !== keyRange[b][2]) {
|
|
||||||
return (keyRange[a][2] < keyRange[b][2]) ? 1 : -1;
|
|
||||||
}
|
|
||||||
// then sort by range of the key
|
|
||||||
return ((keyRange[a][1] - keyRange[a][0]) < (keyRange[b][1] - keyRange[b][0])) ? 1 : -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(sortedKeys);
|
|
||||||
|
|
||||||
ctxAry.sort((a, b) => {
|
|
||||||
for (let i = 0; i < sortedKeys.length; i++) {
|
|
||||||
const key = sortedKeys[i];
|
|
||||||
|
|
||||||
if (a.numbers[key] === undefined && b.numbers[key] === undefined)
|
|
||||||
continue;
|
|
||||||
if (a.numbers[key] === undefined)
|
|
||||||
return 1;
|
|
||||||
if (b.numbers[key] === undefined)
|
|
||||||
return -1;
|
|
||||||
if (a.numbers[key] === b.numbers[key])
|
|
||||||
continue;
|
|
||||||
return (a.numbers[key] > b.numbers[key]) ? 1 : -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
const sortedItems = [];
|
|
||||||
ctxAry.forEach(ctx => {
|
|
||||||
sortedItems.push(items[ctx.index]);
|
|
||||||
});
|
|
||||||
items = sortedItems;
|
|
||||||
|
|
||||||
if (dir === 'down') {
|
|
||||||
items.reverse();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
items.sort((a, b) => {
|
|
||||||
var res;
|
|
||||||
if (by === 'name')
|
|
||||||
res = $(a).find('.uk-card-title').text() > $(b).find('.uk-card-title').text();
|
|
||||||
else if (by === 'date')
|
|
||||||
res = $(a).attr('data-mtime') > $(b).attr('data-mtime');
|
|
||||||
else if (by === 'progress') {
|
|
||||||
const ap = parseFloat($(a).attr('data-progress'));
|
|
||||||
const bp = parseFloat($(b).attr('data-progress'));
|
|
||||||
if (ap === bp)
|
|
||||||
// if progress is the same, we compare by name
|
|
||||||
res = $(a).find('.uk-card-title').text() > $(b).find('.uk-card-title').text();
|
|
||||||
else
|
|
||||||
res = ap > bp;
|
|
||||||
}
|
|
||||||
if (dir === 'up')
|
|
||||||
return res ? 1 : -1;
|
|
||||||
else
|
|
||||||
return !res ? 1 : -1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$('#item-container').append(items);
|
|
||||||
setupAcard();
|
|
||||||
};
|
|
||||||
|
|
||||||
$('#sort-select').change(() => {
|
|
||||||
sortItems();
|
|
||||||
});
|
|
||||||
|
|
||||||
const sortID = localStorage.getItem(`sort-${titleID}`);
|
|
||||||
if (sortID)
|
|
||||||
$(`option#${sortID}`).attr('selected', '');
|
|
||||||
else if ($('option#auto-up').length > 0)
|
|
||||||
$('option#auto-up').attr('selected', '');
|
|
||||||
else
|
|
||||||
$('option#name-up').attr('selected', '');
|
|
||||||
|
|
||||||
sortItems();
|
|
||||||
});
|
|
||||||
|
@ -38,6 +38,9 @@ end
|
|||||||
describe "chapter_sort" do
|
describe "chapter_sort" do
|
||||||
it "sorts correctly" do
|
it "sorts correctly" do
|
||||||
ary = ["Vol.1 Ch.01", "Vol.1 Ch.02", "Vol.2 Ch. 2.5", "Ch. 3", "Ch.04"]
|
ary = ["Vol.1 Ch.01", "Vol.1 Ch.02", "Vol.2 Ch. 2.5", "Ch. 3", "Ch.04"]
|
||||||
chapter_sort(ary.reverse).should eq ary
|
sorter = ChapterSorter.new ary
|
||||||
|
ary.reverse.sort do |a, b|
|
||||||
|
sorter.compare a, b
|
||||||
|
end.should eq ary
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
185
src/library.cr
185
src/library.cr
@ -6,6 +6,45 @@ require "./archive"
|
|||||||
|
|
||||||
SUPPORTED_IMG_TYPES = ["image/jpeg", "image/png", "image/webp"]
|
SUPPORTED_IMG_TYPES = ["image/jpeg", "image/png", "image/webp"]
|
||||||
|
|
||||||
|
enum SortMethod
|
||||||
|
Auto
|
||||||
|
Title
|
||||||
|
Progress
|
||||||
|
TimeModified
|
||||||
|
TimeAdded
|
||||||
|
end
|
||||||
|
|
||||||
|
class SortOptions
|
||||||
|
property method : SortMethod, ascend : Bool
|
||||||
|
|
||||||
|
def initialize(in_method : String? = nil, @ascend = true)
|
||||||
|
@method = SortMethod::Auto
|
||||||
|
SortMethod.each do |m, _|
|
||||||
|
if in_method && m.to_s.underscore == in_method
|
||||||
|
@method = m
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(in_method : SortMethod? = nil, @ascend = true)
|
||||||
|
if in_method
|
||||||
|
@method = in_method
|
||||||
|
else
|
||||||
|
@method = SortMethod::Auto
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_tuple(tp : Tuple(String, Bool))
|
||||||
|
method, ascend = tp
|
||||||
|
self.new method, ascend
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_tuple
|
||||||
|
{@method.to_s.underscore, ascend}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
struct Image
|
struct Image
|
||||||
property data : Bytes
|
property data : Bytes
|
||||||
property mime : String
|
property mime : String
|
||||||
@ -99,10 +138,11 @@ class Entry
|
|||||||
img
|
img
|
||||||
end
|
end
|
||||||
|
|
||||||
def next_entry
|
def next_entry(username)
|
||||||
idx = @book.entries.index self
|
entries = @book.sorted_entries username
|
||||||
return nil if idx.nil? || idx == @book.entries.size - 1
|
idx = entries.index self
|
||||||
@book.entries[idx + 1]
|
return nil if idx.nil? || idx == entries.size - 1
|
||||||
|
entries[idx + 1]
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_entry
|
def previous_entry
|
||||||
@ -239,8 +279,9 @@ class Title
|
|||||||
compare_numerically @library.title_hash[a].title,
|
compare_numerically @library.title_hash[a].title,
|
||||||
@library.title_hash[b].title
|
@library.title_hash[b].title
|
||||||
end
|
end
|
||||||
|
sorter = ChapterSorter.new @entries.map { |e| e.title }
|
||||||
@entries.sort! do |a, b|
|
@entries.sort! do |a, b|
|
||||||
compare_numerically a.title, b.title
|
sorter.compare a.title, b.title
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -405,28 +446,20 @@ class Title
|
|||||||
deep_read_page_count(username) / deep_total_page_count
|
deep_read_page_count(username) / deep_total_page_count
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_continue_reading_entry(username)
|
def load_progress_for_all_entries(username, opt : SortOptions? = nil,
|
||||||
in_progress_entries = @entries.select do |e|
|
unsorted = false)
|
||||||
load_progress(username, e.title) > 0
|
|
||||||
end
|
|
||||||
return nil if in_progress_entries.empty?
|
|
||||||
|
|
||||||
latest_read_entry = in_progress_entries[-1]
|
|
||||||
if load_progress(username, latest_read_entry.title) ==
|
|
||||||
latest_read_entry.pages
|
|
||||||
next_entry latest_read_entry
|
|
||||||
else
|
|
||||||
latest_read_entry
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_progress_for_all_entries(username)
|
|
||||||
progress = {} of String => Int32
|
progress = {} of String => Int32
|
||||||
TitleInfo.new @dir do |info|
|
TitleInfo.new @dir do |info|
|
||||||
progress = info.progress[username]?
|
progress = info.progress[username]?
|
||||||
end
|
end
|
||||||
|
|
||||||
@entries.map do |e|
|
if unsorted
|
||||||
|
ary = @entries
|
||||||
|
else
|
||||||
|
ary = sorted_entries username, opt
|
||||||
|
end
|
||||||
|
|
||||||
|
ary.map do |e|
|
||||||
info_progress = 0
|
info_progress = 0
|
||||||
if progress && progress.has_key? e.title
|
if progress && progress.has_key? e.title
|
||||||
info_progress = [progress[e.title], e.pages].min
|
info_progress = [progress[e.title], e.pages].min
|
||||||
@ -435,13 +468,71 @@ class Title
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_percentage_for_all_entries(username)
|
def load_percentage_for_all_entries(username, opt : SortOptions? = nil,
|
||||||
progress = load_progress_for_all_entries username
|
unsorted = false)
|
||||||
@entries.map_with_index do |e, i|
|
if unsorted
|
||||||
|
ary = @entries
|
||||||
|
else
|
||||||
|
ary = sorted_entries username, opt
|
||||||
|
end
|
||||||
|
|
||||||
|
progress = load_progress_for_all_entries username, opt, unsorted
|
||||||
|
ary.map_with_index do |e, i|
|
||||||
progress[i] / e.pages
|
progress[i] / e.pages
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns the sorted entries array
|
||||||
|
#
|
||||||
|
# When `opt` is nil, it uses the preferred sorting options in info.json, or
|
||||||
|
# use the default (auto, ascending)
|
||||||
|
# When `opt` is not nil, it saves the options to info.json
|
||||||
|
def sorted_entries(username, opt : SortOptions? = nil)
|
||||||
|
if opt.nil?
|
||||||
|
opt = load_sort_options username
|
||||||
|
else
|
||||||
|
TitleInfo.new @dir do |info|
|
||||||
|
info.sort_by[username] = opt.to_tuple
|
||||||
|
info.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
case opt.not_nil!.method
|
||||||
|
when .title?
|
||||||
|
ary = @entries.sort { |a, b| compare_numerically a.title, b.title }
|
||||||
|
when .time_modified?
|
||||||
|
ary = @entries.sort { |a, b| a.mtime <=> b.mtime }
|
||||||
|
when .time_added?
|
||||||
|
ary = @entries.sort { |a, b| a.date_added <=> b.date_added }
|
||||||
|
when .progress?
|
||||||
|
percentage_ary = load_percentage_for_all_entries username, opt, true
|
||||||
|
ary = @entries.zip(percentage_ary)
|
||||||
|
.sort { |a_tp, b_tp| a_tp[1] <=> b_tp[1] }
|
||||||
|
.map { |tp| tp[0] }
|
||||||
|
when .auto?
|
||||||
|
sorter = ChapterSorter.new @entries.map { |e| e.title }
|
||||||
|
ary = @entries.sort do |a, b|
|
||||||
|
sorter.compare a.title, b.title
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "Unknown sorting method #{opt.not_nil!.method}"
|
||||||
|
end
|
||||||
|
|
||||||
|
ary.reverse! unless opt.not_nil!.ascend
|
||||||
|
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_sort_options(username)
|
||||||
|
opt = SortOptions.new
|
||||||
|
TitleInfo.new @dir do |info|
|
||||||
|
if info.sort_by.has_key? username
|
||||||
|
opt = SortOptions.from_tuple info.sort_by[username]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
opt
|
||||||
|
end
|
||||||
|
|
||||||
# === helper methods ===
|
# === helper methods ===
|
||||||
|
|
||||||
# Gets the last read entry in the title. If the entry has been completed,
|
# Gets the last read entry in the title. If the entry has been completed,
|
||||||
@ -464,7 +555,7 @@ class Title
|
|||||||
end
|
end
|
||||||
|
|
||||||
if last_read_entry && last_read_entry.finished? username
|
if last_read_entry && last_read_entry.finished? username
|
||||||
last_read_entry = last_read_entry.next_entry
|
last_read_entry = last_read_entry.next_entry username
|
||||||
end
|
end
|
||||||
|
|
||||||
last_read_entry
|
last_read_entry
|
||||||
@ -511,6 +602,7 @@ class TitleInfo
|
|||||||
property entry_cover_url = {} of String => String
|
property entry_cover_url = {} of String => String
|
||||||
property last_read = {} of String => Hash(String, Time)
|
property last_read = {} of String => Hash(String, Time)
|
||||||
property date_added = {} of String => Time
|
property date_added = {} of String => Time
|
||||||
|
property sort_by = {} of String => Tuple(String, Bool)
|
||||||
|
|
||||||
@[JSON::Field(ignore: true)]
|
@[JSON::Field(ignore: true)]
|
||||||
property dir : String = ""
|
property dir : String = ""
|
||||||
@ -693,4 +785,45 @@ class Library
|
|||||||
|
|
||||||
recently_added[0..11]
|
recently_added[0..11]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sorted_titles(username, opt : SortOptions? = nil)
|
||||||
|
if opt.nil?
|
||||||
|
opt = load_sort_options username
|
||||||
|
else
|
||||||
|
TitleInfo.new @dir do |info|
|
||||||
|
info.sort_by[username] = opt.to_tuple
|
||||||
|
info.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is a hack to bypass a compiler bug
|
||||||
|
ary = titles
|
||||||
|
|
||||||
|
case opt.not_nil!.method
|
||||||
|
when .auto?
|
||||||
|
ary.sort! { |a, b| compare_numerically a.title, b.title }
|
||||||
|
when .time_modified?
|
||||||
|
ary.sort! { |a, b| a.mtime <=> b.mtime }
|
||||||
|
when .progress?
|
||||||
|
ary.sort! do |a, b|
|
||||||
|
a.load_percentage(username) <=> b.load_percentage(username)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "Unknown sorting method #{opt.not_nil!.method}"
|
||||||
|
end
|
||||||
|
|
||||||
|
ary.reverse! unless opt.not_nil!.ascend
|
||||||
|
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_sort_options(username)
|
||||||
|
opt = SortOptions.new
|
||||||
|
TitleInfo.new @dir do |info|
|
||||||
|
if info.sort_by.has_key? username
|
||||||
|
opt = SortOptions.from_tuple info.sort_by[username]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
opt
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -39,9 +39,14 @@ class MainRouter < Router
|
|||||||
|
|
||||||
get "/library" do |env|
|
get "/library" do |env|
|
||||||
begin
|
begin
|
||||||
titles = @context.library.titles
|
|
||||||
username = get_username env
|
username = get_username env
|
||||||
|
|
||||||
|
sort_opt = @context.library.load_sort_options username
|
||||||
|
get_sort_opt
|
||||||
|
|
||||||
|
titles = @context.library.sorted_titles username, sort_opt
|
||||||
percentage = titles.map &.load_percentage username
|
percentage = titles.map &.load_percentage username
|
||||||
|
|
||||||
layout "library"
|
layout "library"
|
||||||
rescue e
|
rescue e
|
||||||
@context.error e
|
@context.error e
|
||||||
@ -53,12 +58,18 @@ class MainRouter < Router
|
|||||||
begin
|
begin
|
||||||
title = (@context.library.get_title env.params.url["title"]).not_nil!
|
title = (@context.library.get_title env.params.url["title"]).not_nil!
|
||||||
username = get_username env
|
username = get_username env
|
||||||
percentage = title.load_percentage_for_all_entries username
|
|
||||||
|
sort_opt = title.load_sort_options username
|
||||||
|
get_sort_opt
|
||||||
|
|
||||||
|
entries = title.sorted_entries username, sort_opt
|
||||||
|
|
||||||
|
percentage = title.load_percentage_for_all_entries username, sort_opt
|
||||||
title_percentage = title.titles.map &.load_percentage username
|
title_percentage = title.titles.map &.load_percentage username
|
||||||
layout "title"
|
layout "title"
|
||||||
rescue e
|
rescue e
|
||||||
@context.error e
|
@context.error e
|
||||||
env.response.status_code = 404
|
env.response.status_code = 500
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class ReaderRouter < Router
|
|||||||
next_page = page + IMGS_PER_PAGE
|
next_page = page + IMGS_PER_PAGE
|
||||||
next_url = next_entry_url = nil
|
next_url = next_entry_url = nil
|
||||||
exit_url = "#{base_url}book/#{title.id}"
|
exit_url = "#{base_url}book/#{title.id}"
|
||||||
next_entry = entry.next_entry
|
next_entry = entry.next_entry username
|
||||||
unless next_page > entry.pages
|
unless next_page > entry.pages
|
||||||
next_url = "#{base_url}reader/#{title.id}/#{entry.id}/#{next_page}"
|
next_url = "#{base_url}reader/#{title.id}/#{entry.id}/#{next_page}"
|
||||||
end
|
end
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
require "big"
|
require "big"
|
||||||
|
|
||||||
private class Item
|
private class Item
|
||||||
getter index : Int32, numbers : Hash(String, BigDecimal)
|
getter numbers : Hash(String, BigDecimal)
|
||||||
|
|
||||||
def initialize(@index, @numbers)
|
def initialize(@numbers)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Compare with another Item using keys
|
# Compare with another Item using keys
|
||||||
@ -51,38 +51,27 @@ private class KeyRange
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def chapter_sort(in_ary : Array(String)) : Array(String)
|
class ChapterSorter
|
||||||
ary = in_ary.sort do |a, b|
|
@sorted_keys = [] of String
|
||||||
compare_numerically a, b
|
|
||||||
end
|
|
||||||
|
|
||||||
items = [] of Item
|
def initialize(str_ary : Array(String))
|
||||||
keys = {} of String => KeyRange
|
keys = {} of String => KeyRange
|
||||||
|
|
||||||
ary.each_with_index do |str, i|
|
str_ary.each do |str|
|
||||||
numbers = {} of String => BigDecimal
|
scan str do |k, v|
|
||||||
|
if keys.has_key? k
|
||||||
str.scan /([^0-9\n\r\ ]*)[ ]*([0-9]*\.*[0-9]+)/ do |match|
|
keys[k].update v
|
||||||
key = match[1]
|
|
||||||
num = match[2].to_big_d
|
|
||||||
|
|
||||||
numbers[key] = num
|
|
||||||
|
|
||||||
if keys.has_key? key
|
|
||||||
keys[key].update num
|
|
||||||
else
|
else
|
||||||
keys[key] = KeyRange.new num
|
keys[k] = KeyRange.new v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
items << Item.new(i, numbers)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the array of keys string and sort them
|
# Get the array of keys string and sort them
|
||||||
sorted_keys = keys.keys
|
@sorted_keys = keys.keys
|
||||||
# Only use keys that are present in over half of the strings
|
# Only use keys that are present in over half of the strings
|
||||||
.select do |key|
|
.select do |key|
|
||||||
keys[key].count >= ary.size / 2
|
keys[key].count >= str_ary.size / 2
|
||||||
end
|
end
|
||||||
.sort do |a_key, b_key|
|
.sort do |a_key, b_key|
|
||||||
a = keys[a_key]
|
a = keys[a_key]
|
||||||
@ -96,12 +85,28 @@ def chapter_sort(in_ary : Array(String)) : Array(String)
|
|||||||
count_compare
|
count_compare
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
items
|
def compare(a : String, b : String)
|
||||||
.sort do |a, b|
|
item_a = str_to_item a
|
||||||
a.<=>(b, sorted_keys)
|
item_b = str_to_item b
|
||||||
|
item_a.<=>(item_b, @sorted_keys)
|
||||||
end
|
end
|
||||||
.map do |item|
|
|
||||||
ary[item.index]
|
private def scan(str, &)
|
||||||
|
str.scan /([^0-9\n\r\ ]*)[ ]*([0-9]*\.*[0-9]+)/ do |match|
|
||||||
|
key = match[1]
|
||||||
|
num = match[2].to_big_d
|
||||||
|
|
||||||
|
yield key, num
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def str_to_item(str)
|
||||||
|
numbers = {} of String => BigDecimal
|
||||||
|
scan str do |k, v|
|
||||||
|
numbers[k] = v
|
||||||
|
end
|
||||||
|
Item.new numbers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -66,3 +66,18 @@ end
|
|||||||
macro render_component(filename)
|
macro render_component(filename)
|
||||||
render "src/views/components/#{{{filename}}}.html.ecr"
|
render "src/views/components/#{{{filename}}}.html.ecr"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
macro get_sort_opt
|
||||||
|
sort_method = env.params.query["sort"]?
|
||||||
|
|
||||||
|
if sort_method
|
||||||
|
is_ascending = true
|
||||||
|
|
||||||
|
ascend = env.params.query["ascend"]?
|
||||||
|
if ascend && ascend.to_i? == 0
|
||||||
|
is_ascending = false
|
||||||
|
end
|
||||||
|
|
||||||
|
sort_opt = SortOptions.new sort_method, is_ascending
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
<div class="uk-form-horizontal">
|
<div class="uk-form-horizontal">
|
||||||
<select class="uk-select" id="sort-select">
|
<select class="uk-select" id="sort-select">
|
||||||
<% hash.each do |k, v| %>
|
<% hash.each do |k, v| %>
|
||||||
<option id="<%= k %>-up">▲ <%= v %></option>
|
<option id="<%= k %>-up"
|
||||||
<option id="<%= k %>-down">▼ <%= v %></option>
|
<% if sort_opt && k == sort_opt.method.to_s.underscore && sort_opt.ascend %>
|
||||||
|
<%= "selected" %>
|
||||||
|
<% end %>>▲ <%= v %></option>
|
||||||
|
<option id="<%= k %>-down"
|
||||||
|
<% if sort_opt && k == sort_opt.method.to_s.underscore && !sort_opt.ascend %>
|
||||||
|
<%= "selected" %>
|
||||||
|
<% end %>>▼ <%= v %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="uk-margin-bottom uk-width-1-4@s">
|
<div class="uk-margin-bottom uk-width-1-4@s">
|
||||||
<% hash = {
|
<% hash = {
|
||||||
"name" => "Name",
|
"auto" => "Auto",
|
||||||
"date" => "Date Modified",
|
"time_modified" => "Date Modified",
|
||||||
"progress" => "Progress"
|
"progress" => "Progress"
|
||||||
} %>
|
} %>
|
||||||
<%= render_component "sort-form" %>
|
<%= render_component "sort-form" %>
|
||||||
|
@ -25,8 +25,9 @@
|
|||||||
<div class="uk-margin-bottom uk-width-1-4@s">
|
<div class="uk-margin-bottom uk-width-1-4@s">
|
||||||
<% hash = {
|
<% hash = {
|
||||||
"auto" => "Auto",
|
"auto" => "Auto",
|
||||||
"name" => "Name",
|
"title" => "Name",
|
||||||
"date" => "Date Modified",
|
"time_modified" => "Date Modified",
|
||||||
|
"time_added" => "Date Added",
|
||||||
"progress" => "Progress"
|
"progress" => "Progress"
|
||||||
} %>
|
} %>
|
||||||
<%= render_component "sort-form" %>
|
<%= render_component "sort-form" %>
|
||||||
@ -37,7 +38,7 @@
|
|||||||
<% progress = title_percentage[i] %>
|
<% progress = title_percentage[i] %>
|
||||||
<%= render_component "card" %>
|
<%= render_component "card" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% title.entries.each_with_index do |item, i| %>
|
<% entries.each_with_index do |item, i| %>
|
||||||
<% progress = percentage[i] %>
|
<% progress = percentage[i] %>
|
||||||
<%= render_component "card" %>
|
<%= render_component "card" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user