Merge branch 'master' into node-6.9

This commit is contained in:
Shane Kilkelly 2017-02-24 11:27:32 +00:00
commit 34a1bd0e91
11 changed files with 234 additions and 97 deletions

View file

@ -93,7 +93,7 @@ header.toolbar.toolbar-header.toolbar-with-labels(
) )
i.review-icon i.review-icon
p.toolbar-label p.toolbar-label
| Review | #{translate("review")}
span(style="vertical-align: 20%; margin-left: 4px; padding: 2px 4px;").beta-feature-badge span(style="vertical-align: 20%; margin-left: 4px; padding: 2px 4px;").beta-feature-badge
a.btn.btn-full-height( a.btn.btn-full-height(
href, href,

View file

@ -4,9 +4,7 @@
ng-if="editor.wantTrackChanges" ng-if="editor.wantTrackChanges"
ng-click="toggleReviewPanel();" ng-click="toggleReviewPanel();"
ng-class="{ 'rp-track-changes-indicator-on-dark' : darkTheme }" ng-class="{ 'rp-track-changes-indicator-on-dark' : darkTheme }"
) Track changes is ) !{translate("track_changes_is_on")}
strong on
.review-panel-toolbar .review-panel-toolbar
resolved-comments-dropdown( resolved-comments-dropdown(
class="rp-flex-block" class="rp-flex-block"
@ -21,10 +19,8 @@
permissions="permissions" permissions="permissions"
) )
span.review-panel-toolbar-label(ng-if="permissions.write") span.review-panel-toolbar-label(ng-if="permissions.write")
span(ng-click="toggleTrackChanges(true)", ng-if="editor.wantTrackChanges === false") Track Changes is span(ng-click="toggleTrackChanges(true)", ng-if="editor.wantTrackChanges === false") !{translate("track_changes_is_off")}
strong off span(ng-click="toggleTrackChanges(false)", ng-if="editor.wantTrackChanges === true") !{translate("track_changes_is_on")}
span(ng-click="toggleTrackChanges(false)", ng-if="editor.wantTrackChanges === true") Track Changes is
strong on
review-panel-toggle( review-panel-toggle(
ng-if="editor.wantTrackChanges == editor.trackChanges" ng-if="editor.wantTrackChanges == editor.trackChanges"
ng-model="editor.wantTrackChanges" ng-model="editor.wantTrackChanges"
@ -33,10 +29,8 @@
on-disabled-click="openTrackChangesUpgradeModal" on-disabled-click="openTrackChangesUpgradeModal"
) )
span.review-panel-toolbar-label.review-panel-toolbar-label-disabled(ng-if="!permissions.write") span.review-panel-toolbar-label.review-panel-toolbar-label-disabled(ng-if="!permissions.write")
span(ng-if="editor.wantTrackChanges === false") Track Changes is span(ng-if="editor.wantTrackChanges === false") !{translate("track_changes_is_off")}
strong off span(ng-if="editor.wantTrackChanges === true") !{translate("track_changes_is_on")}
span(ng-if="editor.wantTrackChanges === true") Track Changes is
strong on
span.review-panel-toolbar-spinner(ng-if="editor.wantTrackChanges != editor.trackChanges") span.review-panel-toolbar-spinner(ng-if="editor.wantTrackChanges != editor.trackChanges")
i.fa.fa-spin.fa-spinner i.fa.fa-spin.fa-spinner
@ -47,6 +41,7 @@
.rp-entry-list-inner .rp-entry-list-inner
.rp-entry-wrapper( .rp-entry-wrapper(
ng-repeat="(entry_id, entry) in reviewPanel.entries[editor.open_doc_id]" ng-repeat="(entry_id, entry) in reviewPanel.entries[editor.open_doc_id]"
ng-if="entry.visible"
) )
div(ng-if="entry.type === 'insert' || entry.type === 'delete'") div(ng-if="entry.type === 'insert' || entry.type === 'delete'")
change-entry( change-entry(
@ -92,33 +87,45 @@
ng-if="!reviewPanel.overview.loading" ng-if="!reviewPanel.overview.loading"
) )
.rp-overview-file-header( .rp-overview-file-header(
ng-if="reviewPanel.entries[doc.doc.id] | notEmpty" ng-if="(reviewPanel.entries[doc.doc.id] != null) && (reviewPanel.entries[doc.doc.id] | notEmpty)"
ng-click="reviewPanel.overview.docsCollapsedState[doc.doc.id] = ! reviewPanel.overview.docsCollapsedState[doc.doc.id]"
) )
span.rp-overview-file-header-collapse(
ng-class="{ 'rp-overview-file-header-collapse-on': reviewPanel.overview.docsCollapsedState[doc.doc.id] }"
)
i.fa.fa-angle-down
| {{ doc.path }} | {{ doc.path }}
.rp-entry-wrapper( span.rp-overview-file-num-entries(
ng-repeat="(entry_id, entry) in reviewPanel.entries[doc.doc.id] | orderOverviewEntries" ng-show="reviewPanel.overview.docsCollapsedState[doc.doc.id]"
ng-if="!(entry.type === 'comment' && reviewPanel.commentThreads[entry.thread_id].resolved === true)" )  ({{ reviewPanel.entries[doc.doc.id] | numKeys }})
)
div(ng-if="entry.type === 'insert' || entry.type === 'delete'")
change-entry(
entry="entry"
user="users[entry.metadata.user_id]"
on-indicator-click="toggleReviewPanel();"
ng-click="gotoEntry(doc.doc.id, entry)"
permissions="permissions"
)
div(ng-if="entry.type === 'comment'") .rp-overview-file-entries(
comment-entry( review-panel-collapse-height="reviewPanel.overview.docsCollapsedState[doc.doc.id]"
entry="entry" )
threads="reviewPanel.commentThreads" .rp-entry-wrapper(
on-reply="submitReply(entry, entry_id);" ng-repeat="(entry_id, entry) in reviewPanel.entries[doc.doc.id] | orderOverviewEntries"
on-save-edit="saveEdit(entry.thread_id, comment)" ng-if="!(entry.type === 'comment' && reviewPanel.commentThreads[entry.thread_id].resolved === true)"
on-delete="deleteComment(entry.thread_id, comment)" )
on-indicator-click="toggleReviewPanel();" div(ng-if="entry.type === 'insert' || entry.type === 'delete'")
ng-click="gotoEntry(doc.doc.id, entry)" change-entry(
permissions="permissions" entry="entry"
) user="users[entry.metadata.user_id]"
on-indicator-click="toggleReviewPanel();"
ng-click="gotoEntry(doc.doc.id, entry)"
permissions="permissions"
)
div(ng-if="entry.type === 'comment'")
comment-entry(
entry="entry"
threads="reviewPanel.commentThreads"
on-reply="submitReply(entry, entry_id);"
on-save-edit="saveEdit(entry.thread_id, comment)"
on-delete="deleteComment(entry.thread_id, comment)"
on-indicator-click="toggleReviewPanel();"
ng-click="gotoEntry(doc.doc.id, entry)"
permissions="permissions"
)
.rp-nav .rp-nav
a.rp-nav-item( a.rp-nav-item(
@ -127,14 +134,14 @@
ng-class="{ 'rp-nav-item-active' : reviewPanel.subView === SubViews.CUR_FILE }" ng-class="{ 'rp-nav-item-active' : reviewPanel.subView === SubViews.CUR_FILE }"
) )
i.fa.fa-file-text-o i.fa.fa-file-text-o
span.rp-nav-label Current file span.rp-nav-label #{translate("current_file")}
a.rp-nav-item( a.rp-nav-item(
href href
ng-click="setSubView(SubViews.OVERVIEW);" ng-click="setSubView(SubViews.OVERVIEW);"
ng-class="{ 'rp-nav-item-active' : reviewPanel.subView === SubViews.OVERVIEW }" ng-class="{ 'rp-nav-item-active' : reviewPanel.subView === SubViews.OVERVIEW }"
) )
i.fa.fa-list i.fa.fa-list
span.rp-nav-label Overview span.rp-nav-label #{translate("overview")}
script(type='text/ng-template', id='changeEntryTemplate') script(type='text/ng-template', id='changeEntryTemplate')
@ -158,30 +165,30 @@ script(type='text/ng-template', id='changeEntryTemplate')
i.rp-icon-delete(ng-switch-when="delete") i.rp-icon-delete(ng-switch-when="delete")
.rp-entry-details .rp-entry-details
.rp-entry-description(ng-switch="entry.type") .rp-entry-description(ng-switch="entry.type")
span(ng-switch-when="insert") Added  span(ng-switch-when="insert") #{translate("tracked_change_added")} 
ins.rp-content-highlight {{ entry.content | limitTo:(isCollapsed ? contentLimit : entry.content.length) }} ins.rp-content-highlight {{ entry.content | limitTo:(isCollapsed ? contentLimit : entry.content.length) }}
a.rp-collapse-toggle( a.rp-collapse-toggle(
href href
ng-if="needsCollapsing" ng-if="needsCollapsing"
ng-click="toggleCollapse();" ng-click="toggleCollapse();"
) {{ isCollapsed ? '... (show all)' : ' (show less)' }} ) {{ isCollapsed ? '... (#{translate("show_all")})' : ' (#{translate("show_less")})' }}
span(ng-switch-when="delete") Deleted  span(ng-switch-when="delete") #{translate("tracked_change_deleted")} 
del.rp-content-highlight {{ entry.content | limitTo:(isCollapsed ? contentLimit : entry.content.length) }} del.rp-content-highlight {{ entry.content | limitTo:(isCollapsed ? contentLimit : entry.content.length) }}
a.rp-collapse-toggle( a.rp-collapse-toggle(
href href
ng-if="needsCollapsing" ng-if="needsCollapsing"
ng-click="toggleCollapse();" ng-click="toggleCollapse();"
) {{ isCollapsed ? '... (show all)' : ' (show less)' }} ) {{ isCollapsed ? '... (#{translate("show_all")})' : ' (#{translate("show_less")})' }}
.rp-entry-metadata .rp-entry-metadata
| {{ entry.metadata.ts | date : 'MMM d, y h:mm a' }} •  | {{ entry.metadata.ts | date : 'MMM d, y h:mm a' }} • 
span.rp-entry-user(style="color: hsl({{ user.hue }}, 70%, 40%);") {{ user.name }} span.rp-entry-user(style="color: hsl({{ user.hue }}, 70%, 40%);") {{ user.name }}
.rp-entry-actions(ng-if="permissions.write") .rp-entry-actions(ng-if="permissions.write")
a.rp-entry-button(href, ng-click="onReject();") a.rp-entry-button(href, ng-click="onReject();")
i.fa.fa-times i.fa.fa-times
|  Reject |  #{translate("reject")}
a.rp-entry-button(href, ng-click="onAccept();") a.rp-entry-button(href, ng-click="onAccept();")
i.fa.fa-check i.fa.fa-check
|  Accept |  #{translate("accept")}
script(type='text/ng-template', id='commentEntryTemplate') script(type='text/ng-template', id='commentEntryTemplate')
.rp-comment-wrapper( .rp-comment-wrapper(
@ -198,7 +205,7 @@ script(type='text/ng-template', id='commentEntryTemplate')
) )
.rp-loading(ng-if="!threads[entry.thread_id].submitting && (!threads[entry.thread_id] || threads[entry.thread_id].messages.length == 0)") .rp-loading(ng-if="!threads[entry.thread_id].submitting && (!threads[entry.thread_id] || threads[entry.thread_id].messages.length == 0)")
| No comments | #{translate("no_comments")}
.rp-comment-loaded .rp-comment-loaded
.rp-comment( .rp-comment(
ng-repeat="comment in threads[entry.thread_id].messages track by comment.id" ng-repeat="comment in threads[entry.thread_id].messages track by comment.id"
@ -222,16 +229,16 @@ script(type='text/ng-template', id='commentEntryTemplate')
span(ng-if="!comment.deleting") {{ comment.timestamp | date : 'MMM d, y h:mm a' }} span(ng-if="!comment.deleting") {{ comment.timestamp | date : 'MMM d, y h:mm a' }}
span.rp-comment-actions(ng-if="comment.user.isSelf && !comment.deleting") span.rp-comment-actions(ng-if="comment.user.isSelf && !comment.deleting")
|  •  |  • 
a(href, ng-click="startEditing(comment)") Edit a(href, ng-click="startEditing(comment)") #{translate("edit")}
span(ng-if="threads[entry.thread_id].messages.length > 1") span(ng-if="threads[entry.thread_id].messages.length > 1")
|  •  |  • 
a(href, ng-click="confirmDelete(comment)") Delete a(href, ng-click="confirmDelete(comment)") #{translate("delete")}
span.rp-confim-delete(ng-if="comment.user.isSelf && comment.deleting") span.rp-confim-delete(ng-if="comment.user.isSelf && comment.deleting")
| Are you sure? | #{translate("are_you_sure")}
| •  | • 
a(href, ng-click="doDelete(comment)") Delete a(href, ng-click="doDelete(comment)") #{translate("delete")}
|  •  |  • 
a(href, ng-click="cancelDelete(comment)") Cancel a(href, ng-click="cancelDelete(comment)") #{translate("cancel")}
.rp-loading(ng-if="threads[entry.thread_id].submitting") .rp-loading(ng-if="threads[entry.thread_id].submitting")
i.fa.fa-spinner.fa-spin i.fa.fa-spinner.fa-spin
@ -241,7 +248,7 @@ script(type='text/ng-template', id='commentEntryTemplate')
ng-model="entry.replyContent" ng-model="entry.replyContent"
ng-keypress="handleCommentReplyKeyPress($event);" ng-keypress="handleCommentReplyKeyPress($event);"
stop-propagation="click" stop-propagation="click"
placeholder="{{ 'Hit \"Enter\" to reply' + (entry.resolved ? ' and re-open' : '') }}" placeholder=translate("hit_enter_to_reply")
) )
.rp-entry-actions .rp-entry-actions
button.rp-entry-button( button.rp-entry-button(
@ -249,20 +256,20 @@ script(type='text/ng-template', id='commentEntryTemplate')
ng-if="permissions.comment && permissions.write" ng-if="permissions.comment && permissions.write"
) )
i.fa.fa-inbox i.fa.fa-inbox
|  Resolve |  #{translate("resolve")}
button.rp-entry-button( button.rp-entry-button(
ng-click="onReply();" ng-click="onReply();"
ng-if="permissions.comment" ng-if="permissions.comment"
ng-disabled="!entry.replyContent.length" ng-disabled="!entry.replyContent.length"
) )
i.fa.fa-reply i.fa.fa-reply
|  Reply |  #{translate("reply")}
script(type='text/ng-template', id='resolvedCommentEntryTemplate') script(type='text/ng-template', id='resolvedCommentEntryTemplate')
.rp-resolved-comment .rp-resolved-comment
div div
.rp-resolved-comment-context .rp-resolved-comment-context
| Quoted text on  | #{translate("quoted_text_in")} 
span.rp-resolved-comment-context-file {{ thread.docName }} span.rp-resolved-comment-context-file {{ thread.docName }}
p.rp-resolved-comment-context-quote p.rp-resolved-comment-context-quote
span {{ thread.content | limitTo:(isCollapsed ? contentLimit : thread.content.length)}} span {{ thread.content | limitTo:(isCollapsed ? contentLimit : thread.content.length)}}
@ -270,7 +277,7 @@ script(type='text/ng-template', id='resolvedCommentEntryTemplate')
href href
ng-if="needsCollapsing" ng-if="needsCollapsing"
ng-click="toggleCollapse();" ng-click="toggleCollapse();"
)  {{ isCollapsed ? '... (show all)' : ' (show less)' }} )  {{ isCollapsed ? '... (#{translate("show_all")})' : ' (#{translate("show_less")})' }}
.rp-comment( .rp-comment(
ng-repeat="comment in thread.messages track by comment.id" ng-repeat="comment in thread.messages track by comment.id"
) )
@ -287,7 +294,7 @@ script(type='text/ng-template', id='resolvedCommentEntryTemplate')
span.rp-entry-user( span.rp-entry-user(
style="color: hsl({{ thread.resolved_by_user.hue }}, 70%, 40%);" style="color: hsl({{ thread.resolved_by_user.hue }}, 70%, 40%);"
) {{ thread.resolved_by_user.name }}:  ) {{ thread.resolved_by_user.name }}: 
| Marked as resolved. | #{translate("mark_as_resolved")}.
.rp-entry-metadata .rp-entry-metadata
| {{ thread.resolved_at | date : 'MMM d, y h:mm a' }} | {{ thread.resolved_at | date : 'MMM d, y h:mm a' }}
@ -296,12 +303,12 @@ script(type='text/ng-template', id='resolvedCommentEntryTemplate')
href href
ng-click="onUnresolve({ 'threadId': thread.threadId });" ng-click="onUnresolve({ 'threadId': thread.threadId });"
) )
|  Re-open |  #{translate("reopen")}
a.rp-entry-button( a.rp-entry-button(
href href
ng-click="onDelete({ 'entryId': thread.entryId, 'docId': thread.docId, 'threadId': thread.threadId });" ng-click="onDelete({ 'entryId': thread.entryId, 'docId': thread.docId, 'threadId': thread.threadId });"
) )
|  Delete |  #{translate("delete")}
script(type='text/ng-template', id='addCommentEntryTemplate') script(type='text/ng-template', id='addCommentEntryTemplate')
@ -310,7 +317,7 @@ script(type='text/ng-template', id='addCommentEntryTemplate')
.rp-entry-indicator( .rp-entry-indicator(
ng-if="!commentState.adding" ng-if="!commentState.adding"
ng-click="startNewComment(); onIndicatorClick();" ng-click="startNewComment(); onIndicatorClick();"
tooltip="Add a comment" tooltip=translate("add_comment")
tooltip-placement="{{ layoutToLeft ? 'left' : 'right' }}" tooltip-placement="{{ layoutToLeft ? 'left' : 'right' }}"
tooltip-append-to-body="true" tooltip-append-to-body="true"
) )
@ -324,14 +331,14 @@ script(type='text/ng-template', id='addCommentEntryTemplate')
ng-click="startNewComment();" ng-click="startNewComment();"
) )
i.fa.fa-comment i.fa.fa-comment
|  Add comment |  #{translate("add_comment")}
div(ng-if="state.isAdding") div(ng-if="state.isAdding")
.rp-new-comment .rp-new-comment
textarea.rp-comment-input( textarea.rp-comment-input(
expandable-text-area expandable-text-area
ng-model="state.content" ng-model="state.content"
ng-keypress="handleCommentKeyPress($event);" ng-keypress="handleCommentKeyPress($event);"
placeholder="Add your comment here" placeholder=translate("add_your_comment_here")
focus-on="comment:new:open" focus-on="comment:new:open"
ng-blur="submitNewComment()" ng-blur="submitNewComment()"
) )
@ -340,13 +347,13 @@ script(type='text/ng-template', id='addCommentEntryTemplate')
ng-click="cancelNewComment();" ng-click="cancelNewComment();"
) )
i.fa.fa-times i.fa.fa-times
|  Cancel |  #{translate("cancel")}
button.rp-entry-button( button.rp-entry-button(
ng-click="submitNewComment()" ng-click="submitNewComment()"
ng-disabled="!state.content.length" ng-disabled="!state.content.length"
) )
i.fa.fa-comment i.fa.fa-comment
|  Comment |  #{translate("comment")}
script(type='text/ng-template', id='resolvedCommentsDropdownTemplate') script(type='text/ng-template', id='resolvedCommentsDropdownTemplate')
.resolved-comments .resolved-comments
@ -357,7 +364,7 @@ script(type='text/ng-template', id='resolvedCommentsDropdownTemplate')
a.resolved-comments-toggle( a.resolved-comments-toggle(
href href
ng-click="toggleOpenState();" ng-click="toggleOpenState();"
tooltip="Resolved Comments" tooltip=translate("resolved_comments")
tooltip-placement="bottom" tooltip-placement="bottom"
tooltip-append-to-body="true" tooltip-append-to-body="true"
) )
@ -378,7 +385,7 @@ script(type='text/ng-template', id='resolvedCommentsDropdownTemplate')
permissions="permissions" permissions="permissions"
) )
.rp-loading(ng-if="!resolvedComments.length") .rp-loading(ng-if="!resolvedComments.length")
| No resolved threads. | #{translate("no_resolved_threads")}.
script(type="text/ng-template", id="trackChangesUpgradeModalTemplate") script(type="text/ng-template", id="trackChangesUpgradeModalTemplate")
.modal-header .modal-header
@ -387,14 +394,14 @@ script(type="text/ng-template", id="trackChangesUpgradeModalTemplate")
data-dismiss="modal" data-dismiss="modal"
ng-click="cancel()" ng-click="cancel()"
) × ) ×
h3 Upgrade to Track Changes h3 #{translate("upgrade_to_track_changes")}
.modal-body .modal-body
.teaser-video-container .teaser-video-container
video.teaser-video(autoplay, loop) video.teaser-video(autoplay, loop)
source(src="/img/teasers/track-changes/teaser-track-changes.mp4", type="video/mp4") source(src="/img/teasers/track-changes/teaser-track-changes.mp4", type="video/mp4")
img(src="/img/teasers/track-changes/teaser-track-changes.gif") img(src="/img/teasers/track-changes/teaser-track-changes.gif")
h4.teaser-title See changes in your documents, live h4.teaser-title #{translate("see_changes_in_your_documents_live")}
p.small(ng-show="startedFreeTrial") p.small(ng-show="startedFreeTrial")
| #{translate("refresh_page_after_starting_free_trial")} | #{translate("refresh_page_after_starting_free_trial")}
@ -404,15 +411,15 @@ script(type="text/ng-template", id="trackChangesUpgradeModalTemplate")
ul.list-unstyled ul.list-unstyled
li li
i.fa.fa-check   i.fa.fa-check  
| Track any change, in real-time | #{translate("track_any_change_in_real_time")}
li li
i.fa.fa-check   i.fa.fa-check  
| Review your peers' work | #{translate("review_your_peers_work")}
li li
i.fa.fa-check   i.fa.fa-check  
| Accept or reject each change individually | #{translate("accept_or_reject_each_changes_individually")}
.row.text-center(ng-controller="FreeTrialModalController") .row.text-center(ng-controller="FreeTrialModalController")
@ -420,8 +427,8 @@ script(type="text/ng-template", id="trackChangesUpgradeModalTemplate")
href href
ng-click="startFreeTrial('real-time-track-changes')" ng-click="startFreeTrial('real-time-track-changes')"
ng-show="project.owner._id == user.id" ng-show="project.owner._id == user.id"
) Try it for free ) #{translate("try_it_for_free")}
p(ng-show="project.owner._id != user.id"): strong Please ask the project owner to upgrade to use track changes p(ng-show="project.owner._id != user.id"): strong #{translate("please_ask_the_project_owner_to_upgrade_to_track_changes")}
.modal-footer() .modal-footer()
button.btn.btn-default( button.btn.btn-default(

View file

@ -184,6 +184,28 @@ define [
session = editor.getSession() session = editor.getSession()
session.setScrollTop(session.getScrollTop() + newScreenPosition - previousScreenPosition) session.setScrollTop(session.getScrollTop() + newScreenPosition - previousScreenPosition)
scope.$on "#{scope.name}:set-scroll-size", (e, size) ->
# Make sure that the editor has enough scroll margin above and below
# to scroll the review panel with the given size
marginTop = size.overflowTop
maxHeight = editor.renderer.layerConfig.maxHeight
marginBottom = Math.max(size.height - maxHeight, 0)
setScrollMargins(marginTop, marginBottom)
setScrollMargins = (marginTop, marginBottom) ->
marginChanged = false
if editor.renderer.scrollMargin.top != marginTop
editor.renderer.scrollMargin.top = marginTop
marginChanged = true
if editor.renderer.scrollMargin.bottom != marginBottom
editor.renderer.scrollMargin.bottom = marginBottom
marginChanged = true
if marginChanged
editor.renderer.updateFull()
resetScrollMargins = () ->
setScrollMargins(0,0)
scope.$watch "theme", (value) -> scope.$watch "theme", (value) ->
editor.setTheme("ace/theme/#{value}") editor.setTheme("ace/theme/#{value}")
@ -308,6 +330,8 @@ define [
sharejs_doc.attachToAce(editor) sharejs_doc.attachToAce(editor)
editor.initing = false editor.initing = false
resetScrollMargins()
# need to set annotations after attaching because attaching # need to set annotations after attaching because attaching
# deletes and then inserts document content # deletes and then inserts document content
session.setAnnotations scope.annotations session.setAnnotations scope.annotations

View file

@ -60,6 +60,18 @@ define [
onChangeSession = (e) => onChangeSession = (e) =>
@clearAnnotations() @clearAnnotations()
@redrawAnnotations() @redrawAnnotations()
@editor.session.on "changeScrollTop", onChangeScroll
_scrollTimeout = null
onChangeScroll = () =>
if _scrollTimeout?
return
else
_scrollTimeout = setTimeout () =>
@recalculateVisibleEntries()
@$scope.$apply()
_scrollTimeout = null
, 200
bindToAce = () => bindToAce = () =>
@editor.on "changeSelection", onChangeSelection @editor.on "changeSelection", onChangeSelection
@ -282,6 +294,7 @@ define [
recalculateReviewEntriesScreenPositions: () -> recalculateReviewEntriesScreenPositions: () ->
session = @editor.getSession() session = @editor.getSession()
renderer = @editor.renderer renderer = @editor.renderer
{firstRow, lastRow} = renderer.layerConfig
entries = @_getCurrentDocEntries() entries = @_getCurrentDocEntries()
for entry_id, entry of entries or {} for entry_id, entry of entries or {}
doc_position = @_shareJsOffsetToAcePosition(entry.offset) doc_position = @_shareJsOffsetToAcePosition(entry.offset)
@ -290,9 +303,24 @@ define [
entry.screenPos ?= {} entry.screenPos ?= {}
entry.screenPos.y = y entry.screenPos.y = y
entry.docPos = doc_position entry.docPos = doc_position
@recalculateVisibleEntries()
@$scope.$apply() @$scope.$apply()
recalculateVisibleEntries: () ->
OFFSCREEN_ROWS = 20
CULL_AFTER = 100 # With less than this number of entries, don't bother culling to avoid little UI jumps when scrolling.
{firstRow, lastRow} = @editor.renderer.layerConfig
entries = @_getCurrentDocEntries() or {}
entriesLength = Object.keys(entries).length
changed = false
for entry_id, entry of entries
old = entry.visible
entry.visible = (entriesLength < CULL_AFTER) or (firstRow - OFFSCREEN_ROWS <= entry.docPos.row <= lastRow + OFFSCREEN_ROWS)
if (entry.visible != old)
changed = true
if changed
@$scope.$emit "editor:track-changes:visibility_changed"
_getCurrentDocEntries: () -> _getCurrentDocEntries: () ->
doc_id = @$scope.docId doc_id = @$scope.docId
entries = @$scope.reviewPanel.entries[doc_id] ?= {} entries = @$scope.reviewPanel.entries[doc_id] ?= {}

View file

@ -8,6 +8,8 @@ define [
"ide/review-panel/directives/addCommentEntry" "ide/review-panel/directives/addCommentEntry"
"ide/review-panel/directives/resolvedCommentEntry" "ide/review-panel/directives/resolvedCommentEntry"
"ide/review-panel/directives/resolvedCommentsDropdown" "ide/review-panel/directives/resolvedCommentsDropdown"
"ide/review-panel/directives/reviewPanelCollapseHeight"
"ide/review-panel/filters/notEmpty" "ide/review-panel/filters/notEmpty"
"ide/review-panel/filters/numKeys"
"ide/review-panel/filters/orderOverviewEntries" "ide/review-panel/filters/orderOverviewEntries"
], () -> ], () ->

View file

@ -4,7 +4,7 @@ define [
"ide/colors/ColorManager" "ide/colors/ColorManager"
"ide/review-panel/RangesTracker" "ide/review-panel/RangesTracker"
], (App, EventEmitter, ColorManager, RangesTracker) -> ], (App, EventEmitter, ColorManager, RangesTracker) ->
App.controller "ReviewPanelController", ($scope, $element, ide, $timeout, $http, $modal, event_tracking) -> App.controller "ReviewPanelController", ($scope, $element, ide, $timeout, $http, $modal, event_tracking, localStorage) ->
$reviewPanelEl = $element.find "#review-panel" $reviewPanelEl = $element.find "#review-panel"
$scope.SubViews = $scope.SubViews =
@ -19,6 +19,7 @@ define [
openSubView: $scope.SubViews.CUR_FILE openSubView: $scope.SubViews.CUR_FILE
overview: overview:
loading: false loading: false
docsCollapsedState: JSON.parse(localStorage("docs_collapsed_state:#{$scope.project_id}")) or {}
dropdown: dropdown:
loading: false loading: false
commentThreads: {} commentThreads: {}
@ -27,6 +28,14 @@ define [
rendererData: {} rendererData: {}
loadingThreads: false loadingThreads: false
window.addEventListener "beforeunload", () ->
collapsedStates = {}
for doc, state of $scope.reviewPanel.overview.docsCollapsedState
if state
collapsedStates[doc] = state
valToStore = if Object.keys(collapsedStates).length > 0 then JSON.stringify(collapsedStates) else null
localStorage("docs_collapsed_state:#{$scope.project_id}", valToStore)
$scope.$on "layout:pdf:linked", (event, state) -> $scope.$on "layout:pdf:linked", (event, state) ->
$scope.reviewPanel.layoutToLeft = (state.east?.size < 220 || state.east?.initClosed) $scope.reviewPanel.layoutToLeft = (state.east?.size < 220 || state.east?.initClosed)
$scope.$broadcast "review-panel:layout" $scope.$broadcast "review-panel:layout"
@ -39,6 +48,9 @@ define [
$timeout () -> $timeout () ->
$scope.$broadcast "review-panel:layout" $scope.$broadcast "review-panel:layout"
$scope.$on "review-panel:sizes", (e, sizes) ->
$scope.$broadcast "editor:set-scroll-size", sizes
$scope.$watch "ui.pdfLayout", (layout) -> $scope.$watch "ui.pdfLayout", (layout) ->
$scope.reviewPanel.layoutToLeft = (layout == "flat") $scope.reviewPanel.layoutToLeft = (layout == "flat")
@ -171,6 +183,8 @@ define [
$http.get "/project/#{$scope.project_id}/ranges" $http.get "/project/#{$scope.project_id}/ranges"
.success (docs) -> .success (docs) ->
for doc in docs for doc in docs
if !$scope.reviewPanel.overview.docsCollapsedState[doc.id]?
$scope.reviewPanel.overview.docsCollapsedState[doc.id] = false
if doc.id != $scope.editor.open_doc_id # this is kept up to date in real-time, don't overwrite if doc.id != $scope.editor.open_doc_id # this is kept up to date in real-time, don't overwrite
rangesTracker = getChangeTracker(doc.id) rangesTracker = getChangeTracker(doc.id)
rangesTracker.comments = doc.ranges?.comments or [] rangesTracker.comments = doc.ranges?.comments or []
@ -264,6 +278,10 @@ define [
$scope.$broadcast "review-panel:recalculate-screen-positions" $scope.$broadcast "review-panel:recalculate-screen-positions"
$scope.$broadcast "review-panel:layout" $scope.$broadcast "review-panel:layout"
$scope.$on "editor:track-changes:visibility_changed", () ->
$timeout () ->
$scope.$broadcast "review-panel:layout", false
$scope.$on "editor:focus:changed", (e, selection_offset_start, selection_offset_end, selection) -> $scope.$on "editor:focus:changed", (e, selection_offset_start, selection_offset_end, selection) ->
doc_id = $scope.editor.open_doc_id doc_id = $scope.editor.open_doc_id
entries = getDocEntries(doc_id) entries = getDocEntries(doc_id)

View file

@ -0,0 +1,18 @@
define [
"base"
], (App) ->
App.directive "reviewPanelCollapseHeight", ($parse) ->
return {
restrict: "A",
link: (scope, element, attrs) ->
scope.$watch (() -> $parse(attrs.reviewPanelCollapseHeight)(scope)), (shouldCollapse) ->
neededHeight = element.prop("scrollHeight")
if neededHeight > 0
if shouldCollapse
element.animate { height: 0 }, 150
else
element.animate { height: neededHeight }, 150
else
if shouldCollapse
element.height 0
}

View file

@ -15,9 +15,11 @@ define [
if scope.ui.reviewPanelOpen if scope.ui.reviewPanelOpen
PADDING = 8 PADDING = 8
TOOLBAR_HEIGHT = 38 TOOLBAR_HEIGHT = 38
OVERVIEW_TOGGLE_HEIGHT = 57
else else
PADDING = 4 PADDING = 4
TOOLBAR_HEIGHT = 4 TOOLBAR_HEIGHT = 4
OVERVIEW_TOGGLE_HEIGHT = 0
entries = [] entries = []
for el in element.find(".rp-entry-wrapper") for el in element.find(".rp-entry-wrapper")
@ -31,6 +33,7 @@ define [
entry.$layout_el = entry.$box_el entry.$layout_el = entry.$box_el
else else
entry.$layout_el = entry.$indicator_el entry.$layout_el = entry.$indicator_el
entry.height = entry.$layout_el.height() # Do all of our DOM reads first for perfomance, see http://wilsonpage.co.uk/preventing-layout-thrashing/
entries.push entry entries.push entry
entries.sort (a,b) -> a.scope.entry.offset - b.scope.entry.offset entries.sort (a,b) -> a.scope.entry.offset - b.scope.entry.offset
@ -50,19 +53,6 @@ define [
sl_console.log "focused_entry_index", focused_entry_index sl_console.log "focused_entry_index", focused_entry_index
# As we go backwards, we run the risk of pushing things off the top of the editor.
# If we go through the entries before and assume they are as pushed together as they
# could be, we can work out the 'ceiling' that each one can't go through. I.e. the first
# on can't go beyond the toolbar height, the next one can't go beyond the bottom of the first
# one at this minimum height, etc.
heights = (entry.$layout_el.height() for entry in entries_before)
previousMinTop = TOOLBAR_HEIGHT
min_tops = []
for height in heights
min_tops.push previousMinTop
previousMinTop += PADDING + height
min_tops.reverse()
positionLayoutEl = ($callout_el, original_top, top) -> positionLayoutEl = ($callout_el, original_top, top) ->
if original_top <= top if original_top <= top
$callout_el.removeClass("rp-entry-callout-inverted") $callout_el.removeClass("rp-entry-callout-inverted")
@ -72,7 +62,7 @@ define [
$callout_el.css(top: top + line_height, height: original_top - top) $callout_el.css(top: top + line_height, height: original_top - top)
# Put the focused entry as close to where it wants to be as possible # Put the focused entry as close to where it wants to be as possible
focused_entry_top = Math.max(previousMinTop, focused_entry.scope.entry.screenPos.y) focused_entry_top = Math.max(focused_entry.scope.entry.screenPos.y, TOOLBAR_HEIGHT)
focused_entry.$box_el.css(top: focused_entry_top) focused_entry.$box_el.css(top: focused_entry_top)
focused_entry.$indicator_el.css(top: focused_entry_top) focused_entry.$indicator_el.css(top: focused_entry_top)
positionLayoutEl(focused_entry.$callout_el, focused_entry.scope.entry.screenPos.y, focused_entry_top) positionLayoutEl(focused_entry.$callout_el, focused_entry.scope.entry.screenPos.y, focused_entry_top)
@ -80,7 +70,7 @@ define [
previousBottom = focused_entry_top + focused_entry.$layout_el.height() previousBottom = focused_entry_top + focused_entry.$layout_el.height()
for entry in entries_after for entry in entries_after
original_top = entry.scope.entry.screenPos.y original_top = entry.scope.entry.screenPos.y
height = entry.$layout_el.height() height = entry.height
top = Math.max(original_top, previousBottom + PADDING) top = Math.max(original_top, previousBottom + PADDING)
previousBottom = top + height previousBottom = top + height
entry.$box_el.css(top: top) entry.$box_el.css(top: top)
@ -88,20 +78,32 @@ define [
positionLayoutEl(entry.$callout_el, original_top, top) positionLayoutEl(entry.$callout_el, original_top, top)
sl_console.log "ENTRY", {entry: entry.scope.entry, top} sl_console.log "ENTRY", {entry: entry.scope.entry, top}
lastBottom = previousBottom
previousTop = focused_entry_top previousTop = focused_entry_top
entries_before.reverse() # Work through backwards, starting with the one just above entries_before.reverse() # Work through backwards, starting with the one just above
for entry, i in entries_before for entry, i in entries_before
original_top = entry.scope.entry.screenPos.y original_top = entry.scope.entry.screenPos.y
height = entry.$layout_el.height() height = entry.height
original_bottom = original_top + height original_bottom = original_top + height
bottom = Math.min(original_bottom, previousTop - PADDING) bottom = Math.min(original_bottom, previousTop - PADDING)
top = Math.max(bottom - height, min_tops[i]) top = bottom - height
previousTop = top previousTop = top
entry.$box_el.css(top: top) entry.$box_el.css(top: top)
entry.$indicator_el.css(top: top) entry.$indicator_el.css(top: top)
positionLayoutEl(entry.$callout_el, original_top, top) positionLayoutEl(entry.$callout_el, original_top, top)
sl_console.log "ENTRY", {entry: entry.scope.entry, top} sl_console.log "ENTRY", {entry: entry.scope.entry, top}
lastTop = top
if lastTop < TOOLBAR_HEIGHT
overflowTop = -lastTop + TOOLBAR_HEIGHT
else
overflowTop = 0
scope.$emit "review-panel:sizes", {
overflowTop: overflowTop,
height: previousBottom + OVERVIEW_TOGGLE_HEIGHT
}
scope.$applyAsync () -> scope.$applyAsync () ->
layout() layout()

View file

@ -0,0 +1,9 @@
define [
"base"
], (App) ->
app.filter "numKeys", () ->
(object) ->
if object?
return Object.keys(object).length
else
return 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -29,6 +29,8 @@
@rp-toolbar-height : 32px; @rp-toolbar-height : 32px;
@rp-entry-animation-speed: 0.3s;
.rp-button() { .rp-button() {
@ -49,11 +51,11 @@
} }
&[disabled] { &[disabled] {
opacity: 0.5; background-color: tint(@rp-highlight-blue, 50%);
&:hover, &:hover,
&:focus { &:focus {
background-color: @rp-highlight-blue; background-color: tint(@rp-highlight-blue, 50%);
} }
} }
} }
@ -158,6 +160,7 @@
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
padding-bottom: 52px;
} }
.rp-state-overview & { .rp-state-overview & {
@ -183,7 +186,7 @@
border-radius: 3px; border-radius: 3px;
color: #FFF; color: #FFF;
cursor: pointer; cursor: pointer;
transition: top 0.3s, left 0.1s, right 0.1s; transition: top @rp-entry-animation-speed, left 0.1s, right 0.1s;
.no-animate & { .no-animate & {
transition: none; transition: none;
} }
@ -279,7 +282,7 @@
border-left: solid @rp-entry-ribbon-width transparent; border-left: solid @rp-entry-ribbon-width transparent;
border-radius: 3px; border-radius: 3px;
background-color: #FFF; background-color: #FFF;
transition: top 0.3s, left 0.1s, right 0.1s; transition: top @rp-entry-animation-speed, left 0.1s, right 0.1s;
.no-animate & { .no-animate & {
transition: none; transition: none;
} }
@ -461,6 +464,7 @@
margin-top: 3px; margin-top: 3px;
overflow-x: hidden; overflow-x: hidden;
min-height: 3em; min-height: 3em;
max-height: 400px;
} }
.rp-icon-delete { .rp-icon-delete {
@ -496,6 +500,7 @@
} }
.rp-entry-callout { .rp-entry-callout {
transition: top @rp-entry-animation-speed, height @rp-entry-animation-speed;
.rp-state-current-file & { .rp-state-current-file & {
position: absolute; position: absolute;
border-top: 1px solid grey; border-top: 1px solid grey;
@ -567,8 +572,28 @@
margin-top: 10px; margin-top: 10px;
font-weight: @rp-semibold-weight; font-weight: @rp-semibold-weight;
text-align: center; text-align: center;
cursor: pointer;
} }
.rp-overview-file-num-entries {
font-weight: normal;
font-size: 0.9em;
}
.rp-overview-file-header-collapse {
display: inline-block;
float: left;
transform: rotateZ(0deg);
transition: transform 0.15s ease
}
.rp-overview-file-header-collapse-on {
transform: rotateZ(-90deg);
}
.rp-overview-file-entries {
overflow: hidden;
}
.rp-comment-wrapper { .rp-comment-wrapper {
transition: .35s opacity ease-out .2s; transition: .35s opacity ease-out .2s;
@ -762,6 +787,10 @@
.toolbar .btn-full-height:active & { .toolbar .btn-full-height:active & {
background-position-y: -60px; background-position-y: -60px;
} }
@media (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
background-image: url('/img/review-icon-sprite@2x.png');
}
} }
.resolved-comments-toggle { .resolved-comments-toggle {