polymer-rails-forms 0.2.01 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  <link rel="import" href="rails-form-helpers.html" >
2
2
  <link rel="import" href="rails-form-validators.html" >
3
+ <link rel="import" href="rails-form-autocomplete.html" >
3
4
  <link rel="import" href="gc-paper-decorator.html" >
4
5
  <link rel="import" href="form-spinner.html" >
5
6
  <link rel="import" href="../paper-button/paper-button.html" >
@@ -8,70 +9,112 @@
8
9
 
9
10
  <script src='pikaday.js'></script>
10
11
 
12
+ <style>
13
+ /*
14
+ these are attached to the body if scrollTarget is set, so they need to be outside of the shadowDOM
15
+ */
16
+ .pseudo-select {
17
+ opacity: 1;
18
+ display: block;
19
+ transition: opacity 250ms ease-out;
20
+ position: absolute;
21
+ top: 100%;
22
+ width: 106%;
23
+ background-color: white;
24
+ box-shadow: -2px 1px 10px #888;
25
+ z-index: 1;
26
+ margin-top: -10px;
27
+ }
28
+
29
+ .pseudo-select > div {
30
+ min-height: 2em;
31
+ border-bottom: solid 1px;
32
+ padding: 5px 3%;
33
+ position: relative;
34
+ }
35
+
36
+ .pseudo-select > div:hover {
37
+ background-color: #D5DAFF;
38
+ }
39
+
40
+ .pseudo-select > div paper-checkbox {
41
+ float: left;
42
+ margin-right: 15px;
43
+ }
44
+ </style>
45
+
11
46
  <polymer-element name="rails-form">
12
47
  <template>
13
48
  <link rel='stylesheet' href='pikaday.css'>
14
49
  <style>
15
- :host {
16
- display: block;
50
+ #inputs-wrapper {
51
+ display: flex;
52
+ flex-wrap: wrap;
53
+ flex-direction: row;
54
+ position: relative;
17
55
  }
