Wednesday, February 22, 2017

Virtual Reality Art Show

For the past six years, I've helped out at our community Art and Craft Festival. The logistics behind such a show are surprisingly daunting - hundreds of pieces of art have to be delivered, catalogued, tracked and (ideally) sold over the course of one weekend.

A couple of years ago I built a mobile website to help with this process. The website allows show coordinators to invite artists; artists to photograph (using their camera phone) and upload their art; the gallery team to arrange the paintings on walls using a drag-and-drop UI; and finally the sales team to track sales.

Another requested feature was to allow people to buy art online, before the show. But how to give people a good impression of how that art will look in their home? How to give a feel for the size of the art, and what colour walls it suits?

In previous years I solved this using stock photos of lounge rooms overlayed with simple CSS perspective transforms - to 'hang' the art on the wall.

However for the 2017 show I've upgraded the site to use the new A-Frame framework. I think this presents a great case study of using Virtual Reality for non-gaming. Virtual Reality excels at giving people a sense of scale, which (hopefully) will really help in deciding to buy art online. Art galleries are also a great application for Web VR, as paintings use only simple geometry which is easy for lower-end devices to handle.

Check it out at http://tinyurl.com/owps-art or see the screenshow below:

As a bonus, I've also Open Sourced the A-Frame collision detection component I developed to prevent the user walking through walls.

Thursday, February 9, 2017

Metawidget Ported to Angular 2

Metawidget development has slowed lately, thanks to two factors: the framework itself is very mature; and I've been consumed by my startup.

However, thanks to the Open Source community others have taken up the mantle. I look forward to integrating their work into the main branch in the future! For now, keep an eye on:

In addition, I'm still doing small changes and fixes as they come up.

Thursday, August 11, 2016

Metawidget and React

Congratulations to the University of Technology, Sydney's Metawidget Team.

For their first semester Software Development Studio project, they prototyped a React version of Metawidget and demonstrated retrofitting it into an existing application to deliver significant reductions in error-prone, boilerplate code.

Monday, July 18, 2016

500 on Air

I was interviewed on ABC Radio 612 Brisbane about our online 500 card game website.

Here's a link to the audio from the show.

Thursday, July 7, 2016

Wealth Projector featured in Queensland Country Life

Congratulations to AgriHive Wealth Projector for their write-up in Queensland Country Life:

"I think this is the direction we’re heading in - this type of thing will be important as an advocacy tool... [farming businesses] all believe in this."

Thursday, May 5, 2016

Metawidget: associating EventListeners with widgets

I was recently asked how to "associate further rules with widgets, depending on the populated data [when using the pure JavaScript Metawidget]". The answer is to use a WidgetProcessor. Here's a complete example:

<!DOCTYPE html>
<html>
   <head>
      <script src="lib/metawidget/core/metawidget-core.min.js" type="text/javascript"></script>
   </head>
   <body>
      <div id="metawidget">
      </div>
      <script type="text/javascript">

         var mw = new metawidget.Metawidget( document.getElementById( 'metawidget' ), {
            inspector: new metawidget.inspector.CompositeInspector( [
               new metawidget.inspector.PropertyTypeInspector(),
               new metawidget.inspector.JsonSchemaInspector( {

                  // Insert custom attribute into the metadata

                  properties: {
                     surname: {
                        disabledUnless: 'firstname'
                     }
                  }
               } ) ] ),
            appendWidgetProcessors: function( widget, elementName, attributes, mw ) {

               // Watch for custom attribute, and add an EventListener

               if ( attributes.disabledUnless !== undefined ) {
                  var triggerWidgetId = metawidget.util.getId( 'property', { name: attributes.disabledUnless }, mw );
                  var triggerWidget = document.getElementById( triggerWidgetId );
                  triggerWidget.addEventListener( 'keyup', function() {
                     widget.disabled = ( triggerWidget.value === '' );
                  } );
                  widget.disabled = true;
               }
               return widget;
            }
         } );

         mw.toInspect = {
            firstname: '',
            surname: ''
         };

         mw.buildWidgets();
      </script>
   </body>
