function(name, scope, targetToolbars){
			// targetToolbars are optional, we don't require a toolbar to function
			if(!name || name === '') throw('textAngular Error: An editor requires a name');
			if(!scope) throw('textAngular Error: An editor requires a scope');
			if(editors[name]) throw('textAngular Error: An Editor with name "' + name + '" already exists');
			// _toolbars is an ARRAY of toolbar scopes
			var _toolbars = [];
			angular.forEach(targetToolbars, function(_name){
				if(toolbars[_name]) _toolbars.push(toolbars[_name]);
				// if it doesn't exist it may not have been compiled yet and it will be added later
			});
			editors[name] = {
				scope: scope,
				toolbars: targetToolbars,
				_registerToolbar: function(toolbarScope){
					// add to the list late
					if(this.toolbars.indexOf(toolbarScope.name) >= 0) _toolbars.push(toolbarScope);
				},
				// this is a suite of functions the editor should use to update all it's linked toolbars
				editorFunctions: {
					disable: function(){
						// disable all linked toolbars
						angular.forEach(_toolbars, function(toolbarScope){ toolbarScope.disabled = true; });
					},
					enable: function(){
						// enable all linked toolbars
						angular.forEach(_toolbars, function(toolbarScope){ toolbarScope.disabled = false; });
					},
					focus: function(){
						// this should be called when the editor is focussed
						angular.forEach(_toolbars, function(toolbarScope){
							toolbarScope._parent = scope;
							toolbarScope.disabled = false;
							toolbarScope.focussed = true;
							scope.focussed = true;
						});
					},
					unfocus: function(){
						// this should be called when the editor becomes unfocussed
						angular.forEach(_toolbars, function(toolbarScope){
							toolbarScope.disabled = true;
							toolbarScope.focussed = false;
						});
						scope.focussed = false;
					},
					updateSelectedStyles: function(selectedElement){
						// update the active state of all buttons on liked toolbars
						angular.forEach(_toolbars, function(toolbarScope){
							angular.forEach(toolbarScope.tools, function(toolScope){
								if(toolScope.activeState){
									toolbarScope._parent = scope;
									toolScope.active = toolScope.activeState(selectedElement);
								}
							});
						});
					},
					sendKeyCommand: function(event){
						// we return true if we applied an action, false otherwise
						var result = false;
						if(event.ctrlKey || event.metaKey) angular.forEach(taTools, function(tool, name){
							if(tool.commandKeyCode && tool.commandKeyCode === event.which){
								for(var _t = 0; _t < _toolbars.length; _t++){
									if(_toolbars[_t].tools[name] !== undefined){
										taToolExecuteAction.call(_toolbars[_t].tools[name], scope);
										result = true;
										break;
									}
								}
							}
						});
						return result;
					},
					triggerElementSelect: function(event, element){
						// search through the taTools to see if a match for the tag is made.
						// if there is, see if the tool is on a registered toolbar and not disabled.
						// NOTE: This can trigger on MULTIPLE tools simultaneously.
						var elementHasAttrs = function(_element, attrs){
							var result = true;
							for(var i = 0; i < attrs.length; i++) result = result && _element.attr(attrs[i]);
							return result;
						};
						var workerTools = [];
						var unfilteredTools = {};
						var result = false;
						element = angular.element(element);
						// get all valid tools by element name, keep track if one matches the
						var onlyWithAttrsFilter = false;
						angular.forEach(taTools, function(tool, name){
							if(
								tool.onElementSelect &&
								tool.onElementSelect.element &&
								tool.onElementSelect.element.toLowerCase() === element[0].tagName.toLowerCase() &&
								(!tool.onElementSelect.filter || tool.onElementSelect.filter(element))
							){
								// this should only end up true if the element matches the only attributes
								onlyWithAttrsFilter = onlyWithAttrsFilter ||
									(angular.isArray(tool.onElementSelect.onlyWithAttrs) && elementHasAttrs(element, tool.onElementSelect.onlyWithAttrs));
								if(!tool.onElementSelect.onlyWithAttrs || elementHasAttrs(element, tool.onElementSelect.onlyWithAttrs)) unfilteredTools[name] = tool;
							}
						});
						// if we matched attributes to filter on, then filter, else continue
						if(onlyWithAttrsFilter){
							angular.forEach(unfilteredTools, function(tool, name){
								if(tool.onElementSelect.onlyWithAttrs && elementHasAttrs(element, tool.onElementSelect.onlyWithAttrs)) workerTools.push({'name': name, 'tool': tool});
							});
							// sort most specific (most attrs to find) first
							workerTools.sort(function(a,b){
								return b.tool.onElementSelect.onlyWithAttrs.length - a.tool.onElementSelect.onlyWithAttrs.length;
							});
						}else{
							angular.forEach(unfilteredTools, function(tool, name){
								workerTools.push({'name': name, 'tool': tool});
							});
						}
						// Run the actions on the first visible filtered tool only
						if(workerTools.length > 0){
							for(var _i = 0; _i < workerTools.length; _i++){
								var tool = workerTools[_i].tool;
								var name = workerTools[_i].name;
								for(var _t = 0; _t < _toolbars.length; _t++){
									if(_toolbars[_t].tools[name] !== undefined){
										tool.onElementSelect.action.call(_toolbars[_t].tools[name], event, element, scope);
										result = true;
										break;
									}
								}
								if(result) break; 
							}
						}
						return result;
					}
				}
			};
			return editors[name].editorFunctions;
		}
