From ef340c908581904534dc0640587148cf2a0aaeb3 Mon Sep 17 00:00:00 2001
From: Petr Vobornik <pvoborni@redhat.com>
Date: Thu, 3 May 2012 21:02:56 +0200
Subject: [PATCH 123/124] IFW base - classes, builders

---
 install/ui/framework.js              |  248 ++++++++++++++++++++++++++++++++++
 install/ui/jsl.conf                  |    1 +
 install/ui/test/framework_tests.html |   22 +++
 install/ui/test/framework_tests.js   |   82 +++++++++++
 install/ui/test/jsl.conf             |    4 +-
 5 files changed, 356 insertions(+), 1 deletions(-)
 create mode 100644 install/ui/framework.js
 create mode 100644 install/ui/test/framework_tests.html
 create mode 100644 install/ui/test/framework_tests.js

diff --git a/install/ui/framework.js b/install/ui/framework.js
new file mode 100644
index 0000000000000000000000000000000000000000..ebd8df9bfca7c0a260bdb94fd99098adcc49bf38
--- /dev/null
+++ b/install/ui/framework.js
@@ -0,0 +1,248 @@
+/*jsl:import jquery.ordered-map.js */
+/*  Authors:
+ *    Petr Vobornik <pvoborni@redhat.com>
+ *
+ * Copyright (C) 2012 Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* REQUIRES: jquery.ordered-map.js */
+/*global $:true, location:true */
+
+var IFW = {}; //Ipa FrameWork
+
+
+//
+// Classes
+//
+
+
+//type collection
+IFW.types = $.ordered_map();
+
+IFW.register = function(class_name, builder) {
+
+    IFW.types.put(class_name, builder);
+};
+
+IFW.unregister = function(class_name) {
+    IFW.types.remove(class_name);
+};
+
+IFW.event = function() {
+    return {};
+};
+
+//creates base object - Framework's root class
+IFW.object = function() {
+
+    var that = {};
+
+    //private function container to avoid name conflicts
+    that._ = {
+
+        //reference to containing object
+        obj: that,
+
+        //current class name
+        klass: null,
+
+        //parent class name
+        ancestor: null,
+
+        //class catalogue
+        classes: $.ordered_map(),
+
+        //event catalogue
+        events: $.ordered_map(),
+
+        //raises existing event
+        raise: function(event_name, args) {
+
+            var event = this.events[event_name];
+
+            if (event) event.notify(args);
+        },
+
+        //registers event
+        event: function(name) {
+
+            var event = IFW.event();
+
+            this.obj[name] = event;
+            this.events.put(name, event);
+        },
+
+        //registers new method for current class. A way of overriding methods.
+        method: function(name, func, pre_event, post_event) {
+
+            //to 'that' objects sets functions which raises preevent then executes
+            //function in 'that' object context and finishes with raising post event
+
+            //this way each subclass can easily register a method with the same
+            //name as ancestor class. In method's function it can call ancestor
+            //method. And the nice part is that events are raised in proper way
+            //regardless whether ancestor method is called.
+            var _ = this;
+            this.obj[name] = function(args) {
+
+                if (pre_event) {
+                    _.raise('pre'+name, args);
+                }
+
+                func.call(_.obj, args);
+
+                if (post_event) {
+                    _.raise('post'+name, args);
+                }
+            };
+
+            //register method to catalogue
+            var klass = this.classes.get(this.klass);
+            klass.methods.put(name, func);
+        },
+
+        //calls some ancestor class or event this class method with given args
+        //usufull for method overriding
+        call: function(class_name, method_name) {
+
+            var klass = this.classes.get(class_name);
+            if (!klass) throw 'object is not of '+ class_name +'type';
+
+            var method = klass.methods.get(method_name);
+            if (!method) throw class_name + ' doesn\'t have method: '+method_name;
+
+            var args = Array.prototype.slice.call(arguments, 2);
+            method.apply(this.obj, args);
+        },
+
+        //shortcut for parent method call
+        parent: function(method_name) {
+
+            var args = Array.prototype.slice.call(arguments);
+            args.unshift(this.ancestor);
+            this.call.apply(this, args);
+        },
+
+        //intializes the class
+        inherit: function(name) {
+
+            this.ancestor = this.klass;
+            this.klass = name;
+
+            this.classes.put(name, {
+
+                //class name
+                name: name,
+
+                //rememeber ancestor
+                ancestor: this.ancestor,
+
+                //method catalogue
+                methods: $.ordered_map()
+            });
+        }
+    };
+
+    that._.inherit('object'); //initialization
+    return that;
+};
+
+//main function for creating new classes
+IFW.type = function(class_name, ancestor) {
+
+    if (!ancestor) ancestor = IFW.object;
+
+    var that = ancestor();
+    that._.inherit(class_name);
+    return that;
+};
+
+//
+// Builders
+//
+
+IFW.builder = function(spec) {
+
+    spec = spec || {};
+
+    var that = {};
+
+    that.factory = spec.factory || IFW.default_factory;
+
+    that.build = function(spec) {
+
+        var factory = spec.factory || that.factory;
+
+        //when spec is a factory function
+        if (!spec.factory && typeof spec === 'function') {
+            factory = spec;
+            spec = {};
+        }
+
+        var obj = factory();
+
+        if (typeof obj.preinit === 'function') obj.preinit(spec);
+        if (typeof obj.init === 'function') obj.init(spec);
+        if (typeof obj.postinit === 'function') obj.postinit(spec);
+
+        return obj;
+    };
+
+    that.build_objects = function(specs) {
+
+        var objects = [];
+
+        for (var i=0; i<specs.length; i++) {
+            var spec = specs[i];
+            var obj = that.build(spec);
+            objects.push(obj);
+        }
+
+        return objects;
+    };
+
+    return that;
+};
+
+IFW.build = function(spec, builder_fac) {
+
+    if (!spec) return null;
+
+    if (!builder_fac) builder_fac = IFW.builder;
+
+    var builder = builder_fac();
+    var product;
+
+    if ($.isArray(spec)) {
+        product = builder.build_objects(spec);
+    } else {
+        product = builder.build(spec);
+    }
+
+    return product;
+};
+
+IFW.default_factory = function(spec) {
+
+    spec = spec || {};
+
+    var that = {};
+
+    $.extend(that, spec);
+
+    return that;
+};
diff --git a/install/ui/jsl.conf b/install/ui/jsl.conf
index 3eebda4e8f6f6cb9ed9b3b502feec3558d723bc4..9572b6a66ef4ab031beea856a88ea99009b2e409 100644
--- a/install/ui/jsl.conf
+++ b/install/ui/jsl.conf
@@ -126,6 +126,7 @@
 # or "+process Folder\Path\*.htm".
 #
 +process jquery.ordered-map.js
