Skip to content

Instantly share code, notes, and snippets.

@wenbert
Forked from melanke/README.md
Created July 25, 2012 01:29
Show Gist options
  • Save wenbert/3173849 to your computer and use it in GitHub Desktop.
Save wenbert/3173849 to your computer and use it in GitHub Desktop.
watch the changes of some object or attribute

Watch.js 1.0.13

##compatibility Works with: IE 9+, FF 4+, SF 5+, WebKit, CH 7+, OP 12+, BESEN, Rhino 1.7+ If you want a similar API that works for all browser try MultiGetSet

About

Watch.JS is a small library that brings a lot of possibilities. Maybe you know the design pattern called "Observer", imagine the possibility of executing some function always that some object changes. Well, already exists other libraries that do this, but with Watch.JS you will not have to change your way to develop. Give a chance to Watch.JS, take a look at the examples and how is simple to embody Watch.JS to your routine.

Observe the changes of one object attribute

//defining our object no matter which way we want
var ex1 = {
	attr1: "initial value of attr1",
	attr2: "initial value of attr2"
};

//defining a 'watcher' for an attribute
ex1.watch("attr1", function(){
	alert("attr1 changes!");
});

//when changing the attribute its watcher will be invoked
ex1.attr1 = "other value";

Try out

Observe the changes of more than one object attribute

//defining our object no matter which way we want
var ex2 = {
	attr1: 0,
	attr2: 0,
	attr3: 0
};

//defining a 'watcher' for the attributes
ex2.watch(["attr2", "attr3"], function(){
	alert("attr2 or attr3 changes!");
});

//when changing one of the attributes its watcher will be invoked
ex2.attr2 = 50;

Try out

Observe the changes of all attributes of the object

//defining our object no matter which way we want
var ex3 = {
	attr1: 0,
	attr2: "initial value of attr2",
	attr3: ["a", 3, null]
};

//defining a 'watcher' for the object
ex3.watch(function(){
	alert("some attribute of ex3 changes!");
});

//when changing one of the attributes of the object the watcher will be invoked
ex3.attr3.push("new value");

Try out

Don't worry about the Inifinite Loop

Different of some libraries, with Watch.JS you will never to worry about inifinite loop when change the object inside its own watcher, the watcher will not be invoked from itself.

//defining our object no matter which way we want
var ex4 = {
	attr1: 0,
	attr2: 1,
	attr3: ["a", 3, null]
};

//defining a 'watcher' for an attribute
ex4.watch("attr2", function(){
    alert("attr2 changes");
	ex4.attr2 = ex4.attr2 * 2; //will not invoke the watcher
});

ex4.attr2 = 2; //attr2 will be changed to 2 and inside the watcher it will be changed to 4

Try out

Chill out, no surprises, new attributes will not be considered

After declaring a watcher for some object, when you add new attributes for this object and/or change it, the watcher will not be invoked. If you want the new attributes to be observed you only need to add the watcher to these new attributes again.

//defining our object no matter which way we want
var ex6 = {
	attr1: 0,
	attr2: 1
};

//defining a 'watcher' for the object
ex6.watch(function(){
	alert("some attribute of ex6 changes!")
});

ex6.attr3 = null; //no watcher will be invoked
ex6.attr3 = "value"; //no watcher will be invoked

Try out

Invoke the watcher anytime you want

//defining our object no matter which way we want
var ex7 = {
	attr1: 0,
	attr2: 1
};

//defining a 'watcher' for the object
ex7.watch(function(){
	alert("some attribute of ex6 changes!")
});

ex7.callWatchers(); //invoke the watcher

Try out

Compatible with JQuery

$(function(){

    var obj = {cont: 0};
    
    obj.watch("cont", function(){
        alert("obj.cont = "+obj.cont);
    });
    
    $("#button").click(function(){
        obj.cont++;
    });
});

Try out

