11// author: Remy Sharp
22// license: http://rem.mit-license.org/
33// source: https://github.com/remy/bind/
4-
54var Bind = ( function Bind ( global ) {
65 'use strict' ;
76 // support check
@@ -11,9 +10,6 @@ var Bind = (function Bind(global) {
1110
1211 var debug = false ;
1312
14- // this is a conditional because we're also supporting node environment
15- var $ ;
16- try { $ = document . querySelectorAll . bind ( document ) ; } catch ( e ) { }
1713 var array = [ ] ;
1814 var isArray = Array . isArray ;
1915 var o = 'object' ;
@@ -112,6 +108,19 @@ var Bind = (function Bind(global) {
112108 if ( settings . ready && object . __callback ) {
113109 return target ;
114110 }
111+
112+ // don't rebind
113+ if ( object instanceof Bind ) {
114+ return object ;
115+ }
116+
117+ // this is a conditional because we're also supporting node environment
118+ var $ ;
119+ try {
120+ var context = settings . context || document ;
121+ $ = context . querySelectorAll . bind ( context ) ;
122+ } catch ( e ) { }
123+
115124 // loop through each property, and make getters & setters for
116125 // each type of "regular" value. If the key/value pair is an
117126 // object, then recursively call extend with the target and
@@ -135,6 +144,10 @@ var Bind = (function Bind(global) {
135144
136145 var selector = settings . mapping [ path . join ( '.' ) ] ;
137146
147+ if ( debug ) {
148+ console . log ( 'key: %s / %s' , key , path . join ( '.' ) , selector ) ;
149+ }
150+
138151 // then we've got an advanced config - rather than 1-1 mapping
139152 if ( selector && selector . toString ( ) === '[object Object]' ) {
140153 if ( selector . callback ) {
@@ -151,14 +164,20 @@ var Bind = (function Bind(global) {
151164 selector = selector . dom ;
152165 }
153166
167+ var elements ;
168+ if ( typeof selector === 'string' ) {
169+ // cache the matched elements. Note the :) is because qSA won't allow an
170+ // empty (or undefined) string so I like the smilie.
171+ elements = $ ( selector || '☺' ) ;
172+ } else if ( global . Element && selector instanceof global . Element ) {
173+ elements = [ selector ] ;
174+ }
175+
154176 // look for the path in the mapping arg, and if the gave
155177 // us a callback, use that, otherwise...
156178 if ( typeof selector === 'function' ) {
157179 callback = selector ;
158- } else if ( typeof selector === 'string' ) {
159- // cache the matched elements. Note the :) is because qSA won't allow an
160- // empty (or undefined) string so I like the smilie.
161- var elements = $ ( selector || '☺' ) ;
180+ } else if ( elements ) {
162181 if ( elements . length === 0 ) {
163182 console . warn ( 'No elements found against "' + selector + '" selector' ) ;
164183 }
@@ -167,17 +186,20 @@ var Bind = (function Bind(global) {
167186 // matched from the selector (set up below), that checks
168187 // the node type, and either sets the input.value or
169188 // element.innerHTML to the value
170- var valueSetters = [ 'SELECT ' , 'INPUT' , 'PROGRESS' , 'TEXTAREA' ] ;
189+ var valueSetters = [ 'OPTION ' , 'INPUT' , 'PROGRESS' , 'TEXTAREA' ] ;
171190
172191 if ( value === null || value === undefined ) {
173192 if ( valueSetters . indexOf ( elements [ 0 ] . nodeName ) !== - 1 ) {
174- value = parse ( elements [ 0 ] . value ) ;
193+ if ( elements [ 0 ] . hasOwnProperty ( 'checked' ) ) {
194+ value = parse ( elements [ 0 ] . value === 'on' ? elements [ 0 ] . checked : elements [ 0 ] . value ) ;
195+ } else {
196+ value = parse ( elements [ 0 ] . value ) ;
197+ }
175198 } else {
176199 value = parse ( elements [ 0 ] . innerHTML ) ;
177200 }
178201 }
179202
180-
181203 var oldCallback = callback ;
182204 callback = function ( value ) {
183205 // make it a live selection
@@ -193,7 +215,7 @@ var Bind = (function Bind(global) {
193215 if ( valueSetters . indexOf ( element . nodeName ) !== - 1 ) {
194216 // TODO select[multiple]
195217 // special case for multi-select items
196- var result = transform ( value ) ;
218+ var result = transform ( value , target ) ;
197219 if ( element . type === 'checkbox' ) {
198220 if ( value instanceof Array ) {
199221 var found = value . filter ( function ( value ) {
@@ -205,6 +227,8 @@ var Bind = (function Bind(global) {
205227 if ( found . length === 0 ) {
206228 element . checked = false ;
207229 }
230+ } else if ( typeof value === 'boolean' ) {
231+ element . checked = value ;
208232 }
209233 } else if ( element . type === 'radio' ) {
210234 element . checked = element . value === result ;
@@ -222,11 +246,21 @@ var Bind = (function Bind(global) {
222246 if ( ! ( value instanceof Array ) ) {
223247 value = [ value ] ;
224248 }
225- var html = '' ;
249+ var html = [ ] ;
250+
226251 forEach ( value , function ( value ) {
227- html += transform ( value ) ;
252+ html . push ( transform ( value , target ) ) ;
228253 } ) ;
229- element . innerHTML = html ;
254+ // peek the first item, if it's a node, append
255+ // otherwise set the innerHTML
256+ if ( typeof html [ 0 ] === 'object' ) {
257+ element . innerHTML = '' ; // blow away original
258+ html . forEach ( function ( el ) {
259+ element . appendChild ( el ) ;
260+ } ) ;
261+ } else {
262+ element . innerHTML = html . join ( '' ) ;
263+ }
230264 }
231265 } ) ;
232266 }
@@ -239,27 +273,29 @@ var Bind = (function Bind(global) {
239273 // note that this doesn't support event delegation
240274 forEach ( elements , function ( element ) {
241275 if ( element . nodeName === 'INPUT' || element . nodeName === 'SELECT' || element . nodeName === 'TEXTAREA' ) {
276+
277+ // build up the event handler function
242278 var oninput = function ( ) {
243279 // we set a dirty flag against this dom node to prevent a
244280 // circular update / max stack explode
245281 this . __dirty = true ;
246282 var result ;
247283 if ( element . type === 'checkbox' ) {
248- var inputs = ( element . form || document ) . querySelectorAll ( 'input[name="' + element . name + '"][type="' + element . type + ' "]') ;
284+ var inputs = ( element . form || document ) . querySelectorAll ( 'input[name="' + element . name + '"][type="checkbox "]' ) ;
249285 if ( target [ key ] instanceof Array ) {
250286 var results = [ ] ;
251287 forEach ( inputs , function ( input ) {
252288 if ( input . checked ) {
253- results . push ( parse ( input . value ) ) ;
289+ results . push ( parse ( input . value === 'on' ? input . checked : input . value ) ) ;
254290 }
255291 } ) ;
256292 result = results ;
257293 } else {
258- result = this . checked ? parse ( this . value ) : null ;
294+ result = parse ( this . value === 'on' ? this . checked : this . value ) ;
259295 }
260296 } else {
261297 if ( element . type === 'radio' ) {
262- result = this . checked ? parse ( this . value ) : null ;
298+ result = parse ( this . value === 'on' ? this . checked : this . value ) ;
263299 } if ( typeof target [ key ] === 'number' ) {
264300 result = parse ( this . value * 1 ) ;
265301 } else {
@@ -280,6 +316,7 @@ var Bind = (function Bind(global) {
280316 } ;
281317
282318 var event = {
319+ // select: 'change',
283320 checkbox : 'change' ,
284321 radio : 'change' ,
285322 } [ element . type ] ;
@@ -356,7 +393,7 @@ var Bind = (function Bind(global) {
356393 }
357394
358395 if ( dirty && always ) {
359- var instance = always . instance ;
396+ instance = always . instance ;
360397 always . callback . call ( settings . instance , __export ( instance instanceof Array ? [ ] : { } , instance ) ) ;
361398 }
362399 } ;
@@ -383,7 +420,9 @@ var Bind = (function Bind(global) {
383420 value = v ;
384421 }
385422
386- if ( debug ) console . log ( 'set: key(%s): %s -> %s' , key , JSON . stringify ( old ) , JSON . stringify ( v ) ) ;
423+ if ( debug ) {
424+ console . log ( 'set: key(%s): %s -> %s' , key , JSON . stringify ( old ) , JSON . stringify ( v ) ) ;
425+ }
387426
388427 // expose the callback so that child properties can call the
389428 // parent callback function
@@ -398,7 +437,9 @@ var Bind = (function Bind(global) {
398437 findCallback ( value ) ;
399438 } else {
400439 // defer the callback until we're fully booted
401- settings . deferred . push ( findCallback . bind ( target , value , old ) ) ;
440+ if ( typeof settings . mapping [ path . join ( '.' ) ] !== 'undefined' ) {
441+ settings . deferred . push ( findCallback . bind ( target , value , old ) ) ;
442+ }
402443 }
403444 } ,
404445 get : function ( ) {
@@ -410,7 +451,9 @@ var Bind = (function Bind(global) {
410451 try {
411452 Object . defineProperty ( target , key , definition ) ;
412453 } catch ( e ) {
413- // console.log(e.toString(), e.stack);
454+ if ( debug ) {
455+ console . log ( 'failed on Object.defineProperty' , e . toString ( ) , e . stack ) ;
456+ }
414457 }
415458
416459 // finally, set the target aka the returned value's property to the value
@@ -454,7 +497,8 @@ var Bind = (function Bind(global) {
454497 __export ( target [ key ] || { } , value ) :
455498 value ;
456499 } ) ;
457- } else if ( typeof value === o && value !== null && ! isArray ( value ) ) {
500+ } else if ( typeof value === o && value !== null && ! isArray ( value ) &&
501+ value . toString === '[Object object]' ) {
458502 target [ key ] = __export ( target [ key ] || { } , value ) ;
459503 } else {
460504 target [ key ] = value ;
@@ -465,12 +509,13 @@ var Bind = (function Bind(global) {
465509 }
466510
467511
468- function Bind ( obj , mapping ) {
512+ function Bind ( obj , mapping , context ) {
469513 if ( ! this || this === global ) {
470514 return new Bind ( obj , mapping ) ;
471515 }
472516
473517 var settings = {
518+ context : context || global . document ,
474519 mapping : mapping || { } ,
475520 callbacks : { } ,
476521 deferred : [ ] ,
@@ -512,4 +557,4 @@ var Bind = (function Bind(global) {
512557
513558if ( typeof exports !== 'undefined' ) {
514559 module . exports = Bind ;
515- }
560+ }
0 commit comments