<commit_before>import PageSeoService from "../services/pageSeoService";
import {pageSeoTypes as ActionTypes}  from "../constants/actionTypes";
import pageActionTypes  from "../constants/actionTypes/pageActionTypes";
import util from "utils";
const pageSeoActions = {
    openNewForm() {
        return (dispatch) => {
            dispatch({
                type: ActionTypes.SEO_OPEN_NEW_FORM
            });
        };
    },
    closeNewForm() {
        return (dispatch) => {
            dispatch({
                type: ActionTypes.SEO_CLOSE_NEW_FORM
            });
        };
    },
    openEditForm(url) {
        return (dispatch) => {
            dispatch({
                type: ActionTypes.SEO_OPEN_EDIT_FORM,
                payload: {
                    url
                }
            });
        };
    },
    closeEditForm() {
        return (dispatch) => {
            dispatch({
                type: ActionTypes.SEO_CLOSE_EDIT_FORM
            });
        };
    },
    change(key, value) {
        return (dispatch) => {
            dispatch({
                type: ActionTypes.SEO_CHANGE_URL,
                payload: {
                    key: key,
                    value: value
                }
            });
        };
    },
    addUrl(url, primaryAliasId) {
        return (dispatch, getState) => {
            dispatch({
                type: ActionTypes.SEO_ADD_URL
            });

            const {pages} = getState();
            const tabId = pages.selectedPage.tabId;
            PageSeoService.add(url, tabId, primaryAliasId).then((response) => {
                const newUrl = {
                    ...url
                };
                if (!response.Success) {
                    dispatch({
                        type: ActionTypes.ERROR_SEO_ADDING_URL,
                        data: {error: response.ErrorMessage}
                    });
                    return;
                }
                newUrl.id = response.Id;
                dispatch({
                    type: ActionTypes.SEO_ADDED_URL
                });
                dispatch({
                    type: pageActionTypes.ADD_CUSTOM_URL,
                    payload: {
                        newUrl
                    }
                });
            }).catch((error) => {
                dispatch({
                    type: ActionTypes.ERROR_SEO_ADDING_URL,
                    data: {error}
                });
            });
        };
    },
    saveUrl(url, primaryAliasId) {
        return (dispatch, getState) => {
            dispatch({
                type: ActionTypes.SEO_SAVE_URL
            });

            const {pages} = getState();
            const tabId = pages.selectedPage.tabId;
            PageSeoService.save(url, tabId, primaryAliasId).then((response) => {
                if (!response.Success) {
                    dispatch({
                        type: ActionTypes.ERROR_SEO_SAVING_URL,
                        data: {error: response.ErrorMessage}
                    });
                    util.utilities.notifyError(response.ErrorMessage);
                    return;
                }

                dispatch({
                    type: ActionTypes.SEO_SAVED_URL
                });
                dispatch({
                    type: pageActionTypes.REPLACE_CUSTOM_URL,
                    payload: {
                        url
                    }
                });
            }).catch((error) => {
                dispatch({
                    type: ActionTypes.ERROR_SEO_SAVING_URL,
                    data: {error: error.ErrorMessage}
                });
            });
        };
    },
    deleteUrl(url) {
        return (dispatch, getState) => {
            dispatch({
                type: ActionTypes.SEO_DELETE_URL
            });

            const {pages} = getState();
            const tabId = pages.selectedPage.tabId;
            PageSeoService.delete(url, tabId).then((response) => {
                if (!response.Success) {
                    dispatch({
                        type: ActionTypes.ERROR_SEO_DELETING_URL,
                        data: {error: response.ErrorMessage}
                    });
                    return;
                }

                dispatch({
                    type: ActionTypes.SEO_DELETED_URL
                });
                dispatch({
                    type: pageActionTypes.DELETE_CUSTOM_URL,
                    payload: {
                        id: url.id
                    }
                });
            }).catch((error) => {
                dispatch({
                    type: ActionTypes.ERROR_SEO_DELETING_URL,
                    data: {error: error.ErrorMessage}
                });
            });
        };
    }
};

export default pageSeoActions;
<commit_msg>DNN-9919: show error msg for both add & save methods<commit_after>import PageSeoService from "../services/pageSeoService";
import {pageSeoTypes as ActionTypes}  from "../constants/actionTypes";
import pageActionTypes  from "../constants/actionTypes/pageActionTypes";
import utils from "../utils";
const pageSeoActions = {
    openNewForm() {
        return (dispatch) => {
            dispatch({
                type: ActionTypes.SEO_OPEN_NEW_FORM
            });
        };
    },
    closeNewForm() {
        return (dispatch) => {
            dispatch({
                type: ActionTypes.SEO_CLOSE_NEW_FORM
            });
        };
    },
    openEditForm(url) {
        return (dispatch) => {
            dispatch({
                type: ActionTypes.SEO_OPEN_EDIT_FORM,
                payload: {
                    url
                }
            });
        };
    },
    closeEditForm() {
        return (dispatch) => {
            dispatch({
                type: ActionTypes.SEO_CLOSE_EDIT_FORM
            });
        };
    },
    change(key, value) {
        return (dispatch) => {
            dispatch({
                type: ActionTypes.SEO_CHANGE_URL,
                payload: {
                    key: key,
                    value: value
                }
            });
        };
    },
    addUrl(url, primaryAliasId) {
        return (dispatch, getState) => {
            dispatch({
                type: ActionTypes.SEO_ADD_URL
            });

            const {pages} = getState();
            const tabId = pages.selectedPage.tabId;
            PageSeoService.add(url, tabId, primaryAliasId).then((response) => {
                const newUrl = {
                    ...url
                };
                if (!response.Success) {
                    dispatch({
                        type: ActionTypes.ERROR_SEO_ADDING_URL,
                        data: {error: response.ErrorMessage}
                    });
                    utils.notifyError(response.ErrorMessage);
                    return;
                }
                newUrl.id = response.Id;
                dispatch({
                    type: ActionTypes.SEO_ADDED_URL
                });
                dispatch({
                    type: pageActionTypes.ADD_CUSTOM_URL,
                    payload: {
                        newUrl
                    }
                });
            }).catch((error) => {
                dispatch({
                    type: ActionTypes.ERROR_SEO_ADDING_URL,
                    data: {error}
                });
            });
        };
    },
    saveUrl(url, primaryAliasId) {
        return (dispatch, getState) => {
            dispatch({
                type: ActionTypes.SEO_SAVE_URL
            });

            const {pages} = getState();
            const tabId = pages.selectedPage.tabId;
            PageSeoService.save(url, tabId, primaryAliasId).then((response) => {
                if (!response.Success) {
                    dispatch({
                        type: ActionTypes.ERROR_SEO_SAVING_URL,
                        data: {error: response.ErrorMessage}
                    });
                    utils.notifyError(response.ErrorMessage);
                    return;
                }

                dispatch({
                    type: ActionTypes.SEO_SAVED_URL
                });
                dispatch({
                    type: pageActionTypes.REPLACE_CUSTOM_URL,
                    payload: {
                        url
                    }
                });
            }).catch((error) => {
                dispatch({
                    type: ActionTypes.ERROR_SEO_SAVING_URL,
                    data: {error: error.ErrorMessage}
                });
            });
        };
    },
    deleteUrl(url) {
        return (dispatch, getState) => {
            dispatch({
                type: ActionTypes.SEO_DELETE_URL
            });

            const {pages} = getState();
            const tabId = pages.selectedPage.tabId;
            PageSeoService.delete(url, tabId).then((response) => {
                if (!response.Success) {
                    dispatch({
                        type: ActionTypes.ERROR_SEO_DELETING_URL,
                        data: {error: response.ErrorMessage}
                    });
                    return;
                }

                dispatch({
                    type: ActionTypes.SEO_DELETED_URL
                });
                dispatch({
                    type: pageActionTypes.DELETE_CUSTOM_URL,
                    payload: {
                        id: url.id
                    }
                });
            }).catch((error) => {
                dispatch({
                    type: ActionTypes.ERROR_SEO_DELETING_URL,
                    data: {error: error.ErrorMessage}
                });
            });
        };
    }
};

export default pageSeoActions;
<|endoftext|>


<commit_before>/**
 * this JavaScript files provites all logic for the page editor
 *
 * @author IcedReaper <Kevin.Lang@gmx.de>
 */
$(function() {
    if($('.content.editable').length === 1) {
        var editor = new irEditor($('.content.editable'));
    }
});

