├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── convert.md ├── documentation.md ├── index.js ├── lprc.js ├── package-lock.json ├── package.json ├── project.md ├── setup.md ├── test.js └── tests ├── blackwhitehats ├── .gitignore ├── blackwhitehats.md ├── canonical │ └── build │ │ └── hats.js └── original │ └── blackwhitehats.md ├── cheerio ├── .gitignore ├── canonical │ ├── build │ │ └── out.html │ └── out.test └── cheers.md ├── cinnamon ├── .gitignore ├── canonical │ └── build │ │ ├── cinnamon.css │ │ ├── cinnamon.html │ │ ├── cinnamon.js │ │ └── cinnamon_emb.js ├── cinnamon.md └── original │ └── cinnamon.md ├── csv ├── .gitignore ├── canonical │ └── build │ │ └── out.csv └── project.md ├── date ├── .gitignore ├── canonical │ └── build │ │ └── out.txt └── project.md ├── fence ├── .gitignore ├── canonical │ └── build │ │ └── fence.txt ├── fence.md └── original │ └── fence.md ├── first ├── .gitignore ├── canonical │ └── build │ │ └── first.txt ├── first.md ├── first.txt ├── second.md └── toread.txt ├── fizzbuzz ├── .gitignore ├── canonical │ └── build │ │ └── fizzbuzz.js ├── fizzbuzz.md └── original │ └── fizzbuzz.md ├── he ├── .gitignore ├── canonical │ └── build │ │ └── out.txt └── project.md ├── integrated ├── .gitignore ├── canonical │ └── build │ │ ├── dev │ │ └── index.html │ │ └── prod │ │ └── index.html ├── integrated.md ├── lprc.js └── lprc.md ├── jstidy ├── .gitignore ├── canonical │ └── build │ │ └── jstidytest.js ├── jstidy.md └── original │ └── jstidy.md ├── lodash ├── .gitignore ├── canonical │ └── build │ │ └── out.txt └── project.md ├── logs ├── .gitignore ├── canonical │ └── build │ │ ├── logs.htm │ │ └── logs.html ├── canonicalold │ └── build │ │ ├── logs.htm │ │ └── logs.html ├── logs.md ├── lprc.js └── original │ └── logs.md ├── lprc.js ├── matrix ├── .gitignore ├── canonical │ └── build │ │ └── out.txt └── matrix.md ├── primes ├── .gitignore ├── canonical │ └── out.test ├── original │ └── primes.md └── primes.md ├── sample ├── .gitignore ├── canonical │ └── build │ │ └── count.js ├── original │ └── sample.md └── sample.md ├── template ├── .gitignore ├── canonical │ └── build │ │ ├── first.html │ │ ├── out.txt │ │ └── snippet ├── original │ ├── simpletemp.md │ └── templating.md ├── simpletemp.md └── templating.md └── testpro ├── .gitignore ├── canonical └── build │ ├── just-talk │ ├── out.txt │ └── testpro.js ├── original ├── period.md └── testpro.md ├── period.md └── testpro.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build 2 | .checksum 3 | cache 4 | tests 5 | test.js 6 | .travis.yml 7 | lprc.js 8 | *.md 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "4.0" 5 | sudo: false 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2020 James Taylor 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # literate-programming [![Build Status](https://travis-ci.org/jostylr/literate-programming.png)](https://travis-ci.org/jostylr/literate-programming) 2 | 3 | 4 | This is the fat command-line client for 5 | [literate-programming-lib](https://github.com/jostylr/literate-programming-lib). 6 | It contains the full functionality for literate programming, including useful 7 | commands such as jshint included in it. For a thin client, 8 | check out 9 | [litpro](https://github.com/jostylr/litpro) 10 | 11 | 12 | Full documentation: [Literate Programming, MD: How to Treat and Prevent Software Project Mess](https://leanpub.com/literate-programming-md) 13 | 14 | This is not done being fully baked, hence v0.9. But this does represent a 15 | significant break from 0.8.4. You can take a look at convert.md for some 16 | observations of mine as I converted from the old version to the new. 17 | 18 | Install using `npm install literate-programming` 19 | 20 | Usage is `./node_modules/bin/litpro file` and it has some command flags. 21 | 22 | If you want a global install so that you just need to write 23 | `literate-programming` then use `npm install -g literate-programming`. 24 | 25 | The library has a full listing of the syntax, commands, and directives. Here 26 | we list the flags and new commands and directives. 27 | 28 | ## Example usage 29 | 30 | Save the following code to file `project.md` and run `literate-programming project.md`. 31 | 32 | # Welcome 33 | 34 | So you want to make a literate program? Let's have a program that outputs 35 | all numbers between 1 to 10. 36 | 37 | Let's save it in file count.js 38 | 39 | [count.js](#Structure "save: | jshint") 40 | 41 | ## Structure 42 | 43 | We have some intial setup. Then we will generate the array of numbers. We 44 | end with outputting the numbers. 45 | 46 | var numarr = [], start=1, end = 11, step = 1; 47 | 48 | _"Loop" 49 | 50 | _"Output" 51 | 52 | ## Output 53 | 54 | At this point, we have the array of numbers. Now we can join them with a 55 | comma and output that to the console. 56 | 57 | console.log("The numbers are: ", numarr.join(", ") ); 58 | 59 | ## Loop 60 | 61 | Set the loop up and push the numbers onto it. 62 | 63 | var i; 64 | for (i = start; i < end; i += step) { 65 | numarr.push(i); 66 | } 67 | 68 | ## Documentation 69 | 70 | For more information, see the 71 | [documentation book](https://leanpub.com/literate-programming-md) 72 | which is free to read online or available for purchase as a PDF. 73 | 74 | Some particularly useful syntax sections are: 75 | 76 | * [command-line flags](https://leanpub.com/literate-programming-md/read#leanpub-auto-command-line-1) 77 | * [directives](https://leanpub.com/literate-programming-md/read#leanpub-auto-directives-1) 78 | * [commands](https://leanpub.com/literate-programming-md/read#leanpub-auto-commands-1) 79 | * [subcommands](https://leanpub.com/literate-programming-md/read#leanpub-auto-subcommands-1) 80 | 81 | 82 | 83 | ## Use and Security 84 | 85 | It is inherently unsecure to compile literate 86 | program documents. No effort has been made to make it secure. Compiling a 87 | literate program using this program is equivalent to running arbitrary code on 88 | your computer. Only compile from trusted sources, i.e., use the same 89 | precautions as running a node module. 90 | 91 | 92 | ## LICENSE 93 | 94 | [MIT-LICENSE](https://github.com/jostylr/literate-programming/blob/master/LICENSE-MIT) 95 | -------------------------------------------------------------------------------- /convert.md: -------------------------------------------------------------------------------- 1 | Are you coming from the old version? There are syntax differences. Here we 2 | list a few tips to help the process. 3 | 4 | Old way: `| clean raw"` takes the raw text of the section and makes it 5 | cleaned up New way: New version does not see the commentary text. So we need 6 | to act on the document itself. We also don't want the blocks to get evaluated 7 | which automatically happens in this version. So we place directives to turn 8 | off that feature: `[off](# "block:")` turns off block reading, 9 | `[on](# "block:")` turns it back on. We then need to do the snippet cutting: 10 | `| raw ## README, !---- | sub \n\ #, \n# |trim` 11 | Raw says to take the raw document and 12 | cut between the two given pieces. We still shift the headings to not be read 13 | as heading and the sub puts it back. 14 | 15 | Moving towards a conventional setup of setup.md containing project files to 16 | be put in home directory (package.json and lprc.js for example) using `litpro 17 | -b . setup.md` then one can do `npm install` and then do `litpro` to process 18 | `project.md` which then calls the other files. 19 | 20 | Convert substitute(...) to sub. In VIM: `:%s/substitute(\([^)]\+\))/sub \1/g` 21 | 22 | Boilerplate. Old syntax, rather wonky, was to use * to indicate a template to 23 | fill in. New version has the compile command. So instead of `float tests*test 24 | template` we would have `_"test template | compile float tests"` All the minor 25 | sections in the template are required, but they can be empty. Instead of the * 26 | in front of the minor, escape the underscore. `_"*:code"` --> `\_":code"` 27 | While the asterisk notation is, in some sense, nicer, the async nature of the 28 | new version made it problematic. 29 | 30 | Beware of h5 and h6 headers; they fill a new role. Reduce the number of hashes. 31 | 32 | Minor blocks should now best be in the form `[name]()` They can appear 33 | anywhere; it is the form that matters. Alternatively, `[name](# ": | ...")` 34 | can be used if there are pipe transformations desired. The key is the leading 35 | colon and having a name. 36 | 37 | The old setup had minors that could have an extension between `: |` and that 38 | would become part of the name. That is not present in the new one. It was not 39 | really needed. Also minors can be referred to in the hash -- just use the 40 | colon as one would, e.g. `[logs.htm](#structure "Save: main")` becomes 41 | `[logs.htm](#structure:main "Save: ")` 42 | 43 | Blocks cannot refer to each other without problems as they will mutually wait 44 | for each other. 45 | 46 | Fencing of code blocks follows commonmark syntax. Be careful about lists too. 47 | 48 | To eval code directly, one can use `| eval _"some code"`. The incoming text is 49 | in the variable `text` and that variable is what the outgoing text is recorded 50 | as. 51 | 52 | We no longer can evaluate the blocks in terms of where we are in the command 53 | input stage. This was always confusing, anyway. Instead use backslashes and 54 | the compile command. 55 | 56 | To access the arguments called by the command line, one can do 57 | `doc.parent.stdin[whatever property]` and one can create whatever property by 58 | doing `- z prop:val` 59 | 60 | The two things above allow one to have the literate program directly doing 61 | computations instead of just creating a script that can be called. No real 62 | reason for this, I suppose, but hey, it works. 63 | 64 | No replacement for 65 | 66 | ``` 67 | \_"*:expected|heading" 68 | ## Heading 69 | 70 | function () { 71 | return this.hblock.heading.split("*")[0]; 72 | } 73 | 74 | [heading](#heading "define: command | | now") 75 | ``` 76 | 77 | 78 | ## Break with previous versions 79 | 80 | This is a complete rewrite. The syntax is simplified so that only the ``_`code 81 | block| function | functionn` `` syntax is needed. Leave off the code block to 82 | just start using functions (leave the pipe). The code block in that syntax 83 | need not be a code block, but could be a user-defined variable name, 84 | 85 | Another break in the syntax is the switch link. This was a link that was on a 86 | line by itself. It was a kind of heading, one that would make a quick separate 87 | code block under the same heading. I find it convenient. But in trying to 88 | match the general parsing of markdown programs, I am moving towards using a 89 | professional markdown parser that would make it difficult to recognize the 90 | positioning of a link, but trivial to parse one. So the switch link will be a 91 | link whose title quote starts with a colon. So an empty directive. It can 92 | still be positioned as always. Also can implement it so that if the 93 | parenthetical is completely empty, then that is a switch. I noticed that that 94 | is what I often do. 95 | 96 | For header purposes, a link's square bracket portion will be returned to the 97 | surrounding block. 98 | 99 | Also headers will have a way to be modified based on their levels. 100 | I have never used levels 5 and 6, for example. 101 | As an example, one could have level 5 headers for tests, docs, and examples, 102 | that could then be compiled and run bey selecting those headers. Not sure yet. 103 | 104 | Also, there is no tracking of the non-significant text. So for example, raw 105 | will not work in the same way. It was always a bit of a hack and now it will 106 | be more so. There can be easily a plugin that will search for the heading and 107 | cut the rest, etc. 108 | 109 | Multiple substitute cycles are no longer supported. I always found it hard to 110 | reason about it and it greatly simplifies the code. If you need that 111 | functionality, it probably is workable with substitutes and the variable 112 | storage introduced. 113 | 114 | The compiled blocks are stored as variables. We can store arbitrary variable 115 | names and so potentially can conflict with block names. You have been warned. 116 | It is all "global" scope though you can use syntax to kind of scope it. Well, 117 | actually we are scoped to the documents, that is `docname::..` gives another 118 | scope which the var setting respects. 119 | 120 | Another break is that block names need to match. There is the main block which 121 | has no minor associated with it and then there are the minors of the block. If 122 | you want the minor commands on the main, then start the code line with `[](# 123 | ":|...")` where the colon is there to indicate a minor directive, but with no 124 | name and no extension, this will signal the main block. Note that this will 125 | overwrite whatever was in the main code block, if anything. Once a block 126 | (minor or not) is switched from, it cannot be added to later. Trust me, this 127 | is a good thing. 128 | -------------------------------------------------------------------------------- /documentation.md: -------------------------------------------------------------------------------- 1 | * **jshint** This takes the input and runs it through JSHint. The command 2 | is of the form 3 | `js stuff | jshint options, globals, shortname, print clean`. 4 | 5 | * The options is an object that corresponds to the [options that JShint 6 | accepts](http://jshint.com/docs/options/); you can use a subcommand to 7 | create the options object if you like. Default is unused:true, else is 8 | their defaults. 9 | * Globals is an array of global 10 | names; if they can be written over, pass in `name:true` instead of 11 | `name`. 12 | * Shortname is the shortname to present in understanding what is being 13 | jshinted. Otherwise, it does its best to give you a cryptic but 14 | informative name. 15 | * If the fourth argument is a boolean, `t()` or `f()` will do it, then 16 | that toggles whether to print the message that it all went smoothly or 17 | not, respectively. The default is to not print it. 18 | * You can override the defaults repeatedly by modifying the 19 | `Folder.plugins.jshint` object with the names: `options`, `globals`, and 20 | `clean`. 21 | * **md** This takes the input as markdown and puts out html. The first 22 | argument is an optional string naming the renderer to use. The other 23 | arguments should be booleans, namely, `f()`, if one does not want 24 | preprocessing/post to occur. The default preprocessors, in order, are 25 | literate programming subs and math subs rendering to katex. 26 | 27 | To create a renderer, you can use Folder.plugins.md.req as the markdoan 28 | object and then render it per the instructions (an options object 29 | `req(options).use(...)`. This is all best done in the lprc.js file. 30 | Store the rendered under the preferred name in plugins.md. 31 | 32 | See the logs test directory and its lprc.js. 33 | * **cheerio** This gives access to the cheerio module, a lightweight node 34 | version of jQuery-esque without the overhead of jsdom. It can't do 35 | everything, but it does most things: 36 | [cheeriojs](https://github.com/cheeriojs/cheerio). To use, the incoming 37 | text is the html doc to modify, the first argument is the selector, the 38 | second the method, and then the arguments to the method, e.g., 39 | `somehtml | cheerio h2.title, .text, Hello there!` 40 | * **ch-replace** This is a convenience method for cheerio. This will use 41 | the first argument as a selector and the second argument as a 42 | html replacement. 43 | * **postcss** This takes incoming text and runs it through postcss. To do 44 | something useful, you need to have the arguments be the commands to use. 45 | At the moment, the only one shipping with this is autoprefixer, but 46 | others are likely to be added (minimizers and fixers, in particular). 47 | You can add them yourself by, in lprcs.js, saying (installing cssnano as 48 | example) 49 | `Folder.plugins.postcss[cssnano] = require('cssnano');` and ensuring 50 | that the cssnano module is installed in npm. 51 | * **tidy** This uses [js-beautify](https://www.npmjs.com/package/js-beautify) 52 | The first argument is the type: js, css, or html. The second argument are 53 | options that get merged with the defaults. The js has a default of 54 | `indent_size` of 4 and `jslint_happy` true. An unrecognized first argument 55 | (or none) will default to js. 56 | * **minify** The first argument says the type of minifier: js, css, and 57 | html. js is the default if the first argument is not realized. The 58 | second argument is an object of options that get passed in. This uses 59 | uglify-js, clean-css, and 60 | [html-minifier](https://www.npmjs.com/package/html-minifier), 61 | respectively. For css, the 62 | second argument can be a boolean indicating whether to pass on the 63 | results object (if true, `t()` ) or just the css output text (default). 64 | * **date** `... |date method||date, arg1, arg2, ..`. This uses the 65 | [date-fns](https://date-fns.org/) library. Any valid function in that 66 | should work fine. There are a few scenarios for getting a date going: 67 | 68 | * ` date object | date method, arg1, ...` will apply the method of 69 | `datefns` to the date as the leading argument and use the rest of the 70 | arguments to fill it in. Alias: `date object | -method arg1, ..` 71 | * `date string | date method, arg1, ...` will apply the method to the 72 | date parsed by `datefns.parse` Alias: `date string | -method arg1, ..` 73 | * `| date method, arg1, ...` will apply the method to today's date. 74 | Alias `| -method arg1, ...` 75 | * `| date` Just returns today's date. No alias 76 | * `| date string date, method, args1, ...` will parse the string date 77 | and apply the method. No alias. 78 | * Note that there is also a subcommand `date` that will generate today's 79 | date or a date object based on the input. 80 | 81 | Recommended form: `| date string | -method arg1, ...| ...` 82 | * **csv-parse/transform/stringify** 83 | This is an interface into the node-csv library. It does the three 84 | named methods. 85 | The first argument can be an object of options except for transform in 86 | which the options are second and the first argument is a function to 87 | execute on each row. 88 | See [node-csv](http://csv.adaltas.com/) for more details. 89 | 90 | If you need to use the streaming power, you should access the full power 91 | of it using `Folder.requres.csv` and take a look at, for example, [so](http://stackoverflow.com/questions/23080413/nodejs-reading-csv-file) 92 | * **lodash** The incoming data is the first 93 | argument into the function while the first argument is the method name. 94 | The other arguments are what they are. 95 | 96 | Example: ` abc | - pad 8, 0` 97 | * **html-encode/decode/qescape** This is an interface to the 98 | [he](https://github.com/mathiasbynens/he) library. It encodes and 99 | decodes all named html entities. There is also a simple escape function, 100 | that includes quotes which the lit-native html-escape does not. 101 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /*global process, require */ 4 | 5 | var mod = require('literate-programming-cli'); 6 | 7 | var args = mod.opts.parse(); 8 | 9 | args.build = args.build.map(function (el) { 10 | if (el.slice(-1) === "/") { 11 | return el.slice(0, -1); 12 | } else { 13 | return el; 14 | } 15 | }); 16 | 17 | var z = {}; 18 | args.other.forEach(function (arg) { 19 | var pair = arg.split(":"); 20 | if (pair.length === 1) { 21 | args[pair[0]] = true; 22 | } else if (pair.length === 2) { 23 | args[pair[0]] = pair[1]; 24 | } else { 25 | args[pair[0]] = pair.slice(1); 26 | } 27 | z[pair[0]] = args[pair[0]]; 28 | }); 29 | 30 | //console.warn("!!", args); 31 | 32 | var Folder = mod.Folder; 33 | 34 | Folder.inputs = args; 35 | Folder.z = z; 36 | 37 | var merge = Folder.requires.merge; 38 | if (! Folder.requires) { 39 | Folder.requires = {}; 40 | } 41 | var typeit = Folder.requires.typeit; 42 | 43 | var jshint = require('jshint').JSHINT; 44 | Folder.plugins.jshint = { 45 | options: {unused: true}, 46 | globals: [], 47 | clean : false 48 | } ; 49 | Folder.sync("jshint", function (input, args, name) { 50 | 51 | var doc = this; 52 | var options, globals; 53 | 54 | var log = [], err, i, lines, line, logcopy, 55 | globhash, ind, shortname; 56 | 57 | var plug = doc.plugins.jshint; 58 | 59 | options = merge(true, plug.options, args[0] || {}); 60 | 61 | globals = plug.globals.concat(args[1] || []); 62 | 63 | if (args[2]) { 64 | shortname = args[2]; 65 | } else { 66 | shortname = name.slice(name.lastIndexOf(":")+1, 67 | name.indexOf(doc.colon.v, ind) ); 68 | } 69 | 70 | globhash = {}; 71 | globals.forEach( function (el) { 72 | var bits; 73 | bits = el.trim().split(":"); 74 | bits[1] = bits[1] === "true"; 75 | globhash[bits[0].trim()] = bits[1]; 76 | }); 77 | 78 | jshint(input, options, globhash); 79 | 80 | 81 | var data = jshint.data(); 82 | 83 | 84 | lines = input.split("\n"); 85 | for (i = 0; i < jshint.errors.length; i += 1) { 86 | err = jshint.errors[i]; 87 | if (!err) {continue;} 88 | if (err.reason.indexOf("is defined but never used.") !== -1) { 89 | continue; //this is covered elsewhere. 90 | } 91 | line = lines[err.line-1]; 92 | if (line.trim().length < 4) { 93 | line = "\n---\n" + lines.slice(err.line-2, err.line+1).join("\n") + 94 | "\n---\n"; 95 | } 96 | log.push("E "+ err.line+","+err.character+": "+err.reason + 97 | " "+ line.trim()); 98 | } 99 | if (data.hasOwnProperty("implieds") ) { 100 | for (i = 0; i < data.implieds.length; i += 1) { 101 | err = data.implieds[i]; 102 | log.push("Implied Gobal "+ err.line+": "+err.name + 103 | " "+ lines[err.line[0]-1].trim()); 104 | } 105 | } 106 | if (data.hasOwnProperty("unused") ) { 107 | for (i = 0; i < data.unused.length; i += 1) { 108 | err = data.unused[i]; 109 | log.push("Unused "+ err.line+": "+err.name + 110 | " "+ lines[err.line-1].trim()); 111 | } 112 | } 113 | 114 | if (log.length > 0 ) { 115 | logcopy = log.slice(); 116 | logcopy.unshift(shortname, "jshint Report"); 117 | doc.log.apply(doc, logcopy); 118 | } else { 119 | if (args[3] || plug.clean) { 120 | doc.log(shortname, "jshint clean"); 121 | } 122 | } 123 | 124 | return input; 125 | }); 126 | 127 | var pug = require('pug'); 128 | Folder.plugins.pug = {pretty:true}; 129 | 130 | Folder.sync("pug" , function (code, args) { 131 | var options = merge(true, this.plugins.pug, args); 132 | return pug.render(code, options); 133 | }); 134 | 135 | var mdit = require('markdown-it'); 136 | 137 | var mddefault = { 138 | html:true, 139 | linkify:true 140 | }; 141 | 142 | 143 | Folder.plugins.md = { 144 | req : mdit, 145 | def: mdit(mddefault), 146 | options: mddefault, 147 | prepost : [function litsub (code) { 148 | var snips = []; 149 | var masklit = function (match) { 150 | snips.push(match); 151 | return ""; 152 | }; 153 | 154 | var rep = function (match, num) { 155 | return snips[parseInt(num, 10)]; 156 | }; 157 | 158 | var undo = function (html) { 159 | var reg = /<\!\-\-LITSNIP(\d+)\-\->/g; 160 | return html.replace(reg, rep); 161 | }; 162 | 163 | var lit = /(?:\\|\\\d*)?\_+(\"[^"]+\"|\`[^`]+\`|\'[^']\')/g; 164 | code = code.replace(lit, masklit); 165 | return [code, undo]; 166 | }, function mathsub (code) { 167 | var snips = []; 168 | var maskinline = function (match, ignore, math) { 169 | snips.push("\\(" + math + "\\)" ); 170 | return ""; 171 | }; 172 | 173 | var maskdisp = function (match, ignore, math) { 174 | snips.push("\\[" + math + "\\]" ); 175 | return ""; 176 | }; 177 | 178 | var rep = function (match, num) { 179 | return snips[parseInt(num, 10)]; 180 | }; 181 | 182 | var undo = function (html) { 183 | var reg = /<\!\-\-MATHSNIP(\d+)\-\->/g; 184 | return html.replace(reg, rep); 185 | }; 186 | 187 | var inline = /(\`+)([^`]+)\1\$/g; 188 | code = code.replace(inline, maskinline); 189 | var display = /(\`+)\$([^`]+)\1/g; 190 | code = code.replace(display, maskdisp); 191 | return [code, undo]; 192 | }] 193 | }; 194 | 195 | Folder.sync( "md", function (code, args) { 196 | var plug = this.plugins.md; 197 | var html, rend; 198 | rend = plug[args[0]] || plug.def; 199 | var post = []; 200 | plug.prepost.forEach(function (el, ind) { 201 | var temp; 202 | if (args[ind+1] !== false) { 203 | temp = el(code); 204 | post.push(temp[1]); 205 | code = temp[0]; 206 | } 207 | }); 208 | html = rend.render(code); 209 | post.forEach(function (el) { 210 | html = el(html); 211 | }); 212 | return html; 213 | }); 214 | 215 | var cheerio = require('cheerio'); 216 | Folder.plugins.cheerio = { 217 | req: cheerio 218 | }; 219 | 220 | Folder.sync( "cheerio" , function(code, args) { 221 | var selector = args.shift(); 222 | var method = args.shift(); 223 | var $ = cheerio.load(code); 224 | var el$ = $(selector); 225 | try { 226 | el$[method].apply(el$, args); 227 | return $.html(); 228 | } catch (e) { 229 | this.log("Problem with cheerio: " + selector + "," + 230 | method + "," + args.join(",")); 231 | return code; 232 | } 233 | }); 234 | Folder.sync( "ch-replace" , function(code, args) { 235 | var selector, replacement; 236 | var n = args.length; 237 | var $ = cheerio.load(code); 238 | var i; 239 | for (i = 0; i < n; i += 2) { 240 | selector = args[i]; 241 | replacement = args[i+1]; 242 | $(selector).replaceWith(replacement); 243 | } 244 | return $.html(); 245 | }); 246 | 247 | var postcss = require('postcss'); 248 | 249 | Folder.commands.postcss = function (input, args, name) { 250 | var doc = this; 251 | var pc = doc.plugins.postcss; 252 | var cmds = []; 253 | if ( (typeof input !== "string") || (input === '') ) { 254 | doc.gcd.emit("text ready:" + name, input); 255 | return; 256 | } 257 | args.forEach(function (el) { 258 | if (typeof pc[el] === "function" ) { 259 | cmds.push(pc[el]); 260 | } 261 | }); 262 | postcss(cmds).process(input).then(function (result) { 263 | result.warnings().forEach(function (warn) { 264 | doc.log(warn.toString()); 265 | }); 266 | doc.gcd.emit("text ready:" + name, result.css); 267 | }).catch(function (error) { 268 | doc.log(error.toString()); 269 | }); 270 | }; 271 | 272 | Folder.plugins.postcss = { 273 | req : postcss, 274 | autoprefixer : require('autoprefixer') 275 | }; 276 | 277 | var tidy = require("js-beautify"); 278 | 279 | Folder.plugins.tidy = { 280 | js : { 281 | "indent_size": 4, 282 | "jslint_happy": true 283 | }, 284 | css: {}, 285 | html :{} 286 | }; 287 | 288 | Folder.sync("tidy", function (code, args) { 289 | var type = args[0]; 290 | var options = args[1] || {}; 291 | var plug = this.plugins.tidy; 292 | 293 | if (! plug.hasOwnProperty(type) ) { 294 | type = "js"; 295 | } 296 | 297 | code = tidy[type](code, merge(true, plug[type], options )); 298 | 299 | return code; 300 | }); 301 | 302 | var jsmin = require("uglify-js").minify; 303 | var cssmin = require("clean-css"); 304 | var htmlmin = require("html-minifier").minify; 305 | 306 | Folder.plugins.minify = { 307 | js : {}, 308 | css : {}, 309 | html : {} 310 | }; 311 | 312 | Folder.sync("minify", function (code, args) { 313 | var type = args[0]; 314 | var options = args[1] || {}; 315 | var plug = this.plugins.minify; 316 | 317 | if (! plug.hasOwnProperty(type) ) { 318 | type = "js"; 319 | } 320 | 321 | options = merge(true, plug[type], options); 322 | 323 | switch (type) { 324 | case 'js' : 325 | options.fromString = true; 326 | code = jsmin(code, options); 327 | break; 328 | case 'css' : 329 | code = new cssmin(options).minify(code); 330 | if (args[2] !== true) { 331 | code = code.styles; 332 | } 333 | break; 334 | case 'html': 335 | code = htmlmin(code, options); 336 | break; 337 | } 338 | 339 | return code; 340 | } ); 341 | 342 | var datefns = require('date-fns'); 343 | Folder.requires.datefns = datefns; 344 | Folder.dash.date = [datefns, 0]; 345 | Folder.sync('date', function (date, args) { 346 | var fn = args[0]; 347 | if (date) { 348 | if (typeit(date) !== 'date') { 349 | date = datefns.parse(date); 350 | } 351 | } else { 352 | if (datefns.hasOwnProperty(fn) ) { 353 | // has method and no incoming so make date 354 | date = new Date(); 355 | } else if (! fn) { 356 | return new Date(); 357 | } else { 358 | // assuming date string in first argument 359 | date = datefns.parse(args.shift()); 360 | fn = args[0]; 361 | } 362 | } 363 | if (! (datefns.hasOwnProperty(fn) ) ) { 364 | // no method just get a date object 365 | return date; 366 | } else { 367 | args[0] = date; 368 | } 369 | return datefns[fn].apply(datefns, args); 370 | }); 371 | 372 | var csv = Folder.requires.csv = require("csv"); 373 | Folder.plugins.csv = { 374 | parse : {}, 375 | stringify : {}, 376 | transform : {} 377 | }; 378 | Folder.async("csv-parse", function (input, args, cb) { 379 | var options = merge(args[0], this.plugins.csv.parse); 380 | csv.parse(input, options, cb); 381 | }); 382 | Folder.async("csv-transform", function (input, args, cb) { 383 | var options = merge(args[1], this.plugins.csv.transform); 384 | var f = (typeit(args[0] === "function" ) ) ? 385 | args[0] : function (el) {return el;}; 386 | csv.transform( input, f, options, cb); 387 | }); 388 | Folder.async("csv-stringify", function (input, args, cb) { 389 | var options = merge(args[0], this.plugins.csv.stringify); 390 | csv.stringify(input, options, cb); 391 | }); 392 | 393 | var lodash = Folder.requires.lodash = require("lodash"); 394 | Folder.dash.lodash = [lodash, 1]; 395 | Folder.sync("lodash", function (input, args) { 396 | if (args.length) { 397 | var method = args[0]; 398 | if ( typeit(lodash[method], 'function') ) { 399 | args[0] = input; 400 | return lodash[method].apply(lodash, args); 401 | } else { 402 | // this is an error. need to come up with a warning. 403 | this.warn("lodash", "unrecognized method", method); 404 | return input; 405 | } 406 | } else { 407 | return input; 408 | } 409 | }); 410 | 411 | var he = require('he'); 412 | Folder.requires.he = he; 413 | Folder.plugins.he = { 414 | encode : {}, 415 | decode : {}, 416 | }; 417 | Folder.sync("html-encode", function (input, args) { 418 | var options = merge(this.plugins.he.encode, args[0]); 419 | return he.encode(input, options); 420 | }); 421 | Folder.sync("html-decode", function (input, args) { 422 | var options = merge(this.plugins.he.decode, args[0]); 423 | return he.decode(input, options); 424 | }); 425 | Folder.sync("html-qescape", function (input) { 426 | return he.escape(input); 427 | }); 428 | 429 | Folder.prototype.encoding = args.encoding; 430 | Folder.prototype.displayScopes = (args.scopes ? function () { 431 | var folder = this; 432 | var str = ''; 433 | Object.keys(folder.scopes).forEach(function (el) { 434 | str += el+ "::\n------\n"; 435 | var scope = folder.scopes[el]; 436 | Object.keys(scope).sort().forEach( 437 | function (v) { 438 | str+= v + ": '" + scope[v] + "'\n* * *\n"; 439 | }); 440 | str += "\n------\n"; 441 | }); 442 | str = str.replace(/\n\n/g, "\n"); 443 | console.log(str); 444 | } : 445 | function () {} ); 446 | 447 | 448 | Folder.lprc(args.lprc, args); 449 | 450 | Folder.process(args); 451 | 452 | process.on('exit', Folder.exit()); 453 | -------------------------------------------------------------------------------- /lprc.js: -------------------------------------------------------------------------------- 1 | /*global module, require */ 2 | module.exports = function(Folder, args) { 3 | 4 | if (args.file.length === 0) { 5 | args.file = ["project.md"]; 6 | } 7 | 8 | args.src = "."; 9 | 10 | //require('litpro-jshint')(Folder, args); 11 | 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "literate-programming", 3 | "description": "Fat command line for literate-programming", 4 | "version": "1.1.0", 5 | "homepage": "https://github.com/jostylr/literate-programming", 6 | "author": { 7 | "name": "James Taylor", 8 | "email": "jostylr@gmail.com" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/jostylr/literate-programming.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/jostylr/literate-programming/issues" 16 | }, 17 | "license": "MIT", 18 | "main": "index.js", 19 | "engines": { 20 | "node": ">=4.0" 21 | }, 22 | "dependencies":{ 23 | "literate-programming-cli" : "^2.1.0", 24 | "jshint" : "^2.11.0", 25 | "postcss" : "^7.0.26", 26 | "autoprefixer" : "^9.7.4", 27 | "pug" : "^2.0.4", 28 | "markdown-it" : "^10.0.0", 29 | "cheerio" : "^0.22.0", 30 | "js-beautify" : "^1.10.3", 31 | "html-minifier" : "^4.0.0", 32 | "clean-css" : "^4.2.1", 33 | "uglify-js" : "^3.7.5", 34 | "csv" : "^5.3.1", 35 | "date-fns" : "^1.30.1", 36 | "lodash" : "^4.17.15", 37 | "he" : "^1.2.0" 38 | }, 39 | "devDependencies" : { 40 | "litpro-jshint" : "^0.4.0", 41 | "literate-programming-cli-test" : "^0.5.1", 42 | "markdown-it-anchor" : "^5.2.5" 43 | }, 44 | "scripts" : { 45 | "test" : "node ./test.js" 46 | }, 47 | "keywords": ["literate programming"], 48 | "bin": { 49 | "literate-programming" : "./index.js" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /project.md: -------------------------------------------------------------------------------- 1 | # Fat Client 2 | 3 | This is the fat literate programming client, full batteries included. The 4 | batteries largely pertain to web development and may grow in time. 5 | 6 | Currently, it includes markdown converter, pugs (formerly jade), postcss and 7 | autoprefixer, jshint, (minimizers, beautifiers). 8 | 9 | It avoids anything that involves compiling to install. Otherwise, this is 10 | quite a liberal package and issues are welcome to include new batteries. 11 | 12 | 13 | ## Files 14 | 15 | Most of this is adding in the plugins and running full tests. 16 | 17 | ### Load 18 | 19 | Reading file into cli 20 | 21 | * [cli](node_modules/literate-programming-cli/litpro.js "readfile:| 22 | sub ./index.js, literate-programming-cli | 23 | sub //plugin-to-folder, _'plugin to folder ' ") 24 | 25 | ### Save 26 | 27 | * [../](# "cd: save") It all goes in the main directory. 28 | * [index.js](#cli "save:|jshint ") 29 | * [README.md](# "save: | raw ## README, ---! | sub \n\ #, \n# |trim 30 | | sub COMDOC, _'comdoc | .join \n' ") The standard README. 31 | * [convert.md](# "save: | raw ## Converting, --! | sub \n\ #, # |trim") 32 | * [test.js](#test "save: |jshint ") The testing file. 33 | * [documentation.md](#comdoc "save: | .join \n ") 34 | * [](# "cd: save") 35 | 36 | 37 | ### h5 38 | 39 | The h5 headings can be used in a special way. Here we initialize 40 | 41 | * [comdoc](#cdoc "h5: ") 42 | 43 | 44 | ### Display scopes 45 | 46 | This is a function that displays the scopes. 47 | 48 | function () { 49 | var folder = this; 50 | var str = ''; 51 | Object.keys(folder.scopes).forEach(function (el) { 52 | str += el+ "::\n------\n"; 53 | var scope = folder.scopes[el]; 54 | Object.keys(scope).sort().forEach( 55 | function (v) { 56 | str+= v + ": '" + scope[v] + "'\n* * *\n"; 57 | }); 58 | str += "\n------\n"; 59 | 60 | }); 61 | str = str.replace(/\n\n/g, "\n"); 62 | console.log(str); 63 | } 64 | 65 | ## Plugin to Folder 66 | 67 | This is where the fat comes in. 68 | 69 | var merge = Folder.requires.merge; 70 | if (! Folder.requires) { 71 | Folder.requires = {}; 72 | } 73 | var typeit = Folder.requires.typeit; 74 | 75 | _"jshint" 76 | 77 | _"pug" 78 | 79 | _"md" 80 | 81 | _"cheerio" 82 | 83 | _"postcss" 84 | 85 | _"tidy" 86 | 87 | _"minify" 88 | 89 | _"date" 90 | 91 | _"csv" 92 | 93 | _"lodash" 94 | 95 | _"html encodings" 96 | 97 | `_"formatters"` 98 | 99 | 100 | ### Formatters 101 | 102 | This is some custom formatters for the command line client 103 | 104 | var oneArgOnly = _":one arg"; 105 | ["jshint", "jshint clean"]. 106 | forEach(function (el) { 107 | Folder.prototype.formatters[el] = oneArgOnly; 108 | }); 109 | 110 | 111 | [one arg]() 112 | 113 | This just lists the files that were saved. 114 | 115 | function (list) { 116 | var ret = ''; 117 | ret += list.map( 118 | function (args) { 119 | return args.shift(); 120 | }). 121 | join("\n"); 122 | return ret; 123 | } 124 | 125 | 126 | 127 | 128 | # JShint 129 | 130 | This checks for JS problems. 131 | 132 | This is the module entry point. It adds the commands jshint and the directive 133 | jshint which loads options and globals. 134 | 135 | var jshint = require('jshint').JSHINT; 136 | Folder.plugins.jshint = { 137 | options: {unused: true}, 138 | globals: [], 139 | clean : false 140 | } ; 141 | Folder.sync("jshint", _"jshint command"); 142 | 143 | 144 | 145 | ##### cdoc 146 | 147 | * **jshint** This takes the input and runs it through JSHint. The command 148 | is of the form 149 | `js stuff | jshint options, globals, shortname, print clean`. 150 | 151 | * The options is an object that corresponds to the [options that JShint 152 | accepts](http://jshint.com/docs/options/); you can use a subcommand to 153 | create the options object if you like. Default is unused:true, else is 154 | their defaults. 155 | * Globals is an array of global 156 | names; if they can be written over, pass in `name:true` instead of 157 | `name`. 158 | * Shortname is the shortname to present in understanding what is being 159 | jshinted. Otherwise, it does its best to give you a cryptic but 160 | informative name. 161 | * If the fourth argument is a boolean, `t()` or `f()` will do it, then 162 | that toggles whether to print the message that it all went smoothly or 163 | not, respectively. The default is to not print it. 164 | * You can override the defaults repeatedly by modifying the 165 | `Folder.plugins.jshint` object with the names: `options`, `globals`, and 166 | `clean`. 167 | 168 | ### jshint command 169 | 170 | The only thing we add is the jshint command. This takes an incoming text and 171 | hints it. The first argument, if present, is a JSON object of options. The 172 | 173 | function (input, args, name) { 174 | 175 | var doc = this; 176 | var options, globals; 177 | 178 | var log = [], err, i, lines, line, logcopy, 179 | globhash, ind, shortname; 180 | 181 | var plug = doc.plugins.jshint; 182 | 183 | _":options" 184 | 185 | jshint(input, options, globhash); 186 | 187 | 188 | var data = jshint.data(); 189 | 190 | 191 | _":generating the logs" 192 | 193 | _":report logs" 194 | 195 | return input; 196 | } 197 | 198 | 199 | 200 | [report logs]() 201 | 202 | if (log.length > 0 ) { 203 | logcopy = log.slice(); 204 | logcopy.unshift(shortname, "jshint Report"); 205 | doc.log.apply(doc, logcopy); 206 | } else { 207 | if (args[3] || plug.clean) { 208 | doc.log(shortname, "jshint clean"); 209 | } 210 | } 211 | 212 | [options]() 213 | 214 | We can get options from the plugins.jshint object as well as from the 215 | arguments. We are opinionated in setting unused to true. 216 | 217 | Globals are ultimately an object that has a bunch of true/false properties. If 218 | true, then they can be written too. There is also some blacklist property, but 219 | I am not sure where that gets put so ignoring it. 220 | 221 | 222 | options = merge(true, plug.options, args[0] || {}); 223 | 224 | globals = plug.globals.concat(args[1] || []); 225 | 226 | if (args[2]) { 227 | shortname = args[2]; 228 | } else { 229 | shortname = name.slice(name.lastIndexOf(":")+1, 230 | name.indexOf(doc.colon.v, ind) ); 231 | } 232 | 233 | globhash = {}; 234 | globals.forEach( function (el) { 235 | var bits; 236 | bits = el.trim().split(":"); 237 | bits[1] = bits[1] === "true"; 238 | globhash[bits[0].trim()] = bits[1]; 239 | }); 240 | 241 | 242 | 243 | 244 | 245 | [generating the logs]() 246 | 247 | 248 | lines = input.split("\n"); 249 | for (i = 0; i < jshint.errors.length; i += 1) { 250 | err = jshint.errors[i]; 251 | if (!err) {continue;} 252 | if (err.reason.indexOf("is defined but never used.") !== -1) { 253 | continue; //this is covered elsewhere. 254 | } 255 | line = lines[err.line-1]; 256 | if (line.trim().length < 4) { 257 | line = "\n---\n" + lines.slice(err.line-2, err.line+1).join("\n") + 258 | "\n---\n"; 259 | } 260 | log.push("E "+ err.line+","+err.character+": "+err.reason + 261 | " "+ line.trim()); 262 | } 263 | if (data.hasOwnProperty("implieds") ) { 264 | for (i = 0; i < data.implieds.length; i += 1) { 265 | err = data.implieds[i]; 266 | log.push("Implied Gobal "+ err.line+": "+err.name + 267 | " "+ lines[err.line[0]-1].trim()); 268 | } 269 | } 270 | if (data.hasOwnProperty("unused") ) { 271 | for (i = 0; i < data.unused.length; i += 1) { 272 | err = data.unused[i]; 273 | log.push("Unused "+ err.line+": "+err.name + 274 | " "+ lines[err.line-1].trim()); 275 | } 276 | } 277 | 278 | 279 | 280 | ## Pug 281 | 282 | pug converts the pug syntax into html. It is mainly used for structures as 283 | opposed to content. `pug text...|pug` 284 | 285 | var pug = require('pug'); 286 | Folder.plugins.pug = {pretty:true}; 287 | 288 | Folder.sync("pug" , function (code, args) { 289 | var options = merge(true, this.plugins.pug, args); 290 | return pug.render(code, options); 291 | }); 292 | 293 | ##### Doc 294 | 295 | * **pug** This transforms the incoming text by using the 296 | [pug, formerly jade, transformation engine](https://pugjs.org). 297 | Pass in an object as first argument for 298 | options. The defaults are currently used except `pretty:true`. 299 | 300 | 301 | ## Md 302 | 303 | This uses markdown-it. 304 | 305 | Because a new renderer is being made with the change of options and behavior 306 | is modified with uses, these should all be done not inline. The only choices 307 | inline is choosing the name (arg0), and toggling on, off the pre/post 308 | processors. 309 | 310 | The default is to process math and literate snippets. 311 | 312 | 313 | var mdit = require('markdown-it'); 314 | 315 | var mddefault = { 316 | html:true, 317 | linkify:true 318 | }; 319 | 320 | 321 | Folder.plugins.md = { 322 | req : mdit, 323 | def: mdit(mddefault), 324 | options: mddefault, 325 | prepost : [_":litpro subbing", _":math subbing"] 326 | }; 327 | 328 | Folder.sync( "md", function (code, args) { 329 | var plug = this.plugins.md; 330 | var html, rend; 331 | rend = plug[args[0]] || plug.def; 332 | var post = []; 333 | plug.prepost.forEach(function (el, ind) { 334 | var temp; 335 | if (args[ind+1] !== false) { 336 | temp = el(code); 337 | post.push(temp[1]); 338 | code = temp[0]; 339 | } 340 | }); 341 | html = rend.render(code); 342 | post.forEach(function (el) { 343 | html = el(html); 344 | }); 345 | return html; 346 | }); 347 | 348 | 349 | [litpro subbing]() 350 | 351 | We can create an option that does literate programming substituting before 352 | rendering and then replacing it. 353 | 354 | The comment html is to ensure that the markdown does not wrap something else 355 | around it. I hope. This requires `html:true` presumably. 356 | 357 | 358 | function litsub (code) { 359 | var snips = []; 360 | var masklit = function (match) { 361 | snips.push(match); 362 | return ""; 363 | }; 364 | 365 | var rep = function (match, num) { 366 | return snips[parseInt(num, 10)]; 367 | }; 368 | 369 | var undo = function (html) { 370 | var reg = /<\!\-\-LITSNIP(\d+)\-\->/g; 371 | return html.replace(reg, rep); 372 | }; 373 | 374 | var lit = /(?:\\|\\\d*)?\_+(\"[^"]+\"|\`[^`]+\`|\'[^']\')/g; 375 | code = code.replace(lit, masklit); 376 | return [code, undo]; 377 | } 378 | 379 | [math subbing]() 380 | 381 | We want to replace the math expressions to avoid markdown processing of random 382 | parts. Our syntax will be `` `x^2 + 3 < 5`$ `` and gets rendered by katex into 383 | html which is then subbed in after the markdown processing. We also use code 384 | fences for display math, the dollar sign comes after the first fence, where 385 | the language goes. Actually, we have if the dollar sign is inside the first 386 | set of backticks, then it is display, if it is outside after the second set, 387 | then it is inline. No spaces allowed in either case between the backtick and 388 | dollar sign. So this does allow display to happen for inline code and inline 389 | to happen for fenced code. This does mean that a dollar sign next to a 390 | backtick is will almost surely trigger this though a space will stop that. 391 | 392 | function mathsub (code) { 393 | var snips = []; 394 | var maskinline = function (match, ignore, math) { 395 | snips.push("\\(" + math + "\\)" ); 396 | return ""; 397 | }; 398 | 399 | var maskdisp = function (match, ignore, math) { 400 | snips.push("\\[" + math + "\\]" ); 401 | return ""; 402 | }; 403 | 404 | var rep = function (match, num) { 405 | return snips[parseInt(num, 10)]; 406 | }; 407 | 408 | var undo = function (html) { 409 | var reg = /<\!\-\-MATHSNIP(\d+)\-\->/g; 410 | return html.replace(reg, rep); 411 | }; 412 | 413 | var inline = /(\`+)([^`]+)\1\$/g; 414 | code = code.replace(inline, maskinline); 415 | var display = /(\`+)\$([^`]+)\1/g; 416 | code = code.replace(display, maskdisp); 417 | return [code, undo]; 418 | } 419 | 420 | [with katex]() 421 | 422 | Ignored for now as the rendered html is very verbose. 423 | 424 | function mathsub (code) { 425 | var snips = []; 426 | var options = { 427 | throwOnError: false, 428 | display : false 429 | }; 430 | var maskmath = function (match, ignore, math) { 431 | console.log("DEBUG0", match, ignore, math); 432 | snips.push(katex.renderToString(math, options)); 433 | return ""; 434 | }; 435 | 436 | var rep = function (match, num) { 437 | return snips[parseInt(num, 10)]; 438 | }; 439 | 440 | var undo = function (html) { 441 | var reg = /<\!\-\-MATHSNIP(\d+)\-\->/g; 442 | return html.replace(reg, rep); 443 | }; 444 | 445 | var inline = /(\`+)([^`]+)\1\$/g; 446 | code = code.replace(inline, maskmath); 447 | var display = /(\`+)\$([^`]+)\1/g; 448 | options.display = true; 449 | code = code.replace(display, maskmath); 450 | return [code, undo]; 451 | } 452 | 453 | 454 | ##### Cdoc 455 | 456 | * **md** This takes the input as markdown and puts out html. The first 457 | argument is an optional string naming the renderer to use. The other 458 | arguments should be booleans, namely, `f()`, if one does not want 459 | preprocessing/post to occur. The default preprocessors, in order, are 460 | literate programming subs and math subs rendering to katex. 461 | 462 | To create a renderer, you can use Folder.plugins.md.req as the markdoan 463 | object and then render it per the instructions (an options object 464 | `req(options).use(...)`. This is all best done in the lprc.js file. 465 | Store the rendered under the preferred name in plugins.md. 466 | 467 | See the logs test directory and its lprc.js. 468 | 469 | 470 | ## Cheerio 471 | 472 | Cheerio takes in html and can do replacements on it, like jQuery does. The 473 | syntax is `html... | cheerio selector, method, args to method...` 474 | 475 | var cheerio = require('cheerio'); 476 | Folder.plugins.cheerio = { 477 | req: cheerio 478 | }; 479 | 480 | Folder.sync( "cheerio" , function(code, args) { 481 | var selector = args.shift(); 482 | var method = args.shift(); 483 | var $ = cheerio.load(code); 484 | var el$ = $(selector); 485 | try { 486 | el$[method].apply(el$, args); 487 | return $.html(); 488 | } catch (e) { 489 | this.log("Problem with cheerio: " + selector + "," + 490 | method + "," + args.join(",")); 491 | return code; 492 | } 493 | }); 494 | 495 | There is a special function of replacement where the arguments are paired to 496 | be a selector and the html replacement. This is like the standard sub command. 497 | 498 | Folder.sync( "ch-replace" , function(code, args) { 499 | var selector, replacement; 500 | var n = args.length; 501 | var $ = cheerio.load(code); 502 | var i; 503 | for (i = 0; i < n; i += 2) { 504 | selector = args[i]; 505 | replacement = args[i+1]; 506 | $(selector).replaceWith(replacement); 507 | } 508 | return $.html(); 509 | }); 510 | 511 | 512 | ##### cdoc 513 | 514 | * **cheerio** This gives access to the cheerio module, a lightweight node 515 | version of jQuery-esque without the overhead of jsdom. It can't do 516 | everything, but it does most things: 517 | [cheeriojs](https://github.com/cheeriojs/cheerio). To use, the incoming 518 | text is the html doc to modify, the first argument is the selector, the 519 | second the method, and then the arguments to the method, e.g., 520 | `somehtml | cheerio h2.title, .text, Hello there!` 521 | * **ch-replace** This is a convenience method for cheerio. This will use 522 | the first argument as a selector and the second argument as a 523 | html replacement. 524 | 525 | 526 | ## Postcss 527 | 528 | This uses postcss to work its magic on the incoming text. The plugins should 529 | be loaded here; right now it is just autoprefixer. Then it can be used as 530 | `css...|postcss cmd1, cmd2, ...` 531 | 532 | var postcss = require('postcss'); 533 | 534 | Folder.commands.postcss = function (input, args, name) { 535 | var doc = this; 536 | var pc = doc.plugins.postcss; 537 | var cmds = []; 538 | if ( (typeof input !== "string") || (input === '') ) { 539 | doc.gcd.emit("text ready:" + name, input); 540 | return; 541 | } 542 | args.forEach(function (el) { 543 | if (typeof pc[el] === "function" ) { 544 | cmds.push(pc[el]); 545 | } 546 | }); 547 | postcss(cmds).process(input).then(function (result) { 548 | result.warnings().forEach(function (warn) { 549 | doc.log(warn.toString()); 550 | }); 551 | doc.gcd.emit("text ready:" + name, result.css); 552 | }).catch(function (error) { 553 | doc.log(error.toString()); 554 | }); 555 | }; 556 | 557 | Folder.plugins.postcss = { 558 | req : postcss, 559 | autoprefixer : require('autoprefixer') 560 | }; 561 | 562 | 563 | ##### cdoc 564 | 565 | * **postcss** This takes incoming text and runs it through postcss. To do 566 | something useful, you need to have the arguments be the commands to use. 567 | At the moment, the only one shipping with this is autoprefixer, but 568 | others are likely to be added (minimizers and fixers, in particular). 569 | You can add them yourself by, in lprcs.js, saying (installing cssnano as 570 | example) 571 | `Folder.plugins.postcss[cssnano] = require('cssnano');` and ensuring 572 | that the cssnano module is installed in npm. 573 | 574 | ## tidy 575 | 576 | This creates the web-tidy command using js-beautify. Why the name change? Cause 577 | its shorter. 578 | 579 | var tidy = require("js-beautify"); 580 | 581 | Folder.plugins.tidy = { 582 | js : { 583 | "indent_size": 4, 584 | "jslint_happy": true 585 | }, 586 | css: {}, 587 | html :{} 588 | }; 589 | 590 | Folder.sync("tidy", _":fun"); 591 | 592 | 593 | 594 | [fun]() 595 | 596 | function (code, args) { 597 | var type = args[0]; 598 | var options = args[1] || {}; 599 | var plug = this.plugins.tidy; 600 | 601 | if (! plug.hasOwnProperty(type) ) { 602 | type = "js"; 603 | } 604 | 605 | code = tidy[type](code, merge(true, plug[type], options )); 606 | 607 | return code; 608 | } 609 | 610 | ##### cdoc 611 | 612 | * **tidy** This uses [js-beautify](https://www.npmjs.com/package/js-beautify) 613 | The first argument is the type: js, css, or html. The second argument are 614 | options that get merged with the defaults. The js has a default of 615 | `indent_size` of 4 and `jslint_happy` true. An unrecognized first argument 616 | (or none) will default to js. 617 | 618 | 619 | ## Minify 620 | 621 | This combines three different minimizers into a single command. 622 | 623 | var jsmin = require("uglify-js").minify; 624 | var cssmin = require("clean-css"); 625 | var htmlmin = require("html-minifier").minify; 626 | 627 | Folder.plugins.minify = { 628 | js : {}, 629 | css : {}, 630 | html : {} 631 | }; 632 | 633 | Folder.sync("minify", _":fun"); 634 | 635 | [fun]() 636 | 637 | function (code, args) { 638 | var type = args[0]; 639 | var options = args[1] || {}; 640 | var plug = this.plugins.minify; 641 | 642 | if (! plug.hasOwnProperty(type) ) { 643 | type = "js"; 644 | } 645 | 646 | options = merge(true, plug[type], options); 647 | 648 | switch (type) { 649 | case 'js' : 650 | options.fromString = true; 651 | code = jsmin(code, options); 652 | break; 653 | case 'css' : 654 | code = new cssmin(options).minify(code); 655 | if (args[2] !== true) { 656 | code = code.styles; 657 | } 658 | break; 659 | case 'html': 660 | code = htmlmin(code, options); 661 | break; 662 | } 663 | 664 | return code; 665 | } 666 | 667 | 668 | ##### cdoc 669 | 670 | * **minify** The first argument says the type of minifier: js, css, and 671 | html. js is the default if the first argument is not realized. The 672 | second argument is an object of options that get passed in. This uses 673 | uglify-js, clean-css, and 674 | [html-minifier](https://www.npmjs.com/package/html-minifier), 675 | respectively. For css, the 676 | second argument can be a boolean indicating whether to pass on the 677 | results object (if true, `t()` ) or just the css output text (default). 678 | 679 | ## js-bench 680 | 681 | This is to benchmark javscript code. It should be a directive. 682 | 683 | There is the code of the block which could be transformed in a variety of 684 | ways. The linkname could be the benchmark name `bench:case` with the colon 685 | being the case name; the directive would then compare the different cases with 686 | the bench name. 687 | 688 | `[bench:case](#start "js-bench: off/log/varname | pipes for preprocessing")` 689 | 690 | function (input, args) { 691 | 692 | } 693 | 694 | 695 | ## js-test 696 | 697 | Not sure what to really do. This could be similar to the benchmark directive 698 | in form, but not sure if it to use tape, or role my own (with grabbing the 699 | deep-equal algorithm). 700 | 701 | 702 | ## date 703 | 704 | This exposes a nice api for getting dates done nicely. It uses [date-fns](https://date-fns.org/) 705 | 706 | So the incoming input is the date (or whatever) and the arguments are the 707 | function name and other arguments. We get the function name and then we will 708 | apply the args, putting in the date as the first one. If there are no 709 | arguments, a new Date() is returned. If the first argument is not a known 710 | method, then we assume it was a date to be parsed. 711 | 712 | var datefns = require('date-fns'); 713 | Folder.requires.datefns = datefns; 714 | Folder.dash.date = [datefns, 0]; 715 | Folder.sync('date', _":fun"); 716 | 717 | 718 | [fun]() 719 | 720 | function (date, args) { 721 | var fn = args[0]; 722 | _":get date" 723 | _":check for method" 724 | return datefns[fn].apply(datefns, args); 725 | } 726 | 727 | [get date]() 728 | 729 | The issue is that we might have dates coming in different ways. We could have 730 | an incoming date string, an incoming date object, no incoming, but rather have 731 | it as a first argument or we could have no date in either place and thus 732 | create a new date. Here we deal with that logic. 733 | 734 | if (date) { 735 | if (typeit(date) !== 'date') { 736 | date = datefns.parse(date); 737 | } 738 | } else { 739 | if (datefns.hasOwnProperty(fn) ) { 740 | // has method and no incoming so make date 741 | date = new Date(); 742 | } else if (! fn) { 743 | return new Date(); 744 | } else { 745 | // assuming date string in first argument 746 | date = datefns.parse(args.shift()); 747 | fn = args[0]; 748 | } 749 | } 750 | 751 | Could probably move check for method out of the whole thing. 752 | 753 | [check for method]() 754 | 755 | This checks for the method and returns date if not found. 756 | 757 | if (! (datefns.hasOwnProperty(fn) ) ) { 758 | // no method just get a date object 759 | return date; 760 | } else { 761 | args[0] = date; 762 | } 763 | 764 | ##### cdoc 765 | 766 | * **date** `... |date method||date, arg1, arg2, ..`. This uses the 767 | [date-fns](https://date-fns.org/) library. Any valid function in that 768 | should work fine. There are a few scenarios for getting a date going: 769 | 770 | * ` date object | date method, arg1, ...` will apply the method of 771 | `datefns` to the date as the leading argument and use the rest of the 772 | arguments to fill it in. Alias: `date object | -method arg1, ..` 773 | * `date string | date method, arg1, ...` will apply the method to the 774 | date parsed by `datefns.parse` Alias: `date string | -method arg1, ..` 775 | * `| date method, arg1, ...` will apply the method to today's date. 776 | Alias `| -method arg1, ...` 777 | * `| date` Just returns today's date. No alias 778 | * `| date string date, method, args1, ...` will parse the string date 779 | and apply the method. No alias. 780 | * Note that there is also a subcommand `date` that will generate today's 781 | date or a date object based on the input. 782 | 783 | Recommended form: `| date string | -method arg1, ...| ...` 784 | 785 | 786 | ## csv 787 | 788 | This exposes a csv parsing and stringifying library, [node-csv](https://github.com/wdavidw/node-csv) 789 | 790 | We provide a command that takes the incoming as the data, the first argument 791 | as the method, and the rest as options or function, depending. In calling the 792 | csv method, it uses callbacks that we push onto the args array. 793 | 794 | var csv = Folder.requires.csv = require("csv"); 795 | Folder.plugins.csv = { 796 | parse : {}, 797 | stringify : {}, 798 | transform : {} 799 | }; 800 | Folder.async("csv-parse", _":parse"); 801 | Folder.async("csv-transform", _":transform"); 802 | Folder.async("csv-stringify", _":parse | sub parse, stringify"); 803 | 804 | 805 | 806 | [parse]() 807 | 808 | function (input, args, cb) { 809 | var options = merge(args[0], this.plugins.csv.parse); 810 | csv.parse(input, options, cb); 811 | } 812 | 813 | 814 | [transform]() 815 | 816 | The first argument is needed and should be a function. 817 | 818 | function (input, args, cb) { 819 | var options = merge(args[1], this.plugins.csv.transform); 820 | var f = (typeit(args[0] === "function" ) ) ? 821 | args[0] : function (el) {return el;}; 822 | csv.transform( input, f, options, cb); 823 | } 824 | 825 | 826 | ##### cdoc 827 | 828 | * **csv-parse/transform/stringify** 829 | This is an interface into the node-csv library. It does the three 830 | named methods. 831 | The first argument can be an object of options except for transform in 832 | which the options are second and the first argument is a function to 833 | execute on each row. 834 | See [node-csv](http://csv.adaltas.com/) for more details. 835 | 836 | If you need to use the streaming power, you should access the full power 837 | of it using `Folder.requres.csv` and take a look at, for example, [so](http://stackoverflow.com/questions/23080413/nodejs-reading-csv-file) 838 | 839 | 840 | ## lodash 841 | 842 | This adds in the utility belt of lodash for very quick and easy manipulations 843 | of various objects. 844 | 845 | Since underscore is special in this syntax (though it probably could work), we 846 | use the command `dash`. 847 | 848 | var lodash = Folder.requires.lodash = require("lodash"); 849 | Folder.dash.lodash = [lodash, 1]; 850 | Folder.sync("lodash", _":fun"); 851 | 852 | [fun]() 853 | 854 | This takes the incoming as the first argument, making it appear as a method on 855 | the incoming thing. 856 | 857 | function (input, args) { 858 | if (args.length) { 859 | var method = args[0]; 860 | if ( typeit(lodash[method], 'function') ) { 861 | args[0] = input; 862 | return lodash[method].apply(lodash, args); 863 | } else { 864 | // this is an error. need to come up with a warning. 865 | this.warn("lodash", "unrecognized method", method); 866 | return input; 867 | } 868 | } else { 869 | return input; 870 | } 871 | } 872 | 873 | ##### cdoc 874 | 875 | * **lodash** The incoming data is the first 876 | argument into the function while the first argument is the method name. 877 | The other arguments are what they are. 878 | 879 | Example: ` abc | - pad 8, 0` 880 | 881 | ## html encodings 882 | 883 | This loads in the `he` npm module. 884 | 885 | var he = require('he'); 886 | Folder.requires.he = he; 887 | Folder.plugins.he = { 888 | encode : {}, 889 | decode : {}, 890 | }; 891 | Folder.sync("html-encode", _":encode"); 892 | Folder.sync("html-decode", _":encode| sub encode, decode"); 893 | Folder.sync("html-qescape", _":escape"); 894 | 895 | [encode]() 896 | 897 | function (input, args) { 898 | var options = merge(this.plugins.he.encode, args[0]); 899 | return he.encode(input, options); 900 | } 901 | 902 | [escape]() 903 | 904 | function (input) { 905 | return he.escape(input); 906 | } 907 | 908 | ##### cdoc 909 | 910 | * **html-encode/decode/qescape** This is an interface to the 911 | [he](https://github.com/mathiasbynens/he) library. It encodes and 912 | decodes all named html entities. There is also a simple escape function, 913 | that includes quotes which the lit-native html-escape does not. 914 | 915 | 916 | 917 | [off](# "block:") 918 | 919 | ## README 920 | 921 | 922 | # literate-programming [![Build Status](https://travis-ci.org/jostylr/literate-programming.png)](https://travis-ci.org/jostylr/literate-programming) 923 | 924 | 925 | This is the fat command-line client for 926 | [literate-programming-lib](https://github.com/jostylr/literate-programming-lib). 927 | It contains the full functionality for literate programming, including useful 928 | commands such as jshint included in it. For a thin client, 929 | check out 930 | [litpro](https://github.com/jostylr/litpro) 931 | 932 | 933 | Full documentation: [Literate Programming, MD: How to Treat and Prevent Software Project Mess](https://leanpub.com/literate-programming-md) 934 | 935 | This is not done being fully baked, hence v0.9. But this does represent a 936 | significant break from 0.8.4. You can take a look at convert.md for some 937 | observations of mine as I converted from the old version to the new. 938 | 939 | Install using `npm install literate-programming` 940 | 941 | Usage is `./node_modules/bin/litpro file` and it has some command flags. 942 | 943 | If you want a global install so that you just need to write 944 | `literate-programming` then use `npm install -g literate-programming`. 945 | 946 | The library has a full listing of the syntax, commands, and directives. Here 947 | we list the flags and new commands and directives. 948 | 949 | ## Example usage 950 | 951 | Save the following code to file `project.md` and run `literate-programming project.md`. 952 | 953 | # Welcome 954 | 955 | So you want to make a literate program? Let's have a program that outputs 956 | all numbers between 1 to 10. 957 | 958 | Let's save it in file count.js 959 | 960 | [count.js](#Structure "save: | jshint") 961 | 962 | ## Structure 963 | 964 | We have some intial setup. Then we will generate the array of numbers. We 965 | end with outputting the numbers. 966 | 967 | var numarr = [], start=1, end = 11, step = 1; 968 | 969 | _"Loop" 970 | 971 | _"Output" 972 | 973 | ## Output 974 | 975 | At this point, we have the array of numbers. Now we can join them with a 976 | comma and output that to the console. 977 | 978 | console.log("The numbers are: ", numarr.join(", ") ); 979 | 980 | ## Loop 981 | 982 | Set the loop up and push the numbers onto it. 983 | 984 | var i; 985 | for (i = start; i < end; i += step) { 986 | numarr.push(i); 987 | } 988 | 989 | ## Documentation 990 | 991 | For more information, see the 992 | [documentation book](https://leanpub.com/literate-programming-md) 993 | which is free to read online or available for purchase as a PDF. 994 | 995 | Some particularly useful syntax sections are: 996 | 997 | * [command-line flags](https://leanpub.com/literate-programming-md/read#leanpub-auto-command-line-1) 998 | * [directives](https://leanpub.com/literate-programming-md/read#leanpub-auto-directives-1) 999 | * [commands](https://leanpub.com/literate-programming-md/read#leanpub-auto-commands-1) 1000 | * [subcommands](https://leanpub.com/literate-programming-md/read#leanpub-auto-subcommands-1) 1001 | 1002 | 1003 | 1004 | ## Use and Security 1005 | 1006 | It is inherently unsecure to compile literate 1007 | program documents. No effort has been made to make it secure. Compiling a 1008 | literate program using this program is equivalent to running arbitrary code on 1009 | your computer. Only compile from trusted sources, i.e., use the same 1010 | precautions as running a node module. 1011 | 1012 | 1013 | ## LICENSE 1014 | 1015 | [MIT-LICENSE](https://github.com/jostylr/literate-programming/blob/master/LICENSE-MIT) 1016 | 1017 | ---! 1018 | 1019 | 1020 | [on](# "block:") 1021 | 1022 | ## Test 1023 | 1024 | The first test determines that it is hooked up correctly. This should pass the 1025 | same tests as literate-programming-cli. Just need to make sure it is hooked 1026 | up correctly. 1027 | 1028 | Then we have tests to specifically test each of the command and directives. 1029 | 1030 | We also have integration tests, many of which come from the old version. In 1031 | addition to the usual directory items, it also has an "old" directory for each 1032 | one to allow comparison of the old version and new version. This may be of 1033 | help for those adapting to the new version. 1034 | 1035 | 1036 | 1037 | /* global require */ 1038 | var tests = require('literate-programming-cli-test')("node ../../index.js"); 1039 | 1040 | var files = [["first", "first.md second.md -s ."], 1041 | ["testpro", "period.md testpro.md -s ."], 1042 | ["primes", "primes.md -s . -z primes:20"], 1043 | ["sample", "sample.md -s ."], 1044 | ["template", "templating.md simpletemp.md -s ."], 1045 | ["blackwhitehats", "blackwhitehats.md -s ."], 1046 | ["cinnamon", "cinnamon.md -s ."], 1047 | ["fence", "fence.md -s ."], 1048 | ["jstidy", "jstidy.md -s ."], 1049 | ["fizzbuzz", "fizzbuzz.md -s ."], 1050 | ["matrix", "matrix.md -s ."], 1051 | ["logs", "logs.md -s ."], 1052 | ["cheerio", "cheers.md -s ."], 1053 | ["integrated", "integrated.md -s ."], 1054 | ["date"], 1055 | ["csv"], 1056 | ["lodash"], 1057 | ["he"] 1058 | ].slice(0); 1059 | tests.apply(null, files); 1060 | 1061 | ### test gitignore 1062 | 1063 | This needs to go into each directory in tests. 1064 | 1065 | node_modules 1066 | /build 1067 | /cache 1068 | /.checksum 1069 | /err.test 1070 | /out.test 1071 | 1072 | Need to think of a better way of propagating one file to multiple 1073 | destinations. 1074 | 1075 | * [../tests/](# "cd: save") 1076 | * [first/.gitignore](# "save:") 1077 | * [testpro/.gitignore](# "save:") 1078 | * [primes/.gitignore](# "save:") 1079 | * [sample/.gitignore](# "save:") 1080 | * [template/.gitignore](# "save:") 1081 | * [blackwhitehats/.gitignore](# "save:") 1082 | * [cinnamon/.gitignore](# "save:") 1083 | * [fence/.gitignore](# "save:") 1084 | * [jstidy/.gitignore](# "save:") 1085 | * [fizzbuzz/.gitignore](# "save:") 1086 | * [matrix/.gitignore](# "save:") 1087 | * [logs/.gitignore](# "save:") 1088 | * [cheerio/.gitignore](# "save:") 1089 | * [integrated/.gitignore](# "save:") 1090 | * [date/.gitignore](# "save:") 1091 | * [csv/.gitignore](# "save:") 1092 | * [lodash/.gitignore](# "save:") 1093 | * [he/.gitignore](# "save:") 1094 | * [](# "cd: save" 1095 | 1096 | 1097 | 1098 | [off](# "block:) 1099 | 1100 | ## Converting 1101 | 1102 | Are you coming from the old version? There are syntax differences. Here we 1103 | list a few tips to help the process. 1104 | 1105 | Old way: `| clean raw"` takes the raw text of the section and makes it 1106 | cleaned up New way: New version does not see the commentary text. So we need 1107 | to act on the document itself. We also don't want the blocks to get evaluated 1108 | which automatically happens in this version. So we place directives to turn 1109 | off that feature: `[off](# "block:")` turns off block reading, 1110 | `[on](# "block:")` turns it back on. We then need to do the snippet cutting: 1111 | `| raw ## README, !---- | sub \n\ #, \n# |trim` 1112 | Raw says to take the raw document and 1113 | cut between the two given pieces. We still shift the headings to not be read 1114 | as heading and the sub puts it back. 1115 | 1116 | Moving towards a conventional setup of setup.md containing project files to 1117 | be put in home directory (package.json and lprc.js for example) using `litpro 1118 | -b . setup.md` then one can do `npm install` and then do `litpro` to process 1119 | `project.md` which then calls the other files. 1120 | 1121 | Convert substitute(...) to sub. In VIM: `:%s/substitute(\([^)]\+\))/sub \1/g` 1122 | 1123 | Boilerplate. Old syntax, rather wonky, was to use * to indicate a template to 1124 | fill in. New version has the compile command. So instead of `float tests*test 1125 | template` we would have `_"test template | compile float tests"` All the minor 1126 | sections in the template are required, but they can be empty. Instead of the * 1127 | in front of the minor, escape the underscore. `_"*:code"` --> `\_":code"` 1128 | While the asterisk notation is, in some sense, nicer, the async nature of the 1129 | new version made it problematic. 1130 | 1131 | Beware of h5 and h6 headers; they fill a new role. Reduce the number of hashes. 1132 | 1133 | Minor blocks should now best be in the form `[name]()` They can appear 1134 | anywhere; it is the form that matters. Alternatively, `[name](# ": | ...")` 1135 | can be used if there are pipe transformations desired. The key is the leading 1136 | colon and having a name. 1137 | 1138 | The old setup had minors that could have an extension between `: |` and that 1139 | would become part of the name. That is not present in the new one. It was not 1140 | really needed. Also minors can be referred to in the hash -- just use the 1141 | colon as one would, e.g. `[logs.htm](#structure "Save: main")` becomes 1142 | `[logs.htm](#structure:main "Save: ")` 1143 | 1144 | Blocks cannot refer to each other without problems as they will mutually wait 1145 | for each other. 1146 | 1147 | Fencing of code blocks follows commonmark syntax. Be careful about lists too. 1148 | 1149 | To eval code directly, one can use `| eval _"some code"`. The incoming text is 1150 | in the variable `text` and that variable is what the outgoing text is recorded 1151 | as. 1152 | 1153 | We no longer can evaluate the blocks in terms of where we are in the command 1154 | input stage. This was always confusing, anyway. Instead use backslashes and 1155 | the compile command. 1156 | 1157 | To access the arguments called by the command line, one can do 1158 | `doc.parent.stdin[whatever property]` and one can create whatever property by 1159 | doing `- z prop:val` 1160 | 1161 | The two things above allow one to have the literate program directly doing 1162 | computations instead of just creating a script that can be called. No real 1163 | reason for this, I suppose, but hey, it works. 1164 | 1165 | No replacement for 1166 | 1167 | ``` 1168 | \_"*:expected|heading" 1169 | ## Heading 1170 | 1171 | function () { 1172 | return this.hblock.heading.split("*")[0]; 1173 | } 1174 | 1175 | [heading](#heading "define: command | | now") 1176 | ``` 1177 | 1178 | 1179 | ## Break with previous versions 1180 | 1181 | This is a complete rewrite. The syntax is simplified so that only the ``_`code 1182 | block| function | functionn` `` syntax is needed. Leave off the code block to 1183 | just start using functions (leave the pipe). The code block in that syntax 1184 | need not be a code block, but could be a user-defined variable name, 1185 | 1186 | Another break in the syntax is the switch link. This was a link that was on a 1187 | line by itself. It was a kind of heading, one that would make a quick separate 1188 | code block under the same heading. I find it convenient. But in trying to 1189 | match the general parsing of markdown programs, I am moving towards using a 1190 | professional markdown parser that would make it difficult to recognize the 1191 | positioning of a link, but trivial to parse one. So the switch link will be a 1192 | link whose title quote starts with a colon. So an empty directive. It can 1193 | still be positioned as always. Also can implement it so that if the 1194 | parenthetical is completely empty, then that is a switch. I noticed that that 1195 | is what I often do. 1196 | 1197 | For header purposes, a link's square bracket portion will be returned to the 1198 | surrounding block. 1199 | 1200 | Also headers will have a way to be modified based on their levels. 1201 | I have never used levels 5 and 6, for example. 1202 | As an example, one could have level 5 headers for tests, docs, and examples, 1203 | that could then be compiled and run bey selecting those headers. Not sure yet. 1204 | 1205 | Also, there is no tracking of the non-significant text. So for example, raw 1206 | will not work in the same way. It was always a bit of a hack and now it will 1207 | be more so. There can be easily a plugin that will search for the heading and 1208 | cut the rest, etc. 1209 | 1210 | Multiple substitute cycles are no longer supported. I always found it hard to 1211 | reason about it and it greatly simplifies the code. If you need that 1212 | functionality, it probably is workable with substitutes and the variable 1213 | storage introduced. 1214 | 1215 | The compiled blocks are stored as variables. We can store arbitrary variable 1216 | names and so potentially can conflict with block names. You have been warned. 1217 | It is all "global" scope though you can use syntax to kind of scope it. Well, 1218 | actually we are scoped to the documents, that is `docname::..` gives another 1219 | scope which the var setting respects. 1220 | 1221 | Another break is that block names need to match. There is the main block which 1222 | has no minor associated with it and then there are the minors of the block. If 1223 | you want the minor commands on the main, then start the code line with `[](# 1224 | ":|...")` where the colon is there to indicate a minor directive, but with no 1225 | name and no extension, this will signal the main block. Note that this will 1226 | overwrite whatever was in the main code block, if anything. Once a block 1227 | (minor or not) is switched from, it cannot be added to later. Trust me, this 1228 | is a good thing. 1229 | 1230 | 1231 | --! 1232 | 1233 | [on](# "block:") 1234 | 1235 | 1236 | -------------------------------------------------------------------------------- /setup.md: -------------------------------------------------------------------------------- 1 | # [literate-programming](# "version:1.1.0; Fat command line for literate-programming") 2 | 3 | This is the command line portion of literate-programming. It depends on 4 | literate-programming-lib. 5 | 6 | 7 | At the moment, at least, I am of the firm opinion that one should structure a 8 | litpro directory as cache, build, src, lprc.js as where you start. These 9 | locations can be changed in the command line, but the idea is that you are at 10 | the top, it all goes down. 11 | 12 | Any initially given filenames are read as is. This allows for shell 13 | completion. It is a little odd in that command line is non-prefixed while 14 | loading from within doc is prefixed. One can also specify starting files in 15 | lprc.js by modifying args.files. 16 | 17 | ## Directory structure 18 | 19 | * [../](# "cd: save") 20 | * [lprc.js](#lprc "save: |jshint") This will define the jshint command 21 | * [package.json](#npm-package "save: | jshint ") The requisite package file for a npm project. 22 | * [LICENSE](#license-mit "save: ") The MIT license as I think that is the standard in the node community. 23 | * [.npmignore](#npmignore "save: ") 24 | * [.gitignore](#gitignore "save: ") 25 | * [.travis.yml](#travis "save: ") 26 | * [](# "cd: save") 27 | 28 | 29 | ## lprc 30 | 31 | This imports the jshint command and starts that the build directory is the 32 | current one and the default file to process is this one. 33 | 34 | /*global module, require */ 35 | module.exports = function(Folder, args) { 36 | 37 | if (args.file.length === 0) { 38 | args.file = ["project.md"]; 39 | } 40 | 41 | args.src = "."; 42 | 43 | //require('litpro-jshint')(Folder, args); 44 | 45 | }; 46 | 47 | 48 | 49 | ## NPM package 50 | 51 | The requisite npm package file. 52 | 53 | 54 | { 55 | "name": "_`g::docname`", 56 | "description": "_`g::tagline`", 57 | "version": "_`g::docversion`", 58 | "homepage": "https://github.com/_`g::gituser`/_`g::docname`", 59 | "author": { 60 | "name": "_`g::authorname`", 61 | "email": "_`g::authoremail`" 62 | }, 63 | "repository": { 64 | "type": "git", 65 | "url": "git://github.com/_`g::gituser`/_`g::docname`.git" 66 | }, 67 | "bugs": { 68 | "url": "https://github.com/_`g::gituser`/_`g::docname`/issues" 69 | }, 70 | "license": "MIT", 71 | "main": "index.js", 72 | "engines": { 73 | "node": ">=4.0" 74 | }, 75 | "dependencies":{ 76 | _"g::npm dependencies" 77 | }, 78 | "devDependencies" : { 79 | _"g::npm dev dependencies" 80 | }, 81 | "scripts" : { 82 | "test" : "node ./test.js" 83 | }, 84 | "keywords": ["literate programming"], 85 | "bin": { 86 | "literate-programming" : "./index.js" 87 | } 88 | } 89 | 90 | 91 | ## gitignore 92 | 93 | node_modules 94 | /build 95 | /cache 96 | /.checksum 97 | 98 | 99 | ## npmignore 100 | 101 | 102 | build 103 | .checksum 104 | cache 105 | tests 106 | test.js 107 | .travis.yml 108 | lprc.js 109 | *.md 110 | 111 | 112 | ## Travis 113 | 114 | A travis.yml file for continuous test integration! 115 | 116 | language: node_js 117 | node_js: 118 | - "node" 119 | - "4.0" 120 | sudo: false 121 | 122 | 123 | 124 | ## LICENSE MIT 125 | 126 | 127 | The MIT License (MIT) 128 | Copyright (c) _"g::year" _"g::authorname" 129 | 130 | Permission is hereby granted, free of charge, to any person obtaining a copy 131 | of this software and associated documentation files (the "Software"), to deal 132 | in the Software without restriction, including without limitation the rights 133 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 134 | copies of the Software, and to permit persons to whom the Software is 135 | furnished to do so, subject to the following conditions: 136 | 137 | The above copyright notice and this permission notice shall be included in all 138 | copies or substantial portions of the Software. 139 | 140 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 141 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 142 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 143 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 144 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 145 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 146 | SOFTWARE. 147 | 148 | 149 | 150 | 151 | 152 | by [James Taylor](https://github.com/jostylr "npminfo: jostylr@gmail.com ; 153 | deps: literate-programming-cli 2.1.0, jshint 2.11.0, 154 | postcss 7.0.26, autoprefixer 9.7.4, pug 2.0.4, 155 | markdown-it 10.0.0, cheerio 0.22.0, js-beautify 1.10.3, 156 | html-minifier 4.0.0, clean-css 4.2.1, uglify-js 3.7.5, 157 | csv 5.3.1, date-fns 1.30.1, lodash 4.17.15, he 1.2.0 ; 158 | dev: litpro-jshint 0.4.0, 159 | literate-programming-cli-test 0.5.1, 160 | markdown-it-anchor 5.2.5") 161 | 162 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* global require */ 2 | var tests = require('literate-programming-cli-test')("node ../../index.js"); 3 | 4 | var files = [["first", "first.md second.md -s ."], 5 | ["testpro", "period.md testpro.md -s ."], 6 | ["primes", "primes.md -s . -z primes:20"], 7 | ["sample", "sample.md -s ."], 8 | ["template", "templating.md simpletemp.md -s ."], 9 | ["blackwhitehats", "blackwhitehats.md -s ."], 10 | ["cinnamon", "cinnamon.md -s ."], 11 | ["fence", "fence.md -s ."], 12 | ["jstidy", "jstidy.md -s ."], 13 | ["fizzbuzz", "fizzbuzz.md -s ."], 14 | ["matrix", "matrix.md -s ."], 15 | ["logs", "logs.md -s ."], 16 | ["cheerio", "cheers.md -s ."], 17 | ["integrated", "integrated.md -s ."], 18 | ["date"], 19 | ["csv"], 20 | ["lodash"], 21 | ["he"] 22 | ].slice(0); 23 | tests.apply(null, files); 24 | -------------------------------------------------------------------------------- /tests/blackwhitehats/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/blackwhitehats/blackwhitehats.md: -------------------------------------------------------------------------------- 1 | # Black and White Hats 2 | 3 | This is a literate program to implement strategies concerning the following 4 | problem: 5 | 6 | >You have 100 people in a line. Each is wearing a black or white hat, but they 7 | >don't know which. Each hat is equally likely to be on each person's head. 8 | >They are put in a line, and they can see all the people after them. Starting 9 | >with the first, they are each asked to make a guess as to which color hat 10 | >they are wearing. They can agree on a strategy and they can hear all previous 11 | >guesses. There is also one person in the line that may not follow the 12 | >strategy. What is the optimal strategy for maximizing the number of right 13 | >guesses? 14 | 15 | What follows below is the solution and can also be found on 16 | [github](https://github.com/jostylr/literate-programming/blob/master/examples/blackwhitehats.md). 17 | There is a live version on [JS 18 | Bin](http://jsbin.com/UpeveZe/1/edit?js,console). 19 | 20 | ## [hats.js](#hats.js "save: | jshint") 21 | 22 | We are going to simulate this with a nodejs program. We first generate the 23 | line. Then we have a starting function followed by a function that works for 24 | the later ones, using the guesses and the next line. 25 | 26 | Our strategy will be the first person indicates the parity of the number of 27 | black hats in the line (black if odd, white if not). Then the rest will simply 28 | say "black" if the parity changes and white otherwise. The one who does not 29 | follow this will mess up the person following them, but that's it. At least 30 | that's the assertion. 31 | 32 | 33 | var i, 34 | n = 100, 35 | line = [], 36 | msg = [], 37 | remainder = [], 38 | //traitor = n+1, 39 | traitor = Math.floor(Math.random()*n), 40 | success = 0, 41 | current, 42 | oldParity, 43 | parity; 44 | 45 | var parityFun = _"parity"; 46 | 47 | _"Make a line" 48 | 49 | remainder = line.slice(1); 50 | 51 | _"First guess" 52 | 53 | _"Rest of guesses" 54 | 55 | console.log(line.join('')); 56 | console.log(msg.join('')); 57 | 58 | console.log("traitor at "+ traitor + "\nNumber of Correct guesses: " + success); 59 | 60 | ## Make a line 61 | 62 | We can start with making the line. 63 | 64 | for (i = 0; i < n; i += 1) { 65 | if (Math.random() <= 0.5) { 66 | line.push("b"); 67 | } else { 68 | line.push("w"); 69 | } 70 | } 71 | 72 | ## Parity 73 | 74 | We need to determine the parity of the number of blacks. 75 | 76 | function (arr) { 77 | var i, n = arr.length, count = 0; 78 | 79 | for (i = 0; i < n; i += 1) { 80 | if (arr[i] === "b") { 81 | count +=1; 82 | } 83 | } 84 | 85 | return count %2; 86 | } 87 | 88 | ## First guess 89 | 90 | If the parity is 1, we say "b" otherwise "w". Then we check for success. 91 | 92 | parity = parityFun(remainder); 93 | i = 0; 94 | if (i === traitor) { 95 | msg.push("T"); 96 | parity = (parity +1)%2; 97 | } else { 98 | if (parity) { 99 | msg.push("b"); 100 | } else { 101 | msg.push("w"); 102 | } 103 | if (msg[i] === line[i]) { 104 | success += 1; 105 | } 106 | } 107 | 108 | 109 | ## Rest of guesses 110 | 111 | Here we go through all the remaining ones. Different parity, then black hat. 112 | Otherwise not. 113 | 114 | 115 | while (remainder.length !== 0) { 116 | i += 1; 117 | current = remainder.shift(); 118 | oldParity = parity; 119 | parity = parityFun(remainder); 120 | if (i === traitor) { 121 | msg.push("T"); 122 | parity = (parity +1)%2; 123 | } else { 124 | if (parity !== oldParity) { 125 | msg.push("b"); 126 | } else { 127 | msg.push("w"); 128 | } 129 | if (msg[i] === line[i]) { 130 | success += 1; 131 | } 132 | } 133 | } 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /tests/blackwhitehats/canonical/build/hats.js: -------------------------------------------------------------------------------- 1 | var i, 2 | n = 100, 3 | line = [], 4 | msg = [], 5 | remainder = [], 6 | //traitor = n+1, 7 | traitor = Math.floor(Math.random()*n), 8 | success = 0, 9 | current, 10 | oldParity, 11 | parity; 12 | 13 | var parityFun = function (arr) { 14 | var i, n = arr.length, count = 0; 15 | 16 | for (i = 0; i < n; i += 1) { 17 | if (arr[i] === "b") { 18 | count +=1; 19 | } 20 | } 21 | 22 | return count %2; 23 | }; 24 | 25 | for (i = 0; i < n; i += 1) { 26 | if (Math.random() <= 0.5) { 27 | line.push("b"); 28 | } else { 29 | line.push("w"); 30 | } 31 | } 32 | 33 | remainder = line.slice(1); 34 | 35 | parity = parityFun(remainder); 36 | i = 0; 37 | if (i === traitor) { 38 | msg.push("T"); 39 | parity = (parity +1)%2; 40 | } else { 41 | if (parity) { 42 | msg.push("b"); 43 | } else { 44 | msg.push("w"); 45 | } 46 | if (msg[i] === line[i]) { 47 | success += 1; 48 | } 49 | } 50 | 51 | while (remainder.length !== 0) { 52 | i += 1; 53 | current = remainder.shift(); 54 | oldParity = parity; 55 | parity = parityFun(remainder); 56 | if (i === traitor) { 57 | msg.push("T"); 58 | parity = (parity +1)%2; 59 | } else { 60 | if (parity !== oldParity) { 61 | msg.push("b"); 62 | } else { 63 | msg.push("w"); 64 | } 65 | if (msg[i] === line[i]) { 66 | success += 1; 67 | } 68 | } 69 | } 70 | 71 | console.log(line.join('')); 72 | console.log(msg.join('')); 73 | 74 | console.log("traitor at "+ traitor + "\nNumber of Correct guesses: " + success); 75 | -------------------------------------------------------------------------------- /tests/blackwhitehats/original/blackwhitehats.md: -------------------------------------------------------------------------------- 1 | # Black and White Hats 2 | 3 | This is a literate program to implement strategies concerning the following problem: 4 | 5 | >You have 100 people in a line. Each is wearing a black or white hat, but they don't know which. Each hat is equally likely to be on each person's head. They are put in a line, and they can see all the people after them. Starting with the first, they are each asked to make a guess as to which color hat they are wearing. They can agree on a strategy and they can hear all previous guesses. There is also one person in the line that may not follow the strategy. What is the optimal strategy for maximizing the number of right guesses? 6 | 7 | What follows below is the solution and can also be found on [github](https://github.com/jostylr/literate-programming/blob/master/examples/blackwhitehats.md). There is a live version on [JS Bin](http://jsbin.com/UpeveZe/1/edit?js,console). 8 | 9 | ## [hats.js](#hats.js "save: | jshint") 10 | 11 | We are going to simulate this with a nodejs program. We first generate the line. Then we have a starting function followed by a function that works for the later ones, using the guesses and the next line. 12 | 13 | Our strategy will be the first person indicates the parity of the number of black hats in the line (black if odd, white if not). Then the rest will simply say "black" if the parity changes and white otherwise. The one who does not follow this will mess up the person following them, but that's it. At least that's the assertion. 14 | 15 | 16 | var i, 17 | n = 100, 18 | line = [], 19 | msg = [], 20 | remainder = [], 21 | //traitor = n+1, 22 | traitor = Math.floor(Math.random()*n), 23 | success = 0, 24 | current, 25 | oldParity, 26 | parity; 27 | 28 | var parityFun = _"parity"; 29 | 30 | _"Make a line" 31 | 32 | remainder = line.slice(1); 33 | 34 | _"First guess" 35 | 36 | _"Rest of guesses" 37 | 38 | console.log(line.join('')); 39 | console.log(msg.join('')); 40 | 41 | console.log("traitor at "+ traitor + "\nNumber of Correct guesses: " + success); 42 | 43 | ## Make a line 44 | 45 | We can start with making the line. 46 | 47 | for (i = 0; i < n; i += 1) { 48 | if (Math.random() <= 0.5) { 49 | line.push("b"); 50 | } else { 51 | line.push("w"); 52 | } 53 | } 54 | 55 | ## Parity 56 | 57 | We need to determine the parity of the number of blacks. 58 | 59 | function (arr) { 60 | var i, n = arr.length, count = 0; 61 | 62 | for (i = 0; i < n; i += 1) { 63 | if (arr[i] === "b") { 64 | count +=1; 65 | } 66 | } 67 | 68 | return count %2; 69 | } 70 | 71 | ## First guess 72 | 73 | If the parity is 1, we say "b" otherwise "w". Then we check for success. 74 | 75 | parity = parityFun(remainder); 76 | i = 0; 77 | if (i === traitor) { 78 | msg.push("T"); 79 | parity = (parity +1)%2; 80 | } else { 81 | if (parity) { 82 | msg.push("b"); 83 | } else { 84 | msg.push("w"); 85 | } 86 | if (msg[i] === line[i]) { 87 | success += 1; 88 | } 89 | } 90 | 91 | 92 | ## Rest of guesses 93 | 94 | Here we go through all the remaining ones. Different parity, then black hat. Otherwise not. 95 | 96 | 97 | while (remainder.length !== 0) { 98 | i += 1; 99 | current = remainder.shift(); 100 | oldParity = parity; 101 | parity = parityFun(remainder); 102 | if (i === traitor) { 103 | msg.push("T"); 104 | parity = (parity +1)%2; 105 | } else { 106 | if (parity !== oldParity) { 107 | msg.push("b"); 108 | } else { 109 | msg.push("w"); 110 | } 111 | if (msg[i] === line[i]) { 112 | success += 1; 113 | } 114 | } 115 | } 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /tests/cheerio/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/cheerio/canonical/build/out.html: -------------------------------------------------------------------------------- 1 | 4 |

