├── Coding Beyond Logic └── quine.js ├── Functional JavaScript └── oddweb.js ├── Progress ├── elements-r1.js ├── elements-r2.js ├── elements-r3.js ├── elements-results.txt └── elements-test-suite.js ├── README.md └── eval and Domain-Specific Languages ├── binMatch.html ├── binMatch.js └── compile.js /Coding Beyond Logic/quine.js: -------------------------------------------------------------------------------- 1 | var a = []; a[0] = 'var a = []; '; a[1] = 'a['; 2 | a[2] = '] = '; 3 | a[3] = '\''; 4 | a[4] = '\\'; 5 | a[5] = ';'; 6 | a[6] = ''; 7 | a[7] = 'for(var i = 0; i < a.length; i++) console.log((i == 0 ? a[0] : a[6]) + a[1] + i + a[2] + a[3] + ((i == 3 || i == 4) ? a[4] : a[6]) + a[i] + a[3] + a[5] + (i == 7 ? a[7] : a[6]))';for(var i = 0; i < a.length; i++) console.log((i==0?a[0]:a[6])+a[1]+i+a[2]+a[3]+((i==3||i==4)?a[4]:a[6])+a[i]+a[3]+a[5]+(i==7?a[7]:a[6])) -------------------------------------------------------------------------------- /Functional JavaScript/oddweb.js: -------------------------------------------------------------------------------- 1 | "use script" 2 | 3 | // This is a commented version of oddweb, a static site generator 4 | // that was mentioned in Beautiful JavaScript (O'Reilly 2015) 5 | // 6 | // For the most up-to-date version of this file and its test suite, 7 | // please refer to its original repository: https://github.com/valueof/oddweb 8 | 9 | var path = require("path") 10 | var sh = require("shelljs") 11 | var fs = require("fs") 12 | var mime = require("mime") 13 | var handlebars = require("handlebars") 14 | var markdown = require("markdown").markdown 15 | var moment = require("moment") 16 | 17 | handlebars.registerHelper("date", function (date, format) { 18 | return moment(date).format(format) 19 | }) 20 | 21 | // Our main focus of interest are the following three 22 | // functions: read, build, and write. Each one of them 23 | // takes an object as an argument, modifies the object 24 | // in place and returns the modified object. 25 | // 26 | // The object's signature: 27 | // { 28 | // src: string, 29 | // dev: boolean, 30 | // pages: array, 31 | // cache: array, 32 | // resources: array 33 | // } 34 | 35 | // Here we read all the necessary information from 36 | // the disk: templates, supporting files, and so on. 37 | function read(site) { 38 | var dir = site.dir 39 | var uid = 0 40 | 41 | // All pages are in the directory named 'pages'. Each 42 | // page may contain meta information before the template 43 | // so we check to see if the file starts with an opening 44 | // brace. If it does, we parse it as JSON, extract the 45 | // meta data and make necessary modifications. 46 | site.pages = sh.ls("-R", path.join(dir, "pages")) 47 | site.pages = site.pages.reduce(function (acc, file) { 48 | var ap = path.join(dir, "pages", file) 49 | 50 | if (sh.test("-d", ap)) 51 | return acc 52 | 53 | var data = sh.cat(ap) 54 | var meta = {} 55 | 56 | if (data.trim()[0] === "{") { 57 | data = data.split("\n\n") 58 | meta = JSON.parse(data[0]) 59 | data = data.slice(1).join("\n\n") 60 | } 61 | 62 | // Add runtime data to the meta object. 63 | meta.id = uid++ 64 | meta.type = path.extname(file).slice(1) 65 | meta.path = meta.type === "md" ? file.replace(/\.\S+$/, ".html") : file 66 | 67 | // Each page can declare alternative URL (useful for 68 | // legacy links) so we should respect it. 69 | if (meta.altUrl) { 70 | meta.altPath = normalize(meta.altUrl, meta.path) 71 | 72 | if (path.extname(meta.altPath) === "") 73 | meta.altPath = path.join(meta.altPath, "index.html") 74 | } 75 | 76 | if (meta.url) 77 | meta.path = normalize(meta.url, meta.path) 78 | else 79 | meta.url = "/" + meta.path 80 | 81 | if (path.extname(meta.path) === "") 82 | meta.path = path.join(meta.path, "index.html") 83 | 84 | if (meta.template && path.extname(meta.template) === "") 85 | meta.template = meta.template + ".html" 86 | 87 | return acc.concat({ meta: meta, data: data }) 88 | }, []) 89 | 90 | // We want to read templates once and then access them 91 | // from memory in any of the functions that might follow. 92 | site.cache = sh.ls("-R", path.join(dir, "templates")) 93 | site.cache = site.cache.reduce(function (acc, file) { 94 | var ap = path.join(dir, "templates", file) 95 | 96 | if (sh.test("-d", ap) || path.extname(ap) !== ".html") 97 | return acc 98 | 99 | acc[file] = sh.cat(ap) 100 | return acc 101 | }, {}) 102 | 103 | // The same logic applies to supporting files: functions 104 | // that might follow should have quick access to them without 105 | // having to know where on the disk these files are located. 106 | site.resources = sh.ls("-R", path.join(dir, "res")) 107 | site.resources = site.resources.reduce(function (acc, file) { 108 | var ap = path.join(dir, "res", file) 109 | 110 | if (sh.test("-d", ap)) 111 | return acc 112 | 113 | var meta = { path: file, binary: !/^text\//.test(mime.lookup(file)) } 114 | var data = meta.binary ? fs.readFileSync(ap, { encoding: "binary" }) : sh.cat(ap) 115 | 116 | return acc.concat({ meta: meta, data: data }) 117 | }, []) 118 | 119 | return site 120 | } 121 | 122 | // This function builds the site: it compiles all templates, 123 | // executes plugins, and so on. 124 | function build(site) { 125 | var src = path.resolve(site.src) 126 | var config = path.join(src, "package.json") 127 | var plugins = require(config).oddwebPlugins || [] 128 | 129 | // Since we're working with a state that is represented 130 | // by a simple object, the following nine lines of code 131 | // are everything we need for a working plugin system. 132 | // Each plugin mirrors this function: it takes an object, 133 | // modifies it, and returns it back to us. 134 | // 135 | // For example, here's a plugin that replaces word 'cloud' 136 | // with 'cat' on all pages: 137 | // 138 | // module.exports = function (site, handlebars) { 139 | // site.pages = site.pages.map(function (page) { 140 | // page.data = page.data.replace(/cloud/g/, "cat") 141 | // return page 142 | // }) 143 | // 144 | // return site 145 | // } 146 | site = plugins.reduce(function (acc, plugin) { 147 | if (/^core\//.test(plugin)) 148 | return require(path.join(path.dirname(module.filename), plugin) + ".js")(acc, handlebars) 149 | 150 | if (path.extname(plugin) === ".js") 151 | return require(path.join(src, plugin))(acc, handlebars) 152 | 153 | return require(path.join(src, "node_modules", plugin))(acc, handlebars) 154 | }, site) 155 | 156 | // This is 'the core' of oddweb where we compile templates 157 | // and Markdown files. 158 | site.pages = site.pages.map(function (page) { 159 | if (page.meta.skip) 160 | return page 161 | 162 | switch (page.meta.type) { 163 | case "xml": 164 | case "html": 165 | page.data = handlebars.compile(page.data)({ page: page.meta, site: site }) 166 | break 167 | case "md": 168 | var html = [] 169 | var tmp = page.data.split("\n\n").map(function (block) { 170 | if (block.trim()[0] === "<") 171 | return "$" + (html.push(block) - 1) + "$" 172 | return block 173 | }).join("\n\n") 174 | 175 | page.data = markdown.toHTML(tmp).split("\n\n").map(function (block) { 176 | if (/^
\$\d+\$<\/p>$/.test(block))
177 | return html[block.slice(4, block.length - 5)]
178 | return block
179 | }).join("\n\n")
180 |
181 | if (!page.meta.url)
182 | page.meta.path = page.meta.path.replace(/\.md$/, ".html")
183 | }
184 |
185 | if (page.meta.template) {
186 | page.data = handlebars.compile(site.cache[page.meta.template])({
187 | content: new handlebars.SafeString(page.data),
188 | page: page.meta,
189 | site: site
190 | })
191 | }
192 |
193 | return page
194 | })
195 |
196 | return site
197 | }
198 |
199 | // Finally, a function to write our generated site
200 | // to disk. It's pretty straightforward. We're generating
201 | // a static site so its file hierarchy reflects its URL
202 | // hierarchy.
203 | function write(site) {
204 | function prep(root, p) {
205 | var dir = path.join(root, path.dirname(p))
206 |
207 | if (!sh.test("-e", dir))
208 | sh.mkdir("-p", dir)
209 |
210 | return path.join(root, p)
211 | }
212 |
213 | function wrt(list, root) {
214 | list.forEach(function (item) {
215 | var ap = prep(root, item.meta.path)
216 |
217 | if (item.meta.binary)
218 | fs.writeFileSync(ap, item.data, { encoding: "binary" })
219 | else
220 | item.data.to(ap)
221 |
222 | if (item.meta.altPath)
223 | ("").to(prep(root, item.meta.altPath))
224 | })
225 | }
226 |
227 | wrt(site.pages, path.resolve(path.join(site.src, "site")))
228 | wrt(site.resources, path.resolve(path.join(site.src, "site", "res")))
229 |
230 | return site
231 | }
232 |
233 | module.exports = {
234 | read: read,
235 | build: build,
236 | write: write
237 | }
238 |
239 | // Usage example:
240 | //
241 | // var oddweb = require('oddweb')
242 | // var site = { dir: '/path/to/dir', dev: false }
243 | //
244 | // oddweb.write(oddweb.build(oddweb.read(site)))
245 |
--------------------------------------------------------------------------------
/Progress/elements-r1.js:
--------------------------------------------------------------------------------
1 | function Elements(selector, context) {
2 | var elems, elem, k;
3 |
4 | selector = selector || "";
5 |
6 | this.context = context || document;
7 |
8 | if (Array.isArray(selector) || selector instanceof Elements) {
9 | elems = selector;
10 | } else {
11 | try {
12 | elems = this.context.querySelectorAll(selector);
13 | } catch (e) {
14 | elems = [];
15 | }
16 | }
17 |
18 | if (!elems) {
19 | // elems is either:
20 | // - undefined because the selector was invalid
21 | // resulting in a thrown exception
22 | // - null because the querySelectorAll returns
23 | // null instead of an empty object when no
24 | // matching elements are found.
25 | elems = []
26 | }
27 |
28 | if (elems.length) {
29 | k = -1;
30 | while (elem = elems[++k]) {
31 | this[k] = elem;
32 | }
33 | }
34 |
35 | this.length = elems.length;
36 | }
37 |
38 | Elements.prototype = {
39 | constructor: Elements,
40 | addClass: function(value) {
41 | this.forEach(function(elem) {
42 | elem.classList.add(value);
43 | });
44 |
45 | return this;
46 | },
47 | attr: function(key, value) {
48 | if (typeof value !== "undefined") {
49 | this.forEach(function(elem) {
50 | elem.setAttribute(key, value);
51 | });
52 |
53 | return this;
54 | } else {
55 | return this[0] && this[0].getAttribute(key);
56 | }
57 | },
58 | css: function(key, value) {
59 | if (typeof value !== "undefined") {
60 | this.forEach(function(elem) {
61 | elem.style[key] = value;
62 | });
63 |
64 | return this;
65 | } else {
66 | return this[0] && this[0].style[key];
67 | }
68 | },
69 | html: function(html) {
70 | if (typeof html !== "undefined") {
71 | this.forEach(function(elem) {
72 | elem.innerHTML = html;
73 | });
74 | return this;
75 | } else {
76 | return this[0] && this[0].innerHTML;
77 | }
78 | },
79 | filter: function() {
80 | return new Elements([].filter.apply(this, arguments));
81 | },
82 | forEach: function() {
83 | [].forEach.apply(this, arguments);
84 | return this;
85 | },
86 | indexOf: function() {
87 | return [].indexOf.apply(this, arguments);
88 | },
89 | push: function() {
90 | [].push.apply(this, arguments);
91 | return this;
92 | },
93 | slice: function() {
94 | return new Elements([].slice.apply(this, arguments));
95 | },
96 | sort: function() {
97 | return [].sort.apply(this, arguments);
98 | }
99 | };
--------------------------------------------------------------------------------
/Progress/elements-r2.js:
--------------------------------------------------------------------------------
1 | function Elements(selector, context) {
2 | Array.call(this);
3 |
4 | var elems;
5 |
6 | this.context = context || document;
7 |
8 | if (Array.isArray(selector) || selector instanceof Elements) {
9 | elems = selector;
10 | } else {
11 | try {
12 | elems = this.context.querySelectorAll(selector || "");
13 | } catch (e) {
14 | elems = [];
15 | }
16 | }
17 |
18 | if (!elems) {
19 | // elems is either:
20 | // - undefined because the selector was invalid
21 | // resulting in a thrown exception
22 | // - null because the querySelectorAll returns
23 | // null instead of an empty object when no
24 | // matching elements are found.
25 | elems = []
26 | }
27 |
28 | this.push.apply(this, elems);
29 | }
30 |
31 | Elements.prototype = Object.create(Array.prototype);
32 | Elements.prototype.constructor = Elements;
33 |
34 | Elements.prototype.addClass = function(value) {
35 | this.forEach(function(elem) {
36 | elem.classList.add(value);
37 | });
38 |
39 | return this;
40 | };
41 | Elements.prototype.attr = function(key, value) {
42 | if (typeof value !== "undefined") {
43 | this.forEach(function(elem) {
44 | elem.setAttribute(key, value);
45 | });
46 |
47 | return this;
48 | } else {
49 | return this[0] && this[0].getAttribute(key);
50 | }
51 | };
52 | Elements.prototype.css = function(key, value) {
53 | if (typeof value !== "undefined") {
54 | this.forEach(function(elem) {
55 | elem.style[key] = value;
56 | });
57 |
58 | return this;
59 | } else {
60 | return this[0] && this[0].style[key];
61 | }
62 | };
63 | Elements.prototype.html = function(html) {
64 | if (typeof html !== "undefined") {
65 | this.forEach(function(elem) {
66 | elem.innerHTML = html;
67 | });
68 | return this;
69 | } else {
70 | return this[0] && this[0].innerHTML;
71 | }
72 | };
73 | Elements.prototype.filter = function() {
74 | return new Elements([].filter.apply(this, arguments));
75 | };
76 | Elements.prototype.slice = function() {
77 | return new Elements([].slice.apply(this, arguments));
78 | };
79 | Elements.prototype.push = function() {
80 | [].push.apply(this, arguments);
81 | return this;
82 | };
--------------------------------------------------------------------------------
/Progress/elements-r3.js:
--------------------------------------------------------------------------------
1 | class Elements extends Array {
2 | constructor(selector = "", context = document) {
3 | super();
4 |
5 | let elems;
6 |
7 | this.context = context;
8 |
9 | if (Array.isArray(selector) ||
10 | selector instanceof Elements) {
11 | elems = selector;
12 | } else {
13 | try {
14 | elems = this.context.querySelectorAll(selector);
15 | } catch (e) {
16 | // Thrown Exceptions caused by invalid selectors
17 | // is a nuisance.
18 | }
19 | }
20 |
21 | if (!elems) {
22 | // elems is either:
23 | // - undefined because the selector was invalid
24 | // resulting in a thrown exception
25 | // - null because the querySelectorAll returns
26 | // null instead of an empty object when no
27 | // matching elements are found.
28 | elems = []
29 | }
30 |
31 | this.push(...elems);
32 | }
33 | addClass(value) {
34 | return this.forEach(elem => elem.classList.add(value));
35 | }
36 | attr(key, value) {
37 | if (typeof value !== "undefined") {
38 | return this.forEach(elem => elem.setAttribute(key, value));
39 | } else {
40 | return this[0] && this[0].getAttribute(key);
41 | }
42 | }
43 | css(key, value) {
44 | if (typeof value !== "undefined") {
45 | return this.forEach(elem => elem.style[key] = value);
46 | } else {
47 | return this[0] && this[0].style[key];
48 | }
49 | }
50 | html(html) {
51 | if (typeof html !== "undefined") {
52 | return this.forEach(elem => elem.innerHTML = html);
53 | } else {
54 | return this[0] && this[0].innerHTML;
55 | }
56 | }
57 | filter(callback) {
58 | return new Elements(super.filter(callback, this));
59 | }
60 | slice(...args) {
61 | return new Elements(super.slice(...args));
62 | }
63 | forEach(callback) {
64 | super.forEach(callback, this);
65 | return this;
66 | }
67 | push(...elems) {
68 | super.push(...elems);
69 | return this;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Progress/elements-results.txt:
--------------------------------------------------------------------------------
1 | [[Result]] The Elements class prototype
2 | [[Result]] Zero length instance
3 | [[Result]] Elements from Elements
4 | [[Result]] One match will have a length of 1 (no context)
5 | [[Result]] One match will have a length of 1
6 | [[Result]] Two matches will have a length of 2
7 | [[Result]] Add a class
8 | [[Result]] Set and get an attribute
9 | [[Result]] Set and get a css style property
10 | [[Result]] Set and get some html
11 | [[Result]] Filtering produces a new instance
12 | [[Result]] Filter with a dummy predicate
13 | [[Result]] Filter with a predicate
14 | [[Result]] Invocation forEach item in the list
15 | [[Result]] Find the indexOf an element
16 | [[Result]] Push an element onto the list
17 | [[Result]] Push returns the instance, not the length
18 | [[Result]] Slicing produces a new instance
19 | [[Result]] Slice a list of elements
20 | [[Result]] Sort a list of elements by nodeName
--------------------------------------------------------------------------------
/Progress/elements-test-suite.js:
--------------------------------------------------------------------------------
1 | function assert(expr) {
2 | return !!expr;
3 | }
4 |
5 | function fixture() {
6 | var div = document.createElement("div");
7 | var li = document.createElement("li");
8 | var ul = document.createElement("ul");
9 |
10 | div.appendChild(document.createElement("span"));
11 | div.firstElementChild.textContent = "test text content";
12 |
13 | div.appendChild(ul.cloneNode());
14 | div.lastElementChild.appendChild(li.cloneNode());
15 |
16 | div.appendChild(ul.cloneNode());
17 | div.lastElementChild.appendChild(li.cloneNode());
18 |
19 | return div;
20 | }
21 |
22 | var tests = {
23 | "The Elements class prototype": function() {
24 | var elems = new Elements();
25 | var methods = [
26 | "addClass", "attr", "css", "html",
27 | "filter", "forEach", "indexOf", "push", "slice", "sort"
28 | ];
29 |
30 | return methods.reduce(function(result, method) {
31 | return typeof Elements.prototype[method] === "function";
32 | }, true);
33 | },
34 | "Zero length instance": function() {
35 | var elems = new Elements();
36 |
37 | return assert(elems.length === 0);
38 | },
39 | "Elements from Elements": function(fixture) {
40 | var elems = new Elements(new Elements([fixture]));
41 |
42 | return assert(elems.length === 1);
43 | },
44 | "One match will have a length of 1 (no context)": function() {
45 | var elems = new Elements("body");
46 |
47 | return assert(elems.length === 1);
48 | },
49 | "One match will have a length of 1": function(fixture) {
50 | var elems = new Elements("span", fixture);
51 |
52 | return assert(elems.length === 1);
53 | },
54 | "Two matches will have a length of 2": function(fixture) {
55 | var elems = new Elements("ul", fixture);
56 |
57 | return assert(elems.length === 2);
58 | },
59 | "Add a class": function(fixture) {
60 | var elems = new Elements("span", fixture);
61 |
62 | elems.addClass("foo");
63 |
64 | return assert(elems[0].className === "foo");
65 | },
66 | "Set and get an attribute": function(fixture) {
67 | var elems = new Elements("span", fixture);
68 |
69 | elems.attr("id", "foo");
70 |
71 | return assert(elems.attr("id") === "foo");
72 | },
73 | "Set and get a css style property": function(fixture) {
74 | var elems = new Elements("span", fixture);
75 |
76 | elems.css("color", "red");
77 |
78 | return assert(elems.css("color") === "red");
79 | },
80 | "Set and get some html": function(fixture) {
81 | var elems = new Elements("span", fixture);
82 | var html = "hi!";
83 |
84 | elems.html(html);
85 |
86 | return assert(elems.html() === html);
87 | },
88 | "Filtering produces a new instance": function(fixture) {
89 | var elems = new Elements("*", fixture);
90 | var filtered = elems.filter(Boolean);
91 |
92 | return assert(filtered instanceof Elements) &&
93 | assert(filtered !== elems);
94 | },
95 | "Filter with a dummy predicate": function(fixture) {
96 | var elems = new Elements("*", fixture);
97 | var filtered = elems.filter(Boolean);
98 |
99 | return assert(filtered.length === elems.length) &&
100 | assert(filtered instanceof Elements) &&
101 | assert(filtered !== elems);
102 | },
103 | "Filter with a predicate": function(fixture) {
104 | var elems = new Elements("*", fixture);
105 | var filtered = elems.filter(function(elem) {
106 | return elem.nodeName.toLowerCase() === "span";
107 | });
108 |
109 | return assert(filtered.length === 1);
110 | },
111 | "Invocation forEach item in the list": function(fixture) {
112 | var elems = new Elements("*", fixture);
113 | var calls = 0;
114 | elems.forEach(function(elem) {
115 | calls++;
116 | });
117 |
118 | return assert(calls === 5);
119 | },
120 | "Find the indexOf an element": function(fixture) {
121 | var elems = new Elements("*", fixture);
122 |
123 | return assert(elems.indexOf(fixture.firstElementChild) === 0);
124 | },
125 | "Push an element onto the list": function(fixture) {
126 | var elems = new Elements("*", fixture);
127 | elems.push(fixture.firstElementChild);
128 |
129 | return assert(elems.length === 6);
130 | },
131 | "Push returns the instance, not the length": function(fixture) {
132 | var elems = new Elements("*", fixture);
133 | elems.push(fixture.firstElementChild);
134 |
135 | return assert(elems.push(fixture.firstElementChild) === elems);
136 | },
137 | "Slicing produces a new instance": function(fixture) {
138 | var elems = new Elements("*", fixture);
139 | var sliced = elems.slice(1);
140 |
141 | return assert(sliced instanceof Elements) &&
142 | assert(sliced !== elems);
143 | },
144 | "Slice a list of elements": function(fixture) {
145 | var elems = new Elements("*", fixture);
146 | var sliced = elems.slice(1);
147 |
148 | return assert(sliced.length === 4);
149 | },
150 | "Sort a list of elements by nodeName": function(fixture) {
151 | var elems = new Elements("*", fixture);
152 | elems.sort(function(a, b) {
153 | if (a.nodeName < b.nodeName) {
154 | return -1;
155 | }
156 | if (a.nodeName > b.nodeName) {
157 | return 1;
158 | }
159 | return 0;
160 | });
161 |
162 | return assert(elems[0].nodeName === "LI") &&
163 | assert(elems[2].nodeName === "SPAN") &&
164 | assert(elems[3].nodeName === "UL");
165 | }
166 | };
167 |
168 | Object.keys(tests).forEach(function(message) {
169 | var result = tests[message](fixture());
170 | var response = (result ? "Pass" : "Fail") + ": " + message;
171 |
172 | console.log(response);
173 | });
174 |
175 | console.log("tests complete");
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Beautiful JavaScript
2 | ==========
3 |
4 | This is the example code that accompanies Beautiful JavaScript by Anton Kovalyov (9781449370756).
5 |
6 | Click the Download Zip button to the right to download example code.
7 |
8 | Visit the catalog page [here](http://shop.oreilly.com/product/0636920030706.do).
9 |
10 | See an error? Report it [here](http://oreilly.com/catalog/errata.csp?isbn=0636920030706), or simply fork and send us a pull request.
11 |
12 |
--------------------------------------------------------------------------------
/eval and Domain-Specific Languages/binMatch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/eval and Domain-Specific Languages/binMatch.js:
--------------------------------------------------------------------------------
1 | function binMatch(spec) {
2 | var totalSize = 0, code = "", match;
3 | while (match = /^([^:]+):(\w+)(\d+)\s*/.exec(spec)) {
4 | spec = spec.slice(match[0].length);
5 | var pattern = match[1], type = match[2], size = Number(match[3]);
6 | totalSize += size;
7 |
8 | if (pattern == "_") {
9 | code += "pos += " + size + ";";
10 | } else if (/^[\w$]+$/.test(pattern)) {
11 | code += "out." + pattern + " = " + binMatch.read[type](size) + ";";
12 | } else {
13 | code += "if (" + binMatch.read[type](size) + " !== " +
14 | pattern + ") return null;";
15 | }
16 | }
17 |
18 | code = "if (input.length - pos < " + totalSize + ") return null;" +
19 | "var out = {end: pos + " + totalSize + "};" + code + "return out;";
20 | return new Function("input, pos", code);
21 | }
22 |
23 | binMatch.read = {
24 | uint: function(size) {
25 | for(var exprs=[],i=1;i<=size;++i)
26 | exprs.push("input[pos++] * " + Math.pow(256, size - i));
27 | return exprs.join(" + ");
28 | },
29 | str: function(size) {
30 | for(var exprs=[],i=0;i