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

  <head>
    <link data-require="bootstrap-css@*" data-semver="3.0.0" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
    <script src="//tinymce.cachefly.net/4.0/tinymce.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/ckeditor/4.2/ckeditor.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/ckeditor/4.2/styles.js"></script>
    <script data-require="angular.js@1.2.0-rc3-nonmin" data-semver="1.2.0-rc3-nonmin" src="http://code.angularjs.org/1.2.0-rc.3/angular.js"></script>
    <script data-require="angular-route@*" data-semver="1.2.0-rc2" src="http://code.angularjs.org/1.2.0-rc.2/angular-route.js"></script>
    <script data-require="ui-bootstrap@*" data-semver="0.6.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.6.0.js"></script>
    <script src="tinymce.js"></script>
    <script src="ng-ckeditor.js"></script>
    <script src="script.js"></script>
  </head>

  <body>
    <div>
      <script type="text/ng-template" id="home.tpl.html">
        home here
      </script>
      <script type="text/ng-template" id="tinymce.tpl.html">
        <textarea ui-tinymce='tinymceOptions' ng-model='content'></textarea>
        <textarea ckeditor="" ng-model="content"></textarea>
      </script>
      <script type="text/ng-template" id="list.tpl.html">
        <ul>
          <li ng-repeat="item in items"><a href="item/{{item.name}}">{{item.name}}</a></li>
        </ul>
      </script>
      <script type="text/ng-template" id="item.tpl.html">
        <h3>{{item.name}}</h3>
        <textarea ng-model='item.content'></textarea>
        <textarea ui-tinymce='tinymceOptions' ng-model='item.content'></textarea>
        <textarea ckeditor="" ng-model="item.content"></textarea>
      </script>
      <div ng-controller="MyCtrl">
        <h3>Routes</h3>
        <ul>
          <li>
            <a href="/home">home</a>
          </li>
          <li>
            <a href="/list">list</a>
          </li>
        </ul>
        <div ng-view=""></div>
      </div>
    </div>
  </body>

</html>
// Code goes here

angular.module('myApp', [
  'myApp.controllers', 
  'ngRoute', 
  'ui.tinymce',
  'ui.bootstrap',
  'ngCkeditor'
])
  .config(function($routeProvider, $locationProvider) {
       
    $routeProvider.when('/home', {
        controller: 'HomeCtrl',
        templateUrl: 'home.tpl.html'
    }).when('/list', {
        controller: 'ListCtrl',
        templateUrl: 'list.tpl.html'
    }).when('/item/:itemName', {
        controller: 'ItemCtrl',
        templateUrl: 'item.tpl.html'
    }).otherwise({
        redirectTo: '/home'
    });
    $locationProvider.html5Mode(true);
  });

angular.module('myApp.controllers', {}).
  controller('MyCtrl', function($scope) {
    $scope.items = [
      {
        name: 'Item One',
        content: 'Hello there'
      },
      {
        name: 'Item Two',
        content: 'Goodbye then'
      }
    ];
  }).
  controller('ListCtrl', function($scope) {
    
  }).
  controller('HomeCtrl', function($scope) {
    
  }).
  controller('ItemCtrl', function($scope, $routeParams, $timeout) {
    $timeout(function() {
      for (var i in $scope.items) {
        if ($scope.items[i].name == $routeParams.itemName) {
          $scope.item = $scope.items[i];
          break;
        }
      }
    }, 1);
    $scope.tinymceOptions = {};
  }).
  controller('TinyCtrl', function($scope) {
    $scope.content = 'Hello world!';
    $scope.tinymceOptions = {
      script_url: 'http://tinymce.moxiecode.com/js/tinymce/jscripts/tiny_mce/tiny_mce.js'
    };
  });
/**
 * Binds a TinyMCE widget to <textarea> elements.
 */
angular.module('ui.tinymce', [])
  .value('uiTinymceConfig', {})
  .directive('uiTinymce', ['uiTinymceConfig', function (uiTinymceConfig) {
    uiTinymceConfig = uiTinymceConfig || {};
    var generatedIds = 0;
    return {
      require: 'ngModel',
      link: function (scope, elm, attrs, ngModel) {
        var expression, options, tinyInstance,
          updateView = function () {
            ngModel.$setViewValue(elm.val());
            if (!scope.$root.$$phase) {
              scope.$apply();
            }
          };
        // generate an ID if not present
        if (!attrs.id) {
          attrs.$set('id', 'uiTinymce' + generatedIds++);
        }

        if (attrs.uiTinymce) {
          expression = scope.$eval(attrs.uiTinymce);
        } else {
          expression = {};
        }
        options = {
          // Update model when calling setContent (such as from the source editor popup)
          setup: function (ed) {
            var args;
            ed.on('init', function(args) {
              ngModel.$render();
            });
            // Update model on button click
            ed.on('ExecCommand', function (e) {
              ed.save();
              updateView();
            });
            // Update model on keypress
            ed.on('KeyUp', function (e) {
              ed.save();
              updateView();
            });
            // Update model on change, i.e. copy/pasted text, plugins altering content
            ed.on('SetContent', function (e) {
              if(!e.initial){
                ed.save();
                updateView();
              }
            });
            if (expression.setup) {
              scope.$eval(expression.setup);
              delete expression.setup;
            }
          },
          mode: 'exact',
          elements: attrs.id
        };
        // extend options with initial uiTinymceConfig and options from directive attribute value
        angular.extend(options, uiTinymceConfig, expression);
        setTimeout(function () {
          tinymce.init(options);
        });


        ngModel.$render = function() {
          if (!tinyInstance) {
            tinyInstance = tinymce.get(attrs.id);
          }
          if (tinyInstance) {
            tinyInstance.setContent(ngModel.$viewValue || '');
          }
        };
      }
    };
  }]);
