SergejKasper
1/20/2014 - 11:50 AM

jsbin.eGAgExEj.html

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
  
  <div ng-app="app">
        <div ng-controller="Ctrl">
          <h1>result impl1: {{data1}}</h1>
          <h1>result impl2: {{data2}}</h1>
          <input type="button" ng-click="cl()" value="Bind1">
          <input type="button" ng-click="cl2()" value="Bind2">
          <input type="button" ng-click="cl3()" value="Unsub2">
        </div>
    </div>
</body>
</html>
var App = {};

App.Factory = function Parent() {
	var subUid = -1;
	var events = {};
	var context = {};
	var publish = function(subject, result) {
		events[subject].subscribers.forEach(function(o) {
			o.func.apply(this, [result]);
		});
	};
	var sub = function(event, func) {
		var token = (++subUid).toString();
      if(typeof events[event] === 'object'){
      events[event].subscribers.push({
			token: token,
			func: func
		});
      }else{
        func.apply(this, ['is Undefined']);
      }
		return token;
	};
	var unsub = function(event, token) {
		var removed = false;
		events[event].subscribers.some(function(sub, i) {
			if (sub && sub.hasOwnProperty('token') && sub.token === token) {
				events[event].subscribers.splice(i, 1);
				removed = true;
				return removed;
			}
		});
		return removed;
	};
	return {
		addEvent: function addEvent(publisher) {
			if (publisher) {
				if (!events.hasOwnProperty(publisher)) {
					events[publisher] = {
						event: {
							name: publisher,
							publish: function perform(result) {
								context[publisher] = result;
								publish(publisher, result);
							}
						},
						subscribers: []
					};
				}
				console.log('new event: ' + events[publisher].event.name);
			} else {
				throw {
					error: 'no name given'
				};
			}
		},
		subscribe: sub,
		unsubscribe: unsub,
		act: function(publisher, result) {
			events[publisher].event.publish(result);
		}
	};
};

angular.module('app', []).config([
  '$provide', function($provide) {
    return $provide.decorator('$rootScope', [
      '$delegate', function($delegate) {
        $delegate.safeApply = function(fn) {
          var phase = $delegate.$$phase;
          if (phase === "$apply" || phase === "$digest") {
            if (fn && typeof fn === 'function') {
              fn();
            }
          } else {
            $delegate.$apply(fn);
          }
        };
        return $delegate;
      }
    ]);
  }
]).service('context', function(){
  var a = App.Factory();
  
  a.addEvent('aImpl');
  a.addEvent('aImpl');
  
  return{
    subscribe: function(handler, func){
      return a.subscribe(handler, func);
    }, 
    unsubscribe: function(handler, token){
      return a.unsubscribe(handler, token);
    },
    act: function(actor, result){
      a.act(actor, result);
    }
  };  
}).controller('Ctrl', ['$scope', '$rootScope', 'context', function SecondCtrl($scope, $rootScope, context) {
  $scope.data1 = 2;
  var sub1 = context.subscribe('aImpl', function(result){
      $rootScope.safeApply(function(){
        $scope.data1 = result;
      });
  });
  $scope.data2 = 2;
  var sub2 = context.subscribe('bImpl', function(result){
      $rootScope.safeApply(function(){
        $scope.data2 = result;
      });
  });

  $scope.cl = function(){
    context.act('aImpl', $scope.data1);
  };

  $scope.cl2 = function(){
    context.act('aImpl', $scope.data2 * 2);
  };
  
  $scope.cl3 = function(){
    context.unsubscribe('bImpl', sub2);
  };
  
}]);