Announcing OrganicJS

OrganicJS is a micro-framework for reusable JS+HTML+SVG components. It is also a set of 
framework facets that enable more powerful and more maintainable component architecture for 
D3.js, the popular JS+HTML+SVG visualization library.

OrganicJS supports reusable components with chainable properties and public methods, dynamic 
setters/getters, reusable widget markup using nestable HTML/SVG fragments and in-place fragment cloning and rendering, and sharing of data/behavior among components in a soft, decoupled 
manner via contextual component caching.

OrganicJS re-envisions, re-implements and supersedes my earlier ideas around using D3, which can be seen here (while link is up, works on Chrome only)

One of the ideas is the support for borrowing of data/behavior of one component by another component in non-hardwired manner without use of events. Another idea is the building of “widgets” (collection of components)  from reusable, nestable HTML+SVG fragments, as an alternative to the way it is done in D3 (although the framework can still be used in the D3 way without the reusable widget markup.)

Demo:
Features Overview:
Repo:
Advertisements

D3 Component Data Model

This post expands on the previous post: Improvements to D3 Resuable Component Pattern

Why?

I recently had this situation where a JSON Array needed to be populated by two instances of the same D3 component that I had built, at virtually the same time, using two different datasets.

The array was then further processed by two instances of another of my D3 components that manipulated the data differently based on the dataset.

I had two scopes to deal with, the first scope included the first instance of the first component and the first instance of the second component and the second scope included the second instance of the first component and the second instance of the second component.

Using locally defined data structures and an event bus (with channel as scope) to both orchestrate execution across components as well as transfer data structures between components (in event details) in a de-coupled way is one way of doing it, but it’s helpful when debugging an app to have your “global” data structures in one place so you can inspect their content from anywhere. Another way, which is is simpler from a framework-code-size and maintenance point, is having a “model” component that is exposed to all components where the components may declare a scope.

D3 is needed to run the code below but you may redefine the model on its own and use without D3.