18
- paper-button {
19
- font-size: 14px;
56
+
57
+ .input, .nest, .list-group, .group, .step{
58
+ flex: 1;
59
+ min-width: 100%;
60
+ max-width: 0;
61
+ position: relative;
62
+ display: inline-block;
63
+ box-sizing: border-box;
64
+ margin-bottom: 0px;
20
65
  }
21
66
 
22
- paper-button core-icon {
23
- margin-right: 5px;
24
- color: #5bc0de;
67
+ .input {
68
+ padding: 0px 10px;
25
69
  }
26
-
27
- #submit-button {
28
- margin-top: 15px;
29
- margin-bottom: 5px;
30
- padding-left: 30px;
31
- padding-right: 30px;
32
- background-color: #5bc0de;
33
- border-color: #46b8da;
34
- color: white;
35
- clear: both;
36
- margin-right: 15px;
70
+
71
+ .input.json {
72
+ padding: 0px;
37
73
  }
38
74
 
39
- #submit-button.in-transit span{
75
+ .input.json .json-wrapper > textarea {
40
76
  display: none;
41
77
  }
42
78
 
43
- #submit-button.in-transit form-spinner {
44
- display: block;
79
+ .nest {
80
+ display: flex;
81
+ flex-wrap: wrap;
45
82
  }
46
83
 
47
- #submit-button form-spinner {
48
- display: none;
84
+ .step:last-of-type {
85
+ margin-bottom: -60px; /* To suck the submit buttom up */
49
86
  }
50
87
 
51
- gc-input-decorator {
52
- text-align: left !important;
88
+ gc-input-decorator.invalid /deep/ .error {
89
+ display: none !important;
53
90
  }
54
91
 
55
- gc-input-decorator .label {
56
- text-align: left !important;
92
+ /* File upload CSS */
93
+ .btn-file-upload input[type=file] {
94
+ position: absolute;
95
+ top: 0;
96
+ left: 0;
97
+ width: 100%;
98
+ height: 100%;
99
+ font-size: 999px;
100
+ text-align: right;
101
+ filter: alpha(opacity=0);
102
+ opacity: 0;
103
+ outline: none;
104
+ background: white;
105
+ cursor: pointer;
106
+ display: block;
107
+ z-index: 1;
57
108
  }
58
109
 
59
- .btn-image-upload {
110
+ .btn-file-upload {
60
111
  position: relative;
61
112
  overflow: hidden;
62
- width: 100%;
63
- height: 59px;
64
- display: table;
65
- }
66
-
67
- .btn-image-upload core-icon {
68
- height: 105px;
69
- width: 105px;
70
- margin: 0 auto;
71
113
  display: block;
72
- color: #666;
114
+
73
115
  }
74
116
 
117
+ /* Image upload CSS */
75
118
  .btn-image-upload input[type=file] {
76
119
  position: absolute;
77
120
  top: 0;
@@ -84,78 +127,78 @@
84
127
  opacity: 0;
85
128
  outline: none;
86
129
  background: white;
87
- cursor: inherit;
130
+ cursor: pointer;
88
131
  display: block;
132
+ z-index: 1;
89
133
  }
90
134
 
91
- .list-group {
92
- width: 100%;
93
- box-sizing: border-box;
135
+ .btn-image-upload {
136
+ position: relative;
137
+ overflow: hidden;
94
138
  display: block;
95
- padding: 9px 0px 0px 10px;
96
- font-size: 14px;
139
+ font-size: 1000%;
140
+ width: 1.5em;
141
+ height: 1em;
142
+ text-align: center;
143
+ margin: 0px auto;
144
+ background-size: contain;
145
+ background-repeat: no-repeat;
146
+ background-position: center;
97
147
  }
98
148
 
99
- .list-group .list-item {
149
+ .btn-image-upload core-icon {
150
+ height: 100%;
100
151
  width: 100%;
152
+ margin: 0 auto;
153
+ display: block;
154
+ color: #666;
155
+ font-size: 10%;
101
156
  }
102
157
 
103
- .list-group .list-item .list-item-display {
104
- border-bottom: solid 1px;
105
- display: flex;
106
- flex-direction: row;
158
+ .has-image .btn-image-upload core-icon {
159
+ display: none;
107
160
  }
108
161
 
109
- .list-group .list-item .list-item-display div {
110
- flex: 1;
111
- cursor: pointer;
162
+ .has-image .btn-image-upload p {
163
+ display: none;
112
164
  }
113
165
 
114
- .list-wrapper {
115
- overflow: auto;
116
- font-size: 0px;
117
- padding-top: 10px;
118
- min-height: 55px;
166
+ .btn-image-upload div {
167
+ height: 75%;
168
+ width: 100%;
119
169
  }
120
170
 
121
- .list-wrapper paper-button {
122
- max-width: 28%;
123
- margin: 0px;
171
+ .btn-image-upload p {
172
+ font-size: 10%;
173
+ margin-top: 0px;
124
174
  }
125
175
 
126
- .list-wrapper paper-button /deep/ .button-content{
127
- -webkit-justify-content: flex-start !important;
128
- justify-content: flex-start !important;
176
+ /* Lists */
177
+
178
+ .list-group > .list-form {
179
+ display: none;
129
180
  }
130
181
 
131
- .list-wrapper .list {
132
- overflow: auto;
133
- position: relative;
134
- float: right;
135
- box-sizing: border-box;
136
- width: 72%;
137
- max-height: 81px;
182
+ .list-group > .list-item {
183
+ display: block;
138
184
  }
139
185
 
140
- .list-wrapper h4 {
141
- padding: 10px 15px;
142
- float: left;
143
- box-sizing: border-box;
144
- width: 25%;
145
- margin: 0;
146
- font-weight: 500;
147
- font-size: 14px;
186
+ .list-group.focused .list-item, .list-group.is-invalid .list-item, .list-group.is-blank > .list-item {
187
+ display: none;
148
188
  }
149
189
 
150
- .list-group .list-item {
190
+ .list-group.focused .list-form, .list-group.is-invalid .list-form, .list-group.is-blank > .list-form {
151
191
  display: block;
152
- list-style-type: none;
153
- margin: 0;
154
- padding: 0 15px;
155
- box-sizing: border-box;
192
+ overflow: hidden;
156
193
  }
157
194
 
158
- .list-group .list-item core-icon {
195
+ .list-group .list-item-display {
196
+ display: flex;
197
+ border-bottom: solid 1px;
198
+ margin-bottom: 5px;
199
+ }
200
+
201
+ .list-group .list-item-display > core-icon {
159
202
  height: 0px;
160
203
  border: solid 1px #A1A1A1;
161
204
  padding: 7px;
@@ -163,25 +206,68 @@
163
206
  border-radius: 8px;
164
207
  color: red;
165
208
  margin-right: 10px;
209
+ cursor: pointer;
166
210
  }
167
211
 
168
- .list-group .list-form {
169
- display: none;
212
+ .list-group .list-item-display > div {
213
+ cursor: pointer;
214
+ flex: 1;
170
215
  }
171
216
 
172
- .list-group.focused .list-item, .list-group.is-invalid .list-item, .list-group.is-blank .list-item {
173
- display: none;
217
+ .nest.addable {
218
+ padding-left: 25%;
174
219
  }
175
220
 
176
- .list-group.focused .list-form, .list-group.is-invalid .list-form, .list-group.is-blank .list-form {
177
- display: block;
178
- overflow: hidden;
221
+ .nest.addable .add-button {
222
+ flex: 1;
223
+ min-width: 25%;
224
+ max-width: 25%;
225
+ margin: 0;
226
+ padding: 0px 1em 0px 0em;
227
+ box-sizing: border-box;
228
+ min-height: 4em;
229
+ margin-left: -25%;
230
+ }
231
+
232
+ .nest.addable .add-button core-icon {
233
+ min-width: 2em;
234
+ min-height: 2em;
235
+
236
+ margin-left: -2.5em;
237
+ display: inline-block;
238
+ position: absolute;
239
+ left: 0;
240
+ color: #5bc0de;
241
+ }
242
+
243
+ .nest.addable .add-button div {
244
+ display: inline-block;
245
+ }
246
+
247
+ .list-group .list-form .list-group .list-item {
248
+ min-width: 100%;
249
+ }
250
+
251
+ .addable .list-form, .addable .list-item {
252
+ min-width: 100%;
253
+ }
254
+
255
+ .list-group .list-form {
256
+
179
257
  }
180
258
 
259
+ .list-group .list-item {
260
+ padding: 10px;
261
+ box-sizing: border-box;
262
+ }
263
+
264
+
265
+ /* Checkboxes */
181
266
  paper-checkbox input[type=checkbox] {
182
267
  display: none;
183
268
  }
184
269
 
270
+ /* Selects */
185
271
  .select-wrapper {
186
272
  position: relative;
187
273
  }
@@ -218,6 +304,13 @@
218
304
  height: 35px;
219
305
  }
220
306
 
307
+ /* Firefox fix */
308
+ .select-wrapper .select-icon svg {
309
+ width: 35px;
310
+ right: 0;
311
+ left: auto;
312
+ }
313
+
221
314
  .select-wrapper .pseudo-select > div {
222
315
  min-height: 2em;
223
316
  border-bottom: solid 1px;
@@ -234,88 +327,103 @@
234
327
  margin-right: 15px;
235
328
  }
236
329
 
330
+ .input.id {
331
+ display: none;
332
+ }
333
+
334
+ /* Autocomplete */
335
+
336
+ .autocomplete-selector {
337
+ position: absolute;
338
+ background-color: white;
339
+ box-shadow: -2px 1px 10px #888;
340
+ z-index: 1;
341
+ padding: 5px;
342
+ border: solid 1px #888;
343
+ box-sizing: border-box;
344
+ margin-top: -11px;
345
+ }
346
+
347
+ .autocomplete-selector .autocomplete-list-item {
348
+ border-bottom: solid 1px #ccc;
349
+ margin-top: 5px;
350
+ padding: 0px 10px 0px 0px;
351
+ }
352
+
353
+ .autocomplete-selector .autocomplete-list-item:last-of-type {
354
+ border-bottom: 0px;
355
+ }
356
+
357
+ .autocomplete-selector .autocomplete-list-item.core-selected {
358
+ background-color: #D5DAFF;
359
+ }
360
+
361
+ .autocomplete-selector .autocomplete-list-item:hover {
362
+ background-color: #D5DAFF;
363
+ }
364
+
365
+ /* Submit Button */
366
+
367
+ #submit-button, .step .next-button, .step .back-button {
368
+ margin-top: 15px;
369
+ margin-bottom: 5px;
370
+ padding-left: 30px;
371
+ padding-right: 30px;
372
+ background-color: #5bc0de;
373
+ border-color: #46b8da;
374
+ color: white;
375
+ clear: both;
376
+ margin-right: 15px;
377
+ }
378
+
379
+ .stepped .step .back-button {
380
+ display: none;
381
+ }
382
+
383
+ .stepped .step ~ .step .back-button {
384
+ display: inline-block;
385
+ }
386
+
387
+ .stepped #submit-button, .stepped .next-button {
388
+ float: right;
389
+ clear: inherit;
390
+ }
391
+
392
+ .stepped .back-button {
393
+ float: left;
394
+ }
395
+
396
+ #submit-button.in-transit span{
397
+ display: none;
398
+ }
399
+
400
+ #submit-button.in-transit form-spinner {
401
+ display: block;
402
+ }
403
+
404
+ #submit-button form-spinner {
405
+ display: none;
406
+ }
237
407
  </style>
238
408
  <!-- TODO: structures should be arrays to preserve order -->
239
409
  <form id="rails_form" _action="{{ action }}" method="{{ method }}" enctype="multipart/form-data">
240
- <template bind="{{ data as data }}">
241
- <template bind="{{ structure as structure }}" bind="{{ scope as scope }}" id="form-group">
242
- <template repeat="{{ key in structure | getKeys }}">
243
- <div class='{{ structure[key] | wrapperClass }} {{ key }} {{ structure[key] | isHidden }}'>
244
- <template if="{{ structure[key] | isNest }}">
245
- <template if="{{ structure[key].allowAdd }}">
246
- <paper-button class='{{ key | scopeToClass }}' on-click="{{ addItem }}"><core-icon icon="add-circle-outline"></core-icon>{{ structure[key].label }}</paper-button>
247
- </template>
248
- <template if="{{ structure[key] | isMultiple }}">
249
- <template if="{{ !structure[key].allowAdd }}">
250
- <template repeat="{{ item in data | scopeNestData(key) | enumerate }}">
251
- <template ref="form-group" bind="{{ structure[key].structure as structure }}" parentContext="{{ key | updateParentContext }}" scope="{{ scope | updateScope(key) | addIndex(item) }}" data="{{ data | scopeDataToIndex(item) }}"></template>
252
- </template>
253
- </template>
254
-
255
- <template if='{{ structure[key].allowAdd }}'>
256
- <template bind="{{ data as data }}">
257
- <div class='list'>
258
- <template repeat="{{ item in data | scopeNestData(key) | enumerate }}">
259
- <div class='list-group {{ item | isEmpty }}'>
260
- <div class='list-item {{ item | listItemClassName }}'>
261
- {{ item | createListItem }}
262
- </div>
263
- <div class='list-form {{ item | listItemClassName }}'>
264
- <template ref="form-group" bind="{{ structure[key].structure as structure }}" parentContext="{{ key | updateParentContext }}" scope="{{ scope | updateScope(key) | addIndex(item) }}" data="{{ data | scopeDataToIndex(item) }}"></template>
265
- </div>
266
- </div>
267
- </template>
268
- </div>
269
- </template>
270
- </template>
271
- </template>
272
-
273
- <template if="{{ structure[key] | isNotMultiple }}">
274
- <template bind="{{ context as context }}">
275
- <template ref="form-group" bind="{{ structure[key].structure as structure }}" parentContext="{{ key | updateParentContext }}" scope="{{ scope | updateScope(key) }}" data="{{ data | scopeNestData(key) }}"></template>
276
- </template>
277
- </template>
278
- </template>
279
-
280
- <template if="{{ structure[key] | isField }}">
281
- <template bind="{{ data as data }}">
282
- <div class="input {{ key | scopeToClass }}" scope="{{ data | scopeFieldData(key) }}">
283
- {{ structure[key] | createInput }}
284
- </div>
285
- </template>
286
- </template>
287
- </div>
288
- </template>
289
- </template>
290
- </template>
410
+ <div id='inputs-wrapper'>
411
+
412
+ </div>
413
+
291
414
  <div class='sumbit-button-wrapper'>
292
415
  <paper-button id='submit-button' raised>
293
416
  <span class='submit-text'>{{ submitText }}</span>
294
- <form-spinner height="1.5" width="1.5"></form-spinner>
417
+ <form-spinner height="1.5" width="1.5" unit='em'></form-spinner>
295
418
  </paper-button>
296
- <!-- <paper-button id='test-button' raised>Test</paper-button> -->
297
419
  <template if="{{ xhr }}">
298
420
  <core-xhr id='xhr'></core-xhr>
299
421
  </template>
300
- </div>
422
+ </div>
423
+
301
424
  </form>
302
425
  </template>
303
426
  <script>
304
- /*
305
- Default Structure Item: {
306
- type: "string",
307
- label: "",
308
- options: {
309
- floatingLabel: true,
310
- unscoped: false,
311
- events: {}
312
- },
313
- attributes: {
314
-
315
- }
316
- }
317
- */
318
-
319
427
  Polymer( Polymer.mixin ({
320
428
  publish: {
321
429
  scope: "",
@@ -328,21 +436,17 @@
328
436
  unscopedData: null
329
437
  },
330
438
 
331
- parentContext: null,
332
- parentStructure: null,
333
- parentData: null,
334
-
439
+ jsonData: null,
335
440
  inTransit: false,
336
-
337
- toArray: function(str){
338
- return [str];
339
- },
441
+ steps: null,
340
442
 
341
443
  created: function(){
342
- this.structure = {};
444
+ this.structure = [];
343
445
  this.inputList = [];
344
- this.indexStore = {};
345
446
 
447
+ this.errors = {};
448
+ this.validatedInputs = {};
449
+
346
450
  var data = this.data === null ? {} : this.data;
347
451
  if (!!this.scope && this.scope.replace(/\s/g, "").length > 0){
348
452
  if (data[this.scope] === void(0)) data[this.scope] = {};
@@ -352,17 +456,14 @@
352
456
  }
353
457
 
354
458
  this.unscopedData = data;
355
- this.unscopedScope = this.scope;
356
- },
357
-
358
- handleXhrCallback: function(){
359
-
459
+ this.jsonData = {};
360
460
  },
361
461
 
362
462
  domReady: function(){
463
+ this.buildForm();
464
+
363
465
  var form = this.shadowRoot.querySelector("form"),
364
- button = this.shadowRoot.querySelector("#submit-button"),
365
- test = this.shadowRoot.querySelector("#test-button");
466
+ button = this.shadowRoot.querySelector("#submit-button");
366
467
 
367
468
  var callback = function(resp){
368
469
  this.inTransit = false;
@@ -372,6 +473,7 @@
372
473
  button.addEventListener("click", function(e){
373
474
  // console.log(this.action, this.unscopedData);
374
475
  // return;
476
+
375
477
  if (this.inTransit) return;
376
478
  if (this.isFormInvalid()) return;
377
479
 
@@ -401,12 +503,6 @@
401
503
  }
402
504
  }.bind(this), false)
403
505
 
404
- this.appendInputs();
405
- var inputObserver = new ArrayObserver(this.inputList);
406
- inputObserver.open(function(splices){
407
- this.appendInputs();
408
- }.bind(this))
409
-
410
506
  form.addEventListener("change", function(e){
411
507
  var group = this.findParent(e.target, ".list-group")
412
508
  if (!!group){
@@ -427,9 +523,260 @@
427
523
 
428
524
  },
429
525
 
430
- get getData() {
431
- return this.data;
526
+ handleXhrCallback: function(){
527
+
528
+ },
529
+
530
+ buildForm: function(){
531
+ var wrapper = this.shadowRoot.querySelector("#inputs-wrapper"),
532
+ self = this;
533
+
534
+ if (!Array.isArray(this.structure)) return;
535
+
536
+ this.buildStructure(this.structure, this.data, wrapper, this.scope);
537
+
538
+ this.steps = wrapper.querySelectorAll(".step.form-step");
539
+ if (this.steps.length > 0){
540
+ this.addClass(this.shadowRoot.querySelector("form"), "stepped");
541
+ this.showStep(0);
542
+
543
+ this.steps[this.steps.length - 1].querySelector(".next-button").style.display = 'none';
544
+ }
545
+ },
546
+
547
+ currentStep: 0,
548
+ showStep: function(index){
549
+ for (var i=0; i<this.steps.length; i++){
550
+ if (i === index){
551
+ this.steps[i].style.display = "";
552
+ } else {
553
+ this.steps[i].style.display = "none";
554
+ }
555
+ }
556
+
557
+ if (index === this.steps.length - 1){
558
+ this.shadowRoot.querySelector("#submit-button").style.display = "";
559
+ } else {
560
+ this.shadowRoot.querySelector("#submit-button").style.display = "none";
561
+ }
562
+ },
563
+
564
+ nextStep: function(){
565
+ if (this.currentStep < this.steps.length - 1){
566
+ this.currentStep += 1;
567
+ this.showStep(this.currentStep);
568
+ }
569
+ },
570
+
571
+ previousStep: function(){
572
+ if (this.currentStep > 0){
573
+ this.currentStep -= 1;
574
+ this.showStep(this.currentStep);
575
+ }
576
+ },
577
+
578
+ updateStructureDOM: function(splices, structure, data, wrapper, scope, dom, domKeys){
579
+ var self = this;
580
+
581
+ for (var i=0; i<splices.length; i++){
582
+ /* delete removed children */
583
+ for (var j=0; j<splices[i].removed.length; j++){
584
+ var key = splices[i].removed[j].key,
585
+ element = domKeys[key];
586
+
587
+ element.parentNode.removeChild(element),
588
+ dom.splice(dom.indexOf(element), 1);
589
+
590
+ delete dom[key];
591
+ delete data[key];
592
+ }
593
+
594
+ /* add new children */
595
+ for (var j=0; j<splices[i].addedCount; j++){
596
+ var index = splices[i].index + j,
597
+ previousElement = dom[index -1],
598
+ field = self.createField(structure[index], data, scope);
599
+
600
+ if (!!previousElement){
601
+ wrapper.appendChild(field);
602
+ } else {
603
+ previousElement.parentNode.insertBefore(field, previousElement.nextSibling);
604
+ }
605
+
606
+ dom.push(field);
607
+ domKeys[structure[index].key] = field;
608
+ }
609
+ }
610
+ },
611
+
612
+ buildStructure: function(structure, data, wrapper, scope){
613
+ /* handling DOM insertion and removal */
614
+ var dom = [],
615
+ domKeys = {},
616
+ self = this;
617
+
618
+ new ArrayObserver(structure).open(function(splices){
619
+ self.updateStructureDOM(splices, structure, data, wrapper, scope, dom, domKeys);
620
+ self.fire("structure-changed", { ref: this });
621
+ });
622
+
623
+ /* actually building the form */
624
+ for (var i=0; i<structure.length; i++){
625
+ var field = self.createField(structure[i], data, scope)
626
+ wrapper.appendChild(field);
627
+
628
+ dom.push(field);
629
+ domKeys[structure[i].key] = field;
630
+
631
+ if ((structure[i].type === "nest") && structure[i].multiple){
632
+ var watched_data = !!structure[i].unscoped ? this.unscopedData[structure[i].key] : data[structure[i].key];
633
+ new ArrayObserver(watched_data).open( (function(s, f){
634
+ return function(splices){
635
+ var nField = self.createField(s, data, scope);
636
+ f.parentNode.insertBefore(nField, f);
637
+ f.parentNode.removeChild(f);
638
+ f = nField;
639
+
640
+ self.fire("data-inserted", {ref: this});
641
+ };
642
+ })(structure[i], field) );
643
+ }
644
+ }
645
+
646
+ return wrapper;
647
+ },
648
+
649
+ createField: function(structure, data, scope){
650
+ var key = structure.key,
651
+ self = this;
652
+
653
+ if (structure.type === "group"){
654
+ var wrap = this.createWithAttributes("div", {"class": ["group", structure.key, structure.additionalClasses].join(" ")}),
655
+ contents = this.buildStructure(structure.structure, data, wrap, scope);
656
+
657
+ return wrap;
658
+ } else if (structure.type === "step") {
659
+ var wrap = this.createWithAttributes("div", {"class": ["step form-step", structure.key, structure.additionalClasses].join(" ")}),
660
+ next_button = this.createWithAttributes("paper-button", { "class": "next-button", text: (structure.nextText || "Next"), raised: true }),
661
+ back_button = this.createWithAttributes("paper-button", { "class": "back-button", text: (structure.backText || "Back"), raised: true }),
662
+ contents = this.buildStructure(structure.structure, data, wrap, scope);
663
+
664
+ next_button.addEventListener("click", function(){ this.nextStep(); }.bind(this), false);
665
+ back_button.addEventListener("click", function(){ this.previousStep(); }.bind(this), false);
666
+
667
+ wrap.appendChild(back_button);
668
+ wrap.appendChild(next_button);
669
+
670
+ return wrap;
671
+ } else if (structure.type === "nest"){
672
+ var wrap = self.createWithAttributes("div", {"class": "nest " + key}),
673
+ nested_data = self.scopeNestData(structure, key, data),
674
+ nested_scope = self.updateScope(structure, key, scope);
675
+
676
+ if (structure.allowAdd){
677
+ var add_button = self.createWithAttributes("paper-button", {"class": "add-button " + self.scopeToClass(structure, key, scope)}),
678
+ add_icon = self.createWithAttributes("core-icon", {icon: "add-circle-outline"}),
679
+ add_button_text = self.createWithAttributes("div", {text: structure.label || key.replace(/_/g)});
680
+
681
+ add_button.appendChild(add_icon);
682
+ add_button.appendChild(add_button_text);
683
+
684
+ wrap.appendChild(add_button);
685
+ this.addClass(wrap, 'addable');
686
+
687
+ add_button.addEventListener('click', function(){
688
+ var items = wrap.querySelectorAll('.list-group'),
689
+ blank = false;
690
+
691
+ for (var i=0; i<items.length; i++){
692
+ if (self.checkForBlank(items[i])){
693
+ blank = true;
694
+ break;
695
+ }
696
+ }
697
+
698
+ if (!blank) nested_data.unshift({});
699
+ }, false);
700
+ }
701
+
702
+ if (structure.multiple){
703
+
704
+ for (var i=0; i<nested_data.length; i++){
705
+ if (structure.allowAdd){
706
+ var item_wrapper = self.createWithAttributes("div", {"class": "list-group"}),
707
+ list_item = self.createWithAttributes("div", {"class": "list-item"}),
708
+ list_form = self.createWithAttributes("div", {"class": "list-form"});
709
+
710
+ list_item.appendChild( self.createListItem(nested_data[i], structure, nested_data) );
711
+ item_wrapper.appendChild(list_item);
712
+ item_wrapper.appendChild(list_form);
713
+
714
+ self.buildStructure(structure.structure, nested_data[i], list_form, self.addIndex(nested_scope, i));
715
+ } else {
716
+ var item_wrapper = self.createWithAttributes("div", {"class": "list-group"});
717
+ self.buildStructure(structure.structure, nested_data[i], item_wrapper, self.addIndex(nested_scope, i));
718
+ }
719
+
720
+ wrap.appendChild(item_wrapper);
721
+ self.checkForBlank(item_wrapper);
722
+ }
723
+ } else {
724
+ self.buildStructure(structure.structure, nested_data, wrap, nested_scope);
725
+ }
726
+
727
+ return wrap;
728
+
729
+ } else {
730
+ //TODO: set defaults here
731
+ var options = structure.options || {},
732
+ fieldData = self.scopeFieldData((options.unscoped ? self.unscopedData : data), structure, key),
733
+ wrap = self.createWithAttributes("div", {"class": "input " + structure.type + " " + key}),
734
+ inputElement = self.createInput(structure, fieldData, key, scope);
735
+
736
+ wrap.appendChild(inputElement);
737
+ return wrap;
738
+ }
739
+ },
740
+
741
+ getField: function(key){
742
+ var key = key.split("."),
743
+ structure = this.structure;
744
+ for (var i=0; i<key.length; i++){
745
+ if (!structure) return null;
746
+ var field = structure.filter(function(item){ return item.key === key[i] })[0];
747
+ if (!field) return null;
748
+
749
+ structure = field.structure;
750
+ }
751
+
752
+ return field;
753
+ },
754
+
755
+ loadData: function(data){
756
+ //TODO: deal with unscoped data somehow
757
+ var recursive = function(d1, d2, structure){
758
+ if (Array.isArray(d1) && Array.isArray(d2)){
759
+ for (var i=0; i<d2.length; i++){
760
+ if (!d1[i]) d1.push({});
761
+ recursive(d1[i], d2[i], structure);
762
+ }
763
+ } else {
764
+ for (var i=0; i<structure.length; i++){
765
+ if (d2[structure[i].key] !== void(0)){
766
+ if (structure[i].type === 'nest'){
767
+ recursive(d1[structure[i].key], d2[structure[i].key], structure[i].structure);
768
+ } else {
769
+ d1[structure[i].key] = d2[structure[i].key];
770
+ }
771
+ }
772
+ }
773
+ }
774
+ }
775
+
776
+ this.async(function(){
777
+ recursive(this.data, data, this.structure);
778
+ });
432
779
  }
433
- }, FormHelpers, Validation));
780
+ }, FormHelpers, Validation, Autocomplete));
434
781
  </script>
435
782
  </polymer-element>