Introduce widget class

Change-Id: I949cd46c48b9176c0b013c1c65f1fba949413c8e
diff --git a/dev/demo/plugin-client.html b/dev/demo/plugin-client.html
index e06b38f..52f35de 100644
--- a/dev/demo/plugin-client.html
+++ b/dev/demo/plugin-client.html
@@ -5,10 +5,21 @@
     <link type="text/css" rel="stylesheet" href="/css/kalamar.css" />
 
     <!-- load client javascript library -->
-    <script src="/js/src/plugin/client.js"></script>
+    <script src="/js/src/plugin/client.js" data-server="http://localhost:3003/"></script>
+    <style>
+      body, html {
+      padding: 0;
+      margin: 0;
+      box-sizing: border-box;
+      border-width: 0;
+      }
+    </style>
   </head>
-  <body>
+  <body style="background-color: yellow">
     <h2>Example Widget!</h2>
-    <a onclick="KorAPlugin.log(333, 'Huhu!')">Send log!</a>
+    <ul>
+      <li><a onclick="KorAPlugin.log(333, 'Huhu!')">Send log!</a></li>
+      <li><a onclick="KorAPlugin.resize()">Resize</a></li>
+    </ul>
   </body>
 </html>
diff --git a/dev/demo/plugin-serverdemo.js b/dev/demo/plugin-serverdemo.js
index d1e6e20..563f302 100644
--- a/dev/demo/plugin-serverdemo.js
+++ b/dev/demo/plugin-serverdemo.js
@@ -7,10 +7,12 @@
 
 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');
+    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
index f14a7f7..6918da4 100644
--- a/dev/js/src/plugin/client.js
+++ b/dev/js/src/plugin/client.js
@@ -8,6 +8,19 @@
  * @author Nils Diewald
  */
 
+/*
+* TODO:
+*   Some methods require bidirectional
+*   calling, like
+*   - getKoralQuery()
+*   this probably should be done using a callback,
+*   like fetch({data}, function () {}, '*'), that will
+*   add a unique ID to the message and will call on the cb
+*   once the answer to that message arrives.
+*/
+
+var cs = document.currentScript;
+
 (function () {
   "use strict";
 
@@ -24,16 +37,18 @@
      * Initialize plugin
      */
     _init : function () {
-      console.log('Init');
+      this.widgetID = window.name;
+      this.server = cs.getAttribute('data-server') || '*';
       this.resize();
       return this;
     },
 
     // Send a message
     _sendMsg : function (data) {
+      data["originID"] = this.widgetID;
 
       // TODO: This should send a correct origin
-      window.parent.postMessage(data, '*');
+      window.parent.postMessage(data, this.server);
     },
 
     /**
@@ -54,8 +69,6 @@
     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]);
diff --git a/dev/js/src/plugin/server.js b/dev/js/src/plugin/server.js
index 01dddac..db59422 100644
--- a/dev/js/src/plugin/server.js
+++ b/dev/js/src/plugin/server.js
@@ -8,9 +8,20 @@
  * @author Nils Diewald
  */
 
-define(["util"], function () {
+define(["plugin/widget", "util"], function (widgetClass) {
   "use strict";
 
+  // TODO:
+  //   This is a counter to limit acceptable incoming messages
+  //   to hundred. For every message, this will be decreased
+  //   (down to 0), for every second this will be increased
+  //   (up to 100).
+  var c = 100;
+
+  // Contains all widgets to address with
+  // messages to them
+  var widgets = {};
+
   return {
 
     /**
@@ -35,17 +46,22 @@
 
     /**
      * 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);
+      // Create a unique random ID per widget
+      var id = 'id-' + this._randomID();
+
+      // Create a new widget
+      var widget = widgetClass.create(src, id);
+
+      // Store the widget based on the identifier
+      widgets[id] = widget;
+
+      // Open widget in frontend
+      element.appendChild(
+        widget.element()
+      );
     },
 
     // Receive a call from an embedded iframe
@@ -53,30 +69,37 @@
       // Get event data
       var d = e.data;
 
-      // TODO: Check for e.origin!
+      // e.origin is probably set and okay
 
-      // TODO: Deal with mad iframes
-   
+      // TODO:
+      //   Deal with mad iframes
+
+      // Get the widget
+      var widget = widgets[d["originID"]];
+
+      // If the addressed widget does not exist - fail
+      if (!widget)
+        return;
+
+
       // 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');
+        widget.resize(d);
       }
 
       // Log message from iframe
       else if (d.action === 'log') {
         KorAP.log(d.code, d.msg);
-      }
+      };
+
+      // TODO:
+      //   Close
     },
 
-
-    // Resize the calling iframe
-    resize : function (iframe, d) {
-      iframe.style.height = d.height + 'px';
+    // Get a random identifier
+    _randomID : function () {
+      return randomID(20);
     }
   }
 });
diff --git a/dev/js/src/plugin/widget.js b/dev/js/src/plugin/widget.js
new file mode 100644
index 0000000..4c2f09c
--- /dev/null
+++ b/dev/js/src/plugin/widget.js
@@ -0,0 +1,54 @@
+/**
+ * The plugin system is based
+ * on registered widgets (iframes) from
+ * foreign services.
+ * The widget component represents a single iframe.
+ *
+ * @author Nils Diewald
+ */
+
+define(["util"], function () {
+  "use strict";
+
+  return {
+
+    /**
+     * Create new widget
+     */
+    create : function (src, id) {
+      return Object.create(this)._init(src, id);
+    },
+
+    _init : function (src, id) {
+      this.src = src;
+      this.id = id;
+      return this;
+    },
+
+    /**
+     * The element of the widget
+     */
+    element : function () {
+
+      if (this._element)
+        return this._element;
+
+      // Spawn new iframe
+      var i = document.createElement('iframe');
+      i.setAttribute('allowTransparency',"true");
+      i.setAttribute('frameborder', 0);
+      i.setAttribute('sandbox','allow-scripts');
+      i.classList.add('widget');
+      i.setAttribute('name', this.id);
+      i.setAttribute('src', this.src);
+      this._element = i;
+
+      return i;
+    },
+
+    // Resize iframe
+    resize : function (data) {
+      this._element.style.height = data.height + 'px';
+    }
+  }
+});
diff --git a/dev/js/src/util.js b/dev/js/src/util.js
index 5b6c8f8..80f6364 100644
--- a/dev/js/src/util.js
+++ b/dev/js/src/util.js
@@ -59,6 +59,24 @@
   return e.keyCode;
 };
 
+function _dec2hex (dec) {
+  return ('0' + dec.toString(16)).substr(-2)
+};
+
+
+/**
+ * Create random identifiers
+ */
+/*
+ * code based on
+ * https://stackoverflow.com/questions/1349404/generate-random-string-characters-in-javascript#8084248
+ */
+function randomID (len) {
+  var arr = new Uint8Array((len || 40) / 2)
+  window.crypto.getRandomValues(arr)
+  return Array.from(arr, _dec2hex).join('')
+};
+
 
 define(function () {
   // Todo: That's double now!