dom, vdom, hyperjson, and an api which provides a matrix of conversions
This commit is contained in:
parent
4cf2a8a0bd
commit
b958caebdd
57
www/common/convert.js
Normal file
57
www/common/convert.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
define([
|
||||||
|
'/common/virtual-dom.js',
|
||||||
|
'/common/hyperjson.js',
|
||||||
|
'/common/hyperscript.js'
|
||||||
|
], function (vdom, hyperjson, hyperscript) {
|
||||||
|
// complain if you don't find the required APIs
|
||||||
|
if (!(vdom && hyperjson && hyperscript)) { throw new Error(); }
|
||||||
|
|
||||||
|
// Generate a matrix of conversions
|
||||||
|
/*
|
||||||
|
convert.dom.to.hjson, convert.hjson.to.dom,
|
||||||
|
convert.dom.to.vdom, convert.vdom.to.dom,
|
||||||
|
convert.vdom.to.hjson, convert.hjson.to.vdom
|
||||||
|
|
||||||
|
and of course, identify functions in case you try to
|
||||||
|
convert a datatype to itself
|
||||||
|
*/
|
||||||
|
var convert = (function () {
|
||||||
|
var Self = function (x) {
|
||||||
|
return x;
|
||||||
|
},
|
||||||
|
methods = {
|
||||||
|
dom:{
|
||||||
|
dom: Self,
|
||||||
|
hjson: hyperjson.fromDOM,
|
||||||
|
vdom: function (D) {
|
||||||
|
return hyperjson.callOn(hyperjson.fromDOM(D), vdom.h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hjson:{
|
||||||
|
hjson: Self,
|
||||||
|
dom: function (H) {
|
||||||
|
// hyperjson.fromDOM,
|
||||||
|
return hyperjson.callOn(H, hyperscript);
|
||||||
|
},
|
||||||
|
vdom: function (H) {
|
||||||
|
return hyperjson.callOn(H, vdom.h);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
vdom:{
|
||||||
|
vdom: Self,
|
||||||
|
dom: function (V) {
|
||||||
|
return vdom.create(V);
|
||||||
|
},
|
||||||
|
hjson: function (V) {
|
||||||
|
return hyperjson.fromDOM(vdom.create(V));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
convert = {};
|
||||||
|
Object.keys(methods).forEach(function (method) {
|
||||||
|
convert[method] = { to: methods[method] };
|
||||||
|
});
|
||||||
|
return convert;
|
||||||
|
}());
|
||||||
|
return convert;
|
||||||
|
});
|
||||||
96
www/common/hyperjson.js
Normal file
96
www/common/hyperjson.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
define([], function () {
|
||||||
|
|
||||||
|
// this makes recursing a lot simpler
|
||||||
|
var isArray = function (A) {
|
||||||
|
return Object.prototype.toString.call(A)==='[object Array]';
|
||||||
|
};
|
||||||
|
|
||||||
|
var parseStyle = function(el){
|
||||||
|
var style = el.style;
|
||||||
|
var output = {};
|
||||||
|
for (var i = 0; i < style.length; ++i) {
|
||||||
|
var item = style.item(i);
|
||||||
|
output[item] = style[item];
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
var callOnHyperJSON = function (hj, cb) {
|
||||||
|
var children;
|
||||||
|
|
||||||
|
if (hj && hj[2]) {
|
||||||
|
children = hj[2].map(function (child) {
|
||||||
|
if (isArray(child)) {
|
||||||
|
// if the child is an array, recurse
|
||||||
|
return callOnHyperJSON(child, cb);
|
||||||
|
} else if (typeof (child) === 'string') {
|
||||||
|
// string nodes have leading and trailing quotes
|
||||||
|
return child.replace(/(^"|"$)/g,"");
|
||||||
|
} else {
|
||||||
|
// the above branches should cover all methods
|
||||||
|
// if we hit this, there is a problem
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
children = [];
|
||||||
|
}
|
||||||
|
// this should return the top level element of your new DOM
|
||||||
|
return cb(hj[0], hj[1], children);
|
||||||
|
};
|
||||||
|
|
||||||
|
var DOM2HyperJSON = function(el){
|
||||||
|
if(!el.tagName && el.nodeType === Node.TEXT_NODE){
|
||||||
|
return el.textContent;
|
||||||
|
}
|
||||||
|
if(!el.attributes){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var attributes = {};
|
||||||
|
for(var i = 0; i < el.attributes.length; i++){
|
||||||
|
var attr = el.attributes[i];
|
||||||
|
if(attr.name && attr.value){
|
||||||
|
if(attr.name == "style"){
|
||||||
|
attributes.style = parseStyle(el);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
attributes[attr.name] = attr.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should never be longer than three elements
|
||||||
|
var result = [];
|
||||||
|
|
||||||
|
// get the element type, id, and classes of the element
|
||||||
|
// and push them to the result array
|
||||||
|
var sel = el.tagName;
|
||||||
|
|
||||||
|
if(attributes.id){
|
||||||
|
sel = sel +'#'+ attributes.id;
|
||||||
|
delete attributes.id;
|
||||||
|
}
|
||||||
|
if(attributes.class){
|
||||||
|
sel = sel +'.'+ attributes.class.replace(/ /g,".");
|
||||||
|
delete attributes.class;
|
||||||
|
}
|
||||||
|
result.push(sel);
|
||||||
|
|
||||||
|
// second element of the array is the element attributes
|
||||||
|
result.push(attributes);
|
||||||
|
|
||||||
|
// third element of the array is an array of child nodes
|
||||||
|
var children = [];
|
||||||
|
for(var i = 0; i < el.childNodes.length; i++){
|
||||||
|
children.push(DOM2HyperJSON(el.childNodes[i]));
|
||||||
|
}
|
||||||
|
result.push(children);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
fromDOM: DOM2HyperJSON,
|
||||||
|
callOn: callOnHyperJSON
|
||||||
|
};
|
||||||
|
});
|
||||||
397
www/common/hyperscript.js
Normal file
397
www/common/hyperscript.js
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
define([], function () {
|
||||||
|
var Hyperscript;
|
||||||
|
|
||||||
|
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||||
|
var split = require('browser-split')
|
||||||
|
var ClassList = require('class-list')
|
||||||
|
require('html-element')
|
||||||
|
|
||||||
|
function context () {
|
||||||
|
|
||||||
|
var cleanupFuncs = []
|
||||||
|
|
||||||
|
function h() {
|
||||||
|
var args = [].slice.call(arguments), e = null
|
||||||
|
function item (l) {
|
||||||
|
var r
|
||||||
|
function parseClass (string) {
|
||||||
|
// Our minimal parser doesn’t understand escaping CSS special
|
||||||
|
// characters like `#`. Don’t use them. More reading:
|
||||||
|
// https://mathiasbynens.be/notes/css-escapes .
|
||||||
|
|
||||||
|
var m = split(string, /([\.#]?[^\s#.]+)/)
|
||||||
|
if(/^\.|#/.test(m[1]))
|
||||||
|
e = document.createElement('div')
|
||||||
|
forEach(m, function (v) {
|
||||||
|
var s = v.substring(1,v.length)
|
||||||
|
if(!v) return
|
||||||
|
if(!e)
|
||||||
|
e = document.createElement(v)
|
||||||
|
else if (v[0] === '.')
|
||||||
|
ClassList(e).add(s)
|
||||||
|
else if (v[0] === '#')
|
||||||
|
e.setAttribute('id', s)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if(l == null)
|
||||||
|
;
|
||||||
|
else if('string' === typeof l) {
|
||||||
|
if(!e)
|
||||||
|
parseClass(l)
|
||||||
|
else
|
||||||
|
e.appendChild(r = document.createTextNode(l))
|
||||||
|
}
|
||||||
|
else if('number' === typeof l
|
||||||
|
|| 'boolean' === typeof l
|
||||||
|
|| l instanceof Date
|
||||||
|
|| l instanceof RegExp ) {
|
||||||
|
e.appendChild(r = document.createTextNode(l.toString()))
|
||||||
|
}
|
||||||
|
//there might be a better way to handle this...
|
||||||
|
else if (isArray(l))
|
||||||
|
forEach(l, item)
|
||||||
|
else if(isNode(l))
|
||||||
|
e.appendChild(r = l)
|
||||||
|
else if(l instanceof Text)
|
||||||
|
e.appendChild(r = l)
|
||||||
|
else if ('object' === typeof l) {
|
||||||
|
for (var k in l) {
|
||||||
|
if('function' === typeof l[k]) {
|
||||||
|
if(/^on\w+/.test(k)) {
|
||||||
|
(function (k, l) { // capture k, l in the closure
|
||||||
|
if (e.addEventListener){
|
||||||
|
e.addEventListener(k.substring(2), l[k], false)
|
||||||
|
cleanupFuncs.push(function(){
|
||||||
|
e.removeEventListener(k.substring(2), l[k], false)
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
e.attachEvent(k, l[k])
|
||||||
|
cleanupFuncs.push(function(){
|
||||||
|
e.detachEvent(k, l[k])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})(k, l)
|
||||||
|
} else {
|
||||||
|
// observable
|
||||||
|
e[k] = l[k]()
|
||||||
|
cleanupFuncs.push(l[k](function (v) {
|
||||||
|
e[k] = v
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(k === 'style') {
|
||||||
|
if('string' === typeof l[k]) {
|
||||||
|
e.style.cssText = l[k]
|
||||||
|
}else{
|
||||||
|
for (var s in l[k]) (function(s, v) {
|
||||||
|
if('function' === typeof v) {
|
||||||
|
// observable
|
||||||
|
e.style.setProperty(s, v())
|
||||||
|
cleanupFuncs.push(v(function (val) {
|
||||||
|
e.style.setProperty(s, val)
|
||||||
|
}))
|
||||||
|
} else
|
||||||
|
e.style.setProperty(s, l[k][s])
|
||||||
|
})(s, l[k][s])
|
||||||
|
}
|
||||||
|
} else if (k.substr(0, 5) === "data-") {
|
||||||
|
e.setAttribute(k, l[k])
|
||||||
|
} else {
|
||||||
|
e[k] = l[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('function' === typeof l) {
|
||||||
|
//assume it's an observable!
|
||||||
|
var v = l()
|
||||||
|
e.appendChild(r = isNode(v) ? v : document.createTextNode(v))
|
||||||
|
|
||||||
|
cleanupFuncs.push(l(function (v) {
|
||||||
|
if(isNode(v) && r.parentElement)
|
||||||
|
r.parentElement.replaceChild(v, r), r = v
|
||||||
|
else
|
||||||
|
r.textContent = v
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
while(args.length)
|
||||||
|
item(args.shift())
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
h.cleanup = function () {
|
||||||
|
for (var i = 0; i < cleanupFuncs.length; i++){
|
||||||
|
cleanupFuncs[i]()
|
||||||
|
}
|
||||||
|
cleanupFuncs.length = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
var h = module.exports = context()
|
||||||
|
h.context = context
|
||||||
|
|
||||||
|
Hyperscript = h;
|
||||||
|
|
||||||
|
function isNode (el) {
|
||||||
|
return el && el.nodeName && el.nodeType
|
||||||
|
}
|
||||||
|
|
||||||
|
function forEach (arr, fn) {
|
||||||
|
if (arr.forEach) return arr.forEach(fn)
|
||||||
|
for (var i = 0; i < arr.length; i++) fn(arr[i], i)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isArray (arr) {
|
||||||
|
return Object.prototype.toString.call(arr) == '[object Array]'
|
||||||
|
}
|
||||||
|
|
||||||
|
},{"browser-split":2,"class-list":3,"html-element":6}],2:[function(require,module,exports){
|
||||||
|
/*!
|
||||||
|
* Cross-Browser Split 1.1.1
|
||||||
|
* Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
|
||||||
|
* Available under the MIT License
|
||||||
|
* ECMAScript compliant, uniform cross-browser split method
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits a string into an array of strings using a regex or string separator. Matches of the
|
||||||
|
* separator are not included in the result array. However, if `separator` is a regex that contains
|
||||||
|
* capturing groups, backreferences are spliced into the result each time `separator` is matched.
|
||||||
|
* Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably
|
||||||
|
* cross-browser.
|
||||||
|
* @param {String} str String to split.
|
||||||
|
* @param {RegExp|String} separator Regex or string to use for separating the string.
|
||||||
|
* @param {Number} [limit] Maximum number of items to include in the result array.
|
||||||
|
* @returns {Array} Array of substrings.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* // Basic use
|
||||||
|
* split('a b c d', ' ');
|
||||||
|
* // -> ['a', 'b', 'c', 'd']
|
||||||
|
*
|
||||||
|
* // With limit
|
||||||
|
* split('a b c d', ' ', 2);
|
||||||
|
* // -> ['a', 'b']
|
||||||
|
*
|
||||||
|
* // Backreferences in result array
|
||||||
|
* split('..word1 word2..', /([a-z]+)(\d+)/i);
|
||||||
|
* // -> ['..', 'word', '1', ' ', 'word', '2', '..']
|
||||||
|
*/
|
||||||
|
module.exports = (function split(undef) {
|
||||||
|
|
||||||
|
var nativeSplit = String.prototype.split,
|
||||||
|
compliantExecNpcg = /()??/.exec("")[1] === undef,
|
||||||
|
// NPCG: nonparticipating capturing group
|
||||||
|
self;
|
||||||
|
|
||||||
|
self = function(str, separator, limit) {
|
||||||
|
// If `separator` is not a regex, use `nativeSplit`
|
||||||
|
if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
|
||||||
|
return nativeSplit.call(str, separator, limit);
|
||||||
|
}
|
||||||
|
var output = [],
|
||||||
|
flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.extended ? "x" : "") + // Proposed for ES6
|
||||||
|
(separator.sticky ? "y" : ""),
|
||||||
|
// Firefox 3+
|
||||||
|
lastLastIndex = 0,
|
||||||
|
// Make `global` and avoid `lastIndex` issues by working with a copy
|
||||||
|
separator = new RegExp(separator.source, flags + "g"),
|
||||||
|
separator2, match, lastIndex, lastLength;
|
||||||
|
str += ""; // Type-convert
|
||||||
|
if (!compliantExecNpcg) {
|
||||||
|
// Doesn't need flags gy, but they don't hurt
|
||||||
|
separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
|
||||||
|
}
|
||||||
|
/* Values for `limit`, per the spec:
|
||||||
|
* If undefined: 4294967295 // Math.pow(2, 32) - 1
|
||||||
|
* If 0, Infinity, or NaN: 0
|
||||||
|
* If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
|
||||||
|
* If negative number: 4294967296 - Math.floor(Math.abs(limit))
|
||||||
|
* If other: Type-convert, then use the above rules
|
||||||
|
*/
|
||||||
|
limit = limit === undef ? -1 >>> 0 : // Math.pow(2, 32) - 1
|
||||||
|
limit >>> 0; // ToUint32(limit)
|
||||||
|
while (match = separator.exec(str)) {
|
||||||
|
// `separator.lastIndex` is not reliable cross-browser
|
||||||
|
lastIndex = match.index + match[0].length;
|
||||||
|
if (lastIndex > lastLastIndex) {
|
||||||
|
output.push(str.slice(lastLastIndex, match.index));
|
||||||
|
// Fix browsers whose `exec` methods don't consistently return `undefined` for
|
||||||
|
// nonparticipating capturing groups
|
||||||
|
if (!compliantExecNpcg && match.length > 1) {
|
||||||
|
match[0].replace(separator2, function() {
|
||||||
|
for (var i = 1; i < arguments.length - 2; i++) {
|
||||||
|
if (arguments[i] === undef) {
|
||||||
|
match[i] = undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (match.length > 1 && match.index < str.length) {
|
||||||
|
Array.prototype.push.apply(output, match.slice(1));
|
||||||
|
}
|
||||||
|
lastLength = match[0].length;
|
||||||
|
lastLastIndex = lastIndex;
|
||||||
|
if (output.length >= limit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (separator.lastIndex === match.index) {
|
||||||
|
separator.lastIndex++; // Avoid an infinite loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastLastIndex === str.length) {
|
||||||
|
if (lastLength || !separator.test("")) {
|
||||||
|
output.push("");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output.push(str.slice(lastLastIndex));
|
||||||
|
}
|
||||||
|
return output.length > limit ? output.slice(0, limit) : output;
|
||||||
|
};
|
||||||
|
|
||||||
|
return self;
|
||||||
|
})();
|
||||||
|
|
||||||
|
},{}],3:[function(require,module,exports){
|
||||||
|
// contains, add, remove, toggle
|
||||||
|
var indexof = require('indexof')
|
||||||
|
|
||||||
|
module.exports = ClassList
|
||||||
|
|
||||||
|
function ClassList(elem) {
|
||||||
|
var cl = elem.classList
|
||||||
|
|
||||||
|
if (cl) {
|
||||||
|
return cl
|
||||||
|
}
|
||||||
|
|
||||||
|
var classList = {
|
||||||
|
add: add
|
||||||
|
, remove: remove
|
||||||
|
, contains: contains
|
||||||
|
, toggle: toggle
|
||||||
|
, toString: $toString
|
||||||
|
, length: 0
|
||||||
|
, item: item
|
||||||
|
}
|
||||||
|
|
||||||
|
return classList
|
||||||
|
|
||||||
|
function add(token) {
|
||||||
|
var list = getTokens()
|
||||||
|
if (indexof(list, token) > -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list.push(token)
|
||||||
|
setTokens(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(token) {
|
||||||
|
var list = getTokens()
|
||||||
|
, index = indexof(list, token)
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list.splice(index, 1)
|
||||||
|
setTokens(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
function contains(token) {
|
||||||
|
return indexof(getTokens(), token) > -1
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(token) {
|
||||||
|
if (contains(token)) {
|
||||||
|
remove(token)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
add(token)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function $toString() {
|
||||||
|
return elem.className
|
||||||
|
}
|
||||||
|
|
||||||
|
function item(index) {
|
||||||
|
var tokens = getTokens()
|
||||||
|
return tokens[index] || null
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTokens() {
|
||||||
|
var className = elem.className
|
||||||
|
|
||||||
|
return filter(className.split(" "), isTruthy)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTokens(list) {
|
||||||
|
var length = list.length
|
||||||
|
|
||||||
|
elem.className = list.join(" ")
|
||||||
|
classList.length = length
|
||||||
|
|
||||||
|
for (var i = 0; i < list.length; i++) {
|
||||||
|
classList[i] = list[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
delete list[length]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filter (arr, fn) {
|
||||||
|
var ret = []
|
||||||
|
for (var i = 0; i < arr.length; i++) {
|
||||||
|
if (fn(arr[i])) ret.push(arr[i])
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTruthy(value) {
|
||||||
|
return !!value
|
||||||
|
}
|
||||||
|
|
||||||
|
},{"indexof":4}],4:[function(require,module,exports){
|
||||||
|
|
||||||
|
var indexOf = [].indexOf;
|
||||||
|
|
||||||
|
module.exports = function(arr, obj){
|
||||||
|
if (indexOf) return arr.indexOf(obj);
|
||||||
|
for (var i = 0; i < arr.length; ++i) {
|
||||||
|
if (arr[i] === obj) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
},{}],5:[function(require,module,exports){
|
||||||
|
var h = require("./index.js");
|
||||||
|
|
||||||
|
module.exports = h;
|
||||||
|
|
||||||
|
/*
|
||||||
|
$(function () {
|
||||||
|
|
||||||
|
var newDoc = h('p',
|
||||||
|
|
||||||
|
h('ul', 'bang bang bang'.split(/\s/).map(function (word) {
|
||||||
|
return h('li', word);
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
$('body').html(newDoc.outerHTML);
|
||||||
|
});
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
},{"./index.js":1}],6:[function(require,module,exports){
|
||||||
|
|
||||||
|
},{}]},{},[5]);
|
||||||
|
|
||||||
|
return Hyperscript;
|
||||||
|
});
|
||||||
1669
www/common/virtual-dom.js
Normal file
1669
www/common/virtual-dom.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user