var irEditor = function($editor) {
    var isNumeric = function (n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    };

    $('form#irEditor').on('submit', function() {
        try {
            cleanup();
            $('input[name="content"]').val(buildSkeleton());
            setup();

            return true;
        }
        catch (error) {
            console.log(error);

            return false;
        }
    });

    var buildSkeleton = function() {
        var buildSubSkeleton = function($selector) {
            var skeletonNode = [];

            $selector.each(function(index) {
                skeletonNode[index] = {};
                var lastElement = false;

                if($(this).hasClass('row')) {
                    skeletonNode[index].name = 'row';
                }
                if($(this).attr('class').substring(0, 3) === 'col') {
                    skeletonNode[index].name    = 'col';
                    skeletonNode[index].classes = $(this).attr('class');
                }
                if($(this).hasClass('textBlock')) {
                    skeletonNode[index].name = 'textBlock';
                    skeletonNode[index].text = $(this).html();

                    lastElement = true;
                }
                if($(this).hasClass('heroImage')) {
                    skeletonNode[index].name            = 'heroImage';
                    skeletonNode[index].backgroundImage = $(this).css('background-image').replace(/(url\("https*:\/\/(\w+\.*)+|"\))/gi, '');
                    if($('div', $(this)).length === 1) {
                        skeletonNode[index].content = $('div', $(this)).html();
                    }

                    lastElement = true;
                }
                if($(this).hasClass('carousel')) {
                    skeletonNode[index].name = 'slider';
                    skeletonNode[index].id   = $(this).attr('id');

                    if(typeof $(this).attr('interval') !== 'undefined') { skeletonNode[index].interval = $(this).attr('interval'); }
                    if(typeof $(this).attr('pause')    !== 'undefined') { skeletonNode[index].pause    = $(this).attr('pause'); }
                    if(typeof $(this).attr('wrap')     !== 'undefined') { skeletonNode[index].wrap     = $(this).attr('wrap'); }

                    skeletonNode[index].images = [];
                    $('.carousel-inner > .item', $(this)).each(function(imageIndex) {
                        skeletonNode[index].images[imageIndex] = {};
                        skeletonNode[index].images[imageIndex].source = $('img', $(this)).attr('src');

                        if(typeof $(this).attr('alt') !== 'undefined') { skeletonNode[index].images[imageIndex].alt = $(this).attr('alt'); }

                        if($('.carousel-caption > h3', $(this)).length === 1) {
                            skeletonNode[index].images[imageIndex].headline = $('.carousel-caption > h3', $(this)).text();
                        }

                        if($('.carousel-caption > span', $(this)).length === 1) {
                            skeletonNode[index].images[imageIndex].description = $('.carousel-caption > span', $(this)).text();
                        }
                    });
                    lastElement = true;
                }

                if($(this).children().length > 0 && ! lastElement) {
                    skeletonNode[index].modules = buildSubSkeleton($(this).children());
                }
            });

            return skeletonNode;
        }

        var skeleton = buildSubSkeleton($('.content.editable'));
        return JSON.stringify(skeleton[0].modules).replace(/\\n/gi, '');
    };

    var initItem = {
        'deleteHandler': function($module) {
            var $module = ! isNumeric($module) ? $module : $(this);

            var delButton = $('<div/>').addClass('btn btn-danger')
                                       .append($('<span/>').addClass('glyphicon glyphicon-trash'))
                                       .on('click', function() {
                                           $(this).closest('.irEditor-wrapper').remove();
                                       });
            var editContainer = $('<aside/>').addClass('editButton')
                                             .append(delButton);

            $module.wrap('<div/>')
                   .closest('div')
                   .addClass('irEditor-wrapper')
                   .append(editContainer);
            return $module.closest('.irEditor-wrapper');
        },
        'textBlock':     function($textBlock) {
            var $textBlock = ! isNumeric($textBlock) ? $textBlock : $(this);

            $textBlock.tinymce({
                theme: "modern",
                plugins: [
                    ["autolink link image lists preview hr anchor"],    //advlist pagebreak charmap
                    ["searchreplace insertdatetime media nonbreaking"], // wordcount visualblocks visualchars fullscreen code
                    ["table contextmenu directionality template paste"] // emoticons
                ],
                menu: {
                    edit:   {title: 'Edit',   items: 'undo redo | cut copy paste | selectall'},
                    view:   {title: 'View',   items: 'visualaid'},
                    format: {title: 'Format', items: 'bold italic underline strikethrough superscript subscript | formats | removeformat'},
                    table:  {title: 'Table',  items: 'inserttable tableprops deletetable cell row column'},
                },
                add_unload_trigger: false,
                schema: "html5",
                inline: true,
                toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
                statusbar: false
            });

            return $textBlock;
        },
        'carousel':      function($carousel) {
            var $carousel = ! isNumeric($carousel) ? $carousel : $(this);

            var createOptionControl = function(label, attrName, defaultValue) {
                return $('<div/>').addClass('form-group')
                                  .append($('<div/>').addClass('col-md-3 control-label')
                                                     .append($('<label/>').text(label))
                                         )
                                  .append($('<div/>').addClass('col-md-9')
                                                     .append($('<input/>').addClass('form-control')
                                                                          .val($carousel.attr(attrName) || defaultValue)
                                                                          .on('input', function() {
                                                                              $carousel.attr(attrName, $(this).val());
                                                                          })
                                                            )
                                         );
            }

            $carousel.before($('<aside/>').addClass('slider-options widget')
                                          .append($('<fieldset/>').append($('<legend/>').text('Optionen'))
                                                                  .append(createOptionControl('Interval', 'data-interval', ''))
                                                                  .append(createOptionControl('Pause',    'data-pause', 'hover'))
                                                                  .append(createOptionControl('Wrap',     'data-wrap', 'true'))
                                                 )
                            );

            $('.item', $carousel).each(function(itemIndex) {
                var $item = $(this);

                var createControl = function(label, value, inputFunction) {
                    return $('<div/>').addClass('form-group')
                                      .append($('<div/>').addClass('col-md-3 control-label')
                                                         .text(label))
                                      .append($('<div/>').addClass('col-md-9')
                                                         .append($('<input/>').addClass('form-control')
                                                                              .val(value)
                                                                              .on('input', inputFunction)
                                                                )
                                             );
                };

                var pathEdit = createControl('Bildpfad', $('img', $item).attr('src') || '', function() {
                                                                                $('img', $item).attr('src', $(this).val());
                                                                                }
                                            );

                var titleEdit = createControl('Titel', $('img', $item).attr('alt') || '', function() {
                                                                                $('img', $item).attr('alt', $(this).val());
                                                                                }
                                             );

                var headlineEdit = createControl('Überschrift', $('.carousel-caption > h3', $item).text(), function() {
                                                                                if($(this).val() !== '') {
                                                                                if($('.carousel-caption > h3', $item).length === 0) {
                                                                                $('.carousel-caption', $item).prepend($('<h3/>'));
                                                                                }
                                                                                $('.carousel-caption > h3', $item).text($(this).val());
                                                                                }
                                                                                else {
                                                                                $('.carousel-caption > h3', $item).remove();
                                                                                }
                                                                                }
                                                   );

                var DescriptionEdit = createControl('Beschreibung', $('.carousel-caption > span', $item).text(), function() {
                                                                                if($(this).val() !== '') {
                                                                                if($('.carousel-caption > span', $item).length === 0) {
                                                                                $('.carousel-caption', $item).append($('<span/>'));
                                                                                }
                                                                                $('.carousel-caption > span', $item).text($(this).val());
                                                                                }
                                                                                else {
                                                                                $('.carousel-caption > span', $item).remove();
                                                                                }
                                                                                }
                                                   );

                var deleteBtn = $('<div/>').addClass('btn btn-danger')
                                           .append($('<i/>').addClass('glyphicon glyphicon-trash'))
                                           .append($('<span/>').text('Slide löschen'))
                                           .on('click', function() {
                                               // circle to the next
                                               $carousel.carousel('cycle');

                                               // remove marker
                                               $('li[data-slide-to]', $carousel).each(function(actIndex) {
                                                   if(actIndex === itemIndex) {
                                                       $(this).remove();
                                                   }
                                                   if(actIndex > itemIndex) {
                                                       $(this).attr('data-slide-to', $(this).attr('data-slide-to')-1);
                                                   }
                                               });

                                               // remove item
                                               $item.remove();
                                           });

                $item.append($('<aside/>').addClass('editControls widget')
                                          .append($('<fieldset/>').append($('<legend/>').text('Optionen des aktuellen Slide'))
                                                                  .append(pathEdit)
                                                                  .append(titleEdit)
                                                                  .append(headlineEdit)
                                                                  .append(DescriptionEdit)
                                                                  .append(deleteBtn)
                                                 )
                            );
            });

            $('aside.editButton', $carousel.closest('.irEditor-wrapper')).prepend($('<div/>').addClass('btn btn-success')
                                                                                .append($('<span/>').addClass('glyphicon glyphicon-plus'))
                                                                                .on('click', function() {
                                                                                console.log('add slider image');
                                                                                })
                                                                                .after(' '));

            return $carousel;
        },
        'heroImage':     function($heroImage) {
            var $heroImage = ! isNumeric($heroImage) ? $heroImage : $(this);

            var createOption = function(label, val, on, updateFunction) {
                return $('<div/>').addClass('form-group')
                                  .append($('<div/>').addClass('col-md-3 control-label')
                                                     .text(label))
                                  .append($('<div/>').addClass('col-md-9')
                                                     .append($('<input/>').addClass('form-control')
                                                                          .val(val)
                                                                          .on(on, updateFunction)
                                                            )
                                         );
            };

            var backgroundImage = createOption('Bildpfad',
                                               $heroImage.css('background-image').replace(/(url\("https*:\/\/(\w+\.*)+|"\))/gi, ''),
                                               'input',
                                               function() {
                                                   $heroImage.css('background-image', "url("+$(this).val()+")")
                                               });

            var content = createOption('Beschreibung',
                                       $('> div', $heroImage).html(),
                                       'change',
                                       null);

            var $container = $('<aside/>').addClass('editControls widget')
                                          .append($('<fieldset/>').append($('<legend/>').text('Optionen'))
                                                                  .append(backgroundImage)
                                                                  .append(content)
                                                 );

            $heroImage.append($container);

            $('input', content).tinymce({
                theme: "modern",
                plugins: [
                    ["autolink link image lists preview hr anchor"],    // advlist pagebreak charmap
                    ["searchreplace insertdatetime media nonbreaking"], // wordcount visualblocks visualchars fullscreen code
                    ["table contextmenu directionality template paste"] // emoticons
                ],
                menu: {
                    edit:   {title: 'Edit',   items: 'undo redo | cut copy paste | selectall'},
                    format: {title: 'Format', items: 'bold italic underline strikethrough superscript subscript | formats | removeformat'},
                },
                add_unload_trigger: false,
                schema: "html5",
                toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link ",
                statusbar: false,
                setup: function(ed) {
                    ed.on("change", function(event) {
                        if($('> div', $heroImage).length === 0) {
                            $heroImage.prepend($('<div/>'));
                        }
                        $('> div', $heroImage).html(ed.getContent());
                    });
                }
            });

            return $heroImage;
        }
    };

    var cleanupItem = {
        'textBlock': function($textBlock) {
            var $textBlock = ! isNumeric($textBlock) ? $textBlock : $(this);

            $textBlock.tinymce().remove();
        },
        'carousel':  function($carousel) {
            var $carousel = ! isNumeric($carousel) ? $carousel : $(this);

            $('.content.editable aside.slider-options').remove();
        },
        'heroImage': function($heroImage) {
            var $heroImage = ! isNumeric($heroImage) ? $heroImage : $(this);

        }
    };

    var initAddHandler = function() {
        $('.irEditor-wrapper', $('.content.editable')).after($('#moduleAddHandler').html());

        $('.addHandler[data-type]').each(function() {
            var $addHandler = $(this);
            $('a[data-module]', $addHandler).on('click', function(e) {
                e.preventDefault();

                var $anchor = $(this);
                var type    = $anchor.closest('.addHandler').attr('data-type');
                var module  = $anchor.attr('data-module');

                var newModule = $($('.contentTemplate[data-type="'+type+'"][data-module="'+module+'"]').html());
                var classes = newModule.attr('class');
                newModule = initItem.deleteHandler(newModule);

                $addHandler.before(newModule);

                var $module = $('.'+classes.replace(/ /gi, '.'), newModule);
                initItem[module]($module);
            });
        });
        $('.row', $('.content.editable')).after($('#rowAddHandler').html());
    };
    var cleanupAddHandler = function() {
        $('.addHandler').remove();
    };

    var setup = function() {
        $('.module', $editor).each(initItem.deleteHandler);

        initAddHandler();

        // modules
        $('.module.textBlock').each(initItem.textBlock);
        $('.module.carousel').each(initItem.carousel);
        $('.module.heroImage').each(initItem.heroImage);
    };
    var cleanup = function() {
        $('.content.editable aside.editButton').remove();
        $('.module', $editor).unwrap();

        cleanupAddHandler();

        // modules
        $('.module.textBlock[id^="mce_"]').each(cleanupItem.textBlock);
        $('.content.editable aside.editControls').remove();
        $('.module.carousel').each(cleanupItem.carousel);
        $('.module.heroImage').each(cleanupItem.heroImage);
    };

    setup();
};<commit_msg>Fixed the removal of slides from the carousel.<commit_after>/**
 * this JavaScript files provites all logic for the page editor
 *
 * @author IcedReaper <Kevin.Lang@gmx.de>
 */
