-       /*
-        * Basic perspective.
-        */
-       $.wiki.Perspective = function(options) {
-               if(!options) return;
-
-               this.doc = options.doc;
-               if (options.id) {
-                       this.perspective_id = options.id;
-               }
-               else {
-                       this.perspective_id = '';
-               }
-
-               if(options.callback)
-                       options.callback.call(this);
-       };
-
-       $.wiki.Perspective.prototype.config = function() {
-               return $.wiki.state.perspectives[this.perspective_id];
-       }
-
-       $.wiki.Perspective.prototype.toString = function() {
-               return this.perspective_id;
-       };
-
-       $.wiki.Perspective.prototype.dirty = function() {
-               return true;
-       };
-
-       $.wiki.Perspective.prototype.onEnter = function () {
-               // called when perspective in initialized
-               if (!this.noupdate_hash_onenter) {
-                       document.location.hash = '#' + this.perspective_id;
-               }
-       };
-
-       $.wiki.Perspective.prototype.onExit = function () {
-               // called when user switches to another perspective
-               if (!this.noupdate_hash_onenter) {
-                       document.location.hash = '';
-               }
-       };
-
-       $.wiki.Perspective.prototype.destroy = function() {
-               // pass
-       };
-
-       $.wiki.Perspective.prototype.freezeState = function () {
-               // free UI state (don't store data here)
-       };
-
-       $.wiki.Perspective.prototype.unfreezeState = function (frozenState) {
-               // restore UI state
-       };
-
-       /*
-        * Stub rendering (used in generating history)
-        */
-       $.wiki.renderStub = function(params)
-       {
-               params = $.extend({ 'filters': {} }, params);
-               var $elem = params.stub.clone();
-               $elem.removeClass('row-stub');
-               params.container.append($elem);
-
-               $('*[data-stub-value]', $elem).each(function() {
-                       var $this = $(this);
-                       var field = $this.attr('data-stub-value');
-
-                       var value = params.data[field];
-
-                       if(params.filters[field])
-                               value = params.filters[field](value);
-
-                       if(value === null || value === undefined) return;
-
-                       if(!$this.attr('data-stub-target')) {
-                               $this.text(value);
-                       }
-                       else {
-                               $this.attr($this.attr('data-stub-target'), value);
-                               $this.removeAttr('data-stub-target');
-                               $this.removeAttr('data-stub-value');
-                       }
-               });
-
-               $elem.show();
-               return $elem;
-       };
-
-       /*
-        * Dialogs
-        */
-       function GenericDialog(element) {
-               if(!element) return;
-
-               var self = this;
-
-               self.$elem = $(element);
-
-               if(!self.$elem.attr('data-ui-initialized')) {
-                       console.log("Initializing dialog", this);
-                       self.initialize();
-                       self.$elem.attr('data-ui-initialized', true);
-               }
-
-               self.show();
-       };
-
-       GenericDialog.prototype = {
-
-               /*
-               * Steps to follow when the dialog in first loaded on page.
-               */
-               initialize: function(){
-                       var self = this;
-
-                       /* bind buttons */
-                       $('button[data-ui-action]', self.$elem).click(function(event) {
-                               event.preventDefault();
-
-                               var action = $(this).attr('data-ui-action');
-                               console.log("Button pressed, action: ", action);
-
-                               try {
-                                       self[action + "Action"].call(self);
-                               } catch(e) {
-                                       console.log("Action failed:", e);
-                                       // always hide on cancel
-                                       if(action == 'cancel')
-                                               self.hide();
-                               }
-                       });
-               },
-
-               /*
-                * Prepare dialog for user. Clear any unnessary data.
-               */
-               show: function() {
-                       $.blockUI({
-                               message: this.$elem,
-                               css: {
-                                    'top': '25%',
-                                    'left': '25%',
-                                    'width': '50%',
-                                    'max-height': '75%',
-                                    'overflow-y': 'scroll'
-                               }
-                       });
-               },
-
-               hide: function(){
-                       $.unblockUI();
-               },
-
-               cancelAction: function() {
-                       this.hide();
-               },
-
-               doneAction: function() {
-                       this.hide();
-               },
-
-               clearForm: function() {
-                       $("*[data-ui-error-for]", this.$elem).text('');
-               },
-
-               reportErrors: function(errors) {
-                       var global = $("*[data-ui-error-for='__all__']", this.$elem);
-                       var unassigned = [];
+    /*
+     * Basic perspective.
+     */
+    $.wiki.Perspective = class Perspective {
+        constructor(options) {
+            this.doc = options.doc;
+            this.perspective_id = options.id || ''
+        };
+
+        config() {
+            return $.wiki.state.perspectives[this.perspective_id];
+        }
+
+        toString() {
+            return this.perspective_id;
+        }
+
+        dirty() {
+            return true;
+        }
+
+        onEnter() {
+            // called when perspective in initialized
+            if (!this.noupdate_hash_onenter) {
+                document.location.hash = '#' + this.perspective_id;
+            }
+        }
+
+        onExit () {
+            // called when user switches to another perspective
+            if (!this.noupdate_hash_onenter) {
+                document.location.hash = '';
+            }
+        }
+
+        destroy() {
+            // pass
+        }
+    }
+
+    /*
+     * Stub rendering (used in generating history)
+     */
+    $.wiki.renderStub = function(params)
+    {
+        params = $.extend({ 'filters': {} }, params);
+        var $elem = params.stub.clone();
+        $elem.removeClass('row-stub');
+        params.container.append($elem);
+
+        var populate = function($this) {
+            var field = $this.attr('data-stub-value');
+
+            var value = params.data[field];
+
+            if(params.filters[field])
+                value = params.filters[field](value);
+
+            if(value === null || value === undefined) return;
+
+            if(!$this.attr('data-stub-target')) {
+                $this.text(value);
+            }
+            else {
+                $this.attr($this.attr('data-stub-target'), value);
+                $this.removeAttr('data-stub-target');
+                $this.removeAttr('data-stub-value');
+            }
+        }
+        if ($elem.attr('data-stub-value')) populate($elem);
+        $('*[data-stub-value]', $elem).each(function() {populate($(this))});
+
+        $elem.show();
+        return $elem;
+    };