From 6570ead1d309b4a0757d827f39b35d502b970755 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Fri, 5 Jun 2015 16:19:36 +0200 Subject: [PATCH] Init version of highlighter. --- .../app/controllers/ticket_zoom.js.coffee | 20 ++- .../ticket_zoom/article_view.js.coffee | 91 ++++++++----- .../ticket_zoom/higlighter.js.coffee | 124 +++++++++++------- .../app/models/ticket_article.js.coffee | 2 +- .../app/views/layout_ref/highlight.jst.eco | 2 +- .../views/ticket_zoom/article_view.jst.eco | 2 +- .../app/views/ticket_zoom/highlighter.jst.eco | 4 +- app/models/ticket/article.rb | 3 + ...5204_add_preferences_to_ticket_articles.rb | 5 + 9 files changed, 168 insertions(+), 85 deletions(-) create mode 100644 db/migrate/20150604215204_add_preferences_to_ticket_articles.rb diff --git a/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee b/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee index becd1977f..78c9c4614 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee @@ -91,7 +91,11 @@ class App.TicketZoom extends App.Controller return if @activeState @activeState = true - App.Event.trigger('ui::ticket::shown', { ticket_id: @ticket.id } ) + App.Event.trigger('ui::ticket::shown', { ticket_id: @ticket_id } ) + + if !@highlighed + @highlighed = true + @highligher.loadHighlights() App.OnlineNotification.seen( 'Ticket', @ticket_id ) @navupdate '#' @@ -292,10 +296,15 @@ class App.TicketZoom extends App.Controller ui: @ ) + @highligher = new App.TicketZoomHighlighter( + el: @$('.highlighter') + ticket_id: @ticket.id + ) + # rerender whole sidebar if customer or organization has changed if @ticketLastAttributes.customer_id isnt @ticket.customer_id || @ticketLastAttributes.organization_id isnt @ticket.organization_id new App.WidgetAvatar( - el: @$('.page-header .js-avatar') + el: @$('.ticketZoom-header .js-avatar') user_id: @ticket.customer_id size: 50 ) @@ -313,9 +322,10 @@ class App.TicketZoom extends App.Controller # show article if !@article_view @article_view = new App.TicketZoomArticleView( - ticket: @ticket - el: @el.find('.ticket-article') - ui: @ + ticket: @ticket + el: @el.find('.ticket-article') + ui: @ + highlighter: @highlighter ) @article_view.execute( diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/article_view.js.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/article_view.js.coffee index 0244fa6a5..52ef8a755 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/article_view.js.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_view.js.coffee @@ -5,6 +5,7 @@ class App.TicketZoomArticleView extends App.Controller @article_controller = {} execute: (params) -> + all = [] for ticket_article_id in params.ticket_article_ids if !@article_controller[ticket_article_id] el = $('
') @@ -14,14 +15,21 @@ class App.TicketZoomArticleView extends App.Controller el: el ui: @ui ) - @el.append( el ) + all.push el + @el.append( all ) class ArticleViewItem extends App.Controller + hasChangedAttributes: ['from', 'to', 'cc', 'subject', 'body', 'internal'] + + elements: + '.textBubble-content': 'textBubbleContent' + '.textBubble-overflowContainer': 'textBubbleOverflowContainer' + events: - 'click .show_toogle': 'show_toogle' - 'click .textBubble': 'toggle_meta_with_delay' - 'click .textBubble a': 'stopPropagation' - 'click .js-unfold': 'unfold' + 'click .show_toogle': 'show_toogle' + 'click .textBubble': 'toggle_meta_with_delay' + 'click .textBubble a': 'stopPropagation' + 'click .js-unfold': 'unfold' constructor: -> super @@ -34,21 +42,43 @@ class ArticleViewItem extends App.Controller @bind( 'ui::ticket::shown' (data) => - if data.ticket_id is @ticket.id + if data.ticket_id.toString() is @ticket.id.toString() @setSeeMore() ) # subscribe to changes - @subscribeId = App.TicketArticle.full( @ticket_article_id, @render, false, true ) + @subscribeId = App.TicketArticle.full(@ticket_article_id, @render, false, true) release: => - App.User.TicketArticle(@subscribeId) + App.TicketArticle.unsubscribe(@subscribeId) + + hasChanged: (article) => + + # if no last article exists, remember it and return true + if !@article_last_updated + @article_last_updated = {} + for item in @hasChangedAttributes + @article_last_updated[item] = article[item] + return true + + # compare last and current article attributes + article_last_updated_check = {} + for item in @hasChangedAttributes + article_last_updated_check[item] = article[item] + diff = difference(@article_last_updated, article_last_updated_check) + return false if !diff || _.isEmpty( diff ) + @article_last_updated = article_last_updated_check + true render: (article) => # get articles @article = App.TicketArticle.fullLocal( @ticket_article_id ) + # check if rerender is needed + return if !@hasChanged(@article) + + console.log('RERENDER', @ticket_article_id) # prepare html body if @article.content_type is 'text/html' @article['html'] = @article.body @@ -82,34 +112,35 @@ class ArticleViewItem extends App.Controller # set see more options setSeeMore: => - maxHeight = 560 - bubble = @$('.textBubble-content') + maxHeight = 560 + bubbleContent = @textBubbleContent + bubbleOvervlowContainer = @textBubbleOverflowContainer # expand if see more is already clicked if @seeMore - bubble.css('height', 'auto') - bubble.parent().find('.textBubble-overflowContainer').addClass('hide') + bubbleContent.css('height', 'auto') + bubbleOvervlowContainer.addClass('hide') return # reset bubble heigth and "see more" opacity - bubble.css('height', '') - bubble.parent().find('.textBubble-overflowContainer').css('opacity', '') + bubbleContent.css('height', '') + bubbleOvervlowContainer.css('opacity', '') # remember offset of "see more" - offsetTop = bubble.find('.js-signatureMarker').position() + offsetTop = bubbleContent.find('.js-signatureMarker').position() # remember bubble heigth - heigth = bubble.height() - if offsetTop - bubble.attr('data-height', heigth) - bubble.css('height', "#{offsetTop.top + 30}px") - bubble.parent().find('.textBubble-overflowContainer').removeClass('hide') + heigth = bubbleContent.height() + if offsetTop && heigth + bubbleContent.attr('data-height', heigth) + bubbleContent.css('height', "#{offsetTop.top + 30}px") + bubbleOvervlowContainer.removeClass('hide') else if heigth > maxHeight - bubble.attr('data-height', heigth) - bubble.css('height', "#{maxHeight}px") - bubble.parent().find('.textBubble-overflowContainer').removeClass('hide') + bubbleContent.attr('data-height', heigth) + bubbleContent.css('height', "#{maxHeight}px") + bubbleOvervlowContainer.removeClass('hide') else - bubble.parent().find('.textBubble-overflowContainer').addClass('hide') + bubbleOvervlowContainer.addClass('hide') show_toogle: (e) -> e.stopPropagation() @@ -218,21 +249,21 @@ class ArticleViewItem extends App.Controller @seeMore = true - container = $(e.currentTarget).parents('.textBubble-content') - overflowContainer = container.find('.textBubble-overflowContainer') + bubbleContent = @textBubbleContent + bubbleOvervlowContainer = @textBubbleOverflowContainer - overflowContainer.velocity + bubbleOvervlowContainer.velocity properties: opacity: 0 options: duration: 300 - container.velocity + bubbleContent.velocity properties: - height: container.attr('data-height') + height: bubbleContent.attr('data-height') options: duration: 300 - complete: -> overflowContainer.addClass('hide'); + complete: -> bubbleOvervlowContainer.addClass('hide'); isOrContains: (node, container) -> while node diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/higlighter.js.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/higlighter.js.coffee index c52f49bd3..e3088ccd3 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/higlighter.js.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/higlighter.js.coffee @@ -1,32 +1,32 @@ class App.TicketZoomHighlighter extends App.Controller elements: '.textBubble-content': 'articles' - '.js-highlight .marker-icon': 'highlighterControl' + '.js-highlight-icon': 'highlighterControl' events: - 'click .js-highlight': 'toggleHighlight' + 'click .js-highlight': 'toggleHighlight' 'click .js-highlightColor': 'pickColor' colors: [ { name: 'Yellow' - color: "#f7e7b2" + color: '#f7e7b2' }, { name: 'Green' - color: "#bce7b6" + color: '#bce7b6' }, { name: 'Blue' - color: "#b3ddf9" + color: '#b3ddf9' }, { name: 'Pink' - color: "#fea9c5" + color: '#fea9c5' }, { name: 'Purple' - color: "#eac5ee" + color: '#eac5ee' } ] @@ -41,7 +41,6 @@ class App.TicketZoomHighlighter extends App.Controller rangy.init() @highlighter = rangy.createHighlighter(document, 'TextRange') - @addClassApplier entry for entry in @colors @setColor() @@ -50,13 +49,19 @@ class App.TicketZoomHighlighter extends App.Controller # store original highlight css data @storeOriginalHighlight() - @loadHighlights() + update = => + @loadHighlights() + @refreshObserver() + App.Delay.set( update, 800 ) render: -> @html App.view('ticket_zoom/highlighter') colors: @colors activeColorIndex: @activeColorIndex + highlighterInstance: => + @highlighter + storeOriginalHighlight: => @originalHighlight = fill: @highlighterControl.css('fill') @@ -70,19 +75,33 @@ class App.TicketZoomHighlighter extends App.Controller highlightEnable: => @isActive = true @highlighterControl.css('opacity', 1) + @highlighterControl.css('fill', @activeColor) + + @refreshObserver() highlightDisable: => @isActive = false @restoreOriginalHighlight() - #@highlighterControl.css('opacity', @originalHighlight.opacity) - active: => - @isActive + articles = @el.closest('.content').find('.textBubble') + articles.removeAttr('data-highlightcolor') + @refreshObserver() + + refreshObserver: => + articles = @el.closest('.content').find('.textBubble-content') + console.log('refreshObserver', articles) + articles.off('mouseup', @onMouseUp) + articles.on('mouseup', @onMouseUp) #future: touchend # for testing purposes the highlights get stored in localStorage loadHighlights: -> - if highlights = localStorage['highlights'] - @highlighter.deserialize localStorage['highlights'] + @el.closest('.content').find('.textBubble-content').each( (index, element) => + article_id = $(element).data('id') + article = App.TicketArticle.find(article_id) + if article.preferences && article.preferences['highlight'] + console.log('highlight', article.preferences['highlight']) + @highlighter.deserialize(article.preferences['highlight']) + ) # the serialization creates one string for the entiery ticket # containing the offsets and the highlight classes @@ -91,8 +110,12 @@ class App.TicketZoomHighlighter extends App.Controller # # if classes can be changed in the admin interface # we have to watch out to not end up with empty highlight classes - storeHighlights: -> - localStorage['highlights'] = @highlighter.serialize() + storeHighlights: (article_id) -> + article = App.TicketArticle.find(article_id) + data = @highlighter.serialize() + console.log('HI', article_id, data) + article.preferences['highlight'] = data + article.save() # the colors is set via css classes (can't do it inline with rangy) # thus we have to create a stylesheet if the colors @@ -102,54 +125,60 @@ class App.TicketZoomHighlighter extends App.Controller setColor: -> @highlightClass = @highlightClassPrefix + @colors[@activeColorIndex].name + @activeColor = @colors[@activeColorIndex].color if @isActive - @articles.attr('data-highlightcolor', @colors[@activeColorIndex].name) + articles = @el.closest('.content').find('.textBubble') + articles.attr('data-highlightcolor', @colors[@activeColorIndex].name) toggleHighlight: (e) => - if @isActive - @restoreOriginalHighlight() - else - @highlightEnable() - return + console.log('toggleHighlight', @isActive) - console.log('toggleHighlight', @isActive, @articles) if @isActive $(e.currentTarget).removeClass('active') - @isActive = false - @articles.off('mouseup', @onMouseUp) - @articles.removeAttr('data-highlightcolor') + + @highlightDisable() else + @highlightEnable() selection = rangy.getSelection() # if there's already something selected, # don't go into highlight mode # just toggle the selected if !selection.isCollapsed - @toggleHighlightAtSelection $(selection.anchorNode).closest @articles.selector - else + @toggleHighlightAtSelection(@content, @article_id) + #else # toggle ui - $(e.currentTarget).addClass('active') + #$(e.currentTarget).addClass('active') # activate selection background - @articles.attr('data-highlightcolor', @colors[@activeColorIndex].name) - - @isActive = true - @articles.on('mouseup', @onMouseUp) #future: touchend + #@articles.attr('data-highlightcolor', @colors[@activeColorIndex].name) pickColor: (e) => - @$('.js-highlightColor .visibility-change.active').removeClass('active') - $(e.currentTarget).find('.visibility-change').addClass('active') - @activeColorIndex = $(e.currentTarget).attr('data-key') + # TODO: @mrflix - still needed? + #@$('.js-highlightColor .visibility-change.active').removeClass('active') + #$(e.currentTarget).find('.visibility-change').addClass('active') - - @isActive = true - console.log('ooo', @activeColorIndex, @colors[@activeColorIndex].color, @colors[@activeColorIndex]) - @highlighterControl.css('fill', @colors[@activeColorIndex].color) - @highlighterControl.css('opacity', 1) + @activeColorIndex = $(e.currentTarget).attr('data-key') @setColor() + @highlightEnable() + + # check if selection exists - highlight it or remove highlight + @toggleHighlightAtSelection(@content, @article_id) + onMouseUp: (e) => - #@toggleHighlightAtSelection $(e.currentTarget).closest('.textBubble-content')# @articles.selector + @updateSelectedArticle(e) + + console.log('onMouseUp', @isActive, @content, @article_id) + if @isActive + @toggleHighlightAtSelection(@content, @article_id) # @articles.selector + + updateSelectedArticle: (e) => + @content = $(e.currentTarget).closest('.textBubble-content') + @article_id = @content.data('id') + if !@article_id + @content = $(e.currentTarget) + @article_id = @content.data('id') # # toggle Highlight @@ -160,12 +189,17 @@ class App.TicketZoomHighlighter extends App.Controller # - or highlights the selection # - clears the selection - toggleHighlightAtSelection: (article) => + toggleHighlightAtSelection: (article, article_id) => selection = rangy.getSelection() + # activate selection background + article.attr('data-highlightcolor', @colors[@activeColorIndex].name) + if @highlighter.selectionOverlapsHighlight selection + console.log('SELECTION EXISTS, REMOVED IT') @highlighter.unhighlightSelection() else + console.log('NEW SELECTION') @highlighter.highlightSelection @highlightClass, selection: selection containerElementId: article.get(0).id @@ -175,5 +209,5 @@ class App.TicketZoomHighlighter extends App.Controller @highlightDisable() - - #@storeHighlights() \ No newline at end of file + # save new selections + @storeHighlights(article_id) \ No newline at end of file diff --git a/app/assets/javascripts/app/models/ticket_article.js.coffee b/app/assets/javascripts/app/models/ticket_article.js.coffee index 67a42cc76..3ac3bf28a 100644 --- a/app/assets/javascripts/app/models/ticket_article.js.coffee +++ b/app/assets/javascripts/app/models/ticket_article.js.coffee @@ -1,5 +1,5 @@ class App.TicketArticle extends App.Model - @configure 'TicketArticle', 'from', 'to', 'cc', 'subject', 'body', 'content_type', 'ticket_id', 'type_id', 'sender_id', 'internal', 'in_reply_to', 'form_id', 'updated_at' + @configure 'TicketArticle', 'from', 'to', 'cc', 'subject', 'body', 'content_type', 'ticket_id', 'type_id', 'sender_id', 'internal', 'in_reply_to', 'form_id', 'preferences', 'updated_at' @extend Spine.Model.Ajax @url: @apiPath + '/ticket_articles' @configure_attributes = [ diff --git a/app/assets/javascripts/app/views/layout_ref/highlight.jst.eco b/app/assets/javascripts/app/views/layout_ref/highlight.jst.eco index 11a40c86b..94d5cc4ae 100644 --- a/app/assets/javascripts/app/views/layout_ref/highlight.jst.eco +++ b/app/assets/javascripts/app/views/layout_ref/highlight.jst.eco @@ -16,7 +16,7 @@ - <%= entry.name %> + <%- @Ti(entry.name) %> diff --git a/app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco b/app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco index a3de00638..a989a4bbd 100644 --- a/app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco +++ b/app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco @@ -43,7 +43,7 @@
-
+
<%- App.Utils.signatureIdentify( @article.html ) %>
<%- @T('See more') %>
diff --git a/app/assets/javascripts/app/views/ticket_zoom/highlighter.jst.eco b/app/assets/javascripts/app/views/ticket_zoom/highlighter.jst.eco index 796b36851..d59889b9e 100644 --- a/app/assets/javascripts/app/views/ticket_zoom/highlighter.jst.eco +++ b/app/assets/javascripts/app/views/ticket_zoom/highlighter.jst.eco @@ -1,5 +1,5 @@
- +