$(function() {
    if($('.content.editable').length === 1) {
        var editor = new irEditor($('.content.editable'));
    }
});

var irEditor = function($editor) {
    var isNumeric = function (n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    };

    $('form#irEditor').on('submit', function() {
        try {
            cleanup();
            $('input[name="content"]').val(buildSkeleton());
            setup();

            return true;
        }
        catch (error) {
            console.log(error);

            return false;
        }
    });

    var buildSkeleton = function() {
        var buildSubSkeleton = function($selector) {
            var skeletonNode = [];

            $selector.each(function(index) {
                skeletonNode[index] = {};
                var lastElement = false;

                if($(this).hasClass('row')) {
                    skeletonNode[index].name = 'row';
                }
                if($(this).attr('class').substring(0, 3) === 'col') {
                    skeletonNode[index].name    = 'col';
                    skeletonNode[index].classes = $(this).attr('class');
                }
                if($(this).hasClass('textBlock')) {
                    skeletonNode[index].name = 'textBlock';
                    skeletonNode[index].text = $(this).html();

                    lastElement = true;
                }
                if($(this).hasClass('heroImage')) {
                    skeletonNode[index].name            = 'heroImage';
                    skeletonNode[index].backgroundImage = $(this).css('background-image').replace(/(url\("https*:\/\/(\w+\.*)+|"\))/gi, '');
                    if($('div', $(this)).length === 1) {
                        skeletonNode[index].content = $('div', $(this)).html();
                    }

                    lastElement = true;
                }
                if($(this).hasClass('carousel')) {
                    skeletonNode[index].name = 'slider';
                    skeletonNode[index].id   = $(this).attr('id');

                    if(typeof $(this).attr('interval') !== 'undefined') { skeletonNode[index].interval = $(this).attr('interval'); }
                    if(typeof $(this).attr('pause')    !== 'undefined') { skeletonNode[index].pause    = $(this).attr('pause'); }
                    if(typeof $(this).attr('wrap')     !== 'undefined') { skeletonNode[index].wrap     = $(this).attr('wrap'); }

                    skeletonNode[index].images = [];
                    $('.carousel-inner > .item', $(this)).each(function(imageIndex) {
                        skeletonNode[index].images[imageIndex] = {};
                        skeletonNode[index].images[imageIndex].source = $('img', $(this)).attr('src');

                        if(typeof $(this).attr('alt') !== 'undefined') { skeletonNode[index].images[imageIndex].alt = $(this).attr('alt'); }

                        if($('.carousel-caption > h3', $(this)).length === 1) {
                            skeletonNode[index].images[imageIndex].headline = $('.carousel-caption > h3', $(this)).text();
                        }

                        if($('.carousel-caption > span', $(this)).length === 1) {
                            skeletonNode[index].images[imageIndex].description = $('.carousel-caption > span', $(this)).text();
                        }
                    });
                    lastElement = true;
                }

                if($(this).children().length > 0 && ! lastElement) {
                    skeletonNode[index].modules = buildSubSkeleton($(this).children());
                }
            });

            return skeletonNode;
        }

        var skeleton = buildSubSkeleton($('.content.editable'));
        return JSON.stringify(skeleton[0].modules).replace(/\\n/gi, '');
    };

    var initItem = {
        'deleteHandler': function($module) {
            var $module = ! isNumeric($module) ? $module : $(this);

            var delButton = $('<div/>').addClass('btn btn-danger')
                                       .append($('<span/>').addClass('glyphicon glyphicon-trash'))
                                       .on('click', function() {
                                           $(this).closest('.irEditor-wrapper').remove();
                                       });
            var editContainer = $('<aside/>').addClass('editButton')
                                             .append(delButton);

            $module.wrap('<div/>')
                   .closest('div')
                   .addClass('irEditor-wrapper')
                   .append(editContainer);
            return $module.closest('.irEditor-wrapper');
        },
        'textBlock':     function($textBlock) {
            var $textBlock = ! isNumeric($textBlock) ? $textBlock : $(this);

            $textBlock.tinymce({
                theme: "modern",
                plugins: [
                    ["autolink link image lists preview hr anchor"],    //advlist pagebreak charmap
                    ["searchreplace insertdatetime media nonbreaking"], // wordcount visualblocks visualchars fullscreen code
                    ["table contextmenu directionality template paste"] // emoticons
                ],
                menu: {
                    edit:   {title: 'Edit',   items: 'undo redo | cut copy paste | selectall'},
                    view:   {title: 'View',   items: 'visualaid'},
                    format: {title: 'Format', items: 'bold italic underline strikethrough superscript subscript | formats | removeformat'},
                    table:  {title: 'Table',  items: 'inserttable tableprops deletetable cell row column'},
                },
                add_unload_trigger: false,
                schema: "html5",
                inline: true,
                toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
                statusbar: false
            });

            return $textBlock;
        },
        'carousel':      function($carousel) {
            var $carousel = ! isNumeric($carousel) ? $carousel : $(this);

            var createOptionControl = function(label, attrName, defaultValue) {
                return $('<div/>').addClass('form-group')
                                  .append($('<div/>').addClass('col-md-3 control-label')
                                                     .append($('<label/>').text(label))
                                         )
                                  .append($('<div/>').addClass('col-md-9')
                                                     .append($('<input/>').addClass('form-control')
                                                                          .val($carousel.attr(attrName) || defaultValue)
                                                                          .on('input', function() {
                                                                              $carousel.attr(attrName, $(this).val());
                                                                          })
                                                            )
                                         );
            }

            $carousel.before($('<aside/>').addClass('slider-options widget')
                                          .append($('<fieldset/>').append($('<legend/>').text('Optionen'))
                                                                  .append(createOptionControl('Interval', 'data-interval', ''))
                                                                  .append(createOptionControl('Pause',    'data-pause', 'hover'))
                                                                  .append(createOptionControl('Wrap',     'data-wrap', 'true'))
                                                 )
                            );

            $('.item', $carousel).each(function(itemIndex) {
                var $item = $(this);

                var createControl = function(label, value, inputFunction) {
                    return $('<div/>').addClass('form-group')
                                      .append($('<div/>').addClass('col-md-3 control-label')
                                                         .text(label))
                                      .append($('<div/>').addClass('col-md-9')
                                                         .append($('<input/>').addClass('form-control')
                                                                              .val(value)
                                                                              .on('input', inputFunction)
                                                                )
                                             );
                };

                var pathEdit = createControl('Bildpfad', $('img', $item).attr('src') || '', function() {
                                                                                $('img', $item).attr('src', $(this).val());
                                                                                }
                                            );

                var titleEdit = createControl('Titel', $('img', $item).attr('alt') || '', function() {
                                                                                $('img', $item).attr('alt', $(this).val());
                                                                                }
                                             );

                var headlineEdit = createControl('Überschrift', $('.carousel-caption > h3', $item).text(), function() {
                                                                                if($(this).val() !== '') {
                                                                                if($('.carousel-caption > h3', $item).length === 0) {
                                                                                $('.carousel-caption', $item).prepend($('<h3/>'));
                                                                                }
                                                                                $('.carousel-caption > h3', $item).text($(this).val());
                                                                                }
                                                                                else {
                                                                                $('.carousel-caption > h3', $item).remove();
                                                                                }
                                                                                }
                                                   );

                var DescriptionEdit = createControl('Beschreibung', $('.carousel-caption > span', $item).text(), function() {
                                                                                if($(this).val() !== '') {
                                                                                if($('.carousel-caption > span', $item).length === 0) {
                                                                                $('.carousel-caption', $item).append($('<span/>'));
                                                                                }
                                                                                $('.carousel-caption > span', $item).text($(this).val());
                                                                                }
                                                                                else {
                                                                                $('.carousel-caption > span', $item).remove();
                                                                                }
                                                                                }
                                                   );

                var deleteBtn = $('<div/>').addClass('btn btn-danger')
                                           .append($('<i/>').addClass('glyphicon glyphicon-trash'))
                                           .append($('<span/>').text('Slide löschen'))
                                           .on('click', function() {
                                               // get the actual index
                                               var itemIndex = function() {
                                                   var itemIndex = 0;
                                                   $('.item', $carousel).each(function(index) {
                                                       if($(this).hasClass('active')) {
                                                           itemIndex = index;
                                                       }
                                                   });

                                                   return itemIndex;
                                               }();

                                               // remove the indicator and activate the correct slide and indicator
                                               if($('li[data-slide-to]:last', $carousel).attr('data-slide-to') != itemIndex) {
                                                   $('li[data-slide-to]:last', $carousel).remove();

                                                   $item.removeClass('active').next('.item').addClass('active');
                                               }
                                               else {
                                                   $('li[data-slide-to]:last', $carousel).remove();
                                                   $('li[data-slide-to]:first', $carousel).addClass('active');

                                                   $('.item', $carousel).removeClass('active').first().addClass('active');
                                               }

                                               // remove item
                                               $item.remove();
                                           });

                $item.append($('<aside/>').addClass('editControls widget')
                                          .append($('<fieldset/>').append($('<legend/>').text('Optionen des aktuellen Slide'))
                                                                  .append(pathEdit)
                                                                  .append(titleEdit)
                                                                  .append(headlineEdit)
                                                                  .append(DescriptionEdit)
                                                                  .append(deleteBtn)
                                                 )
                            );
            });

            $('aside.editButton', $carousel.closest('.irEditor-wrapper')).prepend($('<div/>').addClass('btn btn-success')
                                                                                .append($('<span/>').addClass('glyphicon glyphicon-plus'))
                                                                                .on('click', function() {
                                                                                console.log('add slider image');
                                                                                })
                                                                                .after(' '));

            return $carousel;
        },
        'heroImage':     function($heroImage) {
            var $heroImage = ! isNumeric($heroImage) ? $heroImage : $(this);

            var createOption = function(label, val, on, updateFunction) {
                return $('<div/>').addClass('form-group')
                                  .append($('<div/>').addClass('col-md-3 control-label')
                                                     .text(label))
                                  .append($('<div/>').addClass('col-md-9')
                                                     .append($('<input/>').addClass('form-control')
                                                                          .val(val)
                                                                          .on(on, updateFunction)
                                                            )
                                         );
            };

            var backgroundImage = createOption('Bildpfad',
                                               $heroImage.css('background-image').replace(/(url\("https*:\/\/(\w+\.*)+|"\))/gi, ''),
                                               'input',
                                               function() {
                                                   $heroImage.css('background-image', "url("+$(this).val()+")")
                                               });

            var content = createOption('Beschreibung',
                                       $('> div', $heroImage).html(),
                                       'change',
                                       null);

            var $container = $('<aside/>').addClass('editControls widget')
                                          .append($('<fieldset/>').append($('<legend/>').text('Optionen'))
                                                                  .append(backgroundImage)
                                                                  .append(content)
                                                 );

            $heroImage.append($container);

            $('input', content).tinymce({
                theme: "modern",
                plugins: [
                    ["autolink link image lists preview hr anchor"],    // advlist pagebreak charmap
                    ["searchreplace insertdatetime media nonbreaking"], // wordcount visualblocks visualchars fullscreen code
                    ["table contextmenu directionality template paste"] // emoticons
                ],
                menu: {
                    edit:   {title: 'Edit',   items: 'undo redo | cut copy paste | selectall'},
                    format: {title: 'Format', items: 'bold italic underline strikethrough superscript subscript | formats | removeformat'},
                },
                add_unload_trigger: false,
                schema: "html5",
                toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link ",
                statusbar: false,
                setup: function(ed) {
                    ed.on("change", function(event) {
                        if($('> div', $heroImage).length === 0) {
                            $heroImage.prepend($('<div/>'));
                        }
                        $('> div', $heroImage).html(ed.getContent());
                    });
                }
            });

            return $heroImage;
        }
    };

    var cleanupItem = {
        'textBlock': function($textBlock) {
            var $textBlock = ! isNumeric($textBlock) ? $textBlock : $(this);

            $textBlock.tinymce().remove();
        },
        'carousel':  function($carousel) {
            var $carousel = ! isNumeric($carousel) ? $carousel : $(this);

            $('.content.editable aside.slider-options').remove();
        },
        'heroImage': function($heroImage) {
            var $heroImage = ! isNumeric($heroImage) ? $heroImage : $(this);
        }
    };

    var initAddHandler = function() {
        $('.irEditor-wrapper', $('.content.editable')).after($('#moduleAddHandler').html());

        $('.addHandler[data-type]').each(function() {
            var $addHandler = $(this);
            $('a[data-module]', $addHandler).on('click', function(e) {
                e.preventDefault();

                var $anchor = $(this);
                var type    = $anchor.closest('.addHandler').attr('data-type');
                var module  = $anchor.attr('data-module');

                var newModule = $($('.contentTemplate[data-type="'+type+'"][data-module="'+module+'"]').html());
                var classes = newModule.attr('class');
                newModule = initItem.deleteHandler(newModule);

                $addHandler.before(newModule);

                var $module = $('.'+classes.replace(/ /gi, '.'), newModule);
                initItem[module]($module);
            });
        });
        $('.row', $('.content.editable')).after($('#rowAddHandler').html());
    };
    var cleanupAddHandler = function() {
        $('.addHandler').remove();
    };

    var setup = function() {
        $('.module', $editor).each(initItem.deleteHandler);

        initAddHandler();

        // modules
        $('.module.textBlock').each(initItem.textBlock);
        $('.module.carousel').each(initItem.carousel);
        $('.module.heroImage').each(initItem.heroImage);
    };
    var cleanup = function() {
        $('.content.editable aside.editButton').remove();
        $('.module', $editor).unwrap();

        cleanupAddHandler();

        // modules
        $('.module.textBlock[id^="mce_"]').each(cleanupItem.textBlock);
        $('.content.editable aside.editControls').remove();
        $('.module.carousel').each(cleanupItem.carousel);
        $('.module.heroImage').each(cleanupItem.heroImage);
    };

    setup();
};<|endoftext|>


