Skip to content

Instantly share code, notes, and snippets.

@webresh
Forked from jlongster/bloop.js
Created December 3, 2016 10:01
Show Gist options
  • Save webresh/bc88f28e56a9f8c8707af6f194cde1a4 to your computer and use it in GitHub Desktop.
Save webresh/bc88f28e56a9f8c8707af6f194cde1a4 to your computer and use it in GitHub Desktop.

Revisions

  1. @jlongster jlongster revised this gist May 13, 2014. 1 changed file with 68 additions and 16 deletions.
    84 changes: 68 additions & 16 deletions bloop.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,7 @@
    (function() {

    // Do not use this library. This is just a fun example to prove a
    // point.

    var Bloop = window.Bloop = {};

    var mountId = 0;
    @@ -14,7 +16,7 @@
    }

    var args = _.toArray(arguments);
    if(!(_.isPlainObject(props) && !props.type)) {
    if(!(_.isPlainObject(props) && !props._type)) {
    children = props;
    props = null;
    args.unshift(null);
    @@ -32,6 +34,7 @@
    this[k] = val;
    }, this);

    this.props = props;
    this.state = this.getInitialState ? this.getInitialState() : null;

    if(props && props.state) {
    @@ -42,9 +45,8 @@
    delete props.state;
    }

    this.props = props;
    this.children = children;
    this.type = "custom";
    this._type = "custom";

    if(this.init) {
    this.init();
    @@ -94,14 +96,21 @@
    return changed;
    };

    Bloop.renderComponentToString = function(comp) {
    return renderVirtualDOMToString(comp.render());
    };

    function serializeVirtualDOM(dom) {
    var str = dom.type;
    var str = dom._type;
    if(dom.props) {
    _.keys(dom.props).forEach(function(k) {
    var prop = dom.props[k];
    if(_.isFunction(prop)) {
    str += k + 'function';
    }
    else if(k === 'style') {
    str += _.values(dom.props[k]).toString();
    }
    else if(dom.props[k]) {
    str += k + dom.props[k].toString();
    }
    @@ -110,22 +119,17 @@
    if(dom.children) {
    dom.children.forEach(function(child) {
    if(child) {
    str += child.type ? serializeVirtualDOM(child) : child;
    str += child._type ? serializeVirtualDOM(child) : child;
    }
    });
    }
    return str;
    }

    function renderComponentToString(comp) {
    var dom = renderVirtualDOM(comp.render());
    return dom.outerHTML;
    }

    function renderVirtualDOM(comp) {
    if(comp && comp.type) {
    if(comp && comp._type) {
    // render native thing
    var node = document.createElement(comp.type);
    var node = document.createElement(comp._type);
    var props = comp.props;
    var children = comp.children;

    @@ -135,6 +139,12 @@
    if(k.indexOf('on') === 0) {
    node.addEventListener(k.substring(2).toLowerCase(), props[k]);
    }
    else if(k === 'style') {
    var styles = props[k];
    _.keys(styles).forEach(function(k) {
    node.style[k] = styles[k];
    });
    }
    else {
    node[k] = props[k];
    }
    @@ -155,10 +165,50 @@
    }
    }

    function renderVirtualDOMToString(comp) {
    if(comp && comp._type) {
    var node = '<' + comp._type;
    var props = comp.props;
    var children = comp.children;

    if(props) {
    for(var k in props) {
    if(props.hasOwnProperty(k)) {
    if(k.indexOf('on') !== 0 && k !== 'style') {
    var val = props[k];
    if(k === 'className') {
    k = 'class';
    }
    else {
    k = k.replace(/[A-Z]/g, function(s) { return '-' + s.toLowerCase(); });
    }

    node += ' ' + k + '="' + val + '"';
    }
    }
    }
    }

    node += '>';

    children.forEach(function(child) {
    if(child != null) {
    node += renderVirtualDOMToString(child);
    }
    });

    node += '</' + comp._type + '>';
    return node;
    }
    else {
    return comp;
    }
    }

    function DOMCreator(type) {
    return function(props, children) {
    var args = _.toArray(arguments);
    if(!(_.isPlainObject(props) && !props.type)) {
    if(!(_.isPlainObject(props) && !props._type)) {
    children = props;
    props = null;
    args.unshift(null);
    @@ -169,7 +219,7 @@
    }

    return {
    type: type,
    _type: type,
    props: props,
    children: children.map(function(child) {
    if(child instanceof Bloop.createClass) {
    @@ -186,6 +236,9 @@
    span: DOMCreator('span'),
    a: DOMCreator('a'),
    p: DOMCreator('p'),
    em: DOMCreator('em'),
    img: DOMCreator('img'),
    form: DOMCreator('form'),
    button: DOMCreator('button'),
    ul: DOMCreator('ul'),
    li: DOMCreator('li'),
    @@ -197,5 +250,4 @@
    input: DOMCreator('input'),
    textarea: DOMCreator('textarea')
    };

    })();
  2. @jlongster jlongster revised this gist Apr 22, 2014. 1 changed file with 0 additions and 68 deletions.
    68 changes: 0 additions & 68 deletions bloop.js
    Original file line number Diff line number Diff line change
    @@ -198,72 +198,4 @@
    textarea: DOMCreator('textarea')
    };

    // routes

    var ROUTES = [];

    function addRoutes(/* routes */) {
    var routes = _.toArray(_.groupBy(arguments, function(el, i) {
    return Math.floor(i / 2);
    }));
    ROUTES = ROUTES.concat(routes);
    }

    function replaceRoutes(routes) {
    ROUTES = routes;
    }

    function changeRoute(path) {
    history.pushState(null, null, path);
    syncRoute(path);
    }

    function syncRoute(path) {
    if(path[0] !== '/') {
    var parts = window.location.pathname.split('/');
    parts.pop();
    path = '/' + parts.join('/') + path;
    }

    ROUTES.some(function(route) {
    var targetPath = route[0];
    var targetFunc = route[1];

    var params = _.zip(targetPath.split('/'), path.split('/')).reduce(function(acc, check) {
    var routePart = check[0];
    var input = check[1];

    if(acc && routePart[0] == ':' && input) {
    acc.push(input);
    return acc;
    }
    else if(routePart !== input) {
    return null;
    }
    else {
    return acc;
    }
    }, []);

    if(params) {
    targetFunc.apply(null, params);
    return true;
    }
    });
    }

    function redirect(path) {
    history.replaceState(null, null, path);
    syncRoute(path);
    }

    window.addEventListener('popstate', function(e) {
    syncRoute(window.location.pathname);
    });

    window.addRoutes = addRoutes;
    window.changeRoute = changeRoute;
    window.syncRoute = syncRoute;
    window.redirect = redirect;

    })();
  3. @jlongster jlongster created this gist Apr 22, 2014.
    269 changes: 269 additions & 0 deletions bloop.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,269 @@
    (function() {

    var Bloop = window.Bloop = {};

    var mountId = 0;
    function newMountId() {
    return mountId++;
    }

    Bloop.createClass = function(proto) {
    function _component(props, children) {
    if(this.constructor !== _component) {
    return new _component(props, children);
    }

    var args = _.toArray(arguments);
    if(!(_.isPlainObject(props) && !props.type)) {
    children = props;
    props = null;
    args.unshift(null);
    }

    if(!Array.isArray(children)) {
    children = args.slice(1);
    }

    _.keys(proto).forEach(function(k) {
    var val = proto[k];
    if(_.isFunction(val)) {
    val = val.bind(this);
    }
    this[k] = val;
    }, this);

    this.state = this.getInitialState ? this.getInitialState() : null;

    if(props && props.state) {
    _.keys(this.state).forEach(function(k) {
    props.state[k] = this.state[k];
    }, this);
    this.state = props.state;
    delete props.state;
    }

    this.props = props;
    this.children = children;
    this.type = "custom";

    if(this.init) {
    this.init();
    }
    }

    _component.prototype = Object.create(Bloop.createClass.prototype);
    _component.prototype.constructor = _component;

    return _component;
    };

    Bloop.renderComponent = function(comp, node) {
    var virtualDOM = comp.render();
    var changed = false;

    if(!comp.mountId) {
    comp.mountId = 'rerender-' + newMountId();
    var mount = document.createElement('div');
    mount.id = comp.mountId;
    mount._virtualDOM = virtualDOM;
    mount.appendChild(renderVirtualDOM(virtualDOM));
    node.appendChild(mount);

    if(comp.componentDidRender) {
    comp.componentDidRender();
    }
    }
    else {
    var mount = document.querySelector('#' + comp.mountId);
    var prevDOM = mount._virtualDOM;

    if(serializeVirtualDOM(virtualDOM) !== serializeVirtualDOM(prevDOM)) {
    var dom = renderVirtualDOM(virtualDOM);

    changed = true;
    mount.innerHTML = '';
    mount.appendChild(dom);
    mount._virtualDOM = virtualDOM;

    if(comp.componentDidRender) {
    comp.componentDidRender();
    }
    }
    }

    return changed;
    };

    function serializeVirtualDOM(dom) {
    var str = dom.type;
    if(dom.props) {
    _.keys(dom.props).forEach(function(k) {
    var prop = dom.props[k];
    if(_.isFunction(prop)) {
    str += k + 'function';
    }
    else if(dom.props[k]) {
    str += k + dom.props[k].toString();
    }
    });
    }
    if(dom.children) {
    dom.children.forEach(function(child) {
    if(child) {
    str += child.type ? serializeVirtualDOM(child) : child;
    }
    });
    }
    return str;
    }

    function renderComponentToString(comp) {
    var dom = renderVirtualDOM(comp.render());
    return dom.outerHTML;
    }

    function renderVirtualDOM(comp) {
    if(comp && comp.type) {
    // render native thing
    var node = document.createElement(comp.type);
    var props = comp.props;
    var children = comp.children;

    if(props) {
    for(var k in props) {
    if(props.hasOwnProperty(k)) {
    if(k.indexOf('on') === 0) {
    node.addEventListener(k.substring(2).toLowerCase(), props[k]);
    }
    else {
    node[k] = props[k];
    }
    }
    }
    }

    children.forEach(function(child) {
    if(child != null) {
    node.appendChild(renderVirtualDOM(child));
    }
    });

    return node;
    }
    else {
    return document.createTextNode(comp);
    }
    }

    function DOMCreator(type) {
    return function(props, children) {
    var args = _.toArray(arguments);
    if(!(_.isPlainObject(props) && !props.type)) {
    children = props;
    props = null;
    args.unshift(null);
    }

    if(!Array.isArray(children)) {
    children = args.slice(1);
    }

    return {
    type: type,
    props: props,
    children: children.map(function(child) {
    if(child instanceof Bloop.createClass) {
    return child.render();
    }
    return child;
    })
    };
    };
    }

    Bloop.dom = {
    div: DOMCreator('div'),
    span: DOMCreator('span'),
    a: DOMCreator('a'),
    p: DOMCreator('p'),
    button: DOMCreator('button'),
    ul: DOMCreator('ul'),
    li: DOMCreator('li'),
    header: DOMCreator('header'),
    h1: DOMCreator('h1'),
    h2: DOMCreator('h2'),
    h3: DOMCreator('h3'),
    h4: DOMCreator('h4'),
    input: DOMCreator('input'),
    textarea: DOMCreator('textarea')
    };

    // routes

    var ROUTES = [];

    function addRoutes(/* routes */) {
    var routes = _.toArray(_.groupBy(arguments, function(el, i) {
    return Math.floor(i / 2);
    }));
    ROUTES = ROUTES.concat(routes);
    }

    function replaceRoutes(routes) {
    ROUTES = routes;
    }

    function changeRoute(path) {
    history.pushState(null, null, path);
    syncRoute(path);
    }

    function syncRoute(path) {
    if(path[0] !== '/') {
    var parts = window.location.pathname.split('/');
    parts.pop();
    path = '/' + parts.join('/') + path;
    }

    ROUTES.some(function(route) {
    var targetPath = route[0];
    var targetFunc = route[1];

    var params = _.zip(targetPath.split('/'), path.split('/')).reduce(function(acc, check) {
    var routePart = check[0];
    var input = check[1];

    if(acc && routePart[0] == ':' && input) {
    acc.push(input);
    return acc;
    }
    else if(routePart !== input) {
    return null;
    }
    else {
    return acc;
    }
    }, []);

    if(params) {
    targetFunc.apply(null, params);
    return true;
    }
    });
    }

    function redirect(path) {
    history.replaceState(null, null, path);
    syncRoute(path);
    }

    window.addEventListener('popstate', function(e) {
    syncRoute(window.location.pathname);
    });

    window.addRoutes = addRoutes;
    window.changeRoute = changeRoute;
    window.syncRoute = syncRoute;
    window.redirect = redirect;

    })();