Skip to content

Commit 770eee2

Browse files
committed
Initial commit
0 parents  commit 770eee2

15 files changed

+712
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
data.json
3+
json_output.json
4+
.DS_Store

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# JSON-Builder
2+
3+
#### TODO

app/css/bootstrap-theme.min.css

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/css/bootstrap.min.css

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/css/main.css

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.label-overrides{
2+
display: flex;
3+
}
4+
#button-container{
5+
margin-bottom:30px;
6+
}
7+
#form-container{
8+
margin-bottom:30px;
9+
}
10+
textarea{
11+
min-height:150px;
12+
}
19.7 KB
Binary file not shown.

app/fonts/glyphicons-halflings-regular.svg

+288
Loading
44.3 KB
Binary file not shown.
22.9 KB
Binary file not shown.
17.6 KB
Binary file not shown.

app/index.html

+303
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
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>

app/js/bootstrap.min.js

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

main.js

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const electron = require('electron')
2+
// Module to control application life.
3+
const app = electron.app
4+
// Module to create native browser window.
5+
const BrowserWindow = electron.BrowserWindow
6+
7+
const path = require('path')
8+
const url = require('url')
9+
10+
// Keep a global reference of the window object, if you don't, the window will
11+
// be closed automatically when the JavaScript object is garbage collected.
12+
let mainWindow
13+
14+
function createWindow () {
15+
// Create the browser window.
16+
mainWindow = new BrowserWindow({width: 800, height: 600})
17+
18+
// and load the index.html of the app.
19+
mainWindow.loadURL(url.format({
20+
pathname: path.join(__dirname, 'app/index.html'),
21+
protocol: 'file:',
22+
slashes: true
23+
}))
24+
console.log(path.join(__dirname, 'app/index.html'))
25+
// Open the DevTools.
26+
mainWindow.webContents.openDevTools()
27+
28+
// Emitted when the window is closed.
29+
mainWindow.on('closed', function () {
30+
// Dereference the window object, usually you would store windows
31+
// in an array if your app supports multi windows, this is the time
32+
// when you should delete the corresponding element.
33+
mainWindow = null
34+
})
35+
}
36+
37+
// This method will be called when Electron has finished
38+
// initialization and is ready to create browser windows.
39+
// Some APIs can only be used after this event occurs.
40+
app.on('ready', createWindow)
41+
42+
// Quit when all windows are closed.
43+
app.on('window-all-closed', function () {
44+
// On OS X it is common for applications and their menu bar
45+
// to stay active until the user quits explicitly with Cmd + Q
46+
if (process.platform !== 'darwin') {
47+
app.quit()
48+
}
49+
})
50+
51+
app.on('activate', function () {
52+
// On OS X it's common to re-create a window in the app when the
53+
// dock icon is clicked and there are no other windows open.
54+
if (mainWindow === null) {
55+
createWindow()
56+
}
57+
})
58+
59+
// In this file you can include the rest of your app's specific main process
60+
// code. You can also put them in separate files and require them here.

0 commit comments

Comments
 (0)