intertext
Version:
Services for Recurrent Text-related Tasks
288 lines (257 loc) • 11.3 kB
JavaScript
(function() {
'use strict';
var CND, Cupofjoe, DATOM, alert, assign, badge, compact_tagname_re, debug, echo, excluded_content_parts, freeze, help, info, is_frozen, isa, jr, lets, log, new_datom, rpr, select, thaw, type_of, types, urge, validate, warn, whisper,
indexOf = [].indexOf;
//###########################################################################################################
CND = require('cnd');
rpr = CND.rpr;
badge = 'INTERTEXT/HTML';
log = CND.get_logger('plain', badge);
info = CND.get_logger('info', badge);
whisper = CND.get_logger('whisper', badge);
alert = CND.get_logger('alert', badge);
debug = CND.get_logger('debug', badge);
warn = CND.get_logger('warn', badge);
help = CND.get_logger('help', badge);
urge = CND.get_logger('urge', badge);
echo = CND.echo.bind(CND);
({jr} = CND);
//...........................................................................................................
DATOM = new (require('datom')).Datom({
dirty: false
});
({new_datom, lets, freeze, thaw, is_frozen, select} = DATOM.export());
types = require('./types');
// cast
// declare
// declare_cast
// check
// sad
// is_sad
// is_happy
({isa, validate, type_of} = types);
//...........................................................................................................
({Cupofjoe} = require('cupofjoe'));
assign = Object.assign;
excluded_content_parts = ['', null, void 0];
compact_tagname_re = /(?<prefix>[^\s.:#]+(?=:))|(?<id>(?<=#)[^\s.:#]+)|(?<class>(?<=\.)[^\s.:#]+)|(?<name>[^\s.:#]+)/ug;
//-----------------------------------------------------------------------------------------------------------
this.parse_compact_tagname = function(compact_tagname, strict = false) {
var R, groups, k, ref, target, v, y;
R = {};
ref = compact_tagname.matchAll(compact_tagname_re);
for (y of ref) {
({groups} = y);
for (k in groups) {
v = groups[k];
if ((v == null) || (v === '')) {
continue;
}
if (k === 'class') {
(R.class != null ? R.class : R.class = []).push(v);
} else {
if ((target = R[k]) != null) {
throw new Error(`^intertext/htmlish/parse_compact_tagname@1^ found duplicate values for ${rpr(k)}: ${rpr(target)}, ${rpr(v)}`);
}
R[k] = v;
}
}
}
if (strict && (R.name == null)) {
throw new Error(`^intertext/htmlish/parse_compact_tagname@2^ illegal compact tag syntax in ${rpr(compact_tagname)}`);
}
return R;
};
//===========================================================================================================
// #-----------------------------------------------------------------------------------------------------------
// @_escape_text = ( x ) ->
// R = x
// R = R.replace /&/g, '&'
// R = R.replace /</g, '<'
// R = R.replace />/g, '>'
// return R
// #-----------------------------------------------------------------------------------------------------------
// @_as_attribute_literal = ( x ) ->
// R = if isa.text x then x else JSON.stringify x
// must_quote = not isa._intertext_html_naked_attribute_text R
// R = @_escape_text R
// R = R.replace /'/g, '''
// R = R.replace /\n/g, ' '
// R = "'" + R + "'" if must_quote
// return R
/*
#-----------------------------------------------------------------------------------------------------------
@_parse_compact_tagname = ( compact_tagname ) ->
{ tagname
attributes } = ( compact_tagname.match /^(?<tagname>[^#.]*)(?<attributes>.*)$/ ).groups
R = {}
R.tagname = tagname unless tagname is ''
return R if attributes is ''
for attribute in attributes.split /([#.][^#.]*)/
continue if attribute is ''
avalue = attribute[ 1 .. ]
unless avalue.length > 0
throw new Error "^intertext/_parse_compact_tagname@1234^ illegal compact tag syntax in #{rpr compact_tagname}"
if attribute[ 0 ] is '#' then R.id = avalue
else ( R.class ?= [] ).push avalue
R.class = R.class.join ' ' if R.class?
return R
#-----------------------------------------------------------------------------------------------------------
@tag = ( compact_tagname, attributes, content... ) ->
{ start, content, end, } = @_tag compact_tagname, attributes, content
return [ start, content..., end, ] if end?
return [ start, content..., ]
#-----------------------------------------------------------------------------------------------------------
@_tag = ( compact_tagname, attributes, content, settings ) ->
validate.nonempty_text compact_tagname
{ tagname, id, class: clasz, } = @_parse_compact_tagname compact_tagname
validate.intertext_html_tagname tagname
use_attributes = false
processed_content = []
call_functions = settings?.call_functions ? true
content = thaw content if ( Object.isFrozen content )
#.........................................................................................................
if attributes?
if isa.object attributes then use_attributes = true
else content.unshift attributes
#.........................................................................................................
if content.length is 0
sigil = '^'
end_tag = null
#.........................................................................................................
else
sigil = '<'
end_tag = new_datom ">#{tagname}"
for part in content
continue if part in excluded_content_parts
switch type_of part
when 'text' then processed_content.push new_datom '^text', { text: part, }
when 'function'
if call_functions then processed_content.push x unless ( x = part() ) in excluded_content_parts
else processed_content.push part
else processed_content.push part
#.........................................................................................................
if id? or clasz?
idclass = {}
idclass.id = id if id?
idclass.class = clasz if clasz?
if use_attributes
attributes = assign idclass, attributes
else
use_attributes = true
attributes = idclass
#.........................................................................................................
if use_attributes then start_tag = new_datom "#{sigil}#{tagname}", attributes
else start_tag = new_datom "#{sigil}#{tagname}"
#.........................................................................................................
processed_content = processed_content.flat Infinity
return { start: start_tag, content: processed_content, end: end_tag, } if end_tag?
return { start: start_tag, content: processed_content, }
*/
//-----------------------------------------------------------------------------------------------------------
this.html_from_datoms = function(ds, settings) {
var d;
return ((function() {
var i, len, ref, results;
ref = ds.flat(2e308);
results = [];
for (i = 0, len = ref.length; i < len; i++) {
d = ref[i];
results.push(this._html_from_datom(d));
}
return results;
}).call(this)).join('');
};
//-----------------------------------------------------------------------------------------------------------
this.$html_from_datoms = function() {
var $;
({$} = (require('steampipes')).export());
return $((d, send) => {
var i, len, ref, x;
if (!isa.list(d)) {
return send(this._html_from_datom(d));
}
ref = this.html_from_datoms(...d);
for (i = 0, len = ref.length; i < len; i++) {
x = ref[i];
send(x);
}
return null;
});
};
//-----------------------------------------------------------------------------------------------------------
this._html_from_datom = function(d) {
var atxt, bnl, i, is_block_tag, is_empty_tag, key, len, ref, ref1, ref2, ref3, ref4, sigil, slash, src, tagname, value, x_key, x_sys_key, xnl;
if (isa.text(d)) {
return this._html_from_datom((this.text(d))[0]);
}
/* TAINT ??? */ DATOM.types.validate.datom_datom(d);
atxt = '';
sigil = d.$key[0];
tagname = d.$key.slice(1);
is_empty_tag = isa._intertext_html_empty_element_tagname(tagname);
x_key = null;
is_block_tag = (ref = d.$blk) != null ? ref : false;
bnl = is_block_tag ? '\n\n' : ''/* TAINT make configurable */
xnl = '\n'/* TAINT make configurable */
//.........................................................................................................
/* TAINT simplistic solution; namespace might already be taken? */
if (indexOf.call('[~]', sigil) >= 0) {
switch (sigil) {
case '[':
sigil = '<';
break;
case '~':
sigil = '^';
break;
case ']':
sigil = '>';
}
[x_key, tagname] = [tagname, 'x-sys'];
}
if ((sigil === '^') && (tagname === 'text')) {
//.........................................................................................................
return this._escape_text((ref1 = d.text) != null ? ref1 : '');
}
if ((sigil === '^') && (tagname === 'raw')) {
return (ref2 = d.text) != null ? ref2 : '';
}
if ((sigil === '^') && (tagname === 'doctype')) {
return `<!DOCTYPE ${(ref3 = d.$value) != null ? ref3 : 'html'}>${xnl}`;
}
if (sigil === '>') {
return `</${tagname}>${bnl}`;
}
//.........................................................................................................
/* NOTE sorting atxt by keys to make result predictable: */
if (isa.object(d.$value)) {
src = d.$value;
} else {
src = d;
}
if (x_key != null) {
atxt += ` x-key=${this._as_attribute_literal(x_key)}`;
}
ref4 = (Object.keys(src)).sort();
for (i = 0, len = ref4.length; i < len; i++) {
key = ref4[i];
if (key.startsWith('$')) {
continue;
}
if ((value = src[key]) === true) {
atxt += ` ${key}`;
} else {
atxt += ` ${key}=${this._as_attribute_literal(value)}`;
}
}
//.........................................................................................................
/* TAINT make self-closing elements configurable, depend on HTML5 type */
slash = (sigil === '<') || is_empty_tag ? '' : `</${tagname}>${bnl}`;
x_sys_key = x_key != null ? `<x-sys-key>${x_key}</x-sys-key>` : '';
if (atxt === '') {
return `<${tagname}>${slash}${x_sys_key}`;
}
return `<${tagname}${atxt}>${x_sys_key}${slash}`;
};
}).call(this);
//# sourceMappingURL=htmlish.js.map