<!DOCTYPE html>
<html ng-app="SPA.Module">
<!--add Module-->

<head>
  <!--css file-->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.css" />
  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css" />
  <!--javascript file-->
  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
  <script src="//code.angularjs.org/1.3.1/angular.min.js"></script>
  <script src="https://rawgit.com/angular-ui/ui-router/0.2.11/release/angular-ui-router.js"></script>
  <script src="https://code.angularjs.org/1.3.1/angular-sanitize.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.js"></script>
  <script src="https://code.angularjs.org/1.3.1/angular-animate.min.js"></script>
  <!--firebase-->
  <script src="https://cdn.firebase.com/js/client/2.0.3/firebase.js"></script>
  <!--textAngular Reference:http://textangular.com/-->
  <script src="plugins/textAngular.js"></script>
  <!--angular-loading-bar Reference:http://chieffancypants.github.io/angular-loading-bar/-->
  <link rel="stylesheet" href="plugins/loading-bar.css" />
  <script src="plugins/loading-bar.js"></script>
  <!--custom controller and css-->
  <script src="SPA.js"></script>
  <script src="resourceService.js"></script>
  <link rel="stylesheet" href="css/container.css"/>
</head>

<body ng-controller="mainController">
  <!--views parent controller-->
  <div class="panel text-center" style="background: #1d9e74">
    <h1 class="headerText">Single Page Application Blog
        <small>Simple CRUD</small>
        <div id="logoutDiv" ng-hide="userId" class="pull-right">
            <a class="btn btn-primary" ng-click="login('google')"><i class="fa fa-google"></i>Sing in with Google</a>
        </div>
        <div id="loginDiv" ng-show="userId" class="btn-group pull-right">
            <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" >
                <img ng-src="{{picturePath}}" alt="Smiley face" height="24" width="24" style="border-radius: 20em">Hello {{userName}}~
                <span class="caret"></span>
            </button>
            <ul class="dropdown-menu" role="menu">
                <li><a ui-sref="create"><i class="fa fa-plus"> New Post</i></a></li>
                <li><a ui-sref="home"><i class="fa fa-home"> Home</i></a></li>
                <li><a href="javascript:void(0)" ng-click="logout()"><i class="fa fa-sign-out"> Logout</i></a></li>
            </ul>
        </div>
    </h1>
  </div>
  <div class="row-fluid ">
    <div class="col-md-10 col-md-offset-1 " ui-view></div>
  </div>
</body>

</html>
### Firebase Authentication Blog JSON Format 
"datas" has [{key:value},{key:value}] pattern and key is id,value is "template"
```javascript
{
    "authenticationBlog":{
        "datas":{

        },
        "template":{
            "author":"",
            "title":"",
            "content":"",
            "shortContent":"",
            "postDate":"",
            "userid":""
        }
    }
}
```
[textAngular]

[angular-loading-bar]
[textAngular]:http://textangular.com/
[angular-loading-bar]:http://chieffancypants.github.io/angular-loading-bar
/*
 textAngular
 Author : Austin Anderson
 License : 2013 MIT
 Version 1.1.2

 See README.md or https://github.com/fraywing/textAngular/wiki for requirements and use.
 */

if(!window.console) console = {log: function() {}}; // fixes IE console undefined errors

var textAngular = angular.module("textAngular", ['ngSanitize']); //This makes ngSanitize required

