polymer-rails-forms 0.2.01 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,16 +2,12 @@
2
2
  /* TODO clean this up */
3
3
  var FormHelpers = {
4
4
  /* Utility things */
5
- log: function(o){
6
- console.log("Data", o)
7
- },
8
-
9
5
  capitalize: function(str){
10
- if (str && str.length > 1){
11
- return str.split(" ").map( function(s){ return s[0].toUpperCase() + s.substr(1) } ).join(" ");
12
- } else {
13
- return "";
14
- }
6
+ if (str && str.length > 1){
7
+ return str.split(" ").map( function(s){ return s[0].toUpperCase() + s.substr(1) } ).join(" ");
8
+ } else {
9
+ return "";
10
+ }
15
11
  },
16
12
 
17
13
  findParent: function(el, selector){
@@ -52,132 +48,6 @@
52
48
  }
53
49
  },
54
50
 
55
- indexStore: {},
56
- genIndexStoreKey: function(){
57
- var key = "_" + ((Math.random() * 1000000) | 0);
58
- while(this.indexStore[key] !== void(0)){
59
- key = "_" + ((Math.random() * 1000000) | 0);
60
- }
61
-
62
- return key;
63
- },
64
-
65
- getIndex: function(obj){
66
- for (var x in this.indexStore){
67
- if (this.indexStore[x].obj === obj) return this.indexStore[x].index;
68
- }
69
- },
70
-
71
- addToIndexStore: function(obj, index){
72
- var key = this.genIndexStoreKey();
73
- this.indexStore[key] = {obj: obj, index: index}
74
- },
75
-
76
- removeFromIndexStore: function(obj){
77
- for (var x in this.indexStore){
78
- if (this.indexStore[x].obj === obj) delete this.indexStore[x];
79
- }
80
- },
81
-
82
- updateIndex: function(obj, index){
83
- for (var x in this.indexStore){
84
- if (this.indexStore[x].obj === obj) this.indexStore[x].index = index;
85
- }
86
- },
87
-
88
- enumerate: function(obj){
89
- //TODO: keygen should check for uniquness
90
- var used = [];
91
- for (var i=0; i<obj.length; i++){
92
- var current_index = this.getIndex(obj[i]);
93
- if (current_index) used.push();
94
- }
95
-
96
- for (var i=0; i<obj.length; i++){
97
- var index = i;
98
- while (used.indexOf(index) !== -1){ index++ }
99
- this.addToIndexStore(obj[i], index);
100
- }
101
-
102
- return obj
103
- },
104
-
105
- /* Type Checks */
106
-
107
- getKeys: function(o){
108
- return Object.keys(o);
109
- },
110
-
111
- isArray: function(v){
112
- return Array.isArray(v);
113
- },
114
-
115
- isNotArray: function(v){
116
- return !Array.isArray(v);
117
- },
118
-
119
- isNest: function(v){
120
- return v.type === "nest";
121
- },
122
-
123
- isField: function(v){
124
- return v.type !== "nest";
125
- },
126
-
127
- isMultiple: function(s){
128
- return s.multiple;
129
- },
130
-
131
- isNotMultiple: function(s){
132
- return !s.multiple
133
- },
134
-
135
- isHidden: function(o){
136
- return o.type === 'hidden' ? "hidden" : "";
137
- },
138
-
139
- isEmpty: function(o){
140
- var blank = true;
141
- for (var x in this.structure[this.key].structure){
142
- if (!blank) break;
143
- if (this.structure[this.key].structure[x].type !== 'nest'){
144
- if (this.structure[this.key].structure[x].type === 'json'){
145
- for (var y in o[x]){
146
- if (typeof o[x][y] !== 'object'){
147
- if (!(o[x][y] === null || o[x][y].length === 0)){
148
- blank = false;
149
- break;
150
- }
151
- }
152
- }
153
- } else {
154
- if (!(o[x] === null || o[x].length === 0)){
155
- blank = false;
156
- break;
157
- }
158
- }
159
- }
160
- }
161
-
162
- if (!blank){
163
- return "";
164
- } else {
165
- return "is-blank";
166
- }
167
- },
168
-
169
- exists: function(o, key){
170
- return o[key] !== void(0)
171
- },
172
-
173
- notExists: function(o, key){
174
- return o[key] === void (0)
175
- },
176
-
177
- displayableField: function(field){
178
- return ["string"].indexOf(field.type) !== -1;
179
- },
180
-
181
51
  /* Scope things */
182
52
  wrapperClass: function(structure){
183
53
  if (structure.type === "nest"){
@@ -191,96 +61,54 @@
191
61
  }
192
62
  },
193
63
 