// extensions object on d3
    d3.ext = {};

    // this creates automatic getter/setter on the returned object for each public property
    // so that public properties can be accessed as componentInstance.property(value) (when setting)
    // and componentInstance.property() (when getting)
    function getterSetter(obj)  {

       if (obj.props.scope)
            throw new Error("scope should be configured outside of component")

      // place holder for scope getter/setter
       obj.props.scope = undefined;

       for (o in obj.props) {
            obj[o] =  new Function("value", "if (!arguments.length) return typeof this.props['" + o +
            "'] == 'function' ? this.props['" + o + "'].call(this) : this.props['" + o + "'];" +
            "this.props['" + o + "'] = (typeof value == 'function' ? value.call(this) : value); return this")
       }

    }

        // Component Data model
    // use model.create("scope", {property: value, property: value, etc}) to create new properties
    // use model.propertyName("scope") to get the value for a property in the given scope
    // use model.propertyName("scope", value) to set the value for a property in the given scope
    // use console.log(model.entries()) to view all entries for all components
    //
    // setters are chain-able
    //
    // the scope is usually the component's .context() but can be any other id

    d3.ext.model = function() {

        var obj = {}

        obj.entries = function() { return obj}

        obj.create = function(scope, json) {

            if (scope === undefined)
                throw new Error("scope property not configured").stack

            obj[scope] = obj[scope] || {}

            obj[scope].props = obj[scope].props || {}

            for (key in json) {
                obj[scope].props[key] = json[key]
            }

            for (o in obj[scope].props) {
                if (obj[scope].props.hasOwnProperty(o)) {
                    obj[o] =  new Function("scope, value", "if (!scope) return; if (!value) " +
                        "return this[scope].props['" + o + "'];" +
                        "this[scope].props['" + o +
                        "'] = value; return this")
                }
            }
            return obj;
        }
        return obj;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////

    var model = d3.ext.model()

    // create component
	var myChart = function chart(model) {

		// private vars defined ordinarily
		var a, b, c, d, etc;

		// let's refer to the instance as obj
		var obj = {};

		// place to hold publicly accessible properties
		obj.props = {};

		// all publicly accessible properties are defined here
		obj.props.width=60;
		obj.props.height=70;

		obj.props.area = function() {
			return this.props.height *  this.props.width
		}

		obj.props.someArray = [];

		obj.props.test = function() {
			return Math.random()
		}

		// create getters/setters
		getterSetter(obj);

		// this is how private methods are defined
		var somePrivateMethod = function() { console.log('hi, i am a private method')}

		// this is how public methods are defined...
		obj.render = function() {

			// generate chart here, using `obj.props.width` and `obj.props.height`

			console.log('rendering chart with width = ' + obj.props.width +  ' and height = ' + obj.props.height)

			if (model) console.log('scoped global var:', JSON.stringify(model.testValue(obj.scope())))

			return obj;
		}

		return obj;
	}

    // testing the original settings
	myChart()
		.render();
	console.log(myChart().width(), myChart().height())

    model.create("A", {testValue: {a: 10, b: 20, c: 30}})
    model.create("B", {testValue: {a: 100, b: 200, c: 300}})

	// create new instances with new settings
	var chart1 = myChart(model)
			.scope("A")
			.width(500)
			.height(300)
			.render()

	console.log(chart1.test())
	console.log(chart1.test())
	console.log(chart1.test())

	var chart2 =  myChart(model)
			.scope("A")
			.width(200)
			.height(800);
	// example of rendering after setting
	chart2.render()

	var chart3 = myChart(model)
			.scope("B")
			.width(100)
			// example of function as value generator
			.height(function() {return this.width() * 4})
			.render()

	var chart4 = myChart(model)
			.scope("B")
			.width(100)
			.height(120)
			.render()

	// testing the new settings
	console.log(chart1.width(), chart1.height())
	console.log(chart2.width(), chart2.height())
	console.log(chart3.width(), chart3.height())

	// set a new width for chart3
	chart3.width(3000)

	console.log(chart3.width(), chart3.height())

	console.log(chart4.width(), chart4.height())

	// tests for function reference as value for setter

	// test getter for property
	console.log('area property with default function', chart1.area())

	// set property to function reference via closure (use only a function reference if assigning by value)
	// else use two as shown
	chart1.area(function() {return function() { return Math.pow(this.width(), 2) * (Math.PI/4)}})

	// test getter after change
	console.log('area property for chart1 with updated function', chart1.area())

	console.log('area property for chart1 with updated function after width is changed', chart1.width(700).area())

	console.log('area property for chart2 with original function', chart2.area())

        // test assigning a closure to a property
	function testClosure() {
		var someFunction = function() {
			return ~~(Math.random() * 1000);
		}
		return function() {
			return someFunction();
		}
	}

    chart1.width(testClosure)

    console.log(chart1.width())
	console.log(chart1.width())
	console.log(chart1.width())

	// using typed properties
	chart1.someArray().push(5) // or chart1.someArray([1,2,3])

	console.log(chart1.someArray())

Improvements to D3’s Reusable Component Pattern

Updated: 12/26/2012

If you’re new to D3, it’s a dynamic, DOM-element-creating-and-data-binding, SVG-path-,-style-,-and-transform-specifying, swiss army knife for data visualization that is –as of v3– capable of generating canvas path data!

Take a look at the range of stuff developed with it over here:

https://github.com/mbostock/d3/wiki/Gallery

It was suggested a while ago, as a starting point, to use the following pattern for building data visualization components that mimic D3’s own components in terms of configuration and use patterns. It was called the “Reusable chart Pattern” and the gist of it is this:


function chart() {
  var width = 720, // default width
      height = 80; // default height

  function my() {
    // generate chart here, using `width` and `height`
  }

  my.width = function(value) {
    if (!arguments.length) return width;
    width = value;
    return my;
  };

  my.height = function(value) {
    if (!arguments.length) return height;
    height = value;
    return my;
  };

  return my;
}

Source: http://bost.ocks.org/mike/chart/

I have had a few key requirements on several occasions that are not provided by the original pattern above:

1. Getter/Setter methods needs to be generated automatically (why: because there may be many of them)

2. In the component code itself, properties that will be exposed publicly need to be defined separately than local vars (this is needed both for clarity/abstraction and for achieving #1 above)

3. Must be able to assign functions to properties by reference or value, not just from inside the component but also from outside of it, where in both cases “this” in the function body must refer to the component instance.

So here is what I came up with:


	// this function (when called from inside a component) creates automatic getter/setter on the returned object 
	// for each public property so that public properties can be accessed as componentInstance.property(value) 
	// (when setting) and componentInstance.property() (when getting)
    function getterSetter()  {
      for (o in this.props) {
           if (this.props.hasOwnProperty(o)) {         
                this[o] =  new Function("value", "if (!arguments.length) return typeof this.props['" + o + 
                "'] == 'function' ? this.props['" + o + "'].call(this) : this.props['" + o + "'];" + 
                "this.props['" + o + "'] = (typeof value == 'function' ? value.call(this) : value); return this") 
            }
       }
    }
    
    // create component 
	var myChart = function chart(args) {
		
		// private vars defined ordinarily 
		var a, b, c, d, etc;
		  
		// let's refer to the instance as obj	
		var obj = {};
		  
		// place to hold publicly accessible properties	
		obj.props = {};
		  
		// all publicly accessible properties are defined here
		obj.props.width=60;
		obj.props.height=70;
		  
		obj.props.area = function() {	  	
			return this.props.height *  this.props.width 
		}
		
		obj.props.someArray = [];
		
		obj.props.test = function() {
			return Math.random()
		}
		
		// create getters/setters 
		getterSetter();
		
		// this is how private methods are defined
		var somePrivateMethod = function() { console.log('hi, i am a private method')}
		  
		// this is how public methods are defined... 
		obj.render = function() {
	  	
			// generate chart here, using `obj.props.width` and `obj.props.height`
			    
			console.log('rendering chart with width = ' + obj.props.width +  ' and height = ' + obj.props.height)
			    
			if (args) console.log('detected component scoped argument: ' + args)
			    
			return obj;
		}
	
		return obj; 
	}
	
    // testing the original settings
	myChart().render()
	console.log(myChart().width(), myChart().height())

	// create new instances with new settings
	var chart1 = myChart()
			.width(500)
			.height(300)
			.render()
			
	console.log(chart1.test())		
	console.log(chart1.test())	
	console.log(chart1.test())	

	var chart2 =  myChart()
			.width(200)
			.height(800);
	// example of rendering after setting
	chart2.render()
	
	var chart3 = myChart()
			.width(100)
			// example of function as value generator
			.height(function() {return this.width() * 4})	
			.render()
	var chart4 = myChart(999)
			.width(100)
			.height(120)
			.render()
	
	// testing the new settings
	console.log(chart1.width(), chart1.height())
	console.log(chart2.width(), chart2.height())
	console.log(chart3.width(), chart3.height())
        
	// set a new width for chart3
	chart3.width(3000)
	
	console.log(chart3.width(), chart3.height())
	
	console.log(chart4.width(), chart4.height())
	
	// tests for function reference as value for setter
	
	// test getter for property
	console.log('area property with default function', chart1.area())
	
	// set property to function reference via closure (use only a function reference if assigning by value)
	// else use two as shown 
	chart1.area(function() {return function() { return Math.pow(this.width(), 2) * (Math.PI/4)}})
	
	// test getter after change
	console.log('area property for chart1 with updated function', chart1.area())
	
	console.log('area property for chart1 with updated function after width is changed', chart1.width(700).area())
	
	console.log('area property for chart2 with original function', chart2.area())
	
        // test assigning a closure to a property
	function testClosure() {	
		var someFunction = function() {
			return ~~(Math.random() * 1000);
		}
		return function() {
			return someFunction();
		}
	}
	
    chart1.width(testClosure)
	
    console.log(chart1.width())
	console.log(chart1.width())
	console.log(chart1.width())
	
	// using typed properties
	chart1.someArray().push(5) // or chart1.someArray([1,2,3])
	
	console.log(chart1.someArray())
	

JSON API for templated HTML view compositing

My contribution to JSON-based HTML templating. Supports template inheritance and nesting, but not recursive structures. Overshadowed by the W3C Shadow DOM and Web Components which at the time of this post remains an experimental feature in Chrome Canary (see: chrome://flags)

project page: http://idibidiart.github.com/idi.bidi.dom/

sources: https://github.com/idibidiart/idi.bidi.dom


			

About my Javascript+HTML5+Canvas+SVG project

One day I decided to try out Twitter (about 9 months ago) …

I was browsing people’s feeds and adding folks and somehow I ended up with a tweet on my timeline that was made entirely of unicode characters, the kind that was used in terminals to display user interface elements before bitmapped graphics came along.

It inspired me to make an Editor for this kind of art (which is called “Twitter Art” by the way)

Make sure to watch these demonstration videos videos on youtube in HD (720p)

http://www.youtube.com/watch?v=Wd-d1p-c56U (Original)

http://www.youtube.com/watch?v=UX5jOvdLG8c (Canvas2Vector Effects 1)

http://www.youtube.com/watch?v=jCAp22_HYLk (Canvas2Vector Effects 2)

http://www.youtube.com/watch?v=R1fin3jBFTw (Canvas2Vector Effects 3)

http://www.youtube.com/watch?v=VLyLb3Otw5s (SVG Saving And Manipulation)

I’m working on free open source license for non-commercial use and also a royalty-free commercial license with commented sources, to help this new medium take hold.

The app is built entirely with Javacsript, HTML5 (canvas), and SVG. There are no server components, other than a 20-line Python HTTP server based on Google Appengine API. Static HTML5, JS and .png files are served to the client and everything runs inside the browser.

UPDATE:

The Firefox team, after having been pouched to death by Google, decided to remove the ability in Firefox to send keystrokes to a textarea. Thus, the death of idibidiart (until I have time to port it to Chrome, which has become the more advanced development environment, which wasn’t the case when I started)

http://idibidiart.com (RIP til later notice)

@idibidiart