(function(angular, factory) {
    if (typeof define === 'function' && define.amd) {
        define('ng-ckeditor', ['jquery', 'angular', 'ckeditor'], function($, angular) {
            return factory(angular);
        });
    } else {
        return factory(angular);
    }
}(angular || null, function(angular) {
var app = angular.module('ngCkeditor', []);

app.directive('ckeditor', function () {
        'use strict';

        return {
            restrict: 'A',
            require: 'ngModel',
            scope: false,
            link: function (scope, element, attrs, ngModel) {
                var expression = attrs.ngModel;
                var el = $(element);

                if (angular.isUndefined(CKEDITOR) || angular.isUndefined(CKEDITOR.instances)) {
                    return;
                }

                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' ] }
                        /*{ name: 'colors', items : ['bazalt-image'] },*/
                    ],
                    disableNativeSpellChecker: true,
                    uiColor: '#FAFAFA',
                    height: '400px',
                    width: '100%'
                };
                //CKEDITOR.config.spellerPagesServerScript = '/examples/spellcheck/handler.php';
                options = angular.extend(options, scope[attrs.ckeditor]);
                var instance = CKEDITOR.replace(el.get(0), options);

                element.bind('$destroy', function () {
                    instance.destroy(false);
                });
                instance.on('instanceReady', function () {
                    instance.setData(ngModel.$viewValue);
                });
                instance.on('pasteState', function () {
                    ngModel.$setViewValue(instance.getData());
                });
                instance.on('change', function () {
                    ngModel.$setViewValue(instance.getData());
                    if (!scope.$$phase) {
                        scope.$apply();
                    }
                });

                // for source view
                instance.on('key', function() {
                    ngModel.$setViewValue(instance.getData());
                    if (!scope.$$phase) {
                        scope.$apply();
                    }
                });

                ngModel.$render = function () {
                    instance.setData(ngModel.$viewValue);
                };

                scope.$watch(expression, function () {
                    if (!instance) {
                        return;
                    }
                    if (ngModel.$viewValue == instance.getData()) {
                        return;
                    }
                    instance.setData(ngModel.$viewValue);
                });
                scope.$watch(function () {
                    if (!element) {
                        return null;
                    }
                    return instance.getData();
                }, function (val, oldVal) {
                    if (val === '' && angular.isDefined(oldVal) && oldVal !== '') { // when ckeditor loaded first
                        return;
                    }
                    ngModel.$setViewValue(instance.getData());
                });
                instance.on('blur', function () {
                    if (!scope.$$phase) {
                        scope.$apply();
                    }
                });
            }
        };
    });
    return app;
}));
CKEDITOR.plugins.add('backup',{
    init:function(editor){
        editor.on( 'instanceReady', function(e) { 
            var div = document.createElement('div'),
                select = 0,
                style =  'display:inline-block; margin-left:10px;position:relative;margin-top:5px;overflow:hidden;float:right;',
                bname =  'backup_'+editor.name, init = true, oldtext = '';
            div.setAttribute('style',style);
            if( localStorage.getItem( bname) == undefined )
                localStorage.setItem( bname,'{}'); // создаем наше хранилище
            var format = function(_time){
                var n = new Date(parseInt(_time));
                var frm = function(dd){
                    if ( dd < 10 ) dd = '0' + dd;
                    return dd;
                };
                return n.getHours()+'.'+frm(n.getMinutes())+'.'+frm(n.getSeconds());
            };
            editor.backup = function(del){
                var chages = false,now = new Date().getTime(),bu = {};
                if(del!='del'){
                    var text = editor.getSnapshot();
                    if( text!='' ){
                        if( localStorage.getItem( bname) && oldtext && text!=oldtext ){
                            bu = JSON.parse(localStorage.getItem( bname));
                            bu[now] = text;
                            localStorage.setItem( bname,JSON.stringify(bu));
                            chages = true;
                        }
                    }
                }else{
                    if( confirm('Are you sure you want to delete entire backup?') ){
                        localStorage.setItem( bname,'{}');
                        chages = true;
                    }
                }
                if( chages || init){
                    if(init&&localStorage.getItem( bname)){
                        bu = JSON.parse(localStorage.getItem( bname));
                    }
                    var opt = '<option>---</option>';
                    for(var r in bu)
                        opt+='<option value="'+r+'">'+format(r)+'</option>';
                    select.setHtml(opt);
                    init = false;
                }
                oldtext = text;
            },
            editor.restore = function(){
                var text = editor.getSnapshot();
                var val = select.getValue();
                var bu = JSON.parse( localStorage.getItem( bname) );
                if( bu[val]!=undefined && (text==''||confirm('Are you sure you want to replace existing text with the backup?')) ){
                    editor.loadSnapshot( bu[val] );
                }
            };
            var mixer = 0;
            editor.on( 'change',function(){
                clearTimeout(mixer);
                mixer = setTimeout(function(){
                    editor.backup();
                },3000);
            });
            div.innerHTML = '<select style="margin-top:-5px;" id="backuper_'+editor.name+'"></select>&nbsp;<input type="image" value="del" onclick="CKEDITOR.instances[\''+editor.name+'\'].backup(\'del\'); return false;" src="/Images/clear.png"/>';
            div.onchange = editor.restore;
            CKEDITOR.document.getById( editor.ui.spaceId?editor.ui.spaceId("bottom"): 'cke_bottom_'+editor.name ).append(new CKEDITOR.dom.node(div));
            select = CKEDITOR.document.getById( 'backuper_'+editor.name );
            editor.backup();
        });
    }
});
/*
 * @file change event plugin for CKEditor
 * Copyright (C) 2011 Alfonso Martнnez de Lizarrondo
 *
 * == BEGIN LICENSE ==
 *
 * Licensed under the terms of any of the following licenses at your
 * choice:
 *
 *  - GNU General Public License Version 2 or later (the "GPL")
 *    http://www.gnu.org/licenses/gpl.html
 *
 *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
 *    http://www.gnu.org/licenses/lgpl.html
 *
 *  - Mozilla Public License Version 1.1 or later (the "MPL")
 *    http://www.mozilla.org/MPL/MPL-1.1.html
 *
 * == END LICENSE ==
 *
 */

 // Keeps track of changes to the content and fires a "change" event