textAngular.directive("textAngular", ['$compile', '$window', '$document', '$rootScope', '$timeout', 'taFixChrome', function($compile, $window, $document, $rootScope, $timeout, taFixChrome) {
    // deepExtend instead of angular.extend in order to allow easy customization of "display" for default buttons
    // snatched from: http://stackoverflow.com/a/15311794/2966847
    function deepExtend(destination, source) {
        for (var property in source) {
            if (source[property] && source[property].constructor &&
                source[property].constructor === Object) {
                destination[property] = destination[property] || {};
                arguments.callee(destination[property], source[property]);
            } else {
                destination[property] = source[property];
            }
        }
        return destination;
    };
    // Here we set up the global display defaults, make sure we don't overwrite any that the user may have already set.
    $rootScope.textAngularOpts = deepExtend({
        toolbar: [['h1', 'h2', 'h3', 'p', 'pre'], ['bold', 'italics', 'underline', 'ul', 'ol', 'redo', 'undo'], ['justifyLeft','justifyCenter','justifyRight'],['insertImage', 'insertLink','clear','segments']],
        classes: {
            focussed: "focussed",
            toolbar: "btn-toolbar",
            toolbarGroup: "btn-group",
            toolbarButton: "btn btn-default",
            toolbarButtonActive: "active",
            textEditor: 'form-control',
            htmlEditor: 'form-control'
        }
    }, ($rootScope.textAngularOpts != null)? $rootScope.textAngularOpts : {});
    // Setup the default toolbar tools, this way allows the user to add new tools like plugins
    var queryFormatBlockState = function(command){
        command = command.toLowerCase();
        var val = $document[0].queryCommandValue('formatBlock').toLowerCase();
        return val === command || val === command;
    }
    $rootScope.textAngularTools = deepExtend({
        h1: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)'>H1</button>",
            action: function() {
                return this.$parent.wrapSelection("formatBlock", "<H1>");
            },
            activeState: function() { return queryFormatBlockState('h1'); }
        },
        h2: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)'>H2</button>",
            action: function() {
                return this.$parent.wrapSelection("formatBlock", "<H2>");
            },
            activeState: function() { return queryFormatBlockState('h2'); }
        },
        h3: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)'>H3</button>",
            action: function() {
                return this.$parent.wrapSelection("formatBlock", "<H3>");
            },
            activeState: function() { return queryFormatBlockState('h3'); }
        },
        p: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='Paragraph'>P</button>",
            action: function() {
                return this.$parent.wrapSelection("formatBlock", "<P>");
            },
            activeState: function() { return queryFormatBlockState('p'); }
        },
        pre: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' data-toggle='tooltip' title='code pane'><i class='fa fa-codepen'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("formatBlock", "<PRE>");
            },
            activeState: function() { return queryFormatBlockState('pre'); }
        },
        ul: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)'><i class='fa fa-list-ul' title='li'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("insertUnorderedList", null);
            },
            activeState: function() { return $document[0].queryCommandState('insertUnorderedList'); }
        },
        ol: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)'><i class='fa fa-list-ol' title='ol'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("insertOrderedList", null);
            },
            activeState: function() { return $document[0].queryCommandState('insertOrderedList'); }
        },

        undo: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='Undo'><i class='fa fa-undo'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("undo", null);
            }
        },
        redo: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='Redo'><i class='fa fa-repeat'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("redo", null);
            }
        },
        bold: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)'><i class='fa fa-bold'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("bold", null);
            },
            activeState: function() {
                return $document[0].queryCommandState('bold');
            }
        },
        justifyLeft: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='justifyLeft'><i class='fa fa-align-left'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("justifyLeft", null);
            },
            activeState: function() {
                return $document[0].queryCommandState('justifyLeft');
            }
        },
        justifyRight: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='justifyRight'><i class='fa fa-align-right'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("justifyRight", null);
            },
            activeState: function() {
                return $document[0].queryCommandState('justifyRight');
            }
        },
        justifyCenter: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='justifyRight'><i class='fa fa-align-center'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("justifyCenter", null);
            },
            activeState: function() {
                return $document[0].queryCommandState('justifyCenter');
            }
        },
        italics: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='italics'><i class='fa fa-italic'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("italic", null);
            },
            activeState: function() {
                return $document[0].queryCommandState('italic');
            }
        },
        underline: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='underline'><i class='fa fa-underline'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("underline", null);
            },
            activeState: function() {
                return $document[0].queryCommandState('underline');
            }
        },
        clear: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='clear'><i class='fa fa-ban'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("removeFormat", null);
            }
        },
        segments:{
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='articleSegments when homepage showing'><i class='fa fa-code'></i></button>",
            action: function() {
                return this.$parent.wrapSelection("segments", null);
            }
        },
        insertImage: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='insertImage'><i class='fa fa-picture-o'></i></button>",
            action: function() {
                var imageLink;
                imageLink = prompt("Please enter an image URL to insert", 'http://');
                if (imageLink !== '') {
                    return this.$parent.wrapSelection('insertImage', imageLink);
                }
            }
        },
        insertLink: {
            display: "<button type='button' ng-click='action()' ng-class='displayActiveToolClass(active)' title='insertLink'><i class='fa fa-link'></i></button>",
            action: function() {
                var urlLink;
                urlLink = prompt("Please enter an URL to insert", 'http://');
                if (urlLink !== '') {
                    return this.$parent.wrapSelection('createLink', urlLink);
                }
            }
        }
    }, ($rootScope.textAngularTools != null)? $rootScope.textAngularTools : {})

    return {
        require: 'ngModel',
        scope: {
            segmentsText: '='
        },
        restrict: "EA",
        link: function(scope, element, attrs, ngModel) {
            var group, groupElement, keydown, keyup, tool, toolElement; //all these vars should not be accessable outside this directive
            // get the settings from the defaults and add our specific functions that need to be on the scope
            angular.extend(scope, $rootScope.textAngularOpts, {

                // wraps the selection in the provided tag / execCommand function.
                wrapSelection: function(command, opt) {
                    document.execCommand(command, false, opt);
                    // strip out the chrome specific rubbish that gets put in when using lists
                    if (command == 'segments'){
                        var htmlText = scope.displayElements.text.html()
                        var segmentsText = '<!--more-->'
                        if (scope.segmentsText) segmentsText = scope.segmentsText
                        htmlText = htmlText.replace(new RegExp(segmentsText, "gi"),"")
                        htmlText += segmentsText
                        scope.displayElements.text.html(htmlText)
                    }
                    if(command === 'insertUnorderedList' || command === 'insertOrderedList') taFixChrome(scope.displayElements.text);
                    // refocus on the shown display element, this fixes a display bug when using :focus styles to outline the box. You still have focus on the text/html input it just doesn't show up
                    if (scope.showHtml)
                        scope.displayElements.html[0].focus();
                    else
                        scope.displayElements.text[0].focus();
                    // note that wrapSelection is called via ng-click in the tool plugins so we are already within a $apply
                    scope.updateSelectedStyles();
                    if (!scope.showHtml) scope.updateTaBindtext(); // only update if NOT in html mode
                },
                showHtml: false
            });
            // setup the options from the optional attributes
            if (!!attrs.taToolbar)					scope.toolbar = scope.$eval(attrs.taToolbar);
            if (!!attrs.taFocussedClass)			scope.classes.focussed = scope.$eval(attrs.taFocussedClass);
            if (!!attrs.taToolbarClass)				scope.classes.toolbar = attrs.taToolbarClass;
            if (!!attrs.taToolbarGroupClass)		scope.classes.toolbarGroup = attrs.taToolbarGroupClass;
            if (!!attrs.taToolbarButtonClass)		scope.classes.toolbarButton = attrs.taToolbarButtonClass;
            if (!!attrs.taToolbarActiveButtonClass)	scope.classes.toolbarButtonActive = attrs.taToolbarActiveButtonClass;
            if (!!attrs.taTextEditorClass)			scope.classes.textEditor = attrs.taTextEditorClass;
            if (!!attrs.taHtmlEditorClass)			scope.classes.htmlEditor = attrs.taHtmlEditorClass;

            // Setup the HTML elements as variable references for use later
            scope.displayElements = {
                toolbar: angular.element("<div></div>"),
                forminput: angular.element("<input type='hidden' style='display: none;'>"),
                html: angular.element("<textarea ng-show='showHtml' ta-bind='html' ng-model='html' ></textarea>"),
                text: angular.element("<div style='overflow: auto;height:800px;max-height: 500px;' contentEditable='true' ng-hide='showHtml' ta-bind='text' ng-model='text' ></div>")
            };
            // add the main elements to the origional element
            element.append(scope.displayElements.toolbar);
            element.append(scope.displayElements.text);
            element.append(scope.displayElements.html);

            if(!!attrs.name){
                scope.displayElements.forminput.attr('name', attrs.name);
                element.append(scope.displayElements.forminput);
            }

            // compile the scope with the text and html elements only - if we do this with the main element it causes a compile loop
            $compile(scope.displayElements.text)(scope);
            $compile(scope.displayElements.html)(scope);
            // add the classes manually last
            element.addClass("ta-root");
            scope.displayElements.toolbar.addClass("ta-toolbar " + scope.classes.toolbar);
//            scope.displayElements.text.addClass("ta-text ta-editor " + scope.classes.textEditor);
//            scope.displayElements.html.addClass("ta-html ta-editor " + scope.classes.textEditor);

            // note that focusout > focusin is called everytime we click a button
            element.on('focusin', function(){ // cascades to displayElements.text and displayElements.html automatically.
                element.addClass(scope.classes.focussed);
                $timeout(function(){ element.triggerHandler('focus'); }, 0); // to prevent multiple apply error defer to next seems to work.
            });
            element.on('focusout', function(){
                $timeout(function(){
                    // if we have NOT focussed again on the text etc then fire the blur events
                    if(!($document[0].activeElement === scope.displayElements.html[0]) && !($document[0].activeElement === scope.displayElements.text[0])){
                        element.removeClass(scope.classes.focussed);
                        $timeout(function(){ element.triggerHandler('blur'); }, 0); // to prevent multiple apply error defer to next seems to work.
                    }
                }, 0);
            });

            scope.tools = {}; // Keep a reference for updating the active states later
            // create the tools in the toolbar
            for (var _i = 0; _i < scope.toolbar.length; _i++) {
                // setup the toolbar group
                group = scope.toolbar[_i];
                groupElement = angular.element("<div></div>");
                groupElement.addClass(scope.classes.toolbarGroup);
                for (var _j = 0; _j < group.length; _j++) {
                    // init and add the tools to the group
                    tool = group[_j]; // a tool name (key name from textAngularTools struct)
                    toolElement = angular.element($rootScope.textAngularTools[tool].display);
                    toolElement.addClass(scope.classes.toolbarButton);
                    toolElement.attr('unselectable', 'on'); // important to not take focus from the main text/html entry
                    toolElement.attr('ng-disabled', 'showHtml()');
                    var childScope = angular.extend(scope.$new(true), $rootScope.textAngularTools[tool], { // add the tool specific functions
                        name: tool,
                        showHtml: function(){
                            if(this.name !== 'html') return this.$parent.showHtml;
                            return false;
                        },
                        displayActiveToolClass: function(active){
                            return (active)? this.$parent.classes.toolbarButtonActive : '';
                        }
                    }); //creates a child scope of the main angularText scope and then extends the childScope with the functions of this particular tool
                    scope.tools[tool] = childScope; // reference to the scope kept
                    groupElement.append($compile(toolElement)(childScope)); // append the tool compiled with the childScope to the group element
                }
                scope.displayElements.toolbar.append(groupElement); // append the group to the toolbar
            }

            // changes to the model variable from outside the html/text inputs
            ngModel.$render = function() {
                scope.displayElements.forminput.val(ngModel.$viewValue);
                if(ngModel.$viewValue === undefined) return;
                // if the editors aren't focused they need to be updated, otherwise they are doing the updating
                if (!($document[0].activeElement === scope.displayElements.html[0]) && !($document[0].activeElement === scope.displayElements.text[0])) {
                    var val = ngModel.$viewValue || ''; // in case model is null
                    scope.text = val;
                    scope.html = val;
                }
            };

            scope.$watch('text', function(newValue, oldValue){
                scope.html = newValue;
                ngModel.$setViewValue(newValue);
                scope.displayElements.forminput.val(newValue);
            });
            scope.$watch('html', function(newValue, oldValue){
                scope.text = newValue;
                ngModel.$setViewValue(newValue);
                scope.displayElements.forminput.val(newValue);
            });

            // the following is for applying the active states to the tools that support it
            scope.bUpdateSelectedStyles = false;
            // loop through all the tools polling their activeState function if it exists
            scope.updateSelectedStyles = function() {
                for (var _k = 0; _k < scope.toolbar.length; _k++) {
                    var groups = scope.toolbar[_k];
                    for (var _l = 0; _l < groups.length; _l++) {
                        tool = groups[_l];
                        if (scope.tools[tool].activeState != null) {
                            scope.tools[tool].active = scope.tools[tool].activeState.apply(scope);
                        }
                    }
                }
                if (this.bUpdateSelectedStyles) $timeout(this.updateSelectedStyles, 200); // used to update the active state when a key is held down, ie the left arrow
            };
            // start updating on keydown
            keydown = function(e) {
                scope.bUpdateSelectedStyles = true;
                scope.$apply(function() {
                    scope.updateSelectedStyles();
                });
            };
            scope.displayElements.html.on('keydown', keydown);
            scope.displayElements.text.on('keydown', keydown);
            // stop updating on key up and update the display/model
            keyup = function(e) {
                scope.bUpdateSelectedStyles = false;
            };
            scope.displayElements.html.on('keyup', keyup);
            scope.displayElements.text.on('keyup', keyup);
            // update the toolbar active states when we click somewhere in the text/html boxed
            mouseup = function(e) {
                scope.$apply(function() {
                    scope.updateSelectedStyles();
                });
            };
            scope.displayElements.html.on('mouseup', mouseup);
            scope.displayElements.text.on('mouseup', mouseup);
        }
    };
}]).directive('taBind', ['$sanitize', '$document', 'taFixChrome','$sce', function($sanitize,$document, taFixChrome,$sce){
    // Uses for this are textarea or input with ng-model and ta-bind='text' OR any non-form element with contenteditable="contenteditable" ta-bind="html|text" ng-model
    return {
        require: 'ngModel',
        scope: {'taBind': '@'},
        link: function(scope, element, attrs, ngModel){
            var isContentEditable = element[0].tagName.toLowerCase() !== 'textarea' && element[0].tagName.toLowerCase() !== 'input' && element.attr('contenteditable') !== undefined;
            // in here we are undoing the converts used elsewhere to prevent the < > and & being displayed when they shouldn't in the code.
            var compileHtml = function(){
                var result = taFixChrome(angular.element("<div>").append(element.html())).html();
                if(scope.taBind === 'html' && isContentEditable) result = result.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, '&');
                return result;
            };

            scope.$parent['updateTaBind' + scope.taBind] = function(){//used for updating when inserting wrapped elements
                var compHtml = compileHtml();
                var tempParsers = ngModel.$parsers;
                ngModel.$parsers = []; // temp disable of the parsers
                ngModel.$oldViewValue = compHtml;
                ngModel.$setViewValue(compHtml);
                ngModel.$parsers = tempParsers;
            };

            //this code is used to update the models when data is entered/deleted
            if(isContentEditable){
                element.on('keyup', function(e){
                    ngModel.$setViewValue(compileHtml());
                });
            }

            ngModel.$parsers.push(function(value){
                // all the code here takes the information from the above keyup function or any other time that the viewValue is updated and parses it for storage in the ngModel
                if(ngModel.$oldViewValue === undefined) ngModel.$oldViewValue = value;
                try{
                    $sanitize(value); // this is what runs when ng-bind-html is used on the variable
                }catch(e){
                    return ngModel.$oldViewValue; //prevents the errors occuring when we are typing in html code
                }
                ngModel.$oldViewValue = value;
                return value;
            });

            // changes to the model variable from outside the html/text inputs
            ngModel.$render = function() {
                if(ngModel.$viewValue === undefined) return;
                // if the editor isn't focused it needs to be updated, otherwise it's receiving user input
                if ($document[0].activeElement !== element[0]) {
                    var val = ngModel.$viewValue || ''; // in case model is null
                    ngModel.$oldViewValue = val;
                    if(scope.taBind === 'text'){ //WYSIWYG Mode
//                        $sce.trustAsHtml(val);
                        element.html(val);
                        element.find('a').on('click', function(e){
                            e.preventDefault();
                            return false;
                        });
                    }else if(isContentEditable || (element[0].tagName.toLowerCase() !== 'textarea' && element[0].tagName.toLowerCase() !== 'input')) // make sure the end user can SEE the html code.
                        element.html(val.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, '&gt;'));
                    else element.val(val); // only for input and textarea inputs
                }else if(!isContentEditable) element.val(val); // only for input and textarea inputs
            };
        }
    };
}]).factory('taFixChrome', function(){
    // get whaterever rubbish is inserted in chrome
    var taFixChrome = function($html){ // should be an angular.element object, returns object for chaining convenience
        // fix the chrome trash that gets inserted sometimes
        var spans = angular.element($html).find('span'); // default wrapper is a span so find all of them
        for(var s = 0; s < spans.length; s++){
            var span = angular.element(spans[s]);
            if(span.attr('style') && span.attr('style').match(/line-height: 1.428571429;|color: inherit; line-height: 1.1;/i)){ // chrome specific string that gets inserted into the style attribute, other parts may vary. Second part is specific ONLY to hitting backspace in Headers
                if(span.next().length > 0 && span.next()[0].tagName === 'BR') span.next().remove()
                span.replaceWith(span.html());
            }
        }
        var result = $html.html().replace(/style="[^"]*?(line-height: 1.428571429;|color: inherit; line-height: 1.1;)[^"]*"/ig, ''); // regex to replace ONLY offending styles - these can be inserted into various other tags on delete
        $html.html(result);
        return $html;
    };
    return taFixChrome;
});

