// v1.0
OPC_config = {
  token:'7e61b230-481d-4551-b24b-ba9046e3d8f2',
  serverURL: ''
};

window.OASDefs = {
  bad_q: {type:"text", desc:"Text to display when data quality is bad", placeholder:"BAD DATA"},
  string: {type:"text", desc:"Use {0} as a placeholder for the data value", placeholder:"e.g. Value={0}"},
  bool_f: {type:"text", desc:"Use with Boolean data only - string for FALSE", placeholder:"e.g. OFF"},
  bool_t: {type:"text", desc:"Use with Boolean data only - string for TRUE", placeholder:"e.g. ON"},
  int: {type:"text", desc:"Integer number formatter", placeholder:"#,###"},
  float: {type:"text", desc:"Floating point number formatter", placeholder:"#,##0.000"},
  date: {type:"text", desc:"Date formatter", placeholder:"MM/dd/yyyy"},
  locale: {type:"text", desc:"Specify a local - defaults to 'us' and only affects numerics"},
  offset: {type:"text", desc:"Numeric offset applied to raw value"},
  gain: {type:"text", desc:"Numeric multiplier applied to raw value"},
  style: {type:"text", desc:"CSS style"},
  cls: {type:"text", desc:"CSS class"},
  color: {type:"text", desc:"HTML color"},
  img: {type:"text", desc:"image URL"},
  img_pos: {type:"text", desc:"pixel position"},
  rate: {type:"number", desc:"used in conjunction with repeat, the interval in milliseconds between changes, defaults to 200 if omitted"},
  repeat: {type:"number", desc:"the number of times to toggle a change, primarily used with flashing attributes"},
  trigger: {type:"select", desc:"when to trigger a change to the element. Valid values are:",
    opts:{
      on_true : "when the server tag evaluates to TRUE",
      on_false : "when the server tag evaluates to FALSE",
      trans : "when the server value changes",
      trans_true : "when the server value changes to TRUE from FALSE",
      trans_false : "when the server value changes from FALSE to TRUE"
    }
  },
  bad_q_en: {type:"check", desc:"if set to false, will disable the element when data quality is bad"},
  evt: {type:"select", desc:"the client-side event that will be monitored on th element",
    opts: {
      click: "when the element is clicked once",
      "dbl-click": "when the element is rapidly clicked twice",
      change: "when the value of the element changes"
    }
  },
  set: {type:"select", desc:"The method used to set a Tag value on the server.",
    opts: {
      toggle : "negate the current server boolean value so TRUE becomes FALSE, and FALSE becomes TRUE",
      input : "display an input dialog allowing users to manually enter the value sent to the server",
      value : "send either the current element’s value to the server (default), a fixed value defined by the set_value configuration, or the value of element with an id defined in the set_src configuration"
    }
  },
  set_src: {type:"text", desc:"the id of an element containing the value to be sent to the server"},
  set_value: {type:"text", desc:"a hard-coded value to send to the server, e.g. for creating explicit ON or OFF buttons"},
  set_confirm: {type:"checkbox", desc:"display a confirmation dialog before allowing the value to be set"},
  set_confirm_buttons:{type:"select", desc:"used only with a set value of input, this determines the text values on the dialog buttons",
    opts: {
      ok : "OK/Cancel (default if omitted)",
      yesno : "YES/NO"
    }
  },
  set_confirm_msg: {type:"text", desc:"used only with a set value if input, a message displayed in the input dialog."},
  set_confirm_title: {type:"text", desc:"used only with a set value if input, a title displayed in the header of the input dialog."}
};

