<!DOCTYPE html>
<html ng-app="app">

<head>
  <title>check all</title>

  <script src="http://cdn.bootcss.com/jquery/2.1.2/jquery.js"></script>
  <script src="http://cdn.bootcss.com/angular.js/1.3.15/angular.js"></script>
  <script src="./checkbox.js"></script>
  <link href="http://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.css" rel="stylesheet">
  <script>
    angular.module('app', ['test.checkbox']);
  </script>
</head>

<body>
  <a checkbox-select-all class="btn btn-default" group="['foodGroup', 'bookGroup']">全都喜欢</a>

  <a checkbox-select-all="bookGroup" class="btn btn-default" group="['group1', 'group2', 'group3']">全选所有书籍</a>
  <a checkbox-select-all="foodGroup" class="btn btn-default" group="['group4']">全选所有美食</a>

  <div class="well">

    <label class="label label-info">科幻类</label>

    <a checkbox-select-all="group1" class="btn btn-default" group="['checkbox1', 'checkbox2', 'checkbox3']">全选</a>

    <div>
      <label class="checkbox-inline">
        <input type="checkbox" ng-model="checkbox1">三体
      </label>
      <label class="checkbox-inline">
        <input type="checkbox" ng-model="checkbox2">璀璨的银河
      </label>
      <label class="checkbox-inline">
        <input type="checkbox" ng-model="checkbox3">球状闪电
      </label>
    </div>

    <hr />
    <label class="label label-info">计算机类</label>
    <a checkbox-select-all="group2" class="btn btn-default" group="['checkbox4', 'checkbox5']">全选</a>
    <div>
      <label class="checkbox-inline">
        <input type="checkbox" ng-model="checkbox4">深入浅出NodeJS
      </label>
      <label class="checkbox-inline">
        <input type="checkbox" ng-model="checkbox5">Angularjs权威教程
      </label>
    </div>

    <hr />
    <label class="label label-info">人文类</label>
    <a checkbox-select-all="group3" class="btn btn-default" group="['checkbox6', 'checkbox7', 'checkbox8']">全选</a>
    <div>
      <label class="checkbox-inline">
        <input type="checkbox" ng-model="checkbox6">黑客与画家
      </label>
      <label class="checkbox-inline">
        <input type="checkbox" ng-model="checkbox7">大教堂与集市
      </label>
      <label class="checkbox-inline">
        <input type="checkbox" ng-model="checkbox8">把时间当朋友
      </label>
    </div>

  </div>

  <div class="well">

    <label class="label label-info">川菜</label>

    <a checkbox-select-all="group4" class="btn btn-default" group="['checkbox9', 'checkbox10', 'checkbox11']">全选</a>
    
    <div>
      <label class="checkbox-inline">
        <input type="checkbox" ng-model="checkbox9">辣子鸡丁
      </label>
      <label class="checkbox-inline">
        <input type="checkbox" ng-model="checkbox10">红烧茄子
      </label>
      <label class="checkbox-inline">
        <input type="checkbox" ng-model="checkbox11">酸辣土豆丝
      </label>
    </div>

  </div>
</body>

</html>
var checkbtn = angular.module('test.checkbox', []);

checkbtn.factory('checkboxSelectGroupStack', [function(){
  
  var stack = {};
  
  return {
    
    'put': function(grpRef, group){
      stack[grpRef] = group;
    },
    
    'get': function(grpRef){
      return stack[grpRef];
    }
    
  };
  
}]);

checkbtn.directive('checkboxSelectAll', ['$parse', 'checkboxSelectGroupStack', function($parse, groupStack){
	
	return {
		'restrict': 'A',
		
		'link': function(scope, element, attrs){
			
			//组引用 (该引用 代表着 group 中所有的成员)
			var groupRef = attrs.checkboxSelectAll;
			
			//组成员 (该引用成员既包括 checkbox,也可以是一个groupRef)
			var groupItems = $parse(attrs.group || "[]")(scope);
			
			// checkbox-select-all 的属性值代表着其所在元素的 group 引用的组成员。 
			// group 所引用的成员既可以是 一个 checkbox 成员 也可以是一个 checkbox-select-all 成员
			if (typeof groupRef === 'string' && groupRef.length >0 ) {
			  groupStack.put(groupRef,  groupItems);
			}
			
			//equals jQuery.merge
			var merge = function ( first, second ) {
    		var len = +second.length,
    			j = 0,
    			i = first.length;
    
    		for ( ; j < len; j++ ) {
    			first[ i++ ] = second[ j ];
    		}
    
    		first.length = i;
    
    		return first;
    	};
			
			// 在堆上计算 组成员中的属性值,并将该数组展平成1维数组
			var flattenGroupItemsOnGroupStack = function(groupItems){
			  
			  var flattenGroupItems = [];
			  
			  groupItems.forEach(function(groupItemOrRef){
			    // 如果该成员是堆上引用
			    if ( Array.isArray(groupStack.get(groupItemOrRef))  ) {
			      // 递归将对堆上的引用全部计算成checkbox级别的组成员
			      merge(flattenGroupItems, flattenGroupItemsOnGroupStack(groupStack.get(groupItemOrRef)));
			    }else{
			      flattenGroupItems.push(groupItemOrRef);
			    }
			  });
			  
			  return flattenGroupItems;
			};
			
			var allSelect = function(){
			  
			  scope.$apply(function(){
			    
			    flattenGroupItemsOnGroupStack(groupItems).forEach(function(modelName){
			      
			      scope[modelName] = true;  // 全选
			      
			    });
			    
			  });
			  
			  // toggle event
			  element.unbind('click', allSelect).bind('click', reverseSelect);
			};
			
			var reverseSelect = function(){
			  
			  scope.$apply(function(){
			    
			    flattenGroupItemsOnGroupStack(groupItems).forEach(function(modelName){
			      
			      scope[modelName] = !scope[modelName];  //反选
			      
			    });
			    
			  });
			  
			  // toggle event
			  element.unbind('click', reverseSelect).bind('click', allSelect);
			};
			
			element.bind('click', allSelect);
			
		}
	}
	
}]);