/* Make clicks pass-through */
#loading-bar,
#loading-bar-spinner {
  pointer-events: none;
  -webkit-pointer-events: none;
  -webkit-transition: 350ms linear all;
  -moz-transition: 350ms linear all;
  -o-transition: 350ms linear all;
  transition: 350ms linear all;
}

#loading-bar.ng-enter,
#loading-bar.ng-leave.ng-leave-active,
#loading-bar-spinner.ng-enter,
#loading-bar-spinner.ng-leave.ng-leave-active {
  opacity: 0;
}

#loading-bar.ng-enter.ng-enter-active,
#loading-bar.ng-leave,
#loading-bar-spinner.ng-enter.ng-enter-active,
#loading-bar-spinner.ng-leave {
  opacity: 1;
}

#loading-bar .bar {
  -webkit-transition: width 350ms;
  -moz-transition: width 350ms;
  -o-transition: width 350ms;
  transition: width 350ms;

  background: #000000;
  position: fixed;
  z-index: 10002;
  top: 0;
  left: 0;
  width: 100%;
  height: 2px;
  border-bottom-right-radius: 1px;
  border-top-right-radius: 1px;
}

/* Fancy blur effect */
#loading-bar .peg {
  position: absolute;
  width: 70px;
  right: 0;
  top: 0;
  height: 2px;
  opacity: .45;
  -moz-box-shadow: #000000 1px 0 6px 1px;
  -ms-box-shadow: #000000 1px 0 6px 1px;
  -webkit-box-shadow: #000000 1px 0 6px 1px;
  box-shadow: #000000 1px 0 6px 1px;
  -moz-border-radius: 100%;
  -webkit-border-radius: 100%;
  border-radius: 100%;
}

