frMarkdown.js 7.95 KB
import showdown from '../../../js/vendor/showdown';
import htmlescape from '../../../js/vendor/showdown-htmlescape';
import newline from '../../../js/showdown-extensions/newline';
import autoembed from '../../../js/showdown-extensions/autoembed';
import poll from '../../../js/showdown-extensions/poll';
import pagedown from '../../../../resources/js/markdown/Markdown.Editor';

angular.module('footyroom')

.service('markdown', function () {
    var converter = new showdown.Converter({
        extensions: [
            htmlescape,
            newline,
            poll,
            autoembed,
        ],
        simplifiedAutoLink: true,
        tables: true,
    });

    return {
        render: function (markdown) {
            return converter.makeHtml(markdown);
        },

        _converter: converter,
    };
})

.directive('markdownEditor', ['$timeout', '$compile', 'markdown', function ($timeout, $compile, markdown) {
    var nextId = 0;

    return {
        restrict: 'E',
        template:

		'<div id="wmd-button-bar-{{ editorId }}"></div>' +
		'<div ng-if="showHelp" class="mdhelp">' +
			'<div class="spinner mdhelp-spinner"></div>' +
			'<div ng-include="\'/views/ng/markdown/help.html?3\'"></div>' +
		'</div>' +
		'<textarea id="wmd-input-{{ editorId }}" class="wmd-input" rows="{{ rows }}" tabindex="100" placeholder="{{ placeholder }}"></textarea>' +
		'<div class="wmd-preview comment-text markdown-view" ng-style="content? {}: {display: \'none\'}" ng-if="showPreview"></div>',

        scope: {
            content: '=',
            showPreview: '=',
            previewEl: '=?',
            rows: '=?',
            placeholder: '=?',
        },

        link: function (scope, element, attrs) {
            scope.rows = scope.rows || 12;
            scope.editorId = nextId++;

            function help() {
                scope.showHelp = !scope.showHelp;
                scope.$apply();
            }

            $timeout(function () {
                /** @type {HTMLInputElement} */
                const inputElement = document.querySelector('#wmd-input-' + scope.editorId);
                const previewElement = scope.previewEl
                    ? document.querySelector(scope.previewEl)
                    : document.querySelector('.wmd-preview');

                previewElement.setAttribute('id', 'wmd-preview-' + scope.editorId);

                const editor = new pagedown.Editor(markdown._converter, '-' + scope.editorId, {
                    handler: help,
                });

                inputElement.value = scope.content || window.localStorage.getItem('markdownContent');

                // We need to watch changes on content happening programmatically.
                // When they happen we need to update the textarea and call for a
                // refresh.
                scope.$watch('content', function (newValue, oldValue) {
                    window.localStorage.setItem('markdownContent', inputElement.value);

                    if (newValue !== inputElement.value) {
                        inputElement.value = newValue;
                        editor.refreshPreview();
                    }
                });

                editor.hooks.chain('onPreviewRefresh', function () {
                    // Wire up changes caused by user interaction with the editor.
                    // We need this because we don't use ng-model on textarea.
                    // Doing so leads to many redundant call for refreshes.
                    // This seems to be the best way.
                    if (scope.content !== inputElement.value) {
                        scope.$apply(function () {
                            scope.content = inputElement.value;
                        });
                    }

                    // Compile output of preview with angular. We need this because we have
                    // angular directives in the output which need to be compiled, otherwise
                    // they just don't work.
                    if (previewElement) {
                        $compile(previewElement.innerHTML)(scope);
                    }
                });

                editor.run();
            });
        },
    };
}])

.directive('forumEditor', function () {
    return {
        restrict: 'E',
        template:

		'<div id="commenter" class="forum-editor clearfix">' +
			'<div class="box">' +
				'<markdown-editor content="content" show-preview="true"></markdown-editor>' +
				'<div class="error commenter-errors" ng-show="errors" ng-bind="errors"></div>' +
				'<div class="control clearfix">' +
					'<ul class="right-menu">' +
						'<li ng-click="submit()" tabindex="120">Say it</li>' +
						'<li class="cancel" ng-if="cancel" ng-click="cancel()" tabindex="121">Cancel</li>' +
					'</ul>' +
				'</div>' +
			'</div>' +
		'</div>',

        scope: {
            showPreview: '=',
            content: '=',
            errors: '=',
            submit: '=onSubmit',
            cancel: '=onCancel',
        },
    };
})

.directive('forumCommenter', ['$http', '$window', function ($http, $window) {
    var isSaving;

    return {
        restrict: 'E',
        template: `
            <forum-editor content="content" show-preview="showPreview" errors="errors" on-submit="submit" on-cancel="cancel"></forum-editor>
            <input name="name-email-url" type="text" style="display:none !important" tabindex="-1" autocomplete="off" ng-model="nameEmailUrl">
        `,
        scope: {
            commentId: '=',
            showPreview: '=',
            postId: '=',
            cancel: '=onCancel',
        },

        link: function ($scope, $element) {
            if ($scope.commentId) {
                loadContent();
            }

            $scope.submit = function () {
                save()

                .then(function () {
                    window.localStorage.removeItem('markdownContent');
                    $window.location.reload();
                })

                .catch(function (response) {
                    $scope.errors = response.data.errors || "Something went wrong, but we don't know what. Try again later or report this to us.";

                    isSaving = false;

                    $element.find('markdown-editor').litelay({ off: true });
                });
            };

            function loadContent() {
                $element.find('markdown-editor').litelay({ spinner: true });

                $http.get('/comments/' + $scope.commentId)

                .then(function (response) {
                    $scope.content = response.data.comment.content;
                    $element.find('markdown-editor').litelay({ off: true });
                });
            }

            function save(data) {
                if (!isSaving) {
                    isSaving = true;

                    $element.find('markdown-editor').litelay({ spinner: true });

                    if ($scope.commentId) {
                        return update();
                    }
                    return create();
                }
            }

            function create() {
                return $http.post('/forum/comments', {
                    content: $scope.content,
                    honeypot: $scope.nameEmailUrl,
                    postId: $scope.postId,
                });
            }

            function update() {
                return $http.put('/forum/comments/' + $scope.commentId, {
                    content: $scope.content,
                });
            }
        },
    };
}])

/**
 * A directive that binds click event on the element, upon which the element is
 * replaced with iframe whose source is defined by the directive's attribute.
 */
.directive('frMarkdownIframe', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            element.bind('click', function (e) {
                element.replaceWith('<div class="iframe-wrap"><iframe src="' + attr.frMarkdownIframe + '" frameborder="0" allowfullscreen="allowfullscreen"></iframe></div>');
            });
        },
    };
});