5 | 6 | 7 | -------------------------------------------------------------------------------- /tests/cheerio/canonical/out.test: -------------------------------------------------------------------------------- 1 | # DOC: cheers.md 2 | ## 0 3 | * Problem with cheerio: .great,replace,

hi

4 | # FOLDER LOGS 5 | ## SAVED 6 | ./build/out.html 7 | ## DONE 8 | ./build 9 | -------------------------------------------------------------------------------- /tests/cheerio/cheers.md: -------------------------------------------------------------------------------- 1 | # Testing cheerio 2 | 3 | 6 |
7 |
8 |
9 | 10 | 11 | [out.html](# "save: | cheerio .great, append,
  • awesome
  • | 12 | ch-replace #geese,

    , .right, ") 13 | 14 | 15 | ## Failure 16 | 17 | _"testing cheerio| cheerio .great, replace,

    hi

    " 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/cinnamon/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/cinnamon/canonical/build/cinnamon.css: -------------------------------------------------------------------------------- 1 | [data-cinnamon] { 2 | position: relative; 3 | display: inline-block; 4 | } 5 | .cinnamon { 6 | z-index: -1; 7 | position: absolute; 8 | top: 0; left: 0; 9 | display: inline-block; 10 | height: 100%; 11 | width: 100%; 12 | color: rgba(0,0,0,0); 13 | overflow: hidden; font-size: 999px; 14 | } 15 | @media all and (device-width: 768px) and (device-height: 1024px) { 16 | .cinnamon { 17 | z-index: 1; 18 | opacity: 0.25; } 19 | } 20 | -------------------------------------------------------------------------------- /tests/cinnamon/canonical/build/cinnamon.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cinnamon.js 5 | 26 | 27 | 28 |

    Cinnamon.js

    29 | 30 |

    A visitor to your site wants to follow your Twitter account. You have a link to it in your footer, but their search for "Twitter" comes up empty and they move on — unfortunately, you happened to name the link "@username" instead.

    31 | 32 |

    Cinnamon.js is a script that allows users to find elements by their synonyms, using the browser's built-in Find feature. To see it in action, search this page for "Twitter", "Spice", "Email" or "Contact".

    33 | 34 |
    35 | 36 |

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque cursus orci ut mi laoreet rhoncus. Pellentesque congue urna tincidunt tortor rhoncus dapibus. Duis faucibus dolor a sem ultrices at facilisis risus cursus. Cras vel euismod nisl. Ut vitae risus et libero sagittis ultrices et vitae ligula. Aliquam at turpis id diam placerat consequat at vitae est. Aenean tellus magna, lacinia vitae facilisis facilisis, egestas ut sapien. Follow me at @thomashpark. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec tincidunt dapibus dui luctus rhoncus. Nam sagittis egestas blandit. Nulla imperdiet tincidunt enim, a tempus sapien volutpat a. Maecenas sed elit ipsum, ut tristique odio. Suspendisse potenti.

    37 | 38 |

    Cinnamon
    Photo by kobiz7

    39 | 40 |

    Etiam et elit enim, quis semper metus. Nunc sodales posuere turpis, viverra vestibulum lectus vehicula sed. Nulla quis augue nec nibh varius pharetra adipiscing non felis. Etiam lobortis vestibulum luctus. Cras non felis enim, id gravida libero. Vivamus vulputate nisi at tellus pulvinar faucibus sodales mi feugiat. Reach me here. Proin id est ut quam dictum venenatis ac non risus. Ut porttitor mattis odio vel elementum. Aliquam molestie lorem nec diam pharetra et malesuada diam condimentum. Cras feugiat pulvinar sollicitudin. Etiam id diam fermentum quam varius laoreet. Quisque nibh nunc, ullamcorper et bibendum at, ultrices et lorem. Cras vitae euismod felis. Donec lectus libero, ornare eget luctus nec, dictum et sapien. Proin viverra justo ac augue pellentesque aliquet.

    41 | 42 |
    43 | 44 |

    To add to your page, include cinnamon.js. Then wrap your element of choice (such as in span tags) and give the data-cinnamon attribute a comma-separated list of terms. For example, <span data-cinnamon="azure,cerulean,cobalt">blue</span>. If you wrap an image, its alt text will also be used, as in the example above.

    45 | 46 | 47 | 48 | 49 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /tests/cinnamon/canonical/build/cinnamon.js: -------------------------------------------------------------------------------- 1 | // Cinnamon.js 2 | // Version: 1.0.0 3 | // Author: Thomas Park 4 | // License: MIT 5 | 6 | (function () { 7 | 8 | // Add styles 9 | var overflow = 'hidden', 10 | fontsize = '999px'; 11 | 12 | var css = "[data-cinnamon] { " + 13 | " position: relative; " + 14 | " display: inline-block; " + 15 | "} " + 16 | ".cinnamon { " + 17 | " z-index: -1; " + 18 | " position: absolute; " + 19 | " top: 0; left: 0; " + 20 | " display: inline-block; " + 21 | " height: 100%; " + 22 | " width: 100%; " + 23 | " color: rgba(0,0,0,0); " + 24 | " overflow: hidden; font-size: 999px; " + 25 | "}" + 26 | "@media all and (device-width: 768px) and (device-height: 1024px) {" + 27 | " .cinnamon { " + 28 | " z-index: 1; " + 29 | " opacity: 0.25; } " + 30 | "}"; 31 | 32 | // Alternate styles for Safari 33 | if ((navigator.userAgent.indexOf('Safari') !== -1 ) && ( navigator.userAgent.indexOf('Chrome') === -1)) { 34 | css.replace('overflow: hidden; font-size: 999px; ', 'overflow: visible; font-size: inherit; ' ); 35 | } 36 | 37 | var head = document.head || document.getElementsByTagName('head')[0], 38 | style = document.createElement('style'); 39 | 40 | style.type = 'text/css'; 41 | 42 | if (style.styleSheet) { 43 | style.styleSheet.cssText = css; 44 | } else { 45 | style.appendChild(document.createTextNode(css)); 46 | } 47 | 48 | head.appendChild(style); 49 | 50 | // Add elements 51 | var cinnamons = document.querySelectorAll('[data-cinnamon]'); 52 | 53 | for (var i = 0; i < cinnamons.length; i++) { 54 | 55 | var cinnamon = cinnamons[i], 56 | synonyms = cinnamon.getAttribute('data-cinnamon').split(','), 57 | image = cinnamon.getElementsByTagName('img')[0]; 58 | 59 | if (image && image.getAttribute('alt')) { 60 | synonyms.push(image.getAttribute('alt')); 61 | } 62 | 63 | for (var j = 0; j < synonyms.length; j++) { 64 | var e = document.createElement('span'); 65 | e.className = 'cinnamon'; 66 | 67 | // IE8 doesn't support textContent 68 | if ((e.textContent) && (typeof (e.textContent) !== "undefined")) { 69 | e.textContent = synonyms[j]; 70 | } else { 71 | e.innerText = synonyms[j]; 72 | } 73 | 74 | cinnamon.appendChild(e); 75 | } 76 | } 77 | })(); 78 | -------------------------------------------------------------------------------- /tests/cinnamon/canonical/build/cinnamon_emb.js: -------------------------------------------------------------------------------- 1 | // Cinnamon.js 2 | // Version: 1.0.0 3 | // Author: Thomas Park 4 | // License: MIT 5 | 6 | (function () { 7 | 8 | // Alternate styles for Safari 9 | if ((navigator.userAgent.indexOf('Safari') !== -1 ) && ( navigator.userAgent.indexOf('Chrome') === -1)) { 10 | var css = ".cinnamon { overflow: visible; font-size: inherit; }"; 11 | 12 | var head = document.head || document.getElementsByTagName('head')[0], 13 | style = document.createElement('style'); 14 | 15 | style.type = 'text/css'; 16 | 17 | if (style.styleSheet) { 18 | style.styleSheet.cssText = css; 19 | } else { 20 | style.appendChild(document.createTextNode(css)); 21 | } 22 | 23 | head.appendChild(style); 24 | } 25 | 26 | // Add elements 27 | var cinnamons = document.querySelectorAll('[data-cinnamon]'); 28 | 29 | for (var i = 0; i < cinnamons.length; i++) { 30 | 31 | var cinnamon = cinnamons[i], 32 | synonyms = cinnamon.getAttribute('data-cinnamon').split(','), 33 | image = cinnamon.getElementsByTagName('img')[0]; 34 | 35 | if (image && image.getAttribute('alt')) { 36 | synonyms.push(image.getAttribute('alt')); 37 | } 38 | 39 | for (var j = 0; j < synonyms.length; j++) { 40 | var e = document.createElement('span'); 41 | e.className = 'cinnamon'; 42 | 43 | // IE8 doesn't support textContent 44 | if ((e.textContent) && (typeof (e.textContent) !== "undefined")) { 45 | e.textContent = synonyms[j]; 46 | } else { 47 | e.innerText = synonyms[j]; 48 | } 49 | 50 | cinnamon.appendChild(e); 51 | } 52 | } 53 | })(); 54 | -------------------------------------------------------------------------------- /tests/cinnamon/cinnamon.md: -------------------------------------------------------------------------------- 1 | # Cinnamon 2 | 3 | This is a literate program version of what was once found at https://github.com/thomaspark/cinnamon.js It does not reflect the current state and is just another example. 4 | 5 | The goal wass to add synonyms to the text for Ctrl+F purposes. 6 | 7 | There are two versions. One is for loading a single file that handles both CSS and JavaScript. 8 | 9 | ## Files 10 | 11 | [cinnamon.js ](#One-file "Save:| jshint") 12 | 13 | And then there is the more emebeddable, separated version. 14 | 15 | [cinnamon.css](#Making-it-hidden:css "Save:") 16 | 17 | [cinnamon_emb.js](#Embedded "save:| jshint") 18 | 19 | And the example file, 20 | 21 | [cinnamon.html](#A-sample-document "save:") 22 | 23 | ## One file 24 | 25 | // Cinnamon.js 26 | // Version: 1.0.0 27 | // Author: Thomas Park 28 | // License: MIT 29 | 30 | (function () { 31 | 32 | _"For single file" 33 | 34 | _"Adding the synonyms" 35 | })(); 36 | 37 | 38 | ## Embedded 39 | 40 | Here we have the CSS separate. 41 | 42 | // Cinnamon.js 43 | // Version: 1.0.0 44 | // Author: Thomas Park 45 | // License: MIT 46 | 47 | (function () { 48 | 49 | _"Dealing with Safari" 50 | 51 | _"Adding the synonyms" 52 | })(); 53 | 54 | 55 | 56 | 57 | ## Adding the synonyms 58 | 59 | Grab the elements with data-cinnamon. Looping over those, we get the synonyms 60 | by splitting on commas and then add in span elements with the words. 61 | 62 | Note that this should be at the end of the body so that it has all the 63 | elements to work with. 64 | 65 | // Add elements 66 | var cinnamons = document.querySelectorAll('[data-cinnamon]'); 67 | 68 | for (var i = 0; i < cinnamons.length; i++) { 69 | 70 | var cinnamon = cinnamons[i], 71 | synonyms = cinnamon.getAttribute('data-cinnamon').split(','), 72 | image = cinnamon.getElementsByTagName('img')[0]; 73 | 74 | if (image && image.getAttribute('alt')) { 75 | synonyms.push(image.getAttribute('alt')); 76 | } 77 | 78 | for (var j = 0; j < synonyms.length; j++) { 79 | var e = document.createElement('span'); 80 | e.className = 'cinnamon'; 81 | 82 | // IE8 doesn't support textContent 83 | if ((e.textContent) && (typeof (e.textContent) !== "undefined")) { 84 | e.textContent = synonyms[j]; 85 | } else { 86 | e.innerText = synonyms[j]; 87 | } 88 | 89 | cinnamon.appendChild(e); 90 | } 91 | } 92 | 93 | 94 | ## Making it hidden 95 | 96 | [html]() 97 | 98 | 101 | 102 | [css]() 103 | 104 | [data-cinnamon] { 105 | position: relative; 106 | display: inline-block; 107 | } 108 | .cinnamon { 109 | z-index: -1; 110 | position: absolute; 111 | top: 0; left: 0; 112 | display: inline-block; 113 | height: 100%; 114 | width: 100%; 115 | color: rgba(0,0,0,0); 116 | _":Default over font" 117 | } 118 | @media all and (device-width: 768px) and (device-height: 1024px) { 119 | .cinnamon { 120 | z-index: 1; 121 | opacity: 0.25; } 122 | } 123 | 124 | [Default over font]() 125 | 126 | overflow: hidden; font-size: 999px; 127 | 128 | [Safari over font]() 129 | 130 | overflow: visible; font-size: inherit; 131 | 132 | [Dealing with Safari]() 133 | 134 | Safari has some issues so we need to do some replacing in that case. 135 | 136 | // Alternate styles for Safari 137 | if ((navigator.userAgent.indexOf('Safari') !== -1 ) && ( navigator.userAgent.indexOf('Chrome') === -1)) { 138 | css.replace('_":default over font"', '_":Safari over font"' ); 139 | } 140 | 141 | 142 | 143 | ### For single file 144 | 145 | This is for attaching the style. 146 | 147 | // Add styles 148 | var overflow = 'hidden', 149 | fontsize = '999px'; 150 | 151 | var css = _"Making it hidden:css | js-string"; 152 | 153 | _"Making it hidden:Dealing with Safari" 154 | 155 | _"Adding CSS" 156 | 157 | 158 | ## Dealing with Safari 159 | 160 | // Alternate styles for Safari 161 | if ((navigator.userAgent.indexOf('Safari') !== -1 ) && ( navigator.userAgent.indexOf('Chrome') === -1)) { 162 | var css = ".cinnamon { _"Making it hidden:safari over font" }"; 163 | 164 | _"Adding CSS" 165 | } 166 | 167 | ## Adding CSS 168 | 169 | This is how we add CSS stylistically. 170 | 171 | var head = document.head || document.getElementsByTagName('head')[0], 172 | style = document.createElement('style'); 173 | 174 | style.type = 'text/css'; 175 | 176 | if (style.styleSheet) { 177 | style.styleSheet.cssText = css; 178 | } else { 179 | style.appendChild(document.createTextNode(css)); 180 | } 181 | 182 | head.appendChild(style); 183 | 184 | 185 | ## A sample document 186 | 187 | 188 | 189 | 190 | Cinnamon.js 191 | _"Making it hidden:html" 192 | 193 | 194 |

    Cinnamon.js

    195 | 196 |

    A visitor to your site wants to follow your Twitter account. You have a link to it in your footer, but their search for "Twitter" comes up empty and they move on — unfortunately, you happened to name the link "@username" instead.

    197 | 198 |

    Cinnamon.js is a script that allows users to find elements by their synonyms, using the browser's built-in Find feature. To see it in action, search this page for "Twitter", "Spice", "Email" or "Contact".

    199 | 200 |
    201 | 202 |

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque cursus orci ut mi laoreet rhoncus. Pellentesque congue urna tincidunt tortor rhoncus dapibus. Duis faucibus dolor a sem ultrices at facilisis risus cursus. Cras vel euismod nisl. Ut vitae risus et libero sagittis ultrices et vitae ligula. Aliquam at turpis id diam placerat consequat at vitae est. Aenean tellus magna, lacinia vitae facilisis facilisis, egestas ut sapien. Follow me at @thomashpark. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec tincidunt dapibus dui luctus rhoncus. Nam sagittis egestas blandit. Nulla imperdiet tincidunt enim, a tempus sapien volutpat a. Maecenas sed elit ipsum, ut tristique odio. Suspendisse potenti.

    203 | 204 |

    Cinnamon
    Photo by kobiz7

    205 | 206 |

    Etiam et elit enim, quis semper metus. Nunc sodales posuere turpis, viverra vestibulum lectus vehicula sed. Nulla quis augue nec nibh varius pharetra adipiscing non felis. Etiam lobortis vestibulum luctus. Cras non felis enim, id gravida libero. Vivamus vulputate nisi at tellus pulvinar faucibus sodales mi feugiat. Reach me here. Proin id est ut quam dictum venenatis ac non risus. Ut porttitor mattis odio vel elementum. Aliquam molestie lorem nec diam pharetra et malesuada diam condimentum. Cras feugiat pulvinar sollicitudin. Etiam id diam fermentum quam varius laoreet. Quisque nibh nunc, ullamcorper et bibendum at, ultrices et lorem. Cras vitae euismod felis. Donec lectus libero, ornare eget luctus nec, dictum et sapien. Proin viverra justo ac augue pellentesque aliquet.

    207 | 208 |
    209 | 210 |

    To add to your page, include cinnamon.js. Then wrap your element of choice (such as in span tags) and give the data-cinnamon attribute a comma-separated list of terms. For example, <span data-cinnamon="azure,cerulean,cobalt">blue</span>. If you wrap an image, its alt text will also be used, as in the example above.

    211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | -------------------------------------------------------------------------------- /tests/cinnamon/original/cinnamon.md: -------------------------------------------------------------------------------- 1 | # Cinnamon 2 | 3 | This is a literate program version of what was once found at https://github.com/thomaspark/cinnamon.js It does not reflect the current state and is just another example. 4 | 5 | The goal wass to add synonyms to the text for Ctrl+F purposes. 6 | 7 | There are two versions. One is for loading a single file that handles both CSS and JavaScript. 8 | 9 | ## Files 10 | 11 | [cinnamon.js ](#One-file "Save:| jshint") 12 | 13 | And then there is the more mebeddable, separated version. 14 | 15 | [cinnamon.css](#Making-it-hidden "Save: .css") 16 | 17 | [cinnamon_emb.js](#Embedded "save:| jshint") 18 | 19 | And the example file, 20 | 21 | [cinnamon.html](#A-sample-document "save:") 22 | 23 | ## One file 24 | 25 | // Cinnamon.js 26 | // Version: 1.0.0 27 | // Author: Thomas Park 28 | // License: MIT 29 | 30 | (function () { 31 | 32 | _"For single file" 33 | 34 | _"Adding the synonyms" 35 | })(); 36 | 37 | 38 | ## Embedded 39 | 40 | Here we have the CSS separate. 41 | 42 | // Cinnamon.js 43 | // Version: 1.0.0 44 | // Author: Thomas Park 45 | // License: MIT 46 | 47 | (function () { 48 | 49 | _"Dealing with Safari" 50 | 51 | _"Adding the synonyms" 52 | })(); 53 | 54 | 55 | 56 | 57 | ## Adding the synonyms 58 | 59 | Grab the elements with data-cinnamon. Looping over those, we get the synonyms by splitting on commas and then add in span elements with the words. 60 | 61 | Note that this should be at the end of the body so that it has all the elements to work with. 62 | 63 | // Add elements 64 | var cinnamons = document.querySelectorAll('[data-cinnamon]'); 65 | 66 | for (var i = 0; i < cinnamons.length; i++) { 67 | 68 | var cinnamon = cinnamons[i], 69 | synonyms = cinnamon.getAttribute('data-cinnamon').split(','), 70 | image = cinnamon.getElementsByTagName('img')[0]; 71 | 72 | if (image && image.getAttribute('alt')) { 73 | synonyms.push(image.getAttribute('alt')); 74 | } 75 | 76 | for (var j = 0; j < synonyms.length; j++) { 77 | var e = document.createElement('span'); 78 | e.className = 'cinnamon'; 79 | 80 | // IE8 doesn't support textContent 81 | if ((e.textContent) && (typeof (e.textContent) !== "undefined")) { 82 | e.textContent = synonyms[j]; 83 | } else { 84 | e.innerText = synonyms[j]; 85 | } 86 | 87 | cinnamon.appendChild(e); 88 | } 89 | } 90 | 91 | 92 | ## Making it hidden 93 | 94 | [](# "html") 95 | 96 | 99 | 100 | [](# "CSS") 101 | 102 | [data-cinnamon] { 103 | position: relative; 104 | display: inline-block; 105 | } 106 | .cinnamon { 107 | z-index: -1; 108 | position: absolute; 109 | top: 0; left: 0; 110 | display: inline-block; 111 | height: 100%; 112 | width: 100%; 113 | color: rgba(0,0,0,0); 114 | _":Default over font" 115 | } 116 | @media all and (device-width: 768px) and (device-height: 1024px) { 117 | .cinnamon { 118 | z-index: 1; 119 | opacity: 0.25; } 120 | } 121 | 122 | [Default over font](# "css") 123 | 124 | overflow: hidden; font-size: 999px; 125 | 126 | [Safari over font](# "css") 127 | 128 | overflow: visible; font-size: inherit; 129 | 130 | [Dealing with Safari](# "css") 131 | 132 | Safari has some issues so we need to do some replacing in that case. 133 | 134 | // Alternate styles for Safari 135 | if ((navigator.userAgent.indexOf('Safari') !== -1 ) && ( navigator.userAgent.indexOf('Chrome') === -1)) { 136 | css.replace('_":default over font"', '_":Safari over font"' ); 137 | } 138 | 139 | 140 | 141 | ### For single file 142 | 143 | This is for attaching the style. 144 | 145 | // Add styles 146 | var overflow = 'hidden', 147 | fontsize = '999px'; 148 | 149 | var css = _"Making it hidden | stringify()"; 150 | 151 | _"Making it hidden : Dealing with Safari" 152 | 153 | _"Adding CSS" 154 | 155 | 156 | ## Dealing with Safari 157 | 158 | // Alternate styles for Safari 159 | if ((navigator.userAgent.indexOf('Safari') !== -1 ) && ( navigator.userAgent.indexOf('Chrome') === -1)) { 160 | var css = ".cinnamon { _"Making it hidden : safari over font" }"; 161 | 162 | _"Adding CSS" 163 | } 164 | 165 | ## Adding CSS 166 | 167 | This is how we add CSS stylistically. 168 | 169 | var head = document.head || document.getElementsByTagName('head')[0], 170 | style = document.createElement('style'); 171 | 172 | style.type = 'text/css'; 173 | 174 | if (style.styleSheet) { 175 | style.styleSheet.cssText = css; 176 | } else { 177 | style.appendChild(document.createTextNode(css)); 178 | } 179 | 180 | head.appendChild(style); 181 | 182 | 183 | ## A sample document 184 | 185 | 186 | 187 | 188 | Cinnamon.js 189 | _"Making it hidden:.html" 190 | 191 | 192 |

    Cinnamon.js

    193 | 194 |

    A visitor to your site wants to follow your Twitter account. You have a link to it in your footer, but their search for "Twitter" comes up empty and they move on — unfortunately, you happened to name the link "@username" instead.

    195 | 196 |

    Cinnamon.js is a script that allows users to find elements by their synonyms, using the browser's built-in Find feature. To see it in action, search this page for "Twitter", "Spice", "Email" or "Contact".

    197 | 198 |
    199 | 200 |

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque cursus orci ut mi laoreet rhoncus. Pellentesque congue urna tincidunt tortor rhoncus dapibus. Duis faucibus dolor a sem ultrices at facilisis risus cursus. Cras vel euismod nisl. Ut vitae risus et libero sagittis ultrices et vitae ligula. Aliquam at turpis id diam placerat consequat at vitae est. Aenean tellus magna, lacinia vitae facilisis facilisis, egestas ut sapien. Follow me at @thomashpark. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec tincidunt dapibus dui luctus rhoncus. Nam sagittis egestas blandit. Nulla imperdiet tincidunt enim, a tempus sapien volutpat a. Maecenas sed elit ipsum, ut tristique odio. Suspendisse potenti.

    201 | 202 |

    Cinnamon
    Photo by kobiz7

    203 | 204 |

    Etiam et elit enim, quis semper metus. Nunc sodales posuere turpis, viverra vestibulum lectus vehicula sed. Nulla quis augue nec nibh varius pharetra adipiscing non felis. Etiam lobortis vestibulum luctus. Cras non felis enim, id gravida libero. Vivamus vulputate nisi at tellus pulvinar faucibus sodales mi feugiat. Reach me here. Proin id est ut quam dictum venenatis ac non risus. Ut porttitor mattis odio vel elementum. Aliquam molestie lorem nec diam pharetra et malesuada diam condimentum. Cras feugiat pulvinar sollicitudin. Etiam id diam fermentum quam varius laoreet. Quisque nibh nunc, ullamcorper et bibendum at, ultrices et lorem. Cras vitae euismod felis. Donec lectus libero, ornare eget luctus nec, dictum et sapien. Proin viverra justo ac augue pellentesque aliquet.

    205 | 206 |
    207 | 208 |

    To add to your page, include cinnamon.js. Then wrap your element of choice (such as in span tags) and give the data-cinnamon attribute a comma-separated list of terms. For example, <span data-cinnamon="azure,cerulean,cobalt">blue</span>. If you wrap an image, its alt text will also be used, as in the example above.

    209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /tests/csv/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/csv/canonical/build/out.csv: -------------------------------------------------------------------------------- 1 | NAME, DATE, JOB 2 | JOHN, 1/5/19, CARPENTER 3 | JANE, 2/3/17, ASTRONAUT 4 | -------------------------------------------------------------------------------- /tests/csv/project.md: -------------------------------------------------------------------------------- 1 | This is a quick test of a csv parsing exercise. 2 | 3 | _"some data | csv-parse | csv-transform _"f | funify" | csv-stringify" 4 | 5 | [out.csv](# "save:") 6 | 7 | ## some data 8 | 9 | An example csv 10 | 11 | name, date, job 12 | John, 1/5/19, carpenter 13 | Jane, 2/3/17, astronaut 14 | 15 | 16 | ## f 17 | 18 | This is the transformation function 19 | 20 | function (data) { 21 | return data.map(function (el) { 22 | return el.toUpperCase(); 23 | }); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /tests/date/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/date/canonical/build/out.txt: -------------------------------------------------------------------------------- 1 | May 06, 2011 2 | May/06/2011 3 | 4 | 3rd Month Fri, 2009 5 | 6 | May 05/06/2011 7 | 8 | May 05/06/2011 9 | -------------------------------------------------------------------------------- /tests/date/project.md: -------------------------------------------------------------------------------- 1 | # First 2 | 3 | This tests the date project. 4 | 5 | 6 | _" | date 2011-05-03T22:10, addDays, num(3) " 7 | 8 | 9 | [out.txt](# "save: | date format, ec('MMM DD, YYYY') 10 | | join \n, _':others' ") 11 | 12 | [others]() 13 | 14 | _" | date 2011-05-03T22:10 | date addDays, num(3) | date format, 15 | ec('MMM/DD/YYYY') |log " 16 | 17 | _"| date ec('Mar 27, 2009') | -format ec('Mo [Month] ddd, YYYY') |log " 18 | 19 | _" | echo 2011-05-03T22:10 | -addDays num(3) | date format, 20 | ec('MMM MM/DD/YYYY') " 21 | 22 | _" | echo -format( 23 | -addDays( 24 | date( 2011-05-03T22:10 ), 25 | num(3) ), 26 | ec('MMM MM/DD/YYYY') 27 | ) " 28 | 29 | ## A couple of other dates 30 | 31 | This checks the current date for passing in nothing. 32 | 33 | _"| date | date format, ec('MM/DD/YY') " 34 | 35 | _"| date format, ec('Mo [Month] ddd, YYYY') " 36 | 37 | [ignore.txt](# "save:") 38 | -------------------------------------------------------------------------------- /tests/fence/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/fence/canonical/build/fence.txt: -------------------------------------------------------------------------------- 1 | some code here. 2 |

    We got code.

    3 | 6 | not seen 7 | code fence not matched ``` 8 | for (i = 0; i < n; i += 1) { 9 | if (code) { 10 | return yay; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/fence/fence.md: -------------------------------------------------------------------------------- 1 | # Fencing 2 | 3 | So this is an example of code fencing a literate program. 4 | 5 | ## Files 6 | 7 | Just one file, [fence.txt](#fence "save:") 8 | 9 | ## Fence 10 | 11 | Here we start. 12 | 13 | ``` 14 | some code here. 15 | _"section a" 16 | _"section b" 17 | _":footer" 18 | ``` 19 | 20 | 21 | [footer]() 22 | 23 | ```js 24 | for (i = 0; i < n; i += 1) { 25 | if (code) { 26 | return yay; 27 | } 28 | } 29 | ``` 30 | 31 | ### Section a 32 | 33 | Let's make a list with some code. 34 | 35 | * This is great. 36 | * Not so great. 37 | 38 | ```html 39 |

    We got code.

    40 | 43 | ``` 44 | ### section b 45 | 46 | This is a choppy section 47 | 48 | ``` 49 | not seen 50 | code fence not matched ``` 51 | ``` 52 | 53 | There be slicing and there be shifted boundaries. Need the ending code fence 54 | on its own line. 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /tests/fence/original/fence.md: -------------------------------------------------------------------------------- 1 | # Fencing 2 | 3 | So this is an example of code fencing a literate program. 4 | 5 | ## Files 6 | 7 | Just one file, [fence.txt](#fence "save:") 8 | 9 | ## Fence 10 | 11 | Here we start. 12 | 13 | ``` 14 | some code here. 15 | _"section a" 16 | _"section b" 17 | _":footer" 18 | ``` 19 | 20 | 21 | [footer]() 22 | 23 | ```js 24 | for (i = 0; i < n; i += 1) { 25 | if (code) { 26 | return yay; 27 | } 28 | } 29 | ``` 30 | 31 | ### Section a 32 | 33 | Let's make a list with some code. 34 | 35 | * This is great. 36 | * Not so great. 37 | 38 | ```html 39 |

    We got code.

    40 | 43 | ``` 44 | ### section b 45 | 46 | This is a choppy section 47 | 48 | ``` 49 | not seen 50 | code fence not matched ``` 51 | ``` 52 | 53 | There be slicing and there be shifted boundaries. Need the ending code fence 54 | on its own line. 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /tests/first/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/first/canonical/build/first.txt: -------------------------------------------------------------------------------- 1 | just a quick hi!?! 2 | 3 | Just a quick note for first. 4 | 5 | Hi. Thare are some text. 6 | -------------------------------------------------------------------------------- /tests/first/first.md: -------------------------------------------------------------------------------- 1 | # First 2 | 3 | This is the first in a long series of litpro. 4 | 5 | just a quick hi!?! 6 | 7 | _"second.md::note" 8 | 9 | _"some more text" 10 | 11 | [first.txt](# "save:") 12 | 13 | [some more text](toread.txt "readfile: | sub is, are") 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/first/first.txt: -------------------------------------------------------------------------------- 1 | just a quick hi!?! 2 | 3 | Just a quick note for first. 4 | 5 | Hi. Thare are some text. 6 | -------------------------------------------------------------------------------- /tests/first/second.md: -------------------------------------------------------------------------------- 1 | # note 2 | 3 | Just a quick note for first. 4 | -------------------------------------------------------------------------------- /tests/first/toread.txt: -------------------------------------------------------------------------------- 1 | Hi. This is some text. 2 | -------------------------------------------------------------------------------- /tests/fizzbuzz/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/fizzbuzz/canonical/build/fizzbuzz.js: -------------------------------------------------------------------------------- 1 | /*global console */ 2 | var overwrite = function (arr, m, str) { 3 | var n = arr.length; 4 | for (i = m - 1; i < n; i += m) { 5 | arr[i] = str; 6 | } 7 | }; 8 | 9 | var numarr = new Array(100); 10 | var i; 11 | for (i = 0; i < 100; i += 1) { 12 | numarr[i] = i + 1; 13 | } 14 | 15 | overwrite(numarr, 3, "Fizz"); 16 | overwrite(numarr, 5, "Buzz"); 17 | overwrite(numarr, 15, "FizzBuzz"); 18 | 19 | console.log(numarr.join(", ")); 20 | -------------------------------------------------------------------------------- /tests/fizzbuzz/fizzbuzz.md: -------------------------------------------------------------------------------- 1 | # FizzBuzz 2 | 3 | FizzBuzz is a trivial program that [weeds out programmers](http://www.codinghorror.com/blog/2007/02/why-cant-programmers-program.html). 4 | 5 | The goal of the program is to to print out the numbers from 1 to 100 except multiples of 3 should output Fizz, of 5 Buzz, and of both FizzBuzz. 6 | 7 | We create just one file. To run it, run node on [fizzbuzz.js](#structure "save:| tidy js, kv(indent_size, 2) ") 8 | 9 | ## Structure 10 | 11 | We will approach this problem by making an array of all 100 numbers and then writing over the appropriate multiples by going over the array 3 more times though only touching the relevant multiples. 12 | 13 | [](# ":| jshint ") 14 | 15 | /*global console */ 16 | var overwrite = _"Overwrite multiples in array"; 17 | 18 | var numarr = new Array(100); 19 | _":Initial array" 20 | 21 | overwrite(numarr, 3, "Fizz"); 22 | overwrite(numarr, 5, "Buzz"); 23 | overwrite(numarr, 15, "FizzBuzz"); 24 | 25 | _"Output array" 26 | 27 | 28 | [Initial array]() 29 | 30 | This is a simple loop that puts in the right digit. Note that we want the 31 | value of the arrays to start at 1 while the index starts at 0. 32 | 33 | var i; 34 | for (i = 0; i < 100; i += 1) { 35 | numarr[i] = i+1; 36 | } 37 | 38 | ## Output array 39 | 40 | We just join the array and output it. 41 | 42 | console.log(numarr.join(", ")); 43 | 44 | ## Overwrite multiples in array 45 | 46 | This is a function that takes in an array, a multiple, and a string and replaces each of the multiples with that string. 47 | 48 | function (arr, m, str) { 49 | var n = arr.length; 50 | for (i = m-1; i < n; i += m) { 51 | arr[i] = str; 52 | } 53 | } 54 | 55 | ## Immediate gratification 56 | 57 | Because this is a literate program, we can pipe text through a command. In this case, we will evaluate it. 58 | 59 | _"Structure | evil " 60 | -------------------------------------------------------------------------------- /tests/fizzbuzz/original/fizzbuzz.md: -------------------------------------------------------------------------------- 1 | # FizzBuzz 2 | 3 | FizzBuzz is a trivial program that [weeds out programmers](http://www.codinghorror.com/blog/2007/02/why-cant-programmers-program.html). 4 | 5 | The goal of the program is to to print out the numbers from 1 to 100 except multiples of 3 should output Fizz, of 5 Buzz, and of both FizzBuzz. 6 | 7 | We create just one file. To run it, run node on [fizzbuzz.js](#structure "save:| jstidy()") 8 | 9 | ## Structure 10 | 11 | We will approach this problem by making an array of all 100 numbers and then writing over the appropriate multiples by going over the array 3 more times though only touching the relevant multiples. 12 | 13 | [](# "js | jshint() ") 14 | 15 | /*global console */ 16 | var overwrite = _"Overwrite multiples in array"; 17 | 18 | var numarr = new Array(100); 19 | _":Initial array" 20 | 21 | overwrite(numarr, 3, "Fizz"); 22 | overwrite(numarr, 5, "Buzz"); 23 | overwrite(numarr, 15, "FizzBuzz"); 24 | 25 | _"Output array" 26 | 27 | 28 | [Initial array](# "js") 29 | 30 | This is a simple loop that puts in the right digit. Note that we want the value of the arrays to start at 1 while the index starts at 0. 31 | 32 | var i; 33 | for (i = 0; i < 100; i += 1) { 34 | numarr[i] = i+1; 35 | } 36 | 37 | ## Output array 38 | 39 | We just join the array and output it. 40 | 41 | console.log(numarr.join(", ")); 42 | 43 | ## Overwrite multiples in array 44 | 45 | This is a function that takes in an array, a multiple, and a string and replaces each of the multiples with that string. 46 | 47 | function (arr, m, str) { 48 | var n = arr.length; 49 | for (i = m-1; i < n; i += m) { 50 | arr[i] = str; 51 | } 52 | } 53 | 54 | ## Immediate gratification 55 | 56 | Because this is a literate program, we can pipe text through a command. In this case, we will evaluate it. 57 | 58 | [](# "|eval") 59 | 60 | _"Structure" 61 | -------------------------------------------------------------------------------- /tests/he/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/he/canonical/build/out.txt: -------------------------------------------------------------------------------- 1 | foo © bar ≠ baz α qux 2 | 3 | foo © bar ≠ baz 𝌆 qux 4 | 5 | <p dude="right">Hey &ne; there.</p> 6 | 7 |

    Hey ≠ there.

    8 | 9 |

    Hey ≠ there.

    10 | 11 | <p dude="right">Hey &ne; there.</p> 12 | 13 |

    Hey ≠ there.

    14 | -------------------------------------------------------------------------------- /tests/he/project.md: -------------------------------------------------------------------------------- 1 | This does some html encoding and decoding. 2 | 3 | _"text | html-encode" 4 | 5 | _"encoded | html-decode" 6 | 7 | _"normal | html-qescape" 8 | 9 | _"normal | html-qescape | html-unescape" 10 | 11 | _"normal | html-qescape | html-decode" 12 | 13 | _"normal | html-escape" 14 | 15 | _"normal | html-escape | html-unescape" 16 | 17 | 18 | [out.txt](# "save:") 19 | 20 | ## Text 21 | 22 | foo © bar ≠ baz α qux 23 | 24 | ## encoded 25 | 26 | foo © bar ≠ baz 𝌆 qux 27 | 28 | ## normal 29 | 30 |

    Hey ≠ there.

    31 | 32 | -------------------------------------------------------------------------------- /tests/integrated/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/integrated/canonical/build/dev/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Great Writing 5 | 6 | 7 | 8 | 9 | 10 |
    Some more *blathering*
    11 |
    12 |

    A bird in the hand is worth more than two in the bush.

    13 |
    14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/integrated/canonical/build/prod/index.html: -------------------------------------------------------------------------------- 1 | Great Writing
    Some more *blathering*

    A bird in the hand is worth more than two in the bush.

    2 | -------------------------------------------------------------------------------- /tests/integrated/integrated.md: -------------------------------------------------------------------------------- 1 | # A combo page 2 | 3 | This should highlight the various technologies. 4 | 5 | So we will create a html structure with pug as the bones and md as the 6 | content, using subbing and cheerio to do the substitutions. 7 | 8 | For css, we'll use postcss, autoprefixer, and cssfuture to make a snazzy css 9 | file. Maybe a css linter? 10 | 11 | For js, we'll simply jshint it. We'll have two files. 12 | 13 | After all that, we'll save the files in one directory in a tidy fashion. This 14 | would be for developmet. 15 | 16 | In the other directory, we'll have the minified files. 17 | 18 | All the source code will be here. We will keep it short. 19 | 20 | ## Files 21 | 22 | * [dev/](# "cd: save") 23 | * [index.html](#boilerplate "save: | pug | 24 | compile content | 25 | ch-replace #scripts, _'|s scripts, main, side', 26 | #css, _'|s css, main' | 27 | cheerio #quote, html, _'witticism|md' | 28 | tidy html, kv(indent_size, 6)") 29 | * [main.js](#core-js "rave: ") 30 | * [side.js](#side-js "rave:") 31 | * [main.css](#css "rave:") 32 | * [prod/](# "cd: save") 33 | * [index.html](#boilerplate "save: | pug | compile content | 34 | ch-replace #scripts, _'|s scripts, all', 35 | #css, _'|s css, main' | 36 | cheerio #quote, html, _'witticism|md' | 37 | minify html, kv(removeComments, true(), 38 | collapseWhitespace, true())") 39 | * [all.js](#core-js "rave: | join \n, _'side js' ") 40 | * [main.css](#css "rave:") 41 | * [](# "cd: save") 42 | 43 | # boilerplate 44 | 45 | html 46 | head 47 | title \_`:title` 48 | #css 49 | body 50 | // main then quote then scripts 51 | main \_`:main` 52 | #quote 53 | #scripts 54 | 55 | 56 | ## Content 57 | 58 | This goes the content of the page 59 | 60 | [title]() 61 | 62 | Great Writing 63 | 64 | [main]() 65 | 66 | Some more *blathering* 67 | 68 | 69 | # witticism 70 | 71 | A bird in the hand is worth more than two in the bush. 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /tests/integrated/lprc.js: -------------------------------------------------------------------------------- 1 | /*global module, require */ 2 | module.exports = function(Folder, args) { 3 | 4 | Folder.plugins.snippets = Folder.requires.merge(Folder.plugins.snippets, 5 | { 6 | scripts : function () { 7 | var i, n = arguments.length; 8 | var ret = ''; 9 | for (i=0; i < n; i += 1) { 10 | ret += ''; 11 | } 12 | return ret; 13 | }, 14 | css : function () { 15 | var i, n = arguments.length; 16 | var ret = ''; 17 | for (i=0; i < n; i += 1) { 18 | ret += ''; 19 | } 20 | return ret; 21 | } 22 | }); 23 | 24 | }; 25 | -------------------------------------------------------------------------------- /tests/integrated/lprc.md: -------------------------------------------------------------------------------- 1 | # lprc 2 | 3 | This is the lprc file. We also write this in a literate fashion. 4 | 5 | The main task is to load up any extras and the snippets. 6 | 7 | ## 8 | 9 | /*global module, require */ 10 | module.exports = function(Folder, args) { 11 | 12 | _"snippets" 13 | 14 | }; 15 | 16 | [lprc.js](# "save: |jshint ") 17 | 18 | ## snippets 19 | 20 | We merge these snippets with the plugin snippets 21 | 22 | Folder.plugins.snippets = Folder.requires.merge(Folder.plugins.snippets, 23 | { 24 | scripts : _":scripts", 25 | css : _":css" 26 | }); 27 | 28 | [scripts]() 29 | 30 | This takes in a string of file names sans js and we should output a script. 31 | 32 | function () { 33 | var i, n = arguments.length; 34 | var ret = ''; 35 | for (i=0; i < n; i += 1) { 36 | ret += ''; 37 | } 38 | return ret; 39 | } 40 | 41 | [css]() 42 | 43 | This creates links. 44 | 45 | function () { 46 | var i, n = arguments.length; 47 | var ret = ''; 48 | for (i=0; i < n; i += 1) { 49 | ret += ''; 50 | } 51 | return ret; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /tests/jstidy/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/jstidy/canonical/build/jstidytest.js: -------------------------------------------------------------------------------- 1 | var newDomNodes = domNodes.enter() 2 | .insert('g', '.root') 3 | .attr('class', function (node) { 4 | var classes = ['node', 'enter']; 5 | node.root && classes.push('root'); 6 | return classes.join(' '); 7 | }) 8 | .attr('id', function (node) { 9 | return node.id; 10 | }); 11 | -------------------------------------------------------------------------------- /tests/jstidy/jstidy.md: -------------------------------------------------------------------------------- 1 | # JSTidy option 2 | 3 | So this is inspired from an issue and is good to have a stand-alone plugin 4 | test. 5 | 6 | The goal is to get code formatted well. 7 | 8 | * [jstidytest.js](#:original "save: |log | tidy js, kv(indent_size, 2)") 9 | 10 | --- 11 | 12 | [original]() 13 | 14 | var newDomNodes = domNodes.enter() 15 | .insert('g', '.root') 16 | .attr('class', function (node) { 17 | var classes = ['node', 'enter']; 18 | node.root && classes.push('root'); 19 | return classes.join(' '); 20 | }) 21 | .attr('id', function (node) { 22 | return node.id; 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /tests/jstidy/original/jstidy.md: -------------------------------------------------------------------------------- 1 | # JSTidy option 2 | 3 | So this is inspired from an issue and is good to have a stand-alone plugin 4 | test. 5 | 6 | The goal is to get code formatted well. 7 | 8 | * [jstidytest.js](# "save:original|jstidy") 9 | 10 | 11 | [original](# ) 12 | 13 | var newDomNodes = domNodes.enter() 14 | .insert('g', '.root') 15 | .attr('class', function (node) { 16 | var classes = ['node', 'enter']; 17 | node.root && classes.push('root'); 18 | return classes.join(' '); 19 | }) 20 | .attr('id', function (node) { 21 | return node.id; 22 | }); 23 | 24 | -------------------------------------------------------------------------------- /tests/lodash/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/lodash/canonical/build/out.txt: -------------------------------------------------------------------------------- 1 | -----00dude??00 2 | -------------------------------------------------------------------------------- /tests/lodash/project.md: -------------------------------------------------------------------------------- 1 | This is a lodash test. 2 | 3 | _"| echo -padEnd(dude, num(6), ?) | - pad, num(10), 0 4 | | -padStart num(15), - " 5 | 6 | [out.txt](# "save:") 7 | -------------------------------------------------------------------------------- /tests/logs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/logs/canonical/build/logs.htm: -------------------------------------------------------------------------------- 1 |

    Big Factorial? Log it down to size

    2 |

    Problem

    3 |

    Factorials get too big too fast. For example, \(69! \approx 1.7\times 4 | 10^{98}\) is the maximum factorial a calculator can handle. And Google 5 | maxes out around 170!

    6 |

    The issue is that the numbers get too big for the computer to store in 7 | memory in its default way to store numbers.

    8 |

    Key Insight

    9 |

    Logarithms cut numbers down to size, but we cannot use the logarithm of 10 | the answer without computing it. Or can we?

    11 |

    The logarithm of a product is the sum of the logarithms...

    12 |

    Solution

    13 |

    Using logarithms, I claim \(1000! \approx 4.02\times 10^{2567}\).

    14 |

    To start with, computing \(\ln(1000!)\) does not help since \(1000!\) is 15 | computed first before the logarithm can act.

    16 |

    But remember that logarithms convert products to sums:

    17 | \[ 18 | \ln (1000* 999* 998* ... *3 *2 *1) = \ln(1000) + \ln(999) + \ln(998) + \cdots + \ln(3)+\ln(2) + \ln(1) 19 | \] 20 |

    For example, if you run the following command in http://geogebra.org: 21 | sum(sequence(ln(n), n, 1, 1000)) you will get \(5912.13\).

    22 |

    Now we cannot exponentiate that directly since it would generate too large 23 | a number. It is currently in base \(e\), i.e.,

    24 | \(e^{5912} 25 | \approx 1000!\) 26 |

    How do we get it to base 10? Divide by \(\ln(10)\)

    27 |

    Once in base 10, the integer part is the power of 10. The fractional part 28 | could then be exponentiated.

    29 |

    sum(sequence(ln(n), n, 1, 1000))/ln(10) = 2567.6046

    30 |

    Therefore,

    31 | \[ 32 | 1000! \approx 10^{0.6046} \times 10^{2567} \approx 4.02 \times 10^{2567} 33 | \] 34 |

    A few factorials

    35 |

    To see the growth of the factorial, let's compute a few of them.

    36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
    nn!\(\log_{10} n!\)
    51202.08
    103.62880E66.56
    503.04141E6464.5
    1009.33262E157158
    5001.22014E11341.13e+3
    10004.02387E25672.57e+3
    50004.22858E163251.63e+4
    100002.84626E356593.57e+4
    500003.34732E2132362.13e+5
    1000002.82423E4565734.57e+5
    5000001.02280E26323412.63e+6
    10000008.26393E55657085.57e+6
    51 | 52 |

    Factorial Computer

    53 |

    To use, put in a positive integer.

    54 | 55 | 56 | 57 |
    58 | 59 |
    /*global $*/ 60 | $("#computeFactorial").click(function () { 61 | var n = $("#n").val(); 62 | if ((n < 0) || (Math.floor(n) !== n) ) { 63 | $("#factorial").text("Input a positive integer"); 64 | } 65 | var lf = 0, nf; 66 | //sum ove the logs 67 | var i; 68 | for (i = 0; i < n; i += 1) { 69 | lf += Math.log(i+1); 70 | } 71 | var lf10 = lf/Math.LN10; 72 | nf = lf10 < 6 ? Math.round(Math.pow(10, lf10) ) : 73 | Math.pow(10, lf10-Math.floor(lf10)).toPrecision(6) + 74 | "E" + Math.floor(lf10); 75 | var text = n + "! = " + nf; 76 | $("#factorial").text(text ); 77 | });
    78 |

    Working code

    79 |

    Would you like to play around with the code that generated this? Try 80 | editing the follow code block:

    81 | 93 |

    The result of the above computation is .

    94 |

    This is the implementation described above. We first compute the some of 95 | the logarithms, then we compute the logarithm relative to 10. The final 96 | line is just an attempt to present the result nicely.

    97 |

    Feel free to modify the code to see how it works.

    98 | 99 |

    Conclusion

    100 |

    Factorials are large. For example, suffling a deck of cards leads to about

    101 | \(52! \approx 8.13 \times 10^{67}\) different possible outcomes. That is a 102 |

    number with 67 digits. To put that into perspective, the number of 103 | estimated molecules in the observed universe is about \(10^{80}\).

    104 |

    While Google maxes out at \(170!\), one can use 105 | Wolfram Alpha 106 | to compute the factorial and check our work.

    107 |

    Always keep in mind the power of logarithms.

    108 | -------------------------------------------------------------------------------- /tests/logs/canonical/build/logs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Log and Factorial 6 | 7 | 8 | 10 | 29 | 30 | 31 | 32 |
    33 |

    Big Factorial? Log it down to size

    34 |

    Problem

    35 |

    Factorials get too big too fast. For example, \(69! \approx 1.7\times 36 | 10^{98}\) is the maximum factorial a calculator can handle. And Google 37 | maxes out around 170!

    38 |

    The issue is that the numbers get too big for the computer to store in 39 | memory in its default way to store numbers.

    40 |

    Key Insight

    41 |

    Logarithms cut numbers down to size, but we cannot use the logarithm of 42 | the answer without computing it. Or can we?

    43 |

    The logarithm of a product is the sum of the logarithms...

    44 |

    Solution

    45 |

    Using logarithms, I claim \(1000! \approx 4.02\times 10^{2567}\).

    46 |

    To start with, computing \(\ln(1000!)\) does not help since \(1000!\) is 47 | computed first before the logarithm can act.

    48 |

    But remember that logarithms convert products to sums:

    49 | \[ 50 | \ln (1000* 999* 998* ... *3 *2 *1) = \ln(1000) + \ln(999) + \ln(998) + \cdots + \ln(3)+\ln(2) + \ln(1) 51 | \] 52 |

    For example, if you run the following command in http://geogebra.org: 53 | sum(sequence(ln(n), n, 1, 1000)) you will get \(5912.13\).

    54 |

    Now we cannot exponentiate that directly since it would generate too large 55 | a number. It is currently in base \(e\), i.e.,

    56 | \(e^{5912} 57 | \approx 1000!\) 58 |

    How do we get it to base 10? Divide by \(\ln(10)\)

    59 |

    Once in base 10, the integer part is the power of 10. The fractional part 60 | could then be exponentiated.

    61 |

    sum(sequence(ln(n), n, 1, 1000))/ln(10) = 2567.6046

    62 |

    Therefore,

    63 | \[ 64 | 1000! \approx 10^{0.6046} \times 10^{2567} \approx 4.02 \times 10^{2567} 65 | \] 66 |

    A few factorials

    67 |

    To see the growth of the factorial, let's compute a few of them.

    68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 |
    nn!\(\log_{10} n!\)
    51202.08
    103.62880E66.56
    503.04141E6464.5
    1009.33262E157158
    5001.22014E11341.13e+3
    10004.02387E25672.57e+3
    50004.22858E163251.63e+4
    100002.84626E356593.57e+4
    500003.34732E2132362.13e+5
    1000002.82423E4565734.57e+5
    5000001.02280E26323412.63e+6
    10000008.26393E55657085.57e+6
    83 | 84 |

    Factorial Computer

    85 |

    To use, put in a positive integer.

    86 | 87 | 88 | 89 |
    90 | 91 |
    /*global $*/ 92 | $("#computeFactorial").click(function () { 93 | var n = $("#n").val(); 94 | if ((n < 0) || (Math.floor(n) !== n) ) { 95 | $("#factorial").text("Input a positive integer"); 96 | } 97 | var lf = 0, nf; 98 | //sum ove the logs 99 | var i; 100 | for (i = 0; i < n; i += 1) { 101 | lf += Math.log(i+1); 102 | } 103 | var lf10 = lf/Math.LN10; 104 | nf = lf10 < 6 ? Math.round(Math.pow(10, lf10) ) : 105 | Math.pow(10, lf10-Math.floor(lf10)).toPrecision(6) + 106 | "E" + Math.floor(lf10); 107 | var text = n + "! = " + nf; 108 | $("#factorial").text(text ); 109 | });
    110 |

    Working code

    111 |

    Would you like to play around with the code that generated this? Try 112 | editing the follow code block:

    113 | 125 |

    The result of the above computation is .

    126 |

    This is the implementation described above. We first compute the some of 127 | the logarithms, then we compute the logarithm relative to 10. The final 128 | line is just an attempt to present the result nicely.

    129 |

    Feel free to modify the code to see how it works.

    130 | 131 |

    Conclusion

    132 |

    Factorials are large. For example, suffling a deck of cards leads to about

    133 | \(52! \approx 8.13 \times 10^{67}\) different possible outcomes. That is a 134 |

    number with 67 digits. To put that into perspective, the number of 135 | estimated molecules in the observed universe is about \(10^{80}\).

    136 |

    While Google maxes out at \(170!\), one can use 137 | Wolfram Alpha 138 | to compute the factorial and check our work.

    139 |

    Always keep in mind the power of logarithms.

    140 | 141 |
    142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /tests/logs/canonicalold/build/logs.htm: -------------------------------------------------------------------------------- 1 |

    Big Factorial? Log it down to size

    2 |

    Problem

    3 |

    Factorials get too big too fast. For example, $69! \approx 1.7\times 10^{98}$ is the maximum factorial a calculator can handle. And Google maxes out around 170!

    4 |

    The issue is that the numbers get too big for the computer to store in memory in its default way to store numbers.

    5 |

    Key Insight

    6 |

    Logarithms cut numbers down to size, but we cannot use the logarithm of the answer without computing it. Or can we?

    7 |

    The logarithm of a product is the sum of the logarithms...

    8 |

    Solution

    9 |

    Using logarithms, I claim $1000! \approx 4.02\times 10^{2567}$.

    10 |

    To start with, computing $\ln(1000!)$ does not help since $1000!$ is computed first before the logarithm can act.

    11 |

    But remember that logarithms convert products to sums: 12 | $$\ln (1000* 999* 998* ... *3 *2 *1) = \ln(1000) + \ln(999) + \ln(998) + \cdots + \ln(3)+\ln(2) + \ln(1) $$

    13 |

    For example, if you run the following command in GeoGebra: sum(sequence(ln(n), n, 1, 1000)) you will get $5912.13$.

    14 |

    Now we cannot exponentiate that directly since it would generate too large a number. It is currently in base $e$, i.e., \(e^{5912} \approx 1000!\)

    15 |

    How do we get it to base 10? Divide by $\ln(10)$

    16 |

    Once in base 10, the integer part is the power of 10. The fractional part could then be exponentiated.

    17 |

    sum(sequence(ln(n), n, 1, 1000))/ln(10) = 2567.6046

    18 |

    Therefore, 19 | $$1000! \approx 10^{0.6046} \times 10^{2567} \approx 4.02 \times 10^{2567}$$

    20 |

    A few factorials

    21 |

    To see the growth of the factorial, let's compute a few of them.

    22 |
    nn!\(\log_{10}(n!)\)
    51202.08
    103.62880E66.56
    503.04141E6464.5
    1009.33262E157158
    5001.22014E11341.13e+3
    10004.02387E25672.57e+3
    50004.22858E163251.63e+4
    100002.84626E356593.57e+4
    500003.34732E2132362.13e+5
    1000002.82423E4565734.57e+5
    5000001.02280E26323412.63e+6
    10000008.26393E55657085.57e+6
    23 | 24 |

    Factorial Computer

    25 |

    To use, put in a positive integer.

    26 | 27 | 28 |
    29 | 30 |
    /*global $*/ 31 | $("#computeFactorial").click(function () { 32 | var n = $("#n").val(); 33 | if ((n &lt; 0) || (Math.floor(n) !== n) ) { 34 | $("#factorial").text("Input a positive integer"); 35 | } 36 | var lf = 0, nf; 37 | //sum ove the logs 38 | var i; 39 | for (i = 0; i &lt; n; i += 1) { 40 | lf += Math.log(i+1); 41 | } 42 | var lf10 = lf/Math.LN10; 43 | nf = lf10 &lt; 6 ? Math.round(Math.pow(10, lf10) ) : Math.pow(10, lf10-Math.floor(lf10)).toPrecision(6) + "E" + Math.floor(lf10); 44 | var text = n + "! = " + nf; 45 | $("#factorial").text(text ); 46 | });
    47 | 48 |

    Working code

    49 |

    Would you like to play around with the code that generated this? Try editing the follow code block:

    50 | 60 | 61 |

    The result of the above computation is .

    62 | 63 |

    This is the implementation described above. We first compute the some of the logarithms, then we compute the logarithm relative to 10. The final line is just an attempt to present the result nicely.

    64 |

    Feel free to modify the code to see how it works.

    65 | 66 | 67 |

    Conclusion

    68 |

    Factorials are large. For example, suffling a deck of cards leads to about $52! \approx 8.13 \times 10^{67}$ different possible outcomes. That is a number with 67 digits. To put that into perspective, the number of estimated molecules in the observed universe is about $10^{80}$.

    69 |

    While Google maxes out at $170!$, one can use Wolfram Alpha to compute the factorial and check our work.

    70 |

    Always keep in mind the power of logarithms.

    71 | -------------------------------------------------------------------------------- /tests/logs/canonicalold/build/logs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Log and Factorial 6 | 7 | 24 | 27 | 30 | 49 | 50 | 51 | 52 | 53 |
    54 |

    Big Factorial? Log it down to size

    55 |

    Problem

    56 |

    Factorials get too big too fast. For example, $69! \approx 1.7\times 10^{98}$ is the maximum factorial a calculator can handle. And Google maxes out around 170!

    57 |

    The issue is that the numbers get too big for the computer to store in memory in its default way to store numbers.

    58 |

    Key Insight

    59 |

    Logarithms cut numbers down to size, but we cannot use the logarithm of the answer without computing it. Or can we?

    60 |

    The logarithm of a product is the sum of the logarithms...

    61 |

    Solution

    62 |

    Using logarithms, I claim $1000! \approx 4.02\times 10^{2567}$.

    63 |

    To start with, computing $\ln(1000!)$ does not help since $1000!$ is computed first before the logarithm can act.

    64 |

    But remember that logarithms convert products to sums: 65 | $$\ln (1000* 999* 998* ... *3 *2 *1) = \ln(1000) + \ln(999) + \ln(998) + \cdots + \ln(3)+\ln(2) + \ln(1) $$

    66 |

    For example, if you run the following command in GeoGebra: sum(sequence(ln(n), n, 1, 1000)) you will get $5912.13$.

    67 |

    Now we cannot exponentiate that directly since it would generate too large a number. It is currently in base $e$, i.e., \(e^{5912} \approx 1000!\)

    68 |

    How do we get it to base 10? Divide by $\ln(10)$

    69 |

    Once in base 10, the integer part is the power of 10. The fractional part could then be exponentiated.

    70 |

    sum(sequence(ln(n), n, 1, 1000))/ln(10) = 2567.6046

    71 |

    Therefore, 72 | $$1000! \approx 10^{0.6046} \times 10^{2567} \approx 4.02 \times 10^{2567}$$

    73 |

    A few factorials

    74 |

    To see the growth of the factorial, let's compute a few of them.

    75 |
    nn!\(\log_{10}(n!)\)
    51202.08
    103.62880E66.56
    503.04141E6464.5
    1009.33262E157158
    5001.22014E11341.13e+3
    10004.02387E25672.57e+3
    50004.22858E163251.63e+4
    100002.84626E356593.57e+4
    500003.34732E2132362.13e+5
    1000002.82423E4565734.57e+5
    5000001.02280E26323412.63e+6
    10000008.26393E55657085.57e+6
    76 | 77 |

    Factorial Computer

    78 |

    To use, put in a positive integer.

    79 | 80 | 81 |
    82 | 83 |
    /*global $*/ 84 | $("#computeFactorial").click(function () { 85 | var n = $("#n").val(); 86 | if ((n &lt; 0) || (Math.floor(n) !== n) ) { 87 | $("#factorial").text("Input a positive integer"); 88 | } 89 | var lf = 0, nf; 90 | //sum ove the logs 91 | var i; 92 | for (i = 0; i &lt; n; i += 1) { 93 | lf += Math.log(i+1); 94 | } 95 | var lf10 = lf/Math.LN10; 96 | nf = lf10 &lt; 6 ? Math.round(Math.pow(10, lf10) ) : Math.pow(10, lf10-Math.floor(lf10)).toPrecision(6) + "E" + Math.floor(lf10); 97 | var text = n + "! = " + nf; 98 | $("#factorial").text(text ); 99 | });
    100 | 101 |

    Working code

    102 |

    Would you like to play around with the code that generated this? Try editing the follow code block:

    103 | 113 | 114 |

    The result of the above computation is .

    115 | 116 |

    This is the implementation described above. We first compute the some of the logarithms, then we compute the logarithm relative to 10. The final line is just an attempt to present the result nicely.

    117 |

    Feel free to modify the code to see how it works.

    118 | 119 | 120 |

    Conclusion

    121 |

    Factorials are large. For example, suffling a deck of cards leads to about $52! \approx 8.13 \times 10^{67}$ different possible outcomes. That is a number with 67 digits. To put that into perspective, the number of estimated molecules in the observed universe is about $10^{80}$.

    122 |

    While Google maxes out at $170!$, one can use Wolfram Alpha to compute the factorial and check our work.

    123 |

    Always keep in mind the power of logarithms.

    124 | 125 |
    126 | 127 | 128 | -------------------------------------------------------------------------------- /tests/logs/logs.md: -------------------------------------------------------------------------------- 1 | [Factorial](# "Version: 0.1.0") 2 | === 3 | 4 | This post is about computing factorials via the logarithm. We use loops to compute it. 5 | 6 | Starting from the parent directory, type `literate-programming -r examples logs.md` to compile. 7 | 8 | This literate program compiles into the following files: 9 | 10 | [logs.htm](#structure:main "Save: ") 11 | 12 | [logs.html](#stand-alone-page "Save:") 13 | 14 | Structure 15 | ========= 16 | 17 | [main](#Structure ':|md | compile ' ) 18 | 19 | _"Introduction" 20 | 21 | _"Computing 1000!" 22 | 23 | \_"Table of Factorials:main" 24 | 25 | \_"Factorial for all:main" 26 | 27 | \_"Behind the scenes:main" 28 | 29 | _"Comments" 30 | 31 | 32 | 33 | Introduction 34 | ============ 35 | 36 | Introduce the problem 37 | 38 | # Big Factorial? Log it down to size 39 | 40 | 41 | ## Problem 42 | 43 | Factorials get too big too fast. For example, `69! \approx 1.7\times 44 | 10^{98}`$ is the maximum factorial a calculator can handle. And Google 45 | maxes out around 170! 46 | 47 | The issue is that the numbers get too big for the computer to store in 48 | memory in its default way to store numbers. 49 | 50 | ## Key Insight 51 | 52 | Logarithms cut numbers down to size, but we cannot use the logarithm of 53 | the answer without computing it. Or can we? 54 | 55 | The logarithm of a product is the sum of the logarithms... 56 | 57 | 58 | ## Computing 1000! 59 | 60 | Here we work out step by step how to compute out 1000! 61 | 62 | 63 | ## Solution 64 | Using logarithms, I claim `1000! \approx 4.02\times 10^{2567}`$. 65 | 66 | To start with, computing `\ln(1000!)`$ does not help since `1000!`$ is 67 | computed first before the logarithm can act. 68 | 69 | But remember that logarithms convert products to sums: 70 | ```$ 71 | \ln (1000* 999* 998* ... *3 *2 *1) = \ln(1000) + \ln(999) + \ln(998) + \cdots + \ln(3)+\ln(2) + \ln(1) 72 | ``` 73 | 74 | For example, if you run the following command in _"|s geogebra": 75 | `sum(sequence(ln(n), n, 1, 1000))` you will get `5912.13`$. 76 | 77 | Now we cannot exponentiate that directly since it would generate too large 78 | a number. It is currently in base `e`$, i.e., 79 | `e^{5912} 80 | \approx 1000!`$ 81 | 82 | How do we get it to base 10? Divide by `\ln(10)`$ 83 | 84 | Once in base 10, the integer part is the power of 10. The fractional part 85 | could then be exponentiated. 86 | 87 | `sum(sequence(ln(n), n, 1, 1000))/ln(10) = 2567.6046` 88 | 89 | Therefore, 90 | ```$ 91 | 1000! \approx 10^{0.6046} \times 10^{2567} \approx 4.02 \times 10^{2567} 92 | ``` 93 | 94 | ## Comments 95 | 96 | ## Conclusion 97 | 98 | Factorials are large. For example, suffling a deck of cards leads to about 99 | `52! \approx 8.13 \times 10^{67}`$ different possible outcomes. That is a 100 | number with 67 digits. To put that into perspective, the number of 101 | estimated molecules in the observed universe is about `10^{80}`$. 102 | 103 | While Google maxes out at `170!`$, one can use 104 | [Wolfram Alpha](http://www.wolframalpha.com/input/?i=1000%21) 105 | to compute the factorial and check our work. 106 | 107 | Always keep in mind the power of logarithms. 108 | 109 | ## Factorial for all 110 | 111 | Here we give a little calculator to compute factorials of large size. Could 112 | even get larger using Stirling's approximation stuff: 113 | `(x – 1/2) log(x) – x + (1/2) log(2 π)` from 114 | [Endeavor](http://www.johndcook.com/blog/2010/08/16/how-to-compute-log-factorial/) 115 | 116 | [main](# ":|md | compile") 117 | 118 | ## Factorial Computer 119 | 120 | To use, put in a positive integer. 121 | 122 | \_"Calculator:html" 123 | 124 | 125 | ## Behind the scenes 126 | 127 | Give an editable code block that can be edited and run. 128 | 129 | 130 | [main](# ":| md | compile code block for factorial ") 131 | 132 | ## Working code 133 | 134 | Would you like to play around with the code that generated this? Try 135 | editing the follow code block: 136 | 137 | \_":js" 138 | 139 | \_":html" 140 | 141 | This is the implementation described above. We first compute the some of 142 | the logarithms, then we compute the logarithm relative to 10. The final 143 | line is just an attempt to present the result nicely. 144 | 145 | Feel free to modify the code to see how it works. 146 | 147 | 148 | 149 | ## Calculator 150 | 151 | We need an input box and a way to run it. We will have a button that implements it. 152 | 153 | [html]() 154 | 155 | 156 | 157 | 158 |
    159 | 160 | _":js| jshint | html-escape | html-wrap div, hide, run" 161 | 162 | We will attach a function to the click action 163 | 164 | [js]() 165 | 166 | /*global $*/ 167 | $("#computeFactorial").click(function () { 168 | var n = $("#n").val(); 169 | _"Check n" 170 | _"Common factorial" 171 | var text = n + "! = " + nf; 172 | $("#factorial").text(text ); 173 | }); 174 | 175 | 176 | ### Check n 177 | 178 | We need to make sure that n is a positive integer. 179 | 180 | if ((n < 0) || (Math.floor(n) !== n) ) { 181 | $("#factorial").text("Input a positive integer"); 182 | } 183 | 184 | 185 | ## Code block for factorial 186 | 187 | [js](# ": | jshint | html-wrap textarea, runnable, cols=120, rows = 10 ") 188 | 189 | var n = 1000; 190 | _"Common factorial" 191 | $("#result").text(nf); 192 | 193 | [html]() 194 | 195 |

    The result of the above computation is .

    196 | 197 | 198 | ## Common factorial 199 | 200 | Here we define the loop, get the result and format it appropriately. n is 201 | already defined as its definition differs. 202 | 203 | 204 | var lf = 0, nf; 205 | //sum ove the logs 206 | var i; 207 | for (i = 0; i < n; i += 1) { 208 | lf += Math.log(i+1); 209 | } 210 | var lf10 = lf/Math.LN10; 211 | nf = lf10 < 6 ? Math.round(Math.pow(10, lf10) ) : 212 | Math.pow(10, lf10-Math.floor(lf10)).toPrecision(6) + 213 | "E" + Math.floor(lf10); 214 | 215 | 216 | 217 | ## Table of Factorials 218 | 219 | It might be nice to see a variety of factorials. 220 | We'll do a crescendo of 1, 5, 10, 50, 100, 500, .... 221 | 222 | [main](# ":|md |compile ") 223 | 224 | ## A few factorials 225 | To see the growth of the factorial, let's compute a few of them. 226 | 227 | \_":Factorial Table| evil | html-table arr(n, n!, 228 | \_'|echo `\log_{10} n!`$ | md ') " 229 | 230 | [Factorial Table](# ":|jshint") 231 | 232 | var i = 1; 233 | var fact = function (n) { 234 | _"Common factorial" 235 | return [n, nf, lf10.toPrecision(3)]; 236 | }; 237 | var mat = []; 238 | while (i < 1e6) { 239 | i *= 5; 240 | mat.push(fact(i)); 241 | i *= 2; 242 | mat.push(fact(i)); 243 | } 244 | ret = new doc.Folder.Matrix(mat); 245 | 246 | 247 | ## Testing the factorial 248 | 249 | Let's make sure our factorial function is working. 250 | 251 | [](# ":|jshint | evil") 252 | 253 | (function () { 254 | var factorial = function (n) { 255 | _"common factorial" 256 | return nf; 257 | }; 258 | var factorials = {1 : 1, 2: 2, 3: 6, 4: 24, 5:120, 6:720}; 259 | var n, flag = false; 260 | for (n in factorials) { 261 | if (factorial(n) != factorials[n] ) { 262 | console.log(n + " does not work"); 263 | flag = true; 264 | } 265 | } 266 | if (flag) { 267 | console.log( "factorial function had errors"); 268 | } else { 269 | console.log("factorial function passed test"); 270 | } 271 | return ""; 272 | })(); 273 | 274 | 275 | Stand alone page 276 | ============= 277 | 278 | Boiler plate taken from the well-written page: [SitePoint](http://www.sitepoint.com/a-minimal-html-document-html5-edition/) 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | Log and Factorial 287 | _"|s jquery" 288 | _"|s katex" 289 | _"Scripted Writing:css" 290 | _"Scripted Writing:js" 291 | _"|s bootswatch, simplex" 292 | 293 | 294 |
    295 | _"Structure:main" 296 |
    297 | _"|s katex-body" 298 | 299 | 300 | 301 | 302 | Scripted Writing 303 | ----------- 304 | 305 | Our own homebrew solutions for a bit of scripted writing action. This will be split off into its own place, but for now it is here for demo purposes. 306 | 307 | [css](# ":| html-wrap style ") 308 | 309 | .runnable { border : 3px solid lightblue; width : 100%} 310 | .hide { display : none} 311 | 312 | [js](# ":| jshint | html-wrap script ") 313 | 314 | $(document).ready(function () { 315 | 316 | _":run" 317 | 318 | _":runnable" 319 | 320 | }); 321 | 322 | [run]() 323 | 324 | The class .run should have runnable, escaped code that we unescape and then run. 325 | 326 | $(".run").each(function () { 327 | var code = $(this).text(); 328 | code = code.replace(/\<\;/g, "<"); 329 | code = code.replace(/\>\;/g, ">"); 330 | code = code.replace(/\&\;/g, "&"); 331 | eval(code); 332 | }); 333 | 334 | [runnable]() 335 | 336 | $(".runnable").each(function () { 337 | var el$ = $(this); 338 | el$.blur(function () { 339 | eval(el$.val()); 340 | }).blur(); 341 | 342 | }); 343 | 344 | 345 | -------------------------------------------------------------------------------- /tests/logs/lprc.js: -------------------------------------------------------------------------------- 1 | /*global module, require */ 2 | module.exports = function(Folder, args) { 3 | 4 | Folder.requires.merge(Folder.plugins.snippets, { 5 | jquery : '', 8 | geogebra: "http://geogebra.org", 9 | mathjax : ' ', 28 | bootswatch : '', 30 | katex : '' + 33 | ''+ 35 | '', 37 | 'katex-body' : '', 38 | 'katex-style': '' 41 | 42 | }); 43 | 44 | var mdp = Folder.plugins.md; 45 | 46 | // we replace def because we want to anchorify all. 47 | mdp.old = mdp.def; 48 | mdp.def = mdp.req(mdp.options). 49 | use(require('markdown-it-anchor', {renderPermalink:true})); 50 | 51 | 52 | }; 53 | 54 | 55 | -------------------------------------------------------------------------------- /tests/logs/original/logs.md: -------------------------------------------------------------------------------- 1 | [Factorial](# "Version: 0.1.0") 2 | === 3 | 4 | This post is about computing factorials via the logarithm. We use loops to compute it. 5 | 6 | Starting from the parent directory, type `literate-programming -r examples logs.md` to compile. 7 | 8 | This literate program compiles into the following files: 9 | 10 | [logs.htm](#structure "Save: main") 11 | 12 | [logs.html](#stand-alone-page "Save:") 13 | 14 | Structure 15 | ========= 16 | 17 | [main](#Structure "md| 1 marked") 18 | 19 | _"Introduction" 20 | 21 | _"Computing 1000!" 22 | 23 | __"Table of Factorials" 24 | 25 | __"Factorial for all" 26 | 27 | __"Behind the scenes" 28 | 29 | _"Comments" 30 | 31 | 32 | 33 | Introduction 34 | ============ 35 | 36 | Introduce the problem 37 | 38 | [](# "MD") 39 | 40 | # Big Factorial? Log it down to size 41 | 42 | 43 | ## Problem 44 | 45 | Factorials get too big too fast. For example, $69! \approx 1.7\times 10^{98}$ is the maximum factorial a calculator can handle. And Google maxes out around 170! 46 | 47 | The issue is that the numbers get too big for the computer to store in memory in its default way to store numbers. 48 | 49 | ## Key Insight 50 | 51 | Logarithms cut numbers down to size, but we cannot use the logarithm of the answer without computing it. Or can we? 52 | 53 | The logarithm of a product is the sum of the logarithms... 54 | 55 | 56 | ## Computing 1000! 57 | 58 | Here we work out step by step how to compute out 1000! 59 | 60 | [](# "MD") 61 | 62 | 63 | ## Solution 64 | Using logarithms, I claim $1000! \approx 4.02\times 10^{2567}$. 65 | 66 | To start with, computing $\ln(1000!)$ does not help since $1000!$ is computed first before the logarithm can act. 67 | 68 | But remember that logarithms convert products to sums: 69 | $$\ln (1000* 999* 998* ... *3 *2 *1) = \ln(1000) + \ln(999) + \ln(998) + \cdots + \ln(3)+\ln(2) + \ln(1) $$ 70 | 71 | For example, if you run the following command in GEOGEBRA: `sum(sequence(ln(n), n, 1, 1000))` you will get $5912.13$. 72 | 73 | Now we cannot exponentiate that directly since it would generate too large a number. It is currently in base $e$, i.e., \(e^{5912} \approx 1000!\) 74 | 75 | How do we get it to base 10? Divide by $\ln(10)$ 76 | 77 | Once in base 10, the integer part is the power of 10. The fractional part could then be exponentiated. 78 | 79 | `sum(sequence(ln(n), n, 1, 1000))/ln(10) = 2567.6046` 80 | 81 | Therefore, 82 | $$1000! \approx 10^{0.6046} \times 10^{2567} \approx 4.02 \times 10^{2567}$$ 83 | 84 | 85 | 86 | 87 | ## Comments 88 | 89 | [](# "MD") 90 | 91 | ##Conclusion 92 | Factorials are large. For example, suffling a deck of cards leads to about $52! \approx 8.13 \times 10^{67}$ different possible outcomes. That is a number with 67 digits. To put that into perspective, the number of estimated molecules in the observed universe is about $10^{80}$. 93 | 94 | While Google maxes out at $170!$, one can use [Wolfram Alpha](http://www.wolframalpha.com/input/?i=1000%21) to compute the factorial and check our work. 95 | 96 | Always keep in mind the power of logarithms. 97 | 98 | ## Factorial for all 99 | 100 | Here we give a little calculator to compute factorials of large size. Could even get larger using Stirling's approximation stuff: (x – 1/2) log(x) – x + (1/2) log(2 π) from [Endeavor](http://www.johndcook.com/blog/2010/08/16/how-to-compute-log-factorial/) 101 | 102 | [](# "MD| 1 marked ") 103 | 104 | ## Factorial Computer 105 | 106 | To use, put in a positive integer. 107 | 108 | __"Calculator : html" 109 | 110 | 111 | ## Behind the scenes 112 | 113 | Give an editable code block that can be edited and run. 114 | 115 | 116 | [](# "MD| 1 marked ") 117 | 118 | ## Working code 119 | 120 | Would you like to play around with the code that generated this? Try editing the follow code block: 121 | 122 | __"Code block for factorial" 123 | 124 | __"Code block for factorial:html" 125 | 126 | This is the implementation described above. We first compute the some of the logarithms, then we compute the logarithm relative to 10. The final line is just an attempt to present the result nicely. 127 | 128 | Feel free to modify the code to see how it works. 129 | 130 | 131 | 132 | ## Calculator 133 | 134 | We need an input box and a way to run it. We will have a button that implements it. 135 | 136 | [](# "HTML") 137 | 138 | 139 | 140 |
    141 | 142 | __":js|jshint|escape|wrap(div, hide, run)" 143 | 144 | We will attach a function to the click action 145 | 146 | [](# "JS") 147 | 148 | /*global $*/ 149 | $("#computeFactorial").click(function () { 150 | var n = $("#n").val(); 151 | _"Check n" 152 | _"Common factorial" 153 | var text = n + "! = " + nf; 154 | $("#factorial").text(text ); 155 | }); 156 | 157 | 158 | ### Check n 159 | 160 | We need to make sure that n is a positive integer. 161 | 162 | [](# "JS") 163 | 164 | if ((n < 0) || (Math.floor(n) !== n) ) { 165 | $("#factorial").text("Input a positive integer"); 166 | } 167 | 168 | 169 | ## Code block for factorial 170 | 171 | [](# "JS | jshint | wrap(textarea, runnable, cols='120', rows = '10') ") 172 | 173 | var n = 1000; 174 | _"Common factorial" 175 | $("#result").text(nf); 176 | 177 | [](# "HTML") 178 | 179 |

    The result of the above computation is .

    180 | 181 | 182 | ## Common factorial 183 | 184 | Here we define the loop, get the result and format it appropriately. n is already defined as its definition differs. 185 | 186 | [](# "JS") 187 | 188 | var lf = 0, nf; 189 | //sum ove the logs 190 | var i; 191 | for (i = 0; i < n; i += 1) { 192 | lf += Math.log(i+1); 193 | } 194 | var lf10 = lf/Math.LN10; 195 | nf = lf10 < 6 ? Math.round(Math.pow(10, lf10) ) : Math.pow(10, lf10-Math.floor(lf10)).toPrecision(6) + "E" + Math.floor(lf10); 196 | 197 | 198 | 199 | ## Table of Factorials 200 | 201 | It might be nice to see a variety of factorials. We'll do a cresendo of 1, 5, 10, 50, 100, 500, .... 202 | 203 | [](# "MD |1 marked") 204 | 205 | ## A few factorials 206 | 207 | To see the growth of the factorial, let's compute a few of them. 208 | 209 | __":Factorial Table| eval | htmltable(rowswheader)" 210 | 211 | [Factorial Table](# "JS |jshint") 212 | 213 | var i = 1; 214 | var fact = function (n) { 215 | _"Common factorial|indent(4,4)" 216 | return [n, nf, lf10.toPrecision(3)]; 217 | }; 218 | var tarr = [["n", "n!", "\\(\\log_{10}(n!)\\)"]]; 219 | while (i < 1e6) { 220 | i *= 5; 221 | tarr.push(fact(i)); 222 | i *= 2; 223 | tarr.push(fact(i)); 224 | } 225 | 226 | return tarr; 227 | 228 | ## Jquery version 229 | 230 | Not active as we have a default jquery macro in the common standard plugins. But here as an example of how to define a macro until another example comes up. Remove leading space in front of DEFINE to get it to be active. 231 | 232 | function (v) { 233 | return ''; 235 | } 236 | 237 | [](# "define: jquery") 238 | 239 | ## Testing the factorial 240 | 241 | Let's make sure our factorial function is working. 242 | 243 | [](# "JS |jshint | eval") 244 | 245 | (function () { 246 | var factorial = function (n) { 247 | _"common factorial" 248 | return nf; 249 | }; 250 | var factorials = {1 : 1, 2: 2, 3: 6, 4: 24, 5:120, 6:720}; 251 | var n, flag = false; 252 | for (n in factorials) { 253 | if (factorial(n) != factorials[n] ) { 254 | console.log(n + " does not work"); 255 | flag = true; 256 | } 257 | } 258 | if (flag) { 259 | console.log( "factorial function had errors"); 260 | } else { 261 | console.log("factorial function passed test"); 262 | } 263 | return ""; 264 | })(); 265 | 266 | 267 | Stand alone page 268 | ============= 269 | 270 | Boiler plate taken from the well-written page: [SitePoint](http://www.sitepoint.com/a-minimal-html-document-html5-edition/) 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | Log and Factorial 279 | JQUERY(1.9.0) 280 | MATHJAX 281 | _"Scripted Writing : css" 282 | _"Scripted Writing : js" 283 | BOOTSWATCH(simplex) 284 | 285 | 286 | 287 | 288 | 289 |
    290 | _"Structure" 291 |
    292 | 293 | 294 | 295 | 296 | 297 | 298 | Scripted Writing 299 | ----------- 300 | 301 | Our own homebrew solutions for a bit of scripted writing action. This will be split off into its own place, but for now it is here for demo purposes. 302 | 303 | [](# "CSS | wrap(style) ") 304 | 305 | .runnable { border : 3px solid lightblue; width : 100%} 306 | 307 | 308 | 309 | .hide { display : none} 310 | 311 | [](# "JS | wrap(script) ") 312 | 313 | $(document).ready(function () { 314 | 315 | _":run" 316 | 317 | _":runnable" 318 | 319 | }); 320 | 321 | [run](# "JS") 322 | 323 | The class .run should have runnable, escaped code that we unescape and then run. 324 | 325 | $(".run").each(function () { 326 | var code = $(this).text(); 327 | code = code.replace(/\<\;/g, "<"); 328 | code = code.replace(/\>\;/g, ">"); 329 | code = code.replace(/\&\;/g, "&"); 330 | eval(code); 331 | }); 332 | 333 | [](# "runnable.JS") 334 | 335 | $(".runnable").each(function () { 336 | var el$ = $(this); 337 | el$.blur(function () { 338 | eval(el$.val()); 339 | }).blur(); 340 | 341 | }); 342 | 343 | 344 | -------------------------------------------------------------------------------- /tests/lprc.js: -------------------------------------------------------------------------------- 1 | /*global module, require*/ 2 | module.exports = { 3 | 'whacky' : function (doc) { 4 | doc.addConstants('geogebra', '[GeoGebra](http://geogebra.org)'); 5 | } 6 | }; -------------------------------------------------------------------------------- /tests/matrix/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/matrix/canonical/build/out.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
    8418
    10620
    14824
    161046
    7 | -------------------------------------------------------------------------------- /tests/matrix/matrix.md: -------------------------------------------------------------------------------- 1 | # Matrix 2 | 3 | This is to test out the matrix commands. 4 | 5 | ## data 6 | 7 | 4, 5, 7, 8 8 | 2, 3, 4, 5 9 | 9,10, 12, 23 10 | 11 | [out.txt](# "save: | matrixify | .transpose | .num | .scale 2 | html-table") 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/primes/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/primes/canonical/out.test: -------------------------------------------------------------------------------- 1 | The first 20 primes are: 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,49,53,59,61,67 2 | 3 | ## DONE 4 | ./build 5 | -------------------------------------------------------------------------------- /tests/primes/original/primes.md: -------------------------------------------------------------------------------- 1 | # Printing the first 1000 primes 2 | 3 | This is an example of literate programming as written in [Knuth Literate Programming](http://www.literateprogramming.com/knuthweb.pdf). 4 | 5 | ## Basic Outline 6 | 7 | This program will produce a function that takes in an integer `m` and returns an array of the first `m` primes. 8 | 9 | 10 | var firstPrimes = _"Compute first m primes"; 11 | 12 | 13 | ## Using it 14 | 15 | This is a command line literate program. We evaluate the basic outline and output it 16 | 17 | [](# "|eval") 18 | 19 | /*global console, inputs*/ 20 | _"Basic Outline" 21 | console.log("The first "+inputs[0]+" primes are: " + firstPrimes(inputs[0])); 22 | 23 | 24 | ## Doc 25 | 26 | The file primes.js exports a single object with two methods: 27 | 28 | arr .m (int m ) Computes the first m primes and returns them as an array. 29 | 30 | 31 | ## Compute first m primes 32 | 33 | We need a function that takes in an integer m and gives us an array of the first m primes. 34 | 35 | The variable `primes` will hold the array. 36 | 37 | We continue to loop until the primes array is filled. 38 | 39 | function (m) { 40 | 41 | _"Check m makes sense" 42 | 43 | _"Is it prime:vars" 44 | _"Modify Limit:vars" 45 | 46 | var primes = [2]; 47 | var current = 3; 48 | 49 | while (primes.length < m) { 50 | 51 | _"Is it prime:code" 52 | 53 | _"If prime" 54 | 55 | _"Modify limit:code" 56 | 57 | current += 2; 58 | 59 | } 60 | 61 | return primes; 62 | 63 | 64 | } 65 | 66 | ### Is it prime 67 | 68 | We loop through all computed primes up to a limit testing whether they divide the current number. If one does, then it is not prime. Even none divide, then it is prime, assuming the limit is appropriately used. 69 | 70 | [vars](#) 71 | 72 | var isPrime = true; 73 | var i = 1; 74 | // external: primes, current, limit 75 | 76 | 77 | [code](#) 78 | 79 | isPrime = true; 80 | for (i = 1; i < limit; i += 1) { 81 | if (current % primes[i] === 0) { 82 | isPrime = false; 83 | break; 84 | } 85 | } 86 | 87 | 88 | ### If prime 89 | 90 | This is simple; just append the prime to the primes list: 91 | 92 | if (isPrime) { 93 | primes.push(current); 94 | } 95 | 96 | 97 | ### Modify limit 98 | 99 | Because of being divisible by the square root of a number if it is non-prime, we can see that it must be less than the next even square. But we can say even more as we are safe as long as the current is less than the square of the smallest prime that could be the smallest divisor. 100 | 101 | For example, if the limit is 2, this will check divisibility by 3. This works for the numbers 5, 7, and 9. At 9, we are at the square 9 and we must step up. The next prime is 5. So now we are good until 25. Indeed, 11, 13, 15, 17, 19, 21, 23 are mostly primes and if not, they are divisible by 3 or 5. 102 | 103 | 104 | [vars](#) 105 | 106 | var limit = 2; 107 | var square = 9; 108 | //external current, pimes 109 | 110 | [code](#) 111 | 112 | if (current === square) { 113 | limit += 1; 114 | square = primes[limit]*primes[limit]; 115 | } 116 | 117 | Knuth raises the point that `primes[limit]` needs to be defined. It happens that this is so, but it is not trivial. 118 | 119 | ### Check m makes sense 120 | 121 | Is m a positive integer? 122 | 123 | m = parseInt(m, 10); 124 | if (m <= 0) { 125 | console.log("error: need a positive m"); 126 | return []; 127 | } 128 | 129 | -------------------------------------------------------------------------------- /tests/primes/primes.md: -------------------------------------------------------------------------------- 1 | # Printing the first 1000 primes 2 | 3 | This is an example of literate programming as written in [Knuth Literate Programming](http://www.literateprogramming.com/knuthweb.pdf). 4 | 5 | ## Basic Outline 6 | 7 | This program will produce a function that takes in an integer `m` and returns an array of the first `m` primes. 8 | 9 | 10 | var firstPrimes = _"Compute first m primes"; 11 | 12 | 13 | ## Using it 14 | 15 | This is a command line literate program. We evaluate the basic outline and output it 16 | 17 | [](# ":| eval _':cmdline' ") 18 | 19 | /*global console, inputs*/ 20 | _"Basic Outline" 21 | var inputs = _"|z primes"; 22 | console.log("The first "+inputs +" primes are: " + firstPrimes(inputs)); 23 | 24 | [cmdline]() 25 | 26 | eval(text); 27 | 28 | 29 | ## Doc 30 | 31 | The file primes.js exports a single object with two methods: 32 | 33 | arr .m (int m ) Computes the first m primes and returns them as an array. 34 | 35 | 36 | ## Compute first m primes 37 | 38 | We need a function that takes in an integer m and gives us an array of the first m primes. 39 | 40 | The variable `primes` will hold the array. 41 | 42 | We continue to loop until the primes array is filled. 43 | 44 | function (m) { 45 | 46 | _"Check m makes sense" 47 | 48 | _"Is it prime:vars" 49 | _"Modify Limit:vars" 50 | 51 | var primes = [2]; 52 | var current = 3; 53 | 54 | while (primes.length < m) { 55 | 56 | _"Is it prime:code" 57 | 58 | _"If prime" 59 | 60 | _"Modify limit:code" 61 | 62 | current += 2; 63 | 64 | } 65 | 66 | return primes; 67 | 68 | 69 | } 70 | 71 | ### Is it prime 72 | 73 | We loop through all computed primes up to a limit testing whether they divide the current number. If one does, then it is not prime. Even none divide, then it is prime, assuming the limit is appropriately used. 74 | 75 | [vars]() 76 | 77 | var isPrime = true; 78 | var i = 1; 79 | // external: primes, current, limit 80 | 81 | 82 | [code]() 83 | 84 | isPrime = true; 85 | for (i = 1; i < limit; i += 1) { 86 | if (current % primes[i] === 0) { 87 | isPrime = false; 88 | break; 89 | } 90 | } 91 | 92 | 93 | ### If prime 94 | 95 | This is simple; just append the prime to the primes list: 96 | 97 | if (isPrime) { 98 | primes.push(current); 99 | } 100 | 101 | 102 | ### Modify limit 103 | 104 | Because of being divisible by the square root of a number if it is non-prime, we can see that it must be less than the next even square. But we can say even more as we are safe as long as the current is less than the square of the smallest prime that could be the smallest divisor. 105 | 106 | For example, if the limit is 2, this will check divisibility by 3. This works for the numbers 5, 7, and 9. At 9, we are at the square 9 and we must step up. The next prime is 5. So now we are good until 25. Indeed, 11, 13, 15, 17, 19, 21, 23 are mostly primes and if not, they are divisible by 3 or 5. 107 | 108 | 109 | [vars]() 110 | 111 | var limit = 2; 112 | var square = 9; 113 | //external current, pimes 114 | 115 | [code]() 116 | 117 | if (current === square) { 118 | limit += 1; 119 | square = primes[limit]*primes[limit]; 120 | } 121 | 122 | Knuth raises the point that `primes[limit]` needs to be defined. It happens that this is so, but it is not trivial. 123 | 124 | ### Check m makes sense 125 | 126 | Is m a positive integer? 127 | 128 | m = parseInt(m, 10); 129 | if (m <= 0) { 130 | console.log("error: need a positive m"); 131 | return []; 132 | } 133 | 134 | -------------------------------------------------------------------------------- /tests/sample/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/sample/canonical/build/count.js: -------------------------------------------------------------------------------- 1 | var numarr = [], start=1, end = 11, step = 1; 2 | 3 | var i; 4 | for (i = start; i < end; i += step) { 5 | numarr.push(i); 6 | } 7 | 8 | console.log("The numbers are: ", numarr.join(", ") ); 9 | -------------------------------------------------------------------------------- /tests/sample/original/sample.md: -------------------------------------------------------------------------------- 1 | # Welcome 2 | 3 | So you want to make a literate program? Let's have a program that outputs all numbers between 1 to 10. 4 | 5 | Let's save it in file count.js 6 | 7 | [count.js](#Structure "save:") 8 | 9 | ## Structure 10 | 11 | We have some intial setup. Then we will generate the array of numbers. We end with outputting the numbers. 12 | 13 | var numarr = [], start=1, end = 11, step = 1; 14 | 15 | _"Loop" 16 | 17 | _"Output" 18 | 19 | ## Output 20 | 21 | At this point, we have the array of numbers. Now we can join them with a comma and output that to the console. 22 | 23 | console.log("The numbers are: ", numarr.join(", ") ); 24 | 25 | ## Loop 26 | 27 | Set the loop up and push the numbers onto it. 28 | 29 | var i; 30 | for (i = start; i < end; i += step) { 31 | numarr.push(i); 32 | } -------------------------------------------------------------------------------- /tests/sample/sample.md: -------------------------------------------------------------------------------- 1 | # Welcome 2 | 3 | So you want to make a literate program? Let's have a program that outputs all numbers between 1 to 10. 4 | 5 | Let's save it in file count.js 6 | 7 | [count.js](#Structure "save:") 8 | 9 | ## Structure 10 | 11 | We have some intial setup. Then we will generate the array of numbers. We end with outputting the numbers. 12 | 13 | var numarr = [], start=1, end = 11, step = 1; 14 | 15 | _"Loop" 16 | 17 | _"Output" 18 | 19 | ## Output 20 | 21 | At this point, we have the array of numbers. Now we can join them with a comma and output that to the console. 22 | 23 | console.log("The numbers are: ", numarr.join(", ") ); 24 | 25 | ## Loop 26 | 27 | Set the loop up and push the numbers onto it. 28 | 29 | var i; 30 | for (i = start; i < end; i += step) { 31 | numarr.push(i); 32 | } -------------------------------------------------------------------------------- /tests/template/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/template/canonical/build/first.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Great Smokey Mountains 6 | 7 | 8 |

    Great Smokey Mountains

    9 |

    These are the great and magnificent mountains.

    10 |

    The mountains rose from the ground over so many years...

    11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/template/canonical/build/out.txt: -------------------------------------------------------------------------------- 1 | great: How are you? 2 | 3 | bye 4 | -------------------------------------------------------------------------------- /tests/template/canonical/build/snippet: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | yadda yadda 5 |
    6 |
    7 |
    8 |
    9 | more yadda yadda but with sliders 10 |
    11 |
    12 |
    13 | -------------------------------------------------------------------------------- /tests/template/original/simpletemp.md: -------------------------------------------------------------------------------- 1 | # Simple 2 | 3 | This is a simple setup for insertion into a template 4 | 5 | [out.txt](#Something "Save:") 6 | 7 | ## Temp 8 | 9 | A template 10 | 11 | great: _"*:simple" 12 | 13 | bye 14 | 15 | ## Something 16 | 17 | _"Fill in*temp" 18 | 19 | ## Fill in 20 | 21 | Hey 22 | 23 | [simple]() 24 | 25 | How are you? -------------------------------------------------------------------------------- /tests/template/original/templating.md: -------------------------------------------------------------------------------- 1 | # Templating 2 | 3 | The idea is that we want to write something which gets used over and over again. This is a common pattern. 4 | 5 | We want to implement various scenarios. One is to have subcblocks that can be used to fill in the template. Another is to have a list of items that get wrapped. The third is to allow for smart insertion somehow. Let's try to figure this stuff out. 6 | 7 | We will use the new syntax that is coming. 8 | 9 | ## Boilerplate 10 | 11 | We use asterisks in the usual litpro quotes. 12 | 13 | 14 | 15 | 16 | 17 | _"*:title" 18 | 19 | 20 |

    _"*:title"

    21 | _"*:body" 22 | _"*:scripts" 23 | 24 | 25 | 26 | ## File usage 27 | 28 | We could then invoke this with 29 | 30 | [first.html](#First-page "Save: *boilerplate") 31 | 32 | We look at the section first-page and pull out the relevant subsections. This is hard to accomplish at the moment as the feeder evaluates this stuff first. But probably it can get access to the cblocks. 33 | 34 | 35 | ## First page 36 | 37 | This first page is a bunch of content in a context that gets filled in 38 | 39 | [title](# ) 40 | 41 | Great Smokey Mountains 42 | 43 | [body](# ".md | marked") 44 | 45 | These are the great and magnificent mountains. 46 | 47 | _":history" 48 | 49 | [scripts](# ) 50 | 51 | SCRIPT(jQuery,d3,canvg,http://great.com/zooks.js) 52 | _"scriptxt | wrap(script)" 53 | 54 | 55 | 56 | [history](# ) 57 | 58 | The mountains rose from the ground over so many years... 59 | 60 | ## Script and wrap 61 | 62 | The SCRIPT macro would take in various comma-delimited texts and put them in the usual script format. 63 | 64 | The wrap will wrap the given content into an HTML script tag. This already exists, I believe. 65 | 66 | ## BS Snippets 67 | 68 |
    69 |
    70 |
    71 | _"*:main" 72 |
    73 |
    74 |
    75 |
    76 | _"*:side" 77 |
    78 |
    79 |
    80 | 81 | It is okay not to have it; then it is just a blank. 82 | 83 | 84 | ## Example snippet 85 | 86 | Some nice stuff 87 | 88 | [name](# ) 89 | 90 | Scratch 91 | 92 | [main](# ) 93 | 94 | yadda yadda 95 | 96 | [side](# ) 97 | 98 | more yadda yadda but with sliders 99 | 100 | [script](# ) 101 | 102 | somejavascript 103 | 104 | 105 | ### Body 106 | 107 | _"Example Snippet*bs snippets | log" 108 | -------------------------------------------------------------------------------- /tests/template/simpletemp.md: -------------------------------------------------------------------------------- 1 | # Simple 2 | 3 | This is a simple setup for insertion into a template 4 | 5 | [out.txt](#Something "Save:") 6 | 7 | ## Temp 8 | 9 | A template 10 | 11 | great: \_":simple" 12 | 13 | bye 14 | 15 | ## Something 16 | 17 | _"temp | compile fill in" 18 | 19 | ## Fill in 20 | 21 | Hey 22 | 23 | [simple]() 24 | 25 | How are you? 26 | -------------------------------------------------------------------------------- /tests/template/templating.md: -------------------------------------------------------------------------------- 1 | # Templating 2 | 3 | The idea is that we want to write something which gets used over and over again. This is a common pattern. 4 | 5 | We want to implement various scenarios. One is to have subcblocks that can be used to fill in the template. Another is to have a list of items that get wrapped. The third is to allow for smart insertion somehow. Let's try to figure this stuff out. 6 | 7 | We will use the new syntax that is coming. 8 | 9 | ## Boilerplate 10 | 11 | We use asterisks in the usual litpro quotes. 12 | 13 | 14 | 15 | 16 | 17 | \_":title" 18 | 19 | 20 |

    \_":title"

    21 | \_":body" 22 | \_":scripts" 23 | 24 | 25 | 26 | ## File usage 27 | 28 | We could then invoke this with 29 | 30 | [first.html](#boilerplate "Save: | compile first page") 31 | 32 | We look at the section first-page and pull out the relevant subsections. This is hard to accomplish at the moment as the feeder evaluates this stuff first. But probably it can get access to the cblocks. 33 | 34 | 35 | ## First page 36 | 37 | This first page is a bunch of content in a context that gets filled in 38 | 39 | [title]() 40 | 41 | Great Smokey Mountains 42 | 43 | [body](# ": | md") 44 | 45 | These are the great and magnificent mountains. 46 | 47 | _":history" 48 | 49 | [scripts]() 50 | 51 | _"| script jQuery, d3, canvg, http://great.com/zooks.js" 52 | 53 | _"scriptxt | wrap " 54 | 55 | 56 | 57 | [history]() 58 | 59 | The mountains rose from the ground over so many years... 60 | 61 | ## Script and wrap 62 | 63 | The SCRIPT macro would take in various comma-delimited texts and put them in the usual script format. But just to do something: 64 | 65 | function (code, args) { 66 | return args.reduce(function (val, el) { 67 | return val + ''; 68 | }, ""); 69 | } 70 | 71 | [script](# "define: sync") 72 | 73 | The wrap will wrap the given content into an HTML script tag. This already exists, I believe. 74 | 75 | ### scriptxt 76 | 77 | console.log("awesome"); 78 | 79 | ## Snippets 80 | 81 |
    82 |
    83 |
    84 | \_":main" 85 |
    86 |
    87 |
    88 |
    89 | \_":side" 90 |
    91 |
    92 |
    93 | 94 | It is okay not to have it; then it is just a blank. 95 | 96 | 97 | ## Example snippet 98 | 99 | Some nice stuff 100 | 101 | [name]() 102 | 103 | Scratch 104 | 105 | [main]() 106 | 107 | yadda yadda 108 | 109 | [side]() 110 | 111 | more yadda yadda but with sliders 112 | 113 | [script]() 114 | 115 | somejavascript 116 | 117 | [attr]() 118 | 119 | class="cool" 120 | 121 | [attrNav]() 122 | 123 | 124 | ### Body 125 | 126 | _"snippets | compile example snippet" 127 | 128 | [snippet](# "save:") 129 | -------------------------------------------------------------------------------- /tests/testpro/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/testpro/canonical/build/just-talk: -------------------------------------------------------------------------------- 1 | Alice needs Bob needs _":Alice" 2 | 3 | Bobby needs Alex is good. 4 | 5 | George thinks Alex is good. 6 | -------------------------------------------------------------------------------- /tests/testpro/canonical/build/out.txt: -------------------------------------------------------------------------------- 1 | just some stuff 2 | 3 | more stuff 4 | -------------------------------------------------------------------------------- /tests/testpro/canonical/build/testpro.js: -------------------------------------------------------------------------------- 1 | te = (function () { 2 | return function () { 3 | great = 3; 4 | return great; 5 | }; 6 | })(); 7 | 8 | three = 3; 9 | 10 | function () { 11 | console.log("I was here"); 12 | 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tests/testpro/original/period.md: -------------------------------------------------------------------------------- 1 | # Period problem 2 | 3 | A bug is reported that if there is a period on the next line from a swtich, 4 | then there is an error in parsing. Let's see 5 | 6 | ## Begin 7 | 8 | Hi there 9 | 10 | _"start" 11 | 12 | _"start:end" 13 | 14 | 15 | ## Start 16 | 17 | again? 18 | 19 | just some stuff 20 | 21 | [end]() 22 | . 23 | 24 | more stuff 25 | -------------------------------------------------------------------------------- /tests/testpro/original/testpro.md: -------------------------------------------------------------------------------- 1 | # Test Literate Program 2 | 3 | We just need a place to do some minor tests. 4 | 5 | 6 | ## Body 7 | 8 | The body of the test [testpro.js](# "save: js|jshint") 9 | 10 | 11 | [](# "js") 12 | 13 | te = (function () { 14 | return _"f1"; 15 | })(); 16 | 17 | three = _`1+1+1`; 18 | 19 | function () { 20 | _"cl" 21 | } 22 | 23 | 24 | ## f1 25 | 26 | A function 27 | 28 | function () { 29 | great = 3; 30 | return great; 31 | } 32 | 33 | ## cl 34 | 35 | console.log("I was here"); 36 | 37 | 38 | 39 | 40 | 41 | 42 | ## Now for some bad stuff 43 | 44 | What happens if to blocks refer to each other? 45 | 46 | [Alice](# ) 47 | 48 | Alice needs _":Bob" 49 | 50 | [Bob](# ) 51 | 52 | Bob needs _":Alice" 53 | 54 | ## Now for some bad stuff 55 | 56 | What happens if to blocks refer to each other? 57 | 58 | [Alex](# ) 59 | 60 | Alex is good. 61 | 62 | [Bobby](# "|log" ) 63 | 64 | Bobby needs _":Alex" 65 | 66 | ## What is up 67 | 68 | Can we refer to the second block? 69 | 70 | [out](# "|log") 71 | 72 | George thinks _"Now for some bad stuff 1:Alex" 73 | 74 | -------------------------------------------------------------------------------- /tests/testpro/period.md: -------------------------------------------------------------------------------- 1 | # Period problem 2 | 3 | A bug is reported that if there is a period on the next line from a swtich, 4 | then there is an error in parsing. Let's see 5 | 6 | ## Begin 7 | 8 | Hi there 9 | 10 | _"start" 11 | 12 | _"start:end" 13 | 14 | 15 | [out.txt](# "save:") 16 | 17 | ## Start 18 | 19 | again? 20 | 21 | just some stuff 22 | 23 | [end]() 24 | . 25 | 26 | more stuff 27 | 28 | -------------------------------------------------------------------------------- /tests/testpro/testpro.md: -------------------------------------------------------------------------------- 1 | # Test Literate Program 2 | 3 | We just need a place to do some minor tests. 4 | 5 | 6 | ## Body 7 | 8 | The body of the test [testpro.js](# "save: |jshint") 9 | 10 | 11 | te = (function () { 12 | return _"f1"; 13 | })(); 14 | 15 | three = _"| eval text = 1+1+1"; 16 | 17 | function () { 18 | _"cl" 19 | 20 | 21 | 22 | } 23 | 24 | 25 | ## f1 26 | 27 | A function 28 | 29 | function () { 30 | great = 3; 31 | return great; 32 | } 33 | 34 | ## cl 35 | 36 | console.log("I was here"); 37 | 38 | ## Now for some bad stuff 39 | 40 | What happens if to blocks refer to each other? It does not compile, that's 41 | what. Adding a \ for the underscore to resolve. 42 | 43 | _":alice" 44 | 45 | _":bobby" 46 | 47 | _"what is up:out" 48 | 49 | [just-talk](# "save:") 50 | 51 | [Alice]() 52 | 53 | Alice needs _":Bob" 54 | 55 | [Bob]() 56 | 57 | Bob needs \_":Alice" 58 | 59 | ## Now for some bad stuff 60 | 61 | What happens if to blocks refer to each other? 62 | 63 | [Alex]() 64 | 65 | Alex is good. 66 | 67 | [Bobby](# ":|log" ) 68 | 69 | Bobby needs _":Alex" 70 | 71 | ## What is up 72 | 73 | Can we refer to the second block? 74 | 75 | [out](# ":|log") 76 | 77 | George thinks _"Now for some bad stuff:Alex" 78 | 79 | --------------------------------------------------------------------------------