Init version of new text modules.
This commit is contained in:
parent
83f5b6cabe
commit
14f636fec2
9 changed files with 777 additions and 492 deletions
|
@ -1599,6 +1599,12 @@ class App.ControllerForm extends App.Controller
|
||||||
|
|
||||||
lookupForm = @findForm(form)
|
lookupForm = @findForm(form)
|
||||||
|
|
||||||
|
# get contenteditable
|
||||||
|
for element in lookupForm.find('[contenteditable]')
|
||||||
|
name = $(element).data('name')
|
||||||
|
if name
|
||||||
|
param[name] = $(element).ceg()
|
||||||
|
|
||||||
# get form elements
|
# get form elements
|
||||||
array = lookupForm.serializeArray()
|
array = lookupForm.serializeArray()
|
||||||
|
|
||||||
|
|
|
@ -163,6 +163,8 @@ class LayoutRefCommunicationReply extends App.ControllerContent
|
||||||
maxlength: 2500
|
maxlength: 2500
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@$('[contenteditable]').textmodule()
|
||||||
|
|
||||||
detect_empty_textarea: =>
|
detect_empty_textarea: =>
|
||||||
if !@textarea.text()
|
if !@textarea.text()
|
||||||
@add_textarea_catcher()
|
@add_textarea_catcher()
|
||||||
|
|
|
@ -88,9 +88,6 @@ class App.TicketZoom extends App.Controller
|
||||||
if newTicketRaw.updated_by_id isnt @Session.get('id')
|
if newTicketRaw.updated_by_id isnt @Session.get('id')
|
||||||
App.TaskManager.notify( @task_key )
|
App.TaskManager.notify( @task_key )
|
||||||
|
|
||||||
# rerender edit box
|
|
||||||
@editDone = false
|
|
||||||
|
|
||||||
# remember current data
|
# remember current data
|
||||||
@ticketUpdatedAtLastCall = newTicketRaw.updated_at
|
@ticketUpdatedAtLastCall = newTicketRaw.updated_at
|
||||||
|
|
||||||
|
@ -163,6 +160,15 @@ class App.TicketZoom extends App.Controller
|
||||||
el: @el.find('.ticket-meta')
|
el: @el.find('.ticket-meta')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
new Edit(
|
||||||
|
ticket: @ticket
|
||||||
|
el: @el.find('.ticket-edit')
|
||||||
|
#el: @el.find('.edit')
|
||||||
|
form_meta: @form_meta
|
||||||
|
defaults: @taskGet('article')
|
||||||
|
ui: @
|
||||||
|
)
|
||||||
|
|
||||||
editTicket = (el) =>
|
editTicket = (el) =>
|
||||||
el.append('<form class="edit"></form>')
|
el.append('<form class="edit"></form>')
|
||||||
@editEl = el
|
@editEl = el
|
||||||
|
@ -170,7 +176,7 @@ class App.TicketZoom extends App.Controller
|
||||||
|
|
||||||
reset = (e) =>
|
reset = (e) =>
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
App.TaskManager.update( @task_key, { 'state': {} })
|
@taskReset()
|
||||||
show(@ticket)
|
show(@ticket)
|
||||||
|
|
||||||
show = (ticket) =>
|
show = (ticket) =>
|
||||||
|
@ -202,7 +208,7 @@ class App.TicketZoom extends App.Controller
|
||||||
form.find('[name="' + fieldNameToChange + '"]').replaceWith( newElement )
|
form.find('[name="' + fieldNameToChange + '"]').replaceWith( newElement )
|
||||||
|
|
||||||
defaults = ticket.attributes()
|
defaults = ticket.attributes()
|
||||||
task_state = App.TaskManager.get(@task_key).state || {}
|
task_state = @taskGet('ticket')
|
||||||
modelDiff = @getDiff( defaults, task_state )
|
modelDiff = @getDiff( defaults, task_state )
|
||||||
#if @isRole('Customer')
|
#if @isRole('Customer')
|
||||||
# delete defaults['state_id']
|
# delete defaults['state_id']
|
||||||
|
@ -351,17 +357,13 @@ class App.TicketZoom extends App.Controller
|
||||||
items: items
|
items: items
|
||||||
)
|
)
|
||||||
|
|
||||||
@ArticleView()
|
# show article
|
||||||
|
new ArticleView(
|
||||||
if force || !@editDone
|
ticket: @ticket
|
||||||
# reset form on force reload
|
ticket_article_ids: @ticket_article_ids
|
||||||
if force && _.isEmpty( App.TaskManager.get(@task_key).state )
|
el: @el.find('.ticket-article')
|
||||||
App.TaskManager.update( @task_key, { 'state': {} })
|
ui: @
|
||||||
@editDone = true
|
)
|
||||||
|
|
||||||
# rerender widget if it hasn't changed
|
|
||||||
if !@editWidget || _.isEmpty( App.TaskManager.get(@task_key).state )
|
|
||||||
@editWidget = @Edit()
|
|
||||||
|
|
||||||
# scroll to article if given
|
# scroll to article if given
|
||||||
if @article_id && document.getElementById( 'article-' + @article_id )
|
if @article_id && document.getElementById( 'article-' + @article_id )
|
||||||
|
@ -376,40 +378,30 @@ class App.TicketZoom extends App.Controller
|
||||||
|
|
||||||
@autosaveStart()
|
@autosaveStart()
|
||||||
|
|
||||||
|
|
||||||
ArticleView: =>
|
|
||||||
# show article
|
|
||||||
new ArticleView(
|
|
||||||
ticket: @ticket
|
|
||||||
ticket_article_ids: @ticket_article_ids
|
|
||||||
el: @el.find('.ticket-article')
|
|
||||||
ui: @
|
|
||||||
)
|
|
||||||
|
|
||||||
Edit: =>
|
|
||||||
# show edit
|
|
||||||
new Edit(
|
|
||||||
ticket: @ticket
|
|
||||||
el: @el.find('.ticket-edit')
|
|
||||||
#el: @el.find('.edit')
|
|
||||||
form_meta: @form_meta
|
|
||||||
task_key: @task_key
|
|
||||||
ui: @
|
|
||||||
)
|
|
||||||
|
|
||||||
autosaveStop: =>
|
autosaveStop: =>
|
||||||
@autosaveLast = {}
|
@autosaveLast = {}
|
||||||
@clearInterval( 'autosave' )
|
@clearInterval( 'autosave' )
|
||||||
|
|
||||||
autosaveStart: =>
|
autosaveStart: =>
|
||||||
if !@autosaveLast
|
if !@autosaveLast
|
||||||
@autosaveLast = App.TaskManager.get(@task_key).state || {}
|
@autosaveLast = @taskGet()
|
||||||
update = =>
|
update = =>
|
||||||
currentStore = @ticket.attributes()
|
#console.log('AR', @formParam( @el.find('.article-add') ) )
|
||||||
currentParams = @formParam( @el.find('.edit') )
|
currentStore =
|
||||||
|
ticket: @ticket.attributes()
|
||||||
|
article: {
|
||||||
|
type: ''
|
||||||
|
body: ''
|
||||||
|
internal: ''
|
||||||
|
}
|
||||||
|
currentParams =
|
||||||
|
ticket: @formParam( @el.find('.edit') )
|
||||||
|
article: @formParam( @el.find('.article-add') )
|
||||||
|
|
||||||
# get diff of model
|
# get diff of model
|
||||||
modelDiff = @getDiff( currentStore, currentParams )
|
modelDiff =
|
||||||
|
ticket: @getDiff( currentStore.ticket, currentParams.ticket )
|
||||||
|
article: @getDiff( currentStore.article, currentParams.article )
|
||||||
#console.log('modelDiff', modelDiff)
|
#console.log('modelDiff', modelDiff)
|
||||||
|
|
||||||
# get diff of last save
|
# get diff of last save
|
||||||
|
@ -420,9 +412,9 @@ class App.TicketZoom extends App.Controller
|
||||||
console.log('model DIFF ', modelDiff)
|
console.log('model DIFF ', modelDiff)
|
||||||
|
|
||||||
@autosaveLast = clone(currentParams)
|
@autosaveLast = clone(currentParams)
|
||||||
@markFormDiff( modelDiff )
|
@markFormDiff( modelDiff.ticket )
|
||||||
|
|
||||||
App.TaskManager.update( @task_key, { 'state': modelDiff })
|
@taskUpdateAll( modelDiff )
|
||||||
@interval( update, 3000, 'autosave' )
|
@interval( update, 3000, 'autosave' )
|
||||||
|
|
||||||
getDiff: (model, params) =>
|
getDiff: (model, params) =>
|
||||||
|
@ -584,11 +576,35 @@ class App.TicketZoom extends App.Controller
|
||||||
done: (r) =>
|
done: (r) =>
|
||||||
|
|
||||||
# reset form after save
|
# reset form after save
|
||||||
App.TaskManager.update( @task_key, { 'state': {} })
|
@taskReset()
|
||||||
|
|
||||||
@fetch( ticket.id, true )
|
@fetch( ticket.id, true )
|
||||||
)
|
)
|
||||||
|
|
||||||
|
taskGet: (area) =>
|
||||||
|
@localTaskData = App.TaskManager.get(@task_key).state || {}
|
||||||
|
if area
|
||||||
|
if !@localTaskData[area]
|
||||||
|
@localTaskData[area] = {}
|
||||||
|
return @localTaskData[area]
|
||||||
|
if !@localTaskData
|
||||||
|
@localTaskData = {}
|
||||||
|
@localTaskData
|
||||||
|
|
||||||
|
taskUpdate: (area, data) =>
|
||||||
|
@localTaskData[area] = data
|
||||||
|
App.TaskManager.update( @task_key, { 'state': @localTaskData })
|
||||||
|
|
||||||
|
taskUpdateAll: (data) =>
|
||||||
|
@localTaskData = data
|
||||||
|
App.TaskManager.update( @task_key, { 'state': @localTaskData })
|
||||||
|
|
||||||
|
taskReset: (area, data) =>
|
||||||
|
@localTaskData =
|
||||||
|
ticket: {}
|
||||||
|
article: {}
|
||||||
|
App.TaskManager.update( @task_key, { 'state': @localTaskData })
|
||||||
|
|
||||||
class TicketTitle extends App.Controller
|
class TicketTitle extends App.Controller
|
||||||
events:
|
events:
|
||||||
'blur .ticket-title-update': 'update'
|
'blur .ticket-title-update': 'update'
|
||||||
|
@ -653,38 +669,53 @@ class TicketMeta extends App.Controller
|
||||||
|
|
||||||
class Edit extends App.Controller
|
class Edit extends App.Controller
|
||||||
elements:
|
elements:
|
||||||
'textarea' : 'textarea'
|
'.js-textarea' : 'textarea'
|
||||||
'.edit-control-item' : 'editControlItem'
|
'.attachmentPlaceholder': 'attachmentPlaceholder'
|
||||||
'.edit-controls': 'editControls'
|
'.attachmentPlaceholder-inputHolder': 'attachmentInputHolder'
|
||||||
'.recipient-picker': 'recipientPicker'
|
'.attachmentPlaceholder-hint': 'attachmentHint'
|
||||||
'.recipient-list': 'recipientList'
|
'.article-add': 'ticketEdit'
|
||||||
'.recipient-list .list-arrow': 'recipientListArrow'
|
'.attachments': 'attachmentsHolder'
|
||||||
'.js-attachment': 'attachmentHolder'
|
'.attachmentUpload': 'attachmentUpload'
|
||||||
'.js-attachment-text': 'attachmentText'
|
'.attachmentUpload-progressBar':'progressBar'
|
||||||
'.bubble-placeholder-hint': 'bubblePlaceholderHint'
|
'.js-percentage': 'progressText'
|
||||||
|
#'.edit-control-item' : 'editControlItem'
|
||||||
|
#'.edit-controls': 'editControls'
|
||||||
|
#'.recipient-picker': 'recipientPicker'
|
||||||
|
#'.recipient-list': 'recipientList'
|
||||||
|
#'.recipient-list .list-arrow': 'recipientListArrow'
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click .submit': 'update'
|
#'click .submit': 'update'
|
||||||
'click [data-type="reset"]': 'reset'
|
'click [data-type="reset"]': 'reset'
|
||||||
'click .visibility-toggle': 'toggle_visibility'
|
'click .visibility-toggle': 'toggleVisibility'
|
||||||
'click .pop-selectable': 'selectArticleType'
|
'click .pop-selectable': 'selectArticleType'
|
||||||
'click .pop-selected': 'showSelectableArticleType'
|
'click .pop-selected': 'showSelectableArticleType'
|
||||||
'focus textarea': 'open_textarea'
|
|
||||||
'input textarea': 'detect_empty_textarea'
|
|
||||||
'click .recipient-picker': 'toggle_recipients'
|
'click .recipient-picker': 'toggle_recipients'
|
||||||
'click .recipient-list': 'stopPropagation'
|
'click .recipient-list': 'stopPropagation'
|
||||||
'click .list-entry-type div': 'change_type'
|
'click .list-entry-type div': 'change_type'
|
||||||
'submit .recipient-list form': 'add_recipient'
|
'submit .recipient-list form': 'add_recipient'
|
||||||
|
'focus .js-textarea': 'open_textarea'
|
||||||
|
'input .js-textarea': 'detect_empty_textarea'
|
||||||
|
'dragenter': 'onDragenter'
|
||||||
|
'dragleave': 'onDragleave'
|
||||||
|
'drop': 'onFileDrop'
|
||||||
|
'change input[type=file]': 'onFilePick'
|
||||||
|
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
|
||||||
@textareaHeight =
|
@textareaHeight =
|
||||||
open: 148
|
open: 148
|
||||||
closed: 38
|
closed: 20
|
||||||
|
|
||||||
|
@dragEventCounter = 0
|
||||||
|
@attachments = []
|
||||||
|
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
|
if @textarea.text().trim()
|
||||||
|
@ticketEdit.addClass('is-open')
|
||||||
|
|
||||||
stopPropagation: (e) ->
|
stopPropagation: (e) ->
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
|
@ -729,74 +760,36 @@ class Edit extends App.Controller
|
||||||
icon: 'note'
|
icon: 'note'
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
console.log('DEvvvvvV', @defaults)
|
||||||
@html App.view('ticket_zoom/edit')(
|
@html App.view('ticket_zoom/edit')(
|
||||||
ticket: ticket
|
ticket: ticket
|
||||||
type: @type
|
|
||||||
articleTypes: articleTypes
|
articleTypes: articleTypes
|
||||||
|
article: @defaults
|
||||||
isCustomer: @isRole('Customer')
|
isCustomer: @isRole('Customer')
|
||||||
)
|
)
|
||||||
|
|
||||||
@form_id = App.ControllerForm.formId()
|
configure_attributes = [
|
||||||
defaults = ticket.attributes()
|
{ name: 'customer_id', display: 'Recipients', tag: 'user_autocompletion', null: false, placeholder: 'Enter Person or Organisation/Company', minLengt: 2, disableCreateUser: false },
|
||||||
if @isRole('Customer')
|
]
|
||||||
delete defaults['state_id']
|
|
||||||
delete defaults['state']
|
|
||||||
if !_.isEmpty( App.TaskManager.get(@task_key).state )
|
|
||||||
defaults = App.TaskManager.get(@task_key).state
|
|
||||||
|
|
||||||
new App.ControllerForm(
|
controller = new App.ControllerForm(
|
||||||
el: @el.find('.form-article-update')
|
el: @$('.recipients')
|
||||||
form_id: @form_id
|
model:
|
||||||
model: App.TicketArticle
|
configure_attributes: configure_attributes,
|
||||||
screen: 'edit'
|
|
||||||
filter:
|
|
||||||
type_id: [1,9,5]
|
|
||||||
params: defaults
|
|
||||||
dependency: [
|
|
||||||
{
|
|
||||||
bind: {
|
|
||||||
name: 'type_id'
|
|
||||||
relation: 'TicketArticleType'
|
|
||||||
value: ['email']
|
|
||||||
},
|
|
||||||
change: {
|
|
||||||
action: 'show'
|
|
||||||
name: ['to', 'cc'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
bind: {
|
|
||||||
name: 'type_id'
|
|
||||||
relation: 'TicketArticleType'
|
|
||||||
value: ['note', 'phone', 'twitter status']
|
|
||||||
},
|
|
||||||
change: {
|
|
||||||
action: 'hide'
|
|
||||||
name: ['to', 'cc'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
bind: {
|
|
||||||
name: 'type_id'
|
|
||||||
relation: 'TicketArticleType'
|
|
||||||
value: ['twitter direct-message']
|
|
||||||
},
|
|
||||||
change: {
|
|
||||||
action: 'show'
|
|
||||||
name: ['to'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# start auto save
|
@$('[data-name="body"]').ce({
|
||||||
#@autosaveStart()
|
mode: 'textonly'
|
||||||
|
multiline: true
|
||||||
|
maxlength: 2500
|
||||||
|
})
|
||||||
|
|
||||||
|
@form_id = App.ControllerForm.formId()
|
||||||
|
|
||||||
# show text module UI
|
# show text module UI
|
||||||
if !@isRole('Customer')
|
if !@isRole('Customer')
|
||||||
textModule = new App.WidgetTextModule(
|
textModule = new App.WidgetTextModule(
|
||||||
el: @textarea
|
el: @el
|
||||||
data:
|
data:
|
||||||
ticket: ticket
|
ticket: ticket
|
||||||
)
|
)
|
||||||
|
@ -873,13 +866,16 @@ class Edit extends App.Controller
|
||||||
console.log "add recipient", e
|
console.log "add recipient", e
|
||||||
# store recipient
|
# store recipient
|
||||||
|
|
||||||
toggle_visibility: ->
|
toggleVisibility: ->
|
||||||
if @el.hasClass('is-public')
|
item = @$('.article-add')
|
||||||
@el.removeClass('is-public')
|
if item.hasClass('is-public')
|
||||||
@el.addClass('is-internal')
|
item.removeClass('is-public')
|
||||||
|
item.addClass('is-internal')
|
||||||
|
@$('[name="internal"]').val('true')
|
||||||
else
|
else
|
||||||
@el.addClass('is-public')
|
item.addClass('is-public')
|
||||||
@el.removeClass('is-internal')
|
item.removeClass('is-internal')
|
||||||
|
@$('[name="internal"]').val('')
|
||||||
|
|
||||||
showSelectableArticleType: =>
|
showSelectableArticleType: =>
|
||||||
@el.find('.pop-selector').removeClass('hide')
|
@el.find('.pop-selector').removeClass('hide')
|
||||||
|
@ -903,59 +899,60 @@ class Edit extends App.Controller
|
||||||
if @type
|
if @type
|
||||||
typeIcon.removeClass @type
|
typeIcon.removeClass @type
|
||||||
@type = type
|
@type = type
|
||||||
|
@$('[name="type"]').val(type)
|
||||||
typeIcon.addClass @type
|
typeIcon.addClass @type
|
||||||
|
|
||||||
detect_empty_textarea: =>
|
detect_empty_textarea: =>
|
||||||
if !@textarea.val()
|
if !@textarea.text().trim()
|
||||||
@add_textarea_catcher()
|
@add_textarea_catcher()
|
||||||
else
|
else
|
||||||
@remove_textarea_catcher()
|
@remove_textarea_catcher()
|
||||||
|
|
||||||
open_textarea: =>
|
open_textarea: =>
|
||||||
if !@textareaCatcher and !@textarea.val()
|
console.log('OT', @textareaCatcher , @textarea.text().trim() , @attachments.length)
|
||||||
@el.addClass('is-open')
|
if !@textareaCatcher and !@textarea.text().trim() and !@attachments.length
|
||||||
|
@ticketEdit.addClass('is-open')
|
||||||
|
|
||||||
@textarea.velocity
|
@textarea.velocity
|
||||||
properties:
|
properties:
|
||||||
height: "#{ @textareaHeight.open - 38 }px"
|
minHeight: "#{ @textareaHeight.open - 38 }px"
|
||||||
marginBottom: 38
|
marginBottom: 38
|
||||||
options:
|
options:
|
||||||
duration: 300
|
duration: 300
|
||||||
easing: 'easeOutQuad'
|
easing: 'easeOutQuad'
|
||||||
|
complete: => @add_textarea_catcher()
|
||||||
|
|
||||||
# scroll to bottom
|
# scroll to bottom
|
||||||
@textarea.velocity "scroll",
|
# @textarea.velocity "scroll",
|
||||||
container: @textarea.scrollParent()
|
# container: @textarea.scrollParent()
|
||||||
offset: 99999
|
# offset: 99999
|
||||||
duration: 300
|
# duration: 300
|
||||||
easing: 'easeOutQuad'
|
# easing: 'easeOutQuad'
|
||||||
queue: false
|
# queue: false
|
||||||
|
|
||||||
@editControlItem.velocity "transition.slideRightIn",
|
# @editControlItem.velocity "transition.slideRightIn",
|
||||||
duration: 300
|
# duration: 300
|
||||||
stagger: 50
|
# stagger: 50
|
||||||
drag: true
|
# drag: true
|
||||||
|
|
||||||
# move attachment text to the left bottom (bottom happens automatically)
|
# move attachment text to the left bottom (bottom happens automatically)
|
||||||
|
|
||||||
@attachmentHolder.velocity
|
@attachmentPlaceholder.velocity
|
||||||
properties:
|
properties:
|
||||||
translateX: -@attachmentText.position().left + "px"
|
translateX: -@attachmentInputHolder.position().left + "px"
|
||||||
options:
|
options:
|
||||||
duration: 300
|
duration: 300
|
||||||
easing: 'easeOutQuad'
|
easing: 'easeOutQuad'
|
||||||
|
|
||||||
@bubblePlaceholderHint.velocity
|
@attachmentHint.velocity
|
||||||
properties:
|
properties:
|
||||||
opacity: 0
|
opacity: 0
|
||||||
options:
|
options:
|
||||||
duration: 300
|
duration: 300
|
||||||
|
|
||||||
@add_textarea_catcher()
|
|
||||||
|
|
||||||
add_textarea_catcher: ->
|
add_textarea_catcher: ->
|
||||||
@textareaCatcher = new App.clickCatcher
|
@textareaCatcher = new App.clickCatcher
|
||||||
holder: @el.offsetParent()
|
holder: @ticketEdit.offsetParent()
|
||||||
callback: @close_textarea
|
callback: @close_textarea
|
||||||
zIndexScale: 4
|
zIndexScale: 4
|
||||||
|
|
||||||
|
@ -966,168 +963,107 @@ class Edit extends App.Controller
|
||||||
|
|
||||||
close_textarea: =>
|
close_textarea: =>
|
||||||
@remove_textarea_catcher()
|
@remove_textarea_catcher()
|
||||||
if !@textarea.val()
|
if !@textarea.text().trim() && !@attachments.length
|
||||||
|
|
||||||
@textarea.velocity
|
@textarea.velocity
|
||||||
properties:
|
properties:
|
||||||
height: "#{ @textareaHeight.closed }px"
|
minHeight: "#{ @textareaHeight.closed }px"
|
||||||
marginBottom: 0
|
marginBottom: 0
|
||||||
options:
|
options:
|
||||||
duration: 300
|
duration: 300
|
||||||
easing: 'easeOutQuad'
|
easing: 'easeOutQuad'
|
||||||
complete: => @el.removeClass('is-open')
|
complete: => @ticketEdit.removeClass('is-open')
|
||||||
|
|
||||||
@attachmentHolder.velocity
|
@attachmentPlaceholder.velocity
|
||||||
properties:
|
properties:
|
||||||
translateX: 0
|
translateX: 0
|
||||||
options:
|
options:
|
||||||
duration: 300
|
duration: 300
|
||||||
easing: 'easeOutQuad'
|
easing: 'easeOutQuad'
|
||||||
|
|
||||||
@bubblePlaceholderHint.velocity
|
@attachmentHint.velocity
|
||||||
properties:
|
properties:
|
||||||
opacity: 1
|
opacity: 1
|
||||||
options:
|
options:
|
||||||
duration: 300
|
duration: 300
|
||||||
|
|
||||||
@editControlItem.css('display', 'none')
|
# @editControlItem.css('display', 'none')
|
||||||
|
|
||||||
autosaveStop: =>
|
onDragenter: (event) =>
|
||||||
@clearInterval( 'autosave' )
|
# on the first event,
|
||||||
|
# open textarea (it will only open if its closed)
|
||||||
|
@open_textarea() if @dragEventCounter is 0
|
||||||
|
|
||||||
autosaveStart: =>
|
@dragEventCounter++
|
||||||
@autosaveLast = _.clone( @ui.formDefault )
|
@ticketEdit.addClass('is-dropTarget')
|
||||||
update = =>
|
|
||||||
currentData = @formParam( @el.find('.ticket-update') )
|
|
||||||
diff = difference( @autosaveLast, currentData )
|
|
||||||
if !@autosaveLast || ( diff && !_.isEmpty( diff ) )
|
|
||||||
@autosaveLast = currentData
|
|
||||||
@log 'notice', 'form hash changed', diff, currentData
|
|
||||||
@el.find('.edit').addClass('form-changed')
|
|
||||||
@el.find('.edit').find('.reset-message').show()
|
|
||||||
@el.find('.edit').find('.reset-message').removeClass('hide')
|
|
||||||
App.TaskManager.update( @task_key, { 'state': currentData })
|
|
||||||
@interval( update, 3000, 'autosave' )
|
|
||||||
|
|
||||||
update: (e) =>
|
onDragleave: (event) =>
|
||||||
e.preventDefault()
|
@dragEventCounter--
|
||||||
#@autosaveStop()
|
|
||||||
params = @formParam(e.target)
|
|
||||||
|
|
||||||
# get ticket
|
@ticketEdit.removeClass('is-dropTarget') if @dragEventCounter is 0
|
||||||
ticket = App.Ticket.fullLocal( @ticket.id )
|
|
||||||
|
|
||||||
@log 'notice', 'update', params, ticket
|
onFileDrop: (event) =>
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
files = event.originalEvent.dataTransfer.files
|
||||||
|
@ticketEdit.removeClass('is-dropTarget')
|
||||||
|
|
||||||
# update local ticket
|
@queueUpload(files)
|
||||||
|
|
||||||
# create local article
|
onFilePick: (event) =>
|
||||||
|
@open_textarea()
|
||||||
|
@queueUpload(event.target.files)
|
||||||
|
|
||||||
|
queueUpload: (files) ->
|
||||||
|
@uploadQueue ?= []
|
||||||
|
|
||||||
# find sender_id
|
# add files
|
||||||
if @isRole('Customer')
|
for file in files
|
||||||
sender = App.TicketArticleSender.findByAttribute( 'name', 'Customer' )
|
@uploadQueue.push(file)
|
||||||
type = App.TicketArticleType.findByAttribute( 'name', 'web' )
|
|
||||||
params.type_id = type.id
|
|
||||||
params.sender_id = sender.id
|
|
||||||
else
|
|
||||||
sender = App.TicketArticleSender.findByAttribute( 'name', 'Agent' )
|
|
||||||
type = App.TicketArticleType.find( params['type_id'] )
|
|
||||||
params.sender_id = sender.id
|
|
||||||
|
|
||||||
# update ticket
|
@workOfUploadQueue()
|
||||||
for key, value of params
|
|
||||||
ticket[key] = value
|
|
||||||
|
|
||||||
# check owner assignment
|
workOfUploadQueue: =>
|
||||||
if !@isRole('Customer')
|
if !@uploadQueue.length
|
||||||
if !ticket['owner_id']
|
|
||||||
ticket['owner_id'] = 1
|
|
||||||
|
|
||||||
# check if title exists
|
|
||||||
if !ticket['title']
|
|
||||||
alert( App.i18n.translateContent('Title needed') )
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# validate email params
|
file = @uploadQueue.shift()
|
||||||
if type.name is 'email'
|
# console.log "working of", file, "from", @uploadQueue
|
||||||
|
@fakeUpload file.name, file.size, @workOfUploadQueue
|
||||||
|
|
||||||
# check if recipient exists
|
humanFileSize: (size) =>
|
||||||
if !params['to'] && !params['cc']
|
i = Math.floor( Math.log(size) / Math.log(1024) )
|
||||||
alert( App.i18n.translateContent('Need recipient in "To" or "Cc".') )
|
return ( size / Math.pow(1024, i) ).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]
|
||||||
return
|
|
||||||
|
|
||||||
# check if message exists
|
updateUploadProgress: (progress) =>
|
||||||
if !params['body']
|
@progressBar.width(progress + "%")
|
||||||
alert( App.i18n.translateContent('Text needed') )
|
@progressText.text(progress)
|
||||||
return
|
|
||||||
|
|
||||||
# check attachment
|
if progress is 100
|
||||||
if params['body']
|
@attachmentPlaceholder.removeClass('hide')
|
||||||
attachmentTranslated = App.i18n.translateContent('Attachment')
|
@attachmentUpload.addClass('hide')
|
||||||
attachmentTranslatedRegExp = new RegExp( attachmentTranslated, 'i' )
|
|
||||||
if params['body'].match(/attachment/i) || params['body'].match( attachmentTranslatedRegExp )
|
|
||||||
if !confirm( App.i18n.translateContent('You use attachment in text but no attachment is attached. Do you want to continue?') )
|
|
||||||
#@autosaveStart()
|
|
||||||
return
|
|
||||||
|
|
||||||
# submit ticket & article
|
fakeUpload: (fileName, fileSize, callback) ->
|
||||||
@log 'notice', 'update ticket', ticket
|
@attachmentPlaceholder.addClass('hide')
|
||||||
|
@attachmentUpload.removeClass('hide')
|
||||||
|
|
||||||
# disable form
|
progress = 0;
|
||||||
@formDisable(e)
|
duration = fileSize / 1024
|
||||||
|
|
||||||
# validate ticket
|
for i in [0..100]
|
||||||
errors = ticket.validate(
|
setTimeout @updateUploadProgress, i*duration/100 , i
|
||||||
screen: 'edit'
|
|
||||||
)
|
|
||||||
if errors
|
|
||||||
@log 'error', 'update', errors
|
|
||||||
|
|
||||||
@log 'error', errors
|
setTimeout (=>
|
||||||
@formValidate(
|
callback()
|
||||||
form: e.target
|
@renderAttachment(fileName, fileSize)
|
||||||
errors: errors
|
), duration
|
||||||
screen: 'edit'
|
|
||||||
)
|
|
||||||
@formEnable(e)
|
|
||||||
#@autosaveStart()
|
|
||||||
return
|
|
||||||
|
|
||||||
# validate article
|
renderAttachment: (fileName, fileSize) =>
|
||||||
articleAttributes = App.TicketArticle.attributesGet( 'edit' )
|
@attachments.push([fileName, fileSize])
|
||||||
if params['body'] || ( articleAttributes['body'] && articleAttributes['body']['null'] is false )
|
@attachmentsHolder.append App.view('ticket_zoom/attachment')
|
||||||
article = new App.TicketArticle
|
fileName: fileName
|
||||||
params.from = @Session.get().displayName()
|
fileSize: @humanFileSize(fileSize)
|
||||||
params.ticket_id = ticket.id
|
|
||||||
params.form_id = @form_id
|
|
||||||
|
|
||||||
if !params['internal']
|
|
||||||
params['internal'] = false
|
|
||||||
|
|
||||||
@log 'notice', 'update article', params, sender
|
|
||||||
article.load(params)
|
|
||||||
errors = article.validate()
|
|
||||||
if errors
|
|
||||||
@log 'error', 'update article', errors
|
|
||||||
@formValidate(
|
|
||||||
form: e.target
|
|
||||||
errors: errors
|
|
||||||
screen: 'edit'
|
|
||||||
)
|
|
||||||
@formEnable(e)
|
|
||||||
@autosaveStart()
|
|
||||||
return
|
|
||||||
|
|
||||||
ticket.article = article
|
|
||||||
ticket.save(
|
|
||||||
done: (r) =>
|
|
||||||
|
|
||||||
# reset form after save
|
|
||||||
App.TaskManager.update( @task_key, { 'state': {} })
|
|
||||||
|
|
||||||
@ui.fetch( ticket.id, true )
|
|
||||||
)
|
|
||||||
|
|
||||||
reset: (e) =>
|
reset: (e) =>
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
|
@ -2,38 +2,31 @@ class App.WidgetTextModule extends App.Controller
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
|
||||||
@lastData = {}
|
# remember instances
|
||||||
customItemTemplate = "<div><span /> <small /></div>"
|
@bindElements = []
|
||||||
elementFactory = (element, e) ->
|
if @selector
|
||||||
template = $(customItemTemplate).find('span')
|
@bindElements = @$( @selector ).textmodule()
|
||||||
.text(e.val).end()
|
else
|
||||||
.find('small')
|
@bindElements = @$('[contenteditable]').textmodule()
|
||||||
.text("(" + e.keywords + ")").end()
|
@update()
|
||||||
element.append(template)
|
|
||||||
|
|
||||||
@el.parent().find('textarea').sew(
|
|
||||||
values: @reload(@data)
|
|
||||||
token: '::'
|
|
||||||
elementFactory: elementFactory
|
|
||||||
)
|
|
||||||
|
|
||||||
@subscribeId = App.TextModule.subscribe(@update, initFetch: true )
|
@subscribeId = App.TextModule.subscribe(@update, initFetch: true )
|
||||||
|
|
||||||
release: =>
|
release: =>
|
||||||
App.TextModule.unsubscribe(@subscribeId)
|
App.TextModule.unsubscribe(@subscribeId)
|
||||||
|
|
||||||
reload: (data = false) =>
|
reload: (data) =>
|
||||||
if data
|
return if !data
|
||||||
@lastData['data'] = data
|
|
||||||
@update()
|
@update()
|
||||||
|
|
||||||
update: =>
|
update: =>
|
||||||
all = App.TextModule.all()
|
allRaw = App.TextModule.all()
|
||||||
values = [{val: '-', keywords: '-'}]
|
all = []
|
||||||
ui = @lastData || @
|
ui = @data || @
|
||||||
for item in all
|
for item in allRaw
|
||||||
if item.active is true
|
if item.active is true
|
||||||
contentNew = item.content.replace( /<%=\s{0,2}(.+?)\s{0,2}%>/g, ( all, key ) ->
|
attributes = item.attributes()
|
||||||
|
attributes.content = attributes.content.replace( /<%=\s{0,2}(.+?)\s{0,2}%>/g, ( index, key ) ->
|
||||||
key = key.replace( /@/g, 'ui.data.' )
|
key = key.replace( /@/g, 'ui.data.' )
|
||||||
varString = "#{key}" + ''
|
varString = "#{key}" + ''
|
||||||
# console.log( "tag replacement env: ", ui.data)
|
# console.log( "tag replacement env: ", ui.data)
|
||||||
|
@ -45,16 +38,10 @@ class App.WidgetTextModule extends App.Controller
|
||||||
key = ''
|
key = ''
|
||||||
return key
|
return key
|
||||||
)
|
)
|
||||||
value = { val: contentNew, keywords: item.keywords || item.name }
|
all.push attributes
|
||||||
values.push value
|
|
||||||
|
|
||||||
if values.length isnt 1
|
|
||||||
values.shift()
|
|
||||||
|
|
||||||
# set new data
|
# set new data
|
||||||
if @el[0]
|
if @bindElements[0]
|
||||||
if $(@el[0]).data()
|
for element in @bindElements
|
||||||
if $(@el[0]).data().plugin_sew
|
if $(element).data().plugin_textmodule
|
||||||
$(@el[0]).data().plugin_sew.options.values = values
|
$(element).data().plugin_textmodule.collection = all
|
||||||
|
|
||||||
return values
|
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
#
|
#
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var DEFAULTS = {
|
var pluginName = 'ce',
|
||||||
|
defaults = {
|
||||||
mode: 'richtext',
|
mode: 'richtext',
|
||||||
multiline: true,
|
multiline: true,
|
||||||
allowKey: {
|
allowKey: {
|
||||||
|
@ -38,24 +39,157 @@
|
||||||
73: true, // i
|
73: true, // i
|
||||||
85: true, // u
|
85: true, // u
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function Plugin( element, options ) {
|
||||||
|
this.element = element;
|
||||||
|
this.$element = $(element)
|
||||||
|
|
||||||
|
this.options = $.extend( {}, defaults, options) ;
|
||||||
|
|
||||||
|
this._defaults = defaults;
|
||||||
|
this._name = pluginName;
|
||||||
|
|
||||||
|
this.preventInput = false
|
||||||
|
|
||||||
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Plugin.prototype.init = function () {
|
||||||
|
// process placeholder
|
||||||
|
if ( this.options.placeholder ) {
|
||||||
|
this.updatePlaceholder( 'add' )
|
||||||
|
this.$element.on('focus', $.proxy(function (e) {
|
||||||
|
this.updatePlaceholder( 'remove' )
|
||||||
|
}, this)).on('blur', $.proxy(function (e) {
|
||||||
|
this.updatePlaceholder( 'add' )
|
||||||
|
}, this))
|
||||||
|
}
|
||||||
|
|
||||||
|
// maxlength check
|
||||||
|
//this.options.maxlength = 10
|
||||||
|
if ( this.options.maxlength ) {
|
||||||
|
this.$element.on('keydown', $.proxy(function (e) {
|
||||||
|
console.log('maxlength', e.keyCode, this.allowKey(e))
|
||||||
|
// check control key
|
||||||
|
if ( this.allowKey(e) ) {
|
||||||
|
this.maxLengthOk()
|
||||||
|
}
|
||||||
|
// check type ahead key
|
||||||
|
else {
|
||||||
|
if ( !this.maxLengthOk( true ) ) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, this)).on('keyup', $.proxy(function (e) {
|
||||||
|
// check control key
|
||||||
|
if ( this.allowKey(e) ) {
|
||||||
|
this.maxLengthOk()
|
||||||
|
}
|
||||||
|
// check type ahead key
|
||||||
|
else {
|
||||||
|
if ( !this.maxLengthOk( true ) ) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, this)).on('focus', $.proxy(function (e) {
|
||||||
|
this.maxLengthOk()
|
||||||
|
}, this)).on('blur', $.proxy(function (e) {
|
||||||
|
this.maxLengthOk()
|
||||||
|
}, this))
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle enter
|
||||||
|
this.$element.on('keydown', $.proxy(function (e) {
|
||||||
|
console.log('keydown', e.keyCode)
|
||||||
|
if (this.preventInput) {
|
||||||
|
console.log('preventInput', this.preventInput)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// trap the return key being pressed
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
// disbale multi line
|
||||||
|
if ( !this.options.multiline ) {
|
||||||
|
e.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// limit check
|
||||||
|
if ( !this.maxLengthOk( true ) ) {
|
||||||
|
e.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.options.mode === 'textonly' ) {
|
||||||
|
document.execCommand('insertHTML', false, "\n")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.execCommand('insertHTML', false, '<br>')
|
||||||
|
}
|
||||||
|
// prevent the default behaviour of return key pressed
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}, this))
|
||||||
|
|
||||||
|
// just paste text
|
||||||
|
if ( this.options.mode === 'textonly' ) {
|
||||||
|
this.$element.on('paste', $.proxy(function (e) {
|
||||||
|
var text = (e.originalEvent || e).clipboardData.getData('text/plain')
|
||||||
|
var overlimit = false
|
||||||
|
if (text) {
|
||||||
|
|
||||||
|
// replace new lines
|
||||||
|
if ( !this.options.multiline ) {
|
||||||
|
text = text.replace(/\n/g, '')
|
||||||
|
text = text.replace(/\r/g, '')
|
||||||
|
text = text.replace(/\t/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// limit length, limit paste string
|
||||||
|
if ( this.options.maxlength ) {
|
||||||
|
var pasteLength = text.length
|
||||||
|
var currentLength = this.$element.text().length
|
||||||
|
var overSize = ( currentLength + pasteLength ) - this.options.maxlength
|
||||||
|
if ( overSize > 0 ) {
|
||||||
|
text = text.substr( 0, pasteLength - overSize )
|
||||||
|
overlimit = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert new text
|
||||||
|
e.preventDefault()
|
||||||
|
document.execCommand('inserttext', false, text)
|
||||||
|
this.maxLengthOk( overlimit )
|
||||||
|
}
|
||||||
|
|
||||||
|
}, this))
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable rich text b/u/i
|
||||||
|
if ( this.options.mode === 'textonly' ) {
|
||||||
|
this.$element.on('keydown', $.proxy(function (e) {
|
||||||
|
if ( this.richTextKey(e) ) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}, this))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// add/remove placeholder
|
// add/remove placeholder
|
||||||
var updatePlaceholder = function(target, type) {
|
Plugin.prototype.updatePlaceholder = function(type) {
|
||||||
var options = target.data('ce.options')
|
var text = this.$element.text().trim()
|
||||||
var text = target.text().trim()
|
var placeholder = '<span class="placeholder">' + this.options.placeholder + '</span>'
|
||||||
var placeholder = '<span class="placeholder">' + options.placeholder + '</span>'
|
|
||||||
|
|
||||||
// add placholder if no text exists
|
// add placholder if no text exists
|
||||||
if ( type === 'add') {
|
if ( type === 'add') {
|
||||||
if ( !text ) {
|
if ( !text ) {
|
||||||
target.html( placeholder )
|
this.$element.html( placeholder )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty placeholder text
|
// empty placeholder text
|
||||||
else {
|
else {
|
||||||
if ( text === options.placeholder ) {
|
if ( text === this.options.placeholder ) {
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
document.execCommand('selectAll', false, '');
|
document.execCommand('selectAll', false, '');
|
||||||
document.execCommand('delete', false, '');
|
document.execCommand('delete', false, '');
|
||||||
|
@ -66,178 +200,85 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// max length check
|
// disable/enable input
|
||||||
var maxLengthOk = function(field, typeAhead) {
|
Plugin.prototype.input = function(type) {
|
||||||
var options = field.data('ce.options')
|
if (type === 'off') {
|
||||||
if (!options) {
|
this.preventInput = true
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
this.preventInput = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var length = field.text().length
|
// max length check
|
||||||
|
Plugin.prototype.maxLengthOk = function(typeAhead) {
|
||||||
|
var length = this.$element.text().length
|
||||||
if (typeAhead) {
|
if (typeAhead) {
|
||||||
length = length + 1
|
length = length + 1
|
||||||
}
|
}
|
||||||
if ( length > options.maxlength ) {
|
if ( length > this.options.maxlength ) {
|
||||||
field.addClass('invalid')
|
this.$element.addClass('invalid')
|
||||||
setTimeout(function(){
|
setTimeout($.proxy(function(){
|
||||||
field.removeClass('invalid')
|
this.$element.removeClass('invalid')
|
||||||
}, 1000);
|
}, this), 1000)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if key is allowed, even if length limit is reached
|
// check if key is allowed, even if length limit is reached
|
||||||
var allowKey = function(e) {
|
Plugin.prototype.allowKey = function(e) {
|
||||||
var options = $(e.target).data('ce.options')
|
if ( this.options.allowKey[ e.keyCode ] ) {
|
||||||
|
|
||||||
if ( options.allowKey[ e.keyCode ] ) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if ( ( e.ctrlKey || e.metaKey ) && options.extraAllowKey[ e.keyCode ] ) {
|
if ( ( e.ctrlKey || e.metaKey ) && this.options.extraAllowKey[ e.keyCode ] ) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if rich text key is pressed
|
// check if rich text key is pressed
|
||||||
var richTextKey = function(e) {
|
Plugin.prototype.richTextKey = function(e) {
|
||||||
var options = $(e.target).data('ce.options')
|
if ( ( e.ctrlKey || e.metaKey ) && this.options.richTextFormatKey[ e.keyCode ] ) {
|
||||||
|
|
||||||
if ( ( e.ctrlKey || e.metaKey ) && options.richTextFormatKey[ e.keyCode ] ) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// get correct val if textbox
|
// get value
|
||||||
$.fn.ceg = function(option) {
|
Plugin.prototype.value = function() {
|
||||||
var options = this.data('ce.options')
|
this.updatePlaceholder( 'remove' )
|
||||||
|
|
||||||
updatePlaceholder( this, 'remove' )
|
|
||||||
|
|
||||||
// get text
|
// get text
|
||||||
if ( options.mode === 'textonly' ) {
|
if ( this.options.mode === 'textonly' ) {
|
||||||
|
|
||||||
// strip html signes if multi line exists
|
// strip html signes if multi line exists
|
||||||
if ( options.multiline ) {
|
if ( this.options.multiline ) {
|
||||||
text = this.html()
|
text = this.$element.html()
|
||||||
text = text.replace(/<br>/g, "\n") // new line as br
|
text = text.replace(/<br>/g, "\n") // new line as br
|
||||||
text = text.replace(/<div>/g, "\n") // in some caes, new line als div
|
text = text.replace(/<div>/g, "\n") // in some caes, new line als div
|
||||||
text = $("<div>" + text + "</div>").text().trim()
|
text = $("<div>" + text + "</div>").text().trim()
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
return this.text().trim()
|
return this.$element.text().trim()
|
||||||
}
|
}
|
||||||
return this.html().trim()
|
return this.$element.html().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
$.fn.ce = function(option) {
|
$.fn[pluginName] = function ( options ) {
|
||||||
var options = $.extend({}, DEFAULTS, option)
|
return this.each(function () {
|
||||||
options.placeholder = options.placeholder || this.data('placeholder')
|
if (!$.data(this, 'plugin_' + pluginName)) {
|
||||||
|
$.data(this, 'plugin_' + pluginName,
|
||||||
// store options
|
new Plugin( this, options ));
|
||||||
this.data('ce.options', options)
|
}
|
||||||
|
});
|
||||||
// process placeholder
|
}
|
||||||
if ( options.placeholder ) {
|
|
||||||
updatePlaceholder( this, 'add' )
|
|
||||||
this.bind('focus', function (e) {
|
|
||||||
updatePlaceholder( $(e.target), 'remove' )
|
|
||||||
}).bind('blur', function (e) {
|
|
||||||
updatePlaceholder( $(e.target), 'add' )
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// maxlength check
|
|
||||||
if ( options.maxlength ) {
|
|
||||||
this.bind('keydown', function (e) {
|
|
||||||
|
|
||||||
// check control key
|
|
||||||
if ( allowKey(e) ) {
|
|
||||||
maxLengthOk( $(e.target) )
|
|
||||||
}
|
|
||||||
|
|
||||||
// check type ahead key
|
|
||||||
else {
|
|
||||||
if ( !maxLengthOk( $(e.target), true ) ) {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).bind('keyup', function (e) {
|
|
||||||
|
|
||||||
// check control key
|
|
||||||
if ( allowKey(e) ) {
|
|
||||||
maxLengthOk( $(e.target) )
|
|
||||||
}
|
|
||||||
|
|
||||||
// check type ahead key
|
|
||||||
else {
|
|
||||||
if ( !maxLengthOk( $(e.target), true ) ) {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).bind('focus', function (e) {
|
|
||||||
maxLengthOk( $(e.target) )
|
|
||||||
}).bind('blur', function (e) {
|
|
||||||
maxLengthOk( $(e.target) )
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// just paste text
|
|
||||||
if ( options.mode === 'textonly' ) {
|
|
||||||
this.bind('paste', function (e) {
|
|
||||||
var text = (e.originalEvent || e).clipboardData.getData('text/plain')
|
|
||||||
var overlimit = false
|
|
||||||
if (text) {
|
|
||||||
|
|
||||||
// replace new lines
|
|
||||||
if ( !options.multiline ) {
|
|
||||||
text = text.replace(/\n/g, '')
|
|
||||||
text = text.replace(/\r/g, '')
|
|
||||||
text = text.replace(/\t/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
// limit length, limit paste string
|
|
||||||
if ( options.maxlength ) {
|
|
||||||
var pasteLength = text.length
|
|
||||||
var currentLength = $(e.target).text().length
|
|
||||||
var overSize = ( currentLength + pasteLength ) - options.maxlength
|
|
||||||
if ( overSize > 0 ) {
|
|
||||||
text = text.substr( 0, pasteLength - overSize )
|
|
||||||
overlimit = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert new text
|
|
||||||
e.preventDefault()
|
|
||||||
document.execCommand('inserttext', false, text)
|
|
||||||
maxLengthOk( $(e.target), overlimit )
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable rich text b/u/i
|
|
||||||
if ( options.mode === 'textonly' ) {
|
|
||||||
this.bind('keydown', function (e) {
|
|
||||||
if ( richTextKey(e) ) {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable multi line
|
|
||||||
if ( !options.multiline ) {
|
|
||||||
this.bind('keydown', function (e) {
|
|
||||||
switch ( e.keyCode ) {
|
|
||||||
case 13: // enter
|
|
||||||
e.preventDefault()
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// get correct val if textbox
|
||||||
|
$.fn.ceg = function() {
|
||||||
|
var plugin = $.data(this[0], 'plugin_' + pluginName)
|
||||||
|
return plugin.value()
|
||||||
}
|
}
|
||||||
|
|
||||||
}(jQuery));
|
}(jQuery));
|
||||||
|
|
297
app/assets/javascripts/app/lib/base/jquery.textmodule.js
Normal file
297
app/assets/javascripts/app/lib/base/jquery.textmodule.js
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
(function ($, window, undefined) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
# mode: textonly/richtext / disable b/i/u/enter + strip on paste
|
||||||
|
# pasteOnlyText: true
|
||||||
|
# maxlength: 123
|
||||||
|
# multiline: true / disable enter + strip on paste
|
||||||
|
# placeholder: 'some placeholder'
|
||||||
|
#
|
||||||
|
*/
|
||||||
|
|
||||||
|
var pluginName = 'textmodule',
|
||||||
|
defaults = {}
|
||||||
|
|
||||||
|
function Plugin( element, options ) {
|
||||||
|
this.element = element
|
||||||
|
this.$element = $(element)
|
||||||
|
|
||||||
|
this.options = $.extend( {}, defaults, options)
|
||||||
|
|
||||||
|
this._defaults = defaults
|
||||||
|
this._name = pluginName
|
||||||
|
|
||||||
|
this.collection = []
|
||||||
|
this.active = false
|
||||||
|
this.buffer = ''
|
||||||
|
|
||||||
|
// check if ce exists
|
||||||
|
if ( $.data(element, 'plugin_ce') ) {
|
||||||
|
this.ce = $.data(element, 'plugin_ce')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin.prototype.init = function () {
|
||||||
|
this.baseTemplate()
|
||||||
|
|
||||||
|
this.$element.on('keydown', $.proxy(function (e) {
|
||||||
|
|
||||||
|
// esc
|
||||||
|
if ( e.keyCode === 27 ) {
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// navigate through widget
|
||||||
|
if ( this.isActive() ) {
|
||||||
|
console.log('WIDGET IS OPEN', e.keyCode)
|
||||||
|
|
||||||
|
// enter
|
||||||
|
if ( e.keyCode === 13 ) {
|
||||||
|
e.preventDefault()
|
||||||
|
var id = this.$widget.find('.dropdown-menu li.active a').data('id')
|
||||||
|
console.log('ID', id)
|
||||||
|
this.take(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// arrow keys
|
||||||
|
if ( e.keyCode === 37 || e.keyCode === 38 || e.keyCode === 39 || e.keyCode === 40 ) {
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
// up
|
||||||
|
if ( e.keyCode === 38 ) {
|
||||||
|
if ( !this.$widget.find('.dropdown-menu li.active')[0] ) {
|
||||||
|
var top = this.$widget.find('.dropdown-menu li').last().addClass('active').position().top
|
||||||
|
this.$widget.find('.dropdown-menu').scrollTop( top );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var prev = this.$widget.find('.dropdown-menu li.active').removeClass('active').prev()
|
||||||
|
var top = 300
|
||||||
|
if ( prev[0] ) {
|
||||||
|
top = prev.addClass('active').position().top
|
||||||
|
}
|
||||||
|
this.$widget.find('.dropdown-menu').scrollTop( top );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// down
|
||||||
|
if ( e.keyCode === 40 ) {
|
||||||
|
if ( !this.$widget.find('.dropdown-menu li.active')[0] ) {
|
||||||
|
var top = this.$widget.find('.dropdown-menu li').first().addClass('active').position().top
|
||||||
|
this.$widget.find('.dropdown-menu').scrollTop( top );
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var next = this.$widget.find('.dropdown-menu li.active').removeClass('active').next()
|
||||||
|
var top = 300
|
||||||
|
if ( next[0] ) {
|
||||||
|
top = next.addClass('active').position().top
|
||||||
|
}
|
||||||
|
console.log('scrollTop', top, top-30)
|
||||||
|
this.$widget.find('.dropdown-menu').scrollTop( top );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, this ))
|
||||||
|
|
||||||
|
this.$element.on('keydown', $.proxy(function (e) {
|
||||||
|
|
||||||
|
// backspace
|
||||||
|
if ( e.keyCode === 8 && this.buffer ) {
|
||||||
|
if ( this.buffer === '::' ) {
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
this.buffer = this.buffer.substr( 0, this.buffer.length-1 )
|
||||||
|
console.log('BS', this.buffer)
|
||||||
|
this.result( this.buffer.substr(2,this.buffer.length) )
|
||||||
|
}
|
||||||
|
}, this ))
|
||||||
|
|
||||||
|
this.$element.on('keypress', $.proxy(function (e) {
|
||||||
|
var value = this.$element.text()
|
||||||
|
console.log('BUFF', this.buffer, e.keyCode, String.fromCharCode(e.which) )
|
||||||
|
a = $.proxy(function() {
|
||||||
|
|
||||||
|
// shift
|
||||||
|
if ( e.keyCode === 16 ) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// enter :
|
||||||
|
if ( e.keyCode === 58 ) {
|
||||||
|
this.buffer = this.buffer + ':'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.buffer && this.buffer.substr(0,2) === '::' ) {
|
||||||
|
|
||||||
|
|
||||||
|
var sign = String.fromCharCode(e.which)
|
||||||
|
if ( e.keyCode !== 58 ) {
|
||||||
|
this.buffer = this.buffer + sign
|
||||||
|
}
|
||||||
|
console.log('BUFF HINT', this.buffer, this.buffer.length, e.which)
|
||||||
|
|
||||||
|
this.result( this.buffer.substr(2,this.buffer.length) )
|
||||||
|
|
||||||
|
if (!this.isActive()) {
|
||||||
|
this.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, this)
|
||||||
|
setTimeout(a, 400);
|
||||||
|
|
||||||
|
}, this)).on('focus', $.proxy(function (e) {
|
||||||
|
this.close()
|
||||||
|
}, this)).on('blur', $.proxy(function (e) {
|
||||||
|
this.close()
|
||||||
|
}, this))
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// create base template
|
||||||
|
Plugin.prototype.baseTemplate = function() {
|
||||||
|
this.$element.after('<div class="shortcut dropdown"><ul class="dropdown-menu" style="width: 360px; max-height: 200px;"><li><a>-</a></li></ul></div>')
|
||||||
|
this.$widget = this.$element.next()
|
||||||
|
this.updatePosition()
|
||||||
|
}
|
||||||
|
|
||||||
|
// get cursor position
|
||||||
|
Plugin.prototype.getCaretPosition = function() {
|
||||||
|
document.execCommand('insertHTML', false, '<span id="hidden"></span>');
|
||||||
|
var hiddenNode = document.getElementById('hidden');
|
||||||
|
if (!hiddenNode) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
var position = $(hiddenNode).position()
|
||||||
|
hiddenNode.parentNode.removeChild(hiddenNode)
|
||||||
|
return position
|
||||||
|
}
|
||||||
|
|
||||||
|
// update widget position
|
||||||
|
Plugin.prototype.updatePosition = function() {
|
||||||
|
this.$widget.find('.dropdown-menu').scrollTop( 300 );
|
||||||
|
var position = this.getCaretPosition()
|
||||||
|
var heightTextarea = this.$element.height()
|
||||||
|
var widgetHeight = this.$widget.find('ul').height() + 40
|
||||||
|
console.log('position', position)
|
||||||
|
console.log('heightTextarea', heightTextarea)
|
||||||
|
console.log('widgetHeight', widgetHeight)
|
||||||
|
this.$widget.css('top', position.top - heightTextarea - widgetHeight)
|
||||||
|
if ( !this.isActive() ) {
|
||||||
|
this.$widget.css('left', position.left)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// open widget
|
||||||
|
Plugin.prototype.open = function() {
|
||||||
|
this.active = true
|
||||||
|
if (this.ce) {
|
||||||
|
this.ce.input('off')
|
||||||
|
}
|
||||||
|
this.$widget.addClass('open')
|
||||||
|
}
|
||||||
|
|
||||||
|
// close widget
|
||||||
|
Plugin.prototype.close = function() {
|
||||||
|
this.active = false
|
||||||
|
this.cutInput()
|
||||||
|
if (this.ce) {
|
||||||
|
this.ce.input('on')
|
||||||
|
}
|
||||||
|
this.$widget.removeClass('open')
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if widget is active/open
|
||||||
|
Plugin.prototype.isActive = function() {
|
||||||
|
return this.active
|
||||||
|
}
|
||||||
|
|
||||||
|
// select text module and insert into text
|
||||||
|
Plugin.prototype.take = function(id) {
|
||||||
|
if (!id) {
|
||||||
|
this.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (var i = 0; i < this.collection.length; i++) {
|
||||||
|
var item = this.collection[i]
|
||||||
|
if ( item.id == id ) {
|
||||||
|
var content = item.content + "\n"
|
||||||
|
this.cutInput()
|
||||||
|
document.execCommand('insertHTML', false, content)
|
||||||
|
this.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// cut out search string from text
|
||||||
|
Plugin.prototype.cutInput = function() {
|
||||||
|
if (!this.buffer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var sel = window.getSelection();
|
||||||
|
var range = sel.getRangeAt(0);
|
||||||
|
var clone = range.cloneRange();
|
||||||
|
clone.setStart(range.startContainer, range.startOffset - this.buffer.length);
|
||||||
|
clone.setEnd(range.startContainer, range.startOffset);
|
||||||
|
clone.deleteContents();
|
||||||
|
this.buffer = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// render result
|
||||||
|
Plugin.prototype.result = function(term) {
|
||||||
|
|
||||||
|
var result = _.filter( this.collection, function(item) {
|
||||||
|
reg = new RegExp( term, 'i' )
|
||||||
|
if ( item.name && item.name.match( reg ) ) {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
if ( item.keywords && item.keywords.match( reg ) ) {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$widget.find('ul').html('')
|
||||||
|
console.log('result', term, result)
|
||||||
|
for (var i = 0; i < result.length; i++) {
|
||||||
|
var item = result[i]
|
||||||
|
template = "<li><a href=\"#\" class=\"u-textTruncate\" data-id=" + item.id + ">" + item.name
|
||||||
|
if (item.keywords) {
|
||||||
|
template = template + " (" + item.keywords + ")"
|
||||||
|
}
|
||||||
|
template = template + "</a></li>"
|
||||||
|
this.$widget.find('ul').append(template)
|
||||||
|
}
|
||||||
|
if ( !result[0] ) {
|
||||||
|
this.$widget.find('ul').append("<li><a href='#'>-</a></li>")
|
||||||
|
}
|
||||||
|
this.$widget.find('ul li').on(
|
||||||
|
'click',
|
||||||
|
function(e) {
|
||||||
|
console.log(31231)
|
||||||
|
e.preventDefault()
|
||||||
|
var id = $(e.target).data('id')
|
||||||
|
console.log('99', id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
this.updatePosition()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$.fn[pluginName] = function ( options ) {
|
||||||
|
return this.each(function () {
|
||||||
|
if (!$.data(this, 'plugin_' + pluginName)) {
|
||||||
|
$.data(this, 'plugin_' + pluginName,
|
||||||
|
new Plugin( this, options ));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}(jQuery, window));
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
<div class="ticket-article"></div>
|
<div class="ticket-article"></div>
|
||||||
|
|
||||||
<div class="ticket-edit is-public"></div>
|
<div class="ticket-edit"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
<form class="article-add <% if @formChanged: %>form-changed<% end %>">
|
<form class="article-add <% if @article.internal: %>is-internal<% else: %>is-public<% end %>">
|
||||||
|
<input type="hidden" name="type" value="<%= @article.type %>">
|
||||||
|
<input type="hidden" name="internal" value="<%= @article.internal %>">
|
||||||
<div class="bubble-grid horizontal">
|
<div class="bubble-grid horizontal">
|
||||||
<div class="vertical center edit-controls">
|
<div class="vertical center edit-controls">
|
||||||
<%- App.User.fullLocal( @S('id') ).avatar(false, 'right', 'zIndex-5') %>
|
<%- App.User.fullLocal( @S('id') ).avatar(false, 'right', 'zIndex-5') %>
|
||||||
<div class="dark pop-select zIndex-7 edit-control-item">
|
<div class="dark pop-select zIndex-7 edit-control-item" style="display: block;">
|
||||||
<div class="pop-selected u-clickable centered">
|
<div class="pop-selected u-clickable centered">
|
||||||
<div class="gray <%- @type %> channel icon"></div>
|
<div class="gray <%- @article.type %> channel icon"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pop-selector hide">
|
<div class="pop-selector hide">
|
||||||
<div class="horizontal">
|
<div class="horizontal">
|
||||||
|
@ -16,48 +18,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="u-positionOrigin zIndex-7 edit-control-item">
|
<div class="visibility-toggle zIndex-7 u-clickable edit-control-item" style="display: block;">
|
||||||
<div class="recipient-picker u-clickable horizontal centered">
|
|
||||||
<div class="recipients icon"></div>
|
|
||||||
<div class="recipient-count">3</div>
|
|
||||||
</div>
|
|
||||||
<div class="recipient-list hide">
|
|
||||||
<div class="list-arrow"></div>
|
|
||||||
<div class="list-head horizontal">
|
|
||||||
<%- @T('Recipients') %>
|
|
||||||
<div class="align-right"><%- @T('type') %></div>
|
|
||||||
</div>
|
|
||||||
<div class="list-entry horizontal centered">
|
|
||||||
<div class="avatar" style="background-image: url(http://berta9.express.ge/31/performer/Paul%20van%20Dyk/.photo/34_paul_van_dyk_01.jpg)"></div>
|
|
||||||
<div class="list-entry-name flex">Hans Peter Baxxter</div>
|
|
||||||
<div class="list-entry-type u-clickable horizontal">
|
|
||||||
<div class="active" data-value="to">To</div>
|
|
||||||
<div data-value="cc" title="<%- @T('carbon copy') %>">Cc</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="list-entry horizontal centered">
|
|
||||||
<div class="avatar" style="background-image: url(https://s3.amazonaws.com/uifaces/faces/twitter/adellecharles/48.jpg)"></div>
|
|
||||||
<div class="list-entry-name flex">Julia Maier</div>
|
|
||||||
<div class="list-entry-type u-clickable horizontal">
|
|
||||||
<div class="active" data-value="to">To</div>
|
|
||||||
<div data-value="cc" title="<%- @T('carbon copy') %>">Cc</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="list-entry horizontal centered">
|
|
||||||
<div class="avatar" style="background-image: url(https://s3.amazonaws.com/uifaces/faces/twitter/sindresorhus/48.jpg)"></div>
|
|
||||||
<div class="list-entry-name flex">Remo Batlogg</div>
|
|
||||||
<div class="list-entry-type u-clickable horizontal">
|
|
||||||
<div class="active" data-value="to">To</div>
|
|
||||||
<div data-value="cc" title="<%- @T('carbon copy') %>">Cc</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<form class="list-edit">
|
|
||||||
<input type="email" class="list-entry" placeholder="<%- @T('Add recipients..') %>"></input>
|
|
||||||
<input type="submit" tabindex="-1"></input>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="visibility-toggle zIndex-7 u-clickable edit-control-item">
|
|
||||||
<div class="internal-visibility centered" title="<%- @T("unset internal") %>">
|
<div class="internal-visibility centered" title="<%- @T("unset internal") %>">
|
||||||
<div class="internal visibility icon"></div>
|
<div class="internal visibility icon"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -67,20 +28,76 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex article-content zIndex-5 bubble-gap">
|
<div class="flex article-content zIndex-5 bubble-gap">
|
||||||
|
<!--
|
||||||
|
<label class="recipients"><%- @T('Recipients') %>
|
||||||
|
<div class="avatar" style="background-image: url(https://pbs.twimg.com/profile_images/1216362658/DSC_0084-p_normal.jpg)"></div>
|
||||||
|
<div class="avatar" style="background-image: url(https://pbs.twimg.com/profile_images/1216362658/DSC_0084-p_bigger.jpg)"></div>
|
||||||
|
</label>
|
||||||
|
-->
|
||||||
<div class="internal-border">
|
<div class="internal-border">
|
||||||
<div class="text-bubble">
|
<div class="text-bubble">
|
||||||
<div class="bubble-arrow"></div>
|
<div class="bubble-arrow"></div>
|
||||||
<textarea rows="1"></textarea>
|
<div class="js-textarea ticketEdit-body" contenteditable="true" data-name="body"><%= @article.body %></div>
|
||||||
<!-- .text-bubble grows with textarea (and expanding clone) -->
|
<!-- .text-bubble grows with textarea (and expanding clone) -->
|
||||||
<div class="article-attachment js-attachment u-unclickable">
|
|
||||||
<span class="bubble-placeholder-hint">Antwort eingeben oder</span>
|
<div class="shortcut dropdown">
|
||||||
<span class="u-highlight u-clickable edit-upload-button js-attachment-text">
|
<ul class="dropdown-menu" style="width: 240px; max-height: 200px;">
|
||||||
Dateien wählen..
|
<li><a href="#">shortcut 1</a></li>
|
||||||
<input multiple="multiple" type="file" name="file" style="position: absolute; right: 0px; top: 0px; font-family: Arial; font-size: 118px; margin: 0px; padding: 0px; cursor: pointer; opacity: 0;">
|
<li><a href="#">shortcut 2</a></li>
|
||||||
</span>
|
<li><a href="#">shortcut 3</a></li>
|
||||||
|
<li><a href="#">shortcut 4</a></li>
|
||||||
|
<li><a href="#">shortcut 5</a></li>
|
||||||
|
<li><a href="#">shortcut 6</a></li>
|
||||||
|
<li><a href="#">shortcut 7</a></li>
|
||||||
|
<li><a href="#">shortcut 8</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="attachments"></div>
|
||||||
|
<!--
|
||||||
|
</div>
|
||||||
|
<div class="attachment horizontal">
|
||||||
|
<div class="attachment-name u-highlight">sega-genesis-box.gif</div>
|
||||||
|
<div class="attachment-size">2.4mb</div>
|
||||||
|
<div class="attachment-delete js-delete align-right u-clickable">
|
||||||
|
<div class="delete icon"></div><%- @T('Delete File') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="attachment horizontal">
|
||||||
|
<div class="attachment-name u-highlight">license-key.txt</div>
|
||||||
|
<div class="attachment-size">7kb</div>
|
||||||
|
<div class="attachment-delete js-delete align-right u-clickable">
|
||||||
|
<div class="delete icon"></div><%- @T('Delete File') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>-->
|
||||||
|
<div class="article-attachment u-unclickable">
|
||||||
|
<div class="attachmentPlaceholder">
|
||||||
|
<span class="attachmentPlaceholder-hint">Antwort eingeben oder</span>
|
||||||
|
<span class="attachmentPlaceholder-inputHolder u-highlight u-clickable">
|
||||||
|
Dateien wählen..
|
||||||
|
<input multiple="multiple" type="file" name="file" style="position: absolute; right: 0px; top: 0px; font-family: Arial; font-size: 118px; margin: 0px; padding: 0px; cursor: pointer; opacity: 0;">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="attachmentUpload hide u-clickable">
|
||||||
|
<div class="horizontal">
|
||||||
|
<div class="u-highlight">
|
||||||
|
<%- @T(' Uploading ') %> (<span class="js-percentage">0</span>%) ...
|
||||||
|
</div>
|
||||||
|
<div class="attachmentUpload-cancel align-right js-cancel u-clickable">
|
||||||
|
<div class="delete icon"></div><%- @T('Cancel Upload') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="attachmentUpload-progressBar" style="width: 0%"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="fit dropArea">
|
||||||
|
<div class="dropArea-inner fit centered">
|
||||||
|
<%- @T('Drop Files here') %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
|
@ -2508,7 +2508,6 @@ footer {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin: -5px;
|
margin: -5px;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-internal .internal-border {
|
.is-internal .internal-border {
|
||||||
|
@ -2844,7 +2843,7 @@ footer {
|
||||||
.ticket-edit textarea,
|
.ticket-edit textarea,
|
||||||
.ticketEdit-body {
|
.ticketEdit-body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
/*height: 38px;*/
|
position: relative;
|
||||||
min-height: 20px;
|
min-height: 20px;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
Loading…
Reference in a new issue