From 685e412a11109820d692b596065f0d75e8a567f2 Mon Sep 17 00:00:00 2001 From: rolux Date: Mon, 28 May 2012 16:38:16 +0200 Subject: [PATCH] add example --- .../widget_design_patterns/css/example.css | 3 + examples/widget_design_patterns/index.html | 13 + examples/widget_design_patterns/js/example.js | 298 ++++++++++++++++++ 3 files changed, 314 insertions(+) create mode 100644 examples/widget_design_patterns/css/example.css create mode 100644 examples/widget_design_patterns/index.html create mode 100644 examples/widget_design_patterns/js/example.js diff --git a/examples/widget_design_patterns/css/example.css b/examples/widget_design_patterns/css/example.css new file mode 100644 index 00000000..3c00d862 --- /dev/null +++ b/examples/widget_design_patterns/css/example.css @@ -0,0 +1,3 @@ +OxMyBox.focused { + box-shadow: inset 0 0 1px black; +} \ No newline at end of file diff --git a/examples/widget_design_patterns/index.html b/examples/widget_design_patterns/index.html new file mode 100644 index 00000000..e551837e --- /dev/null +++ b/examples/widget_design_patterns/index.html @@ -0,0 +1,13 @@ + + + + Widget Design Patterns + + + + + + + + + \ No newline at end of file diff --git a/examples/widget_design_patterns/js/example.js b/examples/widget_design_patterns/js/example.js new file mode 100644 index 00000000..19dd77d1 --- /dev/null +++ b/examples/widget_design_patterns/js/example.js @@ -0,0 +1,298 @@ +'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 || {}); + + /* + 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`. + */ + self.setOption = function(key, value) { + if (key == 'color') { + setColor(); + } else if (key == 'size') { + setSize(); + } + }; + + /* + 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()` + */ + + window.myBox = Ox.My.Box().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); + +}); \ No newline at end of file