/**
* DEVELOPED BY
* GIL LOPES BUENO
* [email protected]
*
* WORKS WITH:
* IE 9+, FF 4+, SF 5+, WebKit, CH 7+, OP 12+, BESEN, Rhino 1.7+
*
* FORK:
* https://gist.github.com/1627705
*/
var $watchjs$ = {
isFunction: function(functionToCheck) {
var getType = {};
return functionToCheck && getType.toString.call(functionToCheck) == '[object Function]';
},
isInt: function(x) {
var y = parseInt(x);
if (isNaN(y)) return false;
return x == y && x.toString() == y.toString();
},
isArray: function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
},
defineGetAndSet: function(obj, propName, getter, setter){
try{
Object.defineProperty(obj, propName, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
}catch(error){
try{
Object.prototype.__defineGetter__.call(obj, propName, getter);
Object.prototype.__defineSetter__.call(obj, propName, setter);
}catch(error2){
throw "watchJS error: browser not supported :/"
}
}
},
defineProp: function(obj, propName, value){
try{
Object.defineProperty(obj, propName, {
enumerable: false
, configurable: true
, writable: false
, value: value
});
}catch(error){
obj[propName] = value;
}
}
};
$watchjs$.defineProp(Object.prototype, "watch", function() {
if (arguments.length == 1)
this.watchAll.apply(this, arguments);
else if ($watchjs$.isArray(arguments[0]))
this.watchMany.apply(this, arguments);
else
this.watchOne.apply(this, arguments);
});
$watchjs$.defineProp(Object.prototype, "watchAll", function(watcher) {
var obj = this;
if (obj instanceof String || (!(obj instanceof Object) && !$watchjs$.isArray(obj))) //accepts only objects and array (not string)
return;
var props = [];
if($watchjs$.isArray(obj)){
for (var prop=0; prop<obj.length; prop++) { //for each item if obj is an array
props.push(prop); //put in the props
}
}else{
for (var prop2 in obj) { //for each attribute if obj is an object
props.push(prop2); //put in the props
}
}
obj.watchMany(props, watcher); //watch all itens of the props
});
$watchjs$.defineProp(Object.prototype, "watchMany", function(props, watcher) {
var obj = this;
if($watchjs$.isArray(obj)){
for (var prop in props) { //watch each iten of "props" if is an array
obj.watchOne(props[prop], watcher);
}
}else{
for (var prop2 in props) { //watch each attribute of "props" if is an object
obj.watchOne(props[prop2], watcher);
}
}
});
$watchjs$.defineProp(Object.prototype, "watchOne", function(prop, watcher) {
var obj = this;
var val = obj[prop];
if(obj[prop]===undefined || $watchjs$.isFunction(obj[prop])) //dont watch if it is null or a function
return;
if(obj[prop]!=null)
obj[prop].watchAll(watcher); //recursively watch all attributes of this
obj.watchFunctions(prop);
if (!obj.watchers) {
$watchjs$.defineProp(obj, "watchers", {});
}
if (!obj.watchers[prop])
obj.watchers[prop] = [];
obj.watchers[prop].push(watcher); //add the new watcher in the watchers array
var getter = function() {
return val;
};
var setter = function(newval) {
var oldval = val;
val = newval;
if(obj[prop])
obj[prop].watchAll(watcher);
obj.watchFunctions(prop);
if (JSON.stringify(oldval) != JSON.stringify(newval) && arguments.callee.caller != watcher) {
obj.callWatchers(prop);
}
};
$watchjs$.defineGetAndSet(obj, prop, getter, setter);
});
$watchjs$.defineProp(Object.prototype, "callWatchers", function(prop) {
var obj = this;
for (var wr in obj.watchers[prop]) {
if ($watchjs$.isInt(wr)){
obj.watchers[prop][wr]();
}
}
});
$watchjs$.defineProp(Object.prototype, "watchFunctions", function(prop) {
var obj = this;
if((!obj[prop]) || (obj[prop] instanceof String) || (!$watchjs$.isArray(obj[prop])))
return;
var originalpop = obj[prop].pop;
$watchjs$.defineProp(obj[prop], "pop", function() {
var response = originalpop.apply(this, arguments);
obj.watchOne(obj[prop]);
obj.callWatchers(prop);
return response;
});
var originalpush = obj[prop].push;
$watchjs$.defineProp(obj[prop], "push", function() {
var response = originalpush.apply(this, arguments);
obj.watchOne(obj[prop]);
obj.callWatchers(prop);
return response;
});
var originalreverse = obj[prop].reverse;
$watchjs$.defineProp(obj[prop], "reverse", function() {
var response = originalreverse.apply(this, arguments);
obj.watchOne(obj[prop]);
obj.callWatchers(prop);
return response;
});
var originalshift = obj[prop].shift;
$watchjs$.defineProp(obj[prop], "shift", function() {
var response = originalshift.apply(this, arguments);
obj.watchOne(obj[prop]);
obj.callWatchers(prop);
return response;
});
var originalsort = obj[prop].sort;
$watchjs$.defineProp(obj[prop], "sort", function() {
var response = originalsort.apply(this, arguments);
obj.watchOne(obj[prop]);
obj.callWatchers(prop);
return response;
});
var originalslice = obj[prop].slice;
$watchjs$.defineProp(obj[prop], "slice", function() {
var response = originalslice.apply(this, arguments);
obj.watchOne(obj[prop]);
return response;
});
var originalunshift = obj[prop].unshift;
$watchjs$.defineProp(obj[prop], "unshift", function() {
var response = originalunshift.apply(this, arguments);
obj.watchOne(obj[prop]);
obj.callWatchers(prop);
return response;
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment