function actionChanged( src, dst, id, value, propertyName, container, wave ) {
  if( !Array.isArray( wave ) ) wave = [];

  if( this.dbg ) {
    console.log( "Link " + this.dbg + ": ", {
      src: src, dst: dst, id: id, value: value, propertyName: propertyName, container: container, wave: wave
    } );
  }
  if( hasAlreadyBeenHere( id, wave ) ) {
    if( this.dbg ) {
      console.log( "...has been BLOCKED by the wave! ", wave );
    }
    return;
  }

  var that = this;

  var pmSrc = PropertyManager( src.obj );
  var pmDst = PropertyManager( dst.obj );

  value = processValue( value, src, dst );
  value = processSwitch( value, dst, pmSrc );
  value = processConverter( value, src, dst );
  if( filterFailed( value, src, dst ) ) {
    if( this.dbg ) console.log( "...has been FILTERED!" );
    return;
  }
  value = processFormat( value, src, dst );
  value = processMap( value, src, dst );

  if( typeof dst.delay === 'number' ) {
    if( this.debug ) console.log( "...has been DELAYED for " + dst.delay + " ms!" );
    clearTimeout( dst._id );
    dst._id = setTimeout(function() {
      if( that.dbg ) {
        console.log( "Link " + that.dbg + " (after " + dst.delay + " ms): ", {
          src: src, dst: dst, id: id, value: value, propertyName: propertyName, wave: wave
        } );
        console.log("...try to change a value. ", {
          target: pmDst,
          propertyName: dst.name,
          value: value,
          wave: wave
        });
      }
      pmDst.change( dst.name, value, wave );
    }, dst.delay);
  } else {
    if( this.debug )
      console.log("...try to change a value. ", {
        target: pmDst,
        propertyName: dst.name,
        value: value,
        wave: wave
      });
    pmDst.change( dst.name, value, wave );
  }
}
  it('should use converter', function() {
    var obj1 = {};
    PM( obj1 ).create( "x" );
    var obj2 = {};
    PM( obj2 ).create( "y" );

    new Link({
      A:{ obj:obj1, name:"x" },
      B:{ obj:obj2, name:"y", converter: v => 2*v }
    });

    obj1.x = 27;
    expect( obj2.y ).toBe( 54 );
  });
    it('should propagate value when item is added to a List', function(){
      var obj1 = {};
      PM( obj1 ).create( "list", {init: new List([1,2])} );
      var obj2 = {};
      PM( obj2 ).create( "list", {init: new List([3,4])} );
      
      new Link({
        A: { obj:obj1, name:'list' },
        B: { obj:obj2, name:'list' }
      });

      obj1.list.push( 9 );
      expect( obj2.list.slice() ).toEqual([ 1,2,9 ]);
    });
    it('should propagate the length when an item is pushed', function(){
      var obj1 = {};
      PM( obj1 ).create( "list", {init: new List([])} );
      var obj2 = {};
      PM( obj2 ).create( "size", {init: 0} );
      
      new Link({ name: "<LINK>",
        A: { obj:obj1, name:'list' },
        B: { obj:obj2, name:'size', converter: a => a.length }
      });

      obj1.list.push( 6 );
      obj1.list.push( 3 );
      expect( obj2.size ).toBe( 2 );
    });
function processConverter( value, src, dst ) {
  if( typeof dst.converter === 'function' ) {
    try {
      return dst.converter( value );
    }
    catch( ex ) {
      console.error( ex );
      fail(
        "Error in converter of link "
          + PropertyManager(src.obj) + "." + src.name
          + " -> "
          + PropertyManager(dst.obj) + "." + dst.name + "!"
      );
    }
  }
  return value;
}
Beispiel #6
0
 ["innerHTML", "innerhtml"].forEach(function(name) {
       PM(this).create(name, {
           get: function() { return elem.innerHTML; },
           set: function(v) {
               elem.innerHTML = v;
           }
       });
   }, this);
