describe('textAngular', function(){
    /*
        Display Tests
    */
    'use strict';
    afterEach(inject(function($document){
        $document.find('body').html('');
    }));
    beforeEach(module('textAngular'));
    var $window, element, $rootScope, textAngularManager, editorScope;
    describe('Minimal Initiation', function(){
        beforeEach(inject(function (_$compile_, _$rootScope_) {
            $rootScope = _$rootScope_;
            element = _$compile_('<text-angular><p>Test Content</p></text-angular>')($rootScope);
            $rootScope.$digest();
        }));
        describe('Adds Correct Classes and Elements', function () {
            it('add .ta-root to base element', function(){
                expect(jQuery(element).hasClass('.ta-root'));
            });
            it('adds 2 .ta-editor elements', function(){
                expect(jQuery('.ta-editor', element).length).toBe(2);
            });
            it('adds the WYSIWYG div', function(){
                expect(jQuery('div.ta-text.ta-editor', element).length).toBe(1);
            });
            it('adds the textarea', function(){
                expect(jQuery('textarea.ta-html.ta-editor', element).length).toBe(1);
            });
            it('no hidden form submission element', function(){
                expect(jQuery('input[type=hidden][name=test]', element).length).toBe(0);
            });
        });
    });

    describe('Basic Initiation without ng-model', function(){
        beforeEach(inject(function (_$compile_, _$rootScope_) {
            $rootScope = _$rootScope_;
            element = _$compile_('<text-angular name="test"><p>Test Content</p></text-angular>')($rootScope);
            $rootScope.$digest();
        }));
        describe('Adds Correct Classes and Elements', function () {
            it('add .ta-root to base element', function(){
                expect(jQuery(element).hasClass('.ta-root'));
            });
            it('adds 2 .ta-editor elements', function(){
                expect(jQuery('.ta-editor', element).length).toBe(2);
            });
            it('adds the WYSIWYG div', function(){
                expect(jQuery('div.ta-text.ta-editor', element).length).toBe(1);
            });
            it('adds the textarea', function(){
                expect(jQuery('textarea.ta-html.ta-editor', element).length).toBe(1);
            });
            it('adds one hidden form submission element', function(){
                expect(jQuery('input[type=hidden][name=test]', element).length).toBe(1);
            });
        });
        describe('Gets the proper stripped text', function () {
            it('measure time', function () {
                var t0 = performance.now();
                var stripped = stripHtmlToText('<div class="AppContainer" style="width: 1280px;color: rgb(0, 0, 0);"><div id="c_base" class="c_base"><div id="c_content" class="c_main"><div id="pageContent"><div id="pageInbox" class="v-Page"><div id="inboxControl0f"><div class="containsYSizerBar" style="height: 849px;width: 1280px;"><div class="ContentRight WithRightRail FullView"><div class="ContentRightInner t_mbgc t_qtc t_urtc" style="color: rgb(68, 68, 68);background-color: rgb(255, 255, 255);"><div id="inboxControl0fv-ReadMessageContainer" class="v-ReadMessageContainer slideOnResize"><div class="c-ReadMessage" style="height: 818.03125px;width: 895px;"><div class="rmMessages ClearBoth" id="ReadMessageScrollableSection"><div id="readMessagePartControl1604f" class="c-ReadMessagePart ReadMsgContainer HasLayout ClearBoth HideShadows FullPart NoHistory Read RmIc"><div class="c-ReadMessagePartBody"><div class="readMsgBody"><div id="bodyreadMessagePartBodyControl1609f" class="ExternalClass MsgBodyContainer"><p><u><b>Lorem ipsum</b></u></p><p><b>Lorem ipsum</b></p></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>');
                var t1 = performance.now();
                var duration = (t1 - t0).toFixed(4);
                //console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to do something!');
                expect(duration < 0.001);
                expect(stripped).toBe('Lorem ipsumLorem ipsum');
                stripped = stripHtmlToText(' \n &#8203;&#8203; &#8203; ');
                expect(stripped).toBe('');
            });
        });
        describe('Gets the proper DOM', function () {
            it('from Html', function () {
                var dom = getDomFromHtml('<div><p></p></div>');
                expect(dom.innerHTML).toBe('<div><p></p></div>');
            });
        });
    });

    describe('Add classes via attributes', function(){
        beforeEach(inject(function (_$compile_, _$rootScope_, _textAngularManager_) {
            $rootScope = _$rootScope_;
            textAngularManager = _textAngularManager_;
            element = _$compile_('<text-angular name="test" ta-focussed-class="test-focus-class" ta-text-editor-class="test-text-class" ta-html-editor-class="test-html-class"></text-angular>')($rootScope);
            $rootScope.$digest();
        }));

        describe('Adds Correct Classes', function () {
            it('initially has no focus class', function(){
                expect(!jQuery(element).hasClass('.test-focus-class'));
            });
            it('adds focus class when ta-text is focussed', function(){
                textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('focus');
                expect(jQuery(element).hasClass('.test-focus-class'));
            });
            it('adds focus class when ta-html is focussed', function(){
                jQuery('.ta-html', element).triggerHandler('focus');
                expect(jQuery(element).hasClass('.test-focus-class'));
            });
            it('adds text editor class', function(){
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.hasClass('.test-text-class'));
            });
            it('adds html editor class', function(){
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.html.hasClass('.test-html-class'));
            });
        });
    });

    describe('Change classes via decorator', function(){
        beforeEach(module('textAngular', function($provide){
            // change all the classes at once
            $provide.decorator('taOptions', ['$delegate', function(taOptions){
                taOptions.classes.focussed = "test-focus-class";
                taOptions.classes.disabled = "disabled-test";
                taOptions.classes.textEditor = 'test-text-class';
                taOptions.classes.htmlEditor = 'test-html-class';
                return taOptions;
            }]);
        }));
        beforeEach(inject(function (_$compile_, _$rootScope_, _textAngularManager_) {
            $rootScope = _$rootScope_;
            textAngularManager = _textAngularManager_;
            element = _$compile_('<text-angular name="test"></text-angular>')($rootScope);
            $rootScope.$digest();
        }));
        it('should add .test-focus-class instead of default .focussed', function(){
            textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('focus');
            expect(jQuery(element).hasClass('.test-focus-class'));
        });
        it('adds text editor class', function(){
            expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.hasClass('.test-text-class'));
        });
        it('adds html editor class', function(){
            expect(textAngularManager.retrieveEditor('test').scope.displayElements.html.hasClass('.test-html-class'));
        });
        it('adds disabled class', function(){
            expect(jQuery(element).hasClass('.disabled-test'));
        });
    });

    describe('Add tabindex attribute', function(){
        beforeEach(inject(function (_$compile_, _$rootScope_, _textAngularManager_) {
            $rootScope = _$rootScope_;
            textAngularManager = _textAngularManager_;
            element = _$compile_('<text-angular name="test" tabindex="42"></text-angular>')($rootScope);
            $rootScope.$digest();
        }));

        describe('Check moved across', function () {
            it('to textEditor', function(){
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.attr('tabindex')).toBe('42');
            });
            it('to htmlEditor', function(){
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.html.attr('tabindex')).toBe('42');
            });
            it('removed from .ta-root', function(){
                expect(element.attr('tabindex')).toBeUndefined();
            });
        });
    });

    describe('Use serial attribute', function(){
        beforeEach(inject(function (_$compile_, _$rootScope_, _textAngularManager_) {
            $rootScope = _$rootScope_;
            textAngularManager = _textAngularManager_;
            element = _$compile_('<text-angular serial="test"></text-angular>')($rootScope);
            $rootScope.$digest();
        }));

        describe('generates id\'s and name attributes', function () {
            it('name of editor', function(){
                expect(textAngularManager.retrieveEditor('textAngularEditortest')).not.toBeUndefined();
            });
            it('to textEditor', function(){
                expect(textAngularManager.retrieveEditor('textAngularEditortest').scope.displayElements.text.attr('id')).toBe('taTextElementtest');
            });
            it('to htmlEditor', function(){
                expect(textAngularManager.retrieveEditor('textAngularEditortest').scope.displayElements.html.attr('id')).toBe('taHtmlElementtest');
            });
        });
    });

    describe('Disable the editor', function(){
        beforeEach(inject(function (_$compile_, _$rootScope_) {
            $rootScope = _$rootScope_;
            $rootScope.disabled = true;
            element = _$compile_('<text-angular name="test" ta-disabled="disabled"></text-angular>')($rootScope);
            $rootScope.$digest();
        }));

        describe('check disabled class', function () {
            it('is added initially', function(){
                expect(jQuery(element).hasClass('disaled'));
            });
            it('is removed on change to false', function(){
                $rootScope.disabled = false;
                $rootScope.$digest();
                expect(!jQuery(element).hasClass('disabled'));
            });
            it('is added on change to true', function(){
                $rootScope.disabled = false;
                $rootScope.$digest();
                $rootScope.disabled = true;
                $rootScope.$digest();
                expect(jQuery(element).hasClass('disabled'));
            });
        });
    });

    it('respects the taShowHtml attribute',inject(function ($compile, $rootScope, $document) {
        element = $compile('<text-angular name="test" ta-show-html="true"></text-angular>')($rootScope);
        $document.find('body').append(element);
        $rootScope.$digest();
        expect(jQuery('.ta-text', element[0]).is(':visible')).toBe(false);
        expect(jQuery('.ta-html', element[0]).is(':visible')).toBe(true);
        element.remove();
    }));

    it('respects the taDefaultTagAttributes attribute',inject(function ($compile, $rootScope, $document, textAngularManager) {
        $rootScope.taTestDefaultTagAttributes = {a:{target:"_blank"}, li:{test:"testing"}};
        element = $compile('<text-angular name="test" ta-default-tag-attributes="{{taTestDefaultTagAttributes}}"></text-angular>')($rootScope);
        $document.find('body').append(element);
        editorScope = textAngularManager.retrieveEditor('test').scope;
        $rootScope.$digest();
        expect(editorScope.defaultTagAttributes).toEqual($rootScope.taTestDefaultTagAttributes);
        element.remove();
    }));

    it('uses the default defaultTagAttributes when the taDefaultTagAttributes attribute throws a JSON parse error',inject(function ($compile, $rootScope, $document, textAngularManager) {
        var taTestDefaultTagAttributes = {a:{target:""}};
        element = $compile('<text-angular name="test" ta-default-tag-attributes="invalidJSON"></text-angular>')($rootScope);
        $document.find('body').append(element);
        editorScope = textAngularManager.retrieveEditor('test').scope;
        $rootScope.$digest();
        expect(editorScope.defaultTagAttributes).toEqual(taTestDefaultTagAttributes);
        element.remove();
    }));

    describe('Check view change', function(){
        beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager, $document) {
            $rootScope = _$rootScope_;
            element = _$compile_('<text-angular name="test"></text-angular>')($rootScope);
            $document.find('body').append(element);
            editorScope = textAngularManager.retrieveEditor('test').scope;
            $rootScope.$digest();
        }));
        afterEach(function(){
            element.remove();
        });

        it('initially should hide .ta-html and show .ta-text', function(){
            expect(jQuery('.ta-text', element[0]).is(':visible')).toBe(true);
            expect(jQuery('.ta-html', element[0]).is(':visible')).toBe(false);
        });

        describe('from WYSIWYG text to RAW HTML view', function () {
            it('should hide .ta-text and show .ta-html', function(){
                editorScope.switchView();
                editorScope.$parent.$digest();
                expect(jQuery('.ta-text', element[0]).is(':visible')).toBe(false);
                expect(jQuery('.ta-html', element[0]).is(':visible')).toBe(true);
            });
        });

        describe('from RAW HTML to WYSIWYG text view', function () {
            it('should hide .ta-html and show .ta-text', function(){
                editorScope.switchView();
                editorScope.$parent.$digest();
                editorScope.switchView();
                editorScope.$parent.$digest();
                expect(jQuery('.ta-text', element[0]).is(':visible')).toBe(true);
                expect(jQuery('.ta-html', element[0]).is(':visible')).toBe(false);
            });
        });
    });

    describe('Check focussed class adding', function(){
        beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) {
            $rootScope = _$rootScope_;
            element = _$compile_('<text-angular name="test"></text-angular>')($rootScope);
            $rootScope.$digest();
            editorScope = textAngularManager.retrieveEditor('test').scope;
        }));

        describe('should have added .focussed', function(){
            it('on trigger focus on ta-text', function(){
                editorScope.displayElements.text.triggerHandler('focus');
                expect(element.hasClass('focussed'));
            });
            it('on trigger focus on ta-html', function(){
                editorScope.displayElements.html.triggerHandler('focus');
                expect(element.hasClass('focussed'));
            });
        });

        describe('should have removed .focussed', function(){
            it('on ta-text trigger blur', function(){
                editorScope.displayElements.text.triggerHandler('focus');
                $rootScope.$digest();
                editorScope.displayElements.text.triggerHandler('blur');
                expect(!element.hasClass('focussed'));
            });
            it('on ta-html trigger blur', function(){
                editorScope.displayElements.html.triggerHandler('focus');
                $rootScope.$digest();
                editorScope.displayElements.html.triggerHandler('blur');
                expect(!element.hasClass('focussed'));
            });
        });
    });

    describe('Check text and html editor setup functions', function(){
        beforeEach(inject(function (_$compile_, _$rootScope_) {
            $rootScope = _$rootScope_;
            $rootScope.attrSetup = function($element){
                $element.attr('testattr', 'trueish');
            };
            element = _$compile_('<text-angular ta-text-editor-setup="attrSetup" ta-html-editor-setup="attrSetup" name="test"></text-angular>')($rootScope);
            $rootScope.$digest();
        }));

        describe('should have added attribute to', function(){
            it('ta-text', inject(function(textAngularManager){
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.attr('testattr')).toBe('trueish');
            }));
            it('ta-html', inject(function(textAngularManager){
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.html.attr('testattr')).toBe('trueish');
            }));
        });
    });

    describe('Check placeholder passthrough', function(){
        beforeEach(inject(function (_$compile_, _$rootScope_) {
            $rootScope = _$rootScope_;
            element = _$compile_('<text-angular name="test" placeholder="Test Placeholder"></text-angular>')($rootScope);
            $rootScope.$digest();
        }));

        describe('should have added placeholder to', function(){
            it('ta-text', inject(function(textAngularManager){
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.attr('placeholder')).toBe('Test Placeholder');
            }));
            it('ta-html', inject(function(textAngularManager){
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.html.attr('placeholder')).toBe('Test Placeholder');
            }));
        });
    });

    describe('registration', function(){
        it('should add itself to the textAngularManager', inject(function($rootScope, $compile, textAngularManager){
            $compile('<text-angular name="test"></text-angular>')($rootScope);
            expect(textAngularManager.retrieveEditor('test')).not.toBeUndefined();
        }));

        it('should register toolbars to itself', inject(function($rootScope, $compile, textAngularManager){
            var toolbar1 = {name: 'test-toolbar1'};
            var toolbar2 = {name: 'test-toolbar2'};
            textAngularManager.registerToolbar(toolbar1);
            textAngularManager.registerToolbar(toolbar2);
            $compile('<text-angular name="test" ta-target-toolbars="test-toolbar1,test-toolbar2"></text-angular>')($rootScope);
            var _toolbars = textAngularManager.retrieveToolbarsViaEditor('test');
            expect(_toolbars[0]).toBe(toolbar1);
            expect(_toolbars[1]).toBe(toolbar2);
        }));
    });

    describe('unregistration', function(){
        it('should remove itself from the textAngularManager on $destroy', inject(function($rootScope, $compile, textAngularManager){
            element = $compile('<text-angular name="test"></text-angular>')($rootScope);
            $rootScope.$digest();
            textAngularManager.retrieveEditor('test').scope.$destroy();
            expect(textAngularManager.retrieveEditor('test')).toBeUndefined();
        }));
    });

    /*
        Form validation tests
    */

    describe('form validation', function(){

        describe('basic', function(){
            beforeEach(inject(function (_$compile_, _$rootScope_, _$window_, $document, textAngularManager) {
                $window = _$window_;
                $rootScope = _$rootScope_;
                $rootScope.html = '';
                var _form = angular.element('<form name="form"></form>');
                element = angular.element('<text-angular name="test" ng-model="html"></text-angular>');
                _form.append(element);
                $document.find('body').append(_$compile_(_form)($rootScope));
                element = textAngularManager.retrieveEditor('test').scope.displayElements.text;
                $rootScope.$digest();
            }));

            describe('should start with', function () {
                it('pristine', function(){
                    expect($rootScope.form.$pristine).toBe(true);
                });
                it('field pristine', function(){
                    expect($rootScope.form.test.$pristine).toBe(true);
                });
                it('valid', function(){
                    expect($rootScope.form.$valid).toBe(true);
                });
                it('field valid', function(){
                    expect($rootScope.form.test.$valid).toBe(true);
                });
            });

            describe('should NOT change on direct model change', function () {
                beforeEach(function(){
                    $rootScope.htmlcontent = '<div>Test Change Content</div>';
                    $rootScope.$digest();
                });
                it('pristine', function(){
                    expect($rootScope.form.$pristine).toBe(true);
                });
                it('field pristine', function(){
                    expect($rootScope.form.test.$pristine).toBe(true);
                });
                it('valid', function(){
                    expect($rootScope.form.$valid).toBe(true);
                });
                it('field valid', function(){
                    expect($rootScope.form.test.$valid).toBe(true);
                });
            });

            describe('should change on input update', function () {
                beforeEach(inject(function(textAngularManager, $timeout){
                    element.html('<div>Test Change Content</div>');
                    element.triggerHandler('keyup');
                    $rootScope.$digest();
                    $timeout.flush();
                }));
                it('not pristine', function(){
                    expect($rootScope.form.$pristine).toBe(false);
                });
                it('field not pristine', function(){
                    expect($rootScope.form.test.$pristine).toBe(false);
                });
                it('valid', function(){
                    expect($rootScope.form.$valid).toBe(true);
                });
                it('field valid', function(){
                    expect($rootScope.form.test.$valid).toBe(true);
                });
            });

            describe('should change on blur', function () {
                beforeEach(inject(function(textAngularManager){
                    element.html('<div>Test Change Content</div>');
                    element.triggerHandler('blur');
                    $rootScope.$digest();
                }));
                it('not pristine', function(){
                    expect($rootScope.form.$pristine).toBe(false);
                });
                it('field not pristine', function(){
                    expect($rootScope.form.test.$pristine).toBe(false);
                });
                it('valid', function(){
                    expect($rootScope.form.$valid).toBe(true);
                });
                it('field valid', function(){
                    expect($rootScope.form.test.$valid).toBe(true);
                });
            });
        });
        describe('with errors', function(){
            beforeEach(inject(function (_$compile_, _$rootScope_, _$window_, $document, textAngularManager) {
                $window = _$window_;
                $rootScope = _$rootScope_;
                $rootScope.html = '';
                var _form = angular.element('<form name="form"></form>');
                element = angular.element('<text-angular name="test" ng-model="html" required></text-angular>');
                _form.append(element);
                $document.find('body').append(_$compile_(_form)($rootScope));
                element = textAngularManager.retrieveEditor('test').scope.displayElements.text;
                $rootScope.$digest();
            }));

            describe('should start with', function () {
                it('ng-required', function(){
                    expect($rootScope.form.test.$error.required).toBe(true);
                });
                it('valid', function(){
                    expect($rootScope.form.$invalid).toBe(true);
                });
                it('field valid', function(){
                    expect($rootScope.form.test.$invalid).toBe(true);
                });
            });

            describe('should change on direct model change', function () {
                beforeEach(function(){
                    $rootScope.html = '<div>Test Change Content</div>';
                    $rootScope.$digest();
                });
                it('ng-required', function(){
                    expect($rootScope.form.test.$error.required).toBe(undefined);
                });
                it('valid', function(){
                    expect($rootScope.form.$valid).toBe(true);
                });
                it('field valid', function(){
                    expect($rootScope.form.test.$valid).toBe(true);
                });
            });

            describe('should change on input update', function() {
                beforeEach(inject(function(textAngularManager, $timeout){
                    element.html('<div>Test Change Content</div>');
                    element.triggerHandler('keyup');
                    $rootScope.$digest();
                    $timeout.flush();
                }));
                it('ng-required', function(){
                    expect($rootScope.form.test.$error.required).toBe(undefined);
                });
                it('valid', function(){
                    expect($rootScope.form.$valid).toBe(true);
                });
                it('field valid', function(){
                    expect($rootScope.form.test.$valid).toBe(true);
                });
            });

            describe('should change on blur', function () {
                beforeEach(inject(function(textAngularManager){
                    element.html('<div>Test Change Content</div>');
                    element.triggerHandler('blur');
                    $rootScope.$digest();
                }));
                it('ng-required', function(){
                    expect($rootScope.form.test.$error.required).toBe(undefined);
                });
                it('valid', function(){
                    expect($rootScope.form.$valid).toBe(true);
                });
                it('field valid', function(){
                    expect($rootScope.form.test.$valid).toBe(true);
                });
            });
        });
    });

    /*
        Data Tests
    */

    describe('Basic Initiation without ng-model', function(){
        var displayElements;
        beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) {
            $rootScope = _$rootScope_;
            element = _$compile_('<text-angular name="test"><p>Test Content</p></text-angular>')($rootScope);
            $rootScope.$digest();
            displayElements = textAngularManager.retrieveEditor('test').scope.displayElements;
        }));
        describe('Inserts the current information into the fields', function(){
            it('populates the WYSIWYG area', function(){
                expect(displayElements.text.find('p').length).toBe(1);
            });
            it('populates the textarea', function(){
                expect(displayElements.html.val()).toBe('<p>Test Content</p>');
            });
            it('populates the hidden input value', function(){
                expect(displayElements.forminput.val()).toBe('<p>Test Content</p>');
            });
        });
    });

    describe('Basic Initiation with ng-model', function(){
        var displayElements;
        beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) {
            $rootScope = _$rootScope_;
            $rootScope.html = '<p>Test Content</p>';
            element = _$compile_('<text-angular name="test" ng-model="html"></text-angular>')($rootScope);
            $rootScope.$digest();
            displayElements = textAngularManager.retrieveEditor('test').scope.displayElements;
        }));
        describe('Inserts the current information into the fields', function(){
            it('populates the WYSIWYG area', function(){
                expect(displayElements.text.find('p').length).toBe(1);
            });
            it('populates the textarea', function(){
                expect(displayElements.html.val()).toBe('<p>Test Content</p>');
            });
            it('populates the hidden input value', function(){
                expect(displayElements.forminput.val()).toBe('<p>Test Content</p>');
            });
        });
    });
    describe('Basic Initiation with ng-model and originalContents', function(){
        var displayElements;
        beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) {
            $rootScope = _$rootScope_;
            $rootScope.html = '<p>Test Content</p>';
            element = _$compile_('<text-angular name="test" ng-model="html"><p>Original Content</p></text-angular>')($rootScope);
            $rootScope.$digest();
            displayElements = textAngularManager.retrieveEditor('test').scope.displayElements;
        }));
        describe('Inserts the current information into the fields', function(){
            it('populates the WYSIWYG area', function(){
                expect(displayElements.text.find('p').length).toBe(1);
            });
            it('populates the textarea', function(){
                expect(displayElements.html.val()).toBe('<p>Test Content</p>');
            });
            it('populates the hidden input value', function(){
                expect(displayElements.forminput.val()).toBe('<p>Test Content</p>');
            });
        });
    });

    describe('should respect the ta-default-wrap value', function(){
        it('with ng-model', inject(function($rootScope, $compile, textAngularManager){
            $rootScope.html = '';
            $compile('<text-angular ta-default-wrap="div" name="test" ng-model="html"></text-angular>')($rootScope);
            element = textAngularManager.retrieveEditor('test').scope.displayElements.text;
            $rootScope.$digest();
            element.triggerHandler('focus');
            $rootScope.$digest();
            expect(element.html()).toBe('<div><br></div>');
        }));
        it('without ng-model', inject(function($rootScope, $compile, textAngularManager){
            $compile('<text-angular ta-default-wrap="div" name="test"></text-angular>')($rootScope);
            element = textAngularManager.retrieveEditor('test').scope.displayElements.text;
            $rootScope.$digest();
            element.triggerHandler('focus');
            $rootScope.$digest();
            expect(element.html()).toBe('<div><br></div>');
        }));
    });



    describe('should respect taUnsafeSanitizer attribute', function () {
        var element2, displayElements, $timeout;
        beforeEach(inject(function(_$timeout_){
            $timeout = _$timeout_;
        }));
        describe('without ng-model', function(){
            beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) {
                $rootScope = _$rootScope_;
                element = _$compile_('<text-angular name="test" ta-unsafe-sanitizer="true"><p>Test Contents</p></text-angular>')($rootScope);
                $rootScope.$digest();
                editorScope = textAngularManager.retrieveEditor('test').scope;
                element = textAngularManager.retrieveEditor('test').scope.displayElements.text;
                element2 = textAngularManager.retrieveEditor('test').scope.displayElements.html;
            }));

            it('allow bad tags', function () {
                element.append('<bad-tag>Test 2 Content</bad-tag>');
                element.triggerHandler('keyup');
                $rootScope.$digest();
                $timeout.flush();
                expect(element2.val()).toBe('<p>Test Contents</p>\n<p><bad-tag>Test 2 Content</bad-tag></p>');
            });

            it('not allow malformed html', function () {
                element.append('<bad-tag Test 2 Content</bad-tag>');
                element.triggerHandler('keyup');
                $rootScope.$digest();
                $timeout.flush();
                expect(element2.val()).toBe('<p>Test Contents</p>');
            });
        });

        describe('with ng-model', function(){
            beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) {
                $rootScope = _$rootScope_;
                $rootScope.html = '<p>Test Contents</p>';
                // note that ta-unsafe-sanitizer has no effect for these tests, we simply set it true here to enable 100% coverage
                element = _$compile_('<text-angular name="test" ta-keep-styles="false" ta-unsafe-sanitizer="true" ng-model="html"></text-angular>')($rootScope);
                $rootScope.$digest();
                editorScope = textAngularManager.retrieveEditor('test').scope;
                element = editorScope.displayElements.text;
            }));

            it('allow bad tags', function () {
                element.append('<bad-tag>Test 2 Content</bad-tag>');
                element.triggerHandler('keyup');
                $rootScope.$digest();
                $timeout.flush();
                expect($rootScope.html).toBe('<p>Test Contents</p><p><bad-tag>Test 2 Content</bad-tag></p>');
            });

            it('not allow malformed html', function () {
                element.append('<bad-tag Test 2 Content</bad-tag>');
                element.triggerHandler('keyup');
                $rootScope.$digest();
                $timeout.flush();
                expect($rootScope.html).toBe('<p>Test Contents</p>');
            });
        });
    });

    describe('Updates without ng-model', function(){
        var displayElements;
        beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager, $timeout) {
            $rootScope = _$rootScope_;
            element = _$compile_('<text-angular name="test"><p>Test Content</p></text-angular>')($rootScope);
            $rootScope.$digest();
            displayElements = textAngularManager.retrieveEditor('test').scope.displayElements;
            displayElements.text.html('<div>Test Change Content</div>');
            displayElements.text.triggerHandler('keyup');
            $rootScope.$digest();
            $timeout.flush();
        }));

        describe('updates from .ta-text', function(){
            it('should update .ta-html', function(){
                expect(displayElements.html.val()).toBe('<div>Test Change Content</div>');
            });
            it('should update input[type=hidden]', function(){
                expect(displayElements.forminput.val()).toBe('<div>Test Change Content</div>');
            });
        });

        describe('updates from .ta-html', function(){
            it('should update .ta-text', function(){
                expect(displayElements.text.html()).toBe('<div>Test Change Content</div>');
            });
            it('should update input[type=hidden]', function(){
                expect(displayElements.forminput.val()).toBe('<div>Test Change Content</div>');
            });
        });
    });

    describe('Updates with ng-model', function(){
        var displayElements;
        beforeEach(inject(function (_$compile_, _$rootScope_, textAngularManager) {
            $rootScope = _$rootScope_;
            $rootScope.html = '<p>Test Content</p>';
            element = _$compile_('<text-angular name="test" ng-model="html"></text-angular>')($rootScope);
            $rootScope.$digest();
            $rootScope.html = '<div>Test Change Content</div>';
            $rootScope.$digest();
            displayElements = textAngularManager.retrieveEditor('test').scope.displayElements;
        }));

        describe('updates from model to display', function(){
            it('should update .ta-html', function(){
                expect(displayElements.html.val()).toBe('<div>Test Change Content</div>');
            });
            it('should update .ta-text', function(){
                expect(displayElements.text.html()).toBe('<div>Test Change Content</div>');
            });
            it('should update input[type=hidden]', function(){
                expect(displayElements.forminput.val()).toBe('<div>Test Change Content</div>');
            });
        });
    });

    describe('ng-model should handle undefined and null', function(){
        it('should handle initial undefined to empty-string', inject(function ($compile, $rootScope, textAngularManager) {
            $rootScope.html = undefined;
            element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope);
            $rootScope.$digest();
            expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>');
        }));

        it('should handle initial null to empty-string', inject(function ($compile, $rootScope, textAngularManager) {
            $rootScope.html = null;
            element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope);
            $rootScope.$digest();
            expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>');
        }));

        it('should handle initial undefined to originalContents', inject(function ($compile, $rootScope, textAngularManager) {
            $rootScope.html = undefined;
            element = $compile('<text-angular name="test" ng-model="html">Test Contents</text-angular>')($rootScope);
            $rootScope.$digest();
            expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p>Test Contents</p>');
        }));

        it('should handle initial null to originalContents', inject(function ($compile, $rootScope, textAngularManager) {
            $rootScope.html = null;
            element = $compile('<text-angular name="test" ng-model="html">Test Contents</text-angular>')($rootScope);
            $rootScope.$digest();
            expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p>Test Contents</p>');
        }));

        describe('should reset', function(){
            it('from undefined to empty-string', inject(function ($compile, $rootScope, textAngularManager) {
                $rootScope.html = 'Test Content';
                element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope);
                $rootScope.$digest();
                $rootScope.html = undefined;
                $rootScope.$digest();
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>');
            }));

            it('from null to empty-string', inject(function ($compile, $rootScope, textAngularManager) {
                $rootScope.html = 'Test Content';
                element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope);
                $rootScope.$digest();
                $rootScope.html = null;
                $rootScope.$digest();
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>');
            }));

            it('from undefined to blank/emptystring WITH originalContents', inject(function ($compile, $rootScope, textAngularManager) {
                $rootScope.html = 'Test Content1';
                element = $compile('<text-angular name="test" ng-model="html">Test Contents2</text-angular>')($rootScope);
                $rootScope.$digest();
                $rootScope.html = undefined;
                $rootScope.$digest();
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>');
            }));

            it('from null to blank/emptystring WITH originalContents', inject(function ($compile, $rootScope, textAngularManager) {
                $rootScope.html = 'Test Content1';
                element = $compile('<text-angular name="test" ng-model="html">Test Contents2</text-angular>')($rootScope);
                $rootScope.$digest();
                $rootScope.html = null;
                $rootScope.$digest();
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><br></p>');
            }));
        });
    });

    describe('should have correct startAction and endAction functions', function(){

        it('should have rangy loaded with save-restore module', function(){
            expect(window.rangy).toBeDefined();
            expect(window.rangy.saveSelection).toBeDefined();
        });
        var sel, range;
        beforeEach(inject(function($compile, $rootScope, textAngularManager, $document){
            $rootScope.html = '<p>Lorem ipsum dolor sit amet, <i>consectetur adipisicing</i> elit, <strong>sed do eiusmod tempor incididunt</strong> ut labore et dolore magna aliqua.</p>';
            element = $compile('<text-angular name="test" ng-model="html">Test Contents2</text-angular>')($rootScope);
            $document.find('body').append(element);
            $rootScope.$digest();
            editorScope = textAngularManager.retrieveEditor('test').scope;
            // setup selection
            sel = window.rangy.getSelection();
            range = window.rangy.createRangyRange();
            range.selectNodeContents(editorScope.displayElements.text.find('p').find('strong')[0]);
            sel.setSingleRange(range);
        }));
        afterEach(function(){
            element.remove();
        });
        describe('startAction should return a function that will restore a selection', function(){
            it('should start with the correct selection', function(){
                expect(sel.getRangeAt(0).toHtml()).toBe('sed do eiusmod tempor incididunt');
            });

            it('should set _actionRunning to true', function(){
                editorScope.startAction();
                expect(editorScope._actionRunning);
            });

            it('should return a function that resets the selection', function(){
                var resetFunc = editorScope.startAction();
                expect(sel.toHtml()).toBe('sed do eiusmod tempor incididunt');
                // change selection
                var range = window.rangy.createRangyRange();
                range.selectNodeContents(editorScope.displayElements.text.find('p').find('i')[0]);
                sel.setSingleRange(range);
                sel.refresh();
                expect(sel.toHtml()).toBe('consectetur adipisicing');
                // reset selection
                resetFunc();
                sel.refresh();
                expect(sel.toHtml()).toBe('sed do eiusmod tempor incididunt');
            });
        });

        describe('endAction should remove the ability to restore selection', function(){
            it('shouldn\'t affect the selection', function(){
                editorScope.startAction();
                editorScope.endAction();
                expect(sel.toHtml()).toBe('sed do eiusmod tempor incididunt');
            });

            it('shouldn\'t restore the selection', function(){
                var resetFunc = editorScope.startAction();
                editorScope.endAction();
                var range = window.rangy.createRangyRange();
                range.selectNodeContents(editorScope.displayElements.text.find('p').find('i')[0]);
                sel.setSingleRange(range);
                sel.refresh();
                expect(sel.toHtml()).toBe('consectetur adipisicing');
                // reset selection - should do nothing now
                resetFunc();
                sel.refresh();
                expect(sel.toHtml()).toBe('consectetur adipisicing');
            });
        });
    });

    describe('Toolbar interaction functions work', function(){
        var sel, range;
        beforeEach(inject(function(taRegisterTool, taOptions, taSelectableElements){
            taSelectableElements.push('i');
            taRegisterTool('testbutton', {
                buttontext: 'reactive action',
                action: function(){
                    return this.$element.attr('hit-this', 'true');
                },
                commandKeyCode: 21,
                activeState: function(){ return true; },
                onElementSelect: {
                    element: 'i',
                    action: function(event, element, editorScope){
                        element.attr('hit-via-select', 'true');
                        this.$element.attr('hit-via-select', 'true');
                    }
                }
            });
            taOptions.toolbar = [
                ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'pre', 'quote', 'testbutton'],
                ['bold', 'italics', 'underline', 'strikeThrough', 'ul', 'ol', 'redo', 'undo', 'clear'],
                ['justifyLeft','justifyCenter','justifyRight','indent','outdent'],
                ['html', 'insertImage', 'insertLink', 'unlink']
            ];
        }));
        beforeEach(inject(function($compile, $rootScope, textAngularManager, $document){
            $rootScope.html = '<p>Lorem ipsum <u>dolor sit amet<u>, consectetur <i>adipisicing elit, sed do eiusmod tempor incididunt</i> ut labore et <a>dolore</a> magna aliqua.</p>';
            element = $compile('<text-angular name="test" ng-model="html">Test Contents2</text-angular>')($rootScope);
            $document.find('body').append(element);
            editorScope = textAngularManager.retrieveEditor('test').scope;
            textAngularManager.retrieveEditor('test').editorFunctions.focus();
            $rootScope.$digest();
            // setup selection
            sel = window.rangy.getSelection();
            range = window.rangy.createRangyRange();
            range.selectNodeContents(editorScope.displayElements.text.find('p').find('i')[0]);
            sel.setSingleRange(range);
        }));
        afterEach(inject(function(taSelectableElements){
            taSelectableElements.pop();
            element.remove();
        }));

        it('should not trigger focus out while an action is processing', inject(function($timeout){
            editorScope.displayElements.text.triggerHandler('focus');
            editorScope.$parent.$digest();
            editorScope.startAction();
            editorScope.displayElements.text.triggerHandler('blur');
            editorScope.$parent.$digest();
            $timeout.flush();
            expect(jQuery(element[0]).find('button:disabled').length).toBe(0);
        }));

        it('keypress should call sendKeyCommand', function(){
            editorScope.displayElements.text.triggerHandler('keypress', {metaKey: true, which: 21});
            editorScope.$parent.$digest();
            expect(jQuery(element[0]).find('.ta-toolbar button[name=testbutton]').attr('hit-this')).toBe('true');
        });

        describe('wrapSelection', function(){
            it('should wrap the selected text in tags', function(){
                editorScope.wrapSelection('bold');
                expect(editorScope.displayElements.text.find('p').find('b').length).toBe(1);
            });

            it('should unwrap the selected text in tags on re-call', function(){
                editorScope.wrapSelection('bold');
                editorScope.wrapSelection('bold');
                expect(editorScope.displayElements.text.find('p').find('b').length).toBe(0);
            });
        });

        describe('queryFormatBlockState', function(){
            it('should return true if formatted', function(){
                editorScope.wrapSelection("formatBlock", "<PRE>");
                expect(editorScope.queryFormatBlockState('PRE')).toBe(true);
            });
            it('should return false if un-formatted', function(){
                expect(editorScope.queryFormatBlockState('PRE')).toBe(false);
            });
        });

        describe('queryCommandState', function(){
            it('should return value if not showHtml', function(){
                editorScope.showHtml = false;
                editorScope.$parent.$digest();
                expect(editorScope.queryCommandState('bold')).not.toBe('');
            });
            it('should return empty if showHtml', function(){
                editorScope.showHtml = true;
                editorScope.$parent.$digest();
                expect(editorScope.queryCommandState('bold')).toBe('');
            });
        });

        describe('ta-element-select event', function(){
            it('fires correctly on element selector', function(){
                var triggerElement = editorScope.displayElements.text.find('i');
                triggerElement.triggerHandler('click');
                expect(triggerElement.attr('hit-via-select')).toBe('true');
            });

            it('fires correctly when filter returns true', inject(function(taTools){
                taTools.testbutton.onElementSelect.filter = function(){ return true; };
                var triggerElement = editorScope.displayElements.text.find('i');
                triggerElement.triggerHandler('click');
                expect(triggerElement.attr('hit-via-select')).toBe('true');
            }));

            it('does not fire when filter returns false', inject(function(taTools){
                taTools.testbutton.onElementSelect.filter = function(){ return false; };
                var triggerElement = editorScope.displayElements.text.find('i');
                triggerElement.triggerHandler('click');
                expect(triggerElement.attr('hit-via-select')).toBeUndefined();
            }));
        });

        describe('popover', function(){
            it('getScrollTop should not error', function(){
                expect(function(){
                    editorScope.getScrollTop(editorScope.displayElements.text.find('p'));
                    editorScope.getScrollTop(editorScope.displayElements.text.find('p'), true);
                }).not.toThrow();
            });
            it('should show the popover', function(){
                editorScope.showPopover(editorScope.displayElements.text.find('p').find('i'));
                editorScope.$parent.$digest();
                expect(editorScope.displayElements.popover.hasClass('in')).toBe(true);
            });
            describe('should hide the popover', function(){
                beforeEach(inject(function($timeout){
                    editorScope.showPopover(editorScope.displayElements.text.find('p').find('i'));
                    editorScope.$parent.$digest();
                    $timeout.flush();
                    editorScope.$parent.$digest();
                }));
                it('on function call', function(){
                    editorScope.hidePopover();
                    editorScope.$parent.$digest();
                    expect(editorScope.displayElements.popover.hasClass('in')).toBe(false);
                });
                it('on click in editor', inject(function($document){
                    $document.find('body').triggerHandler('click');
                    editorScope.$parent.$digest();
                    expect(editorScope.displayElements.popover.hasClass('in')).toBe(false);
                }));
                it('should prevent mousedown from propagating up from popover', function(){
                    var event;
                    if(angular.element === jQuery){
                        event = jQuery.Event('mousedown');
                        editorScope.displayElements.popover.triggerHandler(event);
                        editorScope.$parent.$digest();
                        expect(event.isDefaultPrevented()).toBe(true);
                    }else{
                        var _defaultPrevented = false;
                        event = {preventDefault: function(){ _defaultPrevented = true; }};
                        editorScope.displayElements.popover.triggerHandler('mousedown', event);
                        editorScope.$parent.$digest();
                        expect(_defaultPrevented).toBe(true);
                    }
                });
            });
        });

        describe('updating styles', function(){
            var iButton;
            beforeEach(function(){
                //find italics button
                iButton = element.find('button[name=italics]');
            });

            it('should be initially active when selected', function(){
                expect(iButton.hasClass('active'));
            });

            it('should change on keypress', function(){
                range.selectNodeContents(editorScope.displayElements.text.find('p').find('u')[0]);
                sel.setSingleRange(range);
                editorScope.displayElements.text.triggerHandler('keypress');
                expect(!iButton.hasClass('active'));
            });

            it('should change on keydown and stop on keyup', inject(function($timeout, $document){
                $document.hasFocus = function(){return true;};
                editorScope.displayElements.text.triggerHandler('keydown');
                range.selectNodeContents(editorScope.displayElements.text.find('p').find('u')[0]);
                sel.setSingleRange(range);
                setTimeout(function(){
                    expect(!iButton.hasClass('active'));
                    setTimeout(function(){
                        range = window.rangy.createRangyRange();
                        range.selectNodeContents(editorScope.displayElements.text.find('p').find('i')[0]);
                        setTimeout(function(){
                            expect(iButton.hasClass('active'));
                            editorScope.displayElements.text.triggerHandler('keydown');
                            setTimeout(function(){
                                range.selectNodeContents(editorScope.displayElements.text.find('p').find('u')[0]);
                                setTimeout(function(){
                                    expect(iButton.hasClass('active'));
                                }, 201);
                            }, 201);
                        }, 201);
                    }, 201);
                }, 201);
            }));

            it('should change on mouseup', function(){
                range.selectNodeContents(editorScope.displayElements.text.find('p').find('u')[0]);
                sel.setSingleRange(range);
                editorScope.displayElements.text.triggerHandler('mouseup');
                expect(!iButton.hasClass('active'));
            });
        });
    });

    describe('File Drop Event', function(){
        beforeEach(inject(function(_textAngularManager_){
            textAngularManager = _textAngularManager_;
        }));
        afterEach(inject(function($timeout){
            try{
                $timeout.flush();
            }catch(e){}
        }));

        it('should respect the function set in the attribute', inject(function($compile, $rootScope){
            $rootScope.html = '';
            var testvar = false;
            $rootScope.testhandler = function(){
                testvar = true;
            };
            element = $compile('<text-angular name="test" ta-file-drop="testhandler" ng-model="html"></text-angular>')($rootScope);
            if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
            else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
            $rootScope.$digest();
            expect(testvar).toBe(true);
        }));

        it('when no attribute should use the default handler in taOptions', inject(function($compile, $rootScope, taOptions){
            $rootScope.html = '';
            var testvar = false;
            taOptions.defaultFileDropHandler = function(){
                testvar = true;
            };
            element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope);
            if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
            else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
            $rootScope.$digest();
            expect(testvar).toBe(true);
        }));

        it('handler not fired when no files and no errors', inject(function($compile, $rootScope, taOptions){
            $rootScope.html = '';
            var testvar = false;
            taOptions.defaultFileDropHandler = function(){
                testvar = true;
            };
            element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope);
            if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: [], types: ['url or something']}}});
            else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: [], types: ['url or something']}}});
            $rootScope.$digest();
            expect(testvar).toBe(false);
            expect(function(){
                if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {}}});
                else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {}}});
            }).not.toThrow();
            expect(function(){
                if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {}});
                else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {}});
            }).not.toThrow();
        }));

        describe('check handler return values respected', function(){
            it('default inserts returned html', inject(function($compile, $rootScope, taOptions, $document){
                $rootScope.html = '';
                taOptions.defaultFileDropHandler = function(file, insertAction){
                    insertAction('insertHtml', '<img/>');
                    return true;
                };
                element = $compile('<text-angular name="test" ng-model="html"></text-angular>')($rootScope);
                $document.find('body').append(element);
                $rootScope.$digest();
                if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
            else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
                $rootScope.$digest();
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><img><br></p>');
                element.remove();
            }));
            it('attr function inserts returned html', inject(function($compile, $rootScope, taOptions, $document){
                $rootScope.html = '';
                $rootScope.testhandler = function(file, insertAction){
                    insertAction('insertHtml', '<img/>');
                    return true;
                };
                element = $compile('<text-angular name="test" ta-file-drop="testhandler" ng-model="html"></text-angular>')($rootScope);
                $document.find('body').append(element);
                $rootScope.$digest();
                if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
            else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
                $rootScope.$digest();
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><img><br></p>');
                element.remove();
            }));
            it('attr function overrides default', inject(function($compile, $rootScope, taOptions, $document){
                $rootScope.html = '';
                taOptions.defaultFileDropHandler = function(file, insertAction){
                    insertAction('insertHtml', '<wrong/>');
                    return true;
                };
                $rootScope.testhandler = function(file, insertAction){
                    insertAction('insertHtml', '<img/>');
                    return true;
                };
                element = $compile('<text-angular name="test" ta-file-drop="testhandler" ng-model="html"></text-angular>')($rootScope);
                $document.find('body').append(element);
                $rootScope.$digest();
                if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
            else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
                $rootScope.$digest();
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><img><br></p>');
                element.remove();
            }));
            it('default inserted if attr function returns false', inject(function($compile, $rootScope, taOptions, $document){
                $rootScope.html = '';
                taOptions.defaultFileDropHandler = function(file, insertAction){
                    insertAction('insertHtml', '<img/>');
                    return true;
                };
                $rootScope.testhandler = function(file, insertAction){
                    return false;
                };
                element = $compile('<text-angular name="test" ta-file-drop="testhandler" ng-model="html"></text-angular>')($rootScope);
                $document.find('body').append(element);
                $rootScope.$digest();
                if(jQuery === angular.element) textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler({type: 'drop', originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
            else textAngularManager.retrieveEditor('test').scope.displayElements.text.triggerHandler('drop', {originalEvent: {dataTransfer: {files: ['test'], types: ['files']}}});
                $rootScope.$digest();
                expect(textAngularManager.retrieveEditor('test').scope.displayElements.text.html()).toBe('<p><img><br></p>');
                element.remove();
            }));
        });
    });

    describe('Multiple Editors same toolbar', function(){
        // For more info on this see the excellent writeup @ https://github.com/fraywing/textAngular/issues/112
        var element1, element2, toolbar;
        beforeEach(inject(function($compile, _$rootScope_){
            $rootScope = _$rootScope_;
            $rootScope.html = '<p>Lorem ipsum <u>dolor sit amet<u>, consectetur <i>adipisicing elit, sed do eiusmod tempor incididunt</i> ut labore et dolore magna aliqua.</p>';
            toolbar = $compile('<text-angular-toolbar name="toolbar"></text-angular-toolbar>')($rootScope);
            element1 = $compile('<text-angular name="test1" ng-model="html" ta-target-toolbars="toolbar"></text-angular>')($rootScope);
            element2 = $compile('<text-angular name="test2" ng-model="html" ta-target-toolbars="toolbar"></text-angular>')($rootScope);
            $rootScope.$digest();
        }));

        it('should re-focus on toolbar when swapping directly from editor to editor', inject(function($timeout, textAngularManager){
            textAngularManager.retrieveEditor('test1').scope.displayElements.text.triggerHandler('focus');
            $rootScope.$digest();
            expect(jQuery(toolbar[0]).find('button:not(:disabled)').length).toBe(28);
            textAngularManager.retrieveEditor('test2').scope.displayElements.text.triggerHandler('focus');
            $rootScope.$digest();
            $timeout.flush();
            // expect none to be disabled
            expect(jQuery(toolbar[0]).find('button:not(:disabled)').length).toBe(28);
        }));
    });



    describe('handles the ta-paste event correctly', function(){
        beforeEach(inject(function(_textAngularManager_){
            textAngularManager = _textAngularManager_;
        }));
        it('allows conversion of html', inject(function($window, _$rootScope_, $compile, $document, $timeout){
            $rootScope = _$rootScope_;
            $rootScope.html = '<p>Test Contents</p>';
            $rootScope.converter = function(html){
                expect(html).toBe('<font>Test 4 Content</font>');
                return '<b>Changed Content</b>';
            };
            element = $compile('<text-angular name="testpaste" ta-paste="converter($html)" ng-model="html"></text-angular>')($rootScope);
            $document.find('body').append(element);
            element = textAngularManager.retrieveEditor('testpaste').scope.displayElements.text;
            $rootScope.$digest();
            var sel = $window.rangy.getSelection();
            var range = $window.rangy.createRangyRange();
            range.selectNodeContents(element.find('p')[0]);
            sel.setSingleRange(range);
            triggerEvent('paste', element, {originalEvent: {clipboardData: {types: ['text/html'], getData: function(){ return '<font>Test 4 Content</font>';} }}});
            $timeout.flush();
            $rootScope.$digest();
            expect($rootScope.html).toBe('<p><b>Changed Content</b></p>');
        }));
    });

    describe('textAngularVersion directive', function(){
        beforeEach(inject(function(_textAngularManager_){
            textAngularManager = _textAngularManager_;
        }));
        it('functions', inject(function($window, _$rootScope_, $compile, $document, $timeout){
            $rootScope = _$rootScope_;
            var version = textAngularManager.getVersion();
            element = $compile('<div text-angular-version></div>')($rootScope);
            $rootScope.$digest();
            var html = element[0].outerHTML;
            expect(html).toBe('<div text-angular-version="" class="ng-scope">'+version+'</div>');
        }));
    });

});
