UNPKG

85.8 kBJavaScriptView Raw
1/*global require, module */
2/*jslint evil:true*/
3
4var EvW = require('event-when');
5var commonmark = require('commonmark');
6require('string.fromcodepoint');
7
8
9var apply = function (instance, obj) {
10 var meth, i, n;
11
12 for (meth in obj) {
13 n = obj[meth].length;
14 for (i = 0; i < n; i += 1) {
15 instance[meth].apply(instance, obj[meth][i]);
16 }
17
18 }
19};
20
21
22var Folder = function (actions) {
23 actions = actions || Folder.actions;
24
25 var gcd = this.gcd = new EvW();
26 //.when will preserve initial, not emitted order
27 gcd.initialOrdering = true;
28
29 this.docs = {};
30 this.scopes = { g:{} };
31
32 this.commands = Object.create(Folder.commands);
33 this.directives = Object.create(Folder.directives);
34 this.subCommands = Object.create(Folder.subCommands);
35 this.reports = {};
36 this.recording = {};
37 this.stack = {};
38 this.reporters = Folder.reporters;
39 this.plugins = Object.create(Folder.plugins);
40 this.flags = {};
41 this.Folder = Folder;
42
43 this.done = {
44 gcd : new EvW(),
45 cache : {}
46 };
47 this.done.gcd.action("done", function (data, evObj) {
48 var folder = this;
49 folder.done.cache[evObj.ev] = true;
50 }, this);
51
52 gcd.parent = this;
53
54 gcd.on("block needs compiling", "compiling block");
55
56 gcd.action("compiling block", function (data, evObj) {
57 var gcd = evObj.emitter;
58 var file = evObj.pieces[1];
59 var blockname = evObj.pieces[0];
60 var doc = gcd.parent.docs[file];
61 var block = doc.blocks[blockname];
62 doc.blockCompiling(block, file, blockname);
63 }
64 );
65
66
67 gcd.on("heading found", "add block");
68
69 gcd.action("add block", function (data, evObj) {
70 var gcd = evObj.emitter;
71 var file = evObj.pieces[0];
72 var doc = gcd.parent.docs[file];
73 var text = data.trim().toLowerCase();
74 var curname = doc.heading = doc.curname = text;
75 doc.levels[0] = text;
76 doc.levels[1] = '';
77 doc.levels[2] = '';
78 if ( ! doc.blocks.hasOwnProperty(curname) ) {
79 doc.blocks[curname] = '';
80 }
81 }
82 );
83
84 gcd.on("heading found:5", "add slashed block");
85
86 gcd.action("add slashed block", function (data, evObj) {
87 var gcd = evObj.emitter;
88 var file = evObj.pieces[0];
89 var doc = gcd.parent.docs[file];
90 var text = data.trim().toLowerCase();
91 doc.levels[1] = text;
92 doc.levels[2] = '';
93 var curname = doc.heading = doc.curname = doc.levels[0]+'/'+text;
94 if ( ! doc.blocks.hasOwnProperty(curname) ) {
95 doc.blocks[curname] = '';
96 }
97 evObj.stop = true;
98 }
99 );
100
101 gcd.on("heading found:6", "add double slashed block");
102
103 gcd.action("add double slashed block", function (data, evObj) {
104 var gcd = evObj.emitter;
105 var file = evObj.pieces[0];
106 var doc = gcd.parent.docs[file];
107 var text = data.trim().toLowerCase();
108 doc.levels[2] = text;
109 var curname = doc.heading = doc.curname = doc.levels[0]+'/'+doc.levels[1]+'/'+text;
110 if ( ! doc.blocks.hasOwnProperty(curname) ) {
111 doc.blocks[curname] = '';
112 }
113 evObj.stop = true;
114 }
115 );
116
117 gcd.on("switch found", "create minor block");
118
119 gcd.action("create minor block", function (data, evObj) {
120 var gcd = evObj.emitter;
121 var file = evObj.pieces[0];
122 var doc = gcd.parent.docs[file];
123 var colon = doc.colon;
124 var text = data[0].trim().toLowerCase();
125
126 var subEmit, textEmit, doneEmit;
127
128 var curname = doc.curname = doc.heading+colon.v+text;
129 if ( ! doc.blocks.hasOwnProperty(curname) ) {
130 doc.blocks[curname] = '';
131 }
132
133
134 var title = data[1];
135 var fname = evObj.pieces[0] + ":" + curname;
136 doneEmit = "text ready:" + fname;
137 var pipename;
138 if (title) { // need piping
139 title = title.trim()+'"';
140 pipename = fname + colon.v + "sp";
141 textEmit = "text ready:" + pipename;
142 subEmit = "switch chain done:" + pipename;
143
144 gcd.when(textEmit, subEmit);
145
146 gcd.once(subEmit, function (data) {
147 var text = data[data.length-1][1] || '';
148 doc.store(curname, text);
149 gcd.emit(doneEmit, text);
150 });
151
152 gcd.flatWhen("minor ready:" + fname, textEmit);
153
154 doc.pipeParsing(title, 0, '"' , pipename, doc.heading,
155 subEmit, textEmit );
156 } else { //just go
157 gcd.once("minor ready:" + fname, function (text) {
158 doc.store(curname, text);
159 });
160 gcd.flatWhen("minor ready:" + fname, "text ready:" + fname);
161
162 }
163 }
164 );
165
166 gcd.on("code block found", "add code block");
167
168 gcd.action("add code block", function (data, evObj) {
169 var gcd = evObj.emitter;
170 var file = evObj.pieces[0];
171 var doc = gcd.parent.docs[file];
172 if (doc.blockOff > 0) { return;}
173 if (doc.blocks[doc.curname]) {
174 doc.blocks[doc.curname] += doc.join + data;
175 } else {
176 doc.blocks[doc.curname] = data;
177 }
178 }
179 );
180
181 gcd.on("code block found:ignore", "ignore code block");
182
183 gcd.action("ignore code block", function (data, evObj) {
184 var gcd = evObj.emitter;
185 evObj.stop = true;
186 }
187 );
188
189 gcd.on("directive found", "process directives");
190
191 gcd.action("process directives", function (data, evObj) {
192 var gcd = evObj.emitter;
193 var file = evObj.pieces[0];
194 var doc = gcd.parent.docs[file];
195 var fun;
196 var directive = evObj.pieces[1];
197 if (directive && (fun = doc.directives[directive] ) ) {
198 fun.call(doc, data);
199 }
200 }
201 );
202
203 gcd.on("parsing done", "list blocks to compile");
204
205 gcd.action("list blocks to compile", function (data, evObj) {
206 var gcd = evObj.emitter;
207 var file = evObj.pieces[0];
208 var doc = gcd.parent.docs[file];
209 var blocks = doc.blocks;
210 var name;
211 for (name in blocks) {
212 gcd.emit("block needs compiling:" + file + ":" + name);
213 }
214 }
215 );
216
217 gcd.on("waiting for", "wait reporting");
218
219 gcd.action("wait reporting", function (data, evObj) {
220 var gcd = evObj.emitter;
221
222 var reports = gcd.parent.reports;
223
224 var evt = data[0];
225 var msg = evObj.pieces.slice(0,-1).reverse().join(":");
226
227
228 reports[msg] = data.slice(1);
229 gcd.once(evt, function () {
230 delete reports[msg];
231 });
232 }
233 );
234
235 if (actions) {
236 apply(gcd, actions);
237 }
238
239 Folder.postInit(this);
240
241 return this;
242};
243
244Folder.prototype.parse = function (doc) {
245 var gcd = doc.gcd;
246 var file = doc.file;
247
248 gcd.when("marked done:"+file, "parsing done:"+file);
249
250 gcd.on("parsing done:"+file, function () {
251 doc.parsed = true;
252 });
253
254
255 var reader = new commonmark.Parser();
256 var parsed = reader.parse(doc.text);
257
258 var walker = parsed.walker();
259 var event, node, entering, htext = false, ltext = false, lang, code;
260 var ind, pipes, middle, title, href, directive; //for links
261
262 while ((event = walker.next())) {
263 node = event.node;
264 entering = event.entering;
265
266 switch (node.type) {
267 case "Text" :
268 if (htext) {
269 htext.push(node.literal);
270 }
271 if (ltext) {
272 ltext.push(node.literal);
273 }
274 break;
275 case "Link" :
276 if (entering) {
277 ltext = [];
278 } else {
279 href = node.destination;
280 title = node.title;
281 ltext = ltext.join('').trim();
282
283 if (title) {
284 title = title.replace(/&#39;/g, "'").replace(/&quot;/g, '"');
285 }
286 if ((!href) && (!title)) {
287 gcd.emit("switch found:"+file, [ltext, ""]);
288 } else if (title[0] === ":") {
289 if ((ltext.indexOf("|") !== -1) || ( ltext.length === 0) ) {
290 gcd.emit("directive found:transform:" + file,
291 { link : ltext,
292 input : title.slice(1),
293 href: href,
294 cur: doc.curname,
295 directive : "transform"
296 }
297 );
298 } else {
299 ind = 0;
300 pipes = title.indexOf("|");
301 if (pipes === -1) {
302 middle = title.slice(ind+1).trim();
303 pipes = '';
304 } else {
305 middle = title.slice(ind+1, pipes).trim();
306 pipes = title.slice(pipes+1).trim();
307 }
308 if (middle) {
309 ltext += "." + middle.toLowerCase();
310 }
311 gcd.emit("switch found:" + file, [ltext,pipes]);
312 }
313 } else if ( (ind = title.indexOf(":")) !== -1) {
314 directive = title.slice(0,ind).trim().toLowerCase();
315 gcd.emit("directive found:" +
316 directive + ":" + file,
317 { link : ltext,
318 input : title.slice(ind+1),
319 href: href,
320 cur: doc.curname,
321 directive : directive
322 }
323 );
324 }
325 ltext = false;
326 }
327 break;
328 case "CodeBlock" :
329 lang = node.info;
330 code = node.literal || '';
331 if (code[code.length -1] === "\n") {
332 code = code.slice(0,-1);
333 }
334 if (lang) {
335 gcd.emit("code block found:"+lang+":"+file, code);
336 } else {
337 gcd.emit("code block found:"+ file, code);
338 }
339 break;
340 case "Header" :
341 if (entering) {
342 htext = [];
343 } else {
344 gcd.emit("heading found:"+node.level+":"+file, htext.join(""));
345 htext = false;
346 }
347 break;
348 }
349
350 // console.log(node.type, node.literal || '', node.destination|| '', node.title|| '', node.info|| '', node.level|| '', node.sourcepos, event.entering);
351 }
352
353 gcd.emit("marked done:" + file);
354};
355
356Folder.prototype.newdoc = function (name, text, actions) {
357 var parent = this;
358
359 var doc = new parent.Doc(name, text, parent, actions);
360
361 try {
362 parent.parse(doc);
363 } catch (e) {
364 doc.log("Markdown parsing error. Last heading seen: " +
365 doc.curname);
366 }
367
368 return doc;
369
370};
371
372Folder.prototype.colon = { v : "\u2AF6",
373 escape : function (text) {
374 return (typeof text === "string") ?
375 text.replace(/:/g, "\u2AF6") : text;
376 },
377 restore : function (text) {
378 return (typeof text === "string") ?
379 text.replace( /[\u2AF6]/g, ":") : text;
380 }
381};
382
383Folder.prototype.createScope = function (name) {
384 var folder = this;
385 var gcd = folder.gcd;
386 var scopes = folder.scopes;
387 var colon = folder.colon;
388
389 name = colon.escape(name);
390
391 if (! scopes.hasOwnProperty(name) ) {
392 scopes[name] = {};
393 gcd.emit("scope created:" + name);
394 gcd.emit("scope exists:" + name);
395 } else if (typeof scopes[name] === "string") {
396 gcd.emit("error:conflict in scope naming:" + name);
397 scopes[name] = {};
398 }
399
400 return scopes[name];
401};
402
403Folder.prototype.join = "\n";
404
405Folder.prototype.log = function (text) { console.log(text); };
406
407Folder.prototype.indicator = "\u2AF6\u2AF6\u2AF6";
408
409var sync = Folder.prototype.wrapSync = function (fun, label) {
410 var temp;
411 if (typeof fun === "string") {
412 temp = fun;
413 fun = label;
414 label = fun;
415 }
416
417 var f = function (input, args, name, command) {
418 var doc = this;
419 var gcd = doc.gcd;
420
421 try {
422 args = doc.argsPrep(args, name, doc.subCommands, command);
423 var out = fun.call(doc, input, args, name);
424 gcd.scope(name, null); // wipes out scope for args
425 gcd.emit("text ready:" + name, out);
426 } catch (e) {
427 doc.log(e);
428 gcd.emit("error:command execution:" + name,
429 [e, e.stack, input, args, command]);
430 }
431 };
432
433 if (label) {
434 f._label = label;
435 }
436
437 return f;
438};
439Folder.sync = function (name, fun) {
440 return (Folder.commands[name] = sync(name, fun));
441};
442
443var async = Folder.prototype.wrapAsync = function (fun, label) {
444 var temp;
445 if (typeof fun === "string") {
446 temp = fun;
447 fun = label;
448 label = fun;
449 }
450 var f = function (input, args, name, command) {
451
452 var doc = this;
453 var gcd = doc.gcd;
454
455 var callback = function (err, data) {
456 if (err) {
457 doc.log(err);
458 gcd.emit("error:command execution:" + name,
459 [err, input, args, command]);
460 } else {
461 gcd.scope(name, null); // wipes out scope for args
462 gcd.emit("text ready:" + name, data);
463 }
464 };
465 args = doc.argsPrep(args, name, doc.subCommands, command);
466 fun.call(doc, input, args, callback, name);
467 };
468 if (label) {
469 f._label = label;
470 }
471
472 return f;
473};
474Folder.async = function (name, fun) {
475 return (Folder.commands[name] = async(name, fun));
476};
477
478var dirFactory = Folder.prototype.dirFactory = function (namefactory, handlerfactory, other) {
479
480 return function (state) {
481 var doc = this;
482 var gcd = doc.gcd;
483 var colon = doc.colon;
484
485 state.linkname = colon.escape(state.link);
486 var temp;
487
488 state.start = doc.getBlock(state.href, state.cur);
489
490 temp = doc.midPipes(state.input);
491 state.options = temp[0];
492 state.pipes = temp[1];
493
494 namefactory.call(doc, state);
495
496 handlerfactory.call(doc, state);
497
498 other.call(doc, state);
499
500 doc.pipeDirSetup(state.pipes, state.emitname, state.handler,
501 ( state.start || state.block) );
502
503 var pipeEmitStart = "text ready:" + state.emitname + colon.v + "sp";
504 if (! state.value) {
505 doc.retrieve(state.start, pipeEmitStart);
506 } else {
507 gcd.emit(pipeEmitStart, state.value || "" );
508 }
509 };
510
511
512};
513
514// communication between folders, say for caching read in files
515Folder.fcd = new EvW();
516
517
518Folder.prototype.subnameTransform = function (subname, lname, mainblock) {
519 var colind, first, second, main;
520 var doc = this;
521 var colon = doc.colon;
522
523 if (subname[0] === ":") {
524 if (mainblock) {
525 //console.log(mainblock)
526 } else {
527 colind = lname.indexOf(":");
528 mainblock = lname.slice(colind+1, lname.indexOf(colon.v, colind));
529 }
530 if (subname === ":") {
531 subname = mainblock;
532 } else {
533 subname = mainblock + subname;
534 }
535 return subname;
536 }
537
538 if (subname.slice(0, 6) === "../../" ) {
539 //in a/b/c asking for a
540 if (mainblock) {
541 //console.log(mainblock)
542 } else {
543 colind = lname.indexOf(":");
544 mainblock = lname.slice(colind+1, lname.indexOf(colon.v, colind));
545 }
546 main = mainblock.slice(0, mainblock.indexOf("/"));
547 if ((subname.length > 6) && (subname[6] !== ":") ) {
548 subname = main + "/" + subname.slice(6);
549 } else {
550 subname = main + subname.slice(6);
551 }
552 } else if (subname.slice(0,2) === "./" ) {
553 // in a/b asking for a/b/c using ./c
554 if (mainblock) {
555 //console.log(mainblock)
556 } else {
557 colind = lname.indexOf(":");
558 mainblock = lname.slice(colind+1, lname.indexOf(colon.v, colind));
559 }
560 if (subname[2] === ":" ) {
561 subname = mainblock + subname.slice(2);
562 } else {
563 subname = mainblock + "/" + subname.slice(2);
564 }
565 } else if (subname.slice(0,3) === "../") {
566 //either in a/b or in a/b/c and asking for a or a/b, respectively
567 if (mainblock) {
568 //console.log(mainblock)
569 } else {
570 colind = lname.indexOf(":");
571 mainblock = lname.slice(colind+1, lname.indexOf(colon.v, colind));
572 }
573 first = mainblock.indexOf("/");
574 second = mainblock.indexOf("/", first+1);
575
576 if (second !== -1) {
577 // a/b/c case
578 main = mainblock.slice(0, second);
579 } else {
580 main = mainblock.slice(0, first);
581 }
582
583 if ((subname.length > 3) && (subname[3] !== ":") ) {
584 subname = main + "/" + subname.slice(3);
585 } else {
586 subname = main + subname.slice(3);
587 }
588
589 }
590
591
592 return subname;
593
594};
595
596Folder.postInit = function () {}; //a hook for plugin this modification
597Folder.plugins = {};
598Folder.plugins.npminfo = {
599 deps : { val : function (arr) {return arr.join(",\n");},
600 element : function (str) {
601 var pieces;
602
603 if (str) {
604 pieces = str.trim().split(/\s+/);
605 if (pieces.length === 2) {
606 return '"' + pieces[0].trim() + '"' + " : " + '"^' +
607 pieces[1].trim() + '"';
608 }
609 }
610 },
611 save : "npm dependencies"
612 },
613 dev : { val : function (arr) {return arr.join(",\n");},
614 element : function (str) {
615 var pieces;
616
617 if (str) {
618 pieces = str.trim().split(/\s+/);
619 if (pieces.length === 2) {
620 return '"' + pieces[0].trim() + '"' + " : " + '"^' +
621 pieces[1].trim() + '"';
622 }
623 }
624 },
625 save : "npm dev dependencies"
626 }
627};
628
629Folder.reporters = {
630 save : function (args) {
631 var name = this.recording[args[2]] || args[2];
632 return "NOT SAVED: " + args[0] + " AS REQUESTED BY: " + args[1] +
633 " NEED: " + name;
634 },
635 out : function (args) {
636 var name = this.recording[args[2]] || args[2];
637 return "NOT REPORTED OUT: " + args[0] + " AS REQUESTED BY: " + args[1] +
638 "\nNEED: " + name;
639 },
640 "command defined" : function (args) {
641 var name = this.recording[args[2]] || args[2];
642 return "COMMAND NOT DEFINED: " + args[0] + " AS REQUESTED IN: " + args[1] +
643 "\nNEED: " + name;
644 },
645 "scope exists" : function (data) {
646 if (data[3]) {
647 return "NEED SCOPE: " + data[0] + " FOR RETRIEVING: " + data[2] +
648 " IN FILE: " + data[1];
649 } else {
650 return "NEED SCOPE: " + data[0] + " FOR SAVING: " + data[2] +
651 " IN FILE: " + data[1];
652 }
653 },
654 "text" : function (data) {
655 var hint = this.recording[data[0]];
656 var parts = data[0].split(":").reverse();
657 var block = parts[0].split(this.colon.v)[0];
658 if (hint) {
659 return "PROBLEM WITH: " + hint + " IN: " + block +
660 " FIlE: " + parts[1];
661 }
662
663 },
664 "minor" : function (data) {
665 var hint = this.recording[data[0]];
666 var parts = data[0].split(":").reverse();
667 var block = parts[0].split(this.colon.v)[0];
668 if (hint) {
669 return "PROBLEM WITH: " + hint + " IN: " + block +
670 " FIlE: " + parts[1];
671 }
672
673 },
674 "retrieval" : function (data) {
675 return "NEED VAR: " + data[1] + " FROM: " + data[0];
676 },
677 "cmd" : function (data) {
678 var ind = data[1].lastIndexOf(this.colon.v);
679 if (ind === -1) {
680 ind = data[1].length;
681 }
682 var name = data[1].slice(0, ind);
683 var hint = this.recording[name];
684 return "NEED COMMAND: " + data[0] + " FOR: " + hint;
685 }
686
687};
688
689
690Folder.prototype.reportwaits = function () {
691 var report = this.reports;
692 var reporters = this.reporters;
693 var arr, msg, data, temp;
694
695 arr = [];
696
697 for (msg in report) {
698 data = report[msg];
699 if (reporters.hasOwnProperty(data[0]) ) {
700 temp = reporters[data[0]].call(this, data.slice(1) );
701 if (temp) {
702 arr.push(temp);
703 } else {
704 // console.log(msg, data);
705 }
706 }
707 }
708
709 return arr;
710};
711
712Folder.prototype.simpleReport = function () {
713 var folder = this;
714 var recording = folder.recording;
715 var gcd = this.gcd;
716 var key, lname, ret = [], el, pieces;
717 var v = this.colon.v;
718 for (key in gcd.whens) {
719 if (key.slice(0,15) === "stitch fragment") {
720 lname = key.slice(16);
721 ret.push("PROBLEM WITH: " + recording[lname] +
722 " IN: " + lname.slice(lname.indexOf(":")+1,
723 lname.indexOf(v) ) +
724 " FILE: " + lname.slice(0, lname.indexOf(":")));
725 }
726 }
727 for (key in gcd._onces) {
728 el = gcd._onces[key];
729 if ( el[0].slice(0, 15) === "command defined") {
730 pieces = key.split(":");
731 if (pieces.length < 3) {
732 gcd.error("error:simple report:"+ el[1]);
733 return ret;
734 }
735 ret.push("COMMAND REQUESTED: " +
736 pieces[1] +
737 " BUT NOT DEFINED. REQUIRED IN: " +
738 pieces[3].slice(0, pieces[3].indexOf(v)) +
739 " FILE: " + pieces[2] );
740 }
741 }
742 return ret;
743};
744
745Folder.commands = { eval : sync(function ( text, args ) {
746 var doc = this;
747
748 var code = args.shift();
749
750 try {
751 eval(code);
752 return text.toString();
753 } catch (e) {
754 doc.gcd.emit("error:command:eval:", [e, e.stack, code, text]);
755 return e.name + ":" + e.message +"\n" + code + "\n\nACTING ON:\n" +
756 text;
757 }
758}, "eval"),
759 passthru : sync(function (text) {return text;}, "passthru"),
760 sub : function (str, args, name) {
761 var doc = this;
762 var gcd = this.gcd;
763
764 var index = 0, al = args.length, k, keys, obj = {},
765 i, j, old, newstr, indented;
766
767 for (i = 0; i < al; i +=2) {
768 obj[args[i]] = args[i+1];
769 }
770
771 keys = Object.keys(obj).sort(function (a, b) {
772 if ( a.length > b.length) {
773 return -1;
774 } else if (a.length < b.length) {
775 return 1;
776 } else {
777 return 0;
778 } });
779
780
781 k = keys.length;
782 for (j = 0; j < k; j += 1) {
783 index = 0;
784 old = keys[j];
785 newstr = obj.hasOwnProperty(keys[j]) ? obj[keys[j]] : '';
786 while (index < str.length ) {
787 i = str.indexOf(old, index);
788
789 if (i === -1) {
790 break;
791 } else {
792 indented = doc.indent(newstr, doc.getIndent(str, i));
793 str = str.slice(0,i) + indented + str.slice(i+old.length);
794 index = i + indented.length;
795 }
796 }
797 }
798
799 gcd.emit("text ready:" + name, str);
800 },
801 store: sync(function (input, args) {
802 var doc = this;
803
804 var vname = doc.colon.escape(args[0]);
805
806 if (vname) {
807 doc.store(vname, input);
808 }
809 return input;
810 }, "store"),
811 log : sync(function (input, args) {
812 var doc = this;
813 if (args && args.length) {
814 doc.log(input + "\n~~~\n" + args.join("\n~~~\n"));
815 } else {
816 doc.log(input);
817 }
818 return input;
819 }, "log"),
820 async : async(function (text, args, callback) {
821 var doc = this;
822
823 var code = args.shift();
824
825 try {
826 eval(code);
827 } catch (e) {
828 doc.gcd.emit("error:command:async:", [e, e.stack, code, text]);
829 callback( null, e.name + ":" + e.message +"\n" + code +
830 "\n\nACTING ON:\n" + text);
831 }
832 }, "async"),
833 compile : function (input, args, name) {
834 var doc = this;
835 var gcd = doc.gcd;
836 var file = doc.file;
837 var colon = doc.colon.v;
838 var escape = doc.colon.escape;
839 var i, n, start, nextname, oldname, firstname;
840
841 var stripped = name.slice(name.indexOf(":")+1) + colon + "c";
842
843
844 var hanMaker = function (file, nextname, start) {
845 return function (text) {
846 doc.blockCompiling(text, file, nextname, start);
847 };
848 };
849
850
851 if (args.length === 0) {
852 gcd.once("minor ready:" + name + colon + "c", function (text) {
853 gcd.emit("text ready:" + name, text);
854 });
855 doc.blockCompiling(input, file, stripped);
856 } else {
857 n = args.length;
858 firstname = oldname = escape(stripped + colon + ( args[0] || '') + colon + 0);
859 for (i = 1; i < n; i += 1) {
860 start = args[i] || '';
861 nextname = escape(stripped + colon + start + colon + i);
862 gcd.once("minor ready:" + file + ":" + oldname, hanMaker(file,
863 nextname, start) );
864 gcd.emit("waiting for:cmd compiling:" + nextname + ":from:" + doc.file,
865 ["minor ready:" + file + ":" + nextname, "compile", nextname, doc.file, start]);
866 oldname = nextname;
867 }
868 start = args[0] || '';
869 gcd.once("minor ready:" + file + ":" + oldname, function (text) {
870 gcd.emit("text ready:" + name, text);
871 });
872 doc.blockCompiling(input, file, firstname, start);
873 }
874 },
875 raw : sync(function (input, args) {
876 var doc = this;
877 var start, end, text;
878 var gcd = doc.gcd;
879
880 var file = doc.parent.docs[args[2]] || doc;
881
882 if (file) {
883 text = file.text;
884 start = args[0].trim() + "\n";
885 start = text.indexOf(start)+start.length;
886 end = "\n" + args[1].trim();
887 end = text.indexOf(args[1], start);
888 return text.slice(start, end);
889 } else {
890 gcd.emit("error:raw:" + doc.file, args);
891 return '';
892 }
893
894
895 }, "raw"),
896 trim : sync(function (input) {
897 return input.trim();
898 }, "trim"),
899 join : sync(function (input, args) {
900 var sep = args.shift() || '';
901 if (input) {
902 args.unshift(input);
903 }
904 return args.join(sep);
905 }, "join"),
906 cat : sync(function (input, args) {
907 var sep = '';
908 if (input) {
909 args.unshift(input);
910 }
911 return args.join(sep);
912 }, "cat"),
913 push : sync(function (input, args, name) {
914 var folder = this.parent;
915 var stack = folder.stack;
916 var cmdpipe = name.slice(0, name.lastIndexOf(folder.colon.v));
917 if (stack.hasOwnProperty(cmdpipe)) {
918 stack[cmdpipe].push(input);
919 } else {
920 stack[cmdpipe] = [input];
921 }
922 return input;
923 }, "push"),
924 pop : sync(function (input, args, name) {
925 var gcd = this.gcd;
926 var folder = this.parent;
927 var stack = folder.stack;
928 var cmdpipe = name.slice(0, name.lastIndexOf(folder.colon.v));
929 var ret;
930 if (stack.hasOwnProperty(cmdpipe)) {
931 ret = stack[cmdpipe].pop();
932 if (stack[cmdpipe].length === 0) {
933 delete stack[cmdpipe];
934 }
935 } else {
936 gcd.emit("error:pop found nothing to pop:"+name);
937 ret = input;
938 }
939 return ret;
940 }, "pop"),
941 "if" : function (input, args, name) {
942 var doc = this;
943 var gcd = doc.gcd;
944 var flag = args[0];
945
946 if (doc.parent.flags.hasOwnProperty(flag) ) {
947 doc.commands[args[1]].call(doc, input, args.slice(2), name);
948 } else {
949 gcd.emit("text ready:" + name, input);
950 }
951
952
953 },
954 "done" : function (input, args, name) {
955 var gcd = this.gcd;
956 this.parent.done.gcd.emit(args[0]);
957 gcd.emit("text ready:" + name, input);
958 },
959 "when" : function (input, args, name) {
960 var folder = this.parent;
961 var gcd = this.gcd;
962 var done = folder.done;
963 var cache = done.cache;
964 var when = [];
965
966 var i, n = args.length;
967 for (i = 0; i < n; i +=1) {
968 if (! cache[args[i]]) {
969 when.push(args[i]);
970 }
971 }
972 if (when.length > 0) {
973 done.gcd.once("ready to send:" + name, function () {
974 gcd.emit("text ready:" + name, input);
975 });
976 done.gcd.when(when, "ready to send:" + name);
977 } else {
978 gcd.emit("text ready:" + name, input);
979 }
980 }
981};
982
983Folder.directives = {
984 "save" : dirFactory(function (state) {
985 state.emitname = "for save:" + this.file + ":" + state.linkname;
986 }, function (state) {
987 var doc = this;
988 var gcd = this.gcd;
989 var linkname = state.linkname;
990
991 var f = function (data) {
992 if (data[data.length-1] !== "\n") {
993 data += "\n";
994 }
995 gcd.emit("file ready:" + linkname, data);
996 };
997 f._label = "save;;" + linkname;
998
999 state.handler = f;
1000
1001 }, function (state) {
1002 var file = this.file;
1003 var gcd = this.gcd;
1004 var linkname = state.linkname;
1005 var options = state.options;
1006 var start = state.start;
1007 // es6 var {linkname, options, start} = state;
1008
1009 gcd.scope(linkname, options);
1010
1011 gcd.emit("waiting for:saving file:" + linkname + ":from:" + file,
1012 ["file ready:" + linkname, "save", linkname, file, start]);
1013
1014 }),
1015 "new scope" : function (args) {
1016 var doc = this;
1017 var scopename = args.link;
1018
1019 doc.parent.createScope(scopename);
1020
1021 },
1022 "store" : dirFactory(function (state) {
1023 var linkname = state.linkname;
1024
1025 state.emitname = "for store:" + this.file + ":" + linkname;
1026 }, function (state) {
1027 var doc = this;
1028 var gcd = this.gcd;
1029 var linkname = state.linkname;
1030
1031 var f = function (data) {
1032 doc.store(linkname, data);
1033 };
1034 f._label = "storeDir;;" + linkname;
1035
1036 state.handler = f;
1037
1038 }, function (state) {
1039
1040
1041 if (state.options) {
1042 state.block = state.start;
1043 state.start = '';
1044 state.value = state.options;
1045 }
1046 }),
1047 "log" : function (args) {
1048
1049 var doc = this;
1050 var gcd = doc.gcd;
1051
1052 var str = args.link;
1053 var i;
1054 while ( (i = str.indexOf("\\:") ) !== -1 ) {
1055 str = str.slice(0, i) + doc.colon.v + str.slice(i+2);
1056 }
1057
1058 str = str || doc.colon.escape(args.cur);
1059
1060 gcd.monitor(str, function (ev, data) {
1061 doc.log("EVENT: " + ev + " DATA: " + data);
1062 });
1063
1064 },
1065 "out" : dirFactory(function (state) {
1066 state.emitname = "for out:" + this.file + ":" + this.colon.escape(state.linkname);
1067 }, function (state) {
1068 var doc = this;
1069 var gcd = doc.gcd;
1070 var linkname = state.linkname;
1071 var emitname = state.emitname;
1072
1073
1074 var f = function (data) {
1075 gcd.emit(emitname, data);
1076 doc.log(linkname + ":\n" + data + "\n~~~\n");
1077 };
1078
1079 f._label = "out;;" + linkname;
1080
1081 state.handler = f;
1082
1083 }, function (state) {
1084 var gcd = this.gcd;
1085 var linkname = state.linkname;
1086 var emitname = state.emitname;
1087 var start = state.start;
1088 var options = state.options;
1089
1090 gcd.scope(linkname, options);
1091
1092 gcd.emit("waiting for:dumping out:" + linkname,
1093 [emitname, linkname, this.file, start] );
1094 }),
1095 "load" : function (args) {
1096 var doc = this;
1097 var gcd = doc.gcd;
1098 var folder = doc.parent;
1099 var url = args.href.trim();
1100 var options = args.input;
1101 var urlesc = folder.colon.escape(url);
1102 var nickname = doc.colon.escape(args.link.trim());
1103
1104 gcd.scope(urlesc, options);
1105
1106 if (nickname && (nickname !== urlesc) ) {
1107 doc.createLinkedScope(urlesc, nickname);
1108 if (!(folder.docs.hasOwnProperty(urlesc) ) ) {
1109 gcd.emit("waiting for:loading for:" + doc.file,
1110 "need document:" + urlesc);
1111 gcd.emit("need document:" + urlesc, url );
1112 }
1113 } else {
1114 if (!(folder.docs.hasOwnProperty(urlesc) ) ) {
1115 gcd.emit("waiting for:loading for:" + doc.file,
1116 "need document:" + urlesc);
1117 gcd.emit("need document:" + urlesc, url );
1118 }
1119 }
1120
1121 },
1122 "link scope" : function (args) {
1123 var doc = this;
1124 var alias = args.link;
1125 var scopename = args.input;
1126
1127 doc.createLinkedScope(scopename, alias);
1128
1129 },
1130 "transform" : dirFactory(function (state) {
1131 state.name = this.colon.escape(state.start + ":" + state.input);
1132 state.emitname = "for transform:" + this.file + ":" + state.name;
1133 }, function (state) {
1134 var doc = this;
1135 var gcd = doc.gcd;
1136
1137
1138 var f = function (data) {
1139 gcd.emit(state.emitname, data);
1140 var name = doc.getPostPipeName(state.linkname);
1141 if (name) {
1142 doc.store(name, data);
1143 }
1144 };
1145 f._label = "transform;;" + state.name;
1146
1147 state.handler = f;
1148 }, function (state) {
1149 var doc = this;
1150 var gcd = this.gcd;
1151 var name = state.name;
1152 var start = state.start;
1153 var emitname = state.emitname;
1154
1155 gcd.emit("waiting for:transforming:" + name,
1156 [emitname, name, doc.file, start] );
1157 }),
1158 "define" : dirFactory(function (state) {
1159 state.emitname = "cmddefine:" + state.linkname;
1160 }, function (state) {
1161 var cmdname = state.linkname;
1162 var doc = this;
1163 var gcd = this.gcd;
1164
1165 var han = function (block) {
1166 var f;
1167
1168 try {
1169 block = "f="+block;
1170 eval( block);
1171 } catch (e) {
1172 doc.gcd.emit("error:define:"+cmdname, [e, block]);
1173 doc.log(e.name + ":" + e.message +"\n" + block);
1174 return;
1175 }
1176
1177 switch (state.options) {
1178 case "raw" : f._label = cmdname;
1179 doc.commands[cmdname] = f;
1180 break;
1181 case "async" : doc.commands[cmdname] =
1182 doc.wrapAsync(f, cmdname);
1183 break;
1184 default : doc.commands[cmdname] =
1185 doc.wrapSync(f, cmdname);
1186 }
1187
1188 gcd.emit("command defined:" + cmdname);
1189 };
1190 han._label = "cmd define;;" + cmdname;
1191
1192 state.handler = han;
1193
1194 }, function (state) {
1195 var cmdname = state.linkname;
1196
1197 var file = this.file;
1198 var gcd = this.gcd;
1199
1200 gcd.emit("waiting for:command definition:" + cmdname,
1201 ["command defined:"+cmdname, cmdname, file, state.start] );
1202
1203 }),
1204 "subcommand" : function (args) {
1205 var doc = this;
1206
1207 var block = doc.blocks[args.cur];
1208
1209 var subCommandName = args.link;
1210
1211 var cmdName = args.href.trim().slice(1);
1212
1213 var f;
1214
1215 try {
1216 block = "f="+block;
1217 eval( block);
1218 } catch (e) {
1219 doc.gcd.emit("error:subcommand define:"+subCommandName, [e, block]);
1220 doc.log(e.name + ":" + e.message +"\n" + block);
1221 return;
1222 }
1223
1224 doc.defSubCommand( subCommandName, f, cmdName);
1225
1226 },
1227 "block" : function (args) {
1228 var doc = this;
1229
1230 if (args.link === "off") {
1231 doc.blockOff += 1;
1232 } else if (args.link === "on") {
1233 if (doc.blockOff > 0 ) {
1234 doc.blockOff -= 1;
1235 }
1236 } else {
1237 doc.log("block directive found, but the toggle was not understood: " +
1238 args.link + " It should be either on or off");
1239 }
1240
1241 },
1242 "ignore" : function (args) {
1243 var lang = args.link;
1244
1245 var doc = this;
1246 var gcd = doc.gcd;
1247
1248 gcd.on("code block found:" + lang, "ignore code block");
1249
1250 },
1251 "eval" : function (args) {
1252 var doc = this;
1253
1254 var block = doc.blocks[args.cur];
1255
1256 var storageName = doc.getPostPipeName(args.link);
1257 var ret = '';
1258
1259 try {
1260 eval(block);
1261 if (storageName) {
1262 doc.store(storageName, ret);
1263 }
1264 } catch (e) {
1265 doc.gcd.emit("error:dir eval:", [e, block]);
1266 doc.log(e.name + ":" + e.message +"\n" + block);
1267 return;
1268 }
1269
1270 },
1271 "if" : function (args) {
1272
1273 var doc = this;
1274 var folder = doc.parent;
1275 var gcd = doc.gcd;
1276
1277 var title = args.input;
1278 var ind = title.indexOf(";");
1279 var flag = title.slice(0, ind).trim();
1280 var directive, semi, fun;
1281
1282 if (folder.flags.hasOwnProperty(flag) ) {
1283 semi = title.indexOf(":", ind);
1284 directive = title.slice(ind+1, semi).trim();
1285 args.directive = directive;
1286 args.input = title.slice(semi+1).trim();
1287
1288 if (directive && (fun = doc.directives[directive] ) ) {
1289 fun.call(doc, args);
1290 }
1291 }
1292 },
1293 "flag" : function (args) {
1294 this.parent.flags[args.link.trim()] = true;
1295
1296 },
1297 "version" : function (args) {
1298 var doc = this;
1299 var colon = doc.colon;
1300
1301 var ind = args.input.indexOf(";");
1302 if (ind === -1) { ind = args.input.length +1; }
1303
1304 doc.store(colon.escape("g::docname"),
1305 args.link.trim());
1306 doc.store(colon.escape("g::docversion"),
1307 args.input.slice(0, ind).trim());
1308 doc.store(colon.escape("g::tagline"),
1309 (args.input.slice(ind+1).trim() || "Tagline needed" ) );
1310
1311 },
1312 "npminfo" : function self (args) {
1313 var doc = this;
1314 var g = "g" + doc.colon.v + doc.colon.v;
1315
1316 var types = doc.plugins.npminfo;
1317
1318 doc.store(g+"authorname", args.link);
1319
1320 var gituser = args.href.slice(args.href.lastIndexOf("/")+1).trim();
1321 doc.store(g+"gituser", gituser);
1322
1323 var pieces = args.input.split(";");
1324
1325 doc.store(g + "authoremail", (pieces.shift() || '').trim());
1326
1327 pieces.forEach(function (el) {
1328 if (!el) {return;}
1329
1330 var ret = [];
1331
1332 var ind = el.indexOf(":");
1333 var prekind = el.slice(0, ind).trim();
1334 var kind = types[prekind];
1335 if (!kind) {
1336 doc.log("unrecognized type in npminfo:" + prekind );
1337 return;
1338 }
1339 var entries = el.slice(ind+1).split(",");
1340 entries.forEach(function(el) {
1341 if (!el) {return;}
1342 var bits = kind.element(el);
1343 if (bits) {
1344 ret.push(bits);
1345 }
1346 });
1347 doc.store(g + kind.save, kind.val(ret) );
1348 });
1349
1350 doc.store(g + "year", ( new Date() ).getFullYear().toString() );
1351 },
1352};
1353
1354Folder.subCommands = (function () {
1355 var ret = {};
1356
1357 ret.echo = ret.e = function () {
1358 var arr = Array.prototype.slice.call(arguments);
1359
1360 var ret = arr.map(function (str) {
1361 if (("\"'`".indexOf(str[0]) !== -1) &&
1362 (str[0] === str[str.length-1]) ) {
1363
1364 return str.slice(1, -1);
1365 } else {
1366 return str;
1367 }
1368 });
1369
1370 ret.args = true;
1371
1372 return ret;
1373 };
1374
1375 ret.join = ret.j = function (sep) {
1376 var args = Array.prototype.slice.call(arguments, 1);
1377 var ret = [];
1378
1379 args.forEach( function (el) {
1380 if ( Array.isArray(el)) {
1381 ret.push(el.join(sep));
1382 } else {
1383 ret.push(el);
1384 }
1385 });
1386
1387 return ret.join(sep);
1388
1389 };
1390
1391 ret.array = ret.arr = ret.a = function () {
1392 return Array.prototype.slice.call(arguments, 0);
1393 };
1394
1395 ret.object = ret.obj = ret.o = function (str) {
1396 var ret, doc = this;
1397 try {
1398 ret = JSON.parse(str);
1399 if (Array.isArray(ret) ) {
1400 return ["val", ret];
1401 } else {
1402 return ret;
1403 }
1404 } catch (e) {
1405 doc.gcd.emit("error:arg prepping:bad json parse:" + this.cmdname,
1406 [e, e.stack, str]);
1407 return ["error", e];
1408 }
1409 };
1410
1411 ret.merge = function (a) {
1412 var ret, args;
1413 if (Array.isArray(a) ) {
1414 args = Array.prototype.slice.call(arguments, 1);
1415 return Array.prototype.concat.apply(a, args);
1416 } else {
1417 args = Array.prototype.slice.call(arguments, 1);
1418 ret = a;
1419 args.forEach( function (el) {
1420 var key;
1421 for (key in el) {
1422 ret[key] = el[key];
1423 }
1424 });
1425 return ret;
1426 }
1427 };
1428
1429 ret["key-value"] = ret.kv = function () {
1430 var ret = {};
1431 var i, n = arguments.length;
1432 for (i = 0; i < n; i += 2) {
1433 ret[arguments[i]] = arguments[i+1];
1434 }
1435
1436 return ret;
1437 };
1438
1439 ret.act = function (obj, method) {
1440 try {
1441 return obj[method].apply(obj,
1442 Array.prototype.slice.call(arguments, 2)) ;
1443 } catch (e) {
1444 this.gcd.emit("error:arg prepping:bad method:" + this.cmdname,
1445 [e, e.stack, obj, method,
1446 Array.prototype.slice.call(arguments)]);
1447 return ;
1448 }
1449 };
1450
1451 ret.property = ret.prop = function () {
1452 var props = Array.prototype.slice.call(arguments, 0);
1453 var obj;
1454 try {
1455 obj = props.reduce(function (prev, cur) {
1456 return prev[cur];
1457 });
1458 return obj;
1459 } catch (e) {
1460 this.gcd.emit("error:bad property access:" +
1461 this.cmdname, [e, e.stack, props]);
1462 return;
1463 }
1464 };
1465
1466 ret.json = function (obj) {
1467 var doc = this;
1468 try {
1469 return JSON.stringify(obj);
1470 } catch (e) {
1471 this.gcd.emit("error:arg prepping:bad json:" + this.cmdname,
1472 [e, e.stack, obj]);
1473 return ;
1474 }
1475 };
1476
1477 ret.set = function (obj, retType) {
1478 var doc = this;
1479 var gcd = doc.gcd;
1480 var name = doc.cmdName;
1481 var scope, key;
1482
1483 scope = gcd.scope(name);
1484 if (!scope) {
1485 scope = {};
1486 gcd.scope(name, scope);
1487 }
1488 for (key in obj) {
1489 scope[key] = obj[key];
1490 }
1491 if (retType === "pass" ) {
1492 return obj;
1493 } else {
1494 return ;
1495 }
1496 };
1497
1498 ret.gset = function (obj, retType) {
1499 var doc = this;
1500 var gcd = doc.gcd;
1501 var name = doc.cmdName.slice(0, doc.cmdName.lastIndexOf(doc.colon.v)) ;
1502 var scope, key;
1503
1504 scope = gcd.scope(name);
1505 if (!scope) {
1506 scope = {};
1507 gcd.scope(name, scope);
1508 }
1509 for (key in obj) {
1510 scope[key] = obj[key];
1511 }
1512 if (retType === "pass" ) {
1513 return obj;
1514 } else {
1515 return ;
1516 }
1517 } ;
1518
1519 ret.get = function () {
1520 var doc = this;
1521 var gcd = doc.gcd;
1522 var name = doc.cmdName;
1523 var scope;
1524
1525 scope = gcd.scope(name);
1526 if (!scope) {
1527 gcd.emit("error:arg prepping:no scope:" + name);
1528 return ;
1529 }
1530
1531 var i, n = arguments.length;
1532 var ret = [];
1533 for (i = 0; i < n; i +=1 ) {
1534 ret.push(scope[arguments[i]]);
1535 }
1536 ret.args = true; // each is separate
1537 return ret;
1538 };
1539
1540 ret.gget = function () {
1541 var doc = this;
1542 var gcd = doc.gcd;
1543 var name = doc.cmdName.slice(0, doc.cmdName.lastIndexOf(doc.colon.v)) ;
1544 var scope;
1545
1546 scope = gcd.scope(name);
1547 if (!scope) {
1548 gcd.emit("error:arg prepping:no scope:" + name);
1549 return ;
1550 }
1551
1552 var i, n = arguments.length;
1553 var ret = [];
1554 for (i = 0; i < n; i +=1 ) {
1555 ret.push(scope[arguments[i]]);
1556 }
1557 ret.args = true; // each is separate
1558 return ret;
1559 } ;
1560
1561 ret.arguments = ret.args = function (arr) {
1562 var ret = arr.slice(0); //make a shallow copy
1563 ret.args = true;
1564 return ret;
1565 };
1566
1567 ret.number = ret.n = ret["#"] = function () {
1568 var ret = [], i, n = arguments.length;
1569 for (i = 0; i < n; i += 1) {
1570 ret.push(Number(arguments[i]));
1571 }
1572 ret.args = true;
1573 return ret;
1574 };
1575
1576 ret.eval = function (code) {
1577 var ret, doc = this;
1578 var args = Array.prototype.slice.call(arguments, 1);
1579
1580 if ( (code[0] === "`" ) && (code[code.length-1] === code[0]) ) {
1581 code = code.slice(1, code.length-1);
1582 }
1583
1584 try {
1585 eval(code);
1586 return ret;
1587 } catch (e) {
1588 doc.gcd.emit("error:arg prepping:bad eval:" + doc.cmdname,
1589 [e, e.stack, code, args]);
1590 return;
1591 }
1592
1593 };
1594
1595 ret.log = function () {
1596 var doc = this, name = doc.cmdName;
1597 var args = Array.prototype.slice.call(arguments);
1598 doc.log("arguments in " + name + ":\n---\n" +
1599 args.join("\n~~~\n") + "\n---\n");
1600 return args;
1601 };
1602
1603 ret.true = ret.t = function () {return true;};
1604 ret.false = ret.f = function () {return false;};
1605 ret.null = function () {return null;};
1606 ret.doc = function () {return this;};
1607 ret.skip = function () {return ;};
1608
1609 return ret;
1610
1611})();
1612
1613Folder.defSubCommand =function (sub, f, cmd) {
1614 var subs, cmdplug, cmdsub;
1615
1616 if (cmd) {
1617 cmdplug = this.plugins[cmd];
1618 if (!cmdplug) {
1619 cmdplug = this.plugins[cmd] = {};
1620 }
1621 cmdsub = cmdplug.subCommands;
1622 if (!cmdsub) {
1623 cmdsub = cmdplug.subCommands = {};
1624 }
1625 cmdsub[sub] = f;
1626 } else {
1627 subs = this.subCommands;
1628 subs[sub] = f;
1629 }
1630};
1631
1632
1633var Doc = Folder.prototype.Doc = function (file, text, parent, actions) {
1634 this.parent = parent;
1635 var gcd = this.gcd = parent.gcd;
1636 this.Folder = Folder;
1637
1638 this.file = file; // globally unique name for this doc
1639
1640 parent.docs[file] = this;
1641
1642 this.text = text;
1643
1644 this.blockOff = 0;
1645
1646 this.levels = {};
1647 this.blocks = {'' : ''}; //an empty initial block in case of headless
1648 this.heading = this.curname = '';
1649 this.levels[0] = text;
1650 this.levels[1] = '';
1651 this.levels[2] = '';
1652
1653
1654 this.vars = parent.createScope(file);
1655
1656 this.commands = parent.commands;
1657 this.directives = parent.directives;
1658 this.subCommands = parent.subCommands;
1659 this.colon = Object.create(parent.colon);
1660 this.join = parent.join;
1661 this.log = this.parent.log;
1662 this.scopes = this.parent.scopes;
1663 this.subnameTransform = this.parent.subnameTransform;
1664 this.indicator = this.parent.indicator;
1665 this.wrapAsync = parent.wrapAsync;
1666 this.wrapSync = parent.wrapSync;
1667 this.sync = Folder.sync;
1668 this.async = Folder.async;
1669 this.defSubCommand = Folder.defSubCommand;
1670 this.dirFactory = parent.dirFactory;
1671 this.plugins = Object.create(parent.plugins);
1672
1673 if (actions) {
1674 apply(gcd, actions);
1675 }
1676
1677 return this;
1678
1679};
1680
1681var dp = Doc.prototype;
1682
1683dp.retrieve = function (name, cb) {
1684 var doc = this;
1685 var gcd = doc.gcd;
1686
1687 var scope = doc.getScope(name);
1688
1689
1690 var varname = scope[1];
1691 var file = scope[2];
1692 scope = scope[0];
1693 var f;
1694 if (scope) {
1695 if (scope.hasOwnProperty(varname) ) {
1696 if (typeof cb === "function") {
1697 cb(scope[varname]);
1698 } else if (typeof cb === "string") {
1699 gcd.emit(cb, scope[varname]);
1700 } else {
1701 gcd.emit("error:unrecognized callback type:" +
1702 doc.file + ":" + name, (typeof cb) );
1703 }
1704 return ;
1705 } else {
1706 gcd.emit("waiting for:retrieval:" + doc.file,
1707 ["text stored:" + file + ":" + varname, "retrieval", file, varname]);
1708 f = function () {
1709 doc.retrieve(name, cb);
1710 };
1711 f._label = "Retrieving:" + file + ":" + varname;
1712 gcd.once("text stored:" + file + ":" + varname, f);
1713 return ;
1714 }
1715 } else {
1716 gcd.emit("waiting for:retrieval:" + cb+ "need:" + name,
1717 ["scope exists:" + file, "scope exists", file, doc.file, varname,
1718 true]);
1719 f = function () {
1720 doc.retrieve(name, cb);
1721 };
1722 f._label = "Retrieving:" + doc.file + ":" + name;
1723 gcd.once("scope exists:" + file, f);
1724 return ;
1725 }
1726};
1727
1728dp.getScope = function (name) {
1729 var ind, scope, alias, scopename, varname;
1730 var doc = this;
1731 var colon = doc.colon;
1732 var folder = doc.parent;
1733
1734 if ( (ind = name.indexOf( colon.v + colon.v) ) !== -1 ) {
1735 alias = name.slice(0,ind);
1736 varname = name.slice(ind+2);
1737 scopename = doc.scopes[ alias ];
1738 if (typeof scopename === "string") {
1739 while ( typeof (scope = folder.scopes[scopename]) === "string") {
1740 scopename = scope;
1741 }
1742 if (scope) {
1743 return [scope, varname, scopename];
1744 } else { //this should never happen
1745 doc.gcd.emit("error:non-existent scope linked:" +
1746 alias, scopename);
1747 }
1748 } else if (scopename) { //object -- alias is scope's name
1749 return [scopename, varname, alias];
1750 } else { // not defined yet
1751 return [null, varname, alias];
1752 }
1753 } else { //doc's scope is being requested
1754 return [doc.vars, name, doc.file];
1755 }
1756};
1757
1758dp.createLinkedScope = function (name, alias) {
1759 var doc = this;
1760 var gcd = doc.gcd;
1761 var folder = doc.parent;
1762 var scopes = folder.scopes;
1763 var colon = doc.colon;
1764
1765 name = colon.escape(name);
1766 alias = colon.escape(alias);
1767
1768 if (scopes.hasOwnProperty(alias) ) {
1769 if (scopes[alias] !== name ) {
1770 gcd.emit("error:conflict in scope naming:" +
1771 doc.file, [alias, name] );
1772 }
1773 } else {
1774 if ( scopes.hasOwnProperty(name) ) {
1775 folder.scopes[alias] = name;
1776 gcd.emit("scope linked:" + doc.file + ":" + alias, name);
1777 gcd.emit("scope exists:" + alias);
1778 } else {
1779 gcd.once("scope exists:" + name, function () {
1780 folder.scopes[alias] = name;
1781 gcd.emit("scope linked:" + doc.file + ":" + alias, name);
1782 gcd.emit("scope exists:" + alias);
1783 });
1784 }
1785 }
1786
1787
1788};
1789
1790dp.indent = function (text, indent, gcd) {
1791 var line, ret;
1792 var i, n;
1793
1794 n = indent;
1795 line = '';
1796 for (i = 0; i <n; i += 1) {
1797 line += ' ';
1798 }
1799
1800 if (typeof text !== "string") {
1801 gcd.emit("error:indent does not see a text item", text);
1802 return ret;
1803 }
1804
1805 ret = text.replace(/\n/g, "\n"+line);
1806 return ret;
1807};
1808
1809dp.getIndent = function ( block, place ) {
1810 var first, backcount, indent, chr;
1811 first = place;
1812 backcount = place-1;
1813 indent = 0;
1814 while (true) {
1815 if ( (backcount < 0) || ( (chr = block[backcount]) === "\n" ) ) {
1816 indent = first - ( backcount + 1 );
1817 break;
1818 }
1819 if (chr.search(/\S/) === 0) {
1820 first = backcount;
1821 }
1822 backcount -= 1;
1823 }
1824 return indent;
1825};
1826
1827dp.blockCompiling = function (block, file, bname, mainblock) {
1828 var doc = this;
1829 var gcd = doc.gcd;
1830 var colon = doc.colon;
1831
1832 var quote, place, lname, slashcount, numstr, chr, indent;
1833 var name = file + ":" + bname;
1834 var ind = 0;
1835 var start = 0;
1836 var stitchfrag;
1837 var stitchend = "ready to stitch:" + name;
1838 gcd.when("block substitute parsing done:"+name, stitchend);
1839 var n = block.length;
1840
1841 gcd.once(stitchend, function (data) {
1842
1843 var text = '', insert, i, n = data.length;
1844 var indent = doc.indent;
1845
1846 for (i = 1; i < n; i += 1) {
1847 insert = data[i][1];
1848 if ( (i+1 < n) && ( data[i+1][0].slice(0,6) === "indent") ) {
1849 text += indent(insert, data[i+1][1], gcd);
1850 i += 1;
1851 } else {
1852 text += insert;
1853 }
1854
1855 }
1856
1857 if (bname.indexOf(colon.v) !== -1) {
1858 gcd.emit("minor ready:" + name, text);
1859 } else {
1860 doc.store(bname, text);
1861 gcd.emit("text ready:" + name, text);
1862 }
1863 });
1864
1865 var stitcher = function (start) {
1866 if (start < n) {
1867 stitchfrag = "stitch fragment:" + name + colon.v + start;
1868 gcd.when(stitchfrag, stitchend);
1869 }
1870 };
1871
1872 stitcher(0);
1873
1874 while (ind < n) {
1875 ind = block.indexOf("\u005F", ind);
1876 if (ind === -1) {
1877 gcd.emit(stitchfrag, block.slice(start) );
1878 break;
1879 } else {
1880 ind += 1;
1881
1882 if (block[ind].match(/['"`]/)) {
1883 quote = block[ind];
1884 } else {
1885 continue;
1886 }
1887
1888 place = ind-2;
1889 numstr = '0123456789';
1890 slashcount = '';
1891 chr = block[place];
1892 if (chr === '\\' ) { //escaped underscore; no escaping backslash!
1893 gcd.emit(stitchfrag, block.slice(start, place) + "\u005F" );
1894 start = ind;
1895 stitcher(start);
1896 continue;
1897 } else if ( numstr.indexOf(chr) !== -1 ) {
1898 slashcount += chr;
1899 place -= 1;
1900 chr = block[place];
1901 while ( numstr.indexOf(chr) !== -1 ) {
1902 slashcount += chr;
1903 place -= 1;
1904 chr = block[place];
1905 }
1906 if (chr === '\\') {
1907 slashcount = parseInt(slashcount, 10);
1908 if (slashcount > 0) {
1909 slashcount -= 1;
1910
1911 gcd.emit(stitchfrag, block.slice(start, place) + "\\" +
1912 slashcount + "\u005F");
1913
1914 stitcher(place);
1915 start = doc.findMatchQuote(block, quote, ind+1); //+1 to get past quote
1916 // yes this is supposed to be reversed (from quote to quote,
1917 // start is just beyond quote
1918 gcd.emit(stitchfrag, block.slice(ind, start));
1919
1920 stitcher(start);
1921 ind = start;
1922 continue;
1923 } else {
1924 gcd.emit(stitchfrag, block.slice(start, place));
1925 start = ind-1; // underscore
1926 stitcher(start-2); //to point to where the escape sequence
1927 }
1928 }
1929 }
1930
1931 place = ind-1;
1932 if (start !== place ) { // \0 could have happened
1933 gcd.emit(stitchfrag, block.slice(start, place));
1934 start = place; // underscore
1935 stitcher(start);
1936 }
1937 lname = name + colon.v + start;
1938 gcd.flatWhen("text ready:" + lname, stitchfrag);
1939
1940 if (place > 0) {
1941 indent = doc.getIndent(block, place);
1942 if ( indent > 0 ) {
1943 gcd.when("indent for prior:" + lname, stitchend);
1944 gcd.emit("indent for prior:" + lname, doc.getIndent(block, place));
1945 }
1946 }
1947
1948 start = doc.substituteParsing(block, ind+1, quote, lname,
1949 mainblock);
1950
1951 doc.parent.recording[lname] = block.slice(ind-1, start);
1952
1953 stitcher(start);
1954 ind = start ;
1955
1956 }
1957 }
1958
1959
1960 gcd.emit("block substitute parsing done:"+name);
1961};
1962
1963dp.substituteParsing = function (text, ind, quote, lname, mainblock ) {
1964
1965 var doc = this;
1966 var gcd = doc.gcd;
1967 var colon = doc.colon;
1968
1969 var match, subname, chr, subtext;
1970 var subreg = doc.regexs.subname[quote];
1971
1972 var doneEmit = "text ready:" + lname;
1973 var textEmit = doneEmit + colon.v + ind;
1974 var subEmit = "substitution chain done:" + lname;
1975
1976 gcd.when(textEmit, subEmit);
1977
1978 gcd.once(subEmit, function (data) {
1979 gcd.emit(doneEmit, data[data.length-1][1] || '');
1980 } );
1981
1982 subreg.lastIndex = ind;
1983
1984 match = subreg.exec(text);
1985 if (match) {
1986 ind = subreg.lastIndex;
1987 chr = match[2];
1988 subname = match[1].trim().toLowerCase();
1989 subname = doc.subnameTransform(subname, lname, mainblock);
1990 subname = colon.escape(subname);
1991 if (chr === "|") {
1992 ind = doc.pipeParsing(text, ind, quote, lname, mainblock, subEmit,
1993 textEmit );
1994 } else if (chr === quote) {
1995 // nothing to do; it should automatically work !!!
1996 } else {
1997 gcd.emit("failure in parsing:" + lname, ind);
1998 return ind;
1999 }
2000 } else {
2001 gcd.emit("failure in parsing:" + lname, ind);
2002 return ind;
2003 }
2004
2005
2006 if (subname === '') { // no incoming text, just commands acting
2007 gcd.emit(textEmit, '');
2008 } else {
2009 subtext = doc.retrieve(subname, textEmit);
2010 }
2011
2012 return ind;
2013
2014};
2015
2016dp.pipeParsing = function (text, ind, quote, name, mainblock, toEmit, textEmit) {
2017 var doc = this;
2018 var gcd = doc.gcd;
2019 var colon = doc.colon;
2020
2021 var incomingEmit = textEmit;
2022
2023 var chr, match, command,
2024 comname, start;
2025 var n = text.length;
2026 var comreg = doc.regexs.command[quote];
2027
2028
2029 while (ind < n) { // command processing loop
2030
2031 comreg.lastIndex = ind;
2032 start = ind;
2033
2034 match = comreg.exec(text);
2035 if (match) {
2036 command = match[1].trim().toLowerCase();
2037 chr = match[2];
2038 ind = comreg.lastIndex;
2039 if (command === '') {
2040 command = "passthru";
2041 }
2042 command = colon.escape(command);
2043 comname = name + colon.v + start;
2044
2045 gcd.once("arguments ready:" + comname,
2046 doc.argFinishingHandler(comname));
2047
2048 gcd.when([incomingEmit,
2049 "command parsed:" + comname ],
2050 "arguments ready:" + comname );
2051
2052 gcd.when("text ready:" + comname, toEmit);
2053
2054 incomingEmit = "text ready:" + comname;
2055
2056 if (chr === quote) {
2057 ind -= 1; // it is set to just after the last position
2058 gcd.emit("command parsed:" + comname,
2059 [doc.file, command, "text ready:" + comname ]);
2060 break;
2061
2062 } else if (chr === "|") {
2063 // nothing to do; just done.
2064 } else {
2065 ind = doc.argProcessing(text, ind, quote, comname, mainblock );
2066 }
2067
2068 } else {
2069 gcd.emit("error:command parsing:" + name + colon.v + ind);
2070 return ind+1;
2071 }
2072
2073 gcd.emit("command parsed:" + comname,
2074 [doc.file, command, "text ready:" + comname ]);
2075
2076 if (text[ind] === quote) {
2077 break;
2078 } else if (text[ind] === "|") {
2079 start = ind += 1;
2080 } else {
2081 gcd.emit("error:bad terminating character in command" +
2082 name, [start, ind, text[ind]]);
2083 }
2084
2085 }
2086
2087
2088 return ind+1;
2089
2090};
2091
2092dp.regexs = {
2093
2094 command : {
2095 "'" : /\s*([^|'\s]*)(.)/g,
2096 '"' : /\s*([^|"\s]*)(.)/g,
2097 "`" : /\s*([^|`\s]*)(.)/g
2098 },
2099 argument : {
2100 "'" : /\s*([^,|\\']*)(.)/g,
2101 '"' : /\s*([^,|\\"]*)(.)/g,
2102 "`" : /\s*([^,|\\`]*)(.)/g
2103 },
2104 endarg : {
2105 "'" : /\s*([,|\\'])/g,
2106 '"' : /\s*([,|\\"])/g,
2107 "`" : /\s*([,|\\`])/g
2108
2109 },
2110 subname : {
2111 "'" : /\s*([^|']*)(.)/g,
2112 '"' : /\s*([^|"]*)(.)/g,
2113 "`" : /\s*([^|`]*)(.)/g
2114 }
2115
2116
2117};
2118
2119dp.store = function (name, text) {
2120 var doc = this;
2121 var gcd = doc.gcd;
2122 var scope = doc.getScope(name);
2123
2124
2125 var f;
2126 if (! scope[0]) {
2127 gcd.emit("waiting for:storing:" + doc.file + ":" + name,
2128 ["scope exists:" + scope[2], "scope exists", scope[2],
2129 doc.file, scope[1] ]);
2130 f = function () {
2131 doc.store(name, text);
2132 };
2133 f._label = "Storing:" + doc.file + ":" + name;
2134 gcd.once("scope exists:" + scope[2], f);
2135 return;
2136 }
2137
2138 var varname = scope[1];
2139 var file = scope[2];
2140 scope = scope[0];
2141
2142 var old;
2143 if (scope.hasOwnProperty(varname) ) {
2144 old = scope[varname];
2145 scope[varname] = text;
2146 gcd.emit("overwriting existing var:" + file + ":" + varname,
2147 {oldtext:old, newtext: text} );
2148 } else {
2149 scope[varname] = text;
2150 }
2151 gcd.emit("text stored:" + file + ":" + varname, text);
2152};
2153
2154dp.getBlock = function (start, cur) {
2155 var doc = this;
2156 var colon = doc.colon;
2157
2158 if (typeof cur === "string") {
2159 cur = colon.restore(cur);
2160 } else {
2161 cur = '';
2162 }
2163
2164 if (typeof start === "string") {
2165 if ( start[0] === "#") {
2166 start = start.slice(1).replace(/-/g, " ");
2167 }
2168
2169 start = start.trim().toLowerCase();
2170
2171 if (start[0] === ":") {
2172 start = doc.stripSwitch(cur) + start;
2173 }
2174
2175 } else {
2176 doc.gcd.emit("error:start not a string in doc block", [start, cur]);
2177 start = '';
2178 }
2179 if (!start) {
2180 start = cur;
2181 }
2182
2183 return colon.escape(start);
2184};
2185
2186dp.stripSwitch = function (name) {
2187 var ind, blockhead;
2188
2189 blockhead = name;
2190
2191 if ( (ind = name.indexOf("::")) !== -1) {
2192 if ( (ind = name.indexOf(":", ind+2 )) !== -1 ) {
2193 blockhead = name.slice(0, ind);
2194 }
2195 } else if ( (ind = name.indexOf(":") ) !== -1) {
2196 blockhead = name.slice(0, ind);
2197 }
2198
2199 return blockhead;
2200
2201};
2202
2203dp.midPipes = function (str) {
2204 var ind = str.indexOf("|");
2205 var options, pipes;
2206
2207 ind = str.indexOf("|");
2208 if (ind === -1) {
2209 options = str.trim();
2210 pipes = "";
2211 } else {
2212 options = str.slice(0,ind).trim();
2213 pipes = str.slice(ind+1);
2214 }
2215
2216 return [options, pipes];
2217};
2218
2219dp.pipeDirSetup = function (str, emitname, handler, start) {
2220 var doc = this;
2221 var gcd = doc.gcd;
2222 var colon = doc.colon;
2223 var block;
2224
2225 var subEmit, textEmit, doneEmit;
2226
2227 if (str) {
2228 str = str + '"';
2229 doneEmit = "text ready:" + emitname;
2230 textEmit = "text ready:" + emitname + colon.v + "sp";
2231 subEmit = "pipe chain ready:" + emitname + colon.v + "sp";
2232 gcd.once(doneEmit, handler);
2233
2234 gcd.when(textEmit, subEmit);
2235
2236 gcd.once(subEmit, function (data) {
2237 var text = data[data.length-1][1] || '';
2238 gcd.emit(doneEmit, text);
2239 });
2240
2241
2242 block = doc.stripSwitch(colon.restore(start));
2243
2244 doc.pipeParsing(str, 0, '"', emitname + colon.v + "sp", block,
2245 subEmit, textEmit);
2246
2247 } else {
2248 gcd.once("text ready:" + emitname + colon.v + "sp", handler);
2249 }
2250
2251};
2252
2253dp.findMatchQuote = function (text, quote, ind) {
2254 var char;
2255 var n = text.length;
2256 var level = 0;
2257
2258 while ( ind < n) {
2259 char = text[ind];
2260 ind += 1;
2261 if (char === quote) {
2262 if ( level === 0) {
2263 break;
2264 } else {
2265 level -= 1;
2266 }
2267 } else if (char === '\u005F') {
2268 if (text[ind] === quote) {
2269 level += 1;
2270 ind += 1;
2271 }
2272
2273 } else if (char === "\\") {
2274 if ( text[ind] === quote) {
2275 ind += 1; // skip over the quote
2276 }
2277 }
2278 }
2279
2280 return ind;
2281};
2282
2283dp.argHandlerMaker = function (name, gcd) {
2284 var f = function (data) {
2285 var ret = [data[1][1]]; //cmd name
2286 var args = data.slice(2);
2287 args.forEach(function (el) {
2288 ret.push(el[1]);
2289 });
2290 ret.sub = true;
2291 gcd.emit("text ready:" + name, ret);
2292 };
2293 f._label = "arg command processing;;"+name;
2294 return f;
2295 };
2296
2297dp.argEscaping = function (text, ind ) {
2298 var chr, match, num;
2299 var uni = /[0-9A-F]+/g;
2300 var indicator = this.indicator;
2301
2302 chr = text[ind];
2303 switch (chr) {
2304 case "|" : return ["|", ind+1];
2305 case '\u005F' : return ['\u005F', ind+1];
2306 case "\\" : return ["\\", ind+1];
2307 case "'" : return ["'", ind+1];
2308 case "`" : return ["`", ind+1];
2309 case '"' : return ['"', ind+1];
2310 case "n" : return [indicator + "\n" + indicator, ind+1];
2311 case "t" : return [indicator + "\t" + indicator, ind+1];
2312 case " " : return [indicator + " " + indicator, ind+1];
2313 case "," : return [",", ind+1];
2314 case "u" : uni.lastIndex = ind;
2315 match = uni.exec(text);
2316 if (match) {
2317 num = parseInt(match[0], 16);
2318 try {
2319 chr = String.fromCodePoint(num);
2320 return [chr, uni.lastIndex];
2321 } catch (e) {
2322 return ["\\", ind];
2323 }
2324 } else {
2325 return ["\\", ind];
2326 }
2327 break;
2328 default : return ["\\", ind];
2329
2330 }
2331};
2332
2333dp.argProcessing = function (text, ind, quote, topname, mainblock) {
2334 var doc = this;
2335 var gcd = doc.gcd;
2336 var n = text.length;
2337 var stack = [];
2338 var name = [topname];
2339 var emitname = topname;
2340 var colon = doc.colon;
2341 var argstring = '';
2342 var curname;
2343 var err;
2344 var temp;
2345 var start = ind;
2346 var cp = "c\u0028"; // c parentheses
2347 var argdone = false;
2348
2349 var handlerMaker = doc.argHandlerMaker;
2350
2351 var whenIt = function () {
2352 name.push(start);
2353 curname = name.join(colon.v);
2354 gcd.when("text ready:" + curname, "arguments ready:" + emitname);
2355 };
2356
2357
2358 var wsreg = /\S/g;
2359
2360 wsreg.lastIndex = ind;
2361 if (wsreg.test(text) ) {
2362 start = ind = wsreg.lastIndex - 1;
2363 } else {
2364 ind = text.length;
2365 err = [start, ind];
2366 gcd.emit("error:" + topname, [err, "argument is just whitespace with no terminating"]);
2367 return;
2368 }
2369
2370
2371 while ( ind < n ) {
2372
2373 switch (text[ind]) {
2374
2375 case "\u005F" : // underscore
2376 if ( (start === ind) &&
2377 ( "\"'`".indexOf(text[ind+1]) !== -1 ) ) {
2378 whenIt();
2379 temp = doc.substituteParsing(text, ind+2, text[ind+1], curname, mainblock);
2380
2381 if ( temp === text.length) {
2382 //error
2383 err = [curname];
2384 gcd.emit("error:" + topname, [err, "substitution consumed rest of block"]);
2385 return temp;
2386 } else {
2387 ind = temp;
2388 }
2389
2390 argstring = '';
2391 argdone = true;
2392 wsreg.lastIndex = ind;
2393 if (wsreg.test(text) ) {
2394 start = ind = wsreg.lastIndex - 1;
2395 } else {
2396 ind = text.length;
2397 err = [start, ind];
2398 gcd.emit("error:" + topname, [err, "argument is just whitespace with no terminating"]);
2399 return;
2400 }
2401 continue;
2402 } else {
2403 argstring += "\u005F";
2404 }
2405 break;
2406
2407 case "," :
2408 if ( (stack.length === 0 ) || (stack[0] === cp) ) {
2409 if (argdone) {
2410 if (argstring !== "") {
2411 err = [argstring, text[start], text[ind], start, ind];
2412 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2413 argstring = "";
2414 }
2415 argdone = false;
2416 name.pop();
2417 start = ind+1;
2418
2419 } else { // simple string
2420 whenIt();
2421 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2422 name.pop();
2423 argstring = "";
2424 start = ind + 1;
2425 }
2426 ind += 1;
2427 wsreg.lastIndex = ind;
2428 if (wsreg.test(text) ) {
2429 start = ind = wsreg.lastIndex - 1;
2430 } else {
2431 ind = text.length;
2432 err = [start, ind];
2433 gcd.emit("error:" + topname, [err, "argument is just whitespace with no terminating"]);
2434 return;
2435 }
2436 continue;
2437 } else {
2438 argstring += ",";
2439 }
2440 break;
2441
2442 case "\\" :
2443 temp = doc.argEscaping(text, ind+1);
2444 argstring += temp[0];
2445 ind = temp[1];
2446 continue;
2447
2448 case "|" :
2449 if (stack.length === 0) {
2450 // make sure there is an argument
2451 if (argstring.trim()) {
2452 if (argdone) {
2453 if (argstring !== "") {
2454 err = [argstring, text[start], text[ind], start, ind];
2455 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2456 argstring = "";
2457 }
2458 argdone = false;
2459 name.pop();
2460 start = ind+1;
2461
2462 } else { // simple string
2463 whenIt();
2464 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2465 name.pop();
2466 argstring = "";
2467 start = ind + 1;
2468 }
2469 }
2470 return ind;
2471
2472 } else {
2473 argstring += "|";
2474 }
2475 break;
2476
2477
2478 case "[" :
2479 stack.unshift("[");
2480 argstring += "[";
2481 break;
2482
2483 case "]":
2484 if (stack[0] === "[") {
2485 stack.shift();
2486 }
2487 argstring += "]" ;
2488 break;
2489
2490 case "(" :
2491 //make sure no whitespace to be a command
2492 // also make sure either top level or in cp
2493 if ( ( (stack.length ===0) || (stack[0] === cp) ) &&
2494 (argstring.search(/^\S+\s*$/) !== -1) ) {
2495
2496 stack.unshift(cp);
2497 whenIt();
2498 emitname = curname;
2499 gcd.once("arguments ready:" + emitname, handlerMaker(emitname, gcd));
2500 gcd.when(["arg command parsed:" + emitname, "arg command is:" + emitname], "arguments ready:" + emitname);
2501 gcd.emit("arg command is:" + emitname, argstring.trim().toLowerCase());
2502 argstring = '';
2503 ind += 1;
2504 wsreg.lastIndex = ind;
2505 if (wsreg.test(text) ) {
2506 start = ind = wsreg.lastIndex - 1;
2507 } else {
2508 ind = text.length;
2509 err = [start, ind];
2510 gcd.emit("error:" + topname, [err, "argument is just whitespace with no terminating"]);
2511 return;
2512 }
2513 continue;
2514 } else {
2515 stack.unshift("(");
2516 argstring += "(";
2517 }
2518 break;
2519
2520 case ")" :
2521 if (stack[0] === cp) {
2522 stack.shift();
2523 if (argdone) {
2524 if (argstring !== "") {
2525 err = [argstring, text[start], text[ind], start, ind];
2526 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2527 argstring = "";
2528 }
2529 argdone = false;
2530 name.pop();
2531 start = ind+1;
2532
2533 } else { // simple string
2534 whenIt();
2535 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2536 name.pop();
2537 argstring = "";
2538 start = ind + 1;
2539 } // the last argument is popped
2540 gcd.emit("arg command parsed:" + emitname);
2541 emitname = name.slice(0, -1).join(colon.v);
2542 argdone = true;
2543 argstring = '';
2544 ind += 1;
2545 wsreg.lastIndex = ind;
2546 if (wsreg.test(text) ) {
2547 start = ind = wsreg.lastIndex - 1;
2548 } else {
2549 ind = text.length;
2550 err = [start, ind];
2551 gcd.emit("error:" + topname, [err, "argument is just whitespace with no terminating"]);
2552 return;
2553 }
2554 continue;
2555 } else {
2556 if (stack[0] === "(") {
2557 stack.shift();
2558 }
2559 argstring += ")" ;
2560 }
2561 break;
2562
2563 case "{" :
2564 stack.unshift("{");
2565 argstring += "{";
2566 break;
2567
2568 case "}" :
2569 if (stack[0] === "{") {
2570 stack.shift();
2571 }
2572 argstring += "}" ;
2573 break;
2574
2575 case "'" :
2576 if ( (stack.length === 0) && (quote === "'") ) {
2577 if (argstring.trim()) {
2578 if (argdone) {
2579 if (argstring !== "") {
2580 err = [argstring, text[start], text[ind], start, ind];
2581 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2582 argstring = "";
2583 }
2584 argdone = false;
2585 name.pop();
2586 start = ind+1;
2587
2588 } else { // simple string
2589 whenIt();
2590 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2591 name.pop();
2592 argstring = "";
2593 start = ind + 1;
2594 }
2595 }
2596 return ind;
2597 } else {
2598 // start after current place, get quote position
2599 temp = text.indexOf("'", ind+1)+1;
2600 if (temp === -1 ) {
2601 err = [start, ind, temp];
2602 gcd.emit("error:" + topname, [err, "non-terminating quote"]);
2603 argstring += "'";
2604 } else {
2605 argstring += text.slice(ind, temp);
2606 ind = temp;
2607
2608 continue;
2609 }
2610 }
2611 break;
2612 case "\u0022" :
2613 if ( (stack.length === 0) && (quote === "\u0022") ) {
2614 if (argstring.trim()) {
2615 if (argdone) {
2616 if (argstring !== "") {
2617 err = [argstring, text[start], text[ind], start, ind];
2618 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2619 argstring = "";
2620 }
2621 argdone = false;
2622 name.pop();
2623 start = ind+1;
2624
2625 } else { // simple string
2626 whenIt();
2627 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2628 name.pop();
2629 argstring = "";
2630 start = ind + 1;
2631 }
2632 }
2633 return ind;
2634 } else {
2635 // start after current place, get quote position
2636 temp = text.indexOf("\u0022", ind+1)+1;
2637 if (temp === -1 ) {
2638 err = [start, ind, temp];
2639 gcd.emit("error:" + topname, [err, "non-terminating quote"]);
2640 argstring += "\u0022";
2641 } else {
2642 argstring += text.slice(ind, temp);
2643 ind = temp;
2644
2645 continue;
2646 }
2647 }
2648 break;
2649
2650 case "`" :
2651 if ( (stack.length === 0) && (quote === "`") ) {
2652 if (argstring.trim()) {
2653 if (argdone) {
2654 if (argstring !== "") {
2655 err = [argstring, text[start], text[ind], start, ind];
2656 gcd.emit("error:" + topname, [err, "stuff found after argument finished"]);
2657 argstring = "";
2658 }
2659 argdone = false;
2660 name.pop();
2661 start = ind+1;
2662
2663 } else { // simple string
2664 whenIt();
2665 gcd.emit("text ready:" + curname, doc.whitespaceEscape(argstring.trim()));
2666 name.pop();
2667 argstring = "";
2668 start = ind + 1;
2669 }
2670 }
2671 return ind;
2672 } else {
2673 // start after current place, get quote position
2674 temp = text.indexOf("`", ind+1)+1;
2675 if (temp === -1 ) {
2676 err = [start, ind, temp];
2677 gcd.emit("error:" + topname, [err, "non-terminating quote"]);
2678 argstring += "`";
2679 } else {
2680 argstring += text.slice(ind, temp);
2681 ind = temp;
2682
2683 continue;
2684 }
2685 }
2686 break;
2687 default:
2688 argstring += text[ind];
2689
2690
2691
2692 }
2693
2694 ind +=1;
2695
2696 }
2697
2698 return ind;
2699
2700};
2701
2702dp.argFinishingHandler = function (comname) {
2703 var doc = this;
2704 var gcd = this.gcd;
2705
2706 var f = function (data) {
2707 var input, args, command, han;
2708
2709 input = data[0][1];
2710 command = data[1][1][1];
2711 args = data.slice(2).map(function (el) {
2712 return el[1];
2713 });
2714
2715 var fun = doc.commands[command];
2716
2717 if (fun) {
2718 fun.apply(doc, [input, args, comname, command]);
2719 } else {
2720 han = function () {
2721 fun = doc.commands[command];
2722 if (fun) {
2723 fun.apply(doc, [input, args, comname, command]);
2724 } else { // wait some more
2725 gcd.once("command defined:" + command, han);
2726 }
2727 };
2728 han._label = "delayed command:" + command + ":" + comname;
2729 gcd.once("command defined:" + command, han);
2730 }
2731
2732
2733 };
2734 f._label = "waiting for arguments:" + comname;
2735 return f;
2736};
2737
2738dp.whitespaceEscape = function (text) {
2739 var indicator = this.indicator;
2740 var n = indicator.length, start, end, rep;
2741 while ( (start = text.indexOf(indicator) ) !== -1 ) {
2742 end = text.indexOf(indicator, start + n);
2743 rep = text.slice(start+n, end);
2744 if (rep === "n") {
2745 rep = "\n";
2746 }
2747 text = text.slice(0, start) + rep + text.slice(end+n);
2748 }
2749 return text;
2750
2751};
2752
2753dp.argsPrep = function self (args, name, subs, command ) {
2754 var retArgs = [], i, n = args.length;
2755 var ret, subArgs;
2756 var cur, doc = this, gcd = this.gcd;
2757 doc.cmdName = name;
2758 var csubs, subc;
2759 csubs = doc.plugins[command] &&
2760 doc.plugins[command].subCommands ;
2761 for (i = 0; i < n; i += 1) {
2762 cur = args[i];
2763 if (Array.isArray(cur) && cur.sub ) {
2764 subArgs = cur.slice(1);
2765 if (subArgs.length) {
2766 subArgs = self.call(doc, subArgs, name, subs);
2767 }
2768 subc = cur[0];
2769 try {
2770 if (csubs && csubs[subc] ) {
2771 ret = csubs[subc].apply(doc, subArgs);
2772 } else if (subs && subs[subc] ) {
2773 ret = subs[subc].apply(doc, subArgs);
2774 } else {
2775 gcd.emit("error:no such subcommand:" + command + ":" +
2776 subc, [i, subArgs,name]);
2777 continue;
2778 }
2779 } catch (e) {
2780 gcd.emit("error:subcommand failed:" + command + ":" +
2781 subc, [i, subArgs, name]);
2782 continue;
2783 }
2784
2785 if (Array.isArray(ret) && (ret.args === true) ) {
2786 Array.prototype.push.apply(retArgs, ret);
2787 } else if (typeof ret === "undefined") {
2788 // no action, nothing added to retArgs
2789 } else {
2790 retArgs.push(ret);
2791 }
2792 } else { // should never happen
2793 retArgs.push(cur);
2794 }
2795
2796 }
2797
2798 return retArgs;
2799
2800};
2801
2802dp.getPostPipeName = function (name) {
2803 var ind = name.indexOf("|") + 1;
2804 if (ind) {
2805 return name.slice(ind);
2806 } else {
2807 return '';
2808 }
2809} ;
2810
2811module.exports = Folder;