window.OASWizard = {
  resetServer: function(){
    //console.log(window.location)
    var proto = window.location.protocol
    var h = window.location.hostname || window.location.host
    var p = window.location.port || 58725
    var u = proto + "//" + h + ":" + p
    OPC_config.serverURL = u;
    $("#serverURL").val(OPC_config.serverURL)
    OASWizard.render();
  },
  api:{
    txt: {
      type: "tag",
      desc: "Sets the <b>text attribute</b> of an element based on the value of a server tag. This is most useful for setting the content of elements like div or anchor. If you need to set the value of an input element (e.g. text input field) use the Value attribute.",
      configs: {
        formats: {
          bad_q:OASDefs.bad_q,
          string:OASDefs.string,
          bool_f:OASDefs.bool_f,
          bool_t:OASDefs.bool_t,
          int:OASDefs.int,
          float:OASDefs.float,
          date:OASDefs.date
        },
        offset:OASDefs.offset,
        gain:OASDefs.gain
      }
    },
    set: {
      type: "tag",
      desc: "<b>Sets as server value</b> based on a client-side event. Use this to enable buttons or other elements to write data back to OAS server tags.",
      configs: {
        evt:OASDefs.evt,
        set:OASDefs.set,
        set_confirm:OASDefs.set_confirm,
        set_confirm_msg:OASDefs.set_confirm_msg,
        set_confirm_title:OASDefs.set_confirm_title,
        set_confirm_buttons:OASDefs.set_confirm_buttons
      }
    },
    val: {
      type: "tag",
      desc: "Sets the <b>value attribute</b> of an element based on the value of a server tag. This is most useful for setting input fields like text boxes and buttons.",
      configs: {
        formats: {
          bad_q:OASDefs.bad_q,
          string:OASDefs.string,
          bool_f:OASDefs.bool_f,
          bool_t:OASDefs.bool_t,
          int:OASDefs.int,
          float:OASDefs.float,
          date:OASDefs.date
        },
        offset:OASDefs.offset,
        gain:OASDefs.gain
      }
    },
    bkg: {
      type: "group",
      desc: "Sets the <b>background style</b> of the element according to the value and quality of one or more server Tags. This allows you to use a single HTML element to represent the state of multiple server values.",
      configs: {
        style:OASDefs.style,
        cls:OASDefs.cls,
        color:OASDefs.color,
        img:OASDefs.img,
        img_pos:OASDefs.img_pos
      }
    },
    fg: {
      type: "group",
      desc: "Sets the <b>foreground style</b> of the element according to the value and quality of one or more server Tags. This allows you to use a single HTML element to represent the state of multiple server values.",
      configs: {
        style:OASDefs.style,
        cls:OASDefs.cls,
        color:OASDefs.color
      }
    },
    brd: {
      type: "group",
      desc: "Sets the <b>border style</b> of the element according to the value and quality of one or more server Tags. This allows you to use a single HTML element to represent the state of multiple server values.",
      configs: {
        style:OASDefs.style,
        cls:OASDefs.cls,
        color:OASDefs.color
      }
    },
    src: {
      type: "img_group",
      desc: "Sets the <b>image URL</b> of an image tag. Designed to be used exclusively with an HTML <img> tag. It sets the src attribute of of the image according to the value and quality of one or more server Tags. This allows you to use a single HTML element to represent the state of multiple server values."
    },
    tt: {
      type: "tag",
      desc: "Sets the <b>tooltip</b> text of an element based on the value of a server tag. This text is displayed when the user hovers over the element.",
      configs: {
        formats: {
          bad_q:OASDefs.bad_q,
          string:OASDefs.string,
          bool_f:OASDefs.bool_f,
          bool_t:OASDefs.bool_t,
          int:OASDefs.int,
          float:OASDefs.float,
          date:OASDefs.date
        },
        offset:OASDefs.offset,
        gain:OASDefs.gain
      }
    },
    "en": {
      type: "tag",
      desc: "<b>Enables</b> an element based on a trigger. Intended to be used with input fields and buttons.",
      configs: {
        bad_q:OASDefs.bad_q_en,
        trigger:OASDefs.trigger
      }
    },
    "vis": {
      type: "tag",
      desc: "<b>Shows or hides</b> an element based on a trigger.",
      configs: {
        bad_q:OASDefs.bad_q_en,
        trigger:OASDefs.trigger
      }
    },
    op: {
      type: "tag",
      desc: "Sets the <b>opacity</b> of an element based on the value of a server tag. Works only with numeric values with a range of 0.0 - 1.0.",
      configs: {
        formats: {
          bad_q:OASDefs.bad_q,
          int:OASDefs.int,
          float:OASDefs.float,
        },
        offset:OASDefs.offset,
        gain:OASDefs.gain
      }
    },
    wd: {
      type: "tag",
      desc: "Sets the <b>width</b> of an element based on the value of a server tag. Numerics are interpreted as pixel width.",
      configs: {
        formats: {
          bad_q:OASDefs.bad_q,
          int:OASDefs.int,
          float:OASDefs.float,
        },
        offset:OASDefs.offset,
        gain:OASDefs.gain
      }
    },
    ht: {
      type: "tag",
      desc: "Sets the <b>height</b> of an element based on the value of a server tag. Numerics are interpreted as pixel height.",
      configs: {
        formats: {
          bad_q:OASDefs.bad_q,
          int:OASDefs.int,
          float:OASDefs.float,
        },
        offset:OASDefs.offset,
        gain:OASDefs.gain
      }
    },
    sx: {
      type: "tag",
      desc: "Sets the element’s <b>horizontal scale</b> based on the value of a server tag. The scale is always applied to the original size of the element. For example, if the element is defined on the page as 100px wide, and a server tag value is 2.5, the width will then become 250px. If the server value then changes to 1.0, the element will return to 100px wide.",
      configs: {
        formats: {
          bad_q:OASDefs.bad_q,
          int:OASDefs.int,
          float:OASDefs.float,
        },
        offset:OASDefs.offset,
        gain:OASDefs.gain
      }
    },
    sy: {
      type: "tag",
      desc: "Sets the element’s <b>vertical scale</b> based on the value of a server tag. The scale is always applied to the original size of the element.",
      configs: {
        formats: {
          bad_q:OASDefs.bad_q,
          int:OASDefs.int,
          float:OASDefs.float,
        },
        offset:OASDefs.offset,
        gain:OASDefs.gain
      }
    },
    tx: {
      type: "tag",
      desc: "Sets the element’s <b>horizontal position</b> based on the element’s original position. This requires that the element uses absolute positioning.",
      configs: {
        formats: {
          bad_q:OASDefs.bad_q,
          int:OASDefs.int,
          float:OASDefs.float,
        },
        offset:OASDefs.offset,
        gain:OASDefs.gain
      }
    },
    ty: {
      type: "tag",
      desc: "Sets the element’s <b>vertical position</b> based on the element’s original position. This is used the same way as <i>Translate-X</i>.",
      configs: {
        formats: {
          bad_q:OASDefs.bad_q,
          int:OASDefs.int,
          float:OASDefs.float,
        },
        offset:OASDefs.offset,
        gain:OASDefs.gain
      }
    },
    "bkg-fl": {
      type: "tag",
      desc: "<b>Flashes the background</b> of an element as the result of a trigger.",
      configs: {
        style:OASDefs.style,
        cls:OASDefs.cls,
        color:OASDefs.color,
        img:OASDefs.img,
        img_pos:OASDefs.img_pos,
        rate:OASDefs.rate,
        repeat:OASDefs.repeat,
        trigger:OASDefs.trigger
      }
    },
    "fg-fl": {
      type: "tag",
      desc: "<b>Flashes the foreground</b> of an element as the result of a trigger.",
      configs: {
        style:OASDefs.style,
        cls:OASDefs.cls,
        color:OASDefs.color,
        img:OASDefs.img,
        img_pos:OASDefs.img_pos,
        rate:OASDefs.rate,
        repeat:OASDefs.repeat,
        trigger:OASDefs.trigger
      }
    },
    "brd-fl": {
      type: "tag",
      desc: "<b>Flashes the border</b> of an element as the result of a trigger.",
      configs: {
        style:OASDefs.style,
        cls:OASDefs.cls,
        color:OASDefs.color,
        img:OASDefs.img,
        img_pos:OASDefs.img_pos,
        rate:OASDefs.rate,
        repeat:OASDefs.repeat,
        trigger:OASDefs.trigger
      }
    }
  },
  init: function() {
    for (attrib in OASWizard.api) {
      OASWizard.attribs = OASWizard.attribs || {};
      OASWizard.attribs[attrib] = new OASWizard.Attrib(attrib);
    }
    $("input[type=radio]").click(OASWizard.render);
    $(".attrib > .name").click(function(){
      var attr = $(this).closest(".attrib")
      attr.toggleClass("collapsed");
      var chev;
      if (attr.hasClass("collapsed")){
        chev = $(".name i.fa.fa-chevron-circle-down", attr);
      }else{
        chev = $(".name i.fa.fa-chevron-circle-right", attr);
      }
      chev.toggleClass("fa-chevron-circle-right").toggleClass("fa-chevron-circle-down");

      OASWizard.render();
    });
    OASWizard.resetServer();
    OASWizard.render();
    $("#reset_server").click(function(){
      OASWizard.resetServer();
    });
    $("#serverURL").keyup(function(){
      OPC_config.serverURL = $("#serverURL").val().trim();
      OASWizard.triggerRender();
      $("#ex_sine").html('');
      $("#ex_saw").html('');
      $("#ex_pump").html('');
    });

    $('#myTabs a').click(function (e) {
      e.preventDefault()
      $(this).tab('show')
    })
  },
  triggerRender: function() {
    if (OASWizard.tTimer) {
      clearTimeout(OASWizard.tTimer);
    }
    OASWizard.tTimer = setTimeout(OASWizard.render, 300);
  },
  render: function() {
    OPC.toggle_refresh(false);
    $("#test").remove();
    var elem = $("input[name=elem]:checked").val();
    var test_element = "<" + elem + " id='test'";
    if (elem == "input") test_element += " type='text'";
    for (id in OASWizard.api) {
      if ($(".attrib#" + id).hasClass("collapsed")) continue;
      var json = OASWizard.attribs[id].toJSON();
      test_element += " oas-tag-" + id + "='" + json + "'";
    }
    test_element += "></" + elem + ">";
    $("#demoview").html(test_element);
    $("#codeview code").html(test_element.replace(/</g,"&lt;").replace(/>/g,"&gt;"));
    OPC.cache = {};
    OPC.elems = {};
    OPC.config = null;
    OPC.init();

var page ="<!DOCTYPE html>\n\
<html lang='en'>\n\
  <head>\n\
    <title>Sample Page Generated by Web HMI Wizard</title>\n\
    <!--\n\
        WEB HMI Library and References\n\
        NOTE: When using this code, be sure to reference the script\n\
        and CSS files included with the OAS product.\n\
    -->\n\
    <script type='text/javascript' src='js/lib/jquery-1.8.3.min.js'></script>\n\
    <script type='text/javascript' src='js/opc-lib-min.js'></script>\n\
    <script type='text/javascript'>\n\
      OPC_config = {\n\
        token:'7e61b230-481d-4551-b24b-ba9046e3d8f2',\n\
        serverURL: '" + OPC_config.serverURL + "'\n\
      };\n\
    </script>\n\
    <link rel='stylesheet' href='css/opc-style.css'>\n\
  </head>\n\
  <body>\n\n    <!-- Your HTML Element -->\n    " + test_element + "\n\n  </body>\n\
</html>";
$("#codeview_page code").html(page.replace(/</g,"&lt;").replace(/>/g,"&gt;"));

    $('#codeview code, #codeview_page code').each(function(i, block) {
      hljs.highlightBlock(block);
    });
  },
  buildTagFields: function(api, fields) {
    fields.append("<div class='field'><label for='tag'><b>tag:</b> OAS Server Tag</label><input id='tag' type='text'></input></div>");
    if (api.configs) {
      var f = $("<div class='field'></div>");
      fields.append(f);
      
      for (c in api.configs) {
        var cfg = api.configs[c];
        if (!cfg) continue;  
        if (c=="formats") {
          f.append("<label for='formats'>formats:</label>")
          var cfc = $("<ul id='formats' class='configs'></ul>");
          f.append(cfc);
          for (cf in api.configs.formats) {
            var cfd = api.configs.formats[cf];
            var ph = cfd.placeholder ? cfd.placeholder : '';
            cfc.append("<li><label for='" + cf + "'><b>" + cf + ":</b> " + cfd.desc + "</label><input id='" + cf + "' type='text' placeholder='" + ph + "' class='format'></input></li>");
          }
        } else {
          var d = $("<div class='config'></div>");
          d.append("<label for='" + c + "'><b>" + c + ":</b> " + cfg.desc + "</label>");
          if (cfg.type == "text" || cfg.type == "number"){
            d.append("<input id='" + c + "' type='" + cfg.type + "' class='config'></input>");
            f.append(d);
          }
          else if (cfg.type=="select") {
            var s = $("<select id='" + c + "' class='config'><option></option></select>");
            for(o in cfg.opts) {
              s.append("<option value='" + o + "'>" + o + "</option>");
            }
            d.append(s);
            s.change(OASWizard.triggerRender);
            f.append(d);
          }
          else if (cfg.type="check") {
            var ck = $("<input id='" + c + "' type='checkbox' class='config' value=false> " + c + "</input>");
            d.append(ck);
            ck.click(OASWizard.triggerRender);
            f.append(d);
          }
        }
      }
    }
  },
  buildTagGroup: function(attr, groups, rem){
    var id = attr.attr("id");
    var api = OASWizard.api[id];
    var gid = $("ul.group", groups).length + 1
    var new_group = $("<ul id='group_" + gid + "' class='group'></ul>");
    var del = $("<span class='remove'><i class='fa fa-minus-circle'></i> remove Tag</span>");
    var tgli = $("<li class='group_head'></li>")
    new_group.append(tgli);
    if (rem) tgli.append(del);
    tgli.append("<label for='tag'><b>tag:</b></label><input id='tag' type='text' placeholder='OAS Tag'></input>")
    if (rem){
      del.click(function(){
        $(this).closest("ul").remove();
        OASWizard.triggerRender();
      });
    }
    for(c in api.configs) {
      var cfg = api.configs[c];
      var desc = cfg.desc ? cfg.desc : '';
      var ph = cfg.placeholder ? cfg.placeholder : '';
      new_group.append("<li><label for='" + c + "'><b>" + c + ":</b></label><input id='" + c + "' type='text' placeholder='" + desc + "'></input></li>");
    }
    groups.append(new_group);
    $("input", new_group).keyup(OASWizard.triggerRender);
  },
  buildImgTagGroup: function(attr, groups, rem){
    var id = attr.attr("id");
    var api = OASWizard.api[id];
    var gid = $("ul.group", groups).length + 1
    var new_group = $("<ul id='group_" + gid + "' class='group'></ul>");
    var del = $("<span class='remove'><i class='fa fa-minus-circle'></i> remove Tag</span>");
    var tgli = $("<li class='group_head'></li>")
    new_group.append(tgli);
    if (rem) tgli.append(del);
    tgli.append("<label for='tag'><b>tag:</b></label><input id='tag' type='text' placeholder='OAS Tag'></input>")
    if (rem) {
      del.click(function(){
        $(this).closest("ul").remove();
      });
    }
    new_group.append("<li><label for='config'>&nbsp;</label><input type='text' id='config' placeholder='image URL'></input></li>");
    groups.append(new_group);
    $("input", new_group).keyup(OASWizard.triggerRender);
  },
  buildGroupFields: function(api, fields) {
    var groups = $("<div class='groups'></div>");
    fields.append(groups);
    OASWizard.buildTagGroup(fields.closest(".attrib"), groups);
    var add = $("<div class='add'><i class='fa fa-plus-circle'></i> add Tag</div>");
    fields.append(add);
    
    var f = $("<div class='field'><label for='all_f'><b>all_f:</b> configuration applied when all Tags in group are FALSE</label></div>");
    fields.append(f);
    var all_f = $("<ul id='all_f' class='configs'></ul>");
    f.append(all_f);
    
    f = $("<div class='field'><label for='bad_q'><b>bad_q:</b> configuration applied when any Tag in group has bad data quality</label></div>");
    fields.append(f);
    var bad_q = $("<ul id='bad_q' class='configs'></ul>");
    f.append(bad_q);
    
    for(c in api.configs) {
      var cfg = api.configs[c];
      var desc = cfg.desc ? cfg.desc : '';
      var ph = cfg.placeholder ? cfg.placeholder : '';
      all_f.append("<li><label for='" + c + "'><b>" + c + ":</b></label><input id='" + c + "' type='text' placeholder='" + desc + "'></input></li>");
      bad_q.append("<li><label for='" + c + "'><b>" + c + ":</b></label><input id='" + c + "' type='text' placeholder='" + desc + "'></input></li>");
    }

    add.click(function(){
      var attr = $(this).closest(".attrib");
      OASWizard.buildTagGroup(attr, groups, true);
    }); 
    
  },
  buildImgGroupFields: function(api, fields) {
    var groups = $("<div class='groups'></div>");
    fields.append(groups);
    OASWizard.buildImgTagGroup(fields.closest(".attrib"), groups);
    var add = $("<div class='add'><i class='fa fa-plus-circle'></i> add Tag</div>");
    fields.append(add);

    fields.append("<div class='field'><label for='all_f'><b>all_f:</b> configuration applied when all Tags in group are FALSE</label> <input type='text' id='all_f' placeholder='image URL'></input></div>");
    fields.append("<div class='field'><label for='bad_q'><b>bad_q:</b> configuration applied when any Tag in group has bad data quality</label> <input type='text' id='bad_q' placeholder='image URL'></input></div>");
    
    add.click(function(){
      var attr = $(this).closest(".attrib");
      OASWizard.buildImgTagGroup(attr, groups, true);
    }); 
  }
};