</html>

Friday, April 8, 2016

Metawidget and Angular: arrays

I was recently asked how AngularJS Metawidget handles arrays of items. Let's start with a simple out-of-the-box example:

<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="app">
   <head>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js" type="text/javascript"></script>
      <script src="http://metawidget.org/js/4.2/metawidget-core.min.js" type="text/javascript"></script>
      <script src="http://metawidget.org/js/4.2/metawidget-angular.min.js" type="text/javascript"></script>
      <script>
         angular.module( 'app', [ 'metawidget' ] ).controller( 'myController', function( $scope ) {

            $scope.person = {
               firstname: 'Homer',
               age: 43,
               children: [ {
                  id: 1,
                  firstname: 'Bart',
                  age: 10
               }, {
                  id: 2,
                  firstname: 'Lisa',
                  age: 8
               } ]
            }
         } );
      </script>
   </head>
   <body ng-controller="myController">
      <metawidget ng-model="person"></metawidget>
   </body>
</html>

Metawidget does reasonably well here:

It generates the correct labels, given the field names in the JavaScript object. It also renders the correct types of controls (text inputs for strings, number inputs for numbers, tables for arrays) and determines the columns in the table by looking at the array elements. Of course, it doesn't look very pretty - but you can always add some CSS or plug in BootstrapWidgetProcessor to fix that.

The original question asked about hiding the id column within the table. Visibility is not something which is expressed by the JavaScript object format, so we need an additional way to determine which fields should be visible/hidden. Metawidget emphasises using your existing architecture, so it provides lots of options for how to do this. For example, you could plug in an InspectionResultProcessor that hides any field called id. Or perhaps any field whose name starts with _. Here's an alternate approach, plugging in a JsonSchemaInspector to supply the missing metadata:

<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="app">
   <head>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js" type="text/javascript"></script>
      <script src="http://metawidget.org/js/4.2/metawidget-core.min.js" type="text/javascript"></script>
      <script src="http://metawidget.org/js/4.2/metawidget-angular.min.js" type="text/javascript"></script>
      <script>
         angular.module( 'app', [ 'metawidget' ] ).controller( 'myController', function( $scope ) {

            $scope.metawidgetConfig = {
               inspector: new metawidget.inspector.CompositeInspector( [
                  new metawidget.inspector.PropertyTypeInspector(),
                  new metawidget.inspector.JsonSchemaInspector( {
                     properties: {
                        children: {
                           items: {
                              properties: {
                                 id: {
                                    hidden: true
                                 }
                              }
                           }
                        }
                     }
                  } )
               ] )
            };


            $scope.person = {
               firstname: 'Homer',
               age: 43,
               children: [ {
                  id: 1,
                  firstname: 'Bart',
                  age: 10
               }, {
                  id: 2,
                  firstname: 'Lisa',
                  age: 8
               } ]
            }
         } );
      </script>
   </head>
   <body ng-controller="myController">
      <metawidget ng-model="person" config="metawidgetConfig"></metawidget>
   </body>
</html>

This succeeds in hiding the id column:

Note we are using CompositeInspector and PropertyTypeInspector so that Metawidget is still extracting most of its metadata from the raw JavaScript object. This means we only have to specify additional metadata in JsonSchemaInspector, not redefine every field.

The original question also had an unusual, nested format for displaying names. Again, there are a few ways Metawidget can support this, depending on your preferred architecture. Here's an example that plugs in a WidgetBuilder to render the nested format.

