diff --git a/services/web/Gruntfile.coffee b/services/web/Gruntfile.coffee index cb58399d80..9d7e8c3b14 100644 --- a/services/web/Gruntfile.coffee +++ b/services/web/Gruntfile.coffee @@ -37,15 +37,13 @@ module.exports = (grunt) -> join: true files: "public/js/libs/sharejs.js": [ - "public/coffee/editor/ShareJSHeader.coffee" - "public/coffee/editor/sharejs/types/helpers.coffee" - "public/coffee/editor/sharejs/types/text.coffee" - "public/coffee/editor/sharejs/types/text-api.coffee" - "public/coffee/editor/sharejs/types/json.coffee" - "public/coffee/editor/sharejs/types/json-api.coffee" - "public/coffee/editor/sharejs/client/microevent.coffee" - "public/coffee/editor/sharejs/client/doc.coffee" - "public/coffee/editor/sharejs/client/ace.coffee" + "public/coffee/ide/editor/sharejs/header.coffee" + "public/coffee/ide/editor/sharejs/vendor/types/helpers.coffee" + "public/coffee/ide/editor/sharejs/vendor/types/text.coffee" + "public/coffee/ide/editor/sharejs/vendor/types/text-api.coffee" + "public/coffee/ide/editor/sharejs/vendor/client/microevent.coffee" + "public/coffee/ide/editor/sharejs/vendor/client/doc.coffee" + "public/coffee/ide/editor/sharejs/vendor/client/ace.coffee" ] client: @@ -89,12 +87,8 @@ module.exports = (grunt) -> inlineText: false preserveLicenseComments: false paths: - "underscore": "libs/underscore" - "jquery": "libs/jquery" - "moment": "libs/moment" + "moment": "libs/moment-2.7.0" shim: - "libs/backbone": - deps: ["libs/underscore"] "libs/pdfListView/PdfListView": deps: ["libs/pdf"] "libs/pdf": @@ -104,16 +98,12 @@ module.exports = (grunt) -> modules: [ { name: "main", - exclude: ["jquery"] + exclude: ["libs"] }, { name: "ide", - exclude: ["jquery"] + exclude: ["libs", "libs/jquery-layout"] }, { - name: "home", - exclude: ["jquery"] - }, { - name: "list", - exclude: ["jquery"] + name: "libs" } ] diff --git a/services/web/app/views/layout.jade b/services/web/app/views/layout.jade index c0c5847faa..576e0d8d76 100644 --- a/services/web/app/views/layout.jade +++ b/services/web/app/views/layout.jade @@ -30,11 +30,8 @@ html(itemscope, itemtype='http://schema.org/Product') script(type="text/javascript"). window.csrfToken = "#{csrfToken}"; - script(src=jsPath+'libs/jquery.js') - script(src=jsPath+'libs/angular-1.2.17.js') - script(src=jsPath+'libs/moment-2.4.0.js') - script(src=jsPath+'libs/underscore-1.3.3.js') - block scripts + script(src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js") + script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js") body - if(typeof(suppressNavbar) == "undefined") @@ -61,10 +58,13 @@ html(itemscope, itemtype='http://schema.org/Product') - if(typeof(suppressFooter) == "undefined") script(type='text/javascript'). window.requirejs = { - "urlArgs" : "fingerprint=#{fingerprint(jsPath + 'app/main.js')}" + "urlArgs" : "fingerprint=#{fingerprint(jsPath + 'app/main.js')}", + "paths" : { + "moment": "libs/moment-2.7.0" + } }; script( - data-main=jsPath+'app/main.js', + data-main=jsPath+'main.js', baseurl=jsPath, src=jsPath+'libs/require.js?fingerprint='+fingerprint(jsPath + 'libs/require.js') ) diff --git a/services/web/app/views/project/editor.jade b/services/web/app/views/project/editor.jade index 2f0665b2f0..15a593f044 100644 --- a/services/web/app/views/project/editor.jade +++ b/services/web/app/views/project/editor.jade @@ -5,13 +5,6 @@ block vars - var suppressFooter = true - var suppressDefaultJs = true -block scripts - //- Only use the native bootstrap on the editor page, - //- since we use the Angular-based bootstrap elsewhere. - //- script(src=jsPath+'libs/bootstrap-3.1.1.js') - script(src=jsPath+'libs/jquery-layout.js') - script(src=jsPath+'libs/jquery.storage.js') - block content .editor(ng-controller="IdeController") .loading-screen(ng-show="state.loading") @@ -92,19 +85,12 @@ block content window.csrfToken = "!{csrfToken}"; window.requirejs = { "paths" : { - "underscore": "../libs/underscore-1.3.3", "mathjax": "https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS_HTML", - "moment": "libs/moment-2.4.0", - "ace": "#{jsPath}ace", - "libs": "#{jsPath}libs", - "text": "#{jsPath}text" + "moment": "libs/moment-2.7.0" }, "urlArgs" : "fingerprint=#{fingerprint(jsPath + 'ide.js')}", "waitSeconds": 0, "shim": { - "libs/backbone": { - deps: ["libs/underscore-1.3.3"] - }, "libs/pdfListView/PdfListView": { deps: ["libs/pdf"] }, @@ -125,7 +111,7 @@ block content window.sharelatex.pdfJsWorkerPath = "#{pdfJsWorkerPath}" script( - data-main=jsPath+'app/ide.js', + data-main=jsPath+'ide.js', baseurl=jsPath, data-ace-base=jsPath+'ace', src=jsPath+'libs/require.js?fingerprint='+fingerprint(jsPath + 'libs/require.js') diff --git a/services/web/app/views/project/list.jade b/services/web/app/views/project/list.jade index e75b5091fb..821e9e7ee6 100644 --- a/services/web/app/views/project/list.jade +++ b/services/web/app/views/project/list.jade @@ -1,6 +1,6 @@ extends ../layout -block scripts +block content script(type="text/javascript"). window.data = { projects: !{JSON.stringify(projects)}, @@ -13,7 +13,6 @@ block scripts } }; -block content .content.content-alt(ng-controller="ProjectPageController") .container .row diff --git a/services/web/public/coffee/SubscriptionGroupsManager.coffee b/services/web/public/coffee/SubscriptionGroupsManager.coffee deleted file mode 100644 index 053eca14b6..0000000000 --- a/services/web/public/coffee/SubscriptionGroupsManager.coffee +++ /dev/null @@ -1,88 +0,0 @@ -require [ - "libs/mustache" - "./main" - "underscore" -], (m)-> - $(document).ready -> - - tableRowTemplate = ''' - - - {{ email }} - {{ first_name }} {{ last_name }} - {{ !holdingAccount }} - - - - - ''' - - window.temp = tableRowTemplate - - $form = $('form#addUserToGroup') - - addUser = (e)-> - - parseEmails = (emailsString)-> - regexBySpaceOrComma = /[\s,]+/ - emails = emailsString.split(regexBySpaceOrComma) - emails = _.map emails, (email)-> - email = email.trim() - emails = _.select emails, (email)-> - email.indexOf("@") != -1 - return emails - - - sendNewUserToServer = (email)-> - $.ajax - url: "/subscription/group/user" - type: 'POST' - data: - email: email - _csrf: csrfToken - success: (data)-> - if data.limitReached - alert("You have reached your maximum number of members") - else - renderNewUserInList data.user - - renderNewUserInList = (user)-> - html = Mustache.to_html(tableRowTemplate, user) - $('#userList').append(html) - - e.preventDefault() - val = $form.find("input[name=email]").val() - emails = parseEmails(val) - emails.forEach (email)-> - sendNewUserToServer(email) - $form.find("input").val('') - - removeUsers = (e)-> - selectedUserRows = $('td input.select-one:checked').closest('tr').find(".user_id").toArray() - do deleteNext = () -> - row = selectedUserRows.pop() - if row? - user_id = $(row).val() - $.ajax - url: "/subscription/group/user/#{user_id}" - type: 'DELETE' - data: - _csrf: csrfToken - success: -> - $(row).parents("tr").fadeOut(250) - deleteNext() - - $form.on 'keypress', (e)-> - if(e.keyCode == 13) - addUser(e) - - $form.find(".addUser").on 'click', addUser - - $('#deleteUsers').on 'click', removeUsers - - $('input.select-all').on "change", () -> - if $(@).is(":checked") - $("input.select-one").prop( "checked", true ) - else - $("input.select-one").prop( "checked", false ) - diff --git a/services/web/public/coffee/account/AccountManager.coffee b/services/web/public/coffee/account/AccountManager.coffee deleted file mode 100644 index 5329d36de3..0000000000 --- a/services/web/public/coffee/account/AccountManager.coffee +++ /dev/null @@ -1,70 +0,0 @@ -define [ - "utils/Modal" -], (Modal) -> - - - - AccountManager = - askToUpgrade: (ide, options = {}) -> - options.why ||= "to use this feature" - if ide.project.get("owner") == ide.user - if ide.user.get("subscription").freeTrial.allowed - @showCreditCardFreeTrialModal(options) - else - @showUpgradeDialog(ide, options) - else - @showAskOwnerDialog(ide, options) - - showCreditCardFreeTrialModal: (options) -> - Modal.createModal - title: "Start your free trial" - message: "You need to upgrade your account #{options.why}. Would you like to start a 30 day free trial? You can cancel at any point." - buttons: [{ - text: "Cancel" - class: "" - },{ - text: "Enter Billing Information" - class: "btn-primary" - callback: () => - options.onUpgrade?() - @gotoSubscriptionsPage() - }] - - gotoSubscriptionsPage: () -> - window.open("/user/subscription/new?planCode=student_free_trial") - Modal.createModal - title: "Please refresh" - message: "Please refresh this page after starting your free trial. This will make sure all of your features are enabled." - buttons: [{ - text: "OK" - class: "" - }] - - showUpgradeDialog: (ide, options = {}) -> - options.message ||= """ - Sorry, you need to upgrade your account #{options.why}. - You can do this on your account settings page, - accessible in the top right hand corner. - """ - Modal.createModal - title: "Please upgrade your account" - message: options.message - buttons: [{ - text: "OK" - class: "btn" - callback: () -> options.onCancel() if options.onCancel - },{ - text: "See Plans" - class: "btn-success" - callback: () -> window.open("/user/subscription/plans") - }] - - showAskOwnerDialog: (ide, options = {}) -> - Modal.createModal - title: "Owner needs an upgraded account" - message: "Please ask the owner of this project to upgrade their account #{options.why}." - buttons: [{ - text: "OK" - class: "btn-primary" - callback: () -> options.onCancel() if options.onCancel - }] diff --git a/services/web/public/coffee/admin.coffee b/services/web/public/coffee/admin.coffee deleted file mode 100644 index 364d179f9c..0000000000 --- a/services/web/public/coffee/admin.coffee +++ /dev/null @@ -1,33 +0,0 @@ -require [ - "main" - "libs/jquery.tablesorter" -], ()-> - $(document).ready ()-> - $('#connected-users').tablesorter() - - $('button#disconnectAll').click (event)-> - event.preventDefault() - $.ajax - url: "/admin/dissconectAllUsers", - type:'POST', - data: - _csrf: $(@).data("csrf") - success: (data)-> - - $('button#closeEditor').click (event)-> - event.preventDefault() - $.ajax - url: "/admin/closeEditor", - type:'POST', - data: - _csrf: $(@).data("csrf") - success: (data)-> - - $('button#pollTpds').click (event)-> - event.preventDefault() - $.ajax - url: "/admin/pollUsersWithDropbox", - type:'POST', - data: - _csrf: $(@).data("csrf") - success: (data)-> diff --git a/services/web/public/coffee/analytics/AnalyticsManager.coffee b/services/web/public/coffee/analytics/AnalyticsManager.coffee deleted file mode 100644 index e06cf264c3..0000000000 --- a/services/web/public/coffee/analytics/AnalyticsManager.coffee +++ /dev/null @@ -1,33 +0,0 @@ -define [ - "libs/md5" -], () -> - class AnalyticsManager - constructor: (@ide) -> - @ide.editor.on "update:doc", () => - @updateCount ||= 0 - @updateCount++ - if @updateCount == 100 - ga('send', 'event', 'editor-interaction', 'multi-doc-update') - - @ide.pdfManager.on "compile:pdf", () => - @compileCount ||= 0 - @compileCount++ - if @compileCount == 1 - ga('send', 'event', 'editor-interaction', 'single-compile') - if @compileCount == 3 - ga('send', 'event', 'editor-interaction', 'multi-compile') - - getABTestBucket: (test_name, buckets = []) -> - hash = CryptoJS.MD5("#{@ide.user.get("id")}:#{test_name}") - bucketIndex = parseInt(hash.toString().slice(0,2), 16) % buckets.length - return buckets[bucketIndex] - - startABTest: (test_name, buckets = []) -> - value = @getABTestBucket(test_name, buckets) - ga('send', 'event', 'ab_tests', test_name, "viewed-#{value}") - return value - - endABTest: (test_name, buckets = []) -> - value = @getABTestBucket(test_name, buckets) - ga('send', 'event', 'ab_tests', test_name, "converted-#{value}") - return value \ No newline at end of file diff --git a/services/web/public/coffee/app/base.coffee b/services/web/public/coffee/app/base.coffee deleted file mode 100644 index 704b6c9285..0000000000 --- a/services/web/public/coffee/app/base.coffee +++ /dev/null @@ -1,14 +0,0 @@ -define [ - "../libs/angular-autocomplete/angular-autocomplete" - "../libs/ui-bootstrap" - "modules/recursionHelper" - "../libs/ng-context-menu-0.1.4" -], () -> - App = angular.module("SharelatexApp", [ - "ui.bootstrap" - "autocomplete" - "RecursionHelper" - "ng-context-menu" - ]) - - return App \ No newline at end of file diff --git a/services/web/public/coffee/app/directives/asyncForm.coffee b/services/web/public/coffee/app/directives/asyncForm.coffee deleted file mode 100644 index 72702cf347..0000000000 --- a/services/web/public/coffee/app/directives/asyncForm.coffee +++ /dev/null @@ -1,63 +0,0 @@ -define [ - "base" -], (App) -> - App.directive "asyncForm", ($http) -> - return { - link: (scope, element, attrs) -> - formName = attrs.asyncForm - - scope[attrs.name].response = response = {} - - element.on "submit", (e) -> - e.preventDefault() - - formData = {} - for data in element.serializeArray() - formData[data.name] = data.value - - $http - .post(element.attr('action'), formData) - .success (data, status, headers, config) -> - response.success = true - response.error = false - - if data.redir? - ga('send', 'event', formName, 'success') - window.location = data.redir - else if data.message? - response.message = data.message - - if data.message.type == "error" - response.success = false - response.error = true - ga('send', 'event', formName, 'failure', data.message) - else - ga('send', 'event', formName, 'success') - - .error (data, status, headers, config) -> - response.success = false - response.error = true - response.message = - text: data.message or "Something went wrong talking to the server :(. Please try again." - type: 'error' - ga('send', 'event', formName, 'failure', data.message) - } - - App.directive "formMessages", () -> - return { - restrict: "E" - template: """ -
- {{form.response.message.text}} -
-
- """ - transclude: true - scope: { - form: "=for" - } - - } \ No newline at end of file diff --git a/services/web/public/coffee/app/directives/equals.coffee b/services/web/public/coffee/app/directives/equals.coffee deleted file mode 100644 index e41a39a322..0000000000 --- a/services/web/public/coffee/app/directives/equals.coffee +++ /dev/null @@ -1,15 +0,0 @@ -define [ - "base" -], (App) -> - - App.directive 'equals', () -> - return { - require: "ngModel", - link: (scope, element, attrs, ngModel) -> - scope.$watch attrs.ngModel, () -> validate() - attrs.$observe 'equals', () -> validate() - - validate = () -> - equal = (attrs.equals == ngModel.$viewValue) - ngModel.$setValidity('areEqual', equal) - } diff --git a/services/web/public/coffee/app/directives/fineUpload.coffee b/services/web/public/coffee/app/directives/fineUpload.coffee deleted file mode 100644 index b7cb5ffc3b..0000000000 --- a/services/web/public/coffee/app/directives/fineUpload.coffee +++ /dev/null @@ -1,67 +0,0 @@ -define [ - "base" - "../../libs/fineuploader" -], (App) -> - App.directive 'fineUpload', ($timeout) -> - return { - scope: { - multiple: "=" - endpoint: "@" - waitingForResponseText: "@" - failedUploadText: "@" - uploadButtonText: "@" - dragAreaText: "@" - hintText: "@" - allowedExtensions: "=" - onCompleteCallback: "=" - onUploadCallback: "=" - params: "=" - } - link: (scope, element, attrs) -> - multiple = scope.multiple or false - endpoint = scope.endpoint - if scope.allowedExtensions? - validation = - allowedExtensions: scope.allowedExtensions - else - validation = {} - text = - waitingForResponse: scope.waitingForResponseText or "Processing..." - failUpload: scope.failedUploadText or "Failed :(" - uploadButton: scope.uploadButtonText or "Upload" - dragAreaText = scope.dragAreaText or "drag here" - hintText = scope.hintText or "" - - onComplete = scope.onCompleteCallback or () -> - onUpload = scope.onUploadCallback or () -> - params = scope.params or {} - params._csrf = window.csrfToken - - new qq.FineUploader - element: element[0] - multiple: multiple - disabledCancelForFormUploads: true - validation: validation - request: - endpoint: endpoint - forceMultipart: true - params: params - paramsInBody: false - callbacks: - onComplete: onComplete - onUpload: onUpload - text: text - template: """ -
-
{dragZoneText}
-
-
{uploadButtonText}
-
- or - #{dragAreaText} - {dropProcessingText} -
#{hintText}
- -
- """ - } \ No newline at end of file diff --git a/services/web/public/coffee/app/directives/focus.coffee b/services/web/public/coffee/app/directives/focus.coffee deleted file mode 100644 index db742f155f..0000000000 --- a/services/web/public/coffee/app/directives/focus.coffee +++ /dev/null @@ -1,67 +0,0 @@ -define [ - "base" -], (App) -> - App.directive "focusWhen", ($timeout) -> - return { - restrict: "A" - link: (scope, element, attr) -> - scope.$watch attr.focusWhen, (value) -> - if value - $timeout -> - element.focus() - } - - App.directive 'focusOn', ($timeout) -> - return { - restrict: 'A' - link: (scope, element, attrs) -> - scope.$on attrs.focusOn, () -> - element.focus() - } - - App.directive "selectWhen", ($timeout) -> - return { - restrict: "A" - link: (scope, element, attr) -> - scope.$watch attr.selectWhen, (value) -> - if value - $timeout -> - element.select() - } - - App.directive 'selectOn', ($timeout) -> - return { - restrict: 'A' - link: (scope, element, attrs) -> - scope.$on attrs.selectOn, () -> - element.select() - } - - App.directive "selectNameWhen", ($timeout) -> - return { - restrict: 'A' - link: (scope, element, attrs) -> - scope.$watch attrs.selectNameWhen, (value) -> - if value - $timeout () -> - selectName(element) - } - - App.directive "selectNameOn", () -> - return { - restrict: 'A' - link: (scope, element, attrs) -> - scope.$on attrs.selectNameOn, () -> - selectName(element) - } - - selectName = (element) -> - # Select up to last '.'. I.e. everything - # except the file extension - element.focus() - name = element.val() - if element[0].setSelectionRange? - selectionEnd = name.lastIndexOf(".") - if selectionEnd == -1 - selectionEnd = name.length - element[0].setSelectionRange(0, selectionEnd) diff --git a/services/web/public/coffee/app/directives/onEnter.coffee b/services/web/public/coffee/app/directives/onEnter.coffee deleted file mode 100644 index 459d444b60..0000000000 --- a/services/web/public/coffee/app/directives/onEnter.coffee +++ /dev/null @@ -1,10 +0,0 @@ -define [ - "base" -], (App) -> - App.directive 'onEnter', () -> - return (scope, element, attrs) -> - element.bind "keydown keypress", (event) -> - if event.which == 13 - scope.$apply () -> - scope.$eval(attrs.onEnter, event: event) - event.preventDefault() \ No newline at end of file diff --git a/services/web/public/coffee/app/directives/selectAll.coffee b/services/web/public/coffee/app/directives/selectAll.coffee deleted file mode 100644 index 79b683c20a..0000000000 --- a/services/web/public/coffee/app/directives/selectAll.coffee +++ /dev/null @@ -1,67 +0,0 @@ -define [ - "base" -], (App) -> - App.directive "selectAllList", () -> - return { - controller: ["$scope", ($scope) -> - # Selecting or deselecting all should apply to all projects - selectAll = () -> - $scope.$broadcast "select-all:select" - - deselectAll = () -> - $scope.$broadcast "select-all:deselect" - - clearSelectAllState = () -> - $scope.$broadcast "select-all:clear" - - return { - clearSelectAllState: clearSelectAllState - selectAll: selectAll - deselectAll: deselectAll - } - ] - link: (scope, element, attrs) -> - - - } - - App.directive "selectAll", () -> - return { - require: "^selectAllList" - link: (scope, element, attrs, selectAllListController) -> - scope.$on "select-all:clear", () -> - element.prop("checked", false) - - element.change () -> - if element.is(":checked") - selectAllListController.selectAll() - else - selectAllListController.deselectAll() - return true - } - - App.directive "selectIndividual", () -> - return { - require: "^selectAllList" - scope: { - ngModel: "=" - } - link: (scope, element, attrs, selectAllListController) -> - ignoreChanges = false - - scope.$watch "ngModel", (value) -> - if value? and !ignoreChanges - selectAllListController.clearSelectAllState() - - scope.$on "select-all:select", () -> - ignoreChanges = true - scope.$apply () -> - scope.ngModel = true - ignoreChanges = false - - scope.$on "select-all:deselect", () -> - ignoreChanges = true - scope.$apply () -> - scope.ngModel = false - ignoreChanges = false - } \ No newline at end of file diff --git a/services/web/public/coffee/app/directives/stopPropagation.coffee b/services/web/public/coffee/app/directives/stopPropagation.coffee deleted file mode 100644 index 8cd3b25c0a..0000000000 --- a/services/web/public/coffee/app/directives/stopPropagation.coffee +++ /dev/null @@ -1,18 +0,0 @@ -define [ - "base" -], (App) -> - App.directive "stopPropagation", ($http) -> - return { - restrict: "A", - link: (scope, element, attrs) -> - element.bind attrs.stopPropagation, (e) -> - e.stopPropagation() - } - - App.directive "preventDefault", ($http) -> - return { - restrict: "A", - link: (scope, element, attrs) -> - element.bind attrs.preventDefault, (e) -> - e.preventDefault() - } diff --git a/services/web/public/coffee/app/filters/formatDate.coffee b/services/web/public/coffee/app/filters/formatDate.coffee deleted file mode 100644 index c8cef08dbd..0000000000 --- a/services/web/public/coffee/app/filters/formatDate.coffee +++ /dev/null @@ -1,18 +0,0 @@ -define [ - "base" -], (App) -> - moment.lang "en", calendar: - lastDay : '[Yesterday]' - sameDay : '[Today]' - nextDay : '[Tomorrow]' - lastWeek : "ddd, Do MMM YY" - nextWeek : "ddd, Do MMM YY" - sameElse : 'ddd, Do MMM YY' - - App.filter "formatDate", () -> - (date, format = "Do MMM YYYY, h:mm a") -> - moment(date).format(format) - - App.filter "relativeDate", () -> - (date) -> - moment(date).calendar() \ No newline at end of file diff --git a/services/web/public/coffee/app/ide.coffee b/services/web/public/coffee/app/ide.coffee deleted file mode 100644 index 393b77219c..0000000000 --- a/services/web/public/coffee/app/ide.coffee +++ /dev/null @@ -1,70 +0,0 @@ -define [ - "base" - "ide/file-tree/FileTreeManager" - "ide/connection/ConnectionManager" - "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" - "ide/directives/layout" - "ide/services/ide" - "directives/focus" - "directives/fineUpload" - "directives/onEnter" - "filters/formatDate" -], ( - App - FileTreeManager - ConnectionManager - 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 - $scope.$originalApply = $scope.$apply - $scope.$apply = (fn = () ->) -> - phase = @$root.$$phase - if (phase == '$apply' || phase == '$digest') - fn() - else - this.$originalApply(fn); - - $scope.state = { - loading: true - load_progress: 40 - } - $scope.ui = { - leftMenuShown: false - view: "editor" - chatOpen: false - } - $scope.user = window.user - $scope.settings = window.userSettings - - $scope.chat = {} - - window._ide = ide - - ide.project_id = $scope.project_id = window.project_id - ide.$scope = $scope - - ide.connectionManager = new ConnectionManager(ide, $scope) - ide.fileTreeManager = new FileTreeManager(ide, $scope) - ide.editorManager = new EditorManager(ide, $scope) - 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"]) \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/binary-files/BinaryFilesManager.coffee b/services/web/public/coffee/app/ide/binary-files/BinaryFilesManager.coffee deleted file mode 100644 index 9b80c0e0dc..0000000000 --- a/services/web/public/coffee/app/ide/binary-files/BinaryFilesManager.coffee +++ /dev/null @@ -1,12 +0,0 @@ -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 \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/binary-files/controllers/BinaryFileController.coffee b/services/web/public/coffee/app/ide/binary-files/controllers/BinaryFileController.coffee deleted file mode 100644 index 5dcabe8000..0000000000 --- a/services/web/public/coffee/app/ide/binary-files/controllers/BinaryFileController.coffee +++ /dev/null @@ -1,7 +0,0 @@ -define [ - "base" -], (App) -> - App.controller "BinaryFileController", ["$scope", ($scope) -> - $scope.extension = (file) -> - return file.name.split(".").pop()?.toLowerCase() - ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/chat/controllers/ChatButtonController.coffee b/services/web/public/coffee/app/ide/chat/controllers/ChatButtonController.coffee deleted file mode 100644 index 74e6d31ee3..0000000000 --- a/services/web/public/coffee/app/ide/chat/controllers/ChatButtonController.coffee +++ /dev/null @@ -1,7 +0,0 @@ -define [ - "base" -], (App) -> - App.controller "ChatButtonController", ["$scope", ($scope) -> - $scope.toggleChat = () -> - $scope.ui.chatOpen = !$scope.ui.chatOpen - ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/chat/controllers/ChatController.coffee b/services/web/public/coffee/app/ide/chat/controllers/ChatController.coffee deleted file mode 100644 index a5c42dc435..0000000000 --- a/services/web/public/coffee/app/ide/chat/controllers/ChatController.coffee +++ /dev/null @@ -1,64 +0,0 @@ -define [ - "base" -], (App) -> - App.controller "ChatController", ["$scope", ($scope) -> - james = { - id: $scope.user.id - first_name: "James" - last_name: "Allen" - email: "james@sharelatex.com" - } - james2 = { - id: "james2-id" - first_name: "James" - last_name: "Allen" - email: "jamesallen0108@gmail.com" - } - henry = { - id: "henry-id" - first_name: "Henry" - last_name: "Oswald" - email: "henry.oswald@sharelatex.com" - } - $scope.chat.messages = [ - { content: "Hello world", timestamp: Date.now() - 2000, user: james2 } - { content: "Hello, this is the new chat", timestamp: Date.now() - 4000, user: james } - { content: "Here are some longer messages to show what it looks like when I say a lot!", timestamp: Date.now() - 20000, user: henry } - { content: "What about some maths? $x^2 = 1$?", timestamp: Date.now() - 22000, user: james2 } - { content: "Nope, that doesn't work yet!", timestamp: Date.now() - 45000, user: henry } - { content: "I'm running out of things to say.", timestamp: Date.now() - 56000, user: henry } - { content: "Yep, me too", timestamp: Date.now() - 100000, user: james } - { content: "Hmm, looks like we've had this conversation backwards", timestamp: Date.now() - 120000, user: james } - { content: "Hello world", timestamp: Date.now() - 202000, user: james2 } - { content: "Hello, this is the new chat", timestamp: Date.now() - 204000, user: james } - { content: "Here are some longer messages to show what it looks like when I say a lot!", timestamp: Date.now() - 2020000, user: henry } - { content: "What about some maths? $x^2 = 1$?", timestamp: Date.now() - 12022000, user: james2 } - { content: "Nope, that doesn't work yet!", timestamp: Date.now() - 12045000, user: henry } - { content: "I'm running out of things to say.", timestamp: Date.now() - 22056000, user: henry } - { content: "Yep, me too", timestamp: Date.now() - 220100000, user: james } - { content: "Hmm, looks like we've had this conversation backwards", timestamp: Date.now() - 520120000, user: james } - ].reverse() - - $scope.$watch "chat.messages", (messages) -> - if messages? - $scope.chat.groupedMessages = groupMessages(messages) - - TIMESTAMP_GROUP_SIZE = 5 * 60 * 1000 # 5 minutes - groupMessages = (messages) -> - previousMessage = null - groupedMessages = [] - for message in messages - shouldGroup = previousMessage? and - previousMessage.user == message.user and - message.timestamp - previousMessage.timestamp < TIMESTAMP_GROUP_SIZE - if shouldGroup - previousMessage.timestamp = message.timestamp - previousMessage.contents.push message.content - else - groupedMessages.push(previousMessage = { - user: message.user - timestamp: message.timestamp - contents: [message.content] - }) - return groupedMessages - ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/chat/controllers/ChatMessageController.coffee b/services/web/public/coffee/app/ide/chat/controllers/ChatMessageController.coffee deleted file mode 100644 index 9e337cbc10..0000000000 --- a/services/web/public/coffee/app/ide/chat/controllers/ChatMessageController.coffee +++ /dev/null @@ -1,12 +0,0 @@ -define [ - "base" -], (App) -> - App.controller "ChatMessageController", ["$scope", "ide", ($scope, ide) -> - $scope.gravatarUrl = (user) -> - email = user.email.trim().toLowerCase() - hash = CryptoJS.MD5(email).toString() - return "//www.gravatar.com/avatar/#{hash}?d=mm&s=50" - - $scope.hue = (user) -> - ide.onlineUsersManager.getHueForUserId(user.id) - ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/chat/index.coffee b/services/web/public/coffee/app/ide/chat/index.coffee deleted file mode 100644 index f60b41d1fb..0000000000 --- a/services/web/public/coffee/app/ide/chat/index.coffee +++ /dev/null @@ -1,5 +0,0 @@ -define [ - "ide/chat/controllers/ChatButtonController" - "ide/chat/controllers/ChatController" - "ide/chat/controllers/ChatMessageController" -], () -> \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/connection/ConnectionManager.coffee b/services/web/public/coffee/app/ide/connection/ConnectionManager.coffee deleted file mode 100644 index 9d5f29983a..0000000000 --- a/services/web/public/coffee/app/ide/connection/ConnectionManager.coffee +++ /dev/null @@ -1,113 +0,0 @@ -define [], () -> - class ConnectionManager - constructor: (@ide, @$scope) -> - @connected = false - - @$scope.connection = - reconnecting: false - # If we need to force everyone to reload the editor - forced_disconnect: false - - @$scope.tryReconnectNow = () => - @tryReconnect() - - @ide.socket = io.connect null, - reconnect: false - "force new connection": true - - @ide.socket.on "connect", () => - @connected = true - @ide.pushEvent("connected") - - @$scope.$apply () => - @$scope.connection.reconnecting = false - if @$scope.state.loading - @$scope.state.load_progress = 80 - - setTimeout(() => - @joinProject() - , 100) - - @ide.socket.on 'disconnect', () => - @connected = false - @ide.pushEvent("disconnected") - - @$scope.$apply () => - @$scope.connection.reconnecting = false - - setTimeout(=> - ga('send', 'event', 'editor-interaction', 'disconnect') - , 2000) - - if !$scope.connection.forced_disconnect - @startAutoReconnectCountdown() - - @ide.socket.on 'forceDisconnect', (message) => - @$scope.$apply () => - @$scope.connection.forced_disconnect = true - @socket.disconnect() - - joinProject: () -> - @ide.socket.emit 'joinProject', { - project_id: @ide.project_id - }, (err, project, permissionsLevel, protocolVersion) => - if @$scope.protocolVersion? and @$scope.protocolVersion != protocolVersion - location.reload(true) - - @$scope.$apply () => - @$scope.protocolVersion = protocolVersion - @$scope.project = project - @$scope.permissionsLevel = permissionsLevel - @$scope.state.load_progress = 100 - @$scope.state.loading = false - @$scope.$emit "project:joined" - - reconnectImmediately: () -> - @disconnect() - @tryReconnect() - - disconnect: () -> - @ide.socket.disconnect() - - startAutoReconnectCountdown: () -> - lastUpdated = @ide.editorManager.lastUpdated() - - twoMinutes = 2 * 60 * 1000 - if lastUpdated? and new Date() - lastUpdated > twoMinutes - # between 1 minute and 3 minutes - countdown = 60 + Math.floor(Math.random() * 120) - else - countdown = 3 + Math.floor(Math.random() * 7) - - @$scope.$apply () => - @$scope.connection.reconnecting = false - @$scope.connection.reconnection_countdown = countdown - - setTimeout(=> - if !@connected - @timeoutId = setTimeout (=> @decreaseCountdown()), 1000 - , 200) - - cancelReconnect: () -> - clearTimeout @timeoutId if @timeoutId? - - decreaseCountdown: () -> - console.log "Decreasing countdown" - return if !@$scope.connection.reconnection_countdown? - @$scope.$apply () => - @$scope.connection.reconnection_countdown-- - - if @$scope.connection.reconnection_countdown <= 0 - @$scope.$apply () => - @tryReconnect() - else - @timeoutId = setTimeout (=> @decreaseCountdown()), 1000 - - tryReconnect: () -> - console.log "Trying reconnect" - @cancelReconnect() - @$scope.connection.reconnecting = true - delete @$scope.connection.reconnection_countdown - @ide.socket.socket.reconnect() - setTimeout (=> @startAutoReconnectCountdown() if !@connected), 2000 - diff --git a/services/web/public/coffee/app/ide/directives/layout.coffee b/services/web/public/coffee/app/ide/directives/layout.coffee deleted file mode 100644 index af746f30a7..0000000000 --- a/services/web/public/coffee/app/ide/directives/layout.coffee +++ /dev/null @@ -1,101 +0,0 @@ -define [ - "base" -], (App) -> - App.directive "layout", ["$parse", ($parse) -> - return { - compile: () -> - pre: (scope, element, attrs) -> - name = attrs.layout - - if attrs.spacingOpen? - spacingOpen = parseInt(attrs.spacingOpen, 10) - else - spacingOpen = 24 - - if attrs.spacingClosed? - spacingClosed = parseInt(attrs.spacingClosed, 10) - else - spacingClosed = 24 - - options = - spacing_open: spacingOpen - spacing_closed: spacingClosed - slidable: false - onresize: () => - onInternalResize() - maskIframesOnResize: scope.$eval( - attrs.maskIframesOnResize or "false" - ) - east: - size: scope.$eval(attrs.initialSizeEast) - initClosed: scope.$eval(attrs.initClosedEast) - west: - size: scope.$eval(attrs.initialSizeEast) - initClosed: scope.$eval(attrs.initClosedWest) - - # Restore previously recorded state - if (state = $.localStorage("layout.#{name}"))? - options.west = state.west - options.east = state.east - - # Someone moved the resizer - onInternalResize = () -> - state = element.layout().readState() - scope.$broadcast "layout:#{name}:resize", state - repositionControls() - resetOpenStates() - - oldWidth = element.width() - # Something resized our parent element - onExternalResize = () -> - console.log "EXTERNAL RESIOZE", name, attrs.resizeProportionally - if attrs.resizeProportionally? and scope.$eval(attrs.resizeProportionally) - eastState = element.layout().readState().east - if eastState? - newInternalWidth = eastState.size / oldWidth * element.width() - oldWidth = element.width() - element.layout().sizePane("east", newInternalWidth) - return - - element.layout().resizeAll() - - element.layout options - element.layout().resizeAll() - - if attrs.resizeOn? - scope.$on attrs.resizeOn, () -> onExternalResize() - - # Save state when exiting - $(window).unload () -> - $.localStorage("layout.#{name}", element.layout().readState()) - - repositionControls = () -> - state = element.layout().readState() - if state.east? - element.find("> .ui-layout-resizer-controls").css({ - position: "absolute" - right: state.east.size - "z-index": 10 - }) - - resetOpenStates = () -> - state = element.layout().readState() - if attrs.openEast? - openEast = $parse(attrs.openEast) - openEast.assign(scope, !state.east.initClosed) - - if attrs.openEast? - scope.$watch attrs.openEast, (value, oldValue) -> - console.log "Open East", value, oldValue - if value? and value != oldValue - if value - element.layout().open("east") - else - element.layout().close("east") - setTimeout () -> - scope.$digest() - , 0 - - resetOpenStates() - } - ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/editor/Document.coffee b/services/web/public/coffee/app/ide/editor/Document.coffee deleted file mode 100644 index 4b7347b520..0000000000 --- a/services/web/public/coffee/app/ide/editor/Document.coffee +++ /dev/null @@ -1,254 +0,0 @@ -define [ - "utils/EventEmitter" - "ide/editor/ShareJsDoc" - "underscore" -], (EventEmitter, ShareJsDoc) -> - class Document extends EventEmitter - @getDocument: (ide, doc_id) -> - @openDocs ||= {} - if !@openDocs[doc_id]? - @openDocs[doc_id] = new Document(ide, doc_id) - return @openDocs[doc_id] - - @hasUnsavedChanges: () -> - for doc_id, doc of (@openDocs or {}) - return true if doc.hasBufferedOps() - return false - - constructor: (@ide, @doc_id) -> - @connected = @ide.socket.socket.connected - @joined = false - @wantToBeJoined = false - @_checkConsistency = _.bind(@_checkConsistency, @) - @inconsistentCount = 0 - @_bindToEditorEvents() - @_bindToSocketEvents() - - attachToAce: (@ace) -> - @doc?.attachToAce(@ace) - editorDoc = @ace.getSession().getDocument() - editorDoc.on "change", @_checkConsistency - - detachFromAce: () -> - @doc?.detachFromAce() - editorDoc = @ace?.getSession().getDocument() - editorDoc?.off "change", @_checkConsistency - - _checkConsistency: () -> - # We've been seeing a lot of errors when I think there shouldn't be - # any, which may be related to this check happening before the change is - # applied. If we use a timeout, hopefully we can reduce this. - setTimeout () => - editorValue = @ace?.getValue() - sharejsValue = @doc?.getSnapshot() - if editorValue != sharejsValue - @inconsistentCount++ - else - @inconsistentCount = 0 - - if @inconsistentCount >= 3 - @_onError new Error("Editor text does not match server text") - , 0 - - getSnapshot: () -> - @doc?.getSnapshot() - - getType: () -> - @doc?.getType() - - getInflightOp: () -> - @doc?.getInflightOp() - - getPendingOp: () -> - @doc?.getPendingOp() - - hasBufferedOps: () -> - @doc?.hasBufferedOps() - - _bindToSocketEvents: () -> - @_onUpdateAppliedHandler = (update) => @_onUpdateApplied(update) - @ide.socket.on "otUpdateApplied", @_onUpdateAppliedHandler - @_onErrorHandler = (error, update) => @_onError(error, update) - @ide.socket.on "otUpdateError", @_onErrorHandler - @_onDisconnectHandler = (error) => @_onDisconnect(error) - @ide.socket.on "disconnect", @_onDisconnectHandler - - _bindToEditorEvents: () -> - onReconnectHandler = (update) => - @_onReconnect(update) - @_unsubscribeReconnectHandler = @ide.$scope.$on "project:joined", onReconnectHandler - - _unBindFromEditorEvents: () -> - @_unsubscribeReconnectHandler() - - _unBindFromSocketEvents: () -> - @ide.socket.removeListener "otUpdateApplied", @_onUpdateAppliedHandler - @ide.socket.removeListener "otUpdateError", @_onUpdateErrorHandler - @ide.socket.removeListener "disconnect", @_onDisconnectHandler - - leaveAndCleanUp: () -> - @leave (error) => - @_cleanUp() - - join: (callback = (error) ->) -> - @wantToBeJoined = true - @_cancelLeave() - if @connected - return @_joinDoc callback - else - @_joinCallbacks ||= [] - @_joinCallbacks.push callback - - leave: (callback = (error) ->) -> - @wantToBeJoined = false - @_cancelJoin() - if (@doc? and @doc.hasBufferedOps()) - @_leaveCallbacks ||= [] - @_leaveCallbacks.push callback - else if !@connected - callback() - else - @_leaveDoc(callback) - - pollSavedStatus: () -> - # returns false if doc has ops waiting to be acknowledged or - # sent that haven't changed since the last time we checked. - # Otherwise returns true. - inflightOp = @getInflightOp() - pendingOp = @getPendingOp() - if !inflightOp? and !pendingOp? - # there's nothing going on - saved = true - else if inflightOp == @oldInflightOp - saved = false - else if pendingOp? - saved = false - else - saved = true - - @oldInflightOp = inflightOp - return saved - - _cancelLeave: () -> - if @_leaveCallbacks? - delete @_leaveCallbacks - - _cancelJoin: () -> - if @_joinCallbacks? - delete @_joinCallbacks - - _onUpdateApplied: (update) -> - @ide.pushEvent "received-update", - doc_id: @doc_id - remote_doc_id: update?.doc - wantToBeJoined: @wantToBeJoined - update: update - - if Math.random() < (@ide.disconnectRate or 0) - console.log "Simulating disconnect" - @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 - - if !@wantToBeJoined - @leave() - - _onDisconnect: () -> - @connected = false - @joined = false - @doc?.updateConnectionState "disconnected" - - _onReconnect: () -> - @ide.pushEvent "reconnected:afterJoinProject" - - @connected = true - if @wantToBeJoined or @doc?.hasBufferedOps() - @_joinDoc (error) => - return @_onError(error) if error? - @doc.updateConnectionState "ok" - @doc.flushPendingOps() - @_callJoinCallbacks() - - _callJoinCallbacks: () -> - for callback in @_joinCallbacks or [] - callback() - delete @_joinCallbacks - - _joinDoc: (callback = (error) ->) -> - if @doc? - @ide.socket.emit 'joinDoc', @doc_id, @doc.getVersion(), (error, docLines, version, updates) => - return callback(error) if error? - @joined = true - @doc.catchUp( updates ) - callback() - else - @ide.socket.emit 'joinDoc', @doc_id, (error, docLines, version) => - return callback(error) if error? - @joined = true - @doc = new ShareJsDoc @doc_id, docLines, version, @ide.socket - @_bindToShareJsDocEvents() - callback() - - _leaveDoc: (callback = (error) ->) -> - @ide.socket.emit 'leaveDoc', @doc_id, (error) => - return callback(error) if error? - @joined = false - for callback in @_leaveCallbacks or [] - callback(error) - delete @_leaveCallbacks - callback(error) - - _cleanUp: () -> - delete Document.openDocs[@doc_id] - @_unBindFromEditorEvents() - @_unBindFromSocketEvents() - - _bindToShareJsDocEvents: () -> - @doc.on "error", (error, meta) => @_onError error, meta - @doc.on "externalUpdate", () => - @ide.pushEvent "externalUpdate", - doc_id: @doc_id - @trigger "externalUpdate" - @doc.on "remoteop", () => - @ide.pushEvent "remoteop", - doc_id: @doc_id - @trigger "remoteop" - @doc.on "op:sent", (op) => - @ide.pushEvent "op:sent", - doc_id: @doc_id - op: op - @trigger "op:sent" - @doc.on "op:acknowledged", (op) => - @ide.pushEvent "op:acknowledged", - doc_id: @doc_id - op: op - @trigger "op:acknowledged" - @doc.on "op:timeout", (op) => - @ide.pushEvent "op:timeout", - doc_id: @doc_id - op: op - @trigger "op:timeout" - ga?('send', 'event', 'error', "op timeout", "Op was now acknowledged - #{@ide.socket.socket.transport.name}" ) - @ide.connectionManager.reconnectImmediately() - @doc.on "flush", (inflightOp, pendingOp, version) => - @ide.pushEvent "flush", - doc_id: @doc_id, - inflightOp: inflightOp, - pendingOp: pendingOp - v: version - - _onError: (error, meta = {}) -> - console.error "ShareJS error", error, meta - ga?('send', 'event', 'error', "shareJsError", "#{error.message} - #{@ide.socket.socket.transport.name}" ) - @ide.socket.disconnect() - meta.doc_id = @doc_id - @ide.reportError(error, meta) - @doc?.clearInflightAndPendingOps() - @_cleanUp() - @trigger "error", error diff --git a/services/web/public/coffee/app/ide/editor/EditorManager.coffee b/services/web/public/coffee/app/ide/editor/EditorManager.coffee deleted file mode 100644 index 4d0f965d93..0000000000 --- a/services/web/public/coffee/app/ide/editor/EditorManager.coffee +++ /dev/null @@ -1,103 +0,0 @@ -define [ - "ide/editor/Document" - "ide/editor/directives/aceEditor" - "ide/editor/controllers/SavingNotificationController" -], (Document) -> - class EditorManager - constructor: (@ide, @$scope) -> - @$scope.editor = { - sharejs_doc: null - last_updated: null - open_doc_id: null - opening: true - cursorPosition: {} - gotoLine: null - } - - @$scope.$on "entity:selected", (event, entity) => - if (@$scope.ui.view != "track-changes" and entity.type == "doc") - @openDoc(entity) - - initialized = false - @$scope.$on "file-tree:initialized", () => - if !initialized - initialized = true - @autoOpenDoc() - - autoOpenDoc: () -> - open_doc_id = - $.localStorage("doc.open_id.#{@$scope.project_id}") or - @$scope.project.rootDoc_id - return if !open_doc_id? - doc = @ide.fileTreeManager.findEntityById(open_doc_id) - return if !doc? - @openDoc(doc) - - openDoc: (doc, options = {}) -> - @$scope.ui.view = "editor" - - done = () => - if options.gotoLine? - @$scope.editor.gotoLine = options.gotoLine - - if doc.id == @$scope.editor.open_doc_id and !options.forceReopen - @$scope.$apply () => - done() - return - - @$scope.editor.open_doc_id = doc.id - - $.localStorage "doc.open_id.#{@$scope.project_id}", doc.id - @ide.fileTreeManager.selectEntity(doc) - - @$scope.editor.opening = true - @_openNewDocument doc, (error, sharejs_doc) => - if error? - @ide.showGenericServerErrorMessage() - return - - @$scope.$broadcast "doc:opened" - - @$scope.$apply () => - @$scope.editor.opening = false - @$scope.editor.sharejs_doc = sharejs_doc - done() - - _openNewDocument: (doc, callback = (error, sharejs_doc) ->) -> - current_sharejs_doc = @$scope.editor.sharejs_doc - if current_sharejs_doc? - current_sharejs_doc.leaveAndCleanUp() - @_unbindFromDocumentEvents(current_sharejs_doc) - - new_sharejs_doc = Document.getDocument @ide, doc.id - - new_sharejs_doc.join (error) => - return callback(error) if error? - @_bindToDocumentEvents(doc, new_sharejs_doc) - callback null, new_sharejs_doc - - _bindToDocumentEvents: (doc, sharejs_doc) -> - sharejs_doc.on "error", (error) => - @openDoc(doc, forceReopen: true) - @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", () => - @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() - - lastUpdated: () -> - @$scope.editor.last_updated - - getCurrentDocValue: () -> - @$scope.editor.sharejs_doc?.getSnapshot() - - getCurrentDocId: () -> - @$scope.editor.open_doc_id diff --git a/services/web/public/coffee/app/ide/editor/ShareJsDoc.coffee b/services/web/public/coffee/app/ide/editor/ShareJsDoc.coffee deleted file mode 100644 index fe61580041..0000000000 --- a/services/web/public/coffee/app/ide/editor/ShareJsDoc.coffee +++ /dev/null @@ -1,123 +0,0 @@ -define [ - "utils/EventEmitter" - "../../../libs/sharejs" -], (EventEmitter, ShareJs) -> - class ShareJsDoc extends EventEmitter - constructor: (@doc_id, docLines, version, @socket) -> - # Dencode any binary bits of data - # See http://ecmanaut.blogspot.co.uk/2006/07/encoding-decoding-utf8-in-javascript.html - @type = "text" - docLines = for line in docLines - if line.text? - @type = "json" - line.text = decodeURIComponent(escape(line.text)) - else - @type = "text" - line = decodeURIComponent(escape(line)) - line - - if @type == "text" - snapshot = docLines.join("\n") - else if @type == "json" - snapshot = { lines: docLines } - else - throw new Error("Unknown type: #{@type}") - - @connection = { - send: (update) => - @_startInflightOpTimeout(update) - @socket.emit "applyOtUpdate", @doc_id, update - state: "ok" - id: @socket.socket.sessionid - } - - @_doc = new ShareJs.Doc @connection, @doc_id, - type: @type - @_doc.on "change", () => - @trigger "change" - @_doc.on "acknowledge", () => - @trigger "acknowledge" - @_doc.on "remoteop", () => - @trigger "remoteop" - - @_bindToDocChanges(@_doc) - - @processUpdateFromServer - open: true - v: version - snapshot: snapshot - - submitOp: (args...) -> @_doc.submitOp(args...) - - processUpdateFromServer: (message) -> - try - @_doc._onMessage message - catch error - # Version mismatches are thrown as errors - @_handleError(error) - - if message?.meta?.type == "external" - @trigger "externalUpdate" - - catchUp: (updates) -> - for update, i in updates - update.v = @_doc.version - update.doc = @doc_id - @processUpdateFromServer(update) - - getSnapshot: () -> @_doc.snapshot - getVersion: () -> @_doc.version - getType: () -> @type - - clearInflightAndPendingOps: () -> - @_doc.inflightOp = null - @_doc.inflightCallbacks = [] - @_doc.pendingOp = null - @_doc.pendingCallbacks = [] - - flushPendingOps: () -> - # This will flush any ops that are pending. - # If there is an inflight op it will do nothing. - @_doc.flush() - - updateConnectionState: (state) -> - @connection.state = state - @connection.id = @socket.socket.sessionid - @_doc.autoOpen = false - @_doc._connectionStateChanged(state) - - hasBufferedOps: () -> - @_doc.inflightOp? or @_doc.pendingOp? - - getInflightOp: () -> @_doc.inflightOp - getPendingOp: () -> @_doc.pendingOp - - attachToAce: (ace) -> @_doc.attach_ace(ace) - detachFromAce: () -> @_doc.detach_ace?() - - INFLIGHT_OP_TIMEOUT: 10000 - _startInflightOpTimeout: (update) -> - meta = - v: update.v - op_sent_at: new Date() - timer = setTimeout () => - @trigger "op:timeout", update - , @INFLIGHT_OP_TIMEOUT - @_doc.inflightCallbacks.push () => - clearTimeout timer - - _handleError: (error, meta = {}) -> - @trigger "error", error, meta - - _bindToDocChanges: (doc) -> - submitOp = doc.submitOp - doc.submitOp = (args...) => - @trigger "op:sent", args... - doc.pendingCallbacks.push () => - @trigger "op:acknowledged", args... - submitOp.apply(doc, args) - - flush = doc.flush - doc.flush = (args...) => - @trigger "flush", doc.inflightOp, doc.pendingOp, doc.version - flush.apply(doc, args) diff --git a/services/web/public/coffee/app/ide/editor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/app/ide/editor/auto-complete/AutoCompleteManager.coffee deleted file mode 100644 index 20f451e040..0000000000 --- a/services/web/public/coffee/app/ide/editor/auto-complete/AutoCompleteManager.coffee +++ /dev/null @@ -1,95 +0,0 @@ -define [ - "ide/editor/auto-complete/SuggestionManager" - "ide/editor/auto-complete/Snippets" - "ace/autocomplete/util" - "ace/autocomplete" - "ace/range" - "ace/ext/language_tools" -], (SuggestionManager, Snippets, Util, AutoComplete) -> - Range = require("ace/range").Range - Autocomplete = AutoComplete.Autocomplete - - Util.retrievePrecedingIdentifier = (text, pos, regex) -> - currentLineOffset = 0 - for i in [(pos-1)..0] - if text[i] == "\n" - currentLineOffset = i + 1 - break - currentLine = text.slice(currentLineOffset, pos) - fragment = getLastCommandFragment(currentLine) or "" - return fragment - - getLastCommandFragment = (lineUpToCursor) -> - if m = lineUpToCursor.match(/(\\[^\\ ]+)$/) - return m[1] - else - return null - - class AutoCompleteManager - constructor: (@$scope, @editor) -> - @suggestionManager = new SuggestionManager() - - if !Autocomplete::_insertMatch? - # Only override this once since it's global but we may create multiple - # autocomplete handlers - Autocomplete::_insertMatch = Autocomplete::insertMatch - Autocomplete::insertMatch = (data) -> - pos = editor.getCursorPosition() - range = new Range(pos.row, pos.column, pos.row, pos.column + 1) - nextChar = editor.session.getTextRange(range) - - console.log "INSERT MATCH", this - - # If we are in \begin{it|}, then we need to remove the trailing } - # since it will be adding in with the autocomplete of \begin{item}... - if this.completions.filterText.match(/^\\begin\{/) and nextChar == "}" - editor.session.remove(range) - - Autocomplete::_insertMatch.call this, data - - @$scope.$watch "autoComplete", (autocomplete) => - console.log "autocomplete change", autocomplete - if autocomplete - @enable() - else - @disable() - - - @editor.on "changeSession", (e) => - @bindToSession(e.session) - - enable: () -> - @editor.setOptions({ - enableBasicAutocompletion: true, - enableSnippets: true - }) - - SnippetCompleter = - getCompletions: (editor, session, pos, prefix, callback) -> - callback null, Snippets - @editor.completers = [@suggestionManager, SnippetCompleter] - - disable: () -> - @editor.setOptions({ - enableBasicAutocompletion: false, - enableSnippets: false - }) - - bindToSession: (@aceSession) -> - @aceSession.on "change", (change) => @onChange(change) - - onChange: (change) -> - cursorPosition = @editor.getCursorPosition() - end = change.data.range.end - # Check that this change was made by us, not a collaborator - # (Cursor is still one place behind) - if end.row == cursorPosition.row and end.column == cursorPosition.column + 1 - if change.data.action == "insertText" - range = new Range(end.row, 0, end.row, end.column) - lineUpToCursor = @aceSession.getTextRange(range) - commandFragment = getLastCommandFragment(lineUpToCursor) - - if commandFragment? and commandFragment.length > 2 - setTimeout () => - @editor.execCommand("startAutocomplete") - , 0 diff --git a/services/web/public/coffee/app/ide/editor/auto-complete/Snippets.coffee b/services/web/public/coffee/app/ide/editor/auto-complete/Snippets.coffee deleted file mode 100644 index 53e0f57fa1..0000000000 --- a/services/web/public/coffee/app/ide/editor/auto-complete/Snippets.coffee +++ /dev/null @@ -1,100 +0,0 @@ -define () -> - environments = [ - "abstract", - "align", "align*", - "equation", "equation*", - "gather", "gather*", - "multline", "multline*", - "split", - "verbatim" - ] - - snippets = for env in environments - { - caption: "\\begin{#{env}}..." - snippet: """ - \\begin{#{env}} - $1 - \\end{#{env}} - """ - meta: "env" - } - - snippets = snippets.concat [{ - caption: "\\begin{array}..." - snippet: """ - \\begin{array}{${1:cc}} - $2 & $3 \\\\\\\\ - $4 & $5 - \\end{array} - """ - meta: "env" - }, { - caption: "\\begin{figure}..." - snippet: """ - \\begin{figure} - \\centering - \\includegraphics{$1} - \\caption{${2:Caption}} - \\label{${3:fig:my_label}} - \\end{figure} - """ - meta: "env" - }, { - caption: "\\begin{tabular}..." - snippet: """ - \\begin{tabular}{${1:c|c}} - $2 & $3 \\\\\\\\ - $4 & $5 - \\end{tabular} - """ - meta: "env" - }, { - caption: "\\begin{table}..." - snippet: """ - \\begin{table}[$1] - \\centering - \\begin{tabular}{${2:c|c}} - $3 & $4 \\\\\\\\ - $5 & $6 - \\end{tabular} - \\caption{${7:Caption}} - \\label{${8:tab:my_label}} - \\end{table} - """ - meta: "env" - }, { - caption: "\\begin{list}..." - snippet: """ - \\begin{list} - \\item $1 - \\end{list} - """ - meta: "env" - }, { - caption: "\\begin{enumerate}..." - snippet: """ - \\begin{enumerate} - \\item $1 - \\end{enumerate} - """ - meta: "env" - }, { - caption: "\\begin{itemize}..." - snippet: """ - \\begin{itemize} - \\item $1 - \\end{itemize} - """ - meta: "env" - }, { - caption: "\\begin{frame}..." - snippet: """ - \\begin{frame}{${1:Frame Title}} - $2 - \\end{frame} - """ - meta: "env" - }] - - return snippets \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/editor/auto-complete/SuggestionManager.coffee b/services/web/public/coffee/app/ide/editor/auto-complete/SuggestionManager.coffee deleted file mode 100644 index 559a2c5981..0000000000 --- a/services/web/public/coffee/app/ide/editor/auto-complete/SuggestionManager.coffee +++ /dev/null @@ -1,126 +0,0 @@ -define [], () -> - class Parser - constructor: (@doc) -> - - parse: () -> - commands = [] - seen = {} - while command = @nextCommand() - docState = @doc - - optionalArgs = 0 - while @consumeArgument("[", "]") - optionalArgs++ - - args = 0 - while @consumeArgument("{", "}") - args++ - - commandHash = "#{command}\\#{optionalArgs}\\#{args}" - if !seen[commandHash]? - seen[commandHash] = true - commands.push [command, optionalArgs, args] - - # Reset to before argument to handle nested commands - @doc = docState - - return commands - - # Ignore single letter commands since auto complete is moot then. - commandRegex: /\\([a-zA-Z][a-zA-Z]+)/ - - nextCommand: () -> - i = @doc.search(@commandRegex) - if i == -1 - return false - else - match = @doc.match(@commandRegex)[1] - @doc = @doc.substr(i + match.length + 1) - return match - - consumeWhitespace: () -> - match = @doc.match(/^[ \t\n]*/m)[0] - @doc = @doc.substr(match.length) - - consumeArgument: (openingBracket, closingBracket) -> - @consumeWhitespace() - - if @doc[0] == openingBracket - i = 1 - bracketParity = 1 - while bracketParity > 0 and i < @doc.length - if @doc[i] == openingBracket - bracketParity++ - else if @doc[i] == closingBracket - bracketParity-- - i++ - - if bracketParity == 0 - @doc = @doc.substr(i) - return true - else - return false - else - return false - - class SuggestionManager - getCompletions: (editor, session, pos, prefix, callback) -> - doc = session.getValue() - parser = new Parser(doc) - commands = parser.parse() - - completions = [] - for command in commands - caption = "\\#{command[0]}" - snippet = caption - i = 1 - _.times command[1], () -> - snippet += "[${#{i}}]" - caption += "[]" - i++ - _.times command[2], () -> - snippet += "{${#{i}}}" - caption += "{}" - i++ - unless caption == prefix - completions.push { - caption: caption - snippet: snippet - meta: "cmd" - } - - callback null, completions - - loadCommandsFromDoc: (doc) -> - parser = new Parser(doc) - @commands = parser.parse() - - getSuggestions: (commandFragment) -> - matchingCommands = _.filter @commands, (command) -> - command[0].slice(0, commandFragment.length) == commandFragment - - return _.map matchingCommands, (command) -> - base = "\\" + commandFragment - - args = "" - _.times command[1], () -> args = args + "[]" - _.times command[2], () -> args = args + "{}" - completionBase = command[0].slice(commandFragment.length) - - squareArgsNo = command[1] - curlyArgsNo = command[2] - totalArgs = squareArgsNo + curlyArgsNo - if totalArgs == 0 - completionBeforeCursor = completionBase - completionAfterCurspr = "" - else - completionBeforeCursor = completionBase + args[0] - completionAfterCursor = args.slice(1) - - return { - base: base, - completion: completionBase + args, - completionBeforeCursor: completionBeforeCursor - completionAfterCursor: completionAfterCursor - } - diff --git a/services/web/public/coffee/app/ide/editor/controllers/SavingNotificationController.coffee b/services/web/public/coffee/app/ide/editor/controllers/SavingNotificationController.coffee deleted file mode 100644 index edefe07ea9..0000000000 --- a/services/web/public/coffee/app/ide/editor/controllers/SavingNotificationController.coffee +++ /dev/null @@ -1,33 +0,0 @@ -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." - ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/editor/cursor-position/CursorPositionManager.coffee b/services/web/public/coffee/app/ide/editor/cursor-position/CursorPositionManager.coffee deleted file mode 100644 index 27f7045d77..0000000000 --- a/services/web/public/coffee/app/ide/editor/cursor-position/CursorPositionManager.coffee +++ /dev/null @@ -1,52 +0,0 @@ -define [], () -> - class CursorPositionManager - constructor: (@$scope, @editor, @element) -> - - @editor.on "changeSession", (e) => - e.session.on "changeScrollTop", (e) => - @onScrollTopChange(e) - - e.session.selection.on 'changeCursor', (e) => - @onCursorChange(e) - - @gotoStoredPosition() - - @editor.on "changeSelection", () => - cursor = @editor.getCursorPosition() - @$scope.$apply () => - if @$scope.cursorPosition? - @$scope.cursorPosition = cursor - - @$scope.$watch "gotoLine", (value) => - console.log "Going to line", value - if value? - setTimeout () => - @gotoLine(value) - @$scope.$apply () => - @$scope.gotoLine = null - , 0 - - onScrollTopChange: (event) -> - if !@ignoreCursorPositionChanges and doc_id = @$scope.sharejsDoc?.doc_id - docPosition = $.localStorage("doc.position.#{doc_id}") || {} - docPosition.scrollTop = @editor.getSession().getScrollTop() - $.localStorage("doc.position.#{doc_id}", docPosition) - - onCursorChange: (event) -> - if !@ignoreCursorPositionChanges and doc_id = @$scope.sharejsDoc?.doc_id - docPosition = $.localStorage("doc.position.#{doc_id}") || {} - docPosition.cursorPosition = @editor.getCursorPosition() - $.localStorage("doc.position.#{doc_id}", docPosition) - - gotoStoredPosition: () -> - doc_id = @$scope.sharejsDoc?.doc_id - return if !doc_id? - pos = $.localStorage("doc.position.#{doc_id}") || {} - @ignoreCursorPositionChanges = true - @editor.moveCursorToPosition(pos.cursorPosition or {row: 0, column: 0}) - @editor.getSession().setScrollTop(pos.scrollTop or 0) - delete @ignoreCursorPositionChanges - - gotoLine: (line) -> - @editor.gotoLine(line) - @editor.focus() \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/app/ide/editor/directives/aceEditor.coffee deleted file mode 100644 index 423f9a1307..0000000000 --- a/services/web/public/coffee/app/ide/editor/directives/aceEditor.coffee +++ /dev/null @@ -1,187 +0,0 @@ -define [ - "base" - "ace/ace" - "ide/editor/undo/UndoManager" - "ide/editor/auto-complete/AutoCompleteManager" - "ide/editor/spell-check/SpellCheckManager" - "ide/editor/highlights/HighlightsManager" - "ide/editor/cursor-position/CursorPositionManager" - "ace/keyboard/vim" - "ace/keyboard/emacs" - "ace/mode/latex" - "ace/edit_session" -], (App, Ace, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager) -> - LatexMode = require("ace/mode/latex").Mode - EditSession = require('ace/edit_session').EditSession - - App.directive "aceEditor", ["$timeout", ($timeout) -> - return { - scope: { - theme: "=" - showPrintMargin: "=" - keybindings: "=" - fontSize: "=" - autoComplete: "=" - sharejsDoc: "=" - lastUpdated: "=" - spellCheckLanguage: "=" - cursorPosition: "=" - highlights: "=" - text: "=" - readOnly: "=" - gotoLine: "=" - annotations: "=" - } - link: (scope, element, attrs) -> - # 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); - - editor = Ace.edit(element.find(".ace-editor-body")[0]) - window.editors ||= [] - window.editors.push editor - - autoCompleteManager = new AutoCompleteManager(scope, editor, element) - spellCheckManager = new SpellCheckManager(scope, editor, element) - undoManager = new UndoManager(scope, editor, element) - highlightsManager = new HighlightsManager(scope, editor, element) - cursorPositionManager = new CursorPositionManager(scope, editor, element) - - # Prevert Ctrl|Cmd-S from triggering save dialog - editor.commands.addCommand - name: "save", - bindKey: win: "Ctrl-S", mac: "Command-S" - exec: () -> - readOnly: true - editor.commands.removeCommand "transposeletters" - editor.commands.removeCommand "showSettingsMenu" - editor.commands.removeCommand "foldall" - - if attrs.resizeOn? - for event in attrs.resizeOn.split(",") - scope.$on event, () -> - editor.resize() - - scope.$watch "theme", (value) -> - editor.setTheme("ace/theme/#{value}") - - scope.$watch "showPrintMargin", (value) -> - editor.setShowPrintMargin(value) - - scope.$watch "keybindings", (value) -> - Vim = require("ace/keyboard/vim").handler - Emacs = require("ace/keyboard/emacs").handler - keybindings = vim: Vim, emacs: Emacs - editor.setKeyboardHandler(keybindings[value]) - - scope.$watch "fontSize", (value) -> - element.find(".ace_editor, .ace_content").css({ - "font-size": value + "px" - }) - - scope.$watch "sharejsDoc", (sharejs_doc, old_sharejs_doc) -> - if old_sharejs_doc? - detachFromAce(old_sharejs_doc) - - if sharejs_doc? - attachToAce(sharejs_doc) - - scope.$watch "text", (text) -> - if text? - editor.setValue(text, -1) - session = editor.getSession() - session.setUseWrapMode(true) - session.setMode(new LatexMode()) - - scope.$watch "annotations", (annotations) -> - console.log "SETTING ANNOTATIONS", annotations - if annotations? - session = editor.getSession() - session.setAnnotations annotations - - scope.$watch "readOnly", (value) -> - editor.setReadOnly !!value - - resetSession = () -> - session = editor.getSession() - session.setUseWrapMode(true) - session.setMode(new LatexMode()) - session.setAnnotations scope.annotations - - attachToAce = (sharejs_doc) -> - lines = sharejs_doc.getSnapshot().split("\n") - editor.setSession(new EditSession(lines)) - resetSession() - session = editor.getSession() - - doc = session.getDocument() - doc.on "change", () -> - scope.$apply () -> - scope.lastUpdated = new Date() - - sharejs_doc.on "remoteop.recordForUndo", () => - undoManager.nextUpdateIsRemote = true - - sharejs_doc.attachToAce(editor) - - editor.focus() - - detachFromAce = (sharejs_doc) -> - sharejs_doc.detachFromAce() - sharejs_doc.off "remoteop.recordForUndo" - - template: """ -
-
- Watch out! - We had to undo some of your collaborators changes before we could undo yours. - Dismiss -
-
- -
- {{ annotationLabel.text }} -
-
- """ - } - ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/editor/highlights/HighlightsManager.coffee b/services/web/public/coffee/app/ide/editor/highlights/HighlightsManager.coffee deleted file mode 100644 index a8b7699407..0000000000 --- a/services/web/public/coffee/app/ide/editor/highlights/HighlightsManager.coffee +++ /dev/null @@ -1,228 +0,0 @@ -define [ - "ace/range" -], () -> - Range = require("ace/range").Range - - class HighlightsManager - constructor: (@$scope, @editor, @element) -> - @markerIds = [] - @labels = [] - - @$scope.annotationLabel = { - show: false - right: "auto" - left: "auto" - top: "auto" - bottom: "auto" - backgroundColor: "black" - text: "" - } - - @$scope.$watch "highlights", (value) => - @redrawAnnotations() - - @$scope.$watch "theme", (value) => - @redrawAnnotations() - - @editor.on "mousemove", (e) => - position = @editor.renderer.screenToTextCoordinates(e.clientX, e.clientY) - e.position = position - @showAnnotationLabels(position) - - @editor.on "changeSession", () => - @redrawAnnotations() - - redrawAnnotations: () -> - @_clearMarkers() - @_clearLabels() - - for annotation in @$scope.highlights or [] - do (annotation) => - colorScheme = @_getColorScheme(annotation.hue) - if annotation.cursor? - console.log "DRAWING CURSOR", annotation - @labels.push { - text: annotation.label - range: new Range( - annotation.cursor.row, annotation.cursor.column, - annotation.cursor.row, annotation.cursor.column + 1 - ) - colorScheme: colorScheme - snapToStartOfRange: true - } - @_drawCursor(annotation, colorScheme) - else if annotation.highlight? - @labels.push { - text: annotation.label - range: new Range( - annotation.highlight.start.row, annotation.highlight.start.column, - annotation.highlight.end.row, annotation.highlight.end.column - ) - colorScheme: colorScheme - } - @_drawHighlight(annotation, colorScheme) - else if annotation.strikeThrough? - @labels.push { - text: annotation.label - range: new Range( - annotation.strikeThrough.start.row, annotation.strikeThrough.start.column, - annotation.strikeThrough.end.row, annotation.strikeThrough.end.column - ) - colorScheme: colorScheme - } - @_drawStrikeThrough(annotation, colorScheme) - - showAnnotationLabels: (position) -> - labelToShow = null - for label in @labels or [] - if label.range.contains(position.row, position.column) - labelToShow = label - - if !labelToShow? - @$scope.$apply () => - @$scope.annotationLabel.show = false - else - $ace = $(@editor.renderer.container).find(".ace_scroller") - # Move the label into the Ace content area so that offsets and positions are easy to calculate. - $ace.append(@element.find(".annotation-label")) - - if labelToShow.snapToStartOfRange - coords = @editor.renderer.textToScreenCoordinates(labelToShow.range.start.row, labelToShow.range.start.column) - else - coords = @editor.renderer.textToScreenCoordinates(position.row, position.column) - - offset = $ace.offset() - height = $ace.height() - coords.pageX = coords.pageX - offset.left - coords.pageY = coords.pageY - offset.top - - if coords.pageY > @editor.renderer.lineHeight * 2 - top = "auto" - bottom = height - coords.pageY - else - top = coords.pageY + @editor.renderer.lineHeight - bottom = "auto" - - # Apply this first that the label has the correct width when calculating below - @$scope.$apply () => - @$scope.annotationLabel.text = labelToShow.text - @$scope.annotationLabel.show = true - - $label = @element.find(".annotation-label") - console.log "pageX", coords.pageX, "label", $label.outerWidth(), "ace", $ace.width() - - if coords.pageX + $label.outerWidth() < $ace.width() - left = coords.pageX - right = "auto" - else - right = 0 - left = "auto" - - @$scope.$apply () => - @$scope.annotationLabel = { - show: true - left: left - right: right - bottom: bottom - top: top - backgroundColor: labelToShow.colorScheme.labelBackgroundColor - text: labelToShow.text - } - - _clearMarkers: () -> - for marker_id in @markerIds - @editor.getSession().removeMarker(marker_id) - @markerIds = [] - - _clearLabels: () -> - @labels = [] - - _drawCursor: (annotation, colorScheme) -> - @markerIds.push @editor.getSession().addMarker new Range( - annotation.cursor.row, annotation.cursor.column, - annotation.cursor.row, annotation.cursor.column + 1 - ), "annotation remote-cursor", (html, range, left, top, config) -> - div = """ -
-
-
- """ - html.push div - , true - - _drawHighlight: (annotation, colorScheme) -> - @_addMarkerWithCustomStyle( - new Range( - annotation.highlight.start.row, annotation.highlight.start.column, - annotation.highlight.end.row, annotation.highlight.end.column + 1 - ), - "annotation highlight", - false, - "background-color: #{colorScheme.highlightBackgroundColor}" - ) - - _drawStrikeThrough: (annotation, colorScheme) -> - lineHeight = @editor.renderer.lineHeight - @_addMarkerWithCustomStyle( - new Range( - annotation.strikeThrough.start.row, annotation.strikeThrough.start.column, - annotation.strikeThrough.end.row, annotation.strikeThrough.end.column + 1 - ), - "annotation strike-through-background", - false, - "background-color: #{colorScheme.strikeThroughBackgroundColor}" - ) - @_addMarkerWithCustomStyle( - new Range( - annotation.strikeThrough.start.row, annotation.strikeThrough.start.column, - annotation.strikeThrough.end.row, annotation.strikeThrough.end.column + 1 - ), - "annotation strike-through-foreground", - true, - """ - height: #{Math.round(lineHeight/2) + 2}px; - border-bottom: 2px solid #{colorScheme.strikeThroughForegroundColor}; - """ - ) - - _addMarkerWithCustomStyle: (range, klass, foreground, style) -> - if foreground? - markerLayer = @editor.renderer.$markerBack - else - markerLayer = @editor.renderer.$markerFront - - @markerIds.push @editor.getSession().addMarker range, klass, (html, range, left, top, config) -> - if range.isMultiLine() - markerLayer.drawTextMarker(html, range, klass, config, style) - else - markerLayer.drawSingleLineMarker(html, range, "#{klass} ace_start", config, 0, style) - , foreground - - _getColorScheme: (hue) -> - if @_isDarkTheme() - return { - 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}, 70%, 50%)" - labelBackgroundColor: "hsl(#{hue}, 70%, 50%)" - highlightBackgroundColor: "hsl(#{hue}, 70%, 85%);" - strikeThroughBackgroundColor: "hsl(#{hue}, 70%, 95%);" - strikeThroughForegroundColor: "hsl(#{hue}, 70%, 40%);" - } - - _isDarkTheme: () -> - rgb = @element.find(".ace_editor").css("background-color"); - [m, r, g, b] = rgb.match(/rgb\(([0-9]+), ([0-9]+), ([0-9]+)\)/) - r = parseInt(r, 10) - g = parseInt(g, 10) - b = parseInt(b, 10) - return r + g + b < 3 * 128 \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/editor/spell-check/HighlightedWordManager.coffee b/services/web/public/coffee/app/ide/editor/spell-check/HighlightedWordManager.coffee deleted file mode 100644 index acd4c512e9..0000000000 --- a/services/web/public/coffee/app/ide/editor/spell-check/HighlightedWordManager.coffee +++ /dev/null @@ -1,150 +0,0 @@ -define [ - "ace/range" -], () -> - Range = require("ace/range").Range - - class Highlight - constructor: (options) -> - @row = options.row - @column = options.column - @word = options.word - @suggestions = options.suggestions - - class HighlightedWordManager - constructor: (@editor) -> - @highlights = rows: [] - - addHighlight: (highlight) -> - unless highlight instanceof Highlight - highlight = new Highlight(highlight) - range = new Range( - highlight.row, highlight.column, - highlight.row, highlight.column + highlight.word.length - ) - highlight.markerId = @editor.getSession().addMarker range, "spelling-highlight" - @highlights.rows[highlight.row] ||= [] - @highlights.rows[highlight.row].push highlight - - removeHighlight: (highlight) -> - @editor.getSession().removeMarker(highlight.markerId) - for h, i in @highlights.rows[highlight.row] - if h == highlight - @highlights.rows[highlight.row].splice(i, 1) - - removeWord: (word) -> - toRemove = [] - for row in @highlights.rows - for highlight in (row || []) - if highlight.word == word - toRemove.push(highlight) - for highlight in toRemove - @removeHighlight highlight - - moveHighlight: (highlight, position) -> - @removeHighlight highlight - highlight.row = position.row - highlight.column = position.column - @addHighlight highlight - - clearRows: (from, to) -> - from ||= 0 - to ||= @highlights.rows.length - 1 - for row in @highlights.rows.slice(from, to + 1) - for highlight in (row || []).slice(0) - @removeHighlight highlight - - insertRows: (offset, number) -> - # rows are inserted after offset. i.e. offset row is not modified - affectedHighlights = [] - for row in @highlights.rows.slice(offset) - affectedHighlights.push(highlight) for highlight in (row || []) - for highlight in affectedHighlights - @moveHighlight highlight, - row: highlight.row + number - column: highlight.column - - removeRows: (offset, number) -> - # offset is the first row to delete - affectedHighlights = [] - for row in @highlights.rows.slice(offset) - affectedHighlights.push(highlight) for highlight in (row || []) - for highlight in affectedHighlights - if highlight.row >= offset + number - @moveHighlight highlight, - row: highlight.row - number - column: highlight.column - else - @removeHighlight highlight - - findHighlightWithinRange: (range) -> - rows = @highlights.rows.slice(range.start.row, range.end.row + 1) - for row in rows - for highlight in (row || []) - if @_doesHighlightOverlapRange(highlight, range.start, range.end) - return highlight - return null - - applyChange: (change) -> - start = change.range.start - end = change.range.end - if change.action == "insertText" - if start.row != end.row - rowsAdded = end.row - start.row - @insertRows start.row + 1, rowsAdded - # make a copy since we're going to modify in place - oldHighlights = (@highlights.rows[start.row] || []).slice(0) - for highlight in oldHighlights - if highlight.column > start.column - # insertion was fully before this highlight - @moveHighlight highlight, - row: end.row - column: highlight.column + (end.column - start.column) - else if highlight.column + highlight.word.length >= start.column - # insertion was inside this highlight - @removeHighlight highlight - - else if change.action == "insertLines" - @insertRows start.row, change.lines.length - - else if change.action == "removeText" - if start.row == end.row - oldHighlights = (@highlights.rows[start.row] || []).slice(0) - else - rowsRemoved = end.row - start.row - oldHighlights = - (@highlights.rows[start.row] || []).concat( - (@highlights.rows[end.row] || []) - ) - @removeRows start.row + 1, rowsRemoved - - for highlight in oldHighlights - if @_doesHighlightOverlapRange highlight, start, end - @removeHighlight highlight - else if @_isHighlightAfterRange highlight, start, end - @moveHighlight highlight, - row: start.row - column: highlight.column - (end.column - start.column) - - else if change.action == "removeLines" - @removeRows start.row, change.lines.length - - _doesHighlightOverlapRange: (highlight, start, end) -> - highlightIsAllBeforeRange = - highlight.row < start.row or - (highlight.row == start.row and highlight.column + highlight.word.length <= start.column) - highlightIsAllAfterRange = - highlight.row > end.row or - (highlight.row == end.row and highlight.column >= end.column) - !(highlightIsAllBeforeRange or highlightIsAllAfterRange) - - _isHighlightAfterRange: (highlight, start, end) -> - return true if highlight.row > end.row - return false if highlight.row < end.row - highlight.column >= end.column - - - - - - - diff --git a/services/web/public/coffee/app/ide/editor/spell-check/SpellCheckManager.coffee b/services/web/public/coffee/app/ide/editor/spell-check/SpellCheckManager.coffee deleted file mode 100644 index 2f9838a9ab..0000000000 --- a/services/web/public/coffee/app/ide/editor/spell-check/SpellCheckManager.coffee +++ /dev/null @@ -1,191 +0,0 @@ -define [ - "ide/editor/spell-check/HighlightedWordManager" - "ace/range" -], (HighlightedWordManager) -> - Range = require("ace/range").Range - - class SpellCheckManager - constructor: (@$scope, @editor, @element) -> - @updatedLines = [] - @highlightedWordManager = new HighlightedWordManager(@editor) - - @$scope.$watch "spellCheckLanguage", (language, oldLanguage) => - if language != oldLanguage and oldLanguage? - @runFullCheck() - - @editor.on "changeSession", (e) => - @runFullCheck() - - doc = e.session.getDocument() - doc.on "change", (e) => - @runCheckOnChange(e) - - @$scope.spellingMenu = {left: '0px', top: '0px'} - - @editor.on "nativecontextmenu", (e) => - @closeContextMenu(e.domEvent) - @openContextMenu(e.domEvent) - - $(document).on "click", (e) => - @closeContextMenu(e) - return true - # $(document).on "contextmenu", (e) => - # @closeContextMenu(e) - - @$scope.replaceWord = (highlight, suggestion) => - @replaceWord(highlight, suggestion) - - @$scope.learnWord = (highlight) => - @learnWord(highlight) - - runFullCheck: () -> - console.log "Running full check" - @highlightedWordManager.clearRows() - if @$scope.spellCheckLanguage and @$scope.spellCheckLanguage != "" - @runSpellCheck() - - runCheckOnChange: (e) -> - console.log "Checking change", e.data - if @$scope.spellCheckLanguage and @$scope.spellCheckLanguage != "" - @highlightedWordManager.applyChange(e.data) - @markLinesAsUpdated(e.data) - @runSpellCheckSoon() - - openContextMenu: (e) -> - position = @editor.renderer.screenToTextCoordinates(e.clientX, e.clientY) - highlight = @highlightedWordManager.findHighlightWithinRange - start: position - end: position - - @$scope.$apply () => - @$scope.spellingMenu.highlight = highlight - - console.log "highlight", @$scope.highlight_under_mouse - - if highlight - e.stopPropagation() - e.preventDefault() - - @editor.getSession().getSelection().setSelectionRange( - new Range( - highlight.row, highlight.column - highlight.row, highlight.column + highlight.word.length - ) - ) - - console.log "Height", @element.find(".context-menu").height() - - @$scope.$apply () => - @$scope.spellingMenu.open = true - @$scope.spellingMenu.left = e.clientX + 'px' - @$scope.spellingMenu.top = e.clientY + 'px' - - closeContextMenu: (e) -> - @$scope.$apply () => - @$scope.spellingMenu.open = false - - replaceWord: (highlight, text) -> - @editor.getSession().replace(new Range( - highlight.row, highlight.column, - highlight.row, highlight.column + highlight.word.length - ), text) - - learnWord: (highlight) -> - @apiRequest "/learn", word: highlight.word - @highlightedWordManager.removeWord highlight.word - - getHighlightedWordAtCursor: () -> - cursor = @editor.getCursorPosition() - highlight = @highlightedWordManager.findHighlightWithinRange - start: cursor - end: cursor - return highlight - - runSpellCheckSoon: () -> - run = () => - delete @timeoutId - @runSpellCheck(@updatedLines) - @updatedLines = [] - if @timeoutId? - clearTimeout @timeoutId - @timeoutId = setTimeout run, 1000 - - markLinesAsUpdated: (change) -> - start = change.range.start - end = change.range.end - - insertLines = () => - lines = end.row - start.row - while lines-- - @updatedLines.splice(start.row, 0, true) - - removeLines = () => - lines = end.row - start.row - while lines-- - @updatedLines.splice(start.row + 1, 1) - - if change.action == "insertText" - @updatedLines[start.row] = true - insertLines() - else if change.action == "removeText" - @updatedLines[start.row] = true - removeLines() - else if change.action == "insertLines" - insertLines() - else if change.action == "removeLines" - removeLines() - - runSpellCheck: (linesToProcess) -> - {words, positions} = @getWords(linesToProcess) - language = @$scope.spellCheckLanguage - @apiRequest "/check", {language: language, words: words}, (error, result) => - if error? or !result? or !result.misspellings? - return null - - if linesToProcess? - for shouldProcess, row in linesToProcess - @highlightedWordManager.clearRows(row, row) if shouldProcess - else - @highlightedWordManager.clearRows() - - for misspelling in result.misspellings - word = words[misspelling.index] - position = positions[misspelling.index] - @highlightedWordManager.addHighlight - column: position.column - row: position.row - word: word - suggestions: misspelling.suggestions - - getWords: (linesToProcess) -> - lines = @editor.getValue().split("\n") - words = [] - positions = [] - for line, row in lines - if !linesToProcess? or linesToProcess[row] - wordRegex = /\\?['a-zA-Z\u00C0-\u00FF]+/g - while (result = wordRegex.exec(line)) - word = result[0] - if word[0] == "'" - word = word.slice(1) - if word[word.length - 1] == "'" - word = word.slice(0,-1) - positions.push row: row, column: result.index - words.push(word) - return words: words, positions: positions - - apiRequest: (endpoint, data, callback = (error, result) ->)-> - data.token = window.user.id - data._csrf = window.csrfToken - options = - url: "/spelling" + endpoint - type: "POST" - dataType: "json" - headers: - "Content-Type": "application/json" - data: JSON.stringify data - success: (data, status, xhr) -> - callback null, data - error: (xhr, status, error) -> - callback error - $.ajax options diff --git a/services/web/public/coffee/app/ide/editor/spell-check/SpellingMenuView.coffee b/services/web/public/coffee/app/ide/editor/spell-check/SpellingMenuView.coffee deleted file mode 100644 index 3ba9fd1d96..0000000000 --- a/services/web/public/coffee/app/ide/editor/spell-check/SpellingMenuView.coffee +++ /dev/null @@ -1,82 +0,0 @@ -define [ - "libs/backbone" - "libs/mustache" -], () -> - SUGGESTIONS_TO_SHOW = 5 - - SpellingMenuView = Backbone.View.extend - templates: - menu: $("#spellingMenuTemplate").html() - entry: $("#spellingMenuEntryTemplate").html() - - events: - "click a#learnWord": -> - @trigger "click:learn", @_currentHighlight - @hide() - - initialize: (options) -> - @ide = options.ide - @ide.editor.getContainerElement().append @render().el - @ide.editor.on "click", () => @hide() - @ide.editor.on "scroll", () => @hide() - @ide.editor.on "update:doc", () => @hide() - @ide.editor.on "change:doc", () => @hide() - - render: () -> - @setElement($(@templates.menu)) - @$el.css "z-index" : 10000 - @$(".dropdown-toggle").dropdown() - @hide() - return @ - - showForHighlight: (highlight) -> - if @_currentHighlight? and highlight != @_currentHighlight - @_close() - - if !@_currentHighlight? - @_currentHighlight = highlight - @_setSuggestions(highlight) - position = @ide.editor.textToEditorCoordinates( - highlight.row - highlight.column + highlight.word.length - ) - @_position(position.x, position.y) - @_show() - - hideIfAppropriate: (cursorPosition) -> - if @_currentHighlight? - if !@_cursorCloseToHighlight(cursorPosition, @_currentHighlight) and !@_isOpen() - @hide() - - hide: () -> - delete @_currentHighlight - @_close() - @$el.hide() - - _setSuggestions: (highlight) -> - @$(".spelling-suggestion").remove() - divider = @$(".divider") - for suggestion in highlight.suggestions.slice(0, SUGGESTIONS_TO_SHOW) - do (suggestion) => - entry = $(Mustache.to_html(@templates.entry, word: suggestion)) - divider.before(entry) - entry.on "click", () => - @trigger "click:suggestion", suggestion, highlight - - _show: () -> @$el.show() - - _isOpen: () -> - @$(".dropdown-menu").is(":visible") - - _close: () -> - if @_isOpen() - @$el.dropdown("toggle") - - _cursorCloseToHighlight: (position, highlight) -> - position.row == highlight.row and - position.column >= highlight.column and - position.column <= highlight.column + highlight.word.length + 1 - - _position: (x,y) -> @$el.css left: x, top: y - - diff --git a/services/web/public/coffee/app/ide/editor/undo/UndoManager.coffee b/services/web/public/coffee/app/ide/editor/undo/UndoManager.coffee deleted file mode 100644 index a6caf52372..0000000000 --- a/services/web/public/coffee/app/ide/editor/undo/UndoManager.coffee +++ /dev/null @@ -1,374 +0,0 @@ -define [ - "ace/range" - "ace/edit_session" - "ace/document" -], () -> - Range = require("ace/range").Range - EditSession = require("ace/edit_session").EditSession - Doc = require("ace/document").Document - - class UndoManager - constructor: (@$scope, @editor) -> - @$scope.undo = - show_remote_warning: false - - @reset() - @nextUpdateIsRemote = false - - @editor.on "changeSession", (e) => - console.log "setting undo manager", e.session - e.session.setUndoManager(@) - - showUndoConflictWarning: () -> - @$scope.$apply () => - @$scope.undo.show_remote_warning = true - - setTimeout () => - @$scope.$apply () => - @$scope.undo.show_remote_warning = false - , 4000 - - reset: () -> - @undoStack = [] - @redoStack = [] - - execute: (options) -> - aceDeltaSets = options.args[0] - @session = options.args[1] - return if !aceDeltaSets? - - lines = @session.getDocument().getAllLines() - linesBeforeChange = @_revertAceDeltaSetsOnDocLines(aceDeltaSets, lines) - simpleDeltaSets = @_aceDeltaSetsToSimpleDeltaSets(aceDeltaSets, linesBeforeChange) - @undoStack.push( - deltaSets: simpleDeltaSets - remote: @nextUpdateIsRemote - ) - @redoStack = [] - @nextUpdateIsRemote = false - - undo: (dontSelect) -> - localUpdatesMade = @_shiftLocalChangeToTopOfUndoStack() - return if !localUpdatesMade - - update = @undoStack.pop() - return if !update? - - if update.remote - @showUndoConflictWarning() - - lines = @session.getDocument().getAllLines() - linesBeforeDelta = @_revertSimpleDeltaSetsOnDocLines(update.deltaSets, lines) - deltaSets = @_simpleDeltaSetsToAceDeltaSets(update.deltaSets, linesBeforeDelta) - selectionRange = @session.undoChanges(deltaSets, dontSelect) - @redoStack.push(update) - return selectionRange - - redo: (dontSelect) -> - update = @redoStack.pop() - return if !update? - lines = @session.getDocument().getAllLines() - deltaSets = @_simpleDeltaSetsToAceDeltaSets(update.deltaSets, lines) - selectionRange = @session.redoChanges(deltaSets, dontSelect) - @undoStack.push(update) - return selectionRange - - _shiftLocalChangeToTopOfUndoStack: () -> - head = [] - localChangeExists = false - while @undoStack.length > 0 - update = @undoStack.pop() - head.unshift update - if !update.remote - localChangeExists = true - break - - if !localChangeExists - @undoStack = @undoStack.concat head - return false - else - # Undo stack looks like undoStack ++ reorderedhead ++ head - # Reordered head starts of empty and consumes entries from head - # while keeping the localChange at the top for as long as it can - localChange = head.shift() - reorderedHead = [localChange] - while head.length > 0 - remoteChange = head.shift() - localChange = reorderedHead.pop() - result = @_swapSimpleDeltaSetsOrder(localChange.deltaSets, remoteChange.deltaSets) - if result? - remoteChange.deltaSets = result[0] - localChange.deltaSets = result[1] - reorderedHead.push remoteChange - reorderedHead.push localChange - else - reorderedHead.push localChange - reorderedHead.push remoteChange - break - @undoStack = @undoStack.concat(reorderedHead).concat(head) - return true - - - _swapSimpleDeltaSetsOrder: (firstDeltaSets, secondDeltaSets) -> - newFirstDeltaSets = @_copyDeltaSets(firstDeltaSets) - newSecondDeltaSets = @_copyDeltaSets(secondDeltaSets) - for firstDeltaSet in newFirstDeltaSets.slice(0).reverse() - for firstDelta in firstDeltaSet.deltas.slice(0).reverse() - for secondDeltaSet in newSecondDeltaSets - for secondDelta in secondDeltaSet.deltas - success = @_swapSimpleDeltaOrderInPlace(firstDelta, secondDelta) - return null if !success - return [newSecondDeltaSets, newFirstDeltaSets] - - _copyDeltaSets: (deltaSets) -> - newDeltaSets = [] - for deltaSet in deltaSets - newDeltaSet = - deltas: [] - group: deltaSet.group - newDeltaSets.push newDeltaSet - for delta in deltaSet.deltas - newDelta = - position: delta.position - newDelta.insert = delta.insert if delta.insert? - newDelta.remove = delta.remove if delta.remove? - newDeltaSet.deltas.push newDelta - return newDeltaSets - - _swapSimpleDeltaOrderInPlace: (firstDelta, secondDelta) -> - result = @_swapSimpleDeltaOrder(firstDelta, secondDelta) - return false if !result? - firstDelta.position = result[1].position - secondDelta.position = result[0].position - return true - - _swapSimpleDeltaOrder: (firstDelta, secondDelta) -> - if firstDelta.insert? and secondDelta.insert? - if secondDelta.position >= firstDelta.position + firstDelta.insert.length - secondDelta.position -= firstDelta.insert.length - return [secondDelta, firstDelta] - else if secondDelta.position > firstDelta.position - return null - else - firstDelta.position += secondDelta.insert.length - return [secondDelta, firstDelta] - else if firstDelta.remove? and secondDelta.remove? - if secondDelta.position >= firstDelta.position - secondDelta.position += firstDelta.remove.length - return [secondDelta, firstDelta] - else if secondDelta.position + secondDelta.remove.length > firstDelta.position - return null - else - firstDelta.position -= secondDelta.remove.length - return [secondDelta, firstDelta] - else if firstDelta.insert? and secondDelta.remove? - if secondDelta.position >= firstDelta.position + firstDelta.insert.length - secondDelta.position -= firstDelta.insert.length - return [secondDelta, firstDelta] - else if secondDelta.position + secondDelta.remove.length > firstDelta.position - return null - else - firstDelta.position -= secondDelta.remove.length - return [secondDelta, firstDelta] - else if firstDelta.remove? and secondDelta.insert? - if secondDelta.position >= firstDelta.position - secondDelta.position += firstDelta.remove.length - return [secondDelta, firstDelta] - else - firstDelta.position += secondDelta.insert.length - return [secondDelta, firstDelta] - else - throw "Unknown delta types" - - _applyAceDeltasToDocLines: (deltas, docLines) -> - doc = new Doc(docLines.join("\n")) - doc.applyDeltas(deltas) - return doc.getAllLines() - - _revertAceDeltaSetsOnDocLines: (deltaSets, docLines) -> - session = new EditSession(docLines.join("\n")) - session.undoChanges(deltaSets) - return session.getDocument().getAllLines() - - _revertSimpleDeltaSetsOnDocLines: (deltaSets, docLines) -> - doc = docLines.join("\n") - for deltaSet in deltaSets.slice(0).reverse() - for delta in deltaSet.deltas.slice(0).reverse() - if delta.remove? - doc = doc.slice(0, delta.position) + delta.remove + doc.slice(delta.position) - else if delta.insert? - doc = doc.slice(0, delta.position) + doc.slice(delta.position + delta.insert.length) - else - throw "Unknown delta type" - return doc.split("\n") - - _aceDeltaSetsToSimpleDeltaSets: (aceDeltaSets, docLines) -> - for deltaSet in aceDeltaSets - simpleDeltas = [] - for delta in deltaSet.deltas - simpleDeltas.push @_aceDeltaToSimpleDelta(delta, docLines) - docLines = @_applyAceDeltasToDocLines([delta], docLines) - { - deltas: simpleDeltas - group: deltaSet.group - } - - _simpleDeltaSetsToAceDeltaSets: (simpleDeltaSets, docLines) -> - for deltaSet in simpleDeltaSets - aceDeltas = [] - for delta in deltaSet.deltas - newAceDeltas = @_simpleDeltaToAceDeltas(delta, docLines) - docLines = @_applyAceDeltasToDocLines(newAceDeltas, docLines) - aceDeltas = aceDeltas.concat newAceDeltas - { - deltas: aceDeltas - group: deltaSet.group - } - - _aceDeltaToSimpleDelta: (aceDelta, docLines) -> - start = aceDelta.range.start - linesBefore = docLines.slice(0, start.row) - position = - linesBefore.join("").length + # full lines - linesBefore.length + # new line characters - start.column # partial line - switch aceDelta.action - when "insertText" - return { - position: position - insert: aceDelta.text - } - when "insertLines" - return { - position: position - insert: aceDelta.lines.join("\n") + "\n" - } - when "removeText" - return { - position: position - remove: aceDelta.text - } - when "removeLines" - return { - position: position - remove: aceDelta.lines.join("\n") + "\n" - } - else - throw "Unknown Ace action: #{aceDelta.action}" - - _simplePositionToAcePosition: (position, docLines) -> - column = 0 - row = 0 - for line in docLines - if position > line.length - row++ - position -= (line + "\n").length - else - column = position - break - return {row: row, column: column} - - _textToAceActions: (simpleText, row, column, type) -> - aceDeltas = [] - lines = simpleText.split("\n") - - range = (options) -> new Range(options.start.row, options.start.column, options.end.row, options.end.column) - - do stripFirstLine = () -> - firstLine = lines.shift() - if firstLine.length > 0 - aceDeltas.push { - text: firstLine - range: range( - start: column: column, row: row - end: column: column + firstLine.length, row: row - ) - action: "#{type}Text" - } - column += firstLine.length - - do stripFirstNewLine = () -> - if lines.length > 0 - aceDeltas.push { - text: "\n" - range: range( - start: column: column, row: row - end: column: 0, row: row + 1 - ) - action: "#{type}Text" - } - row += 1 - - do stripMiddleFullLines = () -> - middleLines = lines.slice(0, -1) - if middleLines.length > 0 - aceDeltas.push { - lines: middleLines - range: range( - start: column: 0, row: row - end: column: 0, row: row + middleLines.length - ) - action: "#{type}Lines" - } - row += middleLines.length - - do stripLastLine = () -> - if lines.length > 0 - lastLine = lines.pop() - aceDeltas.push { - text: lastLine - range: range( - start: column: 0, row: row - end: column: lastLine.length , row: row - ) - action: "#{type}Text" - } - - return aceDeltas - - - _simpleDeltaToAceDeltas: (simpleDelta, docLines) -> - {row, column} = @_simplePositionToAcePosition(simpleDelta.position, docLines) - - if simpleDelta.insert? - return @_textToAceActions(simpleDelta.insert, row, column, "insert") - if simpleDelta.remove? - return @_textToAceActions(simpleDelta.remove, row, column, "remove").reverse() - else - throw "Unknown simple delta: #{simpleDelta}" - - _concatSimpleDeltas: (deltas) -> - return [] if deltas.length == 0 - - concattedDeltas = [] - previousDelta = deltas.shift() - for delta in deltas - if delta.insert? and previousDelta.insert? - if previousDelta.position + previousDelta.insert.length == delta.position - previousDelta = - insert: previousDelta.insert + delta.insert - position: previousDelta.position - else - concattedDeltas.push previousDelta - previousDelta = delta - - else if delta.remove? and previousDelta.remove? - if previousDelta.position == delta.position - previousDelta = - remove: previousDelta.remove + delta.remove - position: delta.position - else - concattedDeltas.push previousDelta - previousDelta = delta - else - concattedDeltas.push previousDelta - previousDelta = delta - concattedDeltas.push previousDelta - - - return concattedDeltas - - - hasUndo: () -> @undoStack.length > 0 - hasRedo: () -> @redoStack.length > 0 - diff --git a/services/web/public/coffee/app/ide/file-tree/FileTreeManager.coffee b/services/web/public/coffee/app/ide/file-tree/FileTreeManager.coffee deleted file mode 100644 index 812b5d3fb6..0000000000 --- a/services/web/public/coffee/app/ide/file-tree/FileTreeManager.coffee +++ /dev/null @@ -1,281 +0,0 @@ -define [ - "ide/file-tree/directives/fileEntity" - "ide/file-tree/directives/draggable" - "ide/file-tree/directives/droppable" - "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) -> - @$scope.$on "project:joined", => - @loadRootFolder() - @loadDeletedDocs() - @$scope.$emit "file-tree:initialized" - - @_bindToSocketEvents() - - _bindToSocketEvents: () -> - @ide.socket.on "reciveNewDoc", (parent_folder_id, doc) => - parent_folder = @findEntityById(parent_folder_id) or @$scope.rootFolder - @$scope.$apply () -> - parent_folder.children.push { - name: doc.name - id: doc._id - type: "doc" - } - - @ide.socket.on "reciveNewFile", (parent_folder_id, file) => - parent_folder = @findEntityById(parent_folder_id) or @$scope.rootFolder - @$scope.$apply () -> - parent_folder.children.push { - name: file.name - id: file._id - type: "file" - } - - @ide.socket.on "reciveNewFolder", (parent_folder_id, folder) => - parent_folder = @findEntityById(parent_folder_id) or @$scope.rootFolder - @$scope.$apply () -> - parent_folder.children.push { - name: folder.name - id: folder._id - type: "folder" - children: [] - } - - @ide.socket.on "reciveEntityRename", (entity_id, name) => - entity = @findEntityById(entity_id) - return if !entity? - @$scope.$apply () -> - entity.name = name - - @ide.socket.on "removeEntity", (entity_id) => - entity = @findEntityById(entity_id) - return if !entity? - @$scope.$apply () => - @_deleteEntityFromScope entity - - @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) - - selectEntity: (entity) -> - @selected_entity_id = entity.id # For reselecting after a reconnect - @ide.fileTreeManager.forEachEntity (entity) -> - 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? - - if options.includeDeleted - for entity in @$scope.deletedDocs - return entity if entity.id == id - - return null - - _findEntityByIdInFolder: (folder, id) -> - for entity in folder.children or [] - if entity.id == id - return entity - else if entity.children? - result = @_findEntityByIdInFolder(entity, id) - return result if result? - - return null - - findEntityByPath: (path) -> - @_findEntityByPathInFolder @$scope.rootFolder, path - - _findEntityByPathInFolder: (folder, path) -> - parts = path.split("/") - name = parts.shift() - rest = parts.join("/") - - if name == "." - return @_findEntityByPathInFolder(folder, rest) - - for entity in folder.children - if entity.name == name - if rest == "" - return entity - else if entity.type == "folder" - return @_findEntityByPathInFolder(entity, rest) - return null - - forEachEntity: (callback = (entity, parent_folder) ->) -> - @_forEachEntityInFolder(@$scope.rootFolder, callback) - - for entity in @$scope.deletedDocs or [] - callback(entity) - - _forEachEntityInFolder: (folder, callback) -> - for entity in folder.children or [] - callback(entity, folder) - if entity.children? - @_forEachEntityInFolder(entity, callback) - - getEntityPath: (entity) -> - @_getEntityPathInFolder @$scope.rootFolder, entity - - _getEntityPathInFolder: (folder, entity) -> - for child in folder.children or [] - if child == entity - return entity.name - else if child.type == "folder" - path = @_getEntityPathInFolder(child, entity) - if path? - return child.name + "/" + path - return null - - getRootDocDirname: () -> - rootDoc = @findEntityById @$scope.project.rootDoc_id - return if !rootDoc? - path = @getEntityPath(rootDoc) - return if !path? - return path.split("/").slice(0, -1).join("/") - - # forEachFolder: (callback) -> - # @forEachEntity (entity) -> - # if entity.type == "folder" - # callback(entity) - - loadRootFolder: () -> - @$scope.rootFolder = @_parseFolder(@$scope.project.rootFolder[0]) - - _parseFolder: (rawFolder) -> - folder = { - name: rawFolder.name - id: rawFolder._id - type: "folder" - children: [] - selected: (rawFolder._id == @selected_entity_id) - } - - for doc in rawFolder.docs or [] - folder.children.push { - name: doc.name - id: doc._id - type: "doc" - selected: (doc._id == @selected_entity_id) - } - - for file in rawFolder.fileRefs or [] - folder.children.push { - name: file.name - id: file._id - type: "file" - selected: (file._id == @selected_entity_id) - } - - for childFolder in rawFolder.folders or [] - folder.children.push @_parseFolder(childFolder) - - return folder - - loadDeletedDocs: () -> - @$scope.deletedDocs = [] - for doc in @$scope.project.deletedDocs - @$scope.deletedDocs.push { - name: doc.name - id: doc._id - type: "doc" - deleted: true - } - - getCurrentFolder: () -> - # Return the root folder if nothing is selected - @_getCurrentFolder(@$scope.rootFolder) or @$scope.rootFolder - - _getCurrentFolder: (startFolder = @$scope.rootFolder) -> - for entity in startFolder.children or [] - # The 'current' folder is either the one selected, or - # the one containing the selected doc/file - if entity.selected - if entity.type == "folder" - return entity - else - return startFolder - - if entity.type == "folder" - result = @_getCurrentFolder(entity) - return result if result? - - return null - - createDoc: (name, parent_folder = @getCurrentFolder()) -> - # We'll wait for the socket.io notification to actually - # add the doc for us. - @ide.$http.post "/project/#{@ide.project_id}/doc", { - name: name, - parent_folder_id: parent_folder?.id - _csrf: window.csrfToken - } - - createFolder: (name, parent_folder = @getCurrentFolder()) -> - # We'll wait for the socket.io notification to actually - # add the folder for us. - return @ide.$http.post "/project/#{@ide.project_id}/folder", { - name: name, - parent_folder_id: parent_folder?.id - _csrf: window.csrfToken - } - - renameEntity: (entity, name, callback = (error) ->) -> - return if entity.name == name - entity.name = name - return @ide.$http.post "/project/#{@ide.project_id}/#{entity.type}/#{entity.id}/rename", { - name: name, - _csrf: window.csrfToken - } - - deleteEntity: (entity, callback = (error) ->) -> - # We'll wait for the socket.io notification to - # delete from scope. - return @ide.$http { - method: "DELETE" - url: "/project/#{@ide.project_id}/#{entity.type}/#{entity.id}" - headers: - "X-Csrf-Token": window.csrfToken - } - - moveEntity: (entity, parent_folder, callback = (error) ->) -> - @_moveEntityInScope(entity, parent_folder) - return @ide.$http.post "/project/#{@ide.project_id}/#{entity.type}/#{entity.id}/move", { - folder_id: parent_folder.id - _csrf: window.csrfToken - } - - _deleteEntityFromScope: (entity, options = { moveToDeleted: true }) -> - parent_folder = null - @forEachEntity (possible_entity, folder) -> - if possible_entity == entity - parent_folder = folder - - if parent_folder? - index = parent_folder.children.indexOf(entity) - if index > -1 - parent_folder.children.splice(index, 1) - - 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, moveToDeleted: false) - parent_folder.children.push(entity) diff --git a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeController.coffee b/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeController.coffee deleted file mode 100644 index 31015229ac..0000000000 --- a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeController.coffee +++ /dev/null @@ -1,113 +0,0 @@ -define [ - "base" -], (App) -> - App.controller "FileTreeController", ["$scope", "$modal", "ide", ($scope, $modal, ide) -> - $scope.openNewDocModal = () -> - $modal.open( - templateUrl: "newDocModalTemplate" - controller: "NewDocModalController" - resolve: { - parent_folder: () -> ide.fileTreeManager.getCurrentFolder() - } - ) - - $scope.openNewFolderModal = () -> - $modal.open( - templateUrl: "newFolderModalTemplate" - controller: "NewFolderModalController" - resolve: { - parent_folder: () -> ide.fileTreeManager.getCurrentFolder() - } - ) - - $scope.openUploadFileModal = () -> - $modal.open( - templateUrl: "uploadFileModalTemplate" - controller: "UploadFileModalController" - scope: $scope - resolve: { - parent_folder: () -> ide.fileTreeManager.getCurrentFolder() - } - ) - - $scope.orderByFoldersFirst = (entity) -> - return '0' if entity.type == "folder" - return '1' - - $scope.startRenamingSelected = () -> - $scope.$broadcast "rename:selected" - - $scope.openDeleteModalForSelected = () -> - $scope.$broadcast "delete:selected" - ] - - App.controller "NewDocModalController", [ - "$scope", "ide", "$modalInstance", "$timeout", "parent_folder", - ($scope, ide, $modalInstance, $timeout, parent_folder) -> - $scope.inputs = - name: "name.tex" - $scope.state = - inflight: false - - $modalInstance.opened.then () -> - $timeout () -> - $scope.$broadcast "open" - , 200 - - $scope.create = () -> - $scope.state.inflight = true - ide.fileTreeManager - .createDoc($scope.inputs.name, parent_folder) - .success () -> - $scope.state.inflight = false - $modalInstance.close() - - $scope.cancel = () -> - $modalInstance.dismiss('cancel') - ] - - App.controller "NewFolderModalController", [ - "$scope", "ide", "$modalInstance", "$timeout", "parent_folder", - ($scope, ide, $modalInstance, $timeout, parent_folder) -> - $scope.inputs = - name: "name" - $scope.state = - inflight: false - - $modalInstance.opened.then () -> - $timeout () -> - $scope.$broadcast "open" - , 200 - - $scope.create = () -> - $scope.state.inflight = true - ide.fileTreeManager - .createFolder($scope.inputs.name, parent_folder) - .success () -> - $scope.state.inflight = false - $modalInstance.close() - - $scope.cancel = () -> - $modalInstance.dismiss('cancel') - ] - - App.controller "UploadFileModalController", [ - "$scope", "ide", "$modalInstance", "$timeout", "parent_folder", - ($scope, ide, $modalInstance, $timeout, parent_folder) -> - console.log "PArent folder", parent_folder - $scope.parent_folder_id = parent_folder?.id - - uploadCount = 0 - $scope.onUpload = () -> - uploadCount++ - - $scope.onComplete = (error, name, response) -> - $timeout (() -> - uploadCount-- - if uploadCount == 0 and response? and response.success - $modalInstance.close("done") - ), 250 - - $scope.cancel = () -> - $modalInstance.dismiss('cancel') - ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeEntityController.coffee b/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeEntityController.coffee deleted file mode 100644 index 7c56d1cf24..0000000000 --- a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeEntityController.coffee +++ /dev/null @@ -1,49 +0,0 @@ -define [ - "base" -], (App) -> - App.controller "FileTreeEntityController", ["$scope", "ide", "$modal", ($scope, ide, $modal) -> - $scope.select = () -> - ide.fileTreeManager.selectEntity($scope.entity) - $scope.$emit "entity:selected", $scope.entity - - $scope.inputs = - name: $scope.entity.name - - $scope.startRenaming = () -> - $scope.entity.renaming = true - - $scope.finishRenaming = () -> - delete $scope.entity.renaming - ide.fileTreeManager.renameEntity($scope.entity, $scope.inputs.name) - - $scope.$on "rename:selected", () -> - $scope.startRenaming() if $scope.entity.selected - - $scope.openDeleteModal = () -> - $modal.open( - templateUrl: "deleteEntityModalTemplate" - controller: "DeleteEntityModalController" - scope: $scope - ) - - $scope.$on "delete:selected", () -> - $scope.openDeleteModal() if $scope.entity.selected - ] - - App.controller "DeleteEntityModalController", [ - "$scope", "ide", "$modalInstance", - ($scope, ide, $modalInstance) -> - $scope.state = - inflight: false - - $scope.delete = () -> - $scope.state.inflight = true - ide.fileTreeManager - .deleteEntity($scope.entity) - .success () -> - $scope.state.inflight = false - $modalInstance.close() - - $scope.cancel = () -> - $modalInstance.dismiss('cancel') - ] diff --git a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeFolderController.coffee b/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeFolderController.coffee deleted file mode 100644 index 93daaae1f5..0000000000 --- a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeFolderController.coffee +++ /dev/null @@ -1,51 +0,0 @@ -define [ - "base" -], (App) -> - App.controller "FileTreeFolderController", ["$scope", "ide", "$modal", ($scope, ide, $modal) -> - $scope.expanded = $.localStorage("folder.#{$scope.entity.id}.expanded") or false - - $scope.toggleExpanded = () -> - $scope.expanded = !$scope.expanded - $.localStorage("folder.#{$scope.entity.id}.expanded", $scope.expanded) - - $scope.onDrop = (events, ui) -> - source = $(ui.draggable).scope().entity - return if !source? - ide.fileTreeManager.moveEntity(source, $scope.entity) - - $scope.orderByFoldersFirst = (entity) -> - # We need this here as well as in FileTreeController - # since the file-entity diretive creates a new scope - # that doesn't inherit from previous scopes. - return '0' if entity.type == "folder" - return '1' - - $scope.openNewDocModal = () -> - $modal.open( - templateUrl: "newDocModalTemplate" - controller: "NewDocModalController" - resolve: { - parent_folder: () -> $scope.entity - } - ) - - $scope.openNewFolderModal = () -> - $modal.open( - templateUrl: "newFolderModalTemplate" - controller: "NewFolderModalController" - resolve: { - parent_folder: () -> $scope.entity - } - ) - - $scope.openUploadFileModal = () -> - $scope.project_id = ide.project_id - $modal.open( - templateUrl: "uploadFileModalTemplate" - controller: "UploadFileModalController" - scope: $scope - resolve: { - parent_folder: () -> $scope.entity - } - ) - ] diff --git a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeRootFolderController.coffee b/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeRootFolderController.coffee deleted file mode 100644 index 8e3fe5e8bb..0000000000 --- a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeRootFolderController.coffee +++ /dev/null @@ -1,13 +0,0 @@ -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) - ] diff --git a/services/web/public/coffee/app/ide/file-tree/directives/draggable.coffee b/services/web/public/coffee/app/ide/file-tree/directives/draggable.coffee deleted file mode 100644 index 4b08a0daf6..0000000000 --- a/services/web/public/coffee/app/ide/file-tree/directives/draggable.coffee +++ /dev/null @@ -1,14 +0,0 @@ -define [ - "base" -], (App) -> - App.directive "draggable", () -> - return { - link: (scope, element, attrs) -> - scope.$watch attrs.draggable, (draggable) -> - if draggable - element.draggable - delay: 250 - opacity: 0.7 - helper: "clone" - scroll: true - } \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/file-tree/directives/droppable.coffee b/services/web/public/coffee/app/ide/file-tree/directives/droppable.coffee deleted file mode 100644 index ea68630a21..0000000000 --- a/services/web/public/coffee/app/ide/file-tree/directives/droppable.coffee +++ /dev/null @@ -1,17 +0,0 @@ -define [ - "base" -], (App) -> - App.directive "droppable", () -> - return { - scope: { - onDropCallback: "=" - } - link: (scope, element, attrs) -> - scope.$watch attrs.droppable, (droppable) -> - if droppable - element.droppable - greedy: true - hoverClass: "droppable-hover" - accept: attrs.accept - drop: scope.onDropCallback - } \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/file-tree/directives/fileEntity.coffee b/services/web/public/coffee/app/ide/file-tree/directives/fileEntity.coffee deleted file mode 100644 index 9f3fcd2407..0000000000 --- a/services/web/public/coffee/app/ide/file-tree/directives/fileEntity.coffee +++ /dev/null @@ -1,23 +0,0 @@ -define [ - "base" -], (App) -> - App.directive "fileEntity", ["RecursionHelper", (RecursionHelper) -> - return { - restrict: "E" - scope: { - entity: "=" - permissions: "=" - } - templateUrl: "entityListItemTemplate" - compile: (element) -> - RecursionHelper.compile element, (scope, element, attrs, ctrl) -> - # 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); - } - ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/online-users/OnlineUsersManager.coffee b/services/web/public/coffee/app/ide/online-users/OnlineUsersManager.coffee deleted file mode 100644 index 5e73a6446b..0000000000 --- a/services/web/public/coffee/app/ide/online-users/OnlineUsersManager.coffee +++ /dev/null @@ -1,71 +0,0 @@ -define [ - "../../../libs/md5" -], () -> - class OnlineUsersManager - constructor: (@ide, @$scope) -> - @$scope.onlineUsers = {} - @$scope.onlineUserCursorHighlights = {} - - @$scope.$watch "editor.cursorPosition", (position) => - if position? - @sendCursorPositionUpdate() - - @ide.socket.on "clientTracking.clientUpdated", (client) => - if client.id != @ide.socket.socket.sessionid # Check it's not me! - @$scope.$apply () => - @$scope.onlineUsers[client.id] = client - @updateCursorHighlights() - - @ide.socket.on "clientTracking.clientDisconnected", (client_id) => - @$scope.$apply () => - delete @$scope.onlineUsers[client_id] - @updateCursorHighlights() - - updateCursorHighlights: () -> - console.log "UPDATING CURSOR HIGHLIGHTS" - @$scope.onlineUserCursorHighlights = {} - for client_id, client of @$scope.onlineUsers - doc_id = client.doc_id - continue if !doc_id? - @$scope.onlineUserCursorHighlights[doc_id] ||= [] - @$scope.onlineUserCursorHighlights[doc_id].push { - label: client.name - cursor: - row: client.row - column: client.column - hue: @getHueForUserId(client.user_id) - } - - UPDATE_INTERVAL: 500 - sendCursorPositionUpdate: () -> - console.log "SENDING CURSOR POSITION UPDATE", @$scope.editor.cursorPosition - if !@cursorUpdateTimeout? - @cursorUpdateTimeout = setTimeout ()=> - position = @$scope.editor.cursorPosition - doc_id = @$scope.editor.open_doc_id - - @ide.socket.emit "clientTracking.updatePosition", { - row: position.row - column: position.column - doc_id: doc_id - } - - delete @cursorUpdateTimeout - , @UPDATE_INTERVAL - - OWN_HUE: 200 # We will always appear as this color to ourselves - ANONYMOUS_HUE: 100 - getHueForUserId: (user_id) -> - if !user_id? or user_id == "anonymous-user" - return @ANONYMOUS_HUE - - if window.user.id == user_id - return @OWN_HUE - - hash = CryptoJS.MD5(user_id) - hue = parseInt(hash.toString().slice(0,8), 16) % 320 - # Avoid 20 degrees either side of the personal hue - if hue > @OWNER_HUE - 20 - hue = hue + 40 - return hue - diff --git a/services/web/public/coffee/app/ide/pdf/PdfManager.coffee b/services/web/public/coffee/app/ide/pdf/PdfManager.coffee deleted file mode 100644 index 47ac42a1b6..0000000000 --- a/services/web/public/coffee/app/ide/pdf/PdfManager.coffee +++ /dev/null @@ -1,20 +0,0 @@ -define [ - "ide/pdf/controllers/PdfController" - "ide/pdf/directives/pdfJs" -], () -> - class PdfManager - constructor: (@ide, @$scope) -> - @$scope.pdf = - url: null # Pdf Url - error: false # Server error - timeout: false # Server timed out - failure: false # PDF failed to compile - compiling: false - uncompiled: true - logEntries: [] - logEntryAnnotations: {} - rawLog: "" - view: null # 'pdf' 'logs' - showRawLog: false - highlights: [] - position: null diff --git a/services/web/public/coffee/app/ide/pdf/controllers/PdfController.coffee b/services/web/public/coffee/app/ide/pdf/controllers/PdfController.coffee deleted file mode 100644 index 4fa3829f0d..0000000000 --- a/services/web/public/coffee/app/ide/pdf/controllers/PdfController.coffee +++ /dev/null @@ -1,277 +0,0 @@ -define [ - "base" - "libs/latex-log-parser" -], (App, LogParser) -> - App.controller "PdfController", ["$scope", "$http", "ide", "$modal", "synctex", ($scope, $http, ide, $modal, synctex) -> - autoCompile = true - $scope.$on "doc:opened", () -> - return if !autoCompile - autoCompile = false - $scope.recompile(isAutoCompile: true) - - sendCompileRequest = (options = {}) -> - url = "/project/#{$scope.project_id}/compile" - if options.isAutoCompile - url += "?auto_compile=true" - return $http.post url, { - settingsOverride: - rootDoc_id: options.rootDocOverride_id or null - _csrf: window.csrfToken - } - - parseCompileResponse = (response) -> - # Reset everything - $scope.pdf.error = false - $scope.pdf.timedout = false - $scope.pdf.failure = false - $scope.pdf.uncompiled = false - $scope.pdf.url = null - - if response.status == "timedout" - $scope.pdf.timedout = true - else if response.status == "autocompile-backoff" - $scope.pdf.uncompiled = true - else if response.status == "failure" - $scope.pdf.failure = true - fetchLogs() - else if response.status == "success" - $scope.pdf.url = "/project/#{$scope.project_id}/output/output.pdf?cache_bust=#{Date.now()}" - fetchLogs() - - IGNORE_FILES = ["output.fls", "output.fdb_latexmk"] - $scope.pdf.outputFiles = [] - for file in response.outputFiles - if IGNORE_FILES.indexOf(file.path) == -1 - # Turn 'output.blg' into 'blg file'. - if file.path.match(/^output\./) - file.name = "#{file.path.replace(/^output\./, "")} file" - else - file.name = file.path - $scope.pdf.outputFiles.push file - - fetchLogs = () -> - $http.get "/project/#{$scope.project_id}/output/output.log" - .success (log) -> - $scope.pdf.rawLog = log - logEntries = LogParser.parse(log, ignoreDuplicates: true) - $scope.pdf.logEntries = logEntries - $scope.pdf.logEntries.all = logEntries.errors.concat(logEntries.warnings).concat(logEntries.typesetting) - - $scope.pdf.logEntryAnnotations = {} - for entry in logEntries.all - entry.file = normalizeFilePath(entry.file) - - entity = ide.fileTreeManager.findEntityByPath(entry.file) - if entity? - $scope.pdf.logEntryAnnotations[entity.id] ||= [] - $scope.pdf.logEntryAnnotations[entity.id].push { - row: entry.line - 1 - type: if entry.level == "error" then "error" else "warning" - text: entry.message - } - - .error () -> - $scope.pdf.logEntries = [] - $scope.pdf.rawLog = "" - - getRootDocOverride_id = () -> - doc = ide.editorManager.getCurrentDocValue() - return null if !doc? - for line in doc.split("\n") - match = line.match /(.*)\\documentclass/ - if match and !match[1].match /%/ - return ide.editorManager.getCurrentDocId() - return null - - normalizeFilePath = (path) -> - path = path.replace(/^(.*)\/compiles\/[0-9a-f]{24}\/(\.\/)?/, "") - path = path.replace(/^\/compile\//, "") - - rootDocDirname = ide.fileTreeManager.getRootDocDirname() - if rootDocDirname? - path = path.replace(/^\.\//, rootDocDirname + "/") - - return path - - $scope.recompile = (options = {}) -> - console.log "Recompiling", options - return if $scope.pdf.compiling - $scope.pdf.compiling = true - - options.rootDocOverride_id = getRootDocOverride_id() - - sendCompileRequest(options) - .success (data) -> - $scope.pdf.view = "pdf" - $scope.pdf.compiling = false - parseCompileResponse(data) - .error () -> - $scope.pdf.compiling = false - $scope.pdf.error = true - - $scope.clearCache = () -> - $http { - url: "/project/#{$scope.project_id}/output" - method: "DELETE" - headers: - "X-Csrf-Token": window.csrfToken - } - - $scope.toggleLogs = () -> - if !$scope.pdf.view? or $scope.pdf.view == "pdf" - $scope.pdf.view = "logs" - else - $scope.pdf.view = "pdf" - - $scope.showPdf = () -> - $scope.pdf.view = "pdf" - - $scope.toggleRawLog = () -> - $scope.pdf.showRawLog = !$scope.pdf.showRawLog - - $scope.openOutputFile = (file) -> - window.open("/project/#{$scope.project_id}/output/#{file.path}") - - $scope.openClearCacheModal = () -> - modalInstance = $modal.open( - templateUrl: "clearCacheModalTemplate" - controller: "ClearCacheModalController" - scope: $scope - ) - - $scope.syncToCode = (position) -> - console.log "SYNCING VIA DBL CLICK", position - synctex - .syncToCode(position) - .then (data) -> - {doc, line} = data - ide.editorManager.openDoc(doc, gotoLine: line) - - ] - - App.factory "synctex", ["ide", "$http", "$q", (ide, $http, $q) -> - synctex = - syncToPdf: (cursorPosition) -> - deferred = $q.defer() - - doc_id = ide.editorManager.getCurrentDocId() - if !doc_id? - deferred.reject() - return deferred.promise - doc = ide.fileTreeManager.findEntityById(doc_id) - if !doc? - deferred.reject() - return deferred.promise - path = ide.fileTreeManager.getEntityPath(doc) - if !path? - deferred.reject() - return deferred.promise - - # If the root file is folder/main.tex, then synctex sees the - # path as folder/./main.tex - rootDocDirname = ide.fileTreeManager.getRootDocDirname() - if rootDocDirname? and rootDocDirname != "" - path = path.replace(RegExp("^#{rootDocDirname}"), "#{rootDocDirname}/.") - - {row, column} = cursorPosition - - $http({ - url: "/project/#{ide.project_id}/sync/code", - method: "GET", - params: { - file: path - line: row + 1 - column: column - } - }) - .success (data) -> - deferred.resolve(data.pdf or []) - .error (error) -> - deferred.reject(error) - - return deferred.promise - - syncToCode: (position, options = {}) -> - deferred = $q.defer() - if !position? - deferred.reject() - return deferred.promise - - # It's not clear exactly where we should sync to if it wasn't directly - # clicked on, but a little bit down from the very top seems best. - if options.includeVisualOffset - position.offset.top = position.offset.top + 80 - - $http({ - url: "/project/#{ide.project_id}/sync/pdf", - method: "GET", - params: { - page: position.page + 1 - h: position.offset.left.toFixed(2) - v: position.offset.top.toFixed(2) - } - }) - .success (data) -> - if data.code? and data.code.length > 0 - doc = ide.fileTreeManager.findEntityByPath(data.code[0].file) - return if !doc? - deferred.resolve({doc: doc, line: data.code[0].line}) - .error (error) -> - deferred.reject(error) - - return deferred.promise - - return synctex - ] - - App.controller "PdfSynctexController", ["$scope", "synctex", "ide", ($scope, synctex, ide) -> - $scope.showControls = true - $scope.$on "layout:pdf:resize", (event, data) -> - if data.east.initClosed - $scope.showControls = false - else - $scope.showControls = true - setTimeout () -> - $scope.$digest() - , 0 - - $scope.syncToPdf = () -> - synctex - .syncToPdf($scope.editor.cursorPosition) - .then (highlights) -> - $scope.pdf.highlights = highlights - - $scope.syncToCode = () -> - synctex - .syncToCode($scope.pdf.position, includeVisualOffset: true) - .then (data) -> - {doc, line} = data - console.log "OPENING DOC", doc, line - ide.editorManager.openDoc(doc, gotoLine: line) - ] - - App.controller "PdfLogEntryController", ["$scope", "ide", ($scope, ide) -> - $scope.openInEditor = (entry) -> - console.log "OPENING", entry.file, entry.line - entity = ide.fileTreeManager.findEntityByPath(entry.file) - return if !entity? or entity.type != "doc" - if entry.line? - line = entry.line - ide.editorManager.openDoc(entity, gotoLine: line) - ] - - App.controller 'ClearCacheModalController', ["$scope", "$modalInstance", ($scope, $modalInstance) -> - $scope.state = - inflight: false - - $scope.clear = () -> - $scope.state.inflight = true - $scope - .clearCache() - .then () -> - $scope.state.inflight = false - $modalInstance.close() - - $scope.cancel = () -> - $modalInstance.dismiss('cancel') - ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/pdf/directives/pdfJs.coffee b/services/web/public/coffee/app/ide/pdf/directives/pdfJs.coffee deleted file mode 100644 index b5ef69c4e3..0000000000 --- a/services/web/public/coffee/app/ide/pdf/directives/pdfJs.coffee +++ /dev/null @@ -1,196 +0,0 @@ -define [ - "base" - "libs/pdfListView/PdfListView" - "libs/pdfListView/TextLayerBuilder" - "libs/pdfListView/AnnotationsLayerBuilder" - "libs/pdfListView/HighlightsLayerBuilder" - "text!libs/pdfListView/TextLayer.css" - "text!libs/pdfListView/AnnotationsLayer.css" - "text!libs/pdfListView/HighlightsLayer.css" -], ( - App - PDFListView - TextLayerBuilder - AnnotationsLayerBuilder - HighlightsLayerBuilder - textLayerCss - annotationsLayerCss - highlightsLayerCss -) -> - if PDFJS? - PDFJS.workerSrc = "#{window.sharelatex.pdfJsWorkerPath}" - - style = $("