function(name, scope, targetToolbars){
			
			if(!name || name === '') throw('textAngular Error: An editor requires a name');
			if(!scope) throw('textAngular Error: An editor requires a scope');
			if(editors[name]) throw('textAngular Error: An Editor with name "' + name + '" already exists');
			
			var _toolbars = [];
			angular.forEach(targetToolbars, function(_name){
				if(toolbars[_name]) _toolbars.push(toolbars[_name]);
				
			});
			editors[name] = {
				scope: scope,
				toolbars: targetToolbars,
				_registerToolbar: function(toolbarScope){
					
					if(this.toolbars.indexOf(toolbarScope.name) >= 0) _toolbars.push(toolbarScope);
				},
				
				editorFunctions: {
					disable: function(){
						
						angular.forEach(_toolbars, function(toolbarScope){ toolbarScope.disabled = true; });
					},
					enable: function(){
						
						angular.forEach(_toolbars, function(toolbarScope){ toolbarScope.disabled = false; });
					},
					focus: function(){
						
						angular.forEach(_toolbars, function(toolbarScope){
							toolbarScope._parent = scope;
							toolbarScope.disabled = false;
							toolbarScope.focussed = true;
							scope.focussed = true;
						});
					},
					unfocus: function(){
						
						angular.forEach(_toolbars, function(toolbarScope){
							toolbarScope.disabled = true;
							toolbarScope.focussed = false;
						});
						scope.focussed = false;
					},
					updateSelectedStyles: function(selectedElement){
						
						angular.forEach(_toolbars, function(toolbarScope){
							angular.forEach(toolbarScope.tools, function(toolScope){
								if(toolScope.activeState){
									toolbarScope._parent = scope;
									toolScope.active = toolScope.activeState(selectedElement);
								}
							});
						});
					},
					sendKeyCommand: function(event){
						
						var result = false;
						if(event.ctrlKey || event.metaKey) angular.forEach(taTools, function(tool, name){
							if(tool.commandKeyCode && tool.commandKeyCode === event.which){
								for(var _t = 0; _t < _toolbars.length; _t++){
									if(_toolbars[_t].tools[name] !== undefined){
										taToolExecuteAction.call(_toolbars[_t].tools[name], scope);
										result = true;
										break;
									}
								}
							}
						});
						return result;
					},
					triggerElementSelect: function(event, element){
						
						
						
						var elementHasAttrs = function(_element, attrs){
							var result = true;
							for(var i = 0; i < attrs.length; i++) result = result && _element.attr(attrs[i]);
							return result;
						};
						var workerTools = [];
						var unfilteredTools = {};
						var result = false;
						element = angular.element(element);
						
						var onlyWithAttrsFilter = false;
						angular.forEach(taTools, function(tool, name){
							if(
								tool.onElementSelect &&
								tool.onElementSelect.element &&
								tool.onElementSelect.element.toLowerCase() === element[0].tagName.toLowerCase() &&
								(!tool.onElementSelect.filter || tool.onElementSelect.filter(element))
							){
								
								onlyWithAttrsFilter = onlyWithAttrsFilter ||
									(angular.isArray(tool.onElementSelect.onlyWithAttrs) && elementHasAttrs(element, tool.onElementSelect.onlyWithAttrs));
								if(!tool.onElementSelect.onlyWithAttrs || elementHasAttrs(element, tool.onElementSelect.onlyWithAttrs)) unfilteredTools[name] = tool;
							}
						});
						
						if(onlyWithAttrsFilter){
							angular.forEach(unfilteredTools, function(tool, name){
								if(tool.onElementSelect.onlyWithAttrs && elementHasAttrs(element, tool.onElementSelect.onlyWithAttrs)) workerTools.push({'name': name, 'tool': tool});
							});
							
							workerTools.sort(function(a,b){
								return b.tool.onElementSelect.onlyWithAttrs.length - a.tool.onElementSelect.onlyWithAttrs.length;
							});
						}else{
							angular.forEach(unfilteredTools, function(tool, name){
								workerTools.push({'name': name, 'tool': tool});
							});
						}
						
						if(workerTools.length > 0){
							for(var _i = 0; _i < workerTools.length; _i++){
								var tool = workerTools[_i].tool;
								var name = workerTools[_i].name;
								for(var _t = 0; _t < _toolbars.length; _t++){
									if(_toolbars[_t].tools[name] !== undefined){
										tool.onElementSelect.action.call(_toolbars[_t].tools[name], event, element, scope);
										result = true;
										break;
									}
								}
								if(result) break; 
							}
						}
						return result;
					}
				}
			};
			return editors[name].editorFunctions;
		}