Beispiel #7
0
function defineAttribFocus(elem) {
    var that = this;
    PM(this).create('focus', {
        cast: Converters.get('boolean')(),
        delay: 1
    });
    PM(this).on("focus", function(v) {
        if (v) elem.focus();
        else elem.blur();
    });
    elem.addEventListener("focus", function() {
        that.focus = true;
    }, false);
    elem.addEventListener("blur", function() {
        that.focus = false;
    }, false);
}
function filterFailed(value, src, dst) {
  if( typeof dst.filter === 'function' ) {
    try {
      if( !dst.filter( value ) ) return true;
    }
    catch( ex ) {
      console.error( ex );
      fail(
        "Error in filter of link "
          + PropertyManager(src.obj) + "." + src.name
          + " -> "
          + PropertyManager(dst.obj) + "." + dst.name + "!"
      );
    }
  }
  return false;
}
function processValue( value, src, dst ) {
  if( typeof dst.value === 'undefined' ) return value;
  if( typeof dst.value === 'function' ) {
    try {
      return dst.value( src.name );
    }
    catch( ex ) {
      console.error( ex );
      fail(
        "Error in value(" + src.name + ") of link "
          + PropertyManager(src.obj) + "." + src.name
          + " -> "
          + PropertyManager(dst.obj) + "." + dst.name + "!"
      );
    }
  }
  return dst.value;
}
Beispiel #10
0
function defineStandardAttrib(elem, attName) {
    PM(this).create(attName, {
        get: function() {
            return elem.getAttribute(attName);
        },
        set: function(v) {
            elem.setAttribute(attName, v);
        }
    });
}
  it('should propagate new value of first property to the third', function() {
    var obj1 = {};
    PM( obj1 ).create( "x" );
    var obj2 = {};
    PM( obj2 ).create( "y" );
    var obj3 = {};
    PM( obj3 ).create( "z" );

    new Link({
      A:{ obj:obj1, name:"x" },
      B:{ obj:obj2, name:"y" }
    });
    new Link({
      A:{ obj:obj2, name:"y" },
      B:{ obj:obj3, name:"z" }
    });

    obj1.x = 27;
    expect( obj3.z ).toBe( 27 );
  });
Beispiel #12
0
  ["textContent", "textcontent"].forEach(function(name) {
        PM(this).create(name, {
            get: function() { return elem.textContent; },
            set: function(v) {
                if (typeof v !== 'string') v = "" + v;

                if (v.substr(0, 6) === '<html>')
                    elem.innerHTML = v.substr(6);
                else
                    elem.textContent = v;
            }
        });
    }, this);
Beispiel #13
0
function defineAttribValue(elem) {
    var that = this;

    var lastSettedValue = null;
    PM(this).create('value', {
        get: function() { return lastSettedValue; },
        set: function(v) {
            elem.value = v;
            lastSettedValue = v;
        }
    });
    elem.addEventListener("input", function(evt) {
        PM(that).change('value', evt.target.value);
    }, false);
}
/**
 * `format` is used  with an intl function.  It must  be an array with
 * two elements: a function and a string.  The function will be called
 * with the  string as first argument  and the `value` as  second. The
 * result will be the transformed value.
 */
function processFormat( value, src, dst ) {
  if( !dst.format ) return value;
  try {
    if( !Array.isArray( dst.format ) )
      throw "Must be an array with two elements!";
    var intlFunc = dst.format[0];
    if( typeof intlFunc !== 'function' )
      throw "First element of the array must be a function!";
    var intlId = dst.format[1];
    if( typeof intlId !== 'string' )
      throw "Second element of the array must be a string!";
    return intlFunc( intlId, value );
  }
  catch( ex ) {
    console.error( ex );
    fail(
      "Error in format of link "
        + PropertyManager(src.obj) + "." + src.name
        + " -> "
        + PropertyManager(dst.obj) + "." + dst.name + "!\n"
        + ex
    );
  }
}
 args[emitterKey].forEach(function (emitter, emitterIndex) {
   if( typeof emitter.name === 'string'
       && typeof receiver.name === 'string'
       && emitter.obj === receiver.obj
       && emitter.name === receiver.name )
   {
     console.error(
       "It is forbidden to bind a property on itself! ("
         + emitterIndex + " -> " + receiverIndex + ")"
     );
     console.info("[tfw.binding.link] args=", args);
     return;
   }
   var pmEmitter = PropertyManager( emitter.obj );
   var slot = actionChanged.bind( that, emitter, receiver, id );
   pmEmitter.on( emitter.name, slot );
   onChanged.push({ pm: pmEmitter, name: emitter.name, slot: slot });
 });