<commit_before>import angular from "angular";
import inviewModule from "angular-inview";

import won from "../won-es6.js";
import Immutable from "immutable";
import squareImageModule from "./square-image.js";
import labelledHrModule from "./labelled-hr.js";
import connectionMessageStatusModule from "./connection-message-status.js";
import messageContentModule from "./message-content.js";
import referencedMessageContentModule from "./referenced-message-content.js";
import trigModule from "./trig.js";
import { connect2Redux } from "../won-utils.js";
import { attach, get, getIn } from "../utils.js";
import {
  buildProposalMessage,
  buildModificationMessage,
} from "../won-message-utils.js";
import { actionCreators } from "../actions/actions.js";
import { selectNeedByConnectionUri } from "../selectors.js";
import { classOnComponentRoot } from "../cstm-ng-utils.js";

import { ownerBaseUrl } from "config";
import urljoin from "url-join";

const MESSAGE_READ_TIMEOUT = 1500;

const serviceDependencies = ["$ngRedux", "$scope", "$element"];

function genComponentConf() {
  let template = `
        <won-square-image
            title="self.theirNeed.get('title')"
            src="self.theirNeed.get('TODOtitleImgSrc')"
            uri="self.theirNeed.get('uri')"
            ng-click="self.router__stateGoCurrent({postUri: self.theirNeed.get('uri')})"
            ng-if="!self.message.get('outgoingMessage')">
        </won-square-image>

{{ self.message.get("messageStatus").toJS() }}

                  <won-message-content
                      ng-if="!self.isConnectionMessage() || self.message.get('hasContent')"
                      message-uri="self.message.get('uri')"
                      connection-uri="self.connection.get('uri')">
                  </won-message-content>
                  <won-referenced-message-content
                      ng-if="self.message.get('hasReferences')"
                      message-uri="self.message.get('uri')"
                      connection-uri="self.connection.get('uri')">
                  </won-referenced-message-content>

                    <svg ng-show="!self.showDetail">
                        <use xlink:href="#ico16_arrow_down" href="#ico16_arrow_down"></use>
                    </svg>
                    <svg ng-show="self.showDetail">
                        <use xlink:href="#ico16_arrow_up" href="#ico16_arrow_up"></use>
                    </svg>

                <won-trig
                    trig="self.contentGraphTrig"
                    ng-show="self.shouldShowRdf && self.contentGraphTrig">
                </won-trig>

                    <button class="won-button--filled thin black"
                        ng-click="self.sendProposal(); self.showDetail = !self.showDetail">
                        Propose (again)
                    </button>
                    <button class="won-button--filled thin black"
                        ng-click="self.retractMessage(); self.showDetail = !self.showDetail"
                        ng-if="self.message.get('outgoingMessage')">
                        Retract
                    </button>

                    <button class="won-button--filled thin red"
                        ng-if="!self.message.get('outgoingMessage') && !self.isAccepted() && !self.isCancelled() && !self.isCancellationPending()"
                        ng-disabled="self.clicked"
                        ng-click="self.sendAccept()">
                      Accept
                    </button>
                    <button class="won-button--filled thin black"
                        ng-show="!self.message.get('outgoingMessage') && !self.isAccepted() && !self.isCancelled() && !self.isCancellationPending()"
                        ng-disabled="self.clicked"
                        ng-click="self.rejectMessage()">
                      Reject
                    </button>
                    <button class="won-button--filled thin black"
                        ng-if="self.message.get('outgoingMessage') && !self.isAccepted() && !self.isCancelled() && !self.isCancellationPending()"
                        ng-disabled="self.clicked"
                        ng-click="self.retractMessage()">
                      Retract
                    </button>
                    <button class="won-button--filled thin red"
                        ng-if="self.isAccepted() && !self.isCancelled() && !self.isCancellationPending()"
                        ng-disabled="self.clicked"
                        ng-click="self.proposeToCancel()">
                      Propose To Cancel
                    </button>
                    <button class="won-button--filled thin red"
                        ng-if="self.isAccepted() && self.isCancellationPending()"
                        ng-disabled="true">
                      Cancellation Pending...
                    </button>

            <won-connection-message-status message-uri="self.message.get('uri')" connection-uri="self.connection.get('uri')">
            </won-connection-messages-status>

                <svg class="rdflink__small clickable">
                    <use xlink:href="#rdf_logo_2" href="#rdf_logo_2"></use>
                </svg>

    `;

  class Controller {
    constructor(/* arguments = dependency injections */) {
      attach(this, serviceDependencies, arguments);
      this.clicked = false;
      this.showDetail = false;
      this.won = won;

      const selectFromState = state => {
        const ownNeed =
          this.connectionUri &&
          selectNeedByConnectionUri(state, this.connectionUri);
        const connection =
          ownNeed && ownNeed.getIn(["connections", this.connectionUri]);
        const theirNeed =
          connection && state.getIn(["needs", connection.get("remoteNeedUri")]);
        const message =
          connection && this.messageUri
            ? getIn(connection, ["messages", this.messageUri])
            : Immutable.Map();

        const shouldShowRdf = state.get("showRdf");

        let rdfLinkURL;
        if (shouldShowRdf && ownerBaseUrl && ownNeed && message) {
          rdfLinkURL = urljoin(
            ownerBaseUrl,
            "/rest/linked-data/",
            `?requester=${this.encodeParam(ownNeed.get("uri"))}`,
            `&uri=${this.encodeParam(message.get("uri"))}`,
            message.get("outgoingMessage") ? "&deep=true" : ""
          );
        }

        return {
          ownNeed,
          theirNeed,
          connection,
          message,
          contentGraphs: get(message, "contentGraphs") || Immutable.List(),
          contentGraphTrig: get(message, "contentGraphTrigRaw"),
          shouldShowRdf,
          rdfLinkURL,
          allowProposals:
            connection &&
            connection.get("state") === won.WON.Connected &&
            !message.get("hasReferences") &&
            message.get("hasContent"), //allow showing details only when the connection is already present
        };
      };

      connect2Redux(
        selectFromState,
        actionCreators,
        ["self.connectionUri", "self.messageUri"],
        this
      );

      classOnComponentRoot(
        "won-cm--left",
        () => this.isReceivedMessage(),
        this
      );
      classOnComponentRoot(
        "won-cm--right",
        () => this.isOutgoingMessage(),
        this
      );
      classOnComponentRoot(
        "won-cm--system",
        () => this.isSystemMessage(),
        this
      );
      classOnComponentRoot("won-is-rejected", () => this.isRejected(), this);
      classOnComponentRoot("won-is-retracted", () => this.isRetracted(), this);
      classOnComponentRoot("won-is-accepted", () => this.isAccepted(), this);
      classOnComponentRoot("won-is-cancelled", () => this.isCancelled(), this);
      classOnComponentRoot(
        "won-is-cancellationPending",
        () => this.isCancellationPending(),
        this
      );
      classOnComponentRoot("won-unread", () => this.isUnread(), this);
    }

    isReceivedMessage() {
      return this.message && !this.message.get("outgoingMessage");
    }

    isSystemMessage() {
      //TODO: IMPLEMENT THIS METHOD
      return false;
    }

    isOutgoingMessage() {
      return this.message && this.message.get("outgoingMessage");
    }

    isUnread() {
      return this.message && this.message.get("unread");
    }

    isRejected() {
      const messageStatus = this.message && this.message.get("messageStatus");
      return messageStatus && messageStatus.get("isRejected");
    }
    isAccepted() {
      const messageStatus = this.message && this.message.get("messageStatus");
      return messageStatus && messageStatus.get("isAccepted");
    }
    isRetracted() {
      const messageStatus = this.message && this.message.get("messageStatus");
      return messageStatus && messageStatus.get("isRetracted");
    }
    isCancelled() {
      const messageStatus = this.message && this.message.get("messageStatus");
      return messageStatus && messageStatus.get("isCancelled");
    }
    isCancellationPending() {
      const messageStatus = this.message && this.message.get("messageStatus");
      return messageStatus && messageStatus.get("isCancellationPending");
    }

    hasProposesReferences() {
      const references = this.message && this.message.get("references");
      return (
        references &&
        references.get("proposes") &&
        references.get("proposes").size > 0
      );
    }
    hasProposesToCancelReferences() {
      const references = this.message && this.message.get("references");
      return (
        references &&
        references.get("proposesToCancel") &&
        references.get("proposesToCancel").size > 0
      );
    }

    markAsRead() {
      if (this.message && this.message.get("unread")) {
        const payload = {
          messageUri: this.message.get("uri"),
          connectionUri: this.connectionUri,
          needUri: this.ownNeed.get("uri"),
        };

        const tmp_messages__markAsRead = this.messages__markAsRead;

        setTimeout(function() {
          tmp_messages__markAsRead(payload);
        }, MESSAGE_READ_TIMEOUT);
      }
    }

    markAsAccepted(accepted) {
      const payload = {
        messageUri: this.message.get("uri"),
        connectionUri: this.connectionUri,
        needUri: this.ownNeed.get("uri"),
        accepted: accepted,
      };

      this.messages__messageStatus__markAsAccepted(payload);
    }

    markAsRejected(rejected) {
      const payload = {
        messageUri: this.message.get("uri"),
        connectionUri: this.connectionUri,
        needUri: this.ownNeed.get("uri"),
        rejected: rejected,
      };

      this.messages__messageStatus__markAsRejected(payload);
    }

    markAsRetracted(retracted) {
      const payload = {
        messageUri: this.message.get("uri"),
        connectionUri: this.connectionUri,
        needUri: this.ownNeed.get("uri"),
        retracted: retracted,
      };

      this.messages__messageStatus__markAsRetracted(payload);
    }

    markAsCancelled(cancelled) {
      const payload = {
        messageUri: this.message.get("uri"),
        connectionUri: this.connectionUri,
        needUri: this.ownNeed.get("uri"),
        cancelled: cancelled,
      };

      this.messages__messageStatus__markAsCancelled(payload);
    }

    markAsCancellationPending(cancellationPending) {
      const payload = {
        messageUri: this.message.get("uri"),
        connectionUri: this.connectionUri,
        needUri: this.ownNeed.get("uri"),
        cancellationPending: cancellationPending,
      };

      this.messages__messageStatus__markAsCancellationPending(payload);
    }

    sendProposal() {
      this.clicked = true;
      const uri = this.message.get("remoteUri")
        ? this.message.get("remoteUri")
        : this.message.get("uri");
      const trimmedMsg = buildProposalMessage(
        uri,
        "proposes",
        "Ok, I am hereby making a proposal"
      );
      this.connections__sendChatMessage(trimmedMsg, this.connectionUri, true);

      this.onSendProposal({ proposalUri: uri });
    }

    proposeToCancel() {
      this.clicked = true;
      const uri = this.isOwn
        ? this.message.get("uri")
        : this.message.get("remoteUri");
      const msg = "Propose to cancel agreement : " + uri;
      const trimmedMsg = buildProposalMessage(uri, "proposesToCancel", msg);
      this.connections__sendChatMessage(trimmedMsg, this.connectionUri, true);

      this.onUpdate();
    }

    sendAccept() {
      this.clicked = true;
      const trimmedMsg = buildProposalMessage(
        this.message.get("remoteUri"),
        "accepts",
        "I accept the following proposition"
      );
      this.connections__sendChatMessage(trimmedMsg, this.connectionUri, true);

      this.markAsAccepted(true);
      this.onRemoveData({ proposalUri: this.messageUri });
    }

    retractMessage() {
      this.clicked = true;
      const uri = this.message.get("remoteUri")
        ? this.message.get("remoteUri")
        : this.message.get("uri");
      const trimmedMsg = buildModificationMessage(
        uri,
        "retracts",
        "Retracting the message"
      );
      this.connections__sendChatMessage(trimmedMsg, this.connectionUri, true);

      this.markAsRetracted(true);
      this.onUpdate();
    }

    rejectMessage() {
      this.clicked = true;
      const uri = this.message.get("remoteUri")
        ? this.message.get("remoteUri")
        : this.message.get("uri");
      const trimmedMsg = buildProposalMessage(
        uri,
        "rejects",
        "Rejecting the message"
      );
      this.connections__sendChatMessage(trimmedMsg, this.connectionUri, true);

      this.markAsRejected(true);
      this.onUpdate();
    }

    rdfToString(jsonld) {
      return JSON.stringify(jsonld);
    }

    /**
     * determines if the sent message is not received by any of the servers yet but not failed either
     */
    isPending() {
      return (
        this.message.get("outgoingMessage") &&
        !this.message.get("failedToSend") &&
        !this.message.get("isReceivedByOwn") &&
        !this.message.get("isReceivedByRemote")
      );
    }

    /**
     * determines if the sent message is received by any of the servers yet but not failed either
     */
    isPartiallyLoaded() {
      return (
        this.message.get("outgoingMessage") &&
        !this.message.get("failedToSend") &&
        (!(
          this.message.get("isReceivedByOwn") &&
          this.message.get("isReceivedByRemote")
        ) &&
          (this.message.get("isReceivedByOwn") ||
            this.message.get("isReceivedByRemote")))
      );
    }

    encodeParam(param) {
      return encodeURIComponent(param);
    }

    isConnectionMessage() {
      return this.message.get("messageType") === won.WONMSG.connectionMessage;
    }
  }
  Controller.$inject = serviceDependencies;

  return {
    restrict: "E",
    controller: Controller,
    controllerAs: "self",
    bindToController: true, //scope-bindings -> ctrl
    scope: {
      messageUri: "=",
      connectionUri: "=",
      // Usage: on-update="::myCallback(draft)"
      onUpdate: "&",
      onSendProposal: "&",
      onRemoveData: "&",
    },
    template: template,
  };
}