#loading-bar-spinner {
  display: block;
  position: fixed;
  z-index: 10002;
  top: 10px;
  left: 10px;
}

#loading-bar-spinner .spinner-icon {
  width: 14px;
  height: 14px;

  border:  solid 2px transparent;
  border-top-color: #000000;
  border-left-color: #000000;
  border-radius: 10px;

  -webkit-animation: loading-bar-spinner 400ms linear infinite;
  -moz-animation:    loading-bar-spinner 400ms linear infinite;
  -ms-animation:     loading-bar-spinner 400ms linear infinite;
  -o-animation:      loading-bar-spinner 400ms linear infinite;
  animation:         loading-bar-spinner 400ms linear infinite;
}

@-webkit-keyframes loading-bar-spinner {
  0%   { -webkit-transform: rotate(0deg);   transform: rotate(0deg); }
  100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }
}
@-moz-keyframes loading-bar-spinner {
  0%   { -moz-transform: rotate(0deg);   transform: rotate(0deg); }
  100% { -moz-transform: rotate(360deg); transform: rotate(360deg); }
}
@-o-keyframes loading-bar-spinner {
  0%   { -o-transform: rotate(0deg);   transform: rotate(0deg); }
  100% { -o-transform: rotate(360deg); transform: rotate(360deg); }
}
@-ms-keyframes loading-bar-spinner {
  0%   { -ms-transform: rotate(0deg);   transform: rotate(0deg); }
  100% { -ms-transform: rotate(360deg); transform: rotate(360deg); }
}
@keyframes loading-bar-spinner {
  0%   { transform: rotate(0deg);   transform: rotate(0deg); }
  100% { transform: rotate(360deg); transform: rotate(360deg); }
}
/*
 * angular-loading-bar
 *
 * intercepts XHR requests and creates a loading bar.
 * Based on the excellent nprogress work by rstacruz (more info in readme)
 *
 * (c) 2013 Wes Cruver
 * License: MIT
 */


(function() {

'use strict';

// Alias the loading bar for various backwards compatibilities since the project has matured:
angular.module('angular-loading-bar', ['cfp.loadingBarInterceptor']);
angular.module('chieffancypants.loadingBar', ['cfp.loadingBarInterceptor']);


/**
 * loadingBarInterceptor service
 *
 * Registers itself as an Angular interceptor and listens for XHR requests.
 */
angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar'])
  .config(['$httpProvider', function ($httpProvider) {

    var interceptor = ['$q', '$cacheFactory', '$timeout', '$rootScope', 'cfpLoadingBar', function ($q, $cacheFactory, $timeout, $rootScope, cfpLoadingBar) {

      /**
       * The total number of requests made
       */
      var reqsTotal = 0;

      /**
       * The number of requests completed (either successfully or not)
       */
      var reqsCompleted = 0;

      /**
       * The amount of time spent fetching before showing the loading bar
       */
      var latencyThreshold = cfpLoadingBar.latencyThreshold;

      /**
       * $timeout handle for latencyThreshold
       */
      var startTimeout;


      /**
       * calls cfpLoadingBar.complete() which removes the
       * loading bar from the DOM.
       */
      function setComplete() {
        $timeout.cancel(startTimeout);
        cfpLoadingBar.complete();
        reqsCompleted = 0;
        reqsTotal = 0;
      }

      /**
       * Determine if the response has already been cached
       * @param  {Object}  config the config option from the request
       * @return {Boolean} retrns true if cached, otherwise false
       */
      function isCached(config) {
        var cache;
        var defaultCache = $cacheFactory.get('$http');
        var defaults = $httpProvider.defaults;

        // Choose the proper cache source. Borrowed from angular: $http service
        if ((config.cache || defaults.cache) && config.cache !== false &&
          (config.method === 'GET' || config.method === 'JSONP')) {
            cache = angular.isObject(config.cache) ? config.cache
              : angular.isObject(defaults.cache) ? defaults.cache
              : defaultCache;
        }

        var cached = cache !== undefined ?
          cache.get(config.url) !== undefined : false;

        if (config.cached !== undefined && cached !== config.cached) {
          return config.cached;
        }
        config.cached = cached;
        return cached;
      }


      return {
        'request': function(config) {
          // Check to make sure this request hasn't already been cached and that
          // the requester didn't explicitly ask us to ignore this request:
          if (!config.ignoreLoadingBar && !isCached(config)) {
            $rootScope.$broadcast('cfpLoadingBar:loading', {url: config.url});
            if (reqsTotal === 0) {
              startTimeout = $timeout(function() {
                cfpLoadingBar.start();
              }, latencyThreshold);
            }
            reqsTotal++;
            cfpLoadingBar.set(reqsCompleted / reqsTotal);
          }
          return config;
        },

        'response': function(response) {
          if (!response.config.ignoreLoadingBar && !isCached(response.config)) {
            reqsCompleted++;
            $rootScope.$broadcast('cfpLoadingBar:loaded', {url: response.config.url, result: response});
            if (reqsCompleted >= reqsTotal) {
              setComplete();
            } else {
              cfpLoadingBar.set(reqsCompleted / reqsTotal);
            }
          }
          return response;
        },

        'responseError': function(rejection) {
          if (!rejection.config.ignoreLoadingBar && !isCached(rejection.config)) {
            reqsCompleted++;
            $rootScope.$broadcast('cfpLoadingBar:loaded', {url: rejection.config.url, result: rejection});
            if (reqsCompleted >= reqsTotal) {
              setComplete();
            } else {
              cfpLoadingBar.set(reqsCompleted / reqsTotal);
            }
          }
          return $q.reject(rejection);
        }
      };
    }];

    $httpProvider.interceptors.push(interceptor);
  }]);


/**
 * Loading Bar
 *
 * This service handles adding and removing the actual element in the DOM.
 * Generally, best practices for DOM manipulation is to take place in a
 * directive, but because the element itself is injected in the DOM only upon
 * XHR requests, and it's likely needed on every view, the best option is to
 * use a service.
 */
angular.module('cfp.loadingBar', [])
  .provider('cfpLoadingBar', function() {

    this.includeSpinner = true;
    this.includeBar = true;
    this.latencyThreshold = 100;
    this.startSize = 0.02;
    this.parentSelector = 'body';
    this.spinnerTemplate = '<div id="loading-bar-spinner"><div class="spinner-icon"></div></div>';
    this.loadingBarTemplate = '<div id="loading-bar"><div class="bar"><div class="peg"></div></div></div>';

    this.$get = ['$injector', '$document', '$timeout', '$rootScope', function ($injector, $document, $timeout, $rootScope) {
      var $animate;
      var $parentSelector = this.parentSelector,
        loadingBarContainer = angular.element(this.loadingBarTemplate),
        loadingBar = loadingBarContainer.find('div').eq(0),
        spinner = angular.element(this.spinnerTemplate);

      var incTimeout,
        completeTimeout,
        started = false,
        status = 0;

      var includeSpinner = this.includeSpinner;
      var includeBar = this.includeBar;
      var startSize = this.startSize;

      /**
       * Inserts the loading bar element into the dom, and sets it to 2%
       */
      function _start() {
        if (!$animate) {
          $animate = $injector.get('$animate');
        }

        var $parent = $document.find($parentSelector).eq(0);
        $timeout.cancel(completeTimeout);

        // do not continually broadcast the started event:
        if (started) {
          return;
        }

        $rootScope.$broadcast('cfpLoadingBar:started');
        started = true;

        if (includeBar) {
          $animate.enter(loadingBarContainer, $parent);
        }

        if (includeSpinner) {
          $animate.enter(spinner, $parent);
        }

        _set(startSize);
      }

      /**
       * Set the loading bar's width to a certain percent.
       *
       * @param n any value between 0 and 1
       */
      function _set(n) {
        if (!started) {
          return;
        }
        var pct = (n * 100) + '%';
        loadingBar.css('width', pct);
        status = n;

        // increment loadingbar to give the illusion that there is always
        // progress but make sure to cancel the previous timeouts so we don't
        // have multiple incs running at the same time.
        $timeout.cancel(incTimeout);
        incTimeout = $timeout(function() {
          _inc();
        }, 250);
      }

      /**
       * Increments the loading bar by a random amount
       * but slows down as it progresses
       */
      function _inc() {
        if (_status() >= 1) {
          return;
        }

        var rnd = 0;

        // TODO: do this mathmatically instead of through conditions

        var stat = _status();
        if (stat >= 0 && stat < 0.25) {
          // Start out between 3 - 6% increments
          rnd = (Math.random() * (5 - 3 + 1) + 3) / 100;
        } else if (stat >= 0.25 && stat < 0.65) {
          // increment between 0 - 3%
          rnd = (Math.random() * 3) / 100;
        } else if (stat >= 0.65 && stat < 0.9) {
          // increment between 0 - 2%
          rnd = (Math.random() * 2) / 100;
        } else if (stat >= 0.9 && stat < 0.99) {
          // finally, increment it .5 %
          rnd = 0.005;
        } else {
          // after 99%, don't increment:
          rnd = 0;
        }

        var pct = _status() + rnd;
        _set(pct);
      }

      function _status() {
        return status;
      }

      function _completeAnimation() {
        status = 0;
        started = false;
      }

      function _complete() {
        if (!$animate) {
          $animate = $injector.get('$animate');
        }

        $rootScope.$broadcast('cfpLoadingBar:completed');
        _set(1);

        $timeout.cancel(completeTimeout);

        // Attempt to aggregate any start/complete calls within 500ms:
        completeTimeout = $timeout(function() {
          var promise = $animate.leave(loadingBarContainer, _completeAnimation);
          if (promise && promise.then) {
            promise.then(_completeAnimation);
          }
          $animate.leave(spinner);
        }, 500);
      }

      return {
        start            : _start,
        set              : _set,
        status           : _status,
        inc              : _inc,
        complete         : _complete,
        includeSpinner   : this.includeSpinner,
        latencyThreshold : this.latencyThreshold,
        parentSelector   : this.parentSelector,
        startSize        : this.startSize
      };


    }];     //
  });       // wtf javascript. srsly
})();       //
/**
 * Created by WillChen on 2014/11/3.
 */
