Initial approach to a widget based plugin system

Change-Id: I8ad07c817248a0d205ec9eb66f0dc579fbcd7160
diff --git a/dev/demo/plugin-client.html b/dev/demo/plugin-client.html
new file mode 100644
index 0000000..e06b38f
--- /dev/null
+++ b/dev/demo/plugin-client.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Plugin demo</title>
+    <link type="text/css" rel="stylesheet" href="/css/kalamar.css" />
+
+    <!-- load client javascript library -->
+    <script src="/js/src/plugin/client.js"></script>
+  </head>
+  <body>
+    <h2>Example Widget!</h2>
+    <a onclick="KorAPlugin.log(333, 'Huhu!')">Send log!</a>
+  </body>
+</html>
diff --git a/dev/demo/plugin-server.html b/dev/demo/plugin-server.html
new file mode 100644
index 0000000..f513b46
--- /dev/null
+++ b/dev/demo/plugin-server.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Plugin demo</title>
+    <link type="text/css" rel="stylesheet" href="/css/kalamar.css" />
+    <script data-main="/demo/plugin-serverdemo.js" src="/js/lib/require.js" async="async"></script>
+  </head>
+  <body>
+    <p>Start the demo server with <code>morbo -l 'http://*:3003' t/plugin-server.pl</code> and open <a href="http://localhost:3003/demo/plugin-server.html"><code>this website</code></a>.</p>
+    <main>
+      <div id="container"></div>
+    </main>
+  </body>
+</html>
diff --git a/dev/demo/plugin-serverdemo.js b/dev/demo/plugin-serverdemo.js
new file mode 100644
index 0000000..d1e6e20
--- /dev/null
+++ b/dev/demo/plugin-serverdemo.js
@@ -0,0 +1,16 @@
+requirejs.config({
+  baseUrl: '/js/src',
+  paths : {
+    'lib': '../lib'
+  }
+});
+
+define(['app/en','plugin/server','lib/domReady','init','hint/foundries/cnx'], function (lang, pluginClass, domReady) {
+  domReady(function () {
+    console.log("Los geht's");
+    var p = pluginClass.create();
+
+    // Open widget!
+    p.addWidget(document.getElementById('container'), 'http://localhost:3003/demo/plugin-client.html');
+  });
+});
diff --git a/dev/js/src/plugin/client.js b/dev/js/src/plugin/client.js
new file mode 100644
index 0000000..f14a7f7
--- /dev/null
+++ b/dev/js/src/plugin/client.js
@@ -0,0 +1,80 @@
+/**
+ * The plugin system is based
+ * on registered widgets (iframes) from foreign services.
+ * The client component is loaded independently
+ * in a plugin and communicates with the embedding
+ * KorAP service.
+ *
+ * @author Nils Diewald
+ */
+
+(function () {
+  "use strict";
+
+  var obj = {
+
+    /**
+     * Create new plugin
+     */
+    create : function () {
+      return Object.create(this)._init();
+    },
+
+    /*
+     * Initialize plugin
+     */
+    _init : function () {
+      console.log('Init');
+      this.resize();
+      return this;
+    },
+
+    // Send a message
+    _sendMsg : function (data) {
+
+      // TODO: This should send a correct origin
+      window.parent.postMessage(data, '*');
+    },
+
+    /**
+     * Send a log message to the embedding KorAP
+     */
+    log : function (code, msg) {
+      this._sendMsg({
+        action : 'log',
+        code : code,
+        msg : msg
+      });
+    },
+
+    /**
+     * Send a resize command to the
+     * embedding KorAP
+     */
+    resize : function () {
+      var body = document.body;
+
+      console.log('Resize');
+
+      // recognize margin of first element
+      // (don't know why in FF)
+      var cs = getComputedStyle(body.children[0]);
+
+      var offsetHeight = parseInt(body.offsetHeight) +
+          parseInt(cs.getPropertyValue("margin-top")) +
+          parseInt(cs.getPropertyValue("margin-bottom"));
+
+      this._sendMsg({
+        'action' : 'resize',
+        'height' : offsetHeight
+      });
+    }
+  };
+
+  // Create plugin on windows load
+  window.onload = function () {
+    window.KorAPlugin = window.KorAPlugin || obj.create();
+  };
+})();
+
+
diff --git a/dev/js/src/plugin/server.js b/dev/js/src/plugin/server.js
new file mode 100644
index 0000000..01dddac
--- /dev/null
+++ b/dev/js/src/plugin/server.js
@@ -0,0 +1,82 @@
+/**
+ * The plugin system is based
+ * on registered widgets (iframes) from
+ * foreign services.
+ * The server component spawns new iframes and
+ * listens to them.
+ *
+ * @author Nils Diewald
+ */
+
+define(["util"], function () {
+  "use strict";
+
+  return {
+
+    /**
+     * Create new plugin management system
+     */
+    create : function () {
+      return Object.create(this)._init();
+    },
+
+    /*
+     * Initialize the plugin manager by establishing
+     * the global 'message' hook.
+     */
+    _init : function () {
+
+      var that = this;
+      window.addEventListener("message", function (e) {
+        that._receiveMsg(e);
+      });
+      return this;
+    },
+
+    /**
+     * Open a new widget on a certain element
+     * TODO: and register
+     */
+    addWidget : function (element, src) {
+
+      // Spawn new iframe
+      var iframe = element.addE('iframe');
+      iframe.setAttribute('allowTransparency',"true");
+      iframe.setAttribute('frameborder',0);
+      iframe.setAttribute('sandbox','allow-scripts');
+      iframe.classList.add('widget');
+      iframe.setAttribute('src', src);
+    },
+
+    // Receive a call from an embedded iframe
+    _receiveMsg : function (e) {
+      // Get event data
+      var d = e.data;
+
+      // TODO: Check for e.origin!
+
+      // TODO: Deal with mad iframes
+   
+      // Resize the iframe
+      if (d.action === 'resize') {
+
+        // TODO: Check which iframe it was
+        // var iframe = document.getElementById('?');
+
+        // this.resize(iframe, d);
+        console.log('Resizing not yet implemented');
+      }
+
+      // Log message from iframe
+      else if (d.action === 'log') {
+        KorAP.log(d.code, d.msg);
+      }
+    },
+
+
+    // Resize the calling iframe
+    resize : function (iframe, d) {
+      iframe.style.height = d.height + 'px';
+    }
+  }
+});
diff --git a/t/plugin-server.pl b/t/plugin-server.pl
new file mode 100644
index 0000000..974a33f
--- /dev/null
+++ b/t/plugin-server.pl
@@ -0,0 +1,7 @@
+use Mojolicious::Lite;
+
+my $base = app->home->child('..');
+
+push @{app->static->paths}, $base->child('dev');
+
+app->start;