Library, primarily for use within appedit. Wraps direape, fri, and reun, and adds extra functionality
var solsort = exports; var ss = solsort;
var da = require('direape'); da.testSuite('solsort');
var info = {
github: 'solsort/solsort'
};
da.test('examples', () => {
Render the following JSON-HTML
ss.html(() => ['div',
Make a heading, that counts the number of clicks
['h1.red', {onClick: ss.event('ss:click')},
'Clicks: ', String(ss.getJS('click-count', 0))],
Load a react component from npm
['react-star-rating:default', {name: 'hi', rating: 5}]
]);
Increase click-count in the application state, on click-event
ss.handle('ss:click', () =>
ss.setJS('click-count', ss.getJS('click-count', 0) + 1));
Set some styling if(ss.isBrowser()) { ss.loadStyle('myStyle', {'.red': {background: 'red'}}); } });
See https://appedit.solsort.com/?Read/js/gh/solsort/direape for details about DireApe
.
Object.assign(ss, da, {info: info});
da.ready(() => Object.assign(ss, da, {info: info}));
See https://appedit.solsort.com/?Read/js/gh/solsort/reun for details about Reun
.
var reun = require('reun');
Object.assign(ss, reun);
See https://appedit.solsort.com/?Read/js/gh/solsort/fri for details about FRI
.
var fri = require('fri');
Object.assign(ss, fri);
ss.set = fri.setJS;
ss.get = fri.getJS;
ss.update = (path, f) => ss.set(path, f(ss.get(path)));
ss.html = (val) => {
if(typeof val=== 'function') {
ss.rerun('ss:html', () => ss.set(['ui', 'html'], val()));
} else {
ss.set(['ui', 'html'], val);
}
};
ss.event = (name, opt) => ({solsortEvent: Object.assign({name: name, pid: da.pid}, opt)});
if(ss.isBrowser()) {
require('react/dist/react.js');
require('react-dom/dist/react-dom.js');
ss.rerun('solsort:ui-renderer', () => {
var html = ss.getJS(['ui', 'html']);
var rootElem = document.getElementById('solsort-ui');
if(!rootElem) {
return;
}
ss.nextTick(() => {
ss.set('ui.client', {
top: rootElem.clientTop,
left: rootElem.clientLeft,
height: rootElem.clientHeight,
width: rootElem.clientWidth
});
});
if(typeof html === 'string') {
rootElem.innerHTML = html;
} else if(Array.isArray(html)) {
ss.renderJsonml(html, rootElem);
}
});
}
ss.sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms || 0));
ss.renderJsonml = (jsonml, elem) => ss.eval(() => {
var dom = require('react-dom');
dom.render(jsonml2react(jsonml), elem);
});
ss.loadStyle = (name, style) => {
if(typeof document === 'undefined') {
return;
}
name = name + '-css';
var elem = document.getElementById(name);
if(!elem) {
elem = document.createElement('style');
elem.id = name;
document.head.appendChild(elem);
}
var str = '';
for(var selector in style) {
str += selector + '{';
for(var property in style[selector]) {
var value = style[selector][property];
if(typeof value === 'number') {
value = value + 'px';
}
str += property.replace(/[A-Z]/g, s => '-' + s.toLowerCase());
str += ':' + value + ';';
}
str += '}';
}
elem.innerHTML = str;
};
if(da.isBrowser()) {
da.test('loadStyle', () => {
var testStyle = {
'.testStyle': { textColor: 'red', width: 100 },
'.testStyle2': { }
};
return Promise.resolve(ss.loadStyle('testStyle', testStyle))
.then(() => ss.sleep())
.then(() => da.assertEquals(
document.getElementById('testStyle-css').innerHTML,
'.testStyle{text-color:red;width:100px;}.testStyle2{}'));
});
}
ss.loadCss = (url) => {
if(typeof document === 'undefined') {
return;
}
var id = url.toLowerCase().replace(/[^a-z0-9]/g,'');
var elem;
if(!document.getElementById(id)) {
elem = document.createElement('link');
elem.rel = 'stylesheet';
elem.id = id;
elem.href = url;
document.head.appendChild(elem);
}
};
ss.bodyElem = (id, type) => {
type = type || 'div';
var elem = document.getElementById(id);
if(!elem) {
elem = document.createElement(type);
elem.id = id;
document.body.appendChild(elem);
}
return elem;
};
if(da.isBrowser()) {
da.ready(() => {
var search = location.search.slice(1);
if(search) {
var route = {};
search = search.split('&').map(s => s.split('='));
for(var kv of search) {
var k = kv[0], v = kv[1];
route[uriParse(k)] = uriParse(v);
}
ss.setJS('route', route);
}
ss.rerun('ss:route-url', () =>
history.replaceState(null, null,
location.href.replace(/[?#].*.?/, '') + routeUrl()));
});
}
function jsonml2react(o) {
if(Array.isArray(o)) {
var react = require('react');
var name = o[0];
var node = document.createElement(o[0]);
var params = o[1];
var args = o.slice(2);
if(typeof params !== 'object' || params.constructor !== Object) {
params = {};
args = o.slice(1);
} else {
params = Object.assign({}, params);
}
args = args.map(jsonml2react);
In addition to normal element names, we also support names like
'npmModule:exportedSymbol', which loads an npm-module with require.
Example: ['react-star-rating:default', {name: 'hi', rating: 5}]
if(name.indexOf(':') !== -1) {
name = name.split(':');
name = require(name[0])[name[1]];
} else {
name = name.replace(/[.][^.#]*/g, (cls) => {
params.className = params.className || '';
params.className += ' ' + cls.slice(1);
return '';
});
}
for(var k in params) {
var v = params[k];
if(k === 'class') {
params.className += ' ' + v;
params.className = params.className.trim();
delete params.class;
}
if(isSolsortEvent(v)) {
params[k] = makeSolsortCallback(v);
}
}
return react.createElement.apply(react, [name, params].concat(args));
} else {
return String(o);
}
}
function isSolsortEvent(o) {
return o && typeof o === 'object' && o.solsortEvent && Object.keys(o).length === 1;
}
function makeSolsortCallback(o) {
o = o.solsortEvent;
return e => {
if(o.stopPropagation) { e.stopPropagation(); }
if(o.preventDefault) { e.preventDefault(); }
var result = {};
var extract = o.extract || [];
if(typeof extract === 'string') {
extract = [extract];
}
extract = extract.map(o => o.split('.'));
for(var i = 0; i < extract.length; ++i) {
jsSetIn(result, extract[i],
jsGetIn(e, extract[i]));
}
da.emit(o.pid, o.name, result, o.data);
};
}
function jsSetIn(o, path, val) {
if(!path.length) { return val; }
var k = path[0];
try { o[k] = o[k]; } catch(e) { o = {}; }
o[k] = jsSetIn(o[k], path.slice(1), val);
return o;
}
function jsGetIn(o, path, defaultValue) {
try {
if(path.length === 1) {
if(o[path[0]] === undefined) {
return defaultValue;
} else {
return o[path[0]];
}
}
return jsGetIn(o[path[0]], path.slice(1), defaultValue);
} catch(e) {
return defaultValue;
}
}
Converts the current route state into a url-search-component
function routeUrl() {
var params = [];
var route = ss.getJS('route');
for(var k in route) {
if(route[k] !== undefined) {
params.push(uriStringify(k) + '=' + uriStringify(route[k]));
}
}
return '?' + params.join('&');
}
Converts an uri component string into json
function uriParse(s) {
s = decodeURIComponent(s);
try { s = JSON.parse(s); } catch(_) { /* empty */ }
return s;
}
Converts json to an uri-component string
function uriStringify(s) {
try {
JSON.parse(s);
s = JSON.stringify(s);
} catch(_) { /* empty */ }
return encodeURIComponent(s);
}
if(require.main === module) {
ss.ready(() => {
ss.runTests('solsort');
});
}
This software is copyrighted solsort.com ApS, and available under GPLv3, as well as proprietary license upon request.
Versions older than 10 years also fall into the public domain.