194
- addIndex: function(scope, obj){
195
- var index = this.getIndex(obj);
196
-
197
- this.scope = this.structure[this.key].multiple ? scope + "[" + index + "]" : scope;
198
- return this.scope;
64
+ addIndex: function(scope, index){
65
+ return scope + "[" + index + "]";
199
66
  },
200
67
 
201
- addItem: function(e, d, t){
202
- var parent = this.findParent(t, ".list-wrapper"),
203
- items = parent.querySelectorAll(".list-form"),
204
- invalid = parent.querySelectorAll(".input-wrapper.is-invalid input")[0];
205
-
206
- for (var i=0; i<items.length; i++){
207
- var inputs = items[i].querySelectorAll("input"),
208
- blank = true;
209
-
210
- for (var j=0; j<inputs.length; j++){
211
- if (inputs[j].value.replace(/\s/g, "").length !== 0){ blank = false; break; }
212
- }
213
-
214
- if (blank){
215
- invalid = inputs[0];
216
- break;
217
- }
218
- }
219
-
220
- if (!invalid){
221
- var data = this.classToData(t.className),
222
- newData = {};
223
-
224
- var index = data.length,
225
- used = [];
226
- for (var i=0; i<data.length; i++){ used.push(this.getIndex(data[i])) }
227
- while (used.indexOf(index) !== -1){ index++ }
228
-
229
- this.addToIndexStore(newData, index);
230
- data.push(newData)
231
- } else {
232
- invalid.focus();
233
- }
68
+ updateFieldScope: function(structure, key, scope){
69
+ if (scope === null) return null;
70
+ return structure.options.unscoped ? key : scope + "[" + key + "]"
234
71
  },
235
72
 
236
- updateScope: function(key){
237
- var scope = this.structure[this.key].unscoped ? this.key : this.scope + "[" + this.key + "_attributes]";
238
-
239
- this.scope = scope;
240
- return this.scope;
73
+ updateScope: function(structure, key, scope){
74
+ if (scope === null) return null;
75
+
76
+ scope = structure.unscoped ? key : scope + "[" + key + "_attributes]";
77
+ return scope;
241
78
  },
242
79
 
243
- scopeFieldData: function(data, key){
244
- data = this.structure[this.key].unscoped ? this.unscopedData : data;
245
- //console.log("field", key, data[key]);
246
- if (this.structure[this.key].type !== 'html'){
247
- if (data[key] === void(0)) data[key] = this.structure[this.key].value || null;
80
+ scopeFieldData: function(data, structure, key){
81
+ //data = this.structure[this.key].unscoped ? this.unscopedData : data;
82
+ if (structure.type !== 'html'){
83
+ if (data[key] === void(0)) data[key] = structure.value || null;
248
84
  return data;
249
85
  }
250
86
  },
251
87
 
252
- scopeNestData: function(data, key){
253
- data = this.structure[this.key].unscoped ? this.unscopedData : data;
88
+ scopeNestData: function(structure, key, data){
89
+ data = structure.unscoped ? this.unscopedData : data;
254
90
  //console.log("nest", key, data[key]);
255
- if (this.structure[key].multiple){
256
- if (data[key] === void(0)){
257
- if (this.structure[key].allowAdd) {
91
+ if (structure.multiple){
92
+ if (data[key] === void(0)){
93
+ if (structure.allowAdd) {
258
94
  data[key] = [];
259
95
  } else {
260
96
  data[key] = [{}];
261
97
  }
262
98
  }
263
99
 
264
- this.data = data[key];
265
- } else {
266
- if (data[key] === void(0)) data[key] = {};
267
- this.data = data[key];
268
- }
269
-
270
- return this.data;
271
- },
100
+ } else {
101
+ if (data[key] === void(0)) data[key] = {};
102
+ }
272
103
 
273
- updateParentContext: function(key){
274
- this.parentContext = key;
275
- this.parentStructure = this.structure;
276
- this.parentData = this.data;
104
+ return data[key];
277
105
  },
278
106
 
279
107
  scopeDataToIndex: function(data, obj){
280
108
  var index = this.getIndex(obj);
281
109
 
282
110
  this.data = this.data[index];
283
- return this.data
111
+ return this.data
284
112
  },
285
113
 
286
114
  listItemClassName: function(obj){
@@ -349,25 +177,25 @@
349
177
  },
350
178
 
351
179
  /* Creating Element */
352
- createListItem: function(obj){
180
+ //TODO: Wow, this seems like it could be way simpler.
181
+ createListItem: function(obj, structure, data){
353
182
  var className = this.listItemClassName(obj),
354
- isEmpty = Object.keys(obj).length === 0,
355
183
  container = this.createWithAttributes("div", {'class': "list-item-display"});
356
-
357
- if (this.structure[this.key].display_fields){
358
- for (var i=0; i<this.structure[this.key].display_fields.length; i++){
359
- var field = this.structure[this.key].display_fields[i],
184
+
185
+ if (structure.display_fields){
186
+ for (var i=0; i<structure.display_fields.length; i++){
187
+ var field = structure.display_fields[i],
360
188
  baseField = field.split(".")[0],
361
189
  nameField = field.split(".")[field.split(".").length - 1]
362
190
 
363
191
  var el = document.createElement("div"),
364
192
  label = this.createWithAttributes("span", { text: this.capitalize(nameField.replace(/_/g, " ")) + ": " });
365
-
366
-
193
+
367
194
  var value = document.createElement("span"),
368
- val = document.createTextNode("");
195
+ val = document.createTextNode(""),
196
+ row = structure.structure.filter(function(item){ return item.key === baseField })[0];
369
197
 
370
- if (this.structure[this.key].structure[field.split(".")[0]].type === "json"){
198
+ if (row.type === "json"){
371
199
  var struct = field.split("."),
372
200
  o = obj;
373
201
 
@@ -392,38 +220,23 @@
392
220
  container.appendChild(el);
393
221
  }
394
222
  } else {
395
- container.appendChild(this.createWithAttributes("div", {text: "item " + (this.getIndex(obj) + 1)}))
223
+ container.appendChild(this.createWithAttributes("div", {text: "item " + (data.indexOf(obj) + 1) }))
396
224
  }
397
225
 
398
226
  var remove_button = this.createWithAttributes("core-icon", {icon: 'clear'});
399
227
  remove_button.addEventListener("click", function(e){
400
228
  e.stopPropagation();
401
- var clone = this.parentData.slice(0);
402
- clone.splice(clone.indexOf(obj), 1);
403
-
404
- while (this.parentData.length){
405
- this.removeFromIndexStore(this.parentData[0]);
406
- this.parentData.shift();
407
- }
408
-
409
- this.async(function(){
410
- for (var i=0; i<clone.length; i++){
411
- this.addToIndexStore(clone[i], i);
412
- this.parentData.push(clone[i]);
413
- }
414
- });
415
-
229
+ data.splice(data.indexOf(obj), 1);
416
230
  }.bind(this), false)
417
231
 
418
232
  container.appendChild(remove_button);
419
233
 
420
234
  var self = this;
421
- this.inputList.push({ element: container, container: ".list-item." + className, events: {} })
422
-
235
+
423
236
  container.addEventListener("click", function(e){
424
237
  var stop = function(e){ e.stopPropagation(); }
425
238
  group = self.findParent(container, ".list-group"),
426
- first_input = group.querySelector(".input-wrapper gc-input-decorator");
239
+ first_input = group.querySelector(".input gc-input-decorator");
427
240
 
428
241
  stop(e);
429
242
  var alreadyFocued = self.shadowRoot.querySelectorAll(".list-group.focused");
@@ -432,9 +245,9 @@
432
245
  }
433
246
 
434
247
  first_input.focused = true;
435
- setTimeout(function(){
248
+ self.async(function(){
436
249
  first_input.querySelector("input").focus();
437
- }, 100)
250
+ });
438
251
 
439
252
  self.addClass(group, "focused");
440
253
  group.addEventListener("click", stop, false);
@@ -444,10 +257,14 @@
444
257
  self.removeClass(group, "focused");
445
258
  }, false)
446
259
  }, false)
260
+
261
+ return container;
447
262
  },
448
263
 
449
- createInput: function(structure){
450
- var default_structure = {
264
+ createInput: function(structure, data, key, scope){
265
+ data = data || this.data;
266
+
267
+ var default_structure = {
451
268
  type: "string",
452
269
  label: "",
453
270
  options: {
@@ -474,62 +291,26 @@
474
291
  }
475
292
 
476
293
  setOptions(structure, default_structure);
477
- var el = this.getElement(structure),
478
- obj = { element: el, container: "." + this.scopeToClass(this.key), events: structure.options.events };
479
-
480
- this.inputList.push(obj)
294
+
295
+ return this.getElement(structure, data, key, scope);
296
+ //this.inputList.push(obj)
481
297
  },
482
298
 
483
- scopeToClass: function(key){
484
- if (this.structure[this.key].unscoped){
299
+ scopeToClass: function(structure, key, scope){
300
+ if (scope === null) return key;
301
+
302
+ if (structure.unscoped){
485
303
  var className = key
486
304
  } else {
487
- var className = this.scope.replace(/\[/g, "_").replace(/\]/g, "") + "_" + key;
488
- }
489
- return className;
490
- },
491
-
492
- classToData: function(cn){
493
- var self = this,
494
- data = this.unscopedData;
495
-
496
- for (var x in self.unscopedData){
497
- if (cn.substr(0, x.length) === x){
498
- cn = cn.replace(new RegExp(x + "_?"), "");
499
- data = data[x];
500
- break;
501
- }
502
- }
503
-
504
- function nest(str){
505
- var p = str.split("_attributes_");
506
- if (p.length > 1){
507
- data = data[p[0]]
508
- cn = p.slice(1).join("_attributes_");
509
- return true;
510
- } else {
511
- return false
512
- }
305
+ var className = scope.replace(/\[/g, "_").replace(/\]/g, "") + "_" + key;
513
306
  }
514
-
515
- function arr(str){
516
- var p = str.split("_");
517
- if (p.length > 1 && !isNaN(p[0])){
518
- data = data[p[0]];
519
- cn = p.slice(1).join("_");
520
- return true;
521
- } else {
522
- return false
523
- }
524
- }
525
-
526
- while (nest(cn) || arr(cn)){ }
527
- return cn.length > 0 ? data[cn] : data;
307
+ return className;
528
308
  },
529
309
 
530
310
  checkForBlank: function(group){
531
- var inputs = group.querySelectorAll("input"),
532
- blank = true;
311
+ var inputs = group.querySelectorAll("input[type=text], textarea, select"),
312
+ blank = true,
313
+ single = true;
533
314
 
534
315
  for (var i=0; i<inputs.length; i++){
535
316
  if (inputs[i].value.replace(/\s/g, "").length !== 0){
@@ -539,59 +320,59 @@
539
320
  }
540
321
 
541
322
  if (blank){
542
- this.addClass(group, 'is-blank');
323
+ this.addClass(this.findParent(group, ".input"), 'is-blank');
324
+ return true;
543
325
  } else {
544
- this.removeClass(group, 'is-blank');
326
+ this.removeClass(this.findParent(group, ".input"), 'is-blank');
327
+ return false;
545
328
  }
546
329
  },
547
330
 
548
- updateFieldScope: function(){
549
- return this.structure[this.key].options.unscoped ? this.key : this.scope + "[" + this.key + "]"
550
- },
331
+ getElement: function(structure, data, key, scope){
332
+ data = data || this.data;
551
333
 
552
- getElement: function(structure){
553
334
  var inputs = {
554
335
  string: function(){
555
- return this.createBasicInput("text");
336
+ return this.createBasicInput("text", data, structure, key, scope);
556
337
  }.bind(this),
557
338
 
558
339
  email: function(){
559
- return this.createBasicInput("text");
340
+ return this.createBasicInput("text", data, structure, key, scope);
560
341
  },
561
342
 
562
343
  hidden: function(){
563
- return this.createBasicInput("hidden");
344
+ return this.createBasicInput("hidden", data, structure, key, scope);
564
345
  }.bind(this),
565
346
 
566
347
  json: function(){
567
- return this.createJSONField(structure.fields);
348
+ return this.createJSONField(structure.fields, data, structure, key, scope);
568
349
  }.bind(this),
569
350
 
570
351
  password: function(){
571
- return this.createBasicInput("password");
352
+ return this.createBasicInput("password", data, structure, key, scope);
572
353
  }.bind(this),
573
354
 
574
355
  integer: function(){
575
- return this.createBasicInput("text");
356
+ return this.createBasicInput("text", data, structure, key, scope);
576
357
  }.bind(this),
577
358
 
578
359
  url: function(){
579
- return this.createBasicInput("text");
360
+ return this.createBasicInput("text", data, structure, key, scope);
580
361
  }.bind(this),
581
362
 
582
363
  textarea: function(){
583
- return this.createBasicInput("textarea")
364
+ return this.createBasicInput("textarea", data, structure, key, scope)
584
365
  }.bind(this),
585
366
 
586
367
  checkbox: function(){
587
368
  var box = document.createElement("paper-checkbox"),
588
- input = this.createWithAttributes('input', {type: "checkbox", name: this.updateFieldScope()});
369
+ input = this.createWithAttributes('input', {type: "checkbox", name: this.updateFieldScope(structure, key, scope)});
589
370
 
590
- box.label = this.capitalize(this.structure[this.key].label || this.key);
371
+ box.label = this.capitalize(structure.label || key);
591
372
  box.appendChild(input);
592
373
 
593
374
  box.addEventListener("change", function(e){
594
- this.data[this.key] = e.target.checked ? 1 : 0;
375
+ data[key] = e.target.checked ? 1 : 0;
595
376
  input.checked = e.target.checked;
596
377
  }.bind(this), false)
597
378
 
@@ -600,98 +381,162 @@
600
381
  }.bind(this),
601
382
 
602
383
  date: function(){
603
- var wrapper = this.createBasicInput("text"),
604
- input = wrapper.querySelector("input");
384
+ var wrapper = this.createWithAttributes("div", {"class": "date-wrapper"}),
385
+ decorator = this.createWithAttributes("gc-input-decorator", { label: this.capitalize(structure.label || key.replace(/_/g, " ")), floatingLabel: structure.options.floatingLabel}),
386
+ visibleInput = document.createElement("input"),
387
+ input = this.createWithAttributes("input", {type: "hidden", name: this.updateFieldScope(structure, key, scope) })
388
+
389
+ decorator.appendChild(visibleInput);
390
+ wrapper.appendChild(decorator);
391
+ wrapper.appendChild(input);
605
392
 
606
- var picker = new Pikaday({ field: input, bound: false });
393
+ var picker = new Pikaday({ field: visibleInput, bound: false, onSelect: function(){
394
+ data[key] = picker._d.getFullYear() + "-" + (picker._d.getMonth() + 1) + "-" + picker._d.getDate();
395
+ }.bind(this) });
607
396
 
608
- input.addEventListener("focus", function(){
609
- picker.show();
610
- });
397
+ visibleInput.addEventListener("focus", function(){ picker.show(); });
398
+ new PathObserver(data, key).open(function(){ input.value = data[key]; });
611
399
 
612
400
  return wrapper;
613
401
  }.bind(this),
614
402
 
615
403
  select: function(){
616
- var structure = this.structure[this.key],
617
- values = structure.values;
404
+ var values = structure.values;
618
405
 
619
406
  var wrapper = this.createWithAttributes("div", {"class": "select-wrapper"}),
620
- decorator = this.createWithAttributes("gc-input-decorator", { label: this.capitalize(structure.label || this.key), floatingLabel: structure.options.floatingLabel}),
621
- secretInput = this.createWithAttributes('input', {type: 'hidden', name: this.updateFieldScope()}),
407
+ decorator = this.createWithAttributes("gc-input-decorator", { label: this.capitalize(structure.label || key), floatingLabel: structure.options.floatingLabel}),
408
+ secretInput = this.createWithAttributes('input', {type: 'hidden', name: this.updateFieldScope(structure, key, scope)}),
622
409
  input = this.createWithAttributes("input", { type: "text", name: "", "class": "pseudo-select-input", disabled: true }),
623
410
  select = this.createWithAttributes("core-selector", {valueattr: "val", "class": "pseudo-select", multi: (!!structure.multi) });
624
411
 
625
- secretInput.bind('value', new PathObserver(this.data, this.key));
626
- select.bind('selected', new PathObserver(this.data, this.key));
412
+ //secretInput.bind('value', new PathObserver(data, key));
413
+ //select.bind('selected', new PathObserver(data, key));
414
+ new PathObserver(data, key).open(function(){
415
+ secretInput.value = !!structure.multi ? JSON.stringify(data[key]) : data[key];
416
+ select.selected = data[key];
417
+ })
627
418
 
628
419
  if (!structure.blank){
629
- this.data[this.key] = values.length > 0 ? values[0][0] : "";
420
+ data[key] = values.length > 0 ? values[0][0] : (!!structure.multi ? [] : "");
630
421
  input.value = values.length > 0 ? values[0][1] : "";
631
422
  }
632
423
  var self = this;
633
424
 
634
- select.addEventListener("click", function(e){
635
- if (!!self.structure[self.key].multi) e.stopPropagation();
636
- }, false);
425
+ if (!structure.opened){
426
+
427
+ select.addEventListener("click", function(e){
428
+ if (!!structure.multi) e.stopPropagation();
429
+ }, false);
637
430
 
638
- decorator.addEventListener('click', function(e){
639
- if (self.hasClass(wrapper, 'opened')) return;
431
+ decorator.addEventListener('click', function(e){
432
+ if (self.hasClass(wrapper, 'opened')) return;
640
433
 
641
- e.stopPropagation();
642
- decorator.updateLabelVisibility("down");
643
- self.addClass(wrapper, 'opened');
644
-
645
- window.addEventListener('click', function(){
646
- self.removeClass(wrapper, 'opened');
647
- window.removeEventListener('click', arguments.callee);
434
+ e.stopPropagation();
435
+ decorator.updateLabelVisibility("down");
436
+
437
+ if (!structure.scrollTarget){
438
+ self.addClass(wrapper, 'opened');
439
+ } else {
440
+ var scrollTarget = document.querySelector(structure.scrollTarget);
441
+
442
+ if (scrollTarget === document.body){
443
+ var coords = wrapper.getBoundingClientRect();
444
+ coords.top = coords.top + window.pageYOffset + wrapper.offsetHeight;
445
+ coords.left = coords.left + window.pageXOffset;
446
+ } else {
447
+ var parentCoords = scrollTarget.tagName.indexOf("-") !== -1 ? scrollTarget.scroller.getBoundingClientRect() : scrollTarget.getBoundingClientRect(),
448
+ wrapperCoords = wrapper.getBoundingClientRect(),
449
+ scrollTop = scrollTarget.tagName.indexOf("-") !== -1 ? scrollTarget.scroller.scrollTop : scrollTarget.scrollTop;
450
+
451
+ var coords = {
452
+ width: wrapperCoords.width,
453
+ height: wrapperCoords.height,
454
+ top: wrapperCoords.top - parentCoords.top + scrollTop + wrapperCoords.height,
455
+ left: wrapperCoords.left - parentCoords.left + scrollTarget.scrollLeft
456
+ }
457
+ }
458
+
459
+ select.style.width = coords.width + "px";
460
+ select.style.top = (coords.top ) + "px";
461
+ select.style.left = (coords.left) + "px";
462
+
463
+ scrollTarget.appendChild(select);
464
+ }
465
+
466
+ window.addEventListener('click', function(){
467
+ if (!structure.scrollTarget){
468
+ self.removeClass(wrapper, 'opened');
469
+ } else {
470
+ wrapper.appendChild(select);
471
+ }
472
+
473
+ window.removeEventListener('click', arguments.callee);
474
+ }, false);
648
475
  }, false);
649
- }, false);
476
+ } else {
477
+ self.addClass(wrapper, 'opened');
478
+ decorator.updateLabelVisibility("down");
479
+ }
650
480
 
651
481
  select.addEventListener('core-select', function(){
652
- if (!self.structure[self.key].multi) self.removeClass(wrapper, 'opened');
482
+ if (!structure.multi) self.removeClass(wrapper, 'opened');
653
483
  decorator.updateLabelVisibility(select.selected ? select.selected : "");
654
484
 
655
485
  self.async(function(){
656
486
  if (!!structure.multi){
657
487
  input.value = select === null ? "" : select.selectedItem.map(function(s){ return s.textContent }).join(', ');
658
- secretInput.value = JSON.stringify( select.selectedItem.map(function(s){ return s.getAttribute('val') }) );
488
+ data[key] = select.selectedItem.map(function(s){ return s.getAttribute('val') });
659
489
  } else {
660
490
  input.value = select === null ? "" : select.selectedItem.textContent;
661
- secretInput.value = select.selectedItem.getAttribute('val');
491
+ data[key] = select.selectedItem.getAttribute('val');
662
492
  }
663
493
  });
664
494
  }, false);
665
495
 
666
496
  var build_options = function(){
497
+ var keys = {};
498
+
667
499
  while (select.firstChild) { select.removeChild(select.firstChild) };
668
- if (structure.blank) select.appendChild(this.createWithAttributes("div", {val: "", text: "" }))
500
+ if (!!structure.blank) select.appendChild(this.createWithAttributes("div", {val: "huh", text: (typeof structure.blank === "string" ? structure.blank : "") }))
669
501
  for (var i=0; i<structure.values.length; i++){
670
502
  var div = self.createWithAttributes("div", {val: structure.values[i][0], text: structure.values[i][1] });
671
503
 
672
504
  if (!!structure.multi){
673
505
  var checkbox = this.createWithAttributes("paper-checkbox", {"class": "select-check"});
674
506
  div.appendChild(checkbox);
675
- div.addEventListener("click", function(el, box){
676
- this.async(function(){
677
- box.checked = this.hasClass(el, 'core-selected');
678
- }.bind(this));
679
- }.bind(this, div, checkbox), false)
680
507
  }
681
508
  select.appendChild(div);
682
509
  }
683
510
 
684
511
  var nil = structure.values.length === 0 ? self.addClass(wrapper, 'is-blank') : self.removeClass(wrapper, 'is-blank');
685
512
  nil = structure.values.length === 1 ? self.addClass(wrapper, 'is-single') : self.removeClass(wrapper, 'is-single');
513
+
514
+ //if (!structure.blank && values.length > 0) select.selected = values[0][0];
686
515
  }.bind(this);
687
516
 
688
517
  build_options();
689
- new PathObserver(structure, 'values').open(function(){
518
+ new ArrayObserver(structure.values).open(function(){
690
519
  build_options();
691
520
  })
692
521
 
522
+ //Binding the checkboxes state to whether or not they're selected
523
+ if (!!structure.multi){
524
+ new ArrayObserver(select.selection).open(function(changed){
525
+ for (var j=0; j<changed.length; j++){
526
+ var changes = changed[j];
527
+ for (var i=0; i<changes.addedCount; i++){
528
+ select.selectedItem[changes.index + i].querySelector("paper-checkbox").checked = true;
529
+ }
530
+
531
+ for (var i=0; i<changes.removed.length; i++){
532
+ changes.removed[i].querySelector("paper-checkbox").checked = false;
533
+ }
534
+ }
535
+ })
536
+ }
537
+
693
538
  new PathObserver(structure, 'label').open(function(){
694
- decorator.label = this.capitalize(this.structure[this.key].label || this.key)
539
+ decorator.label = this.capitalize(structure.label || key)
695
540
  }.bind(this));
696
541
 
697
542
  wrapper.appendChild(decorator);
@@ -706,14 +551,16 @@
706
551
  var wrapper = document.createElement("gc-input-decorator"),
707
552
  input = document.createElement( "input" );
708
553
  //google-location-input
709
- if (!this.data[this.key]) this.data[this.key] = { value: null, lat: null, lng: null }
554
+ if (!data[key]) data[key] = { value: null, lat: null, lng: null }
710
555
 
711
- wrapper.label = this.capitalize(this.structure[this.key].label || this.key);
712
- wrapper.floatingLabel = this.structure[this.key].options.floatingLabel;
556
+ wrapper.label = this.capitalize(structure.label || key);
557
+ wrapper.floatingLabel = structure.options.floatingLabel;
713
558
 
714
559
  input.type = "text";
715
- input.name = this.updateFieldScope();
716
- input.bind('value', new PathObserver(this.data[this.key], "value"))
560
+ input.name = this.updateFieldScope(structure, key, scope);
561
+ input.bind('value', new PathObserver(data[key], "value"))
562
+
563
+ this.addValidation('location', input, structure, key, data);
717
564
 
718
565
  var autocomplete = new google.maps.places.Autocomplete(
719
566
  input, { types: ['(cities)'] }
@@ -723,12 +570,12 @@
723
570
  google.maps.event.addListener(autocomplete, 'place_changed', function () {
724
571
  var place = autocomplete.getPlace();
725
572
 
726
- self.data[self.key].value = input.value;
727
- self.data[self.key].lat = place.geometry.location.lat();
728
- self.data[self.key].lng = place.geometry.location.lng();
573
+ data[key].value = input.value;
574
+ data[key].lat = place.geometry.location.lat();
575
+ data[key].lng = place.geometry.location.lng();
729
576
 
730
- if (!!self.structure[self.key].events.select){
731
- self.structure[self.key].events.select(self.data[self.key])
577
+ if (!!structure.options.events.select){
578
+ structure.options.events.select(data[key])
732
579
  }
733
580
  })
734
581
 
@@ -738,7 +585,7 @@
738
585
 
739
586
  image: function(){
740
587
  var wrapper = this.createWithAttributes("span", { "class": "btn-image-upload" }),
741
- input = this.createWithAttributes("input", { type: "file", name: this.updateFieldScope() }),
588
+ input = this.createWithAttributes("input", { type: "file", name: this.updateFieldScope(structure, key, scope), required: structure.required || false }),
742
589
  icon_wrapper = this.createWithAttributes("div", {"class": "image-icon-wrapper"}),
743
590
  explanation = this.createWithAttributes("p", {text: "Click here to upload an image"}),
744
591
  icon = this.createWithAttributes("core-icon", {icon: "image:photo"});
@@ -748,7 +595,7 @@
748
595
  wrapper.appendChild(icon_wrapper);
749
596
  wrapper.appendChild(explanation);
750
597
 
751
- this.addValidation('image', input)
598
+ this.addValidation('image', input, structure, key);
752
599
  input.addEventListener("click", function(e){ e.stopPropagation(); }, true)
753
600
 
754
601
  var self = this;
@@ -774,105 +621,131 @@
774
621
  return wrapper;
775
622
  }.bind(this),
776
623
 
624
+ file: function(){
625
+ var wrapper = this.createWithAttributes("span", { "class": "btn-file-upload" }),
626
+ decorator = this.createWithAttributes("gc-input-decorator", { "class": "file-decorator", label: this.capitalize(structure.label || key.replace(/_/g, " ")), floatingLabel: structure.options.floatingLabel }),
627
+ textInput = this.createWithAttributes("input", { type: "text", "class": "file-dummy" }),
628
+ input = this.createWithAttributes("input", { type: "file", name: this.updateFieldScope(structure, key, scope), required: structure.required || false });
629
+
630
+ decorator.appendChild(textInput);
631
+ wrapper.appendChild(input);
632
+ wrapper.appendChild(decorator);
633
+
634
+ this.addValidation('file', input, structure, key);
635
+ input.addEventListener("click", function(e){ e.stopPropagation(); }, true)
636
+
637
+ var self = this;
638
+ input.addEventListener("change", function(){
639
+ if (this.files.length){
640
+ textInput.value = Array.apply(null, this.files).map(function(item){ return item.name }).join(",");
641
+ self.addClass(wrapper.parentNode, "has-file");
642
+ self.data[self.key] = this.files;
643
+ decorator.updateLabelVisibility(this.files[0].name);
644
+
645
+ } else {
646
+ textInput.value = "";
647
+ self.removeClass(wrapper.parentNode, "has-file");
648
+ self.data[self.key] = null;
649
+ decorator.updateLabelVisibility("");
650
+ }
651
+ }, true)
652
+
653
+ return wrapper;
654
+ }.bind(this),
655
+
777
656
  html: function(){
778
657
  var content = document.createElement('div');
779
- content.innerHTML = this.structure[this.key].content;
658
+ content.innerHTML = structure.content;
780
659
  return content;
781
660
  }.bind(this)
782
661
 
783
662
  }
784
663
 
785
- try {
664
+ //try {
786
665
  return inputs[structure.type]()
787
- } catch(e){
788
- throw "Unknown field type: " + structure.type
789
- }
790
- },
791
-
792
- appendInputs: function(){
793
- while (this.inputList.length){
794
- var obj = this.inputList.shift(),
795
- container = this.shadowRoot.querySelector(obj.container);
796
-
797
- //TODO: insert points
798
- if (!obj.position || obj.position === 'append'){
799
- if (this.findParent(obj.element, obj.container) === null){
800
- container.appendChild(obj.element)
801
- }
802
- } else if (!!obj.position && obj.position === 'before') {
803
- if ([].indexOf.call(container.parentNode.childNodes, obj.element) === -1){
804
- container.parentNode.insertBefore(obj.element, container)
805
- }
806
- }
807
-
808
- if (!!obj.events.onFocus) container.addEventListener("focus", obj.events.onFocus, false);
809
- if (!!obj.events.onBlur) container.addEventListener("blur", obj.events.onBlur, false);
810
- }
666
+ //} catch(e){
667
+ // throw "Unknown field type: " + structure.type
668
+ //}
811
669
  },
812
670
 
813
- createBasicInput: function(type, name, label){
671
+ createBasicInput: function(type, data, structure, key, scope){
814
672
  var wrapper = document.createElement("gc-input-decorator"),
815
- input = document.createElement( type === "textarea" ? "textarea" : "input" );
673
+ input = document.createElement( type === "textarea" ? "textarea" : "input" );
816
674
 
817
- wrapper.label = this.capitalize(label || this.structure[this.key].label || this.key);
818
- wrapper.floatingLabel = this.structure[this.key].options.floatingLabel;
675
+ wrapper.label = this.capitalize(structure.label || key);
676
+ wrapper.floatingLabel = structure.options.floatingLabel;
819
677
 
820
678
  input.type = type;
821
- input.name = name || this.updateFieldScope();
822
- input.value = this.structure[this.key].value || '';
823
- input.id = this.scopeToClass(this.key)
824
- if (this.structure[this.key].required) input.required = true;
825
- input.bind('value', new PathObserver(this.data, this.key))
826
-
827
- this.addValidation(type, input)
679
+ input.name = this.updateFieldScope(structure, key, scope);
680
+ input.value = structure.value || '';
681
+ input.id = this.scopeToClass(structure, key, scope)
682
+ if (structure.required) input.required = true;
683
+ input.bind('value', new PathObserver(data, key))
684
+
685
+ this.addValidation(type, input, structure, key);
828
686
 
687
+ if (!!structure.autocomplete && typeof structure.autocomplete === "string"){
688
+ this.autocomplete(input, structure.autocomplete, structure.autocompleteOptions || {});
689
+ }
690
+
829
691
  if (type === 'textarea') input.rows = 3;
830
- if (type !== "hidden"){
692
+ if (type !== "hidden"){
831
693
  wrapper.appendChild(input);
832
- return wrapper;
833
- } else {
834
- return input;
835
- }
694
+ return wrapper;
695
+ } else {
696
+ return input;
697
+ }
836
698
 
837
699
  },
838
700
 
839
- createJSONField: function(fields){
840
- //TODO: only basic JSON support, if you want crazy complicated stuff, just use a textarea or custom input
701
+ createJSONField: function(fields, data, structure, key, scope){
702
+
703
+ // //TODO: only basic JSON support, if you want crazy complicated stuff, just use a textarea or custom input
841
704
  var wrapper = this.createWithAttributes('div', { "class": "json-wrapper" }),
842
- input = this.createWithAttributes("textarea", { name: this.updateFieldScope(), "class": "hidden", text: JSON.stringify(this.data[this.key]) }),
843
- isList = this.parentStructure !== null && this.parentStructure[this.parentContext].allowAdd;
844
-
845
- if (!this.data[this.key]) this.data[this.key] = {};
705
+ input = this.createWithAttributes("textarea", { name: this.updateFieldScope(structure, key, scope), "class": "hidden", text: JSON.stringify(data[key]) }),
706
+ isList = structure.allowAdd;
846
707
 
847
- new ObjectObserver(this.data[this.key]).open(function(){
848
- input.value = JSON.stringify(this.data[this.key])
849
- }.bind(this))
850
-
851
- // input.bind('value', new PathObserver(this.data, this.key))
852
-
853
- wrapper.appendChild(input);
854
- for (var i=0; i<fields.length; i++){
855
- this.data[this.key][fields[i]] = this.data[this.key][fields[i]] || "";
856
- var input_wrapper = this.createWithAttributes("div", { "class": (isList ? "input-wrapper" : "input") + " json-input-wrapper"})
857
- decorator = document.createElement("gc-input-decorator"),
858
- el = this.createWithAttributes('input');
859
-
860
- input_wrapper.appendChild(decorator);
861
- decorator.label = this.capitalize(fields[i]);
862
- decorator.floatingLabel = true;
863
- decorator.appendChild(el);
864
-
865
- el.bind("value", new PathObserver(this.data[this.key], fields[i]))
866
-
867
- if (isList){
868
- var className = "." + this.parentContext + "-" + this.getIndex(this.data) + " .input-wrapper." + this.key;
869
- var o = { element: input_wrapper, container: className, events: {}, position: "before" }
708
+ if (structure.multiple){
709
+ this.jsonData[key] = [{}]
710
+ this.buildStructure(structure.structure, this.jsonData[key][0], wrapper, null);
711
+ } else {
712
+ this.jsonData[key] = {}
713
+ this.buildStructure(structure.structure, this.jsonData[key], wrapper, null);
714
+ }
715
+ wrapper.appendChild(input)
716
+
717
+ var self = this;
718
+
719
+ function recurse(obj){
720
+ if (Array.isArray(obj)){
721
+ new ArrayObserver(obj).open(function(changed){
722
+ for (var j=0; j<changed.length; j++){
723
+ var changes = changed[j];
724
+ for (var i=0; i<changes.addedCount; i++){
725
+ recurse(obj[changes.index + i]);
726
+ }
727
+ }
728
+
729
+ input.value = JSON.stringify(self.jsonData[key]);
730
+ data[key] = JSON.parse(input.value);
731
+ })
732
+
733
+ for (var i=0; i<obj.length; i++) recurse(obj[i]);
870
734
  } else {
871
- var o = { element: input_wrapper, container: "." + this.scopeToClass(this.key), events: {} };
872
- }
735
+ new ObjectObserver(obj).open(function(){
736
+ input.value = JSON.stringify(self.jsonData[key]);
737
+ data[key] = JSON.parse(input.value);
738
+ });
873
739
 
874
- this.inputList.push(o)
740
+ for (var x in obj){
741
+ if (typeof obj[x] === 'object' && obj[x] !== null) recurse(obj[x]);
742
+ }
743
+ }
875
744
  }
745
+ recurse(this.jsonData[key]);
746
+
747
+ input.value = JSON.stringify(this.jsonData[key]);
748
+ data[key] = JSON.parse(input.value);
876
749
 
877
750
  return wrapper;
878
751
  },