export default angular
  .module("won.owner.components.connectionMessage", [
    squareImageModule,
    labelledHrModule,
    connectionMessageStatusModule,
    messageContentModule,
    referencedMessageContentModule,
    inviewModule.name,
    trigModule,
  ])
  .directive("wonConnectionMessage", genComponentConf).name;
<commit_msg>add isRetracted to reject and retract button ng-if<commit_after>import angular from "angular";
import inviewModule from "angular-inview";

import won from "../won-es6.js";
import Immutable from "immutable";
import squareImageModule from "./square-image.js";
import labelledHrModule from "./labelled-hr.js";
import connectionMessageStatusModule from "./connection-message-status.js";
import messageContentModule from "./message-content.js";
import referencedMessageContentModule from "./referenced-message-content.js";
import trigModule from "./trig.js";
import { connect2Redux } from "../won-utils.js";
import { attach, get, getIn } from "../utils.js";
import {
  buildProposalMessage,
  buildModificationMessage,
} from "../won-message-utils.js";
import { actionCreators } from "../actions/actions.js";
import { selectNeedByConnectionUri } from "../selectors.js";
import { classOnComponentRoot } from "../cstm-ng-utils.js";

import { ownerBaseUrl } from "config";
import urljoin from "url-join";

const MESSAGE_READ_TIMEOUT = 1500;