(function () {
    angular.module('SPA.Module', ['ui.router', 'resourceServiceModule', 'ngSanitize', 'textAngular','chieffancypants.loadingBar', 'ngAnimate'])
        .config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function ($stateProvider, $urlRouterProvider, $locationProvider) {
            $urlRouterProvider.otherwise("/home")
            $stateProvider
                .state('home', {
                    url: "/home",
                    templateUrl: "home.html",
                    controller: 'homeController'
                })
                .state('detail', {
                    url: "/details/:detailId",
                    templateUrl: "detail.html",
                    controller: 'detailController'
                })
                .state('edit', {
                    url: "/edit/:detailId",
                    templateUrl: "edit.html",
                    controller: "editController"
                })
                .state('create', {
                    url: '/create',
                    templateUrl: "edit.html",
                    controller: 'createController'
                })
        }])
        .controller('mainController', function ($rootScope,$scope,$state, resourceService) {
            resourceService.setAuthListener(onAuthChange,true)//監聽認證狀態
            $rootScope.isAllBrowseState = true
            $scope.search = {}
            $rootScope.checkLoginUser = function(model){
                return (model && $rootScope.userId == model.userId)
            }
            $scope.$watch('isAllBrowseState',function(newVal){
                if (newVal){
                    $scope.browseStateBtnText = 'My Posts'
                    if ($scope.search.userId != undefined) delete $scope.search.userId
                }else{
                    $scope.browseStateBtnText = 'All Posts'
                    $scope.search.userId = $rootScope.userId
                }
            })
            $scope.browseStateChange = function(isAllBrowseState){
                $rootScope.isAllBrowseState = isAllBrowseState
            }
            $scope.login = function (provider) {
                resourceService.login(provider).then(function (authData) {
                    resourceService.setAuthListener(onAuthChange,true)//監聽認證狀態
                }, function (error) {
                    resourceService.setAuthListener(onAuthChange,false)//解除監聽狀態
                })
            }
            $scope.logout = function () {
                resourceService.logout()
                resourceService.setAuthListener(onAuthChange,false)//解除監聽狀態
                $scope.browseStateChange(true)
                $state.go('home')
            }
            function onAuthChange(authData){
               if (authData) {
                   var cachedUserProfile = authData['google'].cachedUserProfile
                   setAuthData(authData.uid,cachedUserProfile.given_name,cachedUserProfile.picture)
               }
               else setAuthData(null,null,null)
            }
            function setAuthData(userId,userName,picturePath){
                $rootScope.userId = userId
                $rootScope.userName = userName
                $rootScope.picturePath = picturePath
            }

        })
        .controller('homeController', function ($scope, $sce,resourceService,cfpLoadingBar) {
            cfpLoadingBar.start()
            resourceService.read().then(function (data) {
                $scope.datasource = data
                cfpLoadingBar.complete()
            })
            $scope.getShortContentTrust = function(content){
                return $sce.trustAsHtml(content)
            }
        })
        .controller('detailController', function ($scope, $sce,$stateParams, $state, resourceService) {
            $scope.selectedId = $stateParams.detailId.toString()
            resourceService.read($scope.selectedId).then(function (data) {
                $scope.model = data
                $scope.model.content = $sce.trustAsHtml($scope.model.content)
            })
            $scope.deleteData = function (selectedId) {
                resourceService.delete(selectedId)//待修改
                $state.go('home')
            }
        })
        .controller('editController', function ($rootScope,$scope,$stateParams, $state, resourceService, operatingService) {
            $scope.segmentsText = '<!--segments-->'
            $scope.selectedId = $stateParams.detailId.toString()
            $scope.isTextAngular = true
            resourceService.read($scope.selectedId).then(function (data) {
                $scope.model = data
                if (!$rootScope.checkLoginUser(data)) $state.go('home')//新增處
            })
            $scope.setPrettyEdit = function (isPrettyEdit) {
                $scope.isTextAngular = isPrettyEdit
            }
            $scope.doModify = function (id, item) {
                item.postDate = operatingService.getNowDatetime()
                item.shortContent = operatingService.transformToShortContent(item.content, $scope.segmentsText)
                item.userId =  $rootScope.userId//新增處
                item.author = $rootScope.userName
                resourceService.update(id, item)
                $state.go('home')
            }
        })
        .controller('createController', function ($rootScope,$scope, $state, resourceService, operatingService) {
            $scope.isTextAngular = true
            resourceService.read(null, true).then(function (data) {
                $scope.model = data
                if (!$rootScope.userId) $state.go('home')//新增處
            })
            $scope.setPrettyEdit = function (isPrettyEdit) {
                $scope.isTextAngular = isPrettyEdit
            }
            $scope.doInsert = function (item) {
                item.postDate = operatingService.getNowDatetime()
                item.shortContent = operatingService.transformToShortContent(item.content, $scope.segmentsText)
                item.userId = $rootScope.userId//新增處
                item.author = $rootScope.userName
                resourceService.create(item)
                $state.go('home')
            }
        })
        .filter('objectToArray', function () {
            return function (obj) {
                if (!(obj instanceof Object)) return obj;
                return Object.keys(obj).map(function (key) {
                    return Object.defineProperty(obj[key], 'key', {__proto__: null, value: key});
                })
            }
        })

})()
/**
 * Created by WillChen on 2014/11/4.
 */