OASWizard.Attrib = (function() {
  function Attrib(id) {
    this.id = id;
    var api = OASWizard.api[id];
    this.el = $("<div id='" + id + "' class='attrib collapsed'></div>");
    this.el.append("<div class='name'><i class='fa fa-chevron-circle-right'></i> oas-tag-" + id + "</div>");
    this.el.append("<div class='desc'>" + api.desc + "</div>");
    var fields = $("<div class='fields'></div>");
    this.el.append(fields);

    if (api.type == "tag") {
      OASWizard.buildTagFields(api, fields);
    }
    else if (api.type == "group") {
      OASWizard.buildGroupFields(api, fields);
    }
    else if (api.type == "img_group") {
      OASWizard.buildImgGroupFields(api, fields);
    }
    else if (api.type == "set") {
      OASWizard.buildSetFields(api, fields);
    }
        
    $("#attribs").append(this.el);
    this.active = true;

    var t = $("input", this.el)
    if (t!=null && t.length>0) {
      $("input", this.el).each(function(){
        $(this).keyup(OASWizard.triggerRender);
      });
    } 
  };

  Attrib.prototype.genTag = function(json) {
    var fmts = $("ul#formats input", this.el)
    var fs = null;
    for(var i=0;i<fmts.length;i++){
      var inp = $(fmts[i]);
      var v = inp.val().trim();
      if (v!='') {
        fs = fs || {};
        var id = inp.attr("id");
        fs[id] = v;
      }
    }
    if (fs) json.config = {"formats":fs};

    var cfgs = $(".field input.config, .field select.config", this.el);
    json.config = json.config || {};
    for (var i=0;i<cfgs.length;i++) {
      var cfg = $(cfgs[i]);
      //delete json.config[id];
      var id = cfg.attr("id");
      if (cfg.attr("type") == "checkbox") {
        var ck = cfg.is(":checked") ? true : false;
        console.log(ck);
        json.config[id] = ck;
      }else{
        var v = cfg.val().trim();
        if (v != '') json.config[id] = v;
      }
    }
  };

  Attrib.prototype.genGroup = function(json) {
    json.type="group";
    var all_f = {};
    delete json["all_f"];
    var cfgs = $("ul#all_f input", this.el)
    for(var i=0;i<cfgs.length;i++){
      var inp = $(cfgs[i]);
      var v = inp.val().trim();
      if (v!='') {
        var id = inp.attr("id");
        all_f[id] = v;
      }
    }
    json.all_f = all_f;
    var bad_q = {};
    delete json["bad_q"];
    var cfgs = $("ul#bad_q input", this.el)
    for(var i=0;i<cfgs.length;i++){
      var inp = $(cfgs[i]);
      var v = inp.val().trim();
      if (v!='') {
        var id = inp.attr("id");
        bad_q[id] = v;
      }
    }
    json.bad_q = bad_q;
    json.group = [];
    var gs = $(".groups ul.group", this.el);
    for(var g=0;g<gs.length;g++){
      var cfgs = $("input", gs[g]);
      var group = {};
      for(var x=0; x<cfgs.length; x++){
        var inp = $(cfgs[x]);
        var v = inp.val().trim();
        if (v!='') {
          var id = inp.attr("id");
          if (id=="tag") {
            group[id] = v;
          } else {
            group.config = group.config || {};
            group.config[id] = v;
          }
        }
      }
      json.group.push(group);
    }
  };

  Attrib.prototype.genImgGroup = function(json) {
    json.type="group";
    delete json["all_f"];
    var afv = $("input#all_f", this.el)
    if (afv && afv.length > 0 && afv.val().trim() != '') {
      json.all_f = afv.val().trim();
    }
    delete json["bad_q"];
    var bq = $("input#bad_q", this.el)
    if (bq && bq.length > 0 && bq.val().trim() != '') {
      json.bad_q = bq.val().trim();
    }
    json.group = [];
    var gs = $(".groups ul.group", this.el);
    for(var g=0;g<gs.length;g++){
      var cfgs = $("input", gs[g]);
      var group = {};
      for(var x=0; x<cfgs.length; x++){
        var inp = $(cfgs[x]);
        var v = inp.val().trim();
        if (v!='') {
          var id = inp.attr("id");
          group[id] = v;
        }
      }
      json.group.push(group);
    }
  };

  Attrib.prototype.toJSON = function() {
    var json = {};
    var api = OASWizard.api[this.id];
    if (api.type == "tag") {
      var t = $("input#tag", this.el)
      if (t!=null && t.length>0 && t.val().trim() != '') {
        json.tag = t.val().trim();
      }
      this.genTag(json);
    }
    else if (api.type == "group") {
      this.genGroup(json);
    }
    else if (api.type == "img_group") {
      this.genImgGroup(json);
    }
    var j = JSON.stringify(json, null, 4);
    return j;
  };

  return Attrib;

})();

var attribs = {};

$(document).ready(
  function(){
    OASWizard.init();
  }
);

// function htmlEncode(value){
//   //create a in-memory div, set it's inner text(which jQuery automatically encodes)
//   //then grab the encoded contents back out.  The div never exists on the page.
//   return $('<div/>').text(value).html();
// }

// function fixJson(str) {
//   return String(str)
//     .replace(/"{/g, "'{")
//     .replace(/}"/g, "}'");
// }