const serviceDependencies = ["$ngRedux", "$scope", "$element"];

function genComponentConf() {
  let template = `
        <won-square-image
            title="self.theirNeed.get('title')"
            src="self.theirNeed.get('TODOtitleImgSrc')"
            uri="self.theirNeed.get('uri')"
            ng-click="self.router__stateGoCurrent({postUri: self.theirNeed.get('uri')})"
            ng-if="!self.message.get('outgoingMessage')">
        </won-square-image>

{{ self.message.get("messageStatus").toJS() }}

                  <won-message-content
                      ng-if="!self.isConnectionMessage() || self.message.get('hasContent')"
                      message-uri="self.message.get('uri')"
                      connection-uri="self.connection.get('uri')">
                  </won-message-content>
                  <won-referenced-message-content
                      ng-if="self.message.get('hasReferences')"
                      message-uri="self.message.get('uri')"
                      connection-uri="self.connection.get('uri')">
                  </won-referenced-message-content>

                    <svg ng-show="!self.showDetail">
                        <use xlink:href="#ico16_arrow_down" href="#ico16_arrow_down"></use>
                    </svg>
                    <svg ng-show="self.showDetail">
                        <use xlink:href="#ico16_arrow_up" href="#ico16_arrow_up"></use>
                    </svg>

                <won-trig
                    trig="self.contentGraphTrig"
                    ng-show="self.shouldShowRdf && self.contentGraphTrig">
                </won-trig>

                    <button class="won-button--filled thin black"
                        ng-click="self.sendProposal(); self.showDetail = !self.showDetail">
                        Propose (again)
                    </button>
                    <button class="won-button--filled thin black"
                        ng-click="self.retractMessage(); self.showDetail = !self.showDetail"
                        ng-if="self.message.get('outgoingMessage')">
                        Retract
                    </button>

                    <button class="won-button--filled thin red"
                        ng-if="!self.message.get('outgoingMessage') && !self.isAccepted() && !self.isCancelled() && !self.isCancellationPending()"
                        ng-disabled="self.clicked"
                        ng-click="self.sendAccept()">
                      Accept
                    </button>
                    <button class="won-button--filled thin black"
                        ng-show="!self.message.get('outgoingMessage') && !self.isAccepted() && !self.isCancelled() && !self.isCancellationPending() && !self.isRetracted()"
                        ng-disabled="self.clicked"
                        ng-click="self.rejectMessage()">
                      Reject
                    </button>
                    <button class="won-button--filled thin black"
                        ng-if="self.message.get('outgoingMessage') && !self.isAccepted() && !self.isCancelled() && !self.isCancellationPending() && !self.isRetracted()"
                        ng-disabled="self.clicked"
                        ng-click="self.retractMessage()">
                      Retract
                    </button>
                    <button class="won-button--filled thin red"
                        ng-if="self.isAccepted() && !self.isCancelled() && !self.isCancellationPending()"
                        ng-disabled="self.clicked"
                        ng-click="self.proposeToCancel()">
                      Propose To Cancel
                    </button>
                    <button class="won-button--filled thin red"
                        ng-if="self.isAccepted() && self.isCancellationPending()"
                        ng-disabled="true">
                      Cancellation Pending...
                    </button>

            <won-connection-message-status message-uri="self.message.get('uri')" connection-uri="self.connection.get('uri')">
            </won-connection-messages-status>

                <svg class="rdflink__small clickable">
                    <use xlink:href="#rdf_logo_2" href="#rdf_logo_2"></use>
                </svg>

    `;

  class Controller {
    constructor(/* arguments = dependency injections */) {
      attach(this, serviceDependencies, arguments);
      this.clicked = false;
      this.showDetail = false;
      this.won = won;

      const selectFromState = state => {
        const ownNeed =
          this.connectionUri &&
          selectNeedByConnectionUri(state, this.connectionUri);
        const connection =
          ownNeed && ownNeed.getIn(["connections", this.connectionUri]);
        const theirNeed =
          connection && state.getIn(["needs", connection.get("remoteNeedUri")]);
        const message =
          connection && this.messageUri
            ? getIn(connection, ["messages", this.messageUri])
            : Immutable.Map();

        const shouldShowRdf = state.get("showRdf");

        let rdfLinkURL;
        if (shouldShowRdf && ownerBaseUrl && ownNeed && message) {
          rdfLinkURL = urljoin(
            ownerBaseUrl,
            "/rest/linked-data/",
            `?requester=${this.encodeParam(ownNeed.get("uri"))}`,
            `&uri=${this.encodeParam(message.get("uri"))}`,
            message.get("outgoingMessage") ? "&deep=true" : ""
          );
        }

        return {
          ownNeed,
          theirNeed,
          connection,
          message,
          contentGraphs: get(message, "contentGraphs") || Immutable.List(),
          contentGraphTrig: get(message, "contentGraphTrigRaw"),
          shouldShowRdf,
          rdfLinkURL,
          allowProposals:
            connection &&
            connection.get("state") === won.WON.Connected &&
            !message.get("hasReferences") &&
            message.get("hasContent"), //allow showing details only when the connection is already present
        };
      };

      connect2Redux(
        selectFromState,
        actionCreators,
        ["self.connectionUri", "self.messageUri"],
        this
      );

      classOnComponentRoot(
        "won-cm--left",
        () => this.isReceivedMessage(),
        this
      );
      classOnComponentRoot(
        "won-cm--right",
        () => this.isOutgoingMessage(),
        this
      );
      classOnComponentRoot(
        "won-cm--system",
        () => this.isSystemMessage(),
        this
      );
      classOnComponentRoot("won-is-rejected", () => this.isRejected(), this);
      classOnComponentRoot("won-is-retracted", () => this.isRetracted(), this);
      classOnComponentRoot("won-is-accepted", () => this.isAccepted(), this);
      classOnComponentRoot("won-is-cancelled", () => this.isCancelled(), this);
      classOnComponentRoot(
        "won-is-cancellationPending",
        () => this.isCancellationPending(),
        this
      );
      classOnComponentRoot("won-unread", () => this.isUnread(), this);
    }

    isReceivedMessage() {
      return this.message && !this.message.get("outgoingMessage");
    }

    isSystemMessage() {
      //TODO: IMPLEMENT THIS METHOD
      return false;
    }

    isOutgoingMessage() {
      return this.message && this.message.get("outgoingMessage");
    }

    isUnread() {
      return this.message && this.message.get("unread");
    }

    isRejected() {
      const messageStatus = this.message && this.message.get("messageStatus");
      return messageStatus && messageStatus.get("isRejected");
    }
    isAccepted() {
      const messageStatus = this.message && this.message.get("messageStatus");
      return messageStatus && messageStatus.get("isAccepted");
    }
    isRetracted() {
      const messageStatus = this.message && this.message.get("messageStatus");
      return messageStatus && messageStatus.get("isRetracted");
    }
    isCancelled() {
      const messageStatus = this.message && this.message.get("messageStatus");
      return messageStatus && messageStatus.get("isCancelled");
    }
    isCancellationPending() {
      const messageStatus = this.message && this.message.get("messageStatus");
      return messageStatus && messageStatus.get("isCancellationPending");
    }

    hasProposesReferences() {
      const references = this.message && this.message.get("references");
      return (
        references &&
        references.get("proposes") &&
        references.get("proposes").size > 0
      );
    }
    hasProposesToCancelReferences() {
      const references = this.message && this.message.get("references");
      return (
        references &&
        references.get("proposesToCancel") &&
        references.get("proposesToCancel").size > 0
      );
    }

    markAsRead() {
      if (this.message && this.message.get("unread")) {
        const payload = {
          messageUri: this.message.get("uri"),
          connectionUri: this.connectionUri,
          needUri: this.ownNeed.get("uri"),
        };

        const tmp_messages__markAsRead = this.messages__markAsRead;

        setTimeout(function() {
          tmp_messages__markAsRead(payload);
        }, MESSAGE_READ_TIMEOUT);
      }
    }

    markAsAccepted(accepted) {
      const payload = {
        messageUri: this.message.get("uri"),
        connectionUri: this.connectionUri,
        needUri: this.ownNeed.get("uri"),
        accepted: accepted,
      };

      this.messages__messageStatus__markAsAccepted(payload);
    }

    markAsRejected(rejected) {
      const payload = {
        messageUri: this.message.get("uri"),
        connectionUri: this.connectionUri,
        needUri: this.ownNeed.get("uri"),
        rejected: rejected,
      };

      this.messages__messageStatus__markAsRejected(payload);
    }

    markAsRetracted(retracted) {
      const payload = {
        messageUri: this.message.get("uri"),
        connectionUri: this.connectionUri,
        needUri: this.ownNeed.get("uri"),
        retracted: retracted,
      };

      this.messages__messageStatus__markAsRetracted(payload);
    }

    markAsCancelled(cancelled) {
      const payload = {
        messageUri: this.message.get("uri"),
        connectionUri: this.connectionUri,
        needUri: this.ownNeed.get("uri"),
        cancelled: cancelled,
      };

      this.messages__messageStatus__markAsCancelled(payload);
    }

    markAsCancellationPending(cancellationPending) {
      const payload = {
        messageUri: this.message.get("uri"),
        connectionUri: this.connectionUri,
        needUri: this.ownNeed.get("uri"),
        cancellationPending: cancellationPending,
      };

      this.messages__messageStatus__markAsCancellationPending(payload);
    }

    sendProposal() {
      this.clicked = true;
      const uri = this.message.get("remoteUri")
        ? this.message.get("remoteUri")
        : this.message.get("uri");
      const trimmedMsg = buildProposalMessage(
        uri,
        "proposes",
        "Ok, I am hereby making a proposal"
      );
      this.connections__sendChatMessage(trimmedMsg, this.connectionUri, true);

      this.onSendProposal({ proposalUri: uri });
    }

    proposeToCancel() {
      this.clicked = true;
      const uri = this.isOwn
        ? this.message.get("uri")
        : this.message.get("remoteUri");
      const msg = "Propose to cancel agreement : " + uri;
      const trimmedMsg = buildProposalMessage(uri, "proposesToCancel", msg);
      this.connections__sendChatMessage(trimmedMsg, this.connectionUri, true);

      this.onUpdate();
    }

    sendAccept() {
      this.clicked = true;
      const trimmedMsg = buildProposalMessage(
        this.message.get("remoteUri"),
        "accepts",
        "I accept the following proposition"
      );
      this.connections__sendChatMessage(trimmedMsg, this.connectionUri, true);

      this.markAsAccepted(true);
      this.onRemoveData({ proposalUri: this.messageUri });
    }

    retractMessage() {
      this.clicked = true;
      const uri = this.message.get("remoteUri")
        ? this.message.get("remoteUri")
        : this.message.get("uri");
      const trimmedMsg = buildModificationMessage(
        uri,
        "retracts",
        "Retracting the message"
      );
      this.connections__sendChatMessage(trimmedMsg, this.connectionUri, true);

      this.markAsRetracted(true);
      this.onUpdate();
    }

    rejectMessage() {
      this.clicked = true;
      const uri = this.message.get("remoteUri")
        ? this.message.get("remoteUri")
        : this.message.get("uri");
      const trimmedMsg = buildProposalMessage(
        uri,
        "rejects",
        "Rejecting the message"
      );
      this.connections__sendChatMessage(trimmedMsg, this.connectionUri, true);

      this.markAsRejected(true);
      this.onUpdate();
    }

    rdfToString(jsonld) {
      return JSON.stringify(jsonld);
    }

    /**
     * determines if the sent message is not received by any of the servers yet but not failed either
     */
    isPending() {
      return (
        this.message.get("outgoingMessage") &&
        !this.message.get("failedToSend") &&
        !this.message.get("isReceivedByOwn") &&
        !this.message.get("isReceivedByRemote")
      );
    }

    /**
     * determines if the sent message is received by any of the servers yet but not failed either
     */
    isPartiallyLoaded() {
      return (
        this.message.get("outgoingMessage") &&
        !this.message.get("failedToSend") &&
        (!(
          this.message.get("isReceivedByOwn") &&
          this.message.get("isReceivedByRemote")
        ) &&
          (this.message.get("isReceivedByOwn") ||
            this.message.get("isReceivedByRemote")))
      );
    }

    encodeParam(param) {
      return encodeURIComponent(param);
    }

    isConnectionMessage() {
      return this.message.get("messageType") === won.WONMSG.connectionMessage;
    }
  }
  Controller.$inject = serviceDependencies;

  return {
    restrict: "E",
    controller: Controller,
    controllerAs: "self",
    bindToController: true, //scope-bindings -> ctrl
    scope: {
      messageUri: "=",
      connectionUri: "=",
      // Usage: on-update="::myCallback(draft)"
      onUpdate: "&",
      onSendProposal: "&",
      onRemoveData: "&",
    },
    template: template,
  };
}

