docs: function renderAttribute(attribute, mode) {
  
      var daList;
      var daItems;
      var formGroup;
      var label;
      var inputDiv;
      var input;
      var helpTextDiv;
      var sz;
      var button;
      var select;
      var textNode;
      var inputGroupDiv;
      var inputGroupSpan;
      var inputGroupButton;
      var inputGroupDropDownMenu;
      var initSwitchery;
      var items;
      var j;
      var validating;
  
      /**
       * Create formGroup container and label
       */
  
      formGroup = addEle(panel.panelForm, 'div', 'form-group');
      addEle(formGroup, 'label', 'col-sm-3 control-label').innerHTML = attribute.label;
  
      /**
       * Create inputDiv - set with of input based on size of field
       */
      sz = '1';
      if (attribute.size > 2) sz = '2';
      if (attribute.size > 5) sz = '3';
      if (attribute.size > 10) sz = '4';
      if (attribute.size > 20) sz = '5';
      if (attribute.size > 25) sz = '6';
      if (attribute.size > 30) sz = '7';
      if (attribute.size > 40) sz = '8';
      if (attribute.size > 50) sz = '9';
      if (attribute.type == 'Number') sz = '3';
      if (attribute.type == 'Date') sz = '3';
      if (attribute.type == 'Boolean') sz = '3';
      inputDiv = addEle(formGroup, 'div', 'col-sm-' + sz);
  
      /**
       * Render based on type
       */
      switch (mode + attribute.type) {
  
        case 'ViewBoolean':
          input = addEle(inputDiv, 'input', 'js-switch');
          input.setAttribute("type", "checkbox");
          if (attribute.value)
            input.setAttribute("checked", "true");
  
          initSwitchery = new Switchery(input, {
            //color: window.getComputedStyle(panel.panelTitle, null).getPropertyValue('color'),
            color: '#5bc0de', // todo based on panel theme
            secondaryColor: '#dfdfdf',
            className: 'switchery',
            disabled: true,
            disabledOpacity: 0.5,
            speed: '0.1s'
          });
          $(input).on('change', function () {
            attribute.value = input.checked;
          });
          break;
  
        case 'EditBoolean':
          input = addEle(inputDiv, 'input', 'js-switch');
          input.setAttribute("type", "checkbox");
          if (attribute.value)
            input.setAttribute("checked", "true");
  
          initSwitchery = new Switchery(input, {
            //color: window.getComputedStyle(panel.panelTitle, null).getPropertyValue('color'),
            color: '#5bc0de', // todo based on panel theme
            secondaryColor: '#dfdfdf',
            className: 'switchery',
            disabled: false,
            disabledOpacity: 0.5,
            speed: '0.1s'
          });
          $(input).on('change', function () {
            attribute.value = input.checked;
          });
          break;
  
        case 'EditDate':
          inputGroupDiv = addEle(inputDiv, 'div', 'input-group date');
          input = addEle(inputGroupDiv, 'input', 'form-control');
          if (attribute.placeHolder)
            input.setAttribute("placeHolder", attribute.placeHolder);
          if (attribute.value)
            input.value = (1 + attribute.value.getMonth()) + '/' + attribute.value.getDate() + '/' + attribute.value.getFullYear();
          inputGroupSpan = addEle(inputGroupDiv, 'span', 'input-group-addon');
          inputGroupSpan.innerHTML = '<i class="fa fa-calendar"></i>';
          $(inputGroupDiv).datepicker({
            autoclose: true,
            todayBtn: true,
            todayHighlight: true,
            showOnFocus: false
          }).on('hide', function (e) {
            validateInput();
            e.preventDefault();
          });
          panel.listeners.push(inputGroupDiv); // so we can avoid leakage on deleting panel
          break;
  
        case 'EditNumber':
          input = addEle(inputDiv, 'input', 'form-control');
          if (attribute.placeHolder)
            input.setAttribute("placeHolder", attribute.placeHolder);
          input.setAttribute("type", "number");
          input.setAttribute("maxlength", attribute.size);
          input.setAttribute("value", attribute.value ? attribute.value : 0);
          break;
  
        case 'EditString':
          if (attribute.quickPick) {
            inputGroupDiv = addEle(inputDiv, 'div', 'input-group');
            input = addEle(inputGroupDiv, 'input', 'form-control');
          } else {
            input = addEle(inputDiv, 'input', 'form-control');
          }
          if (attribute.placeHolder)
            input.setAttribute("placeHolder", attribute.placeHolder);
          input.setAttribute("type", attribute.hint.password ? "password" : "text");
          input.setAttribute("maxlength", attribute.size);
          if (attribute.value)
            input.setAttribute("value", attribute.value);
          if (attribute.quickPick) {
            inputGroupSpan = addEle(inputGroupDiv, 'span', 'input-group-btn');
            inputGroupButton = addEle(inputGroupSpan, 'button', 'btn btn-default dropdown-toggle');
            inputGroupButton.type = 'button';
            inputGroupButton.setAttribute('data-toggle', 'dropdown');
            inputGroupButton.innerHTML = '<span class="caret"></span>';
            daItems = attribute.quickPick;
            daList = '';
            for (j = 0; j < daItems.length; j++) {
              daList += '<li><a href="#">' + daItems[j] + '</a></li>';
            }
            inputGroupDropDownMenu = addEle(inputGroupSpan, 'ul', 'dropdown-menu pull-right');
            inputGroupDropDownMenu.innerHTML = daList;
            $(inputGroupDropDownMenu).click(function (e) {
              input.value = e.originalEvent.srcElement.innerText;
              validateInput();
              e.preventDefault();
            });
            panel.listeners.push(inputGroupDropDownMenu); // so we can avoid leakage on deleting panel
          }
          break;
  
        case 'ViewDate':
          input = addEle(inputDiv, 'p', 'form-control-static');
          if (attribute.value)
            input.innerHTML = (1 + attribute.value.getMonth()) + '/' + attribute.value.getDate() + '/' + attribute.value.getFullYear();
  
          break;
  
        default: // View
          input = addEle(inputDiv, 'p', 'form-control-static');
          input.innerHTML = attribute.value;
  
      }
  
      /**
       * When focus lost on attribute - validate it
       */
      var validateInput = function (event) {
        switch (attribute.type) {
          case 'Date':
            attribute.value = (input.value === '') ? null : attribute.coerce(input.value);
            if (attribute.value != null) {
              var mm = attribute.value.getMonth() + 1;
              var dd = attribute.value.getDate();
              var yyyy = attribute.value.getFullYear();
              if (mm < 10) mm = '0' + mm;
              if (dd < 10) dd = '0' + dd;
              input.value = mm + '/' + dd + '/' + yyyy;
            } else {
              input.value = '';
            }
            break;
          default:
            attribute.value = (input.value === '') ? null : attribute.coerce(input.value);
            if (attribute.value != null)
              input.value = attribute.value;
            break;
        }
        attribute.validate(function () {
        });
      };
      /**
       * Validate when focus lost
       */
      $(input).on('focusout', validateInput);
      panel.listeners.push(input); // so we can avoid leakage on deleting panel
  
      /**
       * Monitor state changes to attribute
       */
      attribute.onEvent('Validate', function () {
        attribute._validationDone = true;
      });
      attribute.onEvent('StateChange', function () {
        switch (mode + attribute.type) {
          case 'EditBoolean':
            if (( attribute.value ? true : false ) != input.checked)
              $(input).click();
            break;
          case 'EditDate':
            input.value = attribute.value ? '' + (1 + attribute.value.getMonth()) + '/' + attribute.value.getDate() + '/' + attribute.value.getFullYear() : '';
            break;
          case 'EditNumber':
            input.value = attribute.value ? attribute.value : 0;
            break;
          case 'EditString':
            input.value = attribute.value ? '' + attribute.value : '';
            break;
          case 'ViewDate':
            input.innerHTML = attribute.value ? '' + (1 + attribute.value.getMonth()) + '/' + attribute.value.getDate() + '/' + attribute.value.getFullYear() : '';
            break;
          default: // View String
            input.innerHTML = attribute.value;
            break;
        }
        renderHelpText(attribute._validationDone ? attribute.validationMessage : '');
        attribute._validationDone = false;
      });
      panel.attributeListeners.push(attribute); // so we can avoid leakage on deleting panel
  
      /**
       * For attribute error display
       */
      function renderHelpText(text) {
        if (text) {
          if (!helpTextDiv) {
            helpTextDiv = document.createElement("div");
            helpTextDiv.className = 'col-sm-9 col-sm-offset-3 has-error';
            formGroup.appendChild(helpTextDiv);
          }
          helpTextDiv.innerHTML = '<span style="display: block;" class="help-block">' + text + '</span>';
          $(formGroup).addClass('has-error');
          if (inputGroupButton)
            $(inputGroupButton).addClass('btn-danger');
        } else {
          setTimeout(function () {
            if (helpTextDiv) {
              $(helpTextDiv).remove();
              helpTextDiv = null;
            }
          }, 250);
          $(formGroup).removeClass('has-error');
          if (inputGroupButton)
            $(inputGroupButton).removeClass('btn-danger');
        }
      }
    }