servel 0.11.0 → 0.12.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.
@@ -1,18 +1,18 @@
1
- !!!
2
- %html
3
- %head
4
- %meta{charset: 'utf-8'}
5
- %title Browse Listings
6
- %meta{name: 'viewport', content: 'width=device-width, height=device-height, user-scalable=no'}
7
- :css
8
- #{include('css/normalize.css')}
9
- #{include('css/common.css')}
10
- #{include('css/home.css')}
11
- %body
12
- #home
13
- %h1 Browse Listings
14
- %ul#roots
15
- - roots.each do |root|
16
- %li
17
- %a{href: root}= root
18
- %a.new-tab{href: root, target: "_blank"} (New Tab)
1
+ !!!
2
+ %html
3
+ %head
4
+ %meta{charset: 'utf-8'}
5
+ %title Browse Listings
6
+ %meta{name: 'viewport', content: 'width=device-width, height=device-height, user-scalable=no'}
7
+ :css
8
+ #{include('css/normalize.css')}
9
+ #{include('css/common.css')}
10
+ #{include('css/home.css')}
11
+ %body
12
+ #home
13
+ %h1 Browse Listings
14
+ %ul#roots
15
+ - roots.each do |root|
16
+ %li
17
+ %a{href: root}= root
18
+ %a.new-tab{href: root, target: "_blank"} (New Tab)
@@ -1,21 +1,23 @@
1
- !!!
2
- %html
3
- %head
4
- %meta{charset: 'utf-8'}
5
- %title Listing of #{url_root}#{url_path}
6
- %meta{name: 'viewport', content: 'width=device-width, height=device-height, user-scalable=no'}
7
- :css
8
- #{include('css/normalize.css')}
9
- #{include('css/common.css')}
10
- #{include('css/index.css')}
11
- #{include('css/listing.css')}
12
- #{include('css/gallery.css')}
13
- #{include('css/gallery-text.css')}
14
-
15
- :javascript
16
- #{include('js/ume.js')}
17
- #{include('js/gallery.js')}
18
- %body
19
- - if files.any? { |f| f.media? }
20
- #gallery!= partial('gallery')
21
- #listing!= partial('listing', { url_root: url_root, url_path: url_path, directories: directories, files: files })
1
+ !!!
2
+ %html
3
+ %head
4
+ %meta{charset: 'utf-8'}
5
+ %title Listing of #{url_root}#{url_path}
6
+ %meta{name: 'viewport', content: 'width=device-width, height=device-height, user-scalable=no'}
7
+ :css
8
+ #{include('css/normalize.css')}
9
+ #{include('css/common.css')}
10
+ #{include('css/index.css')}
11
+ #{include('css/listing.css')}
12
+ #{include('css/gallery.css')}
13
+ #{include('css/gallery-text.css')}
14
+
15
+ :javascript
16
+ #{include('js/url-search-params.js')}
17
+ #{include('js/listing.js')}
18
+ #{include('js/ume.js')}
19
+ #{include('js/gallery.js')}
20
+ %body
21
+ - if files.any? { |f| f.media? }
22
+ #gallery!= partial('gallery')
23
+ #listing!= partial('listing', { url_root: url_root, url_path: url_path, directories: directories, files: files, sort: sort })
@@ -1,9 +1,11 @@
1
1
  var $;
2
2
 
