|
| 1 | +<!DOCTYPE html> |
| 2 | +<html> |
| 3 | + <head> |
| 4 | + <meta charset="UTF-8"> |
| 5 | + <link rel="stylesheet" type="text/css" href="./css/bootstrap.min.css"> |
| 6 | + <link rel="stylesheet" type="text/css" href="./css/bootstrap-theme.min.css"> |
| 7 | + <link rel="stylesheet" type="text/css" href="./css/main.css"> |
| 8 | + <title>JSON Builder</title> |
| 9 | + </head> |
| 10 | + <body> |
| 11 | + <div class="container-fluid"> |
| 12 | + <h1>Add a New Entry</h1> |
| 13 | + |
| 14 | + <div class="flex-wrapper"> |
| 15 | + <div> |
| 16 | + <label class="col-sm-2 control-label" for="global-key" style="width:auto; display:flex">Global Key</label> |
| 17 | + <input class="form-control" type="input" id="global-key" style="width:auto; display:flex"/> |
| 18 | + </div> |
| 19 | + </div> |
| 20 | + |
| 21 | + <div id="button-container"> |
| 22 | + <input type="button" class="btn" value="Add Page" id="add-page" onclick="add_page()"/> |
| 23 | + <input type="button" class="btn" value="Generate" id="generate-json" onclick="generate_json_file()"/> |
| 24 | + <select id="select-active-form" class="form-control" onchange="visible_item(this)"> |
| 25 | + <option value="-1">All Items</option> |
| 26 | + <option value="0">Item 1</option> |
| 27 | + </select> |
| 28 | + </div> |
| 29 | + <hr> |
| 30 | + |
| 31 | + <div id="form-container"> |
| 32 | + </div> |
| 33 | + |
| 34 | + </div> |
| 35 | + </body> |
| 36 | + |
| 37 | + <script> |
| 38 | + 'use strict' |
| 39 | + |
| 40 | + require('../renderer.js') |
| 41 | + var fs = require('fs'); |
| 42 | + var payload = require('../data.json') |
| 43 | + |
| 44 | + var object_array_key = Object.keys(payload)[0] |
| 45 | + var json_object = payload[object_array_key][0] |
| 46 | + var object_key = 'name' |
| 47 | + |
| 48 | + if (!document.getElementById('global-key').value) { |
| 49 | + document.getElementById('global-key').value = object_array_key |
| 50 | + } |
| 51 | + |
| 52 | + |
| 53 | + generate_form(0) |
| 54 | + |
| 55 | + function generate_form(form_id){ |
| 56 | + /* Generate DOM objects for Form contents */ |
| 57 | + let form_data = loop_through_json([], json_object, '') |
| 58 | + |
| 59 | + /* Generate form object */ |
| 60 | + let form = document.createElement('form') |
| 61 | + form.setAttribute('method', 'post') |
| 62 | + form.setAttribute('data-number', form_id) |
| 63 | + form.setAttribute('data-index', form_id) |
| 64 | + form.setAttribute('id', `json-builder-${form_id}`) |
| 65 | + document.getElementById('form-container').appendChild(form) |
| 66 | + |
| 67 | + /* Render to HTML page */ |
| 68 | + populate_dom_form_from_data(form_data, document.getElementById(form.id)) |
| 69 | + } |
| 70 | + |
| 71 | + function prettify(str){ |
| 72 | + /* "camera-min apertaure" -> "Camera : Min Aperature" */ |
| 73 | + return( |
| 74 | + str.split('-').map((words) => |
| 75 | + words.split(' ').map((word) => |
| 76 | + word[0].toUpperCase() + word.substring(1, word.length) |
| 77 | + ).join(' ') |
| 78 | + ).join(' : ') |
| 79 | + ) |
| 80 | + } |
| 81 | + |
| 82 | + function create_element(target_form, keyname, el, level){ |
| 83 | + /* Create new HTML Input or Text element */ |
| 84 | + |
| 85 | + if (typeof el === 'string'){ |
| 86 | + if (el === 'String') { |
| 87 | + var new_input = document.createElement('Input'); |
| 88 | + new_input.setAttribute('type', 'text'); |
| 89 | + new_input.setAttribute('data-structure', 'string'); |
| 90 | + new_input.setAttribute('class', 'form-control'); |
| 91 | + } else { |
| 92 | + var new_input = document.createElement('TextArea'); |
| 93 | + new_input.setAttribute('data-structure', 'text') |
| 94 | + new_input.setAttribute('class', 'form-control'); |
| 95 | + } |
| 96 | + return new_input |
| 97 | + |
| 98 | + } else if (el instanceof Array) { |
| 99 | + var new_input = document.createElement('Input'); |
| 100 | + new_input.setAttribute('type', 'text'); |
| 101 | + new_input.setAttribute('data-structure', 'array'); |
| 102 | + new_input.setAttribute('data-array-num', '0'); |
| 103 | + new_input.setAttribute('class', 'form-control'); |
| 104 | + //new_input.style.marginLeft = 20 * level + "px" |
| 105 | + return new_input |
| 106 | + |
| 107 | + } else if (el instanceof Object) { |
| 108 | + loop_through_json(target_form, el, keyname, level + 1) |
| 109 | + return; |
| 110 | + |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + function loop_through_json(target_form, key_vals, prepend_key, nested_level = 0){ |
| 115 | + for (var key in key_vals){ |
| 116 | + |
| 117 | + if (prepend_key) { |
| 118 | + var key_id = `${prepend_key}-${key}` |
| 119 | + } else { |
| 120 | + var key_id = key |
| 121 | + } |
| 122 | + |
| 123 | + var new_label = document.createElement('Label'); |
| 124 | + var new_value = create_element(target_form, key, key_vals[key], nested_level); |
| 125 | + |
| 126 | + new_label.innerHTML = prettify(key_id) |
| 127 | + new_label.className += ' label-overrides' |
| 128 | + if (new_value){ |
| 129 | + new_value.id = key_id; |
| 130 | + new_value.className += ' label-overrides' |
| 131 | + } |
| 132 | + |
| 133 | + let add_field_btn = null |
| 134 | + if (key_vals[key] instanceof Array) { |
| 135 | + add_field_btn = document.createElement('input') |
| 136 | + add_field_btn.setAttribute('type', 'button') |
| 137 | + add_field_btn.setAttribute('value', '+') |
| 138 | + add_field_btn.setAttribute('data-for', new_value.id) |
| 139 | + add_field_btn.setAttribute('data-idx', new_value.getAttribute('data-array-num')) |
| 140 | + add_field_btn.setAttribute('onclick', 'add_input_field(this)') |
| 141 | + add_field_btn.setAttribute('class', 'btn btn-primary'); |
| 142 | + } |
| 143 | + |
| 144 | + if (new_value != null) { |
| 145 | + target_form.push( |
| 146 | + new_label, |
| 147 | + new_value |
| 148 | + ) |
| 149 | + if (add_field_btn) target_form.push(add_field_btn) |
| 150 | + } |
| 151 | + } |
| 152 | + return target_form; |
| 153 | + } |
| 154 | + |
| 155 | + function populate_dom_form_from_data(form_data, target_form){ |
| 156 | + form_data.forEach((el) => { |
| 157 | + target_form.appendChild(el); |
| 158 | + }) |
| 159 | + } |
| 160 | + |
| 161 | + |
| 162 | + var nest = function(obj, keys, v) { |
| 163 | + /* Insert element, v into object with nested keys */ |
| 164 | + if (keys.length === 1) { |
| 165 | + if (v.getAttribute('data-structure') == 'array'){ |
| 166 | + if(!obj[keys[0]]){ |
| 167 | + obj[keys[0]] = [] |
| 168 | + } |
| 169 | + if (v.value) obj[keys[0]].push(v.value) |
| 170 | + } else { |
| 171 | + obj[keys[0]] = v.value; |
| 172 | + } |
| 173 | + } else { |
| 174 | + var key = keys.shift(); |
| 175 | + obj[key] = nest(typeof obj[key] === 'undefined' ? {} : obj[key], keys, v); |
| 176 | + } |
| 177 | + |
| 178 | + return obj; |
| 179 | + }; |
| 180 | + |
| 181 | + function create_json(form_id){ |
| 182 | + /* Build form data into a JSON object */ |
| 183 | + var json_data = document.getElementById(`json-builder-${form_id}`).elements; |
| 184 | + var json_payload = {} |
| 185 | + |
| 186 | + for(let i=0; i < json_data.length ; i++){ |
| 187 | + let element = json_data[i] |
| 188 | + let nested_keys = element.id.split('-') |
| 189 | + |
| 190 | + /* skip labels, etc */ |
| 191 | + if (element.type === 'text'){ |
| 192 | + /* object[key] = value (no deeper nesting) */ |
| 193 | + if ((nested_keys.length - 1) === 0) { |
| 194 | + if (element.getAttribute('data-structure') === 'array'){ |
| 195 | + if (!json_payload[element.id]) { |
| 196 | + json_payload[element.id] = [] |
| 197 | + if (element.value) json_payload[element.id].push(element.value); |
| 198 | + } else { |
| 199 | + if (element.value) json_payload[element.id].push(element.value); |
| 200 | + } |
| 201 | + } else { // if not array, must be string |
| 202 | + if (element.value) json_payload[element.id] = element.value; |
| 203 | + } |
| 204 | + /* object[key1][key2] = value (need to ensure parent objects exist) */ |
| 205 | + } else { |
| 206 | + json_payload = nest(json_payload, nested_keys, element) |
| 207 | + } |
| 208 | + } |
| 209 | + } |
| 210 | + return json_payload; |
| 211 | + } |
| 212 | + |
| 213 | + function json_subobjects(obj, key){ |
| 214 | + var parent_obj = {} |
| 215 | + parent_obj[key] = [] |
| 216 | + obj.forEach(payload => |
| 217 | + parent_obj[key].push(payload) |
| 218 | + ); |
| 219 | + return parent_obj |
| 220 | + } |
| 221 | + |
| 222 | + function write_json(file_contents, filename='./json_output.json'){ |
| 223 | + /* Write JSON object to file */ |
| 224 | + fs.writeFile(filename, JSON.stringify(file_contents, null, 2), (err) => { |
| 225 | + if(err) console.error(err); |
| 226 | + }); |
| 227 | + } |
| 228 | + |
| 229 | + function add_page(){ |
| 230 | + let form_count = document.getElementById('form-container').children.length; |
| 231 | + generate_form(form_count) |
| 232 | + generate_option(form_count) |
| 233 | + } |
| 234 | + |
| 235 | + function add_input_field(btn){ |
| 236 | + let form_id = btn.parentNode.getAttribute('data-number') |
| 237 | + let target_field = btn.previousElementSibling |
| 238 | + let next_input_field = target_field.cloneNode(true) |
| 239 | + next_input_field.setAttribute('data-array-num', Number(target_field.getAttribute('data-array-num')) + 1) |
| 240 | + next_input_field.value = '' |
| 241 | + document.getElementById(`json-builder-${form_id}`).insertBefore(next_input_field, btn) |
| 242 | + } |
| 243 | + |
| 244 | + function generate_json_file(){ |
| 245 | + let forms = document.getElementById('form-container').children |
| 246 | + let extracted_json_data = [] |
| 247 | + for (let i=0; i < forms.length; i++){ |
| 248 | + extracted_json_data.push(create_json(i)) |
| 249 | + } |
| 250 | + |
| 251 | + let payload_key = document.getElementById('global-key').value || object_array_key |
| 252 | + let formatted_json = json_subobjects(extracted_json_data, payload_key) |
| 253 | + write_json(formatted_json) |
| 254 | + } |
| 255 | + |
| 256 | + function visible_item(selection){ |
| 257 | + /* Hide all forms whose 'data-number' don't match selection.value */ |
| 258 | + let forms = document.getElementById('form-container').children; |
| 259 | + for(var i=0; i<forms.length; i++){ |
| 260 | + if ((forms[i].getAttribute('data-number') == selection.value) || selection.value == -1) { |
| 261 | + forms[i].style.display = '' |
| 262 | + } else { |
| 263 | + forms[i].style.display = 'none' |
| 264 | + } |
| 265 | + } |
| 266 | + } |
| 267 | + |
| 268 | + function generate_option(option_id){ |
| 269 | + /* Add new option to selector */ |
| 270 | + let select = document.getElementById('select-active-form') |
| 271 | + let option = document.createElement('option') |
| 272 | + option.value = option_id |
| 273 | + option.innerHTML = `Item ${option_id + 1}` |
| 274 | + select.appendChild(option) |
| 275 | + } |
| 276 | + |
| 277 | + function repopulate_option_list(){ |
| 278 | + /* Add new option to select (when new form is created) */ |
| 279 | + let select = document.getElementById('select-active-form') |
| 280 | + let forms = document.getElementById('form-container').children |
| 281 | + |
| 282 | + for(var i=0; i < select.children.length ; i++){ |
| 283 | + var target_form_id = select.children[i].value |
| 284 | + for(var j=0; j < forms.length; j++){ |
| 285 | + if (forms[j].getAttribute('data-number') == target_form_id){ |
| 286 | + select.children[i].innerHTML = `Item ${++target_form_id} : ${get_name(forms[j])}` |
| 287 | + break; |
| 288 | + } |
| 289 | + } |
| 290 | + } |
| 291 | + } |
| 292 | + |
| 293 | + function get_name(form, field='name'){ |
| 294 | + /* extract value of child node whose id matches 'name') */ |
| 295 | + for(var i=0; i<form.children.length; i++){ |
| 296 | + if (form.children[i].id == field) { |
| 297 | + return form.children[i].value || '""'; |
| 298 | + } |
| 299 | + } |
| 300 | + } |
| 301 | + |
| 302 | + </script> |
| 303 | +</html> |
0 commit comments