(function () {
    angular.module('resourceServiceModule', [])
        .service('resourceService', function ($q) {
            var directory = 'https://willapp.firebaseio.com/authenticationBlog/'
            var dataDirectory = directory + 'datas/'
            var blogDataRef = new Firebase(dataDirectory)
            function _create(item) {
                blogDataRef.push(item)
            }
            function _read(id, isTemplate) {
                var tempRef
                if (isTemplate) tempRef = new Firebase(directory + 'template')
                else if (id) tempRef = new Firebase(dataDirectory + id)
                else tempRef = blogDataRef
                var deferred = $q.defer()
                tempRef.on('value', function (snapshot) {
                    deferred.resolve(snapshot.val())
                }, function (errorObject) {
                    deferred.reject(errorObject)
                });
                return deferred.promise
            }
            function _update(id, item) {
                var obj = {}
                obj[id] = item
                blogDataRef.update(obj)
            }
            function _delete(id) {
                var itemRef = blogDataRef.child(id)
                itemRef.remove();
            }
            function _setAuthListener(authChange, isOn) {
                if (isOn) blogDataRef.onAuth(authChange)
                else blogDataRef.offAuth(authChange)
            }
            function _login(provider) {
                var deferred = $q.defer()
                blogDataRef.authWithOAuthPopup(provider, function (error, authData) {
                    if (!error) deferred.resolve(authData)
                    else deferred.reject(error)
                })
                return deferred.promise
            }
            function _logout() {
                blogDataRef.unauth()
            }
            return{
                create: _create,
                read: _read,
                update: _update,
                delete: _delete,
                setAuthListener: _setAuthListener,
                login: _login,
                logout: _logout
            }
        })
        .service('operatingService', function () {
            function _transformToShortContent(content, segmentsText) {
                var rexExpText = '(.*?)' + segmentsText
                var arr = content.match(new RegExp(rexExpText, "i"))
                return arr ? arr[arr.length - 1] : ""
            }

            function _getNowDatetime() {
                var now = new Date()
                return now.toISOString()
            }

            return{
                transformToShortContent: _transformToShortContent,
                getNowDatetime: _getNowDatetime
            }
        })
})()
.headerText{
    color: #ffffff;
    text-shadow: 2px 1px 1px rgba(196, 196, 196, 1);
    margin: 0;
    padding: 1em;
}
.containerBorder {
    margin-top: 1em;
    padding: 0.5em;
    -webkit-box-shadow: 1px -1px 5px 5px rgba(209,240,216,1);
    -moz-box-shadow: 1px -1px 5px 5px rgba(209,240,216,1);
    box-shadow: 1px -1px 5px 5px rgba(209,240,216,1);
}
.textboxRadius{
    color: #000000;
    border-radius:25px;
    -moz-border-radius:25px;
    -webkit-border-radius:25px;
}
.btn-circle {
    width: 2.5em;
    height: 2.5em;
    text-align: center;
    padding: 6px 0;
    font-size: 12px;
    line-height: 1.428571429em;
    border-radius: 1.2em;
}
<div class="row">
    <div class="col-md-10">
        <div class="input-group ">
            <span class="input-group-addon"><i class="fa fa-search fa-fw"></i></span>
            <input class="form-control textboxRadius" type="text" placeholder="Search title..." ng-model="search.title">
        </div>
    </div>
    <div class="col-md-2">
    <button ng-if="userId" ng-class="{true:'btn btn-default btn-circle',false:'btn btn-success btn-circle'}[isAllBrowseState]" ng-click="browseStateChange(!isAllBrowseState)"><i class="fa fa-user"></i></button>
    </div>
