dom, vdom, hyperjson, and an api which provides a matrix of conversions

pull/1/head
ansuz 9 years ago
parent 4cf2a8a0bd
commit b958caebdd

@ -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;
});

@ -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
};
});

@ -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 doesnt understand escaping CSS special
// characters like `#`. Dont 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;
});

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save