mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'master-redesign' into master-redesign-templates-and-blog
This commit is contained in:
commit
86a9d08e5c
32 changed files with 446 additions and 201 deletions
|
@ -33,6 +33,14 @@ block content
|
|||
.alert.alert-warning.small(ng-if="connection.reconnecting")
|
||||
strong Reconnecting...
|
||||
|
||||
.div(ng-controller="SavingNotificationController")
|
||||
.alert.alert-warning.small(
|
||||
ng-repeat="(doc_id, state) in docSavingStatus"
|
||||
ng-if="state.unsavedSeconds > 3"
|
||||
)
|
||||
| Saving {{ state.doc.name }}... ({{ state.unsavedSeconds }} seconds of unsaved changes)
|
||||
|
||||
|
||||
include ./editor/left-menu
|
||||
|
||||
#chat-wrapper(
|
||||
|
@ -56,11 +64,23 @@ block content
|
|||
|
||||
.ui-layout-center
|
||||
include ./editor/editor
|
||||
include ./editor/binary-file
|
||||
include ./editor/track-changes
|
||||
|
||||
.ui-layout-east
|
||||
include ./editor/chat
|
||||
|
||||
script(type="text/ng-template", id="genericMessageModalTemplate")
|
||||
.modal-header
|
||||
button.close(
|
||||
type="button"
|
||||
data-dismiss="modal"
|
||||
ng-click="done()"
|
||||
) ×
|
||||
h3 {{ title }}
|
||||
.modal-body {{ message }}
|
||||
.modal-footer
|
||||
button.btn.btn-info(ng-click="done()") OK
|
||||
|
||||
script(src='/socket.io/socket.io.js')
|
||||
|
||||
|
|
19
services/web/app/views/project/editor/binary-file.jade
Normal file
19
services/web/app/views/project/editor/binary-file.jade
Normal file
|
@ -0,0 +1,19 @@
|
|||
div.binary-file.full-size(
|
||||
ng-controller="BinaryFileController"
|
||||
ng-show="ui.view == 'file'"
|
||||
ng-if="openFile"
|
||||
)
|
||||
img(
|
||||
ng-src="/project/{{ project_id }}/file/{{ openFile.id }}"
|
||||
ng-if="['png', 'jpg', 'jpeg', 'gif'].indexOf(extension(openFile)) > -1"
|
||||
)
|
||||
img(
|
||||
ng-src="/project/{{ project_id }}/file/{{ openFile.id }}?format=png"
|
||||
ng-if="['pdf', 'eps'].indexOf(extension(openFile)) > -1"
|
||||
)
|
||||
p.no-preview(
|
||||
ng-if="['png', 'jpg', 'jpeg', 'gif', 'pdf', 'eps'].indexOf(extension(openFile)) == -1"
|
||||
) Sorry, no preview is available.
|
||||
a.btn.btn-info(
|
||||
ng-href="/project/{{ project_id }}/file/{{ openFile.id }}"
|
||||
) Download {{ openFile.name }}
|
|
@ -25,7 +25,8 @@ div.full-size(
|
|||
cursor-position="editor.cursorPosition",
|
||||
goto-line="editor.gotoLine",
|
||||
resize-on="layout:main:resize,layout:pdf:resize",
|
||||
annotations="pdf.logEntryAnnotations[editor.open_doc_id]"
|
||||
annotations="pdf.logEntryAnnotations[editor.open_doc_id]",
|
||||
read-only="!permissions.write"
|
||||
)
|
||||
|
||||
.ui-layout-east
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
aside#file-tree(ng-controller="FileTreeController")
|
||||
.toolbar.toolbar-small
|
||||
.toolbar.toolbar-small(ng-if="permissions.write")
|
||||
a(
|
||||
href,
|
||||
ng-click="openNewDocModal()",
|
||||
|
@ -39,10 +39,19 @@ aside#file-tree(ng-controller="FileTreeController")
|
|||
)
|
||||
i.fa.fa-trash-o
|
||||
|
||||
.file-tree-inner
|
||||
ul.list-unstyled.file-tree-list
|
||||
.file-tree-inner(
|
||||
ng-if="rootFolder",
|
||||
ng-controller="FileTreeRootFolderController",
|
||||
ng-class="{ 'no-toolbar': !permissions.write }"
|
||||
)
|
||||
ul.list-unstyled.file-tree-list(
|
||||
droppable="permissions.write"
|
||||
accept=".entity-name"
|
||||
on-drop-callback="onDrop"
|
||||
)
|
||||
file-entity(
|
||||
entity="entity",
|
||||
permissions="permissions",
|
||||
ng-repeat="entity in rootFolder.children | orderBy:[orderByFoldersFirst, 'name']"
|
||||
)
|
||||
|
||||
|
@ -72,11 +81,12 @@ script(type='text/ng-template', id='entityListItemTemplate')
|
|||
.entity(ng-if="entity.type != 'folder'")
|
||||
.entity-name(
|
||||
ng-click="select()"
|
||||
ng-dblclick="startRenaming()"
|
||||
draggable
|
||||
ng-dblclick="permissions.write && startRenaming()"
|
||||
draggable="permissions.write"
|
||||
context-menu
|
||||
data-target="context-menu-{{ entity.id }}"
|
||||
context-menu-container="body"
|
||||
context-menu-disabled="!permissions.write"
|
||||
)
|
||||
//- Just a spacer to align with folders
|
||||
i.fa.fa-fw.toggle(ng-if="entity.type != 'folder'")
|
||||
|
@ -88,6 +98,7 @@ script(type='text/ng-template', id='entityListItemTemplate')
|
|||
ng-hide="entity.renaming"
|
||||
) {{ entity.name }}
|
||||
input(
|
||||
ng-if="permissions.write",
|
||||
ng-show="entity.renaming",
|
||||
ng-model="inputs.name",
|
||||
ng-blur="finishRenaming()",
|
||||
|
@ -95,7 +106,10 @@ script(type='text/ng-template', id='entityListItemTemplate')
|
|||
on-enter="finishRenaming()"
|
||||
)
|
||||
|
||||
span.dropdown(ng-show="entity.selected")
|
||||
span.dropdown(
|
||||
ng-show="entity.selected",
|
||||
ng-if="permissions.write"
|
||||
)
|
||||
a.dropdown-toggle(href)
|
||||
i.fa.fa-chevron-down
|
||||
|
||||
|
@ -113,7 +127,10 @@ script(type='text/ng-template', id='entityListItemTemplate')
|
|||
ng-click="openDeleteModal()"
|
||||
) Delete
|
||||
|
||||
div.dropdown.context-menu(id="context-menu-{{ entity.id }}")
|
||||
div.dropdown.context-menu(
|
||||
id="context-menu-{{ entity.id }}",
|
||||
ng-if="permissions.write"
|
||||
)
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a(
|
||||
|
@ -134,9 +151,9 @@ script(type='text/ng-template', id='entityListItemTemplate')
|
|||
.entity(ng-if="entity.type == 'folder'", ng-controller="FileTreeFolderController")
|
||||
.entity-name(
|
||||
ng-click="select()"
|
||||
ng-dblclick="startRenaming()"
|
||||
draggable
|
||||
droppable
|
||||
ng-dblclick="permissions.write && startRenaming()"
|
||||
draggable="permissions.write"
|
||||
droppable="permissions.write"
|
||||
accept=".entity-name"
|
||||
on-drop-callback="onDrop"
|
||||
)
|
||||
|
@ -144,6 +161,7 @@ script(type='text/ng-template', id='entityListItemTemplate')
|
|||
context-menu
|
||||
data-target="context-menu-{{ entity.id }}"
|
||||
context-menu-container="body"
|
||||
context-menu-disabled="!permissions.write"
|
||||
)
|
||||
i.fa.fa-fw.toggle(
|
||||
ng-if="entity.type == 'folder'"
|
||||
|
@ -164,6 +182,7 @@ script(type='text/ng-template', id='entityListItemTemplate')
|
|||
ng-hide="entity.renaming"
|
||||
) {{ entity.name }}
|
||||
input(
|
||||
ng-if="permissions.write",
|
||||
ng-show="entity.renaming",
|
||||
ng-model="inputs.name",
|
||||
ng-blur="finishRenaming()",
|
||||
|
@ -171,7 +190,10 @@ script(type='text/ng-template', id='entityListItemTemplate')
|
|||
on-enter="finishRenaming()"
|
||||
)
|
||||
|
||||
span.dropdown(ng-show="entity.selected")
|
||||
span.dropdown(
|
||||
ng-if="permissions.write"
|
||||
ng-show="entity.selected"
|
||||
)
|
||||
a.dropdown-toggle(href)
|
||||
i.fa.fa-chevron-down
|
||||
|
||||
|
@ -208,7 +230,10 @@ script(type='text/ng-template', id='entityListItemTemplate')
|
|||
ng-click="openUploadFileModal()"
|
||||
) Upload File
|
||||
|
||||
.dropdown.context-menu(id="context-menu-{{ entity.id }}")
|
||||
.dropdown.context-menu(
|
||||
ng-if="permissions.write"
|
||||
id="context-menu-{{ entity.id }}"
|
||||
)
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a(
|
||||
|
@ -250,12 +275,13 @@ script(type='text/ng-template', id='entityListItemTemplate')
|
|||
ul.list-unstyled(
|
||||
ng-if="entity.type == 'folder'"
|
||||
ng-show="expanded"
|
||||
droppable
|
||||
droppable="permissions.write"
|
||||
accept=".entity-name"
|
||||
on-drop-callback="onDrop"
|
||||
)
|
||||
file-entity(
|
||||
entity="child",
|
||||
permissions="permissions",
|
||||
ng-repeat="child in entity.children | orderBy:[orderByFoldersFirst, 'name']"
|
||||
)
|
||||
|
||||
|
|
|
@ -7,14 +7,35 @@ header.toolbar.toolbar-header(ng-cloak, ng-hide="state.loading")
|
|||
)
|
||||
i.fa.fa-fw.fa-bars
|
||||
|
||||
span.name {{ project.name }}
|
||||
.toolbar-center.project-name(ng-controller="ProjectNameController")
|
||||
span.name(
|
||||
ng-dblclick="startRenaming()",
|
||||
ng-show="!state.renaming"
|
||||
) {{ project.name }}
|
||||
|
||||
a(href='#', data-toggle="tooltip", title="Rename")
|
||||
input.form-control(
|
||||
type="text"
|
||||
ng-model="inputs.name",
|
||||
ng-show="state.renaming",
|
||||
on-enter="finishRenaming()",
|
||||
ng-blur="finishRenaming()",
|
||||
select-name-when="state.renaming"
|
||||
)
|
||||
|
||||
a.rename(
|
||||
href='#',
|
||||
tooltip-placement="bottom",
|
||||
tooltip="Rename",
|
||||
tooltip-append-to-body="true",
|
||||
ng-click="startRenaming()",
|
||||
ng-show="!state.renaming"
|
||||
)
|
||||
i.fa.fa-pencil
|
||||
|
||||
.toolbar-right
|
||||
a.btn.btn-full-height(
|
||||
href,
|
||||
ng-if="permissions.admin",
|
||||
tooltip="Share",
|
||||
tooltip-placement="bottom",
|
||||
ng-click="openShareProjectModal()",
|
||||
|
|
|
@ -9,7 +9,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
|
|||
.modal-body.modal-body-share
|
||||
.container-fluid
|
||||
.row.public-access-level(ng-show="project.publicAccesLevel == 'private'")
|
||||
.col-md-12.text-center
|
||||
.col-xs-12.text-center
|
||||
| This project is private and can only be accessed by the people below.
|
||||
|
|
||||
a(
|
||||
|
@ -17,7 +17,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
|
|||
ng-click="openMakePublicModal()"
|
||||
) Make Public
|
||||
.row.public-access-level(ng-show="project.publicAccesLevel != 'private'")
|
||||
.col-md-12.text-center
|
||||
.col-xs-12.text-center
|
||||
strong(ng-if="project.publicAccesLevel == 'readAndWrite'") This project is public and can be edited by anyone with the URL.
|
||||
strong(ng-if="project.publicAccesLevel == 'readOnly'") This project is public and can be viewed by anyone with the URL.
|
||||
|
|
||||
|
@ -26,16 +26,16 @@ script(type='text/ng-template', id='shareProjectModalTemplate')
|
|||
ng-click="openMakePrivateModal()"
|
||||
) Make Private
|
||||
.row.project-member
|
||||
.col-md-8 {{ project.owner.email }}
|
||||
.col-xs-8 {{ project.owner.email }}
|
||||
.text-right(
|
||||
ng-class="{'col-md-3': project.members.length > 0, 'col-md-4': project.members.length == 0}"
|
||||
ng-class="{'col-xs-3': project.members.length > 0, 'col-xs-4': project.members.length == 0}"
|
||||
) Owner
|
||||
.row.project-member(ng-repeat="member in project.members")
|
||||
.col-md-8 {{ member.email }}
|
||||
.col-md-3.text-right
|
||||
.col-xs-8 {{ member.email }}
|
||||
.col-xs-3.text-right
|
||||
span(ng-show="member.privileges == 'readAndWrite'") Can Edit
|
||||
span(ng-show="member.privileges == 'readOnly'") Read Only
|
||||
.col-md-1
|
||||
.col-xs-1
|
||||
a(
|
||||
href
|
||||
tooltip="Remove collaborator"
|
||||
|
|
|
@ -1,4 +1,17 @@
|
|||
div#trackChanges(ng-show="ui.view == 'track-changes'")
|
||||
.upgrade-prompt(ng-show="!project.features.versioning")
|
||||
.message(ng-show="project.owner._id == user.id")
|
||||
p You need to upgrade your account to use the History feature.
|
||||
p
|
||||
a.btn.btn-info(
|
||||
href="/user/subscription/plans"
|
||||
target="_blank"
|
||||
ng-click="startedFreeTrial = true"
|
||||
) Start Free Trial
|
||||
p.small(ng-show="startedFreeTrial") Please refresh the page after starting your free trial.
|
||||
.message(ng-show="project.owner._id != user.id")
|
||||
p Please ask the project owner to upgrade to use the History feature.
|
||||
|
||||
aside.change-list(
|
||||
ng-controller="TrackChangesListController"
|
||||
infinite-scroll="loadMore()"
|
||||
|
@ -57,8 +70,11 @@ div#trackChanges(ng-show="ui.view == 'track-changes'")
|
|||
div.users
|
||||
div.user(ng-repeat="update_user in update.meta.users")
|
||||
.color-square(ng-style="{'background-color': 'hsl({{ update_user.hue }}, 100%, 50%)'}")
|
||||
span(ng-if="update_user.id != user.id") {{user.first_name}} {{user.last_name}}
|
||||
span(ng-if="update_user.id != user.id") {{update_user.first_name}} {{update_user.last_name}}
|
||||
span(ng-if="update_user.id == user.id") You
|
||||
div.user(ng-if="update.meta.users.length == 0")
|
||||
.color-square(style="background-color: hsl(100, 100%, 50%)")
|
||||
span Anonymous
|
||||
|
||||
.loading(ng-show="trackChanges.loading")
|
||||
i.fa.fa-spin.fa-refresh
|
||||
|
|
|
@ -5,7 +5,9 @@ define [
|
|||
"ide/editor/EditorManager"
|
||||
"ide/online-users/OnlineUsersManager"
|
||||
"ide/track-changes/TrackChangesManager"
|
||||
"ide/permissions/PermissionsManager"
|
||||
"ide/pdf/PdfManager"
|
||||
"ide/binary-files/BinaryFilesManager"
|
||||
"ide/settings/index"
|
||||
"ide/share/index"
|
||||
"ide/chat/index"
|
||||
|
@ -22,7 +24,9 @@ define [
|
|||
EditorManager
|
||||
OnlineUsersManager
|
||||
TrackChangesManager
|
||||
PermissionsManager
|
||||
PdfManager
|
||||
BinaryFilesManager
|
||||
) ->
|
||||
App.controller "IdeController", ["$scope", "$timeout", "ide", ($scope, $timeout, ide) ->
|
||||
# Don't freak out if we're already in an apply callback
|
||||
|
@ -59,6 +63,8 @@ define [
|
|||
ide.onlineUsersManager = new OnlineUsersManager(ide, $scope)
|
||||
ide.trackChangesManager = new TrackChangesManager(ide, $scope)
|
||||
ide.pdfManager = new PdfManager(ide, $scope)
|
||||
ide.permissionsManager = new PermissionsManager(ide, $scope)
|
||||
ide.binaryFilesManager = new BinaryFilesManager(ide, $scope)
|
||||
]
|
||||
|
||||
angular.bootstrap(document.body, ["SharelatexApp"])
|
|
@ -0,0 +1,12 @@
|
|||
define [
|
||||
"ide/binary-files/controllers/BinaryFileController"
|
||||
], () ->
|
||||
class BinaryFilesManager
|
||||
constructor: (@ide, @$scope) ->
|
||||
@$scope.$on "entity:selected", (event, entity) =>
|
||||
if (@$scope.ui.view != "track-changes" and entity.type == "file")
|
||||
@openFile(entity)
|
||||
|
||||
openFile: (file) ->
|
||||
@$scope.ui.view = "file"
|
||||
@$scope.openFile = file
|
|
@ -0,0 +1,7 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
App.controller "BinaryFileController", ["$scope", ($scope) ->
|
||||
$scope.extension = (file) ->
|
||||
return file.name.split(".").pop()?.toLowerCase()
|
||||
]
|
|
@ -57,6 +57,7 @@ define [], () ->
|
|||
@$scope.$apply () =>
|
||||
@$scope.protocolVersion = protocolVersion
|
||||
@$scope.project = project
|
||||
@$scope.permissionsLevel = permissionsLevel
|
||||
@$scope.state.load_progress = 100
|
||||
@$scope.state.loading = false
|
||||
@$scope.$emit "project:joined"
|
||||
|
@ -79,6 +80,7 @@ define [], () ->
|
|||
countdown = 3 + Math.floor(Math.random() * 7)
|
||||
|
||||
@$scope.$apply () =>
|
||||
@$scope.connection.reconnecting = false
|
||||
@$scope.connection.reconnection_countdown = countdown
|
||||
|
||||
setTimeout(=>
|
||||
|
|
|
@ -149,6 +149,10 @@ define [
|
|||
@ide.connectionManager.disconnect()
|
||||
return
|
||||
|
||||
if Math.random() < (@ide.ignoreRate or 0)
|
||||
console.log "Simulating lost update"
|
||||
return
|
||||
|
||||
if update?.doc == @doc_id and @doc?
|
||||
@doc.processUpdateFromServer update
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
define [
|
||||
"ide/editor/Document"
|
||||
"ide/editor/directives/aceEditor"
|
||||
"ide/editor/controllers/SavingNotificationController"
|
||||
], (Document) ->
|
||||
class EditorManager
|
||||
constructor: (@ide, @$scope) ->
|
||||
|
@ -14,7 +15,7 @@ define [
|
|||
}
|
||||
|
||||
@$scope.$on "entity:selected", (event, entity) =>
|
||||
if (@$scope.ui.view == "editor" and entity.type == "doc")
|
||||
if (@$scope.ui.view != "track-changes" and entity.type == "doc")
|
||||
@openDoc(entity)
|
||||
|
||||
initialized = false
|
||||
|
@ -77,25 +78,17 @@ define [
|
|||
|
||||
_bindToDocumentEvents: (doc, sharejs_doc) ->
|
||||
sharejs_doc.on "error", (error) =>
|
||||
console.error "DOC ERROR", error
|
||||
@openDoc(doc, forceReopen: true)
|
||||
|
||||
#TODO!!!
|
||||
# Modal.createModal
|
||||
# title: "Out of sync"
|
||||
# message: "Sorry, this file has gone out of sync and we need to do a full refresh. Please let us know if this happens frequently."
|
||||
# buttons:[
|
||||
# text: "Ok"
|
||||
# ]
|
||||
@ide.showGenericMessageModal(
|
||||
"Out of sync"
|
||||
"Sorry, this file has gone out of sync and we need to do a full refresh. Please let us know if this happens frequently."
|
||||
)
|
||||
|
||||
sharejs_doc.on "externalUpdate", () =>
|
||||
#TODO!!!
|
||||
# Modal.createModal
|
||||
# title: "Document Updated Externally"
|
||||
# message: "This document was just updated externally. Any recent changes you have made may have been overwritten. To see previous versions please look in the history."
|
||||
# buttons:[
|
||||
# text: "Ok"
|
||||
# ]
|
||||
@ide.showGenericMessageModal(
|
||||
"Document Updated Externally"
|
||||
"This document was just updated externally. Any recent changes you have made may have been overwritten. To see previous versions please look in the history."
|
||||
)
|
||||
|
||||
_unbindFromDocumentEvents: (document) ->
|
||||
document.off()
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
define [
|
||||
"base"
|
||||
"ide/editor/Document"
|
||||
], (App, Document) ->
|
||||
App.controller "SavingNotificationController", ["$scope", "$interval", "ide", ($scope, $interval, ide) ->
|
||||
$interval () ->
|
||||
pollSavedStatus()
|
||||
, 1000
|
||||
|
||||
$(window).bind 'beforeunload', () =>
|
||||
warnAboutUnsavedChanges()
|
||||
|
||||
$scope.docSavingStatus = {}
|
||||
pollSavedStatus = () ->
|
||||
oldStatus = $scope.docSavingStatus
|
||||
$scope.docSavingStatus = {}
|
||||
|
||||
for doc_id, doc of Document.openDocs
|
||||
saving = doc.pollSavedStatus()
|
||||
if !saving
|
||||
if oldStatus[doc_id]?
|
||||
$scope.docSavingStatus[doc_id] = oldStatus[doc_id]
|
||||
$scope.docSavingStatus[doc_id].unsavedSeconds += 1
|
||||
else
|
||||
$scope.docSavingStatus[doc_id] = {
|
||||
unsavedSeconds: 0
|
||||
doc: ide.fileTreeManager.findEntityById(doc_id)
|
||||
}
|
||||
|
||||
warnAboutUnsavedChanges = () ->
|
||||
if Document.hasUnsavedChanges()
|
||||
return "You have unsaved changes. If you leave now they will not be saved."
|
||||
]
|
|
@ -204,16 +204,16 @@ define [
|
|||
_getColorScheme: (hue) ->
|
||||
if @_isDarkTheme()
|
||||
return {
|
||||
cursor: "hsl(#{hue}, 100%, 50%)"
|
||||
labelBackgroundColor: "hsl(#{hue}, 100%, 50%)"
|
||||
cursor: "hsl(#{hue}, 70%, 50%)"
|
||||
labelBackgroundColor: "hsl(#{hue}, 70%, 50%)"
|
||||
highlightBackgroundColor: "hsl(#{hue}, 100%, 28%);"
|
||||
strikeThroughBackgroundColor: "hsl(#{hue}, 100%, 20%);"
|
||||
strikeThroughForegroundColor: "hsl(#{hue}, 100%, 60%);"
|
||||
}
|
||||
else
|
||||
return {
|
||||
cursor: "hsl(#{hue}, 100%, 50%)"
|
||||
labelBackgroundColor: "hsl(#{hue}, 100%, 50%)"
|
||||
cursor: "hsl(#{hue}, 70%, 50%)"
|
||||
labelBackgroundColor: "hsl(#{hue}, 70%, 50%)"
|
||||
highlightBackgroundColor: "hsl(#{hue}, 70%, 85%);"
|
||||
strikeThroughBackgroundColor: "hsl(#{hue}, 70%, 95%);"
|
||||
strikeThroughForegroundColor: "hsl(#{hue}, 70%, 40%);"
|
||||
|
|
|
@ -5,6 +5,7 @@ define [
|
|||
"ide/file-tree/controllers/FileTreeController"
|
||||
"ide/file-tree/controllers/FileTreeEntityController"
|
||||
"ide/file-tree/controllers/FileTreeFolderController"
|
||||
"ide/file-tree/controllers/FileTreeRootFolderController"
|
||||
], () ->
|
||||
class FileTreeManager
|
||||
constructor: (@ide, @$scope) ->
|
||||
|
@ -59,6 +60,7 @@ define [
|
|||
@ide.socket.on "reciveEntityMove", (entity_id, folder_id) =>
|
||||
entity = @findEntityById(entity_id)
|
||||
folder = @findEntityById(folder_id)
|
||||
console.log "Got recive ENTITY", entity_id, folder_id, entity, folder
|
||||
@$scope.$apply () =>
|
||||
@_moveEntityInScope(entity, folder)
|
||||
|
||||
|
@ -68,7 +70,15 @@ define [
|
|||
entity.selected = false
|
||||
entity.selected = true
|
||||
|
||||
findSelectedEntity: () ->
|
||||
selected = null
|
||||
@forEachEntity (entity) ->
|
||||
selected = entity if entity.selected
|
||||
return selected
|
||||
|
||||
findEntityById: (id, options = {}) ->
|
||||
return @$scope.rootFolder if @$scope.rootFolder.id == id
|
||||
|
||||
entity = @_findEntityByIdInFolder @$scope.rootFolder, id
|
||||
return entity if entity?
|
||||
|
||||
|
@ -250,7 +260,7 @@ define [
|
|||
_csrf: window.csrfToken
|
||||
}
|
||||
|
||||
_deleteEntityFromScope: (entity) ->
|
||||
_deleteEntityFromScope: (entity, options = { moveToDeleted: true }) ->
|
||||
parent_folder = null
|
||||
@forEachEntity (possible_entity, folder) ->
|
||||
if possible_entity == entity
|
||||
|
@ -261,11 +271,11 @@ define [
|
|||
if index > -1
|
||||
parent_folder.children.splice(index, 1)
|
||||
|
||||
if entity.type == "doc"
|
||||
if entity.type == "doc" and options.moveToDeleted
|
||||
entity.deleted = true
|
||||
@$scope.deletedDocs.push entity
|
||||
|
||||
_moveEntityInScope: (entity, parent_folder) ->
|
||||
return if entity in parent_folder.children
|
||||
@_deleteEntityFromScope(entity)
|
||||
@_deleteEntityFromScope(entity, moveToDeleted: false)
|
||||
parent_folder.children.push(entity)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
App.controller "FileTreeRootFolderController", ["$scope", "ide", ($scope, ide) ->
|
||||
console.log "CREATING FileTreeRootFolderController"
|
||||
rootFolder = $scope.rootFolder
|
||||
console.log "ROOT FOLDER", rootFolder
|
||||
$scope.onDrop = (events, ui) ->
|
||||
source = $(ui.draggable).scope().entity
|
||||
console.log "DROPPED INTO ROOT", source, rootFolder
|
||||
return if !source?
|
||||
ide.fileTreeManager.moveEntity(source, rootFolder)
|
||||
]
|
|
@ -4,6 +4,8 @@ define [
|
|||
App.directive "draggable", () ->
|
||||
return {
|
||||
link: (scope, element, attrs) ->
|
||||
scope.$watch attrs.draggable, (draggable) ->
|
||||
if draggable
|
||||
element.draggable
|
||||
delay: 250
|
||||
opacity: 0.7
|
||||
|
|
|
@ -7,6 +7,8 @@ define [
|
|||
onDropCallback: "="
|
||||
}
|
||||
link: (scope, element, attrs) ->
|
||||
scope.$watch attrs.droppable, (droppable) ->
|
||||
if droppable
|
||||
element.droppable
|
||||
greedy: true
|
||||
hoverClass: "droppable-hover"
|
||||
|
|
|
@ -6,10 +6,18 @@ define [
|
|||
restrict: "E"
|
||||
scope: {
|
||||
entity: "="
|
||||
permissions: "="
|
||||
}
|
||||
templateUrl: "entityListItemTemplate"
|
||||
compile: (element) ->
|
||||
RecursionHelper.compile element, (scope, element, attrs, ctrl) ->
|
||||
# Link function here if needed
|
||||
# Don't freak out if we're already in an apply callback
|
||||
scope.$originalApply = scope.$apply
|
||||
scope.$apply = (fn = () ->) ->
|
||||
phase = @$root.$$phase
|
||||
if (phase == '$apply' || phase == '$digest')
|
||||
fn()
|
||||
else
|
||||
@$originalApply(fn);
|
||||
}
|
||||
]
|
|
@ -56,7 +56,7 @@ define [
|
|||
OWN_HUE: 200 # We will always appear as this color to ourselves
|
||||
ANONYMOUS_HUE: 100
|
||||
getHueForUserId: (user_id) ->
|
||||
if !user_id?
|
||||
if !user_id? or user_id == "anonymous-user"
|
||||
return @ANONYMOUS_HUE
|
||||
|
||||
if window.user.id == user_id
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
define [], () ->
|
||||
class PermissionsManager
|
||||
constructor: (@ide, @$scope) ->
|
||||
@$scope.$watch "permissionsLevel", (permissionsLevel) =>
|
||||
@$scope.permissions =
|
||||
read: false
|
||||
write: false
|
||||
admin: false
|
||||
if permissionsLevel?
|
||||
if permissionsLevel == "readOnly"
|
||||
@$scope.permissions.read = true
|
||||
else if permissionsLevel == "readAndWrite"
|
||||
@$scope.permissions.read = true
|
||||
@$scope.permissions.write = true
|
||||
else if permissionsLevel == "owner"
|
||||
@$scope.permissions.read = true
|
||||
@$scope.permissions.write = true
|
||||
@$scope.permissions.admin = true
|
||||
|
|
@ -3,7 +3,7 @@ define [
|
|||
], (App) ->
|
||||
# We create and provide this as service so that we can access the global ide
|
||||
# from within other parts of the angular app.
|
||||
App.factory "ide", ["$http", ($http) ->
|
||||
App.factory "ide", ["$http", "$modal", ($http, $modal) ->
|
||||
ide = {}
|
||||
ide.$http = $http
|
||||
|
||||
|
@ -16,5 +16,22 @@ define [
|
|||
ide.showGenericServerErrorMessage = () ->
|
||||
console.error "GENERIC SERVER ERROR MESSAGE STUB"
|
||||
|
||||
ide.showGenericMessageModal = (title, message) ->
|
||||
$modal.open {
|
||||
templateUrl: "genericMessageModalTemplate"
|
||||
controller: "GenericMessageModalController"
|
||||
resolve:
|
||||
title: -> title
|
||||
message: -> message
|
||||
}
|
||||
|
||||
return ide
|
||||
]
|
||||
|
||||
App.controller "GenericMessageModalController", ["$scope", "$modalInstance", "title", "message", ($scope, $modalInstance, title, message) ->
|
||||
$scope.title = title
|
||||
$scope.message = message
|
||||
|
||||
$scope.done = () ->
|
||||
$modalInstance.close()
|
||||
]
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
App.controller "ProjectNameController", ["$scope", "settings", "ide", ($scope, settings, ide) ->
|
||||
$scope.state =
|
||||
renaming: false
|
||||
$scope.inputs = {}
|
||||
|
||||
$scope.startRenaming = () ->
|
||||
$scope.inputs.name = $scope.project.name
|
||||
$scope.state.renaming = true
|
||||
$scope.$emit "project:rename:start"
|
||||
|
||||
$scope.finishRenaming = () ->
|
||||
$scope.project.name = $scope.inputs.name
|
||||
settings.saveProjectSettings({name: $scope.inputs.name})
|
||||
$scope.state.renaming = false
|
||||
|
||||
ide.socket.on "projectNameUpdated", (name) ->
|
||||
$scope.$apply () ->
|
||||
$scope.project.name = name
|
||||
|
||||
$scope.$watch "project.name", (name) ->
|
||||
if name?
|
||||
window.document.title = name + " - Online LaTeX Editor ShareLaTeX"
|
||||
]
|
|
@ -1,5 +1,7 @@
|
|||
define [
|
||||
"ide/settings/services/settings"
|
||||
"ide/settings/controllers/SettingsController"
|
||||
"ide/settings/controllers/ProjectNameController"
|
||||
|
||||
], () ->
|
||||
|
||||
|
|
|
@ -9,10 +9,9 @@ define [
|
|||
|
||||
@$scope.toggleTrackChanges = () =>
|
||||
if @$scope.ui.view == "track-changes"
|
||||
@$scope.ui.view = "editor"
|
||||
@hide()
|
||||
else
|
||||
@$scope.ui.view = "track-changes"
|
||||
@onShow()
|
||||
@show()
|
||||
|
||||
@$scope.$watch "trackChanges.selection.updates", (updates) =>
|
||||
if updates? and updates.length > 0
|
||||
|
@ -24,11 +23,14 @@ define [
|
|||
@$scope.trackChanges.selection.doc = entity
|
||||
@reloadDiff()
|
||||
|
||||
onShow: () ->
|
||||
show: () ->
|
||||
@$scope.ui.view = "track-changes"
|
||||
@reset()
|
||||
# @fetchNextBatchOfChanges()
|
||||
# .success () =>
|
||||
# @autoSelectRecentUpdates()
|
||||
|
||||
hide: () ->
|
||||
@$scope.ui.view = "editor"
|
||||
# Make sure we run the 'open' logic for whatever is currently selected
|
||||
@$scope.$emit "entity:selected", @ide.fileTreeManager.findSelectedEntity()
|
||||
|
||||
reset: () ->
|
||||
@$scope.trackChanges = {
|
||||
|
@ -157,21 +159,24 @@ define [
|
|||
}
|
||||
|
||||
if entry.i? or entry.d?
|
||||
if entry.meta.user?
|
||||
name = "#{entry.meta.user.first_name} #{entry.meta.user.last_name}"
|
||||
if entry.meta.user.id == @$scope.user.id
|
||||
else
|
||||
name = "Anonymous"
|
||||
if entry.meta.user?.id == @$scope.user.id
|
||||
name = "you"
|
||||
date = moment(entry.meta.end_ts).format("Do MMM YYYY, h:mm a")
|
||||
if entry.i?
|
||||
highlights.push {
|
||||
label: "Added by #{name} on #{date}"
|
||||
highlight: range
|
||||
hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user.id)
|
||||
hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user?.id)
|
||||
}
|
||||
else if entry.d?
|
||||
highlights.push {
|
||||
label: "Deleted by #{name} on #{date}"
|
||||
strikeThrough: range
|
||||
hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user.id)
|
||||
hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user?.id)
|
||||
}
|
||||
|
||||
return {text, highlights}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
@import "./editor/pdf.less";
|
||||
@import "./editor/share.less";
|
||||
@import "./editor/chat.less";
|
||||
@import "./editor/binary-file.less";
|
||||
|
||||
.full-size {
|
||||
position: absolute;
|
||||
|
@ -17,10 +18,15 @@
|
|||
.global-alerts {
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
top: (@line-height-computed / 4);
|
||||
top: 2px;
|
||||
width: 400px;
|
||||
left: 50%;
|
||||
margin-left: -200px;
|
||||
.alert {
|
||||
padding: (@line-height-computed / 4);
|
||||
font-size: 14px;
|
||||
margin-bottom: (@line-height-computed / 4);
|
||||
}
|
||||
}
|
||||
|
||||
#chat-wrapper {
|
||||
|
@ -57,6 +63,35 @@
|
|||
}
|
||||
}
|
||||
|
||||
.project-name {
|
||||
.name {
|
||||
display: inline-block;
|
||||
max-width: 250px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: top;
|
||||
padding: 6px;
|
||||
color: @gray;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
input {
|
||||
height: 30px;
|
||||
margin-top: 4px;
|
||||
text-align: center;
|
||||
padding: 6px;
|
||||
font-weight: 700;
|
||||
}
|
||||
a.rename {
|
||||
visibility: hidden;
|
||||
}
|
||||
&:hover {
|
||||
a.rename {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The internal components of the aceEditor directive
|
||||
.ace-editor-wrapper {
|
||||
.full-size;
|
||||
|
|
20
services/web/public/stylesheets/app/editor/binary-file.less
Normal file
20
services/web/public/stylesheets/app/editor/binary-file.less
Normal file
|
@ -0,0 +1,20 @@
|
|||
.binary-file {
|
||||
padding: @line-height-computed / 2;
|
||||
background-color: @gray-lightest;
|
||||
text-align: center;
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 90%;
|
||||
display: block;
|
||||
margin: auto;
|
||||
margin-bottom: @line-height-computed / 2;
|
||||
border: 1px solid @gray;
|
||||
.box-shadow(0 2px 3px @gray;);
|
||||
background-color: white;
|
||||
}
|
||||
p.no-preview {
|
||||
font-size: 24px;
|
||||
color: @gray;
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,10 @@ aside#file-tree {
|
|||
left: 0;
|
||||
right: 0;
|
||||
overflow-y: auto;
|
||||
|
||||
&.no-toolbar {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
.log-btn {
|
||||
position: relative;
|
||||
.label {
|
||||
|
@ -86,6 +87,13 @@
|
|||
.label {
|
||||
display: none;
|
||||
}
|
||||
color: white;
|
||||
background-color: @link-color;
|
||||
.box-shadow(inset 0 3px 5px rgba(0, 0, 0, 0.225));
|
||||
&:hover {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,11 +13,6 @@
|
|||
color: @gray-dark;
|
||||
text-decoration: none;
|
||||
}
|
||||
&.active, &:active {
|
||||
color: white;
|
||||
background-color: @link-color;
|
||||
.box-shadow(inset 0 3px 5px rgba(0, 0, 0, 0.225));
|
||||
}
|
||||
}
|
||||
|
||||
.btn-full-height {
|
||||
|
@ -47,6 +42,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
.toolbar-center {
|
||||
width: 300px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
margin-left: -150px;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.toolbar-header {
|
||||
box-shadow: 0 0 2px #ccc;
|
||||
position: absolute;
|
||||
|
|
|
@ -9,117 +9,25 @@
|
|||
@range-bar-selected-offset: 14px;
|
||||
|
||||
#trackChanges {
|
||||
// .track-changes-diff {
|
||||
// position: absolute;
|
||||
// right: @changesListWidth + 1px;
|
||||
// left: 0;
|
||||
// top: 0;
|
||||
// bottom: 0;
|
||||
// height: 100%;
|
||||
// .ace_editor {
|
||||
// position: absolute;
|
||||
// top: 42px;
|
||||
// left: 0;
|
||||
// right: 0;
|
||||
// bottom: 0;
|
||||
// .ace_active-line, .ace_cursor-layer, .ace_gutter-active-line {
|
||||
// display: none;
|
||||
// }
|
||||
// }
|
||||
// .track-changes-diff-toolbar {
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// right: -1px;
|
||||
// height: 32px;
|
||||
// padding: 5px 5px 5px 5px;
|
||||
// margin: 0;
|
||||
// background-color: #282828;
|
||||
// color: white;
|
||||
// border-right: 1px solid white;
|
||||
// .number-of-changes, .restore {
|
||||
// position: absolute;
|
||||
// }
|
||||
// .number-of-changes {
|
||||
// left: 10px;
|
||||
// bottom: 7px;
|
||||
// }
|
||||
// .restore {
|
||||
// right: 10px;
|
||||
// bottom: 5px;
|
||||
// padding: 3px 9px;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// .track-changes-upgrade-control, .track-changes-upgrade-popup {
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// bottom: 0;
|
||||
// left: 0;
|
||||
// right: 0;
|
||||
// z-index: 100;
|
||||
// }
|
||||
|
||||
// .track-changes-upgrade-popup {
|
||||
// background-color: rgba(128,128,128,0.4);
|
||||
// .message {
|
||||
// margin: auto;
|
||||
// margin-top: 200px;
|
||||
// padding: 10px 10px 14px 10px;
|
||||
// width: 400px;
|
||||
// font-weight: bold;
|
||||
// text-align: center;
|
||||
// background-color: white;
|
||||
// .border-radius(8px);
|
||||
// }
|
||||
// }
|
||||
|
||||
// .track-changes-upgrade-control {
|
||||
// background-color: #eeeeee;
|
||||
// text-align: center;
|
||||
// .message {
|
||||
// font-size: 18px;
|
||||
// margin: 12px;
|
||||
// margin-top: 36px;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .deleted-background,
|
||||
// .deleted-foreground,
|
||||
// .inserted-background,
|
||||
// .name-marker,
|
||||
// .changes-before,
|
||||
// .changes-after {
|
||||
// position: absolute;
|
||||
// z-index: 2;
|
||||
// }
|
||||
|
||||
// .name-marker {
|
||||
// font-size: 0.8em;
|
||||
// padding: 2px 6px;
|
||||
// .border-radius(3px 3px 3px 3px);
|
||||
// position: absolute;
|
||||
// border: 1px solid #999;
|
||||
// left: 0;
|
||||
// white-space: pre;
|
||||
// }
|
||||
|
||||
// .changes-before {
|
||||
// top: 6px;
|
||||
// right: 6px;
|
||||
// }
|
||||
// .changes-after {
|
||||
// bottom: 6px;
|
||||
// right: 6px;
|
||||
// }
|
||||
// .changes-before, .changes-after {
|
||||
// padding: 4px 8px;
|
||||
// background-color: #eee;
|
||||
// border: 1px solid #999;
|
||||
// .border-radius(3px);
|
||||
// }
|
||||
.upgrade-prompt {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background-color: rgba(128,128,128,0.4);
|
||||
.message {
|
||||
margin: auto;
|
||||
margin-top: 100px;
|
||||
padding: 10px 10px 14px 10px;
|
||||
width: 500px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.diff {
|
||||
margin-right: @changesListWidth;
|
||||
|
|
Loading…
Reference in a new issue