'use strict'; /* Load the UI module. */ Ox.load('UI', function() { /* Create our own namespace. */ Ox.My = {}; /* First, lets build the most basic Box widget. A widget is a "constructor" function that takes two (optional) arguments, `options` and `self`, and returns a widget object. It's not a constructor in JavaScript terms though: It doesn't have to be called with `new`, and doesn't return an instance of anything. It just enhances another widget object and returns it. */ Ox.My.Box = function(options, self) { /* This is how every widget "constructor" begins. `self` is the widget's shared private object. It is private since it can only be accessed inside the widget, but shared because it can be passed to a widget upon its creation. */ self = self || {}; /* `that` is the widget itself, its public object, or, in JavaScript terms, its `this`. Every widget "inherits" from another widget, by simple assignment. All public properties of the "super" widget, i.e. all properties of its `that`, will be present on our own `that`. In this case, we use Ox.Element, the "root" widget at the end of the inheritance chain, and pass an empty options object. But we always pass our own `self`, which means that any property that Ox.Element (or any other widget in the inheritance chain) adds to self will be present on our own `self`. Then we call the public `defaults` and `options` methods. `defaults` assigns the defaults object to `self.defaults` and copies it to `self.options`, then `options` extends `self.options` with the options object. */ var that = Ox.Element({}, self) .defaults({ color: [128, 128, 128], size: 128 }) .options(options || {}) .update({ color: setColor, size: setSize }); /* Ox.Element, and every widget that inherits from it, has a private `self.setOption` method that is invoked whenever, by way of calling the public `that.options` method, a property of `self.options` is modified or added. If we don't want to react to such changes, we can leave `self.setOption` untouched. In this case though, we want to update color and size, so we just overwrite `self.setOption`. */ /* The following part of the "constructor" function can be thought of as the "initializer", and contains everything needed to set up the "instance". In this case, we just define a minimum and maximum size and then set the widget's color and size. We could have used `var minSize` and `var maxSize` here, but by using `self` for private variables that we want to be accessible across all the widget's methods, we can be sure that inside such methods, any local `var` is actually local to the method. We could have set color and size directly, too, but as we also need to set them in `self.setOption`, we create methods for both cases. */ self.minSize = 128; self.maxSize = 384; setColor(); setSize(); /* Now we declare the widget's private methods. These are simple function declarations, hoisted to the top of the "constructor". */ function setColor() { /* To interact with the DOM, Ox.Element (and any widget derived from it) wraps jQuery. If you type `Ox.Element()` in the console, you will get something like `[
]`, and the widget's prototype has all the methods of a `$('
')`, with proper chaining. If you have `var $d = $('
'), $e = Ox.Element();`, then `$d.appendTo($e)` returns `$d`, and `$e.append($d)` returns `$e`. */ that.css({ backgroundColor: 'rgb(' + self.options.color.join(', ') + ')', }); } function setSize() { /* Before setting the size, we make sure the value is between minSize and maxSize. */ self.options.size = Ox.limit( self.options.size, self.minSize, self.maxSize ); that.css({ height: self.options.size + 'px', width: self.options.size + 'px' }); } /* Next, we define the widgets public methods, as properties of `that`. (Note that unlike private methods, they are not hoisted.) */ that.showOptions = function() { /* As there isn't much to do yet, this method just displays the widget's options. */ that.html(JSON.stringify(self.options).replace(/([,:])/g, '$1 ')); /* Public methods should return `that`, for chaining. */ return that; }; /* At the very end of the "constructor", we always return `that`. And that's it. */ return that; }; /* `Ox.My.Box({size: 256}).appendTo(Ox.$body).showOptions()` */ Ox.My.RoundedBox = function(options, self) { self = self || {}; var that = Ox.My.Box({}, self); that.defaults(Ox.extend(that.defaults(), { radius: 16 })) .options(options || {}) .update(function(key, value) { if (key == 'radius') { setRadius(); } }); setRadius(); function setRadius() { that.css({ MozBorderRadius: self.options.radius + 'px', OBorderRadius: self.options.radius + 'px', WebkitBorderRadius: self.options.radius + 'px' }); } return that; }; window.myBox = Ox.My.Box().appendTo(Ox.$body); window.myRoundedBox = Ox.My.RoundedBox().appendTo(Ox.$body); return; Ox.My.Box = function(options, self) { self = self || {}; var that = Ox.Element('
', self) .defaults({ color: [128, 128, 128], size: 128 }) .options(options || {}) .addClass('OxMyBox OxMonospace') .css({ color: 'white', padding: '16px', textShadow: '1px 1px 1px black' }) .bindEvent({ anyclick: focus, key_0: reset, key_equal: function() { Ox.print('+', self.options.size) setSize(self.options.size + 16, true); }, key_minus: function() { setSize(self.options.size - 16, true); } }); self.initialSize = self.options.size; self.minSize = 128; self.maxSize = 384; setColor(); setSize(self.options.size); showOptions(); function focus(e) { Ox.print(e.target); if (e.target == that.$element[0]) { that.gainFocus(); } } function setColor() { that.css({ backgroundColor: 'rgb(' + self.options.color.join(', ') + ')', }); } function setSize(size, animate) { var css; self.options.size = Ox.limit(size, self.minSize, self.maxSize); css = { height: self.options.size + 'px', width: self.options.size + 'px', }; Ox.print('SET SIZE', size, animate, self.minSize, self.maxSize, css) if (animate) { that.stop().animate(css, 100, showOptions); } else { that.css(css); } } function showOptions() { try { that.html(JSON.stringify(self.options).replace(/([,:])/g, '$1 ')); } catch(e) {} } function reset() { self.options = self.initialOptions; showOptions(); } self.setOption = function(key, value) { if (key == 'color') { setColor(); } else if (key == 'size') { setSize(value); } showOptions(); }; return that; }; Ox.My.RoundedBox = function(options, self) { self = self || {}; var that = Ox.My.Box({}, self); that.defaults(Ox.extend(that.defaults(), { radius: 16 })) .options(options || {}); setRadius(); function setRadius() { that.css({ MozBorderRadius: self.options.radius + 'px', OBorderRadius: self.options.radius + 'px', WebkitBorderRadius: self.options.radius + 'px' }); } self.setSuperOption = self.setOption; self.setOption = function(key, value) { if (key == 'radius') { setRadius(); } else { self.setSuperOption(key, value); } } return that; }; Ox.My.MetaBox = function(options, self) { self = self || {}; Ox.print('MB', options) var that = Ox.My.Box({}, self); that.defaults(Ox.extend(that.defaults(), { boxes: [] })) .options(options || {}); self.options.boxes.forEach(function(box) { that.append(box); }); return that; }; Ox.My.MetaBox({ boxes: [ Ox.My.Box({ color: [64, 128, 255] }), Ox.My.RoundedBox({ color: [255, 128, 64] }) ], size: 384 }) .appendTo(Ox.$body); });