</div>
<div ng-repeat="item in datasource|objectToArray|filter:search|orderBy:'postDate':true" class="containerBorder">
    <i class="fa fa-calendar-o" ><i>{{item.postDate|date:'yyyy/MM/dd hh:mm:ss'}}</i></i>
    <a ng-if="checkLoginUser(item)" href="#" ui-sref="detail({detailId:item.key})" class="label label-success pull-right"><i class="fa fa-user"></i>My Post</a>
    <hr/>
    <h3><a href="#" ui-sref="detail({detailId:item.key})">{{item.title}}</a></h3>

    <div ng-bind-html="getShortContentTrust(item.shortContent)"></div>
</div>
<div class="containerBorder">
<a class="btn btn-lg btn-default" ng-if="checkLoginUser(model)" ui-sref="edit({detailId: selectedId})"><i class="fa fa-edit fa-fw"></i></a>
<button type="button" ng-if="checkLoginUser(model)" class="btn btn-lg btn-default " ng-click="deleteData(selectedId)">
    <i class="fa fa-trash-o fa-fw"></i>
</button>
<a class="btn btn-lg btn-default" ui-sref="home"><i class="fa fa-home fa-fw"></i></a>
<hr />

<div style="margin: 10px 0;">
    <h3><a href="javascript:void(0)">{{model.title}}</a></h3>
    <label class="label label-success"><i><i class="fa fa-comment-o"></i>{{model.postDate|date:'yyyy/MM/dd hh:mm:ss'}} by {{model.author}}</i></label>
    <p>
    <div ng-bind-html="model.content"></div>
</div>
</div>
<div class="containerBorder">
    標題:
    <input type="text" class="form-control" placeholder="文章標題" ng-model="model.title" />
    <p></p>
    <div class="btn-group">
        <button type="button" class="btn btn-default btn-sm" ng-click="setPrettyEdit(true)" data-toggle="tooltip" data-placement="bottom" title="Edit Text"><i class="fa fa-pencil"></i></button>
        <button type="button" class="btn btn-default btn-sm" ng-click="setPrettyEdit(false)" data-toggle="tooltip" data-placement="bottom" title="Edit SourceCode"><i class="fa fa-file-code-o"></i></button>
    </div>
    <p></p>
    <div text-angular="text-angular" segments-text="segmentsText" ng-model="model.content" ng-show="isTextAngular"></div>
    <div ng-hide="isTextAngular">
        <textarea class="form-control" rows="20" ng-model="model.content"></textarea>
    </div>
<br />
<a class="btn btn-success" ng-if="doInsert" ng-disabled="model.title == '' || model.content == ''" ng-click="doInsert(model)"><i class="fa fa-check fa-fw"></i>確定新增</a>
<a class="btn btn-success" ng-if="doModify" ng-disabled="model.title == '' || model.content == ''" ng-click="doModify(selectedId,model)"><i class="fa fa-check fa-fw"></i>修改完成</a>
<a class="btn btn-default" ui-sref="home"><i class="fa fa-home fa-fw"></i>回列表</a>
</div>