@@ -70,32 +70,59 @@ var init_label_macros = function() {
7070 } ) ;
7171}
7272
73- var fm_renumber = function ( $wrappers ) {
73+ /**
74+ * Renumber all fields within one or more wrappers based on their sequential
75+ * position in the DOM.
76+ *
77+ * When fields/groups are added, removed, or rearranged, impacted fields' names
78+ * need to be adjusted. For instance, if a field's name is group[1][field][2]
79+ * and that group is moved to the top of the list, the field's name needs to be
80+ * adjusted to group[0][field][2] or else the new position will not be saved
81+ * when the data is posted to the server.
82+ *
83+ * @param {jQuery } $wrappers - Wrapping containers of items that need to be
84+ * renumbered.
85+ */
86+ var fm_renumber = function ( $wrappers ) {
7487 var fmtemp = parseInt ( Math . random ( ) * 100000 , 10 ) ;
7588 $wrappers . each ( function ( ) {
7689 var level_pos = $ ( this ) . data ( 'fm-array-position' ) - 0 ;
7790 var order = 0 ;
91+ var modified_elements = [ ] ;
7892 if ( level_pos > 0 ) {
7993 $ ( this ) . find ( '> .fm-item' ) . each ( function ( ) {
94+ // Do not manipulate prototypes (templates).
8095 if ( $ ( this ) . hasClass ( 'fmjs-proto' ) ) {
81- return ; // continue
96+ return ; // continue;
8297 }
98+
99+ // TODO: nested fields get iterated over multiple times, is this avoidable?
83100 $ ( this ) . find ( '.fm-element, .fm-incrementable' ) . each ( function ( ) {
84- var fname = $ ( this ) . attr ( 'name' ) ;
101+ var $element = $ ( this ) ;
102+ var fname = $element . attr ( 'name' ) ;
103+
85104 if ( fname ) {
86- fname = fname . replace ( / \] / g, '' ) ;
87- parts = fname . split ( '[' ) ;
88- if ( parts [ level_pos ] != order ) {
105+ parts = fname . replace ( / \] / g, '' ) . split ( '[' ) ;
106+ if ( Number . parseInt ( parts [ level_pos ] , 10 ) !== order ) {
89107 parts [ level_pos ] = order ;
90108 var new_fname = parts [ 0 ] + '[' + parts . slice ( 1 ) . join ( '][' ) + ']' ;
109+
110+ // If the name hasn't updated, skip unnecessary DOM updates.
111+ if ( new_fname === fname ) {
112+ return ; // continue;
113+ }
114+
91115 // Rename the field and add a temporary prefix to prevent name collisions.
92- $ ( this ) . attr ( 'name' , 'fmtemp_' + ( ++ fmtemp ) . toString ( ) + '_' + new_fname ) ;
93- if ( $ ( this ) . attr ( 'id' ) && $ ( this ) . attr ( 'id' ) . match ( '-proto' ) && ! new_fname . match ( 'proto' ) ) {
94- $ ( this ) . attr ( 'id' , 'fm-edit-dynamic-' + dynamic_seq ) ;
95- if ( $ ( this ) . parent ( ) . hasClass ( 'fm-option' ) ) {
96- $ ( this ) . parent ( ) . find ( 'label' ) . attr ( 'for' , 'fm-edit-dynamic-' + dynamic_seq ) ;
116+ $element . attr ( 'name' , 'fmtemp_' + ( ++ fmtemp ) . toString ( ) + '_' + new_fname ) ;
117+ modified_elements . push ( $element ) ;
118+
119+ // If this field is newly generated from a prototype, update [id]s and [for]s.
120+ if ( $element . attr ( 'id' ) && $element . attr ( 'id' ) . match ( '-proto' ) && ! new_fname . match ( 'proto' ) ) {
121+ $element . attr ( 'id' , 'fm-edit-dynamic-' + dynamic_seq ) ;
122+ if ( $element . parent ( ) . hasClass ( 'fm-option' ) ) {
123+ $element . parent ( ) . find ( 'label' ) . attr ( 'for' , 'fm-edit-dynamic-' + dynamic_seq ) ;
97124 } else {
98- var parent = $ ( this ) . closest ( '.fm-item' ) ;
125+ var parent = $element . closest ( '.fm-item' ) ;
99126 if ( parent . length && parent . find ( '.fm-label label' ) . length ) {
100127 parent . find ( '.fm-label label' ) . attr ( 'for' , 'fm-edit-dynamic-' + dynamic_seq ) ;
101128 }
@@ -105,22 +132,24 @@ var fm_renumber = function( $wrappers ) {
105132 }
106133 }
107134 }
108- if ( $ ( this ) . hasClass ( 'fm-incrementable' ) ) {
109- $ ( this ) . attr ( 'id' , 'fm-edit-dynamic-' + dynamic_seq ) ;
135+ if ( $element . hasClass ( 'fm-incrementable' ) ) {
136+ $element . attr ( 'id' , 'fm-edit-dynamic-' + dynamic_seq ) ;
110137 dynamic_seq ++ ;
111138 }
112139 } ) ;
113140 order ++ ;
114141 } ) ;
115142 }
116- $ ( this ) . find ( '.fm-wrapper' ) . each ( function ( ) {
117- fm_renumber ( $ ( this ) ) ;
118- } ) ;
143+
144+ // Iterate over subgroups.
145+ fm_renumber ( $ ( this ) . find ( '.fm-wrapper' ) ) ;
119146
120147 // Remove temporary name prefix in renumbered fields.
121- $ ( '.fm-element[name^="fmtemp_"], .fm-incrementable[name^="fmtemp_"]' ) . each ( function ( ) {
122- $ ( this ) . attr ( 'name' , $ ( this ) . attr ( 'name' ) . replace ( / ^ f m t e m p _ \d + _ / , '' ) ) ;
123- } ) ;
148+ if ( modified_elements . length ) {
149+ $ . each ( modified_elements , function ( index , $element ) {
150+ $element . attr ( 'name' , $element . attr ( 'name' ) . replace ( / ^ f m t e m p _ \d + _ / , '' ) ) ;
151+ } ) ;
152+ }
124153 } ) ;
125154}
126155
0 commit comments