servel 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2763001e5065c946d727b67fdf97e42a888d6794159528c72b2f15c7492c3409
4
- data.tar.gz: d44b73a268aa7e69ea9b431ade3700a311b37906614fd8601effafc09473b2f4
3
+ metadata.gz: 2fa8d88495eaaf272edc23d6f8ee35358cdbf85e11339fc46f786cbead036ff9
4
+ data.tar.gz: dc9d48be2ce10ec35ebe1918f96985cef584bb2871ffaffebbb61ef2ea80a659
5
5
  SHA512:
6
- metadata.gz: ea0615e5c1661f40afd3ebf639e6d751db50989506fec1434c81f72ca3aaa3107c0eb5a8aa5f40e899e7d94ca62c99332e0998389c4a23296c910f8dd2c43750
7
- data.tar.gz: 0071e811b8c7c5a401b5a060c297a686616e311a9bd6726a4e4bd5d6ed6626ebf25cd193ea2202dbeaff9a9ca851bfc4f19de73ad819a5245e33c196cb0c3ede
6
+ metadata.gz: c06b97acc7830e90bda35051313097cf3c414f1f3f4b15e6c2ec1af4be0cc6e665ce9480ace68a20827ad8d140396f9b34630061daa929f289d0c4027f3d2997
7
+ data.tar.gz: 8d0d2d034cf124464ceb421c110e01750341b9e5aa2bd009963e0bb1ec6262fe8bd24edc97b7bcb4bafaf6b0466879f65caadade8feee8b7a9663f400cb01352
File without changes
File without changes
@@ -23,18 +23,3 @@ a {
23
23
  a:hover, a:focus, a:active {
24
24
  text-decoration: underline;
25
25
  }
26
-
27
- #loading {
28
- display: flex;
29
- align-items: center;
30
- justify-content: center;
31
- position: fixed;
32
- top: 0;
33
- right: 0;
34
- bottom: 0;
35
- left: 0;
36
- z-index: 2;
37
- background-color: rgba(255, 255, 255, 0.6);
38
- font-size: 24px;
39
- font-weight: bold;
40
- }
File without changes
File without changes
File without changes
@@ -1,5 +1,4 @@
1
1
  #listing {
2
- display: none;
3
2
  padding: 10px;
4
3
  position: relative;
5
4
  z-index: 1;
File without changes
File without changes
@@ -4,6 +4,12 @@
4
4
  %meta{charset: 'utf-8'}
5
5
  %title Listing of #{url_root}#{url_path}
6
6
  %meta{name: 'viewport', content: 'width=device-width, height=device-height, user-scalable=no'}
7
+
8
+ :javascript
9
+ window.specialEntries = #{special_entries};
10
+ window.directoryEntries = #{directory_entries};
11
+ window.fileEntries = #{file_entries};
12
+
7
13
  :css
8
14
  #{include('css/normalize.css')}
9
15
  #{include('css/common.css')}
@@ -12,11 +18,11 @@
12
18
  #{include('css/gallery-text.css')}
13
19
 
14
20
  :javascript
21
+ #{include('js/natural-orderby.min.js')}
22
+ #{include('js/entries.js')}
15
23
  #{include('js/listing.js')}
16
24
  #{include('js/ume.js')}
17
25
  #{include('js/gallery.js')}
18
- #{include('js/index.js')}
19
26
  %body
20
27
  #gallery!= partial('gallery')
