diff --git a/app/assets/javascripts/app/controllers/getting_started.js.coffee b/app/assets/javascripts/app/controllers/getting_started.js.coffee index 8d76bdcc0..87b375439 100644 --- a/app/assets/javascripts/app/controllers/getting_started.js.coffee +++ b/app/assets/javascripts/app/controllers/getting_started.js.coffee @@ -287,6 +287,11 @@ class Base extends App.ControllerContent @navigate '#import/' + data.import_backend return + # import config options + if data.config + for key, value of data.config + App.Config.set(key, value) + # render page @render() ) diff --git a/app/assets/javascripts/app/controllers/layout_ref.js.coffee b/app/assets/javascripts/app/controllers/layout_ref.js.coffee index 55616d440..3cc9b34e4 100644 --- a/app/assets/javascripts/app/controllers/layout_ref.js.coffee +++ b/app/assets/javascripts/app/controllers/layout_ref.js.coffee @@ -375,7 +375,7 @@ class LayoutRefCommunicationReply extends App.ControllerContent for i in [0..100] setTimeout @updateUploadProgress, i*duration/100 , i - setTimeout (=> + setTimeout (=> callback() @renderAttachment(fileName, fileSize) ), duration @@ -584,7 +584,7 @@ class ImportWizard extends App.ControllerWizard @render() # wait 500 ms after the last user input before we check the link - @otrsLink.on 'input', _.debounce(@checkOtrsLink, 600) + @otrsLink.on 'input', _.debounce(@checkOtrsLink, 600) checkOtrsLink: (e) => if @otrsLink.val() is "" @@ -852,14 +852,14 @@ class highlightRef extends App.ControllerContent # containing the offsets and the highlight classes # # we have to check how it works with having open several tickets – it might break - # + # # 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() # the colors is set via css classes (can't do it inline with rangy) - # thus we have to create a stylesheet if the colors + # thus we have to create a stylesheet if the colors # can be changed in the admin interface addClassApplier: (entry) -> @highlighter.addClassApplier rangy.createCssClassApplier(@highlightClassPrefix + entry.name) @@ -884,7 +884,7 @@ class highlightRef extends App.ControllerContent activate: -> selection = rangy.getSelection() - # if there's already something selected, + # if there's already something selected, # don't go into highlight mode # just toggle the selected if !selection.isCollapsed @@ -914,10 +914,10 @@ class highlightRef extends App.ControllerContent onMouseUp: (e) => @toggleHighlightAtSelection $(e.currentTarget).closest @articles.selector - # + # # toggle Highlight # ================ - # + # # - only works when the selection starts and ends inside an article # - clears highlights in selection # - or highlights the selection @@ -931,7 +931,7 @@ class highlightRef extends App.ControllerContent else @highlighter.highlightSelection @highlightClass, selection: selection - containerElementId: article.get(0).id + containerElementId: article.get(0).id # remove selection selection.removeAllRanges() @@ -1096,7 +1096,7 @@ class cluesRef extends App.ControllerContent showWindow: => @modalWindow.velocity - properties: + properties: scale: [1, 0.2] opacity: [1, 0] options: @@ -1105,7 +1105,7 @@ class cluesRef extends App.ControllerContent hideWindow: (callback) => @modalWindow.velocity - properties: + properties: scale: [0.2, 1] opacity: 0 options: @@ -1131,7 +1131,7 @@ class cluesRef extends App.ControllerContent if target.right + modal.width <= maxWidth left = target.right position = 'right' - else + else # place left left = target.left - modal.width position = 'left' @@ -1208,7 +1208,7 @@ class cluesRef extends App.ControllerContent getVisibleBoundingBox: (el) -> ### - getBoundingClientRect doesn't take + getBoundingClientRect doesn't take absolute-positioned child nodes into account ### @@ -1412,7 +1412,7 @@ class schedulersRef extends App.ControllerContent switch items.length when 1 then return items[0] when 2 then return "#{ items[0] } and #{ items[1] }" - else + else return "#{ items.slice(0, -1).join(', ') } and #{ items[items.length-1] }" App.Config.set( 'layout_ref/schedulers', schedulersRef, 'Routes' ) @@ -1431,11 +1431,22 @@ class InputsRef extends App.ControllerContent # selectable search searchableSelectObject = new App.SearchableSelect attribute: - name: 'project-name' - id: 'project-name-123' + name: 'project-name' + id: 'project-name-123' placeholder: 'Enter Project Name' - options: [{"value":0,"name":"Apple"},{"value":1,"name":"Microsoft","selected":true},{"value":2,"name":"Google"},{"value":3,"name":"Deutsche Bahn"},{"value":4,"name":"Sparkasse"},{"value":5,"name":"Deutsche Post"},{"value":6,"name":"Mitfahrzentrale"},{"value":7,"name":"Starbucks"},{"value":8,"name":"Mac Donalds"},{"value":9,"name":"Flixbus"},{"value":10,"name":"Betahaus"},{"value":11,"name":"Bruno Banani"},{"value":12,"name":"Alpina"},{"value":13,"name":"Samsung"},{"value":14,"name":"ChariTea"},{"value":15,"name":"fritz-kola"},{"value":16,"name":"Vitamin Water"},{"value":17,"name":"Znuny"},{"value":18,"name":"Max & Moritz"}] - @$('.searchableSelectPlaceholder').replaceWith( searchableSelectObject.el ) + options: [{"value":0,"name":"Apple"},{"value":1,"name":"Microsoft","selected":true},{"value":2,"name":"Google"},{"value":3,"name":"Deutsche Bahn"},{"value":4,"name":"Sparkasse"},{"value":5,"name":"Deutsche Post"},{"value":6,"name":"Mitfahrzentrale"},{"value":7,"name":"Starbucks"},{"value":8,"name":"Mac Donalds"},{"value":9,"name":"Flixbus"},{"value":10,"name":"Betahaus"},{"value":11,"name":"Bruno Banani"},{"value":12,"name":"Alpina"},{"value":13,"name":"Samsung"},{"value":14,"name":"ChariTea"},{"value":15,"name":"fritz-kola"},{"value":16,"name":"Vitamin Water"},{"value":17,"name":"Znuny"},{"value":18,"name":"Max & Moritz"}] + @$('.searchableSelectPlaceholder').replaceWith( searchableSelectObject.element() ) + + # selectable search + searchableAjaxSelectObject = new App.SearchableAjaxSelect + attribute: + name: 'user' + id: 'user-123' + placeholder: 'Enter User' + limt: 10 + object: 'User' + + @$('.searchableAjaxSelectPlaceholder').replaceWith( searchableAjaxSelectObject.element() ) # time and timeframe @$('.time').timepicker() @@ -1515,7 +1526,7 @@ class calendarSubscriptionsRef extends App.ControllerContent switch items.length when 1 then return items[0] when 2 then return "#{ items[0] } and #{ items[1] }" - else + else return "#{ items.slice(0, -1).join(', ') } and #{ items[items.length-1] }" diff --git a/app/assets/javascripts/app/controllers/navigation.js.coffee b/app/assets/javascripts/app/controllers/navigation.js.coffee index 084e82b8f..56fc6dac4 100644 --- a/app/assets/javascripts/app/controllers/navigation.js.coffee +++ b/app/assets/javascripts/app/controllers/navigation.js.coffee @@ -166,35 +166,17 @@ class App.Navigation extends App.ControllerWidgetPermanent area.result = [] for id in area.ids ticket = App.Ticket.find( id ) - data = - display: "##{ticket.number} - #{ticket.title}" - id: ticket.id - class: "level-#{ticket.level()} ticket-popover" - url: ticket.uiUrl() - iconClass: "priority" - area.result.push data + area.result.push ticket.searchResultAttributes() else if area.name is 'User' area.result = [] for id in area.ids user = App.User.find( id ) - data = - display: "#{user.displayName()}" - id: user.id - class: "user user-popover" - url: user.uiUrl() - iconClass: "user" - area.result.push data + area.result.push user.searchResultAttributes() else if area.name is 'Organization' area.result = [] for id in area.ids organization = App.Organization.find( id ) - data = - display: "#{organization.displayName()}" - id: organization.id - class: "organization organization-popover" - url: organization.uiUrl() - iconClass: "organization" - area.result.push data + area.result.push organization.searchResultAttributes() @renderResult(result) diff --git a/app/assets/javascripts/app/lib/app_post/searchable_select.js.coffee b/app/assets/javascripts/app/lib/app_post/searchable_select.js.coffee index 9eae1d37c..3cbfc62fa 100644 --- a/app/assets/javascripts/app/lib/app_post/searchable_select.js.coffee +++ b/app/assets/javascripts/app/lib/app_post/searchable_select.js.coffee @@ -26,7 +26,6 @@ class App.SearchableSelect extends Spine.Controller @render() render: -> - console.log "options", @options firstSelected = _.find @options.attribute.options, (option) -> option.selected if firstSelected diff --git a/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.js.coffee b/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.js.coffee index 4c13dc901..995abb241 100644 --- a/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.js.coffee +++ b/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.js.coffee @@ -14,7 +14,7 @@ class App.UserOrganizationAutocompletion extends App.Controller @key = Math.floor( Math.random() * 999999 ).toString() if !@attribute.source - @attribute.source = @apiPath + '/search_user_org' + @attribute.source = @apiPath + '/search/user-organization' @build() element: => @@ -114,6 +114,10 @@ class App.UserOrganizationAutocompletion extends App.Controller item = '' @close() + # if tab / close recipientList + if e.keyCode is 9 + @close() + # ignore arrow keys if e.keyCode is 37 return diff --git a/app/assets/javascripts/app/lib/app_post/utils.js.coffee b/app/assets/javascripts/app/lib/app_post/utils.js.coffee index a3096c1bc..064d7bf6a 100644 --- a/app/assets/javascripts/app/lib/app_post/utils.js.coffee +++ b/app/assets/javascripts/app/lib/app_post/utils.js.coffee @@ -100,47 +100,62 @@ class App.Utils # textWithoutTags = App.Utils.htmlRemoveTags( html ) @htmlRemoveTags: (html) -> + htmlTmp = $( '
' + html.html() + '
' ) + + # remove comments + @_removeComments( htmlTmp ) + + # remove work markup + htmlTmp = @_removeWordMarkup( htmlTmp ) # remove tags, keep content - html.find('div, span, p, li, ul, ol, a, b, u, i, label, small, strong, strike, pre, code, center, blockquote, form, textarea, font, address, table, thead, tbody, tr, td, h1, h2, h3, h4, h5, h6').replaceWith( -> + htmlTmp.find('div, span, p, li, ul, ol, a, b, u, i, label, small, strong, strike, pre, code, center, blockquote, form, textarea, font, address, table, thead, tbody, tr, td, h1, h2, h3, h4, h5, h6').replaceWith( -> $(@).contents() ) # remove tags & content - html.find('div, span, p, li, ul, ol, a, b, u, i, label, small, strong, strike, pre, code, center, blockquote, form, textarea, font, table, thead, tbody, tr, td, h1, h2, h3, h4, h5, h6, br, hr, img, input, select, button, style, applet, canvas, script, frame, iframe').remove() + htmlTmp.find('div, span, p, li, ul, ol, a, b, u, i, label, small, strong, strike, pre, code, center, blockquote, form, textarea, font, table, thead, tbody, tr, td, h1, h2, h3, h4, h5, h6, br, hr, img, input, select, button, style, applet, embed, noframes, canvas, script, frame, iframe').remove() - html + html.html(htmlTmp) # htmlOnlyWithRichtext = App.Utils.htmlRemoveRichtext( html ) @htmlRemoveRichtext: (html) -> + htmlTmp = $( '
' + html.html() + '
' ) # remove comments - @_removeComments( html ) + @_removeComments( htmlTmp ) # remove style and class - @_removeAttributes( html ) + @_removeAttributes( htmlTmp ) + + # remove work markup + htmlTmp = @_removeWordMarkup( htmlTmp ) # remove tags, keep content - html.find('li, ul, ol, a, b, u, i, label, small, strong, strike, pre, code, center, blockquote, form, textarea, font, address, table, thead, tbody, tr, td, h1, h2, h3, h4, h5, h6').replaceWith( -> + htmlTmp.find('li, ul, ol, a, b, u, i, label, small, strong, strike, pre, code, center, blockquote, form, textarea, font, address, table, thead, tbody, tr, td, h1, h2, h3, h4, h5, h6').replaceWith( -> $(@).contents() ) # remove tags & content - html.find('li, ul, ol, a, b, u, i, label, small, strong, strike, pre, code, center, blockquote, form, textarea, font, address, table, thead, tbody, tr, td, h1, h2, h3, h4, h5, h6, hr, img, input, select, button, style, applet, canvas, script, frame, iframe').remove() + htmlTmp.find('li, ul, ol, a, b, u, i, label, small, strong, strike, pre, code, center, blockquote, form, textarea, font, address, table, thead, tbody, tr, td, h1, h2, h3, h4, h5, h6, hr, img, input, select, button, style, applet, embed, noframes, canvas, script, frame, iframe').remove() - html + html.html(htmlTmp) # cleanHtmlWithRichText = App.Utils.htmlCleanup( html ) @htmlCleanup: (html) -> + htmlTmp = $( '
' + html.html() + '
' ) # remove comments - @_removeComments( html ) + @_removeComments( htmlTmp ) # remove style and class - @_removeAttributes( html ) + @_removeAttributes( htmlTmp ) + + # remove work markup + htmlTmp = @_removeWordMarkup( htmlTmp ) # remove tags, keep content - html.find('a, font, small, time').replaceWith( -> + htmlTmp.find('a, font, small, time').replaceWith( -> $(@).contents() ) @@ -148,31 +163,32 @@ class App.Utils # New type of the tag replacementTag = 'div'; - # Replace all a tags with the type of replacementTag - html.find('h1, h2, h3, h4, h5, h6, textarea').each( -> + # Replace all x tags with the type of replacementTag + htmlTmp.find('h1, h2, h3, h4, h5, h6, textarea').each( -> outer = this.outerHTML; # Replace opening tag - regex = new RegExp('<' + this.tagName, 'i'); - newTag = outer.replace(regex, '<' + replacementTag); + regex = new RegExp('<' + this.tagName, 'i') + newTag = outer.replace(regex, '<' + replacementTag) # Replace closing tag - regex = new RegExp(' - html.find('div, span, p, li, ul, ol, a, b, u, i, label, small, strong, strike, pre, code, center, blockquote, h1, h2, h3, h4, h5, h6') + html.find('*') .removeAttr( 'style' ) .removeAttr( 'class' ) .removeAttr( 'title' ) + .removeAttr( 'lang' ) html @_removeComments: (html) -> @@ -182,6 +198,14 @@ class App.Utils ) html + @_removeWordMarkup: (html) -> + htmlTmp = html.get(0).outerHTML + regex = new RegExp('<(/w|w)\:[A-Za-z]{3}>') + htmlTmp = htmlTmp.replace(regex, '') + regex = new RegExp('<(/o|o)\:[A-Za-z]{1}>') + htmlTmp = htmlTmp.replace(regex, '') + $(htmlTmp) + # signatureNeeded = App.Utils.signatureCheck( message, signature ) @signatureCheck: (message, signature) -> messageText = $( '
' + message + '
' ).text().trim() diff --git a/app/assets/javascripts/app/lib/app_post/z_searchable_ajax_select.js.coffee b/app/assets/javascripts/app/lib/app_post/z_searchable_ajax_select.js.coffee index 1ef08cd5e..3abaf241b 100644 --- a/app/assets/javascripts/app/lib/app_post/z_searchable_ajax_select.js.coffee +++ b/app/assets/javascripts/app/lib/app_post/z_searchable_ajax_select.js.coffee @@ -3,12 +3,81 @@ class App.SearchableAjaxSelect extends App.SearchableSelect onInput: (event) => super - # send ajax request @query + # convert requested object + # e.g. Ticket to ticket or AnotherObject to another_object + objectString = underscored( @options.attribute.object ) - onAjaxResponse: (data) => + # create common accessors + @apiPath = App.Config.get('api_path') + + # create cache and cache key + @searchResultCache = @searchResultCache || {} + + @cacheKey = "#{objectString}+#{@query}" + + # use cache for search result + if @searchResultCache[@cacheKey] + return @onAjaxResponse( @searchResultCache[@cacheKey] ) + + # add timout for loader icon + clearTimeout @loaderTimeoutId + @loaderTimeoutId = setTimeout @showLoader, 1000 + + # start search request and update options + App.Ajax.request( + id: @options.attribute.id + type: 'GET' + url: "#{@apiPath}/search/#{objectString}" + data: + query: @query + limit: @options.attribute.limit + processData: true + success: @onAjaxResponse + ) + + onAjaxResponse: (data, status, xhr) => + + # clear timout and remove loader icon + clearTimeout @loaderTimeoutId + @el.removeClass('is-loading') + + # cache search result + @searchResultCache[@cacheKey] = data + + # load assets + App.Collection.loadAssets( data.assets ) + + # get options from search result + options = [] + for object in data.result + if object.type is 'Ticket' + ticket = App.Ticket.find( object.id ) + data = + name: "##{ticket.number} - #{ticket.title}" + value: ticket.id + options.push data + else if object.type is 'User' + user = App.User.find( object.id ) + data = + name: "#{user.displayName()}" + value: user.id + options.push data + else if object.type is 'Organization' + organization = App.Organization.find( object.id ) + data = + name: "#{organization.displayName()}" + value: organization.id + options.push data + + # fill template with gathered options @optionsList.html App.view('generic/searchable_select_options') - options: data + options: options + # refresh elements @refreshElements() - @filterByQuery @query \ No newline at end of file + # execute filter + @filterByQuery @query + + showLoader: => + @el.addClass('is-loading') diff --git a/app/assets/javascripts/app/models/organization.js.coffee b/app/assets/javascripts/app/models/organization.js.coffee index 1748a4b76..68a60be0e 100644 --- a/app/assets/javascripts/app/models/organization.js.coffee +++ b/app/assets/javascripts/app/models/organization.js.coffee @@ -30,3 +30,10 @@ class App.Organization extends App.Model user = App.User.find( user_id ) data['members'].push user data + + searchResultAttributes: -> + display: "#{@displayName()}" + id: @id + class: "organization organization-popover" + url: @uiUrl() + iconClass: "organization" \ No newline at end of file diff --git a/app/assets/javascripts/app/models/ticket.js.coffee b/app/assets/javascripts/app/models/ticket.js.coffee index c941a3963..e87e0f3f3 100644 --- a/app/assets/javascripts/app/models/ticket.js.coffee +++ b/app/assets/javascripts/app/models/ticket.js.coffee @@ -50,4 +50,10 @@ class App.Ticket extends App.Model iconActivity: (user) -> if @owner_id == user.id return 'important' - '' \ No newline at end of file + '' + searchResultAttributes: -> + display: "##{@number} - #{@title}" + id: @id + class: "level-#{@level()} ticket-popover" + url: @uiUrl() + iconClass: "priority" diff --git a/app/assets/javascripts/app/models/user.js.coffee b/app/assets/javascripts/app/models/user.js.coffee index a37b09397..cedbf80c4 100644 --- a/app/assets/javascripts/app/models/user.js.coffee +++ b/app/assets/javascripts/app/models/user.js.coffee @@ -119,3 +119,10 @@ class App.User extends App.Model data['groups'].push group data + + searchResultAttributes: -> + display: "#{@displayName()}" + id: @id + class: "user user-popover" + url: @uiUrl() + iconClass: "user" \ No newline at end of file diff --git a/app/assets/javascripts/app/views/generic/searchable_select_options.jst.eco b/app/assets/javascripts/app/views/generic/searchable_select_options.jst.eco index d255b2be6..bf6cf3fc2 100644 --- a/app/assets/javascripts/app/views/generic/searchable_select_options.jst.eco +++ b/app/assets/javascripts/app/views/generic/searchable_select_options.jst.eco @@ -1,3 +1,5 @@ -<% for option in @options: %> -