export default angular
  .module("won.owner.components.connectionMessage", [
    squareImageModule,
    labelledHrModule,
    connectionMessageStatusModule,
    messageContentModule,
    referencedMessageContentModule,
    inviewModule.name,
    trigModule,
  ])
  .directive("wonConnectionMessage", genComponentConf).name;
<|endoftext|>


<commit_before>/* Project specific Javascript goes here. */

/*
Formatting hack to get around crispy-forms unfortunate hardcoding
in helpers.FormHelper:

    if template_pack == 'bootstrap4':
        grid_colum_matcher = re.compile('\w*col-(xs|sm|md|lg|xl)-\d+\w*')
        using_grid_layout = (grid_colum_matcher.match(self.label_class) or
                             grid_colum_matcher.match(self.field_class))
        if using_grid_layout:
            items['using_grid_layout'] = True

Issues with the above approach:

1. Fragile: Assumes Bootstrap 4's API doesn't change (it does)
2. Unforgiving: Doesn't allow for any variation in template design
3. Really Unforgiving: No way to override this behavior
4. Undocumented: No mention in the documentation, or it's too hard for me to find
*/
$('.form-group').removeClass('row');
<commit_msg>Update project.js<commit_after>/* Project specific Javascript goes here. */

/*
Formatting hack to get around crispy-forms unfortunate hardcoding
in helpers.FormHelper:

    if template_pack == 'bootstrap4':
        grid_colum_matcher = re.compile('\w*col-(xs|sm|md|lg|xl)-\d+\w*')
        using_grid_layout = (grid_colum_matcher.match(self.label_class) or
                             grid_colum_matcher.match(self.field_class))
        if using_grid_layout:
            items['using_grid_layout'] = True

Issues with the above approach:

1. Fragile: Assumes Bootstrap 4's API doesn't change (it does)
2. Unforgiving: Doesn't allow for any variation in template design
3. Really Unforgiving: No way to override this behavior
4. Undocumented: No mention in the documentation, or it's too hard for me to find
*/
<|endoftext|>


