// [JSON fix]: added support for passing configuration as inline json, Branko
// [$dirty fix]: disabled setting model value to default 'null' on init, pepa


(function(angular, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['angular', 'ckeditor'], function(angular) {
            return factory(angular);
        });
    } else {
        return factory(angular);
    }
}(angular || null, function(angular) {
var app = angular.module('ngCkeditor', []);
var $defer, loaded = false;

app.run(['$q', '$timeout', function($q, $timeout) {
    $defer = $q.defer();

    if (angular.isUndefined(CKEDITOR)) {
        throw new Error('CKEDITOR not found');
    }
    CKEDITOR.disableAutoInline = true;
    function checkLoaded() {
        if (CKEDITOR.status == 'loaded') {
            loaded = true;
            $defer.resolve();
        } else {
            checkLoaded();
        }
    }
    CKEDITOR.on('loaded', checkLoaded);
    $timeout(checkLoaded, 100);
}])

app.directive('ckeditor', ['$timeout', '$q', function ($timeout, $q) {
    'use strict';

    return {
        restrict: 'AC',
        require: 'ngModel',
        scope: false,
        link: function (scope, element, attrs, ngModel) {
            var EMPTY_HTML = '<p></p>',
                isTextarea = element[0].tagName.toLowerCase() == 'textarea',
                data = [],
                isReady = false,
                instanceReady = false,
                modelReady = false;

            if (!isTextarea) {
                element.attr('contenteditable', true);
            }

            var onLoad = function () {
                var options = {
                    toolbar: 'full',
                    toolbar_full: [
                        { name: 'basicstyles',
                            items: [ 'Bold', 'Italic', 'Strike', 'Underline' ] },
                        { name: 'paragraph', items: [ 'BulletedList', 'NumberedList', 'Blockquote' ] },
                        { name: 'editing', items: ['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock' ] },
                        { name: 'links', items: [ 'Link', 'Unlink', 'Anchor' ] },
                        { name: 'tools', items: [ 'SpellChecker', 'Maximize' ] },
                        '/',
                        { name: 'styles', items: [ 'Format', 'FontSize', 'TextColor', 'PasteText', 'PasteFromWord', 'RemoveFormat' ] },
                        { name: 'insert', items: [ 'Image', 'Table', 'SpecialChar' ] },
                        { name: 'forms', items: [ 'Outdent', 'Indent' ] },
                        { name: 'clipboard', items: [ 'Undo', 'Redo' ] },
                        { name: 'document', items: [ 'PageBreak', 'Source' ] }
                    ],
                    disableNativeSpellChecker: false,
                    uiColor: '#FAFAFA',
                    height: '400px',
                    width: '100%'
                };

            // [JSON fix]
                // options = angular.extend(options, scope[attrs.ckeditor]);
                try {
                    options = angular.extend(options, JSON.parse(attrs.ckeditor));
                } catch (e) {
                    options = angular.extend(options, scope[attrs.ckeditor]);
                }
            // ---

                var instance = (isTextarea) ? CKEDITOR.replace(element[0], options) : CKEDITOR.inline(element[0], options),
                    configLoaderDef = $q.defer();

                element.bind('$destroy', function () {
                    return;     // [sort fix]
                    instance.destroy(
                        false //If the instance is replacing a DOM element, this parameter indicates whether or not to update the element with the instance contents.
                    );
                });

                // [sort fix]
                scope.$on('unbind-ckeditor', function() {
                    instance.destroy();
                    element.css('visibility', 'hidden');
                });
                scope.$on('rebind-ckeditor', function() {
                    element.css('visibility', 'visible');
                    instanceReady = false;
                    instance = (isTextarea) ? CKEDITOR.replace(element[0], options) : CKEDITOR.inline(element[0], options);
                    setInstanceEvents();
                });
                // ---
                
                var setModelData = function(params) {
                    var content = instance.getData();
                    
                    // [$dirty fix]
                    if (ngModel.$viewValue == content) { return; }
                    
                    if (params.name != 'change' && content == '' && content != ngModel.$viewValue && ngModel.$pristine) {
                        
                        if (params.name == 'pasteState' && (1 || params.editor._.previousValue == '<br>' || params.editor._.previousValue == '<p><br></p>')) {
                            if (1 || params.editor._.previousValue == '<p><br></p>') {
                                params.setPristine = true;
                            }
                        } else {
                            data.push(ngModel.$viewValue);
                            onUpdateModelData(params);
                            return;
                        }
                    }
                    // ---
                    
                    if (content == EMPTY_HTML) {
                        content = null;
                    }
                    
                    $timeout(function () { // for key up event
                        ngModel.$setViewValue(content);
                        // [$dirty fix]
                        if (params.setPristine === true) {
                            ngModel.$setPristine();
                        }
                        // ---
                    }, 0);
                }, onUpdateModelData = function(params) {
                    // [$dirty fix]
                    if (ngModel.$viewValue === undefined) { return; }
                    // ---
                    
                    if (!data.length) { return; }

                        var item = data.pop();
                        instance.setData(item || EMPTY_HTML, function () {
                            setModelData(params);
                            isReady = false;
                        });
                },
                // [$dirty fix]
                readyCheck = function() {
                    if (instanceReady && modelReady) {
                        onUpdateModelData({setPristine: true});
                        instance.on('mode',         setModelData);
                        instance.on('pasteState',   setModelData);
                        instance.on('change',       setModelData);
                        instance.on('blur',         setModelData);
                        instance.on('key',          setModelData); // for source view
                        $timeout(function () {
                            ngModel.$setPristine();
                        }, 0);
                    }
                }
                // ---
                function setInstanceEvents() {
                    instance.on('instanceReady', function() {
                        // [$dirty fix]
                        instanceReady = true;
                        readyCheck();
                        //scope.$apply(function() {
                            //onUpdateModelData('ready');
                        //});
                        // ---
                    });
                    instance.on('customConfigLoaded', function() {
                        configLoaderDef.resolve();
                    });
                };
                setInstanceEvents();

                ngModel.$render = function() {
                    // [$dirty fix]
                    /*
                    if (ngModel.$viewValue === undefined) {
                        ngModel.$setViewValue(null);
                        ngModel.$viewValue = null;
                    }
                    */
                    // ---

                    data.push(ngModel.$viewValue);
                    
                    // [$dirty fix]
                    modelReady = true;
                    readyCheck();
                    /*
                    if (!isReady) {
                        isReady = false;

                        onUpdateModelData('init');
                    }
                    */
                    // ---
                };
            };

            if (CKEDITOR.status == 'loaded') {
                loaded = true;
            }
            if (loaded) {
                onLoad();
            } else {
                $defer.promise.then(onLoad);
            }
        }
    };
}]);
    return app;
}));