Beispiel #16
0
exports.defProp = function( obj, name, opts, initialValues ) {
  var pm = PropertyManager( obj );

  if( typeof opts === 'undefined' ) opts = {};
  if( typeof opts === 'function' ) opts = { set: opts };
  if( Array.isArray( opts ) ) opts = { init: opts };
  if( typeof opts === 'string' ) opts = { 
    init: opts,
    cast: function(v) { return "" + v; }
  };
  if( typeof opts === 'number' ) opts = { 
    init: opts,
    cast: function(v) { return parseFloat( v ); }
  };
  if( typeof opts === 'boolean' ) opts = { 
    init: opts,
    cast: function(v) { return v ? true : false; }
  };
  if( typeof opts !== 'object' ) opts = { init: opts };

  pm.converter( name, createConverter( opts.cast ) );
  pm.delay( name, opts.delay );
  if( typeof opts.set === 'function' ) {
    pm.on( name, opts.set );
  }

  Object.defineProperty( obj, name, {
    set: pm.change.bind( pm, name ),
    get: pm.get.bind( pm, name ),
    configurable: false,
    enumerable: true
  });

  // Intitial value.
  if( typeof initialValues !== 'undefined' && typeof initialValues[name] !== 'undefined') {
    opts.init = initialValues[name];
  }
  if( typeof opts.init !== 'undefined' ) {
    pm.set( name, opts.init );
  }
};
function checkPod( pod, index ) {
  try {
    if( !pod.action ) {
      if( typeof pod.obj === 'undefined' ) fail("Missing `[" + index + "].obj`!");
      if( typeof pod.name === 'undefined' ) pod.name = "*";
      // Check if the attribute exists.
      if( !PropertyManager.isLinkable( pod.obj, pod.name ) )
        throw "`" + pod.name + "` is not a linkable attribute.\n"
        + "Valid linkable attributes are: "
        + PropertyManager.getAllAttributesNames( pod.obj ).join(", ") + ".";
    }
    else if ( typeof pod.action !== 'function' ) {
      throw "Attribute `[" + index + "].action` must be a function!";
    }
    else {
      if( typeof pod.obj !== 'undefined' )
        throw "[" + index + "].action cannot be defined in the same time of ["
        + index + "].obj! They are exclusive attributes.";
      if( typeof pod.name !== 'undefined' )
        throw "[" + index + "].action cannot be defined in the same time of ["
        + index + "].name! They are exclusive attributes.";

      // An action is emulated by a hollow object.
      var hollowObject = {};
      PropertyManager( hollowObject ).create("<action>", {
        set: pod.action
      });
      pod.obj = hollowObject;
      pod.name = "<action>";
    }
    if( typeof pod.open === 'undefined' ) pod.open = true;
  }
  catch( ex ) {
    console.error("checkpod(", pod, ", ", index, ")");
    fail( ex, "checkpod( <pod>, " + index + ")" );
  }
}
function on( action ) {
  PM( this ).on( "value", action );
}
Beispiel #19
0
 elem.addEventListener("input", function(evt) {
     PM(that).change('value', evt.target.value);
 }, false);
function processMap( value, src, dst ) {
  if( value && typeof value.map === 'function' && typeof dst.map === 'function' ) {
    try {
      var result = [];
      var more = {
        context: {},
        list: value,
        index: 0
      };
      if( typeof dst.header === 'function' ) {
        try {
          result.push(dst.header( value, more.context ));
        }
        catch( ex ) {
          console.error("[tfw.binding.link/processMap] Exception while calling a header function: ", ex);
          console.error({
            value: value,
            src: src,
            dst: dst,
            more: more
          });
        }
      }
      value.forEach(function (itm, idx) {
        more.index = idx;
        try {
          result.push( dst.map( itm, more ) );
        }
        catch( ex ) {
          console.error("[tfw.binding.link/processMap] Exception while calling a map function: ", ex);
          console.error({
            item: itm,
            index: idx,
            value: value,
            src: src,
            dst: dst,
            result: result,
            more: more
          });
        }
      });

      if( typeof dst.footer === 'function' ) {
        try {
          result.push(dst.header( value, more.context ));
        }
        catch( ex ) {
          console.error("[tfw.binding.link/processMap] Exception while calling a footer function: ", ex);
          console.error({
            value: value,
            src: src,
            dst: dst,
            result: result,
            more: more
          });
        }
      }
      
      return result.filter(function( item ) {
        return item != null;
      });
    }
    catch( ex ) {
      console.error( ex );
      fail(
        "Error in map of link "
          + PropertyManager(src.obj) + "." + src.name
          + " -> "
          + PropertyManager(dst.obj) + "." + dst.name + "!"
      );
    }
  }
  return value;
}