function ( name , scope , targetToolbars ) { // targetToolbars are optional, we don't require a toolbar to function if ( ! name || name === '' ) throw ( 'textAngular Error: An editor requires a name' ) ; if ( ! scope ) throw ( 'textAngular Error: An editor requires a scope' ) ; if ( editors [ name ] ) throw ( 'textAngular Error: An Editor with name "' + name + '" already exists' ) ; // _toolbars is an ARRAY of toolbar scopes var _toolbars = [ ] ; angular . forEach ( targetToolbars , function ( _name ) { if ( toolbars [ _name ] ) _toolbars . push ( toolbars [ _name ] ) ; // if it doesn't exist it may not have been compiled yet and it will be added later } ) ; editors [ name ] = { scope : scope , toolbars : targetToolbars , _registerToolbar : function ( toolbarScope ) { // add to the list late if ( this . toolbars . indexOf ( toolbarScope . name ) >= 0 ) _toolbars . push ( toolbarScope ) ; } , // this is a suite of functions the editor should use to update all it's linked toolbars editorFunctions : { disable : function ( ) { // disable all linked toolbars angular . forEach ( _toolbars , function ( toolbarScope ) { toolbarScope . disabled = true ; } ) ; } , enable : function ( ) { // enable all linked toolbars angular . forEach ( _toolbars , function ( toolbarScope ) { toolbarScope . disabled = false ; } ) ; } , focus : function ( ) { // this should be called when the editor is focussed angular . forEach ( _toolbars , function ( toolbarScope ) { toolbarScope . _parent = scope ; toolbarScope . disabled = false ; toolbarScope . focussed = true ; scope . focussed = true ; } ) ; } , unfocus : function ( ) { // this should be called when the editor becomes unfocussed angular . forEach ( _toolbars , function ( toolbarScope ) { toolbarScope . disabled = true ; toolbarScope . focussed = false ; } ) ; scope . focussed = false ; } , updateSelectedStyles : function ( selectedElement ) { // update the active state of all buttons on liked toolbars angular . forEach ( _toolbars , function ( toolbarScope ) { angular . forEach ( toolbarScope . tools , function ( toolScope ) { if ( toolScope . activeState ) { toolbarScope . _parent = scope ; toolScope . active = toolScope . activeState ( selectedElement ) ; } } ) ; } ) ; } , sendKeyCommand : function ( event ) { // we return true if we applied an action, false otherwise var result = false ; if ( event . ctrlKey || event . metaKey ) angular . forEach ( taTools , function ( tool , name ) { if ( tool . commandKeyCode && tool . commandKeyCode === event . which ) { for ( var _t = 0 ; _t < _toolbars . length ; _t ++ ) { if ( _toolbars [ _t ] . tools [ name ] !== undefined ) { taToolExecuteAction . call ( _toolbars [ _t ] . tools [ name ] , scope ) ; result = true ; break ; } } } } ) ; return result ; } , triggerElementSelect : function ( event , element ) { // search through the taTools to see if a match for the tag is made. // if there is, see if the tool is on a registered toolbar and not disabled. // NOTE: This can trigger on MULTIPLE tools simultaneously. var elementHasAttrs = function ( _element , attrs ) { var result = true ; for ( var i = 0 ; i < attrs . length ; i ++ ) result = result && _element . attr ( attrs [ i ] ) ; return result ; } ; var workerTools = [ ] ; var unfilteredTools = { } ; var result = false ; element = angular . element ( element ) ; // get all valid tools by element name, keep track if one matches the var onlyWithAttrsFilter = false ; angular . forEach ( taTools , function ( tool , name ) { if ( tool . onElementSelect && tool . onElementSelect . element && tool . onElementSelect . element . toLowerCase ( ) === element [ 0 ] . tagName . toLowerCase ( ) && ( ! tool . onElementSelect . filter || tool . onElementSelect . filter ( element ) ) ) { // this should only end up true if the element matches the only attributes onlyWithAttrsFilter = onlyWithAttrsFilter || ( angular . isArray ( tool . onElementSelect . onlyWithAttrs ) && elementHasAttrs ( element , tool . onElementSelect . onlyWithAttrs ) ) ; if ( ! tool . onElementSelect . onlyWithAttrs || elementHasAttrs ( element , tool . onElementSelect . onlyWithAttrs ) ) unfilteredTools [ name ] = tool ; } } ) ; // if we matched attributes to filter on, then filter, else continue if ( onlyWithAttrsFilter ) { angular . forEach ( unfilteredTools , function ( tool , name ) { if ( tool . onElementSelect . onlyWithAttrs && elementHasAttrs ( element , tool . onElementSelect . onlyWithAttrs ) ) workerTools . push ( { 'name' : name , 'tool' : tool } ) ; } ) ; // sort most specific (most attrs to find) first workerTools . sort ( function ( a , b ) { return b . tool . onElementSelect . onlyWithAttrs . length - a . tool . onElementSelect . onlyWithAttrs . length ; } ) ; } else { angular . forEach ( unfilteredTools , function ( tool , name ) { workerTools . push ( { 'name' : name , 'tool' : tool } ) ; } ) ; } // Run the actions on the first visible filtered tool only if ( workerTools . length > 0 ) { for ( var _i = 0 ; _i < workerTools . length ; _i ++ ) { var tool = workerTools [ _i ] . tool ; var name = workerTools [ _i ] . name ; for ( var _t = 0 ; _t < _toolbars . length ; _t ++ ) { if ( _toolbars [ _t ] . tools [ name ] !== undefined ) { tool . onElementSelect . action . call ( _toolbars [ _t ] . tools [ name ] , event , element , scope ) ; result = true ; break ; } } if ( result ) break ; } } return result ; } } } ; return editors [ name ] . editorFunctions ; }
register an editor and the toolbars that it is affected by
register an editor and the toolbars that it is affected by