3
3
  var Gallery = (function() {
4
+ var TYPES = ["image", "video", "audio", "text"];
4
5
  var urls = [];
5
6
  var types = [];
6
7
  var currentIndex = 0;
8
+ var $gallery;
7
9
 
8
10
  function initItems() {
9
11
  var links = document.querySelectorAll("#listing a.default.media");
@@ -26,12 +28,27 @@ var Gallery = (function() {
26
28
  http.send();
27
29
  }
28
30
 
31
+ function clearContent() {
32
+ for(var i = 0; i < TYPES.length; i++) {
33
+ var type = TYPES[i];
34
+
35
+ $gallery.classList.remove(type);
36
+
37
+ if(type == "text") {
38
+ $("#text-content").innerHTML = "";
39
+ }
40
+ else {
41
+ $("#" + type).src = "about:none";
42
+ }
43
+ }
44
+ }
45
+
29
46
  function render() {
47
+ clearContent();
48
+
30
49
  var url = urls[currentIndex];
31
50
  var type = types[currentIndex];
32
51
 
33
- var $gallery = $("#gallery");
34
- $gallery.classList.remove("image", "video", "audio", "text");
35
52
  $gallery.classList.add(type);
36
53
 
37
54
  if(type == "text") {
@@ -129,6 +146,8 @@ var Gallery = (function() {
129
146
  }
130
147
 
131
148
  function init() {
149
+ $gallery = $("#gallery");
150
+
132
151
  initItems();
133
152
  initEvents();
134
153
  render();
@@ -0,0 +1,26 @@
1
+ function initListing() {
2
+ $ = document.querySelector.bind(document);
3
+
4
+ function applySort(sortable) {
5
+ var params = new URLSearchParams(location.search.slice(1));
6
+ params.set("_servel_sort_method", sortable.dataset.sortMethod);
7
+ params.set("_servel_sort_direction", sortable.dataset.sortDirection);
8
+
9
+ if('sortActive' in sortable.dataset) {
10
+ params.set("_servel_sort_direction", sortable.dataset.sortDirection == "asc" ? "desc" : "asc");
11
+ }
12
+
13
+ location.href = location.pathname + "?" + params.toString();
14
+ }
15
+
16
+ document.body.addEventListener("click", function(e) {
17
+ if(!e.target) return;
18
+
19
+ if(e.target.closest("th.sortable")) {
20
+ e.preventDefault();
21
+ applySort(e.target.closest("th.sortable"));
22
+ }
23
+ });
24
+ }
25
+
26
+ window.addEventListener("DOMContentLoaded", initListing);
@@ -1,63 +1,63 @@
1
- var ume = (function() {
2
- function normalise(text) {
3
- return text.replace(/\s+/g, " ").trim();
4
- }
5
-
6
- function escapeEntities(text) {
7
- return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
8
- }
9
-
10
- const BOLD_ITALIC_REGEX = /(?:(\W|^)(\*)|(\*)(\W|$)|(\W|^)(_)|(_)(\W|$))/;
11
-
12
- function parseInlines(text) {
13
- const parts = text.split(BOLD_ITALIC_REGEX);
14
-
15
- if(parts.filter(function(part) { return part == "*"; }).length % 2 != 0) return text;
16
- if(parts.filter(function(part) { return part == "_"; }).length % 2 != 0) return text;
17
-
18
- var inBold = false;
19
- var inItalic = false;
20
- var html = [];
21
-
22
- for(const part of parts) {
23
- if(part == "*") {
24
- inBold = !inBold;
25
- html.push(inBold ? "<b>" : "</b>"); //Note that in_bold has been inverted, so this is inverted as well
26
- }
27
- else if(part == "_") {
28
- inItalic = !inItalic;
29
- html.push(inItalic ? "<i>" : "</i>"); //Note that in_italic has been inverted, so this is inverted as well
30
- }
31
- else {
32
- html.push(part);
33
- }
34
- }
35
-
36
- return html.join("");
37
- }
38
-
39
- function parseHeading(text) {
40
- const parts = text.match(/^(#+) (.*)$/);
41
- return "<h" + parts[1].length + ">" + parseInlines(parts[2]) + "</h" + parts[1].length + ">";
42
- }
43
-
44
- function parseParagraph(text) {
45
- return "<p>" + parseInlines(text) + "</p>";
46
- }
47
-
48
- return function(text) {
49
- const nodes = text.split(/(\r?\n){2,}/g);
50
- var html = [];
51
-
52
- for(const node of nodes) {
53
- const cleanedNode = escapeEntities(normalise(node));
54
-
55
- if(cleanedNode.startsWith("#")) html.push(parseHeading(cleanedNode));
56
- else html.push(parseParagraph(cleanedNode));
57
- }
58
-
59
- return html.join("");
60
- }
61
- })();
62
-
63
- if(typeof module === "object" && typeof module.exports === "object") module.exports = ume;
1
+ var ume = (function() {
2
+ function normalise(text) {
3
+ return text.replace(/\s+/g, " ").trim();
4
+ }
5
+
6
+ function escapeEntities(text) {
7
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
8
+ }
9
+
10
+ const BOLD_ITALIC_REGEX = /(?:(\W|^)(\*)|(\*)(\W|$)|(\W|^)(_)|(_)(\W|$))/;
11
+
12
+ function parseInlines(text) {
13
+ const parts = text.split(BOLD_ITALIC_REGEX);
14
+
15
+ if(parts.filter(function(part) { return part == "*"; }).length % 2 != 0) return text;
16
+ if(parts.filter(function(part) { return part == "_"; }).length % 2 != 0) return text;
17
+
18
+ var inBold = false;
19
+ var inItalic = false;
20
+ var html = [];
21
+
22
+ for(const part of parts) {
23
+ if(part == "*") {
24
+ inBold = !inBold;
25
+ html.push(inBold ? "<b>" : "</b>"); //Note that in_bold has been inverted, so this is inverted as well
26
+ }
27
+ else if(part == "_") {
28
+ inItalic = !inItalic;
29
+ html.push(inItalic ? "<i>" : "</i>"); //Note that in_italic has been inverted, so this is inverted as well
30
+ }
31
+ else {
32
+ html.push(part);
33
+ }
34
+ }
35
+
36
+ return html.join("");
37
+ }
38
+
39
+ function parseHeading(text) {
40
+ const parts = text.match(/^(#+) (.*)$/);
41
+ return "<h" + parts[1].length + ">" + parseInlines(parts[2]) + "</h" + parts[1].length + ">";
42
+ }
43
+
44
+ function parseParagraph(text) {
45
+ return "<p>" + parseInlines(text) + "</p>";
46
+ }
47
+
48
+ return function(text) {
49
+ const nodes = text.split(/(\r?\n){2,}/g);
50
+ var html = [];
51
+
52
+ for(const node of nodes) {
53
+ const cleanedNode = escapeEntities(normalise(node));
54
+
55
+ if(cleanedNode.startsWith("#")) html.push(parseHeading(cleanedNode));
56
+ else html.push(parseParagraph(cleanedNode));
57
+ }
58
+
59
+ return html.join("");
60
+ }
61
+ })();
62
+
63
+ if(typeof module === "object" && typeof module.exports === "object") module.exports = ume;
@@ -0,0 +1,2 @@
1
+ /*! (C) Andrea Giammarchi - Mit Style License */
2
+ var URLSearchParams=URLSearchParams||function(){"use strict";function URLSearchParams(query){var index,key,value,pairs,i,length,dict=Object.create(null);this[secret]=dict;if(!query)return;if(typeof query==="string"){if(query.charAt(0)==="?"){query=query.slice(1)}for(pairs=query.split("&"),i=0,length=pairs.length;i<length;i++){value=pairs[i];index=value.indexOf("=");if(-1<index){appendTo(dict,decode(value.slice(0,index)),decode(value.slice(index+1)))}else if(value.length){appendTo(dict,decode(value),"")}}}else{if(isArray(query)){for(i=0,length=query.length;i<length;i++){value=query[i];appendTo(dict,value[0],value[1])}}else{for(key in query){appendTo(dict,key,query[key])}}}}var isArray=Array.isArray,URLSearchParamsProto=URLSearchParams.prototype,find=/[!'\(\)~]|%20|%00/g,plus=/\+/g,replace={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"},replacer=function(match){return replace[match]},secret="__URLSearchParams__:"+Math.random();function appendTo(dict,name,value){if(name in dict){dict[name].push(""+value)}else{dict[name]=isArray(value)?value:[""+value]}}function decode(str){return decodeURIComponent(str.replace(plus," "))}function encode(str){return encodeURIComponent(str).replace(find,replacer)}URLSearchParamsProto.append=function append(name,value){appendTo(this[secret],name,value)};URLSearchParamsProto["delete"]=function del(name){delete this[secret][name]};URLSearchParamsProto.get=function get(name){var dict=this[secret];return name in dict?dict[name][0]:null};URLSearchParamsProto.getAll=function getAll(name){var dict=this[secret];return name in dict?dict[name].slice(0):[]};URLSearchParamsProto.has=function has(name){return name in this[secret]};URLSearchParamsProto.set=function set(name,value){this[secret][name]=[""+value]};URLSearchParamsProto.forEach=function forEach(callback,thisArg){var dict=this[secret];Object.getOwnPropertyNames(dict).forEach(function(name){dict[name].forEach(function(value){callback.call(thisArg,value,name,this)},this)},this)};URLSearchParamsProto.toJSON=function toJSON(){return{}};URLSearchParamsProto.toString=function toString(){var dict=this[secret],query=[],i,key,name,value;for(key in dict){name=encode(key);for(i=0,value=dict[key];i<value.length;i++){query.push(name+"="+encode(value[i]))}}return query.join("&")};var dP=Object.defineProperty,gOPD=Object.getOwnPropertyDescriptor,createSearchParamsPollute=function(search){function append(name,value){URLSearchParamsProto.append.call(this,name,value);name=this.toString();search.set.call(this._usp,name?"?"+name:"")}function del(name){URLSearchParamsProto["delete"].call(this,name);name=this.toString();search.set.call(this._usp,name?"?"+name:"")}function set(name,value){URLSearchParamsProto.set.call(this,name,value);name=this.toString();search.set.call(this._usp,name?"?"+name:"")}return function(sp,value){sp.append=append;sp["delete"]=del;sp.set=set;return dP(sp,"_usp",{configurable:true,writable:true,value:value})}},createSearchParamsCreate=function(polluteSearchParams){return function(obj,sp){dP(obj,"_searchParams",{configurable:true,writable:true,value:polluteSearchParams(sp,obj)});return sp}},updateSearchParams=function(sp){var append=sp.append;sp.append=URLSearchParamsProto.append;URLSearchParams.call(sp,sp._usp.search.slice(1));sp.append=append},verifySearchParams=function(obj,Class){if(!(obj instanceof Class))throw new TypeError("'searchParams' accessed on an object that "+"does not implement interface "+Class.name)},upgradeClass=function(Class){var ClassProto=Class.prototype,searchParams=gOPD(ClassProto,"searchParams"),href=gOPD(ClassProto,"href"),search=gOPD(ClassProto,"search"),createSearchParams;if(!searchParams&&search&&search.set){createSearchParams=createSearchParamsCreate(createSearchParamsPollute(search));Object.defineProperties(ClassProto,{href:{get:function(){return href.get.call(this)},set:function(value){var sp=this._searchParams;href.set.call(this,value);if(sp)updateSearchParams(sp)}},search:{get:function(){return search.get.call(this)},set:function(value){var sp=this._searchParams;search.set.call(this,value);if(sp)updateSearchParams(sp)}},searchParams:{get:function(){verifySearchParams(this,Class);return this._searchParams||createSearchParams(this,new URLSearchParams(this.search.slice(1)))},set:function(sp){verifySearchParams(this,Class);createSearchParams(this,sp)}}})}};upgradeClass(HTMLAnchorElement);if(/^function|object$/.test(typeof URL)&&URL.prototype)upgradeClass(URL);return URLSearchParams}();(function(URLSearchParamsProto){var iterable=function(){try{return!!Symbol.iterator}catch(error){return false}}();if(!("forEach"in URLSearchParamsProto)){URLSearchParamsProto.forEach=function forEach(callback,thisArg){var names=Object.create(null);this.toString().replace(/=[\s\S]*?(?:&|$)/g,"=").split("=").forEach(function(name){if(!name.length||name in names)return;(names[name]=this.getAll(name)).forEach(function(value){callback.call(thisArg,value,name,this)},this)},this)}}if(!("keys"in URLSearchParamsProto)){URLSearchParamsProto.keys=function keys(){var items=[];this.forEach(function(value,name){items.push(name)});var iterator={next:function(){var value=items.shift();return{done:value===undefined,value:value}}};if(iterable){iterator[Symbol.iterator]=function(){return iterator}}return iterator}}if(!("values"in URLSearchParamsProto)){URLSearchParamsProto.values=function values(){var items=[];this.forEach(function(value){items.push(value)});var iterator={next:function(){var value=items.shift();return{done:value===undefined,value:value}}};if(iterable){iterator[Symbol.iterator]=function(){return iterator}}return iterator}}if(!("entries"in URLSearchParamsProto)){URLSearchParamsProto.entries=function entries(){var items=[];this.forEach(function(value,name){items.push([name,value])});var iterator={next:function(){var value=items.shift();return{done:value===undefined,value:value}}};if(iterable){iterator[Symbol.iterator]=function(){return iterator}}return iterator}}if(iterable&&!(Symbol.iterator in URLSearchParamsProto)){URLSearchParamsProto[Symbol.iterator]=URLSearchParamsProto.entries}if(!("sort"in URLSearchParamsProto)){URLSearchParamsProto.sort=function sort(){var entries=this.entries(),entry=entries.next(),done=entry.done,keys=[],values=Object.create(null),i,key,value;while(!done){value=entry.value;key=value[0];keys.push(key);if(!(key in values)){values[key]=[]}values[key].push(value[1]);entry=entries.next();done=entry.done}keys.sort();for(i=0;i<keys.length;i++){this["delete"](keys[i])}for(i=0;i<keys.length;i++){key=keys[i];this.append(key,values[key].shift())}}}})(URLSearchParams.prototype);
@@ -1,3 +1,3 @@
1
1
  module Servel
2
- VERSION = "0.11.0"
2
+ VERSION = "0.12.0"
3
3
  end
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_dependency "rack", "~> 2.0"
27
27
  spec.add_dependency "puma"
28
28
  spec.add_dependency "naturalsorter"
29
- spec.add_dependency "haml", "~> 4"
29
+ spec.add_dependency "hamlit"
30
30
  spec.add_dependency "activesupport"
31
+ spec.add_dependency "lru_redux"
31
32
  end
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.11.0
4
+ version: 0.12.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-03-19 00:00:00.000000000 Z
11
+ date: 2018-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -81,19 +81,19 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: haml
84
+ name: hamlit
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '4'
89
+ version: '0'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '4'
96
+ version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: activesupport
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: lru_redux
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  description: Serves files and directories over HTTP.
112
126
  email:
113
127
  - i@bloople.net
@@ -132,6 +146,7 @@ files:
132
146
  - lib/servel/haml_context.rb
133
147
  - lib/servel/home_app.rb
134
148
  - lib/servel/index.rb
149
+ - lib/servel/instrumentation.rb
135
150
  - lib/servel/templates/_gallery.haml
136
151
  - lib/servel/templates/_listing.haml
137
152
  - lib/servel/templates/css/common.css
@@ -144,7 +159,9 @@ files:
144
159
  - lib/servel/templates/home.haml
145
160
  - lib/servel/templates/index.haml
146
161
  - lib/servel/templates/js/gallery.js
162
+ - lib/servel/templates/js/listing.js
147
163
  - lib/servel/templates/js/ume.js
164
+ - lib/servel/templates/js/url-search-params.js
148
165
  - lib/servel/version.rb
149
166
  - servel.gemspec
150
167
  homepage: http://bloople.net/