<commit_before>({
    /**
     * Verify the correct AppCache events are received and are in the right order, without an error event.
     */
    testAppCacheEvents : {
        browsers : [ "-FIREFOX" ],
        test : [
                function waitForAppCacheToPopulate(component) {
                    var that = this;
                    $A.test.addWaitForWithFailureMessage(
                            true,
                            function() {
                                return that.getIndexOfAppCacheEvent("cached") > -1;
                            },
                            "appcache cached event was never received, events: " + JSON.stringify(window.appCacheEvents));
                },
                function verifyAppCacheEvents(component) {
                    $A.test.assertEquals(-1, this.getIndexOfAppCacheEvent("error"), "Test prerequisite failed: appcache must not be populated when the test is started; received noupdate appcache event indicating it was");
                    $A.test.assertEquals(-1, this.getIndexOfAppCacheEvent("error"), "appcache error event received, events: " + JSON.stringify(window.appCacheEvents));

                    var checking = this.getIndexOfAppCacheEvent("checking");
                    var downloading = this.getIndexOfAppCacheEvent("downloading");
                    var cached = this.getIndexOfAppCacheEvent("cached");

                    $A.test.assertNotEquals(-1, checking, "appcache checking event not received, events: " + JSON.stringify(window.appCacheEvents));
                    $A.test.assertNotEquals(-1, downloading, "appcache downloading event not received, events: " + JSON.stringify(window.appCacheEvents));
                    $A.test.assertNotEquals(-1, cached, "appcache cached event not received, events: " + JSON.stringify(window.appCacheEvents));
                    $A.test.assertTrue(checking < downloading, "appcache checking event not received before downloading, events: " + JSON.stringify(window.appCacheEvents));
                    $A.test.assertTrue(downloading < cached, "appcache checking event not received before downloading, events: " + JSON.stringify(window.appCacheEvents));
                    $A.test.assertEquals(window.appCacheEvents.length -1, cached, "appcache cached event not the last received event, events: " + JSON.stringify(window.appCacheEvents));

                } ]
    },

    /**
     * Gets the index of the specified appcache event type.
     * @param {String} type the type of appcache event to find.
     * @return {Number} index of the event, -1 if not found.
     */
    getIndexOfAppCacheEvent: function(type) {
        for (var i = 0; i < window.appCacheEvents.length; i++) {
            if (window.appCacheEvents[i].type === type) {
                return i;
            }
        }
        return -1;
    }
})
<commit_msg>Disable appcache sequence test in IE @bug W-34543122@ @rev chengi.li@<commit_after>({
    /**
     * Verify the correct AppCache events are received and are in the right order, without an error event.
     */
    testAppCacheEvents : {
        browsers : [ "-FIREFOX", "-IE7","-IE8","-IE9", "-IE10", "-IE11" ],
        test : [
                function waitForAppCacheToPopulate(component) {
                    var that = this;
                    $A.test.addWaitForWithFailureMessage(
                            true,
                            function() {
                                return that.getIndexOfAppCacheEvent("cached") > -1;
                            },
                            "appcache cached event was never received, events: " + JSON.stringify(window.appCacheEvents));
                },
                function verifyAppCacheEvents(component) {
                    $A.test.assertEquals(-1, this.getIndexOfAppCacheEvent("error"), "Test prerequisite failed: appcache must not be populated when the test is started; received noupdate appcache event indicating it was");
                    $A.test.assertEquals(-1, this.getIndexOfAppCacheEvent("error"), "appcache error event received, events: " + JSON.stringify(window.appCacheEvents));

                    // note: spec says checking will be fired but it's observed most browsers do not fire it
                    var downloading = this.getIndexOfAppCacheEvent("downloading");
                    var cached = this.getIndexOfAppCacheEvent("cached");

                    $A.test.assertNotEquals(-1, downloading, "appcache downloading event not received, events: " + JSON.stringify(window.appCacheEvents));
                    $A.test.assertNotEquals(-1, cached, "appcache cached event not received, events: " + JSON.stringify(window.appCacheEvents));
                    $A.test.assertTrue(downloading < cached, "appcache checking event not received before downloading, events: " + JSON.stringify(window.appCacheEvents));
                    $A.test.assertEquals(window.appCacheEvents.length -1, cached, "appcache cached event not the last received event, events: " + JSON.stringify(window.appCacheEvents));

                } ]
    },

    /**
     * Gets the index of the specified appcache event type.
     * @param {String} type the type of appcache event to find.
     * @return {Number} index of the event, -1 if not found.
     */
    getIndexOfAppCacheEvent: function(type) {
        for (var i = 0; i < window.appCacheEvents.length; i++) {
            if (window.appCacheEvents[i].type === type) {
                return i;
            }
        }
        return -1;
    }
})
<|endoftext|>