++process framework.js
 +process ipa.js
 +process widget.js
 +process field.js
diff --git a/install/ui/test/framework_tests.html b/install/ui/test/framework_tests.html
new file mode 100644
index 0000000000000000000000000000000000000000..ecac007a72407a2cc08e807abdbc111ff1b6635e
--- /dev/null
+++ b/install/ui/test/framework_tests.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Framework test suite</title>
+    <link rel="stylesheet" href="qunit.css" type="text/css" media="screen">
+    <script type="text/javascript" src="qunit.js"></script>
+    <script type="text/javascript" src="../jquery.js"></script>
+    <script type="text/javascript" src="../jquery.ba-bbq.js"></script>
+    <script type="text/javascript" src="../jquery-ui.js"></script>
+    <script type="text/javascript" src="../jquery.ordered-map.js"></script>
+    <script type="text/javascript" src="../framework.js"></script>
+    <script type="text/javascript" src="framework_tests.js"></script>
+</head>
+<body>
+    <h1 id="qunit-header">Framework test suite</h1>
+    <h2 id="qunit-banner"></h2>
+    <div id="qunit-testrunner-toolbar"></div>
+    <h2 id="qunit-userAgent"></h2>
+    <ol id="qunit-tests"></ol>
+    <div id="qunit-fixture"></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/install/ui/test/framework_tests.js b/install/ui/test/framework_tests.js
new file mode 100644
index 0000000000000000000000000000000000000000..e82a54a5f5c8a797eb639d13c61e0f6b74eb6854
--- /dev/null
+++ b/install/ui/test/framework_tests.js
@@ -0,0 +1,82 @@
+/*  Authors:
+ *    Petr Vobornik <pvoborni@redhat.com>
+ *
+ * Copyright (C) 2012 Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+var FOO = {}; //foo app
+
+module('framework',{
+    setup: function() {
+        FOO.facet = function() {
+
+            //this is a new class: facet
+            var that = IFW.type('facet');
+
+            //define method init in facet class
+            that._.method('init', function(spec) {
+
+                that.foo = spec.foo || 'facet_foo';
+                that.baz = spec.baz || 'facet_baz';
+                that.bar = spec.bar || 'facet_bar';
+            });
+
+            return that;
+        };
+
+        IFW.register('facet', FOO.facet);
+
+        FOO.details_facet = function() {
+
+            //new class details facet, inherits from FOO.facet
+            var that = IFW.type('details_facet', FOO.facet);
+
+            //define method init in details_facet class.
+            //this overrides init method form facet class
+            that._.method('init', function(spec) {
+
+                that._.parent('init', spec);
+
+                that.baz = spec.baz || 'details_baz';
+            });
+
+            return that;
+        };
+
+        IFW.register('details_facet', FOO.details_facet);
+    },
+    teardown: function() {
+
+        IFW.register('facet');
+        IFW.register('details_facet');
+    }
+});
+
+test("Build and inheritence" ,function() {
+
+    var details_facet = IFW.build({
+        factory: FOO.details_facet,
+        bar: 'custom_bar'
+    });
+
+    same(details_facet._.klass, 'details_facet', "Class");
+    same(details_facet._.ancestor, 'facet', "Ancestor");
+
+    same(details_facet.foo, 'facet_foo', "Property - ancestor's default");
+    same(details_facet.baz, 'details_baz', "Property - child's default");
+    same(details_facet.bar, 'custom_bar', "Property - custom");
+});
diff --git a/install/ui/test/jsl.conf b/install/ui/test/jsl.conf
index 4654b714f91cb00696043bf3042fccd2b2e394bc..a7352f70a2a55c829ee2841f20f65ab045f745a9 100644
--- a/install/ui/test/jsl.conf
+++ b/install/ui/test/jsl.conf
@@ -129,6 +129,7 @@
 +define IPA
 +define expect
 +define NET
++define IFW
 
 
 ### Files
@@ -146,4 +147,5 @@
 +process ipa_tests.js
 +process ordered_map_tests.js
 +process widget_tests.js
-+process ip_tests.js
\ No newline at end of file
++process ip_tests.js
++process framework_tests.js
\ No newline at end of file
-- 
1.7.7.6

