Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

apage

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apage - npm Package Compare versions

Comparing version 0.0.3 to 0.0.15

bin/akaybe-build

440

build/apage.js

@@ -1,46 +0,91 @@

// Generated by CoffeeScript 1.9.1
// Generated by CoffeeScript 1.9.2
/*! Apage 0.0.3 //// MIT Licence //// http://apage.richplastow.com/ */
/*! Apage 0.0.15 //// MIT Licence //// http://apage.richplastow.com/ */
(function() {
var A, B, E, F, I, Main, N, O, R, S, U, V, X, header, style, ª;
var Article, Main, dirname, filename, filterLine, marked, ordername, page, parseFilename, renderer, script, tidypath, ª, ªA, ªB, ªE, ªF, ªI, ªN, ªO, ªR, ªS, ªU, ªV, ªX, ªclone, ªex, ªhas, ªpopulate, ªretrieve, ªtype,
slice = [].slice;
I = 'Apage';
ªI = 'Apage';
V = '0.0.3';
ªV = '0.0.15';
A = 'array';
ªA = 'array';
B = 'boolean';
ªB = 'boolean';
E = 'error';
ªE = 'error';
F = 'function';
ªF = 'function';
N = 'number';
ªN = 'number';
O = 'object';
ªO = 'object';
R = 'regexp';
ªR = 'regexp';
S = 'string';
ªS = 'string';
U = 'undefined';
ªU = 'undefined';
X = 'null';
ªX = 'null';
ª = console.log.bind(console);
ª.type = function(x) {
ªex = function(x, a, b) {
var pos;
if (-1 === (pos = a.indexOf(x))) {
return x;
} else {
return b.charAt(pos);
}
};
ªhas = function(h, n, t, f) {
if (t == null) {
t = true;
}
if (f == null) {
f = false;
}
if (-1 !== h.indexOf(n)) {
return t;
} else {
return f;
}
};
ªtype = function(x) {
return {}.toString.call(x).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
};
ª.populate = function(candidate, subject, rules, updating) {
var errors, i, j, key, len, len1, rule, test, type, use, value;
if (O !== ª.type(candidate)) {
return ["`candidate` is type '" + (ª.type(candidate)) + "' not 'object'"];
ªretrieve = function(instances, identifier) {
var instance;
instance = instances[identifier];
if (!instance) {
switch (typeof identifier) {
case ªS:
throw new Error("'" + identifier + "' does not exist");
break;
case ªN:
throw new Error("`" + identifier + "` does not exist");
break;
case ªU:
throw new Error("`identifier` is `undefined`");
break;
default:
throw new Error("`identifier` is type '" + (ªtype(identifier)) + "'");
}
}
return instance;
};
ªpopulate = function(candidate, subject, rules, updating) {
var errors, j, k, key, len, len1, rule, test, type, use, value;
if (ªO !== ªtype(candidate)) {
return ["`candidate` is type '" + (ªtype(candidate)) + "' not 'object'"];
}
errors = [];
for (i = 0, len = rules.length; i < len; i++) {
rule = rules[i];
for (j = 0, len = rules.length; j < len; j++) {
rule = rules[j];
key = rule[0], use = rule[1], type = rule[2], test = rule[3];

@@ -52,8 +97,8 @@ value = candidate[key];

} else {
errors.push("Missing key '" + key + "' is mandatory");
errors.push("Missing field '" + key + "' is mandatory");
}
} else if (type !== ª.type(value)) {
errors.push("Key '" + key + "' is type '" + (ª.type(value)) + "' not '" + type + "'");
} else if (type !== ªtype(value)) {
errors.push("Field '" + key + "' is type '" + (ªtype(value)) + "' not '" + type + "'");
} else if (!test.test(value)) {
errors.push("Key '" + key + "' fails " + ('' + test));
errors.push("Field '" + key + "' fails " + ('' + test));
}

@@ -64,4 +109,4 @@ }

}
for (j = 0, len1 = rules.length; j < len1; j++) {
rule = rules[j];
for (k = 0, len1 = rules.length; k < len1; k++) {
rule = rules[k];
key = rule[0], use = rule[1], type = rule[2], test = rule[3];

@@ -79,8 +124,8 @@ value = candidate[key];

ª.clone = function(subject, rules) {
var i, key, len, out, rule;
ªclone = function(subject, rules) {
var j, key, len, out, rule;
out = {};
for (i = 0, len = rules.length; i < len; i++) {
rule = rules[i];
key = rule[0];
for (j = 0, len = rules.length; j < len; j++) {
rule = rules[j];
key = ªS === typeof rule ? rule : rule[0];
out[key] = subject[key];

@@ -91,5 +136,151 @@ }

parseFilename = function(nm, part) {
var dash, dot, parts;
if (ªS !== ªtype(nm)) {
throw new Error("`nm` is " + (ªtype(nm)) + ", not string");
}
parts = {};
dash = nm.indexOf('-');
dot = nm.indexOf('.');
if (-1 === dot) {
nm += '.';
dot = nm.length - 1;
}
if (-1 !== dash && dash < dot) {
parts.order = nm.substr(0, dash) * 1;
if (isNaN(parts.order)) {
dash = -1;
}
} else {
dash = -1;
}
parts.title = nm.substr(dash + 1, dot - dash - 1);
parts.ext = nm.substr(dot + 1);
parts.slug = parts.title.toLowerCase().replace(/[“”‘’,]/g, '').replace(/[\s–—…·:;]/g, '-').replace(/^-+|-+$/g, '').replace(/-+/g, '-').replace(/[àáäâèéëêìíïîòóöôùúüûñç]/g, function(c) {
return ªex(c, 'àáäâèéëêìíïîòóöôùúüûñç', 'aaaaeeeeiiiioooouuuunc');
});
if (isNaN(parts.order)) {
parts.order = parts.slug * 1;
}
if (isNaN(parts.order)) {
parts.order = parts.slug;
}
if (!part) {
return parts;
}
if (ªU !== ªtype(parts[part])) {
return parts[part];
}
throw new Error("`part` not recognised, use 'order|title|slug|ext'");
};
Article = (function() {
Article.prototype.I = 'Article';
Article.prototype.toString = function() {
return "[object " + this.I + "]";
};
Article.prototype._rules = {
config: [['path', void 0, 'string', /^[a-z0-9][-\/a-z0-9]{0,63}\.[.a-z0-9]+$/i], ['raw', '', 'string', /^[^\x00-\x08\x0E-\x1F]{0,10000}$/]],
properties: [['id', void 0, 'string', /^apage_[-_0-9a-z]{1,10}$/], ['order', void 0, 'string', /^[-_0-9a-z]{1,10}$/]]
};
function Article(config) {
var errors, fname, i, j, key, len, line, ref, ref1, value;
if (config == null) {
config = {};
}
this._config = {};
if (errors = ªpopulate(config, this._config, this._rules.config)) {
throw new Error('Invalid `config`:\n ' + errors.join('\n '));
}
this.path = this._config.path.replace(/^[.\/]+|[.\/]+$/g, '');
this.id = (function() {
var j, len, ref, results;
ref = this.path.split('/');
results = [];
for (i = j = 0, len = ref.length; j < len; i = ++j) {
fname = ref[i];
results.push(parseFilename(fname, 'slug'));
}
return results;
}).call(this);
this.id = 'apage_' + this.id.join('_');
this.order = parseFilename(fname, 'order');
this.front = [];
if ('---\n' === this._config.raw.substr(0, 4)) {
this._config.raw = this._config.raw.split('---\n');
ref = this._config.raw[1].split('\n');
for (i = j = 0, len = ref.length; j < len; i = ++j) {
line = ref[i];
ref1 = line.split(':'), key = ref1[0], value = 2 <= ref1.length ? slice.call(ref1, 1) : [];
key = key != null ? key.replace(/^\s+|\s+$/g, '') : void 0;
value = value != null ? value.join(':').replace(/^\s+|\s+$/g, '') : void 0;
if (!key || !value) {
continue;
}
switch (key) {
case 'id':
this.id = "apage_" + (value.replace(/^apage_/, ''));
break;
case 'order':
this.order = isNaN(value * 1) ? value : parseInt(value, 10);
break;
case 'title':
this.title = value;
break;
default:
this.front.push([key, value]);
}
}
this._config.raw = (this._config.raw.slice(2)).join('---\n');
}
this._config.raw = this._config.raw.replace(/^\s+|\s+$/g, '');
this.title = this.title || (this._config.raw.split('\n'))[0];
this.html = (marked(this._config.raw)).replace(/\\/g, '\\\\').split('\n');
if (errors = ªpopulate(config, this, this._rules.properties, true)) {
throw new Error('Invalid `config`:\n ' + errors.join('\n '));
}
}
Article.prototype.clone = function() {
return ªclone(this, ['id', 'path', 'order']);
};
Article.prototype.destructor = function() {};
Article.prototype.edit = function(amend) {
return ªpopulate(amend, this, this._rules.properties, true);
};
Article.prototype.config = function(key, value) {
var obj;
switch (arguments.length) {
case 0:
return ªclone(this._config, this._rules.config);
case 1:
switch (ªtype(key)) {
case ªS:
return this._config[key];
case ªO:
return ªpopulate(key, this._config, this._rules.config, true);
}
break;
case 2:
obj = {};
obj[key] = value;
return this.config(obj);
}
};
return Article;
})();
Main = (function() {
Main.prototype.I = I;
Main.prototype.I = ªI;
Main.prototype.V = ªV;
Main.prototype.toString = function() {

@@ -100,3 +291,3 @@ return "[object " + this.I + "]";

Main.prototype._rules = {
config: [['title', 'Untitled', 'string', /^[^\x00-\x1F]{1,24}$/]]
config: [['title', 'Untitled', 'string', /^[^\x00-\x1F]{1,24}$/], ['url', false, 'string', /^[-:.\/a-z0-9]{1,64}$/], ['plugin', '', 'string', /^[^\x00-\x08\x0E-\x1F]{0,10000}$/]]
};

@@ -109,4 +300,5 @@

}
this._c = {};
if (errors = ª.populate(config, this._c, this._rules.config)) {
this._config = {};
this._articles = [];
if (errors = ªpopulate(config, this._config, this._rules.config)) {
throw new Error('Invalid `config`:\n ' + errors.join('\n '));

@@ -120,9 +312,9 @@ }

case 0:
return ª.clone(this._c, this._rules.config);
return ªclone(this._config, this._rules.config);
case 1:
switch (ª.type(key)) {
case S:
return this._c[key];
case O:
return ª.populate(key, this._c, this._rules.config, true);
switch (ªtype(key)) {
case ªS:
return this._config[key];
case ªO:
return ªpopulate(key, this._config, this._rules.config, true);
}

@@ -137,4 +329,74 @@ break;

Main.prototype.browse = function() {
var a, j, len, ref, results;
ref = this._articles;
results = [];
for (j = 0, len = ref.length; j < len; j++) {
a = ref[j];
results.push((function(a) {
return {
id: a.id,
order: a.order
};
})(a));
}
return results;
};
Main.prototype.read = function(identifier) {
var art;
art = ªretrieve(this._articles, identifier);
return art.clone();
};
Main.prototype.destroy = function(identifier) {
var art;
art = ªretrieve(this._articles, identifier);
delete this._articles[art.id];
this._articles.splice(art.index, 1);
art.destructor();
return this;
};
Main.prototype.edit = function(identifier, amend) {
var art, errors;
art = ªretrieve(this._articles, identifier);
if (errors = art.edit(amend)) {
throw new Error('Invalid `amend`:\n ' + errors.join('\n '));
}
return this;
};
Main.prototype.add = function(article) {
var instance;
if (!article) {
return this;
}
instance = new Article(article);
if (this._articles[instance.id]) {
throw new Error("'" + instance.id + "' already exists");
}
instance.index = this._articles.length;
this._articles.push(instance);
this._articles.sort(function(a, b) {
if (a.order > b.order) {
return 1;
}
if (a.order < b.order) {
return -1;
}
if (a.id > b.id) {
return 1;
}
if (a.id < b.id) {
return -1;
}
return 0;
});
this._articles[instance.id] = instance;
return this;
};
Main.prototype.render = function() {
return header(this._c);
return "" + (page(this._config, this._articles));
};

@@ -146,20 +408,96 @@

if (F === typeof define && define.amd) {
if (ªF === typeof define && define.amd) {
define(function() {
return Main;
});
} else if (O === typeof module && module && module.exports) {
} else if (ªO === typeof module && module && module.exports) {
module.exports = Main;
} else {
this[I] = Main;
this[ªI] = Main;
}
header = function(config) {
return "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <title>" + config.title + "</title>\n <meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">\n <style>\n " + (style()) + "\n </style>\n</head>\n<body>\n <h1>" + config.title + "</h1>";
if (ªF === typeof define && define.amd) {
} else if (ªO === typeof module && module && module.exports) {
marked = require('marked');
} else {
marked = window.marked;
}
renderer = new marked.Renderer;
renderer.heading = function(text, level) {
return "<h" + level + ">" + text + "</h" + level + ">\n";
};
style = function() {
return "/* normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n /* Document */\n html { font-family:sans-serif; -ms-text-size-adjust:100%; -webkit-text-size-adjust:100% }\n body { margin:0 }\n\n /* HTML5 */\n article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,\n section,summary { display: block }\n audio,canvas,progress,video { display:inline-block; vertical-align:baseline }\n audio:not([controls]) { display:none; height:0 }\n [hidden],template { display:none }\n\n /* Links */\n a { background-color:transparent }\n a:active,a:hover { outline:0 }\n\n /* Text */\n abbr[title] { border-bottom: 1px dotted }\n b, strong { font-weight: bold }\n dfn { font-style: italic }\n h1 { font-size: 2em; margin: 0.67em 0 }\n mark { background: #ff0; color: #000 }\n small { font-size: 80% }\n sub,sup { font-size:75%; line-height:0; position:relative; vertical-align:baseline }\n sup { top: -0.5em }\n sub { bottom: -0.25em }\n\n /* Embedded */\n img { border: 0 }\n svg:not(:root) { overflow: hidden }\n\n /* Grouping */\n figure { margin: 1em 40px }\n hr { box-sizing: content-box; height: 0 }\n pre { overflow: auto }\n code,kbd,pre,samp { font-family:monospace,monospace; font-size:1em }\n\n /* Forms */\n button,input,optgroup,select,textarea { color:inherit; font:inherit; margin:0 }\n button { overflow:visible }\n button,select { text-transform:none }\n button,html input[type=\"button\"],input[type=\"reset\"],\n input[type=\"submit\"] { -webkit-appearance:button; cursor:pointer }\n button[disabled],html input[disabled] { cursor:default }\n button::-moz-focus-inner,input::-moz-focus-inner{ border:0; padding:0 }\n input { line-height:normal }\n input[type=\"checkbox\"],input[type=\"radio\"] { box-sizing:border-box; padding:0 }\n input[type=\"number\"]::-webkit-inner-spin-button,\n input[type=\"number\"]::-webkit-outer-spin-button { height:auto }\n input[type=\"search\"] { -webkit-appearance:textfield; box-sizing:content-box }\n input[type=\"search\"]::-webkit-search-cancel-button,\n input[type=\"search\"]::-webkit-search-decoration { -webkit-appearance:none }\n fieldset { border:1px solid #c0c0c0; margin:0 2px; padding:0.35em 0.625em 0.75em }\n legend { border:0; padding:0 }\n textarea { overflow:auto }\n optgroup { font-weight:bold }\n\n /* Tables */\n table { border-collapse:collapse; border-spacing:0 }\n td,th { padding:0 }\n";
marked.setOptions({
renderer: renderer
});
page = function(config, articles) {
var article, comment, generator, i, j, k, len, len1, line, out, ref;
generator = ªI + " " + ªV + " http://apage.richplastow.com/";
comment = config.plugin ? '‘Inspect Element’ here, for Apage’s injected CSS' : 'Apage was configured with no plugins, so no CSS is injected here';
out = ["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <title>" + config.title + "</title>\n <meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">\n <meta name=\"generator\" content=\"" + generator + "\">\n <style>\n /* " + comment + " */\n </style>\n" + (script(config, articles)) + "\n</head>\n<body>\n"];
if (!articles.length) {
out.push('<!-- Apage was rendered with no articles -->\n');
}
for (i = j = 0, len = articles.length; j < len; i = ++j) {
article = articles[i];
out.push("<article id=\"" + article.id + "\"\n class=\"apage\"\n data-apage-opath=\"/" + article.path + "\"\n data-apage-dname=\"_" + (dirname(article.path)) + "\"\n data-apage-order=\"" + article.order + "\"\n data-apage-front='" + ((JSON.stringify(article.front)).replace(/'/g, "&#39;")) + "'\n data-apage-title=\"" + article.title + "\">");
ref = article.html;
for (k = 0, len1 = ref.length; k < len1; k++) {
line = ref[k];
out.push(filterLine(config, line));
}
out.push("</article><!-- / #" + article.id + " -->\n\n");
}
comment = config.plugin ? '‘Inspect Element’ here, for Apage’s injected elements' : 'Apage was configured with no plugins, so nothing is injected here';
out.push("<!-- " + comment + " -->");
return out.join('\n ') + '\n\n</body>\n</html>';
};
filterLine = function(config, line) {
var rx;
if (config.url) {
rx = new RegExp('href="' + config.url, 'g');
return line.replace(rx, 'href="');
} else {
return line;
}
};
tidypath = function(p) {
var name, order;
name = filename(p).split('-');
order = name[0];
name = isNaN(order * 1) ? name.join('-') : name.slice(1).join('-');
return dirname(p) + name.split('.').slice(0, -1).join('.');
};
dirname = function(p) {
return ªhas(p, '/', p.split('/').slice(0, -1).join('_') + '_', '');
};
filename = function(p) {
return p.split('/').slice(-1)[0];
};
ordername = function(p) {
var order;
order = filename(p).split('-')[0];
if (isNaN(order * 1)) {
return '';
} else {
return order * 1;
}
};
script = function(config, articles) {
if (!config.plugin) {
return '';
}
return "\n <script>\n\n//// When the DOM is ready, set up Apage and inject the plugins. \nwindow.addEventListener('load', function () { (function (d) { 'use strict'; \n\n\n//// Declare iterator, length and HTML-reference variables. \nvar i, l, $ref\n\n\n//// Initialize two arrays which are available to all Apage plugins. \n ,arts = []\n ,resolvers = []\n ,updaters = []\n\n\n//// Like jQuery, but native. \n ,$ = d.querySelector.bind(d)\n ,$$ = d.querySelectorAll.bind(d)\n\n\n//// Get a reference to all `<article class=\"apage\">` elements. \n ,$arts = $$('article.apage')\n\n\n//// Convert JavaScript’s native `arguments` object to an array. \n ,getArgs = function (args, offset) {\n return Array.prototype.slice.call(args, offset || 0);\n }\n\n\n//// `unattribute($ref,'data-apage-','opath'...)` removes data attributes. \n ,unattribute = function ($ref, prefix) {\n for ( var i=0, suffs=getArgs(arguments,2), l=suffs.length; i<l; i++ ) {\n $ref.removeAttribute(prefix + suffs[i]);\n }\n }\n\n\n//// Runs each resolver in order. These are added by the plugins, below. \n//// Resolvers are used to map a query to an article. \n ,resolve = function (query) {\n for (var i=0, l=resolvers.length, backstop, result={}; i<l; i++) {\n result = resolvers[i](query);\n if (result.art) { break; } // `query` does resolve to an article\n backstop = result.backstop || backstop; // may return a backstop\n }\n return result.art ? result.art : backstop; //@todo test logic of 'last valid backstop return' with several plugins at once\n }\n\n\n//// Runs each updater in order. These are added by the plugins, below. \n//// Updaters change the current DOM state, eg to show a single article. \n ,update = function (query) {\n for (var i=0, l=updaters.length, current=resolve(query); i<l; i++) {\n updaters[i](current);\n }\n }\n\n\n//// Tidies the URL hash and runs `update()` when the URL hash changes. \n ,onHashchange = function (event) {\n update( window.location.hash.substr(1).replace(/\\//g,'_') );\n if (event) { event.preventDefault(); }\n }\n\n;\n\n\n//// Populate the `arts` array using data from Apage `<ARTICLE>` elements. \nfor (i=0, l=$arts.length; i<l; i++) {\n $ref = $arts[i];\n arts.push({\n id: $ref.getAttribute('id')\n ,opath: $ref.getAttribute('data-apage-opath')\n ,dname: $ref.getAttribute('data-apage-dname')\n ,order: $ref.getAttribute('data-apage-order')\n ,front: JSON.parse( $ref.getAttribute('data-apage-front') )\n ,title: $ref.getAttribute('data-apage-title')\n ,$ref: $ref\n });\n unattribute($ref,'data-apage-','opath','dname','order','front','title');\n}\n\n\n//// Begin injecting plugins. \n\n" + config.plugin + "\n\n//// End injecting plugins. \n\n\n//// Run each updater when the page loads, and when the URL hash changes. \nonHashchange();\nwindow.addEventListener('hashchange', onHashchange);\n\n\n}).call(this, document) });\n\n </script>\n";
};
}).call(this);

1161

build/test/apage-with-test.js

@@ -1,43 +0,88 @@

// Generated by CoffeeScript 1.9.1
// Generated by CoffeeScript 1.9.2
/*! Apage 0.0.3 //// MIT Licence //// http://apage.richplastow.com/ */
/*! Apage 0.0.15 //// MIT Licence //// http://apage.richplastow.com/ */
(function() {
var A, B, E, F, I, Main, N, O, R, S, Tudor, U, V, X, header, style, tudor, ª,
var Article, Main, Tudor, dirname, filename, filterLine, marked, ordername, page, parseFilename, renderer, script, tidypath, tudor, ª, ªA, ªB, ªE, ªF, ªI, ªN, ªO, ªR, ªS, ªU, ªV, ªX, ªclone, ªex, ªhas, ªpopulate, ªretrieve, ªtype,
slice = [].slice,
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
I = 'Apage';
ªI = 'Apage';
V = '0.0.3';
ªV = '0.0.15';
A = 'array';
ªA = 'array';
B = 'boolean';
ªB = 'boolean';
E = 'error';
ªE = 'error';
F = 'function';
ªF = 'function';
N = 'number';
ªN = 'number';
O = 'object';
ªO = 'object';
R = 'regexp';
ªR = 'regexp';
S = 'string';
ªS = 'string';
U = 'undefined';
ªU = 'undefined';
X = 'null';
ªX = 'null';
ª = console.log.bind(console);
ª.type = function(x) {
ªex = function(x, a, b) {
var pos;
if (-1 === (pos = a.indexOf(x))) {
return x;
} else {
return b.charAt(pos);
}
};
ªhas = function(h, n, t, f) {
if (t == null) {
t = true;
}
if (f == null) {
f = false;
}
if (-1 !== h.indexOf(n)) {
return t;
} else {
return f;
}
};
ªtype = function(x) {
return {}.toString.call(x).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
};
ª.populate = function(candidate, subject, rules, updating) {
ªretrieve = function(instances, identifier) {
var instance;
instance = instances[identifier];
if (!instance) {
switch (typeof identifier) {
case ªS:
throw new Error("'" + identifier + "' does not exist");
break;
case ªN:
throw new Error("`" + identifier + "` does not exist");
break;
case ªU:
throw new Error("`identifier` is `undefined`");
break;
default:
throw new Error("`identifier` is type '" + (ªtype(identifier)) + "'");
}
}
return instance;
};
ªpopulate = function(candidate, subject, rules, updating) {
var errors, j, k, key, len, len1, rule, test, type, use, value;
if (O !== ª.type(candidate)) {
return ["`candidate` is type '" + (ª.type(candidate)) + "' not 'object'"];
if (ªO !== ªtype(candidate)) {
return ["`candidate` is type '" + (ªtype(candidate)) + "' not 'object'"];
}

@@ -53,8 +98,8 @@ errors = [];

} else {
errors.push("Missing key '" + key + "' is mandatory");
errors.push("Missing field '" + key + "' is mandatory");
}
} else if (type !== ª.type(value)) {
errors.push("Key '" + key + "' is type '" + (ª.type(value)) + "' not '" + type + "'");
} else if (type !== ªtype(value)) {
errors.push("Field '" + key + "' is type '" + (ªtype(value)) + "' not '" + type + "'");
} else if (!test.test(value)) {
errors.push("Key '" + key + "' fails " + ('' + test));
errors.push("Field '" + key + "' fails " + ('' + test));
}

@@ -79,3 +124,3 @@ }

ª.clone = function(subject, rules) {
ªclone = function(subject, rules) {
var j, key, len, out, rule;

@@ -85,3 +130,3 @@ out = {};

rule = rules[j];
key = rule[0];
key = ªS === typeof rule ? rule : rule[0];
out[key] = subject[key];

@@ -92,5 +137,151 @@ }

parseFilename = function(nm, part) {
var dash, dot, parts;
if (ªS !== ªtype(nm)) {
throw new Error("`nm` is " + (ªtype(nm)) + ", not string");
}
parts = {};
dash = nm.indexOf('-');
dot = nm.indexOf('.');
if (-1 === dot) {
nm += '.';
dot = nm.length - 1;
}
if (-1 !== dash && dash < dot) {
parts.order = nm.substr(0, dash) * 1;
if (isNaN(parts.order)) {
dash = -1;
}
} else {
dash = -1;
}
parts.title = nm.substr(dash + 1, dot - dash - 1);
parts.ext = nm.substr(dot + 1);
parts.slug = parts.title.toLowerCase().replace(/[“”‘’,]/g, '').replace(/[\s–—…·:;]/g, '-').replace(/^-+|-+$/g, '').replace(/-+/g, '-').replace(/[àáäâèéëêìíïîòóöôùúüûñç]/g, function(c) {
return ªex(c, 'àáäâèéëêìíïîòóöôùúüûñç', 'aaaaeeeeiiiioooouuuunc');
});
if (isNaN(parts.order)) {
parts.order = parts.slug * 1;
}
if (isNaN(parts.order)) {
parts.order = parts.slug;
}
if (!part) {
return parts;
}
if (ªU !== ªtype(parts[part])) {
return parts[part];
}
throw new Error("`part` not recognised, use 'order|title|slug|ext'");
};
Article = (function() {
Article.prototype.I = 'Article';
Article.prototype.toString = function() {
return "[object " + this.I + "]";
};
Article.prototype._rules = {
config: [['path', void 0, 'string', /^[a-z0-9][-\/a-z0-9]{0,63}\.[.a-z0-9]+$/i], ['raw', '', 'string', /^[^\x00-\x08\x0E-\x1F]{0,10000}$/]],
properties: [['id', void 0, 'string', /^apage_[-_0-9a-z]{1,10}$/], ['order', void 0, 'string', /^[-_0-9a-z]{1,10}$/]]
};
function Article(config) {
var errors, fname, i, j, key, len, line, ref, ref1, value;
if (config == null) {
config = {};
}
this._config = {};
if (errors = ªpopulate(config, this._config, this._rules.config)) {
throw new Error('Invalid `config`:\n ' + errors.join('\n '));
}
this.path = this._config.path.replace(/^[.\/]+|[.\/]+$/g, '');
this.id = (function() {
var j, len, ref, results;
ref = this.path.split('/');
results = [];
for (i = j = 0, len = ref.length; j < len; i = ++j) {
fname = ref[i];
results.push(parseFilename(fname, 'slug'));
}
return results;
}).call(this);
this.id = 'apage_' + this.id.join('_');
this.order = parseFilename(fname, 'order');
this.front = [];
if ('---\n' === this._config.raw.substr(0, 4)) {
this._config.raw = this._config.raw.split('---\n');
ref = this._config.raw[1].split('\n');
for (i = j = 0, len = ref.length; j < len; i = ++j) {
line = ref[i];
ref1 = line.split(':'), key = ref1[0], value = 2 <= ref1.length ? slice.call(ref1, 1) : [];
key = key != null ? key.replace(/^\s+|\s+$/g, '') : void 0;
value = value != null ? value.join(':').replace(/^\s+|\s+$/g, '') : void 0;
if (!key || !value) {
continue;
}
switch (key) {
case 'id':
this.id = "apage_" + (value.replace(/^apage_/, ''));
break;
case 'order':
this.order = isNaN(value * 1) ? value : parseInt(value, 10);
break;
case 'title':
this.title = value;
break;
default:
this.front.push([key, value]);
}
}
this._config.raw = (this._config.raw.slice(2)).join('---\n');
}
this._config.raw = this._config.raw.replace(/^\s+|\s+$/g, '');
this.title = this.title || (this._config.raw.split('\n'))[0];
this.html = (marked(this._config.raw)).replace(/\\/g, '\\\\').split('\n');
if (errors = ªpopulate(config, this, this._rules.properties, true)) {
throw new Error('Invalid `config`:\n ' + errors.join('\n '));
}
}
Article.prototype.clone = function() {
return ªclone(this, ['id', 'path', 'order']);
};
Article.prototype.destructor = function() {};
Article.prototype.edit = function(amend) {
return ªpopulate(amend, this, this._rules.properties, true);
};
Article.prototype.config = function(key, value) {
var obj;
switch (arguments.length) {
case 0:
return ªclone(this._config, this._rules.config);
case 1:
switch (ªtype(key)) {
case ªS:
return this._config[key];
case ªO:
return ªpopulate(key, this._config, this._rules.config, true);
}
break;
case 2:
obj = {};
obj[key] = value;
return this.config(obj);
}
};
return Article;
})();
Main = (function() {
Main.prototype.I = I;
Main.prototype.I = ªI;
Main.prototype.V = ªV;
Main.prototype.toString = function() {

@@ -101,3 +292,3 @@ return "[object " + this.I + "]";

Main.prototype._rules = {
config: [['title', 'Untitled', 'string', /^[^\x00-\x1F]{1,24}$/]]
config: [['title', 'Untitled', 'string', /^[^\x00-\x1F]{1,24}$/], ['url', false, 'string', /^[-:.\/a-z0-9]{1,64}$/], ['plugin', '', 'string', /^[^\x00-\x08\x0E-\x1F]{0,10000}$/]]
};

@@ -110,4 +301,5 @@

}
this._c = {};
if (errors = ª.populate(config, this._c, this._rules.config)) {
this._config = {};
this._articles = [];
if (errors = ªpopulate(config, this._config, this._rules.config)) {
throw new Error('Invalid `config`:\n ' + errors.join('\n '));

@@ -121,9 +313,9 @@ }

case 0:
return ª.clone(this._c, this._rules.config);
return ªclone(this._config, this._rules.config);
case 1:
switch (ª.type(key)) {
case S:
return this._c[key];
case O:
return ª.populate(key, this._c, this._rules.config, true);
switch (ªtype(key)) {
case ªS:
return this._config[key];
case ªO:
return ªpopulate(key, this._config, this._rules.config, true);
}

@@ -138,4 +330,74 @@ break;

Main.prototype.browse = function() {
var a, j, len, ref, results;
ref = this._articles;
results = [];
for (j = 0, len = ref.length; j < len; j++) {
a = ref[j];
results.push((function(a) {
return {
id: a.id,
order: a.order
};
})(a));
}
return results;
};
Main.prototype.read = function(identifier) {
var art;
art = ªretrieve(this._articles, identifier);
return art.clone();
};
Main.prototype.destroy = function(identifier) {
var art;
art = ªretrieve(this._articles, identifier);
delete this._articles[art.id];
this._articles.splice(art.index, 1);
art.destructor();
return this;
};
Main.prototype.edit = function(identifier, amend) {
var art, errors;
art = ªretrieve(this._articles, identifier);
if (errors = art.edit(amend)) {
throw new Error('Invalid `amend`:\n ' + errors.join('\n '));
}
return this;
};
Main.prototype.add = function(article) {
var instance;
if (!article) {
return this;
}
instance = new Article(article);
if (this._articles[instance.id]) {
throw new Error("'" + instance.id + "' already exists");
}
instance.index = this._articles.length;
this._articles.push(instance);
this._articles.sort(function(a, b) {
if (a.order > b.order) {
return 1;
}
if (a.order < b.order) {
return -1;
}
if (a.id > b.id) {
return 1;
}
if (a.id < b.id) {
return -1;
}
return 0;
});
this._articles[instance.id] = instance;
return this;
};
Main.prototype.render = function() {
return header(this._c);
return "" + (page(this._config, this._articles));
};

@@ -147,23 +409,97 @@

if (F === typeof define && define.amd) {
if (ªF === typeof define && define.amd) {
define(function() {
return Main;
});
} else if (O === typeof module && module && module.exports) {
} else if (ªO === typeof module && module && module.exports) {
module.exports = Main;
} else {
this[I] = Main;
this[ªI] = Main;
}
header = function(config) {
return "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <title>" + config.title + "</title>\n <meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">\n <style>\n " + (style()) + "\n </style>\n</head>\n<body>\n <h1>" + config.title + "</h1>";
if (ªF === typeof define && define.amd) {
} else if (ªO === typeof module && module && module.exports) {
marked = require('marked');
} else {
marked = window.marked;
}
renderer = new marked.Renderer;
renderer.heading = function(text, level) {
return "<h" + level + ">" + text + "</h" + level + ">\n";
};
style = function() {
return "/* normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n /* Document */\n html { font-family:sans-serif; -ms-text-size-adjust:100%; -webkit-text-size-adjust:100% }\n body { margin:0 }\n\n /* HTML5 */\n article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,\n section,summary { display: block }\n audio,canvas,progress,video { display:inline-block; vertical-align:baseline }\n audio:not([controls]) { display:none; height:0 }\n [hidden],template { display:none }\n\n /* Links */\n a { background-color:transparent }\n a:active,a:hover { outline:0 }\n\n /* Text */\n abbr[title] { border-bottom: 1px dotted }\n b, strong { font-weight: bold }\n dfn { font-style: italic }\n h1 { font-size: 2em; margin: 0.67em 0 }\n mark { background: #ff0; color: #000 }\n small { font-size: 80% }\n sub,sup { font-size:75%; line-height:0; position:relative; vertical-align:baseline }\n sup { top: -0.5em }\n sub { bottom: -0.25em }\n\n /* Embedded */\n img { border: 0 }\n svg:not(:root) { overflow: hidden }\n\n /* Grouping */\n figure { margin: 1em 40px }\n hr { box-sizing: content-box; height: 0 }\n pre { overflow: auto }\n code,kbd,pre,samp { font-family:monospace,monospace; font-size:1em }\n\n /* Forms */\n button,input,optgroup,select,textarea { color:inherit; font:inherit; margin:0 }\n button { overflow:visible }\n button,select { text-transform:none }\n button,html input[type=\"button\"],input[type=\"reset\"],\n input[type=\"submit\"] { -webkit-appearance:button; cursor:pointer }\n button[disabled],html input[disabled] { cursor:default }\n button::-moz-focus-inner,input::-moz-focus-inner{ border:0; padding:0 }\n input { line-height:normal }\n input[type=\"checkbox\"],input[type=\"radio\"] { box-sizing:border-box; padding:0 }\n input[type=\"number\"]::-webkit-inner-spin-button,\n input[type=\"number\"]::-webkit-outer-spin-button { height:auto }\n input[type=\"search\"] { -webkit-appearance:textfield; box-sizing:content-box }\n input[type=\"search\"]::-webkit-search-cancel-button,\n input[type=\"search\"]::-webkit-search-decoration { -webkit-appearance:none }\n fieldset { border:1px solid #c0c0c0; margin:0 2px; padding:0.35em 0.625em 0.75em }\n legend { border:0; padding:0 }\n textarea { overflow:auto }\n optgroup { font-weight:bold }\n\n /* Tables */\n table { border-collapse:collapse; border-spacing:0 }\n td,th { padding:0 }\n";
marked.setOptions({
renderer: renderer
});
page = function(config, articles) {
var article, comment, generator, i, j, k, len, len1, line, out, ref;
generator = ªI + " " + ªV + " http://apage.richplastow.com/";
comment = config.plugin ? '‘Inspect Element’ here, for Apage’s injected CSS' : 'Apage was configured with no plugins, so no CSS is injected here';
out = ["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <title>" + config.title + "</title>\n <meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">\n <meta name=\"generator\" content=\"" + generator + "\">\n <style>\n /* " + comment + " */\n </style>\n" + (script(config, articles)) + "\n</head>\n<body>\n"];
if (!articles.length) {
out.push('<!-- Apage was rendered with no articles -->\n');
}
for (i = j = 0, len = articles.length; j < len; i = ++j) {
article = articles[i];
out.push("<article id=\"" + article.id + "\"\n class=\"apage\"\n data-apage-opath=\"/" + article.path + "\"\n data-apage-dname=\"_" + (dirname(article.path)) + "\"\n data-apage-order=\"" + article.order + "\"\n data-apage-front='" + ((JSON.stringify(article.front)).replace(/'/g, "&#39;")) + "'\n data-apage-title=\"" + article.title + "\">");
ref = article.html;
for (k = 0, len1 = ref.length; k < len1; k++) {
line = ref[k];
out.push(filterLine(config, line));
}
out.push("</article><!-- / #" + article.id + " -->\n\n");
}
comment = config.plugin ? '‘Inspect Element’ here, for Apage’s injected elements' : 'Apage was configured with no plugins, so nothing is injected here';
out.push("<!-- " + comment + " -->");
return out.join('\n ') + '\n\n</body>\n</html>';
};
filterLine = function(config, line) {
var rx;
if (config.url) {
rx = new RegExp('href="' + config.url, 'g');
return line.replace(rx, 'href="');
} else {
return line;
}
};
tidypath = function(p) {
var name, order;
name = filename(p).split('-');
order = name[0];
name = isNaN(order * 1) ? name.join('-') : name.slice(1).join('-');
return dirname(p) + name.split('.').slice(0, -1).join('.');
};
dirname = function(p) {
return ªhas(p, '/', p.split('/').slice(0, -1).join('_') + '_', '');
};
filename = function(p) {
return p.split('/').slice(-1)[0];
};
ordername = function(p) {
var order;
order = filename(p).split('-')[0];
if (isNaN(order * 1)) {
return '';
} else {
return order * 1;
}
};
script = function(config, articles) {
if (!config.plugin) {
return '';
}
return "\n <script>\n\n//// When the DOM is ready, set up Apage and inject the plugins. \nwindow.addEventListener('load', function () { (function (d) { 'use strict'; \n\n\n//// Declare iterator, length and HTML-reference variables. \nvar i, l, $ref\n\n\n//// Initialize two arrays which are available to all Apage plugins. \n ,arts = []\n ,resolvers = []\n ,updaters = []\n\n\n//// Like jQuery, but native. \n ,$ = d.querySelector.bind(d)\n ,$$ = d.querySelectorAll.bind(d)\n\n\n//// Get a reference to all `<article class=\"apage\">` elements. \n ,$arts = $$('article.apage')\n\n\n//// Convert JavaScript’s native `arguments` object to an array. \n ,getArgs = function (args, offset) {\n return Array.prototype.slice.call(args, offset || 0);\n }\n\n\n//// `unattribute($ref,'data-apage-','opath'...)` removes data attributes. \n ,unattribute = function ($ref, prefix) {\n for ( var i=0, suffs=getArgs(arguments,2), l=suffs.length; i<l; i++ ) {\n $ref.removeAttribute(prefix + suffs[i]);\n }\n }\n\n\n//// Runs each resolver in order. These are added by the plugins, below. \n//// Resolvers are used to map a query to an article. \n ,resolve = function (query) {\n for (var i=0, l=resolvers.length, backstop, result={}; i<l; i++) {\n result = resolvers[i](query);\n if (result.art) { break; } // `query` does resolve to an article\n backstop = result.backstop || backstop; // may return a backstop\n }\n return result.art ? result.art : backstop; //@todo test logic of 'last valid backstop return' with several plugins at once\n }\n\n\n//// Runs each updater in order. These are added by the plugins, below. \n//// Updaters change the current DOM state, eg to show a single article. \n ,update = function (query) {\n for (var i=0, l=updaters.length, current=resolve(query); i<l; i++) {\n updaters[i](current);\n }\n }\n\n\n//// Tidies the URL hash and runs `update()` when the URL hash changes. \n ,onHashchange = function (event) {\n update( window.location.hash.substr(1).replace(/\\//g,'_') );\n if (event) { event.preventDefault(); }\n }\n\n;\n\n\n//// Populate the `arts` array using data from Apage `<ARTICLE>` elements. \nfor (i=0, l=$arts.length; i<l; i++) {\n $ref = $arts[i];\n arts.push({\n id: $ref.getAttribute('id')\n ,opath: $ref.getAttribute('data-apage-opath')\n ,dname: $ref.getAttribute('data-apage-dname')\n ,order: $ref.getAttribute('data-apage-order')\n ,front: JSON.parse( $ref.getAttribute('data-apage-front') )\n ,title: $ref.getAttribute('data-apage-title')\n ,$ref: $ref\n });\n unattribute($ref,'data-apage-','opath','dname','order','front','title');\n}\n\n\n//// Begin injecting plugins. \n\n" + config.plugin + "\n\n//// End injecting plugins. \n\n\n//// Run each updater when the page loads, and when the URL hash changes. \nonHashchange();\nwindow.addEventListener('hashchange', onHashchange);\n\n\n}).call(this, document) });\n\n </script>\n";
};
Tudor = (function() {
var invisibles;
Tudor.prototype.I = 'Tudor';

@@ -175,86 +511,191 @@

Tudor.prototype.jobs = [];
Tudor.prototype.articles = [];
function Tudor(opt) {
if (opt == null) {
opt = {};
}
this.opt = opt != null ? opt : {};
this["do"] = bind(this["do"], this);
switch (opt.format) {
switch (this.opt.format) {
case 'html':
this.header = '<a href="#end" id="top">\u2b07</a>';
this.footer = '\n<a href="#top" id="end">\u2b06</a>';
this.pageHead = function(summary) {
return "<style>\n body { font-family: sans-serif; }\n a { outline: 0; }\n b { display: inline-block; width: .7em }\n\n b.pass { color: #393 }\n b.fail { color: #bbb }\n article.fail b.pass { color: #bbb }\n section.fail b.pass { color: #bbb }\n\n pre { padding: .5em; margin: .2em 0; border-radius: 4px; }\n pre.fn { background-color: #fde }\n pre.pass { background-color: #cfc }\n pre.fail { background-color: #d8e0e8 }\n\n article { margin-bottom: .5rem }\n article h2 { padding-left:.5rem; margin:0; font-weight:normal }\n article.pass { border-left: 5px solid #9c9 }\n article.fail { border-left: 5px solid #9bf }\n article.fail h2 { margin-bottom: .5rem }\n article.pass >div { display: none }\n\n section { margin-bottom: .5rem }\n section h3 { padding-left: .5rem; margin: 0; }\n section.pass { border-left: 3px solid #9c9 }\n section.fail { border-left: 3px solid #9bf }\n section.fail h3 { margin-bottom: .5rem }\n section.pass >div { display: none }\n\n article.fail section.pass { border-left-color: #ccc }\n\n div { padding-left: .5em; }\n div.fail { border-left: 3px solid #9bf; font-size: .8rem }\n div h4 { margin: 0 }\n div h4 { font: normal .8rem/1.2rem monaco, monospace }\n div.fail, div.fail h4 { margin: .5rem 0 }\n\n</style>\n<h4><a href=\"#end\" id=\"top\">\u2b07</a> " + summary + "</h4>";
};
this.pageFoot = function(summary) {
return "<h4><a href=\"#top\" id=\"end\">\u2b06</a> " + summary + "</h4>\n<script>\n document.title='" + (summary.replace(/<\/?[^>]+>/g, '')) + "';\n</script>";
};
this.articleHead = function(heading, fail) {
return ("<article class=\"" + (fail ? 'fail' : 'pass') + "\">") + ("<h2>" + (fail ? this.cross : this.tick) + heading + "</h2><div>");
};
this.articleFoot = '</div></article>';
this.sectionHead = function(heading, fail) {
return ("<section class=\"" + (fail ? 'fail' : 'pass') + "\">") + ("<h3>" + (fail ? this.cross : this.tick) + heading + "</h3><div>");
};
this.sectionFoot = '</div></section>';
this.jobFormat = function(heading, result) {
return ("<div class=\"" + (result ? 'fail' : 'pass') + "\">") + ("<h4>" + (result ? this.cross : this.tick) + heading + "</h4>") + ("" + (result ? this.formatError(result) : '')) + "</div>";
};
this.tick = '<b class="pass">\u2713</b> ';
this.cross = '<b class="fail">\u2718</b> ';
break;
default:
this.header = '\u2b07';
this.footer = '\n\u2b06';
this.pageHead = function(summary) {
return "" + summary;
};
this.pageFoot = function(summary) {
return "\n" + summary;
};
this.articleHead = function(heading, fail) {
return "\n" + (fail ? this.cross : this.tick) + " " + heading + "\n===" + (new Array(heading.length).join('=')) + "\n";
};
this.articleFoot = '';
this.sectionHead = function(heading, fail) {
return "\n" + (fail ? this.cross : this.tick) + " " + heading + "\n---" + (new Array(heading.length).join('-')) + "\n";
};
this.sectionFoot = '';
this.jobFormat = function(heading, result) {
return ((result ? this.cross : this.tick) + " " + heading) + ("" + (result ? '\n' + this.formatError(result) : ''));
};
this.jobFoot = '';
this.tick = '\u2713';
this.cross = '\u2718';
}
}
Tudor.prototype["do"] = function() {
var actual, double, expect, j, job, len, md, name, ref, result, runner, summary, tallies;
md = [];
tallies = [0, 0];
double = null;
ref = this.jobs;
for (j = 0, len = ref.length; j < len; j++) {
job = ref[j];
switch (ª.type(job)) {
case 'function':
double = job(double);
Tudor.prototype.add = function(lines) {
var article, i, line, runner, section;
article = {
sections: []
};
runner = null;
section = null;
if (ªA !== ªtype(lines)) {
throw new Error("`lines` isn’t an array");
}
if (0 === lines.length) {
throw new Error("`lines` has no elements");
}
if (ªS !== ªtype(lines[0])) {
throw new Error("`lines[0]` isn’t a string");
}
article.heading = lines.shift();
i = 0;
while (i < lines.length) {
line = lines[i];
switch (ªtype(line)) {
case ªO:
if (!line.runner) {
throw new Error("Errant object");
}
runner = line.runner;
break;
case 'string':
md.push(job);
case ªF:
section.jobs.push(line);
break;
case 'array':
runner = job[0], name = job[1], expect = job[2], actual = job[3];
result = runner(expect, actual, double);
if (!result) {
md.push("\u2713 " + name + " ");
tallies[0]++;
case ªS:
if (this.isAssertion(lines[i + 1], lines[i + 2])) {
if (!section) {
throw new Error("Cannot add an assertion here");
}
section.jobs.push([runner, line, lines[++i], lines[++i]]);
} else {
md.push("\u2718 " + name + " ");
md.push(" " + result + " ");
tallies[1]++;
section = {
heading: line,
jobs: []
};
article.sections.push(section);
}
}
summary = " passed " + tallies[0] + "/" + (tallies[0] + tallies[1]) + " ";
summary += tallies[1] ? '\u2718' : '\u2714';
i++;
}
md.unshift(this.header + summary);
md.push(this.footer + summary);
return md.join('\n');
return this.articles.push(article);
};
Tudor.prototype.page = function(text) {
return this.jobs.push(("\n\n" + text + "\n=") + (new Array(text.length).join('=')));
Tudor.prototype["do"] = function() {
var actual, art, artFail, artPass, article, e, error, expect, heading, j, job, k, l, len, len1, len2, mock, pge, pgeFail, pgePass, ref, ref1, ref2, result, runner, sec, secFail, secPass, section, summary;
pge = [];
mock = null;
pgePass = pgeFail = 0;
ref = this.articles;
for (j = 0, len = ref.length; j < len; j++) {
article = ref[j];
art = [];
artPass = artFail = 0;
ref1 = article.sections;
for (k = 0, len1 = ref1.length; k < len1; k++) {
section = ref1[k];
sec = [];
secPass = secFail = 0;
ref2 = section.jobs;
for (l = 0, len2 = ref2.length; l < len2; l++) {
job = ref2[l];
switch (ªtype(job)) {
case ªF:
try {
mock = job(mock);
} catch (_error) {
e = _error;
error = e.message;
}
if (error) {
sec.push(this.formatMockModifierError(job, error));
}
break;
case ªA:
runner = job[0], heading = job[1], expect = job[2], actual = job[3];
result = runner(expect, actual, mock);
if (!result) {
sec.push(this.jobFormat("" + (this.sanitize(heading))));
pgePass++;
artPass++;
secPass++;
} else {
sec.push(this.jobFormat("" + (this.sanitize(heading)), result));
pgeFail++;
artFail++;
secFail++;
}
}
}
sec.unshift(this.sectionHead("" + (this.sanitize(section.heading)), secFail));
sec.push(this.sectionFoot);
art = art.concat(sec);
}
art.unshift(this.articleHead("" + (this.sanitize(article.heading)), artFail));
art.push(this.articleFoot);
pge = pge.concat(art);
summary = pgeFail ? this.cross + " FAILED " + pgeFail + "/" + (pgePass + pgeFail) : this.tick + " Passed " + pgePass + "/" + (pgePass + pgeFail);
}
pge.unshift(this.pageHead(summary));
pge.push(this.pageFoot(summary));
return pge.join('\n');
};
Tudor.prototype.section = function(text) {
return this.jobs.push(("\n\n" + text + "\n-") + (new Array(text.length).join('-')) + '\n');
};
Tudor.prototype.custom = function(tests, runner) {
var i;
i = 0;
while (i < tests.length) {
if ('function' === ª.type(tests[i])) {
this.jobs.push(tests[i]);
} else {
this.jobs.push([runner, tests[i], tests[++i], tests[++i]]);
}
i++;
Tudor.prototype.formatError = function(result) {
switch (result.length + "-" + this.opt.format) {
case '2-html':
return result[0] + "\n<pre class=\"fail\">" + (this.sanitize(result[1].message)) + "</pre>";
case '2-plain':
return result[0] + "\n" + (this.sanitize(result[1].message));
case '3-html':
return "<pre class=\"fail\">" + (this.sanitize(this.reveal(result[0]))) + "</pre>\n...was " + result[1] + ", but expected...\n<pre class=\"pass\">" + (this.sanitize(this.reveal(result[2]))) + "</pre>";
case '3-plain':
return (this.sanitize(this.reveal(result[0]))) + "\n...was " + result[1] + ", but expected...\n" + (this.sanitize(this.reveal(result[2])));
case '4-html':
return "<pre class=\"fail\">" + (this.sanitize(this.reveal(result[0]))) + " (" + (ªtype(result[0])) + ")</pre>\n...was " + result[1] + ", but expected...\n<pre class=\"pass\">" + (this.sanitize(this.reveal(result[2]))) + " (" + (ªtype(result[2])) + ")</pre>";
case '4-plain':
return (this.sanitize(this.reveal(result[0]))) + " (" + (ªtype(result[0])) + ")\n...was " + result[1] + ", but expected...\n" + (this.sanitize(this.reveal(result[2]))) + " (" + (ªtype(result[2])) + ")";
default:
throw new Error("Cannot process '" + result.length + "-" + this.opt.format + "'");
}
return this.jobs.push('- - -');
};
Tudor.prototype.fail = function(result, delivery, expect, types) {
if (types) {
result = (invisibles(result)) + " (" + (ª.type(result)) + ")";
expect = (invisibles(expect)) + " (" + (ª.type(expect)) + ")";
Tudor.prototype.formatMockModifierError = function(fn, error) {
switch (this.opt.format) {
case 'html':
return "<pre class=\"fn\">" + (this.sanitize(fn + '')) + "</pre>\n...encountered an exception:\n<pre class=\"fail\">" + (this.sanitize(error)) + "</pre>";
default:
return (this.sanitize(fn + '')) + "\n...encountered an exception:\n" + (this.sanitize(error));
}
return (invisibles(result)) + "\n ...was " + delivery + ", but expected...\n " + (invisibles(expect));
};
invisibles = function(value) {
Tudor.prototype.reveal = function(value) {
return value != null ? value.toString().replace(/^\s+|\s+$/g, function(match) {

@@ -265,62 +706,79 @@ return '\u00b7' + (new Array(match.length)).join('\u00b7');

Tudor.prototype.throws = function(tests) {
return this.custom(tests, (function(_this) {
return function(expect, actual, double) {
var e, error;
error = false;
try {
actual(double);
} catch (_error) {
e = _error;
error = e.message;
}
if (!error) {
return "No exception thrown, expected...\n " + expect;
} else if (expect !== error) {
return _this.fail(error, 'thrown', expect);
}
};
})(this));
Tudor.prototype.sanitize = function(value) {
switch (this.opt.format) {
case 'html':
return value != null ? value.toString().replace(/</g, '&lt;') : void 0;
default:
return value;
}
};
Tudor.prototype.equal = function(tests) {
return this.custom(tests, (function(_this) {
return function(expect, actual, double) {
var e, error, result;
error = false;
try {
result = actual(double);
} catch (_error) {
e = _error;
error = e.message;
}
if (error) {
return "Unexpected exception...\n " + error;
} else if (expect !== result) {
return _this.fail(result, 'returned', expect, result + '' === expect + '');
}
};
})(this));
Tudor.prototype["throw"] = {
runner: function(expect, actual, mock) {
var e, error;
error = false;
try {
actual(mock);
} catch (_error) {
e = _error;
error = e;
}
if (!error) {
return ['No exception thrown, expected', expect];
} else if (expect !== error.message) {
return [error.message, 'thrown', expect];
}
}
};
Tudor.prototype.is = function(tests) {
return this.custom(tests, (function(_this) {
return function(expect, actual, double) {
var e, error, result;
error = false;
try {
result = actual(double);
} catch (_error) {
e = _error;
error = e.message;
Tudor.prototype.equal = {
runner: function(expect, actual, mock) {
var e, error, result;
error = false;
try {
result = actual(mock);
} catch (_error) {
e = _error;
error = e;
}
if (error) {
return ['Unexpected exception', error];
} else if (expect !== result) {
if (result + '' === expect + '') {
return [result, 'returned', expect, true];
} else {
return [result, 'returned', expect];
}
if (error) {
return "Unexpected exception...\n " + error;
} else if (expect !== ª.type(result)) {
return _this.fail("type " + (ª.type(result)), 'returned', "type " + expect);
}
};
})(this));
}
}
};
Tudor.prototype.is = {
runner: function(expect, actual, mock) {
var e, error, result;
error = false;
try {
result = actual(mock);
} catch (_error) {
e = _error;
error = e;
}
if (error) {
return ['Unexpected exception', error];
} else if (expect !== ªtype(result)) {
return ["type " + (ªtype(result)), 'returned', "type " + expect];
}
}
};
Tudor.prototype.isAssertion = function(line1, line2) {
if (ªF !== ªtype(line2)) {
return false;
}
if ((ªO === ªtype(line1)) && ªF === ªtype(line1.runner)) {
return false;
}
return true;
};
return Tudor;

@@ -331,3 +789,3 @@

tudor = new Tudor({
format: O === typeof window ? 'html' : 'plain'
format: ªO === typeof window ? 'html' : 'plain'
});

@@ -337,28 +795,14 @@

tudor.page("`Apage` Constructor Usage");
tudor.section("No `config` Argument");
tudor.is([
"Class is a function", F, function() {
tudor.add([
"01 Main Constructor Usage", "No `config` Argument", function() {
return new Main;
}, tudor.is, "Class is a function", ªF, function() {
return Main;
}, function() {
return new Main;
}, "Instance is an object", O, function(mock) {
}, "Instance is an object", ªO, function(mock) {
return mock;
}
]);
tudor.equal([
"`toString()` is '[object Apage]'", '[object Apage]', function(mock) {
}, tudor.equal, "`toString()` is '[object Apage]'", '[object Apage]', function(mock) {
return '' + mock;
}, "`config` is null", '[object Apage]', function() {
return '' + new Main(null);
}
]);
tudor.section("Basic `config`");
tudor.equal([
"Set the title", '[object Apage]', function() {
}, "Basic `config`", "Set the title", '[object Apage]', function() {
return '' + new Main({

@@ -370,8 +814,6 @@ title: 'Café'

tudor.page("`Apage` Constructor Errors");
tudor.section("Invalid `config` Argument");
tudor.throws([
"`config` is not an object", "Invalid `config`:\n `candidate` is type 'number' not 'object'", function() {
tudor.add([
"02 `Apage` Constructor Errors", "Invalid `config` argument", function() {
return new Main;
}, tudor["throw"], "`config` is not an object", "Invalid `config`:\n `candidate` is type 'number' not 'object'", function() {
return new Main(1);

@@ -382,21 +824,15 @@ }, "`config` is a `Date` object", "Invalid `config`:\n `candidate` is type 'date' not 'object'", function() {

return new Main(new String('yikes!'));
}
]);
tudor.section("Invalid `config.title`");
tudor.throws([
"A number", "Invalid `config`:\n Key 'title' is type 'number' not 'string'", function() {
}, "Invalid `config.title`", "A number", "Invalid `config`:\n Field 'title' is type 'number' not 'string'", function() {
return new Main({
title: 0
});
}, "An empty string", "Invalid `config`:\n Key 'title' fails /^[^\\x00-\\x1F]{1,24}$/", function() {
}, "An empty string", "Invalid `config`:\n Field 'title' fails /^[^\\x00-\\x1F]{1,24}$/", function() {
return new Main({
title: ''
});
}, "25 characters long", "Invalid `config`:\n Key 'title' fails /^[^\\x00-\\x1F]{1,24}$/", function() {
}, "25 characters long", "Invalid `config`:\n Field 'title' fails /^[^\\x00-\\x1F]{1,24}$/", function() {
return new Main({
title: '1234567890123456789012345'
});
}, "Contains a tab", "Invalid `config`:\n Key 'title' fails /^[^\\x00-\\x1F]{1,24}$/", function() {
}, "Contains a tab", "Invalid `config`:\n Field 'title' fails /^[^\\x00-\\x1F]{1,24}$/", function() {
return new Main({

@@ -408,28 +844,14 @@ title: 'tab character: \t'

tudor.page("`apage.config()` Usage");
tudor.section("No argument");
tudor.is([
function() {
tudor.add([
"03 `apage.config()` Usage", "No argument", function() {
return new Main;
}, "`config()` is a function", F, function(mock) {
}, tudor.is, "`config()` is a function", ªF, function(mock) {
return mock.config;
}, "Returns an object", O, function(mock) {
}, "Returns an object", ªO, function(mock) {
return mock.config();
}
]);
tudor.equal([
"Returned object contains expected defaults", '{"title":"Untitled"}', function(mock) {
}, tudor.equal, "Returned object contains expected defaults", '{"title":"Untitled","url":false,"plugin":""}', function(mock) {
return JSON.stringify(mock.config());
}, "Returns a new object each time", false, function(mock) {
return mock.config() === mock.config();
}
]);
tudor.section("Retrieve a config value");
tudor.equal([
"Default `title` is 'Untitled'", 'Untitled', function(mock) {
}, "Retrieve a config value", "Default `title` is 'Untitled'", 'Untitled', function(mock) {
return mock.config('title');

@@ -444,9 +866,3 @@ }, function() {

return mock.config('unrecognized');
}
]);
tudor.section("Set a valid config value");
tudor.equal([
"Set a Chinese `title`, two arg syntax, returns `undefined`", void 0, function(mock) {
}, "Set a valid config value", "Set a Chinese `title`, two arg syntax, returns `undefined`", void 0, function(mock) {
return mock.config('title', '語文教學・语文教学');

@@ -461,41 +877,294 @@ }, "Check that Chinese `title` has been set", '語文教學・语文教学', function(mock) {

return mock.config('title');
}, "Set an invalid config value", tudor.is, "Returns an array", ªA, function(mock) {
return mock.config('title', '');
}, tudor.equal, "The array has one element", 1, function(mock) {
return (mock.config('title', '')).length;
}, "The array element is an expected error message", "Field 'title' fails /^[^\\x00-\\x1F]{1,24}$/", function(mock) {
return (mock.config('title', ''))[0];
}
]);
tudor.section("Set an invalid config value");
tudor.add([
"05 `apage.render()` Usage", "No argument", function() {
return new Main;
}, tudor.is, "`render()` is a function", ªF, function(mock) {
return mock.render;
}, "Returns a string", ªS, function(mock) {
return mock.render();
}, tudor.equal, "Returned string is expected length", 468, function(mock) {
return mock.render().length;
}, "Characters up to opening <title> as expected", "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <title>", function(mock) {
return mock.render().substr(0, 49);
}, "Default <title> is 'Untitled'", 'Untitled', function(mock) {
return mock.render().substr(49, 8);
}, "Characters from </title> to </html> as expected", "</title>\n <meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">\n <meta name=\"generator\" content=\"Apage " + ªV + " http://apage.richplastow.com/\">\n <style>\n /* Apage was configured with no plugins, so no CSS is injected here */\n </style>\n\n</head>\n<body>\n\n <!-- Apage was rendered with no articles -->\n\n <!-- Apage was configured with no plugins, so nothing is injected here -->\n\n</body>\n</html>", function(mock) {
return mock.render().substr(57);
}, "Shorter `title` changes string length", 462, function(mock) {
mock.config('title', 'OK');
return mock.render().length;
}
]);
tudor.is([
"Returns an array", A, function(mock) {
return mock.config('title', '');
tudor.add([
"07 `apage.add()` and `apage.browse()` Typical Usage", "Basic checks and usage, with zero articles", function() {
return new Main;
}, tudor.is, "`add()` is a function", ªF, function(mock) {
return mock.add;
}, "`add()` returns an object", ªO, function(mock) {
return mock.add();
}, "`browse()` is a function", ªF, function(mock) {
return mock.browse;
}, "`browse()` returns an array", ªA, function(mock) {
return mock.browse();
}, "Returned `browse()` array is not a shared reference", ªU, function(mock) {
var a;
a = mock.browse();
a.foo = 1;
return mock.browse().foo;
}, tudor.equal, "Returned `add()` object is the instance itself", true, function(mock) {
return mock.add() === mock;
}, "Returned `browse()` array is initially empty", 0, function(mock) {
return mock.browse().length;
}, "Adding some articles", "A very minimal article with numeric name", "<article id=\"apage_123\"\n class=\"apage\"\n data-apage-opath=\"/123.md\"\n data-apage-dname=\"_\"\n data-apage-order=\"123\"\n data-apage-front='[]'\n data-apage-title=\"\">\n \n </article><!-- / #apage_123 -->", function(mock) {
return mock.add({
path: '123.md'
}).render().substr(329, 206);
}, "`browse().length` after adding one article", 1, function(mock) {
return mock.browse().length;
}, "Use `browse()` to get article ‘apage_123’s id", 'apage_123', function(mock) {
return mock.browse()[0].id;
}, "Use `browse()` to get article ‘apage_123’s order", 123, function(mock) {
return mock.browse()[0].order;
}, "A `browse()` element is not a shared reference", void 0, function(mock) {
var o;
o = mock.browse()[0];
o.foo = 1;
return mock.browse()[0].foo;
}, "A fairly minimal article with an ordered name", "<article id=\"apage_a_b\"\n class=\"apage\"\n data-apage-opath=\"/A/5-b.c.MaRkDoWn\"\n data-apage-dname=\"_A_\"\n data-apage-order=\"5\"\n data-apage-front='[[\"foo\",\"bar\"]]'\n data-apage-title=\"Ok\">\n <h1>Ok</h1>\n <p>Lorem.</p>\n \n </article><!-- / #apage_a_b -->", function(mock) {
return mock.add({
path: 'A/5-b.c.MaRkDoWn',
raw: '---\nfoo:bar\n---\nOk\n==\nLorem.'
}).render().substr(329, 261);
}, "Use `browse()` to get article ‘apage_a_b’s id", 'apage_a_b', function(mock) {
return mock.browse()[0].id;
}, "Use `browse()` to get article ‘apage_a_b’s order", 5, function(mock) {
return mock.browse()[0].order;
}, "An article which uses frontmatter to override defaults", "<article id=\"apage_foo\"\n class=\"apage\"\n data-apage-opath=\"/7-foo.txt\"\n data-apage-dname=\"_\"\n data-apage-order=\"2\"\n data-apage-front='[[\"My Custom\",\"Field : Here\"]]'\n data-apage-title=\"Foo\">\n <p>Not the title</p>\n \n </article><!-- / #apage_foo -->", function(mock) {
return mock.add({
path: '7-foo.txt',
raw: "---\n id : apage_foo \n title:Foo \norder:02.2\n My Custom : Field : Here \n---\nNot the title"
}).render().substr(329, 263);
}, "Use `browse()` to get article ‘apage_foo’s id", 'apage_foo', function(mock) {
return mock.browse()[0].id;
}, "Use `browse()` to get article ‘apage_foo’s order", 2, function(mock) {
return mock.browse()[0].order;
}, "`browse()` orders after adding three articles", '2 5 123 undefined', function(mock) {
var browse;
browse = mock.browse();
return browse[0].order + " " + browse[1].order + " " + browse[2].order + " " + browse[3];
}, "Ordering falls back to `id`, when `order` is duplicate", "\"id\": \"apage_2\",\n\"order\": 2\n\"id\": \"apage_e\",\n\"order\": 2\n\"id\": \"apage_foo\",\n\"order\": 2\n\"id\": \"apage_g\",\n\"order\": 2\n\"id\": \"apage_h\",\n\"order\": 2\n\"id\": \"apage_a_b\",\n\"order\": 5\n\"id\": \"apage_123\",\n\"order\": 123", function(mock) {
return JSON.stringify(mock.add({
path: '2-h.md'
}).add({
path: '2-e.md'
}).add({
path: '2.md'
}).add({
path: '2-G.md'
}).browse(), null, 2).replace(/\s \},\n \{\n| /g, '').slice(6, -6);
}
]);
tudor.equal([
"The array has one element", 1, function(mock) {
return (mock.config('title', '')).length;
}, "The array element is an expected error message", "Key 'title' fails /^[^\\x00-\\x1F]{1,24}$/", function(mock) {
return (mock.config('title', ''))[0];
tudor.add([
"08 `apage.read()`, `edit()` and `destroy()` Usage", "Basic checks", function() {
return new Main;
}, tudor.is, "`read()` is a function", ªF, function(mock) {
return mock.read;
}, "`edit()` is a function", ªF, function(mock) {
return mock.edit;
}, "`destroy()` is a function", ªF, function(mock) {
return mock.destroy;
}, "Basic exceptions, with no articles", function() {
return new Main;
}, tudor["throw"], "`read()` without an argument", "`identifier` is `undefined`", function(mock) {
return mock.read();
}, "`edit()` without any arguments", "`identifier` is `undefined`", function(mock) {
return mock.edit();
}, "`destroy()` without an argument", "`identifier` is `undefined`", function(mock) {
return mock.destroy();
}, "`read()` with an invalid identifier", "`identifier` is type 'boolean'", function(mock) {
return mock.read(true);
}, "`edit()` with an invalid identifier", "`identifier` is type 'array'", function(mock) {
return mock.edit([]);
}, "`destroy()` with an invalid identifier", "`identifier` is type 'function'", function(mock) {
return mock.destroy(function() {
return 123;
});
}, "`read()` with a non-existant numeric identifier", "`0` does not exist", function(mock) {
return mock.read(0);
}, "`edit()` with a non-existant numeric identifier", "`918` does not exist", function(mock) {
return mock.edit(918);
}, "`destroy()` with a non-existant numeric identifier", "`44.12` does not exist", function(mock) {
return mock.destroy(44.12);
}, "`read()` with a non-existant string identifier", "'' does not exist", function(mock) {
return mock.read('');
}, "`edit()` with a non-existant string identifier", "'abc' does not exist", function(mock) {
return mock.edit('abc');
}, "`destroy()` with a non-existant string identifier", "'3' does not exist", function(mock) {
return mock.destroy('3');
}, "Basic usage", tudor.equal, function(mock) {
mock.add({
path: 'foo.txt'
});
return mock;
}, "Can retrieve using `read()`", '{"id":"apage_foo","path":"foo.txt","order":"foo"}', function(mock) {
return JSON.stringify(mock.read(0));
}, "The retrieved object is a clone, not a reference", '{"id":"apage_foo","path":"foo.txt","order":"foo"}', function(mock) {
var art;
art = mock.read(0);
art.order = 123;
return JSON.stringify(mock.read(0));
}, "Can change properties using `edit()`", 'new-order', function(mock) {
mock.edit(0, {
order: 'new-order'
});
return (mock.read(0)).order;
}, tudor["throw"], "`edit()` cannot change properties to invalid values", "Invalid `amend`:\n Field 'id' is type 'null' not 'string'", function(mock) {
return mock.edit(0, {
order: 'another',
id: null
});
}, "Multiple `edit()` errors are reported", "Invalid `amend`:\n Field 'id' is type 'null' not 'string'\n Field 'order' is type 'number' not 'string'", function(mock) {
return mock.edit(0, {
order: -1,
id: null
});
}, tudor.equal, "`edit()` is atomic, so the previous calls did not change `order`", 'new-order', function(mock) {
return (mock.read(0)).order;
}, "Can delete using `destroy()`", 0, function(mock) {
return (mock.destroy(0)).browse().length;
}
]);
tudor.page("`apage.render()` Usage");
tudor.add([
"09 `apage.add()` Advanced Usage", "Adding articles with non-mandatory config keys", function() {
return new Main;
}, tudor.equal, "`id` can be set explicitly during instantiation", '{"id":"apage_xyz","path":"abc.md","order":"abc"}', function(mock) {
mock.add({
path: 'abc.md',
id: 'apage_xyz'
});
return JSON.stringify(mock.read(0));
}
]);
tudor.section("No argument");
tudor.is([
function() {
tudor.add([
"10 `apage.add()` Errors", "Invalid `article` argument", function() {
return new Main;
}, "`render()` is a function", F, function(mock) {
return mock.render;
}, "Returns a string", S, function(mock) {
return mock.render();
}, tudor["throw"], "`config` is a number", "Invalid `config`:\n `candidate` is type 'number' not 'object'", function(mock) {
return mock.add(456);
}, tudor.equal, "Invalid object is not added", 0, function(mock) {
return mock.browse().length;
}, "Invalid `path` field", tudor["throw"], "`path` is not set", "Invalid `config`:\n Missing field 'path' is mandatory", function(mock) {
return mock.add({});
}, "`path` is a boolean", "Invalid `config`:\n Field 'path' is type 'boolean' not 'string'", function(mock) {
return mock.add({
path: true
});
}, "`path` is an empty string", "Invalid `config`:\n Field 'path' fails /^[a-z0-9][-\\/a-z0-9]{0,63}\\.[.a-z0-9]+$/i", function(mock) {
return mock.add({
path: ''
});
}, "`path` is too long", "Invalid `config`:\n Field 'path' fails /^[a-z0-9][-\\/a-z0-9]{0,63}\\.[.a-z0-9]+$/i", function(mock) {
return mock.add({
path: (new Array(66)).join('-')
});
}, "`path` contains a backslash", "Invalid `config`:\n Field 'path' fails /^[a-z0-9][-\\/a-z0-9]{0,63}\\.[.a-z0-9]+$/i", function(mock) {
return mock.add({
path: 'a\\b'
});
}, "`path` contains an underscore", "Invalid `config`:\n Field 'path' fails /^[a-z0-9][-\\/a-z0-9]{0,63}\\.[.a-z0-9]+$/i", function(mock) {
return mock.add({
path: 'a_b'
});
}, "`path` begins with a hyphen", "Invalid `config`:\n Field 'path' fails /^[a-z0-9][-\\/a-z0-9]{0,63}\\.[.a-z0-9]+$/i", function(mock) {
return mock.add({
path: '-a'
});
}, "Duplicate `id`, due to identical `path` slugs", "'apage_a' already exists", function(mock) {
mock.add({
path: '00-A.txt'
});
return mock.add({
path: '3-a.jpeg'
});
}, "Invalid `raw` field", "`raw` is an object", "Invalid `config`:\n Field 'raw' is type 'object' not 'string'", function(mock) {
return mock.add({
path: 'a.md',
raw: {}
});
}, "`raw` is too long", "Invalid `config`:\n Field 'raw' fails /^[^\\x00-\\x08\\x0E-\\x1F]{0,10000}$/", function(mock) {
return mock.add({
path: 'b.md',
raw: (new Array(10002)).join('-')
});
}, "`raw` contains an invalid character", "Invalid `config`:\n Field 'raw' fails /^[^\\x00-\\x08\\x0E-\\x1F]{0,10000}$/", function(mock) {
return mock.add({
path: 'b.md',
raw: 'x\bz'
});
}, tudor.equal, "Invalid objects still not added", 1, function(mock) {
return mock.browse().length;
}
]);
tudor.equal([
"Returned string is expected length", 2657, function(mock) {
return mock.render().length;
}, "Shorter `title` changes string length", 2645, function(mock) {
mock.config('title', 'OK');
return mock.render().length;
tudor.add([
"11 App Helpers, Errors and Usage: `parseFilename()`", "Basic checks and errors", tudor.is, "`parseFilename()` is a function", ªF, function() {
return parseFilename;
}, tudor["throw"], "Called with no arguments", '`nm` is undefined, not string', function() {
return parseFilename();
}, "Called with wrong `fn` type", '`nm` is number, not string', function() {
return parseFilename(1);
}, "Called with unrecognised `part` string", "`part` not recognised, use 'order|title|slug|ext'", function() {
return parseFilename('abc', 123);
}, tudor.equal, "Typical 'order' usage", "Get the order from a typical filename", 3, function() {
return parseFilename('03-The Third.txt', 'order');
}, "Fractional orders are not allowed", 3, function() {
return parseFilename('3.1-Only whole numbers allowed.txt', 'order');
}, "The order must only contain digits", '3b-must-all-be-digits', function() {
return parseFilename('3b-Must all be digits.md.txt', 'order');
}, "Typical 'title' usage", "Get the title from a typical filename", 'The Third', function() {
return parseFilename('03-The Third.txt', 'title');
}, "Fractional orders are not allowed", '3', function() {
return parseFilename('3.1-Only whole numbers allowed.txt', 'title');
}, "The order must only contain digits", '3b-Must all be digits', function() {
return parseFilename('3b-Must all be digits.md.txt', 'title');
}, "Typical 'slug' usage", "Get the slug from a typical filename", 'the-third', function() {
return parseFilename('03-The Third.txt', 'slug');
}, "Fractional orders are not allowed", '3', function() {
return parseFilename('3.1-Only whole numbers allowed.txt', 'slug');
}, "The order must only contain digits", '3b-must-all-be-digits', function() {
return parseFilename('3b-Must all be digits.md.txt', 'slug');
}, "Typical 'ext' usage", "Get the ext from a typical filename", 'txt', function() {
return parseFilename('03-The Third.txt', 'ext');
}, "Fractional orders are not allowed", '1-Only whole numbers allowed.txt', function() {
return parseFilename('3.1-Only whole numbers allowed.txt', 'ext');
}, "The order must only contain digits", 'md.txt', function() {
return parseFilename('3b-Must all be digits.md.txt', 'ext');
}, "Typical usage with no 'part' argument", "Get the order from a typical filename", 3, function() {
return (parseFilename('03-The Third.txt')).order;
}, "Fractional orders are not allowed", '3', function() {
return (parseFilename('3.1-Only whole numbers allowed.txt')).slug;
}, "The order must only contain digits", 'md.txt', function() {
return (parseFilename('3b-Must all be digits.md.txt')).ext;
}, "Edge case orders", "Filename is all digits", 123456, function() {
return parseFilename('123456.txt', 'order');
}, "Advanced slugification", "Leading, trailing, and multiple whitespace is removed", 'spaces-and-tabs-before-and-after', function() {
return parseFilename(' \t spaces and tabs before and after \t ', 'slug');
}, "Some punctuation is removed", 'its-too-punctuated', function() {
return parseFilename('“It’s too, ‘punctuated’”. It’s kept here!', 'slug');
}, "Some punctuation marks are converted to hyphens", 'em-en-etc', function() {
return parseFilename('em —, en – … etc.', 'slug');
}, "Some accents are removed", 'cafe-cafe', function() {
return parseFilename('CAFÉ, café', 'slug');
}

@@ -502,0 +1171,0 @@ ]);

{
"name": "apage",
"title": "Apage",
"version": "0.0.3",
"version": "0.0.15",
"license": "MIT",
"homepage": "http://apage.richplastow.com/",
"author": "Rich Plastow <rich@richplastow.com> (http://richplastow.com/)",
"description": "Generate a page from lots of markdown",
"main": "build/apage.js",
"description": "A single-page website from lots of markdown",
"main": "./build/apage.js",
"bin":{
"apage": "./bin/apage",
"akaybe-build": "./bin/akaybe-build"
},
"preferGlobal": true,
"repository": {

@@ -23,5 +27,10 @@ "type": "git",

"generator",
"jekyll",
"frontmatter",
"coffeescript",
"litcoffee"
],
"dependencies": {
"marked": "latest"
},
"devDependencies": {

@@ -32,10 +41,8 @@ "coffee-script": "latest",

"scripts": {
"build": "node bin/akaybe-build --plugin",
"test": "node test/run-test.js",
"source": "npm run v:head --loglevel silent | cat - node_modules/akaybe/src/*.litcoffee src/*.litcoffee",
"build": "npm run source --loglevel silent | coffee --stdio --literate --compile > build/apage.js",
"watch": "nodemon -x \"npm run source --loglevel silent | cat - test/*.litcoffee | coffee -slc > build/test/apage-with-test.js\" -e litcoffee -w src -w test",
"v:list": "grep -ron $npm_package_version * | awk 1 ORS=' ' | sed \"s@:$npm_package_version@@g\"",
"v:open": "subl $(npm run v:list --loglevel silent)",
"v:head": "echo \" ###! $npm_package_title $npm_package_version //// $npm_package_license Licence //// $npm_package_homepage ###\\n I = '$npm_package_title'\\n V = '$npm_package_version'\\n\""
"index": "./bin/apage -i README.md doc/*.md -o index.html -t Apage -p \"`cat build/plugin/*.js`\"",
"v:list": "grep -ron $npm_package_version {bin,src,*.json} | awk 1 ORS=' ' | sed \"s@:$npm_package_version@@g\"",
"v:open": "subl $(npm run v:list --loglevel silent)"
}
}

@@ -1,6 +0,9 @@

Apage 0.0.3
===========
Apage
=====
#### Apage generates a single HTML page from multiple markdown sources.
#### Apage generates a single HTML page from multiple markdown sources
[Apage Docs](http://apage.richplastow.com/)
- [Homepage](http://apage.richplastow.com/)
- [Documentation](http://apage.richplastow.com/#/doc/documentation)
- [Test](http://apage.richplastow.com/test/run-test.html)
- [Fork Apage on GitHub](https://github.com/richplastow/apage)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc