index.js
function (){

    var outputPropertyName,
        callback,
        inputPropertyNames,
        inputs,
        output;

    if(arguments.length === 0){
      return configurationProperty();
    } else if(arguments.length === 1){
      if(typeof arguments[0] === "object"){

        // The invocation is of the form model(configuration)
        return setConfiguration(arguments[0]);
      } else {

        // The invocation is of the form model(propertyName)
        return addProperty(arguments[0]);
      }
    } else if(arguments.length === 2){
      if(typeof arguments[0] === "function"){

        // The invocation is of the form model(callback, inputPropertyNames)
        inputPropertyNames = arguments[1];
        callback = arguments[0];
        outputPropertyName = undefined;
      } else {

        // The invocation is of the form model(propertyName, defaultValue)
        return addProperty(arguments[0], arguments[1]);
      }
    } else if(arguments.length === 3){
      outputPropertyName = arguments[0];
      callback = arguments[1];
      inputPropertyNames = arguments[2];
    }

    // inputPropertyNames may be a string of comma-separated property names,
    // or an array of property names.
    if(typeof inputPropertyNames === "string"){
      inputPropertyNames = inputPropertyNames.split(",").map(invoke("trim"));
    }

    inputs = inputPropertyNames.map(function (propertyName){
      var property = getProperty(propertyName);
      if(typeof property === "undefined"){
        throw new Error([
          "The property \"",
          propertyName,
          "\" was referenced as a dependency for a reactive function before it was defined. ",
          "Please define each property first before referencing them in reactive functions."
        ].join(""));
      }
      return property;
    });

    // Create a new reactive property for the output and assign it to the model.
    if(outputPropertyName){
      addProperty(outputPropertyName);
      output = model[outputPropertyName];
    }

    // If the number of arguments expected by the callback is one greater than the
    // number of inputs, then the last argument is the "done" callback, and this
    // reactive function will be set up to be asynchronous. The "done" callback should
    // be called with the new value of the output property asynchronously.
    var isAsynchronous = (callback.length === inputs.length + 1);
    if(isAsynchronous){
      reactiveFunctions.push(ReactiveFunction({
        inputs: inputs,
        callback: function (){

          // Convert the arguments passed into this function into an array.
          var args = Array.prototype.slice.call(arguments);

          // Push the "done" callback onto the args array.
          // We are actally passing the output reactive property here, invoking it
          // as the "done" callback will set the value of the output property.
          args.push(output);

          // Wrap in setTimeout to guarantee that the output property is set
          // asynchronously, outside of the current digest. This is necessary
          // to ensure that if developers inadvertently invoke the "done" callback 
          // synchronously, their code will still have the expected behavior.
          setTimeout(function (){

            // Invoke the original callback with the args array as arguments.
            callback.apply(null, args);
          });
        }
      }));
    } else {
      reactiveFunctions.push(ReactiveFunction({
        inputs: inputs,
        output: output, // This may be undefined.
        callback: callback
      }));
    }
    return model;
  }
function (){

    var outputPropertyName,
        callback,
        inputPropertyNames,
        inputs,
        output;

    if(arguments.length === 0){
      return configurationProperty();
    } else if(arguments.length === 1){
      if(typeof arguments[0] === "object"){

        // The invocation is of the form model(configuration)
        return setConfiguration(arguments[0]);
      } else {

        // The invocation is of the form model(propertyName)
        return addProperty(arguments[0]);
      }
    } else if(arguments.length === 2){
      if(typeof arguments[0] === "function"){

        // The invocation is of the form model(callback, inputPropertyNames)
        inputPropertyNames = arguments[1];
        callback = arguments[0];
        outputPropertyName = undefined;
      } else {

        // The invocation is of the form model(propertyName, defaultValue)
        return addProperty(arguments[0], arguments[1]);
      }
    } else if(arguments.length === 3){
      outputPropertyName = arguments[0];
      callback = arguments[1];
      inputPropertyNames = arguments[2];
    }

    // inputPropertyNames may be a string of comma-separated property names,
    // or an array of property names.
    if(typeof inputPropertyNames === "string"){
      inputPropertyNames = inputPropertyNames.split(",").map(invoke("trim"));
    }

    inputs = inputPropertyNames.map(function (propertyName){
      var property = getProperty(propertyName);
      if(typeof property === "undefined"){
        throw new Error([
          "The property \"",
          propertyName,
          "\" was referenced as a dependency for a reactive function before it was defined. ",
          "Please define each property first before referencing them in reactive functions."
        ].join(""));
      }
      return property;
    });

    // Create a new reactive property for the output and assign it to the model.
    if(outputPropertyName){
      addProperty(outputPropertyName);
      output = model[outputPropertyName];
    }

    // If the number of arguments expected by the callback is one greater than the
    // number of inputs, then the last argument is the "done" callback, and this
    // reactive function will be set up to be asynchronous. The "done" callback should
    // be called with the new value of the output property asynchronously.
    var isAsynchronous = (callback.length === inputs.length + 1);
    if(isAsynchronous){
      reactiveFunctions.push(ReactiveFunction({
        inputs: inputs,
        callback: function (){

          // Convert the arguments passed into this function into an array.
          var args = Array.prototype.slice.call(arguments);

          // Push the "done" callback onto the args array.
          // We are actally passing the output reactive property here, invoking it
          // as the "done" callback will set the value of the output property.
          args.push(output);

          // Wrap in setTimeout to guarantee that the output property is set
          // asynchronously, outside of the current digest. This is necessary
          // to ensure that if developers inadvertently invoke the "done" callback 
          // synchronously, their code will still have the expected behavior.
          setTimeout(function (){

            // Invoke the original callback with the args array as arguments.
            callback.apply(null, args);
          });
        }
      }));
    } else {
      reactiveFunctions.push(ReactiveFunction({
        inputs: inputs,
        output: output, // This may be undefined.
        callback: callback
      }));
    }
    return model;
  }
function ( ) { var outputPropertyName , callback , inputPropertyNames , inputs , output ; if ( arguments . length === 0 ) { return configurationProperty ( ) ; } else if ( arguments . length === 1 ) { if ( typeof arguments [ 0 ] === "object" ) { // The invocation is of the form model(configuration) return setConfiguration ( arguments [ 0 ] ) ; } else { // The invocation is of the form model(propertyName) return addProperty ( arguments [ 0 ] ) ; } } else if ( arguments . length === 2 ) { if ( typeof arguments [ 0 ] === "function" ) { // The invocation is of the form model(callback, inputPropertyNames) inputPropertyNames = arguments [ 1 ] ; callback = arguments [ 0 ] ; outputPropertyName = undefined ; } else { // The invocation is of the form model(propertyName, defaultValue) return addProperty ( arguments [ 0 ] , arguments [ 1 ] ) ; } } else if ( arguments . length === 3 ) { outputPropertyName = arguments [ 0 ] ; callback = arguments [ 1 ] ; inputPropertyNames = arguments [ 2 ] ; } // inputPropertyNames may be a string of comma-separated property names, // or an array of property names. if ( typeof inputPropertyNames === "string" ) { inputPropertyNames = inputPropertyNames . split ( "," ) . map ( invoke ( "trim" ) ) ; } inputs = inputPropertyNames . map ( function ( propertyName ) { var property = getProperty ( propertyName ) ; if ( typeof property === "undefined" ) { throw new Error ( [ "The property \"" , propertyName , "\" was referenced as a dependency for a reactive function before it was defined. " , "Please define each property first before referencing them in reactive functions." ] . join ( "" ) ) ; } return property ; } ) ; // Create a new reactive property for the output and assign it to the model. if ( outputPropertyName ) { addProperty ( outputPropertyName ) ; output = model [ outputPropertyName ] ; } // If the number of arguments expected by the callback is one greater than the // number of inputs, then the last argument is the "done" callback, and this // reactive function will be set up to be asynchronous. The "done" callback should // be called with the new value of the output property asynchronously. var isAsynchronous = ( callback . length === inputs . length + 1 ) ; if ( isAsynchronous ) { reactiveFunctions . push ( ReactiveFunction ( { inputs : inputs , callback : function ( ) { // Convert the arguments passed into this function into an array. var args = Array . prototype . slice . call ( arguments ) ; // Push the "done" callback onto the args array. // We are actally passing the output reactive property here, invoking it // as the "done" callback will set the value of the output property. args . push ( output ) ; // Wrap in setTimeout to guarantee that the output property is set // asynchronously, outside of the current digest. This is necessary // to ensure that if developers inadvertently invoke the "done" callback  // synchronously, their code will still have the expected behavior. setTimeout ( function ( ) { // Invoke the original callback with the args array as arguments. callback . apply ( null , args ) ; } ) ; } } ) ) ; } else { reactiveFunctions . push ( ReactiveFunction ( { inputs : inputs , output : output , // This may be undefined. callback : callback } ) ) ; } return model ; }
The model instance object. This is the value returned from the constructor.
The model instance object . This is the value returned from the constructor .