21
- #listing!= partial('listing', { url_root: url_root, url_path: url_path })
22
- #loading Loading...
28
+ #listing!= partial('listing', { url_root: url_root, url_path: url_path })
data/app/js/entries.js ADDED
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+
3
+ var Entries = (function() {
4
+ //Lists
5
+ var all;
6
+ var media;
7
+ var hasMedia;
8
+
9
+ var sortMethod = "name";
10
+ var sortDirection = "asc";
11
+ var filterText = "";
12
+
13
+ function runFilter(entries) {
14
+ if(filterText == "") return entries;
15
+
16
+ var filteredEntries = [];
17
+
18
+ for(var i = 0; i < entries.length; i++) {
19
+ var entry = entries[i];
20
+ if(entry.name.toLowerCase().includes(filterText.toLowerCase())) filteredEntries.push(entry);
21
+ }
22
+
23
+ return filteredEntries;
24
+ }
25
+
26
+ function sortByName(entries) {
27
+ return naturalOrderBy.orderBy(entries, function(entry) {
28
+ return entry.name.toLowerCase();
29
+ }, sortDirection);
30
+ }
31
+
32
+ function sortByMtime(a, b) {
33
+ return b.mtime - a.mtime;
34
+ }
35
+
36
+ function sortBySize(a, b) {
37
+ return b.size - a.size;
38
+ }
39
+
40
+ function sortByType(a, b) {
41
+ var aType = a.type.toLowerCase();
42
+ var bType = b.type.toLowerCase();
43
+
44
+ if(aType < bType) return -1;
45
+ if(aType > bType) return 1;
46
+
47
+ return 0;
48
+ }
49
+
50
+ function runSort(entries) {
51
+ if(sortMethod == "name") return sortByName(entries);
52
+
53
+ var sortFunction;
54
+ if(sortMethod == "mtime") sortFunction = sortByMtime;
55
+ else if(sortMethod == "size") sortFunction = sortBySize;
56
+ else if(sortMethod == "type") sortFunction = sortByType;
57
+
58
+ entries.sort(sortFunction);
59
+ if(sortDirection == "desc") entries.reverse();
60
+
61
+ return entries;
62
+ }
63
+
64
+ function updateLists() {
65
+ specialEntries = window.specialEntries.slice();
66
+ directoryEntries = runSort(runFilter(window.directoryEntries.slice()));
67
+ fileEntries = runSort(runFilter(window.fileEntries.slice()));
68
+
69
+ all = [].concat(specialEntries, directoryEntries, fileEntries);
70
+
71
+ media = [];
72
+ for(var i = 0; i < fileEntries.length; i++) {
73
+ if(fileEntries[i].media) media.push(fileEntries[i]);
74
+ }
75
+
76
+ hasMedia = media.length > 0;
77
+ }
78
+
79
+ function update() {
80
+ updateLists();
81
+ Gallery.onEntriesUpdate();
82
+ Listing.onEntriesUpdate();
83
+ }
84
+
85
+ function sort(newSortMethod, newSortDirection) {
86
+ sortMethod = newSortMethod;
87
+ sortDirection = newSortDirection;
88
+ update();
89
+ }
90
+
91
+ function filter(newFilterText) {
92
+ filterText = newFilterText;
93
+ update();
94
+ }
95
+
96
+ updateLists();
97
+
98
+ return {
99
+ all: function() {
100
+ return all;
101
+ },
102
+ media: function() {
103
+ return media;
104
+ },
105
+ hasMedia: function() {
106
+ return hasMedia;
107
+ },
108
+ filter: filter,
109
+ sort: sort
110
+ };
111
+ })();
@@ -3,8 +3,7 @@
3
3
  var Gallery = (function() {
4
4
  var $;
5
5
  var $gallery;
6
- var mediaEntries = [];
7
- var currentIndex = 0;
6
+ var currentIndex;
8
7
  var layoutItemMax = false;
9
8
 
10
9
  function renderText(url) {
@@ -29,10 +28,10 @@ var Gallery = (function() {
29
28
  function render() {
30
29
  clearContent();
31
30
 
32
- var entry = mediaEntries[currentIndex];
31
+ var entry = Entries.media()[currentIndex];
33
32
 
34
33
  var url = entry.href;
35
- var type = entry.media_type;
34
+ var type = entry.mediaType;
36
35
 
37
36
  $gallery.classList.add(type);
38
37
 
@@ -52,7 +51,7 @@ var Gallery = (function() {
52
51
 
53
52
  function clamp(index) {
54
53
  if(index == null || isNaN(index) || index < 0) return 0;
55
- if(index >= mediaEntries.length) return mediaEntries.length - 1;
54
+ if(index >= Entries.media().length) return Entries.media().length - 1;
56
55
  return index;
57
56
  }
58
57
 
@@ -81,7 +80,7 @@ var Gallery = (function() {
81
80
  }
82
81
 
83
82
  function jump(url) {
84
- var index = mediaEntries.findIndex(function(entry) {
83
+ var index = Entries.media().findIndex(function(entry) {
85
84
  return entry.href == url;
86
85
  });
87
86
  go(index);
@@ -156,10 +155,18 @@ var Gallery = (function() {
156
155
  layout();
157
156
  }
158
157
 
159
- function onEntriesInit() {
158
+ function onEntriesUpdate() {
159
+ currentIndex = 0;
160
+ if(Entries.hasMedia()) render();
161
+ }
162
+
163
+ function init() {
164
+ $ = document.querySelector.bind(document);
165
+ $gallery = $("#gallery");
166
+
160
167
  onEntriesUpdate();
161
168
 
162
- if(mediaEntries.length > 0) {
169
+ if(Entries.hasMedia()) {
163
170
  $gallery.style.display = "flex";
164
171
 
165
172
  initEvents();
@@ -167,23 +174,8 @@ var Gallery = (function() {
167
174
  }
168
175
  }
169
176
 
170
- function onEntriesUpdate() {
171
- currentIndex = 0;
172
- mediaEntries = [];
173
- for(var i = 0; i < window.entries.length; i++) {
174
- if(window.entries[i].media) mediaEntries.push(window.entries[i]);
175
- }
176
-
177
- if(mediaEntries.length > 0) render();
178
- }
179
-
180
- window.addEventListener("DOMContentLoaded", function() {
181
- $ = document.querySelector.bind(document);
182
- $gallery = $("#gallery");
183
- });
184
-
185
177
  return {
186
- onEntriesInit: onEntriesInit,
178
+ init: init,
187
179
  onEntriesUpdate: onEntriesUpdate,
188
180
  go: go,
189
181
  prev: prev,
@@ -193,3 +185,5 @@ var Gallery = (function() {
193
185
  jump: jump
194
186
  };
195
187
  })();
188
+
189
+ window.addEventListener("DOMContentLoaded", Gallery.init);
@@ -3,10 +3,9 @@
3
3
  var Listing = (function() {
4
4
  var $;
5
5
  var $container;
6
- var filteredEntries = [];
7
6
  var perPage = 99;
8
- var currentIndex = 0;
9
- var moreContent = true;
7
+ var currentIndex;
8
+ var moreContent;
10
9
  var scrollDebounce = false;
11
10
 
12
11
  function escapeHTML(unsafe) {
@@ -31,14 +30,14 @@ var Listing = (function() {
31
30
  <tr>
32
31
  <td class="name">
33
32
  <span class="icon">${file.icon}</span>
34
- <a href="${file.href}" class="default ${file.class}" data-url="${file.href}" data-type="${file.media_type}">${file.name}</a>
33
+ <a href="${file.href}" class="default ${file.class}" data-url="${file.href}" data-type="${file.mediaType}">${file.name}</a>
35
34
  </td>
36
35
  <td class="new-tab">
37
36
  <a href="${file.href}" class="new-tab ${file.class}" target="_blank">(New tab)</a>
38
37
  </td>
39
38
  <td class="type">${file.type}</td>
40
- <td class="size">${file.size}</td>
41
- <td class="modified">${file.mtime}</td>
39
+ <td class="size">${file.sizeText}</td>
40
+ <td class="modified">${file.mtimeText}</td>
42
41
  </tr>
43
42
  `;
44
43
  }
@@ -58,7 +57,7 @@ var Listing = (function() {
58
57
  }
59
58
 
60
59
  function render() {
61
- var currentEntries = filteredEntries.slice(currentIndex, currentIndex + perPage);
60
+ var currentEntries = Entries.all().slice(currentIndex, currentIndex + perPage);
62
61
  $container.insertAdjacentHTML("beforeend", renderTable(currentEntries));
63
62
  }
64
63
 
@@ -69,7 +68,7 @@ var Listing = (function() {
69
68
  function onScrolled() {
70
69
  if(atBottom() && moreContent) {
71
70
  currentIndex += perPage;
72
- if(currentIndex >= filteredEntries.length) moreContent = false;
71
+ if(currentIndex >= Entries.all().length) moreContent = false;
73
72
  render();
74
73
  }
75
74
  scrollDebounce = false;
@@ -85,32 +84,8 @@ var Listing = (function() {
85
84
 
86
85
  sortable.classList.add("sort-active", "sort-" + sortable.dataset.sortDirection);
87
86
 
88
- Index.loadEntries(function() {
89
- sortable.scrollIntoView();
90
- });
91
- }
92
-
93
- function updateFilteredEntries(needle) {
94
- if(needle == "") {
95
- filteredEntries = window.entries;
96
- }
97
- else {
98
- filteredEntries = [];
99
-
100
- for(var i = 0; i < window.entries.length; i++) {
101
- var entry = window.entries[i];
102
- if(entry.name.toLowerCase().includes(needle.toLowerCase())) filteredEntries.push(entry);
103
- }
104
- }
105
- }
106
-
107
- function applyFilter(needle) {
108
- updateFilteredEntries(needle);
109
-
110
- $container.innerHTML = "";
111
- currentIndex = 0;
112
- moreContent = true;
113
- render();
87
+ Entries.sort(sortable.dataset.sortMethod, sortable.dataset.sortDirection);
88
+ sortable.scrollIntoView();
114
89
  }
115
90
 
116
91
  function initEvents() {
@@ -134,28 +109,31 @@ var Listing = (function() {
134
109
  e.stopPropagation();
135
110
 
136
111
  if(e.keyCode == 13) {
137
- applyFilter($("#search").value);
112
+ Entries.filter($("#search").value);
138
113
  }
139
114
  });
140
115
  }
141
116
 
142
- function onEntriesInit() {
143
- onEntriesUpdate();
144
- $("#listing").style.display = "block";
145
- initEvents();
146
- }
147
-
148
117
  function onEntriesUpdate() {
149
- applyFilter($("#search").value);
118
+ $container.innerHTML = "";
119
+ currentIndex = 0;
120
+ moreContent = true;
121
+ render();
150
122
  }
151
123
 
152
- window.addEventListener("DOMContentLoaded", function() {
124
+ function init() {
153
125
  $ = document.querySelector.bind(document);
154
126
  $container = $("#listing-container");
155
- });
127
+
128
+ onEntriesUpdate();
129
+
130
+ initEvents();
131
+ }
156
132
 
157
133
  return {
158
- onEntriesInit: onEntriesInit,
134
+ init: init,
159
135
  onEntriesUpdate: onEntriesUpdate
160
136
  };
161
137
  })();
138
+
139
+ window.addEventListener("DOMContentLoaded", Listing.init);
@@ -0,0 +1,2 @@
1
+ var naturalOrderBy=function(e){"use strict";var d=function(e,n){return e<n?-1:n<e?1:0},r=/(^0x[\da-fA-F]+$|^([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?(?!\.\d+)(?=\D|\s|$))|\d+)/g,l=/^\s+|\s+$/g,o=/\s+/g,u=/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/,t=/(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[/-]\d{1,4}[/-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,a=/^0+[1-9]{1}[0-9]*$/,y=/[^\x00-\x80]/,m=function(e,n){return e.value===n.value?0:void 0!==e.parsedNumber&&void 0!==n.parsedNumber?d(e.parsedNumber,n.parsedNumber):e.chunks&&n.chunks?function(e,n){for(var r,t,i,o,u=e.length,a=n.length,s=Math.min(u,a),c=0;c<s;c++){var f=e[c],l=n[c];if(f!==l)return""===f!=(""===l)?""===f?-1:1:"number"==typeof f&&"number"==typeof l?d(f,l):"number"==typeof f||"number"==typeof l?"number"==typeof f?-1:1:y.test(f+l)&&f.localeCompare?(i=l,(o=f.localeCompare(i))?o/Math.abs(o):0):(r=f)<(t=l)?-1:t<r?1:0}return s<u||s<a?u<=s?-1:1:0}(e.chunks,n.chunks):(t=n,((r=e).chunks?!t.chunks:t.chunks)?r.chunks?-1:1:(r.isNaN?!t.isNaN:t.isNaN)?r.isNaN?-1:1:(r.isSymbol?!t.isSymbol:t.isSymbol)?r.isSymbol?-1:1:(r.isObject?!t.isObject:t.isObject)?r.isObject?-1:1:(r.isArray?!t.isArray:t.isArray)?r.isArray?-1:1:(r.isFunction?!t.isFunction:t.isFunction)?r.isFunction?-1:1:(r.isNull?!t.isNull:t.isNull)?r.isNull?-1:1:0);var r,t},v="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},s=function(r){return"function"==typeof r?r:function(e){if(Array.isArray(e)){var n=Number(r);if(Number.isInteger(n))return e[n]}else if(e&&"object"===(void 0===e?"undefined":v(e))&&"function"!=typeof r)return e[r];return e}},c=function(e){if(0!==e.length){var n=Number(e);if(!Number.isNaN(n))return n}},b=function(e){var n=c(e);return void 0!==n?n:function(e){if(t.test(e)){var n=Date.parse(e);if(!Number.isNaN(n))return n}}(e)},p=function(e){var n;return(n=e,n.replace(r,"\0$1\0").replace(/\0$/,"").replace(/^\0/,"").split("\0")).map(function(e,n,r){return t=e,i=r.length,!u.test(t)||a.test(t)&&1!==i?t.replace(o," ").replace(l,""):c(t)||0;var t,i})},N=function(e){return Number.isNaN(e)||e instanceof Number&&Number.isNaN(e.valueOf())},f=function(){var f=!(0<arguments.length&&void 0!==arguments[0])||arguments[0];return function(e){if("string"==typeof e||e instanceof String||("number"==typeof e||e instanceof Number)&&!N(e)||"boolean"==typeof e||e instanceof Boolean||e instanceof Date){var n=(i=f,"boolean"==typeof(t=e)||t instanceof Boolean?Number(t).toString():"number"==typeof t||t instanceof Number?t.toString():t instanceof Date?t.getTime().toString():"string"==typeof t||t instanceof String?(i?t:t.toLowerCase()).replace(l,""):""),r=b(n);return{parsedNumber:r,chunks:p(r?""+r:n),value:e}}var t,i,o,u,a,s,c;return{isArray:Array.isArray(e),isFunction:(c=e,"function"==typeof c),isNaN:N(e),isNull:(s=e,null===s),isObject:(a=e,!(null===a||"object"!==(void 0===a?"undefined":v(a))||Array.isArray(a)||a instanceof Number||a instanceof String||a instanceof Boolean||a instanceof Date)),isSymbol:(u=e,"symbol"===(void 0===u?"undefined":v(u))),isUndefined:(o=e,void 0===o),value:e}}},g=function(t,e,r){var i=e.length?e.map(s):[function(e){return e}],n=t.map(function(n,e){return{index:e,values:i.map(function(e){return e(n)}).map(f())}});return n.sort(function(e,n){return function(e,n,r){for(var t=e.values,i=n.values,o=t.length,u=r.length,a=0;a<o;a++){var s=a<u?r[a]:null;if(s&&"function"==typeof s){var c=s(t[a].value,i[a].value);if(c)return c}else{var f=m(t[a],i[a]);if(f)return f*("desc"===s?-1:1)}}return 0}(e,n,r)}),n.map(function(e){return n=t,r=e.index,n[r];var n,r})};var i={caseSensitive:!0,order:"asc"};return e.orderBy=function(e,n,r){if(!e||!Array.isArray(e))return[];var t=function(e){if(!e)return[];var n=Array.isArray(e)?[].concat(e):[e];return n.some(function(e){return"string"!=typeof e&&"number"!=typeof e&&"function"!=typeof e})?[]:n}(n),i=function(e){if(!e)return[];var n=Array.isArray(e)?[].concat(e):[e];return n.some(function(e){return"asc"!==e&&"desc"!==e&&"function"!=typeof e})?[]:n}(r);return g(e,t,i)},e.compare=function(e){var n,o,r=(n=e)&&"object"===(void 0===n?"undefined":v(n))?{caseSensitive:"boolean"==typeof n.caseSensitive?n.caseSensitive:i.caseSensitive,order:"asc"===n.order||"desc"===n.order?n.order:i.order}:i;return o=r,function(e,n){var r=f(o.caseSensitive),t=r(e),i=r(n);return m(t,i)*("desc"===o.order?-1:1)}},e}({});
2
+ //# sourceMappingURL=natural-orderby.min.js.map
File without changes
data/lib/servel.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'rack'
2
2
  require 'rack/handler/puma'
3
3
  require 'hamlit'
4
- require 'naturalsorter'
5
4
  require 'active_support/all'
6
5
  require 'lru_redux'
7
6
 
@@ -23,7 +22,6 @@ require "servel/instrumentation"
23
22
  require "servel/entry"
24
23
  require "servel/entry_factory"
25
24
  require "servel/haml_context"
26
- require "servel/entries"
27
25
  require "servel/index"
28
26
  require "servel/app"
29
27
  require "servel/home_app"
data/lib/servel/app.rb CHANGED
@@ -25,13 +25,7 @@ class Servel::App
25
25
 
26
26
  return [404, {}, []] unless fs_path.exist?
27
27
 
28
- request = Rack::Request.new(env)
29
-
30
- if json_request?(request)
31
- Servel::Entries.new(url_root: url_root, url_path: url_path, fs_path: fs_path, params: request.params).render
32
- else
33
- Servel::Index.new(url_root: url_root, url_path: url_path).render
34
- end
28
+ Servel::Index.new(url_root: url_root, url_path: url_path, fs_path: fs_path).render
35
29
  end
36
30
 
37
31
  def redirect(location)
@@ -44,10 +38,6 @@ class Servel::App
44
38
  Rack::Utils.clean_path_info(url_path)
45
39
  end
46
40
 
47
- def json_request?(request)
48
- Rack::Utils.best_q_match(request.get_header("HTTP_ACCEPT"), ['text/html', 'application/json']) == 'application/json'
49
- end
50
-
51
41
  def try_encode(string)
52
42
  return string if string.encoding == UTF_8
53
43
  string.encode(UTF_8)
data/lib/servel/entry.rb CHANGED
@@ -32,11 +32,13 @@ class Servel::Entry
32
32
  icon: @icon,
33
33
  href: Rack::Utils.escape_path(@href),
34
34
  class: @listing_classes,
35
- media_type: @media_type,
35
+ mediaType: @media_type,
36
36
  name: @name,
37
37
  type: @type,
38
- size: @size.nil? ? "-" : @size,
39
- mtime: @mtime.nil? ? "-" : @mtime.strftime("%e %b %Y %l:%M %p"),
38
+ size: @size.to_i,
39
+ sizeText: @size.nil? ? "-" : @size,
40
+ mtime: @mtime.to_i,
41
+ mtimeText: @mtime.nil? ? "-" : @mtime.strftime("%e %b %Y %l:%M %p"),
40
42
  media: media?
41
43
  }
42
44
  end
@@ -9,7 +9,7 @@ class Servel::HamlContext
9
9
  end
10
10
 
11
11
  def initialize
12
- @build_path = Pathname.new(__FILE__).dirname.realpath + 'templates'
12
+ @build_path = Pathname.new(__FILE__).dirname.realpath + "../../app"
13
13
  end
14
14
 
15
15
  def render(template, locals = {})
data/lib/servel/index.rb CHANGED
@@ -1,21 +1,44 @@
1
1
  class Servel::Index
2
2
  extend Servel::Instrumentation
3
+ RENDER_CACHE = LruRedux::ThreadSafeCache.new(100)
3
4
 
4
- def initialize(url_root:, url_path:)
5
+ def initialize(url_root:, url_path:, fs_path:)
5
6
  @url_root = url_root
6
7
  @url_path = url_path
8
+ @fs_path = fs_path
7
9
  end
8
10
 
9
11
  def render
10
- Servel::HamlContext.render('index.haml', locals)
12
+ RENDER_CACHE.getset(render_cache_key) { Servel::HamlContext.render('index.haml', locals) }
13
+ end
14
+
15
+ def render_cache_key
16
+ @render_cache_key ||= [@fs_path.to_s, @fs_path.mtime.to_i].join("-")
11
17
  end
12
18
 
13
19
  def locals
20
+ children = @fs_path.children.map { |path| Servel::EntryFactory.for(path) }.compact
21
+
14
22
  {
15
23
  url_root: @url_root,
16
- url_path: @url_path
24
+ url_path: @url_path,
25
+ special_entries: special_entries.to_json,
26
+ directory_entries: children.select(&:directory?).to_json,
27
+ file_entries: children.select(&:file?).to_json
17
28
  }
18
29
  end
19
30
 
31
+ def special_entries
32
+ list = []
33
+ list << Servel::EntryFactory.home("/") if @url_root != ""
34
+
35
+ unless @url_path == "/"
36
+ list << Servel::EntryFactory.top(@url_root == "" ? "/" : @url_root)
37
+ list << Servel::EntryFactory.parent("../")
38
+ end
39
+
40
+ list
41
+ end
42
+
20
43
  instrument :render, :locals
21
44
  end
@@ -1,3 +1,3 @@
1
1
  module Servel
2
- VERSION = "0.20.0"
2
+ VERSION = "0.21.0"
3
3
  end
data/servel.gemspec CHANGED
@@ -25,7 +25,6 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "rake", "~> 12.0"
26
26
  spec.add_dependency "rack", "~> 2.0"
27
27
  spec.add_dependency "puma"
28
- spec.add_dependency "naturalsorter"
29
28
  spec.add_dependency "hamlit"
30
29
  spec.add_dependency "activesupport"
31
30
  spec.add_dependency "lru_redux"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: servel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.0
4
+ version: 0.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brenton "B-Train" Fletcher
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-30 00:00:00.000000000 Z
11
+ date: 2018-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: naturalsorter
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: hamlit
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -135,33 +121,33 @@ files:
135
121
  - LICENSE.txt
136
122
  - README.md
137
123
  - Rakefile
124
+ - app/_gallery.haml
125
+ - app/_listing.haml
126
+ - app/css/common.css
127
+ - app/css/gallery-text.css
128
+ - app/css/gallery.css
129
+ - app/css/home.css
130
+ - app/css/listing.css
131
+ - app/css/normalize.css
132
+ - app/home.haml
133
+ - app/index.haml
134
+ - app/js/entries.js
135
+ - app/js/gallery.js
136
+ - app/js/listing.js
137
+ - app/js/natural-orderby.min.js
138
+ - app/js/ume.js
138
139
  - bin/console
139
140
  - bin/servel
140
141
  - bin/setup
141
142
  - lib/servel.rb
142
143
  - lib/servel/app.rb
143
144
  - lib/servel/cli.rb
144
- - lib/servel/entries.rb
145
145
  - lib/servel/entry.rb
146
146
  - lib/servel/entry_factory.rb
147
147
  - lib/servel/haml_context.rb
148
148
  - lib/servel/home_app.rb
149
149
  - lib/servel/index.rb
150
150
  - lib/servel/instrumentation.rb
151
- - lib/servel/templates/_gallery.haml
152
- - lib/servel/templates/_listing.haml
153
- - lib/servel/templates/css/common.css
154
- - lib/servel/templates/css/gallery-text.css
155
- - lib/servel/templates/css/gallery.css
156
- - lib/servel/templates/css/home.css
157
- - lib/servel/templates/css/listing.css
158
- - lib/servel/templates/css/normalize.css
159
- - lib/servel/templates/home.haml
160
- - lib/servel/templates/index.haml
161
- - lib/servel/templates/js/gallery.js
162
- - lib/servel/templates/js/index.js
163
- - lib/servel/templates/js/listing.js
164
- - lib/servel/templates/js/ume.js
165
151
  - lib/servel/version.rb
166
152
  - servel.gemspec
167
153
  homepage: http://bloople.net/
@@ -1,68 +0,0 @@
1
- class Servel::Entries
2
- extend Servel::Instrumentation
3
- RENDER_CACHE = LruRedux::ThreadSafeCache.new(100)
4
- SORT_METHODS = ["name", "mtime", "size", "type"]
5
- SORT_DIRECTIONS = ["asc", "desc"]
6
-
7
- def initialize(url_root:, url_path:, fs_path:, params:)
8
- @url_root = url_root
9
- @url_path = url_path
10
- @fs_path = fs_path
11
- @params = params
12
- end
13
-
14
- def render
15
- RENDER_CACHE.getset(render_cache_key) { [200, {}, [entries.to_json]] }
16
- end
17
-
18
- def render_cache_key
19
- @render_cache_key ||= [@fs_path.to_s, @fs_path.mtime.to_i, sort_method, sort_direction].join("-")
20
- end
21
-
22
- def entries
23
- children = @fs_path.children.map { |path| Servel::EntryFactory.for(path) }.compact
24
- special_entries + apply_sort(children.select(&:directory?)) + apply_sort(children.select(&:file?))
25
- end
26
-
27
- def sort_method
28
- param = @params["_servel_sort_method"]
29
- param = "name" unless SORT_METHODS.include?(param)
30
- param
31
- end
32
-
33
- def sort_direction
34
- param = @params["_servel_sort_direction"]
35
- param = "asc" unless SORT_DIRECTIONS.include?(param)
36
- param
37
- end
38
-
39
- def special_entries
40
- list = []
41
- list << Servel::EntryFactory.home("/") if @url_root != ""
42
-
43
- unless @url_path == "/"
44
- list << Servel::EntryFactory.top(@url_root == "" ? "/" : @url_root)
45
- list << Servel::EntryFactory.parent("../")
46
- end
47
-
48
- list
49
- end
50
-
51
- def apply_sort(entries)
52
- entries = case sort_method
53
- when "name"
54
- Naturalsorter::Sorter.sort_by_method(entries, :name, true)
55
- when "mtime"
56
- entries.sort_by { |entry| entry.mtime }
57
- when "size"
58
- entries.sort_by { |entry| entry.size || 0 }
59
- when "type"
60
- entries.sort_by { |entry| entry.type }
61
- end
62
-
63
- entries.reverse! if sort_direction == "desc"
64
- entries
65
- end
66
-
67
- instrument :render, :entries, :apply_sort
68
- end
@@ -1,54 +0,0 @@
1
- "use strict";
2
-
3
- var Index = (function() {
4
- var $;
5
- var inited = false;
6
-
7
- function entriesURL() {
8
- var sortable = $("th.sortable.sort-active");
9
- return `${location.pathname}?_servel_sort_method=${sortable.dataset.sortMethod}&_servel_sort_direction=${sortable.dataset.sortDirection}`;
10
- }
11
-
12
- function onEntriesLoad(callback) {
13
- if(inited) {
14
- Gallery.onEntriesUpdate();
15
- Listing.onEntriesUpdate();
16
- }
17
- else {
18
- inited = true;
19
- Gallery.onEntriesInit();
20
- Listing.onEntriesInit();
21
- }
22
-
23
- $("#loading").style.display = "none";
24
- if(callback) callback();
25
- }
26
-
27
- function loadEntries(callback) {
28
- $("#loading").style.display = "flex";
29
-
30
- var http = new XMLHttpRequest();
31
- http.open("GET", entriesURL());
32
-
33
- http.onreadystatechange = function() {
34
- if(http.readyState === 4 && http.status === 200) {
35
- window.entries = JSON.parse(http.responseText);
36
- setTimeout(function() {
37
- onEntriesLoad(callback);
38
- }, 0);
39
- }
40
- };
41
-
42
- http.setRequestHeader("Accept", "application/json");
43
- http.send();
44
- }
45
-
46
- window.addEventListener("DOMContentLoaded", function() {
47
- $ = document.querySelector.bind(document);
48
- loadEntries();
49
- });
50
-
51
- return {
52
- loadEntries: loadEntries
53
- };
54
- })();