diff --git a/app/assets/javascripts/app/controllers/_application_controller.js.coffee b/app/assets/javascripts/app/controllers/_application_controller.js.coffee index e8599a747..078e3dd82 100644 --- a/app/assets/javascripts/app/controllers/_application_controller.js.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller.js.coffee @@ -233,7 +233,8 @@ class App.Controller extends Spine.Controller trigger: 'hover' container: 'body' html: true - delay: { show: 400, hide: 800 } + animation: false + delay: 100 placement: position title: -> ticket_id = $(@).data('id') @@ -270,7 +271,8 @@ class App.Controller extends Spine.Controller trigger: 'hover' container: 'body' html: true - delay: { show: 400, hide: 800 } + animation: false + delay: 100 placement: "auto #{position}" title: -> user_id = $(@).data('id') @@ -326,7 +328,8 @@ class App.Controller extends Spine.Controller trigger: 'hover' container: 'body' html: true - delay: { show: 400, hide: 800 } + animation: false + delay: 100 placement: "auto #{position}" title: -> organization_id = $(@).data('id') @@ -379,7 +382,8 @@ class App.Controller extends Spine.Controller trigger: 'hover' container: 'body' html: true - delay: { show: 500, hide: 5200 } + animation: false + delay: 100 placement: "auto #{data.position}" title: -> $(@).find('[title="*"]').val() @@ -496,6 +500,7 @@ class App.ControllerModal extends App.Controller head: '?' buttonClass: 'btn--success' centerButtons: [] + container: null options = _.extend( defaults, options ) @@ -522,10 +527,14 @@ class App.ControllerModal extends App.Controller if @content @body.html @content + if @container + @el.addClass('modal--local') + @el.modal keyboard: @keyboard show: true backdrop: @backdrop + container: @container .on 'show.bs.modal': @onShow 'shown.bs.modal': @onShown diff --git a/app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee b/app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee index 62262e123..aa7632475 100644 --- a/app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee +++ b/app/assets/javascripts/app/controllers/agent_ticket_create.js.coffee @@ -3,10 +3,10 @@ class App.TicketCreate extends App.Controller '.tabsSidebar' : 'sidebar' events: - 'click .type-tabs .tab': 'changeFormType' - 'submit form': 'submit' - 'click .submit': 'submit' - 'click .cancel': 'cancel' + 'click .type-tabs .tab': 'changeFormType' + 'submit form': 'submit' + 'click .submit': 'submit' + 'click .cancel': 'cancel' constructor: (params) -> super @@ -87,6 +87,9 @@ class App.TicketCreate extends App.Controller # update form @el.find('[name="formSenderType"]').val(type) + # force changing signature + @el.find('[name="group_id"]').trigger('change') + meta: => text = '' if @articleAttributes @@ -167,10 +170,15 @@ class App.TicketCreate extends App.Controller a = App.TicketArticle.find( params.article_id ) # reset owner - t.owner_id = 0 - t.customer_id_autocompletion = a.from - t.subject = a.subject || t.title - t.body = a.body + t.owner_id = 0 + t.customer_id_completion = a.from + t.subject = a.subject || t.title + + # convert non text/html from text 2 html + if a.content_type.match(/\/html/) + t.body = a.body + else + t.body = App.Utils.text2html( a.body ) # render page @render( options: t ) @@ -191,10 +199,10 @@ class App.TicketCreate extends App.Controller @form_id = App.ControllerForm.formId() @html App.view('agent_ticket_create')( - head: 'New Ticket' - agent: @isRole('Agent') - admin: @isRole('Admin') - form_id: @form_id + head: 'New Ticket' + agent: @isRole('Agent') + admin: @isRole('Admin') + form_id: @form_id ) formChanges = (params, attribute, attributes, classname, form, ui) => @@ -220,6 +228,44 @@ class App.TicketCreate extends App.Controller # replace new option list form.find('[name="' + fieldNameToChange + '"]').closest('.form-group').replaceWith( newElement ) + signatureChanges = (params, attribute, attributes, classname, form, ui) => + if attribute && attribute.name is 'group_id' + signature = undefined + if params['group_id'] + group = App.Group.find( params['group_id'] ) + if group && group.signature_id + signature = App.Signature.find( group.signature_id ) + + # check if signature need to be added + type = @$('[name="formSenderType"]').val() + + if signature isnt undefined && signature.body && type is 'email-out' + signatureFinished = App.Utils.text2html( + App.Utils.replaceTags( signature.body, { user: App.Session.get() } ) + ) + + # get current body + body = @$('[data-name="body"]').html() || '' + if App.Utils.signatureCheck( body, signatureFinished ) + + # if signature has changed, replace it + signature_id = @$('[data-signature=true]').data('signature-id') + if signature_id && signature_id.toString() isnt signature.id.toString() + + # remove old signature + @$('[data-signature="true"]').remove() + body = @$('[data-name="body"]').html() || '' + + if !App.Utils.lastLineEmpty(body) + body = body + '
' + body = body + "
#{signatureFinished}
" + + @$('[data-name="body"]').html(body) + + # remove old signature + else + @$('[data-name="body"]').find('[data-signature=true]').remove() + new App.ControllerForm( el: @el.find('.ticket-form-top') form_id: @form_id @@ -228,7 +274,8 @@ class App.TicketCreate extends App.Controller events: 'change [name=customer_id]': @localUserInfo handlers: [ - formChanges + formChanges, + signatureChanges, ] filter: @form_meta.filter autofocus: true @@ -236,21 +283,22 @@ class App.TicketCreate extends App.Controller ) new App.ControllerForm( - el: @el.find('.article-form-top') - form_id: @form_id - model: App.TicketArticle - screen: 'create_top' - params: params + el: @el.find('.article-form-top') + form_id: @form_id + model: App.TicketArticle + screen: 'create_top' + params: params ) new App.ControllerForm( - el: @el.find('.ticket-form-middle') - form_id: @form_id - model: App.Ticket - screen: 'create_middle' + el: @el.find('.ticket-form-middle') + form_id: @form_id + model: App.Ticket + screen: 'create_middle' events: 'change [name=customer_id]': @localUserInfo handlers: [ - formChanges + formChanges, + signatureChanges, ] filter: @form_meta.filter params: params @@ -264,7 +312,8 @@ class App.TicketCreate extends App.Controller events: 'change [name=customer_id]': @localUserInfo handlers: [ - formChanges + formChanges, + signatureChanges, ] filter: @form_meta.filter params: params @@ -330,7 +379,7 @@ class App.TicketCreate extends App.Controller if sender.name is 'Customer' params['article'] = { to: (group && group.name) || '' - from: params.customer_id_autocompletion + from: params.customer_id_completion cc: params.cc subject: params.subject body: params.body @@ -342,7 +391,7 @@ class App.TicketCreate extends App.Controller else params['article'] = { from: (group && group.name) || '' - to: params.customer_id_autocompletion + to: params.customer_id_completion cc: params.cc subject: params.subject body: params.body @@ -467,6 +516,7 @@ class Sidebar extends App.Controller icon: 'person' actions: [ { + title: 'Edit Customer' name: 'Edit Customer' class: 'glyphicon glyphicon-edit' callback: editCustomer diff --git a/app/assets/javascripts/app/controllers/layout_ref.js.coffee b/app/assets/javascripts/app/controllers/layout_ref.js.coffee index acbd2d8f2..9ff018978 100644 --- a/app/assets/javascripts/app/controllers/layout_ref.js.coffee +++ b/app/assets/javascripts/app/controllers/layout_ref.js.coffee @@ -11,9 +11,9 @@ App.Config.set( 'layout_ref', Index, 'Routes' ) class Content extends App.ControllerContent events: - 'hide.bs.dropdown .js-recipientDropdown': 'hideOrganisationMembers' - 'click .js-organisation': 'showOrganisationMembers' - 'click .js-back': 'hideOrganisationMembers' + 'hide.bs.dropdown .js-recipientDropdown': 'hideOrganizationMembers' + 'click .js-organization': 'showOrganizationMembers' + 'click .js-back': 'hideOrganizationMembers' constructor: -> super @@ -46,21 +46,21 @@ class Content extends App.ControllerContent render: -> @html App.view('layout_ref/content')() - showOrganisationMembers: (e) => + showOrganizationMembers: (e) => e.stopPropagation() listEntry = $(e.currentTarget) - organisationId = listEntry.data('organisation-id') + organizationId = listEntry.data('organization-id') @recipientList = @$('.recipientList') - @organisationList = @$("##{ organisationId }") + @organizationList = @$("##{ organizationId }") - # move organisation-list to the right and slide it in + # move organization-list to the right and slide it in - $.Velocity.hook(@organisationList, 'translateX', '100%') - @organisationList.removeClass('hide') + $.Velocity.hook(@organizationList, 'translateX', '100%') + @organizationList.removeClass('hide') - @organisationList.velocity + @organizationList.velocity properties: translateX: 0 options: @@ -73,12 +73,12 @@ class Content extends App.ControllerContent translateX: '-100%' options: speed: 300 - complete: => @recipientList.height(@organisationList.height()) + complete: => @recipientList.height(@organizationList.height()) - hideOrganisationMembers: (e) => + hideOrganizationMembers: (e) => e && e.stopPropagation() - return if !@organisationList + return if !@organizationList # fade list back in @@ -92,18 +92,20 @@ class Content extends App.ControllerContent @recipientList.height('') - # slide out organisation-list and hide it - @organisationList.velocity + # slide out organization-list and hide it + @organizationList.velocity properties: translateX: '100%' options: speed: 300 - complete: => @organisationList.addClass('hide') + complete: => @organizationList.addClass('hide') App.Config.set( 'layout_ref/content', Content, 'Routes' ) class CommunicationOverview extends App.ControllerContent + events: + 'click .js-unfold': 'unfold' constructor: -> super @@ -116,9 +118,38 @@ class CommunicationOverview extends App.ControllerContent scrollHolder = pageHeader.scrollParent() scrollBody = scrollHolder.get(0).scrollHeight - scrollHolder.height() + unfold: (e) -> + container = $(e.currentTarget).parents('.textBubble-content') + overflowContainer = container.find('.textBubble-overflowContainer') + + overflowContainer.velocity + properties: + opacity: 0 + options: + duration: 300 + + container.velocity + properties: + height: container.attr('data-height') + options: + duration: 300 + complete: -> overflowContainer.addClass('hide'); + render: -> @html App.view('layout_ref/communication_overview')() + # set see more options + previewHeight = 240 + @$('.textBubble-content').each( (index) -> + bubble = $( @ ) + heigth = bubble.height() + if heigth > (previewHeight + 30) + bubble.attr('data-height', heigth) + bubble.css('height', "#{previewHeight}px") + else + bubble.parent().find('.textBubble-overflowContainer').addClass('hide') + ) + App.Config.set( 'layout_ref/communication_overview', CommunicationOverview, 'Routes' ) @@ -133,7 +164,7 @@ class LayoutRefCommunicationReply extends App.ControllerContent '.attachmentUpload': 'attachmentUpload' '.attachmentUpload-progressBar':'progressBar' '.js-percentage': 'progressText' - '.text-bubble': 'textBubble' + '.textBubble': 'textBubble' events: 'focus .js-textarea': 'open_textarea' @@ -713,4 +744,37 @@ class RichText extends App.ControllerContent App.Config.set( 'layout_ref/richtext', RichText, 'Routes' ) +class LocalModalRef extends App.ControllerContent + + constructor: -> + super + @render() + + render: -> + @html App.view('layout_ref/local_modal')() + +App.Config.set( 'layout_ref/local_modal', LocalModalRef, 'Routes' ) + +class loadingPlaceholderRef extends App.ControllerContent + + constructor: -> + super + @render() + + render: -> + @html App.view('layout_ref/loading_placeholder')() + +App.Config.set( 'layout_ref/loading_placeholder', loadingPlaceholderRef, 'Routes' ) + +class insufficientRightsRef extends App.ControllerContent + + constructor: -> + super + @render() + + render: -> + @html App.view('layout_ref/insufficient_rights')() + +App.Config.set( 'layout_ref/insufficient_rights', insufficientRightsRef, 'Routes' ) + App.Config.set( 'LayoutRef', { prio: 1700, parent: '#current_user', name: 'Layout Reference', target: '#layout_ref', role: [ 'Admin' ] }, 'NavBarRight' ) \ No newline at end of file diff --git a/app/assets/javascripts/app/controllers/navigation.js.coffee b/app/assets/javascripts/app/controllers/navigation.js.coffee index f0af2b19c..0d888dce8 100644 --- a/app/assets/javascripts/app/controllers/navigation.js.coffee +++ b/app/assets/javascripts/app/controllers/navigation.js.coffee @@ -173,9 +173,9 @@ class App.Navigation extends App.Controller data = display: "#{organization.displayName()}" id: organization.id - class: "organisation organization-popover" + class: "organization organization-popover" url: organization.uiUrl() - iconClass: "organisation" + iconClass: "organization" area.result.push data @renderResult(result) diff --git a/app/assets/javascripts/app/controllers/taskbar_widget.js.coffee b/app/assets/javascripts/app/controllers/taskbar_widget.js.coffee index c2495436e..2cbd715e2 100644 --- a/app/assets/javascripts/app/controllers/taskbar_widget.js.coffee +++ b/app/assets/javascripts/app/controllers/taskbar_widget.js.coffee @@ -29,6 +29,7 @@ class App.TaskbarWidget extends App.Controller data = url: '#' id: false + iconClass: 'loading dot' title: App.i18n.translateInline('Loading...') head: App.i18n.translateInline('Loading...') worker = App.TaskManager.worker( task.key ) diff --git a/app/assets/javascripts/app/controllers/ticket_customer.js.coffee b/app/assets/javascripts/app/controllers/ticket_customer.js.coffee index 45ae81ecb..1856adb2b 100644 --- a/app/assets/javascripts/app/controllers/ticket_customer.js.coffee +++ b/app/assets/javascripts/app/controllers/ticket_customer.js.coffee @@ -8,7 +8,7 @@ class App.TicketCustomer extends App.ControllerModal @button = true configure_attributes = [ - { name: 'customer_id', display: 'Customer', tag: 'user_autocompletion', null: false, placeholder: 'Enter Person or Organisation/Company', minLengt: 2, disableCreateUser: true }, + { name: 'customer_id', display: 'Customer', tag: 'user_autocompletion', null: false, placeholder: 'Enter Person or Organization/Company', minLengt: 2, disableCreateUser: true }, ] controller = new App.ControllerForm( diff --git a/app/assets/javascripts/app/controllers/ticket_overview.js.coffee b/app/assets/javascripts/app/controllers/ticket_overview.js.coffee index faa1f0b71..68247f680 100644 --- a/app/assets/javascripts/app/controllers/ticket_overview.js.coffee +++ b/app/assets/javascripts/app/controllers/ticket_overview.js.coffee @@ -482,6 +482,7 @@ class Table extends App.ControllerContent new App.OverviewSettings( overview_id: @overview.id view_mode: @view_mode + container: @el ) class App.OverviewSettings extends App.ControllerModal diff --git a/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee b/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee index 85f897f11..501225356 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom.js.coffee @@ -18,10 +18,9 @@ class App.TicketZoom extends App.Controller @navupdate '#' - @form_meta = undefined - @ticket_id = params.ticket_id - @article_id = params.article_id - @signature = undefined + @form_meta = undefined + @ticket_id = params.ticket_id + @article_id = params.article_id @key = 'ticket::' + @ticket_id cache = App.Store.get( @key ) @@ -44,15 +43,25 @@ class App.TicketZoom extends App.Controller ) meta: => + + # default attributes meta = - url: @url() - id: @ticket_id - iconClass: "priority" + url: @url() + id: @ticket_id + + # set icon and tilte if defined + if @taskIconClass + meta.iconClass = @taskIconClass + if @taskHead + meta.head = @taskHead + + # set icon and title based on ticket if @ticket - @ticket = App.Ticket.fullLocal( @ticket.id ) - meta.head = @ticket.title - meta.title = '#' + @ticket.number + ' - ' + @ticket.title - meta.class = "level-#{@ticket.level()}" + @ticket = App.Ticket.fullLocal( @ticket.id ) + meta.head = @ticket.title + meta.title = '#' + @ticket.number + ' - ' + @ticket.title + meta.class = "level-#{@ticket.level()}" + meta.iconClass = 'priority' meta url: => @@ -67,6 +76,7 @@ class App.TicketZoom extends App.Controller @positionPageHeaderStop() changed: => + return false if !@ticket formCurrent = @formParam( @el.find('.edit') ) ticket = App.Ticket.find(@ticket_id).attributes() modelDiff = @getDiff( ticket, formCurrent ) @@ -115,11 +125,31 @@ class App.TicketZoom extends App.Controller # do not close window if request is aborted return if status is 'abort' - # do not close window on network error but if object is not found - return if status is 'error' && error isnt 'Not Found' + # show error message + if xhr.status is 401 || error is 'Unauthorized' + @taskHead = '» ' + App.i18n.translateInline('Unauthorized') + ' «' + @taskIconClass = 'error' + @html App.view('generic/error/unauthorized')( objectName: 'Ticket' ) + else if xhr.status is 404 || error is 'Not Found' + @taskHead = '» ' + App.i18n.translateInline('Not Found') + ' «' + @taskIconClass = 'error' + @html App.view('generic/error/not_found')( objectName: 'Ticket' ) + else + @taskHead = '» ' + App.i18n.translateInline('Error') + ' «' + @taskIconClass = 'error' - # remove task - App.TaskManager.remove( @task_key ) + status = xhr.status + detail = xhr.responseText + if !status && !detail + detail = 'General communication error, maybe internet is not available!' + @html App.view('generic/error/generic')( + status: status + detail: detail + objectName: 'Ticket' + ) + + # update current task title + App.Event.trigger 'task:render' ) @@ -137,9 +167,6 @@ class App.TicketZoom extends App.Controller # get edit form attributes @form_meta = data.form_meta - # get signature - @signature = data.signature - # load assets App.Collection.loadAssets( data.assets ) @@ -316,7 +343,10 @@ class App.TicketZoom extends App.Controller showTicketHistory = => new App.TicketHistory( ticket_id: @ticket.id ) showTicketMerge = => - new App.TicketMerge( ticket: @ticket, task_key: @task_key ) + new App.TicketMerge + ticket: @ticket + task_key: @task_key + container: @el changeCustomer = (e, el) => new App.TicketCustomer( ticket: @ticket @@ -423,6 +453,18 @@ class App.TicketZoom extends App.Controller ui: @ ) + # set see more options + previewHeight = 270 + @$('.textBubble-content').each( (index) -> + bubble = $( @ ) + heigth = bubble.height() + if heigth > (previewHeight + 30) + bubble.attr('data-height', heigth) + bubble.css('height', "#{previewHeight}px") + else + bubble.parent().find('.textBubble-overflowContainer').addClass('hide') + ) + # scroll to article if given if @article_id && document.getElementById( 'article-' + @article_id ) offset = document.getElementById( 'article-' + @article_id ).offsetTop @@ -775,7 +817,7 @@ class Edit extends App.Controller '.attachmentUpload-progressBar': 'progressBar' '.js-percentage': 'progressText' '.js-cancel': 'cancelContainer' - '.text-bubble': 'textBubble' + '.textBubble': 'textBubble' '.editControls-item': 'editControlItem' #'.editControls': 'editControls' #'.recipient-picker': 'recipientPicker' @@ -860,14 +902,15 @@ class Edit extends App.Controller if data.ticket.id is @ticket.id #@setArticleType(data.type.name) - # preselect article type - @setArticleType( 'email' ) @open_textarea(null, true) for key, value of data.article if key is 'body' @$('[data-name="' + key + '"]').html(value) else @$('[name="' + key + '"]').val(value) + + # preselect article type + @setArticleType( 'email' ) ) isIE10: -> @@ -893,7 +936,7 @@ class Edit extends App.Controller @setArticleType(@type) configure_attributes = [ - { name: 'customer_id', display: 'Recipients', tag: 'user_autocompletion', null: false, placeholder: 'Enter Person or Organisation/Company', minLengt: 2, disableCreateUser: false }, + { name: 'customer_id', display: 'Recipients', tag: 'user_autocompletion', null: false, placeholder: 'Enter Person or Organization/Company', minLengt: 2, disableCreateUser: false }, ] controller = new App.ControllerForm( @@ -905,7 +948,7 @@ class Edit extends App.Controller @$('[data-name="body"]').ce({ mode: 'richtext' multiline: true - maxlength: 2500 + maxlength: 5000 }) html5Upload.initialize( @@ -1075,10 +1118,29 @@ class Edit extends App.Controller # show/hide attributes for articleType in @articleTypes if articleType.name is type - @$(".form-group").addClass('hide') + @$('.form-group').addClass('hide') for name in articleType.attributes @$("[name=#{name}]").closest('.form-group').removeClass('hide') + # check if signature need to be added + body = @$('[data-name="body"]').html() || '' + signature = undefined + if @ticket.group.signature_id + signature = App.Signature.find( @ticket.group.signature_id ) + if signature && signature.body && @type is 'email' + signatureFinished = App.Utils.text2html( + App.Utils.replaceTags( signature.body, { user: App.Session.get(), ticket: @ticket } ) + ) + if App.Utils.signatureCheck( body, signatureFinished ) + if !App.Utils.lastLineEmpty(body) + body = body + '
' + body = body + "
#{signatureFinished}
" + @$('[data-name="body"]').html(body) + + # remove old signature + else + @$('[data-name="body"]').find("[data-signature=true]").remove() + detect_empty_textarea: => if !@textarea.text().trim() @add_textarea_catcher() @@ -1241,13 +1303,14 @@ class Edit extends App.Controller class ArticleView extends App.Controller events: - 'click [data-type=public]': 'public_internal' - 'click [data-type=internal]': 'public_internal' - 'click .show_toogle': 'show_toogle' - 'click [data-type=reply]': 'reply' - 'click [data-type=replyAll]': 'replyAll' - 'click .text-bubble': 'toggle_meta' - 'click .text-bubble a': 'stopPropagation' + 'click [data-type=public]': 'public_internal' + 'click [data-type=internal]': 'public_internal' + 'click .show_toogle': 'show_toogle' + 'click [data-type=reply]': 'reply' + 'click [data-type=replyAll]': 'replyAll' + 'click .textBubble': 'toggle_meta_with_delay' + 'click .textBubble a': 'stopPropagation' + 'click .js-unfold': 'unfold' constructor: -> super @@ -1311,7 +1374,17 @@ class ArticleView extends App.Controller stopPropagation: (e) -> e.stopPropagation() - toggle_meta: (e) -> + toggle_meta_with_delay: (e) => + # allow double click select + # by adding a delay to the toggle + + if @lastClick and +new Date - @lastClick < 150 + clearTimeout(@toggleMetaTimeout) + else + @toggleMetaTimeout = setTimeout(@toggle_meta, 150, e) + @lastClick = +new Date + + toggle_meta: (e) => e.preventDefault() animSpeed = 300 @@ -1387,6 +1460,25 @@ class ArticleView extends App.Controller metaTopClip.velocity({ height: metaTop.outerHeight() }, animSpeed, 'easeOutQuad') metaBottomClip.velocity({ height: metaBottom.outerHeight() }, animSpeed, 'easeOutQuad') + unfold: (e) -> + e.preventDefault() + e.stopPropagation() + container = $(e.currentTarget).parents('.textBubble-content') + overflowContainer = container.find('.textBubble-overflowContainer') + + overflowContainer.velocity + properties: + opacity: 0 + options: + duration: 300 + + container.velocity + properties: + height: container.attr('data-height') + options: + duration: 300 + complete: -> overflowContainer.addClass('hide'); + isOrContains: (node, container) -> while node if node is container @@ -1488,12 +1580,6 @@ class ArticleView extends App.Controller # get current body body = @ui.el.find('[data-name="body"]').html() || '' - # check if signature need to be added - if @ui.signature && @ui.signature.body && type.name is 'email' - signature = App.Utils.text2html( @ui.signature.body ) - if App.Utils.signatureCheck( body, signature ) - body = body + signature - # check if quote need to be added selectedText = App.ClipBoard.getSelected() if selectedText @@ -1613,11 +1699,11 @@ class Article extends App.Controller type: 'replyAll' href: '#' } - actions.push { - name: 'split' - type: 'split' - href: '#ticket/create/' + @article.ticket_id + '/' + @article.id - } + actions.push { + name: 'split' + type: 'split' + href: '#ticket/create/' + @article.ticket_id + '/' + @article.id + } @article.actions = actions attachments: -> diff --git a/app/assets/javascripts/app/controllers/widget/online_notification.js.coffee b/app/assets/javascripts/app/controllers/widget/online_notification.js.coffee index f3a0689eb..ac7c0348e 100644 --- a/app/assets/javascripts/app/controllers/widget/online_notification.js.coffee +++ b/app/assets/javascripts/app/controllers/widget/online_notification.js.coffee @@ -1,11 +1,14 @@ class App.OnlineNotificationWidget extends App.Controller + elements: + '.js-toggleNavigation': 'toggle' + constructor: -> super @bind 'OnlineNotification::changed', => @delay( => @fetch() - 1000 + 1200 'online-notification-changed' ) @@ -17,14 +20,14 @@ class App.OnlineNotificationWidget extends App.Controller if !@access() @el.find('activity-counter').html('') return - @start() + @createContainer() if @access() - @start() - @subscribeId = App.OnlineNotification.subscribe( @start ) + @createContainer() + @subscribeId = App.OnlineNotification.subscribe( @updateContent ) release: => - @stop() + @removeContainer() App.OnlineNotification.unsubscribe( @subscribeId ) access: -> @@ -38,24 +41,64 @@ class App.OnlineNotificationWidget extends App.Controller @el.find('.activity-counter').remove() return - if @el.find('.logo .activity-counter')[0] - @el.find('.logo .activity-counter').html(count) + if @el.find('.js-toggleNavigation .activity-counter')[0] + @el.find('.js-toggleNavigation .activity-counter').html(count) else - @el.find('.logo').append('
' + count.toString() + '
') + @toggle.append('
' + count.toString() + '
') - markAllAsSeen: (items) => - for item in items - if !item.seen - App.OnlineNotification.seen( 'Ticket', item.id ) + markAllAsRead: => + @ajax( + id: 'markAllAsRead' + type: 'POST' + url: @apiPath + '/online_notifications/mark_all_as_read' + data: JSON.stringify( '' ) + processData: true + ) - stop: => - @counterUpdate(0) - @el.find('.logo').popover('destroy') + removeClickCatcher: () => + return if !@clickCatcher + @clickCatcher.remove() + @clickCatcher = null - start: => - @stop() + onShow: => + @updateContent() - # show popover + # set heigth of notification popover + height = $('#app').height() + $('.js-notificationsContainer').css('height', "#{height-12}px") + $('.js-notificationsContainer .arrow').css('top', '20px') + + # close notification list on click + $('.js-notificationsContainer').on('click', (e) => + #console.log('CL') + @hidePopover() + ) + + # mark all notifications as read + $('.js-markAllAsRead').on('click', (e) => + e.preventDefault() + @markAllAsRead() + ) + + # add clickCatcher + @clickCatcher = new App.clickCatcher + holder: @el.offsetParent() + callback: @hidePopover + zIndexScale: 4 + + onHide: => + @removeClickCatcher() + + hidePopover: => + @toggle.popover('hide') + + fetch: => + load = (items) => + App.OnlineNotification.refresh( items, { clear: true } ) + @updateContent() + App.OnlineNotification.fetchFull(load) + + updateContent: => items = App.OnlineNotification.search( sortBy: 'created_at', order: 'DESC' ) counter = 0 for item in items @@ -63,33 +106,46 @@ class App.OnlineNotificationWidget extends App.Controller counter = counter + 1 @counterUpdate(counter) - items = @prepareForObjectList(items) - - @el.find('.logo').popover( - trigger: 'click' - container: 'body' - html: true - delay: { show: 100, hide: 0 } - placement: 'right' - title: -> - App.i18n.translateInline( 'Notifications' ) + " #{counter}" - content: => - # insert data - $( App.view('widget/online_notification')(items: items)) - ).on('shown.bs.popover', => - - # show frontend times - $('#markAllAsSeen').bind('click', (e) => - e.preventDefault() - @markAllAsSeen(items) - ); - @frontendTimeUpdate() - ).on('hide.bs.popover', => - $('#markAllAsSeen').unbind('click') + # update title + $('.js-notificationsContainer .popover-title').html( + App.i18n.translateInline( 'Notifications' ) + " #{counter}" ) - fetch: => - load = (items) => - App.OnlineNotification.refresh( items, { clear: true } ) - @start() - App.OnlineNotification.fetchFull(load) \ No newline at end of file + # update content + items = @prepareForObjectList(items) + $('.js-notificationsContainer .popover-content').html( + $( App.view('widget/online_notification_content')(items: items) ) + ) + + # show frontend times + @frontendTimeUpdate() + + createContainer: => + @removeContainer() + + # show popover + waitUntilOldPopoverIsRemoved = => + @toggle.popover + trigger: 'click' + container: 'body' + html: true + placement: 'right' + viewport: { "selector": "#app", "padding": 10 } + template: App.view('widget/online_notification')() + title: ' ' + content: ' ' + .on + 'shown.bs.popover': @onShow + 'hide.bs.popover': @onHide + + @updateContent() + + @delay( + => waitUntilOldPopoverIsRemoved() + 600 + 'popover' + ) + + removeContainer: => + @counterUpdate(0) + @toggle.popover('destroy') diff --git a/app/assets/javascripts/app/lib/animations/velocity.min.js b/app/assets/javascripts/app/lib/animations/velocity.min.js index 8454789cb..33676b351 100644 --- a/app/assets/javascripts/app/lib/animations/velocity.min.js +++ b/app/assets/javascripts/app/lib/animations/velocity.min.js @@ -1,4 +1,4 @@ -/*! VelocityJS.org (1.0.0). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License */ -/*! VelocityJS.org jQuery Shim (1.0.0-rc1). (C) 2014 The jQuery Foundation. MIT @license: en.wikipedia.org/wiki/MIT_License. */ -!function(e){function t(e){var t=e.length,r=$.type(e);return"function"===r||$.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===r||0===t||"number"==typeof t&&t>0&&t-1 in e}if(!e.jQuery){var $=function(e,t){return new $.fn.init(e,t)};$.isWindow=function(e){return null!=e&&e==e.window},$.type=function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?a[o.call(e)]||"object":typeof e},$.isArray=Array.isArray||function(e){return"array"===$.type(e)},$.isPlainObject=function(e){var t;if(!e||"object"!==$.type(e)||e.nodeType||$.isWindow(e))return!1;try{if(e.constructor&&!n.call(e,"constructor")&&!n.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}for(t in e);return void 0===t||n.call(e,t)},$.each=function(e,r,a){var n,o=0,i=e.length,s=t(e);if(a){if(s)for(;i>o&&(n=r.apply(e[o],a),n!==!1);o++);else for(o in e)if(n=r.apply(e[o],a),n===!1)break}else if(s)for(;i>o&&(n=r.call(e[o],o,e[o]),n!==!1);o++);else for(o in e)if(n=r.call(e[o],o,e[o]),n===!1)break;return e},$.data=function(e,t,a){if(void 0===a){var n=e[$.expando],o=n&&r[n];if(void 0===t)return o;if(o&&t in o)return o[t]}else if(void 0!==t){var n=e[$.expando]||(e[$.expando]=++$.uuid);return r[n]=r[n]||{},r[n][t]=a,a}},$.removeData=function(e,t){var a=e[$.expando],n=a&&r[a];n&&$.each(t,function(e,t){delete n[t]})},$.extend=function(){var e,t,r,a,n,o,i=arguments[0]||{},s=1,l=arguments.length,u=!1;for("boolean"==typeof i&&(u=i,i=arguments[s]||{},s++),"object"!=typeof i&&"function"!==$.type(i)&&(i={}),s===l&&(i=this,s--);l>s;s++)if(null!=(n=arguments[s]))for(a in n)e=i[a],r=n[a],i!==r&&(u&&r&&($.isPlainObject(r)||(t=$.isArray(r)))?(t?(t=!1,o=e&&$.isArray(e)?e:[]):o=e&&$.isPlainObject(e)?e:{},i[a]=$.extend(u,o,r)):void 0!==r&&(i[a]=r));return i},$.queue=function(e,r,a){function n(e,r){var a=r||[];return null!=e&&(t(Object(e))?!function(e,t){for(var r=+t.length,a=0,n=e.length;r>a;)e[n++]=t[a++];if(r!==r)for(;void 0!==t[a];)e[n++]=t[a++];return e.length=n,e}(a,"string"==typeof e?[e]:e):[].push.call(a,e)),a}if(e){r=(r||"fx")+"queue";var o=$.data(e,r);return a?(!o||$.isArray(a)?o=$.data(e,r,n(a)):o.push(a),o):o||[]}},$.dequeue=function(e,t){$.each(e.nodeType?[e]:e,function(e,r){t=t||"fx";var a=$.queue(r,t),n=a.shift();"inprogress"===n&&(n=a.shift()),n&&("fx"===t&&a.unshift("inprogress"),n.call(r,function(){$.dequeue(r,t)}))})},$.fn=$.prototype={init:function(e){if(e.nodeType)return this[0]=e,this;throw new Error("Not a DOM node.")},offset:function(){var t=this[0].getBoundingClientRect();return{top:t.top+(e.pageYOffset||document.scrollTop||0)-(document.clientTop||0),left:t.left+(e.pageXOffset||document.scrollLeft||0)-(document.clientLeft||0)}},position:function(){function e(){for(var e=this.offsetParent||document;e&&"html"===!e.nodeType.toLowerCase&&"static"===e.style.position;)e=e.offsetParent;return e||document}var t=this[0],e=e.apply(t),r=this.offset(),a=/^(?:body|html)$/i.test(e.nodeName)?{top:0,left:0}:$(e).offset();return r.top-=parseFloat(t.style.marginTop)||0,r.left-=parseFloat(t.style.marginLeft)||0,e.style&&(a.top+=parseFloat(e.style.borderTopWidth)||0,a.left+=parseFloat(e.style.borderLeftWidth)||0),{top:r.top-a.top,left:r.left-a.left}}};var r={};$.expando="velocity"+(new Date).getTime(),$.uuid=0;for(var a={},n=a.hasOwnProperty,o=a.toString,i="Boolean Number String Function Array Date RegExp Object Error".split(" "),s=0;sn;++n){var o=u(r,e,a);if(0===o)return r;var i=l(r,e,a)-t;r-=i/o}return r}function p(){for(var t=0;b>t;++t)w[t]=l(t*x,e,a)}function f(t,r,n){var o,i,s=0;do i=r+(n-r)/2,o=l(i,e,a)-t,o>0?n=i:r=i;while(Math.abs(o)>h&&++s=y?c(t,s):0==l?s:f(t,r,r+x)}function g(){V=!0,(e!=r||a!=n)&&p()}var m=4,y=.001,h=1e-7,v=10,b=11,x=1/(b-1),S="Float32Array"in t;if(4!==arguments.length)return!1;for(var P=0;4>P;++P)if("number"!=typeof arguments[P]||isNaN(arguments[P])||!isFinite(arguments[P]))return!1;e=Math.min(e,1),a=Math.min(a,1),e=Math.max(e,0),a=Math.max(a,0);var w=S?new Float32Array(b):new Array(b),V=!1,C=function(t){return V||g(),e===r&&a===n?t:0===t?0:1===t?1:l(d(t),r,n)};C.getControlPoints=function(){return[{x:e,y:r},{x:a,y:n}]};var T="generateBezier("+[e,r,a,n]+")";return C.toString=function(){return T},C}function u(e,t){var r=e;return g.isString(e)?v.Easings[e]||(r=!1):r=g.isArray(e)&&1===e.length?s.apply(null,e):g.isArray(e)&&2===e.length?b.apply(null,e.concat([t])):g.isArray(e)&&4===e.length?l.apply(null,e):!1,r===!1&&(r=v.Easings[v.defaults.easing]?v.defaults.easing:h),r}function c(e){if(e)for(var t=(new Date).getTime(),r=0,n=v.State.calls.length;n>r;r++)if(v.State.calls[r]){var o=v.State.calls[r],s=o[0],l=o[2],u=o[3];u||(u=v.State.calls[r][3]=t-16);for(var f=Math.min((t-u)/l.duration,1),d=0,m=s.length;m>d;d++){var y=s[d],h=y.element;if(i(h)){var b=!1;if(l.display!==a&&null!==l.display&&"none"!==l.display){if("flex"===l.display){var S=["-webkit-box","-moz-box","-ms-flexbox","-webkit-flex"];$.each(S,function(e,t){x.setPropertyValue(h,"display",t)})}x.setPropertyValue(h,"display",l.display)}l.visibility&&"hidden"!==l.visibility&&x.setPropertyValue(h,"visibility",l.visibility);for(var w in y)if("element"!==w){var V=y[w],C,T=g.isString(V.easing)?v.Easings[V.easing]:V.easing;if(C=1===f?V.endValue:V.startValue+(V.endValue-V.startValue)*T(f),V.currentValue=C,x.Hooks.registered[w]){var k=x.Hooks.getRoot(w),A=i(h).rootPropertyValueCache[k];A&&(V.rootPropertyValue=A)}var F=x.setPropertyValue(h,w,V.currentValue+(0===parseFloat(C)?"":V.unitType),V.rootPropertyValue,V.scrollData);x.Hooks.registered[w]&&(i(h).rootPropertyValueCache[k]=x.Normalizations.registered[k]?x.Normalizations.registered[k]("extract",null,F[1]):F[1]),"transform"===F[0]&&(b=!0)}l.mobileHA&&i(h).transformCache.translate3d===a&&(i(h).transformCache.translate3d="(0px, 0px, 0px)",b=!0),b&&x.flushTransformCache(h)}}l.display!==a&&"none"!==l.display&&(v.State.calls[r][2].display=!1),l.visibility&&"hidden"!==l.visibility&&(v.State.calls[r][2].visibility=!1),l.progress&&l.progress.call(o[1],o[1],f,Math.max(0,u+l.duration-t),u),1===f&&p(r)}v.State.isTicking&&P(c)}function p(e,t){if(!v.State.calls[e])return!1;for(var r=v.State.calls[e][0],n=v.State.calls[e][1],o=v.State.calls[e][2],s=v.State.calls[e][4],l=!1,u=0,c=r.length;c>u;u++){var p=r[u].element;if(t||o.loop||("none"===o.display&&x.setPropertyValue(p,"display",o.display),"hidden"===o.visibility&&x.setPropertyValue(p,"visibility",o.visibility)),($.queue(p)[1]===a||!/\.velocityQueueEntryFlag/i.test($.queue(p)[1]))&&i(p)){i(p).isAnimating=!1,i(p).rootPropertyValueCache={};var f=!1;$.each(x.Lists.transforms3D,function(e,t){var r=/^scale/.test(t)?1:0,n=i(p).transformCache[t];i(p).transformCache[t]!==a&&new RegExp("^\\("+r+"[^.]").test(n)&&(f=!0,delete i(p).transformCache[t])}),o.mobileHA&&(f=!0,delete i(p).transformCache.translate3d),f&&x.flushTransformCache(p),x.Values.removeClass(p,"velocity-animating")}if(!t&&o.complete&&!o.loop&&u===c-1)try{o.complete.call(n,n)}catch(d){setTimeout(function(){throw d},1)}s&&o.loop!==!0&&s(n),o.loop!==!0||t||($.each(i(p).tweensContainer,function(e,t){/^rotate/.test(e)&&360===parseFloat(t.endValue)&&(t.endValue=0,t.startValue=360)}),v(p,"reverse",{loop:!0,delay:o.delay})),o.queue!==!1&&$.dequeue(p,o.queue)}v.State.calls[e]=!1;for(var g=0,m=v.State.calls.length;m>g;g++)if(v.State.calls[g]!==!1){l=!0;break}l===!1&&(v.State.isTicking=!1,delete v.State.calls,v.State.calls=[])}var f=function(){if(r.documentMode)return r.documentMode;for(var e=7;e>4;e--){var t=r.createElement("div");if(t.innerHTML="",t.getElementsByTagName("span").length)return t=null,e}return a}(),d=function(){var e=0;return t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||function(t){var r=(new Date).getTime(),a;return a=Math.max(0,16-(r-e)),e=r+a,setTimeout(function(){t(r+a)},a)}}(),g={isString:function(e){return"string"==typeof e},isArray:Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},isFunction:function(e){return"[object Function]"===Object.prototype.toString.call(e)},isNode:function(e){return e&&e.nodeType},isNodeList:function(e){return"object"==typeof e&&/^\[object (HTMLCollection|NodeList|Object)\]$/.test(Object.prototype.toString.call(e))&&e.length!==a&&(0===e.length||"object"==typeof e[0]&&e[0].nodeType>0)},isWrapped:function(e){return e&&(e.jquery||t.Zepto&&t.Zepto.zepto.isZ(e))},isSVG:function(e){return t.SVGElement&&e instanceof SVGElement},isEmptyObject:function(e){var t;for(t in e)return!1;return!0}},$,m=!1;if(e.fn&&e.fn.jquery?($=e,m=!0):$=t.Velocity.Utilities,8>=f&&!m)throw new Error("Velocity: IE8 and below require jQuery to be loaded before Velocity.");if(7>=f)return void(jQuery.fn.velocity=jQuery.fn.animate);var y=400,h="swing",v={State:{isMobile:/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),isAndroid:/Android/i.test(navigator.userAgent),isGingerbread:/Android 2\.3\.[3-7]/i.test(navigator.userAgent),isChrome:t.chrome,isFirefox:/Firefox/i.test(navigator.userAgent),prefixElement:r.createElement("div"),prefixMatches:{},scrollAnchor:null,scrollPropertyLeft:null,scrollPropertyTop:null,isTicking:!1,calls:[]},CSS:{},Utilities:$,Sequences:{},Easings:{},Promise:t.Promise,defaults:{queue:"",duration:y,easing:h,begin:null,complete:null,progress:null,display:a,loop:!1,delay:!1,mobileHA:!0,_cacheValues:!0},init:function(e){$.data(e,"velocity",{isSVG:g.isSVG(e),isAnimating:!1,computedStyle:null,tweensContainer:null,rootPropertyValueCache:{},transformCache:{}})},animate:null,hook:null,mock:!1,version:{major:1,minor:0,patch:0},debug:!1};t.pageYOffset!==a?(v.State.scrollAnchor=t,v.State.scrollPropertyLeft="pageXOffset",v.State.scrollPropertyTop="pageYOffset"):(v.State.scrollAnchor=r.documentElement||r.body.parentNode||r.body,v.State.scrollPropertyLeft="scrollLeft",v.State.scrollPropertyTop="scrollTop");var b=function(){function e(e){return-e.tension*e.x-e.friction*e.v}function t(t,r,a){var n={x:t.x+a.dx*r,v:t.v+a.dv*r,tension:t.tension,friction:t.friction};return{dx:n.v,dv:e(n)}}function r(r,a){var n={dx:r.v,dv:e(r)},o=t(r,.5*a,n),i=t(r,.5*a,o),s=t(r,a,i),l=1/6*(n.dx+2*(o.dx+i.dx)+s.dx),u=1/6*(n.dv+2*(o.dv+i.dv)+s.dv);return r.x=r.x+l*a,r.v=r.v+u*a,r}return function a(e,t,n){var o={x:-1,v:0,tension:null,friction:null},i=[0],s=0,l=1e-4,u=.016,c,p,f;for(e=parseFloat(e)||500,t=parseFloat(t)||20,n=n||null,o.tension=e,o.friction=t,c=null!==n,c?(s=a(e,t),p=s/n*u):p=u;;)if(f=r(f||o,p),i.push(1+f.x),s+=16,!(Math.abs(f.x)>l&&Math.abs(f.v)>l))break;return c?function(e){return i[e*(i.length-1)|0]}:s}}();v.Easings={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},spring:function(e){return 1-Math.cos(4.5*e*Math.PI)*Math.exp(6*-e)}},$.each([["ease",[.25,.1,.25,1]],["ease-in",[.42,0,1,1]],["ease-out",[0,0,.58,1]],["ease-in-out",[.42,0,.58,1]],["easeInSine",[.47,0,.745,.715]],["easeOutSine",[.39,.575,.565,1]],["easeInOutSine",[.445,.05,.55,.95]],["easeInQuad",[.55,.085,.68,.53]],["easeOutQuad",[.25,.46,.45,.94]],["easeInOutQuad",[.455,.03,.515,.955]],["easeInCubic",[.55,.055,.675,.19]],["easeOutCubic",[.215,.61,.355,1]],["easeInOutCubic",[.645,.045,.355,1]],["easeInQuart",[.895,.03,.685,.22]],["easeOutQuart",[.165,.84,.44,1]],["easeInOutQuart",[.77,0,.175,1]],["easeInQuint",[.755,.05,.855,.06]],["easeOutQuint",[.23,1,.32,1]],["easeInOutQuint",[.86,0,.07,1]],["easeInExpo",[.95,.05,.795,.035]],["easeOutExpo",[.19,1,.22,1]],["easeInOutExpo",[1,0,0,1]],["easeInCirc",[.6,.04,.98,.335]],["easeOutCirc",[.075,.82,.165,1]],["easeInOutCirc",[.785,.135,.15,.86]]],function(e,t){v.Easings[t[0]]=l.apply(null,t[1])});var x=v.CSS={RegEx:{isHex:/^#([A-f\d]{3}){1,2}$/i,valueUnwrap:/^[A-z]+\((.*)\)$/i,wrappedValueAlreadyExtracted:/[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/,valueSplit:/([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/gi},Lists:{colors:["fill","stroke","stopColor","color","backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","outlineColor"],transformsBase:["translateX","translateY","scale","scaleX","scaleY","skewX","skewY","rotateZ"],transforms3D:["transformPerspective","translateZ","scaleZ","rotateX","rotateY"]},Hooks:{templates:{textShadow:["Color X Y Blur","black 0px 0px 0px"],boxShadow:["Color X Y Blur Spread","black 0px 0px 0px 0px"],clip:["Top Right Bottom Left","0px 0px 0px 0px"],backgroundPosition:["X Y","0% 0%"],transformOrigin:["X Y Z","50% 50% 0px"],perspectiveOrigin:["X Y","50% 50%"]},registered:{},register:function(){for(var e=0;e=f)switch(e){case"name":return"filter";case"extract":var a=r.toString().match(/alpha\(opacity=(.*)\)/i);return r=a?a[1]/100:1;case"inject":return t.style.zoom=1,parseFloat(r)>=1?"":"alpha(opacity="+parseInt(100*parseFloat(r),10)+")"}else switch(e){case"name":return"opacity";case"extract":return r;case"inject":return r}}},register:function(){9>=f||v.State.isGingerbread||(x.Lists.transformsBase=x.Lists.transformsBase.concat(x.Lists.transforms3D));for(var e=0;en&&(n=1),o=!/(\d)$/i.test(n);break;case"skew":o=!/(deg|\d)$/i.test(n);break;case"rotate":o=!/(deg|\d)$/i.test(n)}return o||(i(r).transformCache[t]="("+n+")"),i(r).transformCache[t]}}}();for(var e=0;e=f||3!==o.split(" ").length||(o+=" 1"),o;case"inject":return 8>=f?4===n.split(" ").length&&(n=n.split(/\s+/).slice(0,3).join(" ")):3===n.split(" ").length&&(n+=" 1"),(8>=f?"rgb":"rgba")+"("+n.replace(/\s+/g,",").replace(/\.(\d)+(?=,)/g,"")+")"}}}()}},Names:{camelCase:function(e){return e.replace(/-(\w)/g,function(e,t){return t.toUpperCase()})},SVGAttribute:function(e){var t="width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2";return(f||v.State.isAndroid&&!v.State.isChrome)&&(t+="|transform"),new RegExp("^("+t+")$","i").test(e)},prefixCheck:function(e){if(v.State.prefixMatches[e])return[v.State.prefixMatches[e],!0];for(var t=["","Webkit","Moz","ms","O"],r=0,a=t.length;a>r;r++){var n;if(n=0===r?e:t[r]+e.replace(/^\w/,function(e){return e.toUpperCase()}),g.isString(v.State.prefixElement.style[n]))return v.State.prefixMatches[e]=n,[n,!0]}return[e,!1]}},Values:{hexToRgb:function(e){var t=/^#?([a-f\d])([a-f\d])([a-f\d])$/i,r=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,a;return e=e.replace(t,function(e,t,r,a){return t+t+r+r+a+a}),a=r.exec(e),a?[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16)]:[0,0,0]},isCSSNullValue:function(e){return 0==e||/^(none|auto|transparent|(rgba\(0, ?0, ?0, ?0\)))$/i.test(e)},getUnitType:function(e){return/^(rotate|skew)/i.test(e)?"deg":/(^(scale|scaleX|scaleY|scaleZ|alpha|flexGrow|flexHeight|zIndex|fontWeight)$)|((opacity|red|green|blue|alpha)$)/i.test(e)?"":"px"},getDisplayType:function(e){var t=e.tagName.toString().toLowerCase();return/^(b|big|i|small|tt|abbr|acronym|cite|code|dfn|em|kbd|strong|samp|var|a|bdo|br|img|map|object|q|script|span|sub|sup|button|input|label|select|textarea)$/i.test(t)?"inline":/^(li)$/i.test(t)?"list-item":/^(tr)$/i.test(t)?"table-row":"block"},addClass:function(e,t){e.classList?e.classList.add(t):e.className+=(e.className.length?" ":"")+t},removeClass:function(e,t){e.classList?e.classList.remove(t):e.className=e.className.toString().replace(new RegExp("(^|\\s)"+t.split(" ").join("|")+"(\\s|$)","gi")," ")}},getPropertyValue:function(e,r,n,o){function s(e,r){function n(){u&&x.setPropertyValue(e,"display","none")}var l=0;if(8>=f)l=$.css(e,r);else{var u=!1;if(/^(width|height)$/.test(r)&&0===x.getPropertyValue(e,"display")&&(u=!0,x.setPropertyValue(e,"display",x.Values.getDisplayType(e))),!o){if("height"===r&&"border-box"!==x.getPropertyValue(e,"boxSizing").toString().toLowerCase()){var c=e.offsetHeight-(parseFloat(x.getPropertyValue(e,"borderTopWidth"))||0)-(parseFloat(x.getPropertyValue(e,"borderBottomWidth"))||0)-(parseFloat(x.getPropertyValue(e,"paddingTop"))||0)-(parseFloat(x.getPropertyValue(e,"paddingBottom"))||0);return n(),c}if("width"===r&&"border-box"!==x.getPropertyValue(e,"boxSizing").toString().toLowerCase()){var p=e.offsetWidth-(parseFloat(x.getPropertyValue(e,"borderLeftWidth"))||0)-(parseFloat(x.getPropertyValue(e,"borderRightWidth"))||0)-(parseFloat(x.getPropertyValue(e,"paddingLeft"))||0)-(parseFloat(x.getPropertyValue(e,"paddingRight"))||0);return n(),p}}var d;d=i(e)===a?t.getComputedStyle(e,null):i(e).computedStyle?i(e).computedStyle:i(e).computedStyle=t.getComputedStyle(e,null),(f||v.State.isFirefox)&&"borderColor"===r&&(r="borderTopColor"),l=9===f&&"filter"===r?d.getPropertyValue(r):d[r],(""===l||null===l)&&(l=e.style[r]),n()}if("auto"===l&&/^(top|right|bottom|left)$/i.test(r)){var g=s(e,"position");("fixed"===g||"absolute"===g&&/top|left/i.test(r))&&(l=$(e).position()[r]+"px")}return l}var l;if(x.Hooks.registered[r]){var u=r,c=x.Hooks.getRoot(u);n===a&&(n=x.getPropertyValue(e,x.Names.prefixCheck(c)[0])),x.Normalizations.registered[c]&&(n=x.Normalizations.registered[c]("extract",e,n)),l=x.Hooks.extractValue(u,n)}else if(x.Normalizations.registered[r]){var p,d;p=x.Normalizations.registered[r]("name",e),"transform"!==p&&(d=s(e,x.Names.prefixCheck(p)[0]),x.Values.isCSSNullValue(d)&&x.Hooks.templates[r]&&(d=x.Hooks.templates[r][1])),l=x.Normalizations.registered[r]("extract",e,d)}return/^[\d-]/.test(l)||(l=i(e)&&i(e).isSVG&&x.Names.SVGAttribute(r)?/^(height|width)$/i.test(r)?e.getBBox()[r]:e.getAttribute(r):s(e,x.Names.prefixCheck(r)[0])),x.Values.isCSSNullValue(l)&&(l=0),v.debug>=2&&console.log("Get "+r+": "+l),l},setPropertyValue:function(e,r,a,n,o){var s=r;if("scroll"===r)o.container?o.container["scroll"+o.direction]=a:"Left"===o.direction?t.scrollTo(a,o.alternateValue):t.scrollTo(o.alternateValue,a);else if(x.Normalizations.registered[r]&&"transform"===x.Normalizations.registered[r]("name",e))x.Normalizations.registered[r]("inject",e,a),s="transform",a=i(e).transformCache[r];else{if(x.Hooks.registered[r]){var l=r,u=x.Hooks.getRoot(r);n=n||x.getPropertyValue(e,u),a=x.Hooks.injectValue(l,a,n),r=u}if(x.Normalizations.registered[r]&&(a=x.Normalizations.registered[r]("inject",e,a),r=x.Normalizations.registered[r]("name",e)),s=x.Names.prefixCheck(r)[0],8>=f)try{e.style[s]=a}catch(c){v.debug&&console.log("Browser does not support ["+a+"] for ["+s+"]")}else i(e)&&i(e).isSVG&&x.Names.SVGAttribute(r)?e.setAttribute(r,a):e.style[s]=a;v.debug>=2&&console.log("Set "+r+" ("+s+"): "+a)}return[s,a]},flushTransformCache:function(e){function t(t){return parseFloat(x.getPropertyValue(e,t))}var r="";if((f||v.State.isAndroid&&!v.State.isChrome)&&i(e).isSVG){var a={translate:[t("translateX"),t("translateY")],skewX:[t("skewX")],skewY:[t("skewY")],scale:1!==t("scale")?[t("scale"),t("scale")]:[t("scaleX"),t("scaleY")],rotate:[t("rotateZ"),0,0]};$.each(i(e).transformCache,function(e){/^translate/i.test(e)?e="translate":/^scale/i.test(e)?e="scale":/^rotate/i.test(e)&&(e="rotate"),a[e]&&(r+=e+"("+a[e].join(" ")+") ",delete a[e])})}else{var n,o;$.each(i(e).transformCache,function(t){return n=i(e).transformCache[t],"transformPerspective"===t?(o=n,!0):(9===f&&"rotateZ"===t&&(t="rotate"),void(r+=t+n+" "))}),o&&(r="perspective"+o+" "+r)}x.setPropertyValue(e,"transform",r)}};x.Hooks.register(),x.Normalizations.register(),v.hook=function(e,t,r){var n=a;return e=o(e),$.each(e,function(e,o){if(i(o)===a&&v.init(o),r===a)n===a&&(n=v.CSS.getPropertyValue(o,t));else{var s=v.CSS.setPropertyValue(o,t,r);"transform"===s[0]&&v.CSS.flushTransformCache(o),n=s}}),n};var S=function(){function e(){return f?k.promise||null:d}function s(){function e(e){function f(e,t){var r=a,n=a,i=a;return g.isArray(e)?(r=e[0],!g.isArray(e[1])&&/^[\d-]/.test(e[1])||g.isFunction(e[1])||x.RegEx.isHex.test(e[1])?i=e[1]:(g.isString(e[1])&&!x.RegEx.isHex.test(e[1])||g.isArray(e[1]))&&(n=t?e[1]:u(e[1],s.duration),e[2]!==a&&(i=e[2]))):r=e,t||(n=n||s.easing),g.isFunction(r)&&(r=r.call(o,V,w)),g.isFunction(i)&&(i=i.call(o,V,w)),[r||0,n,i]}function d(e,t){var r,a;return a=(t||0).toString().toLowerCase().replace(/[%A-z]+$/,function(e){return r=e,""}),r||(r=x.Values.getUnitType(e)),[a,r]}function m(){var e={myParent:o.parentNode||r.body,position:x.getPropertyValue(o,"position"),fontSize:x.getPropertyValue(o,"fontSize")},a=e.position===L.lastPosition&&e.myParent===L.lastParent,n=e.fontSize===L.lastFontSize;L.lastParent=e.myParent,L.lastPosition=e.position,L.lastFontSize=e.fontSize;var s=100,l={};if(n&&a)l.emToPx=L.lastEmToPx,l.percentToPxWidth=L.lastPercentToPxWidth,l.percentToPxHeight=L.lastPercentToPxHeight;else{var u=i(o).isSVG?r.createElementNS("http://www.w3.org/2000/svg","rect"):r.createElement("div");v.init(u),e.myParent.appendChild(u),$.each(["overflow","overflowX","overflowY"],function(e,t){v.CSS.setPropertyValue(u,t,"hidden")}),v.CSS.setPropertyValue(u,"position",e.position),v.CSS.setPropertyValue(u,"fontSize",e.fontSize),v.CSS.setPropertyValue(u,"boxSizing","content-box"),$.each(["minWidth","maxWidth","width","minHeight","maxHeight","height"],function(e,t){v.CSS.setPropertyValue(u,t,s+"%")}),v.CSS.setPropertyValue(u,"paddingLeft",s+"em"),l.percentToPxWidth=L.lastPercentToPxWidth=(parseFloat(x.getPropertyValue(u,"width",null,!0))||1)/s,l.percentToPxHeight=L.lastPercentToPxHeight=(parseFloat(x.getPropertyValue(u,"height",null,!0))||1)/s,l.emToPx=L.lastEmToPx=(parseFloat(x.getPropertyValue(u,"paddingLeft"))||1)/s,e.myParent.removeChild(u)}return null===L.remToPx&&(L.remToPx=parseFloat(x.getPropertyValue(r.body,"fontSize"))||16),null===L.vwToPx&&(L.vwToPx=parseFloat(t.innerWidth)/100,L.vhToPx=parseFloat(t.innerHeight)/100),l.remToPx=L.remToPx,l.vwToPx=L.vwToPx,l.vhToPx=L.vhToPx,v.debug>=1&&console.log("Unit ratios: "+JSON.stringify(l),o),l}if(s.begin&&0===V)try{s.begin.call(h,h)}catch(y){setTimeout(function(){throw y},1)}if("scroll"===A){var S=/^x$/i.test(s.axis)?"Left":"Top",C=parseFloat(s.offset)||0,T,F,E;s.container?g.isWrapped(s.container)||g.isNode(s.container)?(s.container=s.container[0]||s.container,T=s.container["scroll"+S],E=T+$(o).position()[S.toLowerCase()]+C):s.container=null:(T=v.State.scrollAnchor[v.State["scrollProperty"+S]],F=v.State.scrollAnchor[v.State["scrollProperty"+("Left"===S?"Top":"Left")]],E=$(o).offset()[S.toLowerCase()]+C),l={scroll:{rootPropertyValue:!1,startValue:T,currentValue:T,endValue:E,unitType:"",easing:s.easing,scrollData:{container:s.container,direction:S,alternateValue:F}},element:o},v.debug&&console.log("tweensContainer (scroll): ",l.scroll,o)}else if("reverse"===A){if(!i(o).tweensContainer)return void $.dequeue(o,s.queue);"none"===i(o).opts.display&&(i(o).opts.display="auto"),"hidden"===i(o).opts.visibility&&(i(o).opts.visibility="visible"),i(o).opts.loop=!1,i(o).opts.begin=null,i(o).opts.complete=null,P.easing||delete s.easing,P.duration||delete s.duration,s=$.extend({},i(o).opts,s);var j=$.extend(!0,{},i(o).tweensContainer);for(var H in j)if("element"!==H){var N=j[H].startValue;j[H].startValue=j[H].currentValue=j[H].endValue,j[H].endValue=N,g.isEmptyObject(P)||(j[H].easing=s.easing),v.debug&&console.log("reverse tweensContainer ("+H+"): "+JSON.stringify(j[H]),o)}l=j}else if("start"===A){var j;i(o).tweensContainer&&i(o).isAnimating===!0&&(j=i(o).tweensContainer),$.each(b,function(e,t){if(RegExp("^"+x.Lists.colors.join("$|^")+"$").test(e)){var r=f(t,!0),n=r[0],o=r[1],i=r[2];if(x.RegEx.isHex.test(n)){for(var s=["Red","Green","Blue"],l=x.Values.hexToRgb(n),u=i?x.Values.hexToRgb(i):a,c=0;c1e4&&(v.State.calls=n(v.State.calls)),v.State.calls.push([O,h,s,null,k.resolver]),v.State.isTicking===!1&&(v.State.isTicking=!0,c())):V++)}var o=this,s=$.extend({},v.defaults,P),l={},p;if(i(o)===a&&v.init(o),parseFloat(s.delay)&&s.queue!==!1&&$.queue(o,s.queue,function(e){v.velocityQueueEntryFlag=!0,i(o).delayTimer={setTimeout:setTimeout(e,parseFloat(s.delay)),next:e}}),v.mock===!0)s.duration=1;else switch(s.duration.toString().toLowerCase()){case"fast":s.duration=200;break;case"normal":s.duration=y;break;case"slow":s.duration=600;break;default:s.duration=parseFloat(s.duration)||1}s.easing=u(s.easing,s.duration),s.begin&&!g.isFunction(s.begin)&&(s.begin=null),s.progress&&!g.isFunction(s.progress)&&(s.progress=null),s.complete&&!g.isFunction(s.complete)&&(s.complete=null),s.display!==a&&null!==s.display&&(s.display=s.display.toString().toLowerCase(),"auto"===s.display&&(s.display=v.CSS.Values.getDisplayType(o))),s.visibility&&(s.visibility=s.visibility.toString().toLowerCase()),s.mobileHA=s.mobileHA&&v.State.isMobile&&!v.State.isGingerbread,s.queue===!1?s.delay?setTimeout(e,s.delay):e():$.queue(o,s.queue,function(t,r){return r===!0?(k.promise&&k.resolver(h),!0):(v.velocityQueueEntryFlag=!0,void e(t))}),""!==s.queue&&"fx"!==s.queue||"inprogress"===$.queue(o)[0]||$.dequeue(o)}var l=arguments[0]&&($.isPlainObject(arguments[0].properties)&&!arguments[0].properties.names||g.isString(arguments[0].properties)),f,d,m,h,b,P;if(g.isWrapped(this)?(f=!1,m=0,h=this,d=this):(f=!0,m=1,h=l?arguments[0].elements:arguments[0]),h=o(h)){l?(b=arguments[0].properties,P=arguments[0].options):(b=arguments[m],P=arguments[m+1]);var w=h.length,V=0;if("stop"!==b&&!$.isPlainObject(P)){var C=m+1;P={};for(var T=C;Tq;q++){var R={delay:E.delay};q===z-1&&(R.display=E.display,R.visibility=E.visibility,R.complete=E.complete),S(h,"reverse",R)}return e()}};v=$.extend(S,v),v.animate=S;var P=t.requestAnimationFrame||d;return v.State.isMobile||r.hidden===a||r.addEventListener("visibilitychange",function(){r.hidden?(P=function(e){return setTimeout(function(){e(!0)},16)},c()):P=t.requestAnimationFrame||d}),e.Velocity=v,e!==t&&(e.fn.velocity=S,e.fn.velocity.defaults=v.defaults),$.each(["Down","Up"],function(e,t){v.Sequences["slide"+t]=function(e,r,n,o,i,s){var l=$.extend({},r),u=l.begin,c=l.complete,p={height:"",marginTop:"",marginBottom:"",paddingTop:"",paddingBottom:""},f={};l.display===a&&(l.display="Down"===t?"inline"===v.CSS.Values.getDisplayType(e)?"inline-block":"block":"none"),l.begin=function(){u&&u.call(i,i),f.overflow=e.style.overflow,e.style.overflow="hidden";for(var r in p){f[r]=e.style[r];var a=v.CSS.getPropertyValue(e,r);p[r]="Down"===t?[a,0]:[0,a]}},l.complete=function(){for(var t in f)e.style[t]=f[t];c&&c.call(i,i),s&&s.resolver(i)},v(e,p,l)}}),$.each(["In","Out"],function(e,t){v.Sequences["fade"+t]=function(e,r,n,o,i,s){var l=$.extend({},r),u={opacity:"In"===t?1:0},c=l.complete;l.complete=n!==o-1?l.begin=null:function(){c&&c.call(i,i),s&&s.resolver(i)},l.display===a&&(l.display="In"===t?"auto":"none"),v(this,u,l)}}),v}(window.jQuery||window.Zepto||window,window,document)}); \ No newline at end of file +/*! VelocityJS.org (1.2.1). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License */ +/*! VelocityJS.org jQuery Shim (1.0.1). (C) 2014 The jQuery Foundation. MIT @license: en.wikipedia.org/wiki/MIT_License. */ +!function(e){function t(e){var t=e.length,r=$.type(e);return"function"===r||$.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===r||0===t||"number"==typeof t&&t>0&&t-1 in e}if(!e.jQuery){var $=function(e,t){return new $.fn.init(e,t)};$.isWindow=function(e){return null!=e&&e==e.window},$.type=function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?a[o.call(e)]||"object":typeof e},$.isArray=Array.isArray||function(e){return"array"===$.type(e)},$.isPlainObject=function(e){var t;if(!e||"object"!==$.type(e)||e.nodeType||$.isWindow(e))return!1;try{if(e.constructor&&!n.call(e,"constructor")&&!n.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}for(t in e);return void 0===t||n.call(e,t)},$.each=function(e,r,a){var n,o=0,i=e.length,s=t(e);if(a){if(s)for(;i>o&&(n=r.apply(e[o],a),n!==!1);o++);else for(o in e)if(n=r.apply(e[o],a),n===!1)break}else if(s)for(;i>o&&(n=r.call(e[o],o,e[o]),n!==!1);o++);else for(o in e)if(n=r.call(e[o],o,e[o]),n===!1)break;return e},$.data=function(e,t,a){if(void 0===a){var n=e[$.expando],o=n&&r[n];if(void 0===t)return o;if(o&&t in o)return o[t]}else if(void 0!==t){var n=e[$.expando]||(e[$.expando]=++$.uuid);return r[n]=r[n]||{},r[n][t]=a,a}},$.removeData=function(e,t){var a=e[$.expando],n=a&&r[a];n&&$.each(t,function(e,t){delete n[t]})},$.extend=function(){var e,t,r,a,n,o,i=arguments[0]||{},s=1,l=arguments.length,u=!1;for("boolean"==typeof i&&(u=i,i=arguments[s]||{},s++),"object"!=typeof i&&"function"!==$.type(i)&&(i={}),s===l&&(i=this,s--);l>s;s++)if(null!=(n=arguments[s]))for(a in n)e=i[a],r=n[a],i!==r&&(u&&r&&($.isPlainObject(r)||(t=$.isArray(r)))?(t?(t=!1,o=e&&$.isArray(e)?e:[]):o=e&&$.isPlainObject(e)?e:{},i[a]=$.extend(u,o,r)):void 0!==r&&(i[a]=r));return i},$.queue=function(e,r,a){function n(e,r){var a=r||[];return null!=e&&(t(Object(e))?!function(e,t){for(var r=+t.length,a=0,n=e.length;r>a;)e[n++]=t[a++];if(r!==r)for(;void 0!==t[a];)e[n++]=t[a++];return e.length=n,e}(a,"string"==typeof e?[e]:e):[].push.call(a,e)),a}if(e){r=(r||"fx")+"queue";var o=$.data(e,r);return a?(!o||$.isArray(a)?o=$.data(e,r,n(a)):o.push(a),o):o||[]}},$.dequeue=function(e,t){$.each(e.nodeType?[e]:e,function(e,r){t=t||"fx";var a=$.queue(r,t),n=a.shift();"inprogress"===n&&(n=a.shift()),n&&("fx"===t&&a.unshift("inprogress"),n.call(r,function(){$.dequeue(r,t)}))})},$.fn=$.prototype={init:function(e){if(e.nodeType)return this[0]=e,this;throw new Error("Not a DOM node.")},offset:function(){var t=this[0].getBoundingClientRect?this[0].getBoundingClientRect():{top:0,left:0};return{top:t.top+(e.pageYOffset||document.scrollTop||0)-(document.clientTop||0),left:t.left+(e.pageXOffset||document.scrollLeft||0)-(document.clientLeft||0)}},position:function(){function e(){for(var e=this.offsetParent||document;e&&"html"===!e.nodeType.toLowerCase&&"static"===e.style.position;)e=e.offsetParent;return e||document}var t=this[0],e=e.apply(t),r=this.offset(),a=/^(?:body|html)$/i.test(e.nodeName)?{top:0,left:0}:$(e).offset();return r.top-=parseFloat(t.style.marginTop)||0,r.left-=parseFloat(t.style.marginLeft)||0,e.style&&(a.top+=parseFloat(e.style.borderTopWidth)||0,a.left+=parseFloat(e.style.borderLeftWidth)||0),{top:r.top-a.top,left:r.left-a.left}}};var r={};$.expando="velocity"+(new Date).getTime(),$.uuid=0;for(var a={},n=a.hasOwnProperty,o=a.toString,i="Boolean Number String Function Array Date RegExp Object Error".split(" "),s=0;sn;++n){var o=u(r,e,a);if(0===o)return r;var i=l(r,e,a)-t;r-=i/o}return r}function p(){for(var t=0;b>t;++t)w[t]=l(t*x,e,a)}function f(t,r,n){var o,i,s=0;do i=r+(n-r)/2,o=l(i,e,a)-t,o>0?n=i:r=i;while(Math.abs(o)>h&&++s=y?c(t,s):0==l?s:f(t,r,r+x)}function g(){V=!0,(e!=r||a!=n)&&p()}var m=4,y=.001,h=1e-7,v=10,b=11,x=1/(b-1),S="Float32Array"in t;if(4!==arguments.length)return!1;for(var P=0;4>P;++P)if("number"!=typeof arguments[P]||isNaN(arguments[P])||!isFinite(arguments[P]))return!1;e=Math.min(e,1),a=Math.min(a,1),e=Math.max(e,0),a=Math.max(a,0);var w=S?new Float32Array(b):new Array(b),V=!1,C=function(t){return V||g(),e===r&&a===n?t:0===t?0:1===t?1:l(d(t),r,n)};C.getControlPoints=function(){return[{x:e,y:r},{x:a,y:n}]};var T="generateBezier("+[e,r,a,n]+")";return C.toString=function(){return T},C}function u(e,t){var r=e;return g.isString(e)?v.Easings[e]||(r=!1):r=g.isArray(e)&&1===e.length?s.apply(null,e):g.isArray(e)&&2===e.length?b.apply(null,e.concat([t])):g.isArray(e)&&4===e.length?l.apply(null,e):!1,r===!1&&(r=v.Easings[v.defaults.easing]?v.defaults.easing:h),r}function c(e){if(e){var t=(new Date).getTime(),r=v.State.calls.length;r>1e4&&(v.State.calls=n(v.State.calls));for(var o=0;r>o;o++)if(v.State.calls[o]){var s=v.State.calls[o],l=s[0],u=s[2],f=s[3],d=!!f,m=null;f||(f=v.State.calls[o][3]=t-16);for(var y=Math.min((t-f)/u.duration,1),h=0,b=l.length;b>h;h++){var S=l[h],w=S.element;if(i(w)){var V=!1;if(u.display!==a&&null!==u.display&&"none"!==u.display){if("flex"===u.display){var C=["-webkit-box","-moz-box","-ms-flexbox","-webkit-flex"];$.each(C,function(e,t){x.setPropertyValue(w,"display",t)})}x.setPropertyValue(w,"display",u.display)}u.visibility!==a&&"hidden"!==u.visibility&&x.setPropertyValue(w,"visibility",u.visibility);for(var T in S)if("element"!==T){var k=S[T],A,F=g.isString(k.easing)?v.Easings[k.easing]:k.easing;if(1===y)A=k.endValue;else{var E=k.endValue-k.startValue;if(A=k.startValue+E*F(y,u,E),!d&&A===k.currentValue)continue}if(k.currentValue=A,"tween"===T)m=A;else{if(x.Hooks.registered[T]){var j=x.Hooks.getRoot(T),H=i(w).rootPropertyValueCache[j];H&&(k.rootPropertyValue=H)}var N=x.setPropertyValue(w,T,k.currentValue+(0===parseFloat(A)?"":k.unitType),k.rootPropertyValue,k.scrollData);x.Hooks.registered[T]&&(i(w).rootPropertyValueCache[j]=x.Normalizations.registered[j]?x.Normalizations.registered[j]("extract",null,N[1]):N[1]),"transform"===N[0]&&(V=!0)}}u.mobileHA&&i(w).transformCache.translate3d===a&&(i(w).transformCache.translate3d="(0px, 0px, 0px)",V=!0),V&&x.flushTransformCache(w)}}u.display!==a&&"none"!==u.display&&(v.State.calls[o][2].display=!1),u.visibility!==a&&"hidden"!==u.visibility&&(v.State.calls[o][2].visibility=!1),u.progress&&u.progress.call(s[1],s[1],y,Math.max(0,f+u.duration-t),f,m),1===y&&p(o)}}v.State.isTicking&&P(c)}function p(e,t){if(!v.State.calls[e])return!1;for(var r=v.State.calls[e][0],n=v.State.calls[e][1],o=v.State.calls[e][2],s=v.State.calls[e][4],l=!1,u=0,c=r.length;c>u;u++){var p=r[u].element;if(t||o.loop||("none"===o.display&&x.setPropertyValue(p,"display",o.display),"hidden"===o.visibility&&x.setPropertyValue(p,"visibility",o.visibility)),o.loop!==!0&&($.queue(p)[1]===a||!/\.velocityQueueEntryFlag/i.test($.queue(p)[1]))&&i(p)){i(p).isAnimating=!1,i(p).rootPropertyValueCache={};var f=!1;$.each(x.Lists.transforms3D,function(e,t){var r=/^scale/.test(t)?1:0,n=i(p).transformCache[t];i(p).transformCache[t]!==a&&new RegExp("^\\("+r+"[^.]").test(n)&&(f=!0,delete i(p).transformCache[t])}),o.mobileHA&&(f=!0,delete i(p).transformCache.translate3d),f&&x.flushTransformCache(p),x.Values.removeClass(p,"velocity-animating")}if(!t&&o.complete&&!o.loop&&u===c-1)try{o.complete.call(n,n)}catch(d){setTimeout(function(){throw d},1)}s&&o.loop!==!0&&s(n),o.loop!==!0||t||($.each(i(p).tweensContainer,function(e,t){/^rotate/.test(e)&&360===parseFloat(t.endValue)&&(t.endValue=0,t.startValue=360),/^backgroundPosition/.test(e)&&100===parseFloat(t.endValue)&&"%"===t.unitType&&(t.endValue=0,t.startValue=100)}),v(p,"reverse",{loop:!0,delay:o.delay})),o.queue!==!1&&$.dequeue(p,o.queue)}v.State.calls[e]=!1;for(var g=0,m=v.State.calls.length;m>g;g++)if(v.State.calls[g]!==!1){l=!0;break}l===!1&&(v.State.isTicking=!1,delete v.State.calls,v.State.calls=[])}var f=function(){if(r.documentMode)return r.documentMode;for(var e=7;e>4;e--){var t=r.createElement("div");if(t.innerHTML="",t.getElementsByTagName("span").length)return t=null,e}return a}(),d=function(){var e=0;return t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||function(t){var r=(new Date).getTime(),a;return a=Math.max(0,16-(r-e)),e=r+a,setTimeout(function(){t(r+a)},a)}}(),g={isString:function(e){return"string"==typeof e},isArray:Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},isFunction:function(e){return"[object Function]"===Object.prototype.toString.call(e)},isNode:function(e){return e&&e.nodeType},isNodeList:function(e){return"object"==typeof e&&/^\[object (HTMLCollection|NodeList|Object)\]$/.test(Object.prototype.toString.call(e))&&e.length!==a&&(0===e.length||"object"==typeof e[0]&&e[0].nodeType>0)},isWrapped:function(e){return e&&(e.jquery||t.Zepto&&t.Zepto.zepto.isZ(e))},isSVG:function(e){return t.SVGElement&&e instanceof t.SVGElement},isEmptyObject:function(e){for(var t in e)return!1;return!0}},$,m=!1;if(e.fn&&e.fn.jquery?($=e,m=!0):$=t.Velocity.Utilities,8>=f&&!m)throw new Error("Velocity: IE8 and below require jQuery to be loaded before Velocity.");if(7>=f)return void(jQuery.fn.velocity=jQuery.fn.animate);var y=400,h="swing",v={State:{isMobile:/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),isAndroid:/Android/i.test(navigator.userAgent),isGingerbread:/Android 2\.3\.[3-7]/i.test(navigator.userAgent),isChrome:t.chrome,isFirefox:/Firefox/i.test(navigator.userAgent),prefixElement:r.createElement("div"),prefixMatches:{},scrollAnchor:null,scrollPropertyLeft:null,scrollPropertyTop:null,isTicking:!1,calls:[]},CSS:{},Utilities:$,Redirects:{},Easings:{},Promise:t.Promise,defaults:{queue:"",duration:y,easing:h,begin:a,complete:a,progress:a,display:a,visibility:a,loop:!1,delay:!1,mobileHA:!0,_cacheValues:!0},init:function(e){$.data(e,"velocity",{isSVG:g.isSVG(e),isAnimating:!1,computedStyle:null,tweensContainer:null,rootPropertyValueCache:{},transformCache:{}})},hook:null,mock:!1,version:{major:1,minor:2,patch:1},debug:!1};t.pageYOffset!==a?(v.State.scrollAnchor=t,v.State.scrollPropertyLeft="pageXOffset",v.State.scrollPropertyTop="pageYOffset"):(v.State.scrollAnchor=r.documentElement||r.body.parentNode||r.body,v.State.scrollPropertyLeft="scrollLeft",v.State.scrollPropertyTop="scrollTop");var b=function(){function e(e){return-e.tension*e.x-e.friction*e.v}function t(t,r,a){var n={x:t.x+a.dx*r,v:t.v+a.dv*r,tension:t.tension,friction:t.friction};return{dx:n.v,dv:e(n)}}function r(r,a){var n={dx:r.v,dv:e(r)},o=t(r,.5*a,n),i=t(r,.5*a,o),s=t(r,a,i),l=1/6*(n.dx+2*(o.dx+i.dx)+s.dx),u=1/6*(n.dv+2*(o.dv+i.dv)+s.dv);return r.x=r.x+l*a,r.v=r.v+u*a,r}return function a(e,t,n){var o={x:-1,v:0,tension:null,friction:null},i=[0],s=0,l=1e-4,u=.016,c,p,f;for(e=parseFloat(e)||500,t=parseFloat(t)||20,n=n||null,o.tension=e,o.friction=t,c=null!==n,c?(s=a(e,t),p=s/n*u):p=u;;)if(f=r(f||o,p),i.push(1+f.x),s+=16,!(Math.abs(f.x)>l&&Math.abs(f.v)>l))break;return c?function(e){return i[e*(i.length-1)|0]}:s}}();v.Easings={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},spring:function(e){return 1-Math.cos(4.5*e*Math.PI)*Math.exp(6*-e)}},$.each([["ease",[.25,.1,.25,1]],["ease-in",[.42,0,1,1]],["ease-out",[0,0,.58,1]],["ease-in-out",[.42,0,.58,1]],["easeInSine",[.47,0,.745,.715]],["easeOutSine",[.39,.575,.565,1]],["easeInOutSine",[.445,.05,.55,.95]],["easeInQuad",[.55,.085,.68,.53]],["easeOutQuad",[.25,.46,.45,.94]],["easeInOutQuad",[.455,.03,.515,.955]],["easeInCubic",[.55,.055,.675,.19]],["easeOutCubic",[.215,.61,.355,1]],["easeInOutCubic",[.645,.045,.355,1]],["easeInQuart",[.895,.03,.685,.22]],["easeOutQuart",[.165,.84,.44,1]],["easeInOutQuart",[.77,0,.175,1]],["easeInQuint",[.755,.05,.855,.06]],["easeOutQuint",[.23,1,.32,1]],["easeInOutQuint",[.86,0,.07,1]],["easeInExpo",[.95,.05,.795,.035]],["easeOutExpo",[.19,1,.22,1]],["easeInOutExpo",[1,0,0,1]],["easeInCirc",[.6,.04,.98,.335]],["easeOutCirc",[.075,.82,.165,1]],["easeInOutCirc",[.785,.135,.15,.86]]],function(e,t){v.Easings[t[0]]=l.apply(null,t[1])});var x=v.CSS={RegEx:{isHex:/^#([A-f\d]{3}){1,2}$/i,valueUnwrap:/^[A-z]+\((.*)\)$/i,wrappedValueAlreadyExtracted:/[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/,valueSplit:/([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/gi},Lists:{colors:["fill","stroke","stopColor","color","backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","outlineColor"],transformsBase:["translateX","translateY","scale","scaleX","scaleY","skewX","skewY","rotateZ"],transforms3D:["transformPerspective","translateZ","scaleZ","rotateX","rotateY"]},Hooks:{templates:{textShadow:["Color X Y Blur","black 0px 0px 0px"],boxShadow:["Color X Y Blur Spread","black 0px 0px 0px 0px"],clip:["Top Right Bottom Left","0px 0px 0px 0px"],backgroundPosition:["X Y","0% 0%"],transformOrigin:["X Y Z","50% 50% 0px"],perspectiveOrigin:["X Y","50% 50%"]},registered:{},register:function(){for(var e=0;e=f)switch(e){case"name":return"filter";case"extract":var a=r.toString().match(/alpha\(opacity=(.*)\)/i);return r=a?a[1]/100:1;case"inject":return t.style.zoom=1,parseFloat(r)>=1?"":"alpha(opacity="+parseInt(100*parseFloat(r),10)+")"}else switch(e){case"name":return"opacity";case"extract":return r;case"inject":return r}}},register:function(){9>=f||v.State.isGingerbread||(x.Lists.transformsBase=x.Lists.transformsBase.concat(x.Lists.transforms3D));for(var e=0;en&&(n=1),o=!/(\d)$/i.test(n);break;case"skew":o=!/(deg|\d)$/i.test(n);break;case"rotate":o=!/(deg|\d)$/i.test(n)}return o||(i(r).transformCache[t]="("+n+")"),i(r).transformCache[t]}}}();for(var e=0;e=f||3!==o.split(" ").length||(o+=" 1"),o;case"inject":return 8>=f?4===n.split(" ").length&&(n=n.split(/\s+/).slice(0,3).join(" ")):3===n.split(" ").length&&(n+=" 1"),(8>=f?"rgb":"rgba")+"("+n.replace(/\s+/g,",").replace(/\.(\d)+(?=,)/g,"")+")"}}}()}},Names:{camelCase:function(e){return e.replace(/-(\w)/g,function(e,t){return t.toUpperCase()})},SVGAttribute:function(e){var t="width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2";return(f||v.State.isAndroid&&!v.State.isChrome)&&(t+="|transform"),new RegExp("^("+t+")$","i").test(e)},prefixCheck:function(e){if(v.State.prefixMatches[e])return[v.State.prefixMatches[e],!0];for(var t=["","Webkit","Moz","ms","O"],r=0,a=t.length;a>r;r++){var n;if(n=0===r?e:t[r]+e.replace(/^\w/,function(e){return e.toUpperCase()}),g.isString(v.State.prefixElement.style[n]))return v.State.prefixMatches[e]=n,[n,!0]}return[e,!1]}},Values:{hexToRgb:function(e){var t=/^#?([a-f\d])([a-f\d])([a-f\d])$/i,r=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,a;return e=e.replace(t,function(e,t,r,a){return t+t+r+r+a+a}),a=r.exec(e),a?[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16)]:[0,0,0]},isCSSNullValue:function(e){return 0==e||/^(none|auto|transparent|(rgba\(0, ?0, ?0, ?0\)))$/i.test(e)},getUnitType:function(e){return/^(rotate|skew)/i.test(e)?"deg":/(^(scale|scaleX|scaleY|scaleZ|alpha|flexGrow|flexHeight|zIndex|fontWeight)$)|((opacity|red|green|blue|alpha)$)/i.test(e)?"":"px"},getDisplayType:function(e){var t=e&&e.tagName.toString().toLowerCase();return/^(b|big|i|small|tt|abbr|acronym|cite|code|dfn|em|kbd|strong|samp|var|a|bdo|br|img|map|object|q|script|span|sub|sup|button|input|label|select|textarea)$/i.test(t)?"inline":/^(li)$/i.test(t)?"list-item":/^(tr)$/i.test(t)?"table-row":/^(table)$/i.test(t)?"table":/^(tbody)$/i.test(t)?"table-row-group":"block"},addClass:function(e,t){e.classList?e.classList.add(t):e.className+=(e.className.length?" ":"")+t},removeClass:function(e,t){e.classList?e.classList.remove(t):e.className=e.className.toString().replace(new RegExp("(^|\\s)"+t.split(" ").join("|")+"(\\s|$)","gi")," ")}},getPropertyValue:function(e,r,n,o){function s(e,r){function n(){u&&x.setPropertyValue(e,"display","none")}var l=0;if(8>=f)l=$.css(e,r);else{var u=!1;if(/^(width|height)$/.test(r)&&0===x.getPropertyValue(e,"display")&&(u=!0,x.setPropertyValue(e,"display",x.Values.getDisplayType(e))),!o){if("height"===r&&"border-box"!==x.getPropertyValue(e,"boxSizing").toString().toLowerCase()){var c=e.offsetHeight-(parseFloat(x.getPropertyValue(e,"borderTopWidth"))||0)-(parseFloat(x.getPropertyValue(e,"borderBottomWidth"))||0)-(parseFloat(x.getPropertyValue(e,"paddingTop"))||0)-(parseFloat(x.getPropertyValue(e,"paddingBottom"))||0);return n(),c}if("width"===r&&"border-box"!==x.getPropertyValue(e,"boxSizing").toString().toLowerCase()){var p=e.offsetWidth-(parseFloat(x.getPropertyValue(e,"borderLeftWidth"))||0)-(parseFloat(x.getPropertyValue(e,"borderRightWidth"))||0)-(parseFloat(x.getPropertyValue(e,"paddingLeft"))||0)-(parseFloat(x.getPropertyValue(e,"paddingRight"))||0);return n(),p}}var d;d=i(e)===a?t.getComputedStyle(e,null):i(e).computedStyle?i(e).computedStyle:i(e).computedStyle=t.getComputedStyle(e,null),"borderColor"===r&&(r="borderTopColor"),l=9===f&&"filter"===r?d.getPropertyValue(r):d[r],(""===l||null===l)&&(l=e.style[r]),n()}if("auto"===l&&/^(top|right|bottom|left)$/i.test(r)){var g=s(e,"position");("fixed"===g||"absolute"===g&&/top|left/i.test(r))&&(l=$(e).position()[r]+"px")}return l}var l;if(x.Hooks.registered[r]){var u=r,c=x.Hooks.getRoot(u);n===a&&(n=x.getPropertyValue(e,x.Names.prefixCheck(c)[0])),x.Normalizations.registered[c]&&(n=x.Normalizations.registered[c]("extract",e,n)),l=x.Hooks.extractValue(u,n)}else if(x.Normalizations.registered[r]){var p,d;p=x.Normalizations.registered[r]("name",e),"transform"!==p&&(d=s(e,x.Names.prefixCheck(p)[0]),x.Values.isCSSNullValue(d)&&x.Hooks.templates[r]&&(d=x.Hooks.templates[r][1])),l=x.Normalizations.registered[r]("extract",e,d)}if(!/^[\d-]/.test(l))if(i(e)&&i(e).isSVG&&x.Names.SVGAttribute(r))if(/^(height|width)$/i.test(r))try{l=e.getBBox()[r]}catch(g){l=0}else l=e.getAttribute(r);else l=s(e,x.Names.prefixCheck(r)[0]);return x.Values.isCSSNullValue(l)&&(l=0),v.debug>=2&&console.log("Get "+r+": "+l),l},setPropertyValue:function(e,r,a,n,o){var s=r;if("scroll"===r)o.container?o.container["scroll"+o.direction]=a:"Left"===o.direction?t.scrollTo(a,o.alternateValue):t.scrollTo(o.alternateValue,a);else if(x.Normalizations.registered[r]&&"transform"===x.Normalizations.registered[r]("name",e))x.Normalizations.registered[r]("inject",e,a),s="transform",a=i(e).transformCache[r];else{if(x.Hooks.registered[r]){var l=r,u=x.Hooks.getRoot(r);n=n||x.getPropertyValue(e,u),a=x.Hooks.injectValue(l,a,n),r=u}if(x.Normalizations.registered[r]&&(a=x.Normalizations.registered[r]("inject",e,a),r=x.Normalizations.registered[r]("name",e)),s=x.Names.prefixCheck(r)[0],8>=f)try{e.style[s]=a}catch(c){v.debug&&console.log("Browser does not support ["+a+"] for ["+s+"]")}else i(e)&&i(e).isSVG&&x.Names.SVGAttribute(r)?e.setAttribute(r,a):e.style[s]=a;v.debug>=2&&console.log("Set "+r+" ("+s+"): "+a)}return[s,a]},flushTransformCache:function(e){function t(t){return parseFloat(x.getPropertyValue(e,t))}var r="";if((f||v.State.isAndroid&&!v.State.isChrome)&&i(e).isSVG){var a={translate:[t("translateX"),t("translateY")],skewX:[t("skewX")],skewY:[t("skewY")],scale:1!==t("scale")?[t("scale"),t("scale")]:[t("scaleX"),t("scaleY")],rotate:[t("rotateZ"),0,0]};$.each(i(e).transformCache,function(e){/^translate/i.test(e)?e="translate":/^scale/i.test(e)?e="scale":/^rotate/i.test(e)&&(e="rotate"),a[e]&&(r+=e+"("+a[e].join(" ")+") ",delete a[e])})}else{var n,o;$.each(i(e).transformCache,function(t){return n=i(e).transformCache[t],"transformPerspective"===t?(o=n,!0):(9===f&&"rotateZ"===t&&(t="rotate"),void(r+=t+n+" "))}),o&&(r="perspective"+o+" "+r)}x.setPropertyValue(e,"transform",r)}};x.Hooks.register(),x.Normalizations.register(),v.hook=function(e,t,r){var n=a;return e=o(e),$.each(e,function(e,o){if(i(o)===a&&v.init(o),r===a)n===a&&(n=v.CSS.getPropertyValue(o,t));else{var s=v.CSS.setPropertyValue(o,t,r);"transform"===s[0]&&v.CSS.flushTransformCache(o),n=s}}),n};var S=function(){function e(){return l?T.promise||null:f}function n(){function e(e){function p(e,t){var r=a,i=a,s=a;return g.isArray(e)?(r=e[0],!g.isArray(e[1])&&/^[\d-]/.test(e[1])||g.isFunction(e[1])||x.RegEx.isHex.test(e[1])?s=e[1]:(g.isString(e[1])&&!x.RegEx.isHex.test(e[1])||g.isArray(e[1]))&&(i=t?e[1]:u(e[1],o.duration),e[2]!==a&&(s=e[2]))):r=e,t||(i=i||o.easing),g.isFunction(r)&&(r=r.call(n,w,P)),g.isFunction(s)&&(s=s.call(n,w,P)),[r||0,i,s]}function f(e,t){var r,a;return a=(t||"0").toString().toLowerCase().replace(/[%A-z]+$/,function(e){return r=e,""}),r||(r=x.Values.getUnitType(e)),[a,r]}function d(){var e={myParent:n.parentNode||r.body,position:x.getPropertyValue(n,"position"),fontSize:x.getPropertyValue(n,"fontSize")},a=e.position===N.lastPosition&&e.myParent===N.lastParent,o=e.fontSize===N.lastFontSize;N.lastParent=e.myParent,N.lastPosition=e.position,N.lastFontSize=e.fontSize;var s=100,l={};if(o&&a)l.emToPx=N.lastEmToPx,l.percentToPxWidth=N.lastPercentToPxWidth,l.percentToPxHeight=N.lastPercentToPxHeight;else{var u=i(n).isSVG?r.createElementNS("http://www.w3.org/2000/svg","rect"):r.createElement("div");v.init(u),e.myParent.appendChild(u),$.each(["overflow","overflowX","overflowY"],function(e,t){v.CSS.setPropertyValue(u,t,"hidden")}),v.CSS.setPropertyValue(u,"position",e.position),v.CSS.setPropertyValue(u,"fontSize",e.fontSize),v.CSS.setPropertyValue(u,"boxSizing","content-box"),$.each(["minWidth","maxWidth","width","minHeight","maxHeight","height"],function(e,t){v.CSS.setPropertyValue(u,t,s+"%")}),v.CSS.setPropertyValue(u,"paddingLeft",s+"em"),l.percentToPxWidth=N.lastPercentToPxWidth=(parseFloat(x.getPropertyValue(u,"width",null,!0))||1)/s,l.percentToPxHeight=N.lastPercentToPxHeight=(parseFloat(x.getPropertyValue(u,"height",null,!0))||1)/s,l.emToPx=N.lastEmToPx=(parseFloat(x.getPropertyValue(u,"paddingLeft"))||1)/s,e.myParent.removeChild(u)}return null===N.remToPx&&(N.remToPx=parseFloat(x.getPropertyValue(r.body,"fontSize"))||16),null===N.vwToPx&&(N.vwToPx=parseFloat(t.innerWidth)/100,N.vhToPx=parseFloat(t.innerHeight)/100),l.remToPx=N.remToPx,l.vwToPx=N.vwToPx,l.vhToPx=N.vhToPx,v.debug>=1&&console.log("Unit ratios: "+JSON.stringify(l),n),l}if(o.begin&&0===w)try{o.begin.call(m,m)}catch(y){setTimeout(function(){throw y},1)}if("scroll"===k){var S=/^x$/i.test(o.axis)?"Left":"Top",V=parseFloat(o.offset)||0,C,A,F;o.container?g.isWrapped(o.container)||g.isNode(o.container)?(o.container=o.container[0]||o.container,C=o.container["scroll"+S],F=C+$(n).position()[S.toLowerCase()]+V):o.container=null:(C=v.State.scrollAnchor[v.State["scrollProperty"+S]],A=v.State.scrollAnchor[v.State["scrollProperty"+("Left"===S?"Top":"Left")]],F=$(n).offset()[S.toLowerCase()]+V),s={scroll:{rootPropertyValue:!1,startValue:C,currentValue:C,endValue:F,unitType:"",easing:o.easing,scrollData:{container:o.container,direction:S,alternateValue:A}},element:n},v.debug&&console.log("tweensContainer (scroll): ",s.scroll,n)}else if("reverse"===k){if(!i(n).tweensContainer)return void $.dequeue(n,o.queue);"none"===i(n).opts.display&&(i(n).opts.display="auto"),"hidden"===i(n).opts.visibility&&(i(n).opts.visibility="visible"),i(n).opts.loop=!1,i(n).opts.begin=null,i(n).opts.complete=null,b.easing||delete o.easing,b.duration||delete o.duration,o=$.extend({},i(n).opts,o);var E=$.extend(!0,{},i(n).tweensContainer);for(var j in E)if("element"!==j){var H=E[j].startValue;E[j].startValue=E[j].currentValue=E[j].endValue,E[j].endValue=H,g.isEmptyObject(b)||(E[j].easing=o.easing),v.debug&&console.log("reverse tweensContainer ("+j+"): "+JSON.stringify(E[j]),n)}s=E}else if("start"===k){var E;i(n).tweensContainer&&i(n).isAnimating===!0&&(E=i(n).tweensContainer),$.each(h,function(e,t){if(RegExp("^"+x.Lists.colors.join("$|^")+"$").test(e)){var r=p(t,!0),n=r[0],o=r[1],i=r[2];if(x.RegEx.isHex.test(n)){for(var s=["Red","Green","Blue"],l=x.Values.hexToRgb(n),u=i?x.Values.hexToRgb(i):a,c=0;cO;O++){var z={delay:F.delay,progress:F.progress};O===R-1&&(z.display=F.display,z.visibility=F.visibility,z.complete=F.complete),S(m,"reverse",z)}return e()}};v=$.extend(S,v),v.animate=S;var P=t.requestAnimationFrame||d;return v.State.isMobile||r.hidden===a||r.addEventListener("visibilitychange",function(){r.hidden?(P=function(e){return setTimeout(function(){e(!0)},16)},c()):P=t.requestAnimationFrame||d}),e.Velocity=v,e!==t&&(e.fn.velocity=S,e.fn.velocity.defaults=v.defaults),$.each(["Down","Up"],function(e,t){v.Redirects["slide"+t]=function(e,r,n,o,i,s){var l=$.extend({},r),u=l.begin,c=l.complete,p={height:"",marginTop:"",marginBottom:"",paddingTop:"",paddingBottom:""},f={};l.display===a&&(l.display="Down"===t?"inline"===v.CSS.Values.getDisplayType(e)?"inline-block":"block":"none"),l.begin=function(){u&&u.call(i,i);for(var r in p){f[r]=e.style[r];var a=v.CSS.getPropertyValue(e,r);p[r]="Down"===t?[a,0]:[0,a]}f.overflow=e.style.overflow,e.style.overflow="hidden"},l.complete=function(){for(var t in f)e.style[t]=f[t];c&&c.call(i,i),s&&s.resolver(i)},v(e,p,l)}}),$.each(["In","Out"],function(e,t){v.Redirects["fade"+t]=function(e,r,n,o,i,s){var l=$.extend({},r),u={opacity:"In"===t?1:0},c=l.complete;l.complete=n!==o-1?l.begin=null:function(){c&&c.call(i,i),s&&s.resolver(i)},l.display===a&&(l.display="In"===t?"auto":"none"),v(this,u,l)}}),v}(window.jQuery||window.Zepto||window,window,document)}); \ No newline at end of file diff --git a/app/assets/javascripts/app/lib/animations/velocity.ui.js b/app/assets/javascripts/app/lib/animations/velocity.ui.js index 2f4aee8ef..e5cc830c3 100644 --- a/app/assets/javascripts/app/lib/animations/velocity.ui.js +++ b/app/assets/javascripts/app/lib/animations/velocity.ui.js @@ -2,48 +2,77 @@ Velocity UI Pack **********************/ -/* VelocityJS.org UI Pack (4.1.4). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License. Portions copyright Daniel Eden, Christian Pucci. */ +/* VelocityJS.org UI Pack (5.0.2). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License. Portions copyright Daniel Eden, Christian Pucci. */ -;(function (factory) { +;(function (factory) { /* CommonJS module. */ - if (typeof module === "object" && typeof module.exports === "object") { + if (typeof require === "function" && typeof exports === "object" ) { module.exports = factory(); /* AMD module. */ } else if (typeof define === "function" && define.amd) { define([ "velocity" ], factory); /* Browser globals. */ - } else { + } else { factory(); } }(function() { return function (global, window, document, undefined) { - /************** + /************* Checks - **************/ + *************/ if (!global.Velocity || !global.Velocity.Utilities) { window.console && console.log("Velocity UI Pack: Velocity must be loaded first. Aborting."); return; - } else if (!global.Velocity.version || (global.Velocity.version.major <= 0 && global.Velocity.version.minor <= 11 && global.Velocity.version.patch < 8)) { - var abortError = "Velocity UI Pack: You need to update Velocity (jquery.velocity.js) to a newer version. Visit http://github.com/julianshapiro/velocity."; + } else { + var Velocity = global.Velocity, + $ = Velocity.Utilities; + } + var velocityVersion = Velocity.version, + requiredVersion = { major: 1, minor: 1, patch: 0 }; + + function greaterSemver (primary, secondary) { + var versionInts = []; + + if (!primary || !secondary) { return false; } + + $.each([ primary, secondary ], function(i, versionObject) { + var versionIntsComponents = []; + + $.each(versionObject, function(component, value) { + while (value.toString().length < 5) { + value = "0" + value; + } + versionIntsComponents.push(value); + }); + + versionInts.push(versionIntsComponents.join("")) + }); + + return (parseFloat(versionInts[0]) > parseFloat(versionInts[1])); + } + + if (greaterSemver(requiredVersion, velocityVersion)){ + var abortError = "Velocity UI Pack: You need to update Velocity (jquery.velocity.js) to a newer version. Visit http://github.com/julianshapiro/velocity."; alert(abortError); throw new Error(abortError); } - /****************** - Register UI - ******************/ + /************************ + Effect Registration + ************************/ - global.Velocity.RegisterUI = function (effectName, properties) { + /* Note: RegisterUI is a legacy name. */ + Velocity.RegisterEffect = Velocity.RegisterUI = function (effectName, properties) { /* Animate the expansion/contraction of the elements' parent's height for In/Out effects. */ function animateParentHeight (elements, direction, totalDuration, stagger) { var totalHeightDelta = 0, parentNode; /* Sum the total height (including padding and margin) of all targeted elements. */ - global.Velocity.Utilities.each(elements.nodeType ? [ elements ] : elements, function(i, element) { + $.each(elements.nodeType ? [ elements ] : elements, function(i, element) { if (stagger) { /* Increase the totalDuration by the successive delay amounts produced by the stagger option. */ totalDuration += i * stagger; @@ -51,84 +80,99 @@ return function (global, window, document, undefined) { parentNode = element.parentNode; - global.Velocity.Utilities.each([ "height", "paddingTop", "paddingBottom", "marginTop", "marginBottom"], function(i, property) { - totalHeightDelta += parseFloat(global.Velocity.CSS.getPropertyValue(element, property)); + $.each([ "height", "paddingTop", "paddingBottom", "marginTop", "marginBottom"], function(i, property) { + totalHeightDelta += parseFloat(Velocity.CSS.getPropertyValue(element, property)); }); }); /* Animate the parent element's height adjustment (with a varying duration multiplier for aesthetic benefits). */ - global.Velocity.animate( + Velocity.animate( parentNode, { height: (direction === "In" ? "+" : "-") + "=" + totalHeightDelta }, { queue: false, easing: "ease-in-out", duration: totalDuration * (direction === "In" ? 0.6 : 1) } ); } - /* Register a custom sequence for each effect. */ - global.Velocity.Sequences[effectName] = function (element, sequenceOptions, elementsIndex, elementsSize, elements, promiseData) { + /* Register a custom redirect for each effect. */ + Velocity.Redirects[effectName] = function (element, redirectOptions, elementsIndex, elementsSize, elements, promiseData) { var finalElement = (elementsIndex === elementsSize - 1); + if (typeof properties.defaultDuration === "function") { + properties.defaultDuration = properties.defaultDuration.call(elements, elements); + } else { + properties.defaultDuration = parseFloat(properties.defaultDuration); + } + /* Iterate through each effect's call array. */ for (var callIndex = 0; callIndex < properties.calls.length; callIndex++) { var call = properties.calls[callIndex], propertyMap = call[0], - sequenceDuration = (sequenceOptions.duration || properties.defaultDuration || 1000), + redirectDuration = (redirectOptions.duration || properties.defaultDuration || 1000), durationPercentage = call[1], callOptions = call[2] || {}, opts = {}; /* Assign the whitelisted per-call options. */ - opts.duration = sequenceDuration * (durationPercentage || 1); - opts.queue = sequenceOptions.queue || ""; + opts.duration = redirectDuration * (durationPercentage || 1); + opts.queue = redirectOptions.queue || ""; opts.easing = callOptions.easing || "ease"; - opts.delay = callOptions.delay || 0; + opts.delay = parseFloat(callOptions.delay) || 0; opts._cacheValues = callOptions._cacheValues || true; /* Special processing for the first effect call. */ if (callIndex === 0) { - /* If a delay was passed into the sequence, combine it with the first call's delay. */ - opts.delay += (sequenceOptions.delay || 0); + /* If a delay was passed into the redirect, combine it with the first call's delay. */ + opts.delay += (parseFloat(redirectOptions.delay) || 0); if (elementsIndex === 0) { opts.begin = function() { /* Only trigger a begin callback on the first effect call with the first element in the set. */ - sequenceOptions.begin && sequenceOptions.begin.call(elements, elements); + redirectOptions.begin && redirectOptions.begin.call(elements, elements); + + var direction = effectName.match(/(In|Out)$/); + + /* Make "in" transitioning elements invisible immediately so that there's no FOUC between now + and the first RAF tick. */ + if ((direction && direction[0] === "In") && propertyMap.opacity !== undefined) { + $.each(elements.nodeType ? [ elements ] : elements, function(i, element) { + Velocity.CSS.setPropertyValue(element, "opacity", 0); + }); + } /* Only trigger animateParentHeight() if we're using an In/Out transition. */ - var direction = effectName.match(/(In|Out)$/); - if (sequenceOptions.animateParentHeight && direction) { - animateParentHeight(elements, direction[0], sequenceDuration + opts.delay, sequenceOptions.stagger); + if (redirectOptions.animateParentHeight && direction) { + animateParentHeight(elements, direction[0], redirectDuration + opts.delay, redirectOptions.stagger); } } } /* If the user isn't overriding the display option, default to "auto" for "In"-suffixed transitions. */ - if (sequenceOptions.display !== null) { - if (sequenceOptions.display !== undefined && sequenceOptions.display !== "none") { - opts.display = sequenceOptions.display; + if (redirectOptions.display !== null) { + if (redirectOptions.display !== undefined && redirectOptions.display !== "none") { + opts.display = redirectOptions.display; } else if (/In$/.test(effectName)) { /* Inline elements cannot be subjected to transforms, so we switch them to inline-block. */ - var defaultDisplay = global.Velocity.CSS.Values.getDisplayType(element); + var defaultDisplay = Velocity.CSS.Values.getDisplayType(element); opts.display = (defaultDisplay === "inline") ? "inline-block" : defaultDisplay; } } - if (sequenceOptions.visibility && sequenceOptions.visibility !== "hidden") { - opts.visibility = sequenceOptions.visibility; + if (redirectOptions.visibility && redirectOptions.visibility !== "hidden") { + opts.visibility = redirectOptions.visibility; } } /* Special processing for the last effect call. */ if (callIndex === properties.calls.length - 1) { - /* Append promise resolving onto the user's sequence callback. */ + /* Append promise resolving onto the user's redirect callback. */ function injectFinalCallbacks () { - if ((sequenceOptions.display === undefined || sequenceOptions.display === "none") && /Out$/.test(effectName)) { - global.Velocity.Utilities.each(elements.nodeType ? [ elements ] : elements, function(i, element) { - global.Velocity.CSS.setPropertyValue(element, "display", "none"); + if ((redirectOptions.display === undefined || redirectOptions.display === "none") && /Out$/.test(effectName)) { + $.each(elements.nodeType ? [ elements ] : elements, function(i, element) { + Velocity.CSS.setPropertyValue(element, "display", "none"); }); } - sequenceOptions.complete && sequenceOptions.complete.call(elements, elements); + redirectOptions.complete && redirectOptions.complete.call(elements, elements); if (promiseData) { promiseData.resolver(elements || element); @@ -142,7 +186,8 @@ return function (global, window, document, undefined) { /* Format each non-array value in the reset property map to [ value, value ] so that changes apply immediately and DOM querying is avoided (via forcefeeding). */ - if (typeof resetValue === "string" || typeof resetValue === "number") { + /* Note: Don't forcefeed hooks, otherwise their hook roots will be defaulted to their null values. */ + if (Velocity.CSS.Hooks.registered[resetProperty] === undefined && (typeof resetValue === "string" || typeof resetValue === "number")) { properties.reset[resetProperty] = [ properties.reset[resetProperty], properties.reset[resetProperty] ]; } } @@ -153,26 +198,26 @@ return function (global, window, document, undefined) { /* Since the reset option uses up the complete callback, we trigger the user's complete callback at the end of ours. */ if (finalElement) { resetOptions.complete = injectFinalCallbacks; - } + } - global.Velocity.animate(element, properties.reset, resetOptions); + Velocity.animate(element, properties.reset, resetOptions); /* Only trigger the user's complete callback on the last effect call with the last element in the set. */ } else if (finalElement) { injectFinalCallbacks(); } }; - if (sequenceOptions.visibility === "hidden") { - opts.visibility = sequenceOptions.visibility; + if (redirectOptions.visibility === "hidden") { + opts.visibility = redirectOptions.visibility; } } - global.Velocity.animate(element, propertyMap, opts); + Velocity.animate(element, propertyMap, opts); } }; /* Return the Velocity object so that RegisterUI calls can be chained. */ - return global.Velocity; + return Velocity; }; /********************* @@ -181,8 +226,8 @@ return function (global, window, document, undefined) { /* Externalize the packagedEffects data so that they can optionally be modified and re-registered. */ /* Support: <=IE8: Callouts will have no effect, and transitions will simply fade in/out. IE9/Android 2.3: Most effects are fully supported, the rest fade in/out. All other browsers: full support. */ - global.Velocity.RegisterUI.packagedEffects = - { + Velocity.RegisterEffect.packagedEffects = + { /* Animate.css */ "callout.bounce": { defaultDuration: 550, @@ -196,7 +241,7 @@ return function (global, window, document, undefined) { /* Animate.css */ "callout.shake": { defaultDuration: 800, - calls: [ + calls: [ [ { translateX: -11 }, 0.125 ], [ { translateX: 11 }, 0.125 ], [ { translateX: -11 }, 0.125 ], @@ -210,7 +255,7 @@ return function (global, window, document, undefined) { /* Animate.css */ "callout.flash": { defaultDuration: 1100, - calls: [ + calls: [ [ { opacity: [ 0, "easeInOutQuad", 1 ] }, 0.25 ], [ { opacity: [ 1, "easeInOutQuad" ] }, 0.25 ], [ { opacity: [ 0, "easeInOutQuad" ] }, 0.25 ], @@ -220,15 +265,15 @@ return function (global, window, document, undefined) { /* Animate.css */ "callout.pulse": { defaultDuration: 825, - calls: [ - [ { scaleX: 1.1, scaleY: 1.1 }, 0.50 ], + calls: [ + [ { scaleX: 1.1, scaleY: 1.1 }, 0.50, { easing: "easeInExpo" } ], [ { scaleX: 1, scaleY: 1 }, 0.50 ] ] }, /* Animate.css */ "callout.swing": { defaultDuration: 950, - calls: [ + calls: [ [ { rotateZ: 15 }, 0.20 ], [ { rotateZ: -10 }, 0.20 ], [ { rotateZ: 5 }, 0.20 ], @@ -239,7 +284,7 @@ return function (global, window, document, undefined) { /* Animate.css */ "callout.tada": { defaultDuration: 1000, - calls: [ + calls: [ [ { scaleX: 0.9, scaleY: 0.9, rotateZ: -3 }, 0.10 ], [ { scaleX: 1.1, scaleY: 1.1, rotateZ: 3 }, 0.10 ], [ { scaleX: 1.1, scaleY: 1.1, rotateZ: -3 }, 0.10 ], @@ -266,7 +311,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.flipXIn": { defaultDuration: 700, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], transformPerspective: [ 800, 800 ], rotateY: [ 0, -55 ] } ] ], reset: { transformPerspective: 0 } @@ -274,7 +319,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.flipXOut": { defaultDuration: 700, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], transformPerspective: [ 800, 800 ], rotateY: 55 } ] ], reset: { transformPerspective: 0, rotateY: 0 } @@ -282,7 +327,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.flipYIn": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], transformPerspective: [ 800, 800 ], rotateX: [ 0, -45 ] } ] ], reset: { transformPerspective: 0 } @@ -290,7 +335,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.flipYOut": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], transformPerspective: [ 800, 800 ], rotateX: 25 } ] ], reset: { transformPerspective: 0, rotateX: 0 } @@ -299,7 +344,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.flipBounceXIn": { defaultDuration: 900, - calls: [ + calls: [ [ { opacity: [ 0.725, 0 ], transformPerspective: [ 400, 400 ], rotateY: [ -10, 90 ] }, 0.50 ], [ { opacity: 0.80, rotateY: 10 }, 0.25 ], [ { opacity: 1, rotateY: 0 }, 0.25 ] @@ -310,7 +355,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.flipBounceXOut": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 0.9, 1 ], transformPerspective: [ 400, 400 ], rotateY: -10 }, 0.50 ], [ { opacity: 0, rotateY: 90 }, 0.50 ] ], @@ -320,7 +365,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.flipBounceYIn": { defaultDuration: 850, - calls: [ + calls: [ [ { opacity: [ 0.725, 0 ], transformPerspective: [ 400, 400 ], rotateX: [ -10, 90 ] }, 0.50 ], [ { opacity: 0.80, rotateX: 10 }, 0.25 ], [ { opacity: 1, rotateX: 0 }, 0.25 ] @@ -331,7 +376,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.flipBounceYOut": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 0.9, 1 ], transformPerspective: [ 400, 400 ], rotateX: -15 }, 0.50 ], [ { opacity: 0, rotateX: 90 }, 0.50 ] ], @@ -340,7 +385,7 @@ return function (global, window, document, undefined) { /* Magic.css */ "transition.swoopIn": { defaultDuration: 850, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], transformOriginX: [ "100%", "50%" ], transformOriginY: [ "100%", "100%" ], scaleX: [ 1, 0 ], scaleY: [ 1, 0 ], translateX: [ 0, -700 ], translateZ: 0 } ] ], reset: { transformOriginX: "50%", transformOriginY: "50%" } @@ -348,7 +393,7 @@ return function (global, window, document, undefined) { /* Magic.css */ "transition.swoopOut": { defaultDuration: 850, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], transformOriginX: [ "50%", "100%" ], transformOriginY: [ "100%", "100%" ], scaleX: 0, scaleY: 0, translateX: -700, translateZ: 0 } ] ], reset: { transformOriginX: "50%", transformOriginY: "50%", scaleX: 1, scaleY: 1, translateX: 0 } @@ -356,42 +401,42 @@ return function (global, window, document, undefined) { /* Magic.css */ /* Support: Loses rotation in IE9/Android 2.3. (Fades and scales only.) */ "transition.whirlIn": { - defaultDuration: 900, - calls: [ - [ { opacity: [ 1, 0 ], transformOriginX: [ "50%", "50%" ], transformOriginY: [ "50%", "50%" ], scaleX: [ 1, 0 ], scaleY: [ 1, 0 ], rotateY: [ 0, 160 ] } ] + defaultDuration: 850, + calls: [ + [ { opacity: [ 1, 0 ], transformOriginX: [ "50%", "50%" ], transformOriginY: [ "50%", "50%" ], scaleX: [ 1, 0 ], scaleY: [ 1, 0 ], rotateY: [ 0, 160 ] }, 1, { easing: "easeInOutSine" } ] ] }, /* Magic.css */ /* Support: Loses rotation in IE9/Android 2.3. (Fades and scales only.) */ "transition.whirlOut": { - defaultDuration: 900, - calls: [ - [ { opacity: [ 0, 1 ], transformOriginX: [ "50%", "50%" ], transformOriginY: [ "50%", "50%" ], scaleX: 0, scaleY: 0, rotateY: 160 } ] + defaultDuration: 750, + calls: [ + [ { opacity: [ 0, "easeInOutQuint", 1 ], transformOriginX: [ "50%", "50%" ], transformOriginY: [ "50%", "50%" ], scaleX: 0, scaleY: 0, rotateY: 160 }, 1, { easing: "swing" } ] ], reset: { scaleX: 1, scaleY: 1, rotateY: 0 } }, "transition.shrinkIn": { - defaultDuration: 700, - calls: [ + defaultDuration: 750, + calls: [ [ { opacity: [ 1, 0 ], transformOriginX: [ "50%", "50%" ], transformOriginY: [ "50%", "50%" ], scaleX: [ 1, 1.5 ], scaleY: [ 1, 1.5 ], translateZ: 0 } ] ] }, "transition.shrinkOut": { - defaultDuration: 650, - calls: [ + defaultDuration: 600, + calls: [ [ { opacity: [ 0, 1 ], transformOriginX: [ "50%", "50%" ], transformOriginY: [ "50%", "50%" ], scaleX: 1.3, scaleY: 1.3, translateZ: 0 } ] ], reset: { scaleX: 1, scaleY: 1 } }, "transition.expandIn": { defaultDuration: 700, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], transformOriginX: [ "50%", "50%" ], transformOriginY: [ "50%", "50%" ], scaleX: [ 1, 0.625 ], scaleY: [ 1, 0.625 ], translateZ: 0 } ] ] }, "transition.expandOut": { defaultDuration: 700, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], transformOriginX: [ "50%", "50%" ], transformOriginY: [ "50%", "50%" ], scaleX: 0.5, scaleY: 0.5, translateZ: 0 } ] ], reset: { scaleX: 1, scaleY: 1 } @@ -399,7 +444,7 @@ return function (global, window, document, undefined) { /* Animate.css */ "transition.bounceIn": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], scaleX: [ 1.05, 0.3 ], scaleY: [ 1.05, 0.3 ] }, 0.40 ], [ { scaleX: 0.9, scaleY: 0.9, translateZ: 0 }, 0.20 ], [ { scaleX: 1, scaleY: 1 }, 0.50 ] @@ -408,17 +453,17 @@ return function (global, window, document, undefined) { /* Animate.css */ "transition.bounceOut": { defaultDuration: 800, - calls: [ - [ { scaleX: 0.95, scaleY: 0.95 }, 0.40 ], - [ { scaleX: 1.1, scaleY: 1.1, translateZ: 0 }, 0.40 ], - [ { opacity: [ 0, 1 ], scaleX: 0.3, scaleY: 0.3 }, 0.20 ] + calls: [ + [ { scaleX: 0.95, scaleY: 0.95 }, 0.35 ], + [ { scaleX: 1.1, scaleY: 1.1, translateZ: 0 }, 0.35 ], + [ { opacity: [ 0, 1 ], scaleX: 0.3, scaleY: 0.3 }, 0.30 ] ], reset: { scaleX: 1, scaleY: 1 } }, /* Animate.css */ "transition.bounceUpIn": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateY: [ -30, 1000 ] }, 0.60, { easing: "easeOutCirc" } ], [ { translateY: 10 }, 0.20 ], [ { translateY: 0 }, 0.20 ] @@ -427,7 +472,7 @@ return function (global, window, document, undefined) { /* Animate.css */ "transition.bounceUpOut": { defaultDuration: 1000, - calls: [ + calls: [ [ { translateY: 20 }, 0.20 ], [ { opacity: [ 0, "easeInCirc", 1 ], translateY: -1000 }, 0.80 ] ], @@ -436,7 +481,7 @@ return function (global, window, document, undefined) { /* Animate.css */ "transition.bounceDownIn": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateY: [ 30, -1000 ] }, 0.60, { easing: "easeOutCirc" } ], [ { translateY: -10 }, 0.20 ], [ { translateY: 0 }, 0.20 ] @@ -445,7 +490,7 @@ return function (global, window, document, undefined) { /* Animate.css */ "transition.bounceDownOut": { defaultDuration: 1000, - calls: [ + calls: [ [ { translateY: -20 }, 0.20 ], [ { opacity: [ 0, "easeInCirc", 1 ], translateY: 1000 }, 0.80 ] ], @@ -454,7 +499,7 @@ return function (global, window, document, undefined) { /* Animate.css */ "transition.bounceLeftIn": { defaultDuration: 750, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateX: [ 30, -1250 ] }, 0.60, { easing: "easeOutCirc" } ], [ { translateX: -10 }, 0.20 ], [ { translateX: 0 }, 0.20 ] @@ -463,7 +508,7 @@ return function (global, window, document, undefined) { /* Animate.css */ "transition.bounceLeftOut": { defaultDuration: 750, - calls: [ + calls: [ [ { translateX: 30 }, 0.20 ], [ { opacity: [ 0, "easeInCirc", 1 ], translateX: -1250 }, 0.80 ] ], @@ -472,7 +517,7 @@ return function (global, window, document, undefined) { /* Animate.css */ "transition.bounceRightIn": { defaultDuration: 750, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateX: [ -30, 1250 ] }, 0.60, { easing: "easeOutCirc" } ], [ { translateX: 10 }, 0.20 ], [ { translateX: 0 }, 0.20 ] @@ -481,7 +526,7 @@ return function (global, window, document, undefined) { /* Animate.css */ "transition.bounceRightOut": { defaultDuration: 750, - calls: [ + calls: [ [ { translateX: -30 }, 0.20 ], [ { opacity: [ 0, "easeInCirc", 1 ], translateX: 1250 }, 0.80 ] ], @@ -489,104 +534,104 @@ return function (global, window, document, undefined) { }, "transition.slideUpIn": { defaultDuration: 900, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateY: [ 0, 20 ], translateZ: 0 } ] ] }, "transition.slideUpOut": { defaultDuration: 900, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], translateY: -20, translateZ: 0 } ] ], reset: { translateY: 0 } }, "transition.slideDownIn": { defaultDuration: 900, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateY: [ 0, -20 ], translateZ: 0 } ] ] }, "transition.slideDownOut": { defaultDuration: 900, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], translateY: 20, translateZ: 0 } ] ], reset: { translateY: 0 } }, "transition.slideLeftIn": { defaultDuration: 1000, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateX: [ 0, -20 ], translateZ: 0 } ] ] }, "transition.slideLeftOut": { defaultDuration: 1050, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], translateX: -20, translateZ: 0 } ] ], reset: { translateX: 0 } }, "transition.slideRightIn": { defaultDuration: 1000, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateX: [ 0, 20 ], translateZ: 0 } ] ] }, "transition.slideRightOut": { defaultDuration: 1050, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], translateX: 20, translateZ: 0 } ] ], reset: { translateX: 0 } }, "transition.slideUpBigIn": { defaultDuration: 850, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateY: [ 0, 75 ], translateZ: 0 } ] ] }, "transition.slideUpBigOut": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], translateY: -75, translateZ: 0 } ] ], reset: { translateY: 0 } }, "transition.slideDownBigIn": { defaultDuration: 850, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateY: [ 0, -75 ], translateZ: 0 } ] ] }, "transition.slideDownBigOut": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], translateY: 75, translateZ: 0 } ] ], reset: { translateY: 0 } }, "transition.slideLeftBigIn": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateX: [ 0, -75 ], translateZ: 0 } ] ] }, "transition.slideLeftBigOut": { defaultDuration: 750, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], translateX: -75, translateZ: 0 } ] ], reset: { translateX: 0 } }, "transition.slideRightBigIn": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], translateX: [ 0, 75 ], translateZ: 0 } ] ] }, "transition.slideRightBigOut": { defaultDuration: 750, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], translateX: 75, translateZ: 0 } ] ], reset: { translateX: 0 } @@ -594,16 +639,15 @@ return function (global, window, document, undefined) { /* Magic.css */ "transition.perspectiveUpIn": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], transformPerspective: [ 800, 800 ], transformOriginX: [ 0, 0 ], transformOriginY: [ "100%", "100%" ], rotateX: [ 0, -180 ] } ] - ], - reset: { transformPerspective: 0, transformOriginX: "50%", transformOriginY: "50%" } + ] }, /* Magic.css */ /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.perspectiveUpOut": { defaultDuration: 850, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], transformPerspective: [ 800, 800 ], transformOriginX: [ 0, 0 ], transformOriginY: [ "100%", "100%" ], rotateX: -180 } ] ], reset: { transformPerspective: 0, transformOriginX: "50%", transformOriginY: "50%", rotateX: 0 } @@ -612,7 +656,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.perspectiveDownIn": { defaultDuration: 800, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], transformPerspective: [ 800, 800 ], transformOriginX: [ 0, 0 ], transformOriginY: [ 0, 0 ], rotateX: [ 0, 180 ] } ] ], reset: { transformPerspective: 0, transformOriginX: "50%", transformOriginY: "50%" } @@ -621,7 +665,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.perspectiveDownOut": { defaultDuration: 850, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], transformPerspective: [ 800, 800 ], transformOriginX: [ 0, 0 ], transformOriginY: [ 0, 0 ], rotateX: 180 } ] ], reset: { transformPerspective: 0, transformOriginX: "50%", transformOriginY: "50%", rotateX: 0 } @@ -630,7 +674,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.perspectiveLeftIn": { defaultDuration: 950, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], transformPerspective: [ 2000, 2000 ], transformOriginX: [ 0, 0 ], transformOriginY: [ 0, 0 ], rotateY: [ 0, -180 ] } ] ], reset: { transformPerspective: 0, transformOriginX: "50%", transformOriginY: "50%" } @@ -639,7 +683,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.perspectiveLeftOut": { defaultDuration: 950, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], transformPerspective: [ 2000, 2000 ], transformOriginX: [ 0, 0 ], transformOriginY: [ 0, 0 ], rotateY: -180 } ] ], reset: { transformPerspective: 0, transformOriginX: "50%", transformOriginY: "50%", rotateY: 0 } @@ -648,7 +692,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.perspectiveRightIn": { defaultDuration: 950, - calls: [ + calls: [ [ { opacity: [ 1, 0 ], transformPerspective: [ 2000, 2000 ], transformOriginX: [ "100%", "100%" ], transformOriginY: [ 0, 0 ], rotateY: [ 0, 180 ] } ] ], reset: { transformPerspective: 0, transformOriginX: "50%", transformOriginY: "50%" } @@ -657,7 +701,7 @@ return function (global, window, document, undefined) { /* Support: Loses rotation in IE9/Android 2.3 (fades only). */ "transition.perspectiveRightOut": { defaultDuration: 950, - calls: [ + calls: [ [ { opacity: [ 0, 1 ], transformPerspective: [ 2000, 2000 ], transformOriginX: [ "100%", "100%" ], transformOriginY: [ 0, 0 ], rotateY: 180 } ] ], reset: { transformPerspective: 0, transformOriginX: "50%", transformOriginY: "50%", rotateY: 0 } @@ -665,8 +709,46 @@ return function (global, window, document, undefined) { }; /* Register the packaged effects. */ - for (var effectName in global.Velocity.RegisterUI.packagedEffects) { - global.Velocity.RegisterUI(effectName, global.Velocity.RegisterUI.packagedEffects[effectName]); + for (var effectName in Velocity.RegisterEffect.packagedEffects) { + Velocity.RegisterEffect(effectName, Velocity.RegisterEffect.packagedEffects[effectName]); } + + /********************* + Sequence Running + **********************/ + + /* Note: Sequence calls must use Velocity's single-object arguments syntax. */ + Velocity.RunSequence = function (originalSequence) { + var sequence = $.extend(true, [], originalSequence); + + if (sequence.length > 1) { + $.each(sequence.reverse(), function(i, currentCall) { + var nextCall = sequence[i + 1]; + + if (nextCall) { + /* Parallel sequence calls (indicated via sequenceQueue:false) are triggered + in the previous call's begin callback. Otherwise, chained calls are normally triggered + in the previous call's complete callback. */ + var timing = (currentCall.options && currentCall.options.sequenceQueue === false) ? "begin" : "complete", + callbackOriginal = nextCall.options && nextCall.options[timing], + options = {}; + + options[timing] = function() { + var nextCallElements = nextCall.elements || nextCall.e; + var elements = nextCallElements.nodeType ? [ nextCallElements ] : nextCallElements; + + callbackOriginal && callbackOriginal.call(elements, elements); + Velocity(currentCall); + } + + nextCall.options = $.extend({}, nextCall.options, options); + } + }); + + sequence.reverse(); + } + + Velocity(sequence[0]); + }; }((window.jQuery || window.Zepto || window), window, document); })); \ No newline at end of file diff --git a/app/assets/javascripts/app/lib/app_post/ajax.js.coffee b/app/assets/javascripts/app/lib/app_post/ajax.js.coffee index 337139af8..095bda739 100644 --- a/app/assets/javascripts/app/lib/app_post/ajax.js.coffee +++ b/app/assets/javascripts/app/lib/app_post/ajax.js.coffee @@ -62,16 +62,15 @@ class _ajaxSingleton # do not show aborded requests return if status is 0 - # do not show any error message on wrong login - return if status is 401 && !settings.url.match('login') - - # do not show any error message with code 200 + # 200, all is fine return if status is 200 - # show human readable message - if status is 401 - status = 'Access denied.' - detail = '' + # do not show any error message with code 401/404 (handled by controllers) + return if status is 401 + return if status is 404 + + # do not show any error message with code 502 + return if status is 502 # show error message new App.ErrorModal( diff --git a/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.js.coffee b/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.js.coffee index 2a2f0fcc5..a5c9a98b7 100644 --- a/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.js.coffee +++ b/app/assets/javascripts/app/lib/app_post/user_organization_autocompletion.js.coffee @@ -1,9 +1,9 @@ class App.UserOrganizationAutocompletion extends App.Controller className: 'dropdown js-recipientDropdown zIndex-2' events: - 'hide.bs.dropdown .js-recipientDropdown': 'hideOrganisationMembers' - 'click .js-organisation': 'showOrganisationMembers' - 'click .js-back': 'hideOrganisationMembers' + 'hide.bs.dropdown .js-recipientDropdown': 'hideOrganizationMembers' + 'click .js-organization': 'showOrganizationMembers' + 'click .js-back': 'hideOrganizationMembers' 'click .js-user': 'selectUser' 'click .js-user-new': 'newUser' 'focus input': 'open' @@ -148,9 +148,9 @@ class App.UserOrganizationAutocompletion extends App.Controller e.preventDefault() userId = @$('.recipientList').find('li.is-active').data('user-id') if !userId - organisationId = @$('.recipientList').find('li.is-active').data('organisation-id') - if organisationId - @showOrganisationMembers(undefined, @$('.recipientList').find('li.is-active')) + organizationId = @$('.recipientList').find('li.is-active').data('organization-id') + if organizationId + @showOrganizationMembers(undefined, @$('.recipientList').find('li.is-active')) return if userId is 'new' @newUser() @@ -217,24 +217,24 @@ class App.UserOrganizationAutocompletion extends App.Controller emptyResultList: => @$('.recipientList').empty() - @$('.recipientList-organisationMembers').remove() + @$('.recipientList-organizationMembers').remove() - showOrganisationMembers: (e,listEntry) => + showOrganizationMembers: (e,listEntry) => if e e.stopPropagation() listEntry = $(e.currentTarget) - organisationId = listEntry.data('organisation-id') + organizationId = listEntry.data('organization-id') @recipientList = @$('.recipientList') - @organisationList = @$("##{ organisationId }") + @organizationList = @$("##{ organizationId }") - # move organisation-list to the right and slide it in + # move organization-list to the right and slide it in - $.Velocity.hook(@organisationList, 'translateX', '100%') - @organisationList.removeClass('hide') + $.Velocity.hook(@organizationList, 'translateX', '100%') + @organizationList.removeClass('hide') - @organisationList.velocity + @organizationList.velocity properties: translateX: 0 options: @@ -246,12 +246,12 @@ class App.UserOrganizationAutocompletion extends App.Controller translateX: '-100%' options: speed: 300 - complete: => @recipientList.height(@organisationList.height()) + complete: => @recipientList.height(@organizationList.height()) - hideOrganisationMembers: (e) => + hideOrganizationMembers: (e) => e && e.stopPropagation() - return if !@organisationList + return if !@organizationList # fade list back in @recipientList.velocity @@ -264,13 +264,13 @@ class App.UserOrganizationAutocompletion extends App.Controller @recipientList.height('') - # slide out organisation-list and hide it - @organisationList.velocity + # slide out organization-list and hide it + @organizationList.velocity properties: translateX: '100%' options: speed: 300 - complete: => @organisationList.addClass('hide') + complete: => @organizationList.addClass('hide') newUser: (e) => if e diff --git a/app/assets/javascripts/app/lib/app_post/utils.js.coffee b/app/assets/javascripts/app/lib/app_post/utils.js.coffee index 5f576a74f..0ae38e317 100644 --- a/app/assets/javascripts/app/lib/app_post/utils.js.coffee +++ b/app/assets/javascripts/app/lib/app_post/utils.js.coffee @@ -114,7 +114,7 @@ class App.Utils @_removeAttributes( html ) # remove tags, keep content - html.find('a, font').replaceWith( -> + html.find('a, font, small, time').replaceWith( -> $(@).contents() ) @@ -166,3 +166,27 @@ class App.Utils else true + # textReplaced = App.Utils.replaceTags( template, { user: { firstname: 'Bob', lastname: 'Smith' } } ) + @replaceTags: (template, objects) -> + template = template.replace( /#\{\s{0,2}(.+?)\s{0,2}\}/g, ( index, key ) -> + levels = key.split(/\./) + dataRef = objects + for level in levels + if dataRef[level] + dataRef = dataRef[level] + if typeof dataRef is 'function' + value = dataRef() + else if typeof dataRef is 'string' + value = dataRef + else + value = '' + #console.log( "tag replacement #{key}, #{value} env: ", objects) + value + ) + + # true|false = App.Utils.lastLineEmpty( message ) + @lastLineEmpty: (message) -> + messageCleanup = message.replace(/>\s+<').replace(/(\n|\r|\t)/g, '').trim() + return true if messageCleanup.match(/<(br|\s+?|\/)>$/im) + return true if messageCleanup.match(/<\/div>$/im) + false diff --git a/app/assets/javascripts/app/lib/base/jquery.contenteditable.js b/app/assets/javascripts/app/lib/base/jquery.contenteditable.js index f47e1ac43..ee1d5ac8c 100644 --- a/app/assets/javascripts/app/lib/base/jquery.contenteditable.js +++ b/app/assets/javascripts/app/lib/base/jquery.contenteditable.js @@ -25,6 +25,7 @@ 40: true, // left 91: true, // cmd left 92: true, // cmd right + 224: true, // cmd left }, extraAllowKey: { 65: true, // a + ctrl - select all @@ -38,6 +39,7 @@ 73: true, // i 85: true, // u }, + //maxlength: 20, }; function Plugin( element, options ) { @@ -58,13 +60,9 @@ this.preventInput = false this.init(); - // bind - - // bind paste } - Plugin.prototype.init = function () { var _this = this @@ -84,98 +82,55 @@ e.preventDefault() return } - // limit check - if ( !_this.maxLengthOk( true ) ) { + } + + // limit check + if ( !_this.allowKey(e) ) { + if ( !_this.maxLengthOk( 1 ) ) { e.preventDefault() return } } }) - this.$element.on('keyup', function (e) { - console.log('KU', e.ctrlKey) - // do not remove tags on space, enter or backspace key, it's needed for FF - if ( _this.options.mode === 'textonly' ) { - if ( !_this.options.multiline ) { - - // do tricky this for FF - if ( e.keyCode !== 32 && e.keyCode !== 13 && e.keyCode !== 8 ) { - - // start request to remove tags - _this.htmlRemoveTags() - } - else { - - // clear request to delete tags, in FF we need
anytime at the end - _this.htmlRemoveTagsClearClearTimeout() - } - } - else { - App.Utils.htmlRemoveRichtext(_this.$element) - } - } - else { - App.Utils.htmlClanup(_this.$element) - } - }) - - // just paste text this.$element.on('paste', function (e) { console.log('paste') + + // check existing + paste text for limit + var text + if (window.clipboardData) { // IE + text = window.clipboardData.getData('Text') + } + else { + text = (e.originalEvent || e).clipboardData.getData('text/plain') + } + + if ( !_this.maxLengthOk( text.length) ) { + e.preventDefault() + return + } + + // use setTimeout() with 0 to execute it right after paste event if ( _this.options.mode === 'textonly' ) { if ( !_this.options.multiline ) { - _this.htmlRemoveTags() + setTimeout($.proxy(function(){ + App.Utils.htmlRemoveTags(this.$element) + }, _this), 0) } else { - App.Utils.htmlRemoveRichtext(_this.$element) + setTimeout($.proxy(function(){ + App.Utils.htmlRemoveRichtext(this.$element) + }, _this), 0) } } else { - App.Utils.htmlClanup(_this.$element) + setTimeout($.proxy(function(){ + App.Utils.htmlClanup(this.$element) + }, _this), 0) } + return true - if ( this.options.mode === 'textonly' ) { - e.preventDefault() - var text - if (window.clipboardData) { // IE - text = window.clipboardData.getData('Text') - } - else { - 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 - if (document.selection) { // IE - var range = document.selection.createRange() - range.pasteHTML(text) - } - else { - document.execCommand('inserttext', false, text) - } - _this.maxLengthOk( overlimit ) - } - } }) // disable rich text b/u/i @@ -188,23 +143,15 @@ } } - // check if rich text key is pressed - Plugin.prototype.htmlRemoveTags = function() { - - // clear old clear request - this.htmlRemoveTagsClearClearTimeout() - - // set new clear request - this._setTimeOutReformat = setTimeout($.proxy(function(){ - App.Utils.htmlRemoveTags(this.$element) - }, this), 100) - console.log('htmlRemoveTagsClearSetTimeout', this._setTimeOutReformat) - } - Plugin.prototype.htmlRemoveTagsClearClearTimeout = function() { - if (this._setTimeOutReformat) { - console.log('htmlRemoveTagsClearClearTimeout', this._setTimeOutReformat) - clearTimeout(this._setTimeOutReformat) + // check if key is allowed, even if length limit is reached + Plugin.prototype.allowKey = function(e) { + if ( this.options.allowKey[ e.keyCode ] ) { + return true } + if ( ( e.ctrlKey || e.metaKey ) && this.options.extraAllowKey[ e.keyCode ] ) { + return true + } + return false } // check if rich text key is pressed @@ -225,10 +172,14 @@ // max length check Plugin.prototype.maxLengthOk = function(typeAhead) { + if ( !this.options.maxlength ) { + return true + } var length = this.$element.text().length if (typeAhead) { - length = length + 1 + length = length + typeAhead } + console.log('maxLengthOk', length, this.options.maxlength) if ( length > this.options.maxlength ) { // try to set error on framework form diff --git a/app/assets/javascripts/app/lib/base/jquery.textmodule.js b/app/assets/javascripts/app/lib/base/jquery.textmodule.js index 989443f39..613b79271 100644 --- a/app/assets/javascripts/app/lib/base/jquery.textmodule.js +++ b/app/assets/javascripts/app/lib/base/jquery.textmodule.js @@ -22,8 +22,8 @@ this._name = pluginName this.collection = [] - this.active = false - this.buffer = '' + this.active = false + this.buffer = '' // check if ce exists if ( $.data(element, 'plugin_ce') ) { @@ -52,18 +52,22 @@ e.preventDefault() var id = _this.$widget.find('.dropdown-menu li.active a').data('id') _this.take(id) + return } - // arrow keys - if ( e.keyCode === 37 || e.keyCode === 38 || e.keyCode === 39 || e.keyCode === 40 ) { + // arrow keys left/right + if ( e.keyCode === 37 || e.keyCode === 39 ) { e.preventDefault() + return } // up if ( e.keyCode === 38 ) { + e.preventDefault() 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 ); + return } else { var prev = _this.$widget.find('.dropdown-menu li.active').removeClass('active').prev() @@ -72,15 +76,17 @@ top = prev.addClass('active').position().top } _this.$widget.find('.dropdown-menu').scrollTop( top ); + return } } // down if ( e.keyCode === 40 ) { + e.preventDefault() 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 ); - + return } else { var next = _this.$widget.find('.dropdown-menu li.active').removeClass('active').next() @@ -89,6 +95,7 @@ top = next.addClass('active').position().top } _this.$widget.find('.dropdown-menu').scrollTop( top ); + return } } @@ -100,9 +107,15 @@ // backspace if ( e.keyCode === 8 && _this.buffer ) { + + // backspace + buffer === :: -> close textmodule if ( _this.buffer === '::' ) { _this.close() + e.preventDefault() + return } + + // reduce buffer and show new result var length = _this.buffer.length _this.buffer = _this.buffer.substr( 0, length-1 ) console.log('BS backspace', _this.buffer) @@ -115,22 +128,26 @@ console.log('BUFF', _this.buffer, e.keyCode, String.fromCharCode(e.which) ) // shift - if ( e.keyCode === 16 ) { - return - } + if ( e.keyCode === 16 ) return // enter - if ( e.keyCode === 13 ) { - return - } + if ( e.keyCode === 13 ) return // arrow keys - if ( e.keyCode === 37 || e.keyCode === 38 || e.keyCode === 39 || e.keyCode === 40 ) { - return + if ( e.keyCode === 37 || e.keyCode === 38 || e.keyCode === 39 || e.keyCode === 40 ) return + + // observer other second key + if ( _this.buffer === ':' && String.fromCharCode(e.which) !== ':' ) { + _this.buffer = '' } - // enter : - if ( String.fromCharCode(e.which) === ':' ) { + // oberserve second : + if ( _this.buffer === ':' && String.fromCharCode(e.which) === ':' ) { + _this.buffer = _this.buffer + ':' + } + + // oberserve first : + if ( !_this.buffer && String.fromCharCode(e.which) === ':' ) { _this.buffer = _this.buffer + ':' } @@ -172,57 +189,38 @@ this.$widget = this.$element.next() } - // update widget position + // set height of widget + Plugin.prototype.movePosition = function() { + if (!this._position) return + var height = this.$element.height() + 20 + var widgetHeight = this.$widget.find('ul').height() //+ 60 // + height + var top = -( widgetHeight + height ) + this._position.top + this.$widget.css('top', top) + this.$widget.css('left', this._position.left) + } + + // set position of widget Plugin.prototype.updatePosition = function() { this.$widget.find('.dropdown-menu').scrollTop( 300 ); if ( !this.$element.is(':visible') ) return // get cursor position var marker = '' - - // IE9 and non-IE - sel = window.getSelection(); - if (sel.getRangeAt && sel.rangeCount) { - range = sel.getRangeAt(0); - range.deleteContents(); - - // Range.createContextualFragment() would be useful here but is - // only relatively recently standardized and is not supported in - // some browsers (IE9, for one) - var el = document.createElement("div"); - el.innerHTML = marker; - var frag = document.createDocumentFragment(), node, lastNode; - while ( (node = el.firstChild) ) { - lastNode = frag.appendChild(node); - } - range.insertNode(frag); - - // Preserve the selection - if (lastNode) { - range = range.cloneRange(); - range.setStartAfter(lastNode); - range.collapse(true); - sel.removeAllRanges(); - sel.addRange(range); - } - } - position = $('#js-cursor-position').position() + var range = this.getFirstRange(); + var clone = range.cloneRange() + clone.pasteHtml(marker) + this._position = $('#js-cursor-position').position() $('#js-cursor-position').remove() - if (!position) return + if (!this._position) return // set position of widget - var height = this.$element.height() - var widgetHeight = this.$widget.find('ul').height() //+ 60 // + height - var top = -( widgetHeight + height ) + position.top - this.$widget.css('top', top) - this.$widget.css('left', position.left) + this.movePosition() } // open widget Plugin.prototype.open = function() { this.active = true this.updatePosition() - b = $.proxy(function() { this.$widget.addClass('open') }, this) @@ -231,9 +229,11 @@ // close widget Plugin.prototype.close = function() { - this.active = false - this.cutInput() this.$widget.removeClass('open') + if ( this.active ) { + this.cutInput(true) + } + this.active = false } // check if widget is active/open @@ -255,11 +255,15 @@ // cut some content Plugin.prototype.cut = function(string) { + var range = this.getFirstRange(); + if (!range) return + /* var sel = window.getSelection() if ( !sel || sel.rangeCount < 1) { return } var range = sel.getRangeAt(0) + */ var clone = range.cloneRange() // improve error handling @@ -267,9 +271,34 @@ if (start < 0) { start = 0 } + + // for chrome, remove also leading space, add it later - otherwice space will be tropped + if (start) { + clone.setStart(range.startContainer, start-1) + clone.setEnd(range.startContainer, start) + var spacerChar = clone.toString() + if ( spacerChar === ' ' ) { + start = start - 1 + } + } + //console.log('CUT FOR', string, "-"+clone.toString()+"-", start, range.startOffset) clone.setStart(range.startContainer, start) clone.setEnd(range.startContainer, range.startOffset) clone.deleteContents() + + // for chrome, insert space again + if (start) { + if ( spacerChar === ' ' ) { + string = " " + if (document.selection) { // IE + var range = document.selection.createRange() + range.pasteHTML(string) + } + else { + document.execCommand('insertHTML', false, string) + } + } + } } // select text module and insert into text @@ -291,15 +320,15 @@ return } + Plugin.prototype.getFirstRange = function() { + var sel = rangy.getSelection(); + return sel.rangeCount ? sel.getRangeAt(0) : null; + } + // cut out search string from text Plugin.prototype.cutInput = function() { if (!this.buffer) return if (!this.$element.text()) return - var sel = window.getSelection() - if ( !sel || sel.rangeCount < 1) { - this.buffer = '' - return - } this.cut(this.buffer) this.buffer = '' } @@ -322,9 +351,9 @@ console.log('result', term, result) for (var i = 0; i < result.length; i++) { var item = result[i] - var template = "
  • " + item.name + var template = "
  • " + App.Utils.htmlEscape(item.name) if (item.keywords) { - template = template + " (" + item.keywords + ")" + template = template + " (" + App.Utils.htmlEscape(item.keywords) + ")" } template = template + "
  • " this.$widget.find('ul').append(template) @@ -340,10 +369,9 @@ _this.take(id) } ) - this.updatePosition() + this.movePosition() } - $.fn[pluginName] = function ( options ) { return this.each(function () { if (!$.data(this, 'plugin_' + pluginName)) { diff --git a/app/assets/javascripts/app/lib/base/rangy-textrange.js b/app/assets/javascripts/app/lib/base/rangy-textrange.js new file mode 100755 index 000000000..58d7528b5 --- /dev/null +++ b/app/assets/javascripts/app/lib/base/rangy-textrange.js @@ -0,0 +1,1925 @@ +/** + * Text range module for Rangy. + * Text-based manipulation and searching of ranges and selections. + * + * Features + * + * - Ability to move range boundaries by character or word offsets + * - Customizable word tokenizer + * - Ignores text nodes inside