y\x10\x003㘡\x893\xc1\xc1MI\x84] \x00\x06!\x0f\x02`f\xaa\xaaJ\x92D\x1d\xa6\xa9,KW\x87\x9b\xba\\.\xf6\x95\x9f\xac\xd3\xe9d8\b`.\xe4A\x00̏ϟ'\xcbp\x10n3\x97\x06\x80Y\x90\a\x010?EQ\x9c\xcfgu\x98\x1a\xc3A\x8c#\xcb2\xfb\xcaOP\xf3Xn\x1e\xce\xea\x000\x17\xf2 \x00fɈ\xd0\x04\x19\r\xc0\xcd\xe6\xb1\f\xc0\\ȃ\x00\x98%\x03\x02SS\u05f5\xe1 F\xd3\xdcl\x9e\x00\x93r\xbd^\x9bDz:\x00̈<\b\x80\xb92 0)F\x03p˹\x1c\x00̈<\b\x80\xb9J\xd3\xd4\xc1\xf3\x13\xd1\\\b\xdd #K\x92\xc4\x13`\"\xec\x1d\x060G\xf2 \x00\xe6\xca\xc1\xf3\xd3\xf1\xfa\xfa\xea\x90i\xc6\u007f\x02H!\xa7\xf3\x04P\x04\x80ّ\a\x01\xa0\t!\x88\xe1 \xeeE\"<\x91'\x80\v\x010G\xf2 \x00f\xcc\xc1\xf3S`8\x88;>\x01N\xa7\x93:\xdcW\x92$\x9e\x00\x00s$\x0f\x02`\xdeL\xa6ܗ\xe1 \xee˾\xf2\x1e\xc2\x00\xf4#\x0f\x02`\xde\x1c<\u007f_F\x03\xb8/C\x82\xf7u:\x9d\x9aK\xa0\x0e\x00s$\x0f\x02`\xf6\f\bܑ\xd1\x00܄\x8a\x0f\xc0\x1cɃ\x00\x98=\xc7Nߋ\xd1\x00\xa6 ˲<\xcf\xd5a|M\xd9/\x97\x8b:\x00̔<\b\x80%\xf0\x19\xf5]\x98\xccb\"\x9co\xe5\t\x00@W\xf2 \x00t\x83\xf4a8\x88I=\x01\xec#6\xb2\xa6\xe0Y\x96\xa9\x03\xc0|Ƀ\x00X\x02\xc7N\x8f\xcfh\x00nH\x05\a`\xbe\xe4A\x00,\x84\x11\xa11\x19\x0ebj\xd24\xb5\x8f\xd8h\x9aR{\xe4\x02̝<\b\x80\x85Ȳ\xecz\xbd\xaa\xc38l\xd8\xc4\xd4TU\xe5\xb6\xf4\x04\x00\xa0=y\x10\x00Z\x14\xbaq\xa8\x10\x93}\x02\x18\x11\x1aASd\x0f[\x80\x05\x90\a\x01\xb0\x1c\x0e\x9e\x1f\x87}C\x98\xa6\xaa\xaa\xd24U\x87[k\x8aܔZ\x1d\x00\xe6N\x1e\x04\xc0\xa2\xf8\xd4\xfa\xd6\xf2K\x84\x01\x16C\x1e\x04\xc0\x02\xbb\xc1<\xcf\xd5ap\x86\x83\x98\v\xc1\xe5\x8d?+\x02\xc0bȃ\x00\xd0\r\xa2\xaa,\x8a\x11\xa1[\xb0s\x10\xc0\xc2ȃ\x00X\xa0,ˌ\b\r\xcb{\"\xcc˗/_\xea\xbaV\x87\xa14Ŕ\b\x03,\x8c<\b\x80\xc5v\x83\x8a0 \xef\x890/EQ\xbc\xbe\xbe\xaa\xc3P\x9ab\x1a\x0e\x02X\x18y\x10\x00˔e\xd9\xf9|V\x87A\xbc\xbc\xbch\x05\x99\x9d/_\xbe\x94e\xa9\x0e\xe1\x9a2J\xd8\x01\x96G\x1e\x04\xc0b\x19i\x19\xaa\x154g\xc1L\x1d\x8fGEPF\x00\xde%\x0f\x02`\xb1\x8a\xa2\xf8\u05ff\xfe\xa5\x0e\xe1\xad`UU\xea\xc0\x1ceY\xe6!\x10\xa8)\xa0\xbd\xc3\x00\x16I\x1e\x04\xc0\x92\xd9SV+\x88\x87\x80\xb7\xc6z\xf3\xa6\x18\xc0\x82Ƀ\x00X\xb2\xaa\xaa\xbc5\xa6\x15$\xf2\x87\x80םz3\x1e\b\xb0`\xf2 \x00\x16.I\x12g\xcfk\x05\x89\x99\xb7\xc6\xfa1\x1e\b\xb0l\xf2 \x00\x96ψP\x0f///ZA\x96\xf4\x10\xb8^\xaf\xea\xd0^S.ON\x80e\x93\a\x01\xb0|\x97\xcb\xe5\xe5\xe5E\x1d\xda\xcb\xf3ܛb,\xcc~\xbf\xb7\x9bXKM\xa1\x9ar\xa9\x03\xc0\xb2Ƀ\x00\x88\u0097/_L\ah\x05\x89YQ\x14n얚B5\xe5R\a\x80e\x93\a\x01\x10Q\x87c:\xa0\x8d\xddng\xdb \x16)˲\xbf\xff\xfd\xef\xea\xf0\xb1\xa6D\xde\x15\x05\x88\x81<\b\x80X\x14Ea;\x8c6\xad\xe0\xe5rQ\a\x96*I\x92\xd3\xe9\xa4\x0e?\xd2\x14\xa7)\x91:\x00\xc4@\x1e\x04\x80V\x90߽\xbc\xbch\x05Y\xbc\xe3\xf1\xe89\xf0\xae\xa6,Mq\xd4\x01 \x12\xf2 \x00\xa2k\x05m$\xf4\xa3V\xd0\x1e\xd2\xc4\xf3\x1c\xc8\xf3\\\x1d\xbe\xd5\x14D\x18\x04\x10\x15y\x10\x00\xd1\xd9\xedv6\x12\xfa\x8e\xb9\x00b\xb3\xdf\xefE\xc3_5\xa5\xb0\xd96@l\xe4A\x00D\xa7\xaa*\x91з\x84AD\xfb\x1c\x10\t}\xfa-\f\xb2\x8b<@\x84\xe4A\x00\xc4\xe8r\xb9H@\xde\b\x83\x88\xd6[$\x14\xf9\x8bcͷ/\f\x02\x88\x93<\b\x80H\xa5i\xea\xe4ia\x10\x91{\x8b\x84\xa2\xdd^\xba\xf9ƅA\x00ђ\a\x01\x10\xaf$Ib\x8e\x84^^^\x84A\xf0)\xd6\x13\xc7\xc4\xc1\x00\x91\x93\a\x01\x10\xb5h#\xa1\xe6\xbbv\x9a\x18|u<\x1e\xa3z\x144߬0\b r\xff\xa5\x04\x00D.I\x92\xe6\xaf\xff\xfe\xf7\xbf#\xf9~\xeb\xba\xde\xedv\x97\xcbť\x87\xef\x1e\x05EQ\xa4i\xbaZ\xad\x96\xfd\x04\xd8\xef\xf7Y\x96\xb9\xe2\x00\x913\x1f\x04\x00\xff\xe9\x03\xff\xf6\xb7\xbf\xc5p\xe2X\x9e\xe7\xdb\xedV\x18\x04\xefʲ\xec\xf1\xf1q\xc1\x87\x8e5\xdfZ\xf3\r\n\x83\x00h\xfc\xa4\x040M\xc7\xe3\xb1\xe9\xd9\xd4a\x90>\xbf(\nu\xa0\x8d\xb76i\xc1\xa3\x01///\xde\x11\x83?\xb5^\xaf___\x0f\x87\xc3¾\xaf\xd3\xe9\xf4\xfc\xfcl\xf7hn\xad\xf9\r\xd6ۈ\x83\xf0#\x1b\x00`\xd4>\xf0r\xb9\xfc\xf7\xe2\x14E\xf1\xf8\xf8\xe8\xfaB{\xfb\xfd\xbe\xaa\xaae<\x01\x9ao\xa4\xf9v\\S\x00\x00\x80\x8f\xbc\xbe\xbe.)\f\xfa\xf2\xe5\xcbz\xbdvY\xa1\xabf\xe1\xa4i:\xf7'@\xf3-x\x02\x00\x00\x00\xb4\xf2\xb6\xe3\xf2\xdc\xfb\xc0,˼y\n\xe1O\x83\xa2(f:\x18\xd8|\xf1\xae \x00\x00@7_\xbe|\x99\xe9\v#Y\x96\xe9\x03a@o;\xef\xcc\xe8\x05\xb1\xe6\vv\xd5\x00\x00\x00zZ\xaf\xd7\xf3J\x85$A\x10\xf3Ӡ\xf9\xf2\xbc\"\n\x00\x000X\x1f\xf8\xfc\xfc<\xf1wF\x92$\x91\x04\xc1\bO\x83\xe3\xf18\xc1\xa7\xc1\xe5rq\xa8\x13\x00\x00\xc0M\xec\xf7\xfb$I&5 \xd04\x81\xcf\xcf\xcf\xc6\x01`d\xbb\xddn\nO\x83\xe6\vh\xbe\f\a\b\x02\x00\x00\x8c\xe1-\x18\xba\xe3\x8c@\x9a\xa6\xcf\xcf϶\x8b\x86\x89<\rF\x0e\x86\x9a\x87O\xf3\x1fu\x8a<\x00\xbd\xfd\xa4\x04\x00\x10\xe2\xf17\xbbݮ\xf9\xeb\xc3\xc3\xc3\xed\xfeCu]_.\x97\xec\u007f\xa9 "), - } - - // define dirs - dirb := &embedded.EmbeddedDir{ - Filename: "", - DirModTime: time.Unix(1611536125, 0), - ChildFiles: []*embedded.EmbeddedFile{ - filec, // ".gitkeep" - filed, // "favicon.png" - filee, // "logo.svg" - - }, - } - - // link ChildDirs - dirb.ChildDirs = []*embedded.EmbeddedDir{} - - // register embeddedBox - embedded.RegisterEmbeddedBox(`static/img`, &embedded.EmbeddedBox{ - Name: `static/img`, - Time: time.Unix(1611536125, 0), - Dirs: map[string]*embedded.EmbeddedDir{ - "": dirb, - }, - Files: map[string]*embedded.EmbeddedFile{ - ".gitkeep": filec, - "favicon.png": filed, - "logo.svg": filee, - }, - }) -} - -func init() { - - // define files - fileg := &embedded.EmbeddedFile{ - Filename: ".gitkeep", - FileModTime: time.Unix(1609727320, 0), - - Content: string(""), - } - fileh := &embedded.EmbeddedFile{ - Filename: "01-umbrella.js", - FileModTime: time.Unix(1611561370, 0), - - Content: string("// Umbrella JS http://umbrellajs.com/\n// -----------\n// Small, lightweight jQuery alternative\n// @author Francisco Presencia Fandos https://francisco.io/\n// @inspiration http://youmightnotneedjquery.com/\n\n// Initialize the library\nvar u = function(parameter, context) {\n // Make it an instance of u() to avoid needing 'new' as in 'new u()' and just\n // use 'u().bla();'.\n // @reference http://stackoverflow.com/q/24019863\n // @reference http://stackoverflow.com/q/8875878\n if (!(this instanceof u)) {\n return new u(parameter, context);\n }\n\n // No need to further processing it if it's already an instance\n if (parameter instanceof u) {\n return parameter;\n }\n\n // Parse it as a CSS selector if it's a string\n if (typeof parameter === 'string') {\n parameter = this.select(parameter, context);\n }\n\n // If we're referring a specific node as in on('click', function(){ u(this) })\n // or the select() function returned a single node such as in '#id'\n if (parameter && parameter.nodeName) {\n parameter = [parameter];\n }\n\n // Convert to an array, since there are many 'array-like' stuff in js-land\n this.nodes = this.slice(parameter);\n};\n\n// Map u(...).length to u(...).nodes.length\nu.prototype = {\n get length() {\n return this.nodes.length;\n }\n};\n\n// This made the code faster, read \"Initializing instance variables\" in\n// https://developers.google.com/speed/articles/optimizing-javascript\nu.prototype.nodes = [];\n\n// Add class(es) to the matched nodes\nu.prototype.addClass = function() {\n return this.eacharg(arguments, function(el, name) {\n el.classList.add(name);\n });\n};\n\n\n// [INTERNAL USE ONLY]\n// Add text in the specified position. It is used by other functions\nu.prototype.adjacent = function(html, data, callback) {\n if (typeof data === 'number') {\n if (data === 0) {\n data = [];\n } else {\n data = new Array(data).join().split(',').map(Number.call, Number);\n }\n }\n\n // Loop through all the nodes. It cannot reuse the eacharg() since the data\n // we want to do it once even if there's no \"data\" and we accept a selector\n return this.each(function(node, j) {\n var fragment = document.createDocumentFragment();\n\n // Allow for data to be falsy and still loop once\n u(data || {}).map(function(el, i) {\n // Allow for callbacks that accept some data\n var part = (typeof html === 'function') ? html.call(this, el, i, node, j) : html;\n\n if (typeof part === 'string') {\n return this.generate(part);\n }\n\n return u(part);\n }).each(function(n) {\n this.isInPage(n) ?\n fragment.appendChild(u(n).clone().first()) :\n fragment.appendChild(n);\n });\n\n callback.call(this, node, fragment);\n });\n};\n\n// Add some html as a sibling after each of the matched elements.\nu.prototype.after = function(html, data) {\n return this.adjacent(html, data, function(node, fragment) {\n node.parentNode.insertBefore(fragment, node.nextSibling);\n });\n};\n\n\n// Add some html as a child at the end of each of the matched elements.\nu.prototype.append = function(html, data) {\n return this.adjacent(html, data, function(node, fragment) {\n node.appendChild(fragment);\n });\n};\n\n\n// [INTERNAL USE ONLY]\n\n// Normalize the arguments to an array of strings\n// Allow for several class names like \"a b, c\" and several parameters\nu.prototype.args = function(args, node, i) {\n if (typeof args === 'function') {\n args = args(node, i);\n }\n\n // First flatten it all to a string http://stackoverflow.com/q/22920305\n // If we try to slice a string bad things happen: ['n', 'a', 'm', 'e']\n if (typeof args !== 'string') {\n args = this.slice(args).map(this.str(node, i));\n }\n\n // Then convert that string to an array of not-null strings\n return args.toString().split(/[\\s,]+/).filter(function(e) {\n return e.length;\n });\n};\n\n\n// Merge all of the nodes that the callback return into a simple array\nu.prototype.array = function(callback) {\n callback = callback;\n var self = this;\n return this.nodes.reduce(function(list, node, i) {\n var val;\n if (callback) {\n val = callback.call(self, node, i);\n if (!val) val = false;\n if (typeof val === 'string') val = u(val);\n if (val instanceof u) val = val.nodes;\n } else {\n val = node.innerHTML;\n }\n return list.concat(val !== false ? val : []);\n }, []);\n};\n\n\n// [INTERNAL USE ONLY]\n\n// Handle attributes for the matched elements\nu.prototype.attr = function(name, value, data) {\n data = data ? 'data-' : '';\n\n // This will handle those elements that can accept a pair with these footprints:\n // .attr('a'), .attr('a', 'b'), .attr({ a: 'b' })\n return this.pairs(name, value, function(node, name) {\n return node.getAttribute(data + name);\n }, function(node, name, value) {\n node.setAttribute(data + name, value);\n });\n};\n\n\n// Add some html before each of the matched elements.\nu.prototype.before = function(html, data) {\n return this.adjacent(html, data, function(node, fragment) {\n node.parentNode.insertBefore(fragment, node);\n });\n};\n\n\n// Get the direct children of all of the nodes with an optional filter\nu.prototype.children = function(selector) {\n return this.map(function(node) {\n return this.slice(node.children);\n }).filter(selector);\n};\n\n\n/**\n * Deep clone a DOM node and its descendants.\n * @return {[Object]} Returns an Umbrella.js instance.\n */\nu.prototype.clone = function() {\n return this.map(function(node, i) {\n var clone = node.cloneNode(true);\n var dest = this.getAll(clone);\n\n this.getAll(node).each(function(src, i) {\n for (var key in this.mirror) {\n if (this.mirror[key]) {\n this.mirror[key](src, dest.nodes[i]);\n }\n }\n });\n\n return clone;\n });\n};\n\n/**\n * Return an array of DOM nodes of a source node and its children.\n * @param {[Object]} context DOM node.\n * @param {[String]} tag DOM node tagName.\n * @return {[Array]} Array containing queried DOM nodes.\n */\nu.prototype.getAll = function getAll(context) {\n return u([context].concat(u('*', context).nodes));\n};\n\n// Store all of the operations to perform when cloning elements\nu.prototype.mirror = {};\n\n/**\n * Copy all JavaScript events of source node to destination node.\n * @param {[Object]} source DOM node\n * @param {[Object]} destination DOM node\n * @return {[undefined]]}\n */\nu.prototype.mirror.events = function(src, dest) {\n if (!src._e) return;\n\n for (var type in src._e) {\n src._e[type].forEach(function(ref) {\n u(dest).on(type, ref.callback);\n });\n }\n};\n\n/**\n * Copy select input value to its clone.\n * @param {[Object]} src DOM node\n * @param {[Object]} dest DOM node\n * @return {[undefined]}\n */\nu.prototype.mirror.select = function(src, dest) {\n if (u(src).is('select')) {\n dest.value = src.value;\n }\n};\n\n/**\n * Copy textarea input value to its clone\n * @param {[Object]} src DOM node\n * @param {[Object]} dest DOM node\n * @return {[undefined]}\n */\nu.prototype.mirror.textarea = function(src, dest) {\n if (u(src).is('textarea')) {\n dest.value = src.value;\n }\n};\n\n\n// Find the first ancestor that matches the selector for each node\nu.prototype.closest = function(selector) {\n return this.map(function(node) {\n // Keep going up and up on the tree. First element is also checked\n do {\n if (u(node).is(selector)) {\n return node;\n }\n } while ((node = node.parentNode) && node !== document);\n });\n};\n\n\n// Handle data-* attributes for the matched elements\nu.prototype.data = function(name, value) {\n return this.attr(name, value, true);\n};\n\n\n// Loops through every node from the current call\nu.prototype.each = function(callback) {\n // By doing callback.call we allow \"this\" to be the context for\n // the callback (see http://stackoverflow.com/q/4065353 precisely)\n this.nodes.forEach(callback.bind(this));\n\n return this;\n};\n\n\n// [INTERNAL USE ONLY]\n// Loop through the combination of every node and every argument passed\nu.prototype.eacharg = function(args, callback) {\n return this.each(function(node, i) {\n this.args(args, node, i).forEach(function(arg) {\n // Perform the callback for this node\n // By doing callback.call we allow \"this\" to be the context for\n // the callback (see http://stackoverflow.com/q/4065353 precisely)\n callback.call(this, node, arg);\n }, this);\n });\n};\n\n\n// Remove all children of the matched nodes from the DOM.\nu.prototype.empty = function() {\n return this.each(function(node) {\n while (node.firstChild) {\n node.removeChild(node.firstChild);\n }\n });\n};\n\n\n// .filter(selector)\n// Delete all of the nodes that don't pass the selector\nu.prototype.filter = function(selector) {\n // The default function if it's a CSS selector\n // Cannot change name to 'selector' since it'd mess with it inside this fn\n var callback = function(node) {\n // Make it compatible with some other browsers\n node.matches = node.matches || node.msMatchesSelector || node.webkitMatchesSelector;\n\n // Check if it's the same element (or any element if no selector was passed)\n return node.matches(selector || '*');\n };\n\n // filter() receives a function as in .filter(e => u(e).children().length)\n if (typeof selector === 'function') callback = selector;\n\n // filter() receives an instance of Umbrella as in .filter(u('a'))\n if (selector instanceof u) {\n callback = function(node) {\n return (selector.nodes).indexOf(node) !== -1;\n };\n }\n\n // Just a native filtering function for ultra-speed\n return u(this.nodes.filter(callback));\n};\n\n\n// Find all the nodes children of the current ones matched by a selector\nu.prototype.find = function(selector) {\n return this.map(function(node) {\n return u(selector || '*', node);\n });\n};\n\n\n// Get the first of the nodes\nu.prototype.first = function() {\n return this.nodes[0] || false;\n};\n\n\n// [INTERNAL USE ONLY]\n// Generate a fragment of HTML. This irons out the inconsistences\nu.prototype.generate = function(html) {\n // Table elements need to be child of for some f***ed up reason\n if (/^\\s*
]/.test(html)) {\n return u(document.createElement('table')).html(html).children().children().nodes;\n } else if (/^\\s* ]/.test(html)) {\n return u(document.createElement('table')).html(html).children().children().children().nodes;\n } else if (/^\\s* 0;\n};\n\n\n/**\n * Internal use only. This function checks to see if an element is in the page's body. As contains is inclusive and determining if the body contains itself isn't the intention of isInPage this case explicitly returns false.\nhttps://developer.mozilla.org/en-US/docs/Web/API/Node/contains\n * @param {[Object]} node DOM node\n * @return {Boolean} The Node.contains() method returns a Boolean value indicating whether a node is a descendant of a given node or not.\n */\nu.prototype.isInPage = function isInPage(node) {\n return (node === document.body) ? false : document.body.contains(node);\n};\n\n// Get the last of the nodes\nu.prototype.last = function() {\n return this.nodes[this.length - 1] || false;\n};\n\n\n// Merge all of the nodes that the callback returns\nu.prototype.map = function(callback) {\n return callback ? u(this.array(callback)).unique() : this;\n};\n\n\n// Delete all of the nodes that equals the filter\nu.prototype.not = function(filter) {\n return this.filter(function(node) {\n return !u(node).is(filter || true);\n });\n};\n\n\n// Removes the callback to the event listener for each node\nu.prototype.off = function(events, cb, cb2) {\n var cb_filter_off = (cb == null && cb2 == null);\n var sel = null;\n var cb_to_be_removed = cb;\n if (typeof cb === 'string') {\n sel = cb;\n cb_to_be_removed = cb2;\n }\n\n return this.eacharg(events, function(node, event) {\n u(node._e ? node._e[event] : []).each(function(ref) {\n if (cb_filter_off || (ref.orig_callback === cb_to_be_removed && ref.selector === sel)) {\n node.removeEventListener(event, ref.callback);\n }\n });\n });\n};\n\n\n// Attach a callback to the specified events\nu.prototype.on = function(events, cb, cb2) {\n var sel = null;\n var orig_callback = cb;\n if (typeof cb === 'string') {\n sel = cb;\n orig_callback = cb2;\n cb = function(e) {\n var args = arguments;\n var targetFound = false;\n u(e.currentTarget)\n .find(sel)\n .each(function(target) {\n if (target === e.target || target.contains(e.target)) {\n targetFound = true;\n try {\n Object.defineProperty(e, 'currentTarget', {\n get: function() {\n return target;\n }\n });\n } catch (err) {}\n cb2.apply(target, args);\n }\n });\n // due to e.currentEvent reassigning a second (or subsequent) handler may\n // not be fired for a single event, so chekc and apply if needed.\n if (!targetFound && e.currentTarget === e.target) {\n cb2.apply(e.target, args);\n }\n };\n }\n\n // Add the custom data as arguments to the callback\n var callback = function(e) {\n return cb.apply(this, [e].concat(e.detail || []));\n };\n\n return this.eacharg(events, function(node, event) {\n node.addEventListener(event, callback);\n\n // Store it so we can dereference it with `.off()` later on\n node._e = node._e || {};\n node._e[event] = node._e[event] || [];\n node._e[event].push({\n callback: callback,\n orig_callback: orig_callback,\n selector: sel\n });\n });\n};\n\n\n// [INTERNAL USE ONLY]\n\n// Take the arguments and a couple of callback to handle the getter/setter pairs\n// such as: .css('a'), .css('a', 'b'), .css({ a: 'b' })\nu.prototype.pairs = function(name, value, get, set) {\n // Convert it into a plain object if it is not\n if (typeof value !== 'undefined') {\n var nm = name;\n name = {};\n name[nm] = value;\n }\n\n if (typeof name === 'object') {\n // Set the value of each one, for each of the { prop: value } pairs\n return this.each(function(node) {\n for (var key in name) {\n set(node, key, name[key]);\n }\n });\n }\n\n // Return the style of the first one\n return this.length ? get(this.first(), name) : '';\n};\n\n// [INTERNAL USE ONLY]\n\n// Parametize an object: { a: 'b', c: 'd' } => 'a=b&c=d'\nu.prototype.param = function(obj) {\n return Object.keys(obj).map(function(key) {\n return this.uri(key) + '=' + this.uri(obj[key]);\n }.bind(this)).join('&');\n};\n\n// Travel the matched elements one node up\nu.prototype.parent = function(selector) {\n return this.map(function(node) {\n return node.parentNode;\n }).filter(selector);\n};\n\n\n// Add nodes at the beginning of each node\nu.prototype.prepend = function(html, data) {\n return this.adjacent(html, data, function(node, fragment) {\n node.insertBefore(fragment, node.firstChild);\n });\n};\n\n\n// Delete the matched nodes from the DOM\nu.prototype.remove = function() {\n // Loop through all the nodes\n return this.each(function(node) {\n // Perform the removal only if the node has a parent\n if (node.parentNode) {\n node.parentNode.removeChild(node);\n }\n });\n};\n\n\n// Removes a class from all of the matched nodes\nu.prototype.removeClass = function() {\n // Loop the combination of each node with each argument\n return this.eacharg(arguments, function(el, name) {\n // Remove the class using the native method\n el.classList.remove(name);\n });\n};\n\n\n// Replace the matched elements with the passed argument.\nu.prototype.replace = function(html, data) {\n var nodes = [];\n this.adjacent(html, data, function(node, fragment) {\n nodes = nodes.concat(this.slice(fragment.children));\n node.parentNode.replaceChild(fragment, node);\n });\n return u(nodes);\n};\n\n\n// Scroll to the first matched element\nu.prototype.scroll = function() {\n this.first().scrollIntoView({\n behavior: 'smooth'\n });\n return this;\n};\n\n\n// [INTERNAL USE ONLY]\n// Select the adequate part from the context\nu.prototype.select = function(parameter, context) {\n // Allow for spaces before or after\n parameter = parameter.replace(/^\\s*/, '').replace(/\\s*$/, '');\n\n if (/^'),\n // 2) clone the currently matched node\n // 3) append cloned dom node to constructed node based on selector\n return this.map(function(node) {\n return u(selector).each(function(n) {\n findDeepestNode(n)\n .append(node.cloneNode(true));\n\n node\n .parentNode\n .replaceChild(n, node);\n });\n });\n};\n\n// Export it for webpack\nif (typeof module === 'object' && module.exports) {\n // Avoid breaking it for `import { u } from ...`. Add `import u from ...`\n module.exports = u;\n module.exports.u = u;\n}\n"), - } - filei := &embedded.EmbeddedFile{ - Filename: "02-polyfill.js", - FileModTime: time.Unix(1609727320, 0), - - Content: string("(function () {\n\nif (typeof window.Element === \"undefined\" || \"classList\" in document.documentElement) return;\n\nvar prototype = Array.prototype,\n push = prototype.push,\n splice = prototype.splice,\n join = prototype.join;\n\nfunction DOMTokenList(el) {\n this.el = el;\n // The className needs to be trimmed and split on whitespace\n // to retrieve a list of classes.\n var classes = el.className.replace(/^\\s+|\\s+$/g,'').split(/\\s+/);\n for (var i = 0; i < classes.length; i++) {\n push.call(this, classes[i]);\n }\n};\n\nDOMTokenList.prototype = {\n add: function(token) {\n if(this.contains(token)) return;\n push.call(this, token);\n this.el.className = this.toString();\n },\n contains: function(token) {\n return this.el.className.indexOf(token) != -1;\n },\n item: function(index) {\n return this[index] || null;\n },\n remove: function(token) {\n if (!this.contains(token)) return;\n for (var i = 0; i < this.length; i++) {\n if (this[i] == token) break;\n }\n splice.call(this, i, 1);\n this.el.className = this.toString();\n },\n toString: function() {\n return join.call(this, ' ');\n },\n toggle: function(token) {\n if (!this.contains(token)) {\n this.add(token);\n } else {\n this.remove(token);\n }\n\n return this.contains(token);\n }\n};\n\nwindow.DOMTokenList = DOMTokenList;\n\nfunction defineElementGetter (obj, prop, getter) {\n if (Object.defineProperty) {\n Object.defineProperty(obj, prop,{\n get : getter\n });\n } else {\n obj.__defineGetter__(prop, getter);\n }\n}\n\ndefineElementGetter(Element.prototype, 'classList', function () {\n return new DOMTokenList(this);\n});\n\n})();\n"), - } - filej := &embedded.EmbeddedFile{ - Filename: "03-twix.js", - FileModTime: time.Unix(1609727320, 0), - - Content: string("/*\n * Twix v1.0 - a lightweight library for making AJAX requests.\n * Author: Neil Cowburn (neilco@gmail.com)\n * \n * Copyright (c) 2013 Neil Cowburn (http://github.com/neilco/)\n * \n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\nvar Twix = (function () {\n function Twix() { }\n \n Twix.ajax = function(options) {\n options = options || {url:\"\"};\n options.type = options.type.toUpperCase() || 'GET';\n options.headers = options.headers || {};\n options.timeout = parseInt(options.timeout) || 0;\n options.success = options.success || function() {};\n options.error = options.error || function() {};\n options.async = typeof options.async === 'undefined' ? true : options.async;\n\n var client = new XMLHttpRequest();\n if (options.timeout > 0) {\n client.timeout = options.timeout;\n client.ontimeout = function () { \n options.error('timeout', 'timeout', client); \n };\n }\n client.open(options.type, options.url, options.async);\n\n for (var i in options.headers) {\n if (options.headers.hasOwnProperty(i)) {\n client.setRequestHeader(i, options.headers[i]);\n }\n }\n \n client.send(options.data);\n client.onreadystatechange = function() {\n if (this.readyState == 4 && ((this.status >= 200 && this.status < 300) || this.status == 304)) {\n var data = this.responseText;\n var contentType = this.getResponseHeader('Content-Type');\n if (contentType && contentType.match(/json/)) {\n data = JSON.parse(this.responseText);\n }\n options.success(data, this.statusText, this);\n } else if (this.readyState == 4) {\n options.error(this.status, this.statusText, this);\n }\n };\n\n if (options.async == false) {\n if (client.readyState == 4 && ((client.status >= 200 && client.status < 300) || client.status == 304)) {\n options.success(client.responseText, client);\n } else if (client.readyState == 4) {\n options.error(client.status, client.statusText, client);\n }\n } \n\n return client;\n };\n\n var _ajax = function(type, url, data, callback) {\n if (typeof data === \"function\") {\n callback = data;\n data = undefined;\n }\n return Twix.ajax({\n url: url,\n data: data, \n type: type,\n success: callback\n });\n };\n\n Twix.get = function(url, data, callback) {\n return _ajax(\"GET\", url, data, callback);\n };\n\n Twix.head = function(url, data, callback) {\n return _ajax(\"HEAD\", url, data, callback);\n };\n\n Twix.post = function(url, data, callback) {\n return _ajax(\"POST\", url, data, callback);\n };\n\n Twix.patch = function(url, data, callback) {\n return _ajax(\"PATCH\", url, data, callback);\n };\n\n Twix.put = function(url, data, callback) {\n return _ajax(\"PUT\", url, data, callback);\n };\n\n Twix.delete = function(url, data, callback) {\n return _ajax(\"DELETE\", url, data, callback);\n };\n\n Twix.options = function(url, data, callback) {\n return _ajax(\"OPTIONS\", url, data, callback);\n };\n \n \n return Twix;\n})();\n\n__ = Twix;\n"), - } - filek := &embedded.EmbeddedFile{ - Filename: "99-spyda.js", - FileModTime: time.Unix(1611979072, 0), - - Content: string("// Creare's 'Implied Consent' EU Cookie Law Banner v:2.4\n// Conceived by Robert Kent, James Bavington & Tom Foyster\n\nvar dropCookie = true; // false disables the Cookie, allowing you to style the banner\nvar cookieDuration = 14; // Number of days before the cookie expires, and the banner reappears\nvar cookieName = \"complianceCookie\"; // Name of our cookie\nvar cookieValue = \"on\"; // Value of cookie\n\nfunction createDiv() {\n u(\"body\").prepend(\n ''\n );\n createCookie(window.cookieName, window.cookieValue, window.cookieDuration); // Create the cookie\n}\n\nfunction createCookie(name, value, days) {\n if (days) {\n var date = new Date();\n date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);\n var expires = \"; expires=\" + date.toGMTString();\n } else var expires = \"\";\n if (window.dropCookie) {\n document.cookie = name + \"=\" + value + expires + \"; path=/\";\n }\n}\n\nfunction checkCookie(name) {\n var nameEQ = name + \"=\";\n var ca = document.cookie.split(\";\");\n for (var i = 0; i < ca.length; i++) {\n var c = ca[i];\n while (c.charAt(0) == \" \") c = c.substring(1, c.length);\n if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);\n }\n return null;\n}\n\nfunction eraseCookie(name) {\n createCookie(name, \"\", -1);\n}\n\nwindow.onload = function() {\n if (checkCookie(window.cookieName) != window.cookieValue) {\n createDiv();\n }\n};\n\nfunction removeMe() {\n var element = document.getElementById(\"cookie-law\");\n element.parentNode.removeChild(element);\n}\n\nu(\"#burgerMenu\").on(\"click\", function(e) {\n e.preventDefault();\n\n if (u(\"#mainNav\").hasClass(\"responsive\")) {\n u(\"#mainNav\").removeClass(\"responsive\");\n } else {\n u(\"#mainNav\").addClass(\"responsive\");\n }\n});\n"), - } - filel := &embedded.EmbeddedFile{ - Filename: "spyda.min.js", - FileModTime: time.Unix(1611979522, 0), - - Content: string("var u=function(a,b){if(!(this instanceof u))return new u(a,b);if(a instanceof u)return a;typeof a=='string'&&(a=this.select(a,b)),a&&a.nodeName&&(a=[a]),this.nodes=this.slice(a)},Twix,dropCookie,cookieDuration,cookieName,cookieValue;u.prototype={get length(){return this.nodes.length}},u.prototype.nodes=[],u.prototype.addClass=function(){return this.eacharg(arguments,function(a,b){a.classList.add(b)})},u.prototype.adjacent=function(b,a,c){return typeof a=='number'&&(a===0?a=[]:a=new Array(a).join().split(',').map(Number.call,Number)),this.each(function(e,f){var d=document.createDocumentFragment();u(a||{}).map(function(c,d){var a=typeof b=='function'?b.call(this,c,d,e,f):b;return typeof a=='string'?this.generate(a):u(a)}).each(function(a){this.isInPage(a)?d.appendChild(u(a).clone().first()):d.appendChild(a)}),c.call(this,e,d)})},u.prototype.after=function(a,b){return this.adjacent(a,b,function(a,b){a.parentNode.insertBefore(b,a.nextSibling)})},u.prototype.append=function(a,b){return this.adjacent(a,b,function(a,b){a.appendChild(b)})},u.prototype.args=function(a,b,c){return typeof a=='function'&&(a=a(b,c)),typeof a!='string'&&(a=this.slice(a).map(this.str(b,c))),a.toString().split(/[\\s,]+/).filter(function(a){return a.length})},u.prototype.array=function(a){a=a;var b=this;return this.nodes.reduce(function(e,d,f){var c;return a?(c=a.call(b,d,f),c||(c=!1),typeof c=='string'&&(c=u(c)),c instanceof u&&(c=c.nodes)):c=d.innerHTML,e.concat(c!==!1?c:[])},[])},u.prototype.attr=function(b,c,a){return a=a?'data-':'',this.pairs(b,c,function(b,c){return b.getAttribute(a+c)},function(b,c,d){b.setAttribute(a+c,d)})},u.prototype.before=function(a,b){return this.adjacent(a,b,function(a,b){a.parentNode.insertBefore(b,a)})},u.prototype.children=function(a){return this.map(function(a){return this.slice(a.children)}).filter(a)},u.prototype.clone=function(){return this.map(function(a,d){var b=a.cloneNode(!0),c=this.getAll(b);return this.getAll(a).each(function(b,d){for(var a in this.mirror)this.mirror[a]&&this.mirror[a](b,c.nodes[d])}),b})},u.prototype.getAll=function(a){return u([a].concat(u('*',a).nodes))},u.prototype.mirror={},u.prototype.mirror.events=function(a,c){if(!a._e)return;for(var b in a._e)a._e[b].forEach(function(a){u(c).on(b,a.callback)})},u.prototype.mirror.select=function(a,b){u(a).is('select')&&(b.value=a.value)},u.prototype.mirror.textarea=function(a,b){u(a).is('textarea')&&(b.value=a.value)},u.prototype.closest=function(a){return this.map(function(b){do if(u(b).is(a))return b;while((b=b.parentNode)&&b!==document)})},u.prototype.data=function(a,b){return this.attr(a,b,!0)},u.prototype.each=function(a){return this.nodes.forEach(a.bind(this)),this},u.prototype.eacharg=function(a,b){return this.each(function(c,d){this.args(a,c,d).forEach(function(a){b.call(this,c,a)},this)})},u.prototype.empty=function(){return this.each(function(a){while(a.firstChild)a.removeChild(a.firstChild)})},u.prototype.filter=function(a){var b=function(b){return b.matches=b.matches||b.msMatchesSelector||b.webkitMatchesSelector,b.matches(a||'*')};return typeof a=='function'&&(b=a),a instanceof u&&(b=function(b){return a.nodes.indexOf(b)!==-1}),u(this.nodes.filter(b))},u.prototype.find=function(a){return this.map(function(b){return u(a||'*',b)})},u.prototype.first=function(){return this.nodes[0]||!1},u.prototype.generate=function(a){return/^\\s* ]/.test(a)?u(document.createElement('table')).html(a).children().children().nodes:/^\\s* ]/.test(a)?u(document.createElement('table')).html(a).children().children().children().nodes:/^\\s*0},u.prototype.isInPage=function(a){return!(a===document.body)&&document.body.contains(a)},u.prototype.last=function(){return this.nodes[this.length-1]||!1},u.prototype.map=function(a){return a?u(this.array(a)).unique():this},u.prototype.not=function(a){return this.filter(function(b){return!u(b).is(a||!0)})},u.prototype.off=function(e,a,d){var f=a==null&&d==null,b=null,c=a;return typeof a=='string'&&(b=a,c=d),this.eacharg(e,function(a,d){u(a._e?a._e[d]:[]).each(function(e){(f||e.orig_callback===c&&e.selector===b)&&a.removeEventListener(d,e.callback)})})},u.prototype.on=function(f,a,b){var c=null,d=a,e;return typeof a=='string'&&(c=a,d=b,a=function(a){var e=arguments,d=!1;u(a.currentTarget).find(c).each(function(c){if(c===a.target||c.contains(a.target)){d=!0;try{Object.defineProperty(a,'currentTarget',{get:function(){return c}})}catch(a){}b.apply(c,e)}}),!d&&a.currentTarget===a.target&&b.apply(a.target,e)}),e=function(b){return a.apply(this,[b].concat(b.detail||[]))},this.eacharg(f,function(a,b){a.addEventListener(b,e),a._e=a._e||{},a._e[b]=a._e[b]||[],a._e[b].push({callback:e,orig_callback:d,selector:c})})},u.prototype.pairs=function(a,b,c,d){if(typeof b!='undefined'){var e=a;a={},a[e]=b}return typeof a=='object'?this.each(function(c){for(var b in a)d(c,b,a[b])}):this.length?c(this.first(),a):''},u.prototype.param=function(a){return Object.keys(a).map(function(b){return this.uri(b)+'='+this.uri(a[b])}.bind(this)).join('&')},u.prototype.parent=function(a){return this.map(function(a){return a.parentNode}).filter(a)},u.prototype.prepend=function(a,b){return this.adjacent(a,b,function(a,b){a.insertBefore(b,a.firstChild)})},u.prototype.remove=function(){return this.each(function(a){a.parentNode&&a.parentNode.removeChild(a)})},u.prototype.removeClass=function(){return this.eacharg(arguments,function(a,b){a.classList.remove(b)})},u.prototype.replace=function(b,c){var a=[];return this.adjacent(b,c,function(b,c){a=a.concat(this.slice(c.children)),b.parentNode.replaceChild(c,b)}),u(a)},u.prototype.scroll=function(){return this.first().scrollIntoView({behavior:'smooth'}),this},u.prototype.select=function(a,b){return(a=a.replace(/^\\s*/,'').replace(/\\s*$/,''),/^0&&(b.timeout=a.timeout,b.ontimeout=function(){a.error('timeout','timeout',b)}),b.open(a.type,a.url,a.async);for(c in a.headers)a.headers.hasOwnProperty(c)&&b.setRequestHeader(c,a.headers[c]);return b.send(a.data),b.onreadystatechange=function(){var b,c;this.readyState==4&&(this.status>=200&&this.status<300||this.status==304)?(b=this.responseText,c=this.getResponseHeader('Content-Type'),c&&c.match(/json/)&&(b=JSON.parse(this.responseText)),a.success(b,this.statusText,this)):this.readyState==4&&a.error(this.status,this.statusText,this)},a.async==!1&&(b.readyState==4&&(b.status>=200&&b.status<300||b.status==304)?a.success(b.responseText,b):b.readyState==4&&a.error(b.status,b.statusText,b)),b};var b=function(d,e,b,c){return typeof b==\"function\"&&(c=b,b=void 0),a.ajax({url:e,data:b,type:d,success:c})};return a.get=function(a,c,d){return b(\"GET\",a,c,d)},a.head=function(a,c,d){return b(\"HEAD\",a,c,d)},a.post=function(a,c,d){return b(\"POST\",a,c,d)},a.patch=function(a,c,d){return b(\"PATCH\",a,c,d)},a.put=function(a,c,d){return b(\"PUT\",a,c,d)},a.delete=function(a,c,d){return b(\"DELETE\",a,c,d)},a.options=function(a,c,d){return b(\"OPTIONS\",a,c,d)},a}(),__=Twix,dropCookie=!0,cookieDuration=14,cookieName=\"complianceCookie\",cookieValue=\"on\";function createDiv(){u(\"body\").prepend(''),createCookie(window.cookieName,window.cookieValue,window.cookieDuration)}function createCookie(d,e,c){var a,b;c?(a=new Date,a.setTime(a.getTime()+c*24*60*60*1e3),b=\"; expires=\"+a.toGMTString()):(b=\"\"),window.dropCookie&&(document.cookie=d+\"=\"+e+b+\"; path=/\")}function checkCookie(e){for(var d=e+\"=\",c=document.cookie.split(\";\"),b=0,a;b \n \n \n404 Not Found
\nOoops! The resource you are looking for is not here!
\n \n \n{{end}}\n"), - } - fileo := &embedded.EmbeddedFile{ - Filename: "_partials.html", - FileModTime: time.Unix(1611978335, 0), - - Content: string("{{ define \"pager\" }}\n{{ if .HasPages }}\n\n{{ end }}\n{{ end }}\n"), - } - filep := &embedded.EmbeddedFile{ - Filename: "base.html", - FileModTime: time.Unix(1611979490, 0), - - Content: string("{{define \"base\"}}\n\n\n \n {{ if $.Debug }}\n \n \n \n \n \n {{ else }}\n \n \n {{ end }}\n\n {{ range .Alternatives }}\n \n {{ end }}\n\n {{ range .Links }}\n \n {{ end }}\n\n \n \n\n{{ .InstanceName }} {{ .Title }} \n {{ with .Meta.Title }}{{ end }}\n {{ with .Meta.Author }}{{ end }}\n {{ with .Meta.Keywords }}{{ end }}\n {{ with .Meta.Description }}{{ end }}\n\n \n {{ with .Meta.Title }}{{ end }}\n {{ with .Meta.Description }}{{ end }}\n {{ with .Meta.UpdatedAt }}{{ end }}\n {{ with .Meta.Image }}{{ end }}\n {{ with .Meta.URL }}{{ end }}\n \n \n\n \n\n {{template \"content\" . }}\n \n \n {{ if $.Debug }}\n \n \n \n \n {{ else }}\n \n {{ end }}\n\n\n{{end}}\n"), - } - fileq := &embedded.EmbeddedFile{ - Filename: "cache.html", - FileModTime: time.Unix(1611066114, 0), - - Content: string("\n\nveri \n\n\n\n\nveri
\nThis is a rudimentary cached copy of {{ .URL }}, go there to get the latest copy. No ownership of this content is implied.
\n{{ if not . }}No cache
{{ end }}\n{{ .Title }}
\n\n{{ .Content }}\n\nIf your page appears here and you do not want it to, please contact ~ols/indexing@lists.sr.ht. Contact the same email if you want your page considered for indexing. Be aware this is a public mailing list.
\n\n\n\n"), - } - filer := &embedded.EmbeddedFile{ - Filename: "error.html", - FileModTime: time.Unix(1609727320, 0), - - Content: string("{{define \"content\"}}\n\n \n \n{{ if .Error }}Error{{ else }}Success {{ end }}
\n{{ .Message }}
\n \n \n{{end}}\n"), - } - files := &embedded.EmbeddedFile{ - Filename: "index.html", - FileModTime: time.Unix(1611068566, 0), - - Content: string("\n\n\nveri \n\n\n\n\n\n \nveri
\n\n \n \n\n\n\n"), - } - filet := &embedded.EmbeddedFile{ - Filename: "search.html", - FileModTime: time.Unix(1611068736, 0), - - Content: string("\n\n\nveri \n\n\n\n\n\n \nveri
\n\n \n These results are not ranked, they are just in alphabetical order of URL (this will change)
\n\n {{ if not . }}
\nNo search results
{{ end }}\n {{ range . }}\n{{ .Title }}
{{ .Summary }}{{ else }} class=\"placeholder\">No summary available{{ end }}
{{ .URL }} {{ .Length }} View cached
\n {{ end }}\nIf your page appears here and you do not want it to, please contact ~ols/indexing@lists.sr.ht. Contact the same email if you want your page considered for indexing. Be aware this is a public mailing list.
\n\n\n\n"), - } - fileu := &embedded.EmbeddedFile{ - Filename: "support.html", - FileModTime: time.Unix(1609727320, 0), - - Content: string("{{define \"content\"}}\n\n \n{{end}}\n"), - } - - // define dirs - dirm := &embedded.EmbeddedDir{ - Filename: "", - DirModTime: time.Unix(1611979490, 0), - ChildFiles: []*embedded.EmbeddedFile{ - filen, // "404.html" - fileo, // "_partials.html" - filep, // "base.html" - fileq, // "cache.html" - filer, // "error.html" - files, // "index.html" - filet, // "search.html" - fileu, // "support.html" - - }, - } - - // link ChildDirs - dirm.ChildDirs = []*embedded.EmbeddedDir{} - - // register embeddedBox - embedded.RegisterEmbeddedBox(`templates`, &embedded.EmbeddedBox{ - Name: `templates`, - Time: time.Unix(1611979490, 0), - Dirs: map[string]*embedded.EmbeddedDir{ - "": dirm, - }, - Files: map[string]*embedded.EmbeddedFile{ - "404.html": filen, - "_partials.html": fileo, - "base.html": filep, - "cache.html": fileq, - "error.html": filer, - "index.html": files, - "search.html": filet, - "support.html": fileu, - }, - }) -} diff --git a/internal/robots.go b/internal/robots.go index 60a2ff2..b14cd7e 100644 --- a/internal/robots.go +++ b/internal/robots.go @@ -11,18 +11,11 @@ import ( const robotsTpl = `User-Agent: * Disallow: / Allow: / -Allow: /twt -Allow: /user -Allow: /feed Allow: /about +Allow: /abuse Allow: /help -Allow: /blogs Allow: /privacy Allow: /support -Allow: /search -Allow: /external -Allow: /atom.xml -Allow: /media ` // RobotsHandler ... diff --git a/internal/server.go b/internal/server.go index 5f928e7..386ab7e 100644 --- a/internal/server.go +++ b/internal/server.go @@ -12,7 +12,6 @@ import ( rice "github.com/GeertJohan/go.rice" "github.com/NYTimes/gziphandler" - humanize "github.com/dustin/go-humanize" "github.com/gabstv/merger" "github.com/justinas/nosurf" "github.com/prologic/observe" @@ -33,7 +32,7 @@ var ( ) func init() { - metrics = observe.NewMetrics("twtd") + metrics = observe.NewMetrics("spyda") } // Server ... @@ -234,45 +233,6 @@ func (s *Server) setupMetrics() { "Number of unique feeds in the global feed cache", ) - // feed cache size - metrics.NewGauge( - "cache", "twts", - "Number of active twts in the global feed cache", - ) - - // blogs cache size - metrics.NewGaugeFunc( - "cache", "blogs", - "Number of blogs in the blogs cache", - func() float64 { - return float64(s.blogs.Count()) - }, - ) - - // feed cache processing time - metrics.NewGauge( - "cache", "last_processed_seconds", - "Number of seconds for a feed cache cycle", - ) - - // feed cache limited fetch (feed exceeded MaxFetchLImit or unknown size) - metrics.NewCounter( - "cache", "limited", - "Number of feed cache fetches affected by MaxFetchLimit", - ) - - // archive size - metrics.NewCounter( - "archive", "size", - "Number of items inserted into the global feed archive", - ) - - // archive errors - metrics.NewCounter( - "archive", "error", - "Number of items errored inserting into the global feed archive", - ) - // server info metrics.NewGaugeVec( "server", "info", @@ -353,140 +313,40 @@ func (s *Server) initRoutes() { s.router.GET("/privacy", s.PageHandler("privacy")) s.router.GET("/abuse", s.PageHandler("abuse")) - s.router.GET("/", s.TimelineHandler()) - s.router.HEAD("/", s.TimelineHandler()) + s.router.GET("/", s.IndexHandler()) + s.router.HEAD("/", s.IndexHandler()) s.router.GET("/robots.txt", s.RobotsHandler()) s.router.HEAD("/robots.txt", s.RobotsHandler()) - s.router.GET("/discover", s.am.MustAuth(s.DiscoverHandler())) - s.router.GET("/mentions", s.am.MustAuth(s.MentionsHandler())) s.router.GET("/search", s.SearchHandler()) - s.router.HEAD("/twt/:hash", s.PermalinkHandler()) - s.router.GET("/twt/:hash", s.PermalinkHandler()) - - s.router.GET("/bookmark/:hash", s.BookmarkHandler()) - s.router.POST("/bookmark/:hash", s.BookmarkHandler()) - - s.router.HEAD("/conv/:hash", s.ConversationHandler()) - s.router.GET("/conv/:hash", s.ConversationHandler()) - - s.router.GET("/feeds", s.am.MustAuth(s.FeedsHandler())) - s.router.POST("/feed", s.am.MustAuth(s.FeedHandler())) - - s.router.POST("/post", s.am.MustAuth(s.PostHandler())) - s.router.PATCH("/post", s.am.MustAuth(s.PostHandler())) - s.router.DELETE("/post", s.am.MustAuth(s.PostHandler())) - - // Private Messages - s.router.GET("/messages", s.am.MustAuth(s.ListMessagesHandler())) - s.router.GET("/messages/:msgid", s.am.MustAuth(s.ViewMessageHandler())) - s.router.POST("/messages/send", s.am.MustAuth(s.SendMessageHandler())) - s.router.POST("/messages/delete", s.am.MustAuth(s.DeleteMessagesHandler())) - - s.router.POST("/blog", s.am.MustAuth(s.CreateOrUpdateBlogHandler())) - s.router.GET("/blogs/:author", s.ListBlogsHandler()) - s.router.GET("/blog/:author/:year/:month/:date/:slug", s.ViewBlogHandler()) - s.router.HEAD("/blog/:author/:year/:month/:date/:slug", s.ViewBlogHandler()) - s.router.GET("/blog/:author/:year/:month/:date/:slug/edit", s.EditBlogHandler()) - s.router.GET("/blog/:author/:year/:month/:date/:slug/delete", s.DeleteBlogHandler()) - s.router.GET("/blog/:author/:year/:month/:date/:slug/publish", s.PublishBlogHandler()) - - if s.config.OpenProfiles { - s.router.GET("/user/:nick/", s.ProfileHandler()) - s.router.GET("/user/:nick/config.yaml", s.UserConfigHandler()) - } else { - s.router.GET("/user/:nick/", s.am.MustAuth(s.ProfileHandler())) - s.router.GET("/user/:nick/config.yaml", s.am.MustAuth(s.UserConfigHandler())) - } - s.router.GET("/user/:nick/avatar", s.AvatarHandler()) - s.router.HEAD("/user/:nick/avatar", s.AvatarHandler()) - s.router.GET("/user/:nick/followers", s.FollowersHandler()) - s.router.GET("/user/:nick/following", s.FollowingHandler()) - s.router.GET("/user/:nick/bookmarks", s.BookmarksHandler()) - - s.router.GET("/pod/avatar", s.PodAvatarHandler()) - - // WebMentions - s.router.POST("/user/:nick/webmention", s.WebMentionHandler()) - - // External Feeds - s.router.GET("/external", s.ExternalHandler()) - s.router.GET("/externalAvatar", s.ExternalAvatarHandler()) - s.router.HEAD("/externalAvatar", s.ExternalAvatarHandler()) - - // External Queries (protected by a short-lived token) - s.router.GET("/whoFollows", s.WhoFollowsHandler()) - - // Syndication Formats (RSS, Atom, JSON Feed) - s.router.HEAD("/atom.xml", s.SyndicationHandler()) - s.router.HEAD("/user/:nick/atom.xml", s.SyndicationHandler()) - s.router.GET("/atom.xml", s.SyndicationHandler()) - s.router.GET("/user/:nick/atom.xml", s.SyndicationHandler()) - - s.router.GET("/feed/:name/manage", s.am.MustAuth(s.ManageFeedHandler())) - s.router.POST("/feed/:name/manage", s.am.MustAuth(s.ManageFeedHandler())) - s.router.POST("/feed/:name/archive", s.am.MustAuth(s.ArchiveFeedHandler())) - s.router.GET("/login", s.am.HasAuth(s.LoginHandler())) s.router.POST("/login", s.LoginHandler()) s.router.GET("/logout", s.LogoutHandler()) s.router.POST("/logout", s.LogoutHandler()) - s.router.GET("/register", s.am.HasAuth(s.RegisterHandler())) - s.router.POST("/register", s.RegisterHandler()) - // Reset Password - s.router.GET("/resetPassword", s.ResetPasswordHandler()) - s.router.POST("/resetPassword", s.ResetPasswordHandler()) - s.router.GET("/newPassword", s.ResetPasswordMagicLinkHandler()) - s.router.POST("/newPassword", s.NewPasswordHandler()) + s.router.GET("/pwreset", s.ResetPasswordHandler()) + s.router.POST("/pwreset", s.ResetPasswordHandler()) + s.router.GET("/chpasswd", s.ResetPasswordMagicLinkHandler()) + s.router.POST("/chpasswd", s.NewPasswordHandler()) - // Media Handling - s.router.GET("/media/:name", s.MediaHandler()) - s.router.HEAD("/media/:name", s.MediaHandler()) - s.router.POST("/upload", s.am.MustAuth(s.UploadMediaHandler())) - - // Task State - s.router.GET("/task/:uuid", s.TaskHandler()) - - // User/Feed Lookups - s.router.GET("/lookup", s.am.MustAuth(s.LookupHandler())) - - s.router.GET("/follow", s.am.MustAuth(s.FollowHandler())) - s.router.POST("/follow", s.am.MustAuth(s.FollowHandler())) - - s.router.GET("/import", s.am.MustAuth(s.ImportHandler())) - s.router.POST("/import", s.am.MustAuth(s.ImportHandler())) - - s.router.GET("/unfollow", s.am.MustAuth(s.UnfollowHandler())) - s.router.POST("/unfollow", s.am.MustAuth(s.UnfollowHandler())) - - s.router.GET("/mute", s.am.MustAuth(s.MuteHandler())) - s.router.POST("/mute", s.am.MustAuth(s.MuteHandler())) - s.router.GET("/unmute", s.am.MustAuth(s.UnmuteHandler())) - s.router.POST("/unmute", s.am.MustAuth(s.UnmuteHandler())) - - s.router.GET("/transferFeed/:name", s.TransferFeedHandler()) - s.router.GET("/transferFeed/:name/:transferTo", s.TransferFeedHandler()) + s.router.GET("/add", s.AddHandler()) + s.router.POST("/add", s.AddHandler()) s.router.GET("/settings", s.am.MustAuth(s.SettingsHandler())) s.router.POST("/settings", s.am.MustAuth(s.SettingsHandler())) s.router.POST("/token/delete/:signature", s.am.MustAuth(s.DeleteTokenHandler())) - s.router.GET("/config", s.am.MustAuth(s.PodConfigHandler())) - s.router.GET("/manage/pod", s.ManagePodHandler()) - s.router.POST("/manage/pod", s.ManagePodHandler()) + s.router.GET("/manage", s.ManageHandler()) + s.router.POST("/manage", s.ManageHandler()) s.router.GET("/manage/users", s.ManageUsersHandler()) s.router.POST("/manage/adduser", s.AddUserHandler()) s.router.POST("/manage/deluser", s.DelUserHandler()) - s.router.GET("/deleteFeeds", s.DeleteAccountHandler()) - s.router.POST("/delete", s.am.MustAuth(s.DeleteAllHandler())) - // Support / Report Abuse handlers s.router.GET("/support", s.SupportHandler()) @@ -687,25 +547,12 @@ func NewServer(bind string, options ...Option) (*Server, error) { log.Infof("Admin User: %s", server.config.AdminUser) log.Infof("Admin Name: %s", server.config.AdminName) log.Infof("Admin Email: %s", server.config.AdminEmail) - log.Infof("Max Twts per Page: %d", server.config.TwtsPerPage) - log.Infof("Max Cache TTL: %s", server.config.MaxCacheTTL) - log.Infof("Max Cache Items: %d", server.config.MaxCacheItems) - log.Infof("Maximum length of Posts: %d", server.config.MaxTwtLength) - log.Infof("Open User Profiles: %t", server.config.OpenProfiles) - log.Infof("Open Registrations: %t", server.config.OpenRegistrations) log.Infof("SMTP Host: %s", server.config.SMTPHost) log.Infof("SMTP Port: %d", server.config.SMTPPort) log.Infof("SMTP User: %s", server.config.SMTPUser) log.Infof("SMTP From: %s", server.config.SMTPFrom) - log.Infof("Max Fetch Limit: %s", humanize.Bytes(uint64(server.config.MaxFetchLimit))) - log.Infof("Max Upload Size: %s", humanize.Bytes(uint64(server.config.MaxUploadSize))) log.Infof("API Session Time: %s", server.config.APISessionTime) - // Warn about user registration being disabled. - if !server.config.OpenRegistrations { - log.Warn("Open Registrations are disabled as per configuration (no -R/--open-registrations)") - } - server.initRoutes() api.initRoutes() diff --git a/internal/templates.go b/internal/templates.go index 373ba05..9399081 100644 --- a/internal/templates.go +++ b/internal/templates.go @@ -40,11 +40,7 @@ func NewTemplateManager(conf *Config, blogs *BlogsCache, cache *Cache) (*Templat funcMap["hostnameFromURL"] = HostnameFromURL funcMap["prettyURL"] = PrettyURL funcMap["isLocalURL"] = IsLocalURLFactory(conf) - funcMap["formatTwt"] = FormatTwtFactory(conf) - funcMap["unparseTwt"] = UnparseTwtFactory(conf) funcMap["formatForDateTime"] = FormatForDateTime - funcMap["urlForBlog"] = URLForBlogFactory(conf, blogs) - funcMap["urlForConv"] = URLForConvFactory(conf, cache) funcMap["isAdminUser"] = IsAdminUserFactory(conf) m := &TemplateManager{debug: conf.Debug, templates: templates, funcMap: funcMap} diff --git a/internal/templates/_partials.html b/internal/templates/_partials.html index 6f84957..965a5cd 100644 --- a/internal/templates/_partials.html +++ b/internal/templates/_partials.html @@ -11,7 +11,7 @@\n \n\n \nContact us
\nHow can we help you?
\n \n \n-
- Page {{ .Page }}/{{ .PageNums }} of {{ .Nums }} Twts
+- Page {{ .Page }}/{{ .PageNums }} of {{ .Nums }} Results