From d60af285c3702e1935861e88b02b4e92b2844564 Mon Sep 17 00:00:00 2001
From: ido <idok@wix.com>
Date: Wed, 10 Dec 2014 14:14:22 +0200
Subject: [PATCH] add panel with errors

---
 playground/CodeMirrorEditor.js                | 30 ++++--
 playground/css/fiddle.css                     |  9 ++
 .../codemirror-4.8/addon/display/panel.js     | 94 +++++++++++++++++++
 playground/playground-fiddle.rt               |  4 +-
 playground/playground-fiddle.rt.js            |  2 +
 playground/playground.js                      | 35 +++++--
 6 files changed, 156 insertions(+), 18 deletions(-)
 create mode 100644 playground/libs/codemirror-4.8/addon/display/panel.js

diff --git a/playground/CodeMirrorEditor.js b/playground/CodeMirrorEditor.js
index 299e9f0..ab8720b 100644
--- a/playground/CodeMirrorEditor.js
+++ b/playground/CodeMirrorEditor.js
@@ -1,14 +1,15 @@
-define(['react', 'lodash', './libs/codemirror-4.8/lib/codemirror',
+define(['react', 'lodash', 'jquery', './libs/codemirror-4.8/lib/codemirror',
         './libs/codemirror-4.8/mode/javascript/javascript',
         './libs/codemirror-4.8/addon/hint/html-hint',
         './libs/codemirror-4.8/addon/hint/show-hint',
         './libs/codemirror-4.8/addon/hint/xml-hint',
         './libs/codemirror-4.8/addon/hint/html-hint',
+        './libs/codemirror-4.8/addon/display/panel',
         './libs/codemirror-4.8/mode/xml/xml',
         './libs/codemirror-4.8/mode/css/css',
         './libs/codemirror-4.8/mode/htmlmixed/htmlmixed'
         //'./libs/codemirror-4.8/addon/display/placeholder'
-], function (React, _, CodeMirror) {
+], function (React, _, $, CodeMirror) {
     'use strict';
     //codeMirror: 'libs/codemirror-4.8/lib/codemirror',
     //htmlmixed: 'libs/codemirror-4.8/mode/htmlmixed/htmlmixed',
@@ -33,7 +34,7 @@ define(['react', 'lodash', './libs/codemirror-4.8/lib/codemirror',
     });
 
     function completeAfter(cm, pred) {
-        var cur = cm.getCursor();
+        //var cur = cm.getCursor();
         if (!pred || pred()) {
             setTimeout(function () {
                 if (!cm.state.completionActive) {
@@ -47,6 +48,7 @@ define(['react', 'lodash', './libs/codemirror-4.8/lib/codemirror',
     function completeIfAfterLt(cm) {
         return completeAfter(cm, function () {
             var cur = cm.getCursor();
+            /*eslint new-cap:0*/
             return cm.getRange(CodeMirror.Pos(cur.line, cur.ch - 1), cur) === '<';
         });
     }
@@ -70,7 +72,6 @@ define(['react', 'lodash', './libs/codemirror-4.8/lib/codemirror',
             };
         },
         componentWillMount: function () {
-
         },
         render: function () {
             var props = _.omit(this.props, ['ref', 'key', 'value', 'valueLink', 'onChange']);
@@ -79,7 +80,7 @@ define(['react', 'lodash', './libs/codemirror-4.8/lib/codemirror',
         },
         componentWillUpdate: function (nextProps/*, nextState*/) {
             var value = nextProps.valueLink ? nextProps.valueLink() : nextProps.value;
-            if (this.editor && this.editor.getValue() != value) {
+            if (this.editor && this.editor.getValue() !== value) {
                 this.editor.setValue(value || '');
             }
         },
@@ -88,10 +89,12 @@ define(['react', 'lodash', './libs/codemirror-4.8/lib/codemirror',
             var options = {
                 readOnly: this.props.readOnly,
                 lineWrapping: true,
+                smartIndent: true,
+                matchBrackets: true,
                 value: value,
                 lineNumbers: true,
                 mode: 'javascript',
-                theme: 'solarized' //solarized_light
+                theme: 'solarized' //solarized_light solarized-light
             };
 
             if (this.props.mode === 'html') {
@@ -124,6 +127,21 @@ define(['react', 'lodash', './libs/codemirror-4.8/lib/codemirror',
                 }.bind(this));
             }
         },
+        showMessage: function (msg) {
+            var anOption = document.createElement('div');
+            anOption.innerText = msg;
+            anOption.setAttribute('class', 'error-panel');
+            if (this.panel) {
+                this.panel.clear();
+            }
+            this.panel = this.editor.addPanel(anOption, {height: 22}); // {position: 'bottom'}
+        },
+        clearMessage: function () {
+            if (this.panel) {
+                this.panel.clear();
+                this.panel = null;
+            }
+        },
         componentWillUnmount: function () {
             this.editor.destroy();
         }
diff --git a/playground/css/fiddle.css b/playground/css/fiddle.css
index e384a4f..c3acf17 100644
--- a/playground/css/fiddle.css
+++ b/playground/css/fiddle.css
@@ -125,4 +125,13 @@ body {
     padding: 1px;
 }
 
+.error-panel {
+    /*padding: 4px; */
+    padding-left: 20px;
+    /* position: static; */
+    color: red;
+    background: #f7f7f7;
+    border-bottom: 1px solid #000;
+}
+
 /*position: relative; min-height: 100%;*/
\ No newline at end of file
diff --git a/playground/libs/codemirror-4.8/addon/display/panel.js b/playground/libs/codemirror-4.8/addon/display/panel.js
new file mode 100644
index 0000000..dbe684b
--- /dev/null
+++ b/playground/libs/codemirror-4.8/addon/display/panel.js
@@ -0,0 +1,94 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+    if (typeof exports == "object" && typeof module == "object") // CommonJS
+        mod(require("../../lib/codemirror"));
+    else if (typeof define == "function" && define.amd) // AMD
+        define(["../../lib/codemirror"], mod);
+    else // Plain browser env
+        mod(CodeMirror);
+})(function(CodeMirror) {
+    CodeMirror.defineExtension("addPanel", function(node, options) {
+        if (!this.state.panels) initPanels(this);
+
+        var info = this.state.panels;
+        if (options && options.position == "bottom")
+            info.wrapper.appendChild(node);
+        else
+            info.wrapper.insertBefore(node, info.wrapper.firstChild);
+        var height = (options && options.height) || node.offsetHeight;
+        this._setSize(null, info.heightLeft -= height);
+        info.panels++;
+        return new Panel(this, node, options, height);
+    });
+
+    function Panel(cm, node, options, height) {
+        this.cm = cm;
+        this.node = node;
+        this.options = options;
+        this.height = height;
+        this.cleared = false;
+    }
+
+    Panel.prototype.clear = function() {
+        if (this.cleared) return;
+        this.cleared = true;
+        var info = this.cm.state.panels;
+        this.cm._setSize(null, info.heightLeft += this.height);
+        info.wrapper.removeChild(this.node);
+        if (--info.panels == 0) removePanels(this.cm);
+    };
+
+    Panel.prototype.changed = function(height) {
+        var newHeight = height == null ? this.node.offsetHeight : height;
+        var info = this.cm.state.panels;
+        this.cm._setSize(null, info.height += (newHeight - this.height));
+        this.height = newHeight;
+    };
+
+    function initPanels(cm) {
+        var wrap = cm.getWrapperElement();
+        var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle;
+        var height = parseInt(style.height);
+        var info = cm.state.panels = {
+            setHeight: wrap.style.height,
+            heightLeft: height,
+            panels: 0,
+            wrapper: document.createElement("div")
+        };
+        wrap.parentNode.insertBefore(info.wrapper, wrap);
+        var hasFocus = cm.hasFocus();
+        info.wrapper.appendChild(wrap);
+        if (hasFocus) cm.focus();
+
+        cm._setSize = cm.setSize;
+        if (height != null) cm.setSize = function(width, newHeight) {
+            if (newHeight == null) return this._setSize(width, newHeight);
+            info.setHeight = newHeight;
+            if (typeof newHeight != "number") {
+                var px = /^(\d+\.?\d*)px$/.exec(newHeight);
+                if (px) {
+                    newHeight = Number(px[1]);
+                } else {
+                    info.wrapper.style.height = newHeight;
+                    newHeight = info.wrapper.offsetHeight;
+                    info.wrapper.style.height = "";
+                }
+            }
+            cm._setSize(width, info.heightLeft += (newHeight - height));
+            height = newHeight;
+        };
+    }
+
+    function removePanels(cm) {
+        var info = cm.state.panels;
+        cm.state.panels = null;
+
+        var wrap = cm.getWrapperElement();
+        info.wrapper.parentNode.replaceChild(wrap, info.wrapper);
+        wrap.style.height = info.setHeight;
+        cm.setSize = cm._setSize;
+        cm.setSize();
+    }
+});
\ No newline at end of file
diff --git a/playground/playground-fiddle.rt b/playground/playground-fiddle.rt
index e435e26..36ded82 100644
--- a/playground/playground-fiddle.rt
+++ b/playground/playground-fiddle.rt
@@ -3,13 +3,13 @@
 <div class="playground">
     <div class="fiddle-row">
         <div class="code-area">
-            <CodeEditor id="editor-rt" class="large-text-area" style="border: {this.validHTML ? '1px solid black' : '2px solid red'};"
+            <CodeEditor ref="editorRT" id="editor-rt" class="large-text-area" style="border: {this.validHTML ? '1px solid black' : '2px solid red'};"
                         value="{this.state.templateHTML}"
                         mode="html"
                         onChange="(evt) => this.setState({'templateHTML':evt.target.value})" />
         </div>
         <div class="code-area">
-            <CodeEditor id="editor-code" class="large-text-area" style="border: {this.validProps ? '1px solid black' : '2px solid red'};"
+            <CodeEditor ref="editorCode" id="editor-code" class="large-text-area" style="border: {this.validProps ? '1px solid black' : '2px solid red'};"
                         value="{this.state.templateProps}"
                         mode="javascript"
                         onChange="(evt) => this.setState({'templateProps':evt.target.value})" />
diff --git a/playground/playground-fiddle.rt.js b/playground/playground-fiddle.rt.js
index 3ef6fd1..8c3a200 100644
--- a/playground/playground-fiddle.rt.js
+++ b/playground/playground-fiddle.rt.js
@@ -16,6 +16,7 @@ define([
     }
     return function () {
         return React.createElement('div', { 'className': 'playground' }, React.createElement('div', { 'className': 'fiddle-row' }, React.createElement('div', { 'className': 'code-area' }, React.createElement(CodeEditor, {
+            'ref': 'editorRT',
             'id': 'editor-rt',
             'className': 'large-text-area',
             'style': { border: this.validHTML ? '1px solid black' : '2px solid red' },
@@ -23,6 +24,7 @@ define([
             'mode': 'html',
             'onChange': onChange1.bind(this)
         })), React.createElement('div', { 'className': 'code-area' }, React.createElement(CodeEditor, {
+            'ref': 'editorCode',
             'id': 'editor-code',
             'className': 'large-text-area',
             'style': { border: this.validProps ? '1px solid black' : '2px solid red' },
diff --git a/playground/playground.js b/playground/playground.js
index c75bf2b..bfab186 100644
--- a/playground/playground.js
+++ b/playground/playground.js
@@ -1,20 +1,29 @@
-'use strict';
 /*eslint-env browser*/
 define(['react', 'lodash', './playground-fiddle.rt', './playground.rt'], function (React, _, pgFiddleTemplate, playgroundTemplate) {
+    'use strict';
     function emptyFunc() {
         return null;
     }
 
-    function generateTemplateSource(html) {
+    function generateTemplateSource(html, editor) {
         var code = null;
         try {
             code = window.reactTemplates.convertTemplateToReact(html.trim().replace(/\r/g, ''));
-        } catch (e) {
-            if (e.name === 'RTCodeError') {
-                console.log('');
-                //index: -1 line: -1 message: "Document should have a root element" name: "RTCodeError"
+            if (editor) {
+                editor.clearMessage();
             }
-            console.log('' + e);
+        } catch (e) {
+            var msg;
+            if (e.name === 'RTCodeError') {
+                //index: -1 line: -1 message: "Document should have a root element" name: "RTCodeError"
+                msg = e.message + ', line: ' + e.line;
+            } else {
+                msg = e.message;
+            }
+            if (editor) {
+                editor.showMessage(msg);
+            }
+            console.log(e);
         }
         return code;
     }
@@ -32,7 +41,7 @@ define(['react', 'lodash', './playground-fiddle.rt', './playground.rt'], functio
             var res = eval(code);
             return res;
         } catch (e) {
-            console.log('' + e);
+            console.log(e);
             return emptyFunc;
         }
     }
@@ -72,8 +81,8 @@ define(['react', 'lodash', './playground-fiddle.rt', './playground.rt'], functio
             };
         },
         updateSample: function (state) {
-            this.templateSource = generateTemplateSource(state.templateHTML);
-            this.sampleFunc = generateTemplateFunction(this.templateSource);
+            this.templateSource = generateTemplateSource(state.templateHTML, this.refs.editorRT);
+            this.sampleFunc = generateTemplateFunction(this.templateSource, this.refs.editorCode);
             this.validHTML = this.sampleFunc !== emptyFunc;
             this.sampleRender = generateRenderFunc(this.sampleFunc);
             var classBase = {};
@@ -84,9 +93,15 @@ define(['react', 'lodash', './playground-fiddle.rt', './playground.rt'], functio
                 if (!_.isObject(classBase)) {
                     throw 'failed to eval';
                 }
+                if (this.refs.editorCode) {
+                    this.refs.editorCode.clearMessage();
+                }
             } catch (e) {
                 classBase = {};
                 this.validProps = false;
+                if (this.refs.editorCode) {
+                    this.refs.editorCode.showMessage(e.message);
+                }
             }
             classBase.render = this.sampleRender;
             this.sample = React.createFactory(React.createClass(classBase));