<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="app">
   <head>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js" type="text/javascript"></script>
      <script src="http://metawidget.org/js/4.2/metawidget-core.min.js" type="text/javascript"></script>
      <script src="http://metawidget.org/js/4.2/metawidget-angular.min.js" type="text/javascript"></script>
      <script>
         angular.module( 'app', [ 'metawidget' ] ).controller( 'myController', function( $scope ) {

            $scope.metawidgetConfig = {
               inspector: new metawidget.inspector.CompositeInspector( [
                  new metawidget.inspector.PropertyTypeInspector(),
                  new metawidget.inspector.JsonSchemaInspector( {
                     properties: {
                        children: {
                           readOnly: true,
                           items: {
                              properties: {
                                 id: {
                                    hidden: true
                                 }
                              }
                           }
                        }
                     }
                  } )
               ] ),
               widgetBuilder: new metawidget.widgetbuilder.CompositeWidgetBuilder( [
                  function( elementName, attributes, mw ) {
                     if ( attributes.name === 'name' ) {
                        return angular.element( '<output ng-bind="' + mw.path + '.firstname + \' \' + ' + mw.path + '.surname">' )[0];
                     }
                  },
                  new metawidget.widgetbuilder.HtmlWidgetBuilder()
               ] )

            };

            $scope.person = {
               firstname: 'Homer',
               age: 43,
               children: [ {
                  id: 1,
                  name: {
                      firstname: 'Bart',
                      surname: 'Simpson'
                   },

                  age: 10
               }, {
                  id: 2,
                  name: {
                      firstname: 'Lisa',
                      surname: 'Simpson'
                   },

                  age: 8
               } ]
            }
         } );
      </script>
   </head>
   <body ng-controller="myController">
      <metawidget ng-model="person" config="metawidgetConfig"></metawidget>
   </body>
</html>

Note we are using CompositeWidgetBuilder and HtmlWidgetBuilder so that Metawidget is still building most widgets automatically. This means we only have to specify the additional widget handling, not redefine every widget. In this example, we choose our new widget based on the name of the field. But you could use any of the attributes Metawidget inspects (including custom ones you define):

Finally, a follow up question asked how to display only the table, and none of the surrounding fields. This needs a change to the ng-model path, and also we should swap out the default TableLayout (which wraps widgets with labels in a table) for a layout that doesn't wrap the widgets at all:

<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="app">
   <head>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js" type="text/javascript"></script>
      <script src="http://metawidget.org/js/4.2/metawidget-core.min.js" type="text/javascript"></script>
      <script src="http://metawidget.org/js/4.2/metawidget-angular.min.js" type="text/javascript"></script>
      <script>
         angular.module( 'app', [ 'metawidget' ] ).controller( 'myController', function( $scope ) {

            $scope.metawidgetConfig = {
               inspector: new metawidget.inspector.CompositeInspector( [
                  new metawidget.inspector.PropertyTypeInspector(),
                  new metawidget.inspector.JsonSchemaInspector( {
                     properties: {
                        children: {
                           readOnly: true,
                           items: {
                              properties: {
                                 id: {
                                    hidden: true
                                 }
                              }
                           }
                        }
                     }
                  } )
               ] ),
               widgetBuilder: new metawidget.widgetbuilder.CompositeWidgetBuilder( [
                  function( elementName, attributes, mw ) {
                     if ( mw.path.indexOf( 'person.children' ) === 0 && attributes.name === 'name' ) {
                        return angular.element( '<output ng-bind="' + mw.path + '.firstname + \' \' + ' + mw.path + '.surname">' )[0];
                     }
                  },
                  new metawidget.widgetbuilder.HtmlWidgetBuilder()
               ] ),
               layout: new metawidget.layout.SimpleLayout()
            };

            $scope.person = {
               firstname: 'Homer',
               age: 43,
               children: [ {
                  id: 1,
                  name: {
                     firstname: 'Bart',
                     surname: 'Simpson'
                  },
                  age: 10
               }, {
                  id: 2,
                  name: {
                     firstname: 'Lisa',
                     surname: 'Simpson'
                  },
                  age: 8
               } ]
            }
         } );
      </script>
   </head>
   <body ng-controller="myController">
      <metawidget ng-model="person.children" config="metawidgetConfig"></metawidget>
   </body>
</html>

Hope that helps!