text: /**
   *    Copyright (C) 2015 Deciso B.V.
   *
   *    All rights reserved.
   *
   *    Redistribution and use in source and binary forms, with or without
   *    modification, are permitted provided that the following conditions are met:
   *
   *    1. Redistributions of source code must retain the above copyright notice,
   *       this list of conditions and the following disclaimer.
   *
   *    2. Redistributions in binary form must reproduce the above copyright
   *       notice, this list of conditions and the following disclaimer in the
   *       documentation and/or other materials provided with the distribution.
   *
   *    THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
   *    INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
   *    AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   *    AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
   *    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   *    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   *    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   *    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   *    POSSIBILITY OF SUCH DAMAGE.
   *
   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
   *
   *    shared components
   *
   */
  
   /**
    * html decode text into textarea tag and return decoded value.
    *
    * @param value encoded text
    * @return string decoded text
    */
   function htmlDecode(value) {
       return $("<textarea/>").html(value).text();
   }
  
   /**
   *
   * Map input fields from given parent tag to structure of named arrays.
   *
   * @param parent tag id in dom
   * @return array
   */
  function getFormData(parent) {
  
      var data = {};
      $( "#"+parent+"  input,#"+parent+" select,#"+parent+" textarea" ).each(function( index ) {
          if ($(this).prop('id') == undefined) {
              // we need an id.
              return;
          }
          var node = data ; // target node
          var sourceNode = $(this); // document node to fetch data from
          var keyparts = sourceNode.prop('id').split('.');
          $.each(keyparts,function(indx,keypart){
              if (!(keypart in node)) {
                  node[keypart] = {};
              }
              if (indx < keyparts.length - 1 ) {
                  node = node[keypart];
              } else {
                  if (sourceNode.is("select")) {
                      // selectbox, collect selected items
                      var tmp_str = "";
                      sourceNode.children().each(function(index){
                          if ($(this).prop("selected")){
                              if (tmp_str != "") tmp_str = tmp_str + ",";
                              tmp_str = tmp_str + $(this).val();
                          }
                      });
                      node[keypart] = tmp_str;
                  } else if (sourceNode.prop("type") == "checkbox") {
                      // checkbox input type
                      if (sourceNode.prop("checked")) {
                          node[keypart] = "1" ;
                      } else {
                          node[keypart] = "0" ;
                      }
                  } else {
                      // regular input type
                      node[keypart] = sourceNode.val();
                  }
              }
          });
      });
  
      return data;
  }
  
  /**
   * bind data to form, using named arrays
   *
   * for example,
   *      data = {'host':{'name':'opnsense'}}
   *      parent = 'general'
   *
   *      will search for an input tag host.name within the parent tag 'general' and fills it with the value 'opnsense'
   *
   * @param parent tag id in dom
   * @param data named array structure
   */
  function setFormData(parent,data) {
      $( "#"+parent+"  input,#"+parent+" select,#"+parent+" span,#"+parent+" textarea" ).each(function( index ) {
          if ($(this).prop('id') == undefined) {
              // we need an id.
              return;
          }
          var node = data ;
          var targetNode = $(this); // document node to fetch data to
          var keyparts = $(this).prop('id').split('.');
          $.each(keyparts,function(indx,keypart){
              if (keypart in node) {
                  if (indx < keyparts.length - 1 ) {
                      node = node[keypart];
                  } else {
                      // data node found, handle per type
                      if (targetNode.is("select")) {
                          // handle select boxes
                          targetNode.empty(); // flush
                          $.each(node[keypart],function(indxItem, keyItem){
                              if (keyItem["selected"] != "0") {
                                  targetNode.append("<option value='"+indxItem+"' selected>" + keyItem["value"] + " </option>");
                              } else {
                                  targetNode.append("<option value='"+indxItem+"'>" + keyItem["value"] + " </option>");
                              }
                          });
                      } else if (targetNode.prop("type") == "checkbox") {
                          // checkbox type
                          if (node[keypart] != 0) {
                              targetNode.prop("checked",true) ;
                          } else {
                              targetNode.prop("checked",false) ;
                          }
                      } else if (targetNode.is("span")) {
                          if (node[keypart] != null) {
                              targetNode.text("");
                              targetNode.append(htmlDecode(node[keypart]));
                          }
                      } else {
                          // regular input type
                          targetNode.val(htmlDecode(node[keypart]));
                      }
                  }
              }
          });
      });
  }
  
  
  /**
   * handle form validations
   * @param parent
   * @param validationErrors
   */
  function handleFormValidation(parent,validationErrors) {
      $( "#"+parent).find("*").each(function( index ) {
          if (validationErrors != undefined && $(this).prop('id') in validationErrors) {
              $("*[for='" + $(this).prop('id') + "']").addClass("has-error");
              $("span[for='" + $(this).prop('id') + "']").text(validationErrors[$(this).prop('id')]);
          } else {
              $("*[for='" + $(this).prop('id') + "']").removeClass("has-error");
              $("span[for='" + $(this).prop('id') + "']").text("");
          }
      });
  }
  
  /**
   * clear form validations
   * @param parent
   */
  function clearFormValidation(parent) {
      handleFormValidation(parent, {});
  }
  
  /**
   * call remote function (post request), wrapper around standard jQuery lib.
   * @param url endpoint url
   * @param sendData input structure
   * @param callback callback function
   * @return deferred object
   */
  function ajaxCall(url, sendData, callback) {
      return $.ajax({
          type: "POST",
          url: url,
          dataType:"json",
          contentType: "application/json",
          complete: function(data, status) {
              if ( callback == null ) {
                  null;
              } else if ( "responseJSON" in data ) {
                  callback(data['responseJSON'],status);
              } else {
                  callback(data,status);
              }
          },
          data: JSON.stringify(sendData)
      });
  }
  
  /**
   * retrieve json type data (GET request) from remote url
   * @param url endpoint url
   * @param sendData input structure
   * @param callback callback function
   * @return deferred object
   */
  function ajaxGet(url,sendData,callback) {
      return $.ajax({
          type: "GET",
          url: url,
          dataType:"json",
          contentType: "application/json",
          complete: function(data,status) {
              if ( callback == null ) {
                  null;
              } else if ( "responseJSON" in data ) {
                  callback(data['responseJSON'],status);
              } else {
                  callback({},status);
              }
          },
          data: sendData
      });
  }
  
  /**
   * watch scroll position and set to last known on page load
   */
  function watchScrollPosition() {
      function current_location() {
          // concat url pieces to indentify this page and parameters
          return window.location.href.replace(/\/|\:|\.|\?|\#/gi, '');
      }
  
      // link on scroll event handler
      $(window).scroll(function(){
          sessionStorage.setItem('scrollpos', current_location()+"|"+$(window).scrollTop());
      });
  
      // move to last known position on page load
      $( document ).ready(function() {
          var scrollpos = sessionStorage.getItem('scrollpos');
          if (scrollpos != null) {
              if (scrollpos.split('|')[0] == current_location()) {
                  $(window).scrollTop(scrollpos.split('|')[1]);
              }
          }
      });
  }
  