Added keyboard shortcut support.
This commit is contained in:
parent
3d372543b2
commit
ffe011553d
25 changed files with 784 additions and 119 deletions
|
@ -128,6 +128,11 @@ Source: https://jqueryui.com
|
||||||
Copyright: 2014 jQuery Foundation
|
Copyright: 2014 jQuery Foundation
|
||||||
License: MIT license
|
License: MIT license
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
|
jquery.hotkeys.js
|
||||||
|
Source: https://github.com/jeresig/jquery.hotkeys
|
||||||
|
Copyright 2010, John Resig
|
||||||
|
License: Dual licensed under the MIT or GPL Version 2 licenses.
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
underscore.js
|
underscore.js
|
||||||
Source: http://underscorejs.org
|
Source: http://underscorejs.org
|
||||||
Copyright: 2009-2015 Jeremy Ashkenas, DocumentCloud
|
Copyright: 2009-2015 Jeremy Ashkenas, DocumentCloud
|
||||||
|
|
|
@ -458,6 +458,9 @@ class App.Controller extends Spine.Controller
|
||||||
if @userTicketPopupsList
|
if @userTicketPopupsList
|
||||||
@userTicketPopupsList.popover('destroy')
|
@userTicketPopupsList.popover('destroy')
|
||||||
|
|
||||||
|
anyPopoversDestroy: ->
|
||||||
|
$('.popover').remove()
|
||||||
|
|
||||||
recentView: (object, o_id) =>
|
recentView: (object, o_id) =>
|
||||||
params =
|
params =
|
||||||
object: object
|
object: object
|
||||||
|
|
|
@ -351,6 +351,9 @@ class App.ControllerNavSidbar extends App.ControllerContent
|
||||||
constructor: (params) ->
|
constructor: (params) ->
|
||||||
super
|
super
|
||||||
|
|
||||||
|
if @authenticateRequired
|
||||||
|
return if !@authenticate()
|
||||||
|
|
||||||
@params = params
|
@params = params
|
||||||
|
|
||||||
# get accessable groups
|
# get accessable groups
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
class Index extends App.ControllerModal
|
||||||
|
large: true
|
||||||
|
head: 'Keyboard Shortcuts'
|
||||||
|
buttonClose: true
|
||||||
|
buttonCancel: false
|
||||||
|
buttonSubmit: false
|
||||||
|
|
||||||
|
constructor: (params = {}) ->
|
||||||
|
delete params.el # do attache to body
|
||||||
|
super(params)
|
||||||
|
|
||||||
|
return if !@authenticate()
|
||||||
|
|
||||||
|
content: ->
|
||||||
|
App.view('keyboard_shortcuts')(
|
||||||
|
areas: App.Config.get('keyboard_shortcuts')
|
||||||
|
)
|
||||||
|
|
||||||
|
onClosed: ->
|
||||||
|
window.history.go(-1)
|
||||||
|
|
||||||
|
onSubmit: ->
|
||||||
|
window.history.go(-1)
|
||||||
|
|
||||||
|
onCancel: ->
|
||||||
|
window.history.go(-1)
|
||||||
|
|
||||||
|
App.Config.set('keyboard_shortcuts', Index, 'Routes')
|
||||||
|
|
||||||
|
App.Config.set('KeyboardShortcuts', { prio: 1700, parent: '#current_user', name: 'Keyboard Shortcuts', translate: true, target: '#keyboard_shortcuts', role: [ 'Admin', 'Agent' ] }, 'NavBarRight')
|
|
@ -2223,4 +2223,4 @@ class ChatToTicketRef extends App.ControllerContent
|
||||||
App.Config.set( 'layout_ref/chat_to_ticket', ChatToTicketRef, 'Routes' )
|
App.Config.set( 'layout_ref/chat_to_ticket', ChatToTicketRef, 'Routes' )
|
||||||
|
|
||||||
|
|
||||||
App.Config.set( 'LayoutRef', { prio: 1700, parent: '#current_user', name: 'Layout Reference', translate: true, target: '#layout_ref', role: [ 'Admin' ] }, 'NavBarRight' )
|
App.Config.set( 'LayoutRef', { prio: 1600, parent: '#current_user', name: 'Layout Reference', translate: true, target: '#layout_ref', role: [ 'Admin' ] }, 'NavBarRight' )
|
|
@ -1,4 +1,5 @@
|
||||||
class IndexRouter extends App.ControllerNavSidbar
|
class IndexRouter extends App.ControllerNavSidbar
|
||||||
|
authenticateRequired: true
|
||||||
configKey: 'NavBarAdmin'
|
configKey: 'NavBarAdmin'
|
||||||
|
|
||||||
App.Config.set('manage', IndexRouter, 'Routes')
|
App.Config.set('manage', IndexRouter, 'Routes')
|
||||||
|
|
|
@ -179,11 +179,98 @@ class App.Navigation extends App.ControllerWidgetPermanent
|
||||||
# renderPersonal
|
# renderPersonal
|
||||||
@renderPersonal()
|
@renderPersonal()
|
||||||
|
|
||||||
searchFunction = =>
|
# observer search box
|
||||||
|
@$('#global-search').bind('focusout', (e) =>
|
||||||
|
# delay to be able to click x
|
||||||
|
update = =>
|
||||||
|
@$('.search').removeClass('focused')
|
||||||
|
@delay(update, 100, 'removeFocused')
|
||||||
|
)
|
||||||
|
@$('#global-search').bind('focusin', (e) =>
|
||||||
|
@query = '' # reset query cache
|
||||||
|
@$('.search').addClass('focused')
|
||||||
|
@anyPopoversDestroy()
|
||||||
|
@searchFunction(0)
|
||||||
|
)
|
||||||
|
@$('form.search').on('submit', (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
)
|
||||||
|
@$('#global-search').on('keydown', @navigate)
|
||||||
|
|
||||||
|
# bind to empty search
|
||||||
|
@$('.empty-search').on('click', =>
|
||||||
|
@emptyAndClose()
|
||||||
|
)
|
||||||
|
|
||||||
|
new App.OnlineNotificationWidget(
|
||||||
|
el: @el
|
||||||
|
)
|
||||||
|
|
||||||
|
navigate: (e) =>
|
||||||
|
if e.keyCode is 27 # close on esc
|
||||||
|
@emptyAndClose()
|
||||||
|
return
|
||||||
|
else if e.keyCode is 38 # up
|
||||||
|
@nudge(e, -1)
|
||||||
|
return
|
||||||
|
else if e.keyCode is 40 # down
|
||||||
|
@nudge(e, 1)
|
||||||
|
return
|
||||||
|
else if e.keyCode is 13 # enter
|
||||||
|
href = @$('#global-search-result .nav-tab.is-active').attr('href')
|
||||||
|
@locationExecute(href)
|
||||||
|
@emptyAndClose()
|
||||||
|
return
|
||||||
|
|
||||||
|
# on other keys, show result
|
||||||
|
@searchFunction(200)
|
||||||
|
|
||||||
|
nudge: (e, position) =>
|
||||||
|
|
||||||
|
# get current
|
||||||
|
navigationResult = @$('#global-search-result')
|
||||||
|
current = navigationResult.find('.nav-tab.is-active')
|
||||||
|
if !current.get(0)
|
||||||
|
navigationResult.find('.nav-tab').first().addClass('is-active')
|
||||||
|
return
|
||||||
|
|
||||||
|
if position is 1
|
||||||
|
next = current.closest('li').nextAll('li').not('.divider').first().find('.nav-tab')
|
||||||
|
if next.get(0)
|
||||||
|
current.removeClass('is-active').popover('hide')
|
||||||
|
next.addClass('is-active').popover('show')
|
||||||
|
else
|
||||||
|
prev = current.closest('li').prevAll('li').not('.divider').first().find('.nav-tab')
|
||||||
|
if prev.get(0)
|
||||||
|
current.removeClass('is-active').popover('hide')
|
||||||
|
prev.addClass('is-active').popover('show')
|
||||||
|
|
||||||
|
emptyAndClose: =>
|
||||||
|
@$('#global-search').val('').blur()
|
||||||
|
@$('.search').removeClass('filled').removeClass('open')
|
||||||
|
|
||||||
|
# remove not needed popovers
|
||||||
|
@delay(@anyPopoversDestroy, 100, 'removePopovers')
|
||||||
|
|
||||||
|
andClose: =>
|
||||||
|
@$('#global-search').blur()
|
||||||
|
@$('.search').removeClass('open')
|
||||||
|
@delay(@anyPopoversDestroy, 100, 'removePopovers')
|
||||||
|
|
||||||
|
searchFunction: (delay) =>
|
||||||
|
|
||||||
|
search = =>
|
||||||
|
query = @$('#global-search').val().trim()
|
||||||
|
return if !query
|
||||||
|
return if query is @query
|
||||||
|
@query = query
|
||||||
|
@$('.search').toggleClass('filled', !!@query)
|
||||||
|
|
||||||
# use cache for search result
|
# use cache for search result
|
||||||
if @searchResultCache[@query]
|
if @searchResultCache[@query]
|
||||||
@renderResult( @searchResultCache[@query] )
|
@renderResult(@searchResultCache[@query].result)
|
||||||
|
currentTime = new Date
|
||||||
|
return if @searchResultCache[@query].time > currentTime.setSeconds(currentTime.getSeconds() - 20)
|
||||||
|
|
||||||
App.Ajax.request(
|
App.Ajax.request(
|
||||||
id: 'search'
|
id: 'search'
|
||||||
|
@ -193,13 +280,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
|
||||||
query: @query
|
query: @query
|
||||||
processData: true,
|
processData: true,
|
||||||
success: (data, status, xhr) =>
|
success: (data, status, xhr) =>
|
||||||
|
|
||||||
# load assets
|
|
||||||
App.Collection.loadAssets(data.assets)
|
App.Collection.loadAssets(data.assets)
|
||||||
|
|
||||||
# cache search result
|
|
||||||
@searchResultCache[@query] = data.result
|
|
||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
for item in data.result
|
for item in data.result
|
||||||
if App[item.type] && App[item.type].find
|
if App[item.type] && App[item.type].find
|
||||||
|
@ -214,84 +295,26 @@ class App.Navigation extends App.ControllerWidgetPermanent
|
||||||
else
|
else
|
||||||
@log 'error', "No such model App.#{item.type}"
|
@log 'error', "No such model App.#{item.type}"
|
||||||
|
|
||||||
|
diff = false
|
||||||
|
if @searchResultCache[@query]
|
||||||
|
diff = difference(@searchResultCache[@query].resultRaw, data.result)
|
||||||
|
|
||||||
|
# cache search result
|
||||||
|
@searchResultCache[@query] =
|
||||||
|
result: result
|
||||||
|
resultRaw: data.result
|
||||||
|
time: new Date
|
||||||
|
|
||||||
|
# if result hasn't changed, do not rerender
|
||||||
|
return if diff isnt false && _.isEmpty(diff)
|
||||||
|
|
||||||
@renderResult(result)
|
@renderResult(result)
|
||||||
|
|
||||||
@$('#global-search-result').on('click', 'a', ->
|
@$('#global-search-result').on('click', 'a', =>
|
||||||
close()
|
@andClose()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@delay(search, 200, 'search')
|
||||||
removePopovers = ->
|
|
||||||
$('.popover').remove()
|
|
||||||
|
|
||||||
close = =>
|
|
||||||
@$('#global-search').blur()
|
|
||||||
@$('.search').removeClass('open')
|
|
||||||
|
|
||||||
# remove not needed popovers
|
|
||||||
@delay( removePopovers, 280, 'removePopovers' )
|
|
||||||
|
|
||||||
emptyAndClose = =>
|
|
||||||
@$('#global-search').val('').blur()
|
|
||||||
@$('.search').removeClass('filled').removeClass('open')
|
|
||||||
|
|
||||||
# remove not needed popovers
|
|
||||||
@delay( removePopovers, 280, 'removePopovers' )
|
|
||||||
|
|
||||||
# observer search box
|
|
||||||
@$('#global-search').bind( 'focusout', (e) =>
|
|
||||||
# delay to be able to click x
|
|
||||||
update = =>
|
|
||||||
@$('.search').removeClass('focused')
|
|
||||||
@delay( update, 180, 'removeFocused' )
|
|
||||||
)
|
|
||||||
|
|
||||||
@$('#global-search').bind( 'focusin', (e) =>
|
|
||||||
|
|
||||||
@$('.search').addClass('focused')
|
|
||||||
|
|
||||||
# remove not needed popovers
|
|
||||||
removePopovers()
|
|
||||||
|
|
||||||
# check if search is needed
|
|
||||||
query = @$('#global-search').val().trim()
|
|
||||||
return if !query
|
|
||||||
@query = query
|
|
||||||
@delay( searchFunction, 220, 'search' )
|
|
||||||
)
|
|
||||||
|
|
||||||
# prevent submit of search box
|
|
||||||
@$('form.search').on( 'submit', (e) ->
|
|
||||||
e.preventDefault()
|
|
||||||
)
|
|
||||||
|
|
||||||
# start search
|
|
||||||
@$('#global-search').on( 'keyup', (e) =>
|
|
||||||
|
|
||||||
# close on esc
|
|
||||||
if e.which == 27
|
|
||||||
emptyAndClose()
|
|
||||||
return
|
|
||||||
|
|
||||||
# on other keys, show result
|
|
||||||
query = @$('#global-search').val().trim()
|
|
||||||
return if !query
|
|
||||||
return if query is @query
|
|
||||||
@query = query
|
|
||||||
@$('.search').toggleClass('filled', !!@query)
|
|
||||||
@delay( searchFunction, 200, 'search' )
|
|
||||||
)
|
|
||||||
|
|
||||||
# bind to empty search
|
|
||||||
@$('.empty-search').on(
|
|
||||||
'click'
|
|
||||||
->
|
|
||||||
emptyAndClose()
|
|
||||||
)
|
|
||||||
|
|
||||||
new App.OnlineNotificationWidget(
|
|
||||||
el: @el
|
|
||||||
)
|
|
||||||
|
|
||||||
getItems: (data) ->
|
getItems: (data) ->
|
||||||
navbar = _.values(data.navbar)
|
navbar = _.values(data.navbar)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
class Index extends App.ControllerNavSidbar
|
class Index extends App.ControllerNavSidbar
|
||||||
|
authenticateRequired: true
|
||||||
configKey: 'NavBarProfile'
|
configKey: 'NavBarProfile'
|
||||||
|
|
||||||
App.Config.set( 'profile', Index, 'Routes' )
|
App.Config.set( 'profile', Index, 'Routes' )
|
||||||
|
|
|
@ -133,6 +133,9 @@ class App.TicketZoomArticleNew extends App.Controller
|
||||||
|
|
||||||
# preselect article type
|
# preselect article type
|
||||||
@setArticleType(data.type.name)
|
@setArticleType(data.type.name)
|
||||||
|
|
||||||
|
# set focus into field
|
||||||
|
@textarea.focus()
|
||||||
)
|
)
|
||||||
|
|
||||||
# reset new article screen
|
# reset new article screen
|
||||||
|
|
|
@ -85,7 +85,7 @@ class Index extends App.ControllerContent
|
||||||
release: =>
|
release: =>
|
||||||
rerender = ->
|
rerender = ->
|
||||||
App.Event.trigger('ui:rerender')
|
App.Event.trigger('ui:rerender')
|
||||||
if @translationList.changes()
|
if @translationList && @translationList.changes()
|
||||||
App.Delay.set(rerender, 400)
|
App.Delay.set(rerender, 400)
|
||||||
|
|
||||||
showAction: =>
|
showAction: =>
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
class FFlt35
|
|
||||||
constructor: ->
|
|
||||||
data = App.Browser.detection()
|
|
||||||
if data.browser.name is 'Firefox' && data.browser.major && data.browser.major < 35
|
|
||||||
|
|
||||||
# for firefox lower 35 we need to set a class to hide own dropdown images
|
|
||||||
# whole file can be removed after dropping firefox 34 and lower support
|
|
||||||
$('html').addClass('ff-lt-35')
|
|
||||||
|
|
||||||
App.Config.set( 'aaa_ff-lt-35', FFlt35, 'Widgets' )
|
|
|
@ -0,0 +1,293 @@
|
||||||
|
class App.KeyboardShortcutWidget extends Spine.Module
|
||||||
|
@include App.LogInclude
|
||||||
|
|
||||||
|
constructor: ->
|
||||||
|
@observerKeys()
|
||||||
|
|
||||||
|
observerKeys: =>
|
||||||
|
#jQuery.hotkeys.options.filterInputAcceptingElements = false
|
||||||
|
navigationHotkeys = 'alt+ctrl'
|
||||||
|
areas = App.Config.get('keyboard_shortcuts')
|
||||||
|
for area in areas
|
||||||
|
for item in area.content
|
||||||
|
for shortcut in item.shortcuts
|
||||||
|
modifier = ''
|
||||||
|
if shortcut.hotkeys
|
||||||
|
modifier += navigationHotkeys
|
||||||
|
if shortcut.key
|
||||||
|
if modifier isnt ''
|
||||||
|
modifier += '+'
|
||||||
|
modifier += shortcut.key
|
||||||
|
if shortcut.callback
|
||||||
|
@log 'debug', 'bind for', modifier
|
||||||
|
$(document).bind('keydown', modifier, shortcut.callback)
|
||||||
|
|
||||||
|
App.Config.set('keyboard_shortcuts', App.KeyboardShortcutWidget, 'Widgets')
|
||||||
|
App.Config.set(
|
||||||
|
'keyboard_shortcuts',
|
||||||
|
[
|
||||||
|
{
|
||||||
|
headline: 'Navigation'
|
||||||
|
location: 'left'
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
where: 'Used anywhere'
|
||||||
|
shortcuts: [
|
||||||
|
{
|
||||||
|
key: 'd'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Dashboard'
|
||||||
|
callback: ->
|
||||||
|
window.location.hash = '#dashboard'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'o'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Overviews'
|
||||||
|
callback: ->
|
||||||
|
window.location.hash = '#ticket/view'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 's'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Search'
|
||||||
|
callback: ->
|
||||||
|
$('#global-search').focus()
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'n'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'New Ticket'
|
||||||
|
callback: ->
|
||||||
|
window.location.hash = '#ticket/create'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'e'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Logout'
|
||||||
|
callback: ->
|
||||||
|
window.location.hash = '#logout'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'h'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'List of shortcuts'
|
||||||
|
callback: ->
|
||||||
|
window.location.hash = '#keyboard_shortcuts'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'x'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Close current tab'
|
||||||
|
callback: ->
|
||||||
|
$('#navigation .tasks .is-active .js-close').click()
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'tab'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Next in tab'
|
||||||
|
callback: ->
|
||||||
|
if $('#navigation .tasks .is-active').get(0)
|
||||||
|
if $('#navigation .tasks .is-active').next().get(0)
|
||||||
|
$('#navigation .tasks .is-active').next().find('div').first().click()
|
||||||
|
return
|
||||||
|
$('#navigation .tasks .task').first().find('div').first().click()
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'shift+tab'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Previous tab'
|
||||||
|
callback: ->
|
||||||
|
if $('#navigation .tasks .is-active').get(0)
|
||||||
|
if $('#navigation .tasks .is-active').prev().get(0)
|
||||||
|
$('#navigation .tasks .is-active').prev().find('div').first().click()
|
||||||
|
return
|
||||||
|
$('#navigation .tasks .task').last().find('div').first().click()
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'return'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Confirm/submit dialog'
|
||||||
|
callback: ->
|
||||||
|
|
||||||
|
# check of primary modal exists
|
||||||
|
dialog = $('body > div.modal')
|
||||||
|
if dialog.get(0)
|
||||||
|
dialog.find('.js-submit').click()
|
||||||
|
return
|
||||||
|
|
||||||
|
# check of local modal exists
|
||||||
|
dialog = $('.active.content > div.modal')
|
||||||
|
if dialog.get(0)
|
||||||
|
dialog.find('.js-submit').click()
|
||||||
|
return
|
||||||
|
|
||||||
|
# check ticket edit
|
||||||
|
dialog = $('.active.content .js-attributeBar .js-submit')
|
||||||
|
if dialog.get(0)
|
||||||
|
dialog.first().click()
|
||||||
|
return
|
||||||
|
|
||||||
|
dialog = $('.active.content .js-submit')
|
||||||
|
if dialog.get(0)
|
||||||
|
dialog.first().click()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
where: 'Used in lists (views and results)'
|
||||||
|
shortcuts: [
|
||||||
|
{
|
||||||
|
key: ['▲', '▼']
|
||||||
|
description: 'Move up and down'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: ['◀', '▶']
|
||||||
|
description: 'Move left and right'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'enter'
|
||||||
|
description: 'Select item',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
headline: 'Translations'
|
||||||
|
location: 'left'
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
where: 'Used anywhere (admin only)'
|
||||||
|
shortcuts: [
|
||||||
|
{
|
||||||
|
admin: true
|
||||||
|
key: 't'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Enable/disable inline translations'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
headline: 'Tickets'
|
||||||
|
location: 'right'
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
where: 'Used when viewing a Ticket'
|
||||||
|
shortcuts: [
|
||||||
|
{
|
||||||
|
key: 'm'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Open note box'
|
||||||
|
callback: ->
|
||||||
|
$('.active.content .article-new .articleNewEdit-body').first().focus()
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'r'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Reply to last article'
|
||||||
|
callback: ->
|
||||||
|
lastArticleWithReply = $('.active.content .ticket-article .icon-reply').last()
|
||||||
|
lastArticleWithReplyAll = lastArticleWithReply.parent().find('.icon-reply-all')
|
||||||
|
if lastArticleWithReplyAll.get(0)
|
||||||
|
lastArticleWithReplyAll.click()
|
||||||
|
return
|
||||||
|
lastArticleWithReply.click()
|
||||||
|
}
|
||||||
|
#{
|
||||||
|
# key: 'm'
|
||||||
|
# hotkeys: true
|
||||||
|
# description: 'Open macro selection'
|
||||||
|
# callback: ->
|
||||||
|
# window.location.hash = '#ticket/create'
|
||||||
|
#}
|
||||||
|
{
|
||||||
|
key: 'c'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Update as closed'
|
||||||
|
callback: ->
|
||||||
|
return if !$('.active.content .edit').get(0)
|
||||||
|
$('.active.content .edit [name="state_id"]').val(4)
|
||||||
|
$('.active.content .js-attributeBar .js-submit').first().click()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
headline: 'Text editing'
|
||||||
|
location: 'right'
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
where: 'Used when composing a text'
|
||||||
|
shortcuts: [
|
||||||
|
{
|
||||||
|
key: 'u'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Format as _underlined_'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'b'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Format as |bold|'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'i'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Format as ||italic||'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 't'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Format as //strikethrough//'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'f'
|
||||||
|
hotkeys: true
|
||||||
|
description: 'Removes the formatting'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'z'
|
||||||
|
hotkeys: true,
|
||||||
|
description: 'Inserts a horizontal rule'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'l'
|
||||||
|
hotkeys: true,
|
||||||
|
description: 'Format as unordered list'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'k'
|
||||||
|
hotkeys: true,
|
||||||
|
description: 'Format as ordered list'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: '1'
|
||||||
|
hotkeys: true,
|
||||||
|
description: 'Format as h1 heading'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: '2'
|
||||||
|
hotkeys: true,
|
||||||
|
description: 'Format as h2 heading'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: '3'
|
||||||
|
hotkeys: true,
|
||||||
|
description: 'Format as h3 heading'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: 'w'
|
||||||
|
hotkeys: true,
|
||||||
|
description: 'Removes any hyperlink'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
|
@ -38,7 +38,7 @@ class App.Browser
|
||||||
# define min. required browser version
|
# define min. required browser version
|
||||||
map =
|
map =
|
||||||
Chrome: 37
|
Chrome: 37
|
||||||
Firefox: 31
|
Firefox: 36
|
||||||
Explorer: 10
|
Explorer: 10
|
||||||
Safari: 6
|
Safari: 6
|
||||||
Opera: 22
|
Opera: 22
|
||||||
|
|
|
@ -224,6 +224,7 @@ class _i18nSingleton extends Spine.Module
|
||||||
.replace(/\|\|(.+?)\|\|/gm, '<i>$1</i>')
|
.replace(/\|\|(.+?)\|\|/gm, '<i>$1</i>')
|
||||||
.replace(/\|(.+?)\|/gm, '<b>$1</b>')
|
.replace(/\|(.+?)\|/gm, '<b>$1</b>')
|
||||||
.replace(/_(.+?)_/gm, '<u>$1</u>')
|
.replace(/_(.+?)_/gm, '<u>$1</u>')
|
||||||
|
.replace(/\/\/(.+?)\/\//gm, '<del>$1</del>')
|
||||||
.replace(/§(.+?)§/gm, '<kbd>$1</kbd>')
|
.replace(/§(.+?)§/gm, '<kbd>$1</kbd>')
|
||||||
|
|
||||||
# search %s
|
# search %s
|
||||||
|
|
|
@ -6,8 +6,7 @@ class App.Run extends App.Controller
|
||||||
App.Event.trigger('app:init')
|
App.Event.trigger('app:init')
|
||||||
|
|
||||||
# browser check
|
# browser check
|
||||||
if !App.Browser.check()
|
return if !App.Browser.check()
|
||||||
return
|
|
||||||
|
|
||||||
# hide splash screen
|
# hide splash screen
|
||||||
$('.splash').hide()
|
$('.splash').hide()
|
||||||
|
|
|
@ -51,7 +51,6 @@
|
||||||
|
|
||||||
this._defaults = defaults;
|
this._defaults = defaults;
|
||||||
this._name = pluginName;
|
this._name = pluginName;
|
||||||
this._setTimeOutReformat = false;
|
|
||||||
|
|
||||||
// take placeholder from markup
|
// take placeholder from markup
|
||||||
if ( !this.options.placeholder && this.$element.data('placeholder') ) {
|
if ( !this.options.placeholder && this.$element.data('placeholder') ) {
|
||||||
|
@ -67,6 +66,19 @@
|
||||||
Plugin.prototype.init = function () {
|
Plugin.prototype.init = function () {
|
||||||
var _this = this
|
var _this = this
|
||||||
|
|
||||||
|
this.toggleBlock = function(tag) {
|
||||||
|
sel = window.getSelection()
|
||||||
|
node = $(sel.anchorNode)
|
||||||
|
console.log('toggleBlock', tag, node.parent(), node.is())
|
||||||
|
if (node.is(tag) || node.parent().is(tag) || node.parent().parent().is(tag)) {
|
||||||
|
document.execCommand('formatBlock', false, 'div')
|
||||||
|
//document.execCommand('RemoveFormat')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.execCommand('formatBlock', false, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handle enter
|
// handle enter
|
||||||
this.$element.on('keydown', function (e) {
|
this.$element.on('keydown', function (e) {
|
||||||
_this.log('keydown', e.keyCode)
|
_this.log('keydown', e.keyCode)
|
||||||
|
@ -94,19 +106,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// on zammad metaKey + ctrlKey + i/b/u
|
// on zammad altKey + ctrlKey + i/b/u
|
||||||
// metaKey + ctrlKey + u -> Toggles the current selection between underlined and not underlined
|
// altKey + ctrlKey + u -> Toggles the current selection between underlined and not underlined
|
||||||
// metaKey + ctrlKey + b -> Toggles the current selection between bold and non-bold
|
// altKey + ctrlKey + b -> Toggles the current selection between bold and non-bold
|
||||||
// metaKey + ctrlKey + i -> Toggles the current selection between italic and non-italic
|
// altKey + ctrlKey + i -> Toggles the current selection between italic and non-italic
|
||||||
// metaKey + ctrlKey + r -> Removes the formatting tags from the current selection
|
// altKey + ctrlKey + f -> Removes the formatting tags from the current selection
|
||||||
// metaKey + ctrlKey + h -> Inserts a Horizontal Rule
|
// altKey + ctrlKey + z -> Inserts a Horizontal Rule
|
||||||
// metaKey + ctrlKey + l -> Toggles the text selection between an unordered list and a normal block
|
// altKey + ctrlKey + l -> Toggles the text selection between an unordered list and a normal block
|
||||||
// metaKey + ctrlKey + k -> Toggles the text selection between an ordered list and a normal block
|
// altKey + ctrlKey + k -> Toggles the text selection between an ordered list and a normal block
|
||||||
// metaKey + ctrlKey + o -> Draws a line through the middle of the current selection
|
// altKey + ctrlKey + o -> Draws a line through the middle of the current selection
|
||||||
// metaKey + ctrlKey + w -> Removes any hyperlink from the current selection
|
// altKey + ctrlKey + w -> Removes any hyperlink from the current selection
|
||||||
if ( !e.altKey && e.ctrlKey && e.metaKey && (_this.options.richTextFormatKey[ e.keyCode ]
|
if ( e.altKey && e.ctrlKey && !e.metaKey && (_this.options.richTextFormatKey[ e.keyCode ]
|
||||||
|| e.keyCode == 82
|
|| e.keyCode == 49
|
||||||
|| e.keyCode == 72
|
|| e.keyCode == 50
|
||||||
|
|| e.keyCode == 51
|
||||||
|
|| e.keyCode == 70
|
||||||
|
|| e.keyCode == 90
|
||||||
|| e.keyCode == 76
|
|| e.keyCode == 76
|
||||||
|| e.keyCode == 75
|
|| e.keyCode == 75
|
||||||
|| e.keyCode == 79
|
|| e.keyCode == 79
|
||||||
|
@ -121,10 +136,10 @@
|
||||||
if (e.keyCode == 85) {
|
if (e.keyCode == 85) {
|
||||||
document.execCommand('Underline')
|
document.execCommand('Underline')
|
||||||
}
|
}
|
||||||
if (e.keyCode == 82) {
|
if (e.keyCode == 70) {
|
||||||
document.execCommand('RemoveFormat')
|
document.execCommand('RemoveFormat')
|
||||||
}
|
}
|
||||||
if (e.keyCode == 72) {
|
if (e.keyCode == 90) {
|
||||||
document.execCommand('insertHorizontalRule')
|
document.execCommand('insertHorizontalRule')
|
||||||
}
|
}
|
||||||
if (e.keyCode == 76) {
|
if (e.keyCode == 76) {
|
||||||
|
@ -139,6 +154,15 @@
|
||||||
if (e.keyCode == 87) {
|
if (e.keyCode == 87) {
|
||||||
document.execCommand('Unlink')
|
document.execCommand('Unlink')
|
||||||
}
|
}
|
||||||
|
if (e.keyCode == 49) {
|
||||||
|
_this.toggleBlock('h1')
|
||||||
|
}
|
||||||
|
if (e.keyCode == 50) {
|
||||||
|
_this.toggleBlock('h2')
|
||||||
|
}
|
||||||
|
if (e.keyCode == 51) {
|
||||||
|
_this.toggleBlock('h3')
|
||||||
|
}
|
||||||
_this.log('content editable richtext key', e.keyCode)
|
_this.log('content editable richtext key', e.keyCode)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
204
app/assets/javascripts/app/lib/base/jquery.hotkeys.js
Normal file
204
app/assets/javascripts/app/lib/base/jquery.hotkeys.js
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
/*jslint browser: true*/
|
||||||
|
/*jslint jquery: true*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* jQuery Hotkeys Plugin
|
||||||
|
* Copyright 2010, John Resig
|
||||||
|
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||||
|
*
|
||||||
|
* Based upon the plugin by Tzury Bar Yochay:
|
||||||
|
* https://github.com/tzuryby/jquery.hotkeys
|
||||||
|
*
|
||||||
|
* Original idea by:
|
||||||
|
* Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* One small change is: now keys are passed by object { keys: '...' }
|
||||||
|
* Might be useful, when you want to pass some other data to your handler
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(jQuery) {
|
||||||
|
|
||||||
|
jQuery.hotkeys = {
|
||||||
|
version: "0.2.0",
|
||||||
|
|
||||||
|
specialKeys: {
|
||||||
|
8: "backspace",
|
||||||
|
9: "tab",
|
||||||
|
10: "return",
|
||||||
|
13: "return",
|
||||||
|
16: "shift",
|
||||||
|
17: "ctrl",
|
||||||
|
18: "alt",
|
||||||
|
19: "pause",
|
||||||
|
20: "capslock",
|
||||||
|
27: "esc",
|
||||||
|
32: "space",
|
||||||
|
33: "pageup",
|
||||||
|
34: "pagedown",
|
||||||
|
35: "end",
|
||||||
|
36: "home",
|
||||||
|
37: "left",
|
||||||
|
38: "up",
|
||||||
|
39: "right",
|
||||||
|
40: "down",
|
||||||
|
45: "insert",
|
||||||
|
46: "del",
|
||||||
|
59: ";",
|
||||||
|
61: "=",
|
||||||
|
96: "0",
|
||||||
|
97: "1",
|
||||||
|
98: "2",
|
||||||
|
99: "3",
|
||||||
|
100: "4",
|
||||||
|
101: "5",
|
||||||
|
102: "6",
|
||||||
|
103: "7",
|
||||||
|
104: "8",
|
||||||
|
105: "9",
|
||||||
|
106: "*",
|
||||||
|
107: "+",
|
||||||
|
109: "-",
|
||||||
|
110: ".",
|
||||||
|
111: "/",
|
||||||
|
112: "f1",
|
||||||
|
113: "f2",
|
||||||
|
114: "f3",
|
||||||
|
115: "f4",
|
||||||
|
116: "f5",
|
||||||
|
117: "f6",
|
||||||
|
118: "f7",
|
||||||
|
119: "f8",
|
||||||
|
120: "f9",
|
||||||
|
121: "f10",
|
||||||
|
122: "f11",
|
||||||
|
123: "f12",
|
||||||
|
144: "numlock",
|
||||||
|
145: "scroll",
|
||||||
|
173: "-",
|
||||||
|
186: ";",
|
||||||
|
187: "=",
|
||||||
|
188: ",",
|
||||||
|
189: "-",
|
||||||
|
190: ".",
|
||||||
|
191: "/",
|
||||||
|
192: "`",
|
||||||
|
219: "[",
|
||||||
|
220: "\\",
|
||||||
|
221: "]",
|
||||||
|
222: "'"
|
||||||
|
},
|
||||||
|
|
||||||
|
shiftNums: {
|
||||||
|
"`": "~",
|
||||||
|
"1": "!",
|
||||||
|
"2": "@",
|
||||||
|
"3": "#",
|
||||||
|
"4": "$",
|
||||||
|
"5": "%",
|
||||||
|
"6": "^",
|
||||||
|
"7": "&",
|
||||||
|
"8": "*",
|
||||||
|
"9": "(",
|
||||||
|
"0": ")",
|
||||||
|
"-": "_",
|
||||||
|
"=": "+",
|
||||||
|
";": ": ",
|
||||||
|
"'": "\"",
|
||||||
|
",": "<",
|
||||||
|
".": ">",
|
||||||
|
"/": "?",
|
||||||
|
"\\": "|"
|
||||||
|
},
|
||||||
|
|
||||||
|
// excludes: button, checkbox, file, hidden, image, password, radio, reset, search, submit, url
|
||||||
|
textAcceptingInputTypes: [
|
||||||
|
"text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime",
|
||||||
|
"datetime-local", "search", "color", "tel"],
|
||||||
|
|
||||||
|
// default input types not to bind to unless bound directly
|
||||||
|
textInputTypes: /textarea|input|select/i,
|
||||||
|
|
||||||
|
options: {
|
||||||
|
filterInputAcceptingElements: true,
|
||||||
|
filterTextInputs: true,
|
||||||
|
filterContentEditable: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function keyHandler(handleObj) {
|
||||||
|
if (typeof handleObj.data === "string") {
|
||||||
|
handleObj.data = {
|
||||||
|
keys: handleObj.data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only care when a possible input has been specified
|
||||||
|
if (!handleObj.data || !handleObj.data.keys || typeof handleObj.data.keys !== "string") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var origHandler = handleObj.handler,
|
||||||
|
keys = handleObj.data.keys.toLowerCase().split(" ");
|
||||||
|
|
||||||
|
handleObj.handler = function(event) {
|
||||||
|
// Don't fire in text-accepting inputs that we didn't directly bind to
|
||||||
|
if (this !== event.target &&
|
||||||
|
(jQuery.hotkeys.options.filterInputAcceptingElements &&
|
||||||
|
jQuery.hotkeys.textInputTypes.test(event.target.nodeName) ||
|
||||||
|
(jQuery.hotkeys.options.filterContentEditable && jQuery(event.target).attr('contenteditable')) ||
|
||||||
|
(jQuery.hotkeys.options.filterTextInputs &&
|
||||||
|
jQuery.inArray(event.target.type, jQuery.hotkeys.textAcceptingInputTypes) > -1))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[event.which],
|
||||||
|
character = String.fromCharCode(event.which).toLowerCase(),
|
||||||
|
modif = "",
|
||||||
|
possible = {};
|
||||||
|
|
||||||
|
jQuery.each(["alt", "ctrl", "shift"], function(index, specialKey) {
|
||||||
|
|
||||||
|
if (event[specialKey + 'Key'] && special !== specialKey) {
|
||||||
|
modif += specialKey + '+';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// metaKey is triggered off ctrlKey erronously
|
||||||
|
if (event.metaKey && !event.ctrlKey && special !== "meta") {
|
||||||
|
modif += "meta+";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.metaKey && special !== "meta" && modif.indexOf("alt+ctrl+shift+") > -1) {
|
||||||
|
modif = modif.replace("alt+ctrl+shift+", "hyper+");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (special) {
|
||||||
|
possible[modif + special] = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
possible[modif + character] = true;
|
||||||
|
possible[modif + jQuery.hotkeys.shiftNums[character]] = true;
|
||||||
|
|
||||||
|
// "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
|
||||||
|
if (modif === "shift+") {
|
||||||
|
possible[jQuery.hotkeys.shiftNums[character]] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0, l = keys.length; i < l; i++) {
|
||||||
|
if (possible[keys[i]]) {
|
||||||
|
return origHandler.apply(this, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery.each(["keydown", "keyup", "keypress"], function() {
|
||||||
|
jQuery.event.special[this] = {
|
||||||
|
add: keyHandler
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
})(jQuery || this.jQuery || window.jQuery);
|
|
@ -59,6 +59,9 @@ var originalShow = $.fn.popover.Constructor.prototype.show;
|
||||||
$.fn.popover.Constructor.prototype.show = function(){
|
$.fn.popover.Constructor.prototype.show = function(){
|
||||||
originalShow.call(this);
|
originalShow.call(this);
|
||||||
|
|
||||||
var maxHeight = $(this.options.viewport.selector).height() - 2 * this.options.viewport.padding;
|
// improved error handling - no exeption if no $tip exists
|
||||||
|
if (!this.$tip) {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.$tip.find('.popover-body').css('maxHeight', maxHeight);
|
this.$tip.find('.popover-body').css('maxHeight', maxHeight);
|
||||||
}
|
}
|
38
app/assets/javascripts/app/views/keyboard_shortcuts.jst.eco
Normal file
38
app/assets/javascripts/app/views/keyboard_shortcuts.jst.eco
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<div class="horizontal">
|
||||||
|
<div class="flex">
|
||||||
|
<% for area in @areas: %>
|
||||||
|
<% if area.location is 'left': %>
|
||||||
|
<h2><%- @T(area.headline) %></h2>
|
||||||
|
<% for item in area.content: %>
|
||||||
|
<% if item.where: %><p><i><%- @T(item.where) %></i></p><% end %>
|
||||||
|
<% for shortcut in item.shortcuts: %>
|
||||||
|
<% if shortcut.hotkeys: %><kbd>ctrl</kbd> <kbd>alt</kbd> <% end %>
|
||||||
|
<% if _.isArray(shortcut.key): %><% for key in shortcut.key: %> <kbd><%- key %></kbd><% end %>
|
||||||
|
<% else: %>
|
||||||
|
<kbd><%- shortcut.key %></kbd>
|
||||||
|
<% end %>
|
||||||
|
<%- @T(shortcut.description) %><br>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="flex">
|
||||||
|
<% for area in @areas: %>
|
||||||
|
<% if area.location is 'right': %>
|
||||||
|
<h2><%- @T(area.headline) %></h2>
|
||||||
|
<% for item in area.content: %>
|
||||||
|
<% if item.where: %><p><i><%- @T(item.where) %></i></p><% end %>
|
||||||
|
<% for shortcut in item.shortcuts: %>
|
||||||
|
<% if shortcut.hotkeys: %><kbd>ctrl</kbd> <kbd>alt</kbd> <% end %>
|
||||||
|
<% if _.isArray(shortcut.key): %><% for key in shortcut.key: %> <kbd><%- key %></kbd><% end %>
|
||||||
|
<% else: %>
|
||||||
|
<kbd><%- shortcut.key %></kbd>
|
||||||
|
<% end %>
|
||||||
|
<%- @T(shortcut.description) %><br>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -289,6 +289,9 @@ test( "i18n", function() {
|
||||||
translated = App.i18n.translateContent('§%s§ %s test', 123, 'xxx');
|
translated = App.i18n.translateContent('§%s§ %s test', 123, 'xxx');
|
||||||
equal( translated, '<kbd>123</kbd> xxx test', 'de-de - §%s§ %s' );
|
equal( translated, '<kbd>123</kbd> xxx test', 'de-de - §%s§ %s' );
|
||||||
|
|
||||||
|
translated = App.i18n.translateContent('//%s// %s test', 123, 'xxx');
|
||||||
|
equal( translated, '<del>123</del> xxx test', 'de-de - //%s// %s' );
|
||||||
|
|
||||||
translated = App.i18n.translateContent('\'%s\' %s test', 123, 'xxx');
|
translated = App.i18n.translateContent('\'%s\' %s test', 123, 'xxx');
|
||||||
equal( translated, ''123' xxx test', 'de-de - \'%s\' %s' );
|
equal( translated, ''123' xxx test', 'de-de - \'%s\' %s' );
|
||||||
|
|
||||||
|
@ -332,6 +335,9 @@ test( "i18n", function() {
|
||||||
translated = App.i18n.translateContent('§%s§ %s test', 123, 'xxx');
|
translated = App.i18n.translateContent('§%s§ %s test', 123, 'xxx');
|
||||||
equal( translated, '<kbd>123</kbd> xxx test', 'en-us - §%s§ %s' );
|
equal( translated, '<kbd>123</kbd> xxx test', 'en-us - §%s§ %s' );
|
||||||
|
|
||||||
|
translated = App.i18n.translateContent('//%s// %s test', 123, 'xxx');
|
||||||
|
equal( translated, '<del>123</del> xxx test', 'en-us - //%s// %s' );
|
||||||
|
|
||||||
translated = App.i18n.translateContent('\'%s\' %s test', 123, 'xxx');
|
translated = App.i18n.translateContent('\'%s\' %s test', 123, 'xxx');
|
||||||
equal( translated, ''123' xxx test', 'en-us - \'%s\' %s' );
|
equal( translated, ''123' xxx test', 'en-us - \'%s\' %s' );
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ elif [ "$LEVEL" == '2' ]; then
|
||||||
# only ticket action
|
# only ticket action
|
||||||
rm test/browser/aab_unit_test.rb
|
rm test/browser/aab_unit_test.rb
|
||||||
rm test/browser/aac_basic_richtext_test.rb
|
rm test/browser/aac_basic_richtext_test.rb
|
||||||
|
rm test/browser/aab_basic_urls_test.rb
|
||||||
rm test/browser/agent_organization_profile_test.rb
|
rm test/browser/agent_organization_profile_test.rb
|
||||||
rm test/browser/agent_user_*.rb
|
rm test/browser/agent_user_*.rb
|
||||||
rm test/browser/auth_test.rb
|
rm test/browser/auth_test.rb
|
||||||
|
@ -40,6 +41,7 @@ elif [ "$LEVEL" == '3' ]; then
|
||||||
# only profile action
|
# only profile action
|
||||||
rm test/browser/aab_unit_test.rb
|
rm test/browser/aab_unit_test.rb
|
||||||
rm test/browser/aac_basic_richtext_test.rb
|
rm test/browser/aac_basic_richtext_test.rb
|
||||||
|
rm test/browser/aab_basic_urls_test.rb
|
||||||
rm test/browser/agent_user_manage_test.rb
|
rm test/browser/agent_user_manage_test.rb
|
||||||
rm test/browser/agent_ticket_*.rb
|
rm test/browser/agent_ticket_*.rb
|
||||||
rm test/browser/auth_test.rb
|
rm test/browser/auth_test.rb
|
||||||
|
|
36
test/browser/aab_basic_urls_test.rb
Normal file
36
test/browser/aab_basic_urls_test.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
require 'browser_test_helper'
|
||||||
|
|
||||||
|
class AABBasicUrlsTest < TestCase
|
||||||
|
|
||||||
|
def test_logout
|
||||||
|
@browser = browser_instance
|
||||||
|
location(
|
||||||
|
url: "#{browser_url}/#logout",
|
||||||
|
)
|
||||||
|
location_check(
|
||||||
|
url: "#{browser_url}/#login",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_session
|
||||||
|
@browser = browser_instance
|
||||||
|
location(
|
||||||
|
url: "#{browser_url}/#system/sessions",
|
||||||
|
)
|
||||||
|
location_check(
|
||||||
|
url: "#{browser_url}/#login",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_profile
|
||||||
|
@browser = browser_instance
|
||||||
|
location(
|
||||||
|
url: "#{browser_url}/#profile/linked",
|
||||||
|
)
|
||||||
|
location_check(
|
||||||
|
url: "#{browser_url}/#login",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -272,7 +272,7 @@ class TestCase < Test::Unit::TestCase
|
||||||
instance.get(params[:url])
|
instance.get(params[:url])
|
||||||
|
|
||||||
# check if reload was successfull
|
# check if reload was successfull
|
||||||
if !instance.find_elements({ css: 'body' })[0] || instance.find_elements({ css: 'body' })[0].text =~ /unavailable or too busy/i
|
if !instance.find_elements(css: 'body')[0] || instance.find_elements(css: 'body')[0].text =~ /unavailable or too busy/i
|
||||||
instance.navigate.refresh
|
instance.navigate.refresh
|
||||||
end
|
end
|
||||||
screenshot(browser: instance, comment: 'location')
|
screenshot(browser: instance, comment: 'location')
|
||||||
|
|
Loading…
Reference in a new issue