CKEDITOR.plugins.add( 'onchange',
{
	init : function( editor )
	{
//		// Test:
//		editor.on( 'change', function(e) { console.log( e ) });

		var timer,
			theMutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
			observer;
// http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#mutation-observers
// http://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/

		// Avoid firing the event too often
		function somethingChanged()
		{
			// don't fire events if the editor is readOnly as they are false detections
			if (editor.readOnly)
				return;

			if (timer)
				return;

			timer = setTimeout( function() {
				timer = 0;
				editor.fire( 'change' );
			}, editor.config.minimumChangeMilliseconds || 100);
		}
		// Kill the timer on editor destroy
		editor.on( 'destroy', function() { if ( timer ) clearTimeout( timer ); timer = null; });

		// in theory this block should be enabled only for browsers that don't support MutationObservers,
		// but it doesn't seem to fire correctly in all the situations. Maybe in the future...
		{
			// Set several listeners to watch for changes to the content
			editor.on( 'saveSnapshot', function( evt )
			{
				if ( !evt.data || !evt.data.contentOnly )
					somethingChanged();
			});

			var undoCmd = editor.getCommand('undo');
			undoCmd && undoCmd.on( 'afterUndo', somethingChanged);
			var redoCmd = editor.getCommand('redo');
			redoCmd && redoCmd.on( 'afterRedo', somethingChanged);

			editor.on( 'afterCommandExec', function( event )
			{
				if ( event.data.name == 'source' )
					return;

				if ( event.data.command.canUndo !== false )
					somethingChanged();
			} );
		}

		if ( theMutationObserver )
		{
			observer = new theMutationObserver( function( mutations ) {
				somethingChanged();
			} );

			// To check that we are using a cool browser.
			if (window.console && window.console.log)
				console.log("Detecting changes using MutationObservers");
		}

		// Changes in WYSIWYG mode
		editor.on( 'contentDom', function()
			{
				if ( observer )
				{
					// A notification is fired right now, but we don't want it so soon
					var interval = setInterval( function() {
						if ( typeof editor.document === 'object' ) {
							observer.observe( editor.document.getBody().$, {
								attributes: true,
								childList: true,
								characterData: true
							});
							clearInterval(interval);
						}
					}, 100);
				}

				editor.document.on( 'keydown', function( event )
					{
						// Do not capture CTRL hotkeys.
						if ( event.data.$.ctrlKey ||event.data.$.metaKey )
							return;

						var keyCode = event.data.$.keyCode;
						// Filter movement keys and related
						if (keyCode==8 || keyCode == 13 || keyCode == 32 || ( keyCode >= 46 && keyCode <= 90) || ( keyCode >= 96 && keyCode <= 111) || ( keyCode >= 186 && keyCode <= 222) || keyCode == 229)
							somethingChanged();
					});

					// Firefox OK
				editor.document.on( 'drop', somethingChanged);
					// IE OK
				editor.document.getBody().on( 'drop', somethingChanged);
			});

		// Detect changes in source mode
		editor.on( 'mode', function( e )
			{
				if ( editor.mode != 'source' )
					return;

				var textarea = (editor.textarea || editor._.editable);
				textarea.on( 'keydown', function( event )
					{
						// Do not capture CTRL hotkeys.
						if ( !event.data.$.ctrlKey && !event.data.$.metaKey )
							somethingChanged();
					});

				textarea.on( 'drop', somethingChanged);
				textarea.on( 'input', somethingChanged);
				if (CKEDITOR.env.ie)
				{
					textarea.on( 'cut', somethingChanged);
					textarea.on( 'paste', somethingChanged);
				}
			});

	} //Init
} );